Skip to content

Commit b8d8fe7

Browse files
committed
fix(web): reserve the Windows title-bar area and paint the sidebar solid
Every desktop chrome rule was gated on darwin, so Windows reserved no space for the 44px Window Controls Overlay and the controls covered the chat header's branch label. Add a shell-owned drag strip anchored to the titlebar-area safe area, which stays correct under RTL and while the preview panel animates, and give Windows a solid sidebar instead of the shared translucent fill. Also change the VS Code extension display name, which the Marketplace rejects as taken, and run the pythinker-web suite in the root vitest projects so the new contract test executes in CI.
1 parent 7b0be7d commit b8d8fe7

5 files changed

Lines changed: 92 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Reserve the Windows title-bar area so the window controls no longer overlap the chat header, paint the Windows sidebar solid, and change the VS Code extension display name to `Pythinker` because the previous name is reserved on the Marketplace.

apps/pythinker-web/src/App.vue

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@ function openPr(url: string): void {
871871

872872
<template>
873873
<div class="app-shell">
874+
<div class="windows-titlebar" aria-hidden="true"></div>
874875
<section v-if="showAuthGate" class="auth-page">
875876
<div class="auth-page-inner">
876877
<PythinkerLogo size="lg" interactive class="auth-page-logo" />
@@ -1337,6 +1338,21 @@ function openPr(url: string): void {
13371338
overflow: hidden;
13381339
box-sizing: border-box;
13391340
}
1341+
.windows-titlebar { display: none; }
1342+
:global(html[data-desktop-platform='win32'] .app-shell) {
1343+
padding-top: env(titlebar-area-height, 44px);
1344+
background: var(--panel);
1345+
}
1346+
:global(html[data-desktop-platform='win32'] .windows-titlebar) {
1347+
display: block;
1348+
position: fixed;
1349+
top: env(titlebar-area-y, 0px);
1350+
left: env(titlebar-area-x, 0px);
1351+
width: env(titlebar-area-width, 100%);
1352+
height: env(titlebar-area-height, 44px);
1353+
-webkit-app-region: drag;
1354+
user-select: none;
1355+
}
13401356
.auth-page {
13411357
flex: 1;
13421358
min-height: 0;
@@ -1420,19 +1436,25 @@ function openPr(url: string): void {
14201436
overflow: hidden;
14211437
box-sizing: border-box;
14221438
}
1423-
:global(html[data-desktop-platform='darwin'] .app),
1424-
:global(html[data-desktop-platform='win32'] .app) {
1439+
:global(html[data-desktop-platform='darwin'] .app) {
14251440
background: transparent;
14261441
}
1442+
:global(html[data-desktop-platform='win32'] .app) {
1443+
background: var(--bg);
1444+
}
14271445
:global(html[data-desktop-platform='darwin'] .side),
1446+
:global(html[data-desktop-platform='darwin'] .sidebar-rail) {
1447+
background: color-mix(in srgb, var(--panel) 55%, transparent);
1448+
}
14281449
:global(html[data-desktop-platform='win32'] .side),
1429-
:global(html[data-desktop-platform='darwin'] .sidebar-rail),
14301450
:global(html[data-desktop-platform='win32'] .sidebar-rail) {
1431-
background: color-mix(in srgb, var(--panel) 55%, transparent);
1451+
background: var(--panel);
14321452
}
14331453
:global(html[data-desktop-platform='darwin'] .con),
1454+
:global(html[data-desktop-platform='darwin'] .global-preview) {
1455+
background: var(--bg);
1456+
}
14341457
:global(html[data-desktop-platform='win32'] .con),
1435-
:global(html[data-desktop-platform='darwin'] .global-preview),
14361458
:global(html[data-desktop-platform='win32'] .global-preview) {
14371459
background: var(--bg);
14381460
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Static contract check: CI and local development do not have a Windows host,
2+
// so this checks the CSS contract rather than rendered geometry.
3+
4+
import { existsSync, readFileSync } from 'node:fs';
5+
import { describe, expect, it } from 'vitest';
6+
7+
const appPath = ['src/App.vue', 'apps/pythinker-web/src/App.vue'].find(existsSync);
8+
if (!appPath) throw new Error('App.vue source was not found');
9+
10+
const appSource = readFileSync(appPath, 'utf8');
11+
const styleMatch = appSource.match(/<style scoped>([\s\S]*?)<\/style>/);
12+
13+
if (!styleMatch?.[1]) throw new Error('App.vue must have a scoped style block');
14+
15+
const cssRules = [...styleMatch[1].matchAll(/([^{}]+)\{([^{}]*)\}/g)].map(([, selector, declarations]) => ({
16+
selector: selector!,
17+
declarations: declarations!,
18+
}));
19+
const win32 = "data-desktop-platform='win32'";
20+
const darwin = "data-desktop-platform='darwin'";
21+
22+
function rulesFor(platform: string, ...selectors: string[]) {
23+
return cssRules.filter((rule) => (
24+
rule.selector.includes(platform) && selectors.every((selector) => rule.selector.includes(selector))
25+
));
26+
}
27+
28+
describe('Windows titlebar CSS contract', () => {
29+
it('keeps the titlebar safe area and sidebar platform-specific', () => {
30+
expect(appSource).toContain('class="windows-titlebar"');
31+
expect(appSource).toContain('aria-hidden="true"');
32+
33+
const win32DragRules = cssRules.filter((rule) => (
34+
rule.selector.includes(win32) && rule.declarations.includes('-webkit-app-region: drag')
35+
));
36+
expect(win32DragRules).toHaveLength(1);
37+
expect(win32DragRules[0]!.selector).toContain('.windows-titlebar');
38+
39+
const win32TitlebarRules = rulesFor(win32, '.windows-titlebar');
40+
expect(win32TitlebarRules).toHaveLength(1);
41+
expect(win32TitlebarRules[0]!.declarations).toContain('titlebar-area-x');
42+
expect(win32TitlebarRules[0]!.declarations).toContain('titlebar-area-width');
43+
44+
const win32ShellRules = rulesFor(win32, '.app-shell');
45+
expect(win32ShellRules).toHaveLength(1);
46+
expect(win32ShellRules[0]!.declarations).toContain('titlebar-area-height');
47+
48+
const win32SidebarRules = rulesFor(win32, ' .side)', '.sidebar-rail');
49+
expect(win32SidebarRules).toHaveLength(1);
50+
expect(win32SidebarRules[0]!.declarations).not.toMatch(/transparent|color-mix/);
51+
52+
const darwinSidebarRules = rulesFor(darwin, ' .side)', '.sidebar-rail');
53+
expect(darwinSidebarRules).toHaveLength(1);
54+
expect(darwinSidebarRules[0]!.declarations).toContain(
55+
'color-mix(in srgb, var(--panel) 55%, transparent)',
56+
);
57+
});
58+
});

apps/vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "pythinker",
33
"publisher": "pymodel",
4-
"displayName": "Pythinker Code",
4+
"displayName": "Pythinker",
55
"description": "Pythinker Code extension for VS Code",
66
"version": "0.9.2",
77
"private": true,

vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { defineConfig } from 'vitest/config';
22

33
export default defineConfig({
44
test: {
5-
projects: ['packages/*', 'apps/pythinker-code', 'apps/desktop'],
5+
projects: ['packages/*', 'apps/pythinker-code', 'apps/desktop', 'apps/pythinker-web'],
66
coverage: {
77
provider: 'v8',
88
include: ['packages/*/src/**/*.ts', 'apps/*/src/**/*.ts'],

0 commit comments

Comments
 (0)