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)}>