@corum/corum-ui-base 0.6.0 → 0.7.0

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.
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["forwardRef","useCallback","css","forwardRef","useRef","useCallback","css","useState","createContext","useContext","useState","useRef","useCallback","createPortal","css","css","createPortal","css","Fragment","useState"],"sources":["../src/client/grid.ts","../src/client/RegionCard.module.css","../src/client/RegionCard.tsx","../src/client/GridView.module.css","../src/client/GridView.tsx","../src/client/FloatingLayer.module.css","../src/client/FloatingLayer.tsx","../src/client/ConfirmDialog.module.css","../src/client/ConfirmDialog.tsx","../src/client/theme-presenter.ts","../src/client/mosaic.ts","../src/client/mosaic.module.css","../src/client/MosaicWall.tsx","../src/client/platform-paths.ts","../src/client/web-search-credential.ts"],"sourcesContent":["/**\n * GridView 布局树 —— VSCode editor-group 式自由二维网格的数据模型。\n *\n * 布局是一棵分割树:\n * - Leaf:一个模块窗格(持有 slotKey,渲染对应槽位内容)。\n * - Branch:一个水平(row)或垂直(column) split,含若干子节点 + 各子节点\n * 占比 weight(相对份额,渲染时换算成 px/fr),子节点间是 sash。\n *\n * 本包(shell-base)只提供通用机制:树类型 + 全部树操作 + 槽位注册表 +\n * 构造 helper(leafNode/rowBranch/columnBranch)+ 持久化(key 可配)。\n * 不含任何内置槽位与默认布局——子壳用 registerSlot() 注册自己的槽位、\n * 用构造 helper 写字面量布局树作为初始布局。\n *\n * 这棵树是唯一事实源,持久化到 localStorage(尺寸份额 + 结构)。\n */\n\n/**\n * 可进网格的槽位 key。泛型槽域:默认 `string`(运行时动态注册,行为与历史\n * 完全一致);子壳可用字面量联合实例化(如 ide-shell 的 IdeGridSlot)让\n * registerSlot 等定义处获得拼写/重命名的编译期保障。纯类型层——本文件保持\n * cordis-free(零 import),运行时零变化;树结构 GridNode 仍是 string 槽域\n * (运行时本就可容纳动态注册的任意槽,不做静态收窄)。\n */\nexport type GridSlot<T extends string = string> = T\n\n/** 槽位元数据:显示名 + 默认权重 + 区域最小尺寸(可选)。 */\nexport interface SlotMeta {\n label: string\n defaultWeight: number\n /**\n * 区域最小宽度(px):叶子落在 row 分支(左右并排)时,sash 拖拽收窄的\n * 下限。不声明时用 SLOT_FALLBACK_MIN_WIDTH 兜底,保证基本浏览体验。\n */\n minWidth?: number\n /** 区域最小高度(px):叶子落在 column 分支(上下叠放)时的下限;缺省 SLOT_FALLBACK_MIN_HEIGHT。 */\n minHeight?: number\n /**\n * 钉住(pinned):该槽位的 leaf 在网格中位置/归属固定,不参与自由组合——\n * 不可被 drop 拖走(作 source)、不可被拖入 split/swap(作 target)、不可被\n * 摘除。用于 IDE 侧栏这类「位置固定、其余区域自由组合」的壳。sash 拖拽不\n * 受此限(它只调相邻权重、不移动 leaf 位置;钉住叶独立成列后天然不被其它\n * sash 影响)。缺省 false(全部区域自由组合)。\n */\n pinned?: boolean\n /**\n * 折叠宽(px):该槽位可被「折叠收起」为一条窄轨(如 IDE 侧栏收成 56px\n * 图标轨)。声明后 GridView 经 `collapsedSlots` 运行时状态把它渲染成此固定\n * 宽(不参与 weight 分配、不可 sash 拖),同时 subtreeMinSize 在折叠态取此值\n * (替代 minWidth——窗口自适应不会把它拉回展开宽)。缺省不可折叠。\n */\n collapsedWidth?: number\n /**\n * 可见性(C1 插件自声明槽):\n * - 'fixed' —— 壳/专职插件固定占位的槽(侧栏/对话区/编辑器…),不进\n * 「添加区域」面板与插件中心视图管理;\n * - 'addable' —— 用户可经「添加区域」/视图管理自由拖入网格(缺省值,\n * 与历史行为一致);\n * - 'hidden' —— 无独立 UI 的插件(纯服务/壳自身/测试占位),注册表只\n * 记录、任何清单都不展示。\n * 缺省 'addable'(历史 registerSlot 不带本字段即自动可添加)。\n */\n visibility?: 'fixed' | 'addable' | 'hidden'\n}\n\n/**\n * 槽位注册表面(C1:cordis 服务跨 bundle 单例载体)。\n *\n * 壳(corum-ide-ui)把实现本接口的实例经 `ctx.reflect.provide('slotRegistry')`\n * 注册为 cordis 服务——实例唯一性由 root context `reflect.store` 保证,与各\n * bundle 的模块实例无关(实证 `.dbg/cordis-singleton-probe.md`),故任何插件\n * 在自己 bundle 里调 registerSlot(),壳的 bundle 立即可见。\n *\n * 本文件保持 cordis-free(纯库纪律):接口为结构类型,无任何 cordis import。\n */\nexport interface SlotRegistryFace {\n register<T extends string = string>(key: T, meta: SlotMeta): void\n getMeta(key: string): SlotMeta | undefined\n getAll(): string[]\n}\n\n/**\n * 注册表后端解析(C1 关键设计——模块顶层时序解耦)。\n *\n * 背景:cordis 插件的**模块顶层**在「加载依赖图」阶段执行(此时壳的 apply\n * 尚未跑、服务未 provide),**apply** 才在「激活」阶段执行。ide-layout.ts\n * 的内建槽注册在模块顶层,若用「模块级 backend 变量 + bind 后直读」会把\n * 顶层注册永久锁进 fallback Map(bind 时 backend 还是 null,之后不再重判)。\n *\n * 解法:后端解析推迟到**每次调用时**——\n * 1. 首选 window.__corumSlotRegistry(壳 apply 提供的一次性桥)。\n * **这是合法的 window 挂载**(written once, read-only,规范 §1 例外,与\n * window.corumDesktop / __DSH_BOOT__ 同类),**不是红线 1 禁止的「可变\n * 共享状态挂 window」**——本文件是 cordis-free 纯库(零 cordis import,\n * 被 tsdown 内联进每个 bundle),无法 inject/ctx.get,window 桥是它拿到\n * 壳 `ctx.slotRegistry` 服务实例的**唯一通道**。删除它会让 registerSlot\n * 锁进各 bundle 自己的 fallback Map,slotRegistry 跨 bundle 单例立刻退化\n * 为按 bundle 拆分(回到 C1 之前)。2026-09 统一事件中心二期复核:保留。\n * 2. 桥未挂(无壳组合 / 壳 apply 前的顶层调用)落模块级 fallback Map;\n * 3. 壳 apply 在 provide 后调用 drainPendingSlots() 把 fallback 里的早期\n * 注册合并进服务,保证「顶层早于 apply」的注册最终汇聚到同一实例。\n */\nconst REGISTRY_BRIDGE_KEY = '__corumSlotRegistry'\n\nfunction resolveBackend(): SlotRegistryFace | null {\n if (typeof window === 'undefined') return null\n return (window as unknown as Record<string, SlotRegistryFace | undefined>)[REGISTRY_BRIDGE_KEY] ?? null\n}\n\n/** 模块级 fallback 注册表(服务桥未挂时的暂存 + 无壳组合的永久载体)。 */\nconst slotRegistry = new Map<string, SlotMeta>()\n\n/**\n * 把 fallback 里的早期注册合并进服务(壳 apply 提供桥后调一次)。\n * 合并后清空 fallback,之后所有读写直达服务。\n */\nexport function drainPendingSlots(face: SlotRegistryFace): void {\n for (const [key, meta] of slotRegistry) face.register(key, meta)\n slotRegistry.clear()\n}\n\n/** 注册一个槽位(重复注册覆盖旧元数据)。泛型 T 让子壳把注册 key 收窄进\n * 自己的字面量槽域(拼错即编译错,B2);缺省 string 与历史一致。\n * 写路径每次调用时解析:桥已挂直达服务(跨 bundle 单例);未挂暂存\n * fallback(壳 apply 时 drain 合并)。 */\nexport function registerSlot<T extends string = string>(key: T, meta: SlotMeta): void {\n const backend = resolveBackend()\n if (backend !== null) { backend.register(key, meta); return }\n slotRegistry.set(key, meta)\n}\n\n/** 查询某槽位的元数据。 */\nexport function getSlotMeta(key: string): SlotMeta | undefined {\n const backend = resolveBackend()\n if (backend !== null) return backend.getMeta(key)\n return slotRegistry.get(key)\n}\n\n/** 该槽位是否被钉住(位置/归属固定,不参与 drop 自由组合)。 */\nexport function isPinnedSlot(key: string): boolean {\n return getSlotMeta(key)?.pinned === true\n}\n\n/**\n * 折叠态的空集缺省(调用方不传 collapsed 时用)。\n *\n * P2-2(2026-09-09,.dbg/event-bus-audit-2026-09.md):原实现在本模块持有一个\n * 模块级可变 `collapsedSlots` Set(`setSlotCollapsed` 写 / `leafMinSize` 读)。\n * ui-base 是**被各 bundle 内联的纯库**——同一份源码在每个 bundle 各有一份模块\n * 实例,Set 因此按 bundle 分裂(与 `__corumSidebarMode` 同型风险,当时靠\n * 「写读同 bundle」幸存)。现改为**显式参数**:折叠态由壳(AppFrame 的\n * sidebarCollapsed)持有并传入 grid 数学与 GridView,本模块不再有任何可变状态。\n */\nconst EMPTY_COLLAPSED: ReadonlySet<string> = new Set()\n\n/** 列出所有已注册的槽位 key(有序)。 */\nexport function getAllRegisteredSlots(): string[] {\n const backend = resolveBackend()\n if (backend !== null) return backend.getAll()\n return [...slotRegistry.keys()]\n}\n\n/** 未声明 minWidth 的槽位在 row 分支里的最小宽度兜底(px)——保证基本浏览与交互。 */\nexport const SLOT_FALLBACK_MIN_WIDTH = 200\n\n/** 未声明 minHeight 的槽位在 column 分支里的最小高度兜底(px)——与宽度兜底同为 200 正方形。 */\nexport const SLOT_FALLBACK_MIN_HEIGHT = 200\n\n/** 叶子在指定轴上的最小尺寸:折叠态取 collapsedWidth;否则 SlotMeta 声明优先,缺省走兜底。 */\nfunction leafMinSize(slot: GridSlot, isRow: boolean, collapsed: ReadonlySet<string>): number {\n const meta = getSlotMeta(slot)\n // 折叠态(仅 row 轴的宽度方向生效——折叠收的是宽):min 取折叠宽,\n // 窗口自适应 rescaleGrid 不会把它拉回展开宽。\n if (isRow && collapsed.has(slot) && meta?.collapsedWidth !== undefined) {\n return meta.collapsedWidth\n }\n const declared = isRow ? meta?.minWidth : meta?.minHeight\n return declared ?? (isRow ? SLOT_FALLBACK_MIN_WIDTH : SLOT_FALLBACK_MIN_HEIGHT)\n}\n\n/**\n * 子树在指定轴上的最小尺寸(sash 拖拽 / 窗口自适应 / 渲染夹取三处统一取值):\n * - 同轴分支(row 分支求宽度 / column 分支求高度)= 各子最小之和(并排需同时满足);\n * - 正交分支(row 分支求高度 / column 分支求宽度)= 各子最小的最大值\n * (叠放共用同一主轴宽度,取最宽需求)。\n * 整体隐藏的子树(nodeAllHidden:leaf hidden,或 branch 所有后代 leaf 全 hidden)\n * min=0——它已不占空间,若仍计入 min 会让兄弟格被「隐形 min」顶住、无法在\n * computeCellSizes 里填满(2026-08-28 走查:隐藏编辑器+资源管理器后终端不铺满,\n * 除 detached 误判外,subtreeMinSize 把全隐藏 top-row 的 min 633 也算进 minTotal)。\n * 部分隐藏的 branch 仍按可见子树计 min(可见部分需要下限);单 leaf hidden 同理\n * 取 0。\n */\nexport function subtreeMinSize(node: GridNode, isRow: boolean, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): number {\n if (node.type === 'leaf') return node.hidden === true ? 0 : leafMinSize(node.slot, isRow, collapsed)\n const sizes = node.children.map(c => subtreeMinSize(c, isRow, collapsed))\n return node.direction === (isRow ? 'row' : 'column')\n ? sizes.reduce((a, b) => a + b, 0)\n : sizes.reduce((a, b) => Math.max(a, b), 0)\n}\n\nexport interface LeafNode {\n type: 'leaf'\n id: string\n slot: GridSlot\n /** 用户关闭(不显示):保留在树里(可恢复),渲染时折叠为 0 宽、相邻填满。 */\n hidden?: boolean\n}\n\nexport interface BranchNode {\n type: 'branch'\n id: string\n /** row = 水平排列(左右分,sash 垂直);column = 垂直排列(上下分,sash 水平)。 */\n direction: 'row' | 'column'\n children: GridNode[]\n /** 与 children 等长的相对份额(渲染换算;拖拽 sash 改它)。 */\n weights: number[]\n}\n\nexport type GridNode = LeafNode | BranchNode\n\nlet idSeq = 0\nconst nid = (p: string) => `${p}-${++idSeq}-${Math.random().toString(36).slice(2, 7)}`\n\n// ── 构造 helper(帮子壳省 nid 细节,直接写字面量布局树)────────────────\n\n/** 构造一个叶子节点。 */\nexport function leafNode(slot: GridSlot): LeafNode {\n return { type: 'leaf', id: nid('l'), slot }\n}\n\n/** 构造一个水平分支(左右排列)。weights 缺省时每格 400。 */\nexport function rowBranch(children: GridNode[], weights?: number[]): BranchNode {\n return { type: 'branch', id: nid('b'), direction: 'row', children, weights: weights ?? children.map(() => 400) }\n}\n\n/** 构造一个垂直分支(上下排列)。weights 缺省时每格 400。 */\nexport function columnBranch(children: GridNode[], weights?: number[]): BranchNode {\n return { type: 'branch', id: nid('b'), direction: 'column', children, weights: weights ?? children.map(() => 400) }\n}\n\n/** 深拷贝(操作用纯函数,返回新树)。 */\nexport function cloneNode(node: GridNode): GridNode {\n if (node.type === 'leaf') return { ...node }\n return { ...node, children: node.children.map(cloneNode), weights: [...node.weights] }\n}\n\n/** 按 id 找节点 + 其父分支 + 在父中的下标。 */\nexport function findNode(root: GridNode, id: string): { node: GridNode; parent: BranchNode | null; index: number } | null {\n if (root.id === id) return { node: root, parent: null, index: -1 }\n if (root.type === 'branch') {\n for (let i = 0; i < root.children.length; i++) {\n const found = findNode(root.children[i], id)\n if (found) {\n return found.parent === null && found.node.id === root.children[i].id\n ? { node: found.node, parent: root, index: i }\n : found\n }\n }\n }\n return null\n}\n\n/** 找到某 leaf 的第一个匹配(按 slot)。 */\nexport function findLeafBySlot(root: GridNode, slot: GridSlot): LeafNode | null {\n if (root.type === 'leaf') return root.slot === slot ? root : null\n for (const child of root.children) {\n const found = findLeafBySlot(child, slot)\n if (found) return found\n }\n return null\n}\n\nexport type DropZone = 'left' | 'right' | 'top' | 'bottom' | 'center'\n\n/**\n * 把 `sourceId` 的 leaf 放到 `targetId` 的 leaf 的某个 zone:\n * - 四边:在该方向 split —— 若目标父分支方向与该边一致则直接插入相邻位,\n * 否则把目标 leaf 包成一个新分支(两个子节点),source 插入对应侧。\n * - center:交换两格的 slot。\n * 源格从原位置摘除;剪枝后返回新树。source === target 时 no-op。\n */\nexport function dropLeaf(root: GridNode, sourceId: string, targetId: string, zone: DropZone): GridNode {\n if (sourceId === targetId) return root\n const tree = cloneNode(root)\n const src = findNode(tree, sourceId)\n const tgt = findNode(tree, targetId)\n if (!src || !tgt || src.node.type !== 'leaf' || tgt.node.type !== 'leaf') return root\n\n const srcLeaf = src.node as LeafNode\n const tgtLeaf = tgt.node as LeafNode\n\n // 钉住的 leaf 不参与自由组合:source 是 pinned(不可被拖走)或 target 是\n // pinned(不可被拖入 split/swap)都 no-op。\n if (isPinnedSlot(srcLeaf.slot) || isPinnedSlot(tgtLeaf.slot)) return root\n\n if (zone === 'center') {\n // 交换两格内容。\n const tmp = srcLeaf.slot\n srcLeaf.slot = tgtLeaf.slot\n tgtLeaf.slot = tmp\n return tree\n }\n\n // 先从原位置摘除 source。\n removeLeaf(tree, sourceId)\n // 摘除后 tgt 的父/下标可能变化,重新定位。\n const tgtAfter = findNode(tree, targetId)\n if (!tgtAfter) return tree\n // 根分支被 prune 塌陷成单 leaf(原根只有两格、摘掉一格后剩一格)时\n // tgtAfter.parent === null —— 不能放弃(source 已被摘除,放弃就丢窗格),\n // 把根包成新分支,按 zone 排两个子节点。\n if (tgtAfter.parent === null) {\n const wantDir: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertFirst = (zone === 'left' || zone === 'top')\n const remaining = tree // 此时 tree 已是塌陷后的单 leaf(即 tgt)\n return {\n type: 'branch',\n id: nid('b'),\n direction: wantDir,\n children: insertFirst ? [srcLeaf, remaining] : [remaining, srcLeaf],\n weights: [1, 1], // 包成新根分支:两格均分(render 端按容器 flexBasis,均分即各半)\n }\n }\n const parent = tgtAfter.parent\n const index = tgtAfter.index\n\n const wantDirection: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertBefore = (zone === 'left' || zone === 'top')\n\n if (parent.direction === wantDirection) {\n // 父分支同向:直接插入目标相邻位。\n const at = insertBefore ? index : index + 1\n parent.children.splice(at, 0, srcLeaf)\n // 目标原份额拆分:source 拿目标份额的一半。\n const targetWeight = parent.weights[index] ?? 1\n const srcWeight = targetWeight / 2\n parent.weights[index] = targetWeight / 2\n parent.weights.splice(at, 0, srcWeight)\n } else {\n // 父分支反向:把目标 leaf 包成一个新分支(两个子节点)。weights 是像素\n // (flexBasis)——两格按目标格原份额各半,不能写死 [1,1](那会塌陷成\n // 1px 直到下一次窗口 resize 才修复)。\n const targetWeight = parent.weights[index] ?? 1\n const half = targetWeight / 2\n const wrapper: BranchNode = {\n type: 'branch',\n id: nid('b'),\n direction: wantDirection,\n children: insertBefore ? [srcLeaf, tgtAfter.node] : [tgtAfter.node, srcLeaf],\n weights: [half, half],\n }\n parent.children[index] = wrapper\n }\n return tree\n}\n\n/** 按 slot 设置某 leaf 的 hidden(关闭/恢复显示)。树保留该 leaf,只翻标记。 */\nexport function setLeafHidden(root: GridNode, slot: GridSlot, hidden: boolean): GridNode {\n const tree = cloneNode(root)\n const leaf = findLeafBySlot(tree, slot)\n if (leaf !== null) leaf.hidden = hidden\n return tree\n}\n\n/**\n * 该节点是否「整体隐藏」——leaf 看自身 hidden;branch 看其**所有后代 leaf 是否\n * 全部 hidden**(全隐藏则整支不占空间,兄弟格填满)。GridView 的 BranchView 用它\n * 判定各格 detached:否则外层分支的直接子是嵌套 branch(如 right-col 里的\n * row(editor,explorer))时,其内部 leaf 全 hidden 也判不出,导致该支仍按 weight\n * 占位、兄弟格(终端/对话区)不铺满(2026-08-28 走查两问题根因)。\n * detachedSlots(运行时脱出)只作用于 leaf,branch 不判——脱出是临时态。\n */\nexport function nodeAllHidden(node: GridNode): boolean {\n if (node.type === 'leaf') return node.hidden === true\n return node.children.every(nodeAllHidden)\n}\n\n/** 列出当前关闭(hidden)的槽位。 */\nexport function hiddenSlots(root: GridNode): GridSlot[] {\n const out: GridSlot[] = []\n const walk = (n: GridNode): void => {\n if (n.type === 'leaf') { if (n.hidden === true) out.push(n.slot); return }\n n.children.forEach(walk)\n }\n walk(root)\n return out\n}\n\n/** 列出当前在网格中的所有槽位(含 hidden 的)。 */\nexport function slotsInGrid(root: GridNode): Set<GridSlot> {\n const out = new Set<GridSlot>()\n const walk = (n: GridNode): void => {\n if (n.type === 'leaf') { out.add(n.slot); return }\n n.children.forEach(walk)\n }\n walk(root)\n return out\n}\n\n/** 列出当前不在网格中的槽位(可添加的)。 */\nexport function slotsNotInGrid(root: GridNode): GridSlot[] {\n const inGrid = slotsInGrid(root)\n return getAllRegisteredSlots().filter((s) => !inGrid.has(s))\n}\n\n/**\n * 把一个槽位作为新 leaf 添加到网格末尾(根分支右侧)。\n * 如果根不是分支则包一层。返回新树。\n */\nexport function addSlot(root: GridNode, slot: GridSlot): GridNode {\n const leaf: LeafNode = { type: 'leaf', id: nid('l'), slot }\n const weight = getSlotMeta(slot)?.defaultWeight ?? 400\n if (root.type === 'leaf') {\n return { type: 'branch', id: nid('b'), direction: 'row', children: [root, leaf], weights: [800, weight] }\n }\n const tree = cloneNode(root)\n if (tree.type !== 'branch') return root\n tree.children.push(leaf)\n tree.weights.push(weight)\n return tree\n}\n\n/**\n * 把一个新槽位 drop 到网格中已有 leaf 的某侧(split 或包壳)。\n * 与 dropLeaf 类似,但 source 是新创建的 leaf(不从树里摘除)。\n */\nexport function addSlotAt(root: GridNode, slot: GridSlot, targetId: string, zone: DropZone): GridNode {\n if (zone === 'center') return root // 新槽位不支持 swap\n const tree = cloneNode(root)\n const tgt = findNode(tree, targetId)\n if (!tgt || tgt.node.type !== 'leaf') return root\n\n const newLeaf: LeafNode = { type: 'leaf', id: nid('l'), slot }\n const wantDirection: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertBefore = (zone === 'left' || zone === 'top')\n\n // 钉住的 leaf 不可被拖入 split(新槽位落到它四边会改变它的位置/归属)。\n if (isPinnedSlot(tgt.node.slot)) return root\n\n if (tgt.parent === null) {\n // 目标是根 leaf —— 包成新分支。新叶子按 slot 默认份额,已有叶保留\n // 合理份额(与包壳处「目标份额各半」同思路,基准取 defaultWeight)。\n const newWeight = getSlotMeta(slot)?.defaultWeight ?? 400\n return {\n type: 'branch', id: nid('b'), direction: wantDirection,\n children: insertBefore ? [newLeaf, tree] : [tree, newLeaf],\n weights: insertBefore ? [newWeight, 400] : [400, newWeight],\n }\n }\n\n const parent = tgt.parent\n const index = tgt.index\n\n if (parent.direction === wantDirection) {\n const at = insertBefore ? index : index + 1\n const targetWeight = parent.weights[index] ?? 400\n const half = targetWeight / 2\n parent.weights[index] = half\n parent.children.splice(at, 0, newLeaf)\n parent.weights.splice(at, 0, half)\n } else {\n const targetWeight = parent.weights[index] ?? 400\n const half = targetWeight / 2\n const wrapper: BranchNode = {\n type: 'branch', id: nid('b'), direction: wantDirection,\n children: insertBefore ? [newLeaf, tgt.node] : [tgt.node, newLeaf],\n weights: [half, half],\n }\n parent.children[index] = wrapper\n }\n return tree\n}\n\n/** 从树里摘除一个 leaf 并剪枝(父分支只剩一个子时提升该子)。 */\nexport function removeLeaf(root: GridNode, id: string): GridNode {\n const found = findNode(root, id)\n if (!found || !found.parent) return root\n // 钉住的 leaf 不可被摘除(防御:drop 拖走的 source 拦截已覆盖,这里兜底)。\n if (found.node.type === 'leaf' && isPinnedSlot(found.node.slot)) return root\n const parent = found.parent\n parent.children.splice(found.index, 1)\n parent.weights.splice(found.index, 1)\n return prune(root)\n}\n\n/** 剪枝:任何只剩一个子的分支被该子替换(自底向上)。 */\nexport function prune(node: GridNode): GridNode {\n if (node.type === 'leaf') return node\n node.children = node.children.map(prune)\n // 移除非法空分支。\n node.children = node.children.filter((c) => c.type === 'leaf' || c.children.length > 0)\n if (node.children.length === 1) return node.children[0]\n // 权重长度对齐 children(防御)。\n if (node.weights.length !== node.children.length) {\n node.weights = node.children.map((_, i) => node.weights[i] ?? 1)\n }\n return node\n}\n\n/**\n * 调整某分支里两个相邻子节点间的 sash(带传导推动)。\n * delta>0 = 左/上侧(sashIndex)变大、推向右/下方;delta<0 = 右/下侧变大、\n * 推向左/上方。份额总量守恒,只在被推动的一侧内部转移:\n * - 先与紧贴 sash 的邻格互相消长(邻格最多让到自己的 min);\n * - 邻格顶到 min 后仍有未消化的 delta 时,**沿推动方向向同分支后续格传导**:\n * 下一格让出自己的富余(当前值 − 自身 min),逐格传递,直到 delta 耗尽或\n * 该方向没有任何余量(2026-08-27 用户定调:隔壁到 min 后推动该方向仍有\n * 余量的区域,直到无余量为止)。传导只在同分支内进行,不跨嵌套下钻。\n * 被拉动侧自身也夹到自己的 min(不会为给对侧让路而压过 min)。\n * 各格最小份额 = 各侧子树的 subtreeMinSize(SlotMeta 声明 + 兜底);minWeight\n * 显式传入时覆盖紧贴 sash 的两侧(保留给特殊调方)。\n */\nexport function resizeBranch(root: GridNode, branchId: string, sashIndex: number, delta: number, minWeight?: number, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): GridNode {\n if (delta === 0) return root\n const tree = cloneNode(root)\n const found = findNode(tree, branchId)\n if (!found || found.node.type !== 'branch') return root\n const branch = found.node\n const i = sashIndex\n if (i < 0 || i >= branch.weights.length - 1) return root\n const isRow = branch.direction === 'row'\n const mins = branch.children.map(c => subtreeMinSize(c, isRow, collapsed))\n if (minWeight !== undefined) { mins[i] = minWeight; mins[i + 1] = minWeight }\n\n if (delta > 0) {\n // 左侧 i 要增大:从右侧 i+1, i+2, ... 依次取富余(各让到 min 为止)。\n let want = delta\n // i 自身也有上限?无——i 可以无限增大(只受对侧能让出多少限制)。\n for (let j = i + 1; j < branch.weights.length && want > 0; j++) {\n const slack = Math.max(0, branch.weights[j] - mins[j])\n const give = Math.min(slack, want)\n branch.weights[j] -= give\n want -= give\n }\n const gained = delta - want // 实际从右侧凑到的量\n branch.weights[i] += gained\n } else {\n // 右侧 i+1 要增大:从左侧 i, i-1, ... 依次取富余。\n let want = -delta\n for (let j = i; j >= 0 && want > 0; j--) {\n const slack = Math.max(0, branch.weights[j] - mins[j])\n const give = Math.min(slack, want)\n branch.weights[j] -= give\n want -= give\n }\n const gained = -delta - want\n branch.weights[i + 1] += gained\n }\n return tree\n}\n\n/**\n * 窗口尺寸变化时按当前比例重标定所有分支的 weights 到新总宽(自适应)。\n * weights 是像素;窗口缩放时各列等比缩放,不截断、不溢出。row 分支沿宽度\n * 缩放、column 分支沿高度缩放——按方向分别处理。\n */\nexport function rescaleGrid(node: GridNode, width: number, height: number, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): GridNode {\n const scale = (branch: BranchNode, span: number): void => {\n // 主轴方向各格最小尺寸 = 各子树的 subtreeMinSize(SlotMeta 声明 + 兜底)。\n const isRow = branch.direction === 'row'\n const mins = branch.children.map(c => subtreeMinSize(c, isRow, collapsed))\n const total = branch.weights.reduce((a, b) => a + b, 0)\n if (total <= 0 || span <= 0) return\n // 先按比例分配。\n let ws = branch.weights.map((w) => (w / total) * span)\n // 每格至少自己的 min;但若 Σmin 超过可用空间(窗口太窄),按各格 min 的\n // 比例分配(而非等比平分)——保住各区域声明的最小比,小窗不丢布局、\n // 大窗恢复后仍贴近用户拖的比例,绝不溢出截断。\n const minTotal = mins.reduce((a, b) => a + b, 0)\n if (minTotal >= span) {\n branch.weights = mins.map((m) => (m / minTotal) * span)\n return\n }\n // 正常:夹 min,夹取的差额从仍有富余的格里补给(保持 Σ = span)。\n let deficit = 0\n ws = ws.map((w, i) => {\n if (w < mins[i]) { deficit += mins[i] - w; return mins[i] }\n return w\n })\n if (deficit > 0) {\n const slack = ws.reduce((a, w, i) => a + Math.max(0, w - mins[i]), 0)\n if (slack > deficit) {\n ws = ws.map((w, i) => (w > mins[i] ? w - (Math.max(0, w - mins[i]) / slack) * deficit : w))\n } else {\n // 富余不够补差额(多格同时低于 min):按各格 min 比例分配(与上面\n // minTotal>=span 分支同策略),保证 Σ=span 恒成立、绝不溢出、不丢最小比。\n branch.weights = mins.map((m) => (m / minTotal) * span)\n return\n }\n }\n branch.weights = ws\n }\n const walk = (n: GridNode, w: number, h: number): void => {\n if (n.type === 'leaf') return\n const isRow = n.direction === 'row'\n scale(n, isRow ? w : h)\n // 子分支的正交尺寸 = 父分支该方向尺寸;子分支沿其主轴的尺寸 = 它自己的 weight。\n n.children.forEach((child, i) => {\n const cw = isRow ? n.weights[i] : w\n const ch = isRow ? h : n.weights[i]\n walk(child, cw, ch)\n })\n }\n const tree = cloneNode(node)\n walk(tree, width, height)\n return tree\n}\n\n// ── 持久化 ────────────────────────────────────────────────────────────\n\n/** 默认持久化 key;子壳应传自己的 key(如 ide-shell 用 'corum.ide.grid.v3')。 */\nexport const DEFAULT_GRID_STORAGE_KEY = 'corum.grid.v1'\n\n/** 序列化树(weights 保留两位小数)。 */\nexport function serializeGrid(node: GridNode): string {\n const strip = (n: GridNode): unknown => n.type === 'leaf'\n ? (n.hidden === true ? { t: 'l', slot: n.slot, h: 1 } : { t: 'l', slot: n.slot })\n : { t: 'b', d: n.direction, c: n.children.map(strip), w: n.weights.map((w) => Math.round(w * 100) / 100) }\n return JSON.stringify(strip(node))\n}\n\n/** 反序列化(重新发 id,结构非法时返回 null,由调用方回退到自己的默认布局)。\n * `topLevelRow` 可选:传子壳默认布局的顶层 row 拓扑签名(叶子=槽名、\n * 分支='*')——持久化的顶层 row 与默认签名不同(如旧版把 conversation\n * 嵌进了 right-col 内部)时**整树废弃回退默认**,不沿用旧拓扑。否则旧\n * 布局(对话区被裹进 right-col 分支)会被 root leafTopOffset 按「非叶子\n * 格 offset=0」整支抬顶,撞破只压左列的标题栏浮层(2026-09-04:编辑器/\n * 资源管理器没有顶在窗口最上面、反而压在会话区标题栏下方的根因)。 */\nexport function deserializeGrid(json: string, topLevelRow?: readonly string[]): GridNode | null {\n try {\n const build = (raw: unknown): GridNode | null => {\n if (typeof raw !== 'object' || raw === null) return null\n const r = raw as Record<string, unknown>\n if (r.t === 'l' && typeof r.slot === 'string') {\n // 槽位已不在注册表(插件被移除等)——丢弃该叶子,避免永久显示\n // 「此区域暂无内容」且无法从「添加区域」清除。\n if (getSlotMeta(r.slot) === undefined) return null\n return { type: 'leaf', id: nid('l'), slot: r.slot as GridSlot, ...(r.h === 1 ? { hidden: true } : {}) }\n }\n if (r.t === 'b' && (r.d === 'row' || r.d === 'column') && Array.isArray(r.c) && Array.isArray(r.w)) {\n const children = r.c.map(build).filter((c): c is GridNode => c !== null)\n if (children.length === 0) return null\n return { type: 'branch', id: nid('b'), direction: r.d, children, weights: r.w.map((w) => (typeof w === 'number' ? w : 1)) }\n }\n return null\n }\n const tree = build(JSON.parse(json))\n if (tree === null) return null\n // 顶层 row 拓扑守卫:与默认布局的顶层槽序不一致即废弃(见上注释)。\n if (topLevelRow !== undefined) {\n if (tree.type !== 'branch' || tree.direction !== 'row') return null\n // 顶层格的拓扑签名:叶子=槽名、分支='*'(分支位置可变——right-col 等\n // 分支格允许存在,但叶子槽序必须与默认一致;旧布局把叶子嵌进分支\n // 内部时槽序对不上即废弃)。分支格内的结构(嵌套/权重)不校验——\n // 用户拖拽重组自由,只保证顶层叶子仍直接挂在 root row 下。\n const sig = tree.children.map((c) => (c.type === 'leaf' ? c.slot : '*'))\n if (sig.length !== topLevelRow.length || sig.some((s, i) => s !== topLevelRow[i])) return null\n }\n // 对齐 weights/children 长度并剪枝(含 slot 校验丢弃叶子后的塌陷)。\n const pruned = prune(tree)\n // 整树被剪空(如只剩一个未注册 slot 的叶子)时回退默认布局。\n if (pruned.type === 'branch' && pruned.children.length === 0) return null\n return pruned\n } catch {\n return null\n }\n}\n\n/**\n * 读持久化布局;无或非法时回退 `fallback()`(子壳的默认布局)。\n * key 缺省 DEFAULT_GRID_STORAGE_KEY;子壳传自己的 key 以隔离/兼容存量布局。\n */\nexport function loadGrid(fallback: () => GridNode, key: string = DEFAULT_GRID_STORAGE_KEY): GridNode {\n if (typeof localStorage === 'undefined') return fallback()\n const raw = localStorage.getItem(key)\n if (raw === null) return fallback()\n // 从子壳默认布局提取顶层 row 的拓扑签名(叶子=槽名、分支='*')作为守卫\n // 基准——默认布局本身是子壳特化(如 ide-layout 的 [sidebar, conversation,\n // right-col(分支)]),本包不硬编码业务槽名。默认布局顶层不是 row 时不施加\n // 守卫。\n const def = fallback()\n const topLevelRow = def.type === 'branch' && def.direction === 'row'\n ? def.children.map((c) => (c.type === 'leaf' ? c.slot : '*'))\n : undefined\n return deserializeGrid(raw, topLevelRow) ?? fallback()\n}\n\n/** 写持久化布局。key 缺省 DEFAULT_GRID_STORAGE_KEY。 */\nexport function saveGrid(node: GridNode, key: string = DEFAULT_GRID_STORAGE_KEY): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(key, serializeGrid(node))\n } catch { /* quota/private mode — non-fatal */ }\n}\n\n/** 清空持久化(恢复子壳默认布局)。key 缺省 DEFAULT_GRID_STORAGE_KEY。 */\nexport function resetGridStorage(key: string = DEFAULT_GRID_STORAGE_KEY): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(key)\n}\n","/* RegionCard —— 标准区域卡片基座。背景/描边走 token 兜底:\n 未定义 --corum-glass-*(子壳主题变量)时下落到官方 --dsw-alias-*。 */\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n background: var(--corum-glass-1, var(--dsw-alias-bg-layer-1));\n border: 1px solid var(--corum-glass-border, var(--dsw-alias-border-l1));\n border-radius: 18px;\n}\n\n/* 透明变体:子壳内容子卡独立、间隙透出背景时启用——基座只留定位/裁剪,\n 不垫背景。 */\n.cardTransparent {\n background: transparent;\n border-color: transparent;\n}\n\n.body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.body > * {\n flex: 1;\n min-height: 0;\n}\n","/**\n * RegionCard —— 区域的标准卡片基座。\n *\n * 所有区域(子壳自行定义的槽位)都通过这个基座渲染,保证统一的卡片外观\n * 与区域级操作:\n * - 卡片背景/描边走 token 兜底(--corum-glass-1 → --dsw-alias-bg-layer-1),\n * shell-base 的极简主题下落到官方 token;子壳(如 ide-shell)可用自己的\n * 主题变量覆盖成玻璃质感\n * - 内容区非交互处按下并拖出窗口 → 触发拖出(组件交互/文字选择不受影响)\n */\nimport { forwardRef, useCallback } from 'react'\nimport type { ReactNode } from 'react'\nimport css from './RegionCard.module.css'\n\n/** RegionCard 的 props。 */\nexport interface RegionCardProps {\n /** 区域内容(slot 渲染结果)。 */\n children: ReactNode\n /** 拖出为浮动窗(区域内非交互处长按左键拖拽出窗口时触发;缺省禁用拖出)。 */\n onPopOut?: (() => void) | undefined\n /** 透传给卡片根的 data-slot(用于 grid 定位/调试)。 */\n slotKey?: string | undefined\n /** 内容区附加 className(区域自定义内容布局)。 */\n bodyClassName?: string | undefined\n /** 透明变体:不画整卡背景/描边(子壳内容子卡独立、间隙透出背景时启用)。 */\n transparent?: boolean | undefined\n}\n\n/**\n * 交互元素判定清单(拖拽让位):这些内容上的按下/拖拽交给组件自身交互或\n * 文字选择,不触发区域拖拽/网格重排。GridView 的 dragstart 让位与本文件的\n * mousedown 让位共用此常量,避免两处清单漂移。\n *\n * 覆盖(2026-08-30 补全):原生交互控件(button/a/input/textarea/select/img/\n * summary/label/audio/video)+ 内容编辑(contenteditable)+ Monaco\n * ([data-monaco-editor] 宿主 + .monaco-editor 内核类——宿主可能因空态未挂载,\n * 内核类兜底已挂载场景)+ 常见 ARIA 交互角色(button/textbox/link/checkbox/\n * radio/slider/spinbutton/combobox/listbox/option/menuitem 系/tab/switch/\n * searchbox/treeitem/scrollbar)+ 显式标记 data-interactive。\n * 故意不含 [draggable=\"true\"]——GridView 整叶根本身 draggable,含它会让每次\n * dragstart 命中整叶根被 preventDefault、整叶永远拖不动(见 GridView 注释)。\n */\nexport const INTERACTIVE_SELECTOR = [\n 'button',\n 'a',\n 'input',\n 'textarea',\n 'select',\n 'img',\n 'summary',\n 'label',\n 'audio',\n 'video',\n '[contenteditable=\"true\"]',\n '[data-monaco-editor]',\n '.monaco-editor',\n '[data-interactive]',\n '[role=\"button\"]',\n '[role=\"textbox\"]',\n '[role=\"link\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"slider\"]',\n '[role=\"spinbutton\"]',\n '[role=\"combobox\"]',\n '[role=\"listbox\"]',\n '[role=\"option\"]',\n '[role=\"menuitem\"]',\n '[role=\"menuitemcheckbox\"]',\n '[role=\"menuitemradio\"]',\n '[role=\"tab\"]',\n '[role=\"switch\"]',\n '[role=\"searchbox\"]',\n '[role=\"treeitem\"]',\n '[role=\"scrollbar\"]',\n].join(', ')\n\n/**\n * 卡片容器清单(2026-08-30 用户定调:卡片整体不可拖——卡片上按下拖动 = 选中\n * 文字或无反应,不触发区域拖拽/网格重排)。命中:消息气泡 / 工具聚合卡 /\n * 审批卡 / Review 卡 / 子 Agent 卡 / 会话行 / 文件树行 / 项目行等。CSS\n * Modules 哈希前缀稳定(如 _9Q52kG_sr / LRfKSG_bubble),用语义子串的\n * 属性前缀选择器匹配([class*=\"_sr\"] 等)。GridView 的 dragstart 让位与\n * 本文件的 mousedown 让位共用。\n */\nexport const CARD_SELECTOR = [\n // 会话区消息卡(corum-ui-chat / corum-ui-approval 各卡片根)。\n '[class*=\"_bubble\"]',\n '[class*=\"_reviewCard\"]',\n '[class*=\"_approvalCard\"]',\n '[class*=\"_subagentCard\"]',\n '[class*=\"_turnProcess\"]',\n '[class*=\"_toolCard\"]',\n // AI 消息体(fork corum-ui-chat 的 assistant 消息根 qsr5ja_* + markdown 正文\n // _markdown_* / _plain_* / _plainRun_*)——AI 结论文本块整体不可拖(选中\n // 文字)。这是「秋日五绝结论仍是拖动区域」的根因:旧清单只认 _bubble/_card,\n // fork 的 ai 消息用 qsr5ja_/markdown 命名,整条命中链无一命中。\n '[class*=\"qsr5ja_\"]',\n '[class*=\"_markdown_\"]',\n '[class*=\"_plain_\"]',\n '[class*=\"_plainRun_\"]',\n // 消息流条目容器(_Yx6sq_flowItem = ChatView 的每条消息包装)。\n '[class*=\"_flowItem\"]',\n // 侧栏会话行 / 组行 / 历史行 / 团队行 / 管理行。\n '[class*=\"_sr\"]',\n '[class*=\"_groupRow\"]',\n '[class*=\"_historyRow\"]',\n '[class*=\"_teamSr\"]',\n '[class*=\"_manageRow\"]',\n // 文件树行 / 项目行。\n '[class*=\"_treeRow\"]',\n '[class*=\"_projectRow\"]',\n].join(', ')\n\n/** 交互元素判定:这些内容上的按下不触发区域拖拽(交给组件自身交互/文字选择)。 */\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n return target.closest(INTERACTIVE_SELECTOR) !== null\n}\n\n/** 卡片容器判定:卡片上的按下不触发区域拖拽(用户定调:卡片整体不可拖)。 */\nexport function isCardTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n return target.closest(CARD_SELECTOR) !== null\n}\n\n/**\n * 标准区域卡片基座。forwardRef 暴露卡片根(GridView 需要 ref 测量 drop zone)。\n */\nexport const RegionCard = forwardRef<HTMLDivElement, RegionCardProps>(function RegionCard(props, ref) {\n const { children, onPopOut, slotKey, bodyClassName, transparent } = props\n\n // 非交互处按下并拖拽 → 脱出为浮动窗。拖出窗口可视区(坐标越界)触发 onPopOut;\n // 窗口内释放 / 位移太小(当作文字选择或偶然拖动)则取消,不移动内容。\n const onBodyMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (e.button !== 0 || !onPopOut) return\n if (isInteractiveTarget(e.target) || isCardTarget(e.target)) return\n const selection = window.getSelection()\n if (selection !== null && !selection.isCollapsed) return // 用户在选文字,让位\n const startX = e.clientX\n const startY = e.clientY\n let armed = false // 是否已超过拖拽阈值(避免点击/选文字被误判成拖拽)\n const DRAG_THRESHOLD = 6\n const style = document.createElement('style')\n const arm = () => {\n if (armed) return\n armed = true\n style.textContent = `* { cursor: grabbing !important; user-select: none !important; -webkit-user-select: none !important; }`\n document.head.appendChild(style)\n }\n const onMove = (ev: MouseEvent) => {\n if (!armed && Math.hypot(ev.clientX - startX, ev.clientY - startY) < DRAG_THRESHOLD) return\n arm()\n ev.preventDefault()\n }\n const onUp = (ev: MouseEvent) => {\n window.removeEventListener('mousemove', onMove, true)\n window.removeEventListener('mouseup', onUp, true)\n style.remove()\n if (!armed) return // 未达阈值:当作普通点击/选择,不脱出\n const outX = ev.clientX <= 0 || ev.clientX >= window.innerWidth\n const outY = ev.clientY <= 0 || ev.clientY >= window.innerHeight\n if (outX || outY) onPopOut()\n }\n window.addEventListener('mousemove', onMove, true)\n window.addEventListener('mouseup', onUp, true)\n }, [onPopOut])\n\n return (\n <div ref={ref} className={`${css.card}${transparent ? ` ${css.cardTransparent}` : ''}`} data-slot={slotKey}>\n {/* 内容区:非交互处按下并拖出窗口 → 拖出为浮动窗。 */}\n <div className={bodyClassName ? `${css.body} ${bodyClassName}` : css.body} onMouseDown={onBodyMouseDown}>\n {children}\n </div>\n </div>\n )\n})\n","/* GridView —— 自由二维网格。token-only。 */\n\n.grid {\n position: relative;\n height: 100%;\n width: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n}\n\n/* VSCode SplitView 式绝对定位:分支容器 relative,每格 absolute,位置/尺寸\n 由 JS 计算并直接写 DOM style(left/top/width/height)。不再用 flex。 */\n.branchRow,\n.branchColumn {\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n}\n\n.branchCell {\n position: absolute;\n /* 最小宽度约束由 JS 布局夹取(computeCellSizes 的 MIN_MAIN_SIZE),CSS 不再\n 用 min-width——绝对定位下 min-width 会与 JS 写入的 width 冲突造成溢出。 */\n min-height: 0;\n min-width: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* 顶层 row 格的内容让位包装(leafTopOffset):JS 写入 top/height(内容格\n 顶部下移、高度缩短——如 sidebar/conversation 让位标题栏行,right-col 顶到\n 容器顶)。格自身仍绝对定位占满全高,包装在内层绝对定位。\n 注意不设 overflow:hidden——浮动窗拖回的 dock 预览(dropHint/dockPreview)\n 需要能透出到格边界外(overflow 在 .leaf 层裁剪)。 */\n.branchInner {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n min-height: 0;\n min-width: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* ── sash(沿用 VSCode Sash 视觉)── */\n.sashV {\n position: absolute; /* JS 写 left/top/height;命中区 8px 宽压在缝上 */\n width: 8px;\n cursor: col-resize;\n z-index: 35;\n touch-action: none;\n}\n\n.sashH {\n position: absolute; /* JS 写 left/top/width */\n height: 8px;\n cursor: row-resize;\n z-index: 35;\n touch-action: none;\n}\n\n.sashV::after,\n.sashH::after {\n content: '';\n position: absolute;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0;\n transition: opacity 120ms ease;\n border-radius: 2px;\n}\n\n.sashV::after {\n top: 0; bottom: 0; left: 50%; width: 3px; margin-left: -1.5px;\n}\n\n.sashH::after {\n left: 0; right: 0; top: 50%; height: 3px; margin-top: -1.5px;\n}\n\n.sashV:hover::after,\n.sashH:hover::after {\n opacity: 1;\n}\n\n/* ── 叶子窗格 ──\n .leaf 只是 grid 拖放高亮的定位包装(drop zone / dropHint 的锚),卡片\n 外观与区域操作按钮由 RegionCard 基座承载。\n overflow:hidden 裁剪叶内容(如 Monaco 的 16777214px 虚拟画布),避免\n HTML5 拖拽 ghost 快照把这些巨大画布一并拍进去、覆盖相邻区域。 */\n.leaf {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n /* 创建独立合成层,隔离 leaf 的渲染——否则 HTML5 拖拽的 ghost 快照会把相邻\n leaf 一起拍进去(manifest 为「拖动带动相邻区域」)。\n 注意:本层 + 0.1.2 会话区换肤的 backdrop-filter 玻璃层组合会让 Chromium\n 的整页 native ghost 失效(空态拍进相邻区域 / 非空态无 ghost),故\n GridView 改为 dragstart 克隆 leaf 作显式 drag image(见 .dragGhost)。 */\n will-change: transform;\n /* Chromium 对 draggable=true 元素的默认行为:整棵子树 user-select:none\n (实测:leaf draggable=true → 子树全 none,去掉 draggable 立即回 auto)——\n 这会让「draggable 整叶」里的文本(结论正文/消息/树行)无法选中。显式覆盖\n 回 text:文本恢复可选中;拖拽让位由 GridView 的 CARD_SELECTOR/INTERACTIVE\n 判定(卡片/交互上 preventDefault dragstart)保证「可选中且不拖」并存。 */\n user-select: text;\n}\n\n/* 拖拽时的显式 drag image 快照(GridView dragstart 克隆的 leaf):必须是干净的\n 静态快照——剥离一切 backdrop-filter(玻璃层会让 Chromium 快照失效/半透明穿帮),\n 快照自身不再 blur(拍的是「当前画面」,底层内容在快照生成时已定格)。 */\n[data-drag-ghost],\n[data-drag-ghost] * {\n backdrop-filter: none !important;\n}\n\n/* 已脱出占位卡。 */\n.detachedPlaceholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(6px * var(--corum-density-scale, 1));\n height: 100%;\n color: var(--dsw-alias-label-tertiary);\n font-size: calc(12px * var(--corum-ui-font-scale, 1));\n}\n\n.detachedIcon {\n font-size: calc(20px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-brand-primary);\n}\n\n.detachedHint {\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* drop 高亮:四边或中心。 */\n.dropHint {\n position: absolute;\n inset: 0;\n pointer-events: none;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0.16;\n z-index: 5;\n}\n\n.dropHint[data-zone='left'] { right: 50%; border-radius: 14px 0 0 14px; }\n.dropHint[data-zone='right'] { left: 50%; border-radius: 0 14px 14px 0; }\n.dropHint[data-zone='top'] { bottom: 50%; border-radius: 14px 14px 0 0; }\n.dropHint[data-zone='bottom'] { top: 50%; border-radius: 0 0 14px 14px; }\n.dropHint[data-zone='center'] { inset: 0; border-radius: 14px; opacity: 0.22; }\n\n/* 正在被拖的窗格(HTML5 DnD 源)略透明。 */\n.leaf[data-drop-zone] {\n outline: 2px solid var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n outline-offset: -2px;\n}\n\n/* 浮动窗拖回的实时插入预览块。 */\n.dockPreview {\n position: absolute;\n pointer-events: none;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0.22;\n border: 2px solid var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n border-radius: 12px;\n z-index: 40;\n transition: left 80ms ease, top 80ms ease, width 80ms ease, height 80ms ease;\n}\n","/**\n * GridView —— 递归渲染布局树:分支(row/column split)按 weights 分配空间,\n * 相邻子节点间一条 sash(复用 VSCode Sash 拖拽机制);叶子是模块窗格\n * (标题栏可拖 + 槽位内容)。拖标题到另一窗格四边 → split,中心 → 交换。\n *\n * 纯组件:树与回调经 props 传入,槽位内容经 renderSlot 解析。\n */\nimport { forwardRef, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { BranchNode, DropZone, GridNode, GridSlot, LeafNode } from './grid.ts'\nimport { subtreeMinSize, isPinnedSlot, getSlotMeta } from './grid.ts'\nimport { RegionCard, INTERACTIVE_SELECTOR, CARD_SELECTOR } from './RegionCard.tsx'\nimport css from './GridView.module.css'\n\n/** GridView 的整体 props。 */\nexport interface GridViewProps {\n root: GridNode\n /** 槽位内容渲染(壳的 renderSlot)。 */\n renderSlot: (slot: GridSlot) => ReactNode\n /** sash 拖拽:branchId + 子节点下标 + 份额 delta。 */\n onResize: (branchId: string, sashIndex: number, deltaFraction: number) => void\n /** 拖放:source leaf id → target leaf id 的 zone。 */\n onDrop: (sourceId: string, targetId: string, zone: DropZone) => void\n /** 脱出某槽位到浮动窗(可选)。 */\n onPopOut?: (slot: GridSlot) => void\n /** 关闭某槽位(不显示,可在状态栏恢复)。 */\n onClose?: (slot: GridSlot) => void\n /** 从面板拖入新槽位到 target leaf 的某侧。 */\n onDropNewSlot?: (slot: GridSlot, targetId: string, zone: DropZone) => void\n /** 已脱出到浮动窗的槽位集合——运行时折叠(树不动)。 */\n detachedSlots?: ReadonlySet<string>\n /** 透明整卡的槽位(子卡独立、间隙透出背景,不垫外层玻璃卡)。由子壳按设计注入。 */\n transparentSlots?: ReadonlySet<string>\n /** 顶层 row 分支各格内容顶部下移像素(design.pen:titlebar-row 只压左列,\n * sidebar/conversation 格内容让位标题栏,right-col 格顶到容器顶)。\n * 沿主轴按 child 下标取数(root row 的格序固定)。 */\n leafTopOffset?: readonly number[]\n /**\n * 折叠收起的槽位集合(如 IDE 侧栏收成 56px 图标轨):这些 leaf 渲染为各自\n * SlotMeta.collapsedWidth 的固定宽(不参与 weight 分配、两侧 sash 隐藏不可\n * 拖),不再是 detached 的 0 宽。P2-2:折叠态由本 prop 显式传入\n * (grid.ts 不再持有模块级折叠 Set),subtreeMinSize 在折叠态取\n * collapsedWidth,窗口自适应不会拉回展开宽。\n */\n collapsedSlots?: ReadonlySet<string>\n /**\n * 运行时锁定宽(px):slot → 固定宽。与 collapsedSlots 的 locked 同机制\n * (locked 格宽度锁定、不参与 weight 分配、两侧 sash 隐藏),但宽度由壳\n * 直接给(不走 SlotMeta.collapsedWidth 注册表)。用于「某区域临时锁为\n * 固定宽」的壳层场景——如 IDE 右侧三区域全隐藏时,侧栏锁为 300(而非按\n * 300:509 占比被拉宽,2026-08-30 用户定调 A)。\n */\n lockedSlots?: ReadonlyMap<string, number>\n}\n\n/** 每格主轴最小尺寸来自各子树的 subtreeMinSize(grid.ts 注册表 + 兜底),本文件不再持有硬编码常量。 */\n\n/** 一条 sash(沿用 AppFrame 验证过的 VSCode 机制);ref 透传给分支布局写位置。 */\nconst Sash = forwardRef<HTMLDivElement, { direction: 'row' | 'column'; onDrag: (delta: number) => void }>(function Sash(props, ref) {\n const vertical = props.direction === 'row' // row 分支的 sash 是竖条(拖左右)\n const origin = useRef(0)\n const cbRef = useRef(props.onDrag)\n cbRef.current = props.onDrag\n\n const onMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (e.button !== 0) return\n e.preventDefault()\n origin.current = vertical ? e.clientX : e.clientY\n const cursor = vertical ? 'col-resize' : 'row-resize'\n const style = document.createElement('style')\n style.textContent = `* { cursor: ${cursor} !important; user-select: none !important; -webkit-user-select: none !important; }`\n document.head.appendChild(style)\n const onMove = (ev: MouseEvent) => {\n ev.preventDefault()\n cbRef.current((vertical ? ev.clientX : ev.clientY) - origin.current)\n origin.current = vertical ? ev.clientX : ev.clientY\n }\n const onUp = () => {\n window.removeEventListener('mousemove', onMove, true)\n window.removeEventListener('mouseup', onUp, true)\n style.remove()\n }\n window.addEventListener('mousemove', onMove, true)\n window.addEventListener('mouseup', onUp, true)\n }, [vertical])\n\n return <div ref={ref} className={vertical ? css.sashV : css.sashH} onMouseDown={onMouseDown} data-sash={props.direction} />\n})\n\n/** 一个叶子窗格:标准区域卡片(RegionCard 基座)+ grid 拖放高亮。 */\nfunction LeafView(props: {\n leaf: LeafNode\n renderSlot: (slot: GridSlot) => ReactNode\n onDrop: GridViewProps['onDrop']\n onPopOut?: GridViewProps['onPopOut']\n onDropNewSlot?: GridViewProps['onDropNewSlot']\n transparentSlots?: ReadonlySet<string> | undefined\n}) {\n const { leaf, renderSlot, onDrop, onPopOut, onDropNewSlot, transparentSlots } = props\n // 钉住的 leaf(如 IDE 侧栏)不参与自由组合:不作拖拽源(不可拖走/脱出)、\n // 不响应 dragover/drop(不可被拖入 split/swap)。\n const pinned = isPinnedSlot(leaf.slot)\n const [zone, setZone] = useState<DropZone | null>(null)\n /** 当前叶子是否为拖拽源:拖拽源自身不响应 dragover(落到自己是 no-op,不该高亮)。 */\n const [sourceDragging, setSourceDragging] = useState(false)\n const ref = useRef<HTMLDivElement | null>(null)\n /** 当前拖拽的 ghost 克隆移除句柄(dragstart 设置,dragend 清理——所有结束路径)。 */\n const ghostCleanupRef = useRef<(() => void) | null>(null)\n\n const zoneFromPoint = (clientX: number, clientY: number): DropZone => {\n const el = ref.current\n if (el === null) return 'center'\n const r = el.getBoundingClientRect()\n const fx = (clientX - r.left) / r.width\n const fy = (clientY - r.top) / r.height\n const EDGE = 0.25\n if (fx < EDGE) return 'left'\n if (fx > 1 - EDGE) return 'right'\n if (fy < EDGE) return 'top'\n if (fy > 1 - EDGE) return 'bottom'\n return 'center'\n }\n\n // 接受两种 drag 类型:已有窗格拖拽(corum/leaf-id)和面板新槽位拖入(corum/new-slot)\n const hasDropType = (types: readonly string[]) => types.includes('corum/leaf-id') || types.includes('corum/new-slot')\n\n // 网格内重排 + 拖出浮动窗的统一拖拽源:整叶 draggable(设计稿无区域标题栏)。\n // 让位只排除「明确交互控件 + 已选中文字」,其余区域按下拖动即发起\n // corum/leaf-id:窗口内释放 → 网格内 split/swap;拖出窗口外 → 浮动窗。\n //\n // 关键(2026-08-30 实测根因):原生 HTML5 DnD 的 dragstart 由浏览器在\n // 「mousedown + 移动超过阈值」时向上找**最近的 draggable 祖先**(= 整叶根,\n // 因整叶 draggable=true)并**在该祖先上派发**——因此本回调的 e.target 是\n // leaf 根本身,**不是** mousedown 命中的正文/按钮。用 e.target.closest(...)\n // 查交互/卡片恒 null(卡片是 leaf 的后代非祖先),清单形同虚设。故判定\n // 让位必须用 **mousedown 真实命中点元素**(dragstart 的 clientX/Y 与按下点\n // 一致,elementFromPoint 复取),不能用 e.target。\n const onLeafDragStart = (e: React.DragEvent<HTMLDivElement>) => {\n // 钉住的 leaf 不作拖拽源(位置固定,不可拖走/脱出浮动窗)。\n if (pinned) { e.preventDefault(); return }\n // mousedown 真实命中的元素(e.target 是 leaf 根——见上方注释)。\n const hit = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n const target = (hit !== null && (e.currentTarget as HTMLElement).contains(hit) ? hit : e.target) as HTMLElement\n // 让位:交互控件(按钮/输入/Monaco/图片等)上的按下不触发整叶拖拽。\n // 注意 INTERACTIVE_SELECTOR 不能含 [draggable=\"true\"],否则会命中整叶根\n // 自身,导致每次 dragstart 都被 preventDefault、整叶永远拖不动。\n if (target.closest(INTERACTIVE_SELECTOR)) { e.preventDefault(); return }\n // 让位:卡片容器(消息气泡/工具卡/审批卡/Review 卡/会话行/文件树行等)\n // 整体不可拖(2026-08-30 用户定调)——卡片上按下拖动 = 选中文字或无反应。\n if (target.closest(CARD_SELECTOR)) { e.preventDefault(); return }\n const selection = window.getSelection()\n if (selection !== null && !selection.isCollapsed) { e.preventDefault(); return }\n e.dataTransfer.setData('corum/leaf-id', leaf.id)\n e.dataTransfer.effectAllowed = 'move'\n setSourceDragging(true)\n // 显式 drag image(2026-08-30 终版 · 最小可用形态):leaf 的 will-change:\n // transform 独立合成层 + 0.1.2 会话区换肤的 backdrop-filter 玻璃层组合下,\n // Chromium 的整页 native ghost 失效(空态拍进相邻区域 / 非空态无 ghost)。\n // 因此克隆 leaf 作显式 drag image(只含本区域、剥离 backdrop-filter)。\n //\n // 坑(2026-08-30 多轮实测收敛):不要对克隆加 transform/opacity/scrollTop\n // 等任何会触发「非平凡渲染」的样式——非空态消息流克隆含合成层+玻璃层,\n // 任何额外渲染干预都会让 Chromium 对克隆的快照拍空(无 ghost)。克隆就\n // 原样挂 body 末尾、z-index:-1 压底(不被看见),快照稳定可见。快照内容\n // 是克隆滚动容器 scrollTop=0 的顶部画面(非空态不含底部 composer)——\n // 这是「稳定有 ghost」与「画面含 composer」二选一的取舍,用户确认保稳定。\n //\n // ghost 源用 e.currentTarget(= leaf 根,整叶画面)。\n const sourceEl = e.currentTarget as HTMLElement\n const rect = sourceEl.getBoundingClientRect()\n const ghost = sourceEl.cloneNode(true) as HTMLElement\n ghost.setAttribute('data-drag-ghost', '')\n ghost.style.position = 'fixed'\n ghost.style.left = '0'\n ghost.style.top = '0'\n ghost.style.width = `${rect.width}px`\n ghost.style.height = `${rect.height}px`\n // 克隆本体不被看见:负 margin-top 把内容推出视口(纯布局位移——不碰\n // top/transform/opacity/scrollTop 这些会让 Chromium 快照拍空的渲染样式;\n // z-index:-1 在含背景/transform 的祖先下压不住,曾致克隆在左上角可见)。\n // position:fixed 相对视口定位,top:0 + margin-top:-(height+100) → 内容整体\n // 在视口外,不占屏幕像素;快照仍可拍(文档内、可见、非 display:none)。\n ghost.style.margin = '0'\n ghost.style.marginTop = `${-Math.ceil(rect.height) - 100}px`\n ghost.style.pointerEvents = 'none'\n document.body.appendChild(ghost)\n e.dataTransfer.setDragImage(\n ghost,\n Math.round(e.clientX - rect.left),\n Math.round(e.clientY - rect.top),\n )\n // 克隆移除统一收进 onLeafDragEnd(React dragend——所有拖拽结束路径都走:\n // 窗口内 drop / 窗口外释放 / Esc 取消);此闭包只保留「窗口外释放 → 脱出」。\n ghostCleanupRef.current = () => ghost.remove()\n // 拖出主窗口外 → 该区域脱出为独立浮动窗。document 的 dragend 在窗口外释放\n // 时也触发;释放点坐标越界(离开窗口可视区)视为「拖到 APP 外」,触发脱出\n // 而非网格内拆分。网格内释放则走各 leaf 的 onDrop(split / swap)。\n const onDragEndDoc = (ev: DragEvent) => {\n document.removeEventListener('dragend', onDragEndDoc, true)\n const outX = ev.clientX <= 0 || ev.clientX >= window.innerWidth\n const outY = ev.clientY <= 0 || ev.clientY >= window.innerHeight\n if ((outX || outY) && onPopOut) onPopOut(leaf.slot)\n }\n document.addEventListener('dragend', onDragEndDoc, true)\n }\n const onLeafDragEnd = (): void => {\n ghostCleanupRef.current?.()\n ghostCleanupRef.current = null\n setSourceDragging(false)\n setZone(null)\n }\n\n return (\n <div\n ref={ref}\n className={css.leaf}\n data-slot={leaf.slot}\n data-drop-zone={zone ?? undefined}\n data-pinned={pinned || undefined}\n draggable={!pinned}\n onDragStart={onLeafDragStart}\n onDragEnd={onLeafDragEnd}\n onDragOver={(e) => {\n if (sourceDragging) return // 拖拽源自身不响应 dragover\n if (pinned) return // 钉住的 leaf 不可被拖入 split/swap,不显示 drop hint\n if (!hasDropType(e.dataTransfer.types)) return\n e.preventDefault()\n e.dataTransfer.dropEffect = e.dataTransfer.types.includes('corum/new-slot') ? 'copy' : 'move'\n setZone(zoneFromPoint(e.clientX, e.clientY))\n }}\n onDragLeave={() => setZone(null)}\n onDrop={(e) => {\n const newSlot = e.dataTransfer.getData('corum/new-slot') as GridSlot | ''\n const sourceId = e.dataTransfer.getData('corum/leaf-id')\n setZone(null)\n if (newSlot !== '') {\n e.preventDefault()\n onDropNewSlot?.(newSlot, leaf.id, zoneFromPoint(e.clientX, e.clientY))\n } else if (sourceId !== '') {\n e.preventDefault()\n onDrop(sourceId, leaf.id, zoneFromPoint(e.clientX, e.clientY))\n }\n }}\n >\n <RegionCard\n slotKey={leaf.slot}\n transparent={transparentSlots?.has(leaf.slot) ?? false}\n >\n {renderSlot(leaf.slot)}\n </RegionCard>\n {zone !== null && <div className={css.dropHint} data-zone={zone} aria-hidden=\"true\" />}\n </div>\n )\n}\n\n/** 递归渲染一个节点。 */\nfunction NodeView(props: GridViewProps & { node: GridNode; depth?: number }) {\n const { node, depth = 0, ...rest } = props\n if (node.type === 'leaf') {\n return <LeafView leaf={node} renderSlot={rest.renderSlot} onDrop={rest.onDrop} onPopOut={rest.onPopOut} onDropNewSlot={rest.onDropNewSlot} transparentSlots={rest.transparentSlots} />\n }\n return <BranchView branch={node} depth={depth} {...rest} />\n}\n\n/**\n * 计算一个分支各格的主轴像素(VSCode SplitView proportionalLayout 的对应物,\n * splitview.ts L856–873):可见格按 weight 占比瓜分容器主轴 span,脱出的格\n * size=0(不占 offset),Σmin > span 时等比压缩到正好放下(不溢出截断)。\n * weights 即各格目标像素;sash 拖动只在相邻两格转移 weight(Σ不变),所以\n * 非相邻格的计算结果像素严格不变——不传导。\n */\nfunction computeCellSizes(weights: number[], detached: boolean[], mins: number[], span: number, locked: readonly (number | null)[] = []): number[] {\n const n = weights.length\n // locked 格:宽度锁定(折叠轨),从 span 先扣除,不参与 weight 分配。\n let lockedTotal = 0\n for (let i = 0; i < n; i++) if (locked[i] != null) lockedTotal += locked[i] as number\n const freeSpan = span - lockedTotal\n const visible = weights.map((_, i) => !detached[i] && locked[i] == null)\n const visCount = visible.filter(Boolean).length\n const result: number[] = weights.map((_, i) => (locked[i] != null ? (locked[i] as number) : 0))\n if (visCount === 0 || freeSpan <= 0) return result\n let total = 0\n for (let i = 0; i < n; i++) if (visible[i]) total += Math.max(0, weights[i] ?? 0)\n if (total <= 0) {\n // 防御:可见格 weight 全 0(非法树)——均分。\n const each = freeSpan / visCount\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = each\n return result\n }\n let sizes = weights.map((w, i) => (visible[i] ? (Math.max(0, w) / total) * freeSpan : 0))\n // 各格最小尺寸(脱出的格不参与求和——它不占空间)。\n let minTotal = 0\n for (let i = 0; i < n; i++) if (visible[i]) minTotal += mins[i] ?? 0\n if (minTotal >= freeSpan) {\n // 容器太窄:按各格 min 比例分配(而非等比平分)——保住各区域声明的\n // 最小比,小窗不丢布局、大窗恢复后仍贴近用户拖的比例,绝不溢出截断。\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = ((mins[i] ?? 0) / minTotal) * freeSpan\n return result\n }\n // 夹各格自己的 min,夹取的差额从仍有富余的格里按比例补给(保持 Σ = freeSpan)。\n let deficit = 0\n sizes = sizes.map((s, i) => {\n if (!visible[i]) return 0\n const min = mins[i] ?? 0\n if (s < min) { deficit += min - s; return min }\n return s\n })\n if (deficit > 0) {\n const slack = sizes.reduce((a, s, i) => a + Math.max(0, s - (visible[i] ? mins[i] ?? 0 : 0)), 0)\n if (slack > 0) {\n sizes = sizes.map((s, i) => {\n const min = visible[i] ? mins[i] ?? 0 : 0\n return s > min ? s - (Math.max(0, s - min) / slack) * deficit : s\n })\n }\n }\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = sizes[i]\n return result\n}\n\n/**\n * 渲染一个分支(VSCode SplitView 式绝对定位):容器 position:relative,每格\n * position:absolute,left/top/width/height 由 JS 沿主轴累加 offset 算出并\n * 直接写 DOM style(不经 setState,避免每格重渲染)。容器尺寸用\n * ResizeObserver 监听(rAF 节流),变化时按 weights 占比重标定(自适应)。\n * sash 位置 = 相邻格边界;相邻有脱出格时该缝隐藏(拖它会改隐藏格 weight,\n * 经比例分配传导到非相邻格,违背「只相邻两格变」)。\n */\nfunction BranchView(props: Omit<GridViewProps, 'root'> & { branch: BranchNode; depth?: number }) {\n const { branch, depth = 0, ...rest } = props\n const containerRef = useRef<HTMLDivElement | null>(null)\n const cellRefs = useRef(new Map<string, HTMLDivElement>())\n const innerRefs = useRef(new Map<string, HTMLDivElement>())\n const sashRefs = useRef(new Map<number, HTMLDivElement>())\n\n // 折叠 = 运行时脱出(detachedSlots)或持久化关闭(hidden)。两者都折叠为\n // 0 宽、相邻填满;hidden 进持久化(重启保持关闭),detached 是临时的。\n // 注意:直接子可能是嵌套 branch(如 right-col 里的 row(editor,explorer))——\n // 不能只看 c.type==='leaf' 的 c.hidden,否则 branch 子其内部 leaf 全 hidden 也\n // 判不出,该支仍按 weight 占位、兄弟格(终端/对话区)不铺满。branch 子用\n // 递归判「所有后代 leaf 全隐藏(leaf.hidden 或 detachedSlots)」——2026-08-30\n // 修复:nodeAllHidden 只看 leaf.hidden,detachedSlots(右侧三区域默认隐藏/\n // 浮动窗脱出)不在其内 → right-col 全 detached 也判不出,对话区不铺满空缺。\n const allDetached = (node: GridNode): boolean =>\n node.type === 'leaf'\n ? ((rest.detachedSlots?.has(node.slot) ?? false) || node.hidden === true)\n : node.children.every(allDetached)\n const detached = branch.children.map((c) =>\n c.type === 'leaf'\n ? ((rest.detachedSlots?.has(c.slot) ?? false) || c.hidden === true)\n : allDetached(c))\n // 折叠收起(collapsedSlots):leaf 锁定为各自 collapsedWidth 的固定宽(非 0),\n // 不参与 weight 分配、两侧 sash 隐藏不可拖。折叠态直接来自本 prop(P2-2:\n // grid.ts 无状态,leafMinSize 经 collapsed 参数取同一份声明值)。\n // lockedSlots(壳层运行时锁定宽,如右侧全隐藏时侧栏锁 300)优先——同一\n // locked 机制,宽度由壳直给。\n const locked = branch.children.map((c) => {\n if (c.type !== 'leaf') return null\n const runtimeLocked = rest.lockedSlots?.get(c.slot)\n if (runtimeLocked !== undefined) return runtimeLocked\n if (!(rest.collapsedSlots?.has(c.slot) ?? false)) return null\n return getSlotMeta(c.slot)?.collapsedWidth ?? null\n })\n\n // 每次渲染重建的最新 layout 闭包(读最新 branch/detached/locked),供 RO/rAF 调用。\n const layoutRef = useRef<() => void>(() => {})\n layoutRef.current = () => {\n const el = containerRef.current\n if (el === null) return\n const w = el.clientWidth\n const h = el.clientHeight\n if (w <= 0 || h <= 0) return\n const isRow = branch.direction === 'row'\n const span = isRow ? w : h\n // 各子树 min:全 detached 的子树(leaf.hidden 或 detachedSlots)min 取 0——\n // subtreeMinSize 只看 leaf.hidden,detachedSlots(右侧三区域默认隐藏/浮动窗\n // 脱出)不在其内;否则全 detached 支的 min 仍计入 minTotal,兄弟格被「隐形\n // min」顶住无法铺满空缺(2026-08-30:right-col 全隐藏后对话区不填满)。\n const mins = branch.children.map((c, i) => (detached[i] ? 0 : subtreeMinSize(c, isRow, rest.collapsedSlots)))\n const sizes = computeCellSizes(branch.weights, detached, mins, span, locked)\n // 顶层 row 的 leafTopOffset:内容格 top 下移、height 相应缩短(绝对定位格\n // 自身仍占满全高,内容格让位)。只作用于 depth=0 的 row 分支。\n const topOffsets = depth === 0 && isRow ? rest.leafTopOffset : undefined\n let offset = 0\n for (let i = 0; i < branch.children.length; i++) {\n const size = sizes[i] ?? 0\n // sash i-1 在 cell i 的左/上边界上。\n if (i > 0) {\n const sash = sashRefs.current.get(i - 1)\n if (sash) {\n // 折叠轨相邻的 sash 隐藏(折叠格宽度锁定不可拖;拖它会改锁定格)。\n const show = size > 0 && (sizes[i - 1] ?? 0) > 0 && locked[i] == null && locked[i - 1] == null\n sash.style.visibility = show ? 'visible' : 'hidden'\n if (isRow) {\n sash.style.left = `${offset - 4}px`\n sash.style.top = '0px'\n sash.style.height = `${h}px`\n } else {\n sash.style.top = `${offset - 4}px`\n sash.style.left = '0px'\n sash.style.width = `${w}px`\n }\n }\n }\n const cell = cellRefs.current.get(branch.children[i].id)\n if (cell) {\n if (size <= 0) {\n cell.style.visibility = 'hidden'\n } else {\n cell.style.visibility = 'visible'\n if (isRow) {\n cell.style.left = `${offset}px`\n cell.style.top = '0px'\n cell.style.width = `${size}px`\n cell.style.height = `${h}px`\n // 内容格让位:top 下移 offset、height 缩短 offset(卡片从标题栏下方开始)。\n const inner = innerRefs.current.get(branch.children[i].id)\n if (inner) {\n const topOff = topOffsets?.[i] ?? 0\n inner.style.top = `${topOff}px`\n inner.style.height = `${Math.max(0, h - topOff)}px`\n }\n } else {\n cell.style.top = `${offset}px`\n cell.style.left = '0px'\n cell.style.height = `${size}px`\n cell.style.width = `${w}px`\n }\n }\n }\n offset += size\n }\n }\n\n // 渲染后(paint 前)重排:树 weights / detached / 方向任何变化都立刻反映。\n useLayoutEffect(() => {\n layoutRef.current()\n })\n\n // 容器尺寸变化(窗口缩放 / 父格重排)→ rAF 节流后按占比重标定 + layout。\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n let raf: number | null = null\n const observer = new ResizeObserver(() => {\n raf ??= requestAnimationFrame(() => {\n raf = null\n layoutRef.current()\n })\n })\n observer.observe(el)\n return () => {\n observer.disconnect()\n if (raf !== null) cancelAnimationFrame(raf)\n }\n }, [])\n\n // sash 拖动:把 px delta 直接当像素转移(weights 就是像素),交给上层改树。\n const makeSashDrag = (sashIndex: number) => (pxDelta: number) => {\n rest.onResize(branch.id, sashIndex, pxDelta)\n }\n\n const cells: ReactNode[] = []\n branch.children.forEach((child, i) => {\n if (i > 0) {\n const sashIndex = i - 1\n cells.push(\n <Sash\n key={`sash-${i}`}\n ref={(el) => {\n if (el) sashRefs.current.set(sashIndex, el)\n else sashRefs.current.delete(sashIndex)\n }}\n direction={branch.direction}\n onDrag={makeSashDrag(sashIndex)}\n />,\n )\n }\n cells.push(\n <div\n key={child.id}\n ref={(el) => {\n if (el) cellRefs.current.set(child.id, el)\n else cellRefs.current.delete(child.id)\n }}\n className={css.branchCell}\n data-detached={detached[i] || undefined}\n >\n <div\n ref={(el) => {\n if (el) innerRefs.current.set(child.id, el)\n else innerRefs.current.delete(child.id)\n }}\n className={css.branchInner}\n >\n {detached[i] ? null : <NodeView {...rest} root={child} node={child} depth={depth + 1} />}\n </div>\n </div>,\n )\n })\n\n return (\n <div\n ref={containerRef}\n className={branch.direction === 'row' ? css.branchRow : css.branchColumn}\n data-branch={branch.id}\n data-direction={branch.direction}\n >\n {cells}\n </div>\n )\n}\n\n/** 顶层 GridView:网格 + 浮动窗拖回的实时插入预览。 */\nexport function GridView(props: GridViewProps) {\n const gridRef = useRef<HTMLDivElement | null>(null)\n // 浮动窗拖回预览:{x, y} 相对网格左上角;dragging=false 时清除。\n const [preview, setPreview] = useState<{ x: number; y: number } | null>(null)\n\n useEffect(() => {\n const bridge = (window as unknown as { corumDesktop?: { onFloatingDrag?: (cb: (p: { dragging: boolean; x?: number; y?: number }) => void) => () => void } }).corumDesktop\n if (bridge?.onFloatingDrag === undefined) return\n return bridge.onFloatingDrag((p) => {\n const grid = gridRef.current\n if (grid === null) return\n if (!p.dragging || p.x === undefined || p.y === undefined) {\n setPreview(null)\n return\n }\n // p.x/p.y 是相对主窗口左上角的屏幕坐标;换算成相对网格的坐标。\n const r = grid.getBoundingClientRect()\n setPreview({ x: p.x - r.left, y: p.y - r.top })\n })\n }, [])\n\n // 命中的窗格 + zone:从预览坐标向下找 leaf,再按相对位置判 zone。\n const previewTarget = preview === null ? null : (() => {\n const grid = gridRef.current\n if (grid === null) return null\n const r = grid.getBoundingClientRect()\n const el = document.elementFromPoint(r.left + preview.x, r.top + preview.y)\n // LeafView 根同时带 data-slot 与 draggable(RegionCard 的 data-slot 在内层、无\n // draggable),用「draggable 的网格叶子」精确命中,避免双条件选择器落空。\n const leafEl = el?.closest('[data-slot][draggable=\"true\"]') as HTMLElement | null\n if (leafEl == null) return null\n const lr = leafEl.getBoundingClientRect()\n const fx = (r.left + preview.x - lr.left) / lr.width\n const fy = (r.top + preview.y - lr.top) / lr.height\n const EDGE = 0.25\n const zone = fx < EDGE ? 'left' : fx > 1 - EDGE ? 'right' : fy < EDGE ? 'top' : fy > 1 - EDGE ? 'bottom' : 'center'\n // 预览块的几何:zone 覆盖 leaf 的一半,center 覆盖全部(相对网格坐标)。\n let rect: { left: number; top: number; width: number; height: number }\n const relLeft = lr.left - r.left\n const relTop = lr.top - r.top\n if (zone === 'left') rect = { left: relLeft, top: relTop, width: lr.width / 2, height: lr.height }\n else if (zone === 'right') rect = { left: relLeft + lr.width / 2, top: relTop, width: lr.width / 2, height: lr.height }\n else if (zone === 'top') rect = { left: relLeft, top: relTop, width: lr.width, height: lr.height / 2 }\n else if (zone === 'bottom') rect = { left: relLeft, top: relTop + lr.height / 2, width: lr.width, height: lr.height / 2 }\n else rect = { left: relLeft, top: relTop, width: lr.width, height: lr.height }\n return { rect, zone }\n })()\n\n return (\n <div className={css.grid} ref={gridRef} style={{ position: 'relative' }}>\n <NodeView {...props} node={props.root} />\n {previewTarget !== null && (\n <div\n className={css.dockPreview}\n data-zone={previewTarget.zone}\n style={{\n left: previewTarget.rect.left,\n top: previewTarget.rect.top,\n width: previewTarget.rect.width,\n height: previewTarget.rect.height,\n }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n )\n}\n","/* FloatingLayer —— 全应用级悬浮层。挂 document.body,z-index 高于一切\n 帧内容。容器本身不挡交互(pointer-events:none),各悬浮项按需恢复。 */\n\n.host {\n position: fixed;\n inset: 0;\n z-index: 100;\n pointer-events: none;\n}\n\n/* 单个悬浮项:占满视口,modal 项挡交互,非 modal 项(通知)不挡。 */\n.item {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.item[data-modal] {\n pointer-events: auto;\n}\n\n/* modal 遮罩:点击关闭该项。 */\n.mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n/* 内容体:相对遮罩在上,恢复交互。 */\n.body {\n position: relative;\n pointer-events: auto;\n}\n","/**\n * FloatingLayer —— 通用悬浮层(shell-base)。\n *\n * 与 shell.overlay(帧内浮层,受 frame 的 transform/overflow 约束)不同,\n * FloatingLayer 经 createPortal 挂到 document.body,彻底脱离网格/卡片的\n * 合成层与裁剪上下文,是全应用级模态/悬浮的唯一合法出口:\n * - 设置面板(官方 ui-settings-general,面板在窗格 DOM 内,需迁移上来)\n * - 未来的应用内通知、对话框、确认框等\n *\n * 两条使用路径:\n * 1. 注册式(自建内容):openFloating({ id, content, modal }) ——\n * 内容以 React 元素注册,层内渲染;closeFloating(id) 关闭。\n * 2. 迁移式(第三方/官方组件,组件树不能重建):floatingLayerHost(el)\n * 拿到容器,把已渲染的 DOM 子树 appendChild 进来,关闭时移回原位。\n * DOM 移动不触发 React 重建,组件内部 state(如 open)保留。\n *\n * 多个悬浮项按打开顺序堆叠;modal 项带遮罩(点击遮罩关闭该项),\n * 非 modal 项(如通知)不挡下层交互。Escape 关闭最顶层 modal 项。\n */\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport css from './FloatingLayer.module.css'\n\n/** 一个悬浮项。 */\nexport interface FloatingItem {\n /** 唯一 id(重复 open 同 id = 替换内容并置顶)。 */\n id: string\n /** 悬浮内容(注册式)。 */\n content: ReactNode\n /** 是否模态(带遮罩、挡下层交互、Escape/点遮罩关闭)。默认 true。 */\n modal?: boolean\n /** 关闭回调(点遮罩 / Escape / closeFloating 触发)。 */\n onClose?: (() => void) | undefined\n}\n\n/** 悬浮层对外面:注册/关闭。 */\nexport interface FloatingLayerApi {\n openFloating: (item: FloatingItem) => void\n closeFloating: (id: string) => void\n}\n\nconst FloatingContext = createContext<FloatingLayerApi | null>(null)\n\n/** 在 FloatingLayer 子树内取悬浮层 API(未挂载时返回 null)。 */\nexport function useFloatingLayer(): FloatingLayerApi | null {\n return useContext(FloatingContext)\n}\n\n/**\n * 迁移式使用的宿主容器:全局唯一(应用只挂一个 FloatingLayer)。\n * FloatingLayer 挂载后把容器 DOM 记录到这里,供「把官方组件渲染出的\n * DOM 子树移上来」的场景取用(不经过 React,组件状态不丢)。\n */\nlet hostElement: HTMLDivElement | null = null\n\n/** 取悬浮层容器(FloatingLayer 已挂载时非 null)。 */\nexport function floatingLayerHost(): HTMLDivElement | null {\n return hostElement\n}\n\n/** 通用悬浮层。挂在壳的 frame 根(应用唯一)。 */\nexport function FloatingLayer(props: { children?: ReactNode }) {\n const [items, setItems] = useState<readonly FloatingItem[]>([])\n const hostRef = useRef<HTMLDivElement | null>(null)\n\n const closeFloating = useCallback((id: string) => {\n setItems((prev) => {\n const item = prev.find((i) => i.id === id)\n item?.onClose?.()\n return prev.filter((i) => i.id !== id)\n })\n }, [])\n\n const openFloating = useCallback((item: FloatingItem) => {\n setItems((prev) => [...prev.filter((i) => i.id !== item.id), item])\n }, [])\n\n // 记录全局宿主容器(迁移式场景用)。\n useEffect(() => {\n hostElement = hostRef.current\n return () => { hostElement = null }\n }, [])\n\n // Escape 关闭最顶层 modal 项。\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent): void => {\n if (e.key !== 'Escape') return\n setItems((prev) => {\n for (let i = prev.length - 1; i >= 0; i--) {\n if (prev[i].modal !== false) {\n prev[i].onClose?.()\n return [...prev.slice(0, i), ...prev.slice(i + 1)]\n }\n }\n return prev\n })\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [])\n\n return (\n <FloatingContext.Provider value={{ openFloating, closeFloating }}>\n {props.children}\n {createPortal(\n <div ref={hostRef} className={css.host} data-floating-layer>\n {items.map((item) => (\n <div key={item.id} className={css.item} data-modal={item.modal !== false || undefined}>\n {item.modal !== false && (\n <div className={css.mask} aria-hidden=\"true\" onClick={() => closeFloating(item.id)} />\n )}\n <div className={css.body}>{item.content}</div>\n </div>\n ))}\n </div>,\n document.body,\n )}\n </FloatingContext.Provider>\n )\n}\n","/* ConfirmDialog —— 统一确认提示框样式(全 token,深浅主题自动翻转) */\n\n/* ── 居中模态 ─────────────────────────────────────────────────────── */\n\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.35);\n backdrop-filter: blur(8px);\n}\n\n.dialog {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: calc(8px * var(--corum-density-scale, 1));\n width: 360px;\n max-width: calc(100vw - 48px);\n padding: calc(16px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 16px;\n background: var(--corum-glass-1);\n backdrop-filter: blur(16px) saturate(140%);\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);\n box-sizing: border-box;\n}\n\n.closeBtn {\n position: absolute;\n top: 10px;\n right: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 6px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.closeBtn:hover {\n background: var(--corum-glass-2);\n color: var(--dsw-alias-label-secondary);\n}\n\n/* ── 内联气泡(设计稿 DKRwC confirm-pop)───────────────────────────── */\n/* 调用方容器需 position:relative;默认贴右下方向上弹出。 */\n\n.inlinePop {\n position: absolute;\n right: 0;\n bottom: calc(100% + 8px);\n width: 224px;\n display: flex;\n flex-direction: column;\n gap: calc(8px * var(--corum-density-scale, 1));\n padding: calc(10px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 12px;\n background: var(--corum-glass-1);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);\n box-sizing: border-box;\n z-index: 20;\n}\n\n/* ── 共享内部元素 ─────────────────────────────────────────────────── */\n\n.title {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(12px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.dialog .title {\n font-size: calc(15px * var(--corum-ui-font-scale, 1));\n font-weight: 700;\n}\n\n.message {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-label-secondary);\n line-height: 1.5;\n}\n\n.dialog .message {\n font-size: calc(13px * var(--corum-ui-font-scale, 1));\n}\n\n.warning {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(10px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-state-error-primary);\n line-height: 1.4;\n}\n\n.dialog .warning {\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n}\n\n.row {\n display: flex;\n justify-content: flex-end;\n gap: calc(6px * var(--corum-density-scale, 1));\n padding-top: calc(4px * var(--corum-density-scale, 1));\n}\n\n.cancelBtn {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 8px;\n background: var(--corum-glass-2);\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n}\n\n.cancelBtn:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.confirmBtnDanger {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: none;\n border-radius: 8px;\n background: var(--dsw-alias-state-error-primary);\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--corum-label-on-brand);\n}\n\n.confirmBtnPrimary {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: none;\n border-radius: 8px;\n background: var(--dsw-alias-brand-primary, var(--corum-state-running));\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--corum-label-on-brand);\n}\n\n.confirmBtnDanger:disabled,\n.confirmBtnPrimary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n","/**\n * ConfirmDialog —— 统一确认提示框(corum 各域「确认/取消」场景复用)。\n *\n * 两种形态:\n * - placement=\"center\":全屏遮罩 + 居中玻璃对话框(默认,适合打断性确认,如更换模型)。\n * - placement=\"inline\":内联气泡弹窗(适合就地二次确认,对应设计稿 DKRwC confirm-pop;\n * 调用方负责定位容器 position:relative)。\n *\n * 视觉 token 全部走设计变量,深浅主题随 body[data-ds-dark-theme] 自动翻转。\n * 自绘 14px 描边 × 图标(不引 lucide,保持本包零业务依赖)。\n * @module corum-ui-base/client/ConfirmDialog\n */\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport css from './ConfirmDialog.module.css'\n\nexport interface ConfirmDialogProps {\n /** 标题(如「更换模型」「删除技能」)。 */\n title: string\n /** 主文案(确认问题)。 */\n message: ReactNode\n /** 警示补充(如「建议在新任务中更换模型」;可选)。 */\n warning?: ReactNode\n /** 内联错误信息(确认动作失败时展示;可选)。 */\n error?: string | null\n /** 主操作文案(默认「确认」)。 */\n confirmLabel?: ReactNode\n /** 次操作文案(默认「取消」)。 */\n cancelLabel?: ReactNode\n /** 主操作色调:danger=危险(删除等),primary=主操作(默认 danger)。 */\n tone?: 'danger' | 'primary'\n /** 形态:center=居中模态(默认) / inline=内联气泡。 */\n placement?: 'center' | 'inline'\n /** 主操作进行中(禁用按钮并显示 busy 文案)。 */\n busy?: boolean\n /** busy 时主操作文案(默认沿用 confirmLabel)。 */\n busyLabel?: ReactNode\n /** 主操作额外禁用条件。 */\n confirmDisabled?: boolean\n /**\n * 形态:confirm=双键确认(默认) / info=单键告知(替原生 alert,只有主操作键,\n * 点主键/遮罩/× 都走 onCancel 关闭)。\n */\n kind?: 'confirm' | 'info'\n /** 点主操作。kind=info 时可为空(仅关闭)。 */\n onConfirm?: () => void\n /** 点次操作 / 遮罩 / ×;kind=info 时主键也走这里关闭。 */\n onCancel: () => void\n}\n\n/** 14px 描边 ×(关闭)图标,stroke=currentColor。 */\nfunction IconClose() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3L11 11M11 3L3 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport function ConfirmDialog({\n title,\n message,\n warning,\n error,\n confirmLabel,\n cancelLabel = '取消',\n tone = 'danger',\n placement = 'center',\n busy = false,\n busyLabel,\n confirmDisabled = false,\n kind = 'confirm',\n onConfirm,\n onCancel,\n}: ConfirmDialogProps) {\n const confirmText = confirmLabel ?? (kind === 'info' ? '知道了' : '确认')\n const body = (\n <>\n <div className={css.title}>{title}</div>\n <div className={css.message}>{message}</div>\n {warning !== undefined && <div className={css.warning}>{warning}</div>}\n {error !== undefined && error !== null && error !== '' && <div className={css.warning}>{error}</div>}\n <div className={css.row}>\n {kind === 'confirm' && (\n <button type=\"button\" className={css.cancelBtn} onClick={onCancel} disabled={busy}>{cancelLabel}</button>\n )}\n <button\n type=\"button\"\n className={tone === 'primary' ? css.confirmBtnPrimary : css.confirmBtnDanger}\n onClick={kind === 'info' ? onCancel : onConfirm}\n disabled={busy || confirmDisabled}\n >{busy ? (busyLabel ?? confirmText) : confirmText}</button>\n </div>\n </>\n )\n\n if (placement === 'inline') {\n return <div className={css.inlinePop}>{body}</div>\n }\n\n return createPortal(\n <div className={css.overlay} onClick={onCancel}>\n <div className={css.dialog} onClick={e => e.stopPropagation()} role=\"alertdialog\" aria-modal=\"true\">\n <button type=\"button\" className={css.closeBtn} onClick={onCancel} aria-label=\"关闭\"><IconClose /></button>\n {body}\n </div>\n </div>,\n document.body,\n )\n}\n","/**\n * Global theme DOM applier: projects the resolved ThemeSnapshot onto the\n * document — `html { color-scheme }` for native UA chrome, `body[data-ds-dark-theme]`\n * for the token palette, the active theme's alias-token overrides as inline CSS\n * variables on body, the content font-size axis (`--dsh-content-font-size`), and\n * one presenter-owned `meta[name=\"theme-color\"]`. Pure DOM writes, no React\n * involvement. This is a fork of ui-layout's presenter: a corum shell owns the\n * same projection duty (the official ui-layout is disabled, so nothing else\n * writes the body palette attribute).\n *\n * ## 正文字号轴(2026-09-16 补上,修「运行时断链」)\n *\n * 官方把 `--dsh-content-font-size` 的写入职责放在 **ui-layout 的 ThemePresenter** 里\n * (`ui-layout/src/client/theme-presenter.ts:47`)。corum 禁用了 ui-layout,\n * 而本 fork 起初**只投影了调色板、漏了这条轴** ⇒ 于是出现:\n * - `boot-theme.ts` 在**加载时**写入一次(值 = 当时的设置),所以变量**存在**;\n * - 但**运行时更新无人负责** ⇒ 用户改「会话正文字号」后变量纹丝不动,\n * **改了等于没改**(真机实测:设 17 后变量仍为 14px)。\n *\n * 这正是 PRD §4.3.1 记的「⚠️ 运行时断链,需修」。现按官方语义补上写入,\n * 并把该变量纳入 `dispose()` 的撤回集(与官方一致:presenter 只撤回自己写的东西)。\n */\nimport type { ThemeSnapshot } from '@deepseek-ai/dsh-client-ui-theme/client'\n\n/** Body attribute selecting the dark base palette in the token stylesheets. */\nexport const DARK_ATTRIBUTE = 'data-ds-dark-theme'\n\n/**\n * Body variable carrying the user's content font size in px.\n *\n * ⚠️ 必须与官方 `ui-layout/src/client/theme-presenter.ts` 的\n * `CONTENT_FONT_SIZE_VARIABLE` 保持**同名**,否则消费端(corum-ui-chat /\n * corum-ui-conversation 等大量 CSS 里的 `var(--dsh-content-font-size, 14px)`)\n * 解析不到。\n */\nexport const CONTENT_FONT_SIZE_VARIABLE = '--dsh-content-font-size'\n\n/** Applies theme snapshots to the document; one instance per plugin fiber. */\nexport class ThemePresenter {\n /** Token names this presenter wrote in the last apply (its retraction set). */\n private appliedTokens: string[] = []\n /** The single metadata node this presenter inserts and removes. */\n private readonly themeColorMeta: HTMLMetaElement\n\n constructor() {\n this.themeColorMeta = document.createElement('meta')\n this.themeColorMeta.name = 'theme-color'\n }\n\n apply(snapshot: ThemeSnapshot): void {\n const scheme = snapshot.active.colorScheme\n document.documentElement.style.colorScheme = scheme\n const body = document.body\n if (scheme === 'dark') body.setAttribute(DARK_ATTRIBUTE, '')\n else body.removeAttribute(DARK_ATTRIBUTE)\n // 正文字号轴:官方由 ui-layout 的 ThemePresenter 写,corum 侧必须自己写,\n // 否则「会话正文字号」设置只落盘、不生效(PRD §4.3.1 的运行时断链)。\n body.style.setProperty(CONTENT_FONT_SIZE_VARIABLE, `${snapshot.fontSize}px`)\n for (const name of this.appliedTokens) body.style.removeProperty(name)\n this.appliedTokens = []\n for (const [name, value] of Object.entries(snapshot.active.tokens)) {\n body.style.setProperty(name, value)\n this.appliedTokens.push(name)\n }\n this.themeColorMeta.content = getComputedStyle(body).backgroundColor\n if (!this.themeColorMeta.isConnected) document.head.append(this.themeColorMeta)\n }\n\n dispose(): void {\n document.documentElement.style.removeProperty('color-scheme')\n const body = document.body\n body.removeAttribute(DARK_ATTRIBUTE)\n body.style.removeProperty(CONTENT_FONT_SIZE_VARIABLE)\n for (const name of this.appliedTokens) body.style.removeProperty(name)\n this.appliedTokens = []\n this.themeColorMeta.remove()\n }\n}\n","/**\n * 磁贴墙排布算法(Mosaic)—— 集成中心三个内容页(插件 / MCP / 技能)共用的\n * 「错落有致」块生成器。**这是排布算法的单一事实源**:三个页面不再各持一份副本\n * (副本分叉过一次,见文件末「为什么抽出来」)。\n *\n * ## 几何(design.pen 四帧逐帧读实)\n *\n * 设计稿的磁贴墙**不是 CSS grid**,而是「若干等宽 808 的块」上下堆叠;每块内部\n * 一行 flex、各列等高。块只有两种高度:\n *\n * - **高 264 的块**:四列,宽度是 `[264,264,128,128]` 的某个排列。\n * 264 宽列装「一张大贴」或「两条宽贴竖叠」;128 宽列装「一张高贴」或\n * 「两条小贴竖叠」。宽度校验:264+8+264+8+128+8+128 = **808** ✓\n * - **高 128 的块**:列宽模式三选一,均为 808:\n * `[128×6]` = 768+40 / `[264,128×4]` = 776+32 / `[264,264,128×2]` = 784+24。\n *\n * 列宽 128 与「两条小贴竖叠」的关系:128+8+128 = 264,故 128 宽的列与 264 宽的\n * 列在纵向能装下同样多的贴,两种块的列高都自洽。\n *\n * ## 为什么是「固定种子伪随机」而不是逐块手写\n *\n * 设计稿每帧只画两行,谈不上覆盖真实数据量(插件市场可能几十条)。排布若按\n * 固定序号循环,每块会排得一模一样——用户反馈「当前这种每一列都一种排列方式\n * 是不好看的」。故逐块抽型:**种子为常量 ⇒ 同一份数据每次渲染完全一致**(可\n * 复现、可对账),同时块与块之间形态不同,呈现错落。\n *\n * 曾经的做法是 `display:grid; grid-auto-flow:dense` + 固定序号循环,实测空洞率\n * 6.3%(大贴跨两行时自动放置填不满)且每块同形;改块生成器后整块恰好铺满。\n *\n * @module corum-ui-base/client/mosaic\n */\n\n/** 磁贴尺寸档(与 design.pen 的 264×264 / 264×128 / 128×264 / 128×128 对应)。 */\nexport type MosaicSize = 'big' | 'wide' | 'tall' | 'small'\n\n/** 块高度档:264 块装大/高贴,128 块装宽/小贴。 */\nexport type MosaicBlockKind = '264' | '128'\n\n/**\n * 列数模式:6 = 设计稿的细粒度排布(128 基准);3 = 窄窗降列(264 基准)。\n *\n * 两种模式的块宽**恒等**,故单位宽公式无需分叉:\n * 6 列行 = 2×(2u+g) + 2×u + 3g = 6u+5g\n * 3 列行 = 3×(2u+g) + 2g = 6u+5g\n * 降列的意义是把同一容器宽摊到更少的列上——实测 546px 容器下 6 列的单位宽只有\n * 83px(名称放不下),降成 3 列后每列 174px,名称与作者都能正常显示。\n */\nexport type MosaicColumns = 6 | 3\n\n/**\n * 固定种子:**换掉它就换一整套图案**,同一份数据下恒定。\n * 取值本身无深意(任意常量),只承担「可复现」这一个职责。\n */\nexport const MOSAIC_SEED = 0x5a17\n\n/**\n * 相邻块同型时的重抽上限。几何上两型等概率,5 次仍同型就直接取相\n * 反型——保证「相邻不同型」这条硬约束不会因随机而破。\n */\nconst BLOCK_RETYPE_MAX = 5\n\n/**\n * 块内一张贴的槽位:尺寸档 + 它承载哪一条数据。\n *\n * `itemIndex` 由算法显式给出,而不是「按顺序数下去」:把长名字安排到 264 宽的\n * 槽需要**跨位置挑选数据**,落位不再是「第 i 张贴 = 第 i 条数据」,故每张槽位都\n * 记下自己的数据下标({@link MosaicWall} 按它取数据)。\n */\nexport interface MosaicSlot {\n readonly size: MosaicSize\n /** 承载的数据下标;形状计划完成后由算法填写(长名字会与短名字成对交换)。 */\n itemIndex: number\n}\n\n/** 块内一列:宽度档 + 该列自上而下的贴槽位。 */\nexport interface MosaicCol {\n /** 列宽(px,未乘密度缩放):264 或 128。 */\n readonly width: 264 | 128\n /** 该列的贴尺寸序列(自上而下 1~2 张);分配数据后为可变数组。 */\n readonly sizes: MosaicSize[]\n /** 分配数据后填入的槽位(与 {@link sizes} 同长同序)。 */\n slots?: MosaicSlot[]\n}\n\n/** 一个块行:高度档 + 列序列。 */\nexport interface MosaicBlock {\n readonly kind: MosaicBlockKind\n readonly cols: readonly MosaicCol[]\n}\n\n/** mulberry32:小而稳定的 32 位 PRNG(纯函数式状态推进,无外部依赖)。 */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0\n return () => {\n a = (a + 0x6d2b79f5) >>> 0\n let t = a\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Fisher–Yates 洗牌(消费同一 PRNG,保证整条序列可复现)。 */\nfunction shuffled<T>(rng: () => number, items: readonly T[]): T[] {\n const out = [...items]\n for (let i = out.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[out[i], out[j]] = [out[j]!, out[i]!]\n }\n return out\n}\n\n\n/**\n * 名字档:算法用它在槽位之间做取舍(长名字优先给 264 宽的列)。\n */\n/** 一条数据的排布提示。 */\nexport interface MosaicItemHint {\n /** 展示名的字符数(调用方按**实际用于渲染**的那个名字算)。 */\n readonly nameLength: number\n /** 是否有描述(有描述的长名才值得给大贴,换来额外高度放描述)。 */\n readonly hasDescription?: boolean\n}\n\n/** 名字「偏长」的阈值:达到即优先安排进 264 宽的列(宽贴或大贴)。 */\nconst LONG_NAME_LENGTH = 18\n\n/** 名字「很长」的阈值:配合描述可给大贴(264×264),拿到额外高度显示描述。 */\nconst VERY_LONG_NAME_LENGTH = 22\n\n/** 6 列模式的块几何:四列宽 `[264,264,128,128]` 的某个排列(6u+5g)。 */\nconst WIDTHS_6COL = [264, 264, 128, 128] as const\n\n/**\n * 3 列模式的块几何:三列固定 264 宽(3×(2u+g)+2g = 6u+5g,与 6 列块等宽)。\n * 窄窗降列时用——同一容器宽摊到 3 列上,每列宽约翻倍,长名字才放得下。\n */\nconst WIDTHS_3COL = [264, 264, 264] as const\n\n/**\n * 3 列模式下的第二种列宽模式:`[128,128,264,264]`(1+1+2+2 单位)。\n *\n * 为什么 3 列模式还需要它:**`tall`(竖直矩形 128×264)只能长在 128 宽的列上**,\n * 而 3 列的唯一合法宽度组合是 `264+264+264`(数学上 2+2+2 是 `6u+5g` 的唯一解)\n * —— 全是 264 宽的列 ⇒ 3 列下结构上生不出竖直矩形(实测形状分布只有 big/wide)。\n * 引入这个 4 列模式后,3 列模式也能排出「竖直矩形 + 大正方形」并存的形态。\n * 宽度校验:128+8+128+8+264+8+264 = 808 = 6u+5g ✓(与 3 列块等宽)\n */\nconst WIDTHS_3COL_TALL = [128, 128, 264, 264] as const\n\n/**\n * 一个块内的**形状配额**:每种形状各出几张。\n *\n * 这是「美观」的第二维——原来只按名字长度决定宽窄(长名给 264 列),结果 264 列\n * 一律退化成「两条宽贴」,整片墙只剩长方形、大正方形消失(实测 wide:17 / big:1)。\n * 现在改为**先给每个块定一份形状配方**(大正方形几张、竖直矩形几张、宽贴几张、\n * 小贴几张),再由位置去消费配额:长名字仍然优先占宽槽,但**形状本身不再由名字\n * 长度决定**——短名字也会轮到正方形与竖直矩形。\n */\nexport interface BlockShapePlan {\n /** 大贴(264×264,正方形)。 */\n readonly big: number\n /** 宽贴(264×128,横向长方形)。 */\n readonly wide: number\n /** 高贴(128×264,竖直矩形)。 */\n readonly tall: number\n /** 小贴(128×128)。 */\n readonly small: number\n}\n\n/**\n * 为第 `blockIndex` 个块生成形状配方。\n *\n * 设计意图(用户 2026-10-01:「不要只按照名字长度来进行,短名字也可以有大正方形、\n * 竖直的矩形,要增加一个维度,适当调节卡片的排列看上去更加有美感」):\n * - **每块至少一张大正方形**(264 块的锚点),保证无论数据怎么排都有正方形;\n * - **每个 264 块至少一张竖直矩形**(128×264),让「竖着的长方形」稳定出现;\n * - 其余槽位按一个**轮转配方表**分配,使相邻块的形状组合不同(错落);\n * - 配方按块序号轮转(不是随机),故同一份数据每次渲染一致、可复现对账。\n *\n * @param blockIndex - 第几个块(0 起)。\n * @returns 该块的形状配额。\n */\nexport function planBlockShape(blockIndex: number): BlockShapePlan {\n /* 轮转配方表:四种形态权重不同但每轮都覆盖到,相邻块配方不同 ⇒ 整墙不单调。\n 每行的和 ≈ 一个块的槽位数(264 块 6~7 槽 / 128 块 4~6 槽),多余的配额无害\n (骨架消费不完就忽略),不足时 264 列自动落到双宽、128 列落到双小。 */\n const RECIPES: readonly BlockShapePlan[] = [\n { big: 1, wide: 1, tall: 1, small: 0 },\n { big: 1, wide: 2, tall: 1, small: 1 },\n { big: 1, wide: 1, tall: 1, small: 0 },\n { big: 1, wide: 1, tall: 1, small: 1 },\n ]\n return RECIPES[blockIndex % RECIPES.length]!\n}\n\n/**\n * 生成一个高 264 的块的**列骨架**(只决定列宽排列与每列的贴数,不含数据)。\n *\n * 264 宽列可装「一张大贴」或「两条宽贴竖叠」;128 宽列可装「一张高贴」或\n * 「两条小贴竖叠」。约束:至少一张大贴(视觉锚点)、264 列不得全为双宽、\n * 128 列不得全为双小,违反重抽;耗尽后退到设计稿首帧排法。\n *\n * @param rng - 该次渲染的 PRNG。\n * @param mode - 列数模式(6 或 3)。\n * @param pinFirstTwoSmalls - 首列钉成「两条小贴竖叠」:集成中心的入口贴\n * (添加服务器 / 添加技能等)固定落在左上角第一张,故需要这枚钉子。\n */\nexport function buildColSkeleton(\n rng: () => number,\n mode: MosaicColumns = 6,\n pinFirstTwoSmalls = false,\n shape: BlockShapePlan = { big: 0, wide: 0, tall: 0, small: 0 },\n): MosaicCol[] {\n /* 3 列模式用 `[128,128,264,264]`(4 列、6 单位)——它是 3 列模式里唯一**同时**\n 能长出 `tall`(竖直矩形,需 128 列)与容纳入口小贴的形态;纯 `[264,264,264]`\n 三列只能出 big/wide,且首个槽位放不下小贴(264 列最小单元就是一张宽贴)。\n 6 列模式用 `WIDTHS_6COL`。两者块宽恒等(6 单位 + 5 间隙)。 */\n const base = mode === 3 ? WIDTHS_3COL_TALL : WIDTHS_6COL\n for (let attempt = 0; attempt < BLOCK_RETYPE_MAX; attempt++) {\n const widths = [...shuffled(rng, base)]\n /* 入口贴钉在左上角。**必须换「同单位」的列,不能直接改首列宽**:\n 块宽恒等式是「单位数 = 6」(128 列 = 1 单位、264 列 = 2 单位)—— 把 2 单位的\n 首列改成 1 单位,整块就少 1 单位(实测钉后列宽 [128,128,128,264] 只剩 672、\n 应为 808);给 3 列硬塞一个 128 列则会多出列数(实测 [264,264,264,128] 涨到\n 944)。故这里**只对调、不改值**,把首列换成同模式里已存在的 128 列。 */\n if (pinFirstTwoSmalls) {\n const j = widths.findIndex(w => w === 128)\n if (j > 0) { const t = widths[0]!; widths[0] = widths[j]!; widths[j] = t }\n }\n // 按配额消耗形状:264 列先拿 big(每块至少一张),再 wide;128 列在 tall / small 间交替。\n let bigLeft = shape.big\n let wideLeft = shape.wide\n let tallLeft = shape.tall\n let smallLeft = shape.small\n let hasBig = false\n let hasNonDoubled = false\n const cols: MosaicCol[] = widths.map((w, i) => {\n if (w === 264) {\n /* 264 列可装「一张大贴」(占 1 张、正方形)或「两条宽贴」(占 2 张)。\n 先满足大贴配额(它是视觉锚点),配额用尽再出双宽。 */\n if (bigLeft > 0) {\n bigLeft -= 1\n hasBig = true\n hasNonDoubled = true\n return { width: 264, sizes: ['big'] }\n }\n wideLeft -= 1\n return { width: 264, sizes: ['wide', 'wide'] }\n }\n /* 128 列可装「一张高贴」(竖直矩形)或「两条小贴」。 */\n if (tallLeft > 0) {\n tallLeft -= 1\n hasNonDoubled = true\n return { width: 128, sizes: ['tall'] }\n }\n smallLeft -= 1\n return { width: 128, sizes: ['small', 'small'] }\n })\n /* 约束放宽后的判据:整块至少要有一个「大贴」或「高贴」这类**非竖叠**形状,\n 否则整块全是成对的贴、没有任何形态变化(早期只查 hasBig,导致宽窗下\n 大贴被 preferWide 挤掉后整片都是长方形)。 */\n if (hasNonDoubled || hasBig) return cols\n if (pinFirstTwoSmalls && mode === 6 && cols.length > 0) {\n cols[0] = { width: 128, sizes: ['small', 'small'] }\n }\n }\n // 重抽耗尽:退到设计稿首帧的排法(大 | 双宽 | 高 | 双小),保证仍有大正方形。\n return mode === 3\n ? [\n { width: 264, sizes: ['big'] },\n { width: 264, sizes: ['wide', 'wide'] },\n { width: 264, sizes: ['tall'] },\n ]\n : [\n { width: 264, sizes: ['big'] },\n { width: 264, sizes: ['wide', 'wide'] },\n { width: 128, sizes: ['tall'] },\n { width: 128, sizes: ['small', 'small'] },\n ]\n}\n\n/**\n * 高 128 的块骨架:三种列宽模式(均 6u+5g)三选一,再洗牌 264 宽列的位置。\n * 3 列模式下退化为三张宽贴(3×(2u+g)+2g),与该模式其它块等宽。\n */\nexport function buildColSkeleton128(rng: () => number, mode: MosaicColumns = 6): MosaicCol[] {\n if (mode === 3) {\n return shuffled(rng, WIDTHS_3COL.map(() => ({ width: 264 as const, sizes: ['wide'] as MosaicSize[] })))\n }\n const roll = rng()\n const small = (): MosaicCol => ({ width: 128, sizes: ['small'] })\n const wide = (): MosaicCol => ({ width: 264, sizes: ['wide'] })\n const base: MosaicCol[] = roll < 1 / 3\n ? Array.from({ length: 6 }, small)\n : roll < 2 / 3\n ? [wide(), small(), small(), small(), small()]\n : [wide(), wide(), small(), small()]\n return shuffled(rng, base)\n}\n\n/** 取一个块的贴容量。 */\nfunction blockCapacity(cols: readonly MosaicCol[]): number {\n return cols.reduce((n, c) => n + c.sizes.length, 0)\n}\n\n/** 按列顺序保留能铺满的列前缀(不留空占位)。 */\nfunction trimCols(cols: readonly MosaicCol[], room: number): MosaicCol[] {\n const kept: MosaicCol[] = []\n let used = 0\n for (const col of cols) {\n if (used + col.sizes.length > room) break\n kept.push(col)\n used += col.sizes.length\n }\n return kept\n}\n\n/**\n * 排布选项。\n */\nexport interface MosaicOptions {\n /**\n * 首块首列钉成「两条小贴竖叠」:集成中心三个页面的入口贴(添加服务器 /\n * 添加技能 / 安装本地包)都固定落在左上角第一张,故需要这枚钉子。\n */\n readonly pinFirstTwoSmalls?: boolean\n /**\n * 列数模式:6(默认,设计稿的细粒度排布)或 3(窄窗降列)。\n * 调用方按容器宽选择——见 {@link pickMosaicColumns}。\n */\n readonly columns?: MosaicColumns\n /**\n * 每条数据的排布提示(与 `items` 同序)。给了它,算法就会把**名字长的**安排到\n * 264 宽的列(宽贴/大贴)上,而不是随机落进 128 宽的窄贴被截断。\n * 省略则退回纯随机(与旧行为一致)。\n */\n readonly hints?: readonly MosaicItemHint[]\n}\n\n/**\n * 按容器宽选列数:够宽用 6 列(设计稿形态),窄到单位宽撑不住长名字时降为 3 列。\n *\n * 判据是**单位宽**而不是容器宽本身:6 列下单位宽 = (容器宽 − 5×gap)/6,低于\n * {@link MIN_UNIT_FOR_6COL} 时长名字在 128 列里必然截断,此时降列比缩字更可读。\n *\n * @param containerWidth - 磁贴群的内容宽(px)。\n * @param gap - 列间隙(px)。\n * @param density - 界面密度缩放(默认 1)。\n */\nexport const MIN_UNIT_FOR_6COL = 96\n\nexport function pickMosaicColumns(containerWidth: number, gap = 8, density = 1): MosaicColumns {\n const g = gap * density\n const unit = (containerWidth - 5 * g) / 6\n return unit < MIN_UNIT_FOR_6COL * density ? 3 : 6\n}\n\n/**\n * 把 `items` 排进块序列。\n *\n * ## 长名字优先给宽槽\n * 算法**先在整条数据里**按名字长度挑出「长名」,再按槽位顺序把它们填进 264 宽的\n * 槽(大贴 / 宽贴);短名字补剩下的 128 宽槽(小贴 / 高贴)。这样长名字拿到\n * 264px 宽(窄窗降 3 列后是 ~174px 起),不再被 128px 的窄贴截成「corum…」。\n *\n * ## 为什么返回 itemIndex 而不是「按顺序数下去」\n * 长名字要跨位置挑选,落位不再是「第 i 张贴 = 第 i 条数据」;块内每张槽位都显式\n * 记下它承载的数据下标,调用方按序渲染即可(见 {@link MosaicSlot})。\n *\n * ## 其它约束\n * - 相邻块不同型(同型重抽,上限 {@link BLOCK_RETYPE_MAX});\n * - 整块放得下就整块收,放不下则列级裁剪收尾(末块允许窄于整宽,属裁剪形态);\n * - **裁剪后仍继续消费剩余数据**——早期实现此处直接退出循环,导致剩余贴被静默\n * 丢弃(实测 2 张贴只渲染出 1 张,即少显示一个条目)。\n *\n * @param items - 数据条数,或与 `hints` 等长的数据数组(只读其 `length`)。\n * @param options - 见 {@link MosaicOptions}。\n * @returns 块序列;块内槽位带 `size` 与 `itemIndex`,调用方按序遍历取数据渲染。\n */\nexport function buildMosaic(\n items: number | readonly unknown[],\n options: MosaicOptions = {},\n): MosaicBlock[] {\n const count = typeof items === 'number' ? items : items.length\n if (count <= 0) return []\n const mode = options.columns ?? 6\n const rng = mulberry32(MOSAIC_SEED)\n const blocks: MosaicBlock[] = []\n\n /* ── 数据分配:长名字优先占 264 宽的槽 ───────────────────────────────────\n 入口贴(下标 0,通常是「添加」)**不进长名队列**:它固定占整个墙的第一个槽位\n (左上角)。若把它混进队列,长名字会排到它前面、把它挤到墙中间\n (实测传 hints 时「左上角是入口贴」的命中率只有 3/118)。 */\n const hints = options.hints ?? []\n const entryPinned = options.pinFirstTwoSmalls === true && count >= 2\n const longFirst: number[] = []\n const shortRest: number[] = []\n for (let i = entryPinned ? 1 : 0; i < count; i++) {\n if ((hints[i]?.nameLength ?? 0) >= LONG_NAME_LENGTH) longFirst.push(i)\n else shortRest.push(i)\n }\n // 长名内部:更长 + 有描述者优先(大贴要给描述留高度)。\n longFirst.sort((a, b) => {\n const la = hints[a]?.nameLength ?? 0\n const lb = hints[b]?.nameLength ?? 0\n if (lb !== la) return lb - la\n const da = hints[a]?.hasDescription === true ? 1 : 0\n const db = hints[b]?.hasDescription === true ? 1 : 0\n return db - da\n })\n /** 数据分配顺序:入口贴在队首(若被钉),其后是「长名(最长在前)→ 其余」。 */\n const order = entryPinned ? [0, ...longFirst, ...shortRest] : [...longFirst, ...shortRest]\n let cursor = 0\n const take = (): number => order[cursor++] ?? 0\n\n /* ── 逐块生成 ─────────────────────────────────────────────────────────────\n 顺序至关重要:**先按剩余额度裁剪列骨架,再给留下的槽位分配数据**。\n 反过来(先给整块分配、再裁剪)会让被裁掉的那些列已经 `take()` 走数据下标,\n 而 cursor 已越过它们 ⇒ 那些数据被静默丢弃(实测 n=12 只产出 6 个槽位、\n n=6 只产出 1 个)。这是本算法长期存在的缺陷,与「末块裁剪」无关。\n\n 形状**不按名字长度决定**:每块先用 {@link planBlockShape} 取一份形状配方\n (大正方形 / 竖直矩形 / 宽贴 / 小贴各几张),再由位置消费配额 —— 于是短名字\n 也会轮到正方形与竖直矩形。名字长度只影响「谁先占宽槽」(数据分配顺序),\n 不再决定形状本身。 */\n const buildBlock = (kind: MosaicBlockKind, pin: boolean, room: number, blockIndex: number): MosaicCol[] => {\n const skeleton = kind === '264'\n ? buildColSkeleton(rng, mode, pin, planBlockShape(blockIndex))\n : buildColSkeleton128(rng, mode)\n const kept = trimCols(skeleton, room)\n /* 入口贴先钉进**视觉首列的第一个槽**(左上角)。不能只依赖宽度排序:列是按\n 宽度降序取数的(为了让长名字进 264 列),那样数据 0 会落到最宽的列、而不是\n 最靠前的列(实测命中率只有 203/400)。 */\n let firstColPlaced = false\n if (pin && kept.length > 0 && kept[0]!.sizes.length > 0) {\n const first = kept[0]!\n first.slots = [{ size: first.sizes[0]!, itemIndex: take() }]\n firstColPlaced = true\n }\n // 其余槽位:264 宽的列先填(长名字优先),128 宽的列后填。\n const ordered = [...kept].sort((a, b) => b.width - a.width)\n for (const col of ordered) {\n const start = firstColPlaced && col === kept[0] ? 1 : 0\n const rest = col.sizes.slice(start).map(size => ({ size, itemIndex: take() }))\n col.slots = col.slots === undefined ? rest : [...col.slots, ...rest]\n }\n return kept\n }\n\n /** 收一个块:列已按额度裁好,此处只做登记。 */\n const emit = (block: MosaicBlock): boolean => {\n if (block.cols.length === 0) return false\n blocks.push(block)\n return true\n }\n\n if (options.pinFirstTwoSmalls === true && count >= 2) {\n const cols = buildBlock('264', true, count, 0)\n emit({ kind: '264', cols })\n } else if (count === 1) {\n const itemIndex = take()\n emit({ kind: '128', cols: [{ width: 128, sizes: ['small'], slots: [{ size: 'small', itemIndex }] }] })\n }\n\n while (cursor < count) {\n let kind: MosaicBlockKind = rng() < 0.5 ? '264' : '128'\n const last = blocks.length === 0 ? null : blocks[blocks.length - 1]!.kind\n if (last !== null) {\n for (let i = 0; i < BLOCK_RETYPE_MAX && kind === last; i++) {\n kind = rng() < 0.5 ? '264' : '128'\n }\n if (kind === last) kind = last === '264' ? '128' : '264'\n }\n const block = { kind, cols: buildBlock(kind, false, count - cursor, blocks.length) }\n if (emit(block)) continue\n // 连一列都放不下(剩余额度小于任一列的张数):退化为单张贴窄块收尾。\n const tailKind: MosaicBlockKind = last === '264' ? '128' : '264'\n const itemIndex = take()\n const tailSize: MosaicSize = tailKind === '264' ? 'tall' : 'small'\n emit({\n kind: tailKind,\n cols: [{ width: 128, sizes: [tailSize], slots: [{ size: tailSize, itemIndex }] }],\n })\n }\n return blocks\n}\n\n/**\n * 块序列 → 扁平贴序列(按「块 → 列 → 列内自上而下」的渲染顺序),\n * 只返回尺寸档,数据由调用方按同序提供。\n *\n * 走 `buildMosaic` 的 `itemIndex` 时会重排数据,故大多数调用方应直接用\n * {@link MosaicWall}(它按 `itemIndex` 取数据);本函数留给「数据顺序与槽位\n * 顺序一致」的简单场景。\n */\nexport function flattenMosaic(blocks: readonly MosaicBlock[]): MosaicSize[] {\n const out: MosaicSize[] = []\n for (const block of blocks) {\n for (const col of block.cols) {\n for (const slot of col.slots ?? []) out.push(slot.size)\n }\n }\n return out\n}\n","/*\n * 磁贴墙排布样式(Mosaic)—— 集成中心三个内容页(插件 / MCP / 技能)共用。\n * 与 `mosaic.ts` 是一对:那个决定「哪一块哪一列放哪一档尺寸」,这里负责把\n * 几何真正落到像素上。**这是排布样式的单一事实源**(三个页面不再各持副本)。\n *\n * 几何口径(design.pen 四帧逐帧读实):\n * 块内容宽 = 808(= 磁贴群宽):264+8+264+8+128+8+128,也是 128×6+8×5。\n * 块高两档:264 块 / 128 块。\n * 列宽两档:264 / 128;列内贴纵向排、gap 8。\n *\n * 尺寸档靠 `data-tile-size` 属性选择器(不是类名):尺寸与块型是同一份算法的\n * 两个输出,属性让 DOM 自带可断言口径(验收时直接读 data 属性)。\n *\n * 密度缩放(--corum-density-scale)与字号缩放(--corum-ui-font-scale)全仓统一;\n * 磁贴内的字号乘字号缩放,间距/圆角/尺寸乘密度缩放。\n */\n\n/* ── 磁贴群:块行纵向堆叠 ─────────────────────────────────────────────────── */\n\n.tileGrid {\n /* 响应式流体基准(本元素的 inline-size 作为查询容器,故 `100cqw` = 本元素内容宽)。\n 整套排布是**正方形单位网格**:264 = 2×128 + 8,故一个单位宽算出来后,列宽与\n 块高都能精确表达(见下)。\n\n 单位宽**只需一个算式**,与「几列」无关:\n 6 列块 [2,2,1,1] 单位 = 2×(2u+g) + 2×u + 3g = 6u+5g\n 3 列块 [2,2,2] 单位 = 3×(2u+g) + 2g = 6u+5g\n 4 列块 [1,1,2,2] 单位 = u+u+(2u+g)+(2u+g)+3g = 6u+5g\n 三种列数的块宽恒等 ⇒ u = (W − 5g)/6 一套通吃;窄窗时 u 随之缩小、宽窗封顶在\n 稿里的 128 ⇒ 宽窗下与设计稿逐像素一致。\n ⚠️ 曾误加过一条「3 列用 (W−2g)/3」的式子:那是把**列宽**当成了单位宽,\n 会让 264 列变成 356 宽、三列放不下(实测块内容溢出到 944 > 538)。 */\n container-type: inline-size;\n --mosaic-gap: calc(8px * var(--corum-density-scale, 1));\n --mosaic-unit: min(\n calc(128px * var(--corum-density-scale, 1)),\n calc((100cqw - 5 * var(--mosaic-gap)) / 6)\n );\n\n display: flex;\n flex-direction: column;\n gap: var(--mosaic-gap);\n align-content: start;\n min-width: 0;\n}\n\n/* ── 块行:横向 flex,各列等高 ─────────────────────────────────────────────\n `width: 100%` 是必需的:列宽走单位宽(见下),若本行不撑满容器,列会按\n 自身宽度收缩、整块右侧留白;早期副本漏了这条,列被 `flex: N 1 0` 拉到\n 808 宽(贴被拉成整行)——列宽改为单位宽 + 本行撑满后不再互相争抢。 */\n.mosaicBlock {\n display: flex;\n align-items: stretch;\n gap: var(--mosaic-gap);\n width: 100%;\n min-width: 0;\n}\n\n/* ── 块高 ─────────────────────────────────────────────────────────────────\n 几何上:264 块 = 2 单位 + 1 间隙(与 264 = 2×128+8 同构);128 块 = 1 单位。\n 宽窗(单位封顶 128)下就是设计稿的 264 / 128,逐像素一致。\n\n 但**贴的内容有最小高度需求**(图标 + 名称 + 作者 ≈ 96px),而窄窗降列后单位宽\n 掉到 80px 出头 ⇒ 264 块里的「两条竖叠贴」各只有 83px、128 块只有 83px,内容差\n 1px 放不下(实测 3 列下 16 张贴全被裁)。故给两种块各加**可读性保底**:\n - 128 块:≥ 一张贴的最小内容高(96);\n - 264 块:≥ 两条竖叠贴各 96 + 间隙(96×2 + 8 = 200)。\n 宽窗下单位推导值更大、保底不生效,设计稿形态不变;窄窗下块略高于正方形单位\n (代价是磁贴不再严格正方形),换来内容不被裁。 */\n.mosaicBlock[data-mosaic-block='264'] {\n height: max(\n calc(2 * var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) + var(--mosaic-gap)),\n calc(200px * var(--corum-density-scale, 1))\n );\n}\n.mosaicBlock[data-mosaic-block='128'] {\n height: max(\n var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))),\n calc(96px * var(--corum-density-scale, 1))\n );\n}\n\n/* ── 列:纵向 flex;宽度由 data-col 决定 ───────────────────────────────────\n 列宽必须是**单位宽的整数倍**而不是 flex 比例:264 列的语义是「能装一张大贴\n 或两条宽贴」,若用 flex-grow 分配,窗口越宽列越宽、贴被拉成条状(不再是磁贴)。\n 128 列 = 1 单位,264 列 = 2 单位 + 1 间隙(128+8+128 = 264,纵向容量相同)。 */\n.mosaicCol {\n display: flex;\n flex-direction: column;\n gap: var(--mosaic-gap);\n min-width: 0;\n min-height: 0;\n flex: none;\n width: var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1)));\n}\n\n.mosaicCol[data-col='264'] {\n width: calc(2 * var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) + var(--mosaic-gap));\n}\n\n/* 列内贴:撑满列宽;纵向按尺寸档分配列高。 */\n.mosaicCol > * {\n flex: 1 1 0;\n min-height: 0;\n min-width: 0;\n}\n\n/* 大贴 / 高贴独占整列(`auto` 基准让它们按内容与列高取大者,实际等同撑满)。 */\n.mosaicCol > [data-tile-size='big'],\n.mosaicCol > [data-tile-size='tall'] { flex: 1 1 auto; }\n\n/* ── 磁贴本体 ───────────────────────────────────────────────────────────── */\n\n.tile {\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n overflow: hidden;\n box-sizing: border-box;\n /* 内边距随单位宽一起收(稿里 12px 是按 128 基准定的):缩到 83px 时那 24px\n 上下内边距会占掉近三成高度,是底部被裁的主因。宽窗封顶 12、窄窗压到 8。 */\n padding: clamp(\n calc(8px * var(--corum-density-scale, 1)),\n calc(var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) * 0.09),\n calc(12px * var(--corum-density-scale, 1))\n );\n border: 1px solid var(--corum-glass-border);\n border-radius: 10px;\n background: var(--tile-tint, var(--corum-glass-1));\n text-align: left;\n font-family: inherit;\n cursor: pointer;\n transition: border-color .15s, background .15s;\n}\n\n.tile:hover { border-color: var(--corum-glass-border-active); }\n.tile:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }\n\n/* 四档语义底色(design.pen 磁贴 fill,写死 hex 的四色)。带 alpha ⇒ 叠浅色底\n 自然变浅、叠深色底保持霓虹,深浅共用一组值,无需主题分叉。 */\n.tileTintViolet { --tile-tint: var(--corum-tile-violet, var(--corum-glass-1)); }\n.tileTintDeep { --tile-tint: var(--corum-tile-deep, var(--corum-glass-1)); }\n.tileTintSlate { --tile-tint: var(--corum-tile-slate, var(--corum-glass-1)); }\n.tileTintMauve { --tile-tint: var(--corum-tile-mauve, var(--corum-glass-1)); }\n\n/* 选中态:抬高一层玻璃 + 激活描边。 */\n.tileActive {\n background: var(--corum-glass-2);\n border-color: var(--corum-glass-border-active);\n}\n\n/* 大贴的品牌色 glow(design.pen 大贴内的 radial ellipse)。 */\n.tile[data-tile-size='big']::before,\n.tileGlow::before {\n content: '';\n position: absolute;\n inset: 0;\n pointer-events: none;\n background: radial-gradient(circle at 78% 22%,\n color-mix(in srgb, var(--dsw-alias-brand-primary) 36%, transparent) 0%,\n transparent 62%);\n}\n\n/* ── 磁贴内部分区 ───────────────────────────────────────────────────────── */\n\n.tileTop {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(6px * var(--corum-density-scale, 1));\n}\n\n.tileIcon {\n flex: none;\n display: inline-flex;\n color: var(--dsw-alias-brand-text);\n}\n\n/* 角标(热度 / 状态点 / 开关)绝对定位于右上角,不参与文档流 ⇒ 不挤压图标。 */\n.tileCorner {\n position: absolute;\n top: calc(10px * var(--corum-density-scale, 1));\n right: calc(10px * var(--corum-density-scale, 1));\n display: inline-flex;\n align-items: center;\n gap: calc(3px * var(--corum-density-scale, 1));\n}\n\n.tileBottom {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: calc(3px * var(--corum-density-scale, 1));\n min-width: 0;\n}\n\n/* 名称与版本徽章**纵向**排(不是并排):并排时窄贴(128)里名称会被徽章挤到\n 只剩一两个字(实测 12/18 张被截断、极端情况只剩「d.」)。纵向后名称独享\n 整行宽、可换行到两行,徽章另起一行。 */\n.tileNameRow {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: calc(4px * var(--corum-density-scale, 1));\n min-width: 0;\n width: 100%;\n}\n\n.tileName {\n width: 100%;\n min-width: 0;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 3;\n overflow: hidden;\n overflow-wrap: anywhere;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n font-size: calc(13px * var(--corum-ui-font-scale, 1));\n line-height: 1.3;\n /* 固定三行行高:内容长短不一(占位贴只有「添加」)时磁贴内文字块不跳动。\n 简介移出卡片后这里由两行放宽到三行 —— 腾出的空间给名称。 */\n min-height: calc(3 * 1.3em);\n}\n\n.tile[data-tile-size='big'] .tileName { font-size: calc(15px * var(--corum-ui-font-scale, 1)); }\n\n.tileVersion {\n flex: none;\n display: inline-flex;\n align-items: center;\n height: calc(16px * var(--corum-density-scale, 1));\n padding: 0 calc(6px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 999px;\n background: var(--corum-glass-3);\n color: var(--dsw-alias-label-tertiary);\n font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: calc(9px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n white-space: nowrap;\n width: fit-content;\n}\n\n.tileSub {\n font-size: calc(10px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-tertiary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ── 按形状分档的贴内布局 ─────────────────────────────────────────────────\n 同一个 `MosaicTileBody` 骨架,四种形状各自换**信息组织方式**(不只是调字号):\n\n big 264×264 正方形:图标居左上 → 名称(最多 3 行)→ 作者\n wide 264×128 宽扁: **图文并排**(图标左、文字右)→ 名称(2 行)→ 作者\n tall 128×264 窄高: 图标居上 → 名称(3 行)→ 作者沉底\n small 128×128 最小: 图标 + 名称(2 行)\n\n ⚠️ **卡片上不渲染简介**(用户 2026-10-02 定调:「我希望将简介放在详细信息里去」)。\n 简介只在右侧详情面板呈现(三页的 `.detailDesc` 已展示全文)。理由有二:\n 1. 卡片面积有限,简介在窄贴里只能被钳成两三行残句,信息价值低;\n 2. 原先靠「@container 按贴高隐藏简介」实现分档,而贴高随窗口在阈值附近来回跨越\n ⇒ **同一张卡片在不同窗口尺寸下简介时而出现时而消失**(用户实测报障\n 「缩小窗口后小卡片反而有简介,放大全屏反而没有」)。移除后这类分支不复存在。\n 腾出的纵向空间让给名称(行数 2 → 3)与图标(略放大),卡片不显得空。\n\n 实现方式:骨架元素(`.tileBodyScaffold`)按形状切换 `flex-direction`,\n 而不是给每种形状写一套 JSX —— 结构一份、布局四档,消费方无需分支。\n -------------------------------------------------------------------------- */\n\n.tileBodyScaffold {\n display: flex;\n flex-direction: column;\n gap: calc(3px * var(--corum-density-scale, 1));\n min-width: 0;\n min-height: 0;\n width: 100%;\n}\n\n/* wide:图文并排——图标与文字块左右分栏,图标不再独占一行。 */\n.tile[data-tile-size='wide'] .tileBodyScaffold {\n flex-direction: row;\n align-items: flex-start;\n gap: calc(10px * var(--corum-density-scale, 1));\n}\n\n.tile[data-tile-size='wide'] .tileBodyText {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* tall:纵向富余 ⇒ 作者沉底(名称与作者分列两端)。 */\n.tile[data-tile-size='tall'] .tileBodyScaffold {\n justify-content: space-between;\n}\n\n/* 大贴与高贴的名称可占三行(简介移出卡片后,行数从两行放宽)。 */\n.tile[data-tile-size='big'] .tileName,\n.tile[data-tile-size='tall'] .tileName { -webkit-line-clamp: 3; min-height: calc(3 * 1.3em); }\n\n/* ── 磁贴角标:状态点 / 启停开关 ─────────────────────────────────────────── */\n\n.tileDot {\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.tileDotOff { background: var(--dsw-alias-label-dimmed); }\n\n.tileSwitch {\n flex: none;\n position: relative;\n width: calc(32px * var(--corum-density-scale, 1));\n height: calc(18px * var(--corum-density-scale, 1));\n padding: 0;\n border: 1px solid transparent;\n border-radius: 999px;\n background: var(--dsw-alias-brand-primary);\n cursor: pointer;\n transition: background .15s;\n}\n\n.tileSwitch[data-off] {\n background: var(--corum-glass-3);\n border-color: var(--corum-glass-border);\n}\n\n.tileSwitch:disabled { opacity: .5; cursor: default; }\n\n.tileSwitchKnob {\n position: absolute;\n top: 2px;\n left: 14px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--corum-label-on-brand);\n transition: left .15s;\n}\n\n.tileSwitch[data-off] .tileSwitchKnob {\n left: 2px;\n background: var(--dsw-alias-label-dimmed);\n}\n\n/* ── 占位空卡(无数据时的虚线占位,保留设计稿的「即将上线」形态)────────── */\n\n.tilePlaceholder {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(8px * var(--corum-density-scale, 1));\n overflow: hidden;\n box-sizing: border-box;\n padding: calc(12px * var(--corum-density-scale, 1));\n border: 1px dashed var(--corum-glass-border);\n border-radius: 10px;\n background: transparent;\n color: var(--dsw-alias-label-dimmed);\n cursor: default;\n}\n\n.tilePlaceholderIcon {\n display: inline-flex;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.tilePlaceholderText {\n margin: 0;\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* ── 最热门热排(196×120 ×4,只在插件市场用)────────────────────────────── */\n\n/* 热排是 4 张等宽贴 + 3 个间隙;与磁贴群用同一套单位口径:\n 196 = 264 − 2×128/…… 不成立,故热排不按单位推,而是**按容器等分**\n (每张 =(100% − 3×间隙)/4)。窄窗下与磁贴群同步收缩、不留溢出。 */\n.hotRow {\n display: flex;\n gap: var(--mosaic-gap, calc(8px * var(--corum-density-scale, 1)));\n min-width: 0;\n}\n\n.hotRow > * {\n flex: 1 1 0;\n min-width: 0;\n height: calc(120px * var(--corum-density-scale, 1));\n}\n\n/*\n * 窄窗适配:磁贴自身是尺寸查询容器。窗口变窄时整面墙按单位宽等比缩小,但**文字\n * 不跟着缩**(缩字会伤可读性),于是小贴会先装不下:实测 1280 窗口下小贴被缩到\n * 83×83,而「图标行 + 两行名称 + 版本徽章 + 作者小字」需约 99px,底部被裁。\n * 按重要性依次让位(名称 > 作者 > 版本):\n * ① 名称收单行(保住主标识,仍有 ellipsis);\n * ② 再小则隐去版本徽章(版本在右侧详情面板里始终可见)。\n * 宽窗(单位 128 或更大)两条都不触发,保持设计稿的两行外观与完整信息。\n *\n * 容器**不留 container-name**:CSS module 会给名字加哈希前缀,而 `@container`\n * 查询里的名字是字面量、不参与哈希 ⇒ 具名查询永远匹配不上。匿名查询取最近的\n * 容器,正是这里的 .tile。\n */\n.tile {\n container-type: size;\n}\n\n@container (max-height: 180px) {\n .tileName {\n -webkit-line-clamp: 2;\n min-height: calc(2 * 1.3em);\n }\n}\n\n@container (max-height: 140px) {\n .tileName {\n -webkit-line-clamp: 1;\n white-space: nowrap;\n text-overflow: ellipsis;\n /* 单行时行高也收回一行:三行行高是为「不跳动」留的,窄贴里会把作者顶出贴外。 */\n min-height: calc(1.3em);\n }\n}\n\n@container (max-height: 170px) {\n .tileVersion { display: none; }\n}\n\n/* 最后一段:最小窗口(1219)下小贴被缩到 73px,而图标是 TSX 给的固定 24px\n (不随单位缩)⇒ 图标 + 名称 + 作者仍差几像素装不下。此时把图标压到 16px。\n 图标尺寸本身与「界面字号」设置无关(同族插件都固定像素),故这里只按容器\n 高度收,不参与 --corum-ui-font-scale。 */\n@container (max-height: 85px) {\n .tileIcon svg {\n width: 16px;\n height: 16px;\n }\n}\n","/**\n * 磁贴墙(Mosaic)React 组件 —— 集成中心三个内容页共用的展示骨架。\n * 算法在 `./mosaic.ts`、几何在 `./mosaic.module.css`;本文件把两者接成组件。\n *\n * ## 为什么抽出来\n *\n * 插件 / MCP / 技能三页原本各持一份「块生成器 + 排布 CSS + 磁贴本体 + 详情面板」\n * 的副本(跨包 CSS module 不能直接 import 的历史约束)。三份副本必然分叉,实测\n * 已经分叉过:插件页列宽走 `[data-col]` 属性选择器、另两页走 `.mosaicCol264`\n * 类名,且后者漏了块行的 `width: 100%` ⇒ MCP 页的每张贴被拉成整行 808×128,\n * 完全不是磁贴。收敛到本模块后只有一个几何实现。\n *\n * ## 用法\n *\n * ```tsx\n * const blocks = buildMosaic(items.length) // 或 flattenMosaic 取尺寸序列\n * <MosaicWall items={items} blocks={blocks} renderTile={(item, size, i) => ...} />\n * ```\n *\n * @module corum-ui-base/client/Mosaic\n */\nimport { Fragment, useEffect, useState, type MutableRefObject, type ReactNode } from 'react'\nimport css from './mosaic.module.css'\nimport { pickMosaicColumns } from './mosaic.ts'\nimport type { MosaicBlock, MosaicColumns, MosaicSize } from './mosaic.ts'\n\n/** 磁贴底色四档(design.pen 磁贴 fill 的四色,token 见 theme.css)。 */\nexport type MosaicTint = 'violet' | 'deep' | 'slate' | 'mauve'\n\n/** 档位 → 样式类名(消费方按语义挑档,不再自己拼类名)。 */\nconst TINT_CLASS: Readonly<Record<MosaicTint, string>> = {\n violet: css.tileTintViolet,\n deep: css.tileTintDeep,\n slate: css.tileTintSlate,\n mauve: css.tileTintMauve,\n}\n\n/**\n * 贴内骨架的 props:**一份结构、四种布局**。\n *\n * 形状由外层贴的 `data-tile-size` 决定(CSS 按它切 `flex-direction` 与行数),\n * 故这里不需要 `size` 参数 —— 消费方把骨架放进已带 `data-tile-size` 的贴里即可。\n *\n * 各形状的取舍(用户 2026-10-02 定调):\n * - `big`:图标居左上 → 名称(最多 3 行)→ 作者;\n * - `wide`:图文**并排**(图标左、文字右);\n * - `tall`:纵向 → 名称 → 作者沉底;\n * - `small`:图标 + 名称。\n *\n * **卡片上不渲染简介**(用户 2026-10-02 定调):简介只在右侧详情面板呈现。\n * 故本组件没有 `desc` prop —— 原先按贴高隐藏简介的容器查询会让同一张卡片在不同\n * 窗口尺寸下时有时无(用户实测报障),移除后该分支不复存在。\n */\nexport interface MosaicTileBodyProps {\n /** 图标(消费方按形状自定尺寸;骨架只负责落位)。 */\n readonly icon: ReactNode\n /** 名称(必给)。 */\n readonly name: ReactNode\n /** 版本徽章;省略则不渲染。窄贴会被容器查询自动隐去。 */\n readonly version?: ReactNode\n /** 底部小字(作者 / 地址等);省略则不渲染。 */\n readonly sub?: ReactNode\n /** 右上角角标(热度 / 状态点 / 开关);绝对定位,不参与排版。 */\n readonly corner?: ReactNode\n}\n\n/**\n * 按形状分档的贴内骨架。\n *\n * @param props - 见 {@link MosaicTileBodyProps}。\n * @returns 贴内结构(外层应是一个带 `data-tile-size` 的磁贴元素)。\n */\nexport function MosaicTileBody({ icon, name, version, sub, corner }: MosaicTileBodyProps): ReactNode {\n return (\n <>\n {corner}\n <div className={css.tileBodyScaffold}>\n <span className={css.tileIcon}>{icon}</span>\n <div className={`${css.tileBottom} ${css.tileBodyText}`}>\n <div className={css.tileNameRow}>\n <span className={css.tileName}>{name}</span>\n {version !== undefined && <span className={css.tileVersion}>{version}</span>}\n </div>\n {sub !== undefined && <span className={css.tileSub}>{sub}</span>}\n </div>\n </div>\n </>\n )\n}\n\n/** 磁贴墙:把块序列渲染成块行 + 列 + 贴。 */\nexport interface MosaicWallProps<T> {\n /** 贴的数据源(按渲染顺序,与 {@link flattenMosaic} 的顺序一致)。 */\n readonly items: readonly T[]\n /** 块序列(`buildMosaic` 的输出)。 */\n readonly blocks: readonly MosaicBlock[]\n /**\n * 列数模式(与传给 `buildMosaic` 的 `columns` 一致)。\n *\n * 必须传:单位宽算式随列数切换(6 列 `(W−5g)/6` / 3 列 `(W−2g)/3`),CSS 靠这个\n * 属性选式子。不传则 3 列下三列宽和小于块宽、右缘留白(实测 522 vs 538)。\n */\n readonly columns?: MosaicColumns\n /**\n * 渲染单张贴。\n * @param item - 对应位置的数据。\n * @param size - 该位置应使用的尺寸档(已由算法决定,消费方按它调字号/图标)。\n * @param index - 在整面墙里的序号(0 起;入口贴通常在第 0 位)。\n */\n readonly renderTile: (item: T, size: MosaicSize, index: number) => ReactNode\n}\n\n/**\n * 磁贴墙(块行 → 列 → 贴)。\n *\n * @param props - 见 {@link MosaicWallProps}。\n * @returns 磁贴墙元素。\n */\nexport function MosaicWall<T>({ items, blocks, columns = 6, renderTile }: MosaicWallProps<T>): ReactNode {\n return (\n <div className={css.tileGrid} data-mosaic-columns={columns}>\n {blocks.map((block, bi) => (\n <div key={`block:${bi}`} className={css.mosaicBlock} data-mosaic-block={block.kind}>\n {block.cols.map((col, ci) => (\n <div key={`col:${bi}:${ci}`} className={css.mosaicCol} data-col={col.width}>\n {col.sizes.map((size, si) => {\n /* 数据下标由算法给出(长名字会跨位置挑选,落位不再等于渲染序),\n 无槽位信息时退回「按序取」以兼容只给 sizes 的简单用法。 */\n const index = col.slots?.[si]?.itemIndex ?? si\n const item = items[index]\n if (item === undefined) return null\n return (\n <Fragment key={`tile:${bi}:${ci}:${si}`}>\n {renderTile(item, size, index)}\n </Fragment>\n )\n })}\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n}\n\n/**\n * 取某一张贴该挂的属性(消费方展开到自己的 `<button>` 上)。\n *\n * 贴必须是列的**直接子元素**(CSS 靠 `.mosaicCol > [data-tile-size]` 分档列高),\n * 故这里不包壳层元素,只把类名与 data 属性交给消费方。\n *\n * @param props - 见 {@link MosaicTileProps}。\n * @returns 可展开的属性对象。\n */\nexport function mosaicTileAttrs(props: MosaicTileProps): {\n className: string\n 'data-tile-size': MosaicSize\n} {\n return {\n className: mosaicTileClass(props),\n 'data-tile-size': props.size,\n }\n}\n\n/** 磁贴按钮的属性集(消费方直接展开到自己的 `<button>`/`<div>` 上)。 */\nexport interface MosaicTileProps {\n /** 尺寸档(算法给的,决定字号/图标/是否独占列高)。 */\n readonly size: MosaicSize\n /** 底色档。 */\n readonly tint: MosaicTint\n /** 是否选中(进详情面板的那一张)。 */\n readonly active?: boolean\n /** 是否加品牌色 glow(大贴/主推)。 */\n readonly glow?: boolean\n /** 额外类名。 */\n readonly className?: string\n}\n\n/** 拼磁贴的类名(`size` 走 data 属性,其余走类名)。 */\nexport function mosaicTileClass({ tint, active, glow, className }: MosaicTileProps): string {\n return [\n css.tile,\n TINT_CLASS[tint],\n active === true ? css.tileActive : '',\n glow === true ? css.tileGlow : '',\n className ?? '',\n ].filter(Boolean).join(' ')\n}\n\n/** 共享样式表的窄化面(消费方拼自己的结构时按需取这些类名)。 */\nexport interface MosaicStyleSheet {\n readonly tileGrid: string\n readonly mosaicBlock: string\n readonly mosaicCol: string\n readonly tile: string\n readonly tileTop: string\n readonly tileBodyScaffold: string\n readonly tileBodyText: string\n readonly tileIcon: string\n readonly tileCorner: string\n readonly tileBottom: string\n readonly tileNameRow: string\n readonly tileName: string\n readonly tileVersion: string\n readonly tileSub: string\n readonly tileDot: string\n readonly tileDotOff: string\n readonly tileSwitch: string\n readonly tileSwitchKnob: string\n readonly tilePlaceholder: string\n readonly tilePlaceholderIcon: string\n readonly tilePlaceholderText: string\n readonly hotRow: string\n}\n\n/**\n * 共享样式表:三个页面拼贴内结构(图标行 / 名称行 / 小字…)时取这里的类名,\n * 不再各持一份 CSS。返回的是本模块自己 import 的那份 CSS module 对象。\n */\nexport const mosaicStyles: MosaicStyleSheet = {\n tileGrid: css.tileGrid,\n mosaicBlock: css.mosaicBlock,\n mosaicCol: css.mosaicCol,\n tile: css.tile,\n tileTop: css.tileTop,\n tileBodyScaffold: css.tileBodyScaffold,\n tileBodyText: css.tileBodyText,\n tileIcon: css.tileIcon,\n tileCorner: css.tileCorner,\n tileBottom: css.tileBottom,\n tileNameRow: css.tileNameRow,\n tileName: css.tileName,\n tileVersion: css.tileVersion,\n tileSub: css.tileSub,\n tileDot: css.tileDot,\n tileDotOff: css.tileDotOff,\n tileSwitch: css.tileSwitch,\n tileSwitchKnob: css.tileSwitchKnob,\n tilePlaceholder: css.tilePlaceholder,\n tilePlaceholderIcon: css.tilePlaceholderIcon,\n tilePlaceholderText: css.tilePlaceholderText,\n hotRow: css.hotRow,\n}\n\nexport { mosaicStyles as css }\n\n/**\n * 按容器宽决定列数,并在窗口变化时更新(窄窗降 3 列、宽窗回 6 列)。\n *\n * 为什么需要它:6 列在窄窗下单位宽会掉到 80px 出头,长名字必然截断;降成 3 列后\n * 每列宽约翻倍(同一容器宽摊到更少的列上),名称与作者都能正常显示。两种模式的\n * 块宽**恒等**(6u+5g),故切换只改块内列结构、不改变整墙宽度。\n *\n * @param ref - 磁贴群容器(量其内容宽;`ResizeObserver` 监听尺寸变化)。\n * @returns 当前应使用的列数。\n */\nexport function useMosaicColumns(ref: MutableRefObject<HTMLElement | null>): MosaicColumns {\n const [columns, setColumns] = useState<MosaicColumns>(6)\n useEffect(() => {\n const el = ref.current\n if (el === null || typeof ResizeObserver === 'undefined') return\n const measure = (): void => {\n const style = getComputedStyle(el)\n const gap = Number.parseFloat(style.gap) || 8\n /* scrollbar-gutter 预留的滚动槽也计入 clientWidth,扣掉才是真正的列可用宽。 */\n const contentWidth = el.clientWidth - (el.offsetWidth - el.clientWidth)\n setColumns(pickMosaicColumns(contentWidth, gap))\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => { ro.disconnect() }\n }, [ref])\n return columns\n}\n","/**\n * 平台相关的路径工具(P2:渲染层消费面收口的**唯一共享面**)。\n *\n * 事实源:渲染层经 preload 的 `window.corumDesktop.getPlatform()`(合法 window\n * 挂载,写一次只读,红线 1 例外)拿到**实际运行平台**的路径分隔符;桥缺席\n * (非桌面壳,如纯 web)时按 POSIX 处理。渲染层**拿不到 `process.platform`**\n * (方案 §4.4:plugins 各包 src 里零命中),也**绝不硬编码 `/`**——Windows 的\n * cwd 用 `\\`,硬拼 `/` 永不匹配。\n *\n * 各消费点(file-tool-card / SubagentChanges / EmptyStateHero / FileExplorer)\n * 从这里取,不各自 import 整份桥声明(红线 3:本模块即那份窄接口)。\n * @module corum-ui-base/client/platform-paths\n */\n\ndeclare global {\n interface Window {\n /** 桌面 preload 暴露的桥(本模块只用到 getPlatform 这一个方法)。 */\n corumDesktop?: { getPlatform?: () => 'darwin' | 'linux' | 'win32' }\n }\n}\n\n/** 当前平台的路径分隔符('\\\\' = win32,'/' = 其余/桥缺席)。 */\nexport function pathSep(): '/' | '\\\\' {\n if (typeof window === 'undefined') return '/'\n return window.corumDesktop?.getPlatform?.() === 'win32' ? '\\\\' : '/'\n}\n\n/**\n * 拼一段路径(`parent` + `name`),分隔符按当前平台。\n * `parent` 已以分隔符结尾(含根 '/' 或盘符根 'C:\\\\')时不重复加。\n */\nexport function joinPath(parent: string, name: string): string {\n if (parent === '') return name\n if (parent.endsWith('/') || parent.endsWith('\\\\')) return `${parent}${name}`\n return `${parent}${pathSep()}${name}`\n}\n\n/**\n * 取路径末段(basename),同时认 `/` 与 `\\`——输入可能来自任一端\n * (host 给的路径用平台分隔符,快照里的相对路径常用 `/`),只认一种会在\n * 跨平台数据流上漏判。\n */\nexport function basenameOf(path: string): string {\n const parts = path.replace(/[/\\\\]+$/, '').split(/[/\\\\]/)\n return parts[parts.length - 1] ?? path\n}\n\n/**\n * cwd 相对化(官方 `relativizeToCwd` 同语义):`path` 在 `cwd` 之下时返回相对段。\n * 分隔符按当前平台匹配(Windows 的 cwd 用 `\\`,硬拼 `/` 永不匹配)。\n */\nexport function shortenPath(path: string, cwd: string | undefined): string {\n if (cwd === undefined || cwd === '') return path\n const prefix = `${cwd}${pathSep()}`\n if (path.startsWith(prefix)) return path.slice(prefix.length)\n return path\n}\n","/**\n * 「web 搜索 provider 的凭据引用」这条规则的**唯一家**。\n *\n * 规则本身:settings section 里显式声明的 `apiKeyEnv` 优先,否则回落到 provider\n * 自己解析时使用的环境变量名常量。\n *\n * 收敛前这条规则在本仓有第三份拷贝\n * (`corum-ui-settings-plugins/src/client/web-search-card-controller.ts` 的\n * 局部 `refOf()`,已随那张卡的注册一并删除);另两份在 `corum-ui-settings-models`\n * 的 `keyRefFor` / `apiKeyEnvOf`。**不要**把二者合并:`keyRefFor` 的规则是\n * `named ?? <PROVIDER>_API_KEY`(按 provider 名推导),与这里的固定常量语义不同。\n *\n * 放在 `@corum/corum-ui-base/client` 而不是某个设置页里:该包是本仓唯一被跨\n * bundle **值** import 的 client 面(实证 `corum-ide-plugin-manager-ui/src/\n * client/index.tsx`),tsdown 能把这份源码内联进每个消费方 bundle;而跨 client\n * 包的值 import 会被 tsdown 解析失败(同教训:client bundle 之间只敢 type-only\n * import)。\n *\n * @module @corum/corum-ui-base/client/web-search-credential\n */\n\n/**\n * 官方 `@deepseek-ai/dsh-web-search-deepseek` provider 在 settings section 未声明\n * `apiKeyEnv` 时解析的凭据引用(它的 `DEFAULT_API_KEY_ENV`,未导出,此处按同一\n * 事实重声明)。\n */\nexport const DEFAULT_WEB_SEARCH_API_KEY_REF = 'DEEPSEEK_API_KEY'\n\n/** 参与凭据引用判定的 settings section 窄化面。 */\nexport interface WebSearchCredentialSection {\n /** section 声明的凭据引用;缺省/空串 = 用 provider 默认。 */\n apiKeyEnv?: string\n}\n\n/**\n * 一个 web 搜索 settings section 实际寻址的凭据引用。\n * @param section - 当前 section 值(namespace 未注册时可能为 undefined)。\n * @returns 要寻址的引用名。\n */\nexport function webSearchCredentialRef(\n section: WebSearchCredentialSection | undefined | null,\n): string {\n const declared = section?.apiKeyEnv\n return declared !== undefined && declared.length > 0 ? declared : DEFAULT_WEB_SEARCH_API_KEY_REF\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["forwardRef","useCallback","css","forwardRef","useRef","useCallback","css","useState","createContext","useContext","useState","useRef","useCallback","createPortal","css","css","createPortal","css","Fragment","useState"],"sources":["../src/client/grid.ts","../src/client/RegionCard.module.css","../src/client/RegionCard.tsx","../src/client/GridView.module.css","../src/client/GridView.tsx","../src/client/FloatingLayer.module.css","../src/client/FloatingLayer.tsx","../src/client/ConfirmDialog.module.css","../src/client/ConfirmDialog.tsx","../src/client/theme-presenter.ts","../src/client/mosaic.ts","../src/client/mosaic.module.css","../src/client/MosaicWall.tsx","../src/client/platform-paths.ts","../src/client/web-search-credential.ts"],"sourcesContent":["/**\n * GridView 布局树 —— VSCode editor-group 式自由二维网格的数据模型。\n *\n * 布局是一棵分割树:\n * - Leaf:一个模块窗格(持有 slotKey,渲染对应槽位内容)。\n * - Branch:一个水平(row)或垂直(column) split,含若干子节点 + 各子节点\n * 占比 weight(相对份额,渲染时换算成 px/fr),子节点间是 sash。\n *\n * 本包(shell-base)只提供通用机制:树类型 + 全部树操作 + 槽位注册表 +\n * 构造 helper(leafNode/rowBranch/columnBranch)+ 持久化(key 可配)。\n * 不含任何内置槽位与默认布局——子壳用 registerSlot() 注册自己的槽位、\n * 用构造 helper 写字面量布局树作为初始布局。\n *\n * 这棵树是唯一事实源,持久化到 localStorage(尺寸份额 + 结构)。\n */\n\n/**\n * 可进网格的槽位 key。泛型槽域:默认 `string`(运行时动态注册,行为与历史\n * 完全一致);子壳可用字面量联合实例化(如 ide-shell 的 IdeGridSlot)让\n * registerSlot 等定义处获得拼写/重命名的编译期保障。纯类型层——本文件保持\n * cordis-free(零 import),运行时零变化;树结构 GridNode 仍是 string 槽域\n * (运行时本就可容纳动态注册的任意槽,不做静态收窄)。\n */\nexport type GridSlot<T extends string = string> = T\n\n/** 槽位元数据:显示名 + 默认权重 + 区域最小尺寸(可选)。 */\nexport interface SlotMeta {\n label: string\n defaultWeight: number\n /**\n * 区域最小宽度(px):叶子落在 row 分支(左右并排)时,sash 拖拽收窄的\n * 下限。不声明时用 SLOT_FALLBACK_MIN_WIDTH 兜底,保证基本浏览体验。\n */\n minWidth?: number\n /** 区域最小高度(px):叶子落在 column 分支(上下叠放)时的下限;缺省 SLOT_FALLBACK_MIN_HEIGHT。 */\n minHeight?: number\n /**\n * 钉住(pinned):该槽位的 leaf 在网格中位置/归属固定,不参与自由组合——\n * 不可被 drop 拖走(作 source)、不可被拖入 split/swap(作 target)、不可被\n * 摘除。用于 IDE 侧栏这类「位置固定、其余区域自由组合」的壳。sash 拖拽不\n * 受此限(它只调相邻权重、不移动 leaf 位置;钉住叶独立成列后天然不被其它\n * sash 影响)。缺省 false(全部区域自由组合)。\n */\n pinned?: boolean\n /**\n * 折叠宽(px):该槽位可被「折叠收起」为一条窄轨(如 IDE 侧栏收成 56px\n * 图标轨)。声明后 GridView 经 `collapsedSlots` 运行时状态把它渲染成此固定\n * 宽(不参与 weight 分配、不可 sash 拖),同时 subtreeMinSize 在折叠态取此值\n * (替代 minWidth——窗口自适应不会把它拉回展开宽)。缺省不可折叠。\n */\n collapsedWidth?: number\n /**\n * 可见性(C1 插件自声明槽):\n * - 'fixed' —— 壳/专职插件固定占位的槽(侧栏/对话区/编辑器…),不进\n * 「添加区域」面板与插件中心视图管理;\n * - 'addable' —— 用户可经「添加区域」/视图管理自由拖入网格(缺省值,\n * 与历史行为一致);\n * - 'hidden' —— 无独立 UI 的插件(纯服务/壳自身/测试占位),注册表只\n * 记录、任何清单都不展示。\n * 缺省 'addable'(历史 registerSlot 不带本字段即自动可添加)。\n */\n visibility?: 'fixed' | 'addable' | 'hidden'\n}\n\n/**\n * 槽位注册表面(C1:cordis 服务跨 bundle 单例载体)。\n *\n * 壳(corum-ide-ui)把实现本接口的实例经 `ctx.reflect.provide('slotRegistry')`\n * 注册为 cordis 服务——实例唯一性由 root context `reflect.store` 保证,与各\n * bundle 的模块实例无关(实证 `.dbg/cordis-singleton-probe.md`),故任何插件\n * 在自己 bundle 里调 registerSlot(),壳的 bundle 立即可见。\n *\n * 本文件保持 cordis-free(纯库纪律):接口为结构类型,无任何 cordis import。\n */\nexport interface SlotRegistryFace {\n register<T extends string = string>(key: T, meta: SlotMeta): void\n getMeta(key: string): SlotMeta | undefined\n getAll(): string[]\n}\n\n/**\n * 注册表后端解析(C1 关键设计——模块顶层时序解耦)。\n *\n * 背景:cordis 插件的**模块顶层**在「加载依赖图」阶段执行(此时壳的 apply\n * 尚未跑、服务未 provide),**apply** 才在「激活」阶段执行。ide-layout.ts\n * 的内建槽注册在模块顶层,若用「模块级 backend 变量 + bind 后直读」会把\n * 顶层注册永久锁进 fallback Map(bind 时 backend 还是 null,之后不再重判)。\n *\n * 解法:后端解析推迟到**每次调用时**——\n * 1. 首选 window.__corumSlotRegistry(壳 apply 提供的一次性桥)。\n * **这是合法的 window 挂载**(written once, read-only,规范 §1 例外,与\n * window.corumDesktop / __DSH_BOOT__ 同类),**不是红线 1 禁止的「可变\n * 共享状态挂 window」**——本文件是 cordis-free 纯库(零 cordis import,\n * 被 tsdown 内联进每个 bundle),无法 inject/ctx.get,window 桥是它拿到\n * 壳 `ctx.slotRegistry` 服务实例的**唯一通道**。删除它会让 registerSlot\n * 锁进各 bundle 自己的 fallback Map,slotRegistry 跨 bundle 单例立刻退化\n * 为按 bundle 拆分(回到 C1 之前)。2026-09 统一事件中心二期复核:保留。\n * 2. 桥未挂(无壳组合 / 壳 apply 前的顶层调用)落模块级 fallback Map;\n * 3. 壳 apply 在 provide 后调用 drainPendingSlots() 把 fallback 里的早期\n * 注册合并进服务,保证「顶层早于 apply」的注册最终汇聚到同一实例。\n */\nconst REGISTRY_BRIDGE_KEY = '__corumSlotRegistry'\n\nfunction resolveBackend(): SlotRegistryFace | null {\n if (typeof window === 'undefined') return null\n return (window as unknown as Record<string, SlotRegistryFace | undefined>)[REGISTRY_BRIDGE_KEY] ?? null\n}\n\n/** 模块级 fallback 注册表(服务桥未挂时的暂存 + 无壳组合的永久载体)。 */\nconst slotRegistry = new Map<string, SlotMeta>()\n\n/**\n * 把 fallback 里的早期注册合并进服务(壳 apply 提供桥后调一次)。\n * 合并后清空 fallback,之后所有读写直达服务。\n */\nexport function drainPendingSlots(face: SlotRegistryFace): void {\n for (const [key, meta] of slotRegistry) face.register(key, meta)\n slotRegistry.clear()\n}\n\n/** 注册一个槽位(重复注册覆盖旧元数据)。泛型 T 让子壳把注册 key 收窄进\n * 自己的字面量槽域(拼错即编译错,B2);缺省 string 与历史一致。\n * 写路径每次调用时解析:桥已挂直达服务(跨 bundle 单例);未挂暂存\n * fallback(壳 apply 时 drain 合并)。 */\nexport function registerSlot<T extends string = string>(key: T, meta: SlotMeta): void {\n const backend = resolveBackend()\n if (backend !== null) { backend.register(key, meta); return }\n slotRegistry.set(key, meta)\n}\n\n/** 查询某槽位的元数据。 */\nexport function getSlotMeta(key: string): SlotMeta | undefined {\n const backend = resolveBackend()\n if (backend !== null) return backend.getMeta(key)\n return slotRegistry.get(key)\n}\n\n/** 该槽位是否被钉住(位置/归属固定,不参与 drop 自由组合)。 */\nexport function isPinnedSlot(key: string): boolean {\n return getSlotMeta(key)?.pinned === true\n}\n\n/**\n * 折叠态的空集缺省(调用方不传 collapsed 时用)。\n *\n * P2-2(2026-09-09,.dbg/event-bus-audit-2026-09.md):原实现在本模块持有一个\n * 模块级可变 `collapsedSlots` Set(`setSlotCollapsed` 写 / `leafMinSize` 读)。\n * ui-base 是**被各 bundle 内联的纯库**——同一份源码在每个 bundle 各有一份模块\n * 实例,Set 因此按 bundle 分裂(与 `__corumSidebarMode` 同型风险,当时靠\n * 「写读同 bundle」幸存)。现改为**显式参数**:折叠态由壳(AppFrame 的\n * sidebarCollapsed)持有并传入 grid 数学与 GridView,本模块不再有任何可变状态。\n */\nconst EMPTY_COLLAPSED: ReadonlySet<string> = new Set()\n\n/** 列出所有已注册的槽位 key(有序)。 */\nexport function getAllRegisteredSlots(): string[] {\n const backend = resolveBackend()\n if (backend !== null) return backend.getAll()\n return [...slotRegistry.keys()]\n}\n\n/** 未声明 minWidth 的槽位在 row 分支里的最小宽度兜底(px)——保证基本浏览与交互。 */\nexport const SLOT_FALLBACK_MIN_WIDTH = 200\n\n/** 未声明 minHeight 的槽位在 column 分支里的最小高度兜底(px)——与宽度兜底同为 200 正方形。 */\nexport const SLOT_FALLBACK_MIN_HEIGHT = 200\n\n/** 叶子在指定轴上的最小尺寸:折叠态取 collapsedWidth;否则 SlotMeta 声明优先,缺省走兜底。 */\nfunction leafMinSize(slot: GridSlot, isRow: boolean, collapsed: ReadonlySet<string>): number {\n const meta = getSlotMeta(slot)\n // 折叠态(仅 row 轴的宽度方向生效——折叠收的是宽):min 取折叠宽,\n // 窗口自适应 rescaleGrid 不会把它拉回展开宽。\n if (isRow && collapsed.has(slot) && meta?.collapsedWidth !== undefined) {\n return meta.collapsedWidth\n }\n const declared = isRow ? meta?.minWidth : meta?.minHeight\n return declared ?? (isRow ? SLOT_FALLBACK_MIN_WIDTH : SLOT_FALLBACK_MIN_HEIGHT)\n}\n\n/**\n * 子树在指定轴上的最小尺寸(sash 拖拽 / 窗口自适应 / 渲染夹取三处统一取值):\n * - 同轴分支(row 分支求宽度 / column 分支求高度)= 各子最小之和(并排需同时满足);\n * - 正交分支(row 分支求高度 / column 分支求宽度)= 各子最小的最大值\n * (叠放共用同一主轴宽度,取最宽需求)。\n * 整体隐藏的子树(nodeAllHidden:leaf hidden,或 branch 所有后代 leaf 全 hidden)\n * min=0——它已不占空间,若仍计入 min 会让兄弟格被「隐形 min」顶住、无法在\n * computeCellSizes 里填满(2026-08-28 走查:隐藏编辑器+资源管理器后终端不铺满,\n * 除 detached 误判外,subtreeMinSize 把全隐藏 top-row 的 min 633 也算进 minTotal)。\n * 部分隐藏的 branch 仍按可见子树计 min(可见部分需要下限);单 leaf hidden 同理\n * 取 0。\n */\nexport function subtreeMinSize(node: GridNode, isRow: boolean, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): number {\n if (node.type === 'leaf') return node.hidden === true ? 0 : leafMinSize(node.slot, isRow, collapsed)\n const sizes = node.children.map(c => subtreeMinSize(c, isRow, collapsed))\n return node.direction === (isRow ? 'row' : 'column')\n ? sizes.reduce((a, b) => a + b, 0)\n : sizes.reduce((a, b) => Math.max(a, b), 0)\n}\n\nexport interface LeafNode {\n type: 'leaf'\n id: string\n slot: GridSlot\n /** 用户关闭(不显示):保留在树里(可恢复),渲染时折叠为 0 宽、相邻填满。 */\n hidden?: boolean\n}\n\nexport interface BranchNode {\n type: 'branch'\n id: string\n /** row = 水平排列(左右分,sash 垂直);column = 垂直排列(上下分,sash 水平)。 */\n direction: 'row' | 'column'\n children: GridNode[]\n /** 与 children 等长的相对份额(渲染换算;拖拽 sash 改它)。 */\n weights: number[]\n}\n\nexport type GridNode = LeafNode | BranchNode\n\nlet idSeq = 0\nconst nid = (p: string) => `${p}-${++idSeq}-${Math.random().toString(36).slice(2, 7)}`\n\n// ── 构造 helper(帮子壳省 nid 细节,直接写字面量布局树)────────────────\n\n/** 构造一个叶子节点。 */\nexport function leafNode(slot: GridSlot): LeafNode {\n return { type: 'leaf', id: nid('l'), slot }\n}\n\n/** 构造一个水平分支(左右排列)。weights 缺省时每格 400。 */\nexport function rowBranch(children: GridNode[], weights?: number[]): BranchNode {\n return { type: 'branch', id: nid('b'), direction: 'row', children, weights: weights ?? children.map(() => 400) }\n}\n\n/** 构造一个垂直分支(上下排列)。weights 缺省时每格 400。 */\nexport function columnBranch(children: GridNode[], weights?: number[]): BranchNode {\n return { type: 'branch', id: nid('b'), direction: 'column', children, weights: weights ?? children.map(() => 400) }\n}\n\n/** 深拷贝(操作用纯函数,返回新树)。 */\nexport function cloneNode(node: GridNode): GridNode {\n if (node.type === 'leaf') return { ...node }\n return { ...node, children: node.children.map(cloneNode), weights: [...node.weights] }\n}\n\n/** 按 id 找节点 + 其父分支 + 在父中的下标。 */\nexport function findNode(root: GridNode, id: string): { node: GridNode; parent: BranchNode | null; index: number } | null {\n if (root.id === id) return { node: root, parent: null, index: -1 }\n if (root.type === 'branch') {\n for (let i = 0; i < root.children.length; i++) {\n const found = findNode(root.children[i], id)\n if (found) {\n return found.parent === null && found.node.id === root.children[i].id\n ? { node: found.node, parent: root, index: i }\n : found\n }\n }\n }\n return null\n}\n\n/** 找到某 leaf 的第一个匹配(按 slot)。 */\nexport function findLeafBySlot(root: GridNode, slot: GridSlot): LeafNode | null {\n if (root.type === 'leaf') return root.slot === slot ? root : null\n for (const child of root.children) {\n const found = findLeafBySlot(child, slot)\n if (found) return found\n }\n return null\n}\n\nexport type DropZone = 'left' | 'right' | 'top' | 'bottom' | 'center'\n\n/**\n * 把 `sourceId` 的 leaf 放到 `targetId` 的 leaf 的某个 zone:\n * - 四边:在该方向 split —— 若目标父分支方向与该边一致则直接插入相邻位,\n * 否则把目标 leaf 包成一个新分支(两个子节点),source 插入对应侧。\n * - center:交换两格的 slot。\n * 源格从原位置摘除;剪枝后返回新树。source === target 时 no-op。\n */\nexport function dropLeaf(root: GridNode, sourceId: string, targetId: string, zone: DropZone): GridNode {\n if (sourceId === targetId) return root\n const tree = cloneNode(root)\n const src = findNode(tree, sourceId)\n const tgt = findNode(tree, targetId)\n if (!src || !tgt || src.node.type !== 'leaf' || tgt.node.type !== 'leaf') return root\n\n const srcLeaf = src.node as LeafNode\n const tgtLeaf = tgt.node as LeafNode\n\n // 钉住的 leaf 不参与自由组合:source 是 pinned(不可被拖走)或 target 是\n // pinned(不可被拖入 split/swap)都 no-op。\n if (isPinnedSlot(srcLeaf.slot) || isPinnedSlot(tgtLeaf.slot)) return root\n\n if (zone === 'center') {\n // 交换两格内容。\n const tmp = srcLeaf.slot\n srcLeaf.slot = tgtLeaf.slot\n tgtLeaf.slot = tmp\n return tree\n }\n\n // 先从原位置摘除 source。\n removeLeaf(tree, sourceId)\n // 摘除后 tgt 的父/下标可能变化,重新定位。\n const tgtAfter = findNode(tree, targetId)\n if (!tgtAfter) return tree\n // 根分支被 prune 塌陷成单 leaf(原根只有两格、摘掉一格后剩一格)时\n // tgtAfter.parent === null —— 不能放弃(source 已被摘除,放弃就丢窗格),\n // 把根包成新分支,按 zone 排两个子节点。\n if (tgtAfter.parent === null) {\n const wantDir: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertFirst = (zone === 'left' || zone === 'top')\n const remaining = tree // 此时 tree 已是塌陷后的单 leaf(即 tgt)\n return {\n type: 'branch',\n id: nid('b'),\n direction: wantDir,\n children: insertFirst ? [srcLeaf, remaining] : [remaining, srcLeaf],\n weights: [1, 1], // 包成新根分支:两格均分(render 端按容器 flexBasis,均分即各半)\n }\n }\n const parent = tgtAfter.parent\n const index = tgtAfter.index\n\n const wantDirection: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertBefore = (zone === 'left' || zone === 'top')\n\n if (parent.direction === wantDirection) {\n // 父分支同向:直接插入目标相邻位。\n const at = insertBefore ? index : index + 1\n parent.children.splice(at, 0, srcLeaf)\n // 目标原份额拆分:source 拿目标份额的一半。\n const targetWeight = parent.weights[index] ?? 1\n const srcWeight = targetWeight / 2\n parent.weights[index] = targetWeight / 2\n parent.weights.splice(at, 0, srcWeight)\n } else {\n // 父分支反向:把目标 leaf 包成一个新分支(两个子节点)。weights 是像素\n // (flexBasis)——两格按目标格原份额各半,不能写死 [1,1](那会塌陷成\n // 1px 直到下一次窗口 resize 才修复)。\n const targetWeight = parent.weights[index] ?? 1\n const half = targetWeight / 2\n const wrapper: BranchNode = {\n type: 'branch',\n id: nid('b'),\n direction: wantDirection,\n children: insertBefore ? [srcLeaf, tgtAfter.node] : [tgtAfter.node, srcLeaf],\n weights: [half, half],\n }\n parent.children[index] = wrapper\n }\n return tree\n}\n\n/** 按 slot 设置某 leaf 的 hidden(关闭/恢复显示)。树保留该 leaf,只翻标记。 */\nexport function setLeafHidden(root: GridNode, slot: GridSlot, hidden: boolean): GridNode {\n const tree = cloneNode(root)\n const leaf = findLeafBySlot(tree, slot)\n if (leaf !== null) leaf.hidden = hidden\n return tree\n}\n\n/**\n * 该节点是否「整体隐藏」——leaf 看自身 hidden;branch 看其**所有后代 leaf 是否\n * 全部 hidden**(全隐藏则整支不占空间,兄弟格填满)。GridView 的 BranchView 用它\n * 判定各格 detached:否则外层分支的直接子是嵌套 branch(如 right-col 里的\n * row(editor,explorer))时,其内部 leaf 全 hidden 也判不出,导致该支仍按 weight\n * 占位、兄弟格(终端/对话区)不铺满(2026-08-28 走查两问题根因)。\n * detachedSlots(运行时脱出)只作用于 leaf,branch 不判——脱出是临时态。\n */\nexport function nodeAllHidden(node: GridNode): boolean {\n if (node.type === 'leaf') return node.hidden === true\n return node.children.every(nodeAllHidden)\n}\n\n/** 列出当前关闭(hidden)的槽位。 */\nexport function hiddenSlots(root: GridNode): GridSlot[] {\n const out: GridSlot[] = []\n const walk = (n: GridNode): void => {\n if (n.type === 'leaf') { if (n.hidden === true) out.push(n.slot); return }\n n.children.forEach(walk)\n }\n walk(root)\n return out\n}\n\n/** 列出当前在网格中的所有槽位(含 hidden 的)。 */\nexport function slotsInGrid(root: GridNode): Set<GridSlot> {\n const out = new Set<GridSlot>()\n const walk = (n: GridNode): void => {\n if (n.type === 'leaf') { out.add(n.slot); return }\n n.children.forEach(walk)\n }\n walk(root)\n return out\n}\n\n/** 列出当前不在网格中的槽位(可添加的)。 */\nexport function slotsNotInGrid(root: GridNode): GridSlot[] {\n const inGrid = slotsInGrid(root)\n return getAllRegisteredSlots().filter((s) => !inGrid.has(s))\n}\n\n/**\n * 把一个槽位作为新 leaf 添加到网格末尾(根分支右侧)。\n * 如果根不是分支则包一层。返回新树。\n */\nexport function addSlot(root: GridNode, slot: GridSlot): GridNode {\n const leaf: LeafNode = { type: 'leaf', id: nid('l'), slot }\n const weight = getSlotMeta(slot)?.defaultWeight ?? 400\n if (root.type === 'leaf') {\n return { type: 'branch', id: nid('b'), direction: 'row', children: [root, leaf], weights: [800, weight] }\n }\n const tree = cloneNode(root)\n if (tree.type !== 'branch') return root\n tree.children.push(leaf)\n tree.weights.push(weight)\n return tree\n}\n\n/**\n * 把一个新槽位 drop 到网格中已有 leaf 的某侧(split 或包壳)。\n * 与 dropLeaf 类似,但 source 是新创建的 leaf(不从树里摘除)。\n */\nexport function addSlotAt(root: GridNode, slot: GridSlot, targetId: string, zone: DropZone): GridNode {\n if (zone === 'center') return root // 新槽位不支持 swap\n const tree = cloneNode(root)\n const tgt = findNode(tree, targetId)\n if (!tgt || tgt.node.type !== 'leaf') return root\n\n const newLeaf: LeafNode = { type: 'leaf', id: nid('l'), slot }\n const wantDirection: BranchNode['direction'] = (zone === 'left' || zone === 'right') ? 'row' : 'column'\n const insertBefore = (zone === 'left' || zone === 'top')\n\n // 钉住的 leaf 不可被拖入 split(新槽位落到它四边会改变它的位置/归属)。\n if (isPinnedSlot(tgt.node.slot)) return root\n\n if (tgt.parent === null) {\n // 目标是根 leaf —— 包成新分支。新叶子按 slot 默认份额,已有叶保留\n // 合理份额(与包壳处「目标份额各半」同思路,基准取 defaultWeight)。\n const newWeight = getSlotMeta(slot)?.defaultWeight ?? 400\n return {\n type: 'branch', id: nid('b'), direction: wantDirection,\n children: insertBefore ? [newLeaf, tree] : [tree, newLeaf],\n weights: insertBefore ? [newWeight, 400] : [400, newWeight],\n }\n }\n\n const parent = tgt.parent\n const index = tgt.index\n\n if (parent.direction === wantDirection) {\n const at = insertBefore ? index : index + 1\n const targetWeight = parent.weights[index] ?? 400\n const half = targetWeight / 2\n parent.weights[index] = half\n parent.children.splice(at, 0, newLeaf)\n parent.weights.splice(at, 0, half)\n } else {\n const targetWeight = parent.weights[index] ?? 400\n const half = targetWeight / 2\n const wrapper: BranchNode = {\n type: 'branch', id: nid('b'), direction: wantDirection,\n children: insertBefore ? [newLeaf, tgt.node] : [tgt.node, newLeaf],\n weights: [half, half],\n }\n parent.children[index] = wrapper\n }\n return tree\n}\n\n/** 从树里摘除一个 leaf 并剪枝(父分支只剩一个子时提升该子)。 */\nexport function removeLeaf(root: GridNode, id: string): GridNode {\n const found = findNode(root, id)\n if (!found || !found.parent) return root\n // 钉住的 leaf 不可被摘除(防御:drop 拖走的 source 拦截已覆盖,这里兜底)。\n if (found.node.type === 'leaf' && isPinnedSlot(found.node.slot)) return root\n const parent = found.parent\n parent.children.splice(found.index, 1)\n parent.weights.splice(found.index, 1)\n return prune(root)\n}\n\n/** 剪枝:任何只剩一个子的分支被该子替换(自底向上)。 */\nexport function prune(node: GridNode): GridNode {\n if (node.type === 'leaf') return node\n node.children = node.children.map(prune)\n // 移除非法空分支。\n node.children = node.children.filter((c) => c.type === 'leaf' || c.children.length > 0)\n if (node.children.length === 1) return node.children[0]\n // 权重长度对齐 children(防御)。\n if (node.weights.length !== node.children.length) {\n node.weights = node.children.map((_, i) => node.weights[i] ?? 1)\n }\n return node\n}\n\n/**\n * 调整某分支里两个相邻子节点间的 sash(带传导推动)。\n * delta>0 = 左/上侧(sashIndex)变大、推向右/下方;delta<0 = 右/下侧变大、\n * 推向左/上方。份额总量守恒,只在被推动的一侧内部转移:\n * - 先与紧贴 sash 的邻格互相消长(邻格最多让到自己的 min);\n * - 邻格顶到 min 后仍有未消化的 delta 时,**沿推动方向向同分支后续格传导**:\n * 下一格让出自己的富余(当前值 − 自身 min),逐格传递,直到 delta 耗尽或\n * 该方向没有任何余量(2026-08-27 用户定调:隔壁到 min 后推动该方向仍有\n * 余量的区域,直到无余量为止)。传导只在同分支内进行,不跨嵌套下钻。\n * 被拉动侧自身也夹到自己的 min(不会为给对侧让路而压过 min)。\n * 各格最小份额 = 各侧子树的 subtreeMinSize(SlotMeta 声明 + 兜底);minWeight\n * 显式传入时覆盖紧贴 sash 的两侧(保留给特殊调方)。\n */\nexport function resizeBranch(root: GridNode, branchId: string, sashIndex: number, delta: number, minWeight?: number, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): GridNode {\n if (delta === 0) return root\n const tree = cloneNode(root)\n const found = findNode(tree, branchId)\n if (!found || found.node.type !== 'branch') return root\n const branch = found.node\n const i = sashIndex\n if (i < 0 || i >= branch.weights.length - 1) return root\n const isRow = branch.direction === 'row'\n const mins = branch.children.map(c => subtreeMinSize(c, isRow, collapsed))\n if (minWeight !== undefined) { mins[i] = minWeight; mins[i + 1] = minWeight }\n\n if (delta > 0) {\n // 左侧 i 要增大:从右侧 i+1, i+2, ... 依次取富余(各让到 min 为止)。\n let want = delta\n // i 自身也有上限?无——i 可以无限增大(只受对侧能让出多少限制)。\n for (let j = i + 1; j < branch.weights.length && want > 0; j++) {\n const slack = Math.max(0, branch.weights[j] - mins[j])\n const give = Math.min(slack, want)\n branch.weights[j] -= give\n want -= give\n }\n const gained = delta - want // 实际从右侧凑到的量\n branch.weights[i] += gained\n } else {\n // 右侧 i+1 要增大:从左侧 i, i-1, ... 依次取富余。\n let want = -delta\n for (let j = i; j >= 0 && want > 0; j--) {\n const slack = Math.max(0, branch.weights[j] - mins[j])\n const give = Math.min(slack, want)\n branch.weights[j] -= give\n want -= give\n }\n const gained = -delta - want\n branch.weights[i + 1] += gained\n }\n return tree\n}\n\n/**\n * 窗口尺寸变化时按当前比例重标定所有分支的 weights 到新总宽(自适应)。\n * weights 是像素;窗口缩放时各列等比缩放,不截断、不溢出。row 分支沿宽度\n * 缩放、column 分支沿高度缩放——按方向分别处理。\n */\nexport function rescaleGrid(node: GridNode, width: number, height: number, collapsed: ReadonlySet<string> = EMPTY_COLLAPSED): GridNode {\n const scale = (branch: BranchNode, span: number): void => {\n // 主轴方向各格最小尺寸 = 各子树的 subtreeMinSize(SlotMeta 声明 + 兜底)。\n const isRow = branch.direction === 'row'\n const mins = branch.children.map(c => subtreeMinSize(c, isRow, collapsed))\n const total = branch.weights.reduce((a, b) => a + b, 0)\n if (total <= 0 || span <= 0) return\n // 先按比例分配。\n let ws = branch.weights.map((w) => (w / total) * span)\n // 每格至少自己的 min;但若 Σmin 超过可用空间(窗口太窄),按各格 min 的\n // 比例分配(而非等比平分)——保住各区域声明的最小比,小窗不丢布局、\n // 大窗恢复后仍贴近用户拖的比例,绝不溢出截断。\n const minTotal = mins.reduce((a, b) => a + b, 0)\n if (minTotal >= span) {\n branch.weights = mins.map((m) => (m / minTotal) * span)\n return\n }\n // 正常:夹 min,夹取的差额从仍有富余的格里补给(保持 Σ = span)。\n let deficit = 0\n ws = ws.map((w, i) => {\n if (w < mins[i]) { deficit += mins[i] - w; return mins[i] }\n return w\n })\n if (deficit > 0) {\n const slack = ws.reduce((a, w, i) => a + Math.max(0, w - mins[i]), 0)\n if (slack > deficit) {\n ws = ws.map((w, i) => (w > mins[i] ? w - (Math.max(0, w - mins[i]) / slack) * deficit : w))\n } else {\n // 富余不够补差额(多格同时低于 min):按各格 min 比例分配(与上面\n // minTotal>=span 分支同策略),保证 Σ=span 恒成立、绝不溢出、不丢最小比。\n branch.weights = mins.map((m) => (m / minTotal) * span)\n return\n }\n }\n branch.weights = ws\n }\n const walk = (n: GridNode, w: number, h: number): void => {\n if (n.type === 'leaf') return\n const isRow = n.direction === 'row'\n scale(n, isRow ? w : h)\n // 子分支的正交尺寸 = 父分支该方向尺寸;子分支沿其主轴的尺寸 = 它自己的 weight。\n n.children.forEach((child, i) => {\n const cw = isRow ? n.weights[i] : w\n const ch = isRow ? h : n.weights[i]\n walk(child, cw, ch)\n })\n }\n const tree = cloneNode(node)\n walk(tree, width, height)\n return tree\n}\n\n// ── 持久化 ────────────────────────────────────────────────────────────\n\n/** 默认持久化 key;子壳应传自己的 key(如 ide-shell 用 'corum.ide.grid.v3')。 */\nexport const DEFAULT_GRID_STORAGE_KEY = 'corum.grid.v1'\n\n/** 序列化树(weights 保留两位小数)。 */\nexport function serializeGrid(node: GridNode): string {\n const strip = (n: GridNode): unknown => n.type === 'leaf'\n ? (n.hidden === true ? { t: 'l', slot: n.slot, h: 1 } : { t: 'l', slot: n.slot })\n : { t: 'b', d: n.direction, c: n.children.map(strip), w: n.weights.map((w) => Math.round(w * 100) / 100) }\n return JSON.stringify(strip(node))\n}\n\n/** 反序列化(重新发 id,结构非法时返回 null,由调用方回退到自己的默认布局)。\n * `topLevelRow` 可选:传子壳默认布局的顶层 row 拓扑签名(叶子=槽名、\n * 分支='*')——持久化的顶层 row 与默认签名不同(如旧版把 conversation\n * 嵌进了 right-col 内部)时**整树废弃回退默认**,不沿用旧拓扑。否则旧\n * 布局(对话区被裹进 right-col 分支)会被 root leafTopOffset 按「非叶子\n * 格 offset=0」整支抬顶,撞破只压左列的标题栏浮层(2026-09-04:编辑器/\n * 资源管理器没有顶在窗口最上面、反而压在会话区标题栏下方的根因)。 */\nexport function deserializeGrid(json: string, topLevelRow?: readonly string[]): GridNode | null {\n try {\n const build = (raw: unknown): GridNode | null => {\n if (typeof raw !== 'object' || raw === null) return null\n const r = raw as Record<string, unknown>\n if (r.t === 'l' && typeof r.slot === 'string') {\n // 槽位已不在注册表(插件被移除等)——丢弃该叶子,避免永久显示\n // 「此区域暂无内容」且无法从「添加区域」清除。\n if (getSlotMeta(r.slot) === undefined) return null\n return { type: 'leaf', id: nid('l'), slot: r.slot as GridSlot, ...(r.h === 1 ? { hidden: true } : {}) }\n }\n if (r.t === 'b' && (r.d === 'row' || r.d === 'column') && Array.isArray(r.c) && Array.isArray(r.w)) {\n const children = r.c.map(build).filter((c): c is GridNode => c !== null)\n if (children.length === 0) return null\n return { type: 'branch', id: nid('b'), direction: r.d, children, weights: r.w.map((w) => (typeof w === 'number' ? w : 1)) }\n }\n return null\n }\n const tree = build(JSON.parse(json))\n if (tree === null) return null\n // 顶层 row 拓扑守卫:与默认布局的顶层槽序不一致即废弃(见上注释)。\n if (topLevelRow !== undefined) {\n if (tree.type !== 'branch' || tree.direction !== 'row') return null\n // 顶层格的拓扑签名:叶子=槽名、分支='*'(分支位置可变——right-col 等\n // 分支格允许存在,但叶子槽序必须与默认一致;旧布局把叶子嵌进分支\n // 内部时槽序对不上即废弃)。分支格内的结构(嵌套/权重)不校验——\n // 用户拖拽重组自由,只保证顶层叶子仍直接挂在 root row 下。\n const sig = tree.children.map((c) => (c.type === 'leaf' ? c.slot : '*'))\n if (sig.length !== topLevelRow.length || sig.some((s, i) => s !== topLevelRow[i])) return null\n }\n // 对齐 weights/children 长度并剪枝(含 slot 校验丢弃叶子后的塌陷)。\n const pruned = prune(tree)\n // 整树被剪空(如只剩一个未注册 slot 的叶子)时回退默认布局。\n if (pruned.type === 'branch' && pruned.children.length === 0) return null\n return pruned\n } catch {\n return null\n }\n}\n\n/**\n * 读持久化布局;无或非法时回退 `fallback()`(子壳的默认布局)。\n * key 缺省 DEFAULT_GRID_STORAGE_KEY;子壳传自己的 key 以隔离/兼容存量布局。\n */\nexport function loadGrid(fallback: () => GridNode, key: string = DEFAULT_GRID_STORAGE_KEY): GridNode {\n if (typeof localStorage === 'undefined') return fallback()\n const raw = localStorage.getItem(key)\n if (raw === null) return fallback()\n // 从子壳默认布局提取顶层 row 的拓扑签名(叶子=槽名、分支='*')作为守卫\n // 基准——默认布局本身是子壳特化(如 ide-layout 的 [sidebar, conversation,\n // right-col(分支)]),本包不硬编码业务槽名。默认布局顶层不是 row 时不施加\n // 守卫。\n const def = fallback()\n const topLevelRow = def.type === 'branch' && def.direction === 'row'\n ? def.children.map((c) => (c.type === 'leaf' ? c.slot : '*'))\n : undefined\n return deserializeGrid(raw, topLevelRow) ?? fallback()\n}\n\n/** 写持久化布局。key 缺省 DEFAULT_GRID_STORAGE_KEY。 */\nexport function saveGrid(node: GridNode, key: string = DEFAULT_GRID_STORAGE_KEY): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(key, serializeGrid(node))\n } catch { /* quota/private mode — non-fatal */ }\n}\n\n/** 清空持久化(恢复子壳默认布局)。key 缺省 DEFAULT_GRID_STORAGE_KEY。 */\nexport function resetGridStorage(key: string = DEFAULT_GRID_STORAGE_KEY): void {\n if (typeof localStorage === 'undefined') return\n localStorage.removeItem(key)\n}\n","/* RegionCard —— 标准区域卡片基座。背景/描边走 token 兜底:\n 未定义 --corum-glass-*(子壳主题变量)时下落到官方 --dsw-alias-*。 */\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n background: var(--corum-glass-1, var(--dsw-alias-bg-layer-1));\n border: 1px solid var(--corum-glass-border, var(--dsw-alias-border-l1));\n border-radius: 18px;\n}\n\n/* 透明变体:子壳内容子卡独立、间隙透出背景时启用——基座只留定位/裁剪,\n 不垫背景。 */\n.cardTransparent {\n background: transparent;\n border-color: transparent;\n}\n\n.body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.body > * {\n flex: 1;\n min-height: 0;\n}\n","/**\n * RegionCard —— 区域的标准卡片基座。\n *\n * 所有区域(子壳自行定义的槽位)都通过这个基座渲染,保证统一的卡片外观\n * 与区域级操作:\n * - 卡片背景/描边走 token 兜底(--corum-glass-1 → --dsw-alias-bg-layer-1),\n * shell-base 的极简主题下落到官方 token;子壳(如 ide-shell)可用自己的\n * 主题变量覆盖成玻璃质感\n * - 内容区非交互处按下并拖出窗口 → 触发拖出(组件交互/文字选择不受影响)\n */\nimport { forwardRef, useCallback } from 'react'\nimport type { ReactNode } from 'react'\nimport css from './RegionCard.module.css'\n\n/** RegionCard 的 props。 */\nexport interface RegionCardProps {\n /** 区域内容(slot 渲染结果)。 */\n children: ReactNode\n /** 拖出为浮动窗(区域内非交互处长按左键拖拽出窗口时触发;缺省禁用拖出)。 */\n onPopOut?: (() => void) | undefined\n /** 透传给卡片根的 data-slot(用于 grid 定位/调试)。 */\n slotKey?: string | undefined\n /** 内容区附加 className(区域自定义内容布局)。 */\n bodyClassName?: string | undefined\n /** 透明变体:不画整卡背景/描边(子壳内容子卡独立、间隙透出背景时启用)。 */\n transparent?: boolean | undefined\n}\n\n/**\n * 交互元素判定清单(拖拽让位):这些内容上的按下/拖拽交给组件自身交互或\n * 文字选择,不触发区域拖拽/网格重排。GridView 的 dragstart 让位与本文件的\n * mousedown 让位共用此常量,避免两处清单漂移。\n *\n * 覆盖(2026-08-30 补全):原生交互控件(button/a/input/textarea/select/img/\n * summary/label/audio/video)+ 内容编辑(contenteditable)+ Monaco\n * ([data-monaco-editor] 宿主 + .monaco-editor 内核类——宿主可能因空态未挂载,\n * 内核类兜底已挂载场景)+ 常见 ARIA 交互角色(button/textbox/link/checkbox/\n * radio/slider/spinbutton/combobox/listbox/option/menuitem 系/tab/switch/\n * searchbox/treeitem/scrollbar)+ 显式标记 data-interactive。\n * 故意不含 [draggable=\"true\"]——GridView 整叶根本身 draggable,含它会让每次\n * dragstart 命中整叶根被 preventDefault、整叶永远拖不动(见 GridView 注释)。\n */\nexport const INTERACTIVE_SELECTOR = [\n 'button',\n 'a',\n 'input',\n 'textarea',\n 'select',\n 'img',\n 'summary',\n 'label',\n 'audio',\n 'video',\n '[contenteditable=\"true\"]',\n '[data-monaco-editor]',\n '.monaco-editor',\n '[data-interactive]',\n '[role=\"button\"]',\n '[role=\"textbox\"]',\n '[role=\"link\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"slider\"]',\n '[role=\"spinbutton\"]',\n '[role=\"combobox\"]',\n '[role=\"listbox\"]',\n '[role=\"option\"]',\n '[role=\"menuitem\"]',\n '[role=\"menuitemcheckbox\"]',\n '[role=\"menuitemradio\"]',\n '[role=\"tab\"]',\n '[role=\"switch\"]',\n '[role=\"searchbox\"]',\n '[role=\"treeitem\"]',\n '[role=\"scrollbar\"]',\n].join(', ')\n\n/**\n * 卡片容器清单(2026-08-30 用户定调:卡片整体不可拖——卡片上按下拖动 = 选中\n * 文字或无反应,不触发区域拖拽/网格重排)。命中:消息气泡 / 工具聚合卡 /\n * 审批卡 / Review 卡 / 子 Agent 卡 / 会话行 / 文件树行 / 项目行等。CSS\n * Modules 哈希前缀稳定(如 _9Q52kG_sr / LRfKSG_bubble),用语义子串的\n * 属性前缀选择器匹配([class*=\"_sr\"] 等)。GridView 的 dragstart 让位与\n * 本文件的 mousedown 让位共用。\n */\nexport const CARD_SELECTOR = [\n // 会话区消息卡(corum-ui-chat / corum-ui-approval 各卡片根)。\n '[class*=\"_bubble\"]',\n '[class*=\"_reviewCard\"]',\n '[class*=\"_approvalCard\"]',\n '[class*=\"_subagentCard\"]',\n '[class*=\"_turnProcess\"]',\n '[class*=\"_toolCard\"]',\n // AI 消息体(fork corum-ui-chat 的 assistant 消息根 qsr5ja_* + markdown 正文\n // _markdown_* / _plain_* / _plainRun_*)——AI 结论文本块整体不可拖(选中\n // 文字)。这是「秋日五绝结论仍是拖动区域」的根因:旧清单只认 _bubble/_card,\n // fork 的 ai 消息用 qsr5ja_/markdown 命名,整条命中链无一命中。\n '[class*=\"qsr5ja_\"]',\n '[class*=\"_markdown_\"]',\n '[class*=\"_plain_\"]',\n '[class*=\"_plainRun_\"]',\n // 消息流条目容器(_Yx6sq_flowItem = ChatView 的每条消息包装)。\n '[class*=\"_flowItem\"]',\n // 侧栏会话行 / 组行 / 历史行 / 团队行 / 管理行。\n '[class*=\"_sr\"]',\n '[class*=\"_groupRow\"]',\n '[class*=\"_historyRow\"]',\n '[class*=\"_teamSr\"]',\n '[class*=\"_manageRow\"]',\n // 文件树行 / 项目行。\n '[class*=\"_treeRow\"]',\n '[class*=\"_projectRow\"]',\n].join(', ')\n\n/** 交互元素判定:这些内容上的按下不触发区域拖拽(交给组件自身交互/文字选择)。 */\nfunction isInteractiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n return target.closest(INTERACTIVE_SELECTOR) !== null\n}\n\n/** 卡片容器判定:卡片上的按下不触发区域拖拽(用户定调:卡片整体不可拖)。 */\nexport function isCardTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n return target.closest(CARD_SELECTOR) !== null\n}\n\n/**\n * 标准区域卡片基座。forwardRef 暴露卡片根(GridView 需要 ref 测量 drop zone)。\n */\nexport const RegionCard = forwardRef<HTMLDivElement, RegionCardProps>(function RegionCard(props, ref) {\n const { children, onPopOut, slotKey, bodyClassName, transparent } = props\n\n // 非交互处按下并拖拽 → 脱出为浮动窗。拖出窗口可视区(坐标越界)触发 onPopOut;\n // 窗口内释放 / 位移太小(当作文字选择或偶然拖动)则取消,不移动内容。\n const onBodyMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (e.button !== 0 || !onPopOut) return\n if (isInteractiveTarget(e.target) || isCardTarget(e.target)) return\n const selection = window.getSelection()\n if (selection !== null && !selection.isCollapsed) return // 用户在选文字,让位\n const startX = e.clientX\n const startY = e.clientY\n let armed = false // 是否已超过拖拽阈值(避免点击/选文字被误判成拖拽)\n const DRAG_THRESHOLD = 6\n const style = document.createElement('style')\n const arm = () => {\n if (armed) return\n armed = true\n style.textContent = `* { cursor: grabbing !important; user-select: none !important; -webkit-user-select: none !important; }`\n document.head.appendChild(style)\n }\n const onMove = (ev: MouseEvent) => {\n if (!armed && Math.hypot(ev.clientX - startX, ev.clientY - startY) < DRAG_THRESHOLD) return\n arm()\n ev.preventDefault()\n }\n const onUp = (ev: MouseEvent) => {\n window.removeEventListener('mousemove', onMove, true)\n window.removeEventListener('mouseup', onUp, true)\n style.remove()\n if (!armed) return // 未达阈值:当作普通点击/选择,不脱出\n const outX = ev.clientX <= 0 || ev.clientX >= window.innerWidth\n const outY = ev.clientY <= 0 || ev.clientY >= window.innerHeight\n if (outX || outY) onPopOut()\n }\n window.addEventListener('mousemove', onMove, true)\n window.addEventListener('mouseup', onUp, true)\n }, [onPopOut])\n\n return (\n <div ref={ref} className={`${css.card}${transparent ? ` ${css.cardTransparent}` : ''}`} data-slot={slotKey}>\n {/* 内容区:非交互处按下并拖出窗口 → 拖出为浮动窗。 */}\n <div className={bodyClassName ? `${css.body} ${bodyClassName}` : css.body} onMouseDown={onBodyMouseDown}>\n {children}\n </div>\n </div>\n )\n})\n","/* GridView —— 自由二维网格。token-only。 */\n\n.grid {\n position: relative;\n height: 100%;\n width: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n}\n\n/* VSCode SplitView 式绝对定位:分支容器 relative,每格 absolute,位置/尺寸\n 由 JS 计算并直接写 DOM style(left/top/width/height)。不再用 flex。 */\n.branchRow,\n.branchColumn {\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n}\n\n.branchCell {\n position: absolute;\n /* 最小宽度约束由 JS 布局夹取(computeCellSizes 的 MIN_MAIN_SIZE),CSS 不再\n 用 min-width——绝对定位下 min-width 会与 JS 写入的 width 冲突造成溢出。 */\n min-height: 0;\n min-width: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* 顶层 row 格的内容让位包装(leafTopOffset):JS 写入 top/height(内容格\n 顶部下移、高度缩短——如 sidebar/conversation 让位标题栏行,right-col 顶到\n 容器顶)。格自身仍绝对定位占满全高,包装在内层绝对定位。\n 注意不设 overflow:hidden——浮动窗拖回的 dock 预览(dropHint/dockPreview)\n 需要能透出到格边界外(overflow 在 .leaf 层裁剪)。 */\n.branchInner {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n min-height: 0;\n min-width: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* ── sash(沿用 VSCode Sash 视觉)── */\n.sashV {\n position: absolute; /* JS 写 left/top/height;命中区 8px 宽压在缝上 */\n width: 8px;\n cursor: col-resize;\n z-index: 35;\n touch-action: none;\n}\n\n.sashH {\n position: absolute; /* JS 写 left/top/width */\n height: 8px;\n cursor: row-resize;\n z-index: 35;\n touch-action: none;\n}\n\n.sashV::after,\n.sashH::after {\n content: '';\n position: absolute;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0;\n transition: opacity 120ms ease;\n border-radius: 2px;\n}\n\n.sashV::after {\n top: 0; bottom: 0; left: 50%; width: 3px; margin-left: -1.5px;\n}\n\n.sashH::after {\n left: 0; right: 0; top: 50%; height: 3px; margin-top: -1.5px;\n}\n\n.sashV:hover::after,\n.sashH:hover::after {\n opacity: 1;\n}\n\n/* ── 叶子窗格 ──\n .leaf 只是 grid 拖放高亮的定位包装(drop zone / dropHint 的锚),卡片\n 外观与区域操作按钮由 RegionCard 基座承载。\n overflow:hidden 裁剪叶内容(如 Monaco 的 16777214px 虚拟画布),避免\n HTML5 拖拽 ghost 快照把这些巨大画布一并拍进去、覆盖相邻区域。 */\n.leaf {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n /* 创建独立合成层,隔离 leaf 的渲染——否则 HTML5 拖拽的 ghost 快照会把相邻\n leaf 一起拍进去(manifest 为「拖动带动相邻区域」)。\n 注意:本层 + 0.1.2 会话区换肤的 backdrop-filter 玻璃层组合会让 Chromium\n 的整页 native ghost 失效(空态拍进相邻区域 / 非空态无 ghost),故\n GridView 改为 dragstart 克隆 leaf 作显式 drag image(见 .dragGhost)。 */\n will-change: transform;\n /* Chromium 对 draggable=true 元素的默认行为:整棵子树 user-select:none\n (实测:leaf draggable=true → 子树全 none,去掉 draggable 立即回 auto)——\n 这会让「draggable 整叶」里的文本(结论正文/消息/树行)无法选中。显式覆盖\n 回 text:文本恢复可选中;拖拽让位由 GridView 的 CARD_SELECTOR/INTERACTIVE\n 判定(卡片/交互上 preventDefault dragstart)保证「可选中且不拖」并存。 */\n user-select: text;\n}\n\n/* 拖拽时的显式 drag image 快照(GridView dragstart 克隆的 leaf):必须是干净的\n 静态快照——剥离一切 backdrop-filter(玻璃层会让 Chromium 快照失效/半透明穿帮),\n 快照自身不再 blur(拍的是「当前画面」,底层内容在快照生成时已定格)。 */\n[data-drag-ghost],\n[data-drag-ghost] * {\n backdrop-filter: none !important;\n}\n\n/* 已脱出占位卡。 */\n.detachedPlaceholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(6px * var(--corum-density-scale, 1));\n height: 100%;\n color: var(--dsw-alias-label-tertiary);\n font-size: calc(12px * var(--corum-ui-font-scale, 1));\n}\n\n.detachedIcon {\n font-size: calc(20px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-brand-primary);\n}\n\n.detachedHint {\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* drop 高亮:四边或中心。 */\n.dropHint {\n position: absolute;\n inset: 0;\n pointer-events: none;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0.16;\n z-index: 5;\n}\n\n.dropHint[data-zone='left'] { right: 50%; border-radius: 14px 0 0 14px; }\n.dropHint[data-zone='right'] { left: 50%; border-radius: 0 14px 14px 0; }\n.dropHint[data-zone='top'] { bottom: 50%; border-radius: 14px 14px 0 0; }\n.dropHint[data-zone='bottom'] { top: 50%; border-radius: 0 0 14px 14px; }\n.dropHint[data-zone='center'] { inset: 0; border-radius: 14px; opacity: 0.22; }\n\n/* 正在被拖的窗格(HTML5 DnD 源)略透明。 */\n.leaf[data-drop-zone] {\n outline: 2px solid var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n outline-offset: -2px;\n}\n\n/* 浮动窗拖回的实时插入预览块。 */\n.dockPreview {\n position: absolute;\n pointer-events: none;\n background: var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n opacity: 0.22;\n border: 2px solid var(--corum-glass-border-active, var(--dsw-alias-brand-primary));\n border-radius: 12px;\n z-index: 40;\n transition: left 80ms ease, top 80ms ease, width 80ms ease, height 80ms ease;\n}\n","/**\n * GridView —— 递归渲染布局树:分支(row/column split)按 weights 分配空间,\n * 相邻子节点间一条 sash(复用 VSCode Sash 拖拽机制);叶子是模块窗格\n * (标题栏可拖 + 槽位内容)。拖标题到另一窗格四边 → split,中心 → 交换。\n *\n * 纯组件:树与回调经 props 传入,槽位内容经 renderSlot 解析。\n */\nimport { forwardRef, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { BranchNode, DropZone, GridNode, GridSlot, LeafNode } from './grid.ts'\nimport { subtreeMinSize, isPinnedSlot, getSlotMeta } from './grid.ts'\nimport { RegionCard, INTERACTIVE_SELECTOR, CARD_SELECTOR } from './RegionCard.tsx'\nimport css from './GridView.module.css'\n\n/** GridView 的整体 props。 */\nexport interface GridViewProps {\n root: GridNode\n /** 槽位内容渲染(壳的 renderSlot)。 */\n renderSlot: (slot: GridSlot) => ReactNode\n /** sash 拖拽:branchId + 子节点下标 + 份额 delta。 */\n onResize: (branchId: string, sashIndex: number, deltaFraction: number) => void\n /** 拖放:source leaf id → target leaf id 的 zone。 */\n onDrop: (sourceId: string, targetId: string, zone: DropZone) => void\n /** 脱出某槽位到浮动窗(可选)。 */\n onPopOut?: (slot: GridSlot) => void\n /** 关闭某槽位(不显示,可在状态栏恢复)。 */\n onClose?: (slot: GridSlot) => void\n /** 从面板拖入新槽位到 target leaf 的某侧。 */\n onDropNewSlot?: (slot: GridSlot, targetId: string, zone: DropZone) => void\n /** 已脱出到浮动窗的槽位集合——运行时折叠(树不动)。 */\n detachedSlots?: ReadonlySet<string>\n /** 透明整卡的槽位(子卡独立、间隙透出背景,不垫外层玻璃卡)。由子壳按设计注入。 */\n transparentSlots?: ReadonlySet<string>\n /** 顶层 row 分支各格内容顶部下移像素(design.pen:titlebar-row 只压左列,\n * sidebar/conversation 格内容让位标题栏,right-col 格顶到容器顶)。\n * 沿主轴按 child 下标取数(root row 的格序固定)。 */\n leafTopOffset?: readonly number[]\n /**\n * 折叠收起的槽位集合(如 IDE 侧栏收成 56px 图标轨):这些 leaf 渲染为各自\n * SlotMeta.collapsedWidth 的固定宽(不参与 weight 分配、两侧 sash 隐藏不可\n * 拖),不再是 detached 的 0 宽。P2-2:折叠态由本 prop 显式传入\n * (grid.ts 不再持有模块级折叠 Set),subtreeMinSize 在折叠态取\n * collapsedWidth,窗口自适应不会拉回展开宽。\n */\n collapsedSlots?: ReadonlySet<string>\n /**\n * 运行时锁定宽(px):slot → 固定宽。与 collapsedSlots 的 locked 同机制\n * (locked 格宽度锁定、不参与 weight 分配、两侧 sash 隐藏),但宽度由壳\n * 直接给(不走 SlotMeta.collapsedWidth 注册表)。用于「某区域临时锁为\n * 固定宽」的壳层场景——如 IDE 右侧三区域全隐藏时,侧栏锁为 300(而非按\n * 300:509 占比被拉宽,2026-08-30 用户定调 A)。\n */\n lockedSlots?: ReadonlyMap<string, number>\n}\n\n/** 每格主轴最小尺寸来自各子树的 subtreeMinSize(grid.ts 注册表 + 兜底),本文件不再持有硬编码常量。 */\n\n/** 一条 sash(沿用 AppFrame 验证过的 VSCode 机制);ref 透传给分支布局写位置。 */\nconst Sash = forwardRef<HTMLDivElement, { direction: 'row' | 'column'; onDrag: (delta: number) => void }>(function Sash(props, ref) {\n const vertical = props.direction === 'row' // row 分支的 sash 是竖条(拖左右)\n const origin = useRef(0)\n const cbRef = useRef(props.onDrag)\n cbRef.current = props.onDrag\n\n const onMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (e.button !== 0) return\n e.preventDefault()\n origin.current = vertical ? e.clientX : e.clientY\n const cursor = vertical ? 'col-resize' : 'row-resize'\n const style = document.createElement('style')\n style.textContent = `* { cursor: ${cursor} !important; user-select: none !important; -webkit-user-select: none !important; }`\n document.head.appendChild(style)\n const onMove = (ev: MouseEvent) => {\n ev.preventDefault()\n cbRef.current((vertical ? ev.clientX : ev.clientY) - origin.current)\n origin.current = vertical ? ev.clientX : ev.clientY\n }\n const onUp = () => {\n window.removeEventListener('mousemove', onMove, true)\n window.removeEventListener('mouseup', onUp, true)\n style.remove()\n }\n window.addEventListener('mousemove', onMove, true)\n window.addEventListener('mouseup', onUp, true)\n }, [vertical])\n\n return <div ref={ref} className={vertical ? css.sashV : css.sashH} onMouseDown={onMouseDown} data-sash={props.direction} />\n})\n\n/** 一个叶子窗格:标准区域卡片(RegionCard 基座)+ grid 拖放高亮。 */\nfunction LeafView(props: {\n leaf: LeafNode\n renderSlot: (slot: GridSlot) => ReactNode\n onDrop: GridViewProps['onDrop']\n onPopOut?: GridViewProps['onPopOut']\n onDropNewSlot?: GridViewProps['onDropNewSlot']\n transparentSlots?: ReadonlySet<string> | undefined\n}) {\n const { leaf, renderSlot, onDrop, onPopOut, onDropNewSlot, transparentSlots } = props\n // 钉住的 leaf(如 IDE 侧栏)不参与自由组合:不作拖拽源(不可拖走/脱出)、\n // 不响应 dragover/drop(不可被拖入 split/swap)。\n const pinned = isPinnedSlot(leaf.slot)\n const [zone, setZone] = useState<DropZone | null>(null)\n /** 当前叶子是否为拖拽源:拖拽源自身不响应 dragover(落到自己是 no-op,不该高亮)。 */\n const [sourceDragging, setSourceDragging] = useState(false)\n const ref = useRef<HTMLDivElement | null>(null)\n /** 当前拖拽的 ghost 克隆移除句柄(dragstart 设置,dragend 清理——所有结束路径)。 */\n const ghostCleanupRef = useRef<(() => void) | null>(null)\n\n const zoneFromPoint = (clientX: number, clientY: number): DropZone => {\n const el = ref.current\n if (el === null) return 'center'\n const r = el.getBoundingClientRect()\n const fx = (clientX - r.left) / r.width\n const fy = (clientY - r.top) / r.height\n const EDGE = 0.25\n if (fx < EDGE) return 'left'\n if (fx > 1 - EDGE) return 'right'\n if (fy < EDGE) return 'top'\n if (fy > 1 - EDGE) return 'bottom'\n return 'center'\n }\n\n // 接受两种 drag 类型:已有窗格拖拽(corum/leaf-id)和面板新槽位拖入(corum/new-slot)\n const hasDropType = (types: readonly string[]) => types.includes('corum/leaf-id') || types.includes('corum/new-slot')\n\n // 网格内重排 + 拖出浮动窗的统一拖拽源:整叶 draggable(设计稿无区域标题栏)。\n // 让位只排除「明确交互控件 + 已选中文字」,其余区域按下拖动即发起\n // corum/leaf-id:窗口内释放 → 网格内 split/swap;拖出窗口外 → 浮动窗。\n //\n // 关键(2026-08-30 实测根因):原生 HTML5 DnD 的 dragstart 由浏览器在\n // 「mousedown + 移动超过阈值」时向上找**最近的 draggable 祖先**(= 整叶根,\n // 因整叶 draggable=true)并**在该祖先上派发**——因此本回调的 e.target 是\n // leaf 根本身,**不是** mousedown 命中的正文/按钮。用 e.target.closest(...)\n // 查交互/卡片恒 null(卡片是 leaf 的后代非祖先),清单形同虚设。故判定\n // 让位必须用 **mousedown 真实命中点元素**(dragstart 的 clientX/Y 与按下点\n // 一致,elementFromPoint 复取),不能用 e.target。\n const onLeafDragStart = (e: React.DragEvent<HTMLDivElement>) => {\n // 钉住的 leaf 不作拖拽源(位置固定,不可拖走/脱出浮动窗)。\n if (pinned) { e.preventDefault(); return }\n // mousedown 真实命中的元素(e.target 是 leaf 根——见上方注释)。\n const hit = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n const target = (hit !== null && (e.currentTarget as HTMLElement).contains(hit) ? hit : e.target) as HTMLElement\n // 让位:交互控件(按钮/输入/Monaco/图片等)上的按下不触发整叶拖拽。\n // 注意 INTERACTIVE_SELECTOR 不能含 [draggable=\"true\"],否则会命中整叶根\n // 自身,导致每次 dragstart 都被 preventDefault、整叶永远拖不动。\n if (target.closest(INTERACTIVE_SELECTOR)) { e.preventDefault(); return }\n // 让位:卡片容器(消息气泡/工具卡/审批卡/Review 卡/会话行/文件树行等)\n // 整体不可拖(2026-08-30 用户定调)——卡片上按下拖动 = 选中文字或无反应。\n if (target.closest(CARD_SELECTOR)) { e.preventDefault(); return }\n const selection = window.getSelection()\n if (selection !== null && !selection.isCollapsed) { e.preventDefault(); return }\n e.dataTransfer.setData('corum/leaf-id', leaf.id)\n e.dataTransfer.effectAllowed = 'move'\n setSourceDragging(true)\n // 显式 drag image(2026-08-30 终版 · 最小可用形态):leaf 的 will-change:\n // transform 独立合成层 + 0.1.2 会话区换肤的 backdrop-filter 玻璃层组合下,\n // Chromium 的整页 native ghost 失效(空态拍进相邻区域 / 非空态无 ghost)。\n // 因此克隆 leaf 作显式 drag image(只含本区域、剥离 backdrop-filter)。\n //\n // 坑(2026-08-30 多轮实测收敛):不要对克隆加 transform/opacity/scrollTop\n // 等任何会触发「非平凡渲染」的样式——非空态消息流克隆含合成层+玻璃层,\n // 任何额外渲染干预都会让 Chromium 对克隆的快照拍空(无 ghost)。克隆就\n // 原样挂 body 末尾、z-index:-1 压底(不被看见),快照稳定可见。快照内容\n // 是克隆滚动容器 scrollTop=0 的顶部画面(非空态不含底部 composer)——\n // 这是「稳定有 ghost」与「画面含 composer」二选一的取舍,用户确认保稳定。\n //\n // ghost 源用 e.currentTarget(= leaf 根,整叶画面)。\n const sourceEl = e.currentTarget as HTMLElement\n const rect = sourceEl.getBoundingClientRect()\n const ghost = sourceEl.cloneNode(true) as HTMLElement\n ghost.setAttribute('data-drag-ghost', '')\n ghost.style.position = 'fixed'\n ghost.style.left = '0'\n ghost.style.top = '0'\n ghost.style.width = `${rect.width}px`\n ghost.style.height = `${rect.height}px`\n // 克隆本体不被看见:负 margin-top 把内容推出视口(纯布局位移——不碰\n // top/transform/opacity/scrollTop 这些会让 Chromium 快照拍空的渲染样式;\n // z-index:-1 在含背景/transform 的祖先下压不住,曾致克隆在左上角可见)。\n // position:fixed 相对视口定位,top:0 + margin-top:-(height+100) → 内容整体\n // 在视口外,不占屏幕像素;快照仍可拍(文档内、可见、非 display:none)。\n ghost.style.margin = '0'\n ghost.style.marginTop = `${-Math.ceil(rect.height) - 100}px`\n ghost.style.pointerEvents = 'none'\n document.body.appendChild(ghost)\n e.dataTransfer.setDragImage(\n ghost,\n Math.round(e.clientX - rect.left),\n Math.round(e.clientY - rect.top),\n )\n // 克隆移除统一收进 onLeafDragEnd(React dragend——所有拖拽结束路径都走:\n // 窗口内 drop / 窗口外释放 / Esc 取消);此闭包只保留「窗口外释放 → 脱出」。\n ghostCleanupRef.current = () => ghost.remove()\n // 拖出主窗口外 → 该区域脱出为独立浮动窗。document 的 dragend 在窗口外释放\n // 时也触发;释放点坐标越界(离开窗口可视区)视为「拖到 APP 外」,触发脱出\n // 而非网格内拆分。网格内释放则走各 leaf 的 onDrop(split / swap)。\n const onDragEndDoc = (ev: DragEvent) => {\n document.removeEventListener('dragend', onDragEndDoc, true)\n const outX = ev.clientX <= 0 || ev.clientX >= window.innerWidth\n const outY = ev.clientY <= 0 || ev.clientY >= window.innerHeight\n if ((outX || outY) && onPopOut) onPopOut(leaf.slot)\n }\n document.addEventListener('dragend', onDragEndDoc, true)\n }\n const onLeafDragEnd = (): void => {\n ghostCleanupRef.current?.()\n ghostCleanupRef.current = null\n setSourceDragging(false)\n setZone(null)\n }\n\n return (\n <div\n ref={ref}\n className={css.leaf}\n data-slot={leaf.slot}\n data-drop-zone={zone ?? undefined}\n data-pinned={pinned || undefined}\n draggable={!pinned}\n onDragStart={onLeafDragStart}\n onDragEnd={onLeafDragEnd}\n onDragOver={(e) => {\n if (sourceDragging) return // 拖拽源自身不响应 dragover\n if (pinned) return // 钉住的 leaf 不可被拖入 split/swap,不显示 drop hint\n if (!hasDropType(e.dataTransfer.types)) return\n e.preventDefault()\n e.dataTransfer.dropEffect = e.dataTransfer.types.includes('corum/new-slot') ? 'copy' : 'move'\n setZone(zoneFromPoint(e.clientX, e.clientY))\n }}\n onDragLeave={() => setZone(null)}\n onDrop={(e) => {\n const newSlot = e.dataTransfer.getData('corum/new-slot') as GridSlot | ''\n const sourceId = e.dataTransfer.getData('corum/leaf-id')\n setZone(null)\n if (newSlot !== '') {\n e.preventDefault()\n onDropNewSlot?.(newSlot, leaf.id, zoneFromPoint(e.clientX, e.clientY))\n } else if (sourceId !== '') {\n e.preventDefault()\n onDrop(sourceId, leaf.id, zoneFromPoint(e.clientX, e.clientY))\n }\n }}\n >\n <RegionCard\n slotKey={leaf.slot}\n transparent={transparentSlots?.has(leaf.slot) ?? false}\n >\n {renderSlot(leaf.slot)}\n </RegionCard>\n {zone !== null && <div className={css.dropHint} data-zone={zone} aria-hidden=\"true\" />}\n </div>\n )\n}\n\n/** 递归渲染一个节点。 */\nfunction NodeView(props: GridViewProps & { node: GridNode; depth?: number }) {\n const { node, depth = 0, ...rest } = props\n if (node.type === 'leaf') {\n return <LeafView leaf={node} renderSlot={rest.renderSlot} onDrop={rest.onDrop} onPopOut={rest.onPopOut} onDropNewSlot={rest.onDropNewSlot} transparentSlots={rest.transparentSlots} />\n }\n return <BranchView branch={node} depth={depth} {...rest} />\n}\n\n/**\n * 计算一个分支各格的主轴像素(VSCode SplitView proportionalLayout 的对应物,\n * splitview.ts L856–873):可见格按 weight 占比瓜分容器主轴 span,脱出的格\n * size=0(不占 offset),Σmin > span 时等比压缩到正好放下(不溢出截断)。\n * weights 即各格目标像素;sash 拖动只在相邻两格转移 weight(Σ不变),所以\n * 非相邻格的计算结果像素严格不变——不传导。\n */\nfunction computeCellSizes(weights: number[], detached: boolean[], mins: number[], span: number, locked: readonly (number | null)[] = []): number[] {\n const n = weights.length\n // locked 格:宽度锁定(折叠轨),从 span 先扣除,不参与 weight 分配。\n let lockedTotal = 0\n for (let i = 0; i < n; i++) if (locked[i] != null) lockedTotal += locked[i] as number\n const freeSpan = span - lockedTotal\n const visible = weights.map((_, i) => !detached[i] && locked[i] == null)\n const visCount = visible.filter(Boolean).length\n const result: number[] = weights.map((_, i) => (locked[i] != null ? (locked[i] as number) : 0))\n if (visCount === 0 || freeSpan <= 0) return result\n let total = 0\n for (let i = 0; i < n; i++) if (visible[i]) total += Math.max(0, weights[i] ?? 0)\n if (total <= 0) {\n // 防御:可见格 weight 全 0(非法树)——均分。\n const each = freeSpan / visCount\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = each\n return result\n }\n let sizes = weights.map((w, i) => (visible[i] ? (Math.max(0, w) / total) * freeSpan : 0))\n // 各格最小尺寸(脱出的格不参与求和——它不占空间)。\n let minTotal = 0\n for (let i = 0; i < n; i++) if (visible[i]) minTotal += mins[i] ?? 0\n if (minTotal >= freeSpan) {\n // 容器太窄:按各格 min 比例分配(而非等比平分)——保住各区域声明的\n // 最小比,小窗不丢布局、大窗恢复后仍贴近用户拖的比例,绝不溢出截断。\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = ((mins[i] ?? 0) / minTotal) * freeSpan\n return result\n }\n // 夹各格自己的 min,夹取的差额从仍有富余的格里按比例补给(保持 Σ = freeSpan)。\n let deficit = 0\n sizes = sizes.map((s, i) => {\n if (!visible[i]) return 0\n const min = mins[i] ?? 0\n if (s < min) { deficit += min - s; return min }\n return s\n })\n if (deficit > 0) {\n const slack = sizes.reduce((a, s, i) => a + Math.max(0, s - (visible[i] ? mins[i] ?? 0 : 0)), 0)\n if (slack > 0) {\n sizes = sizes.map((s, i) => {\n const min = visible[i] ? mins[i] ?? 0 : 0\n return s > min ? s - (Math.max(0, s - min) / slack) * deficit : s\n })\n }\n }\n for (let i = 0; i < n; i++) if (visible[i]) result[i] = sizes[i]\n return result\n}\n\n/**\n * 渲染一个分支(VSCode SplitView 式绝对定位):容器 position:relative,每格\n * position:absolute,left/top/width/height 由 JS 沿主轴累加 offset 算出并\n * 直接写 DOM style(不经 setState,避免每格重渲染)。容器尺寸用\n * ResizeObserver 监听(rAF 节流),变化时按 weights 占比重标定(自适应)。\n * sash 位置 = 相邻格边界;相邻有脱出格时该缝隐藏(拖它会改隐藏格 weight,\n * 经比例分配传导到非相邻格,违背「只相邻两格变」)。\n */\nfunction BranchView(props: Omit<GridViewProps, 'root'> & { branch: BranchNode; depth?: number }) {\n const { branch, depth = 0, ...rest } = props\n const containerRef = useRef<HTMLDivElement | null>(null)\n const cellRefs = useRef(new Map<string, HTMLDivElement>())\n const innerRefs = useRef(new Map<string, HTMLDivElement>())\n const sashRefs = useRef(new Map<number, HTMLDivElement>())\n\n // 折叠 = 运行时脱出(detachedSlots)或持久化关闭(hidden)。两者都折叠为\n // 0 宽、相邻填满;hidden 进持久化(重启保持关闭),detached 是临时的。\n // 注意:直接子可能是嵌套 branch(如 right-col 里的 row(editor,explorer))——\n // 不能只看 c.type==='leaf' 的 c.hidden,否则 branch 子其内部 leaf 全 hidden 也\n // 判不出,该支仍按 weight 占位、兄弟格(终端/对话区)不铺满。branch 子用\n // 递归判「所有后代 leaf 全隐藏(leaf.hidden 或 detachedSlots)」——2026-08-30\n // 修复:nodeAllHidden 只看 leaf.hidden,detachedSlots(右侧三区域默认隐藏/\n // 浮动窗脱出)不在其内 → right-col 全 detached 也判不出,对话区不铺满空缺。\n const allDetached = (node: GridNode): boolean =>\n node.type === 'leaf'\n ? ((rest.detachedSlots?.has(node.slot) ?? false) || node.hidden === true)\n : node.children.every(allDetached)\n const detached = branch.children.map((c) =>\n c.type === 'leaf'\n ? ((rest.detachedSlots?.has(c.slot) ?? false) || c.hidden === true)\n : allDetached(c))\n // 折叠收起(collapsedSlots):leaf 锁定为各自 collapsedWidth 的固定宽(非 0),\n // 不参与 weight 分配、两侧 sash 隐藏不可拖。折叠态直接来自本 prop(P2-2:\n // grid.ts 无状态,leafMinSize 经 collapsed 参数取同一份声明值)。\n // lockedSlots(壳层运行时锁定宽,如右侧全隐藏时侧栏锁 300)优先——同一\n // locked 机制,宽度由壳直给。\n const locked = branch.children.map((c) => {\n if (c.type !== 'leaf') return null\n const runtimeLocked = rest.lockedSlots?.get(c.slot)\n if (runtimeLocked !== undefined) return runtimeLocked\n if (!(rest.collapsedSlots?.has(c.slot) ?? false)) return null\n return getSlotMeta(c.slot)?.collapsedWidth ?? null\n })\n\n // 每次渲染重建的最新 layout 闭包(读最新 branch/detached/locked),供 RO/rAF 调用。\n const layoutRef = useRef<() => void>(() => {})\n layoutRef.current = () => {\n const el = containerRef.current\n if (el === null) return\n const w = el.clientWidth\n const h = el.clientHeight\n if (w <= 0 || h <= 0) return\n const isRow = branch.direction === 'row'\n const span = isRow ? w : h\n // 各子树 min:全 detached 的子树(leaf.hidden 或 detachedSlots)min 取 0——\n // subtreeMinSize 只看 leaf.hidden,detachedSlots(右侧三区域默认隐藏/浮动窗\n // 脱出)不在其内;否则全 detached 支的 min 仍计入 minTotal,兄弟格被「隐形\n // min」顶住无法铺满空缺(2026-08-30:right-col 全隐藏后对话区不填满)。\n const mins = branch.children.map((c, i) => (detached[i] ? 0 : subtreeMinSize(c, isRow, rest.collapsedSlots)))\n const sizes = computeCellSizes(branch.weights, detached, mins, span, locked)\n // 顶层 row 的 leafTopOffset:内容格 top 下移、height 相应缩短(绝对定位格\n // 自身仍占满全高,内容格让位)。只作用于 depth=0 的 row 分支。\n const topOffsets = depth === 0 && isRow ? rest.leafTopOffset : undefined\n let offset = 0\n for (let i = 0; i < branch.children.length; i++) {\n const size = sizes[i] ?? 0\n // sash i-1 在 cell i 的左/上边界上。\n if (i > 0) {\n const sash = sashRefs.current.get(i - 1)\n if (sash) {\n // 折叠轨相邻的 sash 隐藏(折叠格宽度锁定不可拖;拖它会改锁定格)。\n const show = size > 0 && (sizes[i - 1] ?? 0) > 0 && locked[i] == null && locked[i - 1] == null\n sash.style.visibility = show ? 'visible' : 'hidden'\n if (isRow) {\n sash.style.left = `${offset - 4}px`\n sash.style.top = '0px'\n sash.style.height = `${h}px`\n } else {\n sash.style.top = `${offset - 4}px`\n sash.style.left = '0px'\n sash.style.width = `${w}px`\n }\n }\n }\n const cell = cellRefs.current.get(branch.children[i].id)\n if (cell) {\n if (size <= 0) {\n cell.style.visibility = 'hidden'\n } else {\n cell.style.visibility = 'visible'\n if (isRow) {\n cell.style.left = `${offset}px`\n cell.style.top = '0px'\n cell.style.width = `${size}px`\n cell.style.height = `${h}px`\n // 内容格让位:top 下移 offset、height 缩短 offset(卡片从标题栏下方开始)。\n const inner = innerRefs.current.get(branch.children[i].id)\n if (inner) {\n const topOff = topOffsets?.[i] ?? 0\n inner.style.top = `${topOff}px`\n inner.style.height = `${Math.max(0, h - topOff)}px`\n }\n } else {\n cell.style.top = `${offset}px`\n cell.style.left = '0px'\n cell.style.height = `${size}px`\n cell.style.width = `${w}px`\n }\n }\n }\n offset += size\n }\n }\n\n // 渲染后(paint 前)重排:树 weights / detached / 方向任何变化都立刻反映。\n useLayoutEffect(() => {\n layoutRef.current()\n })\n\n // 容器尺寸变化(窗口缩放 / 父格重排)→ rAF 节流后按占比重标定 + layout。\n useEffect(() => {\n const el = containerRef.current\n if (el === null) return\n let raf: number | null = null\n const observer = new ResizeObserver(() => {\n raf ??= requestAnimationFrame(() => {\n raf = null\n layoutRef.current()\n })\n })\n observer.observe(el)\n return () => {\n observer.disconnect()\n if (raf !== null) cancelAnimationFrame(raf)\n }\n }, [])\n\n // sash 拖动:把 px delta 直接当像素转移(weights 就是像素),交给上层改树。\n const makeSashDrag = (sashIndex: number) => (pxDelta: number) => {\n rest.onResize(branch.id, sashIndex, pxDelta)\n }\n\n const cells: ReactNode[] = []\n branch.children.forEach((child, i) => {\n if (i > 0) {\n const sashIndex = i - 1\n cells.push(\n <Sash\n key={`sash-${i}`}\n ref={(el) => {\n if (el) sashRefs.current.set(sashIndex, el)\n else sashRefs.current.delete(sashIndex)\n }}\n direction={branch.direction}\n onDrag={makeSashDrag(sashIndex)}\n />,\n )\n }\n cells.push(\n <div\n key={child.id}\n ref={(el) => {\n if (el) cellRefs.current.set(child.id, el)\n else cellRefs.current.delete(child.id)\n }}\n className={css.branchCell}\n data-detached={detached[i] || undefined}\n >\n <div\n ref={(el) => {\n if (el) innerRefs.current.set(child.id, el)\n else innerRefs.current.delete(child.id)\n }}\n className={css.branchInner}\n >\n {detached[i] ? null : <NodeView {...rest} root={child} node={child} depth={depth + 1} />}\n </div>\n </div>,\n )\n })\n\n return (\n <div\n ref={containerRef}\n className={branch.direction === 'row' ? css.branchRow : css.branchColumn}\n data-branch={branch.id}\n data-direction={branch.direction}\n >\n {cells}\n </div>\n )\n}\n\n/** 顶层 GridView:网格 + 浮动窗拖回的实时插入预览。 */\nexport function GridView(props: GridViewProps) {\n const gridRef = useRef<HTMLDivElement | null>(null)\n // 浮动窗拖回预览:{x, y} 相对网格左上角;dragging=false 时清除。\n const [preview, setPreview] = useState<{ x: number; y: number } | null>(null)\n\n useEffect(() => {\n const bridge = (window as unknown as { corumDesktop?: { onFloatingDrag?: (cb: (p: { dragging: boolean; x?: number; y?: number }) => void) => () => void } }).corumDesktop\n if (bridge?.onFloatingDrag === undefined) return\n return bridge.onFloatingDrag((p) => {\n const grid = gridRef.current\n if (grid === null) return\n if (!p.dragging || p.x === undefined || p.y === undefined) {\n setPreview(null)\n return\n }\n // p.x/p.y 是相对主窗口左上角的屏幕坐标;换算成相对网格的坐标。\n const r = grid.getBoundingClientRect()\n setPreview({ x: p.x - r.left, y: p.y - r.top })\n })\n }, [])\n\n // 命中的窗格 + zone:从预览坐标向下找 leaf,再按相对位置判 zone。\n const previewTarget = preview === null ? null : (() => {\n const grid = gridRef.current\n if (grid === null) return null\n const r = grid.getBoundingClientRect()\n const el = document.elementFromPoint(r.left + preview.x, r.top + preview.y)\n // LeafView 根同时带 data-slot 与 draggable(RegionCard 的 data-slot 在内层、无\n // draggable),用「draggable 的网格叶子」精确命中,避免双条件选择器落空。\n const leafEl = el?.closest('[data-slot][draggable=\"true\"]') as HTMLElement | null\n if (leafEl == null) return null\n const lr = leafEl.getBoundingClientRect()\n const fx = (r.left + preview.x - lr.left) / lr.width\n const fy = (r.top + preview.y - lr.top) / lr.height\n const EDGE = 0.25\n const zone = fx < EDGE ? 'left' : fx > 1 - EDGE ? 'right' : fy < EDGE ? 'top' : fy > 1 - EDGE ? 'bottom' : 'center'\n // 预览块的几何:zone 覆盖 leaf 的一半,center 覆盖全部(相对网格坐标)。\n let rect: { left: number; top: number; width: number; height: number }\n const relLeft = lr.left - r.left\n const relTop = lr.top - r.top\n if (zone === 'left') rect = { left: relLeft, top: relTop, width: lr.width / 2, height: lr.height }\n else if (zone === 'right') rect = { left: relLeft + lr.width / 2, top: relTop, width: lr.width / 2, height: lr.height }\n else if (zone === 'top') rect = { left: relLeft, top: relTop, width: lr.width, height: lr.height / 2 }\n else if (zone === 'bottom') rect = { left: relLeft, top: relTop + lr.height / 2, width: lr.width, height: lr.height / 2 }\n else rect = { left: relLeft, top: relTop, width: lr.width, height: lr.height }\n return { rect, zone }\n })()\n\n return (\n <div className={css.grid} ref={gridRef} style={{ position: 'relative' }}>\n <NodeView {...props} node={props.root} />\n {previewTarget !== null && (\n <div\n className={css.dockPreview}\n data-zone={previewTarget.zone}\n style={{\n left: previewTarget.rect.left,\n top: previewTarget.rect.top,\n width: previewTarget.rect.width,\n height: previewTarget.rect.height,\n }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n )\n}\n","/* FloatingLayer —— 全应用级悬浮层。挂 document.body,z-index 高于一切\n 帧内容。容器本身不挡交互(pointer-events:none),各悬浮项按需恢复。 */\n\n.host {\n position: fixed;\n inset: 0;\n z-index: 100;\n pointer-events: none;\n}\n\n/* 单个悬浮项:占满视口,modal 项挡交互,非 modal 项(通知)不挡。 */\n.item {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.item[data-modal] {\n pointer-events: auto;\n}\n\n/* modal 遮罩:点击关闭该项。 */\n.mask {\n position: absolute;\n inset: 0;\n background: var(--dsw-alias-bg-mask-1);\n backdrop-filter: var(--dsw-mask-blur);\n}\n\n/* 内容体:相对遮罩在上,恢复交互。 */\n.body {\n position: relative;\n pointer-events: auto;\n}\n","/**\n * FloatingLayer —— 通用悬浮层(shell-base)。\n *\n * 与 shell.overlay(帧内浮层,受 frame 的 transform/overflow 约束)不同,\n * FloatingLayer 经 createPortal 挂到 document.body,彻底脱离网格/卡片的\n * 合成层与裁剪上下文,是全应用级模态/悬浮的唯一合法出口:\n * - 设置面板(官方 ui-settings-general,面板在窗格 DOM 内,需迁移上来)\n * - 未来的应用内通知、对话框、确认框等\n *\n * 两条使用路径:\n * 1. 注册式(自建内容):openFloating({ id, content, modal }) ——\n * 内容以 React 元素注册,层内渲染;closeFloating(id) 关闭。\n * 2. 迁移式(第三方/官方组件,组件树不能重建):floatingLayerHost(el)\n * 拿到容器,把已渲染的 DOM 子树 appendChild 进来,关闭时移回原位。\n * DOM 移动不触发 React 重建,组件内部 state(如 open)保留。\n *\n * 多个悬浮项按打开顺序堆叠;modal 项带遮罩(点击遮罩关闭该项),\n * 非 modal 项(如通知)不挡下层交互。Escape 关闭最顶层 modal 项。\n */\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport css from './FloatingLayer.module.css'\n\n/** 一个悬浮项。 */\nexport interface FloatingItem {\n /** 唯一 id(重复 open 同 id = 替换内容并置顶)。 */\n id: string\n /** 悬浮内容(注册式)。 */\n content: ReactNode\n /** 是否模态(带遮罩、挡下层交互、Escape/点遮罩关闭)。默认 true。 */\n modal?: boolean\n /** 关闭回调(点遮罩 / Escape / closeFloating 触发)。 */\n onClose?: (() => void) | undefined\n}\n\n/** 悬浮层对外面:注册/关闭。 */\nexport interface FloatingLayerApi {\n openFloating: (item: FloatingItem) => void\n closeFloating: (id: string) => void\n}\n\nconst FloatingContext = createContext<FloatingLayerApi | null>(null)\n\n/** 在 FloatingLayer 子树内取悬浮层 API(未挂载时返回 null)。 */\nexport function useFloatingLayer(): FloatingLayerApi | null {\n return useContext(FloatingContext)\n}\n\n/**\n * 迁移式使用的宿主容器:全局唯一(应用只挂一个 FloatingLayer)。\n * FloatingLayer 挂载后把容器 DOM 记录到这里,供「把官方组件渲染出的\n * DOM 子树移上来」的场景取用(不经过 React,组件状态不丢)。\n */\nlet hostElement: HTMLDivElement | null = null\n\n/** 取悬浮层容器(FloatingLayer 已挂载时非 null)。 */\nexport function floatingLayerHost(): HTMLDivElement | null {\n return hostElement\n}\n\n/** 通用悬浮层。挂在壳的 frame 根(应用唯一)。 */\nexport function FloatingLayer(props: { children?: ReactNode }) {\n const [items, setItems] = useState<readonly FloatingItem[]>([])\n const hostRef = useRef<HTMLDivElement | null>(null)\n\n const closeFloating = useCallback((id: string) => {\n setItems((prev) => {\n const item = prev.find((i) => i.id === id)\n item?.onClose?.()\n return prev.filter((i) => i.id !== id)\n })\n }, [])\n\n const openFloating = useCallback((item: FloatingItem) => {\n setItems((prev) => [...prev.filter((i) => i.id !== item.id), item])\n }, [])\n\n // 记录全局宿主容器(迁移式场景用)。\n useEffect(() => {\n hostElement = hostRef.current\n return () => { hostElement = null }\n }, [])\n\n // Escape 关闭最顶层 modal 项。\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent): void => {\n if (e.key !== 'Escape') return\n setItems((prev) => {\n for (let i = prev.length - 1; i >= 0; i--) {\n if (prev[i].modal !== false) {\n prev[i].onClose?.()\n return [...prev.slice(0, i), ...prev.slice(i + 1)]\n }\n }\n return prev\n })\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [])\n\n return (\n <FloatingContext.Provider value={{ openFloating, closeFloating }}>\n {props.children}\n {createPortal(\n <div ref={hostRef} className={css.host} data-floating-layer>\n {items.map((item) => (\n <div key={item.id} className={css.item} data-modal={item.modal !== false || undefined}>\n {item.modal !== false && (\n <div className={css.mask} aria-hidden=\"true\" onClick={() => closeFloating(item.id)} />\n )}\n <div className={css.body}>{item.content}</div>\n </div>\n ))}\n </div>,\n document.body,\n )}\n </FloatingContext.Provider>\n )\n}\n","/* ConfirmDialog —— 统一确认提示框样式(全 token,深浅主题自动翻转) */\n\n/* ── 居中模态 ─────────────────────────────────────────────────────── */\n\n.overlay {\n position: fixed;\n inset: 0;\n z-index: 10001;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.35);\n backdrop-filter: blur(8px);\n}\n\n.dialog {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: calc(8px * var(--corum-density-scale, 1));\n width: 360px;\n max-width: calc(100vw - 48px);\n padding: calc(16px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 16px;\n background: var(--corum-glass-1);\n backdrop-filter: blur(16px) saturate(140%);\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);\n box-sizing: border-box;\n}\n\n.closeBtn {\n position: absolute;\n top: 10px;\n right: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 6px;\n background: transparent;\n cursor: pointer;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.closeBtn:hover {\n background: var(--corum-glass-2);\n color: var(--dsw-alias-label-secondary);\n}\n\n/* ── 内联气泡(设计稿 DKRwC confirm-pop)───────────────────────────── */\n/* 调用方容器需 position:relative;默认贴右下方向上弹出。 */\n\n.inlinePop {\n position: absolute;\n right: 0;\n bottom: calc(100% + 8px);\n width: 224px;\n display: flex;\n flex-direction: column;\n gap: calc(8px * var(--corum-density-scale, 1));\n padding: calc(10px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 12px;\n background: var(--corum-glass-1);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);\n box-sizing: border-box;\n z-index: 20;\n}\n\n/* ── 共享内部元素 ─────────────────────────────────────────────────── */\n\n.title {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(12px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.dialog .title {\n font-size: calc(15px * var(--corum-ui-font-scale, 1));\n font-weight: 700;\n}\n\n.message {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-label-secondary);\n line-height: 1.5;\n}\n\n.dialog .message {\n font-size: calc(13px * var(--corum-ui-font-scale, 1));\n}\n\n.warning {\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(10px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-state-error-primary);\n line-height: 1.4;\n}\n\n.dialog .warning {\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n}\n\n.row {\n display: flex;\n justify-content: flex-end;\n gap: calc(6px * var(--corum-density-scale, 1));\n padding-top: calc(4px * var(--corum-density-scale, 1));\n}\n\n.cancelBtn {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 8px;\n background: var(--corum-glass-2);\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n}\n\n.cancelBtn:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.confirmBtnDanger {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: none;\n border-radius: 8px;\n background: var(--dsw-alias-state-error-primary);\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--corum-label-on-brand);\n}\n\n.confirmBtnPrimary {\n padding: calc(5px * var(--corum-density-scale, 1)) calc(12px * var(--corum-density-scale, 1));\n border: none;\n border-radius: 8px;\n background: var(--dsw-alias-brand-primary, var(--corum-state-running));\n cursor: pointer;\n font-family: var(--corum-ui-font-family, 'Inter', -apple-system, sans-serif);\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n color: var(--corum-label-on-brand);\n}\n\n.confirmBtnDanger:disabled,\n.confirmBtnPrimary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n","/**\n * ConfirmDialog —— 统一确认提示框(corum 各域「确认/取消」场景复用)。\n *\n * 两种形态:\n * - placement=\"center\":全屏遮罩 + 居中玻璃对话框(默认,适合打断性确认,如更换模型)。\n * - placement=\"inline\":内联气泡弹窗(适合就地二次确认,对应设计稿 DKRwC confirm-pop;\n * 调用方负责定位容器 position:relative)。\n *\n * 视觉 token 全部走设计变量,深浅主题随 body[data-ds-dark-theme] 自动翻转。\n * 自绘 14px 描边 × 图标(不引 lucide,保持本包零业务依赖)。\n * @module corum-ui-base/client/ConfirmDialog\n */\nimport type { ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport css from './ConfirmDialog.module.css'\n\nexport interface ConfirmDialogProps {\n /** 标题(如「更换模型」「删除技能」)。 */\n title: string\n /** 主文案(确认问题)。 */\n message: ReactNode\n /** 警示补充(如「建议在新任务中更换模型」;可选)。 */\n warning?: ReactNode\n /** 内联错误信息(确认动作失败时展示;可选)。 */\n error?: string | null\n /** 主操作文案(默认「确认」)。 */\n confirmLabel?: ReactNode\n /** 次操作文案(默认「取消」)。 */\n cancelLabel?: ReactNode\n /** 主操作色调:danger=危险(删除等),primary=主操作(默认 danger)。 */\n tone?: 'danger' | 'primary'\n /** 形态:center=居中模态(默认) / inline=内联气泡。 */\n placement?: 'center' | 'inline'\n /** 主操作进行中(禁用按钮并显示 busy 文案)。 */\n busy?: boolean\n /** busy 时主操作文案(默认沿用 confirmLabel)。 */\n busyLabel?: ReactNode\n /** 主操作额外禁用条件。 */\n confirmDisabled?: boolean\n /**\n * 形态:confirm=双键确认(默认) / info=单键告知(替原生 alert,只有主操作键,\n * 点主键/遮罩/× 都走 onCancel 关闭)。\n */\n kind?: 'confirm' | 'info'\n /** 点主操作。kind=info 时可为空(仅关闭)。 */\n onConfirm?: () => void\n /** 点次操作 / 遮罩 / ×;kind=info 时主键也走这里关闭。 */\n onCancel: () => void\n}\n\n/** 14px 描边 ×(关闭)图标,stroke=currentColor。 */\nfunction IconClose() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3L11 11M11 3L3 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport function ConfirmDialog({\n title,\n message,\n warning,\n error,\n confirmLabel,\n cancelLabel = '取消',\n tone = 'danger',\n placement = 'center',\n busy = false,\n busyLabel,\n confirmDisabled = false,\n kind = 'confirm',\n onConfirm,\n onCancel,\n}: ConfirmDialogProps) {\n const confirmText = confirmLabel ?? (kind === 'info' ? '知道了' : '确认')\n const body = (\n <>\n <div className={css.title}>{title}</div>\n <div className={css.message}>{message}</div>\n {warning !== undefined && <div className={css.warning}>{warning}</div>}\n {error !== undefined && error !== null && error !== '' && <div className={css.warning}>{error}</div>}\n <div className={css.row}>\n {kind === 'confirm' && (\n <button type=\"button\" className={css.cancelBtn} onClick={onCancel} disabled={busy}>{cancelLabel}</button>\n )}\n <button\n type=\"button\"\n className={tone === 'primary' ? css.confirmBtnPrimary : css.confirmBtnDanger}\n onClick={kind === 'info' ? onCancel : onConfirm}\n disabled={busy || confirmDisabled}\n >{busy ? (busyLabel ?? confirmText) : confirmText}</button>\n </div>\n </>\n )\n\n if (placement === 'inline') {\n return <div className={css.inlinePop}>{body}</div>\n }\n\n return createPortal(\n <div className={css.overlay} onClick={onCancel}>\n <div className={css.dialog} onClick={e => e.stopPropagation()} role=\"alertdialog\" aria-modal=\"true\">\n <button type=\"button\" className={css.closeBtn} onClick={onCancel} aria-label=\"关闭\"><IconClose /></button>\n {body}\n </div>\n </div>,\n document.body,\n )\n}\n","/**\n * Global theme DOM applier: projects the resolved ThemeSnapshot onto the\n * document — `html { color-scheme }` for native UA chrome, `body[data-ds-dark-theme]`\n * for the token palette, the active theme's alias-token overrides as inline CSS\n * variables on body, the content font-size axis (`--dsh-content-font-size`), and\n * one presenter-owned `meta[name=\"theme-color\"]`. Pure DOM writes, no React\n * involvement. This is a fork of ui-layout's presenter: a corum shell owns the\n * same projection duty (the official ui-layout is disabled, so nothing else\n * writes the body palette attribute).\n *\n * ## 正文字号轴(2026-09-16 补上,修「运行时断链」)\n *\n * 官方把 `--dsh-content-font-size` 的写入职责放在 **ui-layout 的 ThemePresenter** 里\n * (`ui-layout/src/client/theme-presenter.ts:47`)。corum 禁用了 ui-layout,\n * 而本 fork 起初**只投影了调色板、漏了这条轴** ⇒ 于是出现:\n * - `boot-theme.ts` 在**加载时**写入一次(值 = 当时的设置),所以变量**存在**;\n * - 但**运行时更新无人负责** ⇒ 用户改「会话正文字号」后变量纹丝不动,\n * **改了等于没改**(真机实测:设 17 后变量仍为 14px)。\n *\n * 这正是 PRD §4.3.1 记的「⚠️ 运行时断链,需修」。现按官方语义补上写入,\n * 并把该变量纳入 `dispose()` 的撤回集(与官方一致:presenter 只撤回自己写的东西)。\n */\nimport type { ThemeSnapshot } from '@deepseek-ai/dsh-client-ui-theme/client'\n\n/** Body attribute selecting the dark base palette in the token stylesheets. */\nexport const DARK_ATTRIBUTE = 'data-ds-dark-theme'\n\n/**\n * Body variable carrying the user's content font size in px.\n *\n * ⚠️ 必须与官方 `ui-layout/src/client/theme-presenter.ts` 的\n * `CONTENT_FONT_SIZE_VARIABLE` 保持**同名**,否则消费端(corum-ui-chat /\n * corum-ui-conversation 等大量 CSS 里的 `var(--dsh-content-font-size, 14px)`)\n * 解析不到。\n */\nexport const CONTENT_FONT_SIZE_VARIABLE = '--dsh-content-font-size'\n\n/** Applies theme snapshots to the document; one instance per plugin fiber. */\nexport class ThemePresenter {\n /** Token names this presenter wrote in the last apply (its retraction set). */\n private appliedTokens: string[] = []\n /** The single metadata node this presenter inserts and removes. */\n private readonly themeColorMeta: HTMLMetaElement\n\n constructor() {\n this.themeColorMeta = document.createElement('meta')\n this.themeColorMeta.name = 'theme-color'\n }\n\n apply(snapshot: ThemeSnapshot): void {\n const scheme = snapshot.active.colorScheme\n document.documentElement.style.colorScheme = scheme\n const body = document.body\n if (scheme === 'dark') body.setAttribute(DARK_ATTRIBUTE, '')\n else body.removeAttribute(DARK_ATTRIBUTE)\n // 正文字号轴:官方由 ui-layout 的 ThemePresenter 写,corum 侧必须自己写,\n // 否则「会话正文字号」设置只落盘、不生效(PRD §4.3.1 的运行时断链)。\n body.style.setProperty(CONTENT_FONT_SIZE_VARIABLE, `${snapshot.fontSize}px`)\n for (const name of this.appliedTokens) body.style.removeProperty(name)\n this.appliedTokens = []\n for (const [name, value] of Object.entries(snapshot.active.tokens)) {\n body.style.setProperty(name, value)\n this.appliedTokens.push(name)\n }\n this.themeColorMeta.content = getComputedStyle(body).backgroundColor\n if (!this.themeColorMeta.isConnected) document.head.append(this.themeColorMeta)\n }\n\n dispose(): void {\n document.documentElement.style.removeProperty('color-scheme')\n const body = document.body\n body.removeAttribute(DARK_ATTRIBUTE)\n body.style.removeProperty(CONTENT_FONT_SIZE_VARIABLE)\n for (const name of this.appliedTokens) body.style.removeProperty(name)\n this.appliedTokens = []\n this.themeColorMeta.remove()\n }\n}\n","/**\n * 磁贴墙排布算法(Mosaic)—— 集成中心三个内容页(插件 / MCP / 技能)共用的\n * 「错落有致」块生成器。**这是排布算法的单一事实源**:三个页面不再各持一份副本\n * (副本分叉过一次,见文件末「为什么抽出来」)。\n *\n * ## 几何(design.pen 四帧逐帧读实)\n *\n * 设计稿的磁贴墙**不是 CSS grid**,而是「若干等宽 808 的块」上下堆叠;每块内部\n * 一行 flex、各列等高。块只有两种高度:\n *\n * - **高 264 的块**:四列,宽度是 `[264,264,128,128]` 的某个排列。\n * 264 宽列装「一张大贴」或「两条宽贴竖叠」;128 宽列装「一张高贴」或\n * 「两条小贴竖叠」。宽度校验:264+8+264+8+128+8+128 = **808** ✓\n * - **高 128 的块**:列宽模式三选一,均为 808:\n * `[128×6]` = 768+40 / `[264,128×4]` = 776+32 / `[264,264,128×2]` = 784+24。\n *\n * 列宽 128 与「两条小贴竖叠」的关系:128+8+128 = 264,故 128 宽的列与 264 宽的\n * 列在纵向能装下同样多的贴,两种块的列高都自洽。\n *\n * ## 为什么是「固定种子伪随机」而不是逐块手写\n *\n * 设计稿每帧只画两行,谈不上覆盖真实数据量(插件市场可能几十条)。排布若按\n * 固定序号循环,每块会排得一模一样——用户反馈「当前这种每一列都一种排列方式\n * 是不好看的」。故逐块抽型:**种子为常量 ⇒ 同一份数据每次渲染完全一致**(可\n * 复现、可对账),同时块与块之间形态不同,呈现错落。\n *\n * 曾经的做法是 `display:grid; grid-auto-flow:dense` + 固定序号循环,实测空洞率\n * 6.3%(大贴跨两行时自动放置填不满)且每块同形;改块生成器后整块恰好铺满。\n *\n * @module corum-ui-base/client/mosaic\n */\n\n/** 磁贴尺寸档(与 design.pen 的 264×264 / 264×128 / 128×264 / 128×128 对应)。 */\nexport type MosaicSize = 'big' | 'wide' | 'tall' | 'small'\n\n/** 块高度档:264 块装大/高贴,128 块装宽/小贴。 */\nexport type MosaicBlockKind = '264' | '128'\n\n/**\n * 列数模式:6 = 设计稿的细粒度排布(128 基准);3 = 窄窗降列(264 基准)。\n *\n * 两种模式的块宽**恒等**,故单位宽公式无需分叉:\n * 6 列行 = 2×(2u+g) + 2×u + 3g = 6u+5g\n * 3 列行 = 3×(2u+g) + 2g = 6u+5g\n * 降列的意义是把同一容器宽摊到更少的列上——实测 546px 容器下 6 列的单位宽只有\n * 83px(名称放不下),降成 3 列后每列 174px,名称与作者都能正常显示。\n */\nexport type MosaicColumns = 6 | 3\n\n/**\n * 固定种子:**换掉它就换一整套图案**,同一份数据下恒定。\n * 取值本身无深意(任意常量),只承担「可复现」这一个职责。\n */\nexport const MOSAIC_SEED = 0x5a17\n\n/**\n * 相邻块同型时的重抽上限。几何上两型等概率,5 次仍同型就直接取相\n * 反型——保证「相邻不同型」这条硬约束不会因随机而破。\n */\nconst BLOCK_RETYPE_MAX = 5\n\n/**\n * 块内一张贴的槽位:尺寸档 + 它承载哪一条数据。\n *\n * `itemIndex` 由算法显式给出,而不是「按顺序数下去」:把长名字安排到 264 宽的\n * 槽需要**跨位置挑选数据**,落位不再是「第 i 张贴 = 第 i 条数据」,故每张槽位都\n * 记下自己的数据下标({@link MosaicWall} 按它取数据)。\n */\nexport interface MosaicSlot {\n readonly size: MosaicSize\n /** 承载的数据下标;形状计划完成后由算法填写(长名字会与短名字成对交换)。 */\n itemIndex: number\n}\n\n/** 块内一列:宽度档 + 该列自上而下的贴槽位。 */\nexport interface MosaicCol {\n /** 列宽(px,未乘密度缩放):264 或 128。 */\n readonly width: 264 | 128\n /** 该列的贴尺寸序列(自上而下 1~2 张);分配数据后为可变数组。 */\n readonly sizes: MosaicSize[]\n /** 分配数据后填入的槽位(与 {@link sizes} 同长同序)。 */\n slots?: MosaicSlot[]\n}\n\n/** 一个块行:高度档 + 列序列。 */\nexport interface MosaicBlock {\n readonly kind: MosaicBlockKind\n readonly cols: readonly MosaicCol[]\n}\n\n/** mulberry32:小而稳定的 32 位 PRNG(纯函数式状态推进,无外部依赖)。 */\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0\n return () => {\n a = (a + 0x6d2b79f5) >>> 0\n let t = a\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** Fisher–Yates 洗牌(消费同一 PRNG,保证整条序列可复现)。 */\nfunction shuffled<T>(rng: () => number, items: readonly T[]): T[] {\n const out = [...items]\n for (let i = out.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[out[i], out[j]] = [out[j]!, out[i]!]\n }\n return out\n}\n\n\n/**\n * 名字档:算法用它在槽位之间做取舍(长名字优先给 264 宽的列)。\n */\n/** 一条数据的排布提示。 */\nexport interface MosaicItemHint {\n /** 展示名的字符数(调用方按**实际用于渲染**的那个名字算)。 */\n readonly nameLength: number\n /** 是否有描述(有描述的长名才值得给大贴,换来额外高度放描述)。 */\n readonly hasDescription?: boolean\n}\n\n/** 名字「偏长」的阈值:达到即优先安排进 264 宽的列(宽贴或大贴)。 */\nconst LONG_NAME_LENGTH = 18\n\n/** 名字「很长」的阈值:配合描述可给大贴(264×264),拿到额外高度显示描述。 */\nconst VERY_LONG_NAME_LENGTH = 22\n\n/** 6 列模式的块几何:四列宽 `[264,264,128,128]` 的某个排列(6u+5g)。 */\nconst WIDTHS_6COL = [264, 264, 128, 128] as const\n\n/**\n * 3 列模式的块几何:三列固定 264 宽(3×(2u+g)+2g = 6u+5g,与 6 列块等宽)。\n * 窄窗降列时用——同一容器宽摊到 3 列上,每列宽约翻倍,长名字才放得下。\n */\nconst WIDTHS_3COL = [264, 264, 264] as const\n\n/**\n * 3 列模式下的第二种列宽模式:`[128,128,264,264]`(1+1+2+2 单位)。\n *\n * 为什么 3 列模式还需要它:**`tall`(竖直矩形 128×264)只能长在 128 宽的列上**,\n * 而 3 列的唯一合法宽度组合是 `264+264+264`(数学上 2+2+2 是 `6u+5g` 的唯一解)\n * —— 全是 264 宽的列 ⇒ 3 列下结构上生不出竖直矩形(实测形状分布只有 big/wide)。\n * 引入这个 4 列模式后,3 列模式也能排出「竖直矩形 + 大正方形」并存的形态。\n * 宽度校验:128+8+128+8+264+8+264 = 808 = 6u+5g ✓(与 3 列块等宽)\n */\nconst WIDTHS_3COL_TALL = [128, 128, 264, 264] as const\n\n/**\n * 一个块内的**形状配额**:每种形状各出几张。\n *\n * 这是「美观」的第二维——原来只按名字长度决定宽窄(长名给 264 列),结果 264 列\n * 一律退化成「两条宽贴」,整片墙只剩长方形、大正方形消失(实测 wide:17 / big:1)。\n * 现在改为**先给每个块定一份形状配方**(大正方形几张、竖直矩形几张、宽贴几张、\n * 小贴几张),再由位置去消费配额:长名字仍然优先占宽槽,但**形状本身不再由名字\n * 长度决定**——短名字也会轮到正方形与竖直矩形。\n */\nexport interface BlockShapePlan {\n /** 大贴(264×264,正方形)。 */\n readonly big: number\n /** 宽贴(264×128,横向长方形)。 */\n readonly wide: number\n /** 高贴(128×264,竖直矩形)。 */\n readonly tall: number\n /** 小贴(128×128)。 */\n readonly small: number\n}\n\n/**\n * 为第 `blockIndex` 个块生成形状配方。\n *\n * 设计意图(用户 2026-10-01:「不要只按照名字长度来进行,短名字也可以有大正方形、\n * 竖直的矩形,要增加一个维度,适当调节卡片的排列看上去更加有美感」):\n * - **每块至少一张大正方形**(264 块的锚点),保证无论数据怎么排都有正方形;\n * - **每个 264 块至少一张竖直矩形**(128×264),让「竖着的长方形」稳定出现;\n * - 其余槽位按一个**轮转配方表**分配,使相邻块的形状组合不同(错落);\n * - 配方按块序号轮转(不是随机),故同一份数据每次渲染一致、可复现对账。\n *\n * @param blockIndex - 第几个块(0 起)。\n * @returns 该块的形状配额。\n */\nexport function planBlockShape(blockIndex: number, rng?: () => number): BlockShapePlan {\n /* 有 rng 时**逐块随机配方**(2026-10-24 用户要「排列无规律」):不再按\n blockIndex 轮转固定四配方——那会让整墙呈现可辨认的重复节奏。\n 约束:至少 1 张大贴(视觉锚点);其余配额随机,骨架消费不完就忽略。 */\n if (rng !== undefined) {\n /* 关键:**大贴不再每块都有**(2026-10-24 用户报「必然有一排正方形」)。\n 旧配方 big 恒 ≥1 ⇒ 每个 264 块必出正方形,整墙出现固定节奏。\n 现在 35% 概率给 0 张大贴;此时至少给 1 张高贴,满足骨架的\n 「非全竖叠」约束(否则 buildColSkeleton 会重抽并退回设计稿首帧,\n 反而又把正方形带回来)。 */\n const big = rng() < 0.65 ? (rng() < 0.2 ? 2 : 1) : 0\n const tall = rng() < 0.55 ? (rng() < 0.5 ? 1 : 2) : 0\n return {\n big,\n wide: 1 + Math.floor(rng() * 3),\n tall: big === 0 && tall === 0 ? 1 : tall,\n small: Math.floor(rng() * 2),\n }\n }\n /* 无 rng 的旧路径:轮转配方(保留给确定性调用/测试)。 */\n const RECIPES: readonly BlockShapePlan[] = [\n { big: 1, wide: 1, tall: 1, small: 0 },\n { big: 1, wide: 2, tall: 1, small: 1 },\n { big: 1, wide: 1, tall: 1, small: 0 },\n { big: 1, wide: 1, tall: 1, small: 1 },\n ]\n return RECIPES[blockIndex % RECIPES.length]!\n}\n\n/**\n * 生成一个高 264 的块的**列骨架**(只决定列宽排列与每列的贴数,不含数据)。\n *\n * 264 宽列可装「一张大贴」或「两条宽贴竖叠」;128 宽列可装「一张高贴」或\n * 「两条小贴竖叠」。约束:至少一张大贴(视觉锚点)、264 列不得全为双宽、\n * 128 列不得全为双小,违反重抽;耗尽后退到设计稿首帧排法。\n *\n * @param rng - 该次渲染的 PRNG。\n * @param mode - 列数模式(6 或 3)。\n * @param pinFirstTwoSmalls - 首列钉成「两条小贴竖叠」:集成中心的入口贴\n * (添加服务器 / 添加技能等)固定落在左上角第一张,故需要这枚钉子。\n */\nexport function buildColSkeleton(\n rng: () => number,\n mode: MosaicColumns = 6,\n pinFirstTwoSmalls = false,\n shape: BlockShapePlan = { big: 0, wide: 0, tall: 0, small: 0 },\n): MosaicCol[] {\n /* 3 列模式用 `[128,128,264,264]`(4 列、6 单位)——它是 3 列模式里唯一**同时**\n 能长出 `tall`(竖直矩形,需 128 列)与容纳入口小贴的形态;纯 `[264,264,264]`\n 三列只能出 big/wide,且首个槽位放不下小贴(264 列最小单元就是一张宽贴)。\n 6 列模式用 `WIDTHS_6COL`。两者块宽恒等(6 单位 + 5 间隙)。 */\n const base = mode === 3 ? WIDTHS_3COL_TALL : WIDTHS_6COL\n for (let attempt = 0; attempt < BLOCK_RETYPE_MAX; attempt++) {\n const widths = [...shuffled(rng, base)]\n /* 入口贴钉在左上角。**必须换「同单位」的列,不能直接改首列宽**:\n 块宽恒等式是「单位数 = 6」(128 列 = 1 单位、264 列 = 2 单位)—— 把 2 单位的\n 首列改成 1 单位,整块就少 1 单位(实测钉后列宽 [128,128,128,264] 只剩 672、\n 应为 808);给 3 列硬塞一个 128 列则会多出列数(实测 [264,264,264,128] 涨到\n 944)。故这里**只对调、不改值**,把首列换成同模式里已存在的 128 列。 */\n if (pinFirstTwoSmalls) {\n const j = widths.findIndex(w => w === 128)\n if (j > 0) { const t = widths[0]!; widths[0] = widths[j]!; widths[j] = t }\n }\n // 按配额消耗形状:264 列先拿 big(每块至少一张),再 wide;128 列在 tall / small 间交替。\n let bigLeft = shape.big\n let wideLeft = shape.wide\n let tallLeft = shape.tall\n let smallLeft = shape.small\n let hasBig = false\n let hasNonDoubled = false\n const cols: MosaicCol[] = widths.map((w, i) => {\n if (w === 264) {\n /* 264 列可装「一张大贴」(占 1 张、正方形)或「两条宽贴」(占 2 张)。\n 先满足大贴配额(它是视觉锚点),配额用尽再出双宽。 */\n if (bigLeft > 0) {\n bigLeft -= 1\n hasBig = true\n hasNonDoubled = true\n return { width: 264, sizes: ['big'] }\n }\n wideLeft -= 1\n return { width: 264, sizes: ['wide', 'wide'] }\n }\n /* 128 列可装「一张高贴」(竖直矩形)或「两条小贴」。 */\n if (tallLeft > 0) {\n tallLeft -= 1\n hasNonDoubled = true\n return { width: 128, sizes: ['tall'] }\n }\n smallLeft -= 1\n return { width: 128, sizes: ['small', 'small'] }\n })\n /* 约束放宽后的判据:整块至少要有一个「大贴」或「高贴」这类**非竖叠**形状,\n 否则整块全是成对的贴、没有任何形态变化(早期只查 hasBig,导致宽窗下\n 大贴被 preferWide 挤掉后整片都是长方形)。 */\n if (hasNonDoubled || hasBig) return cols\n if (pinFirstTwoSmalls && mode === 6 && cols.length > 0) {\n cols[0] = { width: 128, sizes: ['small', 'small'] }\n }\n }\n // 重抽耗尽:退到设计稿首帧的排法(大 | 双宽 | 高 | 双小),保证仍有大正方形。\n return mode === 3\n ? [\n { width: 264, sizes: ['big'] },\n { width: 264, sizes: ['wide', 'wide'] },\n { width: 264, sizes: ['tall'] },\n ]\n : [\n { width: 264, sizes: ['big'] },\n { width: 264, sizes: ['wide', 'wide'] },\n { width: 128, sizes: ['tall'] },\n { width: 128, sizes: ['small', 'small'] },\n ]\n}\n\n/**\n * 高 128 的块骨架:三种列宽模式(均 6u+5g)三选一,再洗牌 264 宽列的位置。\n * 3 列模式下退化为三张宽贴(3×(2u+g)+2g),与该模式其它块等宽。\n */\nexport function buildColSkeleton128(rng: () => number, mode: MosaicColumns = 6): MosaicCol[] {\n if (mode === 3) {\n return shuffled(rng, WIDTHS_3COL.map(() => ({ width: 264 as const, sizes: ['wide'] as MosaicSize[] })))\n }\n const roll = rng()\n const small = (): MosaicCol => ({ width: 128, sizes: ['small'] })\n const wide = (): MosaicCol => ({ width: 264, sizes: ['wide'] })\n const base: MosaicCol[] = roll < 1 / 3\n ? Array.from({ length: 6 }, small)\n : roll < 2 / 3\n ? [wide(), small(), small(), small(), small()]\n : [wide(), wide(), small(), small()]\n return shuffled(rng, base)\n}\n\n/** 取一个块的贴容量。 */\nfunction blockCapacity(cols: readonly MosaicCol[]): number {\n return cols.reduce((n, c) => n + c.sizes.length, 0)\n}\n\n/** 按列顺序保留能铺满的列前缀(不留空占位)。 */\nfunction trimCols(cols: readonly MosaicCol[], room: number): MosaicCol[] {\n const kept: MosaicCol[] = []\n let used = 0\n for (const col of cols) {\n if (used + col.sizes.length > room) break\n kept.push(col)\n used += col.sizes.length\n }\n return kept\n}\n\n/**\n * 排布选项。\n */\nexport interface MosaicOptions {\n /**\n * 首块首列钉成「两条小贴竖叠」:集成中心三个页面的入口贴(添加服务器 /\n * 添加技能 / 安装本地包)都固定落在左上角第一张,故需要这枚钉子。\n */\n readonly pinFirstTwoSmalls?: boolean\n /**\n * 列数模式:6(默认,设计稿的细粒度排布)或 3(窄窗降列)。\n * 调用方按容器宽选择——见 {@link pickMosaicColumns}。\n */\n readonly columns?: MosaicColumns\n /**\n * 每条数据的排布提示(与 `items` 同序)。给了它,算法就会把**名字长的**安排到\n * 264 宽的列(宽贴/大贴)上,而不是随机落进 128 宽的窄贴被截断。\n * 省略则退回纯随机(与旧行为一致)。\n */\n readonly hints?: readonly MosaicItemHint[]\n /**\n * 排布随机种子(2026-10-24)。省略 = 固定 `MOSAIC_SEED`(确定性,测试/旧行为);\n * 页面传入每次挂载生成的随机种子 ⇒ 同一批数据每次打开布局不同,且块内形状\n * 逐块随机(不再按 blockIndex 轮转固定配方)。\n */\n readonly seed?: number\n}\n\n/**\n * 按容器宽选列数:够宽用 6 列(设计稿形态),窄到单位宽撑不住长名字时降为 3 列。\n *\n * 判据是**单位宽**而不是容器宽本身:6 列下单位宽 = (容器宽 − 5×gap)/6,低于\n * {@link MIN_UNIT_FOR_6COL} 时长名字在 128 列里必然截断,此时降列比缩字更可读。\n *\n * @param containerWidth - 磁贴群的内容宽(px)。\n * @param gap - 列间隙(px)。\n * @param density - 界面密度缩放(默认 1)。\n */\nexport const MIN_UNIT_FOR_6COL = 96\n\nexport function pickMosaicColumns(containerWidth: number, gap = 8, density = 1): MosaicColumns {\n const g = gap * density\n const unit = (containerWidth - 5 * g) / 6\n return unit < MIN_UNIT_FOR_6COL * density ? 3 : 6\n}\n\n/**\n * 把 `items` 排进块序列。\n *\n * ## 长名字优先给宽槽\n * 算法**先在整条数据里**按名字长度挑出「长名」,再按槽位顺序把它们填进 264 宽的\n * 槽(大贴 / 宽贴);短名字补剩下的 128 宽槽(小贴 / 高贴)。这样长名字拿到\n * 264px 宽(窄窗降 3 列后是 ~174px 起),不再被 128px 的窄贴截成「corum…」。\n *\n * ## 为什么返回 itemIndex 而不是「按顺序数下去」\n * 长名字要跨位置挑选,落位不再是「第 i 张贴 = 第 i 条数据」;块内每张槽位都显式\n * 记下它承载的数据下标,调用方按序渲染即可(见 {@link MosaicSlot})。\n *\n * ## 其它约束\n * - 相邻块不同型(同型重抽,上限 {@link BLOCK_RETYPE_MAX});\n * - 整块放得下就整块收,放不下则列级裁剪收尾(末块允许窄于整宽,属裁剪形态);\n * - **裁剪后仍继续消费剩余数据**——早期实现此处直接退出循环,导致剩余贴被静默\n * 丢弃(实测 2 张贴只渲染出 1 张,即少显示一个条目)。\n *\n * @param items - 数据条数,或与 `hints` 等长的数据数组(只读其 `length`)。\n * @param options - 见 {@link MosaicOptions}。\n * @returns 块序列;块内槽位带 `size` 与 `itemIndex`,调用方按序遍历取数据渲染。\n */\nexport function buildMosaic(\n items: number | readonly unknown[],\n options: MosaicOptions = {},\n): MosaicBlock[] {\n const count = typeof items === 'number' ? items : items.length\n if (count <= 0) return []\n const mode = options.columns ?? 6\n const rng = mulberry32(options.seed ?? MOSAIC_SEED)\n const blocks: MosaicBlock[] = []\n\n /* ── 数据分配:长名字优先占 264 宽的槽 ───────────────────────────────────\n 入口贴(下标 0,通常是「添加」)**不进长名队列**:它固定占整个墙的第一个槽位\n (左上角)。若把它混进队列,长名字会排到它前面、把它挤到墙中间\n (实测传 hints 时「左上角是入口贴」的命中率只有 3/118)。 */\n const hints = options.hints ?? []\n const entryPinned = options.pinFirstTwoSmalls === true && count >= 2\n const longFirst: number[] = []\n const shortRest: number[] = []\n for (let i = entryPinned ? 1 : 0; i < count; i++) {\n if ((hints[i]?.nameLength ?? 0) >= LONG_NAME_LENGTH) longFirst.push(i)\n else shortRest.push(i)\n }\n // 长名内部:更长 + 有描述者优先(大贴要给描述留高度)。\n longFirst.sort((a, b) => {\n const la = hints[a]?.nameLength ?? 0\n const lb = hints[b]?.nameLength ?? 0\n if (lb !== la) return lb - la\n const da = hints[a]?.hasDescription === true ? 1 : 0\n const db = hints[b]?.hasDescription === true ? 1 : 0\n return db - da\n })\n /** 数据分配顺序:入口贴在队首(若被钉),其后是「长名(最长在前)→ 其余」。 */\n const order = entryPinned ? [0, ...longFirst, ...shortRest] : [...longFirst, ...shortRest]\n let cursor = 0\n const take = (): number => order[cursor++] ?? 0\n\n /* ── 逐块生成 ─────────────────────────────────────────────────────────────\n 顺序至关重要:**先按剩余额度裁剪列骨架,再给留下的槽位分配数据**。\n 反过来(先给整块分配、再裁剪)会让被裁掉的那些列已经 `take()` 走数据下标,\n 而 cursor 已越过它们 ⇒ 那些数据被静默丢弃(实测 n=12 只产出 6 个槽位、\n n=6 只产出 1 个)。这是本算法长期存在的缺陷,与「末块裁剪」无关。\n\n 形状**不按名字长度决定**:每块先用 {@link planBlockShape} 取一份形状配方\n (大正方形 / 竖直矩形 / 宽贴 / 小贴各几张),再由位置消费配额 —— 于是短名字\n 也会轮到正方形与竖直矩形。名字长度只影响「谁先占宽槽」(数据分配顺序),\n 不再决定形状本身。 */\n const buildBlock = (kind: MosaicBlockKind, pin: boolean, room: number, blockIndex: number): MosaicCol[] => {\n const skeleton = kind === '264'\n ? buildColSkeleton(rng, mode, pin, planBlockShape(blockIndex, rng))\n : buildColSkeleton128(rng, mode)\n const kept = trimCols(skeleton, room)\n /* 入口贴先钉进**视觉首列的第一个槽**(左上角)。不能只依赖宽度排序:列是按\n 宽度降序取数的(为了让长名字进 264 列),那样数据 0 会落到最宽的列、而不是\n 最靠前的列(实测命中率只有 203/400)。 */\n let firstColPlaced = false\n if (pin && kept.length > 0 && kept[0]!.sizes.length > 0) {\n const first = kept[0]!\n first.slots = [{ size: first.sizes[0]!, itemIndex: take() }]\n firstColPlaced = true\n }\n // 其余槽位:264 宽的列先填(长名字优先),128 宽的列后填。\n const ordered = [...kept].sort((a, b) => b.width - a.width)\n for (const col of ordered) {\n const start = firstColPlaced && col === kept[0] ? 1 : 0\n const rest = col.sizes.slice(start).map(size => ({ size, itemIndex: take() }))\n col.slots = col.slots === undefined ? rest : [...col.slots, ...rest]\n }\n return kept\n }\n\n /** 收一个块:列已按额度裁好,此处只做登记。 */\n const emit = (block: MosaicBlock): boolean => {\n if (block.cols.length === 0) return false\n blocks.push(block)\n return true\n }\n\n if (options.pinFirstTwoSmalls === true && count >= 2) {\n const cols = buildBlock('264', true, count, 0)\n emit({ kind: '264', cols })\n } else if (count === 1) {\n const itemIndex = take()\n emit({ kind: '128', cols: [{ width: 128, sizes: ['small'], slots: [{ size: 'small', itemIndex }] }] })\n }\n\n while (cursor < count) {\n /* 块类型(= 行高:264/128)。**不再强制相邻不同**(2026-10-24 用户报\n 「高度都一样 / 每排都有正方形」):只有两种 kind 时「相邻必须不同」\n 等价于**严格交替**,整墙呈现固定节奏。现在 60% 概率延续上一行的\n kind,形成 2~4 行的同高连续段,再随机换高。 */\n const last = blocks.length === 0 ? null : blocks[blocks.length - 1]!.kind\n let kind: MosaicBlockKind\n if (last === null) {\n kind = rng() < 0.5 ? '264' : '128'\n } else {\n kind = rng() < 0.6 ? last : (last === '264' ? '128' : '264')\n }\n const block = { kind, cols: buildBlock(kind, false, count - cursor, blocks.length) }\n if (emit(block)) continue\n // 连一列都放不下(剩余额度小于任一列的张数):退化为单张贴窄块收尾。\n const tailKind: MosaicBlockKind = last === '264' ? '128' : '264'\n const itemIndex = take()\n const tailSize: MosaicSize = tailKind === '264' ? 'tall' : 'small'\n emit({\n kind: tailKind,\n cols: [{ width: 128, sizes: [tailSize], slots: [{ size: tailSize, itemIndex }] }],\n })\n }\n return blocks\n}\n\n/**\n * 块序列 → 扁平贴序列(按「块 → 列 → 列内自上而下」的渲染顺序),\n * 只返回尺寸档,数据由调用方按同序提供。\n *\n * 走 `buildMosaic` 的 `itemIndex` 时会重排数据,故大多数调用方应直接用\n * {@link MosaicWall}(它按 `itemIndex` 取数据);本函数留给「数据顺序与槽位\n * 顺序一致」的简单场景。\n */\nexport function flattenMosaic(blocks: readonly MosaicBlock[]): MosaicSize[] {\n const out: MosaicSize[] = []\n for (const block of blocks) {\n for (const col of block.cols) {\n for (const slot of col.slots ?? []) out.push(slot.size)\n }\n }\n return out\n}\n","/*\n * 磁贴墙排布样式(Mosaic)—— 集成中心三个内容页(插件 / MCP / 技能)共用。\n * 与 `mosaic.ts` 是一对:那个决定「哪一块哪一列放哪一档尺寸」,这里负责把\n * 几何真正落到像素上。**这是排布样式的单一事实源**(三个页面不再各持副本)。\n *\n * 几何口径(design.pen 四帧逐帧读实):\n * 块内容宽 = 808(= 磁贴群宽):264+8+264+8+128+8+128,也是 128×6+8×5。\n * 块高两档:264 块 / 128 块。\n * 列宽两档:264 / 128;列内贴纵向排、gap 8。\n *\n * 尺寸档靠 `data-tile-size` 属性选择器(不是类名):尺寸与块型是同一份算法的\n * 两个输出,属性让 DOM 自带可断言口径(验收时直接读 data 属性)。\n *\n * 密度缩放(--corum-density-scale)与字号缩放(--corum-ui-font-scale)全仓统一;\n * 磁贴内的字号乘字号缩放,间距/圆角/尺寸乘密度缩放。\n */\n\n/* ── 磁贴群:块行纵向堆叠 ─────────────────────────────────────────────────── */\n\n.tileGrid {\n /* 响应式流体基准(本元素的 inline-size 作为查询容器,故 `100cqw` = 本元素内容宽)。\n 整套排布是**正方形单位网格**:264 = 2×128 + 8,故一个单位宽算出来后,列宽与\n 块高都能精确表达(见下)。\n\n 单位宽**只需一个算式**,与「几列」无关:\n 6 列块 [2,2,1,1] 单位 = 2×(2u+g) + 2×u + 3g = 6u+5g\n 3 列块 [2,2,2] 单位 = 3×(2u+g) + 2g = 6u+5g\n 4 列块 [1,1,2,2] 单位 = u+u+(2u+g)+(2u+g)+3g = 6u+5g\n 三种列数的块宽恒等 ⇒ u = (W − 5g)/6 一套通吃;窄窗时 u 随之缩小、宽窗封顶在\n 稿里的 128 ⇒ 宽窗下与设计稿逐像素一致。\n ⚠️ 曾误加过一条「3 列用 (W−2g)/3」的式子:那是把**列宽**当成了单位宽,\n 会让 264 列变成 356 宽、三列放不下(实测块内容溢出到 944 > 538)。 */\n container-type: inline-size;\n --mosaic-gap: calc(8px * var(--corum-density-scale, 1));\n --mosaic-unit: min(\n calc(128px * var(--corum-density-scale, 1)),\n calc((100cqw - 5 * var(--mosaic-gap)) / 6)\n );\n\n display: flex;\n flex-direction: column;\n gap: var(--mosaic-gap);\n align-content: start;\n min-width: 0;\n}\n\n/* ── 块行:横向 flex,各列等高 ─────────────────────────────────────────────\n `width: 100%` 是必需的:列宽走单位宽(见下),若本行不撑满容器,列会按\n 自身宽度收缩、整块右侧留白;早期副本漏了这条,列被 `flex: N 1 0` 拉到\n 808 宽(贴被拉成整行)——列宽改为单位宽 + 本行撑满后不再互相争抢。 */\n.mosaicBlock {\n display: flex;\n align-items: stretch;\n gap: var(--mosaic-gap);\n width: 100%;\n min-width: 0;\n}\n\n/* ── 块高 ─────────────────────────────────────────────────────────────────\n 几何上:264 块 = 2 单位 + 1 间隙(与 264 = 2×128+8 同构);128 块 = 1 单位。\n 宽窗(单位封顶 128)下就是设计稿的 264 / 128,逐像素一致。\n\n 但**贴的内容有最小高度需求**(图标 + 名称 + 作者 ≈ 96px),而窄窗降列后单位宽\n 掉到 80px 出头 ⇒ 264 块里的「两条竖叠贴」各只有 83px、128 块只有 83px,内容差\n 1px 放不下(实测 3 列下 16 张贴全被裁)。故给两种块各加**可读性保底**:\n - 128 块:≥ 一张贴的最小内容高(96);\n - 264 块:≥ 两条竖叠贴各 96 + 间隙(96×2 + 8 = 200)。\n 宽窗下单位推导值更大、保底不生效,设计稿形态不变;窄窗下块略高于正方形单位\n (代价是磁贴不再严格正方形),换来内容不被裁。 */\n.mosaicBlock[data-mosaic-block='264'] {\n height: max(\n calc(2 * var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) + var(--mosaic-gap)),\n calc(200px * var(--corum-density-scale, 1))\n );\n}\n.mosaicBlock[data-mosaic-block='128'] {\n height: max(\n var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))),\n calc(96px * var(--corum-density-scale, 1))\n );\n}\n\n/* ── 列:纵向 flex;宽度由 data-col 决定 ───────────────────────────────────\n 列宽必须是**单位宽的整数倍**而不是 flex 比例:264 列的语义是「能装一张大贴\n 或两条宽贴」,若用 flex-grow 分配,窗口越宽列越宽、贴被拉成条状(不再是磁贴)。\n 128 列 = 1 单位,264 列 = 2 单位 + 1 间隙(128+8+128 = 264,纵向容量相同)。 */\n.mosaicCol {\n display: flex;\n flex-direction: column;\n gap: var(--mosaic-gap);\n min-width: 0;\n min-height: 0;\n flex: none;\n width: var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1)));\n}\n\n.mosaicCol[data-col='264'] {\n width: calc(2 * var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) + var(--mosaic-gap));\n}\n\n/* 列内贴:撑满列宽;纵向按尺寸档分配列高。 */\n.mosaicCol > * {\n flex: 1 1 0;\n min-height: 0;\n min-width: 0;\n}\n\n/* 大贴 / 高贴独占整列(`auto` 基准让它们按内容与列高取大者,实际等同撑满)。 */\n.mosaicCol > [data-tile-size='big'],\n.mosaicCol > [data-tile-size='tall'] { flex: 1 1 auto; }\n\n/* ── 磁贴本体 ───────────────────────────────────────────────────────────── */\n\n.tile {\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n overflow: hidden;\n box-sizing: border-box;\n /* 内边距随单位宽一起收(稿里 12px 是按 128 基准定的):缩到 83px 时那 24px\n 上下内边距会占掉近三成高度,是底部被裁的主因。宽窗封顶 12、窄窗压到 8。 */\n padding: clamp(\n calc(8px * var(--corum-density-scale, 1)),\n calc(var(--mosaic-unit, calc(128px * var(--corum-density-scale, 1))) * 0.09),\n calc(12px * var(--corum-density-scale, 1))\n );\n border: 1px solid var(--corum-glass-border);\n border-radius: 0;\n background: var(--tile-tint, var(--corum-glass-1));\n text-align: left;\n font-family: inherit;\n cursor: pointer;\n transition: border-color .15s, background .15s;\n}\n\n.tile:hover { border-color: var(--corum-glass-border-active); }\n.tile:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }\n\n/* 四档语义底色(design.pen 磁贴 fill,写死 hex 的四色)。带 alpha ⇒ 叠浅色底\n 自然变浅、叠深色底保持霓虹,深浅共用一组值,无需主题分叉。 */\n.tileTintViolet { --tile-tint: var(--corum-tile-violet, var(--corum-glass-1)); }\n.tileTintDeep { --tile-tint: var(--corum-tile-deep, var(--corum-glass-1)); }\n.tileTintSlate { --tile-tint: var(--corum-tile-slate, var(--corum-glass-1)); }\n.tileTintMauve { --tile-tint: var(--corum-tile-mauve, var(--corum-glass-1)); }\n.tileTintBlue { --tile-tint: var(--corum-tile-blue, var(--corum-glass-1)); }\n.tileTintCyan { --tile-tint: var(--corum-tile-cyan, var(--corum-glass-1)); }\n.tileTintEmerald { --tile-tint: var(--corum-tile-emerald, var(--corum-glass-1)); }\n.tileTintAmber { --tile-tint: var(--corum-tile-amber, var(--corum-glass-1)); }\n.tileTintRose { --tile-tint: var(--corum-tile-rose, var(--corum-glass-1)); }\n\n/* 选中态:抬高一层玻璃 + 激活描边。 */\n.tileActive {\n background: var(--corum-glass-2);\n border-color: var(--corum-glass-border-active);\n}\n\n/* 大贴的品牌色 glow(design.pen 大贴内的 radial ellipse)。 */\n.tile[data-tile-size='big']::before,\n.tileGlow::before {\n content: '';\n position: absolute;\n inset: 0;\n pointer-events: none;\n background: radial-gradient(circle at 78% 22%,\n color-mix(in srgb, var(--dsw-alias-brand-primary) 36%, transparent) 0%,\n transparent 62%);\n}\n\n/* ── 磁贴内部分区 ───────────────────────────────────────────────────────── */\n\n.tileTop {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: calc(6px * var(--corum-density-scale, 1));\n}\n\n.tileIcon {\n flex: none;\n display: inline-flex;\n color: var(--dsw-alias-brand-text);\n}\n\n/* 角标(热度 / 状态点 / 开关)绝对定位于右上角,不参与文档流 ⇒ 不挤压图标。 */\n.tileCorner {\n position: absolute;\n top: calc(10px * var(--corum-density-scale, 1));\n right: calc(10px * var(--corum-density-scale, 1));\n display: inline-flex;\n align-items: center;\n gap: calc(3px * var(--corum-density-scale, 1));\n}\n\n.tileBottom {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: calc(3px * var(--corum-density-scale, 1));\n min-width: 0;\n}\n\n/* 名称与版本徽章**纵向**排(不是并排):并排时窄贴(128)里名称会被徽章挤到\n 只剩一两个字(实测 12/18 张被截断、极端情况只剩「d.」)。纵向后名称独享\n 整行宽、可换行到两行,徽章另起一行。 */\n.tileNameRow {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: calc(4px * var(--corum-density-scale, 1));\n min-width: 0;\n width: 100%;\n}\n\n.tileName {\n width: 100%;\n min-width: 0;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 3;\n overflow: hidden;\n overflow-wrap: anywhere;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n font-size: calc(13px * var(--corum-ui-font-scale, 1));\n line-height: 1.3;\n /* 固定三行行高:内容长短不一(占位贴只有「添加」)时磁贴内文字块不跳动。\n 简介移出卡片后这里由两行放宽到三行 —— 腾出的空间给名称。 */\n min-height: calc(3 * 1.3em);\n}\n\n.tile[data-tile-size='big'] .tileName { font-size: calc(15px * var(--corum-ui-font-scale, 1)); }\n\n.tileVersion {\n flex: none;\n display: inline-flex;\n align-items: center;\n height: calc(16px * var(--corum-density-scale, 1));\n padding: 0 calc(6px * var(--corum-density-scale, 1));\n border: 1px solid var(--corum-glass-border);\n border-radius: 999px;\n background: var(--corum-glass-3);\n color: var(--dsw-alias-label-tertiary);\n font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: calc(9px * var(--corum-ui-font-scale, 1));\n font-weight: 600;\n white-space: nowrap;\n width: fit-content;\n}\n\n.tileSub {\n font-size: calc(10px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-tertiary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ── 按形状分档的贴内布局 ─────────────────────────────────────────────────\n 同一个 `MosaicTileBody` 骨架,四种形状各自换**信息组织方式**(不只是调字号):\n\n big 264×264 正方形:图标居左上 → 名称(最多 3 行)→ 作者\n wide 264×128 宽扁: **图文并排**(图标左、文字右)→ 名称(2 行)→ 作者\n tall 128×264 窄高: 图标居上 → 名称(3 行)→ 作者沉底\n small 128×128 最小: 图标 + 名称(2 行)\n\n ⚠️ **卡片上不渲染简介**(用户 2026-10-02 定调:「我希望将简介放在详细信息里去」)。\n 简介只在右侧详情面板呈现(三页的 `.detailDesc` 已展示全文)。理由有二:\n 1. 卡片面积有限,简介在窄贴里只能被钳成两三行残句,信息价值低;\n 2. 原先靠「@container 按贴高隐藏简介」实现分档,而贴高随窗口在阈值附近来回跨越\n ⇒ **同一张卡片在不同窗口尺寸下简介时而出现时而消失**(用户实测报障\n 「缩小窗口后小卡片反而有简介,放大全屏反而没有」)。移除后这类分支不复存在。\n 腾出的纵向空间让给名称(行数 2 → 3)与图标(略放大),卡片不显得空。\n\n 实现方式:骨架元素(`.tileBodyScaffold`)按形状切换 `flex-direction`,\n 而不是给每种形状写一套 JSX —— 结构一份、布局四档,消费方无需分支。\n -------------------------------------------------------------------------- */\n\n.tileBodyScaffold {\n display: flex;\n flex-direction: column;\n gap: calc(3px * var(--corum-density-scale, 1));\n min-width: 0;\n min-height: 0;\n width: 100%;\n}\n\n/* wide:图文并排——图标与文字块左右分栏,图标不再独占一行。 */\n.tile[data-tile-size='wide'] .tileBodyScaffold {\n flex-direction: row;\n align-items: flex-start;\n gap: calc(10px * var(--corum-density-scale, 1));\n}\n\n.tile[data-tile-size='wide'] .tileBodyText {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* tall:纵向富余 ⇒ 作者沉底(名称与作者分列两端)。 */\n.tile[data-tile-size='tall'] .tileBodyScaffold {\n justify-content: space-between;\n}\n\n/* 大贴与高贴的名称可占三行(简介移出卡片后,行数从两行放宽)。 */\n.tile[data-tile-size='big'] .tileName,\n.tile[data-tile-size='tall'] .tileName { -webkit-line-clamp: 3; min-height: calc(3 * 1.3em); }\n\n/* ── 磁贴角标:状态点 / 启停开关 ─────────────────────────────────────────── */\n\n.tileDot {\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.tileDotOff { background: var(--dsw-alias-label-dimmed); }\n\n.tileSwitch {\n flex: none;\n position: relative;\n width: calc(32px * var(--corum-density-scale, 1));\n height: calc(18px * var(--corum-density-scale, 1));\n padding: 0;\n border: 1px solid transparent;\n border-radius: 999px;\n background: var(--dsw-alias-brand-primary);\n cursor: pointer;\n transition: background .15s;\n}\n\n.tileSwitch[data-off] {\n background: var(--corum-glass-3);\n border-color: var(--corum-glass-border);\n}\n\n.tileSwitch:disabled { opacity: .5; cursor: default; }\n\n.tileSwitchKnob {\n position: absolute;\n top: 2px;\n left: 14px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--corum-label-on-brand);\n transition: left .15s;\n}\n\n.tileSwitch[data-off] .tileSwitchKnob {\n left: 2px;\n background: var(--dsw-alias-label-dimmed);\n}\n\n/* ── 占位空卡(无数据时的虚线占位,保留设计稿的「即将上线」形态)────────── */\n\n.tilePlaceholder {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: calc(8px * var(--corum-density-scale, 1));\n overflow: hidden;\n box-sizing: border-box;\n padding: calc(12px * var(--corum-density-scale, 1));\n border: 1px dashed var(--corum-glass-border);\n border-radius: 0;\n background: transparent;\n color: var(--dsw-alias-label-dimmed);\n cursor: default;\n}\n\n.tilePlaceholderIcon {\n display: inline-flex;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.tilePlaceholderText {\n margin: 0;\n font-size: calc(11px * var(--corum-ui-font-scale, 1));\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* ── 最热门热排(196×120 ×4,只在插件市场用)────────────────────────────── */\n\n/* 热排是 4 张等宽贴 + 3 个间隙;与磁贴群用同一套单位口径:\n 196 = 264 − 2×128/…… 不成立,故热排不按单位推,而是**按容器等分**\n (每张 =(100% − 3×间隙)/4)。窄窗下与磁贴群同步收缩、不留溢出。 */\n.hotRow {\n display: flex;\n gap: var(--mosaic-gap, calc(8px * var(--corum-density-scale, 1)));\n min-width: 0;\n}\n\n.hotRow > * {\n flex: 1 1 0;\n min-width: 0;\n height: calc(120px * var(--corum-density-scale, 1));\n}\n\n/*\n * 窄窗适配:磁贴自身是尺寸查询容器。窗口变窄时整面墙按单位宽等比缩小,但**文字\n * 不跟着缩**(缩字会伤可读性),于是小贴会先装不下:实测 1280 窗口下小贴被缩到\n * 83×83,而「图标行 + 两行名称 + 版本徽章 + 作者小字」需约 99px,底部被裁。\n * 按重要性依次让位(名称 > 作者 > 版本):\n * ① 名称收单行(保住主标识,仍有 ellipsis);\n * ② 再小则隐去版本徽章(版本在右侧详情面板里始终可见)。\n * 宽窗(单位 128 或更大)两条都不触发,保持设计稿的两行外观与完整信息。\n *\n * 容器**不留 container-name**:CSS module 会给名字加哈希前缀,而 `@container`\n * 查询里的名字是字面量、不参与哈希 ⇒ 具名查询永远匹配不上。匿名查询取最近的\n * 容器,正是这里的 .tile。\n */\n.tile {\n container-type: size;\n}\n\n@container (max-height: 180px) {\n .tileName {\n -webkit-line-clamp: 2;\n min-height: calc(2 * 1.3em);\n }\n}\n\n@container (max-height: 140px) {\n .tileName {\n -webkit-line-clamp: 1;\n white-space: nowrap;\n text-overflow: ellipsis;\n /* 单行时行高也收回一行:三行行高是为「不跳动」留的,窄贴里会把作者顶出贴外。 */\n min-height: calc(1.3em);\n }\n}\n\n@container (max-height: 170px) {\n .tileVersion { display: none; }\n}\n\n/* 最后一段:最小窗口(1219)下小贴被缩到 73px,而图标是 TSX 给的固定 24px\n (不随单位缩)⇒ 图标 + 名称 + 作者仍差几像素装不下。此时把图标压到 16px。\n 图标尺寸本身与「界面字号」设置无关(同族插件都固定像素),故这里只按容器\n 高度收,不参与 --corum-ui-font-scale。 */\n@container (max-height: 85px) {\n .tileIcon svg {\n width: 16px;\n height: 16px;\n }\n}\n","/**\n * 磁贴墙(Mosaic)React 组件 —— 集成中心三个内容页共用的展示骨架。\n * 算法在 `./mosaic.ts`、几何在 `./mosaic.module.css`;本文件把两者接成组件。\n *\n * ## 为什么抽出来\n *\n * 插件 / MCP / 技能三页原本各持一份「块生成器 + 排布 CSS + 磁贴本体 + 详情面板」\n * 的副本(跨包 CSS module 不能直接 import 的历史约束)。三份副本必然分叉,实测\n * 已经分叉过:插件页列宽走 `[data-col]` 属性选择器、另两页走 `.mosaicCol264`\n * 类名,且后者漏了块行的 `width: 100%` ⇒ MCP 页的每张贴被拉成整行 808×128,\n * 完全不是磁贴。收敛到本模块后只有一个几何实现。\n *\n * ## 用法\n *\n * ```tsx\n * const blocks = buildMosaic(items.length) // 或 flattenMosaic 取尺寸序列\n * <MosaicWall items={items} blocks={blocks} renderTile={(item, size, i) => ...} />\n * ```\n *\n * @module corum-ui-base/client/Mosaic\n */\nimport { Fragment, useEffect, useState, type MutableRefObject, type ReactNode } from 'react'\nimport css from './mosaic.module.css'\nimport { pickMosaicColumns } from './mosaic.ts'\nimport type { MosaicBlock, MosaicColumns, MosaicSize } from './mosaic.ts'\n\n/**\n * 磁贴底色档位(token 见 theme.css)。\n *\n * 2026-10-24 从 4 档扩到 9 档:用户反馈「配色太单一」。档位按色相命名,\n * 不绑定语义;具体用哪档由 `tintForName` 按名字稳定散列,保证同一项每次\n * 渲染同色、相邻项不同色。\n */\nexport const MOSAIC_TINTS = [\n 'violet', 'blue', 'emerald', 'amber', 'rose', 'cyan', 'mauve', 'deep', 'slate',\n] as const\n\n/** 磁贴底色档位类型。 */\nexport type MosaicTint = (typeof MOSAIC_TINTS)[number]\n\n/** 档位 → 样式类名(消费方按语义挑档,不再自己拼类名)。 */\nconst TINT_CLASS: Readonly<Record<MosaicTint, string>> = {\n violet: css.tileTintViolet,\n blue: css.tileTintBlue,\n emerald: css.tileTintEmerald,\n amber: css.tileTintAmber,\n rose: css.tileTintRose,\n cyan: css.tileTintCyan,\n mauve: css.tileTintMauve,\n deep: css.tileTintDeep,\n slate: css.tileTintSlate,\n}\n\n/**\n * 按名字稳定取一个磁贴底色档(FNV-1a 散列)。\n *\n * 用途:列表页的磁贴不再「一律紫」,但也不能每次渲染变来变去——名字相同 ⇒\n * 颜色相同;不同项按色相轮转。语义色(类型徽标、选中态)仍由调用方单独控制。\n *\n * @param name - 稳定标识(包名 / 服务器名 / 技能名 / 扩展包 id)。\n * @returns 9 档之一。\n */\nexport function tintForName(name: string): MosaicTint {\n let hash = 0x811c9dc5\n for (const ch of name.toLowerCase()) {\n hash ^= ch.codePointAt(0) ?? 0\n hash = Math.imul(hash, 0x01000193)\n }\n return MOSAIC_TINTS[(hash >>> 0) % MOSAIC_TINTS.length]!\n}\n\n/**\n * 贴内骨架的 props:**一份结构、四种布局**。\n *\n * 形状由外层贴的 `data-tile-size` 决定(CSS 按它切 `flex-direction` 与行数),\n * 故这里不需要 `size` 参数 —— 消费方把骨架放进已带 `data-tile-size` 的贴里即可。\n *\n * 各形状的取舍(用户 2026-10-02 定调):\n * - `big`:图标居左上 → 名称(最多 3 行)→ 作者;\n * - `wide`:图文**并排**(图标左、文字右);\n * - `tall`:纵向 → 名称 → 作者沉底;\n * - `small`:图标 + 名称。\n *\n * **卡片上不渲染简介**(用户 2026-10-02 定调):简介只在右侧详情面板呈现。\n * 故本组件没有 `desc` prop —— 原先按贴高隐藏简介的容器查询会让同一张卡片在不同\n * 窗口尺寸下时有时无(用户实测报障),移除后该分支不复存在。\n */\nexport interface MosaicTileBodyProps {\n /** 图标(消费方按形状自定尺寸;骨架只负责落位)。 */\n readonly icon: ReactNode\n /** 名称(必给)。 */\n readonly name: ReactNode\n /** 版本徽章;省略则不渲染。窄贴会被容器查询自动隐去。 */\n readonly version?: ReactNode\n /** 底部小字(作者 / 地址等);省略则不渲染。 */\n readonly sub?: ReactNode\n /** 右上角角标(热度 / 状态点 / 开关);绝对定位,不参与排版。 */\n readonly corner?: ReactNode\n}\n\n/**\n * 按形状分档的贴内骨架。\n *\n * @param props - 见 {@link MosaicTileBodyProps}。\n * @returns 贴内结构(外层应是一个带 `data-tile-size` 的磁贴元素)。\n */\nexport function MosaicTileBody({ icon, name, version, sub, corner }: MosaicTileBodyProps): ReactNode {\n return (\n <>\n {corner}\n <div className={css.tileBodyScaffold}>\n <span className={css.tileIcon}>{icon}</span>\n <div className={`${css.tileBottom} ${css.tileBodyText}`}>\n <div className={css.tileNameRow}>\n <span className={css.tileName}>{name}</span>\n {version !== undefined && <span className={css.tileVersion}>{version}</span>}\n </div>\n {sub !== undefined && <span className={css.tileSub}>{sub}</span>}\n </div>\n </div>\n </>\n )\n}\n\n/** 磁贴墙:把块序列渲染成块行 + 列 + 贴。 */\nexport interface MosaicWallProps<T> {\n /** 贴的数据源(按渲染顺序,与 {@link flattenMosaic} 的顺序一致)。 */\n readonly items: readonly T[]\n /** 块序列(`buildMosaic` 的输出)。 */\n readonly blocks: readonly MosaicBlock[]\n /**\n * 列数模式(与传给 `buildMosaic` 的 `columns` 一致)。\n *\n * 必须传:单位宽算式随列数切换(6 列 `(W−5g)/6` / 3 列 `(W−2g)/3`),CSS 靠这个\n * 属性选式子。不传则 3 列下三列宽和小于块宽、右缘留白(实测 522 vs 538)。\n */\n readonly columns?: MosaicColumns\n /**\n * 渲染单张贴。\n * @param item - 对应位置的数据。\n * @param size - 该位置应使用的尺寸档(已由算法决定,消费方按它调字号/图标)。\n * @param index - 在整面墙里的序号(0 起;入口贴通常在第 0 位)。\n */\n readonly renderTile: (item: T, size: MosaicSize, index: number) => ReactNode\n}\n\n/**\n * 磁贴墙(块行 → 列 → 贴)。\n *\n * @param props - 见 {@link MosaicWallProps}。\n * @returns 磁贴墙元素。\n */\nexport function MosaicWall<T>({ items, blocks, columns = 6, renderTile }: MosaicWallProps<T>): ReactNode {\n return (\n <div className={css.tileGrid} data-mosaic-columns={columns}>\n {blocks.map((block, bi) => (\n <div key={`block:${bi}`} className={css.mosaicBlock} data-mosaic-block={block.kind}>\n {block.cols.map((col, ci) => (\n <div key={`col:${bi}:${ci}`} className={css.mosaicCol} data-col={col.width}>\n {col.sizes.map((size, si) => {\n /* 数据下标由算法给出(长名字会跨位置挑选,落位不再等于渲染序),\n 无槽位信息时退回「按序取」以兼容只给 sizes 的简单用法。 */\n const index = col.slots?.[si]?.itemIndex ?? si\n const item = items[index]\n if (item === undefined) return null\n return (\n <Fragment key={`tile:${bi}:${ci}:${si}`}>\n {renderTile(item, size, index)}\n </Fragment>\n )\n })}\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n}\n\n/**\n * 取某一张贴该挂的属性(消费方展开到自己的 `<button>` 上)。\n *\n * 贴必须是列的**直接子元素**(CSS 靠 `.mosaicCol > [data-tile-size]` 分档列高),\n * 故这里不包壳层元素,只把类名与 data 属性交给消费方。\n *\n * @param props - 见 {@link MosaicTileProps}。\n * @returns 可展开的属性对象。\n */\nexport function mosaicTileAttrs(props: MosaicTileProps): {\n className: string\n 'data-tile-size': MosaicSize\n} {\n return {\n className: mosaicTileClass(props),\n 'data-tile-size': props.size,\n }\n}\n\n/** 磁贴按钮的属性集(消费方直接展开到自己的 `<button>`/`<div>` 上)。 */\nexport interface MosaicTileProps {\n /** 尺寸档(算法给的,决定字号/图标/是否独占列高)。 */\n readonly size: MosaicSize\n /** 底色档。 */\n readonly tint: MosaicTint\n /** 是否选中(进详情面板的那一张)。 */\n readonly active?: boolean\n /** 是否加品牌色 glow(大贴/主推)。 */\n readonly glow?: boolean\n /** 额外类名。 */\n readonly className?: string\n}\n\n/** 拼磁贴的类名(`size` 走 data 属性,其余走类名)。 */\nexport function mosaicTileClass({ tint, active, glow, className }: MosaicTileProps): string {\n return [\n css.tile,\n TINT_CLASS[tint],\n active === true ? css.tileActive : '',\n glow === true ? css.tileGlow : '',\n className ?? '',\n ].filter(Boolean).join(' ')\n}\n\n/** 共享样式表的窄化面(消费方拼自己的结构时按需取这些类名)。 */\nexport interface MosaicStyleSheet {\n readonly tileGrid: string\n readonly mosaicBlock: string\n readonly mosaicCol: string\n readonly tile: string\n readonly tileTop: string\n readonly tileBodyScaffold: string\n readonly tileBodyText: string\n readonly tileIcon: string\n readonly tileCorner: string\n readonly tileBottom: string\n readonly tileNameRow: string\n readonly tileName: string\n readonly tileVersion: string\n readonly tileSub: string\n readonly tileDot: string\n readonly tileDotOff: string\n readonly tileSwitch: string\n readonly tileSwitchKnob: string\n readonly tilePlaceholder: string\n readonly tilePlaceholderIcon: string\n readonly tilePlaceholderText: string\n readonly hotRow: string\n}\n\n/**\n * 共享样式表:三个页面拼贴内结构(图标行 / 名称行 / 小字…)时取这里的类名,\n * 不再各持一份 CSS。返回的是本模块自己 import 的那份 CSS module 对象。\n */\nexport const mosaicStyles: MosaicStyleSheet = {\n tileGrid: css.tileGrid,\n mosaicBlock: css.mosaicBlock,\n mosaicCol: css.mosaicCol,\n tile: css.tile,\n tileTop: css.tileTop,\n tileBodyScaffold: css.tileBodyScaffold,\n tileBodyText: css.tileBodyText,\n tileIcon: css.tileIcon,\n tileCorner: css.tileCorner,\n tileBottom: css.tileBottom,\n tileNameRow: css.tileNameRow,\n tileName: css.tileName,\n tileVersion: css.tileVersion,\n tileSub: css.tileSub,\n tileDot: css.tileDot,\n tileDotOff: css.tileDotOff,\n tileSwitch: css.tileSwitch,\n tileSwitchKnob: css.tileSwitchKnob,\n tilePlaceholder: css.tilePlaceholder,\n tilePlaceholderIcon: css.tilePlaceholderIcon,\n tilePlaceholderText: css.tilePlaceholderText,\n hotRow: css.hotRow,\n}\n\nexport { mosaicStyles as css }\n\n/**\n * 按容器宽决定列数,并在窗口变化时更新(窄窗降 3 列、宽窗回 6 列)。\n *\n * 为什么需要它:6 列在窄窗下单位宽会掉到 80px 出头,长名字必然截断;降成 3 列后\n * 每列宽约翻倍(同一容器宽摊到更少的列上),名称与作者都能正常显示。两种模式的\n * 块宽**恒等**(6u+5g),故切换只改块内列结构、不改变整墙宽度。\n *\n * @param ref - 磁贴群容器(量其内容宽;`ResizeObserver` 监听尺寸变化)。\n * @returns 当前应使用的列数。\n */\nexport function useMosaicColumns(ref: MutableRefObject<HTMLElement | null>): MosaicColumns {\n const [columns, setColumns] = useState<MosaicColumns>(6)\n useEffect(() => {\n const el = ref.current\n if (el === null || typeof ResizeObserver === 'undefined') return\n const measure = (): void => {\n const style = getComputedStyle(el)\n const gap = Number.parseFloat(style.gap) || 8\n /* scrollbar-gutter 预留的滚动槽也计入 clientWidth,扣掉才是真正的列可用宽。 */\n const contentWidth = el.clientWidth - (el.offsetWidth - el.clientWidth)\n setColumns(pickMosaicColumns(contentWidth, gap))\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => { ro.disconnect() }\n }, [ref])\n return columns\n}\n","/**\n * 平台相关的路径工具(P2:渲染层消费面收口的**唯一共享面**)。\n *\n * 事实源:渲染层经 preload 的 `window.corumDesktop.getPlatform()`(合法 window\n * 挂载,写一次只读,红线 1 例外)拿到**实际运行平台**的路径分隔符;桥缺席\n * (非桌面壳,如纯 web)时按 POSIX 处理。渲染层**拿不到 `process.platform`**\n * (方案 §4.4:plugins 各包 src 里零命中),也**绝不硬编码 `/`**——Windows 的\n * cwd 用 `\\`,硬拼 `/` 永不匹配。\n *\n * 各消费点(file-tool-card / SubagentChanges / EmptyStateHero / FileExplorer)\n * 从这里取,不各自 import 整份桥声明(红线 3:本模块即那份窄接口)。\n * @module corum-ui-base/client/platform-paths\n */\n\ndeclare global {\n interface Window {\n /** 桌面 preload 暴露的桥(本模块只用到 getPlatform 这一个方法)。 */\n corumDesktop?: { getPlatform?: () => 'darwin' | 'linux' | 'win32' }\n }\n}\n\n/** 当前平台的路径分隔符('\\\\' = win32,'/' = 其余/桥缺席)。 */\nexport function pathSep(): '/' | '\\\\' {\n if (typeof window === 'undefined') return '/'\n return window.corumDesktop?.getPlatform?.() === 'win32' ? '\\\\' : '/'\n}\n\n/**\n * 拼一段路径(`parent` + `name`),分隔符按当前平台。\n * `parent` 已以分隔符结尾(含根 '/' 或盘符根 'C:\\\\')时不重复加。\n */\nexport function joinPath(parent: string, name: string): string {\n if (parent === '') return name\n if (parent.endsWith('/') || parent.endsWith('\\\\')) return `${parent}${name}`\n return `${parent}${pathSep()}${name}`\n}\n\n/**\n * 取路径末段(basename),同时认 `/` 与 `\\`——输入可能来自任一端\n * (host 给的路径用平台分隔符,快照里的相对路径常用 `/`),只认一种会在\n * 跨平台数据流上漏判。\n */\nexport function basenameOf(path: string): string {\n const parts = path.replace(/[/\\\\]+$/, '').split(/[/\\\\]/)\n return parts[parts.length - 1] ?? path\n}\n\n/**\n * cwd 相对化(官方 `relativizeToCwd` 同语义):`path` 在 `cwd` 之下时返回相对段。\n * 分隔符按当前平台匹配(Windows 的 cwd 用 `\\`,硬拼 `/` 永不匹配)。\n */\nexport function shortenPath(path: string, cwd: string | undefined): string {\n if (cwd === undefined || cwd === '') return path\n const prefix = `${cwd}${pathSep()}`\n if (path.startsWith(prefix)) return path.slice(prefix.length)\n return path\n}\n","/**\n * 「web 搜索 provider 的凭据引用」这条规则的**唯一家**。\n *\n * 规则本身:settings section 里显式声明的 `apiKeyEnv` 优先,否则回落到 provider\n * 自己解析时使用的环境变量名常量。\n *\n * 收敛前这条规则在本仓有第三份拷贝\n * (`corum-ui-settings-plugins/src/client/web-search-card-controller.ts` 的\n * 局部 `refOf()`,已随那张卡的注册一并删除);另两份在 `corum-ui-settings-models`\n * 的 `keyRefFor` / `apiKeyEnvOf`。**不要**把二者合并:`keyRefFor` 的规则是\n * `named ?? <PROVIDER>_API_KEY`(按 provider 名推导),与这里的固定常量语义不同。\n *\n * 放在 `@corum/corum-ui-base/client` 而不是某个设置页里:该包是本仓唯一被跨\n * bundle **值** import 的 client 面(实证 `corum-ide-plugin-manager-ui/src/\n * client/index.tsx`),tsdown 能把这份源码内联进每个消费方 bundle;而跨 client\n * 包的值 import 会被 tsdown 解析失败(同教训:client bundle 之间只敢 type-only\n * import)。\n *\n * @module @corum/corum-ui-base/client/web-search-credential\n */\n\n/**\n * 官方 `@deepseek-ai/dsh-web-search-deepseek` provider 在 settings section 未声明\n * `apiKeyEnv` 时解析的凭据引用(它的 `DEFAULT_API_KEY_ENV`,未导出,此处按同一\n * 事实重声明)。\n */\nexport const DEFAULT_WEB_SEARCH_API_KEY_REF = 'DEEPSEEK_API_KEY'\n\n/** 参与凭据引用判定的 settings section 窄化面。 */\nexport interface WebSearchCredentialSection {\n /** section 声明的凭据引用;缺省/空串 = 用 provider 默认。 */\n apiKeyEnv?: string\n}\n\n/**\n * 一个 web 搜索 settings section 实际寻址的凭据引用。\n * @param section - 当前 section 值(namespace 未注册时可能为 undefined)。\n * @returns 要寻址的引用名。\n */\nexport function webSearchCredentialRef(\n section: WebSearchCredentialSection | undefined | null,\n): string {\n const declared = section?.apiKeyEnv\n return declared !== undefined && declared.length > 0 ? declared : DEFAULT_WEB_SEARCH_API_KEY_REF\n}\n"],"mappings":"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"}