dsh-code-index 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -29,8 +29,572 @@ __export(client_exports, {
29
29
  name: () => name
30
30
  });
31
31
  module.exports = __toCommonJS(client_exports);
32
+ var import_react2 = require("react");
33
+
34
+ // src/client/impact-xray.tsx
32
35
  var import_react = require("react");
36
+
37
+ // src/impact-xray-model.ts
38
+ var XRAY_LIMITS = { nodes: 32, edges: 48, paths: 12, evidenceChars: 900, metadataBytes: 96e3 };
39
+
40
+ // src/client/xray-locales.ts
41
+ var XRAY_EN = {
42
+ changeCenter: "Changed symbols",
43
+ selectionCenter: "Selected symbols \xB7 not a proven edit",
44
+ noCenter: "No mapped symbol. Inspect unmapped changes in Gaps & limits.",
45
+ snapshotCounts: "In this bounded snapshot",
46
+ callCount: "{count} call relations",
47
+ importCount: "{count} import relations",
48
+ testCount: "{count} test leads",
49
+ weakCount: "{count} name-only candidates",
50
+ unmappedCount: "{count} unmapped entries",
51
+ legendTitle: "Line styles & direction",
52
+ focusView: "Expand view",
53
+ closeView: "Close expanded view",
54
+ subtitle: "See the blast radius before your AI edits the code.",
55
+ calls: "Call graph",
56
+ imports: "Import graph",
57
+ tests: "Test leads",
58
+ paths: "Impact paths",
59
+ gaps: "Gaps & limits",
60
+ potential: "Potential impact, not a runtime guarantee",
61
+ changed: "Changed / selected",
62
+ snapshot: "Source snapshot captured with this result. The working tree may since have changed.",
63
+ exact: "exact \xB7 structurally resolved",
64
+ "import-scoped": "import-scoped \xB7 import scope",
65
+ "name-only": "name-only \xB7 low confidence",
66
+ legend: "Solid: exact. Dashed: import-scoped. Dotted amber: name-only. Arrows follow actual caller \u2192 callee or importer \u2192 module.",
67
+ inspect: "Inspect the evidence",
68
+ reason: "Included because",
69
+ evidenceType: "Evidence type",
70
+ source: "Change source",
71
+ current: "current",
72
+ base: "base",
73
+ empty: "No mapped symbols or relationships. Check unmapped changes and analysis gaps.",
74
+ loading: "Analyzing changes\u2026",
75
+ error: "Impact X-Ray unavailable",
76
+ fallback: 'Showing the original tool text. Rerun with outputFormat="xray" to request fresh evidence.',
77
+ expand: "Expand",
78
+ collapse: "Collapse",
79
+ all: "All confidence levels",
80
+ depth: "Max caller depth",
81
+ rows: "{shown} / {total} relations",
82
+ nodes: "{count} nodes",
83
+ open: "Open current file at line {line}",
84
+ openFailed: "The host could not open this file. The saved source snapshot remains available.",
85
+ noOpen: "Native file preview unavailable for this evidence.",
86
+ raw: "Original tool text",
87
+ back: "Previous selection",
88
+ forward: "Next selection",
89
+ reset: "Reset filters",
90
+ noMatch: "No relations match these filters.",
91
+ uncertain: "Know what remains uncertain",
92
+ caveat: "exact describes structural resolution under the index, not guaranteed runtime behavior. Test leads do not prove coverage. Ownerless calls, dynamic dispatch and historical call graphs may be absent.",
93
+ analysisCut: "The analysis engine truncated these categories",
94
+ displayCut: "The presentation budget omitted some rows or source. Missing rows are not proof of no impact.",
95
+ textCut: "The optional X-Ray text was shortened; the default text tool is unchanged.",
96
+ saved: "Saved PoC result: some newer evidence fields are unavailable. The original relationships are preserved.",
97
+ pathConfidence: "Whole-path confidence",
98
+ incompletePath: "Some path steps are outside this bounded snapshot.",
99
+ pathStep: "Step {step} / {total}",
100
+ noTests: "No related indexed tests found. This does not establish test coverage.",
101
+ noPaths: "No entry-point paths were returned.",
102
+ testCaveat: "A test lead is a candidate to inspect, not proof that the test calls a particular function or will fail.",
103
+ unmapped: "Unmapped changes",
104
+ ranges: "Changed line ranges",
105
+ missing: "No source evidence available.",
106
+ "source-unavailable": "Source is unavailable, stale, unindexed, or outside the repository.",
107
+ "line-unavailable": "The recorded line is no longer available.",
108
+ "source-budget": "Source exceeds the evidence budget. No partial line is presented as complete code.",
109
+ "endpoint-unavailable": "A relationship endpoint is absent from the current index.",
110
+ "import-site-unavailable": "An import lead lacks an indexed import statement or a displayed endpoint.",
111
+ "metadata-budget": "Presentation exceeds its size budget. Select a smaller change set.",
112
+ "declaration": "declaration window",
113
+ "call-site": "actual call site",
114
+ "import-site": "actual import statement",
115
+ "test-lead": "test file context (not a call proof)",
116
+ "unmapped-change": "unmapped diff context",
117
+ "diff-mapping": "diff intersects the declaration",
118
+ "explicit-selection": "explicit selection; not a proven edit",
119
+ "changed-module": "module with a mapped or unmapped change",
120
+ module: "module context",
121
+ "import-dependent": "module imports a changed file",
122
+ "structural-owner": "AST structural owner",
123
+ "local-unique": "same-file name candidate; shadowing is not ruled out",
124
+ "explicit-import-binding": "explicit imported binding",
125
+ "namespace-import": "namespace import scope",
126
+ "resolved-import-module": "name exists in an imported module",
127
+ "global-name": "repository-wide name match",
128
+ "changed-test": "the test file changed",
129
+ "imports-changed-file": "imports a changed file",
130
+ "imports-impact-file": "imports a caller/impact file",
131
+ "references-symbol-name": "matches a changed symbol name",
132
+ "path-convention": "matches a test path convention only",
133
+ "outside-symbol": "changed lines are outside indexed declarations",
134
+ "base-content-unavailable": "baseline content could not be mapped",
135
+ modified: "modified",
136
+ added: "added",
137
+ deleted: "deleted",
138
+ renamed: "renamed",
139
+ unclassified: "unclassified",
140
+ change: "change",
141
+ caller: "direct caller",
142
+ impact: "indirect caller",
143
+ test: "test",
144
+ call: "call",
145
+ import: "import",
146
+ git: "working-tree Git diff",
147
+ diff: "supplied diff",
148
+ files: "explicit files",
149
+ symbols: "explicit symbols"
150
+ };
151
+ var XRAY_ZH = {
152
+ changeCenter: "\u771F\u5B9E\u53D8\u66F4\u4E2D\u5FC3",
153
+ selectionCenter: "\u9009\u62E9\u4E2D\u5FC3 \xB7 \u4E0D\u8BC1\u660E\u5B9E\u9645\u4FEE\u6539",
154
+ noCenter: "\u672A\u6620\u5C04\u5230\u7B26\u53F7\uFF0C\u8BF7\u5728\u201C\u7F3A\u53E3\u4E0E\u8FB9\u754C\u201D\u68C0\u67E5\u672A\u6620\u5C04\u53D8\u66F4\u3002",
155
+ snapshotCounts: "\u672C\u6B21\u6709\u754C\u5FEB\u7167",
156
+ callCount: "{count} \u6761\u8C03\u7528\u5173\u7CFB",
157
+ importCount: "{count} \u6761\u5BFC\u5165\u5173\u7CFB",
158
+ testCount: "{count} \u6761\u6D4B\u8BD5\u7EBF\u7D22",
159
+ weakCount: "{count} \u6761\u4EC5\u540D\u79F0\u5019\u9009",
160
+ unmappedCount: "{count} \u9879\u672A\u6620\u5C04\u53D8\u66F4",
161
+ legendTitle: "\u7EBF\u578B\u4E0E\u7BAD\u5934\u65B9\u5411",
162
+ focusView: "\u653E\u5927\u67E5\u770B",
163
+ closeView: "\u5173\u95ED\u653E\u5927\u89C6\u56FE",
164
+ subtitle: "\u5728 AI \u4FEE\u6539\u4EE3\u7801\u524D\uFF0C\u770B\u6E05\u6F5C\u5728\u5F71\u54CD\u8303\u56F4\u3002",
165
+ calls: "\u8C03\u7528\u56FE",
166
+ imports: "\u5BFC\u5165\u56FE",
167
+ tests: "\u6D4B\u8BD5\u7EBF\u7D22",
168
+ paths: "\u5F71\u54CD\u8DEF\u5F84",
169
+ gaps: "\u7F3A\u53E3\u4E0E\u8FB9\u754C",
170
+ potential: "\u6F5C\u5728\u5F71\u54CD\uFF0C\u4E0D\u662F\u8FD0\u884C\u65F6\u4FDD\u8BC1",
171
+ changed: "\u53D8\u66F4 / \u9009\u62E9\u4E2D\u5FC3",
172
+ snapshot: "\u8FD9\u662F\u672C\u6B21\u7ED3\u679C\u4FDD\u5B58\u7684\u6E90\u7801\u5FEB\u7167\uFF0C\u5DE5\u4F5C\u533A\u968F\u540E\u53EF\u80FD\u5DF2\u7ECF\u53D8\u5316\u3002",
173
+ exact: "exact \xB7 \u7ED3\u6784\u89E3\u6790",
174
+ "import-scoped": "import-scoped \xB7 \u5BFC\u5165\u8303\u56F4",
175
+ "name-only": "name-only \xB7 \u4F4E\u7F6E\u4FE1\u5EA6",
176
+ legend: "\u5B9E\u7EBF\uFF1Aexact\uFF1B\u865A\u7EBF\uFF1Aimport-scoped\uFF1B\u9EC4\u8272\u70B9\u7EBF\uFF1Aname-only\u3002\u7BAD\u5934\u4FDD\u7559\u771F\u5B9E\u8C03\u7528\u8005 \u2192 \u88AB\u8C03\u7528\u8005\u6216\u5BFC\u5165\u8005 \u2192 \u6A21\u5757\u65B9\u5411\u3002",
177
+ inspect: "\u68C0\u67E5\u6E90\u7801\u8BC1\u636E",
178
+ reason: "\u7EB3\u5165\u539F\u56E0",
179
+ evidenceType: "\u8BC1\u636E\u7C7B\u578B",
180
+ source: "\u53D8\u66F4\u6765\u6E90",
181
+ current: "\u5F53\u524D",
182
+ base: "\u57FA\u7EBF",
183
+ empty: "\u6CA1\u6709\u6620\u5C04\u5230\u7B26\u53F7\u6216\u5173\u7CFB\uFF0C\u8BF7\u67E5\u770B\u672A\u6620\u5C04\u53D8\u66F4\u548C\u5206\u6790\u7F3A\u53E3\u3002",
184
+ loading: "\u6B63\u5728\u5206\u6790\u53D8\u66F4\u2026",
185
+ error: "Impact X-Ray \u6682\u4E0D\u53EF\u7528",
186
+ fallback: '\u4E0B\u65B9\u4FDD\u7559\u539F\u59CB\u5DE5\u5177\u6587\u672C\u3002\u4F7F\u7528 outputFormat="xray" \u91CD\u65B0\u8FD0\u884C\u53EF\u8BF7\u6C42\u65B0\u7684\u8BC1\u636E\u5FEB\u7167\u3002',
187
+ expand: "\u5C55\u5F00",
188
+ collapse: "\u6536\u8D77",
189
+ all: "\u5168\u90E8\u7F6E\u4FE1\u5EA6",
190
+ depth: "\u6700\u5927\u8C03\u7528\u8005\u6DF1\u5EA6",
191
+ rows: "{shown} / {total} \u6761\u5173\u7CFB",
192
+ nodes: "{count} \u4E2A\u8282\u70B9",
193
+ open: "\u6253\u5F00\u5F53\u524D\u6587\u4EF6\u7B2C {line} \u884C",
194
+ openFailed: "\u5BBF\u4E3B\u672A\u80FD\u6253\u5F00\u6587\u4EF6\uFF0C\u4ECD\u53EF\u67E5\u770B\u5DF2\u4FDD\u5B58\u7684\u6E90\u7801\u5FEB\u7167\u3002",
195
+ noOpen: "\u8BE5\u8BC1\u636E\u6CA1\u6709\u53EF\u7528\u7684\u539F\u751F\u6587\u4EF6\u9884\u89C8\u5165\u53E3\u3002",
196
+ raw: "\u539F\u59CB\u5DE5\u5177\u6587\u672C",
197
+ back: "\u4E0A\u4E00\u4E2A\u9009\u62E9",
198
+ forward: "\u4E0B\u4E00\u4E2A\u9009\u62E9",
199
+ reset: "\u91CD\u7F6E\u7B5B\u9009",
200
+ noMatch: "\u6CA1\u6709\u7B26\u5408\u5F53\u524D\u7B5B\u9009\u7684\u5173\u7CFB\u3002",
201
+ uncertain: "\u4E86\u89E3\u4ECD\u4E0D\u786E\u5B9A\u7684\u90E8\u5206",
202
+ caveat: "exact \u8868\u793A\u7D22\u5F15\u6A21\u578B\u5185\u7684\u7ED3\u6784\u89E3\u6790\uFF0C\u4E0D\u4FDD\u8BC1\u8FD0\u884C\u65F6\u884C\u4E3A\u3002\u6D4B\u8BD5\u7EBF\u7D22\u4E0D\u8BC1\u660E\u8986\u76D6\u7387\uFF1B\u6A21\u5757\u7EA7\u65E0\u6240\u5C5E\u8C03\u7528\u3001\u52A8\u6001\u5206\u6D3E\u53CA\u5386\u53F2\u8C03\u7528\u56FE\u53EF\u80FD\u672A\u8986\u76D6\u3002",
203
+ analysisCut: "\u5206\u6790\u5F15\u64CE\u5DF2\u88C1\u526A\u4EE5\u4E0B\u7C7B\u522B",
204
+ displayCut: "\u5C55\u793A\u9884\u7B97\u7701\u7565\u4E86\u90E8\u5206\u884C\u6216\u6E90\u7801\uFF1B\u6CA1\u6709\u663E\u793A\u4E0D\u4EE3\u8868\u6CA1\u6709\u5F71\u54CD\u3002",
205
+ textCut: "\u53EF\u9009 X-Ray \u6A21\u5F0F\u7684\u6587\u672C\u5DF2\u7F29\u77ED\uFF1B\u9ED8\u8BA4\u6587\u672C\u5DE5\u5177\u4FDD\u6301\u4E0D\u53D8\u3002",
206
+ saved: "\u8FD9\u662F\u5DF2\u4FDD\u5B58\u7684 PoC \u7ED3\u679C\uFF0C\u90E8\u5206\u65B0\u8BC1\u636E\u5B57\u6BB5\u4E0D\u53EF\u7528\uFF1B\u539F\u59CB\u5173\u7CFB\u4ECD\u88AB\u4FDD\u7559\u3002",
207
+ pathConfidence: "\u6574\u6761\u8DEF\u5F84\u7F6E\u4FE1\u5EA6",
208
+ incompletePath: "\u90E8\u5206\u8DEF\u5F84\u6B65\u9AA4\u4E0D\u5728\u672C\u6B21\u6709\u754C\u5FEB\u7167\u5185\u3002",
209
+ pathStep: "\u7B2C {step} / {total} \u6B65",
210
+ noTests: "\u672A\u627E\u5230\u76F8\u5173\u7684\u5DF2\u7D22\u5F15\u6D4B\u8BD5\uFF1B\u8FD9\u4E0D\u80FD\u8BC1\u660E\u6D4B\u8BD5\u8986\u76D6\u60C5\u51B5\u3002",
211
+ noPaths: "\u5206\u6790\u672A\u8FD4\u56DE\u5165\u53E3\u8DEF\u5F84\u3002",
212
+ testCaveat: "\u6D4B\u8BD5\u7EBF\u7D22\u662F\u5F85\u68C0\u67E5\u7684\u5019\u9009\uFF0C\u4E0D\u8BC1\u660E\u6D4B\u8BD5\u8C03\u7528\u4E86\u67D0\u4E2A\u51FD\u6570\uFF0C\u4E5F\u4E0D\u4FDD\u8BC1\u6D4B\u8BD5\u4F1A\u5931\u8D25\u3002",
213
+ unmapped: "\u672A\u6620\u5C04\u53D8\u66F4",
214
+ ranges: "\u53D8\u66F4\u884C\u8303\u56F4",
215
+ missing: "\u6CA1\u6709\u53EF\u7528\u7684\u6E90\u7801\u8BC1\u636E\u3002",
216
+ "source-unavailable": "\u6E90\u7801\u4E0D\u53EF\u7528\u3001\u5DF2\u8FC7\u671F\u3001\u672A\u7D22\u5F15\uFF0C\u6216\u4F4D\u4E8E\u4ED3\u5E93\u8303\u56F4\u4E4B\u5916\u3002",
217
+ "line-unavailable": "\u8BB0\u5F55\u7684\u884C\u53F7\u5DF2\u4E0D\u53EF\u7528\u3002",
218
+ "source-budget": "\u6E90\u7801\u8D85\u51FA\u8BC1\u636E\u9884\u7B97\uFF0C\u4E0D\u5C06\u622A\u65AD\u884C\u4F2A\u88C5\u6210\u5B8C\u6574\u4EE3\u7801\u3002",
219
+ "endpoint-unavailable": "\u5F53\u524D\u7D22\u5F15\u7F3A\u5C11\u5173\u7CFB\u7AEF\u70B9\u3002",
220
+ "import-site-unavailable": "\u5BFC\u5165\u7EBF\u7D22\u7F3A\u5C11\u5DF2\u7D22\u5F15\u7684\u5BFC\u5165\u8BED\u53E5\u6216\u53EF\u663E\u793A\u7684\u7AEF\u70B9\u3002",
221
+ "metadata-budget": "\u5C55\u793A\u7ED3\u679C\u8D85\u51FA\u4F53\u79EF\u9884\u7B97\uFF0C\u8BF7\u7F29\u5C0F\u53D8\u66F4\u9009\u62E9\u3002",
222
+ "declaration": "\u58F0\u660E\u7A97\u53E3",
223
+ "call-site": "\u771F\u5B9E\u8C03\u7528\u4F4D\u7F6E",
224
+ "import-site": "\u771F\u5B9E\u5BFC\u5165\u8BED\u53E5",
225
+ "test-lead": "\u6D4B\u8BD5\u6587\u4EF6\u4E0A\u4E0B\u6587\uFF08\u4E0D\u662F\u8C03\u7528\u8BC1\u660E\uFF09",
226
+ "unmapped-change": "\u672A\u6620\u5C04\u5DEE\u5F02\u4E0A\u4E0B\u6587",
227
+ "diff-mapping": "\u5DEE\u5F02\u884C\u4E0E\u58F0\u660E\u76F8\u4EA4",
228
+ "explicit-selection": "\u663E\u5F0F\u9009\u62E9\uFF0C\u4E0D\u8BC1\u660E\u5B9E\u9645\u53D1\u751F\u4FEE\u6539",
229
+ "changed-module": "\u5305\u542B\u5DF2\u6620\u5C04\u6216\u672A\u6620\u5C04\u53D8\u66F4\u7684\u6A21\u5757",
230
+ module: "\u6A21\u5757\u4E0A\u4E0B\u6587",
231
+ "import-dependent": "\u6A21\u5757\u5BFC\u5165\u4E86\u53D8\u66F4\u6587\u4EF6",
232
+ "structural-owner": "AST \u7ED3\u6784\u6240\u5C5E\u5173\u7CFB",
233
+ "local-unique": "\u540C\u6587\u4EF6\u540D\u79F0\u5019\u9009\uFF0C\u5C1A\u4E0D\u80FD\u6392\u9664\u906E\u853D",
234
+ "explicit-import-binding": "\u663E\u5F0F\u5BFC\u5165\u7ED1\u5B9A",
235
+ "namespace-import": "namespace \u5BFC\u5165\u8303\u56F4",
236
+ "resolved-import-module": "\u540D\u79F0\u5B58\u5728\u4E8E\u5DF2\u5BFC\u5165\u6A21\u5757",
237
+ "global-name": "\u4ED3\u5E93\u8303\u56F4\u540D\u79F0\u5339\u914D",
238
+ "changed-test": "\u6D4B\u8BD5\u6587\u4EF6\u53D1\u751F\u53D8\u66F4",
239
+ "imports-changed-file": "\u5BFC\u5165\u4E86\u53D8\u66F4\u6587\u4EF6",
240
+ "imports-impact-file": "\u5BFC\u5165\u4E86\u8C03\u7528\u8005\u6216\u5F71\u54CD\u6587\u4EF6",
241
+ "references-symbol-name": "\u5339\u914D\u53D8\u66F4\u7B26\u53F7\u540D\u79F0",
242
+ "path-convention": "\u4EC5\u5339\u914D\u6D4B\u8BD5\u8DEF\u5F84\u60EF\u4F8B",
243
+ "outside-symbol": "\u53D8\u66F4\u884C\u4F4D\u4E8E\u5DF2\u7D22\u5F15\u58F0\u660E\u8303\u56F4\u4E4B\u5916",
244
+ "base-content-unavailable": "\u65E0\u6CD5\u6620\u5C04\u57FA\u7EBF\u5185\u5BB9",
245
+ modified: "\u4FEE\u6539",
246
+ added: "\u65B0\u589E",
247
+ deleted: "\u5220\u9664",
248
+ renamed: "\u91CD\u547D\u540D",
249
+ unclassified: "\u672A\u5206\u7C7B",
250
+ change: "\u53D8\u66F4",
251
+ caller: "\u76F4\u63A5\u8C03\u7528\u8005",
252
+ impact: "\u95F4\u63A5\u8C03\u7528\u8005",
253
+ test: "\u6D4B\u8BD5",
254
+ call: "\u8C03\u7528",
255
+ import: "\u5BFC\u5165",
256
+ git: "\u5DE5\u4F5C\u533A Git \u5DEE\u5F02",
257
+ diff: "\u8F93\u5165\u5DEE\u5F02",
258
+ files: "\u663E\u5F0F\u6587\u4EF6\u9009\u62E9",
259
+ symbols: "\u663E\u5F0F\u7B26\u53F7\u9009\u62E9"
260
+ };
261
+ function xrayTranslator(language) {
262
+ const dict = language.toLowerCase().startsWith("zh") ? XRAY_ZH : XRAY_EN;
263
+ return (key, params = {}) => (Object.hasOwn(dict, key) ? dict[key] : key).replace(/\{(\w+)\}/g, (match, name2) => String(params[name2] ?? match));
264
+ }
265
+ var FALLBACK_LOCALE = { getSnapshot: () => "en", subscribe: () => () => {
266
+ } };
267
+
268
+ // src/client/impact-xray.tsx
33
269
  var import_jsx_runtime = require("react/jsx-runtime");
