@bikdotai/bik-component-library 0.0.849-beta.5 → 0.0.849-beta.6
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/ConfigToolPanel.js +10 -10
- package/dist/cjs/components/agent-builder/components/ConfigToolPanel.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/CreateSubAgentPanel.js +2 -2
- package/dist/cjs/components/agent-builder/components/CreateSubAgentPanel.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/MentionPicker.js +49 -25
- package/dist/cjs/components/agent-builder/components/MentionPicker.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/RestApiToolPanel.js +9 -8
- package/dist/cjs/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/dist/cjs/components/agent-builder/components/VariableInlineField.js +56 -0
- package/dist/cjs/components/agent-builder/components/VariableInlineField.js.map +1 -0
- package/dist/cjs/components/agent-builder/components/VariableTriggerArea.js +5 -10
- package/dist/cjs/components/agent-builder/components/VariableTriggerArea.js.map +1 -1
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js +164 -149
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/CreateSubAgentPanel.js +15 -13
- package/dist/esm/components/agent-builder/components/CreateSubAgentPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/MentionPicker.js +199 -165
- package/dist/esm/components/agent-builder/components/MentionPicker.js.map +1 -1
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.d.ts +3 -2
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js +160 -197
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/VariableInlineField.d.ts +26 -0
- package/dist/esm/components/agent-builder/components/VariableInlineField.js +142 -0
- package/dist/esm/components/agent-builder/components/VariableInlineField.js.map +1 -0
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.d.ts +13 -0
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.js +75 -66
- package/dist/esm/components/agent-builder/components/VariableTriggerArea.js.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentionPicker.js","sources":["../../../../../src/components/agent-builder/components/MentionPicker.tsx"],"sourcesContent":["// import ChevronLeft from '@src/assets/icons/chevronLeft.svg';\nimport ChevronRight from '@src/assets/icons/chevronRight.svg';\nimport SearchIcon from '@src/assets/icons/searchIcon.svg';\nimport type { MentionDropdownRenderProps, MentionItem } from '@src/editor';\nimport { Shield } from '@src/icons';\nimport SvgArrowLeft from '@src/icons/Arrows/Normal arrows/ArrowLeft';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport {\n\tBodyCaption,\n\tBodySecondary,\n\tTitleSmall,\n} from '@src/components/TypographyStyle';\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 {\n\tCATEGORIES,\n\tCONFIG_TOOL_SOURCES,\n\ttoolSourceRank,\n} from '../constants/tools';\nimport type { Category, ToolAction, ToolSource } from '../constants/tools';\nimport {\n\tMentionKindMeta,\n\ttoolSourceLabel,\n} from '../utils/mentionSerialization';\nimport { renderSourceIcon, sourceIcon, SourceIcon } from '../utils/sourceIcons';\n\nconst metaOf = (item: MentionItem): MentionKindMeta | undefined =>\n\titem.meta as unknown as MentionKindMeta | undefined;\n\nconst rowLabel = (item: MentionItem): string =>\n\tmetaOf(item)?.plainLabel ?? item.label;\n\n/** One navigable row in the picker's current view. */\ntype PickerRowModel = {\n\tid: string;\n\tlabel: string;\n\t/** Drill-in rows show a chevron; leaf (selectable) rows do not. */\n\tdrillsIn: boolean;\n\t/** Leading brand glyph (source rows only). */\n\ticon?: SourceIcon;\n\t/** Brand-coloured \"Create new\" action row (not a selectable item). */\n\tisCreate?: boolean;\n\t/**\n\t * Brand-coloured right-aligned action text (e.g. \"Connect\") shown in place of\n\t * the drill-in chevron — used for an unconnected integration source.\n\t */\n\trightLabel?: string;\n\t/** Tool leaf touches customer-private data → shows a shield glyph. */\n\tauthRequired?: boolean;\n\tonActivate: () => void;\n};\n\n/** Sentinel id for the sub-agent \"Create new\" row. */\nconst CREATE_SUBAGENT_ID = '__bik_create_subagent__';\n\ntype MentionPickerProps = MentionDropdownRenderProps & {\n\t/** Opens the create-sub-agent drawer (shows a \"Create new\" row in the Sub-agent view). */\n\tonCreateSubAgent?: () => void;\n\t/**\n\t * Open already drilled into this tool source (e.g. `'shopify'`) — its tool list\n\t * is shown immediately. */\n\tinitialSource?: string;\n\t/**\n\t * Built-in tool actions (Rest API / Handover to email) shown in the Tools view.\n\t * Picking one opens its config drawer (via `onSelect`) and dismisses the picker\n\t * — it never inserts a mention into the text.\n\t */\n\ttoolActions?: ToolAction[];\n\t/**\n\t * Per-source integration connection state. A source whose value is `false`\n\t * renders a \"Connect\" row (like an unconnected action) instead of drilling in —\n\t * so native source groups (Shopify / BigCommerce / ShipStation) show \"Connect\"\n\t * consistently with the config actions (Slack).\n\t */\n\tintegrationStatus?: Partial<Record<ToolSource, boolean>>;\n\t/** Open the connect flow for an unconnected source (used by the Connect row). */\n\tonConnectIntegration?: (source: ToolSource) => void;\n\t/**\n\t * Variable catalog for the \"Variables\" view (the shared VariablePicker's\n\t * `data`). Omitted / empty → the Variables category is not offered.\n\t */\n\tvariablesData?: VariableListInterfaceV3[];\n\t/**\n\t * A variable was picked from the Variables view. The picker dismisses itself\n\t * (stripping the typed `@`) BEFORE calling this — the handler inserts a\n\t * `Variable: <name>` mention chip (see MentionEditor).\n\t */\n\tonSelectVariable?: (variable: VariableV3) => void;\n};\n\n/**\n * `@` reference picker. Drills:\n * root → Tools → <source> (Manifest / Shopify / …) → tool\n * → Sub-agent → sub-agent\n * → Variables → shared VariablePicker (embedded)\n * (\"Add condition\" is intentionally not shown yet.)\n *\n * Owns keyboard navigation (↑/↓ move, →/Enter activate, ← go back) over the\n * CURRENTLY visible rows — registered with the editor via `registerKeyHandler`.\n */\nexport const MentionPicker: React.FC<MentionPickerProps> = ({\n\titems,\n\tonSelect,\n\tregisterKeyHandler,\n\tonClose,\n\tonCreateSubAgent,\n\tinitialSource,\n\ttoolActions,\n\tintegrationStatus,\n\tonConnectIntegration,\n\tvariablesData,\n\tonSelectVariable,\n}) => {\n\tconst [category, setCategory] = useState<Category | null>(null);\n\tconst [source, setSource] = useState<string | null>(initialSource ?? null);\n\t// -1 = nothing highlighted; rows highlight only on hover / arrow keys\n\t// (a default highlight reads as a stuck hover state).\n\tconst [activeIndex, setActiveIndex] = useState(-1);\n\t// Search box shown inside a source's tool list (Manifest / Shopify); filters\n\t// that source's tools by name.\n\tconst [search, setSearch] = useState('');\n\n\tconst tools = useMemo(\n\t\t() => items.filter((i) => metaOf(i)?.kind === 'tool'),\n\t\t[items],\n\t);\n\tconst subAgents = useMemo(\n\t\t() => items.filter((i) => metaOf(i)?.kind === 'subagent'),\n\t\t[items],\n\t);\n\tconst sources = useMemo(() => {\n\t\tconst acc: string[] = [];\n\t\ttools.forEach((t) => {\n\t\t\tconst s = metaOf(t)?.source ?? 'other';\n\t\t\tif (!acc.includes(s)) {\n\t\t\t\tacc.push(s);\n\t\t\t}\n\t\t});\n\t\treturn acc;\n\t}, [tools]);\n\t// The built-in \"create\" action per source (Rest API / email / Slack). A source\n\t// with an action drills into [its instances + a \"Create new\" row] — like the\n\t// Sub-agent category — instead of showing a duplicate flat \"create\" row.\n\tconst actionBySource = useMemo(\n\t\t() =>\n\t\t\tnew Map<string, ToolAction>(\n\t\t\t\t(toolActions ?? []).map((a) => [a.source, a]),\n\t\t\t),\n\t\t[toolActions],\n\t);\n\n\t// Show a category when it has items — and ALWAYS show Sub-agent when creating\n\t// one is possible, so an agent with no sub-agents yet still gets the root\n\t// \"Tools / Sub-agent\" choice (with a \"Create new\" inside). When only one is\n\t// available (e.g. the sub-agent drawer offers tools only) the selector is skipped.\n\tconst availableCategories = useMemo(\n\t\t() =>\n\t\t\tCATEGORIES.filter((c) => {\n\t\t\t\tif (c.key === 'tool') {\n\t\t\t\t\treturn tools.length > 0 || (toolActions?.length ?? 0) > 0;\n\t\t\t\t}\n\t\t\t\tif (c.key === 'variable') {\n\t\t\t\t\treturn (variablesData?.length ?? 0) > 0 && !!onSelectVariable;\n\t\t\t\t}\n\t\t\t\treturn subAgents.length > 0 || !!onCreateSubAgent;\n\t\t\t}),\n\t\t[\n\t\t\ttools.length,\n\t\t\tsubAgents.length,\n\t\t\tonCreateSubAgent,\n\t\t\ttoolActions?.length,\n\t\t\tvariablesData?.length,\n\t\t\tonSelectVariable,\n\t\t],\n\t);\n\tconst soleCategory =\n\t\tavailableCategories.length === 1 ? availableCategories[0].key : null;\n\tconst activeCategory = category ?? soleCategory;\n\n\t// Title shown above the current view. `onBack` is omitted at a root level (a\n\t// sole category has nowhere to go back to) — the header then shows as a plain\n\t// title. When `onBack` is set the header leads with a back arrow, followed by\n\t// the source icon in a source detail view: `[← back][icon][title]`.\n\tconst header: {\n\t\ttitle: string;\n\t\tonBack?: () => void;\n\t\ticon?: SourceIcon;\n\t} | null = !activeCategory\n\t\t? null\n\t\t: activeCategory === 'subagent'\n\t\t? // Always titled — with a back arrow only when the category chooser is above.\n\t\t {\n\t\t\t\ttitle: 'Sub-agent',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t }\n\t\t: activeCategory === 'variable'\n\t\t? {\n\t\t\t\ttitle: 'Variables',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t }\n\t\t: source\n\t\t? {\n\t\t\t\ttitle: toolSourceLabel(source),\n\t\t\t\t// Deep-linked to a source (opened from a right-panel source button):\n\t\t\t\t// that source IS the root, so no back arrow. In the @-drill flow\n\t\t\t\t// (no initialSource) back returns to the source chooser.\n\t\t\t\tonBack: initialSource ? undefined : () => setSource(null),\n\t\t\t\ticon: sourceIcon(source),\n\t\t }\n\t\t: // Always titled — matches the Sub-agent header; back arrow only when the\n\t\t // category chooser is above (i.e. not the sole category).\n\t\t {\n\t\t\t\ttitle: 'Tools',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t };\n\n\t// Build the navigable rows for the current view.\n\tconst rows: PickerRowModel[] = useMemo(() => {\n\t\tif (!activeCategory) {\n\t\t\treturn availableCategories.map((c) => ({\n\t\t\t\tid: c.key,\n\t\t\t\tlabel: c.label,\n\t\t\t\tdrillsIn: true,\n\t\t\t\tonActivate: () => {\n\t\t\t\t\tsetCategory(c.key);\n\t\t\t\t\tsetSource(null);\n\t\t\t\t},\n\t\t\t}));\n\t\t}\n\t\tif (activeCategory === 'subagent') {\n\t\t\tconst sq = search.trim().toLowerCase();\n\t\t\tconst leaves: PickerRowModel[] = subAgents\n\t\t\t\t.filter((item) => !sq || rowLabel(item).toLowerCase().includes(sq))\n\t\t\t\t.map((item) => ({\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tlabel: rowLabel(item),\n\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\tonActivate: () => onSelect(item),\n\t\t\t\t}));\n\t\t\t// \"Create new\" opens the drawer — first dismiss the menu (strips the \"@\").\n\t\t\tif (onCreateSubAgent) {\n\t\t\t\tleaves.push({\n\t\t\t\t\tid: CREATE_SUBAGENT_ID,\n\t\t\t\t\tlabel: 'Create new',\n\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\tisCreate: true,\n\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\tonCreateSubAgent();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn leaves;\n\t\t}\n\t\tif (activeCategory === 'variable') {\n\t\t\t// The embedded VariablePicker owns this view (its own search + rows) —\n\t\t\t// no navigable PickerRow list of our own.\n\t\t\treturn [];\n\t\t}\n\t\tif (!source) {\n\t\t\t// One drill-in row per source: the real sources (from instances) plus any\n\t\t\t// action source (Rest API / email / Slack) with no instances yet, so it\n\t\t\t// still appears for first-time creation. Deduped — no more twin rows.\n\t\t\tconst actionOnly = (toolActions ?? [])\n\t\t\t\t.map((a) => a.source)\n\t\t\t\t.filter((s) => !sources.includes(s));\n\t\t\t// Fixed source order (manifest → shopify → rest_api → email → slack →\n\t\t\t// rest) so adding/updating a tool never reshuffles the picker rows.\n\t\t\treturn [...sources, ...actionOnly]\n\t\t\t\t.sort((a, b) => toolSourceRank(a) - toolSourceRank(b))\n\t\t\t\t.map((s) => {\n\t\t\t\t\tconst action = actionBySource.get(s);\n\t\t\t\t\t// Not connected → a \"Connect\" row (no drill-in / no instances / no\n\t\t\t\t\t// \"Create new\"); clicking dismisses the picker and runs the connect\n\t\t\t\t\t// flow. Applies to config actions (their own `connected`) AND native\n\t\t\t\t\t// source groups (via `integrationStatus`), so both look consistent.\n\t\t\t\t\tconst connected =\n\t\t\t\t\t\taction !== undefined\n\t\t\t\t\t\t\t? action.connected\n\t\t\t\t\t\t\t: integrationStatus?.[s as ToolSource];\n\t\t\t\t\tif (connected === false) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: s,\n\t\t\t\t\t\t\tlabel: toolSourceLabel(s),\n\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\ticon: sourceIcon(s) ?? action?.icon,\n\t\t\t\t\t\t\trightLabel: 'Connect',\n\t\t\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\t\t\tif (action?.onConnect) {\n\t\t\t\t\t\t\t\t\taction.onConnect();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tonConnectIntegration?.(s as ToolSource);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\t// A source whose drill-in view would hold exactly one leaf is\n\t\t\t\t\t// pointless to drill into — surface that single leaf right here at\n\t\t\t\t\t// the source level (its function name + the same source icon).\n\t\t\t\t\t// Covers \"no Rest API added yet\" (→ open the create modal directly)\n\t\t\t\t\t// and a config tool's single capability (Slack → \"Send Slack\n\t\t\t\t\t// message\", email → \"Send Email\").\n\t\t\t\t\tconst instances = tools.filter(\n\t\t\t\t\t\t(t) => (metaOf(t)?.source ?? 'other') === s,\n\t\t\t\t\t);\n\t\t\t\t\tif (instances.length + (action ? 1 : 0) === 1) {\n\t\t\t\t\t\tconst icon = sourceIcon(s) ?? action?.icon;\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tid: s,\n\t\t\t\t\t\t\t\tlabel: action.label,\n\t\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\t\ticon,\n\t\t\t\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\t\t\t\taction.onSelect();\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst only = instances[0];\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: String(only.id),\n\t\t\t\t\t\t\tlabel: rowLabel(only),\n\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\ticon,\n\t\t\t\t\t\t\tauthRequired: metaOf(only)?.authRequired,\n\t\t\t\t\t\t\tonActivate: () => onSelect(only),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: s,\n\t\t\t\t\t\tlabel: toolSourceLabel(s),\n\t\t\t\t\t\tdrillsIn: true,\n\t\t\t\t\t\ticon: sourceIcon(s) ?? action?.icon,\n\t\t\t\t\t\tonActivate: () => setSource(s),\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t}\n\t\tconst q = search.trim().toLowerCase();\n\t\tconst leaves: PickerRowModel[] = tools\n\t\t\t.filter((t) => (metaOf(t)?.source ?? 'other') === source)\n\t\t\t.filter((t) => !q || rowLabel(t).toLowerCase().includes(q))\n\t\t\t.map((item) => ({\n\t\t\t\tid: String(item.id),\n\t\t\t\tlabel: rowLabel(item),\n\t\t\t\tdrillsIn: false,\n\t\t\t\tauthRequired: metaOf(item)?.authRequired,\n\t\t\t\tonActivate: () => onSelect(item),\n\t\t\t}));\n\t\t// A configurable source's add row, below any existing instances, opening\n\t\t// its drawer. Config tools (Slack / email) are a single fixed capability →\n\t\t// render like a normal tool leaf (label only, no icon — matching the tool\n\t\t// rows). Rest API instances are user-created, so its \"add\" stays a\n\t\t// brand-coloured create row.\n\t\tconst action = actionBySource.get(source);\n\t\tif (action) {\n\t\t\tconst isConfigTool = source in CONFIG_TOOL_SOURCES;\n\t\t\tleaves.push({\n\t\t\t\tid: `create:${source}`,\n\t\t\t\tlabel: action.label,\n\t\t\t\tdrillsIn: false,\n\t\t\t\tisCreate: !isConfigTool,\n\t\t\t\tonActivate: () => {\n\t\t\t\t\tonClose?.();\n\t\t\t\t\taction.onSelect();\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn leaves;\n\t}, [\n\t\tactiveCategory,\n\t\tavailableCategories,\n\t\tsource,\n\t\tsearch,\n\t\ttools,\n\t\tsubAgents,\n\t\tsources,\n\t\tonSelect,\n\t\tonClose,\n\t\tonCreateSubAgent,\n\t\ttoolActions,\n\t\tactionBySource,\n\t\tintegrationStatus,\n\t\tonConnectIntegration,\n\t]);\n\n\t// Reset highlight whenever the view (or filtered result set) changes.\n\tuseEffect(() => setActiveIndex(-1), [activeCategory, source, search, items]);\n\t// Clear the search box when leaving a source view.\n\tuseEffect(() => setSearch(''), [activeCategory, source]);\n\n\t// Keep the latest rows/index reachable from the stable key handler.\n\tconst navRef = useRef({ rows, activeIndex, header });\n\tnavRef.current = { rows, activeIndex, header };\n\n\tuseEffect(() => {\n\t\tif (!registerKeyHandler) {\n\t\t\treturn;\n\t\t}\n\t\tregisterKeyHandler((event) => {\n\t\t\tconst { rows: r, activeIndex: i, header: h } = navRef.current;\n\t\t\tswitch (event.key) {\n\t\t\t\tcase 'ArrowDown':\n\t\t\t\t\tif (r.length) setActiveIndex((p) => (p + 1) % r.length);\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowUp':\n\t\t\t\t\tif (r.length) setActiveIndex((p) => (p + r.length - 1) % r.length);\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowRight':\n\t\t\t\tcase 'Enter':\n\t\t\t\t\t// Nothing highlighted yet → treat the first row as the target so\n\t\t\t\t\t// the editor's @-flow (type, then Enter) still works.\n\t\t\t\t\t(r[i] ?? r[0])?.onActivate();\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowLeft':\n\t\t\t\t\tif (h?.onBack) {\n\t\t\t\t\t\th.onBack();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\treturn () => registerKeyHandler(null);\n\t}, [registerKeyHandler]);\n\n\tconst variant = !activeCategory\n\t\t? 'categories'\n\t\t: rows.length && !rows[0].drillsIn\n\t\t? 'items'\n\t\t: 'sources';\n\n\t// Search lives inside a source's tool list (Manifest / Shopify) and in the\n\t// sub-agent list.\n\tconst showSearch =\n\t\t(activeCategory === 'tool' && !!source) || activeCategory === 'subagent';\n\n\treturn (\n\t\t<PickerCard className={`bik-mention-picker bik-mention-picker--${variant}`}>\n\t\t\t{header && (\n\t\t\t\t<PickerBack\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={header.onBack}\n\t\t\t\t\tdisabled={!header.onBack}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: COLORS.surface.subdued,\n\t\t\t\t\t\tcursor: header.onBack ? 'pointer' : 'default',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{header.onBack && (\n\t\t\t\t\t\t<SvgArrowLeft\n\t\t\t\t\t\t\twidth={20}\n\t\t\t\t\t\t\theight={20}\n\t\t\t\t\t\t\tcolor={COLORS.content.primary}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t\t{header.icon && renderSourceIcon(header.icon)}\n\t\t\t\t\t<TitleSmall> {header.title}</TitleSmall>\n\t\t\t\t</PickerBack>\n\t\t\t)}\n\t\t\t{showSearch && (\n\t\t\t\t<PickerSearchRow>\n\t\t\t\t\t<SearchIcon\n\t\t\t\t\t\twidth={20}\n\t\t\t\t\t\theight={20}\n\t\t\t\t\t\tcolor={COLORS.content.placeholder}\n\t\t\t\t\t/>\n\t\t\t\t\t<PickerSearchInput\n\t\t\t\t\t\t// Focus on open so the user can type immediately (appears only\n\t\t\t\t\t\t// after they drill into a source).\n\t\t\t\t\t\t// eslint-disable-next-line jsx-a11y/no-autofocus\n\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tplaceholder=\"Search\"\n\t\t\t\t\t\tdata-test=\"mention-picker-search\"\n\t\t\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\t\t\tonKeyDown={(e) => {\n\t\t\t\t\t\t\tif (!rows.length) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (e.key === 'ArrowDown') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\tsetActiveIndex((p) => (p + 1) % rows.length);\n\t\t\t\t\t\t\t} else if (e.key === 'ArrowUp') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\tsetActiveIndex((p) => (p + rows.length - 1) % rows.length);\n\t\t\t\t\t\t\t} else if (e.key === 'Enter') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t(rows[activeIndex] ?? rows[0])?.onActivate();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</PickerSearchRow>\n\t\t\t)}\n\t\t\t{activeCategory === 'variable' ? (\n\t\t\t\t// Shared variable catalog, embedded as this drill-in view. Selecting a\n\t\t\t\t// variable dismisses the menu FIRST (strips the typed \"@\" + query),\n\t\t\t\t// then hands the variable up to insert its `{{token}}` at the cursor.\n\t\t\t\t<VariablePicker\n\t\t\t\t\ttype={PickerType.EMBEDDED}\n\t\t\t\t\tdata={variablesData ?? []}\n\t\t\t\t\tshowRecommendedBar={false}\n\t\t\t\t\tonSelect={(variable: VariableV3) => {\n\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\tonSelectVariable?.(variable);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : rows.length === 0 ? (\n\t\t\t\t<PickerEmpty>\n\t\t\t\t\t{activeCategory === 'subagent' ? null : 'No tools available'}\n\t\t\t\t</PickerEmpty>\n\t\t\t) : (\n\t\t\t\trows.map((row, i) => (\n\t\t\t\t\t<PickerRow\n\t\t\t\t\t\tkey={row.id}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t$active={i === activeIndex}\n\t\t\t\t\t\tonMouseEnter={() => setActiveIndex(i)}\n\t\t\t\t\t\tonMouseLeave={() => setActiveIndex(-1)}\n\t\t\t\t\t\tonClick={row.onActivate}\n\t\t\t\t\t>\n\t\t\t\t\t\t{row.isCreate ? (\n\t\t\t\t\t\t\t<CreateRowText>{row.label}</CreateRowText>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<PickerRowMain>\n\t\t\t\t\t\t\t\t\t{renderSourceIcon(row.icon)}\n\t\t\t\t\t\t\t\t\t<BodySecondary color={COLORS.content.primary}>\n\t\t\t\t\t\t\t\t\t\t{row.label}\n\t\t\t\t\t\t\t\t\t</BodySecondary>\n\t\t\t\t\t\t\t\t\t{row.authRequired && (\n\t\t\t\t\t\t\t\t\t\t<Shield\n\t\t\t\t\t\t\t\t\t\t\tsize={14}\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Requires customer verification\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</PickerRowMain>\n\t\t\t\t\t\t\t\t{row.rightLabel ? (\n\t\t\t\t\t\t\t\t\t<RightActionText>{row.rightLabel}</RightActionText>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\trow.drillsIn && <ChevronRight width={12} height={12} />\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</PickerRow>\n\t\t\t\t))\n\t\t\t)}\n\t\t\t{rows.some((row) => row.authRequired) && (\n\t\t\t\t<PickerLegend>\n\t\t\t\t\t<Shield size={14} />\n\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\tRequires customer verification\n\t\t\t\t\t</BodyCaption>\n\t\t\t\t</PickerLegend>\n\t\t\t)}\n\t\t</PickerCard>\n\t);\n};\n\nconst PickerCard = styled.div`\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 240px;\n\tmax-height: 301px;\n\toverflow-y: auto;\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`;\n\n// Search box at the top of a source's tool list (Manifest / Shopify).\nconst PickerSearchRow = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 10px 12px;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n`;\n\nconst PickerSearchInput = styled.input`\n\tflex: 1;\n\tmin-width: 0;\n\tborder: none;\n\toutline: none;\n\tbackground: transparent;\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tcolor: ${COLORS.content.primary};\n\n\t&::placeholder {\n\t\tcolor: ${COLORS.content.placeholder};\n\t}\n`;\n\nconst PickerRow = styled.button<{ $active?: boolean }>`\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n\tgap: 2px;\n\twidth: 100%;\n\tpadding: 8px 10px;\n\theight: 48px;\n\tborder: none;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n\tbackground: ${(p) => (p.$active ? COLORS.surface.subdued : 'transparent')};\n\tcursor: pointer;\n\ttext-align: left;\n\n\t&:hover {\n\t\tbackground: ${COLORS.surface.hovered};\n\t}\n`;\n\n// Left cluster: leading source glyph + label (chevron stays right via PickerRow).\nconst PickerRowMain = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tmin-width: 0;\n`;\n\n// Footer legend, shown when the list holds any auth-required tool.\nconst PickerLegend = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 8px 12px;\n\tborder-top: 1px solid ${COLORS.stroke.primary};\n`;\n\n// Right-aligned brand action text (e.g. \"Connect\") in place of the chevron.\nconst RightActionText = styled.span`\n\tflex-shrink: 0;\n\tcolor: ${COLORS.content.brand};\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n`;\n\n// \"Create new\" — centered brand text, no icon (matches the side-panel dropdown).\nconst CreateRowText = styled.div`\n\twidth: 100%;\n\ttext-align: center;\n\tcolor: ${COLORS.content.brand};\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tfont-weight: 600;\n`;\n\nconst PickerBack = styled.button`\n\tdisplay: flex;\n\talign-items: center;\n\tpadding: 12px 16px;\n\tborder: none;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n\tbackground: transparent;\n\tcursor: pointer;\n\tfont-family: Inter;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\theight: 48px;\n\tgap: 4px;\n`;\n\nconst PickerEmpty = styled.div`\n\tpadding: 12px 10px;\n\tfont-family: Inter;\n\tfont-size: 13px;\n\tcolor: ${COLORS.content.secondary};\n`;\n"],"names":["metaOf","item","rowLabel","CREATE_SUBAGENT_ID","MentionPicker","items","onSelect","registerKeyHandler","onClose","onCreateSubAgent","initialSource","toolActions","integrationStatus","onConnectIntegration","variablesData","onSelectVariable","category","setCategory","useState","source","setSource","activeIndex","setActiveIndex","search","setSearch","tools","useMemo","i","subAgents","sources","acc","t","s","actionBySource","a","availableCategories","CATEGORIES","c","soleCategory","activeCategory","header","toolSourceLabel","sourceIcon","rows","sq","leaves","actionOnly","b","toolSourceRank","action","instances","icon","only","q","isConfigTool","CONFIG_TOOL_SOURCES","useEffect","navRef","useRef","event","r","h","p","variant","showSearch","jsxs","PickerCard","PickerBack","COLORS","jsx","SvgArrowLeft","renderSourceIcon","TitleSmall","PickerSearchRow","SearchIcon","PickerSearchInput","VariablePicker","PickerType","variable","PickerEmpty","row","PickerRow","CreateRowText","Fragment","PickerRowMain","BodySecondary","Shield","RightActionText","ChevronRight","PickerLegend","BodyCaption","styled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MentionPicker.js","sources":["../../../../../src/components/agent-builder/components/MentionPicker.tsx"],"sourcesContent":["// import ChevronLeft from '@src/assets/icons/chevronLeft.svg';\nimport ChevronRight from '@src/assets/icons/chevronRight.svg';\nimport SearchIcon from '@src/assets/icons/searchIcon.svg';\nimport type { MentionDropdownRenderProps, MentionItem } from '@src/editor';\nimport { Shield } from '@src/icons';\nimport SvgArrowLeft from '@src/icons/Arrows/Normal arrows/ArrowLeft';\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport {\n\tBodyCaption,\n\tBodySecondary,\n\tTitleSmall,\n} from '@src/components/TypographyStyle';\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 {\n\tCATEGORIES,\n\tCONFIG_TOOL_SOURCES,\n\ttoolSourceRank,\n} from '../constants/tools';\nimport type { Category, ToolAction, ToolSource } from '../constants/tools';\nimport {\n\tMentionKindMeta,\n\ttoolSourceLabel,\n} from '../utils/mentionSerialization';\nimport { renderSourceIcon, sourceIcon, SourceIcon } from '../utils/sourceIcons';\n\nconst metaOf = (item: MentionItem): MentionKindMeta | undefined =>\n\titem.meta as unknown as MentionKindMeta | undefined;\n\nconst rowLabel = (item: MentionItem): string =>\n\tmetaOf(item)?.plainLabel ?? item.label;\n\n/** One navigable row in the picker's current view. */\ntype PickerRowModel = {\n\tid: string;\n\tlabel: string;\n\t/** Drill-in rows show a chevron; leaf (selectable) rows do not. */\n\tdrillsIn: boolean;\n\t/** Leading brand glyph (source rows only). */\n\ticon?: SourceIcon;\n\t/** Brand-coloured \"Create new\" action row (not a selectable item). */\n\tisCreate?: boolean;\n\t/**\n\t * Brand-coloured right-aligned action text (e.g. \"Connect\") shown in place of\n\t * the drill-in chevron — used for an unconnected integration source.\n\t */\n\trightLabel?: string;\n\t/** Tool leaf touches customer-private data → shows a shield glyph. */\n\tauthRequired?: boolean;\n\tonActivate: () => void;\n};\n\n/** Sentinel id for the sub-agent \"Create new\" row. */\nconst CREATE_SUBAGENT_ID = '__bik_create_subagent__';\n\ntype MentionPickerProps = MentionDropdownRenderProps & {\n\t/** Opens the create-sub-agent drawer (shows a \"Create new\" row in the Sub-agent view). */\n\tonCreateSubAgent?: () => void;\n\t/**\n\t * Open already drilled into this tool source (e.g. `'shopify'`) — its tool list\n\t * is shown immediately. */\n\tinitialSource?: string;\n\t/**\n\t * Built-in tool actions (Rest API / Handover to email) shown in the Tools view.\n\t * Picking one opens its config drawer (via `onSelect`) and dismisses the picker\n\t * — it never inserts a mention into the text.\n\t */\n\ttoolActions?: ToolAction[];\n\t/**\n\t * Per-source integration connection state. A source whose value is `false`\n\t * renders a \"Connect\" row (like an unconnected action) instead of drilling in —\n\t * so native source groups (Shopify / BigCommerce / ShipStation) show \"Connect\"\n\t * consistently with the config actions (Slack).\n\t */\n\tintegrationStatus?: Partial<Record<ToolSource, boolean>>;\n\t/** Open the connect flow for an unconnected source (used by the Connect row). */\n\tonConnectIntegration?: (source: ToolSource) => void;\n\t/**\n\t * Variable catalog for the \"Variables\" view (the shared VariablePicker's\n\t * `data`). Omitted / empty → the Variables category is not offered.\n\t */\n\tvariablesData?: VariableListInterfaceV3[];\n\t/**\n\t * A variable was picked from the Variables view. The picker dismisses itself\n\t * (stripping the typed `@`) BEFORE calling this — the handler inserts a\n\t * `Variable: <name>` mention chip (see MentionEditor).\n\t */\n\tonSelectVariable?: (variable: VariableV3) => void;\n};\n\n/**\n * `@` reference picker. Drills:\n * root → Tools → <source> (Manifest / Shopify / …) → tool\n * → Sub-agent → sub-agent\n * → Variables → shared VariablePicker (embedded)\n * (\"Add condition\" is intentionally not shown yet.)\n *\n * Owns keyboard navigation (↑/↓ move, →/Enter activate, ← go back) over the\n * CURRENTLY visible rows — registered with the editor via `registerKeyHandler`.\n */\nexport const MentionPicker: React.FC<MentionPickerProps> = ({\n\titems,\n\tonSelect,\n\tregisterKeyHandler,\n\tonClose,\n\tonCreateSubAgent,\n\tinitialSource,\n\ttoolActions,\n\tintegrationStatus,\n\tonConnectIntegration,\n\tvariablesData,\n\tonSelectVariable,\n}) => {\n\tconst [category, setCategory] = useState<Category | null>(null);\n\tconst [source, setSource] = useState<string | null>(initialSource ?? null);\n\t// -1 = nothing highlighted; rows highlight only on hover / arrow keys\n\t// (a default highlight reads as a stuck hover state).\n\tconst [activeIndex, setActiveIndex] = useState(-1);\n\t// Search box shown inside a source's tool list (Manifest / Shopify); filters\n\t// that source's tools by name.\n\tconst [search, setSearch] = useState('');\n\n\tconst tools = useMemo(\n\t\t() => items.filter((i) => metaOf(i)?.kind === 'tool'),\n\t\t[items],\n\t);\n\tconst subAgents = useMemo(\n\t\t() => items.filter((i) => metaOf(i)?.kind === 'subagent'),\n\t\t[items],\n\t);\n\tconst sources = useMemo(() => {\n\t\tconst acc: string[] = [];\n\t\ttools.forEach((t) => {\n\t\t\tconst s = metaOf(t)?.source ?? 'other';\n\t\t\tif (!acc.includes(s)) {\n\t\t\t\tacc.push(s);\n\t\t\t}\n\t\t});\n\t\treturn acc;\n\t}, [tools]);\n\t// The built-in \"create\" action per source (Rest API / email / Slack). A source\n\t// with an action drills into [its instances + a \"Create new\" row] — like the\n\t// Sub-agent category — instead of showing a duplicate flat \"create\" row.\n\tconst actionBySource = useMemo(\n\t\t() =>\n\t\t\tnew Map<string, ToolAction>(\n\t\t\t\t(toolActions ?? []).map((a) => [a.source, a]),\n\t\t\t),\n\t\t[toolActions],\n\t);\n\n\t// Show a category when it has items — and ALWAYS show Sub-agent when creating\n\t// one is possible, so an agent with no sub-agents yet still gets the root\n\t// \"Tools / Sub-agent\" choice (with a \"Create new\" inside). When only one is\n\t// available (e.g. the sub-agent drawer offers tools only) the selector is skipped.\n\tconst availableCategories = useMemo(\n\t\t() =>\n\t\t\tCATEGORIES.filter((c) => {\n\t\t\t\tif (c.key === 'tool') {\n\t\t\t\t\treturn tools.length > 0 || (toolActions?.length ?? 0) > 0;\n\t\t\t\t}\n\t\t\t\tif (c.key === 'variable') {\n\t\t\t\t\treturn (variablesData?.length ?? 0) > 0 && !!onSelectVariable;\n\t\t\t\t}\n\t\t\t\treturn subAgents.length > 0 || !!onCreateSubAgent;\n\t\t\t}),\n\t\t[\n\t\t\ttools.length,\n\t\t\tsubAgents.length,\n\t\t\tonCreateSubAgent,\n\t\t\ttoolActions?.length,\n\t\t\tvariablesData?.length,\n\t\t\tonSelectVariable,\n\t\t],\n\t);\n\tconst soleCategory =\n\t\tavailableCategories.length === 1 ? availableCategories[0].key : null;\n\tconst activeCategory = category ?? soleCategory;\n\n\t// Title shown above the current view. `onBack` is omitted at a root level (a\n\t// sole category has nowhere to go back to) — the header then shows as a plain\n\t// title. When `onBack` is set the header leads with a back arrow, followed by\n\t// the source icon in a source detail view: `[← back][icon][title]`.\n\tconst header: {\n\t\ttitle: string;\n\t\tonBack?: () => void;\n\t\ticon?: SourceIcon;\n\t} | null = !activeCategory\n\t\t? null\n\t\t: activeCategory === 'subagent'\n\t\t? // Always titled — with a back arrow only when the category chooser is above.\n\t\t {\n\t\t\t\ttitle: 'Sub-agent',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t }\n\t\t: activeCategory === 'variable'\n\t\t? {\n\t\t\t\ttitle: 'Variables',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t }\n\t\t: source\n\t\t? {\n\t\t\t\ttitle: toolSourceLabel(source),\n\t\t\t\t// Deep-linked to a source (opened from a right-panel source button):\n\t\t\t\t// that source IS the root, so no back arrow. In the @-drill flow\n\t\t\t\t// (no initialSource) back returns to the source chooser.\n\t\t\t\tonBack: initialSource ? undefined : () => setSource(null),\n\t\t\t\ticon: sourceIcon(source),\n\t\t }\n\t\t: // Always titled — matches the Sub-agent header; back arrow only when the\n\t\t // category chooser is above (i.e. not the sole category).\n\t\t {\n\t\t\t\ttitle: 'Tools',\n\t\t\t\tonBack: soleCategory ? undefined : () => setCategory(null),\n\t\t };\n\n\t// Build the navigable rows for the current view.\n\tconst rows: PickerRowModel[] = useMemo(() => {\n\t\tif (!activeCategory) {\n\t\t\treturn availableCategories.map((c) => ({\n\t\t\t\tid: c.key,\n\t\t\t\tlabel: c.label,\n\t\t\t\tdrillsIn: true,\n\t\t\t\tonActivate: () => {\n\t\t\t\t\tsetCategory(c.key);\n\t\t\t\t\tsetSource(null);\n\t\t\t\t},\n\t\t\t}));\n\t\t}\n\t\tif (activeCategory === 'subagent') {\n\t\t\tconst sq = search.trim().toLowerCase();\n\t\t\tconst leaves: PickerRowModel[] = subAgents\n\t\t\t\t.filter((item) => !sq || rowLabel(item).toLowerCase().includes(sq))\n\t\t\t\t.map((item) => ({\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tlabel: rowLabel(item),\n\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\tonActivate: () => onSelect(item),\n\t\t\t\t}));\n\t\t\t// \"Create new\" opens the drawer — first dismiss the menu (strips the \"@\").\n\t\t\tif (onCreateSubAgent) {\n\t\t\t\tleaves.push({\n\t\t\t\t\tid: CREATE_SUBAGENT_ID,\n\t\t\t\t\tlabel: 'Create new',\n\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\tisCreate: true,\n\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\tonCreateSubAgent();\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn leaves;\n\t\t}\n\t\tif (activeCategory === 'variable') {\n\t\t\t// The embedded VariablePicker owns this view (its own search + rows) —\n\t\t\t// no navigable PickerRow list of our own.\n\t\t\treturn [];\n\t\t}\n\t\tif (!source) {\n\t\t\t// One drill-in row per source: the real sources (from instances) plus any\n\t\t\t// action source (Rest API / email / Slack) with no instances yet, so it\n\t\t\t// still appears for first-time creation. Deduped — no more twin rows.\n\t\t\tconst actionOnly = (toolActions ?? [])\n\t\t\t\t.map((a) => a.source)\n\t\t\t\t.filter((s) => !sources.includes(s));\n\t\t\t// Fixed source order (manifest → shopify → rest_api → email → slack →\n\t\t\t// rest) so adding/updating a tool never reshuffles the picker rows.\n\t\t\treturn [...sources, ...actionOnly]\n\t\t\t\t.sort((a, b) => toolSourceRank(a) - toolSourceRank(b))\n\t\t\t\t.map((s) => {\n\t\t\t\t\tconst action = actionBySource.get(s);\n\t\t\t\t\t// Not connected → a \"Connect\" row (no drill-in / no instances / no\n\t\t\t\t\t// \"Create new\"); clicking dismisses the picker and runs the connect\n\t\t\t\t\t// flow. Applies to config actions (their own `connected`) AND native\n\t\t\t\t\t// source groups (via `integrationStatus`), so both look consistent.\n\t\t\t\t\tconst connected =\n\t\t\t\t\t\taction !== undefined\n\t\t\t\t\t\t\t? action.connected\n\t\t\t\t\t\t\t: integrationStatus?.[s as ToolSource];\n\t\t\t\t\tif (connected === false) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: s,\n\t\t\t\t\t\t\tlabel: toolSourceLabel(s),\n\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\ticon: sourceIcon(s) ?? action?.icon,\n\t\t\t\t\t\t\trightLabel: 'Connect',\n\t\t\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\t\t\tif (action?.onConnect) {\n\t\t\t\t\t\t\t\t\taction.onConnect();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tonConnectIntegration?.(s as ToolSource);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\t// A source whose drill-in view would hold exactly one leaf is\n\t\t\t\t\t// pointless to drill into — surface that single leaf right here at\n\t\t\t\t\t// the source level (its function name + the same source icon).\n\t\t\t\t\t// Covers \"no Rest API added yet\" (→ open the create modal directly)\n\t\t\t\t\t// and a config tool's single capability (Slack → \"Send Slack\n\t\t\t\t\t// message\", email → \"Send Email\").\n\t\t\t\t\tconst instances = tools.filter(\n\t\t\t\t\t\t(t) => (metaOf(t)?.source ?? 'other') === s,\n\t\t\t\t\t);\n\t\t\t\t\tif (instances.length + (action ? 1 : 0) === 1) {\n\t\t\t\t\t\tconst icon = sourceIcon(s) ?? action?.icon;\n\t\t\t\t\t\tif (action) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tid: s,\n\t\t\t\t\t\t\t\tlabel: action.label,\n\t\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\t\ticon,\n\t\t\t\t\t\t\t\tonActivate: () => {\n\t\t\t\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\t\t\t\taction.onSelect();\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst only = instances[0];\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: String(only.id),\n\t\t\t\t\t\t\tlabel: rowLabel(only),\n\t\t\t\t\t\t\tdrillsIn: false,\n\t\t\t\t\t\t\ticon,\n\t\t\t\t\t\t\tauthRequired: metaOf(only)?.authRequired,\n\t\t\t\t\t\t\tonActivate: () => onSelect(only),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: s,\n\t\t\t\t\t\tlabel: toolSourceLabel(s),\n\t\t\t\t\t\tdrillsIn: true,\n\t\t\t\t\t\ticon: sourceIcon(s) ?? action?.icon,\n\t\t\t\t\t\tonActivate: () => setSource(s),\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t}\n\t\tconst q = search.trim().toLowerCase();\n\t\tconst leaves: PickerRowModel[] = tools\n\t\t\t.filter((t) => (metaOf(t)?.source ?? 'other') === source)\n\t\t\t.filter((t) => !q || rowLabel(t).toLowerCase().includes(q))\n\t\t\t.map((item) => ({\n\t\t\t\tid: String(item.id),\n\t\t\t\tlabel: rowLabel(item),\n\t\t\t\tdrillsIn: false,\n\t\t\t\tauthRequired: metaOf(item)?.authRequired,\n\t\t\t\tonActivate: () => onSelect(item),\n\t\t\t}));\n\t\t// A configurable source's add row, below any existing instances, opening\n\t\t// its drawer. Config tools (Slack / email) are a single fixed capability →\n\t\t// render like a normal tool leaf (label only, no icon — matching the tool\n\t\t// rows). Rest API instances are user-created, so its \"add\" stays a\n\t\t// brand-coloured create row.\n\t\tconst action = actionBySource.get(source);\n\t\tif (action) {\n\t\t\tconst isConfigTool = source in CONFIG_TOOL_SOURCES;\n\t\t\tleaves.push({\n\t\t\t\tid: `create:${source}`,\n\t\t\t\tlabel: action.label,\n\t\t\t\tdrillsIn: false,\n\t\t\t\tisCreate: !isConfigTool,\n\t\t\t\tonActivate: () => {\n\t\t\t\t\tonClose?.();\n\t\t\t\t\taction.onSelect();\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn leaves;\n\t}, [\n\t\tactiveCategory,\n\t\tavailableCategories,\n\t\tsource,\n\t\tsearch,\n\t\ttools,\n\t\tsubAgents,\n\t\tsources,\n\t\tonSelect,\n\t\tonClose,\n\t\tonCreateSubAgent,\n\t\ttoolActions,\n\t\tactionBySource,\n\t\tintegrationStatus,\n\t\tonConnectIntegration,\n\t]);\n\n\t// Reset highlight whenever the view (or filtered result set) changes.\n\tuseEffect(() => setActiveIndex(-1), [activeCategory, source, search, items]);\n\t// Clear the search box when leaving a source view.\n\tuseEffect(() => setSearch(''), [activeCategory, source]);\n\n\t// Keep the latest rows/index reachable from the stable key handler.\n\tconst navRef = useRef({ rows, activeIndex, header });\n\tnavRef.current = { rows, activeIndex, header };\n\n\tuseEffect(() => {\n\t\tif (!registerKeyHandler) {\n\t\t\treturn;\n\t\t}\n\t\tregisterKeyHandler((event) => {\n\t\t\tconst { rows: r, activeIndex: i, header: h } = navRef.current;\n\t\t\tswitch (event.key) {\n\t\t\t\tcase 'ArrowDown':\n\t\t\t\t\tif (r.length) setActiveIndex((p) => (p + 1) % r.length);\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowUp':\n\t\t\t\t\tif (r.length) setActiveIndex((p) => (p + r.length - 1) % r.length);\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowRight':\n\t\t\t\tcase 'Enter':\n\t\t\t\t\t// Nothing highlighted yet → treat the first row as the target so\n\t\t\t\t\t// the editor's @-flow (type, then Enter) still works.\n\t\t\t\t\t(r[i] ?? r[0])?.onActivate();\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'ArrowLeft':\n\t\t\t\t\tif (h?.onBack) {\n\t\t\t\t\t\th.onBack();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\treturn () => registerKeyHandler(null);\n\t}, [registerKeyHandler]);\n\n\tconst variant = !activeCategory\n\t\t? 'categories'\n\t\t: rows.length && !rows[0].drillsIn\n\t\t? 'items'\n\t\t: 'sources';\n\n\t// Search lives inside a source's tool list (Manifest / Shopify) and in the\n\t// sub-agent list.\n\tconst showSearch =\n\t\t(activeCategory === 'tool' && !!source) || activeCategory === 'subagent';\n\n\treturn (\n\t\t<PickerCard\n\t\t\tclassName={`bik-mention-picker bik-mention-picker--${variant}`}\n\t\t\t// Variables view: a definite height so the embedded VariablePicker's\n\t\t\t// internal list scroll (below its fixed search bar) can resolve.\n\t\t\t$fixedHeight={activeCategory === 'variable'}\n\t\t>\n\t\t\t{header && (\n\t\t\t\t<PickerBack\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={header.onBack}\n\t\t\t\t\tdisabled={!header.onBack}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: COLORS.surface.subdued,\n\t\t\t\t\t\tcursor: header.onBack ? 'pointer' : 'default',\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{header.onBack && (\n\t\t\t\t\t\t<SvgArrowLeft\n\t\t\t\t\t\t\twidth={20}\n\t\t\t\t\t\t\theight={20}\n\t\t\t\t\t\t\tcolor={COLORS.content.primary}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t\t{header.icon && renderSourceIcon(header.icon)}\n\t\t\t\t\t<TitleSmall> {header.title}</TitleSmall>\n\t\t\t\t</PickerBack>\n\t\t\t)}\n\t\t\t{showSearch && (\n\t\t\t\t<PickerSearchRow>\n\t\t\t\t\t<SearchIcon\n\t\t\t\t\t\twidth={20}\n\t\t\t\t\t\theight={20}\n\t\t\t\t\t\tcolor={COLORS.content.placeholder}\n\t\t\t\t\t/>\n\t\t\t\t\t<PickerSearchInput\n\t\t\t\t\t\t// Focus on open so the user can type immediately (appears only\n\t\t\t\t\t\t// after they drill into a source).\n\t\t\t\t\t\t// eslint-disable-next-line jsx-a11y/no-autofocus\n\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tplaceholder=\"Search\"\n\t\t\t\t\t\tdata-test=\"mention-picker-search\"\n\t\t\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\t\t\tonKeyDown={(e) => {\n\t\t\t\t\t\t\tif (!rows.length) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (e.key === 'ArrowDown') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\tsetActiveIndex((p) => (p + 1) % rows.length);\n\t\t\t\t\t\t\t} else if (e.key === 'ArrowUp') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\tsetActiveIndex((p) => (p + rows.length - 1) % rows.length);\n\t\t\t\t\t\t\t} else if (e.key === 'Enter') {\n\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t(rows[activeIndex] ?? rows[0])?.onActivate();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</PickerSearchRow>\n\t\t\t)}\n\t\t\t{activeCategory === 'variable' ? (\n\t\t\t\t// Shared variable catalog, embedded as this drill-in view. The fill\n\t\t\t\t// wrapper hands it the remaining card height, so ITS search bar stays\n\t\t\t\t// fixed and only the variable list below scrolls. Selecting a\n\t\t\t\t// variable dismisses the menu FIRST (strips the typed \"@\" + query),\n\t\t\t\t// then hands the variable up to insert its `{{token}}` at the cursor.\n\t\t\t\t<VariableFill>\n\t\t\t\t\t<VariablePicker\n\t\t\t\t\t\ttype={PickerType.EMBEDDED}\n\t\t\t\t\t\tdata={variablesData ?? []}\n\t\t\t\t\t\tshowRecommendedBar={false}\n\t\t\t\t\t\t// Insert the reference token directly — the \"set up a default\n\t\t\t\t\t\t// value\" flow doesn't apply in the agent builder.\n\t\t\t\t\t\tskipDefaultVariableContent\n\t\t\t\t\t\tonSelect={(variable: VariableV3) => {\n\t\t\t\t\t\t\tonClose?.();\n\t\t\t\t\t\t\tonSelectVariable?.(variable);\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</VariableFill>\n\t\t\t) : rows.length === 0 ? (\n\t\t\t\t<PickerEmpty>\n\t\t\t\t\t{activeCategory === 'subagent' ? null : 'No tools available'}\n\t\t\t\t</PickerEmpty>\n\t\t\t) : (\n\t\t\t\t<PickerScroll>\n\t\t\t\t\t{rows.map((row, i) => (\n\t\t\t\t\t\t<PickerRow\n\t\t\t\t\t\t\tkey={row.id}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t$active={i === activeIndex}\n\t\t\t\t\t\t\tonMouseEnter={() => setActiveIndex(i)}\n\t\t\t\t\t\t\tonMouseLeave={() => setActiveIndex(-1)}\n\t\t\t\t\t\t\tonClick={row.onActivate}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{row.isCreate ? (\n\t\t\t\t\t\t\t\t<CreateRowText>{row.label}</CreateRowText>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<PickerRowMain>\n\t\t\t\t\t\t\t\t\t\t{renderSourceIcon(row.icon)}\n\t\t\t\t\t\t\t\t\t\t<BodySecondary color={COLORS.content.primary}>\n\t\t\t\t\t\t\t\t\t\t\t{row.label}\n\t\t\t\t\t\t\t\t\t\t</BodySecondary>\n\t\t\t\t\t\t\t\t\t\t{row.authRequired && (\n\t\t\t\t\t\t\t\t\t\t\t<Shield\n\t\t\t\t\t\t\t\t\t\t\t\tsize={14}\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Requires customer verification\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</PickerRowMain>\n\t\t\t\t\t\t\t\t\t{row.rightLabel ? (\n\t\t\t\t\t\t\t\t\t\t<RightActionText>{row.rightLabel}</RightActionText>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\trow.drillsIn && <ChevronRight width={12} height={12} />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</PickerRow>\n\t\t\t\t\t))}\n\t\t\t\t</PickerScroll>\n\t\t\t)}\n\t\t\t{rows.some((row) => row.authRequired) && (\n\t\t\t\t<PickerLegend>\n\t\t\t\t\t<Shield size={14} />\n\t\t\t\t\t<BodyCaption color={COLORS.content.secondary}>\n\t\t\t\t\t\tRequires customer verification\n\t\t\t\t\t</BodyCaption>\n\t\t\t\t</PickerLegend>\n\t\t\t)}\n\t\t</PickerCard>\n\t);\n};\n\n// The card itself never scrolls — the header / search stay fixed and only the\n// list area (PickerScroll, or the embedded VariablePicker's own list) does.\nconst PickerCard = styled.div<{ $fixedHeight?: boolean }>`\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 240px;\n\t${(p) => (p.$fixedHeight ? 'height: 301px;' : 'max-height: 301px;')}\n\toverflow: hidden;\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`;\n\n// Scrollable row list below the fixed header / search (scrollbar hidden —\n// the fixed header already signals there's more below).\nconst PickerScroll = styled.div`\n\tflex: 1 1 auto;\n\tmin-height: 0;\n\toverflow-y: auto;\n\tscrollbar-width: none;\n\n\t&::-webkit-scrollbar {\n\t\tdisplay: none;\n\t}\n`;\n\n// Hands the embedded VariablePicker the card's remaining height (its Content\n// sizes itself with percentages, so the chain must be definite). Its internal\n// list scrollbar is hidden to match PickerScroll.\nconst VariableFill = styled.div`\n\tflex: 1 1 auto;\n\tmin-height: 0;\n\n\t> div {\n\t\theight: 100%;\n\t}\n\n\t* {\n\t\tscrollbar-width: none;\n\t}\n\n\t*::-webkit-scrollbar {\n\t\tdisplay: none;\n\t}\n`;\n\n// Search box at the top of a source's tool list (Manifest / Shopify).\nconst PickerSearchRow = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 10px 12px;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n`;\n\nconst PickerSearchInput = styled.input`\n\tflex: 1;\n\tmin-width: 0;\n\tborder: none;\n\toutline: none;\n\tbackground: transparent;\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tcolor: ${COLORS.content.primary};\n\n\t&::placeholder {\n\t\tcolor: ${COLORS.content.placeholder};\n\t}\n`;\n\nconst PickerRow = styled.button<{ $active?: boolean }>`\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n\tgap: 2px;\n\twidth: 100%;\n\tpadding: 8px 10px;\n\theight: 48px;\n\tborder: none;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n\tbackground: ${(p) => (p.$active ? COLORS.surface.subdued : 'transparent')};\n\tcursor: pointer;\n\ttext-align: left;\n\n\t&:hover {\n\t\tbackground: ${COLORS.surface.hovered};\n\t}\n`;\n\n// Left cluster: leading source glyph + label (chevron stays right via PickerRow).\nconst PickerRowMain = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tmin-width: 0;\n`;\n\n// Footer legend, shown when the list holds any auth-required tool.\nconst PickerLegend = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 8px 12px;\n\tborder-top: 1px solid ${COLORS.stroke.primary};\n`;\n\n// Right-aligned brand action text (e.g. \"Connect\") in place of the chevron.\nconst RightActionText = styled.span`\n\tflex-shrink: 0;\n\tcolor: ${COLORS.content.brand};\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n`;\n\n// \"Create new\" — centered brand text, no icon (matches the side-panel dropdown).\nconst CreateRowText = styled.div`\n\twidth: 100%;\n\ttext-align: center;\n\tcolor: ${COLORS.content.brand};\n\tfont-family: Inter;\n\tfont-size: 14px;\n\tfont-weight: 600;\n`;\n\nconst PickerBack = styled.button`\n\tdisplay: flex;\n\talign-items: center;\n\tpadding: 12px 16px;\n\tborder: none;\n\tborder-bottom: 1px solid ${COLORS.stroke.primary};\n\tbackground: transparent;\n\tcursor: pointer;\n\tfont-family: Inter;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\theight: 48px;\n\tgap: 4px;\n`;\n\nconst PickerEmpty = styled.div`\n\tpadding: 12px 10px;\n\tfont-family: Inter;\n\tfont-size: 13px;\n\tcolor: ${COLORS.content.secondary};\n`;\n"],"names":["metaOf","item","rowLabel","CREATE_SUBAGENT_ID","MentionPicker","items","onSelect","registerKeyHandler","onClose","onCreateSubAgent","initialSource","toolActions","integrationStatus","onConnectIntegration","variablesData","onSelectVariable","category","setCategory","useState","source","setSource","activeIndex","setActiveIndex","search","setSearch","tools","useMemo","i","subAgents","sources","acc","t","s","actionBySource","a","availableCategories","CATEGORIES","c","soleCategory","activeCategory","header","toolSourceLabel","sourceIcon","rows","sq","leaves","actionOnly","b","toolSourceRank","action","instances","icon","only","q","isConfigTool","CONFIG_TOOL_SOURCES","useEffect","navRef","useRef","event","r","h","p","variant","showSearch","jsxs","PickerCard","PickerBack","COLORS","jsx","SvgArrowLeft","renderSourceIcon","TitleSmall","PickerSearchRow","SearchIcon","PickerSearchInput","VariableFill","VariablePicker","PickerType","variable","PickerEmpty","PickerScroll","row","PickerRow","CreateRowText","Fragment","PickerRowMain","BodySecondary","Shield","RightActionText","ChevronRight","PickerLegend","BodyCaption","styled"],"mappings":"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"}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),
|
|
2
|
-
display:
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),U=require("../../../assets/icons/delete.svg.js"),W=require("../../../assets/icons/plus.svg.js"),o=require("react"),Y=require("styled-components"),c=require("../../TypographyStyle.js"),l=require("../../../constants/Theme.js"),i=require("../AgentBuilder.styled.js"),K=require("../constants/charLimits.js"),g=require("./DrawerHeaderText.js"),Q=require("./ToolInstructionsField.js"),k=require("./VariableInlineField.js"),X=require("../../side-modal/SideModal.js"),w=require("../../input/Input.js"),Z=require("../../dropdown/Dropdown.js"),ee=require("../../dropdown/utils.js"),te=require("../../button/Button.js"),E=require("../../../editor/BikEditor.js"),se=r=>r&&typeof r=="object"&&"default"in r?r:{default:r},v=se(Y),re=["GET","POST","PUT","PATCH","DELETE"],ne=re.map(r=>({label:r,value:r})),ae=({onClose:r,onSave:H,initial:a,onImprove:I,charLimits:R=K.AGENT_CHAR_LIMITS,zIndex:A,existingNames:D=[],variablesData:u})=>{const[p,P]=o.useState(a?.name??""),[j,_]=o.useState(a?.instructions??""),[h,F]=o.useState(a?.url??"https://"),[m,M]=o.useState(a?.method??"POST"),[x,y]=o.useState(a?.headers??[{key:"",value:""}]),[C,N]=o.useState(a?.requestBody??""),[b,$]=o.useState(a?.responseSample??""),[O,q]=o.useState(!1),L=(t,s)=>y(n=>n.map((d,G)=>G===t?{...d,...s}:d)),z=()=>y(t=>[...t,{key:"",value:""}]),J=t=>y(s=>s.filter((n,d)=>d!==t)),S=p.trim(),T=S.length>0&&D.some(t=>t.trim().toLowerCase()===S.toLowerCase()),B=S.length>0&&h.trim().length>0&&!T,V=async()=>{if(!B||O)return;const t={id:a?.id??`restapi_${Date.now().toString(36)}`,name:p.trim(),instructions:j,url:h.trim(),method:m,headers:x.filter(n=>n.key.trim()||n.value.trim()),requestBody:C,responseSample:b},s=H(t);if(s&&typeof s.then=="function"){q(!0);try{await s}finally{q(!1)}}};return e.jsxs(X.SideModal,{header:"Rest API",closeOnEscapeKey:!1,zIndex:A,headerCustomComponent:e.jsx(g.DrawerHeaderText,{title:"Rest API",subtitle:"Connect to any API endpoint. Define the request and a sample response for the agent to reference."}),width:"431px",headerStyle:g.DRAWER_HEADER_STYLE,bodyStyle:g.DRAWER_BODY_STYLE,onClose:r,saveButtonProps:{"data-test":"restapi-save",buttonText:"Save",buttonType:"primary",size:"small",disabled:!B,isLoading:O,onClick:V},cancelButtonProps:{"data-test":"restapi-cancel",buttonText:"Cancel",buttonType:"tertiaryGray",size:"small",onClick:r},children:[e.jsx(w.Input,{"data-test":"restapi-name",variant:"small",labelText:"Name",labelTextBold:!0,placeholder:"Enter action name",value:p,maxCharLimit:R.name,state:T?"invalid":void 0,errorMessage:T?"A tool with this name already exists":void 0,onChangeText:P}),e.jsx(Q.ToolInstructionsField,{"data-test":"restapi-instructions",label:"Instructions",value:j,onChange:_,maxLength:R.instructions,placeholder:"Describe how should the LLM generate the rest API",onImprove:I,variablesData:u}),e.jsx(i.FieldBlock,{children:e.jsx(k.VariableInlineField,{"data-test":"restapi-url",label:"Request URL",placeholder:"https://",value:h,onChange:F,variablesData:u})}),e.jsxs(i.FieldBlock,{children:[e.jsx(c.TitleSmall,{color:l.COLORS.content.primary,children:"HTTP method"}),e.jsx(Z.Dropdown,{"data-test":"restapi-method",size:"small",width:"100%",dropdownWidth:"399px",options:ne,defaultOptions:[{label:m,value:m}],onSelect:t=>{const s=ee.unwrapDropdownValue(t);s&&M(s)}})]}),e.jsxs(i.FieldBlock,{children:[e.jsx(c.TitleSmall,{color:l.COLORS.content.primary,children:"Headers"}),x.map((t,s)=>e.jsxs(oe,{"data-test":`restapi-header-${s}`,children:[e.jsx(w.Input,{variant:"small",hideInputHeader:!0,placeholder:"Enter name",value:t.key,onChangeText:n=>L(s,{key:n})}),e.jsx(k.VariableInlineField,{placeholder:"Enter value",value:t.value,onChange:n=>L(s,{value:n}),variablesData:u}),e.jsx(ie,{type:"button","aria-label":"Remove header","data-test":`restapi-header-remove-${s}`,onClick:()=>J(s),children:e.jsx(U.default,{width:16,height:16,color:l.COLORS.content.secondary})})]},`${s}:${x.length}`)),e.jsx(le,{children:e.jsx(te.Button,{"data-test":"restapi-add-header",buttonType:"dashRegular",size:"small",LeadingIcon:W.default,buttonText:"Add header",onClick:z})})]}),e.jsxs(i.FieldBlock,{children:[e.jsx(c.TitleSmall,{color:l.COLORS.content.primary,children:"Request "}),e.jsx(E.BikEditor,{features:{jsonMode:!0},initialContent:C,style:f,placeholder:"Type or paste JSON here",onChange:t=>N(t.text),minHeight:"120px",maxHeight:"240px"})]}),e.jsxs(i.FieldBlock,{children:[e.jsx(c.TitleSmall,{color:l.COLORS.content.primary,children:"Response "}),e.jsx(E.BikEditor,{features:{jsonMode:!0},style:f,initialContent:b,placeholder:"Type or paste JSON here",onChange:t=>$(t.text),minHeight:"120px",maxHeight:"240px"})]})]})},oe=v.default.div`
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 32px;
|
|
3
4
|
align-items: center;
|
|
4
5
|
gap: 8px;
|
|
5
|
-
`,
|
|
6
|
+
`,le=v.default.div`
|
|
6
7
|
display: flex;
|
|
7
|
-
`,
|
|
8
|
+
`,ie=v.default.button`
|
|
8
9
|
display: inline-flex;
|
|
9
10
|
align-items: center;
|
|
10
11
|
justify-content: center;
|
|
11
12
|
flex-shrink: 0;
|
|
12
13
|
width: 32px;
|
|
13
14
|
height: 32px;
|
|
14
|
-
border: 1px solid ${
|
|
15
|
+
border: 1px solid ${l.COLORS.stroke.primary};
|
|
15
16
|
border-radius: 4px;
|
|
16
|
-
background: ${
|
|
17
|
+
background: ${l.COLORS.surface.standard};
|
|
17
18
|
cursor: pointer;
|
|
18
19
|
|
|
19
20
|
&:hover {
|
|
20
|
-
border-color: ${
|
|
21
|
+
border-color: ${l.COLORS.content.secondary};
|
|
21
22
|
}
|
|
22
|
-
`,
|
|
23
|
+
`,f={borderRadius:"4px",overflow:"hidden"};exports.JsonFieldBox=f;exports.RestApiToolPanel=ae;
|
|
23
24
|
//# sourceMappingURL=RestApiToolPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RestApiToolPanel.js","sources":["../../../../../src/components/agent-builder/components/RestApiToolPanel.tsx"],"sourcesContent":["import DeleteIcon from '@src/assets/icons/delete.svg';\nimport PlusIcon from '@src/assets/icons/plus.svg';\nimport { BikEditor, BikEditorRef } from '@src/editor';\nimport React, { useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport { Button } from '@src/components/button';\nimport { Dropdown, unwrapDropdownValue } from '@src/components/dropdown';\nimport { SingleOption } from '@src/components/dropdown/type';\nimport { Input } from '@src/components/input';\nimport { SideModal } from '@src/components/side-modal';\nimport { TitleSmall } from '@src/components/TypographyStyle';\nimport { COLORS } from '@src/constants/Theme';\nimport type { VariableListInterfaceV3 } from '../../variable-picker-v3/model';\nimport {\n\tRestApiHeader,\n\tRestApiMethod,\n\tRestApiToolConfig,\n} from '../AgentBuilder.model';\nimport { FieldBlock } from '../AgentBuilder.styled';\nimport type { AgentCharLimits } from '../constants/charLimits';\nimport { AGENT_CHAR_LIMITS } from '../constants/charLimits';\nimport {\n\tDRAWER_BODY_STYLE,\n\tDRAWER_HEADER_STYLE,\n\tDrawerHeaderText,\n} from './DrawerHeaderText';\nimport { ToolInstructionsField } from './ToolInstructionsField';\nimport { VariableTriggerArea } from './VariableTriggerArea';\n\ninterface RestApiToolPanelProps {\n\tonClose: () => void;\n\t/** Persist the config. A pending promise shows the Save loader. */\n\tonSave: (config: RestApiToolConfig) => void | Promise<void>;\n\t/** Seed values when editing an existing tool. */\n\tinitial?: RestApiToolConfig;\n\t/** Optional \"Improve with AI\" for the instructions field. Omit to hide it. */\n\tonImprove?: (text: string) => Promise<string>;\n\tcharLimits?: AgentCharLimits;\n\t/** SideModal stacking order — raise it when opened over another drawer. */\n\tzIndex?: number;\n\t/** Names of the OTHER tools — a new tool can't reuse one of these. */\n\texistingNames?: string[];\n\t/**\n\t * Variable catalog for the `@` trigger. When non-empty, `@` opens the\n\t * variable picker directly in Instructions / Request URL / header values /\n\t * Request / Response. Absent → the drawer behaves exactly as before.\n\t */\n\tvariablesData?: VariableListInterfaceV3[];\n}\n\nconst METHODS: RestApiMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];\nconst METHOD_OPTIONS: SingleOption[] = METHODS.map((m) => ({\n\tlabel: m,\n\tvalue: m,\n}));\n\n/**\n * Keep the Request / Response bodies valid JSON: wrap the picked token in\n * quotes (`\"email\": \"{{variable:customer.email}}\"`) — unless the `@` was typed\n * inside a string literal already (odd number of unescaped `\"` before it, e.g.\n * `\"Bearer @`), where the bare token interpolates into the existing string.\n */\nexport const jsonQuoteToken = (\n\ttoken: string,\n\tctx: { textBefore: string },\n): string => {\n\tconst quotes = ctx.textBefore.match(/(?<!\\\\)\"/g)?.length ?? 0;\n\treturn quotes % 2 === 1 ? token : `\"${token}\"`;\n};\n\n/**\n * Right-side drawer to configure a \"Rest API\" tool: the endpoint the agent can\n * call, plus a JSON request-body template and a sample response. Mount it fresh\n * per open (key it) so `initial` seeds the state. Built on the shared `SideModal`\n * shell and the BikEditor JSON body mode for the Request / Response editors.\n */\nexport const RestApiToolPanel: React.FC<RestApiToolPanelProps> = ({\n\tonClose,\n\tonSave,\n\tinitial,\n\tonImprove,\n\tcharLimits = AGENT_CHAR_LIMITS,\n\tzIndex,\n\texistingNames = [],\n\tvariablesData,\n}) => {\n\tconst [name, setName] = useState(initial?.name ?? '');\n\t// `@`-picked variables insert into the JSON editors at their own cursor.\n\tconst requestEditorRef = useRef<BikEditorRef>(null);\n\tconst responseEditorRef = useRef<BikEditorRef>(null);\n\tconst [instructions, setInstructions] = useState(initial?.instructions ?? '');\n\t// New tools start with the scheme pre-filled so the user only types the host.\n\tconst [url, setUrl] = useState(initial?.url ?? 'https://');\n\tconst [method, setMethod] = useState<RestApiMethod>(\n\t\tinitial?.method ?? 'POST',\n\t);\n\tconst [headers, setHeaders] = useState<RestApiHeader[]>(\n\t\tinitial?.headers ?? [{ key: '', value: '' }],\n\t);\n\tconst [requestBody, setRequestBody] = useState(initial?.requestBody ?? '');\n\tconst [responseSample, setResponseSample] = useState(\n\t\tinitial?.responseSample ?? '',\n\t);\n\tconst [saving, setSaving] = useState(false);\n\n\tconst patchHeader = (i: number, patch: Partial<RestApiHeader>) =>\n\t\tsetHeaders((prev) =>\n\t\t\tprev.map((h, idx) => (idx === i ? { ...h, ...patch } : h)),\n\t\t);\n\tconst addHeader = () =>\n\t\tsetHeaders((prev) => [...prev, { key: '', value: '' }]);\n\tconst removeHeader = (i: number) =>\n\t\tsetHeaders((prev) => prev.filter((_, idx) => idx !== i));\n\n\tconst nameTrimmed = name.trim();\n\t// Case-insensitive: two tools can't share a name (the LLM references them by it).\n\tconst isDuplicateName =\n\t\tnameTrimmed.length > 0 &&\n\t\texistingNames.some(\n\t\t\t(n) => n.trim().toLowerCase() === nameTrimmed.toLowerCase(),\n\t\t);\n\tconst canSave =\n\t\tnameTrimmed.length > 0 && url.trim().length > 0 && !isDuplicateName;\n\n\tconst handleSave = async () => {\n\t\tif (!canSave || saving) {\n\t\t\treturn;\n\t\t}\n\t\tconst config: RestApiToolConfig = {\n\t\t\tid: initial?.id ?? `restapi_${Date.now().toString(36)}`,\n\t\t\tname: name.trim(),\n\t\t\tinstructions,\n\t\t\turl: url.trim(),\n\t\t\tmethod,\n\t\t\t// Drop fully-empty header rows.\n\t\t\theaders: headers.filter((h) => h.key.trim() || h.value.trim()),\n\t\t\trequestBody,\n\t\t\tresponseSample,\n\t\t};\n\t\tconst result = onSave(config);\n\t\tif (result && typeof (result as Promise<void>).then === 'function') {\n\t\t\tsetSaving(true);\n\t\t\ttry {\n\t\t\t\tawait result;\n\t\t\t} finally {\n\t\t\t\tsetSaving(false);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn (\n\t\t<SideModal\n\t\t\theader=\"Rest API\"\n\t\t\tcloseOnEscapeKey={false}\n\t\t\tzIndex={zIndex}\n\t\t\theaderCustomComponent={\n\t\t\t\t<DrawerHeaderText\n\t\t\t\t\ttitle=\"Rest API\"\n\t\t\t\t\tsubtitle=\"Connect to any API endpoint. Define the request and a sample response for the agent to reference.\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\twidth=\"431px\"\n\t\t\theaderStyle={DRAWER_HEADER_STYLE}\n\t\t\tbodyStyle={DRAWER_BODY_STYLE}\n\t\t\tonClose={onClose}\n\t\t\tsaveButtonProps={{\n\t\t\t\t'data-test': 'restapi-save',\n\t\t\t\tbuttonText: 'Save',\n\t\t\t\tbuttonType: 'primary',\n\t\t\t\tsize: 'small',\n\t\t\t\tdisabled: !canSave,\n\t\t\t\tisLoading: saving,\n\t\t\t\tonClick: handleSave,\n\t\t\t}}\n\t\t\tcancelButtonProps={{\n\t\t\t\t'data-test': 'restapi-cancel',\n\t\t\t\tbuttonText: 'Cancel',\n\t\t\t\tbuttonType: 'tertiaryGray',\n\t\t\t\tsize: 'small',\n\t\t\t\tonClick: onClose,\n\t\t\t}}\n\t\t>\n\t\t\t<Input\n\t\t\t\tdata-test=\"restapi-name\"\n\t\t\t\tvariant=\"small\"\n\t\t\t\tlabelText=\"Name\"\n\t\t\t\tlabelTextBold\n\t\t\t\tplaceholder=\"Enter action name\"\n\t\t\t\tvalue={name}\n\t\t\t\tmaxCharLimit={charLimits.name}\n\t\t\t\tstate={isDuplicateName ? 'invalid' : undefined}\n\t\t\t\terrorMessage={\n\t\t\t\t\tisDuplicateName ? 'A tool with this name already exists' : undefined\n\t\t\t\t}\n\t\t\t\tonChangeText={setName}\n\t\t\t/>\n\n\t\t\t<ToolInstructionsField\n\t\t\t\tdata-test=\"restapi-instructions\"\n\t\t\t\tlabel=\"Instructions\"\n\t\t\t\tvalue={instructions}\n\t\t\t\tonChange={setInstructions}\n\t\t\t\tmaxLength={charLimits.instructions}\n\t\t\t\tplaceholder=\"Describe how should the LLM generate the rest API\"\n\t\t\t\tonImprove={onImprove}\n\t\t\t\tvariablesData={variablesData}\n\t\t\t/>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<VariableTriggerArea\n\t\t\t\t\taddDataButton\n\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\tonInsert={(token, caret) =>\n\t\t\t\t\t\tsetUrl((u) => {\n\t\t\t\t\t\t\tconst at = caret ?? u.length;\n\t\t\t\t\t\t\treturn `${u.slice(0, at)}${token}${u.slice(at)}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tdata-test=\"restapi-url\"\n\t\t\t\t\t\tvariant=\"small\"\n\t\t\t\t\t\tlabelText=\"Request URL\"\n\t\t\t\t\t\tlabelTextBold\n\t\t\t\t\t\tplaceholder=\"https://\"\n\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\tvalue={url}\n\t\t\t\t\t\tonChangeText={setUrl}\n\t\t\t\t\t/>\n\t\t\t\t</VariableTriggerArea>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>HTTP method</TitleSmall>\n\t\t\t\t<Dropdown\n\t\t\t\t\tdata-test=\"restapi-method\"\n\t\t\t\t\tsize=\"small\"\n\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\tdropdownWidth=\"399px\"\n\t\t\t\t\toptions={METHOD_OPTIONS}\n\t\t\t\t\tdefaultOptions={[{ label: method, value: method }]}\n\t\t\t\t\tonSelect={(opt) => {\n\t\t\t\t\t\tconst v = unwrapDropdownValue(opt);\n\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\tsetMethod(v as RestApiMethod);\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Headers</TitleSmall>\n\t\t\t\t{headers.map((h, i) => (\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<HeaderRow key={i} data-test={`restapi-header-${i}`}>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tvariant=\"small\"\n\t\t\t\t\t\t\thideInputHeader\n\t\t\t\t\t\t\tplaceholder=\"Enter name\"\n\t\t\t\t\t\t\tvalue={h.key}\n\t\t\t\t\t\t\tonChangeText={(v) => patchHeader(i, { key: v })}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<VariableTriggerArea\n\t\t\t\t\t\t\taddDataButton\n\t\t\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\t\t\tonInsert={(token, caret) => {\n\t\t\t\t\t\t\t\tconst at = caret ?? h.value.length;\n\t\t\t\t\t\t\t\tpatchHeader(i, {\n\t\t\t\t\t\t\t\t\tvalue: `${h.value.slice(0, at)}${token}${h.value.slice(at)}`,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tvariant=\"small\"\n\t\t\t\t\t\t\t\thideInputHeader\n\t\t\t\t\t\t\t\tplaceholder=\"Enter value\"\n\t\t\t\t\t\t\t\tvalue={h.value}\n\t\t\t\t\t\t\t\tonChangeText={(v) => patchHeader(i, { value: v })}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</VariableTriggerArea>\n\t\t\t\t\t\t<IconBtn\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label=\"Remove header\"\n\t\t\t\t\t\t\tdata-test={`restapi-header-remove-${i}`}\n\t\t\t\t\t\t\tonClick={() => removeHeader(i)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<DeleteIcon\n\t\t\t\t\t\t\t\twidth={16}\n\t\t\t\t\t\t\t\theight={16}\n\t\t\t\t\t\t\t\tcolor={COLORS.content.secondary}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</IconBtn>\n\t\t\t\t\t</HeaderRow>\n\t\t\t\t))}\n\t\t\t\t<AddHeaderRow>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tdata-test=\"restapi-add-header\"\n\t\t\t\t\t\tbuttonType=\"dashRegular\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tLeadingIcon={PlusIcon}\n\t\t\t\t\t\tbuttonText=\"Add header\"\n\t\t\t\t\t\tonClick={addHeader}\n\t\t\t\t\t/>\n\t\t\t\t</AddHeaderRow>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Request </TitleSmall>\n\t\t\t\t<VariableTriggerArea\n\t\t\t\t\taddDataButton\n\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\ttransformToken={jsonQuoteToken}\n\t\t\t\t\tonInsert={(token) =>\n\t\t\t\t\t\trequestEditorRef.current?.insertInlineContent(token)\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t<BikEditor\n\t\t\t\t\t\tref={requestEditorRef}\n\t\t\t\t\t\tfeatures={{ jsonMode: true }}\n\t\t\t\t\t\tinitialContent={requestBody}\n\t\t\t\t\t\tstyle={JsonFieldBox}\n\t\t\t\t\t\tplaceholder=\"Type or paste JSON here\"\n\t\t\t\t\t\tonChange={(c) => setRequestBody(c.text)}\n\t\t\t\t\t\tminHeight=\"120px\"\n\t\t\t\t\t\tmaxHeight=\"240px\"\n\t\t\t\t\t/>\n\t\t\t\t</VariableTriggerArea>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Response </TitleSmall>\n\t\t\t\t<VariableTriggerArea\n\t\t\t\t\taddDataButton\n\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\ttransformToken={jsonQuoteToken}\n\t\t\t\t\tonInsert={(token) =>\n\t\t\t\t\t\tresponseEditorRef.current?.insertInlineContent(token)\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t<BikEditor\n\t\t\t\t\t\tref={responseEditorRef}\n\t\t\t\t\t\tfeatures={{ jsonMode: true }}\n\t\t\t\t\t\tstyle={JsonFieldBox}\n\t\t\t\t\t\tinitialContent={responseSample}\n\t\t\t\t\t\tplaceholder=\"Type or paste JSON here\"\n\t\t\t\t\t\tonChange={(c) => setResponseSample(c.text)}\n\t\t\t\t\t\tminHeight=\"120px\"\n\t\t\t\t\t\tmaxHeight=\"240px\"\n\t\t\t\t\t/>\n\t\t\t\t</VariableTriggerArea>\n\t\t\t</FieldBlock>\n\t\t</SideModal>\n\t);\n};\n\nconst HeaderRow = styled.div`\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n`;\n\nconst AddHeaderRow = styled.div`\n\tdisplay: flex;\n`;\n\nconst IconBtn = styled.button`\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tflex-shrink: 0;\n\twidth: 32px;\n\theight: 32px;\n\tborder: 1px solid ${COLORS.stroke.primary};\n\tborder-radius: 4px;\n\tbackground: ${COLORS.surface.standard};\n\tcursor: pointer;\n\n\t&:hover {\n\t\tborder-color: ${COLORS.content.secondary};\n\t}\n`;\n\nexport const JsonFieldBox = {\n\tborderRadius: '4px',\n\toverflow: 'hidden',\n};\n"],"names":["METHODS","METHOD_OPTIONS","m","jsonQuoteToken","token","ctx","RestApiToolPanel","onClose","onSave","initial","onImprove","charLimits","AGENT_CHAR_LIMITS","zIndex","existingNames","variablesData","name","setName","useState","requestEditorRef","useRef","responseEditorRef","instructions","setInstructions","url","setUrl","method","setMethod","headers","setHeaders","requestBody","setRequestBody","responseSample","setResponseSample","saving","setSaving","patchHeader","i","patch","prev","h","idx","addHeader","removeHeader","_","nameTrimmed","isDuplicateName","n","canSave","handleSave","config","result","jsxs","SideModal","jsx","DrawerHeaderText","DRAWER_HEADER_STYLE","DRAWER_BODY_STYLE","Input","ToolInstructionsField","FieldBlock","VariableTriggerArea","caret","u","at","TitleSmall","COLORS","Dropdown","opt","v","unwrapDropdownValue","HeaderRow","IconBtn","DeleteIcon","AddHeaderRow","Button","PlusIcon","BikEditor","JsonFieldBox","c","styled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RestApiToolPanel.js","sources":["../../../../../src/components/agent-builder/components/RestApiToolPanel.tsx"],"sourcesContent":["import DeleteIcon from '@src/assets/icons/delete.svg';\nimport PlusIcon from '@src/assets/icons/plus.svg';\nimport { BikEditor } from '@src/editor';\nimport React, { useState } from 'react';\nimport styled from 'styled-components';\nimport { Button } from '@src/components/button';\nimport { Dropdown, unwrapDropdownValue } from '@src/components/dropdown';\nimport { SingleOption } from '@src/components/dropdown/type';\nimport { Input } from '@src/components/input';\nimport { SideModal } from '@src/components/side-modal';\nimport { TitleSmall } from '@src/components/TypographyStyle';\nimport { COLORS } from '@src/constants/Theme';\nimport type { VariableListInterfaceV3 } from '../../variable-picker-v3/model';\nimport {\n\tRestApiHeader,\n\tRestApiMethod,\n\tRestApiToolConfig,\n} from '../AgentBuilder.model';\nimport { FieldBlock } from '../AgentBuilder.styled';\nimport type { AgentCharLimits } from '../constants/charLimits';\nimport { AGENT_CHAR_LIMITS } from '../constants/charLimits';\nimport {\n\tDRAWER_BODY_STYLE,\n\tDRAWER_HEADER_STYLE,\n\tDrawerHeaderText,\n} from './DrawerHeaderText';\nimport { ToolInstructionsField } from './ToolInstructionsField';\nimport { VariableInlineField } from './VariableInlineField';\n\ninterface RestApiToolPanelProps {\n\tonClose: () => void;\n\t/** Persist the config. A pending promise shows the Save loader. */\n\tonSave: (config: RestApiToolConfig) => void | Promise<void>;\n\t/** Seed values when editing an existing tool. */\n\tinitial?: RestApiToolConfig;\n\t/** Optional \"Improve with AI\" for the instructions field. Omit to hide it. */\n\tonImprove?: (text: string) => Promise<string>;\n\tcharLimits?: AgentCharLimits;\n\t/** SideModal stacking order — raise it when opened over another drawer. */\n\tzIndex?: number;\n\t/** Names of the OTHER tools — a new tool can't reuse one of these. */\n\texistingNames?: string[];\n\t/**\n\t * Variable catalog for the `@` trigger. When non-empty, `@` opens the\n\t * variable picker directly in Instructions / Request URL / header values.\n\t * (Request / Response JSON editors are deliberately not wired for now.)\n\t * Absent → the drawer behaves exactly as before.\n\t */\n\tvariablesData?: VariableListInterfaceV3[];\n}\n\nconst METHODS: RestApiMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];\nconst METHOD_OPTIONS: SingleOption[] = METHODS.map((m) => ({\n\tlabel: m,\n\tvalue: m,\n}));\n\n/**\n * Keep the Request / Response bodies valid JSON: wrap the picked token in\n * quotes (`\"email\": \"{{variable:customer.email}}\"`) — unless the `@` was typed\n * inside a string literal already (odd number of unescaped `\"` before it, e.g.\n * `\"Bearer @`), where the bare token interpolates into the existing string.\n */\nexport const jsonQuoteToken = (\n\ttoken: string,\n\tctx: { textBefore: string },\n): string => {\n\tconst quotes = ctx.textBefore.match(/(?<!\\\\)\"/g)?.length ?? 0;\n\treturn quotes % 2 === 1 ? token : `\"${token}\"`;\n};\n\n/**\n * Right-side drawer to configure a \"Rest API\" tool: the endpoint the agent can\n * call, plus a JSON request-body template and a sample response. Mount it fresh\n * per open (key it) so `initial` seeds the state. Built on the shared `SideModal`\n * shell and the BikEditor JSON body mode for the Request / Response editors.\n */\nexport const RestApiToolPanel: React.FC<RestApiToolPanelProps> = ({\n\tonClose,\n\tonSave,\n\tinitial,\n\tonImprove,\n\tcharLimits = AGENT_CHAR_LIMITS,\n\tzIndex,\n\texistingNames = [],\n\tvariablesData,\n}) => {\n\tconst [name, setName] = useState(initial?.name ?? '');\n\tconst [instructions, setInstructions] = useState(initial?.instructions ?? '');\n\t// New tools start with the scheme pre-filled so the user only types the host.\n\tconst [url, setUrl] = useState(initial?.url ?? 'https://');\n\tconst [method, setMethod] = useState<RestApiMethod>(\n\t\tinitial?.method ?? 'POST',\n\t);\n\tconst [headers, setHeaders] = useState<RestApiHeader[]>(\n\t\tinitial?.headers ?? [{ key: '', value: '' }],\n\t);\n\tconst [requestBody, setRequestBody] = useState(initial?.requestBody ?? '');\n\tconst [responseSample, setResponseSample] = useState(\n\t\tinitial?.responseSample ?? '',\n\t);\n\tconst [saving, setSaving] = useState(false);\n\n\tconst patchHeader = (i: number, patch: Partial<RestApiHeader>) =>\n\t\tsetHeaders((prev) =>\n\t\t\tprev.map((h, idx) => (idx === i ? { ...h, ...patch } : h)),\n\t\t);\n\tconst addHeader = () =>\n\t\tsetHeaders((prev) => [...prev, { key: '', value: '' }]);\n\tconst removeHeader = (i: number) =>\n\t\tsetHeaders((prev) => prev.filter((_, idx) => idx !== i));\n\n\tconst nameTrimmed = name.trim();\n\t// Case-insensitive: two tools can't share a name (the LLM references them by it).\n\tconst isDuplicateName =\n\t\tnameTrimmed.length > 0 &&\n\t\texistingNames.some(\n\t\t\t(n) => n.trim().toLowerCase() === nameTrimmed.toLowerCase(),\n\t\t);\n\tconst canSave =\n\t\tnameTrimmed.length > 0 && url.trim().length > 0 && !isDuplicateName;\n\n\tconst handleSave = async () => {\n\t\tif (!canSave || saving) {\n\t\t\treturn;\n\t\t}\n\t\tconst config: RestApiToolConfig = {\n\t\t\tid: initial?.id ?? `restapi_${Date.now().toString(36)}`,\n\t\t\tname: name.trim(),\n\t\t\tinstructions,\n\t\t\turl: url.trim(),\n\t\t\tmethod,\n\t\t\t// Drop fully-empty header rows.\n\t\t\theaders: headers.filter((h) => h.key.trim() || h.value.trim()),\n\t\t\trequestBody,\n\t\t\tresponseSample,\n\t\t};\n\t\tconst result = onSave(config);\n\t\tif (result && typeof (result as Promise<void>).then === 'function') {\n\t\t\tsetSaving(true);\n\t\t\ttry {\n\t\t\t\tawait result;\n\t\t\t} finally {\n\t\t\t\tsetSaving(false);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn (\n\t\t<SideModal\n\t\t\theader=\"Rest API\"\n\t\t\tcloseOnEscapeKey={false}\n\t\t\tzIndex={zIndex}\n\t\t\theaderCustomComponent={\n\t\t\t\t<DrawerHeaderText\n\t\t\t\t\ttitle=\"Rest API\"\n\t\t\t\t\tsubtitle=\"Connect to any API endpoint. Define the request and a sample response for the agent to reference.\"\n\t\t\t\t/>\n\t\t\t}\n\t\t\twidth=\"431px\"\n\t\t\theaderStyle={DRAWER_HEADER_STYLE}\n\t\t\tbodyStyle={DRAWER_BODY_STYLE}\n\t\t\tonClose={onClose}\n\t\t\tsaveButtonProps={{\n\t\t\t\t'data-test': 'restapi-save',\n\t\t\t\tbuttonText: 'Save',\n\t\t\t\tbuttonType: 'primary',\n\t\t\t\tsize: 'small',\n\t\t\t\tdisabled: !canSave,\n\t\t\t\tisLoading: saving,\n\t\t\t\tonClick: handleSave,\n\t\t\t}}\n\t\t\tcancelButtonProps={{\n\t\t\t\t'data-test': 'restapi-cancel',\n\t\t\t\tbuttonText: 'Cancel',\n\t\t\t\tbuttonType: 'tertiaryGray',\n\t\t\t\tsize: 'small',\n\t\t\t\tonClick: onClose,\n\t\t\t}}\n\t\t>\n\t\t\t<Input\n\t\t\t\tdata-test=\"restapi-name\"\n\t\t\t\tvariant=\"small\"\n\t\t\t\tlabelText=\"Name\"\n\t\t\t\tlabelTextBold\n\t\t\t\tplaceholder=\"Enter action name\"\n\t\t\t\tvalue={name}\n\t\t\t\tmaxCharLimit={charLimits.name}\n\t\t\t\tstate={isDuplicateName ? 'invalid' : undefined}\n\t\t\t\terrorMessage={\n\t\t\t\t\tisDuplicateName ? 'A tool with this name already exists' : undefined\n\t\t\t\t}\n\t\t\t\tonChangeText={setName}\n\t\t\t/>\n\n\t\t\t<ToolInstructionsField\n\t\t\t\tdata-test=\"restapi-instructions\"\n\t\t\t\tlabel=\"Instructions\"\n\t\t\t\tvalue={instructions}\n\t\t\t\tonChange={setInstructions}\n\t\t\t\tmaxLength={charLimits.instructions}\n\t\t\t\tplaceholder=\"Describe how should the LLM generate the rest API\"\n\t\t\t\tonImprove={onImprove}\n\t\t\t\tvariablesData={variablesData}\n\t\t\t/>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<VariableInlineField\n\t\t\t\t\tdata-test=\"restapi-url\"\n\t\t\t\t\tlabel=\"Request URL\"\n\t\t\t\t\tplaceholder=\"https://\"\n\t\t\t\t\tvalue={url}\n\t\t\t\t\tonChange={setUrl}\n\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t/>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>HTTP method</TitleSmall>\n\t\t\t\t<Dropdown\n\t\t\t\t\tdata-test=\"restapi-method\"\n\t\t\t\t\tsize=\"small\"\n\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\tdropdownWidth=\"399px\"\n\t\t\t\t\toptions={METHOD_OPTIONS}\n\t\t\t\t\tdefaultOptions={[{ label: method, value: method }]}\n\t\t\t\t\tonSelect={(opt) => {\n\t\t\t\t\t\tconst v = unwrapDropdownValue(opt);\n\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\tsetMethod(v as RestApiMethod);\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Headers</TitleSmall>\n\t\t\t\t{headers.map((h, i) => (\n\t\t\t\t\t// Key includes the row count: the value field is an uncontrolled\n\t\t\t\t\t// editor, so add/remove must remount rows or a deleted row's text\n\t\t\t\t\t// would linger in its reused neighbour.\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<HeaderRow\n\t\t\t\t\t\tkey={`${i}:${headers.length}`}\n\t\t\t\t\t\tdata-test={`restapi-header-${i}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tvariant=\"small\"\n\t\t\t\t\t\t\thideInputHeader\n\t\t\t\t\t\t\tplaceholder=\"Enter name\"\n\t\t\t\t\t\t\tvalue={h.key}\n\t\t\t\t\t\t\tonChangeText={(v) => patchHeader(i, { key: v })}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<VariableInlineField\n\t\t\t\t\t\t\tplaceholder=\"Enter value\"\n\t\t\t\t\t\t\tvalue={h.value}\n\t\t\t\t\t\t\tonChange={(v) => patchHeader(i, { value: v })}\n\t\t\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<IconBtn\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\taria-label=\"Remove header\"\n\t\t\t\t\t\t\tdata-test={`restapi-header-remove-${i}`}\n\t\t\t\t\t\t\tonClick={() => removeHeader(i)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<DeleteIcon\n\t\t\t\t\t\t\t\twidth={16}\n\t\t\t\t\t\t\t\theight={16}\n\t\t\t\t\t\t\t\tcolor={COLORS.content.secondary}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</IconBtn>\n\t\t\t\t\t</HeaderRow>\n\t\t\t\t))}\n\t\t\t\t<AddHeaderRow>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tdata-test=\"restapi-add-header\"\n\t\t\t\t\t\tbuttonType=\"dashRegular\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tLeadingIcon={PlusIcon}\n\t\t\t\t\t\tbuttonText=\"Add header\"\n\t\t\t\t\t\tonClick={addHeader}\n\t\t\t\t\t/>\n\t\t\t\t</AddHeaderRow>\n\t\t\t</FieldBlock>\n\n\t\t\t{/* Variables intentionally NOT wired here (yet) — the JSON editors are\n\t\t\t plain code fields; @ types a literal @. */}\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Request </TitleSmall>\n\t\t\t\t<BikEditor\n\t\t\t\t\tfeatures={{ jsonMode: true }}\n\t\t\t\t\tinitialContent={requestBody}\n\t\t\t\t\tstyle={JsonFieldBox}\n\t\t\t\t\tplaceholder=\"Type or paste JSON here\"\n\t\t\t\t\tonChange={(c) => setRequestBody(c.text)}\n\t\t\t\t\tminHeight=\"120px\"\n\t\t\t\t\tmaxHeight=\"240px\"\n\t\t\t\t/>\n\t\t\t</FieldBlock>\n\n\t\t\t<FieldBlock>\n\t\t\t\t<TitleSmall color={COLORS.content.primary}>Response </TitleSmall>\n\t\t\t\t<BikEditor\n\t\t\t\t\tfeatures={{ jsonMode: true }}\n\t\t\t\t\tstyle={JsonFieldBox}\n\t\t\t\t\tinitialContent={responseSample}\n\t\t\t\t\tplaceholder=\"Type or paste JSON here\"\n\t\t\t\t\tonChange={(c) => setResponseSample(c.text)}\n\t\t\t\t\tminHeight=\"120px\"\n\t\t\t\t\tmaxHeight=\"240px\"\n\t\t\t\t/>\n\t\t\t</FieldBlock>\n\t\t</SideModal>\n\t);\n};\n\n// Fixed tracks (name / value / delete): minmax(0, …) lets the fields shrink\n// below their content width, so a long value scrolls inside its field and the\n// delete button can never be pushed off the row.\nconst HeaderRow = styled.div`\n\tdisplay: grid;\n\tgrid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 32px;\n\talign-items: center;\n\tgap: 8px;\n`;\n\nconst AddHeaderRow = styled.div`\n\tdisplay: flex;\n`;\n\nconst IconBtn = styled.button`\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tflex-shrink: 0;\n\twidth: 32px;\n\theight: 32px;\n\tborder: 1px solid ${COLORS.stroke.primary};\n\tborder-radius: 4px;\n\tbackground: ${COLORS.surface.standard};\n\tcursor: pointer;\n\n\t&:hover {\n\t\tborder-color: ${COLORS.content.secondary};\n\t}\n`;\n\nexport const JsonFieldBox = {\n\tborderRadius: '4px',\n\toverflow: 'hidden',\n};\n"],"names":["METHODS","METHOD_OPTIONS","m","RestApiToolPanel","onClose","onSave","initial","onImprove","charLimits","AGENT_CHAR_LIMITS","zIndex","existingNames","variablesData","name","setName","useState","instructions","setInstructions","url","setUrl","method","setMethod","headers","setHeaders","requestBody","setRequestBody","responseSample","setResponseSample","saving","setSaving","patchHeader","i","patch","prev","h","idx","addHeader","removeHeader","_","nameTrimmed","isDuplicateName","n","canSave","handleSave","config","result","jsxs","SideModal","jsx","DrawerHeaderText","DRAWER_HEADER_STYLE","DRAWER_BODY_STYLE","Input","ToolInstructionsField","FieldBlock","VariableInlineField","TitleSmall","COLORS","Dropdown","opt","v","unwrapDropdownValue","HeaderRow","IconBtn","DeleteIcon","AddHeaderRow","Button","PlusIcon","BikEditor","JsonFieldBox","c","styled"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),o=require("react"),f=require("styled-components"),x=require("../../TypographyStyle.js"),s=require("../../../constants/Theme.js"),i=require("../utils/mentionSerialization.js"),y=require("./MentionPicker.js"),v=require("./VariableTriggerArea.js"),k=require("../../../editor/BikEditor.js"),S=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},h=S(f),I=e=>i.variableTokenName(e).replace(/^variable:/,""),T=({value:e,onChange:d,label:c,placeholder:p,variablesData:l,...m})=>{const a=o.useRef(null),u=o.useMemo(()=>i.buildVariableMentionItems(l),[l]),b=o.useMemo(()=>i.instructionsToEditorHtml(e,u),[]),w=o.useCallback(t=>{const r=i.variableTokenName(t.actualValue);a.current?.insertInlineContent(i.buildMentionHtml(r,`{{${r}}}`,"variable"))},[]),g=o.useCallback(()=>{const t=a.current?.getJSON()??null;d(i.editorDocToInstructions(t,[]).replace(/\n+/g,""))},[d]);return n.jsxs(O,{children:[c&&n.jsx(x.TitleSmall,{color:s.COLORS.content.primary,children:c}),n.jsx(v.VariableTriggerArea,{variablesData:l,addDataButton:!0,skipNativeBoundaries:!0,onInsert:t=>{const r=I(t);a.current?.insertInlineContent(i.buildMentionHtml(r,`{{${r}}}`,"variable"))},children:n.jsx(M,{"data-test":m["data-test"],children:n.jsx(k.BikEditor,{ref:a,initialContent:b,placeholder:p,minHeight:"20px",maxHeight:"20px",style:{border:"none"},editorStyle:C,onSend:()=>{},sendShortcut:{key:"Enter"},mentions:{agents:u,removeTriggerOnDismiss:!0,renderDropdown:t=>n.jsx(y.MentionPicker,{...t,variablesData:l,onSelectVariable:w})},onChange:g})})})]})},C={padding:0,fontFamily:"Inter",fontSize:"14px",lineHeight:"20px"},O=h.default.div`
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 4px;
|
|
5
|
+
width: 100%;
|
|
6
|
+
/* In a flex row (header key/value pairs) share the space and allow
|
|
7
|
+
shrinking below content width — long values scroll inside the field
|
|
8
|
+
instead of widening the row / drawer. */
|
|
9
|
+
flex: 1 1 0%;
|
|
10
|
+
min-width: 0;
|
|
11
|
+
`,M=h.default.div`
|
|
12
|
+
/* No global reset in every host — without this, width:100% + padding
|
|
13
|
+
overflows the drawer by the padding + border (the reported overflow). */
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
width: 100%;
|
|
18
|
+
min-width: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
padding: 6px 12px;
|
|
21
|
+
background: ${s.COLORS.surface.standard};
|
|
22
|
+
border: 1px solid ${s.COLORS.stroke.primary};
|
|
23
|
+
border-radius: 4px;
|
|
24
|
+
|
|
25
|
+
/* The editor root is width:100% — cap it so a long value scrolls inside
|
|
26
|
+
the ProseMirror line rather than stretching the shell. */
|
|
27
|
+
> div {
|
|
28
|
+
width: 100%;
|
|
29
|
+
min-width: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:focus-within {
|
|
33
|
+
border-color: ${s.COLORS.stroke.brand};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Keep the editor to one visual line; long values scroll horizontally the
|
|
37
|
+
way a native input does. */
|
|
38
|
+
.ProseMirror {
|
|
39
|
+
width: 100%;
|
|
40
|
+
max-width: 100%;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
overflow-y: hidden;
|
|
44
|
+
word-break: normal;
|
|
45
|
+
scrollbar-width: none;
|
|
46
|
+
|
|
47
|
+
&::-webkit-scrollbar {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.ProseMirror p {
|
|
53
|
+
display: inline;
|
|
54
|
+
}
|
|
55
|
+
`;exports.VariableInlineField=T;
|
|
56
|
+
//# sourceMappingURL=VariableInlineField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VariableInlineField.js","sources":["../../../../../src/components/agent-builder/components/VariableInlineField.tsx"],"sourcesContent":["import { BikEditor, BikEditorRef } from '@src/editor';\nimport React, { useCallback, useMemo, useRef } from 'react';\nimport styled from 'styled-components';\nimport { TitleSmall } from '@src/components/TypographyStyle';\nimport { COLORS } from '@src/constants/Theme';\nimport type {\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport {\n\tbuildMentionHtml,\n\tbuildVariableMentionItems,\n\teditorDocToInstructions,\n\tinstructionsToEditorHtml,\n\tvariableTokenName,\n} from '../utils/mentionSerialization';\nimport { MentionPicker } from './MentionPicker';\nimport { VariableTriggerArea } from './VariableTriggerArea';\n\ninterface VariableInlineFieldProps {\n\t/** Stored string, `{{variable:<name>}}` tokens inline (e.g. a URL). */\n\tvalue: string;\n\tonChange: (text: string) => void;\n\tlabel?: string;\n\tplaceholder?: string;\n\t/** Variable catalog. Absent / empty → behaves as a plain single-line input. */\n\tvariablesData?: VariableListInterfaceV3[];\n\t'data-test'?: string;\n}\n\n/** `{{variable:a.b}}` / `{{a.b}}` → `a.b`. */\nconst nameOf = (token: string): string =>\n\tvariableTokenName(token).replace(/^variable:/, '');\n\n/**\n * Single-line text field whose `{{variable:<name>}}` tokens render as the\n * SAME purple `{{<name>}}` chips as the Instructions / Guardrails editors —\n * BikEditor under the hood, styled like the small `Input`. Used for tool\n * params that mix free text with variables (Request URL, header values).\n *\n * `@` opens the variable panel: after whitespace / at the start via the\n * editor's native mention trigger, after URL/JSON delimiters (`https://@`)\n * via the {@link VariableTriggerArea} wrapper — both render the same\n * {@link MentionPicker}. Enter is swallowed (single-line), and any pasted\n * newlines are dropped on serialize.\n */\nexport const VariableInlineField: React.FC<VariableInlineFieldProps> = ({\n\tvalue,\n\tonChange,\n\tlabel,\n\tplaceholder,\n\tvariablesData,\n\t...rest\n}) => {\n\tconst editorRef = useRef<BikEditorRef>(null);\n\n\tconst mentionItems = useMemo(\n\t\t() => buildVariableMentionItems(variablesData),\n\t\t[variablesData],\n\t);\n\n\t// Mount-only initial HTML — the field is uncontrolled after mount (the\n\t// drawers key their fields per open, so `value` is fresh when it matters).\n\tconst initialHtml = useMemo(\n\t\t() => instructionsToEditorHtml(value, mentionItems),\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t\t[],\n\t);\n\n\t// Insert the purple `{{<name>}}` chip; serialization emits the stored\n\t// `{{variable:<name>}}` token (same path as the instructions editors).\n\tconst insertChip = useCallback((variable: VariableV3) => {\n\t\tconst name = variableTokenName(variable.actualValue);\n\t\teditorRef.current?.insertInlineContent(\n\t\t\tbuildMentionHtml(name, `{{${name}}}`, 'variable'),\n\t\t);\n\t}, []);\n\n\tconst emitChange = useCallback(() => {\n\t\tconst doc = editorRef.current?.getJSON() ?? null;\n\t\t// Single-line: pasted newlines have no meaning in a URL / header value.\n\t\tonChange(editorDocToInstructions(doc, []).replace(/\\n+/g, ''));\n\t}, [onChange]);\n\n\treturn (\n\t\t<FieldBlockCol>\n\t\t\t{label && <TitleSmall color={COLORS.content.primary}>{label}</TitleSmall>}\n\t\t\t<VariableTriggerArea\n\t\t\t\tvariablesData={variablesData}\n\t\t\t\taddDataButton\n\t\t\t\tskipNativeBoundaries\n\t\t\t\tonInsert={(token) => {\n\t\t\t\t\tconst name = nameOf(token);\n\t\t\t\t\teditorRef.current?.insertInlineContent(\n\t\t\t\t\t\tbuildMentionHtml(name, `{{${name}}}`, 'variable'),\n\t\t\t\t\t);\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<InputShell data-test={rest['data-test']}>\n\t\t\t\t\t<BikEditor\n\t\t\t\t\t\tref={editorRef}\n\t\t\t\t\t\tinitialContent={initialHtml}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\tminHeight=\"20px\"\n\t\t\t\t\t\tmaxHeight=\"20px\"\n\t\t\t\t\t\tstyle={{ border: 'none' }}\n\t\t\t\t\t\teditorStyle={INLINE_EDITOR_STYLE}\n\t\t\t\t\t\t// Single-line: Enter is a no-op \"send\".\n\t\t\t\t\t\tonSend={() => undefined}\n\t\t\t\t\t\tsendShortcut={{ key: 'Enter' }}\n\t\t\t\t\t\tmentions={{\n\t\t\t\t\t\t\tagents: mentionItems,\n\t\t\t\t\t\t\tremoveTriggerOnDismiss: true,\n\t\t\t\t\t\t\trenderDropdown: (props) => (\n\t\t\t\t\t\t\t\t<MentionPicker\n\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\t\t\t\t\tonSelectVariable={insertChip}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonChange={emitChange}\n\t\t\t\t\t/>\n\t\t\t\t</InputShell>\n\t\t\t</VariableTriggerArea>\n\t\t</FieldBlockCol>\n\t);\n};\n\nconst INLINE_EDITOR_STYLE: React.CSSProperties = {\n\tpadding: 0,\n\tfontFamily: 'Inter',\n\tfontSize: '14px',\n\tlineHeight: '20px',\n};\n\nconst FieldBlockCol = styled.div`\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\twidth: 100%;\n\t/* In a flex row (header key/value pairs) share the space and allow\n\t shrinking below content width — long values scroll inside the field\n\t instead of widening the row / drawer. */\n\tflex: 1 1 0%;\n\tmin-width: 0;\n`;\n\n// Mirrors the small Input's box (border, radius, padding, focus ring) around\n// the single-line editor.\nconst InputShell = styled.div`\n\t/* No global reset in every host — without this, width:100% + padding\n\t overflows the drawer by the padding + border (the reported overflow). */\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\twidth: 100%;\n\tmin-width: 0;\n\toverflow: hidden;\n\tpadding: 6px 12px;\n\tbackground: ${COLORS.surface.standard};\n\tborder: 1px solid ${COLORS.stroke.primary};\n\tborder-radius: 4px;\n\n\t/* The editor root is width:100% — cap it so a long value scrolls inside\n\t the ProseMirror line rather than stretching the shell. */\n\t> div {\n\t\twidth: 100%;\n\t\tmin-width: 0;\n\t}\n\n\t&:focus-within {\n\t\tborder-color: ${COLORS.stroke.brand};\n\t}\n\n\t/* Keep the editor to one visual line; long values scroll horizontally the\n\t way a native input does. */\n\t.ProseMirror {\n\t\twidth: 100%;\n\t\tmax-width: 100%;\n\t\twhite-space: nowrap;\n\t\toverflow-x: auto;\n\t\toverflow-y: hidden;\n\t\tword-break: normal;\n\t\tscrollbar-width: none;\n\n\t\t&::-webkit-scrollbar {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t.ProseMirror p {\n\t\tdisplay: inline;\n\t}\n`;\n"],"names":["nameOf","token","variableTokenName","VariableInlineField","value","onChange","label","placeholder","variablesData","rest","editorRef","useRef","mentionItems","useMemo","buildVariableMentionItems","initialHtml","instructionsToEditorHtml","insertChip","useCallback","variable","name","buildMentionHtml","emitChange","doc","editorDocToInstructions","FieldBlockCol","TitleSmall","COLORS","jsx","VariableTriggerArea","InputShell","BikEditor","INLINE_EDITOR_STYLE","props","MentionPicker","styled"],"mappings":"mdA+BMA,EAAUC,GACfC,EAAAA,kBAAkBD,CAAK,EAAE,QAAQ,aAAc,EAAE,EAcrCE,EAA0D,CAAC,CACvE,MAAAC,EACA,SAAAC,EACA,MAAAC,EACA,YAAAC,EACA,cAAAC,EACA,GAAGC,CACJ,IAAM,CACL,MAAMC,EAAYC,EAAAA,OAAqB,IAAI,EAErCC,EAAeC,EAAAA,QACpB,IAAMC,EAAAA,0BAA0BN,CAAa,EAC7C,CAACA,CAAa,CAAA,EAKTO,EAAcF,EAAAA,QACnB,IAAMG,EAAAA,yBAAyBZ,EAAOQ,CAAY,EAElD,CAAA,CAAC,EAKIK,EAAaC,cAAaC,GAAyB,CACxD,MAAMC,EAAOlB,EAAAA,kBAAkBiB,EAAS,WAAW,EACnDT,EAAU,SAAS,oBAClBW,EAAAA,iBAAiBD,EAAM,KAAKA,CAAI,KAAM,UAAU,CAAA,CAElD,EAAG,CAAA,CAAE,EAECE,EAAaJ,EAAAA,YAAY,IAAM,CACpC,MAAMK,EAAMb,EAAU,SAAS,QAAA,GAAa,KAE5CL,EAASmB,EAAAA,wBAAwBD,EAAK,CAAA,CAAE,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAC9D,EAAG,CAAClB,CAAQ,CAAC,EAEb,cACEoB,EAAA,CACC,SAAA,CAAAnB,SAAUoB,EAAAA,WAAA,CAAW,MAAOC,SAAO,QAAQ,QAAU,SAAArB,EAAM,EAC5DsB,EAAAA,IAACC,EAAAA,oBAAA,CACA,cAAArB,EACA,cAAa,GACb,qBAAoB,GACpB,SAAWP,GAAU,CACpB,MAAMmB,EAAOpB,EAAOC,CAAK,EACzBS,EAAU,SAAS,oBAClBW,EAAAA,iBAAiBD,EAAM,KAAKA,CAAI,KAAM,UAAU,CAAA,CAElD,EAEA,SAAAQ,EAAAA,IAACE,EAAA,CAAW,YAAWrB,EAAK,WAAW,EACtC,SAAAmB,EAAAA,IAACG,EAAAA,UAAA,CACA,IAAKrB,EACL,eAAgBK,EAChB,YAAAR,EACA,UAAU,OACV,UAAU,OACV,MAAO,CAAE,OAAQ,MAAA,EACjB,YAAayB,EAEb,OAAQ,IAAA,GACR,aAAc,CAAE,IAAK,OAAA,EACrB,SAAU,CACT,OAAQpB,EACR,uBAAwB,GACxB,eAAiBqB,GAChBL,EAAAA,IAACM,EAAAA,cAAA,CACC,GAAGD,EACJ,cAAAzB,EACA,iBAAkBS,CAAA,CAAA,CACnB,EAGF,SAAUK,CAAA,CAAA,CACX,CACD,CAAA,CAAA,CACD,EACD,CAEF,EAEMU,EAA2C,CAChD,QAAS,EACT,WAAY,QACZ,SAAU,OACV,WAAY,MACb,EAEMP,EAAgBU,EAAAA,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcvBL,EAAaK,EAAAA,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAUXR,EAAAA,OAAO,QAAQ,QAAQ;AAAA,qBACjBA,EAAAA,OAAO,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAWxBA,EAAAA,OAAO,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA"}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("react/jsx-runtime"),a=require("react"),E=require("styled-components"),P=require("../utils/mentionSerialization.js"),A=require("./AddDataPill.js"),j=require("./MentionPicker.js"),q=t=>t&&typeof t=="object"&&"default"in t?t:{default:t},R=q(E),T=t=>`{{variable:${P.variableTokenName(t.actualValue)}}}`,k=/[\s,;:/?&=({[<"']/,F=/[,;:/?&=({[<"']/,y=(t,f,i)=>{if(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement){const c=t.selectionStart??t.value.length,d=c===0,p=d?" ":t.value[c-1]??" ";return{ok:d?!i:f.test(p),caret:c,textBefore:t.value.slice(0,c)}}const g=window.getSelection(),l=g?.anchorNode?.textContent??"",r=g?.anchorOffset??0,v=r===0,u=v?" ":l[r-1]??" ";return{ok:v?!i:f.test(u),caret:null,textBefore:l.slice(0,r)}},$=({variablesData:t,onInsert:f,transformToken:i,addDataButton:g,skipNativeBoundaries:l,onOpenChange:r,children:v})=>{const u=a.useRef(null),[c,d]=a.useState(!1),p=a.useRef(null),[o,w]=a.useState(null),m=(t?.length??0)>0,h=e=>{w(e),r?.(!0)},x=()=>{w(null),r?.(!1)},B=()=>{const e=u.current?.getBoundingClientRect();return{above:!!e&&window.innerHeight-e.bottom<340,alignRight:!!e&&window.innerWidth-e.left<360}},S=a.useCallback(e=>{if(!m||e.key!=="@")return;const n=y(e.target,l?F:k,!!l);n.ok&&(e.preventDefault(),e.stopPropagation(),h({caret:n.caret,textBefore:n.textBefore,...B()}))},[m]),D=()=>{const e=y(p.current,k,!1);h({caret:e.caret,textBefore:e.textBefore,...B()})};return a.useEffect(()=>{if(!o)return;const e=s=>{s.key==="Escape"&&(s.stopPropagation(),x())},n=s=>{u.current?.contains(s.target)||x()};return document.addEventListener("keydown",e,!0),document.addEventListener("mousedown",n),()=>{document.removeEventListener("keydown",e,!0),document.removeEventListener("mousedown",n)}},[o]),b.jsxs(C,{ref:u,onKeyDownCapture:S,onFocus:e=>{e.target.closest?.('[data-test="variable-trigger-panel"]')||(p.current=e.target,d(!0))},onBlur:()=>d(!1),children:[v,g&&m&&(c||o)&&b.jsx(A.AddDataPill,{onOpen:D}),o&&b.jsx(L,{$above:o.above,$right:o.alignRight,"data-test":"variable-trigger-panel",onMouseDown:e=>{e.target.closest("input")||e.preventDefault()},children:b.jsx(j.MentionPicker,{items:[],query:"",activeIndex:-1,onSelect:()=>{},onClose:x,variablesData:t,onSelectVariable:e=>{x();const n=T(e),s=i?i(n,{textBefore:o.textBefore}):n;f(s,o.caret)}})})]})},C=R.default.div`
|
|
2
2
|
position: relative;
|
|
3
|
-
`,
|
|
3
|
+
`,L=R.default.div`
|
|
4
4
|
position: absolute;
|
|
5
5
|
z-index: 1000;
|
|
6
6
|
width: 344px;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
box-shadow: 0px -2px 8px 0px #0000001f;
|
|
11
|
-
overflow: hidden;
|
|
12
|
-
${e=>e.$right?"right: 0;":"left: 0;"}
|
|
13
|
-
${e=>e.$above?"bottom: calc(100% + 4px);":"top: calc(100% + 4px);"}
|
|
14
|
-
`;exports.VariableTriggerArea=$;exports.variableToken=y;
|
|
7
|
+
${t=>t.$right?"right: 0;":"left: 0;"}
|
|
8
|
+
${t=>t.$above?"bottom: calc(100% + 4px);":"top: calc(100% + 4px);"}
|
|
9
|
+
`;exports.VariableTriggerArea=$;exports.variableToken=T;
|
|
15
10
|
//# 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 { 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","e","ctx","openFromButton","useEffect","onKey","onDown","jsxs","TriggerWrap","jsx","AddDataPill","FloaterBox","VariablePicker","PickerType","raw","token","styled","COLORS","p"],"mappings":"kcAaaA,EAAiBC,GAC7B,cAAcC,EAAAA,kBAAkBD,EAAS,WAAW,CAAC,KAUhDE,EAAW,yBAEXC,EACLC,GAC+D,CAC/D,GACCA,aAAkB,kBAClBA,aAAkB,oBACjB,CACD,MAAMC,EAAQD,EAAO,gBAAkBA,EAAO,MAAM,OAC9CE,EAAOD,IAAU,EAAI,IAAMD,EAAO,MAAMC,EAAQ,CAAC,GAAK,IAC5D,MAAO,CACN,GAAIH,EAAS,KAAKI,CAAI,EACtB,MAAAD,EACA,WAAYD,EAAO,MAAM,MAAM,EAAGC,CAAK,CAAA,CAEzC,CAIA,MAAME,EAAM,OAAO,aAAA,EACbC,EAAOD,GAAK,YAAY,aAAe,GACvCE,EAASF,GAAK,cAAgB,EAC9BD,EAAOG,IAAW,EAAI,IAAMD,EAAKC,EAAS,CAAC,GAAK,IACtD,MAAO,CACN,GAAIP,EAAS,KAAKI,CAAI,EACtB,MAAO,KACP,WAAYE,EAAK,MAAM,EAAGC,CAAM,CAAA,CAElC,EAqCaC,EAA0D,CAAC,CACvE,cAAAC,EACA,SAAAC,EACA,eAAAC,EACA,cAAAC,EACA,SAAAC,CACD,IAAM,CACL,MAAMC,EAAeC,EAAAA,OAAuB,IAAI,EAG1C,CAACC,EAAcC,CAAe,EAAIC,EAAAA,SAAS,EAAK,EAChDC,EAAiBJ,EAAAA,OAA2B,IAAI,EAKhD,CAACK,EAASC,CAAU,EAAIH,EAAAA,SAKpB,IAAI,EAERI,GAAWb,GAAe,QAAU,GAAK,EAKzCc,EAAY,IAAM,CACvB,MAAMC,EAAOV,EAAa,SAAS,sBAAA,EACnC,MAAO,CACN,MAAO,CAAC,CAACU,GAAQ,OAAO,YAAcA,EAAK,OAAS,IACpD,WAAY,CAAC,CAACA,GAAQ,OAAO,WAAaA,EAAK,KAAO,GAAA,CAExD,EAEMC,EAAmBC,EAAAA,YACvBC,GAA2B,CAC3B,GAAI,CAACL,GAAWK,EAAE,MAAQ,IACzB,OAED,MAAMC,EAAM3B,EAAe0B,EAAE,MAAM,EAC9BC,EAAI,KAITD,EAAE,eAAA,EACFA,EAAE,gBAAA,EACFN,EAAW,CACV,MAAOO,EAAI,MACX,WAAYA,EAAI,WAChB,GAAGL,EAAA,CAAU,CACb,EACF,EAEA,CAACD,CAAO,CAAA,EAKHO,EAAiB,IAAM,CAC5B,MAAMD,EAAM3B,EAAekB,EAAe,OAAO,EACjDE,EAAW,CACV,MAAOO,EAAI,MACX,WAAYA,EAAI,WAChB,GAAGL,EAAA,CAAU,CACb,CACF,EAGAO,OAAAA,EAAAA,UAAU,IAAM,CACf,GAAI,CAACV,EACJ,OAED,MAAMW,EAASJ,GAAqB,CAC/BA,EAAE,MAAQ,WACbA,EAAE,gBAAA,EACFN,EAAW,IAAI,EAEjB,EACMW,EAAUL,GAAkB,CAC5Bb,EAAa,SAAS,SAASa,EAAE,MAAc,GACnDN,EAAW,IAAI,CAEjB,EAGA,gBAAS,iBAAiB,UAAWU,EAAO,EAAI,EAChD,SAAS,iBAAiB,YAAaC,CAAM,EACtC,IAAM,CACZ,SAAS,oBAAoB,UAAWD,EAAO,EAAI,EACnD,SAAS,oBAAoB,YAAaC,CAAM,CACjD,CACD,EAAG,CAACZ,CAAO,CAAC,EAGXa,EAAAA,KAACC,EAAA,CACA,IAAKpB,EACL,iBAAAW,EAGA,QAAUE,GAAM,CAIbA,EAAE,OAAuB,UACzB,sCAAA,IAKFR,EAAe,QAAUQ,EAAE,OAC3BV,EAAgB,EAAI,EACrB,EACA,OAAQ,IAAMA,EAAgB,EAAK,EAElC,SAAA,CAAAJ,EACAD,GAAiBU,IAAYN,GAAgBI,IAC7Ce,EAAAA,IAACC,cAAA,CAAY,OAAQP,EAAgB,EAErCT,GACAe,EAAAA,IAACE,EAAA,CACA,OAAQjB,EAAQ,MAChB,OAAQA,EAAQ,WAChB,YAAU,yBAEV,SAAAe,EAAAA,IAACG,EAAAA,eAAA,CACA,KAAMC,EAAAA,WAAW,QACjB,KAAM9B,GAAiB,CAAA,EACvB,mBAAoB,GACpB,QAAS,IAAMY,EAAW,IAAI,EAC9B,SAAWvB,GAAyB,CACnCuB,EAAW,IAAI,EACf,MAAMmB,EAAM3C,EAAcC,CAAQ,EAC5B2C,EAAQ9B,EACXA,EAAe6B,EAAK,CAAE,WAAYpB,EAAQ,UAAA,CAAY,EACtDoB,EACH9B,EAAS+B,EAAOrB,EAAQ,KAAK,CAC9B,CAAA,CAAA,CACD,CAAA,CACD,CAAA,CAAA,CAIJ,EAEMc,EAAcQ,EAAAA,QAAO;AAAA;AAAA,EAQrBL,EAAaK,EAAAA,QAAO;AAAA;AAAA;AAAA;AAAA,eAIXC,EAAAA,OAAO,QAAQ,QAAQ;AAAA,qBACjBA,EAAAA,OAAO,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA,GAItCC,GAAOA,EAAE,OAAS,YAAc,UAAW;AAAA,GAC3CA,GAAOA,EAAE,OAAS,4BAA8B,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 type {\n\tVariableListInterfaceV3,\n\tVariableV3,\n} from '../../variable-picker-v3/model';\nimport { variableTokenName } from '../utils/mentionSerialization';\nimport { AddDataPill } from './AddDataPill';\nimport { MentionPicker } from './MentionPicker';\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 */\n// Opening delimiters trigger; closing ones (`}` `)` `]` `>`) deliberately do\n// NOT — typing a literal `@` right after an inserted `{{token}}` (e.g.\n// `{{store_name}}@acme.com`) must stay literal.\nconst BOUNDARY = /[\\s,;:/?&=({[<\"']/;\n// The subset the editor's own `@` mention trigger does NOT handle (it only\n// fires after whitespace / at the start) — used by `skipNativeBoundaries`\n// fields so the two triggers never fire for the same keystroke.\nconst NON_NATIVE_BOUNDARY = /[,;:/?&=({[<\"']/;\n\nconst triggerContext = (\n\ttarget: EventTarget | null,\n\tboundary: RegExp,\n\tskipStart: boolean,\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 atStart = caret === 0;\n\t\tconst prev = atStart ? ' ' : target.value[caret - 1] ?? ' ';\n\t\treturn {\n\t\t\tok: atStart ? !skipStart : 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 atStart = offset === 0;\n\tconst prev = atStart ? ' ' : text[offset - 1] ?? ' ';\n\treturn {\n\t\tok: atStart ? !skipStart : 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\t/**\n\t * The wrapped field is a BikEditor whose OWN `@` mention trigger already\n\t * fires after whitespace / at the start — this wrapper then only handles\n\t * the delimiter boundaries the native trigger misses (`https://@`, `\"@`…),\n\t * so the two never open a panel for the same keystroke.\n\t */\n\tskipNativeBoundaries?: boolean;\n\t/**\n\t * Observes the panel's open state. Fields that commit on blur (the email\n\t * chips entry) use this to hold off while focus is bouncing through the\n\t * panel — blur-time focus targets are unreliable mid-transition.\n\t */\n\tonOpenChange?: (open: boolean) => 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\ttransformToken,\n\taddDataButton,\n\tskipNativeBoundaries,\n\tonOpenChange,\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\tconst openPanel = (t: NonNullable<typeof trigger>) => {\n\t\tsetTrigger(t);\n\t\tonOpenChange?.(true);\n\t};\n\tconst closePanel = () => {\n\t\tsetTrigger(null);\n\t\tonOpenChange?.(false);\n\t};\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(\n\t\t\t\te.target,\n\t\t\t\tskipNativeBoundaries ? NON_NATIVE_BOUNDARY : BOUNDARY,\n\t\t\t\t!!skipNativeBoundaries,\n\t\t\t);\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\topenPanel({\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, BOUNDARY, false);\n\t\topenPanel({\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\tclosePanel();\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\tclosePanel();\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\t// Keep the field focused while clicking rows (a blur mid-pick\n\t\t\t\t\t// would e.g. commit a half-typed email chip). The panel's own\n\t\t\t\t\t// inputs (search) still take focus normally.\n\t\t\t\t\tonMouseDown={(e) => {\n\t\t\t\t\t\tconst t = e.target as HTMLElement;\n\t\t\t\t\t\tif (!t.closest('input')) {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{/* The EXACT panel the Instructions / Guardrails @ opens: with no\n\t\t\t\t\t tools / sub-agents, Variables is its sole category, so it\n\t\t\t\t\t renders the \"Variables\" header + variable list directly. */}\n\t\t\t\t\t<MentionPicker\n\t\t\t\t\t\titems={[]}\n\t\t\t\t\t\tquery=\"\"\n\t\t\t\t\t\tactiveIndex={-1}\n\t\t\t\t\t\tonSelect={() => undefined}\n\t\t\t\t\t\tonClose={closePanel}\n\t\t\t\t\t\tvariablesData={variablesData}\n\t\t\t\t\t\tonSelectVariable={(variable: VariableV3) => {\n\t\t\t\t\t\t\tclosePanel();\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 MentionPicker inside brings\n// its own card chrome (same as the Instructions / Guardrails @ menu). High\n// 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\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","NON_NATIVE_BOUNDARY","triggerContext","target","boundary","skipStart","caret","atStart","prev","sel","text","offset","VariableTriggerArea","variablesData","onInsert","transformToken","addDataButton","skipNativeBoundaries","onOpenChange","children","containerRef","useRef","fieldFocused","setFieldFocused","useState","lastFocusedRef","trigger","setTrigger","enabled","openPanel","t","closePanel","placement","rect","onKeyDownCapture","useCallback","ctx","openFromButton","useEffect","onKey","e","onDown","jsxs","TriggerWrap","jsx","AddDataPill","FloaterBox","MentionPicker","raw","token","styled","p"],"mappings":"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"}
|