@burdenoff/microfe-botmanor 2026.1031.1 → 2026.1101.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/pages/PromptStudioPage.js +88 -87
- package/dist/botlit/pages/PromptStudioPage.js.map +1 -1
- package/dist/botlit/pages/PromptTemplateDetailPage.js +42 -41
- package/dist/botlit/pages/PromptTemplateDetailPage.js.map +1 -1
- package/dist/botlit/utils/errors.js +18 -0
- package/dist/botlit/utils/errors.js.map +1 -0
- package/package.json +1 -1
|
@@ -12,13 +12,14 @@ import { FormField as p, Input as m, Textarea as h } from "../components/Input.j
|
|
|
12
12
|
import { ErrorState as g } from "../components/ErrorState.js";
|
|
13
13
|
import { promptTemplateVariables as _, usePromptTemplates as v, useRenderPromptTemplate as y } from "../hooks/usePromptTemplates.js";
|
|
14
14
|
import "../hooks/index.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
15
|
+
import { graphqlErrorMessage as b } from "../utils/errors.js";
|
|
16
|
+
import { useEffect as x, useMemo as S, useState as C } from "react";
|
|
17
|
+
import { GlassCard as w, PagePurpose as T } from "@burdenoff/fe-libs/ui";
|
|
18
|
+
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
19
|
+
import { Play as O, Plus as k, Tag as A } from "lucide-react";
|
|
20
|
+
import { toast as j } from "sonner";
|
|
20
21
|
//#region src/botlit/pages/PromptStudioPage.tsx
|
|
21
|
-
function
|
|
22
|
+
function M(e) {
|
|
22
23
|
if (!e || typeof e != "object" || Array.isArray(e)) return null;
|
|
23
24
|
let t = e, n = typeof t.name == "string" ? t.name : void 0;
|
|
24
25
|
return n ? {
|
|
@@ -29,91 +30,91 @@ function j(e) {
|
|
|
29
30
|
defaultValue: t.defaultValue
|
|
30
31
|
} : null;
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
return e ? _(e).map(
|
|
33
|
+
function N(e) {
|
|
34
|
+
return e ? _(e).map(M).filter((e) => e !== null) : [];
|
|
34
35
|
}
|
|
35
|
-
var
|
|
36
|
-
let { navigateTo: _ } = e(), { data:
|
|
37
|
-
|
|
38
|
-
!
|
|
39
|
-
}, [
|
|
40
|
-
let
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
templateId:
|
|
44
|
-
variables:
|
|
36
|
+
var P = () => {
|
|
37
|
+
let { navigateTo: _ } = e(), { data: M, loading: P, error: F, refetch: I } = v(), { render: L, loading: R } = y(), [z, B] = C(null), [V, H] = C({}), [U, W] = C("");
|
|
38
|
+
x(() => {
|
|
39
|
+
!z && M.length > 0 && B(M[0].id);
|
|
40
|
+
}, [M, z]);
|
|
41
|
+
let G = S(() => M.find((e) => e.id === z), [M, z]), K = S(() => N(G), [G]), q = async () => {
|
|
42
|
+
if (G) try {
|
|
43
|
+
W(await L({
|
|
44
|
+
templateId: G.id,
|
|
45
|
+
variables: V
|
|
45
46
|
}));
|
|
46
47
|
} catch (e) {
|
|
47
|
-
|
|
48
|
+
j.error(b(e, "Failed to render prompt"));
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
|
-
return /* @__PURE__ */
|
|
51
|
+
return /* @__PURE__ */ E(i, {
|
|
51
52
|
title: "Prompt Studio",
|
|
52
53
|
description: "Create, test, and manage prompts for your AI agents",
|
|
53
|
-
actions: /* @__PURE__ */
|
|
54
|
+
actions: /* @__PURE__ */ E(r, {
|
|
54
55
|
permission: n.PROMPT_TEMPLATE_CREATE,
|
|
55
|
-
children: /* @__PURE__ */
|
|
56
|
-
leftIcon: /* @__PURE__ */
|
|
56
|
+
children: /* @__PURE__ */ E(a, {
|
|
57
|
+
leftIcon: /* @__PURE__ */ E(k, { size: 16 }),
|
|
57
58
|
onClick: () => _("prompt-templates/new"),
|
|
58
59
|
children: "New Prompt"
|
|
59
60
|
})
|
|
60
61
|
}),
|
|
61
|
-
children: /* @__PURE__ */
|
|
62
|
+
children: /* @__PURE__ */ D("div", {
|
|
62
63
|
className: "space-y-6",
|
|
63
|
-
children: [/* @__PURE__ */ T
|
|
64
|
-
icon: /* @__PURE__ */
|
|
64
|
+
children: [/* @__PURE__ */ E(T, {
|
|
65
|
+
icon: /* @__PURE__ */ E(O, { size: 18 }),
|
|
65
66
|
children: "Prompt Studio is your interactive workbench for writing and testing prompts before they go live. Pick a prompt, edit its text and variables on the left, then fill in sample values and hit Run Test to preview exactly what the agent will receive — so you can iterate on wording and catch mistakes here instead of in production."
|
|
66
|
-
}), /* @__PURE__ */
|
|
67
|
+
}), /* @__PURE__ */ D("div", {
|
|
67
68
|
className: "grid grid-cols-12 gap-6",
|
|
68
69
|
children: [
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
+
/* @__PURE__ */ E("div", {
|
|
70
71
|
className: "col-span-3",
|
|
71
|
-
children: /* @__PURE__ */
|
|
72
|
+
children: /* @__PURE__ */ D(o, { children: [/* @__PURE__ */ E(l, { children: /* @__PURE__ */ E(u, {
|
|
72
73
|
className: "text-base",
|
|
73
74
|
children: "Prompts"
|
|
74
|
-
}) }), /* @__PURE__ */
|
|
75
|
+
}) }), /* @__PURE__ */ E(s, {
|
|
75
76
|
className: "p-0",
|
|
76
|
-
children:
|
|
77
|
+
children: P ? /* @__PURE__ */ E("div", {
|
|
77
78
|
className: "space-y-2 p-4",
|
|
78
79
|
children: [
|
|
79
80
|
1,
|
|
80
81
|
2,
|
|
81
82
|
3
|
|
82
|
-
].map((e) => /* @__PURE__ */
|
|
83
|
-
}) :
|
|
83
|
+
].map((e) => /* @__PURE__ */ E("div", { className: "h-14 bg-bg-sunken motion-safe:animate-vc-shimmer rounded" }, e))
|
|
84
|
+
}) : F ? /* @__PURE__ */ E(g, {
|
|
84
85
|
resource: "prompt templates",
|
|
85
|
-
error:
|
|
86
|
-
onRetry: () => void
|
|
86
|
+
error: F,
|
|
87
|
+
onRetry: () => void I(),
|
|
87
88
|
className: "m-4"
|
|
88
|
-
}) :
|
|
89
|
+
}) : M.length === 0 ? /* @__PURE__ */ E("p", {
|
|
89
90
|
className: "p-4 text-sm text-text-secondary",
|
|
90
91
|
children: "No prompt templates yet. Create one to start iterating."
|
|
91
|
-
}) : /* @__PURE__ */
|
|
92
|
+
}) : /* @__PURE__ */ E("div", {
|
|
92
93
|
className: "divide-y divide-border-subtle",
|
|
93
|
-
children:
|
|
94
|
+
children: M.map((e) => /* @__PURE__ */ D("button", {
|
|
94
95
|
type: "button",
|
|
95
|
-
className: `w-full text-left p-4 hover:bg-accent transition-colors ${
|
|
96
|
+
className: `w-full text-left p-4 hover:bg-accent transition-colors ${G?.id === e.id ? "bg-accent" : ""}`,
|
|
96
97
|
onClick: () => {
|
|
97
|
-
|
|
98
|
+
B(e.id), H({}), W("");
|
|
98
99
|
},
|
|
99
100
|
children: [
|
|
100
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ E("p", {
|
|
101
102
|
className: "font-medium truncate",
|
|
102
103
|
children: e.name
|
|
103
104
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
+
/* @__PURE__ */ D("p", {
|
|
105
106
|
className: "text-xs text-text-secondary mt-1 truncate",
|
|
106
107
|
children: ["v", e.version]
|
|
107
108
|
}),
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ D("div", {
|
|
109
110
|
className: "flex items-center gap-2 mt-2",
|
|
110
|
-
children: [e.visibility === "PUBLIC" && /* @__PURE__ */
|
|
111
|
+
children: [e.visibility === "PUBLIC" && /* @__PURE__ */ E(d, {
|
|
111
112
|
variant: "secondary",
|
|
112
113
|
className: "text-[10px]",
|
|
113
114
|
children: "Public"
|
|
114
|
-
}), /* @__PURE__ */
|
|
115
|
+
}), /* @__PURE__ */ D("span", {
|
|
115
116
|
className: "text-xs text-text-secondary",
|
|
116
|
-
children: [
|
|
117
|
+
children: [N(e).length, " vars"]
|
|
117
118
|
})]
|
|
118
119
|
})
|
|
119
120
|
]
|
|
@@ -121,115 +122,115 @@ var N = () => {
|
|
|
121
122
|
})
|
|
122
123
|
})] })
|
|
123
124
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ E("div", {
|
|
125
126
|
className: "col-span-6",
|
|
126
|
-
children:
|
|
127
|
+
children: G ? /* @__PURE__ */ D(w, {
|
|
127
128
|
treatment: "glass",
|
|
128
129
|
glow: !0,
|
|
129
130
|
className: "overflow-hidden",
|
|
130
|
-
children: [/* @__PURE__ */
|
|
131
|
+
children: [/* @__PURE__ */ D(l, {
|
|
131
132
|
className: "flex flex-row items-center justify-between",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
133
|
+
children: [/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E(u, { children: G.name }), /* @__PURE__ */ E(c, { children: G.description })] }), /* @__PURE__ */ E(a, {
|
|
133
134
|
size: "sm",
|
|
134
135
|
variant: "outline",
|
|
135
|
-
onClick: () => _(`prompt-templates/${
|
|
136
|
+
onClick: () => _(`prompt-templates/${G.id}`),
|
|
136
137
|
children: "Open template"
|
|
137
138
|
})]
|
|
138
|
-
}), /* @__PURE__ */
|
|
139
|
+
}), /* @__PURE__ */ D(s, {
|
|
139
140
|
className: "space-y-4",
|
|
140
141
|
children: [
|
|
141
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ D("div", {
|
|
142
143
|
className: "flex items-center gap-2",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ E(A, {
|
|
144
145
|
size: 14,
|
|
145
146
|
className: "text-text-secondary"
|
|
146
|
-
}),
|
|
147
|
+
}), G.tags.map((e) => /* @__PURE__ */ E(d, {
|
|
147
148
|
variant: "secondary",
|
|
148
149
|
children: e
|
|
149
150
|
}, e))]
|
|
150
151
|
}),
|
|
151
|
-
/* @__PURE__ */
|
|
152
|
+
/* @__PURE__ */ E(p, {
|
|
152
153
|
label: "Prompt Content",
|
|
153
|
-
children: /* @__PURE__ */
|
|
154
|
-
value:
|
|
154
|
+
children: /* @__PURE__ */ E(h, {
|
|
155
|
+
value: G.templateText,
|
|
155
156
|
rows: 15,
|
|
156
157
|
readOnly: !0,
|
|
157
158
|
className: "font-mono text-sm"
|
|
158
159
|
})
|
|
159
160
|
}),
|
|
160
|
-
|
|
161
|
+
K.length > 0 && /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("p", {
|
|
161
162
|
className: "text-sm font-medium mb-2",
|
|
162
163
|
children: "Variables"
|
|
163
|
-
}), /* @__PURE__ */
|
|
164
|
+
}), /* @__PURE__ */ E("div", {
|
|
164
165
|
className: "space-y-2",
|
|
165
|
-
children:
|
|
166
|
+
children: K.map((e) => /* @__PURE__ */ D("div", {
|
|
166
167
|
className: "flex items-center gap-4 p-3 bg-bg-sunken rounded-lg",
|
|
167
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ D("div", {
|
|
168
169
|
className: "flex-1",
|
|
169
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ D("p", {
|
|
170
171
|
className: "font-medium text-sm",
|
|
171
|
-
children: [`{{${e.name}}}`, e.required && /* @__PURE__ */
|
|
172
|
+
children: [`{{${e.name}}}`, e.required && /* @__PURE__ */ E("span", {
|
|
172
173
|
className: "text-status-error-text ml-1",
|
|
173
174
|
children: "*"
|
|
174
175
|
})]
|
|
175
|
-
}), e.description && /* @__PURE__ */
|
|
176
|
+
}), e.description && /* @__PURE__ */ E("p", {
|
|
176
177
|
className: "text-xs text-text-secondary",
|
|
177
178
|
children: e.description
|
|
178
179
|
})]
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
+
}), /* @__PURE__ */ E(d, {
|
|
180
181
|
variant: "outline",
|
|
181
182
|
className: "capitalize",
|
|
182
183
|
children: e.type
|
|
183
184
|
})]
|
|
184
185
|
}, e.name))
|
|
185
186
|
})] }),
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ D("div", {
|
|
187
188
|
className: "pt-4 text-xs text-text-secondary",
|
|
188
|
-
children: ["Updated ", t(
|
|
189
|
+
children: ["Updated ", t(G.updatedAt)]
|
|
189
190
|
})
|
|
190
191
|
]
|
|
191
192
|
})]
|
|
192
|
-
}) : /* @__PURE__ */
|
|
193
|
+
}) : /* @__PURE__ */ E(f, {
|
|
193
194
|
illustration: "onboarding",
|
|
194
195
|
title: "Select a prompt",
|
|
195
196
|
description: "Choose a prompt from the list to view and edit"
|
|
196
197
|
})
|
|
197
198
|
}),
|
|
198
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ E("div", {
|
|
199
200
|
className: "col-span-3",
|
|
200
|
-
children: /* @__PURE__ */
|
|
201
|
+
children: /* @__PURE__ */ D(o, { children: [/* @__PURE__ */ E(l, { children: /* @__PURE__ */ D(u, {
|
|
201
202
|
className: "text-base flex items-center gap-2",
|
|
202
|
-
children: [/* @__PURE__ */
|
|
203
|
-
}) }), /* @__PURE__ */
|
|
203
|
+
children: [/* @__PURE__ */ E(O, { size: 16 }), "Test Prompt"]
|
|
204
|
+
}) }), /* @__PURE__ */ D(s, {
|
|
204
205
|
className: "space-y-4",
|
|
205
206
|
children: [
|
|
206
|
-
|
|
207
|
+
K.map((e) => /* @__PURE__ */ E(p, {
|
|
207
208
|
label: e.name,
|
|
208
|
-
children: /* @__PURE__ */
|
|
209
|
+
children: /* @__PURE__ */ E(m, {
|
|
209
210
|
placeholder: e.description || `Enter ${e.name}`,
|
|
210
|
-
value:
|
|
211
|
-
onChange: (t) =>
|
|
212
|
-
...
|
|
211
|
+
value: V[e.name] || "",
|
|
212
|
+
onChange: (t) => H({
|
|
213
|
+
...V,
|
|
213
214
|
[e.name]: t.target.value
|
|
214
215
|
})
|
|
215
216
|
})
|
|
216
217
|
}, e.name)),
|
|
217
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ E(r, {
|
|
218
219
|
permission: n.PROMPT_TEMPLATE_EXECUTE,
|
|
219
|
-
children: /* @__PURE__ */
|
|
220
|
+
children: /* @__PURE__ */ E(a, {
|
|
220
221
|
className: "w-full",
|
|
221
|
-
onClick: () => void
|
|
222
|
-
isLoading:
|
|
223
|
-
disabled: !
|
|
222
|
+
onClick: () => void q(),
|
|
223
|
+
isLoading: R,
|
|
224
|
+
disabled: !G,
|
|
224
225
|
children: "Run Test"
|
|
225
226
|
})
|
|
226
227
|
}),
|
|
227
|
-
|
|
228
|
+
U && /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("p", {
|
|
228
229
|
className: "text-sm font-medium mb-2",
|
|
229
230
|
children: "Output"
|
|
230
|
-
}), /* @__PURE__ */
|
|
231
|
+
}), /* @__PURE__ */ E("div", {
|
|
231
232
|
className: "p-3 bg-bg-sunken rounded-lg text-sm whitespace-pre-wrap font-mono max-h-64 overflow-auto",
|
|
232
|
-
children:
|
|
233
|
+
children: U
|
|
233
234
|
})] })
|
|
234
235
|
]
|
|
235
236
|
})] })
|
|
@@ -240,6 +241,6 @@ var N = () => {
|
|
|
240
241
|
});
|
|
241
242
|
};
|
|
242
243
|
//#endregion
|
|
243
|
-
export {
|
|
244
|
+
export { P as PromptStudioPage };
|
|
244
245
|
|
|
245
246
|
//# sourceMappingURL=PromptStudioPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptStudioPage.js","names":[],"sources":["../../../src/botlit/pages/PromptStudioPage.tsx"],"sourcesContent":["/**\n * Prompt Studio Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Plus, Play, Tag } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { Badge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n usePromptTemplates,\n useRenderPromptTemplate,\n promptTemplateVariables,\n type PromptTemplateRecord,\n} from '../hooks';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { PromptVariable } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\n/** Normalises one entry of a template's free-form `variables` JSON. */\nfunction toPromptVariable(value: unknown): PromptVariable | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n const name = typeof record.name === 'string' ? record.name : undefined;\n if (!name) return null;\n const type = typeof record.type === 'string' ? record.type : 'string';\n return {\n name,\n type: type as PromptVariable['type'],\n description: typeof record.description === 'string' ? record.description : undefined,\n required: record.required === true,\n defaultValue: record.defaultValue,\n };\n}\n\nfunction templateVariables(template: PromptTemplateRecord | undefined): PromptVariable[] {\n if (!template) return [];\n return promptTemplateVariables(template)\n .map(toPromptVariable)\n .filter((variable): variable is PromptVariable => variable !== null);\n}\n\nexport const PromptStudioPage: FC = () => {\n const { navigateTo } = useBotlit();\n const { data: templates, loading, error, refetch } = usePromptTemplates();\n const { render, loading: isRunning } = useRenderPromptTemplate();\n\n const [selectedPromptId, setSelectedPromptId] = useState<string | null>(null);\n const [testVariables, setTestVariables] = useState<Record<string, string>>({});\n const [testOutput, setTestOutput] = useState('');\n\n // Select the first template once the list arrives.\n useEffect(() => {\n if (!selectedPromptId && templates.length > 0) {\n setSelectedPromptId(templates[0].id);\n }\n }, [templates, selectedPromptId]);\n\n const selectedPrompt = useMemo(\n () => templates.find((template) => template.id === selectedPromptId),\n [templates, selectedPromptId]\n );\n\n const selectedVariables = useMemo(() => templateVariables(selectedPrompt), [selectedPrompt]);\n\n const handleTest = async () => {\n if (!selectedPrompt) return;\n try {\n // Rendered by wspace-botlit-svc so the preview matches what the agent\n // actually receives — never a client-side string substitution.\n const output = await render({\n templateId: selectedPrompt.id,\n variables: testVariables,\n });\n setTestOutput(output);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to render prompt');\n }\n };\n\n return (\n <PageLayout\n title=\"Prompt Studio\"\n description=\"Create, test, and manage prompts for your AI agents\"\n actions={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_CREATE}>\n <Button leftIcon={<Plus size={16} />} onClick={() => navigateTo('prompt-templates/new')}>\n New Prompt\n </Button>\n </BotmanorPermissionGate>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Play size={18} />}>\n Prompt Studio is your interactive workbench for writing and testing prompts before they go\n live. Pick a prompt, edit its text and variables on the left, then fill in sample values\n and hit Run Test to preview exactly what the agent will receive — so you can iterate on\n wording and catch mistakes here instead of in production.\n </PagePurpose>\n\n <div className=\"grid grid-cols-12 gap-6\">\n {/* Prompt List */}\n <div className=\"col-span-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Prompts</CardTitle>\n </CardHeader>\n <CardContent className=\"p-0\">\n {loading ? (\n <div className=\"space-y-2 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-14 bg-bg-sunken motion-safe:animate-vc-shimmer rounded\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState\n resource=\"prompt templates\"\n error={error}\n onRetry={() => void refetch()}\n className=\"m-4\"\n />\n ) : templates.length === 0 ? (\n <p className=\"p-4 text-sm text-text-secondary\">\n No prompt templates yet. Create one to start iterating.\n </p>\n ) : (\n <div className=\"divide-y divide-border-subtle\">\n {templates.map((prompt: PromptTemplateRecord) => (\n <button\n key={prompt.id}\n type=\"button\"\n className={`w-full text-left p-4 hover:bg-accent transition-colors ${\n selectedPrompt?.id === prompt.id ? 'bg-accent' : ''\n }`}\n onClick={() => {\n setSelectedPromptId(prompt.id);\n setTestVariables({});\n setTestOutput('');\n }}\n >\n <p className=\"font-medium truncate\">{prompt.name}</p>\n <p className=\"text-xs text-text-secondary mt-1 truncate\">\n v{prompt.version}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n {prompt.visibility === 'PUBLIC' && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n Public\n </Badge>\n )}\n <span className=\"text-xs text-text-secondary\">\n {templateVariables(prompt).length} vars\n </span>\n </div>\n </button>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Editor */}\n <div className=\"col-span-6\">\n {selectedPrompt ? (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle>{selectedPrompt.name}</CardTitle>\n <CardDescription>{selectedPrompt.description}</CardDescription>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigateTo(`prompt-templates/${selectedPrompt.id}`)}\n >\n Open template\n </Button>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Tags */}\n <div className=\"flex items-center gap-2\">\n <Tag size={14} className=\"text-text-secondary\" />\n {selectedPrompt.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\">\n {tag}\n </Badge>\n ))}\n </div>\n\n {/* Content */}\n <FormField label=\"Prompt Content\">\n <Textarea\n value={selectedPrompt.templateText}\n rows={15}\n readOnly\n className=\"font-mono text-sm\"\n />\n </FormField>\n\n {/* Variables */}\n {selectedVariables.length > 0 && (\n <div>\n <p className=\"text-sm font-medium mb-2\">Variables</p>\n <div className=\"space-y-2\">\n {selectedVariables.map((variable: PromptVariable) => (\n <div\n key={variable.name}\n className=\"flex items-center gap-4 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex-1\">\n <p className=\"font-medium text-sm\">\n {`{{${variable.name}}}`}\n {variable.required && (\n <span className=\"text-status-error-text ml-1\">*</span>\n )}\n </p>\n {variable.description && (\n <p className=\"text-xs text-text-secondary\">\n {variable.description}\n </p>\n )}\n </div>\n <Badge variant=\"outline\" className=\"capitalize\">\n {variable.type}\n </Badge>\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 text-xs text-text-secondary\">\n Updated {formatRelativeTime(selectedPrompt.updatedAt)}\n </div>\n </CardContent>\n </GlassCard>\n ) : (\n <EmptyState\n illustration=\"onboarding\"\n title=\"Select a prompt\"\n description=\"Choose a prompt from the list to view and edit\"\n />\n )}\n </div>\n\n {/* Test Panel */}\n <div className=\"col-span-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Play size={16} />\n Test Prompt\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {selectedVariables.map((variable: PromptVariable) => (\n <FormField key={variable.name} label={variable.name}>\n <Input\n placeholder={variable.description || `Enter ${variable.name}`}\n value={testVariables[variable.name] || ''}\n onChange={(e) =>\n setTestVariables({ ...testVariables, [variable.name]: e.target.value })\n }\n />\n </FormField>\n ))}\n\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_EXECUTE}>\n <Button\n className=\"w-full\"\n onClick={() => void handleTest()}\n isLoading={isRunning}\n disabled={!selectedPrompt}\n >\n Run Test\n </Button>\n </BotmanorPermissionGate>\n\n {testOutput && (\n <div>\n <p className=\"text-sm font-medium mb-2\">Output</p>\n <div className=\"p-3 bg-bg-sunken rounded-lg text-sm whitespace-pre-wrap font-mono max-h-64 overflow-auto\">\n {testOutput}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PromptStudioPage.js","names":[],"sources":["../../../src/botlit/pages/PromptStudioPage.tsx"],"sourcesContent":["/**\n * Prompt Studio Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Plus, Play, Tag } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { Badge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n usePromptTemplates,\n useRenderPromptTemplate,\n promptTemplateVariables,\n type PromptTemplateRecord,\n} from '../hooks';\nimport { graphqlErrorMessage } from '../utils/errors';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { PromptVariable } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\n/** Normalises one entry of a template's free-form `variables` JSON. */\nfunction toPromptVariable(value: unknown): PromptVariable | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n const name = typeof record.name === 'string' ? record.name : undefined;\n if (!name) return null;\n const type = typeof record.type === 'string' ? record.type : 'string';\n return {\n name,\n type: type as PromptVariable['type'],\n description: typeof record.description === 'string' ? record.description : undefined,\n required: record.required === true,\n defaultValue: record.defaultValue,\n };\n}\n\nfunction templateVariables(template: PromptTemplateRecord | undefined): PromptVariable[] {\n if (!template) return [];\n return promptTemplateVariables(template)\n .map(toPromptVariable)\n .filter((variable): variable is PromptVariable => variable !== null);\n}\n\nexport const PromptStudioPage: FC = () => {\n const { navigateTo } = useBotlit();\n const { data: templates, loading, error, refetch } = usePromptTemplates();\n const { render, loading: isRunning } = useRenderPromptTemplate();\n\n const [selectedPromptId, setSelectedPromptId] = useState<string | null>(null);\n const [testVariables, setTestVariables] = useState<Record<string, string>>({});\n const [testOutput, setTestOutput] = useState('');\n\n // Select the first template once the list arrives.\n useEffect(() => {\n if (!selectedPromptId && templates.length > 0) {\n setSelectedPromptId(templates[0].id);\n }\n }, [templates, selectedPromptId]);\n\n const selectedPrompt = useMemo(\n () => templates.find((template) => template.id === selectedPromptId),\n [templates, selectedPromptId]\n );\n\n const selectedVariables = useMemo(() => templateVariables(selectedPrompt), [selectedPrompt]);\n\n const handleTest = async () => {\n if (!selectedPrompt) return;\n try {\n // Rendered by wspace-botlit-svc so the preview matches what the agent\n // actually receives — never a client-side string substitution.\n const output = await render({\n templateId: selectedPrompt.id,\n variables: testVariables,\n });\n setTestOutput(output);\n } catch (err) {\n toast.error(graphqlErrorMessage(err, 'Failed to render prompt'));\n }\n };\n\n return (\n <PageLayout\n title=\"Prompt Studio\"\n description=\"Create, test, and manage prompts for your AI agents\"\n actions={\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_CREATE}>\n <Button leftIcon={<Plus size={16} />} onClick={() => navigateTo('prompt-templates/new')}>\n New Prompt\n </Button>\n </BotmanorPermissionGate>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Play size={18} />}>\n Prompt Studio is your interactive workbench for writing and testing prompts before they go\n live. Pick a prompt, edit its text and variables on the left, then fill in sample values\n and hit Run Test to preview exactly what the agent will receive — so you can iterate on\n wording and catch mistakes here instead of in production.\n </PagePurpose>\n\n <div className=\"grid grid-cols-12 gap-6\">\n {/* Prompt List */}\n <div className=\"col-span-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Prompts</CardTitle>\n </CardHeader>\n <CardContent className=\"p-0\">\n {loading ? (\n <div className=\"space-y-2 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-14 bg-bg-sunken motion-safe:animate-vc-shimmer rounded\"\n />\n ))}\n </div>\n ) : error ? (\n <ErrorState\n resource=\"prompt templates\"\n error={error}\n onRetry={() => void refetch()}\n className=\"m-4\"\n />\n ) : templates.length === 0 ? (\n <p className=\"p-4 text-sm text-text-secondary\">\n No prompt templates yet. Create one to start iterating.\n </p>\n ) : (\n <div className=\"divide-y divide-border-subtle\">\n {templates.map((prompt: PromptTemplateRecord) => (\n <button\n key={prompt.id}\n type=\"button\"\n className={`w-full text-left p-4 hover:bg-accent transition-colors ${\n selectedPrompt?.id === prompt.id ? 'bg-accent' : ''\n }`}\n onClick={() => {\n setSelectedPromptId(prompt.id);\n setTestVariables({});\n setTestOutput('');\n }}\n >\n <p className=\"font-medium truncate\">{prompt.name}</p>\n <p className=\"text-xs text-text-secondary mt-1 truncate\">\n v{prompt.version}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n {prompt.visibility === 'PUBLIC' && (\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n Public\n </Badge>\n )}\n <span className=\"text-xs text-text-secondary\">\n {templateVariables(prompt).length} vars\n </span>\n </div>\n </button>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Editor */}\n <div className=\"col-span-6\">\n {selectedPrompt ? (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle>{selectedPrompt.name}</CardTitle>\n <CardDescription>{selectedPrompt.description}</CardDescription>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigateTo(`prompt-templates/${selectedPrompt.id}`)}\n >\n Open template\n </Button>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {/* Tags */}\n <div className=\"flex items-center gap-2\">\n <Tag size={14} className=\"text-text-secondary\" />\n {selectedPrompt.tags.map((tag) => (\n <Badge key={tag} variant=\"secondary\">\n {tag}\n </Badge>\n ))}\n </div>\n\n {/* Content */}\n <FormField label=\"Prompt Content\">\n <Textarea\n value={selectedPrompt.templateText}\n rows={15}\n readOnly\n className=\"font-mono text-sm\"\n />\n </FormField>\n\n {/* Variables */}\n {selectedVariables.length > 0 && (\n <div>\n <p className=\"text-sm font-medium mb-2\">Variables</p>\n <div className=\"space-y-2\">\n {selectedVariables.map((variable: PromptVariable) => (\n <div\n key={variable.name}\n className=\"flex items-center gap-4 p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex-1\">\n <p className=\"font-medium text-sm\">\n {`{{${variable.name}}}`}\n {variable.required && (\n <span className=\"text-status-error-text ml-1\">*</span>\n )}\n </p>\n {variable.description && (\n <p className=\"text-xs text-text-secondary\">\n {variable.description}\n </p>\n )}\n </div>\n <Badge variant=\"outline\" className=\"capitalize\">\n {variable.type}\n </Badge>\n </div>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"pt-4 text-xs text-text-secondary\">\n Updated {formatRelativeTime(selectedPrompt.updatedAt)}\n </div>\n </CardContent>\n </GlassCard>\n ) : (\n <EmptyState\n illustration=\"onboarding\"\n title=\"Select a prompt\"\n description=\"Choose a prompt from the list to view and edit\"\n />\n )}\n </div>\n\n {/* Test Panel */}\n <div className=\"col-span-3\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Play size={16} />\n Test Prompt\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {selectedVariables.map((variable: PromptVariable) => (\n <FormField key={variable.name} label={variable.name}>\n <Input\n placeholder={variable.description || `Enter ${variable.name}`}\n value={testVariables[variable.name] || ''}\n onChange={(e) =>\n setTestVariables({ ...testVariables, [variable.name]: e.target.value })\n }\n />\n </FormField>\n ))}\n\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_EXECUTE}>\n <Button\n className=\"w-full\"\n onClick={() => void handleTest()}\n isLoading={isRunning}\n disabled={!selectedPrompt}\n >\n Run Test\n </Button>\n </BotmanorPermissionGate>\n\n {testOutput && (\n <div>\n <p className=\"text-sm font-medium mb-2\">Output</p>\n <div className=\"p-3 bg-bg-sunken rounded-lg text-sm whitespace-pre-wrap font-mono max-h-64 overflow-auto\">\n {testOutput}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -13,11 +13,12 @@ import { FormField as m, Input as h, Textarea as g } from "../components/Input.j
|
|
|
13
13
|
import { toUnion as _ } from "../utils/unions.js";
|
|
14
14
|
import { promptTemplateExamples as v, promptTemplateVariables as ae, useCreatePromptTemplate as oe, useDeletePromptTemplate as se, usePromptTemplate as ce, useRenderPromptTemplate as le, useUpdatePromptTemplate as ue } from "../hooks/usePromptTemplates.js";
|
|
15
15
|
import "../hooks/index.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { graphqlErrorMessage as de } from "../utils/errors.js";
|
|
17
|
+
import { useMemo as fe, useState as y } from "react";
|
|
18
|
+
import { PagePurpose as pe } from "@burdenoff/fe-libs/ui";
|
|
18
19
|
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
19
|
-
import { useParams as
|
|
20
|
-
import { ArrowLeft as
|
|
20
|
+
import { useParams as me } from "react-router";
|
|
21
|
+
import { ArrowLeft as he, Check as ge, Code as C, Copy as _e, Edit3 as ve, Eye as w, FileText as T, Play as E, Save as D, Settings as ye, Trash2 as O, Variable as k } from "lucide-react";
|
|
21
22
|
import { toast as A } from "sonner";
|
|
22
23
|
//#region src/botlit/pages/PromptTemplateDetailPage.tsx
|
|
23
24
|
var j = [
|
|
@@ -26,12 +27,12 @@ var j = [
|
|
|
26
27
|
"boolean",
|
|
27
28
|
"array",
|
|
28
29
|
"object"
|
|
29
|
-
],
|
|
30
|
+
], be = [
|
|
30
31
|
"PRIVATE",
|
|
31
32
|
"WORKSPACE",
|
|
32
33
|
"PUBLIC"
|
|
33
34
|
], M = ({ isNew: M }) => {
|
|
34
|
-
let { templateId:
|
|
35
|
+
let { templateId: xe } = me(), { navigateTo: N } = e(), { data: P, loading: Se } = ce(M ? void 0 : xe), { create: Ce, loading: we } = oe(), { update: Te, loading: Ee } = ue(), { remove: De, loading: Oe } = se(), { render: ke, loading: Ae } = le(), [F, I] = y("edit"), [L, R] = y(!1), [z, je] = y({}), [B, V] = y(null), [H, U] = y(null), [W, G] = y(!1), [Me, K] = y(!1), [q, J] = y({
|
|
35
36
|
name: "",
|
|
36
37
|
slug: "",
|
|
37
38
|
description: "",
|
|
@@ -39,7 +40,7 @@ var j = [
|
|
|
39
40
|
templateText: "",
|
|
40
41
|
variables: [],
|
|
41
42
|
exampleInputs: [{}]
|
|
42
|
-
}), Y =
|
|
43
|
+
}), Y = fe(() => {
|
|
43
44
|
let e = M ? q.templateText : P?.templateText || "", t = /\{\{(\w+)\}\}/g, n = /* @__PURE__ */ new Set(), r;
|
|
44
45
|
for (; (r = t.exec(e)) !== null;) n.add(r[1]);
|
|
45
46
|
return Array.from(n);
|
|
@@ -47,10 +48,10 @@ var j = [
|
|
|
47
48
|
M,
|
|
48
49
|
q.templateText,
|
|
49
50
|
P?.templateText
|
|
50
|
-
]),
|
|
51
|
+
]), Ne = async () => {
|
|
51
52
|
if (!(!q.name || !q.slug)) {
|
|
52
53
|
try {
|
|
53
|
-
await
|
|
54
|
+
await Ce({
|
|
54
55
|
name: q.name,
|
|
55
56
|
slug: q.slug,
|
|
56
57
|
description: q.description,
|
|
@@ -65,7 +66,7 @@ var j = [
|
|
|
65
66
|
}
|
|
66
67
|
A.success(`Template "${q.name}" created`), N("prompt-templates");
|
|
67
68
|
}
|
|
68
|
-
},
|
|
69
|
+
}, Pe = () => {
|
|
69
70
|
J({
|
|
70
71
|
...q,
|
|
71
72
|
variables: [...q.variables, {
|
|
@@ -84,40 +85,40 @@ var j = [
|
|
|
84
85
|
...q,
|
|
85
86
|
variables: n
|
|
86
87
|
});
|
|
87
|
-
},
|
|
88
|
+
}, Fe = (e) => {
|
|
88
89
|
J({
|
|
89
90
|
...q,
|
|
90
91
|
variables: q.variables.filter((t, n) => n !== e)
|
|
91
92
|
});
|
|
92
|
-
},
|
|
93
|
+
}, Ie = async () => {
|
|
93
94
|
if (P) {
|
|
94
95
|
U(null);
|
|
95
96
|
try {
|
|
96
|
-
V(await
|
|
97
|
+
V(await ke({
|
|
97
98
|
templateId: P.id,
|
|
98
99
|
variables: z
|
|
99
100
|
}));
|
|
100
101
|
} catch (e) {
|
|
101
|
-
let t = e
|
|
102
|
+
let t = de(e, "Failed to render prompt template");
|
|
102
103
|
V(null), U(t), A.error(t);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
|
-
},
|
|
106
|
+
}, Le = async () => {
|
|
106
107
|
await te(P?.templateText || q.templateText), R(!0), f() && re("Copied"), setTimeout(() => R(!1), 2e3);
|
|
107
|
-
},
|
|
108
|
+
}, Re = () => {
|
|
108
109
|
P && (J({
|
|
109
110
|
name: P.name,
|
|
110
111
|
slug: P.slug,
|
|
111
112
|
description: P.description || "",
|
|
112
|
-
visibility: _(P.visibility,
|
|
113
|
+
visibility: _(P.visibility, be, "PRIVATE"),
|
|
113
114
|
templateText: P.templateText,
|
|
114
115
|
variables: ae(P),
|
|
115
116
|
exampleInputs: v(P)
|
|
116
117
|
}), G(!0));
|
|
117
|
-
},
|
|
118
|
+
}, ze = async () => {
|
|
118
119
|
if (!(!P || !q.name || !q.slug)) {
|
|
119
120
|
try {
|
|
120
|
-
await
|
|
121
|
+
await Te({
|
|
121
122
|
templateId: P.id,
|
|
122
123
|
input: {
|
|
123
124
|
name: q.name,
|
|
@@ -135,9 +136,9 @@ var j = [
|
|
|
135
136
|
}
|
|
136
137
|
A.success("Template updated"), G(!1);
|
|
137
138
|
}
|
|
138
|
-
},
|
|
139
|
+
}, Be = async () => {
|
|
139
140
|
if (P) try {
|
|
140
|
-
await
|
|
141
|
+
await De(P.id), f() && p("success"), N("prompt-templates");
|
|
141
142
|
} catch (e) {
|
|
142
143
|
throw f() && p("error"), e;
|
|
143
144
|
}
|
|
@@ -153,8 +154,8 @@ var j = [
|
|
|
153
154
|
children: "Cancel"
|
|
154
155
|
}), /* @__PURE__ */ x(a, {
|
|
155
156
|
leftIcon: /* @__PURE__ */ x(D, { size: 16 }),
|
|
156
|
-
onClick:
|
|
157
|
-
isLoading:
|
|
157
|
+
onClick: Ne,
|
|
158
|
+
isLoading: we,
|
|
158
159
|
disabled: !q.name || !q.slug,
|
|
159
160
|
children: "Create Template"
|
|
160
161
|
})]
|
|
@@ -267,7 +268,7 @@ var j = [
|
|
|
267
268
|
}), /* @__PURE__ */ x(a, {
|
|
268
269
|
variant: "outline",
|
|
269
270
|
size: "sm",
|
|
270
|
-
onClick:
|
|
271
|
+
onClick: Pe,
|
|
271
272
|
children: "Add Variable"
|
|
272
273
|
})]
|
|
273
274
|
}), /* @__PURE__ */ x(c, { children: "Define the variables used in your template" })] }), /* @__PURE__ */ x(s, { children: q.variables.length > 0 ? /* @__PURE__ */ x("div", {
|
|
@@ -323,7 +324,7 @@ var j = [
|
|
|
323
324
|
variant: "ghost",
|
|
324
325
|
size: "sm",
|
|
325
326
|
className: "text-status-error-text",
|
|
326
|
-
onClick: () =>
|
|
327
|
+
onClick: () => Fe(t),
|
|
327
328
|
children: "Remove"
|
|
328
329
|
})]
|
|
329
330
|
})
|
|
@@ -385,7 +386,7 @@ var j = [
|
|
|
385
386
|
})]
|
|
386
387
|
})
|
|
387
388
|
});
|
|
388
|
-
if (
|
|
389
|
+
if (Se) return /* @__PURE__ */ x(i, {
|
|
389
390
|
title: "Loading...",
|
|
390
391
|
children: /* @__PURE__ */ x("div", { className: "h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg" })
|
|
391
392
|
});
|
|
@@ -410,7 +411,7 @@ var j = [
|
|
|
410
411
|
variant: "ghost",
|
|
411
412
|
size: "icon",
|
|
412
413
|
onClick: () => N("prompt-templates"),
|
|
413
|
-
children: /* @__PURE__ */ x(
|
|
414
|
+
children: /* @__PURE__ */ x(he, { size: 20 })
|
|
414
415
|
}),
|
|
415
416
|
/* @__PURE__ */ x("div", {
|
|
416
417
|
className: "p-2 rounded-lg bg-action-primary-bg/10",
|
|
@@ -441,8 +442,8 @@ var j = [
|
|
|
441
442
|
className: "flex gap-2",
|
|
442
443
|
children: [/* @__PURE__ */ x(a, {
|
|
443
444
|
variant: "outline",
|
|
444
|
-
leftIcon: x(L ?
|
|
445
|
-
onClick:
|
|
445
|
+
leftIcon: x(L ? ge : _e, { size: 16 }),
|
|
446
|
+
onClick: Le,
|
|
446
447
|
children: L ? "Copied!" : "Copy"
|
|
447
448
|
}), W ? /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(a, {
|
|
448
449
|
variant: "outline",
|
|
@@ -450,11 +451,11 @@ var j = [
|
|
|
450
451
|
children: "Cancel"
|
|
451
452
|
}), /* @__PURE__ */ x(a, {
|
|
452
453
|
leftIcon: /* @__PURE__ */ x(D, { size: 16 }),
|
|
453
|
-
onClick:
|
|
454
|
-
isLoading:
|
|
454
|
+
onClick: ze,
|
|
455
|
+
isLoading: Ee,
|
|
455
456
|
disabled: !q.name || !q.slug,
|
|
456
457
|
children: "Save Changes"
|
|
457
|
-
})] }) :
|
|
458
|
+
})] }) : Me ? /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(a, {
|
|
458
459
|
variant: "outline",
|
|
459
460
|
onClick: () => K(!1),
|
|
460
461
|
children: "Cancel"
|
|
@@ -462,16 +463,16 @@ var j = [
|
|
|
462
463
|
variant: "destructive",
|
|
463
464
|
leftIcon: /* @__PURE__ */ x(O, { size: 16 }),
|
|
464
465
|
onClick: () => {
|
|
465
|
-
ne("medium"),
|
|
466
|
+
ne("medium"), Be();
|
|
466
467
|
},
|
|
467
|
-
isLoading:
|
|
468
|
+
isLoading: Oe,
|
|
468
469
|
children: "Confirm Delete"
|
|
469
470
|
})] }) : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(r, {
|
|
470
471
|
permission: n.PROMPT_TEMPLATE_UPDATE,
|
|
471
472
|
children: /* @__PURE__ */ x(a, {
|
|
472
473
|
variant: "outline",
|
|
473
|
-
leftIcon: /* @__PURE__ */ x(
|
|
474
|
-
onClick:
|
|
474
|
+
leftIcon: /* @__PURE__ */ x(ve, { size: 16 }),
|
|
475
|
+
onClick: Re,
|
|
475
476
|
children: "Edit Template"
|
|
476
477
|
})
|
|
477
478
|
}), /* @__PURE__ */ x(r, {
|
|
@@ -487,7 +488,7 @@ var j = [
|
|
|
487
488
|
children: /* @__PURE__ */ S("div", {
|
|
488
489
|
className: "space-y-6",
|
|
489
490
|
children: [
|
|
490
|
-
/* @__PURE__ */ S(
|
|
491
|
+
/* @__PURE__ */ S(pe, {
|
|
491
492
|
icon: /* @__PURE__ */ x(T, { size: 18 }),
|
|
492
493
|
children: [
|
|
493
494
|
"This is a single reusable prompt template — its content with",
|
|
@@ -738,7 +739,7 @@ var j = [
|
|
|
738
739
|
children: [/* @__PURE__ */ x(h, {
|
|
739
740
|
placeholder: e.defaultValue ? String(e.defaultValue) : `Enter ${e.name}`,
|
|
740
741
|
value: z[e.name] || "",
|
|
741
|
-
onChange: (t) =>
|
|
742
|
+
onChange: (t) => je({
|
|
742
743
|
...z,
|
|
743
744
|
[e.name]: t.target.value
|
|
744
745
|
})
|
|
@@ -751,8 +752,8 @@ var j = [
|
|
|
751
752
|
children: /* @__PURE__ */ x(a, {
|
|
752
753
|
className: "w-full",
|
|
753
754
|
leftIcon: /* @__PURE__ */ x(E, { size: 16 }),
|
|
754
|
-
onClick:
|
|
755
|
-
isLoading:
|
|
755
|
+
onClick: Ie,
|
|
756
|
+
isLoading: Ae,
|
|
756
757
|
children: "Render Template"
|
|
757
758
|
})
|
|
758
759
|
})]
|
|
@@ -792,7 +793,7 @@ var j = [
|
|
|
792
793
|
}) })] }),
|
|
793
794
|
/* @__PURE__ */ S(o, { children: [/* @__PURE__ */ x(l, { children: /* @__PURE__ */ S(u, {
|
|
794
795
|
className: "flex items-center gap-2",
|
|
795
|
-
children: [/* @__PURE__ */ x(
|
|
796
|
+
children: [/* @__PURE__ */ x(ye, { size: 18 }), "Metadata"]
|
|
796
797
|
}) }), /* @__PURE__ */ x(s, { children: /* @__PURE__ */ S("div", {
|
|
797
798
|
className: "grid grid-cols-2 md:grid-cols-4 gap-6 text-sm",
|
|
798
799
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptTemplateDetailPage.js","names":[],"sources":["../../../src/botlit/pages/PromptTemplateDetailPage.tsx"],"sourcesContent":["/**\n * Prompt Template Detail Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState, useMemo } from 'react';\nimport { useParams } from 'react-router';\nimport { toast } from 'sonner';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n isNative,\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\nimport {\n ArrowLeft,\n FileText,\n Variable,\n Play,\n Save,\n Copy,\n Check,\n Settings,\n Code,\n Eye,\n Edit3,\n Trash2,\n} from 'lucide-react';\nimport { PageLayout } from '../components/PageLayout';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { EmptyState } from '../components/EmptyState';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { StatusBadge, Badge } from '../components/Badge';\nimport {\n usePromptTemplate,\n useCreatePromptTemplate,\n useUpdatePromptTemplate,\n useDeletePromptTemplate,\n useRenderPromptTemplate,\n} from '../hooks';\nimport {\n promptTemplateExamples,\n promptTemplateVariables,\n type PromptTemplateVariable,\n} from '../hooks/usePromptTemplates';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { toUnion } from '../utils/unions';\nimport type { Visibility } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\ninterface PromptTemplateDetailPageProps {\n isNew?: boolean;\n}\n\nconst VARIABLE_TYPES = ['string', 'number', 'boolean', 'array', 'object'] as const;\n\nconst VISIBILITIES: Visibility[] = ['PRIVATE', 'WORKSPACE', 'PUBLIC'];\n\nexport const PromptTemplateDetailPage: FC<PromptTemplateDetailPageProps> = ({ isNew }) => {\n const { templateId } = useParams();\n const { navigateTo } = useBotlit();\n const { data: template, loading } = usePromptTemplate(isNew ? undefined : templateId);\n const { create: createTemplate, loading: createLoading } = useCreatePromptTemplate();\n const { update: updateTemplate, loading: updateLoading } = useUpdatePromptTemplate();\n const { remove: deleteTemplate, loading: deleteLoading } = useDeletePromptTemplate();\n const { render: renderTemplate, loading: renderLoading } = useRenderPromptTemplate();\n\n const [activeTab, setActiveTab] = useState<'edit' | 'preview' | 'test'>('edit');\n const [copied, setCopied] = useState(false);\n const [testInputs, setTestInputs] = useState<Record<string, string>>({});\n const [renderedOutput, setRenderedOutput] = useState<string | null>(null);\n const [renderError, setRenderError] = useState<string | null>(null);\n const [isEditing, setIsEditing] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const [formData, setFormData] = useState({\n name: '',\n slug: '',\n description: '',\n visibility: 'PRIVATE' as Visibility,\n templateText: '',\n variables: [] as PromptTemplateVariable[],\n exampleInputs: [{}] as Record<string, unknown>[],\n });\n\n // Extract variables from template text\n const detectedVariables = useMemo(() => {\n const text = isNew ? formData.templateText : template?.templateText || '';\n const regex = /\\{\\{(\\w+)\\}\\}/g;\n const matches = new Set<string>();\n let match;\n while ((match = regex.exec(text)) !== null) {\n matches.add(match[1]);\n }\n return Array.from(matches);\n }, [isNew, formData.templateText, template?.templateText]);\n\n const handleCreate = async () => {\n if (!formData.name || !formData.slug) return;\n try {\n await createTemplate({\n name: formData.name,\n slug: formData.slug,\n description: formData.description,\n visibility: formData.visibility,\n templateText: formData.templateText,\n variables: formData.variables,\n exampleInputs: formData.exampleInputs,\n });\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to create prompt template');\n return;\n }\n toast.success(`Template \"${formData.name}\" created`);\n navigateTo('prompt-templates');\n };\n\n const handleAddVariable = () => {\n setFormData({\n ...formData,\n variables: [\n ...formData.variables,\n { name: '', type: 'string', required: true, defaultValue: undefined },\n ],\n });\n };\n\n const handleUpdateVariable = (index: number, updates: Partial<PromptTemplateVariable>) => {\n const newVariables = [...formData.variables];\n newVariables[index] = { ...newVariables[index], ...updates };\n setFormData({ ...formData, variables: newVariables });\n };\n\n const handleRemoveVariable = (index: number) => {\n setFormData({\n ...formData,\n variables: formData.variables.filter((_, i) => i !== index),\n });\n };\n\n const handleTestRender = async () => {\n if (!template) return;\n setRenderError(null);\n try {\n const result = await renderTemplate({\n templateId: template.id,\n variables: testInputs,\n });\n setRenderedOutput(result);\n } catch (err) {\n // The service enforces the declared variable contract (missing required\n // variables, unknown keys, residual placeholders) — surface that error\n // here instead of dying as an unhandled rejection.\n const message = err instanceof Error ? err.message : 'Failed to render prompt template';\n setRenderedOutput(null);\n setRenderError(message);\n toast.error(message);\n }\n };\n\n const handleCopyTemplate = async () => {\n const text = template?.templateText || formData.templateText;\n await nativeCopyText(text);\n setCopied(true);\n if (isNative()) void nativeToast('Copied');\n setTimeout(() => setCopied(false), 2000);\n };\n\n const startEditing = () => {\n if (template) {\n setFormData({\n name: template.name,\n slug: template.slug,\n description: template.description || '',\n visibility: toUnion(template.visibility, VISIBILITIES, 'PRIVATE'),\n templateText: template.templateText,\n variables: promptTemplateVariables(template),\n exampleInputs: promptTemplateExamples(template),\n });\n setIsEditing(true);\n }\n };\n\n const handleUpdate = async () => {\n if (!template || !formData.name || !formData.slug) return;\n try {\n await updateTemplate({\n templateId: template.id,\n input: {\n name: formData.name,\n slug: formData.slug,\n description: formData.description,\n visibility: formData.visibility,\n templateText: formData.templateText,\n variables: formData.variables,\n exampleInputs: formData.exampleInputs,\n },\n });\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update prompt template');\n return;\n }\n toast.success('Template updated');\n setIsEditing(false);\n };\n\n const handleDelete = async () => {\n if (!template) return;\n try {\n await deleteTemplate(template.id);\n if (isNative()) void nativeNotify('success');\n navigateTo('prompt-templates');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n };\n\n const highlightVariables = (text: string) => {\n return text.replace(\n /\\{\\{(\\w+)\\}\\}/g,\n '<span class=\"bg-action-primary-bg/20 text-primary px-1 rounded font-mono\">{{$1}}</span>'\n );\n };\n\n if (isNew) {\n return (\n <PageLayout\n title=\"Create Prompt Template\"\n description=\"Define a reusable prompt template with variables\"\n actions={\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigateTo('prompt-templates')}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleCreate}\n isLoading={createLoading}\n disabled={!formData.name || !formData.slug}\n >\n Create Template\n </Button>\n </div>\n }\n >\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Left: Form */}\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Template Name\" required>\n <Input\n placeholder=\"e.g., Technical Support Template\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField\n label=\"Slug\"\n required\n description=\"Unique identifier (lowercase, dashes)\"\n >\n <Input\n placeholder=\"e.g., technical-support\"\n value={formData.slug}\n onChange={(e) =>\n setFormData({\n ...formData,\n slug: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n })\n }\n />\n </FormField>\n </div>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What is this template used for?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={2}\n />\n </FormField>\n\n <FormField label=\"Visibility\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.visibility}\n onChange={(e) =>\n setFormData({ ...formData, visibility: e.target.value as Visibility })\n }\n >\n <option value=\"PRIVATE\">Private</option>\n <option value=\"WORKSPACE\">Workspace</option>\n <option value=\"PUBLIC\">Public</option>\n </select>\n </FormField>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Code size={18} />\n Template Content\n </CardTitle>\n <CardDescription>Use {'{{variable_name}}'} syntax for variables</CardDescription>\n </CardHeader>\n <CardContent>\n <Textarea\n className=\"font-mono text-sm min-h-[300px]\"\n placeholder={`You are a helpful assistant for {{company_name}}.\n\nYour role is to help users with {{task_type}}.\n\nGuidelines:\n- Be helpful and friendly\n- Provide accurate information\n- Reference: {{docs_url}}`}\n value={formData.templateText}\n onChange={(e) => setFormData({ ...formData, templateText: e.target.value })}\n />\n {detectedVariables.length > 0 && (\n <div className=\"mt-3 flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Detected variables:</span>\n {detectedVariables.map((v) => (\n <Badge key={v} variant=\"secondary\" className=\"font-mono\">\n {v}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <Variable size={18} />\n Variables\n </span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleAddVariable}>\n Add Variable\n </Button>\n </CardTitle>\n <CardDescription>Define the variables used in your template</CardDescription>\n </CardHeader>\n <CardContent>\n {formData.variables.length > 0 ? (\n <div className=\"space-y-4\">\n {formData.variables.map((variable, index) => (\n <div key={index} className=\"p-4 border rounded-lg bg-bg-sunken/30 space-y-3\">\n <div className=\"grid grid-cols-4 gap-3\">\n <FormField label=\"Name\">\n <Input\n className=\"font-mono\"\n placeholder=\"variable_name\"\n value={variable.name}\n onChange={(e) =>\n handleUpdateVariable(index, { name: e.target.value })\n }\n />\n </FormField>\n <FormField label=\"Type\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={variable.type}\n onChange={(e) =>\n handleUpdateVariable(index, {\n type: toUnion(e.target.value, VARIABLE_TYPES, 'string'),\n })\n }\n >\n {VARIABLE_TYPES.map((type) => (\n <option key={type} value={type}>\n {type}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Default Value\">\n <Input\n placeholder=\"Optional\"\n value={String(variable.defaultValue || '')}\n onChange={(e) =>\n handleUpdateVariable(index, {\n defaultValue: e.target.value || undefined,\n })\n }\n />\n </FormField>\n <div className=\"flex items-end gap-2\">\n <label className=\"flex items-center gap-2 h-10\">\n <input\n type=\"checkbox\"\n checked={variable.required}\n onChange={(e) =>\n handleUpdateVariable(index, { required: e.target.checked })\n }\n className=\"h-4 w-4\"\n />\n <span className=\"text-sm\">Required</span>\n </label>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-status-error-text\"\n onClick={() => handleRemoveVariable(index)}\n >\n Remove\n </Button>\n </div>\n </div>\n <FormField label=\"Description\">\n <Input\n placeholder=\"What is this variable for?\"\n value={variable.description || ''}\n onChange={(e) =>\n handleUpdateVariable(index, { description: e.target.value })\n }\n />\n </FormField>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Variable className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No variables defined yet</p>\n <p className=\"text-sm\">\n Variables are automatically detected from {'{{variable}}'} syntax\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right: Preview */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Eye size={18} />\n Preview\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div\n className=\"p-4 bg-bg-sunken rounded-lg text-sm whitespace-pre-wrap font-mono\"\n dangerouslySetInnerHTML={{\n __html: highlightVariables(formData.templateText || 'Enter template text...'),\n }}\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Quick Tips</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm space-y-2\">\n <p>\n <strong>Variables:</strong> Use {'{{name}}'} syntax\n </p>\n <p>\n <strong>Best Practices:</strong>\n </p>\n <ul className=\"list-disc list-inside text-text-secondary space-y-1\">\n <li>Use descriptive variable names</li>\n <li>Provide default values when possible</li>\n <li>Include example inputs for testing</li>\n <li>Version your templates for changes</li>\n </ul>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n if (loading) {\n return (\n <PageLayout title=\"Loading...\">\n <div className=\"h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </PageLayout>\n );\n }\n\n if (!template) {\n return (\n <PageLayout title=\"Template Not Found\">\n <EmptyState\n illustration=\"empty-files\"\n title=\"Template Not Found\"\n description=\"This prompt template doesn't exist.\"\n action={<Button onClick={() => navigateTo('prompt-templates')}>Back to Templates</Button>}\n />\n </PageLayout>\n );\n }\n\n // `variables`/`exampleInputs` are free-form JSON columns — normalise once.\n const templateVariables = promptTemplateVariables(template);\n const templateExamples = promptTemplateExamples(template);\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('prompt-templates')}>\n <ArrowLeft size={20} />\n </Button>\n <div className=\"p-2 rounded-lg bg-action-primary-bg/10\">\n <FileText className=\"h-6 w-6 text-primary\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2\">\n <span>{template.name}</span>\n <StatusBadge status={template.status} />\n <Badge variant=\"secondary\" className=\"font-mono\">\n v{template.version}\n </Badge>\n </div>\n <p className=\"text-sm font-normal text-text-secondary\">\n {template.slug} - {template.description}\n </p>\n </div>\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={copied ? <Check size={16} /> : <Copy size={16} />}\n onClick={handleCopyTemplate}\n >\n {copied ? 'Copied!' : 'Copy'}\n </Button>\n {isEditing ? (\n <>\n <Button variant=\"outline\" onClick={() => setIsEditing(false)}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleUpdate}\n isLoading={updateLoading}\n disabled={!formData.name || !formData.slug}\n >\n Save Changes\n </Button>\n </>\n ) : showDeleteConfirm ? (\n <>\n <Button variant=\"outline\" onClick={() => setShowDeleteConfirm(false)}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n leftIcon={<Trash2 size={16} />}\n onClick={() => {\n nativeImpact('medium');\n void handleDelete();\n }}\n isLoading={deleteLoading}\n >\n Confirm Delete\n </Button>\n </>\n ) : (\n <>\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_UPDATE}>\n <Button variant=\"outline\" leftIcon={<Edit3 size={16} />} onClick={startEditing}>\n Edit Template\n </Button>\n </BotmanorPermissionGate>\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_DELETE}>\n <Button\n variant=\"outline\"\n leftIcon={<Trash2 size={16} />}\n onClick={() => setShowDeleteConfirm(true)}\n >\n Delete\n </Button>\n </BotmanorPermissionGate>\n </>\n )}\n </div>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<FileText size={18} />}>\n This is a single reusable prompt template — its content with{' '}\n <code>{'{{variables}}'}</code>, its declared variables and example inputs, and its version\n history. Edit and preview it here to perfect the instructions, then reuse it across agents\n so they share consistent, version-controlled behaviour.\n </PagePurpose>\n\n {/* Edit Form */}\n {isEditing && (\n <Card>\n <CardHeader>\n <CardTitle>Edit Template</CardTitle>\n <CardDescription>Update template details and content</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Template Name\" required>\n <Input\n placeholder=\"e.g., Technical Support Template\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField\n label=\"Slug\"\n required\n description=\"Unique identifier (lowercase, dashes)\"\n >\n <Input\n placeholder=\"e.g., technical-support\"\n value={formData.slug}\n onChange={(e) =>\n setFormData({\n ...formData,\n slug: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n })\n }\n />\n </FormField>\n </div>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What is this template used for?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={2}\n />\n </FormField>\n\n <FormField label=\"Visibility\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.visibility}\n onChange={(e) =>\n setFormData({ ...formData, visibility: e.target.value as Visibility })\n }\n >\n <option value=\"PRIVATE\">Private</option>\n <option value=\"WORKSPACE\">Workspace</option>\n <option value=\"PUBLIC\">Public</option>\n </select>\n </FormField>\n\n <FormField\n label=\"Template Content\"\n description=\"Use {{variable_name}} syntax for variables\"\n >\n <Textarea\n className=\"font-mono text-sm min-h-[200px]\"\n value={formData.templateText}\n onChange={(e) => setFormData({ ...formData, templateText: e.target.value })}\n />\n </FormField>\n\n {detectedVariables.length > 0 && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Detected variables:</span>\n {detectedVariables.map((v) => (\n <Badge key={v} variant=\"secondary\" className=\"font-mono\">\n {v}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n )}\n\n {/* Stats */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card treatment=\"glass\" glow>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{templateVariables.length}</p>\n <p className=\"text-sm text-text-secondary\">Variables</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{templateExamples.length}</p>\n <p className=\"text-sm text-text-secondary\">Examples</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{template.revision}</p>\n <p className=\"text-sm text-text-secondary\">Revision</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-lg font-bold text-text-primary capitalize\">\n {template.visibility.toLowerCase()}\n </p>\n <p className=\"text-sm text-text-secondary\">Visibility</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-2 border-b\">\n <Button\n variant={activeTab === 'edit' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('edit')}\n >\n <Code className=\"h-4 w-4 mr-2\" />\n Template\n </Button>\n <Button\n variant={activeTab === 'preview' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('preview')}\n >\n <Eye className=\"h-4 w-4 mr-2\" />\n Preview\n </Button>\n <Button\n variant={activeTab === 'test' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('test')}\n >\n <Play className=\"h-4 w-4 mr-2\" />\n Test\n </Button>\n </div>\n\n {/* Tab Content */}\n {activeTab === 'edit' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Template Content</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n <div\n dangerouslySetInnerHTML={{\n __html: highlightVariables(template.templateText),\n }}\n />\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Variable size={18} />\n Variables ({templateVariables.length})\n </CardTitle>\n </CardHeader>\n <CardContent>\n {templateVariables.length > 0 ? (\n <div className=\"space-y-3\">\n {templateVariables.map(\n (\n v: {\n name: string;\n type: string;\n required?: boolean;\n description?: string | null;\n defaultValue?: unknown;\n },\n i: number\n ) => (\n <div key={i} className=\"p-3 border rounded-lg\">\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"font-mono font-medium\">{v.name}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {v.type}\n </Badge>\n {v.required && (\n <Badge variant=\"destructive\" className=\"text-xs\">\n required\n </Badge>\n )}\n </div>\n </div>\n {v.description && (\n <p className=\"text-sm text-text-secondary\">{v.description}</p>\n )}\n {v.defaultValue !== undefined && (\n <p className=\"text-xs text-text-secondary mt-1\">\n Default:{' '}\n <code className=\"bg-bg-sunken px-1\">{String(v.defaultValue)}</code>\n </p>\n )}\n </div>\n )\n )}\n </div>\n ) : (\n <p className=\"text-text-secondary text-center py-4\">No variables defined</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {activeTab === 'preview' && (\n <Card>\n <CardHeader>\n <CardTitle>Rendered Preview</CardTitle>\n <CardDescription>Preview with example inputs (first example set)</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n {templateExamples[0] ? (\n template.templateText.replace(/\\{\\{(\\w+)\\}\\}/g, (_: string, key: string) => {\n const value = templateExamples[0][key];\n return value !== undefined ? String(value) : `{{${key}}}`;\n })\n ) : (\n <span className=\"text-text-secondary\">\n No example inputs defined. Add examples to preview rendered output.\n </span>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {activeTab === 'test' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Test Inputs</CardTitle>\n <CardDescription>Enter values to test the template</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {templateVariables.map((v) => (\n <FormField key={v.name} label={v.name} required={v.required}>\n <Input\n placeholder={v.defaultValue ? String(v.defaultValue) : `Enter ${v.name}`}\n value={testInputs[v.name] || ''}\n onChange={(e) => setTestInputs({ ...testInputs, [v.name]: e.target.value })}\n />\n {v.description && (\n <p className=\"text-xs text-text-secondary mt-1\">{v.description}</p>\n )}\n </FormField>\n ))}\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_EXECUTE}>\n <Button\n className=\"w-full\"\n leftIcon={<Play size={16} />}\n onClick={handleTestRender}\n isLoading={renderLoading}\n >\n Render Template\n </Button>\n </BotmanorPermissionGate>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Output</CardTitle>\n </CardHeader>\n <CardContent>\n {renderError ? (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-lg border border-status-error-border/40 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\"\n >\n <p>{renderError}</p>\n </div>\n ) : renderedOutput ? (\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n {renderedOutput}\n </div>\n ) : (\n <div className=\"p-8 text-center text-text-secondary\">\n <Play className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>Enter inputs and click \"Render Template\" to see output</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Example Inputs */}\n {templateExamples.length > 0 && activeTab === 'edit' && (\n <Card>\n <CardHeader>\n <CardTitle>Example Inputs</CardTitle>\n <CardDescription>Pre-defined inputs for testing and documentation</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {templateExamples.map((example, index) => (\n <div key={index} className=\"p-4 border rounded-lg bg-bg-sunken/30\">\n <p className=\"text-sm font-medium mb-2\">Example {index + 1}</p>\n <div className=\"space-y-1 text-sm\">\n {Object.entries(example).map(([key, value]) => (\n <div key={key} className=\"flex justify-between\">\n <span className=\"font-mono text-text-secondary\">{key}:</span>\n <span className=\"font-mono truncate max-w-[60%]\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Settings size={18} />\n Metadata\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-6 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Created</p>\n <p className=\"font-medium\">{formatRelativeTime(template.createdAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Updated</p>\n <p className=\"font-medium\">{formatRelativeTime(template.updatedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Tags</p>\n <div className=\"flex gap-1\">\n {template.tags.length > 0 ? (\n template.tags.map((tag: string) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))\n ) : (\n <span className=\"text-text-secondary\">None</span>\n )}\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PromptTemplateDetailPage.js","names":[],"sources":["../../../src/botlit/pages/PromptTemplateDetailPage.tsx"],"sourcesContent":["/**\n * Prompt Template Detail Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState, useMemo } from 'react';\nimport { useParams } from 'react-router';\nimport { toast } from 'sonner';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n isNative,\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\nimport {\n ArrowLeft,\n FileText,\n Variable,\n Play,\n Save,\n Copy,\n Check,\n Settings,\n Code,\n Eye,\n Edit3,\n Trash2,\n} from 'lucide-react';\nimport { PageLayout } from '../components/PageLayout';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { EmptyState } from '../components/EmptyState';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { StatusBadge, Badge } from '../components/Badge';\nimport {\n usePromptTemplate,\n useCreatePromptTemplate,\n useUpdatePromptTemplate,\n useDeletePromptTemplate,\n useRenderPromptTemplate,\n} from '../hooks';\nimport {\n promptTemplateExamples,\n promptTemplateVariables,\n type PromptTemplateVariable,\n} from '../hooks/usePromptTemplates';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { graphqlErrorMessage } from '../utils/errors';\nimport { toUnion } from '../utils/unions';\nimport type { Visibility } from '../types';\nimport { BOTMANOR_PERMISSIONS, BotmanorPermissionGate } from '../permissions';\n\ninterface PromptTemplateDetailPageProps {\n isNew?: boolean;\n}\n\nconst VARIABLE_TYPES = ['string', 'number', 'boolean', 'array', 'object'] as const;\n\nconst VISIBILITIES: Visibility[] = ['PRIVATE', 'WORKSPACE', 'PUBLIC'];\n\nexport const PromptTemplateDetailPage: FC<PromptTemplateDetailPageProps> = ({ isNew }) => {\n const { templateId } = useParams();\n const { navigateTo } = useBotlit();\n const { data: template, loading } = usePromptTemplate(isNew ? undefined : templateId);\n const { create: createTemplate, loading: createLoading } = useCreatePromptTemplate();\n const { update: updateTemplate, loading: updateLoading } = useUpdatePromptTemplate();\n const { remove: deleteTemplate, loading: deleteLoading } = useDeletePromptTemplate();\n const { render: renderTemplate, loading: renderLoading } = useRenderPromptTemplate();\n\n const [activeTab, setActiveTab] = useState<'edit' | 'preview' | 'test'>('edit');\n const [copied, setCopied] = useState(false);\n const [testInputs, setTestInputs] = useState<Record<string, string>>({});\n const [renderedOutput, setRenderedOutput] = useState<string | null>(null);\n const [renderError, setRenderError] = useState<string | null>(null);\n const [isEditing, setIsEditing] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const [formData, setFormData] = useState({\n name: '',\n slug: '',\n description: '',\n visibility: 'PRIVATE' as Visibility,\n templateText: '',\n variables: [] as PromptTemplateVariable[],\n exampleInputs: [{}] as Record<string, unknown>[],\n });\n\n // Extract variables from template text\n const detectedVariables = useMemo(() => {\n const text = isNew ? formData.templateText : template?.templateText || '';\n const regex = /\\{\\{(\\w+)\\}\\}/g;\n const matches = new Set<string>();\n let match;\n while ((match = regex.exec(text)) !== null) {\n matches.add(match[1]);\n }\n return Array.from(matches);\n }, [isNew, formData.templateText, template?.templateText]);\n\n const handleCreate = async () => {\n if (!formData.name || !formData.slug) return;\n try {\n await createTemplate({\n name: formData.name,\n slug: formData.slug,\n description: formData.description,\n visibility: formData.visibility,\n templateText: formData.templateText,\n variables: formData.variables,\n exampleInputs: formData.exampleInputs,\n });\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to create prompt template');\n return;\n }\n toast.success(`Template \"${formData.name}\" created`);\n navigateTo('prompt-templates');\n };\n\n const handleAddVariable = () => {\n setFormData({\n ...formData,\n variables: [\n ...formData.variables,\n { name: '', type: 'string', required: true, defaultValue: undefined },\n ],\n });\n };\n\n const handleUpdateVariable = (index: number, updates: Partial<PromptTemplateVariable>) => {\n const newVariables = [...formData.variables];\n newVariables[index] = { ...newVariables[index], ...updates };\n setFormData({ ...formData, variables: newVariables });\n };\n\n const handleRemoveVariable = (index: number) => {\n setFormData({\n ...formData,\n variables: formData.variables.filter((_, i) => i !== index),\n });\n };\n\n const handleTestRender = async () => {\n if (!template) return;\n setRenderError(null);\n try {\n const result = await renderTemplate({\n templateId: template.id,\n variables: testInputs,\n });\n setRenderedOutput(result);\n } catch (err) {\n // The service enforces the declared variable contract (missing required\n // variables, unknown keys, residual placeholders) — surface that error\n // here instead of dying as an unhandled rejection.\n const message = graphqlErrorMessage(err, 'Failed to render prompt template');\n setRenderedOutput(null);\n setRenderError(message);\n toast.error(message);\n }\n };\n\n const handleCopyTemplate = async () => {\n const text = template?.templateText || formData.templateText;\n await nativeCopyText(text);\n setCopied(true);\n if (isNative()) void nativeToast('Copied');\n setTimeout(() => setCopied(false), 2000);\n };\n\n const startEditing = () => {\n if (template) {\n setFormData({\n name: template.name,\n slug: template.slug,\n description: template.description || '',\n visibility: toUnion(template.visibility, VISIBILITIES, 'PRIVATE'),\n templateText: template.templateText,\n variables: promptTemplateVariables(template),\n exampleInputs: promptTemplateExamples(template),\n });\n setIsEditing(true);\n }\n };\n\n const handleUpdate = async () => {\n if (!template || !formData.name || !formData.slug) return;\n try {\n await updateTemplate({\n templateId: template.id,\n input: {\n name: formData.name,\n slug: formData.slug,\n description: formData.description,\n visibility: formData.visibility,\n templateText: formData.templateText,\n variables: formData.variables,\n exampleInputs: formData.exampleInputs,\n },\n });\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update prompt template');\n return;\n }\n toast.success('Template updated');\n setIsEditing(false);\n };\n\n const handleDelete = async () => {\n if (!template) return;\n try {\n await deleteTemplate(template.id);\n if (isNative()) void nativeNotify('success');\n navigateTo('prompt-templates');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n };\n\n const highlightVariables = (text: string) => {\n return text.replace(\n /\\{\\{(\\w+)\\}\\}/g,\n '<span class=\"bg-action-primary-bg/20 text-primary px-1 rounded font-mono\">{{$1}}</span>'\n );\n };\n\n if (isNew) {\n return (\n <PageLayout\n title=\"Create Prompt Template\"\n description=\"Define a reusable prompt template with variables\"\n actions={\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigateTo('prompt-templates')}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleCreate}\n isLoading={createLoading}\n disabled={!formData.name || !formData.slug}\n >\n Create Template\n </Button>\n </div>\n }\n >\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Left: Form */}\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Template Name\" required>\n <Input\n placeholder=\"e.g., Technical Support Template\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField\n label=\"Slug\"\n required\n description=\"Unique identifier (lowercase, dashes)\"\n >\n <Input\n placeholder=\"e.g., technical-support\"\n value={formData.slug}\n onChange={(e) =>\n setFormData({\n ...formData,\n slug: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n })\n }\n />\n </FormField>\n </div>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What is this template used for?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={2}\n />\n </FormField>\n\n <FormField label=\"Visibility\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.visibility}\n onChange={(e) =>\n setFormData({ ...formData, visibility: e.target.value as Visibility })\n }\n >\n <option value=\"PRIVATE\">Private</option>\n <option value=\"WORKSPACE\">Workspace</option>\n <option value=\"PUBLIC\">Public</option>\n </select>\n </FormField>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Code size={18} />\n Template Content\n </CardTitle>\n <CardDescription>Use {'{{variable_name}}'} syntax for variables</CardDescription>\n </CardHeader>\n <CardContent>\n <Textarea\n className=\"font-mono text-sm min-h-[300px]\"\n placeholder={`You are a helpful assistant for {{company_name}}.\n\nYour role is to help users with {{task_type}}.\n\nGuidelines:\n- Be helpful and friendly\n- Provide accurate information\n- Reference: {{docs_url}}`}\n value={formData.templateText}\n onChange={(e) => setFormData({ ...formData, templateText: e.target.value })}\n />\n {detectedVariables.length > 0 && (\n <div className=\"mt-3 flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Detected variables:</span>\n {detectedVariables.map((v) => (\n <Badge key={v} variant=\"secondary\" className=\"font-mono\">\n {v}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <Variable size={18} />\n Variables\n </span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleAddVariable}>\n Add Variable\n </Button>\n </CardTitle>\n <CardDescription>Define the variables used in your template</CardDescription>\n </CardHeader>\n <CardContent>\n {formData.variables.length > 0 ? (\n <div className=\"space-y-4\">\n {formData.variables.map((variable, index) => (\n <div key={index} className=\"p-4 border rounded-lg bg-bg-sunken/30 space-y-3\">\n <div className=\"grid grid-cols-4 gap-3\">\n <FormField label=\"Name\">\n <Input\n className=\"font-mono\"\n placeholder=\"variable_name\"\n value={variable.name}\n onChange={(e) =>\n handleUpdateVariable(index, { name: e.target.value })\n }\n />\n </FormField>\n <FormField label=\"Type\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={variable.type}\n onChange={(e) =>\n handleUpdateVariable(index, {\n type: toUnion(e.target.value, VARIABLE_TYPES, 'string'),\n })\n }\n >\n {VARIABLE_TYPES.map((type) => (\n <option key={type} value={type}>\n {type}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Default Value\">\n <Input\n placeholder=\"Optional\"\n value={String(variable.defaultValue || '')}\n onChange={(e) =>\n handleUpdateVariable(index, {\n defaultValue: e.target.value || undefined,\n })\n }\n />\n </FormField>\n <div className=\"flex items-end gap-2\">\n <label className=\"flex items-center gap-2 h-10\">\n <input\n type=\"checkbox\"\n checked={variable.required}\n onChange={(e) =>\n handleUpdateVariable(index, { required: e.target.checked })\n }\n className=\"h-4 w-4\"\n />\n <span className=\"text-sm\">Required</span>\n </label>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-status-error-text\"\n onClick={() => handleRemoveVariable(index)}\n >\n Remove\n </Button>\n </div>\n </div>\n <FormField label=\"Description\">\n <Input\n placeholder=\"What is this variable for?\"\n value={variable.description || ''}\n onChange={(e) =>\n handleUpdateVariable(index, { description: e.target.value })\n }\n />\n </FormField>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Variable className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No variables defined yet</p>\n <p className=\"text-sm\">\n Variables are automatically detected from {'{{variable}}'} syntax\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Right: Preview */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Eye size={18} />\n Preview\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div\n className=\"p-4 bg-bg-sunken rounded-lg text-sm whitespace-pre-wrap font-mono\"\n dangerouslySetInnerHTML={{\n __html: highlightVariables(formData.templateText || 'Enter template text...'),\n }}\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Quick Tips</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm space-y-2\">\n <p>\n <strong>Variables:</strong> Use {'{{name}}'} syntax\n </p>\n <p>\n <strong>Best Practices:</strong>\n </p>\n <ul className=\"list-disc list-inside text-text-secondary space-y-1\">\n <li>Use descriptive variable names</li>\n <li>Provide default values when possible</li>\n <li>Include example inputs for testing</li>\n <li>Version your templates for changes</li>\n </ul>\n </CardContent>\n </Card>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n if (loading) {\n return (\n <PageLayout title=\"Loading...\">\n <div className=\"h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </PageLayout>\n );\n }\n\n if (!template) {\n return (\n <PageLayout title=\"Template Not Found\">\n <EmptyState\n illustration=\"empty-files\"\n title=\"Template Not Found\"\n description=\"This prompt template doesn't exist.\"\n action={<Button onClick={() => navigateTo('prompt-templates')}>Back to Templates</Button>}\n />\n </PageLayout>\n );\n }\n\n // `variables`/`exampleInputs` are free-form JSON columns — normalise once.\n const templateVariables = promptTemplateVariables(template);\n const templateExamples = promptTemplateExamples(template);\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('prompt-templates')}>\n <ArrowLeft size={20} />\n </Button>\n <div className=\"p-2 rounded-lg bg-action-primary-bg/10\">\n <FileText className=\"h-6 w-6 text-primary\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2\">\n <span>{template.name}</span>\n <StatusBadge status={template.status} />\n <Badge variant=\"secondary\" className=\"font-mono\">\n v{template.version}\n </Badge>\n </div>\n <p className=\"text-sm font-normal text-text-secondary\">\n {template.slug} - {template.description}\n </p>\n </div>\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={copied ? <Check size={16} /> : <Copy size={16} />}\n onClick={handleCopyTemplate}\n >\n {copied ? 'Copied!' : 'Copy'}\n </Button>\n {isEditing ? (\n <>\n <Button variant=\"outline\" onClick={() => setIsEditing(false)}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleUpdate}\n isLoading={updateLoading}\n disabled={!formData.name || !formData.slug}\n >\n Save Changes\n </Button>\n </>\n ) : showDeleteConfirm ? (\n <>\n <Button variant=\"outline\" onClick={() => setShowDeleteConfirm(false)}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n leftIcon={<Trash2 size={16} />}\n onClick={() => {\n nativeImpact('medium');\n void handleDelete();\n }}\n isLoading={deleteLoading}\n >\n Confirm Delete\n </Button>\n </>\n ) : (\n <>\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_UPDATE}>\n <Button variant=\"outline\" leftIcon={<Edit3 size={16} />} onClick={startEditing}>\n Edit Template\n </Button>\n </BotmanorPermissionGate>\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_DELETE}>\n <Button\n variant=\"outline\"\n leftIcon={<Trash2 size={16} />}\n onClick={() => setShowDeleteConfirm(true)}\n >\n Delete\n </Button>\n </BotmanorPermissionGate>\n </>\n )}\n </div>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<FileText size={18} />}>\n This is a single reusable prompt template — its content with{' '}\n <code>{'{{variables}}'}</code>, its declared variables and example inputs, and its version\n history. Edit and preview it here to perfect the instructions, then reuse it across agents\n so they share consistent, version-controlled behaviour.\n </PagePurpose>\n\n {/* Edit Form */}\n {isEditing && (\n <Card>\n <CardHeader>\n <CardTitle>Edit Template</CardTitle>\n <CardDescription>Update template details and content</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <FormField label=\"Template Name\" required>\n <Input\n placeholder=\"e.g., Technical Support Template\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField\n label=\"Slug\"\n required\n description=\"Unique identifier (lowercase, dashes)\"\n >\n <Input\n placeholder=\"e.g., technical-support\"\n value={formData.slug}\n onChange={(e) =>\n setFormData({\n ...formData,\n slug: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n })\n }\n />\n </FormField>\n </div>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What is this template used for?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={2}\n />\n </FormField>\n\n <FormField label=\"Visibility\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.visibility}\n onChange={(e) =>\n setFormData({ ...formData, visibility: e.target.value as Visibility })\n }\n >\n <option value=\"PRIVATE\">Private</option>\n <option value=\"WORKSPACE\">Workspace</option>\n <option value=\"PUBLIC\">Public</option>\n </select>\n </FormField>\n\n <FormField\n label=\"Template Content\"\n description=\"Use {{variable_name}} syntax for variables\"\n >\n <Textarea\n className=\"font-mono text-sm min-h-[200px]\"\n value={formData.templateText}\n onChange={(e) => setFormData({ ...formData, templateText: e.target.value })}\n />\n </FormField>\n\n {detectedVariables.length > 0 && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Detected variables:</span>\n {detectedVariables.map((v) => (\n <Badge key={v} variant=\"secondary\" className=\"font-mono\">\n {v}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n )}\n\n {/* Stats */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card treatment=\"glass\" glow>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{templateVariables.length}</p>\n <p className=\"text-sm text-text-secondary\">Variables</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{templateExamples.length}</p>\n <p className=\"text-sm text-text-secondary\">Examples</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{template.revision}</p>\n <p className=\"text-sm text-text-secondary\">Revision</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-lg font-bold text-text-primary capitalize\">\n {template.visibility.toLowerCase()}\n </p>\n <p className=\"text-sm text-text-secondary\">Visibility</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-2 border-b\">\n <Button\n variant={activeTab === 'edit' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('edit')}\n >\n <Code className=\"h-4 w-4 mr-2\" />\n Template\n </Button>\n <Button\n variant={activeTab === 'preview' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('preview')}\n >\n <Eye className=\"h-4 w-4 mr-2\" />\n Preview\n </Button>\n <Button\n variant={activeTab === 'test' ? 'default' : 'ghost'}\n onClick={() => setActiveTab('test')}\n >\n <Play className=\"h-4 w-4 mr-2\" />\n Test\n </Button>\n </div>\n\n {/* Tab Content */}\n {activeTab === 'edit' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>Template Content</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n <div\n dangerouslySetInnerHTML={{\n __html: highlightVariables(template.templateText),\n }}\n />\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Variable size={18} />\n Variables ({templateVariables.length})\n </CardTitle>\n </CardHeader>\n <CardContent>\n {templateVariables.length > 0 ? (\n <div className=\"space-y-3\">\n {templateVariables.map(\n (\n v: {\n name: string;\n type: string;\n required?: boolean;\n description?: string | null;\n defaultValue?: unknown;\n },\n i: number\n ) => (\n <div key={i} className=\"p-3 border rounded-lg\">\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"font-mono font-medium\">{v.name}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"outline\" className=\"text-xs\">\n {v.type}\n </Badge>\n {v.required && (\n <Badge variant=\"destructive\" className=\"text-xs\">\n required\n </Badge>\n )}\n </div>\n </div>\n {v.description && (\n <p className=\"text-sm text-text-secondary\">{v.description}</p>\n )}\n {v.defaultValue !== undefined && (\n <p className=\"text-xs text-text-secondary mt-1\">\n Default:{' '}\n <code className=\"bg-bg-sunken px-1\">{String(v.defaultValue)}</code>\n </p>\n )}\n </div>\n )\n )}\n </div>\n ) : (\n <p className=\"text-text-secondary text-center py-4\">No variables defined</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {activeTab === 'preview' && (\n <Card>\n <CardHeader>\n <CardTitle>Rendered Preview</CardTitle>\n <CardDescription>Preview with example inputs (first example set)</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n {templateExamples[0] ? (\n template.templateText.replace(/\\{\\{(\\w+)\\}\\}/g, (_: string, key: string) => {\n const value = templateExamples[0][key];\n return value !== undefined ? String(value) : `{{${key}}}`;\n })\n ) : (\n <span className=\"text-text-secondary\">\n No example inputs defined. Add examples to preview rendered output.\n </span>\n )}\n </div>\n </CardContent>\n </Card>\n )}\n\n {activeTab === 'test' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Test Inputs</CardTitle>\n <CardDescription>Enter values to test the template</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {templateVariables.map((v) => (\n <FormField key={v.name} label={v.name} required={v.required}>\n <Input\n placeholder={v.defaultValue ? String(v.defaultValue) : `Enter ${v.name}`}\n value={testInputs[v.name] || ''}\n onChange={(e) => setTestInputs({ ...testInputs, [v.name]: e.target.value })}\n />\n {v.description && (\n <p className=\"text-xs text-text-secondary mt-1\">{v.description}</p>\n )}\n </FormField>\n ))}\n <BotmanorPermissionGate permission={BOTMANOR_PERMISSIONS.PROMPT_TEMPLATE_EXECUTE}>\n <Button\n className=\"w-full\"\n leftIcon={<Play size={16} />}\n onClick={handleTestRender}\n isLoading={renderLoading}\n >\n Render Template\n </Button>\n </BotmanorPermissionGate>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Output</CardTitle>\n </CardHeader>\n <CardContent>\n {renderError ? (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-lg border border-status-error-border/40 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\"\n >\n <p>{renderError}</p>\n </div>\n ) : renderedOutput ? (\n <div className=\"p-4 bg-bg-sunken rounded-lg font-mono text-sm whitespace-pre-wrap\">\n {renderedOutput}\n </div>\n ) : (\n <div className=\"p-8 text-center text-text-secondary\">\n <Play className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>Enter inputs and click \"Render Template\" to see output</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Example Inputs */}\n {templateExamples.length > 0 && activeTab === 'edit' && (\n <Card>\n <CardHeader>\n <CardTitle>Example Inputs</CardTitle>\n <CardDescription>Pre-defined inputs for testing and documentation</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {templateExamples.map((example, index) => (\n <div key={index} className=\"p-4 border rounded-lg bg-bg-sunken/30\">\n <p className=\"text-sm font-medium mb-2\">Example {index + 1}</p>\n <div className=\"space-y-1 text-sm\">\n {Object.entries(example).map(([key, value]) => (\n <div key={key} className=\"flex justify-between\">\n <span className=\"font-mono text-text-secondary\">{key}:</span>\n <span className=\"font-mono truncate max-w-[60%]\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Settings size={18} />\n Metadata\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-6 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Created</p>\n <p className=\"font-medium\">{formatRelativeTime(template.createdAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Updated</p>\n <p className=\"font-medium\">{formatRelativeTime(template.updatedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Tags</p>\n <div className=\"flex gap-1\">\n {template.tags.length > 0 ? (\n template.tags.map((tag: string) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))\n ) : (\n <span className=\"text-text-secondary\">None</span>\n )}\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/botlit/utils/errors.ts
|
|
2
|
+
function e(e, t) {
|
|
3
|
+
if (typeof e == "object" && e && "graphQLErrors" in e) {
|
|
4
|
+
let t = e.graphQLErrors;
|
|
5
|
+
if (Array.isArray(t)) {
|
|
6
|
+
let e = t[0];
|
|
7
|
+
if (typeof e == "object" && e && "message" in e) {
|
|
8
|
+
let t = e.message;
|
|
9
|
+
if (typeof t == "string" && t.trim() !== "") return t;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return e instanceof Error && e.message.trim() !== "" ? e.message : t;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { e as graphqlErrorMessage };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../../src/botlit/utils/errors.ts"],"sourcesContent":["/**\n * Extracts the user-actionable message from a failed GraphQL mutation.\n *\n * The workspace gateway returns field-level errors (e.g. the prompt-template\n * variable-contract rejections) inside the response's `errors` array. Apollo\n * surfaces them on the thrown error's `graphQLErrors`, while `err.message`\n * alone is often a generic transport/status message. Prefers the first\n * GraphQL error message, falls back to the Error message, then the caller's\n * fallback.\n */\nexport function graphqlErrorMessage(error: unknown, fallback: string): string {\n if (error !== null && typeof error === 'object' && 'graphQLErrors' in error) {\n const graphQLErrors = (error as { graphQLErrors?: unknown }).graphQLErrors;\n if (Array.isArray(graphQLErrors)) {\n const first = graphQLErrors[0];\n if (first !== null && typeof first === 'object' && 'message' in first) {\n const message = (first as { message?: unknown }).message;\n if (typeof message === 'string' && message.trim() !== '') {\n return message;\n }\n }\n }\n }\n if (error instanceof Error && error.message.trim() !== '') {\n return error.message;\n }\n return fallback;\n}\n"],"mappings":";AAUA,SAAgB,EAAoB,GAAgB,GAA0B;AAC5E,KAAsB,OAAO,KAAU,YAAnC,KAA+C,mBAAmB,GAAO;EAC3E,IAAM,IAAiB,EAAsC;AAC7D,MAAI,MAAM,QAAQ,EAAc,EAAE;GAChC,IAAM,IAAQ,EAAc;AAC5B,OAAsB,OAAO,KAAU,YAAnC,KAA+C,aAAa,GAAO;IACrE,IAAM,IAAW,EAAgC;AACjD,QAAI,OAAO,KAAY,YAAY,EAAQ,MAAM,KAAK,GACpD,QAAO;;;;AAQf,QAHI,aAAiB,SAAS,EAAM,QAAQ,MAAM,KAAK,KAC9C,EAAM,UAER"}
|
package/package.json
CHANGED