@radnine/storybook-addon-claude 0.5.1 → 0.5.3

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.
Files changed (2) hide show
  1. package/dist/manager.js +359 -304
  2. package/package.json +1 -1
package/dist/manager.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/manager.js
2
- import React11 from "react";
2
+ import React9 from "react";
3
3
  import { addons, types, useStorybookState as useStorybookState2 } from "storybook/manager-api";
4
4
 
5
5
  // src/constants.js
@@ -18,111 +18,14 @@ var CONNECTION_STATES = {
18
18
  };
19
19
 
20
20
  // src/Panel.jsx
21
- import React7, { useCallback as useCallback3 } from "react";
21
+ import React5, { useCallback as useCallback3 } from "react";
22
22
  import { useAddonState } from "storybook/manager-api";
23
23
 
24
- // src/components/ConnectionStatus.jsx
25
- import React from "react";
26
- var STATUS_CONFIG = {
27
- [CONNECTION_STATES.CONNECTED]: {
28
- label: "Connected",
29
- color: "#4caf50"
30
- },
31
- [CONNECTION_STATES.CONNECTING]: {
32
- label: "Connecting...",
33
- color: "#ff9800"
34
- },
35
- [CONNECTION_STATES.RECONNECTING]: {
36
- label: "Reconnecting...",
37
- color: "#ff9800"
38
- },
39
- [CONNECTION_STATES.DISCONNECTED]: {
40
- label: "Disconnected",
41
- color: "#f44336"
42
- }
43
- };
44
- function ConnectionStatus({ state, sessionId, onDisconnect, onNewChat, isProcessing }) {
45
- const config = STATUS_CONFIG[state] || STATUS_CONFIG[CONNECTION_STATES.DISCONNECTED];
46
- const isConnected = state === CONNECTION_STATES.CONNECTED;
47
- return /* @__PURE__ */ React.createElement("div", { style: styles.container }, /* @__PURE__ */ React.createElement("div", { style: styles.indicator }, /* @__PURE__ */ React.createElement("span", { style: { ...styles.dot, backgroundColor: config.color } }), /* @__PURE__ */ React.createElement("span", { style: styles.label }, config.label)), /* @__PURE__ */ React.createElement("div", { style: styles.right }, sessionId && /* @__PURE__ */ React.createElement("span", { style: styles.sessionId, title: sessionId }, sessionId.slice(0, 8), "..."), onNewChat && isConnected && /* @__PURE__ */ React.createElement(
48
- "button",
49
- {
50
- onClick: onNewChat,
51
- disabled: isProcessing,
52
- style: {
53
- ...styles.actionButton,
54
- ...isProcessing ? styles.actionButtonDisabled : {}
55
- },
56
- title: "Clear chat and start fresh session",
57
- "aria-label": "New Chat"
58
- },
59
- "New Chat"
60
- ), onDisconnect && /* @__PURE__ */ React.createElement(
61
- "button",
62
- {
63
- onClick: onDisconnect,
64
- style: styles.actionButton,
65
- title: isConnected ? "Disconnect" : "Change Token",
66
- "aria-label": isConnected ? "Disconnect" : "Change Token"
67
- },
68
- isConnected ? "Disconnect" : "Change Token"
69
- )));
70
- }
71
- var styles = {
72
- container: {
73
- display: "flex",
74
- alignItems: "center",
75
- justifyContent: "space-between",
76
- padding: "6px 12px",
77
- borderBottom: "1px solid rgba(0,0,0,0.1)",
78
- fontSize: "12px",
79
- fontFamily: "inherit"
80
- },
81
- indicator: {
82
- display: "flex",
83
- alignItems: "center",
84
- gap: "6px"
85
- },
86
- dot: {
87
- width: "8px",
88
- height: "8px",
89
- borderRadius: "50%",
90
- display: "inline-block"
91
- },
92
- label: {
93
- color: "#666"
94
- },
95
- right: {
96
- display: "flex",
97
- alignItems: "center",
98
- gap: "8px"
99
- },
100
- sessionId: {
101
- color: "#999",
102
- fontFamily: "monospace",
103
- fontSize: "11px"
104
- },
105
- actionButton: {
106
- padding: "2px 8px",
107
- border: "1px solid #ccc",
108
- borderRadius: "4px",
109
- backgroundColor: "transparent",
110
- color: "#666",
111
- fontSize: "11px",
112
- cursor: "pointer",
113
- lineHeight: "1.4"
114
- },
115
- actionButtonDisabled: {
116
- opacity: 0.4,
117
- cursor: "default"
118
- }
119
- };
120
-
121
24
  // src/components/MessageList.jsx
122
- import React3, { useEffect, useRef } from "react";
25
+ import React2, { useEffect, useRef } from "react";
123
26
 
124
27
  // src/components/MarkdownContent.jsx
125
- import React2, { useMemo } from "react";
28
+ import React, { useMemo } from "react";
126
29
  import { marked } from "marked";
127
30
  var renderer = new marked.Renderer();
128
31
  renderer.link = function({ href, title, text }) {
@@ -173,7 +76,7 @@ function MarkdownContent({ text }) {
173
76
  if (!text) return "";
174
77
  return marked.parse(text);
175
78
  }, [text]);
176
- return /* @__PURE__ */ React2.createElement(
79
+ return /* @__PURE__ */ React.createElement(
177
80
  "div",
178
81
  {
179
82
  className: "claude-md",
@@ -183,56 +86,58 @@ function MarkdownContent({ text }) {
183
86
  }
184
87
 
185
88
  // src/components/MessageList.jsx
186
- function MessageList({ messages }) {
89
+ function MessageList({ messages, isProcessing }) {
187
90
  const bottomRef = useRef(null);
188
91
  useEffect(() => {
189
92
  var _a;
190
93
  (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
191
- }, [messages]);
94
+ }, [messages, isProcessing]);
192
95
  if (messages.length === 0) {
193
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.empty }, "No messages yet. Send a message to start chatting with Claude.");
96
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.empty }, "No messages yet. Send a message to start chatting with Claude.");
194
97
  }
195
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.container }, messages.map((msg) => /* @__PURE__ */ React3.createElement(MessageItem, { key: msg.id, message: msg })), /* @__PURE__ */ React3.createElement("div", { ref: bottomRef }));
98
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.container }, messages.map((msg) => /* @__PURE__ */ React2.createElement(MessageItem, { key: msg.id, message: msg })), isProcessing && /* @__PURE__ */ React2.createElement(TypingIndicator, null), /* @__PURE__ */ React2.createElement("div", { ref: bottomRef }));
196
99
  }
197
100
  function MessageItem({ message }) {
198
101
  switch (message.type) {
199
102
  case "user_input":
200
- return /* @__PURE__ */ React3.createElement(UserMessage, { text: message.text });
103
+ return /* @__PURE__ */ React2.createElement(UserMessage, { text: message.text });
201
104
  case "skill_invocation":
202
- return /* @__PURE__ */ React3.createElement(SkillInvocationMessage, { skill: message.skill });
105
+ return /* @__PURE__ */ React2.createElement(SkillInvocationMessage, { skill: message.skill });
203
106
  case "output":
204
- return /* @__PURE__ */ React3.createElement(OutputMessage, { data: message.data, replay: message.replay });
107
+ return /* @__PURE__ */ React2.createElement(OutputMessage, { data: message.data, replay: message.replay });
205
108
  case "complete":
206
- return /* @__PURE__ */ React3.createElement(CompleteMessage, { message });
109
+ return /* @__PURE__ */ React2.createElement(CompleteMessage, { message });
110
+ case "cancelled":
111
+ return /* @__PURE__ */ React2.createElement(CancelledMessage, null);
207
112
  case "error":
208
- return /* @__PURE__ */ React3.createElement(ErrorMessage, { message });
113
+ return /* @__PURE__ */ React2.createElement(ErrorMessage, { message });
209
114
  default:
210
115
  return null;
211
116
  }
212
117
  }
213
118
  function UserMessage({ text }) {
214
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.userRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.userBubble }, text));
119
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.userRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.userBubble }, text));
215
120
  }
