mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 08:03:58 +00:00
* feat: select parent worktree for nesting when creating workspace Enable users to pick a parent workspace in the composer's Advanced drawer, organizing newly created worktrees hierarchically in the sidebar. The backend validates lineage relationships and gracefully retries without the parent if it becomes unavailable during creation. * feat: select parent worktree for nesting when creating workspace - Record app-picked parents as manual actions, not CLI-flag equivalents, ensuring the same user action carries consistent cleanup semantics across hosts - Gracefully retry without parent if the selection goes stale, warn the user instead of failing - Refactor create into modules: parent resolution, payload building, state merge * Allow selecting parent worktree when creating workspace - New parent worktree picker filtered by execution host and project - Preserve concurrent local writes to lineage by comparing per-record state instead of key-set membership * Allow selecting parent worktree when creating workspace - Rename "Parent workspace" label to "Parent worktree" - Filter candidates by execution host and project to prevent nesting across hosts - Wire parentWorktreeId through composer state and creation request pipeline - Update translations and add new copy for nesting-related messages
3024 lines
92 KiB
CSS
3024 lines
92 KiB
CSS
@import 'tailwindcss';
|
||
@import 'tw-animate-css';
|
||
@import '@xterm/xterm/css/xterm.css';
|
||
@import 'katex/dist/katex.min.css';
|
||
@import './rich-markdown-editor.css';
|
||
@import './markdown-preview.css';
|
||
@import './terminal.css';
|
||
@import './mobile-page.css';
|
||
|
||
@font-face {
|
||
font-family: 'Geist';
|
||
src: url('./fonts/Geist-Variable.woff2') format('woff2');
|
||
font-weight: 100 900;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
}
|
||
|
||
@font-face {
|
||
font-family: 'Orca Nerd Font Symbols';
|
||
src: url('./fonts/SymbolsNerdFontMono-Regular.woff2') format('woff2');
|
||
font-weight: 400;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
unicode-range: U+E000-F8FF, U+F0000-FFFFD, U+100000-10FFFD;
|
||
}
|
||
|
||
@custom-variant dark (&:is(.dark *));
|
||
|
||
/* Why: hover-reveal controls (sidebar/row action buttons) hide with `opacity-0`
|
||
and fade in via `group-hover`. Touch devices have no hover, so those controls
|
||
stay invisible. Gate the hide behind `can-hover:` so touch (hover: none) keeps
|
||
them visible while pointer devices preserve the reveal-on-hover behavior. */
|
||
@custom-variant can-hover (@media (hover: hover));
|
||
|
||
.theme-transition-disabled *,
|
||
.theme-transition-disabled *::before,
|
||
.theme-transition-disabled *::after {
|
||
/* Why: theme swaps flip many CSS variables at once; transitions make parts of
|
||
the UI fade at different speeds. Animations stay enabled for spinners. */
|
||
transition: none !important;
|
||
}
|
||
|
||
@theme inline {
|
||
--color-background: var(--background);
|
||
--color-foreground: var(--foreground);
|
||
--color-card: var(--card);
|
||
--color-card-foreground: var(--card-foreground);
|
||
--color-popover: var(--popover);
|
||
--color-popover-foreground: var(--popover-foreground);
|
||
--color-primary: var(--primary);
|
||
--color-primary-foreground: var(--primary-foreground);
|
||
--color-secondary: var(--secondary);
|
||
--color-secondary-foreground: var(--secondary-foreground);
|
||
--color-muted: var(--muted);
|
||
--color-muted-foreground: var(--muted-foreground);
|
||
--color-accent: var(--accent);
|
||
--color-accent-foreground: var(--accent-foreground);
|
||
--color-destructive: var(--destructive);
|
||
--color-destructive-foreground: var(--destructive-foreground);
|
||
--color-border: var(--border);
|
||
--color-input: var(--input);
|
||
--color-ring: var(--ring);
|
||
--color-agent-question: var(--agent-question);
|
||
--color-agent-question-text: var(--agent-question-text);
|
||
--color-chart-1: var(--chart-1);
|
||
--color-chart-2: var(--chart-2);
|
||
--color-chart-3: var(--chart-3);
|
||
--color-chart-4: var(--chart-4);
|
||
--color-chart-5: var(--chart-5);
|
||
--color-sidebar: var(--sidebar);
|
||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||
--color-worktree-sidebar: var(--worktree-sidebar);
|
||
--color-worktree-sidebar-foreground: var(--worktree-sidebar-foreground);
|
||
--color-worktree-sidebar-accent: var(--worktree-sidebar-accent);
|
||
--color-worktree-sidebar-accent-foreground: var(--worktree-sidebar-accent-foreground);
|
||
--color-worktree-sidebar-border: var(--worktree-sidebar-border);
|
||
--color-worktree-sidebar-ring: var(--worktree-sidebar-ring);
|
||
--color-sidebar-primary: var(--sidebar-primary);
|
||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||
--color-sidebar-accent: var(--sidebar-accent);
|
||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||
--color-sidebar-border: var(--sidebar-border);
|
||
--color-sidebar-ring: var(--sidebar-ring);
|
||
--color-terminal-pane-title-on-dark-fg: var(--terminal-pane-title-on-dark-fg);
|
||
--color-terminal-pane-title-on-dark-input-fg: var(--terminal-pane-title-on-dark-input-fg);
|
||
--color-terminal-pane-title-on-dark-placeholder: var(--terminal-pane-title-on-dark-placeholder);
|
||
--color-terminal-pane-title-on-dark-button-fg: var(--terminal-pane-title-on-dark-button-fg);
|
||
--color-terminal-pane-title-on-dark-button-hover-fg: var(
|
||
--terminal-pane-title-on-dark-button-hover-fg
|
||
);
|
||
--color-terminal-pane-title-on-dark-input-bg: var(--terminal-pane-title-on-dark-input-bg);
|
||
--color-terminal-pane-title-on-dark-separator: var(--terminal-pane-title-on-dark-separator);
|
||
--color-terminal-pane-title-on-light-fg: var(--terminal-pane-title-on-light-fg);
|
||
--color-terminal-pane-title-on-light-input-fg: var(--terminal-pane-title-on-light-input-fg);
|
||
--color-terminal-pane-title-on-light-placeholder: var(--terminal-pane-title-on-light-placeholder);
|
||
--color-terminal-pane-title-on-light-button-fg: var(--terminal-pane-title-on-light-button-fg);
|
||
--color-terminal-pane-title-on-light-button-hover-fg: var(
|
||
--terminal-pane-title-on-light-button-hover-fg
|
||
);
|
||
--color-terminal-pane-title-on-light-input-bg: var(--terminal-pane-title-on-light-input-bg);
|
||
--color-terminal-pane-title-on-light-separator: var(--terminal-pane-title-on-light-separator);
|
||
--color-ai-action-accent: var(--ai-action-accent);
|
||
--color-status-success: var(--status-success);
|
||
--color-status-success-background: var(--status-success-background);
|
||
--color-status-success-border: var(--status-success-border);
|
||
--color-workspace-status-done: var(--workspace-status-done);
|
||
--color-workspace-status-review: var(--workspace-status-review);
|
||
--color-workspace-status-progress: var(--workspace-status-progress);
|
||
--color-tab-group-split-divider: var(--tab-group-split-divider);
|
||
--color-tab-group-split-divider-strong: var(--tab-group-split-divider-strong);
|
||
--color-git-graph-ref: var(--git-graph-ref);
|
||
--color-git-graph-remote-ref: var(--git-graph-remote-ref);
|
||
--color-git-graph-base-ref: var(--git-graph-base-ref);
|
||
--color-git-graph-lane-1: var(--git-graph-lane-1);
|
||
--color-git-graph-lane-2: var(--git-graph-lane-2);
|
||
--color-git-graph-lane-3: var(--git-graph-lane-3);
|
||
--color-git-graph-lane-4: var(--git-graph-lane-4);
|
||
--color-git-graph-lane-5: var(--git-graph-lane-5);
|
||
--color-annotation-highlight: var(--annotation-highlight);
|
||
--shadow-floating: 0 10px 24px rgb(0 0 0 / 0.18);
|
||
--radius-sm: calc(var(--radius) * 0.6);
|
||
--radius-md: calc(var(--radius) * 0.8);
|
||
--radius-lg: var(--radius);
|
||
--radius-xl: calc(var(--radius) * 1.4);
|
||
--radius-2xl: calc(var(--radius) * 1.8);
|
||
--radius-3xl: calc(var(--radius) * 2.2);
|
||
--radius-4xl: calc(var(--radius) * 2.6);
|
||
}
|
||
|
||
/* ── Light Mode ──────────────────────────────────────── */
|
||
|
||
:root {
|
||
--app-font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||
--font-sans: var(--app-font-family);
|
||
--font-mono:
|
||
'SF Mono', SFMono-Regular, ui-monospace, 'Cascadia Code', Menlo, Consolas, 'Liberation Mono',
|
||
monospace;
|
||
--radius: 0.625rem;
|
||
--orca-security-background: #fff;
|
||
--orca-security-foreground: #0a0a0a;
|
||
--orca-security-card: #fff;
|
||
--orca-security-card-foreground: #0a0a0a;
|
||
--orca-security-popover: #fff;
|
||
--orca-security-popover-foreground: #0a0a0a;
|
||
--orca-security-primary: #171717;
|
||
--orca-security-primary-foreground: #fafafa;
|
||
--orca-security-secondary: #f5f5f5;
|
||
--orca-security-secondary-foreground: #171717;
|
||
--orca-security-muted: #f5f5f5;
|
||
--orca-security-muted-foreground: #737373;
|
||
--orca-security-accent: #f5f5f5;
|
||
--orca-security-accent-foreground: #171717;
|
||
--orca-security-border: #e5e5e5;
|
||
--orca-security-input: #e5e5e5;
|
||
--orca-security-ring: #a1a1a1;
|
||
--background: #fff;
|
||
--editor-surface: #ffffff;
|
||
--foreground: #0a0a0a;
|
||
--card: #fff;
|
||
--card-foreground: #0a0a0a;
|
||
--popover: #fff;
|
||
--popover-foreground: #0a0a0a;
|
||
--primary: #171717;
|
||
--primary-foreground: #fafafa;
|
||
--secondary: #f5f5f5;
|
||
--secondary-foreground: #171717;
|
||
--muted: #f5f5f5;
|
||
--muted-foreground: #737373;
|
||
--accent: #f5f5f5;
|
||
--accent-foreground: #171717;
|
||
--destructive: #e40014;
|
||
--destructive-foreground: #fcf3f3;
|
||
--border: #e5e5e5;
|
||
--input: #e5e5e5;
|
||
--ring: #a1a1a1;
|
||
--terminal-pane-locate: var(--color-blue-600);
|
||
--ai-action-accent: var(--color-violet-500);
|
||
/* "An agent is asking you something" — one hue for every surface that shows
|
||
it (sidebar, tabs, dashboard, agent map). Orange, not amber: on the map it
|
||
has to stay separable from working-yellow at a glance. */
|
||
--agent-question: var(--color-orange-600);
|
||
/* Legible weight for text/glyphs on a tinted --agent-question surface. */
|
||
--agent-question-text: var(--color-orange-700);
|
||
--status-success: #15803d;
|
||
--status-success-background: color-mix(in srgb, var(--status-success) 10%, transparent);
|
||
--status-success-border: color-mix(in srgb, var(--status-success) 25%, transparent);
|
||
/* Workspace PR-state tones: same hue in both themes so a status keeps its
|
||
identity when the theme flips. */
|
||
--workspace-status-done: #c7a594;
|
||
--workspace-status-review: #16a34a;
|
||
--workspace-status-progress: #d4a300;
|
||
--chart-1: var(--color-blue-300);
|
||
--chart-2: var(--color-blue-500);
|
||
--chart-3: var(--color-blue-600);
|
||
--chart-4: var(--color-blue-700);
|
||
--chart-5: var(--color-blue-800);
|
||
--worktree-sidebar: #f5f5f5;
|
||
--worktree-sidebar-foreground: #0a0a0a;
|
||
--worktree-sidebar-accent: #eaeaea;
|
||
--worktree-sidebar-accent-foreground: #171717;
|
||
--worktree-sidebar-border: #e5e5e5;
|
||
--worktree-sidebar-ring: #a1a1a1;
|
||
--sidebar: #fafafa;
|
||
--sidebar-foreground: #0a0a0a;
|
||
--sidebar-primary: #171717;
|
||
--sidebar-primary-foreground: #fafafa;
|
||
--sidebar-accent: #f5f5f5;
|
||
--sidebar-accent-foreground: #171717;
|
||
--sidebar-border: #e5e5e5;
|
||
--sidebar-ring: #a1a1a1;
|
||
--terminal-pane-title-on-dark-fg: rgb(255 255 255 / 0.52);
|
||
--terminal-pane-title-on-dark-input-fg: rgb(255 255 255 / 0.7);
|
||
--terminal-pane-title-on-dark-placeholder: rgb(255 255 255 / 0.38);
|
||
--terminal-pane-title-on-dark-button-fg: rgb(255 255 255);
|
||
--terminal-pane-title-on-dark-button-hover-fg: rgb(255 255 255);
|
||
--terminal-pane-title-on-dark-input-bg: rgb(255 255 255 / 0.04);
|
||
--terminal-pane-title-on-dark-separator: rgb(255 255 255 / 0.06);
|
||
--terminal-pane-title-on-light-fg: rgb(24 24 27 / 0.64);
|
||
--terminal-pane-title-on-light-input-fg: rgb(24 24 27 / 0.82);
|
||
--terminal-pane-title-on-light-placeholder: rgb(24 24 27 / 0.48);
|
||
--terminal-pane-title-on-light-button-fg: rgb(24 24 27);
|
||
--terminal-pane-title-on-light-button-hover-fg: rgb(24 24 27);
|
||
--terminal-pane-title-on-light-input-bg: rgb(24 24 27 / 0.05);
|
||
--terminal-pane-title-on-light-separator: rgb(24 24 27 / 0.1);
|
||
--git-decoration-added: #587c0c;
|
||
--git-decoration-modified: #895503;
|
||
--git-decoration-deleted: #ad0707;
|
||
--git-decoration-renamed: #007acc;
|
||
--git-decoration-untracked: #007100;
|
||
--git-decoration-copied: #007acc;
|
||
--git-decoration-ignored: #8c8c8c;
|
||
--git-graph-ref: #007acc;
|
||
--git-graph-remote-ref: #b66dff;
|
||
--git-graph-base-ref: #ea5c00;
|
||
--git-graph-lane-1: #ffb000;
|
||
--git-graph-lane-2: #dc267f;
|
||
--git-graph-lane-3: #994f00;
|
||
--git-graph-lane-4: #40b0a6;
|
||
--git-graph-lane-5: #b66dff;
|
||
--annotation-highlight: #f59e0b;
|
||
--markdown-search-match: #facc15;
|
||
--markdown-search-match-active: #fb923c;
|
||
/* Why: tab-group splits use a dedicated divider — `--border` is too faint on
|
||
card/editor surfaces when panes are side-by-side or stacked. Default must
|
||
meet >=3:1 against `--card`, not only the hover/drag strong state. */
|
||
--tab-group-split-divider: #868690;
|
||
--tab-group-split-divider-strong: #71717a;
|
||
}
|
||
|
||
/* ── Dark Mode ───────────────────────────────────────── */
|
||
|
||
.dark {
|
||
--orca-security-background: #0a0a0a;
|
||
--orca-security-foreground: #fafafa;
|
||
--orca-security-card: #171717;
|
||
--orca-security-card-foreground: #fafafa;
|
||
--orca-security-popover: #171717;
|
||
--orca-security-popover-foreground: #fafafa;
|
||
--orca-security-primary: #e5e5e5;
|
||
--orca-security-primary-foreground: #171717;
|
||
--orca-security-secondary: #262626;
|
||
--orca-security-secondary-foreground: #fafafa;
|
||
--orca-security-muted: #262626;
|
||
--orca-security-muted-foreground: #a1a1a1;
|
||
--orca-security-accent: #404040;
|
||
--orca-security-accent-foreground: #fafafa;
|
||
--orca-security-border: rgb(255 255 255 / 0.07);
|
||
--orca-security-input: rgb(255 255 255 / 0.15);
|
||
--orca-security-ring: #737373;
|
||
--background: #0a0a0a;
|
||
--editor-surface: #1e1e1e;
|
||
--foreground: #fafafa;
|
||
--card: #171717;
|
||
--card-foreground: #fafafa;
|
||
--popover: #171717;
|
||
--popover-foreground: #fafafa;
|
||
--primary: #e5e5e5;
|
||
--primary-foreground: #171717;
|
||
--secondary: #262626;
|
||
--secondary-foreground: #fafafa;
|
||
--muted: #262626;
|
||
--muted-foreground: #a1a1a1;
|
||
--accent: #404040;
|
||
--accent-foreground: #fafafa;
|
||
--destructive: #ff6568;
|
||
--destructive-foreground: #df2225;
|
||
--border: rgb(255 255 255 / 0.07);
|
||
--input: rgb(255 255 255 / 0.15);
|
||
--ring: #737373;
|
||
--terminal-pane-locate: var(--color-blue-400);
|
||
--ai-action-accent: var(--color-violet-400);
|
||
--agent-question: var(--color-orange-500);
|
||
--agent-question-text: var(--color-orange-300);
|
||
--status-success: #86efac;
|
||
--status-success-background: color-mix(in srgb, var(--status-success) 10%, transparent);
|
||
--status-success-border: color-mix(in srgb, var(--status-success) 25%, transparent);
|
||
--workspace-status-done: #c7a594;
|
||
--workspace-status-review: #16a34a;
|
||
--workspace-status-progress: #d4a300;
|
||
--chart-1: var(--color-blue-300);
|
||
--chart-2: var(--color-blue-500);
|
||
--chart-3: var(--color-blue-600);
|
||
--chart-4: var(--color-blue-700);
|
||
--chart-5: var(--color-blue-800);
|
||
/* Why: light mode sets worktree-sidebar to secondary (#f5f5f5); mirror that
|
||
lift in dark mode so the panel reads clearly above #0a0a0a canvas. */
|
||
--worktree-sidebar: #2a2a2a;
|
||
--worktree-sidebar-foreground: #fafafa;
|
||
--worktree-sidebar-accent: #353535;
|
||
--worktree-sidebar-accent-foreground: #fafafa;
|
||
/* Match --border / --sidebar-border (7%) so the left sidebar's divider lines
|
||
aren't brighter than the rest of the UI in dark mode (#5906). */
|
||
--worktree-sidebar-border: rgb(255 255 255 / 0.07);
|
||
--worktree-sidebar-ring: #737373;
|
||
--sidebar: #171717;
|
||
--sidebar-foreground: #fafafa;
|
||
--sidebar-primary: #1447e6;
|
||
--sidebar-primary-foreground: #fafafa;
|
||
--sidebar-accent: #262626;
|
||
--sidebar-accent-foreground: #fafafa;
|
||
--sidebar-border: rgb(255 255 255 / 0.07);
|
||
--sidebar-ring: #525252;
|
||
--terminal-pane-title-on-dark-fg: rgb(255 255 255 / 0.52);
|
||
--terminal-pane-title-on-dark-input-fg: rgb(255 255 255 / 0.7);
|
||
--terminal-pane-title-on-dark-placeholder: rgb(255 255 255 / 0.38);
|
||
--terminal-pane-title-on-dark-button-fg: rgb(255 255 255);
|
||
--terminal-pane-title-on-dark-button-hover-fg: rgb(255 255 255);
|
||
--terminal-pane-title-on-dark-input-bg: rgb(255 255 255 / 0.04);
|
||
--terminal-pane-title-on-dark-separator: rgb(255 255 255 / 0.06);
|
||
--terminal-pane-title-on-light-fg: rgb(24 24 27 / 0.64);
|
||
--terminal-pane-title-on-light-input-fg: rgb(24 24 27 / 0.82);
|
||
--terminal-pane-title-on-light-placeholder: rgb(24 24 27 / 0.48);
|
||
--terminal-pane-title-on-light-button-fg: rgb(24 24 27);
|
||
--terminal-pane-title-on-light-button-hover-fg: rgb(24 24 27);
|
||
--terminal-pane-title-on-light-input-bg: rgb(24 24 27 / 0.05);
|
||
--terminal-pane-title-on-light-separator: rgb(24 24 27 / 0.1);
|
||
--git-decoration-added: #81b88b;
|
||
--git-decoration-modified: #e2c08d;
|
||
--git-decoration-deleted: #c74e39;
|
||
--git-decoration-renamed: #73c991;
|
||
--git-decoration-untracked: #73c991;
|
||
--git-decoration-copied: #73c991;
|
||
--git-decoration-ignored: #6e6e6e;
|
||
--git-graph-ref: #3794ff;
|
||
--git-graph-remote-ref: #b66dff;
|
||
--git-graph-base-ref: #ea5c00;
|
||
--git-graph-lane-1: #ffb000;
|
||
--git-graph-lane-2: #dc267f;
|
||
--git-graph-lane-3: #ce9178;
|
||
--git-graph-lane-4: #40b0a6;
|
||
--git-graph-lane-5: #b66dff;
|
||
--annotation-highlight: #fbbf24;
|
||
--markdown-search-match: #facc15;
|
||
--markdown-search-match-active: #fb923c;
|
||
/* Brighter than terminal defaults so the always-visible line clears 3:1 on `--card`. */
|
||
--tab-group-split-divider: #71717a;
|
||
--tab-group-split-divider-strong: #a1a1aa;
|
||
}
|
||
|
||
.plugin-security-chrome {
|
||
/* Why: plugin themes may style the app, but provenance and consent must
|
||
retain host-owned contrast so a pack cannot disguise a trust decision. */
|
||
--background: var(--orca-security-background);
|
||
--foreground: var(--orca-security-foreground);
|
||
--card: var(--orca-security-card);
|
||
--card-foreground: var(--orca-security-card-foreground);
|
||
--popover: var(--orca-security-popover);
|
||
--popover-foreground: var(--orca-security-popover-foreground);
|
||
--primary: var(--orca-security-primary);
|
||
--primary-foreground: var(--orca-security-primary-foreground);
|
||
--secondary: var(--orca-security-secondary);
|
||
--secondary-foreground: var(--orca-security-secondary-foreground);
|
||
--muted: var(--orca-security-muted);
|
||
--muted-foreground: var(--orca-security-muted-foreground);
|
||
--accent: var(--orca-security-accent);
|
||
--accent-foreground: var(--orca-security-accent-foreground);
|
||
--border: var(--orca-security-border);
|
||
--input: var(--orca-security-input);
|
||
--ring: var(--orca-security-ring);
|
||
}
|
||
|
||
.linear-priority-bars {
|
||
--linear-priority-bar-inactive-fill: color-mix(in srgb, lch(39.576 1.25 282) 34%, transparent);
|
||
}
|
||
|
||
.dark .linear-priority-bars {
|
||
--linear-priority-bar-inactive-fill: color-mix(in srgb, lch(39.576 1.25 282) 62%, transparent);
|
||
}
|
||
|
||
[data-sonner-toaster] {
|
||
font-family: var(--font-sans);
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] {
|
||
align-items: flex-start;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
padding: 14px 16px;
|
||
box-shadow: var(--shadow-floating);
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-icon] {
|
||
margin-top: 2px;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-content] {
|
||
min-width: 0;
|
||
flex: 1 1 calc(100% - 30px);
|
||
gap: 6px;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-button] {
|
||
/* Why: Sonner's native action slots otherwise share the copy row and squeeze
|
||
long notices; indent the wrapped footer to align with text after the icon. */
|
||
margin-top: 2px;
|
||
margin-left: 26px;
|
||
margin-right: 0;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-button] ~ [data-button] {
|
||
margin-left: 0;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-title] {
|
||
line-height: 1.35;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-description] {
|
||
/* Why: match the 80% body-copy opacity used by the custom-React toast bodies
|
||
(delete-worktree-failure, local-base-ref-suggestion) so string and JSX
|
||
descriptions read at the same weight. */
|
||
color: color-mix(in srgb, var(--popover-foreground) 80%, transparent);
|
||
line-height: 1.45;
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button] {
|
||
/* Why: Sonner's dark-theme defaults can resolve the close icon and button
|
||
background to the same color; keep the X readable on both Orca themes. */
|
||
color: var(--popover-foreground);
|
||
background: var(--popover);
|
||
border-color: rgb(0 0 0 / 0.14);
|
||
}
|
||
|
||
[data-sonner-toaster][data-sonner-theme='dark']
|
||
[data-sonner-toast][data-styled='true']
|
||
[data-close-button] {
|
||
color: var(--popover-foreground);
|
||
background: var(--popover);
|
||
border-color: rgb(255 255 255 / 0.14);
|
||
}
|
||
|
||
.dark [data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button] {
|
||
border-color: rgb(255 255 255 / 0.14);
|
||
}
|
||
|
||
[data-sonner-toaster] [data-sonner-toast][data-styled='true'] [data-close-button]:hover {
|
||
color: var(--accent-foreground);
|
||
background: var(--accent);
|
||
}
|
||
|
||
/* ── Base Layer ──────────────────────────────────────── */
|
||
|
||
@layer base {
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
@apply border-border outline-ring/50;
|
||
}
|
||
|
||
body {
|
||
@apply bg-background text-foreground;
|
||
margin: 0;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
/* Why: mobile browser chrome changes the visible viewport while open. */
|
||
height: 100dvh;
|
||
font-family: var(
|
||
--app-font-family,
|
||
'Geist',
|
||
-apple-system,
|
||
BlinkMacSystemFont,
|
||
'Segoe UI',
|
||
sans-serif
|
||
);
|
||
letter-spacing: 0.01em;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
}
|
||
|
||
ul {
|
||
list-style: none;
|
||
}
|
||
|
||
:where(button:not(:disabled)) {
|
||
cursor: pointer;
|
||
}
|
||
|
||
:where(button:disabled) {
|
||
cursor: default;
|
||
}
|
||
}
|
||
|
||
/* ── Sleek scrollbar (VS Code-like) ─────────────────── */
|
||
|
||
.scrollbar-sleek {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: color-mix(in srgb, var(--muted-foreground, #737373) 34%, transparent) transparent;
|
||
}
|
||
|
||
.scrollbar-sleek::-webkit-scrollbar {
|
||
width: 12px;
|
||
height: 12px;
|
||
}
|
||
|
||
.scrollbar-sleek::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.scrollbar-sleek::-webkit-scrollbar-thumb {
|
||
background: color-mix(in srgb, var(--muted-foreground, #737373) 28%, transparent);
|
||
border: 3px solid transparent;
|
||
border-radius: 0;
|
||
background-clip: padding-box;
|
||
min-height: 28px;
|
||
}
|
||
|
||
/* Thicker variant for dense data tables where the default 6px-thumb is hard to grab. */
|
||
.scrollbar-sleek-lg {
|
||
scrollbar-width: auto;
|
||
}
|
||
|
||
.scrollbar-sleek-lg::-webkit-scrollbar {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.scrollbar-sleek-lg::-webkit-scrollbar-thumb {
|
||
border-width: 3px;
|
||
min-height: 36px;
|
||
}
|
||
|
||
.scrollbar-sleek::-webkit-scrollbar-thumb:hover {
|
||
background-color: color-mix(in srgb, var(--muted-foreground, #737373) 48%, transparent);
|
||
}
|
||
|
||
.scrollbar-sleek::-webkit-scrollbar-thumb:active {
|
||
background-color: color-mix(in srgb, var(--foreground, #171717) 36%, transparent);
|
||
}
|
||
|
||
.scrollbar-sleek-parent:hover .scrollbar-sleek,
|
||
.scrollbar-sleek:hover {
|
||
scrollbar-color: color-mix(in srgb, var(--muted-foreground, #737373) 48%, transparent) transparent;
|
||
}
|
||
|
||
/* Why: no reserved `scrollbar-gutter` — paired with `overflow-y: auto`, a list
|
||
that fits shows no scrollbar (and no Windows scrollbar arrow buttons), so its
|
||
rows stay flush with the fixed header controls, matching macOS. The gutter is
|
||
only taken while actually scrolling. Thumb still fades in on hover. */
|
||
.worktree-sidebar-scrollbar {
|
||
padding-right: 4px;
|
||
scrollbar-color: transparent transparent;
|
||
}
|
||
|
||
.worktree-sidebar-scrollbar::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.worktree-sidebar-scrollbar::-webkit-scrollbar-thumb {
|
||
background-color: transparent;
|
||
/* Why: no right inset — a 3px transparent right border left a visible strip
|
||
beside the sidebar resize seam that read like a gap in the drag handle. */
|
||
border-width: 3px 0 3px 0;
|
||
}
|
||
|
||
.scrollbar-sleek-parent:hover .worktree-sidebar-scrollbar,
|
||
.worktree-sidebar-scrollbar:hover {
|
||
scrollbar-color: color-mix(in srgb, var(--muted-foreground, #737373) 48%, transparent) transparent;
|
||
}
|
||
|
||
.scrollbar-sleek-parent:hover .worktree-sidebar-scrollbar::-webkit-scrollbar-thumb,
|
||
.worktree-sidebar-scrollbar:hover::-webkit-scrollbar-thumb {
|
||
background-color: color-mix(in srgb, var(--muted-foreground, #737373) 28%, transparent);
|
||
}
|
||
|
||
.scrollbar-sleek-parent:hover .worktree-sidebar-scrollbar::-webkit-scrollbar-thumb:hover,
|
||
.worktree-sidebar-scrollbar:hover::-webkit-scrollbar-thumb:hover {
|
||
background-color: color-mix(in srgb, var(--muted-foreground, #737373) 48%, transparent);
|
||
}
|
||
|
||
.scrollbar-sleek-parent:hover .worktree-sidebar-scrollbar::-webkit-scrollbar-thumb:active,
|
||
.worktree-sidebar-scrollbar:hover::-webkit-scrollbar-thumb:active {
|
||
background-color: color-mix(in srgb, var(--foreground, #171717) 36%, transparent);
|
||
}
|
||
|
||
[data-slot='popover-content'] {
|
||
max-height: var(--radix-popover-content-available-height);
|
||
}
|
||
|
||
/* Radix owns the available-height variable on PopoverContent. Make that same
|
||
node scrollable so Chromium scrollbar hit-testing stays inside the layer. */
|
||
.popover-scroll-content {
|
||
color-scheme: dark;
|
||
max-height: min(15rem, var(--radix-popover-content-available-height, 15rem));
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
}
|
||
|
||
/* ── Editor-style scrollbar (matches Monaco) ────────── */
|
||
|
||
.scrollbar-editor,
|
||
.xterm .xterm-viewport {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(121, 121, 121, 0.4) transparent;
|
||
}
|
||
|
||
.scrollbar-editor::-webkit-scrollbar,
|
||
.xterm .xterm-viewport::-webkit-scrollbar {
|
||
width: 14px;
|
||
}
|
||
|
||
.scrollbar-editor::-webkit-scrollbar-track,
|
||
.xterm .xterm-viewport::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.scrollbar-editor::-webkit-scrollbar-thumb,
|
||
.xterm .xterm-viewport::-webkit-scrollbar-thumb {
|
||
background: rgba(121, 121, 121, 0.4);
|
||
border: 3px solid transparent;
|
||
border-radius: 7px;
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.scrollbar-editor::-webkit-scrollbar-thumb:hover,
|
||
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(121, 121, 121, 0.7);
|
||
border: 3px solid transparent;
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.combined-diff-scroll-container {
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.combined-diff-scroll-container::-webkit-scrollbar {
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
/* Why: horizontal tab strips scroll via chevrons, wheel, and trackpad; a
|
||
native scrollbar thumb overlays tab labels and looks broken. */
|
||
.terminal-tab-strip,
|
||
.project-view-tab-strip {
|
||
-ms-overflow-style: none;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.terminal-tab-strip::-webkit-scrollbar,
|
||
.project-view-tab-strip::-webkit-scrollbar {
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
/* Why: mask fades avoid painting card-colored overlays that mismatch active tabs. */
|
||
.terminal-tab-strip--fade-start.terminal-tab-strip--fade-end {
|
||
-webkit-mask-image: linear-gradient(
|
||
to right,
|
||
transparent,
|
||
#000 1.5rem,
|
||
#000 calc(100% - 1.5rem),
|
||
transparent
|
||
);
|
||
mask-image: linear-gradient(
|
||
to right,
|
||
transparent,
|
||
#000 1.5rem,
|
||
#000 calc(100% - 1.5rem),
|
||
transparent
|
||
);
|
||
}
|
||
|
||
.terminal-tab-strip--fade-start:not(.terminal-tab-strip--fade-end) {
|
||
-webkit-mask-image: linear-gradient(to right, transparent, #000 1.5rem, #000 100%);
|
||
mask-image: linear-gradient(to right, transparent, #000 1.5rem, #000 100%);
|
||
}
|
||
|
||
.terminal-tab-strip--fade-end:not(.terminal-tab-strip--fade-start) {
|
||
-webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 1.5rem), transparent);
|
||
mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 1.5rem), transparent);
|
||
}
|
||
|
||
/* Tab activity affordance (amber wash) is rendered as a React DOM child in
|
||
SortableTab.tsx rather than via ::after here, so the drop-indicator
|
||
::before/::after pseudo-elements stay free for drag-and-drop feedback. See
|
||
drop-indicator.ts and SortableTab.tsx for the implementation. */
|
||
|
||
/* ── Layout ──────────────────────────────────────────── */
|
||
|
||
#root {
|
||
height: 100dvh;
|
||
width: 100vw;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.app-layout {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100dvh;
|
||
width: 100vw;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Why: native windows have a stable content box and need no wake-time viewport reflow. */
|
||
html.native-shell,
|
||
html.native-shell body,
|
||
html.native-shell #root,
|
||
html.native-shell .app-layout {
|
||
height: 100%;
|
||
}
|
||
|
||
/* Tab-group split resize handle — wide hit area, visible center line. */
|
||
.tab-group-split-resize-handle {
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
background: transparent;
|
||
}
|
||
|
||
.tab-group-split-resize-handle.is-vertical {
|
||
width: 6px;
|
||
cursor: col-resize;
|
||
}
|
||
|
||
.tab-group-split-resize-handle.is-horizontal {
|
||
height: 6px;
|
||
cursor: row-resize;
|
||
}
|
||
|
||
.tab-group-split-resize-handle::after {
|
||
content: '';
|
||
position: absolute;
|
||
border-radius: 1px;
|
||
background: var(--tab-group-split-divider);
|
||
transition: background 100ms ease;
|
||
}
|
||
|
||
.tab-group-split-resize-handle.is-vertical::after {
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 50%;
|
||
width: 3px;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
.tab-group-split-resize-handle.is-horizontal::after {
|
||
left: 0;
|
||
right: 0;
|
||
top: 50%;
|
||
height: 3px;
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
.tab-group-split-resize-handle:hover::after,
|
||
.tab-group-split-resize-handle.is-dragging::after {
|
||
background: var(--tab-group-split-divider-strong);
|
||
}
|
||
|
||
/* ── Titlebar ────────────────────────────────────────── */
|
||
|
||
/* Why: Radix portals (dropdowns, popovers, dialogs, sheets) render at the
|
||
document root. Electron's OS-level drag hit-test ignores z-index — portaled
|
||
content that visually overlaps a -webkit-app-region: drag element is
|
||
unclickable without an explicit no-drag. These rules cover every Radix
|
||
portal variant: popper wrappers (dropdowns, popovers), dialog/sheet
|
||
overlays, and their content panels. */
|
||
[data-radix-popper-content-wrapper],
|
||
[data-slot='sheet-overlay'],
|
||
[data-slot='sheet-content'],
|
||
[data-slot='dialog-overlay'],
|
||
[data-slot='dialog-content'] {
|
||
-webkit-app-region: no-drag;
|
||
}
|
||
|
||
.titlebar {
|
||
height: 36px;
|
||
min-height: 36px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
background: var(--bg-titlebar, var(--card));
|
||
border-bottom: 1px solid var(--border);
|
||
-webkit-app-region: drag;
|
||
user-select: none;
|
||
}
|
||
|
||
/* Why: workspace view still needs a full titlebar-height strip above the
|
||
sidebar so the native macOS traffic lights and sidebar chrome sit in the
|
||
same vertical band they use in settings/landing. The center and right
|
||
workspace columns are offset separately to align with this 36px baseline
|
||
(4px drag strip + 32px tab row). */
|
||
.titlebar-left {
|
||
height: 36px;
|
||
min-height: 36px;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
flex-shrink: 0;
|
||
/* Why: in workspace view this strip sits directly above the worktree
|
||
sidebar — match its surface so the left column reads as one panel. */
|
||
background: var(--worktree-sidebar);
|
||
/* Why: draw the divider via inset shadow instead of border-bottom so the
|
||
flex content area spans the full 36px. With border-bottom + border-box,
|
||
the content area shrinks to 35px and flex-centered controls land at
|
||
y=17.5, a half-pixel below the native traffic-light center at y=18
|
||
(set via setWindowButtonPosition in createMainWindow.ts). The shadow
|
||
paints the same 1px seam without stealing layout space, so the sidebar
|
||
toggle, agent badge, and back/forward arrows align pixel-perfectly
|
||
with the traffic lights. */
|
||
box-shadow: inset 0 -1px 0 var(--border);
|
||
-webkit-app-region: drag;
|
||
user-select: none;
|
||
}
|
||
|
||
/* Why: when the sidebar is collapsed this header floats over the tab strip,
|
||
so keep the standard titlebar surface instead of the sidebar tint. */
|
||
.titlebar-left-floating {
|
||
background: var(--bg-titlebar, var(--card));
|
||
}
|
||
|
||
.titlebar-traffic-light-pad {
|
||
width: calc(80px / var(--ui-zoom-factor, 1));
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Why: small identity anchor on desktop custom titlebars where native window
|
||
chrome is hidden. Sized to sit comfortably in the 36px titlebar with a
|
||
little horizontal breathing room. The SVG fill is white; light mode inverts
|
||
it to black so it reads on the light titlebar background. */
|
||
.titlebar-logo {
|
||
height: 16px;
|
||
flex-shrink: 0;
|
||
margin: 0 4px 0 10px;
|
||
opacity: 0.75;
|
||
}
|
||
|
||
/* Why: .dark is toggled on <html> (the :root), so :not(.dark) on a descendant
|
||
always matches. Scope to :root:not(.dark) to correctly target light mode only. */
|
||
:root:not(.dark) .titlebar-logo {
|
||
filter: invert(1);
|
||
}
|
||
|
||
/* Why: desktop custom title bar buttons replace hidden native OS chrome.
|
||
Anchored to the top-right corner of the viewport so they overlay any layout
|
||
mode (full titlebar, workspace split, etc.) and stay accessible at all times. */
|
||
.window-controls {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
z-index: 9999;
|
||
display: flex;
|
||
flex-direction: row;
|
||
height: 36px;
|
||
-webkit-app-region: no-drag;
|
||
}
|
||
|
||
.window-controls-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 46px;
|
||
height: 36px;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
transition:
|
||
background 100ms,
|
||
color 100ms;
|
||
}
|
||
|
||
.window-controls-btn:hover {
|
||
background: var(--accent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.window-controls-close:hover {
|
||
background: #c42b1c;
|
||
color: #fff;
|
||
}
|
||
|
||
/* Why: flex spacer that reserves the width occupied by the three window-control
|
||
buttons (3 × 46px) so that the preceding titlebar content is not obscured by
|
||
the fixed-position overlay when custom desktop chrome is active. */
|
||
.window-controls-titlebar-spacer {
|
||
flex-shrink: 0;
|
||
width: var(--window-controls-width, 0px);
|
||
}
|
||
|
||
.right-sidebar-header-drag {
|
||
-webkit-app-region: drag;
|
||
user-select: none;
|
||
}
|
||
|
||
[data-terminal-focus-release-surface='true'] {
|
||
-webkit-app-region: drag;
|
||
}
|
||
|
||
/* Why: Electron drag regions swallow renderer pointer events. While regular
|
||
xterm owns focus, make the next top-chrome click observable so it can release
|
||
terminal zoom ownership; after release these surfaces become draggable again. */
|
||
:root[data-regular-terminal-input-focused] .titlebar,
|
||
:root[data-regular-terminal-input-focused] .titlebar-left,
|
||
:root[data-regular-terminal-input-focused] .window-controls-titlebar-spacer,
|
||
:root[data-regular-terminal-input-focused] .right-sidebar-header-drag,
|
||
:root[data-regular-terminal-input-focused] [data-terminal-focus-release-surface='true'] {
|
||
-webkit-app-region: no-drag;
|
||
}
|
||
|
||
.right-sidebar-header-no-drag {
|
||
-webkit-app-region: no-drag;
|
||
}
|
||
|
||
/* Why: the right sidebar header extends to the window's right edge. When custom
|
||
desktop chrome is active, the fixed-position window-controls overlay sits on
|
||
top of that edge, so we add matching padding-right so the header's close
|
||
button (pr-1) isn't hidden behind the overlay. */
|
||
.right-sidebar-header-inset {
|
||
padding-right: var(--window-controls-width, 0px);
|
||
}
|
||
|
||
/* Why: in side-bar activity-bar mode the icon strip sits flush against the
|
||
right window edge. The fixed-position window-controls overlay (height 36px,
|
||
width 138px) covers the top-right corner when custom desktop chrome is
|
||
active, so the topmost sidebar icons are unreachable without this offset. */
|
||
.side-activity-bar-windows-inset {
|
||
padding-top: var(--window-controls-height, 0px);
|
||
}
|
||
|
||
/* Why: in side activity-bar mode the panel header does NOT extend to the window
|
||
edge — the 40px icon strip sits to its right. But the window-controls overlay
|
||
is 138px wide when custom desktop chrome is active, so it still overlaps the
|
||
panel by (138 - 40) = 98px. Apply exactly that remainder as padding-right so
|
||
the close button clears the minimize button. max(0px, ...) keeps this a no-op
|
||
when the var is 0px and the subtraction would otherwise go negative. */
|
||
.right-sidebar-header-side-inset {
|
||
padding-right: max(0px, calc(var(--window-controls-width, 0px) - 40px));
|
||
}
|
||
|
||
.sidebar-toggle {
|
||
-webkit-app-region: no-drag;
|
||
background: none;
|
||
border: none;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
padding: 4px 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 4px;
|
||
transition: background 150ms;
|
||
}
|
||
|
||
.sidebar-toggle:hover {
|
||
background: var(--accent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
/* Why: tighter padding for the back/forward pair so the arrows sit
|
||
close together as a single navigation group, while the standard
|
||
.sidebar-toggle spacing stays unchanged for other titlebar icons. */
|
||
.sidebar-toggle-compact {
|
||
padding: 4px 5px;
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.sidebar-toggle-compact:disabled {
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.sidebar-toggle:disabled {
|
||
cursor: default;
|
||
opacity: 0.45;
|
||
}
|
||
|
||
.sidebar-toggle:disabled:hover {
|
||
background: none;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.titlebar-app-name {
|
||
-webkit-app-region: no-drag;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
height: 24px;
|
||
max-width: min(280px, 32vw);
|
||
margin-left: 2px;
|
||
margin-right: 4px;
|
||
padding: 0 6px;
|
||
border-radius: 4px;
|
||
color: var(--muted-foreground);
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
line-height: 24px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Why: when the app name sits inside the worktree sidebar header, it should
|
||
inherit that panel's contrast instead of the generic titlebar text color. */
|
||
.titlebar-left:not(.titlebar-left-floating) .titlebar-app-name {
|
||
color: var(--worktree-sidebar-foreground);
|
||
}
|
||
|
||
.titlebar-app-name-main {
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.titlebar-icon-button {
|
||
-webkit-app-region: no-drag;
|
||
background: none;
|
||
border: none;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
padding: 4px;
|
||
margin-right: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 4px;
|
||
transition: background 150ms;
|
||
}
|
||
|
||
.titlebar-icon-button:hover {
|
||
background: var(--accent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.titlebar-icon-button:disabled {
|
||
cursor: default;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.titlebar-icon-button:disabled:hover {
|
||
background: none;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.editor-header {
|
||
display: flex;
|
||
align-items: center;
|
||
/* Keep adjacent actions aligned with the app's compact toolbar spacing. */
|
||
gap: 4px;
|
||
min-height: 36px;
|
||
padding: 6px 14px;
|
||
background: var(--editor-surface);
|
||
border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.editor-header-text {
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
|
||
.editor-header-path {
|
||
display: block;
|
||
min-width: 0;
|
||
flex: 0 1 auto;
|
||
padding: 0;
|
||
max-width: 100%;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--muted-foreground);
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
line-height: 1.2;
|
||
font-family:
|
||
ui-monospace,
|
||
SFMono-Regular,
|
||
SF Mono,
|
||
Menlo,
|
||
Consolas,
|
||
monospace;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
text-align: left;
|
||
}
|
||
|
||
.editor-header-path:hover,
|
||
.editor-header-path:focus-visible {
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.editor-header-path--static {
|
||
cursor: default;
|
||
}
|
||
|
||
.editor-header-path--static:hover,
|
||
.editor-header-path--static:focus-visible {
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.editor-header-path-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.editor-header-copy-toast {
|
||
opacity: 0;
|
||
transform: translateY(2px);
|
||
transition:
|
||
opacity 120ms ease,
|
||
transform 120ms ease;
|
||
pointer-events: none;
|
||
flex-shrink: 0;
|
||
padding: 2px 6px;
|
||
border-radius: 999px;
|
||
background: color-mix(in srgb, var(--accent) 72%, transparent);
|
||
color: var(--foreground);
|
||
font-size: 11px;
|
||
line-height: 1.2;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.editor-header-copy-toast.is-visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* ── Content ─────────────────────────────────────────── */
|
||
|
||
.content-area {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* ── Jump palette (Cmd+J) ─────────────────────────────── */
|
||
|
||
/* Why the extra qualifier: CommandItem also sets data-[selected=true]:bg-accent;
|
||
this selector must beat that utility so light-mode selection is actually visible.
|
||
The role='option' arm covers virtualized rows that render outside cmdk. */
|
||
[data-slot='command-item'].jump-palette-item[data-selected='true'],
|
||
[role='option'].jump-palette-item[data-selected='true'] {
|
||
/* Why: flat --accent (#f5f5f5) on the palette surface (#fff) is nearly invisible
|
||
in light mode — the keyboard cursor has to read as a solid selected row.
|
||
Mix foreground into background and draw an inset ring; expose the surface
|
||
as a var so status-pip cutouts can match without hardcoding. */
|
||
--jump-palette-selection-surface: color-mix(in srgb, var(--foreground) 12%, var(--background));
|
||
background: var(--jump-palette-selection-surface);
|
||
border-color: transparent;
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground) 18%, transparent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.dark [data-slot='command-item'].jump-palette-item[data-selected='true'],
|
||
.dark [role='option'].jump-palette-item[data-selected='true'] {
|
||
/* Why accent, not the light-mode mix: --accent (#404040) already reads clearly on
|
||
dark surfaces; a low-%% foreground mix was a visible step down from the old row. */
|
||
--jump-palette-selection-surface: var(--accent);
|
||
background: var(--jump-palette-selection-surface);
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground) 16%, transparent);
|
||
color: var(--accent-foreground);
|
||
}
|
||
|
||
/* ── Sidebar ─────────────────────────────────────────── */
|
||
|
||
[data-file-explorer-row][data-selected='true'] {
|
||
/* Why: flat bg-accent ≈ panel background in light mode, so selection mixes
|
||
foreground into accent and adds an inset ring to stay legible in both themes. */
|
||
background: color-mix(in srgb, var(--foreground) 8%, var(--accent));
|
||
box-shadow: inset 0 0 0 1px var(--border);
|
||
color: var(--accent-foreground);
|
||
}
|
||
|
||
[data-file-explorer-row][data-selected='true']:hover {
|
||
background: color-mix(in srgb, var(--foreground) 10%, var(--accent));
|
||
}
|
||
|
||
.dark [data-file-explorer-row][data-selected='true'] {
|
||
background: color-mix(in srgb, var(--accent) 70%, transparent);
|
||
}
|
||
|
||
.dark [data-file-explorer-row][data-selected='true']:hover {
|
||
background: color-mix(in srgb, var(--accent) 80%, transparent);
|
||
}
|
||
|
||
.sidebar {
|
||
width: 240px;
|
||
flex-shrink: 0;
|
||
background: var(--sidebar);
|
||
border-right: 1px solid var(--sidebar-border, var(--border));
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
transition: width 200ms ease;
|
||
}
|
||
|
||
/* Why: light-mode sidebar-accent/40 is nearly invisible on the sidebar, while
|
||
agent rows stack on top of the card hover. Keep nested rows quieter than the
|
||
workspace card without changing the established dark-mode balance. */
|
||
.worktree-sidebar-card-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 4%, transparent);
|
||
}
|
||
|
||
.dark .worktree-sidebar-card-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-accent) 40%, transparent);
|
||
}
|
||
|
||
/* Why: the brighter border carries the selected state; a translucent wash keeps
|
||
card text legible instead of lifting the surface toward the foreground color. */
|
||
[data-worktree-card-surface][data-worktree-card-active='primary'] {
|
||
border-color: color-mix(in srgb, var(--worktree-sidebar-border) 40%, transparent);
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 8%, transparent);
|
||
box-shadow: 0 1px 2px color-mix(in srgb, var(--worktree-sidebar-foreground) 4%, transparent);
|
||
}
|
||
|
||
.dark [data-worktree-card-surface][data-worktree-card-active='primary'] {
|
||
border-color: color-mix(
|
||
in srgb,
|
||
var(--worktree-sidebar-foreground) 18%,
|
||
var(--worktree-sidebar-border)
|
||
);
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 10%, transparent);
|
||
box-shadow: 0 1px 2px color-mix(in srgb, var(--worktree-sidebar-foreground) 3%, transparent);
|
||
}
|
||
|
||
[data-worktree-card-surface][data-worktree-card-active='secondary'] {
|
||
border-color: color-mix(in srgb, var(--sidebar-ring) 25%, transparent);
|
||
background: color-mix(in srgb, var(--sidebar-accent) 45%, transparent);
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 15%, transparent);
|
||
}
|
||
|
||
.dark [data-worktree-card-surface][data-worktree-card-active='secondary'] {
|
||
border-color: color-mix(in srgb, var(--sidebar-ring) 28%, transparent);
|
||
background: color-mix(in srgb, var(--sidebar-accent) 34%, transparent);
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 18%, transparent);
|
||
}
|
||
|
||
.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 1.25%, transparent);
|
||
}
|
||
|
||
.worktree-agent-row-hover[data-focused-agent-pane='true'] {
|
||
/* Why: a borderless fill. --sidebar-accent ≈ --sidebar in light mode, so the
|
||
selection wash mixes sidebar-foreground into the accent to stay legible
|
||
without going so dark that muted text (timestamp) drops out. */
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 12%, var(--sidebar-accent));
|
||
}
|
||
|
||
.dark .worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||
}
|
||
|
||
.dark .worktree-agent-row-hover[data-focused-agent-pane='true'] {
|
||
background: color-mix(in srgb, var(--accent) 70%, transparent);
|
||
}
|
||
|
||
/* Why: one-shot sidebar education cards need stronger separation than
|
||
worktree-sidebar-accent, which sits almost on top of the sidebar fill. */
|
||
.worktree-sidebar-notice-card {
|
||
position: relative;
|
||
border: 1px solid
|
||
color-mix(in srgb, var(--worktree-sidebar-foreground) 24%, var(--worktree-sidebar-border));
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 9%, var(--worktree-sidebar));
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--worktree-sidebar-foreground) 8%, transparent),
|
||
0 1px 2px color-mix(in srgb, var(--worktree-sidebar-foreground) 10%, transparent);
|
||
}
|
||
|
||
.dark .worktree-sidebar-notice-card {
|
||
border-color: color-mix(
|
||
in srgb,
|
||
var(--worktree-sidebar-foreground) 32%,
|
||
var(--worktree-sidebar-border)
|
||
);
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 14%, var(--worktree-sidebar));
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--worktree-sidebar-foreground) 12%, transparent),
|
||
0 1px 4px rgb(0 0 0 / 0.28);
|
||
}
|
||
|
||
/* Why: the project-order notice sits directly under the Projects header; a
|
||
caret aligned to that label reads as a callout instead of a random card. */
|
||
.worktree-sidebar-notice-card--to-section-title::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -5px;
|
||
left: 1.75rem;
|
||
width: 10px;
|
||
height: 10px;
|
||
transform: rotate(45deg);
|
||
border-left: 1px solid
|
||
color-mix(in srgb, var(--worktree-sidebar-foreground) 24%, var(--worktree-sidebar-border));
|
||
border-top: 1px solid
|
||
color-mix(in srgb, var(--worktree-sidebar-foreground) 24%, var(--worktree-sidebar-border));
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 9%, var(--worktree-sidebar));
|
||
}
|
||
|
||
.dark .worktree-sidebar-notice-card--to-section-title::before {
|
||
border-left-color: color-mix(
|
||
in srgb,
|
||
var(--worktree-sidebar-foreground) 32%,
|
||
var(--worktree-sidebar-border)
|
||
);
|
||
border-top-color: color-mix(
|
||
in srgb,
|
||
var(--worktree-sidebar-foreground) 32%,
|
||
var(--worktree-sidebar-border)
|
||
);
|
||
background: color-mix(in srgb, var(--worktree-sidebar-foreground) 14%, var(--worktree-sidebar));
|
||
}
|
||
|
||
/* Why: one-shot status-bar callout is portaled fixed above the meters.
|
||
Opaque popover surface (not glass) so titlebar/terminal chrome cannot
|
||
bleed through; use design tokens + the documented floating elevation. */
|
||
.status-bar-change-notice-card {
|
||
border: 1px solid var(--border);
|
||
background: var(--popover);
|
||
color: var(--popover-foreground);
|
||
/* STYLEGUIDE: floating elevation for popovers that escape the editor surface. */
|
||
box-shadow: var(--shadow-floating);
|
||
}
|
||
|
||
/* Why: caret points down at the usage meters this notice explains. */
|
||
.status-bar-change-notice-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 1.25rem;
|
||
bottom: -6px;
|
||
width: 12px;
|
||
height: 12px;
|
||
transform: rotate(45deg);
|
||
border-right: 1px solid var(--border);
|
||
border-bottom: 1px solid var(--border);
|
||
background: var(--popover);
|
||
}
|
||
|
||
/* Why: the first-run Mobile Emulator intro sits directly under that menu row;
|
||
an upward caret aligned to the phone icon reads as a callout, not a footer. */
|
||
.mobile-emulator-tab-intro-callout--menu {
|
||
position: relative;
|
||
}
|
||
|
||
.mobile-emulator-tab-intro-callout--menu::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -7px;
|
||
left: 0.8125rem;
|
||
border-style: solid;
|
||
border-width: 0 7px 7px;
|
||
/* Why: use foreground + border to create a subtle shadow that adapts to theme */
|
||
border-color: transparent transparent color-mix(in srgb, var(--foreground) 10%, var(--border))
|
||
transparent;
|
||
}
|
||
|
||
.mobile-emulator-tab-intro-callout--menu::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: -6px;
|
||
left: 0.8125rem;
|
||
border-style: solid;
|
||
border-width: 0 6px 6px;
|
||
border-color: transparent transparent var(--muted) transparent;
|
||
}
|
||
|
||
.dark .mobile-emulator-tab-intro-callout--menu::before {
|
||
/* Why: use foreground + border to create a subtle shadow that adapts to theme */
|
||
border-bottom-color: color-mix(in srgb, var(--foreground) 12%, var(--border));
|
||
}
|
||
|
||
.dark .mobile-emulator-tab-intro-callout--menu::after {
|
||
border-bottom-color: color-mix(in srgb, var(--accent) 80%, transparent);
|
||
}
|
||
|
||
/* Why: anchor the optional setup card over the emulator without blurring the
|
||
device preview, which read as a rendering glitch. */
|
||
.mobile-emulator-agent-setup-guide-scrim {
|
||
background: linear-gradient(
|
||
to top,
|
||
color-mix(in srgb, var(--background) 92%, transparent),
|
||
transparent
|
||
);
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Why: the detected command is editable, but a pure app-background fill reads
|
||
like a black box in dark mode and overpowers the prompt card. */
|
||
.setup-script-prompt-command {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 2%, var(--sidebar));
|
||
}
|
||
|
||
.dark .setup-script-prompt-command {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 8%, var(--sidebar));
|
||
}
|
||
|
||
.worktree-agent-lineage-parent-row {
|
||
/* Why: inline orchestration trees have two chevron levels; the manager row
|
||
needs a structural cue that is quieter than the workspace card chrome. */
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 3%, var(--sidebar));
|
||
box-shadow:
|
||
inset 2px 0 0 color-mix(in srgb, var(--sidebar-foreground) 38%, transparent),
|
||
inset 0 0 0 1px color-mix(in srgb, var(--sidebar-foreground) 10%, transparent);
|
||
}
|
||
|
||
.worktree-agent-lineage-parent-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 6%, var(--sidebar));
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-parent-row {
|
||
background: color-mix(in srgb, var(--accent) 22%, var(--sidebar));
|
||
box-shadow:
|
||
inset 2px 0 0 color-mix(in srgb, var(--accent) 58%, transparent),
|
||
inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent);
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-parent-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--accent) 30%, var(--sidebar));
|
||
}
|
||
|
||
.worktree-agent-lineage-child-row {
|
||
position: relative;
|
||
background: color-mix(in srgb, var(--sidebar) 74%, var(--background));
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sidebar-foreground) 7%, transparent);
|
||
}
|
||
|
||
.worktree-agent-lineage-child-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 4%, var(--sidebar));
|
||
}
|
||
|
||
.worktree-agent-lineage-child-row::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0.15rem;
|
||
top: 50%;
|
||
width: 0.35rem;
|
||
border-top: 1px solid color-mix(in srgb, var(--sidebar-foreground) 20%, transparent);
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-child-row {
|
||
background: color-mix(in srgb, var(--accent) 10%, var(--sidebar));
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 16%, transparent);
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-child-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--accent) 18%, var(--sidebar));
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-child-row::before {
|
||
border-top-color: color-mix(in srgb, var(--accent) 42%, transparent);
|
||
}
|
||
|
||
.worktree-agent-lineage-children {
|
||
margin-left: 0.75rem;
|
||
padding-left: 0.25rem;
|
||
border-left: 1px solid color-mix(in srgb, var(--sidebar-foreground) 22%, transparent);
|
||
}
|
||
|
||
.dark .worktree-agent-lineage-children {
|
||
border-left-color: color-mix(in srgb, var(--accent) 28%, transparent);
|
||
}
|
||
|
||
.worktree-agent-send-target-button {
|
||
/* Why: the row remains an ordinary agent row during send-target mode; the
|
||
action itself carries the AI target outline so the clickable affordance
|
||
is explicit without boxing the whole session. */
|
||
border-color: color-mix(in srgb, var(--ai-action-accent) 72%, transparent);
|
||
background: color-mix(in srgb, var(--ai-action-accent) 12%, transparent);
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ai-action-accent) 22%, transparent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.worktree-agent-send-target-button:hover {
|
||
border-color: color-mix(in srgb, var(--ai-action-accent) 88%, transparent);
|
||
background: color-mix(in srgb, var(--ai-action-accent) 18%, transparent);
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ai-action-accent) 34%, transparent);
|
||
}
|
||
|
||
.worktree-agent-send-target-button:focus-visible {
|
||
outline: none;
|
||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring) 50%, transparent);
|
||
}
|
||
|
||
/* Why: the virtualized card row is height-measured; animate the grid track
|
||
instead of explicit height so the reveal stays CSS-owned and interruptible. */
|
||
.compact-agent-expansion-grid {
|
||
display: grid;
|
||
grid-template-rows: 0fr;
|
||
transition: grid-template-rows 180ms cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
.compact-agent-expansion-grid-expanded {
|
||
grid-template-rows: 1fr;
|
||
}
|
||
|
||
/* Why: must stay a compositor-driven CSS animation — a JS clock writing
|
||
per-element transforms blocks the renderer input thread (STA-3328: 41
|
||
spinners ⇒ ~490 style writes/s, keystroke p90 363ms). The component anchors
|
||
each animation's startTime once; steps(12) preserves the retired cadence. */
|
||
@keyframes agent-spinner-rotate {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.agent-working-spinner {
|
||
animation: agent-spinner-rotate 1s steps(12, end) infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.agent-working-spinner {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Why: keyframes run when the content mounts already in its final state. */
|
||
@keyframes compact-agent-expansion-reveal {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-2px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.compact-agent-expansion-grid-expanded .compact-agent-expansion-content {
|
||
animation: compact-agent-expansion-reveal 180ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||
}
|
||
|
||
/* Why: the collapsed summary still uses pill spacing, while expanded compact
|
||
agents read as a quiet tree inside the existing worktree card. */
|
||
.compact-agent-summary-panel {
|
||
padding: 2px;
|
||
border-radius: var(--radius-sm, 0.25rem);
|
||
background: transparent;
|
||
box-shadow: inset 0 0 0 1px transparent;
|
||
transition:
|
||
background 180ms cubic-bezier(0.16, 1, 0.3, 1),
|
||
box-shadow 180ms cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
.compact-agent-summary-panel-expanded {
|
||
background: transparent;
|
||
box-shadow: inset 0 0 0 1px transparent;
|
||
}
|
||
|
||
[data-compact-agent-list='true'] {
|
||
margin-inline-start: -0.5rem;
|
||
width: calc(100% + 0.5rem);
|
||
}
|
||
|
||
/* Why: compact expanded agents live inside an already-framed worktree card.
|
||
Keep the tree legible with indentation and interaction states instead of
|
||
stacking row cards and shaded panels. */
|
||
[data-compact-agent-list='true'] .compact-agent-row.worktree-agent-lineage-parent-row,
|
||
[data-compact-agent-list='true'] .compact-agent-row.worktree-agent-lineage-child-row {
|
||
background: transparent;
|
||
box-shadow: none;
|
||
}
|
||
|
||
[data-compact-agent-list='true'] .compact-agent-row.worktree-agent-lineage-child-row::before {
|
||
content: none;
|
||
}
|
||
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-lineage-parent-row.worktree-agent-row-hover:hover,
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-lineage-child-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 1.25%, transparent);
|
||
}
|
||
|
||
.dark
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-lineage-parent-row.worktree-agent-row-hover:hover,
|
||
.dark
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-lineage-child-row.worktree-agent-row-hover:hover {
|
||
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||
}
|
||
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-row-hover[data-focused-agent-pane='true'] {
|
||
background: color-mix(in srgb, var(--sidebar-foreground) 12%, var(--sidebar-accent));
|
||
}
|
||
|
||
.dark
|
||
[data-compact-agent-list='true']
|
||
.compact-agent-row.worktree-agent-row-hover[data-focused-agent-pane='true'] {
|
||
background: color-mix(in srgb, var(--accent) 70%, transparent);
|
||
}
|
||
|
||
[data-compact-agent-list='true'] .worktree-agent-lineage-children {
|
||
border-left-color: color-mix(in srgb, var(--sidebar-foreground) 16%, transparent);
|
||
}
|
||
|
||
.dark [data-compact-agent-list='true'] .worktree-agent-lineage-children {
|
||
border-left-color: color-mix(in srgb, var(--accent) 20%, transparent);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.compact-agent-expansion-grid {
|
||
transition-duration: 0ms;
|
||
}
|
||
|
||
.compact-agent-expansion-grid-expanded .compact-agent-expansion-content {
|
||
animation-duration: 0ms;
|
||
}
|
||
|
||
.compact-agent-summary-panel {
|
||
transition-duration: 0ms;
|
||
}
|
||
}
|
||
|
||
.scroll-to-current-workspace-reveal-highlight {
|
||
position: relative;
|
||
isolation: isolate;
|
||
overflow: visible;
|
||
}
|
||
|
||
.scroll-to-current-workspace-reveal-highlight::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 30;
|
||
pointer-events: none;
|
||
border: 1.5px solid color-mix(in srgb, var(--terminal-pane-locate) 68%, transparent);
|
||
border-radius: inherit;
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--terminal-pane-locate) 34%, transparent),
|
||
0 0 14px color-mix(in srgb, var(--terminal-pane-locate) 36%, transparent);
|
||
animation: scroll-to-current-workspace-reveal-glow 1.5s ease-out forwards;
|
||
}
|
||
|
||
.scroll-to-current-workspace-reveal-highlight--ai::before {
|
||
border-color: color-mix(in srgb, var(--ai-action-accent) 68%, transparent);
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--ai-action-accent) 34%, transparent),
|
||
0 0 14px color-mix(in srgb, var(--ai-action-accent) 36%, transparent);
|
||
}
|
||
|
||
@keyframes scroll-to-current-workspace-reveal-glow {
|
||
0% {
|
||
opacity: 1;
|
||
transform: scale(0.992);
|
||
}
|
||
|
||
24% {
|
||
opacity: 0.72;
|
||
transform: scale(1);
|
||
}
|
||
|
||
60% {
|
||
opacity: 0.28;
|
||
transform: scale(1.006);
|
||
}
|
||
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(1.006);
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.scroll-to-current-workspace-reveal-highlight::before {
|
||
animation: none;
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
@keyframes settings-shell-enter {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(8px);
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.settings-view-shell {
|
||
animation: settings-shell-enter 180ms ease-out;
|
||
}
|
||
|
||
.repo-icon-emoji-picker .EmojiPickerReact {
|
||
--epr-bg-color: var(--card);
|
||
--epr-category-label-bg-color: var(--card);
|
||
--epr-text-color: var(--card-foreground);
|
||
--epr-search-input-bg-color: var(--input);
|
||
--epr-search-input-bg-color-active: var(--input);
|
||
--epr-search-border-color: var(--border);
|
||
--epr-search-border-color-active: var(--ring);
|
||
--epr-hover-bg-color: var(--accent);
|
||
--epr-highlight-color: var(--ring);
|
||
--epr-picker-border-color: var(--border);
|
||
border: none;
|
||
}
|
||
|
||
/* The library sets `font-family: sans-serif` on every descendant, so inheriting from the root never lands. */
|
||
.repo-icon-emoji-picker .EmojiPickerReact * {
|
||
font-family: var(--font-sans);
|
||
}
|
||
|
||
.repo-icon-emoji-picker .epr-body {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: color-mix(in srgb, var(--muted-foreground, #737373) 34%, transparent) transparent;
|
||
}
|
||
|
||
.collapsible-height-content {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.collapsible-height-content[data-state='open'] {
|
||
animation: collapsible-down 200ms ease-out;
|
||
}
|
||
|
||
.collapsible-height-content[data-state='closed'] {
|
||
animation: collapsible-up 180ms ease-out;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.collapsible-height-content[data-state='open'],
|
||
.collapsible-height-content[data-state='closed'] {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@keyframes collapsible-down {
|
||
from {
|
||
height: 0;
|
||
opacity: 0;
|
||
}
|
||
|
||
to {
|
||
height: var(--radix-collapsible-content-height);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes collapsible-up {
|
||
from {
|
||
height: var(--radix-collapsible-content-height);
|
||
opacity: 1;
|
||
}
|
||
|
||
to {
|
||
height: 0;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.sidebar.collapsed {
|
||
width: 0;
|
||
border-right: none;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Why: the workspace board is offset to the sidebar edge. The generic left
|
||
sheet translates by its full width, which makes an offset board fly in from
|
||
far offscreen; this reveals it from the sidebar boundary instead. */
|
||
.workspace-kanban-sheet-content[data-state='open'] {
|
||
animation-name: workspace-kanban-sheet-in;
|
||
animation-duration: 300ms;
|
||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
.workspace-kanban-sheet-content[data-state='closed'] {
|
||
animation-name: workspace-kanban-sheet-out;
|
||
animation-duration: 200ms;
|
||
animation-timing-function: cubic-bezier(0.7, 0, 0.84, 0);
|
||
}
|
||
|
||
/* Why: this board is attached to the worktree sidebar, not floating above it;
|
||
the generic Sheet elevation casts a haze over the sidebar when left open. */
|
||
.workspace-kanban-sheet-content {
|
||
box-shadow: inset 1px 0 0 color-mix(in srgb, var(--worktree-sidebar-border) 80%, transparent);
|
||
backdrop-filter: none;
|
||
}
|
||
|
||
/* Why: a sidebar-card drag should advertise the board as a possible drop
|
||
surface without fully committing the companion panel until the pointer enters it. */
|
||
.workspace-kanban-sheet-content[data-workspace-board-drag-preview='true'] {
|
||
background: color-mix(in srgb, var(--worktree-sidebar) 88%, var(--background));
|
||
box-shadow:
|
||
inset 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 58%, transparent),
|
||
0 10px 24px color-mix(in srgb, var(--foreground) 12%, transparent);
|
||
}
|
||
|
||
.workspace-kanban-sheet-content[data-workspace-board-drag-preview='true'] > * {
|
||
opacity: 0.42;
|
||
}
|
||
|
||
/* Why: while a card drag previews the board, pulse the toolbar trigger with the
|
||
same ring the drawer uses so it reads as another way to open the board. */
|
||
@keyframes workspace-board-trigger-preview-pulse {
|
||
0% {
|
||
box-shadow: 0 0 0 0 color-mix(in srgb, var(--sidebar-ring) 55%, transparent);
|
||
}
|
||
|
||
70%,
|
||
100% {
|
||
box-shadow: 0 0 0 5px color-mix(in srgb, var(--sidebar-ring) 0%, transparent);
|
||
}
|
||
}
|
||
|
||
[data-workspace-board-trigger][data-workspace-board-preview='true'] {
|
||
animation: workspace-board-trigger-preview-pulse 1.4s ease-out infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
[data-workspace-board-trigger][data-workspace-board-preview='true'] {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@keyframes workspace-kanban-sheet-in {
|
||
from {
|
||
clip-path: inset(0 100% 0 0);
|
||
}
|
||
|
||
to {
|
||
clip-path: inset(0 0 0 0);
|
||
}
|
||
}
|
||
|
||
@keyframes workspace-kanban-sheet-out {
|
||
from {
|
||
clip-path: inset(0 0 0 0);
|
||
}
|
||
|
||
to {
|
||
clip-path: inset(0 100% 0 0);
|
||
}
|
||
}
|
||
|
||
/* Why: board-origin cards use pointer drag so Shift+scroll can keep firing;
|
||
these replace the native drag cursor and ghost affordance. */
|
||
[data-workspace-board-pointer-draggable='true'] {
|
||
cursor: grab;
|
||
}
|
||
|
||
[data-workspace-board-card-pointer-dragging='true'] {
|
||
outline: 1px solid var(--sidebar-ring);
|
||
outline-offset: 1px;
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 36%, transparent);
|
||
}
|
||
|
||
[data-workspace-board-card-pointer-dragging='true'],
|
||
[data-workspace-board-card-pointer-dragging='true'] *,
|
||
:root[data-workspace-board-pointer-dragging] * {
|
||
cursor: grabbing !important;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'] {
|
||
z-index: 2147483647;
|
||
overflow: visible;
|
||
border-radius: var(--radius);
|
||
outline: 1px solid var(--sidebar-ring);
|
||
outline-offset: 1px;
|
||
box-shadow: 0 10px 24px color-mix(in srgb, var(--foreground) 16%, transparent);
|
||
opacity: 0.96;
|
||
will-change: transform;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'][data-workspace-board-card-drag-stack='true'] {
|
||
isolation: isolate;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-card='true'] {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'][data-workspace-board-card-drag-stack='true']::before,
|
||
[data-workspace-board-card-drag-preview='true'][data-workspace-board-card-drag-stack='true']::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: -1;
|
||
border-radius: var(--radius);
|
||
border: 1px solid color-mix(in srgb, var(--sidebar-ring) 44%, transparent);
|
||
background: var(--sidebar);
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'][data-workspace-board-card-drag-stack='true']::before {
|
||
transform: translate(4px, 4px);
|
||
opacity: 0.72;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'][data-workspace-board-card-drag-stack='true']::after {
|
||
transform: translate(8px, 8px);
|
||
opacity: 0.44;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-preview='true'] * {
|
||
cursor: grabbing !important;
|
||
}
|
||
|
||
[data-worktree-sidebar-drag-preview='true'] {
|
||
z-index: 2147483000;
|
||
overflow: visible;
|
||
border-radius: var(--radius);
|
||
opacity: 0.96;
|
||
filter: drop-shadow(0 10px 24px color-mix(in srgb, var(--foreground) 18%, transparent));
|
||
will-change: transform;
|
||
}
|
||
|
||
[data-worktree-sidebar-drag-preview='true'] * {
|
||
cursor: grabbing !important;
|
||
}
|
||
|
||
[data-worktree-sidebar-drag-count='true'] {
|
||
position: absolute;
|
||
top: -6px;
|
||
right: 8px;
|
||
display: inline-flex;
|
||
min-width: 18px;
|
||
height: 18px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 9999px;
|
||
background: var(--sidebar-ring);
|
||
color: var(--sidebar);
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
box-shadow: 0 0 0 2px var(--sidebar);
|
||
}
|
||
|
||
[data-workspace-board-card-drop-indicator='true'] {
|
||
z-index: 2147483646;
|
||
height: 3px;
|
||
border-radius: 9999px;
|
||
background: var(--sidebar-ring);
|
||
box-shadow:
|
||
0 0 0 1px color-mix(in srgb, var(--sidebar-ring) 24%, transparent),
|
||
0 8px 20px color-mix(in srgb, var(--sidebar-ring) 36%, transparent);
|
||
opacity: 0;
|
||
transition:
|
||
transform 120ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
||
width 120ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
||
opacity 80ms ease-out;
|
||
will-change: transform, width, opacity;
|
||
}
|
||
|
||
[data-workspace-board-card-drop-indicator='true']::before,
|
||
[data-workspace-board-card-drop-indicator='true']::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 9999px;
|
||
background: var(--sidebar-ring);
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
[data-workspace-board-card-drop-indicator='true']::before {
|
||
left: -1px;
|
||
}
|
||
|
||
[data-workspace-board-card-drop-indicator='true']::after {
|
||
right: -1px;
|
||
}
|
||
|
||
[data-workspace-board-card-drag-count='true'] {
|
||
position: absolute;
|
||
top: -8px;
|
||
right: -8px;
|
||
z-index: 2;
|
||
display: flex;
|
||
min-width: 18px;
|
||
height: 18px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 9999px;
|
||
border: 1px solid var(--sidebar-border);
|
||
background: var(--foreground);
|
||
color: var(--background);
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
}
|
||
|
||
.sidebar-header {
|
||
padding: 12px 16px 8px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.worktree-list {
|
||
padding: 0 4px;
|
||
}
|
||
|
||
.worktree-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
padding: 8px 12px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
transition: background 120ms;
|
||
}
|
||
|
||
.worktree-item:hover {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.worktree-item.active {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.worktree-branch {
|
||
font-size: 13px;
|
||
font-family: inherit;
|
||
color: var(--foreground);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.worktree-path {
|
||
font-size: 11px;
|
||
color: var(--muted-foreground);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* ── Misc ────────────────────────────────────────────── */
|
||
|
||
.number-input-clean {
|
||
appearance: textfield;
|
||
}
|
||
|
||
.number-input-clean::-webkit-outer-spin-button,
|
||
.number-input-clean::-webkit-inner-spin-button {
|
||
-webkit-appearance: none;
|
||
margin: 0;
|
||
}
|
||
|
||
/* ── Landing ─────────────────────────────────────────── */
|
||
|
||
.landing {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--background);
|
||
}
|
||
|
||
.landing-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.landing-title {
|
||
font-size: 48px;
|
||
font-weight: 700;
|
||
color: var(--muted-foreground);
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.landing-action {
|
||
background: var(--secondary);
|
||
border: 1px solid var(--border);
|
||
color: var(--foreground);
|
||
font-family: inherit;
|
||
font-size: 14px;
|
||
padding: 8px 24px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
transition: background 150ms;
|
||
}
|
||
|
||
.landing-action:hover {
|
||
background: var(--accent);
|
||
}
|
||
|
||
.landing-hint {
|
||
font-size: 12px;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
/* ── fix: ensure Monaco find widget layout is not reversed ── */
|
||
.monaco-editor .find-widget,
|
||
.monaco-editor .find-widget .monaco-inputbox,
|
||
.monaco-editor .find-widget .matchesCount {
|
||
direction: ltr !important;
|
||
flex-direction: row !important;
|
||
}
|
||
|
||
.monaco-editor .find-widget .button,
|
||
.monaco-editor .find-widget .monaco-button,
|
||
.monaco-editor .find-widget .monaco-find-input .controls .button {
|
||
display: inline-flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
padding: 0 !important;
|
||
width: 20px !important;
|
||
height: 20px !important;
|
||
line-height: normal !important;
|
||
box-sizing: border-box !important;
|
||
vertical-align: middle !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
transition: none !important;
|
||
}
|
||
|
||
/* Why: the expand/collapse toggle (the chevron on the far left) is positioned
|
||
absolutely and relies on Monaco's default height:100% to vertically center
|
||
its icon over the find row. The generic .button override above forces a fixed
|
||
20px height, which pins the chevron to the top. Restore Monaco's full-height
|
||
18px toggle so the chevron centers like the stock find widget. */
|
||
.monaco-editor .find-widget .button.toggle {
|
||
width: 18px !important;
|
||
height: 100% !important;
|
||
top: 0 !important;
|
||
}
|
||
|
||
.monaco-editor .find-widget .button > .codicon,
|
||
.monaco-editor .find-widget .button > i {
|
||
display: flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
}
|
||
|
||
.monaco-editor .monaco-search-result-highlight {
|
||
background: color-mix(in srgb, var(--warning, #f59e0b) 34%, transparent);
|
||
border-radius: 3px;
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warning, #f59e0b) 52%, transparent);
|
||
}
|
||
|
||
.monaco-editor .monaco-markdown-doc-link {
|
||
color: #58a6ff !important;
|
||
text-decoration: underline;
|
||
text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
|
||
text-underline-offset: 2px;
|
||
}
|
||
|
||
/* Why: Monaco's faded scrollbar uses the class `invisible`, which collides with
|
||
Tailwind's `.invisible` utility (visibility: hidden) — so also restore visibility. */
|
||
.monaco-editor
|
||
.reference-zone-widget
|
||
.monaco-scrollable-element
|
||
> .scrollbar.vertical.invisible.fade {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* Why: Monaco leaves the peek preview container `inline-block` and only sizes the
|
||
editor child, so the container shrink-wraps the child in BOTH axes while the
|
||
inherited automaticLayout ResizeObserver measures the container — a feedback
|
||
loop that ratchets the preview to blank at fractional zoom/DPI (issue #7405).
|
||
Pinning both dimensions to the splitview slot severs the loop. */
|
||
.monaco-editor .reference-zone-widget .preview.inline {
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
|
||
/* ── Git conflict decorations ───────────────────────────────────── */
|
||
|
||
.monaco-editor .orca-conflict-marker-line {
|
||
background: color-mix(in srgb, var(--destructive) 12%, transparent);
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-section-line {
|
||
border-left: 3px solid transparent;
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-current-line {
|
||
background: color-mix(in srgb, var(--git-decoration-modified) 12%, transparent);
|
||
border-left-color: color-mix(in srgb, var(--git-decoration-modified) 72%, transparent);
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-base-line {
|
||
background: color-mix(in srgb, var(--muted-foreground) 7%, transparent);
|
||
border-left-color: color-mix(in srgb, var(--muted-foreground) 36%, transparent);
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-incoming-line {
|
||
background: color-mix(in srgb, var(--git-decoration-renamed) 11%, transparent);
|
||
border-left-color: color-mix(in srgb, var(--git-decoration-renamed) 68%, transparent);
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-line-decoration {
|
||
width: 4px !important;
|
||
margin-left: 3px;
|
||
background: var(--destructive);
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-margin {
|
||
background: color-mix(in srgb, var(--destructive) 8%, transparent);
|
||
}
|
||
|
||
.monaco-editor .orca-conflict-marker-label {
|
||
color: var(--destructive);
|
||
font-family: var(--font-sans, system-ui, sans-serif);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
font-style: normal;
|
||
opacity: 0.92;
|
||
}
|
||
|
||
/* Why: Monaco's find-widget button tooltips (Close, Find in Selection, etc.)
|
||
render inside a `.context-view` wrapper → `.workbench-hover-container` →
|
||
`.workbench-hover.compact`, positioned above the target button. The wrapper's
|
||
bounding box (z-index 2576, `pointer-events: auto`) overlaps the button hit
|
||
area, so as the tooltip appears it fires `mouseleave` on the button → 200ms
|
||
debounce → hover disposes → mouse is again "over" the button → `mouseover`
|
||
re-fires a new tooltip → loop. The visible effect is an infinitely flashing
|
||
tooltip that also blocks clicks on the button beneath it. The overlap is
|
||
only reliably reproducible when the right sidebar is open, because the find
|
||
widget's x-position shifts the wrapper onto the button rect; with the
|
||
sidebar closed the wrapper often lands slightly offset and doesn't intercept.
|
||
|
||
These are compact tooltips (`HoverStyle.Pointer` → `.workbench-hover.compact`)
|
||
configured with `hideOnHover: true`, so the CompositeMouseTracker only tracks
|
||
the target element — not the wrapper. Making the entire wrapper chain
|
||
non-interactive breaks the overlap feedback loop without affecting tracker
|
||
logic, keyboard focus traps, or interactive markdown hovers (which are never
|
||
`.compact`). The `:has()` selector ensures we only disable pointer events on
|
||
context-views that actually contain a compact hover, leaving menus, dropdown
|
||
popups and other context-view consumers untouched. */
|
||
.context-view:has(> .workbench-hover-container > .workbench-hover.compact),
|
||
.workbench-hover-container:has(> .workbench-hover.compact) {
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Why: compact hovers for simple label strings (e.g. "Find in Selection (⌥⌘L)")
|
||
get `white-space: pre-wrap` inline-set by Monaco, so when the tooltip lands
|
||
near the viewport edge its max-width clamps and the label wraps to 2–3 lines,
|
||
pushing the tooltip down over the find-widget buttons. These labels are
|
||
single-line by design; forcing nowrap keeps the tooltip on one line and lets
|
||
the positioning logic flip/shift as needed to fit. Only applies to compact
|
||
hovers — markdown hovers still wrap normally. */
|
||
.workbench-hover.compact .hover-contents {
|
||
white-space: nowrap !important;
|
||
}
|
||
|
||
/* ── Update card animations ───────────────────────────────────────── */
|
||
|
||
@keyframes update-card-enter {
|
||
from {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes update-card-exit {
|
||
from {
|
||
opacity: 1;
|
||
}
|
||
to {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.animate-update-card-enter {
|
||
animation: update-card-enter 200ms ease-out both;
|
||
}
|
||
|
||
/* Why: `skills update` reports no parseable progress, so the skill-update bar is
|
||
deliberately indeterminate rather than a faked percentage. */
|
||
@keyframes skill-update-slide {
|
||
from {
|
||
transform: translateX(-100%);
|
||
}
|
||
to {
|
||
transform: translateX(365%);
|
||
}
|
||
}
|
||
|
||
.animate-update-card-exit {
|
||
animation: update-card-exit 150ms ease-in both;
|
||
}
|
||
|
||
/* Waveform bars in the FeatureTipsModal voice tip — purely decorative. */
|
||
@keyframes waveform {
|
||
0%,
|
||
100% {
|
||
transform: scaleY(0.4);
|
||
opacity: 0.6;
|
||
}
|
||
50% {
|
||
transform: scaleY(1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.animate-waveform {
|
||
animation: waveform 1.4s ease-in-out infinite;
|
||
transform-origin: center;
|
||
}
|
||
|
||
/* CLI feature tip — terminal cursor inside the agent-control command. */
|
||
@keyframes cli-tip-caret {
|
||
0%,
|
||
45% {
|
||
opacity: 1;
|
||
}
|
||
46%,
|
||
100% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.animate-cli-tip-caret {
|
||
animation: cli-tip-caret 0.9s steps(1, end) infinite;
|
||
}
|
||
|
||
@keyframes cli-tip-command-line {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(2px);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.animate-cli-tip-command-line {
|
||
animation: cli-tip-command-line 160ms ease-out both;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.animate-cli-tip-caret {
|
||
animation: none;
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
|
||
.animate-cli-tip-command-line {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
.cli-tip-orchestration-frame .feature-wall-orch-stage {
|
||
--feature-wall-workspace-title-size: 11.5px;
|
||
--feature-wall-agent-message-size: 10.5px;
|
||
--feature-wall-agent-row-gap: 5px;
|
||
--feature-wall-agent-status-col: 10px;
|
||
--feature-wall-agent-icon-col: 12px;
|
||
--feature-wall-agent-status-box: 10px;
|
||
--feature-wall-agent-status-icon: 9px;
|
||
--feature-wall-agent-icon-box: 12px;
|
||
--feature-wall-child-indent: 22px;
|
||
align-content: start !important;
|
||
padding-top: 10px !important;
|
||
padding-left: 12px !important;
|
||
padding-right: 52px !important;
|
||
}
|
||
|
||
.cli-tip-orchestration-frame .feature-wall-agent-icon svg {
|
||
height: 11px;
|
||
width: 11px;
|
||
}
|
||
|
||
.cli-tip-orchestration-frame .feature-wall-agent-status [aria-label] {
|
||
height: 10px;
|
||
width: 10px;
|
||
}
|
||
|
||
.cli-tip-orchestration-frame .feature-wall-agent-status [aria-label] > span {
|
||
height: 6px;
|
||
width: 6px;
|
||
border-width: 1.5px;
|
||
}
|
||
|
||
/* Cmd+J feature tip — JS-driven sequence (keypress → palette → typing → results).
|
||
The keypress press-down and palette entrance are handled inline by React state
|
||
transitions; this file only defines the caret blink and the results fade-in. */
|
||
|
||
/* Blinking caret for the simulated command-palette input while "typing". */
|
||
@keyframes cmd-j-tip-caret {
|
||
0%,
|
||
100% {
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.animate-cmd-j-tip-caret {
|
||
animation: cmd-j-tip-caret 1s step-end infinite;
|
||
}
|
||
|
||
/* Worktree rows fade in when the palette opens — mirrors the real palette,
|
||
which shows recent worktrees on empty query and filters synchronously. */
|
||
@keyframes cmd-j-tip-result-in {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(2px);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.animate-cmd-j-tip-result-in {
|
||
animation: cmd-j-tip-result-in 120ms ease-out both;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.animate-cmd-j-tip-caret {
|
||
animation: none;
|
||
opacity: 1;
|
||
}
|
||
|
||
.animate-cmd-j-tip-result-in {
|
||
animation: none;
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
/* ── Feature wall: agents-orchestration animations ───────────────── */
|
||
|
||
/* Why: drives the supported-agents marquee on page 1 of the agents tile.
|
||
Track is duplicated in the DOM so a -50% translate loops seamlessly. */
|
||
@keyframes feature-wall-marquee-flow {
|
||
0% {
|
||
transform: translateX(0);
|
||
}
|
||
100% {
|
||
transform: translateX(-50%);
|
||
}
|
||
}
|
||
|
||
.feature-wall-marquee-track {
|
||
animation: feature-wall-marquee-flow 36s linear infinite;
|
||
}
|
||
|
||
/* Shared click pulse for feature-wall fake cursors. Matches the Editor tour
|
||
ring so every animated mouse click reads as the same interaction. */
|
||
.feature-wall-click-ring {
|
||
animation: feature-wall-click-ring 460ms ease-out forwards;
|
||
}
|
||
|
||
@keyframes feature-wall-click-ring {
|
||
0% {
|
||
transform: scale(0.4);
|
||
opacity: 0.9;
|
||
}
|
||
100% {
|
||
transform: scale(1.4);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* Why: smooth reveal for the Tasks-tile workspace card so it eases in once the
|
||
workspace flips from "Creating" to "Ready" instead of popping in. */
|
||
@keyframes tasks-workspace-in {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(4px);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
/* Why: orchestrator → child connector arrows. Faint dashed strokes drawn in
|
||
JS so endpoints follow the real card geometry as cards animate between
|
||
phase 1 (parent + nested child) and phase 2 (parent + spawned peer). */
|
||
.feature-wall-orch-arrows {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
transition: opacity 280ms ease;
|
||
}
|
||
.feature-wall-orch-arrows[data-fading='true'] {
|
||
opacity: 0;
|
||
}
|
||
.feature-wall-orch-arrows path {
|
||
fill: none;
|
||
stroke: color-mix(in srgb, var(--foreground) 32%, transparent);
|
||
stroke-width: 1.5;
|
||
stroke-dasharray: 4 4;
|
||
}
|
||
|
||
/* Message envelope that travels along the dashed connector. Position is
|
||
driven by `offset-path` so the bubble visibly follows the same shape the
|
||
connector drew. */
|
||
.feature-wall-bubble {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 3;
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 999px;
|
||
background: var(--foreground);
|
||
color: var(--background);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow:
|
||
0 6px 16px rgba(24, 24, 27, 0.22),
|
||
0 1px 2px rgba(24, 24, 27, 0.12);
|
||
opacity: 0;
|
||
offset-rotate: 0deg;
|
||
offset-distance: 0%;
|
||
pointer-events: none;
|
||
will-change: offset-distance, opacity, transform;
|
||
transform: translate(-12px, -12px) scale(0.7);
|
||
}
|
||
.feature-wall-bubble svg {
|
||
width: 13px;
|
||
height: 13px;
|
||
}
|
||
.feature-wall-bubble.in-flight {
|
||
opacity: 1;
|
||
offset-distance: 100%;
|
||
transform: translate(-12px, -12px) scale(1);
|
||
transition:
|
||
offset-distance 1600ms cubic-bezier(0.25, 0.6, 0.25, 1),
|
||
transform 280ms ease,
|
||
opacity 280ms ease;
|
||
}
|
||
.feature-wall-bubble.landed {
|
||
opacity: 0;
|
||
offset-distance: 100%;
|
||
transform: translate(-12px, -12px) scale(0.85);
|
||
transition: opacity 320ms ease 80ms;
|
||
}
|
||
|
||
/* When a message just lands, briefly tint the recipient line so the new
|
||
text reads as a fresh inbound message rather than ambient text. */
|
||
@keyframes feature-wall-msg-pop {
|
||
0% {
|
||
color: rgb(180 83 9);
|
||
}
|
||
70% {
|
||
color: rgb(180 83 9);
|
||
}
|
||
100% {
|
||
color: var(--foreground);
|
||
}
|
||
}
|
||
.feature-wall-msg-received {
|
||
animation: feature-wall-msg-pop 1100ms ease-out;
|
||
}
|
||
|
||
/* Children container fades in once the orchestrator finishes creating. */
|
||
.feature-wall-children-wrapper {
|
||
opacity: 0;
|
||
transform: translateY(6px);
|
||
transition:
|
||
opacity 320ms ease,
|
||
transform 320ms ease;
|
||
}
|
||
.feature-wall-children-wrapper[data-visible='true'] {
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
|
||
@keyframes feature-wall-child-card-in {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(4px);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
.feature-wall-child-card-shell {
|
||
animation: feature-wall-child-card-in 260ms ease-out both;
|
||
}
|
||
|
||
/* Each child agent row stays collapsed until *its* dispatch bubble lands on
|
||
it, so the child workspace visibly gains an agent as work arrives. */
|
||
.feature-wall-spawn-row {
|
||
overflow: hidden;
|
||
opacity: 1;
|
||
max-height: 60px;
|
||
transform: none;
|
||
transition:
|
||
opacity 320ms ease 80ms,
|
||
transform 320ms ease 80ms,
|
||
max-height 320ms cubic-bezier(0.4, 0, 0.2, 1) 80ms,
|
||
margin 320ms cubic-bezier(0.4, 0, 0.2, 1) 80ms,
|
||
padding 320ms cubic-bezier(0.4, 0, 0.2, 1) 80ms;
|
||
}
|
||
.feature-wall-spawn-row[data-pending='true'] {
|
||
opacity: 0;
|
||
transform: translateY(2px);
|
||
pointer-events: none;
|
||
max-height: 0;
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
padding-top: 0;
|
||
padding-bottom: 0;
|
||
transition:
|
||
opacity 220ms ease,
|
||
transform 220ms ease,
|
||
max-height 320ms cubic-bezier(0.4, 0, 0.2, 1),
|
||
margin 320ms cubic-bezier(0.4, 0, 0.2, 1),
|
||
padding 320ms cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
/* Brief one-shot pulse when the Codex limits popover swaps accounts and the
|
||
"0% used" label flashes from red to green. */
|
||
@keyframes feature-wall-meta-pulse {
|
||
0% {
|
||
color: rgb(220 38 38);
|
||
transform: scale(1);
|
||
}
|
||
20% {
|
||
color: rgb(22 163 74);
|
||
transform: scale(1.08);
|
||
}
|
||
100% {
|
||
color: rgb(22 163 74);
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
.feature-wall-meta-pulse {
|
||
color: rgb(22 163 74);
|
||
font-weight: 600;
|
||
animation: feature-wall-meta-pulse 1100ms ease-out 1;
|
||
}
|
||
|
||
.orca-contextual-tour-target-rings {
|
||
border-radius: 9999px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.orca-contextual-tour-target-rings::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -2px;
|
||
border: 1px solid color-mix(in srgb, var(--ring) 72%, transparent);
|
||
border-radius: inherit;
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ring) 18%, transparent);
|
||
opacity: 0;
|
||
transform-origin: center;
|
||
animation: contextual-tour-target-ring 1.8s ease-out infinite;
|
||
}
|
||
|
||
@keyframes contextual-tour-target-ring {
|
||
0% {
|
||
opacity: 0.72;
|
||
transform: scale(0.68);
|
||
}
|
||
72%,
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(2.4);
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.orca-contextual-tour-target-rings::before {
|
||
animation: none;
|
||
opacity: 0.72;
|
||
transform: scale(1.28);
|
||
}
|
||
}
|
||
|
||
/* ── Diff comment decorations ────────────────────────────────────── */
|
||
|
||
/* Why: the "+" button is appended into Monaco's editor DOM node by
|
||
useDiffCommentDecorator. Keep it visually subtle until hovered so it does
|
||
not distract from the diff itself. Left position overlaps the glyph margin
|
||
so the affordance reads as "add comment on this line". */
|
||
.orca-diff-comment-add-btn {
|
||
position: absolute;
|
||
left: 4px;
|
||
width: 18px;
|
||
height: 18px;
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0;
|
||
border: 1px solid color-mix(in srgb, var(--foreground) 22%, var(--border));
|
||
border-radius: 4px;
|
||
background: color-mix(in srgb, var(--foreground) 5%, var(--editor-surface));
|
||
color: color-mix(in srgb, var(--foreground) 78%, var(--muted-foreground));
|
||
cursor: pointer;
|
||
z-index: 5;
|
||
opacity: 1;
|
||
box-shadow: 0 1px 2px color-mix(in srgb, var(--foreground) 12%, transparent);
|
||
transition:
|
||
color 100ms ease,
|
||
background-color 100ms ease,
|
||
border-color 100ms ease,
|
||
box-shadow 100ms ease;
|
||
}
|
||
|
||
.orca-diff-comment-add-btn:hover {
|
||
color: var(--primary);
|
||
border-color: color-mix(in srgb, var(--primary) 52%, var(--border));
|
||
background: color-mix(in srgb, var(--primary) 12%, var(--background));
|
||
box-shadow: 0 1px 4px color-mix(in srgb, var(--foreground) 16%, transparent);
|
||
}
|
||
|
||
.orca-diff-comment-add-btn:focus-visible {
|
||
outline: 2px solid var(--primary);
|
||
outline-offset: 1px;
|
||
}
|
||
|
||
.orca-diff-comment-range-highlight {
|
||
background: rgba(59, 130, 246, 0.14);
|
||
}
|
||
|
||
.dark .orca-diff-comment-range-highlight {
|
||
background: rgba(96, 165, 250, 0.18);
|
||
}
|
||
|
||
.orca-diff-comment-inline {
|
||
width: 100%;
|
||
/* Why: Monaco's view-zone node already starts after the diff gutter. Avoid
|
||
adding a second left inset so saved notes sit flush with the code column. */
|
||
padding: 4px 24px 6px 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.orca-diff-comment-inline > .orca-diff-comment-card {
|
||
max-width: 420px;
|
||
}
|
||
|
||
/* Why: the saved note used to blend into the editor background because it
|
||
reused the same muted/background mix the editor itself uses. Stay
|
||
grayscale to match the rest of the UI, but lift the card off the editor
|
||
with a foreground-tinted fill, a solid left accent bar, a soft shadow,
|
||
and a bolded uppercase meta label. Dark mode uses 6% (vs light's 5%)
|
||
to produce roughly the same absolute luminance delta above its editor
|
||
surface — the foreground color is different (white vs black) so equal
|
||
percentages would not give equal contrast. Override lives in `.dark`. */
|
||
.orca-diff-comment-card {
|
||
position: relative;
|
||
/* Why: Monaco's view-lines overlay (data-mprt=8) is positioned absolutely on
|
||
top of view-zone DOM. Without an explicit z-index here, clicks on
|
||
interior controls (Cancel/Save in edit mode, the textarea, etc.) hit the
|
||
overlay first and never reach our buttons. Lifting the whole card creates
|
||
a stacking context that keeps every nested control clickable. */
|
||
z-index: 1;
|
||
display: flex;
|
||
gap: 12px;
|
||
border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border));
|
||
border-radius: 8px;
|
||
background: var(--background);
|
||
padding: 12px 14px;
|
||
font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
|
||
box-shadow:
|
||
0 1px 3px rgba(0, 0, 0, 0.04),
|
||
0 4px 12px rgba(0, 0, 0, 0.03);
|
||
transition:
|
||
border-color 150ms ease,
|
||
box-shadow 150ms ease,
|
||
background-color 150ms ease;
|
||
}
|
||
|
||
.dark .orca-diff-comment-card {
|
||
border-color: color-mix(in srgb, var(--foreground) 12%, var(--border));
|
||
background: color-mix(in srgb, var(--foreground) 2%, var(--editor-surface));
|
||
box-shadow:
|
||
0 1px 3px rgba(0, 0, 0, 0.2),
|
||
0 4px 16px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
/* Content Column */
|
||
.orca-diff-comment-content-col {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
|
||
/* Header Row */
|
||
.orca-diff-comment-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Meta Group (Name, line, timestamp) */
|
||
.orca-diff-comment-meta-group {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
font-size: 13px;
|
||
line-height: 1.4;
|
||
font-weight: 600;
|
||
color: var(--foreground);
|
||
}
|
||
|
||
/* Action button pill: segmented control like Notion's Resolve / ... */
|
||
.orca-diff-comment-actions-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
padding: 0 2px;
|
||
border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border));
|
||
border-radius: 6px;
|
||
background: var(--background);
|
||
overflow: hidden;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
.dark .orca-diff-comment-actions-pill {
|
||
border-color: color-mix(in srgb, var(--foreground) 14%, var(--border));
|
||
background: color-mix(in srgb, var(--foreground) 4%, var(--editor-surface));
|
||
}
|
||
|
||
.orca-diff-comment-pill-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 28px;
|
||
padding: 4px 7px;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
color: var(--muted-foreground);
|
||
border: none;
|
||
background: transparent;
|
||
cursor: pointer;
|
||
height: 24px;
|
||
transition:
|
||
background-color 100ms ease,
|
||
color 100ms ease;
|
||
}
|
||
|
||
.orca-diff-comment-edit {
|
||
min-width: 28px;
|
||
height: 24px;
|
||
padding: 4px 7px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.orca-diff-comment-pill-btn:hover {
|
||
background: color-mix(in srgb, var(--foreground) 6%, transparent);
|
||
color: var(--foreground);
|
||
}
|
||
|
||
.orca-diff-comment-pill-btn-danger:hover {
|
||
background: color-mix(in srgb, var(--destructive) 10%, transparent);
|
||
color: var(--destructive);
|
||
}
|
||
|
||
.orca-diff-comment-pill-divider {
|
||
width: 1px;
|
||
height: 12px;
|
||
background: color-mix(in srgb, var(--foreground) 12%, var(--border));
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Quote/Reference block: Warm, gorgeous Notion-like callout */
|
||
.orca-diff-comment-quote {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
margin: 2px 0 4px 0;
|
||
padding-left: 8px;
|
||
border-left: 2px solid color-mix(in srgb, #e7b10a 35%, transparent);
|
||
color: var(--muted-foreground);
|
||
font-size: 12.5px;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.orca-diff-comment-quote-text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Comment Body */
|
||
.orca-diff-comment-body {
|
||
font-size: 13.5px;
|
||
line-height: 1.5;
|
||
color: var(--foreground);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* Popover for adding new comment */
|
||
.orca-diff-comment-popover {
|
||
position: absolute;
|
||
left: 56px;
|
||
right: 24px;
|
||
max-width: 420px;
|
||
z-index: 1000;
|
||
padding: 12px 14px;
|
||
border: 1px solid color-mix(in srgb, var(--foreground) 10%, var(--border));
|
||
border-radius: 8px;
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
box-shadow:
|
||
0 10px 32px rgba(0, 0, 0, 0.12),
|
||
0 4px 12px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.dark .orca-diff-comment-popover {
|
||
border-color: color-mix(in srgb, var(--foreground) 14%, var(--border));
|
||
background: color-mix(in srgb, var(--foreground) 2%, var(--editor-surface));
|
||
box-shadow:
|
||
0 10px 32px rgba(0, 0, 0, 0.3),
|
||
0 4px 16px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.orca-diff-comment-popover-label {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
color: var(--muted-foreground);
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.orca-diff-comment-popover-textarea {
|
||
width: 100%;
|
||
min-height: 56px;
|
||
max-height: 240px;
|
||
overflow-y: auto;
|
||
resize: none;
|
||
padding: 8px 12px;
|
||
border: 1px solid color-mix(in srgb, var(--foreground) 12%, var(--border));
|
||
border-radius: 6px;
|
||
background: var(--background);
|
||
color: var(--foreground);
|
||
font-family: inherit;
|
||
font-size: 13px;
|
||
line-height: 1.45;
|
||
outline: none;
|
||
transition:
|
||
border-color 150ms ease,
|
||
box-shadow 150ms ease;
|
||
}
|
||
|
||
.dark .orca-diff-comment-popover-textarea {
|
||
border-color: color-mix(in srgb, var(--foreground) 10%, var(--border));
|
||
background: color-mix(in srgb, var(--foreground) 3%, var(--editor-surface));
|
||
}
|
||
|
||
.orca-diff-comment-popover-textarea:focus {
|
||
border-color: color-mix(in srgb, var(--ring) 70%, var(--border));
|
||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring) 18%, transparent);
|
||
}
|
||
|
||
.dark .orca-diff-comment-popover-textarea:focus {
|
||
border-color: color-mix(in srgb, var(--ring) 70%, var(--border));
|
||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring) 12%, transparent);
|
||
}
|
||
|
||
.orca-diff-comment-popover-textarea::-webkit-scrollbar {
|
||
width: 12px;
|
||
}
|
||
|
||
.orca-diff-comment-popover-textarea::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.orca-diff-comment-popover-textarea::-webkit-scrollbar-thumb {
|
||
background: color-mix(in srgb, var(--muted-foreground, #737373) 28%, transparent);
|
||
border: 3px solid transparent;
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.orca-diff-comment-popover-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Why: tours need a distinct item surface without highlighting the underlying target. */
|
||
.orca-contextual-tour-panel {
|
||
--contextual-tour-panel-surface: color-mix(in srgb, var(--foreground) 7%, var(--popover));
|
||
--contextual-tour-panel-border: color-mix(in srgb, var(--foreground) 14%, var(--border));
|
||
background: var(--contextual-tour-panel-surface);
|
||
border-color: var(--contextual-tour-panel-border);
|
||
box-shadow:
|
||
inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 10%, transparent),
|
||
0 10px 24px rgba(0, 0, 0, 0.18);
|
||
}
|
||
|
||
.dark .orca-contextual-tour-panel {
|
||
--contextual-tour-panel-surface: color-mix(in srgb, var(--foreground) 10%, var(--popover));
|
||
--contextual-tour-panel-border: color-mix(in srgb, var(--foreground) 16%, var(--border));
|
||
box-shadow:
|
||
inset 0 1px 0 0 color-mix(in srgb, var(--foreground) 5%, transparent),
|
||
0 10px 24px rgba(0, 0, 0, 0.18);
|
||
}
|