fix(studio): the configure-workspace dialog follows the board

360px panel, a small two-segment switch with the creation note under it, a source-branch label, 32px search and branch rows in the mono face with a pill for the current or occupied branch, and the one-line note at the foot. The per-row chevrons, dividers and the switch's icons and ticks are gone; the folder name and path stay as quiet lines because the board does not draw the current-folder page.
This commit is contained in:
yhh committed 2026-11-05 17:44:52 -08:00
1 parent b7da613abe
commit b3b5ef409f
2 files changed
+41 -39

No files matched your search

+34 -33
View File
@@ -1,40 +1,41 @@
.worktree-trigger .lb { max-width: 230px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
:root .compose .mode.worktree-trigger[data-on] { color: var(--muted); background: var(--overlay) }
:root .compose .mode.worktree-trigger[data-on] .lb, :root .compose .mode.worktree-trigger[data-on] .studio-icon { color: var(--muted) }
.workspace-palette { position: fixed; z-index: 1000; width: min(400px, calc(100vw / var(--zoom, 1) - 24px)); max-height: calc(100vh / var(--zoom, 1) - 24px); overflow: auto; padding: 16px; border: 1px solid var(--hair); border-radius: var(--r-lg); background: var(--float); color: var(--text); box-shadow: var(--shadow-float); font: var(--w-reg) var(--fs-ui)/1.5 var(--ui) }
.workspace-palette .studio-icon { width: 18px; height: 18px; flex-shrink: 0 }
.workspace-palette-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-bottom: 12px; margin-bottom: 16px; border-bottom: 1px solid var(--hair); font-size: var(--fs-ui); font-weight: var(--w-med) }
.workspace-palette { position: fixed; z-index: 1000; box-sizing: border-box; width: min(360px, calc(100vw / var(--zoom, 1) - 24px)); max-height: calc(100vh / var(--zoom, 1) - 24px); overflow: auto; border: 1px solid var(--border); border-radius: var(--r-md); background: var(--float); color: var(--text); box-shadow: var(--shadow-float); font: var(--w-reg) var(--fs-ui)/1.5 var(--ui) }
.workspace-palette .studio-icon { width: 14px; height: 14px; flex-shrink: 0 }
.workspace-palette-heading { display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; font: var(--w-semi) var(--fs-ui)/1.3 var(--ui) }
.workspace-palette-heading > span { flex: 1 }
.workspace-palette-close { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--r-sm); color: var(--muted); background: none; cursor: pointer }
.workspace-palette-close:hover { background: var(--float-hi); color: var(--text) }
.workspace-palette-identity { display: flex; align-items: center; gap: 10px; min-width: 0; margin-bottom: 16px }
.workspace-palette-identity > .studio-icon { width: 23px; height: 23px; color: var(--brand) }
.workspace-palette-identity > strong { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: var(--w-med) }
.workspace-palette-identity > span { flex-shrink: 0; max-width: 46%; padding: 3px 7px; border-radius: var(--r-sm); color: var(--muted); background: var(--overlay); font-size: var(--fs-micro) }
.worktree-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 3px; margin: 0 0 14px; background: var(--raised); border: 1px solid var(--hair); border-radius: var(--r-md) }
.worktree-modes button { display: flex; align-items: center; justify-content: center; gap: 7px; border: 1px solid transparent; border-radius: var(--r-sm); min-height: 36px; padding: 6px; color: var(--muted); background: none; font: inherit; font-size: var(--fs-meta); cursor: pointer }
.worktree-modes button[aria-pressed="true"] { background: var(--float); border-color: var(--hair); color: var(--text) }
.workspace-palette-base { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 12px; margin-bottom: 12px; font-size: var(--fs-meta) }
.workspace-palette-base > span, .workspace-palette-base > small { color: var(--muted) }
.workspace-palette-base > strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; font-weight: var(--w-med) }
.workspace-palette-base > small { grid-column: 1 / -1; font-size: var(--fs-micro) }
.workspace-palette-search { display: flex; align-items: center; gap: 8px; min-height: 40px; padding: 8px 11px; margin-bottom: 10px; border: 1px solid var(--hair); border-radius: var(--r-md); color: var(--muted); background: var(--raised) }
.workspace-palette-close:hover { background: var(--overlay); color: var(--text) }
.workspace-palette-close .studio-icon { width: 16px; height: 16px }
.workspace-palette-identity { display: flex; align-items: center; gap: 8px; min-width: 0; padding: 0 14px 10px; color: var(--muted) }
.workspace-palette-identity > strong { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--text); font: var(--w-med) var(--fs-meta)/1.3 var(--ui) }
.workspace-palette-identity > span { flex-shrink: 0; max-width: 46%; padding: 2px 7px; border-radius: var(--r-pill); color: var(--muted); background: var(--overlay); font: var(--w-reg) var(--fs-micro)/1.4 var(--ui) }
.worktree-modes { display: flex; gap: 2px; width: max-content; padding: 2px; margin: 0 14px 10px; background: var(--overlay); border-radius: var(--r-sm) }
.worktree-modes button { display: inline-grid; place-items: center; height: 24px; padding: 0 10px; border: 0; border-radius: var(--r-xs); color: var(--muted); background: none; font: var(--w-reg) var(--fs-meta)/1 var(--ui); white-space: nowrap; cursor: pointer }
.worktree-modes button[aria-pressed="true"] { background: var(--raised); color: var(--text); font-weight: var(--w-med); box-shadow: 0 1px 2px rgb(0 0 0 / .08) }
.worktree-modes button:disabled { opacity: .5; cursor: not-allowed }
.workspace-palette-desc { margin: 0; padding: 0 14px 10px; color: var(--muted); font: var(--w-reg) var(--fs-meta)/1.6 var(--ui) }
.workspace-palette-label { padding: 0 14px 6px; color: var(--faint); font: var(--w-med) var(--fs-micro)/1.4 var(--ui) }
.workspace-palette-search { display: flex; align-items: center; box-sizing: border-box; height: 32px; gap: 6px; padding: 0 10px; margin: 0 14px 6px; border: 1px solid var(--border); border-radius: var(--r-sm); color: var(--faint); background: var(--surface) }
.workspace-palette-search:focus-within { border-color: var(--focus) }
.workspace-palette-search input { flex: 1; min-width: 0; width: 100%; border: 0; padding: 0; border-radius: 0; background: transparent; color: var(--text); font: inherit; outline: none }
.worktree-sources { max-height: 250px; overflow: auto; overscroll-behavior: contain }
.workspace-palette-group { display: block; padding: 8px; color: var(--muted); font-size: var(--fs-micro) }
.worktree-sources button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; border: 0; border-bottom: 1px solid var(--hair); border-radius: 0; background: none; color: var(--text); padding: 10px 8px; text-align: left; font: inherit; cursor: pointer }
.worktree-sources > div > button:last-child { border-bottom-color: transparent }
.worktree-sources button .workspace-palette-branch { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--fs-ui) }
.worktree-sources button small { color: var(--muted); font-size: var(--fs-micro); white-space: nowrap }
.worktree-sources button > .studio-icon:last-child { width: 14px; height: 14px; color: var(--muted) }
.worktree-sources button:not(:disabled):hover { background: var(--float-hi); border-radius: var(--r-sm) }
.worktree-sources button[aria-selected="true"] { color: var(--brand); background: var(--brand-wash); border-radius: var(--r-sm); border-bottom-color: transparent }
.worktree-sources button[aria-selected="true"] > .studio-icon:last-child { color: var(--brand) }
.worktree-sources button[aria-selected="true"] small { color: var(--brand); background: var(--brand-wash); padding: 2px 5px; border-radius: var(--r-xs) }
.worktree-sources button:disabled { color: var(--muted); cursor: not-allowed }
.workspace-palette-note { margin: 12px 0 14px; color: var(--muted); font-size: var(--fs-micro); line-height: 1.6 }
.workspace-palette-footer { padding-top: 8px; border-top: 1px solid var(--hair) }
.workspace-palette-empty { padding: 18px 8px; text-align: center; color: var(--muted); font-size: var(--fs-meta) }
.workspace-palette-search input { flex: 1; min-width: 0; width: 100%; border: 0; padding: 0; border-radius: 0; background: transparent; color: var(--text); font: var(--w-reg) var(--fs-ui)/1.3 var(--ui); outline: none }
.worktree-sources { max-height: 250px; padding: 0 4px 6px; overflow: auto; overscroll-behavior: contain }
.workspace-palette-group { display: block; padding: 4px 10px; color: var(--faint); font: var(--w-med) var(--fs-micro)/1.4 var(--ui) }
.worktree-sources button { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 10px; border: 0; border-radius: var(--r-sm); background: none; color: var(--text); text-align: left; font: inherit; cursor: pointer }
.worktree-sources button > .studio-icon { color: var(--faint) }
.worktree-sources button .workspace-palette-branch { min-width: 0; flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--w-reg) var(--fs-ui)/1.3 var(--mono) }
.worktree-sources button small { display: inline-grid; place-items: center; height: 20px; padding: 0 7px; border-radius: var(--r-pill); background: var(--overlay); color: var(--muted); font: var(--w-reg) var(--fs-micro)/1 var(--ui); white-space: nowrap }
.worktree-sources button:not(:disabled):hover { background: var(--overlay) }
.worktree-sources button[aria-selected="true"] { background: var(--overlay) }
.worktree-sources button:disabled { opacity: .5; cursor: not-allowed }
.workspace-palette-note { margin: 0; padding: 8px 14px; border-top: 1px solid var(--hair); color: var(--faint); font: var(--w-reg) var(--fs-meta)/1.5 var(--ui) }
.workspace-palette-footer { padding: 0 8px 6px; border-top: 1px solid var(--hair) }
.workspace-palette-footer .workspace-path summary { min-height: 28px; padding: 2px 6px }
.workspace-palette-footer .workspace-path summary > .studio-icon { width: 14px; height: 14px }
.workspace-palette-footer .workspace-path .workspace-path-copy { width: 28px; min-width: 28px; height: 28px }
.workspace-palette-footer .workspace-path .workspace-path-copy .studio-icon { width: 14px; height: 14px }
.workspace-palette-empty { padding: 18px 14px; text-align: center; color: var(--muted); font-size: var(--fs-meta) }
.workspace-palette-empty p { margin: 0 0 10px }
.workspace-palette :is(button, summary):focus-visible { outline: 2px solid var(--focus); outline-offset: -2px }
@media (max-width: 480px) { .workspace-palette { padding: 12px } .workspace-palette-identity { gap: 7px } .worktree-sources button { gap: 7px; padding-inline: 5px } .worktree-sources button small { max-width: 92px; white-space: normal; font-size: var(--fs-micro) } }
@media (max-width: 480px) { .worktree-sources button { gap: 7px; padding-inline: 8px } .worktree-sources button small { max-width: 92px; height: auto; padding: 2px 7px; white-space: normal } }
@@ -101,10 +101,11 @@ export function WorktreeChip({ sources, workspace, canCreate, selection, branch,
<div className="workspace-palette-heading"><span id={title}>{t("composer.configure_workspace")}</span><button data-action="workspace.configure" className="workspace-palette-close" aria-label={t("composer.close_workspace_configuration")} onClick={close}><StudioIcon name="close"/></button></div>
<div className="workspace-palette-identity"><StudioIcon name="folder"/><strong title={sources.sourceRoot}>{workspaceName(sources.sourceRoot)}</strong>{(selection || isolated) && <span title={directoryDescription}>{t("common.isolated_worktree")}</span>}</div>
{canCreate && <div className="worktree-modes">
<button data-action="workspace.worktree-toggle" title={workspaceDirectoryDescription(false)} aria-pressed={!selection} disabled={busy || moving !== null} onClick={() => { onSelect(null); setQuery(""); }}><StudioIcon name="folder"/>{t("composer.current_folder")}{!selection && <StudioIcon name="check"/>}</button>
<button data-action="workspace.worktree-toggle" title={creationDescription} aria-pressed={!!selection} disabled={busy || moving !== null || !sources.available} onClick={selectWorktree}><StudioIcon name="branch"/>{t("common.isolated_worktree")}{selection && <StudioIcon name="check"/>}</button>
<button data-action="workspace.worktree-toggle" title={workspaceDirectoryDescription(false)} aria-pressed={!selection} disabled={busy || moving !== null} onClick={() => { onSelect(null); setQuery(""); }}>{t("composer.current_folder")}</button>
<button data-action="workspace.worktree-toggle" title={creationDescription} aria-pressed={!!selection} disabled={busy || moving !== null || !sources.available} onClick={selectWorktree}>{t("common.isolated_worktree")}</button>
</div>}
{selection && <div className="workspace-palette-base"><span>{t("common.source_branch")}</span><strong title={label}>{compactWorkspaceValue(label)}</strong><small>{t("composer.a_task_branch_will_be")}</small></div>}
{canCreate && <p className="workspace-palette-desc">{selection ? creationDescription : workspaceDirectoryDescription(false)}</p>}
{selection && <div className="workspace-palette-label">{t("common.source_branch")}</div>}
<div className="workspace-palette-search"><StudioIcon name="search"/><input data-action-change="workspace.source-search" data-action-keydown="workspace.source-navigate" ref={search} type="search" aria-label={t("composer.search_branches")} placeholder={t(selection ? "composer.search_source_branches" : "composer.search_local_branches")} value={query} onChange={(event) => setQuery(event.target.value)} onKeyDown={(event) => {
if (event.key === "ArrowDown") { event.preventDefault(); panel.current?.querySelector<HTMLElement>('[role="option"]:not(:disabled)')?.focus(); }
}}/></div>
@@ -115,12 +116,12 @@ export function WorktreeChip({ sources, workspace, canCreate, selection, branch,
if (next !== at) { event.preventDefault(); items[(next + items.length) % items.length]?.focus(); }
if (event.key === "Home" || event.key === "End") { event.preventDefault(); (event.key === "Home" ? items[0] : items.at(-1))?.focus(); }
}}>
{[false, true].map((remote) => <div key={String(remote)}>{remote && shown.some((source) => source.remote) && <small className="workspace-palette-group">{t("composer.remote_branches")}</small>}{shown.filter((source) => source.remote === remote).map((source) => <button data-action={selection ? "workspace.source-select" : "git.branch"} key={source.ref} type="button" role="option" aria-label={source.name} aria-selected={selected(source)} disabled={!selection && (!!source.worktree || moving !== null)} title={source.worktree ? `${source.name}\n${source.worktree}` : source.name} onClick={() => {
{[false, true].map((remote) => <div key={String(remote)}>{selection && shown.some((source) => source.remote) && shown.some((source) => source.remote === remote) && <small className="workspace-palette-group">{t(remote ? "composer.remote_branches" : "composer.local_branches")}</small>}{shown.filter((source) => source.remote === remote).map((source) => <button data-action={selection ? "workspace.source-select" : "git.branch"} key={source.ref} type="button" role="option" aria-label={source.name} aria-selected={selected(source)} disabled={!selection && (!!source.worktree || moving !== null)} title={source.worktree ? `${source.name}\n${source.worktree}` : source.name} onClick={() => {
if (selection) onSelect({ ref: source.ref, sourceRoot: sources.sourceRoot }); else switchTo(source.name);
}}><StudioIcon name="branch"/><span className="workspace-palette-branch"><WorkspaceBranchName value={source.name}/></span>{source.worktree ? <small>{t("composer.used_by_another_worktree")}</small> : selected(source) && <small>{t(selection ? "composer.source" : "common.now")}</small>}<StudioIcon name={selected(source) ? "check" : "chevron"}/></button>)}</div>)}
}}><StudioIcon name="branch"/><span className="workspace-palette-branch"><WorkspaceBranchName value={source.name}/></span>{source.worktree ? <small>{t("composer.used_by_another_worktree")}</small> : selected(source) && <small>{t(selection ? "composer.source" : "common.now")}</small>}</button>)}</div>)}
{shown.length === 0 && <p className="workspace-palette-empty">{t("composer.no_matching_branches")}</p>}
</div>}
<p className="workspace-palette-note">{t(selection ? "composer.create_from_the_committed_version" : "composer.switches_immediately_other_sessions_in")}</p>
<p className="workspace-palette-note">{selection ? `${t("composer.a_task_branch_will_be")} · ${t("composer.create_from_the_committed_version")}` : t("composer.switches_immediately_other_sessions_in")}</p>
<div className="workspace-palette-footer"><WorkspacePath path={sources.sourceRoot} onResize={put}/></div>
</div>, document.body)}
</div>;