216
121
  function OutputMessage({ data, replay }) {
217
122
  if (!data) return null;
218
123
  const classifiedType = classifyOutputType(data);
219
124
  switch (classifiedType) {
220
125
  case "assistant":
221
- return /* @__PURE__ */ React3.createElement(AssistantMessage, { data, replay });
126
+ return /* @__PURE__ */ React2.createElement(AssistantMessage, { data, replay });
222
127
  case "tool_use":
223
- return /* @__PURE__ */ React3.createElement(ToolUseMessage, { data });
128
+ return /* @__PURE__ */ React2.createElement(ToolUseMessage, { data });
224
129
  case "tool_result":
225
- return /* @__PURE__ */ React3.createElement(ToolResultMessage, { data });
130
+ return /* @__PURE__ */ React2.createElement(ToolResultMessage, { data });
226
131
  case "result":
227
- return /* @__PURE__ */ React3.createElement(ResultMessage, { data });
132
+ return /* @__PURE__ */ React2.createElement(ResultMessage, { data });
228
133
  case "system":
229
- return /* @__PURE__ */ React3.createElement(SystemMessage, { data });
134
+ return /* @__PURE__ */ React2.createElement(SystemMessage, { data });
230
135
  case "rate_limit_event":
231
- return /* @__PURE__ */ React3.createElement(RateLimitMessage, { data });
136
+ return /* @__PURE__ */ React2.createElement(RateLimitMessage, { data });
232
137
  case "_skip":
233
138
  return null;
234
139
  default:
235
- return /* @__PURE__ */ React3.createElement(GenericOutputMessage, { data, replay });
140
+ return /* @__PURE__ */ React2.createElement(GenericOutputMessage, { data, replay });
236
141
  }
237
142
  }
238
143
  function classifyOutputType(data) {
@@ -268,31 +173,31 @@ function classifyOutputType(data) {
268
173
  }
269
174
  function AssistantMessage({ data, replay }) {
270
175
  const text = extractAssistantText(data);
271
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.assistantRow }, /* @__PURE__ */ React3.createElement("div", { style: { ...styles2.assistantBubble, ...replay ? styles2.replay : {} } }, text ? /* @__PURE__ */ React3.createElement(MarkdownContent, { text }) : "(empty response)"));
176
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.assistantRow }, /* @__PURE__ */ React2.createElement("div", { style: { ...styles.assistantBubble, ...replay ? styles.replay : {} } }, text ? /* @__PURE__ */ React2.createElement(MarkdownContent, { text }) : "(empty response)"));
272
177
  }
273
178
  function ToolUseMessage({ data }) {
274
179
  const toolName = extractToolName(data);
275
180
  const summary = extractToolSummary(data);
276
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.toolRow }, /* @__PURE__ */ React3.createElement("details", { style: styles2.toolDetails }, /* @__PURE__ */ React3.createElement("summary", { style: styles2.toolSummary }, /* @__PURE__ */ React3.createElement("span", { style: styles2.toolIcon }, "\u2699"), " ", toolName, summary ? `: ${summary}` : ""), /* @__PURE__ */ React3.createElement("pre", { style: styles2.toolContent }, JSON.stringify(data, null, 2))));
181
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.toolRow }, /* @__PURE__ */ React2.createElement("details", { style: styles.toolDetails }, /* @__PURE__ */ React2.createElement("summary", { style: styles.toolSummary }, /* @__PURE__ */ React2.createElement("span", { style: styles.toolIcon }, "\u2699"), " ", toolName, summary ? `: ${summary}` : ""), /* @__PURE__ */ React2.createElement("pre", { style: styles.toolContent }, JSON.stringify(data, null, 2))));
277
182
  }
278
183
  function ToolResultMessage({ data }) {
279
184
  const isError = extractToolResultError(data);
280
185
  const summary = extractToolResultSummary(data);
281
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.toolRow }, /* @__PURE__ */ React3.createElement("details", { style: styles2.toolDetails }, /* @__PURE__ */ React3.createElement("summary", { style: { ...styles2.toolSummary, color: isError ? "#f44336" : "#4caf50" } }, isError ? "Error" : "Done", summary ? `: ${summary}` : ""), /* @__PURE__ */ React3.createElement("pre", { style: styles2.toolContent }, JSON.stringify(data, null, 2))));
186
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.toolRow }, /* @__PURE__ */ React2.createElement("details", { style: styles.toolDetails }, /* @__PURE__ */ React2.createElement("summary", { style: { ...styles.toolSummary, color: isError ? "#f44336" : "#4caf50" } }, isError ? "Error" : "Done", summary ? `: ${summary}` : ""), /* @__PURE__ */ React2.createElement("pre", { style: styles.toolContent }, JSON.stringify(data, null, 2))));
282
187
  }
283
188
  function ResultMessage({ data }) {
284
189
  const subtype = (data == null ? void 0 : data.subtype) || "unknown";
285
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.resultRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.resultBubble }, /* @__PURE__ */ React3.createElement("span", { style: styles2.resultLabel }, subtype === "success" ? "Completed" : `Result: ${subtype}`)));
190
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.resultRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.resultBubble }, /* @__PURE__ */ React2.createElement("span", { style: styles.resultLabel }, subtype === "success" ? "Completed" : `Result: ${subtype}`)));
286
191
  }
287
192
  function SystemMessage({ data }) {
288
193
  if (typeof (data == null ? void 0 : data.message) === "string") {
289
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.systemRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.systemBubble }, data.message));
194
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.systemRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.systemBubble }, data.message));
290
195
  }
291
196
  const label = (data == null ? void 0 : data.subtype) ? `System: ${data.subtype}` : "System event";
292
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.toolRow }, /* @__PURE__ */ React3.createElement("details", { style: styles2.toolDetails }, /* @__PURE__ */ React3.createElement("summary", { style: styles2.toolSummary }, label), /* @__PURE__ */ React3.createElement("pre", { style: styles2.toolContent }, JSON.stringify(data, null, 2))));
197
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.toolRow }, /* @__PURE__ */ React2.createElement("details", { style: styles.toolDetails }, /* @__PURE__ */ React2.createElement("summary", { style: styles.toolSummary }, label), /* @__PURE__ */ React2.createElement("pre", { style: styles.toolContent }, JSON.stringify(data, null, 2))));
293
198
  }
294
199
  function RateLimitMessage({ data }) {
295
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.toolRow }, /* @__PURE__ */ React3.createElement("details", { style: styles2.toolDetails }, /* @__PURE__ */ React3.createElement("summary", { style: { ...styles2.toolSummary, color: "#ff9800" } }, "Rate limited (waiting...)"), /* @__PURE__ */ React3.createElement("pre", { style: styles2.toolContent }, JSON.stringify(data, null, 2))));
200
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.toolRow }, /* @__PURE__ */ React2.createElement("details", { style: styles.toolDetails }, /* @__PURE__ */ React2.createElement("summary", { style: { ...styles.toolSummary, color: "#ff9800" } }, "Rate limited (waiting...)"), /* @__PURE__ */ React2.createElement("pre", { style: styles.toolContent }, JSON.stringify(data, null, 2))));
296
201
  }
297
202
  function GenericOutputMessage({ data, replay }) {
298
203
  const text = extractAssistantText(data);
@@ -300,19 +205,30 @@ function GenericOutputMessage({ data, replay }) {
300
205
  return null;
301
206
  }
302
207
  if (text) {
303
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.assistantRow }, /* @__PURE__ */ React3.createElement("div", { style: { ...styles2.assistantBubble, ...replay ? styles2.replay : {} } }, /* @__PURE__ */ React3.createElement(MarkdownContent, { text })));
208
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.assistantRow }, /* @__PURE__ */ React2.createElement("div", { style: { ...styles.assistantBubble, ...replay ? styles.replay : {} } }, /* @__PURE__ */ React2.createElement(MarkdownContent, { text })));
304
209
  }
305
210
  const summaryLabel = typeof data === "object" && data.type ? `System: ${data.type}` : "System message";
306
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.toolRow }, /* @__PURE__ */ React3.createElement("details", { style: styles2.toolDetails }, /* @__PURE__ */ React3.createElement("summary", { style: styles2.toolSummary }, summaryLabel), /* @__PURE__ */ React3.createElement("pre", { style: styles2.toolContent }, JSON.stringify(data, null, 2))));
211
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.toolRow }, /* @__PURE__ */ React2.createElement("details", { style: styles.toolDetails }, /* @__PURE__ */ React2.createElement("summary", { style: styles.toolSummary }, summaryLabel), /* @__PURE__ */ React2.createElement("pre", { style: styles.toolContent }, JSON.stringify(data, null, 2))));
307
212
  }
308
213
  function CompleteMessage({ message }) {
309
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.systemRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.completeBubble }, "Command completed (exit ", message.exitCode ?? "?", ")", message.durationMs != null && ` in ${(message.durationMs / 1e3).toFixed(1)}s`));
214
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.systemRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.completeBubble }, "Command completed (exit ", message.exitCode ?? "?", ")", message.durationMs != null && ` in ${(message.durationMs / 1e3).toFixed(1)}s`));
215
+ }
216
+ function CancelledMessage() {
217
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.resultRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.cancelledBubble }, /* @__PURE__ */ React2.createElement("span", { style: styles.resultLabel }, "Cancelled")));
310
218
  }
