/* 输入区(Composer)视觉层 —— 两层结构(design-system.md Warm Paper 铁律): 上层 = textarea(呼吸空间足),下层 = 控件条: 左组【对话|任务 分段 pill + 批准档 + 权限小字】,右组【思考滑轨 → 模型 → 发送】。 组内聚拢(8px)、组间留白(16px)、控件统一 32px 高、圆角按令牌; 模型选择是窄触发器 + 向上 popover(--shadow-float 描边浮层,不用毛玻璃)。 窄窗时按「整组」换行,绝不把控件压扁——发送按钮永远待在右下角。 本文件只服务 Composer.tsx;模式切换/批准档的原始规则在 task.css, 这里用 .composer-bar 前缀做作用域内的密度统一(不改 task.css)。 */ /* ---------- 外层:贴底输入区 ---------- */ .composer-wrap { padding: 16px 48px 24px; border-top: 1px solid var(--border); } /* ---------- 输入盒:sunken 底,聚焦 accent 描边 + 极浅外晕 ---------- */ .composer { max-width: 760px; margin: 0 auto; padding: 12px 14px; border: 1px solid transparent; border-radius: var(--radius-float); background: var(--bg-sunken); transition: border-color var(--dur) var(--ease); } .composer:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-soft) 65%, transparent); } /* ---------- 上层:输入 textarea(不与控件抢空间) ---------- */ .composer-input { display: block; width: 100%; min-height: 44px; max-height: 200px; padding: 4px 2px 8px; border: none; background: transparent; color: var(--text); font-family: var(--font-sans); font-size: 15px; line-height: 1.65; resize: none; overflow-y: auto; } .composer-input:focus { outline: none; } .composer-input::placeholder { color: var(--text-tertiary); } /* ---------- 下层:控件条(左右两组) ---------- */ .composer-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; /* 行间 8、组间 16:8px 网格 */ margin-top: 8px; } .composer-group { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: 0 0 auto; /* 不缩小:放不下就整组换行,永不挤压控件 */ } .composer-group-end { margin-left: auto; } /* ---------- 模式切换(契约 6D):Codex 式无边框文字切换 ---------- 去掉容器边框与凹底(覆盖 task.css 基础款),激活档用暖陶土淡染, 不再是实心 pill——与整条控制条的「文字优先」一致。 */ .composer-bar .mode-switch { gap: 2px; padding: 0; border: none; background: transparent; } .composer-bar .mode-pill { height: 28px; padding: 0 10px; border-radius: var(--radius-chip); font-size: 13px; color: var(--text-tertiary); background: transparent; } .composer-bar .mode-pill:hover { color: var(--text-secondary); background: color-mix(in srgb, var(--accent-soft) 45%, transparent); } .composer-bar .mode-pill.is-active { background: color-mix(in srgb, var(--accent-soft) 85%, transparent); color: var(--accent); } .quick-tool-wrap{position:relative;z-index:20} .composer-bar .quick-tool-trigger{display:inline-flex;align-items:center;gap:6px;height:27px;padding:0 8px;border:1px solid var(--border);border-radius:6px;background:transparent;color:var(--text-secondary);font:10px var(--font-sans);cursor:pointer} .composer-bar .quick-tool-trigger:hover,.composer-bar .quick-tool-trigger[aria-expanded="true"]{color:var(--accent);background:var(--accent-soft)} .quick-tool-menu{position:absolute;left:0;bottom:calc(100% + 9px);width:268px;padding:8px;border:1px solid var(--border);border-radius:8px;background:var(--bg-elevated);box-shadow:var(--shadow-float)} .quick-tool-menu>strong{display:block;padding:5px 7px 8px;color:var(--text-tertiary);font-size:9px;font-weight:500} .quick-tool-item{display:flex;align-items:center;gap:2px} .quick-tool-item>button:first-child{display:flex;align-items:center;gap:9px;flex:1;min-width:0;height:34px;padding:0 8px;border:0;border-radius:5px;background:transparent;color:var(--text);font-size:10px;text-align:left;cursor:pointer} .quick-tool-item>button:first-child:hover{background:var(--accent-soft)} .quick-tool-item>button:first-child>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .quick-tool-pin{display:grid;place-items:center;width:27px;height:27px;border:0;border-radius:5px;background:transparent;color:var(--text-tertiary);cursor:pointer} .quick-tool-pin:hover,.quick-tool-pin.is-pinned{color:var(--warning);background:var(--bg-sunken)} .quick-tool-menu>small{display:block;padding:8px 7px 3px;border-top:1px solid var(--border);color:var(--text-tertiary);font-size:9px} .research-consent{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 14px;margin:0 8px 8px;border:1px solid color-mix(in srgb,var(--warning) 45%,var(--border));border-radius:7px;background:var(--bg-sunken)} .research-consent>div{flex:1;min-width:220px} .research-consent strong{font-size:11px;color:var(--text)} .research-consent p{margin:4px 0 0;color:var(--text-secondary);font-size:10px;line-height:1.6} .research-consent .ws-primary{padding:7px 10px} .research-consent .ws-link{font-size:10px} /* ---------- 批准档(仅任务模式):彩色文字 + 安全图标,去边框下拉 ---------- Codex 的「⚠ 完全访问」范式:select 视觉隐藏为纯文字载体,图标与文字 承担全部观感;全访问=陶土警示色,智能=暖中性偏正向,询问=中性。 */ .composer-bar .perm-picker { gap: 5px; height: 28px; padding: 0 6px; border-radius: var(--radius-chip); cursor: pointer; transition: background-color var(--dur) var(--ease); } .composer-bar .perm-picker:hover { background: color-mix(in srgb, var(--accent-soft) 45%, transparent); } .composer-bar .perm-picker select { appearance: none; -webkit-appearance: none; height: 28px; padding: 0; border: none; background: transparent; color: inherit; font-family: inherit; font-size: 12.5px; cursor: pointer; } .composer-bar .perm-picker select:focus { outline: none; } .composer-bar .perm-picker select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* 图标与文字同色:按档位着色(图标靠 color: inherit 跟随) */ .composer-bar .perm-picker.is-full-access { color: var(--danger); } .composer-bar .perm-picker.is-smart-approve { color: var(--accent); } .composer-bar .perm-picker.is-always-ask { color: var(--text-secondary); } /* ---------- 权限总闸状态小字(只读,契约 13A) ---------- */ .composer-bar .perm-gate-hint { align-self: center; font-size: 11.5px; line-height: 1.2; color: var(--text-tertiary); white-space: nowrap; } /* ---------- 模型选择:窄触发器 + 向上 popover ---------- */ .model-menu-wrap { position: relative; display: inline-flex; } .model-trigger { display: inline-flex; align-items: center; gap: 6px; height: 28px; max-width: 176px; padding: 0 6px; border: none; border-radius: var(--radius-chip); background: transparent; color: var(--text-secondary); font-family: inherit; font-size: 12.5px; cursor: pointer; transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease); } .model-trigger:hover { color: var(--text); background: color-mix(in srgb, var(--accent-soft) 45%, transparent); } .model-trigger[aria-expanded="true"] { color: var(--text); background: color-mix(in srgb, var(--accent-soft) 60%, transparent); } .model-trigger:focus-visible, .model-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .model-trigger-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .model-trigger-caret { flex: 0 0 auto; color: var(--text-tertiary); } /* 「云」徽章:与全站徽章同尺寸同字重(同 bench 云徽章口径) */ .model-cloud { flex: 0 0 auto; font-size: 11px; line-height: 1; padding: 3px 6px; border-radius: var(--radius-chip); background: var(--accent-soft); color: var(--text); } /* popover:Warm Paper 浮层(elevated 底 + 1px 描边 + --shadow-float; 毛玻璃禁用——透明窗口上 WebKit 的 backdrop-filter 有已知坑)。 入场 = opacity + translateY 6px,180ms。 */ .model-menu { position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 30; min-width: 236px; max-width: 320px; max-height: 264px; overflow-y: auto; padding: 6px; border: 1px solid var(--border); border-radius: var(--radius-float); background: var(--bg-elevated); box-shadow: var(--shadow-float); animation: model-menu-in 180ms var(--ease); } @keyframes model-menu-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } @media (prefers-reduced-motion: reduce) { .model-menu { animation: none; } } .model-option { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 10px; border: none; border-radius: var(--radius-control); background: transparent; color: var(--text); font-family: inherit; font-size: 12.5px; text-align: left; cursor: pointer; transition: background-color var(--dur) var(--ease); } .model-option:hover { background: color-mix(in srgb, var(--accent-soft) 40%, transparent); } .model-option.is-active { background: var(--accent-soft); } .model-option-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 空态(没模型):触发器只显示短句,完整人话在浮层里 */ .model-menu-empty { margin: 0; padding: 6px 8px; max-width: 264px; color: var(--text-tertiary); font-size: 12px; line-height: 1.6; } /* ---------- 右组动作:快捷键提示 + 发送/停止 ---------- */ .composer-actions { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; } .composer-hint { font-size: 12px; line-height: 1; color: var(--text-tertiary); white-space: nowrap; } /* 发送:Codex 式圆形图标按钮(暖陶土实底 + ↑),去文字。 */ .btn-send { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 30px; height: 30px; padding: 0; border: none; border-radius: 50%; background: var(--accent); color: var(--bg); cursor: pointer; transition: background-color var(--dur) var(--ease), opacity var(--dur) var(--ease), transform var(--dur) var(--ease); } .btn-send:hover:not(:disabled) { filter: brightness(0.94); } .btn-send:active:not(:disabled) { transform: scale(0.94); } .btn-send:disabled { opacity: 0.4; cursor: default; } .btn-send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* 停止:圆形描边图标按钮(□),与发送同尺寸。 */ .btn-stop { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 30px; height: 30px; padding: 0; border: 1px solid var(--border); border-radius: 50%; background: transparent; color: var(--text); cursor: pointer; transition: background-color var(--dur) var(--ease); } .btn-stop:hover { background: var(--accent-soft); } .btn-stop:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .composer-error { max-width: 760px; margin: 0 auto 8px; color: var(--danger); font-size: 12.5px; } /* ---------- 思考强度胶囊滑轨(契约 6F)---------- Warm Paper 基调:sunken 细轨 + 五个刻度点 + 圆点手柄; 手柄 150ms transform 过渡滑动;Ultra 档手柄与轨道末端用 accent 渐变提示「最烧脑」。不引外部滑块库。 收紧版:档名衬线小字(约 50px)+ 72px 轨道(五档间距 18px), 整条约 128px;任务模式/狂暴锁 Ultra 的语义一字未改。 */ /* ── 思考强度滑块(契约 6F)—— 对齐参考设计 ────────────────────── 顶行「思考强度」标题 + 右侧当前档值;下方暗色渐变轨道 + 白色圆旋钮。 五档(关/低/中/高/炽焰)连续拖拽,松手吸附。 */ .think-slider { display: flex; flex-direction: column; gap: 5px; flex: 0 0 auto; width: 220px; min-width: 0; } .think-slider.is-disabled { opacity: 0.45; pointer-events: none; } .think-head { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; } .think-label { font-size: 11px; color: var(--ink-3); white-space: nowrap; } .think-value { font-size: 11px; color: var(--ink-1); font-weight: 600; font-variant-numeric: tabular-nums; transition: color var(--dur-2) var(--ease); } .think-slider[data-lv="4"] .think-value { color: var(--warning); } /* 暗色圆角轨道 + 渐变填充 + 白色圆旋钮 */ .think-track { position: relative; height: 16px; border-radius: 999px; background: linear-gradient(180deg, rgba(0,0,0,0.35), rgba(0,0,0,0.15)); box-shadow: inset 0 1px 2px rgba(0,0,0,0.35); cursor: pointer; touch-action: none; } .think-fill { position: absolute; left: 2px; top: 2px; bottom: 2px; width: calc((var(--think-pos, 0) / 4) * (100% - 4px)); border-radius: 999px; background: linear-gradient(90deg, rgba(217,119,87,0.55), rgba(201,162,39,0.75)); transition: width var(--dur-2) var(--ease); overflow: hidden; } .think-knob { position: absolute; top: 50%; left: calc(2px + (var(--think-pos, 0) / 4) * (100% - 4px)); width: 16px; height: 16px; transform: translate(-50%, -50%); border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.45), 0 0 0 1px rgba(0,0,0,0.08); transition: left var(--dur-2) var(--ease); pointer-events: none; } .think-slider[data-lv="4"] .think-knob { box-shadow: 0 1px 4px rgba(0,0,0,0.45), 0 0 8px rgba(201,162,39,0.6); } .think-slider.is-dragging .think-knob, .think-slider.is-dragging .think-fill { transition: none; } /* 星点(飞驰小点,随档位加速) */ .think-spark { position: absolute; top: 50%; width: 3px; height: 3px; transform: translateY(-50%); border-radius: 50%; background: rgba(255,255,255,0.85); opacity: 0; pointer-events: none; animation: think-fly var(--d, 1400ms) var(--ease) infinite; animation-delay: var(--delay, 0ms); } @keyframes think-fly { 0% { left: 0; opacity: 0; } 15% { opacity: var(--o, 0.8); } 85% { opacity: var(--o, 0.8); } 100% { left: 100%; opacity: 0; } } @media (prefers-reduced-motion: reduce) { .think-spark { animation: none; opacity: 0; } }