// Generate & Attribute 页:在 start + chat + attribution-inspector 之上叠本页差异 @use "attribution-inspector"; @use "generation-status"; @use "lmf-readout" as lmf; // 单行右对齐:Cached history 最右、下拉相对整行 bar 全宽;Cached demos 靠其左侧、下拉随按钮宽(同 query-history 默认 left/right 于窄 wrapper) .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: flex-start; 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:定位层仍在窄 wrapper 上;下拉本身用 max-content 加宽(见下方 #gen_attr_cached_demos_dropdown) .chat-cached-history-bar--dual .gen-attr-cached-demos-wrap { position: relative; } // History:不设定位层,子下拉以父级 bar 为 containing block,left/right:0 占满整行 .chat-cached-history-bar--dual .gen-attr-cached-history-wrap { position: static; } // Cached history / demos:随内容变高,封顶 min(32vh, 360px),竖向 resize;覆盖 chat 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; } // 覆盖 mixin 的 left/right:0(与按钮同宽):按标签内容加宽,右缘与按钮对齐、向左延伸;过长时由 max-width 限制,条内仍 ellipsis #gen_attr_cached_demos_dropdown.semantic-search-history-dropdown { left: auto; right: 0; width: max-content; min-width: max(100%, 240px); max-width: min(100vw - 32px, 40rem); 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: 20px; background: var(--text-area-bg); color: var(--text-color); overflow: visible; } @media (max-width: 767px) { #results.gen-attr-results-surface.LMF { padding: 15px 12px; } } // DAG:不可见测量层撑开高度,SVG 绝对定位叠在同栈内(坐标与 LMF 正文一致) // 初始 d3.zoom 缩放为 1/本变量,抵消 display-scale 在屏上的整体缩小感(见 genAttributeDagView) // 边色:`--dag-normal-line-color`、`--dag-highlight-line-color-in` / `out` 与节点描边 `--token-hover-outline` 同在 start.scss(见 genAttributeDagView)。 #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; } // 勾选「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) ); font-family: inherit; font-weight: inherit; fill: var(--text-color, #222); // 与 `xml:space="preserve"` 一致,避免部分引擎压扁/吞掉空格 white-space: pre; } // 节点框:默认无边框;悬停/选中描边与 start.scss `.token` 的 outline 2px 在屏上一致: // 几何按 --gen-attr-dag-display-scale 缩小后,genAttributeDagView 用 zoom 1/scale 拉回,故用户空间描边需 ×scale, // 使 2×s × (1/s) = 2(css px)。(s 可变时仍成立。) #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node > rect { stroke: none; stroke-width: 0; fill: var(--gen-attr-dag-generated-node-fill); } #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node--prompt > rect { fill: var(--gen-attr-dag-prompt-node-fill); } #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node--hover > rect, #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .gen-attr-dag-node--selected > rect { stroke: var(--token-hover-outline); stroke-width: calc(2 * var(--gen-attr-dag-display-scale, 1)); } // 与 genAttributeDagView 中「仅 default(text-flow) 且选中时可拖」一致:可拖时提示 grab #results.gen-attr-results-surface.LMF .gen-attr-dag-svg .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 { 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) #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; } // --- 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: 20px; 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; } // 紧凑数字输入(Max tokens、DAG Text width / compactness / 回放时长等共用) .gen-attr-max-tokens-input, .gen-attr-dag-measure-width-input { box-sizing: border-box; flex: 0 0 auto; min-width: 0; padding: 3px 6px; 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 { span.semantic-submode-label { color: var(--text-muted); } .semantic-submode-row:not(.attribution-exclude-prompt-patterns-header):not(.attribution-exclude-generated-patterns-header) { label.semantic-submode-label { color: var(--text-primary); } label.semantic-submode-label:has(> input[type='checkbox']:not(:checked)) { color: var(--text-muted); } } // Start 上方的 prompt 区标题与同系字号(非 .semantic-submode-row 后代时补齐 9pt) > .semantic-submode-row.chat-raw-prompt-mode-row label.semantic-submode-label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; } .chat-prompt-panel > .input-header > .semantic-submode-label { font-size: 9pt; } // System:在 input-header 内(非 semantic-submode-row),勾选主次色与勾选行一致 #gen_attr_system_prompt_panel.chat-prompt-panel > .input-header label.chat-use-system-label.semantic-submode-label { font-size: 9pt; &:has(> input[type='checkbox']:not(:checked)) { color: var(--text-muted); } &:has(> input[type='checkbox']:checked) { color: var(--text-primary); } } } // 与 Attribution 页 Exclude prompt patterns 同形;generated 仅本页有 UI(持久化键见 attributionExclude*PatternsStorage) .attribution-exclude-prompt-patterns-row { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; // Teacher forcing:与同页 Chat 模板的相邻面板间距(12px,见 chat.scss `#chat_input_panel`)对齐;容器默认 gap 仅 4px 会显得挤。 &:has(.gen-attr-teacher-forcing-toggle-row) { gap: 12px; } } // 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; } .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; } .gen-attr-dag-measure-width-row .gen-attr-dag-layout-mode-group { margin-right: 12px; .semantic-submode-label, .gen-attr-dag-layout-mode-select { font-weight: 700; } } .gen-attr-dag-measure-width-row .gen-attr-dag-replay-speed-row { > .semantic-submode-label, .gen-attr-dag-replay-mode-select { font-weight: 700; } } .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; font-size: 9pt; 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; }