blob: 9748460c48c56dfe6ee6e8c5653ba3532776b6ca [file]
/* 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);
}