From 35bb433c8bfaef758ef40e2577467110f5a500da Mon Sep 17 00:00:00 2001
From: forthfate
Date: Sat, 19 Sep 2026 12:10:08 +0900
Subject: [PATCH 1/2] style: align settings section layouts
---
frontend/src/features/assets/page.tsx | 36 +++++++------
frontend/src/features/settings/orbit-logs.tsx | 18 ++++---
frontend/src/features/settings/page.tsx | 52 +++++++++++--------
frontend/src/theme-overrides.css | 9 ++--
specs/product/settings-section-patterns.md | 24 +++++++++
5 files changed, 89 insertions(+), 50 deletions(-)
create mode 100644 specs/product/settings-section-patterns.md
diff --git a/frontend/src/features/assets/page.tsx b/frontend/src/features/assets/page.tsx
index 912fd31..91c7b3d 100644
--- a/frontend/src/features/assets/page.tsx
+++ b/frontend/src/features/assets/page.tsx
@@ -802,24 +802,26 @@ export function ProfileCatalog({
{copy.profiles.create}
-
- {profiles.map((profile) => (
- {
- setSettings(profile);
- setOpen(true);
- }}
- onDelete={() => onDelete(profile.profile_name)}
- deleteLabel={`${copy.profiles.delete} ${profile.profile_name}`}
- />
+
+
+ {profiles.map((profile) => (
+ {
+ setSettings(profile);
+ setOpen(true);
+ }}
+ onDelete={() => onDelete(profile.profile_name)}
+ deleteLabel={`${copy.profiles.delete} ${profile.profile_name}`}
+ />
))}
-
+
+
{t.description}
-
- {logs.length ? logs.map((log, index) => (
-
-
- {log.name}
- {log.message || log.status}
-
- )) :
{t.empty}
}
+
+
+ {logs.length ? logs.map((log, index) => (
+
+
+ {log.name}
+ {log.message || log.status}
+
+ )) :
{t.empty}
}
+
);
diff --git a/frontend/src/features/settings/page.tsx b/frontend/src/features/settings/page.tsx
index 41de328..7b647b5 100644
--- a/frontend/src/features/settings/page.tsx
+++ b/frontend/src/features/settings/page.tsx
@@ -294,22 +294,32 @@ export function SettingsPage({
-
+
+
+
- } />
- {settingsCopy.storage.description}
+
+
+
} />
+
{settingsCopy.storage.description}
+
+ {!dataEditing &&
}
+
-
- {settingsCopy.storage.location}
- {settingsCopy.storage.locationHint}
-
+
+ {settingsCopy.storage.location}
+ {settingsCopy.storage.locationHint}
+
{dataEditing ? (
setDataPathDraft(event.target.value)} />
@@ -326,10 +336,6 @@ export function SettingsPage({
) : (
{dataPath}
-
)}
@@ -410,11 +416,13 @@ export function SettingsPage({
{l.edit}
-
-
- {l.warning}
-
- {prompt || l.empty}
+
+
+
+ {l.warning}
+
+
{prompt || l.empty}
+
setMcpOpen(false)}>
diff --git a/frontend/src/theme-overrides.css b/frontend/src/theme-overrides.css
index ba8013f..f590a2b 100644
--- a/frontend/src/theme-overrides.css
+++ b/frontend/src/theme-overrides.css
@@ -96,8 +96,10 @@ footer { display:flex; align-items:center; justify-content:space-between; margin
.run-detail-trigger { border:0; padding:0; color:inherit; background:transparent; text-align:left; cursor:pointer; }.run-detail { position:fixed; z-index:15; top:0; right:0; width:min(560px,100vw); height:100vh; overflow:auto; padding:24px; border-left:1px solid var(--line); background:var(--surface); box-shadow:-16px 0 48px rgb(0 0 0 / .35); }.run-detail header { margin-bottom:18px; }.run-tabs { display:flex; gap:6px; border-bottom:1px solid var(--line); }.run-tabs button { border:0; border-bottom:2px solid transparent; padding:10px 12px; color:var(--muted); background:transparent; }.run-tabs button.active { border-color:var(--accent); color:var(--text); }.console-output section { padding:14px 0; border-bottom:1px solid var(--line); }.console-output small { display:block; color:var(--muted); margin:4px 0; }.line-numbered-output { max-width:100%; overflow:auto; padding:8px 0; border:1px solid var(--line); border-radius:8px; background:var(--bg); color:var(--text); font:11px/1.55 'DM Mono',monospace; }.line-numbered-output > div { display:grid; grid-template-columns:46px minmax(0,1fr); min-width:max-content; }.line-numbered-output span { padding:0 10px; border-right:1px solid var(--line); color:var(--muted); text-align:right; user-select:none; }.line-numbered-output code { min-width:0; padding:0 12px; color:inherit; white-space:pre; }.supervisor-translation-action { display:flex; justify-content:flex-end; margin-bottom:10px; }.supervisor-output { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; padding:0; }.supervisor-output section { display:grid; grid-template-rows:auto minmax(0,1fr); gap:8px; height:clamp(260px,calc(100vh - 390px),460px); min-height:0; padding:12px; border:1px solid var(--line); border-radius:8px; background:var(--surface-raised); }.supervisor-output .line-numbered-output { min-height:0; }.supervisor-output__head { display:flex; align-items:center; justify-content:space-between; gap:12px; }.supervisor-output p { margin:0; overflow:auto; color:var(--muted); } @media(max-width:760px){.supervisor-output { grid-template-columns:1fr; }.supervisor-output section { height:300px; }}
.iteration-navigator { display:flex; align-items:center; justify-content:flex-end; gap:6px; min-height:48px; border-bottom:1px solid var(--line); }.iteration-navigator select { width:auto; min-width:76px; height:30px; padding:0 28px 0 10px; font:11px 'DM Mono',monospace; }.iteration-navigator .icon-button { display:grid; width:30px; height:30px; place-items:center; padding:0; }.iteration-navigator .icon-button:disabled { cursor:not-allowed; opacity:.4; }
.asset-tabs { display:flex; gap:4px; margin:0 0 16px; overflow-x:auto; border-bottom:1px solid var(--line); }.asset-tabs button { flex:0 0 auto; border:0; border-bottom:2px solid transparent; padding:10px 14px; color:var(--muted); background:transparent; font:600 12px/1.2 'DM Mono',monospace; }.asset-tabs button:hover,.asset-tabs button.selected { border-bottom-color:var(--accent); color:var(--text); }.asset-tabs button:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; }
-.app-settings { display:grid; grid-template-columns:1fr; gap:0; }.app-settings + .app-settings { margin-top:16px; }.setting-row { display:flex !important; align-items:center; justify-content:space-between; gap:24px; padding:18px 0; border-top:1px solid var(--line); }.setting-row span { display:grid; gap:5px; }.setting-row strong { color:var(--text); }.setting-row select { min-width:220px; }.setting-actions { display:flex; align-items:center; gap:8px; }.setting-actions .approve { white-space:nowrap; }
-.setting-row--stacked { align-items:stretch; flex-direction:column; }.setting-row--stacked > span,.setting-row--stacked > .cm-editor,.setting-row--stacked > .setting-actions { width:100%; }.setting-row--stacked .setting-actions { justify-content:flex-end; }
+.app-settings { display:grid; grid-template-columns:1fr; gap:0; }.app-settings + .app-settings { margin-top:16px; }.setting-row { display:flex !important; align-items:center; justify-content:space-between; gap:24px; padding:18px 0; }.setting-row + .setting-row { border-top:1px solid var(--line); }.setting-row span { display:grid; gap:5px; }.setting-row strong { color:var(--text); }.setting-row select { min-width:220px; }.setting-row > .approve { display:inline-flex; flex:none; align-items:center; gap:5px; white-space:nowrap; }.setting-actions { display:flex; align-items:center; gap:8px; }.setting-actions .approve { white-space:nowrap; }
+.settings-summary-value { margin:0; color:var(--muted); font-size:12px; line-height:1.5; }
+.settings-summary { display:grid; gap:3px; min-width:0; }.settings-summary-label { color:var(--muted); font:500 10px 'DM Mono',monospace; letter-spacing:.02em; }.settings-summary-detail { color:var(--muted); font-size:11px; line-height:1.45; }
+.settings-section-content { box-sizing:border-box; min-width:0; }
.mcp-server-table .tr { min-width:0; }.mcp-server-table .tr.th { border-top:0; }.mcp-server-table .tr > span:last-child { display:flex; align-items:center; }
@media (max-width:900px) { .build-form,.profile-form { grid-template-columns:1fr; } }
.recent-evaluations { display:grid; grid-template-columns:repeat(auto-fill,280px); gap:16px; }.evaluation-card { display:grid; gap:10px; min-height:150px; padding:20px; border:1px solid var(--line); border-radius:12px; color:var(--text); background:var(--surface); text-align:left; transition:transform .16s ease,border-color .16s ease,background .16s ease; }.evaluation-card:hover { transform:translateY(-2px); border-color:var(--accent); background:var(--surface-raised); }.evaluation-card small { color:var(--accent); font:500 10px 'DM Mono',monospace; }.evaluation-card strong { font-size:14px; }.evaluation-card__labels { display:flex; flex-wrap:wrap; gap:6px; align-items:center; }.evaluation-card__labels .badge { font-size:10px; }
@@ -157,8 +159,9 @@ main > aside,main > .content { transition:width .2s ease,margin-left .2s ease,pa
.skeleton { display:block; border-radius:5px; background:linear-gradient(90deg,var(--surface-raised) 25%,color-mix(in srgb,var(--muted) 18%,var(--surface-raised)) 50%,var(--surface-raised) 75%); background-size:200% 100%; animation:skeleton-shimmer 1.4s ease-in-out infinite; }.catalog-skeleton { display:grid; grid-template-columns:minmax(0,1fr) 180px; gap:7px 14px; padding:13px 4px; border-top:1px solid var(--line); }.catalog-skeleton__title { height:12px; }.catalog-skeleton__detail { height:10px; align-self:center; } @keyframes skeleton-shimmer { to { background-position:-200% 0; } } @media(prefers-reduced-motion:reduce) { .skeleton { animation:none; } }
.section-skeleton { display:grid; gap:12px; }.section-skeleton__title { width:38%; height:18px; }.section-skeleton__hint { width:62%; height:11px; }.section-skeleton__rows { display:grid; gap:8px; }.section-skeleton__row { height:42px; }.section-skeleton__metrics { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; }.section-skeleton__metric { height:104px; }.section-skeleton__chart { height:180px; border-radius:8px; }
.dashboard-hero--skeleton { display:grid; gap:12px; }.dashboard-hero-skeleton__eyebrow { width:120px; height:11px; }.dashboard-hero-skeleton__title { width:min(540px,80%); height:42px; }.dashboard-hero-skeleton__description { width:min(680px,92%); height:16px; }.dashboard-hero-skeleton__button { width:132px; height:34px; margin-top:4px; }.dashboard-quick-starts--skeleton { display:grid; gap:12px; }.dashboard-quick-start-skeleton { min-height:104px; }.recent-evaluations--skeleton { grid-template-columns:repeat(3,minmax(0,1fr)); }.evaluation-card-skeleton { min-height:116px; }.analytics-chart--skeleton { display:grid; gap:14px; }.analytics-chart-skeleton__title { width:42%; height:14px; }.analytics-chart-skeleton__body { height:250px; }.feedback-trends-skeleton__select { width:72px; height:30px; } @media(max-width:760px){.recent-evaluations--skeleton { grid-template-columns:1fr; }}
-.app-data-settings .panel-head { grid-column:1/-1; }.app-data-settings .setting-row { grid-column:1/-1; flex-direction:column; align-items:stretch; gap:12px; }.app-data-path-actions { display:flex; align-items:center; gap:8px; min-width:0; width:100%; max-width:640px; }.app-data-path-actions input,.app-data-path { min-width:0; flex:1 1 220px; }.app-data-path { margin:0; overflow-wrap:anywhere; }.app-data-path-actions .approve,.app-data-edit-actions,.app-data-edit-actions .approve { display:inline-flex; align-items:center; gap:5px; }.app-data-edit-actions { gap:8px; }.app-data-size { display:flex; grid-column:1/-1; align-items:center; gap:8px; padding:11px 12px; border:1px solid var(--line); border-radius:8px; color:var(--muted); background:var(--bg); font-size:11px; }.app-data-size svg { color:var(--accent); }.app-data-size strong { margin-left:auto; color:var(--text); font:600 12px 'DM Mono',monospace; }
+.app-data-settings .panel-head { grid-column:1/-1; }.app-data-settings .setting-row { grid-column:1/-1; }.app-data-path-actions { display:flex; align-items:center; gap:8px; min-width:0; width:min(640px,100%); }.app-data-path-actions input,.app-data-path { min-width:0; flex:1 1 220px; }.app-data-path { margin:0; overflow-wrap:anywhere; }.app-data-path-actions .approve,.app-data-edit-actions,.app-data-edit-actions .approve { display:inline-flex; align-items:center; gap:5px; }.app-data-edit-actions { display:flex; gap:8px; }.app-data-size { display:flex; grid-column:1/-1; align-items:center; gap:8px; padding:11px 12px; border:1px solid var(--line); border-radius:8px; color:var(--muted); background:var(--bg); font-size:11px; }.app-data-size svg { color:var(--accent); }.app-data-size strong { margin-left:auto; color:var(--text); font:600 12px 'DM Mono',monospace; } @media(max-width:760px){.app-data-settings .setting-row { align-items:stretch; flex-direction:column; gap:12px; }.app-data-path-actions { width:100%; }}
.app-data-settings .setting-actions .approve { display:inline-flex; align-items:center; gap:5px; }
+.app-data-settings .setting-row { align-items:stretch; flex-direction:column; gap:12px; }.app-data-path-actions { width:100%; }
.retry-confirmation .modal-actions { flex-wrap:wrap; }.retry-confirmation .approve { background:var(--accent); color:#182318; }
.build-schedule-toggle,.build-schedule-fields label { display:flex; align-items:center; gap:7px; color:var(--text); font-size:11px; }.build-schedule-fields { display:grid; grid-template-columns:1fr 1fr; gap:9px; margin-top:10px; }.build-schedule-fields > label { display:grid; gap:5px; color:var(--muted); }.build-schedule-days { grid-column:1/-1; display:flex; flex-wrap:wrap; gap:8px; }.build-schedule-days label { padding:5px 7px; border:1px solid var(--line); border-radius:6px; }.build-schedule-fields input[type="time"] { width:100%; }
.runner-template-card__select { display:grid; gap:9px; width:100%; border:0; padding:0; color:inherit; text-align:left; background:transparent; cursor:pointer; }
diff --git a/specs/product/settings-section-patterns.md b/specs/product/settings-section-patterns.md
new file mode 100644
index 0000000..d843aa6
--- /dev/null
+++ b/specs/product/settings-section-patterns.md
@@ -0,0 +1,24 @@
+# Settings section patterns
+
+Use these rules for settings and connection-management UI.
+
+## Information hierarchy
+
+- Treat an independently managed resource (for example, MCP connections) as a dedicated section, not as an input inside an unrelated settings section.
+- Order sections from general application preferences, to global connections, to storage and operational configuration.
+- Every section uses `PanelHeader` and `SectionInfo`, with the same description shown below the header using `section-description`.
+
+## Summary before editing
+
+- The default view shows a concise, truthful summary: configured status, item count, and useful identifiers.
+- Use the shared summary-value typography for read-only configuration values; do not style them as headings, metrics, or prompt/source previews.
+- Do not render large structured configuration (JSON, YAML, source) inline by default.
+- Open structured editors from an explicit `Edit` action in a modal or dedicated editing state.
+- Do not claim a connection is active unless runtime health is available; use “configured” or “registered” for saved configuration alone.
+
+## Controls and copy
+
+- Reuse shared action labels: use the common `Save` label for persistence, not resource-specific variants such as “Save MCP configuration”.
+- Buttons containing an icon and label must keep the label on one line and use the existing `setting-actions`/button styling.
+- Keep section typography, row borders, spacing, and action alignment consistent with the other settings sections.
+- Add all visible copy and accessibility labels to every supported locale.
From e1d415e27054e69477c774048451f7af301fafb1 Mon Sep 17 00:00:00 2001
From: forthfate
Date: Sat, 19 Sep 2026 12:17:33 +0900
Subject: [PATCH 2/2] fix: keep asset tabs visible while loading
---
frontend/src/features/assets/page.tsx | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/frontend/src/features/assets/page.tsx b/frontend/src/features/assets/page.tsx
index 91c7b3d..a8debf6 100644
--- a/frontend/src/features/assets/page.tsx
+++ b/frontend/src/features/assets/page.tsx
@@ -1927,11 +1927,16 @@ function EnvironmentCatalog({
);
}
-function PersonaCatalog({ builds, onRefresh, locale }: { builds: Build[]; onRefresh: () => Promise; locale: Locale }) {
+function PersonaCatalog({ builds, onRefresh, locale, loading }: { builds: Build[]; onRefresh: () => Promise; locale: Locale; loading: boolean }) {
const t = text[locale];
- const [items, setItems] = useState([]), [draft, setDraft] = useState(null), [error, setError] = useState("");
- const load = () => api("/api/personas").then(setItems);
- useEffect(() => { void load(); }, []);
+ const [items, setItems] = useState([]), [draft, setDraft] = useState(null), [error, setError] = useState(""), [itemsLoading, setItemsLoading] = useState(true);
+ const load = () => {
+ setItemsLoading(true);
+ return api("/api/personas").then(setItems).finally(() => setItemsLoading(false));
+ };
+ useEffect(() => {
+ void api("/api/personas").then(setItems).finally(() => setItemsLoading(false));
+ }, []);
const save = () => {
if (!draft) return;
const exists = items.some((item) => item.id === draft.id);
@@ -1939,7 +1944,7 @@ function PersonaCatalog({ builds, onRefresh, locale }: { builds: Build[]; onRefr
.then(() => { setDraft(null); setError(""); return Promise.all([load(), onRefresh()]); })
.catch((value) => setError(value.message));
};
- return } />
{t.personaDescription}
{items.map((item) => build.persona_ids?.includes(item.id)).length} locale={locale} onClick={() => setDraft(item)} onDelete={() => api(`/api/personas/${item.id}`, "DELETE").then(() => Promise.all([load(), onRefresh()]).then(() => undefined)).catch((value) => setError(value.message))} />)}{draft && setDraft(null)}>};
+ return } />
{t.personaDescription}
{items.map((item) => build.persona_ids?.includes(item.id)).length} locale={locale} onClick={() => setDraft(item)} onDelete={() => api(`/api/personas/${item.id}`, "DELETE").then(() => Promise.all([load(), onRefresh()]).then(() => undefined)).catch((value) => setError(value.message))} />)}{draft && setDraft(null)}>};
}
export function AssetsPage({
@@ -2010,14 +2015,6 @@ export function AssetsPage({
const index = tabs.findIndex((tab) => tab.id === activeTab);
selectTab(tabs[(index + offset + tabs.length) % tabs.length].id);
};
- if (loading) return <>
-
-
-
-
-
-
- >;
return (
<>
@@ -2058,7 +2055,7 @@ export function AssetsPage({
>}
{activeTab === "test-design" && <>
-
+
>}
{activeTab === "run-setup" && <>