| /* Composer — outer container, controls, pickers, tool buttons, send/workspace/branch/streaming. Composer block merged in from tool-output.css (was misplaced there) — issue #546 PR3. Runtime permission prompt styles live in permission-dialog.css. */ |
| .composer { |
| flex: 0 0 auto; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| padding: var(--space-2) var(--space-6) var(--space-2); |
| /* PR-GRAY-CARD-LIFT-5 (WAWQAQ msg `d4277aba` round 3): the composer |
| outer used to paint a vertical white-fade over `--background`, |
| designed for the older parchment shell where the page bg was |
| warm-white. Now that `--background` itself is pure white and the |
| shell is a visibly gray `--surface-canvas`, the composer outer |
| painted itself white-on-white, defeating the gray-plate / white- |
| card lift. Switch to transparent so the gray plate shows around |
| the composer card. */ |
| background: transparent; |
| } |
| |
| .composerActions { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| gap: var(--space-2); |
| margin-top: var(--space-1); |
| padding-top: var(--space-0-5); |
| border-top: 0; |
| } |
| |
| .maka-composer-left-controls, |
| .maka-composer-right-controls { |
| display: flex; |
| align-items: center; |
| /* PR-REFERENCE-PIXEL-8 (WAWQAQ msg `f79de85f` round 8): reference implementation's |
| bundle uses gap values centered on 4-8px (extracted from |
| `globals-UfMzAdiO.css` — 4px is the most common gap, 12px never |
| appears as a toolbar gap). Maka's 12px composer toolbar gap |
| read as too loose vs reference implementation's tighter density. 8px keeps |
| individual controls separable while pulling them into one |
| coherent toolbar group. */ |
| gap: var(--space-1-5); |
| } |
| |
| .maka-composer-left-controls { |
| flex: 0 0 auto; |
| } |
| |
| .maka-composer-right-controls { |
| flex: 0 0 auto; |
| justify-content: flex-end; |
| } |
| |
| .maka-composer-status-slot { |
| min-width: 0; |
| flex: 1 1 auto; |
| color: var(--muted-foreground); |
| font-size: var(--font-size-caption); |
| text-align: center; |
| } |
| |
| /* Unified composer picker trigger — permission Select ([data-slot="select-trigger"]) |
| + both model pickers (active .maka-model-switcher-trigger, empty-state |
| .maka-composer-model-chip) share ONE quiet-chip geometry so the two |
| controls read identical at rest and on hover. */ |
| .maka-composer-left-controls [data-slot="select-trigger"], |
| .maka-composer-right-controls .maka-model-switcher-trigger, |
| .maka-composer-model-chip, |
| .maka-composer-mode-indicator { |
| height: 26px; |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| gap: var(--space-1); |
| padding: 0 var(--space-2); |
| border: var(--border-width-hairline) solid transparent; |
| border-radius: var(--radius-pill); |
| background: transparent; |
| color: var(--foreground-secondary); |
| font-size: var(--font-size-ui); |
| font-weight: var(--font-weight-medium); |
| white-space: nowrap; |
| transition: |
| background-color var(--duration-base) var(--ease-out-strong), |
| color var(--duration-base) var(--ease-out-strong); |
| } |
| .maka-composer-left-controls [data-slot="select-trigger"]:hover, |
| .maka-composer-right-controls .maka-model-switcher-trigger:hover:not(:disabled):not([data-disabled]), |
| .maka-composer-model-chip:hover, |
| .maka-composer-mode-indicator:hover:not(:disabled) { |
| background: var(--state-hover-bg); |
| color: var(--foreground); |
| border-color: transparent; |
| } |
| |
| /* #1433: active-mode mark (Plan/Swarm) uses the unified composer-control |
| geometry above. Its stronger text plus the trailing X communicate both |
| “enabled” and the direct click-to-close action without inventing a smaller |
| chip or hover footprint beside the permission picker. */ |
| .maka-composer-mode-indicator { |
| color: var(--foreground); |
| font-weight: var(--font-weight-semibold); |
| } |
| |
| .maka-composer-mode-indicator:disabled { |
| cursor: not-allowed; |
| color: var(--muted-foreground); |
| } |
| |
| .maka-composer-model-chip { |
| max-width: min(180px, 28vw); |
| } |
| .maka-composer-right-controls .maka-model-switcher { |
| margin: 0; |
| max-width: min(180px, 28vw); |
| } |
| .maka-composer-right-controls .maka-model-switcher-trigger { |
| min-width: 100px; |
| max-width: min(180px, 28vw); |
| } |
| .maka-composer-right-controls .maka-model-switcher-label { |
| display: none; |
| } |
| .maka-composer-right-controls .maka-model-switcher-value { |
| max-width: 128px; |
| color: inherit; |
| font-size: var(--font-size-ui); |
| font-weight: var(--font-weight-medium); |
| } |
| |
| .maka-composer-model-chip-text { |
| min-width: 0; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .maka-composer-provider-mark { |
| width: 16px; |
| height: 16px; |
| flex: 0 0 16px; |
| display: grid; |
| place-items: center; |
| color: inherit; |
| } |
| |
| .maka-composer-provider-mark > svg, |
| .maka-composer-provider-mark > img, |
| .maka-composer-provider-mark > .providerAssetMask { |
| width: 100%; |
| height: 100%; |
| display: block; |
| } |
| |
| .maka-composer-model-status { |
| width: 6px; |
| height: 6px; |
| border-radius: var(--radius-pill); |
| background: var(--destructive); |
| flex: 0 0 auto; |
| } |
| |
| |
| /* ============================================================================ |
| Composer container + input field — single source |
| ---------------------------------------------------------------------------- |
| The composer card (`.maka-composer-inner`) + its textarea + placeholder |
| used to be triplicated across maka-tokens.css (@layer components), |
| styles/tool-output.css, and reference-shell.css — three definitions of the |
| same selector fighting by specificity/layer, plus a duplicated textarea |
| font. Consolidated here as one definition per selector (pinned by |
| composer-container-single-source-contract). |
| |
| Rest / textarea / placeholder values are the exact computed winners — no |
| visual change for those. One deliberate change: tool-output's |
| `.composer .maka-composer-inner::before { content: none }` shared specificity |
| (0,2,1) with — and was imported after — the streaming top-sweep that used to |
| live in chat-header.css (`.maka-composer-inner[data-streaming="true"]::before`), |
| so it was silently suppressing that deliberate streaming indicator. Removing |
| the reset restores the sweep; the matching `::after` reset had no competing |
| rule and was genuinely dead. |
| |
| #546 PR2 relocated that streaming top-sweep here so all `.maka-composer-inner` |
| chrome (rest / focus / streaming state) is in one file; see the |
| `[data-streaming="true"]` block below the card chrome. */ |
| |
| /* The composer card — a calm parchment surface: hairline border + 1px ring |
| at rest, single accent ring on focus. The shared `.agents-parchment-paper- |
| surface` base (reference-shell.css) resets border/bg/box-shadow; this rule |
| re-establishes the composer's own chrome on top of it. */ |
| .composer .maka-composer-inner { |
| position: relative; |
| display: flex; |
| flex-direction: column; |
| gap: var(--space-1-5); |
| width: min(var(--maka-chat-measure), 100%); |
| max-width: var(--maka-chat-measure); |
| margin: 0 auto; |
| margin-inline: auto; |
| box-sizing: border-box; |
| border: var(--border-width-hairline) solid var(--color-border-tertiary); |
| border-radius: var(--radius-modal); |
| padding: var(--space-2) var(--space-3) var(--space-1-5); |
| background-color: var(--agents-content-area-bg); |
| /* Ring alpha is a token so dark mode can strengthen the edge without a |
| second rest rule (composer-container-single-source-contract). In dark |
| the composer and pane share the same bg, and 3.5% of the near-white |
| foreground vanished — the card lost its outline (designer audit P1-6). */ |
| box-shadow: 0 0 0 1px oklch(from var(--foreground) l c h / var(--composer-ring-alpha, 0.035)); |
| transition: |
| border-color var(--duration-emphasized) var(--ease-out-strong), |
| box-shadow var(--duration-emphasized) var(--ease-out-strong); |
| } |
| |
| .composer .maka-composer-inner:focus-within { |
| border-color: oklch(from var(--focus-ring) l c h / 0.45); |
| box-shadow: 0 0 0 var(--focus-ring-width) oklch(from var(--focus-ring) l c h / 0.30); |
| } |
| |
| /* The active transcript and docked card meet at one boundary. Keeping the |
| outer composer's default top padding here exposes an extra strip of panel |
| background after the transcript viewport has already ended. */ |
| .mainColumn:not([data-home-surface="true"]) .composer { |
| padding-top: 0; |
| } |
| |
| /* Docked resting card — hairline only, no wash (PR-COMPOSER-BAND-SEAM). |
| The docked composer rides the same pure-white content surface as the |
| transcript above it (`.composer` outer is transparent; the card fill is |
| `--agents-content-area-bg`, identical to the panel). The earlier |
| "docked-over-stream lift" added a two-layer foreground-derived drop |
| (`0 2px 6px` + `0 10px 22px`) UNDER the resting, unfocused card. Because |
| the card is ~measure-wide and nearly fills the dock, that soft grey drop |
| read as a light-grey band with a boundary line beneath the composer — |
| the "色差" the maintainer flagged — and it appeared only while the |
| composer was NOT focused (focus swaps in the accent ring below), which is |
| exactly the reported "sometimes". The band is not a separate layer, so |
| there is nothing to converge structurally: the fix is to drop the wash and |
| keep AT MOST the card's own hairline ring, so the dock converges cleanly |
| onto the white content surface. Dark mode already carries no resting drop |
| (the one-step tone lift below is its only cue). */ |
| .mainColumn:not([data-home-surface="true"]) .composer .maka-composer-inner:not(:focus-within) { |
| box-shadow: 0 0 0 1px oklch(from var(--foreground) l c h / var(--composer-ring-alpha, 0.035)); |
| } |
| |
| /* Dark mode: separate the card from the panel by ONE monotonic tone step and |
| nothing else. Any line at the boundary that is brighter than the card fill |
| becomes a local luminance peak, which the eye parses as its own object — |
| that is the "third layer". So the card sits one step lighter than the panel |
| (0.24 → 0.29, matching a mainstream dark composer's fill/canvas ratio) and |
| carries NO resting line: no drop (halo), no ring (double frame), no colored |
| border (peak). The panel↔card profile is a single step. (Light mode carries |
| only the card's hairline ring above; there the white panel already |
| separates by tone.) */ |
| .dark .mainColumn:not([data-home-surface="true"]) .composer .maka-composer-inner { |
| background-color: oklch(from var(--agents-content-area-bg) calc(l + 0.05) c h); |
| } |
| |
| /* Resting state carries no boundary line at all — the tone step is the only |
| cue. border-color is zeroed (the 1px width stays to avoid a focus layout |
| jump) and the ring is dropped. Scoped off :focus-within so the accent border |
| + focus ring still land on focus. */ |
| .dark .mainColumn:not([data-home-surface="true"]) .composer .maka-composer-inner:not(:focus-within) { |
| border-color: transparent; |
| box-shadow: none; |
| } |
| |
| /* D6 spectrum #2 (Command Input processing): while the assistant is |
| streaming, a slow top-edge light sweep on the composer shell keeps |
| "working" visible at the exact place the user's attention returns |
| to. 1px line, foreground-derived, infinite = functional status. |
| Relocated from chat-header.css (#546 PR2) so the card's own rest / |
| focus / streaming states live together. */ |
| .maka-composer-inner[data-streaming="true"] { |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .maka-composer-inner[data-streaming="true"]::before { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| right: 0; |
| height: 1px; |
| border-radius: var(--radius-pill); |
| background: linear-gradient( |
| 90deg, |
| transparent, |
| oklch(from var(--status-running) l c h / 0.55) 50%, |
| transparent |
| ); |
| background-size: 200% 100%; |
| animation: maka-processing-sweep 2.2s var(--ease-in-out-strong) infinite; |
| pointer-events: none; |
| } |
| |
| @keyframes maka-processing-sweep { |
| 0% { background-position: 180% 0; } |
| 100% { background-position: -80% 0; } |
| } |
| |
| /* The textarea owns no chrome (the card does). It auto-resizes from |
| --h-composer-min up to 240px (capped in @maka/ui), then scrolls. Font is |
| set explicitly on the element AND its placeholder so neither relies on the |
| fragile `[font: inherit]` chain from the bare-field reset. */ |
| .composer .maka-composer-textarea { |
| display: block; |
| width: 100%; |
| appearance: none; |
| border: none; |
| outline: none; |
| resize: none; |
| overflow-y: auto; |
| background: transparent; |
| color: var(--foreground); |
| font-family: var(--font-default); |
| font-size: var(--font-size-base); |
| line-height: var(--leading-normal); |
| min-height: var(--h-composer-min); |
| max-height: 240px; |
| } |
| |
| .composer .maka-composer-textarea::placeholder { |
| color: var(--muted-foreground); |
| font-family: var(--font-default); |
| font-size: var(--font-size-base); |
| line-height: var(--leading-normal); |
| transition: opacity var(--duration-emphasized) var(--ease-out-strong); |
| } |
| |
| .composer .maka-composer-textarea:focus::placeholder { |
| opacity: var(--opacity-disabled); |
| } |
| |
| /* Composer send button — flat solid action fill. #546: dropped the gradient |
| + inner sheen + drop shadow (the "machined hardware" look read heavy next |
| to the calm parchment container). Flat --action with darken on hover/press |
| keeps it the clear primary CTA without the weight. */ |
| .maka-composer-workspace-row { |
| /* PR-PARCHMENT-HOME-2: must share the new composer card measure |
| (620px) so the "选择工作目录 ▾" pill stays aligned with the card |
| left edge above it. */ |
| width: min(var(--maka-chat-measure), 100%); |
| display: flex; |
| justify-content: flex-start; |
| gap: var(--space-1); |
| box-sizing: border-box; |
| margin: var(--space-2) auto 0; |
| } |
| |
| .maka-composer-workspace-picker { |
| -webkit-app-region: no-drag; |
| min-width: 0; |
| text-align: left; |
| } |
| |
| .maka-composer-workspace-picker svg { |
| flex: 0 0 auto; |
| } |
| |
| .maka-composer-workspace-current { |
| min-width: 0; |
| max-width: 180px; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .maka-composer-branch-picker { |
| -webkit-app-region: no-drag; |
| min-width: 0; |
| text-align: left; |
| } |
| |
| .maka-composer-branch-picker svg { |
| flex: 0 0 auto; |
| } |
| |
| .maka-composer-branch-current { |
| min-width: 0; |
| max-width: 140px; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .maka-composer-branch-empty { |
| padding: var(--space-2) var(--space-2); |
| color: var(--muted-foreground); |
| font-size: var(--font-size-caption); |
| text-align: center; |
| } |
| |
| .maka-composer-branch-check { |
| margin-left: auto; |
| } |
| |
| /* The project catalog is the only scrollable region. Add Project and the |
| deliberately low-emphasis No Project escape hatch stay fixed below it. */ |
| .maka-composer-workspace-menu { |
| min-width: 220px; |
| max-width: min(320px, calc(100vw - var(--space-6))); |
| } |
| .maka-composer-workspace-menu [data-slot="menu-item"] svg { |
| flex: 0 0 auto; |
| } |
| .maka-composer-workspace-menu [data-slot="menu-item"] span { |
| min-width: 0; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| |
| .maka-composer-project-scroll { |
| max-height: 224px; |
| overflow-y: auto; |
| overscroll-behavior: contain; |
| scrollbar-gutter: stable; |
| scrollbar-color: var(--border-strong) transparent; |
| scrollbar-width: thin; |
| } |
| |
| .maka-composer-project-scroll::-webkit-scrollbar { |
| width: 8px; |
| } |
| |
| .maka-composer-project-scroll::-webkit-scrollbar-thumb { |
| border: var(--border-width-thick) solid transparent; |
| border-radius: var(--radius-pill); |
| background: var(--border-strong); |
| background-clip: padding-box; |
| } |
| |
| .maka-composer-project-status { |
| margin-left: auto; |
| color: var(--muted-foreground); |
| font-size: var(--font-size-caption); |
| } |
| |
| .maka-composer-project-check { |
| margin-left: auto; |
| } |
| |
| .maka-composer-no-project { |
| color: var(--muted-foreground); |
| } |
| |
| /* Composer streaming hint — replaces the Enter/Shift+Enter help text while |
| * Maka is mid-stream. Pulsing dot signals "live", and the Stop button on |
| * the right swaps in as the only primary action so Send isn't ambiguous. */ |
| .maka-composer-streaming-hint { |
| display: inline-flex; |
| align-items: center; |
| gap: var(--space-2); |
| color: var(--foreground-secondary); |
| font-size: var(--font-size-caption); |
| } |
| |
| /* Streaming indicator — 3-dot typing pattern (the center dot is the |
| element itself, the side dots are ::before / ::after). Each dot |
| bounces 4px up on its own offset cycle so the row reads as a |
| talking-back animation rather than a single pulsing blip. */ |
| .maka-composer-streaming-dot { |
| position: relative; |
| width: 5px; |
| height: 5px; |
| margin-inline: var(--space-2); |
| border-radius: 50%; |
| background: var(--status-running); |
| animation: maka-composer-stream-bounce 1.05s var(--ease-in-out-strong) -0.2s infinite both; |
| } |
| |
| .maka-composer-streaming-dot::before, |
| .maka-composer-streaming-dot::after { |
| content: ""; |
| position: absolute; |
| top: 0; |
| width: 5px; |
| height: 5px; |
| border-radius: 50%; |
| background: var(--status-running); |
| animation: maka-composer-stream-bounce 1.05s var(--ease-in-out-strong) infinite both; |
| } |
| |
| .maka-composer-streaming-dot::before { |
| left: -9px; |
| animation-delay: -0.4s; |
| } |
| |
| .maka-composer-streaming-dot::after { |
| left: 9px; |
| animation-delay: 0s; |
| } |
| |
| @keyframes maka-composer-stream-bounce { |
| 0%, 60%, 100% { |
| transform: translateY(0); |
| opacity: 0.55; |
| } |
| 30% { |
| transform: translateY(-3px); |
| opacity: 1; |
| } |
| } |
| |
| @media (prefers-reduced-motion: reduce) { |
| .maka-composer-streaming-dot, |
| .maka-composer-streaming-dot::before, |
| .maka-composer-streaming-dot::after { |
| animation: none; |
| } |
| } |
| |
| /* PR-COMPOSER-PERMISSION-PULSE-0 (WAWQAQ msg `ed67a267`, skills |
| round task #116): paired with the 3-dot streaming bounce — when |
| the composer is disabled awaiting human permission decision, the |
| text gets a pulsing accent dot so the user sees "waiting on YOU" |
| instead of plain prose. Uses a slow opacity pulse (different |
| rhythm from streaming's bounce so the two states are not |
| confusable at a glance). Pattern from design-taste-frontend §8.3 |
| perpetual micro-interactions. */ |
| .maka-composer-permission-hint { |
| display: inline-flex; |
| align-items: center; |
| gap: var(--space-1-5); |
| } |
| |
| .maka-composer-permission-dot { |
| width: 6px; |
| height: 6px; |
| border-radius: 50%; |
| background: var(--status-running); |
| animation: maka-composer-permission-pulse 1.4s var(--ease-in-out-strong) infinite; |
| flex-shrink: 0; |
| } |
| |
| @keyframes maka-composer-permission-pulse { |
| 0%, 100% { opacity: 0.35; transform: scale(0.85); } |
| 50% { opacity: 1; transform: scale(1); } |
| } |
| |
| @media (prefers-reduced-motion: reduce) { |
| .maka-composer-permission-dot { |
| animation: none; |
| opacity: 1; |
| } |
| } |
| |
| /* U3 no-model dead-end hint. Sits ABOVE the composer form (outside its box, so |
| it never touches the #740 constant footprint). Edged, not pill; quiet caption |
| text + one link-button into Settings · 模型. Theme-aware via tokens. Centered to the chat measure, matching the revision-notice layout. */ |
| .maka-composer-no-model-hint { |
| display: flex; |
| align-items: center; |
| gap: var(--space-2); |
| width: min(var(--maka-chat-measure), calc(100% - (2 * var(--space-6)))); |
| max-width: var(--maka-chat-measure); |
| margin: 0 auto var(--space-1-5); |
| padding: var(--space-1) var(--space-2-5); |
| border: var(--border-width-hairline) solid var(--warning); |
| border-radius: var(--radius-control); |
| color: var(--foreground-secondary); |
| font-size: var(--font-size-caption); |
| } |
| |
| .maka-composer-no-model-hint-action { |
| flex-shrink: 0; |
| color: var(--link); |
| font-size: var(--font-size-caption); |
| font-weight: var(--font-weight-medium); |
| text-decoration: none; |
| border-radius: var(--radius-control); |
| } |
| |
| .maka-composer-no-model-hint-action:hover { |
| text-decoration: underline; |
| } |
| |
| .maka-composer-no-model-hint-action:focus-visible { |
| outline: var(--focus-ring-width) solid var(--focus-ring); |
| outline-offset: var(--focus-ring-offset); |
| } |
| |
| /* Edit-and-resend notice. A single quiet inline pill above the composer form, |
| matching the composer toolbar chip family (not a heavy info card). */ |
| .maka-composer-revision-notice { |
| display: flex; |
| align-items: center; |
| gap: var(--space-1-5); |
| width: min(var(--maka-chat-measure), calc(100% - (2 * var(--space-6)))); |
| max-width: var(--maka-chat-measure); |
| margin: 0 auto var(--space-1); |
| padding: 0 var(--space-1-5); |
| box-sizing: border-box; |
| height: 26px; |
| border: var(--border-width-hairline) solid var(--border); |
| border-radius: var(--radius-pill); |
| background: var(--background-elevated); |
| color: var(--foreground-secondary); |
| font-size: var(--font-size-caption); |
| line-height: var(--leading-none); |
| } |
| |
| .maka-composer-revision-notice > svg { |
| flex: 0 0 auto; |
| color: var(--muted-foreground); |
| } |
| |
| .maka-composer-revision-notice-text { |
| min-width: 0; |
| flex: 1 1 auto; |
| display: flex; |
| align-items: baseline; |
| gap: var(--space-1); |
| overflow: hidden; |
| white-space: nowrap; |
| } |
| |
| .maka-composer-revision-notice-detail { |
| color: var(--muted-foreground); |
| overflow: hidden; |
| text-overflow: ellipsis; |
| } |
| |
| .maka-composer-revision-notice-cancel { |
| appearance: none; |
| flex: 0 0 auto; |
| height: 18px; |
| padding: 0 var(--space-1-5); |
| border: none; |
| border-radius: var(--radius-pill); |
| background: transparent; |
| color: var(--muted-foreground); |
| font-size: var(--font-size-caption); |
| font-weight: var(--font-weight-medium); |
| white-space: nowrap; |
| transition: background-color var(--duration-base) var(--ease-out-strong), color var(--duration-base) var(--ease-out-strong); |
| } |
| |
| .maka-composer-revision-notice-cancel:hover:not(:disabled) { |
| background: var(--state-hover-bg); |
| color: var(--foreground); |
| } |
| |
| .maka-composer-revision-notice-cancel:focus-visible { |
| outline: var(--focus-ring-width) solid var(--focus-ring); |
| outline-offset: var(--focus-ring-offset); |
| } |
| |
| .maka-composer-revision-notice-cancel:disabled { |
| opacity: var(--opacity-disabled); |
| } |