cc-viewer 1.7.23 → 1.8.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.
Files changed (237) hide show
  1. package/README.md +19 -0
  2. package/cli.js +92 -11
  3. package/concepts/ar/GlobalSettings.md +2 -2
  4. package/concepts/da/GlobalSettings.md +2 -2
  5. package/concepts/de/GlobalSettings.md +2 -2
  6. package/concepts/en/GlobalSettings.md +2 -2
  7. package/concepts/es/GlobalSettings.md +2 -2
  8. package/concepts/fr/GlobalSettings.md +2 -2
  9. package/concepts/it/GlobalSettings.md +2 -2
  10. package/concepts/ja/GlobalSettings.md +2 -2
  11. package/concepts/ko/GlobalSettings.md +2 -2
  12. package/concepts/no/GlobalSettings.md +2 -2
  13. package/concepts/pl/GlobalSettings.md +2 -2
  14. package/concepts/pt-BR/GlobalSettings.md +2 -2
  15. package/concepts/ru/GlobalSettings.md +2 -2
  16. package/concepts/th/GlobalSettings.md +2 -2
  17. package/concepts/tr/GlobalSettings.md +2 -2
  18. package/concepts/uk/GlobalSettings.md +2 -2
  19. package/concepts/zh/GlobalSettings.md +2 -2
  20. package/concepts/zh-TW/GlobalSettings.md +2 -2
  21. package/dist/assets/{App-CusqoEAB.js → App-CCu0y2Cq.js} +2 -2
  22. package/dist/assets/{MdxEditorPanel-e5DlfcqJ.js → MdxEditorPanel-BBhgTmDk.js} +1 -1
  23. package/dist/assets/{Mobile-B8xwCLgr.js → Mobile-vN4lyiaz.js} +1 -1
  24. package/dist/assets/{ProxyStatsModal-_w677xPM.js → ProxyStatsModal-BOflqr0R.js} +1 -1
  25. package/dist/assets/index-C3BRHDVF.js +2 -0
  26. package/dist/assets/seqResourceLoaders-DI0G5nu1.css +41 -0
  27. package/dist/assets/seqResourceLoaders-DpYqp6SP.js +2 -0
  28. package/dist/index.html +1 -1
  29. package/findcc.js +17 -8
  30. package/node_modules/@ccv/core/package.json +13 -0
  31. package/{server/lib → node_modules/@ccv/core/src}/approval-modal-prefs.js +1 -1
  32. package/node_modules/@ccv/core/src/clearCheckpoint.js +7 -0
  33. package/{src/utils → node_modules/@ccv/core/src}/contentFilter.js +207 -141
  34. package/{server/lib → node_modules/@ccv/core/src}/context-rules.js +2 -2
  35. package/{server/lib → node_modules/@ccv/core/src}/delta-reconstructor.js +60 -4
  36. package/{server/lib → node_modules/@ccv/core/src}/error-report.js +5 -4
  37. package/{server/lib → node_modules/@ccv/core/src}/session-boundary.js +3 -3
  38. package/{src/utils → node_modules/@ccv/core/src}/teammateDetector.js +9 -0
  39. package/{server/lib → node_modules/@ccv/core/src}/tools-xml-formatter.js +3 -3
  40. package/{server/lib → node_modules/@ccv/core/src}/v2-transcript-normalizer.js +1 -1
  41. package/{server/lib → node_modules/@ccv/core/src}/voice-pack-events.js +2 -2
  42. package/package.json +11 -97
  43. package/server/_paths.js +28 -7
  44. package/server/i18n.js +60 -0
  45. package/server/interceptor.js +36 -11
  46. package/server/lib/adapters/dingtalk-adapter.js +2 -2
  47. package/server/lib/adapters/discord-adapter.js +2 -2
  48. package/server/lib/adapters/feishu-adapter.js +2 -2
  49. package/server/lib/adapters/wecom-adapter.js +2 -2
  50. package/server/lib/approval-policy.js +29 -0
  51. package/server/lib/{ask-store.js → ask/ask-store.js} +3 -3
  52. package/server/lib/{perm-bridge.js → ask/perm-bridge.js} +5 -5
  53. package/server/lib/chat-queue.js +341 -0
  54. package/server/lib/cli-inject.js +1 -1
  55. package/server/lib/context-watcher.js +3 -3
  56. package/server/lib/create_system_prompt.js +0 -9
  57. package/server/lib/enrich-plan-input.js +1 -1
  58. package/server/lib/ensure-hooks.js +2 -2
  59. package/server/lib/file-access-policy.js +6 -1
  60. package/server/lib/file-api.js +17 -1
  61. package/server/lib/{dingtalk-bridge.js → im/dingtalk-bridge.js} +1 -1
  62. package/server/lib/{im-append-system.js → im/im-append-system.js} +5 -13
  63. package/server/lib/{im-bridge-core.js → im/im-bridge-core.js} +12 -6
  64. package/server/lib/{im-config.js → im/im-config.js} +1 -1
  65. package/server/lib/im/im-deny.js +4 -0
  66. package/server/lib/{im-lang.js → im/im-lang.js} +1 -1
  67. package/server/lib/{im-lock.js → im/im-lock.js} +2 -2
  68. package/server/lib/{im-process-manager.js → im/im-process-manager.js} +1 -1
  69. package/server/lib/{im-senders.js → im/im-senders.js} +1 -1
  70. package/server/lib/{im-skills.js → im/im-skills.js} +2 -2
  71. package/server/lib/interceptor-core.js +8 -5
  72. package/server/lib/kv-cache-analyzer.js +6 -5
  73. package/server/lib/launch-config.js +321 -0
  74. package/server/lib/log-file-utils.js +21 -0
  75. package/server/lib/log-management.js +6 -17
  76. package/server/lib/log-stream.js +1 -1
  77. package/server/lib/log-watcher.js +1 -1
  78. package/server/lib/model-system-prompts.js +1 -1
  79. package/server/lib/prefs-store.js +2 -2
  80. package/server/lib/project-prefs.js +6 -6
  81. package/server/lib/project-state.js +15 -0
  82. package/server/lib/{proxy-retry.js → proxy/proxy-retry.js} +2 -2
  83. package/server/lib/{proxy-stats.js → proxy/proxy-stats.js} +3 -3
  84. package/server/lib/sdk-adapter.js +6 -74
  85. package/server/lib/sdk-manager.js +383 -278
  86. package/server/lib/skills-api.js +1 -1
  87. package/server/lib/spawn-model-resolver.js +21 -6
  88. package/server/lib/stats-worker.js +2 -2
  89. package/server/lib/system-prompt-files.js +11 -10
  90. package/server/lib/system-prompt-render.js +23 -7
  91. package/server/lib/system-prompt-snapshots.js +452 -0
  92. package/server/lib/updater.js +63 -3
  93. package/server/lib/user-prompt-extract.js +5 -5
  94. package/server/lib/v2/adapter.js +3 -3
  95. package/server/lib/v2/conversation-store.js +1 -1
  96. package/server/lib/v2/convert.js +3 -3
  97. package/server/lib/v2/jsonl-read.js +1 -1
  98. package/server/lib/v2/live-feed.js +3 -3
  99. package/server/lib/v2/meta-rows.js +3 -3
  100. package/server/lib/v2/replay.js +1 -1
  101. package/server/lib/v2/session-list.js +1 -1
  102. package/server/lib/v2/session-owner.js +1 -1
  103. package/server/lib/v2/v2-writer.js +26 -1
  104. package/server/lib/v2/verify.js +1 -1
  105. package/server/lib/voice-pack-manager.js +4 -5
  106. package/server/lib/wire-compress.js +1 -1
  107. package/server/proxy.js +5 -5
  108. package/server/pty-manager.js +282 -184
  109. package/server/routes/ask-perm.js +1 -1
  110. package/server/routes/dingtalk.js +3 -3
  111. package/server/routes/events.js +2 -2
  112. package/server/routes/im.js +5 -5
  113. package/server/routes/preferences.js +3 -3
  114. package/server/routes/workspaces.js +1 -1
  115. package/server/server.js +207 -58
  116. package/server/system-prompt-templates/systemPromptModel.md +0 -8
  117. package/server/system-prompt-templates/systemPromptVariables.ar.md +0 -7
  118. package/server/system-prompt-templates/systemPromptVariables.da.md +0 -7
  119. package/server/system-prompt-templates/systemPromptVariables.de.md +0 -7
  120. package/server/system-prompt-templates/systemPromptVariables.es.md +0 -7
  121. package/server/system-prompt-templates/systemPromptVariables.fr.md +0 -7
  122. package/server/system-prompt-templates/systemPromptVariables.it.md +0 -7
  123. package/server/system-prompt-templates/systemPromptVariables.ja.md +0 -7
  124. package/server/system-prompt-templates/systemPromptVariables.ko.md +0 -7
  125. package/server/system-prompt-templates/systemPromptVariables.md +0 -7
  126. package/server/system-prompt-templates/systemPromptVariables.no.md +0 -7
  127. package/server/system-prompt-templates/systemPromptVariables.pl.md +0 -7
  128. package/server/system-prompt-templates/systemPromptVariables.pt-BR.md +0 -7
  129. package/server/system-prompt-templates/systemPromptVariables.ru.md +0 -7
  130. package/server/system-prompt-templates/systemPromptVariables.th.md +0 -7
  131. package/server/system-prompt-templates/systemPromptVariables.tr.md +0 -7
  132. package/server/system-prompt-templates/systemPromptVariables.uk.md +0 -7
  133. package/server/system-prompt-templates/systemPromptVariables.zh-TW.md +0 -7
  134. package/server/system-prompt-templates/systemPromptVariables.zh.md +0 -7
  135. package/dist/assets/index-D6FmFsIz.js +0 -2
  136. package/dist/assets/seqResourceLoaders-3K77Znll.js +0 -2
  137. package/dist/assets/seqResourceLoaders-BgD_Rwvc.css +0 -41
  138. package/src/utils/apiUrl.js +0 -26
  139. package/src/utils/askFallback.js +0 -21
  140. package/src/utils/askOptionDesc.js +0 -55
  141. package/src/utils/askPortalMatcher.js +0 -54
  142. package/src/utils/autoApproveOptions.js +0 -27
  143. package/src/utils/avatarAnimationPostPass.js +0 -49
  144. package/src/utils/builtinPresets.js +0 -18
  145. package/src/utils/clearCheckpoint.js +0 -7
  146. package/src/utils/commandValidator.js +0 -45
  147. package/src/utils/contextRaw.js +0 -18
  148. package/src/utils/contextSidebarNavigation.js +0 -16
  149. package/src/utils/contextTurns.js +0 -117
  150. package/src/utils/displayScaleHelper.js +0 -47
  151. package/src/utils/effectiveModel.js +0 -17
  152. package/src/utils/entry-slim.js +0 -563
  153. package/src/utils/entryCache.js +0 -144
  154. package/src/utils/errorReport.js +0 -6
  155. package/src/utils/fileExpandedPathsStorage.js +0 -70
  156. package/src/utils/fileIcons.jsx +0 -36
  157. package/src/utils/fileOpen.js +0 -37
  158. package/src/utils/formatters.js +0 -48
  159. package/src/utils/gitApi.js +0 -50
  160. package/src/utils/gitTreeBuilder.js +0 -19
  161. package/src/utils/helpers.js +0 -854
  162. package/src/utils/identityHeal.js +0 -108
  163. package/src/utils/imConnState.js +0 -59
  164. package/src/utils/imOrigin.js +0 -24
  165. package/src/utils/imTr.js +0 -7
  166. package/src/utils/imageCompress.js +0 -83
  167. package/src/utils/imageDownscale.js +0 -28
  168. package/src/utils/imageResize.js +0 -112
  169. package/src/utils/ingestPipeline.js +0 -47
  170. package/src/utils/isProxyMode.js +0 -11
  171. package/src/utils/lazyWithReload.js +0 -53
  172. package/src/utils/markdown.js +0 -30
  173. package/src/utils/markdownIncremental.js +0 -39
  174. package/src/utils/markdownProfiler.js +0 -113
  175. package/src/utils/mdExtensionDetect.js +0 -64
  176. package/src/utils/memoryLinkParser.js +0 -44
  177. package/src/utils/modalMask.js +0 -15
  178. package/src/utils/modelSuggestions.js +0 -48
  179. package/src/utils/monotime.js +0 -6
  180. package/src/utils/pinnedMenu.js +0 -55
  181. package/src/utils/presetShortcuts.js +0 -14
  182. package/src/utils/projectAlias.js +0 -135
  183. package/src/utils/promptClassifier.js +0 -112
  184. package/src/utils/promptDetect.js +0 -196
  185. package/src/utils/promptNav.js +0 -60
  186. package/src/utils/ptyChunkBuilder.js +0 -251
  187. package/src/utils/quickMenuHoverIntent.js +0 -35
  188. package/src/utils/rateLimitParser.js +0 -119
  189. package/src/utils/readResultPool.js +0 -106
  190. package/src/utils/refreshCachedItemProp.js +0 -30
  191. package/src/utils/resizeCalc.js +0 -21
  192. package/src/utils/resolveLocalized.js +0 -17
  193. package/src/utils/searchApi.js +0 -47
  194. package/src/utils/searchReplace.js +0 -51
  195. package/src/utils/seqResourceLoaders.js +0 -79
  196. package/src/utils/sessionManager.js +0 -482
  197. package/src/utils/sessionMerge.js +0 -239
  198. package/src/utils/skillModalController.js +0 -111
  199. package/src/utils/skillsParser.js +0 -116
  200. package/src/utils/slashCommandLabels.js +0 -80
  201. package/src/utils/splitDragCalc.js +0 -73
  202. package/src/utils/stickyBottomController.js +0 -643
  203. package/src/utils/svgSanitize.js +0 -48
  204. package/src/utils/systemTags.js +0 -47
  205. package/src/utils/tClaude.js +0 -20
  206. package/src/utils/teamModalBuilder.js +0 -351
  207. package/src/utils/teamSessionParser.js +0 -190
  208. package/src/utils/teammateAvatars.js +0 -194
  209. package/src/utils/termDiag.js +0 -114
  210. package/src/utils/terminalClipboard.js +0 -117
  211. package/src/utils/terminalWriteQueue.js +0 -398
  212. package/src/utils/thinkingMerge.js +0 -49
  213. package/src/utils/toolCatalog.js +0 -30
  214. package/src/utils/toolResultBuilder.js +0 -411
  215. package/src/utils/toolResultClassifier.js +0 -16
  216. package/src/utils/toolResultCore.js +0 -194
  217. package/src/utils/toolsDiff.js +0 -44
  218. package/src/utils/toolsXmlFormatter.js +0 -3
  219. package/src/utils/ultraplanController.js +0 -109
  220. package/src/utils/ultraplanExperts.js +0 -86
  221. package/src/utils/ultraplanTemplates.js +0 -154
  222. package/src/utils/userImageRefs.js +0 -49
  223. package/src/utils/v3Assembler.js +0 -125
  224. package/src/utils/v3Rows.js +0 -49
  225. package/src/utils/voicePackPlayer.js +0 -233
  226. package/src/utils/webSearchGrouping.js +0 -111
  227. package/src/utils/workflowFormat.js +0 -52
  228. package/src/utils/workflowRuns.js +0 -118
  229. package/src/utils/workflowStore.js +0 -94
  230. /package/{src/utils → node_modules/@ccv/core/src}/requestType.js +0 -0
  231. /package/server/lib/{ask-bridge.js → ask/ask-bridge.js} +0 -0
  232. /package/server/lib/{ask-constants.js → ask/ask-constants.js} +0 -0
  233. /package/server/lib/{ask-reaper.js → ask/ask-reaper.js} +0 -0
  234. /package/server/lib/{dingtalk-config.js → im/dingtalk-config.js} +0 -0
  235. /package/server/lib/{im-log-watcher.js → im/im-log-watcher.js} +0 -0
  236. /package/server/lib/{proxy-env.js → proxy/proxy-env.js} +0 -0
  237. /package/server/lib/{proxy-errors.js → proxy/proxy-errors.js} +0 -0
