clearai-dsh 0.1.3 → 0.1.4
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/CHANGELOG.md +54 -0
- package/README.md +2 -1
- package/README.zh-CN.md +2 -1
- package/lib/client.js +95 -95
- package/lib/fold.js +151 -66
- package/lib/host.js +20 -18
- package/package.json +4 -2
- package/presets/clearai/agent.cordis.yml +107 -36
- package/presets/clearai/plugins/brain.js +2 -2
- package/presets/clearai/plugins/clearai-kernel.js +552 -284
- package/presets/clearai/plugins/commands.js +199 -0
- package/presets/clearai/plugins/ontology.js +3 -3
- package/presets/clearai/plugins/prompts.js +51 -25
package/lib/client.js
CHANGED
|
@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
|
|
30
30
|
const React = require('react')
|
|
31
31
|
/**
|
|
32
|
-
* **原生图标**(
|
|
32
|
+
* **原生图标**(与本体页签同一族):dsh 右栏页签的 guide 吃一个 `icon` 组件,原生那几张页签用的是
|
|
33
33
|
* `@deepseek-ai/dsh-client-ui-primitives` 里那套(文件页签 = FileTypeIcon ✓)。
|
|
34
34
|
* 我们照同一套用,于是"面包屑"与"世界树"在视觉上属于同一个家族。
|
|
35
35
|
*
|
|
@@ -63,7 +63,7 @@ window.__ModuleLoader__.load({
|
|
|
63
63
|
)
|
|
64
64
|
/**
|
|
65
65
|
* 惰性表:语言是**运行时**的事,而这些表在模块加载时就被求值了——直接写 `t(...)`
|
|
66
|
-
* 会把语言冻在加载那一刻(
|
|
66
|
+
* 会把语言冻在加载那一刻(失效模式:面板标题跟着语言换了,表里的词还停在中文)。
|
|
67
67
|
* 这个代理每次取键都重新求值,读起来仍与普通对象一样。
|
|
68
68
|
*/
|
|
69
69
|
const lazyTable = (make) =>
|
|
@@ -113,7 +113,7 @@ window.__ModuleLoader__.load({
|
|
|
113
113
|
/**
|
|
114
114
|
* 依赖的客户端服务,**必须全部声明在这里**。
|
|
115
115
|
*
|
|
116
|
-
*
|
|
116
|
+
* 这不是洁癖,是行为约束:客户端模块在 `slots` 一出现就被激活
|
|
117
117
|
* (`slots` 是平台 seed,启动时就有),而 `sessions` / `sidebarRightTabs` 是**后来**
|
|
118
118
|
* 才由各自的插件 `provide` 的。只声明 `slots` 的话,我们的 `apply()` 会在它们之前跑,
|
|
119
119
|
* 读不到会话服务 → 早退 → **一个插座都不注册**,而且不报错:右栏只剩宿主自带的「文件」,
|
|
@@ -142,7 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
142
142
|
proposed: t('已提出'),
|
|
143
143
|
alive: t('验证中'),
|
|
144
144
|
confirmed: t('已确认'),
|
|
145
|
-
/** 规范词是「已替代」(
|
|
145
|
+
/** 规范词是「已替代」(与验证本体同词);「已改版」是旧写法。 */
|
|
146
146
|
refuted: t('已推翻'),
|
|
147
147
|
superseded: t('已替代'),
|
|
148
148
|
retracted: t('已撤回'),
|
|
@@ -168,7 +168,7 @@ window.__ModuleLoader__.load({
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* 面板的 CSS:按钮与标签照抄**原生那一套**(
|
|
171
|
+
* 面板的 CSS:按钮与标签照抄**原生那一套**(取自宿主自己的包,不是我们编的):
|
|
172
172
|
* · 28px 药丸(设置页的「Add model」):`.5px solid var(--dsw-alias-border-l3)` + `border-radius:14px`
|
|
173
173
|
* + `bg:0 0` + `label-primary` + `font-size:12px;line-height:18px;padding:0 10px`
|
|
174
174
|
* · 11px 小药丸(技能卡上的「inspect」):`.5px solid var(--dsw-alias-border-l4)`
|
|
@@ -181,7 +181,7 @@ window.__ModuleLoader__.load({
|
|
|
181
181
|
.clearai-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
182
182
|
.clearai-btn:disabled{color:var(--dsw-alias-label-secondary);opacity:.5;cursor:default}
|
|
183
183
|
.clearai-btn[data-tone="warn"]{border-color:var(--dsw-alias-state-warn-primary)}
|
|
184
|
-
/* 工具行里的控制(
|
|
184
|
+
/* 工具行里的控制(控制归工具行):照原生那一行的形状——无边框、次级文字色、悬浮才出底。
|
|
185
185
|
刻意不做成我们自己那种药丸:它坐在原生「完全权限」旁边,长得像原生才不突兀。 */
|
|
186
186
|
.clearai-toolctl{box-sizing:border-box;display:inline-flex;align-items:center;gap:2px;padding:2px 6px;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;cursor:pointer}
|
|
187
187
|
.clearai-toolctl:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
@@ -257,7 +257,7 @@ window.__ModuleLoader__.load({
|
|
|
257
257
|
progressTrack: { display: 'inline-block', width: 90, height: 4, background: 'var(--dsw-alias-border-l1)', borderRadius: 2, verticalAlign: 'middle', marginLeft: 4 },
|
|
258
258
|
progressBar: { display: 'block', height: 4, background: 'var(--dsw-alias-brand-primary)', borderRadius: 2 },
|
|
259
259
|
/**
|
|
260
|
-
*
|
|
260
|
+
* 「事实」那一格:一个知识货架(上架=已确认事实,下架=命题)。
|
|
261
261
|
* 默认一行一条命题,只有主张 + 当前处境 + 等级判者;点开才展开来路与证据。
|
|
262
262
|
*/
|
|
263
263
|
group: { display: 'flex', gap: 6, alignItems: 'baseline', fontSize: 11, fontWeight: 600, letterSpacing: 1, color: 'var(--dsw-alias-label-secondary)', margin: '10px 0 2px' },
|
|
@@ -273,7 +273,7 @@ window.__ModuleLoader__.load({
|
|
|
273
273
|
propWhere: { fontSize: 11.5, color: 'var(--dsw-alias-label-secondary)', opacity: 0.85, textAlign: 'left' },
|
|
274
274
|
propJudge: { fontSize: 11.5, color: 'var(--dsw-alias-label-secondary)', textAlign: 'right', whiteSpace: 'nowrap' },
|
|
275
275
|
propBody: { marginTop: 8, paddingLeft: 10, borderLeft: '2px solid var(--dsw-alias-border-l2)', display: 'flex', flexDirection: 'column', gap: 4 },
|
|
276
|
-
/**
|
|
276
|
+
/** 流转图:竖排的状态机,走过的边实线、未走的虚线灰,当前态高亮。 */
|
|
277
277
|
flow: { display: 'flex', flexDirection: 'column', gap: 0, margin: '2px 0 6px' },
|
|
278
278
|
flowStep: { display: 'flex', flexDirection: 'column' },
|
|
279
279
|
flowNode: { alignSelf: 'flex-start', display: 'inline-flex', gap: 6, alignItems: 'baseline', padding: '1px 10px', border: '.5px solid var(--dsw-alias-border-l2)', borderRadius: 999, fontSize: 11.5 },
|
|
@@ -319,7 +319,7 @@ window.__ModuleLoader__.load({
|
|
|
319
319
|
* 为什么不能直接 `response.json()`:那条路上失败时返回的是**纯文本**
|
|
320
320
|
* (`connection` 层查不到 exact fetch route 就回 `404 "not found"`),
|
|
321
321
|
* 直接 parse 会抛出 `Unexpected token 'o', "not found" is not valid JSON`
|
|
322
|
-
*
|
|
322
|
+
* ——人看到的是解析器的抱怨,不是发生了什么。
|
|
323
323
|
* 这里统一成 `{ ok, status, payload, error }`,`error` 里带上状态码与原文。
|
|
324
324
|
*/
|
|
325
325
|
const readResponse = async (response) => {
|
|
@@ -354,7 +354,7 @@ window.__ModuleLoader__.load({
|
|
|
354
354
|
/**
|
|
355
355
|
* 小节。`mark: true` 时在标题前放我们的标记 —— **只有事实货架要它**:
|
|
356
356
|
* 那一格是这套系统真正的产物(已确认、可引用),标一下是"这一格由 ClearAI 维护"的陈述。
|
|
357
|
-
* 别的标题不标:标滥了就只是装饰(
|
|
357
|
+
* 别的标题不标:标滥了就只是装饰(同类同一规矩,而不是到处贴)。
|
|
358
358
|
*/
|
|
359
359
|
function Section(props) {
|
|
360
360
|
return h(
|
|
@@ -371,7 +371,7 @@ window.__ModuleLoader__.load({
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
/**
|
|
374
|
-
*
|
|
374
|
+
* 空态。**每一格空态都带我们的标记**——同类空态同一规矩,不是逐处装饰。
|
|
375
375
|
*
|
|
376
376
|
* 为什么标在这里而不是页签图标那一排:一排里两个一样的标是**重复**(试过,退了);
|
|
377
377
|
* 而空态一次只看得见一个,标记在这里是"这一格是我们的"这一句陈述,克制且不抢。
|
|
@@ -391,7 +391,7 @@ window.__ModuleLoader__.load({
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
/**
|
|
394
|
-
*
|
|
394
|
+
* **目标那一行**:世界树页眉用。只放事实(主张 · 判据 · 阶段与完成度),
|
|
395
395
|
* 详情(结算单、推进次数)在各处自己的面上——页眉不该变成第二份面板。
|
|
396
396
|
*/
|
|
397
397
|
function GoalLine(props) {
|
|
@@ -401,8 +401,8 @@ window.__ModuleLoader__.load({
|
|
|
401
401
|
'div',
|
|
402
402
|
{ style: { ...S.rowFirst, ...S.inline } },
|
|
403
403
|
h('span', null, dash(goal.claim)),
|
|
404
|
-
// 阶段与完成度归**状态条**(常驻可见)⇒ 这里不重复(
|
|
405
|
-
// 判据常常是五条清单(真数据里 ~400 字 ✗)⇒ 页眉只放一句,全文进 tooltip(
|
|
404
|
+
// 阶段与完成度归**状态条**(常驻可见)⇒ 这里不重复(两处说同一件事 = 冗余)
|
|
405
|
+
// 判据常常是五条清单(真数据里 ~400 字 ✗)⇒ 页眉只放一句,全文进 tooltip(页眉只放一句)
|
|
406
406
|
h('span', { style: S.faint, title: String(goal.doneCriteria ?? '') }, `${t('判据:')}${brief(goal.doneCriteria, 56)}`),
|
|
407
407
|
)
|
|
408
408
|
}
|
|
@@ -434,7 +434,7 @@ window.__ModuleLoader__.load({
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
/**
|
|
437
|
-
*
|
|
437
|
+
* 对象的**人话名字**。与本体声明里的对象名一一对应——交叉校验测试里
|
|
438
438
|
* 有一条「声明里每个对象都必须在这里有名字」,少一个就红(界面不许漏对象)。
|
|
439
439
|
*/
|
|
440
440
|
const LOOP_LABEL = lazyTable(() => ({
|
|
@@ -452,7 +452,7 @@ window.__ModuleLoader__.load({
|
|
|
452
452
|
/**
|
|
453
453
|
* 命题的分组 = **本体声明的五个状态**,顺序即认识论的顺序。
|
|
454
454
|
*
|
|
455
|
-
* 用词有权威出处:[`docs/verification-loop.md`](../docs/verification-loop.md)
|
|
455
|
+
* 用词有权威出处:[`docs/verification-loop.md`](../docs/verification-loop.md) 的状态表
|
|
456
456
|
* (那份文档明说「其他文档、提示词与代码注释提到这些概念时,以这里的叫法为准」)。
|
|
457
457
|
* 界面因此不另造一套产品词——「已确认」的命题不在这一列:它已经**升格为事实**,在事实货架上。
|
|
458
458
|
*/
|
|
@@ -484,7 +484,7 @@ window.__ModuleLoader__.load({
|
|
|
484
484
|
*/
|
|
485
485
|
function evidenceOf(data, hypothesisId) {
|
|
486
486
|
/**
|
|
487
|
-
*
|
|
487
|
+
* 走**跨计划**的步骤索引:命题的验证步常常留在**已收尾的旧计划**里
|
|
488
488
|
* (真数据:两条计划,`tests` 全在旧的那条上)⇒ 只查活动计划会全断,
|
|
489
489
|
* 面板上五个命题的证据与判者全是「—」。索引由折法派生,不新增存储。
|
|
490
490
|
*/
|
|
@@ -498,17 +498,17 @@ window.__ModuleLoader__.load({
|
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
/**
|
|
501
|
-
*
|
|
501
|
+
* 一条证据的**出处**。分两层:
|
|
502
502
|
*
|
|
503
503
|
* ① **记账时定下的 `origins` 优先** —— 四类入口(产物 / 评估卡 / 评估者子会话 / 人放行)
|
|
504
504
|
* 是内核在写证据那一刻解析好的事实,界面只渲染;
|
|
505
505
|
* ② 旧日志没有 `origins` 时走**只读回退**:材料 id → 材料表里的路径,换不出来的**丢掉** ——
|
|
506
|
-
* 绝不把裸 id 渲染成"能点"的样子(
|
|
506
|
+
* 绝不把裸 id 渲染成"能点"的样子(失效模式:整排出处因此点不开)。
|
|
507
507
|
*/
|
|
508
508
|
const ORIGIN_LABEL = lazyTable(() => ({ 'audit-card': t('评估卡'), 'evaluator-session': t('看评估者'), 'approval-record': t('审批记录') }))
|
|
509
509
|
/**
|
|
510
510
|
* 出处的**可点标签**:产物用**文件名**,不用「产物」三个字 ——
|
|
511
|
-
*
|
|
511
|
+
* 一条证据常常挂着两三个产物,都叫「产物」就没人知道该点谁。
|
|
512
512
|
* 同名不同目录时才补路径片段(仍然是"一眼能认出点的是哪个")。
|
|
513
513
|
*/
|
|
514
514
|
function fileLabel(path, others) {
|
|
@@ -543,7 +543,7 @@ window.__ModuleLoader__.load({
|
|
|
543
543
|
if (item.anchor === 'auditor') {
|
|
544
544
|
/**
|
|
545
545
|
* 评估卡按**步骤**归属;世界线证据的卡按 `分支` 归属(形如 `k-…:b-…`),
|
|
546
|
-
* 与证据的 `stepId` 不是同一个 id —— 所以两种都要认(
|
|
546
|
+
* 与证据的 `stepId` 不是同一个 id —— 所以两种都要认(只认 stepId 时,
|
|
547
547
|
* 世界线的五条独立证据一条都指不到卡)。
|
|
548
548
|
*/
|
|
549
549
|
const audits = (data?.audits ?? []).filter((row) => row.evaluator === 'independent')
|
|
@@ -572,7 +572,7 @@ window.__ModuleLoader__.load({
|
|
|
572
572
|
h('span', { style: S.evBasis, title: String(item.basis ?? '') }, brief(item.basis, 160)),
|
|
573
573
|
...originsOf(data, item).map((origin, index) => {
|
|
574
574
|
/**
|
|
575
|
-
*
|
|
575
|
+
* 四类入口各自的去处:
|
|
576
576
|
* 产物 / 评估卡 → 右侧**原生预览**打开那个文件;
|
|
577
577
|
* 看评估者 → 旁观**评估者子会话**(论证过程在里面,harness 原生能力);
|
|
578
578
|
* 审批记录 → 没有文件(原生审批对在会话日志里),如实说明它凭什么不可伪造。
|
|
@@ -662,7 +662,7 @@ window.__ModuleLoader__.load({
|
|
|
662
662
|
const out = []
|
|
663
663
|
/**
|
|
664
664
|
* `to` 一律用**声明里的原始状态键**(`alive` / `refuted` …),不做翻译——
|
|
665
|
-
*
|
|
665
|
+
* 翻译只在渲染时做。混用两套键是踩过的坑:流转图按原始键查「这一跳走没走过」,
|
|
666
666
|
* 而这里返回的是中文字面 ⇒ 命不中 ⇒ 走过的边全被画成"没走"。
|
|
667
667
|
*/
|
|
668
668
|
if (evidence.length > 0) {
|
|
@@ -717,7 +717,7 @@ window.__ModuleLoader__.load({
|
|
|
717
717
|
}
|
|
718
718
|
|
|
719
719
|
/**
|
|
720
|
-
*
|
|
720
|
+
* **流转图**:横向主干 + 分支的 SVG 状态机。
|
|
721
721
|
*
|
|
722
722
|
* 三条着色规矩(与世界树同一套视觉语言):
|
|
723
723
|
* · **走过的边**:实线 + 颜色(独立裁决用品牌强调色、自判用前景色),旁边写清触发与凭据;
|
|
@@ -879,7 +879,7 @@ window.__ModuleLoader__.load({
|
|
|
879
879
|
/**
|
|
880
880
|
* 图上那一跳的完整凭据(触发 + 判者 + 两个可点入口)。
|
|
881
881
|
* 这里**不再**摆本体文件路径:那是文档坐标,不是面板上的动作 ——
|
|
882
|
-
*
|
|
882
|
+
* 「这句话不用了」——人拍板删的。
|
|
883
883
|
*/
|
|
884
884
|
evidence.length === 0
|
|
885
885
|
? null
|
|
@@ -908,12 +908,12 @@ window.__ModuleLoader__.load({
|
|
|
908
908
|
function PropositionShelf(props) {
|
|
909
909
|
/**
|
|
910
910
|
* 打开器**自己接**(不靠外层注入):谁渲染这个货架,点证据/点事实都能开原件。
|
|
911
|
-
*
|
|
911
|
+
* 踩过的坑:只有最外层 `Facts` 注入 `openPreview` 时,单独渲染货架(测试缝、
|
|
912
912
|
* 以后的复用)会让整排出处看着能点、点了没反应。
|
|
913
913
|
*/
|
|
914
914
|
/**
|
|
915
915
|
* `props.X ?? 已有值`:外层 `Facts` 只传 `{data}`,而打开器在 `data` 里 ——
|
|
916
|
-
* 直接写 `openRail: props.openRail` 会用 `undefined` 把它盖掉 ⇒ 链接永不渲染 ✗(
|
|
916
|
+
* 直接写 `openRail: props.openRail` 会用 `undefined` 把它盖掉 ⇒ 链接永不渲染 ✗(踩过的坑)。
|
|
917
917
|
*/
|
|
918
918
|
const base = props.data ?? (typeof props.useProjection === 'function' ? props.useProjection('clearai') : undefined) ?? {}
|
|
919
919
|
const data = { ...base, openPreview: props.openPreview ?? base.openPreview, openRail: props.openRail ?? base.openRail, openSpectator: props.openSpectator ?? base.openSpectator }
|
|
@@ -974,19 +974,19 @@ window.__ModuleLoader__.load({
|
|
|
974
974
|
{ style: S.factMeta, title: row.scope === null || row.scope === undefined ? '' : String(row.scope) },
|
|
975
975
|
/**
|
|
976
976
|
* 边界和证据的「依据」是同一类字段 ⇒ **同一条规矩**:列表放摘要、全文进 tooltip。
|
|
977
|
-
* 真数据里边界常是 200~300
|
|
977
|
+
* 真数据里边界常是 200~300 字,三条事实就把这一格撑到一千多字 ✗(货架只放摘要)。
|
|
978
978
|
*/
|
|
979
979
|
`${t('边界:')}${row.scope === null || row.scope === undefined || String(row.scope).trim() === '' ? '(未写——引用前请谨慎)' : brief(row.scope, 80)}${t(' · 支持到 ')}${dash(row.level)}${t(' · 证据 ')}${(row.evidenceIds ?? []).length}${t(' 条')}`,
|
|
980
980
|
/**
|
|
981
981
|
* 原件的标签用**文件名**(「打开原件」四平八稳,但一排下来同样认不出是谁)。
|
|
982
982
|
*/
|
|
983
983
|
/**
|
|
984
|
-
* 整行已经可点开原件 ⇒ 不再挂一个同样动作的「事实存档」✗(
|
|
984
|
+
* 整行已经可点开原件 ⇒ 不再挂一个同样动作的「事实存档」✗(同一动作不开两条路)。
|
|
985
985
|
* 原件路径在整行的 tooltip 里(`打开 <path>`)。
|
|
986
986
|
*/
|
|
987
987
|
/**
|
|
988
|
-
*
|
|
989
|
-
*
|
|
988
|
+
* 跳去它那一步。**不把 `openRail` 当成渲染前提** ——
|
|
989
|
+
* 把渲染前提挂在它上面,整条链接会一起消失。
|
|
990
990
|
* 拿不到 `openRail` 也照常渲染:点了至少把聚焦设上、并试一次原生切视图。
|
|
991
991
|
*/
|
|
992
992
|
stepOfFact(data, row) === null
|
|
@@ -1011,7 +1011,7 @@ window.__ModuleLoader__.load({
|
|
|
1011
1011
|
}
|
|
1012
1012
|
|
|
1013
1013
|
/**
|
|
1014
|
-
*
|
|
1014
|
+
* **「事实」页签**:一个**知识货架**,不是机制说明页。
|
|
1015
1015
|
*
|
|
1016
1016
|
* 上架 = 已确认事实(可用作下一轮已知,按支持等级分级、每条带边界);
|
|
1017
1017
|
* 下架 = 命题(人与模型提出的候选知识,按本体状态分组流转)。
|
|
@@ -1039,7 +1039,7 @@ window.__ModuleLoader__.load({
|
|
|
1039
1039
|
{ style: S.bar },
|
|
1040
1040
|
h('span', { style: S.title }, t('事实')),
|
|
1041
1041
|
/**
|
|
1042
|
-
* 页眉只说这一格是干什么的:计数在下面两段的标题里(
|
|
1042
|
+
* 页眉只说这一格是干什么的:计数在下面两段的标题里(同屏四个数字两两重复 = 噪声),
|
|
1043
1043
|
* 「模型读的是同一张表」是实现保证、不是用户信息 ✗。
|
|
1044
1044
|
*/
|
|
1045
1045
|
h('span', { style: S.faint }, t('能用的在上面,正在验的在下面')),
|
|
@@ -1077,12 +1077,12 @@ window.__ModuleLoader__.load({
|
|
|
1077
1077
|
* (地址语法与编码规则照抄 `workspace-path/file-address`:`/#?` 之前、按 `/` 分段、逐段解码。)
|
|
1078
1078
|
* 路径是绝对路径时,分段里会自然出现一个空段,拼回来仍是绝对路径——原生就是这么分的。
|
|
1079
1079
|
*
|
|
1080
|
-
* **第二参是会话 id,不是路径**(
|
|
1080
|
+
* **第二参是会话 id,不是路径**(传错会直接读空):读面按它查工作区根
|
|
1081
1081
|
* (`workspaceFileScope`),拿错了就原样报
|
|
1082
1082
|
* `lookup provider "workspaceFileScope" did not resolve the requested identity`。
|
|
1083
1083
|
* 谁调用谁负责给对 —— 见 `apply` 里的 `openPreviewFor`,按**座位自己的** props 定型。
|
|
1084
1084
|
*/
|
|
1085
|
-
/** 时间戳 → 「
|
|
1085
|
+
/** 时间戳 → 「YYYY-MM-DD HH:mm」。与宿主半卡片里的写法一致:同一份事实在两处长得不一样,人就会怀疑其中一处。 */
|
|
1086
1086
|
const stampOf = (value) => (typeof value === 'number' && Number.isFinite(value) ? new Date(value).toISOString().slice(0, 16).replace('T', ' ') : t('未知'))
|
|
1087
1087
|
|
|
1088
1088
|
/** 逐段编码:照抄原生的 `encodeSegment`——`:` 保持字面(Windows 盘符),其余交给 encodeURIComponent。 */
|
|
@@ -1114,16 +1114,16 @@ window.__ModuleLoader__.load({
|
|
|
1114
1114
|
}
|
|
1115
1115
|
|
|
1116
1116
|
/**
|
|
1117
|
-
* 产物视图(
|
|
1117
|
+
* 产物视图(中栏第一眼该看「我拿到了什么」)。
|
|
1118
1118
|
*
|
|
1119
1119
|
* 数据走宿主读面(`/api/clearai/deliverables`):交付物的**存在性是文件系统事实**,
|
|
1120
|
-
*
|
|
1120
|
+
* 浏览器读不了盘,所以由宿主在请求时即时 stat(顺带给出字节与修改时间)。
|
|
1121
1121
|
* 投影一变(推进了一步、多了一条证据)就重新取一次——面板不许比状态慢一拍。
|
|
1122
1122
|
*
|
|
1123
1123
|
* 这条读面回**两半**,这一栏也画两半:
|
|
1124
1124
|
* · `stages` = **声明**(计划里写了要交什么、谁验的、盘上在不在);
|
|
1125
1125
|
* · `outputs` = **实际**(products/ 底下真有、但没有任何计划声明过的)。
|
|
1126
|
-
*
|
|
1126
|
+
* 为什么两半都要:在一个已经有产物的老项目里开新会话,原来这一栏
|
|
1127
1127
|
* 只有「阶段 0 · 交付 0/0」—— 可人此刻问的是「我拿到了什么」,盘上那份 46 KB 的 HTML
|
|
1128
1128
|
* 明明就在。声明与实际是两件事,少画一半就等于对着半张表回答。
|
|
1129
1129
|
*/
|
|
@@ -1169,7 +1169,7 @@ window.__ModuleLoader__.load({
|
|
|
1169
1169
|
* 淹没 3 份交付物。
|
|
1170
1170
|
*/
|
|
1171
1171
|
/**
|
|
1172
|
-
* 「核心产物」=
|
|
1172
|
+
* 「核心产物」= **一份文件一行**。
|
|
1173
1173
|
*
|
|
1174
1174
|
* 原先摊平的是「步骤 × 声明产物」这对组合 ⇒ 一个文件被几个步骤声明就出现几次 ✗
|
|
1175
1175
|
* (真数据:`qinrun-dipforming-3d.html` 被 build/verify-3d/rebuild/integrate 四步都声明了,
|
|
@@ -1335,7 +1335,7 @@ window.__ModuleLoader__.load({
|
|
|
1335
1335
|
* ③ **光环** 此刻在动吗 呼吸 = 是(步骤与世界线共用同一惯用法)
|
|
1336
1336
|
* ④ **分段** 被闸门裁断过几次 N>1 才分段,封顶 3 段,驳回的那几段画红
|
|
1337
1337
|
*
|
|
1338
|
-
*
|
|
1338
|
+
* 这一处曾丢过一批行为(分岔/推进/闪烁/选择/裁减/变灰)。必须都在的:
|
|
1339
1339
|
* 空心圈与实心核、呼吸光环与虚线审计环、轮次分段弧、落选/作废的**变灰划掉**、
|
|
1340
1340
|
* 虚线轨道(落选那条)、点行选中 → 树下详情(含给人的两个动作:采纳这条世界线 / 放弃探索)。
|
|
1341
1341
|
*
|
|
@@ -1383,7 +1383,7 @@ window.__ModuleLoader__.load({
|
|
|
1383
1383
|
* 原设计里那棵树也把它留着——「什么都不删」在图上就是「还在,但灰了」。
|
|
1384
1384
|
*/
|
|
1385
1385
|
/**
|
|
1386
|
-
*
|
|
1386
|
+
* **对外聚焦通道**:别的面点一个步骤/世界线,世界树要能**选中那一行**。
|
|
1387
1387
|
*
|
|
1388
1388
|
* 为什么需要它:树原先只有**内部**选中状态(点行看详情),外面够不着 ⇒
|
|
1389
1389
|
* 「在世界树里看这一步」只能把树打开,不定位 ✗ —— 这是"点击跳转闭环"里缺的那一环。
|
|
@@ -1435,7 +1435,7 @@ window.__ModuleLoader__.load({
|
|
|
1435
1435
|
|
|
1436
1436
|
const treeFocus = makeFocusStore()
|
|
1437
1437
|
/**
|
|
1438
|
-
*
|
|
1438
|
+
* **反向聚焦**:从世界树的某一步跳回「事实」那一格,并**展开对应的命题**。
|
|
1439
1439
|
* 与 treeFocus 同一个形状、同一条纪律(界面状态,不进账本)。
|
|
1440
1440
|
*/
|
|
1441
1441
|
const factsFocus = makeFocusStore()
|
|
@@ -1481,7 +1481,7 @@ window.__ModuleLoader__.load({
|
|
|
1481
1481
|
/**
|
|
1482
1482
|
* 行与行之间的**连接**(轨道):不是「相邻两行连一条」,而是显式的扇出/扇入。
|
|
1483
1483
|
*
|
|
1484
|
-
*
|
|
1484
|
+
* 按相邻行连线会把「脊柱裂成四条并行车道」——
|
|
1485
1485
|
* 被画成了一条往右下掉的**蛇**——分叉点只连到第一条车道,第二条连第三条……
|
|
1486
1486
|
* 那不是分叉,是排队。原设计用的是显式连接表(fork → 每条车道、每条车道 → 收敛菱形),
|
|
1487
1487
|
* 所以四条车道是从同一个点扇出去的。这里照它重写。
|
|
@@ -1565,19 +1565,19 @@ window.__ModuleLoader__.load({
|
|
|
1565
1565
|
// · 实心呼吸 = 这条世界线还在探索(原设计的 run 级状态我们这侧没有,所以只画我们真有的)
|
|
1566
1566
|
const auditing = row.kind !== 'branch' && audits.some((audit) => audit.stepId === stepId && (audit.verdict === null || audit.verdict === undefined))
|
|
1567
1567
|
/**
|
|
1568
|
-
* 光环 =
|
|
1568
|
+
* 光环 = 「此刻在动吗」。两个**必须**排除的假话:
|
|
1569
1569
|
* · 步骤已经作废/交付了,它上面那条车道不可能还在跑(分叉没跟着收口时会留下这种孤儿);
|
|
1570
1570
|
* · 分叉自己已经放弃了(它下面没有「在动」的东西)。
|
|
1571
1571
|
* 状态机那一侧该不该让分叉跟着步骤一起收口,是另一件事(记在 recon 里待定);
|
|
1572
1572
|
* 但**界面不许说假话**这一条不依赖那个决定。
|
|
1573
1573
|
*/
|
|
1574
1574
|
const ownerAlive = row.step?.status !== 'void' && row.step?.status !== 'advanced'
|
|
1575
|
-
//
|
|
1575
|
+
// 失败的世界线不再「在动」:它死了,不是还在跑。
|
|
1576
1576
|
const dead = row.kind === 'branch' && row.branch.failed === true
|
|
1577
1577
|
/**
|
|
1578
1578
|
* 「执行者未归」也不在动:分叉已经收口(收敛或放弃),派出去的执行者**再回来也没有归宿**。
|
|
1579
1579
|
* 它既不是失败(没人知道它跑成没跑成),也不是在跑(「结论会自动回灌」那句承诺实现不了)——
|
|
1580
|
-
* 所以它是第三个静止态:不判成败、不呼吸(
|
|
1580
|
+
* 所以它是第三个静止态:不判成败、不呼吸(不说破就会被读成还在跑)。
|
|
1581
1581
|
*/
|
|
1582
1582
|
const unreturned = row.kind === 'branch' && row.branch.unreturned === true
|
|
1583
1583
|
const live = row.kind === 'branch' && row.branch.status === 'exploring' && ownerAlive && !dead && !unreturned
|
|
@@ -1653,7 +1653,7 @@ window.__ModuleLoader__.load({
|
|
|
1653
1653
|
const [picked, setPicked] = React.useState(null)
|
|
1654
1654
|
const focus = useFocus(treeFocus)
|
|
1655
1655
|
/**
|
|
1656
|
-
*
|
|
1656
|
+
* **一个对话会有多个世界树**:活动的那份默认显示,已收尾的可以切回去看
|
|
1657
1657
|
* (它们没被删:文档归档在 `clear/goals/plans/<id>.md`)。
|
|
1658
1658
|
* 优先级:外面点进来的聚焦所指的那份 > 手动切的 > 活动的。
|
|
1659
1659
|
*/
|
|
@@ -1750,7 +1750,7 @@ window.__ModuleLoader__.load({
|
|
|
1750
1750
|
}
|
|
1751
1751
|
/**
|
|
1752
1752
|
* 行文只放**名字**,状态交给颜色/形状/标签说。但「失败」与「随步骤作废而终止」是两种
|
|
1753
|
-
* 容易被误读成「还在跑」的状态,所以它们各带一个后缀(
|
|
1753
|
+
* 容易被误读成「还在跑」的状态,所以它们各带一个后缀(语义:
|
|
1754
1754
|
* 失败不是落选,孤儿不是被裁)。
|
|
1755
1755
|
*/
|
|
1756
1756
|
const rowSuffix = (row) =>
|
|
@@ -1774,7 +1774,7 @@ window.__ModuleLoader__.load({
|
|
|
1774
1774
|
? `${t('已放弃探索:')}${brief(row.fork.question, 24)}`
|
|
1775
1775
|
: /**
|
|
1776
1776
|
* 行里只放**认得出这一步的那几个字**:`do` 常常是整句话(真数据里 ~100 字 ✗),
|
|
1777
|
-
*
|
|
1777
|
+
* 全文在点开的详情里。
|
|
1778
1778
|
*/
|
|
1779
1779
|
`${row.step.ordinal}. ${brief(row.step.do, 26)}`
|
|
1780
1780
|
return h(
|
|
@@ -1787,11 +1787,11 @@ window.__ModuleLoader__.load({
|
|
|
1787
1787
|
/**
|
|
1788
1788
|
* 页眉只放**一句话**:`brief` 常常是整篇 markdown 计划(真数据里这一行渲染出 1794 字 ✗),
|
|
1789
1789
|
* 全文进 tooltip,要读整篇点「打开计划」(原生预览 `clear/goals/plans/<id>.md`)——
|
|
1790
|
-
*
|
|
1790
|
+
* 默认少而准,细节靠点开。
|
|
1791
1791
|
*/
|
|
1792
1792
|
h('span', { style: S.faint, title: briefText }, titleText),
|
|
1793
1793
|
/**
|
|
1794
|
-
*
|
|
1794
|
+
* **历史世界树的入口**:一个对话会有多个计划。
|
|
1795
1795
|
* 只有一份时不出现 ✓;多份时用**一个原生下拉**(一行,不铺一排标签 —— 真数据里有 8 份 ✗)。
|
|
1796
1796
|
* 默认停在最近那份;切到旧的会在下面标出「已收尾 · 存档可看」。
|
|
1797
1797
|
*/
|
|
@@ -1826,7 +1826,7 @@ window.__ModuleLoader__.load({
|
|
|
1826
1826
|
h('span', { style: { ...S.faint, marginLeft: 'auto' } }, t('点一行看细节')),
|
|
1827
1827
|
),
|
|
1828
1828
|
/**
|
|
1829
|
-
*
|
|
1829
|
+
* 这一格是**计划的一切**:目标是脊柱的起点、拓扑是它的形状、
|
|
1830
1830
|
* 要你拍板的那一下(人门)就发生在某条车道上——所以三样都在这张图上,
|
|
1831
1831
|
* 而不是散在三个页签里(「进展」那一格因此撤了)。
|
|
1832
1832
|
*/
|
|
@@ -2020,7 +2020,7 @@ window.__ModuleLoader__.load({
|
|
|
2020
2020
|
}),
|
|
2021
2021
|
),
|
|
2022
2022
|
/**
|
|
2023
|
-
*
|
|
2023
|
+
* **反向跳**:从树里的这一步跳到「事实」那一格,并展开对应的命题。
|
|
2024
2024
|
* 切中栏视图走**原生正门** selectPanel(经可选读法拿到的 layout 服务;守卫:
|
|
2025
2025
|
* 没注册的 key 它会抛,而抛了不该把整棵树带下水)。
|
|
2026
2026
|
*/
|
|
@@ -2134,7 +2134,7 @@ window.__ModuleLoader__.load({
|
|
|
2134
2134
|
* 写动作只留**一个**,而且它走既有的原生机制:
|
|
2135
2135
|
* · 「采纳」→ 人门动词 `promote_skill` → 内核改写 frontmatter(`status: active`)。
|
|
2136
2136
|
* 引用走**原生**的 `/` 技能触发器(输入框里打 `/`,出候选菜单,选一条即注入正文)——
|
|
2137
|
-
*
|
|
2137
|
+
* 我们原来在旁边又放了一个「引用」按钮,做的是同一件事,已砍掉(奥卡姆:重复不是能力)。
|
|
2138
2138
|
*/
|
|
2139
2139
|
function BrainTab(props) {
|
|
2140
2140
|
const sessionId = props.sessionId
|
|
@@ -2154,7 +2154,7 @@ window.__ModuleLoader__.load({
|
|
|
2154
2154
|
*
|
|
2155
2155
|
* 为什么要有这一条:面板的数据来自会话日志,而内核只在 pre-step 里落事实——
|
|
2156
2156
|
* 新建的会话在第一轮对话之前**没有任何日志**,于是页签是空的,可工作区里其实
|
|
2157
|
-
*
|
|
2157
|
+
* 已经躺着模板技能。这条读面直接问宿主的 skills 服务,
|
|
2158
2158
|
* 拿到的就是模型看到的那张合并目录;渲染仍走同一套分组,并如实标注「还没进投影」。
|
|
2159
2159
|
*/
|
|
2160
2160
|
const [live, setLive] = React.useState(null)
|
|
@@ -2208,7 +2208,7 @@ window.__ModuleLoader__.load({
|
|
|
2208
2208
|
}
|
|
2209
2209
|
|
|
2210
2210
|
/**
|
|
2211
|
-
*
|
|
2211
|
+
* 「引用」按钮已删掉(人门动词 `invoke_skill` 一并摘除)。
|
|
2212
2212
|
*
|
|
2213
2213
|
* 原生的 `/` 技能触发器做的正是同一件事,而且更好:`dsh-client-ui-skill` 注册
|
|
2214
2214
|
* `trigger: '/'`,`dsh-client-ui-input-trigger` 出候选菜单,选一条就把技能正文
|
|
@@ -2229,7 +2229,7 @@ window.__ModuleLoader__.load({
|
|
|
2229
2229
|
* 点技能名 → **原生**文档预览(markdown 会渲染成 markdown,不是源码)。
|
|
2230
2230
|
*
|
|
2231
2231
|
* 用内核随投影发下来的 `file`(技能目录下的 SKILL.md):虚拟条目(记忆索引)没有正文文件,
|
|
2232
|
-
* `file` 是 null,面板就不画链接(点进去撞 404
|
|
2232
|
+
* `file` 是 null,面板就不画链接(点进去撞 404 是踩过的坑)。
|
|
2233
2233
|
*/
|
|
2234
2234
|
const pointAt = (skill) => {
|
|
2235
2235
|
if (typeof skill.file !== 'string') return
|
|
@@ -2297,15 +2297,15 @@ window.__ModuleLoader__.load({
|
|
|
2297
2297
|
/**
|
|
2298
2298
|
* 机器读数(字节 / 资源数 / 层级)**不渲染**:它们不帮人认出一条技能,
|
|
2299
2299
|
* 20 条排下来就是噪声(真数据这一格 4741 字里很大一份是它们 ✗),
|
|
2300
|
-
* 而且藏成 `display:none` 也还是 DOM 里的噪声 ⇒ 干脆不画,挂到技能名的 tooltip
|
|
2300
|
+
* 而且藏成 `display:none` 也还是 DOM 里的噪声 ⇒ 干脆不画,挂到技能名的 tooltip。
|
|
2301
2301
|
*/
|
|
2302
2302
|
candidate ? h(Chip, { disabled: busy, onClick: () => promote(skill.name), title: t('采纳:改写 frontmatter,模型从此加载得到它') }, t('采纳')) : null,
|
|
2303
|
-
//
|
|
2303
|
+
// 混合路径:同一件事也可以摆到原生提问卡上答(卡里带它的描述与后果)。
|
|
2304
2304
|
candidate ? h('span', { className: busy ? undefined : 'clearai-link', style: S.faint, onClick: busy ? undefined : () => promote(skill.name, true), title: t('用原生提问卡决定') }, t('用提问卡决定')) : null,
|
|
2305
2305
|
),
|
|
2306
2306
|
/**
|
|
2307
2307
|
* 描述只留**认得出这条技能的那一句**(真数据里每条是一整段「适用/不适用」✗,
|
|
2308
|
-
* 18 条一起看就是信息爆炸)。全文在技能名的 tooltip 里,要选技能用原生的 `/`
|
|
2308
|
+
* 18 条一起看就是信息爆炸)。全文在技能名的 tooltip 里,要选技能用原生的 `/` 触发菜单。
|
|
2309
2309
|
*/
|
|
2310
2310
|
skill.description === null || skill.description === undefined || skill.description === ''
|
|
2311
2311
|
? null
|
|
@@ -2326,7 +2326,7 @@ window.__ModuleLoader__.load({
|
|
|
2326
2326
|
{ style: S.bar },
|
|
2327
2327
|
h('span', { style: S.title }, t('技能 · 记忆')),
|
|
2328
2328
|
/**
|
|
2329
|
-
* 计数在下面各段标题里(
|
|
2329
|
+
* 计数在下面各段标题里(同屏重复计数 = 噪声);
|
|
2330
2330
|
* 「与模型看到的是同一张表」是实现保证、不是用户信息,与事实那一格同一条规矩 → 去掉。
|
|
2331
2331
|
* 只留**状态**:目录到没到、这份是不是"本会话还没有第一轮对话"的工作区现状。
|
|
2332
2332
|
*/
|
|
@@ -2336,8 +2336,8 @@ window.__ModuleLoader__.load({
|
|
|
2336
2336
|
/**
|
|
2337
2337
|
* 章程那一行(这个页签的第一层):**只有文件系统事实** + 一个点开就走的原生预览。
|
|
2338
2338
|
*
|
|
2339
|
-
* 为什么不再有「占位 X/Y
|
|
2340
|
-
*
|
|
2339
|
+
* 为什么不再有「占位 X/Y 条」:那是对文本做格式解析
|
|
2340
|
+
* ——模型把章程第 5 节写成 `- **变更记录**(一行即…)`,条目数就会数错,
|
|
2341
2341
|
* **改一个标点「事实」就变**;而且它奖励的是「把数字清掉」而不是「把章程写实」。
|
|
2342
2342
|
* 章程每回合本来就被原生指令文件整份注入模型上下文,面板这边只需要说清
|
|
2343
2343
|
* 「它在哪、什么时候动过」——`mtime` 不依赖任何格式约定。
|
|
@@ -2390,7 +2390,7 @@ window.__ModuleLoader__.load({
|
|
|
2390
2390
|
h(Link, { onClick: () => props.openPreview?.(file.path) }, h('span', { style: S.mono }, file.file)),
|
|
2391
2391
|
/**
|
|
2392
2392
|
* 一个记忆文件里的条目可能十几条 ⇒ 行里只给**前两条 + 共几条**,
|
|
2393
|
-
* 全文进 tooltip(
|
|
2393
|
+
* 全文进 tooltip(默认少而准,细节靠悬停/点开)。
|
|
2394
2394
|
*/
|
|
2395
2395
|
h(
|
|
2396
2396
|
'span',
|
|
@@ -2428,7 +2428,7 @@ window.__ModuleLoader__.load({
|
|
|
2428
2428
|
const send = (item, extra) => {
|
|
2429
2429
|
setBusy(`${item.kind}:${item.plan || ''}:${item.step || ''}`)
|
|
2430
2430
|
setError(null)
|
|
2431
|
-
// `gate`
|
|
2431
|
+
// `gate` 只有「用提问卡决定」那一步用得上:它是**界面手势**,不是事实动词。
|
|
2432
2432
|
const body = { sessionId, action: item.human_action, plan: item.plan ?? null, fork: extra?.fork ?? null, branch: extra?.branch ?? null, gate: extra?.gate ?? null, skill: extra?.skill ?? null }
|
|
2433
2433
|
fetch('/api/clearai/gate', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })
|
|
2434
2434
|
.then(readResponse)
|
|
@@ -2441,7 +2441,7 @@ window.__ModuleLoader__.load({
|
|
|
2441
2441
|
/**
|
|
2442
2442
|
* 收件箱里现在只有**真门**(计划确认已砍:它是记号不是闸门,见 fold 的 HUMAN_GATE_ACTIONS)。
|
|
2443
2443
|
*
|
|
2444
|
-
*
|
|
2444
|
+
* 门要什么由**数据**说(`item.needs`),不在这里按 kind 猜 ✗:
|
|
2445
2445
|
* · `click` ⇒ 给按钮(白名单动词:裁决 / 采纳);
|
|
2446
2446
|
* · `word` ⇒ 给**一句提示**(「说一句话就行 —— <要说什么>」),
|
|
2447
2447
|
* 因为那种门本来就不是点击能表达的(复核 / 解除阻塞),而它照样会按住续跑。
|
|
@@ -2460,7 +2460,7 @@ window.__ModuleLoader__.load({
|
|
|
2460
2460
|
{ key: `adopt-${fork.id}`, style: S.gateRow },
|
|
2461
2461
|
h('span', { style: S.mono }, `${fork.stepId} · ${fork.question}`),
|
|
2462
2462
|
/**
|
|
2463
|
-
*
|
|
2463
|
+
* 混合路径:同一道门,也可以摆到**原生提问卡**上去答。
|
|
2464
2464
|
* 为什么两条路都留:面板这条是熟手的快路(一步到位);提问卡那条把
|
|
2465
2465
|
* 判据、各分支的读数与推荐**铺在对话框里**,而且它会挡住输入框——
|
|
2466
2466
|
* 人正在看对话时,那是最不会被忽略的位置。两条路的落账完全相同。
|
|
@@ -2574,7 +2574,7 @@ window.__ModuleLoader__.load({
|
|
|
2574
2574
|
/**
|
|
2575
2575
|
* 续跑档的两个小工具(纯函数,便于直接断言文案)。
|
|
2576
2576
|
*
|
|
2577
|
-
*
|
|
2577
|
+
* **说行为,不说机制**:人要回答的是「我走开的时候它还接着干吗」,不是「这是哪个档」。
|
|
2578
2578
|
* 哲学词(人在场 / 无人值守)留在文档、运行态卡与提示词里——那些读者是模型和写文档的人;
|
|
2579
2579
|
* 给人点的那一格说人话,并在 tooltip 里注明两个词是一回事。
|
|
2580
2580
|
*
|
|
@@ -2589,7 +2589,7 @@ window.__ModuleLoader__.load({
|
|
|
2589
2589
|
const tierLabel = (value, pending) => `${TIER_TEXT[value] ?? value}${pending ? '(已切,下一步生效)' : ''}`
|
|
2590
2590
|
|
|
2591
2591
|
/**
|
|
2592
|
-
* 「多问我 /
|
|
2592
|
+
* 「多问我 / 自己跑」那个开关**删掉了**。
|
|
2593
2593
|
*
|
|
2594
2594
|
* 第一性原理 + 奥坎姆:「我要不要在场」是**运行时状态**——有没有门开着、有没有裁决在飞、
|
|
2595
2595
|
* 有没有开着的步;这些系统自己知道 ✗,不该要用户预先声明。
|
|
@@ -2597,9 +2597,9 @@ window.__ModuleLoader__.load({
|
|
|
2597
2597
|
* 续跑本身走**原生 goal**(宿主的回合驱动);我们只决定"什么时候该布防、给多大保险丝"。
|
|
2598
2598
|
*/
|
|
2599
2599
|
/**
|
|
2600
|
-
*
|
|
2600
|
+
* 计划的小图形:一棵**迷你世界树**——一根脊柱、一条岔、两个节点。
|
|
2601
2601
|
* 用 `currentColor`:颜色留给状态(等人确认 = 琥珀,受阻 = 琥珀,收尾 = 灰),形状留给语义。
|
|
2602
|
-
* 为什么要有它:工具行是要抢地盘的地方(
|
|
2602
|
+
* 为什么要有它:工具行是要抢地盘的地方(两个字面项能占掉整行近三成,
|
|
2603
2603
|
* 窄窗口下会把发送键挤到第二行)。图标能省一半宽度,而它指向的正是「世界树」那一页。
|
|
2604
2604
|
*/
|
|
2605
2605
|
function PlanGlyph() {
|
|
@@ -2615,7 +2615,7 @@ window.__ModuleLoader__.load({
|
|
|
2615
2615
|
}
|
|
2616
2616
|
|
|
2617
2617
|
/**
|
|
2618
|
-
*
|
|
2618
|
+
* 计划芯片:坐在原生 plan 座位上的那一格。
|
|
2619
2619
|
*
|
|
2620
2620
|
* 只说**计划**这一件事(进度、受阻、待确认),不重复状态条已经说过的话——
|
|
2621
2621
|
* 一个事实只在一块面上说,是这个面板与原生 dock 之间的分工。
|
|
@@ -2630,16 +2630,16 @@ window.__ModuleLoader__.load({
|
|
|
2630
2630
|
const total = plan.totalCount ?? 0
|
|
2631
2631
|
const done = plan.advancedCount ?? 0
|
|
2632
2632
|
/**
|
|
2633
|
-
*
|
|
2633
|
+
* 说人话:内部 id(`p-xxxx`)不进这一格——它对人没有信息量,只让芯片变长;
|
|
2634
2634
|
* 状态优先于数字(等人确认 / 受阻 / 已收尾都是**要人注意**的那一类)。
|
|
2635
2635
|
*/
|
|
2636
2636
|
/**
|
|
2637
|
-
*
|
|
2637
|
+
* 一格只放**一个符号**:进度,或者一件要人注意的事。
|
|
2638
2638
|
* 要人动手的那件事(等你确认)由**输入框下那条**说「需要你 N」——它在事实面上,
|
|
2639
2639
|
* 而这里只是工具行里的一个指针,不能把两个事实挤在一格里。
|
|
2640
2640
|
*/
|
|
2641
2641
|
/**
|
|
2642
|
-
*
|
|
2642
|
+
* 输入框下**单独占一行**的派生状态条已删掉 ——
|
|
2643
2643
|
* 「已达成 · 100%」与原生目标提示、与这颗 chip 的 `3/8` 说的是同一件事 ✗,
|
|
2644
2644
|
* 却把输入框整行顶上去 ✗。留下的只有**可点、且只有我们知道**的两件:
|
|
2645
2645
|
* 需要你 N(人门计数,点了开世界树)· 续跑停着(为什么停,人是可以处置的)
|
|
@@ -2660,7 +2660,7 @@ window.__ModuleLoader__.load({
|
|
|
2660
2660
|
const meaning = pending ? t('计划在建,**等你确认**') : blocked ? t('计划受阻,等人处置') : plan.status === 'closed' ? `${t('计划已收尾(')}${done}/${total}${t(' 步)')}` : `${t('计划已交付 ')}${done}/${total}${t(' 步')}`
|
|
2661
2661
|
/**
|
|
2662
2662
|
* 等人时**只说一句**:先「需要你 N」(人门计数),没有门才说续跑停着的原因。
|
|
2663
|
-
* 两者是同一根轴(为什么在等人)⇒
|
|
2663
|
+
* 两者是同一根轴(为什么在等人)⇒ 一格只放一个,不并列。
|
|
2664
2664
|
*/
|
|
2665
2665
|
const waiting = inboxCount > 0 ? `${t('需要你 ')}${inboxCount}` : null
|
|
2666
2666
|
const attentionNow = attention || waiting !== null
|
|
@@ -2687,7 +2687,7 @@ window.__ModuleLoader__.load({
|
|
|
2687
2687
|
* 面板要说的正是「平台说不出的那一句」——为什么停。新增理由时这张表跟着补,
|
|
2688
2688
|
* 补漏了也不骗人:认不出来的值原样显示,而不是显示一个体面的空话。
|
|
2689
2689
|
*/
|
|
2690
|
-
/** 窗口停下的理由 = **真的有人的事**(
|
|
2690
|
+
/** 窗口停下的理由 = **真的有人的事**(「一阶段收尾」那条已删:它是档位的表达,不是人的事)。 */
|
|
2691
2691
|
const HOLD_WHY = lazyTable(() => ({
|
|
2692
2692
|
audit: t('等独立裁决'),
|
|
2693
2693
|
plan_confirm: t('计划待确认'),
|
|
@@ -2696,7 +2696,7 @@ window.__ModuleLoader__.load({
|
|
|
2696
2696
|
}))
|
|
2697
2697
|
|
|
2698
2698
|
/**
|
|
2699
|
-
*
|
|
2699
|
+
* 一步的**人话**名字:`do` 是模型写的那句「做什么」,截短了放进一行。
|
|
2700
2700
|
* 内部步 id(`s-3`)只进 tooltip 与世界树——它在日志里是账,在界面上不是给人看的。
|
|
2701
2701
|
*/
|
|
2702
2702
|
function shortDo(text) {
|
|
@@ -2705,7 +2705,7 @@ window.__ModuleLoader__.load({
|
|
|
2705
2705
|
}
|
|
2706
2706
|
|
|
2707
2707
|
/**
|
|
2708
|
-
*
|
|
2708
|
+
* **续跑注**:只在**有话要说**时出现的一格。
|
|
2709
2709
|
*
|
|
2710
2710
|
* 输入框下那条整行删掉之后,它说的三句话里有两句**别处无处可说** ✗:
|
|
2711
2711
|
* · 「续跑已撤回」——人按下清除的那一瞬,dock 已经消失,而内核还没到下一拍;
|
|
@@ -2740,7 +2740,7 @@ window.__ModuleLoader__.load({
|
|
|
2740
2740
|
}
|
|
2741
2741
|
|
|
2742
2742
|
/**
|
|
2743
|
-
*
|
|
2743
|
+
* 输入框下那条派生状态条**删掉了**。
|
|
2744
2744
|
*
|
|
2745
2745
|
* 它说的事(阶段 / 完成度 / 当前步)与**原生目标提示**、与工具行那颗计划 chip 的 `N/M`
|
|
2746
2746
|
* 说的是同一件 ✗ —— 而它**独占一行,把输入框整个顶上去** ✗。
|
|
@@ -2751,7 +2751,7 @@ window.__ModuleLoader__.load({
|
|
|
2751
2751
|
* 席位跟着会话预设进出。
|
|
2752
2752
|
*
|
|
2753
2753
|
* 为什么必须这样:`conversation.view` 是**产品级**插座,它的主人遍历全部注册项投影出
|
|
2754
|
-
* 视图导航,不按会话过滤(
|
|
2754
|
+
* 视图导航,不按会话过滤(宿主原生那个视图页签组件的 viewTabs() 只读 id 与 label)。
|
|
2755
2755
|
* 所以一个宿主平面的插件一旦注册,每个会话、每种模式都会多出一个页。
|
|
2756
2756
|
* 这个面板是循证模式的一部分,它只在那个模式里该出现——于是注册与注销都跟着
|
|
2757
2757
|
* 当前会话的 `projectionValues.agentPreset` 走。
|
|
@@ -2782,7 +2782,7 @@ window.__ModuleLoader__.load({
|
|
|
2782
2782
|
* 语言变了要做两件事:① 让**组件**重画;② 把**座位重挂**一遍。
|
|
2783
2783
|
*
|
|
2784
2784
|
* ② 不是洁癖:座位上的标签是**注册那一刻**求值的,只重画组件的话,中栏页签会
|
|
2785
|
-
* 停在上一种语言(
|
|
2785
|
+
* 停在上一种语言(失效模式:整页已经切成中文,页签还写着 Deliverables / Facts)。
|
|
2786
2786
|
* 走宿主自己的 `locale/change` 事件(它声明了这个事件,也正是为这种情况留的)。
|
|
2787
2787
|
*/
|
|
2788
2788
|
ctx.effect(() => {
|
|
@@ -2822,7 +2822,7 @@ window.__ModuleLoader__.load({
|
|
|
2822
2822
|
const seats = []
|
|
2823
2823
|
/**
|
|
2824
2824
|
* `options` 可以是对象,也可以是**函数**。为什么要有函数这一路:座位上的标签在
|
|
2825
|
-
* 注册那一刻就被求值了,传对象等于把语言冻住(
|
|
2825
|
+
* 注册那一刻就被求值了,传对象等于把语言冻住(失效模式:整页切成中文,页签还写着英文)。
|
|
2826
2826
|
* 传函数就让每次注册都重新求值——语言一变重挂一次,标签跟着走。
|
|
2827
2827
|
*/
|
|
2828
2828
|
const occupy = (name, options, component) => {
|
|
@@ -2867,7 +2867,7 @@ window.__ModuleLoader__.load({
|
|
|
2867
2867
|
* 拿不到就留空 ⇒ 原生默认字形。
|
|
2868
2868
|
*/
|
|
2869
2869
|
/**
|
|
2870
|
-
*
|
|
2870
|
+
* 右栏只剩两格:**世界树**(计划的一切:脊柱、车道、闸门、要你拍板的那一下)
|
|
2871
2871
|
* 与 **技能 · 记忆**(外脑)。
|
|
2872
2872
|
*
|
|
2873
2873
|
* 「进展」撤了:它原先装的四段各有归宿——计划与世界线的行归世界树,
|
|
@@ -2915,7 +2915,7 @@ window.__ModuleLoader__.load({
|
|
|
2915
2915
|
openPreview: openPreviewFor(props),
|
|
2916
2916
|
openSpectator,
|
|
2917
2917
|
/**
|
|
2918
|
-
*
|
|
2918
|
+
* 反向跳:记住要展开哪条命题,再用**原生正门**切中栏视图。
|
|
2919
2919
|
* `layout` 走可选读法(契约里就给了 `ctx.get('layout')` 这一路);
|
|
2920
2920
|
* `selectPanel` 对未注册的 key 会抛 ⇒ 包起来,别把整棵树带下水。
|
|
2921
2921
|
*/
|
|
@@ -2964,7 +2964,7 @@ window.__ModuleLoader__.load({
|
|
|
2964
2964
|
/**
|
|
2965
2965
|
* 这条行所属的会话 id —— 预览地址里那一段**必须是它**。
|
|
2966
2966
|
*
|
|
2967
|
-
*
|
|
2967
|
+
* 一个修过的 bug:原来这里是
|
|
2968
2968
|
* `openPreview = (path) => openNativePreview(sidebarRight, typeof path === 'string' ? path : '', path)` ——
|
|
2969
2969
|
* 第二参本该是**会话 id**,却把路径塞了进去。于是地址成了
|
|
2970
2970
|
* `dsh-resource://file/session/products%2Freport.html`,原生读面按 SessionId 查不到
|
|
@@ -2991,28 +2991,28 @@ window.__ModuleLoader__.load({
|
|
|
2991
2991
|
const openPreviewFor = (props) => (path) => openNativePreview(sidebarRight, sessionIdFor(props), path)
|
|
2992
2992
|
|
|
2993
2993
|
/**
|
|
2994
|
-
* 中栏视图:**产物**(
|
|
2994
|
+
* 中栏视图:**产物**(中栏第一眼该看「我拿到了什么」)。
|
|
2995
2995
|
* 原来占着中栏的「循环」面板搬进右栏「进展」——那种全景适合与对话并排,
|
|
2996
2996
|
* 而中栏这个位子(native 已有 chat / trajectory)该留给**结果**。
|
|
2997
2997
|
*/
|
|
2998
2998
|
occupy('conversation.view', () => ({ id: 'clearai-deliverables', order: 15, label: t('产物') }), (props) => h(LocalizedDeliverables, { ...props, openRail, openPreview: openPreviewFor(props) }))
|
|
2999
2999
|
/**
|
|
3000
|
-
*
|
|
3000
|
+
* 中栏视图:**事实**——整条闭环一屏看完(假设是起点,事实是沉淀)。
|
|
3001
3001
|
* 放在「产物」之后:先看拿到了什么,再看**凭什么**(以及已经确认了什么)。
|
|
3002
3002
|
* 这一格与模型读的 `clear/knowledge/facts/INDEX.md` 是**同一张表**。
|
|
3003
3003
|
*/
|
|
3004
3004
|
/**
|
|
3005
|
-
*
|
|
3005
|
+
* 注册时必须把**打开器**交下去:这一格的证据出处、事实原件、跳世界树
|
|
3006
3006
|
* 全靠这两个回调。漏了它们,界面看着能点、点了什么也不会发生——
|
|
3007
|
-
*
|
|
3007
|
+
* 漏了它,「点击证据没反应」。
|
|
3008
3008
|
*/
|
|
3009
3009
|
/**
|
|
3010
|
-
*
|
|
3011
|
-
*
|
|
3010
|
+
* 注册时必须把**打开器**都交下去:证据的四类出处、事实原件、跳世界树全靠它们。
|
|
3011
|
+
* 漏了它们,界面看着能点、点了什么也不会发生。
|
|
3012
3012
|
*/
|
|
3013
3013
|
occupy('conversation.view', () => ({ id: 'clearai-facts', order: 20, label: t('事实') }), (props) => h(LocalizedFacts, { ...props, openRail, openSpectator, openPreview: openPreviewFor(props) }))
|
|
3014
3014
|
/**
|
|
3015
|
-
* **计划面坐在原生 plan
|
|
3015
|
+
* **计划面坐在原生 plan 那个座位上**。
|
|
3016
3016
|
*
|
|
3017
3017
|
* 为什么是这个座位:DSH 把「计划」这件事的控件固定在 composer 工具行的
|
|
3018
3018
|
* `conversation.input.plan` 上(`dsh-client-ui-plan` 的「Plan ×」),而它只在
|
|
@@ -3030,13 +3030,13 @@ window.__ModuleLoader__.load({
|
|
|
3030
3030
|
occupy('conversation.input.plan', { priority: -1 }, (props) => h(PlanChip, { ...props, openRail }))
|
|
3031
3031
|
occupy('conversation.input.left', { id: 'clearai-continuation', order: 110 }, (props) => h(ContinuationNote, props))
|
|
3032
3032
|
/**
|
|
3033
|
-
* **续跑档控制搬进工具行**(
|
|
3033
|
+
* **续跑档控制搬进工具行**(控制归工具行,事实归输入框下方)。
|
|
3034
3034
|
* `conversation.input.left` 是**加法**座位(list),与原生「完全权限」并排——
|
|
3035
3035
|
* 同一类东西(我能改的)放同一行,这是原生的排版,不是我们发明的。
|
|
3036
3036
|
*/
|
|
3037
3037
|
// 输入框下方的常驻派生条(运行态卡在人这一侧的对应物)
|
|
3038
3038
|
/**
|
|
3039
|
-
*
|
|
3039
|
+
* **输入框下那一条不再注册**:它说的话(阶段 / 完成度 / 当前步)与原生目标提示、
|
|
3040
3040
|
* 与工具行那颗计划 chip 重复 ✗,却**独占一行把输入框顶上去** ✗。
|
|
3041
3041
|
* 唯一可点、且只有我们知道的那件事(「需要你 N」)已经并进计划 chip(同一行,一点直达世界树)。
|
|
3042
3042
|
*/
|