@goodandready/dsh-context-lens 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +47 -96
- package/README.ru.md +39 -113
- package/README.zh.md +42 -116
- package/lib/auto-compress.js +23 -4
- package/lib/client.js +356 -891
- package/lib/compression/log-compressor.js +53 -28
- package/lib/index.js +58 -173
- package/lib/tools.js +99 -113
- package/package.json +5 -12
- package/lib/http.js +0 -114
- package/lib/tokens/estimate.js +0 -6
- package/lib/tokens/tracker.js +0 -89
- package/lib/updater.js +0 -237
package/lib/client.js
CHANGED
|
@@ -4,286 +4,237 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
const React = require('react');
|
|
6
6
|
const NS = '@goodandready/dsh-context-lens';
|
|
7
|
-
// Plugins page row seat (DSH 0.1.6-alpha.2): key = '<package name>#<row id>',
|
|
8
|
-
// row id as cordis.patch.yml declares it.
|
|
9
7
|
const PKG = '@goodandready/dsh-context-lens';
|
|
10
8
|
const ROW_ID = 'dsh-context-lens';
|
|
11
9
|
const ROW_CONFIG_KEY = PKG + '#' + ROW_ID;
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'update.updating': 'Updating…',
|
|
17
|
-
'update.done': 'Successfully updated to v{version}! Please restart DSH.',
|
|
18
|
-
'update.failed': 'Update failed: {error}',
|
|
19
|
-
'update.checking': 'Checking for updates…',
|
|
20
|
-
'update.up_to_date': 'Up to date',
|
|
21
|
-
title: 'Context Lens & Token Guard',
|
|
22
|
-
sub: 'AST compression, log filtering, token budget',
|
|
11
|
+
const en = {
|
|
12
|
+
title: 'Context Lens',
|
|
13
|
+
sub: 'AST code skeletons and test log compression',
|
|
23
14
|
mode: 'Compression mode',
|
|
24
15
|
depth: 'AST max depth',
|
|
25
|
-
|
|
16
|
+
threshold: 'Auto-compress threshold (chars)',
|
|
26
17
|
preview: 'Log preview',
|
|
27
18
|
compress: 'Compress',
|
|
28
|
-
|
|
29
|
-
tokens: 'tokens',
|
|
30
|
-
placeholder: 'Paste Jest/Pytest log here…',
|
|
19
|
+
placeholder: 'Paste test or build log here…',
|
|
31
20
|
saving: 'Saving…',
|
|
32
21
|
ready: 'Ready',
|
|
33
|
-
|
|
34
|
-
history: 'Recent ops',
|
|
35
|
-
lowBudget: 'Budget nearly exhausted',
|
|
36
|
-
threshold: 'Auto-compress threshold (chars)',
|
|
37
|
-
autoCollapse: 'Warn when budget is nearly exhausted',
|
|
38
|
-
budgetAlertPercent: 'Budget alert threshold (%)',
|
|
39
|
-
previewApprox: 'Server preview',
|
|
40
|
-
noData: 'No data yet',
|
|
41
|
-
refresh: 'Refresh',
|
|
42
|
-
ops: 'ops',
|
|
43
|
-
active: 'Active',
|
|
44
|
-
readyState: 'Ready',
|
|
45
|
-
lowBudgetBadge: 'Low Budget ⚠',
|
|
46
|
-
focusPaths: 'Active focus',
|
|
47
|
-
noFocus: 'No focus paths set',
|
|
48
|
-
clearFocus: 'Clear focus'
|
|
22
|
+
previewApprox: 'Local preview'
|
|
49
23
|
};
|
|
50
24
|
|
|
51
25
|
const zh = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'update.updating': '正在更新…',
|
|
55
|
-
'update.done': '更新成功至 v{version}!请重启 DSH 服务生效。',
|
|
56
|
-
'update.failed': '更新失败:{error}',
|
|
57
|
-
'update.checking': '正在检查更新…',
|
|
58
|
-
'update.up_to_date': '已是最新版本',
|
|
59
|
-
title: 'Context Lens 上下文镜头与 Token 守卫',
|
|
60
|
-
sub: 'AST 代码精简、日志过滤与 Token 预算监控',
|
|
26
|
+
title: '上下文透镜',
|
|
27
|
+
sub: 'AST 代码骨架生成与测试日志压缩',
|
|
61
28
|
mode: '压缩模式',
|
|
62
29
|
depth: 'AST 最大深度',
|
|
63
|
-
|
|
30
|
+
threshold: '自动压缩阈值 (字符)',
|
|
64
31
|
preview: '日志预览',
|
|
65
32
|
compress: '压缩',
|
|
66
|
-
|
|
67
|
-
tokens: 'Token',
|
|
68
|
-
placeholder: '在此粘贴 Jest/Pytest 等测试日志…',
|
|
33
|
+
placeholder: '在此粘贴测试或构建日志…',
|
|
69
34
|
saving: '保存中…',
|
|
70
|
-
ready: '
|
|
71
|
-
|
|
72
|
-
history: '近期操作',
|
|
73
|
-
lowBudget: 'Token 预算即将耗尽',
|
|
74
|
-
threshold: '自动压缩阈值 (字符数)',
|
|
75
|
-
autoCollapse: '预算偏低时在界面提示警告',
|
|
76
|
-
budgetAlertPercent: '预算警告阈值 (%)',
|
|
77
|
-
previewApprox: '服务端压缩预览',
|
|
78
|
-
noData: '暂无数据',
|
|
79
|
-
refresh: '刷新',
|
|
80
|
-
ops: '次操作',
|
|
81
|
-
active: '运行中',
|
|
82
|
-
readyState: '已就绪',
|
|
83
|
-
lowBudgetBadge: '预算紧张 ⚠',
|
|
84
|
-
focusPaths: '当前聚焦路径',
|
|
85
|
-
noFocus: '未设置聚焦路径',
|
|
86
|
-
clearFocus: '清除聚焦'
|
|
35
|
+
ready: '就绪',
|
|
36
|
+
previewApprox: '本地预览'
|
|
87
37
|
};
|
|
88
38
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
border-radius: 12px;
|
|
100
|
-
padding: 16px 18px;
|
|
101
|
-
margin-bottom: 12px;
|
|
102
|
-
display: flex;
|
|
103
|
-
flex-direction: column;
|
|
104
|
-
gap: 12px;
|
|
105
|
-
list-style: none;
|
|
106
|
-
}
|
|
107
|
-
.cl-section-header {
|
|
108
|
-
background: none;
|
|
109
|
-
border: none;
|
|
110
|
-
cursor: pointer;
|
|
111
|
-
display: flex;
|
|
112
|
-
align-items: center;
|
|
113
|
-
width: 100%;
|
|
114
|
-
padding: 0;
|
|
115
|
-
text-align: left;
|
|
116
|
-
}
|
|
117
|
-
.cl-badge {
|
|
118
|
-
font-size: 11px;
|
|
119
|
-
padding: 2px 8px;
|
|
120
|
-
border-radius: 999px;
|
|
121
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
122
|
-
display: inline-flex;
|
|
123
|
-
align-items: center;
|
|
124
|
-
gap: 4px;
|
|
125
|
-
font-weight: 500;
|
|
126
|
-
}
|
|
127
|
-
.cl-badge-ok {
|
|
128
|
-
border-color: var(--dsw-alias-state-success-primary, #10b981);
|
|
129
|
-
color: var(--dsw-alias-state-success-primary, #10b981);
|
|
130
|
-
background: color-mix(in srgb, var(--dsw-alias-state-success-primary, #10b981) 8%, transparent);
|
|
131
|
-
}
|
|
132
|
-
.cl-badge-warn {
|
|
133
|
-
border-color: var(--dsw-alias-state-error-primary, #ef4444);
|
|
134
|
-
color: var(--dsw-alias-state-error-primary, #ef4444);
|
|
135
|
-
background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #ef4444) 8%, transparent);
|
|
136
|
-
}
|
|
137
|
-
.cl-stat-grid {
|
|
138
|
-
display: grid;
|
|
139
|
-
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
|
|
140
|
-
gap: 10px;
|
|
141
|
-
margin-top: 4px;
|
|
142
|
-
}
|
|
143
|
-
.cl-stat-box {
|
|
144
|
-
padding: 10px 12px;
|
|
145
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
146
|
-
border-radius: 8px;
|
|
147
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
148
|
-
display: flex;
|
|
149
|
-
flex-direction: column;
|
|
150
|
-
gap: 3px;
|
|
151
|
-
}
|
|
152
|
-
.cl-stat-val {
|
|
153
|
-
font-size: 16px;
|
|
154
|
-
font-weight: 700;
|
|
155
|
-
color: var(--dsw-alias-label-primary);
|
|
156
|
-
}
|
|
157
|
-
.cl-stat-lbl {
|
|
158
|
-
font-size: 11px;
|
|
159
|
-
color: var(--dsw-alias-label-secondary);
|
|
160
|
-
}
|
|
161
|
-
.cl-bar-box {
|
|
162
|
-
display: flex;
|
|
163
|
-
flex-direction: column;
|
|
164
|
-
gap: 6px;
|
|
165
|
-
padding: 10px 12px;
|
|
166
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
167
|
-
border-radius: 8px;
|
|
168
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
169
|
-
}
|
|
170
|
-
.cl-bar-track {
|
|
171
|
-
width: 100%;
|
|
172
|
-
height: 8px;
|
|
173
|
-
border-radius: 999px;
|
|
174
|
-
background: var(--dsw-alias-bg-layer-1);
|
|
175
|
-
overflow: hidden;
|
|
176
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
177
|
-
}
|
|
178
|
-
.cl-bar-fill {
|
|
179
|
-
height: 100%;
|
|
180
|
-
border-radius: 999px;
|
|
181
|
-
transition: width 0.3s;
|
|
182
|
-
}
|
|
183
|
-
.cl-input {
|
|
184
|
-
height: 34px;
|
|
185
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
186
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
187
|
-
color: var(--dsw-alias-label-primary);
|
|
188
|
-
border-radius: 8px;
|
|
189
|
-
padding: 0 12px;
|
|
190
|
-
font-size: 13px;
|
|
191
|
-
box-sizing: border-box;
|
|
192
|
-
}
|
|
193
|
-
.cl-input:focus {
|
|
194
|
-
outline: none;
|
|
195
|
-
border-color: var(--dsw-alias-state-brand-primary, #3b82f6);
|
|
196
|
-
}
|
|
197
|
-
.cl-btn {
|
|
198
|
-
appearance: none;
|
|
199
|
-
font: inherit;
|
|
200
|
-
cursor: pointer;
|
|
201
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
202
|
-
border-radius: 8px;
|
|
203
|
-
padding: 6px 12px;
|
|
204
|
-
font-size: 12px;
|
|
205
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
206
|
-
color: var(--dsw-alias-label-primary);
|
|
207
|
-
font-weight: 500;
|
|
208
|
-
display: inline-flex;
|
|
209
|
-
align-items: center;
|
|
210
|
-
justify-content: center;
|
|
211
|
-
gap: 6px;
|
|
212
|
-
transition: all 0.15s ease;
|
|
213
|
-
}
|
|
214
|
-
.cl-btn:hover:not(:disabled) {
|
|
215
|
-
background: var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-2));
|
|
216
|
-
border-color: var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2));
|
|
217
|
-
}
|
|
218
|
-
.cl-btn-primary {
|
|
219
|
-
background: var(--dsw-alias-label-primary);
|
|
220
|
-
color: var(--dsw-alias-bg-layer-3);
|
|
221
|
-
border-color: transparent;
|
|
222
|
-
}
|
|
223
|
-
.cl-btn-primary:hover:not(:disabled) {
|
|
224
|
-
opacity: 0.88;
|
|
225
|
-
}
|
|
226
|
-
.cl-btn-disabled {
|
|
227
|
-
opacity: 0.5;
|
|
228
|
-
cursor: not-allowed;
|
|
229
|
-
}
|
|
230
|
-
.cl-banner-warn {
|
|
231
|
-
padding: 10px 14px;
|
|
232
|
-
border-radius: 8px;
|
|
233
|
-
background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #ef4444) 10%, transparent);
|
|
234
|
-
border: 1px solid var(--dsw-alias-state-error-primary, #ef4444);
|
|
235
|
-
color: var(--dsw-alias-state-error-primary, #ef4444);
|
|
236
|
-
font-size: 12px;
|
|
237
|
-
display: flex;
|
|
238
|
-
align-items: center;
|
|
239
|
-
gap: 8px;
|
|
240
|
-
font-weight: 500;
|
|
241
|
-
}
|
|
242
|
-
.cl-hist-item {
|
|
243
|
-
font-size: 11px;
|
|
244
|
-
color: var(--dsw-alias-label-secondary);
|
|
245
|
-
padding: 4px 0;
|
|
246
|
-
border-bottom: 1px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));
|
|
247
|
-
}
|
|
248
|
-
.cl-hist-item:last-child {
|
|
249
|
-
border-bottom: none;
|
|
250
|
-
}
|
|
251
|
-
.cl-tag {
|
|
252
|
-
display: inline-flex;
|
|
253
|
-
align-items: center;
|
|
254
|
-
gap: 4px;
|
|
255
|
-
padding: 2px 6px;
|
|
256
|
-
background: var(--dsw-alias-bg-layer-1);
|
|
257
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
258
|
-
border-radius: 4px;
|
|
259
|
-
font-size: 11px;
|
|
260
|
-
font-family: monospace;
|
|
261
|
-
color: var(--dsw-alias-label-secondary);
|
|
262
|
-
}
|
|
263
|
-
.cl-error-box {
|
|
264
|
-
padding: 12px;
|
|
265
|
-
border-radius: 8px;
|
|
266
|
-
background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #ef4444) 8%, transparent);
|
|
267
|
-
border: 1px solid var(--dsw-alias-state-error-primary, #ef4444);
|
|
268
|
-
color: var(--dsw-alias-state-error-primary, #ef4444);
|
|
269
|
-
font-size: 12px;
|
|
270
|
-
}
|
|
271
|
-
`;
|
|
272
|
-
document.head.appendChild(style);
|
|
39
|
+
function getActiveLocale(ctx) {
|
|
40
|
+
try {
|
|
41
|
+
if (ctx && ctx.locale && typeof ctx.locale.getSnapshot === 'function') {
|
|
42
|
+
const snap = ctx.locale.getSnapshot();
|
|
43
|
+
if (snap && snap.active && snap.active.startsWith('zh')) return 'zh';
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.warn('[dsh-context-lens] getActiveLocale failed', err && err.message || err);
|
|
47
|
+
}
|
|
48
|
+
return 'en';
|
|
273
49
|
}
|
|
274
50
|
|
|
275
|
-
function makeT(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
51
|
+
function makeT(locale, fallbackDict) {
|
|
52
|
+
const dict = locale === 'zh' ? zh : fallbackDict;
|
|
53
|
+
return (key, params) => {
|
|
54
|
+
let str = dict[key] || fallbackDict[key] || key;
|
|
55
|
+
if (params && typeof params === 'object') {
|
|
56
|
+
for (const k of Object.keys(params)) {
|
|
57
|
+
str = str.replace(new RegExp('\\{' + k + '\\}', 'g'), String(params[k]));
|
|
281
58
|
}
|
|
282
59
|
}
|
|
283
|
-
return
|
|
60
|
+
return str;
|
|
284
61
|
};
|
|
285
62
|
}
|
|
286
63
|
|
|
64
|
+
const ANSI_RE = /\u001b\[[0-9;]*[a-zA-Z]/g;
|
|
65
|
+
const KEEP_RE = /(FAIL|FAILED|Error|AssertionError|Exception|Traceback|panic|npm ERR!|ERR!|Expected|Received|missing|×|●|✕|FAIL:|--- FAIL|not ok|at\s+.*:\d+:\d+|stack|Caused by|test result:\s*FAILED|running \d+ test|test .* \.\.\. FAILED|BUILD FAILED|Tests run:|FAILURE:|cargo:.*error|error\[E\d+\]|thread '.*' panicked)/i;
|
|
66
|
+
const PASS_RE = /^(PASS|\s*✓|\s*✔|\s*ok\s|…+\s*$|\s*\.\s*$|test result:\s*ok|running \d+ test.*ok)/i;
|
|
67
|
+
const NOISE_RE = /^\s*(npm (notice|warn|info)|Browserslist|cached|Downloading|Done in|Compiling\s|cargo:.*Finished)/i;
|
|
68
|
+
const STACK_RE = /^\s*(at\s+|File ".*", line \d+|#\d+\s+0x|goroutine \d+ \[|thread '.*' panicked|note: run with)/;
|
|
69
|
+
const SUMMARY_RE = /(tests?\s+(passed|failed|run)|test result:|suites?\s+\d+|\d+\s+passed|BUILD (SUCCESS|FAILED)|===.*passed.*===|passed in \d+|built in|modules transformed|Finished.*release|Finished.*dev)/i;
|
|
70
|
+
|
|
71
|
+
function cleanAnsi(str) {
|
|
72
|
+
if (!str || !str.includes('\u001b')) return str || '';
|
|
73
|
+
return str.replace(ANSI_RE, '');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function compressLogClient(text, { mode = 'balanced', maxLines = 200 } = {}) {
|
|
77
|
+
if (!text || typeof text !== 'string') return '';
|
|
78
|
+
const lines = text.split(/\r?\n/);
|
|
79
|
+
if (mode === 'raw') {
|
|
80
|
+
const filtered = lines.filter((l) => {
|
|
81
|
+
const clean = cleanAnsi(l);
|
|
82
|
+
if (KEEP_RE.test(clean) || STACK_RE.test(clean)) return true;
|
|
83
|
+
if (NOISE_RE.test(clean)) return false;
|
|
84
|
+
return true;
|
|
85
|
+
});
|
|
86
|
+
const truncated = filtered.length > maxLines
|
|
87
|
+
? [...filtered.slice(0, maxLines - 1), `… truncated ${filtered.length - maxLines + 1} lines`]
|
|
88
|
+
: filtered;
|
|
89
|
+
return truncated.join('\n');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const keep = new Array(lines.length).fill(false);
|
|
93
|
+
const cleanLines = new Array(lines.length);
|
|
94
|
+
const context = mode === 'aggressive' ? 1 : 2;
|
|
95
|
+
let hasFailure = false;
|
|
96
|
+
|
|
97
|
+
for (let i = 0; i < lines.length; i++) {
|
|
98
|
+
const clean = cleanAnsi(lines[i]);
|
|
99
|
+
cleanLines[i] = clean;
|
|
100
|
+
if (KEEP_RE.test(clean) || STACK_RE.test(clean)) {
|
|
101
|
+
hasFailure = true;
|
|
102
|
+
const s = Math.max(0, i - context);
|
|
103
|
+
const e = Math.min(lines.length - 1, i + context);
|
|
104
|
+
for (let j = s; j <= e; j++) keep[j] = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!hasFailure) {
|
|
109
|
+
const head = Math.min(3, lines.length);
|
|
110
|
+
for (let i = 0; i < head; i++) keep[i] = true;
|
|
111
|
+
for (let i = head; i < lines.length; i++) {
|
|
112
|
+
if (SUMMARY_RE.test(cleanLines[i])) keep[i] = true;
|
|
113
|
+
}
|
|
114
|
+
if (lines.length > 0) keep[lines.length - 1] = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let out = [];
|
|
118
|
+
for (let i = 0; i < lines.length; i++) {
|
|
119
|
+
if (keep[i]) {
|
|
120
|
+
const l = lines[i];
|
|
121
|
+
const clean = cleanLines[i];
|
|
122
|
+
if (mode === 'aggressive' && (PASS_RE.test(clean) || NOISE_RE.test(clean)) && !KEEP_RE.test(clean) && !SUMMARY_RE.test(clean)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
out.push(l);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (out.length > maxLines) {
|
|
130
|
+
const half = Math.floor((maxLines - 1) / 2);
|
|
131
|
+
out = [
|
|
132
|
+
...out.slice(0, half),
|
|
133
|
+
`… truncated ${out.length - maxLines + 1} lines …`,
|
|
134
|
+
...out.slice(out.length - half)
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return out.join('\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function ensureCss() {
|
|
142
|
+
if (typeof document === 'undefined') return;
|
|
143
|
+
if (document.getElementById('dsh-context-lens-css')) return;
|
|
144
|
+
const el = document.createElement('style');
|
|
145
|
+
el.id = 'dsh-context-lens-css';
|
|
146
|
+
el.dataset.dshPlugin = NS;
|
|
147
|
+
el.textContent = `
|
|
148
|
+
.cl-card {
|
|
149
|
+
font-family: inherit;
|
|
150
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
151
|
+
}
|
|
152
|
+
.cl-section-card {
|
|
153
|
+
margin-bottom: 12px;
|
|
154
|
+
border: 1px solid var(--dsw-alias-border-l1, var(--dsw-alias-border-primary, var(--dsw-alias-border-l2)));
|
|
155
|
+
border-radius: 12px;
|
|
156
|
+
background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-layer-3));
|
|
157
|
+
overflow: hidden;
|
|
158
|
+
transition: border-color .15s ease, box-shadow .15s ease;
|
|
159
|
+
}
|
|
160
|
+
.cl-page {
|
|
161
|
+
border: none;
|
|
162
|
+
background: transparent;
|
|
163
|
+
}
|
|
164
|
+
.cl-header {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: space-between;
|
|
168
|
+
padding: 14px 16px;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
user-select: none;
|
|
171
|
+
}
|
|
172
|
+
.cl-title {
|
|
173
|
+
font-weight: 600;
|
|
174
|
+
font-size: 14px;
|
|
175
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
176
|
+
}
|
|
177
|
+
.cl-sub {
|
|
178
|
+
font-size: 12px;
|
|
179
|
+
color: var(--dsw-alias-label-secondary, var(--dsw-alias-label-tertiary));
|
|
180
|
+
margin-top: 2px;
|
|
181
|
+
}
|
|
182
|
+
.cl-body {
|
|
183
|
+
padding: 0 16px 16px;
|
|
184
|
+
}
|
|
185
|
+
.cl-field {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
gap: 6px;
|
|
189
|
+
padding: 8px 0;
|
|
190
|
+
}
|
|
191
|
+
.cl-input {
|
|
192
|
+
padding: 8px 12px;
|
|
193
|
+
border-radius: 8px;
|
|
194
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
195
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
196
|
+
color: var(--dsw-alias-label-primary, inherit);
|
|
197
|
+
font-size: 13px;
|
|
198
|
+
}
|
|
199
|
+
.cl-btn {
|
|
200
|
+
display: inline-flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
padding: 6px 14px;
|
|
204
|
+
border-radius: 8px;
|
|
205
|
+
font-size: 13px;
|
|
206
|
+
font-weight: 500;
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
border: 1px solid transparent;
|
|
209
|
+
transition: background .15s ease;
|
|
210
|
+
}
|
|
211
|
+
.cl-btn-primary {
|
|
212
|
+
background: var(--dsw-alias-btn-primary-bg, var(--dsw-alias-accent-primary));
|
|
213
|
+
color: var(--dsw-alias-btn-primary-color, var(--dsw-alias-text-contrast));
|
|
214
|
+
}
|
|
215
|
+
.cl-btn-primary:hover {
|
|
216
|
+
filter: brightness(0.95);
|
|
217
|
+
}
|
|
218
|
+
.cl-btn-disabled {
|
|
219
|
+
opacity: 0.5;
|
|
220
|
+
cursor: not-allowed;
|
|
221
|
+
}
|
|
222
|
+
.cl-error-box {
|
|
223
|
+
padding: 12px;
|
|
224
|
+
border-radius: 8px;
|
|
225
|
+
background: var(--dsw-alias-state-error-bg);
|
|
226
|
+
color: var(--dsw-alias-state-error-primary);
|
|
227
|
+
}
|
|
228
|
+
.cl-chev {
|
|
229
|
+
transition: transform .16s ease;
|
|
230
|
+
}
|
|
231
|
+
.cl-chev-open {
|
|
232
|
+
transform: rotate(180deg);
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
document.head.appendChild(el);
|
|
236
|
+
}
|
|
237
|
+
|
|
287
238
|
function createErrorBoundary() {
|
|
288
239
|
if (!React || typeof React.Component !== 'function') {
|
|
289
240
|
return function NoopBoundary(props) { return (props && props.children) || null; };
|
|
@@ -318,304 +269,21 @@ window.__ModuleLoader__.load({
|
|
|
318
269
|
}
|
|
319
270
|
const ErrorBoundary = createErrorBoundary();
|
|
320
271
|
|
|
321
|
-
function getActiveLocale(ctx) {
|
|
322
|
-
try {
|
|
323
|
-
if (ctx && ctx.locale && typeof ctx.locale.getSnapshot === 'function') {
|
|
324
|
-
const snap = ctx.locale.getSnapshot();
|
|
325
|
-
if (snap && snap.active && snap.active.startsWith('zh')) return zh;
|
|
326
|
-
}
|
|
327
|
-
} catch (err) {
|
|
328
|
-
/* intentional fallback: default to english on snapshot error */
|
|
329
|
-
}
|
|
330
|
-
return en;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function StatusPanel({ stats, history, focus, sessionId, labels, onRefresh, onClearFocus, title, showBadge, compact }) {
|
|
334
|
-
ensureCss();
|
|
335
|
-
const L = labels || en;
|
|
336
|
-
const warn = !!(stats && stats.lowBudget);
|
|
337
|
-
const hasSavings = !!(stats && stats.savedTokens > 0);
|
|
338
|
-
const barColor = warn ? 'var(--dsw-alias-state-error-primary, #ef4444)' : 'var(--dsw-alias-state-success-primary, #10b981)';
|
|
339
|
-
const previewLen = compact ? 28 : 44;
|
|
340
|
-
const children = [];
|
|
341
|
-
|
|
342
|
-
if (title || showBadge) {
|
|
343
|
-
children.push(React.createElement('div', {
|
|
344
|
-
key: 'head',
|
|
345
|
-
style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }
|
|
346
|
-
},
|
|
347
|
-
title
|
|
348
|
-
? React.createElement('div', { style: { fontWeight: 600, fontSize: 13, display: 'flex', alignItems: 'center', gap: 6, color: 'var(--dsw-alias-label-primary)' } },
|
|
349
|
-
React.createElement('span', { style: { opacity: 0.8 } }, '◐'),
|
|
350
|
-
title
|
|
351
|
-
)
|
|
352
|
-
: React.createElement('span'),
|
|
353
|
-
showBadge
|
|
354
|
-
? React.createElement('span', {
|
|
355
|
-
className: 'cl-badge ' + (warn ? 'cl-badge-warn' : 'cl-badge-ok')
|
|
356
|
-
}, warn ? L.lowBudgetBadge : (stats ? L.active : L.readyState))
|
|
357
|
-
: null
|
|
358
|
-
));
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (stats) {
|
|
362
|
-
children.push(React.createElement('div', { key: 'stats', className: 'cl-stat-grid' },
|
|
363
|
-
React.createElement('div', { className: 'cl-stat-box' },
|
|
364
|
-
React.createElement('div', { className: 'cl-stat-val' }, stats.savedTokens ? `${stats.savedTokens}` : '0'),
|
|
365
|
-
React.createElement('div', { className: 'cl-stat-lbl' }, `${L.saved} ${L.tokens}`)
|
|
366
|
-
),
|
|
367
|
-
React.createElement('div', { className: 'cl-stat-box' },
|
|
368
|
-
React.createElement('div', { className: 'cl-stat-val' }, `${stats.savedPercent || 0}%`),
|
|
369
|
-
React.createElement('div', { className: 'cl-stat-lbl' }, '% ' + L.saved)
|
|
370
|
-
),
|
|
371
|
-
React.createElement('div', { className: 'cl-stat-box' },
|
|
372
|
-
React.createElement('div', { className: 'cl-stat-val' }, `${stats.calls || 0}`),
|
|
373
|
-
React.createElement('div', { className: 'cl-stat-lbl' }, L.ops)
|
|
374
|
-
)
|
|
375
|
-
));
|
|
376
|
-
} else {
|
|
377
|
-
children.push(React.createElement('div', {
|
|
378
|
-
key: 'nodata',
|
|
379
|
-
style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', padding: '6px 0' }
|
|
380
|
-
}, L.noData));
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (stats && stats.lowBudget && !showBadge) {
|
|
384
|
-
children.push(React.createElement('div', {
|
|
385
|
-
key: 'warn',
|
|
386
|
-
className: 'cl-banner-warn',
|
|
387
|
-
style: { marginTop: 8 }
|
|
388
|
-
}, '⚠ ' + L.lowBudget));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (stats && stats.budgetLimit) {
|
|
392
|
-
children.push(React.createElement('div', { key: 'budget', className: 'cl-bar-box', style: { marginTop: 8 } },
|
|
393
|
-
React.createElement('div', {
|
|
394
|
-
style: { display: 'flex', justifyContent: 'space-between', fontSize: 11, color: warn ? 'var(--dsw-alias-state-error-primary, #ef4444)' : 'var(--dsw-alias-label-secondary)' }
|
|
395
|
-
},
|
|
396
|
-
React.createElement('span', { style: { fontWeight: 500 } }, L.budget),
|
|
397
|
-
React.createElement('span', null, `${stats.budgetUsed || 0} / ${stats.budgetLimit} (${stats.budgetPercent || 0}%)`)
|
|
398
|
-
),
|
|
399
|
-
React.createElement('div', { className: 'cl-bar-track' },
|
|
400
|
-
React.createElement('div', {
|
|
401
|
-
className: 'cl-bar-fill',
|
|
402
|
-
style: { width: Math.min(100, stats.budgetPercent || 0) + '%', background: barColor }
|
|
403
|
-
})
|
|
404
|
-
)
|
|
405
|
-
));
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Active focus paths section
|
|
409
|
-
const focusPaths = (focus && focus.paths) || [];
|
|
410
|
-
if (focusPaths.length > 0 || onClearFocus) {
|
|
411
|
-
children.push(React.createElement('div', {
|
|
412
|
-
key: 'focus',
|
|
413
|
-
style: { marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' }
|
|
414
|
-
},
|
|
415
|
-
React.createElement('div', {
|
|
416
|
-
style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }
|
|
417
|
-
},
|
|
418
|
-
React.createElement('span', { style: { fontSize: 11, fontWeight: 600, color: 'var(--dsw-alias-label-secondary)' } }, L.focusPaths),
|
|
419
|
-
focusPaths.length > 0 && onClearFocus
|
|
420
|
-
? React.createElement('button', {
|
|
421
|
-
type: 'button',
|
|
422
|
-
onClick: onClearFocus,
|
|
423
|
-
style: { background: 'none', border: 'none', padding: 0, fontSize: 10, color: 'var(--dsw-alias-label-tertiary)', cursor: 'pointer', textDecoration: 'underline' }
|
|
424
|
-
}, L.clearFocus)
|
|
425
|
-
: null
|
|
426
|
-
),
|
|
427
|
-
focusPaths.length > 0
|
|
428
|
-
? React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 } },
|
|
429
|
-
...focusPaths.slice(0, 4).map((p, i) => React.createElement('span', { key: i, className: 'cl-tag' }, p))
|
|
430
|
-
)
|
|
431
|
-
: React.createElement('div', { style: { fontSize: 11, color: 'var(--dsw-alias-label-tertiary)', fontStyle: 'italic' } }, L.noFocus)
|
|
432
|
-
));
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (history && history.length) {
|
|
436
|
-
children.push(React.createElement('div', {
|
|
437
|
-
key: 'hist',
|
|
438
|
-
style: { marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--dsw-alias-border-l2)' }
|
|
439
|
-
},
|
|
440
|
-
React.createElement('div', { style: { fontSize: 11, fontWeight: 600, color: 'var(--dsw-alias-label-tertiary)', marginBottom: 4 } }, L.history),
|
|
441
|
-
...history.slice(0, 3).map((h) => React.createElement('div', {
|
|
442
|
-
key: h.id,
|
|
443
|
-
className: 'cl-hist-item',
|
|
444
|
-
style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }
|
|
445
|
-
}, `−${h.savedTokens} tk (${h.savedPercent}%) · ${(h.preview || '').slice(0, previewLen)}`))
|
|
446
|
-
));
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (onRefresh) {
|
|
450
|
-
children.push(React.createElement('div', {
|
|
451
|
-
key: 'act',
|
|
452
|
-
style: { marginTop: 10, display: 'flex', justifyContent: 'flex-end' }
|
|
453
|
-
},
|
|
454
|
-
React.createElement('button', {
|
|
455
|
-
type: 'button',
|
|
456
|
-
onClick: onRefresh,
|
|
457
|
-
className: 'cl-btn'
|
|
458
|
-
}, L.refresh)
|
|
459
|
-
));
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return React.createElement(React.Fragment, null, ...children);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function fetchLensStatus() {
|
|
466
|
-
return fetch('/dsh-context-lens/status', { headers: { accept: 'application/json' } })
|
|
467
|
-
.then((r) => r.ok ? r.json() : null);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function clearLensFocus(sessionId) {
|
|
471
|
-
const q = sessionId ? '?sessionId=' + encodeURIComponent(sessionId) : '';
|
|
472
|
-
return fetch('/dsh-context-lens/clear-focus' + q, { method: 'POST', headers: { accept: 'application/json' } })
|
|
473
|
-
.then((r) => r.ok ? r.json() : null);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function LensTabInner({ ctx: _ctx, scope }) {
|
|
477
|
-
ensureCss();
|
|
478
|
-
const [stats, setStats] = React.useState(null);
|
|
479
|
-
const [history, setHistory] = React.useState([]);
|
|
480
|
-
const [focus, setFocus] = React.useState(null);
|
|
481
|
-
const [sessionId, setSessionId] = React.useState(null);
|
|
482
|
-
const L = React.useMemo(() => getActiveLocale(_ctx), [_ctx]);
|
|
483
|
-
const apply = (j) => {
|
|
484
|
-
if (j && j.stats) setStats(j.stats);
|
|
485
|
-
if (j && j.history) setHistory(j.history || []);
|
|
486
|
-
if (j && j.focus) setFocus(j.focus);
|
|
487
|
-
if (j && j.sessionId) setSessionId(j.sessionId);
|
|
488
|
-
};
|
|
489
|
-
const refresh = () => fetchLensStatus().then(apply).catch(() => {});
|
|
490
|
-
const handleClearFocus = () => clearLensFocus(sessionId).then(() => refresh()).catch(() => {});
|
|
491
|
-
React.useEffect(() => { refresh(); }, []);
|
|
492
|
-
return React.createElement('div', { style: { padding: 12 } },
|
|
493
|
-
React.createElement(StatusPanel, {
|
|
494
|
-
stats, history, focus, sessionId, labels: L, onRefresh: refresh, onClearFocus: handleClearFocus, title: 'Context Lens', showBadge: false, compact: false
|
|
495
|
-
})
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function LensTab(props) {
|
|
500
|
-
return React.createElement(ErrorBoundary, null,
|
|
501
|
-
React.createElement(LensTabInner, props)
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function HeaderChip({ ctx: _ctx }) {
|
|
506
|
-
ensureCss();
|
|
507
|
-
const [stats, setStats] = React.useState(null);
|
|
508
|
-
const [history, setHistory] = React.useState([]);
|
|
509
|
-
const [focus, setFocus] = React.useState(null);
|
|
510
|
-
const [sessionId, setSessionId] = React.useState(null);
|
|
511
|
-
const [open, setOpen] = React.useState(false);
|
|
512
|
-
const ref = React.useRef(null);
|
|
513
|
-
const L = React.useMemo(() => getActiveLocale(_ctx), [_ctx]);
|
|
514
|
-
|
|
515
|
-
const fetchStatus = () => fetchLensStatus().then((j) => {
|
|
516
|
-
if (j && j.stats) setStats(j.stats);
|
|
517
|
-
if (j && j.history) setHistory(j.history || []);
|
|
518
|
-
if (j && j.focus) setFocus(j.focus);
|
|
519
|
-
if (j && j.sessionId) setSessionId(j.sessionId);
|
|
520
|
-
}).catch(() => {});
|
|
521
|
-
|
|
522
|
-
const handleClearFocus = () => clearLensFocus(sessionId).then(() => fetchStatus()).catch(() => {});
|
|
523
|
-
|
|
524
|
-
React.useEffect(() => {
|
|
525
|
-
let alive = true;
|
|
526
|
-
fetchStatus();
|
|
527
|
-
const pollInterval = open ? 4000 : 15000;
|
|
528
|
-
const id = setInterval(() => {
|
|
529
|
-
if (!alive) return;
|
|
530
|
-
if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return;
|
|
531
|
-
fetchStatus();
|
|
532
|
-
}, pollInterval);
|
|
533
|
-
|
|
534
|
-
const onVisChange = () => {
|
|
535
|
-
if (typeof document !== 'undefined' && document.visibilityState === 'visible' && alive) {
|
|
536
|
-
fetchStatus();
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
if (typeof document !== 'undefined') {
|
|
540
|
-
document.addEventListener('visibilitychange', onVisChange);
|
|
541
|
-
}
|
|
542
|
-
return () => {
|
|
543
|
-
alive = false;
|
|
544
|
-
clearInterval(id);
|
|
545
|
-
if (typeof document !== 'undefined') {
|
|
546
|
-
document.removeEventListener('visibilitychange', onVisChange);
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
}, [open]);
|
|
550
|
-
|
|
551
|
-
React.useEffect(() => {
|
|
552
|
-
if (!open) return;
|
|
553
|
-
const onDocClick = (e) => {
|
|
554
|
-
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
555
|
-
};
|
|
556
|
-
document.addEventListener('click', onDocClick);
|
|
557
|
-
return () => document.removeEventListener('click', onDocClick);
|
|
558
|
-
}, [open]);
|
|
559
|
-
|
|
560
|
-
const warn = !!(stats && stats.lowBudget);
|
|
561
|
-
const hasSavings = !!(stats && stats.savedTokens > 0);
|
|
562
|
-
const label = hasSavings ? `Lens ${stats.savedPercent}%` : 'Lens';
|
|
563
|
-
const color = warn ? 'var(--dsw-alias-state-error-primary, #ef4444)' : 'var(--dsw-alias-label-secondary)';
|
|
564
|
-
const borderColor = warn ? 'var(--dsw-alias-state-error-primary, #ef4444)' : open ? 'var(--dsw-alias-border-l1)' : 'var(--dsw-alias-border-l2)';
|
|
565
|
-
|
|
566
|
-
const popover = open ? React.createElement('div', {
|
|
567
|
-
style: {
|
|
568
|
-
position: 'absolute', top: 'calc(100% + 6px)', right: 0, width: 280,
|
|
569
|
-
background: 'var(--dsw-alias-bg-layer-3)', border: '1px solid var(--dsw-alias-border-l2)',
|
|
570
|
-
borderRadius: 10, boxShadow: 'var(--dsw-alias-modal-shadow, var(--dsw-alias-shadow-large, 0 4px 16px var(--dsw-alias-border-l1)))', padding: '12px 14px',
|
|
571
|
-
zIndex: 1000, fontSize: 12, color: 'var(--dsw-alias-label-primary)', cursor: 'default', textAlign: 'left'
|
|
572
|
-
},
|
|
573
|
-
onClick: (e) => e.stopPropagation()
|
|
574
|
-
},
|
|
575
|
-
React.createElement(StatusPanel, {
|
|
576
|
-
stats, history, focus, sessionId, labels: L, onRefresh: fetchStatus, onClearFocus: handleClearFocus, title: 'Context Lens', showBadge: true, compact: true
|
|
577
|
-
})
|
|
578
|
-
) : null;
|
|
579
|
-
|
|
580
|
-
return React.createElement('div', { ref, style: { position: 'relative', display: 'inline-flex', alignItems: 'center' } },
|
|
581
|
-
React.createElement('button', {
|
|
582
|
-
type: 'button',
|
|
583
|
-
title: hasSavings ? `Context Lens: ${L.saved} ${stats.savedTokens} ${L.tokens} (${stats.savedPercent}%)` : `Context Lens: ${L.active}`,
|
|
584
|
-
onClick: () => setOpen((v) => !v),
|
|
585
|
-
style: {
|
|
586
|
-
appearance: 'none', font: 'inherit', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 4,
|
|
587
|
-
padding: '2px 8px', borderRadius: 999, fontSize: 11, lineHeight: '16px',
|
|
588
|
-
background: open ? 'var(--dsw-alias-bg-layer-3)' : 'var(--dsw-alias-bg-layer-2)',
|
|
589
|
-
color, border: '1px solid ' + borderColor, marginLeft: 6
|
|
590
|
-
}
|
|
591
|
-
},
|
|
592
|
-
React.createElement('span', { style: { fontSize: 10, opacity: 0.8 } }, '◐'),
|
|
593
|
-
label,
|
|
594
|
-
warn ? React.createElement('span', { style: { color: 'var(--dsw-alias-state-error-primary, #ef4444)' } }, ' ⚠') : null
|
|
595
|
-
),
|
|
596
|
-
popover
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
272
|
function PluginCardInner({ ctx: _ctx, t, page }) {
|
|
601
273
|
ensureCss();
|
|
602
274
|
const [expanded, setExpanded] = React.useState(!!page);
|
|
603
275
|
const [draft, setDraft] = React.useState({
|
|
604
276
|
compressionMode: 'balanced',
|
|
605
277
|
astSkeletonMaxDepth: 3,
|
|
606
|
-
tokenSavingsTracking: true,
|
|
607
|
-
budgetLimit: 100000,
|
|
608
|
-
budgetAlertPercent: 90,
|
|
609
|
-
autoCollapse: true,
|
|
610
278
|
autoCompressThreshold: 4000
|
|
611
279
|
});
|
|
612
280
|
const [status, setStatus] = React.useState('loading');
|
|
613
281
|
const [saving, setSaving] = React.useState(false);
|
|
614
282
|
const [saveErr, setSaveErr] = React.useState('');
|
|
615
|
-
const [previewIn, setPreviewIn] = React.useState(
|
|
283
|
+
const [previewIn, setPreviewIn] = React.useState(
|
|
284
|
+
'FAIL src/app.test.js\n ● should handle\n Expected 1 got 2\n at Object.<anonymous> (src/app.test.js:10:5)\nPASS src/ok.test.js\n'
|
|
285
|
+
);
|
|
616
286
|
const [previewOut, setPreviewOut] = React.useState('');
|
|
617
|
-
const [stats, setStats] = React.useState(null);
|
|
618
|
-
const [budgetWarn, setBudgetWarn] = React.useState(false);
|
|
619
287
|
|
|
620
288
|
const scopeRef = React.useRef(null);
|
|
621
289
|
if (!scopeRef.current && _ctx) {
|
|
@@ -624,12 +292,18 @@ window.__ModuleLoader__.load({
|
|
|
624
292
|
if (s && typeof s.get === 'function') {
|
|
625
293
|
scopeRef.current = s.get(NS);
|
|
626
294
|
}
|
|
627
|
-
} catch (e) {
|
|
295
|
+
} catch (e) {
|
|
296
|
+
console.warn('[dsh-context-lens] configForms get failed', e && e.message || e);
|
|
297
|
+
scopeRef.current = null;
|
|
298
|
+
}
|
|
628
299
|
}
|
|
629
300
|
const scope = scopeRef.current;
|
|
630
301
|
|
|
631
302
|
React.useEffect(() => {
|
|
632
|
-
if (!scope) {
|
|
303
|
+
if (!scope) {
|
|
304
|
+
setStatus('unavailable');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
633
307
|
let cancelled = false;
|
|
634
308
|
|
|
635
309
|
const loadSnap = async () => {
|
|
@@ -648,6 +322,7 @@ window.__ModuleLoader__.load({
|
|
|
648
322
|
}
|
|
649
323
|
setStatus('ready');
|
|
650
324
|
} catch (e) {
|
|
325
|
+
console.warn('[dsh-context-lens] loadSnap failed', e && e.message || e);
|
|
651
326
|
if (!cancelled) setStatus('unavailable');
|
|
652
327
|
}
|
|
653
328
|
};
|
|
@@ -671,79 +346,6 @@ window.__ModuleLoader__.load({
|
|
|
671
346
|
};
|
|
672
347
|
}, [scope]);
|
|
673
348
|
|
|
674
|
-
const [updateState, setUpdateState] = React.useState({
|
|
675
|
-
checking: false,
|
|
676
|
-
updating: false,
|
|
677
|
-
currentVersion: '0.1.18',
|
|
678
|
-
latestVersion: '',
|
|
679
|
-
updateAvailable: false,
|
|
680
|
-
canAutoUpdate: true,
|
|
681
|
-
notice: '',
|
|
682
|
-
error: ''
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
const checkUpdate = React.useCallback(async () => {
|
|
686
|
-
setUpdateState((s) => ({ ...s, checking: true, error: '' }));
|
|
687
|
-
try {
|
|
688
|
-
const res = await fetch('/api/dsh-context-lens/update');
|
|
689
|
-
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
690
|
-
const data = await res.json().catch(() => ({}));
|
|
691
|
-
setUpdateState((s) => ({
|
|
692
|
-
...s,
|
|
693
|
-
checking: false,
|
|
694
|
-
currentVersion: data.currentVersion || s.currentVersion,
|
|
695
|
-
latestVersion: data.latestVersion || '',
|
|
696
|
-
updateAvailable: !!data.updateAvailable,
|
|
697
|
-
canAutoUpdate: data.canAutoUpdate !== false,
|
|
698
|
-
}));
|
|
699
|
-
} catch (_) {
|
|
700
|
-
setUpdateState((s) => ({ ...s, checking: false }));
|
|
701
|
-
}
|
|
702
|
-
}, []);
|
|
703
|
-
|
|
704
|
-
React.useEffect(() => {
|
|
705
|
-
if (expanded) checkUpdate();
|
|
706
|
-
}, [expanded, checkUpdate]);
|
|
707
|
-
|
|
708
|
-
async function handleTriggerUpdate() {
|
|
709
|
-
if (updateState.updating) return;
|
|
710
|
-
setUpdateState((s) => ({ ...s, updating: true, error: '', notice: '' }));
|
|
711
|
-
try {
|
|
712
|
-
const res = await fetch('/api/dsh-context-lens/update', {
|
|
713
|
-
method: 'POST',
|
|
714
|
-
headers: { 'x-dsh-plugin-update': '1', 'x-dsh-context-lens-update': '1' }
|
|
715
|
-
});
|
|
716
|
-
const data = await res.json().catch(() => ({}));
|
|
717
|
-
if (!res.ok || data.ok === false || data.error) {
|
|
718
|
-
throw new Error(data.error || ('HTTP ' + res.status));
|
|
719
|
-
}
|
|
720
|
-
const newVer = data.updatedVersion || updateState.latestVersion || updateState.currentVersion;
|
|
721
|
-
setUpdateState((s) => ({
|
|
722
|
-
...s,
|
|
723
|
-
updating: false,
|
|
724
|
-
updateAvailable: false,
|
|
725
|
-
currentVersion: newVer,
|
|
726
|
-
notice: tt('update.done', { version: newVer }),
|
|
727
|
-
}));
|
|
728
|
-
} catch (e) {
|
|
729
|
-
setUpdateState((s) => ({
|
|
730
|
-
...s,
|
|
731
|
-
updating: false,
|
|
732
|
-
error: tt('update.failed', { error: e && e.message || String(e) }),
|
|
733
|
-
}));
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
React.useEffect(() => {
|
|
738
|
-
if (!expanded) return;
|
|
739
|
-
fetch('/dsh-context-lens/status').then((r) => r.ok ? r.json() : null).then((j) => {
|
|
740
|
-
if (j && j.stats) {
|
|
741
|
-
setStats(j.stats);
|
|
742
|
-
setBudgetWarn(!!(j.stats.lowBudget && draft.autoCollapse !== false));
|
|
743
|
-
}
|
|
744
|
-
}).catch(() => {});
|
|
745
|
-
}, [expanded, draft.autoCollapse]);
|
|
746
|
-
|
|
747
349
|
const L = React.useMemo(() => getActiveLocale(_ctx), [_ctx]);
|
|
748
350
|
const tt = React.useMemo(() => (t ? t : makeT(L, en)), [t, L]);
|
|
749
351
|
|
|
@@ -751,10 +353,20 @@ window.__ModuleLoader__.load({
|
|
|
751
353
|
try {
|
|
752
354
|
const prim = require('@deepseek-ai/dsh-client-ui-primitives');
|
|
753
355
|
ChevronIcon = prim && prim.IconChevronDownOutline14;
|
|
754
|
-
} catch (e) {
|
|
356
|
+
} catch (e) {
|
|
357
|
+
ChevronIcon = null;
|
|
358
|
+
}
|
|
755
359
|
|
|
756
360
|
const Chevron = ChevronIcon ? function ChevronNode(p) {
|
|
757
|
-
return React.createElement(ChevronIcon, {
|
|
361
|
+
return React.createElement(ChevronIcon, {
|
|
362
|
+
className: 'cl-chev' + (p.open ? ' cl-chev-open' : ''),
|
|
363
|
+
style: {
|
|
364
|
+
marginLeft: 'auto',
|
|
365
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
366
|
+
transition: 'transform .16s ease',
|
|
367
|
+
transform: p.open ? 'rotate(180deg)' : 'none'
|
|
368
|
+
}
|
|
369
|
+
});
|
|
758
370
|
} : function Fallback(p) {
|
|
759
371
|
return React.createElement('svg', {
|
|
760
372
|
className: 'cl-chev' + (p.open ? ' cl-chev-open' : ''),
|
|
@@ -769,106 +381,64 @@ window.__ModuleLoader__.load({
|
|
|
769
381
|
display: 'inline-block',
|
|
770
382
|
flexShrink: 0
|
|
771
383
|
}
|
|
772
|
-
},
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
384
|
+
},
|
|
385
|
+
React.createElement('path', {
|
|
386
|
+
d: 'M3.5 5.25L7 8.75L10.5 5.25',
|
|
387
|
+
fill: 'none',
|
|
388
|
+
stroke: 'currentColor',
|
|
389
|
+
strokeWidth: '1.5',
|
|
390
|
+
strokeLinecap: 'round',
|
|
391
|
+
strokeLinejoin: 'round'
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const handlePreview = () => {
|
|
397
|
+
const out = compressLogClient(previewIn, { mode: draft.compressionMode || 'balanced' });
|
|
398
|
+
setPreviewOut(out);
|
|
780
399
|
};
|
|
781
400
|
|
|
782
|
-
async
|
|
783
|
-
if (!scope)
|
|
784
|
-
setSaving(true);
|
|
401
|
+
const handleSave = async () => {
|
|
402
|
+
if (saving || !scope) return;
|
|
403
|
+
setSaving(true);
|
|
404
|
+
setSaveErr('');
|
|
785
405
|
try {
|
|
786
|
-
if (typeof scope.
|
|
787
|
-
await scope.
|
|
788
|
-
} else if (typeof scope.
|
|
789
|
-
await scope.
|
|
790
|
-
} else {
|
|
791
|
-
const keys = Object.keys(draft);
|
|
792
|
-
const errs = [];
|
|
793
|
-
for (const k of keys) {
|
|
794
|
-
try { await scope.set(k, draft[k]); } catch (e) { errs.push(k + ': ' + (e && e.message || String(e))); }
|
|
795
|
-
}
|
|
796
|
-
if (errs.length) throw new Error(errs.join('; '));
|
|
406
|
+
if (typeof scope.update === 'function') {
|
|
407
|
+
await scope.update(draft);
|
|
408
|
+
} else if (typeof scope.set === 'function') {
|
|
409
|
+
await scope.set(NS, draft);
|
|
797
410
|
}
|
|
798
411
|
} catch (e) {
|
|
799
|
-
|
|
412
|
+
console.error('[dsh-context-lens] save failed', e);
|
|
413
|
+
setSaveErr(e && e.message ? e.message : String(e));
|
|
800
414
|
} finally {
|
|
801
415
|
setSaving(false);
|
|
802
416
|
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
async function onPreview() {
|
|
806
|
-
try {
|
|
807
|
-
const res = await fetch('/dsh-context-lens/compress-preview', {
|
|
808
|
-
method: 'POST',
|
|
809
|
-
headers: { 'content-type': 'application/json' },
|
|
810
|
-
body: JSON.stringify({ text: previewIn, mode: draft.compressionMode || 'balanced' })
|
|
811
|
-
});
|
|
812
|
-
const j = await res.json();
|
|
813
|
-
setPreviewOut((j && j.compressed) || (j && j.error) || '');
|
|
814
|
-
} catch (e) {
|
|
815
|
-
setPreviewOut(String(e && e.message || e));
|
|
816
|
-
}
|
|
817
|
-
}
|
|
417
|
+
};
|
|
818
418
|
|
|
819
|
-
return React.createElement(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
419
|
+
return React.createElement(
|
|
420
|
+
page ? 'div' : 'li',
|
|
421
|
+
{ className: page ? 'cl-page' : 'cl-section-card' },
|
|
422
|
+
React.createElement(
|
|
423
|
+
'div',
|
|
424
|
+
{
|
|
425
|
+
className: 'cl-header',
|
|
426
|
+
style: page ? { display: 'none' } : undefined,
|
|
427
|
+
onClick: () => setExpanded((v) => !v)
|
|
428
|
+
},
|
|
429
|
+
React.createElement('div', null,
|
|
430
|
+
React.createElement('div', { className: 'cl-title' }, tt('title')),
|
|
431
|
+
React.createElement('div', { className: 'cl-sub' }, tt('sub'))
|
|
830
432
|
),
|
|
831
433
|
React.createElement(Chevron, { open: expanded })
|
|
832
434
|
),
|
|
833
|
-
expanded ? React.createElement(
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
|
837
|
-
padding: '6px 10px', marginBottom: 10, borderRadius: 8,
|
|
838
|
-
background: 'var(--dsw-alias-bg-layer-2)', border: '1px solid var(--dsw-alias-border-l2)', fontSize: 12
|
|
839
|
-
}
|
|
840
|
-
},
|
|
841
|
-
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
842
|
-
React.createElement('span', { style: { fontWeight: 500, color: 'var(--dsw-alias-label-primary)' } },
|
|
843
|
-
'v' + (updateState.currentVersion || '0.1.18')
|
|
844
|
-
),
|
|
845
|
-
updateState.checking
|
|
846
|
-
? React.createElement('span', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 11 } }, tt('update.checking'))
|
|
847
|
-
: updateState.updateAvailable
|
|
848
|
-
? React.createElement('span', { className: 'cl-badge cl-badge-warn' },
|
|
849
|
-
tt('update.available', { latestVersion: updateState.latestVersion, currentVersion: updateState.currentVersion })
|
|
850
|
-
)
|
|
851
|
-
: React.createElement('span', { className: 'cl-badge cl-badge-ok' },
|
|
852
|
-
'✓ ' + tt('update.up_to_date')
|
|
853
|
-
)
|
|
854
|
-
),
|
|
855
|
-
updateState.updateAvailable
|
|
856
|
-
? React.createElement('button', {
|
|
857
|
-
type: 'button',
|
|
858
|
-
className: 'cl-btn cl-btn-primary',
|
|
859
|
-
disabled: updateState.updating,
|
|
860
|
-
onClick: handleTriggerUpdate,
|
|
861
|
-
style: { padding: '3px 8px', fontSize: 11 }
|
|
862
|
-
}, updateState.updating ? tt('update.updating') : tt('update.btn'))
|
|
863
|
-
: null
|
|
864
|
-
),
|
|
865
|
-
updateState.notice ? React.createElement('div', { className: 'cl-banner-ok', style: { marginBottom: 10, padding: '8px 12px', borderRadius: 8, background: 'color-mix(in srgb, var(--dsw-alias-state-success-primary, #10b981) 8%, transparent)', border: '1px solid var(--dsw-alias-state-success-primary, #10b981)', color: 'var(--dsw-alias-state-success-primary, #10b981)', fontSize: 12 } }, '✓ ' + updateState.notice) : null,
|
|
866
|
-
updateState.error ? React.createElement('div', { className: 'cl-banner-warn', style: { marginBottom: 10 } }, updateState.error) : null,
|
|
867
|
-
budgetWarn ? React.createElement('div', { className: 'cl-banner-warn', style: { marginBottom: 12 } }, '⚠ ' + tt('lowBudget')) : null,
|
|
435
|
+
(expanded || page) ? React.createElement(
|
|
436
|
+
'div',
|
|
437
|
+
{ className: 'cl-body' },
|
|
868
438
|
status === 'loading' ? React.createElement('div', { style: { padding: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Loading…') :
|
|
869
439
|
status === 'unavailable' ? React.createElement('div', { style: { padding: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Settings unavailable — plugin not registered on host yet') :
|
|
870
440
|
React.createElement(React.Fragment, null,
|
|
871
|
-
React.createElement('div', { className: 'cl-field'
|
|
441
|
+
React.createElement('div', { className: 'cl-field' },
|
|
872
442
|
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('mode')),
|
|
873
443
|
React.createElement('select', {
|
|
874
444
|
className: 'cl-input',
|
|
@@ -880,80 +450,81 @@ window.__ModuleLoader__.load({
|
|
|
880
450
|
React.createElement('option', { value: 'aggressive' }, 'aggressive')
|
|
881
451
|
)
|
|
882
452
|
),
|
|
883
|
-
React.createElement('div', { className: 'cl-field'
|
|
453
|
+
React.createElement('div', { className: 'cl-field' },
|
|
884
454
|
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('depth')),
|
|
885
455
|
React.createElement('input', {
|
|
886
456
|
className: 'cl-input',
|
|
887
|
-
type: 'number',
|
|
457
|
+
type: 'number',
|
|
458
|
+
min: 1,
|
|
459
|
+
max: 10,
|
|
888
460
|
value: draft.astSkeletonMaxDepth,
|
|
889
461
|
onChange: (e) => setDraft((d) => ({ ...d, astSkeletonMaxDepth: parseInt(e.target.value, 10) || 3 }))
|
|
890
462
|
})
|
|
891
463
|
),
|
|
892
|
-
React.createElement('div', { className: 'cl-field'
|
|
893
|
-
React.createElement('input', {
|
|
894
|
-
type: 'checkbox',
|
|
895
|
-
checked: !!draft.tokenSavingsTracking,
|
|
896
|
-
onChange: (e) => setDraft((d) => ({ ...d, tokenSavingsTracking: e.target.checked })),
|
|
897
|
-
id: 'cl-tracking'
|
|
898
|
-
}),
|
|
899
|
-
React.createElement('label', { htmlFor: 'cl-tracking', style: { fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, tt('tracking'))
|
|
900
|
-
),
|
|
901
|
-
React.createElement('div', { className: 'cl-field', style: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8, padding: '8px 0' } },
|
|
902
|
-
React.createElement('input', {
|
|
903
|
-
type: 'checkbox',
|
|
904
|
-
checked: !!draft.autoCollapse,
|
|
905
|
-
onChange: (e) => setDraft((d) => ({ ...d, autoCollapse: e.target.checked })),
|
|
906
|
-
id: 'cl-autocollapse'
|
|
907
|
-
}),
|
|
908
|
-
React.createElement('label', { htmlFor: 'cl-autocollapse', style: { fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, tt('autoCollapse'))
|
|
909
|
-
),
|
|
910
|
-
React.createElement('div', { className: 'cl-field', style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 0' } },
|
|
911
|
-
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('budget') + ' (tokens)'),
|
|
912
|
-
React.createElement('input', {
|
|
913
|
-
className: 'cl-input', type: 'number', min: 1000, step: 1000,
|
|
914
|
-
value: draft.budgetLimit,
|
|
915
|
-
onChange: (e) => setDraft((d) => ({ ...d, budgetLimit: parseInt(e.target.value, 10) || 100000 }))
|
|
916
|
-
})
|
|
917
|
-
),
|
|
918
|
-
React.createElement('div', { className: 'cl-field', style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 0' } },
|
|
919
|
-
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('budgetAlertPercent')),
|
|
920
|
-
React.createElement('input', {
|
|
921
|
-
className: 'cl-input', type: 'number', min: 50, max: 99, step: 1,
|
|
922
|
-
value: draft.budgetAlertPercent,
|
|
923
|
-
onChange: (e) => setDraft((d) => ({ ...d, budgetAlertPercent: parseInt(e.target.value, 10) || 90 }))
|
|
924
|
-
})
|
|
925
|
-
),
|
|
926
|
-
React.createElement('div', { className: 'cl-field', style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 0' } },
|
|
464
|
+
React.createElement('div', { className: 'cl-field' },
|
|
927
465
|
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('threshold')),
|
|
928
466
|
React.createElement('input', {
|
|
929
|
-
className: 'cl-input',
|
|
467
|
+
className: 'cl-input',
|
|
468
|
+
type: 'number',
|
|
469
|
+
min: 0,
|
|
470
|
+
step: 500,
|
|
930
471
|
value: draft.autoCompressThreshold,
|
|
931
472
|
onChange: (e) => setDraft((d) => ({ ...d, autoCompressThreshold: parseInt(e.target.value, 10) || 0 }))
|
|
932
473
|
})
|
|
933
474
|
),
|
|
934
|
-
React.createElement('div', { className: 'cl-field'
|
|
475
|
+
React.createElement('div', { className: 'cl-field' },
|
|
935
476
|
React.createElement('label', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } }, tt('preview') + ' (' + tt('previewApprox') + ')'),
|
|
936
477
|
React.createElement('textarea', {
|
|
937
478
|
value: previewIn,
|
|
938
479
|
onChange: (e) => setPreviewIn(e.target.value),
|
|
939
480
|
placeholder: tt('placeholder'),
|
|
940
|
-
rows:
|
|
941
|
-
style: {
|
|
481
|
+
rows: 4,
|
|
482
|
+
style: {
|
|
483
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
484
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
485
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
486
|
+
borderRadius: 8,
|
|
487
|
+
padding: 10,
|
|
488
|
+
fontSize: 12,
|
|
489
|
+
fontFamily: 'monospace'
|
|
490
|
+
}
|
|
942
491
|
}),
|
|
943
492
|
React.createElement('button', {
|
|
944
493
|
type: 'button',
|
|
945
494
|
className: 'cl-btn cl-btn-primary',
|
|
946
|
-
onClick:
|
|
495
|
+
onClick: handlePreview,
|
|
947
496
|
style: { alignSelf: 'flex-start', marginTop: 4 }
|
|
948
497
|
}, tt('compress')),
|
|
949
|
-
previewOut ? React.createElement('pre', {
|
|
498
|
+
previewOut ? React.createElement('pre', {
|
|
499
|
+
style: {
|
|
500
|
+
whiteSpace: 'pre-wrap',
|
|
501
|
+
fontSize: 12,
|
|
502
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
503
|
+
padding: 10,
|
|
504
|
+
borderRadius: 8,
|
|
505
|
+
maxHeight: 180,
|
|
506
|
+
overflow: 'auto',
|
|
507
|
+
border: '1px solid var(--dsw-alias-border-l2)',
|
|
508
|
+
marginTop: 6
|
|
509
|
+
}
|
|
510
|
+
}, previewOut) : null
|
|
950
511
|
),
|
|
951
|
-
saveErr ? React.createElement('div', {
|
|
952
|
-
|
|
512
|
+
saveErr ? React.createElement('div', {
|
|
513
|
+
style: { color: 'var(--dsw-alias-state-error-primary)', fontSize: 12, padding: '4px 0' }
|
|
514
|
+
}, saveErr) : null,
|
|
515
|
+
React.createElement('div', {
|
|
516
|
+
style: {
|
|
517
|
+
borderTop: '1px solid var(--dsw-alias-border-l2)',
|
|
518
|
+
display: 'flex',
|
|
519
|
+
justifyContent: 'flex-end',
|
|
520
|
+
paddingTop: 12,
|
|
521
|
+
marginTop: 8
|
|
522
|
+
}
|
|
523
|
+
},
|
|
953
524
|
React.createElement('button', {
|
|
954
525
|
type: 'button',
|
|
955
526
|
className: 'cl-btn cl-btn-primary' + (saving ? ' cl-btn-disabled' : ''),
|
|
956
|
-
onClick:
|
|
527
|
+
onClick: handleSave,
|
|
957
528
|
disabled: saving
|
|
958
529
|
}, saving ? tt('saving') : tt('ready'))
|
|
959
530
|
)
|
|
@@ -963,11 +534,9 @@ window.__ModuleLoader__.load({
|
|
|
963
534
|
}
|
|
964
535
|
|
|
965
536
|
function PluginCard(props) {
|
|
966
|
-
// Row seat (plugins.row.config): one-liner for the summary, the form without
|
|
967
|
-
// our header/card chrome for the page (the host page draws those itself).
|
|
968
537
|
if (props && props.view === 'summary') {
|
|
969
538
|
return React.createElement('span', { className: 'cl-sub' },
|
|
970
|
-
'Context lens:
|
|
539
|
+
'Context lens: AST code skeletons and test log compression.');
|
|
971
540
|
}
|
|
972
541
|
const page = !!(props && props.view === 'page');
|
|
973
542
|
return React.createElement(ErrorBoundary, null,
|
|
@@ -976,13 +545,17 @@ window.__ModuleLoader__.load({
|
|
|
976
545
|
}
|
|
977
546
|
|
|
978
547
|
module.exports.inject = ['slots', 'locale', 'configForms'];
|
|
548
|
+
|
|
979
549
|
module.exports.apply = function apply(ctx) {
|
|
550
|
+
ensureCss();
|
|
551
|
+
|
|
980
552
|
let hasEffect = false;
|
|
981
553
|
try {
|
|
982
554
|
hasEffect = typeof ctx.effect === 'function';
|
|
983
555
|
} catch (_) {
|
|
984
556
|
hasEffect = false;
|
|
985
557
|
}
|
|
558
|
+
|
|
986
559
|
if (hasEffect) {
|
|
987
560
|
ctx.effect(() => {
|
|
988
561
|
try {
|
|
@@ -995,43 +568,41 @@ window.__ModuleLoader__.load({
|
|
|
995
568
|
}
|
|
996
569
|
}, 'dsh-context-lens: locale');
|
|
997
570
|
} else {
|
|
998
|
-
try {
|
|
571
|
+
try {
|
|
572
|
+
ctx.locale?.register?.(NS, { en, zh });
|
|
573
|
+
} catch (e) {
|
|
574
|
+
console.warn('[dsh-context-lens] locale register failed', e && e.message || e);
|
|
575
|
+
}
|
|
999
576
|
}
|
|
577
|
+
|
|
1000
578
|
if (!ctx.slots) return;
|
|
579
|
+
|
|
1001
580
|
let registered = false;
|
|
1002
|
-
// Primary seat for the current core (0.1.6-alpha.2): the plugin-list seat
|
|
1003
|
-
// 'plugins.item', which is the one the Plugins page renders as the plugin's own
|
|
1004
|
-
// page with its configuration (the host draws the title, icon, crumb and
|
|
1005
|
-
// padding and asks for view 'summary' or view 'page'). The row seat and the
|
|
1006
|
-
// legacy settings.plugin.item seat below stay as fallbacks.
|
|
1007
581
|
if (typeof ctx.slots.inject === 'function') {
|
|
1008
582
|
try {
|
|
1009
583
|
ctx.slots.inject('plugins.item', () => ctx.slots.register({
|
|
1010
584
|
name: 'plugins.item',
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
label: () => 'Context Lens',
|
|
1014
|
-
locale: NS,
|
|
1015
|
-
inject: () => ({ ctx })
|
|
585
|
+
key: PKG,
|
|
586
|
+
title: () => en.title
|
|
1016
587
|
}, PluginCard));
|
|
588
|
+
|
|
1017
589
|
ctx.slots.inject('plugins.row.config', () => ctx.slots.register({
|
|
1018
590
|
name: 'plugins.row.config',
|
|
1019
591
|
key: ROW_CONFIG_KEY,
|
|
1020
|
-
|
|
1021
|
-
inject: () => ({ ctx })
|
|
592
|
+
title: () => en.title
|
|
1022
593
|
}, PluginCard));
|
|
1023
594
|
} catch (e) {
|
|
1024
|
-
console.error('[dsh-context-lens] plugins.
|
|
595
|
+
console.error('[dsh-context-lens] plugins.row.config inject failed', e && e.stack || e);
|
|
1025
596
|
}
|
|
1026
597
|
}
|
|
598
|
+
|
|
1027
599
|
const doRegister = () => {
|
|
1028
600
|
if (registered) return;
|
|
1029
601
|
try {
|
|
1030
602
|
const dispose = ctx.slots.register({
|
|
1031
603
|
name: 'settings.plugin.item',
|
|
1032
604
|
key: NS,
|
|
1033
|
-
|
|
1034
|
-
inject: () => ({ ctx })
|
|
605
|
+
title: () => en.title
|
|
1035
606
|
}, PluginCard);
|
|
1036
607
|
registered = true;
|
|
1037
608
|
return dispose;
|
|
@@ -1040,144 +611,38 @@ window.__ModuleLoader__.load({
|
|
|
1040
611
|
throw e;
|
|
1041
612
|
}
|
|
1042
613
|
};
|
|
614
|
+
|
|
1043
615
|
if (typeof ctx.slots.inject === 'function') {
|
|
1044
616
|
try {
|
|
1045
617
|
const ok = ctx.slots.inject('settings.plugin.item', doRegister);
|
|
1046
|
-
if (!ok) console.warn('[dsh-context-lens] settings.plugin.item inject returned false
|
|
618
|
+
if (!ok) console.warn('[dsh-context-lens] settings.plugin.item inject returned false');
|
|
1047
619
|
} catch (e) {
|
|
1048
620
|
console.error('[dsh-context-lens] settings.plugin.item inject failed', e && e.stack || e);
|
|
1049
|
-
try { doRegister(); } catch (e2) {
|
|
621
|
+
try { doRegister(); } catch (e2) {
|
|
622
|
+
console.error('[dsh-context-lens] fallback register failed', e2 && e2.stack || e2);
|
|
623
|
+
throw e2;
|
|
624
|
+
}
|
|
1050
625
|
throw e;
|
|
1051
626
|
}
|
|
1052
627
|
} else {
|
|
1053
|
-
try { doRegister(); } catch (e) { console.error('[dsh-context-lens] direct register failed (no inject)', e && e.stack || e); throw e; }
|
|
1054
|
-
}
|
|
1055
|
-
if (typeof ctx.inject === "function") {
|
|
1056
628
|
try {
|
|
1057
|
-
ctx.
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
priority: "extension",
|
|
1065
|
-
title: () => "Lens",
|
|
1066
|
-
guide: [{
|
|
1067
|
-
order: 50,
|
|
1068
|
-
title: () => "Context Lens",
|
|
1069
|
-
description: () => "AST compression, log filtering, token budget",
|
|
1070
|
-
icon: () => React.createElement("span", null, "◐")
|
|
1071
|
-
}]
|
|
1072
|
-
};
|
|
1073
|
-
if (typeof sctx.effect === "function") {
|
|
1074
|
-
sctx.effect(() => tabs.register(def));
|
|
1075
|
-
} else {
|
|
1076
|
-
tabs.register(def);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
if (ctx.slots) {
|
|
1080
|
-
const registerPaneTab = () => {
|
|
1081
|
-
try {
|
|
1082
|
-
return ctx.slots.register({
|
|
1083
|
-
name: "sidebar.right.pane.tab",
|
|
1084
|
-
key: "@goodandready/dsh-context-lens",
|
|
1085
|
-
locale: NS,
|
|
1086
|
-
inject: () => ({ ctx })
|
|
1087
|
-
}, LensTab);
|
|
1088
|
-
} catch (e) {
|
|
1089
|
-
console.warn("[dsh-context-lens] native sidebar pane tab register failed", e && e.message || e);
|
|
1090
|
-
}
|
|
1091
|
-
};
|
|
1092
|
-
if (typeof ctx.slots.inject === "function") {
|
|
1093
|
-
try {
|
|
1094
|
-
const ok = ctx.slots.inject("sidebar.right.pane.tab", registerPaneTab);
|
|
1095
|
-
if (!ok) console.warn("[dsh-context-lens] sidebar.right.pane.tab inject returned false");
|
|
1096
|
-
} catch (e) {
|
|
1097
|
-
console.warn("[dsh-context-lens] sidebar.right.pane.tab inject failed, falling back to direct register", e && e.message || e);
|
|
1098
|
-
try {
|
|
1099
|
-
registerPaneTab();
|
|
1100
|
-
} catch (e2) {
|
|
1101
|
-
console.warn("[dsh-context-lens] registerPaneTab direct register fallback failed", e2 && e2.message || e2);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
} else {
|
|
1105
|
-
try {
|
|
1106
|
-
registerPaneTab();
|
|
1107
|
-
} catch (err) {
|
|
1108
|
-
console.warn("[dsh-context-lens] registerPaneTab direct register failed", err && err.message || err);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
} catch (e) {
|
|
1113
|
-
console.warn("[dsh-context-lens] native sidebar registration failed", e && e.message || e);
|
|
1114
|
-
}
|
|
1115
|
-
});
|
|
1116
|
-
} catch (err) {
|
|
1117
|
-
console.warn("[dsh-context-lens] native sidebar inject failed", err && err.message || err);
|
|
629
|
+
ctx.slots.register({
|
|
630
|
+
name: 'plugins.row.config',
|
|
631
|
+
key: ROW_CONFIG_KEY,
|
|
632
|
+
title: () => en.title
|
|
633
|
+
}, PluginCard);
|
|
634
|
+
} catch (e) {
|
|
635
|
+
console.warn('[dsh-context-lens] plugins.row.config register failed', e && e.message || e);
|
|
1118
636
|
}
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
if (typeof ctx.inject === 'function') {
|
|
1122
637
|
try {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
const tabDef = {
|
|
1128
|
-
id: 'dsh-context-lens:tab',
|
|
1129
|
-
title: () => 'Lens',
|
|
1130
|
-
icon: () => React.createElement('span', null, '◐'),
|
|
1131
|
-
order: 50,
|
|
1132
|
-
component: ({ scope }) => React.createElement(LensTab, { ctx, scope })
|
|
1133
|
-
};
|
|
1134
|
-
if (typeof sctx.effect === 'function') {
|
|
1135
|
-
sctx.effect(() => svc.registerTab(tabDef));
|
|
1136
|
-
} else {
|
|
1137
|
-
svc.registerTab(tabDef);
|
|
1138
|
-
}
|
|
1139
|
-
} catch (e) {
|
|
1140
|
-
console.warn('[dsh-context-lens] betterSidebar registerTab failed', e && e.message || e);
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1143
|
-
} catch (err) {
|
|
1144
|
-
console.warn('[dsh-context-lens] betterSidebar inject failed', err && err.message || err);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
if (ctx.slots) {
|
|
1149
|
-
const headerChipRegister = () => {
|
|
1150
|
-
try {
|
|
1151
|
-
return ctx.slots.register({
|
|
1152
|
-
name: 'conversation.session.header.utilities',
|
|
1153
|
-
id: 'dsh-context-lens-header-chip',
|
|
1154
|
-
order: 7,
|
|
1155
|
-
inject: () => ({ ctx })
|
|
1156
|
-
}, HeaderChip);
|
|
1157
|
-
} catch (e) {
|
|
1158
|
-
console.warn('[dsh-context-lens] header chip register failed', e && e.message || e);
|
|
1159
|
-
}
|
|
1160
|
-
};
|
|
1161
|
-
if (typeof ctx.slots.inject === 'function') {
|
|
1162
|
-
try {
|
|
1163
|
-
ctx.slots.inject('conversation.session.header.utilities', headerChipRegister);
|
|
1164
|
-
} catch (e) {
|
|
1165
|
-
console.warn('[dsh-context-lens] utilities inject failed, falling back to direct register', e && e.message || e);
|
|
1166
|
-
try {
|
|
1167
|
-
headerChipRegister();
|
|
1168
|
-
} catch (e2) {
|
|
1169
|
-
console.warn('[dsh-context-lens] headerChipRegister fallback failed', e2 && e2.message || e2);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
} else {
|
|
1173
|
-
try {
|
|
1174
|
-
headerChipRegister();
|
|
1175
|
-
} catch (err) {
|
|
1176
|
-
console.warn('[dsh-context-lens] headerChipRegister direct register failed', err && err.message || err);
|
|
1177
|
-
}
|
|
638
|
+
doRegister();
|
|
639
|
+
} catch (e) {
|
|
640
|
+
console.error('[dsh-context-lens] direct register failed (no inject)', e && e.stack || e);
|
|
641
|
+
throw e;
|
|
1178
642
|
}
|
|
1179
643
|
}
|
|
1180
644
|
};
|
|
645
|
+
|
|
1181
646
|
return module.exports;
|
|
1182
647
|
}
|
|
1183
|
-
});
|
|
648
|
+
});
|