Files
deepseek-harness/packages/client/ui-conversation/src/client/skeleton/InputBar.module.css
Tianyi Cui e423bfbee6 Merge remote-tracking branch 'origin/master' into worktree-i18n-update-workflow
# Conflicts:
#	.agents/notes/implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.i18n.yaml
#	.agents/skills/dsh-translate-docs/SKILL.md
#	docs/i18n/README.i18n.yaml
2026-07-27 09:39:54 +08:00

405 lines
11 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* One-glyph font: maps ONLY U+FFFC to a blank 4em-advance glyph (every other
codepoint falls through to the next family). Loaded first in the composer
font stack, it gives the placeholder a real cell width INSIDE the textarea,
so the backdrop chip (same char, same stack) matches it by construction —
the two layers cannot drift and the chip gets a usable label cell. */
@font-face {
font-family: 'DshChipCell';
src: url('data:font/ttf;base64,AAEAAAAKAIAAAwAgT1MvMkT8SmIAAAEoAAAAYGNtYXAADQBPAAABkAAAADRnbHlmAAAAAAAAAcwAAAABaGVhZCwtPGoAAACsAAAANmhoZWEDIg7bAAAA5AAAACRobXR4EZQAAAAAAYgAAAAIbG9jYQAAAAAAAAHEAAAABm1heHAAAwACAAABCAAAACBuYW1lvljk2gAAAdAAAABscG9zdNNweNQAAAI8AAAALQABAAAAAQAAdia1tV8PPPUAAwPoAAAAAOaLfcUAAAAA5ot9xQAAAAAAAAAAAAAAAwACAAAAAAAAAAEAAAMg/zgAAA+gAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAEAAAACAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAwjKAZAABQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAPz8/PwAA//z//AMg/zgAAAMgAMgAAAAAAAAAAAAAAAAAAAAgAAAB9AAAD6AAAAAAAAIAAAADAAAAFAADAAEAAAAUAAQAIAAAAAQABAABAAD//P//AAD//P//AAUAAQAAAAAAAAAAAAAAAAAAAAAAAAAEADYAAQAAAAAAAQALAAAAAQAAAAAAAgAHAAsAAwABBAkAAQAWABIAAwABBAkAAgAOAChEc2hDaGlwQ2VsbFJlZ3VsYXIARABzAGgAQwBoAGkAcABDAGUAbABsAFIAZQBnAHUAbABhAHIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAABAgZvYmpyZXAAAAA=') format('truetype');
}
/* Floating capsule input (figma Input_Bottom 75:8208): card floats above the
viewport bottom inside the centered message column; textarea on top, action
row below, one primary circle button bottom-right. Input width rides the
column (800 is a cap, not a fixed size — layout rule: the box shrinks with
the center column keeping its padding). Hero variant = the same card
centered in the empty state; the transition between the two is a position
move of one component. */
.root {
display: flex;
flex-direction: column;
align-items: center;
/* figma Input_Bottom: pad L32/R32/B12; the bottom gradient mask is owned by
the chat scroller. Top 8 hosts the error strip's breathing room. */
padding: 8px 32px 12px;
}
.hero {
padding: 0;
}
.error,
.status {
width: 100%;
max-width: 800px;
margin-bottom: 6px;
padding: 4px 8px;
border-radius: 8px;
font-size: 12px;
line-height: 18px;
}
.status {
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-secondary);
}
.notice {
width: 100%;
max-width: 800px;
margin-bottom: 6px;
padding: 4px 8px;
border-radius: 8px;
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-secondary);
font-size: 12px;
line-height: 18px;
}
.noticeError {
background: var(--dsw-alias-interactive-bg-hover-danger);
color: var(--dsw-alias-state-error-primary);
}
.error {
background: var(--dsw-alias-interactive-bg-hover-danger);
color: var(--dsw-alias-state-error-primary);
}
.card {
position: relative; /* overlay anchor positioning context */
display: flex;
flex-direction: column;
/* figma Input 75:8208: 12px between the text area and the button row; 10px
top pad on the card before .InputText. */
gap: 12px;
width: 100%;
max-width: 800px;
padding-top: 10px;
/* Input stroke: black/0.10 light, white/0.06 dark (figma darkmode note says
the input border is one notch weaker than buttons) — exactly the
l2-darkmode-thin pair. Fill: the input surface token (elevated in dark). */
border: 1px solid var(--dsw-alias-border-l2-darkmode-thin);
border-radius: 20px;
background: var(--dsw-specific-input-major);
box-shadow: var(--dsw-shadow-lv2);
font-size: 16px;
line-height: 24px;
}
.accessory {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px 0;
}
/* Floating overlay anchor (menu / popupSelect shell): entries position
themselves against the card (bottom: 100% + gap); closed entries render null. */
.overlayAnchor {
position: absolute;
inset: 0 0 auto;
height: 0;
}
/* Mirror-div auto-grow wrapper: the hidden mirror is in normal flow and sets the height
(min 2 lines / max 14 lines); the textarea rides it absolutely. Mirror and textarea
MUST share font, line-height, padding and wrapping rules or heights diverge. */
.grow {
position: relative;
}
/* Decoration backdrop: same metrics as the textarea, transparent glyphs; only
the highlight backgrounds and the ghost hint show through the transparent
textarea background above it. */
.backdrop {
position: absolute;
inset: 0;
overflow: hidden;
color: transparent;
pointer-events: none;
}
.hlToken {
border-radius: 4px;
/* GOAL 稿 amber token emphasis (state warn pair; glyphs stay the textarea's). */
background: var(--dsw-alias-state-warn-tertiary);
color: transparent;
}
.hlSegment {
border-radius: 4px;
background: var(--dsw-alias-interactive-bg-hover);
color: transparent;
}
.hint {
color: var(--dsw-alias-label-caption);
}
/* Machine pending dot (adjudicating / submitting). */
.pending {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--dsw-alias-state-business-primary);
animation: input-pending 1s ease-in-out infinite alternate;
}
@keyframes input-pending {
from { opacity: 0.35; }
to { opacity: 1; }
}
.input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
resize: none;
overflow-y: auto;
border: none;
outline: none;
background: transparent;
color: var(--dsw-alias-label-primary);
/* Business blue, not brand-primary: that token resolves to ink in this sheet. */
caret-color: var(--dsw-alias-state-business-primary);
}
.input,
.mirror,
.backdrop {
/* figma .InputText 34:10434: pl 16 / pr 12 / pt 4. Backdrop MUST share these
metrics or the highlight ranges drift off the glyphs. */
padding: 4px 12px 0 16px;
/* DshChipCell first: ONLY U+FFFC resolves there (4em blank cell — the chip
slot); everything else falls through to the app stack. All three layers
share the stack, so placeholder advances agree by construction. */
font-family: 'DshChipCell', var(--dsw-font-family);
font-size: inherit;
line-height: inherit;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: anywhere;
}
/* figma 34:10434: #ADB2B8 light / #81858C dark — the caption pair exactly. */
.input::placeholder {
color: var(--dsw-alias-label-caption);
user-select: none;
}
/* Running lock: grayed but the draft stays visible; the turn ending re-enables. */
.input:disabled {
color: var(--dsw-alias-label-tertiary);
cursor: not-allowed;
}
.mirror {
visibility: hidden;
pointer-events: none;
/* figma min-h 52 (= ~2 × 24 line + 4pt); 14-line cap (336px). */
min-height: 52px;
max-height: 336px;
overflow: hidden;
}
/* Toolbar: attach + Plan + Read-only on the left; model + send on the right
(figma Input_Bottom chrome). */
.row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 0 10px 10px 10px;
min-width: 0;
}
.tools,
.modes,
.trailing {
display: flex;
align-items: center;
min-width: 0;
}
/* figma 75:8208: 16 between + and the mode chips; 4 between Plan / Read-only. */
.tools {
gap: 16px;
}
.modes {
gap: 4px;
}
.trailing {
flex: none;
gap: 12px;
}
/* Attach circle (figma + control): 28px, selector fill, primary glyph. */
.add {
display: grid;
place-items: center;
flex: none;
width: 28px;
height: 28px;
border: none;
border-radius: 999px;
background: var(--dsw-specific-selector);
color: var(--dsw-alias-label-primary);
cursor: pointer;
}
.add:hover:not(:disabled) {
background: var(--dsw-alias-interactive-bg-hover-solid);
}
.add:disabled {
opacity: 0.5;
cursor: default;
}
/* Plan / Read-only / model — native <select>, chip-like closed chrome
(figma ToggleButton: 13/20 medium secondary, 12px chevron). */
.select {
max-width: 220px;
height: 28px;
padding: 0 20px 0 8px;
border: none;
border-radius: 8px;
outline: none;
background-color: transparent;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 4px center;
background-size: 12px 12px;
color: var(--dsw-alias-label-secondary);
font-size: 13px;
line-height: 20px;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
appearance: none;
}
.select:hover:not(:disabled) {
background-color: var(--dsw-alias-interactive-bg-hover);
}
.select:disabled {
opacity: 0.5;
cursor: default;
}
/* Primary send (figma IconButton 34:10465): 34px circle, #3964FE light /
#679EFE dark — the info-fill pair (500→400), NOT button-primary (ink);
white glyph; empty text = 0.4 opacity. */
.primary {
display: grid;
place-items: center;
flex: none;
width: 34px;
height: 34px;
border: none;
border-radius: 999px;
background: var(--dsw-alias-button-info-fill);
color: var(--dsw-alias-label-primary-foreground);
cursor: pointer;
}
.primary:hover {
background: var(--dsw-alias-button-info-hover);
}
.primary:disabled {
opacity: 0.4;
cursor: default;
}
/* Stop state: same slot, dimmed brand fill — the running-state send-key
replacement is a design gap filled by us (figma gives no stop form). */
.stopping,
.stopping:hover {
background: var(--dsw-alias-button-primary-dimmed);
color: var(--dsw-alias-brand-text);
}
.retry {
margin-left: 8px;
padding: 1px 8px;
border: 1px solid currentColor;
border-radius: 4px;
background: transparent;
color: inherit;
font-size: 12px;
cursor: pointer;
}
/* Plain-text reference highlight (decision 21): a pure range mark over the
draft's own glyphs — advance untouched, so the two layers cannot drift.
Chip family colors; clone keeps rounded ends on soft-wrap fragments. */
.textRef {
color: transparent;
background-color: transparent;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
position: relative;
}
.textRef:after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 6px;
background: rgba(97, 135, 216, 0.22);
transform: translate(-2px, -1px);
padding: 2px 4px;
}
/* Reference chip: rendered in the backdrop at the placeholder offset. Hard
alignment constraint: the chip's advance must equal the textarea's U+FFFC
advance EXACTLY or every glyph after it drifts (caret/selection follow the
textarea character stream). The ::before renders the same U+FFFC through
the same font stack (DshChipCell 4em cell), so both layers agree by
construction — no measured widths. The label overlays the cell, clipped
with an ellipsis; the full name rides the title tooltip. */
.chip {
position: relative;
border-radius: 6px;
background: rgba(97, 135, 216, 0.22);
}
.chip::before {
content: '\FFFC';
color: transparent;
}
.chipLabel {
/* Compensated-scale centering: overflow clipping happens BEFORE transform,
so the box is laid out at 1/0.72 of the cell and scaled back down — the
clip edge then lands on the visual cell edge, not mid-glyph. */
position: absolute;
left: 50%;
top: 50%;
width: calc(100% / 0.72 - 10px);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: var(--dsw-alias-label-primary);
white-space: nowrap;
transform: translate(-50%, -50%) scale(0.72);
}
.chipInvalid {
background: rgba(216, 97, 97, 0.2);
text-decoration: line-through;
opacity: 0.7;
}