InfoLens / client /src /css /pages /causal_flow.scss
dqy08's picture
动画交互改进,UI改进;chat页面改进
2764e14
Raw
History Blame Contribute Delete
20.5 kB
// 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;