prts-terrarchive 0.2.1 → 0.2.3

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.
Files changed (45) hide show
  1. package/README.en.md +100 -23
  2. package/README.md +82 -18
  3. package/bin/build-mainline-catalog.py +195 -0
  4. package/contracts/corpus_i18n_v1.schema.json +10 -12
  5. package/contracts/corpus_tools_v1.schema.json +30 -1
  6. package/docs/endfield-mainline-navigation.md +45 -0
  7. package/lib/client.js +157 -27
  8. package/lib/skins/common.css +12 -2
  9. package/lib/skins/endfield-aic.css +1 -1
  10. package/lib/skins/prts-agent.css +9 -7
  11. package/package.json +7 -3
  12. package/presets/definition.js +1 -1
  13. package/presets/prts/preset.yml +1 -1
  14. package/resources/endfield-mainline.json +4632 -0
  15. package/skills/prts-composition/SKILL.md +3 -1
  16. package/skills/prts-investigation/SKILL.md +19 -113
  17. package/skills/prts-investigation/references/board-guide.md +131 -0
  18. package/skills/prts-retrieval/SKILL.md +4 -2
  19. package/skills/prts-retrieval/references/cloud-tools.md +3 -1
  20. package/skills/prts-retrieval/references/local-tools.md +3 -3
  21. package/skills/prts-retrieval/references/module-arknights.md +1 -1
  22. package/skills/prts-retrieval/references/module-dual.md +1 -1
  23. package/skills/prts-retrieval/references/module-endfield.md +3 -0
  24. package/skills/prts-retrieval/references/tools-runtime.md +22 -10
  25. package/src/cloud-projection.js +23 -2
  26. package/src/cloud.js +24 -0
  27. package/src/entity-recognizer.js +26 -5
  28. package/src/entity-routing.js +18 -4
  29. package/src/evidence-state.js +4 -2
  30. package/src/i18n.js +79 -30
  31. package/src/index.js +346 -162
  32. package/src/installer.js +86 -14
  33. package/src/investigation-tools.js +40 -17
  34. package/src/navigation.js +342 -0
  35. package/src/read-args.js +86 -0
  36. package/src/read.js +66 -15
  37. package/src/search-projection.js +154 -19
  38. package/src/search.js +177 -21
  39. package/src/skill.js +25 -16
  40. package/src/skin-capabilities.js +28 -0
  41. package/src/store.js +127 -3
  42. package/src/timeline.js +86 -2
  43. package/src/tool-args.js +53 -0
  44. package/src/ui.js +158 -34
  45. package/src/web-guidance.js +14 -2
@@ -19,30 +19,30 @@
19
19
  },
20
20
  "query": {
21
21
  "type": "string",
22
- "description": "名称或原句;仅有片段时使用 match_mode=literal"
22
+ "description": "名称或原句"
23
23
  },
24
24
  "title": {
25
25
  "type": "string",
26
- "description": "已有检索结果的完整篇章标题,与 query/document_uid/text_ids 四选一"
26
+ "description": "已有结果的完整篇章标题"
27
27
  },
28
28
  "document_uid": {
29
29
  "type": "string",
30
- "description": "已有检索结果的文档定位,替代 title"
30
+ "description": "已有结果的文档定位"
31
31
  },
32
32
  "line": {
33
33
  "type": "integer",
34
- "description": "已有中文结果中的行号,只用于定位对应文本/记录"
34
+ "description": "已有中文结果中的行号"
35
35
  },
36
36
  "text_ids": {
37
37
  "type": "array",
38
38
  "items": {
39
39
  "type": "string"
40
40
  },
41
- "description": "显式请求 ID 后可用于精确复查;普通查询不需要"
41
+ "description": "返回的文本 ID 字符串,用于精确复查"
42
42
  },
43
43
  "source_language": {
44
44
  "type": "string",
45
- "description": "原句的语言,默认 CN;也接受 en/ja/ko/zh-CN 等标准代码"
45
+ "description": "原句语言,默认 CN;支持 en/ja/ko/zh-CN 等代码"
46
46
  },
47
47
  "languages": {
48
48
  "type": "array",
@@ -57,11 +57,11 @@
57
57
  "exact",
58
58
  "literal"
59
59
  ],
60
- "description": "原句精确匹配或字面量片段搜索,默认 exact"
60
+ "description": "默认 exact 精确匹配;片段用 literal"
61
61
  },
62
62
  "include_ids": {
63
63
  "type": "boolean",
64
- "description": "默认 false;仅调试、核验或精确复查时返回内部文本 ID 与来源字段"
64
+ "description": "核验时返回内部 ID,默认 false"
65
65
  },
66
66
  "max_matches": {
67
67
  "type": "integer",
@@ -73,14 +73,12 @@
73
73
  },
74
74
  "data_version": {
75
75
  "type": "string",
76
- "description": "绑定已有结果的资料版本,避免混用版本"
76
+ "description": "已有结果的资料版本(可省略;续页给出的短版本号即可)"
77
77
  },
78
78
  "after": {
79
79
  "type": "object",
80
80
  "additionalProperties": false,
81
81
  "required": [
82
- "data_version",
83
- "request_hash",
84
82
  "position",
85
83
  "text_offset"
86
84
  ],
@@ -98,7 +96,7 @@
98
96
  "type": "integer"
99
97
  }
100
98
  },
101
- "description": "仅原样提交工具返回的续页位置;长记录会明确分块返回"
99
+ "description": "原样复制 page.continuation.after"
102
100
  }
103
101
  }
104
102
  },
@@ -67,9 +67,10 @@
67
67
  "minLength": 1
68
68
  },
69
69
  "position": {
70
+ "description": "不透明续页位置,绑定当前安装范围;请原样复制,补装或切换资料范围后重新搜索。",
70
71
  "type": "integer",
71
72
  "minimum": 0,
72
- "maximum": 10000000
73
+ "maximum": 9007199254740991
73
74
  }
74
75
  }
75
76
  },
@@ -750,6 +751,34 @@
750
751
  "type": "string"
751
752
  }
752
753
  },
