Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/web/src/components/FileWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ const BROWSER_TAB_PREFIX = '__browser__:';
// We keep an LRU of the most-recently-activated browser tabs live and unmount
// the rest; switching back to an evicted tab remounts (reloads) it.
const BROWSER_KEEPALIVE_CAP = 3;
const QUICK_SWITCHER_DOCUMENT_CLASS = 'od-quick-switcher-open';
const SKETCH_AUTOSAVE_DELAY_MS = 800;

// Stable empty folder list so the render-phase project-switch reset is
Expand Down Expand Up @@ -1630,6 +1631,13 @@ export function FileWorkspace({
return () => window.removeEventListener('keydown', onKeyDown, { capture: true });
}, [quickSwitcherOpen]);

useEffect(() => {
document.body.classList.toggle(QUICK_SWITCHER_DOCUMENT_CLASS, quickSwitcherOpen);
return () => {
document.body.classList.remove(QUICK_SWITCHER_DOCUMENT_CLASS);
};
}, [quickSwitcherOpen]);

async function handleDelete(name: string) {
if (!confirm(t('workspace.deleteFileConfirm', { name }))) return;
const ok = await deleteProjectFile(projectId, name);
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/components/QuickSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ export function QuickSwitcher({
const t = useT();
const [query, setQuery] = useState('');
const [cursor, setCursor] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<HTMLInputElement | null>(null);
const listRef = useRef<HTMLDivElement>(null);

useEffect(() => {
inputRef.current?.focus();
const setInputRef = useCallback((node: HTMLInputElement | null) => {
inputRef.current = node;
node?.focus();
}, []);

const matches = useMemo<QuickSwitcherResult[]>(() => {
Expand Down Expand Up @@ -159,7 +159,7 @@ export function QuickSwitcher({
exit="exit"
>
<input
ref={inputRef}
ref={setInputRef}
className="qs-input"
value={query}
onChange={(e) => setQuery(e.target.value)}
Expand Down
94 changes: 94 additions & 0 deletions apps/web/src/styles/viewer/routines.css
Original file line number Diff line number Diff line change
Expand Up @@ -1664,13 +1664,87 @@
border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 9%, transparent);
}
/* QuickSwitcher owns the modal input while open. The fixed composer normally
restores pointer-events on its child .composer, so suspend that child too. */
body.od-quick-switcher-open .chat-composer-fixed-layer {
pointer-events: none;
opacity: 0.58;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer {
pointer-events: none;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell:focus-within,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer.drag-active .composer-shell {
border-color: transparent;
box-shadow: none;
}
/* Keep the typing area a clean white field with a soft border so it reads as
the one input surface inside the tinted card — the resting border replaces
the removed toolbar divider as the only separator in the footer. */
.chat-composer-fixed-layer .composer-input-wrap {
background: var(--bg-panel);
border-color: var(--border-soft);
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-input-wrap,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-input-wrap:focus-within {
background: var(--bg-fill-tertiary);
border-color: transparent;
box-shadow: none;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-input-editor [contenteditable='true'],
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-input-editor .composer-editable,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer textarea,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer input {
color: var(--text-muted);
caret-color: transparent;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-input-placeholder {
color: color-mix(in srgb, var(--text-muted) 72%, transparent);
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-design-system-row,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-row {
border-color: transparent;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-picker--design-system .project-ds-picker-trigger,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-picker--design-system .project-ds-picker-trigger:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-picker--design-system .project-ds-picker-trigger.picked,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-chip,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-chip:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context--plugin,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context--workspace,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-order,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-preview-trigger,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-preview-trigger:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-open,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-comment button,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-comment button:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-active-file,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell .context-chip-strip__chip {
background: transparent;
border-color: transparent;
box-shadow: none;
color: var(--text-muted);
}
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-name,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context-kind,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-comment .staged-name strong,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-comment .staged-name span {
color: var(--text-muted);
}
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context .staged-icon,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-context .staged-remove,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-chip .staged-icon,
body.od-quick-switcher-open .chat-composer-fixed-layer .staged-chip .staged-remove,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell .context-chip-strip__icon,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-shell .context-chip-strip__remove {
background: transparent;
border-color: transparent;
box-shadow: none;
color: var(--text-muted);
}
.app .composer textarea {
min-height: 56px;
max-height: min(184px, 34vh);
Expand Down Expand Up @@ -1773,6 +1847,26 @@
color: var(--text-faint);
cursor: default;
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .icon-btn,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .icon-btn:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-import,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-research,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .session-mode-toggle__trigger,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .session-mode-toggle__trigger:hover,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .session-mode-toggle__trigger.is-open,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .avatar-agent-trigger,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-row .avatar-btn,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-send,
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-send:disabled,
body.od-quick-switcher-open .chat-composer-fixed-layer .working-dir-pill-trigger {
background: transparent;
border-color: transparent;
box-shadow: none;
color: var(--text-muted);
}
body.od-quick-switcher-open .chat-composer-fixed-layer .composer-send:disabled {
color: var(--text-faint);
}
/* The pill renders under `.app` (legacy header host) and under
`.chat-composer-fixed-layer` (its current home in the composer toolbar).
Paired selectors cover both contexts — matching the `.icon-btn` convention
Expand Down
Loading
Loading