@radnine/storybook-addon-claude 0.2.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/GlobalPanel.js +299 -0
- package/dist/Panel.js +12 -3
- package/dist/WebSocketClient.js +11 -0
- package/dist/components/GitContextBar.js +84 -0
- package/dist/components/GlobalChatButton.js +39 -0
- package/dist/components/MessageInput.js +3 -2
- package/dist/components/MessageList.js +43 -0
- package/dist/components/SkillsBar.js +70 -0
- package/dist/constants.js +4 -1
- package/dist/manager.js +18 -0
- package/dist/skills.js +64 -0
- package/dist/useClaudeSession.js +21 -12
- package/dist/useGitContext.js +84 -0
- package/package.json +1 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.GlobalPanel = GlobalPanel;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _managerApi = require("storybook/manager-api");
|
|
9
|
+
var _MessageList = require("./components/MessageList");
|
|
10
|
+
var _MessageInput = require("./components/MessageInput");
|
|
11
|
+
var _TokenInput = require("./components/TokenInput");
|
|
12
|
+
var _useClaudeSession = require("./useClaudeSession");
|
|
13
|
+
var _SkillsBar = require("./components/SkillsBar");
|
|
14
|
+
var _useGitContext = require("./useGitContext");
|
|
15
|
+
var _constants = require("./constants");
|
|
16
|
+
var _skills = require("./skills");
|
|
17
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
19
|
+
/**
|
|
20
|
+
* Full-page global Claude chat — not scoped to any component.
|
|
21
|
+
*
|
|
22
|
+
* Registered as an experimental_PAGE so it gets its own route.
|
|
23
|
+
* Uses a fixed session key so the conversation persists across navigations.
|
|
24
|
+
* Does not inject any story/component context.
|
|
25
|
+
*/
|
|
26
|
+
function GlobalPanel() {
|
|
27
|
+
const handleBack = (0, _react.useCallback)(() => {
|
|
28
|
+
window.history.back();
|
|
29
|
+
}, []);
|
|
30
|
+
const [addonState, setAddonState] = (0, _managerApi.useAddonState)(_constants.ADDON_ID, {
|
|
31
|
+
token: getInitialToken(),
|
|
32
|
+
port: getInitialPort()
|
|
33
|
+
});
|
|
34
|
+
const token = addonState?.token || null;
|
|
35
|
+
const port = addonState?.port || _constants.DEFAULT_PORT;
|
|
36
|
+
const {
|
|
37
|
+
connectionState,
|
|
38
|
+
messages,
|
|
39
|
+
sessionId,
|
|
40
|
+
isProcessing,
|
|
41
|
+
authFailed,
|
|
42
|
+
sendMessage,
|
|
43
|
+
startSession,
|
|
44
|
+
setMessages,
|
|
45
|
+
isConnected,
|
|
46
|
+
client
|
|
47
|
+
} = (0, _useClaudeSession.useClaudeSession)({
|
|
48
|
+
host: _constants.DEFAULT_HOST,
|
|
49
|
+
port,
|
|
50
|
+
token,
|
|
51
|
+
sessionId: _constants.GLOBAL_SESSION_KEY,
|
|
52
|
+
storageKey: `${_constants.ADDON_ID}:globalSessionId`
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Git context from daemon
|
|
56
|
+
const gitContext = (0, _useGitContext.useGitContext)(client, connectionState);
|
|
57
|
+
const skills = (0, _react.useMemo)(() => (0, _skills.getGlobalSkills)(gitContext), [gitContext]);
|
|
58
|
+
const handleTokenSubmit = (0, _react.useCallback)((newToken, newPort) => {
|
|
59
|
+
setAddonState({
|
|
60
|
+
...addonState,
|
|
61
|
+
token: newToken,
|
|
62
|
+
port: newPort || _constants.DEFAULT_PORT
|
|
63
|
+
});
|
|
64
|
+
}, [addonState, setAddonState]);
|
|
65
|
+
const handleSend = (0, _react.useCallback)(text => {
|
|
66
|
+
if (!sessionId) {
|
|
67
|
+
startSession(_constants.GLOBAL_SESSION_KEY);
|
|
68
|
+
}
|
|
69
|
+
sendMessage(text);
|
|
70
|
+
}, [sessionId, startSession, sendMessage]);
|
|
71
|
+
const handleSkillTrigger = (0, _react.useCallback)(skill => {
|
|
72
|
+
// Insert a visual indicator in the chat
|
|
73
|
+
setMessages(prev => [...prev, {
|
|
74
|
+
type: 'skill_invocation',
|
|
75
|
+
skill,
|
|
76
|
+
id: crypto.randomUUID(),
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
}]);
|
|
79
|
+
// Send the skill's prompt through the normal chat flow
|
|
80
|
+
handleSend(skill.prompt);
|
|
81
|
+
}, [handleSend, setMessages]);
|
|
82
|
+
if (authFailed && !token) {
|
|
83
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
84
|
+
style: styles.page,
|
|
85
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_TokenInput.TokenInput, {
|
|
86
|
+
onSubmit: handleTokenSubmit
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const statusColor = connectionState === _constants.CONNECTION_STATES.CONNECTED ? '#4caf50' : connectionState === _constants.CONNECTION_STATES.DISCONNECTED ? '#f44336' : '#ff9800';
|
|
91
|
+
const statusLabel = connectionState === _constants.CONNECTION_STATES.CONNECTED ? 'Connected' : connectionState === _constants.CONNECTION_STATES.CONNECTING ? 'Connecting...' : connectionState === _constants.CONNECTION_STATES.RECONNECTING ? 'Reconnecting...' : 'Disconnected';
|
|
92
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
93
|
+
style: styles.page,
|
|
94
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
95
|
+
style: styles.titleBar,
|
|
96
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
97
|
+
style: styles.titleLeft,
|
|
98
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
99
|
+
style: styles.title,
|
|
100
|
+
children: "Claude \u2014 Global Chat"
|
|
101
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
102
|
+
style: {
|
|
103
|
+
...styles.statusDot,
|
|
104
|
+
backgroundColor: statusColor
|
|
105
|
+
},
|
|
106
|
+
title: statusLabel
|
|
107
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
108
|
+
style: styles.statusLabel,
|
|
109
|
+
children: statusLabel
|
|
110
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
111
|
+
style: styles.infoIcon,
|
|
112
|
+
title: "Chat with Claude about your entire project. Not scoped to any specific component.",
|
|
113
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("svg", {
|
|
114
|
+
viewBox: "0 0 16 16",
|
|
115
|
+
width: "14",
|
|
116
|
+
height: "14",
|
|
117
|
+
fill: "currentColor",
|
|
118
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
119
|
+
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"
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
})]
|
|
123
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
|
|
124
|
+
style: styles.backButton,
|
|
125
|
+
onClick: handleBack,
|
|
126
|
+
title: "Back to components",
|
|
127
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("svg", {
|
|
128
|
+
viewBox: "0 0 24 24",
|
|
129
|
+
width: "14",
|
|
130
|
+
height: "14",
|
|
131
|
+
fill: "none",
|
|
132
|
+
stroke: "currentColor",
|
|
133
|
+
strokeWidth: "2",
|
|
134
|
+
strokeLinecap: "round",
|
|
135
|
+
strokeLinejoin: "round",
|
|
136
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
137
|
+
d: "M19 12H5"
|
|
138
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
139
|
+
d: "M12 19l-7-7 7-7"
|
|
140
|
+
})]
|
|
141
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
142
|
+
style: {
|
|
143
|
+
marginLeft: '4px'
|
|
144
|
+
},
|
|
145
|
+
children: "Back"
|
|
146
|
+
})]
|
|
147
|
+
})]
|
|
148
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
149
|
+
style: styles.toolsBar,
|
|
150
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_SkillsBar.SkillsBar, {
|
|
151
|
+
skills: skills,
|
|
152
|
+
onTrigger: handleSkillTrigger,
|
|
153
|
+
disabled: !isConnected
|
|
154
|
+
}), gitContext.branch && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
155
|
+
style: styles.gitContext,
|
|
156
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
157
|
+
style: styles.branchName,
|
|
158
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
159
|
+
style: styles.branchIcon,
|
|
160
|
+
title: "Git branch",
|
|
161
|
+
children: "\u2387"
|
|
162
|
+
}), gitContext.branch]
|
|
163
|
+
}), gitContext.pr && /*#__PURE__*/(0, _jsxRuntime.jsxs)("a", {
|
|
164
|
+
href: gitContext.pr.url,
|
|
165
|
+
target: "_blank",
|
|
166
|
+
rel: "noopener noreferrer",
|
|
167
|
+
style: styles.prLink,
|
|
168
|
+
title: gitContext.pr.title,
|
|
169
|
+
children: ["PR #", gitContext.pr.number]
|
|
170
|
+
})]
|
|
171
|
+
})]
|
|
172
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
173
|
+
style: styles.chatArea,
|
|
174
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MessageList.MessageList, {
|
|
175
|
+
messages: messages
|
|
176
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MessageInput.MessageInput, {
|
|
177
|
+
onSend: handleSend,
|
|
178
|
+
disabled: !isConnected,
|
|
179
|
+
isProcessing: isProcessing,
|
|
180
|
+
placeholder: "Ask Claude about your project..."
|
|
181
|
+
})]
|
|
182
|
+
})]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function getInitialPort() {
|
|
186
|
+
try {
|
|
187
|
+
if (typeof window !== 'undefined' && window.__CLAUDE_DAEMON_PORT__) {
|
|
188
|
+
return parseInt(window.__CLAUDE_DAEMON_PORT__, 10) || _constants.DEFAULT_PORT;
|
|
189
|
+
}
|
|
190
|
+
const envPort = typeof process !== 'undefined' && process.env?.STORYBOOK_CLAUDE_DAEMON_PORT || null;
|
|
191
|
+
return envPort ? parseInt(envPort, 10) : _constants.DEFAULT_PORT;
|
|
192
|
+
} catch {
|
|
193
|
+
return _constants.DEFAULT_PORT;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function getInitialToken() {
|
|
197
|
+
try {
|
|
198
|
+
return typeof process !== 'undefined' && process.env?.STORYBOOK_CLAUDE_DAEMON_TOKEN || typeof process !== 'undefined' && process.env?.CLAUDE_DAEMON_TOKEN || null;
|
|
199
|
+
} catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const styles = {
|
|
204
|
+
page: {
|
|
205
|
+
display: 'flex',
|
|
206
|
+
flexDirection: 'column',
|
|
207
|
+
height: '100vh',
|
|
208
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
|
|
209
|
+
},
|
|
210
|
+
// Bar 1
|
|
211
|
+
titleBar: {
|
|
212
|
+
display: 'flex',
|
|
213
|
+
justifyContent: 'space-between',
|
|
214
|
+
alignItems: 'center',
|
|
215
|
+
padding: '10px 16px',
|
|
216
|
+
borderBottom: '1px solid rgba(0,0,0,0.1)',
|
|
217
|
+
backgroundColor: '#fafafa'
|
|
218
|
+
},
|
|
219
|
+
titleLeft: {
|
|
220
|
+
display: 'flex',
|
|
221
|
+
alignItems: 'center',
|
|
222
|
+
gap: '8px'
|
|
223
|
+
},
|
|
224
|
+
title: {
|
|
225
|
+
fontSize: '16px',
|
|
226
|
+
fontWeight: 600,
|
|
227
|
+
color: '#333'
|
|
228
|
+
},
|
|
229
|
+
statusDot: {
|
|
230
|
+
width: '8px',
|
|
231
|
+
height: '8px',
|
|
232
|
+
borderRadius: '50%',
|
|
233
|
+
display: 'inline-block',
|
|
234
|
+
flexShrink: 0
|
|
235
|
+
},
|
|
236
|
+
statusLabel: {
|
|
237
|
+
fontSize: '12px',
|
|
238
|
+
color: '#888'
|
|
239
|
+
},
|
|
240
|
+
infoIcon: {
|
|
241
|
+
color: '#bbb',
|
|
242
|
+
cursor: 'help',
|
|
243
|
+
display: 'inline-flex',
|
|
244
|
+
alignItems: 'center'
|
|
245
|
+
},
|
|
246
|
+
backButton: {
|
|
247
|
+
display: 'inline-flex',
|
|
248
|
+
alignItems: 'center',
|
|
249
|
+
padding: '6px 12px',
|
|
250
|
+
border: '1px solid rgba(0,0,0,0.15)',
|
|
251
|
+
borderRadius: '4px',
|
|
252
|
+
backgroundColor: 'transparent',
|
|
253
|
+
color: '#555',
|
|
254
|
+
fontSize: '13px',
|
|
255
|
+
cursor: 'pointer',
|
|
256
|
+
whiteSpace: 'nowrap'
|
|
257
|
+
},
|
|
258
|
+
// Bar 2
|
|
259
|
+
toolsBar: {
|
|
260
|
+
display: 'flex',
|
|
261
|
+
alignItems: 'center',
|
|
262
|
+
justifyContent: 'space-between',
|
|
263
|
+
borderBottom: '1px solid rgba(0,0,0,0.06)',
|
|
264
|
+
backgroundColor: '#fff'
|
|
265
|
+
},
|
|
266
|
+
gitContext: {
|
|
267
|
+
display: 'flex',
|
|
268
|
+
flexDirection: 'column',
|
|
269
|
+
alignItems: 'flex-end',
|
|
270
|
+
gap: '1px',
|
|
271
|
+
padding: '6px 12px',
|
|
272
|
+
flexShrink: 0
|
|
273
|
+
},
|
|
274
|
+
branchName: {
|
|
275
|
+
fontFamily: 'monospace',
|
|
276
|
+
fontSize: '11px',
|
|
277
|
+
color: '#555',
|
|
278
|
+
display: 'flex',
|
|
279
|
+
alignItems: 'center',
|
|
280
|
+
gap: '3px'
|
|
281
|
+
},
|
|
282
|
+
branchIcon: {
|
|
283
|
+
fontSize: '12px',
|
|
284
|
+
color: '#888'
|
|
285
|
+
},
|
|
286
|
+
prLink: {
|
|
287
|
+
color: '#0366d6',
|
|
288
|
+
textDecoration: 'none',
|
|
289
|
+
fontFamily: 'monospace',
|
|
290
|
+
fontSize: '11px'
|
|
291
|
+
},
|
|
292
|
+
// Chat area
|
|
293
|
+
chatArea: {
|
|
294
|
+
display: 'flex',
|
|
295
|
+
flexDirection: 'column',
|
|
296
|
+
flex: 1,
|
|
297
|
+
overflow: 'hidden'
|
|
298
|
+
}
|
|
299
|
+
};
|
package/dist/Panel.js
CHANGED
|
@@ -10,13 +10,15 @@ var _ConnectionStatus = require("./components/ConnectionStatus");
|
|
|
10
10
|
var _MessageList = require("./components/MessageList");
|
|
11
11
|
var _MessageInput = require("./components/MessageInput");
|
|
12
12
|
var _TokenInput = require("./components/TokenInput");
|
|
13
|
+
var _GitContextBar = require("./components/GitContextBar");
|
|
13
14
|
var _useClaudeSession = require("./useClaudeSession");
|
|
15
|
+
var _useGitContext = require("./useGitContext");
|
|
14
16
|
var _useStoryContext = require("./useStoryContext");
|
|
15
17
|
var _constants = require("./constants");
|
|
16
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
19
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
18
20
|
/**
|
|
19
|
-
*
|
|
21
|
+
* Component-scoped Storybook addon panel for Claude.
|
|
20
22
|
*
|
|
21
23
|
* Manages the auth token, connects to the standalone daemon,
|
|
22
24
|
* and provides the chat interface with story-context awareness.
|
|
@@ -47,12 +49,16 @@ function ClaudePanel({
|
|
|
47
49
|
sendMessage,
|
|
48
50
|
startSession,
|
|
49
51
|
disconnect,
|
|
50
|
-
isConnected
|
|
52
|
+
isConnected,
|
|
53
|
+
client
|
|
51
54
|
} = (0, _useClaudeSession.useClaudeSession)({
|
|
52
55
|
host: _constants.DEFAULT_HOST,
|
|
53
56
|
port,
|
|
54
57
|
token
|
|
55
58
|
});
|
|
59
|
+
|
|
60
|
+
// Git context from daemon
|
|
61
|
+
const gitContext = (0, _useGitContext.useGitContext)(client, connectionState);
|
|
56
62
|
const handleTokenSubmit = (0, _react.useCallback)((newToken, newPort) => {
|
|
57
63
|
setAddonState({
|
|
58
64
|
...addonState,
|
|
@@ -69,7 +75,6 @@ function ClaudePanel({
|
|
|
69
75
|
}, [addonState, setAddonState, disconnect]);
|
|
70
76
|
const handleSend = (0, _react.useCallback)(text => {
|
|
71
77
|
if (!sessionId) {
|
|
72
|
-
// Auto-start a session on first message
|
|
73
78
|
startSession();
|
|
74
79
|
}
|
|
75
80
|
const prefix = contextEnabled ? (0, _useStoryContext.buildContextPrefix)(storyContext) : null;
|
|
@@ -100,6 +105,10 @@ function ClaudePanel({
|
|
|
100
105
|
state: connectionState,
|
|
101
106
|
sessionId: sessionId,
|
|
102
107
|
onDisconnect: handleDisconnect
|
|
108
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_GitContextBar.GitContextBar, {
|
|
109
|
+
branch: gitContext.branch,
|
|
110
|
+
pr: gitContext.pr,
|
|
111
|
+
loading: gitContext.loading
|
|
103
112
|
}), storyContext.componentPath && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
104
113
|
style: styles.contextBar,
|
|
105
114
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("label", {
|
package/dist/WebSocketClient.js
CHANGED
|
@@ -151,6 +151,16 @@ class WebSocketClient {
|
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Send a query to the daemon.
|
|
156
|
+
*/
|
|
157
|
+
sendQuery(queryType) {
|
|
158
|
+
this._send({
|
|
159
|
+
type: 'query',
|
|
160
|
+
queryType
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
154
164
|
// ---------------------------------------------------------------------------
|
|
155
165
|
// Internal
|
|
156
166
|
// ---------------------------------------------------------------------------
|
|
@@ -174,6 +184,7 @@ class WebSocketClient {
|
|
|
174
184
|
case 'error':
|
|
175
185
|
case 'user_input':
|
|
176
186
|
case 'pong':
|
|
187
|
+
case 'query_result':
|
|
177
188
|
this._emit(msg.type, msg);
|
|
178
189
|
break;
|
|
179
190
|
default:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.GitContextBar = GitContextBar;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
/**
|
|
11
|
+
* Thin horizontal bar displaying the current git branch and PR link.
|
|
12
|
+
*
|
|
13
|
+
* Renders nothing if loading or no data is available.
|
|
14
|
+
*/
|
|
15
|
+
function GitContextBar({
|
|
16
|
+
branch,
|
|
17
|
+
pr,
|
|
18
|
+
loading
|
|
19
|
+
}) {
|
|
20
|
+
if (loading || !branch) return null;
|
|
21
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
22
|
+
style: styles.container,
|
|
23
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
24
|
+
style: styles.left,
|
|
25
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
26
|
+
style: styles.branchIcon,
|
|
27
|
+
title: "Git branch",
|
|
28
|
+
children: "\u2387"
|
|
29
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
30
|
+
style: styles.branchName,
|
|
31
|
+
children: branch
|
|
32
|
+
})]
|
|
33
|
+
}), pr && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
34
|
+
style: styles.right,
|
|
35
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("a", {
|
|
36
|
+
href: pr.url,
|
|
37
|
+
target: "_blank",
|
|
38
|
+
rel: "noopener noreferrer",
|
|
39
|
+
style: styles.prLink,
|
|
40
|
+
title: pr.title,
|
|
41
|
+
children: ["PR #", pr.number]
|
|
42
|
+
})
|
|
43
|
+
})]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const styles = {
|
|
47
|
+
container: {
|
|
48
|
+
display: 'flex',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
justifyContent: 'space-between',
|
|
51
|
+
padding: '3px 12px',
|
|
52
|
+
borderBottom: '1px solid rgba(0,0,0,0.1)',
|
|
53
|
+
backgroundColor: '#f5f5f5',
|
|
54
|
+
fontSize: '11px',
|
|
55
|
+
fontFamily: 'inherit'
|
|
56
|
+
},
|
|
57
|
+
left: {
|
|
58
|
+
display: 'flex',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
gap: '4px'
|
|
61
|
+
},
|
|
62
|
+
branchIcon: {
|
|
63
|
+
fontSize: '12px',
|
|
64
|
+
color: '#888'
|
|
65
|
+
},
|
|
66
|
+
branchName: {
|
|
67
|
+
fontFamily: 'monospace',
|
|
68
|
+
fontSize: '11px',
|
|
69
|
+
color: '#555',
|
|
70
|
+
backgroundColor: '#eee',
|
|
71
|
+
padding: '1px 5px',
|
|
72
|
+
borderRadius: '3px'
|
|
73
|
+
},
|
|
74
|
+
right: {
|
|
75
|
+
display: 'flex',
|
|
76
|
+
alignItems: 'center'
|
|
77
|
+
},
|
|
78
|
+
prLink: {
|
|
79
|
+
color: '#0366d6',
|
|
80
|
+
textDecoration: 'none',
|
|
81
|
+
fontFamily: 'monospace',
|
|
82
|
+
fontSize: '11px'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.GlobalChatButton = GlobalChatButton;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _managerApi = require("storybook/manager-api");
|
|
9
|
+
var _components = require("storybook/internal/components");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
+
/**
|
|
13
|
+
* Toolbar button that navigates to the global Claude chat page.
|
|
14
|
+
*/
|
|
15
|
+
function GlobalChatButton() {
|
|
16
|
+
const api = (0, _managerApi.useStorybookApi)();
|
|
17
|
+
const handleClick = (0, _react.useCallback)(() => {
|
|
18
|
+
api.navigateUrl('/?path=/claude', {
|
|
19
|
+
replace: false
|
|
20
|
+
});
|
|
21
|
+
}, [api]);
|
|
22
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.IconButton, {
|
|
23
|
+
title: "Open Claude Global Chat",
|
|
24
|
+
onClick: handleClick,
|
|
25
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("svg", {
|
|
26
|
+
viewBox: "0 0 24 24",
|
|
27
|
+
width: "14",
|
|
28
|
+
height: "14",
|
|
29
|
+
fill: "none",
|
|
30
|
+
stroke: "currentColor",
|
|
31
|
+
strokeWidth: "2",
|
|
32
|
+
strokeLinecap: "round",
|
|
33
|
+
strokeLinejoin: "round",
|
|
34
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
35
|
+
d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -14,7 +14,8 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
14
14
|
function MessageInput({
|
|
15
15
|
onSend,
|
|
16
16
|
disabled,
|
|
17
|
-
isProcessing
|
|
17
|
+
isProcessing,
|
|
18
|
+
placeholder
|
|
18
19
|
}) {
|
|
19
20
|
const [text, setText] = (0, _react.useState)('');
|
|
20
21
|
const textareaRef = (0, _react.useRef)(null);
|
|
@@ -49,7 +50,7 @@ function MessageInput({
|
|
|
49
50
|
value: text,
|
|
50
51
|
onChange: handleInput,
|
|
51
52
|
onKeyDown: handleKeyDown,
|
|
52
|
-
placeholder: disabled ? 'Connect to daemon first...' : 'Ask Claude...',
|
|
53
|
+
placeholder: disabled ? 'Connect to daemon first...' : placeholder || 'Ask Claude...',
|
|
53
54
|
disabled: disabled,
|
|
54
55
|
rows: 1
|
|
55
56
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
@@ -53,6 +53,10 @@ function MessageItem({
|
|
|
53
53
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(UserMessage, {
|
|
54
54
|
text: message.text
|
|
55
55
|
});
|
|
56
|
+
case 'skill_invocation':
|
|
57
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(SkillInvocationMessage, {
|
|
58
|
+
skill: message.skill
|
|
59
|
+
});
|
|
56
60
|
case 'output':
|
|
57
61
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(OutputMessage, {
|
|
58
62
|
data: message.data,
|
|
@@ -354,6 +358,23 @@ function CompleteMessage({
|
|
|
354
358
|
})
|
|
355
359
|
});
|
|
356
360
|
}
|
|
361
|
+
function SkillInvocationMessage({
|
|
362
|
+
skill
|
|
363
|
+
}) {
|
|
364
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
365
|
+
style: styles.skillRow,
|
|
366
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
367
|
+
style: styles.skillBubble,
|
|
368
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
369
|
+
style: styles.skillIcon,
|
|
370
|
+
children: skill.icon
|
|
371
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
372
|
+
style: styles.skillLabel,
|
|
373
|
+
children: ["Skill: ", skill.label]
|
|
374
|
+
})]
|
|
375
|
+
})
|
|
376
|
+
});
|
|
377
|
+
}
|
|
357
378
|
function ErrorMessage({
|
|
358
379
|
message
|
|
359
380
|
}) {
|
|
@@ -567,6 +588,28 @@ const styles = {
|
|
|
567
588
|
maxHeight: '200px',
|
|
568
589
|
maxWidth: '90%'
|
|
569
590
|
},
|
|
591
|
+
// Skill invocation
|
|
592
|
+
skillRow: {
|
|
593
|
+
display: 'flex',
|
|
594
|
+
justifyContent: 'center'
|
|
595
|
+
},
|
|
596
|
+
skillBubble: {
|
|
597
|
+
display: 'inline-flex',
|
|
598
|
+
alignItems: 'center',
|
|
599
|
+
gap: '6px',
|
|
600
|
+
padding: '6px 14px',
|
|
601
|
+
borderRadius: '8px',
|
|
602
|
+
backgroundColor: '#e8eaf6',
|
|
603
|
+
fontSize: '12px',
|
|
604
|
+
color: '#3949ab',
|
|
605
|
+
fontWeight: 500
|
|
606
|
+
},
|
|
607
|
+
skillIcon: {
|
|
608
|
+
fontSize: '14px'
|
|
609
|
+
},
|
|
610
|
+
skillLabel: {
|
|
611
|
+
fontWeight: 600
|
|
612
|
+
},
|
|
570
613
|
// Error messages
|
|
571
614
|
errorRow: {
|
|
572
615
|
display: 'flex',
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SkillsBar = SkillsBar;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
/**
|
|
11
|
+
* Horizontal row of skill buttons rendered in the global chat page.
|
|
12
|
+
*
|
|
13
|
+
* Each button triggers its skill's prompt through the chat session.
|
|
14
|
+
* Disabled when not connected to the daemon.
|
|
15
|
+
*/
|
|
16
|
+
function SkillsBar({
|
|
17
|
+
skills,
|
|
18
|
+
onTrigger,
|
|
19
|
+
disabled
|
|
20
|
+
}) {
|
|
21
|
+
if (!skills || skills.length === 0) return null;
|
|
22
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
23
|
+
style: styles.bar,
|
|
24
|
+
children: skills.map(skill => /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
|
|
25
|
+
style: {
|
|
26
|
+
...styles.button,
|
|
27
|
+
...(disabled ? styles.buttonDisabled : {})
|
|
28
|
+
},
|
|
29
|
+
onClick: () => onTrigger(skill),
|
|
30
|
+
disabled: disabled,
|
|
31
|
+
title: skill.prompt,
|
|
32
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
33
|
+
style: styles.icon,
|
|
34
|
+
children: skill.icon
|
|
35
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
36
|
+
children: skill.label
|
|
37
|
+
})]
|
|
38
|
+
}, skill.id))
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const styles = {
|
|
42
|
+
bar: {
|
|
43
|
+
display: 'flex',
|
|
44
|
+
gap: '6px',
|
|
45
|
+
padding: '8px 12px',
|
|
46
|
+
overflowX: 'auto',
|
|
47
|
+
flexShrink: 0
|
|
48
|
+
},
|
|
49
|
+
button: {
|
|
50
|
+
display: 'inline-flex',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
gap: '4px',
|
|
53
|
+
padding: '5px 10px',
|
|
54
|
+
border: '1px solid rgba(0,0,0,0.12)',
|
|
55
|
+
borderRadius: '16px',
|
|
56
|
+
backgroundColor: '#fff',
|
|
57
|
+
color: '#444',
|
|
58
|
+
fontSize: '12px',
|
|
59
|
+
cursor: 'pointer',
|
|
60
|
+
whiteSpace: 'nowrap',
|
|
61
|
+
transition: 'background-color 0.15s'
|
|
62
|
+
},
|
|
63
|
+
buttonDisabled: {
|
|
64
|
+
opacity: 0.45,
|
|
65
|
+
cursor: 'default'
|
|
66
|
+
},
|
|
67
|
+
icon: {
|
|
68
|
+
fontSize: '13px'
|
|
69
|
+
}
|
|
70
|
+
};
|
package/dist/constants.js
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.PARAM_KEY = exports.PANEL_ID = exports.DEFAULT_PORT = exports.DEFAULT_HOST = exports.CONNECTION_STATES = exports.ADDON_ID = void 0;
|
|
6
|
+
exports.TOOL_ID = exports.PARAM_KEY = exports.PANEL_ID = exports.PAGE_ID = exports.GLOBAL_SESSION_KEY = exports.DEFAULT_PORT = exports.DEFAULT_HOST = exports.CONNECTION_STATES = exports.ADDON_ID = void 0;
|
|
7
7
|
const ADDON_ID = exports.ADDON_ID = 'storybook-addon-claude';
|
|
8
8
|
const PANEL_ID = exports.PANEL_ID = `${ADDON_ID}/panel`;
|
|
9
|
+
const PAGE_ID = exports.PAGE_ID = `${ADDON_ID}/page`;
|
|
10
|
+
const TOOL_ID = exports.TOOL_ID = `${ADDON_ID}/tool`;
|
|
9
11
|
const PARAM_KEY = exports.PARAM_KEY = 'claude';
|
|
10
12
|
const DEFAULT_PORT = exports.DEFAULT_PORT = 3001;
|
|
11
13
|
const DEFAULT_HOST = exports.DEFAULT_HOST = 'localhost';
|
|
14
|
+
const GLOBAL_SESSION_KEY = exports.GLOBAL_SESSION_KEY = 'claude-global';
|
|
12
15
|
const CONNECTION_STATES = exports.CONNECTION_STATES = {
|
|
13
16
|
DISCONNECTED: 'disconnected',
|
|
14
17
|
CONNECTING: 'connecting',
|
package/dist/manager.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
var _managerApi = require("storybook/manager-api");
|
|
4
4
|
var _constants = require("./constants");
|
|
5
5
|
var _Panel = require("./Panel");
|
|
6
|
+
var _GlobalPanel = require("./GlobalPanel");
|
|
7
|
+
var _GlobalChatButton = require("./components/GlobalChatButton");
|
|
6
8
|
_managerApi.addons.register(_constants.ADDON_ID, api => {
|
|
9
|
+
// Component-scoped chat panel (addon area below canvas)
|
|
7
10
|
_managerApi.addons.add(_constants.PANEL_ID, {
|
|
8
11
|
type: _managerApi.types.PANEL,
|
|
9
12
|
title: 'Claude',
|
|
@@ -12,4 +15,19 @@ _managerApi.addons.register(_constants.ADDON_ID, api => {
|
|
|
12
15
|
}) => viewMode === 'story',
|
|
13
16
|
render: _Panel.ClaudePanel
|
|
14
17
|
});
|
|
18
|
+
|
|
19
|
+
// Global chat page (full-page route, not scoped to any component)
|
|
20
|
+
_managerApi.addons.add(_constants.PAGE_ID, {
|
|
21
|
+
type: _managerApi.types.experimental_PAGE,
|
|
22
|
+
title: 'Claude Global',
|
|
23
|
+
url: '/claude',
|
|
24
|
+
render: _GlobalPanel.GlobalPanel
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Toolbar button for quick navigation to global chat
|
|
28
|
+
_managerApi.addons.add(_constants.TOOL_ID, {
|
|
29
|
+
type: _managerApi.types.TOOLEXTRA,
|
|
30
|
+
title: 'Claude Global Chat',
|
|
31
|
+
render: _GlobalChatButton.GlobalChatButton
|
|
32
|
+
});
|
|
15
33
|
});
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getGlobalSkills = getGlobalSkills;
|
|
7
|
+
/**
|
|
8
|
+
* Hardcoded global skill definitions.
|
|
9
|
+
*
|
|
10
|
+
* Each skill is a chat shortcut — triggering it sends the prompt through the
|
|
11
|
+
* active session and shows a visual indicator in the message list.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const STATIC_SKILLS = [{
|
|
15
|
+
id: 'sync-components',
|
|
16
|
+
label: 'Sync Components',
|
|
17
|
+
icon: '\u21BB',
|
|
18
|
+
prompt: 'Synchronize all Storybook components with their Figma designs. Report what changed.'
|
|
19
|
+
}, {
|
|
20
|
+
id: 'run-tests',
|
|
21
|
+
label: 'Run Tests',
|
|
22
|
+
icon: '\u25B6',
|
|
23
|
+
prompt: 'Run the full test suite and summarize the results.'
|
|
24
|
+
}, {
|
|
25
|
+
id: 'audit-stories',
|
|
26
|
+
label: 'Audit Stories',
|
|
27
|
+
icon: '\uD83D\uDD0D',
|
|
28
|
+
prompt: 'Audit all components and list any that are missing Storybook stories or have outdated ones.'
|
|
29
|
+
}];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns the PR skill definition based on whether a PR exists on the current branch.
|
|
33
|
+
*/
|
|
34
|
+
function getPrSkill(pr) {
|
|
35
|
+
if (pr && pr.number) {
|
|
36
|
+
return {
|
|
37
|
+
id: 'finalize-pr',
|
|
38
|
+
label: 'Finalize PR',
|
|
39
|
+
icon: '\u2714',
|
|
40
|
+
prompt: `Check the status of PR #${pr.number} (${pr.url}). Review CI checks, review status, and merge conflicts. If everything is green and approved, offer to merge it. If there are issues, summarize what needs attention to make it ready for merge.`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
id: 'new-pr',
|
|
45
|
+
label: 'New PR',
|
|
46
|
+
icon: '\u2795',
|
|
47
|
+
prompt: 'Create a new pull request for the current branch with a summary of all changes.'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build the full skills list, with the PR skill adapting to git context.
|
|
53
|
+
*/
|
|
54
|
+
function getGlobalSkills(gitContext) {
|
|
55
|
+
const pr = gitContext?.pr || null;
|
|
56
|
+
return [STATIC_SKILLS[0],
|
|
57
|
+
// Sync Components
|
|
58
|
+
getPrSkill(pr),
|
|
59
|
+
// New PR / Finalize PR
|
|
60
|
+
STATIC_SKILLS[1],
|
|
61
|
+
// Run Tests
|
|
62
|
+
STATIC_SKILLS[2] // Audit Stories
|
|
63
|
+
];
|
|
64
|
+
}
|
package/dist/useClaudeSession.js
CHANGED
|
@@ -7,20 +7,20 @@ exports.useClaudeSession = useClaudeSession;
|
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _WebSocketClient = require("./WebSocketClient");
|
|
9
9
|
var _constants = require("./constants");
|
|
10
|
-
const
|
|
11
|
-
function loadPersistedSessionId() {
|
|
10
|
+
const DEFAULT_STORAGE_KEY = `${_constants.ADDON_ID}:sessionId`;
|
|
11
|
+
function loadPersistedSessionId(storageKey) {
|
|
12
12
|
try {
|
|
13
|
-
return localStorage.getItem(
|
|
13
|
+
return localStorage.getItem(storageKey) || null;
|
|
14
14
|
} catch {
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
function persistSessionId(id) {
|
|
18
|
+
function persistSessionId(storageKey, id) {
|
|
19
19
|
try {
|
|
20
20
|
if (id) {
|
|
21
|
-
localStorage.setItem(
|
|
21
|
+
localStorage.setItem(storageKey, id);
|
|
22
22
|
} else {
|
|
23
|
-
localStorage.removeItem(
|
|
23
|
+
localStorage.removeItem(storageKey);
|
|
24
24
|
}
|
|
25
25
|
} catch {
|
|
26
26
|
// localStorage may be unavailable
|
|
@@ -39,11 +39,13 @@ function useClaudeSession(options = {}) {
|
|
|
39
39
|
host,
|
|
40
40
|
port,
|
|
41
41
|
token,
|
|
42
|
-
sessionId: initialSessionId
|
|
42
|
+
sessionId: initialSessionId,
|
|
43
|
+
storageKey
|
|
43
44
|
} = options;
|
|
45
|
+
const _storageKey = storageKey || DEFAULT_STORAGE_KEY;
|
|
44
46
|
const [connectionState, setConnectionState] = (0, _react.useState)(_constants.CONNECTION_STATES.DISCONNECTED);
|
|
45
47
|
const [messages, setMessages] = (0, _react.useState)([]);
|
|
46
|
-
const [sessionId, setSessionId] = (0, _react.useState)(initialSessionId || loadPersistedSessionId());
|
|
48
|
+
const [sessionId, setSessionId] = (0, _react.useState)(initialSessionId || loadPersistedSessionId(_storageKey));
|
|
47
49
|
const [isProcessing, setIsProcessing] = (0, _react.useState)(false);
|
|
48
50
|
const [authFailed, setAuthFailed] = (0, _react.useState)(false);
|
|
49
51
|
const clientRef = (0, _react.useRef)(null);
|
|
@@ -64,13 +66,18 @@ function useClaudeSession(options = {}) {
|
|
|
64
66
|
setConnectionState(newState);
|
|
65
67
|
}), client.on('session_activated', msg => {
|
|
66
68
|
setSessionId(msg.sessionId);
|
|
67
|
-
persistSessionId(msg.sessionId);
|
|
69
|
+
persistSessionId(_storageKey, msg.sessionId);
|
|
68
70
|
}), client.on('output', msg => {
|
|
69
71
|
if (msg.sessionId !== sessionIdRef.current) return;
|
|
70
72
|
setMessages(prev => [...prev, {
|
|
71
73
|
...msg,
|
|
72
74
|
id: crypto.randomUUID()
|
|
73
75
|
}]);
|
|
76
|
+
// A 'result' output signals the end of Claude's turn in streaming mode
|
|
77
|
+
// (the CLI process stays alive, so 'complete' may never fire)
|
|
78
|
+
if (msg.data && msg.data.type === 'result') {
|
|
79
|
+
setIsProcessing(false);
|
|
80
|
+
}
|
|
74
81
|
}), client.on('user_input', msg => {
|
|
75
82
|
if (msg.sessionId !== sessionIdRef.current) return;
|
|
76
83
|
// Replayed user messages from session history — strip context prefix
|
|
@@ -155,7 +162,7 @@ function useClaudeSession(options = {}) {
|
|
|
155
162
|
// Synchronously update the ref so sendMessage sees it immediately
|
|
156
163
|
sessionIdRef.current = sid;
|
|
157
164
|
setSessionId(sid);
|
|
158
|
-
persistSessionId(sid);
|
|
165
|
+
persistSessionId(_storageKey, sid);
|
|
159
166
|
setMessages([]);
|
|
160
167
|
if (client && client.isConnected) {
|
|
161
168
|
client.activate(sid, workingDir);
|
|
@@ -172,7 +179,7 @@ function useClaudeSession(options = {}) {
|
|
|
172
179
|
}
|
|
173
180
|
sessionIdRef.current = null;
|
|
174
181
|
setSessionId(null);
|
|
175
|
-
persistSessionId(null);
|
|
182
|
+
persistSessionId(_storageKey, null);
|
|
176
183
|
setMessages([]);
|
|
177
184
|
setIsProcessing(false);
|
|
178
185
|
}, []);
|
|
@@ -185,7 +192,9 @@ function useClaudeSession(options = {}) {
|
|
|
185
192
|
sendMessage,
|
|
186
193
|
startSession,
|
|
187
194
|
clearMessages,
|
|
195
|
+
setMessages,
|
|
188
196
|
disconnect,
|
|
189
|
-
isConnected: connectionState === _constants.CONNECTION_STATES.CONNECTED
|
|
197
|
+
isConnected: connectionState === _constants.CONNECTION_STATES.CONNECTED,
|
|
198
|
+
client: clientRef.current
|
|
190
199
|
};
|
|
191
200
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useGitContext = useGitContext;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _constants = require("./constants");
|
|
9
|
+
/**
|
|
10
|
+
* React hook that fetches git context (branch, PR info) from the daemon.
|
|
11
|
+
*
|
|
12
|
+
* Sends a `query` message with `queryType: 'git_context'` when the WebSocket
|
|
13
|
+
* client is connected, and refreshes every 60 seconds.
|
|
14
|
+
*
|
|
15
|
+
* @param {import('./WebSocketClient').WebSocketClient} client - The WebSocket client instance
|
|
16
|
+
* @param {string} connectionState - Current connection state
|
|
17
|
+
* @returns {{ branch: string|null, pr: object|null, loading: boolean }}
|
|
18
|
+
*/
|
|
19
|
+
function useGitContext(client, connectionState) {
|
|
20
|
+
const [gitData, setGitData] = (0, _react.useState)(null);
|
|
21
|
+
const [loading, setLoading] = (0, _react.useState)(false);
|
|
22
|
+
const intervalRef = (0, _react.useRef)(null);
|
|
23
|
+
const unsubRef = (0, _react.useRef)(null);
|
|
24
|
+
const fetchContext = (0, _react.useCallback)(() => {
|
|
25
|
+
if (!client || !client.isConnected) return;
|
|
26
|
+
setLoading(true);
|
|
27
|
+
client.sendQuery('git_context');
|
|
28
|
+
}, [client]);
|
|
29
|
+
(0, _react.useEffect)(() => {
|
|
30
|
+
if (!client) return;
|
|
31
|
+
|
|
32
|
+
// Listen for query_result messages
|
|
33
|
+
const unsub = client.on('query_result', msg => {
|
|
34
|
+
if (msg.queryType === 'git_context') {
|
|
35
|
+
setGitData(msg.data || null);
|
|
36
|
+
setLoading(false);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Also listen for errors related to queries (older daemons)
|
|
41
|
+
const unsubErr = client.on('error', msg => {
|
|
42
|
+
if (msg.code === 'UNKNOWN_TYPE' || msg.code === 'UNKNOWN_QUERY') {
|
|
43
|
+
// Daemon doesn't support query — stop trying
|
|
44
|
+
setLoading(false);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
unsubRef.current = () => {
|
|
48
|
+
unsub();
|
|
49
|
+
unsubErr();
|
|
50
|
+
};
|
|
51
|
+
return () => {
|
|
52
|
+
if (unsubRef.current) unsubRef.current();
|
|
53
|
+
};
|
|
54
|
+
}, [client]);
|
|
55
|
+
|
|
56
|
+
// Fetch on connect and refresh every 60 seconds
|
|
57
|
+
(0, _react.useEffect)(() => {
|
|
58
|
+
if (connectionState !== _constants.CONNECTION_STATES.CONNECTED) {
|
|
59
|
+
// Clear interval when not connected
|
|
60
|
+
if (intervalRef.current) {
|
|
61
|
+
clearInterval(intervalRef.current);
|
|
62
|
+
intervalRef.current = null;
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Fetch immediately on connect
|
|
68
|
+
fetchContext();
|
|
69
|
+
|
|
70
|
+
// Refresh every 60 seconds
|
|
71
|
+
intervalRef.current = setInterval(fetchContext, 60000);
|
|
72
|
+
return () => {
|
|
73
|
+
if (intervalRef.current) {
|
|
74
|
+
clearInterval(intervalRef.current);
|
|
75
|
+
intervalRef.current = null;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}, [connectionState, fetchContext]);
|
|
79
|
+
return {
|
|
80
|
+
branch: gitData?.branch || null,
|
|
81
|
+
pr: gitData?.pr || null,
|
|
82
|
+
loading
|
|
83
|
+
};
|
|
84
|
+
}
|
package/package.json
CHANGED