);
@@ -54,12 +63,21 @@ export function ConnectionStatusBanner() {
lastReconnectAttempt !== null &&
now - lastReconnectAttempt >= SLOW_RECOVERY_HINT_MS &&
(connectionStatus === "reconnecting" || connectionStatus === "disconnected");
+ const stacked = showSlowRecoveryHint && isMobile;
+ const slowRecoveryHintText = isMobile ? SLOW_RECOVERY_HINT_TEXT_MOBILE : SLOW_RECOVERY_HINT_TEXT;
+ const className = [
+ "connection-banner",
+ isMobile ? "connection-banner--mobile" : null,
+ stacked ? "connection-banner--stacked" : null,
+ ]
+ .filter(Boolean)
+ .join(" ");
return (
-
-
连接已断开,正在重新连接...
+
+ 连接已断开,正在重新连接...
{showSlowRecoveryHint ? (
- 连接恢复较慢,可能是网络问题。如果长时间没有恢复,可以刷新页面。
+ {slowRecoveryHintText}
) : null}
);
diff --git a/packages/web/src/styles/base.css b/packages/web/src/styles/base.css
index 1e119dc92..6018e2255 100644
--- a/packages/web/src/styles/base.css
+++ b/packages/web/src/styles/base.css
@@ -22,8 +22,9 @@ html {
body {
font-family: var(--font-sans);
- font-size: var(--text-base);
- line-height: var(--leading-normal);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
color: var(--text-primary);
background-color: var(--bg-page);
overflow: hidden;
@@ -36,88 +37,93 @@ body {
}
/* ========== Typography ========== */
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- font-weight: var(--font-semibold);
- line-height: var(--leading-tight);
- color: var(--text-primary);
-}
-
h1 {
- font-size: var(--text-3xl);
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
+ color: var(--text-primary);
}
h2 {
- font-size: var(--text-2xl);
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
+ color: var(--text-primary);
}
h3 {
- font-size: var(--text-xl);
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
+ color: var(--text-primary);
}
h4 {
- font-size: var(--text-lg);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-app-title-weight);
+ color: var(--text-primary);
}
h5 {
- font-size: var(--text-base);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-app-title-weight);
+ color: var(--text-primary);
}
h6 {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
+ color: var(--text-primary);
}
p {
color: var(--text-secondary);
- line-height: var(--leading-normal);
+ line-height: var(--type-body-line-height);
}
.page-kicker,
-.section-kicker {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+.section-kicker,
+.section-title {
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-app-title-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
.page-title {
- font-size: var(--text-3xl);
- font-weight: var(--font-semibold);
- line-height: var(--leading-tight);
- color: var(--text-primary);
-}
-
-.section-title {
- font-size: var(--text-base);
- font-weight: var(--font-semibold);
- letter-spacing: 0.06em;
- text-transform: uppercase;
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
color: var(--text-primary);
}
.meta-text {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
.hint-text {
- font-size: var(--text-xs);
- line-height: 1.45;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-muted);
}
.mono-meta {
- font-family: var(--font-mono);
- font-size: var(--text-xs);
color: var(--text-muted);
}
+.mono-meta,
code,
pre,
kbd,
samp {
- font-family: var(--font-mono);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
code {
@@ -188,7 +194,9 @@ a:hover {
/* ========== Buttons Base ========== */
button {
font-family: var(--font-sans);
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
cursor: pointer;
border: none;
background: none;
@@ -205,7 +213,9 @@ input,
textarea,
select {
font-family: var(--font-sans);
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
background-color: var(--bg-input);
border: 1px solid var(--border);
@@ -650,12 +660,51 @@ textarea::placeholder {
display: flex;
align-items: center;
justify-content: center;
+ gap: var(--sp-2);
+ padding: 0 var(--sp-4);
background: var(--accent-amber);
color: var(--text-inverse);
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
z-index: var(--z-toast);
}
+.connection-banner__primary,
+.connection-banner__hint {
+ min-width: 0;
+}
+
+.connection-banner__hint {
+ opacity: 0.88;
+}
+
+.connection-banner--mobile {
+ min-height: calc(var(--sp-6) + var(--mobile-safe-top));
+ padding-top: var(--mobile-safe-top);
+ padding-right: calc(var(--mobile-safe-right) + var(--sp-3));
+ padding-left: calc(var(--mobile-safe-left) + var(--sp-3));
+}
+
+.connection-banner--stacked {
+ height: auto;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: center;
+ gap: 2px;
+ padding-top: calc(var(--mobile-safe-top) + var(--sp-1));
+ padding-right: calc(var(--mobile-safe-right) + var(--sp-3));
+ padding-bottom: var(--sp-1);
+ padding-left: calc(var(--mobile-safe-left) + var(--sp-3));
+}
+
+.connection-banner--stacked .connection-banner__primary,
+.connection-banner--stacked .connection-banner__hint {
+ width: 100%;
+ text-align: left;
+ overflow-wrap: anywhere;
+}
+
.connection-banner--error {
background: var(--color-error);
}
@@ -706,24 +755,27 @@ textarea::placeholder {
.app-loading-kicker {
margin-bottom: var(--sp-3);
color: var(--text-tertiary);
- font-size: 11px;
- font-weight: 600;
- letter-spacing: 0.12em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
.app-loading-title {
margin: 0;
color: var(--text-primary);
- font-size: clamp(28px, 4vw, 38px);
- font-weight: 600;
- letter-spacing: -0.03em;
+ font-size: var(--type-display-size);
+ line-height: var(--type-display-line-height);
+ font-weight: var(--type-display-weight);
+ letter-spacing: var(--type-display-letter-spacing);
}
.app-loading-desc {
margin-top: var(--sp-3);
color: var(--text-secondary);
- font-size: var(--text-sm);
- line-height: 1.7;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
max-width: 42ch;
}
diff --git a/packages/web/src/styles/base.theme.test.ts b/packages/web/src/styles/base.theme.test.ts
index 01ff6c57d..5e41d977b 100644
--- a/packages/web/src/styles/base.theme.test.ts
+++ b/packages/web/src/styles/base.theme.test.ts
@@ -77,12 +77,33 @@ describe("base.css desktop typography foundation", () => {
expect(tokensStylesheet).toContain("--desktop-modal-max-width-lg:");
});
- it("maps desktop heading helpers to the corrected typography tokens", () => {
- expect(getRuleBlock("h2")).toContain("font-size: var(--text-2xl)");
- expect(getRuleBlock(".page-kicker")).toContain("font-size: var(--text-xs)");
- expect(getRuleBlock(".page-title")).toContain("font-size: var(--text-3xl)");
- expect(getRuleBlock(".section-title")).toContain("font-size: var(--text-base)");
- expect(getRuleBlock(".hint-text")).toContain("color: var(--text-muted)");
- expect(getRuleBlock(".mono-meta")).toContain("font-family: var(--font-mono)");
+ it("maps base text elements onto semantic typography tokens", () => {
+ expect(getRuleBlock("body")).toContain("font-size: var(--type-body-size)");
+ expect(getRuleBlock("body")).toContain("line-height: var(--type-body-line-height)");
+ expect(getRuleBlock("body")).toContain("font-weight: var(--type-body-weight)");
+ expect(getRuleBlock("p")).toContain("line-height: var(--type-body-line-height)");
+
+ expect(getRuleBlock("button")).toContain("font-size: var(--type-body-strong-size)");
+ expect(getRuleBlock("button")).toContain("line-height: var(--type-body-strong-line-height)");
+ expect(getRuleBlock("input")).toContain("font-size: var(--type-body-strong-size)");
+ expect(getRuleBlock("textarea")).toContain("font-size: var(--type-body-strong-size)");
+ expect(getRuleBlock("select")).toContain("font-size: var(--type-body-strong-size)");
+ });
+
+ it("maps headings and helper text onto the new semantic hierarchy", () => {
+ expect(getRuleBlock("h1")).toContain("font-size: var(--type-page-title-size)");
+ expect(getRuleBlock("h2")).toContain("font-size: var(--type-section-title-size)");
+ expect(getRuleBlock("h3")).toContain("font-size: var(--type-app-title-size)");
+ expect(getRuleBlock("h4")).toContain("font-size: var(--type-body-strong-size)");
+ expect(getRuleBlock("h5")).toContain("font-size: var(--type-label-size)");
+ expect(getRuleBlock("h6")).toContain("font-size: var(--type-meta-size)");
+
+ expect(getRuleBlock(".page-kicker")).toContain("font-size: var(--type-kicker-size)");
+ expect(getRuleBlock(".page-title")).toContain("font-size: var(--type-page-title-size)");
+ expect(getRuleBlock(".section-title")).toContain("font-size: var(--type-kicker-size)");
+ expect(getRuleBlock(".meta-text")).toContain("font-size: var(--type-meta-size)");
+ expect(getRuleBlock(".hint-text")).toContain("font-size: var(--type-meta-size)");
+ expect(getRuleBlock(".mono-meta")).toContain("font-size: var(--type-code-inline-size)");
+ expect(getRuleBlock(".mono-meta")).toContain("font-family: var(--type-code-inline-family)");
});
});
diff --git a/packages/web/src/styles/components.css b/packages/web/src/styles/components.css
index 4b78fd525..149e30aa5 100644
--- a/packages/web/src/styles/components.css
+++ b/packages/web/src/styles/components.css
@@ -41,15 +41,18 @@
}
.command-palette-kicker {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
.command-palette-meta {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -69,7 +72,9 @@
flex: 1;
background: transparent;
border: none;
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
outline: none;
}
@@ -80,7 +85,9 @@
.command-palette-hint {
padding: var(--sp-2) var(--sp-4);
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
border-bottom: 1px solid var(--border);
}
@@ -122,22 +129,28 @@
}
.command-palette-item-label {
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.command-palette-item-desc {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
.command-palette-item-shortcut {
- font-size: var(--text-xs);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
color: var(--text-tertiary);
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
}
.command-palette-empty {
@@ -257,10 +270,11 @@
.mobile-select-sheet__section-title {
padding: 0 var(--sp-1);
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
@@ -347,17 +361,23 @@
}
.mobile-select-sheet__item-label {
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.mobile-select-sheet__item-description {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
.mobile-select-sheet__item-meta {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
@@ -366,7 +386,9 @@
border-radius: var(--radius-full);
background: var(--bg-hover);
color: var(--text-secondary);
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
flex-shrink: 0;
}
@@ -431,8 +453,9 @@
border-radius: 4px;
background: color-mix(in srgb, var(--bg-page) 82%, var(--bg-surface) 18%);
color: var(--text-primary);
- font-size: 12px;
- line-height: 1.2;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
box-shadow: none;
}
@@ -456,9 +479,11 @@
.mobile-select-sheet--command .mobile-select-sheet__section-title {
padding: 0 var(--sp-4);
margin-bottom: 4px;
- font-family: var(--font-mono);
- font-size: 10px;
- letter-spacing: 0.14em;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
color: color-mix(in srgb, var(--text-tertiary) 92%, var(--bg-panel));
}
@@ -553,23 +578,25 @@
}
.mobile-select-sheet--command .mobile-select-sheet__item-label {
- font-size: 12px;
- font-weight: var(--font-semibold);
- line-height: 1.3;
+ font-size: var(--type-body-strong-size);
+ font-weight: var(--type-body-strong-weight);
+ line-height: var(--type-body-strong-line-height);
letter-spacing: 0.01em;
}
.mobile-select-sheet--command .mobile-select-sheet__item-description {
color: var(--text-secondary);
- font-size: 11px;
- line-height: 1.3;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.mobile-select-sheet--command .mobile-select-sheet__item-meta {
color: var(--text-tertiary);
- font-family: var(--font-mono);
- font-size: 10px;
- line-height: 1.35;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
letter-spacing: 0.05em;
}
@@ -589,7 +616,9 @@
border: 1px solid color-mix(in srgb, var(--border) 64%, transparent);
background: transparent;
color: var(--text-tertiary);
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-select-sheet--command .mobile-select-sheet__item-side-action {
@@ -644,6 +673,14 @@
min-width: 0;
}
+.page-header--primary {
+ min-height: 56px;
+}
+
+.page-header--secondary {
+ min-height: 48px;
+}
+
.page-header__leading {
display: flex;
align-items: center;
@@ -665,24 +702,43 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
+.page-header--primary .page-header__kicker {
+ letter-spacing: var(--type-kicker-letter-spacing);
+}
+
.page-header__title {
margin: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-base);
- font-weight: 600;
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
color: var(--text-primary);
text-align: left;
}
+.page-header--primary .page-header__title {
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
+}
+
+.page-header--secondary .page-header__title {
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
+}
+
.page-header__back {
display: inline-flex;
align-items: center;
@@ -692,8 +748,9 @@
border: none;
background: transparent;
color: var(--text-secondary);
- font-size: var(--text-sm);
- font-weight: 500;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
flex-shrink: 0;
transition: all var(--duration-fast) var(--ease-out);
}
@@ -717,37 +774,6 @@
background: var(--bg-surface);
border-bottom: 1px solid var(--border);
}
-
-.settings-header__desktop {
- width: 100%;
- margin: 0;
- display: flex;
- align-items: center;
- justify-content: flex-start;
- gap: var(--sp-3);
-}
-
-.settings-header__back {
- flex-shrink: 0;
- min-height: 30px;
- font-size: var(--text-xs);
-}
-
-.settings-header__copy {
- min-width: 0;
- display: flex;
- flex: 0 1 auto;
- flex-direction: column;
- justify-content: flex-start;
- gap: 0;
-}
-
-.settings-header__title {
- margin: 0;
- font-size: 18px;
- line-height: 1.05;
-}
-
.settings-body {
display: flex;
flex: 1;
@@ -801,7 +827,9 @@
.settings-nav-label {
flex: 1;
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.settings-nav-arrow {
@@ -864,16 +892,19 @@
}
.settings-group-title {
- font-size: var(--text-base);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
margin-bottom: var(--sp-2);
text-transform: uppercase;
- letter-spacing: 0.06em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-primary);
}
.settings-group-desc {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
margin-bottom: var(--sp-4);
max-width: 60ch;
@@ -904,14 +935,16 @@
}
.settings-toggle-label {
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.settings-toggle-desc {
- font-size: var(--text-xs);
- line-height: 1.5;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
@@ -967,9 +1000,10 @@
}
.settings-info-label {
- font-size: var(--text-xs);
- font-weight: var(--font-medium);
- letter-spacing: 0.06em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-secondary);
}
@@ -981,7 +1015,9 @@
justify-content: center;
justify-self: end;
gap: var(--sp-1);
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-primary);
text-align: right;
max-width: 40ch;
@@ -1008,8 +1044,9 @@
.settings-status-hint {
color: var(--text-tertiary);
- font-size: var(--text-xs);
- line-height: 1.45;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.settings-link {
@@ -1135,14 +1172,18 @@
}
.settings-provider-mobile-entry__title {
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.settings-provider-mobile-entry__meta {
- font-size: var(--text-sm);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
}
.settings-provider-mobile-config-header {
@@ -1168,11 +1209,13 @@
.settings-config-label {
display: block;
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
margin-bottom: 0;
text-transform: uppercase;
- letter-spacing: 0.06em;
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.settings-config-field--inline .settings-config-label {
@@ -1197,9 +1240,10 @@
box-sizing: border-box;
background: color-mix(in srgb, var(--bg-input) 82%, var(--bg-surface) 18%);
border-color: var(--border);
- font-family: var(--font-mono);
- font-size: var(--text-sm);
- line-height: 1.5;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.settings-command-preview {
@@ -1209,8 +1253,10 @@
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--bg-input) 82%, var(--bg-surface) 18%);
color: var(--text-primary);
- font-family: var(--font-mono);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
min-height: 44px;
white-space: pre-wrap;
overflow-wrap: anywhere;
@@ -1238,16 +1284,20 @@
}
.settings-autosave {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
text-transform: uppercase;
- letter-spacing: 0.06em;
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.settings-version {
- font-size: var(--text-xs);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-tertiary);
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
}
/* ========== Terminal Panel ========== */
@@ -1800,28 +1850,65 @@ body.focus-mode-active .split-divider-h {
/* ========== 分割线 ========== */
.split-divider-v {
- width: 8px;
- background: var(--border);
+ width: 10px;
+ margin-left: -5px;
+ margin-right: -5px;
+ background: transparent;
cursor: col-resize;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
+ position: relative;
+ z-index: 2;
}
.split-divider-v:hover {
- background: var(--blue);
+ background: transparent;
}
.split-divider-h {
- height: 8px;
- background: var(--border);
+ height: 10px;
+ margin-top: -5px;
+ margin-bottom: -5px;
+ background: transparent;
cursor: row-resize;
flex-shrink: 0;
+ position: relative;
+ z-index: 2;
}
.split-divider-h:hover {
- background: var(--blue);
+ background: transparent;
+}
+
+.split-divider-v::before,
+.split-divider-h::before {
+ content: "";
+ position: absolute;
+ background: color-mix(in srgb, var(--border) 62%, transparent);
+ pointer-events: none;
+}
+
+.split-divider-v::before {
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 1px;
+ transform: translateX(-50%);
+}
+
+.split-divider-h::before {
+ left: 0;
+ right: 0;
+ top: 50%;
+ height: 1px;
+ transform: translateY(-50%);
+}
+
+.split-divider-v:hover::before,
+.split-divider-h:hover::before {
+ background: color-mix(in srgb, var(--border-focus) 58%, transparent);
}
/* Disable text selection during resize */
@@ -1852,9 +1939,11 @@ body.is-resizing-panels * {
}
.panel-kicker {
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-ter);
margin-bottom: 6px;
}
@@ -1863,7 +1952,10 @@ body.is-resizing-panels * {
display: inline-flex;
align-items: center;
gap: 4px;
- font-size: 12px;
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
+ font-family: var(--type-code-inline-family);
color: var(--text-sec);
background: var(--bg-hover);
border-radius: 4px;
@@ -1877,7 +1969,9 @@ body.is-resizing-panels * {
}
.panel-tab {
- font-size: 12px;
+ font-size: var(--type-label-size);
+ font-weight: var(--type-label-weight);
+ line-height: var(--type-label-line-height);
padding: 2px 8px;
border-radius: 4px;
color: var(--text-ter);
@@ -1891,7 +1985,7 @@ body.is-resizing-panels * {
.panel-tab.active {
background: var(--blue);
color: var(--bg);
- font-weight: 500;
+ font-weight: var(--type-label-weight);
}
.panel-toolbar {
@@ -1932,7 +2026,9 @@ body.is-resizing-panels * {
align-items: center;
gap: 4px;
padding: 3px 12px 3px 16px;
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-sec);
cursor: pointer;
transition: background 0.06s;
@@ -1998,14 +2094,18 @@ body.is-resizing-panels * {
.tree-empty-hint {
padding-left: 26px;
color: var(--text-muted);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
font-style: italic;
}
.tree-loading {
padding-left: 26px;
color: var(--text-muted);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
/* ========== Center Area ========== */
@@ -2088,15 +2188,19 @@ body.is-resizing-panels * {
background: var(--text-tertiary);
}
.agent-title {
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
font-family: var(--mono);
text-transform: uppercase;
- letter-spacing: 0.5px;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-sec);
}
.agent-badge {
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
padding: 1px 6px;
border-radius: 3px;
background: var(--bg-hover);
@@ -2104,7 +2208,9 @@ body.is-resizing-panels * {
}
.agent-status {
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
padding: 1px 6px;
border-radius: 3px;
}
@@ -2237,79 +2343,91 @@ body.is-resizing-panels * {
}
/* ========== Welcome Page ========== */
-.welcome-container {
+.welcome-container,
+.auth-screen {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
+ padding: var(--sp-8);
+ background: linear-gradient(
+ 180deg,
+ color-mix(in srgb, var(--bg-page) 97%, var(--bg-surface) 3%) 0%,
+ var(--bg-page) 100%
+ );
+}
+
+.welcome-container {
padding: var(--sp-10);
- background:
- radial-gradient(
- circle at top,
- color-mix(in srgb, var(--accent-green) 10%, transparent),
- transparent 36%
- ),
- radial-gradient(
- circle at bottom right,
- color-mix(in srgb, var(--accent-blue) 12%, transparent),
- transparent 32%
- ),
- var(--bg-page);
}
.welcome-card {
- width: min(100%, 760px);
- max-width: 760px;
- text-align: center;
- padding: var(--sp-10);
+ width: min(100%, 920px);
+ max-width: 920px;
display: flex;
flex-direction: column;
- align-items: center;
+ align-items: stretch;
gap: var(--sp-6);
- background: var(--bg-elevated);
+ padding: var(--sp-8);
+ background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
- box-shadow: var(--shadow-xl);
+ box-shadow: var(--shadow-sm);
+ text-align: left;
}
.welcome-card__hero,
.welcome-card__actions,
-.welcome-card__panel {
+.welcome-card__panel,
+.welcome-card__features {
width: 100%;
}
-.welcome-card__actions {
+.welcome-card__hero {
display: flex;
flex-direction: column;
- align-items: center;
- gap: var(--sp-2);
+ gap: var(--sp-3);
}
-.welcome-card__panel {
- padding-top: var(--sp-2);
+.welcome-card__actions,
+.welcome-card__panel,
+.welcome-card__features {
+ padding-top: var(--sp-5);
+ border-top: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
+}
+
+.welcome-actions-group {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ flex-wrap: wrap;
}
.welcome-kicker {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
- font-weight: var(--font-semibold);
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-ter);
}
.welcome-title {
- font-size: var(--text-3xl);
- font-weight: var(--font-semibold);
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
margin: 0;
color: var(--text);
}
.welcome-body {
- font-size: var(--text-lg);
- color: var(--text-sec);
- line-height: var(--leading-relaxed);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
+ color: var(--text-secondary);
max-width: 440px;
+ margin: 0;
}
.welcome-btn {
@@ -2317,22 +2435,28 @@ body.is-resizing-panels * {
align-items: center;
justify-content: center;
gap: var(--sp-3);
- padding: var(--sp-3) var(--sp-8);
+ min-height: var(--btn-height-lg);
+ min-width: 220px;
+ padding: 0 var(--sp-6);
+ border: 1px solid color-mix(in srgb, var(--accent-blue) 28%, var(--border));
border-radius: var(--radius-md);
- background: var(--blue);
- color: var(--bg);
- font-size: var(--text-base);
+ background: var(--accent-blue);
+ color: var(--text-inverse);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
font-weight: var(--font-medium);
- border: none;
cursor: pointer;
font-family: var(--sans);
- transition: filter var(--duration-fast) var(--ease-out);
- min-height: var(--btn-height-lg);
- width: min(100%, 320px);
+ transition:
+ background var(--duration-fast) var(--ease-out),
+ border-color var(--duration-fast) var(--ease-out),
+ color var(--duration-fast) var(--ease-out);
+ width: auto;
}
.welcome-btn:hover {
- filter: brightness(1.1);
+ filter: none;
+ background: color-mix(in srgb, var(--accent-blue) 88%, white 12%);
}
.welcome-link {
@@ -2340,25 +2464,27 @@ body.is-resizing-panels * {
align-items: center;
justify-content: center;
gap: var(--sp-2);
- font-size: var(--text-sm);
- color: var(--text-ter);
- background: none;
- border: none;
+ min-height: 40px;
+ padding: 0 var(--sp-4);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
+ color: var(--text-secondary);
+ background: transparent;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
cursor: pointer;
font-family: var(--sans);
- padding: var(--sp-2);
- transition: color var(--duration-fast) var(--ease-out);
+ transition:
+ color var(--duration-fast) var(--ease-out),
+ border-color var(--duration-fast) var(--ease-out),
+ background var(--duration-fast) var(--ease-out);
}
.welcome-link:hover {
- color: var(--text-sec);
-}
-
-.welcome-divider {
- width: 48px;
- height: 1px;
- background: var(--border);
- margin: var(--sp-4) 0;
+ color: var(--text-primary);
+ border-color: var(--border-light);
+ background: var(--bg-hover);
}
.welcome-features {
@@ -2373,12 +2499,13 @@ body.is-resizing-panels * {
align-items: flex-start;
gap: var(--sp-3);
padding: var(--sp-4);
- border-radius: var(--radius-md);
- background: var(--bg-panel);
- min-height: 148px;
+ border-radius: var(--radius-lg);
+ background: transparent;
+ min-height: 0;
justify-content: flex-start;
flex-direction: column;
- border: 1px solid var(--border);
+ border: 1px solid color-mix(in srgb, var(--border) 84%, transparent);
+ box-shadow: none;
text-align: left;
}
@@ -2395,57 +2522,41 @@ body.is-resizing-panels * {
}
.welcome-feature-title {
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.welcome-feature-desc {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
- line-height: var(--leading-normal);
-}
-
-.auth-screen {
- padding: var(--sp-8);
- background:
- radial-gradient(
- circle at top,
- color-mix(in srgb, var(--accent-green) 12%, transparent),
- transparent 34%
- ),
- radial-gradient(
- circle at bottom right,
- color-mix(in srgb, var(--accent-blue) 14%, transparent),
- transparent 30%
- ),
- linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-page) 92%, var(--bg-surface) 8%),
- var(--bg-page)
- );
}
.auth-card-shell {
- width: min(100%, 480px);
+ width: min(100%, 520px);
align-items: stretch;
text-align: left;
- gap: var(--sp-4);
- padding: var(--sp-10);
- background: linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-surface) 98%, white 2%),
- color-mix(in srgb, var(--bg-surface) 88%, var(--bg-page) 12%)
- );
- border-color: color-mix(in srgb, var(--border) 88%, var(--accent-blue) 12%);
- box-shadow: var(--shadow-xl);
+ gap: var(--sp-6);
+ padding: var(--sp-8);
+ background: var(--bg-surface);
+ border-color: color-mix(in srgb, var(--border) 88%, transparent);
+ box-shadow: var(--shadow-sm);
}
.auth-card-shell__content {
width: 100%;
display: flex;
flex-direction: column;
- gap: var(--sp-4);
+ gap: var(--sp-5);
+}
+
+.auth-hero {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
}
.auth-card-shell .welcome-kicker,
@@ -2463,34 +2574,41 @@ body.is-resizing-panels * {
display: flex;
flex-direction: column;
gap: var(--sp-2);
- padding: var(--sp-4);
- border-radius: var(--radius-lg);
- border: 1px solid color-mix(in srgb, var(--border) 92%, rgba(255, 255, 255, 0.04));
- background: color-mix(in srgb, var(--bg-surface) 92%, rgba(255, 255, 255, 0.02));
+ padding-top: var(--sp-5);
+ padding-bottom: 0;
+ padding-inline: 0;
+ border-top: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
+ border-radius: 0;
+ border-right: none;
+ border-bottom: none;
+ border-left: none;
+ background: transparent;
}
.auth-status-panel-error {
- border-color: color-mix(in srgb, var(--color-error) 38%, transparent);
- background: color-mix(in srgb, var(--color-error) 10%, var(--bg-surface));
+ border-top-color: color-mix(in srgb, var(--color-error) 34%, var(--border));
+ background: transparent;
}
.auth-status-eyebrow {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
- letter-spacing: 0.08em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
.auth-status-detail {
margin: 0;
- font-size: var(--text-sm);
- line-height: var(--leading-relaxed);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
.auth-status-panel-error .auth-status-detail {
- color: var(--color-error);
+ color: color-mix(in srgb, var(--color-error) 84%, var(--text-primary));
}
.auth-form {
@@ -2498,7 +2616,9 @@ body.is-resizing-panels * {
display: flex;
flex-direction: column;
gap: var(--sp-4);
- margin-top: var(--sp-1);
+ padding-top: var(--sp-5);
+ border-top: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
+ margin-top: 0;
}
.auth-input {
@@ -2507,6 +2627,15 @@ body.is-resizing-panels * {
.auth-input.input {
height: 44px;
+ background: var(--bg-page);
+}
+
+.auth-actions {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ padding-top: var(--sp-5);
+ border-top: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
}
.auth-submit {
@@ -2543,6 +2672,8 @@ body.is-resizing-panels * {
background: var(--border);
transition: background var(--duration-fast) var(--ease-out);
flex-shrink: 0;
+ position: relative;
+ z-index: 2;
}
.pane-layout-divider:hover {
@@ -2551,10 +2682,20 @@ body.is-resizing-panels * {
.pane-layout-horizontal .pane-layout-divider {
cursor: col-resize;
+ width: 10px;
+ margin-left: -5px;
+ margin-right: -5px;
+ justify-self: center;
+ background: transparent;
}
.pane-layout-vertical .pane-layout-divider {
cursor: row-resize;
+ height: 10px;
+ margin-top: -5px;
+ margin-bottom: -5px;
+ align-self: center;
+ background: transparent;
}
/* ========== Draft Launcher ========== */
@@ -2577,8 +2718,9 @@ body.is-resizing-panels * {
}
.agent-draft-title {
- font-size: var(--text-xl);
- font-weight: var(--font-semibold);
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
color: var(--text-primary);
}
@@ -2666,22 +2808,27 @@ body.is-resizing-panels * {
}
.session-title {
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-primary);
- font-family: var(--font-mono);
}
.session-provider-badge {
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-secondary);
}
.session-status-label {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
}
@@ -2740,7 +2887,9 @@ body.is-resizing-panels * {
gap: 6px;
padding: 4px 12px;
border-radius: 6px;
- font-size: 12px;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-sec);
cursor: pointer;
position: relative;
@@ -2775,8 +2924,10 @@ body.is-resizing-panels * {
.topbar-unread {
background: var(--blue);
color: var(--bg);
- font-size: 10px;
- font-weight: 600;
+ font-family: var(--font-sans);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
min-width: 16px;
height: 16px;
border-radius: 8px;
@@ -2883,7 +3034,9 @@ body.is-resizing-panels * {
}
.connection-status-label {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
}
@@ -2904,10 +3057,11 @@ body.is-resizing-panels * {
}
.file-tree-label {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
@@ -2918,7 +3072,10 @@ body.is-resizing-panels * {
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
+ font-family: var(--type-code-inline-family);
color: var(--text-secondary);
}
@@ -2936,7 +3093,9 @@ body.is-resizing-panels * {
.file-tree-empty {
color: var(--text-tertiary);
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
padding: var(--sp-4);
}
@@ -2970,7 +3129,9 @@ body.is-resizing-panels * {
}
.file-tree-name {
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
@@ -2999,10 +3160,11 @@ body.is-resizing-panels * {
}
.git-label {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
@@ -3013,7 +3175,10 @@ body.is-resizing-panels * {
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
+ font-family: var(--type-code-inline-family);
color: var(--text-secondary);
}
@@ -3045,7 +3210,9 @@ body.is-resizing-panels * {
.git-empty {
color: var(--text-tertiary);
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
padding: var(--sp-4);
}
@@ -3059,15 +3226,18 @@ body.is-resizing-panels * {
justify-content: space-between;
padding: var(--sp-2);
color: var(--text-secondary);
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.git-change-group-count {
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
}
@@ -3093,18 +3263,22 @@ body.is-resizing-panels * {
.git-change-path {
flex: 1;
- font-size: var(--text-sm);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
}
.git-change-status {
padding: var(--sp-1) var(--sp-2);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.git-status-staged {
@@ -3142,9 +3316,10 @@ body.is-resizing-panels * {
padding: var(--sp-1) var(--sp-2);
background: var(--accent-blue);
border-radius: var(--radius-full);
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-inverse);
- font-weight: var(--font-medium);
}
/* ========== Supervisor Strip (Phase 3) ==========
@@ -3187,9 +3362,10 @@ body.is-resizing-panels * {
border-radius: var(--radius-sm);
color: var(--text-tertiary);
font-family: var(--font-sans);
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
letter-spacing: 0.02em;
- line-height: 1;
cursor: pointer;
transition:
border-color var(--duration-normal) var(--ease-out),
@@ -3286,10 +3462,11 @@ body.is-resizing-panels * {
}
.supervisor-label {
- font-size: var(--text-xs);
- font-weight: var(--font-medium);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-secondary);
- letter-spacing: 0.04em;
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
@@ -3297,11 +3474,11 @@ body.is-resizing-panels * {
.supervisor-state-tag {
padding: 1px var(--sp-2);
border-radius: var(--radius-sm);
- font-size: 10px;
- font-weight: var(--font-medium);
- line-height: 1.6;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.04em;
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.supervisor-state-tag.supervisor-state-idle {
@@ -3409,9 +3586,10 @@ body.is-resizing-panels * {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-family: var(--font-mono);
- font-size: var(--text-xs);
- line-height: var(--leading-normal);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
}
@@ -3421,10 +3599,10 @@ body.is-resizing-panels * {
border-radius: var(--radius-sm);
background: rgba(108, 182, 255, 0.12);
color: var(--accent-blue);
- font-family: var(--font-mono);
- font-size: 10px;
- font-weight: var(--font-medium);
- line-height: 1.6;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
letter-spacing: 0.04em;
text-transform: lowercase;
}
@@ -3465,9 +3643,10 @@ body.is-resizing-panels * {
.supervisor-details-label {
flex-shrink: 0;
- font-size: 10px;
- font-weight: var(--font-medium);
- letter-spacing: 0.04em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
@@ -3477,9 +3656,10 @@ body.is-resizing-panels * {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-family: var(--font-mono);
- font-size: var(--text-xs);
- line-height: var(--leading-normal);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
}
@@ -3498,9 +3678,10 @@ body.is-resizing-panels * {
.supervisor-details-toggle-text {
flex-shrink: 0;
- font-size: 10px;
- line-height: 1.4;
- letter-spacing: 0.04em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
@@ -3528,9 +3709,10 @@ body.is-resizing-panels * {
.supervisor-meta-label {
margin: 0;
- font-size: 10px;
- line-height: 1.4;
- letter-spacing: 0.03em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
@@ -3539,10 +3721,11 @@ body.is-resizing-panels * {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
- font-family: var(--font-mono);
- font-size: var(--text-xs);
- line-height: var(--leading-normal);
+ white-space: nowrap;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
}
@@ -3561,8 +3744,9 @@ body.is-resizing-panels * {
align-items: baseline;
gap: var(--sp-2);
min-width: 0;
- font-size: 10px;
- line-height: var(--leading-normal);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -3624,8 +3808,9 @@ body.is-resizing-panels * {
background: rgba(255, 158, 176, 0.08);
border-left: 2px solid var(--accent-pink);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
- line-height: var(--leading-normal);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
color: var(--accent-pink);
word-break: break-word;
}
@@ -3641,9 +3826,10 @@ body.is-resizing-panels * {
background: var(--bg-terminal);
border: 1px solid var(--border);
border-radius: var(--radius-md);
- font-family: var(--font-mono);
- font-size: var(--text-sm);
- line-height: var(--leading-normal);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
white-space: pre-wrap;
word-break: break-word;
@@ -3653,61 +3839,108 @@ body.is-resizing-panels * {
.dialog-helper {
margin: 0;
- font-size: var(--text-xs);
- line-height: var(--leading-normal);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
-/* Intent-coloured accent at the top of the supervisor dialog */
-.supervisor-dialog .modal-header h3 {
- margin: 0;
- font-size: var(--text-base);
- font-weight: var(--font-semibold);
- color: var(--text-primary);
- line-height: 1.25;
+.supervisor-dialog-header-icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ border-radius: var(--radius-md);
+ background: var(--icon-surface-success);
+ color: var(--icon-success);
+}
+
+.supervisor-dialog--edit .supervisor-dialog-header-icon {
+ background: var(--icon-surface-info);
+ color: var(--icon-info);
+}
+
+.supervisor-dialog--disable .supervisor-dialog-header-icon {
+ background: var(--icon-surface-error);
+ color: var(--icon-error);
+}
+
+.supervisor-dialog .modal-body {
+ gap: var(--sp-3);
+}
+
+.supervisor-dialog .form-group {
+ gap: 6px;
}
-.supervisor-dialog-header {
+.supervisor-dialog-intro {
display: flex;
- align-items: center;
+ align-items: flex-start;
gap: var(--sp-3);
- min-width: 0;
+ padding: var(--sp-3);
+ border: 1px solid color-mix(in srgb, var(--border) 90%, transparent);
+ border-radius: var(--radius-md);
+ background: color-mix(in srgb, var(--bg-surface) 88%, var(--bg-hover));
}
-.supervisor-dialog-header-icon {
+.supervisor-dialog-intro__icon {
display: inline-flex;
+ width: 24px;
+ height: 24px;
align-items: center;
justify-content: center;
- width: 28px;
- height: 28px;
- border-radius: var(--radius-md);
+ border-radius: var(--radius-sm);
background: var(--icon-surface-success);
color: var(--icon-success);
+ flex-shrink: 0;
}
-.supervisor-dialog[data-mode="edit"] .supervisor-dialog-header-icon {
+.supervisor-dialog--edit .supervisor-dialog-intro__icon {
background: var(--icon-surface-info);
color: var(--icon-info);
}
-.supervisor-dialog[data-mode="disable"] .supervisor-dialog-header-icon {
+.supervisor-dialog--disable .supervisor-dialog-intro__icon {
background: var(--icon-surface-error);
color: var(--icon-error);
}
-.supervisor-dialog-subtitle {
+.supervisor-dialog-intro__copy {
+ min-width: 0;
+}
+
+.supervisor-dialog-intro__title {
display: block;
- margin-top: 2px;
- font-size: var(--text-xs);
- font-weight: var(--font-normal);
- color: var(--text-tertiary);
+ margin: 0;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--font-medium);
+ color: var(--text-primary);
+}
+
+.supervisor-dialog-intro__description {
+ margin: 2px 0 0;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
+ color: var(--text-secondary);
+}
+
+.supervisor-dialog .input,
+.supervisor-dialog .mobile-select-trigger {
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.supervisor-dialog .textarea {
- font-family: var(--font-mono);
- font-size: var(--text-sm);
- line-height: var(--leading-relaxed);
- min-height: 120px;
+ min-height: 104px;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
+ color: var(--text-secondary);
}
.supervisor-dialog .input[type="select"],
@@ -3718,11 +3951,11 @@ body.is-resizing-panels * {
/* Destructive action callout for the disable flow */
.supervisor-danger-callout {
display: flex;
- gap: var(--sp-3);
- padding: var(--sp-3);
+ gap: var(--sp-2);
+ padding: var(--sp-2) var(--sp-3);
background: var(--icon-surface-error);
- border: 1px solid color-mix(in srgb, var(--icon-error) 36%, var(--border));
- border-left-width: 2px;
+ border: 1px solid color-mix(in srgb, var(--icon-error) 24%, var(--border));
+ border-left-width: 1px;
border-radius: var(--radius-md);
}
@@ -3734,8 +3967,10 @@ body.is-resizing-panels * {
.supervisor-danger-callout-copy {
display: flex;
flex-direction: column;
- gap: var(--sp-1);
- font-size: var(--text-sm);
+ gap: 2px;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-primary);
}
@@ -3744,7 +3979,9 @@ body.is-resizing-panels * {
}
.supervisor-danger-callout-copy small {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
@@ -3756,7 +3993,7 @@ body.is-resizing-panels * {
display: flex;
align-items: center;
justify-content: center;
- z-index: 1000;
+ z-index: var(--z-modal-backdrop);
}
.modal-content {
@@ -3780,8 +4017,9 @@ body.is-resizing-panels * {
display: flex;
align-items: center;
gap: var(--sp-2);
- font-size: var(--text-lg);
- font-weight: var(--font-semibold);
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
color: var(--text-primary);
}
@@ -3815,14 +4053,17 @@ body.is-resizing-panels * {
}
.form-label {
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
}
.form-error {
color: var(--pink);
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
/* Modal card — the generic container used by supervisor and worktree dialogs.
@@ -3851,8 +4092,9 @@ body.is-resizing-panels * {
}
.form-group label {
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
}
@@ -3900,7 +4142,9 @@ body.is-resizing-panels * {
.worktree-chip {
padding: var(--sp-1) var(--sp-2);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
background: var(--bg-hover);
}
@@ -3952,7 +4196,9 @@ body.is-resizing-panels * {
border-radius: var(--radius-sm);
cursor: pointer;
text-align: left;
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
transition: background var(--duration-fast) var(--ease-out);
}
@@ -3968,7 +4214,10 @@ body.is-resizing-panels * {
.worktree-list-row__branch {
color: var(--accent-blue);
- font-size: var(--text-xs);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.worktree-summary-card {
@@ -3998,7 +4247,9 @@ body.is-resizing-panels * {
.worktree-summary-card__copy p {
margin: 0;
color: var(--text-secondary);
- font-size: var(--text-sm);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.worktree-summary-card__actions,
@@ -4015,7 +4266,9 @@ body.is-resizing-panels * {
flex-wrap: wrap;
gap: var(--sp-2);
color: var(--text-secondary);
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.worktree-empty {
@@ -4031,7 +4284,9 @@ body.is-resizing-panels * {
.modal-tab {
padding: var(--sp-3) var(--sp-4);
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
background: transparent;
border: none;
@@ -4078,13 +4333,17 @@ body.is-resizing-panels * {
}
.worktree-info-label {
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-tertiary);
min-width: 80px;
}
.worktree-info-value {
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
@@ -4106,8 +4365,10 @@ body.is-resizing-panels * {
}
.worktree-diff-output {
- font-family: var(--font-mono);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
white-space: pre;
overflow-x: auto;
padding: var(--sp-3);
@@ -4143,7 +4404,9 @@ body.is-resizing-panels * {
}
.worktree-tree-name {
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
@@ -4168,14 +4431,18 @@ body.is-resizing-panels * {
}
.worktree-manager__field-label {
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
}
.worktree-manager__field-hint,
.worktree-manager__confirm-path {
color: var(--text-tertiary);
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.worktree-manager__row {
@@ -4214,8 +4481,10 @@ body.is-resizing-panels * {
}
.worktree-manager__row-path {
- font-family: var(--font-mono);
- font-size: var(--text-xs);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
overflow-wrap: anywhere;
}
@@ -4256,7 +4525,9 @@ body.is-resizing-panels * {
padding: var(--sp-2) var(--sp-3);
border: 1px solid transparent;
border-radius: var(--radius-md);
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
background: transparent;
cursor: pointer;
@@ -4305,12 +4576,16 @@ body.is-resizing-panels * {
}
.shortcuts-name {
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.shortcuts-desc {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -4326,8 +4601,10 @@ body.is-resizing-panels * {
background: color-mix(in srgb, var(--bg-input) 82%, var(--bg-surface) 18%);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
- font-size: var(--text-sm);
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-primary);
cursor: pointer;
min-width: 80px;
@@ -4399,21 +4676,26 @@ body.is-resizing-panels * {
}
.launch-header-left .launch-kicker {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
.launch-header-left .launch-title {
- font-size: var(--text-xl);
- font-weight: var(--font-medium);
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
color: var(--text-primary);
margin-top: var(--sp-1);
}
.launch-header-left .launch-hint {
- font-size: var(--text-sm);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
color: var(--text-tertiary);
margin-top: var(--sp-1);
}
@@ -4467,7 +4749,9 @@ body.is-resizing-panels * {
.fp-btn {
padding: var(--sp-1) var(--sp-2);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
background: var(--bg-surface);
border: 1px solid var(--border);
@@ -4492,8 +4776,10 @@ body.is-resizing-panels * {
.fp-chip {
padding: var(--sp-1) var(--sp-2);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
background: var(--bg-surface);
border: 1px solid var(--border);
@@ -4542,7 +4828,9 @@ body.is-resizing-panels * {
}
.fp-dir-name {
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
@@ -4551,7 +4839,9 @@ body.is-resizing-panels * {
}
.fp-dir-hint {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -4562,7 +4852,9 @@ body.is-resizing-panels * {
border: 1px solid color-mix(in srgb, var(--accent-blue) 38%, var(--border) 62%);
border-radius: 999px;
background: rgba(108, 182, 255, 0.08);
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
font-family: var(--font-sans);
color: var(--accent-blue);
display: inline-flex;
@@ -4608,8 +4900,9 @@ body.is-resizing-panels * {
color: var(--text-inverse);
border: none;
border-radius: 14px;
- font-size: var(--text-base);
- font-weight: 600;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--font-medium);
letter-spacing: 0.01em;
font-family: var(--font-sans);
cursor: pointer;
@@ -4647,7 +4940,9 @@ body.is-resizing-panels * {
.launch-start-btn--mobile {
min-height: 52px;
border-radius: 18px;
- font-size: var(--text-lg);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--font-medium);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.18),
0 14px 32px rgba(37, 99, 235, 0.24);
@@ -4668,7 +4963,10 @@ body.is-resizing-panels * {
border-radius: var(--radius-sm);
margin-bottom: var(--sp-3);
color: var(--text-secondary);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.breadcrumb-path {
@@ -4753,7 +5051,10 @@ body.is-resizing-panels * {
padding: var(--sp-2) var(--sp-3);
background: var(--accent-bg);
border-radius: var(--radius-sm);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.selected-path-label {
@@ -4778,9 +5079,9 @@ body.is-resizing-panels * {
border: 1px solid transparent;
border-radius: var(--radius-md);
font-family: var(--font-sans);
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
font-weight: var(--font-medium);
- line-height: 1;
+ line-height: var(--type-body-strong-line-height);
white-space: nowrap;
transition:
background var(--duration-normal) var(--ease-out),
@@ -4842,14 +5143,15 @@ body.is-resizing-panels * {
height: 24px;
padding: 0 var(--sp-2);
border-radius: var(--radius-sm);
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ font-weight: var(--font-medium);
+ line-height: var(--type-label-line-height);
}
.btn-lg {
height: 40px;
padding: 0 var(--sp-6);
border-radius: var(--radius-lg);
- font-size: var(--text-lg);
}
.input {
@@ -4860,7 +5162,9 @@ body.is-resizing-panels * {
border-radius: var(--radius-md);
color: var(--text-primary);
font-family: var(--font-sans);
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ font-weight: var(--type-body-strong-weight);
+ line-height: var(--type-body-strong-line-height);
outline: none;
transition:
border-color var(--duration-normal) var(--ease-out),
@@ -4891,9 +5195,11 @@ textarea.input {
height: 20px;
padding: 0 var(--sp-2);
border-radius: var(--radius-sm);
- font-size: 10px;
- font-weight: var(--font-semibold);
- letter-spacing: 0.04em;
+ font-family: var(--font-sans);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
@@ -4942,7 +5248,9 @@ textarea.input {
}
.topbar-hint {
- font-size: var(--text-sm);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -5014,11 +5322,16 @@ textarea.input {
width: auto;
padding: 0 10px;
gap: 6px;
- font-size: var(--text-sm);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-secondary);
}
.topbar-btn-label {
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
white-space: nowrap;
}
@@ -5072,8 +5385,10 @@ textarea.input {
.panel-kicker {
margin-bottom: 8px;
- font-size: 10px;
- letter-spacing: 0.12em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.panel-branch {
@@ -5122,7 +5437,9 @@ textarea.input {
padding: 0 10px;
border: none;
border-radius: 4px;
- font-size: 12px;
+ font-size: var(--type-label-size);
+ font-weight: var(--type-label-weight);
+ line-height: var(--type-label-line-height);
color: var(--text-tertiary);
background: transparent;
}
@@ -5135,7 +5452,7 @@ textarea.input {
.panel-tab.active {
background: var(--accent-blue);
color: var(--text-inverse);
- font-weight: var(--font-medium);
+ font-weight: var(--type-label-weight);
}
.panel-toolbar {
@@ -5150,10 +5467,11 @@ textarea.input {
}
.panel-toolbar-label {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
@@ -5230,7 +5548,9 @@ textarea.input {
}
.tree-label {
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.tree-children {
@@ -5258,16 +5578,19 @@ textarea.input {
}
.agent-draft-kicker {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
- letter-spacing: 0.12em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
text-align: center;
}
.agent-draft-title {
- font-size: 22px;
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
text-align: center;
}
@@ -5343,15 +5666,17 @@ textarea.input {
}
.agent-provider-card-title {
- font-size: var(--text-lg);
- font-weight: var(--font-semibold);
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
color: var(--text-primary);
}
.agent-provider-card-meta {
- font-size: 10px;
- font-weight: var(--font-semibold);
- letter-spacing: 0.08em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
@@ -5363,14 +5688,16 @@ textarea.input {
.agent-provider-card-cta {
margin-top: var(--sp-2);
- font-size: var(--text-sm);
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.agent-provider-card-status {
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
color: var(--text-tertiary);
}
@@ -5382,8 +5709,9 @@ textarea.input {
flex-direction: column;
gap: var(--sp-2);
color: var(--text-secondary);
- font-size: var(--text-sm);
- line-height: var(--leading-normal);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.agent-provider-card-guide a {
@@ -5455,9 +5783,10 @@ textarea.input {
}
.session-title {
- font-size: 11px;
- font-family: var(--font-mono);
- font-weight: var(--font-medium);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
letter-spacing: 0.06em;
color: var(--text-secondary);
min-width: 0;
@@ -5472,7 +5801,9 @@ textarea.input {
align-items: center;
gap: var(--sp-3);
margin-top: 4px;
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
}
@@ -5710,7 +6041,9 @@ textarea.input {
color: var(--text-primary);
resize: none;
font: inherit;
- font-size: 12px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
outline: none;
}
@@ -5730,7 +6063,9 @@ textarea.input {
}
.git-head-summary__label {
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
}
@@ -5739,7 +6074,10 @@ textarea.input {
align-items: center;
gap: 8px;
min-width: 0;
- font-size: 12px;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
}
@@ -5771,7 +6109,9 @@ textarea.input {
align-items: center;
justify-content: space-between;
padding: 8px 12px 6px;
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
}
@@ -5823,7 +6163,9 @@ textarea.input {
.git-row-name {
display: block;
min-width: 0;
- font-size: 12px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
@@ -5848,7 +6190,10 @@ textarea.input {
.git-row-dir {
flex: 1;
min-width: 0;
- font-size: 11px;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-tertiary);
overflow: hidden;
text-overflow: ellipsis;
@@ -5857,8 +6202,9 @@ textarea.input {
.git-row-status {
flex-shrink: 0;
- font-size: 10px;
- font-weight: var(--font-medium);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
letter-spacing: 0.01em;
}
@@ -5911,7 +6257,9 @@ textarea.input {
padding: 20px 16px;
text-align: center;
color: var(--text-tertiary);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.workspace-git-view {
@@ -6170,14 +6518,29 @@ textarea.input {
.pane-layout-divider::after {
content: "";
position: absolute;
- inset: 0;
- margin: 2px;
+ pointer-events: none;
border-radius: 999px;
- background: linear-gradient(180deg, transparent, rgba(108, 182, 255, 0.18), transparent);
+ background: color-mix(in srgb, var(--border) 62%, transparent);
+}
+
+.pane-layout-horizontal .pane-layout-divider::after {
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 1px;
+ transform: translateX(-50%);
+}
+
+.pane-layout-vertical .pane-layout-divider::after {
+ left: 0;
+ right: 0;
+ top: 50%;
+ height: 1px;
+ transform: translateY(-50%);
}
.pane-layout-divider:hover::after {
- background: linear-gradient(180deg, transparent, rgba(108, 182, 255, 0.4), transparent);
+ background: color-mix(in srgb, var(--border-focus) 58%, transparent);
}
.session-card {
@@ -6217,7 +6580,7 @@ textarea.input {
.workspace-bottom-panel > .bottom-terminal {
border: 1px solid color-mix(in srgb, var(--border-light) 64%, var(--accent-blue) 36%);
- border-radius: 14px;
+ border-radius: 0;
box-shadow: var(--shadow-lg);
overflow: hidden;
background: linear-gradient(
@@ -6449,6 +6812,10 @@ textarea.input {
border-radius: 999px;
background: color-mix(in srgb, var(--accent-blue) 82%, white 18%);
color: #081118;
+ font-family: var(--font-sans);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.topbar-close {
@@ -6532,7 +6899,9 @@ textarea.input {
.file-tree-empty {
padding: 20px 16px;
color: var(--text-tertiary);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.tree-item {
@@ -6558,7 +6927,9 @@ textarea.input {
}
.tree-label {
- font-size: 12px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-secondary);
}
@@ -6570,7 +6941,11 @@ textarea.input {
height: 18px;
padding: 0 8px;
border-radius: 5px;
- letter-spacing: 0.06em;
+ font-family: var(--font-sans);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.session-dot {
@@ -6630,7 +7005,9 @@ textarea.input {
.workspace-empty-inner p {
color: var(--text-secondary);
- font-size: var(--text-lg);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.workspace-resolving-card {
@@ -6639,26 +7016,29 @@ textarea.input {
.workspace-resolving-kicker {
color: color-mix(in srgb, var(--text-tertiary) 80%, var(--accent-blue) 20%);
- font-size: var(--text-xs);
- font-weight: var(--font-semibold);
- letter-spacing: 0.12em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
.workspace-resolving-title {
margin-top: var(--sp-3);
color: var(--text-primary);
- font-size: clamp(26px, 4vw, 36px);
- font-weight: var(--font-semibold);
- letter-spacing: -0.03em;
+ font-size: var(--type-display-size);
+ line-height: var(--type-display-line-height);
+ font-weight: var(--type-display-weight);
+ letter-spacing: var(--type-display-letter-spacing);
}
.workspace-resolving-desc {
max-width: 48ch;
margin-top: var(--sp-3);
color: var(--text-secondary);
- font-size: var(--text-sm);
- line-height: 1.7;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.workspace-resolving-preview {
@@ -6854,11 +7234,81 @@ textarea.input {
}
.panel-header {
- padding: 12px 12px 8px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--sp-3);
+ min-height: 40px;
+ padding: 6px 12px;
background: transparent;
border-bottom: 1px solid var(--border);
}
+.panel-header__leading {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ flex: 1;
+ min-width: 0;
+}
+
+.panel-header__copy {
+ display: flex;
+ min-width: 0;
+ flex: 1;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.panel-header__title-row {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ min-width: 0;
+}
+
+.panel-header--inline-meta .panel-header__title-row {
+ flex-wrap: nowrap;
+}
+
+.panel-header__title {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
+ color: var(--text-primary);
+}
+
+.panel-header__status {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-1);
+ flex: 0 0 auto;
+}
+
+.panel-header__meta {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ min-width: 0;
+}
+
+.panel-header__meta--inline {
+ flex: 0 0 auto;
+}
+
+.panel-header__actions {
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: var(--sp-1);
+ margin-left: auto;
+ flex-shrink: 0;
+}
+
.panel-kicker {
margin-bottom: 6px;
color: var(--text-tertiary);
@@ -6906,12 +7356,16 @@ textarea.input {
padding: 2px 8px;
border-radius: 4px;
background: transparent;
+ font-size: var(--type-label-size);
+ font-weight: var(--type-label-weight);
+ line-height: var(--type-label-line-height);
color: var(--text-tertiary);
}
.panel-tab.active {
background: var(--accent-blue);
color: var(--text-inverse);
+ font-weight: var(--type-label-weight);
}
.panel-toolbar {
@@ -6947,7 +7401,9 @@ textarea.input {
border: none;
background: transparent;
color: var(--text-primary);
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
outline: none;
}
@@ -6992,7 +7448,9 @@ textarea.input {
}
.tree-label {
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-secondary);
}
@@ -7006,7 +7464,10 @@ textarea.input {
.tree-search-path {
overflow: hidden;
color: var(--text-tertiary);
- font-size: 11px;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -7017,11 +7478,16 @@ textarea.input {
.session-card {
background: var(--bg-page);
- border: 1px solid var(--border);
+ border: none;
border-radius: 0;
box-shadow: none;
}
+.session-card.session-card--active {
+ background: var(--bg-active);
+ box-shadow: inset 0 0 0 1px var(--border-focus);
+}
+
.session-progress {
height: 3px;
background: rgba(108, 182, 255, 0.08);
@@ -7034,6 +7500,16 @@ textarea.input {
backdrop-filter: none;
}
+.session-card.session-card--active .session-header {
+ background: color-mix(in srgb, var(--bg-active) 88%, var(--bg-page) 12%);
+ border-bottom-color: var(--border-focus);
+}
+
+.session-card.session-card--active > .panel-header {
+ background: color-mix(in srgb, var(--bg-active) 88%, var(--bg-page) 12%);
+ border-bottom-color: var(--border-focus);
+}
+
.session-header-left {
gap: 8px;
}
@@ -7044,16 +7520,26 @@ textarea.input {
}
.session-title-row {
+ display: inline-flex;
+ align-items: center;
gap: 8px;
min-width: 0;
+ flex-wrap: nowrap;
}
-.session-title {
- font-size: 11px;
+.session-card > .panel-header .panel-header__title {
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
color: var(--text-secondary);
letter-spacing: 0.04em;
}
+.session-card.session-card--active > .panel-header .panel-header__title {
+ color: var(--text-primary);
+}
+
.session-dot {
box-shadow: none;
}
@@ -7067,13 +7553,19 @@ textarea.input {
height: 16px;
padding: 0 6px;
border-radius: 3px;
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
letter-spacing: 0;
text-transform: none;
flex: 0 0 auto;
white-space: nowrap;
}
+.session-card > .panel-header.panel-header--inline-meta .panel-header__meta {
+ overflow: hidden;
+}
+
.session-header-actions {
gap: 4px;
}
@@ -7107,7 +7599,7 @@ textarea.input {
flex-shrink: 0;
min-height: 0;
overflow: visible;
- padding: 0 14px 14px;
+ padding: 0;
background: transparent;
border-top: none;
}
@@ -7115,7 +7607,7 @@ textarea.input {
.workspace-bottom-panel > .bottom-terminal {
flex: 1;
min-height: 0;
- border-top: none;
+ border: none;
background: linear-gradient(
180deg,
color-mix(in srgb, var(--bg-terminal) 96%, white 4%),
@@ -7460,16 +7952,17 @@ textarea.input {
}
.toast__title {
- font-size: var(--text-base);
- font-weight: var(--font-medium);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
- line-height: var(--leading-tight);
}
.toast__body {
- font-size: var(--text-sm);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
color: var(--text-secondary);
- line-height: var(--leading-normal);
/* Notification engine composes a 2-line body (metadata + summary)
separated by `\n`; preserve those breaks while still wrapping long lines. */
white-space: pre-line;
@@ -7683,6 +8176,8 @@ textarea.input {
}
.code-editor-header {
+ padding: 9px 14px;
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, var(--accent-blue) 28%);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--bg-surface) 97%, white 3%),
@@ -7887,9 +8382,10 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-family: var(--font-mono);
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.config-card-header-right {
@@ -7905,8 +8401,9 @@ textarea.input {
align-items: center;
flex-shrink: 0;
gap: var(--sp-1);
- font-size: var(--text-xs);
- font-weight: var(--font-medium);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
white-space: nowrap;
}
@@ -8023,14 +8520,17 @@ textarea.input {
}
.config-empty-title {
- font-size: var(--text-base);
- font-weight: var(--font-medium);
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
color: var(--text-primary);
margin-bottom: var(--sp-1);
}
.config-empty-desc {
- font-size: var(--text-sm);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
color: var(--text-secondary);
max-width: 44ch;
}
@@ -8057,7 +8557,9 @@ textarea.input {
.config-card-error-message {
color: var(--text-primary);
- font-size: var(--text-sm);
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
@media (max-width: 640px) {
@@ -8127,7 +8629,9 @@ textarea.input {
flex: 1;
background: transparent;
border: none;
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
outline: none;
}
@@ -8162,13 +8666,17 @@ textarea.input {
.branch-quick-pick-name {
flex: 1;
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
.branch-quick-pick-badge {
flex-shrink: 0;
- font-size: var(--text-xs);
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
padding: var(--sp-1) var(--sp-2);
background: var(--bg-hover);
border-radius: var(--radius-sm);
@@ -8182,7 +8690,9 @@ textarea.input {
.branch-quick-pick-create-label {
flex: 1;
- font-size: var(--text-base);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
@@ -8197,7 +8707,9 @@ textarea.input {
align-items: center;
gap: var(--sp-4);
padding: var(--sp-2) var(--sp-4);
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-tertiary);
border-top: 1px solid var(--border);
}
@@ -8272,7 +8784,7 @@ textarea.input {
height: 36px;
padding: 0 var(--sp-3);
border: none;
- border-bottom: 1px solid color-mix(in srgb, var(--border) 62%, transparent);
+ border-bottom: none;
border-radius: 0;
background: color-mix(in srgb, var(--bg-surface) 24%, transparent);
color: var(--text-primary);
@@ -8290,9 +8802,9 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: 13px;
- font-weight: var(--font-semibold);
- line-height: 1.2;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.mobile-topbar__session-switcher {
@@ -8329,11 +8841,12 @@ textarea.input {
}
.mobile-topbar__session-label {
- font-size: 10px;
- line-height: 1.1;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
text-transform: uppercase;
- letter-spacing: 0.06em;
+ letter-spacing: var(--type-kicker-letter-spacing);
}
.mobile-topbar__session-name,
@@ -8342,8 +8855,9 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-sm);
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.mobile-topbar__session-empty {
@@ -8401,12 +8915,15 @@ textarea.input {
}
.mobile-shell__recovery-title {
- font-size: var(--text-sm);
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.mobile-shell__recovery-body {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
@@ -8419,9 +8936,9 @@ textarea.input {
min-height: 36px;
height: 36px;
padding: 0;
- border: 1px solid color-mix(in srgb, var(--border) 66%, transparent);
+ border: none;
border-radius: 10px;
- background: color-mix(in srgb, var(--bg-surface) 42%, transparent);
+ background: transparent;
color: var(--text-primary);
transition:
background-color var(--mobile-shell-motion-surface-duration) var(--ease-out),
@@ -8566,14 +9083,17 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-sm);
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.mobile-inline-sheet__provider-meta,
.mobile-inline-sheet__option-meta,
.mobile-inline-sheet__empty {
- font-size: var(--text-xs);
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
color: var(--text-secondary);
}
@@ -8694,10 +9214,10 @@ textarea.input {
.mobile-shell__empty-title {
margin: 0;
color: var(--text-primary);
- font-size: clamp(24px, 6.4vw, 32px);
- font-weight: var(--font-semibold);
- line-height: 1.08;
- letter-spacing: -0.035em;
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
+ letter-spacing: var(--type-app-title-letter-spacing);
}
.mobile-shell__placeholder-copy {
@@ -8712,8 +9232,9 @@ textarea.input {
.mobile-shell__placeholder-copy p {
margin: 0;
- font-size: 14px;
- line-height: 1.62;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.mobile-shell__empty-action-row {
@@ -8731,12 +9252,13 @@ textarea.input {
width: auto;
min-width: 136px;
padding: 0 var(--sp-4);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--font-medium);
border: 1px solid color-mix(in srgb, var(--accent-blue) 34%, var(--border));
border-radius: 12px;
background: color-mix(in srgb, var(--accent-blue) 8%, var(--bg-surface));
color: var(--text-primary);
- font-size: 13px;
- font-weight: var(--font-semibold);
transition:
background-color var(--mobile-shell-motion-surface-duration) var(--ease-out),
border-color var(--mobile-shell-motion-surface-duration) var(--ease-out),
@@ -8811,16 +9333,16 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-sm);
- font-weight: var(--font-medium);
- line-height: 1;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.mobile-shell__agent-stage .session-card {
background: var(--bg-page);
}
- .mobile-shell__agent-stage .session-header {
+ .mobile-shell__agent-stage > .session-card > .panel-header {
padding: 4px calc(var(--mobile-safe-right) + var(--sp-3)) 4px
calc(var(--mobile-safe-left) + var(--sp-3));
gap: var(--sp-2);
@@ -8828,19 +9350,15 @@ textarea.input {
border-bottom: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
}
- .mobile-shell__agent-stage .session-header-left,
- .mobile-shell__agent-stage .session-header-copy,
.mobile-shell__agent-stage .session-header-accessory {
min-width: 0;
- }
-
- .mobile-shell__agent-stage .session-header-left {
- gap: 6px;
+ max-width: 100%;
}
.mobile-shell__agent-stage .session-header-right {
+ flex: 0 1 auto;
min-width: 0;
- max-width: min(48%, 220px);
+ max-width: 100%;
}
.mobile-shell__agent-stage .session-title-row {
@@ -8851,20 +9369,14 @@ textarea.input {
flex-wrap: nowrap;
}
- .mobile-shell__agent-stage .session-title {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex: 1 1 120px;
- }
-
.mobile-shell__agent-stage .session-provider-badge,
.mobile-shell__agent-stage .session-state-badge {
height: 15px;
padding: 0 5px;
border-radius: 3px;
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
flex-shrink: 0;
max-width: 100%;
}
@@ -8880,7 +9392,9 @@ textarea.input {
.mobile-shell__agent-stage .mobile-supervisor-badge__label {
max-width: 100%;
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-dock {
@@ -8888,6 +9402,7 @@ textarea.input {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--sp-2);
+ min-height: 36px;
padding: 0 0 var(--sp-1);
background: transparent;
border-top: none;
@@ -8900,9 +9415,10 @@ textarea.input {
flex-direction: column;
align-items: center;
justify-content: center;
- min-height: 30px;
+ min-height: 36px;
+ height: 36px;
gap: 4px;
- padding: 2px var(--sp-2) 2px;
+ padding: 1px var(--sp-2) 0;
border: none;
border-top: 1px solid transparent;
border-radius: 0;
@@ -8939,9 +9455,9 @@ textarea.input {
}
.mobile-dock__label {
- font-size: 10px;
- font-weight: var(--font-medium);
- line-height: 1.1;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-sheet-layer,
@@ -9058,10 +9574,14 @@ textarea.input {
.mobile-page-header {
width: 100%;
- min-height: 38px;
+ min-height: 44px;
gap: 0;
}
+ .panel-header {
+ min-height: 44px;
+ }
+
.mobile-page-header .page-header__leading {
min-width: 0;
gap: 8px;
@@ -9072,10 +9592,10 @@ textarea.input {
}
.mobile-page-header .page-header__title {
- font-size: 14px;
- font-weight: var(--font-semibold);
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
letter-spacing: 0.01em;
- line-height: 1.25;
}
.mobile-page-header .page-header__back {
@@ -9084,7 +9604,9 @@ textarea.input {
gap: 5px;
color: color-mix(in srgb, var(--text-tertiary) 88%, var(--bg-panel));
font-family: var(--font-mono);
- font-size: 10px;
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
letter-spacing: 0.04em;
}
@@ -9221,7 +9743,9 @@ textarea.input {
border: 1px solid color-mix(in srgb, var(--accent-blue) 52%, transparent);
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--accent-blue) 86%, var(--bg-surface));
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
box-shadow: none;
}
@@ -9240,8 +9764,9 @@ textarea.input {
.mobile-sheet--launch .mobile-sheet__title {
margin-top: 0;
- font-size: var(--text-lg);
- line-height: 1.2;
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
}
.mobile-sheet--launch .mobile-sheet__body {
@@ -9321,7 +9846,9 @@ textarea.input {
}
.command-palette-sheet .command-palette-input {
- font-size: 15px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.command-palette-sheet .command-palette-hint {
@@ -9330,9 +9857,11 @@ textarea.input {
}
.command-palette-sheet .command-palette-meta {
- font-family: var(--font-mono);
- font-size: 10px;
- letter-spacing: 0.08em;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
@@ -9369,8 +9898,9 @@ textarea.input {
}
.command-palette-item-label {
- font-size: 14px;
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.command-palette-item-desc {
@@ -9384,7 +9914,9 @@ textarea.input {
border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
border-radius: 999px;
background: color-mix(in srgb, var(--bg-page) 76%, var(--bg-surface) 24%);
- font-size: 10px;
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
letter-spacing: 0.06em;
}
@@ -9464,21 +9996,23 @@ textarea.input {
}
.mobile-files-sheet__branch-label {
- font-size: 10px;
- line-height: 1.1;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
color: var(--text-tertiary);
- letter-spacing: 0.12em;
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
- font-family: var(--font-mono);
}
.mobile-files-sheet__branch-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: 12px;
- line-height: 1.25;
- font-weight: 600;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.mobile-files-sheet__tabs {
@@ -9548,9 +10082,11 @@ textarea.input {
}
.mobile-files-sheet__kicker {
- font-size: var(--text-xs);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
text-transform: uppercase;
- letter-spacing: 0.08em;
+ letter-spacing: var(--type-kicker-letter-spacing);
color: var(--text-tertiary);
}
@@ -9559,7 +10095,9 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-lg);
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
color: var(--text-primary);
}
@@ -9605,8 +10143,9 @@ textarea.input {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- font-size: var(--text-lg);
- line-height: 1.2;
+ font-size: var(--type-app-title-size);
+ line-height: var(--type-app-title-line-height);
+ font-weight: var(--type-app-title-weight);
}
.mobile-sheet--terminal,
@@ -9622,11 +10161,6 @@ textarea.input {
letter-spacing: 0.12em;
}
- .mobile-sheet--fullscreen .page-header__title {
- font-size: 16px;
- line-height: 1.2;
- }
-
.mobile-sheet__action {
display: inline-flex;
align-items: center;
@@ -9636,8 +10170,9 @@ textarea.input {
padding: 0 var(--sp-3);
border-radius: var(--radius-md);
color: var(--text-secondary);
- font-size: var(--text-sm);
- font-weight: 500;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
transition:
background-color var(--mobile-shell-motion-surface-duration) var(--ease-out),
color var(--mobile-shell-motion-surface-duration) var(--ease-out);
@@ -9690,11 +10225,9 @@ textarea.input {
.mobile-sheet--files .workspace-git-view {
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
- border-radius: var(--radius-xl) var(--radius-xl) 0 0;
- background: color-mix(in srgb, var(--bg-surface) 90%, var(--bg-page));
- box-shadow:
- inset 0 1px 0 color-mix(in srgb, var(--fg-white) 5%, transparent),
- 0 14px 34px color-mix(in srgb, var(--bg-page) 18%, transparent);
+ border-radius: var(--radius-md);
+ background: var(--bg-panel);
+ box-shadow: none;
}
.mobile-sheet--files .workspace-git-view {
@@ -9838,19 +10371,15 @@ textarea.input {
}
.mobile-supervisor-sheet {
- gap: var(--sp-3);
- padding: var(--sp-3) var(--sp-3) 0;
- background: linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-surface) 96%, transparent) 0%,
- var(--bg-page) 100%
- );
+ gap: 0;
+ padding: 0;
+ background: color-mix(in srgb, var(--bg-page) 98%, var(--bg-surface) 2%);
}
.mobile-sheet__body--supervisor-detail {
display: flex;
flex-direction: column;
- gap: var(--sp-4);
+ gap: var(--sp-3);
}
.mobile-supervisor-sheet--root,
@@ -9864,18 +10393,21 @@ textarea.input {
flex: 1;
min-height: 0;
flex-direction: column;
- gap: var(--sp-4);
- padding: var(--sp-4);
- padding-bottom: var(--sp-5);
- border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
- border-radius: var(--radius-xl) var(--radius-xl) 0 0;
- background: color-mix(in srgb, var(--bg-surface) 90%, var(--bg-page));
- box-shadow:
- inset 0 1px 0 color-mix(in srgb, var(--fg-white) 5%, transparent),
- 0 14px 34px color-mix(in srgb, var(--bg-page) 18%, transparent);
+ gap: var(--sp-3);
+ padding: var(--sp-3);
+ padding-bottom: var(--sp-4);
+ background: transparent;
overflow: auto;
}
+ .mobile-supervisor-sheet__detail .input,
+ .mobile-supervisor-sheet__detail .mobile-select-trigger,
+ .mobile-supervisor-sheet__detail .textarea {
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
+ }
+
.mobile-supervisor-sheet__actions,
.mobile-supervisor-sheet__footer {
display: grid;
@@ -9885,15 +10417,16 @@ textarea.input {
.mobile-supervisor-sheet__footer {
width: 100%;
- padding: var(--sp-2);
- border: 1px solid color-mix(in srgb, var(--border) 86%, transparent);
- border-radius: var(--radius-xl);
- background: color-mix(in srgb, var(--bg-surface) 96%, var(--bg-page));
- box-shadow: inset 0 1px 0 color-mix(in srgb, var(--fg-white) 6%, transparent);
+ padding: var(--sp-1) var(--sp-2);
+ border: none;
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
}
.mobile-supervisor-sheet.mobile-sheet--fullscreen .mobile-sheet__footer {
- padding: var(--sp-2) var(--sp-4) calc(var(--mobile-safe-bottom) + var(--sp-4));
+ padding: var(--sp-1) var(--sp-3) calc(var(--mobile-safe-bottom) + var(--sp-3));
+ background: color-mix(in srgb, var(--bg-page) 94%, var(--bg-surface) 6%);
}
.mobile-supervisor-sheet__actions > .btn,
@@ -9911,47 +10444,6 @@ textarea.input {
box-shadow: none;
}
- .mobile-supervisor-sheet__detail-header {
- display: flex;
- align-items: flex-start;
- gap: var(--sp-3);
- padding: var(--sp-3);
- border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
- border-radius: var(--radius-xl);
- background: linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-surface) 90%, transparent) 0%,
- color-mix(in srgb, var(--bg-page) 92%, var(--bg-surface) 8%) 100%
- );
- }
-
- .mobile-supervisor-sheet__detail-header h3,
- .mobile-supervisor-sheet__detail-header p {
- margin: 0;
- }
-
- .mobile-supervisor-sheet__detail-header h3 {
- font-size: var(--text-base);
- font-weight: var(--font-semibold);
- color: var(--text-primary);
- line-height: 1.25;
- }
-
- .mobile-supervisor-sheet__detail-header p {
- margin-top: 2px;
- font-size: var(--text-xs);
- color: var(--text-tertiary);
- line-height: var(--leading-normal);
- }
-
- .mobile-supervisor-sheet__detail-header .supervisor-dialog-header-icon {
- flex-shrink: 0;
- width: 36px;
- height: 36px;
- border-radius: var(--radius-md);
- background: color-mix(in srgb, var(--icon-surface-subtle) 84%, var(--bg-surface));
- }
-
.mobile-supervisor-sheet__empty {
display: flex;
flex: 1;
@@ -10014,9 +10506,11 @@ textarea.input {
display: inline-flex;
align-items: center;
gap: 6px;
- font-family: var(--font-mono);
- font-size: 10px;
- letter-spacing: 0.08em;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
color: var(--text-tertiary);
}
@@ -10032,10 +10526,9 @@ textarea.input {
.mobile-workspace-drawer__title {
margin: 0;
- font-size: 18px;
- font-weight: var(--font-semibold);
- line-height: 1.2;
- letter-spacing: -0.01em;
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
color: var(--text-primary);
}
@@ -10156,9 +10649,9 @@ textarea.input {
min-width: 0;
flex: 1;
color: var(--text-primary);
- font-size: 13px;
- font-weight: var(--font-semibold);
- line-height: 1.35;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.mobile-workspace-drawer__item-state {
@@ -10170,10 +10663,10 @@ textarea.input {
border-radius: 999px;
background: color-mix(in srgb, var(--accent-blue) 12%, transparent);
color: color-mix(in srgb, var(--accent-blue) 74%, var(--text-primary));
- font-size: 10px;
- font-weight: var(--font-semibold);
- line-height: 1.2;
- letter-spacing: 0.04em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
white-space: nowrap;
}
@@ -10181,10 +10674,10 @@ textarea.input {
max-width: 100%;
overflow: hidden;
color: var(--text-tertiary);
- font-family: var(--font-mono);
- font-size: 10px;
- line-height: 1.45;
- letter-spacing: 0.02em;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -10210,7 +10703,8 @@ textarea.input {
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--bg-surface) 80%, transparent);
color: var(--text-primary);
- font-size: 12px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
font-weight: var(--font-medium);
transition:
background-color var(--mobile-shell-motion-surface-duration) var(--ease-out),
@@ -10332,6 +10826,7 @@ textarea.input {
}
.welcome-container--mobile {
+ flex-direction: column;
align-items: stretch;
justify-content: flex-start;
min-height: 100dvh;
@@ -10339,57 +10834,49 @@ textarea.input {
calc(env(safe-area-inset-right, 0px) + var(--sp-4))
calc(env(safe-area-inset-bottom, 0px) + var(--sp-5))
calc(env(safe-area-inset-left, 0px) + var(--sp-4));
- background:
- radial-gradient(
- circle at top center,
- color-mix(in srgb, var(--accent-blue) 10%, transparent),
- transparent 38%
- ),
- linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-page) 92%, var(--bg-surface) 8%) 0%,
- var(--bg-page) 100%
- );
}
.welcome-card--mobile {
width: 100%;
max-width: none;
- padding: var(--sp-7) var(--sp-5);
+ padding: var(--sp-6) var(--sp-4);
gap: var(--sp-5);
- border-radius: var(--radius-xl);
+ border-radius: var(--radius-lg);
+ box-shadow: none;
}
.welcome-card--mobile .welcome-card__actions {
align-items: stretch;
}
- .welcome-card--mobile .welcome-btn {
- width: 100%;
+ .welcome-card--mobile .welcome-actions-group {
+ flex-direction: column;
+ align-items: stretch;
}
.welcome-card--mobile .welcome-title {
- font-size: clamp(30px, 8vw, 40px);
- line-height: 1.05;
- letter-spacing: -0.03em;
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
+ letter-spacing: 0;
}
.welcome-card--mobile .welcome-body {
max-width: none;
- font-size: 16px;
- line-height: 1.7;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.welcome-card--mobile .welcome-features {
grid-template-columns: 1fr;
- margin-top: var(--sp-2);
}
.welcome-card--mobile .welcome-feature {
align-items: flex-start;
min-height: 0;
padding: var(--sp-4);
- border-radius: var(--radius-xl);
+ border-radius: var(--radius-lg);
text-align: left;
}
@@ -10405,7 +10892,7 @@ textarea.input {
}
.welcome-card--mobile .welcome-link {
- margin-top: 0;
+ width: 100%;
}
.auth-screen--mobile {
@@ -10417,25 +10904,28 @@ textarea.input {
.auth-card-shell--mobile {
width: 100%;
- padding: var(--sp-7) var(--sp-5);
+ padding: var(--sp-6) var(--sp-4);
gap: var(--sp-5);
- border-radius: var(--radius-xl);
+ border-radius: var(--radius-lg);
+ box-shadow: none;
}
.auth-card-shell--mobile .welcome-title {
- font-size: clamp(28px, 7.8vw, 38px);
- line-height: 1.06;
- letter-spacing: -0.03em;
+ font-size: var(--type-page-title-size);
+ line-height: var(--type-page-title-line-height);
+ font-weight: var(--type-page-title-weight);
+ letter-spacing: 0;
}
.auth-card-shell--mobile .welcome-body {
- font-size: 15px;
- line-height: 1.65;
+ font-size: var(--type-body-size);
+ line-height: var(--type-body-line-height);
+ font-weight: var(--type-body-weight);
}
.auth-card-shell--mobile .auth-status-panel {
- padding: var(--sp-4);
- border-radius: var(--radius-xl);
+ padding-top: var(--sp-4);
+ border-radius: var(--radius-md);
}
.settings-page--mobile {
@@ -10539,9 +11029,10 @@ textarea.input {
margin: 0;
padding: 0 var(--sp-1);
color: var(--text-secondary);
- font-size: 11px;
- font-weight: var(--font-semibold);
- letter-spacing: 0.08em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-transform: uppercase;
}
@@ -10596,8 +11087,9 @@ textarea.input {
}
.settings-mobile-item__label {
- font-size: 15px;
- font-weight: var(--font-semibold);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
color: var(--text-primary);
}
@@ -10607,8 +11099,9 @@ textarea.input {
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
color: var(--text-tertiary);
- font-size: 11px;
- line-height: 1.45;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.settings-mobile-item__arrow {
@@ -10759,7 +11252,9 @@ textarea.input {
}
.mobile-shell--landscape-compact .mobile-dock__label {
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-shell--landscape-compact .mobile-sheet {
@@ -10872,8 +11367,9 @@ textarea.input {
border: none;
background: transparent;
color: var(--text-tertiary);
- font-size: 11px;
- line-height: 1;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
white-space: nowrap;
}
@@ -10927,7 +11423,7 @@ textarea.input {
display: flex;
min-height: var(--desktop-statusbar-height);
flex-shrink: 0;
- border-top: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
+ border-top: 1px solid color-mix(in srgb, var(--border) 62%, transparent);
background: var(--bg-panel);
}
@@ -10954,16 +11450,6 @@ textarea.input {
background: var(--bg-panel);
}
-.workspace-sidebar-panel__header {
- display: flex;
- align-items: flex-end;
- justify-content: space-between;
- gap: 12px;
- min-height: var(--desktop-sidebar-header-height);
- padding: 10px var(--desktop-panel-padding) 8px;
- border-bottom: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
-}
-
.workspace-sidebar-panel__tabs {
display: flex;
align-items: center;
@@ -10981,8 +11467,9 @@ textarea.input {
border: none;
background: transparent;
color: var(--text-tertiary);
- font-size: 13px;
- font-weight: 500;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
transition: color var(--duration-fast) var(--ease-out);
}
@@ -11060,7 +11547,9 @@ textarea.input {
.file-tree-shell .file-tree-search-input {
min-height: 30px;
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.file-tree-shell .file-tree {
@@ -11106,7 +11595,9 @@ textarea.input {
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-primary);
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.file-tree-shell .tree-search-labels {
@@ -11117,8 +11608,10 @@ textarea.input {
.file-tree-shell .tree-empty-hint,
.file-tree-shell .tree-loading {
color: var(--text-tertiary);
- font-size: 11px;
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.file-tree-shell .tree-item-actions {
@@ -11132,7 +11625,9 @@ textarea.input {
.file-tree-empty {
padding: 18px 16px;
color: var(--text-tertiary);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.file-tree-shell--desktop .tree-item-actions {
@@ -11145,8 +11640,8 @@ textarea.input {
opacity: 1;
}
-.file-tree-shell--mobile .file-tree-search {
- margin: 0 0 10px;
+.mobile-sheet--files .file-tree-shell--mobile .file-tree-search {
+ margin: 0;
border-right: none;
border-left: none;
border-radius: 0;
@@ -11154,11 +11649,11 @@ textarea.input {
background: transparent;
}
-.file-tree-shell--mobile .file-tree {
+.mobile-sheet--files .file-tree-shell--mobile .file-tree {
padding-bottom: 10px;
}
-.file-tree-shell--mobile .tree-item {
+.mobile-sheet--files .file-tree-shell--mobile .tree-item {
min-height: 40px;
margin: 0;
border-radius: 0;
@@ -11166,7 +11661,9 @@ textarea.input {
padding-bottom: 8px;
}
-.file-tree-shell--mobile .tree-item.selected {
+.mobile-sheet--files .file-tree-shell--mobile .tree-item.selected {
+ padding-left: 14px;
+ border-left: 2px solid color-mix(in srgb, var(--accent-blue) 88%, white 12%);
background: color-mix(in srgb, var(--accent-blue) 12%, transparent);
}
@@ -11230,9 +11727,10 @@ textarea.input {
border: none;
background: transparent;
color: var(--text-secondary);
- font-size: 11px;
- font-weight: 600;
- letter-spacing: 0.06em;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
+ letter-spacing: var(--type-kicker-letter-spacing);
text-align: left;
text-transform: uppercase;
}
@@ -11262,7 +11760,9 @@ textarea.input {
border: none;
background: transparent;
color: var(--text-tertiary);
- font-size: 11px;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.git-panel-section-link:hover {
@@ -11285,7 +11785,9 @@ textarea.input {
.git-panel-empty {
padding: 12px 0;
color: var(--text-tertiary);
- font-size: 12px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
}
.git-worktree-row {
@@ -11340,7 +11842,9 @@ textarea.input {
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-primary);
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.git-worktree-row__meta,
@@ -11349,14 +11853,18 @@ textarea.input {
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-tertiary);
- font-size: 11px;
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.git-worktree-row__chip {
flex-shrink: 0;
color: color-mix(in srgb, var(--accent-blue) 80%, white 20%);
- font-size: 11px;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.git-commit-block {
@@ -11375,8 +11883,9 @@ textarea.input {
border-radius: 8px;
background: color-mix(in srgb, var(--bg-surface) 92%, var(--bg-page));
color: var(--text-primary);
- font-size: 13px;
- line-height: 1.45;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.git-panel .git-commit-input:focus {
@@ -11406,8 +11915,9 @@ textarea.input {
border: none;
background: color-mix(in srgb, var(--accent-blue) 78%, white 22%);
color: #0b1020;
- font-size: 12px;
- font-weight: 600;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--font-medium);
}
.git-commit-primary:disabled {
@@ -11453,9 +11963,10 @@ textarea.input {
justify-content: center;
width: 18px;
flex-shrink: 0;
- font-family: var(--font-mono);
- font-size: 12px;
- font-weight: 600;
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.git-row-status-badge-added {
@@ -11485,7 +11996,9 @@ textarea.input {
.git-panel .git-row-name {
color: var(--text-primary);
- font-size: 13px;
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
+ font-weight: var(--type-body-strong-weight);
}
.git-panel .git-row-meta {
@@ -11499,8 +12012,10 @@ textarea.input {
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-tertiary);
- font-size: 11px;
- font-family: var(--font-mono);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
}
.git-panel .git-row-actions {
@@ -11562,7 +12077,9 @@ textarea.input {
.git-history-row__when {
color: var(--text-tertiary);
- font-size: 11px;
+ font-size: var(--type-meta-size);
+ line-height: var(--type-meta-line-height);
+ font-weight: var(--type-meta-weight);
font-variant-numeric: tabular-nums;
}
@@ -11574,7 +12091,9 @@ textarea.input {
min-height: 24px;
padding: 0 12px;
color: var(--text-tertiary);
- font-size: 11px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.git-panel-status-strip__branch,
@@ -11638,7 +12157,9 @@ textarea.input {
}
.git-panel-status-strip .git-status-bar__item {
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-files-sheet--root {
@@ -11653,17 +12174,11 @@ textarea.input {
align-items: center;
flex-shrink: 0;
gap: var(--sp-3);
- padding: var(--sp-3) var(--sp-3) var(--sp-2);
- border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
- border-radius: 18px;
- background: linear-gradient(
- 180deg,
- color-mix(in srgb, var(--bg-surface) 92%, transparent) 0%,
- color-mix(in srgb, var(--bg-page) 92%, var(--bg-surface) 8%) 100%
- );
- box-shadow:
- inset 0 1px 0 color-mix(in srgb, var(--fg-white) 5%, transparent),
- 0 10px 24px color-mix(in srgb, var(--bg-page) 16%, transparent);
+ padding: 0 0 var(--sp-2);
+ border-bottom: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
+ border-radius: 0;
+ background: transparent;
+ box-shadow: none;
}
.mobile-files-sheet__segment {
@@ -11672,14 +12187,15 @@ textarea.input {
align-items: center;
justify-content: flex-start;
gap: 6px;
- min-height: 36px;
- padding: 0 4px;
+ min-height: 32px;
+ padding: 0;
border: none;
- border-radius: 999px;
+ border-radius: 0;
background: transparent;
color: var(--text-tertiary);
- font-size: 13px;
- font-weight: 500;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
transition: color var(--duration-fast) var(--ease-out);
}
@@ -11695,10 +12211,10 @@ textarea.input {
.mobile-files-sheet__segment.active::after {
content: "";
position: absolute;
- right: 4px;
- bottom: 2px;
- left: 4px;
- height: 2px;
+ right: 0;
+ bottom: -10px;
+ left: 0;
+ height: 1.5px;
border-radius: 999px;
background: color-mix(in srgb, var(--accent-blue) 90%, white 10%);
}
@@ -11706,10 +12222,9 @@ textarea.input {
.mobile-files-sheet__tab-actions {
display: inline-flex;
align-items: center;
- gap: 6px;
+ gap: 4px;
flex-shrink: 0;
margin-left: auto;
- padding-bottom: 0;
}
.mobile-files-sheet__tab-action {
@@ -11719,9 +12234,9 @@ textarea.input {
width: 32px;
height: 32px;
padding: 0;
- border: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
- border-radius: 999px;
- background: color-mix(in srgb, var(--bg-surface) 74%, transparent);
+ border: none;
+ border-radius: 6px;
+ background: transparent;
color: var(--text-tertiary);
transition:
background-color var(--duration-fast) var(--ease-out),
@@ -11775,7 +12290,9 @@ textarea.input {
.git-panel--mobile .git-panel-section-toggle,
.git-panel--mobile .git-panel-section-link {
- font-size: 12px;
+ font-size: var(--type-label-size);
+ line-height: var(--type-label-line-height);
+ font-weight: var(--type-label-weight);
}
.git-panel--mobile .git-panel-section-actions {
@@ -11812,7 +12329,9 @@ textarea.input {
.mobile-shell__bottom-stack .git-panel-status-strip {
gap: 8px;
min-height: 28px;
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
padding: 0 calc(var(--mobile-safe-right) + var(--sp-4)) 0
calc(var(--mobile-safe-left) + var(--sp-4));
}
@@ -11833,7 +12352,9 @@ textarea.input {
}
.mobile-shell__bottom-stack .git-panel-status-strip .git-status-bar__item {
- font-size: 10px;
+ font-size: var(--type-kicker-size);
+ line-height: var(--type-kicker-line-height);
+ font-weight: var(--type-kicker-weight);
}
.mobile-sheet__footer .git-panel-status-strip {
@@ -11871,8 +12392,9 @@ textarea.input {
.paste-dialog__title {
margin: 0;
- font-size: var(--text-base);
- font-weight: var(--font-semibold);
+ font-size: var(--type-section-title-size);
+ line-height: var(--type-section-title-line-height);
+ font-weight: var(--type-section-title-weight);
color: var(--text-primary);
}
@@ -11883,8 +12405,10 @@ textarea.input {
border: 1px solid var(--border);
border-radius: var(--radius-md);
color: var(--text-primary);
- font-family: var(--font-mono);
- font-size: var(--text-sm);
+ font-family: var(--type-code-inline-family);
+ font-size: var(--type-code-inline-size);
+ line-height: var(--type-code-inline-line-height);
+ font-weight: var(--type-code-inline-weight);
resize: vertical;
}
@@ -11904,7 +12428,8 @@ textarea.input {
padding: var(--sp-2) var(--sp-4);
border: 1px solid var(--border);
border-radius: var(--radius-md);
- font-size: var(--text-sm);
+ font-size: var(--type-body-strong-size);
+ line-height: var(--type-body-strong-line-height);
font-weight: var(--font-medium);
cursor: pointer;
transition: all var(--duration-fast) var(--ease-out);
diff --git a/packages/web/src/styles/components.theme.test.ts b/packages/web/src/styles/components.theme.test.ts
index 40e5b9f0a..da77596aa 100644
--- a/packages/web/src/styles/components.theme.test.ts
+++ b/packages/web/src/styles/components.theme.test.ts
@@ -1,5 +1,7 @@
// @vitest-environment node
-import { readFileSync } from "node:fs";
+import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
+import { join } from "node:path";
+import { build } from "vite";
import { describe, expect, it } from "vitest";
const baseStylesheet = readFileSync(`${process.cwd()}/src/styles/base.css`, "utf8");
@@ -17,10 +19,27 @@ const pillStylesheet = readFileSync(
`${process.cwd()}/src/components/ui/pill/index.module.css`,
"utf8"
);
+const tagStyles = readFileSync(`${process.cwd()}/src/components/ui/tag/index.module.css`, "utf8");
+const badgeStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/badge/index.module.css`,
+ "utf8"
+);
+const tooltipStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/tooltip/index.module.css`,
+ "utf8"
+);
const noticeStylesheet = readFileSync(
`${process.cwd()}/src/components/ui/notice/index.module.css`,
"utf8"
);
+const modalStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/modal/index.module.css`,
+ "utf8"
+);
+const emptyStateStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/empty-state/index.module.css`,
+ "utf8"
+);
const toastStyles = readFileSync(
`${process.cwd()}/src/components/ui/toast/index.module.css`,
"utf8"
@@ -29,6 +48,27 @@ const confirmDialogStyles = readFileSync(
`${process.cwd()}/src/components/ui/confirm-dialog/index.module.css`,
"utf8"
);
+const modalStylesheet = readFileSync(
+ `${process.cwd()}/src/components/ui/modal/index.module.css`,
+ "utf8"
+);
+const buttonStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/button/index.module.css`,
+ "utf8"
+);
+const popoverStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/popover/index.module.css`,
+ "utf8"
+);
+const inputStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/input/index.module.css`,
+ "utf8"
+);
+const textareaStyles = readFileSync(
+ `${process.cwd()}/src/components/ui/textarea/index.module.css`,
+ "utf8"
+);
+const tabsStyles = readFileSync(`${process.cwd()}/src/components/ui/tabs/index.module.css`, "utf8");
function getLastGroupedRuleBlockFrom(source: string, pattern: RegExp) {
const matches = Array.from(source.matchAll(pattern));
@@ -67,10 +107,81 @@ function getRuleBlocksFrom(source: string, selector: string) {
return blocks;
}
+function hasRuleBlockFrom(source: string, selector: string) {
+ const matcher = /([^{}]+)\{([^}]*)\}/g;
+ const normalizedSelector = selector.replace(/\s+/g, " ").trim();
+ let match: RegExpExecArray | null = null;
+
+ while ((match = matcher.exec(source))) {
+ const selectors = match[1]
+ .replace(/\/\*[\s\S]*?\*\//g, "")
+ .split(",")
+ .map((entry) => entry.replace(/\s+/g, " ").trim());
+
+ if (selectors.includes(normalizedSelector)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function hasRuleBlock(selector: string) {
+ return hasRuleBlockFrom(stylesheet, selector);
+}
+
function getLastRuleBlock(selector: string) {
return getLastRuleBlockFrom(stylesheet, selector);
}
+async function buildRuntimeStylesheet() {
+ const tempDir = mkdtempSync(join(process.cwd(), ".vite-style-build-"));
+ const entryFile = join(tempDir, "runtime-entry.ts");
+
+ writeFileSync(
+ entryFile,
+ [
+ 'import buttonStyles from "../src/components/ui/button/index.module.css";',
+ 'import tabsStyles from "../src/components/ui/tabs/index.module.css";',
+ "void [buttonStyles.btn, tabsStyles.tab];",
+ ].join("\n"),
+ "utf8"
+ );
+
+ try {
+ const output = await build({
+ configFile: false,
+ root: process.cwd(),
+ plugins: [],
+ build: {
+ write: false,
+ outDir: join(tempDir, "dist"),
+ cssCodeSplit: true,
+ lib: {
+ entry: entryFile,
+ formats: ["es"],
+ },
+ },
+ });
+
+ const bundle = Array.isArray(output) ? output[0] : output;
+ const chunks = "output" in bundle ? bundle.output : [];
+ const cssAssets = chunks.filter(
+ (chunk): chunk is { type: "asset"; fileName: string; source: string | Uint8Array } =>
+ chunk.type === "asset" && chunk.fileName.endsWith(".css")
+ );
+
+ expect(cssAssets.length, "expected built CSS asset").toBeGreaterThan(0);
+ return cssAssets
+ .map((asset) =>
+ typeof asset.source === "string" ? asset.source : Buffer.from(asset.source).toString("utf8")
+ )
+ .join("\n");
+ } finally {
+ rmSync(tempDir, { recursive: true, force: true });
+ }
+}
+
describe("components.css theme-sensitive surfaces", () => {
it("routes file tree and git status icons through icon theme tokens", () => {
expect(getLastRuleBlock(".tree-icon")).toContain("width: 14px");
@@ -205,6 +316,129 @@ describe("components.css theme-sensitive surfaces", () => {
);
});
+ it("maps text-entry and navigation primitives onto semantic typography tokens", async () => {
+ expect(getLastRuleBlockFrom(buttonStyles, ".btn")).toContain(
+ "font-size: var(--type-body-strong-size)"
+ );
+ expect(getLastRuleBlockFrom(buttonStyles, ".btn")).toContain("font-weight: var(--font-medium)");
+ expect(getLastRuleBlockFrom(buttonStyles, ".btn")).toContain(
+ "line-height: var(--type-body-strong-line-height)"
+ );
+ expect(getLastRuleBlockFrom(buttonStyles, ".sm")).toContain(
+ "font-size: var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(buttonStyles, ".sm")).toContain("font-weight: var(--font-medium)");
+ expect(getLastRuleBlockFrom(buttonStyles, ".lg")).not.toContain("font-size:");
+
+ expect(getLastRuleBlockFrom(inputStyles, ".input")).toContain(
+ "font-size: var(--type-body-strong-size)"
+ );
+ expect(getLastRuleBlockFrom(inputStyles, ".input")).toContain(
+ "font-weight: var(--type-body-strong-weight)"
+ );
+ expect(getLastRuleBlockFrom(inputStyles, ".sm")).toContain("font-size: var(--type-label-size)");
+ expect(getLastRuleBlockFrom(inputStyles, ".sm")).toContain(
+ "font-weight: var(--type-label-weight)"
+ );
+ expect(getLastRuleBlockFrom(inputStyles, ".lg")).not.toContain("font-size:");
+
+ expect(getLastRuleBlockFrom(textareaStyles, ".input")).toContain(
+ "font-size: var(--type-body-strong-size)"
+ );
+ expect(getLastRuleBlockFrom(textareaStyles, ".input")).toContain(
+ "font-weight: var(--type-body-strong-weight)"
+ );
+ expect(getLastRuleBlockFrom(textareaStyles, ".lg")).not.toContain("font-size:");
+
+ expect(getLastRuleBlockFrom(tabsStyles, ":global(.panel-tab)")).toContain(
+ "font-size: var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(tabsStyles, ":global(.panel-tab)")).toContain(
+ "font-weight: var(--type-label-weight)"
+ );
+ expect(getLastRuleBlockFrom(tabsStyles, ":global(.panel-tab)")).toContain(
+ "line-height: var(--type-label-line-height)"
+ );
+ expect(getLastRuleBlockFrom(tabsStyles, ":global(.worktree-tab)")).toContain(
+ "font-size: var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(tabsStyles, ":global(.worktree-tab)")).toContain(
+ "font-weight: var(--type-label-weight)"
+ );
+
+ const runtimeStylesheet = await buildRuntimeStylesheet();
+
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".btn")).toContain(
+ "font-size:var(--type-body-strong-size)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".btn")).toContain(
+ "font-weight:var(--font-medium)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".btn-sm")).toContain(
+ "font-size:var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".panel-tab")).toContain(
+ "font-size:var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".panel-tab")).toContain(
+ "font-weight:var(--type-label-weight)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".worktree-tab")).toContain(
+ "font-size:var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(runtimeStylesheet, ".worktree-tab")).toContain(
+ "font-weight:var(--type-label-weight)"
+ );
+ });
+
+ it("keeps desktop popovers above modal shells so picker overlays are not occluded", () => {
+ expect(getLastRuleBlockFrom(modalStylesheet, ":global(.modal-overlay)")).toContain(
+ "z-index: var(--z-modal-backdrop)"
+ );
+ expect(getLastRuleBlockFrom(popoverStyles, ".content")).toContain("z-index: var(--z-popover)");
+ });
+
+ it("maps display and status primitives onto semantic typography roles", () => {
+ expect(getLastRuleBlockFrom(tagStyles, ":where(.tag)")).toContain(
+ "font-size: var(--type-kicker-size)"
+ );
+ expect(getLastRuleBlockFrom(tagStyles, ":where(.tag)")).toContain(
+ "letter-spacing: var(--type-kicker-letter-spacing)"
+ );
+ expect(getLastRuleBlockFrom(tagStyles, ".sm")).not.toContain("font-size:");
+
+ expect(getLastRuleBlockFrom(badgeStyles, ":where(.badge)")).toContain(
+ "font-size: var(--type-kicker-size)"
+ );
+ expect(getLastRuleBlockFrom(badgeStyles, ":where(.badge)")).toContain(
+ "font-weight: var(--type-kicker-weight)"
+ );
+ expect(getLastRuleBlockFrom(pillStylesheet, ".pill")).toContain(
+ "font-size: var(--type-label-size)"
+ );
+ expect(getLastRuleBlockFrom(pillStylesheet, ".pill")).toContain(
+ "font-weight: var(--type-label-weight)"
+ );
+ expect(getLastRuleBlockFrom(tooltipStyles, ".tooltip")).toContain(
+ "font-size: var(--type-meta-size)"
+ );
+ expect(getLastRuleBlockFrom(noticeStylesheet, ".title")).toContain(
+ "font-size: var(--type-kicker-size)"
+ );
+ expect(getLastRuleBlockFrom(noticeStylesheet, ".message")).toContain(
+ "font-size: var(--type-meta-size)"
+ );
+ expect(getLastRuleBlockFrom(modalStyles, ".title")).toContain(
+ "font-size: var(--type-section-title-size)"
+ );
+ expect(getLastRuleBlockFrom(emptyStateStyles, ".title")).toContain(
+ "font-size: var(--type-app-title-size)"
+ );
+ expect(getLastRuleBlockFrom(emptyStateStyles, ".description")).toContain(
+ "font-size: var(--type-body-size)"
+ );
+ });
+
it("keeps config status colors on icon tokens", () => {
expect(getLastRuleBlock(".config-status--success")).toContain("var(--icon-success)");
expect(getLastRuleBlock(".config-status--warning")).toContain("var(--icon-warning)");
@@ -235,7 +469,42 @@ describe("components.css theme-sensitive surfaces", () => {
const resolvingCard = getLastRuleBlock(".workspace-resolving-card");
const mainStage = getLastRuleBlock(".workspace-main-stage");
const sessionTerminal = getLastRuleBlock(".session-terminal");
+ const sessionCard = getLastRuleBlock(".session-card");
+ const activeSessionCard = getLastRuleBlock(".session-card.session-card--active");
+ const activeSessionHeader = getLastRuleBlock(
+ ".session-card.session-card--active > .panel-header"
+ );
+ const activeSessionTitle = getLastRuleBlock(
+ ".session-card.session-card--active > .panel-header .panel-header__title"
+ );
+ const statusBar = getLastRuleBlock(".workspace-status-bar");
const agentPanes = getLastRuleBlock(".workspace-main-stage > .agent-panes");
+ const bottomPanel = getLastRuleBlock(".workspace-bottom-panel");
+ const verticalDividerRules = getRuleBlocksFrom(stylesheet, ".split-divider-v").join("\n");
+ const horizontalDividerRules = getRuleBlocksFrom(stylesheet, ".split-divider-h").join("\n");
+ const verticalDividerLineRules = getRuleBlocksFrom(stylesheet, ".split-divider-v::before").join(
+ "\n"
+ );
+ const horizontalDividerLineRules = getRuleBlocksFrom(
+ stylesheet,
+ ".split-divider-h::before"
+ ).join("\n");
+ const paneDividerBaseRules = getRuleBlocksFrom(stylesheet, ".pane-layout-divider").join("\n");
+ const paneDividerLineRules = getRuleBlocksFrom(stylesheet, ".pane-layout-divider::after").join(
+ "\n"
+ );
+ const paneDividerHorizontalRules = getRuleBlocksFrom(
+ stylesheet,
+ ".pane-layout-horizontal .pane-layout-divider"
+ ).join("\n");
+ const paneDividerVerticalRules = getRuleBlocksFrom(
+ stylesheet,
+ ".pane-layout-vertical .pane-layout-divider"
+ ).join("\n");
+ const bottomTerminalShellRules = getRuleBlocksFrom(
+ stylesheet,
+ ".workspace-bottom-panel > .bottom-terminal"
+ ).join("\n");
const bottomTerminalShell = getLastRuleBlock(".workspace-bottom-panel > .bottom-terminal");
expect(topbar).toContain("var(--bg-surface)");
@@ -250,38 +519,83 @@ describe("components.css theme-sensitive surfaces", () => {
expect(mainStage).toContain("flex-direction: column");
expect(agentPanes).toContain("flex: 1");
expect(agentPanes).toContain("min-height: 0");
+ expect(agentPanes).toContain("padding: 0");
expect(sessionTerminal).toContain("var(--bg-terminal)");
expect(sessionTerminal).not.toContain("rgba(11, 18, 24, 0.98)");
- expect(bottomTerminalShell).toContain("var(--bg-terminal)");
- expect(bottomTerminalShell).not.toContain("rgba(17, 24, 31, 0.96)");
+ expect(sessionCard).toContain("border: none");
+ expect(sessionCard).not.toContain("border: 1px solid var(--border)");
+ expect(activeSessionCard).toContain("background: var(--bg-active)");
+ expect(activeSessionCard).toContain("box-shadow: inset 0 0 0 1px var(--border-focus)");
+ expect(activeSessionHeader).toContain(
+ "background: color-mix(in srgb, var(--bg-active) 88%, var(--bg-page) 12%)"
+ );
+ expect(activeSessionTitle).toContain("color: var(--text-primary)");
+ expect(verticalDividerRules).toContain("width: 10px");
+ expect(verticalDividerRules).not.toContain("width: 8px");
+ expect(verticalDividerRules).toContain("margin-left: -5px");
+ expect(verticalDividerRules).toContain("margin-right: -5px");
+ expect(verticalDividerLineRules).toContain(
+ "background: color-mix(in srgb, var(--border) 62%, transparent)"
+ );
+ expect(horizontalDividerRules).toContain("height: 10px");
+ expect(horizontalDividerRules).not.toContain("height: 8px");
+ expect(horizontalDividerRules).toContain("margin-top: -5px");
+ expect(horizontalDividerRules).toContain("margin-bottom: -5px");
+ expect(horizontalDividerLineRules).toContain(
+ "background: color-mix(in srgb, var(--border) 62%, transparent)"
+ );
+ expect(paneDividerBaseRules).toContain("position: relative");
+ expect(paneDividerLineRules).toContain(
+ "background: color-mix(in srgb, var(--border) 62%, transparent)"
+ );
+ expect(paneDividerHorizontalRules).toContain("width: 10px");
+ expect(paneDividerHorizontalRules).toContain("margin-left: -5px");
+ expect(paneDividerHorizontalRules).toContain("margin-right: -5px");
+ expect(paneDividerVerticalRules).toContain("height: 10px");
+ expect(paneDividerVerticalRules).toContain("margin-top: -5px");
+ expect(paneDividerVerticalRules).toContain("margin-bottom: -5px");
+ expect(bottomPanel).toContain("padding: 0");
+ expect(bottomPanel).not.toContain("padding: 0 0 14px");
+ expect(bottomPanel).not.toContain("padding: 0 14px 14px");
+ expect(bottomTerminalShellRules).toContain("var(--bg-terminal)");
+ expect(bottomTerminalShellRules).not.toContain("rgba(17, 24, 31, 0.96)");
+ expect(bottomTerminalShell).toContain("border: none");
+ expect(bottomTerminalShellRules).toContain("border-radius: 0");
+ expect(bottomTerminalShellRules).not.toContain("border-radius: 14px");
+ expect(statusBar).toContain(
+ "border-top: 1px solid color-mix(in srgb, var(--border) 62%, transparent)"
+ );
});
it("maps desktop chrome blocks to the dedicated desktop layout tokens", () => {
const topbar = getLastRuleBlock(".app-topbar");
const statusBar = getLastRuleBlock(".workspace-status-bar");
- const sidebarHeader = getLastRuleBlock(".workspace-sidebar-panel__header");
const commandPalette = getLastRuleBlock(".command-palette");
const launchModal = getLastRuleBlock(".launch-modal");
expect(topbar).toContain("min-height: var(--desktop-topbar-height)");
expect(statusBar).toContain("min-height: var(--desktop-statusbar-height)");
- expect(sidebarHeader).toContain("min-height: var(--desktop-sidebar-header-height)");
- expect(sidebarHeader).toContain("padding: 10px var(--desktop-panel-padding) 8px");
expect(commandPalette).toContain("max-width: var(--desktop-modal-max-width-md)");
expect(launchModal).toContain("max-width: min(var(--desktop-modal-max-width-lg), 90vw)");
});
- it("keeps auth shells theme-aware instead of forcing dark gradients", () => {
+ it("keeps auth and welcome shells on flat page surfaces", () => {
const authScreen = getLastRuleBlock(".auth-screen");
const authCard = getLastRuleBlock(".auth-card-shell");
+ const welcomeCard = getLastRuleBlock(".welcome-card");
+ const welcomeFeature = getLastRuleBlock(".welcome-feature");
expect(authScreen).toContain("var(--bg-page)");
- expect(authScreen).toContain("var(--accent-green)");
- expect(authScreen).toContain("var(--accent-blue)");
+ expect(authScreen).not.toContain("radial-gradient(");
expect(authScreen).not.toContain("rgba(17, 24, 31, 0.96)");
- expect(authCard).toContain("var(--bg-surface)");
- expect(authCard).toContain("var(--accent-blue)");
- expect(authCard).toContain("var(--shadow-xl)");
+ expect(authCard).toContain("background: var(--bg-surface)");
+ expect(authCard).toContain("box-shadow: var(--shadow-sm)");
+ expect(authCard).not.toContain("linear-gradient(");
+ expect(welcomeCard).toContain("background: var(--bg-surface)");
+ expect(welcomeCard).toContain("align-items: stretch");
+ expect(welcomeCard).not.toContain("box-shadow: var(--shadow-xl)");
+ expect(welcomeFeature).toContain("background: transparent");
+ expect(welcomeFeature).not.toContain("min-height: 148px");
expect(authCard).not.toContain("rgba(13, 20, 26, 0.94)");
});
@@ -453,6 +767,7 @@ describe("components.css theme-sensitive surfaces", () => {
const pageHeader = getLastRuleBlock(".mobile-sheet--fullscreen .page-header");
const mobilePageHeader = getLastRuleBlock(".mobile-page-header");
const mobilePageHeaderLeading = getLastRuleBlock(".mobile-page-header .page-header__leading");
+ const mobilePageHeaderTitle = getLastRuleBlock(".mobile-page-header .page-header__title");
const mobilePageHeaderBack = getLastRuleBlock(".mobile-page-header .page-header__back");
const headerLeading = getLastRuleBlock(".page-header__leading");
const backButton = getLastRuleBlock(".mobile-sheet--fullscreen .page-header__back");
@@ -460,16 +775,177 @@ describe("components.css theme-sensitive surfaces", () => {
expect(fullscreenHeader).toContain("padding: 0 var(--sp-3)");
expect(pageHeader).toContain("width: 100%");
- expect(mobilePageHeader).toContain("min-height: 38px");
+ expect(mobilePageHeader).toContain("min-height: 44px");
expect(mobilePageHeaderLeading).toContain("gap: 8px");
+ expect(mobilePageHeaderTitle).toContain("font-size: var(--type-app-title-size)");
+ expect(mobilePageHeaderTitle).toContain("line-height: var(--type-app-title-line-height)");
+ expect(mobilePageHeaderTitle).toContain("font-weight: var(--type-app-title-weight)");
expect(mobilePageHeaderBack).toContain("min-height: 26px");
expect(mobilePageHeaderBack).toContain("font-family: var(--font-mono)");
expect(headerLeading).toContain("flex: 1");
expect(backButton).toContain("background: transparent");
expect(backButton).not.toContain("border-radius: 999px");
+ expect(hasRuleBlock(".mobile-sheet--fullscreen .page-header__title")).toBe(false);
expect(headerActions).toContain("margin-left: auto");
});
+ it("keeps page header levels on the approved desktop sizing contract", () => {
+ const baseTitle = getLastRuleBlock(".page-header__title");
+ const primaryHeader = getLastRuleBlock(".page-header--primary");
+ const secondaryHeader = getLastRuleBlock(".page-header--secondary");
+ const primaryTitle = getLastRuleBlock(".page-header--primary .page-header__title");
+ const secondaryTitle = getLastRuleBlock(".page-header--secondary .page-header__title");
+
+ expect(baseTitle).toContain("font-weight: var(--type-app-title-weight)");
+ expect(primaryHeader).toContain("min-height: 56px");
+ expect(secondaryHeader).toContain("min-height: 48px");
+ expect(primaryTitle).toContain("font-size: var(--type-section-title-size)");
+ expect(primaryTitle).toContain("line-height: var(--type-section-title-line-height)");
+ expect(primaryTitle).toContain("font-weight: var(--type-section-title-weight)");
+ expect(secondaryTitle).toContain("font-size: var(--type-app-title-size)");
+ expect(secondaryTitle).toContain("line-height: var(--type-app-title-line-height)");
+ expect(secondaryTitle).toContain("font-weight: var(--type-app-title-weight)");
+ });
+
+ it("keeps panel headers on the approved dense container chrome contract", () => {
+ const panelHeader =
+ [...getRuleBlocksFrom(stylesheet, ".panel-header")]
+ .reverse()
+ .find((block) => block.includes("padding: 6px 12px")) ?? "";
+ const leading = getLastRuleBlock(".panel-header__leading");
+ const copy = getLastRuleBlock(".panel-header__copy");
+ const title = getLastRuleBlock(".panel-header__title");
+ const meta = getLastRuleBlock(".panel-header__meta");
+ const actions = getLastRuleBlock(".panel-header__actions");
+ const mobilePanelHeader = getLastGroupedRuleBlockFrom(
+ stylesheet,
+ /@media \(max-width: 640px\)\s*\{[\s\S]*?\n\s*\.panel-header\s*\{([^}]*)\}/g
+ );
+
+ expect(panelHeader).toContain("min-height: 40px");
+ expect(panelHeader).toContain("padding: 6px 12px");
+ expect(panelHeader).toContain("border-bottom: 1px solid var(--border)");
+ expect(leading).toContain("flex: 1");
+ expect(copy).toContain("min-width: 0");
+ expect(title).toContain("font-size: var(--type-app-title-size)");
+ expect(title).toContain("line-height: var(--type-app-title-line-height)");
+ expect(title).toContain("font-weight: var(--type-app-title-weight)");
+ expect(meta).toContain("display: flex");
+ expect(actions).toContain("margin-left: auto");
+ expect(actions).toContain("flex-shrink: 0");
+ expect(mobilePanelHeader).toContain("min-height: 44px");
+ expect(hasRuleBlock(".session-header .panel-header__title")).toBe(false);
+ expect(hasRuleBlock(".mobile-shell__agent-stage .session-header .panel-header__actions")).toBe(
+ false
+ );
+ expect(hasRuleBlock(".code-editor-header .panel-header__title")).toBe(false);
+ expect(hasRuleBlock(".workspace-sidebar-panel__header .panel-header__title-row")).toBe(false);
+ });
+
+ it("keeps dialog headers on the approved modal header contract", () => {
+ const modalTitle = getLastRuleBlockFrom(modalStylesheet, ".title");
+ const dialogHeader =
+ getRuleBlocksFrom(modalStylesheet, ".dialogHeader").find((block) =>
+ block.includes("align-items: flex-start")
+ ) ?? "";
+ const dialogDescription = getLastRuleBlockFrom(
+ modalStylesheet,
+ ":global(.dialog-header__description)"
+ );
+ const dialogIcon = getLastRuleBlock(".supervisor-dialog-header-icon");
+ const editTone = getLastRuleBlock(".supervisor-dialog--edit .supervisor-dialog-header-icon");
+ const disableTone = getLastRuleBlock(
+ ".supervisor-dialog--disable .supervisor-dialog-header-icon"
+ );
+
+ expect(modalTitle).toContain("font-size: var(--type-section-title-size)");
+ expect(modalTitle).toContain("line-height: var(--type-section-title-line-height)");
+ expect(modalTitle).toContain("font-weight: var(--type-section-title-weight)");
+ expect(dialogDescription).toContain("font-size: var(--type-meta-size)");
+ expect(dialogDescription).toContain("line-height: var(--type-meta-line-height)");
+ expect(dialogDescription).toContain("font-weight: var(--type-meta-weight)");
+ expect(dialogHeader).toContain("align-items: flex-start");
+ expect(dialogIcon).toContain("width: 28px");
+ expect(dialogIcon).toContain("height: 28px");
+ expect(editTone).toContain("var(--icon-surface-info)");
+ expect(editTone).toContain("var(--icon-info)");
+ expect(disableTone).toContain("var(--icon-surface-error)");
+ expect(disableTone).toContain("var(--icon-error)");
+ expect(hasRuleBlock(".supervisor-dialog-header")).toBe(false);
+ expect(hasRuleBlock(".supervisor-dialog-subtitle")).toBe(false);
+ expect(hasRuleBlock(".supervisor-dialog .modal-header h3")).toBe(false);
+ });
+
+ it("keeps supervisor dialog body styling flat and dense", () => {
+ const modalBody = getLastRuleBlock(".supervisor-dialog .modal-body");
+ const formGroup = getLastRuleBlock(".supervisor-dialog .form-group");
+ const intro = getLastRuleBlock(".supervisor-dialog-intro");
+ const introEditTone = getLastRuleBlock(
+ ".supervisor-dialog--edit .supervisor-dialog-intro__icon"
+ );
+ const introDisableTone = getLastRuleBlock(
+ ".supervisor-dialog--disable .supervisor-dialog-intro__icon"
+ );
+ const introTitle = getLastRuleBlock(".supervisor-dialog-intro__title");
+ const introDescription = getLastRuleBlock(".supervisor-dialog-intro__description");
+ const compactInputGroup = getLastGroupedRuleBlock(
+ /\.supervisor-dialog \.input,\s*\n\.supervisor-dialog \.mobile-select-trigger\s*\{([^}]*)\}/g
+ );
+ const textarea = getLastRuleBlock(".supervisor-dialog .textarea");
+ const dangerCallout = getLastRuleBlock(".supervisor-danger-callout");
+ const dangerCalloutCopy = getLastRuleBlock(".supervisor-danger-callout-copy");
+
+ expect(modalBody).toContain("gap: var(--sp-3)");
+ expect(formGroup).toContain("gap: 6px");
+ expect(intro).toContain("display: flex");
+ expect(intro).toContain("padding: var(--sp-3)");
+ expect(intro).toContain("border: 1px solid color-mix(in srgb, var(--border) 90%, transparent)");
+ expect(intro).toContain(
+ "background: color-mix(in srgb, var(--bg-surface) 88%, var(--bg-hover))"
+ );
+ expect(introEditTone).toContain("var(--icon-surface-info)");
+ expect(introEditTone).toContain("var(--icon-info)");
+ expect(introDisableTone).toContain("var(--icon-surface-error)");
+ expect(introDisableTone).toContain("var(--icon-error)");
+ expect(introTitle).toContain("font-size: var(--type-body-size)");
+ expect(introTitle).toContain("line-height: var(--type-body-line-height)");
+ expect(introDescription).toContain("font-size: var(--type-meta-size)");
+ expect(introDescription).toContain("line-height: var(--type-meta-line-height)");
+ expect(compactInputGroup).toContain("font-size: var(--type-label-size)");
+ expect(compactInputGroup).toContain("line-height: var(--type-label-line-height)");
+ expect(textarea).toContain("font-size: var(--type-code-inline-size)");
+ expect(textarea).toContain("min-height: 104px");
+ expect(textarea).toContain("color: var(--text-secondary)");
+ expect(dangerCallout).toContain("gap: var(--sp-2)");
+ expect(dangerCallout).toContain("padding: var(--sp-2) var(--sp-3)");
+ expect(dangerCallout).toContain("border-left-width: 1px");
+ expect(dangerCalloutCopy).toContain("font-size: var(--type-label-size)");
+ expect(dangerCalloutCopy).toContain("line-height: var(--type-label-line-height)");
+ });
+
+ it("does not allow page or modal wrappers to override approved header typography tokens", () => {
+ const settingsBack = getLastRuleBlock(
+ ".settings-header .mobile-page-header .page-header__back"
+ );
+ const mobileSettingsBack = getLastRuleBlock(
+ ".settings-page--mobile > .settings-header .mobile-page-header .page-header__back"
+ );
+
+ expect(hasRuleBlock(".mobile-sheet--fullscreen .page-header__title")).toBe(false);
+ expect(hasRuleBlock(".settings-header .mobile-page-header .page-header__title")).toBe(false);
+ expect(
+ hasRuleBlock(
+ ".settings-page--mobile > .settings-header .mobile-page-header .page-header__title"
+ )
+ ).toBe(false);
+ expect(settingsBack).not.toContain("font-size:");
+ expect(settingsBack).not.toContain("line-height:");
+ expect(settingsBack).not.toContain("font-weight:");
+ expect(mobileSettingsBack).not.toContain("font-size:");
+ expect(mobileSettingsBack).not.toContain("line-height:");
+ expect(mobileSettingsBack).not.toContain("font-weight:");
+ });
+
it("uses a unified inline sheet treatment for mobile selectors and keeps topbar controls height-aligned", () => {
const inlineSheet = getLastRuleBlock(".mobile-inline-sheet");
const inlineSheetAction = getLastRuleBlock(".mobile-inline-sheet__action");
@@ -529,10 +1005,12 @@ describe("components.css theme-sensitive surfaces", () => {
expect(mobileDock).toContain("grid-template-columns: repeat(3, minmax(0, 1fr))");
expect(mobileDock).toContain("gap: var(--sp-2)");
+ expect(mobileDock).toContain("min-height: 36px");
expect(mobileDockItem).toBeTruthy();
expect(mobileDockItem).toContain("border-top: 1px solid transparent");
expect(mobileDockItem).toContain("background: transparent");
- expect(mobileDockItem).toContain("min-height: 30px");
+ expect(mobileDockItem).toContain("min-height: 36px");
+ expect(mobileDockItem).toContain("height: 36px");
expect(activeDockItem).toBeTruthy();
expect(activeDockItem).toContain("border-top-color:");
});
@@ -553,9 +1031,7 @@ describe("components.css theme-sensitive surfaces", () => {
it("keeps settings navigation aligned with desktop editor chrome on both desktop and mobile", () => {
const settingsPage = getLastRuleBlock(".settings-page");
const baseSettingsHeader = getRuleBlocksFrom(stylesheet, ".settings-header")[0];
- const desktopSettingsHeader = getLastRuleBlock(".settings-header__desktop");
- const desktopSettingsCopy = getLastRuleBlock(".settings-header__copy");
- const desktopSettingsTitle = getLastRuleBlock(".settings-header__title");
+ const desktopSettingsHeader = getLastRuleBlock(".page-header--secondary");
const settingsBody = getLastRuleBlock(".settings-body");
const settingsSidebar = getLastRuleBlock(".settings-sidebar");
const settingsContent = getLastRuleBlock(".settings-content");
@@ -585,16 +1061,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(baseSettingsHeader).toContain("background: var(--bg-surface)");
expect(baseSettingsHeader).toContain("border-bottom: 1px solid var(--border)");
expect(baseSettingsHeader).toContain("padding: var(--sp-1) var(--sp-4)");
- expect(desktopSettingsHeader).toContain("width: 100%");
- expect(desktopSettingsHeader).toContain("margin: 0");
- expect(desktopSettingsHeader).toContain("display: flex");
- expect(desktopSettingsHeader).toContain("align-items: center");
- expect(desktopSettingsHeader).toContain("justify-content: flex-start");
- expect(desktopSettingsCopy).toContain("justify-content: flex-start");
- expect(desktopSettingsCopy).toContain("flex: 0 1 auto");
- expect(desktopSettingsCopy).toContain("gap: 0");
- expect(desktopSettingsTitle).toContain("font-size: 18px");
- expect(desktopSettingsTitle).toContain("line-height: 1.05");
+ expect(desktopSettingsHeader).toContain("min-height: 48px");
expect(settingsBody).toContain("align-items: stretch");
expect(settingsBody).toContain("background: var(--bg-page)");
expect(settingsSidebar).toContain("background: var(--bg-panel)");
@@ -706,7 +1173,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(launchActionRail).toContain("background: color-mix(");
expect(launchActionButton).toContain("min-height: 44px");
expect(launchActionButton).toContain("border-radius: var(--radius-md)");
- expect(launchActionButton).toContain("font-size: var(--text-sm)");
+ expect(launchActionButton).toContain("font-size: var(--type-body-strong-size)");
expect(launchActionButton).toContain("box-shadow: none");
});
@@ -716,6 +1183,9 @@ describe("components.css theme-sensitive surfaces", () => {
/\s+/g,
" "
);
+ const supervisorDetailInputs = getLastGroupedRuleBlock(
+ /\.mobile-supervisor-sheet__detail \.input,\s*\n\s*\.mobile-supervisor-sheet__detail \.mobile-select-trigger,\s*\n\s*\.mobile-supervisor-sheet__detail \.textarea\s*\{([^}]*)\}/g
+ );
const supervisorFullscreenFooter = getLastRuleBlock(
".mobile-supervisor-sheet.mobile-sheet--fullscreen .mobile-sheet__footer"
).replace(/\s+/g, " ");
@@ -726,23 +1196,35 @@ describe("components.css theme-sensitive surfaces", () => {
".mobile-supervisor-sheet__footer > .btn"
).replace(/\s+/g, " ");
- expect(supervisorRoot).toContain("padding: var(--sp-4)");
- expect(supervisorRoot).toContain("padding-bottom: var(--sp-5)");
- expect(supervisorDetail).toContain("padding: var(--sp-4)");
- expect(supervisorDetail).toContain("padding-bottom: var(--sp-5)");
+ expect(hasRuleBlock(".mobile-supervisor-sheet__detail-header")).toBe(false);
+ expect(supervisorRoot).toContain("padding: var(--sp-3)");
+ expect(supervisorRoot).toContain("padding-bottom: var(--sp-4)");
+ expect(supervisorRoot).not.toContain("border: 1px solid");
+ expect(supervisorRoot).not.toContain("border-radius:");
+ expect(supervisorRoot).not.toContain("box-shadow:");
+ expect(supervisorDetail).toContain("padding: var(--sp-3)");
+ expect(supervisorDetail).toContain("padding-bottom: var(--sp-4)");
+ expect(supervisorDetailInputs).toContain("font-size: var(--type-label-size)");
+ expect(supervisorDetailInputs).toContain("line-height: var(--type-label-line-height)");
+ expect(supervisorDetailInputs).toContain("font-weight: var(--type-label-weight)");
+ expect(supervisorDetail).not.toContain("border: 1px solid");
+ expect(supervisorDetail).not.toContain("border-radius:");
+ expect(supervisorDetail).not.toContain("box-shadow:");
expect(supervisorFullscreenFooter).toContain(
- "padding: var(--sp-2) var(--sp-4) calc(var(--mobile-safe-bottom) + var(--sp-4))"
+ "padding: var(--sp-1) var(--sp-3) calc(var(--mobile-safe-bottom) + var(--sp-3))"
);
expect(supervisorActionButton).toContain("min-height: 44px");
expect(supervisorFooterButton).toContain("min-height: 44px");
expect(supervisorFooterButton).toContain("box-shadow: none");
- expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain("padding: var(--sp-2)");
expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain(
- "border-radius: var(--radius-xl)"
+ "padding: var(--sp-1) var(--sp-2)"
);
+ expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain("border: none");
+ expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain("border-radius: 0");
expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain(
- "background: color-mix("
+ "background: transparent"
);
+ expect(getLastRuleBlock(".mobile-supervisor-sheet__footer")).toContain("box-shadow: none");
});
it("keeps the mobile workspace home screen aligned to settings chrome and editor-pane empty states", () => {
@@ -774,9 +1256,11 @@ describe("components.css theme-sensitive surfaces", () => {
expect(topbar).toContain(
"padding: calc(var(--mobile-safe-top) + var(--sp-1)) calc(var(--mobile-safe-right) + var(--sp-4)) var(--sp-1) calc(var(--mobile-safe-left) + var(--sp-4))"
);
- expect(topbarWorkspaceButton).toContain("border-bottom: 1px solid");
+ expect(topbarWorkspaceButton).toContain("border-bottom: none");
expect(topbarWorkspaceButton).toContain("border-radius: 0");
+ expect(topbarIconButton).toContain("border: none");
expect(topbarIconButton).toContain("border-radius: 10px");
+ expect(topbarIconButton).toContain("background: transparent");
expect(emptyStage).toContain("padding: clamp(34px, 9vh, 72px) var(--sp-4) var(--sp-3)");
expect(bottomStack).toContain("background: linear-gradient(");
expect(bottomStack).toContain("border-top: 1px solid color-mix(");
@@ -786,14 +1270,16 @@ describe("components.css theme-sensitive surfaces", () => {
);
expect(dock).toContain("gap: var(--sp-2)");
expect(dock).toContain("border-bottom: none");
+ expect(dock).toContain("min-height: 36px");
expect(dockItem).toBeTruthy();
- expect(dockItem).toContain("min-height: 30px");
- expect(dockItem).toContain("padding: 2px var(--sp-2) 2px");
- expect(dockLabel).toContain("font-size: 10px");
+ expect(dockItem).toContain("min-height: 36px");
+ expect(dockItem).toContain("height: 36px");
+ expect(dockItem).toContain("padding: 1px var(--sp-2) 0");
+ expect(dockLabel).toContain("font-size: var(--type-kicker-size)");
expect(statusBar).toContain("padding-bottom: calc(var(--mobile-safe-bottom) + var(--sp-1))");
expect(statusBar).toContain("border-top: 1px solid");
expect(statusStrip).toContain("min-height: 28px");
- expect(statusStrip).toContain("font-size: 10px");
+ expect(statusStrip).toContain("font-size: var(--type-kicker-size)");
expect(statusStrip).toContain(
"padding: 0 calc(var(--mobile-safe-right) + var(--sp-4)) 0 calc(var(--mobile-safe-left) + var(--sp-4))"
);
@@ -807,7 +1293,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(emptyState).toContain("width: 100%");
expect(emptyState).toContain("text-align: left");
expect(emptyState).toContain("gap: var(--sp-3)");
- expect(emptyTitle).toContain("font-size: clamp(24px, 6.4vw, 32px)");
+ expect(emptyTitle).toContain("font-size: var(--type-app-title-size)");
expect(placeholderCopy).toContain("gap: var(--sp-2)");
expect(emptyCta).toContain("min-height: 38px");
expect(emptyCta).toContain("width: auto");
@@ -822,8 +1308,24 @@ describe("components.css theme-sensitive surfaces", () => {
const mobileTerminalSheet = getLastRuleBlock(".mobile-terminal-sheet");
const mobileTerminal = getLastRuleBlock(".mobile-terminal-sheet .bottom-terminal");
const mobileFilesSurface = getLastRuleBlock(".mobile-sheet--files .file-tree-shell--mobile");
+ const mobileFilesGitSurface = getLastRuleBlock(".mobile-sheet--files .git-panel--mobile");
+ const mobileFilesSegmented = getLastRuleBlock(".mobile-files-sheet__segmented");
+ const mobileFilesSegment = getLastRuleBlock(".mobile-files-sheet__segment");
+ const mobileFilesSegmentActive = getLastRuleBlock(".mobile-files-sheet__segment.active");
+ const mobileFilesSegmentIndicator = getLastRuleBlock(
+ ".mobile-files-sheet__segment.active::after"
+ );
+ const mobileFilesTabAction = getLastRuleBlock(".mobile-files-sheet__tab-action");
+ const mobileFileSearch = getLastRuleBlock(
+ ".mobile-sheet--files .file-tree-shell--mobile .file-tree-search"
+ );
+ const mobileFileRow = getLastRuleBlock(
+ ".mobile-sheet--files .file-tree-shell--mobile .tree-item"
+ );
+ const mobileFileRowSelected = getLastRuleBlock(
+ ".mobile-sheet--files .file-tree-shell--mobile .tree-item.selected"
+ );
const supervisorRoot = getLastRuleBlock(".mobile-supervisor-sheet__root");
- const supervisorHeader = getLastRuleBlock(".mobile-supervisor-sheet__detail-header");
const drawerItem = getLastRuleBlock(".mobile-workspace-drawer__item");
const drawerFooterButton = getLastRuleBlock(".mobile-workspace-drawer__footer-button");
const welcomeCard = getLastRuleBlock(".welcome-card--mobile");
@@ -839,20 +1341,65 @@ describe("components.css theme-sensitive surfaces", () => {
expect(mobileTerminalSheet).not.toContain("linear-gradient(");
expect(mobileTerminal).toContain("border-radius: 0");
expect(mobileTerminal).not.toContain("var(--radius-xl) var(--radius-xl) 0 0");
- expect(mobileFilesSurface).toContain("border-radius: var(--radius-xl) var(--radius-xl) 0 0");
- expect(supervisorRoot).toContain("border-radius: var(--radius-xl) var(--radius-xl) 0 0");
- expect(supervisorHeader).toContain("border-radius: var(--radius-xl)");
+ expect(supervisorRoot).not.toContain("border-radius: var(--radius-xl) var(--radius-xl) 0 0");
+ expect(hasRuleBlock(".mobile-supervisor-sheet__detail-header")).toBe(false);
+ expect(mobileFilesSegmented).toContain(
+ "border-bottom: 1px solid color-mix(in srgb, var(--border) 78%, transparent)"
+ );
+ expect(mobileFilesSegmented).toContain("border-radius: 0");
+ expect(mobileFilesSegmented).not.toContain("linear-gradient(");
+ expect(mobileFilesSegmented).toContain("box-shadow: none");
+ expect(mobileFilesSegment).toContain("padding: 0");
+ expect(mobileFilesSegment).toContain("font-weight: var(--type-label-weight)");
+ expect(mobileFilesSegmentActive).toContain("background: transparent");
+ expect(mobileFilesSegmentIndicator).toContain("height: 1.5px");
+ expect(mobileFilesTabAction).toContain("border: none");
+ expect(mobileFilesTabAction).toContain("border-radius: 6px");
+ expect(mobileFilesTabAction).toContain("background: transparent");
+ expect(mobileFilesSurface).toContain(
+ "border: 1px solid color-mix(in srgb, var(--border) 80%, transparent)"
+ );
+ expect(mobileFilesSurface).toContain("border-radius: var(--radius-md)");
+ expect(mobileFilesSurface).toContain("box-shadow: none");
+ expect(mobileFilesSurface).not.toContain("linear-gradient(");
+ expect(mobileFilesGitSurface).toContain(
+ "border: 1px solid color-mix(in srgb, var(--border) 80%, transparent)"
+ );
+ expect(mobileFilesGitSurface).toContain("border-radius: var(--radius-md)");
+ expect(mobileFilesGitSurface).toContain("box-shadow: none");
+ expect(mobileFileSearch).toContain("margin: 0");
+ expect(mobileFileSearch).toContain("border-radius: 0");
+ expect(mobileFileSearch).toContain("border-right: none");
+ expect(mobileFileSearch).toContain("border-left: none");
+ expect(mobileFileSearch).toContain("background: transparent");
+ expect(mobileFileRow).toContain("min-height: 40px");
+ expect(mobileFileRow).toContain("border-radius: 0");
+ expect(mobileFileRowSelected).toContain(
+ "border-left: 2px solid color-mix(in srgb, var(--accent-blue) 88%, white 12%)"
+ );
+ expect(supervisorRoot).toContain("background: transparent");
+ expect(supervisorRoot).not.toContain("border-radius:");
expect(drawerItem).toContain("border-radius: var(--radius-xl)");
expect(drawerFooterButton).toContain("border-radius: var(--radius-md)");
- expect(welcomeCard).toContain("border-radius: var(--radius-xl)");
- expect(welcomeFeature).toContain("border-radius: var(--radius-xl)");
+ expect(welcomeCard).toContain("border-radius: var(--radius-lg)");
+ expect(welcomeFeature).toContain("border-radius: var(--radius-lg)");
expect(welcomeButton).toContain("border-radius: var(--radius-md)");
- expect(authCard).toContain("border-radius: var(--radius-xl)");
- expect(authStatusPanel).toContain("border-radius: var(--radius-xl)");
+ expect(authCard).toContain("border-radius: var(--radius-lg)");
+ expect(authStatusPanel).toContain("border-radius: var(--radius-md)");
expect(settingsItem).toContain("border-radius: 0");
expect(settingsItemIconShell).toContain("border-radius: var(--radius-xl)");
});
+ it("stacks mobile welcome and auth shells vertically so cards size to content", () => {
+ const welcomeContainer = getLastRuleBlock(".welcome-container--mobile");
+ const authScreen = getLastRuleBlock(".auth-screen--mobile");
+
+ expect(welcomeContainer).toContain("flex-direction: column");
+ expect(welcomeContainer).toContain("align-items: stretch");
+ expect(welcomeContainer).toContain("justify-content: flex-start");
+ expect(authScreen).toContain("padding:");
+ });
+
it("keeps settings content groups and provider controls aligned with editor configuration panels", () => {
const settingsGroup = getLastRuleBlock(".settings-group");
const settingsGroupTitle = getLastRuleBlock(".settings-group-title");
@@ -881,7 +1428,7 @@ describe("components.css theme-sensitive surfaces", () => {
const settingsFooter = getLastRuleBlock(".settings-footer");
expect(settingsGroup).toContain("margin-bottom: var(--sp-8)");
- expect(settingsGroupTitle).toContain("font-size: var(--text-base)");
+ expect(settingsGroupTitle).toContain("font-size: var(--type-kicker-size)");
expect(settingsGroupTitle).toContain("text-transform: uppercase");
expect(settingsGroupTitle).toContain("letter-spacing:");
expect(settingsGroupDesc).toContain("max-width:");
@@ -905,7 +1452,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(settingsInfoValueMobile).toContain("align-items: flex-start");
expect(settingsInfoValueMobile).toContain("min-width: 0");
expect(settingsInfoValueMobile).toContain("max-width: 100%");
- expect(settingsStatusHint).toContain("line-height: 1.45");
+ expect(settingsStatusHint).toContain("line-height: var(--type-meta-line-height)");
expect(settingsLink).toContain("display: inline-flex");
expect(settingsLink).toContain("font-weight: var(--font-medium)");
expect(providerTabs).toContain("gap: var(--sp-1)");
@@ -942,7 +1489,8 @@ describe("components.css theme-sensitive surfaces", () => {
expect(pill).toContain("border: 1px solid transparent");
expect(pill).toContain("border-radius: var(--radius-md)");
expect(pill).toContain("background: transparent");
- expect(pill).toContain("font-size: var(--text-sm)");
+ expect(pill).toContain("font-size: var(--type-label-size)");
+ expect(pill).toContain("font-weight: var(--type-label-weight)");
expect(pillHover).toContain("background: var(--bg-hover)");
expect(pillHover).toContain("border-color: color-mix");
expect(pillFocus).toContain("border-color: var(--border-focus)");
@@ -966,8 +1514,8 @@ describe("components.css theme-sensitive surfaces", () => {
expect(warning).toContain("background: color-mix");
expect(error).toContain("background: color-mix");
expect(title).toContain("text-transform: uppercase");
- expect(title).toContain("font-size: var(--text-xs)");
- expect(message).toContain("font-size: var(--text-sm)");
+ expect(title).toContain("font-size: var(--type-kicker-size)");
+ expect(message).toContain("font-size: var(--type-meta-size)");
expect(action).toContain("align-self: flex-start");
});
@@ -1156,13 +1704,13 @@ describe("components.css theme-sensitive surfaces", () => {
expect(configField).toContain("flex-direction: column");
expect(configField).toContain("gap: var(--sp-2)");
expect(configField).toContain("margin-bottom: var(--sp-4)");
- expect(configLabel).toContain("font-size: var(--text-xs)");
+ expect(configLabel).toContain("font-size: var(--type-kicker-size)");
expect(configLabel).toContain("text-transform: uppercase");
expect(configLabel).toContain("letter-spacing:");
expect(configLabel).toContain("margin-bottom: 0");
expect(argsInput).toContain("background: color-mix");
expect(argsInput).toContain("border-color: var(--border)");
- expect(argsInput).toContain("font-family: var(--font-mono)");
+ expect(argsInput).toContain("font-family: var(--type-code-inline-family)");
expect(commandPreview).toContain("min-height:");
expect(commandPreview).toContain("white-space: pre-wrap");
expect(commandPreview).toContain("border-radius: var(--radius-sm)");
@@ -1173,7 +1721,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(providerMobileEntryBase).toContain("padding: var(--sp-3) 0");
expect(providerMobileEntryMobile).toContain("padding-left: var(--sp-4)");
expect(providerMobileEntryMobile).toContain("padding-right: var(--sp-4)");
- expect(providerMobileEntryMeta).toContain("font-family: var(--font-mono)");
+ expect(providerMobileEntryMeta).toContain("font-family: var(--type-code-inline-family)");
});
it("keeps git panel sections stretched to the parent width with a full-width toggle hit area", () => {
@@ -1202,7 +1750,9 @@ describe("components.css theme-sensitive surfaces", () => {
expect(search).toContain("margin: 8px 12px 10px");
expect(search).toContain("border-radius: 8px");
expect(search).toContain("background: color-mix(");
- expect(searchInput).toContain("font-size: 13px");
+ expect(searchInput).toContain("font-size: var(--type-body-strong-size)");
+ expect(searchInput).toContain("line-height: var(--type-body-strong-line-height)");
+ expect(searchInput).toContain("font-weight: var(--type-body-strong-weight)");
expect(row).toContain("min-height: 26px");
expect(row).toContain("border-radius: 8px");
expect(row).toContain("transition:");
@@ -1239,7 +1789,7 @@ describe("components.css theme-sensitive surfaces", () => {
it("keeps session header badges on a single line by truncating the title first", () => {
const titleRow = getLastRuleBlock(".mobile-shell__agent-stage .session-title-row");
- const title = getLastRuleBlock(".mobile-shell__agent-stage .session-title");
+ const title = getLastRuleBlock(".panel-header__title");
const badges = getLastGroupedRuleBlock(
/\.mobile-shell__agent-stage \.session-provider-badge,\s*\.mobile-shell__agent-stage \.session-state-badge\s*\{([^}]*)\}/g
);
@@ -1248,6 +1798,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(title).toContain("overflow: hidden");
expect(title).toContain("text-overflow: ellipsis");
expect(title).toContain("white-space: nowrap");
+ expect(hasRuleBlock(".mobile-shell__agent-stage .session-title")).toBe(false);
expect(badges).toContain("flex-shrink: 0");
expect(badges).toContain("max-width: 100%");
});
@@ -1260,9 +1811,9 @@ describe("components.css theme-sensitive surfaces", () => {
);
const sessionCard = getLastRuleBlock(".mobile-shell__agent-stage > .session-card");
const progress = getLastRuleBlock(".mobile-shell__agent-stage .session-progress");
- const header = getLastRuleBlock(".mobile-shell__agent-stage .session-header");
- const headerLeft = getLastRuleBlock(".mobile-shell__agent-stage .session-header-left");
+ const header = getLastRuleBlock(".mobile-shell__agent-stage > .session-card > .panel-header");
const titleRow = getLastRuleBlock(".mobile-shell__agent-stage .session-title-row");
+ const headerRight = getLastRuleBlock(".mobile-shell__agent-stage .session-header-right");
const badges = getLastGroupedRuleBlock(
/\.mobile-shell__agent-stage \.session-provider-badge,\s*\.mobile-shell__agent-stage \.session-state-badge\s*\{([^}]*)\}/g
);
@@ -1282,14 +1833,21 @@ describe("components.css theme-sensitive surfaces", () => {
expect(header).toContain("padding: 4px");
expect(header).toContain("border-bottom:");
expect(header).not.toContain("linear-gradient(");
- expect(headerLeft).toContain("gap: 6px");
expect(titleRow).toContain("gap: 6px");
+ expect(headerRight).toContain("max-width: 100%");
+ expect(headerRight).not.toContain("max-width: min(48%, 220px)");
+ expect(hasRuleBlock(".mobile-shell__agent-stage .session-header")).toBe(false);
+ expect(hasRuleBlock(".mobile-shell__agent-stage .session-header-left")).toBe(false);
+ expect(hasRuleBlock(".mobile-shell__agent-stage .session-title")).toBe(false);
expect(badges).toContain("height: 15px");
expect(badges).toContain("border-radius: 3px");
expect(supervisorBadge).toContain("min-height: 26px");
+ expect(supervisorBadge).not.toContain("width: max-content");
expect(supervisorBadge).toContain("border-radius: 4px");
expect(supervisorBadge).not.toContain("border-radius: var(--radius-lg)");
- expect(supervisorLabel).toContain("font-size: 11px");
+ expect(supervisorLabel).toContain("font-size: var(--type-kicker-size)");
+ expect(supervisorLabel).toContain("line-height: var(--type-kicker-line-height)");
+ expect(supervisorLabel).toContain("font-weight: var(--type-kicker-weight)");
});
it("keeps supervisor entry icons and labels vertically centered", () => {
@@ -1305,11 +1863,12 @@ describe("components.css theme-sensitive surfaces", () => {
const mobileBadgeLabel = getLastRuleBlock(".mobile-supervisor-badge__label");
expect(desktopButton).toContain("justify-content: center");
- expect(desktopButton).toContain("line-height: 1");
+ expect(desktopButton).toContain("line-height: var(--type-label-line-height)");
expect(desktopButtonIcon).toContain("display: inline-flex");
expect(desktopButtonIconSvg).toContain("display: block");
expect(desktopButtonLabel).toContain("display: inline-flex");
expect(desktopButtonLabel).toContain("align-items: center");
+ expect(desktopButtonLabel).toContain("line-height: 1");
expect(mobileBadge).toContain("justify-content: center");
expect(mobileBadge).toContain("line-height: 1");
expect(mobileBadgeIcon).toContain("display: inline-flex");
@@ -1317,7 +1876,7 @@ describe("components.css theme-sensitive surfaces", () => {
expect(mobileBadgeIconSvg).toContain("display: block");
expect(mobileBadgeLabel).toContain("display: inline-flex");
expect(mobileBadgeLabel).toContain("align-items: center");
- expect(mobileBadgeLabel).toContain("line-height: 1");
+ expect(mobileBadgeLabel).toContain("line-height: var(--type-label-line-height)");
});
it("keeps the mobile terminal keybar flow-positioned and token-driven", () => {
diff --git a/packages/web/src/styles/tokens-touch.test.ts b/packages/web/src/styles/tokens-touch.test.ts
index 0bbc84af1..330387b40 100644
--- a/packages/web/src/styles/tokens-touch.test.ts
+++ b/packages/web/src/styles/tokens-touch.test.ts
@@ -95,6 +95,72 @@ describe("tokens.css touch tokens", () => {
expect(body).toContain("--touch-hit-slop: 8px");
});
+ it("defines the desktop typography scale and semantic aliases on :root", () => {
+ const root = getRuleBlock(":root");
+
+ expect(root).toContain("--font-size-100: 11px");
+ expect(root).toContain("--font-size-200: 12px");
+ expect(root).toContain("--font-size-300: 14px");
+ expect(root).toContain("--font-size-400: 16px");
+ expect(root).toContain("--font-size-500: 18px");
+ expect(root).toContain("--font-size-600: 24px");
+ expect(root).toContain("--font-size-700: 32px");
+
+ expect(root).toContain("--type-kicker-size: var(--font-size-100)");
+ expect(root).toContain("--type-kicker-line-height: 1.2");
+ expect(root).toContain("--type-kicker-weight: var(--font-normal)");
+ expect(root).toContain("--type-kicker-letter-spacing: 0.08em");
+
+ expect(root).toContain("--type-label-size: var(--font-size-200)");
+ expect(root).toContain("--type-label-line-height: 1.35");
+ expect(root).toContain("--type-label-weight: var(--font-normal)");
+
+ expect(root).toContain("--type-meta-size: var(--font-size-200)");
+ expect(root).toContain("--type-meta-line-height: 1.45");
+ expect(root).toContain("--type-meta-weight: var(--font-normal)");
+ expect(root).toContain("--type-body-size: var(--font-size-300)");
+ expect(root).toContain("--type-body-line-height: 1.5");
+ expect(root).toContain("--type-body-weight: var(--font-normal)");
+ expect(root).toContain("--type-body-strong-size: var(--font-size-300)");
+ expect(root).toContain("--type-body-strong-line-height: 1.45");
+ expect(root).toContain("--type-body-strong-weight: var(--font-normal)");
+ expect(root).toContain("--type-code-inline-size: var(--font-size-200)");
+ expect(root).toContain("--type-code-inline-line-height: 1.4");
+ expect(root).toContain("--type-code-inline-weight: var(--font-normal)");
+ expect(root).toContain("--type-code-inline-family: var(--font-mono)");
+ expect(root).toContain("--type-app-title-size: var(--font-size-400)");
+ expect(root).toContain("--type-app-title-line-height: 1.25");
+ expect(root).toContain("--type-app-title-weight: var(--font-semibold)");
+ expect(root).toContain("--type-section-title-size: var(--font-size-500)");
+ expect(root).toContain("--type-section-title-line-height: 1.2");
+ expect(root).toContain("--type-section-title-weight: var(--font-semibold)");
+ expect(root).toContain("--type-page-title-size: var(--font-size-600)");
+ expect(root).toContain("--type-page-title-line-height: 1.1");
+ expect(root).toContain("--type-page-title-weight: var(--font-semibold)");
+ expect(root).toContain("--type-display-size: var(--font-size-700)");
+ expect(root).toContain("--type-display-line-height: 1.05");
+ expect(root).toContain("--type-display-weight: var(--font-semibold)");
+ expect(root).toContain("--type-display-letter-spacing: -0.03em");
+ });
+
+ it("overrides the typography scale and dense body line-heights for mobile viewports", () => {
+ const mediaMatch = /@media\s*\(max-width:\s*899px\)\s*\{([\s\S]*?)\}\s*\}/m.exec(stylesheet);
+
+ expect(mediaMatch, "expected @media (max-width: 899px) block").not.toBeNull();
+
+ const body = mediaMatch![1];
+
+ expect(body).toContain("--font-size-100: 12px");
+ expect(body).toContain("--font-size-200: 13px");
+ expect(body).toContain("--font-size-300: 15px");
+ expect(body).toContain("--font-size-400: 17px");
+ expect(body).toContain("--font-size-500: 20px");
+ expect(body).toContain("--font-size-600: 28px");
+ expect(body).toContain("--font-size-700: 36px");
+ expect(body).toContain("--type-body-line-height: 1.55");
+ expect(body).toContain("--type-body-strong-line-height: 1.5");
+ });
+
it("keeps the light theme families visually separated through structure tokens", () => {
const mintLight = getRuleBlock('[data-theme="mint-light"]');
const graphiteLight = getRuleBlock('[data-theme="graphite-light"]');
diff --git a/packages/web/src/styles/tokens.css b/packages/web/src/styles/tokens.css
index 36e5c54a6..67e7c6e35 100644
--- a/packages/web/src/styles/tokens.css
+++ b/packages/web/src/styles/tokens.css
@@ -32,6 +32,14 @@
--font-mono: "JetBrains Mono", "Sarasa Mono SC", "PingFang SC", monospace;
/* Font Sizes */
+ --font-size-100: 11px;
+ --font-size-200: 12px;
+ --font-size-300: 14px;
+ --font-size-400: 16px;
+ --font-size-500: 18px;
+ --font-size-600: 24px;
+ --font-size-700: 32px;
+
--text-xs: 11px;
--text-sm: 12px;
--text-base: 13px;
@@ -40,6 +48,49 @@
--text-2xl: 18px;
--text-3xl: 20px;
+ --type-kicker-size: var(--font-size-100);
+ --type-kicker-line-height: 1.2;
+ --type-kicker-weight: var(--font-normal);
+ --type-kicker-letter-spacing: 0.08em;
+
+ --type-label-size: var(--font-size-200);
+ --type-label-line-height: 1.35;
+ --type-label-weight: var(--font-normal);
+
+ --type-meta-size: var(--font-size-200);
+ --type-meta-line-height: 1.45;
+ --type-meta-weight: var(--font-normal);
+
+ --type-body-size: var(--font-size-300);
+ --type-body-line-height: 1.5;
+ --type-body-weight: var(--font-normal);
+
+ --type-body-strong-size: var(--font-size-300);
+ --type-body-strong-line-height: 1.45;
+ --type-body-strong-weight: var(--font-normal);
+
+ --type-code-inline-size: var(--font-size-200);
+ --type-code-inline-line-height: 1.4;
+ --type-code-inline-weight: var(--font-normal);
+ --type-code-inline-family: var(--font-mono);
+
+ --type-app-title-size: var(--font-size-400);
+ --type-app-title-line-height: 1.25;
+ --type-app-title-weight: var(--font-semibold);
+
+ --type-section-title-size: var(--font-size-500);
+ --type-section-title-line-height: 1.2;
+ --type-section-title-weight: var(--font-semibold);
+
+ --type-page-title-size: var(--font-size-600);
+ --type-page-title-line-height: 1.1;
+ --type-page-title-weight: var(--font-semibold);
+
+ --type-display-size: var(--font-size-700);
+ --type-display-line-height: 1.05;
+ --type-display-weight: var(--font-semibold);
+ --type-display-letter-spacing: -0.03em;
+
/* Line Heights */
--leading-tight: 1.25;
--leading-normal: 1.5;
@@ -783,6 +834,15 @@
/* ========== Mobile / Touch Override (Phase 0) ========== */
@media (max-width: 899px) {
:root {
+ --font-size-100: 12px;
+ --font-size-200: 13px;
+ --font-size-300: 15px;
+ --font-size-400: 17px;
+ --font-size-500: 20px;
+ --font-size-600: 28px;
+ --font-size-700: 36px;
+ --type-body-line-height: 1.55;
+ --type-body-strong-line-height: 1.5;
--touch-target-min: 44px;
--touch-target-comfortable: 48px;
--touch-target-large: 56px;
diff --git a/packages/web/src/styles/typography.guard.test.ts b/packages/web/src/styles/typography.guard.test.ts
new file mode 100644
index 000000000..313405709
--- /dev/null
+++ b/packages/web/src/styles/typography.guard.test.ts
@@ -0,0 +1,100 @@
+// @vitest-environment node
+import { readFileSync } from "node:fs";
+import { describe, expect, it } from "vitest";
+
+const baseStyles = readFileSync(`${process.cwd()}/src/styles/base.css`, "utf8");
+const componentsStyles = readFileSync(`${process.cwd()}/src/styles/components.css`, "utf8");
+const sharedUiSources = [
+ "src/components/ui/button/index.module.css",
+ "src/components/ui/input/index.module.css",
+ "src/components/ui/textarea/index.module.css",
+ "src/components/ui/tabs/index.module.css",
+ "src/components/ui/tag/index.module.css",
+ "src/components/ui/badge/index.module.css",
+ "src/components/ui/pill/index.module.css",
+ "src/components/ui/tooltip/index.module.css",
+ "src/components/ui/notice/index.module.css",
+ "src/components/ui/modal/index.module.css",
+ "src/components/ui/empty-state/index.module.css",
+ "src/components/ui/kbd/index.module.css",
+ "src/components/ui/segmented-control/index.module.css",
+ "src/components/ui/toast/index.module.css",
+ "src/components/ui/confirm-dialog/index.module.css",
+].map((file) => [file, readFileSync(`${process.cwd()}/${file}`, "utf8")] as const);
+
+const forbiddenSharedPattern = /font-size:\s*(?:\d+px|clamp\(|var\(--text-)/;
+const fontSizePattern = /font-size:\s*(?:\d+px|clamp\(|var\(--text-)/;
+const iconOnlySelectors = [
+ /\.panel-toolbar-btn/,
+ /\.tree-chevron/,
+ /\.tree-icon/,
+ /\.agent-action-btn/,
+ /\.topbar-close/,
+ /\.topbar-add/,
+ /\.worktree-tree-icon/,
+ /\.fp-dir-icon/,
+ /\.config-empty-icon/,
+ /\.page-header__back/,
+];
+const exemptBaseSelectors = [/^html$/];
+const exemptComponentSelectors = [
+ /\.session-terminal/,
+ /\.agent-terminal/,
+ /\.bottom-terminal/,
+ /\.terminal-/,
+ /\.mobile-terminal-/,
+ /\.xterm/,
+ /\.code-editor/,
+ /\.monaco/,
+ /\.git-diff/,
+ /\.diff-/,
+ /\.review-/,
+ /\.image-preview-/,
+ /\.code-file-path/,
+ /\.code-lines/,
+];
+
+function getOffenderBlocks(
+ source: string,
+ selectorExemptions: RegExp[],
+ extraExemptions: RegExp[] = []
+) {
+ return Array.from(
+ source.matchAll(/([^{}]+)\{([^}]*font-size:\s*(?:\d+px|clamp\(|var\(--text-)[^}]*)\}/g)
+ )
+ .map((match) => ({
+ selector: match[1]
+ .replace(/\/\*[\s\S]*?\*\//g, "")
+ .replace(/\s+/g, " ")
+ .trim(),
+ block: `${match[1]}{${match[2]}}`,
+ }))
+ .filter(
+ ({ selector, block }) =>
+ !selectorExemptions.some((pattern) => pattern.test(selector)) &&
+ !extraExemptions.some((pattern) => pattern.test(block))
+ )
+ .map(({ block }) => block);
+}
+
+describe("typography guardrails", () => {
+ it("keeps base.css and shared UI modules off raw and legacy font sizes", () => {
+ expect(getOffenderBlocks(baseStyles, exemptBaseSelectors)).toEqual([]);
+
+ for (const [file, source] of sharedUiSources) {
+ expect(source, file).not.toMatch(forbiddenSharedPattern);
+ }
+ });
+
+ it("limits raw or legacy font-size values in components.css to exempt code surfaces", () => {
+ expect(componentsStyles).toMatch(fontSizePattern);
+
+ const offenderBlocks = getOffenderBlocks(
+ componentsStyles,
+ [...exemptComponentSelectors, ...iconOnlySelectors],
+ exemptComponentSelectors
+ );
+
+ expect(offenderBlocks).toEqual([]);
+ });
+});
diff --git a/packages/web/src/ui-preview/catalog.test.tsx b/packages/web/src/ui-preview/catalog.test.tsx
index 469a3736c..5699f7a49 100644
--- a/packages/web/src/ui-preview/catalog.test.tsx
+++ b/packages/web/src/ui-preview/catalog.test.tsx
@@ -122,6 +122,9 @@ describe("UI preview catalog", () => {
const ids = UI_PREVIEW_SCENES.map((scene) => scene.id);
expect(ids).toEqual(
expect.arrayContaining([
+ "readme-desktop-hero",
+ "readme-desktop-review",
+ "readme-mobile-progress",
"workspace-launch-modal",
"command-palette",
"branch-quick-pick",
@@ -308,6 +311,36 @@ describe("UI preview catalog", () => {
expect(screen.getByText(/\+\s+background: var\(--bg-elevated\);/)).toBeInTheDocument();
});
+ it("renders the README desktop hero scene with a live session and shell terminal", async () => {
+ renderScene("readme-desktop-hero");
+
+ expect(
+ await screen.findByText("Ship the header polish and verify README visuals")
+ ).toBeInTheDocument();
+ expect(screen.getByText("Supervisor")).toBeInTheDocument();
+ expect(screen.getByText("Preview Runner")).toBeInTheDocument();
+ expect(document.querySelector(".workspace-page--desktop .session-card")).toBeTruthy();
+ expect(document.querySelector(".workspace-page--desktop .bottom-terminal")).toBeTruthy();
+ });
+
+ it("renders the README desktop review scene with git and diff context", async () => {
+ renderScene("readme-desktop-review");
+
+ expect(await screen.findByText("README capture polish")).toBeInTheDocument();
+ expect(screen.getByText(/Refine the desktop topbar hierarchy/)).toBeInTheDocument();
+ expect(document.querySelector(".workspace-page--desktop .git-panel")).toBeTruthy();
+ expect(document.querySelector(".workspace-page--desktop .workspace-git-editor")).toBeTruthy();
+ });
+
+ it("renders the README mobile progress scene with session continuity and supervisor state", async () => {
+ renderScene("readme-mobile-progress", "mobile");
+
+ expect(await screen.findByText("Resume mobile progress review")).toBeInTheDocument();
+ expect(screen.getAllByText("Supervisor").length).toBeGreaterThan(0);
+ expect(document.querySelector(".mobile-shell .session-card")).toBeTruthy();
+ expect(document.querySelector(".mobile-shell .workspace-status-bar")).toBeTruthy();
+ });
+
it("renders the workspace terminal empty review scene", async () => {
renderScene("workspace-terminal-empty-review");
@@ -321,7 +354,7 @@ describe("UI preview catalog", () => {
renderScene("settings-density-review");
expect(await screen.findByRole("heading", { name: /settings|设置/i })).toBeInTheDocument();
- expect(document.querySelector(".settings-header__desktop")).toBeTruthy();
+ expect(document.querySelector(".settings-header .page-header")).toBeTruthy();
expect(document.querySelector(".settings-sidebar")).toBeTruthy();
});
diff --git a/packages/web/src/ui-preview/preview-store.ts b/packages/web/src/ui-preview/preview-store.ts
index 1ea0298a9..aca8e98ed 100644
--- a/packages/web/src/ui-preview/preview-store.ts
+++ b/packages/web/src/ui-preview/preview-store.ts
@@ -88,6 +88,7 @@ export interface UiPreviewCommands {
createdAt: number;
}>
>;
+ supervisorBySessionId?: Record
;
}
export interface UiPreviewSeed {
@@ -370,12 +371,20 @@ function createPreviewDispatcher(seed: UiPreviewSeed): DispatchCommand {
op === "file.delete" ||
op === "supervisor.create" ||
op === "supervisor.update" ||
- op === "supervisor.delete" ||
- op === "supervisor.get"
+ op === "supervisor.delete"
) {
return ok({} as unknown as T);
}
+ if (op === "supervisor.get") {
+ const sessionId = (args as { sessionId?: string })?.sessionId ?? "";
+ const supervisor =
+ commands.supervisorBySessionId?.[sessionId] ??
+ seed.supervisorBySessionId?.[sessionId] ??
+ null;
+ return ok({ supervisor } as unknown as T);
+ }
+
return err(`Missing preview handler for ${op}`);
};
}
diff --git a/packages/web/src/ui-preview/scene-metadata.test.ts b/packages/web/src/ui-preview/scene-metadata.test.ts
index 8e940051f..0ca815646 100644
--- a/packages/web/src/ui-preview/scene-metadata.test.ts
+++ b/packages/web/src/ui-preview/scene-metadata.test.ts
@@ -20,6 +20,9 @@ describe("ui preview scene metadata", () => {
it("registers the desktop review scene ids", () => {
expect(UI_PREVIEW_SCENE_METADATA.map((scene) => scene.id)).toEqual(
expect.arrayContaining([
+ "readme-desktop-hero",
+ "readme-desktop-review",
+ "readme-mobile-progress",
"workspace-topbar-review",
"workspace-sidebar-files-review",
"workspace-sidebar-git-review",
@@ -62,4 +65,18 @@ describe("ui preview scene metadata", () => {
expect(scene?.capture?.selector).toBe(".settings-mobile-root");
});
+
+ it("captures the README scenes from their full workspace shells", () => {
+ const heroScene = UI_PREVIEW_SCENE_METADATA.find((entry) => entry.id === "readme-desktop-hero");
+ const reviewScene = UI_PREVIEW_SCENE_METADATA.find(
+ (entry) => entry.id === "readme-desktop-review"
+ );
+ const mobileScene = UI_PREVIEW_SCENE_METADATA.find(
+ (entry) => entry.id === "readme-mobile-progress"
+ );
+
+ expect(heroScene?.capture?.selector).toBe(".workspace-page");
+ expect(reviewScene?.capture?.selector).toBe(".workspace-page");
+ expect(mobileScene?.capture?.selector).toBe("[data-testid='mobile-shell']");
+ });
});
diff --git a/packages/web/src/ui-preview/scene-metadata.ts b/packages/web/src/ui-preview/scene-metadata.ts
index 60297df44..e5f386b31 100644
--- a/packages/web/src/ui-preview/scene-metadata.ts
+++ b/packages/web/src/ui-preview/scene-metadata.ts
@@ -318,6 +318,42 @@ export const UI_PREVIEW_SCENE_METADATA: UiPreviewSceneMetadata[] = [
locales: ["zh", "en"],
capture: { selector: ".supervisor-dialog" },
},
+ {
+ id: "readme-desktop-hero",
+ title: "README / Desktop Hero",
+ category: "page",
+ source: "showcase",
+ description:
+ "README-focused desktop workspace scene with a live agent session, supervisor summary, and shell verification terminal.",
+ devices: ["desktop"],
+ themes: allThemeIds(),
+ locales: ["zh", "en"],
+ capture: { selector: ".workspace-page" },
+ },
+ {
+ id: "readme-desktop-review",
+ title: "README / Desktop Review",
+ category: "page",
+ source: "showcase",
+ description:
+ "README-focused desktop scene showing git review workflow with changed files, history, and an open diff.",
+ devices: ["desktop"],
+ themes: allThemeIds(),
+ locales: ["zh", "en"],
+ capture: { selector: ".workspace-page" },
+ },
+ {
+ id: "readme-mobile-progress",
+ title: "README / Mobile Progress",
+ category: "page",
+ source: "showcase",
+ description:
+ "README-focused mobile scene showing an active session with supervisor continuity and branch status.",
+ devices: ["mobile"],
+ themes: allThemeIds(),
+ locales: ["zh", "en"],
+ capture: { selector: "[data-testid='mobile-shell']" },
+ },
{
id: "workspace-topbar-review",
title: "Workspace / Topbar Review",
diff --git a/packages/web/src/ui-preview/scenes/desktop-review-scenes.tsx b/packages/web/src/ui-preview/scenes/desktop-review-scenes.tsx
index 880faac3c..a612d8901 100644
--- a/packages/web/src/ui-preview/scenes/desktop-review-scenes.tsx
+++ b/packages/web/src/ui-preview/scenes/desktop-review-scenes.tsx
@@ -130,7 +130,7 @@ const editorReviewLines = [
"return (",
' ',
'
',
- ' ',
+ "
(Object.entries(fileTreeByPath)),
+ },
+ gitStateByWorkspaceId: {
+ [workspace.id]: readmeDesktopGitStatus,
+ },
+ gitBranchListByWorkspaceId: {
+ [workspace.id]: {
+ current: "feature/readme-refresh",
+ branches: [
+ { name: "feature/readme-refresh", isCurrent: true, isRemote: false },
+ { name: "main", isCurrent: false, isRemote: false },
+ { name: "origin/main", isCurrent: false, isRemote: true },
+ ],
+ },
+ },
+ terminalMetaById: {
+ "term-agent-readme-hero": {
+ id: "term-agent-readme-hero",
+ workspaceId: workspace.id,
+ kind: "agent",
+ alive: true,
+ title: "Codex session",
+ },
+ "term-shell-readme-hero": {
+ id: "term-shell-readme-hero",
+ workspaceId: workspace.id,
+ kind: "shell",
+ alive: true,
+ title: "Workspace Shell",
+ },
+ },
+ terminalOutputById: {
+ "term-agent-readme-hero": [
+ new TextEncoder().encode(
+ [
+ '$ codex run --model gpt-5.5 --task "refresh readme screenshots"',
+ "Analyzing README usage and current preview scenes...",
+ "Plan: add README-specific preview scenes, capture desktop hero, capture mobile continuity shot.",
+ "",
+ "Editing packages/web/src/ui-preview/scenes/showcase-scenes.tsx",
+ "Preparing fresh assets in docs/help/assets/",
+ ].join("\n")
+ ),
+ ],
+ "term-shell-readme-hero": [
+ new TextEncoder().encode(
+ [
+ "$ pnpm --filter @coder-studio/web exec vitest run src/ui-preview/scene-metadata.test.ts src/ui-preview/catalog.test.tsx",
+ "✓ src/ui-preview/scene-metadata.test.ts (9)",
+ "✓ src/ui-preview/catalog.test.tsx (35)",
+ "",
+ '$ pnpm --dir e2e-ui exec playwright test --grep "README /"',
+ "capturing desktop hero and mobile progress scenes...",
+ ].join("\n")
+ ),
+ ],
+ },
+ supervisorBySessionId: {
+ "session-readme-hero": readmeSupervisor,
+ },
+ commands: {
+ workspaceList: [workspace],
+ sessionListByWorkspaceId: {
+ [workspace.id]: readmeDesktopSessions,
+ },
+ fileTreeByWorkspaceId: {
+ [workspace.id]: fileTreeByPath,
+ },
+ gitStatusByWorkspaceId: {
+ [workspace.id]: readmeDesktopGitStatus,
+ },
+ gitBranchesByWorkspaceId: {
+ [workspace.id]: {
+ current: "feature/readme-refresh",
+ branches: [
+ { name: "feature/readme-refresh", isCurrent: true, isRemote: false },
+ { name: "main", isCurrent: false, isRemote: false },
+ { name: "origin/main", isCurrent: false, isRemote: true },
+ ],
+ },
+ },
+ terminalListByWorkspaceId: {
+ [workspace.id]: [
+ {
+ id: "term-shell-readme-hero",
+ workspaceId: workspace.id,
+ kind: "shell",
+ title: "Workspace Shell",
+ cwd: workspace.path,
+ argv: ["zsh"],
+ cols: 120,
+ rows: 28,
+ alive: true,
+ createdAt: 2,
+ },
+ {
+ id: "term-shell-readme-verify",
+ workspaceId: workspace.id,
+ kind: "shell",
+ title: "Preview Runner",
+ cwd: workspace.path,
+ argv: ["zsh"],
+ cols: 120,
+ rows: 28,
+ alive: true,
+ createdAt: 3,
+ },
+ ],
+ },
+ supervisorBySessionId: {
+ "session-readme-hero": readmeSupervisor,
+ },
+ },
+ };
+}
+
+function buildReadmeDesktopReviewSeed(context: {
+ theme: string;
+ locale: "zh" | "en";
+ device: "desktop" | "mobile";
+}) {
+ const fileTreeByPath = createReadmeWorkspaceFileTree();
+
+ return {
+ ...buildReadmeDesktopHeroSeed(context),
+ fileTreeByWorkspaceId: {
+ [workspace.id]: new Map(Object.entries(fileTreeByPath)),
+ },
+ gitDiffPreviewByWorkspaceId: {
+ [workspace.id]: {
+ path: "packages/web/src/features/topbar/index.tsx",
+ title: "README capture polish",
+ diff: [
+ "diff --git a/packages/web/src/features/topbar/index.tsx b/packages/web/src/features/topbar/index.tsx",
+ "@@ Refine the desktop topbar hierarchy",
+ '- Quick Actions',
+ '+ Quick Actions',
+ '+ Review README capture targets',
+ "",
+ "@@ screenshot staging",
+ "+ setWorkspaceLaunchOpen(false)} />",
+ ].join("\n"),
+ source: "file" as const,
+ },
+ },
+ commands: {
+ ...buildReadmeDesktopHeroSeed(context).commands,
+ fileTreeByWorkspaceId: {
+ [workspace.id]: fileTreeByPath,
+ },
+ gitStatusByWorkspaceId: {
+ [workspace.id]: readmeDesktopGitStatus,
+ },
+ gitLogByWorkspaceId: {
+ [workspace.id]: { entries: [...readmeDesktopHistory] },
+ },
+ gitDiffByWorkspaceId: {
+ [workspace.id]: {
+ diff: [
+ "diff --git a/packages/web/src/features/topbar/index.tsx b/packages/web/src/features/topbar/index.tsx",
+ "@@ Refine the desktop topbar hierarchy",
+ '+ Review README capture targets',
+ '+ Keep the workspace hero readable at README width',
+ ].join("\n"),
+ },
+ },
+ gitShowByWorkspaceId: {
+ [workspace.id]: {
+ diff: "@@ latest commit\n+ refresh README desktop review capture",
+ },
+ },
+ supervisorBySessionId: {
+ "session-readme-hero": readmeSupervisor,
+ },
+ },
+ };
+}
+
+function ReadmeDesktopReviewWorkspace() {
+ return (
+
+ );
+}
+
+function ReadmeMobileProgressWorkspace() {
+ const readmeMobileGitState: GitStatus = {
+ branch: "feature/readme-refresh",
+ ahead: 2,
+ behind: 0,
+ staged: [{ path: "README.md", status: "modified" }],
+ modified: [{ path: "docs/help/assets/screenshot-mobile.png", status: "modified" }],
+ untracked: [],
+ deleted: [],
+ };
+
+ return (
+
+
{}}
+ onToggleDrawer={() => {}}
+ />
+
+
+
+
+
+
+
+
+ );
+}
+
+function buildReadmeMobileProgressSeed(context: {
+ theme: string;
+ locale: "zh" | "en";
+ device: "desktop" | "mobile";
+}) {
+ const mobileWorkspace = {
+ ...workspace,
+ uiState: {
+ ...workspace.uiState,
+ activeSessionId: "session-readme-mobile",
+ paneLayout: {
+ id: "root",
+ type: "leaf" as const,
+ sessionId: "session-readme-mobile",
+ },
+ },
+ };
+
+ return {
+ ...context,
+ workspaces: [mobileWorkspace],
+ activeWorkspaceId: mobileWorkspace.id,
+ sessions: readmeMobileSessions,
+ paneLayoutByWorkspaceId: {
+ [mobileWorkspace.id]: {
+ id: "root",
+ type: "leaf",
+ sessionId: "session-readme-mobile",
+ },
+ },
+ gitStateByWorkspaceId: {
+ [mobileWorkspace.id]: {
+ branch: "feature/readme-refresh",
+ ahead: 2,
+ behind: 0,
+ staged: [{ path: "README.md", status: "modified" }],
+ modified: [{ path: "docs/help/assets/screenshot-mobile.png", status: "modified" }],
+ untracked: [],
+ deleted: [],
+ },
+ },
+ terminalMetaById: {
+ "term-agent-readme-mobile": {
+ id: "term-agent-readme-mobile",
+ workspaceId: mobileWorkspace.id,
+ kind: "agent",
+ alive: true,
+ title: "Claude progress review",
+ },
+ },
+ terminalOutputById: {
+ "term-agent-readme-mobile": [
+ new TextEncoder().encode(
+ [
+ "$ claude review --scene readme-mobile-progress",
+ "Checking continuity between desktop hero and mobile status view...",
+ "Recommendation: keep supervisor progress visible above the dock.",
+ "Status: desktop captures approved, mobile continuity shot queued for export.",
+ ].join("\n")
+ ),
+ ],
+ },
+ supervisorBySessionId: {
+ "session-readme-mobile": readmeMobileSupervisor,
+ },
+ commands: {
+ workspaceList: [mobileWorkspace],
+ sessionListByWorkspaceId: {
+ [mobileWorkspace.id]: readmeMobileSessions,
+ },
+ gitStatusByWorkspaceId: {
+ [mobileWorkspace.id]: {
+ branch: "feature/readme-refresh",
+ ahead: 2,
+ behind: 0,
+ staged: [{ path: "README.md", status: "modified" }],
+ modified: [{ path: "docs/help/assets/screenshot-mobile.png", status: "modified" }],
+ untracked: [],
+ deleted: [],
+ },
+ },
+ gitBranchesByWorkspaceId: {
+ [mobileWorkspace.id]: {
+ current: "feature/readme-refresh",
+ branches: [
+ { name: "feature/readme-refresh", isCurrent: true, isRemote: false },
+ { name: "main", isCurrent: false, isRemote: false },
+ ],
+ },
+ },
+ supervisorBySessionId: {
+ "session-readme-mobile": readmeMobileSupervisor,
+ },
+ },
+ };
+}
+
function scene(
id: string,
config: Pick
@@ -655,6 +1270,21 @@ export function createShowcaseScenes(): UiPreviewSceneDefinition[] {
}),
render: () => ,
}),
+ scene("readme-desktop-hero", {
+ router: () => ({ initialEntries: ["/workspace"], path: "/workspace" }),
+ seed: (context) => buildReadmeDesktopHeroSeed(context),
+ render: () => ,
+ }),
+ scene("readme-desktop-review", {
+ router: () => ({ initialEntries: ["/workspace"], path: "/workspace" }),
+ seed: (context) => buildReadmeDesktopReviewSeed(context),
+ render: () => ,
+ }),
+ scene("readme-mobile-progress", {
+ router: () => ({ initialEntries: ["/workspace"], path: "/workspace" }),
+ seed: (context) => buildReadmeMobileProgressSeed({ ...context, device: "mobile" }),
+ render: () => ,
+ }),
scene("worktree-manager", {
router: () => ({ initialEntries: ["/workspace"], path: "/workspace" }),
seed: (context) => ({