@burdenoff/microfe-botmanor 2026.1002.1 → 2026.1007.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/components/AssistantChannelPanel.js +109 -0
- package/dist/botlit/components/AssistantChannelPanel.js.map +1 -0
- package/dist/botlit/components/AssistantRuntimeSetupPanel.js +76 -0
- package/dist/botlit/components/AssistantRuntimeSetupPanel.js.map +1 -0
- package/dist/botlit/components/AssistantWebsitePanel.js +196 -0
- package/dist/botlit/components/AssistantWebsitePanel.js.map +1 -0
- package/dist/botlit/components/KnowledgeSourceImportPanel.js +135 -0
- package/dist/botlit/components/KnowledgeSourceImportPanel.js.map +1 -0
- package/dist/botlit/hooks/useAgents.js +12 -12
- package/dist/botlit/hooks/useAgents.js.map +1 -1
- package/dist/botlit/hooks/useAssistantChannels.js +76 -0
- package/dist/botlit/hooks/useAssistantChannels.js.map +1 -0
- package/dist/botlit/hooks/useAssistantRuntimeSetup.js +112 -0
- package/dist/botlit/hooks/useAssistantRuntimeSetup.js.map +1 -0
- package/dist/botlit/hooks/useAssistantWebsites.js +85 -0
- package/dist/botlit/hooks/useAssistantWebsites.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/useKnowledgeSourceImports.js +86 -0
- package/dist/botlit/hooks/useKnowledgeSourceImports.js.map +1 -0
- package/dist/botlit/hooks/useLLMProviders.js +2 -2
- package/dist/botlit/hooks/useLLMProviders.js.map +1 -1
- package/dist/botlit/pages/AgentDetailPage.js +180 -158
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/ChatPage.js +58 -238
- package/dist/botlit/pages/ChatPage.js.map +1 -1
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js +162 -160
- package/dist/botlit/pages/KnowledgeBaseDetailPage.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 +3 -3
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useBotlit as e } from "../providers/BotlitProvider.js";
|
|
2
|
+
import { Button as t } from "./Button.js";
|
|
3
|
+
import { useChannelRoutes as n } from "../hooks/useChannels.js";
|
|
4
|
+
import { useAssistantChannels as r } from "../hooks/useAssistantChannels.js";
|
|
5
|
+
import { useAssistantWebsites as i } from "../hooks/useAssistantWebsites.js";
|
|
6
|
+
import { useState as a } from "react";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
//#region src/botlit/components/AssistantChannelPanel.tsx
|
|
9
|
+
function c({ agentId: t }) {
|
|
10
|
+
let { workspaceId: n, currentUser: r } = e();
|
|
11
|
+
return /* @__PURE__ */ o(l, { agentId: t }, `${n}:${r?.id}:${t}`);
|
|
12
|
+
}
|
|
13
|
+
function l({ agentId: e }) {
|
|
14
|
+
let c = r(), l = i(e), u = n(), [d, f] = a(""), [p, m] = a(""), [h, g] = a(""), _ = l.installations.data?.filter((e) => e.enabled) ?? [], v = new Set(l.installations.data?.map((e) => e.id) ?? []);
|
|
15
|
+
return /* @__PURE__ */ s("section", {
|
|
16
|
+
className: "space-y-4 rounded-lg border border-border-default p-5",
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ o("h2", {
|
|
19
|
+
className: "text-lg font-semibold",
|
|
20
|
+
children: "Channel assistant"
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ o("p", {
|
|
23
|
+
className: "text-sm text-text-secondary",
|
|
24
|
+
children: "Use an approved public assistant publication on an existing channel route. Messages run through Fluidgrids and replies use channel-svc. Private course teachers cannot be published."
|
|
25
|
+
}),
|
|
26
|
+
(u.error || l.installations.isError || c.installations.isError) && /* @__PURE__ */ o("p", {
|
|
27
|
+
role: "alert",
|
|
28
|
+
children: "Channel setup could not be loaded. Check your publishing permission and reload."
|
|
29
|
+
}),
|
|
30
|
+
/* @__PURE__ */ s("label", {
|
|
31
|
+
className: "grid gap-2 text-sm",
|
|
32
|
+
children: ["Approved public publication", /* @__PURE__ */ s("select", {
|
|
33
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
34
|
+
value: p,
|
|
35
|
+
onChange: (e) => m(e.target.value),
|
|
36
|
+
children: [/* @__PURE__ */ o("option", {
|
|
37
|
+
value: "",
|
|
38
|
+
children: "Select a reviewed publication"
|
|
39
|
+
}), _.map((e) => /* @__PURE__ */ o("option", {
|
|
40
|
+
value: e.id,
|
|
41
|
+
children: e.name
|
|
42
|
+
}, e.id))]
|
|
43
|
+
})]
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ s("label", {
|
|
46
|
+
className: "grid gap-2 text-sm",
|
|
47
|
+
children: ["Existing channel route", /* @__PURE__ */ s("select", {
|
|
48
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
49
|
+
value: d,
|
|
50
|
+
onChange: (e) => f(e.target.value),
|
|
51
|
+
children: [/* @__PURE__ */ o("option", {
|
|
52
|
+
value: "",
|
|
53
|
+
children: "Select a route"
|
|
54
|
+
}), u.data.map((e) => /* @__PURE__ */ s("option", {
|
|
55
|
+
value: e.id,
|
|
56
|
+
children: [
|
|
57
|
+
e.locationId,
|
|
58
|
+
" · ",
|
|
59
|
+
e.appId
|
|
60
|
+
]
|
|
61
|
+
}, e.id))]
|
|
62
|
+
})]
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ o(t, {
|
|
65
|
+
disabled: !_.some((e) => e.id === p) || !u.data.some((e) => e.id === d) || !!u.error,
|
|
66
|
+
isLoading: c.configure.isPending,
|
|
67
|
+
onClick: async () => {
|
|
68
|
+
try {
|
|
69
|
+
await c.configure.mutateAsync({
|
|
70
|
+
routeId: d,
|
|
71
|
+
publicationId: p
|
|
72
|
+
}), g("Channel assistant enabled.");
|
|
73
|
+
} catch {
|
|
74
|
+
g("Channel publication failed. Check the public assistant configuration, channel ownership and publishing permission.");
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
children: "Enable channel assistant"
|
|
78
|
+
}),
|
|
79
|
+
h && /* @__PURE__ */ o("p", {
|
|
80
|
+
role: "status",
|
|
81
|
+
children: h
|
|
82
|
+
}),
|
|
83
|
+
c.installations.data?.filter((e) => v.has(e.publicationId)).map((e) => /* @__PURE__ */ s("div", {
|
|
84
|
+
className: "flex items-center justify-between gap-3 text-sm",
|
|
85
|
+
children: [/* @__PURE__ */ s("span", { children: [
|
|
86
|
+
u.data.find((t) => t.id === e.routeId)?.locationId ?? e.routeId,
|
|
87
|
+
" ·",
|
|
88
|
+
" ",
|
|
89
|
+
e.enabled ? "Enabled" : "Disabled"
|
|
90
|
+
] }), e.enabled && /* @__PURE__ */ o(t, {
|
|
91
|
+
variant: "outline",
|
|
92
|
+
isLoading: c.disable.isPending,
|
|
93
|
+
onClick: async () => {
|
|
94
|
+
try {
|
|
95
|
+
await c.disable.mutateAsync(e.id), g("Channel assistant disabled.");
|
|
96
|
+
} catch {
|
|
97
|
+
g("Channel could not be disabled. Reload and try again.");
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
children: "Disable"
|
|
101
|
+
})]
|
|
102
|
+
}, e.id))
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { c as AssistantChannelPanel };
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=AssistantChannelPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantChannelPanel.js","names":[],"sources":["../../../src/botlit/components/AssistantChannelPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssistantChannels } from '../hooks/useAssistantChannels';\nimport { useAssistantWebsites } from '../hooks/useAssistantWebsites';\nimport { useChannelRoutes } from '../hooks/useChannels';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { Button } from './Button';\n\nexport function AssistantChannelPanel({ agentId }: { agentId: string }) {\n const { workspaceId, currentUser } = useBotlit();\n return (\n <ScopedChannelPanel key={`${workspaceId}:${currentUser?.id}:${agentId}`} agentId={agentId} />\n );\n}\nfunction ScopedChannelPanel({ agentId }: { agentId: string }) {\n const channels = useAssistantChannels(),\n publications = useAssistantWebsites(agentId),\n routes = useChannelRoutes();\n const [routeId, setRouteId] = useState(''),\n [publicationId, setPublicationId] = useState(''),\n [message, setMessage] = useState('');\n const approved = publications.installations.data?.filter((row) => row.enabled) ?? [];\n const ownIds = new Set(publications.installations.data?.map((row) => row.id) ?? []);\n return (\n <section className=\"space-y-4 rounded-lg border border-border-default p-5\">\n <h2 className=\"text-lg font-semibold\">Channel assistant</h2>\n <p className=\"text-sm text-text-secondary\">\n Use an approved public assistant publication on an existing channel route. Messages run\n through Fluidgrids and replies use channel-svc. Private course teachers cannot be published.\n </p>\n {(routes.error || publications.installations.isError || channels.installations.isError) && (\n <p role=\"alert\">\n Channel setup could not be loaded. Check your publishing permission and reload.\n </p>\n )}\n <label className=\"grid gap-2 text-sm\">\n Approved public publication\n <select\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n value={publicationId}\n onChange={(event) => setPublicationId(event.target.value)}\n >\n <option value=\"\">Select a reviewed publication</option>\n {approved.map((row) => (\n <option key={row.id} value={row.id}>\n {row.name}\n </option>\n ))}\n </select>\n </label>\n <label className=\"grid gap-2 text-sm\">\n Existing channel route\n <select\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n value={routeId}\n onChange={(event) => setRouteId(event.target.value)}\n >\n <option value=\"\">Select a route</option>\n {routes.data.map((route) => (\n <option key={route.id} value={route.id}>\n {route.locationId} · {route.appId}\n </option>\n ))}\n </select>\n </label>\n <Button\n disabled={\n !approved.some((row) => row.id === publicationId) ||\n !routes.data.some((row) => row.id === routeId) ||\n Boolean(routes.error)\n }\n isLoading={channels.configure.isPending}\n onClick={async () => {\n try {\n await channels.configure.mutateAsync({ routeId, publicationId });\n setMessage('Channel assistant enabled.');\n } catch {\n setMessage(\n 'Channel publication failed. Check the public assistant configuration, channel ownership and publishing permission.'\n );\n }\n }}\n >\n Enable channel assistant\n </Button>\n {message && <p role=\"status\">{message}</p>}\n {channels.installations.data\n ?.filter((row) => ownIds.has(row.publicationId))\n .map((row) => (\n <div key={row.id} className=\"flex items-center justify-between gap-3 text-sm\">\n <span>\n {routes.data.find((route) => route.id === row.routeId)?.locationId ?? row.routeId} ·{' '}\n {row.enabled ? 'Enabled' : 'Disabled'}\n </span>\n {row.enabled && (\n <Button\n variant=\"outline\"\n isLoading={channels.disable.isPending}\n onClick={async () => {\n try {\n await channels.disable.mutateAsync(row.id);\n setMessage('Channel assistant disabled.');\n } catch {\n setMessage('Channel could not be disabled. Reload and try again.');\n }\n }}\n >\n Disable\n </Button>\n )}\n </div>\n ))}\n </section>\n );\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,EAAsB,EAAE,cAAgC;CACtE,IAAM,EAAE,gBAAa,mBAAgB,GAAW;AAChD,QACE,kBAAC,GAAD,EAAkF,YAAW,EAApE,GAAG,EAAY,GAAG,GAAa,GAAG,GAAG,IAA+B;;AAGjG,SAAS,EAAmB,EAAE,cAAgC;CAC5D,IAAM,IAAW,GAAsB,EACrC,IAAe,EAAqB,EAAQ,EAC5C,IAAS,GAAkB,EACvB,CAAC,GAAS,KAAc,EAAS,GAAG,EACxC,CAAC,GAAe,KAAoB,EAAS,GAAG,EAChD,CAAC,GAAS,KAAc,EAAS,GAAG,EAChC,IAAW,EAAa,cAAc,MAAM,QAAQ,MAAQ,EAAI,QAAQ,IAAI,EAAE,EAC9E,IAAS,IAAI,IAAI,EAAa,cAAc,MAAM,KAAK,MAAQ,EAAI,GAAG,IAAI,EAAE,CAAC;AACnF,QACE,kBAAC,WAAD;EAAS,WAAU;YAAnB;GACE,kBAAC,MAAD;IAAI,WAAU;cAAwB;IAAsB,CAAA;GAC5D,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAGvC,CAAA;IACF,EAAO,SAAS,EAAa,cAAc,WAAW,EAAS,cAAc,YAC7E,kBAAC,KAAD;IAAG,MAAK;cAAQ;IAEZ,CAAA;GAEN,kBAAC,SAAD;IAAO,WAAU;cAAjB,CAAsC,+BAEpC,kBAAC,UAAD;KACE,WAAU;KACV,OAAO;KACP,WAAW,MAAU,EAAiB,EAAM,OAAO,MAAM;eAH3D,CAKE,kBAAC,UAAD;MAAQ,OAAM;gBAAG;MAAsC,CAAA,EACtD,EAAS,KAAK,MACb,kBAAC,UAAD;MAAqB,OAAO,EAAI;gBAC7B,EAAI;MACE,EAFI,EAAI,GAER,CACT,CACK;OACH;;GACR,kBAAC,SAAD;IAAO,WAAU;cAAjB,CAAsC,0BAEpC,kBAAC,UAAD;KACE,WAAU;KACV,OAAO;KACP,WAAW,MAAU,EAAW,EAAM,OAAO,MAAM;eAHrD,CAKE,kBAAC,UAAD;MAAQ,OAAM;gBAAG;MAAuB,CAAA,EACvC,EAAO,KAAK,KAAK,MAChB,kBAAC,UAAD;MAAuB,OAAO,EAAM;gBAApC;OACG,EAAM;OAAW;OAAI,EAAM;OACrB;QAFI,EAAM,GAEV,CACT,CACK;OACH;;GACR,kBAAC,GAAD;IACE,UACE,CAAC,EAAS,MAAM,MAAQ,EAAI,OAAO,EAAc,IACjD,CAAC,EAAO,KAAK,MAAM,MAAQ,EAAI,OAAO,EAAQ,IAC9C,EAAQ,EAAO;IAEjB,WAAW,EAAS,UAAU;IAC9B,SAAS,YAAY;AACnB,SAAI;AAEF,MADA,MAAM,EAAS,UAAU,YAAY;OAAE;OAAS;OAAe,CAAC,EAChE,EAAW,6BAA6B;aAClC;AACN,QACE,qHACD;;;cAGN;IAEQ,CAAA;GACR,KAAW,kBAAC,KAAD;IAAG,MAAK;cAAU;IAAY,CAAA;GACzC,EAAS,cAAc,MACpB,QAAQ,MAAQ,EAAO,IAAI,EAAI,cAAc,CAAC,CAC/C,KAAK,MACJ,kBAAC,OAAD;IAAkB,WAAU;cAA5B,CACE,kBAAC,QAAD,EAAA,UAAA;KACG,EAAO,KAAK,MAAM,MAAU,EAAM,OAAO,EAAI,QAAQ,EAAE,cAAc,EAAI;KAAQ;KAAG;KACpF,EAAI,UAAU,YAAY;KACtB,EAAA,CAAA,EACN,EAAI,WACH,kBAAC,GAAD;KACE,SAAQ;KACR,WAAW,EAAS,QAAQ;KAC5B,SAAS,YAAY;AACnB,UAAI;AAEF,OADA,MAAM,EAAS,QAAQ,YAAY,EAAI,GAAG,EAC1C,EAAW,8BAA8B;cACnC;AACN,SAAW,uDAAuD;;;eAGvE;KAEQ,CAAA,CAEP;MArBI,EAAI,GAqBR,CACN;GACI"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useBotlit as e } from "../providers/BotlitProvider.js";
|
|
2
|
+
import { Button as t } from "./Button.js";
|
|
3
|
+
import { useAssistantRuntimeSetup as n } from "../hooks/useAssistantRuntimeSetup.js";
|
|
4
|
+
import { useState as r } from "react";
|
|
5
|
+
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
//#region src/botlit/components/AssistantRuntimeSetupPanel.tsx
|
|
7
|
+
function s({ agentId: t, onConfigured: n }) {
|
|
8
|
+
let { workspaceId: r, currentUser: i } = e();
|
|
9
|
+
return /* @__PURE__ */ a(c, {
|
|
10
|
+
agentId: t,
|
|
11
|
+
onConfigured: n
|
|
12
|
+
}, `${r}:${i?.id}:${t}`);
|
|
13
|
+
}
|
|
14
|
+
function c({ agentId: e, onConfigured: s }) {
|
|
15
|
+
let { connections: c, configure: l } = n(e), [u, d] = r(""), [f, p] = r(""), [m, h] = r(!1);
|
|
16
|
+
return /* @__PURE__ */ o("section", {
|
|
17
|
+
className: "space-y-4 rounded-lg border border-border-default p-5",
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h2", {
|
|
20
|
+
className: "text-lg font-semibold",
|
|
21
|
+
children: "Assistant setup"
|
|
22
|
+
}), /* @__PURE__ */ a("p", {
|
|
23
|
+
className: "text-sm text-text-secondary",
|
|
24
|
+
children: "Connect this assistant to Fluidgrids for answers and recovery. Course teachers also receive content ingestion workflows."
|
|
25
|
+
})] }),
|
|
26
|
+
c.isPending ? /* @__PURE__ */ a("p", {
|
|
27
|
+
role: "status",
|
|
28
|
+
children: "Loading connections…"
|
|
29
|
+
}) : c.isError ? /* @__PURE__ */ o("div", {
|
|
30
|
+
role: "alert",
|
|
31
|
+
children: [/* @__PURE__ */ a("p", { children: "Connections could not be loaded. Check your access and try again." }), /* @__PURE__ */ a(t, {
|
|
32
|
+
variant: "outline",
|
|
33
|
+
onClick: () => void c.refetch(),
|
|
34
|
+
children: "Reload connections"
|
|
35
|
+
})]
|
|
36
|
+
}) : c.data?.length ? /* @__PURE__ */ o(i, { children: [/* @__PURE__ */ o("label", {
|
|
37
|
+
className: "grid gap-2 text-sm font-medium",
|
|
38
|
+
children: ["Workflow connection", /* @__PURE__ */ o("select", {
|
|
39
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
40
|
+
value: u,
|
|
41
|
+
disabled: l.isPending,
|
|
42
|
+
onChange: (e) => {
|
|
43
|
+
d(e.target.value), p("");
|
|
44
|
+
},
|
|
45
|
+
children: [/* @__PURE__ */ a("option", {
|
|
46
|
+
value: "",
|
|
47
|
+
children: "Choose a connection"
|
|
48
|
+
}), c.data.map((e) => /* @__PURE__ */ a("option", {
|
|
49
|
+
value: e.id,
|
|
50
|
+
children: e.name
|
|
51
|
+
}, e.id))]
|
|
52
|
+
})]
|
|
53
|
+
}), /* @__PURE__ */ a(t, {
|
|
54
|
+
disabled: !u,
|
|
55
|
+
isLoading: l.isPending,
|
|
56
|
+
onClick: async () => {
|
|
57
|
+
p(""), h(!1);
|
|
58
|
+
try {
|
|
59
|
+
p((await l.mutateAsync(u)).ready ? "Assistant configured and ready." : "Workflows configured. Activate the assistant and finish indexing its knowledge before using it."), s();
|
|
60
|
+
} catch {
|
|
61
|
+
h(!0), p("Setup could not be completed. Check the model, knowledge scope and your permissions, then try again.");
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
children: "Configure assistant"
|
|
65
|
+
})] }) : /* @__PURE__ */ a("p", { children: "Create an active Burdenoff service account connection in Integrations, then reload this page." }),
|
|
66
|
+
f && /* @__PURE__ */ a("p", {
|
|
67
|
+
role: m ? "alert" : "status",
|
|
68
|
+
children: f
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { s as AssistantRuntimeSetupPanel };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=AssistantRuntimeSetupPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantRuntimeSetupPanel.js","names":[],"sources":["../../../src/botlit/components/AssistantRuntimeSetupPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssistantRuntimeSetup } from '../hooks/useAssistantRuntimeSetup';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { Button } from './Button';\n\nexport function AssistantRuntimeSetupPanel({\n agentId,\n onConfigured,\n}: {\n agentId: string;\n onConfigured: () => void;\n}) {\n const { workspaceId, currentUser } = useBotlit();\n return (\n <ScopedSetupPanel\n key={`${workspaceId}:${currentUser?.id}:${agentId}`}\n agentId={agentId}\n onConfigured={onConfigured}\n />\n );\n}\n\nfunction ScopedSetupPanel({\n agentId,\n onConfigured,\n}: {\n agentId: string;\n onConfigured: () => void;\n}) {\n const { connections, configure } = useAssistantRuntimeSetup(agentId);\n const [selected, setSelected] = useState('');\n const [message, setMessage] = useState('');\n const [failed, setFailed] = useState(false);\n return (\n <section className=\"space-y-4 rounded-lg border border-border-default p-5\">\n <div>\n <h2 className=\"text-lg font-semibold\">Assistant setup</h2>\n <p className=\"text-sm text-text-secondary\">\n Connect this assistant to Fluidgrids for answers and recovery. Course teachers also\n receive content ingestion workflows.\n </p>\n </div>\n {connections.isPending ? (\n <p role=\"status\">Loading connections…</p>\n ) : connections.isError ? (\n <div role=\"alert\">\n <p>Connections could not be loaded. Check your access and try again.</p>\n <Button variant=\"outline\" onClick={() => void connections.refetch()}>\n Reload connections\n </Button>\n </div>\n ) : !connections.data?.length ? (\n <p>\n Create an active Burdenoff service account connection in Integrations, then reload this\n page.\n </p>\n ) : (\n <>\n <label className=\"grid gap-2 text-sm font-medium\">\n Workflow connection\n <select\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n value={selected}\n disabled={configure.isPending}\n onChange={(event) => {\n setSelected(event.target.value);\n setMessage('');\n }}\n >\n <option value=\"\">Choose a connection</option>\n {connections.data.map((connection) => (\n <option key={connection.id} value={connection.id}>\n {connection.name}\n </option>\n ))}\n </select>\n </label>\n <Button\n disabled={!selected}\n isLoading={configure.isPending}\n onClick={async () => {\n setMessage('');\n setFailed(false);\n try {\n const result = await configure.mutateAsync(selected);\n setMessage(\n result.ready\n ? 'Assistant configured and ready.'\n : 'Workflows configured. Activate the assistant and finish indexing its knowledge before using it.'\n );\n onConfigured();\n } catch {\n setFailed(true);\n setMessage(\n 'Setup could not be completed. Check the model, knowledge scope and your permissions, then try again.'\n );\n }\n }}\n >\n Configure assistant\n </Button>\n </>\n )}\n {message && <p role={failed ? 'alert' : 'status'}>{message}</p>}\n </section>\n );\n}\n"],"mappings":";;;;;;AAKA,SAAgB,EAA2B,EACzC,YACA,mBAIC;CACD,IAAM,EAAE,gBAAa,mBAAgB,GAAW;AAChD,QACE,kBAAC,GAAD;EAEW;EACK;EACd,EAHK,GAAG,EAAY,GAAG,GAAa,GAAG,GAAG,IAG1C;;AAIN,SAAS,EAAiB,EACxB,YACA,mBAIC;CACD,IAAM,EAAE,gBAAa,iBAAc,EAAyB,EAAQ,EAC9D,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAQ,KAAa,EAAS,GAAM;AAC3C,QACE,kBAAC,WAAD;EAAS,WAAU;YAAnB;GACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAwB;IAAoB,CAAA,EAC1D,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAGvC,CAAA,CACA,EAAA,CAAA;GACL,EAAY,YACX,kBAAC,KAAD;IAAG,MAAK;cAAS;IAAwB,CAAA,GACvC,EAAY,UACd,kBAAC,OAAD;IAAK,MAAK;cAAV,CACE,kBAAC,KAAD,EAAA,UAAG,qEAAqE,CAAA,EACxE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,eAAe,KAAK,EAAY,SAAS;eAAE;KAE5D,CAAA,CACL;QACH,EAAY,MAAM,SAMrB,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,WAAU;cAAjB,CAAkD,uBAEhD,kBAAC,UAAD;KACE,WAAU;KACV,OAAO;KACP,UAAU,EAAU;KACpB,WAAW,MAAU;AAEnB,MADA,EAAY,EAAM,OAAO,MAAM,EAC/B,EAAW,GAAG;;eANlB,CASE,kBAAC,UAAD;MAAQ,OAAM;gBAAG;MAA4B,CAAA,EAC5C,EAAY,KAAK,KAAK,MACrB,kBAAC,UAAD;MAA4B,OAAO,EAAW;gBAC3C,EAAW;MACL,EAFI,EAAW,GAEf,CACT,CACK;OACH;OACR,kBAAC,GAAD;IACE,UAAU,CAAC;IACX,WAAW,EAAU;IACrB,SAAS,YAAY;AAEnB,KADA,EAAW,GAAG,EACd,EAAU,GAAM;AAChB,SAAI;AAOF,MALA,GADe,MAAM,EAAU,YAAY,EAAS,EAE3C,QACH,oCACA,kGACL,EACD,GAAc;aACR;AAEN,MADA,EAAU,GAAK,EACf,EACE,uGACD;;;cAGN;IAEQ,CAAA,CACR,EAAA,CAAA,GAjDH,kBAAC,KAAD,EAAA,UAAG,iGAGC,CAAA;GAgDL,KAAW,kBAAC,KAAD;IAAG,MAAM,IAAS,UAAU;cAAW;IAAY,CAAA;GACvD"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useBotlit as e } from "../providers/BotlitProvider.js";
|
|
2
|
+
import { useKnowledgeBases as t } from "../hooks/useKnowledgeBases.js";
|
|
3
|
+
import { Button as n } from "./Button.js";
|
|
4
|
+
import { useAssistantWebsites as r } from "../hooks/useAssistantWebsites.js";
|
|
5
|
+
import { useState as i } from "react";
|
|
6
|
+
import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
7
|
+
//#region src/botlit/components/AssistantWebsitePanel.tsx
|
|
8
|
+
function c(e, t, n) {
|
|
9
|
+
let r = new URL(e);
|
|
10
|
+
if (r.protocol !== "https:" || r.username || r.password || r.search || r.hash || !/^[a-f0-9-]{36}$/i.test(t) || !/^[a-f0-9-]{36}$/i.test(n)) throw Error("Invalid website widget address");
|
|
11
|
+
return `<script src="${r.origin}/api/assistant-web/widget.js" data-workspace="${t}" data-installation="${n}" defer><\/script>`;
|
|
12
|
+
}
|
|
13
|
+
function l({ agentId: t, knowledgeBaseIds: n }) {
|
|
14
|
+
let { workspaceId: r, currentUser: i } = e();
|
|
15
|
+
return /* @__PURE__ */ o(u, {
|
|
16
|
+
agentId: t,
|
|
17
|
+
knowledgeBaseIds: n
|
|
18
|
+
}, `${r}:${i?.id}:${t}:${n.join(",")}`);
|
|
19
|
+
}
|
|
20
|
+
function u({ agentId: l, knowledgeBaseIds: u }) {
|
|
21
|
+
let { apiGatewayUrl: d } = e(), f = r(l), p = t(), [m, h] = i("Website assistant"), [g, _] = i(""), [v, y] = i(!1), [b, x] = i(""), [S, C] = i(!1), w = d || (typeof window < "u" && window.location.hostname.startsWith("alpha") ? "https://alphagraphqlworkspaces.burdenoff.com" : "https://graphqlworkspaces.burdenoff.com"), T = u.map((e) => p.data?.find((t) => t.id === e)), E = (e, t = !1) => {
|
|
22
|
+
x(e), C(t);
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ s("section", {
|
|
25
|
+
className: "space-y-4 rounded-lg border border-border-default p-5",
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("h2", {
|
|
28
|
+
className: "text-lg font-semibold",
|
|
29
|
+
children: "Website assistant"
|
|
30
|
+
}), /* @__PURE__ */ o("p", {
|
|
31
|
+
className: "text-sm text-text-secondary",
|
|
32
|
+
children: "Publish a general assistant on approved websites. Configure its Fluidgrids runtime first. Private course teachers cannot be published."
|
|
33
|
+
})] }),
|
|
34
|
+
/* @__PURE__ */ s("label", {
|
|
35
|
+
className: "grid gap-2 text-sm font-medium",
|
|
36
|
+
children: ["Display name", /* @__PURE__ */ o("input", {
|
|
37
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
38
|
+
maxLength: 120,
|
|
39
|
+
value: m,
|
|
40
|
+
disabled: f.publish.isPending,
|
|
41
|
+
onChange: (e) => h(e.target.value)
|
|
42
|
+
})]
|
|
43
|
+
}),
|
|
44
|
+
/* @__PURE__ */ s("label", {
|
|
45
|
+
className: "grid gap-2 text-sm font-medium",
|
|
46
|
+
children: [
|
|
47
|
+
"Approved website origins",
|
|
48
|
+
/* @__PURE__ */ o("textarea", {
|
|
49
|
+
className: "rounded-md border border-border-default bg-bg-surface p-3",
|
|
50
|
+
rows: 3,
|
|
51
|
+
placeholder: "https://www.example.com",
|
|
52
|
+
value: g,
|
|
53
|
+
disabled: f.publish.isPending,
|
|
54
|
+
onChange: (e) => {
|
|
55
|
+
_(e.target.value), y(!1);
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ o("span", {
|
|
59
|
+
className: "font-normal text-text-secondary",
|
|
60
|
+
children: "One exact HTTPS origin per line, without a path or trailing slash."
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ s("div", {
|
|
65
|
+
className: "text-sm",
|
|
66
|
+
children: [/* @__PURE__ */ o("p", {
|
|
67
|
+
className: "font-medium",
|
|
68
|
+
children: "Knowledge made available to website visitors"
|
|
69
|
+
}), p.loading ? /* @__PURE__ */ o("p", {
|
|
70
|
+
role: "status",
|
|
71
|
+
children: "Loading knowledge sources…"
|
|
72
|
+
}) : T.some((e) => !e) ? /* @__PURE__ */ o("p", {
|
|
73
|
+
role: "alert",
|
|
74
|
+
children: "Some sources could not be reviewed. Reload the assistant before publishing."
|
|
75
|
+
}) : u.length ? /* @__PURE__ */ o("ul", {
|
|
76
|
+
className: "list-disc pl-5",
|
|
77
|
+
children: T.map((e) => e && /* @__PURE__ */ o("li", { children: e.name }, e.id))
|
|
78
|
+
}) : /* @__PURE__ */ o("p", { children: "This assistant has no attached knowledge sources." })]
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ s("label", {
|
|
81
|
+
className: "flex gap-2 text-sm",
|
|
82
|
+
children: [/* @__PURE__ */ o("input", {
|
|
83
|
+
type: "checkbox",
|
|
84
|
+
checked: v,
|
|
85
|
+
disabled: f.publish.isPending || p.loading || T.some((e) => !e),
|
|
86
|
+
onChange: (e) => y(e.target.checked)
|
|
87
|
+
}), "I approve the assistant’s prompt and every listed knowledge source for public website visitors."]
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ o(n, {
|
|
90
|
+
disabled: !v || !m.trim() || !g.trim() || u.length > 20,
|
|
91
|
+
isLoading: f.publish.isPending,
|
|
92
|
+
onClick: async () => {
|
|
93
|
+
E("");
|
|
94
|
+
try {
|
|
95
|
+
let e = g.split("\n").map((e) => e.trim()).filter(Boolean);
|
|
96
|
+
if (e.length > 20 || e.some((e) => {
|
|
97
|
+
try {
|
|
98
|
+
let t = new URL(e);
|
|
99
|
+
return t.protocol !== "https:" || e !== t.origin || !!(t.username || t.password) || t.hostname.includes("*");
|
|
100
|
+
} catch {
|
|
101
|
+
return !0;
|
|
102
|
+
}
|
|
103
|
+
})) {
|
|
104
|
+
E("Use up to twenty exact HTTPS origins.", !0);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await f.publish.mutateAsync({
|
|
108
|
+
name: m,
|
|
109
|
+
allowedOrigins: e,
|
|
110
|
+
approvedKnowledgeBaseIds: u
|
|
111
|
+
}), y(!1), E("Website publication created. Add its snippet to an approved website.");
|
|
112
|
+
} catch {
|
|
113
|
+
E("Publication failed. Check the assistant configuration, public knowledge approval and your publishing permission.", !0);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
children: "Publish website assistant"
|
|
117
|
+
}),
|
|
118
|
+
b && /* @__PURE__ */ o("p", {
|
|
119
|
+
role: S ? "alert" : "status",
|
|
120
|
+
children: b
|
|
121
|
+
}),
|
|
122
|
+
f.installations.isPending ? /* @__PURE__ */ o("p", {
|
|
123
|
+
role: "status",
|
|
124
|
+
children: "Loading website publications…"
|
|
125
|
+
}) : f.installations.isError ? /* @__PURE__ */ s("div", {
|
|
126
|
+
role: "alert",
|
|
127
|
+
children: [/* @__PURE__ */ o("p", { children: "Website publications could not be loaded. Publishing permission is required." }), /* @__PURE__ */ o(n, {
|
|
128
|
+
variant: "outline",
|
|
129
|
+
onClick: () => void f.installations.refetch(),
|
|
130
|
+
children: "Reload publications"
|
|
131
|
+
})]
|
|
132
|
+
}) : f.installations.data?.map((e) => {
|
|
133
|
+
let t = "";
|
|
134
|
+
try {
|
|
135
|
+
t = c(w, e.workspaceId, e.id);
|
|
136
|
+
} catch {}
|
|
137
|
+
return /* @__PURE__ */ s("div", {
|
|
138
|
+
className: "space-y-2 rounded-md border border-border-default p-3",
|
|
139
|
+
children: [
|
|
140
|
+
/* @__PURE__ */ s("p", {
|
|
141
|
+
className: "font-medium",
|
|
142
|
+
children: [
|
|
143
|
+
e.name,
|
|
144
|
+
" · ",
|
|
145
|
+
e.enabled ? "Enabled" : "Disabled"
|
|
146
|
+
]
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ o("p", {
|
|
149
|
+
className: "text-sm text-text-secondary",
|
|
150
|
+
children: e.allowedOrigins.join(", ")
|
|
151
|
+
}),
|
|
152
|
+
e.enabled && /* @__PURE__ */ s(a, { children: [t ? /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o("textarea", {
|
|
153
|
+
"aria-label": `Installation snippet for ${e.name}`,
|
|
154
|
+
readOnly: !0,
|
|
155
|
+
value: t,
|
|
156
|
+
rows: 3,
|
|
157
|
+
className: "w-full rounded-md border border-border-default bg-bg-sunken p-2 font-mono text-xs"
|
|
158
|
+
}), /* @__PURE__ */ o(n, {
|
|
159
|
+
variant: "outline",
|
|
160
|
+
onClick: async () => {
|
|
161
|
+
try {
|
|
162
|
+
await navigator.clipboard.writeText(t), E("Installation snippet copied.");
|
|
163
|
+
} catch {
|
|
164
|
+
E("Copy unavailable. Select and copy the snippet above.", !0);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
children: "Copy snippet"
|
|
168
|
+
})] }) : /* @__PURE__ */ o("p", {
|
|
169
|
+
role: "alert",
|
|
170
|
+
children: "The website widget address is unavailable."
|
|
171
|
+
}), /* @__PURE__ */ o(n, {
|
|
172
|
+
variant: "outline",
|
|
173
|
+
isLoading: f.disable.isPending,
|
|
174
|
+
onClick: async () => {
|
|
175
|
+
try {
|
|
176
|
+
await f.disable.mutateAsync(e.id), E("Publication disabled. Existing visitors can no longer receive answers.");
|
|
177
|
+
} catch {
|
|
178
|
+
E("Disable failed. Reload publications and try again.", !0);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
children: "Disable publication"
|
|
182
|
+
})] })
|
|
183
|
+
]
|
|
184
|
+
}, e.id);
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ o("p", {
|
|
187
|
+
className: "text-sm text-text-secondary",
|
|
188
|
+
children: "Changes to the assistant or its models revoke existing website sessions. Create a new publication after reviewing the updated configuration."
|
|
189
|
+
})
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
export { l as AssistantWebsitePanel };
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=AssistantWebsitePanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantWebsitePanel.js","names":[],"sources":["../../../src/botlit/components/AssistantWebsitePanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssistantWebsites } from '../hooks/useAssistantWebsites';\nimport { useKnowledgeBases } from '../hooks/useKnowledgeBases';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { Button } from './Button';\n\nexport function assistantWidgetSnippet(\n gateway: string,\n workspaceId: string,\n installationId: string\n): string {\n const url = new URL(gateway);\n if (\n url.protocol !== 'https:' ||\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n !/^[a-f0-9-]{36}$/i.test(workspaceId) ||\n !/^[a-f0-9-]{36}$/i.test(installationId)\n )\n throw new Error('Invalid website widget address');\n return `<script src=\"${url.origin}/api/assistant-web/widget.js\" data-workspace=\"${workspaceId}\" data-installation=\"${installationId}\" defer></script>`;\n}\nexport function AssistantWebsitePanel({\n agentId,\n knowledgeBaseIds,\n}: {\n agentId: string;\n knowledgeBaseIds: string[];\n}) {\n const { workspaceId, currentUser } = useBotlit();\n return (\n <ScopedWebsitePanel\n key={`${workspaceId}:${currentUser?.id}:${agentId}:${knowledgeBaseIds.join(',')}`}\n agentId={agentId}\n knowledgeBaseIds={knowledgeBaseIds}\n />\n );\n}\nfunction ScopedWebsitePanel({\n agentId,\n knowledgeBaseIds,\n}: {\n agentId: string;\n knowledgeBaseIds: string[];\n}) {\n const { apiGatewayUrl } = useBotlit(),\n websites = useAssistantWebsites(agentId),\n bases = useKnowledgeBases();\n const [name, setName] = useState('Website assistant'),\n [origins, setOrigins] = useState(''),\n [approved, setApproved] = useState(false),\n [message, setMessage] = useState(''),\n [failed, setFailed] = useState(false);\n const gateway =\n apiGatewayUrl ||\n (typeof window !== 'undefined' && window.location.hostname.startsWith('alpha')\n ? 'https://alphagraphqlworkspaces.burdenoff.com'\n : 'https://graphqlworkspaces.burdenoff.com');\n const sources = knowledgeBaseIds.map((id) => bases.data?.find((base) => base.id === id));\n const showMessage = (value: string, error = false) => {\n setMessage(value);\n setFailed(error);\n };\n return (\n <section className=\"space-y-4 rounded-lg border border-border-default p-5\">\n <div>\n <h2 className=\"text-lg font-semibold\">Website assistant</h2>\n <p className=\"text-sm text-text-secondary\">\n Publish a general assistant on approved websites. Configure its Fluidgrids runtime first.\n Private course teachers cannot be published.\n </p>\n </div>\n <label className=\"grid gap-2 text-sm font-medium\">\n Display name\n <input\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n maxLength={120}\n value={name}\n disabled={websites.publish.isPending}\n onChange={(event) => setName(event.target.value)}\n />\n </label>\n <label className=\"grid gap-2 text-sm font-medium\">\n Approved website origins\n <textarea\n className=\"rounded-md border border-border-default bg-bg-surface p-3\"\n rows={3}\n placeholder=\"https://www.example.com\"\n value={origins}\n disabled={websites.publish.isPending}\n onChange={(event) => {\n setOrigins(event.target.value);\n setApproved(false);\n }}\n />\n <span className=\"font-normal text-text-secondary\">\n One exact HTTPS origin per line, without a path or trailing slash.\n </span>\n </label>\n <div className=\"text-sm\">\n <p className=\"font-medium\">Knowledge made available to website visitors</p>\n {bases.loading ? (\n <p role=\"status\">Loading knowledge sources…</p>\n ) : sources.some((source) => !source) ? (\n <p role=\"alert\">\n Some sources could not be reviewed. Reload the assistant before publishing.\n </p>\n ) : knowledgeBaseIds.length ? (\n <ul className=\"list-disc pl-5\">\n {sources.map((source) => source && <li key={source.id}>{source.name}</li>)}\n </ul>\n ) : (\n <p>This assistant has no attached knowledge sources.</p>\n )}\n </div>\n <label className=\"flex gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={approved}\n disabled={\n websites.publish.isPending || bases.loading || sources.some((source) => !source)\n }\n onChange={(event) => setApproved(event.target.checked)}\n />\n I approve the assistant’s prompt and every listed knowledge source for public website\n visitors.\n </label>\n <Button\n disabled={!approved || !name.trim() || !origins.trim() || knowledgeBaseIds.length > 20}\n isLoading={websites.publish.isPending}\n onClick={async () => {\n showMessage('');\n try {\n const allowedOrigins = origins\n .split('\\n')\n .map((origin) => origin.trim())\n .filter(Boolean);\n if (\n allowedOrigins.length > 20 ||\n allowedOrigins.some((origin) => {\n try {\n const url = new URL(origin);\n return (\n url.protocol !== 'https:' ||\n origin !== url.origin ||\n Boolean(url.username || url.password) ||\n url.hostname.includes('*')\n );\n } catch {\n return true;\n }\n })\n ) {\n showMessage('Use up to twenty exact HTTPS origins.', true);\n return;\n }\n await websites.publish.mutateAsync({\n name,\n allowedOrigins,\n approvedKnowledgeBaseIds: knowledgeBaseIds,\n });\n setApproved(false);\n showMessage('Website publication created. Add its snippet to an approved website.');\n } catch {\n showMessage(\n 'Publication failed. Check the assistant configuration, public knowledge approval and your publishing permission.',\n true\n );\n }\n }}\n >\n Publish website assistant\n </Button>\n {message && <p role={failed ? 'alert' : 'status'}>{message}</p>}\n {websites.installations.isPending ? (\n <p role=\"status\">Loading website publications…</p>\n ) : websites.installations.isError ? (\n <div role=\"alert\">\n <p>Website publications could not be loaded. Publishing permission is required.</p>\n <Button variant=\"outline\" onClick={() => void websites.installations.refetch()}>\n Reload publications\n </Button>\n </div>\n ) : (\n websites.installations.data?.map((installation) => {\n let snippet = '';\n try {\n snippet = assistantWidgetSnippet(gateway, installation.workspaceId, installation.id);\n } catch {\n /* An invalid host configuration cannot produce an unsafe embed. */\n }\n return (\n <div\n key={installation.id}\n className=\"space-y-2 rounded-md border border-border-default p-3\"\n >\n <p className=\"font-medium\">\n {installation.name} · {installation.enabled ? 'Enabled' : 'Disabled'}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {installation.allowedOrigins.join(', ')}\n </p>\n {installation.enabled && (\n <>\n {snippet ? (\n <>\n <textarea\n aria-label={`Installation snippet for ${installation.name}`}\n readOnly\n value={snippet}\n rows={3}\n className=\"w-full rounded-md border border-border-default bg-bg-sunken p-2 font-mono text-xs\"\n />\n <Button\n variant=\"outline\"\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(snippet);\n showMessage('Installation snippet copied.');\n } catch {\n showMessage(\n 'Copy unavailable. Select and copy the snippet above.',\n true\n );\n }\n }}\n >\n Copy snippet\n </Button>\n </>\n ) : (\n <p role=\"alert\">The website widget address is unavailable.</p>\n )}\n <Button\n variant=\"outline\"\n isLoading={websites.disable.isPending}\n onClick={async () => {\n try {\n await websites.disable.mutateAsync(installation.id);\n showMessage(\n 'Publication disabled. Existing visitors can no longer receive answers.'\n );\n } catch {\n showMessage('Disable failed. Reload publications and try again.', true);\n }\n }}\n >\n Disable publication\n </Button>\n </>\n )}\n </div>\n );\n })\n )}\n <p className=\"text-sm text-text-secondary\">\n Changes to the assistant or its models revoke existing website sessions. Create a new\n publication after reviewing the updated configuration.\n </p>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { useBotlit as e } from "../providers/BotlitProvider.js";
|
|
2
|
+
import { Button as t } from "./Button.js";
|
|
3
|
+
import { useKnowledgeSourceImports as n } from "../hooks/useKnowledgeSourceImports.js";
|
|
4
|
+
import { useRef as r, useState as i } from "react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
//#region src/botlit/components/KnowledgeSourceImportPanel.tsx
|
|
7
|
+
function s({ knowledgeBaseId: t }) {
|
|
8
|
+
let { workspaceId: n, currentUser: r } = e();
|
|
9
|
+
return /* @__PURE__ */ a(c, { knowledgeBaseId: t }, `${n}:${r?.id}:${t}`);
|
|
10
|
+
}
|
|
11
|
+
function c({ knowledgeBaseId: e }) {
|
|
12
|
+
let s = n(e), [c, l] = i("website"), [u, d] = i(""), [f, p] = i(""), [m, h] = i(""), g = r(null);
|
|
13
|
+
return /* @__PURE__ */ o("section", {
|
|
14
|
+
className: "space-y-4 rounded-lg border border-border-default p-5",
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ a("h2", {
|
|
17
|
+
className: "text-lg font-semibold",
|
|
18
|
+
children: "Import website or text knowledge"
|
|
19
|
+
}),
|
|
20
|
+
/* @__PURE__ */ a("p", {
|
|
21
|
+
className: "text-sm text-text-secondary",
|
|
22
|
+
children: "Fluidgrids captures each source and checkpoints embedding batches. Course content uses the BuildMyIQ course import. Use a new knowledge base for the first managed import."
|
|
23
|
+
}),
|
|
24
|
+
/* @__PURE__ */ o("label", {
|
|
25
|
+
className: "grid gap-2 text-sm",
|
|
26
|
+
children: ["Source type", /* @__PURE__ */ o("select", {
|
|
27
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
28
|
+
value: c,
|
|
29
|
+
disabled: s.queue.isPending,
|
|
30
|
+
onChange: (e) => {
|
|
31
|
+
l(e.target.value), p("");
|
|
32
|
+
},
|
|
33
|
+
children: [/* @__PURE__ */ a("option", {
|
|
34
|
+
value: "website",
|
|
35
|
+
children: "Public website page"
|
|
36
|
+
}), /* @__PURE__ */ a("option", {
|
|
37
|
+
value: "text",
|
|
38
|
+
children: "Text or Markdown"
|
|
39
|
+
})]
|
|
40
|
+
})]
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ o("label", {
|
|
43
|
+
className: "grid gap-2 text-sm",
|
|
44
|
+
children: ["Title", /* @__PURE__ */ a("input", {
|
|
45
|
+
className: "h-10 rounded-md border border-border-default bg-bg-surface px-3",
|
|
46
|
+
value: u,
|
|
47
|
+
maxLength: 255,
|
|
48
|
+
disabled: s.queue.isPending,
|
|
49
|
+
onChange: (e) => d(e.target.value)
|
|
50
|
+
})]
|
|
51
|
+
}),
|
|
52
|
+
/* @__PURE__ */ o("label", {
|
|
53
|
+
className: "grid gap-2 text-sm",
|
|
54
|
+
children: [c === "website" ? "Exact public HTTPS page" : "Source text", /* @__PURE__ */ a("textarea", {
|
|
55
|
+
className: "rounded-md border border-border-default bg-bg-surface p-3",
|
|
56
|
+
rows: c === "website" ? 2 : 6,
|
|
57
|
+
value: f,
|
|
58
|
+
disabled: s.queue.isPending,
|
|
59
|
+
onChange: (e) => p(e.target.value)
|
|
60
|
+
})]
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ a("p", {
|
|
63
|
+
className: "text-sm text-text-secondary",
|
|
64
|
+
children: "Website imports capture one static page without signing in. Cross-origin redirects and embedded documents are rejected. Text sources must fit within 240 KiB; split larger sources."
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ a(t, {
|
|
67
|
+
disabled: !u.trim() || !f.trim(),
|
|
68
|
+
isLoading: s.queue.isPending,
|
|
69
|
+
onClick: async () => {
|
|
70
|
+
if (c === "text" && new TextEncoder().encode(f).length > 245760 || c === "website" && f.length > 2048) {
|
|
71
|
+
h("The source is too large. Split it into smaller sources.");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let e = JSON.stringify([
|
|
75
|
+
c,
|
|
76
|
+
u,
|
|
77
|
+
f
|
|
78
|
+
]);
|
|
79
|
+
g.current?.fingerprint !== e && (g.current = {
|
|
80
|
+
id: crypto.randomUUID(),
|
|
81
|
+
fingerprint: e
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
await s.queue.mutateAsync({
|
|
85
|
+
clientRequestId: g.current.id,
|
|
86
|
+
title: u,
|
|
87
|
+
...c === "website" ? { sourceUrl: f.trim() } : { content: f }
|
|
88
|
+
}), g.current = null, p(""), h("Source queued. Its progress will appear below.");
|
|
89
|
+
} catch {
|
|
90
|
+
h("Admission could not be confirmed. Retry the same source to reuse its request ID. Check the source, embedding configuration and management permission.");
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
children: "Queue import"
|
|
94
|
+
}),
|
|
95
|
+
m && /* @__PURE__ */ a("p", {
|
|
96
|
+
role: "status",
|
|
97
|
+
children: m
|
|
98
|
+
}),
|
|
99
|
+
s.imports.isError ? /* @__PURE__ */ a("p", {
|
|
100
|
+
role: "alert",
|
|
101
|
+
children: "Imports could not be loaded. Knowledge management permission is required."
|
|
102
|
+
}) : s.imports.isPending ? /* @__PURE__ */ a("p", {
|
|
103
|
+
role: "status",
|
|
104
|
+
children: "Loading imports…"
|
|
105
|
+
}) : s.imports.data?.map((e) => /* @__PURE__ */ o("div", {
|
|
106
|
+
className: "flex items-center justify-between gap-3 text-sm",
|
|
107
|
+
children: [/* @__PURE__ */ o("span", { children: [
|
|
108
|
+
e.title,
|
|
109
|
+
" · ",
|
|
110
|
+
e.status.toLowerCase()
|
|
111
|
+
] }), e.status === "BLOCKED" && /* @__PURE__ */ a(t, {
|
|
112
|
+
variant: "outline",
|
|
113
|
+
isLoading: s.retry.isPending,
|
|
114
|
+
onClick: async () => {
|
|
115
|
+
try {
|
|
116
|
+
await s.retry.mutateAsync(e.id), h("Import recovery queued.");
|
|
117
|
+
} catch {
|
|
118
|
+
h("Recovery failed. The embedding profile must still match the original import.");
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
children: "Retry import"
|
|
122
|
+
})]
|
|
123
|
+
}, e.id)),
|
|
124
|
+
/* @__PURE__ */ a(t, {
|
|
125
|
+
variant: "outline",
|
|
126
|
+
onClick: () => void s.imports.refetch(),
|
|
127
|
+
children: "Refresh imports"
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { s as KnowledgeSourceImportPanel };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=KnowledgeSourceImportPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KnowledgeSourceImportPanel.js","names":[],"sources":["../../../src/botlit/components/KnowledgeSourceImportPanel.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { useKnowledgeSourceImports } from '../hooks/useKnowledgeSourceImports';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { Button } from './Button';\n\nexport function KnowledgeSourceImportPanel({ knowledgeBaseId }: { knowledgeBaseId: string }) {\n const { workspaceId, currentUser } = useBotlit();\n return (\n <ScopedImportPanel\n key={`${workspaceId}:${currentUser?.id}:${knowledgeBaseId}`}\n knowledgeBaseId={knowledgeBaseId}\n />\n );\n}\nfunction ScopedImportPanel({ knowledgeBaseId }: { knowledgeBaseId: string }) {\n const sources = useKnowledgeSourceImports(knowledgeBaseId);\n const [kind, setKind] = useState('website'),\n [title, setTitle] = useState(''),\n [value, setValue] = useState(''),\n [message, setMessage] = useState('');\n const pending = useRef<{ id: string; fingerprint: string } | null>(null);\n return (\n <section className=\"space-y-4 rounded-lg border border-border-default p-5\">\n <h2 className=\"text-lg font-semibold\">Import website or text knowledge</h2>\n <p className=\"text-sm text-text-secondary\">\n Fluidgrids captures each source and checkpoints embedding batches. Course content uses the\n BuildMyIQ course import. Use a new knowledge base for the first managed import.\n </p>\n <label className=\"grid gap-2 text-sm\">\n Source type\n <select\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n value={kind}\n disabled={sources.queue.isPending}\n onChange={(event) => {\n setKind(event.target.value);\n setValue('');\n }}\n >\n <option value=\"website\">Public website page</option>\n <option value=\"text\">Text or Markdown</option>\n </select>\n </label>\n <label className=\"grid gap-2 text-sm\">\n Title\n <input\n className=\"h-10 rounded-md border border-border-default bg-bg-surface px-3\"\n value={title}\n maxLength={255}\n disabled={sources.queue.isPending}\n onChange={(event) => setTitle(event.target.value)}\n />\n </label>\n <label className=\"grid gap-2 text-sm\">\n {kind === 'website' ? 'Exact public HTTPS page' : 'Source text'}\n <textarea\n className=\"rounded-md border border-border-default bg-bg-surface p-3\"\n rows={kind === 'website' ? 2 : 6}\n value={value}\n disabled={sources.queue.isPending}\n onChange={(event) => setValue(event.target.value)}\n />\n </label>\n <p className=\"text-sm text-text-secondary\">\n Website imports capture one static page without signing in. Cross-origin redirects and\n embedded documents are rejected. Text sources must fit within 240 KiB; split larger sources.\n </p>\n <Button\n disabled={!title.trim() || !value.trim()}\n isLoading={sources.queue.isPending}\n onClick={async () => {\n if (\n (kind === 'text' && new TextEncoder().encode(value).length > 245760) ||\n (kind === 'website' && value.length > 2048)\n ) {\n setMessage('The source is too large. Split it into smaller sources.');\n return;\n }\n const fingerprint = JSON.stringify([kind, title, value]);\n if (pending.current?.fingerprint !== fingerprint)\n pending.current = { id: crypto.randomUUID(), fingerprint };\n try {\n await sources.queue.mutateAsync({\n clientRequestId: pending.current.id,\n title,\n ...(kind === 'website' ? { sourceUrl: value.trim() } : { content: value }),\n });\n pending.current = null;\n setValue('');\n setMessage('Source queued. Its progress will appear below.');\n } catch {\n setMessage(\n 'Admission could not be confirmed. Retry the same source to reuse its request ID. Check the source, embedding configuration and management permission.'\n );\n }\n }}\n >\n Queue import\n </Button>\n {message && <p role=\"status\">{message}</p>}\n {sources.imports.isError ? (\n <p role=\"alert\">\n Imports could not be loaded. Knowledge management permission is required.\n </p>\n ) : sources.imports.isPending ? (\n <p role=\"status\">Loading imports…</p>\n ) : (\n sources.imports.data?.map((row) => (\n <div key={row.id} className=\"flex items-center justify-between gap-3 text-sm\">\n <span>\n {row.title} · {row.status.toLowerCase()}\n </span>\n {row.status === 'BLOCKED' && (\n <Button\n variant=\"outline\"\n isLoading={sources.retry.isPending}\n onClick={async () => {\n try {\n await sources.retry.mutateAsync(row.id);\n setMessage('Import recovery queued.');\n } catch {\n setMessage(\n 'Recovery failed. The embedding profile must still match the original import.'\n );\n }\n }}\n >\n Retry import\n </Button>\n )}\n </div>\n ))\n )}\n <Button variant=\"outline\" onClick={() => void sources.imports.refetch()}>\n Refresh imports\n </Button>\n </section>\n );\n}\n"],"mappings":"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"}
|