Skip to content

Commit 2db06b7

Browse files
committed
fix(desktop): restore gateway footer after workflow removal
1 parent 331c4d1 commit 2db06b7

6 files changed

Lines changed: 162 additions & 83 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ Detailed data and runtime contracts: @docs/agents/repository-contracts.md and
7777
top-level concepts.
7878
- Use native platform patterns: Electron/shadcn-style desktop surfaces where
7979
appropriate, and native grouped iOS management surfaces on mobile.
80+
- Desktop app-shell chrome must keep its complete geometry and state recipe in
81+
an always-loaded owner stylesheet. Never place global shell component rules
82+
in a removable feature stylesheet; pin the owner import with a contract test.
8083
- Mobile route state, presentation mapping, formatting, and business-rule
8184
transformations should live in `GaryxMobileCore` with SwiftPM tests.
8285
- Message, transcript, and tool-row display is server-render-state first:

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ Detailed data and runtime contracts: @docs/agents/repository-contracts.md and
7777
top-level concepts.
7878
- Use native platform patterns: Electron/shadcn-style desktop surfaces where
7979
appropriate, and native grouped iOS management surfaces on mobile.
80+
- Desktop app-shell chrome must keep its complete geometry and state recipe in
81+
an always-loaded owner stylesheet. Never place global shell component rules
82+
in a removable feature stylesheet; pin the owner import with a contract test.
8083
- Mobile route state, presentation mapping, formatting, and business-rule
8184
transformations should live in `GaryxMobileCore` with SwiftPM tests.
8285
- Message, transcript, and tool-row display is server-render-state first:

desktop/garyx-desktop/src/renderer/src/sidebar-footer-design.test.mjs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import test from 'node:test';
22
import assert from 'node:assert/strict';
3-
import { readFileSync } from 'node:fs';
3+
import { readFileSync, readdirSync } from 'node:fs';
44
import path from 'node:path';
55
import { fileURLToPath } from 'node:url';
66

@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
99
// insets and gap, a 29px identity row, and a 32px trailing utility button.
1010

1111
const rendererDir = path.dirname(fileURLToPath(import.meta.url));
12+
const stylesDir = path.join(rendererDir, 'styles');
1213
const read = (relativePath) =>
1314
readFileSync(path.join(rendererDir, relativePath), 'utf8');
1415

@@ -57,13 +58,13 @@ test('sidebar footer pins the Codex frame and divider geometry', () => {
5758
});
5859

