// Generate & Attribute 页:共用左栏见 _chat-input-panel.scss @use 'app-pages'; @use "attribution-inspector"; @use "chat-input-panel"; @use "generation-status"; @use "lmf-readout" as lmf; @use '../base/narrow-ios-form-font-tail' as ios-form-font; $gen-attr-option-row-gap: 12px; $gen-attr-cached-demos-panel-width: 21em; // 单行右对齐:Cached history 最右;Cached demo 靠其左侧 .chat-cached-history-bar.chat-cached-history-bar--dual { position: relative; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px 16px; width: 100%; box-sizing: border-box; } .chat-cached-history-bar--dual .chat-prompt-history-wrapper { width: auto; max-width: none; flex: 0 0 auto; } // Demos:Cached demo 与 demo 名横排;选中时 demo 名与下拉同宽(固定值,切换 demo 不换宽) .chat-cached-history-bar--dual .gen-attr-cached-demos-wrap { flex-direction: row; align-items: center; gap: 6px; } .gen-attr-cached-demos-picker { position: relative; width: $gen-attr-cached-demos-panel-width; max-width: min(100vw - 32px, 40rem); font-size: var(--font-size-base); flex-shrink: 0; } #gen_attr_cached_demos_value_btn.gen-attr-cached-demos-value-btn { display: inline-flex; align-items: center; gap: 6px; width: 100%; max-width: none; padding: 4px 8px; font-size: inherit; line-height: 1; font-family: inherit; border: 1px solid var(--input-border); border-radius: 4px; background: var(--input-bg); color: var(--text-color); cursor: pointer; box-sizing: border-box; text-align: left; } .gen-attr-cached-demos-value { flex: 1; min-width: 0; height: 1em; line-height: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; } .gen-attr-cached-demos-chevron { flex-shrink: 0; font-size: inherit; line-height: 1; color: var(--text-muted); } // History:不设定位层,子下拉以父级 bar 为 containing block,left/right:0 占满整行 .chat-cached-history-bar--dual .gen-attr-cached-history-wrap { position: static; flex-direction: row; align-items: center; } // Cached history / demos:随内容变高,封顶 min(32vh, 360px),竖向 resize;覆盖 _chat-input-panel 下拉 mixin 的 max-height:200px #gen_attr_cached_history_dropdown.semantic-search-history-dropdown, #gen_attr_cached_demos_dropdown.semantic-search-history-dropdown { max-height: min(32vh, 360px); resize: vertical; overflow-y: auto; } // demos 下拉与假 select 同宽(相对 .gen-attr-cached-demos-picker) .gen-attr-cached-demos-picker #gen_attr_cached_demos_dropdown.semantic-search-history-dropdown { left: 0; right: 0; width: 100%; min-width: 0; max-width: none; box-sizing: border-box; } // 右栏:#results 即 LMF + 归因表面 + DAG 容器(一层 DOM);.right_panel 纵向 flex 见 _responsive.scss .gen-attribute-page { // 覆盖全局 .LMF 的 min-height:350px,使本栏可作为 flex 子项收缩;图区高度由 svg min-height 兜底 #results.gen-attr-results-surface.LMF { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; width: 100%; box-sizing: border-box; padding: 6px; background: var(--text-area-bg); color: var(--text-color); overflow: visible; } // DAG:不可见测量层撑开高度,SVG 绝对定位叠在同栈内(坐标与 LMF 正文一致) // 初始 d3.zoom 缩放为 1/本变量,抵消 display-scale 在屏上的整体缩小感(见 genAttributeDagView) // 边色与节点追因/选中描边:`--dag-highlight-line-color-in`(accent 80%),见 start.scss。 #results.gen-attr-results-surface.LMF .gen-attr-dag-stack { // compactness:dag界面紧凑程度,范围 [0.05, 1];1时和普通文本排版一致;由 JS 写入 inline style,0.5 为纯 CSS 降级兜底。 --gen-attr-dag-display-scale: var(--gen-attr-dag-compactness, 0.5); --gen-attr-dag-node-text-font-scale: 0.9; --gen-attr-dag-link-stroke-width: calc(2px * var(--gen-attr-dag-compactness)); // --gen-attr-dag-prompt-node-fill: rgba(221, 179, 120, 0.29); // #99BFD1 浅蓝 --gen-attr-dag-prompt-node-fill: rgba(0, 255, 225, 0.28); // --gen-attr-dag-prompt-node-fill: rgba(0, 174, 255, 0.28); --gen-attr-dag-generated-node-fill: rgba(255, 165, 0, 0.28); position: relative; // 测量层在流内可极高;basis 用 0% 避免「内容高度」参与 flex 初始主尺寸,把图区限制在 #results 剩余高度内,以免撑出 .right_panel 纵向滚动条;overflow 非 visible 使 flex 自动最小高度按 0 计(见 css-flexbox-1 #min-size-auto) flex: 1 1 0%; min-height: 280px; overflow: hidden; width: 100%; box-sizing: border-box; } // DAG Top‑K:贴在 #results 内侧右下(absolute);固定尺寸 HUD;超出部分裁剪。 #results.gen-attr-results-surface.LMF > .tooltip.gen-attr-dag-topk-tooltip { box-sizing: border-box; // 与 #major_tooltip 一致:不参与 opacity 过渡,避免 reposition 后与底层叠影 transition-property: background-color, color; max-width: none; width: 14rem; height: 300px; display: flex; flex-direction: column; overflow: hidden; .currentToken, .myDetail { flex-shrink: 0; } .gen-attr-dag-topk-tooltip-predictions-scroll { flex: 1 1 auto; min-height: 0; margin-top: 6px; overflow: hidden; } } // 勾选「Hide inactive edges」时隐藏未与焦点相邻的灰边(linkG 容器内的边); // 与焦点相邻的高亮边由 genAttributeDagView 运行时搬运到 .gen-attr-dag-links-front 中,不受影响。 #results.gen-attr-results-surface.LMF.gen-attr-dag-hide-inactive-edges .gen-attr-dag-svg .gen-attr-dag-links { display: none; } // 节点标签:与测量层同源 1em × display-scale × node-text-font-scale(后者略小于 1 替代固定 padding) #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node-text { font-size: calc( 1em * var(--gen-attr-dag-display-scale, 1) * var(--gen-attr-dag-node-text-font-scale, 0.9) * var(--gen-attr-dag-node-ci-visual-scale, 1) ); font-family: inherit; font-weight: inherit; fill: var(--text-color, #222); // 与 `xml:space="preserve"` 一致,避免部分引擎压扁/吞掉空格 white-space: pre; } // 视觉节点不接收指针事件;命中由顶层 .gen-attr-dag-nodes-hit 承担(见 genAttributeDagView)。 #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-nodes .gen-attr-dag-node { pointer-events: none; } #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node-hit-target { fill: transparent; stroke: none; pointer-events: all; } // 节点 fill/stroke 分层:stroke rect 外扩见 syncNodeStrokeRects(屏上描边约 2px)。 #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node { .gen-attr-dag-node-stroke { fill: none; stroke: none; stroke-width: 0; pointer-events: none; } .gen-attr-dag-node-fill { stroke: none; fill: var(--gen-attr-dag-generated-node-fill); } &--prompt .gen-attr-dag-node-fill { fill: var(--gen-attr-dag-prompt-node-fill); } // 递归追因链上游节点描边(直接模式仅一跳,由蓝/红边表达);opacity 见 genAttributeDagView 中 stay→[0.3,1] 映射。 &--recursive-chain .gen-attr-dag-node-stroke { stroke: var(--dag-highlight-line-color-in); stroke-width: calc(2 * var(--gen-attr-dag-display-scale, 1)); stroke-opacity: var(--gen-attr-dag-node-recursive-share, 1); } // 反向传播动画:当前滑过节点仅改描边色相(与出边红一致),stroke-opacity 仍由 recursive-share 控制。 &--recursive-chain.gen-attr-dag-node--backward-slide .gen-attr-dag-node-stroke { stroke: var(--dag-highlight-line-color-out); } // 选中/悬停 = 焦点(追因起点,nodeShare=1);置于 recursive-chain 之后以覆盖链上节点的 stroke-opacity &--hover .gen-attr-dag-node-stroke, &--selected .gen-attr-dag-node-stroke { stroke: var(--dag-highlight-line-color-in); stroke-width: calc(2 * var(--gen-attr-dag-display-scale, 1)); stroke-opacity: 1; } } // 与 genAttributeDagView 中「仅 default(text-flow) 且选中时可拖」一致:可拖时提示 grab(命中层与视觉层共用 class) #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node--selected, #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node-hit.gen-attr-dag-node--selected { cursor: grab; &:active { cursor: grabbing; } } // 非 text-flow 布局禁拖:不显示 grab(由 .gen-attr-dag-stack 上的 class 标记) #results.gen-attr-results-surface.LMF .gen-attr-dag-stack.gen-attr-dag-no-node-drag-layout .gen-attr-dag-svg .gen-attr-dag-node--selected, #results.gen-attr-results-surface.LMF .gen-attr-dag-stack.gen-attr-dag-no-node-drag-layout .gen-attr-dag-svg .gen-attr-dag-node-hit.gen-attr-dag-node--selected { cursor: default; &:active { cursor: default; } } // 默认 width:100%(随容器)仅作回退;genAttributeDagView 会在 init 与 `setMeasureWidthPx` // 时以 inline style 写入固定像素宽度。测量层宽度是节点几何(折行位置)的唯一输入, // 固定后 resize/全屏等容器变化不再影响节点布局,只影响 fit 的视口缩放/平移。 #results.gen-attr-results-surface.LMF .gen-attr-dag-measure-layer { @include lmf.lmf-readout-text; visibility: hidden; pointer-events: none; width: 100%; box-sizing: border-box; } #results.gen-attr-results-surface.LMF .gen-attr-dag-svg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; min-height: 280px; display: block; touch-action: none; z-index: 1; // 使子节点 `1em` 与右栏 LMF 正文字号一致(与测量层同源) font-size: inherit; user-select: none; -webkit-user-select: none; } // 窄屏 / 强制窄屏:右栏常为 height:auto,DAG 易「很宽、很矮」;min-height 兜底;若非全屏再对 stack 设 max-height(≈80dvh)+flex-shrink:0。(全屏时 #results 带 :fullscreen 或 .css-pseudo-fullscreen-target,用 :not 排除后不施加封顶。) $_dag-narrow-stack-min: max(300px, min(60dvh, 680px)); $_dag-narrow-stack-max-h: 80dvh; $_results-not-fs: ':not(:fullscreen):not(.css-pseudo-fullscreen-target)'; @media (max-width: 767px) { #results.gen-attr-results-surface.LMF .gen-attr-dag-stack, #results.gen-attr-results-surface.LMF .gen-attr-dag-svg { min-height: min($_dag-narrow-stack-min, $_dag-narrow-stack-max-h); } #results.gen-attr-results-surface.LMF#{$_results-not-fs} .gen-attr-dag-stack { flex-shrink: 0; max-height: $_dag-narrow-stack-max-h; } } @at-root html[data-force-narrow]:has(.main_frame) body#{&} { #results.gen-attr-results-surface.LMF .gen-attr-dag-stack, #results.gen-attr-results-surface.LMF .gen-attr-dag-svg { min-height: min($_dag-narrow-stack-min, $_dag-narrow-stack-max-h); } #results.gen-attr-results-surface.LMF#{$_results-not-fs} .gen-attr-dag-stack { flex-shrink: 0; max-height: $_dag-narrow-stack-max-h; } } // #results 已有 .attribution-inspector-surface { position: relative } // 与历史下拉里 ↑ / × 一致:等宽 `--font-icon`(见 start.scss) #results.gen-attr-results-surface.LMF > button.gen-attr-dag-play, #results.gen-attr-results-surface.LMF > button.gen-attr-dag-refresh, #results.gen-attr-results-surface.LMF > button.gen-attr-dag-fullscreen { position: absolute; top: 12px; z-index: 2; font-family: var(--font-icon); font-size: var(--font-icon-size); line-height: 1; user-select: none; -webkit-user-select: none; } // 从右到左:⛶ (fullscreen) → ↻ (refresh) → ▶ / ↯ (play: step replay / propagation) #results.gen-attr-results-surface.LMF > button.gen-attr-dag-fullscreen { right: 12px; } #results.gen-attr-results-surface.LMF > button.gen-attr-dag-refresh { right: 48px; } #results.gen-attr-results-surface.LMF > button.gen-attr-dag-play { right: 84px; } // 传播播放(↯)可点时:灰 ↔ 强调色脉动,提示可播传播链 @keyframes gen-attr-dag-propagation-play-hint { 0%, 100% { color: var(--refresh-btn-color); } 50% { color: var(--accent-color); } } #results.gen-attr-results-surface.LMF > button.gen-attr-dag-play.gen-attr-dag-play--propagation-hint:not(:disabled) { animation: gen-attr-dag-propagation-play-hint 1.6s ease-in-out infinite; } @media (prefers-reduced-motion: reduce) { #results.gen-attr-results-surface.LMF > button.gen-attr-dag-play.gen-attr-dag-play--propagation-hint { animation: none; } } // --- WORKAROUND:原生全屏不可用时的 CSS 伪全屏(见 ts/ui/cssPseudoFullscreen.ts),与 :fullscreen 共用版心样式 --- #results.gen-attr-results-surface.LMF:fullscreen, #results.gen-attr-results-surface.LMF.css-pseudo-fullscreen-target { width: 100vw; height: 100vh; padding: 6px; background: var(--text-area-bg); color: var(--text-color); overflow: auto; box-sizing: border-box; } #results.gen-attr-results-surface.LMF.css-pseudo-fullscreen-target { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 9000; } } :root[data-theme="dark"] .gen-attribute-page #results.gen-attr-results-surface.LMF:fullscreen, :root[data-theme="dark"] .gen-attribute-page #results.gen-attr-results-surface.LMF.css-pseudo-fullscreen-target { background: var(--gen-attr-dag-canvas-bg); } // 夜间:DAG 与 #results 的 padding「外圈」同色(外圈属父级 #results,非 .gen-attr-dag-stack 的 border) :root[data-theme="dark"] .gen-attribute-page #results.gen-attr-results-surface.LMF { --gen-attr-dag-canvas-bg: #101010; background: var(--gen-attr-dag-canvas-bg); } :root[data-theme="dark"] .gen-attribute-page #results.gen-attr-results-surface.LMF .gen-attr-dag-stack { background: var(--gen-attr-dag-canvas-bg); // --gen-attr-dag-prompt-node-fill: rgba(203, 160, 115, 0.28); } // 与 Chat 页 .text-metrics-chat #metric_usage 对齐(本页使用独立 id) .text-metrics-chat #gen_attr_metric_usage .text-metrics-api-usage { font-size: inherit; line-height: inherit; opacity: 1; } // 紧凑数字输入(DAG Text width / compactness / 回放时长等;Max tokens 见 _semantic-analysis.scss) .gen-attr-dag-measure-width-input { box-sizing: border-box; flex: 0 0 auto; min-width: 0; block-size: var(--semantic-submode-control-block-size, auto); padding: 3px 6px; line-height: 1.2; font-size: inherit; border: 1px solid var(--input-border); border-radius: 4px; background: var(--input-bg); color: var(--text-color); text-align: right; // 沿用原先约 4.5–4.75rem 目标,但以 border-box 计满宽(先前 content-box + padding/border 会额外变宽) inline-size: 4rem; max-inline-size: 100%; } // 与 Chat 共用的 .generation-status-slot 见 _generation-status.scss // 与 Chat 页一致:System 输入框较矮 .input-section .textarea-wrapper textarea#gen_attr_system_text { height: 50px; } // Raw / User / Forced continuation:统一高度(不占 chat 默认 250px 主 prompt 区) .input-section .textarea-wrapper textarea#gen_attr_raw_text, .input-section .textarea-wrapper textarea#gen_attr_user_text, .input-section .textarea-wrapper textarea#gen_attr_teacher_forcing_text { height: 90px; min-height: 60px; max-height: 250px; } body.gen-attribute-page .input-section .textarea-wrapper.chat-prompt-actions-row > .chat-completion-options-row.semantic-submode-row { gap: $gen-attr-option-row-gap; } // 与 Attribution 页 Exclude prompt patterns 同形;generated 仅本页有 UI(持久化键见 attributionExclude*PatternsStorage) .attribution-exclude-prompt-patterns-row { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; // 勾选行 ↔ Forced continuation:间距仅来自 .input-header padding-top(避免与本行 gap 叠成双份) &:has(.gen-attr-teacher-forcing-toggle-row) { gap: 0; } } // Teacher forcing / Stop after:勾选行与同页 semantic-submode 一致(字号、未勾选灰色);勾选+文本 inline-flex 对齐 .gen-attr-teacher-forcing-toggle-row label.semantic-submode-label, #gen_attr_teacher_forcing_block.chat-prompt-panel .gen-attr-stop-after-tf-row label.semantic-submode-label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; } // 强制续写块内 textarea 与「Stop after teacher forcing」;整块与下方 Model / Start 行的节奏(比照本列其它块 margin-top 10px) #gen_attr_teacher_forcing_block.chat-prompt-panel .gen-attr-stop-after-tf-row { margin-top: 8px; } .gen-attribute-page .input-section > .textarea-wrapper.chat-prompt-actions-row { margin-top: 10px; } // Start / Model 行与 Reset UI options 之间略加大;“重置”与下方 DAG layout 一行紧凑 .gen-attribute-page .input-section > .textarea-wrapper.chat-prompt-actions-row + .gen-attr-reset-ui-options-row { margin-top: 30px; } .gen-attr-reset-ui-options-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; } .gen-attr-reset-ui-options-row + .gen-attr-dag-measure-width-row { margin-top: 10px; } .attribution-exclude-prompt-patterns-header { flex-wrap: wrap; &:not(:has(#gen_attr_exclude_prompt_patterns_enable:checked)) .semantic-submode-label { color: var(--text-muted); } &:has(#gen_attr_exclude_prompt_patterns_enable:checked) .semantic-submode-label { color: var(--text-primary); } } .attribution-exclude-generated-patterns-header { flex-wrap: wrap; &:not(:has(#gen_attr_exclude_generated_patterns_enable:checked)) .semantic-submode-label { color: var(--text-muted); } &:has(#gen_attr_exclude_generated_patterns_enable:checked) .semantic-submode-label { color: var(--text-primary); } } // DAG 参数行(Text width / compactness 等);允许换行,避免单行被撑得过宽 .gen-attr-dag-measure-width-row { margin-top: 8px; flex-wrap: wrap; &.semantic-submode-row { gap: $gen-attr-option-row-gap; } } // ↯ 与 ▶ / ↻ 等同用 `--font-icon`,与 9pt 标签正文分离 .gen-attr-causal-flow-mode-label { display: inline-flex; align-items: center; gap: 4px; } .gen-attr-causal-flow-mode-icon { font-family: var(--font-icon); font-size: 1.5em; line-height: 1; flex-shrink: 0; } // 高 surprisal 两项勾选项:固定同一行,避免与其它 DAG 行一致的 wrap 把两项拆成两行 .gen-attr-dag-measure-width-row.gen-attr-dag-surprisal-toggles-row { flex-wrap: nowrap; } .gen-attr-dag-replay-speed-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; } // DAG 两行下拉:仅按英文选项收合宽度(不专门为译文留宽)。 .semantic-submode-row select.gen-attr-dag-layout-mode-select, .semantic-submode-row select.gen-attr-dag-replay-mode-select { width: fit-content; min-width: unset; max-width: none; text-align: left; } .gen-attr-dag-replay-value-wrap { align-items: center; gap: 4px; // 仅非 hidden 时设 flex,否则会覆盖浏览器对 [hidden] 的 display:none(两列输入会同时出现) &:not([hidden]) { display: inline-flex; } } .attribution-exclude-prompt-patterns-input { width: 100%; box-sizing: border-box; padding: 6px 8px; border: 1px solid var(--input-border); border-radius: 4px; background: var(--input-bg); color: var(--text-color); &:disabled { opacity: 0.55; cursor: not-allowed; } } // --- WORKAROUND:伪全屏时锁 body 滚动(同 ts/ui/cssPseudoFullscreen.ts) --- body.css-pseudo-fullscreen-body-lock { overflow: hidden; } // 保持在本文件末尾;说明见 responsive.narrow-ios-form-font @include ios-form-font.apply;