270
+ var record = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
271
+ var string = (v) => typeof v === "string" && v.length <= 4096;
272
+ var line = (v) => Number.isSafeInteger(v) && Number(v) >= 1;
273
+ var label = (v) => v === "exact" || v === "import-scoped" || v === "name-only";
274
+ var evidence = (v) => record(v) && string(v.file) && line(v.line) && line(v.startLine) && (v.side === "current" || v.side === "base") && (v.ref === void 0 || string(v.ref)) && (v.type === void 0 || string(v.type) && ["declaration", "call-site", "import-site", "test-lead", "unmapped-change"].includes(v.type)) && (v.code === void 0 || typeof v.code === "string" && v.code.length <= XRAY_LIMITS.evidenceChars) && (v.gap === void 0 || string(v.gap));
275
+ function asImpactXray(v) {
276
+ if (!record(v) || v.card !== "impact-xray" || v.version !== 1 && v.version !== 2 || !string(v.root) || !string(v.source) || !["git", "diff", "files", "symbols"].includes(v.source) || typeof v.truncated !== "boolean") return null;
277
+ if (!Array.isArray(v.nodes) || v.nodes.length > XRAY_LIMITS.nodes || !Array.isArray(v.edges) || v.edges.length > XRAY_LIMITS.edges || !Array.isArray(v.gaps) || v.gaps.length > 24 || !v.gaps.every(string)) return null;
278
+ if (!v.nodes.every((n) => record(n) && string(n.id) && string(n.name) && string(n.kind) && string(n.file) && line(n.line) && typeof n.changed === "boolean" && string(n.reason) && evidence(n.evidence) && (n.category === void 0 || string(n.category) && ["change", "caller", "impact", "module", "test"].includes(n.category)) && (n.resolution === void 0 || label(n.resolution)) && (n.change === void 0 || string(n.change)) && (n.depth === void 0 || Number.isInteger(n.depth) && Number(n.depth) >= 0 && Number(n.depth) <= 6))) return null;
279
+ const ids = new Set(v.nodes.map((n) => n.id));
280
+ if (ids.size !== v.nodes.length || !v.edges.every((e) => record(e) && string(e.id) && ids.has(e.from) && ids.has(e.to) && label(e.resolution) && string(e.reason) && evidence(e.evidence) && (e.kind === void 0 || e.kind === "call" || e.kind === "import"))) return null;
281
+ const edgeIds = new Set(v.edges.map((e) => e.id));
282
+ if (edgeIds.size !== v.edges.length) return null;
283
+ if (!Array.isArray(v.tests) || v.tests.length > 8 || !v.tests.every((t) => record(t) && string(t.file) && string(t.reason) && label(t.resolution) && (t.nodeId === void 0 || ids.has(t.nodeId)))) return null;
284
+ if (!Array.isArray(v.imports) || v.imports.length > 8 || !v.imports.every((i) => record(i) && string(i.file) && string(i.changedFile) && string(i.specifier) && i.resolution === "import-scoped" && (i.edgeId === void 0 || edgeIds.has(i.edgeId)))) return null;
285
+ if (v.paths !== void 0 && (!Array.isArray(v.paths) || v.paths.length > XRAY_LIMITS.paths || !v.paths.every((p) => record(p) && string(p.id) && string(p.from) && string(p.to) && label(p.resolution) && typeof p.omitted === "boolean" && Array.isArray(p.edgeIds) && p.edgeIds.length <= 6 && p.edgeIds.every((id) => edgeIds.has(id))))) return null;
286
+ if (v.unmapped !== void 0 && (!Array.isArray(v.unmapped) || v.unmapped.length > 8 || !v.unmapped.every((u) => record(u) && string(u.file) && string(u.reason) && (u.side === "base" || u.side === "current") && evidence(u.evidence) && Array.isArray(u.ranges) && u.ranges.length <= 16 && u.ranges.every((r) => record(r) && line(r.startLine) && line(r.endLine) && r.endLine >= r.startLine)))) return null;
287
+ if (v.bounds !== void 0 && (!record(v.bounds) || typeof v.bounds.projection !== "boolean" || !record(v.bounds.analysis) || !record(v.bounds.totals) || !["callers", "dependents", "paths", "impact", "tests"].every((k) => typeof v.bounds.analysis[k] === "boolean") || !["changed", "callers", "impact", "imports", "tests", "paths", "unmapped"].every((k) => Number.isSafeInteger(v.bounds.totals[k]) && Number(v.bounds.totals[k]) >= 0))) return null;
288
+ return v;
289
+ }
290
+ function sourcePath(root, file) {
291
+ const base = root.replace(/\\/g, "/"), relative = file.replace(/\\/g, "/");
292
+ if (!(/^[A-Za-z]:\//.test(base) || base.startsWith("/")) || !relative || /[\u0000-\u001f]/.test(base + relative) || relative.startsWith("/") || relative.split("/").some((part) => part === "..") || relative.includes(":")) return null;
293
+ return `${base.replace(/\/$/, "")}/${relative}`;
294
+ }
295
+ var CSS = `
296
+ .ix-dialog{width:min(1180px,96vw);max-width:96vw;max-height:90vh;padding:0;border:0;border-radius:12px;background:#161b24;color:#e7edf5;overflow:auto}.ix-dialog::backdrop{background:#000b}.ix-dialog .ix{min-height:70vh}.ix-dialog .ix-workspace{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:16px}.ix-dialog .ix-evidence{grid-column:2;grid-row:1}.ix-dialog .ix-relations{grid-column:1;grid-row:1}.ix-dialog .ix .pane{max-height:420px}.ix-dialog .ix .graph{max-height:410px}.ix-dialog .ix .code{max-height:240px}@media(max-width:750px){.ix-dialog .ix-workspace{display:block}}
297
+ .ix .change-center{border-left:3px solid #f49b5c;background:#f49b5c0c;border-radius:6px;padding:8px 10px;margin:8px 0}.ix .change-center .chips{margin-top:5px}.ix .metrics{display:flex;gap:6px;flex-wrap:wrap;margin:8px 0}.ix .metrics span{padding:3px 8px;border-radius:5px;background:#65baf212}.ix .uncertainty{color:#e7b852;font-size:11px}.ix .heading{justify-content:space-between}.ix .heading h3{margin:0}.ix .heading .bar{margin:0}.ix .legend{margin:7px 0}
298
+ .ix{border:1px solid var(--dsw-alias-border-l2,#455164);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#161b24);color:var(--dsw-alias-label-primary,#e7edf5);font:12px/1.5 system-ui,sans-serif;padding:12px;overflow-wrap:anywhere;container-type:inline-size}
299
+ .ix *{box-sizing:border-box}.ix h3,.ix p{margin:0 0 7px}.ix h3{font-size:17px}.ix small,.ix .muted{opacity:.72}.ix button,.ix select{font:inherit;color:inherit;border:1px solid var(--dsw-alias-border-l2,#4c586a);border-radius:6px;background:var(--dsw-alias-bg-layer-3,#253042);padding:4px 7px;cursor:pointer}.ix button:disabled{opacity:.35;cursor:default}.ix button:hover:not(:disabled),.ix button[aria-pressed=true]{border-color:#65baf2;background:var(--dsw-alias-bg-layer-1,#33465b)}.ix button:focus-visible,.ix select:focus-visible{outline:2px solid #65baf2;outline-offset:2px}.ix .bar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:8px 0}.ix .badge{font-size:10px;border-radius:4px;padding:1px 4px;background:#374151;color:#fff;white-space:nowrap}.ix .low{color:#e7b852}.ix .pane{border:1px solid var(--dsw-alias-border-l2,#455164);border-radius:8px;padding:9px;margin:8px 0;max-height:250px;overflow:auto}.ix .code{font:11px/1.6 var(--dsh-mono-font,monospace);white-space:pre-wrap;margin:8px 0;max-height:130px;overflow:auto}.ix .code span{display:block}.ix .code .focus{background:#28475b66}.ix .lane{display:grid;grid-template-columns:minmax(0,1fr) 100px minmax(0,1fr);align-items:center;gap:6px;margin:8px 0}.ix .node{text-align:left;min-height:45px;min-width:0;overflow:hidden}.ix .node.changed{border-left:3px solid #f49b5c}.ix .node small{display:block;font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ix .edge{border:0;background:none!important;padding:0;display:flex;flex-direction:column;align-items:center}.ix .edge svg{width:100%;height:20px}.ix .edge small{font-size:9px}.ix .graph{max-height:310px;overflow:auto;padding:0 3px}.ix .notice{border-left:3px solid #e7b852;padding:5px 8px;margin:7px 0}.ix summary{cursor:pointer}.ix .tabs button[aria-pressed=true]{border-bottom:2px solid #65baf2}.ix .chips{display:flex;gap:5px;flex-wrap:wrap}.ix .chips button{text-align:left;max-width:100%}@container(max-width:360px){.ix .lane{grid-template-columns:minmax(0,1fr) 72px minmax(0,1fr)}.ix .node{font-size:11px;padding:4px}.ix .edge small{font-size:8px}}
300
+ `;
301
+ function Evidence({ value, root, openFile, t }) {
302
+ const [failed, setFailed] = (0, import_react.useState)(false);
303
+ const path = sourcePath(root, value.file);
304
+ const canOpen = value.side === "current" && path && openFile && value.code !== void 0;
305
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
308
+ value.file,
309
+ ":",
310
+ value.line
311
+ ] }),
312
+ " \xB7 ",
313
+ t(value.side),
314
+ value.ref ? ` @ ${value.ref}` : ""
315
+ ] }),
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
317
+ t("evidenceType"),
318
+ ": ",
319
+ t(value.type ?? "declaration")
320
+ ] }),
321
+ value.code !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "code", children: value.code.split("\n").map((code, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: value.startLine + i === value.line ? "focus" : "", children: [
322
+ value.startLine + i,
323
+ " ",
324
+ code
325
+ ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", role: "status", children: t(value.gap ?? "missing") }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "muted", children: t("snapshot") }),
327
+ canOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => {
328
+ setFailed(false);
329
+ try {
330
+ Promise.resolve(openFile(path, { line: value.line })).catch(() => setFailed(true));
331
+ } catch {
332
+ setFailed(true);
333
+ }
334
+ }, children: t("open", { line: value.line }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("noOpen") }),
335
+ failed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", children: t("openFailed") }) : null
336
+ ] });
337
+ }
338
+ function ImpactXrayCard({ view, openFile, localeStore = FALLBACK_LOCALE, rawText = "" }) {
339
+ const active = (0, import_react.useSyncExternalStore)(localeStore.subscribe, localeStore.getSnapshot, localeStore.getSnapshot);
340
+ const [language, setLanguage] = (0, import_react.useState)(null);
341
+ const t = xrayTranslator(language ?? active);
342
+ const [tab, setTab] = (0, import_react.useState)("calls"), [visible, setVisible] = (0, import_react.useState)(6), [confidence, setConfidence] = (0, import_react.useState)("all"), [depth, setDepth] = (0, import_react.useState)(6);
343
+ const [pathId, setPathId] = (0, import_react.useState)(null);
344
+ const [history, setHistory] = (0, import_react.useState)(view.nodes[0] ? [{ type: "node", id: view.nodes[0].id }] : []);
345
+ const [cursor, setCursor] = (0, import_react.useState)(view.nodes.length ? 0 : -1), [showEvidence, setShowEvidence] = (0, import_react.useState)(true);
346
+ const pane = (0, import_react.useRef)(null);
347
+ const dialog = (0, import_react.useRef)(null);
348
+ const [focused, setFocused] = (0, import_react.useState)(false);
349
+ (0, import_react.useEffect)(() => {
350
+ if (focused && dialog.current && !dialog.current.open) dialog.current.showModal();
351
+ }, [focused]);
352
+ const byId = (0, import_react.useMemo)(() => new Map(view.nodes.map((n) => [n.id, n])), [view]);
353
+ const centers = view.nodes.filter((n) => n.changed && n.kind !== "module");
354
+ const callCount = view.edges.filter((e) => e.kind !== "import").length;
355
+ const importCount = view.edges.length - callCount;
356
+ const weakCount = view.edges.filter((e) => e.resolution === "name-only").length;
357
+ const selected = history[cursor];
358
+ const selectedNode = selected?.type === "node" ? byId.get(selected.id) : void 0;
359
+ const selectedEdge = selected?.type === "edge" ? view.edges.find((e) => e.id === selected.id) : void 0;
360
+ const unmapped = selected?.type === "unmapped" ? view.unmapped?.[Number(selected.id)] : void 0;
361
+ const selectedEvidence = selectedNode?.evidence ?? selectedEdge?.evidence ?? unmapped?.evidence;
362
+ const pick = (type, id) => {
363
+ setHistory((h) => [...h.slice(0, cursor + 1), { type, id }].slice(-32));
364
+ setCursor(Math.min(cursor + 1, 31));
365
+ setShowEvidence(true);
366
+ requestAnimationFrame(() => pane.current?.scrollIntoView({ block: "nearest" }));
367
+ };
368
+ const path = view.paths?.find((p) => p.id === pathId);
369
+ const filtered = view.edges.filter((e) => (tab === "imports" ? e.kind === "import" : e.kind !== "import") && (confidence === "all" || e.resolution === confidence) && (byId.get(e.from)?.depth ?? 1) <= depth && (!path || path.edgeIds.includes(e.id)));
370
+ const nodeButton = (n) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: `node ${n.changed ? "changed" : ""}`, "aria-pressed": selectedNode?.id === n.id, onClick: () => pick("node", n.id), children: [
371
+ n.name,
372
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
373
+ n.file,
374
+ ":",
375
+ n.line
376
+ ] }),
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "badge", children: t(n.category ?? (n.changed ? "change" : "caller")) })
378
+ ] }, n.id);
379
+ const graph = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
382
+ t("depth"),
383
+ " ",
384
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { "aria-label": t("depth"), value: depth, onChange: (e) => setDepth(Number(e.target.value)), children: [1, 2, 3, 4, 5, 6].map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { children: d }, d)) })
385
+ ] }),
386
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { "aria-label": t("all"), value: confidence, onChange: (e) => setConfidence(e.target.value), children: [
387
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "all", children: t("all") }),
388
+ ["exact", "import-scoped", "name-only"].map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: t(s) }, s))
389
+ ] }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => {
391
+ setDepth(6);
392
+ setConfidence("all");
393
+ setPathId(null);
394
+ setVisible(6);
395
+ }, children: t("reset") })
396
+ ] }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t("rows", { shown: Math.min(visible, filtered.length), total: filtered.length }) }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "graph", "aria-label": t(tab === "imports" ? "imports" : "calls"), children: [
399
+ filtered.slice(0, visible).map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lane", children: [
400
+ nodeButton(byId.get(e.from)),
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: `edge ${e.resolution === "name-only" ? "low" : ""}`, "aria-pressed": selectedEdge?.id === e.id, "aria-label": `${t("inspect")}: ${byId.get(e.from).name} \u2192 ${byId.get(e.to).name} \xB7 ${e.resolution}`, onClick: () => pick("edge", e.id), children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: e.resolution }),
403
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 100 20", "aria-hidden": "true", children: [
404
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M 2 10 H 88", stroke: e.resolution === "name-only" ? "#e7b852" : "#65baf2", strokeWidth: "2", strokeDasharray: e.resolution === "exact" ? void 0 : e.resolution === "name-only" ? "2 5" : "9 5" }),
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M 87 5 L 96 10 L 87 15", fill: "none", stroke: "#65baf2", strokeWidth: "2" })
406
+ ] }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: t(e.kind ?? "call") })
408
+ ] }),
409
+ nodeButton(byId.get(e.to))
410
+ ] }, e.id)),
411
+ !filtered.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", children: t(view.nodes.length ? "noMatch" : "empty") }) : null
412
+ ] }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
414
+ visible < filtered.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", onClick: () => setVisible((v) => v + 6), children: [
415
+ t("expand"),
416
+ " (+6)"
417
+ ] }) : null,
418
+ visible > 6 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => setVisible(6), children: t("collapse") }) : null
419
+ ] })
420
+ ] });
421
+ const card = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "ix", "aria-label": "Impact X-Ray", children: [
422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar heading", children: [
424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Impact X-Ray" }),
425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": "English", onClick: () => setLanguage("en"), children: "EN" }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": "\u7B80\u4F53\u4E2D\u6587", onClick: () => setLanguage("zh"), children: "\u7B80\u4F53\u4E2D\u6587" }),
428
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => setFocused((v) => !v), children: t(focused ? "closeView" : "focusView") })
429
+ ] })
430
+ ] }),
431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("subtitle") }),
432
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("small", { children: [
433
+ t("source"),
434
+ ": ",
435
+ t(view.source),
436
+ " \xB7 ",
437
+ t("snapshotCounts")
438
+ ] }),
439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "change-center", children: [
440
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t(view.source === "git" || view.source === "diff" ? "changeCenter" : "selectionCenter") }),
441
+ centers.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "chips", children: centers.map(nodeButton) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("noCenter") })
442
+ ] }),
443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "metrics", "aria-label": t("snapshotCounts"), children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("callCount", { count: callCount }) }),
445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("importCount", { count: importCount }) }),
446
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("testCount", { count: view.tests.length }) })
447
+ ] }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "uncertainty", children: [
449
+ t("potential"),
450
+ " \xB7 ",
451
+ t("weakCount", { count: weakCount }),
452
+ " \xB7 ",
453
+ t("unmappedCount", { count: view.unmapped?.length ?? 0 })
454
+ ] }),
455
+ view.version === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: t("saved") }) : null,
456
+ view.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: t("displayCut") }) : null,
457
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ix-workspace", children: [
458
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ix-evidence", children: [
459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("inspect") }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": t("back"), disabled: cursor <= 0, onClick: () => setCursor((c) => c - 1), children: "\u2190" }),
462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": t("forward"), disabled: cursor >= history.length - 1, onClick: () => setCursor((c) => c + 1), children: "\u2192" }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => setShowEvidence((v) => !v), children: t(showEvidence ? "collapse" : "expand") })
464
+ ] }),
465
+ showEvidence ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pane", ref: pane, "aria-live": "polite", children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: selectedNode?.name ?? (selectedEdge ? `${byId.get(selectedEdge.from)?.name} \u2192 ${byId.get(selectedEdge.to)?.name}` : unmapped?.file ?? t("inspect")) }) }),
467
+ selectedEdge || selectedNode ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
468
+ t("reason"),
469
+ ": ",
470
+ t(selectedEdge?.reason ?? selectedNode.reason),
471
+ " ",
472
+ selectedNode?.change ? `\xB7 ${t(selectedNode.change)}` : "",
473
+ " ",
474
+ selectedEdge?.resolution || selectedNode?.resolution ? `\xB7 ${t(selectedEdge?.resolution ?? selectedNode.resolution)}` : ""
475
+ ] }) : null,
476
+ selectedEvidence ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Evidence, { value: selectedEvidence, root: view.root, openFile: openFile && focused ? (file, options) => Promise.resolve(openFile(file, options)).then(() => setFocused(false)) : openFile, t }, `${selected?.type}:${selected?.id}`) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("missing") })
477
+ ] }) : null
478
+ ] }),
479
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ix-relations", children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "bar tabs", role: "group", children: ["calls", "imports", "tests", "paths", "gaps"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-pressed": tab === key, onClick: () => {
481
+ setTab(key);
482
+ setVisible(6);
483
+ if (key !== "calls") setPathId(null);
484
+ }, children: t(key) }, key)) }),
485
+ tab === "calls" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
486
+ graph,
487
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "legend", children: [
488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: t("legendTitle") }),
489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("legend") })
490
+ ] })
491
+ ] }) : null,
492
+ tab === "imports" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
493
+ graph,
494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "legend", children: [
495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: t("legendTitle") }),
496
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("legend") })
497
+ ] }),
498
+ view.imports.filter((i) => !i.edgeId).map((i, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
499
+ i.file,
500
+ " \u2192 ",
501
+ i.changedFile,
502
+ " \xB7 ",
503
+ t("import-site-unavailable")
504
+ ] }, n))
505
+ ] }) : null,
506
+ tab === "tests" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("testCaveat") }),
508
+ !view.tests.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("noTests") }) : view.tests.map((test, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
509
+ test.nodeId && byId.has(test.nodeId) ? nodeButton(byId.get(test.nodeId)) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
510
+ test.file,
511
+ " \xB7 ",
512
+ t("missing")
513
+ ] }),
514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
515
+ t(test.resolution),
516
+ " \xB7 ",
517
+ t(test.reason)
518
+ ] })
519
+ ] }, i))
520
+ ] }) : null,
521
+ tab === "paths" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: !view.paths?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("noPaths") }) : view.paths.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pane", children: [
522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
523
+ p.from,
524
+ " \u2192 ",
525
+ p.to
526
+ ] }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
528
+ t("pathConfidence"),
529
+ ": ",
530
+ t(p.resolution)
531
+ ] }),
532
+ p.omitted ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: t("incompletePath") }) : null,
533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
534
+ p.edgeIds.map((id, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => pick("edge", id), children: t("pathStep", { step: i + 1, total: p.edgeIds.length }) }, `${id}:${i}`)),
535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: p.omitted, onClick: () => {
536
+ setPathId(p.id);
537
+ setTab("calls");
538
+ setVisible(48);
539
+ setConfidence("all");
540
+ setDepth(6);
541
+ }, children: t("calls") })
542
+ ] })
543
+ ] }, p.id)) }) : null,
544
+ tab === "gaps" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
545
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { children: t("unmapped") }),
546
+ view.unmapped?.map((u, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", onClick: () => pick("unmapped", String(i)), children: [
548
+ u.file,
549
+ " \xB7 ",
550
+ t(u.side)
551
+ ] }),
552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
553
+ t(u.reason),
554
+ " \xB7 ",
555
+ t("ranges"),
556
+ ": ",
557
+ u.ranges.map((r) => `${r.startLine}\u2013${r.endLine}`).join(", ")
558
+ ] })
559
+ ] }, i)),
560
+ view.bounds && Object.values(view.bounds.analysis).some(Boolean) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
561
+ t("analysisCut"),
562
+ ": ",
563
+ Object.entries(view.bounds.analysis).filter(([, v]) => v).map(([k]) => t(k)).join(", ")
564
+ ] }) : null,
565
+ view.bounds?.projection ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: t("displayCut") }) : null,
566
+ view.bounds?.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("textCut") }) : null,
567
+ view.gaps.map((gap, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: t(gap) }, i)),
568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("caveat") })
569
+ ] }) : null
570
+ ] })
571
+ ] }),
572
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
573
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: t("uncertain") }),
574
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("caveat") })
575
+ ] }),
576
+ rawText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { children: [
577
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: t("raw") }),
578
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "code", children: rawText })
579
+ ] }) : null
580
+ ] });
581
+ return focused ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dialog", { className: "ix-dialog", ref: dialog, "aria-label": "Impact X-Ray", onClose: () => setFocused(false), children: card }) : card;
582
+ }
583
+ function XrayFallback({ loading, text, localeStore = FALLBACK_LOCALE }) {
584
+ const active = (0, import_react.useSyncExternalStore)(localeStore.subscribe, localeStore.getSnapshot, localeStore.getSnapshot), t = xrayTranslator(active);
585
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "ix", role: loading ? "status" : "alert", children: [
586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
587
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "Impact X-Ray" }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t(loading ? "loading" : "error") }),
589
+ !loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: t("fallback") }),
591
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "code", children: text })
592
+ ] }) : null
593
+ ] });
594
+ }
595
+
596
+ // src/client/index.tsx
597
+ var import_jsx_runtime2 = require("react/jsx-runtime");
34
598
  var CARD_STYLE = {
35
599
  border: "1px solid var(--dsh-border, #333)",
36
600
  borderRadius: 6,
@@ -63,8 +627,8 @@ function summarizeArgs(args) {
63
627
  }
64
628
  function asSearchView(view) {
65
629
  if (typeof view !== "object" || view === null || Array.isArray(view)) return void 0;
66
- const record = view;
67
- return record.card === "search" ? record : void 0;
630
+ const record2 = view;
631
+ return record2.card === "search" ? record2 : void 0;
68
632
  }
69
633
  function resultText(block) {
70
634
  return (block.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("\n");
@@ -72,25 +636,29 @@ function resultText(block) {
72
636
  function CodeToolCard(props) {
73
637
  const { toolName, block, openFile } = props;
74
638
  const head = block.call ?? block;
75
- const summary = summarizeArgs(parseArgs(head.argsRaw));
639
+ const args = parseArgs(head.argsRaw);
640
+ const summary = summarizeArgs(args);
76
641
  const search = asSearchView(block.resultView);
77
642
  const isError = block.isError === true;
78
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: CARD_STYLE, children: [
79
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: ROW_STYLE, children: [
80
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: toolName }),
81
- summary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.75 }, children: summary }) : null,
82
- isError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#e5534b" }, children: "error" }) : null
643
+ const xray = !isError && toolName === "code_change_context" ? asImpactXray(block.meta) : null;
644
+ if (xray) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImpactXrayCard, { view: xray, openFile, localeStore: props.localeStore, rawText: resultText(block) });
645
+ if (toolName === "code_change_context" && args.outputFormat === "xray") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(XrayFallback, { loading: props.phase !== "result" && !block.content, text: resultText(block), localeStore: props.localeStore });
646
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: CARD_STYLE, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: ROW_STYLE, children: [
648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: toolName }),
649
+ summary ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.75 }, children: summary }) : null,
650
+ isError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#e5534b" }, children: "error" }) : null
83
651
  ] }),
