@claude-canvas/core 0.2.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/dist/index.js ADDED
@@ -0,0 +1,1433 @@
1
+ // src/shadow-container.ts
2
+ var DESIGN_TOKENS = `
3
+ :host {
4
+ --cc-bg-panel: rgba(30, 30, 30, 0.95);
5
+ --cc-bg-toolbar: rgba(40, 40, 40, 0.92);
6
+ --cc-bg-input: rgba(50, 50, 50, 1);
7
+ --cc-bg-hover: rgba(60, 60, 60, 1);
8
+ --cc-text-primary: #e0e0e0;
9
+ --cc-text-secondary: #a0a0a0;
10
+ --cc-text-muted: #707070;
11
+ --cc-annotation-box: #ff4444;
12
+ --cc-annotation-box-fill: rgba(255, 68, 68, 0.1);
13
+ --cc-highlight-hover: rgba(66, 133, 244, 0.3);
14
+ --cc-highlight-select: rgba(255, 152, 0, 0.4);
15
+ --cc-accent: #7c4dff;
16
+ --cc-success: #4caf50;
17
+ --cc-warning: #ff9800;
18
+ --cc-error: #f44336;
19
+ --cc-info: #2196f3;
20
+ --cc-border: rgba(255, 255, 255, 0.12);
21
+ --cc-border-focus: rgba(124, 77, 255, 0.6);
22
+ --cc-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
23
+ --cc-font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
24
+ --cc-font-size-xs: 11px;
25
+ --cc-font-size-sm: 12px;
26
+ --cc-font-size-md: 13px;
27
+ --cc-font-size-lg: 14px;
28
+ --cc-line-height: 1.4;
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ .cc-container {
38
+ font-family: var(--cc-font-family);
39
+ font-size: var(--cc-font-size-md);
40
+ color: var(--cc-text-primary);
41
+ line-height: var(--cc-line-height);
42
+ }
43
+ `;
44
+ var ShadowContainer = class {
45
+ host;
46
+ shadow;
47
+ container;
48
+ mounted = false;
49
+ constructor() {
50
+ this.host = document.createElement("div");
51
+ this.host.id = "claude-canvas-root";
52
+ this.shadow = this.host.attachShadow({ mode: "open" });
53
+ const style = document.createElement("style");
54
+ style.textContent = DESIGN_TOKENS;
55
+ this.shadow.appendChild(style);
56
+ this.container = document.createElement("div");
57
+ this.container.className = "cc-container";
58
+ this.shadow.appendChild(this.container);
59
+ }
60
+ mount() {
61
+ if (this.mounted) return;
62
+ document.body.appendChild(this.host);
63
+ this.mounted = true;
64
+ }
65
+ unmount() {
66
+ if (!this.mounted) return;
67
+ this.host.remove();
68
+ this.mounted = false;
69
+ }
70
+ getContainer() {
71
+ return this.container;
72
+ }
73
+ getShadowRoot() {
74
+ return this.shadow;
75
+ }
76
+ };
77
+
78
+ // src/toast.ts
79
+ var TOAST_STYLES = `
80
+ .cc-toast-container {
81
+ position: fixed;
82
+ bottom: 16px;
83
+ left: 16px;
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 8px;
87
+ z-index: 10000;
88
+ pointer-events: none;
89
+ }
90
+
91
+ .cc-toast {
92
+ padding: 10px 16px;
93
+ border-radius: 6px;
94
+ font-family: var(--cc-font-family);
95
+ font-size: var(--cc-font-size-sm);
96
+ color: #fff;
97
+ pointer-events: auto;
98
+ animation: cc-toast-in 0.2s ease-out;
99
+ }
100
+
101
+ .cc-toast--success { background: var(--cc-success); }
102
+ .cc-toast--warning { background: var(--cc-warning); }
103
+ .cc-toast--error { background: var(--cc-error); }
104
+ .cc-toast--info { background: var(--cc-info); }
105
+
106
+ @keyframes cc-toast-in {
107
+ from { opacity: 0; transform: translateY(8px); }
108
+ to { opacity: 1; transform: translateY(0); }
109
+ }
110
+ `;
111
+ var Toast = class {
112
+ shadowRoot;
113
+ toastContainer;
114
+ styleInjected = false;
115
+ constructor(shadowRoot) {
116
+ this.shadowRoot = shadowRoot;
117
+ this.toastContainer = document.createElement("div");
118
+ this.toastContainer.className = "cc-toast-container";
119
+ }
120
+ ensureStyle() {
121
+ if (this.styleInjected) return;
122
+ const style = document.createElement("style");
123
+ style.textContent = TOAST_STYLES;
124
+ this.shadowRoot.appendChild(style);
125
+ this.shadowRoot.appendChild(this.toastContainer);
126
+ this.styleInjected = true;
127
+ }
128
+ show(message, variant) {
129
+ this.ensureStyle();
130
+ const el = document.createElement("div");
131
+ el.className = `cc-toast cc-toast--${variant}`;
132
+ el.textContent = message;
133
+ this.toastContainer.appendChild(el);
134
+ setTimeout(() => {
135
+ el.remove();
136
+ }, 3e3);
137
+ }
138
+ hide() {
139
+ const toasts = this.toastContainer.querySelectorAll(".cc-toast");
140
+ for (const t of toasts) {
141
+ t.remove();
142
+ }
143
+ }
144
+ };
145
+
146
+ // src/keyboard-handler.ts
147
+ var KEY_TO_MODE = {
148
+ "1": "select",
149
+ "2": "box",
150
+ "3": "memo",
151
+ "4": "capture"
152
+ };
153
+ var KeyboardHandler = class {
154
+ callbacks;
155
+ enabled = true;
156
+ listener = null;
157
+ constructor(callbacks) {
158
+ this.callbacks = callbacks;
159
+ }
160
+ mount() {
161
+ this.listener = (e) => {
162
+ if (!this.enabled) return;
163
+ if (e.ctrlKey && e.shiftKey && e.key === "A") {
164
+ e.preventDefault();
165
+ this.callbacks.onToggleMode();
166
+ return;
167
+ }
168
+ if (e.key === "Escape") {
169
+ this.callbacks.onEscape();
170
+ return;
171
+ }
172
+ if (e.key === "Enter") {
173
+ this.callbacks.onSend();
174
+ return;
175
+ }
176
+ const mode = KEY_TO_MODE[e.key];
177
+ if (mode) {
178
+ this.callbacks.onToolSwitch(mode);
179
+ }
180
+ };
181
+ window.addEventListener("keydown", this.listener, true);
182
+ }
183
+ unmount() {
184
+ if (this.listener) {
185
+ window.removeEventListener("keydown", this.listener, true);
186
+ this.listener = null;
187
+ }
188
+ }
189
+ setEnabled(enabled) {
190
+ this.enabled = enabled;
191
+ }
192
+ };
193
+
194
+ // src/panels/fab-button.ts
195
+ var FAB_ICON = `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
196
+ <rect x="2" y="2" width="16" height="16" rx="3" stroke="currentColor" stroke-width="1.5" fill="none"/>
197
+ <line x1="6" y1="7" x2="14" y2="7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
198
+ <line x1="6" y1="10" x2="12" y2="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
199
+ <line x1="6" y1="13" x2="10" y2="13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
200
+ </svg>`;
201
+ var FAB_STYLES = `
202
+ .cc-fab-button {
203
+ position: fixed;
204
+ bottom: 24px;
205
+ right: 24px;
206
+ width: 40px;
207
+ height: 40px;
208
+ border-radius: 50%;
209
+ border: none;
210
+ background: var(--cc-accent, #7c4dff);
211
+ color: #fff;
212
+ cursor: pointer;
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: center;
216
+ z-index: 99999;
217
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
218
+ transition: background 0.15s ease, transform 0.15s ease;
219
+ }
220
+
221
+ .cc-fab-button:hover {
222
+ background: var(--cc-border-focus, rgba(124, 77, 255, 0.8));
223
+ transform: scale(1.08);
224
+ }
225
+
226
+ .cc-fab-tooltip {
227
+ position: fixed;
228
+ bottom: 72px;
229
+ right: 16px;
230
+ background: var(--cc-bg-panel, rgba(30, 30, 30, 0.95));
231
+ color: var(--cc-text-primary, #e0e0e0);
232
+ font-family: var(--cc-font-family, sans-serif);
233
+ font-size: var(--cc-font-size-xs, 11px);
234
+ padding: 4px 8px;
235
+ border-radius: 4px;
236
+ white-space: nowrap;
237
+ pointer-events: none;
238
+ z-index: 99999;
239
+ }
240
+ `;
241
+ var FabButton = class {
242
+ shadowRoot;
243
+ options;
244
+ button = null;
245
+ tooltip = null;
246
+ styleEl = null;
247
+ mounted = false;
248
+ constructor(shadowRoot, options) {
249
+ this.shadowRoot = shadowRoot;
250
+ this.options = options;
251
+ }
252
+ mount() {
253
+ if (this.mounted) return;
254
+ this.styleEl = document.createElement("style");
255
+ this.styleEl.textContent = FAB_STYLES;
256
+ this.shadowRoot.appendChild(this.styleEl);
257
+ this.button = document.createElement("button");
258
+ this.button.className = "cc-fab-button";
259
+ this.button.innerHTML = FAB_ICON;
260
+ this.button.style.position = "fixed";
261
+ this.button.style.bottom = "24px";
262
+ this.button.style.right = "24px";
263
+ this.button.style.width = "40px";
264
+ this.button.style.height = "40px";
265
+ this.button.style.borderRadius = "50%";
266
+ this.button.style.zIndex = "99999";
267
+ this.button.addEventListener("click", this.handleClick);
268
+ this.button.addEventListener("mouseenter", this.handleMouseEnter);
269
+ this.button.addEventListener("mouseleave", this.handleMouseLeave);
270
+ this.shadowRoot.appendChild(this.button);
271
+ this.mounted = true;
272
+ }
273
+ unmount() {
274
+ if (!this.mounted) return;
275
+ this.button?.removeEventListener("click", this.handleClick);
276
+ this.button?.removeEventListener("mouseenter", this.handleMouseEnter);
277
+ this.button?.removeEventListener("mouseleave", this.handleMouseLeave);
278
+ this.button?.remove();
279
+ this.styleEl?.remove();
280
+ this.hideTooltip();
281
+ this.button = null;
282
+ this.styleEl = null;
283
+ this.mounted = false;
284
+ }
285
+ setVisible(visible) {
286
+ if (!this.button) return;
287
+ this.button.style.display = visible ? "flex" : "none";
288
+ if (!visible) {
289
+ this.hideTooltip();
290
+ }
291
+ }
292
+ handleClick = () => {
293
+ this.options.onActivate();
294
+ };
295
+ handleMouseEnter = () => {
296
+ this.showTooltip();
297
+ };
298
+ handleMouseLeave = () => {
299
+ this.hideTooltip();
300
+ };
301
+ showTooltip() {
302
+ if (this.tooltip) return;
303
+ this.tooltip = document.createElement("div");
304
+ this.tooltip.className = "cc-fab-tooltip";
305
+ this.tooltip.textContent = "Ctrl+Shift+A";
306
+ this.shadowRoot.appendChild(this.tooltip);
307
+ }
308
+ hideTooltip() {
309
+ this.tooltip?.remove();
310
+ this.tooltip = null;
311
+ }
312
+ };
313
+
314
+ // src/panels/chat-panel.ts
315
+ var CHAT_PANEL_STYLES = `
316
+ .cc-chat-panel {
317
+ position: fixed;
318
+ top: 0;
319
+ right: 0;
320
+ width: 360px;
321
+ height: 100vh;
322
+ background: var(--cc-bg-panel, rgba(30, 30, 30, 0.95));
323
+ border-left: 1px solid var(--cc-border, rgba(255, 255, 255, 0.1));
324
+ display: flex;
325
+ flex-direction: column;
326
+ z-index: 100000;
327
+ transition: transform 0.2s ease;
328
+ font-family: var(--cc-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
329
+ }
330
+
331
+ .cc-chat-panel.collapsed {
332
+ transform: translateX(100%);
333
+ }
334
+
335
+ .cc-chat-header {
336
+ display: flex;
337
+ justify-content: space-between;
338
+ align-items: center;
339
+ padding: 8px 12px;
340
+ border-bottom: 1px solid var(--cc-border, rgba(255, 255, 255, 0.1));
341
+ }
342
+
343
+ .cc-chat-title {
344
+ font-weight: 600;
345
+ font-size: var(--cc-font-size-lg, 14px);
346
+ color: var(--cc-text-primary, #e0e0e0);
347
+ }
348
+
349
+ .cc-collapse-btn {
350
+ background: none;
351
+ border: none;
352
+ color: var(--cc-text-primary, #e0e0e0);
353
+ cursor: pointer;
354
+ padding: 4px 8px;
355
+ font-size: 12px;
356
+ transition: color 0.15s ease;
357
+ }
358
+
359
+ .cc-collapse-btn:hover {
360
+ color: var(--cc-accent, #7c4dff);
361
+ }
362
+
363
+ .cc-message-list {
364
+ flex: 1;
365
+ overflow-y: auto;
366
+ padding: 8px;
367
+ }
368
+
369
+ .cc-message {
370
+ margin-bottom: 8px;
371
+ padding: 8px;
372
+ border-radius: 6px;
373
+ font-size: var(--cc-font-size-md, 12px);
374
+ word-break: break-word;
375
+ }
376
+
377
+ .cc-user-message {
378
+ background: var(--cc-bg-hover, rgba(255, 255, 255, 0.05));
379
+ color: var(--cc-text-primary, #e0e0e0);
380
+ }
381
+
382
+ .cc-assistant-message {
383
+ background: rgba(124, 77, 255, 0.1);
384
+ color: var(--cc-text-primary, #e0e0e0);
385
+ }
386
+
387
+ .cc-system-message {
388
+ background: var(--cc-bg-toolbar, rgba(255, 255, 255, 0.02));
389
+ font-size: var(--cc-font-size-sm, 11px);
390
+ color: var(--cc-text-secondary, #999);
391
+ }
392
+
393
+ .cc-message-content {
394
+ margin-bottom: 4px;
395
+ }
396
+
397
+ .cc-files-changed {
398
+ margin-top: 6px;
399
+ padding-top: 6px;
400
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
401
+ font-size: var(--cc-font-size-sm, 11px);
402
+ }
403
+
404
+ .cc-file-change {
405
+ margin-bottom: 4px;
406
+ display: flex;
407
+ gap: 6px;
408
+ align-items: center;
409
+ }
410
+
411
+ .cc-file-action {
412
+ padding: 2px 6px;
413
+ border-radius: 3px;
414
+ font-size: 10px;
415
+ font-weight: 600;
416
+ min-width: 40px;
417
+ text-align: center;
418
+ }
419
+
420
+ .cc-file-action {
421
+ background: rgba(124, 77, 255, 0.2);
422
+ color: #7c4dff;
423
+ }
424
+
425
+ .cc-annotation-card {
426
+ margin-top: 6px;
427
+ padding: 6px;
428
+ background: rgba(255, 255, 255, 0.05);
429
+ border-left: 2px solid #7c4dff;
430
+ border-radius: 3px;
431
+ font-size: var(--cc-font-size-sm, 11px);
432
+ }
433
+
434
+ .cc-annotation-card > div {
435
+ margin-bottom: 3px;
436
+ color: #b39ddb;
437
+ }
438
+
439
+ .cc-annotation-card > div:last-child {
440
+ margin-bottom: 0;
441
+ }
442
+
443
+ .cc-chat-footer {
444
+ padding: 8px;
445
+ border-top: 1px solid var(--cc-border, rgba(255, 255, 255, 0.1));
446
+ display: flex;
447
+ gap: 4px;
448
+ }
449
+
450
+ .cc-chat-input {
451
+ flex: 1;
452
+ background: var(--cc-bg-input, rgba(255, 255, 255, 0.05));
453
+ color: var(--cc-text-primary, #e0e0e0);
454
+ border: 1px solid var(--cc-border, rgba(255, 255, 255, 0.1));
455
+ border-radius: 4px;
456
+ padding: 6px 8px;
457
+ font-size: var(--cc-font-size-md, 12px);
458
+ font-family: var(--cc-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
459
+ resize: none;
460
+ transition: border-color 0.15s ease;
461
+ }
462
+
463
+ .cc-chat-input:focus {
464
+ outline: none;
465
+ border-color: var(--cc-accent, #7c4dff);
466
+ }
467
+
468
+ .cc-send-btn {
469
+ width: 32px;
470
+ height: 32px;
471
+ background: var(--cc-accent, #7c4dff);
472
+ color: white;
473
+ border: none;
474
+ border-radius: 4px;
475
+ cursor: pointer;
476
+ display: flex;
477
+ align-items: center;
478
+ justify-content: center;
479
+ font-size: 14px;
480
+ transition: background 0.15s ease, transform 0.1s ease;
481
+ }
482
+
483
+ .cc-send-btn:hover {
484
+ background: rgba(124, 77, 255, 0.8);
485
+ }
486
+
487
+ .cc-send-btn:active {
488
+ transform: scale(0.95);
489
+ }
490
+
491
+ .cc-send-btn:disabled {
492
+ opacity: 0.5;
493
+ cursor: not-allowed;
494
+ }
495
+ `;
496
+ var ChatPanel = class {
497
+ container;
498
+ messageList;
499
+ inputArea;
500
+ sendButton;
501
+ messages = [];
502
+ collapsed = false;
503
+ onSend;
504
+ constructor(shadowRoot) {
505
+ const styleEl = document.createElement("style");
506
+ styleEl.textContent = CHAT_PANEL_STYLES;
507
+ shadowRoot.appendChild(styleEl);
508
+ this.container = document.createElement("div");
509
+ this.container.className = "cc-chat-panel";
510
+ this.container.innerHTML = `
511
+ <div class="cc-chat-header">
512
+ <span class="cc-chat-title">Claude Canvas</span>
513
+ <button class="cc-collapse-btn" aria-label="Toggle panel">\u25C0</button>
514
+ </div>
515
+ <div class="cc-message-list"></div>
516
+ <div class="cc-chat-footer">
517
+ <textarea class="cc-chat-input" placeholder="\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694..." rows="2"></textarea>
518
+ <button class="cc-send-btn" aria-label="Send">\u25B6</button>
519
+ </div>
520
+ `;
521
+ shadowRoot.appendChild(this.container);
522
+ this.messageList = this.container.querySelector(".cc-message-list");
523
+ this.inputArea = this.container.querySelector(".cc-chat-input");
524
+ this.sendButton = this.container.querySelector(".cc-send-btn");
525
+ const collapseBtn = this.container.querySelector(".cc-collapse-btn");
526
+ collapseBtn?.addEventListener("click", () => this.toggle());
527
+ this.sendButton.addEventListener("click", () => this.handleSend());
528
+ this.inputArea.addEventListener("keydown", (e) => {
529
+ if (e.key === "Enter" && !e.shiftKey) {
530
+ e.preventDefault();
531
+ this.handleSend();
532
+ }
533
+ });
534
+ }
535
+ addMessage(msg) {
536
+ this.messages.push(msg);
537
+ const el = this.renderMessage(msg);
538
+ this.messageList.appendChild(el);
539
+ this.messageList.scrollTop = this.messageList.scrollHeight;
540
+ }
541
+ appendStream(delta) {
542
+ const last = this.messageList.lastElementChild;
543
+ if (last?.classList.contains("cc-assistant-message")) {
544
+ const content = last.querySelector(".cc-message-content");
545
+ if (content) {
546
+ content.textContent = (content.textContent || "") + delta;
547
+ }
548
+ }
549
+ }
550
+ toggle() {
551
+ this.collapsed = !this.collapsed;
552
+ this.container.classList.toggle("collapsed", this.collapsed);
553
+ }
554
+ isCollapsed() {
555
+ return this.collapsed;
556
+ }
557
+ onMessageSend(cb) {
558
+ this.onSend = cb;
559
+ }
560
+ handleSend() {
561
+ const text = this.inputArea.value.trim();
562
+ if (!text) return;
563
+ this.onSend?.(text);
564
+ this.inputArea.value = "";
565
+ }
566
+ renderMessage(msg) {
567
+ const el = document.createElement("div");
568
+ el.className = `cc-message cc-${msg.role}-message`;
569
+ const content = document.createElement("div");
570
+ content.className = "cc-message-content";
571
+ content.textContent = msg.content;
572
+ el.appendChild(content);
573
+ if (msg.filesChanged?.length) {
574
+ const files = document.createElement("div");
575
+ files.className = "cc-files-changed";
576
+ files.innerHTML = msg.filesChanged.map(
577
+ (f) => `<div class="cc-file-change"><span class="cc-file-action">${f.action}</span> ${f.file} (+${f.linesAdded}/-${f.linesRemoved})</div>`
578
+ ).join("");
579
+ el.appendChild(files);
580
+ }
581
+ if (msg.annotations?.annotations.length) {
582
+ const annCard = document.createElement("div");
583
+ annCard.className = "cc-annotation-card";
584
+ annCard.innerHTML = msg.annotations.annotations.map(
585
+ (a) => a.source ? `<div>${a.source.componentName} \u2014 ${a.source.file}:${a.source.line}</div>` : `<div>${a.memo}</div>`
586
+ ).join("");
587
+ el.appendChild(annCard);
588
+ }
589
+ return el;
590
+ }
591
+ };
592
+
593
+ // src/panels/session-bar.ts
594
+ var SESSION_BAR_STYLES = `
595
+ .cc-session-bar {
596
+ position: fixed;
597
+ top: 0;
598
+ left: 0;
599
+ right: 0;
600
+ height: 28px;
601
+ background: var(--cc-bg-toolbar);
602
+ border-bottom: 1px solid var(--cc-border);
603
+ display: flex;
604
+ align-items: center;
605
+ padding: 0 12px;
606
+ gap: 16px;
607
+ font-size: var(--cc-font-size-sm);
608
+ color: var(--cc-text-secondary);
609
+ z-index: 100003;
610
+ }
611
+ .cc-status-indicator {
612
+ width: 8px;
613
+ height: 8px;
614
+ border-radius: 50%;
615
+ background: var(--cc-success);
616
+ }
617
+ .cc-branch-name {
618
+ font-family: var(--cc-font-mono);
619
+ color: var(--cc-text-primary);
620
+ }
621
+ .cc-end-session-btn {
622
+ margin-left: auto;
623
+ padding: 2px 8px;
624
+ background: var(--cc-error);
625
+ color: white;
626
+ border: none;
627
+ border-radius: 3px;
628
+ cursor: pointer;
629
+ font-size: var(--cc-font-size-xs);
630
+ }
631
+ `;
632
+ var SessionBarPanel = class {
633
+ container;
634
+ statusDot;
635
+ branchLabel;
636
+ changeLabel;
637
+ hmrLabel;
638
+ endBtn;
639
+ onEndSession;
640
+ visible = false;
641
+ constructor(shadowRoot) {
642
+ const style = document.createElement("style");
643
+ style.textContent = SESSION_BAR_STYLES;
644
+ shadowRoot.appendChild(style);
645
+ this.container = document.createElement("div");
646
+ this.container.className = "cc-session-bar";
647
+ this.container.style.display = "none";
648
+ this.container.innerHTML = `
649
+ <span class="cc-status-indicator"></span>
650
+ <span class="cc-branch-name"></span>
651
+ <span class="cc-change-count">0 files changed</span>
652
+ <span class="cc-hmr-status">\u2713 HMR</span>
653
+ <button class="cc-end-session-btn">End Session</button>
654
+ `;
655
+ shadowRoot.appendChild(this.container);
656
+ this.statusDot = this.container.querySelector(".cc-status-indicator");
657
+ this.branchLabel = this.container.querySelector(".cc-branch-name");
658
+ this.changeLabel = this.container.querySelector(".cc-change-count");
659
+ this.hmrLabel = this.container.querySelector(".cc-hmr-status");
660
+ this.endBtn = this.container.querySelector(".cc-end-session-btn");
661
+ this.endBtn.addEventListener("click", () => this.onEndSession?.());
662
+ }
663
+ show(branch) {
664
+ this.branchLabel.textContent = branch;
665
+ this.container.style.display = "flex";
666
+ this.visible = true;
667
+ this.setStatus("active");
668
+ }
669
+ hide() {
670
+ this.container.style.display = "none";
671
+ this.visible = false;
672
+ }
673
+ setStatus(status) {
674
+ const colors = {
675
+ "active": "var(--cc-success)",
676
+ "qa-running": "var(--cc-warning)",
677
+ "qa-passed": "var(--cc-success)",
678
+ "qa-failed": "var(--cc-error)"
679
+ };
680
+ this.statusDot.style.background = colors[status];
681
+ }
682
+ updateChangedFiles(count) {
683
+ this.changeLabel.textContent = `${count} file${count !== 1 ? "s" : ""} changed`;
684
+ }
685
+ setHmrStatus(connected) {
686
+ this.hmrLabel.textContent = connected ? "\u2713 HMR" : "\u2717 HMR";
687
+ this.hmrLabel.style.color = connected ? "var(--cc-success)" : "var(--cc-error)";
688
+ }
689
+ onEnd(cb) {
690
+ this.onEndSession = cb;
691
+ }
692
+ isVisible() {
693
+ return this.visible;
694
+ }
695
+ };
696
+
697
+ // src/panels/qa-result.ts
698
+ var QA_RESULT_STYLES = `
699
+ .cc-qa-modal-overlay {
700
+ position: fixed;
701
+ inset: 0;
702
+ background: rgba(0,0,0,0.6);
703
+ display: flex;
704
+ align-items: center;
705
+ justify-content: center;
706
+ z-index: 100004;
707
+ }
708
+ .cc-qa-modal {
709
+ background: var(--cc-bg-panel);
710
+ border: 1px solid var(--cc-border);
711
+ border-radius: 8px;
712
+ padding: 24px;
713
+ min-width: 420px;
714
+ max-width: 520px;
715
+ box-shadow: 0 8px 32px rgba(0,0,0,0.6);
716
+ }
717
+ .cc-qa-title {
718
+ margin-bottom: 16px;
719
+ font-size: var(--cc-font-size-lg);
720
+ }
721
+ .cc-qa-step {
722
+ padding: 4px 0;
723
+ font-family: var(--cc-font-mono);
724
+ font-size: var(--cc-font-size-sm);
725
+ }
726
+ .cc-change-summary {
727
+ margin: 12px 0;
728
+ padding: 8px;
729
+ background: var(--cc-bg-input);
730
+ border-radius: 4px;
731
+ }
732
+ .cc-qa-actions {
733
+ display: flex;
734
+ gap: 8px;
735
+ margin-top: 16px;
736
+ }
737
+ .cc-qa-merge-btn {
738
+ flex: 1;
739
+ padding: 6px;
740
+ background: var(--cc-success);
741
+ color: white;
742
+ border: none;
743
+ border-radius: 4px;
744
+ cursor: pointer;
745
+ }
746
+ .cc-qa-merge-btn:disabled {
747
+ opacity: 0.5;
748
+ cursor: not-allowed;
749
+ }
750
+ .cc-qa-fix-btn {
751
+ flex: 1;
752
+ padding: 6px;
753
+ background: var(--cc-warning);
754
+ color: white;
755
+ border: none;
756
+ border-radius: 4px;
757
+ cursor: pointer;
758
+ }
759
+ .cc-qa-discard-btn {
760
+ flex: 1;
761
+ padding: 6px;
762
+ background: var(--cc-error);
763
+ color: white;
764
+ border: none;
765
+ border-radius: 4px;
766
+ cursor: pointer;
767
+ }
768
+ .cc-confirm-dialog {
769
+ margin-top: 12px;
770
+ padding: 12px;
771
+ background: var(--cc-bg-input);
772
+ border-radius: 4px;
773
+ }
774
+ .cc-confirm-yes,
775
+ .cc-confirm-no {
776
+ padding: 4px 12px;
777
+ border: none;
778
+ border-radius: 3px;
779
+ cursor: pointer;
780
+ margin-right: 8px;
781
+ }
782
+ .cc-confirm-yes {
783
+ background: var(--cc-error);
784
+ color: white;
785
+ }
786
+ .cc-confirm-no {
787
+ background: var(--cc-bg-hover);
788
+ color: var(--cc-text-primary);
789
+ }
790
+ `;
791
+ var QAResultPanel = class {
792
+ overlay;
793
+ modal;
794
+ stepList;
795
+ changeSummary;
796
+ mergeBtn;
797
+ fixBtn;
798
+ discardBtn;
799
+ confirmDialog = null;
800
+ onMerge;
801
+ onFix;
802
+ onDiscard;
803
+ constructor(shadowRoot) {
804
+ const style = document.createElement("style");
805
+ style.textContent = QA_RESULT_STYLES;
806
+ shadowRoot.appendChild(style);
807
+ this.overlay = document.createElement("div");
808
+ this.overlay.className = "cc-qa-modal-overlay";
809
+ this.overlay.style.display = "none";
810
+ this.overlay.innerHTML = `
811
+ <div class="cc-qa-modal">
812
+ <h3 class="cc-qa-title">QA Results</h3>
813
+ <div class="cc-qa-step-list"></div>
814
+ <div class="cc-change-summary"></div>
815
+ <div class="cc-qa-actions">
816
+ <button class="cc-qa-merge-btn" disabled>Merge</button>
817
+ <button class="cc-qa-fix-btn">Fix</button>
818
+ <button class="cc-qa-discard-btn">Discard</button>
819
+ </div>
820
+ </div>
821
+ `;
822
+ shadowRoot.appendChild(this.overlay);
823
+ this.modal = this.overlay.querySelector(".cc-qa-modal");
824
+ this.stepList = this.overlay.querySelector(".cc-qa-step-list");
825
+ this.changeSummary = this.overlay.querySelector(".cc-change-summary");
826
+ this.mergeBtn = this.overlay.querySelector(".cc-qa-merge-btn");
827
+ this.fixBtn = this.overlay.querySelector(".cc-qa-fix-btn");
828
+ this.discardBtn = this.overlay.querySelector(".cc-qa-discard-btn");
829
+ this.mergeBtn.addEventListener("click", () => this.onMerge?.());
830
+ this.fixBtn.addEventListener("click", () => {
831
+ this.hide();
832
+ this.onFix?.();
833
+ });
834
+ this.discardBtn.addEventListener("click", () => this.showConfirm());
835
+ }
836
+ show() {
837
+ this.overlay.style.display = "flex";
838
+ }
839
+ hide() {
840
+ this.overlay.style.display = "none";
841
+ }
842
+ updateStep(step) {
843
+ let el = this.stepList.querySelector(`[data-step="${step.name}"]`);
844
+ if (!el) {
845
+ el = document.createElement("div");
846
+ el.className = "cc-qa-step";
847
+ el.setAttribute("data-step", step.name);
848
+ this.stepList.appendChild(el);
849
+ }
850
+ const icon = step.status === "pass" ? "\u2713" : step.status === "fail" ? "\u2717" : step.status === "running" ? "\u27F3" : "\u25CB";
851
+ const color = step.status === "pass" ? "var(--cc-success)" : step.status === "fail" ? "var(--cc-error)" : "var(--cc-text-muted)";
852
+ el.innerHTML = `<span style="color:${color}">${icon}</span> ${step.name}${step.duration ? ` (${step.duration}ms)` : ""}`;
853
+ }
854
+ setOverallPass(pass) {
855
+ this.mergeBtn.disabled = !pass;
856
+ }
857
+ setChangeSummary(changes) {
858
+ this.changeSummary.innerHTML = changes.map(
859
+ (c) => `<div class="cc-change-item">${c.action} ${c.file} <span class="cc-diff-stat">+${c.linesAdded}/-${c.linesRemoved}</span></div>`
860
+ ).join("");
861
+ }
862
+ onMergeClick(cb) {
863
+ this.onMerge = cb;
864
+ }
865
+ onFixClick(cb) {
866
+ this.onFix = cb;
867
+ }
868
+ onDiscardClick(cb) {
869
+ this.onDiscard = cb;
870
+ }
871
+ showConfirm() {
872
+ this.confirmDialog = document.createElement("div");
873
+ this.confirmDialog.className = "cc-confirm-dialog";
874
+ this.confirmDialog.innerHTML = `
875
+ <p>\uC815\uB9D0 \uD3D0\uAE30\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \uBAA8\uB4E0 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC0AD\uC81C\uB429\uB2C8\uB2E4.</p>
876
+ <button class="cc-confirm-yes">\uD3D0\uAE30</button>
877
+ <button class="cc-confirm-no">\uCDE8\uC18C</button>
878
+ `;
879
+ this.modal.appendChild(this.confirmDialog);
880
+ this.confirmDialog.querySelector(".cc-confirm-yes")?.addEventListener("click", () => {
881
+ this.hide();
882
+ this.onDiscard?.();
883
+ });
884
+ this.confirmDialog.querySelector(".cc-confirm-no")?.addEventListener("click", () => {
885
+ this.confirmDialog?.remove();
886
+ this.confirmDialog = null;
887
+ });
888
+ }
889
+ };
890
+
891
+ // src/inspector/fiber-walker.ts
892
+ var FIBER_KEY_PREFIXES = ["__reactFiber$", "__reactInternalInstance$"];
893
+ var FiberWalker = {
894
+ /**
895
+ * Find the React fiber attached to a DOM element.
896
+ * React 18+ uses __reactFiber$<hash>, older versions use __reactInternalInstance$<hash>.
897
+ */
898
+ getFiberFromElement(el) {
899
+ const keys = Object.keys(el);
900
+ for (const key of keys) {
901
+ for (const prefix of FIBER_KEY_PREFIXES) {
902
+ if (key.startsWith(prefix)) {
903
+ const fiber = el[key];
904
+ if (fiber && typeof fiber === "object") {
905
+ return fiber;
906
+ }
907
+ }
908
+ }
909
+ }
910
+ return null;
911
+ },
912
+ /**
913
+ * Extract SourceInfo from a React fiber's _debugSource.
914
+ * Returns null when debug source info is unavailable.
915
+ */
916
+ getSourceInfo(fiber) {
917
+ if (!fiber || !fiber._debugSource) {
918
+ return null;
919
+ }
920
+ const { fileName, lineNumber, columnNumber } = fiber._debugSource;
921
+ let componentName = "Anonymous";
922
+ if (fiber.type && typeof fiber.type === "object") {
923
+ const fiberType = fiber.type;
924
+ if (fiberType.displayName) {
925
+ componentName = fiberType.displayName;
926
+ } else if (fiberType.name) {
927
+ componentName = fiberType.name;
928
+ }
929
+ } else if (typeof fiber.type === "string") {
930
+ componentName = fiber.type;
931
+ }
932
+ let tagName = "unknown";
933
+ if (fiber.stateNode && typeof fiber.stateNode.tagName === "string") {
934
+ tagName = fiber.stateNode.tagName.toLowerCase();
935
+ }
936
+ let className;
937
+ if (fiber.stateNode && typeof fiber.stateNode.className === "string" && fiber.stateNode.className.length > 0) {
938
+ className = fiber.stateNode.className;
939
+ }
940
+ return {
941
+ componentName,
942
+ file: fileName,
943
+ line: lineNumber,
944
+ column: columnNumber,
945
+ tagName,
946
+ ...className !== void 0 ? { className } : {}
947
+ };
948
+ }
949
+ };
950
+
951
+ // src/inspector/source-mapper.ts
952
+ var SourceMapper = class _SourceMapper {
953
+ static detectLibrary(source, wrapperPaths) {
954
+ if (source.file.includes("node_modules/@carbon")) return "carbon";
955
+ if (source.library?.includes("@carbon")) return "carbon";
956
+ if (wrapperPaths && wrapperPaths.some((p) => source.file.includes(p))) return "project-wrapper";
957
+ return "unknown";
958
+ }
959
+ static isWrapperComponent(file, wrapperPaths) {
960
+ return wrapperPaths.some((p) => file.includes(p));
961
+ }
962
+ static formatSourcePath(file, line, column) {
963
+ return `${file}:${line}:${column}`;
964
+ }
965
+ // Instance method aliases
966
+ detectLibrary(source, wrapperPaths) {
967
+ return _SourceMapper.detectLibrary(source, wrapperPaths);
968
+ }
969
+ isWrapperComponent(file, wrapperPaths) {
970
+ return _SourceMapper.isWrapperComponent(file, wrapperPaths);
971
+ }
972
+ formatSourcePath(file, line, column) {
973
+ return _SourceMapper.formatSourcePath(file, line, column);
974
+ }
975
+ };
976
+
977
+ // src/annotator/canvas-overlay.ts
978
+ function makeNoopCtx() {
979
+ return new Proxy({}, {
980
+ get: () => () => void 0,
981
+ set: () => true
982
+ });
983
+ }
984
+ var CanvasOverlay = class {
985
+ canvas;
986
+ ctx;
987
+ mode = "off";
988
+ annotations = [];
989
+ constructor() {
990
+ this.canvas = document.createElement("canvas");
991
+ this.canvas.style.cssText = "position:fixed;inset:0;width:100vw;height:100vh;z-index:100001;pointer-events:none;";
992
+ try {
993
+ this.ctx = this.canvas.getContext("2d") ?? makeNoopCtx();
994
+ } catch {
995
+ this.ctx = makeNoopCtx();
996
+ }
997
+ }
998
+ mount() {
999
+ document.body.appendChild(this.canvas);
1000
+ }
1001
+ unmount() {
1002
+ this.canvas.remove();
1003
+ }
1004
+ setMode(mode) {
1005
+ this.mode = mode;
1006
+ this.canvas.style.pointerEvents = mode === "off" ? "none" : "auto";
1007
+ }
1008
+ getMode() {
1009
+ return this.mode;
1010
+ }
1011
+ drawHoverHighlight(rect) {
1012
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1013
+ this.ctx.strokeStyle = "rgba(66, 133, 244, 0.8)";
1014
+ this.ctx.lineWidth = 2;
1015
+ this.ctx.setLineDash([4, 4]);
1016
+ this.ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
1017
+ this.ctx.fillStyle = "rgba(66, 133, 244, 0.1)";
1018
+ this.ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
1019
+ }
1020
+ drawSelectHighlight(rect) {
1021
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1022
+ this.ctx.strokeStyle = "rgba(255, 140, 0, 0.9)";
1023
+ this.ctx.lineWidth = 2;
1024
+ this.ctx.setLineDash([]);
1025
+ this.ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
1026
+ this.ctx.fillStyle = "rgba(255, 140, 0, 0.1)";
1027
+ this.ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
1028
+ }
1029
+ drawBoxAnnotation(rect, memo) {
1030
+ this.ctx.strokeStyle = "rgba(220, 50, 50, 0.9)";
1031
+ this.ctx.lineWidth = 2;
1032
+ this.ctx.setLineDash([]);
1033
+ this.ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
1034
+ if (memo) {
1035
+ this.ctx.fillStyle = "rgba(220, 50, 50, 0.9)";
1036
+ this.ctx.font = "12px sans-serif";
1037
+ this.ctx.fillText(memo, rect.x + 4, rect.y - 4);
1038
+ }
1039
+ }
1040
+ clear() {
1041
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
1042
+ }
1043
+ getAnnotations() {
1044
+ return [...this.annotations];
1045
+ }
1046
+ addAnnotation(ann) {
1047
+ this.annotations.push(ann);
1048
+ }
1049
+ clearAnnotations() {
1050
+ this.annotations = [];
1051
+ }
1052
+ };
1053
+
1054
+ // src/annotator/box-tool.ts
1055
+ var BoxTool = class {
1056
+ startX = 0;
1057
+ startY = 0;
1058
+ drawing = false;
1059
+ startDraw(x, y) {
1060
+ this.startX = x;
1061
+ this.startY = y;
1062
+ this.drawing = true;
1063
+ }
1064
+ updateDraw(x, y) {
1065
+ return {
1066
+ x: Math.min(this.startX, x),
1067
+ y: Math.min(this.startY, y),
1068
+ width: Math.abs(x - this.startX),
1069
+ height: Math.abs(y - this.startY)
1070
+ };
1071
+ }
1072
+ endDraw(x, y) {
1073
+ this.drawing = false;
1074
+ return this.updateDraw(x, y);
1075
+ }
1076
+ isDrawing() {
1077
+ return this.drawing;
1078
+ }
1079
+ };
1080
+
1081
+ // src/annotator/text-memo.ts
1082
+ var TextMemo = class {
1083
+ input;
1084
+ visible = false;
1085
+ constructor(container) {
1086
+ this.input = document.createElement("textarea");
1087
+ this.input.className = "cc-memo-input";
1088
+ this.input.style.cssText = "position:absolute;display:none;width:200px;height:60px;background:var(--cc-bg-input);color:var(--cc-text-primary);border:1px solid var(--cc-border);border-radius:4px;padding:4px 8px;font-size:var(--cc-font-size-sm);resize:none;";
1089
+ container.appendChild(this.input);
1090
+ }
1091
+ show(x, y) {
1092
+ this.input.style.display = "block";
1093
+ this.input.style.left = x + "px";
1094
+ this.input.style.top = y + "px";
1095
+ this.visible = true;
1096
+ this.input.focus();
1097
+ }
1098
+ hide() {
1099
+ this.input.style.display = "none";
1100
+ this.visible = false;
1101
+ this.input.value = "";
1102
+ }
1103
+ getValue() {
1104
+ return this.input.value;
1105
+ }
1106
+ isVisible() {
1107
+ return this.visible;
1108
+ }
1109
+ };
1110
+
1111
+ // src/panels/canvas-overlay.ts
1112
+ var CanvasOverlayPanel = class {
1113
+ overlay;
1114
+ boxTool;
1115
+ mode = "off";
1116
+ onSourceSelect;
1117
+ onAnnotationsReady;
1118
+ constructor() {
1119
+ this.overlay = new CanvasOverlay();
1120
+ this.boxTool = new BoxTool();
1121
+ }
1122
+ mount() {
1123
+ this.overlay.mount();
1124
+ }
1125
+ unmount() {
1126
+ this.overlay.unmount();
1127
+ }
1128
+ setMode(mode) {
1129
+ this.mode = mode;
1130
+ this.overlay.setMode(mode);
1131
+ }
1132
+ getMode() {
1133
+ return this.mode;
1134
+ }
1135
+ onSelect(cb) {
1136
+ this.onSourceSelect = cb;
1137
+ }
1138
+ onSend(cb) {
1139
+ this.onAnnotationsReady = cb;
1140
+ }
1141
+ handleElementHover(el) {
1142
+ const fiber = FiberWalker.getFiberFromElement(el);
1143
+ if (!fiber) return;
1144
+ const rect = el.getBoundingClientRect();
1145
+ this.overlay.drawHoverHighlight(rect);
1146
+ }
1147
+ handleElementSelect(el) {
1148
+ const fiber = FiberWalker.getFiberFromElement(el);
1149
+ if (!fiber) return;
1150
+ const source = FiberWalker.getSourceInfo(fiber);
1151
+ if (!source) return;
1152
+ source.library = SourceMapper.detectLibrary(source);
1153
+ const rect = el.getBoundingClientRect();
1154
+ this.overlay.drawSelectHighlight(rect);
1155
+ const annotation = {
1156
+ id: `ann-${Date.now()}`,
1157
+ type: "element-select",
1158
+ rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
1159
+ source,
1160
+ memo: "",
1161
+ createdAt: Date.now()
1162
+ };
1163
+ this.overlay.addAnnotation(annotation);
1164
+ this.onSourceSelect?.(source);
1165
+ }
1166
+ collectAnnotationSet() {
1167
+ return {
1168
+ annotations: this.overlay.getAnnotations(),
1169
+ viewport: { width: window.innerWidth, height: window.innerHeight }
1170
+ };
1171
+ }
1172
+ send() {
1173
+ const set = this.collectAnnotationSet();
1174
+ this.onAnnotationsReady?.(set);
1175
+ }
1176
+ clearAnnotations() {
1177
+ this.overlay.clearAnnotations();
1178
+ this.overlay.clear();
1179
+ }
1180
+ };
1181
+
1182
+ // src/panels/toolbar.ts
1183
+ var TOOLS = [
1184
+ { mode: "select", label: "S", title: "Select element" },
1185
+ { mode: "box", label: "B", title: "Draw box annotation" },
1186
+ { mode: "memo", label: "M", title: "Add memo" },
1187
+ { mode: "capture", label: "C", title: "Capture screenshot" }
1188
+ ];
1189
+ var TOOLBAR_STYLES = `
1190
+ .cc-toolbar {
1191
+ position: fixed;
1192
+ bottom: 24px;
1193
+ left: 50%;
1194
+ transform: translateX(-50%);
1195
+ display: flex;
1196
+ align-items: center;
1197
+ gap: 4px;
1198
+ background: var(--cc-bg-panel, rgba(30,30,30,0.95));
1199
+ border: 1px solid var(--cc-border, rgba(255,255,255,0.15));
1200
+ border-radius: 8px;
1201
+ padding: 6px 8px;
1202
+ z-index: 100002;
1203
+ box-shadow: 0 4px 16px rgba(0,0,0,0.4);
1204
+ }
1205
+ .cc-tool-btn {
1206
+ width: 32px;
1207
+ height: 32px;
1208
+ border: none;
1209
+ border-radius: 4px;
1210
+ background: transparent;
1211
+ color: var(--cc-text-secondary, rgba(255,255,255,0.6));
1212
+ cursor: pointer;
1213
+ font-size: 11px;
1214
+ font-family: var(--cc-font-family, sans-serif);
1215
+ display: flex;
1216
+ align-items: center;
1217
+ justify-content: center;
1218
+ transition: background 0.1s ease, color 0.1s ease;
1219
+ }
1220
+ .cc-tool-btn:hover {
1221
+ background: var(--cc-bg-hover, rgba(255,255,255,0.1));
1222
+ color: var(--cc-text-primary, #e0e0e0);
1223
+ }
1224
+ .cc-tool-btn.active {
1225
+ background: var(--cc-accent, #7c4dff);
1226
+ color: #fff;
1227
+ }
1228
+ .cc-toolbar-divider {
1229
+ width: 1px;
1230
+ height: 20px;
1231
+ background: var(--cc-border, rgba(255,255,255,0.15));
1232
+ margin: 0 2px;
1233
+ }
1234
+ .cc-send-btn {
1235
+ padding: 0 12px;
1236
+ height: 32px;
1237
+ border: none;
1238
+ border-radius: 4px;
1239
+ background: var(--cc-accent, #7c4dff);
1240
+ color: #fff;
1241
+ cursor: pointer;
1242
+ font-size: 12px;
1243
+ font-family: var(--cc-font-family, sans-serif);
1244
+ white-space: nowrap;
1245
+ transition: background 0.1s ease;
1246
+ }
1247
+ .cc-send-btn:hover {
1248
+ background: var(--cc-border-focus, rgba(124,77,255,0.8));
1249
+ }
1250
+ `;
1251
+ var ToolbarPanel = class {
1252
+ container;
1253
+ currentTool = "select";
1254
+ onModeChange;
1255
+ onSend;
1256
+ buttons = /* @__PURE__ */ new Map();
1257
+ mounted = false;
1258
+ constructor(shadowRoot) {
1259
+ const style = document.createElement("style");
1260
+ style.textContent = TOOLBAR_STYLES;
1261
+ shadowRoot.appendChild(style);
1262
+ this.container = document.createElement("div");
1263
+ this.container.className = "cc-toolbar";
1264
+ this.container.style.display = "none";
1265
+ TOOLS.forEach(({ mode, label, title }) => {
1266
+ const btn = document.createElement("button");
1267
+ btn.className = "cc-tool-btn";
1268
+ btn.textContent = label;
1269
+ btn.title = title;
1270
+ btn.setAttribute("data-mode", mode);
1271
+ btn.addEventListener("click", () => {
1272
+ this.setActiveTool(mode);
1273
+ this.onModeChange?.(mode);
1274
+ });
1275
+ this.buttons.set(mode, btn);
1276
+ this.container.appendChild(btn);
1277
+ });
1278
+ const divider = document.createElement("div");
1279
+ divider.className = "cc-toolbar-divider";
1280
+ this.container.appendChild(divider);
1281
+ const sendBtn = document.createElement("button");
1282
+ sendBtn.className = "cc-send-btn";
1283
+ sendBtn.textContent = "Send";
1284
+ sendBtn.addEventListener("click", () => this.onSend?.());
1285
+ this.container.appendChild(sendBtn);
1286
+ shadowRoot.appendChild(this.container);
1287
+ }
1288
+ mount() {
1289
+ this.container.style.display = "flex";
1290
+ this.mounted = true;
1291
+ }
1292
+ unmount() {
1293
+ this.container.style.display = "none";
1294
+ this.mounted = false;
1295
+ }
1296
+ setActiveTool(mode) {
1297
+ this.currentTool = mode;
1298
+ this.buttons.forEach((btn, btnMode) => {
1299
+ btn.classList.toggle("active", btnMode === mode);
1300
+ });
1301
+ }
1302
+ getActiveTool() {
1303
+ return this.currentTool;
1304
+ }
1305
+ onToolChange(cb) {
1306
+ this.onModeChange = cb;
1307
+ }
1308
+ onSendClick(cb) {
1309
+ this.onSend = cb;
1310
+ }
1311
+ isMounted() {
1312
+ return this.mounted;
1313
+ }
1314
+ };
1315
+
1316
+ // src/panels/inspector-popup.ts
1317
+ var POPUP_STYLES = `
1318
+ .cc-inspector-popup {
1319
+ position: fixed;
1320
+ background: var(--cc-bg-panel, rgba(30,30,30,0.97));
1321
+ border: 1px solid var(--cc-border, rgba(255,255,255,0.15));
1322
+ border-radius: 6px;
1323
+ padding: 8px 12px;
1324
+ font-family: var(--cc-font-family, monospace);
1325
+ font-size: var(--cc-font-size-xs, 11px);
1326
+ color: var(--cc-text-primary, #e0e0e0);
1327
+ z-index: 100003;
1328
+ pointer-events: none;
1329
+ max-width: 320px;
1330
+ box-shadow: 0 4px 16px rgba(0,0,0,0.4);
1331
+ display: none;
1332
+ }
1333
+ .cc-inspector-popup.pinned {
1334
+ pointer-events: auto;
1335
+ }
1336
+ .cc-popup-component {
1337
+ font-weight: bold;
1338
+ color: var(--cc-accent, #7c4dff);
1339
+ margin-bottom: 2px;
1340
+ }
1341
+ .cc-popup-file {
1342
+ color: var(--cc-text-secondary, rgba(255,255,255,0.6));
1343
+ word-break: break-all;
1344
+ }
1345
+ .cc-popup-library {
1346
+ margin-top: 4px;
1347
+ padding: 2px 6px;
1348
+ background: var(--cc-bg-hover, rgba(255,255,255,0.08));
1349
+ border-radius: 3px;
1350
+ display: inline-block;
1351
+ font-size: 10px;
1352
+ }
1353
+ `;
1354
+ var InspectorPopupPanel = class {
1355
+ element;
1356
+ sourceMapper;
1357
+ pinned = false;
1358
+ constructor(shadowRoot) {
1359
+ this.sourceMapper = new SourceMapper();
1360
+ const style = document.createElement("style");
1361
+ style.textContent = POPUP_STYLES;
1362
+ shadowRoot.appendChild(style);
1363
+ this.element = document.createElement("div");
1364
+ this.element.className = "cc-inspector-popup";
1365
+ this.element.style.display = "none";
1366
+ shadowRoot.appendChild(this.element);
1367
+ }
1368
+ show(source, rect) {
1369
+ const library = this.sourceMapper.detectLibrary(source);
1370
+ const path = this.sourceMapper.formatSourcePath(source.file, source.line, source.column);
1371
+ this.element.innerHTML = "";
1372
+ const compLine = document.createElement("div");
1373
+ compLine.className = "cc-popup-component";
1374
+ compLine.textContent = `<${source.componentName}>`;
1375
+ this.element.appendChild(compLine);
1376
+ const fileLine = document.createElement("div");
1377
+ fileLine.className = "cc-popup-file";
1378
+ fileLine.textContent = path;
1379
+ this.element.appendChild(fileLine);
1380
+ if (library !== "unknown") {
1381
+ const libLine = document.createElement("span");
1382
+ libLine.className = "cc-popup-library";
1383
+ libLine.textContent = library;
1384
+ this.element.appendChild(libLine);
1385
+ }
1386
+ const popupHeight = 80;
1387
+ const top = Math.max(4, rect.top - popupHeight - 8);
1388
+ const left = Math.min(rect.left, window.innerWidth - 330);
1389
+ this.element.style.top = top + "px";
1390
+ this.element.style.left = left + "px";
1391
+ this.element.style.display = "block";
1392
+ }
1393
+ hide() {
1394
+ if (this.pinned) return;
1395
+ this.element.style.display = "none";
1396
+ }
1397
+ forceHide() {
1398
+ this.pinned = false;
1399
+ this.element.style.display = "none";
1400
+ }
1401
+ pin() {
1402
+ this.pinned = true;
1403
+ this.element.classList.add("pinned");
1404
+ }
1405
+ unpin() {
1406
+ this.pinned = false;
1407
+ this.element.classList.remove("pinned");
1408
+ }
1409
+ isPinned() {
1410
+ return this.pinned;
1411
+ }
1412
+ isVisible() {
1413
+ return this.element.style.display !== "none";
1414
+ }
1415
+ };
1416
+ export {
1417
+ BoxTool,
1418
+ CanvasOverlay,
1419
+ CanvasOverlayPanel,
1420
+ ChatPanel,
1421
+ FabButton,
1422
+ FiberWalker,
1423
+ InspectorPopupPanel,
1424
+ KeyboardHandler,
1425
+ QAResultPanel,
1426
+ SessionBarPanel,
1427
+ ShadowContainer,
1428
+ SourceMapper,
1429
+ TextMemo,
1430
+ Toast,
1431
+ ToolbarPanel
1432
+ };
1433
+ //# sourceMappingURL=index.js.map