mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
feat(desktop): compact sidebar and topbar chrome
This commit is contained in:
parent
6c59e2bc96
commit
4e64dae91c
4 changed files with 382 additions and 79 deletions
61
.qwen/e2e-tests/electron-desktop/sidebar-topbar-density.md
Normal file
61
.qwen/e2e-tests/electron-desktop/sidebar-topbar-density.md
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Electron Desktop E2E: Sidebar and Topbar Density
|
||||||
|
|
||||||
|
- Slice: Sidebar and Topbar Chrome Density Pass
|
||||||
|
- Date: 2026-04-26
|
||||||
|
- Executable harness: `packages/desktop/scripts/e2e-cdp-smoke.mjs`
|
||||||
|
- Command:
|
||||||
|
`cd packages/desktop && npm run e2e:cdp`
|
||||||
|
- Result: passed
|
||||||
|
- Passing artifact directory:
|
||||||
|
`.qwen/e2e-tests/electron-desktop/artifacts/2026-04-26T03-36-28-286Z/`
|
||||||
|
- Earlier failed artifact directory:
|
||||||
|
`.qwen/e2e-tests/electron-desktop/artifacts/2026-04-26T03-35-53-527Z/`
|
||||||
|
|
||||||
|
## Scenario Steps
|
||||||
|
|
||||||
|
1. Launch the real Electron app with isolated HOME, runtime, user-data, and a
|
||||||
|
temporary fake Git workspace.
|
||||||
|
2. Open the fake project through the desktop project picker flow.
|
||||||
|
3. Send a prompt from the composer, approve the deterministic command request,
|
||||||
|
and wait for the fake ACP assistant result.
|
||||||
|
4. Assert sidebar and topbar geometry, typography, containment, and overflow at
|
||||||
|
the default `1240x820` Electron window.
|
||||||
|
5. Continue the existing branch creation/switching, review drawer, compact
|
||||||
|
review, settings, terminal, discard safety, and commit smoke workflows.
|
||||||
|
|
||||||
|
## Assertions
|
||||||
|
|
||||||
|
- Sidebar width is `252` px, below the previous `272` px baseline.
|
||||||
|
- Sidebar app/footer action rows are `28` px tall, below the previous `32` px
|
||||||
|
baseline.
|
||||||
|
- Project row height is `32.6328125` px and thread row height is `32` px, below
|
||||||
|
the previous `39.75`/`36` px baselines.
|
||||||
|
- Sidebar action/project/thread text is `12` px; section headings are `10` px;
|
||||||
|
project/thread meta text is `9.5` px.
|
||||||
|
- Topbar height is `50` px, below the previous `54` px baseline.
|
||||||
|
- Topbar action buttons are `28x28`; runtime status is `65.6328125x28`.
|
||||||
|
- Long branch and Git status text remain present, clipped, and contained.
|
||||||
|
- No sidebar/topbar horizontal overflow, console errors, or failed local
|
||||||
|
requests are recorded.
|
||||||
|
|
||||||
|
## Failure and Fix
|
||||||
|
|
||||||
|
The first run failed because project/thread button parents still inherited the
|
||||||
|
root `14` px font even though their visible child labels had been compacted.
|
||||||
|
The stylesheet now sets the project and thread row parent font size explicitly,
|
||||||
|
and the harness records both row parent and child text metrics.
|
||||||
|
|
||||||
|
## Artifacts
|
||||||
|
|
||||||
|
- `sidebar-app-rail.json`
|
||||||
|
- `topbar-context-fidelity.json`
|
||||||
|
- `topbar-context-fidelity.png`
|
||||||
|
- `initial-workspace.png`
|
||||||
|
- `electron.log`
|
||||||
|
- `summary.json`
|
||||||
|
|
||||||
|
## Known Uncovered Risk
|
||||||
|
|
||||||
|
This pass is a density/fidelity slice, not a navigation redesign. The harness
|
||||||
|
still covers one project/thread pair; a future visual regression path should add
|
||||||
|
several recent projects and longer localized thread titles at compact width.
|
||||||
|
|
@ -22,6 +22,116 @@ execution order, verification, decisions, and remaining work.
|
||||||
|
|
||||||
## Codex Alignment Progress
|
## Codex Alignment Progress
|
||||||
|
|
||||||
|
### Completed Slice: Sidebar and Topbar Chrome Density Pass
|
||||||
|
|
||||||
|
Status: completed in iteration 26.
|
||||||
|
|
||||||
|
Goal: tighten the remaining oversized sidebar and topbar chrome so the first
|
||||||
|
viewport reads closer to the compact `home.jpg` workbench instead of a heavy
|
||||||
|
dashboard shell.
|
||||||
|
|
||||||
|
User-visible value: users get more room for the conversation and task surfaces,
|
||||||
|
while project, thread, branch, model, review, refresh, and settings controls
|
||||||
|
remain visible, readable, and safely contained.
|
||||||
|
|
||||||
|
Expected files:
|
||||||
|
|
||||||
|
- `packages/desktop/src/renderer/styles.css`
|
||||||
|
- `packages/desktop/scripts/e2e-cdp-smoke.mjs`
|
||||||
|
- `.qwen/e2e-tests/electron-desktop/sidebar-topbar-density.md`
|
||||||
|
- `design/qwen-code-electron-desktop-implementation-plan.md`
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- The desktop sidebar is narrower and uses shorter app action, project, and
|
||||||
|
thread rows without horizontal overflow.
|
||||||
|
- Sidebar app action, project, thread, section, and footer typography moves to a
|
||||||
|
restrained workbench scale while preserving readable labels and accessible
|
||||||
|
button names.
|
||||||
|
- The topbar height, action buttons, runtime pill, and status text are slimmer
|
||||||
|
and remain contained with a deliberately long branch name.
|
||||||
|
- The topbar no longer increases body scroll width or hides conversation,
|
||||||
|
review, settings, terminal, branch, or Git status actions.
|
||||||
|
- No raw project paths, ACP/session IDs, or debug state are introduced into the
|
||||||
|
main sidebar or topbar text.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- Unit/component test command:
|
||||||
|
`cd packages/desktop && SHELL=/bin/bash npx vitest run src/renderer/components/layout/WorkspacePage.test.tsx`
|
||||||
|
- Syntax command: `node --check packages/desktop/scripts/e2e-cdp-smoke.mjs`
|
||||||
|
- Build/typecheck/lint commands:
|
||||||
|
`cd packages/desktop && npm run typecheck && npm run lint && npm run build`
|
||||||
|
- Real Electron harness:
|
||||||
|
`cd packages/desktop && npm run e2e:cdp`
|
||||||
|
- Harness path: `packages/desktop/scripts/e2e-cdp-smoke.mjs`
|
||||||
|
- E2E scenario steps: launch real Electron with isolated HOME/runtime/user-data
|
||||||
|
and fake ACP, open the fake Git project, send a prompt, approve the command,
|
||||||
|
wait for the assistant result, then assert sidebar and topbar chrome geometry
|
||||||
|
at the default viewport before continuing the existing branch, review,
|
||||||
|
settings, terminal, discard safety, and commit workflows.
|
||||||
|
- E2E assertions: sidebar width is below the previous 272 px baseline, top app
|
||||||
|
action rows are below the previous 32 px baseline, project/thread rows are
|
||||||
|
below the previous 39.75/36 px baselines, section headings and row text use a
|
||||||
|
smaller font scale, topbar height is below the previous 54 px baseline,
|
||||||
|
action buttons are below the previous 30 px baseline, runtime status is below
|
||||||
|
the previous 30 px height, long branch/status text remains contained, and no
|
||||||
|
console errors or failed local requests are recorded.
|
||||||
|
- Diagnostic artifacts: `sidebar-app-rail.json`,
|
||||||
|
`topbar-context-fidelity.json`, `topbar-context-fidelity.png`, Electron log,
|
||||||
|
and summary JSON under `.qwen/e2e-tests/electron-desktop/artifacts/`.
|
||||||
|
- Required skills applied: `brainstorming` for choosing the narrow prototype
|
||||||
|
fidelity slice without asking for routine product decisions,
|
||||||
|
`frontend-design` for prototype-constrained density and hierarchy, and
|
||||||
|
`electron-desktop-dev` for real Electron CDP verification.
|
||||||
|
|
||||||
|
Notes and decisions:
|
||||||
|
|
||||||
|
- The prototype remains the constraint: this pass should refine density and
|
||||||
|
hierarchy, not introduce a new navigation model, new routes, or new branding.
|
||||||
|
- This slice deliberately avoids workflow logic so model configuration can
|
||||||
|
resume after the first-viewport chrome is less visually dominant.
|
||||||
|
- The sidebar grid now uses a 252 px rail, 28 px app/footer actions, and
|
||||||
|
roughly 32 px project/thread rows. Project/thread button parents now carry
|
||||||
|
the same 12 px font scale as the visible row titles so inherited styles do
|
||||||
|
not regress unnoticed.
|
||||||
|
- The topbar now uses a 50 px workbench row, 28 px icon buttons, a 28 px runtime
|
||||||
|
status pill, and 10.5 px context text. Long branch and Git status text remain
|
||||||
|
in the DOM for accessibility but are visually contained.
|
||||||
|
- The CDP harness now records sidebar/topbar font metrics as well as geometry,
|
||||||
|
so future fidelity work cannot accidentally reintroduce the heavier 272 px
|
||||||
|
sidebar, 54 px topbar, 32 px action rows, or 30 px topbar buttons.
|
||||||
|
|
||||||
|
Verification results:
|
||||||
|
|
||||||
|
- `node --check packages/desktop/scripts/e2e-cdp-smoke.mjs` passed.
|
||||||
|
- `git diff --check` passed.
|
||||||
|
- `cd packages/desktop && SHELL=/bin/bash npx vitest run src/renderer/components/layout/WorkspacePage.test.tsx`
|
||||||
|
passed with 15 tests.
|
||||||
|
- `cd packages/desktop && npm run typecheck` passed.
|
||||||
|
- `cd packages/desktop && npm run lint` passed.
|
||||||
|
- `cd packages/desktop && npm run build` passed after the final CSS fix.
|
||||||
|
- `cd packages/desktop && npm run e2e:cdp` first failed at
|
||||||
|
`.qwen/e2e-tests/electron-desktop/artifacts/2026-04-26T03-35-53-527Z/`
|
||||||
|
because project/thread row button parents still inherited the root 14 px font
|
||||||
|
even though the visible labels were compact. The CSS now sets the row parent
|
||||||
|
font size explicitly.
|
||||||
|
- `cd packages/desktop && npm run e2e:cdp` then passed through real Electron at
|
||||||
|
`.qwen/e2e-tests/electron-desktop/artifacts/2026-04-26T03-36-28-286Z/`.
|
||||||
|
- Key recorded metrics from the passing run: sidebar width `252` px, app/footer
|
||||||
|
action rows `28` px tall, project row `32.6328125` px tall, thread row `32`
|
||||||
|
px tall, sidebar row font `12` px, sidebar heading font `10` px, topbar height
|
||||||
|
`50` px, topbar action buttons `28x28`, runtime status
|
||||||
|
`65.6328125x28`, topbar context font `10.5` px, no document overflow, no
|
||||||
|
console errors, and no failed local requests.
|
||||||
|
|
||||||
|
Next work:
|
||||||
|
|
||||||
|
- Resume the model configuration workflow from the composer model picker and
|
||||||
|
settings entry now that the first-viewport chrome is less visually dominant.
|
||||||
|
- Continue prototype fidelity by reducing remaining message/file chip button
|
||||||
|
weight only where screenshots show it crowding the conversation.
|
||||||
|
|
||||||
### Completed Slice: Conversation Message Typography Density Pass
|
### Completed Slice: Conversation Message Typography Density Pass
|
||||||
|
|
||||||
Status: completed in iteration 25.
|
Status: completed in iteration 25.
|
||||||
|
|
|
||||||
|
|
@ -667,6 +667,17 @@ async function assertSidebarAppRail(fileName) {
|
||||||
};
|
};
|
||||||
const overflows = (element) =>
|
const overflows = (element) =>
|
||||||
Boolean(element && element.scrollWidth > element.clientWidth + 4);
|
Boolean(element && element.scrollWidth > element.clientWidth + 4);
|
||||||
|
const styleFor = (element) => {
|
||||||
|
if (!element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const style = window.getComputedStyle(element);
|
||||||
|
return {
|
||||||
|
fontSize: Number.parseFloat(style.fontSize),
|
||||||
|
fontWeight: Number.parseFloat(style.fontWeight),
|
||||||
|
lineHeight: Number.parseFloat(style.lineHeight)
|
||||||
|
};
|
||||||
|
};
|
||||||
const sidebar = document.querySelector('[data-testid="project-sidebar"]');
|
const sidebar = document.querySelector('[data-testid="project-sidebar"]');
|
||||||
const appActions = document.querySelector('[data-testid="sidebar-app-actions"]');
|
const appActions = document.querySelector('[data-testid="sidebar-app-actions"]');
|
||||||
const footerSettings = document.querySelector(
|
const footerSettings = document.querySelector(
|
||||||
|
|
@ -684,12 +695,29 @@ async function assertSidebarAppRail(fileName) {
|
||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
text: row.textContent.trim(),
|
text: row.textContent.trim(),
|
||||||
|
className: row.className,
|
||||||
rect: rectFor(row),
|
rect: rectFor(row),
|
||||||
|
style: styleFor(row),
|
||||||
scrollWidth: row.scrollWidth,
|
scrollWidth: row.scrollWidth,
|
||||||
clientWidth: row.clientWidth,
|
clientWidth: row.clientWidth,
|
||||||
overflows: overflows(row)
|
overflows: overflows(row)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const headingStyles = [
|
||||||
|
...document.querySelectorAll('.sidebar-section-heading h2')
|
||||||
|
].map((heading) => styleFor(heading));
|
||||||
|
const projectTitleStyles = [
|
||||||
|
...document.querySelectorAll('.project-row-copy span')
|
||||||
|
].map((title) => styleFor(title));
|
||||||
|
const projectMetaStyles = [
|
||||||
|
...document.querySelectorAll('.project-row-copy small')
|
||||||
|
].map((meta) => styleFor(meta));
|
||||||
|
const threadTitleStyles = [
|
||||||
|
...document.querySelectorAll('.session-row-title')
|
||||||
|
].map((title) => styleFor(title));
|
||||||
|
const threadMetaStyles = [
|
||||||
|
...document.querySelectorAll('.session-row-meta')
|
||||||
|
].map((meta) => styleFor(meta));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
viewport: {
|
viewport: {
|
||||||
|
|
@ -712,6 +740,11 @@ async function assertSidebarAppRail(fileName) {
|
||||||
footerSettings?.textContent.trim() ||
|
footerSettings?.textContent.trim() ||
|
||||||
'',
|
'',
|
||||||
rows,
|
rows,
|
||||||
|
headingStyles,
|
||||||
|
projectTitleStyles,
|
||||||
|
projectMetaStyles,
|
||||||
|
threadTitleStyles,
|
||||||
|
threadMetaStyles,
|
||||||
sidebarText: sidebar?.innerText ?? '',
|
sidebarText: sidebar?.innerText ?? '',
|
||||||
overflows: {
|
overflows: {
|
||||||
sidebar: overflows(sidebar),
|
sidebar: overflows(sidebar),
|
||||||
|
|
@ -760,7 +793,7 @@ async function assertSidebarAppRail(fileName) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metrics.sidebar.width < 236 || metrics.sidebar.width > 320) {
|
if (metrics.sidebar.width < 236 || metrics.sidebar.width > 260) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Sidebar width is no longer compact: ${metrics.sidebar.width}`,
|
`Sidebar width is no longer compact: ${metrics.sidebar.width}`,
|
||||||
);
|
);
|
||||||
|
|
@ -780,7 +813,18 @@ async function assertSidebarAppRail(fileName) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tallRows = metrics.rows.filter((row) => row.rect.height > 44);
|
const tallRows = metrics.rows.filter((row) => {
|
||||||
|
if (row.className.includes('sidebar-action-row')) {
|
||||||
|
return row.rect.height > 30;
|
||||||
|
}
|
||||||
|
if (row.className.includes('session-row')) {
|
||||||
|
return row.rect.height > 34;
|
||||||
|
}
|
||||||
|
if (row.className.includes('project-row')) {
|
||||||
|
return row.rect.height > 38;
|
||||||
|
}
|
||||||
|
return row.rect.height > 38;
|
||||||
|
});
|
||||||
if (tallRows.length > 0) {
|
if (tallRows.length > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Sidebar rows are too tall for the compact rail: ${JSON.stringify(
|
`Sidebar rows are too tall for the compact rail: ${JSON.stringify(
|
||||||
|
|
@ -804,6 +848,54 @@ async function assertSidebarAppRail(fileName) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oversizedRowText = metrics.rows.filter(
|
||||||
|
(row) => row.style && row.style.fontSize > 12.5,
|
||||||
|
);
|
||||||
|
if (oversizedRowText.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Sidebar row typography regressed: ${JSON.stringify(oversizedRowText)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oversizedHeadings = metrics.headingStyles.filter(
|
||||||
|
(style) => style && style.fontSize > 10.5,
|
||||||
|
);
|
||||||
|
if (oversizedHeadings.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Sidebar heading typography regressed: ${JSON.stringify(
|
||||||
|
oversizedHeadings,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oversizedProjectTitles = metrics.projectTitleStyles.filter(
|
||||||
|
(style) => style && style.fontSize > 12.5,
|
||||||
|
);
|
||||||
|
const oversizedProjectMeta = metrics.projectMetaStyles.filter(
|
||||||
|
(style) => style && style.fontSize > 10,
|
||||||
|
);
|
||||||
|
const oversizedThreadTitles = metrics.threadTitleStyles.filter(
|
||||||
|
(style) => style && style.fontSize > 12.5,
|
||||||
|
);
|
||||||
|
const oversizedThreadMeta = metrics.threadMetaStyles.filter(
|
||||||
|
(style) => style && style.fontSize > 10,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
oversizedProjectTitles.length > 0 ||
|
||||||
|
oversizedProjectMeta.length > 0 ||
|
||||||
|
oversizedThreadTitles.length > 0 ||
|
||||||
|
oversizedThreadMeta.length > 0
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Sidebar project/thread text scale regressed: ${JSON.stringify({
|
||||||
|
oversizedProjectTitles,
|
||||||
|
oversizedProjectMeta,
|
||||||
|
oversizedThreadTitles,
|
||||||
|
oversizedThreadMeta,
|
||||||
|
})}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
metrics.sidebarText.includes('session-e2e') ||
|
metrics.sidebarText.includes('session-e2e') ||
|
||||||
metrics.sidebarText.includes('/tmp/') ||
|
metrics.sidebarText.includes('/tmp/') ||
|
||||||
|
|
@ -857,7 +949,10 @@ async function assertTopbarContextFidelity(fileName) {
|
||||||
borderRightWidth: Number.parseFloat(style.borderRightWidth),
|
borderRightWidth: Number.parseFloat(style.borderRightWidth),
|
||||||
borderBottomWidth: Number.parseFloat(style.borderBottomWidth),
|
borderBottomWidth: Number.parseFloat(style.borderBottomWidth),
|
||||||
borderLeftWidth: Number.parseFloat(style.borderLeftWidth),
|
borderLeftWidth: Number.parseFloat(style.borderLeftWidth),
|
||||||
borderTopAlpha: alphaFromColor(style.borderTopColor)
|
borderTopAlpha: alphaFromColor(style.borderTopColor),
|
||||||
|
fontSize: Number.parseFloat(style.fontSize),
|
||||||
|
fontWeight: Number.parseFloat(style.fontWeight),
|
||||||
|
lineHeight: Number.parseFloat(style.lineHeight)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const escapes = (inner, outer) =>
|
const escapes = (inner, outer) =>
|
||||||
|
|
@ -909,6 +1004,8 @@ async function assertTopbarContextFidelity(fileName) {
|
||||||
topbar: topbarRect,
|
topbar: topbarRect,
|
||||||
titleStack: rectFor(titleStack),
|
titleStack: rectFor(titleStack),
|
||||||
title: rectFor(title),
|
title: rectFor(title),
|
||||||
|
titleHeadingStyle: styleFor(title?.querySelector('h2')),
|
||||||
|
titleProjectStyle: styleFor(title?.querySelector('span')),
|
||||||
context: contextRect,
|
context: contextRect,
|
||||||
runtimeStatus: rectFor(runtimeStatus),
|
runtimeStatus: rectFor(runtimeStatus),
|
||||||
runtimeStatusText: runtimeStatus?.textContent.trim() ?? '',
|
runtimeStatusText: runtimeStatus?.textContent.trim() ?? '',
|
||||||
|
|
@ -952,7 +1049,7 @@ async function assertTopbarContextFidelity(fileName) {
|
||||||
throw new Error('Topbar should not render legacy meta pills or tabs.');
|
throw new Error('Topbar should not render legacy meta pills or tabs.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metrics.topbar.height < 48 || metrics.topbar.height > 58) {
|
if (metrics.topbar.height < 46 || metrics.topbar.height > 52) {
|
||||||
throw new Error(`Topbar is no longer slim: ${metrics.topbar.height}`);
|
throw new Error(`Topbar is no longer slim: ${metrics.topbar.height}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -998,16 +1095,50 @@ async function assertTopbarContextFidelity(fileName) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metrics.runtimeStatus.height > 32 || metrics.runtimeStatus.width > 76) {
|
if (metrics.titleHeadingStyle?.fontSize > 13.5) {
|
||||||
|
throw new Error(
|
||||||
|
`Topbar title typography regressed: ${JSON.stringify(
|
||||||
|
metrics.titleHeadingStyle,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metrics.titleProjectStyle?.fontSize > 11) {
|
||||||
|
throw new Error(
|
||||||
|
`Topbar project typography regressed: ${JSON.stringify(
|
||||||
|
metrics.titleProjectStyle,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const oversizedContextText = metrics.contextItems.filter(
|
||||||
|
(item) => item.style.fontSize > 10.75,
|
||||||
|
);
|
||||||
|
if (oversizedContextText.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Topbar context typography regressed: ${JSON.stringify(
|
||||||
|
oversizedContextText,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
metrics.runtimeStatus.height > 29 ||
|
||||||
|
metrics.runtimeStatus.width > 72 ||
|
||||||
|
metrics.runtimeStatusStyle.fontSize > 10.75
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Runtime status should stay compact: ${JSON.stringify(
|
`Runtime status should stay compact: ${JSON.stringify(
|
||||||
metrics.runtimeStatus,
|
{
|
||||||
|
rect: metrics.runtimeStatus,
|
||||||
|
style: metrics.runtimeStatusStyle,
|
||||||
|
},
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oversizedActions = metrics.actionRects.filter(
|
const oversizedActions = metrics.actionRects.filter(
|
||||||
(action) => action.rect.width > 34 || action.rect.height > 34,
|
(action) => action.rect.width > 29 || action.rect.height > 29,
|
||||||
);
|
);
|
||||||
if (oversizedActions.length > 0) {
|
if (oversizedActions.length > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ summary:focus-visible {
|
||||||
|
|
||||||
.desktop-shell {
|
.desktop-shell {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 272px minmax(0, 1fr);
|
grid-template-columns: 252px minmax(0, 1fr);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -96,9 +96,9 @@ summary:focus-visible {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 9px;
|
gap: 7px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 12px 10px;
|
padding: 10px 8px;
|
||||||
border-right: 1px solid var(--line);
|
border-right: 1px solid var(--line);
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(20, 26, 51, 0.98), rgba(13, 17, 32, 0.98)),
|
linear-gradient(180deg, rgba(20, 26, 51, 0.98), rgba(13, 17, 32, 0.98)),
|
||||||
|
|
@ -123,21 +123,21 @@ summary:focus-visible {
|
||||||
.sidebar-app-actions {
|
.sidebar-app-actions {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-action-row {
|
.sidebar-action-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 22px minmax(0, 1fr);
|
grid-template-columns: 20px minmax(0, 1fr);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 32px;
|
min-height: 28px;
|
||||||
gap: 8px;
|
gap: 7px;
|
||||||
padding: 0 8px;
|
padding: 0 7px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: rgba(225, 232, 242, 0.72);
|
color: rgba(225, 232, 242, 0.72);
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 680;
|
font-weight: 660;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition:
|
transition:
|
||||||
|
|
@ -152,8 +152,8 @@ summary:focus-visible {
|
||||||
|
|
||||||
.sidebar-action-row svg {
|
.sidebar-action-row svg {
|
||||||
display: block;
|
display: block;
|
||||||
width: 16px;
|
width: 14px;
|
||||||
height: 16px;
|
height: 14px;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +193,7 @@ summary:focus-visible {
|
||||||
.sidebar-section-heading span {
|
.sidebar-section-heading span {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
font-size: 11px;
|
font-size: 10px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
@ -203,8 +203,8 @@ summary:focus-visible {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
min-height: 22px;
|
min-height: 20px;
|
||||||
padding: 4px 8px 0;
|
padding: 3px 7px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-section-heading span {
|
.sidebar-section-heading span {
|
||||||
|
|
@ -213,11 +213,11 @@ summary:focus-visible {
|
||||||
|
|
||||||
.empty-row,
|
.empty-row,
|
||||||
.workspace-path {
|
.workspace-path {
|
||||||
min-height: 34px;
|
min-height: 30px;
|
||||||
padding: 8px 2px;
|
padding: 7px 2px;
|
||||||
color: rgba(225, 232, 228, 0.52);
|
color: rgba(225, 232, 228, 0.52);
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 680;
|
font-weight: 650;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-path {
|
.workspace-path {
|
||||||
|
|
@ -289,11 +289,12 @@ summary:focus-visible {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 36px;
|
min-height: 32px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: rgba(233, 238, 235, 0.86);
|
color: rgba(233, 238, 235, 0.86);
|
||||||
|
font-size: 12px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition:
|
transition:
|
||||||
background 140ms ease,
|
background 140ms ease,
|
||||||
|
|
@ -301,14 +302,14 @@ summary:focus-visible {
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-row {
|
.project-row {
|
||||||
grid-template-columns: 24px minmax(0, 1fr);
|
grid-template-columns: 21px minmax(0, 1fr);
|
||||||
padding: 5px 8px 5px 6px;
|
padding: 3px 7px 3px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-row {
|
.session-row {
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
column-gap: 7px;
|
column-gap: 6px;
|
||||||
padding: 5px 8px 5px 28px;
|
padding: 4px 7px 4px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-row-active,
|
.project-row-active,
|
||||||
|
|
@ -320,8 +321,8 @@ summary:focus-visible {
|
||||||
.project-row-active::before,
|
.project-row-active::before,
|
||||||
.session-row-active::before {
|
.session-row-active::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 6px;
|
||||||
bottom: 7px;
|
bottom: 6px;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
|
|
@ -337,8 +338,8 @@ summary:focus-visible {
|
||||||
|
|
||||||
.project-row-icon {
|
.project-row-icon {
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
width: 16px;
|
width: 14px;
|
||||||
height: 16px;
|
height: 14px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
color: rgba(225, 232, 228, 0.82);
|
color: rgba(225, 232, 228, 0.82);
|
||||||
}
|
}
|
||||||
|
|
@ -351,7 +352,7 @@ summary:focus-visible {
|
||||||
|
|
||||||
.project-row-copy {
|
.project-row-copy {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-row-copy span,
|
.project-row-copy span,
|
||||||
|
|
@ -366,14 +367,14 @@ summary:focus-visible {
|
||||||
|
|
||||||
.project-row-copy span,
|
.project-row-copy span,
|
||||||
.session-row-title {
|
.session-row-title {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
font-weight: 660;
|
font-weight: 660;
|
||||||
line-height: 1.25;
|
line-height: 1.22;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-row-copy small {
|
.project-row-copy small {
|
||||||
color: rgba(225, 232, 228, 0.48);
|
color: rgba(225, 232, 228, 0.48);
|
||||||
font-size: 10px;
|
font-size: 9.5px;
|
||||||
font-weight: 680;
|
font-weight: 680;
|
||||||
letter-spacing: 0;
|
letter-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -382,15 +383,15 @@ summary:focus-visible {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
min-width: 32px;
|
min-width: 28px;
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
color: rgba(225, 232, 228, 0.52);
|
color: rgba(225, 232, 228, 0.52);
|
||||||
font-weight: 760;
|
font-weight: 760;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-open-icon {
|
.session-open-icon {
|
||||||
width: 15px;
|
width: 13px;
|
||||||
height: 15px;
|
height: 13px;
|
||||||
color: rgba(225, 232, 228, 0.48);
|
color: rgba(225, 232, 228, 0.48);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,17 +400,17 @@ summary:focus-visible {
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-ring {
|
.session-ring {
|
||||||
width: 13px;
|
width: 12px;
|
||||||
height: 13px;
|
height: 12px;
|
||||||
border: 2px solid rgba(225, 232, 228, 0.2);
|
border: 2px solid rgba(225, 232, 228, 0.2);
|
||||||
border-top-color: rgba(225, 232, 228, 0.5);
|
border-top-color: rgba(225, 232, 228, 0.5);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-row-meta {
|
.session-row-meta {
|
||||||
max-width: 48px;
|
max-width: 44px;
|
||||||
color: rgba(225, 232, 228, 0.54);
|
color: rgba(225, 232, 228, 0.54);
|
||||||
font-size: 10px;
|
font-size: 9.5px;
|
||||||
font-weight: 720;
|
font-weight: 720;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +418,7 @@ summary:focus-visible {
|
||||||
.sidebar-footer {
|
.sidebar-footer {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding-top: 6px;
|
padding-top: 5px;
|
||||||
border-top: 1px solid rgba(213, 224, 255, 0.08);
|
border-top: 1px solid rgba(213, 224, 255, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,7 +432,7 @@ summary:focus-visible {
|
||||||
|
|
||||||
.workbench {
|
.workbench {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 54px minmax(0, 1fr) auto;
|
grid-template-rows: 50px minmax(0, 1fr) auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
@ -439,16 +440,16 @@ summary:focus-visible {
|
||||||
}
|
}
|
||||||
|
|
||||||
.workbench-settings-open {
|
.workbench-settings-open {
|
||||||
grid-template-rows: 54px minmax(0, 1fr);
|
grid-template-rows: 50px minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 0 16px 0 20px;
|
padding: 0 14px 0 18px;
|
||||||
border-bottom: 1px solid var(--line);
|
border-bottom: 1px solid var(--line);
|
||||||
background: rgba(13, 16, 17, 0.74);
|
background: rgba(13, 16, 17, 0.74);
|
||||||
}
|
}
|
||||||
|
|
@ -456,22 +457,22 @@ summary:focus-visible {
|
||||||
.topbar-title-stack {
|
.topbar-title-stack {
|
||||||
display: grid;
|
display: grid;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
gap: 3px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-title {
|
.topbar-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-title h2 {
|
.topbar-title h2 {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-weight: 780;
|
font-weight: 780;
|
||||||
line-height: 1.15;
|
line-height: 1.12;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
@ -480,8 +481,8 @@ summary:focus-visible {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 11.5px;
|
font-size: 10.5px;
|
||||||
font-weight: 700;
|
font-weight: 680;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +491,7 @@ summary:focus-visible {
|
||||||
.topbar-actions {
|
.topbar-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 7px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-context {
|
.topbar-context {
|
||||||
|
|
@ -522,12 +523,12 @@ summary:focus-visible {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: 168px;
|
max-width: 168px;
|
||||||
gap: 5px;
|
gap: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: rgba(215, 225, 218, 0.58);
|
color: rgba(215, 225, 218, 0.58);
|
||||||
font-size: 11px;
|
font-size: 10.5px;
|
||||||
font-weight: 720;
|
font-weight: 700;
|
||||||
line-height: 16px;
|
line-height: 15px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,8 +546,8 @@ summary:focus-visible {
|
||||||
|
|
||||||
.topbar-branch-trigger svg {
|
.topbar-branch-trigger svg {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 13px;
|
width: 12px;
|
||||||
height: 13px;
|
height: 12px;
|
||||||
opacity: 0.72;
|
opacity: 0.72;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,8 +573,8 @@ summary:focus-visible {
|
||||||
|
|
||||||
.topbar-context-dot {
|
.topbar-context-dot {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 6px;
|
width: 5px;
|
||||||
height: 6px;
|
height: 5px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: rgba(117, 217, 156, 0.88);
|
background: rgba(117, 217, 156, 0.88);
|
||||||
box-shadow: 0 0 0 3px rgba(117, 217, 156, 0.08);
|
box-shadow: 0 0 0 3px rgba(117, 217, 156, 0.08);
|
||||||
|
|
@ -598,7 +599,7 @@ summary:focus-visible {
|
||||||
|
|
||||||
.topbar-branch-menu {
|
.topbar-branch-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 26px;
|
top: 24px;
|
||||||
left: 7px;
|
left: 7px;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
@ -778,8 +779,8 @@ summary:focus-visible {
|
||||||
.topbar-icon-button {
|
.topbar-icon-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 30px;
|
width: 28px;
|
||||||
height: 30px;
|
height: 28px;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border: 1px solid rgba(213, 224, 255, 0.12);
|
border: 1px solid rgba(213, 224, 255, 0.12);
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
|
|
@ -799,8 +800,8 @@ summary:focus-visible {
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-icon-button svg {
|
.topbar-icon-button svg {
|
||||||
width: 16px;
|
width: 14px;
|
||||||
height: 16px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-action-badge {
|
.topbar-action-badge {
|
||||||
|
|
@ -828,22 +829,22 @@ summary:focus-visible {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 62px;
|
min-width: 58px;
|
||||||
min-height: 30px;
|
min-height: 28px;
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
padding: 0 9px;
|
padding: 0 8px;
|
||||||
border: 1px solid rgba(213, 224, 255, 0.14);
|
border: 1px solid rgba(213, 224, 255, 0.14);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
font-size: 11px;
|
font-size: 10.5px;
|
||||||
font-weight: 820;
|
font-weight: 820;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-pill-dot {
|
.status-pill-dot {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 6px;
|
width: 5px;
|
||||||
height: 6px;
|
height: 5px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
opacity: 0.92;
|
opacity: 0.92;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue