@ibealec/create-zed-bridge 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2536 @@
1
+ // src/ClaudeBridgeProvider.tsx
2
+ import {
3
+ createContext,
4
+ useContext,
5
+ useReducer,
6
+ useEffect as useEffect6,
7
+ useCallback as useCallback4,
8
+ useRef as useRef6
9
+ } from "react";
10
+
11
+ // src/websocket-client.ts
12
+ var BridgeWebSocketClient = class {
13
+ constructor(config) {
14
+ this.ws = null;
15
+ this.messageHandlers = /* @__PURE__ */ new Set();
16
+ this.connectionHandlers = /* @__PURE__ */ new Set();
17
+ this.reconnectAttempts = 0;
18
+ this.maxReconnectAttempts = 5;
19
+ this.reconnectDelay = 1e3;
20
+ this.reconnectTimeout = null;
21
+ this.pingInterval = null;
22
+ this.config = config;
23
+ }
24
+ connect() {
25
+ if (this.ws?.readyState === WebSocket.OPEN) {
26
+ return;
27
+ }
28
+ try {
29
+ const wsUrl = this.config.serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
30
+ this.ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(this.config.token)}`);
31
+ this.ws.onopen = () => {
32
+ console.log("[ClaudeBridge] WebSocket connected");
33
+ this.reconnectAttempts = 0;
34
+ this.notifyConnectionHandlers(true);
35
+ this.startPingInterval();
36
+ };
37
+ this.ws.onmessage = (event) => {
38
+ try {
39
+ const message = JSON.parse(event.data);
40
+ this.notifyMessageHandlers(message);
41
+ } catch (error) {
42
+ console.error("[ClaudeBridge] Failed to parse message:", error);
43
+ }
44
+ };
45
+ this.ws.onclose = () => {
46
+ console.log("[ClaudeBridge] WebSocket disconnected");
47
+ this.notifyConnectionHandlers(false);
48
+ this.stopPingInterval();
49
+ this.scheduleReconnect();
50
+ };
51
+ this.ws.onerror = (error) => {
52
+ console.error("[ClaudeBridge] WebSocket error:", error);
53
+ this.config.onError?.(new Error("WebSocket connection error"));
54
+ };
55
+ } catch (error) {
56
+ console.error("[ClaudeBridge] Failed to connect:", error);
57
+ this.scheduleReconnect();
58
+ }
59
+ }
60
+ disconnect() {
61
+ this.stopPingInterval();
62
+ if (this.reconnectTimeout) {
63
+ clearTimeout(this.reconnectTimeout);
64
+ this.reconnectTimeout = null;
65
+ }
66
+ if (this.ws) {
67
+ this.ws.close();
68
+ this.ws = null;
69
+ }
70
+ }
71
+ scheduleReconnect() {
72
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
73
+ console.log("[ClaudeBridge] Max reconnect attempts reached");
74
+ return;
75
+ }
76
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
77
+ this.reconnectAttempts++;
78
+ console.log(`[ClaudeBridge] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
79
+ this.reconnectTimeout = setTimeout(() => this.connect(), delay);
80
+ }
81
+ startPingInterval() {
82
+ this.pingInterval = setInterval(() => {
83
+ if (this.ws?.readyState === WebSocket.OPEN) {
84
+ this.send({ type: "ping" });
85
+ }
86
+ }, 3e4);
87
+ }
88
+ stopPingInterval() {
89
+ if (this.pingInterval) {
90
+ clearInterval(this.pingInterval);
91
+ this.pingInterval = null;
92
+ }
93
+ }
94
+ send(message) {
95
+ if (this.ws?.readyState !== WebSocket.OPEN) {
96
+ console.warn("[ClaudeBridge] Cannot send message: not connected");
97
+ return false;
98
+ }
99
+ try {
100
+ this.ws.send(JSON.stringify(message));
101
+ return true;
102
+ } catch (error) {
103
+ console.error("[ClaudeBridge] Failed to send message:", error);
104
+ return false;
105
+ }
106
+ }
107
+ createTask(prompt, context) {
108
+ console.log("[ClaudeBridge] createTask context:", {
109
+ sourceFile: context.sourceFile,
110
+ sourceLine: context.sourceLine,
111
+ componentName: context.componentName
112
+ });
113
+ const request = {
114
+ prompt,
115
+ context,
116
+ projectPath: this.config.projectRoot
117
+ };
118
+ return this.send({
119
+ type: "create_task",
120
+ ...request
121
+ });
122
+ }
123
+ continueTask(taskId, prompt, context) {
124
+ return this.send({
125
+ type: "continue_task",
126
+ taskId,
127
+ prompt,
128
+ context
129
+ });
130
+ }
131
+ onMessage(handler) {
132
+ this.messageHandlers.add(handler);
133
+ return () => this.messageHandlers.delete(handler);
134
+ }
135
+ onConnection(handler) {
136
+ this.connectionHandlers.add(handler);
137
+ return () => this.connectionHandlers.delete(handler);
138
+ }
139
+ notifyMessageHandlers(message) {
140
+ for (const handler of this.messageHandlers) {
141
+ try {
142
+ handler(message);
143
+ } catch (error) {
144
+ console.error("[ClaudeBridge] Message handler error:", error);
145
+ }
146
+ }
147
+ }
148
+ notifyConnectionHandlers(connected) {
149
+ for (const handler of this.connectionHandlers) {
150
+ try {
151
+ handler(connected);
152
+ } catch (error) {
153
+ console.error("[ClaudeBridge] Connection handler error:", error);
154
+ }
155
+ }
156
+ }
157
+ get isConnected() {
158
+ return this.ws?.readyState === WebSocket.OPEN;
159
+ }
160
+ };
161
+
162
+ // src/context-capture.ts
163
+ function captureContext(element) {
164
+ return {
165
+ selectedText: getSelectedText(),
166
+ selectedElement: {
167
+ tagName: element.tagName.toLowerCase(),
168
+ className: element.className,
169
+ id: element.id || void 0,
170
+ innerText: truncateText(element.innerText, 200)
171
+ },
172
+ sourceFile: getSourceFile(element),
173
+ sourceLine: getSourceLine(element),
174
+ sourceColumn: getSourceColumn(element),
175
+ componentName: getComponentName(element),
176
+ domPath: getDomPath(element),
177
+ parentComponents: getParentComponents(element),
178
+ currentUrl: window.location.href,
179
+ pageTitle: document.title
180
+ };
181
+ }
182
+ function getSelectedText() {
183
+ const selection = window.getSelection();
184
+ if (selection && selection.toString().trim()) {
185
+ return selection.toString().trim();
186
+ }
187
+ return void 0;
188
+ }
189
+ function truncateText(text, maxLength) {
190
+ const cleaned = text.replace(/\s+/g, " ").trim();
191
+ if (cleaned.length <= maxLength) {
192
+ return cleaned;
193
+ }
194
+ return cleaned.slice(0, maxLength) + "...";
195
+ }
196
+ function getSourceFile(element) {
197
+ let current = element;
198
+ while (current) {
199
+ const locatorId = current.getAttribute("data-locatorjs-id");
200
+ if (locatorId) {
201
+ const [filePath] = locatorId.split(":");
202
+ return filePath;
203
+ }
204
+ const sourceFile = current.getAttribute("data-source-file");
205
+ if (sourceFile) {
206
+ return sourceFile;
207
+ }
208
+ current = current.parentElement;
209
+ }
210
+ return void 0;
211
+ }
212
+ function getSourceLine(element) {
213
+ let current = element;
214
+ while (current) {
215
+ const locatorId = current.getAttribute("data-locatorjs-id");
216
+ if (locatorId) {
217
+ const parts = locatorId.split(":");
218
+ if (parts.length >= 2) {
219
+ const line = parseInt(parts[1], 10);
220
+ if (!isNaN(line)) return line;
221
+ }
222
+ }
223
+ const sourceLine = current.getAttribute("data-source-line");
224
+ if (sourceLine) {
225
+ const line = parseInt(sourceLine, 10);
226
+ if (!isNaN(line)) return line;
227
+ }
228
+ current = current.parentElement;
229
+ }
230
+ return void 0;
231
+ }
232
+ function getSourceColumn(element) {
233
+ let current = element;
234
+ while (current) {
235
+ const locatorId = current.getAttribute("data-locatorjs-id");
236
+ if (locatorId) {
237
+ const parts = locatorId.split(":");
238
+ if (parts.length >= 3) {
239
+ const col = parseInt(parts[2], 10);
240
+ if (!isNaN(col)) return col;
241
+ }
242
+ }
243
+ current = current.parentElement;
244
+ }
245
+ return void 0;
246
+ }
247
+ function getComponentName(element) {
248
+ const fiber = getReactFiber(element);
249
+ if (fiber) {
250
+ const name = getFiberComponentName(fiber);
251
+ if (name) return name;
252
+ }
253
+ let current = element;
254
+ while (current) {
255
+ const componentName = current.getAttribute("data-component");
256
+ if (componentName) return componentName;
257
+ current = current.parentElement;
258
+ }
259
+ return void 0;
260
+ }
261
+ function getReactFiber(element) {
262
+ const keys = Object.keys(element);
263
+ for (const key of keys) {
264
+ if (key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")) {
265
+ return element[key];
266
+ }
267
+ }
268
+ return null;
269
+ }
270
+ function getFiberComponentName(fiber) {
271
+ if (!fiber) return void 0;
272
+ if (fiber.type) {
273
+ if (typeof fiber.type === "function") {
274
+ return fiber.type.displayName || fiber.type.name || void 0;
275
+ }
276
+ if (typeof fiber.type === "string") {
277
+ return void 0;
278
+ }
279
+ if (fiber.type.displayName) {
280
+ return fiber.type.displayName;
281
+ }
282
+ if (fiber.type.render?.displayName || fiber.type.render?.name) {
283
+ return fiber.type.render.displayName || fiber.type.render.name;
284
+ }
285
+ }
286
+ if (fiber.return) {
287
+ return getFiberComponentName(fiber.return);
288
+ }
289
+ return void 0;
290
+ }
291
+ function getDomPath(element) {
292
+ const path = [];
293
+ let current = element;
294
+ while (current && current !== document.body) {
295
+ let selector = current.tagName.toLowerCase();
296
+ if (current.id) {
297
+ selector += `#${current.id}`;
298
+ } else if (current.className) {
299
+ const classes = current.className.split(/\s+/).filter((c) => c && !c.startsWith("__")).slice(0, 2).join(".");
300
+ if (classes) {
301
+ selector += `.${classes}`;
302
+ }
303
+ }
304
+ path.unshift(selector);
305
+ current = current.parentElement;
306
+ }
307
+ path.unshift("body");
308
+ return path.join(" > ");
309
+ }
310
+ function getParentComponents(element) {
311
+ const components = [];
312
+ const seen = /* @__PURE__ */ new Set();
313
+ let fiber = getReactFiber(element);
314
+ while (fiber) {
315
+ const name = getFiberComponentName(fiber);
316
+ if (name && !seen.has(name) && !isBuiltInComponent(name)) {
317
+ seen.add(name);
318
+ components.push(name);
319
+ }
320
+ fiber = fiber.return;
321
+ }
322
+ return components.reverse().slice(0, 10);
323
+ }
324
+ function isBuiltInComponent(name) {
325
+ const builtIns = [
326
+ "Fragment",
327
+ "Suspense",
328
+ "StrictMode",
329
+ "Profiler",
330
+ "Provider",
331
+ "Consumer",
332
+ "Context"
333
+ ];
334
+ return builtIns.some((b) => name.includes(b));
335
+ }
336
+ function capturePageContext() {
337
+ const mainContent = document.querySelector("main") || document.querySelector('[role="main"]');
338
+ const pageStructure = getPageStructure();
339
+ return {
340
+ selectedText: getSelectedText(),
341
+ selectedElement: {
342
+ tagName: "body",
343
+ className: document.body.className || "",
344
+ id: document.body.id || void 0,
345
+ innerText: truncateText(getVisibleTextContent(), 500)
346
+ },
347
+ componentName: mainContent ? getComponentName(mainContent) : void 0,
348
+ domPath: "body",
349
+ parentComponents: getRootComponents(),
350
+ currentUrl: window.location.href,
351
+ pageTitle: document.title
352
+ // Include page structure info in the innerText
353
+ };
354
+ }
355
+ function getPageStructure() {
356
+ const parts = [];
357
+ const landmarks = [
358
+ { selector: 'header, [role="banner"]', name: "header" },
359
+ { selector: 'nav, [role="navigation"]', name: "navigation" },
360
+ { selector: 'main, [role="main"]', name: "main content" },
361
+ { selector: 'aside, [role="complementary"]', name: "sidebar" },
362
+ { selector: 'footer, [role="contentinfo"]', name: "footer" }
363
+ ];
364
+ for (const { selector, name } of landmarks) {
365
+ if (document.querySelector(selector)) {
366
+ parts.push(name);
367
+ }
368
+ }
369
+ const buttons = document.querySelectorAll('button, [role="button"]').length;
370
+ const links = document.querySelectorAll("a[href]").length;
371
+ const forms = document.querySelectorAll("form").length;
372
+ const inputs = document.querySelectorAll("input, textarea, select").length;
373
+ if (buttons > 0) parts.push(`${buttons} buttons`);
374
+ if (links > 0) parts.push(`${links} links`);
375
+ if (forms > 0) parts.push(`${forms} forms`);
376
+ if (inputs > 0) parts.push(`${inputs} inputs`);
377
+ return parts.join(", ");
378
+ }
379
+ function getVisibleTextContent() {
380
+ const clone = document.body.cloneNode(true);
381
+ const toRemove = clone.querySelectorAll('script, style, noscript, [hidden], [aria-hidden="true"]');
382
+ toRemove.forEach((el) => el.remove());
383
+ const text = clone.innerText || clone.textContent || "";
384
+ return text.replace(/\s+/g, " ").trim();
385
+ }
386
+ function getRootComponents() {
387
+ const components = [];
388
+ const seen = /* @__PURE__ */ new Set();
389
+ const root = document.getElementById("root") || document.getElementById("app") || document.querySelector("[data-reactroot]");
390
+ if (root) {
391
+ const fiber = getReactFiber(root);
392
+ if (fiber) {
393
+ let current = fiber;
394
+ let depth = 0;
395
+ while (current && depth < 5) {
396
+ const name = getFiberComponentName(current);
397
+ if (name && !seen.has(name) && !isBuiltInComponent(name)) {
398
+ seen.add(name);
399
+ components.push(name);
400
+ }
401
+ current = current.child;
402
+ depth++;
403
+ }
404
+ }
405
+ }
406
+ return components;
407
+ }
408
+
409
+ // src/screenshot-capture.ts
410
+ import html2canvas from "html2canvas";
411
+ async function captureElementScreenshot(element, options = {}) {
412
+ const { padding = 20, maxWidth = 800, maxHeight = 600 } = options;
413
+ try {
414
+ const rect = element.getBoundingClientRect();
415
+ const x = Math.max(0, rect.left - padding + window.scrollX);
416
+ const y = Math.max(0, rect.top - padding + window.scrollY);
417
+ const width = Math.min(rect.width + padding * 2, maxWidth);
418
+ const height = Math.min(rect.height + padding * 2, maxHeight);
419
+ const canvas = await html2canvas(document.body, {
420
+ x,
421
+ y,
422
+ width,
423
+ height,
424
+ scrollX: -window.scrollX,
425
+ scrollY: -window.scrollY,
426
+ windowWidth: document.documentElement.scrollWidth,
427
+ windowHeight: document.documentElement.scrollHeight,
428
+ useCORS: true,
429
+ allowTaint: true,
430
+ backgroundColor: null,
431
+ logging: false
432
+ });
433
+ return canvas.toDataURL("image/png");
434
+ } catch (error) {
435
+ console.error("[ClaudeBridge] Screenshot capture failed:", error);
436
+ return void 0;
437
+ }
438
+ }
439
+ function createHighlightOverlay() {
440
+ const overlay = document.createElement("div");
441
+ overlay.id = "claude-bridge-highlight";
442
+ overlay.style.cssText = `
443
+ position: fixed;
444
+ pointer-events: none;
445
+ border: 2px solid #6366f1;
446
+ background: rgba(99, 102, 241, 0.1);
447
+ border-radius: 4px;
448
+ z-index: 999998;
449
+ transition: all 0.1s ease-out;
450
+ display: none;
451
+ `;
452
+ document.body.appendChild(overlay);
453
+ const label = document.createElement("div");
454
+ label.style.cssText = `
455
+ position: absolute;
456
+ top: -24px;
457
+ left: 0;
458
+ background: #6366f1;
459
+ color: white;
460
+ font-size: 11px;
461
+ font-family: ui-monospace, monospace;
462
+ padding: 2px 6px;
463
+ border-radius: 3px;
464
+ white-space: nowrap;
465
+ `;
466
+ overlay.appendChild(label);
467
+ return {
468
+ show: (element) => {
469
+ const rect = element.getBoundingClientRect();
470
+ overlay.style.display = "block";
471
+ overlay.style.top = `${rect.top}px`;
472
+ overlay.style.left = `${rect.left}px`;
473
+ overlay.style.width = `${rect.width}px`;
474
+ overlay.style.height = `${rect.height}px`;
475
+ let labelText = element.tagName.toLowerCase();
476
+ if (element.id) {
477
+ labelText += `#${element.id}`;
478
+ } else if (element.className) {
479
+ const firstClass = element.className.split(/\s+/)[0];
480
+ if (firstClass && !firstClass.startsWith("__")) {
481
+ labelText += `.${firstClass}`;
482
+ }
483
+ }
484
+ label.textContent = labelText;
485
+ },
486
+ hide: () => {
487
+ overlay.style.display = "none";
488
+ },
489
+ destroy: () => {
490
+ overlay.remove();
491
+ }
492
+ };
493
+ }
494
+
495
+ // src/SelectionOverlay.tsx
496
+ import { useEffect, useRef, useCallback } from "react";
497
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
498
+ function SelectionOverlay({
499
+ active,
500
+ onElementSelect,
501
+ selectedElement
502
+ }) {
503
+ const highlightRef = useRef(null);
504
+ const hoveredElementRef = useRef(null);
505
+ useEffect(() => {
506
+ highlightRef.current = createHighlightOverlay();
507
+ return () => {
508
+ highlightRef.current?.destroy();
509
+ highlightRef.current = null;
510
+ };
511
+ }, []);
512
+ useEffect(() => {
513
+ if (selectedElement) {
514
+ highlightRef.current?.show(selectedElement);
515
+ } else if (!active) {
516
+ highlightRef.current?.hide();
517
+ }
518
+ }, [selectedElement, active]);
519
+ const handleMouseMove = useCallback(
520
+ (e) => {
521
+ if (!active || selectedElement) return;
522
+ const target = e.target;
523
+ if (target.id === "claude-bridge-highlight" || target.id === "claude-bridge-overlay" || target.closest("#claude-bridge-prompt-dialog")) {
524
+ return;
525
+ }
526
+ if (target === hoveredElementRef.current) return;
527
+ hoveredElementRef.current = target;
528
+ highlightRef.current?.show(target);
529
+ },
530
+ [active, selectedElement]
531
+ );
532
+ const handleClick = useCallback(
533
+ (e) => {
534
+ if (!active) return;
535
+ const target = e.target;
536
+ if (target.id === "claude-bridge-highlight" || target.id === "claude-bridge-overlay" || target.closest("#claude-bridge-prompt-dialog")) {
537
+ return;
538
+ }
539
+ e.preventDefault();
540
+ e.stopPropagation();
541
+ onElementSelect(target);
542
+ },
543
+ [active, onElementSelect]
544
+ );
545
+ useEffect(() => {
546
+ if (!active) {
547
+ highlightRef.current?.hide();
548
+ return;
549
+ }
550
+ document.addEventListener("mousemove", handleMouseMove, true);
551
+ document.addEventListener("click", handleClick, true);
552
+ return () => {
553
+ document.removeEventListener("mousemove", handleMouseMove, true);
554
+ document.removeEventListener("click", handleClick, true);
555
+ };
556
+ }, [active, handleMouseMove, handleClick]);
557
+ if (!active) return null;
558
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
559
+ /* @__PURE__ */ jsx(
560
+ "div",
561
+ {
562
+ id: "claude-bridge-overlay",
563
+ style: {
564
+ position: "fixed",
565
+ top: 0,
566
+ left: 0,
567
+ right: 0,
568
+ bottom: 0,
569
+ zIndex: 999997,
570
+ pointerEvents: "none",
571
+ background: "rgba(0, 0, 0, 0.02)"
572
+ }
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsxs(
576
+ "div",
577
+ {
578
+ style: {
579
+ position: "fixed",
580
+ top: 16,
581
+ left: "50%",
582
+ transform: "translateX(-50%)",
583
+ zIndex: 999999,
584
+ background: "#6366f1",
585
+ color: "white",
586
+ padding: "8px 16px",
587
+ borderRadius: 8,
588
+ fontSize: 14,
589
+ fontFamily: "system-ui, -apple-system, sans-serif",
590
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
591
+ display: "flex",
592
+ alignItems: "center",
593
+ gap: 8
594
+ },
595
+ children: [
596
+ /* @__PURE__ */ jsx(
597
+ "span",
598
+ {
599
+ style: {
600
+ width: 8,
601
+ height: 8,
602
+ borderRadius: "50%",
603
+ background: "#22c55e",
604
+ animation: "claude-bridge-pulse 1.5s infinite"
605
+ }
606
+ }
607
+ ),
608
+ "Click an element to select it",
609
+ /* @__PURE__ */ jsx(
610
+ "span",
611
+ {
612
+ style: {
613
+ opacity: 0.7,
614
+ fontSize: 12,
615
+ marginLeft: 4
616
+ },
617
+ children: "(ESC to cancel)"
618
+ }
619
+ )
620
+ ]
621
+ }
622
+ ),
623
+ /* @__PURE__ */ jsx("style", { children: `
624
+ @keyframes claude-bridge-pulse {
625
+ 0%, 100% { opacity: 1; }
626
+ 50% { opacity: 0.5; }
627
+ }
628
+ ` })
629
+ ] });
630
+ }
631
+
632
+ // src/PromptDialog.tsx
633
+ import { useState, useRef as useRef2, useEffect as useEffect2 } from "react";
634
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
635
+ function PromptDialog({
636
+ open,
637
+ element,
638
+ onSubmit,
639
+ onClose,
640
+ onSwitchToQuestionMode,
641
+ isSubmitting = false
642
+ }) {
643
+ const [prompt, setPrompt] = useState("");
644
+ const inputRef = useRef2(null);
645
+ useEffect2(() => {
646
+ if (open && inputRef.current) {
647
+ inputRef.current.focus();
648
+ }
649
+ }, [open]);
650
+ useEffect2(() => {
651
+ if (!open) {
652
+ setPrompt("");
653
+ }
654
+ }, [open]);
655
+ const handleSubmit = () => {
656
+ if (!prompt.trim() || isSubmitting) return;
657
+ onSubmit(prompt.trim());
658
+ };
659
+ const handleKeyDown = (e) => {
660
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
661
+ e.preventDefault();
662
+ handleSubmit();
663
+ }
664
+ if (e.key === "Escape") {
665
+ e.preventDefault();
666
+ onClose();
667
+ }
668
+ };
669
+ if (!open || !element) return null;
670
+ const context = captureContext(element);
671
+ return /* @__PURE__ */ jsxs2(
672
+ "div",
673
+ {
674
+ id: "claude-bridge-prompt-dialog",
675
+ style: {
676
+ position: "fixed",
677
+ bottom: 24,
678
+ left: "50%",
679
+ transform: "translateX(-50%)",
680
+ zIndex: 1e6,
681
+ width: "100%",
682
+ maxWidth: 600,
683
+ background: "#1e1e2e",
684
+ borderRadius: 12,
685
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
686
+ fontFamily: "system-ui, -apple-system, sans-serif",
687
+ overflow: "hidden"
688
+ },
689
+ children: [
690
+ /* @__PURE__ */ jsxs2(
691
+ "div",
692
+ {
693
+ style: {
694
+ padding: "12px 16px",
695
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
696
+ display: "flex",
697
+ alignItems: "center",
698
+ gap: 8
699
+ },
700
+ children: [
701
+ /* @__PURE__ */ jsx2(
702
+ "div",
703
+ {
704
+ style: {
705
+ width: 8,
706
+ height: 8,
707
+ borderRadius: "50%",
708
+ background: "#6366f1"
709
+ }
710
+ }
711
+ ),
712
+ /* @__PURE__ */ jsx2(
713
+ "span",
714
+ {
715
+ style: {
716
+ color: "rgba(255, 255, 255, 0.9)",
717
+ fontSize: 13,
718
+ fontWeight: 500
719
+ },
720
+ children: context.componentName || context.selectedElement.tagName
721
+ }
722
+ ),
723
+ context.sourceFile && /* @__PURE__ */ jsxs2(
724
+ "span",
725
+ {
726
+ style: {
727
+ color: "rgba(255, 255, 255, 0.5)",
728
+ fontSize: 12,
729
+ fontFamily: "ui-monospace, monospace"
730
+ },
731
+ children: [
732
+ context.sourceFile,
733
+ context.sourceLine ? `:${context.sourceLine}` : ""
734
+ ]
735
+ }
736
+ ),
737
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1 } }),
738
+ /* @__PURE__ */ jsx2(
739
+ "button",
740
+ {
741
+ onClick: onClose,
742
+ style: {
743
+ background: "transparent",
744
+ border: "none",
745
+ color: "rgba(255, 255, 255, 0.5)",
746
+ cursor: "pointer",
747
+ padding: 4,
748
+ borderRadius: 4,
749
+ fontSize: 18,
750
+ lineHeight: 1
751
+ },
752
+ children: "\xD7"
753
+ }
754
+ )
755
+ ]
756
+ }
757
+ ),
758
+ /* @__PURE__ */ jsxs2("div", { style: { padding: 16 }, children: [
759
+ /* @__PURE__ */ jsx2(
760
+ "textarea",
761
+ {
762
+ ref: inputRef,
763
+ value: prompt,
764
+ onChange: (e) => setPrompt(e.target.value),
765
+ onKeyDown: handleKeyDown,
766
+ placeholder: "What would you like Claude to change?",
767
+ disabled: isSubmitting,
768
+ style: {
769
+ width: "100%",
770
+ minHeight: 80,
771
+ maxHeight: 200,
772
+ padding: 12,
773
+ background: "rgba(255, 255, 255, 0.05)",
774
+ border: "1px solid rgba(255, 255, 255, 0.1)",
775
+ borderRadius: 8,
776
+ color: "white",
777
+ fontSize: 14,
778
+ fontFamily: "inherit",
779
+ resize: "vertical",
780
+ outline: "none"
781
+ }
782
+ }
783
+ ),
784
+ /* @__PURE__ */ jsx2(
785
+ "div",
786
+ {
787
+ style: {
788
+ display: "flex",
789
+ flexWrap: "wrap",
790
+ gap: 8,
791
+ marginTop: 12
792
+ },
793
+ children: ["Make this bigger", "Change the color", "Add padding", "Hide this element"].map(
794
+ (suggestion) => /* @__PURE__ */ jsx2(
795
+ "button",
796
+ {
797
+ onClick: () => setPrompt(suggestion),
798
+ disabled: isSubmitting,
799
+ style: {
800
+ background: "rgba(255, 255, 255, 0.05)",
801
+ border: "1px solid rgba(255, 255, 255, 0.1)",
802
+ borderRadius: 4,
803
+ padding: "4px 8px",
804
+ color: "rgba(255, 255, 255, 0.7)",
805
+ fontSize: 12,
806
+ cursor: "pointer"
807
+ },
808
+ children: suggestion
809
+ },
810
+ suggestion
811
+ )
812
+ )
813
+ }
814
+ )
815
+ ] }),
816
+ /* @__PURE__ */ jsxs2(
817
+ "div",
818
+ {
819
+ style: {
820
+ padding: "12px 16px",
821
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
822
+ display: "flex",
823
+ justifyContent: "space-between",
824
+ alignItems: "center"
825
+ },
826
+ children: [
827
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
828
+ /* @__PURE__ */ jsxs2(
829
+ "span",
830
+ {
831
+ style: {
832
+ color: "rgba(255, 255, 255, 0.4)",
833
+ fontSize: 12
834
+ },
835
+ children: [
836
+ navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
837
+ "+Enter to submit"
838
+ ]
839
+ }
840
+ ),
841
+ onSwitchToQuestionMode && /* @__PURE__ */ jsx2(
842
+ "button",
843
+ {
844
+ onClick: onSwitchToQuestionMode,
845
+ disabled: isSubmitting,
846
+ style: {
847
+ background: "transparent",
848
+ border: "none",
849
+ color: "#22c55e",
850
+ fontSize: 12,
851
+ cursor: "pointer",
852
+ padding: 0,
853
+ textDecoration: "underline"
854
+ },
855
+ children: "Just ask a question instead"
856
+ }
857
+ )
858
+ ] }),
859
+ /* @__PURE__ */ jsx2(
860
+ "button",
861
+ {
862
+ onClick: handleSubmit,
863
+ disabled: !prompt.trim() || isSubmitting,
864
+ style: {
865
+ background: prompt.trim() && !isSubmitting ? "#6366f1" : "rgba(99, 102, 241, 0.3)",
866
+ border: "none",
867
+ borderRadius: 6,
868
+ padding: "8px 16px",
869
+ color: "white",
870
+ fontSize: 14,
871
+ fontWeight: 500,
872
+ cursor: prompt.trim() && !isSubmitting ? "pointer" : "not-allowed",
873
+ display: "flex",
874
+ alignItems: "center",
875
+ gap: 6
876
+ },
877
+ children: isSubmitting ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
878
+ /* @__PURE__ */ jsx2(
879
+ "span",
880
+ {
881
+ style: {
882
+ width: 14,
883
+ height: 14,
884
+ border: "2px solid rgba(255, 255, 255, 0.3)",
885
+ borderTopColor: "white",
886
+ borderRadius: "50%",
887
+ animation: "claude-bridge-spin 0.8s linear infinite"
888
+ }
889
+ }
890
+ ),
891
+ "Sending..."
892
+ ] }) : "Send to Claude"
893
+ }
894
+ )
895
+ ]
896
+ }
897
+ ),
898
+ /* @__PURE__ */ jsx2("style", { children: `
899
+ @keyframes claude-bridge-spin {
900
+ to { transform: rotate(360deg); }
901
+ }
902
+ ` })
903
+ ]
904
+ }
905
+ );
906
+ }
907
+
908
+ // src/QuestionDialog.tsx
909
+ import { useState as useState2, useRef as useRef3, useEffect as useEffect3 } from "react";
910
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
911
+ function QuestionDialog({
912
+ open,
913
+ selectedElement,
914
+ onSubmit,
915
+ onClose,
916
+ onSwitchToTaskMode,
917
+ isSubmitting = false
918
+ }) {
919
+ const [prompt, setPrompt] = useState2("");
920
+ const [includeElement, setIncludeElement] = useState2(true);
921
+ const inputRef = useRef3(null);
922
+ useEffect3(() => {
923
+ if (open && inputRef.current) {
924
+ inputRef.current.focus();
925
+ }
926
+ }, [open]);
927
+ useEffect3(() => {
928
+ if (!open) {
929
+ setPrompt("");
930
+ }
931
+ }, [open]);
932
+ useEffect3(() => {
933
+ setIncludeElement(!!selectedElement);
934
+ }, [selectedElement]);
935
+ const handleSubmit = () => {
936
+ if (!prompt.trim() || isSubmitting) return;
937
+ onSubmit(prompt.trim(), includeElement && !!selectedElement);
938
+ };
939
+ const handleKeyDown = (e) => {
940
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
941
+ e.preventDefault();
942
+ handleSubmit();
943
+ }
944
+ if (e.key === "Escape") {
945
+ e.preventDefault();
946
+ onClose();
947
+ }
948
+ };
949
+ if (!open) return null;
950
+ const pageContext = capturePageContext();
951
+ const elementContext = selectedElement ? captureContext(selectedElement) : null;
952
+ return /* @__PURE__ */ jsxs3(
953
+ "div",
954
+ {
955
+ id: "claude-bridge-question-dialog",
956
+ style: {
957
+ position: "fixed",
958
+ bottom: 24,
959
+ left: "50%",
960
+ transform: "translateX(-50%)",
961
+ zIndex: 1e6,
962
+ width: "100%",
963
+ maxWidth: 600,
964
+ background: "#1e1e2e",
965
+ borderRadius: 12,
966
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
967
+ fontFamily: "system-ui, -apple-system, sans-serif",
968
+ overflow: "hidden"
969
+ },
970
+ children: [
971
+ /* @__PURE__ */ jsxs3(
972
+ "div",
973
+ {
974
+ style: {
975
+ padding: "12px 16px",
976
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
977
+ display: "flex",
978
+ alignItems: "center",
979
+ gap: 8
980
+ },
981
+ children: [
982
+ /* @__PURE__ */ jsx3(
983
+ "div",
984
+ {
985
+ style: {
986
+ width: 8,
987
+ height: 8,
988
+ borderRadius: "50%",
989
+ background: "#22c55e"
990
+ // Green to differentiate from task dialog
991
+ }
992
+ }
993
+ ),
994
+ /* @__PURE__ */ jsx3(
995
+ "span",
996
+ {
997
+ style: {
998
+ color: "rgba(255, 255, 255, 0.9)",
999
+ fontSize: 13,
1000
+ fontWeight: 500
1001
+ },
1002
+ children: "Ask a Question"
1003
+ }
1004
+ ),
1005
+ /* @__PURE__ */ jsx3(
1006
+ "span",
1007
+ {
1008
+ style: {
1009
+ color: "rgba(255, 255, 255, 0.5)",
1010
+ fontSize: 12
1011
+ },
1012
+ children: pageContext.pageTitle
1013
+ }
1014
+ ),
1015
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1 } }),
1016
+ /* @__PURE__ */ jsx3(
1017
+ "button",
1018
+ {
1019
+ onClick: onClose,
1020
+ style: {
1021
+ background: "transparent",
1022
+ border: "none",
1023
+ color: "rgba(255, 255, 255, 0.5)",
1024
+ cursor: "pointer",
1025
+ padding: 4,
1026
+ borderRadius: 4,
1027
+ fontSize: 18,
1028
+ lineHeight: 1
1029
+ },
1030
+ children: "\xD7"
1031
+ }
1032
+ )
1033
+ ]
1034
+ }
1035
+ ),
1036
+ selectedElement && /* @__PURE__ */ jsx3(
1037
+ "div",
1038
+ {
1039
+ style: {
1040
+ padding: "8px 16px",
1041
+ background: "rgba(34, 197, 94, 0.1)",
1042
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
1043
+ display: "flex",
1044
+ alignItems: "center",
1045
+ gap: 8
1046
+ },
1047
+ children: /* @__PURE__ */ jsxs3(
1048
+ "label",
1049
+ {
1050
+ style: {
1051
+ display: "flex",
1052
+ alignItems: "center",
1053
+ gap: 8,
1054
+ cursor: "pointer",
1055
+ flex: 1
1056
+ },
1057
+ children: [
1058
+ /* @__PURE__ */ jsx3(
1059
+ "input",
1060
+ {
1061
+ type: "checkbox",
1062
+ checked: includeElement,
1063
+ onChange: (e) => setIncludeElement(e.target.checked),
1064
+ style: { cursor: "pointer" }
1065
+ }
1066
+ ),
1067
+ /* @__PURE__ */ jsx3(
1068
+ "span",
1069
+ {
1070
+ style: {
1071
+ color: "rgba(255, 255, 255, 0.7)",
1072
+ fontSize: 12
1073
+ },
1074
+ children: "Include selected element:"
1075
+ }
1076
+ ),
1077
+ /* @__PURE__ */ jsxs3(
1078
+ "span",
1079
+ {
1080
+ style: {
1081
+ color: "rgba(255, 255, 255, 0.9)",
1082
+ fontSize: 12,
1083
+ fontFamily: "ui-monospace, monospace"
1084
+ },
1085
+ children: [
1086
+ elementContext?.componentName || elementContext?.selectedElement.tagName,
1087
+ elementContext?.sourceFile && /* @__PURE__ */ jsxs3("span", { style: { color: "rgba(255, 255, 255, 0.5)", marginLeft: 4 }, children: [
1088
+ "(",
1089
+ elementContext.sourceFile,
1090
+ elementContext.sourceLine ? `:${elementContext.sourceLine}` : "",
1091
+ ")"
1092
+ ] })
1093
+ ]
1094
+ }
1095
+ )
1096
+ ]
1097
+ }
1098
+ )
1099
+ }
1100
+ ),
1101
+ /* @__PURE__ */ jsxs3("div", { style: { padding: 16 }, children: [
1102
+ /* @__PURE__ */ jsx3(
1103
+ "textarea",
1104
+ {
1105
+ ref: inputRef,
1106
+ value: prompt,
1107
+ onChange: (e) => setPrompt(e.target.value),
1108
+ onKeyDown: handleKeyDown,
1109
+ placeholder: "Ask a question about this page or codebase...",
1110
+ disabled: isSubmitting,
1111
+ style: {
1112
+ width: "100%",
1113
+ minHeight: 80,
1114
+ maxHeight: 200,
1115
+ padding: 12,
1116
+ background: "rgba(255, 255, 255, 0.05)",
1117
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1118
+ borderRadius: 8,
1119
+ color: "white",
1120
+ fontSize: 14,
1121
+ fontFamily: "inherit",
1122
+ resize: "vertical",
1123
+ outline: "none"
1124
+ }
1125
+ }
1126
+ ),
1127
+ /* @__PURE__ */ jsx3(
1128
+ "div",
1129
+ {
1130
+ style: {
1131
+ display: "flex",
1132
+ flexWrap: "wrap",
1133
+ gap: 8,
1134
+ marginTop: 12
1135
+ },
1136
+ children: [
1137
+ "What does this page do?",
1138
+ "Where are the backend routes?",
1139
+ "How does this component work?",
1140
+ "What API calls does this make?"
1141
+ ].map((suggestion) => /* @__PURE__ */ jsx3(
1142
+ "button",
1143
+ {
1144
+ onClick: () => setPrompt(suggestion),
1145
+ disabled: isSubmitting,
1146
+ style: {
1147
+ background: "rgba(255, 255, 255, 0.05)",
1148
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1149
+ borderRadius: 4,
1150
+ padding: "4px 8px",
1151
+ color: "rgba(255, 255, 255, 0.7)",
1152
+ fontSize: 12,
1153
+ cursor: "pointer"
1154
+ },
1155
+ children: suggestion
1156
+ },
1157
+ suggestion
1158
+ ))
1159
+ }
1160
+ )
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxs3(
1163
+ "div",
1164
+ {
1165
+ style: {
1166
+ padding: "12px 16px",
1167
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
1168
+ display: "flex",
1169
+ justifyContent: "space-between",
1170
+ alignItems: "center"
1171
+ },
1172
+ children: [
1173
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1174
+ /* @__PURE__ */ jsxs3(
1175
+ "span",
1176
+ {
1177
+ style: {
1178
+ color: "rgba(255, 255, 255, 0.4)",
1179
+ fontSize: 12
1180
+ },
1181
+ children: [
1182
+ navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
1183
+ "+Enter to submit"
1184
+ ]
1185
+ }
1186
+ ),
1187
+ onSwitchToTaskMode && /* @__PURE__ */ jsx3(
1188
+ "button",
1189
+ {
1190
+ onClick: onSwitchToTaskMode,
1191
+ disabled: isSubmitting,
1192
+ style: {
1193
+ background: "transparent",
1194
+ border: "none",
1195
+ color: "#6366f1",
1196
+ fontSize: 12,
1197
+ cursor: "pointer",
1198
+ padding: 0,
1199
+ textDecoration: "underline"
1200
+ },
1201
+ children: "Make changes instead"
1202
+ }
1203
+ )
1204
+ ] }),
1205
+ /* @__PURE__ */ jsx3(
1206
+ "button",
1207
+ {
1208
+ onClick: handleSubmit,
1209
+ disabled: !prompt.trim() || isSubmitting,
1210
+ style: {
1211
+ background: prompt.trim() && !isSubmitting ? "#22c55e" : "rgba(34, 197, 94, 0.3)",
1212
+ border: "none",
1213
+ borderRadius: 6,
1214
+ padding: "8px 16px",
1215
+ color: "white",
1216
+ fontSize: 14,
1217
+ fontWeight: 500,
1218
+ cursor: prompt.trim() && !isSubmitting ? "pointer" : "not-allowed",
1219
+ display: "flex",
1220
+ alignItems: "center",
1221
+ gap: 6
1222
+ },
1223
+ children: isSubmitting ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1224
+ /* @__PURE__ */ jsx3(
1225
+ "span",
1226
+ {
1227
+ style: {
1228
+ width: 14,
1229
+ height: 14,
1230
+ border: "2px solid rgba(255, 255, 255, 0.3)",
1231
+ borderTopColor: "white",
1232
+ borderRadius: "50%",
1233
+ animation: "claude-bridge-spin 0.8s linear infinite"
1234
+ }
1235
+ }
1236
+ ),
1237
+ "Asking..."
1238
+ ] }) : "Ask Claude"
1239
+ }
1240
+ )
1241
+ ]
1242
+ }
1243
+ ),
1244
+ /* @__PURE__ */ jsx3("style", { children: `
1245
+ @keyframes claude-bridge-spin {
1246
+ to { transform: rotate(360deg); }
1247
+ }
1248
+ ` })
1249
+ ]
1250
+ }
1251
+ );
1252
+ }
1253
+
1254
+ // src/TaskStatusToast.tsx
1255
+ import { useEffect as useEffect5, useState as useState4, useRef as useRef5, useCallback as useCallback3 } from "react";
1256
+
1257
+ // src/ClaudeTerminal.tsx
1258
+ import { useEffect as useEffect4, useRef as useRef4, useCallback as useCallback2, useState as useState3 } from "react";
1259
+ import { Terminal } from "@xterm/xterm";
1260
+ import { FitAddon } from "@xterm/addon-fit";
1261
+ import { WebglAddon } from "@xterm/addon-webgl";
1262
+ import "@xterm/xterm/css/xterm.css";
1263
+ import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1264
+ var XTERM_CRITICAL_CSS = `
1265
+ .xterm {
1266
+ cursor: text;
1267
+ position: relative;
1268
+ user-select: none;
1269
+ -ms-user-select: none;
1270
+ -webkit-user-select: none;
1271
+ }
1272
+ .xterm.focus,
1273
+ .xterm:focus {
1274
+ outline: none;
1275
+ }
1276
+ .xterm .xterm-helpers {
1277
+ position: absolute;
1278
+ top: 0;
1279
+ z-index: 5;
1280
+ }
1281
+ .xterm .xterm-helper-textarea {
1282
+ padding: 0;
1283
+ border: 0;
1284
+ margin: 0;
1285
+ position: absolute;
1286
+ opacity: 0;
1287
+ left: -9999em;
1288
+ top: 0;
1289
+ width: 0;
1290
+ height: 0;
1291
+ z-index: -5;
1292
+ white-space: nowrap;
1293
+ overflow: hidden;
1294
+ resize: none;
1295
+ }
1296
+ .xterm .xterm-helper-textarea:focus {
1297
+ outline: none;
1298
+ }
1299
+ .xterm .composition-view {
1300
+ background: #000;
1301
+ color: #FFF;
1302
+ display: none;
1303
+ position: absolute;
1304
+ white-space: nowrap;
1305
+ z-index: 1;
1306
+ }
1307
+ .xterm .composition-view.active {
1308
+ display: block;
1309
+ }
1310
+ .xterm .xterm-viewport {
1311
+ background-color: #000;
1312
+ overflow-y: scroll;
1313
+ cursor: default;
1314
+ position: absolute;
1315
+ right: 0;
1316
+ left: 0;
1317
+ top: 0;
1318
+ bottom: 0;
1319
+ }
1320
+ .xterm .xterm-screen {
1321
+ position: relative;
1322
+ }
1323
+ .xterm .xterm-screen canvas {
1324
+ position: absolute;
1325
+ left: 0;
1326
+ top: 0;
1327
+ }
1328
+ .xterm-char-measure-element {
1329
+ display: inline-block;
1330
+ visibility: hidden;
1331
+ position: absolute;
1332
+ top: 0;
1333
+ left: -9999em;
1334
+ line-height: normal;
1335
+ }
1336
+ .xterm.enable-mouse-events {
1337
+ cursor: default;
1338
+ }
1339
+ .xterm .xterm-cursor-pointer {
1340
+ cursor: pointer;
1341
+ }
1342
+ .xterm.xterm-cursor-style-block .xterm-cursor:not(.xterm-cursor-blink),
1343
+ .xterm.xterm-cursor-style-bar .xterm-cursor:not(.xterm-cursor-blink),
1344
+ .xterm.xterm-cursor-style-underline .xterm-cursor:not(.xterm-cursor-blink) {
1345
+ visibility: visible;
1346
+ }
1347
+ `;
1348
+ function ClaudeTerminal({
1349
+ serverUrl,
1350
+ token,
1351
+ sessionId,
1352
+ onSessionSelect,
1353
+ onConnectionChange,
1354
+ onSessionsUpdate,
1355
+ terminalOptions = {},
1356
+ style,
1357
+ className
1358
+ }) {
1359
+ const containerRef = useRef4(null);
1360
+ const terminalRef = useRef4(null);
1361
+ const fitAddonRef = useRef4(null);
1362
+ const wsRef = useRef4(null);
1363
+ const handleMessageRef = useRef4(null);
1364
+ const [connected, setConnected] = useState3(false);
1365
+ const [sessions, setSessions] = useState3([]);
1366
+ const [attachedSessionId, setAttachedSessionId] = useState3(null);
1367
+ const [error, setError] = useState3(null);
1368
+ useEffect4(() => {
1369
+ if (!containerRef.current) return;
1370
+ const terminal = new Terminal({
1371
+ cursorBlink: true,
1372
+ cursorStyle: "block",
1373
+ fontSize: terminalOptions.fontSize ?? 14,
1374
+ fontFamily: terminalOptions.fontFamily ?? 'Menlo, Monaco, "Courier New", monospace',
1375
+ theme: {
1376
+ background: terminalOptions.theme?.background ?? "#1a1a1a",
1377
+ foreground: terminalOptions.theme?.foreground ?? "#f0f0f0",
1378
+ cursor: terminalOptions.theme?.cursor ?? "#f0f0f0",
1379
+ cursorAccent: "#1a1a1a",
1380
+ black: "#1a1a1a",
1381
+ red: "#ff5555",
1382
+ green: "#50fa7b",
1383
+ yellow: "#f1fa8c",
1384
+ blue: "#6272a4",
1385
+ magenta: "#ff79c6",
1386
+ cyan: "#8be9fd",
1387
+ white: "#f8f8f2",
1388
+ brightBlack: "#6272a4",
1389
+ brightRed: "#ff6e6e",
1390
+ brightGreen: "#69ff94",
1391
+ brightYellow: "#ffffa5",
1392
+ brightBlue: "#d6acff",
1393
+ brightMagenta: "#ff92df",
1394
+ brightCyan: "#a4ffff",
1395
+ brightWhite: "#ffffff"
1396
+ },
1397
+ allowProposedApi: true,
1398
+ scrollback: 1e4,
1399
+ disableStdin: false,
1400
+ allowTransparency: true
1401
+ });
1402
+ const fitAddon = new FitAddon();
1403
+ terminal.loadAddon(fitAddon);
1404
+ terminal.open(containerRef.current);
1405
+ try {
1406
+ const webglAddon = new WebglAddon();
1407
+ webglAddon.onContextLoss(() => {
1408
+ webglAddon.dispose();
1409
+ });
1410
+ terminal.loadAddon(webglAddon);
1411
+ } catch (e) {
1412
+ console.warn("[ClaudeTerminal] WebGL addon could not be loaded:", e);
1413
+ }
1414
+ terminalRef.current = terminal;
1415
+ fitAddonRef.current = fitAddon;
1416
+ requestAnimationFrame(() => {
1417
+ fitAddon.fit();
1418
+ });
1419
+ const resizeObserver = new ResizeObserver(() => {
1420
+ requestAnimationFrame(() => {
1421
+ fitAddon.fit();
1422
+ if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
1423
+ wsRef.current.send(JSON.stringify({
1424
+ type: "terminal_resize",
1425
+ sessionId: attachedSessionId,
1426
+ cols: terminal.cols,
1427
+ rows: terminal.rows
1428
+ }));
1429
+ }
1430
+ });
1431
+ });
1432
+ resizeObserver.observe(containerRef.current);
1433
+ return () => {
1434
+ resizeObserver.disconnect();
1435
+ terminal.dispose();
1436
+ terminalRef.current = null;
1437
+ fitAddonRef.current = null;
1438
+ };
1439
+ }, [terminalOptions.fontSize, terminalOptions.fontFamily, terminalOptions.theme]);
1440
+ useEffect4(() => {
1441
+ const terminal = terminalRef.current;
1442
+ if (!terminal) return;
1443
+ const disposable = terminal.onData((data) => {
1444
+ if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
1445
+ wsRef.current.send(JSON.stringify({
1446
+ type: "terminal_input",
1447
+ sessionId: attachedSessionId,
1448
+ data
1449
+ }));
1450
+ }
1451
+ });
1452
+ return () => disposable.dispose();
1453
+ }, [attachedSessionId]);
1454
+ const attachedSessionIdRef = useRef4(null);
1455
+ attachedSessionIdRef.current = attachedSessionId;
1456
+ useEffect4(() => {
1457
+ handleMessageRef.current = (event) => {
1458
+ try {
1459
+ const message = JSON.parse(event.data);
1460
+ switch (message.type) {
1461
+ case "connected":
1462
+ wsRef.current?.send(JSON.stringify({ type: "get_sessions" }));
1463
+ break;
1464
+ case "sessions_list":
1465
+ if (message.sessions) {
1466
+ setSessions(message.sessions);
1467
+ onSessionsUpdate?.(message.sessions);
1468
+ }
1469
+ break;
1470
+ case "terminal_attached":
1471
+ if (message.sessionId) {
1472
+ setAttachedSessionId(message.sessionId);
1473
+ setError(null);
1474
+ if (message.scrollback && terminalRef.current) {
1475
+ terminalRef.current.write(message.scrollback);
1476
+ }
1477
+ if (fitAddonRef.current && terminalRef.current) {
1478
+ fitAddonRef.current.fit();
1479
+ wsRef.current?.send(JSON.stringify({
1480
+ type: "terminal_resize",
1481
+ sessionId: message.sessionId,
1482
+ cols: terminalRef.current.cols,
1483
+ rows: terminalRef.current.rows
1484
+ }));
1485
+ }
1486
+ }
1487
+ break;
1488
+ case "terminal_output":
1489
+ if (message.data && terminalRef.current && message.sessionId === attachedSessionIdRef.current) {
1490
+ terminalRef.current.write(message.data);
1491
+ }
1492
+ break;
1493
+ case "terminal_detached":
1494
+ if (message.sessionId === attachedSessionIdRef.current) {
1495
+ setAttachedSessionId(null);
1496
+ }
1497
+ break;
1498
+ case "error":
1499
+ setError(message.error || "Unknown error");
1500
+ console.error("[ClaudeTerminal] Error:", message.error);
1501
+ break;
1502
+ case "pong":
1503
+ break;
1504
+ }
1505
+ } catch (e) {
1506
+ console.error("[ClaudeTerminal] Failed to parse message:", e);
1507
+ }
1508
+ };
1509
+ }, [onSessionsUpdate]);
1510
+ useEffect4(() => {
1511
+ const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
1512
+ const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
1513
+ ws.onopen = () => {
1514
+ setConnected(true);
1515
+ setError(null);
1516
+ onConnectionChange?.(true);
1517
+ };
1518
+ ws.onmessage = (event) => {
1519
+ handleMessageRef.current?.(event);
1520
+ };
1521
+ ws.onclose = () => {
1522
+ setConnected(false);
1523
+ setAttachedSessionId(null);
1524
+ onConnectionChange?.(false);
1525
+ };
1526
+ ws.onerror = () => {
1527
+ setError("WebSocket connection error");
1528
+ };
1529
+ wsRef.current = ws;
1530
+ const pingInterval = setInterval(() => {
1531
+ if (ws.readyState === WebSocket.OPEN) {
1532
+ ws.send(JSON.stringify({ type: "ping" }));
1533
+ }
1534
+ }, 3e4);
1535
+ return () => {
1536
+ clearInterval(pingInterval);
1537
+ ws.close();
1538
+ wsRef.current = null;
1539
+ };
1540
+ }, [serverUrl, token, onConnectionChange]);
1541
+ useEffect4(() => {
1542
+ if (sessionId && connected && !attachedSessionId) {
1543
+ wsRef.current?.send(JSON.stringify({
1544
+ type: "attach_terminal",
1545
+ sessionId
1546
+ }));
1547
+ }
1548
+ }, [sessionId, connected, attachedSessionId]);
1549
+ useEffect4(() => {
1550
+ if (attachedSessionId && terminalRef.current && fitAddonRef.current) {
1551
+ const timer = setTimeout(() => {
1552
+ fitAddonRef.current?.fit();
1553
+ terminalRef.current?.focus();
1554
+ }, 50);
1555
+ return () => clearTimeout(timer);
1556
+ }
1557
+ }, [attachedSessionId]);
1558
+ const handleContainerClick = useCallback2(() => {
1559
+ terminalRef.current?.focus();
1560
+ }, []);
1561
+ const handleSelectSession = useCallback2((session) => {
1562
+ terminalRef.current?.clear();
1563
+ if (attachedSessionId) {
1564
+ wsRef.current?.send(JSON.stringify({
1565
+ type: "detach_terminal",
1566
+ sessionId: attachedSessionId
1567
+ }));
1568
+ }
1569
+ wsRef.current?.send(JSON.stringify({
1570
+ type: "attach_terminal",
1571
+ sessionId: session.id
1572
+ }));
1573
+ onSessionSelect?.(session);
1574
+ }, [attachedSessionId, onSessionSelect]);
1575
+ if (!sessionId && !attachedSessionId) {
1576
+ return /* @__PURE__ */ jsx4(
1577
+ "div",
1578
+ {
1579
+ className,
1580
+ style: {
1581
+ background: "#1a1a1a",
1582
+ color: "#f0f0f0",
1583
+ padding: "20px",
1584
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
1585
+ ...style
1586
+ },
1587
+ children: !connected ? /* @__PURE__ */ jsx4("div", { children: "Connecting to server..." }) : error ? /* @__PURE__ */ jsxs4("div", { style: { color: "#ff5555" }, children: [
1588
+ "Error: ",
1589
+ error
1590
+ ] }) : sessions.length === 0 ? /* @__PURE__ */ jsx4("div", { children: "No Claude sessions available" }) : /* @__PURE__ */ jsxs4("div", { children: [
1591
+ /* @__PURE__ */ jsx4("div", { style: { marginBottom: "10px", fontSize: "14px" }, children: "Select a Claude session:" }),
1592
+ sessions.map((session) => /* @__PURE__ */ jsxs4(
1593
+ "div",
1594
+ {
1595
+ onClick: () => handleSelectSession(session),
1596
+ style: {
1597
+ padding: "10px",
1598
+ margin: "5px 0",
1599
+ background: "#2a2a2a",
1600
+ borderRadius: "4px",
1601
+ cursor: "pointer",
1602
+ border: "1px solid #3a3a3a"
1603
+ },
1604
+ onMouseOver: (e) => {
1605
+ e.currentTarget.style.background = "#3a3a3a";
1606
+ },
1607
+ onMouseOut: (e) => {
1608
+ e.currentTarget.style.background = "#2a2a2a";
1609
+ },
1610
+ children: [
1611
+ /* @__PURE__ */ jsx4("div", { style: { fontWeight: "bold" }, children: session.displayName || session.name }),
1612
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "#888", marginTop: "4px" }, children: session.workingDir })
1613
+ ]
1614
+ },
1615
+ session.id
1616
+ ))
1617
+ ] })
1618
+ }
1619
+ );
1620
+ }
1621
+ return /* @__PURE__ */ jsxs4(Fragment4, { children: [
1622
+ /* @__PURE__ */ jsx4("style", { children: XTERM_CRITICAL_CSS }),
1623
+ /* @__PURE__ */ jsx4(
1624
+ "div",
1625
+ {
1626
+ ref: containerRef,
1627
+ className,
1628
+ onClick: handleContainerClick,
1629
+ style: {
1630
+ width: "100%",
1631
+ height: "100%",
1632
+ ...style
1633
+ }
1634
+ }
1635
+ )
1636
+ ] });
1637
+ }
1638
+
1639
+ // src/TaskStatusToast.tsx
1640
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1641
+ var MIN_WIDTH = 400;
1642
+ var MIN_HEIGHT = 300;
1643
+ var DEFAULT_WIDTH = 700;
1644
+ var DEFAULT_HEIGHT = 500;
1645
+ function TaskStatusToast({
1646
+ task,
1647
+ serverUrl,
1648
+ token,
1649
+ onDismiss,
1650
+ killOnClose = true
1651
+ }) {
1652
+ const [visible, setVisible] = useState4(false);
1653
+ const [expanded, setExpanded] = useState4(false);
1654
+ const [size, setSize] = useState4({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
1655
+ const [isResizing, setIsResizing] = useState4(false);
1656
+ const resizeRef = useRef5(null);
1657
+ useEffect5(() => {
1658
+ if (task) {
1659
+ setVisible(true);
1660
+ if (task.sessionId && (task.status === "running" || task.status === "starting")) {
1661
+ setExpanded(true);
1662
+ }
1663
+ }
1664
+ }, [task, task?.sessionId, task?.status]);
1665
+ useEffect5(() => {
1666
+ if (task && !expanded && (task.status === "completed" || task.status === "failed")) {
1667
+ const timer = setTimeout(() => {
1668
+ setVisible(false);
1669
+ setTimeout(onDismiss, 300);
1670
+ }, 1e4);
1671
+ return () => clearTimeout(timer);
1672
+ }
1673
+ }, [task?.status, expanded, onDismiss]);
1674
+ const handleResizeStart = useCallback3((e, direction) => {
1675
+ e.preventDefault();
1676
+ e.stopPropagation();
1677
+ setIsResizing(true);
1678
+ resizeRef.current = {
1679
+ startX: e.clientX,
1680
+ startY: e.clientY,
1681
+ startWidth: size.width,
1682
+ startHeight: size.height
1683
+ };
1684
+ const handleMouseMove = (moveEvent) => {
1685
+ if (!resizeRef.current) return;
1686
+ let newWidth = resizeRef.current.startWidth;
1687
+ let newHeight = resizeRef.current.startHeight;
1688
+ if (direction.includes("w")) {
1689
+ newWidth = resizeRef.current.startWidth - (moveEvent.clientX - resizeRef.current.startX);
1690
+ }
1691
+ if (direction.includes("n")) {
1692
+ newHeight = resizeRef.current.startHeight - (moveEvent.clientY - resizeRef.current.startY);
1693
+ }
1694
+ setSize({
1695
+ width: Math.max(MIN_WIDTH, newWidth),
1696
+ height: Math.max(MIN_HEIGHT, newHeight)
1697
+ });
1698
+ };
1699
+ const handleMouseUp = () => {
1700
+ setIsResizing(false);
1701
+ resizeRef.current = null;
1702
+ document.removeEventListener("mousemove", handleMouseMove);
1703
+ document.removeEventListener("mouseup", handleMouseUp);
1704
+ };
1705
+ document.addEventListener("mousemove", handleMouseMove);
1706
+ document.addEventListener("mouseup", handleMouseUp);
1707
+ }, [size]);
1708
+ const killSession = useCallback3((sessionId) => {
1709
+ return new Promise((resolve) => {
1710
+ const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
1711
+ const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
1712
+ ws.onopen = () => {
1713
+ ws.send(JSON.stringify({
1714
+ type: "kill_session",
1715
+ sessionId
1716
+ }));
1717
+ setTimeout(() => {
1718
+ ws.close();
1719
+ resolve();
1720
+ }, 100);
1721
+ };
1722
+ ws.onerror = () => {
1723
+ ws.close();
1724
+ resolve();
1725
+ };
1726
+ setTimeout(() => {
1727
+ if (ws.readyState !== WebSocket.CLOSED) {
1728
+ ws.close();
1729
+ }
1730
+ resolve();
1731
+ }, 2e3);
1732
+ });
1733
+ }, [serverUrl, token]);
1734
+ const handleDismiss = useCallback3(async () => {
1735
+ if (killOnClose && task?.sessionId) {
1736
+ await killSession(task.sessionId);
1737
+ }
1738
+ setVisible(false);
1739
+ setExpanded(false);
1740
+ setTimeout(onDismiss, 300);
1741
+ }, [killOnClose, task?.sessionId, killSession, onDismiss]);
1742
+ const handleToggleExpand = useCallback3(() => {
1743
+ setExpanded(!expanded);
1744
+ }, [expanded]);
1745
+ if (!task || !visible) return null;
1746
+ const statusColors = {
1747
+ queued: { bg: "#6366f1", text: "Queued" },
1748
+ starting: { bg: "#f59e0b", text: "Starting..." },
1749
+ running: { bg: "#22c55e", text: "Running" },
1750
+ completed: { bg: "#22c55e", text: "Completed" },
1751
+ failed: { bg: "#ef4444", text: "Failed" }
1752
+ };
1753
+ const status = statusColors[task.status] || statusColors.queued;
1754
+ const zedUrl = task.sessionId ? `${serverUrl}?token=${encodeURIComponent(token)}&session=${task.sessionId}` : `${serverUrl}?token=${encodeURIComponent(token)}`;
1755
+ const handleOpenZed = () => {
1756
+ window.open(zedUrl, "_blank");
1757
+ };
1758
+ if (!expanded) {
1759
+ return /* @__PURE__ */ jsxs5(
1760
+ "div",
1761
+ {
1762
+ style: {
1763
+ position: "fixed",
1764
+ bottom: 24,
1765
+ right: 24,
1766
+ zIndex: 1000001,
1767
+ background: "#1e1e2e",
1768
+ borderRadius: 12,
1769
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
1770
+ fontFamily: "system-ui, -apple-system, sans-serif",
1771
+ minWidth: 320,
1772
+ maxWidth: 400,
1773
+ opacity: visible ? 1 : 0,
1774
+ transform: visible ? "translateY(0)" : "translateY(20px)",
1775
+ transition: "opacity 0.3s, transform 0.3s"
1776
+ },
1777
+ children: [
1778
+ /* @__PURE__ */ jsxs5(
1779
+ "div",
1780
+ {
1781
+ style: {
1782
+ padding: "12px 16px",
1783
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
1784
+ display: "flex",
1785
+ alignItems: "center",
1786
+ gap: 10
1787
+ },
1788
+ children: [
1789
+ /* @__PURE__ */ jsx5(
1790
+ "div",
1791
+ {
1792
+ style: {
1793
+ width: 10,
1794
+ height: 10,
1795
+ borderRadius: "50%",
1796
+ background: status.bg,
1797
+ animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
1798
+ }
1799
+ }
1800
+ ),
1801
+ /* @__PURE__ */ jsxs5(
1802
+ "span",
1803
+ {
1804
+ style: {
1805
+ color: "rgba(255, 255, 255, 0.9)",
1806
+ fontSize: 14,
1807
+ fontWeight: 500,
1808
+ flex: 1
1809
+ },
1810
+ children: [
1811
+ "Claude Task: ",
1812
+ status.text
1813
+ ]
1814
+ }
1815
+ ),
1816
+ /* @__PURE__ */ jsx5(
1817
+ "button",
1818
+ {
1819
+ onClick: handleDismiss,
1820
+ style: {
1821
+ background: "transparent",
1822
+ border: "none",
1823
+ color: "rgba(255, 255, 255, 0.5)",
1824
+ cursor: "pointer",
1825
+ padding: 4,
1826
+ fontSize: 18,
1827
+ lineHeight: 1
1828
+ },
1829
+ children: "\xD7"
1830
+ }
1831
+ )
1832
+ ]
1833
+ }
1834
+ ),
1835
+ /* @__PURE__ */ jsxs5("div", { style: { padding: "12px 16px" }, children: [
1836
+ /* @__PURE__ */ jsx5(
1837
+ "p",
1838
+ {
1839
+ style: {
1840
+ color: "rgba(255, 255, 255, 0.7)",
1841
+ fontSize: 13,
1842
+ margin: 0,
1843
+ lineHeight: 1.5,
1844
+ overflow: "hidden",
1845
+ textOverflow: "ellipsis",
1846
+ display: "-webkit-box",
1847
+ WebkitLineClamp: 2,
1848
+ WebkitBoxOrient: "vertical"
1849
+ },
1850
+ children: task.prompt
1851
+ }
1852
+ ),
1853
+ task.error && /* @__PURE__ */ jsxs5(
1854
+ "p",
1855
+ {
1856
+ style: {
1857
+ color: "#ef4444",
1858
+ fontSize: 12,
1859
+ margin: "8px 0 0"
1860
+ },
1861
+ children: [
1862
+ "Error: ",
1863
+ task.error
1864
+ ]
1865
+ }
1866
+ )
1867
+ ] }),
1868
+ /* @__PURE__ */ jsxs5(
1869
+ "div",
1870
+ {
1871
+ style: {
1872
+ padding: "12px 16px",
1873
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
1874
+ display: "flex",
1875
+ justifyContent: "flex-end",
1876
+ gap: 8
1877
+ },
1878
+ children: [
1879
+ task.sessionId && /* @__PURE__ */ jsx5(
1880
+ "button",
1881
+ {
1882
+ onClick: handleToggleExpand,
1883
+ style: {
1884
+ background: "#374151",
1885
+ border: "none",
1886
+ borderRadius: 6,
1887
+ padding: "8px 16px",
1888
+ color: "white",
1889
+ fontSize: 13,
1890
+ fontWeight: 500,
1891
+ cursor: "pointer",
1892
+ display: "flex",
1893
+ alignItems: "center",
1894
+ gap: 6
1895
+ },
1896
+ children: "Show Terminal"
1897
+ }
1898
+ ),
1899
+ /* @__PURE__ */ jsxs5(
1900
+ "button",
1901
+ {
1902
+ onClick: handleOpenZed,
1903
+ style: {
1904
+ background: "#6366f1",
1905
+ border: "none",
1906
+ borderRadius: 6,
1907
+ padding: "8px 16px",
1908
+ color: "white",
1909
+ fontSize: 13,
1910
+ fontWeight: 500,
1911
+ cursor: "pointer",
1912
+ display: "flex",
1913
+ alignItems: "center",
1914
+ gap: 6
1915
+ },
1916
+ children: [
1917
+ "Open in Zed Controller",
1918
+ /* @__PURE__ */ jsx5("span", { style: { fontSize: 11 }, children: "\u2197" })
1919
+ ]
1920
+ }
1921
+ )
1922
+ ]
1923
+ }
1924
+ ),
1925
+ /* @__PURE__ */ jsx5("style", { children: `
1926
+ @keyframes claude-bridge-pulse {
1927
+ 0%, 100% { opacity: 1; }
1928
+ 50% { opacity: 0.5; }
1929
+ }
1930
+ ` })
1931
+ ]
1932
+ }
1933
+ );
1934
+ }
1935
+ return /* @__PURE__ */ jsxs5(
1936
+ "div",
1937
+ {
1938
+ style: {
1939
+ position: "fixed",
1940
+ bottom: 24,
1941
+ right: 24,
1942
+ zIndex: 1000001,
1943
+ width: size.width,
1944
+ height: size.height,
1945
+ background: "#1e1e2e",
1946
+ borderRadius: 12,
1947
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
1948
+ fontFamily: "system-ui, -apple-system, sans-serif",
1949
+ display: "flex",
1950
+ flexDirection: "column",
1951
+ opacity: visible ? 1 : 0,
1952
+ transition: isResizing ? "none" : "opacity 0.3s",
1953
+ overflow: "hidden"
1954
+ },
1955
+ children: [
1956
+ /* @__PURE__ */ jsx5(
1957
+ "div",
1958
+ {
1959
+ onMouseDown: (e) => handleResizeStart(e, "n"),
1960
+ style: {
1961
+ position: "absolute",
1962
+ top: 0,
1963
+ left: 20,
1964
+ right: 20,
1965
+ height: 6,
1966
+ cursor: "ns-resize",
1967
+ zIndex: 10
1968
+ }
1969
+ }
1970
+ ),
1971
+ /* @__PURE__ */ jsx5(
1972
+ "div",
1973
+ {
1974
+ onMouseDown: (e) => handleResizeStart(e, "w"),
1975
+ style: {
1976
+ position: "absolute",
1977
+ left: 0,
1978
+ top: 20,
1979
+ bottom: 20,
1980
+ width: 6,
1981
+ cursor: "ew-resize",
1982
+ zIndex: 10
1983
+ }
1984
+ }
1985
+ ),
1986
+ /* @__PURE__ */ jsx5(
1987
+ "div",
1988
+ {
1989
+ onMouseDown: (e) => handleResizeStart(e, "nw"),
1990
+ style: {
1991
+ position: "absolute",
1992
+ top: 0,
1993
+ left: 0,
1994
+ width: 20,
1995
+ height: 20,
1996
+ cursor: "nwse-resize",
1997
+ zIndex: 11
1998
+ }
1999
+ }
2000
+ ),
2001
+ /* @__PURE__ */ jsxs5(
2002
+ "div",
2003
+ {
2004
+ style: {
2005
+ padding: "10px 16px",
2006
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
2007
+ display: "flex",
2008
+ alignItems: "center",
2009
+ gap: 10,
2010
+ flexShrink: 0
2011
+ },
2012
+ children: [
2013
+ /* @__PURE__ */ jsx5(
2014
+ "div",
2015
+ {
2016
+ style: {
2017
+ width: 10,
2018
+ height: 10,
2019
+ borderRadius: "50%",
2020
+ background: status.bg,
2021
+ animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
2022
+ }
2023
+ }
2024
+ ),
2025
+ /* @__PURE__ */ jsxs5(
2026
+ "span",
2027
+ {
2028
+ style: {
2029
+ color: "rgba(255, 255, 255, 0.9)",
2030
+ fontSize: 14,
2031
+ fontWeight: 500,
2032
+ flex: 1,
2033
+ overflow: "hidden",
2034
+ textOverflow: "ellipsis",
2035
+ whiteSpace: "nowrap"
2036
+ },
2037
+ title: task.prompt,
2038
+ children: [
2039
+ status.text,
2040
+ ": ",
2041
+ task.prompt.slice(0, 50),
2042
+ task.prompt.length > 50 ? "..." : ""
2043
+ ]
2044
+ }
2045
+ ),
2046
+ /* @__PURE__ */ jsx5(
2047
+ "button",
2048
+ {
2049
+ onClick: handleToggleExpand,
2050
+ title: "Minimize",
2051
+ style: {
2052
+ background: "transparent",
2053
+ border: "none",
2054
+ color: "rgba(255, 255, 255, 0.5)",
2055
+ cursor: "pointer",
2056
+ padding: 4,
2057
+ fontSize: 14,
2058
+ lineHeight: 1
2059
+ },
2060
+ children: "\u2212"
2061
+ }
2062
+ ),
2063
+ /* @__PURE__ */ jsx5(
2064
+ "button",
2065
+ {
2066
+ onClick: handleOpenZed,
2067
+ title: "Open in Zed Controller",
2068
+ style: {
2069
+ background: "transparent",
2070
+ border: "none",
2071
+ color: "rgba(255, 255, 255, 0.5)",
2072
+ cursor: "pointer",
2073
+ padding: 4,
2074
+ fontSize: 14,
2075
+ lineHeight: 1
2076
+ },
2077
+ children: "\u2197"
2078
+ }
2079
+ ),
2080
+ /* @__PURE__ */ jsx5(
2081
+ "button",
2082
+ {
2083
+ onClick: handleDismiss,
2084
+ title: "Close",
2085
+ style: {
2086
+ background: "transparent",
2087
+ border: "none",
2088
+ color: "rgba(255, 255, 255, 0.5)",
2089
+ cursor: "pointer",
2090
+ padding: 4,
2091
+ fontSize: 18,
2092
+ lineHeight: 1
2093
+ },
2094
+ children: "\xD7"
2095
+ }
2096
+ )
2097
+ ]
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ jsx5("div", { style: { flex: 1, minHeight: 0 }, children: task.sessionId ? /* @__PURE__ */ jsx5(
2101
+ ClaudeTerminal,
2102
+ {
2103
+ serverUrl,
2104
+ token,
2105
+ sessionId: task.sessionId,
2106
+ style: { width: "100%", height: "100%" }
2107
+ }
2108
+ ) : /* @__PURE__ */ jsx5(
2109
+ "div",
2110
+ {
2111
+ style: {
2112
+ display: "flex",
2113
+ alignItems: "center",
2114
+ justifyContent: "center",
2115
+ height: "100%",
2116
+ color: "rgba(255, 255, 255, 0.5)"
2117
+ },
2118
+ children: "Waiting for session..."
2119
+ }
2120
+ ) }),
2121
+ task.error && /* @__PURE__ */ jsxs5(
2122
+ "div",
2123
+ {
2124
+ style: {
2125
+ padding: "8px 16px",
2126
+ background: "rgba(239, 68, 68, 0.2)",
2127
+ borderTop: "1px solid rgba(239, 68, 68, 0.3)",
2128
+ color: "#ef4444",
2129
+ fontSize: 12,
2130
+ flexShrink: 0
2131
+ },
2132
+ children: [
2133
+ "Error: ",
2134
+ task.error
2135
+ ]
2136
+ }
2137
+ ),
2138
+ /* @__PURE__ */ jsx5("style", { children: `
2139
+ @keyframes claude-bridge-pulse {
2140
+ 0%, 100% { opacity: 1; }
2141
+ 50% { opacity: 0.5; }
2142
+ }
2143
+ ` })
2144
+ ]
2145
+ }
2146
+ );
2147
+ }
2148
+
2149
+ // src/ClaudeBridgeProvider.tsx
2150
+ import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2151
+ var initialState = {
2152
+ connected: false,
2153
+ selectionMode: false,
2154
+ questionMode: false,
2155
+ selectedElement: null,
2156
+ activeTask: null,
2157
+ tasks: []
2158
+ };
2159
+ function reducer(state, action) {
2160
+ switch (action.type) {
2161
+ case "SET_CONNECTED":
2162
+ return { ...state, connected: action.connected };
2163
+ case "SET_SELECTION_MODE":
2164
+ return {
2165
+ ...state,
2166
+ selectionMode: action.enabled,
2167
+ questionMode: action.enabled ? false : state.questionMode,
2168
+ // Turn off question mode when entering selection mode
2169
+ selectedElement: action.enabled ? state.selectedElement : null
2170
+ };
2171
+ case "SET_QUESTION_MODE":
2172
+ return {
2173
+ ...state,
2174
+ questionMode: action.enabled,
2175
+ selectionMode: action.enabled ? false : state.selectionMode
2176
+ // Turn off selection mode when entering question mode
2177
+ };
2178
+ case "SET_SELECTED_ELEMENT":
2179
+ return { ...state, selectedElement: action.element };
2180
+ case "SET_ACTIVE_TASK":
2181
+ return { ...state, activeTask: action.task };
2182
+ case "ADD_TASK":
2183
+ return { ...state, tasks: [...state.tasks, action.task] };
2184
+ case "UPDATE_TASK":
2185
+ return {
2186
+ ...state,
2187
+ tasks: state.tasks.map(
2188
+ (t) => t.id === action.taskId ? { ...t, ...action.updates } : t
2189
+ ),
2190
+ activeTask: state.activeTask?.id === action.taskId ? { ...state.activeTask, ...action.updates } : state.activeTask
2191
+ };
2192
+ case "CLEAR_TASKS":
2193
+ return { ...state, tasks: [], activeTask: null };
2194
+ default:
2195
+ return state;
2196
+ }
2197
+ }
2198
+ var ClaudeBridgeContext = createContext(null);
2199
+ function ClaudeBridgeProvider({
2200
+ children,
2201
+ serverUrl,
2202
+ token,
2203
+ enabled = true,
2204
+ projectRoot,
2205
+ shortcut = "Meta+Shift+K",
2206
+ questionShortcut = "Meta+Shift+?",
2207
+ onTaskCreated,
2208
+ onTaskProgress,
2209
+ onTaskCompleted,
2210
+ onTaskFailed,
2211
+ onError
2212
+ }) {
2213
+ const [state, dispatch] = useReducer(reducer, initialState);
2214
+ const wsClientRef = useRef6(null);
2215
+ const pendingTaskResolveRef = useRef6(null);
2216
+ const config = {
2217
+ serverUrl,
2218
+ token,
2219
+ enabled,
2220
+ projectRoot,
2221
+ shortcut,
2222
+ questionShortcut,
2223
+ onTaskCreated,
2224
+ onTaskProgress,
2225
+ onTaskCompleted,
2226
+ onTaskFailed,
2227
+ onError
2228
+ };
2229
+ useEffect6(() => {
2230
+ if (!enabled) return;
2231
+ const client = new BridgeWebSocketClient(config);
2232
+ wsClientRef.current = client;
2233
+ const unsubConnection = client.onConnection((connected) => {
2234
+ dispatch({ type: "SET_CONNECTED", connected });
2235
+ });
2236
+ const unsubMessage = client.onMessage((message) => {
2237
+ handleMessage(message);
2238
+ });
2239
+ client.connect();
2240
+ return () => {
2241
+ unsubConnection();
2242
+ unsubMessage();
2243
+ client.disconnect();
2244
+ wsClientRef.current = null;
2245
+ };
2246
+ }, [enabled, serverUrl, token]);
2247
+ const handleMessage = useCallback4((message) => {
2248
+ switch (message.type) {
2249
+ case "task_created":
2250
+ if (message.task) {
2251
+ dispatch({ type: "ADD_TASK", task: message.task });
2252
+ dispatch({ type: "SET_ACTIVE_TASK", task: message.task });
2253
+ onTaskCreated?.(message.task.id);
2254
+ pendingTaskResolveRef.current?.(message.task.id);
2255
+ pendingTaskResolveRef.current = null;
2256
+ }
2257
+ break;
2258
+ case "task_progress":
2259
+ if (message.taskId && message.output) {
2260
+ dispatch({
2261
+ type: "UPDATE_TASK",
2262
+ taskId: message.taskId,
2263
+ updates: { status: "running" }
2264
+ });
2265
+ onTaskProgress?.(message.taskId, message.output);
2266
+ }
2267
+ break;
2268
+ case "task_completed":
2269
+ if (message.taskId) {
2270
+ dispatch({
2271
+ type: "UPDATE_TASK",
2272
+ taskId: message.taskId,
2273
+ updates: {
2274
+ status: "completed",
2275
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
2276
+ }
2277
+ });
2278
+ onTaskCompleted?.(message.taskId);
2279
+ }
2280
+ break;
2281
+ case "task_failed":
2282
+ if (message.taskId) {
2283
+ dispatch({
2284
+ type: "UPDATE_TASK",
2285
+ taskId: message.taskId,
2286
+ updates: {
2287
+ status: "failed",
2288
+ error: message.error,
2289
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
2290
+ }
2291
+ });
2292
+ onTaskFailed?.(message.taskId, message.error || "Unknown error");
2293
+ pendingTaskResolveRef.current?.(null);
2294
+ pendingTaskResolveRef.current = null;
2295
+ }
2296
+ break;
2297
+ case "pong":
2298
+ break;
2299
+ default:
2300
+ console.log("[ClaudeBridge] Unknown message type:", message.type);
2301
+ }
2302
+ }, [onTaskCreated, onTaskProgress, onTaskCompleted, onTaskFailed]);
2303
+ const matchesShortcut = useCallback4((e, shortcutStr) => {
2304
+ const keys = shortcutStr.split("+");
2305
+ const requiresMeta = keys.includes("Meta");
2306
+ const requiresCtrl = keys.includes("Ctrl");
2307
+ const requiresShift = keys.includes("Shift");
2308
+ const requiresAlt = keys.includes("Alt");
2309
+ const key = keys.find((k) => !["Meta", "Ctrl", "Shift", "Alt"].includes(k));
2310
+ return (requiresMeta ? e.metaKey : true) && (requiresCtrl ? e.ctrlKey : true) && (requiresShift ? e.shiftKey : true) && (requiresAlt ? e.altKey : true) && e.key.toUpperCase() === key?.toUpperCase();
2311
+ }, []);
2312
+ useEffect6(() => {
2313
+ if (!enabled) return;
2314
+ const handleKeyDown = (e) => {
2315
+ if (matchesShortcut(e, shortcut)) {
2316
+ e.preventDefault();
2317
+ if (!state.selectionMode) {
2318
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2319
+ }
2320
+ dispatch({ type: "SET_SELECTION_MODE", enabled: !state.selectionMode });
2321
+ return;
2322
+ }
2323
+ if (matchesShortcut(e, questionShortcut)) {
2324
+ e.preventDefault();
2325
+ if (!state.questionMode) {
2326
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2327
+ }
2328
+ dispatch({ type: "SET_QUESTION_MODE", enabled: !state.questionMode });
2329
+ return;
2330
+ }
2331
+ if (e.key === "Escape") {
2332
+ if (state.selectionMode) {
2333
+ dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2334
+ }
2335
+ if (state.questionMode) {
2336
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2337
+ }
2338
+ }
2339
+ };
2340
+ window.addEventListener("keydown", handleKeyDown);
2341
+ return () => window.removeEventListener("keydown", handleKeyDown);
2342
+ }, [enabled, shortcut, questionShortcut, state.selectionMode, state.questionMode, matchesShortcut]);
2343
+ const enableSelectionMode = useCallback4(() => {
2344
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2345
+ dispatch({ type: "SET_SELECTION_MODE", enabled: true });
2346
+ }, []);
2347
+ const disableSelectionMode = useCallback4(() => {
2348
+ dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2349
+ }, []);
2350
+ const enableQuestionMode = useCallback4(() => {
2351
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2352
+ dispatch({ type: "SET_QUESTION_MODE", enabled: true });
2353
+ }, []);
2354
+ const disableQuestionMode = useCallback4(() => {
2355
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2356
+ }, []);
2357
+ const submitTask = useCallback4(async (prompt) => {
2358
+ if (!state.selectedElement || !wsClientRef.current?.isConnected) {
2359
+ return null;
2360
+ }
2361
+ const context = captureContext(state.selectedElement);
2362
+ console.log("[ClaudeBridge] submitTask captured context:", {
2363
+ sourceFile: context.sourceFile,
2364
+ sourceLine: context.sourceLine,
2365
+ componentName: context.componentName,
2366
+ element: state.selectedElement.tagName
2367
+ });
2368
+ const screenshot = await captureElementScreenshot(state.selectedElement);
2369
+ const fullContext = {
2370
+ ...context,
2371
+ screenshot
2372
+ };
2373
+ const taskIdPromise = new Promise((resolve) => {
2374
+ pendingTaskResolveRef.current = resolve;
2375
+ setTimeout(() => {
2376
+ if (pendingTaskResolveRef.current === resolve) {
2377
+ pendingTaskResolveRef.current = null;
2378
+ resolve(null);
2379
+ }
2380
+ }, 1e4);
2381
+ });
2382
+ const sent = wsClientRef.current.createTask(prompt, fullContext);
2383
+ if (!sent) {
2384
+ pendingTaskResolveRef.current = null;
2385
+ return null;
2386
+ }
2387
+ dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2388
+ return taskIdPromise;
2389
+ }, [state.selectedElement]);
2390
+ const continueTask = useCallback4(async (taskId, prompt) => {
2391
+ if (!wsClientRef.current?.isConnected) {
2392
+ throw new Error("Not connected");
2393
+ }
2394
+ let context = void 0;
2395
+ if (state.selectedElement) {
2396
+ const baseContext = captureContext(state.selectedElement);
2397
+ const screenshot = await captureElementScreenshot(state.selectedElement);
2398
+ context = { ...baseContext, screenshot };
2399
+ }
2400
+ const sent = wsClientRef.current.continueTask(taskId, prompt, context);
2401
+ if (!sent) {
2402
+ throw new Error("Failed to send continue task message");
2403
+ }
2404
+ }, [state.selectedElement]);
2405
+ const submitQuestion = useCallback4(async (prompt, includeSelectedElement = false) => {
2406
+ if (!wsClientRef.current?.isConnected) {
2407
+ return null;
2408
+ }
2409
+ let context;
2410
+ if (includeSelectedElement && state.selectedElement) {
2411
+ const baseContext = captureContext(state.selectedElement);
2412
+ const screenshot = await captureElementScreenshot(state.selectedElement);
2413
+ context = { ...baseContext, screenshot };
2414
+ } else {
2415
+ context = capturePageContext();
2416
+ }
2417
+ const questionPrompt = `[QUESTION - No code changes expected, just provide information]
2418
+
2419
+ ${prompt}`;
2420
+ const taskIdPromise = new Promise((resolve) => {
2421
+ pendingTaskResolveRef.current = resolve;
2422
+ setTimeout(() => {
2423
+ if (pendingTaskResolveRef.current === resolve) {
2424
+ pendingTaskResolveRef.current = null;
2425
+ resolve(null);
2426
+ }
2427
+ }, 1e4);
2428
+ });
2429
+ const sent = wsClientRef.current.createTask(questionPrompt, context);
2430
+ if (!sent) {
2431
+ pendingTaskResolveRef.current = null;
2432
+ return null;
2433
+ }
2434
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2435
+ return taskIdPromise;
2436
+ }, [state.selectedElement]);
2437
+ const handleElementSelect = useCallback4((element) => {
2438
+ dispatch({ type: "SET_SELECTED_ELEMENT", element });
2439
+ }, []);
2440
+ const handlePromptSubmit = useCallback4(async (prompt) => {
2441
+ await submitTask(prompt);
2442
+ }, [submitTask]);
2443
+ const handlePromptClose = useCallback4(() => {
2444
+ dispatch({ type: "SET_SELECTED_ELEMENT", element: null });
2445
+ }, []);
2446
+ const handleQuestionSubmit = useCallback4(async (prompt, includeElement) => {
2447
+ await submitQuestion(prompt, includeElement);
2448
+ }, [submitQuestion]);
2449
+ const handleQuestionClose = useCallback4(() => {
2450
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2451
+ }, []);
2452
+ const handleSwitchToQuestionMode = useCallback4(() => {
2453
+ dispatch({ type: "SET_QUESTION_MODE", enabled: true });
2454
+ }, []);
2455
+ const handleSwitchToTaskMode = useCallback4(() => {
2456
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2457
+ if (!state.selectedElement) {
2458
+ dispatch({ type: "SET_SELECTION_MODE", enabled: true });
2459
+ }
2460
+ }, [state.selectedElement]);
2461
+ const handleToastDismiss = useCallback4(() => {
2462
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2463
+ }, []);
2464
+ const contextValue = {
2465
+ state,
2466
+ config,
2467
+ enableSelectionMode,
2468
+ disableSelectionMode,
2469
+ enableQuestionMode,
2470
+ disableQuestionMode,
2471
+ submitTask,
2472
+ submitQuestion,
2473
+ continueTask
2474
+ };
2475
+ return /* @__PURE__ */ jsxs6(ClaudeBridgeContext.Provider, { value: contextValue, children: [
2476
+ children,
2477
+ enabled && /* @__PURE__ */ jsxs6(Fragment5, { children: [
2478
+ /* @__PURE__ */ jsx6(
2479
+ SelectionOverlay,
2480
+ {
2481
+ active: state.selectionMode,
2482
+ onElementSelect: handleElementSelect,
2483
+ selectedElement: state.selectedElement
2484
+ }
2485
+ ),
2486
+ /* @__PURE__ */ jsx6(
2487
+ PromptDialog,
2488
+ {
2489
+ open: state.selectedElement !== null && !state.questionMode,
2490
+ element: state.selectedElement,
2491
+ onSubmit: handlePromptSubmit,
2492
+ onClose: handlePromptClose,
2493
+ onSwitchToQuestionMode: handleSwitchToQuestionMode,
2494
+ isSubmitting: state.activeTask?.status === "starting" || state.activeTask?.status === "running"
2495
+ }
2496
+ ),
2497
+ /* @__PURE__ */ jsx6(
2498
+ QuestionDialog,
2499
+ {
2500
+ open: state.questionMode,
2501
+ selectedElement: state.selectedElement,
2502
+ onSubmit: handleQuestionSubmit,
2503
+ onClose: handleQuestionClose,
2504
+ onSwitchToTaskMode: handleSwitchToTaskMode,
2505
+ isSubmitting: state.activeTask?.status === "starting" || state.activeTask?.status === "running"
2506
+ }
2507
+ ),
2508
+ /* @__PURE__ */ jsx6(
2509
+ TaskStatusToast,
2510
+ {
2511
+ task: state.activeTask,
2512
+ serverUrl,
2513
+ token,
2514
+ onDismiss: handleToastDismiss
2515
+ }
2516
+ )
2517
+ ] })
2518
+ ] });
2519
+ }
2520
+ function useClaudeBridge() {
2521
+ const context = useContext(ClaudeBridgeContext);
2522
+ if (!context) {
2523
+ throw new Error("useClaudeBridge must be used within a ClaudeBridgeProvider");
2524
+ }
2525
+ return context;
2526
+ }
2527
+ export {
2528
+ ClaudeBridgeProvider,
2529
+ ClaudeTerminal,
2530
+ TaskStatusToast,
2531
+ captureContext,
2532
+ captureElementScreenshot,
2533
+ capturePageContext,
2534
+ useClaudeBridge
2535
+ };
2536
+ //# sourceMappingURL=index.js.map