@onefl666/dsh-great-plan 0.0.0-stage → 0.6.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.
@@ -0,0 +1,528 @@
1
+ window.__ModuleLoader__.load({
2
+ id: '@onefl666/dsh-great-plan',
3
+ chunk: 'client.ui.js',
4
+ factory(require) {
5
+ const React = require('react');
6
+ const ReactDOM = require('react-dom');
7
+ const h = React.createElement;
8
+ const { useEffect, useLayoutEffect, useRef, useState } = React;
9
+
10
+ const STYLE_ID = '@onefl666/dsh-great-plan/ui.css';
11
+
12
+ const CSS = `
13
+ .gp-trigger{font:inherit;color:inherit;background:none;border:0;padding:0;min-width:24px;min-height:24px;display:inline-flex;align-items:center;gap:4px;cursor:pointer;white-space:nowrap}
14
+ .gp-trigger:hover{opacity:.78}
15
+ .gp-trigger:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:3px;border-radius:4px}
16
+ .gp-trigger-chevron{opacity:.65}
17
+ .gp-backdrop{position:fixed;inset:0;z-index:1200;background:color-mix(in srgb,var(--dsw-alias-bg-base) 40%,transparent)}
18
+ .gp-dialog{position:fixed;z-index:1201;display:flex;flex-direction:column;background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-xl,16px);box-shadow:var(--dsw-elevation-panel);overflow:hidden;font-family:inherit}
19
+ .gp-dialog,.gp-dialog *{box-sizing:border-box}
20
+ .gp-dialog-header{display:flex;align-items:flex-start;gap:8px;padding:14px 12px 8px 14px;flex:none}
21
+ .gp-dialog-title-wrap{flex:1;min-width:0}
22
+ .gp-dialog-title{margin:0;font-size:15px;font-weight:500;line-height:22px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
23
+ .gp-dialog-title:focus{outline:none}
24
+ .gp-dialog-subtitle{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
25
+ .gp-dialog-body{padding:2px 8px 10px;overflow-y:auto;min-height:0;flex:1 1 auto;scrollbar-width:thin}
26
+ .gp-icon-button{width:24px;height:24px;border:0;border-radius:999px;background:none;color:var(--dsw-alias-label-tertiary);display:grid;place-items:center;padding:0;cursor:pointer;flex:none}
27
+ .gp-icon-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary)}
28
+ .gp-icon-button:disabled{color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary));cursor:default}
29
+ .gp-icon-button:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:1px}
30
+ .gp-row{display:flex;align-items:center;gap:10px;width:100%;min-height:42px;padding:7px 8px;border:0;border-radius:var(--dsw-radius-md,8px);background:none;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer}
31
+ .gp-row:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}
32
+ .gp-row:disabled{color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary));cursor:default}
33
+ .gp-row:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:-2px}
34
+ .gp-row-copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
35
+ .gp-row-title{font-size:13px;line-height:20px}
36
+ .gp-row-desc{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
37
+ .gp-row-value{margin-left:auto;min-width:0;max-width:62%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
38
+ .gp-row-chevron{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary));flex:none}
39
+ .gp-list{padding:2px 0}
40
+ .gp-group-heading{position:sticky;top:0;z-index:1;padding:9px 8px 4px;background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
41
+ .gp-list-row{display:flex;align-items:center;gap:8px;width:100%;min-height:36px;padding:6px 8px;border:0;border-radius:var(--dsw-radius-md,8px);background:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;text-align:left;cursor:pointer}
42
+ .gp-list-row:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}
43
+ .gp-list-row:disabled{color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary));cursor:default}
44
+ .gp-list-row:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:-2px}
45
+ .gp-list-check{width:16px;height:16px;flex:none;display:grid;place-items:center;color:var(--dsw-alias-brand-primary)}
46
+ .gp-list-row-title{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
47
+ .gp-list-row-desc{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
48
+ .gp-separator{height:1px;margin:6px 8px;background:var(--dsw-alias-border-l1)}
49
+ .gp-actions{display:flex;flex-direction:column;gap:7px;padding:2px 0 4px}
50
+ .gp-action{display:flex;align-items:center;gap:10px;width:100%;min-height:56px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg,12px);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer}
51
+ .gp-action:hover:not(:disabled){border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}
52
+ .gp-action:disabled{opacity:.55;cursor:default}
53
+ .gp-action:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:1px}
54
+ .gp-action-primary{background:color-mix(in srgb,var(--dsw-alias-brand-primary) 6%,var(--dsw-alias-bg-layer-1));border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 32%,var(--dsw-alias-border-l1))}
55
+ .gp-action-icon{width:30px;height:30px;flex:none;display:grid;place-items:center;border-radius:9px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}
56
+ .gp-action-primary .gp-action-icon{background:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-brand-primary)}
57
+ .gp-action-copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
58
+ .gp-action-title{font-size:13px;font-weight:500;line-height:19px}
59
+ .gp-action-desc{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
60
+ .gp-action-chevron{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary));flex:none}
61
+ .gp-note{padding:8px 8px 4px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px}
62
+ .gp-footer-note{padding:4px 8px 0;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
63
+ .gp-error{margin:4px 0;padding:7px 9px;border-radius:var(--dsw-radius-md,8px);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);font-size:11px;line-height:17px;overflow-wrap:anywhere}
64
+ .gp-warning{margin:4px 0;padding:7px 9px;border-radius:var(--dsw-radius-md,8px);color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-bg-layer-2);font-size:11px;line-height:17px;overflow-wrap:anywhere}
65
+ .gp-warning-box{margin:2px 0 8px;padding:8px 9px;border:1px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-md,8px);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font-size:11px;line-height:17px;display:flex;gap:7px;align-items:flex-start}
66
+ .gp-warning-box svg{flex:none;margin-top:2px;color:var(--dsw-alias-state-warn-primary)}
67
+ .gp-loading{display:inline-flex;align-items:center;gap:6px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
68
+ .gp-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-brand-primary);animation:gp-pulse 1.2s ease-in-out infinite;flex:none}
69
+ .gp-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;padding:5px 12px;border:1px solid transparent;border-radius:var(--dsw-radius-md,8px);font:inherit;font-size:13px;line-height:20px;cursor:pointer}
70
+ .gp-button-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base)}
71
+ .gp-button-primary:hover:not(:disabled){opacity:.88}
72
+ .gp-button-outline{border-color:var(--dsw-alias-border-l2);background:none;color:var(--dsw-alias-label-primary)}
73
+ .gp-button-outline:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}
74
+ .gp-button-ghost{background:none;color:var(--dsw-alias-label-secondary)}
75
+ .gp-button-ghost:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary)}
76
+ .gp-button:disabled{opacity:.5;cursor:default}
77
+ .gp-button:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:1px}
78
+ .gp-button-full{width:100%}
79
+ .gp-static-row{display:flex;align-items:flex-start;gap:10px;width:100%;min-height:38px;padding:7px 8px;color:var(--dsw-alias-label-primary);font:inherit;text-align:left}
80
+ .gp-static-label{font-size:13px;line-height:20px;flex:none}
81
+ .gp-static-value{margin-left:auto;min-width:0;max-width:64%;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;overflow-wrap:anywhere;text-align:right}
82
+ .gp-clear-actions{padding:4px 0 2px}
83
+ .gp-plan{margin:2px 0 8px;padding:8px 9px;border:1px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-1)}
84
+ .gp-plan-heading{margin:0 0 4px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;font-weight:500}
85
+ .gp-plan-question{margin:0;color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;overflow-wrap:anywhere}
86
+ .gp-plan-preview{margin:4px 0 0;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:17px;overflow-wrap:anywhere}
87
+ .gp-plan-toggle{align-self:flex-start;margin-top:4px}
88
+ .gp-plan-body{margin:6px 0 0;padding:8px;max-height:180px;overflow:auto;border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:11px;line-height:17px;white-space:pre-wrap;overflow-wrap:anywhere;scrollbar-width:thin}
89
+ .gp-search{width:100%;min-height:32px;margin:2px 0 6px;padding:5px 9px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px}
90
+ .gp-search:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:1px}
91
+ .gp-checks{display:flex;flex-direction:column;gap:2px;margin:2px 0 8px}
92
+ .gp-check-heading{margin:0 0 2px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;font-weight:500}
93
+ .gp-check{display:flex;align-items:flex-start;gap:8px;min-height:24px;padding:4px 2px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;cursor:pointer}
94
+ .gp-check input{width:14px;height:14px;margin:2px 0 0;flex:none;accent-color:var(--dsw-alias-brand-primary)}
95
+ .gp-check-disabled{color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary));cursor:default}
96
+ .gp-check-copy{display:flex;flex-direction:column;gap:2px;min-width:0}
97
+ .gp-check-hint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
98
+ .gp-failure{margin:2px 0 8px}
99
+ .gp-failure-actions{display:flex;flex-wrap:wrap;gap:6px;padding:2px 0 4px}
100
+ .gp-status{margin:2px 0 6px;padding:6px 9px;border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:17px;overflow-wrap:anywhere}
101
+ .gp-settings{display:flex;flex-direction:column;gap:4px;padding:4px 0 8px;width:100%}
102
+ .gp-settings-heading{color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px;font-weight:500;margin:0 0 4px}
103
+ .gp-setting-row{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:16px 0;border-bottom:.5px solid var(--dsw-alias-border-l2)}
104
+ .gp-setting-copy{min-width:0;display:flex;flex-direction:column;gap:4px}
105
+ .gp-setting-title{color:var(--dsw-alias-label-primary);font-size:14px;line-height:20px}
106
+ .gp-setting-desc{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
107
+ .gp-setting-controls{display:flex;align-items:center;gap:8px;flex:none}
108
+ .gp-setting-reset{min-height:24px;padding:2px 8px;font-size:12px}
109
+ .gp-switch{position:relative;flex:none;width:36px;height:24px;padding:0;border:0;border-radius:999px;background:var(--dsw-alias-border-l2);cursor:pointer;transition:background .15s ease}
110
+ .gp-switch-on{background:var(--dsw-alias-brand-primary)}
111
+ .gp-switch:disabled{opacity:.5;cursor:default}
112
+ .gp-switch:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));outline-offset:2px}
113
+ .gp-switch-thumb{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-bg-base);transition:transform .15s ease}
114
+ .gp-switch-on .gp-switch-thumb{transform:translateX(12px)}
115
+ @media (max-width:720px){.gp-trigger-label,.gp-trigger-chevron{display:none}.gp-trigger{padding:3px}.gp-dialog{max-height:calc(100vh - 24px)}}
116
+ @media (prefers-reduced-motion:reduce){.gp-dot{animation:none}.gp-switch,.gp-switch-thumb{transition:none}}
117
+ @keyframes gp-pulse{0%,100%{opacity:.35}50%{opacity:1}}
118
+ `;
119
+
120
+ function installStyles() {
121
+ if (typeof document === 'undefined') return () => {};
122
+ if (document.querySelector(`style[data-plugin-css="${STYLE_ID}"]`) !== null) return () => {};
123
+ const style = document.createElement('style');
124
+ style.dataset.plugin = '@onefl666/dsh-great-plan';
125
+ style.dataset.pluginCss = STYLE_ID;
126
+ style.textContent = CSS;
127
+ document.head.appendChild(style);
128
+ return () => {
129
+ style.remove();
130
+ };
131
+ }
132
+
133
+ function cx(...parts) {
134
+ return parts.filter(Boolean).join(' ');
135
+ }
136
+
137
+ function messageOf(error) {
138
+ return error instanceof Error ? error.message : String(error);
139
+ }
140
+
141
+ function useStore(store) {
142
+ return React.useSyncExternalStore(
143
+ (listener) => store.subscribe(listener),
144
+ () => store.getSnapshot(),
145
+ () => store.getSnapshot(),
146
+ );
147
+ }
148
+
149
+ function createStore(initial) {
150
+ let value = initial;
151
+ const listeners = new Set();
152
+ return {
153
+ getSnapshot: () => value,
154
+ subscribe: (listener) => {
155
+ listeners.add(listener);
156
+ return () => {
157
+ listeners.delete(listener);
158
+ };
159
+ },
160
+ set: (next) => {
161
+ value = next;
162
+ for (const listener of [...listeners]) listener();
163
+ },
164
+ };
165
+ }
166
+
167
+ function iconProps(size, className) {
168
+ return {
169
+ viewBox: '0 0 16 16',
170
+ width: size,
171
+ height: size,
172
+ fill: 'none',
173
+ stroke: 'currentColor',
174
+ strokeWidth: 1.5,
175
+ strokeLinecap: 'round',
176
+ strokeLinejoin: 'round',
177
+ 'aria-hidden': true,
178
+ className,
179
+ };
180
+ }
181
+
182
+ function IconSliders({ size = 16, className }) {
183
+ return h('svg', iconProps(size, className),
184
+ h('path', { d: 'M2 4h12M2 8h12M2 12h12' }),
185
+ h('circle', { cx: 6, cy: 4, r: 1.75, fill: 'currentColor', stroke: 'none' }),
186
+ h('circle', { cx: 10, cy: 8, r: 1.75, fill: 'currentColor', stroke: 'none' }),
187
+ h('circle', { cx: 5, cy: 12, r: 1.75, fill: 'currentColor', stroke: 'none' }),
188
+ );
189
+ }
190
+
191
+ function IconChevronRight({ size = 14, className }) {
192
+ return h('svg', iconProps(size, className), h('path', { d: 'M6 3l5 5-5 5' }));
193
+ }
194
+
195
+ function IconChevronDown({ size = 14, className }) {
196
+ return h('svg', iconProps(size, className), h('path', { d: 'M4 6l4 4 4-4' }));
197
+ }
198
+
199
+ function IconChevronLeft({ size = 14, className }) {
200
+ return h('svg', iconProps(size, className), h('path', { d: 'M10 3L5 8l5 5' }));
201
+ }
202
+
203
+ function IconClose({ size = 16, className }) {
204
+ return h('svg', iconProps(size, className), h('path', { d: 'M4 4l8 8M12 4l-8 8' }));
205
+ }
206
+
207
+ function IconCheck({ size = 14, className }) {
208
+ return h('svg', iconProps(size, className), h('path', { d: 'M3 8.5l3 3 7-7' }));
209
+ }
210
+
211
+ function IconPlay({ size = 16, className }) {
212
+ return h('svg', iconProps(size, className), h('path', { d: 'M5 3.5l7 4.5-7 4.5z', fill: 'currentColor', stroke: 'none' }));
213
+ }
214
+
215
+ function IconCompress({ size = 16, className }) {
216
+ return h('svg', iconProps(size, className),
217
+ h('path', { d: 'M2 5h12v8H2z' }),
218
+ h('path', { d: 'M2 5l1.5-2h9L14 5' }),
219
+ h('path', { d: 'M6 9h4' }),
220
+ );
221
+ }
222
+
223
+ function IconNewSession({ size = 16, className }) {
224
+ return h('svg', iconProps(size, className),
225
+ h('rect', { x: 2, y: 4, width: 8, height: 8, rx: 1.5 }),
226
+ h('path', { d: 'M12 8v5M9.5 10.5h5' }),
227
+ );
228
+ }
229
+
230
+ function IconWarning({ size = 14, className }) {
231
+ return h('svg', iconProps(size, className),
232
+ h('path', { d: 'M8 2.5l6 11H2z' }),
233
+ h('path', { d: 'M8 6.5v3.5M8 12v.2' }),
234
+ );
235
+ }
236
+
237
+ function IconButton({ label, title, onClick, disabled, autoFocus, className, children }) {
238
+ return h('button', {
239
+ type: 'button',
240
+ className: cx('gp-icon-button', className),
241
+ 'aria-label': label,
242
+ title: title ?? label,
243
+ 'data-autofocus': autoFocus ? true : undefined,
244
+ disabled,
245
+ onClick,
246
+ }, children);
247
+ }
248
+
249
+ function Button({ variant = 'ghost', full = false, className, children, ...rest }) {
250
+ return h('button', {
251
+ type: 'button',
252
+ className: cx('gp-button', `gp-button-${variant}`, full && 'gp-button-full', className),
253
+ ...rest,
254
+ }, children);
255
+ }
256
+
257
+ function ActionRow({ icon, title, description, primary = false, disabled = false, onClick }) {
258
+ return h('button', {
259
+ type: 'button',
260
+ className: cx('gp-action', primary && 'gp-action-primary'),
261
+ disabled,
262
+ onClick,
263
+ },
264
+ h('span', { className: 'gp-action-icon' }, icon),
265
+ h('span', { className: 'gp-action-copy' },
266
+ h('span', { className: 'gp-action-title' }, title),
267
+ h('span', { className: 'gp-action-desc' }, description),
268
+ ),
269
+ h(IconChevronRight, { className: 'gp-action-chevron' }),
270
+ );
271
+ }
272
+
273
+ function SelectRow({ label, value, description, onClick, disabled = false }) {
274
+ return h('button', {
275
+ type: 'button',
276
+ className: 'gp-row',
277
+ disabled,
278
+ onClick,
279
+ },
280
+ h('span', { className: 'gp-row-copy' },
281
+ h('span', { className: 'gp-row-title' }, label),
282
+ description === undefined ? null : h('span', { className: 'gp-row-desc' }, description),
283
+ ),
284
+ h('span', { className: 'gp-row-value', title: typeof value === 'string' ? value : undefined }, value),
285
+ h(IconChevronRight, { className: 'gp-row-chevron' }),
286
+ );
287
+ }
288
+
289
+ function StaticRow({ label, value }) {
290
+ return h('div', { className: 'gp-static-row' },
291
+ h('span', { className: 'gp-static-label' }, label),
292
+ h('span', { className: 'gp-static-value', title: typeof value === 'string' ? value : undefined }, value),
293
+ );
294
+ }
295
+
296
+ function ListRow({ title, description, selected = false, disabled = false, onClick, role, checked }) {
297
+ const isRadio = role === 'radio';
298
+ const isChecked = checked === undefined ? selected === true : checked === true;
299
+ return h('button', {
300
+ type: 'button',
301
+ className: 'gp-list-row',
302
+ disabled,
303
+ onClick,
304
+ role: isRadio ? 'radio' : undefined,
305
+ 'aria-checked': isRadio ? isChecked : undefined,
306
+ 'aria-pressed': isRadio ? undefined : selected,
307
+ },
308
+ h('span', { className: 'gp-list-check' }, isChecked ? h(IconCheck) : null),
309
+ h('span', { className: 'gp-list-row-title' },
310
+ h('span', null, title),
311
+ description === undefined ? null : h('span', { className: 'gp-list-row-desc' }, description),
312
+ ),
313
+ );
314
+ }
315
+
316
+ function GroupHeading({ children, role }) {
317
+ return h('div', { className: 'gp-group-heading', role }, children);
318
+ }
319
+
320
+ function Checkbox({ label, checked = false, disabled = false, onChange, hint, className }) {
321
+ return h('label', {
322
+ className: cx('gp-check', disabled && 'gp-check-disabled', className),
323
+ },
324
+ h('input', {
325
+ type: 'checkbox',
326
+ checked: checked === true,
327
+ disabled,
328
+ onChange: (event) => {
329
+ if (typeof onChange === 'function') onChange(event?.target?.checked === true);
330
+ },
331
+ }),
332
+ h('span', { className: 'gp-check-copy' },
333
+ h('span', null, label),
334
+ hint === undefined || hint === null ? null : h('span', { className: 'gp-check-hint' }, hint),
335
+ ),
336
+ );
337
+ }
338
+
339
+ function Note({ children, className }) {
340
+ return h('div', { className: cx('gp-note', className) }, children);
341
+ }
342
+
343
+ function ErrorText({ children }) {
344
+ return h('div', { className: 'gp-error', role: 'alert' }, children);
345
+ }
346
+
347
+ function WarningText({ children }) {
348
+ return h('div', { className: 'gp-warning', role: 'status' }, children);
349
+ }
350
+
351
+ function WarningBox({ children }) {
352
+ return h('div', { className: 'gp-warning-box', role: 'status' }, h(IconWarning), h('span', null, children));
353
+ }
354
+
355
+ function Loading({ children }) {
356
+ return h('span', { className: 'gp-loading' }, h('span', { className: 'gp-dot' }), children);
357
+ }
358
+
359
+ function useAnchoredPosition(open, anchorRef, panelRef, setPosition) {
360
+ useLayoutEffect(() => {
361
+ if (!open) {
362
+ setPosition(null);
363
+ return undefined;
364
+ }
365
+ let frame = 0;
366
+ const update = () => {
367
+ cancelAnimationFrame(frame);
368
+ frame = requestAnimationFrame(() => {
369
+ const anchor = anchorRef.current;
370
+ const panel = panelRef.current;
371
+ if (anchor === null || panel === null) return;
372
+ const rect = anchor.getBoundingClientRect();
373
+ const viewportWidth = window.innerWidth;
374
+ const viewportHeight = window.innerHeight;
375
+ const width = Math.max(280, Math.min(380, viewportWidth - 24));
376
+ const panelHeight = panel.offsetHeight;
377
+ let left;
378
+ let top;
379
+ if (viewportWidth <= 720) {
380
+ left = Math.max(12, (viewportWidth - width) / 2);
381
+ top = Math.max(12, (viewportHeight - panelHeight) / 2);
382
+ } else {
383
+ left = Math.min(Math.max(12, rect.right - width), viewportWidth - width - 12);
384
+ top = rect.bottom + 8;
385
+ if (top + panelHeight > viewportHeight - 12) top = Math.max(12, rect.top - panelHeight - 8);
386
+ }
387
+ setPosition({ top, left, width, maxHeight: Math.max(240, viewportHeight - 24) });
388
+ });
389
+ };
390
+ update();
391
+ const observer = typeof ResizeObserver === 'function' && panelRef.current !== null ? new ResizeObserver(update) : null;
392
+ observer?.observe(panelRef.current);
393
+ window.addEventListener('resize', update);
394
+ const onScroll = (event) => {
395
+ if (panelRef.current !== null && event.target instanceof Node && panelRef.current.contains(event.target)) return;
396
+ update();
397
+ };
398
+ window.addEventListener('scroll', onScroll, true);
399
+ return () => {
400
+ cancelAnimationFrame(frame);
401
+ observer?.disconnect();
402
+ window.removeEventListener('resize', update);
403
+ window.removeEventListener('scroll', onScroll, true);
404
+ };
405
+ }, [open, anchorRef, panelRef, setPosition]);
406
+ }
407
+
408
+ function focusableIn(panel) {
409
+ if (panel === null || panel === undefined || typeof panel.querySelectorAll !== 'function') return [];
410
+ return [...panel.querySelectorAll('button:not([disabled]),input:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])')]
411
+ .filter((node) => node instanceof HTMLElement && node.offsetParent !== null);
412
+ }
413
+
414
+ /**
415
+ * One dialog keydown handler: Escape closes only while not blocked, and a
416
+ * blocked Escape is consumed so the host never sees it; Tab stays trapped.
417
+ */
418
+ function handleDialogKeyDown(event, { blocked, onClose, panel } = {}) {
419
+ if (event.key === 'Escape') {
420
+ event.preventDefault();
421
+ event.stopPropagation();
422
+ if (blocked !== true && typeof onClose === 'function') onClose();
423
+ return;
424
+ }
425
+ if (event.key !== 'Tab') return;
426
+ const list = focusableIn(panel);
427
+ if (list.length === 0) {
428
+ event.preventDefault();
429
+ return;
430
+ }
431
+ const firstNode = list[0];
432
+ const lastNode = list[list.length - 1];
433
+ const active = document.activeElement;
434
+ if (event.shiftKey && (active === firstNode || panel?.contains(active) !== true)) {
435
+ event.preventDefault();
436
+ lastNode.focus();
437
+ } else if (!event.shiftKey && (active === lastNode || panel?.contains(active) !== true)) {
438
+ event.preventDefault();
439
+ firstNode.focus();
440
+ }
441
+ }
442
+
443
+ function useDialogFocus(open, panelRef, anchorRef, onClose, blocked) {
444
+ const blockedRef = useRef(blocked);
445
+ blockedRef.current = blocked;
446
+ const onCloseRef = useRef(onClose);
447
+ onCloseRef.current = onClose;
448
+ useEffect(() => {
449
+ if (!open) return undefined;
450
+ const panel = panelRef.current;
451
+ const previous = document.activeElement;
452
+ const first = panel?.querySelector('[data-autofocus]') ?? focusableIn(panel)[0];
453
+ first?.focus();
454
+ const onKeyDown = (event) => {
455
+ handleDialogKeyDown(event, {
456
+ blocked: blockedRef.current,
457
+ onClose: () => onCloseRef.current(),
458
+ panel,
459
+ });
460
+ };
461
+ document.addEventListener('keydown', onKeyDown, true);
462
+ return () => {
463
+ document.removeEventListener('keydown', onKeyDown, true);
464
+ const restore = anchorRef.current instanceof HTMLElement && anchorRef.current.isConnected ? anchorRef.current : previous;
465
+ if (restore instanceof HTMLElement && restore.isConnected) restore.focus();
466
+ };
467
+ }, [open, panelRef, anchorRef]);
468
+ }
469
+
470
+ function Dialog({ open, onRequestClose, blocked, busy = false, anchorRef, labelledBy, children }) {
471
+ const panelRef = useRef(null);
472
+ const [position, setPosition] = useState(null);
473
+ useAnchoredPosition(open, anchorRef, panelRef, setPosition);
474
+ useDialogFocus(open, panelRef, anchorRef, onRequestClose, blocked);
475
+ if (!open) return null;
476
+ return ReactDOM.createPortal(
477
+ h(React.Fragment, null,
478
+ h('div', {
479
+ className: 'gp-backdrop',
480
+ onMouseDown: (event) => {
481
+ if (event.target === event.currentTarget && !blocked) onRequestClose();
482
+ },
483
+ }),
484
+ h('div', {
485
+ ref: panelRef,
486
+ className: 'gp-dialog',
487
+ role: 'dialog',
488
+ 'aria-modal': 'true',
489
+ 'aria-busy': busy ? 'true' : undefined,
490
+ 'aria-labelledby': labelledBy,
491
+ style: position ?? { top: 0, left: 0, opacity: 0 },
492
+ }, children),
493
+ ),
494
+ document.body,
495
+ );
496
+ }
497
+
498
+ return {
499
+ installStyles,
500
+ cx,
501
+ messageOf,
502
+ useStore,
503
+ createStore,
504
+ IconSliders,
505
+ IconChevronDown,
506
+ IconChevronLeft,
507
+ IconClose,
508
+ IconPlay,
509
+ IconCompress,
510
+ IconNewSession,
511
+ IconButton,
512
+ Button,
513
+ ActionRow,
514
+ SelectRow,
515
+ StaticRow,
516
+ ListRow,
517
+ GroupHeading,
518
+ Checkbox,
519
+ Note,
520
+ ErrorText,
521
+ WarningText,
522
+ WarningBox,
523
+ Loading,
524
+ handleDialogKeyDown,
525
+ Dialog,
526
+ };
527
+ },
528
+ });
@@ -0,0 +1,25 @@
1
+ import { createCompactCommand } from './plan-compaction.js';
2
+ import { registerPlanEvidenceSection } from './plan-evidence.js';
3
+
4
+ export function apply(ctx, config) {
5
+ ctx.inject(['commands'], (scope) => {
6
+ return scope.commands.register(createCompactCommand({
7
+ getProjections: () => ctx.get('sessionProjections'),
8
+ getCompaction: (agent) => ctx.get('compaction'),
9
+ getAgentPresets: () => ctx.get('agentPresets'),
10
+ getBalancedAfter: async () => (await import('@deepseek-ai/dsh-compaction')).toolPairingBalancedAfter,
11
+ }));
12
+ });
13
+
14
+ ctx.inject(['systemPrompt'], (scope) => registerPlanEvidenceSection({
15
+ systemPrompt: scope.systemPrompt,
16
+ getPlanMode: () => ctx.get('planMode'),
17
+ getSessionProjections: () => ctx.get('sessionProjections'),
18
+ getConfig: () => config,
19
+ }));
20
+
21
+ // The plugin ships its own Settings page, so no schema-derived page is offered.
22
+ ctx.inject(['settings'], (scope) => {
23
+ return scope.effect(() => scope.settings.configure({ auto: false }, ctx.fiber), 'great-plan: settings presentation');
24
+ });
25
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Plugin configuration for the Settings page.
3
+ *
4
+ * All switches are volatile so the Settings page can change the plan-mode
5
+ * prompt (and the fresh-session execution prompt) without restarting the Host.
6
+ * The first two are read by the Host prompt section; the last two are read by
7
+ * the Client when it composes the fresh-session first message.
8
+ * The schema is imported from the DSH installation's own Schemastery, resolved
9
+ * through the profile runtime.
10
+ */
11
+
12
+ import z from '@deepseek-ai/schemastery';
13
+
14
+ export const Config = z.object({
15
+ injectBrainstormingPrompt: z.boolean().default(true).volatile(),
16
+ injectUnlimitedBudgetPrompt: z.boolean().default(true).volatile(),
17
+ includeOriginalUserInstructions: z.boolean().default(true).volatile(),
18
+ includeMinimalRuntimeContext: z.boolean().default(true).volatile(),
19
+ });