@flowget/ai-chat 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs ADDED
@@ -0,0 +1,567 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var react$1 = require('@assistant-ui/react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/react/WorkflowChat.tsx
9
+
10
+ // src/react/layout.ts
11
+ var ORIGIN_X = 40;
12
+ var ORIGIN_Y = 120;
13
+ var COL_GAP = 240;
14
+ var ROW_GAP = 140;
15
+ function computeDepths(graph) {
16
+ const incoming = /* @__PURE__ */ new Map();
17
+ const children = /* @__PURE__ */ new Map();
18
+ for (const node of graph.nodes) {
19
+ incoming.set(node.id, 0);
20
+ children.set(node.id, []);
21
+ }
22
+ for (const edge of graph.edges) {
23
+ if (!incoming.has(edge.target) || !children.has(edge.source)) continue;
24
+ incoming.set(edge.target, (incoming.get(edge.target) ?? 0) + 1);
25
+ children.get(edge.source)?.push(edge.target);
26
+ }
27
+ const depth = /* @__PURE__ */ new Map();
28
+ const queue = graph.nodes.filter((node) => (incoming.get(node.id) ?? 0) === 0).map((node) => node.id);
29
+ const first = graph.nodes[0];
30
+ if (queue.length === 0 && first) queue.push(first.id);
31
+ for (const id of queue) depth.set(id, 0);
32
+ let guard = graph.nodes.length * graph.nodes.length + 1;
33
+ while (queue.length > 0 && guard-- > 0) {
34
+ const id = queue.shift();
35
+ if (id === void 0) break;
36
+ const current = depth.get(id) ?? 0;
37
+ for (const child of children.get(id) ?? []) {
38
+ const next = current + 1;
39
+ if (next > (depth.get(child) ?? -1)) {
40
+ depth.set(child, next);
41
+ queue.push(child);
42
+ }
43
+ }
44
+ }
45
+ for (const node of graph.nodes) {
46
+ if (!depth.has(node.id)) depth.set(node.id, 0);
47
+ }
48
+ return depth;
49
+ }
50
+ function layeredPositions(graph) {
51
+ const depth = computeDepths(graph);
52
+ const rowByColumn = /* @__PURE__ */ new Map();
53
+ const positions = /* @__PURE__ */ new Map();
54
+ for (const node of graph.nodes) {
55
+ const col = depth.get(node.id) ?? 0;
56
+ const row = rowByColumn.get(col) ?? 0;
57
+ rowByColumn.set(col, row + 1);
58
+ positions.set(node.id, {
59
+ x: ORIGIN_X + col * COL_GAP,
60
+ y: ORIGIN_Y + row * ROW_GAP
61
+ });
62
+ }
63
+ return positions;
64
+ }
65
+ function toCanvasEdge(edge) {
66
+ return {
67
+ id: edge.id,
68
+ source: edge.source,
69
+ target: edge.target,
70
+ // Drop `null` handles / type; the strict canvas edge wants them absent.
71
+ ...typeof edge.sourceHandle === "string" ? { sourceHandle: edge.sourceHandle } : {},
72
+ ...typeof edge.targetHandle === "string" ? { targetHandle: edge.targetHandle } : {},
73
+ ...typeof edge.type === "string" ? { type: edge.type } : {}
74
+ };
75
+ }
76
+ function layoutProposal(proposed, current) {
77
+ const currentPositions = new Map(
78
+ (current?.nodes ?? []).map((node) => [node.id, node.position])
79
+ );
80
+ const layered = layeredPositions(proposed);
81
+ const nodes = proposed.nodes.map((node) => ({
82
+ id: node.id,
83
+ type: node.type,
84
+ data: node.data,
85
+ position: currentPositions.get(node.id) ?? node.position ?? layered.get(node.id) ?? { x: ORIGIN_X, y: ORIGIN_Y }
86
+ }));
87
+ return { nodes, edges: proposed.edges.map(toCanvasEdge) };
88
+ }
89
+ var BridgeContext = react.createContext(null);
90
+ function BridgeProvider({
91
+ bridge,
92
+ children
93
+ }) {
94
+ return /* @__PURE__ */ jsxRuntime.jsx(BridgeContext.Provider, { value: bridge, children });
95
+ }
96
+ function useBridge() {
97
+ const bridge = react.useContext(BridgeContext);
98
+ if (bridge === null) {
99
+ throw new Error("useApplyProposal must be used within <WorkflowChat>");
100
+ }
101
+ return bridge;
102
+ }
103
+ function applyProposal(bridge, proposed) {
104
+ const layout = bridge.layout ?? layoutProposal;
105
+ bridge.applyGraph(layout(proposed, bridge.getCurrentGraph()));
106
+ }
107
+ function useApplyProposal() {
108
+ const bridge = useBridge();
109
+ return react.useCallback(
110
+ (proposed) => applyProposal(bridge, proposed),
111
+ [bridge]
112
+ );
113
+ }
114
+
115
+ // src/react/contract.ts
116
+ var PROPOSE_TOOL_NAME = "propose_workflow";
117
+
118
+ // src/react/adapter.ts
119
+ function latestUserText(messages) {
120
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
121
+ const message = messages[i];
122
+ if (message?.role !== "user") continue;
123
+ return message.content.filter(
124
+ (part) => part.type === "text"
125
+ ).map((part) => part.text).join("\n").trim();
126
+ }
127
+ return "";
128
+ }
129
+ function createWorkflowChatAdapter({
130
+ transport,
131
+ getCurrentGraph,
132
+ getRequestMeta
133
+ }) {
134
+ return {
135
+ async *run({ messages, abortSignal, unstable_getMessage }) {
136
+ const inProgress = unstable_getMessage?.();
137
+ const decided = inProgress?.content.find(
138
+ (part) => part.type === "tool-call" && part.toolName === PROPOSE_TOOL_NAME && part.approval?.approved !== void 0
139
+ );
140
+ if (decided?.type === "tool-call") {
141
+ yield { content: [], status: { type: "complete", reason: "stop" } };
142
+ return;
143
+ }
144
+ const command = latestUserText(messages);
145
+ const meta = getRequestMeta?.() ?? {};
146
+ let text = "";
147
+ let terminal;
148
+ for await (const event of transport(
149
+ {
150
+ command,
151
+ currentGraph: getCurrentGraph(),
152
+ ...meta.actor !== void 0 ? { actor: meta.actor } : {},
153
+ ...meta.context !== void 0 ? { context: meta.context } : {}
154
+ },
155
+ abortSignal
156
+ )) {
157
+ if (event.kind === "text-delta") {
158
+ text += event.delta;
159
+ yield { content: [{ type: "text", text }], status: { type: "running" } };
160
+ } else if (event.kind === "message") {
161
+ terminal = {
162
+ content: [{ type: "text", text: event.text || text }],
163
+ status: { type: "complete", reason: "stop" }
164
+ };
165
+ } else if (event.kind === "proposal") {
166
+ const id = react$1.generateId();
167
+ const args = { graph: event.graph, summary: event.summary };
168
+ terminal = {
169
+ content: [
170
+ ...text ? [{ type: "text", text }] : [],
171
+ {
172
+ type: "tool-call",
173
+ toolCallId: id,
174
+ toolName: PROPOSE_TOOL_NAME,
175
+ args,
176
+ argsText: JSON.stringify(args),
177
+ approval: { id }
178
+ }
179
+ ],
180
+ status: { type: "requires-action", reason: "tool-calls" }
181
+ };
182
+ } else if (event.kind === "error") {
183
+ throw new Error(event.error);
184
+ }
185
+ }
186
+ yield terminal ?? {
187
+ content: [{ type: "text", text }],
188
+ status: { type: "complete", reason: "stop" }
189
+ };
190
+ }
191
+ };
192
+ }
193
+
194
+ // src/react/transport.ts
195
+ function parseFrame(frame) {
196
+ const data = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
197
+ if (data === "") return null;
198
+ try {
199
+ return JSON.parse(data);
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+ function httpChatStreamTransport(endpoint = "/api/chat") {
205
+ return async function* (request, signal) {
206
+ const res = await fetch(endpoint, {
207
+ method: "POST",
208
+ headers: {
209
+ "Content-Type": "application/json",
210
+ Accept: "text/event-stream"
211
+ },
212
+ body: JSON.stringify(request),
213
+ ...signal ? { signal } : {}
214
+ });
215
+ if (!res.ok || res.body === null) {
216
+ throw new Error(`Chat request failed (${res.status}): ${res.statusText}`);
217
+ }
218
+ const reader = res.body.getReader();
219
+ const decoder = new TextDecoder();
220
+ let buffer = "";
221
+ for (; ; ) {
222
+ const { done, value } = await reader.read();
223
+ if (done) break;
224
+ buffer += decoder.decode(value, { stream: true });
225
+ let sep = buffer.indexOf("\n\n");
226
+ while (sep !== -1) {
227
+ const frame = buffer.slice(0, sep);
228
+ buffer = buffer.slice(sep + 2);
229
+ const event = parseFrame(frame);
230
+ if (event !== null) yield event;
231
+ sep = buffer.indexOf("\n\n");
232
+ }
233
+ }
234
+ const tail = parseFrame(buffer);
235
+ if (tail !== null) yield tail;
236
+ };
237
+ }
238
+ function ChatRuntimeProvider({
239
+ children,
240
+ getCurrentGraph,
241
+ transport,
242
+ getRequestMeta
243
+ }) {
244
+ const adapter = react.useMemo(
245
+ () => createWorkflowChatAdapter({
246
+ transport: transport ?? httpChatStreamTransport(),
247
+ getCurrentGraph,
248
+ ...getRequestMeta ? { getRequestMeta } : {}
249
+ }),
250
+ [transport, getCurrentGraph, getRequestMeta]
251
+ );
252
+ const runtime = react$1.useLocalRuntime(adapter);
253
+ return /* @__PURE__ */ jsxRuntime.jsx(react$1.AssistantRuntimeProvider, { runtime, children });
254
+ }
255
+ function SparkleIcon({ size = 16, className }) {
256
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: [
257
+ /* @__PURE__ */ jsxRuntime.jsx(
258
+ "path",
259
+ {
260
+ d: "M12 3l1.9 4.6L18.5 9.5 13.9 11.4 12 16l-1.9-4.6L5.5 9.5l4.6-1.9L12 3z",
261
+ fill: "currentColor"
262
+ }
263
+ ),
264
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 14l.8 2 2 .8-2 .8-.8 2-.8-2-2-.8 2-.8.8-2z", fill: "currentColor", opacity: "0.7" })
265
+ ] });
266
+ }
267
+ function CloseIcon({ size = 16, className }) {
268
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) });
269
+ }
270
+ function SendIcon({ size = 16, className }) {
271
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
272
+ "path",
273
+ {
274
+ d: "M12 20V5M12 5l-6 6M12 5l6 6",
275
+ stroke: "currentColor",
276
+ strokeWidth: "2",
277
+ strokeLinecap: "round",
278
+ strokeLinejoin: "round"
279
+ }
280
+ ) });
281
+ }
282
+ function StopIcon({ size = 14, className }) {
283
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2", fill: "currentColor" }) });
284
+ }
285
+ function ComposeIcon({ size = 15, className }) {
286
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: [
287
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 20h9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
288
+ /* @__PURE__ */ jsxRuntime.jsx(
289
+ "path",
290
+ {
291
+ d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z",
292
+ stroke: "currentColor",
293
+ strokeWidth: "2",
294
+ strokeLinecap: "round",
295
+ strokeLinejoin: "round"
296
+ }
297
+ )
298
+ ] });
299
+ }
300
+ function CheckIcon({ size = 16, className }) {
301
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
302
+ "path",
303
+ {
304
+ d: "M20 6L9 17l-5-5",
305
+ stroke: "currentColor",
306
+ strokeWidth: "2.2",
307
+ strokeLinecap: "round",
308
+ strokeLinejoin: "round"
309
+ }
310
+ ) });
311
+ }
312
+ var DEFAULT_EXAMPLES = [
313
+ "Explain the workflow in a single sentence",
314
+ "Email ops when a new order arrives",
315
+ "On a schedule, fetch a URL and log the response",
316
+ "Add a 5-second delay before the final step"
317
+ ];
318
+ function EmptyState({ examples }) {
319
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-empty", children: [
320
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fg-aichat-empty__mark", children: /* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { size: 22 }) }),
321
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fg-aichat-empty__title", children: "Describe a workflow" }),
322
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fg-aichat-empty__hint", children: "Tell me what it should do \u2014 I\u2019ll draft it, and you apply it to the canvas." }),
323
+ examples.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-empty__examples", children: examples.map((text) => /* @__PURE__ */ jsxRuntime.jsx(
324
+ react$1.ThreadPrimitive.Suggestion,
325
+ {
326
+ className: "fg-aichat-chip",
327
+ prompt: text,
328
+ send: true,
329
+ children: text
330
+ },
331
+ text
332
+ )) })
333
+ ] });
334
+ }
335
+ function UserMessage() {
336
+ return /* @__PURE__ */ jsxRuntime.jsx(react$1.MessagePrimitive.Root, { className: "fg-aichat-msg fg-aichat-msg--user", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-msg__bubble", children: /* @__PURE__ */ jsxRuntime.jsx(react$1.MessagePrimitive.Parts, {}) }) });
337
+ }
338
+ var ThinkingEmpty = ({ status }) => {
339
+ if (status.type !== "running") return null;
340
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-thinking", role: "status", children: [
341
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fg-aichat-thinking__text", children: "Thinking" }),
342
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "fg-aichat-thinking__dots", "aria-hidden": "true", children: [
343
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
344
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
345
+ /* @__PURE__ */ jsxRuntime.jsx("span", {})
346
+ ] })
347
+ ] });
348
+ };
349
+ var ASSISTANT_PARTS = { Empty: ThinkingEmpty };
350
+ function AssistantMessage() {
351
+ return /* @__PURE__ */ jsxRuntime.jsx(react$1.MessagePrimitive.Root, { className: "fg-aichat-msg fg-aichat-msg--assistant", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-msg__body", children: [
352
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.MessagePrimitive.Parts, { components: ASSISTANT_PARTS }),
353
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.MessagePrimitive.Error, { children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ErrorPrimitive.Root, { className: "fg-aichat-error", children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ErrorPrimitive.Message, { className: "fg-aichat-error__msg" }) }) })
354
+ ] }) });
355
+ }
356
+ function Composer() {
357
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-composer", children: /* @__PURE__ */ jsxRuntime.jsxs(react$1.ComposerPrimitive.Root, { className: "fg-aichat-composer__field", children: [
358
+ /* @__PURE__ */ jsxRuntime.jsx(
359
+ react$1.ComposerPrimitive.Input,
360
+ {
361
+ className: "fg-aichat-composer__input",
362
+ placeholder: "Describe or edit a workflow\u2026",
363
+ rows: 1
364
+ }
365
+ ),
366
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.ThreadPrimitive.If, { running: false, children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ComposerPrimitive.Send, { className: "fg-aichat-send", "aria-label": "Send", children: /* @__PURE__ */ jsxRuntime.jsx(SendIcon, { size: 16 }) }) }),
367
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.ThreadPrimitive.If, { running: true, children: /* @__PURE__ */ jsxRuntime.jsx(
368
+ react$1.ComposerPrimitive.Cancel,
369
+ {
370
+ className: "fg-aichat-send fg-aichat-send--stop",
371
+ "aria-label": "Stop",
372
+ children: /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { size: 13 })
373
+ }
374
+ ) })
375
+ ] }) });
376
+ }
377
+ function Thread({ examples = DEFAULT_EXAMPLES } = {}) {
378
+ return /* @__PURE__ */ jsxRuntime.jsxs(react$1.ThreadPrimitive.Root, { className: "fg-aichat-thread", children: [
379
+ /* @__PURE__ */ jsxRuntime.jsxs(react$1.ThreadPrimitive.Viewport, { className: "fg-aichat-thread__viewport", children: [
380
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.ThreadPrimitive.Empty, { children: /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { examples }) }),
381
+ /* @__PURE__ */ jsxRuntime.jsx(
382
+ react$1.ThreadPrimitive.Messages,
383
+ {
384
+ components: { UserMessage, AssistantMessage }
385
+ }
386
+ )
387
+ ] }),
388
+ /* @__PURE__ */ jsxRuntime.jsx(Composer, {})
389
+ ] });
390
+ }
391
+ var ProposalToolUI = react$1.makeAssistantToolUI({
392
+ toolName: PROPOSE_TOOL_NAME,
393
+ display: "standalone",
394
+ render: function ProposalCard({ args, status, approval, respondToApproval }) {
395
+ const applyProposal2 = useApplyProposal();
396
+ if (args?.graph === void 0) {
397
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-card fg-aichat-card--drafting", children: "Drafting\u2026" });
398
+ }
399
+ const { graph, summary } = args;
400
+ const decided = approval?.approved;
401
+ if (decided === true) {
402
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-applied", children: [
403
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fg-aichat-applied__badge", children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { size: 13 }) }),
404
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fg-aichat-applied__summary", children: summary })
405
+ ] });
406
+ }
407
+ if (decided === false) {
408
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-dismissed", children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fg-aichat-dismissed__summary", children: summary }),
410
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fg-aichat-dismissed__tag", children: "Dismissed" })
411
+ ] });
412
+ }
413
+ const apply = () => {
414
+ applyProposal2(graph);
415
+ respondToApproval({ approved: true });
416
+ };
417
+ const dismiss = () => {
418
+ respondToApproval({ approved: false, reason: "Dismissed by user" });
419
+ };
420
+ const busy = status.type === "running";
421
+ const stepCount = graph.nodes.length;
422
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-card fg-aichat-card--proposal", children: [
423
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-card__label", children: "Proposed workflow" }),
424
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fg-aichat-card__summary", children: summary }),
425
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-card__meta", children: [
426
+ stepCount,
427
+ " step",
428
+ stepCount === 1 ? "" : "s"
429
+ ] }),
430
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-card__actions", children: [
431
+ /* @__PURE__ */ jsxRuntime.jsx(
432
+ "button",
433
+ {
434
+ type: "button",
435
+ className: "fg-aichat-btn fg-aichat-btn--ghost",
436
+ onClick: dismiss,
437
+ disabled: busy,
438
+ children: "Dismiss"
439
+ }
440
+ ),
441
+ /* @__PURE__ */ jsxRuntime.jsx(
442
+ "button",
443
+ {
444
+ type: "button",
445
+ className: "fg-aichat-btn fg-aichat-btn--primary",
446
+ onClick: apply,
447
+ disabled: busy,
448
+ children: "Apply to canvas"
449
+ }
450
+ )
451
+ ] })
452
+ ] });
453
+ }
454
+ });
455
+ var DEFAULT_HEADING = "Workflow AI";
456
+ var DEFAULT_SUBTITLE = "Draft & edit with a prompt";
457
+ function ChatHeader({
458
+ heading,
459
+ subtitle
460
+ }) {
461
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "fg-aichat-dock__head", children: [
462
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fg-aichat-dock__brand", children: [
463
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fg-aichat-dock__mark", children: /* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { size: 15 }) }),
464
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
465
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-dock__title", children: heading }),
466
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fg-aichat-dock__subtitle", children: subtitle })
467
+ ] })
468
+ ] }),
469
+ /* @__PURE__ */ jsxRuntime.jsxs(react$1.ThreadListPrimitive.New, { className: "fg-aichat-newchat", children: [
470
+ /* @__PURE__ */ jsxRuntime.jsx(ComposeIcon, { size: 14 }),
471
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "New chat" })
472
+ ] })
473
+ ] });
474
+ }
475
+ function WorkflowChat({
476
+ currentGraph,
477
+ applyGraph,
478
+ transport,
479
+ endpoint,
480
+ heading = DEFAULT_HEADING,
481
+ subtitle = DEFAULT_SUBTITLE,
482
+ examples,
483
+ layout,
484
+ actor,
485
+ context
486
+ }) {
487
+ const graphRef = react.useRef(currentGraph);
488
+ graphRef.current = currentGraph;
489
+ const applyRef = react.useRef(applyGraph);
490
+ applyRef.current = applyGraph;
491
+ const layoutRef = react.useRef(layout);
492
+ layoutRef.current = layout;
493
+ const metaRef = react.useRef({});
494
+ metaRef.current = {
495
+ ...actor !== void 0 ? { actor } : {},
496
+ ...context !== void 0 ? { context } : {}
497
+ };
498
+ const bridge = react.useMemo(
499
+ () => ({
500
+ getCurrentGraph: () => graphRef.current,
501
+ applyGraph: (graph) => applyRef.current(graph),
502
+ layout: (proposed, current) => (layoutRef.current ?? layoutProposal)(proposed, current)
503
+ }),
504
+ []
505
+ );
506
+ const resolvedTransport = react.useMemo(
507
+ () => transport ?? httpChatStreamTransport(endpoint),
508
+ [transport, endpoint]
509
+ );
510
+ const getCurrentGraph = react.useCallback(() => graphRef.current, []);
511
+ const getRequestMeta = react.useCallback(() => metaRef.current, []);
512
+ return /* @__PURE__ */ jsxRuntime.jsx(BridgeProvider, { bridge, children: /* @__PURE__ */ jsxRuntime.jsxs(
513
+ ChatRuntimeProvider,
514
+ {
515
+ getCurrentGraph,
516
+ transport: resolvedTransport,
517
+ getRequestMeta,
518
+ children: [
519
+ /* @__PURE__ */ jsxRuntime.jsxs(react$1.AssistantModalPrimitive.Root, { children: [
520
+ /* @__PURE__ */ jsxRuntime.jsx(react$1.AssistantModalPrimitive.Anchor, { className: "fg-aichat-anchor", children: /* @__PURE__ */ jsxRuntime.jsxs(
521
+ react$1.AssistantModalPrimitive.Trigger,
522
+ {
523
+ className: "fg-aichat-launcher",
524
+ "aria-label": "Toggle workflow AI",
525
+ children: [
526
+ /* @__PURE__ */ jsxRuntime.jsx(
527
+ SparkleIcon,
528
+ {
529
+ size: 20,
530
+ className: "fg-aichat-launcher__icon fg-aichat-launcher__icon--open"
531
+ }
532
+ ),
533
+ /* @__PURE__ */ jsxRuntime.jsx(
534
+ CloseIcon,
535
+ {
536
+ size: 20,
537
+ className: "fg-aichat-launcher__icon fg-aichat-launcher__icon--close"
538
+ }
539
+ )
540
+ ]
541
+ }
542
+ ) }),
543
+ /* @__PURE__ */ jsxRuntime.jsxs(
544
+ react$1.AssistantModalPrimitive.Content,
545
+ {
546
+ className: "fg-aichat-panel",
547
+ side: "top",
548
+ align: "end",
549
+ sideOffset: 12,
550
+ children: [
551
+ /* @__PURE__ */ jsxRuntime.jsx(ChatHeader, { heading, subtitle }),
552
+ /* @__PURE__ */ jsxRuntime.jsx(Thread, { ...examples !== void 0 ? { examples } : {} })
553
+ ]
554
+ }
555
+ )
556
+ ] }),
557
+ /* @__PURE__ */ jsxRuntime.jsx(ProposalToolUI, {})
558
+ ]
559
+ }
560
+ ) });
561
+ }
562
+
563
+ exports.ChatRuntimeProvider = ChatRuntimeProvider;
564
+ exports.WorkflowChat = WorkflowChat;
565
+ exports.createWorkflowChatAdapter = createWorkflowChatAdapter;
566
+ exports.httpChatStreamTransport = httpChatStreamTransport;
567
+ exports.layoutProposal = layoutProposal;