@goodandready/dsh-goal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,694 @@
1
+ window.__ModuleLoader__.load({
2
+ id: '@goodandready/dsh-goal',
3
+ factory: (require) => {
4
+ const module = { exports: {} };
5
+ const React = require('react');
6
+ const { useState, useEffect, useRef, useCallback } = React;
7
+
8
+ const NS = 'dsh-goal';
9
+
10
+ // Безопасная загрузка примитивов ядра
11
+ let Primitives = null;
12
+ try {
13
+ Primitives = require('@deepseek-ai/dsh-client-ui-primitives');
14
+ } catch (_) {
15
+ Primitives = null;
16
+ }
17
+
18
+ // --- SVG ИКОНКИ ---
19
+
20
+ function IconTarget({ size = 16, className = '' }) {
21
+ return React.createElement(
22
+ 'svg',
23
+ {
24
+ width: size,
25
+ height: size,
26
+ viewBox: '0 0 24 24',
27
+ fill: 'none',
28
+ stroke: 'currentColor',
29
+ strokeWidth: 2,
30
+ strokeLinecap: 'round',
31
+ strokeLinejoin: 'round',
32
+ className,
33
+ },
34
+ React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
35
+ React.createElement('circle', { cx: 12, cy: 12, r: 6 }),
36
+ React.createElement('circle', { cx: 12, cy: 12, r: 2 }),
37
+ );
38
+ }
39
+
40
+ function IconTrash({ size = 15, className = '' }) {
41
+ return React.createElement(
42
+ 'svg',
43
+ {
44
+ width: size,
45
+ height: size,
46
+ viewBox: '0 0 24 24',
47
+ fill: 'none',
48
+ stroke: 'currentColor',
49
+ strokeWidth: 1.8,
50
+ strokeLinecap: 'round',
51
+ strokeLinejoin: 'round',
52
+ className,
53
+ },
54
+ React.createElement('path', { d: 'M3 6h18' }),
55
+ React.createElement('path', { d: 'M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6' }),
56
+ React.createElement('path', { d: 'M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2' }),
57
+ );
58
+ }
59
+
60
+ function IconPause({ size = 15, className = '' }) {
61
+ return React.createElement(
62
+ 'svg',
63
+ {
64
+ width: size,
65
+ height: size,
66
+ viewBox: '0 0 24 24',
67
+ fill: 'none',
68
+ stroke: 'currentColor',
69
+ strokeWidth: 1.8,
70
+ strokeLinecap: 'round',
71
+ strokeLinejoin: 'round',
72
+ className,
73
+ },
74
+ React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
75
+ React.createElement('line', { x1: 10, y1: 15, x2: 10, y2: 9 }),
76
+ React.createElement('line', { x1: 14, y1: 15, x2: 14, y2: 9 }),
77
+ );
78
+ }
79
+
80
+ function IconPlay({ size = 15, className = '' }) {
81
+ return React.createElement(
82
+ 'svg',
83
+ {
84
+ width: size,
85
+ height: size,
86
+ viewBox: '0 0 24 24',
87
+ fill: 'none',
88
+ stroke: 'currentColor',
89
+ strokeWidth: 1.8,
90
+ strokeLinecap: 'round',
91
+ strokeLinejoin: 'round',
92
+ className,
93
+ },
94
+ React.createElement('circle', { cx: 12, cy: 12, r: 10 }),
95
+ React.createElement('polygon', { points: '10 8 16 12 10 16 10 8', fill: 'currentColor' }),
96
+ );
97
+ }
98
+
99
+ function IconExpand({ size = 15, className = '' }) {
100
+ return React.createElement(
101
+ 'svg',
102
+ {
103
+ width: size,
104
+ height: size,
105
+ viewBox: '0 0 24 24',
106
+ fill: 'none',
107
+ stroke: 'currentColor',
108
+ strokeWidth: 1.8,
109
+ strokeLinecap: 'round',
110
+ strokeLinejoin: 'round',
111
+ className,
112
+ },
113
+ React.createElement('polyline', { points: '15 3 21 3 21 9' }),
114
+ React.createElement('polyline', { points: '9 21 3 21 3 15' }),
115
+ React.createElement('line', { x1: 21, y1: 3, x2: 14, y2: 10 }),
116
+ React.createElement('line', { x1: 3, y1: 21, x2: 10, y2: 14 }),
117
+ );
118
+ }
119
+
120
+ function IconChevronDown({ size = 14, className = '' }) {
121
+ return React.createElement(
122
+ 'svg',
123
+ {
124
+ width: size,
125
+ height: size,
126
+ viewBox: '0 0 24 24',
127
+ fill: 'none',
128
+ stroke: 'currentColor',
129
+ strokeWidth: 2,
130
+ strokeLinecap: 'round',
131
+ strokeLinejoin: 'round',
132
+ className,
133
+ },
134
+ React.createElement('polyline', { points: '6 9 12 15 18 9' }),
135
+ );
136
+ }
137
+
138
+ // --- СТИЛИ И CSS ПРАВИЛА ---
139
+ const CSS_STYLES = `
140
+ .dsh-goal-banner {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ background: var(--dsw-alias-bg-layer-3, #1e1e20);
145
+ border: 1px solid var(--dsw-alias-border-l2, rgba(255,255,255,0.08));
146
+ border-radius: 10px;
147
+ padding: 8px 14px;
148
+ margin: 6px 12px;
149
+ font-family: inherit;
150
+ font-size: 13px;
151
+ color: var(--dsw-alias-label-primary, #ffffff);
152
+ user-select: none;
153
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
154
+ transition: all 0.2s ease;
155
+ }
156
+ .dsh-goal-left {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 8px;
160
+ min-width: 0;
161
+ flex: 1;
162
+ }
163
+ .dsh-goal-icon {
164
+ color: var(--dsw-alias-label-secondary, #9da2a8);
165
+ flex-shrink: 0;
166
+ display: flex;
167
+ align-items: center;
168
+ }
169
+ .dsh-goal-label {
170
+ font-weight: 600;
171
+ color: var(--dsw-alias-label-primary, #ffffff);
172
+ white-space: nowrap;
173
+ }
174
+ .dsh-goal-title {
175
+ color: var(--dsw-alias-label-secondary, #b4b8be);
176
+ overflow: hidden;
177
+ text-overflow: ellipsis;
178
+ white-space: nowrap;
179
+ font-weight: 400;
180
+ }
181
+ .dsh-goal-time {
182
+ color: var(--dsw-alias-label-tertiary, #6e737a);
183
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
184
+ font-size: 12px;
185
+ white-space: nowrap;
186
+ }
187
+ .dsh-goal-right {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 10px;
191
+ margin-left: 12px;
192
+ flex-shrink: 0;
193
+ }
194
+ .dsh-goal-btn {
195
+ background: transparent;
196
+ border: 0;
197
+ padding: 4px;
198
+ margin: 0;
199
+ color: var(--dsw-alias-label-tertiary, #888d94);
200
+ cursor: pointer;
201
+ display: flex;
202
+ align-items: center;
203
+ justify-content: center;
204
+ border-radius: 6px;
205
+ transition: all 0.15s ease;
206
+ }
207
+ .dsh-goal-btn:hover {
208
+ color: var(--dsw-alias-label-primary, #ffffff);
209
+ background: var(--dsw-alias-bg-layer-2, rgba(255,255,255,0.06));
210
+ }
211
+ .dsh-goal-btn.danger:hover {
212
+ color: var(--dsw-alias-status-danger, #ff5c5c);
213
+ }
214
+
215
+ /* Modal Details */
216
+ .dsh-goal-modal-overlay {
217
+ position: fixed;
218
+ inset: 0;
219
+ background: rgba(0, 0, 0, 0.6);
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ z-index: 10000;
224
+ backdrop-filter: blur(4px);
225
+ }
226
+ .dsh-goal-modal {
227
+ background: var(--dsw-alias-bg-layer-3, #1e1e20);
228
+ border: 1px solid var(--dsw-alias-border-l2, rgba(255,255,255,0.1));
229
+ border-radius: 12px;
230
+ width: 520px;
231
+ max-width: 90vw;
232
+ max-height: 80vh;
233
+ display: flex;
234
+ flex-direction: column;
235
+ box-shadow: 0 16px 36px rgba(0,0,0,0.4);
236
+ }
237
+ .dsh-goal-modal-head {
238
+ padding: 14px 18px;
239
+ border-bottom: 1px solid var(--dsw-alias-border-l2);
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: space-between;
243
+ }
244
+ .dsh-goal-modal-title {
245
+ font-size: 15px;
246
+ font-weight: 600;
247
+ color: var(--dsw-alias-label-primary);
248
+ }
249
+ .dsh-goal-modal-body {
250
+ padding: 16px 18px;
251
+ overflow-y: auto;
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: 12px;
255
+ }
256
+ .dsh-goal-progress-bar {
257
+ height: 6px;
258
+ background: var(--dsw-alias-bg-layer-2, #2c2d30);
259
+ border-radius: 3px;
260
+ overflow: hidden;
261
+ }
262
+ .dsh-goal-progress-fill {
263
+ height: 100%;
264
+ background: var(--dsw-alias-status-success, #22c55e);
265
+ transition: width 0.3s ease;
266
+ }
267
+ .dsh-goal-milestone-item {
268
+ display: flex;
269
+ align-items: flex-start;
270
+ gap: 10px;
271
+ padding: 8px 10px;
272
+ border-radius: 8px;
273
+ background: var(--dsw-alias-bg-layer-2);
274
+ font-size: 13px;
275
+ }
276
+ .dsh-goal-modal-foot {
277
+ padding: 12px 18px;
278
+ border-top: 1px solid var(--dsw-alias-border-l2);
279
+ display: flex;
280
+ justify-content: flex-end;
281
+ gap: 8px;
282
+ }
283
+
284
+ /* Settings Card */
285
+ .dsh-goal-card {
286
+ border: 1px solid var(--dsw-alias-border-l2);
287
+ background: var(--dsw-alias-bg-layer-3);
288
+ border-radius: 12px;
289
+ list-style: none;
290
+ margin-bottom: 10px;
291
+ }
292
+ .dsh-goal-card-head {
293
+ appearance: none;
294
+ width: 100%;
295
+ font: inherit;
296
+ color: inherit;
297
+ text-align: left;
298
+ cursor: pointer;
299
+ background: 0 0;
300
+ border: 0;
301
+ border-radius: 12px;
302
+ display: flex;
303
+ align-items: center;
304
+ gap: 12px;
305
+ padding: 14px 16px;
306
+ }
307
+ .dsh-goal-card-title {
308
+ color: var(--dsw-alias-label-primary);
309
+ font-size: 15px;
310
+ font-weight: 600;
311
+ line-height: 1.4;
312
+ }
313
+ .dsh-goal-card-sub {
314
+ color: var(--dsw-alias-label-secondary);
315
+ font-size: 13px;
316
+ }
317
+ .dsh-goal-card-body {
318
+ border-top: 1px solid var(--dsw-alias-border-l2);
319
+ margin: 0 16px;
320
+ padding-bottom: 12px;
321
+ }
322
+ .dsh-goal-card-field {
323
+ display: flex;
324
+ flex-direction: column;
325
+ gap: 6px;
326
+ padding: 12px 0;
327
+ }
328
+ .dsh-goal-card-input {
329
+ height: 34px;
330
+ border: 1px solid var(--dsw-alias-border-l2);
331
+ background: var(--dsw-alias-bg-layer-3);
332
+ color: var(--dsw-alias-label-primary);
333
+ border-radius: 8px;
334
+ padding: 0 12px;
335
+ font-size: 13px;
336
+ }
337
+ .dsh-goal-chev {
338
+ margin-left: auto;
339
+ flex: none;
340
+ color: var(--dsw-alias-label-tertiary);
341
+ transition: transform .16s;
342
+ }
343
+ .dsh-goal-chev-open {
344
+ transform: rotate(180deg);
345
+ }
346
+ `;
347
+
348
+ function injectStylesOnce() {
349
+ if (document.getElementById('dsh-goal-styles')) return;
350
+ const style = document.createElement('style');
351
+ style.id = 'dsh-goal-styles';
352
+ style.textContent = CSS_STYLES;
353
+ document.head.appendChild(style);
354
+ }
355
+
356
+ // --- МОДАЛЬНОЕ ОКНО ДЕТАЛЕЙ (MODAL DETAILS) ---
357
+ function GoalDetailsModal({ state, onClose, onAction, t }) {
358
+ if (!state) return null;
359
+
360
+ const milestones = state.milestones || [];
361
+ const completedCount = milestones.filter((m) => m.status === 'completed').length;
362
+ const totalCount = milestones.length;
363
+
364
+ return React.createElement(
365
+ 'div',
366
+ {
367
+ className: 'dsh-goal-modal-overlay',
368
+ onClick: (e) => {
369
+ if (e.target === e.currentTarget) onClose();
370
+ },
371
+ },
372
+ React.createElement(
373
+ 'div',
374
+ { className: 'dsh-goal-modal' },
375
+ React.createElement(
376
+ 'div',
377
+ { className: 'dsh-goal-modal-head' },
378
+ React.createElement(
379
+ 'div',
380
+ { style: { display: 'flex', alignItems: 'center', gap: 8 } },
381
+ React.createElement(IconTarget, { size: 18 }),
382
+ React.createElement('span', { className: 'dsh-goal-modal-title' }, t('modalTitle') || 'Цель и вехи выполнения'),
383
+ ),
384
+ React.createElement(
385
+ 'button',
386
+ { className: 'dsh-goal-btn', onClick: onClose },
387
+ '✕',
388
+ ),
389
+ ),
390
+ React.createElement(
391
+ 'div',
392
+ { className: 'dsh-goal-modal-body' },
393
+ React.createElement(
394
+ 'div',
395
+ null,
396
+ React.createElement('div', { style: { fontWeight: 600, marginBottom: 4 } }, state.title),
397
+ state.description ? React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', fontSize: 13 } }, state.description) : null,
398
+ ),
399
+ React.createElement(
400
+ 'div',
401
+ { style: { display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
402
+ React.createElement('span', null, `${t('progress') || 'Прогресс'}: ${completedCount}/${totalCount} (${state.progressPercent || 0}%)`),
403
+ React.createElement('span', null, `${t('time') || 'Время'}: ${state.formattedElapsed || '0s'}`),
404
+ ),
405
+ React.createElement(
406
+ 'div',
407
+ { className: 'dsh-goal-progress-bar' },
408
+ React.createElement('div', { className: 'dsh-goal-progress-fill', style: { width: `${state.progressPercent || 0}%` } }),
409
+ ),
410
+ React.createElement(
411
+ 'div',
412
+ { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 8 } },
413
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 13 } }, t('milestones') || 'Шаги выполнения:'),
414
+ milestones.length === 0
415
+ ? React.createElement('div', { style: { color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 } }, t('noMilestones') || 'Агент декомпозирует шаги автоматически...')
416
+ : milestones.map((m, i) =>
417
+ React.createElement(
418
+ 'div',
419
+ { key: m.id || i, className: 'dsh-goal-milestone-item' },
420
+ React.createElement('span', null, m.status === 'completed' ? '✅' : m.status === 'in_progress' ? '🔄' : '⏳'),
421
+ React.createElement(
422
+ 'div',
423
+ { style: { flex: 1 } },
424
+ React.createElement('div', { style: { textDecoration: m.status === 'completed' ? 'line-through' : 'none' } }, m.title),
425
+ m.notes ? React.createElement('div', { style: { fontSize: 11, color: 'var(--dsw-alias-label-tertiary)' } }, m.notes) : null,
426
+ ),
427
+ ),
428
+ ),
429
+ ),
430
+ ),
431
+ React.createElement(
432
+ 'div',
433
+ { className: 'dsh-goal-modal-foot' },
434
+ React.createElement(
435
+ 'button',
436
+ {
437
+ className: 'dsh-goal-btn',
438
+ style: { padding: '6px 14px', background: 'var(--dsw-alias-bg-layer-2)', color: 'var(--dsw-alias-label-primary)' },
439
+ onClick: onClose,
440
+ },
441
+ t('close') || 'Закрыть',
442
+ ),
443
+ ),
444
+ ),
445
+ );
446
+ }
447
+
448
+ // --- ГЛАВНЫЙ КОМПОНЕНТ: TOP BANNER (В ТОЧНОСТИ КАК НА СКРИНШОТЕ) ---
449
+ function GoalTopBanner(props) {
450
+ const [state, setState] = useState(null);
451
+ const [isModalOpen, setIsModalOpen] = useState(false);
452
+ const t = props.t || ((k) => k);
453
+
454
+ const fetchState = useCallback(async () => {
455
+ try {
456
+ const res = await fetch('/dsh-goal/state');
457
+ if (res.ok) {
458
+ const data = await res.json();
459
+ setState(data);
460
+ }
461
+ } catch (_) {}
462
+ }, []);
463
+
464
+ useEffect(() => {
465
+ injectStylesOnce();
466
+ fetchState();
467
+ const interval = setInterval(fetchState, 1000);
468
+ return () => clearInterval(interval);
469
+ }, [fetchState]);
470
+
471
+ const handleAction = async (action, extra = {}) => {
472
+ try {
473
+ await fetch('/dsh-goal/action', {
474
+ method: 'POST',
475
+ headers: { 'Content-Type': 'application/json' },
476
+ body: JSON.stringify({ action, ...extra }),
477
+ });
478
+ fetchState();
479
+ } catch (_) {}
480
+ };
481
+
482
+ // Все хуки объявлены выше!
483
+ if (!state || !state.hasActiveGoal) {
484
+ return null; // Если нет активной цели, плашка скрыта
485
+ }
486
+
487
+ const isPaused = state.state === 'PAUSED';
488
+
489
+ return React.createElement(
490
+ React.Fragment,
491
+ null,
492
+ React.createElement(
493
+ 'div',
494
+ { className: 'dsh-goal-banner' },
495
+ React.createElement(
496
+ 'div',
497
+ { className: 'dsh-goal-left' },
498
+ React.createElement(
499
+ 'span',
500
+ { className: 'dsh-goal-icon' },
501
+ React.createElement(IconTarget, { size: 16 }),
502
+ ),
503
+ React.createElement(
504
+ 'span',
505
+ { className: 'dsh-goal-label' },
506
+ t('goalLabel') || 'Текущая цель',
507
+ ),
508
+ React.createElement(
509
+ 'span',
510
+ { className: 'dsh-goal-title', title: state.title },
511
+ state.title,
512
+ ),
513
+ React.createElement(
514
+ 'span',
515
+ { className: 'dsh-goal-time' },
516
+ `• ${state.formattedElapsed || '0s'}`,
517
+ ),
518
+ ),
519
+ React.createElement(
520
+ 'div',
521
+ { className: 'dsh-goal-right' },
522
+ React.createElement(
523
+ 'button',
524
+ {
525
+ className: 'dsh-goal-btn danger',
526
+ title: t('clearGoal') || 'Очистить / Отменить цель',
527
+ onClick: () => handleAction('clear'),
528
+ },
529
+ React.createElement(IconTrash, { size: 15 }),
530
+ ),
531
+ React.createElement(
532
+ 'button',
533
+ {
534
+ className: 'dsh-goal-btn',
535
+ title: isPaused ? (t('resume') || 'Возобновить') : (t('pause') || 'Приостановить'),
536
+ onClick: () => handleAction(isPaused ? 'resume' : 'pause'),
537
+ },
538
+ isPaused ? React.createElement(IconPlay, { size: 15 }) : React.createElement(IconPause, { size: 15 }),
539
+ ),
540
+ React.createElement(
541
+ 'button',
542
+ {
543
+ className: 'dsh-goal-btn',
544
+ title: t('details') || 'Развернуть детали цели',
545
+ onClick: () => setIsModalOpen(true),
546
+ },
547
+ React.createElement(IconExpand, { size: 15 }),
548
+ ),
549
+ ),
550
+ ),
551
+ isModalOpen
552
+ ? React.createElement(GoalDetailsModal, {
553
+ state,
554
+ onClose: () => setIsModalOpen(false),
555
+ onAction: handleAction,
556
+ t,
557
+ })
558
+ : null,
559
+ );
560
+ }
561
+
562
+ // --- КАРТОЧКА НАСТРОЕК В SETTINGS.PLUGIN.ITEM ---
563
+ function GoalSettingsCard(props) {
564
+ const [isOpen, setIsOpen] = useState(false);
565
+ const [maxIter, setMaxIter] = useState(25);
566
+ const ctx = props.ctx;
567
+ const t = props.t || ((k) => k);
568
+
569
+ useEffect(() => {
570
+ injectStylesOnce();
571
+ }, []);
572
+
573
+ const Chevron = (Primitives && Primitives.IconChevronDownOutline14) || IconChevronDown;
574
+
575
+ return React.createElement(
576
+ 'li',
577
+ { className: 'dsh-goal-card' },
578
+ React.createElement(
579
+ 'button',
580
+ {
581
+ className: 'dsh-goal-card-head',
582
+ onClick: () => setIsOpen(!isOpen),
583
+ 'aria-expanded': isOpen,
584
+ },
585
+ React.createElement(
586
+ 'div',
587
+ { style: { flex: 1 } },
588
+ React.createElement('div', { className: 'dsh-goal-card-title' }, t('pluginTitle') || 'Цели и автономный режим (Goal Mode)'),
589
+ React.createElement('div', { className: 'dsh-goal-card-sub' }, t('pluginDesc') || 'Верхняя закреплённая панель цели, декомпозиция вех и авто-драйв'),
590
+ ),
591
+ React.createElement(Chevron, { className: `dsh-goal-chev ${isOpen ? 'dsh-goal-chev-open' : ''}` }),
592
+ ),
593
+ isOpen
594
+ ? React.createElement(
595
+ 'div',
596
+ { className: 'dsh-goal-card-body' },
597
+ React.createElement(
598
+ 'div',
599
+ { className: 'dsh-goal-card-field' },
600
+ React.createElement('label', { style: { fontSize: 13, fontWeight: 500 } }, t('maxIterLabel') || 'Максимум итераций (Safety Limit):'),
601
+ React.createElement('input', {
602
+ type: 'number',
603
+ className: 'dsh-goal-card-input',
604
+ value: maxIter,
605
+ onChange: (e) => setMaxIter(Number(e.target.value)),
606
+ }),
607
+ ),
608
+ )
609
+ : null,
610
+ );
611
+ }
612
+
613
+ // --- СЛОВАРИ ЛОКАЛИЗАЦИИ ---
614
+ const LOCALES = {
615
+ ru: {
616
+ goalLabel: 'Текущая цель',
617
+ clearGoal: 'Очистить / Отменить цель',
618
+ pause: 'Приостановить',
619
+ resume: 'Возобновить',
620
+ details: 'Развернуть детали цели',
621
+ modalTitle: 'Детали активной цели',
622
+ progress: 'Прогресс',
623
+ time: 'Время',
624
+ milestones: 'Шаги выполнения:',
625
+ noMilestones: 'Агент декомпозирует шаги автоматически...',
626
+ close: 'Закрыть',
627
+ pluginTitle: 'Цели и автономный режим (Goal Mode)',
628
+ pluginDesc: 'Верхняя закреплённая панель цели, декомпозиция вех и авто-драйв',
629
+ maxIterLabel: 'Максимум итераций (Safety Limit):',
630
+ },
631
+ en: {
632
+ goalLabel: 'Current Goal',
633
+ clearGoal: 'Clear / Cancel Goal',
634
+ pause: 'Pause',
635
+ resume: 'Resume',
636
+ details: 'Expand Goal Details',
637
+ modalTitle: 'Active Goal Details',
638
+ progress: 'Progress',
639
+ time: 'Time',
640
+ milestones: 'Milestones:',
641
+ noMilestones: 'Agent will decompose steps automatically...',
642
+ close: 'Close',
643
+ pluginTitle: 'Goal Mode & Autonomous Loop',
644
+ pluginDesc: 'Sticky top goal banner, milestone decomposition, and auto-drive',
645
+ maxIterLabel: 'Max iterations (Safety Limit):',
646
+ },
647
+ };
648
+
649
+ module.exports.inject = ['slots', 'locale'];
650
+ module.exports.apply = function apply(ctx) {
651
+ // Регистрация локализации
652
+ try {
653
+ ctx.locale?.register?.(NS, LOCALES);
654
+ } catch (_) {}
655
+
656
+ // 1. Регистрация Sticky Top Banner в слот рабочей области / сессии
657
+ ctx.slots?.register?.(
658
+ {
659
+ name: 'session.header',
660
+ id: '@goodandready/dsh-goal',
661
+ order: 10,
662
+ locale: NS,
663
+ inject: () => ({ ctx }),
664
+ },
665
+ GoalTopBanner,
666
+ );
667
+
668
+ // Фоллбек: также регистрируем в chat.banner
669
+ ctx.slots?.register?.(
670
+ {
671
+ name: 'chat.banner',
672
+ id: '@goodandready/dsh-goal:banner',
673
+ order: 5,
674
+ locale: NS,
675
+ inject: () => ({ ctx }),
676
+ },
677
+ GoalTopBanner,
678
+ );
679
+
680
+ // 2. Регистрация карточки настроек
681
+ ctx.slots?.register?.(
682
+ {
683
+ name: 'settings.plugin.item',
684
+ key: NS,
685
+ locale: NS,
686
+ inject: () => ({ ctx }),
687
+ },
688
+ GoalSettingsCard,
689
+ );
690
+ };
691
+
692
+ return module.exports;
693
+ },
694
+ });