@@ -1,106 +0,0 @@
1
- /**
2
- * Read tool_result content intern pool.
3
- *
4
- * 1.6.237 后实测:同一 .jsx 文件被 87 个 SubAgent / 父 user message 各持一份完整副本(30MB+ 重复)。
5
- * 对 Read 工具的 resultText 做 content-addressed dedup,让相同内容共享同一字符串引用。
6
- * hash 仅用 length + 前 64 / 后 64 字符避免大字符串 O(n) hash;当 length > 512
7
- * 再加采中段 64 字符(共三段 192 字节),令仅中段不同的长串也能区分;碰撞概率极低。
8
- *
9
- * 抽到独立模块(无外部依赖)便于 node --test 直接 import,避免被
10
- * toolResultBuilder.js 的传递依赖(./helpers 无 .js 后缀)污染。
11
- */
12
-
13
- // 容量上限设计:1000 vs entry-slim.js 的 _toolsPool/_systemPool 上限 200,差异源自流量域不同。
14
- // tools/system 是 entry-level 配置数组,全 session 命中分布稳定,实测 pool size <5(200 极宽松);
15
- // readResultPool 池化的是 tool_result 字符串(v4 派生层 + v5 raw payload),
16
- // 单 session 唯一字符串数量与 tool 调用次数同阶(v4 实测 4400 calls / pool size 363)。
17
- // B 项加入 raw payload 后预期再 +20-40%,1000 留 ~30% 余量;超阈值由 _poolEvictions 暴露。
18
- const _MAX_READ_POOL_SIZE = 1000;
19
- const _MIN_DEDUP_LEN = 256;
20
- const _SIG_MID_THRESHOLD = 512;
21
- const _readResultPool = new Map();
22
- let _poolEvictions = 0;
23
-
24
- // v5: sig 增加 mid-slice(当 length > 512)。对齐 entry-slim.js _systemSig 的边界增强模式。
25
- // B 项把 raw payload tool_result block.content 也接入此 pool(含 Bash tab-padded / log 类
26
- // 长度+前后缀重合的结构化输出),暴露面变大。length+first 64+last 64 在该场景对碰撞抗性
27
- // 不足,加 mid-64 后要求"长度+三段共 192 字节"全匹配才视为同串。
28
- function _readResultSig(s) {
29
- if (s.length > _SIG_MID_THRESHOLD) {
30
- const midIdx = Math.floor(s.length / 2);
31
- return s.length + ':' + s.slice(0, 64) + ':' + s.slice(midIdx, midIdx + 64) + ':' + s.slice(-64);
32
- }
33
- return s.length + ':' + s.slice(0, 64) + ':' + s.slice(-64);
34
- }
35
-
36
- /**
37
- * 把 Read 工具的 resultText 替换为 pool 中的共享引用(命中时)或注册新值。
38
- * 短字符串(< 256)跳过 dedup(不值得 sig 开销)。
39
- *
40
- * @param {string} s - resultText 原文
41
- * @returns {string} 池化后的字符串引用
42
- */
43
- export function internReadResult(s) {
44
- if (typeof s !== 'string' || s.length < _MIN_DEDUP_LEN) return s;
45
- const sig = _readResultSig(s);
46
- const pooled = _readResultPool.get(sig);
47
- if (pooled !== undefined) return pooled;
48
- if (_readResultPool.size >= _MAX_READ_POOL_SIZE) {
49
- _readResultPool.delete(_readResultPool.keys().next().value);
50
- _poolEvictions++;
51
- }
52
- _readResultPool.set(sig, s);
53
- return s;
54
- }
55
-
56
- /** 测试辅助:清空 Read intern pool 和 eviction counter。 */
57
- export function _resetReadPoolForTest() {
58
- _readResultPool.clear();
59
- _poolEvictions = 0;
60
- }
61
-
62
- /** 测试辅助:观察 pool 当前 size。 */
63
- export function _getReadPoolSizeForTest() {
64
- return _readResultPool.size;
65
- }
66
-
67
- /** 诊断:FIFO 淘汰累计次数。典型 session <50;>200 提示 _MAX_READ_POOL_SIZE 需上调。 */
68
- export function _getReadPoolEvictionsForTest() {
69
- return _poolEvictions;
70
- }
71
-
72
- /**
73
- * 通用版:Read 之外的所有 tool_result(Bash/Grep/Glob/MCP/...)也走同一 pool。
74
- * 算法与 internReadResult 完全相同(同 _readResultSig + 同 _readResultPool),
75
- * 名字区分只是语义上的扩展,便于 toolResultBuilder.js 默认全 intern。
76
- *
77
- * @param {string} s - tool_result 的 resultText 原文
78
- * @returns {string} 池化后的字符串引用
79
- */
80
- export function internToolResult(s) {
81
- return internReadResult(s);
82
- }
83
-
84
- /**
85
- * 命中-aware 变体:仅在 pool 已有相同 sig 时返回共享 ref,否则注册并返回 null。
86
- *
87
- * 设计动机:JS string `===` 是值比较,调用方拿到 internReadResult 的返回值无法判断"这是
88
- * pool 命中(应替换 block.content 节省内存)"还是"刚注册(保持原 ref 即可)"。
89
- * 此函数把命中信号显式上抛——`null` = 未命中或短结果(无需替换),非 null = 命中(应替换)。
90
- * v5 entry-slim.js 的 lazy-clone 路径依赖这个信号决定是否触发 messages 浅克隆。
91
- *
92
- * @param {string} s - tool_result 内容原文
93
- * @returns {string|null} 命中时返回 pool 共享 ref;未命中(已注册)或短结果(< 256)返回 null
94
- */
95
- export function internToolResultIfPooled(s) {
96
- if (typeof s !== 'string' || s.length < _MIN_DEDUP_LEN) return null;
97
- const sig = _readResultSig(s);
98
- const pooled = _readResultPool.get(sig);
99
- if (pooled !== undefined) return pooled;
100
- if (_readResultPool.size >= _MAX_READ_POOL_SIZE) {
101
- _readResultPool.delete(_readResultPool.keys().next().value);
102
- _poolEvictions++;
103
- }
104
- _readResultPool.set(sig, s);
105
- return null;
106
- }
@@ -1,30 +0,0 @@
1
- import React from 'react';
2
-
3
- /**
4
- * 刷新缓存的 assistant ChatMessage element 上随 ChatView `_sessionItemCache` FULL HIT 而冻结的
5
- * 过期 map prop。仅对「持有指定 toolName 的 tool_use」的 element 做 cloneElement 注入 nextMap,
6
- * 其余原样返回;prevMap===nextMap 时返回原数组(零分配)。
7
- *
8
- * 背景:FULL HIT 路径直接返回 `sc.items` 时 React reconciler 看到完全相同的 element 引用就跳过
9
- * diff,ChatMessage SCU 根本不会被调用,元素创建时冻结的旧 map(planApprovalMap / askAnswerMap)
10
- * 永不刷新——导致 ExitPlanMode 审批后卡片不切「已批准」、AskUserQuestion 答完后仍显示 pending。
11
- * 本函数合并自原 refreshPlanApprovalCache / refreshAskAnswerCache 两个孪生模块。
12
- *
13
- * @param {Array} items - 缓存的 ChatMessage React element 数组
14
- * @param {object} prevMap - 上一轮存入 cache 时的 map 引用
15
- * @param {object} nextMap - 本轮派生的 map 引用
16
- * @param {string} toolName - 触发刷新的 tool_use 名('ExitPlanMode' / 'AskUserQuestion')
17
- * @param {string} propName - 要刷新的 prop 名('planApprovalMap' / 'askAnswerMap')
18
- * @returns {Array} 引用全等时为 items;否则为新数组(仅命中者被 cloneElement)
19
- */
20
- export function refreshCachedItemProp(items, prevMap, nextMap, toolName, propName) {
21
- if (prevMap === nextMap) return items;
22
- let dirty = false;
23
- const out = items.map(m => {
24
- if (!m || !m.props || m.props.role !== 'assistant' || !Array.isArray(m.props.content)) return m;
25
- if (!m.props.content.some(b => b.type === 'tool_use' && b.name === toolName)) return m;
26
- dirty = true;
27
- return React.cloneElement(m, { [propName]: nextMap });
28
- });
29
- return dirty ? out : items;
30
- }
@@ -1,21 +0,0 @@
1
- // 拖拽 resize 的纯几何计算 —— 把 startX/Y/curX/curY/startW/startH 换成 clamp 后的 {w, h}。
2
- // 单测可独立验证 4 个 clamp 边界 + 任意方向(dirX/dirY ∈ {-1, +1}),不依赖 DOM/React。
3
- //
4
- // 方向参数语义:
5
- // dirX = -1 → 鼠标往左拉时 w 变大(handle 在左侧,例:左上 / 左下)
6
- // dirX = +1 → 鼠标往右拉时 w 变大(handle 在右侧,例:右上 / 右下)
7
- // dirY 同理(-1 = handle 在上侧)
8
- //
9
- // 调用方需保证 clamp 区间合法(min ≤ max);若不合法则 max 取胜(避 NaN)。
10
- export function calcResizedSize({ startX, startY, curX, curY, startW, startH, dirX, dirY, clamp }) {
11
- const dx = (curX - startX) * dirX;
12
- const dy = (curY - startY) * dirY;
13
- let w = startW + dx;
14
- let h = startH + dy;
15
- const { minW, maxW, minH, maxH } = clamp || {};
16
- if (typeof minW === 'number' && w < minW) w = minW;
17
- if (typeof maxW === 'number' && w > maxW) w = maxW;
18
- if (typeof minH === 'number' && h < minH) h = minH;
19
- if (typeof maxH === 'number' && h > maxH) h = maxH;
20
- return { w: Math.round(w), h: Math.round(h) };
21
- }
@@ -1,17 +0,0 @@
1
- // 把「纯字符串 或 {lang: str} 本地化对象」解析成当前语言下的字符串。
2
- // 用于 ultraAgents 预设专家的 title / description:服务端原样下发本地化对象,
3
- // 前端按 getLang() 解析,语言切换无需重新请求。(content 是单语言字符串,不走本处。)
4
- //
5
- // 回退顺序:精确语言 → 去区域后的主语言(zh-TW→zh, pt-BR→pt) → en → zh → 首个非空字符串值。
6
- // 异常输入(数组 / 数字 / null / 空对象 / 全空串对象)一律安全返回 '',绝不抛错。
7
- export function resolveLocalized(field, lang) {
8
- if (typeof field === 'string') return field;
9
- if (!field || typeof field !== 'object' || Array.isArray(field)) return '';
10
- const pick = (k) => (typeof field[k] === 'string' && field[k].trim() ? field[k] : null);
11
- const base = typeof lang === 'string' ? lang.split('-')[0] : '';
12
- return pick(lang)
13
- || pick(base)
14
- || pick('en')
15
- || pick('zh')
16
- || (Object.values(field).find(v => typeof v === 'string' && v.trim()) || '');
17
- }
@@ -1,47 +0,0 @@
1
- import { apiUrl } from './apiUrl';
2
- import { reportSwallowed } from './errorReport';
3
-
4
- /**
5
- * Search the project codebase for `params.query`.
6
- * @param {object} params { query, caseSensitive, wholeWord, regex, includeGlobs, excludeGlobs }
7
- * @param {AbortSignal} [signal] abort a superseded / cancelled search
8
- * @returns {Promise<{results, truncated, engine, filesScanned, error?}>}
9
- * @throws on network failure or non-OK status (except 400 invalid_regex, surfaced as {error})
10
- */
11
- export async function searchCode(params, signal) {
12
- const res = await fetch(apiUrl('/api/search'), {
13
- method: 'POST',
14
- headers: { 'Content-Type': 'application/json' },
15
- body: JSON.stringify(params),
16
- signal,
17
- });
18
- if (res.status === 400) {
19
- // invalid_regex is an expected, user-facing outcome — return it as data, not a throw.
20
- const data = await res.json().catch(() => ({ error: 'error' }));
21
- return { results: [], truncated: false, engine: 'none', filesScanned: 0, error: data.error || 'error' };
22
- }
23
- if (!res.ok) throw new Error(`search failed: ${res.status}`);
24
- return res.json();
25
- }
26
-
27
- /**
28
- * Replace matches across files (or dry-run to count).
29
- * @param {object} params { query, caseSensitive, wholeWord, regex, includeGlobs, excludeGlobs,
30
- * replacement, scope:'all'|'file'|'match', file?, line?, col?, expectText?,
31
- * skipPaths?, dryRun? }
32
- * @returns {Promise<{changed, skipped, total, error?}>}
33
- */
34
- export async function replaceInFiles(params, signal) {
35
- const res = await fetch(apiUrl('/api/search-replace'), {
36
- method: 'POST',
37
- headers: { 'Content-Type': 'application/json' },
38
- body: JSON.stringify(params),
39
- signal,
40
- });
41
- if (res.status === 400) {
42
- const data = await res.json().catch(() => ({ error: 'error' }));
43
- return { changed: [], skipped: [], total: 0, error: data.error || 'error' };
44
- }
45
- if (!res.ok) throw new Error(`replace failed: ${res.status}`);
46
- return res.json();
47
- }
@@ -1,51 +0,0 @@
1
- // Client mirror of the server's replace primitives (server/lib/code-replace.js +
2
- // buildQueryRegExp/looksCatastrophic in code-search.js). Kept byte-for-byte equivalent so the
3
- // inline replace preview equals exactly what the server writes, and so single-match targeting
4
- // uses the SAME (node/JS) match positions the server will — never a ripgrep-derived submatch
5
- // offset that could disagree. Parity is pinned by test/search-replace-preview.test.js.
6
-
7
- export function escapeRegExp(s) {
8
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
- }
10
-
11
- export function buildQueryRegExp({ query, regex, wholeWord, caseSensitive }) {
12
- let pattern = regex ? query : escapeRegExp(query);
13
- if (wholeWord) pattern = `\\b(?:${pattern})\\b`;
14
- const flags = 'g' + (caseSensitive ? '' : 'i');
15
- return new RegExp(pattern, flags);
16
- }
17
-
18
- export function looksCatastrophic(pattern) {
19
- return /\([^()]*[+*}][^()]*\)\s*[+*]/.test(pattern)
20
- || /\([^()]*[+*][^()]*\)\{\d/.test(pattern);
21
- }
22
-
23
- /** Non-global clone (for native $-expansion of a single match). */
24
- export function noGlobal(reG) {
25
- return new RegExp(reG.source, reG.flags.replace('g', ''));
26
- }
27
-
28
- /** regex → native JS $-substitution; literal → verbatim (no $ interpretation). */
29
- export function applyMatch(matchedText, reNoG, regex, replacement) {
30
- if (!regex) return replacement;
31
- return matchedText.replace(reNoG, replacement);
32
- }
33
-
34
- /**
35
- * For a clicked match, recover the node-engine match position on the line so the server can
36
- * re-verify it. Finds the JS-regex match covering `submatchStart`; returns its index + text.
37
- * @returns {{col:number, expectText:string}|null}
38
- */
39
- export function computeMatchTarget(lineText, submatchStart, reG) {
40
- reG.lastIndex = 0;
41
- let m;
42
- let fallback = null;
43
- while ((m = reG.exec(lineText)) !== null) {
44
- if (m[0] === '') { reG.lastIndex++; continue; }
45
- if (m.index === submatchStart) { reG.lastIndex = 0; return { col: m.index, expectText: m[0] }; }
46
- if (m.index <= submatchStart && submatchStart < m.index + m[0].length) fallback = { col: m.index, expectText: m[0] };
47
- if (m.index > submatchStart) break;
48
- }
49
- reG.lastIndex = 0;
50
- return fallback;
51
- }
@@ -1,79 +0,0 @@
1
- // 三态加载 + seq 防污 共用实现(class 组件用)。
2
- // 三态契约:state[key] === null → 未加载(loading)
3
- // state[key] === false → 加载失败
4
- // state[key] === <data> → 加载成功
5
- // 组件 instance 上必须存在 `_<name>Seq` 计数字段(constructor 初始化为 0)。
6
- // workspace 切换时 componentDidUpdate 应 `++this._<name>Seq` 失效在途请求。
7
- //
8
- // ─── 抽取边界(有意为之,请勿扩张)────────────────────────────────────────────
9
- // 这里**只覆盖 lazy-load 静默路径**:组件首次挂载/workspace 切换时自动 fire,失败
10
- // 落 false 让上游 chip / popover 进入空态。
11
- // 用户主动触发的 refresh(带 toast、loading spinner、错误 message)刻意保留在
12
- // AppHeader.jsx (handleRefreshMemory) / Mobile.jsx 各自的 inline 实现里,原因:
13
- // 1) 主动 refresh 需要 caller 决定 toast 文案、loading 视觉、错误反馈策略;
14
- // 若硬塞进 loader 内部,loader 必须 import message → 反向耦合 antd UI 层。
15
- // 2) loadMemoryDetail / loadClaudeMdDetail 也是用户点击驱动,含「成功后跳详情面板」
16
- // 副作用,同样不属于"加载即更新 state"语义,留 caller 自管更清晰。
17
- // 即:本文件 = 静默路径,inline = 用户触发路径。两者并存是设计选择,不是抽取漏。
18
- // ────────────────────────────────────────────────────────────────────────────
19
-
20
- import { apiUrl } from './apiUrl.js';
21
-
22
- async function _seqGuardedFetch(component, seqProp, url) {
23
- const seq = ++component[seqProp];
24
- let r;
25
- try {
26
- r = await fetch(url);
27
- } catch (e) {
28
- if (seq !== component[seqProp]) return { stale: true };
29
- return { stale: false, ok: false, error: e, errorKind: 'network' };
30
- }
31
- try {
32
- const data = await r.json();
33
- if (seq !== component[seqProp]) return { stale: true };
34
- return { stale: false, ok: r.ok, status: r.status, data };
35
- } catch (e) {
36
- if (seq !== component[seqProp]) return { stale: true };
37
- return { stale: false, ok: false, error: e, errorKind: 'parse', status: r.status };
38
- }
39
- }
40
-
41
- // 拉取项目文件系统 skills。
42
- // 失败时若 _fsSkills 已是数组(前次成功结果)→ 保留不 clobber,
43
- // 避免 popover chip 从乐观态回退到历史空态。
44
- // 返回 { ok: true, skills } / { ok: false, reason: 'local_log'|'stale'|'http:NNN'|'network'|'parse'|<server msg> }
45
- export async function loadFsSkills(component, { isLocalLog } = {}) {
46
- if (isLocalLog) return { ok: false, reason: 'local_log' };
47
- const res = await _seqGuardedFetch(component, '_fsSkillsSeq', apiUrl('/api/skills'));
48
- if (res.stale) return { ok: false, reason: 'stale' };
49
- if (res.error) {
50
- component.setState(prev => ({ _fsSkills: Array.isArray(prev._fsSkills) ? prev._fsSkills : false }));
51
- return { ok: false, reason: res.errorKind || res.error.message || 'network' };
52
- }
53
- if (!res.ok || !res.data?.ok || !Array.isArray(res.data?.skills)) {
54
- const reason = (res.data && res.data.error) || `http:${res.status}`;
55
- component.setState(prev => ({ _fsSkills: Array.isArray(prev._fsSkills) ? prev._fsSkills : false }));
56
- return { ok: false, reason };
57
- }
58
- component.setState({ _fsSkills: res.data.skills });
59
- return { ok: true, skills: res.data.skills };
60
- }
61
-
62
- // 拉取项目入口 MEMORY.md。lazy-load 失败静默回退 false;用户主动刷新走 handleRefreshMemory(带 toast)。
63
- export async function loadProjectMemory(component) {
64
- const res = await _seqGuardedFetch(component, '_memorySeq', apiUrl('/api/project-memory'));
65
- if (res.stale) return;
66
- if (res.error || !res.ok) { component.setState({ _memory: false }); return; }
67
- component.setState({ _memory: res.data });
68
- }
69
-
70
- // 拉取 CLAUDE.md 候选清单。三态:null/false/[]/[{id,scope,tail,...}]。
71
- export async function loadClaudeMdList(component) {
72
- const res = await _seqGuardedFetch(component, '_claudeMdSeq', apiUrl('/api/claude-md'));
73
- if (res.stale) return;
74
- if (res.error || !res.ok || !Array.isArray(res.data?.entries)) {
75
- component.setState({ _claudeMd: false });
76
- return;
77
- }
78
- component.setState({ _claudeMd: res.data.entries });
79
- }