754
+ "by_game": {
755
+ "type": "object",
756
+ "additionalProperties": false,
757
+ "properties": {
758
+ "arknights": {
759
+ "type": "integer"
760
+ },
761
+ "endfield": {
762
+ "type": "integer"
763
+ }
764
+ }
765
+ },
766
+ "scope": {
767
+ "type": "object",
768
+ "additionalProperties": false,
769
+ "required": [
770
+ "documents",
771
+ "lines"
772
+ ],
773
+ "properties": {
774
+ "documents": {
775
+ "type": "integer"
776
+ },
777
+ "lines": {
778
+ "type": "integer"
779
+ }
780
+ }
781
+ },
753
782
  "warnings": {
754
783
  "type": "array",
755
784
  "items": {
@@ -0,0 +1,45 @@
1
+ # 终末地主线导航
2
+
3
+ `corpus_navigate` 是确定性的本地主线目录入口,不调用云端检索,也不生成剧情内容。玩家不必先知道数据包中的系列名或任务名。
4
+
5
+ ```json
6
+ {}
7
+ {"query":"最新主线"}
8
+ {"query":"1.6的主线"}
9
+ {"query":"丹青渡主线"}
10
+ {"query":"第二章第八进程"}
11
+ {"query":"进程VIII"}
12
+ {"query":"第二章第三进程"}
13
+ ```
14
+
15
+ 仅支持可选 `query` 和 `game:"endfield"`。省略 query 返回完整目录。目录包括序章和第一、二章共13个进程。返回值绑定当前 `data_version`,每个进程提供多个梗概及其 `corpus_search`/`corpus_read` 参数、任务集合入口、上下进程入口和逐项 `availability`。默认目录文本约2300字符,完整分类和来源元数据保留在结构化结果中。
16
+
17
+ 最新仅指目录中当前本地可定位的最新进程,不宣称实时游戏版本;旧资料包会降到实际覆盖边界,同时保留新进程的未安装说明。缺少正文不会被解释为没有剧情。读取时原样使用返回的 `read` 定位,合集续页使用 `page.continuation`,不推断文件名中的时间先后。
18
+
19
+ ## 三种信息分别取证
20
+
21
+ - **任务分类**:原始 `MissionRuntimeAsset/{missionId}.json` 的 `missionType`、`charId`、`missionChapterBitmask`、`sortId`、`missionName.key`。导出器生成的 e/c 前缀分类独立保存。唐唐的 c27m1–c27m5 原始 `missionType=0`,属于第二章进程III的主线导航,且有角色关联。角色 Wiki 的文档类型不等于任务分类。
22
+ - **显示目录与任务顺序**:社区显式有序目录提供旧进程的章/进程/任务顺序。英文任务名经 TextTable.id→官方 EN/CN I18n 表精确连接至原始任务ID,不凭英文同名强行选ID。`sortId` 全为0,不能用于排序;`missionChapterBitmask` 不当作章编号。两处 Work Preparation 与一处 Freedom of Expansion 保留候选和未决状态。第二章进程VIII目前只证实五篇正文成员,跨任务顺序 `unknown`,逐任务 `order=null`。
23
+ - **公开版本归属**:版本主题与完整内部构建号分开。1.1进程III、1.4进程VII有官方开放边界;1.2进程IV–VI来自前后官方边界的推导;1.6进程VIII由社区资料支持。1.0、1.3、1.5的新增进程映射保留未知,不表示该版本无新增主线。返回 `mapping_status`、`evidence`,不截取内部构建号推断公开版本。
24
+
25
+ 第二章进程III跨“意外来袭”和“风平水不静”两个生成系列,因此返回两份梗概;第二章进程I的现有生成梗概名为“准备工作”,保留可验证的现有标题。任务名与进程名同名时,“上一段/下一段”不会把任务前后关系偷换成进程前后关系;结果明确 `navigation_level:"process"` 并说明锚点。
26
+
27
+ ## 重建与验证
28
+
29
+ 目录文件为 `resources/endfield-mainline.json`,构建器为 `bin/build-mainline-catalog.py`。构建器仅用Python标准库,按保存的来源快照和固定资料构建,不在运行工具时联网。默认输入为本仓库的最新导出和同机拆包目录,可通过脚本参数指定;先运行 `--help` 查看可配置路径。
30
+
31
+ ```sh
32
+ python3 -B bin/build-mainline-catalog.py
33
+ node --test test/navigation.test.js
34
+ npm test
35
+ ```
36
+
37
+ 父仓库还提供 `agent/evals/endfield-mainline.mjs RELEASES_DIR [REPORT]`:用实际 CorpusStore 运行玩家问法,读取所有梗概以及最新进程的五个任务原文,验证返回定位器而非只验证结果非空。
38
+
39
+ 来源:
40
+
41
+ - https://endfield.wiki.gg/wiki/Mission/Main?oldid=63259
42
+ - https://endfield.wiki.gg/wiki/Sanctuary_of_Ink?oldid=65256
43
+ - https://endfield.gryphline.com/en-us/news/8907
44
+ - https://endfield.gryphline.com/en-us/news/0751
45
+ - https://endfield.gryphline.com/en-us/news/5200
package/lib/client.js CHANGED
@@ -810,7 +810,13 @@ window.__ModuleLoader__.load({
810
810
  if (conversationRect?.width > 0) {
811
811
  const shellOverlay = document.querySelector('[data-shell-overlay]')
812
812
  const shell = shellOverlay?.parentElement
813
- const sidebarRect = shell?.querySelector(':scope > div:nth-of-type(1)')?.getBoundingClientRect?.()
813
+ // 侧栏列没有自己的稳定属性,只有 display:contents 的
814
+ // [data-slot="sidebar"] 锚点。优先用"包含该锚点的那一列"定位,
815
+ // nth-of-type 只在老版式下兜底:DSH 0.2.1 的 frame 里新增了
816
+ // shell.bottom 等兄弟节点,靠序号会逐渐不可靠。
817
+ const sidebarColumn = shell?.querySelector(':scope > div:has(> [data-slot="sidebar"])')
818
+ ?? shell?.querySelector(':scope > div:nth-of-type(1)')
819
+ const sidebarRect = sidebarColumn?.getBoundingClientRect?.()
814
820
  const contentLeft = Math.max(conversationRect.left, sidebarRect?.right ?? 0)
815
821
  const contentRightEdge = conversationRect.right
816
822
  const contentCenter = contentLeft + (contentRightEdge - contentLeft) / 2
@@ -1940,15 +1946,91 @@ window.__ModuleLoader__.load({
1940
1946
  message || (saving ? '正在应用…' : '选择后立即生效,并保留当前的明亮 / 暗色偏好。')))
1941
1947
  }
1942
1948
 
1949
+ /* ---- 版本管理:本地占用、保留策略、切换/回滚/删除 ---- */
1950
+ const KEEP_RELEASES_LABEL = 3
1951
+
1952
+ function VersionManagerCard({ releases, activeRelease, previousReleaseId,
1953
+ onActivate, onDelete, downloading, onRefresh }) {
1954
+ const [pruning, setPruning] = useState(false)
1955
+ const [message, setMessage] = useState(null)
1956
+ const rows = [...(releases ?? [])].sort((left, right) =>
1957
+ String(right.createdAt ?? '').localeCompare(String(left.createdAt ?? '')))
1958
+ const totalBytes = rows.reduce((sum, item) => sum + (Number(item.compressedSize) || 0), 0)
1959
+ const activeId = activeRelease?.releaseId ?? rows.find((item) => item.active)?.releaseId ?? null
1960
+ const prune = async () => {
1961
+ setPruning(true)
1962
+ setMessage(null)
1963
+ try {
1964
+ const result = await jsonFetch('/prune', { method: 'POST', body: '{}' })
1965
+ const removed = Array.isArray(result?.removed) ? result.removed : []
1966
+ setMessage(removed.length
1967
+ ? `已清理 ${removed.length} 个旧版本`
1968
+ : '没有可清理的版本')
1969
+ onRefresh?.()
1970
+ } catch (error) {
1971
+ setMessage(error.message)
1972
+ } finally {
1973
+ setPruning(false)
1974
+ }
1975
+ }
1976
+ return h('div', { style: S.card },
1977
+ h('div', { style: { ...S.row, justifyContent: 'space-between' } },
1978
+ h('div', null,
1979
+ h('h3', { style: { ...S.cardTitle, marginBottom: '3px' } }, '版本管理'),
1980
+ h('span', { style: S.hint },
1981
+ `本地 ${rows.length} 个版本 · 合计 ${fmtBytes(totalBytes)} · 自动保留最近 ${KEEP_RELEASES_LABEL} 个(当前 + 回滚目标 + 余量)`)),
1982
+ h('div', { style: S.row },
1983
+ h('button', { style: S.button, disabled: pruning || downloading, onClick: prune },
1984
+ pruning ? '清理中…' : '清理旧版本'),
1985
+ h('button', { style: S.button, onClick: () => onRefresh?.() }, '刷新'))),
1986
+ message && h('span', { style: { ...S.hint, display: 'block', marginTop: '6px' } }, message),
1987
+ rows.length === 0 && h('span', { style: { ...S.hint, display: 'block', marginTop: '8px' } },
1988
+ '本地还没有可用于回滚的历史版本。'),
1989
+ rows.length > 0 && h('div', { style: { marginTop: '8px' } }, ...rows.map((release) => {
1990
+ const isActive = release.releaseId === activeId
1991
+ const isPrevious = release.releaseId === previousReleaseId
1992
+ return h('div', { key: release.releaseId, style: S.versionRow },
1993
+ h('div', { style: { minWidth: 0, flex: 1 } },
1994
+ h('strong', { style: { display: 'block', fontFamily: 'ui-monospace, monospace',
1995
+ fontSize: '12px', overflow: 'hidden', textOverflow: 'ellipsis' } },
1996
+ release.releaseId),
1997
+ h('span', { style: S.hint },
1998
+ `${release.createdAt ? fmtDate(release.createdAt) : '—'}`
1999
+ + ` · ${(release.documentCount ?? 0).toLocaleString()} 篇`
2000
+ + ` · ${fmtBytes(release.compressedSize ?? 0)}`
2001
+ + (release.complete === false ? ' · 目录不完整' : ''))),
2002
+ isActive && h('span', { style: S.badgeGreen }, '当前'),
2003
+ isPrevious && !isActive && h('span', { style: S.badgeGray }, '上一版本'),
2004
+ !isActive && h('button', { style: S.button, disabled: !release.complete,
2005
+ onClick: () => onActivate?.(release.releaseId) },
2006
+ isPrevious ? '回滚' : '切换'),
2007
+ !isActive && h('button', { style: S.button, onClick: () => onDelete?.(release.releaseId) },
2008
+ '删除'))
2009
+ })))
2010
+ }
2011
+
1943
2012
  /* ---- 资料库选择与按游戏展开的版本管理 ---- */
1944
- function DatasetLibraryCard({ status, releases, activeRelease, enabledGames, savingGames,
2013
+ function DatasetLibraryCard({ status, releases, activeRelease, previousReleaseId, enabledGames, savingGames,
1945
2014
  onEnabledGames, onRefresh, onCheckUpdate, checking, updateInfo,
1946
2015
  onActivate, onDelete, onDownload, downloading }) {
1947
2016
  const store = status?.store ?? {}
1948
2017
  const download = status?.download ?? {}
2018
+ // 两个按钮分工:检查更新只查询;下载最新只在「没装」或「已确认有新版」时可用,
2019
+ // 避免两个入口都能触发同一次下载。
2020
+ const downloadAllowed = store.installed === false || updateInfo?.kind === 'update'
1949
2021
  const percent = download.filesTotal
1950
2022
  ? Math.round((download.filesDone / download.filesTotal) * 100) : null
1951
2023
  const [expandedGame, setExpandedGame] = useState(null)
2024
+ // 下载完成后不要一直把进度条钉在设置页(issue #5):done 只保留几秒,
2025
+ // 之后收起;失败状态不自动收起,避免把错误藏起来。
2026
+ const [doneDismissed, setDoneDismissed] = useState(false)
2027
+ const downloadSettled = download.phase === 'done' && !download.active
2028
+ useEffect(() => {
2029
+ setDoneDismissed(false)
2030
+ if (!downloadSettled) return undefined
2031
+ const timer = setTimeout(() => setDoneDismissed(true), 8000)
2032
+ return () => clearTimeout(timer)
2033
+ }, [downloadSettled, download.finishedAt, download.releaseId, download.error])
1952
2034
  const games = [
1953
2035
  { id: 'arknights', title: '明日方舟', subtitle: '剧情、档案、Wiki 与实体资料' },
1954
2036
  { id: 'endfield', title: '明日方舟:终末地', subtitle: '剧情原文、档案库与自建 Wiki' },
@@ -1970,7 +2052,13 @@ window.__ModuleLoader__.load({
1970
2052
  const releaseName = dataset?.releaseTitle
1971
2053
  || knownTitles[dataset?.releaseName] || dataset?.releaseName
1972
2054
  const names = [releaseName, dataset?.gameVersion].filter(Boolean)
1973
- return names.length ? names.join(' · ') : release.releaseId
2055
+ if (names.length) return names.join(' · ')
2056
+ // 明日方舟侧的资料包没有人类可读版本名时,不要把整条 release id 甩给用户;
2057
+ // 优先展示安装日期与数据版本,让人一眼知道「装的是哪一份」。
2058
+ const when = release?.createdAt ? fmtDate(release.createdAt).slice(0, 10) : ''
2059
+ const data = dataset?.dataVersion ? `数据 ${shortVersion(dataset.dataVersion)}` : ''
2060
+ const fallback = [when, data].filter(Boolean).join(' · ')
2061
+ return fallback || release.releaseId
1974
2062
  }
1975
2063
  const versionRows = expandedGame == null ? [] : (releases ?? [])
1976
2064
  .filter((release) => release.datasets?.[expandedGame]?.present)
@@ -1983,8 +2071,14 @@ window.__ModuleLoader__.load({
1983
2071
  h('button', { style: S.button, onClick: onRefresh }, '刷新'),
1984
2072
  h('button', { style: S.button, disabled: checking, onClick: onCheckUpdate },
1985
2073
  checking ? '检查中…' : '检查更新'),
1986
- h('button', { style: S.buttonPrimary, disabled: downloading, onClick: () => onDownload() },
2074
+ h('button', { style: S.buttonPrimary, onClick: () => onDownload(),
2075
+ disabled: downloading || checking || !downloadAllowed,
2076
+ title: downloadAllowed ? '' : '已是最新;先点「检查更新」确认远端是否有新版本' },
1987
2077
  downloading ? '下载中…' : '下载最新'))),
2078
+ h('span', { style: { ...S.hint, display: 'block', marginTop: '6px' } },
2079
+ downloadAllowed
2080
+ ? '检查更新只比对 PRTS.chat 的发布版本,不改本地文件;下载最新会安装该版本。'
2081
+ : '本地已是最新版本;「检查更新」用来确认远端,有新版后这里才能下载。'),
1988
2082
  store.installed === false && h('div', { style: { ...S.error, marginTop: '8px' } },
1989
2083
  `本地资料尚未就绪:${store.installationIssue || '未找到有效版本'}。便携版用户还应确认 ZIP 已完整解压。`),
1990
2084
  store.installed === true && !store.loaded && h('div', { style: { ...S.hint, marginTop: '8px' } },
@@ -2023,16 +2117,21 @@ window.__ModuleLoader__.load({
2023
2117
  updateInfo?.text && h('div', { style: { ...S.row, marginTop: '8px' } },
2024
2118
  h('span', { style: updateInfo.kind === 'error' ? S.error : updateInfo.kind === 'update' ? S.badgeGreen : S.badgeGray },
2025
2119
  updateInfo.text),
2026
- updateInfo.kind === 'update' && updateInfo.remoteReleaseId
2027
- && h('button', { style: S.buttonPrimary, onClick: updateInfo.onDownload }, '下载更新')),
2028
- download.phase && download.phase !== 'idle'
2120
+ // 下载入口统一收到右上角「下载最新」,这里只报状态。
2121
+ ),
2122
+ download.phase && download.phase !== 'idle' && !(downloadSettled && doneDismissed)
2029
2123
  ? h('div', { style: { ...S.row, marginTop: '8px' } },
2030
- h('span', { style: S.key }, download.active ? '下载中' : '下载状态'),
2031
- h('span', { style: S.badge }, `${download.source ?? ''} · ${download.phase}`),
2032
- percent !== null && h('div', { style: S.progressTrack },
2124
+ h('span', { style: S.key }, downloadSettled ? '下载结果' : '下载中'),
2125
+ h('span', { style: downloadSettled ? S.badgeGreen : S.badge },
2126
+ downloadSettled
2127
+ ? `已完成${download.source ? ` · ${download.source}` : ''}`
2128
+ : `${download.source ?? ''} · ${download.phase}`),
2129
+ // 只有进行中的下载才画宽进度条;完成后用一行摘要代替。
2130
+ !downloadSettled && percent !== null && h('div', { style: S.progressTrack },
2033
2131
  h('div', { style: { ...S.progressFill, width: `${percent}%` } })),
2034
2132
  h('span', { style: S.hint },
2035
- `${download.filesDone}/${download.filesTotal ?? '?'} 文件 · ${fmtBytes(download.bytesDone)}`),
2133
+ `${download.filesDone}/${download.filesTotal ?? '?'} 文件 · ${fmtBytes(download.bytesDone)}`
2134
+ + (downloadSettled && download.finishedAt ? ` · ${fmtDate(download.finishedAt)}` : '')),
2036
2135
  download.error && h('span', { style: S.error }, download.error))
2037
2136
  : null,
2038
2137
  expandedGame && h('div', { style: S.versionPanel },
@@ -2051,8 +2150,11 @@ window.__ModuleLoader__.load({
2051
2150
  release.active
2052
2151
  ? h('span', { style: S.badgeGreen }, '当前')
2053
2152
  : h(Fragment, null,
2153
+ release.releaseId === previousReleaseId
2154
+ && h('span', { style: S.badgeGray }, '上一版本'),
2054
2155
  h('button', { style: S.button, disabled: !release.complete,
2055
- onClick: () => onActivate(release.releaseId) }, '切换'),
2156
+ onClick: () => onActivate(release.releaseId) },
2157
+ release.releaseId === previousReleaseId ? '回滚' : '切换'),
2056
2158
  h('button', { style: S.button, onClick: () => onDelete(release.releaseId) }, '删除')))
2057
2159
  }),
2058
2160
  versionRows.length === 0 && h('span', { style: S.hint }, '本地没有这个资料库的版本。')),
@@ -2061,16 +2163,21 @@ window.__ModuleLoader__.load({
2061
2163
  }
2062
2164
 
2063
2165
  /* ---- 配置编辑 ---- */
2166
+ const MIB = 1024 * 1024
2167
+ // 第三项是展示单位换算:存储仍是字节,界面按 MB 给,避免让用户填 33554432。
2064
2168
  const NUMBER_FIELDS = [
2065
2169
  ['cacheShards', '分片缓存数'],
2066
2170
  ['cloudTimeoutMs', '云端超时(ms)'],
2067
- ['cloudMaxResponseBytes', '云端响应上限(B)'],
2171
+ ['cloudMaxResponseBytes', '云端响应上限(MB)', MIB],
2068
2172
  ]
2069
2173
 
2070
2174
  function ConfigCard({ config, onSaved }) {
2071
2175
  const [draft, setDraft] = useState(null)
2072
2176
  const [saving, setSaving] = useState(false)
2073
2177
  const [message, setMessage] = useState(null)
2178
+ // 云端/资料站点地址默认都是 https://prts.chat,绝大多数用户不用改;
2179
+ // 收进「高级」里,避免两个同值 URL 并排让人误以为要填两处。
2180
+ const [showAdvanced, setShowAdvanced] = useState(false)
2074
2181
  if (!config) return null
2075
2182
  const value = draft ?? {}
2076
2183
  const current = (key, fallback) => Object.hasOwn(value, key) ? value[key] : (config[key] ?? fallback)
@@ -2099,25 +2206,38 @@ window.__ModuleLoader__.load({
2099
2206
  h('input', { type: 'checkbox', checked: Boolean(current('cloudEnabled', false)),
2100
2207
  onChange: set('cloudEnabled') }),
2101
2208
  h('span', { style: S.hint }, 'cloud_search / cloud_inspect(保存后即时注册/注销)')),
2102
- h('div', { style: S.field },
2103
- h('span', { style: S.fieldKey }, '云端检索地址'),
2104
- h('input', { style: S.input, value: String(current('cloudBaseUrl', '')),
2105
- onChange: set('cloudBaseUrl') }),
2106
- h('span', { style: { ...S.hint, flexBasis: '100%', marginLeft: '148px' } }, 'cloud_search / cloud_inspect 服务')),
2107
2209
  h('div', { style: S.field },
2108
2210
  h('span', { style: S.fieldKey }, '访问令牌'),
2109
2211
  h('input', { style: S.input, type: 'password',
2110
2212
  placeholder: config.hasCloudToken ? '已设置(不修改即保持)' : '留空使用匿名会话',
2111
2213
  value: String(current('cloudToken', '')), onChange: set('cloudToken') })),
2112
2214
  h('div', { style: S.grid2 },
2113
- ...NUMBER_FIELDS.map(([key, label]) => h('div', { key, style: S.field },
2215
+ ...NUMBER_FIELDS.map(([key, label, scale]) => h('div', { key, style: S.field },
2114
2216
  h('span', { style: S.fieldKey }, label),
2115
2217
  h('input', { style: { ...S.input, flex: '0 0 96px', width: '96px' }, type: 'number',
2116
- value: String(current(key, '')), onChange: set(key, Number) })))),
2218
+ value: String(scale
2219
+ ? Math.round(((Number(current(key, 0)) || 0) / scale) * 100) / 100
2220
+ : current(key, '')),
2221
+ onChange: set(key, (raw) => (scale ? Math.round(Number(raw) * scale) : Number(raw))) })))),
2117
2222
  h('div', { style: S.field },
2118
- h('span', { style: S.fieldKey }, '资料站点地址'),
2119
- h('input', { style: S.input, value: String(current('downloadSiteBaseUrl', '')),
2120
- onChange: set('downloadSiteBaseUrl') })),
2223
+ h('span', { style: S.fieldKey }, '高级'),
2224
+ h('button', { style: S.buttonLink,
2225
+ onClick: () => setShowAdvanced((previous) => !previous) },
2226
+ showAdvanced ? '收起地址设置' : '显示地址设置'),
2227
+ h('span', { style: S.hint }, '云端与资料站点默认都是 https://prts.chat,一般不需要改。')),
2228
+ showAdvanced && h(Fragment, null,
2229
+ h('div', { style: S.field },
2230
+ h('span', { style: S.fieldKey }, '云端检索地址'),
2231
+ h('input', { style: S.input, value: String(current('cloudBaseUrl', '')),
2232
+ onChange: set('cloudBaseUrl') }),
2233
+ h('span', { style: { ...S.hint, flexBasis: '100%', marginLeft: '148px' } },
2234
+ 'cloud_search / cloud_inspect 使用的服务地址')),
2235
+ h('div', { style: S.field },
2236
+ h('span', { style: S.fieldKey }, '资料站点地址'),
2237
+ h('input', { style: S.input, value: String(current('downloadSiteBaseUrl', '')),
2238
+ onChange: set('downloadSiteBaseUrl') }),
2239
+ h('span', { style: { ...S.hint, flexBasis: '100%', marginLeft: '148px' } },
2240
+ '下载资料包时先按 PRTS.chat current 校验版本,再按镜像顺序取文件'))),
2121
2241
  h('div', { style: S.field },
2122
2242
  h('span', { style: S.fieldKey }, '下载源顺序'),
2123
2243
  h('select', { style: S.select, value: current('downloadOrder', ['modelscope', 'site']).join(','),
@@ -2326,13 +2446,17 @@ window.__ModuleLoader__.load({
2326
2446
  return h('div', { style: S.wrap, className: 'prts-settings-wrap' },
2327
2447
  error && h('div', { style: { ...S.card, ...S.error } }, error),
2328
2448
  h(SkinCard, { skin, onChanged: onSkinChanged }),
2329
- h(DatasetLibraryCard, { status, activeRelease,
2449
+ h(DatasetLibraryCard, { status, activeRelease, previousReleaseId: releaseList?.previousId ?? null,
2330
2450
  releases: releaseList?.releases ?? [], enabledGames, savingGames,
2331
2451
  onEnabledGames: updateEnabledGames, onRefresh: refresh,
2332
2452
  onCheckUpdate: checkUpdate, checking, updateInfo: statusUpdateInfo,
2333
2453
  onActivate: activate, onDelete: remove, onDownload: download,
2334
2454
  downloading: busy || Boolean(status?.download?.active),
2335
2455
  }),
2456
+ h(VersionManagerCard, { releases: releaseList?.releases ?? [], activeRelease,
2457
+ previousReleaseId: releaseList?.previousId ?? null,
2458
+ onActivate: activate, onDelete: remove, onRefresh: refresh,
2459
+ downloading: busy || Boolean(status?.download?.active) }),
2336
2460
  h(ConfigCard, { config: status?.config, onSaved: refresh }))
2337
2461
  }
2338
2462
 
@@ -2787,7 +2911,9 @@ window.__ModuleLoader__.load({
2787
2911
  || !Array.isArray(root.content)) return null
2788
2912
  // PTC receipts are their own log-only block, never text to parse or show.
2789
2913
  // Bind all three identities before accepting a durable source projection.
2790
- const receipt = root.content.find((block) => block?.type === 'prts-archive-receipt'
2914
+ // DSH 的 v3→v4 会话迁移会给未知 block 加 `plugin:` 前缀,两种拼写都要认。
2915
+ const receiptType = (type) => type === 'prts-archive-receipt' || type === 'plugin:prts-archive-receipt'
2916
+ const receipt = root.content.find((block) => receiptType(block?.type)
2791
2917
  && block.version === 1 && block.callId === root.callId
2792
2918
  && block.parentCallId === root.parentCallId && block.tool === root.call?.name
2793
2919
  && supported(block.meta) && Array.isArray(block.meta.sources)
@@ -3757,6 +3883,9 @@ window.__ModuleLoader__.load({
3757
3883
  setOpen(value === 'rhine-lab')
3758
3884
  }
3759
3885
  rhineSkinListeners.add(listener)
3886
+ // Skin loading can finish between this component's initial render and
3887
+ // effect subscription. Reconcile the live value for existing sessions.
3888
+ listener(activeSkin)
3760
3889
  return () => rhineSkinListeners.delete(listener)
3761
3890
  }, [])
3762
3891
  useEffect(() => { workbench.current?.update(snapshot) }, [snapshot])
@@ -3844,9 +3973,10 @@ window.__ModuleLoader__.load({
3844
3973
  /* ---- 插件入口:Settings → Plugins 的「PRTS 语料」tab ---- */
3845
3974
  exports.__sceneStateForTest = { buildSceneSnapshotModel, sceneSnapshotSignature,
3846
3975
  collectSnapshotEvidence, buildEvidence, readEvidenceSource, installDialogFocusTrap }
3847
- exports.__rhineStateForTest = { buildRhineSnapshot, mergeArchiveSources, rhineSnapshotSignature, submitRhineQuestion, RhineControl, createRhineHostControls, createRhineSnapshotDiagnostics, createRhineSnapshotSelector }
3976
+ exports.__rhineStateForTest = { buildRhineSnapshot, mergeArchiveSources, rhineSnapshotSignature, submitRhineQuestion, rhineToolPresentation, RhineControl, createRhineHostControls, createRhineSnapshotDiagnostics, createRhineSnapshotSelector }
3848
3977
  exports.__skinStateForTest = {
3849
- setSkin, writeSkinConfig, SkinCard, PrtsSection, openHostSettings, isHostSettingsOpen,
3978
+ setSkin, writeSkinConfig, SkinCard, PrtsSection, VersionManagerCard, DatasetLibraryCard,
3979
+ openHostSettings, isHostSettingsOpen,
3850
3980
  finishAicBoot: () => aicBootDone(),
3851
3981
  getActiveSkin: () => activeSkin,
3852
3982
  getHostSkinSyncGeneration: () => hostSkinSyncGeneration,
@@ -50,16 +50,26 @@ body[data-prts-skin] [data-plugin-panel]>header>div:first-child::before {
50
50
  letter-spacing:.12em;
51
51
  }
52
52
  body[data-prts-skin] [data-plugin-group]>ul {gap:8px}
53
- body[data-prts-skin] :is([data-plugin-package],[data-plugin-item]) {
53
+ body[data-prts-skin] :is([data-plugin-package],[data-plugin-item],[data-plugin-entry]) {
54
54
  margin:0;
55
55
  padding:5px;
56
56
  border:1px solid var(--prts-plugins-rule);
57
57
  border-radius:var(--prts-plugins-radius);
58
58
  background:var(--prts-plugins-card);
59
59
  }
60
- body[data-prts-skin] :is([data-plugin-package],[data-plugin-item]):is(:hover,:focus-within) {
60
+ body[data-prts-skin] :is([data-plugin-package],[data-plugin-item],[data-plugin-entry]):is(:hover,:focus-within) {
61
61
  border-color:var(--prts-plugins-accent);
62
62
  }
63
+ /* DSH 0.2.1 moved the per-entry plugin inventory into a Settings tab. It names
64
+ its entries with data-plugin-entry/module/scope instead of the page cards,
65
+ so the same card treatment is applied there through those attributes. */
66
+ body[data-prts-skin] [data-slot="settings.section"] [data-plugin-scope] {
67
+ border-top:1px solid var(--prts-plugins-rule);
68
+ padding-top:14px;
69
+ }
70
+ body[data-prts-skin] [data-slot="settings.section"] [data-plugin-scope] [data-plugin-entry] code {
71
+ font-family:ui-monospace,monospace;
72
+ }
63
73
  body[data-prts-skin] :is([data-plugin-config],[data-plugin-rows]) {
64
74
  box-sizing:border-box;
65
75
  padding:20px;
@@ -12,7 +12,7 @@ body.aic-chat-resizing,body.aic-chat-resizing *{cursor:col-resize!important;user
12
12
  body[data-prts-skin="endfield-aic"] [data-sidebar-right-panel]{z-index:55}
13
13
  body[data-prts-skin="endfield-aic"] [data-side="rightbar"]{z-index:56}
14
14
  body[data-prts-skin="endfield-aic"]:has(.aic-drawer) :is([data-sidebar-right-panel],[data-side="rightbar"]){z-index:30}
15
- body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div{grid-template-columns:0 minmax(0,1fr) 0!important;background:#14181b}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:first-child{visibility:visible;width:0!important;overflow:visible!important;pointer-events:none}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])).aic-modal-open #root>[data-slot="root"]>div>div:first-child{position:relative;z-index:100!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:first-child [data-slot="sidebar.settings"] button[aria-haspopup="dialog"]{display:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:first-child [data-slot="sidebar.settings"]>[role="presentation"]{visibility:visible;pointer-events:auto}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-shell-overlay="true"]{z-index:40!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"])>div{position:fixed!important;z-index:50;top:104px;bottom:0;left:0;width:var(--prts-aic-band-width,min(clamp(760px,46vw,960px),calc(100vw - 360px)));height:calc(100vh - 104px)!important;min-height:0;overflow:visible!important;background:transparent!important;box-shadow:none;pointer-events:none}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"])>div::before{content:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) :is([data-conversation-scroll],[data-composer-seat],button,textarea,select,details,[contenteditable="true"]){pointer-events:auto}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) textarea,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) select{border-radius:0!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) details,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) pre{border-radius:0!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.composer.bar"]>div{border-color:rgba(255,255,255,.18)!important;background:rgba(10,13,15,.96)!important;box-shadow:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.input.model"] button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.input.plan"] button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.hero.workspace"] button{color:rgba(238,240,235,.78)!important;background:transparent!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="shell.overlay"]{position:fixed;z-index:40;inset:0;pointer-events:none}
15
+ body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div{grid-template-columns:0 minmax(0,1fr) 0!important;background:#14181b}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:has(> [data-slot="sidebar"]){visibility:visible;width:0!important;overflow:visible!important;pointer-events:none}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])).aic-modal-open #root>[data-slot="root"]>div>div:has(> [data-slot="sidebar"]){position:relative;z-index:100!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:has(> [data-slot="sidebar"]) [data-slot="sidebar.settings"] button[aria-haspopup="dialog"]{display:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) #root>[data-slot="root"]>div>div:has(> [data-slot="sidebar"]) [data-slot="sidebar.settings"]>[role="presentation"]{visibility:visible;pointer-events:auto}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-shell-overlay]{z-index:40!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"])>div{position:fixed!important;z-index:50;top:104px;bottom:0;left:0;width:var(--prts-aic-band-width,min(clamp(760px,46vw,960px),calc(100vw - 360px)));height:calc(100vh - 104px)!important;min-height:0;overflow:visible!important;background:transparent!important;box-shadow:none;pointer-events:none}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"])>div::before{content:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) :is([data-conversation-scroll],[data-composer-seat],button,textarea,select,details,[contenteditable="true"]){pointer-events:auto}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) textarea,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) select{border-radius:0!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) details,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) :is([data-slot="conversation"],[data-slot="main.conversation"]) pre{border-radius:0!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.composer.bar"]>div{border-color:rgba(255,255,255,.18)!important;background:rgba(10,13,15,.96)!important;box-shadow:none!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.input.model"] button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.input.plan"] button,body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="conversation.hero.workspace"] button{color:rgba(238,240,235,.78)!important;background:transparent!important}body[data-prts-skin="endfield-aic"]:not(:has([data-plugin-panel])) [data-slot="shell.overlay"]{position:fixed;z-index:40;inset:0;pointer-events:none}
16
16
  .aic-map-reset{position:absolute;z-index:8;top:22px;right:286px;height:26px;padding:0 12px;border:1px solid rgba(255,255,255,.18);border-radius:0;color:rgba(244,245,241,.7);background:rgba(7,9,10,.52);font:9px ui-monospace,monospace;cursor:pointer;pointer-events:auto}.aic-map-reset:hover{border-color:var(--acid);color:var(--acid)}
17
17
  .aic-region-card{position:absolute;z-index:9;right:24px;top:92px;width:300px;padding:16px;border:1px solid rgba(250,255,63,.28);border-left:3px solid var(--acid);color:#e8eae5;background:rgba(7,9,10,.9);box-shadow:0 18px 45px rgba(0,0,0,.32);backdrop-filter:blur(10px)}.aic-region-card small{color:var(--acid);font:8px ui-monospace,monospace;letter-spacing:.1em}.aic-region-card h3{margin:12px 0 1px;font-size:20px}.aic-region-card em{color:rgba(238,240,235,.35);font:9px ui-monospace,monospace}.aic-region-card p{margin:14px 0;color:rgba(238,240,235,.7);font-size:12px;line-height:1.7}.aic-region-card b{color:rgba(238,240,235,.38);font:8px ui-monospace,monospace}
18
18
  .aic-hud{position:absolute;z-index:30;font:10px/1.35 ui-monospace,monospace;letter-spacing:.08em;text-shadow:0 1px 4px #000}.aic-hud-tl{top:24px;left:24px}.aic-hud-tr{top:22px;right:22px;display:flex;align-items:center;gap:8px}.aic-hud-br{right:25px;bottom:20px;color:rgba(238,240,235,.38)}
@@ -3,8 +3,10 @@ body[data-prts-skin="agent"] {
3
3
  color-scheme: light;
4
4
  color: #111214;
5
5
  background: #fff;
6
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
7
- "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
6
+ /* DSH 0.2.1 lets the user pick a font list; --dsw-font-family leads with it,
7
+ so the skin keeps that choice and only supplies the fallback stack. */
8
+ font-family: var(--dsw-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
9
+ "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif);
8
10
  }
9
11
  body[data-prts-skin="agent"] > :has(> [data-shell-overlay]) { position: relative; z-index: 1; }
10
12
  .prts-agent-scene { position: fixed; z-index: 0; inset: 0; display: none; overflow: hidden;
@@ -112,11 +114,11 @@ body[data-prts-skin="agent"] .prts-evidence-layer{display:block}.prts-evidence-s
112
114
  .prts-agent-scene .prts-empty-hero{position:fixed;z-index:2;top:var(--agent-hero-top);right:var(--agent-content-right);left:var(--agent-content-left);text-align:center;transition:opacity .58s ease,transform .72s cubic-bezier(.22,.8,.24,1),filter .58s ease}.prts-agent-scene .prts-hero-identity{position:relative;top:auto;left:auto;display:inline-flex;width:auto;transform:none;flex-direction:column;align-items:flex-start;text-align:left;filter:drop-shadow(0 2px 12px rgba(255,255,255,.95))}.prts-agent-scene .prts-hero-wordmark{position:relative;display:flex;align-items:flex-start}.prts-agent-scene .prts-hero-wordmark svg{display:block;width:clamp(250px,31vw,470px);height:auto}.prts-agent-scene .prts-hero-wordmark em{position:absolute;top:1px;left:calc(100% + 9px);padding:0 6px;border:1px solid rgba(17,18,20,.36);border-radius:2px;color:rgba(255,255,255,.88);background:rgba(17,18,20,.82);font:600 8px/16px var(--agent-mono);letter-spacing:.08em;white-space:nowrap}.prts-agent-scene .prts-hero-identity small{display:flex;align-items:center;gap:9px;margin-top:11px;color:#111214;font:9px/15px var(--agent-mono);letter-spacing:.035em}.prts-agent-scene .prts-hero-identity small s{color:#858a91;text-decoration-color:#f25a5a}.prts-agent-scene .prts-empty-hero h2{margin:16px 0 7px;font-size:26px;line-height:34px;font-weight:500;letter-spacing:-.4px}.prts-agent-scene .prts-empty-hero>p{max-width:600px;margin:0 auto;color:#62666c;font-size:14px;line-height:22px}
113
115
  .prts-agent-scene[data-phase="active"] .prts-empty-hero{opacity:0;transform:translateY(-34px) scale(.96);filter:blur(3px)}.prts-agent-scene[data-phase="active"] .prts-cpu-assembly{opacity:0;translate:0 -148px;transform:translateX(-50%);filter:blur(5px)}.prts-agent-scene[data-phase="active"] :is(.prts-trace-query,.prts-trace-source,.prts-trace-stack,.prts-node-plan,.prts-node-read,.prts-node-recall,.prts-node-verify,.prts-system-ticker){opacity:1}.prts-agent-scene[data-phase="active"] :is(.prts-trace-source,.prts-node-recall,.prts-node-verify,.prts-system-ticker){translate:7vw 8px}.prts-agent-scene[data-phase="active"] .prts-node-plan{opacity:.48}
114
116
  .prts-agent-scene[data-phase="leaving"] .prts-empty-hero{opacity:0;transform:translateY(-34px) scale(.96);filter:blur(3px)}.prts-agent-scene[data-phase="leaving"] .prts-cpu-assembly{opacity:0;translate:0 -118px;transform:translateX(-50%);filter:blur(4px)}.prts-agent-scene[data-phase="leaving"] :is(.prts-trace-query,.prts-trace-source,.prts-trace-stack,.prts-node-plan,.prts-node-read,.prts-node-recall,.prts-node-verify,.prts-system-ticker){opacity:1}.prts-agent-scene[data-phase="leaving"] :is(.prts-trace-source,.prts-node-recall,.prts-node-verify,.prts-system-ticker){translate:7vw 8px}.prts-agent-scene[data-phase="leaving"] .prts-node-plan{opacity:.48}
115
- body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:nth-of-type(1){background:rgba(250,250,247,.045)!important;box-shadow:none!important;backdrop-filter:none!important}
116
- body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:nth-of-type(1) :is(span,b,small,p,h1,h2,h3,label):not([role="dialog"] *){color:transparent!important;background:linear-gradient(180deg,rgba(250,250,247,.96) 0,rgba(250,250,247,.94) 49vh,rgba(20,22,25,.96) 49.08vh,rgba(20,22,25,.96) 76vh) fixed;background-clip:text;-webkit-background-clip:text}body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:nth-of-type(1) svg:not([role="dialog"] *){color:rgba(250,250,247,.92)!important;mix-blend-mode:difference}body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) [data-slot="sidebar.brand.name"]::before,body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) [data-slot="sidebar.brand.name"]::after{color:transparent;background:linear-gradient(180deg,rgba(250,250,247,.96) 0,rgba(250,250,247,.94) 49vh,rgba(20,22,25,.96) 49.08vh,rgba(20,22,25,.96) 76vh) fixed;background-clip:text;-webkit-background-clip:text}
117
- body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:nth-of-type(1) [role="dialog"]{--dsw-alias-button-elevated-fill:rgba(255,255,255,.92);--dsw-alias-button-floating-hover:#efefeb;--dsw-alias-label-primary:#111214;--dsw-alias-label-secondary:#34373b;--dsw-alias-label-tertiary:#62666c;--dsw-alias-label-caption:#858a91;--dsw-alias-border-l1:rgba(17,18,20,.08);--dsw-alias-border-l2:rgba(17,18,20,.13);--dsw-alias-bg-base:#fff;--dsw-alias-bg-layer-1:#f4f4f1;--dsw-alias-bg-layer-2:#fff;--dsw-alias-bg-layer-3:#fff;--dsw-alias-interactive-bg-hover:rgba(17,18,20,.055);--dsw-specific-sidebar-nav-item-hover:rgba(17,18,20,.055);--dsw-specific-sidebar-nav-item-active:rgba(17,18,20,.095);color:#111214!important;background:#fff!important}
118
- body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:nth-of-type(1) [role="dialog"] :is(span,b,small,p,h1,h2,h3,label){color:inherit!important;background:none!important;background-clip:border-box!important;-webkit-background-clip:border-box!important;-webkit-text-fill-color:currentColor!important}body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:nth-of-type(1) [role="dialog"] svg{color:currentColor!important;mix-blend-mode:normal!important}
119
- body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:nth-of-type(1) button:is([aria-label="新会话"],[aria-label="新建会话"],[aria-label="New chat"],[aria-label="New session"],[aria-label="New conversation"]):has(>svg){border-color:rgba(255,255,255,.1)!important;color:#f7f7f4!important;background:rgba(255,255,255,.1)!important}
117
+ body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]){background:rgba(250,250,247,.045)!important;box-shadow:none!important;backdrop-filter:none!important}
118
+ body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) :is(span,b,small,p,h1,h2,h3,label):not([role="dialog"] *){color:transparent!important;background:linear-gradient(180deg,rgba(250,250,247,.96) 0,rgba(250,250,247,.94) 49vh,rgba(20,22,25,.96) 49.08vh,rgba(20,22,25,.96) 76vh) fixed;background-clip:text;-webkit-background-clip:text}body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) svg:not([role="dialog"] *){color:rgba(250,250,247,.92)!important;mix-blend-mode:difference}body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) [data-slot="sidebar.brand.name"]::before,body[data-prts-skin="agent"]:has(.prts-agent-scene[data-page="conversation"]) [data-slot="sidebar.brand.name"]::after{color:transparent;background:linear-gradient(180deg,rgba(250,250,247,.96) 0,rgba(250,250,247,.94) 49vh,rgba(20,22,25,.96) 49.08vh,rgba(20,22,25,.96) 76vh) fixed;background-clip:text;-webkit-background-clip:text}
119
+ body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) [role="dialog"]{--dsw-alias-button-elevated-fill:rgba(255,255,255,.92);--dsw-alias-button-floating-hover:#efefeb;--dsw-alias-label-primary:#111214;--dsw-alias-label-secondary:#34373b;--dsw-alias-label-tertiary:#62666c;--dsw-alias-label-caption:#858a91;--dsw-alias-border-l1:rgba(17,18,20,.08);--dsw-alias-border-l2:rgba(17,18,20,.13);--dsw-alias-bg-base:#fff;--dsw-alias-bg-layer-1:#f4f4f1;--dsw-alias-bg-layer-2:#fff;--dsw-alias-bg-layer-3:#fff;--dsw-alias-interactive-bg-hover:rgba(17,18,20,.055);--dsw-specific-sidebar-nav-item-hover:rgba(17,18,20,.055);--dsw-specific-sidebar-nav-item-active:rgba(17,18,20,.095);color:#111214!important;background:#fff!important}
120
+ body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) [role="dialog"] :is(span,b,small,p,h1,h2,h3,label){color:inherit!important;background:none!important;background-clip:border-box!important;-webkit-background-clip:border-box!important;-webkit-text-fill-color:currentColor!important}body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) [role="dialog"] svg{color:currentColor!important;mix-blend-mode:normal!important}
121
+ body[data-prts-skin="agent"] :has(> [data-shell-overlay])>div:has(> [data-slot="sidebar"]) button:is([aria-label="新会话"],[aria-label="新建会话"],[aria-label="New chat"],[aria-label="New session"],[aria-label="New conversation"]):has(>svg){border-color:rgba(255,255,255,.1)!important;color:#f7f7f4!important;background:rgba(255,255,255,.1)!important}
120
122
  /* DSH 会话行保持原生单行 flex 语义。过去强制改成两行 grid 会把状态
121
123
  的 screen-reader 文本挤回可视区,形成标题与“进行中”重叠。 */
122
124
  body[data-prts-skin="agent"] div[role="treeitem"][aria-selected]:has(>span:nth-child(4)){position:relative;display:flex!important;align-items:center!important;gap:0!important;box-sizing:border-box;height:44px!important;margin:3px 0!important;padding:6px 10px!important;border-radius:12px!important}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prts-terrarchive",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -43,6 +43,8 @@
43
43
  "bin/rhine-detail-baker.ts",
44
44
  "docs/endfield-official-i18n.md",
45
45
  "docs/web-access.md",
46
+ "bin/build-mainline-catalog.py",
47
+ "docs/endfield-mainline-navigation.md",
46
48
  "bin/build-runtime.mjs",
47
49
  "bin/check-runtime.mjs"
48
50
  ],
@@ -65,6 +67,7 @@
65
67
  "homepage": "https://prts.chat",
66
68
  "bugs": "https://github.com/HTian-qwq/prts-terrarchive/issues",
67
69
  "dsh": {
70
+ "manifestVersion": 1,
68
71
  "bundle": {
69
72
  "patch": "./cordis.patch.yml"
70
73
  },
@@ -86,13 +89,14 @@
86
89
  }
87
90
  },
88
91
  "engines": {
89
- "node": ">=22.19"
92
+ "node": ">=22.19",
93
+ "dsh": ">=0.2.0-rc.1"
90
94
  },
91
95
  "scripts": {
92
96
  "test": "node --test --test-concurrency=1 test/*.test.js",
93
97
  "pack:map": "node bin/pack-map-assets.mjs",
94
98
  "pack:map:restore": "node bin/pack-map-assets.mjs --restore",
95
- "check": "node --check src/index.js && node --check src/search.js && node --check src/search-projection.js && node --check src/read.js && node --check src/wiki.js && node --check src/store.js && node --check src/source-map.js && node --check src/cloud-projection.js && node --check src/evidence-state.js && node --check src/entity-routing.js && node --check src/entity-recognizer.js && node --check src/timeline.js && node --check src/cloud.js && node --check src/release-compatibility.js && node --check src/installer.js && node --check src/state.js && node --check src/ui.js && node --check src/skill.js && node --check lib/client.js && node --check bin/install.js && node --check bin/pack-map-assets.mjs && node --check presets/definition.js && node --check presets/register.js && node --check src/release-watch.js && node --check src/archive-presentation.js && node --check bin/preview-rhine.mjs && node --check src/i18n.js && node --check src/localization-format.js && node --check presets/composition.js && node --check src/tools.js && node --check src/composition-skill.js",
99
+ "check": "node --check src/index.js && node --check src/navigation.js && node --check src/search.js && node --check src/search-projection.js && node --check src/read.js && node --check src/wiki.js && node --check src/store.js && node --check src/source-map.js && node --check src/cloud-projection.js && node --check src/evidence-state.js && node --check src/entity-routing.js && node --check src/entity-recognizer.js && node --check src/timeline.js && node --check src/cloud.js && node --check src/release-compatibility.js && node --check src/installer.js && node --check src/state.js && node --check src/ui.js && node --check src/skill.js && node --check lib/client.js && node --check bin/install.js && node --check bin/pack-map-assets.mjs && node --check presets/definition.js && node --check presets/register.js && node --check src/release-watch.js && node --check src/archive-presentation.js && node --check bin/preview-rhine.mjs && node --check src/i18n.js && node --check src/localization-format.js && node --check presets/composition.js && node --check src/tools.js && node --check src/composition-skill.js && node --check src/tool-args.js",
96
100
  "install:prts": "node bin/install.js",
97
101
  "bake:rhine-details": "node bin/bake-rhine-details.mjs",
98
102
  "build:rhine": "npm run bake:rhine-details && tsc -p tsconfig.rhine.json && vite build --config vite.rhine.config.ts",