Spaces:
Running
Running
Download css/composer.css from hongcoding/perch-design: direct link, hf CLI and curl.
- Browser
- Download file 14.7 kB
-
https://huggingface.co/spaces/hongcoding/perch-design/resolve/main/css/composer.css
- Command line
-
hf download hf://spaces/hongcoding/perch-design/css/composer.css
-
curl -L -o composer.css https://huggingface.co/spaces/hongcoding/perch-design/resolve/main/css/composer.css
14.7 kB
| /* 输入区(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; } | |
| } | |