311
219
  function SkillInvocationMessage({ skill }) {
312
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.skillRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.skillBubble }, /* @__PURE__ */ React3.createElement("span", { style: styles2.skillIcon }, skill.icon), /* @__PURE__ */ React3.createElement("span", { style: styles2.skillLabel }, "Skill: ", skill.label)));
220
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.skillRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.skillBubble }, /* @__PURE__ */ React2.createElement("span", { style: styles.skillIcon }, skill.icon), /* @__PURE__ */ React2.createElement("span", { style: styles.skillLabel }, "Skill: ", skill.label)));
221
+ }
222
+ function TypingIndicator() {
223
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.assistantRow }, /* @__PURE__ */ React2.createElement("style", null, `
224
+ @keyframes claude-typing-bounce {
225
+ 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
226
+ 30% { opacity: 1; transform: translateY(-3px); }
227
+ }
228
+ `), /* @__PURE__ */ React2.createElement("div", { style: styles.typingBubble }, /* @__PURE__ */ React2.createElement("span", { style: styles.typingDot }), /* @__PURE__ */ React2.createElement("span", { style: { ...styles.typingDot, animationDelay: "0.2s" } }), /* @__PURE__ */ React2.createElement("span", { style: { ...styles.typingDot, animationDelay: "0.4s" } })));
313
229
  }
314
230
  function ErrorMessage({ message }) {
315
- return /* @__PURE__ */ React3.createElement("div", { style: styles2.errorRow }, /* @__PURE__ */ React3.createElement("div", { style: styles2.errorBubble }, message.code && /* @__PURE__ */ React3.createElement("span", { style: styles2.errorCode }, message.code, ": "), message.message || "Unknown error"));
231
+ return /* @__PURE__ */ React2.createElement("div", { style: styles.errorRow }, /* @__PURE__ */ React2.createElement("div", { style: styles.errorBubble }, message.code && /* @__PURE__ */ React2.createElement("span", { style: styles.errorCode }, message.code, ": "), message.message || "Unknown error"));
316
232
  }
317
233
  function extractAssistantText(data) {
318
234
  var _a;
@@ -369,7 +285,7 @@ function extractToolResultSummary(data) {
369
285
  }
370
286
  return null;
371
287
  }