5960
test('gateway and settings controls pin the Codex footer alignment', () => {
60-
const layoutCss = read('styles/workflows.css');
61-
expectRule(layoutCss, '.gateway-identity-bar', [
61+
const ownerCss = read('styles/gateway-status.css');
62+
expectRule(ownerCss, '.gateway-identity-bar', [
6263
'gap: 8px',
6364
'height: 46px',
6465
'padding: 0 8px',
6566
]);
66-
expectRule(layoutCss, '.gateway-identity-main', [
67+
expectRule(ownerCss, '.gateway-identity-main', [
6768
'gap: 8px',
6869
'height: 29px',
6970
'padding: 0 8px',
@@ -73,17 +74,16 @@ test('gateway and settings controls pin the Codex footer alignment', () => {
7374
'font-weight: 445',
7475
'line-height: 21px',
7576
]);
76-
expectRule(layoutCss, '.gateway-identity-main:hover', [
77+
expectRule(ownerCss, '.gateway-identity-main:hover', [
7778
'background: var(--color-token-row-hover)',
7879
]);
7980

80-
const controlsCss = read('styles/task-forest.css');
81-
expectRule(controlsCss, '.gateway-identity-name', [
81+
expectRule(ownerCss, '.gateway-identity-name', [
8282
'font-size: inherit',
8383
'font-weight: inherit',
8484
'line-height: inherit',
8585
]);
86-
expectRule(controlsCss, '.gateway-identity-gear', [
86+
expectRule(ownerCss, '.gateway-identity-gear', [
8787
'gap: 4px',
8888
'width: 32px',
8989
'height: 32px',
@@ -93,7 +93,7 @@ test('gateway and settings controls pin the Codex footer alignment', () => {
9393
'color: var(--color-token-description-foreground)',
9494
'font: 445 16px/24px -apple-system',
9595
]);
96-
expectRule(controlsCss, '.gateway-identity-gear:hover', [
96+
expectRule(ownerCss, '.gateway-identity-gear:hover', [
9797
'background: var(--color-token-row-hover)',
9898
]);
9999
});
@@ -122,3 +122,26 @@ test('gateway identity keeps Codex icon scale while preserving status semantics'
122122
);
123123
assert.ok(!source.includes('gateway-identity-status'));
124124
});
125+
126+
test('gateway switcher recipe is always loaded and has one stylesheet owner', () => {
127+
const entryCss = read('styles.css');
128+
assert.equal(
129+
entryCss.match(/@import "\.\/styles\/gateway-status\.css";/g)?.length,
130+
1,
131+
'the renderer entrypoint must import the gateway owner stylesheet exactly once',
132+
);
133+
assert.ok(
134+
!entryCss.includes('workflows.css'),
135+
'app-shell gateway chrome must not depend on the removed workflow feature',
136+
);
137+
138+
const gatewaySelector = /\.(?:gateway-identity|gateway-switcher|gateway-row-glyph|gateway-glyph-badge)(?:[-\w]*)/;
139+
const offenders = readdirSync(stylesDir)
140+
.filter((fileName) => fileName.endsWith('.css') && fileName !== 'gateway-status.css')
141+
.filter((fileName) => gatewaySelector.test(read(`styles/${fileName}`)));
142+
assert.deepEqual(
143+
offenders,
144+
[],
145+
`gateway switcher selectors escaped their owner stylesheet: ${offenders.join(', ')}`,
146+
);
147+
});

desktop/garyx-desktop/src/renderer/src/styles/gateway-status.css

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,45 @@
1-
/* Gateway presence glyph badge. */
1+
/* Bottom-left gateway identity / switcher and shared presence glyphs.
2+
*
3+
* This is app-shell chrome, so its complete layout recipe must stay in this
4+
* always-loaded owner stylesheet. Do not move pieces into removable feature
5+
* stylesheets: deleting the old workflow feature once left the trigger with
6+
* only its child styles and collapsed it into a vertical, content-width stack.
7+
*/
8+
9+
.gateway-identity-bar {
10+
display: flex;
11+
align-items: center;
12+
gap: 8px;
13+
width: 100%;
14+
height: 46px;
15+
min-width: 0;
16+
padding: 0 8px;
17+
}
18+
19+
.gateway-identity-main {
20+
display: flex;
21+
align-items: center;
22+
gap: 8px;
23+
flex: 1;
24+
height: 29px;
25+
min-width: 0;
26+
padding: 0 8px;
27+
border: none;
28+
border-radius: 10px;
29+
background: transparent;
30+
color: var(--color-token-text-primary);
31+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
32+
font-size: 14px;
33+
font-weight: 445;
34+
line-height: 21px;
35+
text-align: left;
36+
cursor: default;
37+
}
38+
39+
.gateway-identity-main:hover,
40+
.gateway-identity-main[data-state='open'] {
41+
background: var(--color-token-row-hover);
42+
}
243

344
/* Avatar-like gateway glyph with a presence badge on its corner. */
445
.gateway-identity-glyph,
@@ -68,3 +109,75 @@
68109
.gateway-glyph-badge.is-offline {
69110
background: var(--color-token-error-foreground);
70111
}
112+
113+
.gateway-identity-copy {
114+
flex: 1;
115+
min-width: 0;
116+
}
117+
118+
.gateway-identity-name {
119+
display: block;
120+
min-width: 0;
121+
overflow: hidden;
122+
color: var(--color-token-text-primary);
123+
font-size: inherit;
124+
font-weight: inherit;
125+
line-height: inherit;
126+
text-overflow: ellipsis;
127+
white-space: nowrap;
128+
}
129+
130+
.gateway-identity-gear {
131+
flex: none;
132+
display: inline-flex;
133+
align-items: center;
134+
justify-content: center;
135+
gap: 4px;
136+
width: 32px;
137+
height: 32px;
138+
padding: 4px 0;
139+
border: 1px solid transparent;
140+
border-radius: 10px;
141+
background: transparent;
142+
color: var(--color-token-description-foreground);
143+
font: 445 16px/24px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
144+
cursor: default;
145+
}
146+
147+
.gateway-identity-gear:hover {
148+
background: var(--color-token-row-hover);
149+
color: var(--color-token-description-foreground);
150+
}
151+
152+
.gateway-switcher-popover {
153+
/* Hug the identity bar below. Surface identity comes from the shared
154+
.menu-popover-surface recipe in styles/menus.css. */
155+
width: var(--radix-popover-trigger-width);
156+
min-width: 208px;
157+
padding: 8px;
158+
}
159+
160+
.gateway-switcher-list {
161+
display: grid;
162+
gap: 2px;
163+
}
164+
165+
/* Row visuals come from the shared two-line menu variant in styles/menus.css;
166+
only gateway-specific chrome lives here. */
167+
.gateway-switcher-item-state {
168+
flex: none;
169+
color: var(--color-token-description-foreground);
170+
font-size: var(--text-xs-plus);
171+
}
172+
173+
.gateway-switcher-check {
174+
flex: none;
175+
color: var(--color-token-text-primary);
176+
}
177+
178+
.gateway-switcher-error {
179+
margin: 4px 0 0;
180+
padding: 0 10px;
181+
color: #9b3d3d;
182+
font-size: 11.5px;
183+
}

desktop/garyx-desktop/src/renderer/src/styles/task-forest.css

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -594,76 +594,3 @@
594594
transition: none;
595595
}
596596
}
597-
598-
.gateway-identity-copy {
599-
flex: 1;
600-
min-width: 0;
601-
}
602-
603-
.gateway-identity-name {
604-
display: block;
605-
min-width: 0;
606-
overflow: hidden;
607-
color: var(--color-token-text-primary);
608-
font-size: inherit;
609-
font-weight: inherit;
610-
line-height: inherit;
611-
text-overflow: ellipsis;
612-
white-space: nowrap;
613-
}
614-
615-
.gateway-identity-gear {
616-
flex: none;
617-
display: inline-flex;
618-
align-items: center;
619-
justify-content: center;
620-
gap: 4px;
621-
width: 32px;
622-
height: 32px;
623-
padding: 4px 0;
624-
border: 1px solid transparent;
625-
border-radius: 10px;
626-
background: transparent;
627-
color: var(--color-token-description-foreground);
628-
font: 445 16px/24px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
629-
cursor: default;
630-
}
631-
632-
.gateway-identity-gear:hover {
633-
background: var(--color-token-row-hover);
634-
color: var(--color-token-description-foreground);
635-
}
636-
637-
.gateway-switcher-popover {
638-
/* Hug the identity bar below: same width as the trigger, so the popover
639-
and the bar read as one column even when the sidebar is resized.
640-
Surface identity comes from .menu-popover-surface (styles/menus.css). */
641-
width: var(--radix-popover-trigger-width);
642-
min-width: 208px;
643-
padding: 8px;
644-
}
645-
646-
.gateway-switcher-list {
647-
display: grid;
648-
gap: 2px;
649-
}
650-
651-
/* Row visuals come from the shared two-line menu variant in styles/menus.css
652-
(.menu-item-two-line and friends); only gateway-specific chrome is local. */
653-
.gateway-switcher-item-state {
654-
flex: none;
655-
color: var(--color-token-description-foreground);
656-
font-size: var(--text-xs-plus);
657-
}
658-
659-
.gateway-switcher-check {
660-
flex: none;
661-
color: var(--color-token-text-primary);
662-
}
663-
664-
.gateway-switcher-error {
665-
margin: 4px 0 0;
666-
padding: 0 10px;
667-
color: #9b3d3d;
668-
font-size: 11.5px;
669-
}

docs/agents/desktop-ui.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
- Menu shortcut hints use the shared `DropdownMenuShortcut` component, not
3333
local spans.
3434

35+
## App-Shell Chrome Ownership
36+
37+
- Global shell components must own one complete, always-loaded CSS recipe.
38+
Do not scatter required layout declarations across unrelated feature files.
39+
- The bottom-left gateway identity, settings control, presence glyph, and
40+
switcher popover are owned by `styles/gateway-status.css`. A contract test
41+
must fail if those selectors escape that file or its root import disappears.
42+
- Deleting an optional feature must not remove styles required by navigation,
43+
the conversation header, the sidebar footer, or other persistent chrome.
44+
3545
## Responsive Conversation Layout
3646

3747
- Keep the desktop app shell horizontal at every supported window width.

0 commit comments

Comments
 (0)