@goodandready/dsh-goal 0.2.1 → 0.2.2
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/lib/card-form-state.js +65 -21
- package/lib/client.js +2249 -0
- package/lib/command-handler.js +276 -250
- package/lib/goal-engine.js +1009 -0
- package/lib/index.js +982 -2
- package/package.json +63 -63
- package/docs/design/DESIGN.md +0 -184
package/lib/client.js
CHANGED
|
@@ -1,2249 +1,4498 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
|
+
|
|
2
3
|
id: '@goodandready/dsh-goal',
|
|
4
|
+
|
|
3
5
|
factory: (require) => {
|
|
6
|
+
|
|
4
7
|
const module = { exports: {} };
|
|
8
|
+
|
|
5
9
|
const React = require('react');
|
|
10
|
+
|
|
6
11
|
const { useState, useEffect, useRef, useCallback, useMemo } = React;
|
|
7
12
|
|
|
13
|
+
|
|
14
|
+
|
|
8
15
|
const NS = 'dsh-goal';
|
|
9
16
|
|
|
17
|
+
|
|
18
|
+
|
|
10
19
|
// Безопасная загрузка примитивов ядра
|
|
20
|
+
|
|
11
21
|
let Primitives = null;
|
|
22
|
+
|
|
12
23
|
try {
|
|
24
|
+
|
|
13
25
|
Primitives = require('@deepseek-ai/dsh-client-ui-primitives');
|
|
26
|
+
|
|
14
27
|
} catch (_) {
|
|
28
|
+
|
|
15
29
|
Primitives = null;
|
|
30
|
+
|
|
16
31
|
}
|
|
17
32
|
|
|
18
33
|
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
19
37
|
// --- 10 ИНЖЕНЕРНЫХ ШАБЛОНОВ БЫСТРОГО ЗАПУСКА ЦЕЛИ ---
|
|
38
|
+
|
|
20
39
|
const QUICK_LAUNCH_TEMPLATES = [
|
|
40
|
+
|
|
21
41
|
{ id: 'fix', icon: '🐛', label: { en: 'Fix Bug', zh: '修复缺陷' }, prefix: { en: 'Investigate bug, pinpoint root cause, apply fix, and verify with tests: ', zh: '调查并定位缺陷根源,实施修复并通过测试验证:' } },
|
|
42
|
+
|
|
22
43
|
{ id: 'refactor', icon: '⚡', label: { en: 'Refactor (YAGNI)', zh: '重构精简' }, prefix: { en: 'Refactor module applying YAGNI principle, delete redundant abstractions, and verify: ', zh: '遵循 YAGNI 原则重构模块,删除冗余抽象并验证:' } },
|
|
44
|
+
|
|
23
45
|
{ id: 'tests', icon: '📝', label: { en: 'Tests & Coverage', zh: '补充测试' }, prefix: { en: 'Write comprehensive unit tests covering edge cases for: ', zh: '为以下模块编写覆盖边界情况的完整单元测试:' } },
|
|
46
|
+
|
|
24
47
|
{ id: 'review', icon: '🔍', label: { en: 'Code Review', zh: '代码审计' }, prefix: { en: 'Review codebase for subtle bugs, memory leaks, and over-engineering in: ', zh: '对代码库进行深入审查,排查潜在缺陷与过度设计:' } },
|
|
48
|
+
|
|
25
49
|
{ id: 'feature', icon: '🚀', label: { en: 'New Feature', zh: '新功能开发' }, prefix: { en: 'Implement new feature according to specification and add tests for: ', zh: '根据规格说明实现新功能并补充测试:' } },
|
|
50
|
+
|
|
26
51
|
{ id: 'security', icon: '🛡️', label: { en: 'Security Audit', zh: '安全加固' }, prefix: { en: 'Audit security boundaries, sanitize inputs, and prevent injection in: ', zh: '审计安全边界,增加输入过滤与防注入保护:' } },
|
|
52
|
+
|
|
27
53
|
{ id: 'docs', icon: '📚', label: { en: 'Docs & Contract', zh: '文档规范' }, prefix: { en: 'Update technical documentation, README, and DESIGN.md conforming to standard for: ', zh: '按标准更新技术文档、README 与 DESIGN.md:' } },
|
|
54
|
+
|
|
28
55
|
{ id: 'upgrade', icon: '📦', label: { en: 'Upgrade Deps', zh: '依赖升级' }, prefix: { en: 'Upgrade dependencies, resolve breaking API changes, and verify build for: ', zh: '安全升级依赖库,解决接口兼容性并确保构建通过:' } },
|
|
56
|
+
|
|
29
57
|
{ id: 'cleanup', icon: '🧹', label: { en: 'Dead Code', zh: '清理死代码' }, prefix: { en: 'Find and safely remove dead code, unused exports, and stale fixtures in: ', zh: '查找并安全移除死代码、未使用的导出与失效测试用例:' } },
|
|
58
|
+
|
|
30
59
|
{ id: 'perf', icon: '⚙️', label: { en: 'Performance', zh: '性能优化' }, prefix: { en: 'Profile performance bottlenecks, eliminate unnecessary blocking calls in: ', zh: '分析性能瓶颈,消除阻塞调用并优化响应速度:' } },
|
|
60
|
+
|
|
31
61
|
];
|
|
32
62
|
|
|
63
|
+
|
|
64
|
+
|
|
33
65
|
// --- SVG ИКОНКИ ---
|
|
34
66
|
|
|
67
|
+
|
|
68
|
+
|
|
35
69
|
function IconTarget({ size = 15, className = '' }) {
|
|
70
|
+
|
|
36
71
|
return React.createElement(
|
|
72
|
+
|
|
37
73
|
'svg',
|
|
74
|
+
|
|
38
75
|
{
|
|
76
|
+
|
|
39
77
|
width: size,
|
|
78
|
+
|
|
40
79
|
height: size,
|
|
80
|
+
|
|
41
81
|
viewBox: '0 0 24 24',
|
|
82
|
+
|
|
42
83
|
fill: 'none',
|
|
84
|
+
|
|
43
85
|
stroke: 'currentColor',
|
|
86
|
+
|
|
44
87
|
strokeWidth: 2,
|
|
88
|
+
|
|
45
89
|
strokeLinecap: 'round',
|
|
90
|
+
|
|
46
91
|
strokeLinejoin: 'round',
|
|
92
|
+
|
|
47
93
|
className,
|
|
94
|
+
|
|
48
95
|
},
|
|
96
|
+
|
|
49
97
|
React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
|
|
98
|
+
|
|
50
99
|
React.createElement('circle', { cx: 12, cy: 12, r: 6 }),
|
|
100
|
+
|
|
51
101
|
React.createElement('circle', { cx: 12, cy: 12, r: 2 }),
|
|
102
|
+
|
|
52
103
|
);
|
|
104
|
+
|
|
53
105
|
}
|
|
54
106
|
|
|
107
|
+
|
|
108
|
+
|
|
55
109
|
function IconTrash({ size = 14, className = '' }) {
|
|
110
|
+
|
|
56
111
|
return React.createElement(
|
|
112
|
+
|
|
57
113
|
'svg',
|
|
114
|
+
|
|
58
115
|
{
|
|
116
|
+
|
|
59
117
|
width: size,
|
|
118
|
+
|
|
60
119
|
height: size,
|
|
120
|
+
|
|
61
121
|
viewBox: '0 0 24 24',
|
|
122
|
+
|
|
62
123
|
fill: 'none',
|
|
124
|
+
|
|
63
125
|
stroke: 'currentColor',
|
|
126
|
+
|
|
64
127
|
strokeWidth: 1.8,
|
|
128
|
+
|
|
65
129
|
strokeLinecap: 'round',
|
|
130
|
+
|
|
66
131
|
strokeLinejoin: 'round',
|
|
132
|
+
|
|
67
133
|
className,
|
|
134
|
+
|
|
68
135
|
},
|
|
136
|
+
|
|
69
137
|
React.createElement('path', { d: 'M3 6h18' }),
|
|
138
|
+
|
|
70
139
|
React.createElement('path', { d: 'M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6' }),
|
|
140
|
+
|
|
71
141
|
React.createElement('path', { d: 'M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2' }),
|
|
142
|
+
|
|
72
143
|
);
|
|
144
|
+
|
|
73
145
|
}
|
|
74
146
|
|
|
147
|
+
|
|
148
|
+
|
|
75
149
|
function IconPause({ size = 14, className = '' }) {
|
|
150
|
+
|
|
76
151
|
return React.createElement(
|
|
152
|
+
|
|
77
153
|
'svg',
|
|
154
|
+
|
|
78
155
|
{
|
|
156
|
+
|
|
79
157
|
width: size,
|
|
158
|
+
|
|
80
159
|
height: size,
|
|
160
|
+
|
|
81
161
|
viewBox: '0 0 24 24',
|
|
162
|
+
|
|
82
163
|
fill: 'none',
|
|
164
|
+
|
|
83
165
|
stroke: 'currentColor',
|
|
166
|
+
|
|
84
167
|
strokeWidth: 1.8,
|
|
168
|
+
|
|
85
169
|
strokeLinecap: 'round',
|
|
170
|
+
|
|
86
171
|
strokeLinejoin: 'round',
|
|
172
|
+
|
|
87
173
|
className,
|
|
174
|
+
|
|
88
175
|
},
|
|
176
|
+
|
|
89
177
|
React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
|
|
178
|
+
|
|
90
179
|
React.createElement('line', { x1: 10, y1: 15, x2: 10, y2: 9 }),
|
|
180
|
+
|
|
91
181
|
React.createElement('line', { x1: 14, y1: 15, x2: 14, y2: 9 }),
|
|
182
|
+
|
|
92
183
|
);
|
|
184
|
+
|
|
93
185
|
}
|
|
94
186
|
|
|
187
|
+
|
|
188
|
+
|
|
95
189
|
function IconPlay({ size = 14, className = '' }) {
|
|
190
|
+
|
|
96
191
|
return React.createElement(
|
|
192
|
+
|
|
97
193
|
'svg',
|
|
194
|
+
|
|
98
195
|
{
|
|
196
|
+
|
|
99
197
|
width: size,
|
|
198
|
+
|
|
100
199
|
height: size,
|
|
200
|
+
|
|
101
201
|
viewBox: '0 0 24 24',
|
|
202
|
+
|
|
102
203
|
fill: 'none',
|
|
204
|
+
|
|
103
205
|
stroke: 'currentColor',
|
|
206
|
+
|
|
104
207
|
strokeWidth: 1.8,
|
|
208
|
+
|
|
105
209
|
strokeLinecap: 'round',
|
|
210
|
+
|
|
106
211
|
strokeLinejoin: 'round',
|
|
212
|
+
|
|
107
213
|
className,
|
|
214
|
+
|
|
108
215
|
},
|
|
216
|
+
|
|
109
217
|
React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
|
|
218
|
+
|
|
110
219
|
React.createElement('polygon', { points: '10 8 16 12 10 16 10 8', fill: 'currentColor' }),
|
|
220
|
+
|
|
111
221
|
);
|
|
222
|
+
|
|
112
223
|
}
|
|
113
224
|
|
|
225
|
+
|
|
226
|
+
|
|
114
227
|
function IconCheck({ size = 15, className = '' }) {
|
|
228
|
+
|
|
115
229
|
return React.createElement(
|
|
230
|
+
|
|
116
231
|
'svg',
|
|
232
|
+
|
|
117
233
|
{
|
|
234
|
+
|
|
118
235
|
width: size,
|
|
236
|
+
|
|
119
237
|
height: size,
|
|
238
|
+
|
|
120
239
|
viewBox: '0 0 24 24',
|
|
240
|
+
|
|
121
241
|
fill: 'none',
|
|
242
|
+
|
|
122
243
|
stroke: 'currentColor',
|
|
244
|
+
|
|
123
245
|
strokeWidth: 2.5,
|
|
246
|
+
|
|
124
247
|
strokeLinecap: 'round',
|
|
248
|
+
|
|
125
249
|
strokeLinejoin: 'round',
|
|
250
|
+
|
|
126
251
|
className,
|
|
252
|
+
|
|
127
253
|
},
|
|
254
|
+
|
|
128
255
|
React.createElement('polyline', { points: '20 6 9 17 4 12' }),
|
|
256
|
+
|
|
129
257
|
);
|
|
258
|
+
|
|
130
259
|
}
|
|
131
260
|
|
|
261
|
+
|
|
262
|
+
|
|
132
263
|
function IconExpand({ size = 14, className = '' }) {
|
|
264
|
+
|
|
133
265
|
return React.createElement(
|
|
266
|
+
|
|
134
267
|
'svg',
|
|
268
|
+
|
|
135
269
|
{
|
|
270
|
+
|
|
136
271
|
width: size,
|
|
272
|
+
|
|
137
273
|
height: size,
|
|
274
|
+
|
|
138
275
|
viewBox: '0 0 24 24',
|
|
276
|
+
|
|
139
277
|
fill: 'none',
|
|
278
|
+
|
|
140
279
|
stroke: 'currentColor',
|
|
280
|
+
|
|
141
281
|
strokeWidth: 1.8,
|
|
282
|
+
|
|
142
283
|
strokeLinecap: 'round',
|
|
284
|
+
|
|
143
285
|
strokeLinejoin: 'round',
|
|
286
|
+
|
|
144
287
|
className,
|
|
288
|
+
|
|
145
289
|
},
|
|
290
|
+
|
|
146
291
|
React.createElement('polyline', { points: '15 3 21 3 21 9' }),
|
|
292
|
+
|
|
147
293
|
React.createElement('polyline', { points: '9 21 3 21 3 15' }),
|
|
294
|
+
|
|
148
295
|
React.createElement('line', { x1: 21, y1: 3, x2: 14, y2: 10 }),
|
|
296
|
+
|
|
149
297
|
React.createElement('line', { x1: 3, y1: 21, x2: 10, y2: 14 }),
|
|
298
|
+
|
|
150
299
|
);
|
|
300
|
+
|
|
151
301
|
}
|
|
152
302
|
|
|
303
|
+
|
|
304
|
+
|
|
153
305
|
function IconRotateCcw({ size = 13, className = '' }) {
|
|
306
|
+
|
|
154
307
|
return React.createElement(
|
|
308
|
+
|
|
155
309
|
'svg',
|
|
310
|
+
|
|
156
311
|
{
|
|
312
|
+
|
|
157
313
|
width: size,
|
|
314
|
+
|
|
158
315
|
height: size,
|
|
316
|
+
|
|
159
317
|
viewBox: '0 0 24 24',
|
|
318
|
+
|
|
160
319
|
fill: 'none',
|
|
320
|
+
|
|
161
321
|
stroke: 'currentColor',
|
|
322
|
+
|
|
162
323
|
strokeWidth: 2,
|
|
324
|
+
|
|
163
325
|
strokeLinecap: 'round',
|
|
326
|
+
|
|
164
327
|
strokeLinejoin: 'round',
|
|
328
|
+
|
|
165
329
|
className,
|
|
330
|
+
|
|
166
331
|
},
|
|
332
|
+
|
|
167
333
|
React.createElement('polyline', { points: '1 4 1 10 7 10' }),
|
|
334
|
+
|
|
168
335
|
React.createElement('path', { d: 'M3.51 15a9 9 0 1 0 2.13-9.36L1 10' }),
|
|
336
|
+
|
|
169
337
|
);
|
|
338
|
+
|
|
170
339
|
}
|
|
171
340
|
|
|
341
|
+
|
|
342
|
+
|
|
172
343
|
function IconChevronDown({ size = 14, className = '' }) {
|
|
344
|
+
|
|
173
345
|
return React.createElement(
|
|
346
|
+
|
|
174
347
|
'svg',
|
|
348
|
+
|
|
175
349
|
{
|
|
350
|
+
|
|
176
351
|
width: size,
|
|
352
|
+
|
|
177
353
|
height: size,
|
|
354
|
+
|
|
178
355
|
viewBox: '0 0 24 24',
|
|
356
|
+
|
|
179
357
|
fill: 'none',
|
|
358
|
+
|
|
180
359
|
stroke: 'currentColor',
|
|
360
|
+
|
|
181
361
|
strokeWidth: 2,
|
|
362
|
+
|
|
182
363
|
strokeLinecap: 'round',
|
|
364
|
+
|
|
183
365
|
strokeLinejoin: 'round',
|
|
366
|
+
|
|
184
367
|
className,
|
|
368
|
+
|
|
185
369
|
},
|
|
370
|
+
|
|
186
371
|
React.createElement('polyline', { points: '6 9 12 15 18 9' }),
|
|
372
|
+
|
|
187
373
|
);
|
|
374
|
+
|
|
188
375
|
}
|
|
189
376
|
|
|
377
|
+
|
|
378
|
+
|
|
190
379
|
// --- СТИЛИ И CSS ПРАВИЛА (СТАНДАРТ DSH-CLINEBOT & ДИЗАЙН-СИСТЕМА DSH) ---
|
|
380
|
+
|
|
191
381
|
const CSS_STYLES = `
|
|
382
|
+
|
|
192
383
|
.dsh-goal-dock {
|
|
384
|
+
|
|
193
385
|
box-sizing: border-box;
|
|
386
|
+
|
|
194
387
|
width: calc(100% - var(--dsh-composer-side-clearance, 0px) * 2 - var(--dsh-composer-dock-inset, 0px) * 4);
|
|
388
|
+
|
|
195
389
|
max-width: calc(var(--dsh-composer-card-max-width, 768px) - var(--dsh-composer-dock-inset, 0px) * 4);
|
|
390
|
+
|
|
196
391
|
margin: 0 auto 8px auto;
|
|
392
|
+
|
|
197
393
|
display: flex;
|
|
394
|
+
|
|
198
395
|
justify-content: center;
|
|
396
|
+
|
|
199
397
|
}
|
|
398
|
+
|
|
200
399
|
.dsh-goal-dock.dsh-goal-dock-quick {
|
|
400
|
+
|
|
201
401
|
justify-content: flex-start;
|
|
402
|
+
|
|
202
403
|
margin-bottom: 4px;
|
|
404
|
+
|
|
203
405
|
}
|
|
406
|
+
|
|
204
407
|
|
|
408
|
+
|
|
205
409
|
.dsh-goal-template-chips {
|
|
410
|
+
|
|
206
411
|
display: flex;
|
|
412
|
+
|
|
207
413
|
flex-wrap: wrap;
|
|
414
|
+
|
|
208
415
|
gap: 6px;
|
|
416
|
+
|
|
209
417
|
margin-bottom: 12px;
|
|
418
|
+
|
|
210
419
|
}
|
|
420
|
+
|
|
211
421
|
.dsh-goal-template-chip {
|
|
422
|
+
|
|
212
423
|
appearance: none;
|
|
424
|
+
|
|
213
425
|
font: inherit;
|
|
426
|
+
|
|
214
427
|
background: var(--dsw-alias-bg-layer-2);
|
|
428
|
+
|
|
215
429
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
430
|
+
|
|
216
431
|
border-radius: 999px;
|
|
432
|
+
|
|
217
433
|
padding: 3px 8px;
|
|
434
|
+
|
|
218
435
|
font-size: 11px;
|
|
436
|
+
|
|
219
437
|
color: var(--dsw-alias-label-secondary);
|
|
438
|
+
|
|
220
439
|
cursor: pointer;
|
|
440
|
+
|
|
221
441
|
display: inline-flex;
|
|
442
|
+
|
|
222
443
|
align-items: center;
|
|
444
|
+
|
|
223
445
|
gap: 4px;
|
|
446
|
+
|
|
224
447
|
transition: all 0.12s ease;
|
|
448
|
+
|
|
225
449
|
}
|
|
450
|
+
|
|
226
451
|
.dsh-goal-template-chip:hover {
|
|
452
|
+
|
|
227
453
|
background: var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-3));
|
|
454
|
+
|
|
228
455
|
border-color: var(--dsw-alias-label-tertiary);
|
|
456
|
+
|
|
229
457
|
color: var(--dsw-alias-label-primary);
|
|
458
|
+
|
|
230
459
|
}
|
|
460
|
+
|
|
231
461
|
.dsh-goal-nudge-box {
|
|
462
|
+
|
|
232
463
|
display: flex;
|
|
464
|
+
|
|
233
465
|
align-items: center;
|
|
466
|
+
|
|
234
467
|
gap: 8px;
|
|
468
|
+
|
|
235
469
|
margin-top: 4px;
|
|
470
|
+
|
|
236
471
|
}
|
|
472
|
+
|
|
237
473
|
.dsh-goal-git-badge {
|
|
474
|
+
|
|
238
475
|
display: inline-flex;
|
|
476
|
+
|
|
239
477
|
align-items: center;
|
|
478
|
+
|
|
240
479
|
gap: 4px;
|
|
480
|
+
|
|
241
481
|
font-family: monospace;
|
|
482
|
+
|
|
242
483
|
font-size: 11px;
|
|
484
|
+
|
|
243
485
|
padding: 2px 6px;
|
|
486
|
+
|
|
244
487
|
border-radius: 4px;
|
|
488
|
+
|
|
245
489
|
background: var(--dsw-alias-bg-layer-2);
|
|
490
|
+
|
|
246
491
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
492
|
+
|
|
247
493
|
color: var(--dsw-alias-label-secondary);
|
|
494
|
+
|
|
248
495
|
cursor: pointer;
|
|
496
|
+
|
|
249
497
|
}
|
|
498
|
+
|
|
250
499
|
.dsh-goal-git-badge:hover {
|
|
500
|
+
|
|
251
501
|
color: var(--dsw-alias-label-primary);
|
|
502
|
+
|
|
252
503
|
border-color: var(--dsw-alias-label-tertiary);
|
|
504
|
+
|
|
253
505
|
}
|
|
506
|
+
|
|
254
507
|
.dsh-goal-quicklaunch-btn {
|
|
508
|
+
|
|
255
509
|
appearance: none;
|
|
510
|
+
|
|
256
511
|
font: inherit;
|
|
512
|
+
|
|
257
513
|
background: var(--dsw-alias-bg-layer-2);
|
|
514
|
+
|
|
258
515
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
516
|
+
|
|
259
517
|
border-radius: 999px;
|
|
518
|
+
|
|
260
519
|
padding: 4px 11px;
|
|
520
|
+
|
|
261
521
|
color: var(--dsw-alias-label-secondary);
|
|
522
|
+
|
|
262
523
|
font-size: 12px;
|
|
524
|
+
|
|
263
525
|
font-weight: 500;
|
|
526
|
+
|
|
264
527
|
cursor: pointer;
|
|
528
|
+
|
|
265
529
|
display: inline-flex;
|
|
530
|
+
|
|
266
531
|
align-items: center;
|
|
532
|
+
|
|
267
533
|
gap: 6px;
|
|
534
|
+
|
|
268
535
|
transition: all 0.15s ease;
|
|
536
|
+
|
|
269
537
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
538
|
+
|
|
270
539
|
}
|
|
540
|
+
|
|
271
541
|
.dsh-goal-quicklaunch-btn:hover {
|
|
542
|
+
|
|
272
543
|
background: var(--dsw-alias-bg-layer-3);
|
|
544
|
+
|
|
273
545
|
border-color: var(--dsw-alias-label-tertiary);
|
|
546
|
+
|
|
274
547
|
color: var(--dsw-alias-label-primary);
|
|
548
|
+
|
|
275
549
|
}
|
|
550
|
+
|
|
276
551
|
.dsh-goal-banner {
|
|
552
|
+
|
|
277
553
|
box-sizing: border-box;
|
|
554
|
+
|
|
278
555
|
width: 100%;
|
|
556
|
+
|
|
279
557
|
display: flex;
|
|
558
|
+
|
|
280
559
|
align-items: center;
|
|
560
|
+
|
|
281
561
|
justify-content: space-between;
|
|
562
|
+
|
|
282
563
|
background: var(--dsw-alias-bg-layer-3);
|
|
564
|
+
|
|
283
565
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
566
|
+
|
|
284
567
|
border-radius: 12px;
|
|
568
|
+
|
|
285
569
|
padding: 7px 14px;
|
|
570
|
+
|
|
286
571
|
font-family: inherit;
|
|
572
|
+
|
|
287
573
|
font-size: 13px;
|
|
574
|
+
|
|
288
575
|
color: var(--dsw-alias-label-primary);
|
|
576
|
+
|
|
289
577
|
user-select: none;
|
|
578
|
+
|
|
290
579
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
|
580
|
+
|
|
291
581
|
transition: all 0.15s ease;
|
|
582
|
+
|
|
292
583
|
}
|
|
584
|
+
|
|
293
585
|
.dsh-goal-banner:hover {
|
|
586
|
+
|
|
294
587
|
border-color: var(--dsw-alias-label-tertiary);
|
|
588
|
+
|
|
295
589
|
}
|
|
590
|
+
|
|
296
591
|
.dsh-goal-banner.completed {
|
|
592
|
+
|
|
297
593
|
border-color: var(--dsw-alias-state-success-primary);
|
|
594
|
+
|
|
298
595
|
background: var(--dsw-alias-bg-layer-3);
|
|
596
|
+
|
|
299
597
|
}
|
|
598
|
+
|
|
300
599
|
.dsh-goal-left {
|
|
600
|
+
|
|
301
601
|
display: flex;
|
|
602
|
+
|
|
302
603
|
align-items: center;
|
|
604
|
+
|
|
303
605
|
gap: 8px;
|
|
606
|
+
|
|
304
607
|
min-width: 0;
|
|
608
|
+
|
|
305
609
|
flex: 1;
|
|
610
|
+
|
|
306
611
|
}
|
|
612
|
+
|
|
307
613
|
.dsh-goal-icon {
|
|
614
|
+
|
|
308
615
|
color: var(--dsw-alias-label-secondary);
|
|
616
|
+
|
|
309
617
|
flex-shrink: 0;
|
|
618
|
+
|
|
310
619
|
display: flex;
|
|
620
|
+
|
|
311
621
|
align-items: center;
|
|
622
|
+
|
|
312
623
|
}
|
|
624
|
+
|
|
313
625
|
.dsh-goal-badge {
|
|
626
|
+
|
|
314
627
|
font-size: 11px;
|
|
628
|
+
|
|
315
629
|
padding: 2px 8px;
|
|
630
|
+
|
|
316
631
|
border-radius: 999px;
|
|
632
|
+
|
|
317
633
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
634
|
+
|
|
318
635
|
display: inline-flex;
|
|
636
|
+
|
|
319
637
|
align-items: center;
|
|
638
|
+
|
|
320
639
|
gap: 4px;
|
|
640
|
+
|
|
321
641
|
font-weight: 600;
|
|
642
|
+
|
|
322
643
|
white-space: nowrap;
|
|
644
|
+
|
|
323
645
|
background: var(--dsw-alias-bg-layer-2);
|
|
646
|
+
|
|
324
647
|
color: var(--dsw-alias-label-primary);
|
|
648
|
+
|
|
325
649
|
}
|
|
650
|
+
|
|
326
651
|
.dsh-goal-badge-ok {
|
|
652
|
+
|
|
327
653
|
border-color: var(--dsw-alias-state-success-primary);
|
|
654
|
+
|
|
328
655
|
color: var(--dsw-alias-state-success-primary);
|
|
656
|
+
|
|
329
657
|
background: rgba(16, 185, 129, 0.08);
|
|
658
|
+
|
|
330
659
|
}
|
|
660
|
+
|
|
331
661
|
.dsh-goal-badge-warn {
|
|
662
|
+
|
|
332
663
|
border-color: var(--dsw-alias-state-warning-primary);
|
|
664
|
+
|
|
333
665
|
color: var(--dsw-alias-state-warning-primary);
|
|
666
|
+
|
|
334
667
|
background: rgba(245, 158, 11, 0.08);
|
|
668
|
+
|
|
335
669
|
}
|
|
670
|
+
|
|
336
671
|
.dsh-goal-label {
|
|
672
|
+
|
|
337
673
|
font-weight: 600;
|
|
674
|
+
|
|
338
675
|
color: var(--dsw-alias-label-primary);
|
|
676
|
+
|
|
339
677
|
white-space: nowrap;
|
|
678
|
+
|
|
340
679
|
}
|
|
680
|
+
|
|
341
681
|
.dsh-goal-label.completed {
|
|
682
|
+
|
|
342
683
|
color: var(--dsw-alias-state-success-primary);
|
|
684
|
+
|
|
343
685
|
}
|
|
686
|
+
|
|
344
687
|
.dsh-goal-title {
|
|
688
|
+
|
|
345
689
|
color: var(--dsw-alias-label-secondary);
|
|
690
|
+
|
|
346
691
|
overflow: hidden;
|
|
692
|
+
|
|
347
693
|
text-overflow: ellipsis;
|
|
694
|
+
|
|
348
695
|
white-space: nowrap;
|
|
696
|
+
|
|
349
697
|
font-weight: 400;
|
|
698
|
+
|
|
350
699
|
flex: 1;
|
|
700
|
+
|
|
351
701
|
min-width: 0;
|
|
702
|
+
|
|
352
703
|
}
|
|
704
|
+
|
|
353
705
|
.dsh-goal-time {
|
|
706
|
+
|
|
354
707
|
color: var(--dsw-alias-label-secondary);
|
|
708
|
+
|
|
355
709
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
710
|
+
|
|
356
711
|
font-size: 12px;
|
|
712
|
+
|
|
357
713
|
font-weight: 500;
|
|
714
|
+
|
|
358
715
|
white-space: nowrap;
|
|
716
|
+
|
|
359
717
|
display: inline-flex;
|
|
718
|
+
|
|
360
719
|
align-items: center;
|
|
720
|
+
|
|
361
721
|
gap: 4px;
|
|
722
|
+
|
|
362
723
|
flex-shrink: 0;
|
|
724
|
+
|
|
363
725
|
}
|
|
726
|
+
|
|
364
727
|
.dsh-goal-right {
|
|
728
|
+
|
|
365
729
|
display: flex;
|
|
730
|
+
|
|
366
731
|
align-items: center;
|
|
732
|
+
|
|
367
733
|
gap: 8px;
|
|
734
|
+
|
|
368
735
|
margin-left: 12px;
|
|
736
|
+
|
|
369
737
|
flex-shrink: 0;
|
|
738
|
+
|
|
370
739
|
}
|
|
740
|
+
|
|
371
741
|
.dsh-goal-btn {
|
|
742
|
+
|
|
372
743
|
appearance: none;
|
|
744
|
+
|
|
373
745
|
font: inherit;
|
|
746
|
+
|
|
374
747
|
background: var(--dsw-alias-bg-layer-2);
|
|
748
|
+
|
|
375
749
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
750
|
+
|
|
376
751
|
padding: 5px 8px;
|
|
752
|
+
|
|
377
753
|
margin: 0;
|
|
754
|
+
|
|
378
755
|
color: var(--dsw-alias-label-primary);
|
|
756
|
+
|
|
379
757
|
cursor: pointer;
|
|
758
|
+
|
|
380
759
|
display: inline-flex;
|
|
760
|
+
|
|
381
761
|
align-items: center;
|
|
762
|
+
|
|
382
763
|
justify-content: center;
|
|
764
|
+
|
|
383
765
|
gap: 5px;
|
|
766
|
+
|
|
384
767
|
border-radius: 8px;
|
|
768
|
+
|
|
385
769
|
font-size: 12px;
|
|
770
|
+
|
|
386
771
|
font-weight: 500;
|
|
772
|
+
|
|
387
773
|
transition: all 0.15s ease;
|
|
774
|
+
|
|
388
775
|
}
|
|
776
|
+
|
|
389
777
|
.dsh-goal-btn:hover:not(:disabled) {
|
|
778
|
+
|
|
390
779
|
background: var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-2));
|
|
780
|
+
|
|
391
781
|
border-color: var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2));
|
|
782
|
+
|
|
392
783
|
}
|
|
784
|
+
|
|
393
785
|
.dsh-goal-btn.icon-only {
|
|
786
|
+
|
|
394
787
|
padding: 4px 6px;
|
|
788
|
+
|
|
395
789
|
border-radius: 6px;
|
|
790
|
+
|
|
396
791
|
border-color: transparent;
|
|
792
|
+
|
|
397
793
|
background: transparent;
|
|
794
|
+
|
|
398
795
|
color: var(--dsw-alias-label-tertiary);
|
|
796
|
+
|
|
399
797
|
}
|
|
798
|
+
|
|
400
799
|
.dsh-goal-btn.icon-only:hover {
|
|
800
|
+
|
|
401
801
|
color: var(--dsw-alias-label-primary);
|
|
802
|
+
|
|
402
803
|
background: var(--dsw-alias-bg-layer-2);
|
|
804
|
+
|
|
403
805
|
border-color: var(--dsw-alias-border-l2);
|
|
806
|
+
|
|
404
807
|
}
|
|
808
|
+
|
|
405
809
|
.dsh-goal-btn.close {
|
|
810
|
+
|
|
406
811
|
font-size: 13px;
|
|
812
|
+
|
|
407
813
|
font-weight: bold;
|
|
814
|
+
|
|
408
815
|
padding: 3px 6px;
|
|
816
|
+
|
|
409
817
|
}
|
|
818
|
+
|
|
410
819
|
.dsh-goal-btn.close:hover {
|
|
820
|
+
|
|
411
821
|
color: var(--dsw-alias-state-error-primary);
|
|
822
|
+
|
|
412
823
|
}
|
|
824
|
+
|
|
413
825
|
.dsh-goal-btn-primary {
|
|
826
|
+
|
|
414
827
|
background: var(--dsw-alias-label-primary);
|
|
828
|
+
|
|
415
829
|
color: var(--dsw-alias-bg-layer-3);
|
|
830
|
+
|
|
416
831
|
border-color: transparent;
|
|
832
|
+
|
|
417
833
|
}
|
|
834
|
+
|
|
418
835
|
.dsh-goal-btn-primary:hover:not(:disabled) {
|
|
836
|
+
|
|
419
837
|
opacity: 0.88;
|
|
838
|
+
|
|
420
839
|
}
|
|
840
|
+
|
|
421
841
|
.dsh-goal-btn-danger {
|
|
842
|
+
|
|
422
843
|
color: var(--dsw-alias-state-error-primary);
|
|
844
|
+
|
|
423
845
|
border-color: rgba(239, 68, 68, 0.3);
|
|
846
|
+
|
|
424
847
|
}
|
|
848
|
+
|
|
425
849
|
.dsh-goal-btn-danger:hover:not(:disabled) {
|
|
850
|
+
|
|
426
851
|
background: rgba(239, 68, 68, 0.12) !important;
|
|
852
|
+
|
|
427
853
|
border-color: rgba(239, 68, 68, 0.5);
|
|
854
|
+
|
|
428
855
|
}
|
|
429
856
|
|
|
857
|
+
|
|
858
|
+
|
|
430
859
|
/* Modal Details */
|
|
860
|
+
|
|
431
861
|
.dsh-goal-modal-overlay {
|
|
862
|
+
|
|
432
863
|
position: fixed;
|
|
864
|
+
|
|
433
865
|
inset: 0;
|
|
866
|
+
|
|
434
867
|
background: rgba(0, 0, 0, 0.6);
|
|
868
|
+
|
|
435
869
|
display: flex;
|
|
870
|
+
|
|
436
871
|
align-items: center;
|
|
872
|
+
|
|
437
873
|
justify-content: center;
|
|
874
|
+
|
|
438
875
|
z-index: 10000;
|
|
876
|
+
|
|
439
877
|
backdrop-filter: blur(4px);
|
|
878
|
+
|
|
440
879
|
}
|
|
880
|
+
|
|
441
881
|
.dsh-goal-modal {
|
|
882
|
+
|
|
442
883
|
background: var(--dsw-alias-bg-layer-3);
|
|
884
|
+
|
|
443
885
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
886
|
+
|
|
444
887
|
border-radius: 12px;
|
|
888
|
+
|
|
445
889
|
width: 560px;
|
|
890
|
+
|
|
446
891
|
max-width: 90vw;
|
|
892
|
+
|
|
447
893
|
max-height: 80vh;
|
|
894
|
+
|
|
448
895
|
display: flex;
|
|
896
|
+
|
|
449
897
|
flex-direction: column;
|
|
898
|
+
|
|
450
899
|
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.4);
|
|
900
|
+
|
|
451
901
|
}
|
|
902
|
+
|
|
452
903
|
.dsh-goal-modal-head {
|
|
904
|
+
|
|
453
905
|
padding: 16px 20px;
|
|
906
|
+
|
|
454
907
|
border-bottom: 1px solid var(--dsw-alias-border-l2);
|
|
908
|
+
|
|
455
909
|
display: flex;
|
|
910
|
+
|
|
456
911
|
align-items: center;
|
|
912
|
+
|
|
457
913
|
justify-content: space-between;
|
|
914
|
+
|
|
458
915
|
}
|
|
916
|
+
|
|
459
917
|
.dsh-goal-modal-title {
|
|
918
|
+
|
|
460
919
|
font-size: 16px;
|
|
920
|
+
|
|
461
921
|
font-weight: 600;
|
|
922
|
+
|
|
462
923
|
color: var(--dsw-alias-label-primary);
|
|
924
|
+
|
|
463
925
|
display: flex;
|
|
926
|
+
|
|
464
927
|
align-items: center;
|
|
928
|
+
|
|
465
929
|
gap: 8px;
|
|
930
|
+
|
|
466
931
|
}
|
|
932
|
+
|
|
467
933
|
.dsh-goal-modal-body {
|
|
934
|
+
|
|
468
935
|
padding: 18px 20px;
|
|
936
|
+
|
|
469
937
|
overflow-y: auto;
|
|
938
|
+
|
|
470
939
|
display: flex;
|
|
940
|
+
|
|
471
941
|
flex-direction: column;
|
|
942
|
+
|
|
472
943
|
gap: 14px;
|
|
944
|
+
|
|
473
945
|
}
|
|
946
|
+
|
|
474
947
|
.dsh-goal-stat-grid {
|
|
948
|
+
|
|
475
949
|
display: grid;
|
|
950
|
+
|
|
476
951
|
grid-template-columns: repeat(auto-fit, minmax(95px, 1fr));
|
|
952
|
+
|
|
477
953
|
gap: 10px;
|
|
954
|
+
|
|
478
955
|
}
|
|
956
|
+
|
|
479
957
|
.dsh-goal-stat-box {
|
|
958
|
+
|
|
480
959
|
padding: 10px 12px;
|
|
960
|
+
|
|
481
961
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
962
|
+
|
|
482
963
|
border-radius: 8px;
|
|
964
|
+
|
|
483
965
|
background: var(--dsw-alias-bg-layer-2);
|
|
966
|
+
|
|
484
967
|
display: flex;
|
|
968
|
+
|
|
485
969
|
flex-direction: column;
|
|
970
|
+
|
|
486
971
|
gap: 2px;
|
|
972
|
+
|
|
487
973
|
}
|
|
974
|
+
|
|
488
975
|
.dsh-goal-stat-val {
|
|
976
|
+
|
|
489
977
|
font-size: 15px;
|
|
978
|
+
|
|
490
979
|
font-weight: 700;
|
|
980
|
+
|
|
491
981
|
color: var(--dsw-alias-label-primary);
|
|
982
|
+
|
|
492
983
|
overflow: hidden;
|
|
984
|
+
|
|
493
985
|
text-overflow: ellipsis;
|
|
986
|
+
|
|
494
987
|
white-space: nowrap;
|
|
988
|
+
|
|
495
989
|
}
|
|
990
|
+
|
|
496
991
|
.dsh-goal-stat-lbl {
|
|
992
|
+
|
|
497
993
|
font-size: 11px;
|
|
994
|
+
|
|
498
995
|
color: var(--dsw-alias-label-secondary);
|
|
996
|
+
|
|
499
997
|
white-space: nowrap;
|
|
998
|
+
|
|
500
999
|
}
|
|
1000
|
+
|
|
501
1001
|
.dsh-goal-milestone-item {
|
|
1002
|
+
|
|
502
1003
|
display: flex;
|
|
1004
|
+
|
|
503
1005
|
align-items: flex-start;
|
|
1006
|
+
|
|
504
1007
|
gap: 10px;
|
|
1008
|
+
|
|
505
1009
|
padding: 10px 12px;
|
|
1010
|
+
|
|
506
1011
|
border-radius: 8px;
|
|
1012
|
+
|
|
507
1013
|
background: var(--dsw-alias-bg-layer-2);
|
|
1014
|
+
|
|
508
1015
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1016
|
+
|
|
509
1017
|
font-size: 13px;
|
|
1018
|
+
|
|
510
1019
|
transition: all 0.15s ease;
|
|
1020
|
+
|
|
511
1021
|
}
|
|
1022
|
+
|
|
512
1023
|
.dsh-goal-modal-foot {
|
|
1024
|
+
|
|
513
1025
|
padding: 14px 20px;
|
|
1026
|
+
|
|
514
1027
|
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
1028
|
+
|
|
515
1029
|
display: flex;
|
|
1030
|
+
|
|
516
1031
|
justify-content: flex-end;
|
|
1032
|
+
|
|
517
1033
|
align-items: center;
|
|
1034
|
+
|
|
518
1035
|
gap: 10px;
|
|
1036
|
+
|
|
519
1037
|
flex-wrap: wrap;
|
|
1038
|
+
|
|
520
1039
|
}
|
|
1040
|
+
|
|
521
1041
|
.dsh-goal-btn-copy {
|
|
1042
|
+
|
|
522
1043
|
background: var(--dsw-alias-bg-layer-2);
|
|
1044
|
+
|
|
523
1045
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1046
|
+
|
|
524
1047
|
color: var(--dsw-alias-label-primary);
|
|
1048
|
+
|
|
525
1049
|
}
|
|
1050
|
+
|
|
526
1051
|
.dsh-goal-btn-copy.copied {
|
|
1052
|
+
|
|
527
1053
|
background: rgba(16, 185, 129, 0.12);
|
|
1054
|
+
|
|
528
1055
|
border-color: var(--dsw-alias-state-success-primary);
|
|
1056
|
+
|
|
529
1057
|
color: var(--dsw-alias-state-success-primary);
|
|
1058
|
+
|
|
530
1059
|
}
|
|
531
1060
|
|
|
1061
|
+
|
|
1062
|
+
|
|
532
1063
|
/* Settings Card (clinebot pattern) */
|
|
1064
|
+
|
|
533
1065
|
.dsh-goal-card {
|
|
1066
|
+
|
|
534
1067
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1068
|
+
|
|
535
1069
|
background: var(--dsw-alias-bg-layer-3);
|
|
1070
|
+
|
|
536
1071
|
border-radius: 12px;
|
|
1072
|
+
|
|
537
1073
|
list-style: none;
|
|
1074
|
+
|
|
538
1075
|
margin-bottom: 12px;
|
|
1076
|
+
|
|
539
1077
|
transition: border-color 0.15s ease;
|
|
1078
|
+
|
|
540
1079
|
}
|
|
1080
|
+
|
|
541
1081
|
.dsh-goal-card:hover {
|
|
1082
|
+
|
|
542
1083
|
border-color: var(--dsw-alias-label-tertiary);
|
|
1084
|
+
|
|
543
1085
|
}
|
|
1086
|
+
|
|
544
1087
|
.dsh-goal-card-head {
|
|
1088
|
+
|
|
545
1089
|
appearance: none;
|
|
1090
|
+
|
|
546
1091
|
width: 100%;
|
|
1092
|
+
|
|
547
1093
|
font: inherit;
|
|
1094
|
+
|
|
548
1095
|
color: inherit;
|
|
1096
|
+
|
|
549
1097
|
text-align: left;
|
|
1098
|
+
|
|
550
1099
|
cursor: pointer;
|
|
1100
|
+
|
|
551
1101
|
background: transparent;
|
|
1102
|
+
|
|
552
1103
|
border: 0;
|
|
1104
|
+
|
|
553
1105
|
border-radius: 12px;
|
|
1106
|
+
|
|
554
1107
|
display: flex;
|
|
1108
|
+
|
|
555
1109
|
align-items: center;
|
|
1110
|
+
|
|
556
1111
|
gap: 12px;
|
|
1112
|
+
|
|
557
1113
|
padding: 16px 20px;
|
|
1114
|
+
|
|
558
1115
|
}
|
|
1116
|
+
|
|
559
1117
|
.dsh-goal-card-title {
|
|
1118
|
+
|
|
560
1119
|
color: var(--dsw-alias-label-primary);
|
|
1120
|
+
|
|
561
1121
|
font-size: 15px;
|
|
1122
|
+
|
|
562
1123
|
font-weight: 600;
|
|
1124
|
+
|
|
563
1125
|
line-height: 1.4;
|
|
1126
|
+
|
|
564
1127
|
}
|
|
1128
|
+
|
|
565
1129
|
.dsh-goal-card-sub {
|
|
1130
|
+
|
|
566
1131
|
color: var(--dsw-alias-label-secondary);
|
|
1132
|
+
|
|
567
1133
|
font-size: 13px;
|
|
1134
|
+
|
|
568
1135
|
}
|
|
1136
|
+
|
|
569
1137
|
.dsh-goal-card-body {
|
|
1138
|
+
|
|
570
1139
|
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
1140
|
+
|
|
571
1141
|
margin: 0 20px;
|
|
1142
|
+
|
|
572
1143
|
padding: 16px 0 16px;
|
|
1144
|
+
|
|
573
1145
|
display: flex;
|
|
1146
|
+
|
|
574
1147
|
flex-direction: column;
|
|
1148
|
+
|
|
575
1149
|
gap: 14px;
|
|
1150
|
+
|
|
576
1151
|
}
|
|
1152
|
+
|
|
577
1153
|
.dsh-goal-card-field {
|
|
1154
|
+
|
|
578
1155
|
display: flex;
|
|
1156
|
+
|
|
579
1157
|
flex-direction: column;
|
|
1158
|
+
|
|
580
1159
|
gap: 6px;
|
|
1160
|
+
|
|
581
1161
|
}
|
|
1162
|
+
|
|
582
1163
|
.dsh-goal-field-label-row {
|
|
1164
|
+
|
|
583
1165
|
display: flex;
|
|
1166
|
+
|
|
584
1167
|
align-items: center;
|
|
1168
|
+
|
|
585
1169
|
justify-content: space-between;
|
|
1170
|
+
|
|
586
1171
|
font-size: 13px;
|
|
1172
|
+
|
|
587
1173
|
font-weight: 500;
|
|
1174
|
+
|
|
588
1175
|
color: var(--dsw-alias-label-primary);
|
|
1176
|
+
|
|
589
1177
|
}
|
|
1178
|
+
|
|
590
1179
|
.dsh-goal-field-meta {
|
|
1180
|
+
|
|
591
1181
|
display: flex;
|
|
1182
|
+
|
|
592
1183
|
align-items: center;
|
|
1184
|
+
|
|
593
1185
|
gap: 6px;
|
|
1186
|
+
|
|
594
1187
|
font-size: 11px;
|
|
1188
|
+
|
|
595
1189
|
color: var(--dsw-alias-label-tertiary);
|
|
1190
|
+
|
|
596
1191
|
}
|
|
1192
|
+
|
|
597
1193
|
.dsh-goal-override-tag {
|
|
1194
|
+
|
|
598
1195
|
font-size: 11px;
|
|
1196
|
+
|
|
599
1197
|
padding: 2px 6px;
|
|
1198
|
+
|
|
600
1199
|
border-radius: 4px;
|
|
1200
|
+
|
|
601
1201
|
background: var(--dsw-alias-bg-layer-2);
|
|
1202
|
+
|
|
602
1203
|
color: var(--dsw-alias-label-secondary);
|
|
1204
|
+
|
|
603
1205
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1206
|
+
|
|
604
1207
|
}
|
|
1208
|
+
|
|
605
1209
|
.dsh-goal-card-input {
|
|
1210
|
+
|
|
606
1211
|
height: 36px;
|
|
1212
|
+
|
|
607
1213
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1214
|
+
|
|
608
1215
|
background: var(--dsw-alias-bg-layer-2);
|
|
1216
|
+
|
|
609
1217
|
color: var(--dsw-alias-label-primary);
|
|
1218
|
+
|
|
610
1219
|
border-radius: 8px;
|
|
1220
|
+
|
|
611
1221
|
padding: 0 12px;
|
|
1222
|
+
|
|
612
1223
|
font-size: 13px;
|
|
1224
|
+
|
|
613
1225
|
width: 100%;
|
|
1226
|
+
|
|
614
1227
|
box-sizing: border-box;
|
|
1228
|
+
|
|
615
1229
|
}
|
|
1230
|
+
|
|
616
1231
|
.dsh-goal-card-input:focus {
|
|
1232
|
+
|
|
617
1233
|
outline: none;
|
|
1234
|
+
|
|
618
1235
|
border-color: var(--dsw-alias-state-brand-primary);
|
|
1236
|
+
|
|
619
1237
|
}
|
|
1238
|
+
|
|
620
1239
|
.dsh-goal-card-input[aria-invalid="true"] {
|
|
1240
|
+
|
|
621
1241
|
border-color: var(--dsw-alias-state-error-primary);
|
|
1242
|
+
|
|
622
1243
|
}
|
|
1244
|
+
|
|
623
1245
|
.dsh-goal-chev {
|
|
1246
|
+
|
|
624
1247
|
margin-left: auto;
|
|
1248
|
+
|
|
625
1249
|
flex: none;
|
|
1250
|
+
|
|
626
1251
|
color: var(--dsw-alias-label-tertiary);
|
|
1252
|
+
|
|
627
1253
|
transition: transform .16s ease;
|
|
1254
|
+
|
|
628
1255
|
}
|
|
1256
|
+
|
|
629
1257
|
.dsh-goal-chev-open {
|
|
1258
|
+
|
|
630
1259
|
transform: rotate(180deg);
|
|
1260
|
+
|
|
631
1261
|
}
|
|
1262
|
+
|
|
632
1263
|
.dsh-goal-check-row {
|
|
1264
|
+
|
|
633
1265
|
display: flex;
|
|
1266
|
+
|
|
634
1267
|
align-items: center;
|
|
1268
|
+
|
|
635
1269
|
justify-content: space-between;
|
|
1270
|
+
|
|
636
1271
|
padding: 6px 0;
|
|
1272
|
+
|
|
637
1273
|
}
|
|
1274
|
+
|
|
638
1275
|
.dsh-goal-check {
|
|
1276
|
+
|
|
639
1277
|
display: flex;
|
|
1278
|
+
|
|
640
1279
|
align-items: center;
|
|
1280
|
+
|
|
641
1281
|
gap: 8px;
|
|
1282
|
+
|
|
642
1283
|
font-size: 13px;
|
|
1284
|
+
|
|
643
1285
|
color: var(--dsw-alias-label-primary);
|
|
1286
|
+
|
|
644
1287
|
cursor: pointer;
|
|
1288
|
+
|
|
645
1289
|
}
|
|
1290
|
+
|
|
646
1291
|
.dsh-goal-foot {
|
|
1292
|
+
|
|
647
1293
|
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
1294
|
+
|
|
648
1295
|
display: flex;
|
|
1296
|
+
|
|
649
1297
|
justify-content: flex-end;
|
|
1298
|
+
|
|
650
1299
|
align-items: center;
|
|
1300
|
+
|
|
651
1301
|
gap: 10px;
|
|
1302
|
+
|
|
652
1303
|
padding: 14px 0 4px;
|
|
1304
|
+
|
|
653
1305
|
}
|
|
1306
|
+
|
|
654
1307
|
.dsh-goal-foot-note {
|
|
1308
|
+
|
|
655
1309
|
color: var(--dsw-alias-state-error-primary);
|
|
1310
|
+
|
|
656
1311
|
font-size: 12px;
|
|
1312
|
+
|
|
657
1313
|
margin-right: auto;
|
|
1314
|
+
|
|
658
1315
|
}
|
|
1316
|
+
|
|
659
1317
|
.dsh-goal-btn-inline-reset {
|
|
1318
|
+
|
|
660
1319
|
background: transparent;
|
|
1320
|
+
|
|
661
1321
|
border: 0;
|
|
1322
|
+
|
|
662
1323
|
cursor: pointer;
|
|
1324
|
+
|
|
663
1325
|
padding: 2px 6px;
|
|
1326
|
+
|
|
664
1327
|
display: flex;
|
|
1328
|
+
|
|
665
1329
|
align-items: center;
|
|
1330
|
+
|
|
666
1331
|
gap: 4px;
|
|
1332
|
+
|
|
667
1333
|
border-radius: 4px;
|
|
1334
|
+
|
|
668
1335
|
color: var(--dsw-alias-label-tertiary);
|
|
1336
|
+
|
|
669
1337
|
font-size: 11px;
|
|
1338
|
+
|
|
670
1339
|
}
|
|
1340
|
+
|
|
671
1341
|
.dsh-goal-btn-inline-reset:hover {
|
|
1342
|
+
|
|
672
1343
|
color: var(--dsw-alias-label-primary);
|
|
1344
|
+
|
|
673
1345
|
background: var(--dsw-alias-bg-layer-2);
|
|
1346
|
+
|
|
674
1347
|
}
|
|
1348
|
+
|
|
675
1349
|
.dsh-goal-discard {
|
|
1350
|
+
|
|
676
1351
|
appearance: none;
|
|
1352
|
+
|
|
677
1353
|
font: inherit;
|
|
1354
|
+
|
|
678
1355
|
cursor: pointer;
|
|
1356
|
+
|
|
679
1357
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
1358
|
+
|
|
680
1359
|
border-radius: 8px;
|
|
1360
|
+
|
|
681
1361
|
padding: 6px 14px;
|
|
1362
|
+
|
|
682
1363
|
font-size: 13px;
|
|
1364
|
+
|
|
683
1365
|
background: var(--dsw-alias-bg-layer-2);
|
|
1366
|
+
|
|
684
1367
|
color: var(--dsw-alias-label-primary);
|
|
1368
|
+
|
|
685
1369
|
transition: all 0.15s ease;
|
|
1370
|
+
|
|
686
1371
|
}
|
|
1372
|
+
|
|
687
1373
|
.dsh-goal-discard:hover:not(:disabled) {
|
|
1374
|
+
|
|
688
1375
|
background: var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-2));
|
|
1376
|
+
|
|
689
1377
|
border-color: var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2));
|
|
1378
|
+
|
|
690
1379
|
}
|
|
1380
|
+
|
|
691
1381
|
.dsh-goal-discard:disabled {
|
|
1382
|
+
|
|
692
1383
|
opacity: 0.4;
|
|
1384
|
+
|
|
693
1385
|
cursor: not-allowed;
|
|
1386
|
+
|
|
694
1387
|
}
|
|
1388
|
+
|
|
695
1389
|
.dsh-goal-save {
|
|
1390
|
+
|
|
696
1391
|
appearance: none;
|
|
1392
|
+
|
|
697
1393
|
font: inherit;
|
|
1394
|
+
|
|
698
1395
|
cursor: pointer;
|
|
1396
|
+
|
|
699
1397
|
border: 1px solid transparent;
|
|
1398
|
+
|
|
700
1399
|
border-radius: 8px;
|
|
1400
|
+
|
|
701
1401
|
padding: 6px 16px;
|
|
1402
|
+
|
|
702
1403
|
font-size: 13px;
|
|
1404
|
+
|
|
703
1405
|
background: var(--dsw-alias-label-primary);
|
|
1406
|
+
|
|
704
1407
|
color: var(--dsw-alias-bg-layer-3);
|
|
1408
|
+
|
|
705
1409
|
font-weight: 500;
|
|
1410
|
+
|
|
706
1411
|
transition: opacity 0.15s ease;
|
|
1412
|
+
|
|
707
1413
|
}
|
|
1414
|
+
|
|
708
1415
|
.dsh-goal-save:hover:not(:disabled) {
|
|
1416
|
+
|
|
709
1417
|
opacity: 0.88;
|
|
1418
|
+
|
|
710
1419
|
}
|
|
1420
|
+
|
|
711
1421
|
.dsh-goal-save:disabled {
|
|
1422
|
+
|
|
712
1423
|
opacity: 0.4;
|
|
1424
|
+
|
|
713
1425
|
cursor: not-allowed;
|
|
1426
|
+
|
|
714
1427
|
}
|
|
1428
|
+
|
|
715
1429
|
`;
|
|
716
1430
|
|
|
1431
|
+
|
|
1432
|
+
|
|
717
1433
|
function injectStylesOnce() {
|
|
1434
|
+
|
|
718
1435
|
if (typeof document === 'undefined') return;
|
|
1436
|
+
|
|
719
1437
|
if (document.getElementById('dsh-goal-styles')) return;
|
|
1438
|
+
|
|
720
1439
|
const style = document.createElement('style');
|
|
1440
|
+
|
|
721
1441
|
style.id = 'dsh-goal-styles';
|
|
1442
|
+
|
|
722
1443
|
style.dataset.dshPlugin = NS;
|
|
1444
|
+
|
|
723
1445
|
style.textContent = CSS_STYLES;
|
|
1446
|
+
|
|
724
1447
|
document.head.appendChild(style);
|
|
1448
|
+
|
|
725
1449
|
}
|
|
726
1450
|
|
|
727
1451
|
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
|
|
728
1455
|
// Синтез приятных звуковых колокольчиков через Web Audio API (Item 6)
|
|
1456
|
+
|
|
729
1457
|
function playGoalChime(isSuccess = true) {
|
|
1458
|
+
|
|
730
1459
|
if (typeof window === 'undefined') return;
|
|
1460
|
+
|
|
731
1461
|
try {
|
|
1462
|
+
|
|
732
1463
|
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
|
1464
|
+
|
|
733
1465
|
if (!AudioCtx) return;
|
|
1466
|
+
|
|
734
1467
|
const actx = new AudioCtx();
|
|
1468
|
+
|
|
735
1469
|
if (actx.state === 'suspended') {
|
|
1470
|
+
|
|
736
1471
|
actx.resume().catch(() => {});
|
|
1472
|
+
|
|
737
1473
|
}
|
|
1474
|
+
|
|
738
1475
|
const now = actx.currentTime;
|
|
1476
|
+
|
|
739
1477
|
// Пентатонические аккорды: успех — восходящий C5-E5-G5-C6; сбой/ошибка — нисходящий G4-Eb4-C4
|
|
1478
|
+
|
|
740
1479
|
const notes = isSuccess
|
|
1480
|
+
|
|
741
1481
|
? [523.25, 659.25, 783.99, 1046.50]
|
|
1482
|
+
|
|
742
1483
|
: [392.00, 311.13, 261.63];
|
|
743
1484
|
|
|
1485
|
+
|
|
1486
|
+
|
|
744
1487
|
notes.forEach((freq, idx) => {
|
|
1488
|
+
|
|
745
1489
|
const osc = actx.createOscillator();
|
|
1490
|
+
|
|
746
1491
|
const gain = actx.createGain();
|
|
1492
|
+
|
|
747
1493
|
osc.type = 'sine';
|
|
1494
|
+
|
|
748
1495
|
osc.frequency.setValueAtTime(freq, now + idx * 0.12);
|
|
1496
|
+
|
|
749
1497
|
|
|
1498
|
+
|
|
750
1499
|
gain.gain.setValueAtTime(0.001, now + idx * 0.12);
|
|
1500
|
+
|
|
751
1501
|
gain.gain.exponentialRampToValueAtTime(0.15, now + idx * 0.12 + 0.02);
|
|
1502
|
+
|
|
752
1503
|
gain.gain.exponentialRampToValueAtTime(0.0001, now + idx * 0.12 + 0.4);
|
|
753
1504
|
|
|
1505
|
+
|
|
1506
|
+
|
|
754
1507
|
osc.connect(gain);
|
|
1508
|
+
|
|
755
1509
|
gain.connect(actx.destination);
|
|
756
1510
|
|
|
1511
|
+
|
|
1512
|
+
|
|
757
1513
|
osc.start(now + idx * 0.12);
|
|
1514
|
+
|
|
758
1515
|
osc.stop(now + idx * 0.12 + 0.45);
|
|
1516
|
+
|
|
759
1517
|
});
|
|
760
1518
|
|
|
1519
|
+
|
|
1520
|
+
|
|
761
1521
|
setTimeout(() => {
|
|
1522
|
+
|
|
762
1523
|
try { actx.close(); } catch (_) {}
|
|
1524
|
+
|
|
763
1525
|
}, 2000);
|
|
1526
|
+
|
|
764
1527
|
} catch (_) {}
|
|
1528
|
+
|
|
765
1529
|
}
|
|
766
1530
|
|
|
767
1531
|
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
|
|
768
1535
|
// --- ГЕНЕРАТОР ОТЧЕТА ДЛЯ GITHUB / GITEA PR COMMENT ---
|
|
1536
|
+
|
|
769
1537
|
function generateGitHubPRComment(state) {
|
|
1538
|
+
|
|
770
1539
|
if (!state) return '';
|
|
1540
|
+
|
|
771
1541
|
const title = state.title || 'Goal Report';
|
|
1542
|
+
|
|
772
1543
|
const status = state.state === 'COMPLETED' ? 'COMPLETED' : state.state;
|
|
1544
|
+
|
|
773
1545
|
const elapsed = state.formattedElapsed || '0s';
|
|
1546
|
+
|
|
774
1547
|
const iter = `${state.iterationsCount || 0}/${state.maxIterations || 25}`;
|
|
1548
|
+
|
|
775
1549
|
const totalTokens = state.tokensUsage?.totalTokens || 0;
|
|
1550
|
+
|
|
776
1551
|
const promptTokens = state.tokensUsage?.promptTokens || 0;
|
|
1552
|
+
|
|
777
1553
|
const compTokens = state.tokensUsage?.completionTokens || 0;
|
|
1554
|
+
|
|
778
1555
|
const gitCommit = state.gitStartCommit ? ` | **Git Start:** \`${state.gitStartCommit}\` 📌` : '';
|
|
779
1556
|
|
|
1557
|
+
|
|
1558
|
+
|
|
780
1559
|
let md = `## 🎯 Autonomous Goal Resolution: ${title}\n\n`;
|
|
1560
|
+
|
|
781
1561
|
md += `> **Status:** \`${status}\` 🚀 | **Duration:** \`${elapsed}\` ⏱️ | **Iterations:** \`${iter}\` 🔄${gitCommit}\n\n`;
|
|
782
1562
|
|
|
1563
|
+
|
|
1564
|
+
|
|
783
1565
|
if (totalTokens > 0) {
|
|
1566
|
+
|
|
784
1567
|
md += `### 📊 Telemetry & Token Usage\n`;
|
|
1568
|
+
|
|
785
1569
|
md += `- **Total Tokens:** \`${totalTokens.toLocaleString('en-US')}\` (Prompt: \`${promptTokens.toLocaleString('en-US')}\`, Completion: \`${compTokens.toLocaleString('en-US')}\`)\n\n`;
|
|
1570
|
+
|
|
786
1571
|
}
|
|
787
1572
|
|
|
1573
|
+
|
|
1574
|
+
|
|
788
1575
|
if (state.resultSummary) {
|
|
1576
|
+
|
|
789
1577
|
md += `### 📦 Deliverables & Achievements\n${state.resultSummary}\n\n`;
|
|
1578
|
+
|
|
790
1579
|
} else if (state.description) {
|
|
1580
|
+
|
|
791
1581
|
md += `### 📝 Objective\n${state.description}\n\n`;
|
|
1582
|
+
|
|
792
1583
|
}
|
|
793
1584
|
|
|
1585
|
+
|
|
1586
|
+
|
|
794
1587
|
const milestones = state.milestones || [];
|
|
1588
|
+
|
|
795
1589
|
if (milestones.length > 0) {
|
|
1590
|
+
|
|
796
1591
|
const completedCount = milestones.filter(m => m.status === 'completed').length;
|
|
1592
|
+
|
|
797
1593
|
md += `<details>\n<summary><b>📋 Milestones Breakdown (${completedCount}/${milestones.length} Completed)</b></summary>\n\n`;
|
|
1594
|
+
|
|
798
1595
|
md += `| # | Status | Milestone | Notes |\n|---|---|---|---|\n`;
|
|
1596
|
+
|
|
799
1597
|
milestones.forEach((m, idx) => {
|
|
1598
|
+
|
|
800
1599
|
const mark = m.status === 'completed' ? '✅ Done' : m.status === 'in_progress' ? '🔄 In Progress' : '⏳ Pending';
|
|
1600
|
+
|
|
801
1601
|
const cleanTitle = (m.title || '').replace(/\|/g, '\\|');
|
|
1602
|
+
|
|
802
1603
|
const cleanNotes = (m.notes || '').replace(/\|/g, '\\|');
|
|
1604
|
+
|
|
803
1605
|
md += `| ${idx + 1} | ${mark} | ${cleanTitle} | ${cleanNotes || '—'} |\n`;
|
|
1606
|
+
|
|
804
1607
|
});
|
|
1608
|
+
|
|
805
1609
|
md += `\n</details>\n\n`;
|
|
1610
|
+
|
|
806
1611
|
}
|
|
807
1612
|
|
|
1613
|
+
|
|
1614
|
+
|
|
808
1615
|
md += `*Automated by [@goodandready/dsh-goal](https://github.com/GooDAnDReaDY/dsh-goal)*\n`;
|
|
1616
|
+
|
|
809
1617
|
return md;
|
|
1618
|
+
|
|
810
1619
|
}
|
|
811
1620
|
|
|
1621
|
+
|
|
1622
|
+
|
|
812
1623
|
// --- ГЕНЕРАТОР MARKDOWN-ОТЧЕТА ---
|
|
1624
|
+
|
|
813
1625
|
function generateMarkdownReport(state) {
|
|
1626
|
+
|
|
814
1627
|
if (!state) return '';
|
|
1628
|
+
|
|
815
1629
|
const title = state.title || 'Goal Report';
|
|
1630
|
+
|
|
816
1631
|
const status = state.state === 'COMPLETED' ? 'COMPLETED' : state.state;
|
|
1632
|
+
|
|
817
1633
|
const elapsed = state.formattedElapsed || '0s';
|
|
1634
|
+
|
|
818
1635
|
const iter = `${state.iterationsCount || 0}/${state.maxIterations || 25}`;
|
|
1636
|
+
|
|
819
1637
|
const totalTokens = state.tokensUsage?.totalTokens || 0;
|
|
1638
|
+
|
|
820
1639
|
const promptTokens = state.tokensUsage?.promptTokens || 0;
|
|
1640
|
+
|
|
821
1641
|
const compTokens = state.tokensUsage?.completionTokens || 0;
|
|
822
1642
|
|
|
1643
|
+
|
|
1644
|
+
|
|
823
1645
|
let md = `# 🎯 Goal Report: ${title}\n\n`;
|
|
1646
|
+
|
|
824
1647
|
md += `**Status:** \`${status}\` | **Duration:** \`${elapsed}\` | **Iterations:** \`${iter}\`\n`;
|
|
1648
|
+
|
|
825
1649
|
if (totalTokens > 0) {
|
|
1650
|
+
|
|
826
1651
|
md += `**Tokens:** \`${totalTokens.toLocaleString('en-US')}\` (Prompt: \`${promptTokens.toLocaleString('en-US')}\`, Completion: \`${compTokens.toLocaleString('en-US')}\`)\n`;
|
|
1652
|
+
|
|
827
1653
|
}
|
|
1654
|
+
|
|
828
1655
|
md += '\n';
|
|
829
1656
|
|
|
1657
|
+
|
|
1658
|
+
|
|
830
1659
|
if (state.description) {
|
|
1660
|
+
|
|
831
1661
|
md += `### Description\n${state.description}\n\n`;
|
|
1662
|
+
|
|
832
1663
|
}
|
|
833
1664
|
|
|
1665
|
+
|
|
1666
|
+
|
|
834
1667
|
if (state.resultSummary) {
|
|
1668
|
+
|
|
835
1669
|
md += `### Summary & Deliverables\n${state.resultSummary}\n\n`;
|
|
1670
|
+
|
|
836
1671
|
}
|
|
837
1672
|
|
|
1673
|
+
|
|
1674
|
+
|
|
838
1675
|
const milestones = state.milestones || [];
|
|
1676
|
+
|
|
839
1677
|
if (milestones.length > 0) {
|
|
1678
|
+
|
|
840
1679
|
md += `### Milestones\n| # | Status | Title | Notes |\n|---|---|---|---|\n`;
|
|
1680
|
+
|
|
841
1681
|
milestones.forEach((m, idx) => {
|
|
1682
|
+
|
|
842
1683
|
const mark = m.status === 'completed' ? '✅ Done' : m.status === 'in_progress' ? '🔄 In Progress' : '⏳ Pending';
|
|
1684
|
+
|
|
843
1685
|
const cleanTitle = (m.title || '').replace(/\|/g, '\\|');
|
|
1686
|
+
|
|
844
1687
|
const cleanNotes = (m.notes || '').replace(/\|/g, '\\|');
|
|
1688
|
+
|
|
845
1689
|
md += `| ${idx + 1} | ${mark} | ${cleanTitle} | ${cleanNotes || '—'} |\n`;
|
|
1690
|
+
|
|
846
1691
|
});
|
|
1692
|
+
|
|
847
1693
|
md += '\n';
|
|
1694
|
+
|
|
848
1695
|
}
|
|
849
1696
|
|
|
1697
|
+
|
|
1698
|
+
|
|
850
1699
|
md += `*Generated by DSH Goal Engine at ${new Date().toISOString()}*\n`;
|
|
1700
|
+
|
|
851
1701
|
return md;
|
|
1702
|
+
|
|
852
1703
|
}
|
|
853
1704
|
|
|
1705
|
+
|
|
1706
|
+
|
|
854
1707
|
// --- МОДАЛЬНОЕ ОКНО ДЕТАЛЕЙ (MODAL DETAILS) ---
|
|
1708
|
+
|
|
855
1709
|
function GoalDetailsModal({ state, onClose, onAction, t }) {
|
|
1710
|
+
|
|
856
1711
|
if (!state) return null;
|
|
857
1712
|
|
|
1713
|
+
|
|
1714
|
+
|
|
858
1715
|
const milestones = state.milestones || [];
|
|
1716
|
+
|
|
859
1717
|
const isCompleted = state.state === 'COMPLETED';
|
|
1718
|
+
|
|
860
1719
|
const [copied, setCopied] = useState(false);
|
|
1720
|
+
|
|
861
1721
|
const [prCopied, setPrCopied] = useState(false);
|
|
1722
|
+
|
|
862
1723
|
const [gitCopied, setGitCopied] = useState(false);
|
|
1724
|
+
|
|
863
1725
|
const [nudgeText, setNudgeText] = useState('');
|
|
1726
|
+
|
|
864
1727
|
const [nudgeSent, setNudgeSent] = useState(false);
|
|
865
1728
|
|
|
1729
|
+
|
|
1730
|
+
|
|
866
1731
|
const totalTokens = state.tokensUsage?.totalTokens || 0;
|
|
1732
|
+
|
|
867
1733
|
const promptTokens = state.tokensUsage?.promptTokens || 0;
|
|
1734
|
+
|
|
868
1735
|
const compTokens = state.tokensUsage?.completionTokens || 0;
|
|
1736
|
+
|
|
869
1737
|
const tokensFormatted = totalTokens > 0
|
|
1738
|
+
|
|
870
1739
|
? (totalTokens >= 1000 ? `${(totalTokens / 1000).toFixed(1)}k` : `${totalTokens}`)
|
|
1740
|
+
|
|
871
1741
|
: '—';
|
|
1742
|
+
|
|
872
1743
|
const tokensTooltip = totalTokens > 0
|
|
1744
|
+
|
|
873
1745
|
? `Prompt: ${promptTokens.toLocaleString('en-US')} | Completion: ${compTokens.toLocaleString('en-US')} | Total: ${totalTokens.toLocaleString('en-US')}`
|
|
1746
|
+
|
|
874
1747
|
: 'Tokens not recorded yet';
|
|
875
1748
|
|
|
1749
|
+
|
|
1750
|
+
|
|
876
1751
|
const handleCopyReport = () => {
|
|
1752
|
+
|
|
877
1753
|
const md = generateMarkdownReport(state);
|
|
1754
|
+
|
|
878
1755
|
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
1756
|
+
|
|
879
1757
|
navigator.clipboard.writeText(md).then(() => {
|
|
1758
|
+
|
|
880
1759
|
setCopied(true);
|
|
1760
|
+
|
|
881
1761
|
setTimeout(() => setCopied(false), 2500);
|
|
1762
|
+
|
|
882
1763
|
}).catch((err) => {
|
|
1764
|
+
|
|
883
1765
|
console.warn('[dsh-goal] Clipboard copy failed:', err);
|
|
1766
|
+
|
|
884
1767
|
});
|
|
1768
|
+
|
|
885
1769
|
}
|
|
1770
|
+
|
|
886
1771
|
};
|
|
887
1772
|
|
|
1773
|
+
|
|
1774
|
+
|
|
888
1775
|
return React.createElement(
|
|
1776
|
+
|
|
889
1777
|
'div',
|
|
1778
|
+
|
|
890
1779
|
{ className: 'dsh-goal-modal-overlay', onClick: onClose },
|
|
1780
|
+
|
|
891
1781
|
React.createElement(
|
|
1782
|
+
|
|
892
1783
|
'div',
|
|
1784
|
+
|
|
893
1785
|
{
|
|
1786
|
+
|
|
894
1787
|
className: 'dsh-goal-modal',
|
|
1788
|
+
|
|
895
1789
|
onClick: (e) => e.stopPropagation(),
|
|
1790
|
+
|
|
896
1791
|
},
|
|
1792
|
+
|
|
897
1793
|
React.createElement(
|
|
1794
|
+
|
|
898
1795
|
'div',
|
|
1796
|
+
|
|
899
1797
|
{ className: 'dsh-goal-modal-head' },
|
|
1798
|
+
|
|
900
1799
|
React.createElement(
|
|
1800
|
+
|
|
901
1801
|
'div',
|
|
1802
|
+
|
|
902
1803
|
{ className: 'dsh-goal-modal-title' },
|
|
1804
|
+
|
|
903
1805
|
isCompleted
|
|
1806
|
+
|
|
904
1807
|
? React.createElement(IconCheck, { size: 18, className: 'cb-badge-ok' })
|
|
1808
|
+
|
|
905
1809
|
: React.createElement(IconTarget, { size: 18 }),
|
|
1810
|
+
|
|
906
1811
|
isCompleted ? (t('modalTitleCompleted') || 'Goal completed: plan and results') : (t('modalTitle') || 'Goal plan and status'),
|
|
1812
|
+
|
|
907
1813
|
),
|
|
1814
|
+
|
|
908
1815
|
React.createElement(
|
|
1816
|
+
|
|
909
1817
|
'button',
|
|
1818
|
+
|
|
910
1819
|
{
|
|
1820
|
+
|
|
911
1821
|
className: 'dsh-goal-btn icon-only close',
|
|
1822
|
+
|
|
912
1823
|
onClick: onClose,
|
|
1824
|
+
|
|
913
1825
|
title: t('close') || 'Close',
|
|
1826
|
+
|
|
914
1827
|
},
|
|
1828
|
+
|
|
915
1829
|
'✕',
|
|
1830
|
+
|
|
916
1831
|
),
|
|
1832
|
+
|
|
917
1833
|
),
|
|
1834
|
+
|
|
918
1835
|
React.createElement(
|
|
1836
|
+
|
|
919
1837
|
'div',
|
|
1838
|
+
|
|
920
1839
|
{ className: 'dsh-goal-modal-body' },
|
|
1840
|
+
|
|
921
1841
|
React.createElement(
|
|
1842
|
+
|
|
922
1843
|
'div',
|
|
1844
|
+
|
|
923
1845
|
null,
|
|
1846
|
+
|
|
924
1847
|
React.createElement('div', { style: { fontWeight: 600, fontSize: 16, marginBottom: 4, color: 'var(--dsw-alias-label-primary)' } }, state.title),
|
|
1848
|
+
|
|
925
1849
|
state.description ? React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', fontSize: 13, marginBottom: 6, lineHeight: 1.4 } }, state.description) : null,
|
|
1850
|
+
|
|
926
1851
|
),
|
|
1852
|
+
|
|
927
1853
|
!isCompleted
|
|
1854
|
+
|
|
928
1855
|
? React.createElement(
|
|
1856
|
+
|
|
929
1857
|
'div',
|
|
1858
|
+
|
|
930
1859
|
{ className: 'dsh-goal-nudge-box', style: { marginBottom: 12 } },
|
|
1860
|
+
|
|
931
1861
|
React.createElement('input', {
|
|
1862
|
+
|
|
932
1863
|
type: 'text',
|
|
1864
|
+
|
|
933
1865
|
className: 'dsh-goal-card-input',
|
|
1866
|
+
|
|
934
1867
|
style: { flex: 1, fontSize: 12 },
|
|
1868
|
+
|
|
935
1869
|
placeholder: t('nudgePlaceholder') || '💡 Clarify task or steer agent...',
|
|
1870
|
+
|
|
936
1871
|
value: nudgeText,
|
|
1872
|
+
|
|
937
1873
|
onChange: (e) => setNudgeText(e.target.value),
|
|
1874
|
+
|
|
938
1875
|
onKeyDown: (e) => {
|
|
1876
|
+
|
|
939
1877
|
if (e.key === 'Enter' && nudgeText.trim()) {
|
|
1878
|
+
|
|
940
1879
|
onAction('nudge', { text: nudgeText.trim() });
|
|
1880
|
+
|
|
941
1881
|
setNudgeText('');
|
|
1882
|
+
|
|
942
1883
|
setNudgeSent(true);
|
|
1884
|
+
|
|
943
1885
|
setTimeout(() => setNudgeSent(false), 2000);
|
|
1886
|
+
|
|
944
1887
|
}
|
|
1888
|
+
|
|
945
1889
|
},
|
|
1890
|
+
|
|
946
1891
|
}),
|
|
1892
|
+
|
|
947
1893
|
React.createElement(
|
|
1894
|
+
|
|
948
1895
|
'button',
|
|
1896
|
+
|
|
949
1897
|
{
|
|
1898
|
+
|
|
950
1899
|
type: 'button',
|
|
1900
|
+
|
|
951
1901
|
className: `dsh-goal-btn ${nudgeSent ? 'dsh-goal-btn-primary' : ''}`,
|
|
1902
|
+
|
|
952
1903
|
disabled: !nudgeText.trim(),
|
|
1904
|
+
|
|
953
1905
|
onClick: () => {
|
|
1906
|
+
|
|
954
1907
|
if (nudgeText.trim()) {
|
|
1908
|
+
|
|
955
1909
|
onAction('nudge', { text: nudgeText.trim() });
|
|
1910
|
+
|
|
956
1911
|
setNudgeText('');
|
|
1912
|
+
|
|
957
1913
|
setNudgeSent(true);
|
|
1914
|
+
|
|
958
1915
|
setTimeout(() => setNudgeSent(false), 2000);
|
|
1916
|
+
|
|
959
1917
|
}
|
|
1918
|
+
|
|
960
1919
|
},
|
|
1920
|
+
|
|
961
1921
|
},
|
|
1922
|
+
|
|
962
1923
|
nudgeSent ? (t('nudgeSent') || '✅ Steering sent!') : (t('sendNudge') || 'Steer'),
|
|
1924
|
+
|
|
963
1925
|
),
|
|
1926
|
+
|
|
964
1927
|
)
|
|
1928
|
+
|
|
965
1929
|
: null,
|
|
1930
|
+
|
|
966
1931
|
state.gitStartCommit
|
|
1932
|
+
|
|
967
1933
|
? React.createElement(
|
|
1934
|
+
|
|
968
1935
|
'div',
|
|
1936
|
+
|
|
969
1937
|
{ style: { display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 } },
|
|
1938
|
+
|
|
970
1939
|
React.createElement(
|
|
1940
|
+
|
|
971
1941
|
'span',
|
|
1942
|
+
|
|
972
1943
|
{
|
|
1944
|
+
|
|
973
1945
|
className: 'dsh-goal-git-badge',
|
|
1946
|
+
|
|
974
1947
|
title: t('copyGitDiff') || 'Click to copy git diff command',
|
|
1948
|
+
|
|
975
1949
|
onClick: () => {
|
|
1950
|
+
|
|
976
1951
|
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
1952
|
+
|
|
977
1953
|
navigator.clipboard.writeText(`git diff ${state.gitStartCommit}`).then(() => {
|
|
1954
|
+
|
|
978
1955
|
setGitCopied(true);
|
|
1956
|
+
|
|
979
1957
|
setTimeout(() => setGitCopied(false), 2000);
|
|
1958
|
+
|
|
980
1959
|
});
|
|
1960
|
+
|
|
981
1961
|
}
|
|
1962
|
+
|
|
982
1963
|
},
|
|
1964
|
+
|
|
983
1965
|
},
|
|
1966
|
+
|
|
984
1967
|
gitCopied ? '✅ Diff copied!' : `📌 Git: ${state.gitStartCommit}`,
|
|
1968
|
+
|
|
985
1969
|
),
|
|
1970
|
+
|
|
986
1971
|
)
|
|
1972
|
+
|
|
987
1973
|
: null,
|
|
1974
|
+
|
|
988
1975
|
React.createElement(
|
|
1976
|
+
|
|
989
1977
|
'div',
|
|
1978
|
+
|
|
990
1979
|
{ className: 'dsh-goal-stat-grid' },
|
|
1980
|
+
|
|
991
1981
|
React.createElement(
|
|
1982
|
+
|
|
992
1983
|
'div',
|
|
1984
|
+
|
|
993
1985
|
{ className: 'dsh-goal-stat-box' },
|
|
1986
|
+
|
|
994
1987
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedElapsed || '0s'),
|
|
1988
|
+
|
|
995
1989
|
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('time') || 'Running time'),
|
|
1990
|
+
|
|
996
1991
|
),
|
|
1992
|
+
|
|
997
1993
|
React.createElement(
|
|
1994
|
+
|
|
998
1995
|
'div',
|
|
1996
|
+
|
|
999
1997
|
{ className: 'dsh-goal-stat-box' },
|
|
1998
|
+
|
|
1000
1999
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, state.formattedETA || (isCompleted ? (t('done') || 'Done') : '—')),
|
|
2000
|
+
|
|
1001
2001
|
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('eta') || 'ETA'),
|
|
2002
|
+
|
|
1002
2003
|
),
|
|
2004
|
+
|
|
1003
2005
|
React.createElement(
|
|
2006
|
+
|
|
1004
2007
|
'div',
|
|
2008
|
+
|
|
1005
2009
|
{ className: 'dsh-goal-stat-box', title: tokensTooltip },
|
|
2010
|
+
|
|
1006
2011
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, tokensFormatted),
|
|
2012
|
+
|
|
1007
2013
|
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('tokens') || 'Tokens'),
|
|
2014
|
+
|
|
1008
2015
|
),
|
|
2016
|
+
|
|
1009
2017
|
React.createElement(
|
|
2018
|
+
|
|
1010
2019
|
'div',
|
|
2020
|
+
|
|
1011
2021
|
{ className: 'dsh-goal-stat-box' },
|
|
2022
|
+
|
|
1012
2023
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.iterationsCount || 0}/${state.maxIterations || 25}`),
|
|
2024
|
+
|
|
1013
2025
|
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('iterations') || 'Iterations'),
|
|
2026
|
+
|
|
1014
2027
|
),
|
|
2028
|
+
|
|
1015
2029
|
React.createElement(
|
|
2030
|
+
|
|
1016
2031
|
'div',
|
|
2032
|
+
|
|
1017
2033
|
{ className: 'dsh-goal-stat-box' },
|
|
2034
|
+
|
|
1018
2035
|
React.createElement('span', { className: 'dsh-goal-stat-val' }, `${state.progressPercent || 0}%`),
|
|
2036
|
+
|
|
1019
2037
|
React.createElement('span', { className: 'dsh-goal-stat-lbl' }, t('progress') || 'Progress'),
|
|
2038
|
+
|
|
1020
2039
|
),
|
|
2040
|
+
|
|
1021
2041
|
),
|
|
2042
|
+
|
|
1022
2043
|
React.createElement(
|
|
2044
|
+
|
|
1023
2045
|
'div',
|
|
2046
|
+
|
|
1024
2047
|
{ style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 } },
|
|
2048
|
+
|
|
1025
2049
|
React.createElement('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--dsw-alias-label-primary)' } }, t('milestones') || 'Plan of work:'),
|
|
2050
|
+
|
|
1026
2051
|
milestones.length === 0
|
|
2052
|
+
|
|
1027
2053
|
? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12, padding: '8px 0' } }, t('noMilestones') || 'Agent is preparing the plan of work...')
|
|
2054
|
+
|
|
1028
2055
|
: milestones.map((m, i) =>
|
|
2056
|
+
|
|
1029
2057
|
React.createElement(
|
|
2058
|
+
|
|
1030
2059
|
'div',
|
|
2060
|
+
|
|
1031
2061
|
{ key: m.id || i, className: 'dsh-goal-milestone-item' },
|
|
2062
|
+
|
|
1032
2063
|
React.createElement('span', { style: { fontSize: 15 } }, m.status === 'completed' ? '✅' : m.status === 'in_progress' ? '🔄' : '⏳'),
|
|
2064
|
+
|
|
1033
2065
|
React.createElement(
|
|
2066
|
+
|
|
1034
2067
|
'div',
|
|
2068
|
+
|
|
1035
2069
|
{ style: { flex: 1, minWidth: 0 } },
|
|
2070
|
+
|
|
1036
2071
|
React.createElement(
|
|
2072
|
+
|
|
1037
2073
|
'div',
|
|
2074
|
+
|
|
1038
2075
|
{
|
|
2076
|
+
|
|
1039
2077
|
style: {
|
|
2078
|
+
|
|
1040
2079
|
textDecoration: m.status === 'completed' ? 'line-through' : 'none',
|
|
2080
|
+
|
|
1041
2081
|
color: m.status === 'completed' ? 'var(--dsw-alias-label-tertiary)' : 'inherit',
|
|
2082
|
+
|
|
1042
2083
|
fontWeight: m.status === 'in_progress' ? 600 : 400,
|
|
2084
|
+
|
|
1043
2085
|
},
|
|
2086
|
+
|
|
1044
2087
|
},
|
|
2088
|
+
|
|
1045
2089
|
`${i + 1}. ${m.title}`,
|
|
2090
|
+
|
|
1046
2091
|
),
|
|
2092
|
+
|
|
1047
2093
|
m.notes ? React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary)', marginTop: 2 } }, m.notes) : null,
|
|
2094
|
+
|
|
1048
2095
|
),
|
|
2096
|
+
|
|
1049
2097
|
),
|
|
2098
|
+
|
|
1050
2099
|
),
|
|
2100
|
+
|
|
1051
2101
|
),
|
|
2102
|
+
|
|
1052
2103
|
),
|
|
2104
|
+
|
|
1053
2105
|
React.createElement(
|
|
2106
|
+
|
|
1054
2107
|
'div',
|
|
2108
|
+
|
|
1055
2109
|
{ className: 'dsh-goal-modal-foot' },
|
|
2110
|
+
|
|
1056
2111
|
isCompleted
|
|
2112
|
+
|
|
1057
2113
|
? React.createElement(
|
|
2114
|
+
|
|
1058
2115
|
React.Fragment,
|
|
2116
|
+
|
|
1059
2117
|
null,
|
|
2118
|
+
|
|
1060
2119
|
React.createElement(
|
|
2120
|
+
|
|
1061
2121
|
'button',
|
|
2122
|
+
|
|
1062
2123
|
{
|
|
2124
|
+
|
|
1063
2125
|
className: `dsh-goal-btn dsh-goal-btn-copy${copied ? ' copied' : ''}`,
|
|
2126
|
+
|
|
1064
2127
|
onClick: handleCopyReport,
|
|
2128
|
+
|
|
1065
2129
|
title: t('copyReport') || 'Copy Report in Markdown',
|
|
2130
|
+
|
|
1066
2131
|
},
|
|
2132
|
+
|
|
1067
2133
|
copied ? (t('reportCopied') || '✅ Copied!') : (t('copyReport') || '📋 Copy Report in Markdown'),
|
|
2134
|
+
|
|
1068
2135
|
),
|
|
2136
|
+
|
|
1069
2137
|
React.createElement(
|
|
2138
|
+
|
|
1070
2139
|
'button',
|
|
2140
|
+
|
|
1071
2141
|
{
|
|
2142
|
+
|
|
1072
2143
|
className: `dsh-goal-btn dsh-goal-btn-copy${prCopied ? ' copied' : ''}`,
|
|
2144
|
+
|
|
1073
2145
|
onClick: () => {
|
|
2146
|
+
|
|
1074
2147
|
const prMd = generateGitHubPRComment(state);
|
|
2148
|
+
|
|
1075
2149
|
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
2150
|
+
|
|
1076
2151
|
navigator.clipboard.writeText(prMd).then(() => {
|
|
2152
|
+
|
|
1077
2153
|
setPrCopied(true);
|
|
2154
|
+
|
|
1078
2155
|
setTimeout(() => setPrCopied(false), 2500);
|
|
2156
|
+
|
|
1079
2157
|
});
|
|
2158
|
+
|
|
1080
2159
|
}
|
|
2160
|
+
|
|
1081
2161
|
},
|
|
2162
|
+
|
|
1082
2163
|
title: 'Copy Report for GitHub / Gitea PR Comment',
|
|
2164
|
+
|
|
1083
2165
|
},
|
|
2166
|
+
|
|
1084
2167
|
prCopied ? (t('prCommentCopied') || '✅ PR Report Copied!') : (t('copyPRComment') || '🐙 Copy for GitHub PR'),
|
|
2168
|
+
|
|
1085
2169
|
),
|
|
2170
|
+
|
|
1086
2171
|
)
|
|
2172
|
+
|
|
1087
2173
|
: null,
|
|
2174
|
+
|
|
1088
2175
|
isCompleted
|
|
2176
|
+
|
|
1089
2177
|
? React.createElement(
|
|
2178
|
+
|
|
1090
2179
|
'button',
|
|
2180
|
+
|
|
1091
2181
|
{
|
|
2182
|
+
|
|
1092
2183
|
className: 'dsh-goal-btn dsh-goal-btn-danger',
|
|
2184
|
+
|
|
1093
2185
|
onClick: () => {
|
|
2186
|
+
|
|
1094
2187
|
onAction('clear');
|
|
2188
|
+
|
|
1095
2189
|
onClose();
|
|
2190
|
+
|
|
1096
2191
|
},
|
|
2192
|
+
|
|
1097
2193
|
},
|
|
2194
|
+
|
|
1098
2195
|
t('closeBanner') || 'Close goal banner',
|
|
2196
|
+
|
|
1099
2197
|
)
|
|
2198
|
+
|
|
1100
2199
|
: null,
|
|
2200
|
+
|
|
1101
2201
|
React.createElement(
|
|
2202
|
+
|
|
1102
2203
|
'button',
|
|
2204
|
+
|
|
1103
2205
|
{
|
|
2206
|
+
|
|
1104
2207
|
className: 'dsh-goal-btn dsh-goal-btn-primary',
|
|
2208
|
+
|
|
1105
2209
|
onClick: onClose,
|
|
2210
|
+
|
|
1106
2211
|
},
|
|
2212
|
+
|
|
1107
2213
|
t('close') || 'Close',
|
|
2214
|
+
|
|
1108
2215
|
),
|
|
2216
|
+
|
|
1109
2217
|
),
|
|
2218
|
+
|
|
1110
2219
|
),
|
|
2220
|
+
|
|
1111
2221
|
);
|
|
2222
|
+
|
|
1112
2223
|
}
|
|
1113
2224
|
|
|
2225
|
+
|
|
2226
|
+
|
|
1114
2227
|
// --- МОДАЛЬНОЕ ОКНО БЫСТРОГО ЗАПУСКА ЦЕЛИ (QUICK LAUNCH MODAL) ---
|
|
2228
|
+
|
|
1115
2229
|
function QuickLaunchModal({ onClose, onStart, t }) {
|
|
2230
|
+
|
|
1116
2231
|
const [title, setTitle] = useState('');
|
|
2232
|
+
|
|
1117
2233
|
const inputRef = useRef(null);
|
|
1118
2234
|
|
|
2235
|
+
|
|
2236
|
+
|
|
1119
2237
|
useEffect(() => {
|
|
2238
|
+
|
|
1120
2239
|
if (inputRef.current) inputRef.current.focus();
|
|
2240
|
+
|
|
1121
2241
|
}, []);
|
|
1122
2242
|
|
|
2243
|
+
|
|
2244
|
+
|
|
1123
2245
|
const handleSubmit = (e) => {
|
|
2246
|
+
|
|
1124
2247
|
if (e) e.preventDefault();
|
|
2248
|
+
|
|
1125
2249
|
const trimmed = title.trim();
|
|
2250
|
+
|
|
1126
2251
|
if (trimmed) {
|
|
2252
|
+
|
|
1127
2253
|
onStart(trimmed);
|
|
2254
|
+
|
|
1128
2255
|
}
|
|
2256
|
+
|
|
1129
2257
|
};
|
|
1130
2258
|
|
|
2259
|
+
|
|
2260
|
+
|
|
1131
2261
|
return React.createElement(
|
|
2262
|
+
|
|
1132
2263
|
'div',
|
|
2264
|
+
|
|
1133
2265
|
{ className: 'dsh-goal-modal-overlay', onClick: onClose },
|
|
2266
|
+
|
|
1134
2267
|
React.createElement(
|
|
2268
|
+
|
|
1135
2269
|
'div',
|
|
2270
|
+
|
|
1136
2271
|
{
|
|
2272
|
+
|
|
1137
2273
|
className: 'dsh-goal-modal',
|
|
2274
|
+
|
|
1138
2275
|
style: { maxWidth: 480 },
|
|
2276
|
+
|
|
1139
2277
|
onClick: (e) => e.stopPropagation(),
|
|
2278
|
+
|
|
1140
2279
|
},
|
|
2280
|
+
|
|
1141
2281
|
React.createElement(
|
|
2282
|
+
|
|
1142
2283
|
'div',
|
|
2284
|
+
|
|
1143
2285
|
{ className: 'dsh-goal-modal-head' },
|
|
2286
|
+
|
|
1144
2287
|
React.createElement(
|
|
2288
|
+
|
|
1145
2289
|
'div',
|
|
2290
|
+
|
|
1146
2291
|
{ className: 'dsh-goal-modal-title' },
|
|
2292
|
+
|
|
1147
2293
|
React.createElement(IconTarget, { size: 18 }),
|
|
2294
|
+
|
|
1148
2295
|
t('quickLaunchTitle') || 'Quick Launch Goal',
|
|
2296
|
+
|
|
1149
2297
|
),
|
|
2298
|
+
|
|
1150
2299
|
React.createElement(
|
|
2300
|
+
|
|
1151
2301
|
'button',
|
|
2302
|
+
|
|
1152
2303
|
{ className: 'dsh-goal-btn icon-only close', onClick: onClose, title: t('close') || 'Close' },
|
|
2304
|
+
|
|
1153
2305
|
'✕',
|
|
2306
|
+
|
|
1154
2307
|
),
|
|
2308
|
+
|
|
1155
2309
|
),
|
|
2310
|
+
|
|
1156
2311
|
React.createElement(
|
|
2312
|
+
|
|
1157
2313
|
'form',
|
|
2314
|
+
|
|
1158
2315
|
{ onSubmit: handleSubmit },
|
|
2316
|
+
|
|
1159
2317
|
React.createElement(
|
|
2318
|
+
|
|
1160
2319
|
'div',
|
|
2320
|
+
|
|
1161
2321
|
{ className: 'dsh-goal-modal-body' },
|
|
2322
|
+
|
|
1162
2323
|
React.createElement(
|
|
2324
|
+
|
|
1163
2325
|
'div',
|
|
2326
|
+
|
|
1164
2327
|
{ style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary)', marginBottom: 8 } },
|
|
2328
|
+
|
|
1165
2329
|
t('quickLaunchDesc') || 'Define the objective for the agent in autonomous mode:',
|
|
2330
|
+
|
|
1166
2331
|
),
|
|
2332
|
+
|
|
1167
2333
|
React.createElement(
|
|
2334
|
+
|
|
1168
2335
|
'div',
|
|
2336
|
+
|
|
1169
2337
|
{ className: 'dsh-goal-template-chips' },
|
|
2338
|
+
|
|
1170
2339
|
QUICK_LAUNCH_TEMPLATES.map((tmpl) => {
|
|
2340
|
+
|
|
1171
2341
|
const lang = t('localeCode') || 'en';
|
|
2342
|
+
|
|
1172
2343
|
const label = tmpl.label[lang] || tmpl.label.en;
|
|
2344
|
+
|
|
1173
2345
|
const prefix = tmpl.prefix[lang] || tmpl.prefix.en;
|
|
2346
|
+
|
|
1174
2347
|
return React.createElement(
|
|
2348
|
+
|
|
1175
2349
|
'button',
|
|
2350
|
+
|
|
1176
2351
|
{
|
|
2352
|
+
|
|
1177
2353
|
key: tmpl.id,
|
|
2354
|
+
|
|
1178
2355
|
type: 'button',
|
|
2356
|
+
|
|
1179
2357
|
className: 'dsh-goal-template-chip',
|
|
2358
|
+
|
|
1180
2359
|
title: prefix,
|
|
2360
|
+
|
|
1181
2361
|
onClick: () => {
|
|
2362
|
+
|
|
1182
2363
|
setTitle(prefix);
|
|
2364
|
+
|
|
1183
2365
|
if (inputRef.current) inputRef.current.focus();
|
|
2366
|
+
|
|
1184
2367
|
},
|
|
2368
|
+
|
|
1185
2369
|
},
|
|
2370
|
+
|
|
1186
2371
|
`${tmpl.icon} ${label}`,
|
|
2372
|
+
|
|
1187
2373
|
);
|
|
2374
|
+
|
|
1188
2375
|
}),
|
|
2376
|
+
|
|
1189
2377
|
),
|
|
2378
|
+
|
|
1190
2379
|
React.createElement('input', {
|
|
2380
|
+
|
|
1191
2381
|
ref: inputRef,
|
|
2382
|
+
|
|
1192
2383
|
type: 'text',
|
|
2384
|
+
|
|
1193
2385
|
className: 'dsh-goal-card-input',
|
|
2386
|
+
|
|
1194
2387
|
style: { width: '100%', boxSizing: 'border-box', fontSize: 13, padding: '8px 10px' },
|
|
2388
|
+
|
|
1195
2389
|
placeholder: t('quickLaunchPlaceholder') || 'e.g. Implement report export and cover with unit tests...',
|
|
2390
|
+
|
|
1196
2391
|
value: title,
|
|
2392
|
+
|
|
1197
2393
|
onChange: (e) => setTitle(e.target.value),
|
|
2394
|
+
|
|
1198
2395
|
onKeyDown: (e) => {
|
|
2396
|
+
|
|
1199
2397
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
2398
|
+
|
|
1200
2399
|
e.preventDefault();
|
|
2400
|
+
|
|
1201
2401
|
handleSubmit();
|
|
2402
|
+
|
|
1202
2403
|
}
|
|
2404
|
+
|
|
1203
2405
|
},
|
|
2406
|
+
|
|
1204
2407
|
}),
|
|
2408
|
+
|
|
1205
2409
|
),
|
|
2410
|
+
|
|
1206
2411
|
React.createElement(
|
|
2412
|
+
|
|
1207
2413
|
'div',
|
|
2414
|
+
|
|
1208
2415
|
{ className: 'dsh-goal-modal-foot' },
|
|
2416
|
+
|
|
1209
2417
|
React.createElement(
|
|
2418
|
+
|
|
1210
2419
|
'button',
|
|
2420
|
+
|
|
1211
2421
|
{ type: 'button', className: 'dsh-goal-btn', onClick: onClose },
|
|
2422
|
+
|
|
1212
2423
|
t('close') || 'Cancel',
|
|
2424
|
+
|
|
1213
2425
|
),
|
|
2426
|
+
|
|
1214
2427
|
React.createElement(
|
|
2428
|
+
|
|
1215
2429
|
'button',
|
|
2430
|
+
|
|
1216
2431
|
{
|
|
2432
|
+
|
|
1217
2433
|
type: 'submit',
|
|
2434
|
+
|
|
1218
2435
|
className: 'dsh-goal-btn dsh-goal-btn-primary',
|
|
2436
|
+
|
|
1219
2437
|
disabled: !title.trim(),
|
|
2438
|
+
|
|
1220
2439
|
},
|
|
2440
|
+
|
|
1221
2441
|
t('startGoal') || 'Start Goal',
|
|
2442
|
+
|
|
1222
2443
|
),
|
|
2444
|
+
|
|
1223
2445
|
),
|
|
2446
|
+
|
|
1224
2447
|
),
|
|
2448
|
+
|
|
1225
2449
|
),
|
|
2450
|
+
|
|
1226
2451
|
);
|
|
2452
|
+
|
|
1227
2453
|
}
|
|
1228
2454
|
|
|
2455
|
+
|
|
2456
|
+
|
|
1229
2457
|
function sessionIdOf(ctx, props) {
|
|
2458
|
+
|
|
1230
2459
|
try {
|
|
2460
|
+
|
|
1231
2461
|
if (props?.session?.id) return String(props.session.id);
|
|
2462
|
+
|
|
1232
2463
|
if (props?.session?.sessionId) return String(props.session.sessionId);
|
|
2464
|
+
|
|
1233
2465
|
if (props?.input?.sessionId) return String(props.input.sessionId);
|
|
2466
|
+
|
|
1234
2467
|
if (ctx?.scope?.session) return String(ctx.scope.session.id || ctx.scope.session.header?.id || '');
|
|
2468
|
+
|
|
1235
2469
|
if (typeof ctx?.scope?.id === 'string') return ctx.scope.id;
|
|
2470
|
+
|
|
1236
2471
|
if (ctx?.session) return String(ctx.session.id || ctx.session.header?.id || '');
|
|
2472
|
+
|
|
1237
2473
|
if (typeof window !== 'undefined' && window.location) {
|
|
2474
|
+
|
|
1238
2475
|
const params = new URLSearchParams(window.location.search);
|
|
2476
|
+
|
|
1239
2477
|
const fromUrl = params.get('session') || params.get('sessionId');
|
|
2478
|
+
|
|
1240
2479
|
if (fromUrl) return fromUrl;
|
|
2480
|
+
|
|
1241
2481
|
}
|
|
2482
|
+
|
|
1242
2483
|
} catch (_) {}
|
|
2484
|
+
|
|
1243
2485
|
return 'default';
|
|
2486
|
+
|
|
1244
2487
|
}
|
|
1245
2488
|
|
|
2489
|
+
|
|
2490
|
+
|
|
1246
2491
|
// --- ПАНЕЛЬ ЦЕЛИ НАД ПОЛЕМ ВВОДА (COMPOSER DOCK WIDGET) ---
|
|
2492
|
+
|
|
1247
2493
|
function GoalTopBanner(props) {
|
|
2494
|
+
|
|
1248
2495
|
const { ctx } = props || {};
|
|
2496
|
+
|
|
1249
2497
|
const sid = sessionIdOf(ctx, props);
|
|
2498
|
+
|
|
1250
2499
|
const [state, setState] = useState(null);
|
|
2500
|
+
|
|
1251
2501
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
2502
|
+
|
|
1252
2503
|
const [isQuickLaunchOpen, setIsQuickLaunchOpen] = useState(false);
|
|
2504
|
+
|
|
1253
2505
|
const [now, setNow] = useState(() => Date.now());
|
|
2506
|
+
|
|
1254
2507
|
const stateRef = useRef(null);
|
|
2508
|
+
|
|
1255
2509
|
stateRef.current = state;
|
|
2510
|
+
|
|
1256
2511
|
const lastStateRef = useRef(null);
|
|
1257
2512
|
|
|
2513
|
+
|
|
2514
|
+
|
|
1258
2515
|
const resolveLocale = () => {
|
|
2516
|
+
|
|
1259
2517
|
let active = 'en';
|
|
2518
|
+
|
|
1260
2519
|
if (ctx?.locale?.getSnapshot) {
|
|
2520
|
+
|
|
1261
2521
|
const snapLoc = ctx.locale.getSnapshot();
|
|
2522
|
+
|
|
1262
2523
|
if (snapLoc?.active) {
|
|
2524
|
+
|
|
1263
2525
|
const code = snapLoc.active.toLowerCase();
|
|
2526
|
+
|
|
1264
2527
|
if (code.startsWith('zh')) active = 'zh';
|
|
2528
|
+
|
|
1265
2529
|
else if (code.startsWith('ru')) active = 'ru';
|
|
2530
|
+
|
|
1266
2531
|
else active = 'en';
|
|
2532
|
+
|
|
1267
2533
|
}
|
|
2534
|
+
|
|
1268
2535
|
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
2536
|
+
|
|
1269
2537
|
const code = navigator.language.toLowerCase();
|
|
2538
|
+
|
|
1270
2539
|
if (code.startsWith('zh')) active = 'zh';
|
|
2540
|
+
|
|
1271
2541
|
else if (code.startsWith('ru')) active = 'ru';
|
|
2542
|
+
|
|
1272
2543
|
else active = 'en';
|
|
2544
|
+
|
|
1273
2545
|
}
|
|
2546
|
+
|
|
1274
2547
|
if (state?.lang) {
|
|
2548
|
+
|
|
1275
2549
|
if (state.lang === 'zh') active = 'zh';
|
|
2550
|
+
|
|
1276
2551
|
else if (state.lang === 'ru') active = 'ru';
|
|
2552
|
+
|
|
1277
2553
|
else if (state.lang === 'en') active = 'en';
|
|
2554
|
+
|
|
1278
2555
|
}
|
|
2556
|
+
|
|
1279
2557
|
return active;
|
|
2558
|
+
|
|
1280
2559
|
};
|
|
1281
2560
|
|
|
2561
|
+
|
|
2562
|
+
|
|
1282
2563
|
const t = (key) => {
|
|
2564
|
+
|
|
1283
2565
|
const active = resolveLocale();
|
|
2566
|
+
|
|
1284
2567
|
return LOCALES[active]?.[key] || LOCALES.en?.[key] || LOCALES.ru?.[key] || key;
|
|
2568
|
+
|
|
1285
2569
|
};
|
|
1286
2570
|
|
|
2571
|
+
|
|
2572
|
+
|
|
1287
2573
|
useEffect(() => {
|
|
2574
|
+
|
|
1288
2575
|
injectStylesOnce();
|
|
2576
|
+
|
|
1289
2577
|
}, []);
|
|
1290
2578
|
|
|
2579
|
+
|
|
2580
|
+
|
|
1291
2581
|
// Локальный секундный интервал для плавного и бесперебойного тикания таймера
|
|
2582
|
+
|
|
1292
2583
|
useEffect(() => {
|
|
2584
|
+
|
|
1293
2585
|
const ticker = setInterval(() => {
|
|
2586
|
+
|
|
1294
2587
|
setNow(Date.now());
|
|
2588
|
+
|
|
1295
2589
|
}, 1000);
|
|
2590
|
+
|
|
1296
2591
|
return () => clearInterval(ticker);
|
|
2592
|
+
|
|
1297
2593
|
}, []);
|
|
1298
2594
|
|
|
2595
|
+
|
|
2596
|
+
|
|
1299
2597
|
const fetchState = useCallback(async () => {
|
|
2598
|
+
|
|
1300
2599
|
try {
|
|
2600
|
+
|
|
1301
2601
|
const query = sid && sid !== 'default' ? `?sessionId=${encodeURIComponent(sid)}` : '';
|
|
2602
|
+
|
|
1302
2603
|
const res = await fetch(`/dsh-goal/state${query}`, {
|
|
2604
|
+
|
|
1303
2605
|
headers: { 'x-dsh-session-id': sid },
|
|
2606
|
+
|
|
1304
2607
|
});
|
|
2608
|
+
|
|
1305
2609
|
if (res.ok) {
|
|
2610
|
+
|
|
1306
2611
|
const data = await res.json();
|
|
2612
|
+
|
|
1307
2613
|
updateStateWithAudio(data);
|
|
2614
|
+
|
|
1308
2615
|
return data;
|
|
2616
|
+
|
|
1309
2617
|
}
|
|
2618
|
+
|
|
1310
2619
|
} catch (_) {}
|
|
2620
|
+
|
|
1311
2621
|
}, [sid]);
|
|
1312
2622
|
|
|
2623
|
+
|
|
2624
|
+
|
|
1313
2625
|
// Звуковое оповещение при переходе в COMPLETED или FAILED
|
|
2626
|
+
|
|
1314
2627
|
const updateStateWithAudio = useCallback((nextState) => {
|
|
2628
|
+
|
|
1315
2629
|
if (nextState && lastStateRef.current) {
|
|
2630
|
+
|
|
1316
2631
|
const prev = lastStateRef.current;
|
|
2632
|
+
|
|
1317
2633
|
if (prev.state !== nextState.state && nextState.enableSound !== false) {
|
|
2634
|
+
|
|
1318
2635
|
if (nextState.state === 'COMPLETED') {
|
|
2636
|
+
|
|
1319
2637
|
playGoalChime(true);
|
|
2638
|
+
|
|
1320
2639
|
} else if (nextState.state === 'FAILED') {
|
|
2640
|
+
|
|
1321
2641
|
playGoalChime(false);
|
|
2642
|
+
|
|
1322
2643
|
}
|
|
1323
2644
|
|
|
2645
|
+
|
|
2646
|
+
|
|
1324
2647
|
// HTML5 Browser Desktop Notification
|
|
2648
|
+
|
|
1325
2649
|
if (typeof window !== 'undefined' && 'Notification' in window) {
|
|
2650
|
+
|
|
1326
2651
|
if (nextState.enableBrowserNotifications !== false && Notification.permission === 'granted') {
|
|
2652
|
+
|
|
1327
2653
|
try {
|
|
2654
|
+
|
|
1328
2655
|
const isOk = nextState.state === 'COMPLETED';
|
|
2656
|
+
|
|
1329
2657
|
new Notification(
|
|
2658
|
+
|
|
1330
2659
|
isOk ? '🎯 Goal Completed' : '⚠️ Goal Failed',
|
|
2660
|
+
|
|
1331
2661
|
{
|
|
2662
|
+
|
|
1332
2663
|
body: `${nextState.title} (${nextState.formattedElapsed || ''})`,
|
|
2664
|
+
|
|
1333
2665
|
icon: '/favicon.ico',
|
|
2666
|
+
|
|
1334
2667
|
}
|
|
2668
|
+
|
|
1335
2669
|
);
|
|
2670
|
+
|
|
1336
2671
|
} catch (_) {}
|
|
2672
|
+
|
|
1337
2673
|
}
|
|
2674
|
+
|
|
1338
2675
|
}
|
|
2676
|
+
|
|
1339
2677
|
}
|
|
2678
|
+
|
|
1340
2679
|
}
|
|
2680
|
+
|
|
1341
2681
|
lastStateRef.current = nextState;
|
|
2682
|
+
|
|
1342
2683
|
setState(nextState);
|
|
2684
|
+
|
|
1343
2685
|
}, []);
|
|
1344
2686
|
|
|
2687
|
+
|
|
2688
|
+
|
|
1345
2689
|
// Realtime Server-Sent Events (SSE) с автоматическим адаптивным fallback-поллингом
|
|
2690
|
+
|
|
1346
2691
|
useEffect(() => {
|
|
2692
|
+
|
|
1347
2693
|
let es = null;
|
|
2694
|
+
|
|
1348
2695
|
let timer = null;
|
|
2696
|
+
|
|
1349
2697
|
let reconnectTimer = null;
|
|
2698
|
+
|
|
1350
2699
|
let isDisposed = false;
|
|
2700
|
+
|
|
1351
2701
|
let sseActive = false;
|
|
2702
|
+
|
|
1352
2703
|
let retryCount = 0;
|
|
1353
2704
|
|
|
2705
|
+
|
|
2706
|
+
|
|
1354
2707
|
const scheduleNextPoll = () => {
|
|
2708
|
+
|
|
1355
2709
|
if (isDisposed || sseActive) return;
|
|
2710
|
+
|
|
1356
2711
|
const currentState = stateRef.current;
|
|
2712
|
+
|
|
1357
2713
|
const isDocHidden = typeof document !== 'undefined' && document.hidden;
|
|
2714
|
+
|
|
1358
2715
|
const isRunning = currentState?.hasActiveGoal && currentState?.state === 'RUNNING';
|
|
1359
2716
|
|
|
2717
|
+
|
|
2718
|
+
|
|
1360
2719
|
let delay = 1500;
|
|
2720
|
+
|
|
1361
2721
|
if (isDocHidden) {
|
|
2722
|
+
|
|
1362
2723
|
delay = 10000;
|
|
2724
|
+
|
|
1363
2725
|
} else if (!isRunning) {
|
|
2726
|
+
|
|
1364
2727
|
delay = 8000;
|
|
2728
|
+
|
|
1365
2729
|
}
|
|
1366
2730
|
|
|
2731
|
+
|
|
2732
|
+
|
|
1367
2733
|
timer = setTimeout(async () => {
|
|
2734
|
+
|
|
1368
2735
|
await fetchState();
|
|
2736
|
+
|
|
1369
2737
|
scheduleNextPoll();
|
|
2738
|
+
|
|
1370
2739
|
}, delay);
|
|
2740
|
+
|
|
1371
2741
|
};
|
|
1372
2742
|
|
|
2743
|
+
|
|
2744
|
+
|
|
1373
2745
|
const connectSSE = () => {
|
|
2746
|
+
|
|
1374
2747
|
if (isDisposed) return;
|
|
2748
|
+
|
|
1375
2749
|
if (typeof window === 'undefined' || typeof window.EventSource === 'undefined') {
|
|
2750
|
+
|
|
1376
2751
|
scheduleNextPoll();
|
|
2752
|
+
|
|
1377
2753
|
return;
|
|
2754
|
+
|
|
1378
2755
|
}
|
|
1379
2756
|
|
|
2757
|
+
|
|
2758
|
+
|
|
1380
2759
|
try {
|
|
2760
|
+
|
|
1381
2761
|
if (es) {
|
|
2762
|
+
|
|
1382
2763
|
es.close();
|
|
2764
|
+
|
|
1383
2765
|
es = null;
|
|
2766
|
+
|
|
1384
2767
|
}
|
|
1385
2768
|
|
|
2769
|
+
|
|
2770
|
+
|
|
1386
2771
|
const query = sid && sid !== 'default' ? `?sessionId=${encodeURIComponent(sid)}` : '';
|
|
2772
|
+
|
|
1387
2773
|
es = new window.EventSource(`/dsh-goal/events${query}`);
|
|
1388
2774
|
|
|
2775
|
+
|
|
2776
|
+
|
|
1389
2777
|
es.onopen = () => {
|
|
2778
|
+
|
|
1390
2779
|
sseActive = true;
|
|
2780
|
+
|
|
1391
2781
|
retryCount = 0;
|
|
2782
|
+
|
|
1392
2783
|
if (timer) {
|
|
2784
|
+
|
|
1393
2785
|
clearTimeout(timer);
|
|
2786
|
+
|
|
1394
2787
|
timer = null;
|
|
2788
|
+
|
|
1395
2789
|
}
|
|
2790
|
+
|
|
1396
2791
|
if (reconnectTimer) {
|
|
2792
|
+
|
|
1397
2793
|
clearTimeout(reconnectTimer);
|
|
2794
|
+
|
|
1398
2795
|
reconnectTimer = null;
|
|
2796
|
+
|
|
1399
2797
|
}
|
|
2798
|
+
|
|
1400
2799
|
};
|
|
1401
2800
|
|
|
2801
|
+
|
|
2802
|
+
|
|
1402
2803
|
es.onmessage = (event) => {
|
|
2804
|
+
|
|
1403
2805
|
if (isDisposed) return;
|
|
2806
|
+
|
|
1404
2807
|
try {
|
|
2808
|
+
|
|
1405
2809
|
const data = JSON.parse(event.data);
|
|
2810
|
+
|
|
1406
2811
|
if (data && typeof data === 'object') {
|
|
2812
|
+
|
|
1407
2813
|
updateStateWithAudio(data);
|
|
2814
|
+
|
|
1408
2815
|
}
|
|
2816
|
+
|
|
1409
2817
|
} catch (_) {}
|
|
2818
|
+
|
|
1410
2819
|
};
|
|
1411
2820
|
|
|
2821
|
+
|
|
2822
|
+
|
|
1412
2823
|
es.onerror = () => {
|
|
2824
|
+
|
|
1413
2825
|
sseActive = false;
|
|
2826
|
+
|
|
1414
2827
|
if (es) {
|
|
2828
|
+
|
|
1415
2829
|
es.close();
|
|
2830
|
+
|
|
1416
2831
|
es = null;
|
|
2832
|
+
|
|
1417
2833
|
}
|
|
2834
|
+
|
|
1418
2835
|
if (!isDisposed) {
|
|
2836
|
+
|
|
1419
2837
|
// Запускаем немедленный опрос для непрерывности работы UI
|
|
2838
|
+
|
|
1420
2839
|
if (!timer) scheduleNextPoll();
|
|
1421
2840
|
|
|
2841
|
+
|
|
2842
|
+
|
|
1422
2843
|
// Экспоненциальный backoff для повторного подключения SSE (от 2s до 30s) с джиттером
|
|
2844
|
+
|
|
1423
2845
|
retryCount = Math.min(retryCount + 1, 5);
|
|
2846
|
+
|
|
1424
2847
|
const backoffBase = Math.min(30000, 2000 * Math.pow(1.8, retryCount - 1));
|
|
2848
|
+
|
|
1425
2849
|
const jitter = Math.random() * 1000;
|
|
2850
|
+
|
|
1426
2851
|
const reconnectDelay = Math.round(backoffBase + jitter);
|
|
1427
2852
|
|
|
2853
|
+
|
|
2854
|
+
|
|
1428
2855
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
2856
|
+
|
|
1429
2857
|
reconnectTimer = setTimeout(() => {
|
|
2858
|
+
|
|
1430
2859
|
reconnectTimer = null;
|
|
2860
|
+
|
|
1431
2861
|
connectSSE();
|
|
2862
|
+
|
|
1432
2863
|
}, reconnectDelay);
|
|
2864
|
+
|
|
1433
2865
|
}
|
|
2866
|
+
|
|
1434
2867
|
};
|
|
2868
|
+
|
|
1435
2869
|
} catch (_) {
|
|
2870
|
+
|
|
1436
2871
|
sseActive = false;
|
|
2872
|
+
|
|
1437
2873
|
scheduleNextPoll();
|
|
2874
|
+
|
|
1438
2875
|
}
|
|
2876
|
+
|
|
1439
2877
|
};
|
|
1440
2878
|
|
|
2879
|
+
|
|
2880
|
+
|
|
1441
2881
|
connectSSE();
|
|
1442
2882
|
|
|
2883
|
+
|
|
2884
|
+
|
|
1443
2885
|
// Первоначальная загрузка состояния
|
|
2886
|
+
|
|
1444
2887
|
fetchState().then(() => {
|
|
2888
|
+
|
|
1445
2889
|
if (!sseActive) scheduleNextPoll();
|
|
2890
|
+
|
|
1446
2891
|
});
|
|
1447
2892
|
|
|
2893
|
+
|
|
2894
|
+
|
|
1448
2895
|
const onVisibilityChange = () => {
|
|
2896
|
+
|
|
1449
2897
|
if (typeof document !== 'undefined' && !document.hidden) {
|
|
2898
|
+
|
|
1450
2899
|
if (!sseActive) {
|
|
2900
|
+
|
|
1451
2901
|
if (timer) clearTimeout(timer);
|
|
2902
|
+
|
|
1452
2903
|
fetchState().then(() => {
|
|
2904
|
+
|
|
1453
2905
|
scheduleNextPoll();
|
|
2906
|
+
|
|
1454
2907
|
});
|
|
2908
|
+
|
|
1455
2909
|
// При возвращении на вкладку можно попытаться сразу переподключить SSE
|
|
2910
|
+
|
|
1456
2911
|
if (!reconnectTimer && !sseActive) {
|
|
2912
|
+
|
|
1457
2913
|
connectSSE();
|
|
2914
|
+
|
|
1458
2915
|
}
|
|
2916
|
+
|
|
1459
2917
|
}
|
|
2918
|
+
|
|
1460
2919
|
}
|
|
2920
|
+
|
|
1461
2921
|
};
|
|
1462
2922
|
|
|
2923
|
+
|
|
2924
|
+
|
|
1463
2925
|
if (typeof document !== 'undefined' && document.addEventListener) {
|
|
2926
|
+
|
|
1464
2927
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
2928
|
+
|
|
1465
2929
|
}
|
|
1466
2930
|
|
|
2931
|
+
|
|
2932
|
+
|
|
1467
2933
|
return () => {
|
|
2934
|
+
|
|
1468
2935
|
isDisposed = true;
|
|
2936
|
+
|
|
1469
2937
|
if (es) {
|
|
2938
|
+
|
|
1470
2939
|
es.close();
|
|
2940
|
+
|
|
1471
2941
|
es = null;
|
|
2942
|
+
|
|
1472
2943
|
}
|
|
2944
|
+
|
|
1473
2945
|
if (timer) clearTimeout(timer);
|
|
2946
|
+
|
|
1474
2947
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
2948
|
+
|
|
1475
2949
|
if (typeof document !== 'undefined' && document.removeEventListener) {
|
|
2950
|
+
|
|
1476
2951
|
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
2952
|
+
|
|
1477
2953
|
}
|
|
2954
|
+
|
|
1478
2955
|
};
|
|
2956
|
+
|
|
1479
2957
|
}, [sid, fetchState, updateStateWithAudio]);
|
|
1480
2958
|
|
|
2959
|
+
|
|
2960
|
+
|
|
1481
2961
|
const handleAction = async (action, extra = {}) => {
|
|
2962
|
+
|
|
1482
2963
|
try {
|
|
2964
|
+
|
|
1483
2965
|
const res = await fetch('/dsh-goal/action', {
|
|
2966
|
+
|
|
1484
2967
|
method: 'POST',
|
|
2968
|
+
|
|
1485
2969
|
headers: {
|
|
2970
|
+
|
|
1486
2971
|
'Content-Type': 'application/json',
|
|
2972
|
+
|
|
1487
2973
|
'x-dsh-session-id': sid,
|
|
2974
|
+
|
|
1488
2975
|
},
|
|
2976
|
+
|
|
1489
2977
|
body: JSON.stringify({ action, sessionId: sid, ...extra }),
|
|
2978
|
+
|
|
1490
2979
|
});
|
|
2980
|
+
|
|
1491
2981
|
if (res.ok) {
|
|
2982
|
+
|
|
1492
2983
|
const data = await res.json();
|
|
2984
|
+
|
|
1493
2985
|
if (data.state) updateStateWithAudio(data.state);
|
|
2986
|
+
|
|
1494
2987
|
}
|
|
2988
|
+
|
|
1495
2989
|
} catch (_) {}
|
|
2990
|
+
|
|
1496
2991
|
};
|
|
1497
2992
|
|
|
2993
|
+
|
|
2994
|
+
|
|
1498
2995
|
const showQuickLaunch = state ? (state.showQuickLaunchButton !== false) : true;
|
|
1499
2996
|
|
|
2997
|
+
|
|
2998
|
+
|
|
1500
2999
|
if (!state || !state.hasActiveGoal) {
|
|
3000
|
+
|
|
1501
3001
|
if (!showQuickLaunch) return null;
|
|
3002
|
+
|
|
1502
3003
|
return React.createElement(
|
|
3004
|
+
|
|
1503
3005
|
React.Fragment,
|
|
3006
|
+
|
|
1504
3007
|
null,
|
|
3008
|
+
|
|
1505
3009
|
React.createElement(
|
|
3010
|
+
|
|
1506
3011
|
'div',
|
|
3012
|
+
|
|
1507
3013
|
{ className: 'dsh-goal-dock dsh-goal-dock-quick', 'data-goal-dock': 'true' },
|
|
3014
|
+
|
|
1508
3015
|
React.createElement(
|
|
3016
|
+
|
|
1509
3017
|
'button',
|
|
3018
|
+
|
|
1510
3019
|
{
|
|
3020
|
+
|
|
1511
3021
|
className: 'dsh-goal-quicklaunch-btn',
|
|
3022
|
+
|
|
1512
3023
|
title: t('quickLaunch') || 'Start Goal (Goal Mode)',
|
|
3024
|
+
|
|
1513
3025
|
onClick: () => setIsQuickLaunchOpen(true),
|
|
3026
|
+
|
|
1514
3027
|
},
|
|
3028
|
+
|
|
1515
3029
|
React.createElement(IconTarget, { size: 14 }),
|
|
3030
|
+
|
|
1516
3031
|
React.createElement('span', null, t('quickLaunch') || 'Start Goal'),
|
|
3032
|
+
|
|
1517
3033
|
),
|
|
3034
|
+
|
|
1518
3035
|
),
|
|
3036
|
+
|
|
1519
3037
|
isQuickLaunchOpen
|
|
3038
|
+
|
|
1520
3039
|
? React.createElement(QuickLaunchModal, {
|
|
3040
|
+
|
|
1521
3041
|
onClose: () => setIsQuickLaunchOpen(false),
|
|
3042
|
+
|
|
1522
3043
|
onStart: async (title) => {
|
|
3044
|
+
|
|
1523
3045
|
await handleAction('start', { title });
|
|
3046
|
+
|
|
1524
3047
|
setIsQuickLaunchOpen(false);
|
|
3048
|
+
|
|
1525
3049
|
},
|
|
3050
|
+
|
|
1526
3051
|
t,
|
|
3052
|
+
|
|
1527
3053
|
})
|
|
3054
|
+
|
|
1528
3055
|
: null,
|
|
3056
|
+
|
|
1529
3057
|
);
|
|
3058
|
+
|
|
1530
3059
|
}
|
|
1531
3060
|
|
|
3061
|
+
|
|
3062
|
+
|
|
1532
3063
|
const isPaused = state.state === 'PAUSED';
|
|
3064
|
+
|
|
1533
3065
|
const isCompleted = state.state === 'COMPLETED';
|
|
1534
3066
|
|
|
3067
|
+
|
|
3068
|
+
|
|
1535
3069
|
const formatLiveElapsed = () => {
|
|
3070
|
+
|
|
1536
3071
|
if (!state) return '0s';
|
|
3072
|
+
|
|
1537
3073
|
if (!state.startedAt) return state.formattedElapsed || '0s';
|
|
3074
|
+
|
|
1538
3075
|
const endTime = state.completedAt || (state.pausedAt || now);
|
|
3076
|
+
|
|
1539
3077
|
const elapsedSec = Math.max(0, Math.floor((endTime - state.startedAt - (state.totalPausedDurationMs || 0)) / 1000));
|
|
3078
|
+
|
|
1540
3079
|
return formatElapsed(elapsedSec);
|
|
3080
|
+
|
|
1541
3081
|
};
|
|
1542
3082
|
|
|
3083
|
+
|
|
3084
|
+
|
|
1543
3085
|
const liveElapsedStr = formatLiveElapsed();
|
|
3086
|
+
|
|
1544
3087
|
const etaStr = state?.formattedETA ? ` (ETA ${state.formattedETA})` : '';
|
|
1545
3088
|
|
|
3089
|
+
|
|
3090
|
+
|
|
1546
3091
|
return React.createElement(
|
|
3092
|
+
|
|
1547
3093
|
React.Fragment,
|
|
3094
|
+
|
|
1548
3095
|
null,
|
|
3096
|
+
|
|
1549
3097
|
React.createElement(
|
|
3098
|
+
|
|
1550
3099
|
'div',
|
|
3100
|
+
|
|
1551
3101
|
{ className: 'dsh-goal-dock', 'data-goal-dock': 'true' },
|
|
3102
|
+
|
|
1552
3103
|
React.createElement(
|
|
3104
|
+
|
|
1553
3105
|
'div',
|
|
3106
|
+
|
|
1554
3107
|
{ className: `dsh-goal-banner${isCompleted ? ' completed' : ''}` },
|
|
3108
|
+
|
|
1555
3109
|
React.createElement(
|
|
3110
|
+
|
|
1556
3111
|
'div',
|
|
3112
|
+
|
|
1557
3113
|
{ className: 'dsh-goal-left' },
|
|
3114
|
+
|
|
1558
3115
|
React.createElement(
|
|
3116
|
+
|
|
1559
3117
|
'span',
|
|
3118
|
+
|
|
1560
3119
|
{ className: 'dsh-goal-icon' },
|
|
3120
|
+
|
|
1561
3121
|
isCompleted ? React.createElement(IconCheck, { size: 16 }) : React.createElement(IconTarget, { size: 15 }),
|
|
3122
|
+
|
|
1562
3123
|
),
|
|
3124
|
+
|
|
1563
3125
|
React.createElement(
|
|
3126
|
+
|
|
1564
3127
|
'span',
|
|
3128
|
+
|
|
1565
3129
|
{
|
|
3130
|
+
|
|
1566
3131
|
className: `dsh-goal-badge ${isCompleted ? 'dsh-goal-badge-ok' : isPaused ? 'dsh-goal-badge-warn' : ''}`,
|
|
3132
|
+
|
|
1567
3133
|
},
|
|
3134
|
+
|
|
1568
3135
|
isCompleted ? (t('goalCompleted') || 'Goal completed') : isPaused ? (t('paused') || 'Paused') : (t('goalLabel') || 'Current goal'),
|
|
3136
|
+
|
|
1569
3137
|
),
|
|
3138
|
+
|
|
1570
3139
|
React.createElement('span', { className: 'dsh-goal-title', title: state.title }, state.title),
|
|
3140
|
+
|
|
1571
3141
|
),
|
|
3142
|
+
|
|
1572
3143
|
React.createElement(
|
|
3144
|
+
|
|
1573
3145
|
'div',
|
|
3146
|
+
|
|
1574
3147
|
{ className: 'dsh-goal-right' },
|
|
3148
|
+
|
|
1575
3149
|
React.createElement(
|
|
3150
|
+
|
|
1576
3151
|
'span',
|
|
3152
|
+
|
|
1577
3153
|
{ className: 'dsh-goal-time' },
|
|
3154
|
+
|
|
1578
3155
|
`⏱ ${liveElapsedStr}${etaStr}`,
|
|
3156
|
+
|
|
1579
3157
|
),
|
|
3158
|
+
|
|
1580
3159
|
!isCompleted
|
|
3160
|
+
|
|
1581
3161
|
? React.createElement(
|
|
3162
|
+
|
|
1582
3163
|
'button',
|
|
3164
|
+
|
|
1583
3165
|
{
|
|
3166
|
+
|
|
1584
3167
|
className: 'dsh-goal-btn icon-only',
|
|
3168
|
+
|
|
1585
3169
|
title: isPaused ? (t('resume') || 'Resume') : (t('pause') || 'Pause'),
|
|
3170
|
+
|
|
1586
3171
|
onClick: () => handleAction(isPaused ? 'resume' : 'pause'),
|
|
3172
|
+
|
|
1587
3173
|
},
|
|
3174
|
+
|
|
1588
3175
|
isPaused ? React.createElement(IconPlay, { size: 14 }) : React.createElement(IconPause, { size: 14 }),
|
|
3176
|
+
|
|
1589
3177
|
)
|
|
3178
|
+
|
|
1590
3179
|
: null,
|
|
3180
|
+
|
|
1591
3181
|
React.createElement(
|
|
3182
|
+
|
|
1592
3183
|
'button',
|
|
3184
|
+
|
|
1593
3185
|
{
|
|
3186
|
+
|
|
1594
3187
|
className: 'dsh-goal-btn icon-only',
|
|
3188
|
+
|
|
1595
3189
|
title: t('details') || 'Expand goal details',
|
|
3190
|
+
|
|
1596
3191
|
onClick: () => setIsModalOpen(true),
|
|
3192
|
+
|
|
1597
3193
|
},
|
|
3194
|
+
|
|
1598
3195
|
React.createElement(IconExpand, { size: 14 }),
|
|
3196
|
+
|
|
1599
3197
|
),
|
|
3198
|
+
|
|
1600
3199
|
isCompleted
|
|
3200
|
+
|
|
1601
3201
|
? React.createElement(
|
|
3202
|
+
|
|
1602
3203
|
'button',
|
|
3204
|
+
|
|
1603
3205
|
{
|
|
3206
|
+
|
|
1604
3207
|
className: 'dsh-goal-btn icon-only close',
|
|
3208
|
+
|
|
1605
3209
|
title: t('closeBanner') || 'Close goal banner',
|
|
3210
|
+
|
|
1606
3211
|
onClick: () => handleAction('clear'),
|
|
3212
|
+
|
|
1607
3213
|
},
|
|
3214
|
+
|
|
1608
3215
|
'✕',
|
|
3216
|
+
|
|
1609
3217
|
)
|
|
3218
|
+
|
|
1610
3219
|
: null,
|
|
3220
|
+
|
|
1611
3221
|
),
|
|
3222
|
+
|
|
1612
3223
|
),
|
|
3224
|
+
|
|
1613
3225
|
),
|
|
3226
|
+
|
|
1614
3227
|
isModalOpen
|
|
3228
|
+
|
|
1615
3229
|
? React.createElement(GoalDetailsModal, {
|
|
3230
|
+
|
|
1616
3231
|
state,
|
|
3232
|
+
|
|
1617
3233
|
onClose: () => setIsModalOpen(false),
|
|
3234
|
+
|
|
1618
3235
|
onAction: handleAction,
|
|
3236
|
+
|
|
1619
3237
|
t,
|
|
3238
|
+
|
|
1620
3239
|
})
|
|
3240
|
+
|
|
1621
3241
|
: null,
|
|
3242
|
+
|
|
1622
3243
|
);
|
|
3244
|
+
|
|
1623
3245
|
}
|
|
1624
3246
|
|
|
3247
|
+
|
|
3248
|
+
|
|
1625
3249
|
// --- ЛОГИКА ФОРМЫ НАСТРОЕК ---
|
|
3250
|
+
|
|
1626
3251
|
const DEFAULT_SETTINGS = {
|
|
3252
|
+
|
|
1627
3253
|
maxIterations: 25,
|
|
3254
|
+
|
|
1628
3255
|
autoDrive: true,
|
|
3256
|
+
|
|
1629
3257
|
enableSound: true,
|
|
3258
|
+
|
|
1630
3259
|
showQuickLaunchButton: true,
|
|
3260
|
+
|
|
1631
3261
|
consecutiveToolFailureLimit: 3,
|
|
3262
|
+
|
|
1632
3263
|
enableBrowserNotifications: true,
|
|
3264
|
+
|
|
1633
3265
|
};
|
|
1634
3266
|
|
|
3267
|
+
|
|
3268
|
+
|
|
1635
3269
|
function parseNumberField(raw) {
|
|
3270
|
+
|
|
1636
3271
|
if (raw === '' || raw === null || raw === undefined) {
|
|
3272
|
+
|
|
1637
3273
|
return undefined;
|
|
3274
|
+
|
|
1638
3275
|
}
|
|
3276
|
+
|
|
1639
3277
|
const n = Number(raw);
|
|
3278
|
+
|
|
1640
3279
|
if (!Number.isFinite(n) || n < 1 || !Number.isInteger(n)) {
|
|
3280
|
+
|
|
1641
3281
|
return NaN;
|
|
3282
|
+
|
|
1642
3283
|
}
|
|
3284
|
+
|
|
1643
3285
|
return n;
|
|
3286
|
+
|
|
1644
3287
|
}
|
|
1645
3288
|
|
|
3289
|
+
|
|
3290
|
+
|
|
1646
3291
|
function getFieldStatus(field, draftValue, snap) {
|
|
3292
|
+
|
|
1647
3293
|
const baseVal = snap?.base?.[field] ?? DEFAULT_SETTINGS[field];
|
|
3294
|
+
|
|
1648
3295
|
const userVal = snap?.user?.[field];
|
|
3296
|
+
|
|
1649
3297
|
const effectiveVal = snap?.value?.[field] ?? baseVal;
|
|
1650
3298
|
|
|
3299
|
+
|
|
3300
|
+
|
|
1651
3301
|
const isOverridden = userVal !== undefined && userVal !== null;
|
|
3302
|
+
|
|
1652
3303
|
const currentVal = draftValue !== undefined ? draftValue : effectiveVal;
|
|
1653
3304
|
|
|
3305
|
+
|
|
3306
|
+
|
|
1654
3307
|
let invalid = false;
|
|
3308
|
+
|
|
1655
3309
|
let isDirty = false;
|
|
1656
3310
|
|
|
3311
|
+
|
|
3312
|
+
|
|
1657
3313
|
if (field === 'maxIterations') {
|
|
3314
|
+
|
|
1658
3315
|
if (draftValue !== undefined) {
|
|
3316
|
+
|
|
1659
3317
|
if (draftValue === '') {
|
|
3318
|
+
|
|
1660
3319
|
invalid = false;
|
|
3320
|
+
|
|
1661
3321
|
isDirty = userVal !== undefined;
|
|
3322
|
+
|
|
1662
3323
|
} else {
|
|
3324
|
+
|
|
1663
3325
|
const parsed = parseNumberField(draftValue);
|
|
3326
|
+
|
|
1664
3327
|
if (Number.isNaN(parsed)) {
|
|
3328
|
+
|
|
1665
3329
|
invalid = true;
|
|
3330
|
+
|
|
1666
3331
|
isDirty = true;
|
|
3332
|
+
|
|
1667
3333
|
} else {
|
|
3334
|
+
|
|
1668
3335
|
isDirty = parsed !== (userVal !== undefined ? userVal : baseVal);
|
|
3336
|
+
|
|
1669
3337
|
}
|
|
3338
|
+
|
|
1670
3339
|
}
|
|
3340
|
+
|
|
1671
3341
|
}
|
|
3342
|
+
|
|
1672
3343
|
} else {
|
|
3344
|
+
|
|
1673
3345
|
if (draftValue !== undefined) {
|
|
3346
|
+
|
|
1674
3347
|
isDirty = Boolean(draftValue) !== (userVal !== undefined ? Boolean(userVal) : Boolean(baseVal));
|
|
3348
|
+
|
|
1675
3349
|
}
|
|
3350
|
+
|
|
1676
3351
|
}
|
|
1677
3352
|
|
|
3353
|
+
|
|
3354
|
+
|
|
1678
3355
|
return {
|
|
3356
|
+
|
|
1679
3357
|
value: currentVal,
|
|
3358
|
+
|
|
1680
3359
|
baseValue: baseVal,
|
|
3360
|
+
|
|
1681
3361
|
userValue: userVal,
|
|
3362
|
+
|
|
1682
3363
|
isOverridden,
|
|
3364
|
+
|
|
1683
3365
|
isDirty,
|
|
3366
|
+
|
|
1684
3367
|
invalid,
|
|
3368
|
+
|
|
1685
3369
|
};
|
|
3370
|
+
|
|
1686
3371
|
}
|
|
1687
3372
|
|
|
3373
|
+
|
|
3374
|
+
|
|
1688
3375
|
function computeSavePlan(draft, snap) {
|
|
3376
|
+
|
|
1689
3377
|
if (!draft) return [];
|
|
1690
3378
|
|
|
3379
|
+
|
|
3380
|
+
|
|
1691
3381
|
const fields = ['maxIterations', 'autoDrive', 'enableSound', 'showQuickLaunchButton'];
|
|
3382
|
+
|
|
1692
3383
|
const writes = [];
|
|
1693
3384
|
|
|
3385
|
+
|
|
3386
|
+
|
|
1694
3387
|
for (const f of fields) {
|
|
3388
|
+
|
|
1695
3389
|
if (draft[f] === undefined) continue;
|
|
1696
3390
|
|
|
3391
|
+
|
|
3392
|
+
|
|
1697
3393
|
const status = getFieldStatus(f, draft[f], snap);
|
|
3394
|
+
|
|
1698
3395
|
if (status.invalid) continue;
|
|
1699
3396
|
|
|
3397
|
+
|
|
3398
|
+
|
|
1700
3399
|
if (status.isDirty) {
|
|
3400
|
+
|
|
1701
3401
|
if (f === 'maxIterations' && draft[f] === '') {
|
|
3402
|
+
|
|
1702
3403
|
writes.push([f, undefined]);
|
|
3404
|
+
|
|
1703
3405
|
} else if (f === 'maxIterations') {
|
|
3406
|
+
|
|
1704
3407
|
writes.push([f, parseNumberField(draft[f])]);
|
|
3408
|
+
|
|
1705
3409
|
} else {
|
|
3410
|
+
|
|
1706
3411
|
writes.push([f, Boolean(draft[f])]);
|
|
3412
|
+
|
|
1707
3413
|
}
|
|
3414
|
+
|
|
1708
3415
|
}
|
|
3416
|
+
|
|
1709
3417
|
}
|
|
1710
3418
|
|
|
3419
|
+
|
|
3420
|
+
|
|
1711
3421
|
return writes;
|
|
3422
|
+
|
|
1712
3423
|
}
|
|
1713
3424
|
|
|
3425
|
+
|
|
3426
|
+
|
|
1714
3427
|
// --- КАРТОЧКА НАСТРОЕК (SETTINGS CARD) ---
|
|
3428
|
+
|
|
1715
3429
|
function GoalSettingsCard(props) {
|
|
3430
|
+
|
|
1716
3431
|
const { scope, ctx } = props || {};
|
|
3432
|
+
|
|
1717
3433
|
const [isOpen, setIsOpen] = useState(false);
|
|
3434
|
+
|
|
1718
3435
|
const [draft, setDraft] = useState(null);
|
|
3436
|
+
|
|
1719
3437
|
const [saving, setSaving] = useState(false);
|
|
3438
|
+
|
|
1720
3439
|
const [failed, setFailed] = useState(false);
|
|
1721
3440
|
|
|
3441
|
+
|
|
3442
|
+
|
|
1722
3443
|
const [snap, setSnap] = useState(() => (scope ? scope.getSnapshot() : { value: DEFAULT_SETTINGS, base: DEFAULT_SETTINGS, user: {} }));
|
|
1723
3444
|
|
|
3445
|
+
|
|
3446
|
+
|
|
1724
3447
|
useEffect(() => {
|
|
3448
|
+
|
|
1725
3449
|
injectStylesOnce();
|
|
3450
|
+
|
|
1726
3451
|
}, []);
|
|
1727
3452
|
|
|
3453
|
+
|
|
3454
|
+
|
|
1728
3455
|
useEffect(() => {
|
|
3456
|
+
|
|
1729
3457
|
if (!scope || typeof scope.subscribe !== 'function') return;
|
|
3458
|
+
|
|
1730
3459
|
return scope.subscribe((next) => {
|
|
3460
|
+
|
|
1731
3461
|
setSnap(next || scope.getSnapshot());
|
|
3462
|
+
|
|
1732
3463
|
});
|
|
3464
|
+
|
|
1733
3465
|
}, [scope]);
|
|
1734
3466
|
|
|
3467
|
+
|
|
3468
|
+
|
|
1735
3469
|
const resolveLocale = () => {
|
|
3470
|
+
|
|
1736
3471
|
let active = 'en';
|
|
3472
|
+
|
|
1737
3473
|
if (ctx?.locale?.getSnapshot) {
|
|
3474
|
+
|
|
1738
3475
|
const snapLoc = ctx.locale.getSnapshot();
|
|
3476
|
+
|
|
1739
3477
|
if (snapLoc?.active) {
|
|
3478
|
+
|
|
1740
3479
|
const code = snapLoc.active.toLowerCase();
|
|
3480
|
+
|
|
1741
3481
|
if (code.startsWith('zh')) active = 'zh';
|
|
3482
|
+
|
|
1742
3483
|
else if (code.startsWith('ru')) active = 'ru';
|
|
3484
|
+
|
|
1743
3485
|
else active = 'en';
|
|
3486
|
+
|
|
1744
3487
|
}
|
|
3488
|
+
|
|
1745
3489
|
} else if (typeof navigator !== 'undefined' && navigator.language) {
|
|
3490
|
+
|
|
1746
3491
|
const code = navigator.language.toLowerCase();
|
|
3492
|
+
|
|
1747
3493
|
if (code.startsWith('zh')) active = 'zh';
|
|
3494
|
+
|
|
1748
3495
|
else if (code.startsWith('ru')) active = 'ru';
|
|
3496
|
+
|
|
1749
3497
|
else active = 'en';
|
|
3498
|
+
|
|
1750
3499
|
}
|
|
3500
|
+
|
|
1751
3501
|
return active;
|
|
3502
|
+
|
|
1752
3503
|
};
|
|
1753
3504
|
|
|
3505
|
+
|
|
3506
|
+
|
|
1754
3507
|
const t = (key) => {
|
|
3508
|
+
|
|
1755
3509
|
const active = resolveLocale();
|
|
3510
|
+
|
|
1756
3511
|
return LOCALES[active]?.[key] || LOCALES.en[key] || LOCALES.ru?.[key] || key;
|
|
3512
|
+
|
|
1757
3513
|
};
|
|
1758
3514
|
|
|
3515
|
+
|
|
3516
|
+
|
|
1759
3517
|
const status = (snap && snap.status) || 'ready';
|
|
3518
|
+
|
|
1760
3519
|
const maxIterStatus = getFieldStatus('maxIterations', draft?.maxIterations, snap);
|
|
3520
|
+
|
|
1761
3521
|
const autoDriveStatus = getFieldStatus('autoDrive', draft?.autoDrive, snap);
|
|
3522
|
+
|
|
1762
3523
|
const enableSoundStatus = getFieldStatus('enableSound', draft?.enableSound, snap);
|
|
3524
|
+
|
|
1763
3525
|
const quickLaunchStatus = getFieldStatus('showQuickLaunchButton', draft?.showQuickLaunchButton, snap);
|
|
3526
|
+
|
|
1764
3527
|
const toolFailStatus = getFieldStatus('consecutiveToolFailureLimit', draft?.consecutiveToolFailureLimit, snap);
|
|
3528
|
+
|
|
1765
3529
|
const notifStatus = getFieldStatus('enableBrowserNotifications', draft?.enableBrowserNotifications, snap);
|
|
1766
3530
|
|
|
3531
|
+
|
|
3532
|
+
|
|
1767
3533
|
const dirty = maxIterStatus.isDirty || autoDriveStatus.isDirty || enableSoundStatus.isDirty || quickLaunchStatus.isDirty || toolFailStatus.isDirty || notifStatus.isDirty;
|
|
3534
|
+
|
|
1768
3535
|
const invalid = maxIterStatus.invalid || toolFailStatus.invalid;
|
|
3536
|
+
|
|
1769
3537
|
const disabled = !scope || saving || snap?.writable === false;
|
|
1770
3538
|
|
|
3539
|
+
|
|
3540
|
+
|
|
1771
3541
|
const edit = (field, val) => {
|
|
3542
|
+
|
|
1772
3543
|
setFailed(false);
|
|
3544
|
+
|
|
1773
3545
|
setDraft((prev) => ({
|
|
3546
|
+
|
|
1774
3547
|
maxIterations: prev?.maxIterations !== undefined ? prev.maxIterations : maxIterStatus.value,
|
|
3548
|
+
|
|
1775
3549
|
autoDrive: prev?.autoDrive !== undefined ? prev.autoDrive : autoDriveStatus.value,
|
|
3550
|
+
|
|
1776
3551
|
enableSound: prev?.enableSound !== undefined ? prev.enableSound : enableSoundStatus.value,
|
|
3552
|
+
|
|
1777
3553
|
showQuickLaunchButton: prev?.showQuickLaunchButton !== undefined ? prev.showQuickLaunchButton : quickLaunchStatus.value,
|
|
3554
|
+
|
|
1778
3555
|
consecutiveToolFailureLimit: prev?.consecutiveToolFailureLimit !== undefined ? prev.consecutiveToolFailureLimit : toolFailStatus.value,
|
|
3556
|
+
|
|
1779
3557
|
enableBrowserNotifications: prev?.enableBrowserNotifications !== undefined ? prev.enableBrowserNotifications : notifStatus.value,
|
|
3558
|
+
|
|
1780
3559
|
[field]: val,
|
|
3560
|
+
|
|
1781
3561
|
}));
|
|
3562
|
+
|
|
1782
3563
|
};
|
|
1783
3564
|
|
|
3565
|
+
|
|
3566
|
+
|
|
1784
3567
|
const resetFieldToDefault = (field) => {
|
|
3568
|
+
|
|
1785
3569
|
if (field === 'maxIterations') {
|
|
3570
|
+
|
|
1786
3571
|
edit(field, '');
|
|
3572
|
+
|
|
1787
3573
|
} else {
|
|
3574
|
+
|
|
1788
3575
|
edit(field, snap?.base?.[field] ?? DEFAULT_SETTINGS[field]);
|
|
3576
|
+
|
|
1789
3577
|
}
|
|
3578
|
+
|
|
1790
3579
|
};
|
|
1791
3580
|
|
|
3581
|
+
|
|
3582
|
+
|
|
1792
3583
|
const save = async () => {
|
|
3584
|
+
|
|
1793
3585
|
if (!scope || !dirty || invalid) return;
|
|
3586
|
+
|
|
1794
3587
|
setSaving(true);
|
|
3588
|
+
|
|
1795
3589
|
setFailed(false);
|
|
1796
3590
|
|
|
3591
|
+
|
|
3592
|
+
|
|
1797
3593
|
const writes = computeSavePlan(draft, snap);
|
|
3594
|
+
|
|
1798
3595
|
let landed = true;
|
|
1799
3596
|
|
|
3597
|
+
|
|
3598
|
+
|
|
1800
3599
|
for (const [field, fieldValue] of writes) {
|
|
3600
|
+
|
|
1801
3601
|
try {
|
|
3602
|
+
|
|
1802
3603
|
await scope.set(field, fieldValue);
|
|
3604
|
+
|
|
1803
3605
|
} catch (err) {
|
|
3606
|
+
|
|
1804
3607
|
console.error('[dsh-goal] failed to save setting', field, err);
|
|
3608
|
+
|
|
1805
3609
|
landed = false;
|
|
3610
|
+
|
|
1806
3611
|
}
|
|
3612
|
+
|
|
1807
3613
|
}
|
|
1808
3614
|
|
|
3615
|
+
|
|
3616
|
+
|
|
1809
3617
|
const fresh = scope.getSnapshot();
|
|
3618
|
+
|
|
1810
3619
|
const user = fresh.user || {};
|
|
3620
|
+
|
|
1811
3621
|
for (const [field, fieldValue] of writes) {
|
|
3622
|
+
|
|
1812
3623
|
if (fieldValue === undefined) {
|
|
3624
|
+
|
|
1813
3625
|
if (user[field] !== undefined) landed = false;
|
|
3626
|
+
|
|
1814
3627
|
} else {
|
|
3628
|
+
|
|
1815
3629
|
if (user[field] !== fieldValue) landed = false;
|
|
3630
|
+
|
|
1816
3631
|
}
|
|
3632
|
+
|
|
1817
3633
|
}
|
|
1818
3634
|
|
|
3635
|
+
|
|
3636
|
+
|
|
1819
3637
|
setSnap(fresh);
|
|
3638
|
+
|
|
1820
3639
|
setSaving(false);
|
|
3640
|
+
|
|
1821
3641
|
if (landed) {
|
|
3642
|
+
|
|
1822
3643
|
setDraft(null);
|
|
3644
|
+
|
|
1823
3645
|
} else {
|
|
3646
|
+
|
|
1824
3647
|
setFailed(true);
|
|
3648
|
+
|
|
1825
3649
|
}
|
|
3650
|
+
|
|
1826
3651
|
};
|
|
1827
3652
|
|
|
3653
|
+
|
|
3654
|
+
|
|
1828
3655
|
const Chevron = (Primitives && Primitives.IconChevronDownOutline14) || IconChevronDown;
|
|
1829
3656
|
|
|
3657
|
+
|
|
3658
|
+
|
|
1830
3659
|
return React.createElement(
|
|
3660
|
+
|
|
1831
3661
|
'li',
|
|
3662
|
+
|
|
1832
3663
|
{ className: 'dsh-goal-card' },
|
|
3664
|
+
|
|
1833
3665
|
React.createElement(
|
|
3666
|
+
|
|
1834
3667
|
'button',
|
|
3668
|
+
|
|
1835
3669
|
{
|
|
3670
|
+
|
|
1836
3671
|
className: 'dsh-goal-card-head',
|
|
3672
|
+
|
|
1837
3673
|
onClick: () => setIsOpen(!isOpen),
|
|
3674
|
+
|
|
1838
3675
|
'aria-expanded': isOpen,
|
|
3676
|
+
|
|
1839
3677
|
},
|
|
3678
|
+
|
|
1840
3679
|
React.createElement(
|
|
3680
|
+
|
|
1841
3681
|
'div',
|
|
3682
|
+
|
|
1842
3683
|
{ style: { flex: 1 } },
|
|
3684
|
+
|
|
1843
3685
|
React.createElement('div', { className: 'dsh-goal-card-title' }, t('pluginTitle') || 'Goal Mode & Autonomous Loop'),
|
|
3686
|
+
|
|
1844
3687
|
React.createElement('div', { className: 'dsh-goal-card-sub' }, t('pluginDesc') || 'Goal banner above composer dock, milestone decomposition, and auto-drive'),
|
|
3688
|
+
|
|
1845
3689
|
),
|
|
3690
|
+
|
|
1846
3691
|
React.createElement(Chevron, { className: `dsh-goal-chev ${isOpen ? 'dsh-goal-chev-open' : ''}` }),
|
|
3692
|
+
|
|
1847
3693
|
),
|
|
3694
|
+
|
|
1848
3695
|
isOpen
|
|
3696
|
+
|
|
1849
3697
|
? React.createElement(
|
|
3698
|
+
|
|
1850
3699
|
'div',
|
|
3700
|
+
|
|
1851
3701
|
{ className: 'dsh-goal-card-body' },
|
|
3702
|
+
|
|
1852
3703
|
status === 'loading'
|
|
3704
|
+
|
|
1853
3705
|
? React.createElement('p', { className: 'dsh-goal-field-hint', style: { padding: '12px 0' } }, t('loading') || 'Loading settings…')
|
|
3706
|
+
|
|
1854
3707
|
: status !== 'ready'
|
|
3708
|
+
|
|
1855
3709
|
? React.createElement('p', { className: 'dsh-goal-foot-note', style: { padding: '12px 0' } }, t('unavailable') || 'Settings unavailable')
|
|
3710
|
+
|
|
1856
3711
|
: React.createElement(
|
|
3712
|
+
|
|
1857
3713
|
React.Fragment,
|
|
3714
|
+
|
|
1858
3715
|
null,
|
|
3716
|
+
|
|
1859
3717
|
React.createElement(
|
|
3718
|
+
|
|
1860
3719
|
'div',
|
|
3720
|
+
|
|
1861
3721
|
{ className: 'dsh-goal-card-field' },
|
|
3722
|
+
|
|
1862
3723
|
React.createElement(
|
|
3724
|
+
|
|
1863
3725
|
'div',
|
|
3726
|
+
|
|
1864
3727
|
{ className: 'dsh-goal-field-label-row' },
|
|
3728
|
+
|
|
1865
3729
|
React.createElement('label', { htmlFor: 'dsh-goal-max-iter' }, t('maxIterLabel') || 'Max iterations (Safety Limit):'),
|
|
3730
|
+
|
|
1866
3731
|
React.createElement(
|
|
3732
|
+
|
|
1867
3733
|
'div',
|
|
3734
|
+
|
|
1868
3735
|
{ className: 'dsh-goal-field-meta' },
|
|
3736
|
+
|
|
1869
3737
|
maxIterStatus.isOverridden
|
|
3738
|
+
|
|
1870
3739
|
? React.createElement('span', { className: 'dsh-goal-override-tag' }, t('overridden') || 'overridden')
|
|
3740
|
+
|
|
1871
3741
|
: null,
|
|
3742
|
+
|
|
1872
3743
|
maxIterStatus.isOverridden
|
|
3744
|
+
|
|
1873
3745
|
? React.createElement(
|
|
3746
|
+
|
|
1874
3747
|
'button',
|
|
3748
|
+
|
|
1875
3749
|
{
|
|
3750
|
+
|
|
1876
3751
|
type: 'button',
|
|
3752
|
+
|
|
1877
3753
|
className: 'dsh-goal-btn-inline-reset',
|
|
3754
|
+
|
|
1878
3755
|
title: t('resetField') || 'Reset to default',
|
|
3756
|
+
|
|
1879
3757
|
onClick: () => resetFieldToDefault('maxIterations'),
|
|
3758
|
+
|
|
1880
3759
|
},
|
|
3760
|
+
|
|
1881
3761
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
3762
|
+
|
|
1882
3763
|
t('resetField') || 'Default',
|
|
3764
|
+
|
|
1883
3765
|
)
|
|
3766
|
+
|
|
1884
3767
|
: null,
|
|
3768
|
+
|
|
1885
3769
|
),
|
|
3770
|
+
|
|
1886
3771
|
),
|
|
3772
|
+
|
|
1887
3773
|
React.createElement('input', {
|
|
3774
|
+
|
|
1888
3775
|
id: 'dsh-goal-max-iter',
|
|
3776
|
+
|
|
1889
3777
|
type: 'number',
|
|
3778
|
+
|
|
1890
3779
|
min: 1,
|
|
3780
|
+
|
|
1891
3781
|
placeholder: String(maxIterStatus.baseValue),
|
|
3782
|
+
|
|
1892
3783
|
className: 'dsh-goal-card-input',
|
|
3784
|
+
|
|
1893
3785
|
value: maxIterStatus.value !== undefined ? maxIterStatus.value : '',
|
|
3786
|
+
|
|
1894
3787
|
disabled,
|
|
3788
|
+
|
|
1895
3789
|
'aria-invalid': invalid,
|
|
3790
|
+
|
|
1896
3791
|
onChange: (e) => edit('maxIterations', e.target.value),
|
|
3792
|
+
|
|
1897
3793
|
}),
|
|
3794
|
+
|
|
1898
3795
|
),
|
|
3796
|
+
|
|
1899
3797
|
React.createElement(
|
|
3798
|
+
|
|
1900
3799
|
'div',
|
|
3800
|
+
|
|
1901
3801
|
{ className: 'dsh-goal-check-row' },
|
|
3802
|
+
|
|
1902
3803
|
React.createElement(
|
|
3804
|
+
|
|
1903
3805
|
'label',
|
|
3806
|
+
|
|
1904
3807
|
{ className: 'dsh-goal-check' },
|
|
3808
|
+
|
|
1905
3809
|
React.createElement('input', {
|
|
3810
|
+
|
|
1906
3811
|
type: 'checkbox',
|
|
3812
|
+
|
|
1907
3813
|
checked: autoDriveStatus.value === true,
|
|
3814
|
+
|
|
1908
3815
|
disabled,
|
|
3816
|
+
|
|
1909
3817
|
onChange: (e) => edit('autoDrive', e.target.checked),
|
|
3818
|
+
|
|
1910
3819
|
}),
|
|
3820
|
+
|
|
1911
3821
|
t('autoDriveLabel') || 'Auto-drive: keep the loop running automatically',
|
|
3822
|
+
|
|
1912
3823
|
),
|
|
3824
|
+
|
|
1913
3825
|
autoDriveStatus.isOverridden
|
|
3826
|
+
|
|
1914
3827
|
? React.createElement(
|
|
3828
|
+
|
|
1915
3829
|
'button',
|
|
3830
|
+
|
|
1916
3831
|
{
|
|
3832
|
+
|
|
1917
3833
|
type: 'button',
|
|
3834
|
+
|
|
1918
3835
|
className: 'dsh-goal-btn-inline-reset',
|
|
3836
|
+
|
|
1919
3837
|
title: t('resetField') || 'Reset to default',
|
|
3838
|
+
|
|
1920
3839
|
onClick: () => resetFieldToDefault('autoDrive'),
|
|
3840
|
+
|
|
1921
3841
|
},
|
|
3842
|
+
|
|
1922
3843
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
3844
|
+
|
|
1923
3845
|
)
|
|
3846
|
+
|
|
1924
3847
|
: null,
|
|
3848
|
+
|
|
1925
3849
|
),
|
|
3850
|
+
|
|
1926
3851
|
React.createElement(
|
|
3852
|
+
|
|
1927
3853
|
'div',
|
|
3854
|
+
|
|
1928
3855
|
{ className: 'dsh-goal-check-row' },
|
|
3856
|
+
|
|
1929
3857
|
React.createElement(
|
|
3858
|
+
|
|
1930
3859
|
'label',
|
|
3860
|
+
|
|
1931
3861
|
{ className: 'dsh-goal-check' },
|
|
3862
|
+
|
|
1932
3863
|
React.createElement('input', {
|
|
3864
|
+
|
|
1933
3865
|
type: 'checkbox',
|
|
3866
|
+
|
|
1934
3867
|
checked: enableSoundStatus.value === true,
|
|
3868
|
+
|
|
1935
3869
|
disabled,
|
|
3870
|
+
|
|
1936
3871
|
onChange: (e) => edit('enableSound', e.target.checked),
|
|
3872
|
+
|
|
1937
3873
|
}),
|
|
3874
|
+
|
|
1938
3875
|
t('soundLabel') || 'Sound when a goal completes',
|
|
3876
|
+
|
|
1939
3877
|
),
|
|
3878
|
+
|
|
1940
3879
|
enableSoundStatus.isOverridden
|
|
3880
|
+
|
|
1941
3881
|
? React.createElement(
|
|
3882
|
+
|
|
1942
3883
|
'button',
|
|
3884
|
+
|
|
1943
3885
|
{
|
|
3886
|
+
|
|
1944
3887
|
type: 'button',
|
|
3888
|
+
|
|
1945
3889
|
className: 'dsh-goal-btn-inline-reset',
|
|
3890
|
+
|
|
1946
3891
|
title: t('resetField') || 'Reset to default',
|
|
3892
|
+
|
|
1947
3893
|
onClick: () => resetFieldToDefault('enableSound'),
|
|
3894
|
+
|
|
1948
3895
|
},
|
|
3896
|
+
|
|
1949
3897
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
3898
|
+
|
|
1950
3899
|
)
|
|
3900
|
+
|
|
1951
3901
|
: null,
|
|
3902
|
+
|
|
1952
3903
|
),
|
|
3904
|
+
|
|
1953
3905
|
React.createElement(
|
|
3906
|
+
|
|
1954
3907
|
'div',
|
|
3908
|
+
|
|
1955
3909
|
{ className: 'dsh-goal-check-row' },
|
|
3910
|
+
|
|
1956
3911
|
React.createElement(
|
|
3912
|
+
|
|
1957
3913
|
'label',
|
|
3914
|
+
|
|
1958
3915
|
{ className: 'dsh-goal-check' },
|
|
3916
|
+
|
|
1959
3917
|
React.createElement('input', {
|
|
3918
|
+
|
|
1960
3919
|
type: 'checkbox',
|
|
3920
|
+
|
|
1961
3921
|
checked: quickLaunchStatus.value !== false,
|
|
3922
|
+
|
|
1962
3923
|
disabled,
|
|
3924
|
+
|
|
1963
3925
|
onChange: (e) => edit('showQuickLaunchButton', e.target.checked),
|
|
3926
|
+
|
|
1964
3927
|
}),
|
|
3928
|
+
|
|
1965
3929
|
t('quickLaunchLabel') || 'Quick launch goal button above composer dock',
|
|
3930
|
+
|
|
1966
3931
|
),
|
|
3932
|
+
|
|
1967
3933
|
quickLaunchStatus.isOverridden
|
|
3934
|
+
|
|
1968
3935
|
? React.createElement(
|
|
3936
|
+
|
|
1969
3937
|
'button',
|
|
3938
|
+
|
|
1970
3939
|
{
|
|
3940
|
+
|
|
1971
3941
|
type: 'button',
|
|
3942
|
+
|
|
1972
3943
|
className: 'dsh-goal-btn-inline-reset',
|
|
3944
|
+
|
|
1973
3945
|
title: t('resetField') || 'Reset to default',
|
|
3946
|
+
|
|
1974
3947
|
onClick: () => resetFieldToDefault('showQuickLaunchButton'),
|
|
3948
|
+
|
|
1975
3949
|
},
|
|
3950
|
+
|
|
1976
3951
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
3952
|
+
|
|
1977
3953
|
)
|
|
3954
|
+
|
|
1978
3955
|
: null,
|
|
3956
|
+
|
|
1979
3957
|
),
|
|
3958
|
+
|
|
1980
3959
|
React.createElement(
|
|
3960
|
+
|
|
1981
3961
|
'div',
|
|
3962
|
+
|
|
1982
3963
|
{ className: 'dsh-goal-card-field' },
|
|
3964
|
+
|
|
1983
3965
|
React.createElement(
|
|
3966
|
+
|
|
1984
3967
|
'div',
|
|
3968
|
+
|
|
1985
3969
|
{ className: 'dsh-goal-field-label-row' },
|
|
3970
|
+
|
|
1986
3971
|
React.createElement('label', { htmlFor: 'dsh-goal-tool-fail' }, t('toolFailLabel') || 'Tool-Failure Breaker Limit:'),
|
|
3972
|
+
|
|
1987
3973
|
React.createElement(
|
|
3974
|
+
|
|
1988
3975
|
'div',
|
|
3976
|
+
|
|
1989
3977
|
{ className: 'dsh-goal-field-meta' },
|
|
3978
|
+
|
|
1990
3979
|
toolFailStatus.isOverridden
|
|
3980
|
+
|
|
1991
3981
|
? React.createElement('span', { className: 'dsh-goal-override-tag' }, t('overridden') || 'overridden')
|
|
3982
|
+
|
|
1992
3983
|
: null,
|
|
3984
|
+
|
|
1993
3985
|
toolFailStatus.isOverridden
|
|
3986
|
+
|
|
1994
3987
|
? React.createElement(
|
|
3988
|
+
|
|
1995
3989
|
'button',
|
|
3990
|
+
|
|
1996
3991
|
{
|
|
3992
|
+
|
|
1997
3993
|
type: 'button',
|
|
3994
|
+
|
|
1998
3995
|
className: 'dsh-goal-btn-inline-reset',
|
|
3996
|
+
|
|
1999
3997
|
title: t('resetField') || 'Reset to default',
|
|
3998
|
+
|
|
2000
3999
|
onClick: () => edit('consecutiveToolFailureLimit', snap?.base?.consecutiveToolFailureLimit ?? DEFAULT_SETTINGS.consecutiveToolFailureLimit),
|
|
4000
|
+
|
|
2001
4001
|
},
|
|
4002
|
+
|
|
2002
4003
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
4004
|
+
|
|
2003
4005
|
t('resetField') || 'Default',
|
|
4006
|
+
|
|
2004
4007
|
)
|
|
4008
|
+
|
|
2005
4009
|
: null,
|
|
4010
|
+
|
|
2006
4011
|
),
|
|
4012
|
+
|
|
2007
4013
|
),
|
|
4014
|
+
|
|
2008
4015
|
React.createElement('input', {
|
|
4016
|
+
|
|
2009
4017
|
id: 'dsh-goal-tool-fail',
|
|
4018
|
+
|
|
2010
4019
|
type: 'number',
|
|
4020
|
+
|
|
2011
4021
|
min: 0,
|
|
4022
|
+
|
|
2012
4023
|
placeholder: String(toolFailStatus.baseValue),
|
|
4024
|
+
|
|
2013
4025
|
value: toolFailStatus.value !== undefined ? String(toolFailStatus.value) : '',
|
|
4026
|
+
|
|
2014
4027
|
disabled,
|
|
4028
|
+
|
|
2015
4029
|
className: 'dsh-goal-card-input',
|
|
4030
|
+
|
|
2016
4031
|
onChange: (e) => edit('consecutiveToolFailureLimit', parseNumberField(e.target.value)),
|
|
4032
|
+
|
|
2017
4033
|
}),
|
|
4034
|
+
|
|
2018
4035
|
React.createElement(
|
|
4036
|
+
|
|
2019
4037
|
'p',
|
|
4038
|
+
|
|
2020
4039
|
{ className: 'dsh-goal-field-hint' },
|
|
4040
|
+
|
|
2021
4041
|
t('toolFailHint') || 'Auto-pause goal if N consecutive turns hit tool execution errors (0 to disable)',
|
|
4042
|
+
|
|
2022
4043
|
),
|
|
4044
|
+
|
|
2023
4045
|
),
|
|
4046
|
+
|
|
2024
4047
|
React.createElement(
|
|
4048
|
+
|
|
2025
4049
|
'div',
|
|
4050
|
+
|
|
2026
4051
|
{ className: 'dsh-goal-check-row' },
|
|
4052
|
+
|
|
2027
4053
|
React.createElement(
|
|
4054
|
+
|
|
2028
4055
|
'label',
|
|
4056
|
+
|
|
2029
4057
|
{ className: 'dsh-goal-check' },
|
|
4058
|
+
|
|
2030
4059
|
React.createElement('input', {
|
|
4060
|
+
|
|
2031
4061
|
type: 'checkbox',
|
|
4062
|
+
|
|
2032
4063
|
checked: notifStatus.value !== false,
|
|
4064
|
+
|
|
2033
4065
|
disabled,
|
|
4066
|
+
|
|
2034
4067
|
onChange: (e) => {
|
|
4068
|
+
|
|
2035
4069
|
edit('enableBrowserNotifications', e.target.checked);
|
|
4070
|
+
|
|
2036
4071
|
if (e.target.checked && typeof window !== 'undefined' && 'Notification' in window && Notification.permission === 'default') {
|
|
4072
|
+
|
|
2037
4073
|
Notification.requestPermission();
|
|
4074
|
+
|
|
2038
4075
|
}
|
|
4076
|
+
|
|
2039
4077
|
},
|
|
4078
|
+
|
|
2040
4079
|
}),
|
|
4080
|
+
|
|
2041
4081
|
t('notifLabel') || 'Native browser desktop notifications on completion',
|
|
4082
|
+
|
|
2042
4083
|
),
|
|
4084
|
+
|
|
2043
4085
|
notifStatus.isOverridden
|
|
4086
|
+
|
|
2044
4087
|
? React.createElement(
|
|
4088
|
+
|
|
2045
4089
|
'button',
|
|
4090
|
+
|
|
2046
4091
|
{
|
|
4092
|
+
|
|
2047
4093
|
type: 'button',
|
|
4094
|
+
|
|
2048
4095
|
className: 'dsh-goal-btn-inline-reset',
|
|
4096
|
+
|
|
2049
4097
|
title: t('resetField') || 'Reset to default',
|
|
4098
|
+
|
|
2050
4099
|
onClick: () => resetFieldToDefault('enableBrowserNotifications'),
|
|
4100
|
+
|
|
2051
4101
|
},
|
|
4102
|
+
|
|
2052
4103
|
React.createElement(IconRotateCcw, { size: 12 }),
|
|
4104
|
+
|
|
2053
4105
|
)
|
|
4106
|
+
|
|
2054
4107
|
: null,
|
|
4108
|
+
|
|
2055
4109
|
),
|
|
4110
|
+
|
|
2056
4111
|
React.createElement(
|
|
4112
|
+
|
|
2057
4113
|
'div',
|
|
4114
|
+
|
|
2058
4115
|
{ className: 'dsh-goal-foot' },
|
|
4116
|
+
|
|
2059
4117
|
failed
|
|
4118
|
+
|
|
2060
4119
|
? React.createElement('span', { className: 'dsh-goal-foot-note' }, t('saveFailed') || 'Not saved — fix the values and try again')
|
|
4120
|
+
|
|
2061
4121
|
: null,
|
|
4122
|
+
|
|
2062
4123
|
React.createElement(
|
|
4124
|
+
|
|
2063
4125
|
'button',
|
|
4126
|
+
|
|
2064
4127
|
{
|
|
4128
|
+
|
|
2065
4129
|
className: 'dsh-goal-discard',
|
|
4130
|
+
|
|
2066
4131
|
disabled: !dirty || disabled,
|
|
4132
|
+
|
|
2067
4133
|
onClick: () => {
|
|
4134
|
+
|
|
2068
4135
|
setFailed(false);
|
|
4136
|
+
|
|
2069
4137
|
setDraft(null);
|
|
4138
|
+
|
|
2070
4139
|
},
|
|
4140
|
+
|
|
2071
4141
|
},
|
|
4142
|
+
|
|
2072
4143
|
t('discard') || 'Discard changes',
|
|
4144
|
+
|
|
2073
4145
|
),
|
|
4146
|
+
|
|
2074
4147
|
React.createElement(
|
|
4148
|
+
|
|
2075
4149
|
'button',
|
|
4150
|
+
|
|
2076
4151
|
{ className: 'dsh-goal-save', disabled: !dirty || disabled || invalid, onClick: save },
|
|
4152
|
+
|
|
2077
4153
|
saving ? (t('saving') || 'Saving…') : (t('save') || 'Save'),
|
|
4154
|
+
|
|
2078
4155
|
),
|
|
4156
|
+
|
|
2079
4157
|
),
|
|
4158
|
+
|
|
2080
4159
|
),
|
|
4160
|
+
|
|
2081
4161
|
)
|
|
4162
|
+
|
|
2082
4163
|
: null,
|
|
4164
|
+
|
|
2083
4165
|
);
|
|
4166
|
+
|
|
2084
4167
|
}
|
|
2085
4168
|
|
|
4169
|
+
|
|
4170
|
+
|
|
2086
4171
|
// --- СЛОВАРИ ЛОКАЛИЗАЦИИ ---
|
|
4172
|
+
|
|
2087
4173
|
const LOCALES = {
|
|
4174
|
+
|
|
2088
4175
|
en: {
|
|
4176
|
+
|
|
2089
4177
|
goalLabel: 'Current goal',
|
|
4178
|
+
|
|
2090
4179
|
goalCompleted: 'Goal completed',
|
|
4180
|
+
|
|
2091
4181
|
clearGoal: 'Clear goal',
|
|
4182
|
+
|
|
2092
4183
|
closeBanner: 'Close goal banner',
|
|
4184
|
+
|
|
2093
4185
|
pause: 'Pause',
|
|
4186
|
+
|
|
2094
4187
|
paused: 'Paused',
|
|
4188
|
+
|
|
2095
4189
|
resume: 'Resume',
|
|
4190
|
+
|
|
2096
4191
|
details: 'Expand Goal Details',
|
|
4192
|
+
|
|
2097
4193
|
modalTitle: 'Goal Plan & Status',
|
|
4194
|
+
|
|
2098
4195
|
modalTitleCompleted: 'Goal Completed: Plan & Results',
|
|
4196
|
+
|
|
2099
4197
|
time: 'Running time',
|
|
4198
|
+
|
|
2100
4199
|
eta: 'ETA',
|
|
4200
|
+
|
|
2101
4201
|
tokens: 'Tokens',
|
|
4202
|
+
|
|
2102
4203
|
iterations: 'Iterations',
|
|
4204
|
+
|
|
2103
4205
|
progress: 'Progress',
|
|
4206
|
+
|
|
2104
4207
|
done: 'Done',
|
|
4208
|
+
|
|
2105
4209
|
milestones: 'Plan of work:',
|
|
4210
|
+
|
|
2106
4211
|
noMilestones: 'Agent is preparing the plan of work...',
|
|
4212
|
+
|
|
2107
4213
|
close: 'Close',
|
|
4214
|
+
|
|
2108
4215
|
pluginTitle: 'Goal Mode & Autonomous Loop',
|
|
4216
|
+
|
|
2109
4217
|
pluginDesc: 'Goal banner above composer dock, milestone decomposition, and auto-drive',
|
|
4218
|
+
|
|
2110
4219
|
maxIterLabel: 'Max iterations (Safety Limit):',
|
|
4220
|
+
|
|
2111
4221
|
autoDriveLabel: 'Auto-drive: keep the loop running automatically',
|
|
4222
|
+
|
|
2112
4223
|
soundLabel: 'Sound when a goal completes',
|
|
4224
|
+
|
|
2113
4225
|
quickLaunchLabel: 'Quick launch goal button above composer dock',
|
|
4226
|
+
|
|
2114
4227
|
quickLaunch: 'Start Goal',
|
|
4228
|
+
|
|
2115
4229
|
quickLaunchTitle: 'Quick Launch Goal',
|
|
4230
|
+
|
|
2116
4231
|
quickLaunchDesc: 'Define the objective for the agent in autonomous mode:',
|
|
4232
|
+
|
|
2117
4233
|
quickLaunchPlaceholder: 'e.g. Implement report export and cover with unit tests...',
|
|
4234
|
+
|
|
2118
4235
|
startGoal: 'Start Goal',
|
|
4236
|
+
|
|
2119
4237
|
copyReport: '📋 Copy Report in Markdown',
|
|
4238
|
+
|
|
2120
4239
|
reportCopied: '✅ Copied!',
|
|
4240
|
+
|
|
2121
4241
|
save: 'Save',
|
|
4242
|
+
|
|
2122
4243
|
saving: 'Saving…',
|
|
4244
|
+
|
|
2123
4245
|
discard: 'Discard changes',
|
|
4246
|
+
|
|
2124
4247
|
resetField: 'Default',
|
|
4248
|
+
|
|
2125
4249
|
overridden: 'overridden',
|
|
4250
|
+
|
|
2126
4251
|
saveFailed: 'Not saved — fix the values and try again',
|
|
4252
|
+
|
|
2127
4253
|
loading: 'Loading settings…',
|
|
4254
|
+
|
|
2128
4255
|
unavailable: 'Settings unavailable',
|
|
4256
|
+
|
|
2129
4257
|
localeCode: 'en',
|
|
4258
|
+
|
|
2130
4259
|
copyPRComment: '🐙 Copy for GitHub PR',
|
|
4260
|
+
|
|
2131
4261
|
prCommentCopied: '✅ PR Report Copied!',
|
|
4262
|
+
|
|
2132
4263
|
nudgePlaceholder: '💡 Clarify task or steer agent...',
|
|
4264
|
+
|
|
2133
4265
|
sendNudge: 'Steer',
|
|
4266
|
+
|
|
2134
4267
|
nudgeSent: '✅ Steering sent!',
|
|
4268
|
+
|
|
2135
4269
|
copyGitDiff: 'Click to copy git diff command',
|
|
4270
|
+
|
|
2136
4271
|
toolFailLabel: 'Tool-Failure Breaker Limit:',
|
|
4272
|
+
|
|
2137
4273
|
toolFailHint: 'Auto-pause goal if N consecutive turns hit tool execution errors (0 to disable)',
|
|
4274
|
+
|
|
2138
4275
|
notifLabel: 'Native browser desktop notifications on completion',
|
|
4276
|
+
|
|
2139
4277
|
},
|
|
4278
|
+
|
|
2140
4279
|
zh: {
|
|
4280
|
+
|
|
2141
4281
|
goalLabel: '当前目标',
|
|
4282
|
+
|
|
2142
4283
|
goalCompleted: '目标已完成',
|
|
4284
|
+
|
|
2143
4285
|
clearGoal: '清除目标',
|
|
4286
|
+
|
|
2144
4287
|
closeBanner: '关闭目标横幅',
|
|
4288
|
+
|
|
2145
4289
|
pause: '暂停',
|
|
4290
|
+
|
|
2146
4291
|
paused: '已暂停',
|
|
4292
|
+
|
|
2147
4293
|
resume: '恢复',
|
|
4294
|
+
|
|
2148
4295
|
details: '展开目标详情',
|
|
4296
|
+
|
|
2149
4297
|
modalTitle: '目标计划与状态',
|
|
4298
|
+
|
|
2150
4299
|
modalTitleCompleted: '目标已完成:计划与成果',
|
|
4300
|
+
|
|
2151
4301
|
time: '运行时间',
|
|
4302
|
+
|
|
2152
4303
|
eta: '预估剩余 (ETA)',
|
|
4304
|
+
|
|
2153
4305
|
tokens: 'Token 消耗',
|
|
4306
|
+
|
|
2154
4307
|
iterations: '迭代次数',
|
|
4308
|
+
|
|
2155
4309
|
progress: '进度',
|
|
4310
|
+
|
|
2156
4311
|
done: '已完成',
|
|
4312
|
+
|
|
2157
4313
|
milestones: '工作计划:',
|
|
4314
|
+
|
|
2158
4315
|
noMilestones: '智能体正在制定工作计划...',
|
|
4316
|
+
|
|
2159
4317
|
close: '关闭',
|
|
4318
|
+
|
|
2160
4319
|
pluginTitle: '目标模式与自主循环 (Goal Mode)',
|
|
4320
|
+
|
|
2161
4321
|
pluginDesc: '输入框上方常驻目标横幅、里程碑拆解与自主循环',
|
|
4322
|
+
|
|
2162
4323
|
maxIterLabel: '最大迭代次数 (Safety Limit):',
|
|
4324
|
+
|
|
2163
4325
|
autoDriveLabel: '自动执行:在各轮次间自动保持循环',
|
|
4326
|
+
|
|
2164
4327
|
soundLabel: '目标完成时播放提示音',
|
|
4328
|
+
|
|
2165
4329
|
quickLaunchLabel: '在输入框上方显示快速启动目标按钮',
|
|
4330
|
+
|
|
2166
4331
|
quickLaunch: '启动目标',
|
|
4332
|
+
|
|
2167
4333
|
quickLaunchTitle: '快速启动目标',
|
|
4334
|
+
|
|
2168
4335
|
quickLaunchDesc: '为自主模式下的智能体设定任务目标:',
|
|
4336
|
+
|
|
2169
4337
|
quickLaunchPlaceholder: '例如:实现报表导出功能并编写单元测试...',
|
|
4338
|
+
|
|
2170
4339
|
startGoal: '启动目标',
|
|
4340
|
+
|
|
2171
4341
|
copyReport: '📋 复制 Markdown 报告',
|
|
4342
|
+
|
|
2172
4343
|
reportCopied: '✅ 已复制!',
|
|
4344
|
+
|
|
2173
4345
|
save: '保存',
|
|
4346
|
+
|
|
2174
4347
|
saving: '保存中…',
|
|
4348
|
+
|
|
2175
4349
|
discard: '放弃更改',
|
|
4350
|
+
|
|
2176
4351
|
resetField: '恢复默认',
|
|
4352
|
+
|
|
2177
4353
|
overridden: '已修改',
|
|
4354
|
+
|
|
2178
4355
|
saveFailed: '保存失败 — 请检查并重试',
|
|
4356
|
+
|
|
2179
4357
|
loading: '正在加载设置…',
|
|
4358
|
+
|
|
2180
4359
|
unavailable: '设置不可用',
|
|
4360
|
+
|
|
2181
4361
|
localeCode: 'zh',
|
|
4362
|
+
|
|
2182
4363
|
copyPRComment: '🐙 复制为 GitHub PR 报告',
|
|
4364
|
+
|
|
2183
4365
|
prCommentCopied: '✅ PR 报告已复制!',
|
|
4366
|
+
|
|
2184
4367
|
nudgePlaceholder: '💡 补充要求或调整智能体方向...',
|
|
4368
|
+
|
|
2185
4369
|
sendNudge: '下达指令',
|
|
4370
|
+
|
|
2186
4371
|
nudgeSent: '✅ 指令已发送!',
|
|
4372
|
+
|
|
2187
4373
|
copyGitDiff: '点击复制 git diff 命令',
|
|
4374
|
+
|
|
2188
4375
|
toolFailLabel: '工具调用连续故障断路器阈值:',
|
|
4376
|
+
|
|
2189
4377
|
toolFailHint: '连续 N 轮遭遇工具调用错误时自动暂停(0 表示禁用)',
|
|
4378
|
+
|
|
2190
4379
|
notifLabel: '目标完成时发送浏览器桌面原生通知',
|
|
4380
|
+
|
|
2191
4381
|
},
|
|
4382
|
+
|
|
2192
4383
|
};
|
|
2193
4384
|
|
|
4385
|
+
|
|
4386
|
+
|
|
2194
4387
|
module.exports.inject = ['slots', 'locale', 'settingsScope'];
|
|
4388
|
+
|
|
2195
4389
|
module.exports.apply = function apply(ctx) {
|
|
4390
|
+
|
|
2196
4391
|
// Регистрация локализации
|
|
4392
|
+
|
|
2197
4393
|
try {
|
|
4394
|
+
|
|
2198
4395
|
ctx.locale?.register?.(NS, LOCALES);
|
|
4396
|
+
|
|
2199
4397
|
} catch (_) {}
|
|
2200
4398
|
|
|
4399
|
+
|
|
4400
|
+
|
|
2201
4401
|
// Пространство настроек плагина
|
|
4402
|
+
|
|
2202
4403
|
const settingsScope =
|
|
4404
|
+
|
|
2203
4405
|
ctx.settingsScope && typeof ctx.settingsScope.bind === 'function'
|
|
4406
|
+
|
|
2204
4407
|
? ctx.settingsScope.bind({ namespace: NS })
|
|
4408
|
+
|
|
2205
4409
|
: null;
|
|
2206
4410
|
|
|
4411
|
+
|
|
4412
|
+
|
|
2207
4413
|
const registerSlotSafe = (name, entry, comp) => {
|
|
4414
|
+
|
|
2208
4415
|
try {
|
|
4416
|
+
|
|
2209
4417
|
if (typeof ctx.slots?.inject === 'function') {
|
|
4418
|
+
|
|
2210
4419
|
ctx.slots.inject(name, () => {
|
|
4420
|
+
|
|
2211
4421
|
try {
|
|
4422
|
+
|
|
2212
4423
|
return ctx.slots.register(entry, comp);
|
|
4424
|
+
|
|
2213
4425
|
} catch (_) {}
|
|
4426
|
+
|
|
2214
4427
|
});
|
|
4428
|
+
|
|
2215
4429
|
} else if (typeof ctx.slots?.register === 'function') {
|
|
4430
|
+
|
|
2216
4431
|
ctx.slots.register(entry, comp);
|
|
4432
|
+
|
|
2217
4433
|
}
|
|
4434
|
+
|
|
2218
4435
|
} catch (_) {}
|
|
4436
|
+
|
|
2219
4437
|
};
|
|
2220
4438
|
|
|
4439
|
+
|
|
4440
|
+
|
|
2221
4441
|
// 1. Регистрация виджета цели над полем ввода (composer dock)
|
|
4442
|
+
|
|
2222
4443
|
registerSlotSafe(
|
|
4444
|
+
|
|
2223
4445
|
'conversation.input.dock',
|
|
4446
|
+
|
|
2224
4447
|
{
|
|
4448
|
+
|
|
2225
4449
|
name: 'conversation.input.dock',
|
|
4450
|
+
|
|
2226
4451
|
id: '@goodandready/dsh-goal',
|
|
4452
|
+
|
|
2227
4453
|
order: 10,
|
|
4454
|
+
|
|
2228
4455
|
locale: NS,
|
|
4456
|
+
|
|
2229
4457
|
inject: (slotProps) => ({ ctx, ...slotProps }),
|
|
4458
|
+
|
|
2230
4459
|
},
|
|
4460
|
+
|
|
2231
4461
|
GoalTopBanner,
|
|
4462
|
+
|
|
2232
4463
|
);
|
|
2233
4464
|
|
|
4465
|
+
|
|
4466
|
+
|
|
2234
4467
|
// 2. Регистрация карточки настроек
|
|
4468
|
+
|
|
2235
4469
|
registerSlotSafe(
|
|
4470
|
+
|
|
2236
4471
|
'settings.plugin.item',
|
|
4472
|
+
|
|
2237
4473
|
{
|
|
4474
|
+
|
|
2238
4475
|
name: 'settings.plugin.item',
|
|
4476
|
+
|
|
2239
4477
|
key: NS,
|
|
4478
|
+
|
|
2240
4479
|
locale: NS,
|
|
4480
|
+
|
|
2241
4481
|
inject: () => ({ ctx, scope: settingsScope }),
|
|
4482
|
+
|
|
2242
4483
|
},
|
|
4484
|
+
|
|
2243
4485
|
GoalSettingsCard,
|
|
4486
|
+
|
|
2244
4487
|
);
|
|
4488
|
+
|
|
2245
4489
|
};
|
|
2246
4490
|
|
|
4491
|
+
|
|
4492
|
+
|
|
2247
4493
|
return module.exports;
|
|
4494
|
+
|
|
2248
4495
|
},
|
|
4496
|
+
|
|
2249
4497
|
});
|
|
4498
|
+
|