372
- var styles2 = {
288
+ var styles = {
373
289
  container: {
374
290
  flex: 1,
375
291
  overflowY: "auto",
@@ -495,6 +411,29 @@ var styles2 = {
495
411
  fontSize: "11px",
496
412
  color: "#777"
497
413
  },
414
+ typingBubble: {
415
+ display: "flex",
416
+ alignItems: "center",
417
+ gap: "4px",
418
+ padding: "10px 14px",
419
+ borderRadius: "12px 12px 12px 2px",
420
+ backgroundColor: "#f0f0f0"
421
+ },
422
+ typingDot: {
423
+ width: "6px",
424
+ height: "6px",
425
+ borderRadius: "50%",
426
+ backgroundColor: "#999",
427
+ animation: "claude-typing-bounce 1.2s infinite"
428
+ },
429
+ cancelledBubble: {
430
+ padding: "6px 12px",
431
+ borderRadius: "8px",
432
+ backgroundColor: "#fff3e0",
433
+ fontSize: "12px",
434
+ color: "#e65100",
435
+ textAlign: "center"
436
+ },
498
437
  genericContent: {
499
438
  margin: 0,
500
439
  padding: "8px",
@@ -547,8 +486,8 @@ var styles2 = {
547
486
  };
548
487
 
549
488
  // src/components/MessageInput.jsx
550
- import React4, { useState, useRef as useRef2 } from "react";
551
- function MessageInput({ onSend, disabled, isProcessing, placeholder }) {
489
+ import React3, { useState, useRef as useRef2 } from "react";
490
+ function MessageInput({ onSend, disabled, isProcessing, placeholder, onCancel }) {
552
491
  const [text, setText] = useState("");
553
492
  const textareaRef = useRef2(null);
554
493
  const handleSend = () => {
@@ -561,7 +500,10 @@ function MessageInput({ onSend, disabled, isProcessing, placeholder }) {
561
500
  }
562
501
  };
563
502
  const handleKeyDown = (e) => {
564
- if (e.key === "Enter" && !e.shiftKey) {
503
+ if (e.key === "Escape" && isProcessing && onCancel) {
504
+ e.preventDefault();
505
+ onCancel();
506
+ } else if (e.key === "Enter" && !e.shiftKey) {
565
507
  e.preventDefault();
566
508
  handleSend();
567
509
  }
@@ -572,11 +514,34 @@ function MessageInput({ onSend, disabled, isProcessing, placeholder }) {
572
514
  el.style.height = "auto";
573
515
  el.style.height = Math.min(el.scrollHeight, 120) + "px";
574
516
  };
575
- return /* @__PURE__ */ React4.createElement("div", { style: styles3.container }, /* @__PURE__ */ React4.createElement(
517
+ if (isProcessing && onCancel) {
518
+ return /* @__PURE__ */ React3.createElement("div", { style: styles2.container }, /* @__PURE__ */ React3.createElement(
519
+ "textarea",
520
+ {
521
+ ref: textareaRef,
522
+ style: styles2.textarea,
523
+ value: text,
524
+ onChange: handleInput,
525
+ onKeyDown: handleKeyDown,
526
+ placeholder: disabled ? "Connect to daemon first..." : placeholder || "Ask Claude...",
527
+ disabled: true,
528
+ rows: 1
529
+ }
530
+ ), /* @__PURE__ */ React3.createElement(
531
+ "button",
532
+ {
533
+ style: styles2.buttonCancel,
534
+ onClick: onCancel,
535
+ title: "Cancel response (Escape)"
536
+ },
537
+ "Cancel"
538
+ ));
539
+ }
540
+ return /* @__PURE__ */ React3.createElement("div", { style: styles2.container }, /* @__PURE__ */ React3.createElement(
576
541
  "textarea",
577
542
  {
578
543
  ref: textareaRef,
579
- style: styles3.textarea,
544
+ style: styles2.textarea,
580
545
  value: text,
581
546
  onChange: handleInput,
582
547
  onKeyDown: handleKeyDown,
@@ -584,21 +549,21 @@ function MessageInput({ onSend, disabled, isProcessing, placeholder }) {
584
549
  disabled,
585
550
  rows: 1
586
551
  }
587
- ), /* @__PURE__ */ React4.createElement(
552
+ ), /* @__PURE__ */ React3.createElement(
588
553
  "button",
589
554
  {
590
555
  style: {
591
- ...styles3.button,
592
- ...disabled || !text.trim() ? styles3.buttonDisabled : {}
556
+ ...styles2.button,
557
+ ...disabled || !text.trim() ? styles2.buttonDisabled : {}
593
558
  },
594
559
  onClick: handleSend,
595
560
  disabled: disabled || !text.trim(),
596
561
  title: "Send message (Enter)"
597
562
  },
598
- isProcessing ? "..." : "Send"
563
+ "Send"
599
564
  ));
600
565
  }
601
- var styles3 = {
566
+ var styles2 = {
602
567
  container: {
603
568
  display: "flex",
604
569
  alignItems: "flex-end",
@@ -634,11 +599,23 @@ var styles3 = {
634
599
  buttonDisabled: {
635
600
  backgroundColor: "#ccc",
636
601
  cursor: "not-allowed"
602
+ },
603
+ buttonCancel: {
604
+ padding: "8px 16px",
605
+ border: "none",
606
+ borderRadius: "8px",
607
+ backgroundColor: "#e53e3e",
608
+ color: "#fff",
609
+ fontSize: "13px",
610
+ fontWeight: 600,
611
+ cursor: "pointer",
612
+ whiteSpace: "nowrap",
613
+ minHeight: "36px"
637
614
  }
638
615
  };
639
616
 
640
617
  // src/components/TokenInput.jsx
641
- import React5, { useState as useState2 } from "react";
618
+ import React4, { useState as useState2 } from "react";
642
619
  function TokenInput({ onSubmit, defaultPort = 3001 }) {
643
620
  const [token, setToken] = useState2("");
644
621
  const [port, setPort] = useState2(String(defaultPort));
@@ -650,42 +627,42 @@ function TokenInput({ onSubmit, defaultPort = 3001 }) {
650
627
  onSubmit(trimmed, portNum);
651
628
  }
652
629
  };
653
- return /* @__PURE__ */ React5.createElement("div", { style: styles4.container }, /* @__PURE__ */ React5.createElement("div", { style: styles4.title }, "Connect to Claude Daemon"), /* @__PURE__ */ React5.createElement("p", { style: styles4.description }, "Enter the auth token from the daemon's startup output, or set the ", /* @__PURE__ */ React5.createElement("code", { style: styles4.code }, "CLAUDE_DAEMON_TOKEN"), " environment variable."), /* @__PURE__ */ React5.createElement("form", { onSubmit: handleSubmit, style: styles4.form }, /* @__PURE__ */ React5.createElement("div", { style: styles4.inputRow }, /* @__PURE__ */ React5.createElement(
630
+ return /* @__PURE__ */ React4.createElement("div", { style: styles3.container }, /* @__PURE__ */ React4.createElement("div", { style: styles3.title }, "Connect to Claude Daemon"), /* @__PURE__ */ React4.createElement("p", { style: styles3.description }, "Enter the auth token from the daemon's startup output, or set the ", /* @__PURE__ */ React4.createElement("code", { style: styles3.code }, "CLAUDE_DAEMON_TOKEN"), " environment variable."), /* @__PURE__ */ React4.createElement("form", { onSubmit: handleSubmit, style: styles3.form }, /* @__PURE__ */ React4.createElement("div", { style: styles3.inputRow }, /* @__PURE__ */ React4.createElement(
654
631
  "input",
655
632
  {
656
633
  type: "text",
657
634
  value: token,
658
635
  onChange: (e) => setToken(e.target.value),
659
636
  placeholder: "Paste daemon auth token...",
660
- style: styles4.input,
637
+ style: styles3.input,
661
638
  autoFocus: true
662
639
  }
663
- ), /* @__PURE__ */ React5.createElement(
640
+ ), /* @__PURE__ */ React4.createElement(
664
641
  "input",
665
642
  {
666
643
  type: "number",
667
644
  value: port,
668
645
  onChange: (e) => setPort(e.target.value),
669
646
  placeholder: "Port",
670
- style: styles4.portInput,
647
+ style: styles3.portInput,
671
648
  min: "1",
672
649
  max: "65535",
673
650
  "aria-label": "Port"
674
651
  }
675
- )), /* @__PURE__ */ React5.createElement(
652
+ )), /* @__PURE__ */ React4.createElement(
676
653
  "button",
677
654
  {
678
655
  type: "submit",
679
656
  disabled: !token.trim(),
680
657
  style: {
681
- ...styles4.button,
682
- ...!token.trim() ? styles4.buttonDisabled : {}
658
+ ...styles3.button,
659
+ ...!token.trim() ? styles3.buttonDisabled : {}
683
660
  }
684
661
  },
685
662
  "Connect"
686
663
  )));
687
664
  }
688
- var styles4 = {
665
+ var styles3 = {
689
666
  container: {
690
667
  display: "flex",
691
668
  flexDirection: "column",
@@ -762,63 +739,6 @@ var styles4 = {
762
739
  }
763
740
  };
764
741
 
765
- // src/components/GitContextBar.jsx
766
- import React6 from "react";
767
- function GitContextBar({ branch, pr, loading }) {
768
- if (loading || !branch) return null;
769
- return /* @__PURE__ */ React6.createElement("div", { style: styles5.container }, /* @__PURE__ */ React6.createElement("div", { style: styles5.left }, /* @__PURE__ */ React6.createElement("span", { style: styles5.branchIcon, title: "Git branch" }, "\u2387"), /* @__PURE__ */ React6.createElement("span", { style: styles5.branchName }, branch)), pr && /* @__PURE__ */ React6.createElement("div", { style: styles5.right }, /* @__PURE__ */ React6.createElement(
770
- "a",
771
- {
772
- href: pr.url,
773
- target: "_blank",
774
- rel: "noopener noreferrer",
775
- style: styles5.prLink,
776
- title: pr.title
777
- },
778
- "PR #",
779
- pr.number
780
- )));
781
- }
782
- var styles5 = {
783
- container: {
784
- display: "flex",
785
- alignItems: "center",
786
- justifyContent: "space-between",
787
- padding: "3px 12px",
788
- borderBottom: "1px solid rgba(0,0,0,0.1)",
789
- backgroundColor: "#f5f5f5",
790
- fontSize: "11px",
791
- fontFamily: "inherit"
792
- },
793
- left: {
794
- display: "flex",
795
- alignItems: "center",
796
- gap: "4px"
797
- },
798
- branchIcon: {
799
- fontSize: "12px",
800
- color: "#888"
801
- },
802
- branchName: {
803
- fontFamily: "monospace",
804
- fontSize: "11px",
805
- color: "#555",
806
- backgroundColor: "#eee",
807
- padding: "1px 5px",
808
- borderRadius: "3px"
809
- },
810
- right: {
811
- display: "flex",
812
- alignItems: "center"
813
- },
814
- prLink: {
815
- color: "#0366d6",
816
- textDecoration: "none",
817
- fontFamily: "monospace",
818
- fontSize: "11px"
819
- }
820
- };
821
-
822
742
  // src/useClaudeSession.js
823
743
  import { useState as useState3, useEffect as useEffect2, useRef as useRef3, useCallback } from "react";
824
744
 
@@ -828,6 +748,7 @@ var WebSocketClient = class {
828
748
  this._host = options.host || DEFAULT_HOST;
829
749
  this._port = options.port || DEFAULT_PORT;
830
750
  this._token = options.token || null;
751
+ this._daemonUrl = options.daemonUrl || null;
831
752
  this._ws = null;
832
753
  this._state = CONNECTION_STATES.DISCONNECTED;
833
754
  this._listeners = /* @__PURE__ */ new Map();
@@ -847,7 +768,7 @@ var WebSocketClient = class {
847
768
  }
848
769
  /**
849
770
  * Register an event listener.
850
- * Events: 'state_change', 'session_activated', 'output', 'complete', 'error', 'pong'
771
+ * Events: 'state_change', 'session_activated', 'output', 'complete', 'error', 'pong', 'cancelled'
851
772
  */
852
773
  on(event, callback) {
853
774
  if (!this._listeners.has(event)) {
@@ -870,7 +791,7 @@ var WebSocketClient = class {
870
791
  this._setState(CONNECTION_STATES.CONNECTING);
871
792
  }
872
793
  const params = this._token ? `?token=${encodeURIComponent(this._token)}` : "";
873
- const url = `ws://${this._host}:${this._port}/${params}`;
794
+ const url = this._buildUrl(params);
874
795
  try {
875
796
  this._ws = new WebSocket(url);
876
797
  } catch (err) {
@@ -922,6 +843,12 @@ var WebSocketClient = class {
922
843
  setToken(token) {
923
844
  this._token = token;
924
845
  }
846
+ /**
847
+ * Update the daemon URL for remote environments.
848
+ */
849
+ setDaemonUrl(url) {
850
+ this._daemonUrl = url;
851
+ }
925
852
  /**
926
853
  * Activate a session. Must be called after connecting.
927
854
  */
@@ -952,6 +879,16 @@ var WebSocketClient = class {
952
879
  sessionId
953
880
  });
954
881
  }
882
+ /**
883
+ * Cancel a command in progress.
884
+ */
885
+ cancelCommand(sessionId, commandId) {
886
+ this._send({
887
+ type: "cancel",
888
+ sessionId,
889
+ commandId
890
+ });
891
+ }
955
892
  /**
956
893
  * Send a query to the daemon.
957
894
  */
@@ -964,6 +901,28 @@ var WebSocketClient = class {
964
901
  // ---------------------------------------------------------------------------
965
902
  // Internal
966
903
  // ---------------------------------------------------------------------------
904
+ /**
905
+ * Build the WebSocket URL. Priority:
906
+ * 1. Explicit `daemonUrl` option (fully custom URL)
907
+ * 2. Auto-detected Codespaces forwarded URL (derived from current hostname)
908
+ * 3. Local `ws://host:port/` fallback
909
+ */
910
+ _buildUrl(params) {
911
+ if (this._daemonUrl) {
912
+ const base = this._daemonUrl.replace(/\/$/, "");
913
+ return `${base}/${params}`;
914
+ }
915
+ if (typeof window !== "undefined" && window.location) {
916
+ const hostname = window.location.hostname;
917
+ const match = hostname.match(/^(.+)-(\d+)(\..*\.app\.github\.dev)$/);
918
+ if (match) {
919
+ const [, prefix, , suffix] = match;
920
+ const daemonHost = `${prefix}-${this._port}${suffix}`;
921
+ return `wss://${daemonHost}/${params}`;
922
+ }
923
+ }
924
+ return `ws://${this._host}:${this._port}/${params}`;
925
+ }
967
926
  _send(msg) {
968
927
  if (this._ws && this._ws.readyState === WebSocket.OPEN) {
969
928
  this._ws.send(JSON.stringify(msg));
@@ -985,6 +944,7 @@ var WebSocketClient = class {
985
944
  case "user_input":
986
945
  case "pong":
987
946
  case "query_result":
947
+ case "cancelled":
988
948
  this._emit(msg.type, msg);
989
949
  break;
990
950
  default:
@@ -1076,6 +1036,7 @@ function useClaudeSession(options = {}) {
1076
1036
  const clientRef = useRef3(null);
1077
1037
  const sessionIdRef = useRef3(sessionId);
1078
1038
  sessionIdRef.current = sessionId;
1039
+ const currentCommandIdRef = useRef3(null);
1079
1040
  useEffect2(() => {
1080
1041
  const client = new WebSocketClient({ host, port, token });
1081
1042
  clientRef.current = client;
@@ -1134,6 +1095,12 @@ function useClaudeSession(options = {}) {
1134
1095
  { type: "error", ...msg, id: crypto.randomUUID(), timestamp: Date.now() }
1135
1096
  ]);
1136
1097
  setIsProcessing(false);
1098
+ }),
1099
+ client.on("cancelled", (msg) => {
1100
+ if (msg.sessionId !== sessionIdRef.current) return;
1101
+ setMessages((prev) => [...prev, { type: "cancelled", id: crypto.randomUUID(), timestamp: msg.timestamp, sessionId: msg.sessionId }]);
1102
+ setIsProcessing(false);
1103
+ currentCommandIdRef.current = null;
1137
1104
  })
1138
1105
  ];
1139
1106
  return () => {
@@ -1173,7 +1140,9 @@ ${text}` : text;
1173
1140
  }
1174
1141
  ]);
1175
1142
  setIsProcessing(true);
1176
- client.sendCommand(sessionIdRef.current, fullText);
1143
+ const commandId = crypto.randomUUID();
1144
+ currentCommandIdRef.current = commandId;
1145
+ client.sendCommand(sessionIdRef.current, fullText, commandId);
1177
1146
  },
1178
1147
  []
1179
1148
  );
@@ -1215,6 +1184,12 @@ ${text}` : text;
1215
1184
  startSession();
1216
1185
  }
1217
1186
  }, [startSession]);
1187
+ const cancelCommand = useCallback(() => {
1188
+ const client = clientRef.current;
1189
+ const cmdId = currentCommandIdRef.current;
1190
+ if (!client || !client.isConnected || !sessionIdRef.current) return;
1191
+ client.cancelCommand(sessionIdRef.current, cmdId);
1192
+ }, []);
1218
1193
  return {
1219
1194
  connectionState,
1220
1195
  messages,
@@ -1227,44 +1202,42 @@ ${text}` : text;
1227
1202
  setMessages,
1228
1203
  disconnect,
1229
1204
  newChat,
1205
+ cancelCommand,
1230
1206
  isConnected: connectionState === CONNECTION_STATES.CONNECTED,
1231
1207
  client: clientRef.current
1232
1208
  };
1233
1209
  }
1234
1210
 
1235
- // src/useGitContext.js
1211
+ // src/useDaemonQuery.js
1236
1212
  import { useState as useState4, useEffect as useEffect3, useRef as useRef4, useCallback as useCallback2 } from "react";
1237
- function useGitContext(client, connectionState) {
1238
- const [gitData, setGitData] = useState4(null);
1239
- const [loading, setLoading] = useState4(false);
1213
+ function useDaemonQuery(client, connectionState, queryType, pollIntervalMs) {
1214
+ const [data, setData] = useState4(null);
1215
+ const [error, setError] = useState4(null);
1240
1216
  const intervalRef = useRef4(null);
1241
- const unsubRef = useRef4(null);
1242
- const fetchContext = useCallback2(() => {
1217
+ const dataRef = useRef4(null);
1218
+ const fetchQuery = useCallback2(() => {
1243
1219
  if (!client || !client.isConnected) return;
1244
- setLoading(true);
1245
- client.sendQuery("git_context");
1246
- }, [client]);
1220
+ client.sendQuery(queryType);
1221
+ }, [client, queryType]);
1247
1222
  useEffect3(() => {
1248
1223
  if (!client) return;
1249
1224
  const unsub = client.on("query_result", (msg) => {
1250
- if (msg.queryType === "git_context") {
1251
- setGitData(msg.data || null);
1252
- setLoading(false);
1225
+ if (msg.queryType === queryType) {
1226
+ dataRef.current = msg.data || null;
1227
+ setData(msg.data || null);
1228
+ setError(null);
1253
1229
  }
1254
1230
  });
1255
1231
  const unsubErr = client.on("error", (msg) => {
1256
1232
  if (msg.code === "UNKNOWN_TYPE" || msg.code === "UNKNOWN_QUERY") {
1257
- setLoading(false);
1233
+ setError(msg.message || "Unsupported query");
1258
1234
  }
1259
1235
  });
1260
- unsubRef.current = () => {
1236
+ return () => {
1261
1237
  unsub();
1262
1238
  unsubErr();
1263
1239
  };
1264
- return () => {
1265
- if (unsubRef.current) unsubRef.current();
1266
- };
1267
- }, [client]);
1240
+ }, [client, queryType]);
1268
1241
  useEffect3(() => {
1269
1242
  if (connectionState !== CONNECTION_STATES.CONNECTED) {
1270
1243
  if (intervalRef.current) {
@@ -1273,18 +1246,25 @@ function useGitContext(client, connectionState) {
1273
1246
  }
1274
1247
  return;
1275
1248
  }
1276
- fetchContext();
1277
- intervalRef.current = setInterval(fetchContext, 6e4);
1249
+ fetchQuery();
1250
+ intervalRef.current = setInterval(fetchQuery, pollIntervalMs);
1278
1251
  return () => {
1279
1252
  if (intervalRef.current) {
1280
1253
  clearInterval(intervalRef.current);
1281
1254
  intervalRef.current = null;
1282
1255
  }
1283
1256
  };
1284
- }, [connectionState, fetchContext]);
1257
+ }, [connectionState, fetchQuery, pollIntervalMs]);
1258
+ const loading = data === null && dataRef.current === null && error === null;
1259
+ return { data, loading, error };
1260
+ }
1261
+
1262
+ // src/useGitContext.js
1263
+ function useGitContext(client, connectionState) {
1264
+ const { data, loading } = useDaemonQuery(client, connectionState, "git_context", 6e4);
1285
1265
  return {
1286
- branch: (gitData == null ? void 0 : gitData.branch) || null,
1287
- pr: (gitData == null ? void 0 : gitData.pr) || null,
1266
+ branch: (data == null ? void 0 : data.branch) || null,
1267
+ pr: (data == null ? void 0 : data.pr) || null,
1288
1268
  loading
1289
1269
  };
1290
1270
  }
@@ -1342,12 +1322,10 @@ function storyFileToComponentFile(storyPath) {
1342
1322
  function ClaudePanel({ active }) {
1343
1323
  const [addonState, setAddonState] = useAddonState(ADDON_ID, {
1344
1324
  token: getInitialToken(),
1345
- port: getInitialPort(),
1346
- contextEnabled: true
1325
+ port: getInitialPort()
1347
1326
  });
1348
1327
  const token = (addonState == null ? void 0 : addonState.token) || null;
1349
1328
  const port = (addonState == null ? void 0 : addonState.port) || DEFAULT_PORT;
1350
- const contextEnabled = (addonState == null ? void 0 : addonState.contextEnabled) ?? true;
1351
1329
  const storyContext = useStoryContext();
1352
1330
  const {
1353
1331
  connectionState,
@@ -1357,8 +1335,8 @@ function ClaudePanel({ active }) {
1357
1335
  authFailed,
1358
1336
  sendMessage,
1359
1337
  startSession,
1360
- disconnect,
1361
1338
  newChat,
1339
+ cancelCommand,
1362
1340
  isConnected,
1363
1341
  client
1364
1342
  } = useClaudeSession({
@@ -1373,41 +1351,53 @@ function ClaudePanel({ active }) {
1373
1351
  },
1374
1352
  [addonState, setAddonState]
1375
1353
  );
1376
- const handleDisconnect = useCallback3(() => {
1377
- disconnect();
1378
- setAddonState({ ...addonState, token: null });
1379
- }, [addonState, setAddonState, disconnect]);
1380
1354
  const handleSend = useCallback3(
1381
1355
  (text) => {
1382
1356
  if (!sessionId) {
1383
1357
  startSession();
1384
1358
  }
1385
- const prefix = contextEnabled ? buildContextPrefix(storyContext) : null;
1359
+ const prefix = buildContextPrefix(storyContext);
1386
1360
  sendMessage(text, prefix);
1387
1361
  },
1388
- [sessionId, startSession, sendMessage, contextEnabled, storyContext]
1362
+ [sessionId, startSession, sendMessage, storyContext]
1389
1363
  );
1390
- const toggleContext = useCallback3(() => {
1391
- setAddonState({ ...addonState, contextEnabled: !contextEnabled });
1392
- }, [addonState, contextEnabled, setAddonState]);
1393
1364
  if (!active) return null;
1394
1365
  if (authFailed && !token) {
1395
- return /* @__PURE__ */ React7.createElement("div", { style: styles6.panel }, /* @__PURE__ */ React7.createElement(TokenInput, { onSubmit: handleTokenSubmit }));
1366
+ return /* @__PURE__ */ React5.createElement("div", { style: styles4.panel }, /* @__PURE__ */ React5.createElement(TokenInput, { onSubmit: handleTokenSubmit }));
1396
1367
  }
1397
- return /* @__PURE__ */ React7.createElement("div", { style: styles6.panel }, /* @__PURE__ */ React7.createElement(ConnectionStatus, { state: connectionState, sessionId, onDisconnect: handleDisconnect, onNewChat: newChat, isProcessing }), /* @__PURE__ */ React7.createElement(GitContextBar, { branch: gitContext.branch, pr: gitContext.pr, loading: gitContext.loading }), storyContext.componentPath && /* @__PURE__ */ React7.createElement("div", { style: styles6.contextBar }, /* @__PURE__ */ React7.createElement("label", { style: styles6.contextLabel }, /* @__PURE__ */ React7.createElement(
1398
- "input",
1368
+ const statusColor = connectionState === CONNECTION_STATES.CONNECTED ? "#4caf50" : connectionState === CONNECTION_STATES.DISCONNECTED ? "#f44336" : "#ff9800";
1369
+ const statusLabel = connectionState === CONNECTION_STATES.CONNECTED ? "Connected" : connectionState === CONNECTION_STATES.CONNECTING ? "Connecting..." : connectionState === CONNECTION_STATES.RECONNECTING ? "Reconnecting..." : "Disconnected";
1370
+ return /* @__PURE__ */ React5.createElement("div", { style: styles4.panel }, /* @__PURE__ */ React5.createElement("div", { style: styles4.headerBar }, /* @__PURE__ */ React5.createElement("div", { style: styles4.headerLeft }, /* @__PURE__ */ React5.createElement("span", { style: { ...styles4.statusDot, backgroundColor: statusColor }, title: statusLabel }), /* @__PURE__ */ React5.createElement("span", { style: styles4.statusLabel }, statusLabel)), /* @__PURE__ */ React5.createElement("div", { style: styles4.headerRight }, gitContext.branch && /* @__PURE__ */ React5.createElement("div", { style: styles4.gitContext }, /* @__PURE__ */ React5.createElement("div", { style: styles4.branchName }, /* @__PURE__ */ React5.createElement("span", { style: styles4.branchIcon, title: "Git branch" }, "\u2387"), gitContext.branch), gitContext.pr && /* @__PURE__ */ React5.createElement(
1371
+ "a",
1399
1372
  {
1400
- type: "checkbox",
1401
- checked: contextEnabled,
1402
- onChange: toggleContext,
1403
- style: styles6.contextCheckbox
1404
- }
1405
- ), "Context: ", /* @__PURE__ */ React7.createElement("code", { style: styles6.contextPath }, storyContext.componentPath))), /* @__PURE__ */ React7.createElement(MessageList, { messages }), /* @__PURE__ */ React7.createElement(
1373
+ href: gitContext.pr.url,
1374
+ target: "_blank",
1375
+ rel: "noopener noreferrer",
1376
+ style: styles4.prLink,
1377
+ title: gitContext.pr.title
1378
+ },
1379
+ "PR #",
1380
+ gitContext.pr.number
1381
+ )), isConnected && /* @__PURE__ */ React5.createElement(
1382
+ "button",
1383
+ {
1384
+ onClick: newChat,
1385
+ disabled: isProcessing,
1386
+ style: {
1387
+ ...styles4.newChatButton,
1388
+ ...isProcessing ? styles4.newChatButtonDisabled : {}
1389
+ },
1390
+ title: "Clear chat and start fresh session",
1391
+ "aria-label": "New Chat"
1392
+ },
1393
+ "New Chat"
1394
+ ))), /* @__PURE__ */ React5.createElement(MessageList, { messages, isProcessing }), /* @__PURE__ */ React5.createElement(
1406
1395
  MessageInput,
1407
1396
  {
1408
1397
  onSend: handleSend,
1409
1398
  disabled: !isConnected,
1410
- isProcessing
1399
+ isProcessing,
1400
+ onCancel: cancelCommand
1411
1401
  }
1412
1402
  ));
1413
1403
  }
@@ -1426,68 +1416,118 @@ function getInitialPort() {
1426
1416
  function getInitialToken() {
1427
1417
  var _a, _b;
1428
1418
  try {
1419
+ if (typeof window !== "undefined" && window.__CLAUDE_DAEMON_TOKEN__) {
1420
+ return window.__CLAUDE_DAEMON_TOKEN__;
1421
+ }
1429
1422
  return typeof process !== "undefined" && ((_a = process.env) == null ? void 0 : _a.STORYBOOK_CLAUDE_DAEMON_TOKEN) || typeof process !== "undefined" && ((_b = process.env) == null ? void 0 : _b.CLAUDE_DAEMON_TOKEN) || null;
1430
1423
  } catch {
1431
1424
  return null;
1432
1425
  }
1433
1426
  }
1434
- var styles6 = {
1427
+ var styles4 = {
1435
1428
  panel: {
1436
1429
  display: "flex",
1437
1430
  flexDirection: "column",
1438
1431
  height: "100%",
1439
1432
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
1440
1433
  },
1441
- contextBar: {
1442
- padding: "4px 12px",
1434
+ headerBar: {
1435
+ display: "flex",
1436
+ alignItems: "center",
1437
+ justifyContent: "space-between",
1438
+ padding: "6px 12px",
1443
1439
  borderBottom: "1px solid rgba(0,0,0,0.1)",
1444
- backgroundColor: "#fafafa",
1445
- fontSize: "12px"
1440
+ backgroundColor: "#f5f5f5",
1441
+ fontSize: "12px",
1442
+ fontFamily: "inherit"
1446
1443
  },
1447
- contextLabel: {
1444
+ headerLeft: {
1448
1445
  display: "flex",
1449
1446
  alignItems: "center",
1450
- gap: "4px",
1451
- color: "#666",
1452
- cursor: "pointer"
1447
+ gap: "6px"
1453
1448
  },
1454
- contextCheckbox: {
1455
- margin: 0
1449
+ statusDot: {
1450
+ width: "8px",
1451
+ height: "8px",
1452
+ borderRadius: "50%",
1453
+ display: "inline-block",
1454
+ flexShrink: 0
1456
1455
  },
1457
- contextPath: {
1456
+ statusLabel: {
1457
+ fontSize: "12px",
1458
+ color: "#666"
1459
+ },
1460
+ headerRight: {
1461
+ display: "flex",
1462
+ alignItems: "center",
1463
+ gap: "12px"
1464
+ },
1465
+ gitContext: {
1466
+ display: "flex",
1467
+ flexDirection: "column",
1468
+ alignItems: "flex-end",
1469
+ gap: "1px"
1470
+ },
1471
+ branchName: {
1458
1472
  fontFamily: "monospace",
1459
1473
  fontSize: "11px",
1460
- backgroundColor: "#eee",
1461
- padding: "1px 4px",
1462
- borderRadius: "3px"
1474
+ color: "#555",
1475
+ display: "flex",
1476
+ alignItems: "center",
1477
+ gap: "3px"
1478
+ },
1479
+ branchIcon: {
1480
+ fontSize: "12px",
1481
+ color: "#888"
1482
+ },
1483
+ prLink: {
1484
+ color: "#0366d6",
1485
+ textDecoration: "none",
1486
+ fontFamily: "monospace",
1487
+ fontSize: "11px"
1488
+ },
1489
+ newChatButton: {
1490
+ padding: "2px 8px",
1491
+ border: "1px solid #ccc",
1492
+ borderRadius: "4px",
1493
+ backgroundColor: "transparent",
1494
+ color: "#666",
1495
+ fontSize: "11px",
1496
+ cursor: "pointer",
1497
+ lineHeight: "1.4",
1498
+ whiteSpace: "nowrap"
1499
+ },
1500
+ newChatButtonDisabled: {
1501
+ opacity: 0.4,
1502
+ cursor: "default"
1463
1503
  }
1464
1504
  };
1465
1505
 
1466
1506
  // src/GlobalPanel.jsx
1467
- import React9, { useCallback as useCallback4, useMemo as useMemo2 } from "react";
1507
+ import React7, { useCallback as useCallback4, useMemo as useMemo2 } from "react";
1468
1508
  import { useAddonState as useAddonState2 } from "storybook/manager-api";
1469
1509
 
1470
1510
  // src/components/SkillsBar.jsx
1471
- import React8 from "react";
1511
+ import React6 from "react";
1472
1512
  function SkillsBar({ skills, onTrigger, disabled }) {
1473
1513
  if (!skills || skills.length === 0) return null;
1474
- return /* @__PURE__ */ React8.createElement("div", { style: styles7.bar }, skills.map((skill) => /* @__PURE__ */ React8.createElement(
1514
+ return /* @__PURE__ */ React6.createElement("div", { style: styles5.bar }, skills.map((skill) => /* @__PURE__ */ React6.createElement(
1475
1515
  "button",
1476
1516
  {
1477
1517
  key: skill.id,
1478
1518
  style: {
1479
- ...styles7.button,
1480
- ...disabled ? styles7.buttonDisabled : {}
1519
+ ...styles5.button,
1520
+ ...disabled ? styles5.buttonDisabled : {}
1481
1521
  },
1482
1522
  onClick: () => onTrigger(skill),
1483
1523
  disabled,
1484
- title: skill.prompt
1524
+ title: skill.description || skill.prompt
1485
1525
  },
1486
- /* @__PURE__ */ React8.createElement("span", { style: styles7.icon }, skill.icon),
1487
- /* @__PURE__ */ React8.createElement("span", null, skill.label)
1526
+ /* @__PURE__ */ React6.createElement("span", { style: styles5.icon }, skill.icon),
1527
+ /* @__PURE__ */ React6.createElement("span", null, skill.label)
1488
1528
  )));
1489
1529
  }
1490
- var styles7 = {
1530
+ var styles5 = {
1491
1531
  bar: {
1492
1532
  display: "flex",
1493
1533
  gap: "6px",
@@ -1518,8 +1558,18 @@ var styles7 = {
1518
1558
  }
1519
1559
  };
1520
1560
 
1561
+ // src/useSkillsConfig.js
1562
+ function useSkillsConfig(client, connectionState) {
1563
+ const { data, loading } = useDaemonQuery(client, connectionState, "skills_config", 12e4);
1564
+ return {
1565
+ skills: (data == null ? void 0 : data.skills) || [],
1566
+ source: (data == null ? void 0 : data.source) || "none",
1567
+ loading
1568
+ };
1569
+ }
1570
+
1521
1571
  // src/skills.js
1522
- var STATIC_SKILLS = [
1572
+ var DEFAULT_SKILLS = [
1523
1573
  {
1524
1574
  id: "sync-components",
1525
1575
  label: "Sync Components",
@@ -1555,17 +1605,13 @@ function getPrSkill(pr) {
1555
1605
  prompt: "Create a new pull request for the current branch with a summary of all changes."
1556
1606
  };
1557
1607
  }
1558
- function getGlobalSkills(gitContext) {
1608
+ function getGlobalSkills(gitContext, configSkills) {
1559
1609
  const pr = (gitContext == null ? void 0 : gitContext.pr) || null;
1610
+ const skills = configSkills && configSkills.length > 0 ? configSkills : DEFAULT_SKILLS;
1560
1611
  return [
1561
- STATIC_SKILLS[0],
1562
- // Sync Components
1612
+ skills[0],
1563
1613
  getPrSkill(pr),
1564
- // New PR / Finalize PR
1565
- STATIC_SKILLS[1],
1566
- // Run Tests
1567
- STATIC_SKILLS[2]
1568
- // Audit Stories
1614
+ ...skills.slice(1)
1569
1615
  ];
1570
1616
  }
1571
1617
 
@@ -1589,6 +1635,7 @@ function GlobalPanel() {
1589
1635
  sendMessage,
1590
1636
  startSession,
1591
1637
  setMessages,
1638
+ cancelCommand,
1592
1639
  isConnected,
1593
1640
  client
1594
1641
  } = useClaudeSession({
@@ -1599,7 +1646,11 @@ function GlobalPanel() {
1599
1646
  storageKey: `${ADDON_ID}:globalSessionId`
1600
1647
  });
1601
1648
  const gitContext = useGitContext(client, connectionState);
1602
- const skills = useMemo2(() => getGlobalSkills(gitContext), [gitContext]);
1649
+ const skillsConfig = useSkillsConfig(client, connectionState);
1650
+ const skills = useMemo2(
1651
+ () => getGlobalSkills(gitContext, skillsConfig.skills),
1652
+ [gitContext, skillsConfig.skills]
1653
+ );
1603
1654
  const handleTokenSubmit = useCallback4(
1604
1655
  (newToken, newPort) => {
1605
1656
  setAddonState({ ...addonState, token: newToken, port: newPort || DEFAULT_PORT });
@@ -1631,35 +1682,36 @@ function GlobalPanel() {
1631
1682
  [handleSend, setMessages]
1632
1683
  );
1633
1684
  if (authFailed && !token) {
1634
- return /* @__PURE__ */ React9.createElement("div", { style: styles8.page }, /* @__PURE__ */ React9.createElement(TokenInput, { onSubmit: handleTokenSubmit }));
1685
+ return /* @__PURE__ */ React7.createElement("div", { style: styles6.page }, /* @__PURE__ */ React7.createElement(TokenInput, { onSubmit: handleTokenSubmit }));
1635
1686
  }
1636
1687
  const statusColor = connectionState === CONNECTION_STATES.CONNECTED ? "#4caf50" : connectionState === CONNECTION_STATES.DISCONNECTED ? "#f44336" : "#ff9800";
1637
1688
  const statusLabel = connectionState === CONNECTION_STATES.CONNECTED ? "Connected" : connectionState === CONNECTION_STATES.CONNECTING ? "Connecting..." : connectionState === CONNECTION_STATES.RECONNECTING ? "Reconnecting..." : "Disconnected";
1638
- return /* @__PURE__ */ React9.createElement("div", { style: styles8.page }, /* @__PURE__ */ React9.createElement("div", { style: styles8.titleBar }, /* @__PURE__ */ React9.createElement("div", { style: styles8.titleLeft }, /* @__PURE__ */ React9.createElement("div", { style: styles8.title }, "Claude \u2014 Global Chat"), /* @__PURE__ */ React9.createElement("span", { style: { ...styles8.statusDot, backgroundColor: statusColor }, title: statusLabel }), /* @__PURE__ */ React9.createElement("span", { style: styles8.statusLabel }, statusLabel), /* @__PURE__ */ React9.createElement(
1689
+ return /* @__PURE__ */ React7.createElement("div", { style: styles6.page }, /* @__PURE__ */ React7.createElement("div", { style: styles6.titleBar }, /* @__PURE__ */ React7.createElement("div", { style: styles6.titleLeft }, /* @__PURE__ */ React7.createElement("div", { style: styles6.title }, "Claude \u2014 Global Chat"), /* @__PURE__ */ React7.createElement("span", { style: { ...styles6.statusDot, backgroundColor: statusColor }, title: statusLabel }), /* @__PURE__ */ React7.createElement("span", { style: styles6.statusLabel }, statusLabel), /* @__PURE__ */ React7.createElement(
1639
1690
  "span",
1640
1691
  {
1641
- style: styles8.infoIcon,
1692
+ style: styles6.infoIcon,
1642
1693
  title: "Chat with Claude about your entire project. Not scoped to any specific component."
1643
1694
  },
1644
- /* @__PURE__ */ React9.createElement("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "currentColor" }, /* @__PURE__ */ React9.createElement("path", { d: "M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm.93 12.588-.003.009H7.074l-.003-.01c-.027-.18-.04-.362-.04-.545 0-1.02.356-1.852 1.065-2.497.295-.268.59-.502.793-.737.2-.232.303-.493.303-.784 0-.318-.112-.564-.337-.738-.228-.177-.56-.266-.994-.266-.39 0-.738.074-1.046.222-.307.148-.57.35-.787.607l-.12.152-.94-.762.12-.152c.32-.395.727-.71 1.222-.943A3.77 3.77 0 0 1 7.92 5.4c.744 0 1.35.19 1.818.57.47.382.706.886.706 1.514 0 .474-.14.892-.42 1.254-.277.358-.656.68-1.137.966-.38.226-.636.463-.765.71-.13.25-.196.564-.196.943 0 .082.003.163.01.244l-.006-.013zM8 14.074a.87.87 0 0 1-.638-.262.87.87 0 0 1-.262-.638c0-.25.087-.463.262-.638A.87.87 0 0 1 8 12.274c.25 0 .463.087.638.262a.87.87 0 0 1 .262.638.87.87 0 0 1-.262.638A.87.87 0 0 1 8 14.074z" }))
1645
- )), /* @__PURE__ */ React9.createElement("button", { style: styles8.backButton, onClick: handleBack, title: "Back to components" }, /* @__PURE__ */ React9.createElement("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React9.createElement("path", { d: "M19 12H5" }), /* @__PURE__ */ React9.createElement("path", { d: "M12 19l-7-7 7-7" })), /* @__PURE__ */ React9.createElement("span", { style: { marginLeft: "4px" } }, "Back"))), /* @__PURE__ */ React9.createElement("div", { style: styles8.toolsBar }, /* @__PURE__ */ React9.createElement(SkillsBar, { skills, onTrigger: handleSkillTrigger, disabled: !isConnected }), gitContext.branch && /* @__PURE__ */ React9.createElement("div", { style: styles8.gitContext }, /* @__PURE__ */ React9.createElement("div", { style: styles8.branchName }, /* @__PURE__ */ React9.createElement("span", { style: styles8.branchIcon, title: "Git branch" }, "\u2387"), gitContext.branch), gitContext.pr && /* @__PURE__ */ React9.createElement(
1695
+ /* @__PURE__ */ React7.createElement("svg", { viewBox: "0 0 16 16", width: "14", height: "14", fill: "currentColor" }, /* @__PURE__ */ React7.createElement("path", { d: "M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm.93 12.588-.003.009H7.074l-.003-.01c-.027-.18-.04-.362-.04-.545 0-1.02.356-1.852 1.065-2.497.295-.268.59-.502.793-.737.2-.232.303-.493.303-.784 0-.318-.112-.564-.337-.738-.228-.177-.56-.266-.994-.266-.39 0-.738.074-1.046.222-.307.148-.57.35-.787.607l-.12.152-.94-.762.12-.152c.32-.395.727-.71 1.222-.943A3.77 3.77 0 0 1 7.92 5.4c.744 0 1.35.19 1.818.57.47.382.706.886.706 1.514 0 .474-.14.892-.42 1.254-.277.358-.656.68-1.137.966-.38.226-.636.463-.765.71-.13.25-.196.564-.196.943 0 .082.003.163.01.244l-.006-.013zM8 14.074a.87.87 0 0 1-.638-.262.87.87 0 0 1-.262-.638c0-.25.087-.463.262-.638A.87.87 0 0 1 8 12.274c.25 0 .463.087.638.262a.87.87 0 0 1 .262.638.87.87 0 0 1-.262.638A.87.87 0 0 1 8 14.074z" }))
1696
+ )), /* @__PURE__ */ React7.createElement("button", { style: styles6.backButton, onClick: handleBack, title: "Back to components" }, /* @__PURE__ */ React7.createElement("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React7.createElement("path", { d: "M19 12H5" }), /* @__PURE__ */ React7.createElement("path", { d: "M12 19l-7-7 7-7" })), /* @__PURE__ */ React7.createElement("span", { style: { marginLeft: "4px" } }, "Back"))), /* @__PURE__ */ React7.createElement("div", { style: styles6.toolsBar }, /* @__PURE__ */ React7.createElement(SkillsBar, { skills, onTrigger: handleSkillTrigger, disabled: !isConnected }), gitContext.branch && /* @__PURE__ */ React7.createElement("div", { style: styles6.gitContext }, /* @__PURE__ */ React7.createElement("div", { style: styles6.branchName }, /* @__PURE__ */ React7.createElement("span", { style: styles6.branchIcon, title: "Git branch" }, "\u2387"), gitContext.branch), gitContext.pr && /* @__PURE__ */ React7.createElement(
1646
1697
  "a",
1647
1698
  {
1648
1699
  href: gitContext.pr.url,
1649
1700
  target: "_blank",
1650
1701
  rel: "noopener noreferrer",
1651
- style: styles8.prLink,
1702
+ style: styles6.prLink,
1652
1703
  title: gitContext.pr.title
1653
1704
  },
1654
1705
  "PR #",
1655
1706
  gitContext.pr.number
1656
- ))), /* @__PURE__ */ React9.createElement("div", { style: styles8.chatArea }, /* @__PURE__ */ React9.createElement(MessageList, { messages }), /* @__PURE__ */ React9.createElement(
1707
+ ))), /* @__PURE__ */ React7.createElement("div", { style: styles6.chatArea }, /* @__PURE__ */ React7.createElement(MessageList, { messages, isProcessing }), /* @__PURE__ */ React7.createElement(
1657
1708
  MessageInput,
1658
1709
  {
1659
1710
  onSend: handleSend,
1660
1711
  disabled: !isConnected,
1661
1712
  isProcessing,
1662
- placeholder: "Ask Claude about your project..."
1713
+ placeholder: "Ask Claude about your project...",
1714
+ onCancel: cancelCommand
1663
1715
  }
1664
1716
  )));
1665
1717
  }
@@ -1678,12 +1730,15 @@ function getInitialPort2() {
1678
1730
  function getInitialToken2() {
1679
1731
  var _a, _b;
1680
1732
  try {
1733
+ if (typeof window !== "undefined" && window.__CLAUDE_DAEMON_TOKEN__) {
1734
+ return window.__CLAUDE_DAEMON_TOKEN__;
1735
+ }
1681
1736
  return typeof process !== "undefined" && ((_a = process.env) == null ? void 0 : _a.STORYBOOK_CLAUDE_DAEMON_TOKEN) || typeof process !== "undefined" && ((_b = process.env) == null ? void 0 : _b.CLAUDE_DAEMON_TOKEN) || null;
1682
1737
  } catch {
1683
1738
  return null;
1684
1739
  }
1685
1740
  }
1686
- var styles8 = {
1741
+ var styles6 = {
1687
1742
  page: {
1688
1743
  display: "flex",
1689
1744
  flexDirection: "column",
@@ -1782,7 +1837,7 @@ var styles8 = {
1782
1837
  };
1783
1838
 
1784
1839
  // src/components/GlobalChatButton.jsx
1785
- import React10, { useCallback as useCallback5 } from "react";
1840
+ import React8, { useCallback as useCallback5 } from "react";
1786
1841
  import { useStorybookApi } from "storybook/manager-api";
1787
1842
  import { Button } from "storybook/internal/components";
1788
1843
  function GlobalChatButton() {
@@ -1790,7 +1845,7 @@ function GlobalChatButton() {
1790
1845
  const handleClick = useCallback5(() => {
1791
1846
  api.navigateUrl("/?path=/claude", { replace: false });
1792
1847
  }, [api]);
1793
- return /* @__PURE__ */ React10.createElement(Button, { variant: "ghost", padding: "small", "aria-label": "Open Claude Global Chat", onClick: handleClick }, /* @__PURE__ */ React10.createElement(
1848
+ return /* @__PURE__ */ React8.createElement(Button, { variant: "ghost", padding: "small", "aria-label": "Open Claude Global Chat", onClick: handleClick }, /* @__PURE__ */ React8.createElement(
1794
1849
  "svg",
1795
1850
  {
1796
1851
  viewBox: "0 0 24 24",
@@ -1802,7 +1857,7 @@ function GlobalChatButton() {
1802
1857
  strokeLinecap: "round",
1803
1858
  strokeLinejoin: "round"
1804
1859
  },
1805
- /* @__PURE__ */ React10.createElement("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1860
+ /* @__PURE__ */ React8.createElement("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1806
1861
  ));
1807
1862
  }
1808
1863
 
@@ -1810,7 +1865,7 @@ function GlobalChatButton() {
1810
1865
  function ClaudePageRoute() {
1811
1866
  const { path } = useStorybookState2();
1812
1867
  if (path !== "/claude") return null;
1813
- return /* @__PURE__ */ React11.createElement(GlobalPanel, null);
1868
+ return /* @__PURE__ */ React9.createElement(GlobalPanel, null);
1814
1869
  }
1815
1870
  addons.register(ADDON_ID, (api) => {
1816
1871
  addons.add(PANEL_ID, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radnine/storybook-addon-claude",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Storybook addon panel for chatting with Claude via the standalone daemon",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {