@bikdotai/bik-component-library 0.0.849-beta.3 → 0.0.849-beta.5
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/cjs/components/agent-builder/components/AddDataPill.js +10 -0
- package/dist/cjs/components/agent-builder/components/AddDataPill.js.map +1 -0
- package/dist/cjs/components/agent-builder/components/ConfigToolPanel.js +1 -1
- package/dist/cjs/components/agent-builder/components/ConfigToolPanel.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/MentionEditor.js +1 -1
- package/dist/cjs/components/agent-builder/components/MentionEditor.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/MentionField.js +1 -1
- package/dist/cjs/components/agent-builder/components/MentionField.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/RestApiToolPanel.js +4 -4
- package/dist/cjs/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/ToolInstructionsField.js +1 -1
- package/dist/cjs/components/agent-builder/components/ToolInstructionsField.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/VariableTriggerArea.js +10 -4
- package/dist/cjs/components/agent-builder/components/VariableTriggerArea.js.map +1 -1
- package/dist/cjs/components/agent-builder/utils/mentionSerialization.js +4 -4
- package/dist/cjs/components/agent-builder/utils/mentionSerialization.js.map +1 -1
- package/dist/cjs/editor/BikEditor.js +1 -1
- package/dist/cjs/editor/BikEditor.js.map +1 -1
- package/dist/cjs/editor/BikEditor.styles.js +5 -3
- package/dist/cjs/editor/BikEditor.styles.js.map +1 -1
- package/dist/cjs/editor/BikEditor.types.js.map +1 -1
- package/dist/esm/components/agent-builder/components/AddDataPill.d.ts +18 -0
- package/dist/esm/components/agent-builder/components/AddDataPill.js +32 -0
- package/dist/esm/components/agent-builder/components/AddDataPill.js.map +1 -0
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js +21 -19
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/MentionEditor.d.ts +7 -2
- package/dist/esm/components/agent-builder/components/MentionEditor.js +119 -102
- package/dist/esm/components/agent-builder/components/MentionEditor.js.map +1 -1
- package/dist/esm/components/agent-builder/components/MentionField.js +30 -29
- package/dist/esm/components/agent-builder/components/MentionField.js.map +1 -1
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.d.ts +9 -0
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js +126 -119
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/ToolInstructionsField.js +20 -19
- package/dist/esm/components/agent-builder/components/ToolInstructionsField.js.map +1 -1
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.d.ts +15 -0
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.js +112 -56
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.js.map +1 -1
- package/dist/esm/components/agent-builder/utils/mentionSerialization.js +26 -20
- package/dist/esm/components/agent-builder/utils/mentionSerialization.js.map +1 -1
- package/dist/esm/editor/BikEditor.js +24 -18
- package/dist/esm/editor/BikEditor.js.map +1 -1
- package/dist/esm/editor/BikEditor.styles.js +5 -3
- package/dist/esm/editor/BikEditor.styles.js.map +1 -1
- package/dist/esm/editor/BikEditor.types.d.ts +9 -0
- package/dist/esm/editor/BikEditor.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,6 +11,21 @@ interface VariableTriggerAreaProps {
|
|
|
11
11
|
* null for rich-editor targets (insert at the editor's own cursor).
|
|
12
12
|
*/
|
|
13
13
|
onInsert: (token: string, caret: number | null) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Rewrite the token before it's handed to `onInsert` — e.g. the Rest API
|
|
16
|
+
* JSON editors wrap it in quotes. `textBefore` is the text preceding the
|
|
17
|
+
* `@` (for input targets the whole value up to the caret; for rich-editor
|
|
18
|
+
* targets just the current text-node fragment).
|
|
19
|
+
*/
|
|
20
|
+
transformToken?: (token: string, ctx: {
|
|
21
|
+
textBefore: string;
|
|
22
|
+
}) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Show the floating "+ Add data" pill while the wrapped field is focused.
|
|
25
|
+
* Clicking it opens this same variable panel at the field's caret — the
|
|
26
|
+
* button equivalent of typing `@`.
|
|
27
|
+
*/
|
|
28
|
+
addDataButton?: boolean;
|
|
14
29
|
children: React.ReactNode;
|
|
15
30
|
}
|
|
16
31
|
/**
|
|
@@ -1,70 +1,126 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { jsxs as E, jsx as p } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as v, useState as b, useCallback as T, useEffect as $ } from "react";
|
|
3
|
+
import B from "styled-components";
|
|
4
|
+
import { COLORS as h } from "../../../constants/Theme.js";
|
|
5
|
+
import { PickerType as F } from "../../variable-picker-v3/model.js";
|
|
6
|
+
import { variableTokenName as L } from "../utils/mentionSerialization.js";
|
|
7
|
+
import { AddDataPill as C } from "./AddDataPill.js";
|
|
8
|
+
import { VariablePicker as O } from "../../variable-picker-v3/VariablePicker.js";
|
|
9
|
+
const A = (t) => `{{variable:${L(t.actualValue)}}}`, w = /[\s,;:/?&=(){}[\]"'<>]/, k = (t) => {
|
|
10
|
+
if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) {
|
|
11
|
+
const n = t.selectionStart ?? t.value.length, d = n === 0 ? " " : t.value[n - 1] ?? " ";
|
|
12
|
+
return {
|
|
13
|
+
ok: w.test(d),
|
|
14
|
+
caret: n,
|
|
15
|
+
textBefore: t.value.slice(0, n)
|
|
16
|
+
};
|
|
11
17
|
}
|
|
12
|
-
const
|
|
13
|
-
return {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const l = window.getSelection(), c = l?.anchorNode?.textContent ?? "", i = l?.anchorOffset ?? 0, u = i === 0 ? " " : c[i - 1] ?? " ";
|
|
19
|
+
return {
|
|
20
|
+
ok: w.test(u),
|
|
21
|
+
caret: null,
|
|
22
|
+
textBefore: c.slice(0, i)
|
|
23
|
+
};
|
|
24
|
+
}, z = ({
|
|
25
|
+
variablesData: t,
|
|
26
|
+
onInsert: l,
|
|
27
|
+
transformToken: c,
|
|
28
|
+
addDataButton: i,
|
|
29
|
+
children: u
|
|
18
30
|
}) => {
|
|
19
|
-
const
|
|
20
|
-
(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
const n = v(null), [d, m] = b(!1), g = v(null), [r, a] = b(null), f = (t?.length ?? 0) > 0, x = () => {
|
|
32
|
+
const e = n.current?.getBoundingClientRect();
|
|
33
|
+
return {
|
|
34
|
+
above: !!e && window.innerHeight - e.bottom < 340,
|
|
35
|
+
alignRight: !!e && window.innerWidth - e.left < 360
|
|
36
|
+
};
|
|
37
|
+
}, y = T(
|
|
38
|
+
(e) => {
|
|
39
|
+
if (!f || e.key !== "@")
|
|
25
40
|
return;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
const o = k(e.target);
|
|
42
|
+
o.ok && (e.preventDefault(), e.stopPropagation(), a({
|
|
43
|
+
caret: o.caret,
|
|
44
|
+
textBefore: o.textBefore,
|
|
45
|
+
...x()
|
|
46
|
+
}));
|
|
29
47
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
+
[f]
|
|
50
|
+
), R = () => {
|
|
51
|
+
const e = k(g.current);
|
|
52
|
+
a({
|
|
53
|
+
caret: e.caret,
|
|
54
|
+
textBefore: e.textBefore,
|
|
55
|
+
...x()
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
return $(() => {
|
|
59
|
+
if (!r)
|
|
34
60
|
return;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
|
|
61
|
+
const e = (s) => {
|
|
62
|
+
s.key === "Escape" && (s.stopPropagation(), a(null));
|
|
63
|
+
}, o = (s) => {
|
|
64
|
+
n.current?.contains(s.target) || a(null);
|
|
39
65
|
};
|
|
40
|
-
return document.addEventListener("keydown",
|
|
41
|
-
document.removeEventListener("keydown",
|
|
66
|
+
return document.addEventListener("keydown", e, !0), document.addEventListener("mousedown", o), () => {
|
|
67
|
+
document.removeEventListener("keydown", e, !0), document.removeEventListener("mousedown", o);
|
|
42
68
|
};
|
|
43
|
-
}, [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
}, [r]), /* @__PURE__ */ E(
|
|
70
|
+
P,
|
|
71
|
+
{
|
|
72
|
+
ref: n,
|
|
73
|
+
onKeyDownCapture: y,
|
|
74
|
+
onFocus: (e) => {
|
|
75
|
+
e.target.closest?.(
|
|
76
|
+
'[data-test="variable-trigger-panel"]'
|
|
77
|
+
) || (g.current = e.target, m(!0));
|
|
78
|
+
},
|
|
79
|
+
onBlur: () => m(!1),
|
|
80
|
+
children: [
|
|
81
|
+
u,
|
|
82
|
+
i && f && (d || r) && /* @__PURE__ */ p(C, { onOpen: R }),
|
|
83
|
+
r && /* @__PURE__ */ p(
|
|
84
|
+
S,
|
|
85
|
+
{
|
|
86
|
+
$above: r.above,
|
|
87
|
+
$right: r.alignRight,
|
|
88
|
+
"data-test": "variable-trigger-panel",
|
|
89
|
+
children: /* @__PURE__ */ p(
|
|
90
|
+
O,
|
|
91
|
+
{
|
|
92
|
+
type: F.FLOATER,
|
|
93
|
+
data: t ?? [],
|
|
94
|
+
showRecommendedBar: !1,
|
|
95
|
+
onClose: () => a(null),
|
|
96
|
+
onSelect: (e) => {
|
|
97
|
+
a(null);
|
|
98
|
+
const o = A(e), s = c ? c(o, { textBefore: r.textBefore }) : o;
|
|
99
|
+
l(s, r.caret);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}, P = B.div`
|
|
59
109
|
position: relative;
|
|
60
|
-
`,
|
|
110
|
+
`, S = B.div`
|
|
61
111
|
position: absolute;
|
|
62
|
-
left: 0;
|
|
63
112
|
z-index: 1000;
|
|
64
|
-
|
|
113
|
+
width: 344px;
|
|
114
|
+
background: ${h.surface.standard};
|
|
115
|
+
border: 1px solid ${h.stroke.primary};
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
box-shadow: 0px -2px 8px 0px #0000001f;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
${(t) => t.$right ? "right: 0;" : "left: 0;"}
|
|
120
|
+
${(t) => t.$above ? "bottom: calc(100% + 4px);" : "top: calc(100% + 4px);"}
|
|
65
121
|
`;
|
|
66
122
|
export {
|
|
67
|
-
|
|
68
|
-
|
|
123
|
+
z as VariableTriggerArea,
|
|
124
|
+
A as variableToken
|
|
69
125
|
};
|
|
70
126
|
//# sourceMappingURL=VariableTriggerArea.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VariableTriggerArea.js","sources":["../../../../../src/components/agent-builder/components/VariableTriggerArea.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport { VariablePicker } from '../../variable-picker-v3';\nimport { PickerType } from '../../variable-picker-v3/model';\nimport type {\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport { variableTokenName } from '../utils/mentionSerialization';\n\n/** The persisted form of a picked variable (`{{variable:<name>}}`). */\nexport const variableToken = (variable: VariableV3): string =>\n\t`{{variable:${variableTokenName(variable.actualValue)}}}`;\n\n/**\n * Boundary test for the `@` trigger: an `@` typed at the start of the field or\n * after a separator opens the picker. The separator set covers URL and JSON\n * delimiters too (`https://@`, `Bearer=@`, `\"@`, `[@`) — an email address's `@`\n * always follows a letter / digit / dot, so `john@acme.com` still types\n * through untouched. Returns the caret index for input / textarea targets\n * (where the caller can splice the token in), null for contenteditable ones.\n */\nconst BOUNDARY = /[\\s,;:/?&=(){}[\\]\"'<>]/;\n\nconst triggerContext = (\n\ttarget: EventTarget | null,\n): { ok: boolean; caret: number | null } => {\n\tif (\n\t\ttarget instanceof HTMLInputElement ||\n\t\ttarget instanceof HTMLTextAreaElement\n\t) {\n\t\tconst caret = target.selectionStart ?? target.value.length;\n\t\tconst prev = caret === 0 ? ' ' : target.value[caret - 1] ?? ' ';\n\t\treturn { ok: BOUNDARY.test(prev), caret };\n\t}\n\t// Rich editor (ProseMirror contenteditable): read the character before the\n\t// selection.\n\tconst sel = window.getSelection();\n\tconst text = sel?.anchorNode?.textContent ?? '';\n\tconst offset = sel?.anchorOffset ?? 0;\n\tconst prev = offset === 0 ? ' ' : text[offset - 1] ?? ' ';\n\treturn { ok: BOUNDARY.test(prev), caret: null };\n};\n\ninterface VariableTriggerAreaProps {\n\t/** Variable catalog. Absent / empty → the wrapper is inert (children only). */\n\tvariablesData?: VariableListInterfaceV3[];\n\t/**\n\t * Insert `token` into the wrapped field. `caret` is the character index the\n\t * `@` was typed at (input / textarea targets — splice the token in there);\n\t * null for rich-editor targets (insert at the editor's own cursor).\n\t */\n\tonInsert: (token: string, caret: number | null) => void;\n\tchildren: React.ReactNode;\n}\n\n/**\n * Wraps any plain field (input / textarea / chips entry / BikEditor) and opens\n * the shared {@link VariablePicker} as a floating panel when `@` is typed at a\n * word boundary. The `@` keystroke itself is swallowed (never reaches the\n * field); picking a variable calls `onInsert` with the `{{variable:<name>}}`\n * token, and Escape / click-outside / the panel's ✕ just closes the panel.\n *\n * Purely presentational wrapper — it renders a `position: relative` block, so\n * the wrapped field must be happy inside a plain block container.\n */\nexport const VariableTriggerArea: React.FC<VariableTriggerAreaProps> = ({\n\tvariablesData,\n\tonInsert,\n\tchildren,\n}) => {\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\t// Non-null while the panel is open: where to put the token when picked, and\n\t// whether the panel renders above the field (not enough room below).\n\tconst [trigger, setTrigger] = useState<{\n\t\tcaret: number | null;\n\t\tabove: boolean;\n\t} | null>(null);\n\n\tconst enabled = (variablesData?.length ?? 0) > 0;\n\n\tconst onKeyDownCapture = useCallback(\n\t\t(e: React.KeyboardEvent) => {\n\t\t\tif (!enabled || e.key !== '@') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst ctx = triggerContext(e.target);\n\t\t\tif (!ctx.ok) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Swallow the `@` so the field's value stays untouched while picking.\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\t\tconst above = !!rect && window.innerHeight - rect.bottom < 340;\n\t\t\tsetTrigger({ caret: ctx.caret, above });\n\t\t},\n\t\t[enabled],\n\t);\n\n\t// Escape / click-outside dismiss, active only while open.\n\tuseEffect(() => {\n\t\tif (!trigger) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (e: KeyboardEvent) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\te.stopPropagation();\n\t\t\t\tsetTrigger(null);\n\t\t\t}\n\t\t};\n\t\tconst onDown = (e: MouseEvent) => {\n\t\t\tif (!containerRef.current?.contains(e.target as Node)) {\n\t\t\t\tsetTrigger(null);\n\t\t\t}\n\t\t};\n\t\t// Capture phase so an Escape meant for the panel never also closes the\n\t\t// drawer underneath.\n\t\tdocument.addEventListener('keydown', onKey, true);\n\t\tdocument.addEventListener('mousedown', onDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', onKey, true);\n\t\t\tdocument.removeEventListener('mousedown', onDown);\n\t\t};\n\t}, [trigger]);\n\n\treturn (\n\t\t<TriggerWrap ref={containerRef} onKeyDownCapture={onKeyDownCapture}>\n\t\t\t{children}\n\t\t\t{trigger && (\n\t\t\t\t<FloaterBox $above={trigger.above} data-test=\"variable-trigger-panel\">\n\t\t\t\t\t<VariablePicker\n\t\t\t\t\t\ttype={PickerType.FLOATER}\n\t\t\t\t\t\tdata={variablesData ?? []}\n\t\t\t\t\t\tshowRecommendedBar={false}\n\t\t\t\t\t\tonClose={() => setTrigger(null)}\n\t\t\t\t\t\tonSelect={(variable: VariableV3) => {\n\t\t\t\t\t\t\tsetTrigger(null);\n\t\t\t\t\t\t\tonInsert(variableToken(variable), trigger.caret);\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</FloaterBox>\n\t\t\t)}\n\t\t</TriggerWrap>\n\t);\n};\n\nconst TriggerWrap = styled.div`\n\tposition: relative;\n`;\n\n// Floats under (or over) the wrapped field; VariablePicker brings its own card\n// styling. High z-index: these fields live inside side-modal drawers.\nconst FloaterBox = styled.div<{ $above: boolean }>`\n\tposition: absolute;\n\tleft: 0;\n\tz-index: 1000;\n\t${(p) => (p.$above ? 'bottom: calc(100% + 4px);' : 'top: calc(100% + 4px);')}\n`;\n"],"names":["variableToken","variable","variableTokenName","BOUNDARY","triggerContext","target","caret","prev","sel","text","offset","VariableTriggerArea","variablesData","onInsert","children","containerRef","useRef","trigger","setTrigger","useState","enabled","onKeyDownCapture","useCallback","e","ctx","rect","above","useEffect","onKey","onDown","jsxs","TriggerWrap","jsx","FloaterBox","VariablePicker","PickerType","styled","p"],"mappings":";;;;;;AAWO,MAAMA,IAAgB,CAACC,MAC7B,cAAcC,EAAkBD,EAAS,WAAW,CAAC,MAUhDE,IAAW,0BAEXC,IAAiB,CACtBC,MAC2C;AAC3C,MACCA,aAAkB,oBAClBA,aAAkB,qBACjB;AACD,UAAMC,IAAQD,EAAO,kBAAkBA,EAAO,MAAM,QAC9CE,IAAOD,MAAU,IAAI,MAAMD,EAAO,MAAMC,IAAQ,CAAC,KAAK;AAC5D,WAAO,EAAE,IAAIH,EAAS,KAAKI,CAAI,GAAG,OAAAD,EAAA;AAAA,EACnC;AAGA,QAAME,IAAM,OAAO,aAAA,GACbC,IAAOD,GAAK,YAAY,eAAe,IACvCE,IAASF,GAAK,gBAAgB,GAC9BD,IAAOG,MAAW,IAAI,MAAMD,EAAKC,IAAS,CAAC,KAAK;AACtD,SAAO,EAAE,IAAIP,EAAS,KAAKI,CAAI,GAAG,OAAO,KAAA;AAC1C,GAwBaI,IAA0D,CAAC;AAAA,EACvE,eAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AACD,MAAM;AACL,QAAMC,IAAeC,EAAuB,IAAI,GAG1C,CAACC,GAASC,CAAU,IAAIC,EAGpB,IAAI,GAERC,KAAWR,GAAe,UAAU,KAAK,GAEzCS,IAAmBC;AAAA,IACxB,CAACC,MAA2B;AAC3B,UAAI,CAACH,KAAWG,EAAE,QAAQ;AACzB;AAED,YAAMC,IAAMpB,EAAemB,EAAE,MAAM;AACnC,UAAI,CAACC,EAAI;AACR;AAGD,MAAAD,EAAE,eAAA,GACFA,EAAE,gBAAA;AACF,YAAME,IAAOV,EAAa,SAAS,sBAAA,GAC7BW,IAAQ,CAAC,CAACD,KAAQ,OAAO,cAAcA,EAAK,SAAS;AAC3D,MAAAP,EAAW,EAAE,OAAOM,EAAI,OAAO,OAAAE,GAAO;AAAA,IACvC;AAAA,IACA,CAACN,CAAO;AAAA,EAAA;AAIT,SAAAO,EAAU,MAAM;AACf,QAAI,CAACV;AACJ;AAED,UAAMW,IAAQ,CAACL,MAAqB;AACnC,MAAIA,EAAE,QAAQ,aACbA,EAAE,gBAAA,GACFL,EAAW,IAAI;AAAA,IAEjB,GACMW,IAAS,CAACN,MAAkB;AACjC,MAAKR,EAAa,SAAS,SAASQ,EAAE,MAAc,KACnDL,EAAW,IAAI;AAAA,IAEjB;AAGA,oBAAS,iBAAiB,WAAWU,GAAO,EAAI,GAChD,SAAS,iBAAiB,aAAaC,CAAM,GACtC,MAAM;AACZ,eAAS,oBAAoB,WAAWD,GAAO,EAAI,GACnD,SAAS,oBAAoB,aAAaC,CAAM;AAAA,IACjD;AAAA,EACD,GAAG,CAACZ,CAAO,CAAC,GAGX,gBAAAa,EAACC,GAAA,EAAY,KAAKhB,GAAc,kBAAAM,GAC9B,UAAA;AAAA,IAAAP;AAAA,IACAG,KACA,gBAAAe,EAACC,GAAA,EAAW,QAAQhB,EAAQ,OAAO,aAAU,0BAC5C,UAAA,gBAAAe;AAAA,MAACE;AAAA,MAAA;AAAA,QACA,MAAMC,EAAW;AAAA,QACjB,MAAMvB,KAAiB,CAAA;AAAA,QACvB,oBAAoB;AAAA,QACpB,SAAS,MAAMM,EAAW,IAAI;AAAA,QAC9B,UAAU,CAACjB,MAAyB;AACnC,UAAAiB,EAAW,IAAI,GACfL,EAASb,EAAcC,CAAQ,GAAGgB,EAAQ,KAAK;AAAA,QAChD;AAAA,MAAA;AAAA,IAAA,EACD,CACD;AAAA,EAAA,GAEF;AAEF,GAEMc,IAAcK,EAAO;AAAA;AAAA,GAMrBH,IAAaG,EAAO;AAAA;AAAA;AAAA;AAAA,GAIvB,CAACC,MAAOA,EAAE,SAAS,8BAA8B,wBAAyB;AAAA;"}
|
|
1
|
+
{"version":3,"file":"VariableTriggerArea.js","sources":["../../../../../src/components/agent-builder/components/VariableTriggerArea.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport { COLORS } from '@src/constants/Theme';\nimport { VariablePicker } from '../../variable-picker-v3';\nimport { PickerType } from '../../variable-picker-v3/model';\nimport type {\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport { variableTokenName } from '../utils/mentionSerialization';\nimport { AddDataPill } from './AddDataPill';\n\n/** The persisted form of a picked variable (`{{variable:<name>}}`). */\nexport const variableToken = (variable: VariableV3): string =>\n\t`{{variable:${variableTokenName(variable.actualValue)}}}`;\n\n/**\n * Boundary test for the `@` trigger: an `@` typed at the start of the field or\n * after a separator opens the picker. The separator set covers URL and JSON\n * delimiters too (`https://@`, `Bearer=@`, `\"@`, `[@`) — an email address's `@`\n * always follows a letter / digit / dot, so `john@acme.com` still types\n * through untouched. Returns the caret index for input / textarea targets\n * (where the caller can splice the token in), null for contenteditable ones.\n */\nconst BOUNDARY = /[\\s,;:/?&=(){}[\\]\"'<>]/;\n\nconst triggerContext = (\n\ttarget: EventTarget | null,\n): { ok: boolean; caret: number | null; textBefore: string } => {\n\tif (\n\t\ttarget instanceof HTMLInputElement ||\n\t\ttarget instanceof HTMLTextAreaElement\n\t) {\n\t\tconst caret = target.selectionStart ?? target.value.length;\n\t\tconst prev = caret === 0 ? ' ' : target.value[caret - 1] ?? ' ';\n\t\treturn {\n\t\t\tok: BOUNDARY.test(prev),\n\t\t\tcaret,\n\t\t\ttextBefore: target.value.slice(0, caret),\n\t\t};\n\t}\n\t// Rich editor (ProseMirror contenteditable): read the character before the\n\t// selection. `textBefore` is only the current text-node fragment (fine for\n\t// the token transforms, which just look at nearby characters).\n\tconst sel = window.getSelection();\n\tconst text = sel?.anchorNode?.textContent ?? '';\n\tconst offset = sel?.anchorOffset ?? 0;\n\tconst prev = offset === 0 ? ' ' : text[offset - 1] ?? ' ';\n\treturn {\n\t\tok: BOUNDARY.test(prev),\n\t\tcaret: null,\n\t\ttextBefore: text.slice(0, offset),\n\t};\n};\n\ninterface VariableTriggerAreaProps {\n\t/** Variable catalog. Absent / empty → the wrapper is inert (children only). */\n\tvariablesData?: VariableListInterfaceV3[];\n\t/**\n\t * Insert `token` into the wrapped field. `caret` is the character index the\n\t * `@` was typed at (input / textarea targets — splice the token in there);\n\t * null for rich-editor targets (insert at the editor's own cursor).\n\t */\n\tonInsert: (token: string, caret: number | null) => void;\n\t/**\n\t * Rewrite the token before it's handed to `onInsert` — e.g. the Rest API\n\t * JSON editors wrap it in quotes. `textBefore` is the text preceding the\n\t * `@` (for input targets the whole value up to the caret; for rich-editor\n\t * targets just the current text-node fragment).\n\t */\n\ttransformToken?: (token: string, ctx: { textBefore: string }) => string;\n\t/**\n\t * Show the floating \"+ Add data\" pill while the wrapped field is focused.\n\t * Clicking it opens this same variable panel at the field's caret — the\n\t * button equivalent of typing `@`.\n\t */\n\taddDataButton?: boolean;\n\tchildren: React.ReactNode;\n}\n\n/**\n * Wraps any plain field (input / textarea / chips entry / BikEditor) and opens\n * the shared {@link VariablePicker} as a floating panel when `@` is typed at a\n * word boundary. The `@` keystroke itself is swallowed (never reaches the\n * field); picking a variable calls `onInsert` with the `{{variable:<name>}}`\n * token, and Escape / click-outside / the panel's ✕ just closes the panel.\n *\n * Purely presentational wrapper — it renders a `position: relative` block, so\n * the wrapped field must be happy inside a plain block container.\n */\nexport const VariableTriggerArea: React.FC<VariableTriggerAreaProps> = ({\n\tvariablesData,\n\tonInsert,\n\ttransformToken,\n\taddDataButton,\n\tchildren,\n}) => {\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\t// Whether the wrapped field (any focusable inside) currently has focus, and\n\t// the element that had it — the pill inserts at that element's caret.\n\tconst [fieldFocused, setFieldFocused] = useState(false);\n\tconst lastFocusedRef = useRef<EventTarget | null>(null);\n\t// Non-null while the panel is open: where to put the token when picked, and\n\t// which way the panel flips to stay on screen (above when there's no room\n\t// below, right-aligned when the field sits too close to the right edge —\n\t// e.g. header-value inputs in the Rest API drawer).\n\tconst [trigger, setTrigger] = useState<{\n\t\tcaret: number | null;\n\t\ttextBefore: string;\n\t\tabove: boolean;\n\t\talignRight: boolean;\n\t} | null>(null);\n\n\tconst enabled = (variablesData?.length ?? 0) > 0;\n\n\t// Panel is ~344px wide / ~340px tall, anchored to the field's left edge —\n\t// flip above / right-align when it would spill past the viewport (avoids\n\t// the drawer growing scrollbars).\n\tconst placement = () => {\n\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\treturn {\n\t\t\tabove: !!rect && window.innerHeight - rect.bottom < 340,\n\t\t\talignRight: !!rect && window.innerWidth - rect.left < 360,\n\t\t};\n\t};\n\n\tconst onKeyDownCapture = useCallback(\n\t\t(e: React.KeyboardEvent) => {\n\t\t\tif (!enabled || e.key !== '@') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst ctx = triggerContext(e.target);\n\t\t\tif (!ctx.ok) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Swallow the `@` so the field's value stays untouched while picking.\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tsetTrigger({\n\t\t\t\tcaret: ctx.caret,\n\t\t\t\ttextBefore: ctx.textBefore,\n\t\t\t\t...placement(),\n\t\t\t});\n\t\t},\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t[enabled],\n\t);\n\n\t// The pill's click — same panel, anchored at the focused field's caret (no\n\t// boundary rule: an explicit button click is always intentional).\n\tconst openFromButton = () => {\n\t\tconst ctx = triggerContext(lastFocusedRef.current);\n\t\tsetTrigger({\n\t\t\tcaret: ctx.caret,\n\t\t\ttextBefore: ctx.textBefore,\n\t\t\t...placement(),\n\t\t});\n\t};\n\n\t// Escape / click-outside dismiss, active only while open.\n\tuseEffect(() => {\n\t\tif (!trigger) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (e: KeyboardEvent) => {\n\t\t\tif (e.key === 'Escape') {\n\t\t\t\te.stopPropagation();\n\t\t\t\tsetTrigger(null);\n\t\t\t}\n\t\t};\n\t\tconst onDown = (e: MouseEvent) => {\n\t\t\tif (!containerRef.current?.contains(e.target as Node)) {\n\t\t\t\tsetTrigger(null);\n\t\t\t}\n\t\t};\n\t\t// Capture phase so an Escape meant for the panel never also closes the\n\t\t// drawer underneath.\n\t\tdocument.addEventListener('keydown', onKey, true);\n\t\tdocument.addEventListener('mousedown', onDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('keydown', onKey, true);\n\t\t\tdocument.removeEventListener('mousedown', onDown);\n\t\t};\n\t}, [trigger]);\n\n\treturn (\n\t\t<TriggerWrap\n\t\t\tref={containerRef}\n\t\t\tonKeyDownCapture={onKeyDownCapture}\n\t\t\t// React's focus/blur bubble — this tracks any focusable child (the\n\t\t\t// input, a chips entry, the ProseMirror contenteditable).\n\t\t\tonFocus={(e) => {\n\t\t\t\t// Focus landing inside the floating panel (its search box) is not\n\t\t\t\t// the field — inserting at \"its caret\" would target the wrong input.\n\t\t\t\tif (\n\t\t\t\t\t(e.target as HTMLElement).closest?.(\n\t\t\t\t\t\t'[data-test=\"variable-trigger-panel\"]',\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlastFocusedRef.current = e.target;\n\t\t\t\tsetFieldFocused(true);\n\t\t\t}}\n\t\t\tonBlur={() => setFieldFocused(false)}\n\t\t>\n\t\t\t{children}\n\t\t\t{addDataButton && enabled && (fieldFocused || trigger) && (\n\t\t\t\t<AddDataPill onOpen={openFromButton} />\n\t\t\t)}\n\t\t\t{trigger && (\n\t\t\t\t<FloaterBox\n\t\t\t\t\t$above={trigger.above}\n\t\t\t\t\t$right={trigger.alignRight}\n\t\t\t\t\tdata-test=\"variable-trigger-panel\"\n\t\t\t\t>\n\t\t\t\t\t<VariablePicker\n\t\t\t\t\t\ttype={PickerType.FLOATER}\n\t\t\t\t\t\tdata={variablesData ?? []}\n\t\t\t\t\t\tshowRecommendedBar={false}\n\t\t\t\t\t\tonClose={() => setTrigger(null)}\n\t\t\t\t\t\tonSelect={(variable: VariableV3) => {\n\t\t\t\t\t\t\tsetTrigger(null);\n\t\t\t\t\t\t\tconst raw = variableToken(variable);\n\t\t\t\t\t\t\tconst token = transformToken\n\t\t\t\t\t\t\t\t? transformToken(raw, { textBefore: trigger.textBefore })\n\t\t\t\t\t\t\t\t: raw;\n\t\t\t\t\t\t\tonInsert(token, trigger.caret);\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</FloaterBox>\n\t\t\t)}\n\t\t</TriggerWrap>\n\t);\n};\n\nconst TriggerWrap = styled.div`\n\tposition: relative;\n`;\n\n// Floats under (or over) the wrapped field. The picker is EMBEDDED (no close\n// cross — Escape / click-outside dismiss instead, matching the Instructions /\n// Guardrails @ menu), so this box provides the card chrome the mention picker\n// normally would. High z-index: these fields live inside side-modal drawers.\nconst FloaterBox = styled.div<{ $above: boolean; $right: boolean }>`\n\tposition: absolute;\n\tz-index: 1000;\n\twidth: 344px;\n\tbackground: ${COLORS.surface.standard};\n\tborder: 1px solid ${COLORS.stroke.primary};\n\tborder-radius: 4px;\n\tbox-shadow: 0px -2px 8px 0px #0000001f;\n\toverflow: hidden;\n\t${(p) => (p.$right ? 'right: 0;' : 'left: 0;')}\n\t${(p) => (p.$above ? 'bottom: calc(100% + 4px);' : 'top: calc(100% + 4px);')}\n`;\n"],"names":["variableToken","variable","variableTokenName","BOUNDARY","triggerContext","target","caret","prev","sel","text","offset","VariableTriggerArea","variablesData","onInsert","transformToken","addDataButton","children","containerRef","useRef","fieldFocused","setFieldFocused","useState","lastFocusedRef","trigger","setTrigger","enabled","placement","rect","onKeyDownCapture","useCallback","ctx","openFromButton","useEffect","onKey","e","onDown","jsxs","TriggerWrap","jsx","AddDataPill","FloaterBox","VariablePicker","PickerType","raw","token","styled","COLORS","p"],"mappings":"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"}
|
|
@@ -24,12 +24,18 @@ const b = (e, t = "Manifest") => e ? m[e] ?? e : t, E = (e) => {
|
|
|
24
24
|
)
|
|
25
25
|
], g = (e) => e.replace(/^\{\{|\}\}$/g, ""), I = (e = []) => {
|
|
26
26
|
const t = [], n = (a) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const o = a.actualValue;
|
|
28
|
+
if (o) {
|
|
29
|
+
const i = g(o);
|
|
30
|
+
t.push({
|
|
31
|
+
id: i,
|
|
32
|
+
// Chip text is the raw token — designers want variables to read as
|
|
33
|
+
// {{a.b.c}} (styled purple by the editor), not a labelled chip.
|
|
34
|
+
label: `{{${i}}}`,
|
|
35
|
+
meta: { kind: "variable", plainLabel: a.displayName }
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
Object.values(a.variables ?? {}).forEach(n);
|
|
33
39
|
};
|
|
34
40
|
return e.forEach(n), t;
|
|
35
41
|
}, k = (e) => e.tool_key === "send_slack_message" ? String(e.params.channelId?.value ?? "").trim() || "Send Slack message" : String(e.params.sendTo?.value ?? "").split(",").map((t) => t.trim()).filter(Boolean)[0] || "Send Email", $ = (e) => e.name || "Rest API", f = (e) => {
|
|
@@ -49,7 +55,7 @@ const b = (e, t = "Manifest") => e ? m[e] ?? e : t, E = (e) => {
|
|
|
49
55
|
}, M = (e = [], t = []) => [
|
|
50
56
|
...t.map(f),
|
|
51
57
|
...e.map(S)
|
|
52
|
-
],
|
|
58
|
+
], l = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"), s = (e) => l(e).replace(/"/g, """), d = (e, t, n) => `<span data-type="mentionAgent" class="bik-mention bik-mention--agent" data-id="${s(e)}" data-label="${s(t)}" data-kind="${s(n)}">${l(t)}</span>`, y = (e, t, n) => d(e, t, n), T = (e) => {
|
|
53
59
|
const t = /* @__PURE__ */ new Map();
|
|
54
60
|
for (const n of e) {
|
|
55
61
|
const a = n.meta;
|
|
@@ -59,18 +65,18 @@ const b = (e, t = "Manifest") => e ? m[e] ?? e : t, E = (e) => {
|
|
|
59
65
|
}, c = /\{\{((?:tool|subagent|variable):[^{}]+)\}\}/g, v = (e, t) => {
|
|
60
66
|
let n = "", a = 0;
|
|
61
67
|
c.lastIndex = 0;
|
|
62
|
-
let
|
|
63
|
-
for (; (
|
|
64
|
-
n +=
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
67
|
-
const p =
|
|
68
|
-
n += d(String(
|
|
68
|
+
let o;
|
|
69
|
+
for (; (o = c.exec(e)) !== null; ) {
|
|
70
|
+
n += l(e.slice(a, o.index));
|
|
71
|
+
const i = t.get(o[1]);
|
|
72
|
+
if (i) {
|
|
73
|
+
const p = i.meta.kind;
|
|
74
|
+
n += d(String(i.id), i.label, p);
|
|
69
75
|
} else
|
|
70
|
-
n += o
|
|
76
|
+
n += l(o[0]);
|
|
71
77
|
a = c.lastIndex;
|
|
72
78
|
}
|
|
73
|
-
return n +=
|
|
79
|
+
return n += l(e.slice(a)), n.replace(/\n/g, "<br>");
|
|
74
80
|
}, A = (e, t = []) => {
|
|
75
81
|
if (!e)
|
|
76
82
|
return "";
|
|
@@ -101,7 +107,7 @@ const b = (e, t = "Manifest") => e ? m[e] ?? e : t, E = (e) => {
|
|
|
101
107
|
case "bulletList": {
|
|
102
108
|
const n = e.type === "orderedList";
|
|
103
109
|
return (e.content ?? []).map(
|
|
104
|
-
(a,
|
|
110
|
+
(a, o) => `${n ? `${o + 1}.` : "-"} ${u(
|
|
105
111
|
a,
|
|
106
112
|
t
|
|
107
113
|
).replace(/\n+$/, "")}`
|
|
@@ -110,12 +116,12 @@ const b = (e, t = "Manifest") => e ? m[e] ?? e : t, E = (e) => {
|
|
|
110
116
|
`;
|
|
111
117
|
}
|
|
112
118
|
default: {
|
|
113
|
-
const n = (e.content ?? []).map((
|
|
119
|
+
const n = (e.content ?? []).map((o) => u(o, t)).join("");
|
|
114
120
|
return ["paragraph", "heading", "listItem", "blockquote"].includes(e.type) ? `${n}
|
|
115
121
|
` : n;
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
|
-
},
|
|
124
|
+
}, B = (e, t) => {
|
|
119
125
|
if (!e)
|
|
120
126
|
return "";
|
|
121
127
|
const n = new Set(t.map((a) => a.tool_id));
|
|
@@ -133,7 +139,7 @@ export {
|
|
|
133
139
|
I as buildVariableMentionItems,
|
|
134
140
|
S as configToolMentionItem,
|
|
135
141
|
k as configToolName,
|
|
136
|
-
|
|
142
|
+
B as editorDocToInstructions,
|
|
137
143
|
A as instructionsToEditorHtml,
|
|
138
144
|
f as restApiMentionItem,
|
|
139
145
|
$ as restApiToolName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mentionSerialization.js","sources":["../../../../../src/components/agent-builder/utils/mentionSerialization.ts"],"sourcesContent":["import type { ApplicationType } from '@bikdotai/bik-models/growth';\nimport { MentionItem } from '@src/editor';\nimport type {\n\tSubHeader,\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport {\n\tAgentTool,\n\tConfigToolConfig,\n\tRestApiToolConfig,\n\tSubAgentRef,\n} from '../AgentBuilder.model';\nimport { TOOL_SOURCE_LABELS, ToolSource } from '../constants/tools';\n\n/**\n * Serialization helpers between the BikEditor document and the firebase\n * `instructions` string.\n *\n * Firebase stores `instructions` as PLAIN TEXT with inline reference tokens:\n * - `{{subagent:<agentId>}}` — hand-off to another agent\n * - `{{tool:<tool_id>}}` — an action the agent can call\n * - `{{variable:<name>}}` — a template variable picked from the catalog\n *\n * In the editor those references are inserted via the `@` mention menu (which we\n * populate with BOTH tools and sub-agents). Mention nodes keep their `id` in the\n * ProseMirror doc JSON (the rendered HTML drops it), so we serialize from the doc\n * JSON — not from HTML.\n */\n\nexport interface MentionKindMeta {\n\t/** Distinguishes a tool / sub-agent / variable reference. */\n\tkind: 'tool' | 'subagent' | 'variable';\n\t/** Plain item name (no prefix) shown in the picker dropdown rows. */\n\tplainLabel?: string;\n\t/** For tools: the source bucket (manifest / shopify / …) the picker groups by. */\n\tsource?: string;\n\t/** For tools: touches customer-private data → shows a shield in the picker. */\n\tauthRequired?: boolean;\n}\n\n// Source labels moved to constants/tools.ts; re-exported for existing importers.\nexport { TOOL_SOURCE_LABELS };\n\n/** Human label for a tool source; falls back to the host-app label, then the raw key. */\nexport const toolSourceLabel = (\n\tsource: string | undefined,\n\tfallback = 'Manifest',\n): string => {\n\tif (!source) {\n\t\treturn fallback;\n\t}\n\treturn TOOL_SOURCE_LABELS[source as ToolSource] ?? source;\n};\n\n/** Human label for the host application, used as the tool-chip prefix. */\nexport const appTypeLabel = (applicationType?: ApplicationType): string => {\n\tconst key = applicationType ? String(applicationType) : '';\n\tif (key === 'BIK') {\n\t\treturn 'BIK';\n\t}\n\tif (key === 'BSP') {\n\t\treturn 'BSP';\n\t}\n\treturn 'Manifest';\n};\n\n/**\n * Build the combined `@` mention list from the available tools + sub-agents.\n *\n * The `label` is the CHIP text shown once inserted — tools read\n * `\"<App> : <tool name>\"` (e.g. \"Manifest : Look up order\"), sub-agents read\n * `\"sub-agent: <name>\"`. `meta.plainLabel` is the un-prefixed name for the\n * picker rows, and `meta.kind` drives the `{{tool}}` / `{{subagent}}` colour.\n */\nexport const buildAgentMentionItems = (\n\ttools: AgentTool[],\n\tsubAgents: SubAgentRef[],\n\tappLabel = 'Manifest',\n): MentionItem[] => [\n\t...tools.map(\n\t\t(t): MentionItem => ({\n\t\t\tid: t.tool_id,\n\t\t\tlabel: `${toolSourceLabel(t.source, appLabel)} : ${t.display_name}`,\n\t\t\tmeta: {\n\t\t\t\tkind: 'tool',\n\t\t\t\tplainLabel: t.display_name,\n\t\t\t\tsource: t.source,\n\t\t\t\tauthRequired: t.auth_required,\n\t\t\t},\n\t\t}),\n\t),\n\t...subAgents.map(\n\t\t(s): MentionItem => ({\n\t\t\tid: s.id,\n\t\t\tlabel: `sub-agent: ${s.name}`,\n\t\t\tmeta: { kind: 'subagent', plainLabel: s.name },\n\t\t}),\n\t),\n];\n\n/** Strip the `{{ }}` wrapper from a variable token (`{{a.b}}` → `a.b`). */\nexport const variableTokenName = (actualValue: string): string =>\n\tactualValue.replace(/^\\{\\{|\\}\\}$/g, '');\n\n/**\n * Flatten the (nested) variable catalog into mention items, so stored\n * `{{<name>}}` tokens rehydrate as `Variable: <display name>` chips. The item\n * id is the BARE token name (no braces) — `resolveMentionToken` re-wraps it on\n * save. Merge alongside {@link buildAgentMentionItems} for serialization.\n */\nexport const buildVariableMentionItems = (\n\tvariablesData: VariableListInterfaceV3[] = [],\n): MentionItem[] => {\n\tconst items: MentionItem[] = [];\n\tconst walk = (entry: VariableListInterfaceV3 | SubHeader | VariableV3) => {\n\t\tconst actual = (entry as VariableV3).actualValue;\n\t\tif (actual) {\n\t\t\titems.push({\n\t\t\t\tid: variableTokenName(actual),\n\t\t\t\tlabel: `Variable: ${entry.displayName}`,\n\t\t\t\tmeta: { kind: 'variable', plainLabel: entry.displayName },\n\t\t\t});\n\t\t}\n\t\tObject.values(entry.variables ?? {}).forEach(walk);\n\t};\n\tvariablesData.forEach(walk);\n\treturn items;\n};\n\n/** Display name for a shared config tool: first recipient / channel, else generic. */\nexport const configToolName = (t: ConfigToolConfig): string => {\n\tif (t.tool_key === 'send_slack_message') {\n\t\treturn (\n\t\t\tString(t.params['channelId']?.value ?? '').trim() || 'Send Slack message'\n\t\t);\n\t}\n\treturn (\n\t\tString(t.params['sendTo']?.value ?? '')\n\t\t\t.split(',')\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)[0] || 'Send Email'\n\t);\n};\n\n/** Display name for a Rest API tool row / chip. */\nexport const restApiToolName = (t: RestApiToolConfig): string =>\n\tt.name || 'Rest API';\n\n/** The `@`-mention chip item for a Rest API tool (kind 'tool', restapi source). */\nexport const restApiMentionItem = (t: RestApiToolConfig): MentionItem => {\n\tconst name = restApiToolName(t);\n\treturn {\n\t\tid: t.id,\n\t\tlabel: `${TOOL_SOURCE_LABELS[ToolSource.RestApi]} : ${name}`,\n\t\tmeta: { kind: 'tool', plainLabel: name, source: ToolSource.RestApi },\n\t};\n};\n\n/** The `@`-mention chip item for a shared config tool (email / Slack). */\nexport const configToolMentionItem = (t: ConfigToolConfig): MentionItem => {\n\tconst source =\n\t\tt.tool_key === 'send_slack_message'\n\t\t\t? ToolSource.Slack\n\t\t\t: ToolSource.EmailHandover;\n\tconst name = configToolName(t);\n\treturn {\n\t\tid: t.id,\n\t\tlabel: `${TOOL_SOURCE_LABELS[source]} : ${name}`,\n\t\tmeta: { kind: 'tool', plainLabel: name, source },\n\t};\n};\n\n/**\n * Mention items for the inline-configured custom tools (Rest API + email / Slack)\n * so their `{{tool:<id>}}` tokens rehydrate as chips — they live on the agent\n * value, not in the catalog `availableTools`. Merge alongside\n * {@link buildAgentMentionItems} in the editor's mention list.\n */\nexport const buildCustomToolMentionItems = (\n\tconfigTools: ConfigToolConfig[] = [],\n\trestApiTools: RestApiToolConfig[] = [],\n): MentionItem[] => [\n\t...restApiTools.map(restApiMentionItem),\n\t...configTools.map(configToolMentionItem),\n];\n\nconst escapeHtml = (raw: string): string =>\n\traw.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n\nconst escapeAttr = (raw: string): string =>\n\tescapeHtml(raw).replace(/\"/g, '"');\n\n/**\n * The HTML for a single mention chip. Must round-trip through the TipTap mention\n * extension's `parseHTML` (`span[data-type=\"mentionAgent\"]`, reading\n * `data-id`/`data-label`/`data-kind`) — see MentionExtension.ts. `normalizeHtml`\n * is taught to keep `data-type=\"mention…\"` spans so this survives to the parser.\n */\nconst mentionSpanHtml = (id: string, label: string, kind: string): string =>\n\t`<span data-type=\"mentionAgent\" class=\"bik-mention bik-mention--agent\"` +\n\t` data-id=\"${escapeAttr(id)}\" data-label=\"${escapeAttr(label)}\"` +\n\t` data-kind=\"${escapeAttr(kind)}\">${escapeHtml(label)}</span>`;\n\n/**\n * Public: HTML for one mention chip, for inserting at the cursor (e.g. when a\n * tool / sub-agent is added from the Step-3 side panel). Round-trips through the\n * same parser path as the rehydrated chips.\n */\nexport const buildMentionHtml = (\n\tid: string,\n\tlabel: string,\n\tkind: 'tool' | 'subagent' | 'variable',\n): string => mentionSpanHtml(id, label, kind);\n\n/**\n * `{{tool:<id>}}` / `{{subagent:<id>}}` / `{{<variableName>}}` → the mention\n * item that produced them. The map key is the token's INNER text.\n */\nconst buildTokenLookup = (\n\tmentionItems: MentionItem[],\n): Map<string, MentionItem> => {\n\tconst byToken = new Map<string, MentionItem>();\n\tfor (const item of mentionItems) {\n\t\tconst meta = item.meta as unknown as MentionKindMeta | undefined;\n\t\t// Everything is keyed by its kind-prefixed id (a tool's id is its tool_id).\n\t\tif (meta?.kind === 'tool') {\n\t\t\tbyToken.set(`tool:${String(item.id)}`, item);\n\t\t} else if (meta?.kind === 'subagent') {\n\t\t\tbyToken.set(`subagent:${String(item.id)}`, item);\n\t\t} else if (meta?.kind === 'variable') {\n\t\t\tbyToken.set(`variable:${String(item.id)}`, item);\n\t\t}\n\t}\n\treturn byToken;\n};\n\n// Matches the kind-prefixed reference tokens we render as chips. Other\n// `{{var}}` tokens fall through untouched (left as literal text for the\n// variable decoration).\nconst REFERENCE_TOKEN = /\\{\\{((?:tool|subagent|variable):[^{}]+)\\}\\}/g;\n\n/** Escape plain text, swapping recognised reference tokens for mention spans. */\nconst inlineToHtml = (\n\tsegment: string,\n\tbyToken: Map<string, MentionItem>,\n): string => {\n\tlet out = '';\n\tlet lastIndex = 0;\n\tREFERENCE_TOKEN.lastIndex = 0;\n\tlet match: RegExpExecArray | null;\n\twhile ((match = REFERENCE_TOKEN.exec(segment)) !== null) {\n\t\tout += escapeHtml(segment.slice(lastIndex, match.index));\n\t\tconst item = byToken.get(match[1]);\n\t\tif (item) {\n\t\t\tconst kind = (item.meta as unknown as MentionKindMeta).kind;\n\t\t\tout += mentionSpanHtml(String(item.id), item.label, kind);\n\t\t} else {\n\t\t\t// Not in any available list (removed tool / unknown variable) → keep the\n\t\t\t// raw token as literal text.\n\t\t\tout += escapeHtml(match[0]);\n\t\t}\n\t\tlastIndex = REFERENCE_TOKEN.lastIndex;\n\t}\n\tout += escapeHtml(segment.slice(lastIndex));\n\treturn out.replace(/\\n/g, '<br>');\n};\n\n/**\n * Convert the stored instructions string into HTML for `BikEditor.initialContent`.\n * `{{tool:KEY}}` / `{{subagent:ID}}` tokens are rehydrated into mention chips\n * (resolved via `mentionItems`); any other `{{…}}` token is left as literal text\n * for the editor's variable decoration. Newlines become paragraph / line breaks.\n */\nexport const instructionsToEditorHtml = (\n\ttext: string,\n\tmentionItems: MentionItem[] = [],\n): string => {\n\tif (!text) {\n\t\treturn '';\n\t}\n\tconst byToken = buildTokenLookup(mentionItems);\n\treturn text\n\t\t.split(/\\n{2,}/)\n\t\t.map((para) => `<p>${inlineToHtml(para, byToken)}</p>`)\n\t\t.join('');\n};\n\ninterface ProseMirrorNode {\n\ttype: string;\n\ttext?: string;\n\tattrs?: Record<string, unknown>;\n\tcontent?: ProseMirrorNode[];\n}\n\n/**\n * The token for a mention node. All kinds store their id in the node, so the\n * token value is just that id — the kind only picks the prefix. The node's\n * `kind` attr is authoritative; `toolIds` is a fallback for older content\n * saved before the kind attr existed.\n */\nconst resolveMentionToken = (\n\tid: string,\n\tkind: string | null,\n\ttoolIds: Set<string>,\n): string => {\n\tif (kind === 'variable') {\n\t\treturn `{{variable:${id}}}`;\n\t}\n\tconst resolved = kind ?? (toolIds.has(id) ? 'tool' : 'subagent');\n\treturn resolved === 'tool' ? `{{tool:${id}}}` : `{{subagent:${id}}}`;\n};\n\nconst nodeToText = (node: ProseMirrorNode, toolIds: Set<string>): string => {\n\tswitch (node.type) {\n\t\tcase 'text':\n\t\t\treturn node.text ?? '';\n\t\tcase 'mentionAgent':\n\t\tcase 'mentionTeam': {\n\t\t\tconst id = node.attrs?.['id'];\n\t\t\tif (id == null) {\n\t\t\t\treturn '';\n\t\t\t}\n\t\t\tconst kind = node.attrs?.['kind'];\n\t\t\treturn resolveMentionToken(\n\t\t\t\tString(id),\n\t\t\t\tkind == null ? null : String(kind),\n\t\t\t\ttoolIds,\n\t\t\t);\n\t\t}\n\t\tcase 'variable':\n\t\t\treturn `{{${String(node.attrs?.['variableName'] ?? '')}}}`;\n\t\tcase 'hardBreak':\n\t\t\treturn '\\n';\n\t\tcase 'orderedList':\n\t\tcase 'bulletList': {\n\t\t\t// Flattening to plain text otherwise drops the list markers, so a\n\t\t\t// numbered / bulleted list looks \"removed\" after publish. Emit an\n\t\t\t// ordinal (\"1. \") / bullet (\"- \") prefix per item to preserve it.\n\t\t\tconst ordered = node.type === 'orderedList';\n\t\t\treturn (\n\t\t\t\t(node.content ?? [])\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(item, i) =>\n\t\t\t\t\t\t\t`${ordered ? `${i + 1}.` : '-'} ${nodeToText(\n\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\ttoolIds,\n\t\t\t\t\t\t\t).replace(/\\n+$/, '')}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n') + '\\n'\n\t\t\t);\n\t\t}\n\t\tdefault: {\n\t\t\tconst inner = (node.content ?? [])\n\t\t\t\t.map((child) => nodeToText(child, toolIds))\n\t\t\t\t.join('');\n\t\t\t// Block nodes terminate with a newline so paragraphs stay separated.\n\t\t\tconst BLOCK_NODES = ['paragraph', 'heading', 'listItem', 'blockquote'];\n\t\t\treturn BLOCK_NODES.includes(node.type) ? `${inner}\\n` : inner;\n\t\t}\n\t}\n};\n\n/**\n * Convert a BikEditor document (from `ref.getJSON()`) into the firebase\n * instructions string, replacing mention nodes with `{{subagent:<id>}}` /\n * `{{tool:<tool_id>}}` tokens.\n */\nexport const editorDocToInstructions = (\n\tdoc: Record<string, unknown> | null,\n\ttools: AgentTool[],\n): string => {\n\tif (!doc) {\n\t\treturn '';\n\t}\n\tconst toolIds = new Set(tools.map((t) => t.tool_id));\n\treturn nodeToText(doc as unknown as ProseMirrorNode, toolIds).replace(\n\t\t/\\n+$/,\n\t\t'',\n\t);\n};\n"],"names":["toolSourceLabel","source","fallback","TOOL_SOURCE_LABELS","appTypeLabel","applicationType","key","buildAgentMentionItems","tools","subAgents","appLabel","t","s","variableTokenName","actualValue","buildVariableMentionItems","variablesData","items","walk","entry","actual","configToolName","restApiToolName","restApiMentionItem","name","ToolSource","configToolMentionItem","buildCustomToolMentionItems","configTools","restApiTools","escapeHtml","raw","escapeAttr","mentionSpanHtml","id","label","kind","buildMentionHtml","buildTokenLookup","mentionItems","byToken","item","meta","REFERENCE_TOKEN","inlineToHtml","segment","out","lastIndex","match","instructionsToEditorHtml","text","para","resolveMentionToken","toolIds","nodeToText","node","ordered","inner","child","editorDocToInstructions","doc"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mentionSerialization.js","sources":["../../../../../src/components/agent-builder/utils/mentionSerialization.ts"],"sourcesContent":["import type { ApplicationType } from '@bikdotai/bik-models/growth';\nimport { MentionItem } from '@src/editor';\nimport type {\n\tSubHeader,\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport {\n\tAgentTool,\n\tConfigToolConfig,\n\tRestApiToolConfig,\n\tSubAgentRef,\n} from '../AgentBuilder.model';\nimport { TOOL_SOURCE_LABELS, ToolSource } from '../constants/tools';\n\n/**\n * Serialization helpers between the BikEditor document and the firebase\n * `instructions` string.\n *\n * Firebase stores `instructions` as PLAIN TEXT with inline reference tokens:\n * - `{{subagent:<agentId>}}` — hand-off to another agent\n * - `{{tool:<tool_id>}}` — an action the agent can call\n * - `{{variable:<name>}}` — a template variable picked from the catalog\n *\n * In the editor those references are inserted via the `@` mention menu (which we\n * populate with BOTH tools and sub-agents). Mention nodes keep their `id` in the\n * ProseMirror doc JSON (the rendered HTML drops it), so we serialize from the doc\n * JSON — not from HTML.\n */\n\nexport interface MentionKindMeta {\n\t/** Distinguishes a tool / sub-agent / variable reference. */\n\tkind: 'tool' | 'subagent' | 'variable';\n\t/** Plain item name (no prefix) shown in the picker dropdown rows. */\n\tplainLabel?: string;\n\t/** For tools: the source bucket (manifest / shopify / …) the picker groups by. */\n\tsource?: string;\n\t/** For tools: touches customer-private data → shows a shield in the picker. */\n\tauthRequired?: boolean;\n}\n\n// Source labels moved to constants/tools.ts; re-exported for existing importers.\nexport { TOOL_SOURCE_LABELS };\n\n/** Human label for a tool source; falls back to the host-app label, then the raw key. */\nexport const toolSourceLabel = (\n\tsource: string | undefined,\n\tfallback = 'Manifest',\n): string => {\n\tif (!source) {\n\t\treturn fallback;\n\t}\n\treturn TOOL_SOURCE_LABELS[source as ToolSource] ?? source;\n};\n\n/** Human label for the host application, used as the tool-chip prefix. */\nexport const appTypeLabel = (applicationType?: ApplicationType): string => {\n\tconst key = applicationType ? String(applicationType) : '';\n\tif (key === 'BIK') {\n\t\treturn 'BIK';\n\t}\n\tif (key === 'BSP') {\n\t\treturn 'BSP';\n\t}\n\treturn 'Manifest';\n};\n\n/**\n * Build the combined `@` mention list from the available tools + sub-agents.\n *\n * The `label` is the CHIP text shown once inserted — tools read\n * `\"<App> : <tool name>\"` (e.g. \"Manifest : Look up order\"), sub-agents read\n * `\"sub-agent: <name>\"`. `meta.plainLabel` is the un-prefixed name for the\n * picker rows, and `meta.kind` drives the `{{tool}}` / `{{subagent}}` colour.\n */\nexport const buildAgentMentionItems = (\n\ttools: AgentTool[],\n\tsubAgents: SubAgentRef[],\n\tappLabel = 'Manifest',\n): MentionItem[] => [\n\t...tools.map(\n\t\t(t): MentionItem => ({\n\t\t\tid: t.tool_id,\n\t\t\tlabel: `${toolSourceLabel(t.source, appLabel)} : ${t.display_name}`,\n\t\t\tmeta: {\n\t\t\t\tkind: 'tool',\n\t\t\t\tplainLabel: t.display_name,\n\t\t\t\tsource: t.source,\n\t\t\t\tauthRequired: t.auth_required,\n\t\t\t},\n\t\t}),\n\t),\n\t...subAgents.map(\n\t\t(s): MentionItem => ({\n\t\t\tid: s.id,\n\t\t\tlabel: `sub-agent: ${s.name}`,\n\t\t\tmeta: { kind: 'subagent', plainLabel: s.name },\n\t\t}),\n\t),\n];\n\n/** Strip the `{{ }}` wrapper from a variable token (`{{a.b}}` → `a.b`). */\nexport const variableTokenName = (actualValue: string): string =>\n\tactualValue.replace(/^\\{\\{|\\}\\}$/g, '');\n\n/**\n * Flatten the (nested) variable catalog into mention items, so stored\n * `{{<name>}}` tokens rehydrate as `Variable: <display name>` chips. The item\n * id is the BARE token name (no braces) — `resolveMentionToken` re-wraps it on\n * save. Merge alongside {@link buildAgentMentionItems} for serialization.\n */\nexport const buildVariableMentionItems = (\n\tvariablesData: VariableListInterfaceV3[] = [],\n): MentionItem[] => {\n\tconst items: MentionItem[] = [];\n\tconst walk = (entry: VariableListInterfaceV3 | SubHeader | VariableV3) => {\n\t\tconst actual = (entry as VariableV3).actualValue;\n\t\tif (actual) {\n\t\t\tconst name = variableTokenName(actual);\n\t\t\titems.push({\n\t\t\t\tid: name,\n\t\t\t\t// Chip text is the raw token — designers want variables to read as\n\t\t\t\t// {{a.b.c}} (styled purple by the editor), not a labelled chip.\n\t\t\t\tlabel: `{{${name}}}`,\n\t\t\t\tmeta: { kind: 'variable', plainLabel: entry.displayName },\n\t\t\t});\n\t\t}\n\t\tObject.values(entry.variables ?? {}).forEach(walk);\n\t};\n\tvariablesData.forEach(walk);\n\treturn items;\n};\n\n/** Display name for a shared config tool: first recipient / channel, else generic. */\nexport const configToolName = (t: ConfigToolConfig): string => {\n\tif (t.tool_key === 'send_slack_message') {\n\t\treturn (\n\t\t\tString(t.params['channelId']?.value ?? '').trim() || 'Send Slack message'\n\t\t);\n\t}\n\treturn (\n\t\tString(t.params['sendTo']?.value ?? '')\n\t\t\t.split(',')\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)[0] || 'Send Email'\n\t);\n};\n\n/** Display name for a Rest API tool row / chip. */\nexport const restApiToolName = (t: RestApiToolConfig): string =>\n\tt.name || 'Rest API';\n\n/** The `@`-mention chip item for a Rest API tool (kind 'tool', restapi source). */\nexport const restApiMentionItem = (t: RestApiToolConfig): MentionItem => {\n\tconst name = restApiToolName(t);\n\treturn {\n\t\tid: t.id,\n\t\tlabel: `${TOOL_SOURCE_LABELS[ToolSource.RestApi]} : ${name}`,\n\t\tmeta: { kind: 'tool', plainLabel: name, source: ToolSource.RestApi },\n\t};\n};\n\n/** The `@`-mention chip item for a shared config tool (email / Slack). */\nexport const configToolMentionItem = (t: ConfigToolConfig): MentionItem => {\n\tconst source =\n\t\tt.tool_key === 'send_slack_message'\n\t\t\t? ToolSource.Slack\n\t\t\t: ToolSource.EmailHandover;\n\tconst name = configToolName(t);\n\treturn {\n\t\tid: t.id,\n\t\tlabel: `${TOOL_SOURCE_LABELS[source]} : ${name}`,\n\t\tmeta: { kind: 'tool', plainLabel: name, source },\n\t};\n};\n\n/**\n * Mention items for the inline-configured custom tools (Rest API + email / Slack)\n * so their `{{tool:<id>}}` tokens rehydrate as chips — they live on the agent\n * value, not in the catalog `availableTools`. Merge alongside\n * {@link buildAgentMentionItems} in the editor's mention list.\n */\nexport const buildCustomToolMentionItems = (\n\tconfigTools: ConfigToolConfig[] = [],\n\trestApiTools: RestApiToolConfig[] = [],\n): MentionItem[] => [\n\t...restApiTools.map(restApiMentionItem),\n\t...configTools.map(configToolMentionItem),\n];\n\nconst escapeHtml = (raw: string): string =>\n\traw.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n\nconst escapeAttr = (raw: string): string =>\n\tescapeHtml(raw).replace(/\"/g, '"');\n\n/**\n * The HTML for a single mention chip. Must round-trip through the TipTap mention\n * extension's `parseHTML` (`span[data-type=\"mentionAgent\"]`, reading\n * `data-id`/`data-label`/`data-kind`) — see MentionExtension.ts. `normalizeHtml`\n * is taught to keep `data-type=\"mention…\"` spans so this survives to the parser.\n */\nconst mentionSpanHtml = (id: string, label: string, kind: string): string =>\n\t`<span data-type=\"mentionAgent\" class=\"bik-mention bik-mention--agent\"` +\n\t` data-id=\"${escapeAttr(id)}\" data-label=\"${escapeAttr(label)}\"` +\n\t` data-kind=\"${escapeAttr(kind)}\">${escapeHtml(label)}</span>`;\n\n/**\n * Public: HTML for one mention chip, for inserting at the cursor (e.g. when a\n * tool / sub-agent is added from the Step-3 side panel). Round-trips through the\n * same parser path as the rehydrated chips.\n */\nexport const buildMentionHtml = (\n\tid: string,\n\tlabel: string,\n\tkind: 'tool' | 'subagent' | 'variable',\n): string => mentionSpanHtml(id, label, kind);\n\n/**\n * `{{tool:<id>}}` / `{{subagent:<id>}}` / `{{<variableName>}}` → the mention\n * item that produced them. The map key is the token's INNER text.\n */\nconst buildTokenLookup = (\n\tmentionItems: MentionItem[],\n): Map<string, MentionItem> => {\n\tconst byToken = new Map<string, MentionItem>();\n\tfor (const item of mentionItems) {\n\t\tconst meta = item.meta as unknown as MentionKindMeta | undefined;\n\t\t// Everything is keyed by its kind-prefixed id (a tool's id is its tool_id).\n\t\tif (meta?.kind === 'tool') {\n\t\t\tbyToken.set(`tool:${String(item.id)}`, item);\n\t\t} else if (meta?.kind === 'subagent') {\n\t\t\tbyToken.set(`subagent:${String(item.id)}`, item);\n\t\t} else if (meta?.kind === 'variable') {\n\t\t\tbyToken.set(`variable:${String(item.id)}`, item);\n\t\t}\n\t}\n\treturn byToken;\n};\n\n// Matches the kind-prefixed reference tokens we render as chips. Other\n// `{{var}}` tokens fall through untouched (left as literal text for the\n// variable decoration).\nconst REFERENCE_TOKEN = /\\{\\{((?:tool|subagent|variable):[^{}]+)\\}\\}/g;\n\n/** Escape plain text, swapping recognised reference tokens for mention spans. */\nconst inlineToHtml = (\n\tsegment: string,\n\tbyToken: Map<string, MentionItem>,\n): string => {\n\tlet out = '';\n\tlet lastIndex = 0;\n\tREFERENCE_TOKEN.lastIndex = 0;\n\tlet match: RegExpExecArray | null;\n\twhile ((match = REFERENCE_TOKEN.exec(segment)) !== null) {\n\t\tout += escapeHtml(segment.slice(lastIndex, match.index));\n\t\tconst item = byToken.get(match[1]);\n\t\tif (item) {\n\t\t\tconst kind = (item.meta as unknown as MentionKindMeta).kind;\n\t\t\tout += mentionSpanHtml(String(item.id), item.label, kind);\n\t\t} else {\n\t\t\t// Not in any available list (removed tool / unknown variable) → keep the\n\t\t\t// raw token as literal text.\n\t\t\tout += escapeHtml(match[0]);\n\t\t}\n\t\tlastIndex = REFERENCE_TOKEN.lastIndex;\n\t}\n\tout += escapeHtml(segment.slice(lastIndex));\n\treturn out.replace(/\\n/g, '<br>');\n};\n\n/**\n * Convert the stored instructions string into HTML for `BikEditor.initialContent`.\n * `{{tool:KEY}}` / `{{subagent:ID}}` tokens are rehydrated into mention chips\n * (resolved via `mentionItems`); any other `{{…}}` token is left as literal text\n * for the editor's variable decoration. Newlines become paragraph / line breaks.\n */\nexport const instructionsToEditorHtml = (\n\ttext: string,\n\tmentionItems: MentionItem[] = [],\n): string => {\n\tif (!text) {\n\t\treturn '';\n\t}\n\tconst byToken = buildTokenLookup(mentionItems);\n\treturn text\n\t\t.split(/\\n{2,}/)\n\t\t.map((para) => `<p>${inlineToHtml(para, byToken)}</p>`)\n\t\t.join('');\n};\n\ninterface ProseMirrorNode {\n\ttype: string;\n\ttext?: string;\n\tattrs?: Record<string, unknown>;\n\tcontent?: ProseMirrorNode[];\n}\n\n/**\n * The token for a mention node. All kinds store their id in the node, so the\n * token value is just that id — the kind only picks the prefix. The node's\n * `kind` attr is authoritative; `toolIds` is a fallback for older content\n * saved before the kind attr existed.\n */\nconst resolveMentionToken = (\n\tid: string,\n\tkind: string | null,\n\ttoolIds: Set<string>,\n): string => {\n\tif (kind === 'variable') {\n\t\treturn `{{variable:${id}}}`;\n\t}\n\tconst resolved = kind ?? (toolIds.has(id) ? 'tool' : 'subagent');\n\treturn resolved === 'tool' ? `{{tool:${id}}}` : `{{subagent:${id}}}`;\n};\n\nconst nodeToText = (node: ProseMirrorNode, toolIds: Set<string>): string => {\n\tswitch (node.type) {\n\t\tcase 'text':\n\t\t\treturn node.text ?? '';\n\t\tcase 'mentionAgent':\n\t\tcase 'mentionTeam': {\n\t\t\tconst id = node.attrs?.['id'];\n\t\t\tif (id == null) {\n\t\t\t\treturn '';\n\t\t\t}\n\t\t\tconst kind = node.attrs?.['kind'];\n\t\t\treturn resolveMentionToken(\n\t\t\t\tString(id),\n\t\t\t\tkind == null ? null : String(kind),\n\t\t\t\ttoolIds,\n\t\t\t);\n\t\t}\n\t\tcase 'variable':\n\t\t\treturn `{{${String(node.attrs?.['variableName'] ?? '')}}}`;\n\t\tcase 'hardBreak':\n\t\t\treturn '\\n';\n\t\tcase 'orderedList':\n\t\tcase 'bulletList': {\n\t\t\t// Flattening to plain text otherwise drops the list markers, so a\n\t\t\t// numbered / bulleted list looks \"removed\" after publish. Emit an\n\t\t\t// ordinal (\"1. \") / bullet (\"- \") prefix per item to preserve it.\n\t\t\tconst ordered = node.type === 'orderedList';\n\t\t\treturn (\n\t\t\t\t(node.content ?? [])\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(item, i) =>\n\t\t\t\t\t\t\t`${ordered ? `${i + 1}.` : '-'} ${nodeToText(\n\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t\ttoolIds,\n\t\t\t\t\t\t\t).replace(/\\n+$/, '')}`,\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n') + '\\n'\n\t\t\t);\n\t\t}\n\t\tdefault: {\n\t\t\tconst inner = (node.content ?? [])\n\t\t\t\t.map((child) => nodeToText(child, toolIds))\n\t\t\t\t.join('');\n\t\t\t// Block nodes terminate with a newline so paragraphs stay separated.\n\t\t\tconst BLOCK_NODES = ['paragraph', 'heading', 'listItem', 'blockquote'];\n\t\t\treturn BLOCK_NODES.includes(node.type) ? `${inner}\\n` : inner;\n\t\t}\n\t}\n};\n\n/**\n * Convert a BikEditor document (from `ref.getJSON()`) into the firebase\n * instructions string, replacing mention nodes with `{{subagent:<id>}}` /\n * `{{tool:<tool_id>}}` tokens.\n */\nexport const editorDocToInstructions = (\n\tdoc: Record<string, unknown> | null,\n\ttools: AgentTool[],\n): string => {\n\tif (!doc) {\n\t\treturn '';\n\t}\n\tconst toolIds = new Set(tools.map((t) => t.tool_id));\n\treturn nodeToText(doc as unknown as ProseMirrorNode, toolIds).replace(\n\t\t/\\n+$/,\n\t\t'',\n\t);\n};\n"],"names":["toolSourceLabel","source","fallback","TOOL_SOURCE_LABELS","appTypeLabel","applicationType","key","buildAgentMentionItems","tools","subAgents","appLabel","t","s","variableTokenName","actualValue","buildVariableMentionItems","variablesData","items","walk","entry","actual","name","configToolName","restApiToolName","restApiMentionItem","ToolSource","configToolMentionItem","buildCustomToolMentionItems","configTools","restApiTools","escapeHtml","raw","escapeAttr","mentionSpanHtml","id","label","kind","buildMentionHtml","buildTokenLookup","mentionItems","byToken","item","meta","REFERENCE_TOKEN","inlineToHtml","segment","out","lastIndex","match","instructionsToEditorHtml","text","para","resolveMentionToken","toolIds","nodeToText","node","ordered","i","inner","child","editorDocToInstructions","doc"],"mappings":"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"}
|
|
@@ -3,12 +3,12 @@ import { getMarkRange as xt } from "../node_modules/@tiptap/core/dist/index.js";
|
|
|
3
3
|
import { useEditor as vt, EditorContent as X } from "@tiptap/react";
|
|
4
4
|
import { forwardRef as bt, useState as A, useRef as a, useEffect as Et, useImperativeHandle as At } from "react";
|
|
5
5
|
import { BodyCaption as Y } from "../components/TypographyStyle.js";
|
|
6
|
-
import { BikEditorShell as Tt, BikJsonHeader as Rt, BikJsonBody as It, BikJsonEmptyState as Jt, BikJsonError as
|
|
6
|
+
import { BikEditorShell as Tt, BikJsonHeader as Rt, BikJsonBody as It, BikJsonEmptyState as Jt, BikJsonError as Mt } from "./BikEditor.styles.js";
|
|
7
7
|
import { DEFAULT_FORMAT_STATE as tt } from "./BikEditor.types.js";
|
|
8
|
-
import { buildJsonCodeBlockHtml as
|
|
8
|
+
import { buildJsonCodeBlockHtml as Lt, buildSectionedContent as et, normalizeHtml as i, extractActiveFormats as nt, extractContent as ot, getCodeBlockText as g, parseJson as p, extractBodyContent as Ot, extractSectionContent as Ft, setCodeBlockText as x, findSectionEndPos as rt, findSectionStartPos as zt, setSectionContentInEditor as T, insertInlineHtml as Nt } from "./BikEditor.utils.js";
|
|
9
9
|
import { buildExtensions as jt } from "./extensions/buildExtensions.js";
|
|
10
|
-
import { LinkBubbleMenu as
|
|
11
|
-
import
|
|
10
|
+
import { LinkBubbleMenu as wt } from "./floating/LinkBubbleMenu.js";
|
|
11
|
+
import Ht from "../icons/BIK AI specific/WandSparkles.js";
|
|
12
12
|
import Pt from "../icons/Actions/Formatting/Copy.js";
|
|
13
13
|
import Dt from "../icons/Actions/Common actions/Code.js";
|
|
14
14
|
import qt from "../icons/Informational/General/Info.js";
|
|
@@ -34,8 +34,8 @@ const $t = (k, v) => {
|
|
|
34
34
|
onReady: I,
|
|
35
35
|
onChange: J,
|
|
36
36
|
onSend: b,
|
|
37
|
-
onFocus:
|
|
38
|
-
onBlur:
|
|
37
|
+
onFocus: M,
|
|
38
|
+
onBlur: L,
|
|
39
39
|
onSelectionChange: O,
|
|
40
40
|
minHeight: ut,
|
|
41
41
|
maxHeight: mt,
|
|
@@ -44,9 +44,9 @@ const $t = (k, v) => {
|
|
|
44
44
|
editorClassName: F,
|
|
45
45
|
editorStyle: z,
|
|
46
46
|
renderJsonError: N
|
|
47
|
-
} = k, m = !!B?.jsonMode, [f, j] = A(null), [ht,
|
|
47
|
+
} = k, m = !!B?.jsonMode, [f, j] = A(null), [ht, w] = A(!1), [pt, H] = A(
|
|
48
48
|
m && !(C ?? "").trim()
|
|
49
|
-
), Ct = m ?
|
|
49
|
+
), Ct = m ? Lt(C ?? "") : y?.length ? et(
|
|
50
50
|
i(C ?? ""),
|
|
51
51
|
y.map((t) => ({ ...t, content: i(t.content) }))
|
|
52
52
|
) : i(C ?? ""), E = a([]), P = a(!1), D = a(I);
|
|
@@ -55,10 +55,10 @@ const $t = (k, v) => {
|
|
|
55
55
|
q.current = J;
|
|
56
56
|
const U = a(O);
|
|
57
57
|
U.current = O;
|
|
58
|
-
const $ = a(tt), W = a(
|
|
59
|
-
W.current =
|
|
60
|
-
const V = a(
|
|
61
|
-
V.current =
|
|
58
|
+
const $ = a(tt), W = a(M);
|
|
59
|
+
W.current = M;
|
|
60
|
+
const V = a(L);
|
|
61
|
+
V.current = L;
|
|
62
62
|
const _ = a(b);
|
|
63
63
|
_.current = b;
|
|
64
64
|
const Q = a(d?.agents ?? []);
|
|
@@ -104,7 +104,7 @@ const $t = (k, v) => {
|
|
|
104
104
|
onUpdate: ({ editor: t }) => {
|
|
105
105
|
if (q.current?.(ot(t)), m) {
|
|
106
106
|
const e = g(t), o = p(e);
|
|
107
|
-
j(o.valid ? null : o),
|
|
107
|
+
j(o.valid ? null : o), H(!e.trim());
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
onTransaction: ({ editor: t }) => {
|
|
@@ -120,7 +120,7 @@ const $t = (k, v) => {
|
|
|
120
120
|
Et(() => {
|
|
121
121
|
if (n && (E.current.length > 0 && E.current.splice(0).forEach((e) => e(n)), P.current || (P.current = !0, D.current?.()), m)) {
|
|
122
122
|
const t = g(n), e = p(t);
|
|
123
|
-
j(e.valid ? null : e),
|
|
123
|
+
j(e.valid ? null : e), H(!t.trim());
|
|
124
124
|
}
|
|
125
125
|
}, [n, m]), At(
|
|
126
126
|
v,
|
|
@@ -299,6 +299,12 @@ const $t = (k, v) => {
|
|
|
299
299
|
insertEmoji: (t) => n?.chain().focus().insertContent(t).run(),
|
|
300
300
|
insertVariable: (t) => n?.chain().focus().insertContent(`{{${t}}}`).run(),
|
|
301
301
|
insertHtml: (t) => n?.commands.insertContent(i(t)),
|
|
302
|
+
openMentionMenu: (t = "@") => {
|
|
303
|
+
if (!n)
|
|
304
|
+
return;
|
|
305
|
+
const { from: e } = n.state.selection, o = e > 1 ? n.state.doc.textBetween(e - 1, e) : "", r = o !== "" && !/\s/.test(o);
|
|
306
|
+
n.chain().focus().insertContent(r ? ` ${t}` : t).run();
|
|
307
|
+
},
|
|
302
308
|
undo: () => n?.chain().focus().undo().run(),
|
|
303
309
|
redo: () => n?.chain().focus().redo().run(),
|
|
304
310
|
canUndo: () => n?.can().undo() ?? !1,
|
|
@@ -331,7 +337,7 @@ const $t = (k, v) => {
|
|
|
331
337
|
const { valid: t, value: e } = p(g(n));
|
|
332
338
|
!t || e === null || x(n, JSON.stringify(e, null, 2));
|
|
333
339
|
}, kt = () => {
|
|
334
|
-
n && (navigator.clipboard?.writeText(g(n)),
|
|
340
|
+
n && (navigator.clipboard?.writeText(g(n)), w(!0), setTimeout(() => w(!1), 1500));
|
|
335
341
|
};
|
|
336
342
|
return n ? /* @__PURE__ */ u(
|
|
337
343
|
Tt,
|
|
@@ -354,7 +360,7 @@ const $t = (k, v) => {
|
|
|
354
360
|
disabled: !!f,
|
|
355
361
|
title: "Format JSON",
|
|
356
362
|
children: [
|
|
357
|
-
/* @__PURE__ */ l(
|
|
363
|
+
/* @__PURE__ */ l(Ht, { size: 16 }),
|
|
358
364
|
/* @__PURE__ */ l(Y, { children: "Beautify" })
|
|
359
365
|
]
|
|
360
366
|
}
|
|
@@ -372,8 +378,8 @@ const $t = (k, v) => {
|
|
|
372
378
|
/* @__PURE__ */ l("span", { children: k.placeholder ?? "Type or paste JSON here" })
|
|
373
379
|
] })
|
|
374
380
|
] }) : /* @__PURE__ */ l(X, { editor: n }),
|
|
375
|
-
/* @__PURE__ */ l(
|
|
376
|
-
m && f ? N ? N(f) : /* @__PURE__ */ u(
|
|
381
|
+
/* @__PURE__ */ l(wt, { editor: n, renderLinkTooltip: lt?.renderTooltip }),
|
|
382
|
+
m && f ? N ? N(f) : /* @__PURE__ */ u(Mt, { children: [
|
|
377
383
|
/* @__PURE__ */ l(qt, { size: 16 }),
|
|
378
384
|
/* @__PURE__ */ u("span", { children: [
|
|
379
385
|
f.line != null && /* @__PURE__ */ u("strong", { children: [
|