@burdenoff/microfe-botmanor 2026.1006.1 → 2026.1008.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/botlit/BotlitRoutes.js +58 -146
- package/dist/botlit/BotlitRoutes.js.map +1 -1
- package/dist/botlit/BotmanorRoot.js.map +1 -1
- package/dist/botlit/BotmanorRootInner.js +39 -67
- package/dist/botlit/BotmanorRootInner.js.map +1 -1
- package/dist/botlit/hooks/useAgents.js +12 -12
- package/dist/botlit/hooks/useAgents.js.map +1 -1
- package/dist/botlit/hooks/useBotWorkflow.js +56 -0
- package/dist/botlit/hooks/useBotWorkflow.js.map +1 -0
- package/dist/botlit/hooks/useKnowledgeBases.js +1 -0
- package/dist/botlit/hooks/useKnowledgeBases.js.map +1 -1
- package/dist/botlit/hooks/useLLMProviders.js +2 -2
- package/dist/botlit/hooks/useLLMProviders.js.map +1 -1
- package/dist/botlit/pages/BotWorkflowTab.js +33 -33
- package/dist/botlit/pages/BotWorkflowTab.js.map +1 -1
- package/dist/botlit/providers/BotlitProvider.js +30 -26
- package/dist/botlit/providers/BotlitProvider.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/generated/wspace-operations.js +11 -11
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/package.json +1 -3
|
@@ -5,9 +5,9 @@ import { Card as r, CardContent as i, CardDescription as a, CardHeader as o, Car
|
|
|
5
5
|
import { Button as c } from "../components/Button.js";
|
|
6
6
|
import { Badge as l, StatusBadge as u } from "../components/Badge.js";
|
|
7
7
|
import { EmptyState as d } from "../components/EmptyState.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { useBotWorkflowDefinition as f, useCreateBotWorkflow as p } from "../hooks/useBotWorkflow.js";
|
|
9
|
+
import { useCallback as m, useState as h } from "react";
|
|
10
|
+
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
11
11
|
import { ExternalLink as v, Sparkles as y, Unlink as b, Workflow as x } from "lucide-react";
|
|
12
12
|
import { toast as S } from "sonner";
|
|
13
13
|
//#region src/botlit/pages/BotWorkflowTab.tsx
|
|
@@ -82,9 +82,9 @@ function j(e, t) {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
var M = ({ botId: w, botName: T, workflowId: E, onWorkflowChanged: D }) => {
|
|
85
|
-
let O = e(), { navigateTo: k } = t(), { createWorkflow: M } =
|
|
85
|
+
let O = e(), { navigateTo: k } = t(), { createWorkflow: M } = p(), [N] = n(), [P, F] = h(!1), { workflow: I, loading: L } = f(void 0, E ?? void 0), R = m((e, t) => {
|
|
86
86
|
k(`workflows/workflow/${e}/${t || C}`);
|
|
87
|
-
}, [k]), z =
|
|
87
|
+
}, [k]), z = m(async () => {
|
|
88
88
|
F(!0);
|
|
89
89
|
try {
|
|
90
90
|
let e = A(), t = `${T} workflow`, n = await M({
|
|
@@ -113,7 +113,7 @@ var M = ({ botId: w, botName: T, workflowId: E, onWorkflowChanged: D }) => {
|
|
|
113
113
|
R,
|
|
114
114
|
O,
|
|
115
115
|
N
|
|
116
|
-
]), B =
|
|
116
|
+
]), B = m(async () => {
|
|
117
117
|
if (confirm(O("botlit.botDetail.workflow.unbindConfirm", "Unbind this workflow from the bot? The workflow itself is not deleted."))) {
|
|
118
118
|
F(!0);
|
|
119
119
|
try {
|
|
@@ -134,69 +134,69 @@ var M = ({ botId: w, botName: T, workflowId: E, onWorkflowChanged: D }) => {
|
|
|
134
134
|
O,
|
|
135
135
|
N
|
|
136
136
|
]);
|
|
137
|
-
return E ? /* @__PURE__ */
|
|
137
|
+
return E ? /* @__PURE__ */ g("div", {
|
|
138
138
|
className: "space-y-6",
|
|
139
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ _(r, { children: [/* @__PURE__ */ _(o, { children: [/* @__PURE__ */ _(s, {
|
|
140
140
|
className: "flex items-center gap-2",
|
|
141
|
-
children: [/* @__PURE__ */
|
|
142
|
-
}), /* @__PURE__ */
|
|
141
|
+
children: [/* @__PURE__ */ g(x, { size: 18 }), O("botlit.botDetail.workflow.bound.title", "Bound workflow")]
|
|
142
|
+
}), /* @__PURE__ */ g(a, { children: O("botlit.botDetail.workflow.bound.description", "This workflow defines what the bot does when it is invoked.") })] }), /* @__PURE__ */ _(i, {
|
|
143
143
|
className: "space-y-6",
|
|
144
|
-
children: [L ? /* @__PURE__ */
|
|
144
|
+
children: [L ? /* @__PURE__ */ g("div", { className: "h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer" }) : I ? /* @__PURE__ */ _("div", {
|
|
145
145
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
146
146
|
children: [
|
|
147
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
148
148
|
className: "text-sm text-text-secondary",
|
|
149
149
|
children: O("botlit.botDetail.workflow.fields.name", "Name")
|
|
150
|
-
}), /* @__PURE__ */
|
|
150
|
+
}), /* @__PURE__ */ g("p", {
|
|
151
151
|
className: "font-medium text-text-primary",
|
|
152
152
|
children: I.name
|
|
153
153
|
})] }),
|
|
154
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
155
155
|
className: "text-sm text-text-secondary",
|
|
156
156
|
children: O("botlit.botDetail.workflow.fields.key", "Key")
|
|
157
|
-
}), /* @__PURE__ */
|
|
157
|
+
}), /* @__PURE__ */ g("p", {
|
|
158
158
|
className: "mt-1 break-all font-mono text-sm text-text-primary",
|
|
159
159
|
children: I.workflowKey
|
|
160
160
|
})] }),
|
|
161
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
162
162
|
className: "text-sm text-text-secondary",
|
|
163
163
|
children: O("botlit.botDetail.workflow.fields.version", "Version")
|
|
164
|
-
}), /* @__PURE__ */
|
|
164
|
+
}), /* @__PURE__ */ g(l, {
|
|
165
165
|
variant: "outline",
|
|
166
166
|
className: "mt-1",
|
|
167
167
|
children: I.version
|
|
168
168
|
})] }),
|
|
169
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
170
170
|
className: "text-sm text-text-secondary",
|
|
171
171
|
children: O("botlit.botDetail.workflow.fields.status", "Status")
|
|
172
|
-
}), /* @__PURE__ */
|
|
172
|
+
}), /* @__PURE__ */ g("div", {
|
|
173
173
|
className: "mt-1",
|
|
174
|
-
children: /* @__PURE__ */
|
|
174
|
+
children: /* @__PURE__ */ g(u, { status: I.status })
|
|
175
175
|
})] })
|
|
176
176
|
]
|
|
177
|
-
}) : /* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
177
|
+
}) : /* @__PURE__ */ _("div", { children: [
|
|
178
|
+
/* @__PURE__ */ g("p", {
|
|
179
179
|
className: "text-sm text-text-secondary",
|
|
180
180
|
children: O("botlit.botDetail.workflow.fields.workflowId", "Workflow ID")
|
|
181
181
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ g("p", {
|
|
183
183
|
className: "mt-1 break-all font-mono text-sm text-text-primary",
|
|
184
184
|
children: E
|
|
185
185
|
}),
|
|
186
|
-
/* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ g("p", {
|
|
187
187
|
className: "mt-2 text-sm text-text-secondary",
|
|
188
188
|
children: O("botlit.botDetail.workflow.unresolved", "This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.")
|
|
189
189
|
})
|
|
190
|
-
] }), /* @__PURE__ */
|
|
190
|
+
] }), /* @__PURE__ */ _("div", {
|
|
191
191
|
className: "flex flex-wrap gap-2",
|
|
192
|
-
children: [I && /* @__PURE__ */
|
|
193
|
-
leftIcon: /* @__PURE__ */
|
|
192
|
+
children: [I && /* @__PURE__ */ g(c, {
|
|
193
|
+
leftIcon: /* @__PURE__ */ g(v, { size: 16 }),
|
|
194
194
|
disabled: P,
|
|
195
195
|
onClick: () => R(I.workflowKey, I.version),
|
|
196
196
|
children: O("botlit.botDetail.workflow.actions.openBuilder", "Open in builder")
|
|
197
|
-
}), /* @__PURE__ */
|
|
197
|
+
}), /* @__PURE__ */ g(c, {
|
|
198
198
|
variant: "outline",
|
|
199
|
-
leftIcon: /* @__PURE__ */
|
|
199
|
+
leftIcon: /* @__PURE__ */ g(b, { size: 16 }),
|
|
200
200
|
isLoading: P,
|
|
201
201
|
disabled: P,
|
|
202
202
|
onClick: () => void B(),
|
|
@@ -204,14 +204,14 @@ var M = ({ botId: w, botName: T, workflowId: E, onWorkflowChanged: D }) => {
|
|
|
204
204
|
})]
|
|
205
205
|
})]
|
|
206
206
|
})] })
|
|
207
|
-
}) : /* @__PURE__ */
|
|
207
|
+
}) : /* @__PURE__ */ g(r, { children: /* @__PURE__ */ g(i, {
|
|
208
208
|
className: "pt-6",
|
|
209
|
-
children: /* @__PURE__ */
|
|
209
|
+
children: /* @__PURE__ */ g(d, {
|
|
210
210
|
illustration: "empty-generic",
|
|
211
211
|
title: O("botlit.botDetail.workflow.empty.title", "Design this bot as a workflow"),
|
|
212
212
|
description: O("botlit.botDetail.workflow.empty.description", "A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you."),
|
|
213
|
-
action: /* @__PURE__ */
|
|
214
|
-
leftIcon: /* @__PURE__ */
|
|
213
|
+
action: /* @__PURE__ */ g(c, {
|
|
214
|
+
leftIcon: /* @__PURE__ */ g(y, { size: 16 }),
|
|
215
215
|
isLoading: P,
|
|
216
216
|
disabled: P,
|
|
217
217
|
onClick: () => void z(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BotWorkflowTab.js","names":[],"sources":["../../../src/botlit/pages/BotWorkflowTab.tsx"],"sourcesContent":["/**\n * BotWorkflowTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Workflow\" tab.\n *\n * A bot's behaviour is designed as a FluidGrids workflow. `BotApp.workflowId`\n * holds the binding (validated server-side by `assertWorkflowBindingsResolvable`\n * on wspace-botlit-svc), and the FluidGrids builder is already mounted INSIDE\n * BotManor at `workflows/workflow/:id/:version` — so this tab never leaves the\n * product.\n *\n * Flow:\n * 1. Bound → resolve the definition by id (`useWorkflowDefinition`), show\n * name / key / version / status, \"Open in builder\", \"Unbind\".\n * A binding that no longer resolves falls back to the raw id so\n * the user can still clear it.\n * 2. Not bound → zero state whose primary action creates a seeded workflow\n * definition, binds it via `updateBotApp(workflowId)` and drops\n * the user straight into the embedded builder.\n *\n * IMPORTANT — seed graph shape. `BotlitFluidGridsBridge.extractTriggerNodeIds`\n * reads nodes from `definitionJson.data.nodes` (NOT the top level) and treats a\n * node as a trigger when its `type` is `trigger`/`trigger:*`/`*_trigger`, OR its\n * `kind[]` / `data.nodeData.kind[]` contains `trigger`. The seed below uses the\n * workflow service's own BUILTIN manual trigger (`internal:api`, \"Trigger\n * Manually\") — the node the executor resolves as the entry point — and carries\n * `kind: ['trigger', ...]` in both places, so a freshly designed bot is\n * immediately runnable instead of failing with `WORKFLOW_NO_TRIGGER_NODE`.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useState } from 'react';\nimport { ExternalLink, Sparkles, Unlink, Workflow } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useCreateWorkflow, useWorkflowDefinition } from '@burdenoff/fluidgrids-fe-sdk';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { useUpdateBot } from '../hooks/useBots';\nimport { useBotlit } from '../providers/BotlitProvider';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Seed graph\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** First version minted for a bot's workflow — matches the builder's own default. */\nconst INITIAL_VERSION = 'v1';\n\n/**\n * BUILTIN \"Trigger Manually\" node (`MANUAL_TRIGGER_MANIFEST` in\n * wspace-workflow-svc). The executor resolves this type as a workflow's entry\n * node, and the builder renders it from the seeded node registry.\n */\nconst MANUAL_TRIGGER_NODE_TYPE = 'internal:api';\nconst MANUAL_TRIGGER_NODE_VERSION = '1.0.0';\nconst MANUAL_TRIGGER_OPERATION = 'onManualTrigger';\nconst MANUAL_TRIGGER_NAME = 'Trigger Manually';\nconst MANUAL_TRIGGER_DESCRIPTION =\n \"Runs the flow when you click 'Test workflow', or when this bot is invoked.\";\n\n/** `kind[]` the BotManor bridge scans for when classifying a node as a trigger. */\nconst MANUAL_TRIGGER_KIND = ['trigger', 'manual', 'entry'];\n\n/** Node shape written into `definitionJson.data.nodes`. */\ntype SeedNode = {\n id: string;\n type: string;\n position: { x: number; y: number };\n /** Top-level kind — the bridge's second trigger-detection path. */\n kind: string[];\n data: {\n operation_name: string;\n operation_type: string;\n version: string;\n nodeData: {\n nodeType: string;\n name: string;\n description: string;\n version: string;\n /** The bridge's third trigger-detection path. */\n kind: string[];\n operations: { name: string; type: string; role: string; description: string }[];\n };\n args: {\n context: { node: { id: string; type: string; kind: string[] } };\n state: Record<string, never>;\n credentials: never[];\n };\n };\n};\n\n/**\n * `definitionJson` document. Mirrors what the builder itself persists\n * (`useSaveWorkflow`) so the graph round-trips through the builder unchanged.\n */\ntype SeedDefinition = {\n key: string;\n name: string;\n description: string;\n workflow_type: string;\n data: {\n id: string;\n workflowKey: string;\n version: string;\n name: string;\n nodes: SeedNode[];\n edges: never[];\n sidebar: { active: string; panels: { nodeProperties: { selectedNode: null } } };\n stickyNotes: never[];\n };\n meta: { creator: string; version: string };\n tags: string[];\n};\n\n/**\n * Generate an opaque id. `crypto.randomUUID` is unavailable on insecure origins\n * and in some test environments, so fall back to a time+random token rather than\n * throwing mid-create.\n */\nfunction generateId(): string {\n const webCrypto = globalThis.crypto as Crypto | undefined;\n if (webCrypto && typeof webCrypto.randomUUID === 'function') {\n return webCrypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Build the starter graph: one manual trigger, nothing else. */\nfunction buildSeedDefinition(workflowKey: string, name: string): SeedDefinition {\n const triggerNode: SeedNode = {\n id: generateId(),\n type: MANUAL_TRIGGER_NODE_TYPE,\n position: { x: 160, y: 160 },\n kind: MANUAL_TRIGGER_KIND,\n data: {\n operation_name: MANUAL_TRIGGER_OPERATION,\n operation_type: 'trigger',\n version: MANUAL_TRIGGER_NODE_VERSION,\n nodeData: {\n nodeType: MANUAL_TRIGGER_NODE_TYPE,\n name: MANUAL_TRIGGER_NAME,\n description: MANUAL_TRIGGER_DESCRIPTION,\n version: MANUAL_TRIGGER_NODE_VERSION,\n kind: MANUAL_TRIGGER_KIND,\n operations: [\n {\n name: MANUAL_TRIGGER_OPERATION,\n type: 'trigger',\n role: 'trigger',\n description: MANUAL_TRIGGER_DESCRIPTION,\n },\n ],\n },\n args: {\n context: {\n node: {\n id: MANUAL_TRIGGER_NODE_TYPE,\n type: MANUAL_TRIGGER_NODE_TYPE,\n kind: MANUAL_TRIGGER_KIND,\n },\n },\n state: {},\n credentials: [],\n },\n },\n };\n\n return {\n key: workflowKey,\n name,\n description: name,\n workflow_type: 'query-conditional',\n data: {\n id: workflowKey,\n workflowKey,\n version: INITIAL_VERSION,\n name,\n nodes: [triggerNode],\n edges: [],\n sidebar: { active: 'store-browse', panels: { nodeProperties: { selectedNode: null } } },\n stickyNotes: [],\n },\n meta: { creator: 'botlit_bot_design', version: INITIAL_VERSION },\n tags: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotWorkflowTabProps {\n botId: string;\n botName: string;\n /** `BotApp.workflowId` — the bound workflow definition id, if any. */\n workflowId: string | null | undefined;\n /** Refetch the bot so the tab reflects the new binding. */\n onWorkflowChanged: () => Promise<void> | void;\n}\n\nexport const BotWorkflowTab: FC<BotWorkflowTabProps> = ({\n botId,\n botName,\n workflowId,\n onWorkflowChanged,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n const { createWorkflow } = useCreateWorkflow();\n const [updateBot] = useUpdateBot();\n const [busy, setBusy] = useState(false);\n\n // Resolve by id — `BotApp.workflowId` stores the definition's opaque id, not\n // its workflowKey (which is what the builder route takes). The hook refetches\n // on its own when the bound id changes, so binding/unbinding only has to\n // refresh the bot.\n const { workflow, loading: workflowLoading } = useWorkflowDefinition(\n undefined,\n workflowId ?? undefined\n );\n\n const openBuilder = useCallback(\n (workflowKey: string, version: string) => {\n navigateTo(`workflows/workflow/${workflowKey}/${version || INITIAL_VERSION}`);\n },\n [navigateTo]\n );\n\n const handleDesign = useCallback(async () => {\n setBusy(true);\n try {\n const workflowKey = generateId();\n const name = `${botName} workflow`;\n const created = await createWorkflow({\n workflowKey,\n version: INITIAL_VERSION,\n name,\n description: t(\n 'botlit.botDetail.workflow.createdDescription',\n 'Behaviour designed for this BotManor bot.'\n ),\n definitionJson: buildSeedDefinition(workflowKey, name),\n });\n\n if (!created) {\n throw new Error(\n t('botlit.botDetail.workflow.createError', 'Failed to create the workflow')\n );\n }\n\n await updateBot({ botId, input: { workflowId: created.id } });\n await onWorkflowChanged();\n toast.success(\n t('botlit.botDetail.workflow.createSuccess', 'Workflow created and bound to this bot')\n );\n openBuilder(created.workflowKey, created.version);\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.createError', 'Failed to create the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, botName, createWorkflow, onWorkflowChanged, openBuilder, t, updateBot]);\n\n const handleUnbind = useCallback(async () => {\n if (\n !confirm(\n t(\n 'botlit.botDetail.workflow.unbindConfirm',\n 'Unbind this workflow from the bot? The workflow itself is not deleted.'\n )\n )\n ) {\n return;\n }\n setBusy(true);\n try {\n await updateBot({ botId, input: { workflowId: null } });\n await onWorkflowChanged();\n toast.success(t('botlit.botDetail.workflow.unbindSuccess', 'Workflow unbound'));\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.unbindError', 'Failed to unbind the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, onWorkflowChanged, t, updateBot]);\n\n // ── Not bound ─────────────────────────────────────────────────────────────\n if (!workflowId) {\n return (\n <Card>\n <CardContent className=\"pt-6\">\n <EmptyState\n illustration=\"empty-generic\"\n title={t('botlit.botDetail.workflow.empty.title', 'Design this bot as a workflow')}\n description={t(\n 'botlit.botDetail.workflow.empty.description',\n \"A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you.\"\n )}\n action={\n <Button\n leftIcon={<Sparkles size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleDesign()}\n >\n {t('botlit.botDetail.workflow.empty.action', 'Design in workflow builder')}\n </Button>\n }\n />\n </CardContent>\n </Card>\n );\n }\n\n // ── Bound ─────────────────────────────────────────────────────────────────\n return (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Workflow size={18} />\n {t('botlit.botDetail.workflow.bound.title', 'Bound workflow')}\n </CardTitle>\n <CardDescription>\n {t(\n 'botlit.botDetail.workflow.bound.description',\n 'This workflow defines what the bot does when it is invoked.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {workflowLoading ? (\n <div className=\"h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ) : workflow ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.name', 'Name')}\n </p>\n <p className=\"font-medium text-text-primary\">{workflow.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.key', 'Key')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">\n {workflow.workflowKey}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.version', 'Version')}\n </p>\n <Badge variant=\"outline\" className=\"mt-1\">\n {workflow.version}\n </Badge>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.status', 'Status')}\n </p>\n <div className=\"mt-1\">\n <StatusBadge status={workflow.status} />\n </div>\n </div>\n </div>\n ) : (\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.workflowId', 'Workflow ID')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">{workflowId}</p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {t(\n 'botlit.botDetail.workflow.unresolved',\n 'This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.'\n )}\n </p>\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {workflow && (\n <Button\n leftIcon={<ExternalLink size={16} />}\n disabled={busy}\n onClick={() => openBuilder(workflow.workflowKey, workflow.version)}\n >\n {t('botlit.botDetail.workflow.actions.openBuilder', 'Open in builder')}\n </Button>\n )}\n <Button\n variant=\"outline\"\n leftIcon={<Unlink size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleUnbind()}\n >\n {t('botlit.botDetail.workflow.actions.unbind', 'Unbind')}\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BotWorkflowTab.js","names":[],"sources":["../../../src/botlit/pages/BotWorkflowTab.tsx"],"sourcesContent":["/**\n * BotWorkflowTab\n * @module botlit/pages\n *\n * Shown inside BotDetailPage → \"Workflow\" tab.\n *\n * A bot's behaviour is designed as a FluidGrids workflow. `BotApp.workflowId`\n * holds the binding (validated server-side by `assertWorkflowBindingsResolvable`\n * on wspace-botlit-svc), and the FluidGrids builder is already mounted INSIDE\n * BotManor at `workflows/workflow/:id/:version` — so this tab never leaves the\n * product.\n *\n * Flow:\n * 1. Bound → resolve the definition by id (`useWorkflowDefinition`), show\n * name / key / version / status, \"Open in builder\", \"Unbind\".\n * A binding that no longer resolves falls back to the raw id so\n * the user can still clear it.\n * 2. Not bound → zero state whose primary action creates a seeded workflow\n * definition, binds it via `updateBotApp(workflowId)` and drops\n * the user straight into the embedded builder.\n *\n * IMPORTANT — seed graph shape. `BotlitFluidGridsBridge.extractTriggerNodeIds`\n * reads nodes from `definitionJson.data.nodes` (NOT the top level) and treats a\n * node as a trigger when its `type` is `trigger`/`trigger:*`/`*_trigger`, OR its\n * `kind[]` / `data.nodeData.kind[]` contains `trigger`. The seed below uses the\n * workflow service's own BUILTIN manual trigger (`internal:api`, \"Trigger\n * Manually\") — the node the executor resolves as the entry point — and carries\n * `kind: ['trigger', ...]` in both places, so a freshly designed bot is\n * immediately runnable instead of failing with `WORKFLOW_NO_TRIGGER_NODE`.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useState } from 'react';\nimport { ExternalLink, Sparkles, Unlink, Workflow } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useCreateBotWorkflow as useCreateWorkflow,\n useBotWorkflowDefinition as useWorkflowDefinition,\n} from '../hooks/useBotWorkflow';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge, StatusBadge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { useUpdateBot } from '../hooks/useBots';\nimport { useBotlit } from '../providers/BotlitProvider';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Seed graph\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** First version minted for a bot's workflow — matches the builder's own default. */\nconst INITIAL_VERSION = 'v1';\n\n/**\n * BUILTIN \"Trigger Manually\" node (`MANUAL_TRIGGER_MANIFEST` in\n * wspace-workflow-svc). The executor resolves this type as a workflow's entry\n * node, and the builder renders it from the seeded node registry.\n */\nconst MANUAL_TRIGGER_NODE_TYPE = 'internal:api';\nconst MANUAL_TRIGGER_NODE_VERSION = '1.0.0';\nconst MANUAL_TRIGGER_OPERATION = 'onManualTrigger';\nconst MANUAL_TRIGGER_NAME = 'Trigger Manually';\nconst MANUAL_TRIGGER_DESCRIPTION =\n \"Runs the flow when you click 'Test workflow', or when this bot is invoked.\";\n\n/** `kind[]` the BotManor bridge scans for when classifying a node as a trigger. */\nconst MANUAL_TRIGGER_KIND = ['trigger', 'manual', 'entry'];\n\n/** Node shape written into `definitionJson.data.nodes`. */\ntype SeedNode = {\n id: string;\n type: string;\n position: { x: number; y: number };\n /** Top-level kind — the bridge's second trigger-detection path. */\n kind: string[];\n data: {\n operation_name: string;\n operation_type: string;\n version: string;\n nodeData: {\n nodeType: string;\n name: string;\n description: string;\n version: string;\n /** The bridge's third trigger-detection path. */\n kind: string[];\n operations: { name: string; type: string; role: string; description: string }[];\n };\n args: {\n context: { node: { id: string; type: string; kind: string[] } };\n state: Record<string, never>;\n credentials: never[];\n };\n };\n};\n\n/**\n * `definitionJson` document. Mirrors what the builder itself persists\n * (`useSaveWorkflow`) so the graph round-trips through the builder unchanged.\n */\ntype SeedDefinition = {\n key: string;\n name: string;\n description: string;\n workflow_type: string;\n data: {\n id: string;\n workflowKey: string;\n version: string;\n name: string;\n nodes: SeedNode[];\n edges: never[];\n sidebar: { active: string; panels: { nodeProperties: { selectedNode: null } } };\n stickyNotes: never[];\n };\n meta: { creator: string; version: string };\n tags: string[];\n};\n\n/**\n * Generate an opaque id. `crypto.randomUUID` is unavailable on insecure origins\n * and in some test environments, so fall back to a time+random token rather than\n * throwing mid-create.\n */\nfunction generateId(): string {\n const webCrypto = globalThis.crypto as Crypto | undefined;\n if (webCrypto && typeof webCrypto.randomUUID === 'function') {\n return webCrypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Build the starter graph: one manual trigger, nothing else. */\nfunction buildSeedDefinition(workflowKey: string, name: string): SeedDefinition {\n const triggerNode: SeedNode = {\n id: generateId(),\n type: MANUAL_TRIGGER_NODE_TYPE,\n position: { x: 160, y: 160 },\n kind: MANUAL_TRIGGER_KIND,\n data: {\n operation_name: MANUAL_TRIGGER_OPERATION,\n operation_type: 'trigger',\n version: MANUAL_TRIGGER_NODE_VERSION,\n nodeData: {\n nodeType: MANUAL_TRIGGER_NODE_TYPE,\n name: MANUAL_TRIGGER_NAME,\n description: MANUAL_TRIGGER_DESCRIPTION,\n version: MANUAL_TRIGGER_NODE_VERSION,\n kind: MANUAL_TRIGGER_KIND,\n operations: [\n {\n name: MANUAL_TRIGGER_OPERATION,\n type: 'trigger',\n role: 'trigger',\n description: MANUAL_TRIGGER_DESCRIPTION,\n },\n ],\n },\n args: {\n context: {\n node: {\n id: MANUAL_TRIGGER_NODE_TYPE,\n type: MANUAL_TRIGGER_NODE_TYPE,\n kind: MANUAL_TRIGGER_KIND,\n },\n },\n state: {},\n credentials: [],\n },\n },\n };\n\n return {\n key: workflowKey,\n name,\n description: name,\n workflow_type: 'query-conditional',\n data: {\n id: workflowKey,\n workflowKey,\n version: INITIAL_VERSION,\n name,\n nodes: [triggerNode],\n edges: [],\n sidebar: { active: 'store-browse', panels: { nodeProperties: { selectedNode: null } } },\n stickyNotes: [],\n },\n meta: { creator: 'botlit_bot_design', version: INITIAL_VERSION },\n tags: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface BotWorkflowTabProps {\n botId: string;\n botName: string;\n /** `BotApp.workflowId` — the bound workflow definition id, if any. */\n workflowId: string | null | undefined;\n /** Refetch the bot so the tab reflects the new binding. */\n onWorkflowChanged: () => Promise<void> | void;\n}\n\nexport const BotWorkflowTab: FC<BotWorkflowTabProps> = ({\n botId,\n botName,\n workflowId,\n onWorkflowChanged,\n}) => {\n const t = useTr();\n const { navigateTo } = useBotlit();\n const { createWorkflow } = useCreateWorkflow();\n const [updateBot] = useUpdateBot();\n const [busy, setBusy] = useState(false);\n\n // Resolve by id — `BotApp.workflowId` stores the definition's opaque id, not\n // its workflowKey (which is what the builder route takes). The hook refetches\n // on its own when the bound id changes, so binding/unbinding only has to\n // refresh the bot.\n const { workflow, loading: workflowLoading } = useWorkflowDefinition(\n undefined,\n workflowId ?? undefined\n );\n\n const openBuilder = useCallback(\n (workflowKey: string, version: string) => {\n navigateTo(`workflows/workflow/${workflowKey}/${version || INITIAL_VERSION}`);\n },\n [navigateTo]\n );\n\n const handleDesign = useCallback(async () => {\n setBusy(true);\n try {\n const workflowKey = generateId();\n const name = `${botName} workflow`;\n const created = await createWorkflow({\n workflowKey,\n version: INITIAL_VERSION,\n name,\n description: t(\n 'botlit.botDetail.workflow.createdDescription',\n 'Behaviour designed for this BotManor bot.'\n ),\n definitionJson: buildSeedDefinition(workflowKey, name),\n });\n\n if (!created) {\n throw new Error(\n t('botlit.botDetail.workflow.createError', 'Failed to create the workflow')\n );\n }\n\n await updateBot({ botId, input: { workflowId: created.id } });\n await onWorkflowChanged();\n toast.success(\n t('botlit.botDetail.workflow.createSuccess', 'Workflow created and bound to this bot')\n );\n openBuilder(created.workflowKey, created.version);\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.createError', 'Failed to create the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, botName, createWorkflow, onWorkflowChanged, openBuilder, t, updateBot]);\n\n const handleUnbind = useCallback(async () => {\n if (\n !confirm(\n t(\n 'botlit.botDetail.workflow.unbindConfirm',\n 'Unbind this workflow from the bot? The workflow itself is not deleted.'\n )\n )\n ) {\n return;\n }\n setBusy(true);\n try {\n await updateBot({ botId, input: { workflowId: null } });\n await onWorkflowChanged();\n toast.success(t('botlit.botDetail.workflow.unbindSuccess', 'Workflow unbound'));\n } catch (err: unknown) {\n const message =\n err instanceof Error\n ? err.message\n : t('botlit.botDetail.workflow.unbindError', 'Failed to unbind the workflow');\n toast.error(message);\n } finally {\n setBusy(false);\n }\n }, [botId, onWorkflowChanged, t, updateBot]);\n\n // ── Not bound ─────────────────────────────────────────────────────────────\n if (!workflowId) {\n return (\n <Card>\n <CardContent className=\"pt-6\">\n <EmptyState\n illustration=\"empty-generic\"\n title={t('botlit.botDetail.workflow.empty.title', 'Design this bot as a workflow')}\n description={t(\n 'botlit.botDetail.workflow.empty.description',\n \"A bot's behaviour is designed as a FluidGrids workflow — the visual canvas where you wire triggers, agents, tools and conditions together. Start one and we will create it, bind it to this bot and open the builder for you.\"\n )}\n action={\n <Button\n leftIcon={<Sparkles size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleDesign()}\n >\n {t('botlit.botDetail.workflow.empty.action', 'Design in workflow builder')}\n </Button>\n }\n />\n </CardContent>\n </Card>\n );\n }\n\n // ── Bound ─────────────────────────────────────────────────────────────────\n return (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Workflow size={18} />\n {t('botlit.botDetail.workflow.bound.title', 'Bound workflow')}\n </CardTitle>\n <CardDescription>\n {t(\n 'botlit.botDetail.workflow.bound.description',\n 'This workflow defines what the bot does when it is invoked.'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {workflowLoading ? (\n <div className=\"h-24 rounded-lg bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ) : workflow ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.name', 'Name')}\n </p>\n <p className=\"font-medium text-text-primary\">{workflow.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.key', 'Key')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">\n {workflow.workflowKey}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.version', 'Version')}\n </p>\n <Badge variant=\"outline\" className=\"mt-1\">\n {workflow.version}\n </Badge>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.status', 'Status')}\n </p>\n <div className=\"mt-1\">\n <StatusBadge status={workflow.status} />\n </div>\n </div>\n </div>\n ) : (\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.botDetail.workflow.fields.workflowId', 'Workflow ID')}\n </p>\n <p className=\"mt-1 break-all font-mono text-sm text-text-primary\">{workflowId}</p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {t(\n 'botlit.botDetail.workflow.unresolved',\n 'This workflow could not be loaded — it may have been deleted. Unbind it and design a new one.'\n )}\n </p>\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {workflow && (\n <Button\n leftIcon={<ExternalLink size={16} />}\n disabled={busy}\n onClick={() => openBuilder(workflow.workflowKey, workflow.version)}\n >\n {t('botlit.botDetail.workflow.actions.openBuilder', 'Open in builder')}\n </Button>\n )}\n <Button\n variant=\"outline\"\n leftIcon={<Unlink size={16} />}\n isLoading={busy}\n disabled={busy}\n onClick={() => void handleUnbind()}\n >\n {t('botlit.botDetail.workflow.actions.unbind', 'Unbind')}\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAoDA,IAAM,IAAkB,MAOlB,IAA2B,gBAC3B,IAA8B,SAC9B,IAA2B,mBAC3B,IAAsB,oBACtB,IACJ,8EAGI,IAAsB;CAAC;CAAW;CAAU;CAAQ;AA0D1D,SAAS,IAAqB;CAC5B,IAAM,IAAY,WAAW;AAI7B,QAHI,KAAa,OAAO,EAAU,cAAe,aACxC,EAAU,YAAY,GAExB,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,GAAG;;AAI9E,SAAS,EAAoB,GAAqB,GAA8B;AAuC9E,QAAO;EACL,KAAK;EACL;EACA,aAAa;EACb,eAAe;EACf,MAAM;GACJ,IAAI;GACJ;GACA,SAAS;GACT;GACA,OAAO,CAhDmB;IAC5B,IAAI,GAAY;IAChB,MAAM;IACN,UAAU;KAAE,GAAG;KAAK,GAAG;KAAK;IAC5B,MAAM;IACN,MAAM;KACJ,gBAAgB;KAChB,gBAAgB;KAChB,SAAS;KACT,UAAU;MACR,UAAU;MACV,MAAM;MACN,aAAa;MACb,SAAS;MACT,MAAM;MACN,YAAY,CACV;OACE,MAAM;OACN,MAAM;OACN,MAAM;OACN,aAAa;OACd,CACF;MACF;KACD,MAAM;MACJ,SAAS,EACP,MAAM;OACJ,IAAI;OACJ,MAAM;OACN,MAAM;OACP,EACF;MACD,OAAO,EAAE;MACT,aAAa,EAAE;MAChB;KACF;IACF,CAYuB;GACpB,OAAO,EAAE;GACT,SAAS;IAAE,QAAQ;IAAgB,QAAQ,EAAE,gBAAgB,EAAE,cAAc,MAAM,EAAE;IAAE;GACvF,aAAa,EAAE;GAChB;EACD,MAAM;GAAE,SAAS;GAAqB,SAAS;GAAiB;EAChE,MAAM,EAAE;EACT;;AAgBH,IAAa,KAA2C,EACtD,UACA,YACA,eACA,2BACI;CACJ,IAAM,IAAI,GAAO,EACX,EAAE,kBAAe,GAAW,EAC5B,EAAE,sBAAmB,GAAmB,EACxC,CAAC,KAAa,GAAc,EAC5B,CAAC,GAAM,KAAW,EAAS,GAAM,EAMjC,EAAE,aAAU,SAAS,MAAoB,EAC7C,KAAA,GACA,KAAc,KAAA,EACf,EAEK,IAAc,GACjB,GAAqB,MAAoB;AACxC,IAAW,sBAAsB,EAAY,GAAG,KAAW,IAAkB;IAE/E,CAAC,EAAW,CACb,EAEK,IAAe,EAAY,YAAY;AAC3C,IAAQ,GAAK;AACb,MAAI;GACF,IAAM,IAAc,GAAY,EAC1B,IAAO,GAAG,EAAQ,YAClB,IAAU,MAAM,EAAe;IACnC;IACA,SAAS;IACT;IACA,aAAa,EACX,gDACA,4CACD;IACD,gBAAgB,EAAoB,GAAa,EAAK;IACvD,CAAC;AAEF,OAAI,CAAC,EACH,OAAU,MACR,EAAE,yCAAyC,gCAAgC,CAC5E;AAQH,GALA,MAAM,EAAU;IAAE;IAAO,OAAO,EAAE,YAAY,EAAQ,IAAI;IAAE,CAAC,EAC7D,MAAM,GAAmB,EACzB,EAAM,QACJ,EAAE,2CAA2C,yCAAyC,CACvF,EACD,EAAY,EAAQ,aAAa,EAAQ,QAAQ;WAC1C,GAAc;GACrB,IAAM,IACJ,aAAe,QACX,EAAI,UACJ,EAAE,yCAAyC,gCAAgC;AACjF,KAAM,MAAM,EAAQ;YACZ;AACR,KAAQ,GAAM;;IAEf;EAAC;EAAO;EAAS;EAAgB;EAAmB;EAAa;EAAG;EAAU,CAAC,EAE5E,IAAe,EAAY,YAAY;AAExC,cACC,EACE,2CACA,yEACD,CACF,EAIH;KAAQ,GAAK;AACb,OAAI;AAGF,IAFA,MAAM,EAAU;KAAE;KAAO,OAAO,EAAE,YAAY,MAAM;KAAE,CAAC,EACvD,MAAM,GAAmB,EACzB,EAAM,QAAQ,EAAE,2CAA2C,mBAAmB,CAAC;YACxE,GAAc;IACrB,IAAM,IACJ,aAAe,QACX,EAAI,UACJ,EAAE,yCAAyC,gCAAgC;AACjF,MAAM,MAAM,EAAQ;aACZ;AACR,MAAQ,GAAM;;;IAEf;EAAC;EAAO;EAAmB;EAAG;EAAU,CAAC;AA+B5C,QA5BK,IA6BH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAW,WAAU;aAArB,CACE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,EAAE,yCAAyC,iBAAiB,CACnD;MACZ,kBAAC,GAAD,EAAA,UACG,EACC,+CACA,8DACD,EACe,CAAA,CACP,EAAA,CAAA,EACb,kBAAC,GAAD;GAAa,WAAU;aAAvB,CACG,IACC,kBAAC,OAAD,EAAK,WAAU,+DAAgE,CAAA,GAC7E,IACF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,yCAAyC,OAAO;MACjD,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBAAiC,EAAS;MAAS,CAAA,CAC5D,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,wCAAwC,MAAM;MAC/C,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAS;MACR,CAAA,CACA,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,4CAA4C,UAAU;MACvD,CAAA,EACJ,kBAAC,GAAD;MAAO,SAAQ;MAAU,WAAU;gBAChC,EAAS;MACJ,CAAA,CACJ,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,2CAA2C,SAAS;MACrD,CAAA,EACJ,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAa,QAAQ,EAAS,QAAU,CAAA;MACpC,CAAA,CACF,EAAA,CAAA;KACF;QAEN,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,+CAA+C,cAAc;KAC9D,CAAA;IACJ,kBAAC,KAAD;KAAG,WAAU;eAAsD;KAAe,CAAA;IAClF,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,wCACA,gGACD;KACC,CAAA;IACA,EAAA,CAAA,EAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,GAAD;KACE,UAAU,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;KACpC,UAAU;KACV,eAAe,EAAY,EAAS,aAAa,EAAS,QAAQ;eAEjE,EAAE,iDAAiD,kBAAkB;KAC/D,CAAA,EAEX,kBAAC,GAAD;KACE,SAAQ;KACR,UAAU,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;KAC9B,WAAW;KACX,UAAU;KACV,eAAe,KAAK,GAAc;eAEjC,EAAE,4CAA4C,SAAS;KACjD,CAAA,CACL;MACM;KACT,EAAA,CAAA;EACH,CAAA,GAlHJ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAa,WAAU;YACrB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,yCAAyC,gCAAgC;GAClF,aAAa,EACX,+CACA,gOACD;GACD,QACE,kBAAC,GAAD;IACE,UAAU,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;IAChC,WAAW;IACX,UAAU;IACV,eAAe,KAAK,GAAc;cAEjC,EAAE,0CAA0C,6BAA6B;IACnE,CAAA;GAEX,CAAA;EACU,CAAA,EACT,CAAA"}
|
|
@@ -9,24 +9,25 @@ function l() {
|
|
|
9
9
|
if (!e) throw Error("useBotlit must be used within a BotlitProvider");
|
|
10
10
|
return e;
|
|
11
11
|
}
|
|
12
|
-
var u = ({ children: t, basePath: r, currentUser: o, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f,
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
}, [
|
|
16
|
-
basePath:
|
|
12
|
+
var u = ({ children: t, basePath: r, currentUser: o, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f, productId: p, onBotSelect: m, onAgentSelect: h }) => {
|
|
13
|
+
let g = s(), _ = r || "/", v = n((t) => e(_, t), [_]), y = n((t) => {
|
|
14
|
+
g(e(_, t));
|
|
15
|
+
}, [_, g]), b = i(() => ({
|
|
16
|
+
basePath: _,
|
|
17
17
|
navigate: void 0,
|
|
18
18
|
currentUser: o,
|
|
19
19
|
tenantId: l,
|
|
20
20
|
workspaceId: u,
|
|
21
21
|
apiGatewayUrl: d,
|
|
22
22
|
authToken: f,
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
productId: p,
|
|
24
|
+
onBotSelect: m,
|
|
25
|
+
onAgentSelect: h,
|
|
25
26
|
isStandalone: !0,
|
|
26
|
-
navigateTo:
|
|
27
|
-
getPath:
|
|
27
|
+
navigateTo: y,
|
|
28
|
+
getPath: v
|
|
28
29
|
}), [
|
|
29
|
-
|
|
30
|
+
_,
|
|
30
31
|
o,
|
|
31
32
|
l,
|
|
32
33
|
u,
|
|
@@ -34,31 +35,33 @@ var u = ({ children: t, basePath: r, currentUser: o, tenantId: l, workspaceId: u
|
|
|
34
35
|
f,
|
|
35
36
|
p,
|
|
36
37
|
m,
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
h,
|
|
39
|
+
y,
|
|
40
|
+
v
|
|
39
41
|
]);
|
|
40
42
|
return /* @__PURE__ */ a(c.Provider, {
|
|
41
|
-
value:
|
|
43
|
+
value: b,
|
|
42
44
|
children: t
|
|
43
45
|
});
|
|
44
|
-
}, d = ({ children: t, basePath: r, navigate: o, currentUser: s, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f,
|
|
45
|
-
let
|
|
46
|
-
o(e(
|
|
47
|
-
}, [
|
|
48
|
-
basePath:
|
|
46
|
+
}, d = ({ children: t, basePath: r, navigate: o, currentUser: s, tenantId: l, workspaceId: u, apiGatewayUrl: d, authToken: f, productId: p, onBotSelect: m, onAgentSelect: h }) => {
|
|
47
|
+
let g = r || "/", _ = n((t) => e(g, t), [g]), v = n((t) => {
|
|
48
|
+
o(e(g, t));
|
|
49
|
+
}, [g, o]), y = i(() => ({
|
|
50
|
+
basePath: g,
|
|
49
51
|
navigate: o,
|
|
50
52
|
currentUser: s,
|
|
51
53
|
tenantId: l,
|
|
52
54
|
workspaceId: u,
|
|
53
55
|
apiGatewayUrl: d,
|
|
54
56
|
authToken: f,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
productId: p,
|
|
58
|
+
onBotSelect: m,
|
|
59
|
+
onAgentSelect: h,
|
|
57
60
|
isStandalone: !1,
|
|
58
|
-
navigateTo:
|
|
59
|
-
getPath:
|
|
61
|
+
navigateTo: v,
|
|
62
|
+
getPath: _
|
|
60
63
|
}), [
|
|
61
|
-
|
|
64
|
+
g,
|
|
62
65
|
o,
|
|
63
66
|
s,
|
|
64
67
|
l,
|
|
@@ -67,11 +70,12 @@ var u = ({ children: t, basePath: r, currentUser: o, tenantId: l, workspaceId: u
|
|
|
67
70
|
f,
|
|
68
71
|
p,
|
|
69
72
|
m,
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
h,
|
|
74
|
+
v,
|
|
75
|
+
_
|
|
72
76
|
]);
|
|
73
77
|
return /* @__PURE__ */ a(c.Provider, {
|
|
74
|
-
value:
|
|
78
|
+
value: y,
|
|
75
79
|
children: t
|
|
76
80
|
});
|
|
77
81
|
}, f = (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BotlitProvider.js","names":[],"sources":["../../../src/botlit/providers/BotlitProvider.tsx"],"sourcesContent":["/**\n * BotManor Context Provider\n * @module botlit/providers\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router';\nimport type { BotmanorRootProps } from '../types';\nimport { joinPath } from '../utils/navigation';\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface BotlitContextValue extends Omit<\n BotmanorRootProps,\n 'registerNavItems' | 'registerFooterItems' | 'navigate'\n> {\n isStandalone: boolean;\n /** Shell's navigate function (only available in integrated mode) */\n navigate?: (path: string) => void;\n /** Navigate to a path relative to basePath */\n navigateTo: (relativePath: string) => void;\n /** Get the full path for a relative path */\n getPath: (relativePath: string) => string;\n}\n\nconst BotlitContext = createContext<BotlitContextValue | null>(null);\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useBotlit(): BotlitContextValue {\n const context = useContext(BotlitContext);\n if (!context) {\n throw new Error('useBotlit must be used within a BotlitProvider');\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\ntype ProviderProps = Omit<BotmanorRootProps, 'registerNavItems' | 'registerFooterItems'> & {\n isStandalone: boolean;\n};\n\n/** Provider for standalone mode (has access to MemoryRouter) */\nconst StandaloneProviderInner: FC<PropsWithChildren<ProviderProps>> = ({\n children,\n basePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n}) => {\n // Always call hooks unconditionally in this component\n const routerNavigate = useRouterNavigate();\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n routerNavigate(fullPath);\n },\n [effectiveBasePath, routerNavigate]\n );\n\n const contextValue = useMemo<BotlitContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate: undefined,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n isStandalone: true,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n navigateTo,\n getPath,\n ]\n );\n\n return <BotlitContext.Provider value={contextValue}>{children}</BotlitContext.Provider>;\n};\n\n/** Provider for integrated mode (shell provides navigate) */\nconst IntegratedProviderInner: FC<\n PropsWithChildren<ProviderProps & { navigate: (path: string) => void }>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n}) => {\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n navigate(fullPath);\n },\n [effectiveBasePath, navigate]\n );\n\n const contextValue = useMemo<BotlitContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n isStandalone: false,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n onBotSelect,\n onAgentSelect,\n navigateTo,\n getPath,\n ]\n );\n\n return <BotlitContext.Provider value={contextValue}>{children}</BotlitContext.Provider>;\n};\n\nexport const BotlitProvider: FC<PropsWithChildren<BotmanorRootProps>> = (props) => {\n const { children, navigate } = props;\n // Standalone mode: no navigate function provided (MFE manages its own router)\n // Integrated mode: navigate function provided by shell (shell provides router)\n const isStandalone = !navigate;\n\n // Standalone mode: wrap in MemoryRouter\n if (isStandalone) {\n return (\n <MemoryRouter>\n <StandaloneProviderInner {...props} isStandalone={true}>\n {children}\n </StandaloneProviderInner>\n </MemoryRouter>\n );\n }\n\n // Integrated mode: shell provides router\n return (\n <IntegratedProviderInner {...props} navigate={navigate} isStandalone={false}>\n {children}\n </IntegratedProviderInner>\n );\n};\n"],"mappings":";;;;;AA4BA,IAAM,IAAgB,EAAyC,KAAK;AAMpE,SAAgB,IAAgC;CAC9C,IAAM,IAAU,EAAW,EAAc;AACzC,KAAI,CAAC,EACH,OAAU,MAAM,iDAAiD;AAEnE,QAAO;;AAYT,IAAM,KAAiE,EACrE,aACA,aACA,gBACA,aACA,gBACA,kBACA,cACA,gBACA,uBACI;CAEJ,IAAM,IAAiB,GAAmB,EACpC,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CAClC;IAE1B,CAAC,GAAmB,EAAe,CACpC,EAEK,IAAe,SACZ;EACL,UAAU;EACV,UAAU,KAAA;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAc,UAAf;EAAwB,OAAO;EAAe;EAAkC,CAAA;GAInF,KAED,EACH,aACA,aACA,aACA,gBACA,aACA,gBACA,kBACA,cACA,gBACA,uBACI;CACJ,IAAM,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CACxC;IAEpB,CAAC,GAAmB,EAAS,CAC9B,EAEK,IAAe,SACZ;EACL,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAc,UAAf;EAAwB,OAAO;EAAe;EAAkC,CAAA;GAG5E,KAA4D,MAAU;CACjF,IAAM,EAAE,aAAU,gBAAa;AAiB/B,QAdsB,IAepB,kBAAC,GAAD;EAAyB,GAAI;EAAiB;EAAU,cAAc;EACnE;EACuB,CAAA,GAZxB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAyB,GAAI;EAAO,cAAc;EAC/C;EACuB,CAAA,EACb,CAAA"}
|
|
1
|
+
{"version":3,"file":"BotlitProvider.js","names":[],"sources":["../../../src/botlit/providers/BotlitProvider.tsx"],"sourcesContent":["/**\n * BotManor Context Provider\n * @module botlit/providers\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router';\nimport type { BotmanorRootProps } from '../types';\nimport { joinPath } from '../utils/navigation';\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface BotlitContextValue extends Omit<\n BotmanorRootProps,\n 'registerNavItems' | 'registerFooterItems' | 'navigate'\n> {\n isStandalone: boolean;\n /** Shell's navigate function (only available in integrated mode) */\n navigate?: (path: string) => void;\n /** Navigate to a path relative to basePath */\n navigateTo: (relativePath: string) => void;\n /** Get the full path for a relative path */\n getPath: (relativePath: string) => string;\n}\n\nconst BotlitContext = createContext<BotlitContextValue | null>(null);\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useBotlit(): BotlitContextValue {\n const context = useContext(BotlitContext);\n if (!context) {\n throw new Error('useBotlit must be used within a BotlitProvider');\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\ntype ProviderProps = Omit<BotmanorRootProps, 'registerNavItems' | 'registerFooterItems'> & {\n isStandalone: boolean;\n};\n\n/** Provider for standalone mode (has access to MemoryRouter) */\nconst StandaloneProviderInner: FC<PropsWithChildren<ProviderProps>> = ({\n children,\n basePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n}) => {\n // Always call hooks unconditionally in this component\n const routerNavigate = useRouterNavigate();\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n routerNavigate(fullPath);\n },\n [effectiveBasePath, routerNavigate]\n );\n\n const contextValue = useMemo<BotlitContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate: undefined,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n isStandalone: true,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n navigateTo,\n getPath,\n ]\n );\n\n return <BotlitContext.Provider value={contextValue}>{children}</BotlitContext.Provider>;\n};\n\n/** Provider for integrated mode (shell provides navigate) */\nconst IntegratedProviderInner: FC<\n PropsWithChildren<ProviderProps & { navigate: (path: string) => void }>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n}) => {\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n const fullPath = joinPath(effectiveBasePath, relativePath);\n navigate(fullPath);\n },\n [effectiveBasePath, navigate]\n );\n\n const contextValue = useMemo<BotlitContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n isStandalone: false,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n productId,\n onBotSelect,\n onAgentSelect,\n navigateTo,\n getPath,\n ]\n );\n\n return <BotlitContext.Provider value={contextValue}>{children}</BotlitContext.Provider>;\n};\n\nexport const BotlitProvider: FC<PropsWithChildren<BotmanorRootProps>> = (props) => {\n const { children, navigate } = props;\n // Standalone mode: no navigate function provided (MFE manages its own router)\n // Integrated mode: navigate function provided by shell (shell provides router)\n const isStandalone = !navigate;\n\n // Standalone mode: wrap in MemoryRouter\n if (isStandalone) {\n return (\n <MemoryRouter>\n <StandaloneProviderInner {...props} isStandalone={true}>\n {children}\n </StandaloneProviderInner>\n </MemoryRouter>\n );\n }\n\n // Integrated mode: shell provides router\n return (\n <IntegratedProviderInner {...props} navigate={navigate} isStandalone={false}>\n {children}\n </IntegratedProviderInner>\n );\n};\n"],"mappings":";;;;;AA4BA,IAAM,IAAgB,EAAyC,KAAK;AAMpE,SAAgB,IAAgC;CAC9C,IAAM,IAAU,EAAW,EAAc;AACzC,KAAI,CAAC,EACH,OAAU,MAAM,iDAAiD;AAEnE,QAAO;;AAYT,IAAM,KAAiE,EACrE,aACA,aACA,gBACA,aACA,gBACA,kBACA,cACA,cACA,gBACA,uBACI;CAEJ,IAAM,IAAiB,GAAmB,EACpC,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CAClC;IAE1B,CAAC,GAAmB,EAAe,CACpC,EAEK,IAAe,SACZ;EACL,UAAU;EACV,UAAU,KAAA;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAc,UAAf;EAAwB,OAAO;EAAe;EAAkC,CAAA;GAInF,KAED,EACH,aACA,aACA,aACA,gBACA,aACA,gBACA,kBACA,cACA,cACA,gBACA,uBACI;CACJ,IAAM,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AAExB,IADiB,EAAS,GAAmB,EAAa,CACxC;IAEpB,CAAC,GAAmB,EAAS,CAC9B,EAEK,IAAe,SACZ;EACL,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QAAO,kBAAC,EAAc,UAAf;EAAwB,OAAO;EAAe;EAAkC,CAAA;GAG5E,KAA4D,MAAU;CACjF,IAAM,EAAE,aAAU,gBAAa;AAiB/B,QAdsB,IAepB,kBAAC,GAAD;EAAyB,GAAI;EAAiB;EAAU,cAAc;EACnE;EACuB,CAAA,GAZxB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAyB,GAAI;EAAO,cAAc;EAC/C;EACuB,CAAA,EACb,CAAA"}
|