@konnga/dsh-hud 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["React","React","React","React","React","React","React"],"sources":["../src/client/styles.ts","../src/client/shared.ts","../src/client/format.ts","../src/client/hooks/use-store.ts","../src/client/components/dock-pill.ts","../src/client/components/session-changes.ts","../src/client/dom.ts","../src/client/components/change-list.ts","../src/client/components/commit-list.ts","../src/client/components/commit-pane.ts","../src/client/components/count-chips.ts","../src/client/components/image-diff.ts","../src/client/components/diff-view.ts","../src/client/components/tag-list.ts","../src/client/hooks/panel.ts","../src/client/components/floating-panel.ts","../src/client/components/pill-tip.ts","../src/client/components/sidebar.ts","../src/client/components/turn-images.ts","../src/client/composer.ts","../src/client/i18n.ts","../src/client/store/attribution.ts","../src/client/store/commits.ts","../src/client/store/diff.ts","../src/client/store/image.ts","../src/client/store/session-info.ts","../src/client/store/tip.ts","../src/client/store/tools.ts","../src/client/store/index.ts","../src/client/styles-apply.ts","../src/client/plugin.ts"],"sourcesContent":["/**\n * 面板与胶囊的样式表。\n *\n * 放在数组里逐段拼(不是模板字符串),是为了每段都能单独读懂;\n * `ensureStyles()` 把它插进 `<head>`,用 `CSS_TAG_ID` 防重复注入。\n */\n\n/**\n * 只覆盖本插件自己的类名,配色一律走主题 CSS 变量,亮/暗主题自动跟随。\n * 不碰 document.body、window 或产品的 DOM 选择器。\n */\nexport const CSS = [\n // dock 座位给的是整栏宽的一行,而输入卡片有自己的最大宽度并居中\n // (--dsh-composer-card-max-width)。外层容器复刻框架自带 dock 占位元素\n // (队列公告)的几何,胶囊才会真正贴在输入卡片上方左端,而不是飘到栏的最左。\n '.dshh-dock{box-sizing:border-box;display:flex;align-items:center;gap:6px;justify-content:flex-start;',\n 'width:calc(100% - var(--dsh-composer-side-clearance,16px) - var(--dsh-composer-side-clearance,16px)',\n ' - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'max-width:calc(var(--dsh-composer-card-max-width,952px) - var(--dsh-composer-dock-inset,8px) - var(--dsh-composer-dock-inset,8px));',\n 'margin:0 auto;}',\n '.dshh-pill{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.6;',\n 'width:fit-content;max-width:100%;',\n 'display:inline-flex;align-items:center;gap:4px;',\n 'padding:2px 8px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:999px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-pill:hover{color:var(--dsw-alias-label-primary,#e5e7eb);border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n // 会话计时胶囊:只读读数,所以指针不该是\"可点\";数字用等宽数字,跳动时宽度不抖。\n '.dshh-timer{cursor:default;gap:5px;}',\n // 悬停气泡:`pointer-events:none` 保证它永远不挡指针;`pre-wrap` 保留调用方给的多行结构。\n '.dshh-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:420px;padding:6px 10px;border-radius:8px;' +\n 'background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,#2c2c2e));' +\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));' +\n 'box-shadow:0 6px 20px rgba(0,0,0,0.28);color:var(--dsw-alias-label-primary,#f9fafb);' +\n 'font-size:11.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word;pointer-events:none;}',\n '.dshh-timerGlyph{flex:none;opacity:.85;}',\n '.dshh-timerValue{font-variant-numeric:tabular-nums;}',\n '.dshh-panel{color:var(--dsw-alias-label-primary,#e5e7eb);font-size:12.5px;line-height:1.6;}',\n '.dshh-header{display:flex;align-items:center;gap:8px;padding:6px 8px;flex:none;cursor:move;line-height:1.4;',\n 'user-select:none;touch-action:none;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-title{font-size:13px;font-weight:600;flex:none;}',\n '.dshh-branch{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;padding:1px 6px;border-radius:6px;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:170px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-spacer{flex:1;}',\n // 固定盒模型:⟳/⌖/✕ 这类符号可能落到行高夸张的 fallback 字体上,\n // 不锁死宽高就会把标题栏整个撑高(面板上方出现一片空白)。\n '.dshh-icon{font:inherit;font-size:12px;line-height:1;width:22px;height:22px;padding:0;border-radius:6px;',\n 'display:inline-flex;align-items:center;justify-content:center;flex:none;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-icon:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-body{padding:10px;display:flex;flex-direction:column;gap:8px;overflow:auto;}',\n '.dshh-meta{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;}',\n // 不用 direction:rtl 做左省略号:它会把绝对路径开头的 “/” 重排到末尾。\n '.dshh-repo{font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-kv{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sectionTitle{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 计数芯片之间要留出横向间隔:11.5px 字号的描边胶囊挨在一起时看着是贴住的。\n '.dshh-chips{display:flex;flex-wrap:wrap;gap:6px 12px;}',\n '.dshh-chip{display:inline-flex;align-items:center;font-size:11.5px;padding:1px 8px;border-radius:999px;border:1px solid currentColor;}',\n '.dshh-clean{font-size:11.5px;color:var(--dsw-alias-state-success-primary,#3fb950);}',\n // 会话归因:概览下面那行 + 「只看本会话改的」开关 + 清单行首的标记。\n '.dshh-session{display:flex;align-items:baseline;gap:6px 10px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-sessionText{min-width:0;}',\n '.dshh-sessionToggle{font:inherit;font-size:11px;padding:0 6px;border-radius:999px;white-space:nowrap;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.35));background:transparent;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);cursor:pointer;}',\n '.dshh-sessionToggle:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));',\n 'color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-sessionToggle:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:1px;}',\n '.dshh-sessionToggleOn{border-color:var(--dsw-alias-brand-primary,#4b8bf5);',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-marker{flex:none;width:8px;text-align:center;font-size:9px;line-height:inherit;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-markerPossible{color:var(--dsw-alias-state-warn-primary,#d9a63c);opacity:.75;}',\n // 行尾的审阅标记:`○` 未审 / `✓` 这一版已审 / `✓`(黄) 改动已变。\n // `⋯` 菜单里那条只读摘要:像一条说明,不像一个可点的菜单项。\n '.dshh-menureadout{display:flex;flex-direction:column;gap:2px;padding:4px 10px 6px;',\n 'font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary,#8a8f98));',\n 'cursor:default;user-select:text;max-width:260px;}',\n '.dshh-menureadoutLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 悬停卡片里那块详情:跟着卡片自己的字号,只做行距与弱化。\n '.dshh-rowhover{display:flex;flex-direction:column;gap:2px;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);min-width:0;}',\n '.dshh-rowhoverLine{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',\n // 「已审 x/y」是个可点控件:同样不能用 label-dimmed(反相表面色,1.26:1 / 1.64:1),\n // 平时用次级文字色,悬停才升到正文色。\n '.dshh-review{flex:none;margin-left:auto;padding:0 2px;font-size:10.5px;cursor:pointer;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-review:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-reviewOn{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-reviewStale{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-ctag{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11px;color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n // 行首的 `○`(与本会话无关):要\"最不显眼\",但同样得看得见 —— 与上面同一个理由。\n '.dshh-markerOther{color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // 工具活动胶囊:与 Git 胶囊同形,但它是只读读数,指针不该是“可点”。\n // 与 codex-hud 对齐的几支色(它的默认配色表:git=magenta、gitBranch=cyan、project=yellow):\n // 产品没有青/品红的语义变量(只有 button-info-*,那是按钮用的),所以这两支写死值,\n // 但都走变量、可被覆盖。\n '.dshh-workspace{color:var(--dsw-alias-state-warn-primary,#d9a63c);flex:none;}',\n '.dshh-gitWrap{color:var(--dsh-hud-magenta,#d946ef);flex:none;}',\n '.dshh-branch{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-ahead{color:var(--dsh-hud-cyan,#22d3ee);flex:none;}',\n '.dshh-workspaceSep{color:var(--dsw-alias-label-tertiary,rgba(138,143,152,0.6));flex:none;}',\n '.dshh-toolsPill{cursor:default;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 配色对齐 codex-hud 的 activity-lines:只有状态符上色,名字/次数/分隔符都用正文色。\n '.dshh-toolsGlyph{flex:none;}',\n '.dshh-toolsGlyphOk{color:var(--dsw-alias-state-success-primary,#3fb950);}',\n '.dshh-toolsGlyphRun{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-toolsGlyphBad{color:var(--dsw-alias-state-error-primary,#f06a6a);}',\n // 唯一一处写死的颜色:codex-hud 把\"正在跑的工具名\"画成青色,产品没有对应的语义变量\n // (只有 button-info-*,那是按钮用的)。留 `--dsh-hud-tool-name` 作为覆盖钩子。\n '.dshh-toolsName{color:var(--dsh-hud-cyan,#22d3ee);}',\n // 技能名与工具名同色系但不同支:工具那支是青,技能走品牌色(一眼能分开)。\n '.dshh-skillName{color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-section{display:flex;flex-direction:column;gap:4px;}',\n '.dshh-list{display:flex;flex-direction:column;gap:1px;max-height:30vh;overflow:auto;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 改动行是按钮(点击看差异),所以要把浏览器的按钮默认样式清掉。\n '.dshh-row{display:flex;align-items:baseline;gap:8px;padding:2px 3px;border-radius:4px;',\n 'font:inherit;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-row:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-row:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n '.dshh-status{flex:none;width:10px;text-align:center;font-family:ui-monospace,Consolas,monospace;font-weight:600;}',\n '.dshh-path{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 和改动清单一样:标题下面一条分隔线(最近提交是最后一段,用它把两段分开)。\n '.dshh-commits{display:flex;flex-direction:column;gap:1px;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.16));padding-top:4px;}',\n // 提交行是按钮(点击看这次提交改了什么),因此要清掉浏览器默认的按钮样式。\n '.dshh-commit{display:flex;align-items:baseline;gap:8px;padding:1px 2px;border-radius:4px;font-size:11.5px;',\n 'font:inherit;font-size:11.5px;text-align:left;width:100%;box-sizing:border-box;color:inherit;background:transparent;',\n 'border:1px solid transparent;cursor:pointer;}',\n '.dshh-commit:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));}',\n '.dshh-commit:focus-visible{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // 打开着的那条提交(明细在右栏,左栏只高亮它)。\n '.dshh-commitActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n // 中间那一栏(提交明细,默认 240px):元信息头 + 这次提交的改动清单。\n // 与左栏同形:各自 border-right 分隔,内部自己滚。\n // 注意 flex 是 `0 1 auto` 而不是 `none`:窗口太窄时由它和差异栏一起被压缩,\n // 而不是把左栏挤出去(左栏是主面板,保持固定 380px)。\n '.dshh-commitpane{flex:0 1 auto;box-sizing:border-box;display:flex;flex-direction:column;',\n 'min-width:0;min-height:0;overflow:hidden;border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitHead{flex:none;display:flex;flex-direction:column;gap:2px;padding:6px 8px;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-commitTitle{display:flex;align-items:baseline;gap:6px;min-width:0;}',\n '.dshh-commitSubject{font-size:12.5px;line-height:1.5;color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-commitMeta{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);',\n 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n // 清单吃掉这一栏剩下的高度(标题跟着一起滚,栏本身不滚动)。\n '.dshh-commitFiles{flex:1 1 auto;min-height:0;overflow:auto;padding:6px 8px 8px;}',\n '.dshh-commitFiles .dshh-list{max-height:none;}',\n '.dshh-csha{flex:none;font-family:ui-monospace,Consolas,monospace;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-csubject{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-chead{flex:none;font-size:10px;line-height:1.4;padding:0 4px;border-radius:4px;',\n 'color:var(--dsw-alias-brand-primary,#4b8bf5);border:1px solid currentColor;}',\n '.dshh-cwhen{flex:none;font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-note{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-empty{padding:14px 12px;font-size:12px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 图片预览(二进制改动) ----\n '.dshh-imgWrap{flex:1 1 auto;min-height:0;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:8px;}',\n '.dshh-imgMeta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgDims{color:var(--dsw-alias-state-warn-primary,#d9a63c);}',\n '.dshh-imgBytes{font-family:ui-monospace,Consolas,monospace;}',\n '.dshh-imgPair{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap;}',\n '.dshh-imgPairSingle{flex-wrap:nowrap;}',\n // 固定档位(非\"适应宽度\")时:图片按固有像素算宽度,容器随内容撑开,外层横向滚动。\n '.dshh-imgPairZoomed{flex-wrap:nowrap;width:max-content;align-items:flex-start;}',\n '.dshh-imgPairZoomed .dshh-imgCell{flex:0 0 auto;}',\n '.dshh-imgPairZoomed .dshh-img{max-height:none;}',\n '.dshh-imgZoom{display:inline-flex;align-items:center;gap:2px;margin-left:auto;}',\n '.dshh-imgZoomBtn{border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));',\n 'background:var(--dsw-alias-bg-layer-1,transparent);color:inherit;cursor:pointer;font:inherit;',\n 'min-width:22px;height:20px;padding:0 6px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;}',\n '.dshh-imgZoomBtn:hover{color:var(--dsw-alias-label-primary,#e5e7eb);',\n 'border-color:var(--dsw-alias-border-l2,rgba(127,127,127,0.45));}',\n '.dshh-imgZoomValue{min-width:74px;font-variant-numeric:tabular-nums;}',\n '.dshh-imgCell{flex:1 1 150px;min-width:0;display:flex;flex-direction:column;gap:4px;}',\n '.dshh-imgLabel{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgNote{font-size:11px;color:var(--dsw-alias-label-secondary,#8a8f98);padding:6px 0;}',\n // 透明像素要有底色才看得出边界:棋盘格由四段半透明渐变拼出来,两种主题都不用另配颜色。\n '.dshh-img{max-width:100%;max-height:420px;object-fit:contain;border-radius:6px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.28));',\n 'background-image:linear-gradient(45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(-45deg,rgba(127,127,127,0.12) 25%,transparent 25%),',\n 'linear-gradient(45deg,transparent 75%,rgba(127,127,127,0.12) 75%),',\n 'linear-gradient(-45deg,transparent 75%,rgba(127,127,127,0.12) 75%);',\n 'background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0;}',\n // ---- 轮尾「本轮图片改动」条 ----\n '.dshh-imgstrip{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripTitle{flex:none;}',\n '.dshh-imgstripItems{display:flex;gap:6px;flex-wrap:wrap;}',\n '.dshh-imgstripItem{display:inline-flex;align-items:center;gap:2px;padding:2px 4px 2px 2px;',\n 'border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.25));border-radius:8px;',\n 'background:var(--dsw-alias-bg-layer-1,transparent);}',\n '.dshh-imgstripOpen{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;',\n 'color:inherit;cursor:pointer;padding:0;font:inherit;}',\n '.dshh-imgstripOpen:hover{color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-imgstripThumb{width:28px;height:28px;object-fit:cover;border-radius:5px;display:block;',\n 'background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));}',\n '.dshh-imgstripThumbEmpty{align-items:center;justify-content:center;font-size:8px;letter-spacing:0.02em;',\n 'display:flex;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-imgstripName{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}',\n '.dshh-imgstripTier{flex:none;opacity:.7;}',\n '.dshh-imgstripCompare{border:0;background:transparent;color:inherit;cursor:pointer;padding:0 2px;',\n 'font:inherit;opacity:.7;}',\n '.dshh-imgstripCompare:hover{opacity:1;color:var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-imgstripMore{flex:none;}',\n '.dshh-footer{display:flex;justify-content:space-between;gap:8px;padding:6px 10px;flex:none;',\n 'border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));',\n 'font-size:10.5px;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n // ---- 差异视图 ----\n '.dshh-workbench{display:flex;flex:1 1 auto;min-height:0;align-items:stretch;}',\n '.dshh-leftpane{flex:none;box-sizing:border-box;display:flex;flex-direction:column;min-height:0;overflow:hidden;',\n 'border-right:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-leftpane .dshh-list{max-height:none;}',\n '.dshh-rowActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));}',\n // 键盘选中的那行:选过还没打开时只它一个亮着,所以用描边而不是底色,\n // 与 .dshh-rowActive(正在看的那份差异)区分得开。\n '.dshh-rowCursor{outline:1px solid var(--dsw-alias-brand-primary,#4b8bf5);outline-offset:-1px;}',\n // hunk 头可点(折叠 / 展开),并显示\"当前 hunk\"——键盘 n/p 与 Space 作用在它身上。\n '.dshh-difrow-hunk{cursor:pointer;user-select:none;}',\n '.dshh-difrow-hunk:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.22));}',\n '.dshh-difrow-hunkActive{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.2));',\n 'box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary,#4b8bf5);}',\n '.dshh-hunkMark{text-align:right;}',\n '.dshh-grip{position:absolute;top:0;right:0;bottom:0;width:6px;cursor:col-resize;z-index:5;}',\n // 栏间那条抓握**必须占宽**:它是提交栏与差异栏之间的真实一格。绝对定位会让它贴到面板右边缘,\n // 与外面那条叠在一起(面板右边缘那条不占宽,靠宽度公式里预留的 6px 让位)。\n '.dshh-gripBetween{position:static;flex:none;width:6px;}',\n '.dshh-grip:hover{background:var(--dsw-alias-border-l2,rgba(127,127,127,0.35));}',\n '.dshh-diffview{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;}',\n '.dshh-toolbar{display:flex;align-items:center;gap:4px;padding:5px 8px;flex:none;overflow:hidden;',\n 'border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,0.22));}',\n '.dshh-difpath{font-family:ui-monospace,Consolas,monospace;font-size:11px;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:38%;}',\n '.dshh-difcounts{display:inline-flex;gap:6px;flex:none;font-family:ui-monospace,Consolas,monospace;font-size:11px;}',\n '.dshh-tab{font:inherit;font-size:11.5px;padding:2px 7px;border-radius:6px;flex:none;cursor:pointer;',\n 'border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-tab:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.16));color:var(--dsw-alias-label-primary,#e5e7eb);}',\n '.dshh-tabActive{color:var(--dsw-alias-label-primary,#e5e7eb);background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.18));}',\n '.dshh-diff{flex:1 1 auto;min-height:0;overflow:auto;padding:4px 0 8px;',\n 'font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.5;}',\n '.dshh-difrow{display:flex;align-items:flex-start;white-space:pre;}',\n '.dshh-difrow-hunk{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,0.14));',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);}',\n '.dshh-difrow-meta{color:var(--dsw-alias-label-secondary,#8a8f98);opacity:.85;}',\n '.dshh-difno{flex:none;width:46px;text-align:right;padding-right:8px;user-select:none;',\n 'color:var(--dsw-alias-label-secondary,#8a8f98);font-variant-numeric:tabular-nums;}',\n '.dshh-diftext{flex:1 1 auto;min-width:0;white-space:pre;overflow:hidden;text-overflow:ellipsis;}',\n '.dshh-diftext.dshh-wrap{white-space:pre-wrap;word-break:break-word;overflow:visible;text-overflow:clip;}',\n].join('')\n","/**\n * 浏览器半的**内核**:常量、存储助手、诊断探针,以及几个到处都用得着的纯函数。\n *\n * 它是最底层 —— store 各片、hook、组件都从这里 import,它自己不 import 任何本包模块,\n * 于是依赖方向永远是\"由外向内\",不会绕成环。\n */\n\nimport React from 'react'\n\nexport var h = React.createElement\n\n/* --------------------------------------------------------------- 领域形状 */\n\n/**\n * 下面这些 interface 是**浏览器半读到的线路形状**(宿主半 `src/host/types.ts` 的镜像)。\n *\n * 为什么集中放在这里:store 各片、hook、组件都从 shared import;类型只写一份,\n * 字段漂移才能一眼看见。宿主半与浏览器半各自打包,不能互相 import 类型,所以\n * 这是刻意的\"同形两份\",改线路形状时两边一起改。\n */\n\n/** 改动类型的八个键(与宿主半 `counts` 的键一致)。 */\nexport type HudCountKey =\n | 'modified'\n | 'added'\n | 'deleted'\n | 'renamed'\n | 'copied'\n | 'typeChanged'\n | 'untracked'\n | 'conflicted'\n\n/** git status 的分组计数(porcelain-v2 的八类)。 */\nexport interface HudStatusCounts {\n modified: number\n added: number\n deleted: number\n renamed: number\n copied: number\n typeChanged: number\n untracked: number\n conflicted: number\n}\n\n/** 改动清单里的一条:porcelain-v2 的两个字母 + 路径。 */\nexport interface HudChange {\n /** 展示用字母:优先 index 侧,index 为 `.` 时取 worktree 侧。 */\n status: string\n /** index 侧字母(未跟踪/未合并的记录是 `.` / `!`)。 */\n index?: string\n /** worktree 侧字母。 */\n worktree?: string\n /** 展示用路径;重命名/复制是 `旧路径 → 新路径`。 */\n path: string\n /** 新路径那一侧(git 命令当 pathspec 用)。 */\n file: string\n /** 重命名/复制的原路径;普通改动没有这个字段。 */\n origFile?: string | null\n}\n\n/** 文件 → “这一版”签名(`size.mtimeMs`,或 `missing`)。 */\nexport type ReviewSignatures = Record<string, string>\n\n/** `status` 端点的结果(工作区概览 + 改动清单)。 */\nexport interface HudStatus {\n /** 仓库根;不在仓库里时为 null。 */\n repo: string | null\n /** 当前分支(detached 时为短 oid);空仓库里也为 null。 */\n branch: string | null\n detached: boolean\n /** 短 oid(7 位);还没有提交时为 null。 */\n oid: string | null\n upstream: string | null\n ahead: number\n behind: number\n dirty: boolean\n counts: HudStatusCounts\n changes: HudChange[]\n truncated: boolean\n /** 「审阅进度」要的“这一版”签名;只在客户端明确要的时候才附上。 */\n review?: ReviewSignatures\n}\n\n/** diff 行的种类:增 / 删 / 上下文 / hunk 头 / git 自己的文件头。 */\nexport type DiffRowKind = 'add' | 'del' | 'ctx' | 'hunk' | 'meta'\n\n/** 差异里的一行。 */\nexport interface DiffRow {\n kind: DiffRowKind\n text: string\n /** 旧侧行号;新增行与 hunk/meta 为 null。 */\n oldLine: number | null\n /** 新侧行号;删除行与 hunk/meta 为 null。 */\n newLine: number | null\n}\n\n/** 一侧 diff 的解析结果。 */\nexport interface DiffSide {\n rows: DiffRow[]\n additions: number\n deletions: number\n hunks: number\n binary: boolean\n truncated: boolean\n}\n\n/** 工作区差异端点载荷:一个改动文件的两侧(工作区 / 已暂存)。 */\nexport interface WorktreeDiffPayload {\n repo: string | null\n path: string\n origPath: string | null\n /** 工作区里的未跟踪文件(`--no-index` 那条分支)。 */\n untracked: boolean\n worktree: DiffSide\n index: DiffSide\n}\n\n/** 右栏差异的 `data`:工作区差异是两侧,提交差异是单侧。 */\nexport type DiffPayload = WorktreeDiffPayload | DiffSide\n\n/** 差异的两种来源。 */\nexport type DiffSource = 'worktree' | 'commit'\n\n/** 差异的三种“侧”。 */\nexport type DiffSideName = 'worktree' | 'index' | 'commit'\n\n/** 右侧差异视图的状态(`state.diff`)。 */\nexport interface HudDiffState {\n source: DiffSource\n phase: string\n sha?: string\n shortSha?: string\n subject?: string\n path: string\n file: string\n origPath: string | null\n data: DiffPayload | null\n side: DiffSideName\n wrap?: boolean\n zoom?: number | null\n hunk?: number\n collapsed?: number[]\n errorCode?: string | null\n}\n\n/** 一侧图片可以预览:内容以 base64 附带,尺寸认出来才给。 */\nexport interface ImagePreview {\n kind: 'image'\n mime: string\n bytes: number\n width: number | null\n height: number | null\n base64: string\n}\n\n/** 一侧图片超过单侧上限:只报字节数,不塞内容。 */\nexport interface ImageTooLarge {\n kind: 'too-large'\n bytes: number\n limit: number\n}\n\n/** 一侧内容认不出是图片。 */\nexport interface ImageNotImage {\n kind: 'not-image'\n bytes: number\n}\n\n/** 一侧不存在(新文件还没有索引副本 / 文件被删掉)。 */\nexport interface ImageMissing {\n kind: 'missing'\n}\n\n/** 一侧内容的可预览状态。 */\nexport type ImageSide = ImagePreview | ImageTooLarge | ImageNotImage | ImageMissing\n\n/** `image` 端点载荷:一个二进制(图片)改动的两侧字节与像素尺寸。 */\nexport interface ImagePayload {\n repo: string | null\n /** 提交明细时才有(工作区/索引视图没有)。 */\n shortSha?: string\n path: string\n origPath: string | null\n side: string\n sha: string | null\n before: ImageSide\n after: ImageSide\n}\n\n/** `image` 请求:缓存键按这几个字段算。 */\nexport interface ImageRequest {\n cwd: string\n path: string\n origPath?: string | null\n side?: string | null\n sha?: string | null\n}\n\n/** 「本会话确定改过」的一个文件。 */\nexport interface SessionChange {\n path: string\n file: string\n writes: number\n edits: number\n firstAt: number\n lastAt: number\n lastTurn: number | null\n turns: number[]\n}\n\n/** 「可能相关」的一个路径(命令旁听来的启发式)。 */\nexport interface SessionChangeCandidate {\n path: string\n file: string\n hits: number\n firstAt: number\n lastAt: number\n reason: string\n lastTurn: number | null\n}\n\n/** 本会话跑过的一条 shell 命令。 */\nexport interface SessionCommand {\n tool: string\n command: string\n at: number\n turn: number | null\n failed: boolean\n readOnly: boolean\n}\n\n/** 「会话归因」这一片 store 的状态(`state.sessionChanges`)。 */\nexport interface SessionChangesState {\n phase: string\n /** `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)。 */\n files: Record<string, SessionChange> | null\n possible: Record<string, SessionChangeCandidate> | null\n commands: SessionCommand[]\n since: number | null\n dropped: number\n possibleDropped: number\n errorCode: string | null\n}\n\n/** 「第几轮 → 那一轮的请求预览」(`state.turns`,宿主 `turnOutline` 的归一结果)。 */\nexport interface TurnsState {\n available: boolean\n prompts: Record<number, string>\n latest: number | null\n}\n\n/** 归因与 git 现状核对后的视图(`sessionChangeView()` 的结果)。 */\nexport interface SessionChangeView {\n /** 本会话改过、且工作区里还有差异。 */\n mine: Record<string, SessionChange>\n /** 命令旁听出来的「可能相关」(只标没被确定认领的)。 */\n possible: Record<string, SessionChangeCandidate>\n turnPrompts: Record<number, string>\n latestTurn: number | null\n latestCount: number\n /** 第几轮 → 那一轮改了几个文件。 */\n byTurn: Record<number, number>\n mineCount: number\n possibleCount: number\n commandCount: number\n writingCommands: number\n commands: SessionCommand[]\n /** 本会话改过、但工作区里已经没有差异的文件。 */\n gone: string[]\n /** 本会话记录过的文件总数。 */\n tracked: number\n since: number | null\n dropped: number\n possibleDropped: number\n /** 工作区里有差异、但不是本会话动的条数。 */\n others: number\n}\n\n/** 审阅标记:这一版审过了(fresh)/ 之后又变了(stale)。 */\nexport type ReviewMark = 'fresh' | 'stale'\n\n/** 「审阅进度」视图(`reviewView()` 的结果)。 */\nexport interface ReviewView {\n marks: Record<string, ReviewMark>\n known: Record<string, true>\n done: number\n total: number\n staleCount: number\n}\n\n/** 存档里一个\"已审\"标记。 */\nexport interface ReviewedEntry {\n signature: string\n at: number\n}\n\n/** 存档:`工作区 + NUL + 文件 → { signature, at }`。 */\nexport type ReviewedMap = Record<string, ReviewedEntry>\n\n/** 面板位置。 */\nexport interface PanelPos {\n left: number\n top: number\n}\n\n/** 右栏里打开着哪几栏(`panelLayout()` 的结果)。 */\nexport interface PanelLayout {\n diff: boolean\n commit: boolean\n}\n\n/** 本地 UI 记忆(`dsh-hud.ui.v1`):只记界面开关与布局,不混业务数据。 */\nexport interface UiPrefs {\n onlySession?: boolean\n onlyLatest?: boolean\n open?: boolean\n pos?: PanelPos | null\n wrap?: boolean\n zoom?: number | null\n diffWidth?: number\n commitWidth?: number\n}\n\n/** 悬停气泡快照(`state.tip`)。 */\nexport interface TipState {\n key: string\n text: string\n rect: { left: number; top: number; right: number }\n}\n\n/** 最近提交里的一行(`log` 端点载荷里的一条)。 */\nexport interface HudLogEntry {\n sha: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n subject: string\n}\n\n/** 提交元信息(`commit` 端点载荷里的 `commit`)。 */\nexport interface HudCommitSummary {\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n}\n\n/** 提交明细里的一个改动文件(`git diff --name-status -z` 的一条)。 */\nexport interface HudCommitFile {\n status: string\n path: string\n file: string\n origFile?: string | null\n}\n\n/** 提交明细(右栏视图)的状态(`state.commit`)。 */\nexport interface HudCommitState {\n phase: string\n sha: string\n shortSha: string\n subject: string\n author: string\n date: string\n committedAt: number | null\n refs: string[]\n merge: boolean\n files: HudCommitFile[]\n truncated: boolean\n errorCode: string | null\n}\n\n/** 标签列表里的一条(`tags` 端点里的一条)。 */\nexport interface HudTag {\n name: string\n sha: string\n annotated: boolean\n date: string\n committedAt: number | null\n subject: string\n}\n\n/** 正在跑的工具(胶囊显示实时秒数用:秒数由客户端本地计时)。 */\nexport interface HudToolActive {\n callId: string | null\n name: string\n summary: string | null\n startedAt: number\n}\n\n/** 最近一次工具调用。 */\nexport interface HudToolRecent {\n name: string\n summary: string | null\n ok: boolean\n at: number\n ms: number | null\n}\n\n/** 某个工具跑了多少次、最近一次成功没有。 */\nexport interface HudToolCount {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 某个技能用了几次、最近一次成功没有。 */\nexport interface HudToolSkill {\n name: string\n count: number\n ok: boolean | null\n}\n\n/** 「工具活动」这一片 store 的状态(`state.tools`)。 */\nexport interface ToolsState {\n phase: string\n active: HudToolActive | null\n recent: HudToolRecent[]\n counts: HudToolCount[]\n skills: HudToolSkill[]\n skillsDropped: number\n total: number\n updatedAt: number | null\n}\n\n/** `session-time` 端点载荷:创建时刻 + 累计墙钟与轮次/步数。 */\nexport interface HudSessionTime {\n available: boolean\n sessionId?: string | null\n createdAt?: number | null\n turns?: number | null\n steps?: number | null\n llmMs?: number | null\n toolMs?: number | null\n now?: number\n}\n\n/** 会话时长快照(`state.sessionTime`):`sessionId` 用来判断\"这份数还是不是这个会话的\"。 */\nexport interface SessionTimeState {\n sessionId: string | null\n value: HudSessionTime | null\n}\n\n/** `prompt-history` 端点载荷。 */\nexport interface HudPromptHistory {\n available: boolean\n sessionId?: string | null\n prompts: string[]\n}\n\n/** prompt 历史快照(`state.promptHistory`)。 */\nexport interface PromptHistoryState {\n sessionId: string | null\n value: HudPromptHistory | null\n}\n\n/** 侧栏每条会话行要的摘要(`session-statuses` 里的一行)。 */\nexport interface HudSessionOverviewRow {\n sessionId: string\n cwd: string | null\n /** 确定改过的文件数(归因记录)。 */\n mine: number\n /** “可能相关”的历史候选路径数。 */\n possible: number\n /** “可能相关”里此刻仍然有差异的个数。 */\n possibleNow: number\n repo: boolean\n error: string | null\n /** 该工作区的 git 摘要还没算出来。 */\n pending?: boolean\n branch?: string | null\n detached?: boolean\n dirty?: boolean\n ahead?: number\n behind?: number\n counts?: HudStatusCounts\n}\n\n/** 「侧栏摘要」这一片 store 的状态(`state.rows`)。 */\nexport interface RowsState {\n phase: string\n byId: Record<string, HudSessionOverviewRow>\n unavailable: boolean\n truncated: boolean\n updatedAt: number | null\n}\n\n/** 紧凑计数简写(胶囊与侧栏 chip 共用)。 */\nexport interface CountShorthand {\n key: HudCountKey\n mark: string\n value: number\n}\n\n/** 侧栏一枚状态 chip 的\"事实摘要\"(`rowSummaryOf()` 的结果)。 */\nexport interface RowSummary {\n row: HudSessionOverviewRow\n mine: number\n possibleNow: number\n possible: number\n shorthands: CountShorthand[]\n changed: number\n dirty: boolean\n hasContent: boolean\n}\n\n/** 浏览器半共享 store 的快照。 */\nexport interface HudState {\n phase: string\n sessionId: string | null\n cwd: string | null\n status: HudStatus | null\n errorCode: string | null\n updatedAt: number | null\n panelOpen: boolean\n pos: PanelPos | null\n /** 胶囊所在 dock 行的视口顶部坐标。 */\n anchorTop: number | null\n diff: HudDiffState | null\n commit: HudCommitState | null\n /** 键盘高亮的那个文件(清单里的 `file`)。 */\n cursor: string | null\n commits: HudLogEntry[] | null\n commitsOid: string | null\n tags: HudTag[] | null\n tagsAt: number\n tagsTruncated: boolean\n diffWidth: number\n commitWidth: number\n sessionChanges: SessionChangesState\n onlySession: boolean\n onlyLatest: boolean\n /** 这一版每个改动文件的签名(`file → signature`)。 */\n review: ReviewSignatures\n tools: ToolsState\n sessionTime: SessionTimeState\n promptHistory: PromptHistoryState\n tip: TipState | null\n rows: RowsState\n reviewed: ReviewedMap\n turns: TurnsState\n}\n\n/**\n * 共享 store 的对外接口(hook / 组件只依赖它,不依赖 `createStore` 的实现)。\n *\n * 只列浏览器半真正用到的那些;方法参数用线路形状(`HudChange` / `ImageRequest` …),\n * 这样 hook 里传参也一起被检查到。\n */\nexport interface HudStore {\n getSnapshot(): HudState\n subscribe(listener: () => void): () => void\n bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void\n refresh(): Promise<unknown>\n openPanel(open: boolean): void\n togglePanel(): void\n movePanel(pos: PanelPos): void\n commitPanel(pos: PanelPos): void\n resetPanel(): void\n setAnchorTop(top: number | null): void\n setDiffWidth(width: number): void\n setCommitWidth(width: number): void\n persistLayout(): void\n openDiff(change: HudChange): Promise<unknown>\n loadImagePair(request: ImageRequest): Promise<unknown>\n cachedImagePair(request: ImageRequest): unknown\n loadSessionChanges(): Promise<unknown>\n loadSessionTime(sessionId: string): Promise<unknown>\n loadPromptHistory(sessionId: string): Promise<unknown>\n showTip(\n key: string,\n text: string,\n rect: { left: number; top: number; right: number } | null,\n ): void\n hideTip(key?: string): void\n tipHandlers(key: string, textOf: () => string): Record<string, unknown>\n closeDiff(): void\n closeDetail(): void\n setDiffSide(side: string): void\n toggleDiffWrap(): void\n setZoom(value: number | null): void\n openCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown>\n openCommitDiff(file: HudCommitFile): Promise<unknown>\n toggleCommit(commit: HudLogEntry | HudCommitSummary): Promise<unknown> | void\n closeCommit(): void\n stepFile(delta: number, open: boolean): HudChange | null\n openCursor(): HudChange | null\n stepHunk(delta: number): void\n toggleHunk(index: number | undefined): void\n toggleOnlySession(): void\n toggleOnlyLatest(): void\n refreshRows(): Promise<unknown>\n loadTools(): Promise<unknown>\n toggleReviewed(file: string): void\n clearReviewed(): void\n}\n\n/** 取词函数(框架把 `t` 交给座位;取不到会回落成键本身)。 */\nexport type Translate = (key: string, params?: Record<string, string>) => string\n\n/** 诊断探针(`createProbe()` 的返回)。 */\nexport interface Probe {\n log(reason: string, detail?: Record<string, unknown>): void\n}\n\n/* --------------------------------------------------------------- 常量 */\n\nexport var CHANNEL = '/dsh-hud-rpc'\nexport var NS = 'dshHud'\n\nexport var UI_KEY = 'dsh-hud.ui.v1'\n/**\n * 插件自诊断的留存键(最近 24 行):会话归因每一步的事实都往这里留一行。\n *\n * 为什么落在这里:客户端插件的异常与\"服务找不到\"都不会进宿主日志,界面出问题时往往只有\n * 有/没有两种观察;把每一步的事实留一行在 localStorage,用户不用开 DevTools 也能把它捞出来,\n * 排查完可以随手删掉。\n */\nexport var DEBUG_KEY = 'dsh-hud.debug'\n\n/** 轮询间隔:面板与胶囊共用,页面不可见时跳过。 */\nexport var POLL_MS = 8000\n/**\n * 侧栏行内 chip 的轮询节奏:比面板慢一档。\n *\n * 那些 chip 是\"扫一眼\"的信息(哪个会话脏了),不值得跟面板一样频繁;宿主侧还按目录\n * 缓存 + 去重,多会话共用一个目录只跑一次 git。\n */\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\nexport var ROWS_POLL_MS = 30000\n/**\n * 工具活动的节拍。比面板那 8s 密得多:宿主这一端是**内存读**(不碰 git、不跑子进程),\n * 密一点才能让\"正在跑什么\"真的看到;代价用\"内容没变就不 set\"抵掉。\n */\nexport var TOOLS_POLL_MS = 3000\n/** 胶囊上最多铺几个技能名(其余收成 `+k`,完整清单在悬停里)。 */\nexport var SKILLS_SHOWN = 2\n/** 面板默认宽度与“停靠在输入框上方”的锚点高度。 */\nexport var PANEL_WIDTH = 380\nexport var COMPOSER_OFFSET = 168\n\nexport var VIEWPORT_MARGIN = 8\n\n/** 超过这个位移才算拖动,避免把头部上的普通点击当成拖动。 */\nexport var DRAG_THRESHOLD_PX = 3\n/** 面板与胶囊所在 dock 行之间的间距。 */\nexport var PANEL_DOCK_GAP = 8\n/**\n * 中间那一栏(打开提交明细时:这次提交的元信息 + 改动清单)的宽度。\n * 它夹在左栏与差异栏之间,是固定的——右边缘那个抓握仍只调整差异栏。\n */\nexport var COMMIT_LIST_WIDTH = 240\n/** 提交明细栏的最小宽度(它装的是文件路径,不能太窄;默认 240 只是起点,可拖)。 */\nexport var COMMIT_MIN_WIDTH = 180\n\n/** diff 模式的面板:更宽,且用固定高度(避免 bottom 锚定下\"内容变高就跳位\")。 */\n/** 右栏(差异)默认宽度、下限,以及面板右边缘抓握的宽度。 */\nexport var DIFF_DEFAULT_WIDTH = 470\nexport var DIFF_MIN_WIDTH = 240\n\nexport var RESIZE_GRIP_WIDTH = 6\n\nexport var DIFF_PANEL_HEIGHT_VH = 0.72\n\n/** 改动类型的固定展示顺序,与宿主半的 counts 键一致。 */\nexport var COUNT_ORDER: HudCountKey[] = [\n 'modified',\n 'added',\n 'deleted',\n 'renamed',\n 'copied',\n 'typeChanged',\n 'untracked',\n 'conflicted',\n]\n\n/** 每类改动对应的主题色令牌(都带回退色,暗亮色主题都能看)。 */\nexport var COUNT_COLOR: Record<HudCountKey, string> = {\n modified: 'var(--dsw-alias-state-warn-primary, #d9a63c)',\n added: 'var(--dsw-alias-state-success-primary, #3fb950)',\n deleted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n // 与 codex-hud 的 `statEntries` 一一对应:R→cyan、C→brightBlue(我们的品牌蓝就是这个位)、\n // T→magenta、?→灰。M/A/D/! 本来就一致(yellow/green/red/critical)。\n // `?` 用 `label-secondary` 而不是 `label-dimmed`:后者是**反相的表面色**(浅 #e1e5ee、深 #43454a),\n // 当文字压在面板底色上只有 1.26:1 / 1.64:1 —— 看不见(issue:未跟踪计数读不出来)。\n // secondary 仍是中性灰、比彩色状态弱一档,但浅 5.8:1 / 深 10.4:1 都读得清。\n renamed: 'var(--dsh-hud-cyan, #22d3ee)',\n copied: 'var(--dsw-alias-brand-primary, #4b8bf5)',\n typeChanged: 'var(--dsh-hud-magenta, #d946ef)',\n untracked: 'var(--dsw-alias-label-secondary, #8a8f98)',\n conflicted: 'var(--dsw-alias-state-error-primary, #f06a6a)',\n}\n\n/**\n * 每个改动类型在胶囊里的简写符,沿用 codex-hud / 上游 statusline 的 git 标记约定:\n * M 已修改、A 已暂存新增、D 已删除、R 重命名、C 复制、T 类型改变、? 未跟踪、! 冲突。\n */\nexport var COUNT_MARK: Record<HudCountKey, string> = {\n modified: 'M',\n added: 'A',\n deleted: 'D',\n renamed: 'R',\n copied: 'C',\n typeChanged: 'T',\n untracked: '?',\n conflicted: '!',\n}\n\n/** 胶囊里分支名的显示上限;超出就中段省略,别把简写符挤出可见区。 */\nexport var PILL_BRANCH_MAX = 28\n/** 胶囊里工作区名最多几个字符(它在分支前面,不该抢分支的位置)。 */\nexport var PILL_WORKSPACE_MAX = 20\n\n/**\n * 差异行的底色:半透明,压在面板表面色之上,因此亮/暗主题都可读\n * (文本与行号仍走主题令牌)。\n */\nexport var DIFF_ADD_BG = 'rgba(63,185,80,0.12)'\nexport var DIFF_DEL_BG = 'rgba(240,106,106,0.13)'\n\nexport var DIFF_GUTTER_COLOR = 'var(--dsw-alias-label-secondary, #8a8f98)'\n\n/** prompt 历史的条数上限(与宿主半的 `PROMPT_HISTORY_MAX` 同一个数)。 */\nexport var PROMPT_HISTORY_MAX = 100\n\n/**\n * 时长格式化:**与 codex-hud 同一套口径**。\n *\n * 不足一分钟给 `<1m`;然后按 `Xd Yh Zm` 只列非零项(`45m` / `2h 14m` / `3d 2h`)。\n * 取的是 floor —— 计时器宁可比真实少一点,也不要\"明明刚过 1 分钟就写 2m\"。\n */\nexport function formatDuration(ms: unknown) {\n var safe = typeof ms === 'number' && isFinite(ms) ? Math.max(0, ms) : 0\n if (safe < 60000) return '<1m'\n var minutes = Math.floor(safe / 60000)\n var days = Math.floor(minutes / 1440)\n var hours = Math.floor((minutes % 1440) / 60)\n var rest = minutes % 60\n var parts = []\n if (days > 0) parts.push(String(days) + 'd')\n if (hours > 0) parts.push(String(hours) + 'h')\n if (rest > 0) parts.push(String(rest) + 'm')\n return parts.join(' ')\n}\n\n/** 固定缩放档位:与内置文档预览的菜单一致(25% / 50% / 100% / 150% / 200%)。 */\nexport var ZOOM_STEPS = [0.25, 0.5, 1, 1.5, 2]\n\n/** 落盘/读回的缩放值:只认档位里的数,其他一律当\"适应宽度\"。 */\nexport function readZoom(ui: UiPrefs | null | undefined): number | null {\n return ui && typeof ui.zoom === 'number' && ZOOM_STEPS.indexOf(ui.zoom) >= 0 ? ui.zoom : null\n}\n\n/**\n * 走一档缩放。\n *\n * `null`(适应宽度)出发:`+` 从 100% 开始、`−` 从 50% 开始 —— 和\"适应宽度\"的直觉一致,\n * 不用先经过 25% 才知道按了有反应。\n */\nexport function stepZoom(current: number | null | undefined, direction: number): number {\n if (current === null || current === undefined) return direction > 0 ? 1 : 0.5\n var index = ZOOM_STEPS.indexOf(current)\n if (index < 0) return direction > 0 ? 1 : 0.5\n var next = index + (direction > 0 ? 1 : -1)\n if (next < 0 || next >= ZOOM_STEPS.length) return current\n return ZOOM_STEPS[next]\n}\n\nexport function readUi(): UiPrefs {\n try {\n var raw = window.localStorage.getItem(UI_KEY)\n if (!raw) return {}\n // localStorage 是不可信输入(旧版本写的、手改过的都有可能):先当 unknown,收窄后再当 UiPrefs。\n var parsed: unknown = JSON.parse(raw)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as UiPrefs) : {}\n } catch (error) {\n return {}\n }\n}\n\nexport function writeUi(next: UiPrefs): void {\n try {\n window.localStorage.setItem(UI_KEY, JSON.stringify(next))\n } catch (error) {\n // 隐私模式等写入失败只影响“记住位置/展开状态”,不影响功能。\n }\n}\n\n/** 「审阅进度」的存档键:按\"工作区 + 文件\"记下审过的那一版签名。 */\nexport var REVIEWED_KEY = 'dsh-hud.reviewed.v1'\n/**\n * 存档键的分隔符:NUL。\n *\n * 用 `String.fromCharCode(0)` 而不是 `\"\\u0000\"` 字面量:路径里不可能有 NUL,而转义写法\n * 在源码与测试里极易被多转一层(写成字面量反斜杠),排查起来极其费劲。\n */\nexport var REVIEW_KEY_SEP = String.fromCharCode(0)\n\n/** 存档键:`工作区 NUL 文件`(同一个文件名在别的仓库里是另一码事)。 */\nexport function reviewKeyOf(cwd: string | null | undefined, file: string): string {\n return String(cwd || '') + REVIEW_KEY_SEP + file\n}\n\n/** 存档上限:超出丢最久没动的那些(不然用久了这个键会一直长)。 */\nexport var REVIEWED_MAX = 200\n\n/** 读取\"我审过哪些版本\":坏数据一律当空表(存档不该把插件带崩)。 */\nexport function readReviewed(): ReviewedMap {\n try {\n var raw = window.localStorage.getItem(REVIEWED_KEY)\n if (!raw) return {}\n // 与 readUi 同理:localStorage 里的 JSON 是不可信输入,逐步收窄而不是直接当目标类型用。\n var parsed: unknown = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n var source = parsed as Record<string, unknown>\n var out: ReviewedMap = {}\n for (var key in source) {\n var item = source[key]\n if (!Object.prototype.hasOwnProperty.call(source, key) || !item || typeof item !== 'object')\n continue\n // 同一系统边界:条目也是不可信 JSON,先收成表再逐个字段判断。\n var entry = item as Record<string, unknown>\n if (typeof entry.signature !== 'string') continue\n out[key] = { signature: entry.signature, at: typeof entry.at === 'number' ? entry.at : 0 }\n }\n return out\n } catch (error) {\n return {}\n }\n}\n\n/** 写回\"我审过哪些版本\":超上限就按最后触达时间丢最旧的。 */\nexport function writeReviewed(next: ReviewedMap): ReviewedMap {\n try {\n var keys = Object.keys(next)\n if (keys.length > REVIEWED_MAX) {\n keys.sort(function (left: string, right: string) {\n return (next[left].at || 0) - (next[right].at || 0)\n })\n var trimmed: ReviewedMap = {}\n var keep = keys.slice(keys.length - REVIEWED_MAX)\n for (var index = 0; index < keep.length; index += 1) trimmed[keep[index]] = next[keep[index]]\n next = trimmed\n }\n window.localStorage.setItem(REVIEWED_KEY, JSON.stringify(next))\n return next\n } catch (error) {\n // 写不进去只意味着\"这次不记住\",不影响功能。\n return next\n }\n}\n\n/* --------------------------------------------------------------- store */\n\n/**\n * 插件自诊断的现场记录:只写控制台,不碰界面、不留全局。\n *\n * 客户端插件的异常和\"服务找不到\"都不会进宿主日志,所以控制台是排查时唯一的通道。\n * 同一个 reason + 同样的事实只报一次(轮询每 8s 会重试),一行 JSON 便于复制。\n */\nexport function createProbe(): Probe {\n var seen: Record<string, true> = {}\n return {\n /** 记一行现场:一行进控制台,同时留一份在 localStorage(都按 reason+事实去重)。 */\n log: function (reason: string, detail?: Record<string, unknown>) {\n var facts = detail === undefined ? {} : detail\n var line = reason + ' ' + JSON.stringify(facts)\n if (seen[line] === true) return\n seen[line] = true\n console.warn('[dsh-hud] ' + line)\n try {\n var history = window.localStorage.getItem(DEBUG_KEY)\n var parsed: unknown = history === null ? [] : JSON.parse(history)\n var list: Array<{ at: number; line: string }> = Array.isArray(parsed) ? parsed : []\n list.push({ at: Date.now(), line: line })\n window.localStorage.setItem(DEBUG_KEY, JSON.stringify(list.slice(-24)))\n } catch (error) {\n // 隐私模式等写不进去只影响这行诊断,不影响功能。\n }\n },\n }\n}\n\n/**\n * 面板左栏那份工作区清单 —— **渲染与键盘导航共用这一份**。\n *\n * 过滤必须发生在一个地方:键盘选中用的是行下标,如果渲染时过滤了而导航没过滤,\n * ↑/↓ 就会落在看不见的行上。\n */\nexport function visibleChangeRows(state: HudState): HudChange[] {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var onlySession = state.onlySession === true\n var onlyLatest = state.onlyLatest === true\n if (!onlySession && !onlyLatest) return changes\n var view = sessionChangeView(state)\n // 归因还没到手时这些开关不该悄悄把清单清空:那时候它们还没有意义。\n if (view === null) return changes\n // 别名:`view` 被闭包捕获后 TS 不再保留\"非 null\"的收窄,这里先取出两份表。\n var mineOf = view.mine\n var possibleOf = view.possible\n var rows = changes\n if (onlySession) {\n // \"可能相关\"(命令旁听)也算本会话碰过的:过滤器的承诺是\"只看本会话动过的\"。\n rows = rows.filter(function (change) {\n return mineOf[change.file] !== undefined || possibleOf[change.file] !== undefined\n })\n }\n var latestTurn = view.latestTurn\n if (onlyLatest && latestTurn !== null) {\n // \"这一轮改了什么\":只留最近一轮确定动过的(\"可能相关\"没有可靠的回合归属,不收)。\n rows = rows.filter(function (change) {\n var record = mineOf[change.file]\n return record !== undefined && record.lastTurn === latestTurn\n })\n }\n return rows\n}\n\n/** 进程内唯一一份探针:两个座位共用,去重也一起算。 */\nexport var probe: Probe = createProbe()\n\n/**\n * 两个 sha 说的是不是同一次提交。\n *\n * 最近提交那份清单里是 **短 sha**,而提交明细里是宿主半解析回来的**完整 sha**,\n * 直接 `===` 永远配不上;按前缀对一下(git 自己认 rev 也是这个语义)。\n */\nexport function sameCommit(left: unknown, right: unknown): boolean {\n if (typeof left !== 'string' || typeof right !== 'string' || left === '' || right === '')\n return false\n return left === right || right.indexOf(left) === 0 || left.indexOf(right) === 0\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/** 图片请求的缓存键:把请求的每个字段都带上,免得两侧共用一个键。 */\nexport function imagePairKey(request: ImageRequest): string {\n return [\n request.cwd || '',\n request.path || '',\n request.origPath || '',\n request.side || '',\n request.sha || '',\n ].join('\\u0000')\n}\n\n/** 字节数:十进制 KB/MB,和文件管理器里看到的一致。 */\nexport function formatBytes(bytes: unknown): string {\n if (typeof bytes !== 'number' || !isFinite(bytes) || bytes < 0) return '?'\n if (bytes < 1000) return String(bytes) + ' B'\n if (bytes < 1000 * 1000) return (bytes / 1000).toFixed(1) + ' KB'\n return (bytes / 1000 / 1000).toFixed(2) + ' MB'\n}\n\n/** 图片类文件的扩展名。这里只做粗筛 —— 真正\"是不是图\"由宿主按文件头判断。 */\nexport var IMAGE_FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'avif']\n\n/* ------------------------------------------------------- 悬浮面板 */\n\n/**\n * 当前右栏里有哪几栏 —— 样式、夹位置、渲染三处必须用同一份判断,抽出来免得漂移。\n *\n * @returns `{ diff, commit }`:`diff` = 有工作区/提交差异,`commit` = 打开着提交明细。\n */\nexport function panelLayout(state: HudState): PanelLayout {\n return {\n diff: state.diff !== null && typeof state.diff === 'object',\n commit: state.commit !== null && typeof state.commit === 'object',\n }\n}\n\n/**\n * 面板宽度:单栏紧凑;打开右栏就按\"左栏 + [提交明细栏] + 抓握 + 差异栏\"算。\n *\n * 右栏(差异)宽度由面板右边缘的抓握拖动调整并记忆;中间那栏是固定宽度。\n * 视口不够宽时只把弹性的一栏(差异)压小——左栏与提交明细栏是有下限的固定栏。\n *\n * @param layout - `panelLayout()` 的结果。\n * @param diffWidth - 差异栏的目标宽度。\n */\nexport function panelWidthFor(\n layout: PanelLayout,\n diffWidth?: number | null,\n commitWidth?: number | null,\n): number {\n var hasCommit = layout.commit === true\n var hasDiff = layout.diff === true\n if (!hasCommit && !hasDiff) return PANEL_WIDTH\n // **逐栏钳到自己的下限**:只对总宽加一个 floor 是不够的 —— 存档里写着 40px 时,\n // 总宽仍然会按 40 算(floor 只保证\"不小于最小总和\",但差额会被算到别的栏头上)。\n var middle = hasCommit\n ? Math.max(COMMIT_MIN_WIDTH, typeof commitWidth === 'number' ? commitWidth : COMMIT_LIST_WIDTH)\n : 0\n var right = hasDiff\n ? Math.max(DIFF_MIN_WIDTH, typeof diffWidth === 'number' ? diffWidth : DIFF_DEFAULT_WIDTH)\n : 0\n // 两条抓握:面板右边缘那条(调差异栏;只有提交栏时调提交栏)—— 它是绝对定位、**不占宽**,\n // 但宽度里要预留 6px 让位,否则它压在内容上;栏间那条是真实的一格,占宽。\n var outerGrip = RESIZE_GRIP_WIDTH\n var innerGrip = hasCommit && hasDiff ? RESIZE_GRIP_WIDTH : 0\n var available = window.innerWidth - 2 * VIEWPORT_MARGIN\n return Math.min(PANEL_WIDTH + middle + innerGrip + right + outerGrip, available)\n}\n\n/** 把位置夹回视口内,避免拖到屏幕外再也点不到。 */\nexport function clampPos(left: number, top: number, width?: number | null): PanelPos {\n var panelWidth = typeof width === 'number' ? width : PANEL_WIDTH\n var maxLeft = Math.max(VIEWPORT_MARGIN, window.innerWidth - panelWidth - VIEWPORT_MARGIN)\n var maxTop = Math.max(VIEWPORT_MARGIN, window.innerHeight - 48)\n return {\n left: Math.min(Math.max(VIEWPORT_MARGIN, left), maxLeft),\n top: Math.min(Math.max(VIEWPORT_MARGIN, top), maxTop),\n }\n}\n\n/**\n * 量出胶囊所在 dock 行的视口顶部坐标。\n *\n * 这里用**自己的** `data-dsh-hud` 标记,不碰产品的 DOM 选择器。dsh-git 把\n * \"输入框堆叠高 168px\"写成了常量并注明那只是一次测量;直接量真实位置可以同时\n * 适配 hero(空白会话,输入框居中)与 active 两种布局。\n *\n * @returns 视口坐标的顶部像素,量不到时返回 null。\n */\n/**\n * 面板当前该有多宽:从 store **现读**(模式切换会改宽度,别用可能过期的渲染闭包)。\n *\n * 原来它是组件里的 `panelWidthOf(store)`,被\"视口夹取\"和\"拖动\"两处用;提成顶层\n * 纯函数后两边共用同一份判断。\n */\nexport function panelWidthOf(store: HudStore): number {\n var snapshot = store.getSnapshot()\n return panelWidthFor(panelLayout(snapshot), snapshot.diffWidth, snapshot.commitWidth)\n}\n\n/** 自有样式表的稳定 tag id:`data-plugin-css` 用它做幂等插入。 */\nexport var CSS_TAG_ID = 'dsh-hud/styles.css'\n\n/* --------------------------------------------------- 会话归因(对账) */\n\n/**\n * 把\"本会话改过哪些文件\"(宿主半按 fs 事件记的)与 git 现状对起来。\n *\n * - `mine`:本会话改过、且工作区里还有差异(`status.changes` 里能找到)\n * - `gone`:本会话改过、但工作区里已经没有差异(被改回去了 / 被别的会话或你自己覆盖了)\n * - `others`:工作区里有差异、但不是本会话动的\n *\n * 名字能直接对上:宿主报的是 cwd 之内的 posix 相对路径,`status.changes[].file` 也是。\n * 还拿不到归因(老宿主 / 请求还没回来)时返回 `null` —— 界面这时**不打任何标记**,\n * 而不是把所有改动都标成\"别人的\"。\n */\nexport function sessionChangeView(state: HudState): SessionChangeView | null {\n // 快照可能来自老宿主/旧存档:这里保留\"缺字段就当没有\"的老行为,不让界面崩。\n var slice: SessionChangesState | null | undefined = state.sessionChanges\n if (slice === null || slice === undefined) return null\n var files = slice.files\n if (files === null || files === undefined) return null\n var possibleAll: Record<string, SessionChangeCandidate> | null =\n slice.possible === undefined ? null : slice.possible\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var mine: Record<string, SessionChange> = {}\n var possible: Record<string, SessionChangeCandidate> = {}\n var gone: string[] = []\n var index\n var change\n // 先认\"确定改过\"的:一档优先于一档,同一个文件不会被标两次。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (Object.prototype.hasOwnProperty.call(files, change.file))\n mine[change.file] = files[change.file]\n }\n // 再看\"可能相关\":只标没有被确定认领的那些(命令旁听来的,置信度低一档)。\n for (index = 0; index < changes.length; index += 1) {\n change = changes[index]\n if (mine[change.file] !== undefined) continue\n if (possibleAll !== null && Object.prototype.hasOwnProperty.call(possibleAll, change.file)) {\n possible[change.file] = possibleAll[change.file]\n }\n }\n for (var file in files) {\n if (Object.prototype.hasOwnProperty.call(files, file) && mine[file] === undefined)\n gone.push(file)\n }\n gone.sort()\n var commands: SessionCommand[] = Array.isArray(slice.commands) ? slice.commands : []\n var turns =\n state.turns !== undefined && state.turns !== null && state.turns.available === true\n ? state.turns\n : null\n var latestTurn = turns === null ? null : turns.latest\n var byTurn: Record<number, number> = {}\n var latestCount = 0\n for (var mineFile in mine) {\n if (!Object.prototype.hasOwnProperty.call(mine, mineFile)) continue\n var turn = typeof mine[mineFile].lastTurn === 'number' ? mine[mineFile].lastTurn : null\n if (turn === null) continue\n byTurn[turn] = (byTurn[turn] === undefined ? 0 : byTurn[turn]) + 1\n if (latestTurn !== null && turn === latestTurn) latestCount += 1\n }\n return {\n mine: mine,\n possible: possible,\n /** 第几轮 → prompt 预览(来自 `turnOutline` 投影;没有就是空表)。 */\n turnPrompts: turns === null ? {} : turns.prompts,\n latestTurn: latestTurn,\n latestCount: latestCount,\n byTurn: byTurn,\n mineCount: Object.keys(mine).length,\n possibleCount: Object.keys(possible).length,\n /** 跑过的命令里\"看着会写文件\"的条数(含失败的,界面自己再分)。 */\n commandCount: commands.length,\n writingCommands: commands.filter(function (item) {\n return item && item.readOnly !== true\n }).length,\n commands: commands,\n gone: gone,\n /** 本会话记录过的文件总数(宿主那边的集合大小)。 */\n tracked: Object.keys(files).length,\n since: slice.since === undefined ? null : slice.since,\n dropped: slice.dropped === undefined ? 0 : slice.dropped,\n possibleDropped: slice.possibleDropped === undefined ? 0 : slice.possibleDropped,\n others: changes.length - Object.keys(mine).length,\n }\n}\n\nexport function reviewView(state: HudState): ReviewView {\n var changes = state.status && Array.isArray(state.status.changes) ? state.status.changes : []\n var signatures: ReviewSignatures =\n state.review && typeof state.review === 'object' ? state.review : {}\n var reviewed = state.reviewed && typeof state.reviewed === 'object' ? state.reviewed : {}\n var prefix = reviewKeyOf(state.cwd, '')\n var marks: Record<string, ReviewMark> = {}\n var known: Record<string, true> = {}\n var done = 0\n var total = 0\n var staleCount = 0\n for (var index = 0; index < changes.length; index += 1) {\n var file = changes[index].file\n var signature = typeof signatures[file] === 'string' ? signatures[file] : null\n if (signature === null) continue\n known[file] = true\n total += 1\n var mark = reviewed[prefix + file]\n if (mark === undefined) continue\n if (mark.signature === signature) {\n marks[file] = 'fresh'\n done += 1\n } else {\n marks[file] = 'stale'\n staleCount += 1\n }\n }\n return { marks: marks, known: known, done: done, total: total, staleCount: staleCount }\n}\n","/**\n * 展示用的格式化:分支名、工作区名、相对时间、计数简写。\n *\n * 都是纯函数(输入数据、输出字符串),所以可以直接单测,也让组件里不再夹带这类判断。\n */\n\nimport type {\n CountShorthand,\n HudCountKey,\n HudSessionOverviewRow,\n HudStatusCounts,\n RowSummary,\n Translate,\n} from './shared.js'\nimport { COUNT_MARK, COUNT_ORDER, PILL_BRANCH_MAX, PILL_WORKSPACE_MAX } from './shared.js'\n/** 从中间省略:保留头尾(路径/分支名两头的区分度都比中间高)。 */\nexport function middleEllipsis(name: string, max: number): string {\n if (name.length <= max) return name\n var head = Math.ceil((max - 1) / 2)\n var tail = max - 1 - head\n return name.slice(0, head) + '…' + name.slice(name.length - tail)\n}\n\n/**\n * 工作区名 = 会话 cwd 的目录名(侧栏给工作区起的名字就是它)。\n *\n * 取的是 **cwd** 而不是仓库顶层:会话开在子目录时,用户认的是那个子目录;\n * 完整路径在悬停与面板里,胶囊上只要一个能认出来的短名字。\n */\nexport function workspaceNameOf(cwd: string | null | undefined): string | null {\n if (typeof cwd !== 'string' || cwd.length === 0) return null\n var trimmed = cwd.replace(/[/\\\\]+$/, '')\n if (trimmed.length === 0) return null\n var index = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'))\n var name = index === -1 ? trimmed : trimmed.slice(index + 1)\n if (name.length === 0) return null\n return middleEllipsis(name, PILL_WORKSPACE_MAX)\n}\n\n/** 分支名过长时中段省略,保留头尾信息量。 */\nexport function shortBranch(name: string): string {\n return middleEllipsis(name, PILL_BRANCH_MAX)\n}\n\n/**\n * 改动计数 → 简写符列表 `[{ key, mark, value }]`(只列非零项,顺序固定)。\n *\n * 胶囊与侧栏行内 chip 共用这一份:两处各写一套算法一定会漂。\n */\nexport function countShorthands(counts: HudStatusCounts | null | undefined): CountShorthand[] {\n var out: CountShorthand[] = []\n var source: Partial<HudStatusCounts> = counts || {}\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = source[key] || 0\n if (value <= 0) continue\n out.push({ key: key, mark: COUNT_MARK[key], value: value })\n }\n return out\n}\n\nexport function branchLabel(\n status: { branch?: string | null; oid?: string | null } | null | undefined,\n t: Translate,\n): string {\n if (!status) return '—'\n if (status.branch) return status.branch\n if (status.oid) return t('panel.detached') + ' ' + status.oid\n return t('panel.detached')\n}\n\nexport function timeLabel(at: number): string {\n var date = new Date(at)\n var pad = function (value: number) {\n return value < 10 ? '0' + String(value) : String(value)\n }\n return pad(date.getHours()) + ':' + pad(date.getMinutes()) + ':' + pad(date.getSeconds())\n}\n\n/* --------------------------------------------------------- 胶囊(dock) */\n\n/**\n * 会话作用域的座位:框架给出 `sessionId`,`useSessions` 快照给出该会话的 `cwd`,\n * 因此它是唯一能完成绑定的座位。\n */\n/**\n * 侧栏每条会话行上的状态 chip(座位 `sidebar.workspaces.session.row.action`)。\n *\n * 一行只有 32px 高、悬停才露出来,所以只放**最该一眼看到的两件事**:这个工作区脏不脏\n * (git 简写符)与**这个会话自己改过几个文件**(`●n`,只我们有这个信息)。干净且本会话\n * 没改过就整枚不画 —— 不占位置、也不制造噪音。完整信息在悬停里。\n */\n/**\n * 一枚侧栏状态的\"事实摘要\":三种呈现(常亮标记 / 悬停动条 / 卡片详情)共用这一份计算。\n *\n * 同一份数据要在三个地方以三种宽度呈现,各写一套一定会漂 —— 所以抽成一处。\n */\nexport function rowSummaryOf(row: HudSessionOverviewRow | null | undefined): RowSummary | null {\n if (row === undefined || row === null || row.pending === true) return null\n var mineCount = typeof row.mine === 'number' ? row.mine : 0\n var possibleCount = typeof row.possible === 'number' ? row.possible : 0\n var shorthands: CountShorthand[] = row.repo === true ? countShorthands(row.counts) : []\n var changed = 0\n for (var index = 0; index < shorthands.length; index += 1) changed += shorthands[index].value\n return {\n row: row,\n mine: mineCount,\n possibleNow: typeof row.possibleNow === 'number' ? row.possibleNow : 0,\n possible: possibleCount,\n shorthands: shorthands,\n changed: changed,\n dirty: row.repo === true && row.dirty === true,\n hasContent: changed > 0 || mineCount > 0 || possibleCount > 0 || row.error !== null,\n }\n}\n\n/** 相对时间;超过 30 天直接给提交自己的日期。 */\nexport function commitWhen(\n commit: { committedAt?: number | null; date?: string | null },\n t: Translate,\n): string {\n if (typeof commit.committedAt !== 'number') return commit.date || ''\n return fullWhen(commit.committedAt)\n}\n\n/**\n * 时刻一律给完整年月日 + 时分(本地时区):`2026-10-16 14:18`。\n *\n * 两级简化都被否掉了:先是从\"2 小时前\"改成具体时间(相对时间在对账场景不够用),\n * 再是\"今天只给 `14:18`\"也被否 —— 用户要**年月日都在**。所以不再分档,一视同仁。\n * 只到分钟:提交/标签不需要秒,列一排在面板里也塞得下。\n */\nexport function fullWhen(at: number): string {\n var then = new Date(at)\n return (\n String(then.getFullYear()) +\n '-' +\n pad2(then.getMonth() + 1) +\n '-' +\n pad2(then.getDate()) +\n ' ' +\n pad2(then.getHours()) +\n ':' +\n pad2(then.getMinutes())\n )\n}\n\nexport function pad2(value: number): string {\n return value < 10 ? '0' + String(value) : String(value)\n}\n\n/** porcelain-v2 的展示字母 → counts 键(用于配色)。 */\nexport function statusKey(letter: string): HudCountKey {\n if (letter === 'M') return 'modified'\n if (letter === 'A') return 'added'\n if (letter === 'D') return 'deleted'\n if (letter === 'R') return 'renamed'\n if (letter === 'C') return 'copied'\n if (letter === 'T') return 'typeChanged'\n if (letter === '?') return 'untracked'\n if (letter === '!') return 'conflicted'\n return 'typeChanged'\n}\n\n/** 宿主半的 `details.code` → 本座位的本地化文案(store 只存机器码)。 */\nexport function errorText(code: string | null | undefined, t: Translate): string {\n if (code === 'timeout') return t('err.timeout')\n if (code === 'no-cwd') return t('err.no-cwd')\n if (code === 'git-missing') return t('err.git-missing')\n if (code === 'invalid-cwd') return t('err.invalid-cwd')\n if (code === 'transport') return t('err.transport')\n if (code === 'invalid-path') return t('err.invalid-path')\n // 提交可能在两次请求之间被 rebase / amend 换掉:给一句能操作的提示,而不是\"git 执行失败\"。\n if (code === 'unknown-commit') return t('commit.unknown')\n // 宿主半是启动时加载的:只刷新页面时,新端点还不存在,这个提示能省下一轮排查。\n if (code === 'unknown-endpoint') return t('err.stale-host')\n return t('err.git-failed')\n}\n","/**\n * 订阅共享 store 的经典写法(不依赖种子里的 `useSyncExternalStore` 构建)。\n */\n\nimport React from 'react'\n\nimport type { HudState, HudStore } from '../shared.js'\n\n/* ---------------------------------------------------------- 订阅 hook */\n\n/** 订阅共享 store 的经典写法(不依赖种子里的 useSyncExternalStore 构建)。 */\nexport function useStore(store: HudStore): HudState {\n var pair = React.useState(function () {\n return store.getSnapshot()\n })\n React.useEffect(\n function () {\n return store.subscribe(function () {\n pair[1](store.getSnapshot())\n })\n },\n [store],\n )\n return pair[0]\n}\n","/**\n * 输入框上方那一行里的胶囊:分支摘要 / 非 git 仓库,点击开关面板;\n * 含两枚工具胶囊、技能清单与悬停气泡的锚点。\n */\n\nimport React from 'react'\n\nimport { branchLabel, countShorthands, fullWhen, shortBranch, workspaceNameOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudState,\n HudStatusCounts,\n HudStore,\n HudToolActive,\n ToolsState,\n Translate,\n} from '../shared.js'\nimport {\n COUNT_COLOR,\n COUNT_ORDER,\n SKILLS_SHOWN,\n formatDuration,\n h,\n probe,\n sessionChangeView,\n} from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\nexport interface SessionSummaryLike {\n cwd?: string | null\n /** 这个会话被哪些视图保留着;`mainView` 为 0 时说明它在内嵌的子会话里。 */\n retainedBy?: { mainView?: number | null } | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\nexport interface SessionsSnapshotLike {\n byId?: Record<string, SessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\nexport type UseSessions = <T>(selector: (snapshot: SessionsSnapshotLike) => T) => T\n\n/** `HudDockPill` 的 props:store + 框架注入的会话作用域信息。 */\nexport interface HudDockPillProps {\n t?: Translate\n /** 当前会话(座位按会话作用域注入)。 */\n sessionId?: string | null\n useSessions: UseSessions\n store: HudStore\n /** 测试/调试用:固定\"现在\",省得等本地计时器。 */\n now?: number\n}\n\n/** 工具胶囊的选项。 */\nexport interface ToolPillOptions {\n t: Translate\n store: HudStore\n now: number\n}\n\n/** 计时胶囊的选项。 */\nexport interface TimerPillOptions {\n t: Translate\n store: HudStore\n sessionId: string | null | undefined\n now: number\n}\n\nexport function HudDockPill(props: HudDockPillProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var store = props.store\n\n // useSessions 是选择器 hook:选择器参数是必需的,裸调用会以\n // \"selector is not a function\" 崩掉。\n var sessions = useSessions(function (snapshot) {\n return snapshot\n })\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n // 右栏里的嵌入式会话(子会话 chat)自带会话绑定;只有主视图座位可以驱动共享\n // 面板,否则一个子会话会把它下面的仓库从用户正在看的会话下抽走。\n // 快照不带这个计数(更旧的宿主)时按主视图放行,胶囊不会因此消失。\n var mainView = !summary || !summary.retainedBy ? true : (summary.retainedBy.mainView || 0) > 0\n\n var state = useStore(store)\n\n /**\n * 工具胶囊的\"实时秒数\"滴答。\n *\n * **必须在组件体里、无条件、固定顺序地调用**:早先放在普通函数 `toolPillNode` 里、还带\n * `if (now === undefined)` 的条件,就是条件钩子 —— React 会抛错,导致整行 dock 渲染失败,\n * 两枚胶囊一起消失(用户实测\"胶囊都不显示了\")。回调里再判断要不要开定时器就没这问题。\n */\n var tick = React.useState(Date.now())\n var tickSet = tick[1]\n var toolsTickKey =\n state.tools && state.tools.active !== null && state.tools.active !== undefined\n ? String(state.tools.active.callId)\n : null\n // 会话计时 + 输入框 ↑ 召回的数据(两个 loader 各自带 TTL,这里按最密的节奏问就行)。\n React.useEffect(\n function () {\n if (typeof sessionId !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`var` 变量在回调里 TS 不再保留)。\n const id = sessionId\n var load = function () {\n if (store.loadSessionTime !== undefined) store.loadSessionTime(id)\n if (store.loadPromptHistory !== undefined) store.loadPromptHistory(id)\n }\n load()\n var poll = window.setInterval(load, 3000)\n return function () {\n window.clearInterval(poll)\n }\n },\n [sessionId],\n )\n\n // 挂钟每 4 秒跳一次:计时胶囊的显示粒度是分钟,但跨分钟不用等下一次取数。\n React.useEffect(function () {\n var clock = window.setInterval(function () {\n tickSet(Date.now())\n }, 4000)\n return function () {\n window.clearInterval(clock)\n }\n }, [])\n\n React.useEffect(\n function () {\n if (toolsTickKey === null) return undefined\n var timer = window.setInterval(function () {\n tickSet(Date.now())\n }, 500)\n return function () {\n window.clearInterval(timer)\n }\n },\n [toolsTickKey],\n )\n\n React.useEffect(\n function () {\n if (!mainView) return\n store.bindSession(sessionId, cwd)\n },\n [sessionId, cwd, mainView, store],\n )\n\n if (!cwd || !mainView || state.sessionId !== sessionId) return null\n\n // 会话归因的胶囊段:只有\"确实改过东西\"时才露头(没数据 / 0 个都不显示)。\n var sessionView = sessionChangeView(state)\n var sessionSegment =\n sessionView !== null && sessionView.mineCount > 0\n ? ' │ ●' + String(sessionView.mineCount)\n : null\n\n var children: React.ReactNode[] = []\n // 工作区名放在最前、且与分支用 `·` 隔开:写成 `git:(dsh-hud/main*)` 会被读成\"分支叫 dsh-hud/main\"。\n // 三种\"非仓库\"状态也带上它 —— 工作区跟 git 没关系,没仓库时它反而更有用。\n var workspace = workspaceNameOf(cwd)\n if (workspace !== null) {\n children.push(h('span', { key: 'workspace', className: 'dshh-workspace' }, workspace))\n children.push(h('span', { key: 'workspaceSep', className: 'dshh-workspaceSep' }, ' · '))\n }\n if (state.phase === 'error') {\n children.push(\n h('span', { key: 'gitWraperr', className: 'dshh-gitWrap' }, 'git:(' + t('pill.error') + ')'),\n )\n } else if (state.phase === 'loading' || state.phase === 'idle') {\n children.push(\n h(\n 'span',\n { key: 'gitWrapload', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.loading') + ')',\n ),\n )\n } else if (!state.status || !state.status.repo) {\n children.push(\n h(\n 'span',\n { key: 'gitWrapnorepo', className: 'dshh-gitWrap' },\n 'git:(' + t('pill.notRepo') + ')',\n ),\n )\n } else {\n var status = state.status\n // `git:(branch*)` —— codex-hud 的 gitSegment 就是这么拼的,配色也照它:\n // `git:(` 与 `)` 品红(colors.git)、分支名与 ↑↓ 青(colors.gitBranch),\n // 而 `*` 在括号**里面**、与分支名同色。\n children.push(h('span', { key: 'gitOpen', className: 'dshh-gitWrap' }, 'git:('))\n children.push(\n h(\n 'span',\n { key: 'branch', className: 'dshh-branch' },\n shortBranch(branchLabel(status, t)) + (status.dirty ? '*' : ''),\n ),\n )\n children.push(h('span', { key: 'gitClose', className: 'dshh-gitWrap' }, ')'))\n if (status.ahead > 0)\n children.push(\n h('span', { key: 'ahead', className: 'dshh-ahead' }, ' ↑' + String(status.ahead)),\n )\n if (status.behind > 0)\n children.push(\n h('span', { key: 'behind', className: 'dshh-ahead' }, ' ↓' + String(status.behind)),\n )\n\n // 简写符:只列非零项,按固定顺序,各自上色(M 黄 / A 绿 / D 红 / ? 灰 …)。\n // 与侧栏行内 chip 共用 `countShorthands` —— 两处各写一套算法一定会漂。\n var shorthands = countShorthands(status.counts)\n for (var index = 0; index < shorthands.length; index += 1) {\n children.push(' ')\n children.push(\n h(\n 'span',\n {\n key: shorthands[index].key,\n style: { color: COUNT_COLOR[shorthands[index].key] },\n },\n shorthands[index].mark + String(shorthands[index].value),\n ),\n )\n }\n }\n\n if (sessionSegment !== null) children.push(sessionSegment)\n\n // 悬停给出完整信息:总数型的托盘里放不下细节,这里补齐。\n var titleLines: string[] = [t('pill.title')]\n if (sessionView !== null) titleLines.push(t('session.pill', { n: String(sessionView.mineCount) }))\n if (cwd) titleLines.push(cwd)\n if (state.status && state.status.repo) {\n var tip = branchLabel(state.status, t)\n if (state.status.upstream) tip += ' · ' + t('panel.upstream') + ' ' + state.status.upstream\n titleLines.push(tip)\n var labels: string[] = []\n var counts2: Partial<HudStatusCounts> = state.status.counts || {}\n for (var i2 = 0; i2 < COUNT_ORDER.length; i2 += 1) {\n var k2 = COUNT_ORDER[i2]\n var v2 = counts2[k2] || 0\n if (v2 > 0) labels.push(t('count.' + k2) + ' ' + String(v2))\n }\n if (labels.length > 0) titleLines.push(labels.join(' · '))\n }\n\n // 外层容器复刻框架自己的 dock 行几何(见 queue 公告):仅靠胶囊自身对齐,\n // 胶囊会飘到整栏的最左边,而输入卡片是居中的。\n return h(\n 'div',\n { className: 'dshh-dock', 'data-dsh-hud': 'dock' },\n h(\n 'button',\n Object.assign(\n {\n type: 'button',\n className: 'dshh-pill',\n 'data-dsh-hud': 'pill',\n 'aria-expanded': state.panelOpen ? 'true' : 'false',\n style: { color: state.panelOpen ? 'var(--dsw-alias-brand-primary, #4b8bf5)' : undefined },\n onClick: function () {\n store.togglePanel()\n },\n },\n store.tipHandlers('pill', function () {\n return titleLines.join('\\n')\n }),\n ),\n children,\n ),\n // 工具活动那一枚并排在后面 —— dock 是竖列,独立注册会另起一整行(用户实测\"换行了\")。\n toolPillNode(state, { t: t, store: store, now: props.now === undefined ? tick[0] : props.now }),\n // 计时胶囊放在**最后**(三枚同在一条注册里 → 同一行;独立注册会另起一整行)。\n timerPillNode(state, {\n t: t,\n store: store,\n sessionId: sessionId,\n now: props.now === undefined ? tick[0] : props.now,\n }),\n )\n}\n\nexport function toolPillNode(state: HudState, options: ToolPillOptions): React.ReactElement | null {\n var t = options.t\n var tools = state.tools\n if (!tools || (tools.active === null && tools.total === 0)) return null\n var now = options.now\n\n var children: React.ReactNode[] = []\n if (tools.active !== null) {\n // 配色对齐 codex-hud 的 activity-lines:`◐` 黄 + 工具名青 + 目标/耗时用默认正文色。\n var elapsed = toolElapsedLabel(tools.active, now)\n children.push(\n h('span', { key: 'glyph', className: 'dshh-toolsGlyph dshh-toolsGlyphRun' }, '\\u25d0'),\n )\n children.push(\n h(\n 'span',\n { key: 'name', className: 'dshh-toolsName' },\n ' ' + String(tools.active.name === undefined ? '?' : tools.active.name),\n ),\n )\n // 带上参数摘要(codex-hud 就是 `◐ exec_command: pnpm test`)—— 技能调用因此能看出是哪个技能。\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0) {\n children.push(': ' + tools.active.summary)\n }\n if (elapsed !== null) children.push(' ' + elapsed)\n } else {\n // 空闲时把**每个工具各自几次**铺出来(codex-hud 那条 `Tools:` 就是这么给的):\n // `✓ bash ×16 │ ✓ apply_patch ×6 │ ✗ write_stdin ×3`。名字与数字贴在一起,\n // 就不会再有\"×54 是总数还是这个工具的次数\"那种误读。\n //\n // 最多铺 3 个(按次数排序,宿主已经排好),其余收成 `+k` —— 悬停里有完整清单与最近几条。\n // `skill` 工具本身对用户没信息量(`skill ×2` 说明不了是哪个技能)—— codex-hud 也是直接列技能名。\n // 所以把它从计数里剔掉,改由下面那段技能清单承担;总次数不受影响。\n var toolCounts = tools.counts.filter(function (item) {\n return item.name !== 'skill'\n })\n var shown = toolCounts.slice(0, 3)\n for (var i = 0; i < shown.length; i += 1) {\n var item = shown[i]\n var bad = item.ok === false\n if (i > 0) children.push(' \\u2502 ')\n children.push(\n h(\n 'span',\n {\n key: 'glyph' + String(i),\n className: 'dshh-toolsGlyph' + (bad ? ' dshh-toolsGlyphBad' : ' dshh-toolsGlyphOk'),\n },\n bad ? '\\u2717' : '\\u2713',\n ),\n )\n children.push(' ' + String(item.name) + ' \\u00d7' + String(item.count))\n }\n // 技能清单:名字直接铺出来(\"会话用了哪些 skill\"要能一眼看到,不只是次数)。\n // 标签用字面的 `skills:`(不翻译)—— 与 `git:(` 同一套:它是约定写法,不是描述。\n var skills = tools.skills || []\n if (skills.length > 0) {\n if (children.length > 0) children.push(' \\u2502 ')\n children.push(t('tools.skills') + ' ')\n for (var k = 0; k < skills.length && k < SKILLS_SHOWN; k += 1) {\n if (k > 0) children.push(' \\u00b7 ')\n children.push(\n h(\n 'span',\n {\n key: 'skill' + String(k),\n className: skills[k].ok === false ? 'dshh-toolsGlyphBad' : 'dshh-skillName',\n },\n String(skills[k].name) + ' \\u00d7' + String(skills[k].count),\n ),\n )\n }\n if (skills.length > SKILLS_SHOWN) children.push(' +' + String(skills.length - SKILLS_SHOWN))\n if (tools.skillsDropped > 0) children.push(' +' + String(tools.skillsDropped))\n }\n if (toolCounts.length > shown.length)\n children.push(' +' + String(toolCounts.length - shown.length))\n }\n\n // 渲染现场:画没画、画的是哪一档(与 row-chip/row-token 同一套排查手段)。\n probe.log('tools-pill', {\n active: tools.active === null ? null : String(tools.active.name),\n total: tools.total,\n })\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-toolsPill',\n 'data-dsh-hud': 'tools-pill',\n key: 'tools',\n },\n options.store.tipHandlers('tools-pill', function () {\n return toolsTitle(tools, t)\n }),\n ),\n children,\n )\n}\n\nexport function timerPillNode(\n state: HudState,\n options: TimerPillOptions,\n): React.ReactElement | null {\n var t = options.t\n var sessionId = options.sessionId\n var now = options.now\n var snapshot = state.sessionTime\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId === sessionId\n ? snapshot.value\n : null\n // 会话不在(还没恢复进存储、已归档、子会话已回收)、宿主服务缺失:不画这枚胶囊,也不占位。\n if (value === null || value.available !== true) return null\n var createdAt = value.createdAt\n if (typeof createdAt !== 'number') return null\n\n var age = Math.max(0, now - createdAt)\n var hints = [t('timer.title'), t('timer.created', { time: fullWhen(createdAt) })]\n if (typeof value.llmMs === 'number' && typeof value.toolMs === 'number') {\n hints.push(\n t('timer.work', { llm: formatDuration(value.llmMs), tool: formatDuration(value.toolMs) }),\n )\n }\n if (typeof value.turns === 'number' && typeof value.steps === 'number') {\n hints.push(t('timer.turns', { turns: String(value.turns), steps: String(value.steps) }))\n }\n probe.log('timer-pill', { age: Math.round(age / 60000) })\n var tipKey = 'session-timer'\n return h(\n 'span',\n Object.assign(\n {\n className: 'dshh-pill dshh-timer',\n 'data-dsh-hud': 'session-timer',\n key: 'timer',\n },\n options.store.tipHandlers(tipKey, function () {\n return hints.join(' · ')\n }),\n ),\n [\n h('span', { key: 'glyph', className: 'dshh-timerGlyph' }, '⏱'),\n h('span', { key: 'value', className: 'dshh-timerValue' }, formatDuration(age)),\n ],\n )\n}\n\nexport function toolsTitle(tools: ToolsState, t: Translate): string {\n var lines: string[] = []\n if (tools.active !== null) {\n lines.push(t('tools.now', { name: String(tools.active.name) }))\n if (typeof tools.active.summary === 'string' && tools.active.summary.length > 0)\n lines.push(tools.active.summary)\n }\n lines.push(t('tools.total', { n: String(tools.total) }))\n if (tools.counts.length > 0) {\n lines.push(\n t('tools.byTool') +\n ' ' +\n tools.counts\n .map(function (item) {\n return String(item.name) + ' \\u00d7' + String(item.count)\n })\n .join(' \\u00b7 '),\n )\n }\n if (tools.skills && tools.skills.length > 0) {\n lines.push(\n t('tools.skills') +\n ' ' +\n tools.skills\n .map(function (item) {\n return (\n String(item.name) +\n ' \\u00d7' +\n String(item.count) +\n (item.ok === false ? ' (\\u2717)' : '')\n )\n })\n .join(' \\u00b7 '),\n )\n }\n var recent = tools.recent.slice(0, 3)\n if (recent.length > 0) {\n lines.push(t('tools.recent'))\n lines.push(\n recent\n .map(function (item) {\n var mark = item.ok === false ? '\\u2717' : '\\u2713'\n var ms = typeof item.ms === 'number' ? ' ' + (item.ms / 1000).toFixed(1) + 's' : ''\n var summary =\n typeof item.summary === 'string' && item.summary.length > 0\n ? ' \\u00b7 ' + item.summary\n : ''\n return mark + ' ' + String(item.name) + ms + summary\n })\n .join('\\n'),\n )\n }\n return lines.join('\\n')\n}\n\nexport function toolElapsedLabel(active: HudToolActive | null, now: number): string | null {\n if (active === null || typeof active !== 'object') return null\n if (typeof active.startedAt !== 'number') return null\n var seconds = Math.max(0, (now - active.startedAt) / 1000)\n return seconds < 10 ? seconds.toFixed(1) + 's' : String(Math.round(seconds)) + 's'\n}\n","/**\n * 「本会话改过哪些文件」那一行的展示:标题、审阅标记、回合标签。\n *\n * 数据来自 store 的会话归因,这里只负责把事实排成一行字。\n */\n\nimport type React from 'react'\n\nimport { timeLabel } from '../format.js'\nimport type {\n ReviewMark,\n ReviewView,\n SessionChangeView,\n SessionCommand,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `SessionChangesLine` 的 props:归因视图 + 审阅进度 + 三枚开关。 */\nexport interface SessionChangesLineProps {\n t: Translate\n /** 会话归因视图;`null` = 这份数据还没到手(这一行只剩开关)。 */\n view: SessionChangeView | null\n /** 「只看本会话改的」开关状态。 */\n onlySession?: boolean\n /** 「只看最近一轮」开关状态。 */\n onlyLatest?: boolean\n /** 审阅进度;`null` / 缺省 = 这次没要签名。 */\n review?: ReviewView | null\n onToggle: () => void\n onToggleLatest: () => void\n onClearReviewed: () => void\n}\n\n/**\n * 改动概览下面那一行:本会话改了几个文件 + 「只看本会话改的」开关。\n *\n * 归因数据没到手(`view === null`)时整行不渲染 —— 不显示一个\"全部都是别人的\"的假象。\n */\nexport function SessionChangesLine(props: SessionChangesLineProps): React.ReactElement {\n var t = props.t\n var view = props.view\n var review = props.review || null\n // 这一行**总是渲染**:它带着三枚开关(只看本会话改的 / 侧栏档位 / 只看最近一轮),\n // 归因或审阅数据缺失时只是没有计数可报 —— 控件不能因为\"数据还没到\"就凭空消失。\n var notes: string[] = []\n if (view !== null) {\n notes.push(t('session.line', { n: String(view.mineCount) }))\n // 回合信息拿得到时,\"最近一轮改了几个\"是最常问的那个问题,直接写在行上。\n if (view.latestTurn !== null && view.latestCount > 0) {\n notes.push(\n t('session.latestTurn', { n: String(view.latestCount), turn: String(view.latestTurn + 1) }),\n )\n }\n if (view.possibleCount > 0) notes.push(t('session.possible', { n: String(view.possibleCount) }))\n }\n if (view !== null) {\n if (view.gone.length > 0) notes.push(t('session.gone', { n: String(view.gone.length) }))\n if (view.dropped > 0) notes.push(t('session.dropped', { n: String(view.dropped) }))\n if (view.possibleDropped > 0)\n notes.push(t('session.possibleDropped', { n: String(view.possibleDropped) }))\n }\n if (props.review && props.review.total > 0) {\n notes.push(\n t('review.count', { n: String(props.review.done), total: String(props.review.total) }),\n )\n }\n if (props.review && props.review.staleCount > 0)\n notes.push(t('review.staleCount', { n: String(props.review.staleCount) }))\n return h(\n 'div',\n { className: 'dshh-session', 'data-dsh-hud': 'session' },\n // 没有任何可报的计数时,文字位置留空(开关照旧在右边)。\n notes.length === 0\n ? null\n : h(\n 'span',\n { className: 'dshh-sessionText', title: sessionLineTitle(view, t) },\n notes.join(' · '),\n ),\n h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlySession === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'only',\n 'aria-pressed': props.onlySession === true ? 'true' : 'false',\n title: t('session.onlyTitle'),\n onClick: props.onToggle,\n },\n props.onlySession === true ? '◉ ' : '○ ',\n t('session.only'),\n ),\n // 已经把某些文件标成\"已审\"时,给一个\"清空\"的出口(只清当前工作区)。\n props.review && props.review.done > 0\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-sessionToggle',\n 'data-session-review': 'clear',\n title: t('review.clear'),\n onClick: props.onClearReviewed,\n },\n t('review.clearShort'),\n )\n : null,\n // 第二枚开关只在\"这一轮的归属拿得到\"时出现:拿不到就整枚不画,别给一个按不动的按钮。\n view === null || view.latestTurn === null\n ? null\n : h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-sessionToggle' + (props.onlyLatest === true ? ' dshh-sessionToggleOn' : ''),\n 'data-session-filter': 'latest',\n 'aria-pressed': props.onlyLatest === true ? 'true' : 'false',\n title: turnLabel(view, view.latestTurn, t) + '\\n' + t('session.onlyLatestTitle'),\n onClick: props.onToggleLatest,\n },\n props.onlyLatest === true ? '◉ ' : '○ ',\n t('session.onlyLatest'),\n ),\n )\n}\n\n/** 一个文件属于哪一档:`mine` 确定改过 / `possible` 命令旁听 / `other` 与本会话无关。 */\nexport function sessionMarkOf(\n view: SessionChangeView,\n file: string,\n): 'mine' | 'possible' | 'other' {\n if (view.mine !== undefined && view.mine[file] !== undefined) return 'mine'\n if (view.possible !== undefined && view.possible[file] !== undefined) return 'possible'\n return 'other'\n}\n\nexport function sessionTitle(view: SessionChangeView, file: string, t: Translate): string {\n var record = view.mine !== undefined ? view.mine[file] : undefined\n if (record === undefined) {\n var guess = view.possible !== undefined ? view.possible[file] : undefined\n if (guess === undefined) return t('session.other')\n // 低一档的置信度:说清是\"从命令里看出来的\",并把那条命令贴出来。\n return t('session.possibleTitle', { reason: String(guess.reason || '').slice(0, 80) })\n }\n var parts: string[] = []\n if (record.writes > 0) parts.push(t('session.writes', { n: String(record.writes) }))\n if (record.edits > 0) parts.push(t('session.edits', { n: String(record.edits) }))\n var when = typeof record.lastAt === 'number' ? timeLabel(record.lastAt) : ''\n var line =\n t('session.mine') +\n (parts.length === 0 ? '' : '(' + parts.join('、') + ')') +\n (when ? ' ' + when : '')\n // 回合是\"这一轮改了什么\"的锚点:有就一并说清是哪一轮、那一轮的请求是什么。\n var turn = typeof record.lastTurn === 'number' ? record.lastTurn : null\n if (turn !== null) line += '\\n' + turnLabel(view, turn, t)\n return line\n}\n\n/** 行尾那个标记的悬停说明:三种状态各说清下一步。 */\nexport function reviewTitle(state: ReviewMark | undefined, t: Translate): string {\n if (state === 'fresh') return t('review.unmark')\n if (state === 'stale') return t('review.stale')\n return t('review.mark')\n}\n\n/**\n * 那一行的悬停说明:自哪一刻起记录 + 跑过哪些命令。\n *\n * \"可能相关\"是从命令里看出来的,用户要能自己判断 —— 所以把命令原文贴出来,而不是只报一个数。\n */\nexport function sessionLineTitle(view: SessionChangeView | null, t: Translate): string | undefined {\n // 没有归因数据(那一行只剩审阅进度)时没有\"自 … 起记录\"也没有命令可列。\n if (view === null) return undefined\n var lines: string[] = []\n if (view.since !== null) lines.push(t('session.since', { time: timeLabel(view.since) }))\n var commands = (view.commands || []).slice(-5).reverse()\n if (commands.length > 0) {\n lines.push(t('session.commands', { n: String(commands.length) }))\n for (var index = 0; index < commands.length; index += 1) {\n var item: Partial<SessionCommand> = commands[index] || {}\n lines.push(\n '· ' +\n String(item.command || '') +\n (item.failed === true ? ' ' + t('session.commandFailed') : ''),\n )\n }\n if (view.writingCommands > commands.length) {\n lines.push(t('session.commandsMore', { n: String(view.writingCommands - commands.length) }))\n }\n }\n return lines.length === 0 ? undefined : lines.join('\\n')\n}\n\n/**\n * 清单里那一行的悬停说明:这行是不是本会话改的。\n *\n * `mine` 里有这条就把写入/编辑次数与时间区间说清楚;没有就直说\"不是这个会话改的\"\n * (可能是你自己、编辑器、或另一个会话)。\n */\n/**\n * 一轮的说明:`第 3 轮:<那一轮的请求预览>`。\n *\n * 回合号从宿主的 `turnBoundary` 投影来,请求预览从 `turnOutline` 投影来 —— 拿不到 prompt\n * 时只说\"第 N 轮\",不编内容。\n */\nexport function turnLabel(view: SessionChangeView, turn: number, t: Translate): string {\n var prompt =\n view.turnPrompts !== undefined && view.turnPrompts !== null ? view.turnPrompts[turn] : undefined\n if (typeof prompt !== 'string' || prompt.length === 0)\n return t('session.turn', { n: String(turn + 1) })\n return t('session.turnWithPrompt', { n: String(turn + 1), prompt: prompt })\n}\n","/**\n * 小段 DOM 判定与定位:焦点在哪、点在不在面板里、差异行怎么滚进视野。\n *\n * 数量少但被 hooks 与组件共用,所以单独一层,避免它们互相 import。\n */\n\n/**\n * 本层用到的 DOM 子集(结构类型)。\n *\n * 为什么不用 `Element` / `instanceof Element`:测试里的假 DOM 是普通对象(没有 Element 类),\n * 用 instanceof 会把那些用例全判成\"没有 DOM\"。这里只关心下面几个字段,按结构收窄即可;\n * 外部传进来的值(事件目标、查询结果)都从 `unknown` 收一次 —— 这就是系统边界。\n */\ninterface DomNodeLike {\n getAttribute?: (name: string) => string | null\n parentNode?: DomNodeLike | null\n tagName?: string\n isContentEditable?: boolean\n className?: string\n scrollTop?: number\n offsetTop?: number\n offsetHeight?: number\n clientHeight?: number\n getBoundingClientRect?: () => { top: number }\n}\n\n/** 把未知值收窄成\"结构上像个 DOM 节点\";不是对象就给 null。 */\nfunction asDomNode(value: unknown): DomNodeLike | null {\n return value !== null && typeof value === 'object' ? (value as DomNodeLike) : null\n}\n\n/** 取当前焦点节点;没有 document / activeElement 时给 null。 */\nexport function activeElement(): Element | null {\n if (typeof document === 'undefined') return null\n return document.activeElement || null\n}\n\n/** 事件目标是否落在面板里(走自己的 `data-dsh-hud=\"panel\"` 标记,不碰产品 DOM)。 */\nexport function isInsidePanel(node: unknown): boolean {\n return isInsideMarked(node, 'panel')\n}\n\n/** 事件目标是否落在某个 `data-dsh-hud` 标记的子树里(自己的标记,不碰产品的选择器)。 */\nexport function isInsideMarked(node: unknown, mark: string): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 64) {\n var attribute =\n typeof current.getAttribute === 'function' ? current.getAttribute('data-dsh-hud') : null\n if (attribute === mark) return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/**\n * 事件目标是不是\"正在输入\"的地方。\n *\n * 产品的输入框是 contenteditable,所以不能只看 tagName;两种都认。\n */\nexport function isTypingTarget(node: unknown): boolean {\n var current = asDomNode(node)\n var depth = 0\n while (current && depth < 8) {\n var tag = typeof current.tagName === 'string' ? current.tagName.toLowerCase() : ''\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return true\n if (current.isContentEditable === true) return true\n var editable =\n typeof current.getAttribute === 'function' ? current.getAttribute('contenteditable') : null\n if (editable === '' || editable === 'true' || editable === 'plaintext-only') return true\n current = asDomNode(current.parentNode)\n depth += 1\n }\n return false\n}\n\n/** 用自己写在节点上的标记找节点;找不到或不支持就是 null。 */\nexport function findMarked(selector: string): Element | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n return document.querySelector(selector)\n}\n\n/**\n * 把一个带自己标记的节点滚进它所在滚动容器的视野(最小滚动量)。\n *\n * 全走能力判断:假 DOM(测试)里没有 offsetTop / clientHeight,这个函数直接\n * no-op,测试断言的是状态与结构,不是像素。不用 `scrollIntoView`:那会连带\n * 滚动祖先容器(面板是浮层,滚了产品的会话列表就不好了)。\n */\nexport function revealRow(node: unknown, containerClassName: string): void {\n var start = asDomNode(node)\n if (!start || typeof start.parentNode === 'undefined') return\n var container = asDomNode(start.parentNode)\n var depth = 0\n while (container && depth < 32) {\n if (String(container.className || '').indexOf(containerClassName) >= 0) break\n container = asDomNode(container.parentNode)\n depth += 1\n }\n if (!container || typeof container.scrollTop !== 'number') return\n var top = start.offsetTop\n var height = start.offsetHeight\n var view = container.clientHeight\n if (typeof top !== 'number' || typeof height !== 'number' || typeof view !== 'number') return\n if (top < container.scrollTop) container.scrollTop = top\n else if (top + height > container.scrollTop + view) container.scrollTop = top + height - view\n}\n\n/* ------------------------------------------------------- 悬浮面板 */\n\nexport function measureDockTop(): number | null {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return null\n var node = document.querySelector('[data-dsh-hud=\"dock\"]')\n if (!node || typeof node.getBoundingClientRect !== 'function') return null\n var rect = node.getBoundingClientRect()\n return typeof rect.top === 'number' ? rect.top : null\n}\n","/**\n * 改动清单(面板左栏):分组、计数、每行的状态标记,以及只显示本会话/最近一轮的过滤。\n */\n\nimport { statusKey } from '../format.js'\nimport React from 'react'\n\nimport { reviewTitle, sessionMarkOf, sessionTitle } from './session-changes.js'\nimport { revealRow } from '../dom.js'\nimport type { HudChange, ReviewView, SessionChangeView, Translate } from '../shared.js'\nimport { COUNT_COLOR, h } from '../shared.js'\n\n/** `ChangeList` 的 props:清单本体 + 会话归因/审阅两列标记 + 交互回调。 */\nexport interface ChangeListProps {\n /** 要渲染的清单:工作区那份是 `HudChange`,提交明细那份是 `HudCommitFile`(结构兼容)。 */\n changes?: HudChange[] | null\n /** 正在看的那份差异对应的文件(`null` = 没打开差异)。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 两份清单同时在屏幕上时要区分\"哪一份\"(滚进视野时按它找行)。 */\n listId?: string\n /** 会话归因视图;`null` = 这份数据还没到手(整列不画)。 */\n sessionView?: SessionChangeView | null\n /** 审阅进度;`null` = 这次没要签名(胶囊那一路的轮询就是这样)。 */\n review?: ReviewView | null\n /** 清单被截断时的那句脚注。 */\n truncated?: boolean\n /** 自定义标题(提交明细那栏用);不给自己拼一个带计数的。 */\n title?: string\n t: Translate\n /** 点一行:打开该文件的差异视图。 */\n onOpen: (change: HudChange) => void\n /** 点行尾审阅标记:切换\"这一版审过了\"(不画审阅列的那份清单可以不给)。 */\n onToggleReviewed?: (file: string) => void\n}\n\nexport function ChangeList(props: ChangeListProps): React.ReactElement | null {\n var changes = props.changes || []\n var activeFile = props.activeFile\n var cursorFile = props.cursor || null\n // 提交明细打开时,屏幕上会同时有两份清单(左栏工作区 + 右栏这次提交),\n // 所以行标记必须带上是哪一份,否则\"把选中行滚进视野\"会滚错栏。\n var listId = props.listId || 'changes'\n\n // 键盘选中(或正在看)的那行可能被滚出视野:把它带回来。\n React.useEffect(\n function () {\n if (typeof document === 'undefined' || typeof document.querySelector !== 'function') return\n var wanted = cursorFile || activeFile\n if (!wanted) return\n for (var index = 0; index < changes.length; index += 1) {\n if (changes[index].file !== wanted) continue\n var node = document.querySelector(\n '[data-dsh-hud=\"row\"][data-row-list=\"' +\n listId +\n '\"][data-row-index=\"' +\n String(index) +\n '\"]',\n )\n revealRow(node, 'dshh-list')\n return\n }\n },\n [cursorFile, activeFile, changes.length, listId],\n )\n\n if (changes.length === 0) return null\n\n // 会话归因标记:只在这份数据到手时才有(`sessionView` 为 null 就整列不画)。\n var sessionView = props.sessionView === undefined ? null : props.sessionView\n // 审阅进度:`null` 表示这次没要签名(胶囊那一路的轮询就是这样),整列不画。\n var review = props.review === undefined ? null : props.review\n\n var rows = changes.map(function (change, index) {\n // 整行可点:打开该文件的差异视图(只读,不改动任何东西)。\n // \"正在看的那行\"和\"键盘选中的那行\"可能是两行(选了还没打开),因此两种标记分开。\n return h(\n 'button',\n {\n type: 'button',\n className:\n 'dshh-row' +\n (activeFile && change.file === activeFile ? ' dshh-rowActive' : '') +\n (cursorFile && change.file === cursorFile ? ' dshh-rowCursor' : ''),\n key: String(index) + ':' + change.path,\n 'data-dsh-hud': 'row',\n 'data-row-list': listId,\n 'data-row-index': String(index),\n title:\n change.path +\n (sessionView === null ? '' : '\\n' + sessionTitle(sessionView, change.file, props.t)) +\n '\\n' +\n props.t('panel.clickForDiff') +\n '\\n' +\n props.t('panel.rowKeys'),\n onClick: function () {\n props.onOpen(change)\n },\n },\n sessionView === null\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-marker' +\n (sessionMarkOf(sessionView, change.file) === 'mine'\n ? ''\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? ' dshh-markerPossible'\n : ' dshh-markerOther'),\n 'data-session-mark': sessionMarkOf(sessionView, change.file),\n 'aria-hidden': 'true',\n },\n sessionMarkOf(sessionView, change.file) === 'mine'\n ? '●'\n : sessionMarkOf(sessionView, change.file) === 'possible'\n ? '◐'\n : '○',\n ),\n h(\n 'span',\n {\n className: 'dshh-status',\n style: {\n color:\n COUNT_COLOR[statusKey(change.status)] || 'var(--dsw-alias-label-secondary, #8a8f98)',\n },\n },\n change.status,\n ),\n h('span', { className: 'dshh-path' }, change.path),\n review === null || review.known === undefined || review.known[change.file] !== true\n ? null\n : h(\n 'span',\n {\n className:\n 'dshh-review' +\n (review.marks[change.file] === 'fresh' ? ' dshh-reviewOn' : '') +\n (review.marks[change.file] === 'stale' ? ' dshh-reviewStale' : ''),\n 'data-review':\n review.marks[change.file] === undefined ? 'none' : review.marks[change.file],\n title: reviewTitle(review.marks[change.file], props.t),\n onClick: function (event: React.MouseEvent<HTMLSpanElement>) {\n // 行本身是按钮(点开差异):这里必须拦住,否则标记一下就顺手打开了差异。\n event.stopPropagation()\n event.preventDefault()\n // 只有画了审阅列的清单才会渲染这枚标记,所以回调不给时这里也不会被走到。\n if (props.onToggleReviewed) props.onToggleReviewed(change.file)\n },\n },\n review.marks[change.file] === undefined ? '○' : '✓',\n ),\n )\n })\n\n var footer = props.truncated\n ? h('div', { className: 'dshh-note' }, props.t('panel.truncated'))\n : null\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h(\n 'div',\n { className: 'dshh-sectionTitle' },\n props.title || props.t('panel.changes') + ' · ' + String(changes.length),\n ),\n h('div', { className: 'dshh-list' }, rows),\n footer,\n )\n}\n","/**\n * 最近提交清单(面板中间那一栏):一条一行,短 sha + 标题 + HEAD 徽标 + 时刻。\n */\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudLogEntry, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `CommitList` 的 props:最近提交清单 + 打开回调 + 高亮的那一条。 */\nexport interface CommitListProps {\n /** 最近提交;`null` / 空表时整段不画。 */\n commits?: HudLogEntry[] | null\n t: Translate\n /** 提交明细当前打开的那条(短 sha 与完整 sha 前缀匹配)。 */\n activeSha?: string | null\n /** 点一行:打开/收起这次提交的明细。 */\n onOpen?: ((commit: HudLogEntry) => void) | null\n}\n\n/**\n * 最近提交:一行一条,sha + 标题 + HEAD 徽标 + 相对时间;整行可点看这次提交改了什么。\n * 打开的那条会高亮(再点一次收起)——提交明细在右栏,左栏这块主面板不动。\n */\nexport function CommitList(props: CommitListProps) {\n var commits = props.commits || []\n if (commits.length === 0) return null\n var t = props.t\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.commits')),\n h(\n 'div',\n { className: 'dshh-commits' },\n commits.map(function (commit, index) {\n var refs = commit.refs || []\n var isHead = refs.some(function (ref) {\n return ref.indexOf('HEAD') === 0\n })\n var when =\n typeof commit.committedAt === 'number' ? fullWhen(commit.committedAt) : commit.date || ''\n var tip = [t('commit.click'), '', commit.subject, commit.author + ' · ' + when]\n .concat(refs)\n .join('\\n')\n // 整行是按钮:点了在左栏换成这次提交的改动清单(只读)。\n var open = sameCommit(commit.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n key: commit.sha + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(commit)\n },\n },\n h('span', { className: 'dshh-csha' }, commit.sha),\n h('span', { className: 'dshh-csubject' }, commit.subject || '—'),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(commit, t)),\n )\n }),\n ),\n )\n}\n","/**\n * 提交明细(右栏):某次提交的元信息与它改过的文件。\n */\n\nimport { errorText } from '../format.js'\nimport { ChangeList } from './change-list.js'\nimport type { HudCommitFile, HudCommitState, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `CommitPane` 的 props:这次提交的完整明细 + 打开某个文件差异的回调。 */\nexport interface CommitPaneProps {\n t: Translate\n /** 提交明细的状态(`state.commit`);只在它非空时才渲染本栏。 */\n commit: HudCommitState\n /** 打开清单里一条(= 提交里的某个文件)的差异。 */\n onOpenFile: (file: HudCommitFile) => void\n /** 正在看的那份差异对应的文件。 */\n activeFile?: string | null\n /** 键盘高亮的文件。 */\n cursor?: string | null\n /** 本栏宽度(右边缘抓握可拖)。 */\n width?: number | null\n}\n\n/**\n * 一次提交的那一栏(**中间**栏,固定宽度):元信息 + 这次提交的改动清单。\n *\n * 差异在它右边那一栏(DiffView)。打开提交不改动左栏——左栏始终是那块主面板,\n * 只是\"最近提交\"里对应的一行高亮起来。清单复用工作区那套 ChangeList(同一个\n * 行样式、同一套键盘移动)。\n */\nexport function CommitPane(props: CommitPaneProps) {\n var t = props.t\n var commit = props.commit\n var style = { width: props.width }\n\n var head\n if (commit.phase === 'loading') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, t('commit.loading')),\n )\n } else if (commit.phase === 'error') {\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h('div', { className: 'dshh-commitMeta' }, errorText(commit.errorCode, t)),\n )\n } else {\n var lines = [commit.author + ' · ' + commit.date]\n if (commit.refs && commit.refs.length > 0) lines.push(commit.refs.join(', '))\n if (commit.merge) lines.push(t('commit.merge'))\n head = h(\n 'div',\n { className: 'dshh-commitHead' },\n h(\n 'div',\n { className: 'dshh-commitTitle' },\n h('span', { className: 'dshh-csha' }, commit.shortSha),\n h(\n 'span',\n { className: 'dshh-commitSubject', title: commit.subject },\n commit.subject || '—',\n ),\n ),\n h('div', { className: 'dshh-commitMeta' }, lines.join(' · ')),\n )\n }\n\n var files = commit.files || []\n var list =\n commit.phase !== 'ready'\n ? null\n : files.length === 0\n ? h(\n 'div',\n { className: 'dshh-commitFiles' },\n h('div', { className: 'dshh-note' }, t('commit.empty')),\n )\n : h(\n 'div',\n { className: 'dshh-commitFiles' },\n h(ChangeList, {\n changes: files,\n truncated: commit.truncated,\n t: t,\n title: t('commit.files') + ' · ' + String(files.length),\n onOpen: props.onOpenFile,\n activeFile: props.activeFile,\n cursor: props.cursor,\n listId: 'commit',\n }),\n )\n\n return h('div', { className: 'dshh-commitpane', style: style }, head, list)\n}\n","/**\n * 概览那一行的计数小片:按「改动 > 新增 > 未跟踪」排列,颜色与胶囊上的简写一致。\n */\n\nimport type React from 'react'\n\nimport type { HudStatusCounts, Translate } from '../shared.js'\nimport { COUNT_COLOR, COUNT_ORDER, h } from '../shared.js'\n\n/** `CountChips` 的 props:八类改动计数 + 取词函数。 */\nexport interface CountChipsProps {\n /** git status 的分组计数;缺字段按 0 处理。 */\n counts?: HudStatusCounts | null\n t: Translate\n}\n\n/* ------------------------------------------------------------- 小组件 */\n\nexport function CountChips(props: CountChipsProps): React.ReactElement {\n var counts: Partial<HudStatusCounts> = props.counts || {}\n var children: React.ReactElement[] = []\n for (var index = 0; index < COUNT_ORDER.length; index += 1) {\n var key = COUNT_ORDER[index]\n var value = counts[key] || 0\n if (value <= 0) continue\n children.push(\n h(\n 'span',\n {\n key: key,\n className: 'dshh-chip',\n style: { color: COUNT_COLOR[key], borderColor: 'currentColor' },\n title: props.t('count.' + key) + ' ' + String(value),\n },\n props.t('count.' + key) + ' ' + String(value),\n ),\n )\n }\n if (children.length === 0) {\n return h('span', { className: 'dshh-clean' }, props.t('panel.clean'))\n }\n return h('div', { className: 'dshh-chips' }, children)\n}\n","/**\n * 图片改动的前后对比:两侧缩略、尺寸与字节数、可缩放,认不出的二进制退回一句话说明。\n */\n\nimport React from 'react'\n\nimport { errorText } from '../format.js'\nimport type {\n HudDiffState,\n HudStore,\n ImagePayload,\n ImageRequest,\n ImageSide,\n Translate,\n} from '../shared.js'\nimport { IMAGE_FILE_EXTENSIONS, formatBytes, h, imagePairKey, stepZoom } from '../shared.js'\n\n/**\n * `image` 端点在 store 缓存里的条目(store 的 image 片写进 `Map` 的那个形状)。\n *\n * 它同时被 image-diff 与本目录的 turn-images 消费;先放在这里(等 shared 收尾后视需要上提)。\n */\nexport interface ImagePairEntry {\n phase: string\n value?: ImagePayload\n code?: string | null\n}\n\n/** 图片请求失败/成功的一侧标签对。 */\nexport interface ImageLabels {\n before: string\n after: string\n}\n\n/** 图片并排/切换时的一格。 */\ninterface ImageCell {\n key: string\n label: string\n side: ImageSide\n}\n\n/** `ImageDiff` 的 props:差异状态(二进制改动才有它)+ store + 工作区(发请求要用)。 */\nexport interface ImageDiffProps {\n t: Translate\n store: HudStore\n diff: HudDiffState\n /** 会话工作区;没有它就没法发 `image` 请求(文本 diff 也用不到)。 */\n cwd?: string | null\n}\n\n/**\n * 二进制(图片)改动的预览:并排两张图,或按需在两张之间切换。\n *\n * 只在文本 diff 报 `binary` 时挂上来。字节来自宿主的 `image` 端点(只读),\n * 这里只负责画:单侧画不出来就画那一侧的原因,两侧都不行才退回原来那句话。\n */\nexport function ImageDiff(props: ImageDiffProps) {\n var t = props.t\n var store = props.store\n var diff = props.diff\n var request = imageRequest(diff, props.cwd)\n var key = request === null ? null : imagePairKey(request)\n // 先同步看缓存:已经取过的图第一帧就画出来,不闪\"读取图片…\"。\n var viewRef = React.useState<ImagePairEntry | null>(function () {\n return request !== null && store.cachedImagePair\n ? asPairEntry(store.cachedImagePair(request))\n : null\n })\n var modeRef = React.useState<'pair' | 'flip'>('pair')\n var flipRef = React.useState<'after' | 'before'>('after')\n\n React.useEffect(\n function () {\n if (request === null || key === null) return undefined\n var cancelled = false\n var cached = store.cachedImagePair ? asPairEntry(store.cachedImagePair(request)) : null\n if (cached !== null) {\n viewRef[1](cached)\n return undefined\n }\n viewRef[1](null)\n store.loadImagePair(request).then(function (entry) {\n if (!cancelled) viewRef[1](asPairEntry(entry))\n })\n return function () {\n cancelled = true\n }\n },\n [key],\n )\n\n if (key === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('diff.binary'))\n\n var view = viewRef[0]\n if (view === null)\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, t('image.loading'))\n if (view.phase === 'error')\n return h('div', { className: 'dshh-empty', 'data-dsh-hud': 'image' }, errorText(view.code, t))\n\n var value: Partial<ImagePayload> = view.value || {}\n var before: ImageSide = value.before || { kind: 'missing' }\n var after: ImageSide = value.after || { kind: 'missing' }\n if (before.kind !== 'image' && after.kind !== 'image') {\n return h(\n 'div',\n { className: 'dshh-empty', 'data-dsh-hud': 'image' },\n imageFallbackText(t, before, after),\n )\n }\n\n var labels = imageLabels(diff, t)\n var mode = modeRef[0]\n var flip = flipRef[0]\n // 缩放住在 store 里(和\"自动换行\"一样记住):`null` = 适应宽度,数字 = 固定档位。\n var zoom = diff.zoom === undefined ? null : diff.zoom\n var cells: ImageCell[] =\n mode === 'pair'\n ? [\n { key: 'before', label: labels.before, side: before },\n { key: 'after', label: labels.after, side: after },\n ]\n : [\n {\n key: flip,\n label: flip === 'after' ? labels.after : labels.before,\n side: flip === 'after' ? after : before,\n },\n ]\n\n function cell(entry: ImageCell): React.ReactElement {\n var side = entry.side\n var body =\n side.kind === 'image'\n ? h('img', {\n className: 'dshh-img',\n alt: entry.label,\n // 缩放:适应宽度交给 CSS(`max-width:100%`);固定档位按**固有像素**算宽度,\n // 因为宿主已经把宽高一起给我们了 —— 不用等图片加载就能算对。\n style:\n zoom === null || typeof side.width !== 'number'\n ? undefined\n : { width: Math.round(side.width * zoom) + 'px', maxWidth: 'none' },\n src: 'data:' + side.mime + ';base64,' + String(side.base64 || ''),\n })\n : h('div', { className: 'dshh-imgNote' }, sideReasonText(side, t))\n return h(\n 'div',\n { className: 'dshh-imgCell', key: entry.key },\n h('div', { className: 'dshh-imgLabel' }, entry.label),\n body,\n )\n }\n\n function tab(id: 'pair' | 'flip', label: string): React.ReactElement {\n var active = mode === id\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n onClick: function () {\n modeRef[1](id)\n },\n },\n label,\n )\n }\n\n // 尺寸变了才是这张图最值得一眼看到的信息(改图最常见的就是换尺寸)。\n var dimsChanged =\n before.kind === 'image' &&\n after.kind === 'image' &&\n typeof before.width === 'number' &&\n typeof after.width === 'number' &&\n (before.width !== after.width || before.height !== after.height)\n ? h(\n 'span',\n { className: 'dshh-imgDims' },\n t('image.dimsChanged', {\n from: t('image.dims', { w: String(before.width), h: String(before.height) }),\n to: t('image.dims', { w: String(after.width), h: String(after.height) }),\n }),\n )\n : null\n var bytes =\n before.kind === 'image' && after.kind === 'image'\n ? h(\n 'span',\n { className: 'dshh-imgBytes' },\n formatBytes(before.bytes) + ' → ' + formatBytes(after.bytes),\n )\n : null\n\n // 缩放按钮:内置那个文档预览把缩放藏在\"预览区底部 72px 的悬停感应带\"里\n // (CSS 默认 opacity:0,还得 Ctrl+滚轮),这里给一排看得见、点的着的。\n var zoomControls = h('span', { className: 'dshh-imgZoom', 'data-dsh-hud': 'image-zoom' }, [\n h(\n 'button',\n {\n key: 'out',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomOut'),\n 'data-dsh-hud': 'image-zoom-out',\n onClick: function () {\n store.setZoom(stepZoom(zoom, -1))\n },\n },\n '−',\n ),\n h(\n 'button',\n {\n key: 'value',\n type: 'button',\n className: 'dshh-imgZoomBtn dshh-imgZoomValue',\n title: t('image.fitWidth'),\n 'data-dsh-hud': 'image-zoom-value',\n onClick: function () {\n store.setZoom(null)\n },\n },\n zoom === null\n ? t('image.fitWidth')\n : t('image.zoomValue', { percent: String(Math.round(zoom * 100)) }),\n ),\n h(\n 'button',\n {\n key: 'in',\n type: 'button',\n className: 'dshh-imgZoomBtn',\n title: t('image.zoomIn'),\n 'data-dsh-hud': 'image-zoom-in',\n onClick: function () {\n store.setZoom(stepZoom(zoom, 1))\n },\n },\n '+',\n ),\n ])\n\n return h(\n 'div',\n { className: 'dshh-imgWrap', 'data-dsh-hud': 'image' },\n h(\n 'div',\n { className: 'dshh-imgMeta' },\n tab('pair', t('image.pair')),\n mode === 'flip'\n ? h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('image.flip'),\n 'data-dsh-hud': 'image-flip',\n onClick: function () {\n flipRef[1](flip === 'after' ? 'before' : 'after')\n },\n },\n t('image.flip'),\n )\n : null,\n zoomControls,\n dimsChanged,\n bytes,\n ),\n h(\n 'div',\n {\n className:\n 'dshh-imgPair' +\n (mode === 'flip' ? ' dshh-imgPairSingle' : '') +\n (zoom === null ? '' : ' dshh-imgPairZoomed'),\n },\n cells.map(cell),\n ),\n )\n}\n\n/**\n * 差异视图:两侧页签(宿主半一次把两侧都取回来了,切页签不再发请求)、\n * 自动换行、复制差异。全部只读——它只是把 git 的输出画出来。\n */\n/* ------------------------------------------------ 图片预览(二进制改动) */\n\n/**\n * 由 diff 状态推出 `image` 请求;推不出(没有 cwd / 文件)返回 null。\n *\n * 页签语义与文本 diff 完全一致:未暂存看「索引 → 工作区」,已暂存看「HEAD → 索引」,\n * 提交明细按 sha 取,所以两个视图里\"未暂存 / 已暂存\"指的是同一件事。\n */\nexport function imageRequest(\n diff: HudDiffState | null | undefined,\n cwd: string | null | undefined,\n): ImageRequest | null {\n if (!diff || !diff.file || !cwd) return null\n var request: ImageRequest = { cwd: cwd, path: diff.file }\n if (diff.origPath) request.origPath = diff.origPath\n if (diff.source === 'commit' && diff.sha) request.sha = diff.sha\n else request.side = diff.side === 'index' ? 'index' : 'worktree'\n return request\n}\n\n/** 两侧的标签跟着\"看的到底是哪两条记录\"走。 */\nexport function imageLabels(diff: HudDiffState, t: Translate): ImageLabels {\n if (diff.source === 'commit')\n return { before: t('image.beforeCommit'), after: t('image.afterCommit') }\n if (diff.side === 'index') return { before: t('image.beforeIndex'), after: t('image.afterIndex') }\n return { before: t('image.beforeWorktree'), after: t('image.afterWorktree') }\n}\n\n/** 一侧画不出来时的原因文案(每一侧各有各的原因)。 */\nexport function sideReasonText(side: ImageSide, t: Translate): string {\n if (side.kind === 'too-large') {\n return t('image.tooLarge', {\n limit: String(Math.round((typeof side.limit === 'number' ? side.limit : 0) / 1024 / 1024)),\n size: formatBytes(side.bytes),\n })\n }\n if (side.kind === 'not-image') return t('image.notImage', { size: formatBytes(side.bytes) })\n return t('image.missing')\n}\n\n/**\n * 两张都画不出来时说什么:能说清原因就说原因,否则退回原来那句话。\n * \"太大\"优先于\"不是图\" —— 前者才是用户会想处理的那件事。\n */\nexport function imageFallbackText(t: Translate, before: ImageSide, after: ImageSide): string {\n var sides = [before, after]\n var tooLarge = sides.filter(function (side) {\n return side.kind === 'too-large'\n })[0]\n if (tooLarge !== undefined) return sideReasonText(tooLarge, t)\n var notImage = sides.filter(function (side) {\n return side.kind === 'not-image'\n })[0]\n if (notImage !== undefined) return sideReasonText(notImage, t)\n return t('diff.binary')\n}\n\n/* ----------------------------------------- 轮尾:本轮改过的图片(一条门) */\n\n/** 按扩展名粗筛\"这看起来是张图\"。 */\nexport function looksLikeImage(file: unknown): boolean {\n if (typeof file !== 'string') return false\n var dot = file.lastIndexOf('.')\n if (dot < 0 || dot === file.length - 1) return false\n return IMAGE_FILE_EXTENSIONS.indexOf(file.slice(dot + 1).toLowerCase()) >= 0\n}\n\n/**\n * store 边界:图片缓存条目由 store 的 image 片构造(形状见 `store/image.ts`),\n * 到这里只做一次\"是不是对象\"的收窄;坏值当没有缓存(会再发一次请求,行为不变)。\n */\nfunction asPairEntry(value: unknown): ImagePairEntry | null {\n return value !== null && value !== undefined && typeof value === 'object'\n ? (value as ImagePairEntry)\n : null\n}\n","/**\n * 差异视图:两侧页签、逐行渲染、折叠 hunk、复制为文本。\n */\n\nimport React from 'react'\n\nimport { ImageDiff } from './image-diff.js'\nimport { findMarked, revealRow } from '../dom.js'\nimport { errorText } from '../format.js'\nimport type {\n DiffPayload,\n DiffRow as DiffRowShape,\n DiffSide,\n HudDiffState,\n HudStore,\n Translate,\n WorktreeDiffPayload,\n} from '../shared.js'\nimport { DIFF_ADD_BG, DIFF_DEL_BG, DIFF_GUTTER_COLOR, h } from '../shared.js'\n\n/** 差异视图的两侧页签(提交差异没有这个概念,不会画)。 */\nexport type DiffTabSide = 'worktree' | 'index'\n\n/** `DiffView` 的 props:当前差异状态 + 打开它的 store + 工作区(图片预览要按它发请求)。 */\nexport interface DiffViewProps {\n t: Translate\n diff: HudDiffState\n store: HudStore\n cwd?: string | null\n}\n\n/** `DiffRow` 的 props:一行解析好的差异 + 它的 hunk 归属与折叠状态。 */\nexport interface DiffRowProps {\n row: DiffRowShape\n /** 自动换行(store 里记住的开关)。 */\n wrap?: boolean\n /** 这一行属于第几个 hunk(头之前的行是 `null`)。 */\n hunk?: number | null\n /** hunk 头是否\"当前正在看\"的那一段。 */\n active?: boolean\n /** hunk 是否被折叠。 */\n collapsed?: boolean\n /** hunk 头的悬停说明(\"点一下折叠\")。 */\n hint?: string\n /** 点 hunk 头:折叠/展开这一段。 */\n onToggle?: (() => void) | null\n}\n\n/** `visibleDiffRows()` 的输出项:要渲染的行 + 它属于哪个 hunk。 */\nexport interface VisibleDiffEntry {\n row: DiffRowShape\n /** hunk 序号;hunk 头之前的行是 `null`。 */\n hunk: number | null\n}\n\nexport function DiffView(props: DiffViewProps) {\n var t = props.t\n var diff = props.diff\n var store = props.store\n // 图片预览要按工作区发请求;文本 diff 用不到它。\n var cwd = props.cwd\n\n var copiedRef = React.useState(false)\n var copied = copiedRef[0]\n var setCopied = copiedRef[1]\n // 复制的\"已复制\"提示要 1.5s 后收回;定时器句柄得留一份好清掉。\n var timerRef = React.useState<number | null>(null)\n\n var isCommit = diff.source === 'commit'\n // 提交差异没有\"侧\"这个概念,但只要不是提交,页签就得知道自己亮的是哪个。\n var side: DiffTabSide = diff.side === 'index' ? 'index' : 'worktree'\n var data = diffPayload(diff)\n var untracked = isCommit ? false : payloadUntracked(diff.data)\n var hunk = typeof diff.hunk === 'number' ? diff.hunk : 0\n var collapsed = diff.collapsed || []\n var rows = data && data.rows ? data.rows : []\n\n // 换了文件 / 侧 / 提交:回到顶部。否则会停在上一份 diff 的滚动位置\n // (React 复用同一个容器节点,scrollTop 会留下来)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (host && typeof host.scrollTop === 'number') host.scrollTop = 0\n },\n [diff.file, diff.sha, diff.side],\n )\n\n // 键盘移动 hunk:把当前 hunk 滚进视野(已经看得见就一点都不动)。\n React.useEffect(\n function () {\n var host = findMarked('[data-dsh-hud=\"diff\"]')\n if (!host || typeof host.querySelector !== 'function') return\n revealRow(\n host.querySelector('[data-dsh-hud=\"hunk\"][data-hunk-index=\"' + String(hunk) + '\"]'),\n 'dshh-diff',\n )\n },\n [hunk, diff.file, diff.sha, diff.side, collapsed.length],\n )\n\n function onCopy(): void {\n if (!data) return\n var clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined\n if (!clipboard || typeof clipboard.writeText !== 'function') return\n Promise.resolve(clipboard.writeText(rowsToText(data.rows || [])))\n .then(function () {\n setCopied(true)\n if (timerRef[0]) window.clearTimeout(timerRef[0])\n timerRef[1](\n window.setTimeout(function () {\n setCopied(false)\n }, 1500),\n )\n })\n .catch(function () {\n // 剪贴板被拒绝(无权限/非安全上下文)时不谎报成功。\n })\n }\n\n function tab(kind: DiffTabSide, label: string, title: string): React.ReactElement {\n var active = side === kind\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (active ? ' dshh-tabActive' : ''),\n 'aria-pressed': active ? 'true' : 'false',\n title: title,\n onClick: function () {\n store.setDiffSide(kind)\n },\n },\n label,\n )\n }\n\n var counts =\n data && data.binary !== true\n ? h(\n 'span',\n { className: 'dshh-difcounts' },\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-success-primary, #3fb950)' } },\n '+' + String(data.additions),\n ),\n h(\n 'span',\n { style: { color: 'var(--dsw-alias-state-error-primary, #f06a6a)' } },\n '-' + String(data.deletions),\n ),\n )\n : null\n\n var body: React.ReactElement\n if (diff.phase === 'loading') {\n body = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else if (diff.phase === 'error') {\n body = h('div', { className: 'dshh-empty' }, errorText(diff.errorCode, t))\n } else if (!data) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else if (data.binary === true) {\n // 二进制里最常被改的就是图:能认出来就画出来,认不出才退回那句话。\n body = h(ImageDiff, { t: t, store: store, diff: diff, cwd: cwd })\n } else if (rows.length === 0) {\n body = h('div', { className: 'dshh-empty' }, t('diff.empty'))\n } else {\n var visible = visibleDiffRows(rows, collapsed)\n body = h(\n 'div',\n { className: 'dshh-diff', 'data-dsh-hud': 'diff' },\n visible.map(function (entry, index) {\n // hunk 序号只在 hunk 头及其后面的行上有;头之前的行是 null。\n var entryHunk = entry.hunk === null ? -1 : entry.hunk\n var isHunk = entry.row.kind === 'hunk'\n return h(DiffRow, {\n key: String(index) + ':' + entry.row.kind,\n row: entry.row,\n wrap: diff.wrap === true,\n hunk: entry.hunk,\n active: isHunk && entry.hunk === hunk && collapsed.indexOf(entryHunk) < 0,\n collapsed: isHunk && collapsed.indexOf(entryHunk) >= 0,\n hint: isHunk ? t('diff.hunkToggle') : undefined,\n onToggle: isHunk\n ? function () {\n store.toggleHunk(entry.hunk === null ? undefined : entry.hunk)\n }\n : undefined,\n })\n }),\n data.truncated === true ? h('div', { className: 'dshh-note' }, t('diff.truncated')) : null,\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-diffview' },\n h(\n 'div',\n { className: 'dshh-toolbar' },\n h('span', { className: 'dshh-difpath', title: diff.path }, diff.path),\n counts,\n h('span', { className: 'dshh-spacer' }),\n // 提交差异只有一个\"侧\"(相对父提交),因此不给 未暂存/已暂存 页签\n // (短 sha 与标题在右栏的明细头里,这里不重复)。\n untracked ? h('span', { className: 'dshh-kv' }, t('diff.untrackedHint')) : null,\n isCommit ? null : tab('worktree', t('diff.worktree'), t('diff.worktree')),\n isCommit || untracked ? null : tab('index', t('diff.index'), t('diff.index')),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab' + (diff.wrap === true ? ' dshh-tabActive' : ''),\n 'aria-pressed': diff.wrap === true ? 'true' : 'false',\n title: t('diff.wrap'),\n onClick: function () {\n store.toggleDiffWrap()\n },\n },\n t('diff.wrap'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n title: t('diff.copy'),\n onClick: onCopy,\n },\n copied ? t('diff.copied') : t('diff.copy'),\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-tab',\n // 提交明细里这个 ✕ 收的是整块明细(差异是明细的一段),文案跟着改。\n title: isCommit ? t('commit.close') : t('diff.close'),\n 'aria-label': isCommit ? t('commit.close') : t('diff.close'),\n onClick: function () {\n store.closeDetail()\n },\n },\n '✕',\n ),\n ),\n body,\n )\n}\n\n/**\n * 一行差异:两个行号槽 + 文本。\n *\n * hunk 头是**可点**的(折叠 / 展开这一段),因此它第一个行号槽放的是折叠标记,\n * 不是行号——那个位置本来就空着,放标记不会让文本错位。\n */\nexport function DiffRow(props: DiffRowProps): React.ReactElement {\n var row = props.row\n var style =\n row.kind === 'add'\n ? { background: DIFF_ADD_BG }\n : row.kind === 'del'\n ? { background: DIFF_DEL_BG }\n : null\n var gutterStyle = { color: DIFF_GUTTER_COLOR }\n var text = row.text === '' ? '\\u00a0' : row.text\n\n if (row.kind === 'hunk') {\n return h(\n 'div',\n {\n className: 'dshh-difrow dshh-difrow-hunk' + (props.active ? ' dshh-difrow-hunkActive' : ''),\n 'data-dsh-hud': 'hunk',\n 'data-hunk-index':\n props.hunk === null || props.hunk === undefined ? undefined : String(props.hunk),\n title: props.hint,\n onClick: props.onToggle,\n },\n h('span', { className: 'dshh-difno', style: gutterStyle }, ''),\n h(\n 'span',\n { className: 'dshh-difno dshh-hunkMark', style: gutterStyle },\n props.collapsed ? '▸' : '▾',\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n }\n\n return h(\n 'div',\n { className: 'dshh-difrow dshh-difrow-' + row.kind, style: style },\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.oldLine === null ? '' : String(row.oldLine),\n ),\n h(\n 'span',\n { className: 'dshh-difno', style: gutterStyle },\n row.newLine === null ? '' : String(row.newLine),\n ),\n h('span', { className: 'dshh-diftext' + (props.wrap ? ' dshh-wrap' : '') }, text),\n )\n}\n\n/**\n * 把 rows 整理成\"要渲染的行 + 它属于哪个 hunk\"。\n *\n * hunk 段从 hunk 头开始,到下一个 hunk 头(或结尾)结束;折叠某个 hunk 就是\n * 把该段里除头以外的行丢掉。hunk 头之前那些行(`diff --git`、`---`、`+++`)\n * 永远显示——它们是\"这是哪个文件\"的上下文。\n *\n * @returns `[{ row, hunk }]`,`hunk` 是 hunk 序号(头之前的行是 null,收起时是负数)。\n */\nexport function visibleDiffRows(rows: DiffRowShape[], collapsed: number[]): VisibleDiffEntry[] {\n var folded = collapsed || []\n var out: VisibleDiffEntry[] = []\n var ordinal = -1\n for (var index = 0; index < rows.length; index += 1) {\n var row = rows[index]\n if (row.kind === 'hunk') {\n ordinal += 1\n out.push({ row: row, hunk: ordinal })\n continue\n }\n if (ordinal >= 0 && folded.indexOf(ordinal) >= 0) continue\n out.push({ row: row, hunk: ordinal >= 0 ? ordinal : null })\n }\n return out\n}\n\n/** 把 rows 还原成 unified diff 文本(\"复制差异\"用)。 */\nexport function rowsToText(rows: DiffRowShape[]): string {\n return rows\n .map(function (row) {\n if (row.kind === 'add') return '+' + row.text\n if (row.kind === 'del') return '-' + row.text\n if (row.kind === 'ctx') return ' ' + row.text\n return row.text\n })\n .join('\\n')\n}\n\n/** 工作区差异的载荷形状:两侧都在。提交差异是单侧,没有这个形状。 */\nfunction isWorktreePayload(payload: DiffPayload): payload is WorktreeDiffPayload {\n return 'worktree' in payload && 'index' in payload\n}\n\n/** 工作区差异载荷里的 `untracked`;单侧载荷(提交差异)没有这个字段。 */\nfunction payloadUntracked(payload: DiffPayload | null): boolean {\n return payload !== null && isWorktreePayload(payload) ? payload.untracked === true : false\n}\n\n/** 当前这一侧的 rows 载体:提交差异只有一侧,工作区差异要按 side 取。 */\nexport function diffPayload(diff: HudDiffState | null | undefined): DiffSide | null {\n if (!diff || !diff.data) return null\n // 域边界:来源是 commit 时宿主只回单侧载荷(`DiffSide`),类型上 `data` 是联合,这里收窄一次。\n if (diff.source === 'commit') return diff.data as DiffSide\n // 工作区差异的 `data` 一定是两侧载荷;收窄成单侧只是给类型看的,取的是同一份 rows。\n if (!isWorktreePayload(diff.data)) return null\n return diff.data[diff.side === 'index' ? 'index' : 'worktree'] || null\n}\n","/**\n * 标签列表(面板底部):只在有标签时渲染,点了复制名字。\n */\n\nimport type React from 'react'\n\nimport { commitWhen, fullWhen } from '../format.js'\nimport type { HudTag, Translate } from '../shared.js'\nimport { h, sameCommit } from '../shared.js'\n\n/** `TagList` 的 props:标签清单 + HEAD 比对 + 打开回调。 */\nexport interface TagListProps {\n /** `null` = 还没取到(整段不画);`[]` = 取回了、确实一个都没有(画一行说明)。 */\n tags?: HudTag[] | null\n t: Translate\n /** 当前 HEAD 的 oid(用来给指向 HEAD 的标签标 HEAD 徽标)。 */\n headSha?: string | null\n /** 提交明细当前打开的那条。 */\n activeSha?: string | null\n truncated?: boolean\n onOpen?: ((tag: HudTag) => void) | null\n}\n\n/**\n * 标签列表:一行一个,tag 名 + 它指向的提交 sha + 相对时间;指向当前 HEAD 的标 HEAD。\n * 点一行 = 用右栏的提交明细看这个 tag 指向的那次提交(复用同一条管道,不新增界面)。\n */\nexport function TagList(props: TagListProps): React.ReactElement | null {\n // 原样保留 `null`(\"还没取到\")—— 写成 `props.tags || []` 会把\"没取到\"和\"确实没有\"合并,\n // 于是面板一打开就先闪一行\"还没有标签\"。\n var tags = props.tags\n var t = props.t\n // `null` = 还没取到(先不画,避免闪一下\"还没有标签\");`[]` = 取回了、确实一个都没有 →\n // 这时要**画一行写明**:整段不画的话,用户没法区分\"这个仓库没有 tag\"与\"这个功能没做\"(实测踩过)。\n if (!Array.isArray(tags)) return null\n if (tags.length === 0) {\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h('div', { className: 'dshh-note', 'data-tags-empty': '1' }, t('panel.tagsEmpty')),\n )\n }\n var headSha = props.headSha\n\n return h(\n 'div',\n { className: 'dshh-section' },\n h('div', { className: 'dshh-sectionTitle' }, t('panel.tags')),\n h(\n 'div',\n { className: 'dshh-commits' },\n tags.map(function (tag, index) {\n var isHead =\n typeof headSha === 'string' &&\n headSha.length > 0 &&\n (tag.sha === headSha || headSha.indexOf(tag.sha) === 0)\n var tip = [\n t('tag.click'),\n '',\n tag.subject || '',\n t(tag.annotated ? 'tag.annotated' : 'tag.lightweight') +\n ' · ' +\n tag.sha +\n ' · ' +\n (typeof tag.committedAt === 'number' ? fullWhen(tag.committedAt) : tag.date || ''),\n ].join('\\n')\n var open = sameCommit(tag.sha, props.activeSha)\n return h(\n 'button',\n {\n type: 'button',\n className: 'dshh-commit' + (open ? ' dshh-commitActive' : ''),\n 'data-tag-row': tag.name,\n key: tag.name + ':' + String(index),\n title: tip,\n onClick: function () {\n if (props.onOpen) props.onOpen(tag)\n },\n },\n h('span', { className: 'dshh-ctag' }, tag.name),\n h('span', { className: 'dshh-csha' }, tag.sha),\n isHead ? h('span', { className: 'dshh-chead' }, 'HEAD') : null,\n h('span', { className: 'dshh-cwhen' }, commitWhen(tag, t)),\n )\n }),\n ),\n props.truncated === true\n ? h('div', { className: 'dshh-note' }, t('panel.tagsCapped', { n: String(tags.length) }))\n : null,\n )\n}\n","/**\n * 面板的三段行为,各自一个 hook:键盘与\"点进来\"手势、视口变化后夹回位置、量 dock 行位置。\n *\n * 提成 hook 的理由:这些是 `FloatingPanel` 里最费劲的部分,而它们只依赖 `store` 与几个快照字段 ——\n * 组件从此只剩\"hooks 调用 + JSX\"。\n */\n\nimport { activeElement, isInsidePanel, isTypingTarget, measureDockTop } from '../dom.js'\nimport { clampPos, panelWidthOf, visibleChangeRows } from '../shared.js'\nimport type { HudStatus, HudStore, PanelPos } from '../shared.js'\nimport React from 'react'\n\n/**\n * 根作用域的座位:读不到当前会话,只读共享 store。\n * 收起时渲染 `null` —— 这样不会在输入框上留一个挡点击的气泡。\n */\n/**\n * 面板的键盘与\"点进来\"手势:Esc 逐层退(右栏 → 收起面板),其余键只在面板被激活时接管。\n *\n * 提成顶层 hook 的理由:这段是面板里最费劲的一部分,而它只依赖 `store` 与 `panelOpen`\n * (`armed` 是就地可变的对象,按引用传进来,语义与原来完全一致)。\n */\nexport function usePanelKeys(\n store: HudStore,\n panelOpen: boolean,\n armed: { inside: boolean },\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var onKeyDown = function (event: KeyboardEvent) {\n // Esc 任何时候都收(和以前一样:焦点在输入框里也能收到)。\n if (event.key === 'Escape') {\n // 和 dsh-git 一样\"退一层\":右栏(差异 / 提交明细)→ 收起面板。\n var current = store.getSnapshot()\n if (current.diff || current.commit) store.closeDetail()\n else store.openPanel(false)\n return\n }\n\n // 其余按键只在面板被激活时接管:先在面板里点一下(或 Tab 进去)。\n // 本插件是挂在输入框上方的浮层,抢键的代价(打字、翻菜单、滚会话\n // 全被吃掉)远大于多几个快捷键,所以宁可要一次\"点进来\"。\n if (!armed.inside && !isInsidePanel(activeElement())) return\n // 正在输入时一律不管。\n if (isTypingTarget(event.target)) return\n // 带修饰键的组合交给产品与浏览器。\n if (event.metaKey || event.ctrlKey || event.altKey) return\n\n var handled = true\n if (event.key === 'ArrowDown') store.stepFile(1, false)\n else if (event.key === 'ArrowUp') store.stepFile(-1, false)\n else if (event.key === ']') store.stepFile(1, true)\n else if (event.key === '[') store.stepFile(-1, true)\n else if (event.key === 'Enter') store.openCursor()\n else if (event.key === 'n') store.stepHunk(1)\n else if (event.key === 'p') store.stepHunk(-1)\n else if (event.key === ' ' || event.code === 'Space') {\n var openDiff = store.getSnapshot().diff\n store.toggleHunk(openDiff ? openDiff.hunk : 0)\n } else if (event.key === 'r') store.refresh()\n // `v`:把当前聚焦的那一行标成\"这一版我审过了\"(再按一次取消)。\n // 用键盘而不是给行尾那个标记加 tabindex:行本身是按钮,里面再塞一个按钮是无效结构。\n else if (event.key === 'v') {\n var snapshot = store.getSnapshot()\n var rows = visibleChangeRows(snapshot)\n var target =\n snapshot.cursor !== null && snapshot.cursor !== undefined ? snapshot.cursor : null\n if (target === null) target = rows[0] ? rows[0].file : null\n if (target !== null) store.toggleReviewed(target)\n } else handled = false\n\n if (handled) {\n // 认领了的键就别再让浏览器滚动页面,也别让产品再处理一遍。\n event.preventDefault()\n event.stopPropagation()\n }\n }\n\n var onPointerDown = function (event: PointerEvent) {\n armed.inside = isInsidePanel(event.target)\n }\n\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('pointerdown', onPointerDown, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('pointerdown', onPointerDown, true)\n }\n },\n [panelOpen, store],\n )\n}\n\n/** 视口变化后把已记忆的面板位置夹回可见区域。 */\nexport function usePanelClamp(store: HudStore, pos: PanelPos | null): void {\n React.useEffect(\n function () {\n if (!pos) return undefined\n var onResize = function () {\n var next = clampPos(pos.left, pos.top, panelWidthOf(store))\n if (next.left !== pos.left || next.top !== pos.top) store.movePanel(next)\n }\n window.addEventListener('resize', onResize)\n return function () {\n window.removeEventListener('resize', onResize)\n }\n },\n [pos, store],\n )\n}\n\n/** 量 dock 行位置:面板据此贴到胶囊上方(会话/状态变化都会重量一次,所以输入框移位时面板跟着走)。 */\nexport function useDockAnchor(\n store: HudStore,\n panelOpen: boolean,\n sessionId: string | null | undefined,\n status: HudStatus | null,\n): void {\n React.useEffect(\n function () {\n if (!panelOpen) return undefined\n var measure = function () {\n store.setAnchorTop(measureDockTop())\n }\n measure()\n window.addEventListener('resize', measure)\n return function () {\n window.removeEventListener('resize', measure)\n }\n },\n [panelOpen, sessionId, status, store],\n )\n}\n","/**\n * 悬浮面板本体:头部、概览、左栏改动清单、中间提交栏、右栏差异/提交明细,以及拖拽与抓握。\n *\n * 三段行为(键盘、视口夹回、dock 锚点)在 `hooks/panel.ts` 里;这里只剩 hooks 调用与 JSX。\n */\n\nimport { reviewView } from '../shared.js'\nimport React from 'react'\n\nimport { ChangeList } from './change-list.js'\nimport { CommitList } from './commit-list.js'\nimport { CommitPane } from './commit-pane.js'\nimport { CountChips } from './count-chips.js'\nimport { DiffView } from './diff-view.js'\nimport { SessionChangesLine } from './session-changes.js'\nimport { TagList } from './tag-list.js'\nimport { branchLabel, errorText, timeLabel } from '../format.js'\nimport { useDockAnchor, usePanelClamp, usePanelKeys } from '../hooks/panel.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudChange, HudLogEntry, HudStore, HudTag, Translate } from '../shared.js'\nimport {\n COMMIT_LIST_WIDTH,\n COMPOSER_OFFSET,\n DIFF_DEFAULT_WIDTH,\n DIFF_PANEL_HEIGHT_VH,\n DRAG_THRESHOLD_PX,\n PANEL_DOCK_GAP,\n PANEL_WIDTH,\n VIEWPORT_MARGIN,\n clampPos,\n h,\n panelLayout,\n panelWidthFor,\n panelWidthOf,\n sessionChangeView,\n visibleChangeRows,\n} from '../shared.js'\n\n/** 头部拖拽的现场(指针 id + 起点 + 按下时指针在面板内的偏移)。 */\ninterface DragState {\n pointerId: number\n startX: number\n startY: number\n offsetX: number\n offsetY: number\n}\n\n/** 抓握拖拽的现场(调哪一栏 + 指针 id + 起点与起始宽度)。 */\ninterface GripState {\n kind: 'diff' | 'commit'\n pointerId: number\n startX: number\n startWidth: number\n}\n\n/** `HudFloatingPanel` 的 props:共享 store(面板状态都在它的快照里)。 */\nexport interface HudFloatingPanelProps {\n t?: Translate\n store: HudStore\n}\n\nexport function HudFloatingPanel(props: HudFloatingPanelProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var store = props.store\n var state = useStore(store)\n\n var dragRef = React.useState<DragState | null>(null)\n var drag = dragRef[0]\n var setDrag = dragRef[1]\n\n var gripRef = React.useState<GripState | null>(null)\n /**\n * \"面板已激活\"标记:在面板里按过指针(或 Tab 进去)才允许接管裸按键。\n *\n * 用可变对象而不是 React state:事件处理里读的必须是最新值,而重建监听\n * 只依赖 `panelOpen`,改这个标记不该触发重渲染。\n * 必须和其它 hook 一起放在下面那个 `!panelOpen → null` 早退之前。\n */\n var armedState = React.useState(function () {\n return { inside: false }\n })\n var armed = armedState[0]\n usePanelKeys(store, state.panelOpen, armed)\n usePanelClamp(store, state.pos)\n useDockAnchor(store, state.panelOpen, state.sessionId, state.status)\n var grip = gripRef[0]\n var setGrip = gripRef[1]\n\n /**\n * 拖动:只在头部按下、并且**不是**按在按钮上时开始;移动走 window 监听,\n * 刻意**不用** `setPointerCapture` —— 一旦在头部捕获指针,`pointerup` 会被\n * 重定向到头部,`click` 便落在按下与松开的公共祖先(头部)上,按钮的\n * `onClick` 永远不触发,面板就「关不掉」了。\n */\n React.useEffect(\n function () {\n if (!drag) return undefined\n // 闭包里用局部常量保住上面的非空收窄(`drag` 是 `var`,TS 在回调里会重新放宽成可空)。\n const activeDrag = drag\n var moved = false\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n if (\n !moved &&\n Math.abs(event.clientX - activeDrag.startX) < DRAG_THRESHOLD_PX &&\n Math.abs(event.clientY - activeDrag.startY) < DRAG_THRESHOLD_PX\n )\n return\n moved = true\n store.movePanel(\n clampPos(\n event.clientX - activeDrag.offsetX,\n event.clientY - activeDrag.offsetY,\n panelWidthOf(store),\n ),\n )\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeDrag.pointerId) return\n setDrag(null)\n // 只有真的拖动过才落盘:头部上的普通点击不该改变位置。\n if (moved) {\n // 拖动过程中 movePanel 已经把位置写进快照,这里收窄只是 strictNullChecks 需要。\n var next = store.getSnapshot().pos\n if (next !== null) store.commitPanel(next)\n }\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [drag, store],\n )\n\n /**\n * 拖动面板**右边缘**调整差异栏宽度(与 dsh-git 的抓握位置一致):\n * 往左拖变宽、往右拖变窄;松手才落盘。\n */\n React.useEffect(\n function () {\n if (!grip) return undefined\n // 同拖动:回调里用局部常量保住非空收窄。\n const activeGrip = grip\n var onMove = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n if (activeGrip.kind === 'commit')\n store.setCommitWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n else store.setDiffWidth(activeGrip.startWidth - (event.clientX - activeGrip.startX))\n }\n var finish = function (event: PointerEvent) {\n if (event.pointerId !== activeGrip.pointerId) return\n setGrip(null)\n store.persistLayout()\n }\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', finish)\n window.addEventListener('pointercancel', finish)\n return function () {\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', finish)\n window.removeEventListener('pointercancel', finish)\n }\n },\n [grip, store],\n )\n\n if (!state.panelOpen) return null\n\n /**\n * 抓握按下。\n *\n * `kind` 决定它调哪一栏:`diff` = 面板右边缘那条(调差异栏);`commit` = 提交栏右边那条\n * (调提交明细栏)。只有提交栏、没有差异栏时,右边缘那条也调提交栏 —— 它是右边界上唯一\n * 还能变宽的东西,不该按下去没反应。\n */\n function onGripPointerDown(\n kind: GripState['kind'],\n event: React.PointerEvent<HTMLDivElement>,\n ): void {\n if (event.button !== 0) return\n var snapshot = store.getSnapshot()\n event.preventDefault()\n var start =\n kind === 'commit'\n ? typeof snapshot.commitWidth === 'number'\n ? snapshot.commitWidth\n : COMMIT_LIST_WIDTH\n : typeof snapshot.diffWidth === 'number'\n ? snapshot.diffWidth\n : DIFF_DEFAULT_WIDTH\n setGrip({ kind: kind, pointerId: event.pointerId, startX: event.clientX, startWidth: start })\n }\n\n function onHeaderPointerDown(event: React.PointerEvent<HTMLDivElement>): void {\n if (event.button !== 0) return\n // DOM 边界:事件的 target 是 EventTarget,`closest` 只存在于 Element 上,按能力判断前先收窄。\n var target = event.target as Element | null\n // 头部里的按钮要保持普通点击,否则拖动会吞掉它们。\n if (target && typeof target.closest === 'function' && target.closest('button')) return\n // DOM 边界:头部的父节点就是面板容器(渲染结构固定),这里按元素取几何。\n var rect = (event.currentTarget.parentNode as HTMLElement).getBoundingClientRect()\n setDrag({\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n offsetX: event.clientX - rect.left,\n offsetY: event.clientY - rect.top,\n })\n }\n\n function onHeaderDoubleClick(): void {\n store.resetPanel()\n }\n\n var layout = panelLayout(state)\n var diffMode = layout.diff\n // 提交明细在场时右栏也在场(明细是中间那一栏,差异是右边那一栏),\n // 而当时 `state.diff` 可能还在路上 —— 因此两种情形都算\"宽面板\"。\n var commitMode = layout.commit\n var wide = diffMode || commitMode\n var dockTop = typeof state.anchorTop === 'number' ? state.anchorTop : null\n // 贴到胶囊所在行的上方;量不到就退回 dsh-git 记录的那次测量(168px)。\n var anchorBottom =\n dockTop === null\n ? COMPOSER_OFFSET\n : Math.max(VIEWPORT_MARGIN, window.innerHeight - dockTop + PANEL_DOCK_GAP)\n\n var panelStyle: React.CSSProperties = {\n position: 'fixed',\n zIndex: 30,\n boxSizing: 'border-box',\n width: panelWidthFor(layout, state.diffWidth, state.commitWidth),\n maxWidth: 'calc(100vw - 16px)',\n display: 'flex',\n flexDirection: 'column',\n border: '1px solid var(--dsw-alias-border-l1, rgba(127,127,127,0.3))',\n borderRadius: 12,\n // 表面用\"抬升面\"令牌:DSH 自己的浮层几乎都用 bg-layer-1,而 bg-overlay\n // 在暗色下是中灰 bluish-700,拿它当面板底色会发灰、与产品观感不一致。\n background: 'var(--dsw-alias-bg-layer-1, #1f2126)',\n boxShadow: 'var(--dsw-shadow-lv2, 0 4px 12px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.12))',\n overflow: 'hidden',\n pointerEvents: 'auto',\n }\n\n if (wide) {\n // 固定高度:面板以 bottom 锚定,若只给 max-height,差异到达时面板会长高并整体跳位。\n var spaceAbove =\n (dockTop === null ? window.innerHeight - anchorBottom : dockTop - PANEL_DOCK_GAP) -\n VIEWPORT_MARGIN\n panelStyle.height = Math.max(\n 240,\n Math.min(Math.round(window.innerHeight * DIFF_PANEL_HEIGHT_VH), spaceAbove),\n )\n } else {\n panelStyle.maxHeight = '62vh'\n }\n\n // 未拖动过:锚在胶囊上方居中;拖动过:显式坐标。\n if (state.pos) {\n panelStyle.left = state.pos.left\n panelStyle.top = state.pos.top\n } else {\n panelStyle.left = '50%'\n panelStyle.bottom = anchorBottom\n panelStyle.transform = 'translateX(-50%)'\n }\n\n // 单栏内容(\"面板本来就长这样\"):两种模式共用同一份,\n // 打开差异时它原封不动地留在左栏,只有右栏是新出现的。\n // \"正在看的那份差异\"与\"键盘选中的那行\"是两件事:前者是打开的文件,\n // 后者可能只是选过还没打开(↑/↓ 移动,Enter 才打开)。两个标记都留着。\n // 左栏那份清单只关心工作区差异:提交明细打开时它不该跟着高亮。\n var openDiff = state.diff\n var openCommit = state.commit\n // 工作区差异(含未暂存/已暂存两页签)与提交差异是两种右栏内容。\n var openFile = openDiff !== null && openDiff.source !== 'commit' ? openDiff.file : null\n var commitOpenFile = openDiff !== null && openDiff.source === 'commit' ? openDiff.file : null\n\n // 会话归因视图与过滤后的清单:渲染期只算一次(标记、过滤、概览那行共用)。\n var sessionView = sessionChangeView(state)\n var sessionRows = visibleChangeRows(state)\n\n var emptyBody: React.ReactElement\n if (state.phase === 'error') {\n emptyBody = h('div', { className: 'dshh-empty' }, errorText(state.errorCode, t))\n } else if (!state.cwd) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.noSession'))\n } else if (state.phase === 'loading' && !state.status) {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.loading'))\n } else {\n emptyBody = h('div', { className: 'dshh-empty' }, t('panel.notRepo'))\n }\n\n var listBody\n if (state.status && state.status.repo) {\n var status = state.status\n var repoChildren: React.ReactNode[] = [\n h(\n 'div',\n { className: 'dshh-meta' },\n h('span', { className: 'dshh-repo', title: status.repo }, status.repo),\n status.upstream\n ? h('span', { className: 'dshh-kv' }, t('panel.upstream') + ' ' + status.upstream)\n : null,\n status.ahead > 0\n ? h('span', { className: 'dshh-kv' }, '↑ ' + t('panel.ahead') + String(status.ahead))\n : null,\n status.behind > 0\n ? h('span', { className: 'dshh-kv' }, '↓ ' + t('panel.behind') + String(status.behind))\n : null,\n status.oid ? h('span', { className: 'dshh-kv' }, status.oid) : null,\n ),\n h('div', { className: 'dshh-sectionTitle' }, t('panel.summary')),\n h(CountChips, { counts: status.counts, t: t }),\n h(SessionChangesLine, {\n t: t,\n view: sessionView,\n onlySession: state.onlySession === true,\n onToggle: function () {\n store.toggleOnlySession()\n },\n onlyLatest: state.onlyLatest === true,\n onToggleLatest: function () {\n store.toggleOnlyLatest()\n },\n review: reviewView(state),\n onClearReviewed: function () {\n store.clearReviewed()\n },\n }),\n // 顺序:改动概览 → **改动清单** → 最近提交。清单是\"现在要处理的\",\n // 提交是背景信息;清单放前面,一屏不够时先看到的是眼前的事。\n sessionRows.length > 0\n ? h(ChangeList, {\n changes: sessionRows,\n truncated: status.truncated,\n t: t,\n onOpen: function (change: HudChange) {\n store.openDiff(change)\n },\n activeFile: openFile,\n cursor: commitMode ? null : state.cursor,\n listId: 'changes',\n sessionView: sessionView,\n review: reviewView(state),\n onToggleReviewed: function (file: string) {\n store.toggleReviewed(file)\n },\n })\n : state.status.changes.length > 0 && state.onlySession === true && sessionView !== null\n ? // 过滤器开着、本会话又什么都没改:说一句,而不是让清单整段消失。\n h('div', { className: 'dshh-note' }, t('session.empty'))\n : null,\n h(CommitList, {\n commits: state.commits,\n t: t,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 再点一次打开着的那条 = 收起明细(打开提交不改左栏,只高亮它)。\n onOpen: function (commit: HudLogEntry) {\n store.toggleCommit(commit)\n },\n }),\n h(TagList, {\n tags: state.tags,\n truncated: state.tagsTruncated,\n t: t,\n headSha: state.status && state.status.oid ? state.status.oid : null,\n activeSha: commitMode && openCommit !== null ? openCommit.sha : null,\n // 点一个 tag = 看它指向的那次提交(tag 自己不是提交,所以把提交信息填进去)。\n onOpen: function (tag: HudTag) {\n store.toggleCommit({\n sha: tag.sha,\n subject: tag.subject || '',\n author: '',\n date: tag.date || '',\n committedAt: typeof tag.committedAt === 'number' ? tag.committedAt : null,\n refs: [tag.name],\n })\n },\n }),\n ]\n listBody = h('div', { className: 'dshh-body' }, ...repoChildren)\n } else {\n listBody = emptyBody\n }\n\n // 工作台:左栏**始终**是上面那份单栏内容(宽度也与单栏一致);打开提交明细时\n // 中间多一栏(这次提交的元信息 + 改动清单),差异固定在右边那一栏。\n // 左栏一个像素都不动,面板右边缘的抓握仍只调整差异栏——与 dsh-git 的 workbench 同形。\n var body = wide\n ? h(\n 'div',\n { className: 'dshh-workbench' },\n h('div', { className: 'dshh-leftpane', style: { width: PANEL_WIDTH } }, listBody),\n commitMode && openCommit !== null\n ? h(CommitPane, {\n t: t,\n commit: openCommit,\n onOpenFile: function (file) {\n store.openCommitDiff(file)\n },\n activeFile: commitOpenFile,\n cursor: state.cursor,\n width: typeof state.commitWidth === 'number' ? state.commitWidth : COMMIT_LIST_WIDTH,\n })\n : null,\n commitMode && diffMode\n ? h('div', {\n className: 'dshh-grip dshh-gripBetween',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n diffMode && openDiff !== null\n ? h(DiffView, { t: t, diff: openDiff, store: store, cwd: state.cwd })\n : null,\n )\n : listBody\n\n return h(\n 'div',\n { className: 'dshh-panel', 'data-dsh-hud': 'panel', style: panelStyle },\n h(\n 'div',\n {\n className: 'dshh-header',\n title: t('panel.dragHint'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onHeaderPointerDown(event)\n },\n onDoubleClick: onHeaderDoubleClick,\n },\n h('span', { className: 'dshh-title' }, t('panel.title')),\n state.status && state.status.repo\n ? h('span', { className: 'dshh-branch' }, branchLabel(state.status, t))\n : null,\n h('span', { className: 'dshh-spacer' }),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.refresh'),\n 'aria-label': t('panel.refresh'),\n onClick: function () {\n store.refresh()\n },\n },\n '⟳',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.reset'),\n 'aria-label': t('panel.reset'),\n onClick: function () {\n store.resetPanel()\n },\n },\n '⌖',\n ),\n h(\n 'button',\n {\n type: 'button',\n className: 'dshh-icon',\n title: t('panel.close'),\n 'aria-label': t('panel.close'),\n onClick: function () {\n store.openPanel(false)\n },\n },\n '✕',\n ),\n ),\n body,\n diffMode\n ? h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip',\n title: t('diff.resize'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('diff', event)\n },\n })\n : commitMode\n ? // 只有提交栏:右边缘这条改成调它(否则按下去没反应)。\n h('div', {\n className: 'dshh-grip',\n 'data-dsh-hud': 'grip-commit',\n title: t('diff.resizeCommit'),\n onPointerDown: function (event: React.PointerEvent<HTMLDivElement>) {\n onGripPointerDown('commit', event)\n },\n })\n : null,\n h(\n 'div',\n { className: 'dshh-footer' },\n h('span', null, t('panel.readonly')),\n h('span', null, state.updatedAt ? t('panel.updated') + timeLabel(state.updatedAt) : ''),\n ),\n )\n}\n","/**\n * 悬停气泡本体:位置由 store 里的 tip 快照给(`shell.overlay` 座位里渲染)。\n */\n\nimport type React from 'react'\n\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `PillTip` 的 props:共享 store(气泡文案与锚点都在它的快照里)。 */\nexport interface PillTipProps {\n store: HudStore\n}\n\n/**\n * 胶囊悬停提示的气泡。**自己画**,不用浏览器原生 `title`。\n *\n * 由一个独立的 `shell.overlay` 座位渲染、`position:fixed` 定位 —— 这样它不受 dock 那行的\n * 裁切与堆叠上下文限制。位置只用锚点矩形的 `top` 与 `right`:\n * 贴在那枚胶囊**上方**,横向按视口夹住(不量自己的宽度,避免\"先渲染再回填\"的抖动)。\n */\nexport function PillTip(props: PillTipProps): React.ReactElement | null {\n var store = props.store\n var state = useStore(store)\n var tip = state.tip\n\n if (tip === null || tip === undefined) return null\n var innerWidth = typeof window.innerWidth === 'number' ? window.innerWidth : 1200\n var innerHeight = typeof window.innerHeight === 'number' ? window.innerHeight : 800\n var margin = 8\n var left = Math.max(\n margin,\n Math.min(typeof tip.rect.left === 'number' ? tip.rect.left : margin, innerWidth - 428),\n )\n var bottom = Math.max(\n margin,\n innerHeight - (typeof tip.rect.top === 'number' ? tip.rect.top : 0) + 6,\n )\n\n return h(\n 'div',\n {\n className: 'dshh-tip',\n 'data-dsh-hud': 'pill-tip',\n role: 'tooltip',\n style: { left: String(left) + 'px', bottom: String(bottom) + 'px' },\n },\n tip.text,\n )\n}\n","/**\n * 侧栏那两处按需展示:⋯ 菜单里的会话摘要、悬停卡片。\n */\n\nimport { branchLabel, rowSummaryOf } from '../format.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type { HudStore, Translate } from '../shared.js'\nimport { h } from '../shared.js'\n\n/** `⋯` 菜单摘要的 props(座位 `sidebar.workspaces.session.menu.item`)。 */\nexport interface SessionMenuSummaryProps {\n t?: Translate\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n store: HudStore\n}\n\n/** 悬停卡片的 props(座位 `sidebar.session.row.hover`)。 */\nexport interface SessionRowHoverChipProps {\n t?: Translate\n sessionId?: string | null\n store: HudStore\n}\n\n/**\n * `⋯` 菜单里的只读摘要(座位 `sidebar.workspaces.session.menu.item`)。\n *\n * 为什么要有这一处:会话行**行尾的常亮位置归产品**(`time` 自适应宽度 + 悬停才换上的动作条),\n * 插件在行尾拿不到常亮的座位;行首那格只有 16px,只能放一个双字符标记。所以要\"完整信息 +\n * 不用等悬停卡片那 800ms\",`⋯` 菜单是最实在的地方 —— 点一下就有,且不用把鼠标停住。\n *\n * 它是**只读**的:不做任何动作,所以做得像一条说明(弱化颜色、不响应 hover 高亮);\n * 工作区干净时也照实说\"干净\",而不是消失(那样用户会以为没有这个功能)。\n */\nexport function SessionMenuSummary(props: SessionMenuSummaryProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n\n var head: string\n if (summary.row.repo === true) {\n head = branchLabel(summary.row, t)\n if (summary.dirty === true) head += '*'\n var aheadOf = summary.row.ahead\n var behindOf = summary.row.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n var counts = summary.shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n head += counts === '' ? ' · ' + t('panel.clean') : ' · ' + counts\n } else if (summary.row.error !== null) {\n head = t('rows.error', { code: String(summary.row.error) })\n } else {\n head = t('rows.noRepo')\n }\n\n var own: string[] = []\n if (summary.mine > 0) own.push(t('session.pill', { n: String(summary.mine) }))\n if (summary.possible > 0) {\n own.push(t('rows.possible', { n: String(summary.possible), now: String(summary.possibleNow) }))\n }\n var ownLine = own.length === 0 ? t('rows.ownNone') : own.join(' · ')\n\n return h(\n 'div',\n {\n className: 'dshh-menureadout',\n 'data-dsh-hud': 'menu-readout',\n title: summary.row.cwd || undefined,\n },\n [\n h('div', { key: 'git', className: 'dshh-menureadoutLine' }, head),\n h('div', { key: 'own', className: 'dshh-menureadoutLine' }, ownLine),\n ],\n )\n}\n\n/**\n * 侧栏会话行**悬停卡片**里的那一块(座位 `sidebar.session.row.hover`)。\n *\n * 动作条那一枚只有图标级宽度(`!4 ●3`),明细放不下;悬停卡是宽敞的地方 ——\n * 分支/上游、分类型计数、这个会话自己的改动,都在这里说清楚。\n * 没有任何可说的(干净、且本会话没改过)就整块不画,卡片保持原来的样子。\n */\nexport function SessionRowHoverChip(props: SessionRowHoverChipProps): React.ReactElement | null {\n var t: Translate =\n props.t ||\n function (key: string) {\n return key\n }\n var sessionId = props.sessionId\n var store = props.store\n var state = useStore(store)\n if (!sessionId) return null\n var row = state.rows && state.rows.byId ? state.rows.byId[sessionId] : undefined\n var summary = rowSummaryOf(row)\n if (summary === null) return null\n // 上面已确认 summary 非空,它带的 row 就是刚落下来的那一行(同一个对象)。\n var rowData = summary.row\n var mineCount = summary.mine\n var possibleCount = summary.possible\n var shorthands = summary.shorthands\n\n var lines: string[] = []\n if (rowData.repo === true) {\n var head = branchLabel(rowData, t)\n if (rowData.dirty === true) head += '*'\n var aheadOf = rowData.ahead\n var behindOf = rowData.behind\n if (typeof aheadOf === 'number' && aheadOf > 0) head += ' ↑' + String(aheadOf)\n if (typeof behindOf === 'number' && behindOf > 0) head += ' ↓' + String(behindOf)\n if (shorthands.length > 0) {\n head +=\n ' · ' +\n shorthands\n .map(function (item) {\n return t('count.' + item.key) + ' ' + String(item.value)\n })\n .join(' ')\n } else {\n // 干净也要说出来 —— 与悬停说明、⋯ 菜单同一个口径(空着看不出是\"干净\"还是\"没读到\")。\n head += ' · ' + t('panel.clean')\n }\n lines.push(head)\n } else if (rowData.repo === false && rowData.error === null) {\n lines.push(t('rows.noRepo'))\n }\n if (rowData.error) lines.push(t('rows.error', { code: String(rowData.error) }))\n var own: string[] = []\n if (mineCount > 0) own.push(t('session.pill', { n: String(mineCount) }))\n if (possibleCount > 0) {\n own.push(t('rows.possible', { n: String(possibleCount), now: String(summary.possibleNow) }))\n }\n // 和第二行对齐:没有改动记录时也明说一句 —— 否则卡片上会缺一行,看着像没加载出来。\n lines.push(own.length === 0 ? t('rows.ownNone') : own.join(' · '))\n if (lines.length === 0) return null\n\n return h(\n 'div',\n {\n className: 'dshh-rowhover',\n 'data-dsh-hud': 'row-hover',\n },\n lines.map(function (line, index) {\n return h('div', { key: String(index), className: 'dshh-rowhoverLine' }, line)\n }),\n )\n}\n","/**\n * 轮尾那一行「本轮图片改动」:只列本轮动过的图,点缩略图走内置预览,⇄ 进 hud 看前后对比。\n */\n\nimport React from 'react'\n\nimport { looksLikeImage } from './image-diff.js'\nimport type { ImagePairEntry } from './image-diff.js'\nimport { useStore } from '../hooks/use-store.js'\nimport type {\n HudChange,\n HudStore,\n ImagePreview,\n ImageRequest,\n SessionChange,\n SessionChangeCandidate,\n SessionChangesState,\n Translate,\n} from '../shared.js'\nimport { h } from '../shared.js'\n\n/**\n * 框架 `useSessions` 快照里本插件用到的会话摘要字段(等 shared 收尾后视需要上提)。\n */\ninterface TurnSessionSummaryLike {\n cwd?: string | null\n}\n\n/** `useSessions` 快照的子集:只用到按 sessionId 索引的那张表。 */\ninterface TurnSessionsSnapshotLike {\n byId?: Record<string, TurnSessionSummaryLike>\n}\n\n/** 框架给座位的选择器 hook:选择器参数是必需的。 */\ntype TurnUseSessions = <T>(selector: (snapshot: TurnSessionsSnapshotLike) => T) => T\n\n/** 「本轮改过的一张图」:文件、档位(确定/命令旁听)、那条命令与最近触达时刻。 */\nexport interface TurnImagePick {\n file: string\n name: string\n tier: 'confirmed' | 'possible'\n reason: string\n at: number\n}\n\n/** `imageChangesOf()` 的结果:挑出来的条目 + `exact`(标题该说\"本轮\"还是\"本会话\")。 */\nexport interface TurnImageChanges {\n files: TurnImagePick[]\n exact: boolean\n}\n\n/** `TurnImages` 的 props:store + 框架注入的会话/回合信息 + 内置预览的打开函数。 */\nexport interface TurnImagesProps {\n t: Translate\n store: HudStore\n /** 座位按会话作用域注入的 sessionId。 */\n sessionId?: string | null\n useSessions?: TurnUseSessions | null\n /** 内置右侧文档预览的\"打开这个文件\"(缩略图点击用)。 */\n openFile?: ((path: string) => void) | null\n /** 槽位给的 TurnLocation(宿主内部形状),只用来取回合号。 */\n turn?: unknown\n}\n\n/**\n * 轮尾那条「本轮图片改动」:每项一张缩略图 + 文件名。\n *\n * 内置的改动卡片点一行是打开 review tab(图片在那儿看不见),这条是补上的门:\n * **点缩略图 → 槽位给的 `openFile(path)`**,即内置的右侧文档预览(图片原生渲染、可缩放);\n * 旁边那个 `⇄` 才打开 dsh-hud 面板看前后两张 —— 前后对比只有我们有。\n */\nexport function TurnImages(props: TurnImagesProps): React.ReactElement | null {\n var t = props.t\n var store = props.store\n var sessionId = props.sessionId\n var useSessions = props.useSessions\n var openFile = props.openFile\n var turnNumber = turnNumberOf(props.turn)\n\n // useSessions 是选择器 hook:选择器参数是必需的。\n var sessions =\n typeof useSessions === 'function'\n ? useSessions(function (snapshot) {\n return snapshot\n })\n : null\n var summary =\n typeof sessionId === 'string' && sessions && sessions.byId\n ? sessions.byId[sessionId]\n : undefined\n var cwd = summary ? summary.cwd : undefined\n\n var state = useStore(store)\n // 缩略图要等字节;缓存本身不在快照里,所以取完自增一下触发重画。\n var tick = React.useState(0)\n var bump = tick[1]\n\n // 轮尾每个已完成的回合都会挂一次:绑定会话 + 没取过会话改动就取一次(宿主侧是内存读,很便宜)。\n React.useEffect(\n function () {\n if (sessionId === undefined || cwd === undefined) return undefined\n store.bindSession(sessionId, cwd)\n if (state.sessionChanges === undefined || state.sessionChanges.phase === 'idle')\n store.loadSessionChanges()\n return undefined\n },\n [sessionId, cwd],\n )\n\n var picked = imageChangesOf(state.sessionChanges, turnNumber)\n var shown = picked.files.slice(0, 4)\n var keys = shown\n .map(function (entry) {\n return entry.file\n })\n .join('\\u0000')\n\n React.useEffect(\n function () {\n if (keys === '' || typeof cwd !== 'string') return undefined\n // 闭包里用局部常量保住上面的收窄(`cwd` 是 `var`,TS 在回调里会重新放宽成可空)。\n const cwdOf = cwd\n var cancelled = false\n var wanted: ImageRequest[] = shown\n .map(function (entry) {\n return { cwd: cwdOf, path: entry.file, side: 'worktree' }\n })\n .filter(function (request) {\n return store.cachedImagePair(request) === null\n })\n if (wanted.length === 0) return undefined\n Promise.all(\n wanted.map(function (request) {\n return store.loadImagePair(request)\n }),\n ).then(function () {\n if (!cancelled)\n bump(function (count) {\n return count + 1\n })\n })\n return function () {\n cancelled = true\n }\n },\n [keys, cwd],\n )\n\n // 会话改动还没到、或这一轮没动过图:整条不渲染(不在每条轮尾留个空壳)。\n if (state.sessionChanges === undefined || state.sessionChanges.phase !== 'ready') return null\n if (shown.length === 0) return null\n\n var rest = picked.files.length - shown.length\n\n function chip(entry: TurnImagePick): React.ReactElement {\n var thumb = thumbSideOf(\n typeof cwd === 'string'\n ? store.cachedImagePair({ cwd: cwd, path: entry.file, side: 'worktree' })\n : null,\n )\n var dot = entry.name.lastIndexOf('.')\n var extension = dot > 0 ? entry.name.slice(dot + 1).toUpperCase() : '?'\n // `◐` 是命令旁听来的(猜的):说清这一点,并把那条命令放进悬停里。\n var hint = entry.file + ' · ' + t('turnImages.open')\n if (entry.tier === 'possible' && entry.reason !== '')\n hint += ' · ' + t('session.possibleTitle', { reason: entry.reason })\n return h(\n 'span',\n {\n key: entry.file,\n className: 'dshh-imgstripItem',\n 'data-dsh-hud': 'turn-image',\n 'data-file': entry.file,\n 'data-tier': entry.tier,\n },\n [\n h(\n 'button',\n {\n key: 'open',\n type: 'button',\n className: 'dshh-imgstripOpen',\n title: hint,\n onClick: function () {\n if (typeof openFile === 'function') openFile(entry.file)\n },\n },\n [\n thumb === null\n ? h(\n 'span',\n { key: 'ph', className: 'dshh-imgstripThumb dshh-imgstripThumbEmpty' },\n extension,\n )\n : h('img', {\n key: 'img',\n className: 'dshh-imgstripThumb',\n alt: entry.name,\n src: 'data:' + thumb.mime + ';base64,' + String(thumb.base64 || ''),\n }),\n entry.tier === 'possible'\n ? h('span', { key: 'tier', className: 'dshh-imgstripTier', title: hint }, '◐')\n : null,\n h('span', { key: 'name', className: 'dshh-imgstripName' }, entry.name),\n ],\n ),\n h(\n 'button',\n {\n key: 'compare',\n type: 'button',\n className: 'dshh-imgstripCompare',\n title: t('turnImages.compare'),\n 'data-dsh-hud': 'turn-image-compare',\n onClick: function () {\n // store 边界:这里只知道文件路径,而 `openDiff` 只读 file/path/origFile,按调用契约补形状。\n var change = { file: entry.file, path: entry.file, origFile: null } as HudChange\n store.openPanel(true)\n store.openDiff(change)\n },\n },\n '⇄',\n ),\n ],\n )\n }\n\n return h('div', { className: 'dshh-imgstrip', 'data-dsh-hud': 'turn-images' }, [\n h(\n 'span',\n { key: 'title', className: 'dshh-imgstripTitle' },\n picked.exact ? t('turnImages.title') : t('turnImages.titleSession'),\n ),\n h('span', { key: 'items', className: 'dshh-imgstripItems' }, shown.map(chip)),\n rest > 0\n ? h(\n 'span',\n { key: 'more', className: 'dshh-imgstripMore' },\n t('turnImages.more', { n: String(rest) }),\n )\n : null,\n ])\n}\n\n/**\n * 从会话改动里挑出\"这一轮改过的图片\",最近的在前。\n *\n * **两档都收**:`●` 确定改过(写文件 / 编辑工具)与 `◐` 命令旁听(`>`、`tee`、`cp`、`rm`…)。\n * 只收确定档不够用 —— 图几乎都是脚本/命令产出的(`python gen.py`、`cp`、`curl -o`),\n * 那些永远进不了确定档,而\"点一下看图\"对它们一样有用。`◐` 是猜的,所以 chip 上带 `◐`、\n * 悬停给出那条命令(措辞与面板里改动清单一致)。\n *\n * 回合号拿不到(`turnNumberOf` 回 null)就不按回合收窄,退回\"本会话最近的图片改动\"。\n *\n * @returns `{ files, exact }`;`exact` 为 false 表示标题该说\"本会话\"而不是\"本轮\"。\n */\nexport function imageChangesOf(\n changes: SessionChangesState,\n turnNumber: number | null,\n): TurnImageChanges {\n if (changes === null || changes === undefined) return { files: [], exact: turnNumber !== null }\n var confirmed: Record<string, SessionChange> = changes.files ? changes.files : {}\n var guessed: Record<string, SessionChangeCandidate> = changes.possible ? changes.possible : {}\n var picked: TurnImagePick[] = []\n var seen: Record<string, true> = {}\n\n for (var file in confirmed) {\n if (!Object.prototype.hasOwnProperty.call(confirmed, file)) continue\n if (!looksLikeImage(file)) continue\n var record: Partial<SessionChange> = confirmed[file] || {}\n if (turnNumber !== null) {\n var turns = Array.isArray(record.turns) ? record.turns : []\n if (turns.indexOf(turnNumber) < 0 && record.lastTurn !== turnNumber) continue\n }\n seen[file] = true\n picked.push({\n file: file,\n name: baseNameOf(file),\n tier: 'confirmed',\n reason: '',\n at: typeof record.lastAt === 'number' ? record.lastAt : 0,\n })\n }\n\n for (var guess in guessed) {\n if (!Object.prototype.hasOwnProperty.call(guessed, guess)) continue\n if (seen[guess] === true) continue\n if (!looksLikeImage(guess)) continue\n var item: Partial<SessionChangeCandidate> = guessed[guess] || {}\n // 命令旁听只记\"最近一次\"的回合号(没有 turns 列表),所以只能按 lastTurn 收窄。\n if (turnNumber !== null && item.lastTurn !== turnNumber) continue\n seen[guess] = true\n picked.push({\n file: guess,\n name: baseNameOf(guess),\n tier: 'possible',\n reason: typeof item.reason === 'string' ? item.reason : '',\n at: typeof item.lastAt === 'number' ? item.lastAt : 0,\n })\n }\n\n picked.sort(function (left, right) {\n return right.at - left.at\n })\n return { files: picked, exact: turnNumber !== null }\n}\n\n/** 缩略图用哪一侧的字节:优先工作区,其次基线(新增/删除时只有一侧有)。 */\nexport function thumbSideOf(entry: unknown): ImagePreview | null {\n // store 边界:图片缓存条目的形状由 store 的 image 片构造,这里只收窄成声明过的联合。\n var pair =\n entry !== null && entry !== undefined && typeof entry === 'object'\n ? (entry as ImagePairEntry)\n : null\n if (pair === null || pair.phase !== 'ready' || !pair.value) return null\n var value = pair.value\n if (value.after && value.after.kind === 'image') return value.after\n if (value.before && value.before.kind === 'image') return value.before\n return null\n}\n\n/**\n * 槽位给的是宿主的 `TurnLocation`。\n *\n * 形状属于宿主内部约定(槽位文档只说\"receives the Turn\"),所以这里宽松取值:\n * 取不到就回 null,由调用方退到\"本会话\"的说法,而不是整条不显示。\n */\nexport function turnNumberOf(turn: unknown): number | null {\n if (typeof turn === 'number' && isFinite(turn)) return turn\n if (turn !== null && turn !== undefined && typeof turn === 'object') {\n // 槽位边界:TurnLocation 的字段形状属于框架内部约定,这里只取用到的 `turn`。\n var record = turn as { turn?: unknown }\n if (typeof record.turn === 'number') return record.turn\n }\n return null\n}\n\n/** 只取文件名(chip 上放不下整条路径)。 */\nexport function baseNameOf(file: string): string {\n var parts = String(file).split('/')\n return parts[parts.length - 1] || String(file)\n}\n","/**\n * 输入框(composer)读写与 ↑ 召回。\n *\n * 这里全部是**纯 DOM 操作**(Lexical 的 contenteditable),最讲究的是\"写进去之后到底成没成\":\n * 粘贴是异步落地的,所以每次写完都要等一拍再核对(`settleComposer`),否则会写第二遍。\n */\n\nimport { probe } from './shared.js'\nimport type { HudStore } from './shared.js'\n\n/* ------------------------------------------------------- 输入框的 DOM 面 */\n\n/**\n * 插件只用到输入框根节点的这几样:找根(`getAttribute` / `isContentEditable` /\n * `parentElement`)、读文本(`innerText` / `textContent`)、写回(`textContent` /\n * `dispatchEvent`)。用**结构类型**而不是 `HTMLElement`:测试里的假 DOM 也满足它,\n * 真实节点天然满足 —— 这里要的本来也不是\"一个 HTMLElement\",而是\"能当输入框用的节点\"。\n */\ninterface ComposerRoot {\n getAttribute?: (name: string) => string | null\n isContentEditable?: boolean\n parentElement?: ComposerRoot | null\n innerText?: string\n textContent?: string | null\n dispatchEvent?: (event: Event) => boolean\n}\n\n/**\n * 结构类型 → 真 `Node`。运行时它本来就是 contenteditable 元素;`ComposerRoot` 只是为了让\n * 测试里的假 DOM 也能满足,所以这里不做运行时检查,只把类型交回给 DOM API。\n */\nfunction nodeOf(root: ComposerRoot): Node {\n return root as unknown as Node\n}\n\n/** 历史翻页方向。 */\ntype HistoryDirection = 'up' | 'down'\n/** 翻完之后光标摆在哪一端。 */\ntype HistoryCaret = 'start' | 'end'\n\n/** 翻页状态机自己的状态:不在历史里时 `index === -1`。 */\ninterface PromptHistoryNavState {\n index: number\n draft: string | null\n}\n\n/** 翻页输入:历史条目(新的在前)与当前输入框的文本/光标。 */\ninterface PromptHistoryInput {\n entries: string[]\n text?: string\n caret?: number\n}\n\n/** 状态机结果:没接管时调用方什么都不做;接管时必然给出要写入的文本与光标位置。 */\ntype PromptHistoryStep =\n | { handled: false; state: PromptHistoryNavState }\n | { handled: true; state: PromptHistoryNavState; text: string; caret: HistoryCaret }\n\n/* ------------------------------------------------------------- 文案 */\n\n/* --------------------------------------------------------- 本地 UI 状态 */\n\n/**\n * 光标此刻允许上下翻历史吗?—— 照抄 OpenCode Desktop 的 `canNavigateHistoryAtCursor`。\n *\n * 它的规矩比 Codex 保守得多:**不在历史里时,`↑` 只在输入框完全为空时才接管**。\n * 于是多行草稿里 `↑` 永远是光标移动,跟内置的 `/` 命令、`@` 提及弹窗也天然不冲突\n * (弹窗必然带着文字)。进了历史之后,只要光标在两端就能继续翻。\n */\nexport function canNavigatePromptHistory(\n direction: HistoryDirection,\n text: string,\n caret: number,\n inHistory: boolean,\n): boolean {\n var length = typeof text === 'string' ? text.length : 0\n var position = Math.max(0, Math.min(typeof caret === 'number' ? caret : 0, length))\n if (inHistory) return position === 0 || position === length\n if (direction === 'up') return position === 0 && length === 0\n return position === length\n}\n\n/**\n * `↑` / `↓` 翻 prompt 历史的**纯状态机**(语义照抄 OpenCode Desktop 的 `navigatePromptHistory`)。\n *\n * `index === -1` 表示\"不在历史里\";`entries[0]` 是最新一条。往旧翻时先把当前草稿存进\n * `draft`,一直翻到最新再往下就把草稿**原样放回**(连光标位置都按方向摆好:往旧放开头、\n * 往新放末尾)。没接管的情况一律回 `handled: false`,调用方据此**不**调用 preventDefault。\n */\nexport function promptHistoryStep(\n state: PromptHistoryNavState,\n direction: HistoryDirection,\n input: PromptHistoryInput | null | undefined,\n): PromptHistoryStep {\n var entries = (input && input.entries) || []\n var text = input && typeof input.text === 'string' ? input.text : ''\n var caret = input && typeof input.caret === 'number' ? input.caret : 0\n var index = state.index\n var draft = state.draft\n\n if (!canNavigatePromptHistory(direction, text, caret, index >= 0))\n return { handled: false, state: state }\n\n if (direction === 'up') {\n if (entries.length === 0) return { handled: false, state: state }\n if (index === -1)\n return { handled: true, state: { index: 0, draft: text }, text: entries[0], caret: 'start' }\n if (index < entries.length - 1) {\n return {\n handled: true,\n state: { index: index + 1, draft: draft },\n text: entries[index + 1],\n caret: 'start',\n }\n }\n return { handled: false, state: state } // 已经到最旧一条:把 ↑ 还给编辑器\n }\n\n if (index > 0)\n return {\n handled: true,\n state: { index: index - 1, draft: draft },\n text: entries[index - 1],\n caret: 'end',\n }\n if (index === 0) {\n return {\n handled: true,\n state: { index: -1, draft: null },\n text: draft === null || draft === undefined ? '' : draft,\n caret: 'end',\n }\n }\n return { handled: false, state: state }\n}\n\n/* -------------------------------------------- 输入框的 ↑ 召回(纯文本) */\n\n/**\n * 从一个事件目标往上找\"输入框根节点\"。\n *\n * DSH 的输入框是 **Lexical** 的 contenteditable(根节点带 `data-lexical-editor=\"true\"`),\n * 不是 textarea —— 所以不能读 `value`。手写 parentElement 上溯(不用 `closest`):\n * 一来少依赖一个 API,二来深度有上限,绝不会误判到页面别处。\n */\nexport function composerRootOf(target: unknown): ComposerRoot | null {\n // 事件目标是 DOM 边界:调用方可能是 EventTarget,我们按结构类型读它的属性。\n var node = target as ComposerRoot | null | undefined\n for (var depth = 0; depth < 6 && node !== null && node !== undefined; depth += 1) {\n if (typeof node.getAttribute === 'function') {\n var marker = node.getAttribute('data-lexical-editor')\n var editable =\n typeof node.isContentEditable === 'boolean'\n ? node.isContentEditable\n : node.getAttribute('contenteditable') === 'true'\n if (marker === 'true' || editable) return node\n }\n node = node.parentElement\n }\n return null\n}\n\n/** 输入框里的纯文本(Lexical 会把内容画在 DOM 里,`innerText` 最贴近用户看到的)。 */\nexport function composerText(root: ComposerRoot | null | undefined): string {\n if (root === null || root === undefined) return ''\n var text =\n typeof root.innerText === 'string'\n ? root.innerText\n : typeof root.textContent === 'string'\n ? root.textContent\n : ''\n return text.replace(/\\u200b/g, '')\n}\n\n/**\n * \"输入框实际上是空的吗\"用的归一化文本。\n *\n * Lexical 的空根节点在 DOM 里通常是 `<p><br></p>`,`innerText` 会给出一个换行符 ——\n * 拿它当\"有内容\"就会让 `↑` 永远排不上队(整个功能静默失效)。所以去掉零宽字符与\n * **只在末尾的空白**:开头的空格是用户真的敲进去的,不能吞。\n */\nexport function composerTextKey(root: ComposerRoot | null | undefined): string {\n return composerText(root).replace(/\\s+$/, '')\n}\n\n/** 写入是否成功:两边都归一化再比 —— Lexical 可能补一个尾换行,那不是\"没写进去\"。 */\nexport function composerTextMatches(text: string, expected: string): boolean {\n return text.replace(/\\s+$/, '') === expected.replace(/\\s+$/, '')\n}\n\n/** 光标在输入框里的字符偏移;没有选区、有选中内容、或拿不到选区时回 null(一律不接管)。 */\nexport function composerCaretOffset(root: ComposerRoot): number | null {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed !== true) return null\n var node = selection.focusNode\n if (!node) return null\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n var before = range.cloneRange()\n try {\n before.setEnd(node, selection.focusOffset)\n } catch (error) {\n return null\n }\n return before.toString().length\n}\n\n/** 把光标放到开头或末尾(`↑` 往旧翻放开头,`↓` 往回翻放末尾 —— 与 OpenCode 一致)。 */\nexport function placeCaretInComposer(root: ComposerRoot, position: HistoryCaret): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n range.collapse(position !== 'end')\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 光标没摆好不影响内容已经写进去了\n }\n}\n\n/** 全选输入框内容(模拟粘贴前先选中,粘贴才能\"替换\"而不是\"追加\")。 */\nexport function selectComposerContents(root: ComposerRoot): void {\n var selection = typeof window.getSelection === 'function' ? window.getSelection() : null\n if (!selection) return\n try {\n var range = document.createRange()\n range.selectNodeContents(nodeOf(root))\n selection.removeAllRanges()\n selection.addRange(range)\n } catch (error) {\n // 选不中也还能走 execCommand 那条退路\n }\n}\n\n/** 等\"写入落地\":微任务总有,有 rAF 的浏览器里再多等一帧(Lexical 的粘贴是帧后落 DOM)。 */\nexport function settleComposer(): Promise<void> {\n var chain = Promise.resolve()\n if (typeof window.requestAnimationFrame === 'function') {\n chain = chain.then(function () {\n return new Promise(function (resolve) {\n window.requestAnimationFrame(function () {\n resolve()\n })\n })\n })\n }\n return chain\n}\n\n/**\n * 把一段纯文本写进 Lexical 输入框 —— **三层依次降级,每层都等落地了再回读**。\n *\n * 为什么要等:Lexical 的粘贴处理是**异步**的(它得先把剪贴板读出来)。写完立刻回读会读到\n * 旧内容、误判成失败,于是下一层再插一次 —— 实测表现就是**文字出现两遍**(第一次真机上\n * 就是这样:粘贴插了一遍,`insertText` 又追加了一遍)。\n *\n * 三层次序:① 模拟粘贴(Lexical 的正规插入通道)② `execCommand(\"insertText\")`\n * ③ 直接改文本 + 派发 `input`。**每次降级前都重新全选**,让重试是\"替换\"而不是\"追加\"。\n */\nexport function writeComposerText(\n root: ComposerRoot,\n value: string,\n caret: HistoryCaret,\n): Promise<boolean> {\n function satisfied() {\n return composerTextMatches(composerText(root), value)\n }\n\n return settleComposer()\n .then(function () {\n if (typeof window.ClipboardEvent !== 'function' || typeof window.DataTransfer !== 'function')\n return false\n if (typeof root.dispatchEvent !== 'function') return false\n try {\n selectComposerContents(root)\n var data = new window.DataTransfer()\n data.setData('text/plain', value)\n root.dispatchEvent(\n new window.ClipboardEvent('paste', {\n clipboardData: data,\n bubbles: true,\n cancelable: true,\n }),\n )\n } catch (error) {\n return false\n }\n return settleComposer().then(satisfied)\n })\n .then(function (wrote) {\n if (wrote) return true\n try {\n if (typeof document.execCommand === 'function') {\n selectComposerContents(root)\n if (document.execCommand('insertText', false, value) === true) {\n return settleComposer().then(satisfied)\n }\n }\n } catch (error) {\n // 落到最后一层\n }\n return settleComposer().then(function () {\n if (satisfied()) return true\n try {\n selectComposerContents(root)\n root.textContent = value\n // 上一层的不变量(dispatchEvent 存在)不跨回调作用域,这里用 ! 保留原来的\"抛了就被 catch 掉\"语义。\n if (typeof window.Event === 'function')\n root.dispatchEvent!(new window.Event('input', { bubbles: true }))\n return true\n } catch (error) {\n return false\n }\n })\n })\n .then(function (wrote) {\n if (!wrote) return false\n placeCaretInComposer(root, caret)\n // 重写内容会让编辑器失焦/滚动跳,下一帧再摆一次光标(OpenCode 也是这么做的)。\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(function () {\n placeCaretInComposer(root, caret)\n })\n }\n return true\n })\n}\n\n/**\n * 把\"输入框里按 ↑/↓ 召回上一条 prompt\"接到窗口上 —— 只接线,判断与翻页逻辑都在纯函数里。\n *\n * 门禁(任一不满足就**完全不介入**,`↑`/`↓` 行为与不装插件时一模一样):\n * 默认行为没被别处吃掉、不是输入法组合中、无修饰键、目标是输入框、历史非空、\n * 选区收起、光标位置通过 `canNavigatePromptHistory`。**接管了才 preventDefault**。\n */\nexport function bindPromptHistory(store: HudStore): () => void {\n var nav: { sessionId: string | null; index: number; draft: string | null; writing: boolean } = {\n sessionId: null,\n index: -1,\n draft: null,\n writing: false,\n }\n\n function onKeyDown(event: KeyboardEvent): void {\n if (event === null || event === undefined) return\n if (event.defaultPrevented === true || event.isComposing === true) return\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n if (event.altKey === true || event.ctrlKey === true || event.metaKey === true) return\n if (typeof event.preventDefault !== 'function') return\n\n var root = composerRootOf(event.target)\n if (root === null) return\n\n var snapshot = store.getSnapshot().promptHistory\n var value =\n snapshot !== null && snapshot !== undefined && snapshot.sessionId !== null\n ? snapshot.value\n : null\n var entries =\n value !== null &&\n value !== undefined &&\n value.available === true &&\n Array.isArray(value.prompts)\n ? value.prompts\n : []\n if (entries.length === 0) return\n\n if (nav.sessionId !== snapshot.sessionId) {\n nav.sessionId = snapshot.sessionId\n nav.index = -1\n nav.draft = null\n }\n\n var caret = composerCaretOffset(root)\n if (caret === null) return\n var direction: HistoryDirection = event.key === 'ArrowUp' ? 'up' : 'down'\n // 归一化后再判\"空不空\"与\"光标在哪端\":Lexical 空输入框里的那个换行不算内容。\n var text = composerTextKey(root)\n var step = promptHistoryStep({ index: nav.index, draft: nav.draft }, direction, {\n entries: entries,\n text: text,\n caret: Math.max(0, Math.min(caret, text.length)),\n })\n if (!step.handled) return\n\n // 到这里才确定要接管:先挡住默认行为(别让 ↑ 先把光标移走),再写内容。\n event.preventDefault()\n if (typeof event.stopPropagation === 'function') event.stopPropagation()\n\n // 写入是异步的(粘贴要等落地),但翻页状态先记下:连按 ↑ 时不会两次都从同一条开始。\n // 真写不进去再回滚 —— 宁可状态与画面都对不上时退回一步,也不要悄悄漂移。\n var previous = { index: nav.index, draft: nav.draft }\n nav.index = step.state.index\n nav.draft = step.state.draft\n nav.writing = true\n\n writeComposerText(root, step.text, step.caret).then(function (wrote) {\n nav.writing = false\n if (wrote === true) {\n probe.log('prompt-history', {\n index: nav.index,\n entries: entries.length,\n direction: direction,\n })\n return\n }\n nav.index = previous.index\n nav.draft = previous.draft\n probe.log('prompt-history: 写不进输入框(三层都失败),已回滚并放弃接管', {\n entries: entries.length,\n })\n })\n }\n\n /** 用户自己动了内容(不是我们写进去的)→ 退出历史、草稿作废,免得把草稿还原到新内容上。 */\n function onInput(event: Event): void {\n if (nav.writing) return\n if (composerRootOf(event && event.target) === null) return\n nav.index = -1\n nav.draft = null\n }\n\n if (typeof window.addEventListener !== 'function') return function () {}\n window.addEventListener('keydown', onKeyDown, true)\n window.addEventListener('input', onInput, true)\n return function () {\n window.removeEventListener('keydown', onKeyDown, true)\n window.removeEventListener('input', onInput, true)\n }\n}\n","/**\n * 两份词典(`dshHud` 命名空间)。键一致、值分别是中文与英文;`t()` 取不到会回落成键本身,\n * 所以\"漏翻\"会直接显示给人看 —— 有一条用例专门盯这件事(它扫的是源码,不是产物)。\n */\n\nexport const zh = {\n 'panel.title': 'Git',\n 'panel.refresh': '刷新',\n 'panel.reset': '复位位置',\n 'panel.close': '收起面板',\n 'panel.dragHint': '拖动标题可移动面板,双击标题复位',\n 'panel.loading': '读取中…',\n 'panel.clean': '工作区干净,没有未提交的改动',\n 'panel.notRepo': '当前会话的工作区不是 Git 仓库',\n 'panel.noSession': '还没有绑定到会话的工作区',\n 'panel.summary': '改动概览',\n 'panel.changes': '改动清单',\n 'panel.truncated': '仅显示前 200 条',\n 'panel.readonly': '只读',\n 'panel.upstream': '上游',\n 'panel.updated': '更新于 ',\n 'panel.ahead': '领先 ',\n 'panel.behind': '落后 ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': '点击一行查看该文件的差异',\n 'panel.rowKeys': '在面板里按过之后再按 ↑/↓ 选行、Enter 打开',\n 'panel.commits': '最近提交',\n 'panel.tags': '标签',\n 'panel.tagsEmpty': '这个仓库还没有标签',\n 'panel.tagsCapped': '只显示最近 {n} 个',\n 'tag.click': '点一下看它指向的提交',\n 'tag.annotated': '附注标签',\n 'tag.lightweight': '轻量标签',\n 'diff.close': '关闭差异',\n 'diff.resize': '拖动右边缘调整差异宽度',\n 'diff.resizeCommit': '拖动调整提交明细栏宽度',\n 'diff.worktree': '未暂存',\n 'diff.index': '已暂存',\n 'diff.wrap': '自动换行',\n 'diff.copy': '复制差异',\n 'diff.copied': '已复制',\n 'diff.empty': '这一侧没有差异',\n 'diff.binary': '二进制文件,没有可显示的文本差异',\n // ── 图片预览(二进制改动里认得出的那几种) ──\n 'image.loading': '读取图片…',\n 'image.pair': '并排',\n 'image.flip': '切换',\n 'image.beforeWorktree': '索引',\n 'image.afterWorktree': '工作区',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': '索引',\n 'image.beforeCommit': '父提交',\n 'image.afterCommit': '这次提交',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': '尺寸 {from} → {to}',\n 'image.tooLarge': '超过 {limit} MB 预览上限({size})',\n 'image.notImage': '不是可预览的图片({size})',\n 'image.missing': '这一侧没有这个文件',\n 'image.zoomOut': '缩小',\n 'image.zoomIn': '放大',\n 'image.fitWidth': '适应宽度',\n 'image.zoomValue': '{percent}%',\n // ── 会话计时胶囊 ──\n 'timer.title': '本次会话运行时长',\n 'timer.created': '创建于 {time}',\n 'timer.work': '其中模型 {llm}、工具 {tool}',\n 'timer.turns': '{turns} 轮 / {steps} 步',\n // ── Session timer capsule ──\n // ── 轮尾的「本轮图片改动」条 ──\n 'turnImages.title': '本轮图片改动',\n 'turnImages.titleSession': '本会话图片改动',\n 'turnImages.open': '在内置预览里打开',\n 'turnImages.compare': '在 dsh-hud 里前后对比',\n 'turnImages.more': '还有 {n} 个',\n 'diff.truncated': '差异过大,仅显示前面部分',\n 'diff.untrackedHint': '未跟踪文件,与空文件对比',\n 'diff.commitHint': '这次提交相对其父提交的差异(只读)',\n 'diff.hunkToggle': '点这一行折叠 / 展开这个 hunk(滚动到它后按 Space 一样)',\n 'commit.files': '这次提交改动',\n 'commit.close': '收起提交明细',\n 'commit.empty': '这次提交没有改动文件(空提交)',\n 'commit.loading': '读取这次提交…',\n 'commit.merge': '合并提交,按第一父提交比较',\n 'commit.unknown': '找不到这次提交:可能已被 rebase / amend 换掉,刷新面板再看',\n 'commit.click': '点击查看这次提交改了什么',\n // ── 会话归因(本会话改过哪些文件) ──\n 'session.line': '本会话改过 {n} 个文件',\n 'tools.now': '现在在跑:{name}',\n 'tools.total': '本会话共 {n} 次工具调用',\n 'tools.byTool': '各工具:',\n 'tools.skills': 'skills:',\n 'tools.recent': '最近:',\n 'rows.ownNone': '本会话没有改动记录',\n 'rows.noRepo': '不在 git 仓库里',\n 'rows.error': '读取失败({code})',\n 'rows.possible': '命令旁听到 {n} 个可能改动的路径({now} 个现在有差异)',\n 'review.count': '已审 {n}/{total}',\n 'review.staleCount': '{n} 个改动已变,需要重看',\n 'review.mark': '标成「这一版我审过了」(键盘:v)',\n 'review.unmark': '取消「已审」(键盘:v)',\n 'review.stale': '改动已变,需要重看(标记的是更早的那一版)',\n 'review.clear': '清空这个工作区的「已审」标记',\n 'review.clearShort': '清空已审',\n 'session.latestTurn': '最近一轮 {n} 个',\n 'session.turn': '第 {n} 轮',\n 'session.turnWithPrompt': '第 {n} 轮:{prompt}',\n 'session.onlyLatest': '只看最近一轮',\n 'session.onlyLatestTitle': '把清单收窄到最近这一轮动过的文件(「这一轮改了什么」)',\n 'session.possible': '另有 {n} 个可能相关(命令旁听)',\n 'session.possibleDropped': '还有 {n} 个超出上限未记',\n 'session.possibleTitle': '可能相关(未确认):{reason}',\n 'session.commands': '最近跑过 {n} 条命令:',\n 'session.commandsMore': '…另有 {n} 条会写文件的命令',\n 'session.commandFailed': '(这条失败了)',\n 'session.gone': '{n} 个已不在 git 现状里(改回去了 / 被忽略 / 被覆盖)',\n 'session.dropped': '另有 {n} 个超出上限未记录',\n 'session.only': '只看本会话改的',\n 'session.onlyTitle':\n '把清单过滤成本会话动过的文件(确定的 + 命令旁听到的「可能相关」,与 git 现状求交)',\n 'session.mine': '本会话改的',\n 'session.other': '不是本会话改的(可能是你自己、编辑器或另一个会话)',\n 'session.writes': '写入 {n} 次',\n 'session.edits': '编辑 {n} 次',\n 'session.since': '自 {time} 起记录(宿主进程启动之前的不算)',\n 'session.pill': '本会话改过 {n} 个文件',\n 'session.empty': '本会话还没有改动(清掉上面的过滤器看看工作区现状)',\n 'pill.loading': '读取中…',\n 'pill.notRepo': '非 git 仓库',\n 'pill.error': '读取失败',\n 'pill.title': '点击展开 Git 面板',\n 'count.modified': '已修改',\n 'count.added': '已暂存新增',\n 'count.deleted': '已删除',\n 'count.renamed': '重命名',\n 'count.copied': '复制',\n 'count.typeChanged': '类型改变',\n 'count.untracked': '未跟踪',\n 'count.conflicted': '冲突',\n 'err.timeout': 'git 超时:仓库过大或磁盘繁忙',\n 'err.no-cwd': '工作区目录不存在',\n 'err.git-missing': '找不到 git 可执行文件',\n 'err.invalid-cwd': '无效的工作区路径',\n 'err.git-failed': 'git 执行失败',\n 'err.invalid-path': '无效的文件路径',\n 'err.stale-host': '宿主半还没有这个端点:插件刚更新,需要重启 DSH 让宿主半重新加载',\n 'err.transport': '无法连接到宿主半',\n}\n\nexport const en = {\n 'panel.title': 'Git',\n 'panel.refresh': 'Refresh',\n 'panel.reset': 'Reset position',\n 'panel.close': 'Collapse panel',\n 'panel.dragHint': 'Drag the title to move the panel; double-click it to reset',\n 'panel.loading': 'Loading…',\n 'panel.clean': 'Working tree clean — nothing uncommitted',\n 'panel.notRepo': 'The current session workspace is not a Git repository',\n 'panel.noSession': 'Not bound to a session workspace yet',\n 'panel.summary': 'Summary',\n 'panel.changes': 'Changes',\n 'panel.truncated': 'Showing the first 200 entries only',\n 'panel.readonly': 'Read-only',\n 'panel.upstream': 'upstream',\n 'panel.updated': 'Updated ',\n 'panel.ahead': 'ahead ',\n 'panel.behind': 'behind ',\n 'panel.detached': 'detached',\n 'panel.clickForDiff': 'Click a file to see its diff',\n 'panel.rowKeys': 'Once you have clicked in the panel: ↑/↓ to select a row, Enter to open it',\n 'panel.commits': 'Recent commits',\n 'panel.tags': 'Tags',\n 'panel.tagsEmpty': 'No tags in this repository yet',\n 'panel.tagsCapped': 'Showing the newest {n}',\n 'tag.click': 'Click to open the commit it points at',\n 'tag.annotated': 'Annotated tag',\n 'tag.lightweight': 'Lightweight tag',\n 'diff.close': 'Close diff',\n 'diff.resize': 'Drag the right edge to resize the diff',\n 'diff.resizeCommit': 'Drag to resize the commit pane',\n 'diff.worktree': 'Unstaged',\n 'diff.index': 'Staged',\n 'diff.wrap': 'Wrap lines',\n 'diff.copy': 'Copy diff',\n 'diff.copied': 'Copied',\n 'diff.empty': 'No changes on this side',\n 'diff.binary': 'Binary file — no text diff to show',\n // ── Image preview (binary changes we can recognise) ──\n 'image.loading': 'Reading image…',\n 'image.pair': 'Side by side',\n 'image.flip': 'Flip',\n 'image.beforeWorktree': 'index',\n 'image.afterWorktree': 'worktree',\n 'image.beforeIndex': 'HEAD',\n 'image.afterIndex': 'index',\n 'image.beforeCommit': 'parent',\n 'image.afterCommit': 'this commit',\n 'image.dims': '{w}×{h}',\n 'image.dimsChanged': 'size {from} → {to}',\n 'image.tooLarge': 'over the {limit} MB preview limit ({size})',\n 'image.notImage': 'not a previewable image ({size})',\n 'image.missing': 'not present on this side',\n 'image.zoomOut': 'Zoom out',\n 'image.zoomIn': 'Zoom in',\n 'image.fitWidth': 'Fit width',\n 'image.zoomValue': '{percent}%',\n // ── Session timer capsule ──\n 'timer.title': \"This session's running time\",\n 'timer.created': 'started {time}',\n 'timer.work': 'model {llm}, tools {tool}',\n 'timer.turns': '{turns} turns / {steps} steps',\n // ── Turn-tail \"images this turn\" strip ──\n 'turnImages.title': 'Images this turn',\n 'turnImages.titleSession': 'Images this session',\n 'turnImages.open': 'Open in the built-in preview',\n 'turnImages.compare': 'Compare before/after in dsh-hud',\n 'turnImages.more': '{n} more',\n 'diff.truncated': 'Diff is large; showing the beginning only',\n 'diff.untrackedHint': 'Untracked file, diffed against an empty file',\n 'diff.commitHint': 'What this commit changed against its parent (read-only)',\n 'diff.hunkToggle':\n 'Click this row to collapse or expand the hunk (same as Space once it is the current one)',\n 'commit.files': 'Files in this commit',\n 'commit.close': 'Close the commit detail',\n 'commit.empty': 'This commit changes no files (empty commit)',\n 'commit.loading': 'Reading this commit…',\n 'commit.merge': 'Merge commit, compared against its first parent',\n 'commit.unknown':\n 'Commit not found: it may have been rebased or amended away — refresh the panel',\n 'commit.click': 'Click to see what this commit changed',\n 'session.line': '{n} file(s) changed by this session',\n 'tools.now': 'Running now: {name}',\n 'tools.total': '{n} tool call(s) this session',\n 'tools.byTool': 'By tool:',\n 'tools.skills': 'skills:',\n 'tools.recent': 'Recent:',\n 'rows.ownNone': 'this session has no recorded changes',\n 'rows.noRepo': 'not in a git repository',\n 'rows.error': 'read failed ({code})',\n 'rows.possible': '{n} path(s) commands may have touched ({now} still differ)',\n 'review.count': '{n}/{total} reviewed',\n 'review.staleCount': '{n} changed since reviewed',\n 'review.mark': 'Mark this revision as reviewed (key: v)',\n 'review.unmark': 'Unmark reviewed (key: v)',\n 'review.stale': 'Changed since you reviewed it (your mark was an earlier revision)',\n 'review.clear': 'Clear the reviewed marks for this workspace',\n 'review.clearShort': 'Clear reviewed',\n 'session.latestTurn': '{n} in the latest turn',\n 'session.turn': 'turn {n}',\n 'session.turnWithPrompt': 'turn {n}: {prompt}',\n 'session.onlyLatest': 'Latest turn only',\n 'session.onlyLatestTitle': 'Narrow the list to the files the latest turn changed',\n 'session.possible': '{n} more possibly related (seen in commands)',\n 'session.possibleDropped': '{n} more over the cap',\n 'session.possibleTitle': 'Possibly related (unconfirmed): {reason}',\n 'session.commands': 'Last {n} command(s) run:',\n 'session.commandsMore': '…plus {n} more that write files',\n 'session.commandFailed': '(this one failed)',\n 'session.gone': '{n} no longer in the git status (reverted, ignored, or overwritten)',\n 'session.dropped': '{n} more not recorded (over the cap)',\n 'session.only': \"Only this session's\",\n 'session.onlyTitle':\n 'Filter the list to files this session touched (confirmed plus command-inferred, intersected with git)',\n 'session.mine': 'Changed by this session',\n 'session.other': 'Not changed by this session (you, your editor, or another session)',\n 'session.writes': '{n} write(s)',\n 'session.edits': '{n} edit(s)',\n 'session.since': 'Recorded since {time} (anything before the host process started is not)',\n 'session.pill': '{n} file(s) changed by this session',\n 'session.empty':\n 'This session has not changed anything yet (clear the filter above to see the workspace)',\n 'pill.loading': 'Loading…',\n 'pill.notRepo': 'not a git repo',\n 'pill.error': 'read failed',\n 'pill.title': 'Click to open the Git panel',\n 'count.modified': 'modified',\n 'count.added': 'added',\n 'count.deleted': 'deleted',\n 'count.renamed': 'renamed',\n 'count.copied': 'copied',\n 'count.typeChanged': 'type changed',\n 'count.untracked': 'untracked',\n 'count.conflicted': 'conflicted',\n 'err.timeout': 'git timed out: the repository is large or the disk is busy',\n 'err.no-cwd': 'The workspace directory does not exist',\n 'err.git-missing': 'git executable not found',\n 'err.invalid-cwd': 'Invalid workspace path',\n 'err.git-failed': 'git failed',\n 'err.invalid-path': 'Invalid file path',\n 'err.stale-host':\n 'The host half does not have this endpoint yet: the plugin was just updated, restart DSH so the host half reloads',\n 'err.transport': 'Cannot reach the host half',\n}\n","/**\n * 「会话归因与审阅」这一片 store:本会话改过哪些文件、哪些标记为已审阅。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { ReviewedMap, SessionChange, SessionChangeCandidate } from '../shared.js'\nimport type {\n AttributionHelpers,\n AttributionSlice,\n GetState,\n SessionChangesPayload,\n StoreContext,\n StoreSet,\n} from './types.js'\n\nimport { CHANNEL, probe, reviewKeyOf, writeReviewed } from '../shared.js'\n\nexport function createAttributionSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: AttributionHelpers,\n): AttributionSlice {\n /** 会话归因那次请求(在途合并)。 */\n var sessionInflight: Promise<unknown> | null = null\n\n /** 胶囊调用:把当前会话的工作区写进 store,变了才重新取数。 */\n function bindSession(sessionId: string | null | undefined, cwd: string | null | undefined): void {\n if (getState().sessionId === sessionId && getState().cwd === (cwd || null)) return\n set({\n sessionId: sessionId === undefined ? null : sessionId,\n cwd: cwd || null,\n status: null,\n errorCode: null,\n commits: null,\n commitsOid: null,\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n // 差异与提交明细都绑定在某个工作区上:换了工作区就把它们收起,\n // 否则会显示另一个仓库的文件或提交。键盘光标同理,它指向的是清单里的行。\n diff: null,\n commit: null,\n cursor: null,\n phase: cwd ? 'loading' : 'idle',\n })\n // 工具活动同理:换会话就清空,别显示上一个会话正在跑的工具。\n set({\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n })\n // 归因是\"这个会话\"的事实:换会话就先清空,别把上一个会话的标记留在新会话的清单上。\n set({\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n })\n // 回合信息同理:换会话就按新会话重新接(旧的先解掉)。\n helpers.bindTurns(sessionId)\n helpers.refresh()\n }\n /**\n * 取\"本会话改过哪些文件\"。\n *\n * 与 `status` 同一条命:状态刷新时顺带取一次(都是本进程内的读,很便宜)。失败就保持\n * `files: null`(界面不打标记、也不过这个滤),不会挡住 git 那半边。\n */\n function loadSessionChanges(): Promise<unknown> {\n var cwd = getState().cwd\n var sessionId = getState().sessionId\n if (!cwd || !sessionId) return Promise.resolve()\n if (sessionInflight) return sessionInflight\n if (getState().sessionChanges.phase === 'idle')\n set({ sessionChanges: Object.assign({}, getState().sessionChanges, { phase: 'loading' }) })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionChangesPayload>(CHANNEL, 'session-changes', {\n args: { cwd: cwd, sessionId: sessionId },\n })\n })\n .then(function (result) {\n sessionInflight = null\n if (getState().cwd !== cwd || getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value && result.value.cwdMismatch === true) {\n probe.log('session-changes', { cwdMismatch: true })\n }\n if (result && result.ok === true && result.value && result.value.cwdMismatch !== true) {\n var files: Record<string, SessionChange> = {}\n var list = Array.isArray(result.value.files) ? result.value.files : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.file === 'string' && item.file !== '') files[item.file] = item\n }\n var possible: Record<string, SessionChangeCandidate> = {}\n var possibleList = Array.isArray(result.value.possible) ? result.value.possible : []\n for (var pIndex = 0; pIndex < possibleList.length; pIndex += 1) {\n var possibleItem = possibleList[pIndex]\n if (possibleItem && typeof possibleItem.file === 'string' && possibleItem.file !== '') {\n possible[possibleItem.file] = possibleItem\n }\n }\n var since = typeof result.value.since === 'number' ? result.value.since : null\n var dropped = typeof result.value.dropped === 'number' ? result.value.dropped : 0\n probe.log('session-changes', {\n files: Object.keys(files).length,\n possible: Object.keys(possible).length,\n commands: Array.isArray(result.value.commands) ? result.value.commands.length : 0,\n dropped: dropped,\n since: since === null ? null : new Date(since).toISOString(),\n })\n set({\n sessionChanges: {\n phase: 'ready',\n files: files,\n possible: possible,\n commands: Array.isArray(result.value.commands) ? result.value.commands : [],\n since: since,\n dropped: dropped,\n possibleDropped:\n typeof result.value.possibleDropped === 'number' ? result.value.possibleDropped : 0,\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-changes-failed', { code: code || 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: code || 'transport',\n },\n })\n })\n .catch(function () {\n sessionInflight = null\n if (getState().cwd !== cwd) return\n probe.log('session-changes-failed', { code: 'transport' })\n set({\n sessionChanges: {\n phase: 'error',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: 'transport',\n },\n })\n })\n\n sessionInflight = task\n return task\n }\n /**\n * 标记 / 取消\"这个文件的这一版我审过了\"。\n *\n * 签名用的是**当前**这一版(宿主 stat 出来的 size+mtimeMs):下次改动一落盘签名就变,\n * 标记自动降级成\"改动已变,需要重看\" —— 而不是继续冒充\"已审\"。\n */\n function toggleReviewed(file: string): void {\n if (typeof file !== 'string' || file.length === 0) return\n var signature = getState().review ? getState().review[file] : undefined\n if (typeof signature !== 'string') {\n probe.log('review-no-signature', { file: file })\n return\n }\n var next: ReviewedMap = {}\n for (var key in getState().reviewed)\n if (Object.prototype.hasOwnProperty.call(getState().reviewed, key))\n next[key] = getState().reviewed[key]\n var markKey = helpers.reviewedKeyOf(file)\n if (next[markKey] !== undefined && next[markKey].signature === signature) delete next[markKey]\n else next[markKey] = { signature: signature, at: Date.now() }\n set({ reviewed: writeReviewed(next) })\n }\n\n /** 清掉**当前工作区**的全部\"已审\"标记(别的仓库不动)。 */\n function clearReviewed(): void {\n var prefix = reviewKeyOf(getState().cwd, '')\n var next: ReviewedMap = {}\n var removed = 0\n for (var key in getState().reviewed) {\n if (!Object.prototype.hasOwnProperty.call(getState().reviewed, key)) continue\n if (key.indexOf(prefix) === 0) {\n removed += 1\n continue\n }\n next[key] = getState().reviewed[key]\n }\n if (removed === 0) return\n set({ reviewed: writeReviewed(next) })\n }\n\n return {\n bindSession: bindSession,\n clearReviewed: clearReviewed,\n loadSessionChanges: loadSessionChanges,\n toggleReviewed: toggleReviewed,\n }\n}\n","/**\n * 「提交与标签」这一片 store:打开某次提交、看它里面某个文件的差异、标签列表。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudCommitFile } from '../shared.js'\nimport type {\n CommitDiffPayload,\n CommitPayload,\n CommitRef,\n CommitsHelpers,\n CommitsSlice,\n GetState,\n LogPayload,\n StoreContext,\n StoreSet,\n TagsPayload,\n} from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, probe, readUi, readZoom, sameCommit } from '../shared.js'\n\nexport function createCommitsSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: CommitsHelpers,\n): CommitsSlice {\n /** 提交那几次请求(在途合并)。 */\n var commitInflight: Promise<unknown> | null = null\n /** 提交里某个文件的差异:与工作区差异**分开**合并(拆分前两者共用一个变量,\n * 拆分后留在差异片里,这里再引用就会 ReferenceError)。 */\n var commitDiffInflight: Promise<unknown> | null = null\n\n /**\n * 打开「某次提交」的视图:先取这次提交的改动清单,再自动打开第一个文件的差异,\n * 这样一次点击就能看到\"这次提交改了什么\"。\n *\n * 提交是历史事实,取回来就不再变,因此不参与轮询;只有 HEAD 变了它也不受影响\n * (面板显示的是你点的那一次提交)。\n */\n function openCommit(commit: CommitRef): Promise<unknown> {\n if (!getState().cwd || !commit || !commit.sha) return Promise.resolve()\n var cwd = getState().cwd\n var sha = commit.sha\n\n set({\n commit: {\n phase: 'loading',\n sha: sha,\n shortSha:\n typeof commit.sha === 'string' && commit.sha.length > 7 ? commit.sha.slice(0, 7) : sha,\n subject: commit.subject || '',\n author: commit.author || '',\n date: commit.date || '',\n committedAt: typeof commit.committedAt === 'number' ? commit.committedAt : null,\n refs: commit.refs || [],\n merge: false,\n files: [],\n truncated: false,\n errorCode: null,\n },\n diff: null,\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitPayload>(CHANNEL, 'commit', {\n args: { cwd: cwd, sha: sha },\n })\n })\n .then(function (result) {\n commitInflight = null\n var current = getState().commit\n // 期间用户可能已经返回清单、点了别的提交,或换了工作区。\n if (!current || current.sha !== sha || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var files = Array.isArray(value.files) ? value.files : []\n var meta = value.commit\n if (meta !== null && meta !== undefined) {\n set({\n commit: {\n phase: 'ready',\n sha: meta.sha || sha,\n shortSha: meta.shortSha || sha,\n subject: meta.subject || '',\n author: meta.author || '',\n date: meta.date || '',\n committedAt: typeof meta.committedAt === 'number' ? meta.committedAt : null,\n refs: meta.refs || [],\n merge: meta.merge === true,\n files: files,\n truncated: value.truncated === true,\n errorCode: null,\n },\n })\n // 空提交(或者清单被截断到没内容)就停在左栏,不去开一个空差异。\n // 有内容就顺手打开第一个文件——一次点击就看到\"改了什么\";这里把\n // diff 的 promise 交回去,于是 await openCommit 就是等整次点击完成。\n if (files.length > 0) return openCommitDiff(files[0])\n return undefined\n }\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n commit: Object.assign({}, current, {\n phase: 'error',\n files: [],\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitInflight = null\n var current = getState().commit\n if (!current || current.sha !== sha) return\n set({ commit: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitInflight = task\n return task\n }\n /**\n * 打开这次提交里某个文件的差异。与工作区差异共用右栏,但只有一个\"侧\"\n * (提交相对其父提交),因此工具栏不显示 未暂存/已暂存 两个页签。\n */\n function openCommitDiff(file: HudCommitFile): Promise<unknown> {\n var current = getState().commit\n if (!getState().cwd || !current || !file || !file.file) return Promise.resolve()\n var cwd = getState().cwd\n var sha = current.sha\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n\n set({\n cursor: file.file,\n diff: {\n source: 'commit',\n phase: 'loading',\n sha: sha,\n shortSha: current.shortSha,\n subject: current.subject,\n path: file.path || file.file,\n file: file.file,\n origPath: file.origFile || null,\n data: null,\n side: 'commit',\n wrap: wrap,\n zoom:\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi()),\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<CommitDiffPayload>(CHANNEL, 'commit-diff', {\n args: { cwd: cwd, sha: sha, path: file.file, origPath: file.origFile || null },\n })\n })\n .then(function (result) {\n commitDiffInflight = null\n var now = getState().diff\n // 期间可能已经返回清单、点了别的文件,或换了提交。\n if (\n !now ||\n now.source !== 'commit' ||\n now.sha !== sha ||\n now.file !== file.file ||\n getState().cwd !== cwd\n )\n return\n if (result && result.ok === true && result.value) {\n set({\n diff: Object.assign({}, now, { phase: 'ready', data: result.value, errorCode: null }),\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: Object.assign({}, now, {\n phase: 'error',\n data: null,\n errorCode: code || 'git-failed',\n }),\n })\n })\n .catch(function () {\n commitDiffInflight = null\n var now = getState().diff\n if (!now || now.source !== 'commit' || now.file !== file.file) return\n set({ diff: Object.assign({}, now, { phase: 'error', errorCode: 'transport' }) })\n })\n\n commitDiffInflight = task\n return task\n }\n /**\n * 点最近提交里的一行:打开这次提交的明细;点已经打开的那条则收起。\n * (明细占的是右栏,左栏主面板只是高亮那一行。)\n */\n function toggleCommit(commit: CommitRef): Promise<unknown> | void {\n if (!commit || !commit.sha) return Promise.resolve()\n var openCommitState = getState().commit\n if (openCommitState !== null && sameCommit(commit.sha, openCommitState.sha))\n return closeCommit()\n return openCommit(commit)\n }\n /** 收起提交明细:它带来的差异一起收起来(清单换了,光标也要清)。 */\n function closeCommit(): void {\n if (getState().commit === null) return\n var openDiff = getState().diff\n var keep = openDiff && openDiff.source === 'commit' ? null : openDiff\n set({ commit: null, diff: keep, cursor: null })\n }\n /**\n * 取最近提交。失败只让这一段不显示,不影响面板其余部分——\n * 提交是背景信息,不该抢走「工作区状态」的注意力。\n */\n function loadCommits(cwd: string, oid: string | null): Promise<unknown> {\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<LogPayload>(CHANNEL, 'log', {\n args: { cwd: cwd, count: 10 },\n })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var commits =\n result && result.ok === true && result.value && Array.isArray(result.value.commits)\n ? result.value.commits\n : []\n set({ commits: commits, commitsOid: oid })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n set({ commits: [], commitsOid: oid })\n })\n }\n /** 取标签列表(面板开着时才取)。失败只让这一段不显示。 */\n function loadTags(cwd: string): Promise<unknown> {\n // 10s 新鲜度窗口:面板每 8s 刷新一次,这里避免每次都对 git 跑一遍 for-each-ref;\n // 又能让\"刚打的新 tag\"在十来秒内出现(HEAD 没动也得看到)。\n if (getState().tags !== null && Date.now() - getState().tagsAt < 10000) return Promise.resolve()\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<TagsPayload>(CHANNEL, 'tags', { args: { cwd: cwd } })\n })\n .then(function (result) {\n if (getState().cwd !== cwd) return\n var value = result && result.ok === true ? result.value : null\n var tags = value && Array.isArray(value.tags) ? value.tags : []\n probe.log('tags', {\n count: tags.length,\n truncated: value ? value.truncated === true : false,\n })\n set({\n tags: tags,\n tagsAt: Date.now(),\n tagsTruncated: value ? value.truncated === true : false,\n })\n })\n .catch(function () {\n if (getState().cwd !== cwd) return\n probe.log('tags-failed', { code: 'transport' })\n set({ tags: [], tagsAt: Date.now(), tagsTruncated: false })\n })\n }\n\n return {\n closeCommit: closeCommit,\n loadCommits: loadCommits,\n loadTags: loadTags,\n openCommit: openCommit,\n openCommitDiff: openCommitDiff,\n toggleCommit: toggleCommit,\n }\n}\n","/**\n * 「差异视图」这一片 store:打开哪份差异、页签、换行,清单里上下走与 hunk 折叠。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;跨片依赖走 `helpers`。\n */\n\nimport type { HudChange, WorktreeDiffPayload } from '../shared.js'\nimport type { DiffHelpers, DiffSlice, GetState, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, ZOOM_STEPS, readUi, readZoom } from '../shared.js'\n\nexport function createDiffSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n helpers: DiffHelpers,\n): DiffSlice {\n /** 差异那次请求(在途合并)。 */\n var diffInflight: Promise<unknown> | null = null\n\n /**\n * 打开一个改动文件的差异视图:一次取回**两侧**的 rows,\n * 默认落在有内容的那一侧(工作区优先),页签切换因此不需要再发请求。\n */\n function openDiff(change: HudChange): Promise<unknown> {\n if (!getState().cwd || !change || !change.file) return Promise.resolve()\n var cwd = getState().cwd\n var currentDiff = getState().diff\n var wrap = currentDiff ? currentDiff.wrap === true : readUi().wrap === true\n var zoom =\n currentDiff &&\n typeof currentDiff.zoom === 'number' &&\n ZOOM_STEPS.indexOf(currentDiff.zoom) >= 0\n ? currentDiff.zoom\n : readZoom(readUi())\n set({\n // 工作区差异与「某次提交的明细」是互斥的两种右栏内容,换过去就收起明细。\n commit: null,\n cursor: change.file,\n diff: {\n source: 'worktree',\n phase: 'loading',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: wrap,\n zoom: zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<WorktreeDiffPayload>(CHANNEL, 'diff', {\n args: { cwd: cwd, path: change.file, origPath: change.origFile || null },\n })\n })\n .then(function (result) {\n diffInflight = null\n var current = getState().diff\n // 期间用户可能已经返回清单或换了文件。\n if (!current || current.file !== change.file || getState().cwd !== cwd) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var worktreeRows = value.worktree && value.worktree.rows ? value.worktree.rows.length : 0\n var indexRows = value.index && value.index.rows ? value.index.rows.length : 0\n set({\n diff: {\n source: 'worktree',\n phase: 'ready',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: value,\n side: worktreeRows > 0 || indexRows === 0 ? 'worktree' : 'index',\n wrap: current.wrap,\n zoom: current.zoom,\n hunk: 0,\n collapsed: [],\n errorCode: null,\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({\n diff: {\n source: 'worktree',\n phase: 'error',\n path: change.path,\n file: change.file,\n origPath: change.origFile || null,\n data: null,\n side: 'worktree',\n wrap: current.wrap,\n errorCode: code || 'git-failed',\n },\n })\n })\n .catch(function () {\n diffInflight = null\n var current = getState().diff\n if (!current || current.file !== change.file) return\n set({ diff: Object.assign({}, current, { phase: 'error', errorCode: 'transport' }) })\n })\n\n // 到这里 task 一定已被赋值;先挂进在途合并变量再返回(返回类型才不是可空的)。\n diffInflight = task\n return task\n }\n function closeDiff(): void {\n if (getState().diff === null) return\n set({ diff: null })\n }\n function setDiffSide(side: string): void {\n var diff = getState().diff\n if (!diff || diff.source === 'commit' || (side !== 'worktree' && side !== 'index')) return\n // 换侧等于换了一份 rows:hunk 序号与折叠状态都不再对应得上。\n set({ diff: Object.assign({}, diff, { side: side, hunk: 0, collapsed: [] }) })\n }\n /**\n * 在当前清单里上下移动一格(↑/↓ 只移动;`[`/`]` 移动并直接打开)。\n *\n * 还没选过时:向下从第一条开始,向上从最后一条开始 —— 这样 ↑ 和 ↓ 都能\n * \"先落在一个合理的位置上\",不需要用户先点一行。\n *\n * @param delta - 1 向下、-1 向上。\n * @param open - 移动后是否立刻打开。\n * @returns 选中的那条,清单为空时返回 null。\n */\n function stepFile(delta: number, open: boolean): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n\n var index = -1\n for (var cursor = 0; cursor < list.length; cursor += 1) {\n if (list[cursor].file === getState().cursor) {\n index = cursor\n break\n }\n }\n\n var next =\n index === -1\n ? delta > 0\n ? 0\n : list.length - 1\n : Math.min(Math.max(0, index + delta), list.length - 1)\n var entry = list[next]\n if (!entry || !entry.file) return null\n\n set({ cursor: entry.file })\n if (open) helpers.openListEntry(entry)\n return entry\n }\n /** Enter:打开键盘选中的那条;还没选过就打开第一条。 */\n function openCursor(): HudChange | null {\n var list = helpers.visibleFiles()\n if (list.length === 0) return null\n for (var index = 0; index < list.length; index += 1) {\n if (list[index].file === getState().cursor) {\n helpers.openListEntry(list[index])\n return list[index]\n }\n }\n return stepFile(1, true)\n }\n /**\n * n / p:跳到下一个 / 上一个 hunk。跳到折叠着的 hunk 会顺手展开它 ——\n * 不然按键看起来什么都没发生。\n */\n function stepHunk(delta: number): void {\n var diff = getState().diff\n if (!diff) return\n var total = helpers.hunkCount()\n if (total === 0) return\n var current = typeof diff.hunk === 'number' ? diff.hunk : 0\n var next = Math.min(Math.max(0, current + delta), total - 1)\n var collapsed = (diff.collapsed || []).filter(function (index) {\n return index !== next\n })\n set({ diff: Object.assign({}, diff, { hunk: next, collapsed: collapsed }) })\n }\n /** 折叠 / 展开一个 hunk(点 hunk 头,或 Space 作用于当前 hunk)。 */\n function toggleHunk(index: number | undefined): void {\n var diff = getState().diff\n if (!diff || typeof index !== 'number' || index < 0) return\n var collapsed = diff.collapsed || []\n var next =\n collapsed.indexOf(index) >= 0\n ? collapsed.filter(function (item) {\n return item !== index\n })\n : collapsed.concat([index])\n set({ diff: Object.assign({}, diff, { hunk: index, collapsed: next }) })\n }\n function toggleDiffWrap(): void {\n var diff = getState().diff\n if (!diff) return\n set({ diff: Object.assign({}, diff, { wrap: diff.wrap !== true }) })\n helpers.persist()\n }\n\n return {\n closeDiff: closeDiff,\n openCursor: openCursor,\n openDiff: openDiff,\n setDiffSide: setDiffSide,\n stepFile: stepFile,\n stepHunk: stepHunk,\n toggleDiffWrap: toggleDiffWrap,\n toggleHunk: toggleHunk,\n }\n}\n","import { CHANNEL, imagePairKey } from '../shared.js'\nimport type { ImageRequest, ImagePayload } from '../shared.js'\nimport type { ImageSlice, ImageView, StoreContext } from './types.js'\n\n/**\n * 「图片预览」这一片 store:二进制改动两侧的字节与尺寸,带一个同步缓存。\n */\n\nexport function createImageSlice(ctx: StoreContext): ImageSlice {\n var imagePairs = new Map<string, ImageView>()\n /** 这一片自己的缓存上限(照宿主半的 `CACHE_MAX_ENTRIES` 同一个量级)。 */\n var IMAGE_CACHE_MAX = 8\n\n /**\n * 同步看一眼缓存:命中就返回条目,否则 null。\n *\n * 有了它,`ImageDiff` 的**第一次渲染**就能把已经取过的图画出来 ——\n * 关掉再打开同一张图不该先闪一下\"读取图片…\",也不该再发一次请求。\n */\n function cachedImagePair(request: ImageRequest): ImageView | null {\n if (request === null || request === undefined) return null\n var entry = imagePairs.get(imagePairKey(request))\n return entry === undefined ? null : entry\n }\n\n /** 供 `ImageDiff` 用:取一个二进制改动两侧的字节与尺寸(同一个请求只发一次)。 */\n function loadImagePair(request: ImageRequest): Promise<ImageView> {\n var key = imagePairKey(request)\n var cached = imagePairs.get(key)\n if (cached !== undefined) return Promise.resolve(cached)\n\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<ImagePayload>(CHANNEL, 'image', { args: request })\n })\n .then(function (result) {\n var entry: ImageView =\n result && result.ok === true && result.value\n ? { phase: 'ready', value: result.value }\n : {\n phase: 'error',\n code:\n result && result.error && result.error.details ? result.error.details.code : null,\n }\n if (imagePairs.size >= IMAGE_CACHE_MAX) imagePairs.clear()\n imagePairs.set(key, entry)\n return entry\n })\n .catch(function () {\n return { phase: 'error', code: 'transport' }\n })\n }\n\n return { cachedImagePair: cachedImagePair, loadImagePair: loadImagePair }\n}\n","/**\n * 「会话信息」这一片 store:会话运行时长(3 秒 / 60 秒两档 TTL)与 prompt 历史。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`(由 `createStore` 注入)。\n */\n\nimport type { HudPromptHistory, HudSessionTime } from '../shared.js'\nimport type { GetState, SessionInfoSlice, StoreContext, StoreSet } from './types.js'\n\nimport { CHANNEL, PROMPT_HISTORY_MAX } from '../shared.js'\n\nexport function createSessionInfoSlice(\n ctx: StoreContext,\n getState: GetState,\n set: StoreSet,\n): SessionInfoSlice {\n /**\n * 会话运行时长的缓存:每分钟最多问一次(统计数字不需要更密),同会话复用。\n *\n * 与图片那种\"重\"缓存分开存:这个条目只有几十字节,TTL 到期直接覆盖。\n */\n var sessionTimes = new Map<string, { at: number; value: HudSessionTime }>()\n /** 拿到数的缓存时长。 */\n var SESSION_TIME_TTL_MS = 60000\n /**\n * **没拿到**时的重问间隔。\n *\n * 会话是异步恢复到宿主 `sessions` 存储里的:重启后头几百毫秒问 `session-time` 得到的是\n * \"这个会话还不存在\"(实测启动 0.6 秒时 live=0,30 秒后才 live=1)。\n * 把这种答案按 60 秒缓存,就会让计时胶囊在你重启后一两分钟才出现 —— 所以负面结果只记 3 秒。\n */\n var SESSION_TIME_RETRY_MS = 3000\n /** 取一个会话的运行时长(含内置 `sessionStats` 的累计数字);拿不到回 `available: false`。 */\n function loadSessionTime(sessionId: string): Promise<HudSessionTime> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false })\n var cached = sessionTimes.get(sessionId)\n if (cached !== undefined) {\n var ttl =\n cached.value && cached.value.available === true\n ? SESSION_TIME_TTL_MS\n : SESSION_TIME_RETRY_MS\n if (Date.now() - cached.at < ttl) return Promise.resolve(cached.value)\n }\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudSessionTime>(CHANNEL, 'session-time', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n var value: HudSessionTime =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId }\n if (sessionTimes.size >= 16) sessionTimes.clear()\n sessionTimes.set(sessionId, { at: Date.now(), value: value })\n var previous: HudSessionTime | null =\n getState().sessionTime && getState().sessionTime.sessionId === sessionId\n ? getState().sessionTime.value\n : null\n // 深度比较没必要:这两个字段(会话创建时刻 + 累计墙钟)就是整份值里会变的部分。\n var same =\n previous !== null &&\n previous.available === value.available &&\n previous.createdAt === value.createdAt &&\n previous.llmMs === value.llmMs &&\n previous.toolMs === value.toolMs &&\n previous.turns === value.turns &&\n previous.steps === value.steps\n if (!same) set({ sessionTime: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId }\n })\n }\n /**\n * prompt 历史:宿主从内置 `turnOutline` 投影里取,比客户端自己记一份可靠得多\n * (分页、压实、重启、换设备都不影响)。prompt 只在\"一整轮发出去\"之后才会变,\n * 所以 5 秒 TTL 足够,而且顺手把值写进快照 —— 按键那侧只读快照,不必再 await。\n */\n var promptHistories = new Map<string, { at: number; value: HudPromptHistory }>()\n var PROMPT_HISTORY_TTL_MS = 5000\n\n function loadPromptHistory(sessionId: string): Promise<HudPromptHistory> {\n if (typeof sessionId !== 'string' || sessionId === '')\n return Promise.resolve({ available: false, prompts: [] })\n var cached = promptHistories.get(sessionId)\n if (cached !== undefined && Date.now() - cached.at < PROMPT_HISTORY_TTL_MS)\n return Promise.resolve(cached.value)\n return Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<HudPromptHistory>(CHANNEL, 'prompt-history', {\n args: { sessionId: sessionId, limit: PROMPT_HISTORY_MAX },\n })\n })\n .then(function (result) {\n var value: HudPromptHistory =\n result && result.ok === true && result.value\n ? result.value\n : { available: false, sessionId: sessionId, prompts: [] }\n if (promptHistories.size >= 16) promptHistories.clear()\n promptHistories.set(sessionId, { at: Date.now(), value: value })\n var previous: HudPromptHistory | null =\n getState().promptHistory && getState().promptHistory.sessionId === sessionId\n ? getState().promptHistory.value\n : null\n var same =\n previous !== null &&\n previous.available === value.available &&\n (previous.prompts || []).length === (value.prompts || []).length &&\n (previous.prompts || []).every(function (item, index) {\n return item === value.prompts[index]\n })\n if (!same) set({ promptHistory: { sessionId: sessionId, value: value } })\n return value\n })\n .catch(function () {\n return { available: false, sessionId: sessionId, prompts: [] }\n })\n }\n\n return { loadPromptHistory: loadPromptHistory, loadSessionTime: loadSessionTime }\n}\n","/**\n * 「悬停提示」这一片 store:把气泡的文案与锚点放进快照,由 overlay 里那枚座位去画。\n */\n\nimport type { GetState, StoreSet, TipAnchor, TipHandlers, TipSlice } from './types.js'\n\n/** 事件里的锚点矩形:真实 DOM 给的是 DOMRect(结构上就是这三个数)。 */\ninterface AnchorTarget {\n getBoundingClientRect?: () => TipAnchor\n}\n\nexport function createTipSlice(getState: GetState, set: StoreSet): TipSlice {\n /** 显示某枚胶囊的悬停提示(`rect` 由调用方从 `getBoundingClientRect()` 给)。 */\n function showTip(key: string, text: string, rect: TipAnchor | null): void {\n if (typeof text !== 'string' || text.length === 0) return\n var at: TipAnchor =\n rect && typeof rect === 'object'\n ? { left: rect.left, top: rect.top, right: rect.right }\n : { left: 0, top: 0, right: 0 }\n set({ tip: { key: key, text: text, rect: at } })\n }\n\n /** 收起悬停提示。带 `key` 时只在\"还是这一枚\"的情况下收 —— 免得旧的 leave 把新的提示关掉。 */\n function hideTip(key?: string): void {\n var current = getState().tip\n if (current === null || current === undefined) return\n if (typeof key === 'string' && current.key !== key) return\n set({ tip: null })\n }\n\n /**\n * 一枚胶囊的悬停接线:算出锚点矩形,把文案交给 store。\n *\n * 为什么走 store 而不是就地渲染气泡:气泡必须**逃出行内盒**(不被 dock 那行的裁切与\n * 堆叠上下文限制),所以它由 `shell.overlay` 里一枚单独的座位画,位置用 `position:fixed`。\n */\n function tipHandlers(key: string, textOf: () => string): TipHandlers {\n return {\n onPointerEnter: function (event) {\n // 事件目标是 DOM 边界:只按 `getBoundingClientRect` 这一项能力收窄。\n var target = event && event.currentTarget ? (event.currentTarget as AnchorTarget) : null\n var rect =\n target && typeof target.getBoundingClientRect === 'function'\n ? target.getBoundingClientRect()\n : null\n showTip(key, textOf(), rect)\n },\n onPointerLeave: function () {\n hideTip(key)\n },\n onPointerDown: function () {\n hideTip(key)\n },\n }\n }\n\n return { hideTip: hideTip, showTip: showTip, tipHandlers: tipHandlers }\n}\n","/**\n * 「工具活动 / 侧栏摘要 / 回合信息」这一片 store:三样都跟会话走、与工作区无关,各自一条轮询节拍;回合信息还带一个 sessions 动态注入。\n *\n * store 拆分后的一片:读快照走 `getState()`,写状态走 `set()`;\n * 跨片与 store 内部依赖走 `helpers`(由 `createStore` 组装时注入)。\n */\n\nimport type { HudSessionOverviewRow, HudToolActive, TurnsState } from '../shared.js'\nimport type {\n GetState,\n SessionStatusesPayload,\n SessionToolsPayload,\n StoreContext,\n SessionsService,\n StoreSet,\n ToolsSlice,\n TurnFace,\n TurnSession,\n} from './types.js'\n\nimport { CHANNEL, probe } from '../shared.js'\n\n/** 回合投影的订阅记账:`{ sessionId, dispose }`;`null` 表示没接。 */\ninterface TurnsBinding {\n sessionId: string | null\n dispose: (() => void) | null\n}\n\nexport function createToolsSlice(ctx: StoreContext, getState: GetState, set: StoreSet): ToolsSlice {\n /** 工具活动那次请求(在途合并)。 */\n var toolsInflight: Promise<unknown> | null = null\n /** 侧栏摘要那次请求(在途合并)。 */\n var rowsInflight: Promise<unknown> | null = null\n /** 回合投影的订阅:`{ sessionId, dispose }`;`null` 表示没接。 */\n var turnsBound: TurnsBinding | null = null\n /** `sessions` 客户端服务(动态注入;没到齐就是 null,回合信息不显示)。 */\n var turnSessions: SessionsService | null = null\n\n // 回合信息要的 `sessions` 客户端服务:**动态注入**等它到齐(软依赖)——\n // 缺它时 git、归因、胶囊一切照旧,只是不显示\"第几轮\"。到齐后把当前会话接上。\n if (typeof ctx.inject === 'function') {\n ctx.effect(function () {\n return ctx.inject(['sessions'], function (scope) {\n turnSessions = scope.sessions\n probe.log('turn-services', { sessions: true })\n bindTurns(getState().sessionId)\n return function () {\n turnSessions = null\n unbindTurns()\n }\n })\n }, 'dsh-hud: turn services')\n }\n\n /**\n * 接上 `turnOutline` 投影(\"第几轮改的\")。\n *\n * 软依赖:`sessions` 服务、已物化的会话、这个投影三者缺一个就安静地不接 —— 归因、\n * git、胶囊一切照旧,只是不显示回合信息。整段包在 try/catch 里,读不到不该把面板带崩。\n */\n function unbindTurns(): void {\n if (turnsBound === null) return\n try {\n if (typeof turnsBound.dispose === 'function') turnsBound.dispose()\n } catch (error) {\n // 解绑失败不影响接下来的绑定。\n }\n turnsBound = null\n set({ turns: { available: false, prompts: {}, latest: null } })\n }\n\n function bindTurns(sessionId: string | null | undefined): void {\n if (turnsBound !== null && turnsBound.sessionId === sessionId) return\n unbindTurns()\n if (!sessionId || turnSessions === null) return\n try {\n var bound = turnSessions.binding(sessionId)\n var live = bound ? bound.session : undefined\n if (!live || !live.projections || typeof live.projections.faceOf !== 'function') return\n var face = live.projections.faceOf('turnOutline')\n if (!face || typeof face.getSnapshot !== 'function') return\n // 已用 typeof 确认 getSnapshot 是函数:这里断言成\"带 getSnapshot 的 face\",\n // 让闭包里可以保持 this 绑定地调用它(把方法拆出来再调会丢 this)。\n var liveFace = face as TurnFace & { getSnapshot: () => unknown }\n var apply = function () {\n try {\n set({ turns: normalizeTurns(liveFace.getSnapshot()) })\n } catch (error) {\n probe.log('turns-read-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n apply()\n var dispose = typeof liveFace.subscribe === 'function' ? liveFace.subscribe(apply) : null\n turnsBound = { sessionId: sessionId, dispose: dispose }\n } catch (error) {\n // 会话还没物化、投影还没注册、注入没到位 —— 都只意味着\"这一轮拿不到回合信息\"。\n probe.log('turns-bind-failed', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n /** 归一 `turnOutline` 快照:`{ turns: [{ turn, prompt }], draft }`。 */\n function normalizeTurns(snapshot: unknown): TurnsState {\n // 投影快照是外部服务给的 unknown:先收成记录,再逐项判断。\n var record =\n snapshot !== null && typeof snapshot === 'object' ? (snapshot as { turns?: unknown }) : null\n var entries = record !== null && Array.isArray(record.turns) ? record.turns : null\n if (entries === null) return { available: false, prompts: {}, latest: null }\n var prompts: Record<number, string> = {}\n var latest: number | null = null\n for (var index = 0; index < entries.length; index += 1) {\n var entry = entries[index]\n if (!entry || typeof entry !== 'object') continue\n // 每条 turn 也是外部形状:同样先收成记录再逐字段判断。\n var item = entry as { turn?: unknown; prompt?: unknown }\n if (typeof item.turn !== 'number') continue\n if (typeof item.prompt === 'string' && item.prompt.length > 0)\n prompts[item.turn] = item.prompt\n if (latest === null || item.turn > latest) latest = item.turn\n }\n return { available: true, prompts: prompts, latest: latest }\n }\n\n /**\n * 取侧栏每条会话行要的摘要(**一次批量**,不是 N 次 RPC)。\n *\n * 会话列表由**宿主**枚举(它有 `sessions` 服务),客户端不用把名单端过去;宿主没有\n * 那个服务时回 `unavailable: true`,那些 chip 就整片不画(其余功能照旧)。\n */\n function refreshRows(): Promise<unknown> {\n if (rowsInflight) return rowsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionStatusesPayload>(CHANNEL, 'session-statuses', {\n args: {},\n })\n })\n .then(function (result) {\n rowsInflight = null\n if (result && result.ok === true && result.value) {\n var byId: Record<string, HudSessionOverviewRow> = {}\n var list = Array.isArray(result.value.sessions) ? result.value.sessions : []\n for (var index = 0; index < list.length; index += 1) {\n var item = list[index]\n if (item && typeof item.sessionId === 'string') byId[item.sessionId] = item\n }\n probe.log('session-statuses', {\n sessions: list.length,\n unavailable: result.value.unavailable === true,\n worktrees: typeof result.value.worktrees === 'number' ? result.value.worktrees : null,\n truncated: result.value.truncated === true,\n })\n set({\n rows: {\n phase: 'ready',\n byId: byId,\n unavailable: result.value.unavailable === true,\n truncated: result.value.truncated === true,\n updatedAt: Date.now(),\n },\n })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-statuses-failed', { code: code || 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n rowsInflight = null\n probe.log('session-statuses-failed', { code: 'transport' })\n set({\n rows: {\n phase: 'error',\n byId: {},\n unavailable: true,\n truncated: false,\n updatedAt: Date.now(),\n },\n })\n })\n rowsInflight = task\n return task\n }\n\n /** 取本会话的工具活动(正在跑哪个 / 跑过哪些 / 各几次)。 */\n function loadTools(): Promise<unknown> {\n var sessionId = getState().sessionId\n if (!sessionId) return Promise.resolve()\n if (toolsInflight) return toolsInflight\n var task = Promise.resolve()\n .then(function () {\n return ctx.connection.rpc.call<SessionToolsPayload>(CHANNEL, 'session-tools', {\n args: { sessionId: sessionId },\n })\n })\n .then(function (result) {\n toolsInflight = null\n if (getState().sessionId !== sessionId) return\n if (result && result.ok === true && result.value) {\n var value = result.value\n var active: HudToolActive | null =\n value.active && typeof value.active === 'object' ? value.active : null\n var tools = {\n phase: 'ready',\n active: active,\n recent: Array.isArray(value.recent) ? value.recent : [],\n counts: Array.isArray(value.counts) ? value.counts : [],\n skills: Array.isArray(value.skills) ? value.skills : [],\n skillsDropped: typeof value.skillsDropped === 'number' ? value.skillsDropped : 0,\n total: typeof value.total === 'number' ? value.total : 0,\n updatedAt: Date.now(),\n }\n // 内容没变就不要 set:这个节拍很密(见 TOOLS_POLL_MS),无谓的重渲染会到处都是。\n var previous = getState().tools\n var same =\n previous !== null && previous.active !== null && tools.active !== null\n ? previous.active.callId === tools.active.callId && previous.total === tools.total\n : previous !== null &&\n previous.active === null &&\n tools.active === null &&\n previous.total === tools.total &&\n previous.recent.length === tools.recent.length &&\n (previous.recent.length === 0 || previous.recent[0].at === tools.recent[0].at)\n if (same) return\n probe.log('session-tools', {\n active: tools.active === null ? null : tools.active.name,\n total: tools.total,\n kinds: tools.counts.length,\n // 技能单独露面:排查\"技能名有没有记到\"时不必再靠猜(这一版新增的字段)。\n skills: tools.skills.length,\n })\n set({ tools: tools })\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n probe.log('session-tools-failed', { code: code || 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n .catch(function () {\n toolsInflight = null\n probe.log('session-tools-failed', { code: 'transport' })\n set({\n tools: {\n phase: 'error',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: Date.now(),\n },\n })\n })\n toolsInflight = task\n return task\n }\n\n return {\n bindTurns: bindTurns,\n loadTools: loadTools,\n refreshRows: refreshRows,\n unbindTurns: unbindTurns,\n }\n}\n","/**\n * 共享 store 的组装层:自己管状态、订阅、持久化与布局,七片各自管自己的域。\n *\n * 它对外只暴露浏览器半需要的那一套(打开面板 / 差异 / 提交 / 会话列表摘要 / 审阅 …),\n * 每个\"片\"的入口都只是转发到这个文件外面的小工厂。\n */\n\nimport { diffPayload } from '../components/diff-view.js'\nimport type { HudChange, HudState, HudStore, HudStatus, PanelPos } from '../shared.js'\nimport {\n CHANNEL,\n COMMIT_LIST_WIDTH,\n COMMIT_MIN_WIDTH,\n DIFF_DEFAULT_WIDTH,\n DIFF_MIN_WIDTH,\n ZOOM_STEPS,\n readReviewed,\n readUi,\n reviewKeyOf,\n visibleChangeRows,\n writeUi,\n} from '../shared.js'\nimport type { StoreContext } from './types.js'\nimport { createAttributionSlice } from './attribution.js'\nimport { createCommitsSlice } from './commits.js'\nimport { createDiffSlice } from './diff.js'\nimport { createImageSlice } from './image.js'\nimport { createSessionInfoSlice } from './session-info.js'\nimport { createTipSlice } from './tip.js'\nimport { createToolsSlice } from './tools.js'\nexport function createStore(ctx: StoreContext): HudStore {\n var imageSlice = createImageSlice(ctx)\n var ui = readUi()\n var pos: PanelPos | null =\n ui.pos &&\n typeof ui.pos === 'object' &&\n typeof ui.pos.left === 'number' &&\n typeof ui.pos.top === 'number'\n ? { left: ui.pos.left, top: ui.pos.top }\n : null\n\n var state: HudState = {\n phase: 'idle',\n sessionId: null,\n cwd: null,\n status: null,\n errorCode: null,\n updatedAt: null,\n panelOpen: ui.open === true,\n pos: pos,\n /** 胶囊所在 dock 行的视口顶部坐标:面板据此贴到它上方,而不是用写死的偏移。 */\n anchorTop: null,\n /** 正在查看的差异;`null` 表示面板停在改动清单。 */\n diff: null,\n /**\n * 正在查看的提交(点了「最近提交」里的一行);`null` 表示左栏是工作区清单。\n * 打开时左栏换成这次提交的改动清单,右栏打开它第一个文件的差异。\n */\n commit: null,\n /**\n * 键盘高亮的那个文件(清单里的 `file`);`null` 表示还没用键盘选过。\n * 指向 `file` 而不是下标:轮询刷新后清单条目可能增减,下标会指错行。\n */\n cursor: null,\n /** 最近提交;`commitsOid` 记住它是哪个 HEAD 取的,oid 变了才重取。 */\n commits: null,\n commitsOid: null,\n /**\n * 标签列表。与提交不同,**新 tag 可以打在老提交上**(HEAD 不动),所以不能只按 oid 判断;\n * 改用\"上次取的时刻\"做新鲜度窗口:面板开着时每次刷新都看一眼,但 10s 内不重复问 git。\n */\n tags: null,\n tagsAt: 0,\n tagsTruncated: false,\n /** 差异栏宽度(面板右边缘可拖),持久化。 */\n diffWidth: typeof ui.diffWidth === 'number' ? ui.diffWidth : DIFF_DEFAULT_WIDTH,\n /** 提交明细栏宽度(它与差异栏之间那条抓握可拖),持久化。 */\n commitWidth: typeof ui.commitWidth === 'number' ? ui.commitWidth : COMMIT_LIST_WIDTH,\n /**\n * 会话归因:本会话改过哪些文件(宿主半按 fs 事件记的)。\n *\n * `files` 为 `null` 表示\"还没有这份数据\"(老宿主 / 还没回来 / 请求失败)——\n * 界面据此不打标记、也不过这个滤。\n */\n sessionChanges: {\n phase: 'idle',\n files: null,\n possible: null,\n commands: [],\n since: null,\n dropped: 0,\n possibleDropped: 0,\n errorCode: null,\n },\n /** 「只看本会话改的」开关(持久化,跟\"自动换行\"一样)。 */\n onlySession: ui.onlySession === true,\n /** 「只看最近一轮」开关:把清单收窄到最近这一轮动过的文件。 */\n onlyLatest: ui.onlyLatest === true,\n /** 这一版每个改动文件的签名(`file → signature`);空表表示\"这次没要\"。 */\n review: {},\n /**\n * 本会话的工具活动:`active`(正在跑哪个)+ `recent` + 计数。\n *\n * 实时秒数由客户端本地算(`active.startedAt`),不靠把轮询压到亚秒级。\n */\n tools: {\n phase: 'idle',\n active: null,\n recent: [],\n counts: [],\n skills: [],\n skillsDropped: 0,\n total: 0,\n updatedAt: null,\n },\n // 会话时长(计时胶囊用):`{ sessionId, value }`;取数写进快照,渲染那边就是纯函数。\n sessionTime: { sessionId: null, value: null },\n // prompt 历史(输入框 `↑` 召回用):`{ sessionId, value }`,value 里 prompts 是**新的在前**。\n promptHistory: { sessionId: null, value: null },\n // 胶囊悬停提示:`{ key, text, rect }` 或 null。**自己画**,不用浏览器原生 tooltip ——\n // 原生那个在真机上按了没反应(试过三种节点/容器查证,都不是被谁挡住),\n // 而它延迟、样式、能否出现我们都控制不了;自己画则位置、换行、颜色全说了算。\n tip: null,\n /** 侧栏行内 chip 要的摘要:一次批量取回,按 sessionId 索引。 */\n rows: { phase: 'idle', byId: {}, unavailable: false, truncated: false, updatedAt: null },\n /**\n * 我审过的版本:`工作区 + NUL + 文件 → { signature, at }`,存在自己的键里\n * (`dsh-hud.reviewed.v1`,不跟界面开关混在一起)。\n */\n reviewed: readReviewed(),\n /**\n * 回合信息(`turnOutline` 投影):`第 N 轮 → 那一轮的请求预览`。\n *\n * `available: false` 表示这个宿主没有这个投影(或还没读到)——界面这时不提回合,\n * 也绝不因为读不到它影响别的部分。\n */\n turns: { available: false, prompts: {}, latest: null },\n }\n\n var listeners = new Set<() => void>()\n var inflight: Promise<unknown> | null = null\n /** 会话归因那次请求(在途合并)。 */\n\n function getSnapshot(): HudState {\n return state\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return function () {\n listeners.delete(listener)\n }\n }\n\n function emit(): void {\n listeners.forEach(function (listener) {\n listener()\n })\n }\n\n function set(patch: Partial<HudState>): void {\n state = Object.assign({}, state, patch)\n emit()\n }\n\n // 悬停提示片:用到 state/set,所以在两者定义之后再组装。\n var tipSlice = createTipSlice(function () {\n return state\n }, set)\n var sessionInfoSlice = createSessionInfoSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var toolsSlice = createToolsSlice(\n ctx,\n function () {\n return state\n },\n set,\n )\n var commitsSlice = createCommitsSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n closeDiff: function () {\n return diffSlice.closeDiff()\n },\n },\n )\n var attributionSlice = createAttributionSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 惰性包装:这几样分属工具片与组装层,用包装就不必纠结创建顺序。\n bindTurns: function (sessionId) {\n return toolsSlice.bindTurns(sessionId)\n },\n loadTools: function () {\n return toolsSlice.loadTools()\n },\n refresh: function () {\n return refresh()\n },\n refreshRows: function () {\n return toolsSlice.refreshRows()\n },\n reviewedKeyOf: reviewedKeyOf,\n },\n )\n var diffSlice = createDiffSlice(\n ctx,\n function () {\n return state\n },\n set,\n {\n // 这两样暂住在 store 里(清单选择 / 提交差异),等提交那一片搬走时再挪过去。\n hunkCount: hunkCount,\n openCommitDiff: commitsSlice.openCommitDiff,\n openListEntry: openListEntry,\n persist: persist,\n visibleFiles: visibleFiles,\n },\n )\n\n function persist(): void {\n var diff = state.diff\n writeUi({\n onlySession: state.onlySession === true,\n onlyLatest: state.onlyLatest === true,\n open: state.panelOpen,\n pos: state.pos,\n wrap: diff ? diff.wrap === true : false,\n zoom:\n diff && typeof diff.zoom === 'number' && ZOOM_STEPS.indexOf(diff.zoom) >= 0\n ? diff.zoom\n : null,\n diffWidth: state.diffWidth,\n commitWidth: state.commitWidth,\n })\n }\n\n /** 拖动提交栏右边那条抓握时只改内存;松手才落盘。 */\n function setCommitWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(COMMIT_MIN_WIDTH, Math.round(width))\n if (state.commitWidth === next) return\n set({ commitWidth: next })\n }\n\n /** 拖动右边缘时只改内存;松手才落盘(persistLayout)。 */\n function setDiffWidth(width: number): void {\n if (typeof width !== 'number' || !Number.isFinite(width)) return\n var next = Math.max(DIFF_MIN_WIDTH, Math.round(width))\n if (state.diffWidth === next) return\n set({ diffWidth: next })\n }\n\n /* --------------------------------------------------- 图片预览(二进制) */\n\n /**\n * 图片两侧的缓存:键与请求一一对应。\n *\n * 一张图的 base64 可能上兆,所以命中就不重取;条目数有上限(超了整表清空,\n * 和宿主的 `CACHE_MAX_ENTRIES` 同一个思路:宁可少缓存,也不要无界增长)。\n */\n\n /**\n * 收起右栏。提交明细把差异包在里面,所以它优先整体退掉;\n * 只有工作区差异时才只是关掉差异。\n */\n function closeDetail(): void {\n if (state.commit !== null) return commitsSlice.closeCommit()\n return diffSlice.closeDiff()\n }\n\n /**\n * 当前能上下选的那份清单:打开提交明细时是这次提交的改动,否则是工作区清单。\n *\n * 清单必须跟视图一致 —— 否则打开着提交明细时按 ↑/↓ 会去动工作区的行,\n * 高亮和打开的文件对不上。\n */\n function visibleFiles(): HudChange[] {\n if (state.commit !== null) {\n return Array.isArray(state.commit.files) ? state.commit.files : []\n }\n // 与面板渲染**共用同一份**过滤结果:过滤只发生在一个地方,↑/↓ 才不会落到看不见的行上。\n return visibleChangeRows(state)\n }\n\n /** 打开清单里的一条:提交明细打开时走提交差异,否则走工作区差异。 */\n function openListEntry(file: HudChange): Promise<unknown> {\n if (state.commit !== null) return commitsSlice.openCommitDiff(file)\n return diffSlice.openDiff(file)\n }\n\n /** 当前差异里有几个 hunk —— n / p 的边界就是它。 */\n function hunkCount(): number {\n var payload = diffPayload(state.diff)\n var rows = payload && payload.rows ? payload.rows : []\n var total = 0\n for (var index = 0; index < rows.length; index += 1) {\n if (rows[index].kind === 'hunk') total += 1\n }\n return total\n }\n\n /**\n * 图片缩放的档位(`null` = 适应宽度)。\n *\n * 放在 store 而不是组件里:一来记住(和\"自动换行\"一样落盘),二来组件的 hooks\n * 在这个假 React 里驱动不了嵌套状态 —— store 的状态任何位置都读得到。\n */\n function setZoom(value: number | null): void {\n var diff = state.diff\n if (!diff) return\n var next = value === null || ZOOM_STEPS.indexOf(value) >= 0 ? value : null\n set({ diff: Object.assign({}, diff, { zoom: next }) })\n persist()\n }\n\n /** dock 行的位置变了才写,避免每次测量都触发重渲染。 */\n function setAnchorTop(top: number | null): void {\n if (state.anchorTop === top) return\n set({ anchorTop: top })\n }\n\n function refresh(): Promise<unknown> {\n // 会话归因跟 status 同一条命(两个都是\"当前会话 + 当前工作区\"的只读事实)。\n attributionSlice.loadSessionChanges()\n // 工具活动只跟会话有关(没有工作区也该显示\"正在跑什么\")。\n toolsSlice.loadTools()\n // 回合投影:会话还没物化时这里会安静地不接,下一次刷新再试。\n toolsSlice.bindTurns(state.sessionId)\n if (!state.cwd) return Promise.resolve()\n if (inflight) return inflight\n var cwd = state.cwd\n\n set({ phase: state.status ? 'ready' : 'loading' })\n\n inflight = Promise.resolve()\n .then(function () {\n // 面板打开时才要\"这一版\"的签名:胶囊那一路不需要,白付一次 stat。\n return ctx.connection.rpc.call<HudStatus>(CHANNEL, 'status', {\n args: { cwd: cwd, review: state.panelOpen === true },\n })\n })\n .then(function (result) {\n inflight = null\n if (state.cwd !== cwd) return\n if (result && result.ok === true) {\n var statusValue = result.value || null\n set({\n phase: 'ready',\n status: statusValue,\n // 送回来的\"这一版\"签名(没要或没改动就是空表)。\n review:\n statusValue && statusValue.review && typeof statusValue.review === 'object'\n ? statusValue.review\n : {},\n errorCode: null,\n updatedAt: Date.now(),\n })\n // 提交只取一次:面板打开时取,且只有 HEAD 变了(提交过、切分支、拉取)才重取。\n if (\n statusValue &&\n state.panelOpen &&\n (state.commits === null || state.commitsOid !== statusValue.oid)\n ) {\n commitsSlice.loadCommits(cwd, statusValue.oid)\n }\n // 标签与 HEAD 无关(新 tag 可以打在老提交上),所以只要面板开着就问一次(10s 窗口)。\n if (statusValue && state.panelOpen) commitsSlice.loadTags(cwd)\n return\n }\n var code = result && result.error && result.error.details ? result.error.details.code : null\n set({ phase: 'error', errorCode: code || 'git-failed' })\n })\n .catch(function () {\n inflight = null\n if (state.cwd !== cwd) return\n set({ phase: 'error', errorCode: 'transport' })\n })\n\n return inflight\n }\n\n /** 审阅存档的键:**按工作区分开**,同一个文件名在别的仓库里是另一码事。 */\n function reviewedKeyOf(file: string): string {\n return reviewKeyOf(state.cwd, file)\n }\n\n /** 「只看最近一轮」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlyLatest(): void {\n set({ onlyLatest: state.onlyLatest !== true })\n persist()\n }\n\n /** 「只看本会话改的」开关:过滤发生在 `visibleChangeRows`(渲染与键盘共用)。 */\n function toggleOnlySession(): void {\n set({ onlySession: state.onlySession !== true })\n persist()\n }\n\n function openPanel(open: boolean): void {\n if (state.panelOpen === open) return\n set({ panelOpen: open })\n persist()\n if (open) refresh()\n }\n\n function togglePanel(): void {\n openPanel(!state.panelOpen)\n }\n\n /** 拖动过程中的临时位置:只改内存,不写 localStorage。 */\n function movePanel(next: PanelPos): void {\n set({ pos: next })\n }\n\n /** 松开指针后的位置:落盘。 */\n function commitPanel(next: PanelPos): void {\n set({ pos: next })\n persist()\n }\n\n function resetPanel(): void {\n set({ pos: null })\n persist()\n }\n\n return {\n getSnapshot: getSnapshot,\n subscribe: subscribe,\n bindSession: attributionSlice.bindSession,\n refresh: refresh,\n openPanel: openPanel,\n togglePanel: togglePanel,\n movePanel: movePanel,\n commitPanel: commitPanel,\n resetPanel: resetPanel,\n setAnchorTop: setAnchorTop,\n setDiffWidth: setDiffWidth,\n setCommitWidth: setCommitWidth,\n persistLayout: persist,\n openDiff: diffSlice.openDiff,\n loadImagePair: imageSlice.loadImagePair,\n cachedImagePair: imageSlice.cachedImagePair,\n loadSessionChanges: attributionSlice.loadSessionChanges,\n loadSessionTime: sessionInfoSlice.loadSessionTime,\n loadPromptHistory: sessionInfoSlice.loadPromptHistory,\n showTip: tipSlice.showTip,\n hideTip: tipSlice.hideTip,\n tipHandlers: tipSlice.tipHandlers,\n closeDiff: diffSlice.closeDiff,\n closeDetail: closeDetail,\n setDiffSide: diffSlice.setDiffSide,\n toggleDiffWrap: diffSlice.toggleDiffWrap,\n setZoom: setZoom,\n openCommit: commitsSlice.openCommit,\n openCommitDiff: commitsSlice.openCommitDiff,\n toggleCommit: commitsSlice.toggleCommit,\n closeCommit: commitsSlice.closeCommit,\n stepFile: diffSlice.stepFile,\n openCursor: diffSlice.openCursor,\n stepHunk: diffSlice.stepHunk,\n toggleHunk: diffSlice.toggleHunk,\n toggleOnlySession: toggleOnlySession,\n toggleOnlyLatest: toggleOnlyLatest,\n refreshRows: toolsSlice.refreshRows,\n loadTools: toolsSlice.loadTools,\n toggleReviewed: attributionSlice.toggleReviewed,\n clearReviewed: attributionSlice.clearReviewed,\n }\n}\n","/**\n * 样式落地:把 `styles.ts` 那份表插进 `<head>`(用 `CSS_TAG_ID` 防重复注入),\n * 以及从已注入的样式里取一个值的助手。\n */\n\nimport { CSS } from './styles.js'\nimport { CSS_TAG_ID } from './shared.js'\n/**\n * 审阅进度:`{ marks: {file → 'fresh'|'stale'}, done, total, staleCount }`。\n *\n * 判据是**签名**:宿主 stat 出来的 size+mtimeMs 和存档里的是同一个才算\"这一版审过了\"。\n * 签名变了(又被改动)就把那条标记降级成 `stale`,界面显示\"改动已变,需要重看\" ——\n * 而不是继续冒充已审。没有签名的文件(宿主这次没算)不参与统计。\n */\n\n/**\n * 从自有样式表里抠出计数芯片的横向间隔。\n *\n * 用来回答\"页面上跑着的到底是哪一版 CSS\"—— 改完样式没生效时,这一眼就能分清是\n * \"bundle 还是旧的\"还是\"样式规则没生效\"。\n */\nexport function cssChipsGap(css: string): string | null {\n var match = /\\.dshh-chips\\{[^}]*gap:([^;}]+)/.exec(css)\n return match === null ? null : match[1].trim()\n}\n\n/* ---------------------------------------------------------------- 样式 */\n\n/* ---------------------------------------------------------------- 样式注入 */\n\n/**\n * 幂等注入自有样式表,并**自己打上 `data-plugin`**。\n *\n * 框架的 client-modules 会在每次模块物化后 `claimStyles(id)`:把当时所有\n * **没有 `data-plugin`** 的 `<style>` 认领给刚物化的那个模块,然后在 HMR 的\n * replace / invalidate / prune 路径上 `removeOwnedStyles(id)` 按 `data-plugin`\n * 删掉。不打标的表会被别的模块领走,在那次 rebuild 里连同别人的表一起删掉 ——\n * 而我们的注入只在插件激活时跑一次,于是页面永久退回骨架(原生 `button`、\n * 没有间隙 / 省略号 / 居中几何)。\n *\n * 框架自己的包与同门的 dsh-git-graph 都按这个约定打标;tag 存在时复用同一节点\n * (HMR 会先把旧表删掉,再由新的 fiber 重新调用本函数)。\n *\n * @returns 该样式节点;没有 `document` 时返回 `null`。\n */\nexport function ensureStyles(): HTMLStyleElement | null {\n if (typeof document === 'undefined') return null\n var selector = 'style[data-plugin-css=\"' + CSS_TAG_ID + '\"]'\n // 选择器只可能命中我们自己注入的 <style>(tag id 是插件私有的):这里断言成 HTMLStyleElement。\n var tag = (\n typeof document.querySelector === 'function' ? document.querySelector(selector) : null\n ) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n // 值必须等于客户端模块 id(= 包名):客户端的 `removeOwnedStyles(id)` 靠它回收样式。\n tag.setAttribute('data-plugin', '@konnga/dsh-hud')\n tag.setAttribute('data-plugin-css', CSS_TAG_ID)\n tag.setAttribute('data-dsh-hud', 'styles')\n document.head.appendChild(tag)\n }\n tag.textContent = CSS\n return tag\n}\n","/**\n * dsh-hud — 浏览器半(`dsh.client` bundle)的**插件面**。\n *\n * 两个座位,一个共享 store:\n *\n * - `conversation.input.dock` → 胶囊:输入框上方那一行里靠左的紧凑状态胶囊\n * (分支摘要,或\"非 git 仓库\"),点击开关面板。这个座位是**会话作用域**,\n * 框架直接把当前 `sessionId` 交给它,它再从 `useSessions` 快照里取该会话的\n * `cwd` 写进共享 store —— 这是 root 作用域的面板**读不到**的东西。\n * - `shell.overlay` → 悬浮面板:收起来时渲染 `null`(不留会挡住输入框的气泡),\n * 展开时是悬浮面板,默认停在输入框上方。\n *\n * 所有 git 工作都在宿主半(`/dsh-hud-rpc`);这里只负责座位注册、轮询节拍、\n * 语言字典与样式注入 —— 具体行为都在 store / hooks / components 里。\n *\n * ⚠️ `lib/client.js` 是从 `src/client/**` **打包出来的产物**(`npm run build`):浏览器半只支持\n * \"一个经典脚本\",所以源码分模块、产物是单文件(见 `rolldown.config.mjs`)。别直接改产物。\n */\n\nimport { CSS } from './styles.js'\nimport { HudDockPill } from './components/dock-pill.js'\nimport { HudFloatingPanel } from './components/floating-panel.js'\nimport { PillTip } from './components/pill-tip.js'\nimport { SessionMenuSummary, SessionRowHoverChip } from './components/sidebar.js'\nimport { TurnImages } from './components/turn-images.js'\nimport { bindPromptHistory } from './composer.js'\nimport { en, zh } from './i18n.js'\nimport { NS, POLL_MS, ROWS_POLL_MS, TOOLS_POLL_MS, probe } from './shared.js'\nimport { createStore } from './store/index.js'\nimport type { StoreContext } from './store/types.js'\nimport { cssChipsGap, ensureStyles } from './styles-apply.js'\n// 侧栏标记删掉之后,这份数据只剩\"悬停卡片 / ⋯ 菜单\"两处**按需展示**在用 ——\n// 所以从 12s 放宽到 30s:按下不表的东西没必要那么勤地去问 git。\n\nexport var inject = ['slots', 'connection', 'locale']\n\n/* --------------------------------------------------------- 框架注入面 */\n\n/**\n * 客户端插件上下文里用到的部分(框架给的 `ctx`)。\n *\n * 座位定义在注册时才拿到 `sessionId`(会话作用域座位有、root 作用域没有),\n * 所以这里把 `inject` 的形参声明出来,让回调里的 `sessionId` 有类型可依。\n */\ninterface HudSlotDefinition {\n name: string\n id: string\n order?: number\n locale?: string\n inject?: (sessionId?: string) => Record<string, unknown>\n}\n\ninterface HudSlots {\n inject(slot: string, callback: () => unknown): unknown\n register(definition: HudSlotDefinition, component: unknown): unknown\n}\n\ninterface HudLocale {\n register(\n namespace: string,\n dictionaries: { zh: Record<string, string>; en: Record<string, string> },\n ): (() => void) | void\n}\n\ninterface HudPluginContext extends StoreContext {\n slots: HudSlots\n locale: HudLocale\n}\n\n/* ---------------------------------------------------------------- 插件 */\n\n/**\n * 客户端插件主体。\n * @param ctx - 客户端根上下文(slots、connection、locale)。\n */\nexport function apply(ctx: HudPluginContext): void {\n ctx.effect(function () {\n return ctx.locale.register(NS, { zh: zh, en: en })\n }, 'dsh-hud: dictionaries')\n\n ctx.effect(function () {\n var element = ensureStyles()\n return function () {\n if (element && element.parentNode) element.parentNode.removeChild(element)\n }\n }, 'dsh-hud: styles')\n\n var store = createStore(ctx)\n\n // 起手留一行:诊断里**有没有这一行**就足以区分\"客户端半压根没加载\"与\"加载了但\n // 哪一环没接上\"(这一行由 apply 发,与后面的投影/名单无关)。\n probe.log('apply', {\n inject: typeof ctx.inject === 'function',\n slots: typeof ctx.slots === 'object',\n // 跑着的那一版自有样式表里,计数芯片的间隔是多少:改完样式没生效时用它分辨\n // \"bundle 还是旧的\"还是\"规则没生效\"。\n chips: cssChipsGap(CSS),\n })\n\n // 面板与胶囊都要能反映磁盘上的实时变化;页面不可见时不打请求。\n // 顺手补一次样式表:框架的 HMR 记账会把插件的 `<style>` 删掉,删掉就自愈。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n ensureStyles()\n store.refresh()\n }, POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: status poll')\n\n // 从别处回到页面时立刻补一次(定时器在隐藏期间被跳过)。\n ctx.effect(function () {\n var onFocus = function () {\n ensureStyles()\n store.refresh()\n }\n window.addEventListener('focus', onFocus)\n return function () {\n window.removeEventListener('focus', onFocus)\n }\n }, 'dsh-hud: focus refresh')\n\n // 悬停气泡单独一枚 overlay 座位:它要浮在 dock 行之外(行内会被裁切/堆叠上下文限制)。\n // order 比面板小 —— 面板开着时仍然压在最上层。\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-pill-tip',\n order: 10,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n PillTip,\n )\n })\n\n ctx.slots.inject('shell.overlay', function () {\n return ctx.slots.register(\n {\n name: 'shell.overlay',\n id: 'dsh-hud-panel',\n order: 20,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n HudFloatingPanel,\n )\n })\n\n // 侧栏行内 chip 的数据:起手取一次,之后按自己的节奏更新(页面隐藏时跳过)。\n ctx.effect(function () {\n store.refreshRows()\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.refreshRows()\n }, ROWS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: session rows poll')\n\n // 工具活动单独一条节拍(3s;宿主是内存读,密一点才看得到\"正在跑\")。\n //\n // 走 `store.loadTools()`:这里**曾经是裸 `loadTools()`**,而那个函数只存在于 store 内部、\n // 也没暴露出来 —— 于是这条节拍每 3 秒抛一次 ReferenceError(被 interval 吞掉,只在控制台\n // 留痕),工具读数实际靠 `refresh()` 的 8 秒节拍在维持。已暴露并改为经 store 调用。\n ctx.effect(function () {\n var id = window.setInterval(function () {\n if (document.visibilityState === 'hidden') return\n store.loadTools()\n }, TOOLS_POLL_MS)\n return function () {\n window.clearInterval(id)\n }\n }, 'dsh-hud: tool activity poll')\n\n // `⋯` 菜单里的一条只读摘要:行尾拿不到常亮座位,这里是最实在的\"完整信息\"落点。\n ctx.slots.inject('sidebar.workspaces.session.menu.item', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.workspaces.session.menu.item',\n id: 'dsh-hud-session-menu',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionMenuSummary,\n )\n })\n\n // 悬停卡片里那块详情(宽敞的地方;动作条那枚放不下的东西都在这儿)。\n ctx.slots.inject('sidebar.session.row.hover', function () {\n return ctx.slots.register(\n {\n name: 'sidebar.session.row.hover',\n id: 'dsh-hud-session-hover',\n order: 30,\n locale: NS,\n inject: function () {\n return { store: store }\n },\n },\n SessionRowHoverChip,\n )\n })\n\n ctx.slots.inject('conversation.input.dock', function () {\n return ctx.slots.register(\n {\n name: 'conversation.input.dock',\n id: 'dsh-hud-pill',\n order: 20,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n HudDockPill,\n )\n })\n\n // 轮尾(内置「已编辑 N 个文件」卡片所在的那条尾巴)的一扇门:本轮动过图,\n // 点缩略图就在内置文档预览里打开;`⇄` 才进本面板看前后对比。\n ctx.slots.inject('conversation.chat.turnTail', function () {\n return ctx.slots.register(\n {\n name: 'conversation.chat.turnTail',\n id: 'dsh-hud-turn-images',\n order: 30,\n locale: NS,\n inject: function (sessionId) {\n return { store: store, sessionId: sessionId }\n },\n },\n TurnImages,\n )\n })\n\n // 输入框里的 ↑ 召回(纯文本):**最后接线** —— 逻辑全在纯函数里,这一层只负责\n // 读值 / 写值 / 摆光标,且**真接管了才拦默认行为**。刻意排在最后注册,好让\n // \"第一个 keydown 监听是面板那条\"这个既有事实保持不变。\n ctx.effect(function () {\n return bindPromptHistory(store)\n }, 'dsh-hud: prompt history (↑ 召回)')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAa,MAAM;CAIjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAKA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAGA;CAEA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,EAAE;;;;;;;;;;ACzPT,IAAW,IAAIA,cAAM;AA0lBrB,IAAW,UAAU;AACrB,IAAW,KAAK;AAEhB,IAAW,SAAS;;;;;;;;AAQpB,IAAW,YAAY;;AAGvB,IAAW,UAAU;;;;;;;AASrB,IAAW,eAAe;;;;;AAK1B,IAAW,gBAAgB;;AAI3B,IAAW,cAAc;AAGzB,IAAW,kBAAkB;;;;;AAU7B,IAAW,oBAAoB;;AAE/B,IAAW,mBAAmB;;;AAI9B,IAAW,qBAAqB;AAChC,IAAW,iBAAiB;AAE5B,IAAW,oBAAoB;AAE/B,IAAW,uBAAuB;;AAGlC,IAAW,cAA6B;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,IAAW,cAA2C;CACpD,UAAU;CACV,OAAO;CACP,SAAS;CAMT,SAAS;CACT,QAAQ;CACR,aAAa;CACb,WAAW;CACX,YAAY;AACd;;;;;AAMA,IAAW,aAA0C;CACnD,UAAU;CACV,OAAO;CACP,SAAS;CACT,SAAS;CACT,QAAQ;CACR,aAAa;CACb,WAAW;CACX,YAAY;AACd;;;;;AAWA,IAAW,cAAc;AACzB,IAAW,cAAc;AAEzB,IAAW,oBAAoB;;;;;;;AAW/B,SAAgB,eAAe,IAAa;CAC1C,IAAI,OAAO,OAAO,OAAO,YAAY,SAAS,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE,IAAI;CACtE,IAAI,OAAO,KAAO,OAAO;CACzB,IAAI,UAAU,KAAK,MAAM,OAAO,GAAK;CACrC,IAAI,OAAO,KAAK,MAAM,UAAU,IAAI;CACpC,IAAI,QAAQ,KAAK,MAAO,UAAU,OAAQ,EAAE;CAC5C,IAAI,OAAO,UAAU;CACrB,IAAI,QAAQ,CAAC;CACb,IAAI,OAAO,GAAG,MAAM,KAAK,OAAO,IAAI,IAAI,GAAG;CAC3C,IAAI,QAAQ,GAAG,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG;CAC7C,IAAI,OAAO,GAAG,MAAM,KAAK,OAAO,IAAI,IAAI,GAAG;CAC3C,OAAO,MAAM,KAAK,GAAG;AACvB;;AAGA,IAAW,aAAa;CAAC;CAAM;CAAK;CAAG;CAAK;AAAC;;AAG7C,SAAgB,SAAS,IAA+C;CACtE,OAAO,MAAM,OAAO,GAAG,SAAS,YAAY,WAAW,QAAQ,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO;AAC3F;;;;;;;AAQA,SAAgB,SAAS,SAAoC,WAA2B;CACtF,IAAI,YAAY,QAAQ,YAAY,QAAW,OAAO,YAAY,IAAI,IAAI;CAC1E,IAAI,QAAQ,WAAW,QAAQ,OAAO;CACtC,IAAI,QAAQ,GAAG,OAAO,YAAY,IAAI,IAAI;CAC1C,IAAI,OAAO,SAAS,YAAY,IAAI,IAAI;CACxC,IAAI,OAAO,KAAK,QAAQ,WAAW,QAAQ,OAAO;CAClD,OAAO,WAAW;AACpB;AAEA,SAAgB,SAAkB;CAChC,IAAI;EACF,IAAI,MAAM,OAAO,aAAa,QAAQ,MAAM;EAC5C,IAAI,CAAC,KAAK,OAAO,CAAC;EAElB,IAAI,SAAkB,KAAK,MAAM,GAAG;EACpC,OAAO,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IAAK,SAAqB,CAAC;CACjG,SAAS,OAAO;EACd,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,QAAQ,MAAqB;CAC3C,IAAI;EACF,OAAO,aAAa,QAAQ,QAAQ,KAAK,UAAU,IAAI,CAAC;CAC1D,SAAS,OAAO,CAEhB;AACF;;AAGA,IAAW,eAAe;;;;;;;AAO1B,IAAW,iBAAiB,OAAO,aAAa,CAAC;;AAGjD,SAAgB,YAAY,KAAgC,MAAsB;CAChF,OAAO,OAAO,OAAO,EAAE,WAAqB;AAC9C;;AAGA,IAAW,eAAe;;AAG1B,SAAgB,eAA4B;CAC1C,IAAI;EACF,IAAI,MAAM,OAAO,aAAa,QAAQ,YAAY;EAClD,IAAI,CAAC,KAAK,OAAO,CAAC;EAElB,IAAI,SAAkB,KAAK,MAAM,GAAG;EACpC,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC;EAC5E,IAAI,SAAS;EACb,IAAI,MAAmB,CAAC;EACxB,KAAK,IAAI,OAAO,QAAQ;GACtB,IAAI,OAAO,OAAO;GAClB,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,KAAK,CAAC,QAAQ,OAAO,SAAS,UACjF;GAEF,IAAI,QAAQ;GACZ,IAAI,OAAO,MAAM,cAAc,UAAU;GACzC,IAAI,OAAO;IAAE,WAAW,MAAM;IAAW,IAAI,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK;GAAE;EAC3F;EACA,OAAO;CACT,SAAS,OAAO;EACd,OAAO,CAAC;CACV;AACF;;AAGA,SAAgB,cAAc,MAAgC;CAC5D,IAAI;EACF,IAAI,OAAO,OAAO,KAAK,IAAI;EAC3B,IAAI,KAAK,cAAuB;GAC9B,KAAK,KAAK,SAAU,MAAc,OAAe;IAC/C,QAAQ,KAAK,KAAK,CAAC,MAAM,MAAM,KAAK,MAAM,CAAC,MAAM;GACnD,CAAC;GACD,IAAI,UAAuB,CAAC;GAC5B,IAAI,OAAO,KAAK,MAAM,KAAK,YAAqB;GAChD,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG,QAAQ,KAAK,UAAU,KAAK,KAAK;GACtF,OAAO;EACT;EACA,OAAO,aAAa,QAAQ,cAAc,KAAK,UAAU,IAAI,CAAC;EAC9D,OAAO;CACT,SAAS,OAAO;EAEd,OAAO;CACT;AACF;;;;;;;AAUA,SAAgB,cAAqB;CACnC,IAAI,OAA6B,CAAC;CAClC,OAAO;;AAEL,KAAK,SAAU,QAAgB,QAAkC;EAC/D,IAAI,QAAQ,WAAW,SAAY,CAAC,IAAI;EACxC,IAAI,OAAO,SAAS,MAAM,KAAK,UAAU,KAAK;EAC9C,IAAI,KAAK,UAAU,MAAM;EACzB,KAAK,QAAQ;EACb,QAAQ,KAAK,eAAe,IAAI;EAChC,IAAI;GACF,IAAI,UAAU,OAAO,aAAa,QAAQ,SAAS;GACnD,IAAI,SAAkB,YAAY,OAAO,CAAC,IAAI,KAAK,MAAM,OAAO;GAChE,IAAI,OAA4C,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;GAClF,KAAK,KAAK;IAAE,IAAI,KAAK,IAAI;IAAS;GAAK,CAAC;GACxC,OAAO,aAAa,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,GAAG,CAAC,CAAC;EACxE,SAAS,OAAO,CAEhB;CACF,EACF;AACF;;;;;;;AAQA,SAAgB,kBAAkB,OAA8B;CAC9D,IAAI,UAAU,MAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,OAAO,IAAI,MAAM,OAAO,UAAU,CAAC;CAC5F,IAAI,cAAc,MAAM,gBAAgB;CACxC,IAAI,aAAa,MAAM,eAAe;CACtC,IAAI,CAAC,eAAe,CAAC,YAAY,OAAO;CACxC,IAAI,OAAO,kBAAkB,KAAK;CAElC,IAAI,SAAS,MAAM,OAAO;CAE1B,IAAI,SAAS,KAAK;CAClB,IAAI,aAAa,KAAK;CACtB,IAAI,OAAO;CACX,IAAI,aAEF,OAAO,KAAK,OAAO,SAAU,QAAQ;EACnC,OAAO,OAAO,OAAO,UAAU,UAAa,WAAW,OAAO,UAAU;CAC1E,CAAC;CAEH,IAAI,aAAa,KAAK;CACtB,IAAI,cAAc,eAAe,MAE/B,OAAO,KAAK,OAAO,SAAU,QAAQ;EACnC,IAAI,SAAS,OAAO,OAAO;EAC3B,OAAO,WAAW,UAAa,OAAO,aAAa;CACrD,CAAC;CAEH,OAAO;AACT;;AAGA,IAAW,QAAe,YAAY;;;;;;;AAQtC,SAAgB,WAAW,MAAe,OAAyB;CACjE,IAAI,OAAO,SAAS,YAAY,OAAO,UAAU,YAAY,SAAS,MAAM,UAAU,IACpF,OAAO;CACT,OAAO,SAAS,SAAS,MAAM,QAAQ,IAAI,MAAM,KAAK,KAAK,QAAQ,KAAK,MAAM;AAChF;;;;;;AASA,SAAgB,aAAa,SAA+B;CAC1D,OAAO;EACL,QAAQ,OAAO;EACf,QAAQ,QAAQ;EAChB,QAAQ,YAAY;EACpB,QAAQ,QAAQ;EAChB,QAAQ,OAAO;CACjB,CAAC,CAAC,KAAK,IAAQ;AACjB;;AAGA,SAAgB,YAAY,OAAwB;CAClD,IAAI,OAAO,UAAU,YAAY,CAAC,SAAS,KAAK,KAAK,QAAQ,GAAG,OAAO;CACvE,IAAI,QAAQ,KAAM,OAAO,OAAO,KAAK,IAAI;CACzC,IAAI,QAAQ,KAAa,QAAQ,QAAQ,IAAI,CAAE,QAAQ,CAAC,IAAI;CAC5D,QAAQ,QAAQ,MAAO,IAAI,CAAE,QAAQ,CAAC,IAAI;AAC5C;;AAGA,IAAW,wBAAwB;CAAC;CAAO;CAAO;CAAQ;CAAO;CAAQ;CAAO;CAAO;AAAM;;;;;;AAS7F,SAAgB,YAAY,OAA8B;CACxD,OAAO;EACL,MAAM,MAAM,SAAS,QAAQ,OAAO,MAAM,SAAS;EACnD,QAAQ,MAAM,WAAW,QAAQ,OAAO,MAAM,WAAW;CAC3D;AACF;;;;;;;;;;AAWA,SAAgB,cACd,QACA,WACA,aACQ;CACR,IAAI,YAAY,OAAO,WAAW;CAClC,IAAI,UAAU,OAAO,SAAS;CAC9B,IAAI,CAAC,aAAa,CAAC,SAAS;CAG5B,IAAI,SAAS,YACT,KAAK,SAAsB,OAAO,gBAAgB,WAAW,iBAA+B,IAC5F;CACJ,IAAI,QAAQ,UACR,KAAK,SAAoB,OAAO,cAAc,WAAW,eAA8B,IACvF;CAGJ,IAAI;CACJ,IAAI,YAAY,aAAa,cAA8B;CAC3D,IAAI,YAAY,OAAO,aAAa;CACpC,OAAO,KAAK,UAAkB,SAAS,YAAY,QAAQ,WAAW,SAAS;AACjF;;AAGA,SAAgB,SAAS,MAAc,KAAa,OAAiC;CACnF,IAAI,aAAa,OAAO,UAAU,WAAW;CAC7C,IAAI,UAAU,KAAK,OAAqB,OAAO,aAAa,cAA4B;CACxF,IAAI,SAAS,KAAK,OAAqB,OAAO,cAAc,EAAE;CAC9D,OAAO;EACL,MAAM,KAAK,IAAI,KAAK,OAAqB,IAAI,GAAG,OAAO;EACvD,KAAK,KAAK,IAAI,KAAK,OAAqB,GAAG,GAAG,MAAM;CACtD;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,aAAa,OAAyB;CACpD,IAAI,WAAW,MAAM,YAAY;CACjC,OAAO,cAAc,YAAY,QAAQ,GAAG,SAAS,WAAW,SAAS,WAAW;AACtF;;AAGA,IAAW,aAAa;;;;;;;;;;;;AAexB,SAAgB,kBAAkB,OAA2C;CAE3E,IAAI,QAAgD,MAAM;CAC1D,IAAI,UAAU,QAAQ,UAAU,QAAW,OAAO;CAClD,IAAI,QAAQ,MAAM;CAClB,IAAI,UAAU,QAAQ,UAAU,QAAW,OAAO;CAClD,IAAI,cACF,MAAM,aAAa,SAAY,OAAO,MAAM;CAC9C,IAAI,UAAU,MAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,OAAO,IAAI,MAAM,OAAO,UAAU,CAAC;CAC5F,IAAI,OAAsC,CAAC;CAC3C,IAAI,WAAmD,CAAC;CACxD,IAAI,OAAiB,CAAC;CACtB,IAAI;CACJ,IAAI;CAEJ,KAAK,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;EAClD,SAAS,QAAQ;EACjB,IAAI,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,IAAI,GACzD,KAAK,OAAO,QAAQ,MAAM,OAAO;CACrC;CAEA,KAAK,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;EAClD,SAAS,QAAQ;EACjB,IAAI,KAAK,OAAO,UAAU,QAAW;EACrC,IAAI,gBAAgB,QAAQ,OAAO,UAAU,eAAe,KAAK,aAAa,OAAO,IAAI,GACvF,SAAS,OAAO,QAAQ,YAAY,OAAO;CAE/C;CACA,KAAK,IAAI,QAAQ,OACf,IAAI,OAAO,UAAU,eAAe,KAAK,OAAO,IAAI,KAAK,KAAK,UAAU,QACtE,KAAK,KAAK,IAAI;CAElB,KAAK,KAAK;CACV,IAAI,WAA6B,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,WAAW,CAAC;CACnF,IAAI,QACF,MAAM,UAAU,UAAa,MAAM,UAAU,QAAQ,MAAM,MAAM,cAAc,OAC3E,MAAM,QACN;CACN,IAAI,aAAa,UAAU,OAAO,OAAO,MAAM;CAC/C,IAAI,SAAiC,CAAC;CACtC,IAAI,cAAc;CAClB,KAAK,IAAI,YAAY,MAAM;EACzB,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,QAAQ,GAAG;EAC3D,IAAI,OAAO,OAAO,KAAK,SAAS,CAAC,aAAa,WAAW,KAAK,SAAS,CAAC,WAAW;EACnF,IAAI,SAAS,MAAM;EACnB,OAAO,SAAS,OAAO,UAAU,SAAY,IAAI,OAAO,SAAS;EACjE,IAAI,eAAe,QAAQ,SAAS,YAAY,eAAe;CACjE;CACA,OAAO;EACC;EACI;;EAEV,aAAa,UAAU,OAAO,CAAC,IAAI,MAAM;EAC7B;EACC;EACL;EACR,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC;EAC7B,eAAe,OAAO,KAAK,QAAQ,CAAC,CAAC;;EAErC,cAAc,SAAS;EACvB,iBAAiB,SAAS,OAAO,SAAU,MAAM;GAC/C,OAAO,QAAQ,KAAK,aAAa;EACnC,CAAC,CAAC,CAAC;EACO;EACJ;;EAEN,SAAS,OAAO,KAAK,KAAK,CAAC,CAAC;EAC5B,OAAO,MAAM,UAAU,SAAY,OAAO,MAAM;EAChD,SAAS,MAAM,YAAY,SAAY,IAAI,MAAM;EACjD,iBAAiB,MAAM,oBAAoB,SAAY,IAAI,MAAM;EACjE,QAAQ,QAAQ,SAAS,OAAO,KAAK,IAAI,CAAC,CAAC;CAC7C;AACF;AAEA,SAAgB,WAAW,OAA6B;CACtD,IAAI,UAAU,MAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,OAAO,IAAI,MAAM,OAAO,UAAU,CAAC;CAC5F,IAAI,aACF,MAAM,UAAU,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,CAAC;CACrE,IAAI,WAAW,MAAM,YAAY,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW,CAAC;CACxF,IAAI,SAAS,YAAY,MAAM,KAAK,EAAE;CACtC,IAAI,QAAoC,CAAC;CACzC,IAAI,QAA8B,CAAC;CACnC,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,aAAa;CACjB,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;EACtD,IAAI,OAAO,QAAQ,MAAM,CAAC;EAC1B,IAAI,YAAY,OAAO,WAAW,UAAU,WAAW,WAAW,QAAQ;EAC1E,IAAI,cAAc,MAAM;EACxB,MAAM,QAAQ;EACd,SAAS;EACT,IAAI,OAAO,SAAS,SAAS;EAC7B,IAAI,SAAS,QAAW;EACxB,IAAI,KAAK,cAAc,WAAW;GAChC,MAAM,QAAQ;GACd,QAAQ;EACV,OAAO;GACL,MAAM,QAAQ;GACd,cAAc;EAChB;CACF;CACA,OAAO;EAAS;EAAc;EAAa;EAAa;EAAmB;CAAW;AACxF;;;;;AC1nCA,SAAgB,eAAe,MAAc,KAAqB;CAChE,IAAI,KAAK,UAAU,KAAK,OAAO;CAC/B,IAAI,OAAO,KAAK,MAAM,MAAM,KAAK,CAAC;CAClC,IAAI,OAAO,MAAM,IAAI;CACrB,OAAO,KAAK,MAAM,GAAG,IAAI,IAAI,MAAM,KAAK,MAAM,KAAK,SAAS,IAAI;AAClE;;;;;;;AAQA,SAAgB,gBAAgB,KAA+C;CAC7E,IAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG,OAAO;CACxD,IAAI,UAAU,IAAI,QAAQ,WAAW,EAAE;CACvC,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,IAAI,QAAQ,KAAK,IAAI,QAAQ,YAAY,GAAG,GAAG,QAAQ,YAAY,IAAI,CAAC;CACxE,IAAI,OAAO,UAAU,KAAK,UAAU,QAAQ,MAAM,QAAQ,CAAC;CAC3D,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,OAAO,eAAe,QAAwB;AAChD;;AAGA,SAAgB,YAAY,MAAsB;CAChD,OAAO,eAAe,QAAqB;AAC7C;;;;;;AAOA,SAAgB,gBAAgB,QAA8D;CAC5F,IAAI,MAAwB,CAAC;CAC7B,IAAI,SAAmC,UAAU,CAAC;CAClD,KAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QAAQ,SAAS,GAAG;EAC1D,IAAI,MAAM,YAAY;EACtB,IAAI,QAAQ,OAAO,QAAQ;EAC3B,IAAI,SAAS,GAAG;EAChB,IAAI,KAAK;GAAO;GAAK,MAAM,WAAW;GAAa;EAAM,CAAC;CAC5D;CACA,OAAO;AACT;AAEA,SAAgB,YACd,QACA,GACQ;CACR,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,QAAQ,OAAO,OAAO;CACjC,IAAI,OAAO,KAAK,OAAO,EAAE,gBAAgB,IAAI,MAAM,OAAO;CAC1D,OAAO,EAAE,gBAAgB;AAC3B;AAEA,SAAgB,UAAU,IAAoB;CAC5C,IAAI,OAAO,IAAI,KAAK,EAAE;CACtB,IAAI,MAAM,SAAU,OAAe;EACjC,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK,IAAI,OAAO,KAAK;CACxD;CACA,OAAO,IAAI,KAAK,SAAS,CAAC,IAAI,MAAM,IAAI,KAAK,WAAW,CAAC,IAAI,MAAM,IAAI,KAAK,WAAW,CAAC;AAC1F;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aAAa,KAAkE;CAC7F,IAAI,QAAQ,UAAa,QAAQ,QAAQ,IAAI,YAAY,MAAM,OAAO;CACtE,IAAI,YAAY,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;CAC1D,IAAI,gBAAgB,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;CACtE,IAAI,aAA+B,IAAI,SAAS,OAAO,gBAAgB,IAAI,MAAM,IAAI,CAAC;CACtF,IAAI,UAAU;CACd,KAAK,IAAI,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG,WAAW,WAAW,MAAM,CAAC;CACxF,OAAO;EACA;EACL,MAAM;EACN,aAAa,OAAO,IAAI,gBAAgB,WAAW,IAAI,cAAc;EACrE,UAAU;EACE;EACH;EACT,OAAO,IAAI,SAAS,QAAQ,IAAI,UAAU;EAC1C,YAAY,UAAU,KAAK,YAAY,KAAK,gBAAgB,KAAK,IAAI,UAAU;CACjF;AACF;;AAGA,SAAgB,WACd,QACA,GACQ;CACR,IAAI,OAAO,OAAO,gBAAgB,UAAU,OAAO,OAAO,QAAQ;CAClE,OAAO,SAAS,OAAO,WAAW;AACpC;;;;;;;;AASA,SAAgB,SAAS,IAAoB;CAC3C,IAAI,OAAO,IAAI,KAAK,EAAE;CACtB,OACE,OAAO,KAAK,YAAY,CAAC,IACzB,MACA,KAAK,KAAK,SAAS,IAAI,CAAC,IACxB,MACA,KAAK,KAAK,QAAQ,CAAC,IACnB,MACA,KAAK,KAAK,SAAS,CAAC,IACpB,MACA,KAAK,KAAK,WAAW,CAAC;AAE1B;AAEA,SAAgB,KAAK,OAAuB;CAC1C,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK,IAAI,OAAO,KAAK;AACxD;;AAGA,SAAgB,UAAU,QAA6B;CACrD,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,IAAI,WAAW,KAAK,OAAO;CAC3B,OAAO;AACT;;AAGA,SAAgB,UAAU,MAAiC,GAAsB;CAC/E,IAAI,SAAS,WAAW,OAAO,EAAE,aAAa;CAC9C,IAAI,SAAS,UAAU,OAAO,EAAE,YAAY;CAC5C,IAAI,SAAS,eAAe,OAAO,EAAE,iBAAiB;CACtD,IAAI,SAAS,eAAe,OAAO,EAAE,iBAAiB;CACtD,IAAI,SAAS,aAAa,OAAO,EAAE,eAAe;CAClD,IAAI,SAAS,gBAAgB,OAAO,EAAE,kBAAkB;CAExD,IAAI,SAAS,kBAAkB,OAAO,EAAE,gBAAgB;CAExD,IAAI,SAAS,oBAAoB,OAAO,EAAE,gBAAgB;CAC1D,OAAO,EAAE,gBAAgB;AAC3B;;;;;;;;ACtKA,SAAgB,SAAS,OAA2B;CAClD,IAAI,OAAOC,cAAM,SAAS,WAAY;EACpC,OAAO,MAAM,YAAY;CAC3B,CAAC;CACD,cAAM,UACJ,WAAY;EACV,OAAO,MAAM,UAAU,WAAY;GACjC,KAAK,EAAE,CAAC,MAAM,YAAY,CAAC;EAC7B,CAAC;CACH,GACA,CAAC,KAAK,CACR;CACA,OAAO,KAAK;AACd;;;;;;;;AC8CA,SAAgB,YAAY,OAAoD;CAC9E,IAAI,IACF,MAAM,KACN,SAAU,KAAa;EACrB,OAAO;CACT;CACF,IAAI,YAAY,MAAM;CACtB,IAAI,cAAc,MAAM;CACxB,IAAI,QAAQ,MAAM;CAIlB,IAAI,WAAW,YAAY,SAAU,UAAU;EAC7C,OAAO;CACT,CAAC;CACD,IAAI,UACF,OAAO,cAAc,YAAY,YAAY,SAAS,OAClD,SAAS,KAAK,aACd;CACN,IAAI,MAAM,UAAU,QAAQ,MAAM;CAKlC,IAAI,WAAW,CAAC,WAAW,CAAC,QAAQ,aAAa,QAAQ,QAAQ,WAAW,YAAY,KAAK;CAE7F,IAAI,QAAQ,SAAS,KAAK;;;;;;;;CAS1B,IAAI,OAAOC,cAAM,SAAS,KAAK,IAAI,CAAC;CACpC,IAAI,UAAU,KAAK;CACnB,IAAI,eACF,MAAM,SAAS,MAAM,MAAM,WAAW,QAAQ,MAAM,MAAM,WAAW,SACjE,OAAO,MAAM,MAAM,OAAO,MAAM,IAChC;CAEN,cAAM,UACJ,WAAY;EACV,IAAI,OAAO,cAAc,UAAU,OAAO;EAE1C,MAAM,KAAK;EACX,IAAI,OAAO,WAAY;GACrB,IAAI,MAAM,oBAAoB,QAAW,MAAM,gBAAgB,EAAE;GACjE,IAAI,MAAM,sBAAsB,QAAW,MAAM,kBAAkB,EAAE;EACvE;EACA,KAAK;EACL,IAAI,OAAO,OAAO,YAAY,MAAM,GAAI;EACxC,OAAO,WAAY;GACjB,OAAO,cAAc,IAAI;EAC3B;CACF,GACA,CAAC,SAAS,CACZ;CAGA,cAAM,UAAU,WAAY;EAC1B,IAAI,QAAQ,OAAO,YAAY,WAAY;GACzC,QAAQ,KAAK,IAAI,CAAC;EACpB,GAAG,GAAI;EACP,OAAO,WAAY;GACjB,OAAO,cAAc,KAAK;EAC5B;CACF,GAAG,CAAC,CAAC;CAEL,cAAM,UACJ,WAAY;EACV,IAAI,iBAAiB,MAAM,OAAO;EAClC,IAAI,QAAQ,OAAO,YAAY,WAAY;GACzC,QAAQ,KAAK,IAAI,CAAC;EACpB,GAAG,GAAG;EACN,OAAO,WAAY;GACjB,OAAO,cAAc,KAAK;EAC5B;CACF,GACA,CAAC,YAAY,CACf;CAEA,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,UAAU;EACf,MAAM,YAAY,WAAW,GAAG;CAClC,GACA;EAAC;EAAW;EAAK;EAAU;CAAK,CAClC;CAEA,IAAI,CAAC,OAAO,CAAC,YAAY,MAAM,cAAc,WAAW,OAAO;CAG/D,IAAI,cAAc,kBAAkB,KAAK;CACzC,IAAI,iBACF,gBAAgB,QAAQ,YAAY,YAAY,IAC5C,SAAS,OAAO,YAAY,SAAS,IACrC;CAEN,IAAI,WAA8B,CAAC;CAGnC,IAAI,YAAY,gBAAgB,GAAG;CACnC,IAAI,cAAc,MAAM;EACtB,SAAS,KAAK,EAAE,QAAQ;GAAE,KAAK;GAAa,WAAW;EAAiB,GAAG,SAAS,CAAC;EACrF,SAAS,KAAK,EAAE,QAAQ;GAAE,KAAK;GAAgB,WAAW;EAAoB,GAAG,KAAK,CAAC;CACzF;CACA,IAAI,MAAM,UAAU,SAClB,SAAS,KACP,EAAE,QAAQ;EAAE,KAAK;EAAc,WAAW;CAAe,GAAG,UAAU,EAAE,YAAY,IAAI,GAAG,CAC7F;MACK,IAAI,MAAM,UAAU,aAAa,MAAM,UAAU,QACtD,SAAS,KACP,EACE,QACA;EAAE,KAAK;EAAe,WAAW;CAAe,GAChD,UAAU,EAAE,cAAc,IAAI,GAChC,CACF;MACK,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,OAAO,MACxC,SAAS,KACP,EACE,QACA;EAAE,KAAK;EAAiB,WAAW;CAAe,GAClD,UAAU,EAAE,cAAc,IAAI,GAChC,CACF;MACK;EACL,IAAI,SAAS,MAAM;EAInB,SAAS,KAAK,EAAE,QAAQ;GAAE,KAAK;GAAW,WAAW;EAAe,GAAG,OAAO,CAAC;EAC/E,SAAS,KACP,EACE,QACA;GAAE,KAAK;GAAU,WAAW;EAAc,GAC1C,YAAY,YAAY,QAAQ,CAAC,CAAC,KAAK,OAAO,QAAQ,MAAM,GAC9D,CACF;EACA,SAAS,KAAK,EAAE,QAAQ;GAAE,KAAK;GAAY,WAAW;EAAe,GAAG,GAAG,CAAC;EAC5E,IAAI,OAAO,QAAQ,GACjB,SAAS,KACP,EAAE,QAAQ;GAAE,KAAK;GAAS,WAAW;EAAa,GAAG,OAAO,OAAO,OAAO,KAAK,CAAC,CAClF;EACF,IAAI,OAAO,SAAS,GAClB,SAAS,KACP,EAAE,QAAQ;GAAE,KAAK;GAAU,WAAW;EAAa,GAAG,OAAO,OAAO,OAAO,MAAM,CAAC,CACpF;EAIF,IAAI,aAAa,gBAAgB,OAAO,MAAM;EAC9C,KAAK,IAAI,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;GACzD,SAAS,KAAK,GAAG;GACjB,SAAS,KACP,EACE,QACA;IACE,KAAK,WAAW,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,YAAY,WAAW,MAAM,CAAC,KAAK;GACrD,GACA,WAAW,MAAM,CAAC,OAAO,OAAO,WAAW,MAAM,CAAC,KAAK,CACzD,CACF;EACF;CACF;CAEA,IAAI,mBAAmB,MAAM,SAAS,KAAK,cAAc;CAGzD,IAAI,aAAuB,CAAC,EAAE,YAAY,CAAC;CAC3C,IAAI,gBAAgB,MAAM,WAAW,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,YAAY,SAAS,EAAE,CAAC,CAAC;CACjG,IAAI,KAAK,WAAW,KAAK,GAAG;CAC5B,IAAI,MAAM,UAAU,MAAM,OAAO,MAAM;EACrC,IAAI,MAAM,YAAY,MAAM,QAAQ,CAAC;EACrC,IAAI,MAAM,OAAO,UAAU,OAAO,QAAQ,EAAE,gBAAgB,IAAI,MAAM,MAAM,OAAO;EACnF,WAAW,KAAK,GAAG;EACnB,IAAI,SAAmB,CAAC;EACxB,IAAI,UAAoC,MAAM,OAAO,UAAU,CAAC;EAChE,KAAK,IAAI,KAAK,GAAG,KAAK,YAAY,QAAQ,MAAM,GAAG;GACjD,IAAI,KAAK,YAAY;GACrB,IAAI,KAAK,QAAQ,OAAO;GACxB,IAAI,KAAK,GAAG,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,MAAM,OAAO,EAAE,CAAC;EAC7D;EACA,IAAI,OAAO,SAAS,GAAG,WAAW,KAAK,OAAO,KAAK,KAAK,CAAC;CAC3D;CAIA,OAAO,EACL,OACA;EAAE,WAAW;EAAa,gBAAgB;CAAO,GACjD,EACE,UACA,OAAO,OACL;EACE,MAAM;EACN,WAAW;EACX,gBAAgB;EAChB,iBAAiB,MAAM,YAAY,SAAS;EAC5C,OAAO,EAAE,OAAO,MAAM,YAAY,4CAA4C,OAAU;EACxF,SAAS,WAAY;GACnB,MAAM,YAAY;EACpB;CACF,GACA,MAAM,YAAY,QAAQ,WAAY;EACpC,OAAO,WAAW,KAAK,IAAI;CAC7B,CAAC,CACH,GACA,QACF,GAEA,aAAa,OAAO;EAAK;EAAU;EAAO,KAAK,MAAM,QAAQ,SAAY,KAAK,KAAK,MAAM;CAAI,CAAC,GAE9F,cAAc,OAAO;EAChB;EACI;EACI;EACX,KAAK,MAAM,QAAQ,SAAY,KAAK,KAAK,MAAM;CACjD,CAAC,CACH;AACF;AAEA,SAAgB,aAAa,OAAiB,SAAqD;CACjG,IAAI,IAAI,QAAQ;CAChB,IAAI,QAAQ,MAAM;CAClB,IAAI,CAAC,SAAU,MAAM,WAAW,QAAQ,MAAM,UAAU,GAAI,OAAO;CACnE,IAAI,MAAM,QAAQ;CAElB,IAAI,WAA8B,CAAC;CACnC,IAAI,MAAM,WAAW,MAAM;EAEzB,IAAI,UAAU,iBAAiB,MAAM,QAAQ,GAAG;EAChD,SAAS,KACP,EAAE,QAAQ;GAAE,KAAK;GAAS,WAAW;EAAqC,GAAG,GAAQ,CACvF;EACA,SAAS,KACP,EACE,QACA;GAAE,KAAK;GAAQ,WAAW;EAAiB,GAC3C,MAAM,OAAO,MAAM,OAAO,SAAS,SAAY,MAAM,MAAM,OAAO,IAAI,CACxE,CACF;EAEA,IAAI,OAAO,MAAM,OAAO,YAAY,YAAY,MAAM,OAAO,QAAQ,SAAS,GAC5E,SAAS,KAAK,OAAO,MAAM,OAAO,OAAO;EAE3C,IAAI,YAAY,MAAM,SAAS,KAAK,MAAM,OAAO;CACnD,OAAO;EAQL,IAAI,aAAa,MAAM,OAAO,OAAO,SAAU,MAAM;GACnD,OAAO,KAAK,SAAS;EACvB,CAAC;EACD,IAAI,QAAQ,WAAW,MAAM,GAAG,CAAC;EACjC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxC,IAAI,OAAO,MAAM;GACjB,IAAI,MAAM,KAAK,OAAO;GACtB,IAAI,IAAI,GAAG,SAAS,KAAK,KAAU;GACnC,SAAS,KACP,EACE,QACA;IACE,KAAK,UAAU,OAAO,CAAC;IACvB,WAAW,qBAAqB,MAAM,wBAAwB;GAChE,GACA,MAAM,MAAW,GACnB,CACF;GACA,SAAS,KAAK,MAAM,OAAO,KAAK,IAAI,IAAI,OAAY,OAAO,KAAK,KAAK,CAAC;EACxE;EAGA,IAAI,SAAS,MAAM,UAAU,CAAC;EAC9B,IAAI,OAAO,SAAS,GAAG;GACrB,IAAI,SAAS,SAAS,GAAG,SAAS,KAAK,KAAU;GACjD,SAAS,KAAK,EAAE,cAAc,IAAI,GAAG;GACrC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,UAAU,OAAkB,KAAK,GAAG;IAC7D,IAAI,IAAI,GAAG,SAAS,KAAK,KAAU;IACnC,SAAS,KACP,EACE,QACA;KACE,KAAK,UAAU,OAAO,CAAC;KACvB,WAAW,OAAO,EAAE,CAAC,OAAO,QAAQ,uBAAuB;IAC7D,GACA,OAAO,OAAO,EAAE,CAAC,IAAI,IAAI,OAAY,OAAO,OAAO,EAAE,CAAC,KAAK,CAC7D,CACF;GACF;GACA,IAAI,OAAO,YAAuB,SAAS,KAAK,OAAO,OAAO,OAAO,UAAqB,CAAC;GAC3F,IAAI,MAAM,gBAAgB,GAAG,SAAS,KAAK,OAAO,OAAO,MAAM,aAAa,CAAC;EAC/E;EACA,IAAI,WAAW,SAAS,MAAM,QAC5B,SAAS,KAAK,OAAO,OAAO,WAAW,SAAS,MAAM,MAAM,CAAC;CACjE;CAGA,MAAM,IAAI,cAAc;EACtB,QAAQ,MAAM,WAAW,OAAO,OAAO,OAAO,MAAM,OAAO,IAAI;EAC/D,OAAO,MAAM;CACf,CAAC;CACD,OAAO,EACL,QACA,OAAO,OACL;EACE,WAAW;EACX,gBAAgB;EAChB,KAAK;CACP,GACA,QAAQ,MAAM,YAAY,cAAc,WAAY;EAClD,OAAO,WAAW,OAAO,CAAC;CAC5B,CAAC,CACH,GACA,QACF;AACF;AAEA,SAAgB,cACd,OACA,SAC2B;CAC3B,IAAI,IAAI,QAAQ;CAChB,IAAI,YAAY,QAAQ;CACxB,IAAI,MAAM,QAAQ;CAClB,IAAI,WAAW,MAAM;CACrB,IAAI,QACF,aAAa,QAAQ,aAAa,UAAa,SAAS,cAAc,YAClE,SAAS,QACT;CAEN,IAAI,UAAU,QAAQ,MAAM,cAAc,MAAM,OAAO;CACvD,IAAI,YAAY,MAAM;CACtB,IAAI,OAAO,cAAc,UAAU,OAAO;CAE1C,IAAI,MAAM,KAAK,IAAI,GAAG,MAAM,SAAS;CACrC,IAAI,QAAQ,CAAC,EAAE,aAAa,GAAG,EAAE,iBAAiB,EAAE,MAAM,SAAS,SAAS,EAAE,CAAC,CAAC;CAChF,IAAI,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,WAAW,UAC7D,MAAM,KACJ,EAAE,cAAc;EAAE,KAAK,eAAe,MAAM,KAAK;EAAG,MAAM,eAAe,MAAM,MAAM;CAAE,CAAC,CAC1F;CAEF,IAAI,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,UAAU,UAC5D,MAAM,KAAK,EAAE,eAAe;EAAE,OAAO,OAAO,MAAM,KAAK;EAAG,OAAO,OAAO,MAAM,KAAK;CAAE,CAAC,CAAC;CAEzF,MAAM,IAAI,cAAc,EAAE,KAAK,KAAK,MAAM,MAAM,GAAK,EAAE,CAAC;CAExD,OAAO,EACL,QACA,OAAO,OACL;EACE,WAAW;EACX,gBAAgB;EAChB,KAAK;CACP,GACA,QAAQ,MAAM,YAAY,iBAAQ,WAAY;EAC5C,OAAO,MAAM,KAAK,KAAK;CACzB,CAAC,CACH,GACA,CACE,EAAE,QAAQ;EAAE,KAAK;EAAS,WAAW;CAAkB,GAAG,GAAG,GAC7D,EAAE,QAAQ;EAAE,KAAK;EAAS,WAAW;CAAkB,GAAG,eAAe,GAAG,CAAC,CAC/E,CACF;AACF;AAEA,SAAgB,WAAW,OAAmB,GAAsB;CAClE,IAAI,QAAkB,CAAC;CACvB,IAAI,MAAM,WAAW,MAAM;EACzB,MAAM,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,MAAM,OAAO,IAAI,EAAE,CAAC,CAAC;EAC9D,IAAI,OAAO,MAAM,OAAO,YAAY,YAAY,MAAM,OAAO,QAAQ,SAAS,GAC5E,MAAM,KAAK,MAAM,OAAO,OAAO;CACnC;CACA,MAAM,KAAK,EAAE,eAAe,EAAE,GAAG,OAAO,MAAM,KAAK,EAAE,CAAC,CAAC;CACvD,IAAI,MAAM,OAAO,SAAS,GACxB,MAAM,KACJ,EAAE,cAAc,IACd,MACA,MAAM,OACH,IAAI,SAAU,MAAM;EACnB,OAAO,OAAO,KAAK,IAAI,IAAI,OAAY,OAAO,KAAK,KAAK;CAC1D,CAAC,CAAC,CACD,KAAK,KAAU,CACtB;CAEF,IAAI,MAAM,UAAU,MAAM,OAAO,SAAS,GACxC,MAAM,KACJ,EAAE,cAAc,IACd,MACA,MAAM,OACH,IAAI,SAAU,MAAM;EACnB,OACE,OAAO,KAAK,IAAI,IAChB,OACA,OAAO,KAAK,KAAK,KAChB,KAAK,OAAO,QAAQ,SAAc;CAEvC,CAAC,CAAC,CACD,KAAK,KAAU,CACtB;CAEF,IAAI,SAAS,MAAM,OAAO,MAAM,GAAG,CAAC;CACpC,IAAI,OAAO,SAAS,GAAG;EACrB,MAAM,KAAK,EAAE,cAAc,CAAC;EAC5B,MAAM,KACJ,OACG,IAAI,SAAU,MAAM;GACnB,IAAI,OAAO,KAAK,OAAO,QAAQ,MAAW;GAC1C,IAAI,KAAK,OAAO,KAAK,OAAO,WAAW,OAAO,KAAK,KAAK,IAAI,CAAE,QAAQ,CAAC,IAAI,MAAM;GACjF,IAAI,UACF,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,IACtD,QAAa,KAAK,UAClB;GACN,OAAO,OAAO,MAAM,OAAO,KAAK,IAAI,IAAI,KAAK;EAC/C,CAAC,CAAC,CACD,KAAK,IAAI,CACd;CACF;CACA,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAgB,iBAAiB,QAA8B,KAA4B;CACzF,IAAI,WAAW,QAAQ,OAAO,WAAW,UAAU,OAAO;CAC1D,IAAI,OAAO,OAAO,cAAc,UAAU,OAAO;CACjD,IAAI,UAAU,KAAK,IAAI,IAAI,MAAM,OAAO,aAAa,GAAI;CACzD,OAAO,UAAU,KAAK,QAAQ,QAAQ,CAAC,IAAI,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,IAAI;AACjF;;;;;;;;;AChdA,SAAgB,mBAAmB,OAAoD;CACrF,IAAI,IAAI,MAAM;CACd,IAAI,OAAO,MAAM;CACJ,MAAM;CAGnB,IAAI,QAAkB,CAAC;CACvB,IAAI,SAAS,MAAM;EACjB,MAAM,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,KAAK,SAAS,EAAE,CAAC,CAAC;EAE3D,IAAI,KAAK,eAAe,QAAQ,KAAK,cAAc,GACjD,MAAM,KACJ,EAAE,sBAAsB;GAAE,GAAG,OAAO,KAAK,WAAW;GAAG,MAAM,OAAO,KAAK,aAAa,CAAC;EAAE,CAAC,CAC5F;EAEF,IAAI,KAAK,gBAAgB,GAAG,MAAM,KAAK,EAAE,oBAAoB,EAAE,GAAG,OAAO,KAAK,aAAa,EAAE,CAAC,CAAC;CACjG;CACA,IAAI,SAAS,MAAM;EACjB,IAAI,KAAK,KAAK,SAAS,GAAG,MAAM,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;EACvF,IAAI,KAAK,UAAU,GAAG,MAAM,KAAK,EAAE,mBAAmB,EAAE,GAAG,OAAO,KAAK,OAAO,EAAE,CAAC,CAAC;EAClF,IAAI,KAAK,kBAAkB,GACzB,MAAM,KAAK,EAAE,2BAA2B,EAAE,GAAG,OAAO,KAAK,eAAe,EAAE,CAAC,CAAC;CAChF;CACA,IAAI,MAAM,UAAU,MAAM,OAAO,QAAQ,GACvC,MAAM,KACJ,EAAE,gBAAgB;EAAE,GAAG,OAAO,MAAM,OAAO,IAAI;EAAG,OAAO,OAAO,MAAM,OAAO,KAAK;CAAE,CAAC,CACvF;CAEF,IAAI,MAAM,UAAU,MAAM,OAAO,aAAa,GAC5C,MAAM,KAAK,EAAE,qBAAqB,EAAE,GAAG,OAAO,MAAM,OAAO,UAAU,EAAE,CAAC,CAAC;CAC3E,OAAO,EACL,OACA;EAAE,WAAW;EAAgB,gBAAgB;CAAU,GAEvD,MAAM,WAAW,IACb,OACA,EACE,QACA;EAAE,WAAW;EAAoB,OAAO,iBAAiB,MAAM,CAAC;CAAE,GAClE,MAAM,KAAK,KAAK,CAClB,GACJ,EACE,UACA;EACE,MAAM;EACN,WACE,wBAAwB,MAAM,gBAAgB,OAAO,0BAA0B;EACjF,uBAAuB;EACvB,gBAAgB,MAAM,gBAAgB,OAAO,SAAS;EACtD,OAAO,EAAE,mBAAmB;EAC5B,SAAS,MAAM;CACjB,GACA,MAAM,gBAAgB,OAAO,OAAO,MACpC,EAAE,cAAc,CAClB,GAEA,MAAM,UAAU,MAAM,OAAO,OAAO,IAChC,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,uBAAuB;EACvB,OAAO,EAAE,cAAc;EACvB,SAAS,MAAM;CACjB,GACA,EAAE,mBAAmB,CACvB,IACA,MAEJ,SAAS,QAAQ,KAAK,eAAe,OACjC,OACA,EACE,UACA;EACE,MAAM;EACN,WACE,wBAAwB,MAAM,eAAe,OAAO,0BAA0B;EAChF,uBAAuB;EACvB,gBAAgB,MAAM,eAAe,OAAO,SAAS;EACrD,OAAO,UAAU,MAAM,KAAK,YAAY,CAAC,IAAI,OAAO,EAAE,yBAAyB;EAC/E,SAAS,MAAM;CACjB,GACA,MAAM,eAAe,OAAO,OAAO,MACnC,EAAE,oBAAoB,CACxB,CACN;AACF;;AAGA,SAAgB,cACd,MACA,MAC+B;CAC/B,IAAI,KAAK,SAAS,UAAa,KAAK,KAAK,UAAU,QAAW,OAAO;CACrE,IAAI,KAAK,aAAa,UAAa,KAAK,SAAS,UAAU,QAAW,OAAO;CAC7E,OAAO;AACT;AAEA,SAAgB,aAAa,MAAyB,MAAc,GAAsB;CACxF,IAAI,SAAS,KAAK,SAAS,SAAY,KAAK,KAAK,QAAQ;CACzD,IAAI,WAAW,QAAW;EACxB,IAAI,QAAQ,KAAK,aAAa,SAAY,KAAK,SAAS,QAAQ;EAChE,IAAI,UAAU,QAAW,OAAO,EAAE,eAAe;EAEjD,OAAO,EAAE,yBAAyB,EAAE,QAAQ,OAAO,MAAM,UAAU,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;CACvF;CACA,IAAI,QAAkB,CAAC;CACvB,IAAI,OAAO,SAAS,GAAG,MAAM,KAAK,EAAE,kBAAkB,EAAE,GAAG,OAAO,OAAO,MAAM,EAAE,CAAC,CAAC;CACnF,IAAI,OAAO,QAAQ,GAAG,MAAM,KAAK,EAAE,iBAAiB,EAAE,GAAG,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC;CAChF,IAAI,OAAO,OAAO,OAAO,WAAW,WAAW,UAAU,OAAO,MAAM,IAAI;CAC1E,IAAI,OACF,EAAE,cAAc,KACf,MAAM,WAAW,IAAI,KAAK,MAAM,MAAM,KAAK,GAAG,IAAI,QAClD,OAAO,MAAM,OAAO;CAEvB,IAAI,OAAO,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;CACnE,IAAI,SAAS,MAAM,QAAQ,OAAO,UAAU,MAAM,MAAM,CAAC;CACzD,OAAO;AACT;;AAGA,SAAgB,YAAY,OAA+B,GAAsB;CAC/E,IAAI,UAAU,SAAS,OAAO,EAAE,eAAe;CAC/C,IAAI,UAAU,SAAS,OAAO,EAAE,cAAc;CAC9C,OAAO,EAAE,aAAa;AACxB;;;;;;AAOA,SAAgB,iBAAiB,MAAgC,GAAkC;CAEjG,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI,QAAkB,CAAC;CACvB,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;CACvF,IAAI,YAAY,KAAK,YAAY,CAAC,EAAC,CAAE,MAAM,EAAE,CAAC,CAAC,QAAQ;CACvD,IAAI,SAAS,SAAS,GAAG;EACvB,MAAM,KAAK,EAAE,oBAAoB,EAAE,GAAG,OAAO,SAAS,MAAM,EAAE,CAAC,CAAC;EAChE,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;GACvD,IAAI,OAAgC,SAAS,UAAU,CAAC;GACxD,MAAM,KACJ,OACE,OAAO,KAAK,WAAW,EAAE,KACxB,KAAK,WAAW,OAAO,MAAM,EAAE,uBAAuB,IAAI,GAC/D;EACF;EACA,IAAI,KAAK,kBAAkB,SAAS,QAClC,MAAM,KAAK,EAAE,wBAAwB,EAAE,GAAG,OAAO,KAAK,kBAAkB,SAAS,MAAM,EAAE,CAAC,CAAC;CAE/F;CACA,OAAO,MAAM,WAAW,IAAI,SAAY,MAAM,KAAK,IAAI;AACzD;;;;;;;;;;;;;AAcA,SAAgB,UAAU,MAAyB,MAAc,GAAsB;CACrF,IAAI,SACF,KAAK,gBAAgB,UAAa,KAAK,gBAAgB,OAAO,KAAK,YAAY,QAAQ;CACzF,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,OAAO,CAAC,EAAE,CAAC;CAClD,OAAO,EAAE,0BAA0B;EAAE,GAAG,OAAO,OAAO,CAAC;EAAW;CAAO,CAAC;AAC5E;;;;;AC1LA,SAAS,UAAU,OAAoC;CACrD,OAAO,UAAU,QAAQ,OAAO,UAAU,WAAY,QAAwB;AAChF;;AAGA,SAAgB,gBAAgC;CAC9C,IAAI,OAAO,aAAa,aAAa,OAAO;CAC5C,OAAO,SAAS,iBAAiB;AACnC;;AAGA,SAAgB,cAAc,MAAwB;CACpD,OAAO,eAAe,MAAM,OAAO;AACrC;;AAGA,SAAgB,eAAe,MAAe,MAAuB;CACnE,IAAI,UAAU,UAAU,IAAI;CAC5B,IAAI,QAAQ;CACZ,OAAO,WAAW,QAAQ,IAAI;EAG5B,KADE,OAAO,QAAQ,iBAAiB,aAAa,QAAQ,aAAa,cAAc,IAAI,UACpE,MAAM,OAAO;EAC/B,UAAU,UAAU,QAAQ,UAAU;EACtC,SAAS;CACX;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,eAAe,MAAwB;CACrD,IAAI,UAAU,UAAU,IAAI;CAC5B,IAAI,QAAQ;CACZ,OAAO,WAAW,QAAQ,GAAG;EAC3B,IAAI,MAAM,OAAO,QAAQ,YAAY,WAAW,QAAQ,QAAQ,YAAY,IAAI;EAChF,IAAI,QAAQ,WAAW,QAAQ,cAAc,QAAQ,UAAU,OAAO;EACtE,IAAI,QAAQ,sBAAsB,MAAM,OAAO;EAC/C,IAAI,WACF,OAAO,QAAQ,iBAAiB,aAAa,QAAQ,aAAa,iBAAiB,IAAI;EACzF,IAAI,aAAa,MAAM,aAAa,UAAU,aAAa,kBAAkB,OAAO;EACpF,UAAU,UAAU,QAAQ,UAAU;EACtC,SAAS;CACX;CACA,OAAO;AACT;;AAGA,SAAgB,WAAW,UAAkC;CAC3D,IAAI,OAAO,aAAa,eAAe,OAAO,SAAS,kBAAkB,YAAY,OAAO;CAC5F,OAAO,SAAS,cAAc,QAAQ;AACxC;;;;;;;;AASA,SAAgB,UAAU,MAAe,oBAAkC;CACzE,IAAI,QAAQ,UAAU,IAAI;CAC1B,IAAI,CAAC,SAAS,OAAO,MAAM,eAAe,aAAa;CACvD,IAAI,YAAY,UAAU,MAAM,UAAU;CAC1C,IAAI,QAAQ;CACZ,OAAO,aAAa,QAAQ,IAAI;EAC9B,IAAI,OAAO,UAAU,aAAa,EAAE,CAAC,CAAC,QAAQ,kBAAkB,KAAK,GAAG;EACxE,YAAY,UAAU,UAAU,UAAU;EAC1C,SAAS;CACX;CACA,IAAI,CAAC,aAAa,OAAO,UAAU,cAAc,UAAU;CAC3D,IAAI,MAAM,MAAM;CAChB,IAAI,SAAS,MAAM;CACnB,IAAI,OAAO,UAAU;CACrB,IAAI,OAAO,QAAQ,YAAY,OAAO,WAAW,YAAY,OAAO,SAAS,UAAU;CACvF,IAAI,MAAM,UAAU,WAAW,UAAU,YAAY;MAChD,IAAI,MAAM,SAAS,UAAU,YAAY,MAAM,UAAU,YAAY,MAAM,SAAS;AAC3F;AAIA,SAAgB,iBAAgC;CAC9C,IAAI,OAAO,aAAa,eAAe,OAAO,SAAS,kBAAkB,YAAY,OAAO;CAC5F,IAAI,OAAO,SAAS,cAAc,yBAAuB;CACzD,IAAI,CAAC,QAAQ,OAAO,KAAK,0BAA0B,YAAY,OAAO;CACtE,IAAI,OAAO,KAAK,sBAAsB;CACtC,OAAO,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM;AACnD;;;;;;;AChFA,SAAgB,WAAW,OAAmD;CAC5E,IAAI,UAAU,MAAM,WAAW,CAAC;CAChC,IAAI,aAAa,MAAM;CACvB,IAAI,aAAa,MAAM,UAAU;CAGjC,IAAI,SAAS,MAAM,UAAU;CAG7B,cAAM,UACJ,WAAY;EACV,IAAI,OAAO,aAAa,eAAe,OAAO,SAAS,kBAAkB,YAAY;EACrF,IAAI,SAAS,cAAc;EAC3B,IAAI,CAAC,QAAQ;EACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;GACtD,IAAI,QAAQ,MAAM,CAAC,SAAS,QAAQ;GACpC,IAAI,OAAO,SAAS,cAClB,4CACE,SACA,0BACA,OAAO,KAAK,IACZ,KACJ;GACA,UAAU,MAAM,WAAW;GAC3B;EACF;CACF,GACA;EAAC;EAAY;EAAY,QAAQ;EAAQ;CAAM,CACjD;CAEA,IAAI,QAAQ,WAAW,GAAG,OAAO;CAGjC,IAAI,cAAc,MAAM,gBAAgB,SAAY,OAAO,MAAM;CAEjE,IAAI,SAAS,MAAM,WAAW,SAAY,OAAO,MAAM;CAEvD,IAAI,OAAO,QAAQ,IAAI,SAAU,QAAQ,OAAO;EAG9C,OAAO,EACL,UACA;GACE,MAAM;GACN,WACE,cACC,cAAc,OAAO,SAAS,aAAa,oBAAoB,OAC/D,cAAc,OAAO,SAAS,aAAa,oBAAoB;GAClE,KAAK,OAAO,KAAK,IAAI,MAAM,OAAO;GAClC,gBAAgB;GAChB,iBAAiB;GACjB,kBAAkB,OAAO,KAAK;GAC9B,OACE,OAAO,QACN,gBAAgB,OAAO,KAAK,OAAO,aAAa,aAAa,OAAO,MAAM,MAAM,CAAC,KAClF,OACA,MAAM,EAAE,oBAAoB,IAC5B,OACA,MAAM,EAAE,eAAe;GACzB,SAAS,WAAY;IACnB,MAAM,OAAO,MAAM;GACrB;EACF,GACA,gBAAgB,OACZ,OACA,EACE,QACA;GACE,WACE,iBACC,cAAc,aAAa,OAAO,IAAI,MAAM,SACzC,KACA,cAAc,aAAa,OAAO,IAAI,MAAM,aAC1C,yBACA;GACR,qBAAqB,cAAc,aAAa,OAAO,IAAI;GAC3D,eAAe;EACjB,GACA,cAAc,aAAa,OAAO,IAAI,MAAM,SACxC,MACA,cAAc,aAAa,OAAO,IAAI,MAAM,aAC1C,MACA,GACR,GACJ,EACE,QACA;GACE,WAAW;GACX,OAAO,EACL,OACE,YAAY,UAAU,OAAO,MAAM,MAAM,4CAC7C;EACF,GACA,OAAO,MACT,GACA,EAAE,QAAQ,EAAE,WAAW,YAAY,GAAG,OAAO,IAAI,GACjD,WAAW,QAAQ,OAAO,UAAU,UAAa,OAAO,MAAM,OAAO,UAAU,OAC3E,OACA,EACE,QACA;GACE,WACE,iBACC,OAAO,MAAM,OAAO,UAAU,UAAU,mBAAmB,OAC3D,OAAO,MAAM,OAAO,UAAU,UAAU,sBAAsB;GACjE,eACE,OAAO,MAAM,OAAO,UAAU,SAAY,SAAS,OAAO,MAAM,OAAO;GACzE,OAAO,YAAY,OAAO,MAAM,OAAO,OAAO,MAAM,CAAC;GACrD,SAAS,SAAU,OAA0C;IAE3D,MAAM,gBAAgB;IACtB,MAAM,eAAe;IAErB,IAAI,MAAM,kBAAkB,MAAM,iBAAiB,OAAO,IAAI;GAChE;EACF,GACA,OAAO,MAAM,OAAO,UAAU,SAAY,MAAM,GAClD,CACN;CACF,CAAC;CAED,IAAI,SAAS,MAAM,YACf,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,MAAM,EAAE,iBAAiB,CAAC,IAC/D;CAEJ,OAAO,EACL,OACA,EAAE,WAAW,eAAe,GAC5B,EACE,OACA,EAAE,WAAW,oBAAoB,GACjC,MAAM,SAAS,MAAM,EAAE,eAAe,IAAI,QAAQ,OAAO,QAAQ,MAAM,CACzE,GACA,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,IAAI,GACzC,MACF;AACF;;;;;;;;;;;ACtJA,SAAgB,WAAW,OAAwB;CACjD,IAAI,UAAU,MAAM,WAAW,CAAC;CAChC,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,IAAI,IAAI,MAAM;CAEd,OAAO,EACL,OACA,EAAE,WAAW,eAAe,GAC5B,EAAE,OAAO,EAAE,WAAW,oBAAoB,GAAG,EAAE,eAAe,CAAC,GAC/D,EACE,OACA,EAAE,WAAW,eAAe,GAC5B,QAAQ,IAAI,SAAU,QAAQ,OAAO;EACnC,IAAI,OAAO,OAAO,QAAQ,CAAC;EAC3B,IAAI,SAAS,KAAK,KAAK,SAAU,KAAK;GACpC,OAAO,IAAI,QAAQ,MAAM,MAAM;EACjC,CAAC;EACD,IAAI,OACF,OAAO,OAAO,gBAAgB,WAAW,SAAS,OAAO,WAAW,IAAI,OAAO,QAAQ;EACzF,IAAI,MAAM;GAAC,EAAE,cAAc;GAAG;GAAI,OAAO;GAAS,OAAO,SAAS,QAAQ;EAAI,CAAC,CAC5E,OAAO,IAAI,CAAC,CACZ,KAAK,IAAI;EAEZ,IAAI,OAAO,WAAW,OAAO,KAAK,MAAM,SAAS;EACjD,OAAO,EACL,UACA;GACE,MAAM;GACN,WAAW,iBAAiB,OAAO,uBAAuB;GAC1D,KAAK,OAAO,MAAM,MAAM,OAAO,KAAK;GACpC,OAAO;GACP,SAAS,WAAY;IACnB,IAAI,MAAM,QAAQ,MAAM,OAAO,MAAM;GACvC;EACF,GACA,EAAE,QAAQ,EAAE,WAAW,YAAY,GAAG,OAAO,GAAG,GAChD,EAAE,QAAQ,EAAE,WAAW,gBAAgB,GAAG,OAAO,WAAW,GAAG,GAC/D,SAAS,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,MAAM,IAAI,MAC1D,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,WAAW,QAAQ,CAAC,CAAC,CAC9D;CACF,CAAC,CACH,CACF;AACF;;;;;;;;;;;;;;ACnCA,SAAgB,WAAW,OAAwB;CACjD,IAAI,IAAI,MAAM;CACd,IAAI,SAAS,MAAM;CACnB,IAAI,QAAQ,EAAE,OAAO,MAAM,MAAM;CAEjC,IAAI;CACJ,IAAI,OAAO,UAAU,WACnB,OAAO,EACL,OACA,EAAE,WAAW,kBAAkB,GAC/B,EAAE,OAAO,EAAE,WAAW,kBAAkB,GAAG,EAAE,gBAAgB,CAAC,CAChE;MACK,IAAI,OAAO,UAAU,SAC1B,OAAO,EACL,OACA,EAAE,WAAW,kBAAkB,GAC/B,EAAE,OAAO,EAAE,WAAW,kBAAkB,GAAG,UAAU,OAAO,WAAW,CAAC,CAAC,CAC3E;MACK;EACL,IAAI,QAAQ,CAAC,OAAO,SAAS,QAAQ,OAAO,IAAI;EAChD,IAAI,OAAO,QAAQ,OAAO,KAAK,SAAS,GAAG,MAAM,KAAK,OAAO,KAAK,KAAK,IAAI,CAAC;EAC5E,IAAI,OAAO,OAAO,MAAM,KAAK,EAAE,cAAc,CAAC;EAC9C,OAAO,EACL,OACA,EAAE,WAAW,kBAAkB,GAC/B,EACE,OACA,EAAE,WAAW,mBAAmB,GAChC,EAAE,QAAQ,EAAE,WAAW,YAAY,GAAG,OAAO,QAAQ,GACrD,EACE,QACA;GAAE,WAAW;GAAsB,OAAO,OAAO;EAAQ,GACzD,OAAO,WAAW,GACpB,CACF,GACA,EAAE,OAAO,EAAE,WAAW,kBAAkB,GAAG,MAAM,KAAK,KAAK,CAAC,CAC9D;CACF;CAEA,IAAI,QAAQ,OAAO,SAAS,CAAC;CAC7B,IAAI,OACF,OAAO,UAAU,UACb,OACA,MAAM,WAAW,IACf,EACE,OACA,EAAE,WAAW,mBAAmB,GAChC,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,EAAE,cAAc,CAAC,CACxD,IACA,EACE,OACA,EAAE,WAAW,mBAAmB,GAChC,EAAE,YAAY;EACZ,SAAS;EACT,WAAW,OAAO;EACf;EACH,OAAO,EAAE,cAAc,IAAI,QAAQ,OAAO,MAAM,MAAM;EACtD,QAAQ,MAAM;EACd,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,QAAQ;CACV,CAAC,CACH;CAER,OAAO,EAAE,OAAO;EAAE,WAAW;EAA0B;CAAM,GAAG,MAAM,IAAI;AAC5E;;;;AC9EA,SAAgB,WAAW,OAA4C;CACrE,IAAI,SAAmC,MAAM,UAAU,CAAC;CACxD,IAAI,WAAiC,CAAC;CACtC,KAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QAAQ,SAAS,GAAG;EAC1D,IAAI,MAAM,YAAY;EACtB,IAAI,QAAQ,OAAO,QAAQ;EAC3B,IAAI,SAAS,GAAG;EAChB,SAAS,KACP,EACE,QACA;GACO;GACL,WAAW;GACX,OAAO;IAAE,OAAO,YAAY;IAAM,aAAa;GAAe;GAC9D,OAAO,MAAM,EAAE,WAAW,GAAG,IAAI,MAAM,OAAO,KAAK;EACrD,GACA,MAAM,EAAE,WAAW,GAAG,IAAI,MAAM,OAAO,KAAK,CAC9C,CACF;CACF;CACA,IAAI,SAAS,WAAW,GACtB,OAAO,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,MAAM,EAAE,aAAa,CAAC;CAEtE,OAAO,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,QAAQ;AACvD;;;;;;;;;;;;;ACcA,SAAgB,UAAU,OAAuB;CAC/C,IAAI,IAAI,MAAM;CACd,IAAI,QAAQ,MAAM;CAClB,IAAI,OAAO,MAAM;CACjB,IAAI,UAAU,aAAa,MAAM,MAAM,GAAG;CAC1C,IAAI,MAAM,YAAY,OAAO,OAAO,aAAa,OAAO;CAExD,IAAI,UAAUC,cAAM,SAAgC,WAAY;EAC9D,OAAO,YAAY,QAAQ,MAAM,kBAC7B,YAAY,MAAM,gBAAgB,OAAO,CAAC,IAC1C;CACN,CAAC;CACD,IAAI,UAAUA,cAAM,SAA0B,MAAM;CACpD,IAAI,UAAUA,cAAM,SAA6B,OAAO;CAExD,cAAM,UACJ,WAAY;EACV,IAAI,YAAY,QAAQ,QAAQ,MAAM,OAAO;EAC7C,IAAI,YAAY;EAChB,IAAI,SAAS,MAAM,kBAAkB,YAAY,MAAM,gBAAgB,OAAO,CAAC,IAAI;EACnF,IAAI,WAAW,MAAM;GACnB,QAAQ,EAAE,CAAC,MAAM;GACjB;EACF;EACA,QAAQ,EAAE,CAAC,IAAI;EACf,MAAM,cAAc,OAAO,CAAC,CAAC,KAAK,SAAU,OAAO;GACjD,IAAI,CAAC,WAAW,QAAQ,EAAE,CAAC,YAAY,KAAK,CAAC;EAC/C,CAAC;EACD,OAAO,WAAY;GACjB,YAAY;EACd;CACF,GACA,CAAC,GAAG,CACN;CAEA,IAAI,QAAQ,MACV,OAAO,EAAE,OAAO;EAAE,WAAW;EAAc,gBAAgB;CAAQ,GAAG,EAAE,aAAa,CAAC;CAExF,IAAI,OAAO,QAAQ;CACnB,IAAI,SAAS,MACX,OAAO,EAAE,OAAO;EAAE,WAAW;EAAc,gBAAgB;CAAQ,GAAG,EAAE,eAAe,CAAC;CAC1F,IAAI,KAAK,UAAU,SACjB,OAAO,EAAE,OAAO;EAAE,WAAW;EAAc,gBAAgB;CAAQ,GAAG,UAAU,KAAK,MAAM,CAAC,CAAC;CAE/F,IAAI,QAA+B,KAAK,SAAS,CAAC;CAClD,IAAI,SAAoB,MAAM,UAAU,EAAE,MAAM,UAAU;CAC1D,IAAI,QAAmB,MAAM,SAAS,EAAE,MAAM,UAAU;CACxD,IAAI,OAAO,SAAS,WAAW,MAAM,SAAS,SAC5C,OAAO,EACL,OACA;EAAE,WAAW;EAAc,gBAAgB;CAAQ,GACnD,kBAAkB,GAAG,QAAQ,KAAK,CACpC;CAGF,IAAI,SAAS,YAAY,MAAM,CAAC;CAChC,IAAI,OAAO,QAAQ;CACnB,IAAI,OAAO,QAAQ;CAEnB,IAAI,OAAO,KAAK,SAAS,SAAY,OAAO,KAAK;CACjD,IAAI,QACF,SAAS,SACL,CACE;EAAE,KAAK;EAAU,OAAO,OAAO;EAAQ,MAAM;CAAO,GACpD;EAAE,KAAK;EAAS,OAAO,OAAO;EAAO,MAAM;CAAM,CACnD,IACA,CACE;EACE,KAAK;EACL,OAAO,SAAS,UAAU,OAAO,QAAQ,OAAO;EAChD,MAAM,SAAS,UAAU,QAAQ;CACnC,CACF;CAEN,SAAS,KAAK,OAAsC;EAClD,IAAI,OAAO,MAAM;EACjB,IAAI,OACF,KAAK,SAAS,UACV,EAAE,OAAO;GACP,WAAW;GACX,KAAK,MAAM;GAGX,OACE,SAAS,QAAQ,OAAO,KAAK,UAAU,WACnC,SACA;IAAE,OAAO,KAAK,MAAM,KAAK,QAAQ,IAAI,IAAI;IAAM,UAAU;GAAO;GACtE,KAAK,UAAU,KAAK,OAAO,aAAa,OAAO,KAAK,UAAU,EAAE;EAClE,CAAC,IACD,EAAE,OAAO,EAAE,WAAW,eAAe,GAAG,eAAe,MAAM,CAAC,CAAC;EACrE,OAAO,EACL,OACA;GAAE,WAAW;GAAgB,KAAK,MAAM;EAAI,GAC5C,EAAE,OAAO,EAAE,WAAW,gBAAgB,GAAG,MAAM,KAAK,GACpD,IACF;CACF;CAEA,SAAS,IAAI,IAAqB,OAAmC;EACnE,IAAI,SAAS,SAAS;EACtB,OAAO,EACL,UACA;GACE,MAAM;GACN,WAAW,cAAc,SAAS,oBAAoB;GACtD,gBAAgB,SAAS,SAAS;GAClC,SAAS,WAAY;IACnB,QAAQ,EAAE,CAAC,EAAE;GACf;EACF,GACA,KACF;CACF;CAGA,IAAI,cACF,OAAO,SAAS,WAChB,MAAM,SAAS,WACf,OAAO,OAAO,UAAU,YACxB,OAAO,MAAM,UAAU,aACtB,OAAO,UAAU,MAAM,SAAS,OAAO,WAAW,MAAM,UACrD,EACE,QACA,EAAE,WAAW,eAAe,GAC5B,EAAE,qBAAqB;EACrB,MAAM,EAAE,cAAc;GAAE,GAAG,OAAO,OAAO,KAAK;GAAG,GAAG,OAAO,OAAO,MAAM;EAAE,CAAC;EAC3E,IAAI,EAAE,cAAc;GAAE,GAAG,OAAO,MAAM,KAAK;GAAG,GAAG,OAAO,MAAM,MAAM;EAAE,CAAC;CACzE,CAAC,CACH,IACA;CACN,IAAI,QACF,OAAO,SAAS,WAAW,MAAM,SAAS,UACtC,EACE,QACA,EAAE,WAAW,gBAAgB,GAC7B,YAAY,OAAO,KAAK,IAAI,QAAQ,YAAY,MAAM,KAAK,CAC7D,IACA;CAIN,IAAI,eAAe,EAAE,QAAQ;EAAE,WAAW;EAAgB,gBAAgB;CAAa,GAAG;EACxF,EACE,UACA;GACE,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO,EAAE,eAAe;GACxB,gBAAgB;GAChB,SAAS,WAAY;IACnB,MAAM,QAAQ,SAAS,MAAM,EAAE,CAAC;GAClC;EACF,GACA,GACF;EACA,EACE,UACA;GACE,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO,EAAE,gBAAgB;GACzB,gBAAgB;GAChB,SAAS,WAAY;IACnB,MAAM,QAAQ,IAAI;GACpB;EACF,GACA,SAAS,OACL,EAAE,gBAAgB,IAClB,EAAE,mBAAmB,EAAE,SAAS,OAAO,KAAK,MAAM,OAAO,GAAG,CAAC,EAAE,CAAC,CACtE;EACA,EACE,UACA;GACE,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO,EAAE,cAAc;GACvB,gBAAgB;GAChB,SAAS,WAAY;IACnB,MAAM,QAAQ,SAAS,MAAM,CAAC,CAAC;GACjC;EACF,GACA,GACF;CACF,CAAC;CAED,OAAO,EACL,OACA;EAAE,WAAW;EAAgB,gBAAgB;CAAQ,GACrD,EACE,OACA,EAAE,WAAW,eAAe,GAC5B,IAAI,QAAQ,EAAE,YAAY,CAAC,GAC3B,SAAS,SACL,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,OAAO,EAAE,YAAY;EACrB,gBAAgB;EAChB,SAAS,WAAY;GACnB,QAAQ,EAAE,CAAC,SAAS,UAAU,WAAW,OAAO;EAClD;CACF,GACA,EAAE,YAAY,CAChB,IACA,MACJ,cACA,aACA,KACF,GACA,EACE,OACA,EACE,WACE,kBACC,SAAS,SAAS,wBAAwB,OAC1C,SAAS,OAAO,KAAK,uBAC1B,GACA,MAAM,IAAI,IAAI,CAChB,CACF;AACF;;;;;;;;;;;AAcA,SAAgB,aACd,MACA,KACqB;CACrB,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,KAAK,OAAO;CACxC,IAAI,UAAwB;EAAO;EAAK,MAAM,KAAK;CAAK;CACxD,IAAI,KAAK,UAAU,QAAQ,WAAW,KAAK;CAC3C,IAAI,KAAK,WAAW,YAAY,KAAK,KAAK,QAAQ,MAAM,KAAK;MACxD,QAAQ,OAAO,KAAK,SAAS,UAAU,UAAU;CACtD,OAAO;AACT;;AAGA,SAAgB,YAAY,MAAoB,GAA2B;CACzE,IAAI,KAAK,WAAW,UAClB,OAAO;EAAE,QAAQ,EAAE,oBAAoB;EAAG,OAAO,EAAE,mBAAmB;CAAE;CAC1E,IAAI,KAAK,SAAS,SAAS,OAAO;EAAE,QAAQ,EAAE,mBAAmB;EAAG,OAAO,EAAE,kBAAkB;CAAE;CACjG,OAAO;EAAE,QAAQ,EAAE,sBAAsB;EAAG,OAAO,EAAE,qBAAqB;CAAE;AAC9E;;AAGA,SAAgB,eAAe,MAAiB,GAAsB;CACpE,IAAI,KAAK,SAAS,aAChB,OAAO,EAAE,kBAAkB;EACzB,OAAO,OAAO,KAAK,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,OAAO,IAAI,CAAC;EACzF,MAAM,YAAY,KAAK,KAAK;CAC9B,CAAC;CAEH,IAAI,KAAK,SAAS,aAAa,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,KAAK,KAAK,EAAE,CAAC;CAC3F,OAAO,EAAE,eAAe;AAC1B;;;;;AAMA,SAAgB,kBAAkB,GAAc,QAAmB,OAA0B;CAC3F,IAAI,QAAQ,CAAC,QAAQ,KAAK;CAC1B,IAAI,WAAW,MAAM,OAAO,SAAU,MAAM;EAC1C,OAAO,KAAK,SAAS;CACvB,CAAC,CAAC,CAAC;CACH,IAAI,aAAa,QAAW,OAAO,eAAe,UAAU,CAAC;CAC7D,IAAI,WAAW,MAAM,OAAO,SAAU,MAAM;EAC1C,OAAO,KAAK,SAAS;CACvB,CAAC,CAAC,CAAC;CACH,IAAI,aAAa,QAAW,OAAO,eAAe,UAAU,CAAC;CAC7D,OAAO,EAAE,aAAa;AACxB;;AAKA,SAAgB,eAAe,MAAwB;CACrD,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,MAAM,KAAK,YAAY,GAAG;CAC9B,IAAI,MAAM,KAAK,QAAQ,KAAK,SAAS,GAAG,OAAO;CAC/C,OAAO,sBAAsB,QAAQ,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK;AAC7E;;;;;AAMA,SAAS,YAAY,OAAuC;CAC1D,OAAO,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,WAC5D,QACD;AACN;;;;;;;ACnTA,SAAgB,SAAS,OAAsB;CAC7C,IAAI,IAAI,MAAM;CACd,IAAI,OAAO,MAAM;CACjB,IAAI,QAAQ,MAAM;CAElB,IAAI,MAAM,MAAM;CAEhB,IAAI,YAAYC,cAAM,SAAS,KAAK;CACpC,IAAI,SAAS,UAAU;CACvB,IAAI,YAAY,UAAU;CAE1B,IAAI,WAAWA,cAAM,SAAwB,IAAI;CAEjD,IAAI,WAAW,KAAK,WAAW;CAE/B,IAAI,OAAoB,KAAK,SAAS,UAAU,UAAU;CAC1D,IAAI,OAAO,YAAY,IAAI;CAC3B,IAAI,YAAY,WAAW,QAAQ,iBAAiB,KAAK,IAAI;CAC7D,IAAI,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;CACvD,IAAI,YAAY,KAAK,aAAa,CAAC;CACnC,IAAI,OAAO,QAAQ,KAAK,OAAO,KAAK,OAAO,CAAC;CAI5C,cAAM,UACJ,WAAY;EACV,IAAI,OAAO,WAAW,yBAAuB;EAC7C,IAAI,QAAQ,OAAO,KAAK,cAAc,UAAU,KAAK,YAAY;CACnE,GACA;EAAC,KAAK;EAAM,KAAK;EAAK,KAAK;CAAI,CACjC;CAGA,cAAM,UACJ,WAAY;EACV,IAAI,OAAO,WAAW,yBAAuB;EAC7C,IAAI,CAAC,QAAQ,OAAO,KAAK,kBAAkB,YAAY;EACvD,UACE,KAAK,cAAc,+CAA4C,OAAO,IAAI,IAAI,KAAI,GAClF,WACF;CACF,GACA;EAAC;EAAM,KAAK;EAAM,KAAK;EAAK,KAAK;EAAM,UAAU;CAAM,CACzD;CAEA,SAAS,SAAe;EACtB,IAAI,CAAC,MAAM;EACX,IAAI,YAAY,OAAO,cAAc,cAAc,UAAU,YAAY;EACzE,IAAI,CAAC,aAAa,OAAO,UAAU,cAAc,YAAY;EAC7D,QAAQ,QAAQ,UAAU,UAAU,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAC9D,KAAK,WAAY;GAChB,UAAU,IAAI;GACd,IAAI,SAAS,IAAI,OAAO,aAAa,SAAS,EAAE;GAChD,SAAS,EAAE,CACT,OAAO,WAAW,WAAY;IAC5B,UAAU,KAAK;GACjB,GAAG,IAAI,CACT;EACF,CAAC,CAAC,CACD,MAAM,WAAY,CAEnB,CAAC;CACL;CAEA,SAAS,IAAI,MAAmB,OAAe,OAAmC;EAChF,IAAI,SAAS,SAAS;EACtB,OAAO,EACL,UACA;GACE,MAAM;GACN,WAAW,cAAc,SAAS,oBAAoB;GACtD,gBAAgB,SAAS,SAAS;GAC3B;GACP,SAAS,WAAY;IACnB,MAAM,YAAY,IAAI;GACxB;EACF,GACA,KACF;CACF;CAEA,IAAI,SACF,QAAQ,KAAK,WAAW,OACpB,EACE,QACA,EAAE,WAAW,iBAAiB,GAC9B,EACE,QACA,EAAE,OAAO,EAAE,OAAO,kDAAkD,EAAE,GACtE,MAAM,OAAO,KAAK,SAAS,CAC7B,GACA,EACE,QACA,EAAE,OAAO,EAAE,OAAO,gDAAgD,EAAE,GACpE,MAAM,OAAO,KAAK,SAAS,CAC7B,CACF,IACA;CAEN,IAAI;CACJ,IAAI,KAAK,UAAU,WACjB,OAAO,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,eAAe,CAAC;MAC1D,IAAI,KAAK,UAAU,SACxB,OAAO,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,UAAU,KAAK,WAAW,CAAC,CAAC;MACpE,IAAI,CAAC,MACV,OAAO,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,YAAY,CAAC;MACvD,IAAI,KAAK,WAAW,MAEzB,OAAO,EAAE,WAAW;EAAK;EAAU;EAAa;EAAW;CAAI,CAAC;MAC3D,IAAI,KAAK,WAAW,GACzB,OAAO,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,YAAY,CAAC;MACvD;EACL,IAAI,UAAU,gBAAgB,MAAM,SAAS;EAC7C,OAAO,EACL,OACA;GAAE,WAAW;GAAa,gBAAgB;EAAO,GACjD,QAAQ,IAAI,SAAU,OAAO,OAAO;GAElC,IAAI,YAAY,MAAM,SAAS,OAAO,KAAK,MAAM;GACjD,IAAI,SAAS,MAAM,IAAI,SAAS;GAChC,OAAO,EAAE,SAAS;IAChB,KAAK,OAAO,KAAK,IAAI,MAAM,MAAM,IAAI;IACrC,KAAK,MAAM;IACX,MAAM,KAAK,SAAS;IACpB,MAAM,MAAM;IACZ,QAAQ,UAAU,MAAM,SAAS,QAAQ,UAAU,QAAQ,SAAS,IAAI;IACxE,WAAW,UAAU,UAAU,QAAQ,SAAS,KAAK;IACrD,MAAM,SAAS,EAAE,iBAAiB,IAAI;IACtC,UAAU,SACN,WAAY;KACV,MAAM,WAAW,MAAM,SAAS,OAAO,SAAY,MAAM,IAAI;IAC/D,IACA;GACN,CAAC;EACH,CAAC,GACD,KAAK,cAAc,OAAO,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,EAAE,gBAAgB,CAAC,IAAI,IACxF;CACF;CAEA,OAAO,EACL,OACA,EAAE,WAAW,gBAAgB,GAC7B,EACE,OACA,EAAE,WAAW,eAAe,GAC5B,EAAE,QAAQ;EAAE,WAAW;EAAgB,OAAO,KAAK;CAAK,GAAG,KAAK,IAAI,GACpE,QACA,EAAE,QAAQ,EAAE,WAAW,cAAc,CAAC,GAGtC,YAAY,EAAE,QAAQ,EAAE,WAAW,UAAU,GAAG,EAAE,oBAAoB,CAAC,IAAI,MAC3E,WAAW,OAAO,IAAI,YAAY,EAAE,eAAe,GAAG,EAAE,eAAe,CAAC,GACxE,YAAY,YAAY,OAAO,IAAI,SAAS,EAAE,YAAY,GAAG,EAAE,YAAY,CAAC,GAC5E,EACE,UACA;EACE,MAAM;EACN,WAAW,cAAc,KAAK,SAAS,OAAO,oBAAoB;EAClE,gBAAgB,KAAK,SAAS,OAAO,SAAS;EAC9C,OAAO,EAAE,WAAW;EACpB,SAAS,WAAY;GACnB,MAAM,eAAe;EACvB;CACF,GACA,EAAE,WAAW,CACf,GACA,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,OAAO,EAAE,WAAW;EACpB,SAAS;CACX,GACA,SAAS,EAAE,aAAa,IAAI,EAAE,WAAW,CAC3C,GACA,EACE,UACA;EACE,MAAM;EACN,WAAW;EAEX,OAAO,WAAW,EAAE,cAAc,IAAI,EAAE,YAAY;EACpD,cAAc,WAAW,EAAE,cAAc,IAAI,EAAE,YAAY;EAC3D,SAAS,WAAY;GACnB,MAAM,YAAY;EACpB;CACF,GACA,GACF,CACF,GACA,IACF;AACF;;;;;;;AAQA,SAAgB,QAAQ,OAAyC;CAC/D,IAAI,MAAM,MAAM;CAChB,IAAI,QACF,IAAI,SAAS,QACT,EAAE,YAAY,YAAY,IAC1B,IAAI,SAAS,QACX,EAAE,YAAY,YAAY,IAC1B;CACR,IAAI,cAAc,EAAE,OAAO,kBAAkB;CAC7C,IAAI,OAAO,IAAI,SAAS,KAAK,SAAW,IAAI;CAE5C,IAAI,IAAI,SAAS,QACf,OAAO,EACL,OACA;EACE,WAAW,kCAAkC,MAAM,SAAS,4BAA4B;EACxF,gBAAgB;EAChB,mBACE,MAAM,SAAS,QAAQ,MAAM,SAAS,SAAY,SAAY,OAAO,MAAM,IAAI;EACjF,OAAO,MAAM;EACb,SAAS,MAAM;CACjB,GACA,EAAE,QAAQ;EAAE,WAAW;EAAc,OAAO;CAAY,GAAG,EAAE,GAC7D,EACE,QACA;EAAE,WAAW;EAA4B,OAAO;CAAY,GAC5D,MAAM,YAAY,MAAM,GAC1B,GACA,EAAE,QAAQ,EAAE,WAAW,kBAAkB,MAAM,OAAO,eAAe,IAAI,GAAG,IAAI,CAClF;CAGF,OAAO,EACL,OACA;EAAE,WAAW,6BAA6B,IAAI;EAAa;CAAM,GACjE,EACE,QACA;EAAE,WAAW;EAAc,OAAO;CAAY,GAC9C,IAAI,YAAY,OAAO,KAAK,OAAO,IAAI,OAAO,CAChD,GACA,EACE,QACA;EAAE,WAAW;EAAc,OAAO;CAAY,GAC9C,IAAI,YAAY,OAAO,KAAK,OAAO,IAAI,OAAO,CAChD,GACA,EAAE,QAAQ,EAAE,WAAW,kBAAkB,MAAM,OAAO,eAAe,IAAI,GAAG,IAAI,CAClF;AACF;;;;;;;;;;AAWA,SAAgB,gBAAgB,MAAsB,WAAyC;CAC7F,IAAI,SAAS,aAAa,CAAC;CAC3B,IAAI,MAA0B,CAAC;CAC/B,IAAI,UAAU;CACd,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,IAAI,MAAM,KAAK;EACf,IAAI,IAAI,SAAS,QAAQ;GACvB,WAAW;GACX,IAAI,KAAK;IAAO;IAAK,MAAM;GAAQ,CAAC;GACpC;EACF;EACA,IAAI,WAAW,KAAK,OAAO,QAAQ,OAAO,KAAK,GAAG;EAClD,IAAI,KAAK;GAAO;GAAK,MAAM,WAAW,IAAI,UAAU;EAAK,CAAC;CAC5D;CACA,OAAO;AACT;;AAGA,SAAgB,WAAW,MAA8B;CACvD,OAAO,KACJ,IAAI,SAAU,KAAK;EAClB,IAAI,IAAI,SAAS,OAAO,OAAO,MAAM,IAAI;EACzC,IAAI,IAAI,SAAS,OAAO,OAAO,MAAM,IAAI;EACzC,IAAI,IAAI,SAAS,OAAO,OAAO,MAAM,IAAI;EACzC,OAAO,IAAI;CACb,CAAC,CAAC,CACD,KAAK,IAAI;AACd;;AAGA,SAAS,kBAAkB,SAAsD;CAC/E,OAAO,cAAc,WAAW,WAAW;AAC7C;;AAGA,SAAS,iBAAiB,SAAsC;CAC9D,OAAO,YAAY,QAAQ,kBAAkB,OAAO,IAAI,QAAQ,cAAc,OAAO;AACvF;;AAGA,SAAgB,YAAY,MAAwD;CAClF,IAAI,CAAC,QAAQ,CAAC,KAAK,MAAM,OAAO;CAEhC,IAAI,KAAK,WAAW,UAAU,OAAO,KAAK;CAE1C,IAAI,CAAC,kBAAkB,KAAK,IAAI,GAAG,OAAO;CAC1C,OAAO,KAAK,KAAK,KAAK,SAAS,UAAU,UAAU,eAAe;AACpE;;;;;;;;AC9UA,SAAgB,QAAQ,OAAgD;CAGtE,IAAI,OAAO,MAAM;CACjB,IAAI,IAAI,MAAM;CAGd,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO;CACjC,IAAI,KAAK,WAAW,GAClB,OAAO,EACL,OACA,EAAE,WAAW,eAAe,GAC5B,EAAE,OAAO,EAAE,WAAW,oBAAoB,GAAG,EAAE,YAAY,CAAC,GAC5D,EAAE,OAAO;EAAE,WAAW;EAAa,mBAAmB;CAAI,GAAG,EAAE,iBAAiB,CAAC,CACnF;CAEF,IAAI,UAAU,MAAM;CAEpB,OAAO,EACL,OACA,EAAE,WAAW,eAAe,GAC5B,EAAE,OAAO,EAAE,WAAW,oBAAoB,GAAG,EAAE,YAAY,CAAC,GAC5D,EACE,OACA,EAAE,WAAW,eAAe,GAC5B,KAAK,IAAI,SAAU,KAAK,OAAO;EAC7B,IAAI,SACF,OAAO,YAAY,YACnB,QAAQ,SAAS,MAChB,IAAI,QAAQ,WAAW,QAAQ,QAAQ,IAAI,GAAG,MAAM;EACvD,IAAI,MAAM;GACR,EAAE,WAAW;GACb;GACA,IAAI,WAAW;GACf,EAAE,IAAI,YAAY,kBAAkB,iBAAiB,IACnD,QACA,IAAI,MACJ,SACC,OAAO,IAAI,gBAAgB,WAAW,SAAS,IAAI,WAAW,IAAI,IAAI,QAAQ;EACnF,CAAC,CAAC,KAAK,IAAI;EACX,IAAI,OAAO,WAAW,IAAI,KAAK,MAAM,SAAS;EAC9C,OAAO,EACL,UACA;GACE,MAAM;GACN,WAAW,iBAAiB,OAAO,uBAAuB;GAC1D,gBAAgB,IAAI;GACpB,KAAK,IAAI,OAAO,MAAM,OAAO,KAAK;GAClC,OAAO;GACP,SAAS,WAAY;IACnB,IAAI,MAAM,QAAQ,MAAM,OAAO,GAAG;GACpC;EACF,GACA,EAAE,QAAQ,EAAE,WAAW,YAAY,GAAG,IAAI,IAAI,GAC9C,EAAE,QAAQ,EAAE,WAAW,YAAY,GAAG,IAAI,GAAG,GAC7C,SAAS,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,MAAM,IAAI,MAC1D,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,WAAW,KAAK,CAAC,CAAC,CAC3D;CACF,CAAC,CACH,GACA,MAAM,cAAc,OAChB,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,EAAE,oBAAoB,EAAE,GAAG,OAAO,KAAK,MAAM,EAAE,CAAC,CAAC,IACtF,IACN;AACF;;;;;;;;;;;;;;;;;;;;ACrEA,SAAgB,aACd,OACA,WACA,OACM;CACN,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI,YAAY,SAAU,OAAsB;GAE9C,IAAI,MAAM,QAAQ,UAAU;IAE1B,IAAI,UAAU,MAAM,YAAY;IAChC,IAAI,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,YAAY;SACjD,MAAM,UAAU,KAAK;IAC1B;GACF;GAKA,IAAI,CAAC,MAAM,UAAU,CAAC,cAAc,cAAc,CAAC,GAAG;GAEtD,IAAI,eAAe,MAAM,MAAM,GAAG;GAElC,IAAI,MAAM,WAAW,MAAM,WAAW,MAAM,QAAQ;GAEpD,IAAI,UAAU;GACd,IAAI,MAAM,QAAQ,aAAa,MAAM,SAAS,GAAG,KAAK;QACjD,IAAI,MAAM,QAAQ,WAAW,MAAM,SAAS,IAAI,KAAK;QACrD,IAAI,MAAM,QAAQ,KAAK,MAAM,SAAS,GAAG,IAAI;QAC7C,IAAI,MAAM,QAAQ,KAAK,MAAM,SAAS,IAAI,IAAI;QAC9C,IAAI,MAAM,QAAQ,SAAS,MAAM,WAAW;QAC5C,IAAI,MAAM,QAAQ,KAAK,MAAM,SAAS,CAAC;QACvC,IAAI,MAAM,QAAQ,KAAK,MAAM,SAAS,EAAE;QACxC,IAAI,MAAM,QAAQ,OAAO,MAAM,SAAS,SAAS;IACpD,IAAI,WAAW,MAAM,YAAY,CAAC,CAAC;IACnC,MAAM,WAAW,WAAW,SAAS,OAAO,CAAC;GAC/C,OAAO,IAAI,MAAM,QAAQ,KAAK,MAAM,QAAQ;QAGvC,IAAI,MAAM,QAAQ,KAAK;IAC1B,IAAI,WAAW,MAAM,YAAY;IACjC,IAAI,OAAO,kBAAkB,QAAQ;IACrC,IAAI,SACF,SAAS,WAAW,QAAQ,SAAS,WAAW,SAAY,SAAS,SAAS;IAChF,IAAI,WAAW,MAAM,SAAS,KAAK,KAAK,KAAK,EAAE,CAAC,OAAO;IACvD,IAAI,WAAW,MAAM,MAAM,eAAe,MAAM;GAClD,OAAO,UAAU;GAEjB,IAAI,SAAS;IAEX,MAAM,eAAe;IACrB,MAAM,gBAAgB;GACxB;EACF;EAEA,IAAI,gBAAgB,SAAU,OAAqB;GACjD,MAAM,SAAS,cAAc,MAAM,MAAM;EAC3C;EAEA,OAAO,iBAAiB,WAAW,WAAW,IAAI;EAClD,OAAO,iBAAiB,eAAe,eAAe,IAAI;EAC1D,OAAO,WAAY;GACjB,OAAO,oBAAoB,WAAW,WAAW,IAAI;GACrD,OAAO,oBAAoB,eAAe,eAAe,IAAI;EAC/D;CACF,GACA,CAAC,WAAW,KAAK,CACnB;AACF;;AAGA,SAAgB,cAAc,OAAiB,KAA4B;CACzE,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,KAAK,OAAO;EACjB,IAAI,WAAW,WAAY;GACzB,IAAI,OAAO,SAAS,IAAI,MAAM,IAAI,KAAK,aAAa,KAAK,CAAC;GAC1D,IAAI,KAAK,SAAS,IAAI,QAAQ,KAAK,QAAQ,IAAI,KAAK,MAAM,UAAU,IAAI;EAC1E;EACA,OAAO,iBAAiB,UAAU,QAAQ;EAC1C,OAAO,WAAY;GACjB,OAAO,oBAAoB,UAAU,QAAQ;EAC/C;CACF,GACA,CAAC,KAAK,KAAK,CACb;AACF;;AAGA,SAAgB,cACd,OACA,WACA,WACA,QACM;CACN,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI,UAAU,WAAY;GACxB,MAAM,aAAa,eAAe,CAAC;EACrC;EACA,QAAQ;EACR,OAAO,iBAAiB,UAAU,OAAO;EACzC,OAAO,WAAY;GACjB,OAAO,oBAAoB,UAAU,OAAO;EAC9C;CACF,GACA;EAAC;EAAW;EAAW;EAAQ;CAAK,CACtC;AACF;;;;;;;;;ACxEA,SAAgB,iBAAiB,OAAyD;CACxF,IAAI,IACF,MAAM,KACN,SAAU,KAAa;EACrB,OAAO;CACT;CACF,IAAI,QAAQ,MAAM;CAClB,IAAI,QAAQ,SAAS,KAAK;CAE1B,IAAI,UAAUC,cAAM,SAA2B,IAAI;CACnD,IAAI,OAAO,QAAQ;CACnB,IAAI,UAAU,QAAQ;CAEtB,IAAI,UAAUA,cAAM,SAA2B,IAAI;CAWnD,IAAI,QAHaA,cAAM,SAAS,WAAY;EAC1C,OAAO,EAAE,QAAQ,MAAM;CACzB,CACqB,CAAC,CAAC;CACvB,aAAa,OAAO,MAAM,WAAW,KAAK;CAC1C,cAAc,OAAO,MAAM,GAAG;CAC9B,cAAc,OAAO,MAAM,WAAW,MAAM,WAAW,MAAM,MAAM;CACnE,IAAI,OAAO,QAAQ;CACnB,IAAI,UAAU,QAAQ;;;;;;;CAQtB,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,MAAM,OAAO;EAElB,MAAM,aAAa;EACnB,IAAI,QAAQ;EACZ,IAAI,SAAS,SAAU,OAAqB;GAC1C,IAAI,MAAM,cAAc,WAAW,WAAW;GAC9C,IACE,CAAC,SACD,KAAK,IAAI,MAAM,UAAU,WAAW,MAAM,SAC1C,KAAK,IAAI,MAAM,UAAU,WAAW,MAAM,OAE1C;GACF,QAAQ;GACR,MAAM,UACJ,SACE,MAAM,UAAU,WAAW,SAC3B,MAAM,UAAU,WAAW,SAC3B,aAAa,KAAK,CACpB,CACF;EACF;EACA,IAAI,SAAS,SAAU,OAAqB;GAC1C,IAAI,MAAM,cAAc,WAAW,WAAW;GAC9C,QAAQ,IAAI;GAEZ,IAAI,OAAO;IAET,IAAI,OAAO,MAAM,YAAY,CAAC,CAAC;IAC/B,IAAI,SAAS,MAAM,MAAM,YAAY,IAAI;GAC3C;EACF;EACA,OAAO,iBAAiB,eAAe,MAAM;EAC7C,OAAO,iBAAiB,aAAa,MAAM;EAC3C,OAAO,iBAAiB,iBAAiB,MAAM;EAC/C,OAAO,WAAY;GACjB,OAAO,oBAAoB,eAAe,MAAM;GAChD,OAAO,oBAAoB,aAAa,MAAM;GAC9C,OAAO,oBAAoB,iBAAiB,MAAM;EACpD;CACF,GACA,CAAC,MAAM,KAAK,CACd;;;;;CAMA,cAAM,UACJ,WAAY;EACV,IAAI,CAAC,MAAM,OAAO;EAElB,MAAM,aAAa;EACnB,IAAI,SAAS,SAAU,OAAqB;GAC1C,IAAI,MAAM,cAAc,WAAW,WAAW;GAC9C,IAAI,WAAW,SAAS,UACtB,MAAM,eAAe,WAAW,cAAc,MAAM,UAAU,WAAW,OAAO;QAC7E,MAAM,aAAa,WAAW,cAAc,MAAM,UAAU,WAAW,OAAO;EACrF;EACA,IAAI,SAAS,SAAU,OAAqB;GAC1C,IAAI,MAAM,cAAc,WAAW,WAAW;GAC9C,QAAQ,IAAI;GACZ,MAAM,cAAc;EACtB;EACA,OAAO,iBAAiB,eAAe,MAAM;EAC7C,OAAO,iBAAiB,aAAa,MAAM;EAC3C,OAAO,iBAAiB,iBAAiB,MAAM;EAC/C,OAAO,WAAY;GACjB,OAAO,oBAAoB,eAAe,MAAM;GAChD,OAAO,oBAAoB,aAAa,MAAM;GAC9C,OAAO,oBAAoB,iBAAiB,MAAM;EACpD;CACF,GACA,CAAC,MAAM,KAAK,CACd;CAEA,IAAI,CAAC,MAAM,WAAW,OAAO;;;;;;;;CAS7B,SAAS,kBACP,MACA,OACM;EACN,IAAI,MAAM,WAAW,GAAG;EACxB,IAAI,WAAW,MAAM,YAAY;EACjC,MAAM,eAAe;EACrB,IAAI,QACF,SAAS,WACL,OAAO,SAAS,gBAAgB,WAC9B,SAAS,oBAEX,OAAO,SAAS,cAAc,WAC5B,SAAS;EAEjB,QAAQ;GAAQ;GAAM,WAAW,MAAM;GAAW,QAAQ,MAAM;GAAS,YAAY;EAAM,CAAC;CAC9F;CAEA,SAAS,oBAAoB,OAAiD;EAC5E,IAAI,MAAM,WAAW,GAAG;EAExB,IAAI,SAAS,MAAM;EAEnB,IAAI,UAAU,OAAO,OAAO,YAAY,cAAc,OAAO,QAAQ,QAAQ,GAAG;EAEhF,IAAI,OAAQ,MAAM,cAAc,WAA2B,sBAAsB;EACjF,QAAQ;GACN,WAAW,MAAM;GACjB,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,SAAS,MAAM,UAAU,KAAK;GAC9B,SAAS,MAAM,UAAU,KAAK;EAChC,CAAC;CACH;CAEA,SAAS,sBAA4B;EACnC,MAAM,WAAW;CACnB;CAEA,IAAI,SAAS,YAAY,KAAK;CAC9B,IAAI,WAAW,OAAO;CAGtB,IAAI,aAAa,OAAO;CACxB,IAAI,OAAO,YAAY;CACvB,IAAI,UAAU,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;CAEtE,IAAI,eACF,YAAY,aAER,KAAK,OAAqB,OAAO,cAAc,WAAwB;CAE7E,IAAI,aAAkC;EACpC,UAAU;EACV,QAAQ;EACR,WAAW;EACX,OAAO,cAAc,QAAQ,MAAM,WAAW,MAAM,WAAW;EAC/D,UAAU;EACV,SAAS;EACT,eAAe;EACf,QAAQ;EACR,cAAc;EAGd,YAAY;EACZ,WAAW;EACX,UAAU;EACV,eAAe;CACjB;CAEA,IAAI,MAAM;EAER,IAAI,cACD,YAAY,OAAO,OAAO,cAAc,eAAe;EAE1D,WAAW,SAAS,KAAK,IACvB,KACA,KAAK,IAAI,KAAK,MAAM,OAAO,cAAc,oBAAoB,GAAG,UAAU,CAC5E;CACF,OACE,WAAW,YAAY;CAIzB,IAAI,MAAM,KAAK;EACb,WAAW,OAAO,MAAM,IAAI;EAC5B,WAAW,MAAM,MAAM,IAAI;CAC7B,OAAO;EACL,WAAW,OAAO;EAClB,WAAW,SAAS;EACpB,WAAW,YAAY;CACzB;CAOA,IAAI,WAAW,MAAM;CACrB,IAAI,aAAa,MAAM;CAEvB,IAAI,WAAW,aAAa,QAAQ,SAAS,WAAW,WAAW,SAAS,OAAO;CACnF,IAAI,iBAAiB,aAAa,QAAQ,SAAS,WAAW,WAAW,SAAS,OAAO;CAGzF,IAAI,cAAc,kBAAkB,KAAK;CACzC,IAAI,cAAc,kBAAkB,KAAK;CAEzC,IAAI;CACJ,IAAI,MAAM,UAAU,SAClB,YAAY,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,UAAU,MAAM,WAAW,CAAC,CAAC;MAC1E,IAAI,CAAC,MAAM,KAChB,YAAY,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,iBAAiB,CAAC;MACjE,IAAI,MAAM,UAAU,aAAa,CAAC,MAAM,QAC7C,YAAY,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,eAAe,CAAC;MAEpE,YAAY,EAAE,OAAO,EAAE,WAAW,aAAa,GAAG,EAAE,eAAe,CAAC;CAGtE,IAAI;CACJ,IAAI,MAAM,UAAU,MAAM,OAAO,MAAM;EACrC,IAAI,SAAS,MAAM;EACnB,IAAI,eAAkC;GACpC,EACE,OACA,EAAE,WAAW,YAAY,GACzB,EAAE,QAAQ;IAAE,WAAW;IAAa,OAAO,OAAO;GAAK,GAAG,OAAO,IAAI,GACrE,OAAO,WACH,EAAE,QAAQ,EAAE,WAAW,UAAU,GAAG,EAAE,gBAAgB,IAAI,MAAM,OAAO,QAAQ,IAC/E,MACJ,OAAO,QAAQ,IACX,EAAE,QAAQ,EAAE,WAAW,UAAU,GAAG,OAAO,EAAE,aAAa,IAAI,OAAO,OAAO,KAAK,CAAC,IAClF,MACJ,OAAO,SAAS,IACZ,EAAE,QAAQ,EAAE,WAAW,UAAU,GAAG,OAAO,EAAE,cAAc,IAAI,OAAO,OAAO,MAAM,CAAC,IACpF,MACJ,OAAO,MAAM,EAAE,QAAQ,EAAE,WAAW,UAAU,GAAG,OAAO,GAAG,IAAI,IACjE;GACA,EAAE,OAAO,EAAE,WAAW,oBAAoB,GAAG,EAAE,eAAe,CAAC;GAC/D,EAAE,YAAY;IAAE,QAAQ,OAAO;IAAW;GAAE,CAAC;GAC7C,EAAE,oBAAoB;IACjB;IACH,MAAM;IACN,aAAa,MAAM,gBAAgB;IACnC,UAAU,WAAY;KACpB,MAAM,kBAAkB;IAC1B;IACA,YAAY,MAAM,eAAe;IACjC,gBAAgB,WAAY;KAC1B,MAAM,iBAAiB;IACzB;IACA,QAAQ,WAAW,KAAK;IACxB,iBAAiB,WAAY;KAC3B,MAAM,cAAc;IACtB;GACF,CAAC;GAGD,YAAY,SAAS,IACjB,EAAE,YAAY;IACZ,SAAS;IACT,WAAW,OAAO;IACf;IACH,QAAQ,SAAU,QAAmB;KACnC,MAAM,SAAS,MAAM;IACvB;IACA,YAAY;IACZ,QAAQ,aAAa,OAAO,MAAM;IAClC,QAAQ;IACK;IACb,QAAQ,WAAW,KAAK;IACxB,kBAAkB,SAAU,MAAc;KACxC,MAAM,eAAe,IAAI;IAC3B;GACF,CAAC,IACD,MAAM,OAAO,QAAQ,SAAS,KAAK,MAAM,gBAAgB,QAAQ,gBAAgB,OAE/E,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,EAAE,eAAe,CAAC,IACvD;GACN,EAAE,YAAY;IACZ,SAAS,MAAM;IACZ;IACH,WAAW,cAAc,eAAe,OAAO,WAAW,MAAM;IAEhE,QAAQ,SAAU,QAAqB;KACrC,MAAM,aAAa,MAAM;IAC3B;GACF,CAAC;GACD,EAAE,SAAS;IACT,MAAM,MAAM;IACZ,WAAW,MAAM;IACd;IACH,SAAS,MAAM,UAAU,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM;IAC/D,WAAW,cAAc,eAAe,OAAO,WAAW,MAAM;IAEhE,QAAQ,SAAU,KAAa;KAC7B,MAAM,aAAa;MACjB,KAAK,IAAI;MACT,SAAS,IAAI,WAAW;MACxB,QAAQ;MACR,MAAM,IAAI,QAAQ;MAClB,aAAa,OAAO,IAAI,gBAAgB,WAAW,IAAI,cAAc;MACrE,MAAM,CAAC,IAAI,IAAI;KACjB,CAAC;IACH;GACF,CAAC;EACH;EACA,WAAW,EAAE,OAAO,EAAE,WAAW,YAAY,GAAG,GAAG,YAAY;CACjE,OACE,WAAW;CAMb,IAAI,OAAO,OACP,EACE,OACA,EAAE,WAAW,iBAAiB,GAC9B,EAAE,OAAO;EAAE,WAAW;EAAiB,OAAO,EAAE,WAAmB;CAAE,GAAG,QAAQ,GAChF,cAAc,eAAe,OACzB,EAAE,YAAY;EACT;EACH,QAAQ;EACR,YAAY,SAAU,MAAM;GAC1B,MAAM,eAAe,IAAI;EAC3B;EACA,YAAY;EACZ,QAAQ,MAAM;EACd,OAAO,OAAO,MAAM,gBAAgB,WAAW,MAAM;CACvD,CAAC,IACD,MACJ,cAAc,WACV,EAAE,OAAO;EACP,WAAW;EACX,gBAAgB;EAChB,OAAO,EAAE,mBAAmB;EAC5B,eAAe,SAAU,OAA2C;GAClE,kBAAkB,UAAU,KAAK;EACnC;CACF,CAAC,IACD,MACJ,YAAY,aAAa,OACrB,EAAE,UAAU;EAAK;EAAG,MAAM;EAAiB;EAAO,KAAK,MAAM;CAAI,CAAC,IAClE,IACN,IACA;CAEJ,OAAO,EACL,OACA;EAAE,WAAW;EAAc,gBAAgB;EAAS,OAAO;CAAW,GACtE,EACE,OACA;EACE,WAAW;EACX,OAAO,EAAE,gBAAgB;EACzB,eAAe,SAAU,OAA2C;GAClE,oBAAoB,KAAK;EAC3B;EACA,eAAe;CACjB,GACA,EAAE,QAAQ,EAAE,WAAW,aAAa,GAAG,EAAE,aAAa,CAAC,GACvD,MAAM,UAAU,MAAM,OAAO,OACzB,EAAE,QAAQ,EAAE,WAAW,cAAc,GAAG,YAAY,MAAM,QAAQ,CAAC,CAAC,IACpE,MACJ,EAAE,QAAQ,EAAE,WAAW,cAAc,CAAC,GACtC,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,OAAO,EAAE,eAAe;EACxB,cAAc,EAAE,eAAe;EAC/B,SAAS,WAAY;GACnB,MAAM,QAAQ;EAChB;CACF,GACA,GACF,GACA,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,OAAO,EAAE,aAAa;EACtB,cAAc,EAAE,aAAa;EAC7B,SAAS,WAAY;GACnB,MAAM,WAAW;EACnB;CACF,GACA,GACF,GACA,EACE,UACA;EACE,MAAM;EACN,WAAW;EACX,OAAO,EAAE,aAAa;EACtB,cAAc,EAAE,aAAa;EAC7B,SAAS,WAAY;GACnB,MAAM,UAAU,KAAK;EACvB;CACF,GACA,GACF,CACF,GACA,MACA,WACI,EAAE,OAAO;EACP,WAAW;EACX,gBAAgB;EAChB,OAAO,EAAE,aAAa;EACtB,eAAe,SAAU,OAA2C;GAClE,kBAAkB,QAAQ,KAAK;EACjC;CACF,CAAC,IACD,aAEE,EAAE,OAAO;EACP,WAAW;EACX,gBAAgB;EAChB,OAAO,EAAE,mBAAmB;EAC5B,eAAe,SAAU,OAA2C;GAClE,kBAAkB,UAAU,KAAK;EACnC;CACF,CAAC,IACD,MACN,EACE,OACA,EAAE,WAAW,cAAc,GAC3B,EAAE,QAAQ,MAAM,EAAE,gBAAgB,CAAC,GACnC,EAAE,QAAQ,MAAM,MAAM,YAAY,EAAE,eAAe,IAAI,UAAU,MAAM,SAAS,IAAI,EAAE,CACxF,CACF;AACF;;;;;;;;;;;AChfA,SAAgB,QAAQ,OAAgD;CACtE,IAAI,QAAQ,MAAM;CAElB,IAAI,MADQ,SAAS,KACP,CAAC,CAAC;CAEhB,IAAI,QAAQ,QAAQ,QAAQ,QAAW,OAAO;CAC9C,IAAI,aAAa,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;CAC7E,IAAI,cAAc,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;CAChF,IAAI,SAAS;CACb,IAAI,OAAO,KAAK,IACd,QACA,KAAK,IAAI,OAAO,IAAI,KAAK,SAAS,WAAW,IAAI,KAAK,OAAO,QAAQ,aAAa,GAAG,CACvF;CACA,IAAI,SAAS,KAAK,IAChB,QACA,eAAe,OAAO,IAAI,KAAK,QAAQ,WAAW,IAAI,KAAK,MAAM,KAAK,CACxE;CAEA,OAAO,EACL,OACA;EACE,WAAW;EACX,gBAAgB;EAChB,MAAM;EACN,OAAO;GAAE,MAAM,OAAO,IAAI,IAAI;GAAM,QAAQ,OAAO,MAAM,IAAI;EAAK;CACpE,GACA,IAAI,IACN;AACF;;;;;;;;;;;;;;;;;AChBA,SAAgB,mBAAmB,OAA2D;CAC5F,IAAI,IACF,MAAM,KACN,SAAU,KAAa;EACrB,OAAO;CACT;CACF,IAAI,YAAY,MAAM;CACtB,IAAI,QAAQ,MAAM;CAClB,IAAI,QAAQ,SAAS,KAAK;CAC1B,IAAI,CAAC,WAAW,OAAO;CACvB,IAAI,MAAM,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,KAAK,KAAK,aAAa;CACvE,IAAI,UAAU,aAAa,GAAG;CAC9B,IAAI,YAAY,MAAM,OAAO;CAE7B,IAAI;CACJ,IAAI,QAAQ,IAAI,SAAS,MAAM;EAC7B,OAAO,YAAY,QAAQ,KAAK,CAAC;EACjC,IAAI,QAAQ,UAAU,MAAM,QAAQ;EACpC,IAAI,UAAU,QAAQ,IAAI;EAC1B,IAAI,WAAW,QAAQ,IAAI;EAC3B,IAAI,OAAO,YAAY,YAAY,UAAU,GAAG,QAAQ,OAAO,OAAO,OAAO;EAC7E,IAAI,OAAO,aAAa,YAAY,WAAW,GAAG,QAAQ,OAAO,OAAO,QAAQ;EAChF,IAAI,SAAS,QAAQ,WAClB,IAAI,SAAU,MAAM;GACnB,OAAO,EAAE,WAAW,KAAK,GAAG,IAAI,MAAM,OAAO,KAAK,KAAK;EACzD,CAAC,CAAC,CACD,KAAK,GAAG;EACX,QAAQ,WAAW,KAAK,QAAQ,EAAE,aAAa,IAAI,QAAQ;CAC7D,OAAO,IAAI,QAAQ,IAAI,UAAU,MAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,QAAQ,IAAI,KAAK,EAAE,CAAC;MAE1D,OAAO,EAAE,aAAa;CAGxB,IAAI,MAAgB,CAAC;CACrB,IAAI,QAAQ,OAAO,GAAG,IAAI,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,QAAQ,IAAI,EAAE,CAAC,CAAC;CAC7E,IAAI,QAAQ,WAAW,GACrB,IAAI,KAAK,EAAE,iBAAiB;EAAE,GAAG,OAAO,QAAQ,QAAQ;EAAG,KAAK,OAAO,QAAQ,WAAW;CAAE,CAAC,CAAC;CAEhG,IAAI,UAAU,IAAI,WAAW,IAAI,EAAE,cAAc,IAAI,IAAI,KAAK,KAAK;CAEnE,OAAO,EACL,OACA;EACE,WAAW;EACX,gBAAgB;EAChB,OAAO,QAAQ,IAAI,OAAO;CAC5B,GACA,CACE,EAAE,OAAO;EAAE,KAAK;EAAO,WAAW;CAAuB,GAAG,IAAI,GAChE,EAAE,OAAO;EAAE,KAAK;EAAO,WAAW;CAAuB,GAAG,OAAO,CACrE,CACF;AACF;;;;;;;;AASA,SAAgB,oBAAoB,OAA4D;CAC9F,IAAI,IACF,MAAM,KACN,SAAU,KAAa;EACrB,OAAO;CACT;CACF,IAAI,YAAY,MAAM;CACtB,IAAI,QAAQ,MAAM;CAClB,IAAI,QAAQ,SAAS,KAAK;CAC1B,IAAI,CAAC,WAAW,OAAO;CACvB,IAAI,MAAM,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,KAAK,KAAK,aAAa;CACvE,IAAI,UAAU,aAAa,GAAG;CAC9B,IAAI,YAAY,MAAM,OAAO;CAE7B,IAAI,UAAU,QAAQ;CACtB,IAAI,YAAY,QAAQ;CACxB,IAAI,gBAAgB,QAAQ;CAC5B,IAAI,aAAa,QAAQ;CAEzB,IAAI,QAAkB,CAAC;CACvB,IAAI,QAAQ,SAAS,MAAM;EACzB,IAAI,OAAO,YAAY,SAAS,CAAC;EACjC,IAAI,QAAQ,UAAU,MAAM,QAAQ;EACpC,IAAI,UAAU,QAAQ;EACtB,IAAI,WAAW,QAAQ;EACvB,IAAI,OAAO,YAAY,YAAY,UAAU,GAAG,QAAQ,OAAO,OAAO,OAAO;EAC7E,IAAI,OAAO,aAAa,YAAY,WAAW,GAAG,QAAQ,OAAO,OAAO,QAAQ;EAChF,IAAI,WAAW,SAAS,GACtB,QACE,QACA,WACG,IAAI,SAAU,MAAM;GACnB,OAAO,EAAE,WAAW,KAAK,GAAG,IAAI,MAAM,OAAO,KAAK,KAAK;EACzD,CAAC,CAAC,CACD,KAAK,GAAG;OAGb,QAAQ,QAAQ,EAAE,aAAa;EAEjC,MAAM,KAAK,IAAI;CACjB,OAAO,IAAI,QAAQ,SAAS,SAAS,QAAQ,UAAU,MACrD,MAAM,KAAK,EAAE,aAAa,CAAC;CAE7B,IAAI,QAAQ,OAAO,MAAM,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,QAAQ,KAAK,EAAE,CAAC,CAAC;CAC9E,IAAI,MAAgB,CAAC;CACrB,IAAI,YAAY,GAAG,IAAI,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,SAAS,EAAE,CAAC,CAAC;CACvE,IAAI,gBAAgB,GAClB,IAAI,KAAK,EAAE,iBAAiB;EAAE,GAAG,OAAO,aAAa;EAAG,KAAK,OAAO,QAAQ,WAAW;CAAE,CAAC,CAAC;CAG7F,MAAM,KAAK,IAAI,WAAW,IAAI,EAAE,cAAc,IAAI,IAAI,KAAK,KAAK,CAAC;CACjE,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO,EACL,OACA;EACE,WAAW;EACX,gBAAgB;CAClB,GACA,MAAM,IAAI,SAAU,MAAM,OAAO;EAC/B,OAAO,EAAE,OAAO;GAAE,KAAK,OAAO,KAAK;GAAG,WAAW;EAAoB,GAAG,IAAI;CAC9E,CAAC,CACH;AACF;;;;;;;;;;;;;;ACxFA,SAAgB,WAAW,OAAmD;CAC5E,IAAI,IAAI,MAAM;CACd,IAAI,QAAQ,MAAM;CAClB,IAAI,YAAY,MAAM;CACtB,IAAI,cAAc,MAAM;CACxB,IAAI,WAAW,MAAM;CACrB,IAAI,aAAa,aAAa,MAAM,IAAI;CAGxC,IAAI,WACF,OAAO,gBAAgB,aACnB,YAAY,SAAU,UAAU;EAC9B,OAAO;CACT,CAAC,IACD;CACN,IAAI,UACF,OAAO,cAAc,YAAY,YAAY,SAAS,OAClD,SAAS,KAAK,aACd;CACN,IAAI,MAAM,UAAU,QAAQ,MAAM;CAElC,IAAI,QAAQ,SAAS,KAAK;CAG1B,IAAI,OADOC,cAAM,SAAS,CACZ,CAAC,CAAC;CAGhB,cAAM,UACJ,WAAY;EACV,IAAI,cAAc,UAAa,QAAQ,QAAW,OAAO;EACzD,MAAM,YAAY,WAAW,GAAG;EAChC,IAAI,MAAM,mBAAmB,UAAa,MAAM,eAAe,UAAU,QACvE,MAAM,mBAAmB;CAE7B,GACA,CAAC,WAAW,GAAG,CACjB;CAEA,IAAI,SAAS,eAAe,MAAM,gBAAgB,UAAU;CAC5D,IAAI,QAAQ,OAAO,MAAM,MAAM,GAAG,CAAC;CACnC,IAAI,OAAO,MACR,IAAI,SAAU,OAAO;EACpB,OAAO,MAAM;CACf,CAAC,CAAC,CACD,KAAK,IAAQ;CAEhB,cAAM,UACJ,WAAY;EACV,IAAI,SAAS,MAAM,OAAO,QAAQ,UAAU,OAAO;EAEnD,MAAM,QAAQ;EACd,IAAI,YAAY;EAChB,IAAI,SAAyB,MAC1B,IAAI,SAAU,OAAO;GACpB,OAAO;IAAE,KAAK;IAAO,MAAM,MAAM;IAAM,MAAM;GAAW;EAC1D,CAAC,CAAC,CACD,OAAO,SAAU,SAAS;GACzB,OAAO,MAAM,gBAAgB,OAAO,MAAM;EAC5C,CAAC;EACH,IAAI,OAAO,WAAW,GAAG,OAAO;EAChC,QAAQ,IACN,OAAO,IAAI,SAAU,SAAS;GAC5B,OAAO,MAAM,cAAc,OAAO;EACpC,CAAC,CACH,CAAC,CAAC,KAAK,WAAY;GACjB,IAAI,CAAC,WACH,KAAK,SAAU,OAAO;IACpB,OAAO,QAAQ;GACjB,CAAC;EACL,CAAC;EACD,OAAO,WAAY;GACjB,YAAY;EACd;CACF,GACA,CAAC,MAAM,GAAG,CACZ;CAGA,IAAI,MAAM,mBAAmB,UAAa,MAAM,eAAe,UAAU,SAAS,OAAO;CACzF,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,IAAI,OAAO,OAAO,MAAM,SAAS,MAAM;CAEvC,SAAS,KAAK,OAA0C;EACtD,IAAI,QAAQ,YACV,OAAO,QAAQ,WACX,MAAM,gBAAgB;GAAO;GAAK,MAAM,MAAM;GAAM,MAAM;EAAW,CAAC,IACtE,IACN;EACA,IAAI,MAAM,MAAM,KAAK,YAAY,GAAG;EACpC,IAAI,YAAY,MAAM,IAAI,MAAM,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI;EAEpE,IAAI,OAAO,MAAM,OAAO,QAAQ,EAAE,iBAAiB;EACnD,IAAI,MAAM,SAAS,cAAc,MAAM,WAAW,IAChD,QAAQ,QAAQ,EAAE,yBAAyB,EAAE,QAAQ,MAAM,OAAO,CAAC;EACrE,OAAO,EACL,QACA;GACE,KAAK,MAAM;GACX,WAAW;GACX,gBAAgB;GAChB,aAAa,MAAM;GACnB,aAAa,MAAM;EACrB,GACA,CACE,EACE,UACA;GACE,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO;GACP,SAAS,WAAY;IACnB,IAAI,OAAO,aAAa,YAAY,SAAS,MAAM,IAAI;GACzD;EACF,GACA;GACE,UAAU,OACN,EACE,QACA;IAAE,KAAK;IAAM,WAAW;GAA6C,GACrE,SACF,IACA,EAAE,OAAO;IACP,KAAK;IACL,WAAW;IACX,KAAK,MAAM;IACX,KAAK,UAAU,MAAM,OAAO,aAAa,OAAO,MAAM,UAAU,EAAE;GACpE,CAAC;GACL,MAAM,SAAS,aACX,EAAE,QAAQ;IAAE,KAAK;IAAQ,WAAW;IAAqB,OAAO;GAAK,GAAG,GAAG,IAC3E;GACJ,EAAE,QAAQ;IAAE,KAAK;IAAQ,WAAW;GAAoB,GAAG,MAAM,IAAI;EACvE,CACF,GACA,EACE,UACA;GACE,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO,EAAE,oBAAoB;GAC7B,gBAAgB;GAChB,SAAS,WAAY;IAEnB,IAAI,SAAS;KAAE,MAAM,MAAM;KAAM,MAAM,MAAM;KAAM,UAAU;IAAK;IAClE,MAAM,UAAU,IAAI;IACpB,MAAM,SAAS,MAAM;GACvB;EACF,GACA,GACF,CACF,CACF;CACF;CAEA,OAAO,EAAE,OAAO;EAAE,WAAW;EAAiB,gBAAgB;CAAc,GAAG;EAC7E,EACE,QACA;GAAE,KAAK;GAAS,WAAW;EAAqB,GAChD,OAAO,QAAQ,EAAE,kBAAkB,IAAI,EAAE,yBAAyB,CACpE;EACA,EAAE,QAAQ;GAAE,KAAK;GAAS,WAAW;EAAqB,GAAG,MAAM,IAAI,IAAI,CAAC;EAC5E,OAAO,IACH,EACE,QACA;GAAE,KAAK;GAAQ,WAAW;EAAoB,GAC9C,EAAE,mBAAmB,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC,CAC1C,IACA;CACN,CAAC;AACH;;;;;;;;;;;;;AAcA,SAAgB,eACd,SACA,YACkB;CAClB,IAAI,YAAY,QAAQ,YAAY,QAAW,OAAO;EAAE,OAAO,CAAC;EAAG,OAAO,eAAe;CAAK;CAC9F,IAAI,YAA2C,QAAQ,QAAQ,QAAQ,QAAQ,CAAC;CAChF,IAAI,UAAkD,QAAQ,WAAW,QAAQ,WAAW,CAAC;CAC7F,IAAI,SAA0B,CAAC;CAC/B,IAAI,OAA6B,CAAC;CAElC,KAAK,IAAI,QAAQ,WAAW;EAC1B,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,WAAW,IAAI,GAAG;EAC5D,IAAI,CAAC,eAAe,IAAI,GAAG;EAC3B,IAAI,SAAiC,UAAU,SAAS,CAAC;EACzD,IAAI,eAAe,MAEjB;QADY,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC,EACjD,CAAC,QAAQ,UAAU,IAAI,KAAK,OAAO,aAAa,YAAY;EAAO;EAE9E,KAAK,QAAQ;EACb,OAAO,KAAK;GACJ;GACN,MAAM,WAAW,IAAI;GACrB,MAAM;GACN,QAAQ;GACR,IAAI,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;EAC1D,CAAC;CACH;CAEA,KAAK,IAAI,SAAS,SAAS;EACzB,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,KAAK,GAAG;EAC3D,IAAI,KAAK,WAAW,MAAM;EAC1B,IAAI,CAAC,eAAe,KAAK,GAAG;EAC5B,IAAI,OAAwC,QAAQ,UAAU,CAAC;EAE/D,IAAI,eAAe,QAAQ,KAAK,aAAa,YAAY;EACzD,KAAK,SAAS;EACd,OAAO,KAAK;GACV,MAAM;GACN,MAAM,WAAW,KAAK;GACtB,MAAM;GACN,QAAQ,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;GACxD,IAAI,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;EACtD,CAAC;CACH;CAEA,OAAO,KAAK,SAAU,MAAM,OAAO;EACjC,OAAO,MAAM,KAAK,KAAK;CACzB,CAAC;CACD,OAAO;EAAE,OAAO;EAAQ,OAAO,eAAe;CAAK;AACrD;;AAGA,SAAgB,YAAY,OAAqC;CAE/D,IAAI,OACF,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,WACrD,QACD;CACN,IAAI,SAAS,QAAQ,KAAK,UAAU,WAAW,CAAC,KAAK,OAAO,OAAO;CACnE,IAAI,QAAQ,KAAK;CACjB,IAAI,MAAM,SAAS,MAAM,MAAM,SAAS,SAAS,OAAO,MAAM;CAC9D,IAAI,MAAM,UAAU,MAAM,OAAO,SAAS,SAAS,OAAO,MAAM;CAChE,OAAO;AACT;;;;;;;AAQA,SAAgB,aAAa,MAA8B;CACzD,IAAI,OAAO,SAAS,YAAY,SAAS,IAAI,GAAG,OAAO;CACvD,IAAI,SAAS,QAAQ,SAAS,UAAa,OAAO,SAAS,UAAU;EAEnE,IAAI,SAAS;EACb,IAAI,OAAO,OAAO,SAAS,UAAU,OAAO,OAAO;CACrD;CACA,OAAO;AACT;;AAGA,SAAgB,WAAW,MAAsB;CAC/C,IAAI,QAAQ,OAAO,IAAI,CAAC,CAAC,MAAM,GAAG;CAClC,OAAO,MAAM,MAAM,SAAS,MAAM,OAAO,IAAI;AAC/C;;;;;;;;;;;;;;ACtTA,SAAS,OAAO,MAA0B;CACxC,OAAO;AACT;;;;;;;;AAoCA,SAAgB,yBACd,WACA,MACA,OACA,WACS;CACT,IAAI,SAAS,OAAO,SAAS,WAAW,KAAK,SAAS;CACtD,IAAI,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,UAAU,WAAW,QAAQ,GAAG,MAAM,CAAC;CAClF,IAAI,WAAW,OAAO,aAAa,KAAK,aAAa;CACrD,IAAI,cAAc,MAAM,OAAO,aAAa,KAAK,WAAW;CAC5D,OAAO,aAAa;AACtB;;;;;;;;AASA,SAAgB,kBACd,OACA,WACA,OACmB;CACnB,IAAI,UAAW,SAAS,MAAM,WAAY,CAAC;CAC3C,IAAI,OAAO,SAAS,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;CAClE,IAAI,QAAQ,SAAS,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;CACrE,IAAI,QAAQ,MAAM;CAClB,IAAI,QAAQ,MAAM;CAElB,IAAI,CAAC,yBAAyB,WAAW,MAAM,OAAO,SAAS,CAAC,GAC9D,OAAO;EAAE,SAAS;EAAc;CAAM;CAExC,IAAI,cAAc,MAAM;EACtB,IAAI,QAAQ,WAAW,GAAG,OAAO;GAAE,SAAS;GAAc;EAAM;EAChE,IAAI,UAAU,IACZ,OAAO;GAAE,SAAS;GAAM,OAAO;IAAE,OAAO;IAAG,OAAO;GAAK;GAAG,MAAM,QAAQ;GAAI,OAAO;EAAQ;EAC7F,IAAI,QAAQ,QAAQ,SAAS,GAC3B,OAAO;GACL,SAAS;GACT,OAAO;IAAE,OAAO,QAAQ;IAAU;GAAM;GACxC,MAAM,QAAQ,QAAQ;GACtB,OAAO;EACT;EAEF,OAAO;GAAE,SAAS;GAAc;EAAM;CACxC;CAEA,IAAI,QAAQ,GACV,OAAO;EACL,SAAS;EACT,OAAO;GAAE,OAAO,QAAQ;GAAU;EAAM;EACxC,MAAM,QAAQ,QAAQ;EACtB,OAAO;CACT;CACF,IAAI,UAAU,GACZ,OAAO;EACL,SAAS;EACT,OAAO;GAAE,OAAO;GAAI,OAAO;EAAK;EAChC,MAAM,UAAU,QAAQ,UAAU,SAAY,KAAK;EACnD,OAAO;CACT;CAEF,OAAO;EAAE,SAAS;EAAc;CAAM;AACxC;;;;;;;;AAWA,SAAgB,eAAe,QAAsC;CAEnE,IAAI,OAAO;CACX,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,SAAS,QAAQ,SAAS,QAAW,SAAS,GAAG;EAChF,IAAI,OAAO,KAAK,iBAAiB,YAAY;GAC3C,IAAI,SAAS,KAAK,aAAa,qBAAqB;GACpD,IAAI,WACF,OAAO,KAAK,sBAAsB,YAC9B,KAAK,oBACL,KAAK,aAAa,iBAAiB,MAAM;GAC/C,IAAI,WAAW,UAAU,UAAU,OAAO;EAC5C;EACA,OAAO,KAAK;CACd;CACA,OAAO;AACT;;AAGA,SAAgB,aAAa,MAA+C;CAC1E,IAAI,SAAS,QAAQ,SAAS,QAAW,OAAO;CAOhD,QALE,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL,GACG,CAAC,QAAQ,WAAW,EAAE;AACnC;;;;;;;;AASA,SAAgB,gBAAgB,MAA+C;CAC7E,OAAO,aAAa,IAAI,CAAC,CAAC,QAAQ,QAAQ,EAAE;AAC9C;;AAGA,SAAgB,oBAAoB,MAAc,UAA2B;CAC3E,OAAO,KAAK,QAAQ,QAAQ,EAAE,MAAM,SAAS,QAAQ,QAAQ,EAAE;AACjE;;AAGA,SAAgB,oBAAoB,MAAmC;CACrE,IAAI,YAAY,OAAO,OAAO,iBAAiB,aAAa,OAAO,aAAa,IAAI;CACpF,IAAI,CAAC,aAAa,UAAU,eAAe,KAAK,UAAU,gBAAgB,MAAM,OAAO;CACvF,IAAI,OAAO,UAAU;CACrB,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,QAAQ,SAAS,YAAY;CACjC,MAAM,mBAAmB,OAAO,IAAI,CAAC;CACrC,IAAI,SAAS,MAAM,WAAW;CAC9B,IAAI;EACF,OAAO,OAAO,MAAM,UAAU,WAAW;CAC3C,SAAS,OAAO;EACd,OAAO;CACT;CACA,OAAO,OAAO,SAAS,CAAC,CAAC;AAC3B;;AAGA,SAAgB,qBAAqB,MAAoB,UAA8B;CACrF,IAAI,YAAY,OAAO,OAAO,iBAAiB,aAAa,OAAO,aAAa,IAAI;CACpF,IAAI,CAAC,WAAW;CAChB,IAAI;EACF,IAAI,QAAQ,SAAS,YAAY;EACjC,MAAM,mBAAmB,OAAO,IAAI,CAAC;EACrC,MAAM,SAAS,aAAa,KAAK;EACjC,UAAU,gBAAgB;EAC1B,UAAU,SAAS,KAAK;CAC1B,SAAS,OAAO,CAEhB;AACF;;AAGA,SAAgB,uBAAuB,MAA0B;CAC/D,IAAI,YAAY,OAAO,OAAO,iBAAiB,aAAa,OAAO,aAAa,IAAI;CACpF,IAAI,CAAC,WAAW;CAChB,IAAI;EACF,IAAI,QAAQ,SAAS,YAAY;EACjC,MAAM,mBAAmB,OAAO,IAAI,CAAC;EACrC,UAAU,gBAAgB;EAC1B,UAAU,SAAS,KAAK;CAC1B,SAAS,OAAO,CAEhB;AACF;;AAGA,SAAgB,iBAAgC;CAC9C,IAAI,QAAQ,QAAQ,QAAQ;CAC5B,IAAI,OAAO,OAAO,0BAA0B,YAC1C,QAAQ,MAAM,KAAK,WAAY;EAC7B,OAAO,IAAI,QAAQ,SAAU,SAAS;GACpC,OAAO,sBAAsB,WAAY;IACvC,QAAQ;GACV,CAAC;EACH,CAAC;CACH,CAAC;CAEH,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,kBACd,MACA,OACA,OACkB;CAClB,SAAS,YAAY;EACnB,OAAO,oBAAoB,aAAa,IAAI,GAAG,KAAK;CACtD;CAEA,OAAO,eAAe,CAAC,CACpB,KAAK,WAAY;EAChB,IAAI,OAAO,OAAO,mBAAmB,cAAc,OAAO,OAAO,iBAAiB,YAChF,OAAO;EACT,IAAI,OAAO,KAAK,kBAAkB,YAAY,OAAO;EACrD,IAAI;GACF,uBAAuB,IAAI;GAC3B,IAAI,OAAO,IAAI,OAAO,aAAa;GACnC,KAAK,QAAQ,cAAc,KAAK;GAChC,KAAK,cACH,IAAI,OAAO,eAAe,SAAS;IACjC,eAAe;IACf,SAAS;IACT,YAAY;GACd,CAAC,CACH;EACF,SAAS,OAAO;GACd,OAAO;EACT;EACA,OAAO,eAAe,CAAC,CAAC,KAAK,SAAS;CACxC,CAAC,CAAC,CACD,KAAK,SAAU,OAAO;EACrB,IAAI,OAAO,OAAO;EAClB,IAAI;GACF,IAAI,OAAO,SAAS,gBAAgB,YAAY;IAC9C,uBAAuB,IAAI;IAC3B,IAAI,SAAS,YAAY,cAAc,OAAO,KAAK,MAAM,MACvD,OAAO,eAAe,CAAC,CAAC,KAAK,SAAS;GAE1C;EACF,SAAS,OAAO,CAEhB;EACA,OAAO,eAAe,CAAC,CAAC,KAAK,WAAY;GACvC,IAAI,UAAU,GAAG,OAAO;GACxB,IAAI;IACF,uBAAuB,IAAI;IAC3B,KAAK,cAAc;IAEnB,IAAI,OAAO,OAAO,UAAU,YAC1B,KAAK,cAAe,IAAI,OAAO,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;IAClE,OAAO;GACT,SAAS,OAAO;IACd,OAAO;GACT;EACF,CAAC;CACH,CAAC,CAAC,CACD,KAAK,SAAU,OAAO;EACrB,IAAI,CAAC,OAAO,OAAO;EACnB,qBAAqB,MAAM,KAAK;EAEhC,IAAI,OAAO,OAAO,0BAA0B,YAC1C,OAAO,sBAAsB,WAAY;GACvC,qBAAqB,MAAM,KAAK;EAClC,CAAC;EAEH,OAAO;CACT,CAAC;AACL;;;;;;;;AASA,SAAgB,kBAAkB,OAA6B;CAC7D,IAAI,MAA2F;EAC7F,WAAW;EACX,OAAO;EACP,OAAO;EACP,SAAS;CACX;CAEA,SAAS,UAAU,OAA4B;EAC7C,IAAI,UAAU,QAAQ,UAAU,QAAW;EAC3C,IAAI,MAAM,qBAAqB,QAAQ,MAAM,gBAAgB,MAAM;EACnE,IAAI,MAAM,QAAQ,aAAa,MAAM,QAAQ,aAAa;EAC1D,IAAI,MAAM,WAAW,QAAQ,MAAM,YAAY,QAAQ,MAAM,YAAY,MAAM;EAC/E,IAAI,OAAO,MAAM,mBAAmB,YAAY;EAEhD,IAAI,OAAO,eAAe,MAAM,MAAM;EACtC,IAAI,SAAS,MAAM;EAEnB,IAAI,WAAW,MAAM,YAAY,CAAC,CAAC;EACnC,IAAI,QACF,aAAa,QAAQ,aAAa,UAAa,SAAS,cAAc,OAClE,SAAS,QACT;EACN,IAAI,UACF,UAAU,QACV,UAAU,UACV,MAAM,cAAc,QACpB,MAAM,QAAQ,MAAM,OAAO,IACvB,MAAM,UACN,CAAC;EACP,IAAI,QAAQ,WAAW,GAAG;EAE1B,IAAI,IAAI,cAAc,SAAS,WAAW;GACxC,IAAI,YAAY,SAAS;GACzB,IAAI,QAAQ;GACZ,IAAI,QAAQ;EACd;EAEA,IAAI,QAAQ,oBAAoB,IAAI;EACpC,IAAI,UAAU,MAAM;EACpB,IAAI,YAA8B,MAAM,QAAQ,YAAY,OAAO;EAEnE,IAAI,OAAO,gBAAgB,IAAI;EAC/B,IAAI,OAAO,kBAAkB;GAAE,OAAO,IAAI;GAAO,OAAO,IAAI;EAAM,GAAG,WAAW;GACrE;GACH;GACN,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;EACjD,CAAC;EACD,IAAI,CAAC,KAAK,SAAS;EAGnB,MAAM,eAAe;EACrB,IAAI,OAAO,MAAM,oBAAoB,YAAY,MAAM,gBAAgB;EAIvE,IAAI,WAAW;GAAE,OAAO,IAAI;GAAO,OAAO,IAAI;EAAM;EACpD,IAAI,QAAQ,KAAK,MAAM;EACvB,IAAI,QAAQ,KAAK,MAAM;EACvB,IAAI,UAAU;EAEd,kBAAkB,MAAM,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,KAAK,SAAU,OAAO;GACnE,IAAI,UAAU;GACd,IAAI,UAAU,MAAM;IAClB,MAAM,IAAI,kBAAkB;KAC1B,OAAO,IAAI;KACX,SAAS,QAAQ;KACN;IACb,CAAC;IACD;GACF;GACA,IAAI,QAAQ,SAAS;GACrB,IAAI,QAAQ,SAAS;GACrB,MAAM,IAAI,0CAA0C,EAClD,SAAS,QAAQ,OACnB,CAAC;EACH,CAAC;CACH;;CAGA,SAAS,QAAQ,OAAoB;EACnC,IAAI,IAAI,SAAS;EACjB,IAAI,eAAe,SAAS,MAAM,MAAM,MAAM,MAAM;EACpD,IAAI,QAAQ;EACZ,IAAI,QAAQ;CACd;CAEA,IAAI,OAAO,OAAO,qBAAqB,YAAY,OAAO,WAAY,CAAC;CACvE,OAAO,iBAAiB,WAAW,WAAW,IAAI;CAClD,OAAO,iBAAiB,SAAS,SAAS,IAAI;CAC9C,OAAO,WAAY;EACjB,OAAO,oBAAoB,WAAW,WAAW,IAAI;EACrD,OAAO,oBAAoB,SAAS,SAAS,IAAI;CACnD;AACF;;;;;;;;AC1aA,MAAa,KAAK;CAChB,eAAe;CACf,iBAAiB;CACjB,eAAe;CACf,eAAe;CACf,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,iBAAiB;CACjB,iBAAiB;CACjB,mBAAmB;CACnB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,gBAAgB;CAChB,kBAAkB;CAClB,sBAAsB;CACtB,iBAAiB;CACjB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,oBAAoB;CACpB,aAAa;CACb,iBAAiB;CACjB,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,qBAAqB;CACrB,iBAAiB;CACjB,cAAc;CACd,aAAa;CACb,aAAa;CACb,eAAe;CACf,cAAc;CACd,eAAe;CAEf,iBAAiB;CACjB,cAAc;CACd,cAAc;CACd,wBAAwB;CACxB,uBAAuB;CACvB,qBAAqB;CACrB,oBAAoB;CACpB,sBAAsB;CACtB,qBAAqB;CACrB,cAAc;CACd,qBAAqB;CACrB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,kBAAkB;CAClB,mBAAmB;CAEnB,eAAe;CACf,iBAAiB;CACjB,cAAc;CACd,eAAe;CAGf,oBAAoB;CACpB,2BAA2B;CAC3B,mBAAmB;CACnB,sBAAsB;CACtB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,mBAAmB;CACnB,mBAAmB;CACnB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,kBAAkB;CAClB,gBAAgB;CAEhB,gBAAgB;CAChB,aAAa;CACb,eAAe;CACf,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CACf,cAAc;CACd,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,eAAe;CACf,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,qBAAqB;CACrB,sBAAsB;CACtB,gBAAgB;CAChB,0BAA0B;CAC1B,sBAAsB;CACtB,2BAA2B;CAC3B,oBAAoB;CACpB,2BAA2B;CAC3B,yBAAyB;CACzB,oBAAoB;CACpB,wBAAwB;CACxB,yBAAyB;CACzB,gBAAgB;CAChB,mBAAmB;CACnB,gBAAgB;CAChB,qBACE;CACF,gBAAgB;CAChB,iBAAiB;CACjB,kBAAkB;CAClB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,cAAc;CACd,cAAc;CACd,kBAAkB;CAClB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,mBAAmB;CACnB,oBAAoB;CACpB,eAAe;CACf,cAAc;CACd,mBAAmB;CACnB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,kBAAkB;CAClB,iBAAiB;AACnB;AAEA,MAAa,KAAK;CAChB,eAAe;CACf,iBAAiB;CACjB,eAAe;CACf,eAAe;CACf,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,iBAAiB;CACjB,iBAAiB;CACjB,mBAAmB;CACnB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,eAAe;CACf,gBAAgB;CAChB,kBAAkB;CAClB,sBAAsB;CACtB,iBAAiB;CACjB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,oBAAoB;CACpB,aAAa;CACb,iBAAiB;CACjB,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,qBAAqB;CACrB,iBAAiB;CACjB,cAAc;CACd,aAAa;CACb,aAAa;CACb,eAAe;CACf,cAAc;CACd,eAAe;CAEf,iBAAiB;CACjB,cAAc;CACd,cAAc;CACd,wBAAwB;CACxB,uBAAuB;CACvB,qBAAqB;CACrB,oBAAoB;CACpB,sBAAsB;CACtB,qBAAqB;CACrB,cAAc;CACd,qBAAqB;CACrB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,kBAAkB;CAClB,mBAAmB;CAEnB,eAAe;CACf,iBAAiB;CACjB,cAAc;CACd,eAAe;CAEf,oBAAoB;CACpB,2BAA2B;CAC3B,mBAAmB;CACnB,sBAAsB;CACtB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,mBAAmB;CACnB,mBACE;CACF,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,kBACE;CACF,gBAAgB;CAChB,gBAAgB;CAChB,aAAa;CACb,eAAe;CACf,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CACf,cAAc;CACd,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,eAAe;CACf,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,qBAAqB;CACrB,sBAAsB;CACtB,gBAAgB;CAChB,0BAA0B;CAC1B,sBAAsB;CACtB,2BAA2B;CAC3B,oBAAoB;CACpB,2BAA2B;CAC3B,yBAAyB;CACzB,oBAAoB;CACpB,wBAAwB;CACxB,yBAAyB;CACzB,gBAAgB;CAChB,mBAAmB;CACnB,gBAAgB;CAChB,qBACE;CACF,gBAAgB;CAChB,iBAAiB;CACjB,kBAAkB;CAClB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,iBACE;CACF,gBAAgB;CAChB,gBAAgB;CAChB,cAAc;CACd,cAAc;CACd,kBAAkB;CAClB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,qBAAqB;CACrB,mBAAmB;CACnB,oBAAoB;CACpB,eAAe;CACf,cAAc;CACd,mBAAmB;CACnB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,kBACE;CACF,iBAAiB;AACnB;;;;AChRA,SAAgB,uBACd,KACA,UACA,KACA,SACkB;;CAElB,IAAI,kBAA2C;;CAG/C,SAAS,YAAY,WAAsC,KAAsC;EAC/F,IAAI,SAAS,CAAC,CAAC,cAAc,aAAa,SAAS,CAAC,CAAC,SAAS,OAAO,OAAO;EAC5E,IAAI;GACF,WAAW,cAAc,SAAY,OAAO;GAC5C,KAAK,OAAO;GACZ,QAAQ;GACR,WAAW;GACX,SAAS;GACT,YAAY;GACZ,MAAM;GACN,QAAQ;GACR,eAAe;GAGf,MAAM;GACN,QAAQ;GACR,QAAQ;GACR,OAAO,MAAM,YAAY;EAC3B,CAAC;EAED,IAAI,EACF,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ,CAAC;GACT,QAAQ,CAAC;GACT,QAAQ,CAAC;GACT,eAAe;GACf,OAAO;GACP,WAAW;EACb,EACF,CAAC;EAED,IAAI,EACF,gBAAgB;GACd,OAAO;GACP,OAAO;GACP,UAAU;GACV,UAAU,CAAC;GACX,OAAO;GACP,SAAS;GACT,iBAAiB;GACjB,WAAW;EACb,EACF,CAAC;EAED,QAAQ,UAAU,SAAS;EAC3B,QAAQ,QAAQ;CAClB;;;;;;;CAOA,SAAS,qBAAuC;EAC9C,IAAI,MAAM,SAAS,CAAC,CAAC;EACrB,IAAI,YAAY,SAAS,CAAC,CAAC;EAC3B,IAAI,CAAC,OAAO,CAAC,WAAW,OAAO,QAAQ,QAAQ;EAC/C,IAAI,iBAAiB,OAAO;EAC5B,IAAI,SAAS,CAAC,CAAC,eAAe,UAAU,QACtC,IAAI,EAAE,gBAAgB,OAAO,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,gBAAgB,EAAE,OAAO,UAAU,CAAC,EAAE,CAAC;EAE5F,IAAI,OAAO,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAA4B,SAAS,mBAAmB,EAChF,MAAM;IAAO;IAAgB;GAAU,EACzC,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,kBAAkB;GAClB,IAAI,SAAS,CAAC,CAAC,QAAQ,OAAO,SAAS,CAAC,CAAC,cAAc,WAAW;GAClE,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,SAAS,OAAO,MAAM,gBAAgB,MAC/E,MAAM,IAAI,mBAAmB,EAAE,aAAa,KAAK,CAAC;GAEpD,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,SAAS,OAAO,MAAM,gBAAgB,MAAM;IACrF,IAAI,QAAuC,CAAC;IAC5C,IAAI,OAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,IAAI,OAAO,MAAM,QAAQ,CAAC;IACrE,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;KACnD,IAAI,OAAO,KAAK;KAChB,IAAI,QAAQ,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,IAAI,MAAM,KAAK,QAAQ;IACpF;IACA,IAAI,WAAmD,CAAC;IACxD,IAAI,eAAe,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI,OAAO,MAAM,WAAW,CAAC;IACnF,KAAK,IAAI,SAAS,GAAG,SAAS,aAAa,QAAQ,UAAU,GAAG;KAC9D,IAAI,eAAe,aAAa;KAChC,IAAI,gBAAgB,OAAO,aAAa,SAAS,YAAY,aAAa,SAAS,IACjF,SAAS,aAAa,QAAQ;IAElC;IACA,IAAI,QAAQ,OAAO,OAAO,MAAM,UAAU,WAAW,OAAO,MAAM,QAAQ;IAC1E,IAAI,UAAU,OAAO,OAAO,MAAM,YAAY,WAAW,OAAO,MAAM,UAAU;IAChF,MAAM,IAAI,mBAAmB;KAC3B,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC;KAC1B,UAAU,OAAO,KAAK,QAAQ,CAAC,CAAC;KAChC,UAAU,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI,OAAO,MAAM,SAAS,SAAS;KACvE;KACT,OAAO,UAAU,OAAO,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,YAAY;IAC7D,CAAC;IACD,IAAI,EACF,gBAAgB;KACd,OAAO;KACA;KACG;KACV,UAAU,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI,OAAO,MAAM,WAAW,CAAC;KACnE;KACE;KACT,iBACE,OAAO,OAAO,MAAM,oBAAoB,WAAW,OAAO,MAAM,kBAAkB;KACpF,WAAW;IACb,EACF,CAAC;IACD;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,MAAM,IAAI,0BAA0B,EAAE,MAAM,QAAQ,YAAY,CAAC;GACjE,IAAI,EACF,gBAAgB;IACd,OAAO;IACP,OAAO;IACP,UAAU;IACV,UAAU,CAAC;IACX,OAAO;IACP,SAAS;IACT,iBAAiB;IACjB,WAAW,QAAQ;GACrB,EACF,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,kBAAkB;GAClB,IAAI,SAAS,CAAC,CAAC,QAAQ,KAAK;GAC5B,MAAM,IAAI,0BAA0B,EAAE,MAAM,YAAY,CAAC;GACzD,IAAI,EACF,gBAAgB;IACd,OAAO;IACP,OAAO;IACP,UAAU;IACV,UAAU,CAAC;IACX,OAAO;IACP,SAAS;IACT,iBAAiB;IACjB,WAAW;GACb,EACF,CAAC;EACH,CAAC;EAEH,kBAAkB;EAClB,OAAO;CACT;;;;;;;CAOA,SAAS,eAAe,MAAoB;EAC1C,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;EACnD,IAAI,YAAY,SAAS,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,OAAO,QAAQ;EAC9D,IAAI,OAAO,cAAc,UAAU;GACjC,MAAM,IAAI,uBAAuB,EAAQ,KAAK,CAAC;GAC/C;EACF;EACA,IAAI,OAAoB,CAAC;EACzB,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC,UACzB,IAAI,OAAO,UAAU,eAAe,KAAK,SAAS,CAAC,CAAC,UAAU,GAAG,GAC/D,KAAK,OAAO,SAAS,CAAC,CAAC,SAAS;EACpC,IAAI,UAAU,QAAQ,cAAc,IAAI;EACxC,IAAI,KAAK,aAAa,UAAa,KAAK,QAAQ,CAAC,cAAc,WAAW,OAAO,KAAK;OACjF,KAAK,WAAW;GAAa;GAAW,IAAI,KAAK,IAAI;EAAE;EAC5D,IAAI,EAAE,UAAU,cAAc,IAAI,EAAE,CAAC;CACvC;;CAGA,SAAS,gBAAsB;EAC7B,IAAI,SAAS,YAAY,SAAS,CAAC,CAAC,KAAK,EAAE;EAC3C,IAAI,OAAoB,CAAC;EACzB,IAAI,UAAU;EACd,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC,UAAU;GACnC,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,CAAC,CAAC,UAAU,GAAG,GAAG;GACrE,IAAI,IAAI,QAAQ,MAAM,MAAM,GAAG;IAC7B,WAAW;IACX;GACF;GACA,KAAK,OAAO,SAAS,CAAC,CAAC,SAAS;EAClC;EACA,IAAI,YAAY,GAAG;EACnB,IAAI,EAAE,UAAU,cAAc,IAAI,EAAE,CAAC;CACvC;CAEA,OAAO;EACQ;EACE;EACK;EACJ;CAClB;AACF;;;;AC3MA,SAAgB,mBACd,KACA,UACA,KACA,SACc;;;;;;;;CAcd,SAAS,WAAW,QAAqC;EACvD,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,OAAO,QAAQ,QAAQ;EACtE,IAAI,MAAM,SAAS,CAAC,CAAC;EACrB,IAAI,MAAM,OAAO;EAEjB,IAAI;GACF,QAAQ;IACN,OAAO;IACF;IACL,UACE,OAAO,OAAO,QAAQ,YAAY,OAAO,IAAI,SAAS,IAAI,OAAO,IAAI,MAAM,GAAG,CAAC,IAAI;IACrF,SAAS,OAAO,WAAW;IAC3B,QAAQ,OAAO,UAAU;IACzB,MAAM,OAAO,QAAQ;IACrB,aAAa,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;IAC3E,MAAM,OAAO,QAAQ,CAAC;IACtB,OAAO;IACP,OAAO,CAAC;IACR,WAAW;IACX,WAAW;GACb;GACA,MAAM;EACR,CAAC;EA0DD,OAxDW,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAoB,SAAS,UAAU,EAC/D,MAAM;IAAO;IAAU;GAAI,EAC7B,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GAEtB,IAAI,UAAU,SAAS,CAAC,CAAC;GAEzB,IAAI,CAAC,WAAW,QAAQ,QAAQ,OAAO,SAAS,CAAC,CAAC,QAAQ,KAAK;GAC/D,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,OAAO;IAChD,IAAI,QAAQ,OAAO;IACnB,IAAI,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,QAAQ,CAAC;IACxD,IAAI,OAAO,MAAM;IACjB,IAAI,SAAS,QAAQ,SAAS,QAAW;KACvC,IAAI,EACF,QAAQ;MACN,OAAO;MACP,KAAK,KAAK,OAAO;MACjB,UAAU,KAAK,YAAY;MAC3B,SAAS,KAAK,WAAW;MACzB,QAAQ,KAAK,UAAU;MACvB,MAAM,KAAK,QAAQ;MACnB,aAAa,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;MACvE,MAAM,KAAK,QAAQ,CAAC;MACpB,OAAO,KAAK,UAAU;MACf;MACP,WAAW,MAAM,cAAc;MAC/B,WAAW;KACb,EACF,CAAC;KAID,IAAI,MAAM,SAAS,GAAG,OAAO,eAAe,MAAM,EAAE;KACpD;IACF;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,IAAI,EACF,QAAQ,OAAO,OAAO,CAAC,GAAG,SAAS;IACjC,OAAO;IACP,OAAO,CAAC;IACR,WAAW,QAAQ;GACrB,CAAC,EACH,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GAEjB,IAAI,UAAU,SAAS,CAAC,CAAC;GACzB,IAAI,CAAC,WAAW,QAAQ,QAAQ,KAAK;GACrC,IAAI,EAAE,QAAQ,OAAO,OAAO,CAAC,GAAG,SAAS;IAAE,OAAO;IAAS,WAAW;GAAY,CAAC,EAAE,CAAC;EACxF,CAGQ;CACZ;;;;;CAKA,SAAS,eAAe,MAAuC;EAC7D,IAAI,UAAU,SAAS,CAAC,CAAC;EACzB,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,MAAM,OAAO,QAAQ,QAAQ;EAC/E,IAAI,MAAM,SAAS,CAAC,CAAC;EACrB,IAAI,MAAM,QAAQ;EAClB,IAAI,cAAc,SAAS,CAAC,CAAC;EAC7B,IAAI,OAAO,cAAc,YAAY,SAAS,OAAO,OAAO,CAAC,CAAC,SAAS;EAEvE,IAAI;GACF,QAAQ,KAAK;GACb,MAAM;IACJ,QAAQ;IACR,OAAO;IACF;IACL,UAAU,QAAQ;IAClB,SAAS,QAAQ;IACjB,MAAM,KAAK,QAAQ,KAAK;IACxB,MAAM,KAAK;IACX,UAAU,KAAK,YAAY;IAC3B,MAAM;IACN,MAAM;IACA;IACN,MACE,eACA,OAAO,YAAY,SAAS,YAC5B,WAAW,QAAQ,YAAY,IAAI,KAAK,IACpC,YAAY,OACZ,SAAS,OAAO,CAAC;IACvB,MAAM;IACN,WAAW,CAAC;IACZ,WAAW;GACb;EACF,CAAC;EA2CD,OAzCW,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAwB,SAAS,eAAe,EACxE,MAAM;IAAO;IAAU;IAAK,MAAM,KAAK;IAAM,UAAU,KAAK,YAAY;GAAK,EAC/E,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GAEtB,IAAI,MAAM,SAAS,CAAC,CAAC;GAErB,IACE,CAAC,OACD,IAAI,WAAW,YACf,IAAI,QAAQ,OACZ,IAAI,SAAS,KAAK,QAClB,SAAS,CAAC,CAAC,QAAQ,KAEnB;GACF,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,OAAO;IAChD,IAAI,EACF,MAAM,OAAO,OAAO,CAAC,GAAG,KAAK;KAAE,OAAO;KAAS,MAAM,OAAO;KAAO,WAAW;IAAK,CAAC,EACtF,CAAC;IACD;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,IAAI,EACF,MAAM,OAAO,OAAO,CAAC,GAAG,KAAK;IAC3B,OAAO;IACP,MAAM;IACN,WAAW,QAAQ;GACrB,CAAC,EACH,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GAEjB,IAAI,MAAM,SAAS,CAAC,CAAC;GACrB,IAAI,CAAC,OAAO,IAAI,WAAW,YAAY,IAAI,SAAS,KAAK,MAAM;GAC/D,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,KAAK;IAAE,OAAO;IAAS,WAAW;GAAY,CAAC,EAAE,CAAC;EAClF,CAGQ;CACZ;;;;;CAKA,SAAS,aAAa,QAA4C;EAChE,IAAI,CAAC,UAAU,CAAC,OAAO,KAAK,OAAO,QAAQ,QAAQ;EACnD,IAAI,kBAAkB,SAAS,CAAC,CAAC;EACjC,IAAI,oBAAoB,QAAQ,WAAW,OAAO,KAAK,gBAAgB,GAAG,GACxE,OAAO,YAAY;EACrB,OAAO,WAAW,MAAM;CAC1B;;CAEA,SAAS,cAAoB;EAC3B,IAAI,SAAS,CAAC,CAAC,WAAW,MAAM;EAChC,IAAI,WAAW,SAAS,CAAC,CAAC;EAE1B,IAAI;GAAE,QAAQ;GAAM,MADT,YAAY,SAAS,WAAW,WAAW,OAAO;GAC7B,QAAQ;EAAK,CAAC;CAChD;;;;;CAKA,SAAS,YAAY,KAAa,KAAsC;EACtE,OAAO,QAAQ,QAAQ,CAAC,CACrB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAiB,SAAS,OAAO,EACzD,MAAM;IAAO;IAAK,OAAO;GAAG,EAC9B,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,IAAI,SAAS,CAAC,CAAC,QAAQ,KAAK;GAK5B,IAAI;IAAE,SAHJ,UAAU,OAAO,OAAO,QAAQ,OAAO,SAAS,MAAM,QAAQ,OAAO,MAAM,OAAO,IAC9E,OAAO,MAAM,UACb,CAAC;IACiB,YAAY;GAAI,CAAC;EAC3C,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,IAAI,SAAS,CAAC,CAAC,QAAQ,KAAK;GAC5B,IAAI;IAAE,SAAS,CAAC;IAAG,YAAY;GAAI,CAAC;EACtC,CAAC;CACL;;CAEA,SAAS,SAAS,KAA+B;EAG/C,IAAI,SAAS,CAAC,CAAC,SAAS,QAAQ,KAAK,IAAI,IAAI,SAAS,CAAC,CAAC,SAAS,KAAO,OAAO,QAAQ,QAAQ;EAC/F,OAAO,QAAQ,QAAQ,CAAC,CACrB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAkB,SAAS,QAAQ,EAAE,MAAM,EAAO,IAAI,EAAE,CAAC;EACrF,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,IAAI,SAAS,CAAC,CAAC,QAAQ,KAAK;GAC5B,IAAI,QAAQ,UAAU,OAAO,OAAO,OAAO,OAAO,QAAQ;GAC1D,IAAI,OAAO,SAAS,MAAM,QAAQ,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC;GAC9D,MAAM,IAAI,QAAQ;IAChB,OAAO,KAAK;IACZ,WAAW,QAAQ,MAAM,cAAc,OAAO;GAChD,CAAC;GACD,IAAI;IACI;IACN,QAAQ,KAAK,IAAI;IACjB,eAAe,QAAQ,MAAM,cAAc,OAAO;GACpD,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,IAAI,SAAS,CAAC,CAAC,QAAQ,KAAK;GAC5B,MAAM,IAAI,eAAe,EAAE,MAAM,YAAY,CAAC;GAC9C,IAAI;IAAE,MAAM,CAAC;IAAG,QAAQ,KAAK,IAAI;IAAG,eAAe;GAAM,CAAC;EAC5D,CAAC;CACL;CAEA,OAAO;EACQ;EACA;EACH;EACE;EACI;EACF;CAChB;AACF;;;;ACjRA,SAAgB,gBACd,KACA,UACA,KACA,SACW;;;;;CAQX,SAAS,SAAS,QAAqC;EACrD,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,MAAM,OAAO,QAAQ,QAAQ;EACvE,IAAI,MAAM,SAAS,CAAC,CAAC;EACrB,IAAI,cAAc,SAAS,CAAC,CAAC;EAC7B,IAAI,OAAO,cAAc,YAAY,SAAS,OAAO,OAAO,CAAC,CAAC,SAAS;EACvE,IAAI,OACF,eACA,OAAO,YAAY,SAAS,YAC5B,WAAW,QAAQ,YAAY,IAAI,KAAK,IACpC,YAAY,OACZ,SAAS,OAAO,CAAC;EACvB,IAAI;GAEF,QAAQ;GACR,QAAQ,OAAO;GACf,MAAM;IACJ,QAAQ;IACR,OAAO;IACP,MAAM,OAAO;IACb,MAAM,OAAO;IACb,UAAU,OAAO,YAAY;IAC7B,MAAM;IACN,MAAM;IACA;IACA;IACN,MAAM;IACN,WAAW,CAAC;IACZ,WAAW;GACb;EACF,CAAC;EA2DD,OAzDW,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAA0B,SAAS,QAAQ,EACnE,MAAM;IAAO;IAAK,MAAM,OAAO;IAAM,UAAU,OAAO,YAAY;GAAK,EACzE,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GAEtB,IAAI,UAAU,SAAS,CAAC,CAAC;GAEzB,IAAI,CAAC,WAAW,QAAQ,SAAS,OAAO,QAAQ,SAAS,CAAC,CAAC,QAAQ,KAAK;GACxE,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,OAAO;IAChD,IAAI,QAAQ,OAAO;IACnB,IAAI,eAAe,MAAM,YAAY,MAAM,SAAS,OAAO,MAAM,SAAS,KAAK,SAAS;IACxF,IAAI,YAAY,MAAM,SAAS,MAAM,MAAM,OAAO,MAAM,MAAM,KAAK,SAAS;IAC5E,IAAI,EACF,MAAM;KACJ,QAAQ;KACR,OAAO;KACP,MAAM,OAAO;KACb,MAAM,OAAO;KACb,UAAU,OAAO,YAAY;KAC7B,MAAM;KACN,MAAM,eAAe,KAAK,cAAc,IAAI,aAAa;KACzD,MAAM,QAAQ;KACd,MAAM,QAAQ;KACd,MAAM;KACN,WAAW,CAAC;KACZ,WAAW;IACb,EACF,CAAC;IACD;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,IAAI,EACF,MAAM;IACJ,QAAQ;IACR,OAAO;IACP,MAAM,OAAO;IACb,MAAM,OAAO;IACb,UAAU,OAAO,YAAY;IAC7B,MAAM;IACN,MAAM;IACN,MAAM,QAAQ;IACd,WAAW,QAAQ;GACrB,EACF,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GAEjB,IAAI,UAAU,SAAS,CAAC,CAAC;GACzB,IAAI,CAAC,WAAW,QAAQ,SAAS,OAAO,MAAM;GAC9C,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,SAAS;IAAE,OAAO;IAAS,WAAW;GAAY,CAAC,EAAE,CAAC;EACtF,CAIQ;CACZ;CACA,SAAS,YAAkB;EACzB,IAAI,SAAS,CAAC,CAAC,SAAS,MAAM;EAC9B,IAAI,EAAE,MAAM,KAAK,CAAC;CACpB;CACA,SAAS,YAAY,MAAoB;EACvC,IAAI,OAAO,SAAS,CAAC,CAAC;EACtB,IAAI,CAAC,QAAQ,KAAK,WAAW,YAAa,SAAS,cAAc,SAAS,SAAU;EAEpF,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM;GAAQ;GAAM,MAAM;GAAG,WAAW,CAAC;EAAE,CAAC,EAAE,CAAC;CAC/E;;;;;;;;;;;CAWA,SAAS,SAAS,OAAe,MAAiC;EAChE,IAAI,OAAO,QAAQ,aAAa;EAChC,IAAI,KAAK,WAAW,GAAG,OAAO;EAE9B,IAAI,QAAQ;EACZ,KAAK,IAAI,SAAS,GAAG,SAAS,KAAK,QAAQ,UAAU,GACnD,IAAI,KAAK,OAAO,CAAC,SAAS,SAAS,CAAC,CAAC,QAAQ;GAC3C,QAAQ;GACR;EACF;EASF,IAAI,QAAQ,KALV,UAAU,KACN,QAAQ,IACN,IACA,KAAK,SAAS,IAChB,KAAK,IAAI,KAAK,IAAI,GAAG,QAAQ,KAAK,GAAG,KAAK,SAAS,CAAC;EAE1D,IAAI,CAAC,SAAS,CAAC,MAAM,MAAM,OAAO;EAElC,IAAI,EAAE,QAAQ,MAAM,KAAK,CAAC;EAC1B,IAAI,MAAM,QAAQ,cAAc,KAAK;EACrC,OAAO;CACT;;CAEA,SAAS,aAA+B;EACtC,IAAI,OAAO,QAAQ,aAAa;EAChC,IAAI,KAAK,WAAW,GAAG,OAAO;EAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAChD,IAAI,KAAK,MAAM,CAAC,SAAS,SAAS,CAAC,CAAC,QAAQ;GAC1C,QAAQ,cAAc,KAAK,MAAM;GACjC,OAAO,KAAK;EACd;EAEF,OAAO,SAAS,GAAG,IAAI;CACzB;;;;;CAKA,SAAS,SAAS,OAAqB;EACrC,IAAI,OAAO,SAAS,CAAC,CAAC;EACtB,IAAI,CAAC,MAAM;EACX,IAAI,QAAQ,QAAQ,UAAU;EAC9B,IAAI,UAAU,GAAG;EACjB,IAAI,UAAU,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;EAC1D,IAAI,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,UAAU,KAAK,GAAG,QAAQ,CAAC;EAC3D,IAAI,aAAa,KAAK,aAAa,CAAC,EAAC,CAAE,OAAO,SAAU,OAAO;GAC7D,OAAO,UAAU;EACnB,CAAC;EACD,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM;GAAE,MAAM;GAAiB;EAAU,CAAC,EAAE,CAAC;CAC7E;;CAEA,SAAS,WAAW,OAAiC;EACnD,IAAI,OAAO,SAAS,CAAC,CAAC;EACtB,IAAI,CAAC,QAAQ,OAAO,UAAU,YAAY,QAAQ,GAAG;EACrD,IAAI,YAAY,KAAK,aAAa,CAAC;EACnC,IAAI,OACF,UAAU,QAAQ,KAAK,KAAK,IACxB,UAAU,OAAO,SAAU,MAAM;GAC/B,OAAO,SAAS;EAClB,CAAC,IACD,UAAU,OAAO,CAAC,KAAK,CAAC;EAC9B,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM;GAAE,MAAM;GAAO,WAAW;EAAK,CAAC,EAAE,CAAC;CACzE;CACA,SAAS,iBAAuB;EAC9B,IAAI,OAAO,SAAS,CAAC,CAAC;EACtB,IAAI,CAAC,MAAM;EACX,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,EAAE,MAAM,KAAK,SAAS,KAAK,CAAC,EAAE,CAAC;EACnE,QAAQ,QAAQ;CAClB;CAEA,OAAO;EACM;EACC;EACF;EACG;EACH;EACA;EACM;EACJ;CACd;AACF;;;;;;;ACjNA,SAAgB,iBAAiB,KAA+B;CAC9D,IAAI,6BAAa,IAAI,IAAuB;;CAE5C,IAAI,kBAAkB;;;;;;;CAQtB,SAAS,gBAAgB,SAAyC;EAChE,IAAI,YAAY,QAAQ,YAAY,QAAW,OAAO;EACtD,IAAI,QAAQ,WAAW,IAAI,aAAa,OAAO,CAAC;EAChD,OAAO,UAAU,SAAY,OAAO;CACtC;;CAGA,SAAS,cAAc,SAA2C;EAChE,IAAI,MAAM,aAAa,OAAO;EAC9B,IAAI,SAAS,WAAW,IAAI,GAAG;EAC/B,IAAI,WAAW,QAAW,OAAO,QAAQ,QAAQ,MAAM;EAEvD,OAAO,QAAQ,QAAQ,CAAC,CACrB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAmB,SAAS,SAAS,EAAE,MAAM,QAAQ,CAAC;EAClF,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,IAAI,QACF,UAAU,OAAO,OAAO,QAAQ,OAAO,QACnC;IAAE,OAAO;IAAS,OAAO,OAAO;GAAM,IACtC;IACE,OAAO;IACP,MACE,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACjF;GACN,IAAI,WAAW,QAAQ,iBAAiB,WAAW,MAAM;GACzD,WAAW,IAAI,KAAK,KAAK;GACzB,OAAO;EACT,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,OAAO;IAAE,OAAO;IAAS,MAAM;GAAY;EAC7C,CAAC;CACL;CAEA,OAAO;EAAmB;EAAgC;CAAc;AAC1E;;;;AC3CA,SAAgB,uBACd,KACA,UACA,KACkB;;;;;;CAMlB,IAAI,+BAAe,IAAI,IAAmD;;CAE1E,IAAI,sBAAsB;;;;;;;;CAQ1B,IAAI,wBAAwB;;CAE5B,SAAS,gBAAgB,WAA4C;EACnE,IAAI,OAAO,cAAc,YAAY,cAAc,IACjD,OAAO,QAAQ,QAAQ,EAAE,WAAW,MAAM,CAAC;EAC7C,IAAI,SAAS,aAAa,IAAI,SAAS;EACvC,IAAI,WAAW,QAAW;GACxB,IAAI,MACF,OAAO,SAAS,OAAO,MAAM,cAAc,OACvC,sBACA;GACN,IAAI,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,KAAK;EACvE;EACA,OAAO,QAAQ,QAAQ,CAAC,CACrB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAqB,SAAS,gBAAgB,EACtE,MAAM,EAAa,UAAU,EAC/B,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,IAAI,QACF,UAAU,OAAO,OAAO,QAAQ,OAAO,QACnC,OAAO,QACP;IAAE,WAAW;IAAkB;GAAU;GAC/C,IAAI,aAAa,QAAQ,IAAI,aAAa,MAAM;GAChD,aAAa,IAAI,WAAW;IAAE,IAAI,KAAK,IAAI;IAAU;GAAM,CAAC;GAC5D,IAAI,WACF,SAAS,CAAC,CAAC,eAAe,SAAS,CAAC,CAAC,YAAY,cAAc,YAC3D,SAAS,CAAC,CAAC,YAAY,QACvB;GAUN,IAAI,EAPF,aAAa,QACb,SAAS,cAAc,MAAM,aAC7B,SAAS,cAAc,MAAM,aAC7B,SAAS,UAAU,MAAM,SACzB,SAAS,WAAW,MAAM,UAC1B,SAAS,UAAU,MAAM,SACzB,SAAS,UAAU,MAAM,QAChB,IAAI,EAAE,aAAa;IAAa;IAAkB;GAAM,EAAE,CAAC;GACtE,OAAO;EACT,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,OAAO;IAAE,WAAW;IAAkB;GAAU;EAClD,CAAC;CACL;;;;;;CAMA,IAAI,kCAAkB,IAAI,IAAqD;CAC/E,IAAI,wBAAwB;CAE5B,SAAS,kBAAkB,WAA8C;EACvE,IAAI,OAAO,cAAc,YAAY,cAAc,IACjD,OAAO,QAAQ,QAAQ;GAAE,WAAW;GAAO,SAAS,CAAC;EAAE,CAAC;EAC1D,IAAI,SAAS,gBAAgB,IAAI,SAAS;EAC1C,IAAI,WAAW,UAAa,KAAK,IAAI,IAAI,OAAO,KAAK,uBACnD,OAAO,QAAQ,QAAQ,OAAO,KAAK;EACrC,OAAO,QAAQ,QAAQ,CAAC,CACrB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAAuB,SAAS,kBAAkB,EAC1E,MAAM;IAAa;IAAW;GAA0B,EAC1D,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,IAAI,QACF,UAAU,OAAO,OAAO,QAAQ,OAAO,QACnC,OAAO,QACP;IAAE,WAAW;IAAkB;IAAW,SAAS,CAAC;GAAE;GAC5D,IAAI,gBAAgB,QAAQ,IAAI,gBAAgB,MAAM;GACtD,gBAAgB,IAAI,WAAW;IAAE,IAAI,KAAK,IAAI;IAAU;GAAM,CAAC;GAC/D,IAAI,WACF,SAAS,CAAC,CAAC,iBAAiB,SAAS,CAAC,CAAC,cAAc,cAAc,YAC/D,SAAS,CAAC,CAAC,cAAc,QACzB;GAQN,IAAI,EANF,aAAa,QACb,SAAS,cAAc,MAAM,cAC5B,SAAS,WAAW,CAAC,EAAC,CAAE,YAAY,MAAM,WAAW,CAAC,EAAC,CAAE,WACzD,SAAS,WAAW,CAAC,EAAC,CAAE,MAAM,SAAU,MAAM,OAAO;IACpD,OAAO,SAAS,MAAM,QAAQ;GAChC,CAAC,IACQ,IAAI,EAAE,eAAe;IAAa;IAAkB;GAAM,EAAE,CAAC;GACxE,OAAO;EACT,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,OAAO;IAAE,WAAW;IAAkB;IAAW,SAAS,CAAC;GAAE;EAC/D,CAAC;CACL;CAEA,OAAO;EAAqB;EAAoC;CAAgB;AAClF;;;;ACjHA,SAAgB,eAAe,UAAoB,KAAyB;;CAE1E,SAAS,QAAQ,KAAa,MAAc,MAA8B;EACxE,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;EAKnD,IAAI,EAAE,KAAK;GAAO;GAAW;GAAM,MAHjC,QAAQ,OAAO,SAAS,WACpB;IAAE,MAAM,KAAK;IAAM,KAAK,KAAK;IAAK,OAAO,KAAK;GAAM,IACpD;IAAE,MAAM;IAAG,KAAK;IAAG,OAAO;GAAE;EACU,EAAE,CAAC;CACjD;;CAGA,SAAS,QAAQ,KAAoB;EACnC,IAAI,UAAU,SAAS,CAAC,CAAC;EACzB,IAAI,YAAY,QAAQ,YAAY,QAAW;EAC/C,IAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,KAAK;EACpD,IAAI,EAAE,KAAK,KAAK,CAAC;CACnB;;;;;;;CAQA,SAAS,YAAY,KAAa,QAAmC;EACnE,OAAO;GACL,gBAAgB,SAAU,OAAO;IAE/B,IAAI,SAAS,SAAS,MAAM,gBAAiB,MAAM,gBAAiC;IACpF,IAAI,OACF,UAAU,OAAO,OAAO,0BAA0B,aAC9C,OAAO,sBAAsB,IAC7B;IACN,QAAQ,KAAK,OAAO,GAAG,IAAI;GAC7B;GACA,gBAAgB,WAAY;IAC1B,QAAQ,GAAG;GACb;GACA,eAAe,WAAY;IACzB,QAAQ,GAAG;GACb;EACF;CACF;CAEA,OAAO;EAAW;EAAkB;EAAsB;CAAY;AACxE;;;;AC7BA,SAAgB,iBAAiB,KAAmB,UAAoB,KAA2B;;CAEjG,IAAI,gBAAyC;;CAE7C,IAAI,eAAwC;;CAE5C,IAAI,aAAkC;;CAEtC,IAAI,eAAuC;CAI3C,IAAI,OAAO,IAAI,WAAW,YACxB,IAAI,OAAO,WAAY;EACrB,OAAO,IAAI,OAAO,CAAC,UAAU,GAAG,SAAU,OAAO;GAC/C,eAAe,MAAM;GACrB,MAAM,IAAI,iBAAiB,EAAE,UAAU,KAAK,CAAC;GAC7C,UAAU,SAAS,CAAC,CAAC,SAAS;GAC9B,OAAO,WAAY;IACjB,eAAe;IACf,YAAY;GACd;EACF,CAAC;CACH,GAAG,wBAAwB;;;;;;;CAS7B,SAAS,cAAoB;EAC3B,IAAI,eAAe,MAAM;EACzB,IAAI;GACF,IAAI,OAAO,WAAW,YAAY,YAAY,WAAW,QAAQ;EACnE,SAAS,OAAO,CAEhB;EACA,aAAa;EACb,IAAI,EAAE,OAAO;GAAE,WAAW;GAAO,SAAS,CAAC;GAAG,QAAQ;EAAK,EAAE,CAAC;CAChE;CAEA,SAAS,UAAU,WAA4C;EAC7D,IAAI,eAAe,QAAQ,WAAW,cAAc,WAAW;EAC/D,YAAY;EACZ,IAAI,CAAC,aAAa,iBAAiB,MAAM;EACzC,IAAI;GACF,IAAI,QAAQ,aAAa,QAAQ,SAAS;GAC1C,IAAI,OAAO,QAAQ,MAAM,UAAU;GACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,eAAe,OAAO,KAAK,YAAY,WAAW,YAAY;GACjF,IAAI,OAAO,KAAK,YAAY,OAAO,aAAa;GAChD,IAAI,CAAC,QAAQ,OAAO,KAAK,gBAAgB,YAAY;GAGrD,IAAI,WAAW;GACf,IAAI,QAAQ,WAAY;IACtB,IAAI;KACF,IAAI,EAAE,OAAO,eAAe,SAAS,YAAY,CAAC,EAAE,CAAC;IACvD,SAAS,OAAO;KACd,MAAM,IAAI,qBAAqB,EAC7B,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAC9D,CAAC;IACH;GACF;GACA,MAAM;GAEN,aAAa;IAAa;IAAW,SADvB,OAAO,SAAS,cAAc,aAAa,SAAS,UAAU,KAAK,IAAI;GAC/B;EACxD,SAAS,OAAO;GAEd,MAAM,IAAI,qBAAqB,EAC7B,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAC9D,CAAC;EACH;CACF;;CAGA,SAAS,eAAe,UAA+B;EAErD,IAAI,SACF,aAAa,QAAQ,OAAO,aAAa,WAAY,WAAmC;EAC1F,IAAI,UAAU,WAAW,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ;EAC9E,IAAI,YAAY,MAAM,OAAO;GAAE,WAAW;GAAO,SAAS,CAAC;GAAG,QAAQ;EAAK;EAC3E,IAAI,UAAkC,CAAC;EACvC,IAAI,SAAwB;EAC5B,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,GAAG;GACtD,IAAI,QAAQ,QAAQ;GACpB,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU;GAEzC,IAAI,OAAO;GACX,IAAI,OAAO,KAAK,SAAS,UAAU;GACnC,IAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,SAAS,GAC1D,QAAQ,KAAK,QAAQ,KAAK;GAC5B,IAAI,WAAW,QAAQ,KAAK,OAAO,QAAQ,SAAS,KAAK;EAC3D;EACA,OAAO;GAAE,WAAW;GAAe;GAAiB;EAAO;CAC7D;;;;;;;CAQA,SAAS,cAAgC;EACvC,IAAI,cAAc,OAAO;EACzB,IAAI,OAAO,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAA6B,SAAS,oBAAoB,EAClF,MAAM,CAAC,EACT,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,eAAe;GACf,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,OAAO;IAChD,IAAI,OAA8C,CAAC;IACnD,IAAI,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAI,OAAO,MAAM,WAAW,CAAC;IAC3E,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;KACnD,IAAI,OAAO,KAAK;KAChB,IAAI,QAAQ,OAAO,KAAK,cAAc,UAAU,KAAK,KAAK,aAAa;IACzE;IACA,MAAM,IAAI,oBAAoB;KAC5B,UAAU,KAAK;KACf,aAAa,OAAO,MAAM,gBAAgB;KAC1C,WAAW,OAAO,OAAO,MAAM,cAAc,WAAW,OAAO,MAAM,YAAY;KACjF,WAAW,OAAO,MAAM,cAAc;IACxC,CAAC;IACD,IAAI,EACF,MAAM;KACJ,OAAO;KACD;KACN,aAAa,OAAO,MAAM,gBAAgB;KAC1C,WAAW,OAAO,MAAM,cAAc;KACtC,WAAW,KAAK,IAAI;IACtB,EACF,CAAC;IACD;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,MAAM,IAAI,2BAA2B,EAAE,MAAM,QAAQ,YAAY,CAAC;GAClE,IAAI,EACF,MAAM;IACJ,OAAO;IACP,MAAM,CAAC;IACP,aAAa;IACb,WAAW;IACX,WAAW,KAAK,IAAI;GACtB,EACF,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,eAAe;GACf,MAAM,IAAI,2BAA2B,EAAE,MAAM,YAAY,CAAC;GAC1D,IAAI,EACF,MAAM;IACJ,OAAO;IACP,MAAM,CAAC;IACP,aAAa;IACb,WAAW;IACX,WAAW,KAAK,IAAI;GACtB,EACF,CAAC;EACH,CAAC;EACH,eAAe;EACf,OAAO;CACT;;CAGA,SAAS,YAA8B;EACrC,IAAI,YAAY,SAAS,CAAC,CAAC;EAC3B,IAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ;EACvC,IAAI,eAAe,OAAO;EAC1B,IAAI,OAAO,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAChB,OAAO,IAAI,WAAW,IAAI,KAA0B,SAAS,iBAAiB,EAC5E,MAAM,EAAa,UAAU,EAC/B,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,gBAAgB;GAChB,IAAI,SAAS,CAAC,CAAC,cAAc,WAAW;GACxC,IAAI,UAAU,OAAO,OAAO,QAAQ,OAAO,OAAO;IAChD,IAAI,QAAQ,OAAO;IAGnB,IAAI,QAAQ;KACV,OAAO;KACP,QAHA,MAAM,UAAU,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;KAIlE,QAAQ,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC;KACtD,QAAQ,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC;KACtD,QAAQ,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC;KACtD,eAAe,OAAO,MAAM,kBAAkB,WAAW,MAAM,gBAAgB;KAC/E,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;KACvD,WAAW,KAAK,IAAI;IACtB;IAEA,IAAI,WAAW,SAAS,CAAC,CAAC;IAU1B,IARE,aAAa,QAAQ,SAAS,WAAW,QAAQ,MAAM,WAAW,OAC9D,SAAS,OAAO,WAAW,MAAM,OAAO,UAAU,SAAS,UAAU,MAAM,QAC3E,aAAa,QACb,SAAS,WAAW,QACpB,MAAM,WAAW,QACjB,SAAS,UAAU,MAAM,SACzB,SAAS,OAAO,WAAW,MAAM,OAAO,WACvC,SAAS,OAAO,WAAW,KAAK,SAAS,OAAO,EAAE,CAAC,OAAO,MAAM,OAAO,EAAE,CAAC,KACvE;IACV,MAAM,IAAI,iBAAiB;KACzB,QAAQ,MAAM,WAAW,OAAO,OAAO,MAAM,OAAO;KACpD,OAAO,MAAM;KACb,OAAO,MAAM,OAAO;KAEpB,QAAQ,MAAM,OAAO;IACvB,CAAC;IACD,IAAI,EAAS,MAAM,CAAC;IACpB;GACF;GACA,IAAI,OAAO,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;GACxF,MAAM,IAAI,wBAAwB,EAAE,MAAM,QAAQ,YAAY,CAAC;GAC/D,IAAI,EACF,OAAO;IACL,OAAO;IACP,QAAQ;IACR,QAAQ,CAAC;IACT,QAAQ,CAAC;IACT,QAAQ,CAAC;IACT,eAAe;IACf,OAAO;IACP,WAAW,KAAK,IAAI;GACtB,EACF,CAAC;EACH,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,gBAAgB;GAChB,MAAM,IAAI,wBAAwB,EAAE,MAAM,YAAY,CAAC;GACvD,IAAI,EACF,OAAO;IACL,OAAO;IACP,QAAQ;IACR,QAAQ,CAAC;IACT,QAAQ,CAAC;IACT,QAAQ,CAAC;IACT,eAAe;IACf,OAAO;IACP,WAAW,KAAK,IAAI;GACtB,EACF,CAAC;EACH,CAAC;EACH,gBAAgB;EAChB,OAAO;CACT;CAEA,OAAO;EACM;EACA;EACE;EACA;CACf;AACF;;;;;;;;;;AChQA,SAAgB,YAAY,KAA6B;CACvD,IAAI,aAAa,iBAAiB,GAAG;CACrC,IAAI,KAAK,OAAO;CAChB,IAAI,MACF,GAAG,OACH,OAAO,GAAG,QAAQ,YAClB,OAAO,GAAG,IAAI,SAAS,YACvB,OAAO,GAAG,IAAI,QAAQ,WAClB;EAAE,MAAM,GAAG,IAAI;EAAM,KAAK,GAAG,IAAI;CAAI,IACrC;CAEN,IAAI,QAAkB;EACpB,OAAO;EACP,WAAW;EACX,KAAK;EACL,QAAQ;EACR,WAAW;EACX,WAAW;EACX,WAAW,GAAG,SAAS;EAClB;;EAEL,WAAW;;EAEX,MAAM;;;;;EAKN,QAAQ;;;;;EAKR,QAAQ;;EAER,SAAS;EACT,YAAY;;;;;EAKZ,MAAM;EACN,QAAQ;EACR,eAAe;;EAEf,WAAW,OAAO,GAAG,cAAc,WAAW,GAAG;;EAEjD,aAAa,OAAO,GAAG,gBAAgB,WAAW,GAAG;;;;;;;EAOrD,gBAAgB;GACd,OAAO;GACP,OAAO;GACP,UAAU;GACV,UAAU,CAAC;GACX,OAAO;GACP,SAAS;GACT,iBAAiB;GACjB,WAAW;EACb;;EAEA,aAAa,GAAG,gBAAgB;;EAEhC,YAAY,GAAG,eAAe;;EAE9B,QAAQ,CAAC;;;;;;EAMT,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ,CAAC;GACT,QAAQ,CAAC;GACT,QAAQ,CAAC;GACT,eAAe;GACf,OAAO;GACP,WAAW;EACb;EAEA,aAAa;GAAE,WAAW;GAAM,OAAO;EAAK;EAE5C,eAAe;GAAE,WAAW;GAAM,OAAO;EAAK;EAI9C,KAAK;;EAEL,MAAM;GAAE,OAAO;GAAQ,MAAM,CAAC;GAAG,aAAa;GAAO,WAAW;GAAO,WAAW;EAAK;;;;;EAKvF,UAAU,aAAa;;;;;;;EAOvB,OAAO;GAAE,WAAW;GAAO,SAAS,CAAC;GAAG,QAAQ;EAAK;CACvD;CAEA,IAAI,4BAAY,IAAI,IAAgB;CACpC,IAAI,WAAoC;;CAGxC,SAAS,cAAwB;EAC/B,OAAO;CACT;CAEA,SAAS,UAAU,UAAkC;EACnD,UAAU,IAAI,QAAQ;EACtB,OAAO,WAAY;GACjB,UAAU,OAAO,QAAQ;EAC3B;CACF;CAEA,SAAS,OAAa;EACpB,UAAU,QAAQ,SAAU,UAAU;GACpC,SAAS;EACX,CAAC;CACH;CAEA,SAAS,IAAI,OAAgC;EAC3C,QAAQ,OAAO,OAAO,CAAC,GAAG,OAAO,KAAK;EACtC,KAAK;CACP;CAGA,IAAI,WAAW,eAAe,WAAY;EACxC,OAAO;CACT,GAAG,GAAG;CACN,IAAI,mBAAmB,uBACrB,KACA,WAAY;EACV,OAAO;CACT,GACA,GACF;CACA,IAAI,aAAa,iBACf,KACA,WAAY;EACV,OAAO;CACT,GACA,GACF;CACA,IAAI,eAAe,mBACjB,KACA,WAAY;EACV,OAAO;CACT,GACA,KACA,EACE,WAAW,WAAY;EACrB,OAAO,UAAU,UAAU;CAC7B,EACF,CACF;CACA,IAAI,mBAAmB,uBACrB,KACA,WAAY;EACV,OAAO;CACT,GACA,KACA;EAEE,WAAW,SAAU,WAAW;GAC9B,OAAO,WAAW,UAAU,SAAS;EACvC;EACA,WAAW,WAAY;GACrB,OAAO,WAAW,UAAU;EAC9B;EACA,SAAS,WAAY;GACnB,OAAO,QAAQ;EACjB;EACA,aAAa,WAAY;GACvB,OAAO,WAAW,YAAY;EAChC;EACe;CACjB,CACF;CACA,IAAI,YAAY,gBACd,KACA,WAAY;EACV,OAAO;CACT,GACA,KACA;EAEa;EACX,gBAAgB,aAAa;EACd;EACN;EACK;CAChB,CACF;CAEA,SAAS,UAAgB;EACvB,IAAI,OAAO,MAAM;EACjB,QAAQ;GACN,aAAa,MAAM,gBAAgB;GACnC,YAAY,MAAM,eAAe;GACjC,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,MAAM,OAAO,KAAK,SAAS,OAAO;GAClC,MACE,QAAQ,OAAO,KAAK,SAAS,YAAY,WAAW,QAAQ,KAAK,IAAI,KAAK,IACtE,KAAK,OACL;GACN,WAAW,MAAM;GACjB,aAAa,MAAM;EACrB,CAAC;CACH;;CAGA,SAAS,eAAe,OAAqB;EAC3C,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;EAC1D,IAAI,OAAO,KAAK,SAAsB,KAAK,MAAM,KAAK,CAAC;EACvD,IAAI,MAAM,gBAAgB,MAAM;EAChC,IAAI,EAAE,aAAa,KAAK,CAAC;CAC3B;;CAGA,SAAS,aAAa,OAAqB;EACzC,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;EAC1D,IAAI,OAAO,KAAK,SAAoB,KAAK,MAAM,KAAK,CAAC;EACrD,IAAI,MAAM,cAAc,MAAM;EAC9B,IAAI,EAAE,WAAW,KAAK,CAAC;CACzB;;;;;;;;;;;CAeA,SAAS,cAAoB;EAC3B,IAAI,MAAM,WAAW,MAAM,OAAO,aAAa,YAAY;EAC3D,OAAO,UAAU,UAAU;CAC7B;;;;;;;CAQA,SAAS,eAA4B;EACnC,IAAI,MAAM,WAAW,MACnB,OAAO,MAAM,QAAQ,MAAM,OAAO,KAAK,IAAI,MAAM,OAAO,QAAQ,CAAC;EAGnE,OAAO,kBAAkB,KAAK;CAChC;;CAGA,SAAS,cAAc,MAAmC;EACxD,IAAI,MAAM,WAAW,MAAM,OAAO,aAAa,eAAe,IAAI;EAClE,OAAO,UAAU,SAAS,IAAI;CAChC;;CAGA,SAAS,YAAoB;EAC3B,IAAI,UAAU,YAAY,MAAM,IAAI;EACpC,IAAI,OAAO,WAAW,QAAQ,OAAO,QAAQ,OAAO,CAAC;EACrD,IAAI,QAAQ;EACZ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAChD,IAAI,KAAK,MAAM,CAAC,SAAS,QAAQ,SAAS;EAE5C,OAAO;CACT;;;;;;;CAQA,SAAS,QAAQ,OAA4B;EAC3C,IAAI,OAAO,MAAM;EACjB,IAAI,CAAC,MAAM;EACX,IAAI,OAAO,UAAU,QAAQ,WAAW,QAAQ,KAAK,KAAK,IAAI,QAAQ;EACtE,IAAI,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;EACrD,QAAQ;CACV;;CAGA,SAAS,aAAa,KAA0B;EAC9C,IAAI,MAAM,cAAc,KAAK;EAC7B,IAAI,EAAE,WAAW,IAAI,CAAC;CACxB;CAEA,SAAS,UAA4B;EAEnC,iBAAiB,mBAAmB;EAEpC,WAAW,UAAU;EAErB,WAAW,UAAU,MAAM,SAAS;EACpC,IAAI,CAAC,MAAM,KAAK,OAAO,QAAQ,QAAQ;EACvC,IAAI,UAAU,OAAO;EACrB,IAAI,MAAM,MAAM;EAEhB,IAAI,EAAE,OAAO,MAAM,SAAS,UAAU,UAAU,CAAC;EAEjD,WAAW,QAAQ,QAAQ,CAAC,CACzB,KAAK,WAAY;GAEhB,OAAO,IAAI,WAAW,IAAI,KAAgB,SAAS,UAAU,EAC3D,MAAM;IAAO;IAAK,QAAQ,MAAM,cAAc;GAAK,EACrD,CAAC;EACH,CAAC,CAAC,CACD,KAAK,SAAU,QAAQ;GACtB,WAAW;GACX,IAAI,MAAM,QAAQ,KAAK;GACvB,IAAI,UAAU,OAAO,OAAO,MAAM;IAChC,IAAI,cAAc,OAAO,SAAS;IAClC,IAAI;KACF,OAAO;KACP,QAAQ;KAER,QACE,eAAe,YAAY,UAAU,OAAO,YAAY,WAAW,WAC/D,YAAY,SACZ,CAAC;KACP,WAAW;KACX,WAAW,KAAK,IAAI;IACtB,CAAC;IAED,IACE,eACA,MAAM,cACL,MAAM,YAAY,QAAQ,MAAM,eAAe,YAAY,MAE5D,aAAa,YAAY,KAAK,YAAY,GAAG;IAG/C,IAAI,eAAe,MAAM,WAAW,aAAa,SAAS,GAAG;IAC7D;GACF;GAEA,IAAI;IAAE,OAAO;IAAS,YADX,UAAU,OAAO,SAAS,OAAO,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO,SAC/C;GAAa,CAAC;EACzD,CAAC,CAAC,CACD,MAAM,WAAY;GACjB,WAAW;GACX,IAAI,MAAM,QAAQ,KAAK;GACvB,IAAI;IAAE,OAAO;IAAS,WAAW;GAAY,CAAC;EAChD,CAAC;EAEH,OAAO;CACT;;CAGA,SAAS,cAAc,MAAsB;EAC3C,OAAO,YAAY,MAAM,KAAK,IAAI;CACpC;;CAGA,SAAS,mBAAyB;EAChC,IAAI,EAAE,YAAY,MAAM,eAAe,KAAK,CAAC;EAC7C,QAAQ;CACV;;CAGA,SAAS,oBAA0B;EACjC,IAAI,EAAE,aAAa,MAAM,gBAAgB,KAAK,CAAC;EAC/C,QAAQ;CACV;CAEA,SAAS,UAAU,MAAqB;EACtC,IAAI,MAAM,cAAc,MAAM;EAC9B,IAAI,EAAE,WAAW,KAAK,CAAC;EACvB,QAAQ;EACR,IAAI,MAAM,QAAQ;CACpB;CAEA,SAAS,cAAoB;EAC3B,UAAU,CAAC,MAAM,SAAS;CAC5B;;CAGA,SAAS,UAAU,MAAsB;EACvC,IAAI,EAAE,KAAK,KAAK,CAAC;CACnB;;CAGA,SAAS,YAAY,MAAsB;EACzC,IAAI,EAAE,KAAK,KAAK,CAAC;EACjB,QAAQ;CACV;CAEA,SAAS,aAAmB;EAC1B,IAAI,EAAE,KAAK,KAAK,CAAC;EACjB,QAAQ;CACV;CAEA,OAAO;EACQ;EACF;EACX,aAAa,iBAAiB;EACrB;EACE;EACE;EACF;EACE;EACD;EACE;EACA;EACE;EAChB,eAAe;EACf,UAAU,UAAU;EACpB,eAAe,WAAW;EAC1B,iBAAiB,WAAW;EAC5B,oBAAoB,iBAAiB;EACrC,iBAAiB,iBAAiB;EAClC,mBAAmB,iBAAiB;EACpC,SAAS,SAAS;EAClB,SAAS,SAAS;EAClB,aAAa,SAAS;EACtB,WAAW,UAAU;EACR;EACb,aAAa,UAAU;EACvB,gBAAgB,UAAU;EACjB;EACT,YAAY,aAAa;EACzB,gBAAgB,aAAa;EAC7B,cAAc,aAAa;EAC3B,aAAa,aAAa;EAC1B,UAAU,UAAU;EACpB,YAAY,UAAU;EACtB,UAAU,UAAU;EACpB,YAAY,UAAU;EACH;EACD;EAClB,aAAa,WAAW;EACxB,WAAW,WAAW;EACtB,gBAAgB,iBAAiB;EACjC,eAAe,iBAAiB;CAClC;AACF;;;;;;;;;;;;;;;;;;;;;AC9cA,SAAgB,YAAY,KAA4B;CACtD,IAAI,QAAQ,kCAAkC,KAAK,GAAG;CACtD,OAAO,UAAU,OAAO,OAAO,MAAM,EAAE,CAAC,KAAK;AAC/C;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAwC;CACtD,IAAI,OAAO,aAAa,aAAa,OAAO;CAC5C,IAAI,WAAW,6BAA4B,aAAa;CAExD,IAAI,MACF,OAAO,SAAS,kBAAkB,aAAa,SAAS,cAAc,QAAQ,IAAI;CAEpF,IAAI,CAAC,KAAK;EACR,MAAM,SAAS,cAAc,OAAO;EAEpC,IAAI,aAAa,eAAe,iBAAiB;EACjD,IAAI,aAAa,mBAAmB,UAAU;EAC9C,IAAI,aAAa,gBAAgB,QAAQ;EACzC,SAAS,KAAK,YAAY,GAAG;CAC/B;CACA,IAAI,cAAc;CAClB,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;AC5BA,IAAW,SAAS;CAAC;CAAS;CAAc;AAAQ;;;;;AAyCpD,SAAgB,MAAM,KAA6B;CACjD,IAAI,OAAO,WAAY;EACrB,OAAO,IAAI,OAAO,SAAS,IAAI;GAAM;GAAQ;EAAG,CAAC;CACnD,GAAG,uBAAuB;CAE1B,IAAI,OAAO,WAAY;EACrB,IAAI,UAAU,aAAa;EAC3B,OAAO,WAAY;GACjB,IAAI,WAAW,QAAQ,YAAY,QAAQ,WAAW,YAAY,OAAO;EAC3E;CACF,GAAG,iBAAiB;CAEpB,IAAI,QAAQ,YAAY,GAAG;CAI3B,MAAM,IAAI,SAAS;EACjB,QAAQ,OAAO,IAAI,WAAW;EAC9B,OAAO,OAAO,IAAI,UAAU;EAG5B,OAAO,YAAY,GAAG;CACxB,CAAC;CAID,IAAI,OAAO,WAAY;EACrB,IAAI,KAAK,OAAO,YAAY,WAAY;GACtC,IAAI,SAAS,oBAAoB,UAAU;GAC3C,aAAa;GACb,MAAM,QAAQ;EAChB,GAAG,OAAO;EACV,OAAO,WAAY;GACjB,OAAO,cAAc,EAAE;EACzB;CACF,GAAG,sBAAsB;CAGzB,IAAI,OAAO,WAAY;EACrB,IAAI,UAAU,WAAY;GACxB,aAAa;GACb,MAAM,QAAQ;EAChB;EACA,OAAO,iBAAiB,SAAS,OAAO;EACxC,OAAO,WAAY;GACjB,OAAO,oBAAoB,SAAS,OAAO;EAC7C;CACF,GAAG,wBAAwB;CAI3B,IAAI,MAAM,OAAO,iBAAiB,WAAY;EAC5C,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,WAAY;IAClB,OAAO,EAAS,MAAM;GACxB;EACF,GACA,OACF;CACF,CAAC;CAED,IAAI,MAAM,OAAO,iBAAiB,WAAY;EAC5C,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,WAAY;IAClB,OAAO,EAAS,MAAM;GACxB;EACF,GACA,gBACF;CACF,CAAC;CAGD,IAAI,OAAO,WAAY;EACrB,MAAM,YAAY;EAClB,IAAI,KAAK,OAAO,YAAY,WAAY;GACtC,IAAI,SAAS,oBAAoB,UAAU;GAC3C,MAAM,YAAY;EACpB,GAAG,YAAY;EACf,OAAO,WAAY;GACjB,OAAO,cAAc,EAAE;EACzB;CACF,GAAG,4BAA4B;CAO/B,IAAI,OAAO,WAAY;EACrB,IAAI,KAAK,OAAO,YAAY,WAAY;GACtC,IAAI,SAAS,oBAAoB,UAAU;GAC3C,MAAM,UAAU;EAClB,GAAG,aAAa;EAChB,OAAO,WAAY;GACjB,OAAO,cAAc,EAAE;EACzB;CACF,GAAG,6BAA6B;CAGhC,IAAI,MAAM,OAAO,wCAAwC,WAAY;EACnE,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,WAAY;IAClB,OAAO,EAAS,MAAM;GACxB;EACF,GACA,kBACF;CACF,CAAC;CAGD,IAAI,MAAM,OAAO,6BAA6B,WAAY;EACxD,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,WAAY;IAClB,OAAO,EAAS,MAAM;GACxB;EACF,GACA,mBACF;CACF,CAAC;CAED,IAAI,MAAM,OAAO,2BAA2B,WAAY;EACtD,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,SAAU,WAAW;IAC3B,OAAO;KAAS;KAAkB;IAAU;GAC9C;EACF,GACA,WACF;CACF,CAAC;CAID,IAAI,MAAM,OAAO,8BAA8B,WAAY;EACzD,OAAO,IAAI,MAAM,SACf;GACE,MAAM;GACN,IAAI;GACJ,OAAO;GACP,QAAQ;GACR,QAAQ,SAAU,WAAW;IAC3B,OAAO;KAAS;KAAkB;IAAU;GAC9C;EACF,GACA,UACF;CACF,CAAC;CAKD,IAAI,OAAO,WAAY;EACrB,OAAO,kBAAkB,KAAK;CAChC,GAAG,gCAAgC;AACrC"}