dsh-cloudq 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,3405 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-cloudq",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ //#region src/client/index.js
7
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
+ let react = require("react");
9
+ react = __toESM(react, 1);
10
+ let react_jsx_runtime = require("react/jsx-runtime");
11
+ function __toESM(mod, isNodeMode, target) {
12
+ target = mod != null ? __create(getProtoOf(mod)) : {};
13
+ if (mod && mod.__esModule) return mod;
14
+ __copyProps(target, mod);
15
+ return target;
16
+ }
17
+ function __create(obj) {
18
+ return Object.create(obj);
19
+ }
20
+ function getProtoOf(obj) {
21
+ return obj && obj.__proto__ ? obj.__proto__ : null;
22
+ }
23
+ function __copyProps(to, from) {
24
+ for (var keys = Object.keys(from), i = 0, n = keys.length, key; i < n; i++) {
25
+ key = keys[i];
26
+ Object.defineProperty(to, key, Object.getOwnPropertyDescriptor(from, key));
27
+ }
28
+ return to;
29
+ }
30
+ const PACKAGE_ID = "dsh-cloudq";
31
+ const STYLE_ID = "dsh-cloudq-styles";
32
+ const CSS = `
33
+ .dsh-cloudq-trigger {
34
+ display: inline-flex;
35
+ height: 30px;
36
+ align-items: center;
37
+ justify-content: center;
38
+ padding: 0;
39
+ border: 0;
40
+ border-radius: 7px;
41
+ background: transparent;
42
+ color: var(--dsw-alias-label-secondary, #626a76);
43
+ cursor: pointer;
44
+ }
45
+ .dsh-cloudq-trigger--idle {
46
+ width: 30px;
47
+ }
48
+ .dsh-cloudq-trigger:hover {
49
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
50
+ color: var(--dsw-alias-state-business-primary, #315efb);
51
+ }
52
+ .dsh-cloudq-trigger:focus-visible {
53
+ outline: 2px solid var(--dsw-alias-state-business-primary, #315efb);
54
+ outline-offset: 2px;
55
+ }
56
+ .dsh-cloudq-trigger svg {
57
+ width: 18px;
58
+ height: 18px;
59
+ fill: none;
60
+ stroke: currentColor;
61
+ stroke-width: 1.6;
62
+ stroke-linecap: round;
63
+ stroke-linejoin: round;
64
+ }
65
+ .dsh-cloudq-trigger--active {
66
+ background: var(--dsw-alias-interactive-bg, #f2f5ff);
67
+ color: var(--dsw-alias-state-business-primary, #315efb);
68
+ }
69
+ .dsh-cloudq-trigger--active:hover {
70
+ background: var(--dsw-alias-interactive-bg-hover, #e8eeff);
71
+ color: var(--dsw-alias-state-business-primary, #315efb);
72
+ }
73
+ .dsh-cloudq-label {
74
+ padding: 0 8px;
75
+ font-size: 12px;
76
+ font-weight: 500;
77
+ line-height: 30px;
78
+ white-space: nowrap;
79
+ }
80
+ .dsh-cloudq-tooltip {
81
+ position: absolute;
82
+ bottom: calc(100% + 8px);
83
+ left: 50%;
84
+ transform: translateX(-50%);
85
+ z-index: 1300;
86
+ padding: 4px 8px;
87
+ border-radius: 6px;
88
+ background: var(--dsw-alias-bg-inverse, #1d2129);
89
+ color: var(--dsw-alias-label-inverse, #fff);
90
+ font-size: 12px;
91
+ line-height: 18px;
92
+ white-space: nowrap;
93
+ pointer-events: none;
94
+ }
95
+ /* ---- settings card ---- */
96
+ .dsh-cloudq-settings-card {
97
+ overflow: hidden;
98
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
99
+ border-radius: 12px;
100
+ background: var(--dsw-alias-bg-layer-3, #fff);
101
+ color: var(--dsw-alias-label-primary, #17191c);
102
+ }
103
+ .dsh-cloudq-settings-card__summary {
104
+ padding: 15px 16px;
105
+ }
106
+ .dsh-cloudq-settings-card__summary-trigger {
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: space-between;
110
+ gap: 16px;
111
+ width: 100%;
112
+ padding: 0;
113
+ border: 0;
114
+ background: transparent;
115
+ color: inherit;
116
+ cursor: pointer;
117
+ font: inherit;
118
+ text-align: left;
119
+ }
120
+ .dsh-cloudq-settings-card__summary-trigger:focus-visible {
121
+ outline: 2px solid var(--dsw-alias-state-business-primary, #315efb);
122
+ outline-offset: 3px;
123
+ border-radius: 4px;
124
+ }
125
+ .dsh-cloudq-settings-card__summary-title {
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: 4px;
129
+ min-width: 0;
130
+ }
131
+ .dsh-cloudq-settings-card__summary-title strong {
132
+ font-size: 15px;
133
+ font-weight: 650;
134
+ }
135
+ .dsh-cloudq-settings-card__summary-title small {
136
+ display: block;
137
+ font-size: 13px;
138
+ color: var(--dsw-alias-label-tertiary, #8b919c);
139
+ }
140
+ .dsh-cloudq-settings-card__summary-right {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 12px;
144
+ flex: none;
145
+ }
146
+ .dsh-cloudq-settings-card__chevron {
147
+ font-size: 18px;
148
+ transition: transform .16s ease;
149
+ color: var(--dsw-alias-label-tertiary, #8b919c);
150
+ }
151
+ .dsh-cloudq-settings-card__chevron.is-open {
152
+ transform: rotate(180deg);
153
+ }
154
+ .dsh-cloudq-settings-card__badge {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ gap: 6px;
158
+ padding: 2px 10px;
159
+ border-radius: 999px;
160
+ font-size: 12px;
161
+ line-height: 20px;
162
+ font-weight: 500;
163
+ }
164
+ .dsh-cloudq-settings-card__badge--ok {
165
+ background: var(--dsw-alias-state-success-bg, #e8f7ee);
166
+ color: var(--dsw-alias-state-success-text, #0f7b3d);
167
+ }
168
+ .dsh-cloudq-settings-card__badge--warn {
169
+ background: var(--dsw-alias-state-warning-bg, #fff4e5);
170
+ color: var(--dsw-alias-state-warning-text, #b25e09);
171
+ }
172
+ .dsh-cloudq-settings-card__badge--off {
173
+ background: var(--dsw-alias-bg-layer-2, #f2f4f8);
174
+ color: var(--dsw-alias-label-secondary, #626a76);
175
+ }
176
+ .dsh-cloudq-settings-card__badge-dot {
177
+ width: 6px;
178
+ height: 6px;
179
+ border-radius: 50%;
180
+ background: currentColor;
181
+ }
182
+ .dsh-cloudq-settings-card__body {
183
+ margin: 0 16px;
184
+ padding: 16px 0;
185
+ border-top: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
186
+ display: flex;
187
+ flex-direction: column;
188
+ gap: 12px;
189
+ }
190
+ .dsh-cloudq-settings-card__rows {
191
+ display: flex;
192
+ flex-direction: column;
193
+ gap: 6px;
194
+ font-size: 13px;
195
+ line-height: 20px;
196
+ color: var(--dsw-alias-label-secondary, #626a76);
197
+ }
198
+ .dsh-cloudq-settings-card__row {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 8px;
202
+ }
203
+ .dsh-cloudq-settings-card__row-key {
204
+ min-width: 96px;
205
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
206
+ }
207
+ .dsh-cloudq-settings-card__row-val {
208
+ color: var(--dsw-alias-label-primary, #1d2129);
209
+ word-break: break-all;
210
+ }
211
+ .dsh-cloudq-settings-card__actions {
212
+ display: flex;
213
+ align-items: center;
214
+ gap: 8px;
215
+ flex-wrap: wrap;
216
+ }
217
+ .dsh-cloudq-settings-card__actions--stacked {
218
+ flex-direction: column-reverse;
219
+ }
220
+ .dsh-cloudq-settings-card__actions--stacked button {
221
+ width: 100%;
222
+ }
223
+ .dsh-cloudq-settings-card__btn {
224
+ display: inline-flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ height: 32px;
228
+ padding: 0 16px;
229
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
230
+ border-radius: 6px;
231
+ background: var(--dsw-alias-bg-card, #fff);
232
+ color: var(--dsw-alias-label-primary, #1d2129);
233
+ font-size: 13px;
234
+ cursor: pointer;
235
+ }
236
+ .dsh-cloudq-settings-card__btn:hover {
237
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
238
+ color: var(--dsw-alias-state-business-primary, #315efb);
239
+ }
240
+ .dsh-cloudq-settings-card__btn:disabled {
241
+ opacity: 0.5;
242
+ cursor: not-allowed;
243
+ }
244
+ .dsh-cloudq-settings-card__btn--primary {
245
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
246
+ background: var(--dsw-alias-state-business-primary, #315efb);
247
+ color: #fff;
248
+ }
249
+ .dsh-cloudq-settings-card__btn--primary:hover {
250
+ background: #2448d4;
251
+ border-color: #2448d4;
252
+ color: #fff;
253
+ }
254
+ .dsh-cloudq-settings-card__btn--danger:hover {
255
+ border-color: var(--dsw-alias-state-danger, #d54941);
256
+ color: var(--dsw-alias-state-danger, #d54941);
257
+ }
258
+ .dsh-cloudq-settings-card__input {
259
+ width: 100%;
260
+ height: 32px;
261
+ padding: 0 12px;
262
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
263
+ border-radius: 6px;
264
+ background: var(--dsw-alias-bg-card, #fff);
265
+ color: var(--dsw-alias-label-primary, #1d2129);
266
+ font-size: 13px;
267
+ outline: none;
268
+ }
269
+ .dsh-cloudq-settings-card__input:focus {
270
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
271
+ }
272
+ .dsh-cloudq-settings-card__hint {
273
+ font-size: 12px;
274
+ line-height: 18px;
275
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
276
+ }
277
+ .dsh-cloudq-settings-card__feedback {
278
+ font-size: 12px;
279
+ line-height: 18px;
280
+ }
281
+ .dsh-cloudq-settings-card__feedback--error {
282
+ color: var(--dsw-alias-state-danger, #d54941);
283
+ }
284
+ .dsh-cloudq-settings-card__feedback--success {
285
+ color: var(--dsw-alias-state-success-text, #0f7b3d);
286
+ }
287
+ /* ---- manual AK/SK form ---- */
288
+ .dsh-cloudq-settings-card__field {
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: 8px;
292
+ padding: 16px 0;
293
+ }
294
+ .dsh-cloudq-settings-card__field + .dsh-cloudq-settings-card__field {
295
+ border-top: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
296
+ }
297
+ .dsh-cloudq-settings-card__label {
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 8px;
301
+ font-size: 14px;
302
+ font-weight: 600;
303
+ color: var(--dsw-alias-label-primary, #1d2129);
304
+ }
305
+ .dsh-cloudq-settings-card__required {
306
+ display: inline-flex;
307
+ align-items: center;
308
+ height: 18px;
309
+ padding: 0 6px;
310
+ border-radius: 3px;
311
+ background: var(--dsw-alias-state-danger-bg, #fdecec);
312
+ color: var(--dsw-alias-state-danger, #d54941);
313
+ font-size: 11px;
314
+ font-weight: 500;
315
+ }
316
+ .dsh-cloudq-settings-card__form-actions {
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: flex-end;
320
+ gap: 8px;
321
+ padding-top: 16px;
322
+ border-top: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
323
+ }
324
+ /* ---- sidebar entry (replaces the former COS entry slot) ---- */
325
+ .dsh-cloudq-sidebar-btn {
326
+ flex: none;
327
+ display: flex;
328
+ align-items: center;
329
+ justify-content: center;
330
+ gap: 6px;
331
+ height: 38px;
332
+ padding: 8px 16px;
333
+ margin: 0 2px 8px;
334
+ box-sizing: border-box;
335
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
336
+ border-radius: 12px;
337
+ background: var(--dsw-alias-button-elevated-fill, #fff);
338
+ color: var(--dsw-alias-label-primary, #1d2129);
339
+ font-size: 14px;
340
+ font-weight: 500;
341
+ line-height: 22px;
342
+ cursor: pointer;
343
+ overflow: hidden;
344
+ font-family: inherit;
345
+ }
346
+ .dsh-cloudq-sidebar-btn:hover {
347
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
348
+ }
349
+ .dsh-cloudq-sidebar-btn svg {
350
+ width: 18px;
351
+ height: 18px;
352
+ fill: none;
353
+ stroke: currentColor;
354
+ stroke-width: 1.6;
355
+ stroke-linecap: round;
356
+ stroke-linejoin: round;
357
+ flex: none;
358
+ }
359
+ .dsh-cloudq-sidebar-btn__icon {
360
+ width: 18px;
361
+ height: 18px;
362
+ flex: none;
363
+ object-fit: contain;
364
+ }
365
+ .dsh-cloudq-sidebar-btn__label {
366
+ max-width: 200px;
367
+ white-space: nowrap;
368
+ overflow: hidden;
369
+ text-overflow: ellipsis;
370
+ }
371
+ /* Persisted CloudQ marker shown on rows whose session id is in the registry. */
372
+ .dsh-cloudq-session-badge {
373
+ flex: none;
374
+ display: inline-flex;
375
+ align-items: center;
376
+ height: 18px;
377
+ padding: 0 5px;
378
+ margin-right: 4px;
379
+ border-radius: 3px;
380
+ background: #e8f2ff;
381
+ color: #006eff;
382
+ font-size: 10px;
383
+ font-weight: 500;
384
+ line-height: 18px;
385
+ white-space: nowrap;
386
+ }
387
+ /* ---- plugin manager card ---- */
388
+ .dsh-cloudq-pm__rows {
389
+ display: flex;
390
+ flex-direction: column;
391
+ gap: 6px;
392
+ }
393
+ .dsh-cloudq-pm__row {
394
+ display: flex;
395
+ align-items: center;
396
+ gap: 10px;
397
+ padding: 8px 10px;
398
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
399
+ border-radius: 6px;
400
+ background: var(--dsw-alias-bg-card, #fff);
401
+ }
402
+ .dsh-cloudq-pm__row-info {
403
+ flex: 1;
404
+ min-width: 0;
405
+ display: flex;
406
+ flex-direction: column;
407
+ gap: 2px;
408
+ }
409
+ .dsh-cloudq-pm__row-name {
410
+ display: flex;
411
+ align-items: center;
412
+ gap: 6px;
413
+ font-size: 13px;
414
+ font-weight: 600;
415
+ color: var(--dsw-alias-label-primary, #1d2129);
416
+ white-space: nowrap;
417
+ overflow: hidden;
418
+ text-overflow: ellipsis;
419
+ }
420
+ .dsh-cloudq-pm__row-tag {
421
+ flex: none;
422
+ padding: 0 6px;
423
+ height: 18px;
424
+ line-height: 18px;
425
+ border-radius: 4px;
426
+ font-size: 11px;
427
+ font-weight: 500;
428
+ }
429
+ .dsh-cloudq-pm__row-tag--self {
430
+ background: var(--dsw-alias-state-business-primary, #315efb);
431
+ color: #fff;
432
+ }
433
+ .dsh-cloudq-pm__row-tag--off {
434
+ background: var(--dsw-alias-state-danger, #d54941);
435
+ color: #fff;
436
+ }
437
+ .dsh-cloudq-pm__row-bundle {
438
+ font-size: 11px;
439
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
440
+ white-space: nowrap;
441
+ overflow: hidden;
442
+ text-overflow: ellipsis;
443
+ }
444
+ .dsh-cloudq-pm__switch {
445
+ position: relative;
446
+ flex: none;
447
+ width: 34px;
448
+ height: 20px;
449
+ border-radius: 10px;
450
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
451
+ background: var(--dsw-alias-bg-sub, #f0f2f5);
452
+ cursor: pointer;
453
+ transition: background 0.15s ease;
454
+ padding: 0;
455
+ }
456
+ .dsh-cloudq-pm__switch::after {
457
+ content: '';
458
+ position: absolute;
459
+ top: 2px;
460
+ left: 2px;
461
+ width: 14px;
462
+ height: 14px;
463
+ border-radius: 50%;
464
+ background: #fff;
465
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
466
+ transition: transform 0.15s ease;
467
+ }
468
+ .dsh-cloudq-pm__switch--on {
469
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
470
+ background: var(--dsw-alias-state-business-primary, #315efb);
471
+ }
472
+ .dsh-cloudq-pm__switch--on::after {
473
+ transform: translateX(14px);
474
+ }
475
+ .dsh-cloudq-pm__switch:disabled {
476
+ opacity: 0.5;
477
+ cursor: not-allowed;
478
+ }
479
+ .dsh-cloudq-pm__state {
480
+ flex: none;
481
+ width: 44px;
482
+ text-align: right;
483
+ font-size: 12px;
484
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
485
+ }
486
+ .dsh-cloudq-pm__state--off {
487
+ color: var(--dsw-alias-state-danger, #d54941);
488
+ }
489
+ .dsh-cloudq-pm__reload {
490
+ display: flex;
491
+ align-items: center;
492
+ gap: 8px;
493
+ flex-wrap: wrap;
494
+ padding: 8px 10px;
495
+ margin-bottom: 8px;
496
+ border: 1px solid var(--dsw-alias-state-business-primary, #315efb);
497
+ border-radius: 6px;
498
+ background: rgba(49, 94, 251, 0.06);
499
+ font-size: 12px;
500
+ color: var(--dsw-alias-label-primary, #1d2129);
501
+ }
502
+ .dsh-cloudq-pm__reload-text {
503
+ flex: 1;
504
+ min-width: 0;
505
+ }
506
+ .dsh-cloudq-pm__reload-cancel,
507
+ .dsh-cloudq-pm__reload-now {
508
+ flex: none;
509
+ height: 24px;
510
+ padding: 0 10px;
511
+ font-size: 12px;
512
+ border-radius: 4px;
513
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
514
+ background: var(--dsw-alias-bg-card, #fff);
515
+ color: var(--dsw-alias-label-primary, #1d2129);
516
+ cursor: pointer;
517
+ }
518
+ .dsh-cloudq-pm__reload-now {
519
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
520
+ color: var(--dsw-alias-state-business-primary, #315efb);
521
+ }
522
+ `;
523
+ function installStyles() {
524
+ if (document.getElementById(STYLE_ID) !== null) return () => {};
525
+ const style = document.createElement("style");
526
+ style.id = STYLE_ID;
527
+ style.dataset.plugin = PACKAGE_ID;
528
+ style.textContent = CSS;
529
+ document.head.appendChild(style);
530
+ return () => {
531
+ style.remove();
532
+ };
533
+ }
534
+ const CLOUDQ_STORAGE_KEY = "dsh-cloudq.sessions";
535
+ const API_CLOUDQ_SESSIONS = "/api/dsh-cloudq/sessions";
536
+ function loadCloudqSessions() {
537
+ try {
538
+ const raw = window.localStorage.getItem(CLOUDQ_STORAGE_KEY);
539
+ const list = raw ? JSON.parse(raw) : [];
540
+ return new Set(Array.isArray(list) ? list : []);
541
+ } catch {
542
+ return /* @__PURE__ */ new Set();
543
+ }
544
+ }
545
+ function saveCloudqSessions(sessions) {
546
+ try {
547
+ window.localStorage.setItem(CLOUDQ_STORAGE_KEY, JSON.stringify(Array.from(sessions)));
548
+ } catch {}
549
+ }
550
+ let cloudqSessions = loadCloudqSessions();
551
+ function markCloudqSession(sessionId) {
552
+ if (!sessionId) return;
553
+ cloudqSessions.add(sessionId);
554
+ saveCloudqSessions(cloudqSessions);
555
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:sessions-changed"));
556
+ }
557
+ function unmarkCloudqSession(sessionId) {
558
+ if (!sessionId || !cloudqSessions.delete(sessionId)) return;
559
+ saveCloudqSessions(cloudqSessions);
560
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:sessions-changed"));
561
+ }
562
+ function isCloudqSession(sessionId) {
563
+ return !!sessionId && cloudqSessions.has(sessionId);
564
+ }
565
+ let cloudqHistorySync = null;
566
+ function reconcileCloudqSessionsFromHost() {
567
+ if (cloudqHistorySync) return cloudqHistorySync;
568
+ cloudqHistorySync = cloudqRequest(API_CLOUDQ_SESSIONS, void 0, 6e4).then((response) => {
569
+ const detected = new Set(Array.isArray(response?.sessionIds) ? response.sessionIds : []);
570
+ const snapshot = cloudqCtx?.sessions?.list?.getSnapshot?.();
571
+ for (const sessionId of Array.from(cloudqSessions)) {
572
+ const summary = snapshot?.byId?.[sessionId];
573
+ if (summary && !summary.blank && !detected.has(sessionId)) cloudqSessions.delete(sessionId);
574
+ }
575
+ for (const sessionId of detected) cloudqSessions.add(sessionId);
576
+ saveCloudqSessions(cloudqSessions);
577
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:sessions-changed"));
578
+ return detected;
579
+ }).catch(() => /* @__PURE__ */ new Set()).finally(() => {
580
+ cloudqHistorySync = null;
581
+ });
582
+ return cloudqHistorySync;
583
+ }
584
+ const CLOUDQ_DRAFT = "/cloudq ";
585
+ const cloudqDraftPrimed = /* @__PURE__ */ new Set();
586
+ function CloudQButton(props) {
587
+ const sessionId = props?.session?.sessionId;
588
+ const draft = props?.input?.draft;
589
+ const actions = props?.inputActions;
590
+ const isCloudq = isCloudqSession(sessionId);
591
+ if (sessionId && actions?.setDraft) {
592
+ if (isCloudq && !cloudqDraftPrimed.has(sessionId)) {
593
+ if (draft === "" || draft === CLOUDQ_DRAFT) {
594
+ cloudqDraftPrimed.add(sessionId);
595
+ try {
596
+ actions.setDraft(CLOUDQ_DRAFT);
597
+ } catch {}
598
+ }
599
+ } else if (!isCloudq && cloudqDraftPrimed.has(sessionId)) {
600
+ cloudqDraftPrimed.delete(sessionId);
601
+ if (draft === CLOUDQ_DRAFT) try {
602
+ actions.setDraft("");
603
+ } catch {}
604
+ }
605
+ }
606
+ if (!isCloudq) return null;
607
+ return null;
608
+ }
609
+ function CloudQBridge(props) {
610
+ const actionsRef = react.useRef(props?.inputActions);
611
+ actionsRef.current = props?.inputActions;
612
+ const sessionIdRef = react.useRef(props?.session?.sessionId);
613
+ sessionIdRef.current = props?.session?.sessionId;
614
+ const draftRef = react.useRef(props?.input?.draft);
615
+ draftRef.current = props?.input?.draft;
616
+ react.useEffect(() => {
617
+ const onDraft = (event) => {
618
+ const text = typeof event?.detail === "string" ? event.detail : "";
619
+ const actions = actionsRef.current;
620
+ if (text && actions?.setDraft) try {
621
+ actions.setDraft(text);
622
+ } catch {}
623
+ };
624
+ const onClearDraft = (event) => {
625
+ const targetId = typeof event?.detail === "string" ? event.detail : "";
626
+ if (!targetId || targetId !== sessionIdRef.current) return;
627
+ const actions = actionsRef.current;
628
+ if (actions?.setDraft && draftRef.current === CLOUDQ_DRAFT) try {
629
+ actions.setDraft("");
630
+ } catch {}
631
+ };
632
+ window.addEventListener("dsh-cloudq:draft", onDraft);
633
+ window.addEventListener("dsh-cloudq:clear-draft", onClearDraft);
634
+ return () => {
635
+ window.removeEventListener("dsh-cloudq:draft", onDraft);
636
+ window.removeEventListener("dsh-cloudq:clear-draft", onClearDraft);
637
+ };
638
+ }, []);
639
+ return null;
640
+ }
641
+ const SIDEBAR_SELECTOR = "[class*=newSession]";
642
+ const CLOUDQ_LOGO_URL = "/api/dsh-cloudq/logo.png";
643
+ let cloudqCtx = null;
644
+ let cloudqStartOperation = 0;
645
+ function startCloudqSession() {
646
+ if (!cloudqCtx?.workspaces || !cloudqCtx?.sessions?.list) return;
647
+ const operation = ++cloudqStartOperation;
648
+ const before = cloudqCtx.sessions.list.getSnapshot();
649
+ const beforeId = before?.current;
650
+ const beforeSummary = beforeId ? before?.byId?.[beforeId] : void 0;
651
+ if (beforeId && beforeSummary?.blank) {
652
+ markCloudqSession(beforeId);
653
+ cloudqCtx.workspaces.startSession();
654
+ return;
655
+ }
656
+ let unsubscribe = null;
657
+ let timer = 0;
658
+ const finish = () => {
659
+ if (unsubscribe) unsubscribe();
660
+ unsubscribe = null;
661
+ if (timer) window.clearTimeout(timer);
662
+ timer = 0;
663
+ };
664
+ const observeTarget = () => {
665
+ if (operation !== cloudqStartOperation) {
666
+ finish();
667
+ return;
668
+ }
669
+ const next = cloudqCtx.sessions.list.getSnapshot();
670
+ const nextId = next?.current;
671
+ if (!nextId || nextId === beforeId || !next?.byId?.[nextId]?.blank) return;
672
+ markCloudqSession(nextId);
673
+ finish();
674
+ };
675
+ unsubscribe = cloudqCtx.sessions.list.subscribe(observeTarget);
676
+ timer = window.setTimeout(finish, 1e4);
677
+ try {
678
+ cloudqCtx.workspaces.startSession();
679
+ observeTarget();
680
+ } catch {
681
+ finish();
682
+ }
683
+ }
684
+ function createSidebarButton() {
685
+ const button = document.createElement("button");
686
+ button.type = "button";
687
+ button.className = "dsh-cloudq-sidebar-btn";
688
+ button.setAttribute("aria-label", "进入 CloudQ 模式");
689
+ const icon = document.createElement("img");
690
+ icon.src = CLOUDQ_LOGO_URL;
691
+ icon.alt = "";
692
+ icon.className = "dsh-cloudq-sidebar-btn__icon";
693
+ const label = document.createElement("span");
694
+ label.className = "dsh-cloudq-sidebar-btn__label";
695
+ label.textContent = "CloudQ";
696
+ button.append(icon, label);
697
+ button.addEventListener("click", startCloudqSession);
698
+ return button;
699
+ }
700
+ function installSidebarButton() {
701
+ if (document.getElementById("dsh-cloudq-sidebar-entry")) return () => {};
702
+ let button = null;
703
+ let observer = null;
704
+ let disposed = false;
705
+ const place = () => {
706
+ if (disposed || document.getElementById("dsh-cloudq-sidebar-entry")) return;
707
+ const host = document.querySelector(SIDEBAR_SELECTOR);
708
+ if (!host || !host.parentElement) {
709
+ window.setTimeout(place, 500);
710
+ return;
711
+ }
712
+ button = createSidebarButton();
713
+ button.id = "dsh-cloudq-sidebar-entry";
714
+ host.insertAdjacentElement("afterend", button);
715
+ observer = new MutationObserver(() => {
716
+ if (button && !button.isConnected && !disposed) {
717
+ const freshHost = document.querySelector(SIDEBAR_SELECTOR);
718
+ if (freshHost) freshHost.insertAdjacentElement("afterend", button);
719
+ }
720
+ });
721
+ observer.observe(host.parentElement, {
722
+ childList: true,
723
+ subtree: true
724
+ });
725
+ };
726
+ place();
727
+ return () => {
728
+ disposed = true;
729
+ if (observer) observer.disconnect();
730
+ if (button) button.remove();
731
+ };
732
+ }
733
+ /** Build the exact visible row order from Host workspace membership. */
734
+ function orderedVisibleSessions(snapshot, workspaces, sortByUpdated) {
735
+ const archived = new Set(workspaces?.archivedSessionIds ?? []);
736
+ const assigned = /* @__PURE__ */ new Set();
737
+ const ordered = [];
738
+ for (const workspace of workspaces?.items ?? []) {
739
+ let items = (workspace?.sessionIds ?? []).map((id) => snapshot?.byId?.[id]).filter(Boolean);
740
+ if (sortByUpdated) items = items.sort((a, b) => b.updatedAt - a.updatedAt);
741
+ for (const summary of items) {
742
+ assigned.add(summary.id);
743
+ if (summary.origin === "subagent" || archived.has(summary.id)) continue;
744
+ if (!summary.blank || summary.id === snapshot.current) ordered.push(summary);
745
+ }
746
+ }
747
+ const ungrouped = (snapshot?.ids ?? []).filter((id) => !assigned.has(id)).map((id) => snapshot?.byId?.[id]).filter((summary) => summary && summary.origin !== "subagent" && !archived.has(summary.id)).filter((summary) => !summary.blank || summary.id === snapshot.current).sort((a, b) => b.updatedAt - a.updatedAt);
748
+ return ordered.concat(ungrouped);
749
+ }
750
+ function rowTitle(row) {
751
+ return row.querySelector("[class*=title]")?.textContent?.trim() ?? "";
752
+ }
753
+ /**
754
+ * Decorate visible rows with CloudQ markers. The workspace renderer does
755
+ * not expose session ids in DOM, so first match its exact Host-owned row
756
+ * order (manual and recent-order variants). Only if a collapsed/search
757
+ * view prevents an exact sequence match do we fall back to title matching.
758
+ */
759
+ function installSessionBadges() {
760
+ let disposed = false;
761
+ let frame = 0;
762
+ let observer = null;
763
+ const sync = () => {
764
+ frame = 0;
765
+ if (disposed) return;
766
+ const snapshot = cloudqCtx?.sessions?.list?.getSnapshot?.();
767
+ if (!snapshot) return;
768
+ const summaries = Object.values(snapshot.byId ?? {});
769
+ const byTitle = /* @__PURE__ */ new Map();
770
+ for (const summary of summaries) {
771
+ if (summary?.blank) continue;
772
+ const title = String(summary?.displayTitle ?? summary?.title ?? "");
773
+ if (!title) continue;
774
+ const bucket = byTitle.get(title) ?? [];
775
+ bucket.push(summary);
776
+ byTitle.set(title, bucket);
777
+ }
778
+ const rows = Array.from(document.querySelectorAll("[role=\"treeitem\"][class*=sessionRow]"));
779
+ const workspaces = cloudqCtx?.workspaces?.list?.getSnapshot?.();
780
+ const exactOrder = [orderedVisibleSessions(snapshot, workspaces, false), orderedVisibleSessions(snapshot, workspaces, true)].find((items) => items.length === rows.length && items.every((summary, index) => summary.displayTitle === rowTitle(rows[index])));
781
+ for (const [index, row] of rows.entries()) {
782
+ const selected = row.getAttribute("aria-selected") === "true";
783
+ const titleNode = row.querySelector("[class*=title]");
784
+ const title = rowTitle(row);
785
+ const exact = exactOrder?.[index] ?? (selected ? snapshot.byId?.[snapshot.current] : void 0);
786
+ const candidates = exact ? [exact] : byTitle.get(title) ?? [];
787
+ const states = new Set(candidates.map((summary) => isCloudqSession(summary.id)));
788
+ const cloudq = candidates.length > 0 && states.size === 1 && states.has(true);
789
+ const existing = row.querySelector("[data-cloudq-session-badge=\"true\"]");
790
+ if (!cloudq) {
791
+ existing?.remove();
792
+ row.removeAttribute("data-cloudq-session");
793
+ continue;
794
+ }
795
+ row.dataset.cloudqSession = "true";
796
+ if (existing || !titleNode) continue;
797
+ const badge = document.createElement("span");
798
+ badge.className = "dsh-cloudq-session-badge";
799
+ badge.dataset.cloudqSessionBadge = "true";
800
+ badge.title = "CloudQ 会话";
801
+ badge.textContent = "CloudQ";
802
+ titleNode.insertAdjacentElement("afterend", badge);
803
+ }
804
+ };
805
+ const schedule = () => {
806
+ if (disposed || frame !== 0) return;
807
+ frame = window.requestAnimationFrame(sync);
808
+ };
809
+ observer = new MutationObserver(schedule);
810
+ observer.observe(document.body, {
811
+ childList: true,
812
+ subtree: true
813
+ });
814
+ const unsubscribe = cloudqCtx?.sessions?.list?.subscribe?.(schedule);
815
+ window.addEventListener("dsh-cloudq:sessions-changed", schedule);
816
+ schedule();
817
+ return () => {
818
+ disposed = true;
819
+ if (frame !== 0) window.cancelAnimationFrame(frame);
820
+ observer?.disconnect();
821
+ unsubscribe?.();
822
+ window.removeEventListener("dsh-cloudq:sessions-changed", schedule);
823
+ document.querySelectorAll("[data-cloudq-session-badge=\"true\"]").forEach((node) => node.remove());
824
+ document.querySelectorAll("[data-cloudq-session=\"true\"]").forEach((node) => node.removeAttribute("data-cloudq-session"));
825
+ };
826
+ }
827
+ const CLOUDQ_HEADLINE = "用云管云就Q我";
828
+ const CLOUDQ_SUBTITLE = "云端架构管家,覆盖云上架构治理与智能运维全场景";
829
+ const CLOUDQ_QUESTIONS = [
830
+ {
831
+ label: "事前预防",
832
+ prompt: "帮我看看系统有哪些风险"
833
+ },
834
+ {
835
+ label: "事中排障",
836
+ prompt: "帮我排查一下当前业务CPU/内存/网络的使用情况,定位可能的瓶颈"
837
+ },
838
+ {
839
+ label: "事后复盘",
840
+ prompt: "帮我总结一下还有哪些系统风险需要持续关注并给出改进建议"
841
+ },
842
+ {
843
+ label: "日常运维",
844
+ prompt: "帮我梳理一下闲置资源并给出降本建议"
845
+ },
846
+ {
847
+ label: "最佳实践",
848
+ prompt: "帮我介绍高频使用场景"
849
+ }
850
+ ];
851
+ const CLOUDQ_HERO_STYLE_ID = "dsh-cloudq-hero-styles-v2";
852
+ const CLOUDQ_HERO_CSS = `
853
+ body.dsh-cloudq-mode-active [class*=headlineText],
854
+ body.dsh-cloudq-mode-active [class*=previewBadge],
855
+ body.dsh-cloudq-mode-active [class*=fish],
856
+ body.dsh-cloudq-mode-active [class*=heroWorkspaceRow] {
857
+ display: none !important;
858
+ }
859
+ /* CloudQ 空会话页只下移输入框,不影响上方标题和推荐问题。 */
860
+ body.dsh-cloudq-mode-active [class*=composerHero] [class*=root][class*=hero] {
861
+ transform: translateY(40px);
862
+ }
863
+ .dsh-cloudq-hero-title {
864
+ margin: 0;
865
+ font-size: 32px;
866
+ font-weight: 600;
867
+ line-height: 1.25;
868
+ color: var(--dsw-alias-label-primary, #1d2129);
869
+ text-align: center;
870
+ }
871
+ .dsh-cloudq-hero-subtitle {
872
+ margin: 12px 0 0;
873
+ font-size: 14px;
874
+ line-height: 22px;
875
+ color: var(--dsw-alias-label-secondary, #626a76);
876
+ text-align: center;
877
+ }
878
+ .dsh-cloudq-hero-questions {
879
+ margin: 28px auto 0;
880
+ display: flex;
881
+ flex-direction: column;
882
+ gap: 8px;
883
+ max-width: 560px;
884
+ }
885
+ .dsh-cloudq-hero-question {
886
+ display: flex;
887
+ align-items: center;
888
+ gap: 12px;
889
+ width: 100%;
890
+ padding: 12px 16px;
891
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
892
+ border-radius: 12px;
893
+ background: var(--dsw-alias-bg-card, #fff);
894
+ color: var(--dsw-alias-label-primary, #1d2129);
895
+ font: inherit;
896
+ font-size: 14px;
897
+ text-align: left;
898
+ cursor: pointer;
899
+ transition: background-color .15s ease, border-color .15s ease;
900
+ }
901
+ .dsh-cloudq-hero-question:hover {
902
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
903
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
904
+ }
905
+ .dsh-cloudq-hero-question__spark {
906
+ width: 16px;
907
+ height: 16px;
908
+ fill: var(--dsw-alias-state-business-primary, #315efb);
909
+ flex: none;
910
+ }
911
+ .dsh-cloudq-hero-question__label {
912
+ font-weight: 500;
913
+ color: var(--dsw-alias-label-primary, #1d2129);
914
+ white-space: nowrap;
915
+ }
916
+ .dsh-cloudq-hero-question__prompt {
917
+ flex: 1;
918
+ color: var(--dsw-alias-label-secondary, #626a76);
919
+ font-size: 13px;
920
+ line-height: 20px;
921
+ white-space: nowrap;
922
+ overflow: hidden;
923
+ text-overflow: ellipsis;
924
+ margin-left: 12px;
925
+ }
926
+ .dsh-cloudq-hero-question__chevron {
927
+ font-size: 18px;
928
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
929
+ flex: none;
930
+ }
931
+ /* ---- CloudQ side panel (in-flow rail that squeezes the main column) ---- */
932
+ .dsh-cloudq-hero-chip {
933
+ position: absolute;
934
+ top: 0;
935
+ right: 0;
936
+ display: inline-flex;
937
+ align-items: center;
938
+ gap: 4px;
939
+ height: 32px;
940
+ padding: 0 12px;
941
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
942
+ border-radius: 8px;
943
+ background: var(--dsw-alias-bg-card, #fff);
944
+ color: var(--dsw-alias-label-secondary, #626a76);
945
+ font-size: 13px;
946
+ cursor: pointer;
947
+ transition: background-color .15s ease, border-color .15s ease;
948
+ padding: 0 12px;
949
+ z-index: 30;
950
+ }
951
+ .dsh-cloudq-hero-chip:hover {
952
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
953
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
954
+ color: var(--dsw-alias-state-business-primary, #315efb);
955
+ }
956
+ .dsh-cloudq-hero-chip svg {
957
+ width: 14px;
958
+ height: 14px;
959
+ }
960
+ .dsh-cloudq-convo-chip {
961
+ position: fixed;
962
+ top: 20px;
963
+ right: 16px;
964
+ display: inline-flex;
965
+ align-items: center;
966
+ justify-content: center;
967
+ width: 32px;
968
+ height: 32px;
969
+ padding: 0;
970
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
971
+ border-radius: 8px;
972
+ background: var(--dsw-alias-bg-card, #fff);
973
+ color: var(--dsw-alias-label-secondary, #626a76);
974
+ cursor: pointer;
975
+ transition: background-color .15s ease, border-color .15s ease, right .2s ease;
976
+ z-index: 700;
977
+ }
978
+ .dsh-cloudq-convo-chip:hover {
979
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
980
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
981
+ color: var(--dsw-alias-state-business-primary, #315efb);
982
+ }
983
+ .dsh-cloudq-convo-chip svg {
984
+ width: 18px;
985
+ height: 18px;
986
+ }
987
+ .dsh-cloudq-convo-chip.is-open {
988
+ /* Leave a 12px gap so the chip doesn't kiss the rail's left edge. */
989
+ right: calc(var(--cloudq-rail-width, 720px) + 12px);
990
+ }
991
+ .dsh-cloudq-panel__rail {
992
+ position: fixed;
993
+ top: 0;
994
+ right: 0;
995
+ bottom: 0;
996
+ width: var(--cloudq-rail-width, 720px);
997
+ max-width: 80vw;
998
+ z-index: 800;
999
+ display: flex;
1000
+ background: var(--dsw-alias-bg-card, #fff);
1001
+ border-left: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1002
+ transform: translateX(100%);
1003
+ transition: transform .2s ease;
1004
+ }
1005
+ .dsh-cloudq-panel__rail.is-open {
1006
+ transform: translateX(0);
1007
+ }
1008
+ .dsh-cloudq-panel__rail-enter,
1009
+ .dsh-cloudq-panel__rail-leave {
1010
+ /* Force-shrink the main column while the rail is open so the layout
1011
+ behaves like a real in-flow pane rather than a floating drawer. */
1012
+ transition: padding-right .2s ease;
1013
+ }
1014
+ .dsh-cloudq-panel__rail-enter { padding-right: var(--cloudq-rail-width, 500px); }
1015
+ .dsh-cloudq-panel__rail-leave { padding-right: 0; }
1016
+ .dsh-cloudq-panel__resizer {
1017
+ flex: none;
1018
+ width: 4px;
1019
+ cursor: col-resize;
1020
+ background: transparent;
1021
+ position: relative;
1022
+ }
1023
+ .dsh-cloudq-panel__resizer::after {
1024
+ content: '';
1025
+ position: absolute;
1026
+ top: 0;
1027
+ bottom: 0;
1028
+ left: 1px;
1029
+ width: 2px;
1030
+ background: var(--dsw-alias-border-l2, #e1e4e8);
1031
+ opacity: 0;
1032
+ transition: opacity .15s ease;
1033
+ }
1034
+ .dsh-cloudq-panel__resizer:hover::after,
1035
+ .dsh-cloudq-panel__resizer.is-dragging::after {
1036
+ opacity: 1;
1037
+ background: var(--dsw-alias-state-business-primary, #315efb);
1038
+ }
1039
+ .dsh-cloudq-panel__inner {
1040
+ flex: 1;
1041
+ min-width: 0;
1042
+ display: flex;
1043
+ flex-direction: column;
1044
+ overflow: hidden;
1045
+ }
1046
+ .dsh-cloudq-panel__header {
1047
+ display: flex;
1048
+ align-items: center;
1049
+ gap: 10px;
1050
+ padding: 12px 16px;
1051
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1052
+ }
1053
+ /* ---- primary view tabs ---- */
1054
+ .dsh-cloudq-panel__tabs {
1055
+ display: flex;
1056
+ align-self: stretch;
1057
+ gap: 24px;
1058
+ }
1059
+ .dsh-cloudq-panel__tab {
1060
+ position: relative;
1061
+ display: inline-flex;
1062
+ align-items: center;
1063
+ justify-content: center;
1064
+ min-width: 44px;
1065
+ padding: 0 2px;
1066
+ border: 0;
1067
+ background: transparent;
1068
+ color: var(--dsw-alias-label-secondary, #626a76);
1069
+ font: inherit;
1070
+ font-size: 14px;
1071
+ cursor: pointer;
1072
+ outline: none;
1073
+ transition: color .15s ease;
1074
+ }
1075
+ .dsh-cloudq-panel__tab:hover {
1076
+ color: var(--dsw-alias-state-business-primary, #006eff);
1077
+ }
1078
+ .dsh-cloudq-panel__tab.is-active {
1079
+ color: var(--dsw-alias-state-business-primary, #006eff);
1080
+ font-weight: 500;
1081
+ }
1082
+ .dsh-cloudq-panel__tab.is-active::after {
1083
+ content: '';
1084
+ position: absolute;
1085
+ right: 0;
1086
+ bottom: -12px;
1087
+ left: 0;
1088
+ height: 2px;
1089
+ background: var(--dsw-alias-state-business-primary, #006eff);
1090
+ }
1091
+ .dsh-cloudq-panel__close {
1092
+ margin-left: auto;
1093
+ width: 28px;
1094
+ height: 28px;
1095
+ display: flex;
1096
+ align-items: center;
1097
+ justify-content: center;
1098
+ border: 0;
1099
+ background: transparent;
1100
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1101
+ cursor: pointer;
1102
+ border-radius: 6px;
1103
+ font-size: 18px;
1104
+ padding: 0;
1105
+ }
1106
+ .dsh-cloudq-panel__close:hover {
1107
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
1108
+ color: var(--dsw-alias-label-primary, #1d2129);
1109
+ }
1110
+ .dsh-cloudq-panel__body {
1111
+ box-sizing: border-box;
1112
+ container-type: inline-size;
1113
+ container-name: cloudq-panel-body;
1114
+ flex: 1;
1115
+ min-width: 0;
1116
+ min-height: 0;
1117
+ overflow-x: hidden;
1118
+ overflow-y: auto;
1119
+ padding: 16px;
1120
+ }
1121
+ .dsh-cloudq-panel__hint {
1122
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1123
+ font-size: 13px;
1124
+ text-align: center;
1125
+ padding: 24px 0;
1126
+ }
1127
+ .dsh-cloudq-panel__error {
1128
+ color: var(--dsw-alias-state-danger, #d54941);
1129
+ font-size: 13px;
1130
+ text-align: center;
1131
+ padding: 16px 0;
1132
+ }
1133
+ /* usage overview */
1134
+ .dsh-cloudq-usage__overview {
1135
+ display: flex;
1136
+ gap: 12px;
1137
+ margin-bottom: 16px;
1138
+ }
1139
+ .dsh-cloudq-usage__card {
1140
+ flex: 1;
1141
+ padding: 14px 16px;
1142
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1143
+ border-radius: 10px;
1144
+ background: var(--dsw-alias-bg-sub, #f7f8fa);
1145
+ }
1146
+ .dsh-cloudq-usage__card-label {
1147
+ font-size: 12px;
1148
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1149
+ margin-bottom: 6px;
1150
+ }
1151
+ .dsh-cloudq-usage__card-value {
1152
+ font-size: 22px;
1153
+ font-weight: 600;
1154
+ color: var(--dsw-alias-label-primary, #1d2129);
1155
+ }
1156
+ .dsh-cloudq-usage__card-sub {
1157
+ font-size: 12px;
1158
+ color: var(--dsw-alias-label-secondary, #626a76);
1159
+ margin-top: 4px;
1160
+ }
1161
+ .dsh-cloudq-usage__table {
1162
+ box-sizing: border-box;
1163
+ width: 100%;
1164
+ min-width: 0;
1165
+ table-layout: fixed;
1166
+ border-collapse: collapse;
1167
+ font-size: 13px;
1168
+ }
1169
+ .dsh-cloudq-usage__table th {
1170
+ box-sizing: border-box;
1171
+ text-align: left;
1172
+ padding: 8px 10px;
1173
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1174
+ font-weight: 500;
1175
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1176
+ white-space: nowrap;
1177
+ overflow: hidden;
1178
+ text-overflow: ellipsis;
1179
+ }
1180
+ .dsh-cloudq-usage__table td {
1181
+ box-sizing: border-box;
1182
+ min-width: 0;
1183
+ padding: 8px 10px;
1184
+ border-bottom: 1px solid var(--dsw-alias-border-l1, #f0f1f4);
1185
+ color: var(--dsw-alias-label-primary, #1d2129);
1186
+ vertical-align: top;
1187
+ overflow: hidden;
1188
+ }
1189
+ .dsh-cloudq-usage__table th:nth-child(1),
1190
+ .dsh-cloudq-usage__table td:nth-child(1) {
1191
+ width: 148px;
1192
+ padding-left: 0;
1193
+ white-space: nowrap;
1194
+ }
1195
+ .dsh-cloudq-usage__table th:nth-child(2),
1196
+ .dsh-cloudq-usage__table td:nth-child(2) {
1197
+ width: 80px;
1198
+ white-space: nowrap;
1199
+ text-overflow: ellipsis;
1200
+ }
1201
+ .dsh-cloudq-usage__table th:nth-child(4),
1202
+ .dsh-cloudq-usage__table td:nth-child(4) {
1203
+ width: 72px;
1204
+ padding-right: 0;
1205
+ text-align: right;
1206
+ }
1207
+ .dsh-cloudq-usage__table__message {
1208
+ display: block;
1209
+ width: 100%;
1210
+ max-width: 200px;
1211
+ white-space: nowrap;
1212
+ overflow: hidden;
1213
+ text-overflow: ellipsis;
1214
+ }
1215
+ .dsh-cloudq-usage__table__credits {
1216
+ font-weight: 600;
1217
+ color: var(--dsw-alias-state-business-primary, #315efb);
1218
+ white-space: nowrap;
1219
+ overflow: hidden;
1220
+ text-overflow: ellipsis;
1221
+ }
1222
+ .dsh-cloudq-usage__table__empty {
1223
+ text-align: center;
1224
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1225
+ padding: 24px 0;
1226
+ }
1227
+ /* inspiration view */
1228
+ .dsh-cloudq-insp__tabs {
1229
+ display: flex;
1230
+ gap: 6px;
1231
+ flex-wrap: wrap;
1232
+ margin-bottom: 14px;
1233
+ }
1234
+ .dsh-cloudq-insp__tab {
1235
+ height: 28px;
1236
+ padding: 0 12px;
1237
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1238
+ border-radius: 14px;
1239
+ background: var(--dsw-alias-bg-card, #fff);
1240
+ color: var(--dsw-alias-label-secondary, #626a76);
1241
+ font-size: 12px;
1242
+ cursor: pointer;
1243
+ transition: all .15s ease;
1244
+ }
1245
+ .dsh-cloudq-insp__tab:hover {
1246
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
1247
+ }
1248
+ .dsh-cloudq-insp__tab.is-active {
1249
+ background: var(--dsw-alias-state-business-primary, #315efb);
1250
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
1251
+ color: #fff;
1252
+ }
1253
+ .dsh-cloudq-insp__list {
1254
+ display: flex;
1255
+ flex-direction: column;
1256
+ gap: 8px;
1257
+ }
1258
+ .dsh-cloudq-insp__card {
1259
+ display: flex;
1260
+ flex-direction: column;
1261
+ gap: 4px;
1262
+ padding: 12px 14px;
1263
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1264
+ border-radius: 10px;
1265
+ background: var(--dsw-alias-bg-card, #fff);
1266
+ text-align: left;
1267
+ cursor: pointer;
1268
+ transition: border-color .15s ease, background-color .15s ease;
1269
+ font: inherit;
1270
+ color: inherit;
1271
+ }
1272
+ .dsh-cloudq-insp__card:hover {
1273
+ border-color: var(--dsw-alias-state-business-primary, #315efb);
1274
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
1275
+ }
1276
+ .dsh-cloudq-insp__card-title {
1277
+ font-size: 14px;
1278
+ font-weight: 600;
1279
+ color: var(--dsw-alias-label-primary, #1d2129);
1280
+ }
1281
+ .dsh-cloudq-insp__card-desc {
1282
+ font-size: 12px;
1283
+ line-height: 18px;
1284
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1285
+ }
1286
+ /* artifact library */
1287
+ .dsh-cloudq-artifact__toolbar {
1288
+ display: flex;
1289
+ align-items: center;
1290
+ justify-content: space-between;
1291
+ min-height: 34px;
1292
+ padding: 0 0 10px;
1293
+ color: var(--dsw-alias-label-secondary, #626a76);
1294
+ font-size: 12px;
1295
+ }
1296
+ .dsh-cloudq-artifact__refresh {
1297
+ height: 28px;
1298
+ padding: 0 12px;
1299
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
1300
+ border-radius: 2px;
1301
+ background: var(--dsw-alias-bg-card, #fff);
1302
+ color: var(--dsw-alias-state-business-primary, #006eff);
1303
+ font: inherit;
1304
+ cursor: pointer;
1305
+ }
1306
+ .dsh-cloudq-artifact__refresh:hover {
1307
+ border-color: var(--dsw-alias-state-business-primary, #006eff);
1308
+ background: var(--dsw-alias-interactive-bg-hover, #f0f2f5);
1309
+ }
1310
+ .dsh-cloudq-artifact__list {
1311
+ border-top: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1312
+ }
1313
+ .dsh-cloudq-artifact__columns {
1314
+ display: grid;
1315
+ grid-template-columns: minmax(0, 1fr) 112px 148px 52px;
1316
+ align-items: center;
1317
+ min-height: 34px;
1318
+ padding: 0 4px 0 26px;
1319
+ background: var(--dsw-alias-bg-sub, #f7f8fa);
1320
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1321
+ font-size: 12px;
1322
+ }
1323
+ .dsh-cloudq-artifact__columns span:last-child {
1324
+ text-align: right;
1325
+ }
1326
+ .dsh-cloudq-artifact__session {
1327
+ border-bottom: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1328
+ }
1329
+ .dsh-cloudq-artifact__session-head {
1330
+ box-sizing: border-box;
1331
+ display: flex;
1332
+ align-items: center;
1333
+ gap: 8px;
1334
+ width: 100%;
1335
+ min-height: 42px;
1336
+ padding: 8px 4px;
1337
+ border: 0;
1338
+ background: var(--dsw-alias-bg-sub, #f7f8fa);
1339
+ color: var(--dsw-alias-label-primary, #1d2129);
1340
+ font: inherit;
1341
+ text-align: left;
1342
+ cursor: pointer;
1343
+ }
1344
+ .dsh-cloudq-artifact__session-head:hover {
1345
+ background: var(--dsw-alias-interactive-bg-hover, #eef1f5);
1346
+ }
1347
+ .dsh-cloudq-artifact__chevron {
1348
+ flex: none;
1349
+ width: 14px;
1350
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1351
+ font-size: 11px;
1352
+ text-align: center;
1353
+ transition: transform .15s ease;
1354
+ }
1355
+ .dsh-cloudq-artifact__session.is-collapsed .dsh-cloudq-artifact__chevron {
1356
+ transform: rotate(-90deg);
1357
+ }
1358
+ .dsh-cloudq-artifact__session-title {
1359
+ min-width: 0;
1360
+ flex: 1;
1361
+ overflow: hidden;
1362
+ color: var(--dsw-alias-label-primary, #1d2129);
1363
+ font-size: 13px;
1364
+ font-weight: 600;
1365
+ white-space: nowrap;
1366
+ text-overflow: ellipsis;
1367
+ }
1368
+ .dsh-cloudq-artifact__session-meta {
1369
+ flex: none;
1370
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1371
+ font-size: 12px;
1372
+ white-space: nowrap;
1373
+ }
1374
+ .dsh-cloudq-artifact__session.is-collapsed .dsh-cloudq-artifact__files {
1375
+ display: none;
1376
+ }
1377
+ .dsh-cloudq-artifact__file {
1378
+ display: grid;
1379
+ grid-template-columns: minmax(0, 1fr) 112px 148px 52px;
1380
+ align-items: center;
1381
+ min-height: 42px;
1382
+ padding: 0 4px 0 26px;
1383
+ border-top: 1px solid var(--dsw-alias-border-l1, #f0f1f4);
1384
+ font-size: 12px;
1385
+ }
1386
+ .dsh-cloudq-artifact__file-main {
1387
+ min-width: 0;
1388
+ display: flex;
1389
+ align-items: center;
1390
+ gap: 8px;
1391
+ }
1392
+ .dsh-cloudq-artifact__file-type {
1393
+ flex: none;
1394
+ min-width: 30px;
1395
+ padding: 2px 4px;
1396
+ border-radius: 2px;
1397
+ background: #e8f2ff;
1398
+ color: #006eff;
1399
+ font-size: 10px;
1400
+ line-height: 16px;
1401
+ text-align: center;
1402
+ text-transform: uppercase;
1403
+ }
1404
+ .dsh-cloudq-artifact__file-name {
1405
+ min-width: 0;
1406
+ overflow: hidden;
1407
+ color: var(--dsw-alias-label-primary, #1d2129);
1408
+ white-space: nowrap;
1409
+ text-overflow: ellipsis;
1410
+ }
1411
+ .dsh-cloudq-artifact__owner,
1412
+ .dsh-cloudq-artifact__time {
1413
+ overflow: hidden;
1414
+ color: var(--dsw-alias-label-secondary, #626a76);
1415
+ white-space: nowrap;
1416
+ text-overflow: ellipsis;
1417
+ }
1418
+ .dsh-cloudq-artifact__download {
1419
+ justify-self: end;
1420
+ color: var(--dsw-alias-state-business-primary, #006eff);
1421
+ text-decoration: none;
1422
+ }
1423
+ .dsh-cloudq-artifact__download:hover {
1424
+ text-decoration: underline;
1425
+ }
1426
+ /* architecture library */
1427
+ .dsh-cloudq-arch__toolbar {
1428
+ position: relative;
1429
+ z-index: 3;
1430
+ display: flex;
1431
+ align-items: center;
1432
+ justify-content: space-between;
1433
+ gap: 12px;
1434
+ margin-bottom: 16px;
1435
+ }
1436
+ .dsh-cloudq-arch__picker {
1437
+ position: relative;
1438
+ display: flex;
1439
+ align-items: center;
1440
+ min-width: 0;
1441
+ gap: 8px;
1442
+ }
1443
+ .dsh-cloudq-arch__picker-label {
1444
+ flex: none;
1445
+ color: var(--dsw-alias-label-secondary, #626a76);
1446
+ font-size: 13px;
1447
+ }
1448
+ .dsh-cloudq-arch__tree-select {
1449
+ position: relative;
1450
+ width: 280px;
1451
+ max-width: calc(100vw - 180px);
1452
+ }
1453
+ .dsh-cloudq-arch__tree-trigger {
1454
+ box-sizing: border-box;
1455
+ display: flex;
1456
+ align-items: center;
1457
+ width: 100%;
1458
+ height: 34px;
1459
+ padding: 0 10px;
1460
+ border: 1px solid var(--dsw-alias-interactive-border, #cdd3da);
1461
+ border-radius: 2px;
1462
+ outline: none;
1463
+ background: var(--dsw-alias-bg-card, #fff);
1464
+ color: var(--dsw-alias-label-primary, #1d2129);
1465
+ font: inherit;
1466
+ font-size: 13px;
1467
+ text-align: left;
1468
+ cursor: pointer;
1469
+ transition: border-color .15s ease, box-shadow .15s ease;
1470
+ }
1471
+ .dsh-cloudq-arch__tree-trigger:hover {
1472
+ border-color: var(--dsw-alias-state-business-primary, #006eff);
1473
+ }
1474
+ .dsh-cloudq-arch__tree-trigger.is-open,
1475
+ .dsh-cloudq-arch__tree-trigger:focus-visible {
1476
+ border-color: var(--dsw-alias-state-business-primary, #006eff);
1477
+ box-shadow: 0 0 0 2px rgba(0, 110, 255, .12);
1478
+ }
1479
+ .dsh-cloudq-arch__tree-trigger-label {
1480
+ min-width: 0;
1481
+ flex: 1;
1482
+ overflow: hidden;
1483
+ white-space: nowrap;
1484
+ text-overflow: ellipsis;
1485
+ }
1486
+ .dsh-cloudq-arch__tree-trigger-arrow {
1487
+ flex: none;
1488
+ margin-left: 8px;
1489
+ color: var(--dsw-alias-label-tertiary, #888e99);
1490
+ font-size: 11px;
1491
+ transition: transform .15s ease;
1492
+ }
1493
+ .dsh-cloudq-arch__tree-trigger.is-open .dsh-cloudq-arch__tree-trigger-arrow {
1494
+ transform: rotate(180deg);
1495
+ }
1496
+ .dsh-cloudq-arch__tree-menu {
1497
+ position: absolute;
1498
+ top: calc(100% + 4px);
1499
+ left: 0;
1500
+ box-sizing: border-box;
1501
+ width: 100%;
1502
+ max-height: 360px;
1503
+ overflow-x: hidden;
1504
+ overflow-y: auto;
1505
+ padding: 4px 0;
1506
+ border: 1px solid var(--dsw-alias-border-l2, #dfe1e5);
1507
+ border-radius: 2px;
1508
+ background: var(--dsw-alias-bg-card, #fff);
1509
+ box-shadow: 0 4px 16px rgba(0, 0, 0, .16);
1510
+ z-index: 20;
1511
+ }
1512
+ .dsh-cloudq-arch__tree-node[hidden],
1513
+ .dsh-cloudq-arch__tree-group[hidden] {
1514
+ display: none;
1515
+ }
1516
+ .dsh-cloudq-arch__tree-row {
1517
+ box-sizing: border-box;
1518
+ display: flex;
1519
+ align-items: center;
1520
+ width: 100%;
1521
+ min-height: 34px;
1522
+ padding-right: 10px;
1523
+ color: var(--dsw-alias-label-primary, #1d2129);
1524
+ font-size: 13px;
1525
+ cursor: pointer;
1526
+ outline: none;
1527
+ user-select: none;
1528
+ }
1529
+ .dsh-cloudq-arch__tree-row:hover,
1530
+ .dsh-cloudq-arch__tree-row:focus-visible {
1531
+ background: var(--dsw-alias-interactive-bg-hover, #f0f2f5);
1532
+ }
1533
+ .dsh-cloudq-arch__tree-row.is-selected {
1534
+ background: #e8f2ff;
1535
+ color: var(--dsw-alias-state-business-primary, #006eff);
1536
+ font-weight: 500;
1537
+ }
1538
+ .dsh-cloudq-arch__tree-caret {
1539
+ flex: none;
1540
+ display: inline-flex;
1541
+ align-items: center;
1542
+ justify-content: center;
1543
+ width: 24px;
1544
+ height: 24px;
1545
+ margin-right: 2px;
1546
+ color: var(--dsw-alias-label-tertiary, #888e99);
1547
+ font-size: 13px;
1548
+ transition: transform .15s ease;
1549
+ }
1550
+ .dsh-cloudq-arch__tree-node.is-expanded > .dsh-cloudq-arch__tree-row .dsh-cloudq-arch__tree-caret {
1551
+ transform: rotate(90deg);
1552
+ }
1553
+ .dsh-cloudq-arch__tree-caret.is-leaf {
1554
+ color: transparent;
1555
+ }
1556
+ .dsh-cloudq-arch__tree-folder {
1557
+ flex: none;
1558
+ width: 15px;
1559
+ height: 11px;
1560
+ margin-right: 8px;
1561
+ border: 1px solid currentColor;
1562
+ border-radius: 1px;
1563
+ color: #7d8793;
1564
+ opacity: .8;
1565
+ }
1566
+ .dsh-cloudq-arch__tree-folder::before {
1567
+ content: '';
1568
+ display: block;
1569
+ width: 7px;
1570
+ height: 3px;
1571
+ margin-top: -4px;
1572
+ margin-left: -1px;
1573
+ border: 1px solid currentColor;
1574
+ border-bottom: 0;
1575
+ border-radius: 1px 1px 0 0;
1576
+ }
1577
+ .dsh-cloudq-arch__tree-name {
1578
+ min-width: 0;
1579
+ flex: 1;
1580
+ overflow: hidden;
1581
+ white-space: nowrap;
1582
+ text-overflow: ellipsis;
1583
+ }
1584
+ .dsh-cloudq-arch__tree-check {
1585
+ flex: none;
1586
+ margin-left: 8px;
1587
+ color: var(--dsw-alias-state-business-primary, #006eff);
1588
+ font-size: 14px;
1589
+ visibility: hidden;
1590
+ }
1591
+ .dsh-cloudq-arch__tree-row.is-selected .dsh-cloudq-arch__tree-check {
1592
+ visibility: visible;
1593
+ }
1594
+ .dsh-cloudq-arch__summary {
1595
+ flex: none;
1596
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1597
+ font-size: 12px;
1598
+ white-space: nowrap;
1599
+ }
1600
+ .dsh-cloudq-arch__grid {
1601
+ display: grid;
1602
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1603
+ gap: 16px;
1604
+ }
1605
+ @container cloudq-panel-body (min-width: 900px) {
1606
+ .dsh-cloudq-arch__grid {
1607
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1608
+ }
1609
+ }
1610
+ @container cloudq-panel-body (min-width: 1260px) {
1611
+ .dsh-cloudq-arch__grid {
1612
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1613
+ }
1614
+ }
1615
+ .dsh-cloudq-arch__grid > .dsh-cloudq-panel__hint,
1616
+ .dsh-cloudq-arch__grid > .dsh-cloudq-panel__error,
1617
+ .dsh-cloudq-arch__grid > .dsh-cloudq-usage__table__empty {
1618
+ grid-column: 1 / -1;
1619
+ }
1620
+ .dsh-cloudq-arch__card {
1621
+ box-sizing: border-box;
1622
+ position: relative;
1623
+ min-width: 0;
1624
+ height: 260px;
1625
+ overflow: hidden;
1626
+ border: 1px solid var(--dsw-alias-border-l2, #e1e4e8);
1627
+ background: #fff;
1628
+ color: inherit;
1629
+ text-decoration: none;
1630
+ transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
1631
+ }
1632
+ .dsh-cloudq-arch__card:hover {
1633
+ border-color: var(--dsw-alias-state-business-primary, #006eff);
1634
+ box-shadow: 0 4px 12px rgba(0, 0, 0, .1);
1635
+ transform: translateY(-1px);
1636
+ }
1637
+ .dsh-cloudq-arch__preview {
1638
+ position: absolute;
1639
+ inset: 0 0 48px;
1640
+ display: flex;
1641
+ align-items: center;
1642
+ justify-content: center;
1643
+ overflow: hidden;
1644
+ background-color: #fff;
1645
+ background-image:
1646
+ linear-gradient(30deg, transparent 49.5%, #edf0f3 49.5%, #edf0f3 50.5%, transparent 50.5%),
1647
+ linear-gradient(-30deg, transparent 49.5%, #edf0f3 49.5%, #edf0f3 50.5%, transparent 50.5%);
1648
+ background-size: 80px 48px;
1649
+ }
1650
+ .dsh-cloudq-arch__image {
1651
+ display: block;
1652
+ width: 100%;
1653
+ height: 100%;
1654
+ object-fit: cover;
1655
+ object-position: center;
1656
+ background: rgba(255, 255, 255, .92);
1657
+ }
1658
+ .dsh-cloudq-arch__placeholder {
1659
+ padding: 0 12px;
1660
+ color: var(--dsw-alias-label-tertiary, #9aa1ab);
1661
+ font-size: 12px;
1662
+ text-align: center;
1663
+ }
1664
+ .dsh-cloudq-arch__footer {
1665
+ position: absolute;
1666
+ right: 0;
1667
+ bottom: 0;
1668
+ left: 0;
1669
+ box-sizing: border-box;
1670
+ display: flex;
1671
+ align-items: center;
1672
+ gap: 8px;
1673
+ height: 48px;
1674
+ padding: 0 14px;
1675
+ background: rgba(31, 35, 41, .7);
1676
+ color: #fff;
1677
+ }
1678
+ .dsh-cloudq-arch__name {
1679
+ min-width: 0;
1680
+ flex: 1;
1681
+ overflow: hidden;
1682
+ font-size: 14px;
1683
+ font-weight: 600;
1684
+ white-space: nowrap;
1685
+ text-overflow: ellipsis;
1686
+ }
1687
+ .dsh-cloudq-arch__meta {
1688
+ flex: none;
1689
+ color: rgba(255, 255, 255, .78);
1690
+ font-size: 11px;
1691
+ white-space: nowrap;
1692
+ }
1693
+ @media (max-width: 760px) {
1694
+ .dsh-cloudq-artifact__file,
1695
+ .dsh-cloudq-artifact__columns {
1696
+ grid-template-columns: minmax(0, 1fr) 52px;
1697
+ }
1698
+ .dsh-cloudq-artifact__owner,
1699
+ .dsh-cloudq-artifact__time,
1700
+ .dsh-cloudq-artifact__columns span:nth-child(2),
1701
+ .dsh-cloudq-artifact__columns span:nth-child(3) {
1702
+ display: none;
1703
+ }
1704
+ }
1705
+ `;
1706
+ function installCloudqHeroStyles() {
1707
+ if (document.getElementById(CLOUDQ_HERO_STYLE_ID) !== null) return () => {};
1708
+ const style = document.createElement("style");
1709
+ style.id = CLOUDQ_HERO_STYLE_ID;
1710
+ style.dataset.plugin = PACKAGE_ID;
1711
+ style.textContent = CLOUDQ_HERO_CSS;
1712
+ document.head.appendChild(style);
1713
+ return () => {
1714
+ style.remove();
1715
+ };
1716
+ }
1717
+ function buildCloudqQuestionButton(question) {
1718
+ const button = document.createElement("button");
1719
+ button.type = "button";
1720
+ button.className = "dsh-cloudq-hero-question";
1721
+ const spark = document.createElement("span");
1722
+ spark.className = "dsh-cloudq-hero-question__spark";
1723
+ spark.setAttribute("aria-hidden", "true");
1724
+ spark.textContent = "✦";
1725
+ const label = document.createElement("span");
1726
+ label.className = "dsh-cloudq-hero-question__label";
1727
+ label.textContent = question.label;
1728
+ const prompt = document.createElement("span");
1729
+ prompt.className = "dsh-cloudq-hero-question__prompt";
1730
+ prompt.textContent = question.prompt;
1731
+ const chevron = document.createElement("span");
1732
+ chevron.className = "dsh-cloudq-hero-question__chevron";
1733
+ chevron.textContent = "›";
1734
+ button.append(spark, label, prompt, chevron);
1735
+ button.addEventListener("click", () => {
1736
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:draft", { detail: question.prompt }));
1737
+ });
1738
+ return button;
1739
+ }
1740
+ const PANEL_MIN_WIDTH = 480;
1741
+ const PANEL_DEFAULT_WIDTH = 720;
1742
+ const API_USAGE = "/api/dsh-cloudq/usage";
1743
+ const API_INSPIRATIONS = "/api/dsh-cloudq/inspirations";
1744
+ const API_ARTIFACTS = "/api/dsh-cloudq/artifacts";
1745
+ const API_ARCH_DIRECTORIES = "/api/dsh-cloudq/architecture/directories";
1746
+ const API_ARCH_LIST = "/api/dsh-cloudq/architecture/list";
1747
+ const CLOUDQ_ARCH_FOLDER_KEY = "dsh-cloudq.architecture-folder-id";
1748
+ function defaultUsageRange() {
1749
+ const end = /* @__PURE__ */ new Date();
1750
+ const start = /* @__PURE__ */ new Date(end.getTime() - 6048e5);
1751
+ const fmt = (d) => {
1752
+ const pad = (n) => String(n).padStart(2, "0");
1753
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
1754
+ };
1755
+ return {
1756
+ start: fmt(start),
1757
+ end: fmt(end)
1758
+ };
1759
+ }
1760
+ function formatCredits(value) {
1761
+ const num = typeof value === "number" ? value : Number(value);
1762
+ if (!Number.isFinite(num)) return "—";
1763
+ return num.toFixed(2);
1764
+ }
1765
+ function formatDateTime(raw) {
1766
+ if (!raw) return "—";
1767
+ return String(raw).replace("T", " ").slice(0, 19);
1768
+ }
1769
+ function formatFileSize(value) {
1770
+ const bytes = Number(value);
1771
+ if (!Number.isFinite(bytes) || bytes < 0) return "—";
1772
+ if (bytes < 1024) return `${bytes} B`;
1773
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
1774
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
1775
+ }
1776
+ function safeDownloadUrl(raw) {
1777
+ if (typeof raw !== "string" || !raw) return "";
1778
+ try {
1779
+ const url = new URL(raw);
1780
+ return url.protocol === "https:" ? url.href : "";
1781
+ } catch {
1782
+ return "";
1783
+ }
1784
+ }
1785
+ function flattenArchitectureDirectories(folders, depth = 0, result = []) {
1786
+ if (!Array.isArray(folders)) return result;
1787
+ for (const folder of folders) {
1788
+ const id = Number(folder?.Id);
1789
+ if (Number.isSafeInteger(id) && id > 0) result.push({
1790
+ id,
1791
+ name: String(folder?.Name || `目录 ${id}`),
1792
+ depth
1793
+ });
1794
+ flattenArchitectureDirectories(folder?.Children, depth + 1, result);
1795
+ }
1796
+ return result;
1797
+ }
1798
+ function loadRememberedArchitectureFolder() {
1799
+ try {
1800
+ const id = Number(window.localStorage.getItem(CLOUDQ_ARCH_FOLDER_KEY));
1801
+ return Number.isSafeInteger(id) && id > 0 ? id : null;
1802
+ } catch {
1803
+ return null;
1804
+ }
1805
+ }
1806
+ function rememberArchitectureFolder(folderId) {
1807
+ try {
1808
+ window.localStorage.setItem(CLOUDQ_ARCH_FOLDER_KEY, String(folderId));
1809
+ } catch {}
1810
+ }
1811
+ let panelEl = null;
1812
+ let panelBody = null;
1813
+ let panelView = "usage";
1814
+ let panelExpanded = false;
1815
+ let inspirationCache = null;
1816
+ let inspirationCategory = 0;
1817
+ let artifactCache = null;
1818
+ let artifactTotal = 0;
1819
+ let architectureDirectoriesCache = null;
1820
+ let architectureFirstFolderId = null;
1821
+ let architectureRequestSeq = 0;
1822
+ let architecturePickerCleanup = null;
1823
+ function renderUsageView() {
1824
+ panelBody.textContent = "";
1825
+ const range = defaultUsageRange();
1826
+ const hint = document.createElement("div");
1827
+ hint.className = "dsh-cloudq-panel__hint";
1828
+ hint.textContent = "正在加载用量…";
1829
+ panelBody.appendChild(hint);
1830
+ cloudqRequest(`${API_USAGE}?start=${encodeURIComponent(range.start)}&end=${encodeURIComponent(range.end)}`).then((data) => {
1831
+ if (panelView !== "usage" || !panelExpanded) return;
1832
+ panelBody.textContent = "";
1833
+ const overview = data.overview ?? {};
1834
+ const rows = Array.isArray(data.detail) ? data.detail : [];
1835
+ const overviewWrap = document.createElement("div");
1836
+ overviewWrap.className = "dsh-cloudq-usage__overview";
1837
+ const createUsageCard = (labelText, valueText, detailText) => {
1838
+ const card = document.createElement("div");
1839
+ card.className = "dsh-cloudq-usage__card";
1840
+ const label = document.createElement("div");
1841
+ label.className = "dsh-cloudq-usage__card-label";
1842
+ label.textContent = labelText;
1843
+ const value = document.createElement("div");
1844
+ value.className = "dsh-cloudq-usage__card-value";
1845
+ value.textContent = valueText;
1846
+ const detail = document.createElement("div");
1847
+ detail.className = "dsh-cloudq-usage__card-sub";
1848
+ detail.textContent = detailText;
1849
+ card.append(label, value, detail);
1850
+ return card;
1851
+ };
1852
+ const cardTotal = createUsageCard("剩余积分", formatCredits(overview.TotalCredits), `自 ${overview.TotalStartDate ?? "—"} 起累计`);
1853
+ const monthly = overview.MonthlyCredits;
1854
+ const cardMonthly = createUsageCard("本月积分", monthly === null || monthly === void 0 ? "—" : formatCredits(monthly), overview.MonthlyStartDate ? `起于 ${overview.MonthlyStartDate}` : "暂无月度数据");
1855
+ overviewWrap.append(cardTotal, cardMonthly);
1856
+ const table = document.createElement("table");
1857
+ table.className = "dsh-cloudq-usage__table";
1858
+ const thead = document.createElement("thead");
1859
+ const headerRow = document.createElement("tr");
1860
+ for (const heading of [
1861
+ "时间",
1862
+ "渠道",
1863
+ "内容",
1864
+ "积分"
1865
+ ]) {
1866
+ const cell = document.createElement("th");
1867
+ cell.textContent = heading;
1868
+ headerRow.appendChild(cell);
1869
+ }
1870
+ thead.appendChild(headerRow);
1871
+ table.appendChild(thead);
1872
+ const tbody = document.createElement("tbody");
1873
+ if (rows.length === 0) {
1874
+ const tr = document.createElement("tr");
1875
+ const empty = document.createElement("td");
1876
+ empty.colSpan = 4;
1877
+ empty.className = "dsh-cloudq-usage__table__empty";
1878
+ empty.textContent = "近 7 天暂无用量记录";
1879
+ tr.appendChild(empty);
1880
+ tbody.appendChild(tr);
1881
+ } else for (const row of rows) {
1882
+ const tr = document.createElement("tr");
1883
+ const time = document.createElement("td");
1884
+ time.textContent = formatDateTime(row.StartTime);
1885
+ const channel = document.createElement("td");
1886
+ channel.textContent = row.Channel ?? "—";
1887
+ const message = document.createElement("td");
1888
+ const msgSpan = document.createElement("span");
1889
+ msgSpan.className = "dsh-cloudq-usage__table__message";
1890
+ msgSpan.textContent = row.Message ?? "";
1891
+ message.appendChild(msgSpan);
1892
+ const credits = document.createElement("td");
1893
+ credits.className = "dsh-cloudq-usage__table__credits";
1894
+ credits.textContent = formatCredits(row.Credits);
1895
+ tr.appendChild(time);
1896
+ tr.appendChild(channel);
1897
+ tr.appendChild(message);
1898
+ tr.appendChild(credits);
1899
+ tbody.appendChild(tr);
1900
+ }
1901
+ table.appendChild(tbody);
1902
+ panelBody.appendChild(overviewWrap);
1903
+ panelBody.appendChild(table);
1904
+ }).catch((error) => {
1905
+ if (!panelExpanded) return;
1906
+ panelBody.textContent = "";
1907
+ const err = document.createElement("div");
1908
+ err.className = "dsh-cloudq-panel__error";
1909
+ err.textContent = `用量加载失败:${error.message}`;
1910
+ panelBody.appendChild(err);
1911
+ });
1912
+ }
1913
+ const INSPIRATION_TABS = [
1914
+ {
1915
+ category: 0,
1916
+ label: "全部"
1917
+ },
1918
+ {
1919
+ category: 2,
1920
+ label: "风险隐患"
1921
+ },
1922
+ {
1923
+ category: 1,
1924
+ label: "FinOps"
1925
+ },
1926
+ {
1927
+ category: 3,
1928
+ label: "容量负载"
1929
+ },
1930
+ {
1931
+ category: 4,
1932
+ label: "架构治理"
1933
+ },
1934
+ {
1935
+ category: 5,
1936
+ label: "根因分析"
1937
+ },
1938
+ {
1939
+ category: 6,
1940
+ label: "定时任务"
1941
+ },
1942
+ {
1943
+ category: 7,
1944
+ label: "解决方案"
1945
+ }
1946
+ ];
1947
+ function renderInspirationView() {
1948
+ panelBody.textContent = "";
1949
+ const tabs = document.createElement("div");
1950
+ tabs.className = "dsh-cloudq-insp__tabs";
1951
+ for (const tab of INSPIRATION_TABS) {
1952
+ const btn = document.createElement("button");
1953
+ btn.type = "button";
1954
+ btn.className = `dsh-cloudq-insp__tab${tab.category === inspirationCategory ? " is-active" : ""}`;
1955
+ btn.textContent = tab.label;
1956
+ btn.addEventListener("click", () => {
1957
+ inspirationCategory = tab.category;
1958
+ renderInspirationView();
1959
+ });
1960
+ tabs.appendChild(btn);
1961
+ }
1962
+ panelBody.appendChild(tabs);
1963
+ const list = document.createElement("div");
1964
+ list.className = "dsh-cloudq-insp__list";
1965
+ panelBody.appendChild(list);
1966
+ const paint = (inspirations) => {
1967
+ if (panelView !== "inspiration" || !panelExpanded) return;
1968
+ list.textContent = "";
1969
+ const filtered = inspirationCategory === 0 ? inspirations : inspirations.filter((item) => Number(item.Category) === inspirationCategory);
1970
+ if (filtered.length === 0) {
1971
+ const empty = document.createElement("div");
1972
+ empty.className = "dsh-cloudq-usage__table__empty";
1973
+ empty.textContent = "该分类下暂无灵感";
1974
+ list.appendChild(empty);
1975
+ return;
1976
+ }
1977
+ for (const item of filtered) {
1978
+ const card = document.createElement("button");
1979
+ card.type = "button";
1980
+ card.className = "dsh-cloudq-insp__card";
1981
+ const title = document.createElement("span");
1982
+ title.className = "dsh-cloudq-insp__card-title";
1983
+ title.textContent = item.Title ?? item.Prompt ?? "";
1984
+ const desc = document.createElement("span");
1985
+ desc.className = "dsh-cloudq-insp__card-desc";
1986
+ desc.textContent = item.Description ?? "";
1987
+ card.appendChild(title);
1988
+ card.appendChild(desc);
1989
+ card.addEventListener("click", () => {
1990
+ const prompt = item.Prompt ?? item.Title ?? "";
1991
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:draft", { detail: prompt }));
1992
+ });
1993
+ list.appendChild(card);
1994
+ }
1995
+ };
1996
+ if (inspirationCache) {
1997
+ paint(inspirationCache);
1998
+ return;
1999
+ }
2000
+ const hint = document.createElement("div");
2001
+ hint.className = "dsh-cloudq-panel__hint";
2002
+ hint.textContent = "正在加载灵感…";
2003
+ list.appendChild(hint);
2004
+ cloudqRequest(API_INSPIRATIONS).then((data) => {
2005
+ inspirationCache = Array.isArray(data.inspirations) ? data.inspirations : [];
2006
+ paint(inspirationCache);
2007
+ }).catch((error) => {
2008
+ if (!panelExpanded) return;
2009
+ list.textContent = "";
2010
+ const err = document.createElement("div");
2011
+ err.className = "dsh-cloudq-panel__error";
2012
+ err.textContent = `灵感加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2013
+ list.appendChild(err);
2014
+ });
2015
+ }
2016
+ function renderArtifactView({ force = false } = {}) {
2017
+ panelBody.textContent = "";
2018
+ const paint = (sessions, total) => {
2019
+ if (panelView !== "artifact" || !panelExpanded) return;
2020
+ panelBody.textContent = "";
2021
+ const safeSessions = Array.isArray(sessions) ? sessions : [];
2022
+ const artifactCount = safeSessions.reduce((sum, session) => {
2023
+ return sum + (Array.isArray(session?.Artifacts) ? session.Artifacts : []).length;
2024
+ }, 0);
2025
+ const toolbar = document.createElement("div");
2026
+ toolbar.className = "dsh-cloudq-artifact__toolbar";
2027
+ const summary = document.createElement("span");
2028
+ summary.textContent = `共 ${Number(total) || safeSessions.length} 组会话,${artifactCount} 个制品`;
2029
+ const refresh = document.createElement("button");
2030
+ refresh.type = "button";
2031
+ refresh.className = "dsh-cloudq-artifact__refresh";
2032
+ refresh.dataset.testid = "cloudq-artifact-refresh-btn";
2033
+ refresh.textContent = "刷新";
2034
+ refresh.addEventListener("click", () => {
2035
+ artifactCache = null;
2036
+ artifactTotal = 0;
2037
+ renderArtifactView({ force: true });
2038
+ });
2039
+ toolbar.appendChild(summary);
2040
+ toolbar.appendChild(refresh);
2041
+ panelBody.appendChild(toolbar);
2042
+ if (safeSessions.length === 0) {
2043
+ const empty = document.createElement("div");
2044
+ empty.className = "dsh-cloudq-usage__table__empty";
2045
+ empty.textContent = "暂无制品";
2046
+ panelBody.appendChild(empty);
2047
+ return;
2048
+ }
2049
+ const list = document.createElement("div");
2050
+ list.className = "dsh-cloudq-artifact__list";
2051
+ const columns = document.createElement("div");
2052
+ columns.className = "dsh-cloudq-artifact__columns";
2053
+ for (const label of [
2054
+ "报告名称",
2055
+ "用户名称",
2056
+ "生成时间",
2057
+ "操作"
2058
+ ]) {
2059
+ const cell = document.createElement("span");
2060
+ cell.textContent = label;
2061
+ columns.appendChild(cell);
2062
+ }
2063
+ list.appendChild(columns);
2064
+ for (const session of safeSessions) {
2065
+ const artifacts = Array.isArray(session?.Artifacts) ? session.Artifacts : [];
2066
+ const group = document.createElement("section");
2067
+ group.className = "dsh-cloudq-artifact__session";
2068
+ const sessionHead = document.createElement("button");
2069
+ sessionHead.type = "button";
2070
+ sessionHead.className = "dsh-cloudq-artifact__session-head";
2071
+ sessionHead.dataset.testid = `cloudq-artifact-session-${session?.SessionID ?? "unknown"}`;
2072
+ sessionHead.setAttribute("aria-expanded", "true");
2073
+ const chevron = document.createElement("span");
2074
+ chevron.className = "dsh-cloudq-artifact__chevron";
2075
+ chevron.textContent = "▼";
2076
+ const title = document.createElement("span");
2077
+ title.className = "dsh-cloudq-artifact__session-title";
2078
+ title.textContent = session?.SessionTitle || "未命名会话";
2079
+ title.title = title.textContent;
2080
+ const latestTime = artifacts.reduce((latest, artifact) => {
2081
+ const time = String(artifact?.ArchiveTime ?? "");
2082
+ return time > latest ? time : latest;
2083
+ }, "");
2084
+ const meta = document.createElement("span");
2085
+ meta.className = "dsh-cloudq-artifact__session-meta";
2086
+ meta.textContent = `${artifacts.length} 个制品${latestTime ? ` · 最近 ${latestTime.slice(0, 10)}` : ""}`;
2087
+ sessionHead.appendChild(chevron);
2088
+ sessionHead.appendChild(title);
2089
+ sessionHead.appendChild(meta);
2090
+ const files = document.createElement("div");
2091
+ files.className = "dsh-cloudq-artifact__files";
2092
+ for (const artifact of artifacts) {
2093
+ const row = document.createElement("div");
2094
+ row.className = "dsh-cloudq-artifact__file";
2095
+ const main = document.createElement("div");
2096
+ main.className = "dsh-cloudq-artifact__file-main";
2097
+ const type = document.createElement("span");
2098
+ type.className = "dsh-cloudq-artifact__file-type";
2099
+ type.textContent = artifact?.FileType || "file";
2100
+ const name = document.createElement("span");
2101
+ name.className = "dsh-cloudq-artifact__file-name";
2102
+ name.textContent = artifact?.FileName || "未命名文件";
2103
+ name.title = `${name.textContent} · ${formatFileSize(artifact?.SizeBytes)}`;
2104
+ main.appendChild(type);
2105
+ main.appendChild(name);
2106
+ const owner = document.createElement("span");
2107
+ owner.className = "dsh-cloudq-artifact__owner";
2108
+ owner.textContent = artifact?.UserName || "—";
2109
+ const time = document.createElement("span");
2110
+ time.className = "dsh-cloudq-artifact__time";
2111
+ time.textContent = formatDateTime(artifact?.ArchiveTime);
2112
+ const downloadUrl = safeDownloadUrl(artifact?.DownloadURL);
2113
+ const action = document.createElement(downloadUrl ? "a" : "span");
2114
+ action.className = "dsh-cloudq-artifact__download";
2115
+ action.textContent = downloadUrl ? "下载" : "不可用";
2116
+ if (downloadUrl) {
2117
+ action.href = downloadUrl;
2118
+ action.target = "_blank";
2119
+ action.rel = "noopener noreferrer";
2120
+ action.dataset.testid = `cloudq-artifact-download-${artifact?.ArtifactID ?? "unknown"}`;
2121
+ }
2122
+ row.appendChild(main);
2123
+ row.appendChild(owner);
2124
+ row.appendChild(time);
2125
+ row.appendChild(action);
2126
+ files.appendChild(row);
2127
+ }
2128
+ sessionHead.addEventListener("click", () => {
2129
+ const collapsed = group.classList.toggle("is-collapsed");
2130
+ sessionHead.setAttribute("aria-expanded", String(!collapsed));
2131
+ });
2132
+ group.appendChild(sessionHead);
2133
+ group.appendChild(files);
2134
+ list.appendChild(group);
2135
+ }
2136
+ panelBody.appendChild(list);
2137
+ };
2138
+ if (artifactCache !== null && !force) {
2139
+ paint(artifactCache, artifactTotal);
2140
+ return;
2141
+ }
2142
+ const hint = document.createElement("div");
2143
+ hint.className = "dsh-cloudq-panel__hint";
2144
+ hint.textContent = "正在加载制品…";
2145
+ panelBody.appendChild(hint);
2146
+ cloudqRequest(API_ARTIFACTS).then((data) => {
2147
+ artifactCache = Array.isArray(data?.sessions) ? data.sessions : [];
2148
+ artifactTotal = Number(data?.total) || artifactCache.length;
2149
+ paint(artifactCache, artifactTotal);
2150
+ }).catch((error) => {
2151
+ if (panelView !== "artifact" || !panelExpanded) return;
2152
+ panelBody.textContent = "";
2153
+ const err = document.createElement("div");
2154
+ err.className = "dsh-cloudq-panel__error";
2155
+ err.textContent = `制品加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2156
+ panelBody.appendChild(err);
2157
+ });
2158
+ }
2159
+ function renderArchitectureView() {
2160
+ panelBody.textContent = "";
2161
+ const viewRequest = ++architectureRequestSeq;
2162
+ const loading = document.createElement("div");
2163
+ loading.className = "dsh-cloudq-panel__hint";
2164
+ loading.textContent = "正在加载架构目录…";
2165
+ panelBody.appendChild(loading);
2166
+ const paintDirectories = (folders, firstFolderId) => {
2167
+ if (panelView !== "architecture" || !panelExpanded || viewRequest !== architectureRequestSeq) return;
2168
+ const directories = flattenArchitectureDirectories(folders);
2169
+ panelBody.textContent = "";
2170
+ if (directories.length === 0) {
2171
+ const empty = document.createElement("div");
2172
+ empty.className = "dsh-cloudq-usage__table__empty";
2173
+ empty.textContent = "暂无架构目录";
2174
+ panelBody.appendChild(empty);
2175
+ return;
2176
+ }
2177
+ const remembered = loadRememberedArchitectureFolder();
2178
+ const rememberedExists = directories.some((directory) => directory.id === remembered);
2179
+ const apiDefault = Number(firstFolderId);
2180
+ const apiDefaultExists = directories.some((directory) => directory.id === apiDefault);
2181
+ let selectedFolderId = rememberedExists ? remembered : apiDefaultExists ? apiDefault : directories[0].id;
2182
+ const toolbar = document.createElement("div");
2183
+ toolbar.className = "dsh-cloudq-arch__toolbar";
2184
+ const picker = document.createElement("div");
2185
+ picker.className = "dsh-cloudq-arch__picker";
2186
+ const pickerLabel = document.createElement("span");
2187
+ pickerLabel.className = "dsh-cloudq-arch__picker-label";
2188
+ pickerLabel.textContent = "目录";
2189
+ const treeSelect = document.createElement("div");
2190
+ treeSelect.className = "dsh-cloudq-arch__tree-select";
2191
+ const trigger = document.createElement("button");
2192
+ trigger.type = "button";
2193
+ trigger.className = "dsh-cloudq-arch__tree-trigger";
2194
+ trigger.dataset.testid = "cloudq-architecture-directory-select";
2195
+ trigger.setAttribute("role", "combobox");
2196
+ trigger.setAttribute("aria-haspopup", "tree");
2197
+ trigger.setAttribute("aria-expanded", "false");
2198
+ trigger.setAttribute("aria-controls", "dsh-cloudq-architecture-directory-tree");
2199
+ const triggerLabel = document.createElement("span");
2200
+ triggerLabel.className = "dsh-cloudq-arch__tree-trigger-label";
2201
+ triggerLabel.textContent = directories.find((directory) => directory.id === selectedFolderId)?.name || "请选择目录";
2202
+ const triggerArrow = document.createElement("span");
2203
+ triggerArrow.className = "dsh-cloudq-arch__tree-trigger-arrow";
2204
+ triggerArrow.setAttribute("aria-hidden", "true");
2205
+ triggerArrow.textContent = "▼";
2206
+ trigger.appendChild(triggerLabel);
2207
+ trigger.appendChild(triggerArrow);
2208
+ const tree = document.createElement("div");
2209
+ tree.id = "dsh-cloudq-architecture-directory-tree";
2210
+ tree.className = "dsh-cloudq-arch__tree-menu";
2211
+ tree.dataset.testid = "cloudq-architecture-directory-tree";
2212
+ tree.setAttribute("role", "tree");
2213
+ tree.setAttribute("aria-label", "架构目录");
2214
+ tree.hidden = true;
2215
+ const containsFolder = (folder, folderId) => {
2216
+ if (Number(folder?.Id) === folderId) return true;
2217
+ return Array.isArray(folder?.Children) && folder.Children.some((child) => containsFolder(child, folderId));
2218
+ };
2219
+ const setExpanded = (node, row, group, expanded) => {
2220
+ node.classList.toggle("is-expanded", expanded);
2221
+ row.setAttribute("aria-expanded", String(expanded));
2222
+ group.hidden = !expanded;
2223
+ };
2224
+ const syncTreeSelection = () => {
2225
+ for (const row of tree.querySelectorAll("[role=\"treeitem\"]")) {
2226
+ const selected = Number(row.dataset.folderId) === selectedFolderId;
2227
+ row.classList.toggle("is-selected", selected);
2228
+ row.setAttribute("aria-selected", String(selected));
2229
+ }
2230
+ };
2231
+ const selectFolder = (folderId, folderName) => {
2232
+ if (!Number.isSafeInteger(folderId) || folderId <= 0) return;
2233
+ const changed = selectedFolderId !== folderId;
2234
+ selectedFolderId = folderId;
2235
+ triggerLabel.textContent = folderName;
2236
+ rememberArchitectureFolder(folderId);
2237
+ syncTreeSelection();
2238
+ setMenuOpen(false);
2239
+ if (changed) loadArchitectures(folderId);
2240
+ };
2241
+ const renderFolderNodes = (foldersToRender, parent, depth = 0) => {
2242
+ if (!Array.isArray(foldersToRender)) return;
2243
+ for (const folder of foldersToRender) {
2244
+ const folderId = Number(folder?.Id);
2245
+ if (!Number.isSafeInteger(folderId) || folderId <= 0) continue;
2246
+ const children = Array.isArray(folder?.Children) ? folder.Children : [];
2247
+ const folderName = String(folder?.Name || `目录 ${folderId}`);
2248
+ const node = document.createElement("div");
2249
+ node.className = "dsh-cloudq-arch__tree-node";
2250
+ const row = document.createElement("div");
2251
+ row.className = "dsh-cloudq-arch__tree-row";
2252
+ row.dataset.folderId = String(folderId);
2253
+ row.dataset.testid = `cloudq-architecture-directory-${folderId}`;
2254
+ row.setAttribute("role", "treeitem");
2255
+ row.setAttribute("aria-level", String(depth + 1));
2256
+ row.setAttribute("aria-selected", String(folderId === selectedFolderId));
2257
+ row.tabIndex = folderId === selectedFolderId ? 0 : -1;
2258
+ row.style.paddingLeft = `${6 + depth * 22}px`;
2259
+ if (folderId === selectedFolderId) row.classList.add("is-selected");
2260
+ const caret = document.createElement("span");
2261
+ caret.className = `dsh-cloudq-arch__tree-caret${children.length === 0 ? " is-leaf" : ""}`;
2262
+ caret.setAttribute("aria-hidden", "true");
2263
+ caret.textContent = "▶";
2264
+ const folderIcon = document.createElement("span");
2265
+ folderIcon.className = "dsh-cloudq-arch__tree-folder";
2266
+ folderIcon.setAttribute("aria-hidden", "true");
2267
+ const name = document.createElement("span");
2268
+ name.className = "dsh-cloudq-arch__tree-name";
2269
+ name.textContent = folderName;
2270
+ name.title = folderName;
2271
+ const check = document.createElement("span");
2272
+ check.className = "dsh-cloudq-arch__tree-check";
2273
+ check.setAttribute("aria-hidden", "true");
2274
+ check.textContent = "✓";
2275
+ row.appendChild(caret);
2276
+ row.appendChild(folderIcon);
2277
+ row.appendChild(name);
2278
+ row.appendChild(check);
2279
+ node.appendChild(row);
2280
+ let group = null;
2281
+ if (children.length > 0) {
2282
+ group = document.createElement("div");
2283
+ group.className = "dsh-cloudq-arch__tree-group";
2284
+ group.setAttribute("role", "group");
2285
+ node.appendChild(group);
2286
+ renderFolderNodes(children, group, depth + 1);
2287
+ const startsExpanded = containsFolder(folder, selectedFolderId) && folderId !== selectedFolderId;
2288
+ setExpanded(node, row, group, startsExpanded);
2289
+ caret.addEventListener("click", (event) => {
2290
+ event.stopPropagation();
2291
+ setExpanded(node, row, group, !node.classList.contains("is-expanded"));
2292
+ });
2293
+ }
2294
+ const choose = () => selectFolder(folderId, folderName);
2295
+ row.addEventListener("click", choose);
2296
+ row.addEventListener("keydown", (event) => {
2297
+ if (event.key === "Enter" || event.key === " ") {
2298
+ event.preventDefault();
2299
+ choose();
2300
+ } else if (event.key === "ArrowRight" && group) {
2301
+ event.preventDefault();
2302
+ setExpanded(node, row, group, true);
2303
+ } else if (event.key === "ArrowLeft" && group) {
2304
+ event.preventDefault();
2305
+ setExpanded(node, row, group, false);
2306
+ } else if (event.key === "Escape") {
2307
+ event.preventDefault();
2308
+ setMenuOpen(false);
2309
+ trigger.focus();
2310
+ }
2311
+ });
2312
+ parent.appendChild(node);
2313
+ }
2314
+ };
2315
+ renderFolderNodes(folders, tree);
2316
+ const onOutside = (event) => {
2317
+ if (!treeSelect.contains(event.target)) setMenuOpen(false);
2318
+ };
2319
+ const onDocumentKeyDown = (event) => {
2320
+ if (event.key !== "Escape") return;
2321
+ setMenuOpen(false);
2322
+ trigger.focus();
2323
+ };
2324
+ function setMenuOpen(open) {
2325
+ tree.hidden = !open;
2326
+ trigger.classList.toggle("is-open", open);
2327
+ trigger.setAttribute("aria-expanded", String(open));
2328
+ document.removeEventListener("mousedown", onOutside, true);
2329
+ document.removeEventListener("keydown", onDocumentKeyDown, true);
2330
+ if (open) {
2331
+ document.addEventListener("mousedown", onOutside, true);
2332
+ document.addEventListener("keydown", onDocumentKeyDown, true);
2333
+ window.requestAnimationFrame(() => {
2334
+ tree.querySelector(".dsh-cloudq-arch__tree-row.is-selected")?.focus();
2335
+ });
2336
+ }
2337
+ }
2338
+ trigger.addEventListener("click", () => setMenuOpen(tree.hidden));
2339
+ trigger.addEventListener("keydown", (event) => {
2340
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
2341
+ event.preventDefault();
2342
+ setMenuOpen(true);
2343
+ } else if (event.key === "Escape") setMenuOpen(false);
2344
+ });
2345
+ architecturePickerCleanup = () => {
2346
+ document.removeEventListener("mousedown", onOutside, true);
2347
+ document.removeEventListener("keydown", onDocumentKeyDown, true);
2348
+ };
2349
+ const summary = document.createElement("span");
2350
+ summary.className = "dsh-cloudq-arch__summary";
2351
+ summary.textContent = "正在加载…";
2352
+ treeSelect.appendChild(trigger);
2353
+ treeSelect.appendChild(tree);
2354
+ picker.appendChild(pickerLabel);
2355
+ picker.appendChild(treeSelect);
2356
+ toolbar.appendChild(picker);
2357
+ toolbar.appendChild(summary);
2358
+ const grid = document.createElement("div");
2359
+ grid.className = "dsh-cloudq-arch__grid";
2360
+ panelBody.appendChild(toolbar);
2361
+ panelBody.appendChild(grid);
2362
+ const loadArchitectures = (folderId) => {
2363
+ const request = ++architectureRequestSeq;
2364
+ grid.textContent = "";
2365
+ summary.textContent = "正在加载…";
2366
+ const hint = document.createElement("div");
2367
+ hint.className = "dsh-cloudq-panel__hint";
2368
+ hint.textContent = "正在加载架构图…";
2369
+ grid.appendChild(hint);
2370
+ cloudqRequest(`${API_ARCH_LIST}?folderId=${encodeURIComponent(folderId)}`).then((data) => {
2371
+ if (panelView !== "architecture" || !panelExpanded || request !== architectureRequestSeq) return;
2372
+ const architectures = Array.isArray(data?.architectures) ? data.architectures : [];
2373
+ grid.textContent = "";
2374
+ summary.textContent = `共 ${Number(data?.total) || architectures.length} 个架构图`;
2375
+ if (architectures.length === 0) {
2376
+ const empty = document.createElement("div");
2377
+ empty.className = "dsh-cloudq-usage__table__empty";
2378
+ empty.textContent = "当前目录暂无架构图";
2379
+ grid.appendChild(empty);
2380
+ return;
2381
+ }
2382
+ for (const architecture of architectures) {
2383
+ const imageUrl = safeDownloadUrl(architecture?.SvgURL);
2384
+ const card = document.createElement(imageUrl ? "a" : "article");
2385
+ card.className = "dsh-cloudq-arch__card";
2386
+ card.dataset.testid = `cloudq-architecture-card-${architecture?.ArchId ?? "unknown"}`;
2387
+ if (imageUrl) {
2388
+ card.href = imageUrl;
2389
+ card.target = "_blank";
2390
+ card.rel = "noopener noreferrer";
2391
+ card.setAttribute("aria-label", `查看架构图:${architecture?.ArchName || "未命名系统"}`);
2392
+ }
2393
+ const preview = document.createElement("div");
2394
+ preview.className = "dsh-cloudq-arch__preview";
2395
+ const placeholder = document.createElement("span");
2396
+ placeholder.className = "dsh-cloudq-arch__placeholder";
2397
+ placeholder.textContent = imageUrl ? "正在加载预览…" : "暂无预览";
2398
+ preview.appendChild(placeholder);
2399
+ if (imageUrl) {
2400
+ const image = document.createElement("img");
2401
+ image.className = "dsh-cloudq-arch__image";
2402
+ image.src = imageUrl;
2403
+ image.alt = architecture?.ArchName || "架构图预览";
2404
+ image.loading = "lazy";
2405
+ image.decoding = "async";
2406
+ image.referrerPolicy = "no-referrer";
2407
+ image.addEventListener("load", () => placeholder.remove());
2408
+ image.addEventListener("error", () => {
2409
+ image.remove();
2410
+ placeholder.textContent = "预览加载失败,点击可打开原图";
2411
+ });
2412
+ preview.appendChild(image);
2413
+ }
2414
+ const footer = document.createElement("div");
2415
+ footer.className = "dsh-cloudq-arch__footer";
2416
+ const name = document.createElement("span");
2417
+ name.className = "dsh-cloudq-arch__name";
2418
+ name.textContent = architecture?.ArchName || "未命名系统";
2419
+ name.title = name.textContent;
2420
+ const meta = document.createElement("span");
2421
+ meta.className = "dsh-cloudq-arch__meta";
2422
+ const tags = Array.isArray(architecture?.Tags) ? architecture.Tags : [];
2423
+ meta.textContent = tags.length > 0 ? `${tags.length} 个标签` : architecture?.VersionName || "";
2424
+ footer.appendChild(name);
2425
+ footer.appendChild(meta);
2426
+ card.appendChild(preview);
2427
+ card.appendChild(footer);
2428
+ grid.appendChild(card);
2429
+ }
2430
+ }).catch((error) => {
2431
+ if (panelView !== "architecture" || !panelExpanded || request !== architectureRequestSeq) return;
2432
+ grid.textContent = "";
2433
+ summary.textContent = "加载失败";
2434
+ const err = document.createElement("div");
2435
+ err.className = "dsh-cloudq-panel__error";
2436
+ err.textContent = `架构图加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2437
+ grid.appendChild(err);
2438
+ });
2439
+ };
2440
+ loadArchitectures(selectedFolderId);
2441
+ };
2442
+ if (architectureDirectoriesCache !== null) {
2443
+ paintDirectories(architectureDirectoriesCache, architectureFirstFolderId);
2444
+ return;
2445
+ }
2446
+ cloudqRequest(API_ARCH_DIRECTORIES).then((data) => {
2447
+ architectureDirectoriesCache = Array.isArray(data?.folders) ? data.folders : [];
2448
+ architectureFirstFolderId = Number(data?.firstFolderId) || null;
2449
+ paintDirectories(architectureDirectoriesCache, architectureFirstFolderId);
2450
+ }).catch((error) => {
2451
+ if (panelView !== "architecture" || !panelExpanded || viewRequest !== architectureRequestSeq) return;
2452
+ panelBody.textContent = "";
2453
+ const err = document.createElement("div");
2454
+ err.className = "dsh-cloudq-panel__error";
2455
+ err.textContent = `架构目录加载失败:${error instanceof Error ? error.message : "未知错误"}`;
2456
+ panelBody.appendChild(err);
2457
+ });
2458
+ }
2459
+ function renderPanel() {
2460
+ if (architecturePickerCleanup) {
2461
+ architecturePickerCleanup();
2462
+ architecturePickerCleanup = null;
2463
+ }
2464
+ if (panelView === "usage") renderUsageView();
2465
+ else if (panelView === "inspiration") renderInspirationView();
2466
+ else if (panelView === "artifact") renderArtifactView();
2467
+ else renderArchitectureView();
2468
+ }
2469
+ function openPanel() {
2470
+ panelExpanded = true;
2471
+ document.documentElement.classList.add("dsh-cloudq-panel__rail-enter");
2472
+ document.documentElement.classList.remove("dsh-cloudq-panel__rail-leave");
2473
+ const chip = document.querySelector("[data-cloudq-convo-chip=\"true\"]");
2474
+ if (chip) chip.classList.add("is-open");
2475
+ document.documentElement.style.setProperty("--cloudq-rail-width", `${PANEL_DEFAULT_WIDTH}px`);
2476
+ if (!panelEl) {
2477
+ panelEl = document.createElement("div");
2478
+ panelEl.className = "dsh-cloudq-panel__rail";
2479
+ panelEl.style.width = `${PANEL_DEFAULT_WIDTH}px`;
2480
+ const resizer = document.createElement("div");
2481
+ resizer.className = "dsh-cloudq-panel__resizer";
2482
+ resizer.addEventListener("mousedown", (event) => {
2483
+ event.preventDefault();
2484
+ resizer.classList.add("is-dragging");
2485
+ const startX = event.clientX;
2486
+ const startWidth = panelEl.getBoundingClientRect().width;
2487
+ const onMove = (moveEvent) => {
2488
+ const nextWidth = Math.max(PANEL_MIN_WIDTH, Math.min(window.innerWidth * .8, startWidth + (startX - moveEvent.clientX)));
2489
+ panelEl.style.width = `${nextWidth}px`;
2490
+ document.documentElement.style.setProperty("--cloudq-rail-width", `${nextWidth}px`);
2491
+ };
2492
+ const onUp = () => {
2493
+ resizer.classList.remove("is-dragging");
2494
+ window.removeEventListener("mousemove", onMove);
2495
+ window.removeEventListener("mouseup", onUp);
2496
+ };
2497
+ window.addEventListener("mousemove", onMove);
2498
+ window.addEventListener("mouseup", onUp);
2499
+ });
2500
+ const inner = document.createElement("div");
2501
+ inner.className = "dsh-cloudq-panel__inner";
2502
+ const header = document.createElement("div");
2503
+ header.className = "dsh-cloudq-panel__header";
2504
+ const PANEL_VIEWS = [
2505
+ {
2506
+ value: "usage",
2507
+ label: "用量"
2508
+ },
2509
+ {
2510
+ value: "inspiration",
2511
+ label: "灵感"
2512
+ },
2513
+ {
2514
+ value: "artifact",
2515
+ label: "制品"
2516
+ },
2517
+ {
2518
+ value: "architecture",
2519
+ label: "架构图"
2520
+ }
2521
+ ];
2522
+ const tabs = document.createElement("div");
2523
+ tabs.className = "dsh-cloudq-panel__tabs";
2524
+ tabs.setAttribute("role", "tablist");
2525
+ tabs.setAttribute("aria-label", "CloudQ 能力");
2526
+ const syncTabs = () => {
2527
+ for (const tab of tabs.children) {
2528
+ const active = tab.dataset.value === panelView;
2529
+ tab.classList.toggle("is-active", active);
2530
+ tab.setAttribute("aria-selected", String(active));
2531
+ tab.tabIndex = active ? 0 : -1;
2532
+ }
2533
+ };
2534
+ for (const view of PANEL_VIEWS) {
2535
+ const tab = document.createElement("button");
2536
+ tab.type = "button";
2537
+ tab.className = "dsh-cloudq-panel__tab";
2538
+ tab.dataset.value = view.value;
2539
+ tab.setAttribute("role", "tab");
2540
+ tab.textContent = view.label;
2541
+ tab.addEventListener("click", () => {
2542
+ if (panelView === view.value) return;
2543
+ panelView = view.value;
2544
+ syncTabs();
2545
+ renderPanel();
2546
+ });
2547
+ tabs.appendChild(tab);
2548
+ }
2549
+ syncTabs();
2550
+ header.appendChild(tabs);
2551
+ panelBody = document.createElement("div");
2552
+ panelBody.className = "dsh-cloudq-panel__body";
2553
+ inner.appendChild(header);
2554
+ inner.appendChild(panelBody);
2555
+ panelEl.appendChild(resizer);
2556
+ panelEl.appendChild(inner);
2557
+ document.body.appendChild(panelEl);
2558
+ }
2559
+ panelEl.classList.add("is-open");
2560
+ renderPanel();
2561
+ }
2562
+ function closePanel() {
2563
+ panelExpanded = false;
2564
+ if (architecturePickerCleanup) {
2565
+ architecturePickerCleanup();
2566
+ architecturePickerCleanup = null;
2567
+ }
2568
+ document.documentElement.classList.remove("dsh-cloudq-panel__rail-enter");
2569
+ document.documentElement.classList.add("dsh-cloudq-panel__rail-leave");
2570
+ const chip = document.querySelector("[data-cloudq-convo-chip=\"true\"]");
2571
+ if (chip) chip.classList.remove("is-open");
2572
+ if (panelEl) panelEl.classList.remove("is-open");
2573
+ }
2574
+ function togglePanel() {
2575
+ if (panelExpanded) closePanel();
2576
+ else openPanel();
2577
+ }
2578
+ function buildIconSvg() {
2579
+ const svgNs = "http://www.w3.org/2000/svg";
2580
+ const svg = document.createElementNS(svgNs, "svg");
2581
+ svg.setAttribute("viewBox", "0 0 24 24");
2582
+ svg.setAttribute("fill", "none");
2583
+ svg.setAttribute("stroke", "currentColor");
2584
+ svg.setAttribute("stroke-width", "1.6");
2585
+ svg.setAttribute("stroke-linecap", "round");
2586
+ svg.setAttribute("stroke-linejoin", "round");
2587
+ const rect = document.createElementNS(svgNs, "rect");
2588
+ rect.setAttribute("x", "4");
2589
+ rect.setAttribute("y", "3.5");
2590
+ rect.setAttribute("width", "16");
2591
+ rect.setAttribute("height", "17");
2592
+ rect.setAttribute("rx", "2.5");
2593
+ svg.appendChild(rect);
2594
+ const seam = document.createElementNS(svgNs, "path");
2595
+ seam.setAttribute("d", "M14 3.5 V20.5");
2596
+ svg.appendChild(seam);
2597
+ return svg;
2598
+ }
2599
+ function buildCloudqHeroPane() {
2600
+ const wrap = document.createElement("div");
2601
+ wrap.dataset.cloudqHero = "true";
2602
+ wrap.style.position = "relative";
2603
+ const title = document.createElement("h1");
2604
+ title.className = "dsh-cloudq-hero-title";
2605
+ title.textContent = CLOUDQ_HEADLINE;
2606
+ const subtitle = document.createElement("p");
2607
+ subtitle.className = "dsh-cloudq-hero-subtitle";
2608
+ subtitle.textContent = CLOUDQ_SUBTITLE;
2609
+ const questions = document.createElement("div");
2610
+ questions.className = "dsh-cloudq-hero-questions";
2611
+ CLOUDQ_QUESTIONS.forEach((q) => questions.appendChild(buildCloudqQuestionButton(q)));
2612
+ wrap.appendChild(title);
2613
+ wrap.appendChild(subtitle);
2614
+ wrap.appendChild(questions);
2615
+ return wrap;
2616
+ }
2617
+ function findHeroRoot() {
2618
+ const headline = document.querySelector("[class*=headlineText]");
2619
+ if (!headline) return null;
2620
+ const stack = headline.parentElement;
2621
+ if (!stack) return null;
2622
+ return stack.parentElement;
2623
+ }
2624
+ function applyCloudqHero(active) {
2625
+ const heroRoot = findHeroRoot();
2626
+ document.body.classList.toggle("dsh-cloudq-mode-active", active);
2627
+ if (!active) {
2628
+ if (heroRoot) {
2629
+ const pane = heroRoot.querySelector("[data-cloudq-hero=\"true\"]");
2630
+ if (pane) pane.remove();
2631
+ }
2632
+ document.querySelector("[data-cloudq-convo-chip=\"true\"]")?.remove();
2633
+ closePanel();
2634
+ return;
2635
+ }
2636
+ if (heroRoot) {
2637
+ let pane = heroRoot.querySelector("[data-cloudq-hero=\"true\"]");
2638
+ if (!pane) {
2639
+ pane = buildCloudqHeroPane();
2640
+ const stack = heroRoot.querySelector("[class*=headlineText]")?.parentElement;
2641
+ if (stack && stack.parentElement === heroRoot) stack.insertAdjacentElement("afterend", pane);
2642
+ else heroRoot.appendChild(pane);
2643
+ }
2644
+ }
2645
+ let chip = document.querySelector("[data-cloudq-convo-chip=\"true\"]");
2646
+ if (!chip) {
2647
+ chip = document.createElement("button");
2648
+ chip.type = "button";
2649
+ chip.className = "dsh-cloudq-convo-chip";
2650
+ chip.dataset.cloudqConvoChip = "true";
2651
+ chip.setAttribute("aria-label", "展开或收起 CloudQ 能力面板");
2652
+ chip.appendChild(buildIconSvg());
2653
+ chip.addEventListener("click", togglePanel);
2654
+ document.body.appendChild(chip);
2655
+ }
2656
+ chip.title = panelExpanded ? "收起 CloudQ 能力面板" : "展开 CloudQ 能力面板";
2657
+ chip.classList.toggle("is-open", panelExpanded);
2658
+ }
2659
+ function installCloudqHero() {
2660
+ installCloudqHeroStyles();
2661
+ let observer = null;
2662
+ let newSessionButton = null;
2663
+ let newSessionListenerAttached = false;
2664
+ let unsubscribeSessions = null;
2665
+ const sync = () => {
2666
+ applyCloudqHero(isCloudqSession(cloudqCtx?.sessions?.list?.getSnapshot?.()?.current ?? cloudqCtx?.sessions?.selection?.getSnapshot?.()?.sessionId));
2667
+ };
2668
+ const onNewSessionClick = () => {
2669
+ const snapshot = cloudqCtx?.sessions?.list?.getSnapshot?.();
2670
+ const currentId = snapshot?.current ?? cloudqCtx?.sessions?.selection?.getSnapshot?.()?.sessionId;
2671
+ if (currentId && snapshot?.byId?.[currentId]?.blank && cloudqSessions.has(currentId)) {
2672
+ unmarkCloudqSession(currentId);
2673
+ cloudqDraftPrimed.delete(currentId);
2674
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:clear-draft", { detail: currentId }));
2675
+ }
2676
+ window.setTimeout(sync, 0);
2677
+ };
2678
+ const tryAttachNewSessionButton = () => {
2679
+ const candidate = document.querySelector("[class*=newSession]:not(.dsh-cloudq-sidebar-btn)");
2680
+ if (!candidate) return false;
2681
+ if (candidate === newSessionButton && newSessionListenerAttached) return true;
2682
+ newSessionButton = candidate;
2683
+ candidate.addEventListener("click", onNewSessionClick, { capture: true });
2684
+ newSessionListenerAttached = true;
2685
+ return true;
2686
+ };
2687
+ const tryAttach = () => {
2688
+ sync();
2689
+ tryAttachNewSessionButton();
2690
+ if (observer) return;
2691
+ observer = new MutationObserver(() => {
2692
+ sync();
2693
+ tryAttachNewSessionButton();
2694
+ });
2695
+ observer.observe(document.body, {
2696
+ childList: true,
2697
+ subtree: true
2698
+ });
2699
+ };
2700
+ tryAttach();
2701
+ unsubscribeSessions = cloudqCtx?.sessions?.list?.subscribe?.(sync) ?? null;
2702
+ window.addEventListener("dsh-cloudq:sessions-changed", sync);
2703
+ return () => {
2704
+ if (observer) observer.disconnect();
2705
+ if (unsubscribeSessions) unsubscribeSessions();
2706
+ window.removeEventListener("dsh-cloudq:sessions-changed", sync);
2707
+ if (newSessionButton && newSessionListenerAttached) {
2708
+ newSessionButton.removeEventListener("click", onNewSessionClick, { capture: true });
2709
+ newSessionListenerAttached = false;
2710
+ newSessionButton = null;
2711
+ }
2712
+ const heroRoot = findHeroRoot();
2713
+ if (heroRoot) {
2714
+ const pane = heroRoot.querySelector("[data-cloudq-hero=\"true\"]");
2715
+ if (pane) pane.remove();
2716
+ }
2717
+ document.querySelector("[data-cloudq-convo-chip=\"true\"]")?.remove();
2718
+ panelExpanded = false;
2719
+ if (panelEl) {
2720
+ panelEl.remove();
2721
+ panelEl = null;
2722
+ panelBody = null;
2723
+ }
2724
+ inspirationCache = null;
2725
+ inspirationCategory = 0;
2726
+ artifactCache = null;
2727
+ artifactTotal = 0;
2728
+ architectureDirectoriesCache = null;
2729
+ architectureFirstFolderId = null;
2730
+ architectureRequestSeq += 1;
2731
+ panelView = "usage";
2732
+ };
2733
+ }
2734
+ const API_CREDENTIAL = "/api/dsh-cloudq/credential";
2735
+ const API_LOGIN_URL = "/api/dsh-cloudq/login/url";
2736
+ const API_LOGIN_SAVE = "/api/dsh-cloudq/login/save";
2737
+ const API_LOGOUT = "/api/dsh-cloudq/logout";
2738
+ const API_CREDENTIAL_TEST = "/api/dsh-cloudq/credential/test";
2739
+ const API_CREDENTIAL_SAVE = "/api/dsh-cloudq/credential/save";
2740
+ const CAPI_CONSOLE_URL = "https://console.cloud.tencent.com/cam/capi";
2741
+ var CloudQApiError = class extends Error {
2742
+ constructor(message, code) {
2743
+ super(message);
2744
+ this.code = code;
2745
+ }
2746
+ };
2747
+ async function cloudqRequest(url, init, timeoutMs = 2e4) {
2748
+ const controller = new AbortController();
2749
+ const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
2750
+ try {
2751
+ const response = await fetch(url, {
2752
+ ...init,
2753
+ credentials: "same-origin",
2754
+ headers: {
2755
+ ...init?.body === void 0 ? {} : { "Content-Type": "application/json" },
2756
+ ...init?.headers
2757
+ },
2758
+ signal: controller.signal
2759
+ });
2760
+ let payload;
2761
+ try {
2762
+ payload = await response.json();
2763
+ } catch {
2764
+ throw new CloudQApiError("服务返回了无法识别的响应。", "invalid-response");
2765
+ }
2766
+ if (!response.ok || payload && payload.ok === false) {
2767
+ const error = payload?.error;
2768
+ throw new CloudQApiError(error?.message ?? "请求失败,请稍后重试。", error?.code ?? "request-failed");
2769
+ }
2770
+ return payload;
2771
+ } catch (error) {
2772
+ if (error instanceof CloudQApiError) throw error;
2773
+ throw new CloudQApiError("网络错误,请稍后重试。", "network-error");
2774
+ } finally {
2775
+ window.clearTimeout(timeout);
2776
+ }
2777
+ }
2778
+ function minutesText(minutes) {
2779
+ if (minutes == null) return "未知";
2780
+ if (minutes <= 0) return "已过期";
2781
+ if (minutes < 60) return `${minutes} 分钟`;
2782
+ const hours = Math.floor(minutes / 60);
2783
+ const rest = minutes % 60;
2784
+ return rest > 0 ? `${hours} 小时 ${rest} 分钟` : `${hours} 小时`;
2785
+ }
2786
+ function CloudQSettingsCard() {
2787
+ const [expanded, setExpanded] = react.useState(true);
2788
+ const [loading, setLoading] = react.useState(true);
2789
+ const [busy, setBusy] = react.useState();
2790
+ const [status, setStatus] = react.useState();
2791
+ const [authorizeUrl, setAuthorizeUrl] = react.useState("");
2792
+ const [code, setCode] = react.useState("");
2793
+ const [feedback, setFeedback] = react.useState();
2794
+ const [secretId, setSecretId] = react.useState("");
2795
+ const [secretKey, setSecretKey] = react.useState("");
2796
+ const refresh = react.useCallback(async () => {
2797
+ setLoading(true);
2798
+ try {
2799
+ const response = await cloudqRequest(API_CREDENTIAL);
2800
+ setStatus(response.status);
2801
+ } catch (error) {
2802
+ setFeedback({
2803
+ kind: "error",
2804
+ text: error.message
2805
+ });
2806
+ } finally {
2807
+ setLoading(false);
2808
+ }
2809
+ }, []);
2810
+ react.useEffect(() => {
2811
+ refresh();
2812
+ }, [refresh]);
2813
+ const handleGetAuthorizeUrl = async () => {
2814
+ setBusy("url");
2815
+ setFeedback(void 0);
2816
+ try {
2817
+ const response = await cloudqRequest(API_LOGIN_URL, { method: "POST" });
2818
+ setAuthorizeUrl(response.authorize_url);
2819
+ if (response.authorize_url) window.open(response.authorize_url, "_blank", "noopener");
2820
+ } catch (error) {
2821
+ setFeedback({
2822
+ kind: "error",
2823
+ text: error.message
2824
+ });
2825
+ } finally {
2826
+ setBusy(void 0);
2827
+ }
2828
+ };
2829
+ const handleSaveCode = async () => {
2830
+ const trimmed = code.trim();
2831
+ if (!trimmed) {
2832
+ setFeedback({
2833
+ kind: "error",
2834
+ text: "请先在浏览器中完成授权,然后粘贴授权码。"
2835
+ });
2836
+ return;
2837
+ }
2838
+ setBusy("save");
2839
+ setFeedback(void 0);
2840
+ try {
2841
+ const response = await cloudqRequest(API_LOGIN_SAVE, {
2842
+ method: "POST",
2843
+ body: JSON.stringify({ code: trimmed })
2844
+ });
2845
+ setStatus(response.status);
2846
+ setCode("");
2847
+ setFeedback({
2848
+ kind: "success",
2849
+ text: "登录成功,凭证已保存。"
2850
+ });
2851
+ } catch (error) {
2852
+ setFeedback({
2853
+ kind: "error",
2854
+ text: error.message
2855
+ });
2856
+ } finally {
2857
+ setBusy(void 0);
2858
+ }
2859
+ };
2860
+ const handleLogout = async () => {
2861
+ setBusy("logout");
2862
+ setFeedback(void 0);
2863
+ try {
2864
+ await cloudqRequest(API_LOGOUT, { method: "POST" });
2865
+ setStatus({ logged_in: false });
2866
+ setAuthorizeUrl("");
2867
+ setCode("");
2868
+ setFeedback({
2869
+ kind: "success",
2870
+ text: "已退出登录。"
2871
+ });
2872
+ } catch (error) {
2873
+ setFeedback({
2874
+ kind: "error",
2875
+ text: error.message
2876
+ });
2877
+ } finally {
2878
+ setBusy(void 0);
2879
+ }
2880
+ };
2881
+ const submitAccessKey = async (endpoint, kind, successText) => {
2882
+ const trimmedId = secretId.trim();
2883
+ const trimmedKey = secretKey.trim();
2884
+ if (!trimmedId || !trimmedKey) {
2885
+ setFeedback({
2886
+ kind: "error",
2887
+ text: "请先填写 SecretId 与 SecretKey。"
2888
+ });
2889
+ return;
2890
+ }
2891
+ setBusy(kind);
2892
+ setFeedback(void 0);
2893
+ try {
2894
+ const response = await cloudqRequest(endpoint, {
2895
+ method: "POST",
2896
+ body: JSON.stringify({
2897
+ secretId: trimmedId,
2898
+ secretKey: trimmedKey
2899
+ })
2900
+ });
2901
+ if (response.status) {
2902
+ setStatus(response.status);
2903
+ setSecretId("");
2904
+ setSecretKey("");
2905
+ }
2906
+ setFeedback({
2907
+ kind: "success",
2908
+ text: successText
2909
+ });
2910
+ } catch (error) {
2911
+ setFeedback({
2912
+ kind: "error",
2913
+ text: error.message
2914
+ });
2915
+ } finally {
2916
+ setBusy(void 0);
2917
+ }
2918
+ };
2919
+ const handleTestConnection = () => submitAccessKey(API_CREDENTIAL_TEST, "test", "连接成功,该密钥可用于 CloudQ。");
2920
+ const handleSaveAccessKey = () => submitAccessKey(API_CREDENTIAL_SAVE, "save-ak", "配置已保存,密钥已安全存放在 DSH Host 端。");
2921
+ const loggedIn = status?.logged_in === true;
2922
+ const expired = status?.expired === true;
2923
+ const badgeClass = expired ? "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--warn" : loggedIn ? "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--ok" : "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--off";
2924
+ const badgeText = expired ? "凭证已过期" : loggedIn ? "已登录" : "未登录";
2925
+ const summarySubtitle = loggedIn ? "腾讯云 CloudQ 凭证已激活,可直接使用 CloudQ 模式。" : "用于在 CloudQ 模式下调用腾讯云 SSE API 与 CLI 工具。";
2926
+ const disabled = loading || busy !== void 0;
2927
+ return react_jsx_runtime.jsx("section", {
2928
+ className: "dsh-cloudq-settings-card",
2929
+ children: [react_jsx_runtime.jsx("div", {
2930
+ className: "dsh-cloudq-settings-card__summary",
2931
+ children: react_jsx_runtime.jsx("button", {
2932
+ type: "button",
2933
+ className: "dsh-cloudq-settings-card__summary-trigger",
2934
+ "aria-expanded": expanded,
2935
+ onClick: () => setExpanded((prev) => !prev),
2936
+ children: [react_jsx_runtime.jsx("span", {
2937
+ className: "dsh-cloudq-settings-card__summary-title",
2938
+ children: [react_jsx_runtime.jsx("strong", { children: "CloudQ" }), react_jsx_runtime.jsx("small", { children: summarySubtitle })]
2939
+ }), react_jsx_runtime.jsx("span", {
2940
+ className: "dsh-cloudq-settings-card__summary-right",
2941
+ children: [react_jsx_runtime.jsx("span", {
2942
+ className: badgeClass,
2943
+ children: [react_jsx_runtime.jsx("span", { className: "dsh-cloudq-settings-card__badge-dot" }), badgeText]
2944
+ }), react_jsx_runtime.jsx("span", {
2945
+ className: `dsh-cloudq-settings-card__chevron${expanded ? " is-open" : ""}`,
2946
+ children: "▾"
2947
+ })]
2948
+ })]
2949
+ })
2950
+ }), !expanded ? null : react_jsx_runtime.jsx("div", {
2951
+ className: "dsh-cloudq-settings-card__body",
2952
+ children: [
2953
+ loading ? react_jsx_runtime.jsx("div", {
2954
+ className: "dsh-cloudq-settings-card__hint",
2955
+ children: "正在检查 CloudQ 登录状态…"
2956
+ }) : react_jsx_runtime.jsx("div", {
2957
+ className: "dsh-cloudq-settings-card__rows",
2958
+ children: [
2959
+ react_jsx_runtime.jsx("div", {
2960
+ className: "dsh-cloudq-settings-card__row",
2961
+ children: [react_jsx_runtime.jsx("span", {
2962
+ className: "dsh-cloudq-settings-card__row-key",
2963
+ children: "状态"
2964
+ }), react_jsx_runtime.jsx("span", {
2965
+ className: "dsh-cloudq-settings-card__row-val",
2966
+ children: badgeText
2967
+ })]
2968
+ }),
2969
+ status?.credential_file ? react_jsx_runtime.jsx("div", {
2970
+ className: "dsh-cloudq-settings-card__row",
2971
+ children: [react_jsx_runtime.jsx("span", {
2972
+ className: "dsh-cloudq-settings-card__row-key",
2973
+ children: "凭证文件"
2974
+ }), react_jsx_runtime.jsx("span", {
2975
+ className: "dsh-cloudq-settings-card__row-val",
2976
+ children: status.credential_file
2977
+ })]
2978
+ }) : null,
2979
+ status?.secret_id_masked ? react_jsx_runtime.jsx("div", {
2980
+ className: "dsh-cloudq-settings-card__row",
2981
+ children: [react_jsx_runtime.jsx("span", {
2982
+ className: "dsh-cloudq-settings-card__row-key",
2983
+ children: "SecretId"
2984
+ }), react_jsx_runtime.jsx("span", {
2985
+ className: "dsh-cloudq-settings-card__row-val",
2986
+ children: status.secret_id_masked
2987
+ })]
2988
+ }) : null,
2989
+ status?.tmp_key_remaining_minutes != null ? react_jsx_runtime.jsx("div", {
2990
+ className: "dsh-cloudq-settings-card__row",
2991
+ children: [react_jsx_runtime.jsx("span", {
2992
+ className: "dsh-cloudq-settings-card__row-key",
2993
+ children: "临时密钥剩余"
2994
+ }), react_jsx_runtime.jsx("span", {
2995
+ className: "dsh-cloudq-settings-card__row-val",
2996
+ children: minutesText(status.tmp_key_remaining_minutes)
2997
+ })]
2998
+ }) : null,
2999
+ status?.access_token_remaining_minutes != null ? react_jsx_runtime.jsx("div", {
3000
+ className: "dsh-cloudq-settings-card__row",
3001
+ children: [react_jsx_runtime.jsx("span", {
3002
+ className: "dsh-cloudq-settings-card__row-key",
3003
+ children: "AccessToken 剩余"
3004
+ }), react_jsx_runtime.jsx("span", {
3005
+ className: "dsh-cloudq-settings-card__row-val",
3006
+ children: minutesText(status.access_token_remaining_minutes)
3007
+ })]
3008
+ }) : null,
3009
+ status?.refresh_error ? react_jsx_runtime.jsx("div", {
3010
+ className: "dsh-cloudq-settings-card__row",
3011
+ children: [react_jsx_runtime.jsx("span", {
3012
+ className: "dsh-cloudq-settings-card__row-key",
3013
+ children: "刷新错误"
3014
+ }), react_jsx_runtime.jsx("span", {
3015
+ className: "dsh-cloudq-settings-card__row-val",
3016
+ children: status.refresh_error.message
3017
+ })]
3018
+ }) : null
3019
+ ]
3020
+ }),
3021
+ feedback ? react_jsx_runtime.jsx("div", {
3022
+ className: `dsh-cloudq-settings-card__feedback dsh-cloudq-settings-card__feedback--${feedback.kind}`,
3023
+ children: feedback.text
3024
+ }) : null,
3025
+ !loggedIn && react_jsx_runtime.jsx("div", { children: [
3026
+ react_jsx_runtime.jsx("div", {
3027
+ className: "dsh-cloudq-settings-card__actions",
3028
+ children: [react_jsx_runtime.jsx("button", {
3029
+ type: "button",
3030
+ className: "dsh-cloudq-settings-card__btn dsh-cloudq-settings-card__btn--primary",
3031
+ disabled,
3032
+ onClick: handleGetAuthorizeUrl,
3033
+ children: busy === "url" ? "获取中…" : "获取授权链接"
3034
+ })]
3035
+ }),
3036
+ authorizeUrl && react_jsx_runtime.jsx("div", {
3037
+ className: "dsh-cloudq-settings-card__hint",
3038
+ children: "已在新标签页打开授权页。授权完成后,请将页面显示的授权码粘贴到下方:"
3039
+ }),
3040
+ react_jsx_runtime.jsx("div", {
3041
+ className: "dsh-cloudq-settings-card__actions dsh-cloudq-settings-card__actions--stacked",
3042
+ children: [react_jsx_runtime.jsx("input", {
3043
+ className: "dsh-cloudq-settings-card__input",
3044
+ placeholder: "粘贴授权码",
3045
+ value: code,
3046
+ onChange: (event) => setCode(event.target.value)
3047
+ }), react_jsx_runtime.jsx("button", {
3048
+ type: "button",
3049
+ className: "dsh-cloudq-settings-card__btn",
3050
+ disabled,
3051
+ onClick: handleSaveCode,
3052
+ children: busy === "save" ? "登录中…" : "完成登录"
3053
+ })]
3054
+ }),
3055
+ react_jsx_runtime.jsx("div", {
3056
+ className: "dsh-cloudq-settings-card__field",
3057
+ children: [
3058
+ react_jsx_runtime.jsx("span", {
3059
+ className: "dsh-cloudq-settings-card__label",
3060
+ children: ["SecretId", react_jsx_runtime.jsx("span", {
3061
+ className: "dsh-cloudq-settings-card__required",
3062
+ children: "必填"
3063
+ })]
3064
+ }),
3065
+ react_jsx_runtime.jsx("span", {
3066
+ className: "dsh-cloudq-settings-card__hint",
3067
+ children: "腾讯云 API 密钥 ID,与 SecretKey 配对使用。密钥只会安全保存到 DSH Host 端。"
3068
+ }),
3069
+ react_jsx_runtime.jsx("input", {
3070
+ className: "dsh-cloudq-settings-card__input",
3071
+ placeholder: "请输入 SecretId",
3072
+ autoComplete: "off",
3073
+ spellCheck: false,
3074
+ value: secretId,
3075
+ onChange: (event) => setSecretId(event.target.value)
3076
+ })
3077
+ ]
3078
+ }),
3079
+ react_jsx_runtime.jsx("div", {
3080
+ className: "dsh-cloudq-settings-card__field",
3081
+ children: [
3082
+ react_jsx_runtime.jsx("span", {
3083
+ className: "dsh-cloudq-settings-card__label",
3084
+ children: ["SecretKey", react_jsx_runtime.jsx("span", {
3085
+ className: "dsh-cloudq-settings-card__required",
3086
+ children: "必填"
3087
+ })]
3088
+ }),
3089
+ react_jsx_runtime.jsx("span", {
3090
+ className: "dsh-cloudq-settings-card__hint",
3091
+ children: [
3092
+ "腾讯云 API 密钥 Key。保存后页面不会读取或回显原文。可前往 ",
3093
+ react_jsx_runtime.jsx("a", {
3094
+ href: CAPI_CONSOLE_URL,
3095
+ target: "_blank",
3096
+ rel: "noopener noreferrer",
3097
+ children: "访问密钥控制台"
3098
+ }),
3099
+ " 获取。"
3100
+ ]
3101
+ }),
3102
+ react_jsx_runtime.jsx("input", {
3103
+ className: "dsh-cloudq-settings-card__input",
3104
+ type: "password",
3105
+ placeholder: "请输入 SecretKey",
3106
+ autoComplete: "new-password",
3107
+ spellCheck: false,
3108
+ value: secretKey,
3109
+ onChange: (event) => setSecretKey(event.target.value)
3110
+ })
3111
+ ]
3112
+ }),
3113
+ react_jsx_runtime.jsx("div", {
3114
+ className: "dsh-cloudq-settings-card__form-actions",
3115
+ children: [react_jsx_runtime.jsx("button", {
3116
+ type: "button",
3117
+ className: "dsh-cloudq-settings-card__btn",
3118
+ disabled,
3119
+ onClick: handleTestConnection,
3120
+ children: busy === "test" ? "测试中…" : "测试连接"
3121
+ }), react_jsx_runtime.jsx("button", {
3122
+ type: "button",
3123
+ className: "dsh-cloudq-settings-card__btn dsh-cloudq-settings-card__btn--primary",
3124
+ disabled,
3125
+ onClick: handleSaveAccessKey,
3126
+ children: busy === "save-ak" ? "保存中…" : "保存配置"
3127
+ })]
3128
+ })
3129
+ ] }),
3130
+ loggedIn && react_jsx_runtime.jsx("div", {
3131
+ className: "dsh-cloudq-settings-card__actions dsh-cloudq-settings-card__actions--stacked",
3132
+ children: react_jsx_runtime.jsx("button", {
3133
+ type: "button",
3134
+ className: "dsh-cloudq-settings-card__btn dsh-cloudq-settings-card__btn--danger",
3135
+ disabled,
3136
+ onClick: handleLogout,
3137
+ children: busy === "logout" ? "登出中…" : "退出登录"
3138
+ })
3139
+ })
3140
+ ]
3141
+ })]
3142
+ });
3143
+ }
3144
+ const API_PLUGINS = "/api/dsh-cloudq/plugins";
3145
+ const API_PLUGINS_TOGGLE = "/api/dsh-cloudq/plugins/toggle";
3146
+ function PluginManagerCard() {
3147
+ const [expanded, setExpanded] = react.useState(true);
3148
+ const [loading, setLoading] = react.useState(true);
3149
+ const [busyId, setBusyId] = react.useState();
3150
+ const [plugins, setPlugins] = react.useState([]);
3151
+ const [feedback, setFeedback] = react.useState();
3152
+ const [pendingReload, setPendingReload] = react.useState(null);
3153
+ const [reloadCountdown, setReloadCountdown] = react.useState(null);
3154
+ const refresh = react.useCallback(async () => {
3155
+ setLoading(true);
3156
+ try {
3157
+ const response = await cloudqRequest(API_PLUGINS);
3158
+ setPlugins(response.plugins ?? []);
3159
+ } catch (error) {
3160
+ setFeedback({
3161
+ kind: "error",
3162
+ text: error.message
3163
+ });
3164
+ } finally {
3165
+ setLoading(false);
3166
+ }
3167
+ }, []);
3168
+ react.useEffect(() => {
3169
+ refresh();
3170
+ }, [refresh]);
3171
+ const scheduleReload = react.useCallback((pluginName) => {
3172
+ setPendingReload({
3173
+ name: pluginName,
3174
+ until: Date.now() + 6e3
3175
+ });
3176
+ }, []);
3177
+ react.useEffect(() => {
3178
+ if (!pendingReload) return void 0;
3179
+ const tick = () => {
3180
+ const left = Math.max(0, pendingReload.until - Date.now());
3181
+ if (left === 0) {
3182
+ window.location.reload();
3183
+ return;
3184
+ }
3185
+ setReloadCountdown(Math.ceil(left / 1e3));
3186
+ };
3187
+ tick();
3188
+ const timer = window.setInterval(tick, 250);
3189
+ return () => window.clearInterval(timer);
3190
+ }, [pendingReload]);
3191
+ const handleToggle = async (plugin) => {
3192
+ const target = !plugin.disabled;
3193
+ setBusyId(plugin.id);
3194
+ setFeedback(void 0);
3195
+ try {
3196
+ await cloudqRequest(API_PLUGINS_TOGGLE, {
3197
+ method: "POST",
3198
+ body: JSON.stringify({
3199
+ id: plugin.id,
3200
+ disabled: target
3201
+ })
3202
+ });
3203
+ setFeedback({
3204
+ kind: "success",
3205
+ text: target ? `已停用 ${plugin.name}:后端热生效,浏览器端将在 ${reloadCountdown ?? "数"} 秒后自动刷新以清理 client UI。` : `已启用 ${plugin.name}:后端热生效,浏览器端将在 ${reloadCountdown ?? "数"} 秒后自动刷新以加载 client UI。`
3206
+ });
3207
+ await refresh();
3208
+ scheduleReload(plugin.name);
3209
+ } catch (error) {
3210
+ setFeedback({
3211
+ kind: "error",
3212
+ text: error.message
3213
+ });
3214
+ } finally {
3215
+ setBusyId(void 0);
3216
+ }
3217
+ };
3218
+ const disabledCount = plugins.filter((p) => p.disabled).length;
3219
+ const summarySubtitle = `热启停 profile 静态插件,保存即生效,无需重启。`;
3220
+ const badgeText = disabledCount > 0 ? `${disabledCount} 个已停用` : "全部启用";
3221
+ const badgeClass = disabledCount > 0 ? "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--warn" : "dsh-cloudq-settings-card__badge dsh-cloudq-settings-card__badge--ok";
3222
+ return react_jsx_runtime.jsx("section", {
3223
+ className: "dsh-cloudq-settings-card",
3224
+ children: [react_jsx_runtime.jsx("div", {
3225
+ className: "dsh-cloudq-settings-card__summary",
3226
+ children: react_jsx_runtime.jsx("button", {
3227
+ type: "button",
3228
+ className: "dsh-cloudq-settings-card__summary-trigger",
3229
+ "aria-expanded": expanded,
3230
+ onClick: () => setExpanded((prev) => !prev),
3231
+ children: [react_jsx_runtime.jsx("span", {
3232
+ className: "dsh-cloudq-settings-card__summary-title",
3233
+ children: [react_jsx_runtime.jsx("strong", { children: "插件管理" }), react_jsx_runtime.jsx("small", { children: summarySubtitle })]
3234
+ }), react_jsx_runtime.jsx("span", {
3235
+ className: "dsh-cloudq-settings-card__summary-right",
3236
+ children: [react_jsx_runtime.jsx("span", {
3237
+ className: badgeClass,
3238
+ children: [react_jsx_runtime.jsx("span", { className: "dsh-cloudq-settings-card__badge-dot" }), badgeText]
3239
+ }), react_jsx_runtime.jsx("span", {
3240
+ className: `dsh-cloudq-settings-card__chevron${expanded ? " is-open" : ""}`,
3241
+ children: "▾"
3242
+ })]
3243
+ })]
3244
+ })
3245
+ }), !expanded ? null : react_jsx_runtime.jsx("div", {
3246
+ className: "dsh-cloudq-settings-card__body",
3247
+ children: [
3248
+ pendingReload ? react_jsx_runtime.jsx("div", {
3249
+ className: "dsh-cloudq-pm__reload",
3250
+ children: [
3251
+ react_jsx_runtime.jsx("span", {
3252
+ className: "dsh-cloudq-pm__reload-text",
3253
+ children: `已切换 ${pendingReload.name},浏览器将自动刷新以应用 client 端(${reloadCountdown ?? 6}s)`
3254
+ }),
3255
+ react_jsx_runtime.jsx("button", {
3256
+ type: "button",
3257
+ className: "dsh-cloudq-pm__reload-cancel",
3258
+ onClick: () => setPendingReload(null),
3259
+ children: "取消"
3260
+ }),
3261
+ react_jsx_runtime.jsx("button", {
3262
+ type: "button",
3263
+ className: "dsh-cloudq-pm__reload-now",
3264
+ onClick: () => window.location.reload(),
3265
+ children: "立即刷新"
3266
+ })
3267
+ ]
3268
+ }) : null,
3269
+ loading ? react_jsx_runtime.jsx("div", {
3270
+ className: "dsh-cloudq-settings-card__hint",
3271
+ children: "正在读取插件清单…"
3272
+ }) : react_jsx_runtime.jsx("div", {
3273
+ className: "dsh-cloudq-pm__rows",
3274
+ children: plugins.map((plugin) => {
3275
+ const isBusy = busyId === plugin.id;
3276
+ const on = !plugin.disabled;
3277
+ return react_jsx_runtime.jsx("div", {
3278
+ className: "dsh-cloudq-pm__row",
3279
+ children: [
3280
+ react_jsx_runtime.jsx("div", {
3281
+ className: "dsh-cloudq-pm__row-info",
3282
+ children: [react_jsx_runtime.jsx("div", {
3283
+ className: "dsh-cloudq-pm__row-name",
3284
+ children: [
3285
+ plugin.name,
3286
+ plugin.self ? react_jsx_runtime.jsx("span", {
3287
+ className: "dsh-cloudq-pm__row-tag dsh-cloudq-pm__row-tag--self",
3288
+ children: "本插件"
3289
+ }) : null,
3290
+ !on ? react_jsx_runtime.jsx("span", {
3291
+ className: "dsh-cloudq-pm__row-tag dsh-cloudq-pm__row-tag--off",
3292
+ children: "已停用"
3293
+ }) : null
3294
+ ]
3295
+ }), react_jsx_runtime.jsx("div", {
3296
+ className: "dsh-cloudq-pm__row-bundle",
3297
+ children: `${plugin.id} · ${plugin.bundle}`
3298
+ })]
3299
+ }),
3300
+ react_jsx_runtime.jsx("button", {
3301
+ type: "button",
3302
+ role: "switch",
3303
+ "aria-checked": on,
3304
+ "aria-label": `${on ? "停用" : "启用"} ${plugin.name}`,
3305
+ className: `dsh-cloudq-pm__switch${on ? " dsh-cloudq-pm__switch--on" : ""}`,
3306
+ disabled: isBusy,
3307
+ onClick: () => handleToggle(plugin)
3308
+ }),
3309
+ react_jsx_runtime.jsx("span", {
3310
+ className: `dsh-cloudq-pm__state${!on ? " dsh-cloudq-pm__state--off" : ""}`,
3311
+ children: isBusy ? "…" : on ? "启用" : "停用"
3312
+ })
3313
+ ]
3314
+ }, plugin.id);
3315
+ })
3316
+ }),
3317
+ feedback ? react_jsx_runtime.jsx("div", {
3318
+ className: `dsh-cloudq-settings-card__feedback dsh-cloudq-settings-card__feedback--${feedback.kind}`,
3319
+ children: feedback.text
3320
+ }) : null,
3321
+ react_jsx_runtime.jsx("div", {
3322
+ className: "dsh-cloudq-settings-card__hint",
3323
+ children: "核心层(dsh-base / dsh-web-app)不可停用。停用本插件后此卡片将消失,重新启用需手动编辑 cordis.patch.yml。"
3324
+ })
3325
+ ]
3326
+ })]
3327
+ });
3328
+ }
3329
+ const inject = [
3330
+ "slots",
3331
+ "workspaces",
3332
+ "sessions",
3333
+ "remote",
3334
+ "remote.commands"
3335
+ ];
3336
+ function apply(ctx) {
3337
+ ctx.effect(() => {
3338
+ cloudqCtx = ctx;
3339
+ return () => {
3340
+ if (cloudqCtx === ctx) cloudqCtx = null;
3341
+ };
3342
+ }, "dsh-cloudq: client context");
3343
+ ctx.effect(() => {
3344
+ let disposed = false;
3345
+ const syncHistory = () => {
3346
+ if (disposed) return;
3347
+ reconcileCloudqSessionsFromHost();
3348
+ };
3349
+ const onStorage = (event) => {
3350
+ if (event.key !== CLOUDQ_STORAGE_KEY) return;
3351
+ cloudqSessions = loadCloudqSessions();
3352
+ window.dispatchEvent(new CustomEvent("dsh-cloudq:sessions-changed"));
3353
+ };
3354
+ window.addEventListener("storage", onStorage);
3355
+ syncHistory();
3356
+ return () => {
3357
+ disposed = true;
3358
+ window.removeEventListener("storage", onStorage);
3359
+ };
3360
+ }, "dsh-cloudq: reconcile durable session identities");
3361
+ ctx.effect(installStyles, "dsh-cloudq: styles");
3362
+ ctx.effect(() => {
3363
+ const slots = ctx.slots;
3364
+ return slots.inject("conversation.input.left", () => slots.register({
3365
+ name: "conversation.input.left",
3366
+ id: "dsh-cloudq.mode",
3367
+ order: 500
3368
+ }, CloudQButton));
3369
+ }, "dsh-cloudq: input-bar CloudQ status seat");
3370
+ ctx.effect(() => {
3371
+ const slots = ctx.slots;
3372
+ return slots.inject("conversation.input.left", () => slots.register({
3373
+ name: "conversation.input.left",
3374
+ id: "dsh-cloudq.bridge",
3375
+ order: -1e3
3376
+ }, CloudQBridge));
3377
+ }, "dsh-cloudq: draft-event bridge");
3378
+ ctx.effect(() => {
3379
+ const slots = ctx.slots;
3380
+ return slots.inject("settings.plugin.item", () => slots.register({
3381
+ name: "settings.plugin.item",
3382
+ id: "dsh-cloudq",
3383
+ key: "dsh-cloudq",
3384
+ order: 100
3385
+ }, CloudQSettingsCard));
3386
+ }, "dsh-cloudq: settings card");
3387
+ ctx.effect(() => {
3388
+ const slots = ctx.slots;
3389
+ return slots.inject("settings.plugin.item", () => slots.register({
3390
+ name: "settings.plugin.item",
3391
+ id: "dsh-cloudq.plugin-manager",
3392
+ key: "dsh-plugin-manager",
3393
+ order: 110
3394
+ }, PluginManagerCard));
3395
+ }, "dsh-cloudq: plugin manager card");
3396
+ ctx.effect(installSidebarButton, "dsh-cloudq: sidebar CloudQ entry");
3397
+ ctx.effect(installSessionBadges, "dsh-cloudq: session list badges");
3398
+ ctx.effect(installCloudqHero, "dsh-cloudq: hero chrome swap");
3399
+ }
3400
+ exports.apply = apply;
3401
+ exports.inject = inject;
3402
+ //#endregion
3403
+ return module.exports;
3404
+ }
3405
+ });