84
- search?.shape === "matches" && search.files ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 4 }, children: [
85
- search.files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
86
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.85 }, children: file.path }),
87
- (file.matches ?? []).map((match, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
652
+ search?.shape === "matches" && search.files ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginTop: 4 }, children: [
653
+ search.files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { opacity: 0.85 }, children: file.path }),
655
+ (file.matches ?? []).map((match, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
88
656
  "div",
89
657
  {
90
658
  style: { paddingLeft: 12, cursor: openFile ? "pointer" : "default" },
91
659
  onClick: () => openFile?.(file.path, { line: match.lineNumber }),
92
660
  children: [
93
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.6 }, children: match.lineNumber }),
661
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.6 }, children: match.lineNumber }),
94
662
  " ",
95
663
  match.line
96
664
  ]
@@ -98,12 +666,12 @@ function CodeToolCard(props) {
98
666
  `${match.lineNumber}-${index}`
99
667
  ))
100
668
  ] }, file.path)),
101
- search.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.6 }, children: [
669
+ search.truncated ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { opacity: 0.6 }, children: [
102
670
  "\u2026 truncated (",
103
671
  search.total,
104
672
  " total)"
105
673
  ] }) : null
106
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { style: { margin: "4px 0 0", whiteSpace: "pre-wrap", maxHeight: 220, overflow: "auto" }, children: resultText(block) })
674
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { style: { margin: "4px 0 0", whiteSpace: "pre-wrap", maxHeight: 220, overflow: "auto" }, children: resultText(block) })
107
675
  ] });
