@guidekit/react 0.1.0-beta.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/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @guidekit/react
2
+
3
+ React bindings for the GuideKit SDK. Provides the `GuideKitProvider`, split hooks, and a Shadow DOM widget for adding an AI assistant to any React application.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @guidekit/core @guidekit/react
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { GuideKitProvider } from '@guidekit/react';
15
+
16
+ function App() {
17
+ return (
18
+ <GuideKitProvider
19
+ tokenEndpoint="/api/guidekit/token"
20
+ agent={{ name: 'Guide', greeting: 'Hi! How can I help?' }}
21
+ options={{ mode: 'text' }}
22
+ >
23
+ <YourApp />
24
+ </GuideKitProvider>
25
+ );
26
+ }
27
+ ```
28
+
29
+ ## Hooks
30
+
31
+ ```tsx
32
+ import {
33
+ useGuideKitStatus,
34
+ useGuideKitVoice,
35
+ useGuideKitActions,
36
+ useGuideKitContext,
37
+ } from '@guidekit/react';
38
+
39
+ function MyComponent() {
40
+ const { status, isReady } = useGuideKitStatus();
41
+ const { sendText, reset } = useGuideKitActions();
42
+ const { isListening, startVoice, stopVoice } = useGuideKitVoice();
43
+ const { transcript, messages } = useGuideKitContext();
44
+
45
+ return (
46
+ <div>
47
+ <p>Status: {status}</p>
48
+ <button onClick={() => sendText('Help me')}>Ask</button>
49
+ </div>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ## Sub-exports
55
+
56
+ ### `@guidekit/react/devtools`
57
+
58
+ Development-only component for inspecting SDK state, events, and context.
59
+
60
+ ```tsx
61
+ import { GuideKitDevTools } from '@guidekit/react/devtools';
62
+ ```
63
+
64
+ ### `@guidekit/react/testing`
65
+
66
+ Test utilities for mocking the provider in unit tests.
67
+
68
+ ```tsx
69
+ import { MockGuideKitProvider, simulateVoiceInput } from '@guidekit/react/testing';
70
+ ```
71
+
72
+ ## Documentation
73
+
74
+ Full documentation: [guidekit.dev/docs](https://guidekit.dev/docs)
75
+
76
+ ## License
77
+
78
+ [MIT](../../LICENSE)
@@ -0,0 +1,8 @@
1
+ import { createContext } from 'react';
2
+
3
+ // src/_context.ts
4
+ var GuideKitContext = createContext(null);
5
+
6
+ export { GuideKitContext };
7
+ //# sourceMappingURL=chunk-4NUEGCBT.js.map
8
+ //# sourceMappingURL=chunk-4NUEGCBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/_context.ts"],"names":[],"mappings":";;;AAGO,IAAM,eAAA,GAAkB,cAAmC,IAAI","file":"chunk-4NUEGCBT.js","sourcesContent":["import { createContext } from 'react';\nimport type { GuideKitCore } from '@guidekit/core';\n\nexport const GuideKitContext = createContext<GuideKitCore | null>(null);\n"]}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/_context.ts
6
+ var GuideKitContext = react.createContext(null);
7
+
8
+ exports.GuideKitContext = GuideKitContext;
9
+ //# sourceMappingURL=chunk-EQGJ2LTR.cjs.map
10
+ //# sourceMappingURL=chunk-EQGJ2LTR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/_context.ts"],"names":["createContext"],"mappings":";;;;;AAGO,IAAM,eAAA,GAAkBA,oBAAmC,IAAI","file":"chunk-EQGJ2LTR.cjs","sourcesContent":["import { createContext } from 'react';\nimport type { GuideKitCore } from '@guidekit/core';\n\nexport const GuideKitContext = createContext<GuideKitCore | null>(null);\n"]}
@@ -0,0 +1,576 @@
1
+ 'use strict';
2
+
3
+ var chunkEQGJ2LTR_cjs = require('./chunk-EQGJ2LTR.cjs');
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var MAX_EVENT_LOG = 200;
8
+ var TAB_LABELS = {
9
+ state: "State",
10
+ events: "Events",
11
+ sections: "Sections",
12
+ ratelimits: "Rate Limits"
13
+ };
14
+ var TAB_ORDER = ["state", "events", "sections", "ratelimits"];
15
+ var S = {
16
+ container: {
17
+ position: "fixed",
18
+ top: 24,
19
+ right: 24,
20
+ zIndex: 2147483646,
21
+ fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace",
22
+ fontSize: 12,
23
+ lineHeight: 1.5,
24
+ color: "#e2e8f0",
25
+ pointerEvents: "auto"
26
+ },
27
+ toggle: {
28
+ display: "flex",
29
+ alignItems: "center",
30
+ gap: 6,
31
+ padding: "6px 12px",
32
+ border: "1px solid #334155",
33
+ borderRadius: 8,
34
+ background: "#1e293b",
35
+ color: "#94a3b8",
36
+ cursor: "pointer",
37
+ fontSize: 11,
38
+ fontFamily: "inherit",
39
+ whiteSpace: "nowrap"
40
+ },
41
+ panel: {
42
+ width: 420,
43
+ maxHeight: 520,
44
+ background: "#0f172a",
45
+ border: "1px solid #334155",
46
+ borderRadius: 10,
47
+ overflow: "hidden",
48
+ display: "flex",
49
+ flexDirection: "column",
50
+ boxShadow: "0 20px 60px rgba(0,0,0,0.5)"
51
+ },
52
+ titleBar: {
53
+ display: "flex",
54
+ alignItems: "center",
55
+ justifyContent: "space-between",
56
+ padding: "8px 12px",
57
+ background: "#1e293b",
58
+ borderBottom: "1px solid #334155",
59
+ flexShrink: 0
60
+ },
61
+ titleText: {
62
+ fontWeight: 700,
63
+ fontSize: 12,
64
+ color: "#e2e8f0",
65
+ margin: 0
66
+ },
67
+ closeBtn: {
68
+ background: "none",
69
+ border: "none",
70
+ color: "#64748b",
71
+ cursor: "pointer",
72
+ fontSize: 16,
73
+ lineHeight: 1,
74
+ padding: "0 4px",
75
+ fontFamily: "inherit"
76
+ },
77
+ tabBar: {
78
+ display: "flex",
79
+ borderBottom: "1px solid #334155",
80
+ background: "#1e293b",
81
+ flexShrink: 0
82
+ },
83
+ tab: (active) => ({
84
+ flex: 1,
85
+ padding: "6px 8px",
86
+ background: active ? "#0f172a" : "transparent",
87
+ color: active ? "#e2e8f0" : "#64748b",
88
+ border: "none",
89
+ borderBottom: active ? "2px solid #6366f1" : "2px solid transparent",
90
+ cursor: "pointer",
91
+ fontSize: 11,
92
+ fontFamily: "inherit",
93
+ fontWeight: active ? 600 : 400,
94
+ transition: "color 0.15s, background 0.15s"
95
+ }),
96
+ tabContent: {
97
+ flex: 1,
98
+ overflow: "auto",
99
+ padding: 12,
100
+ minHeight: 200,
101
+ maxHeight: 400
102
+ },
103
+ row: {
104
+ display: "flex",
105
+ justifyContent: "space-between",
106
+ alignItems: "center",
107
+ padding: "4px 0",
108
+ borderBottom: "1px solid #1e293b"
109
+ },
110
+ label: {
111
+ color: "#94a3b8",
112
+ fontSize: 11
113
+ },
114
+ value: {
115
+ color: "#e2e8f0",
116
+ fontSize: 11,
117
+ fontWeight: 600
118
+ },
119
+ badge: (color) => ({
120
+ display: "inline-block",
121
+ padding: "1px 6px",
122
+ borderRadius: 4,
123
+ fontSize: 10,
124
+ fontWeight: 600,
125
+ background: color,
126
+ color: "#fff"
127
+ }),
128
+ eventRow: {
129
+ padding: "3px 0",
130
+ borderBottom: "1px solid #1e293b",
131
+ wordBreak: "break-all"
132
+ },
133
+ eventTime: {
134
+ color: "#475569",
135
+ fontSize: 10,
136
+ marginRight: 6
137
+ },
138
+ eventName: {
139
+ color: "#818cf8",
140
+ fontSize: 11,
141
+ fontWeight: 600
142
+ },
143
+ eventData: {
144
+ color: "#64748b",
145
+ fontSize: 10,
146
+ marginLeft: 4,
147
+ display: "block",
148
+ whiteSpace: "pre-wrap",
149
+ maxHeight: 60,
150
+ overflow: "hidden"
151
+ },
152
+ sectionRow: {
153
+ padding: "4px 0",
154
+ borderBottom: "1px solid #1e293b"
155
+ },
156
+ sectionId: {
157
+ color: "#38bdf8",
158
+ fontSize: 11,
159
+ fontWeight: 600
160
+ },
161
+ sectionMeta: {
162
+ color: "#64748b",
163
+ fontSize: 10,
164
+ display: "flex",
165
+ gap: 8,
166
+ marginTop: 2
167
+ },
168
+ meter: {
169
+ height: 6,
170
+ borderRadius: 3,
171
+ background: "#1e293b",
172
+ overflow: "hidden",
173
+ marginTop: 4
174
+ },
175
+ meterFill: (pct, color) => ({
176
+ height: "100%",
177
+ width: `${Math.min(pct, 100)}%`,
178
+ background: color,
179
+ borderRadius: 3,
180
+ transition: "width 0.3s ease"
181
+ }),
182
+ emptyState: {
183
+ color: "#475569",
184
+ textAlign: "center",
185
+ padding: "24px 0",
186
+ fontSize: 11
187
+ },
188
+ clearBtn: {
189
+ background: "#1e293b",
190
+ border: "1px solid #334155",
191
+ color: "#94a3b8",
192
+ cursor: "pointer",
193
+ fontSize: 10,
194
+ padding: "2px 8px",
195
+ borderRadius: 4,
196
+ fontFamily: "inherit"
197
+ }
198
+ };
199
+ function statusColor(status) {
200
+ switch (status) {
201
+ case "idle":
202
+ return "#475569";
203
+ case "listening":
204
+ return "#22c55e";
205
+ case "processing":
206
+ return "#f59e0b";
207
+ case "speaking":
208
+ return "#6366f1";
209
+ case "error":
210
+ return "#ef4444";
211
+ default:
212
+ return "#475569";
213
+ }
214
+ }
215
+ function formatTime(ts) {
216
+ const d = new Date(ts);
217
+ const h = d.getHours().toString().padStart(2, "0");
218
+ const m = d.getMinutes().toString().padStart(2, "0");
219
+ const s = d.getSeconds().toString().padStart(2, "0");
220
+ const ms = d.getMilliseconds().toString().padStart(3, "0");
221
+ return `${h}:${m}:${s}.${ms}`;
222
+ }
223
+ function truncateJSON(data, maxLen = 120) {
224
+ try {
225
+ const json = JSON.stringify(data);
226
+ if (json.length > maxLen) return json.slice(0, maxLen) + "...";
227
+ return json;
228
+ } catch {
229
+ return String(data);
230
+ }
231
+ }
232
+ function StateTab({ core }) {
233
+ const subscribe = react.useCallback(
234
+ (listener) => core.subscribe(listener),
235
+ [core]
236
+ );
237
+ const getSnapshot = react.useCallback(() => core.getSnapshot(), [core]);
238
+ const store = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
239
+ const agentStatus = store.status.agentState.status;
240
+ const isReady = store.status.isReady;
241
+ const isListening = store.voice.isListening;
242
+ const isSpeaking = store.voice.isSpeaking;
243
+ const hasVoice = core.hasVoice;
244
+ const quietMode = core.quietMode;
245
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
246
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
247
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "isReady" }),
248
+ /* @__PURE__ */ jsxRuntime.jsx(
249
+ "span",
250
+ {
251
+ style: S.badge(isReady ? "#22c55e" : "#ef4444"),
252
+ children: isReady ? "true" : "false"
253
+ }
254
+ )
255
+ ] }),
256
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
257
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "agentState" }),
258
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.badge(statusColor(agentStatus)), children: agentStatus })
259
+ ] }),
260
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
261
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "isListening" }),
262
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.value, children: String(isListening) })
263
+ ] }),
264
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
265
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "isSpeaking" }),
266
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.value, children: String(isSpeaking) })
267
+ ] }),
268
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "hasVoice" }),
270
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.value, children: String(hasVoice) })
271
+ ] }),
272
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
273
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "quietMode" }),
274
+ /* @__PURE__ */ jsxRuntime.jsx(
275
+ "span",
276
+ {
277
+ style: S.badge(quietMode ? "#f59e0b" : "#475569"),
278
+ children: quietMode ? "on" : "off"
279
+ }
280
+ )
281
+ ] }),
282
+ store.status.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8 }, children: [
283
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...S.label, marginBottom: 4 }, children: "Error" }),
284
+ /* @__PURE__ */ jsxRuntime.jsx(
285
+ "div",
286
+ {
287
+ style: {
288
+ background: "#1e293b",
289
+ padding: 8,
290
+ borderRadius: 6,
291
+ color: "#fca5a5",
292
+ fontSize: 10,
293
+ whiteSpace: "pre-wrap",
294
+ maxHeight: 80,
295
+ overflow: "auto"
296
+ },
297
+ children: store.status.error.message
298
+ }
299
+ )
300
+ ] })
301
+ ] });
302
+ }
303
+ function EventsTab({ core }) {
304
+ const [events, setEvents] = react.useState([]);
305
+ const idRef = react.useRef(0);
306
+ const scrollRef = react.useRef(null);
307
+ react.useEffect(() => {
308
+ const unsub = core.bus.onAny((data, eventName) => {
309
+ setEvents((prev) => {
310
+ const next = [
311
+ ...prev,
312
+ {
313
+ id: ++idRef.current,
314
+ timestamp: Date.now(),
315
+ name: eventName,
316
+ data
317
+ }
318
+ ];
319
+ if (next.length > MAX_EVENT_LOG) {
320
+ return next.slice(next.length - MAX_EVENT_LOG);
321
+ }
322
+ return next;
323
+ });
324
+ });
325
+ return unsub;
326
+ }, [core]);
327
+ react.useEffect(() => {
328
+ const el = scrollRef.current;
329
+ if (el) {
330
+ el.scrollTop = el.scrollHeight;
331
+ }
332
+ }, [events.length]);
333
+ const handleClear = react.useCallback(() => {
334
+ setEvents([]);
335
+ idRef.current = 0;
336
+ }, []);
337
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
338
+ /* @__PURE__ */ jsxRuntime.jsxs(
339
+ "div",
340
+ {
341
+ style: {
342
+ display: "flex",
343
+ justifyContent: "space-between",
344
+ alignItems: "center",
345
+ marginBottom: 8
346
+ },
347
+ children: [
348
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: S.label, children: [
349
+ events.length,
350
+ " events"
351
+ ] }),
352
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: S.clearBtn, onClick: handleClear, children: "Clear" })
353
+ ]
354
+ }
355
+ ),
356
+ /* @__PURE__ */ jsxRuntime.jsx(
357
+ "div",
358
+ {
359
+ ref: scrollRef,
360
+ style: { maxHeight: 340, overflow: "auto" },
361
+ children: events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.emptyState, children: "No events yet." }) : events.map((evt) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.eventRow, children: [
362
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.eventTime, children: formatTime(evt.timestamp) }),
363
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.eventName, children: evt.name }),
364
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.eventData, children: truncateJSON(evt.data) })
365
+ ] }, evt.id))
366
+ }
367
+ )
368
+ ] });
369
+ }
370
+ function SectionsTab({ core }) {
371
+ const [sections, setSections] = react.useState([]);
372
+ const subscribe = react.useCallback(
373
+ (listener) => core.subscribe(listener),
374
+ [core]
375
+ );
376
+ const getSnapshot = react.useCallback(
377
+ () => core.pageModel?.sections ?? [],
378
+ [core]
379
+ );
380
+ const liveSections = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
381
+ react.useEffect(() => {
382
+ setSections(liveSections);
383
+ }, [liveSections]);
384
+ react.useEffect(() => {
385
+ const unsub = core.bus.on("dom:scan-complete", () => {
386
+ setSections(core.pageModel?.sections ?? []);
387
+ });
388
+ return unsub;
389
+ }, [core]);
390
+ if (sections.length === 0) {
391
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.emptyState, children: "No sections discovered." });
392
+ }
393
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
394
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...S.label, marginBottom: 8 }, children: [
395
+ sections.length,
396
+ " section",
397
+ sections.length !== 1 ? "s" : ""
398
+ ] }),
399
+ sections.map((sec) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.sectionRow, children: [
400
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.sectionId, children: sec.id }),
401
+ sec.label && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#cbd5e1", fontSize: 11 }, children: sec.label }),
402
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.sectionMeta, children: [
403
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
404
+ "score: ",
405
+ sec.score
406
+ ] }),
407
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
408
+ "visible:",
409
+ " ",
410
+ /* @__PURE__ */ jsxRuntime.jsx(
411
+ "span",
412
+ {
413
+ style: {
414
+ color: sec.isVisible ? "#22c55e" : "#ef4444",
415
+ fontWeight: 600
416
+ },
417
+ children: sec.isVisible ? "yes" : "no"
418
+ }
419
+ )
420
+ ] }),
421
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
422
+ "ratio: ",
423
+ Math.round(sec.visibilityRatio * 100),
424
+ "%"
425
+ ] }),
426
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
427
+ "depth: ",
428
+ sec.depth
429
+ ] })
430
+ ] })
431
+ ] }, sec.id))
432
+ ] });
433
+ }
434
+ function RateLimitsTab({ core }) {
435
+ const [rlState, setRlState] = react.useState(
436
+ core.rateLimiterState
437
+ );
438
+ react.useEffect(() => {
439
+ const id = setInterval(() => {
440
+ setRlState(core.rateLimiterState);
441
+ }, 1e3);
442
+ return () => clearInterval(id);
443
+ }, [core]);
444
+ const maxLLM = 10;
445
+ const maxSTT = 60;
446
+ const maxTTS = 5e4;
447
+ const llmPct = rlState.llmCallsInWindow / maxLLM * 100;
448
+ const sttPct = rlState.sttMinutesUsed / maxSTT * 100;
449
+ const ttsPct = rlState.ttsCharsUsed / maxTTS * 100;
450
+ const meterColor = (pct) => pct >= 90 ? "#ef4444" : pct >= 60 ? "#f59e0b" : "#22c55e";
451
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 12 }, children: [
453
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
454
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "LLM calls (1 min window)" }),
455
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: S.value, children: [
456
+ rlState.llmCallsInWindow,
457
+ " / ",
458
+ maxLLM
459
+ ] })
460
+ ] }),
461
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meter, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meterFill(llmPct, meterColor(llmPct)) }) })
462
+ ] }),
463
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 12 }, children: [
464
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
465
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "STT minutes (session)" }),
466
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: S.value, children: [
467
+ rlState.sttMinutesUsed.toFixed(2),
468
+ " / ",
469
+ maxSTT
470
+ ] })
471
+ ] }),
472
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meter, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meterFill(sttPct, meterColor(sttPct)) }) })
473
+ ] }),
474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 12 }, children: [
475
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.row, children: [
476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.label, children: "TTS chars (session)" }),
477
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: S.value, children: [
478
+ rlState.ttsCharsUsed.toLocaleString(),
479
+ " / ",
480
+ maxTTS.toLocaleString()
481
+ ] })
482
+ ] }),
483
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meter, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.meterFill(ttsPct, meterColor(ttsPct)) }) })
484
+ ] }),
485
+ /* @__PURE__ */ jsxRuntime.jsxs(
486
+ "div",
487
+ {
488
+ style: {
489
+ ...S.label,
490
+ fontSize: 10,
491
+ marginTop: 8,
492
+ color: "#475569"
493
+ },
494
+ children: [
495
+ "Window started:",
496
+ " ",
497
+ rlState.llmWindowStart ? formatTime(rlState.llmWindowStart) : "N/A"
498
+ ]
499
+ }
500
+ )
501
+ ] });
502
+ }
503
+ function GuideKitDevTools(props) {
504
+ if (typeof window === "undefined") {
505
+ return null;
506
+ }
507
+ return /* @__PURE__ */ jsxRuntime.jsx(DevToolsInner, { core: props?.core });
508
+ }
509
+ function DevToolsInner({ core: coreProp }) {
510
+ const contextCore = react.useContext(chunkEQGJ2LTR_cjs.GuideKitContext);
511
+ const core = coreProp ?? contextCore;
512
+ const [collapsed, setCollapsed] = react.useState(true);
513
+ const [activeTab, setActiveTab] = react.useState("state");
514
+ if (!core) {
515
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.container, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.toggle, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#ef4444", fontWeight: 600 }, children: "GuideKit DevTools: no core instance found" }) }) });
516
+ }
517
+ if (collapsed) {
518
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.container, children: /* @__PURE__ */ jsxRuntime.jsxs(
519
+ "button",
520
+ {
521
+ style: S.toggle,
522
+ onClick: () => setCollapsed(false),
523
+ title: "Open GuideKit DevTools",
524
+ children: [
525
+ /* @__PURE__ */ jsxRuntime.jsx(
526
+ "span",
527
+ {
528
+ style: {
529
+ width: 8,
530
+ height: 8,
531
+ borderRadius: "50%",
532
+ background: "#6366f1",
533
+ display: "inline-block",
534
+ flexShrink: 0
535
+ }
536
+ }
537
+ ),
538
+ "GuideKit DevTools"
539
+ ]
540
+ }
541
+ ) });
542
+ }
543
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.container, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.panel, children: [
544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.titleBar, children: [
545
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: S.titleText, children: "GuideKit DevTools" }),
546
+ /* @__PURE__ */ jsxRuntime.jsx(
547
+ "button",
548
+ {
549
+ style: S.closeBtn,
550
+ onClick: () => setCollapsed(true),
551
+ title: "Close",
552
+ children: "x"
553
+ }
554
+ )
555
+ ] }),
556
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.tabBar, children: TAB_ORDER.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
557
+ "button",
558
+ {
559
+ style: S.tab(activeTab === id),
560
+ onClick: () => setActiveTab(id),
561
+ children: TAB_LABELS[id]
562
+ },
563
+ id
564
+ )) }),
565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.tabContent, children: [
566
+ activeTab === "state" && /* @__PURE__ */ jsxRuntime.jsx(StateTab, { core }),
567
+ activeTab === "events" && /* @__PURE__ */ jsxRuntime.jsx(EventsTab, { core }),
568
+ activeTab === "sections" && /* @__PURE__ */ jsxRuntime.jsx(SectionsTab, { core }),
569
+ activeTab === "ratelimits" && /* @__PURE__ */ jsxRuntime.jsx(RateLimitsTab, { core })
570
+ ] })
571
+ ] }) });
572
+ }
573
+
574
+ exports.GuideKitDevTools = GuideKitDevTools;
575
+ //# sourceMappingURL=devtools.cjs.map
576
+ //# sourceMappingURL=devtools.cjs.map