108
676
  }
109
677
  var CARD_CSS = `
@@ -168,12 +736,12 @@ var SETTINGS_ZH = {
168
736
  function CodeIndexSettingsCard(props) {
169
737
  const scope = props.scope ?? null;
170
738
  const t = props.t ?? ((key) => SETTINGS_EN[key] ?? key);
171
- const [open, setOpen] = (0, import_react.useState)(false);
172
- const [snapshot, setSnapshot] = (0, import_react.useState)(
739
+ const [open, setOpen] = (0, import_react2.useState)(false);
740
+ const [snapshot, setSnapshot] = (0, import_react2.useState)(
173
741
  scope ? scope.getSnapshot() : null
174
742
  );
175
- const [draft, setDraft] = (0, import_react.useState)(null);
176
- (0, import_react.useEffect)(() => {
743
+ const [draft, setDraft] = (0, import_react2.useState)(null);
744
+ (0, import_react2.useEffect)(() => {
177
745
  if (!scope) return;
178
746
  setSnapshot(scope.getSnapshot());
179
747
  return scope.subscribe(() => setSnapshot(scope.getSnapshot()));
@@ -197,9 +765,9 @@ function CodeIndexSettingsCard(props) {
197
765
  }
198
766
  setDraft(null);
199
767
  };
200
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: open ? "dci-card dci-cardOpen" : "dci-card", children: [
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CARD_CSS }),
202
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
768
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: open ? "dci-card dci-cardOpen" : "dci-card", children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: CARD_CSS }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
203
771
  "button",
204
772
  {
205
773
  type: "button",
@@ -210,19 +778,19 @@ function CodeIndexSettingsCard(props) {
210
778
  setOpen(!open);
211
779
  },
212
780
  children: [
213
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dci-headText", children: [
214
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dci-name", children: "dsh-code-index" }),
215
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dci-description", children: t("description") })
781
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dci-headText", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dci-name", children: "dsh-code-index" }),
783
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dci-description", children: t("description") })
216
784
  ] }),
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: open ? "dci-chevron dci-chevronOpen" : "dci-chevron", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" }) })
785
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: open ? "dci-chevron dci-chevronOpen" : "dci-chevron", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" }) })
218
786
  ]
219
787
  }
220
788
  ),
221
- open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-body", children: [
222
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
223
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-head", children: [
224
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-auto-inject", children: t("autoInject") }),
225
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
789
+ open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-body", children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-field", children: [
791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-head", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "dci-label", htmlFor: "dci-auto-inject", children: t("autoInject") }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
226
794
  "input",
227
795
  {
228
796
  id: "dci-auto-inject",
@@ -236,12 +804,12 @@ function CodeIndexSettingsCard(props) {
236
804
  }
237
805
  )
238
806
  ] }),
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("autoInjectHint") })
807
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dci-hint", children: t("autoInjectHint") })
240
808
  ] }),
241
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
242
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-head", children: [
243
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-code-health", children: t("codeHealth") }),
244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
809
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-field", children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-head", children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "dci-label", htmlFor: "dci-code-health", children: t("codeHealth") }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
245
813
  "input",
246
814
  {
247
815
  id: "dci-code-health",
@@ -255,11 +823,11 @@ function CodeIndexSettingsCard(props) {
255
823
  }
256
824
  )
257
825
  ] }),
258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("codeHealthHint") })
826
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dci-hint", children: t("codeHealthHint") })
259
827
  ] }),
260
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-field", children: [
261
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dci-head", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "dci-label", htmlFor: "dci-map-files", children: t("mapFiles") }) }),
262
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
828
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-field", children: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dci-head", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "dci-label", htmlFor: "dci-map-files", children: t("mapFiles") }) }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
263
831
  "input",
264
832
  {
265
833
  id: "dci-map-files",
@@ -273,16 +841,16 @@ function CodeIndexSettingsCard(props) {
273
841
  }
274
842
  }
275
843
  ),
276
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", children: t("mapFilesHint") })
844
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dci-hint", children: t("mapFilesHint") })
277
845
  ] }),
278
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dci-footer", children: [
279
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-discard", disabled: !dirty || !writable, onClick: () => {
846
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dci-footer", children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dci-discard", disabled: !dirty || !writable, onClick: () => {
280
848
  setDraft(null);
281
849
  }, children: t("discard") }),
282
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dci-save", disabled: !dirty || !writable, onClick: save, children: t("save") })
850
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dci-save", disabled: !dirty || !writable, onClick: save, children: t("save") })
283
851
  ] })
284
852
  ] }) : null,
285
- scope ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dci-hint", style: { padding: "0 16px 12px" }, children: t("unavailable") })
853
+ scope ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dci-hint", style: { padding: "0 16px 12px" }, children: t("unavailable") })
286
854
  ] });
287
855
  }
288
856
  var name = "dsh-code-index";
@@ -298,9 +866,36 @@ var TOOLVIEW_KEYS = [
298
866
  "code_health"
299
867
  ];
300
868
  function apply(ctx) {
301
- const card = CodeToolCard;
869
+ let locale;
870
+ const listeners = /* @__PURE__ */ new Set();
871
+ const localeStore = {
872
+ getSnapshot: () => locale?.getSnapshot?.().active ?? "en",
873
+ subscribe: (fn) => {
874
+ listeners.add(fn);
875
+ return () => {
876
+ listeners.delete(fn);
877
+ };
878
+ }
879
+ };
880
+ const card = ((props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CodeToolCard, { ...props, localeStore }));
302
881
  ctx.inject?.(["locale"], (localized) => {
303
882
  if (localized.locale) localized.effect(() => localized.locale.register("settings.codeIndex", { zh: SETTINGS_ZH, en: SETTINGS_EN }));
883
+ if (localized.locale) localized.effect(() => {
884
+ const service = localized.locale;
885
+ locale = service;
886
+ const unregister = service.register("tools.impactXray", { zh: XRAY_ZH, en: XRAY_EN });
887
+ const notify = () => {
888
+ for (const fn of listeners) fn();
889
+ };
890
+ const off = service.subscribe?.(notify);
891
+ notify();
892
+ return () => {
893
+ off?.();
894
+ unregister();
895
+ locale = void 0;
896
+ notify();
897
+ };
898
+ });
304
899
  });
305
900
  ctx.slots.inject("tool.call.toolview", function* () {
306
901
  for (const key of TOOLVIEW_KEYS) {