@bikdotai/bik-component-library 0.0.875-beta.1 → 0.0.875-beta.2
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 +6 -6
- 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/RestApiToolPanel.js +4 -4
- package/dist/cjs/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.d.ts +6 -0
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js +124 -122
- package/dist/esm/components/agent-builder/components/ConfigToolPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/CreateSubAgentPanel.d.ts +6 -0
- package/dist/esm/components/agent-builder/components/CreateSubAgentPanel.js +83 -81
- package/dist/esm/components/agent-builder/components/CreateSubAgentPanel.js.map +1 -1
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.d.ts +6 -0
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js +50 -48
- package/dist/esm/components/agent-builder/components/RestApiToolPanel.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 } 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 { BodyCaption, 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/**\n\t * Portal target for the drawer, passed straight to {@link SideModal}\n\t * (default `document.body`). Lets a host render it into its own positioned\n\t * slot rather than as a viewport overlay.\n\t */\n\telement?: Element | DocumentFragment;\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// A callable endpoint needs an explicit http(s):// scheme AND a real, dotted\n// host. `new URL` alone is too lenient — it accepts single-label hosts\n// (\"https://abc\"), a one-slash scheme (\"https:/abc.com\"), and dangling-dot\n// hosts (\"https://.com\" / \"https://abc.\"). Require the `://` prefix on the raw\n// value plus a hostname that is either dotted (example.com) or an IPv4.\nconst isValidHttpUrl = (value: string): boolean => {\n\tif (!/^https?:\\/\\//i.test(value)) {\n\t\treturn false;\n\t}\n\tlet host: string;\n\ttry {\n\t\thost = new URL(value).hostname;\n\t} catch {\n\t\treturn false;\n\t}\n\treturn host.includes('.') && !host.startsWith('.') && !host.endsWith('.');\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\telement,\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\t// Stable per-row identity for React keys, parallel to `headers`. The value\n\t// field is an uncontrolled editor, so a row must keep its mount across\n\t// sibling add/remove — index-based keys would remount every row (dropping\n\t// focus mid-edit) or let a deleted row's text linger in a reused neighbour.\n\tconst headerIdsRef = useRef<number[]>(\n\t\t(initial?.headers ?? [{ key: '', value: '' }]).map(\n\t\t\t(_h: RestApiHeader, i: number) => i,\n\t\t),\n\t);\n\tconst nextHeaderIdRef = useRef(headerIdsRef.current.length);\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\theaderIdsRef.current = [...headerIdsRef.current, nextHeaderIdRef.current];\n\t\tnextHeaderIdRef.current += 1;\n\t\tsetHeaders((prev) => [...prev, { key: '', value: '' }]);\n\t};\n\tconst removeHeader = (i: number) => {\n\t\theaderIdsRef.current = headerIdsRef.current.filter((_, idx) => idx !== i);\n\t\tsetHeaders((prev) => prev.filter((_, idx) => idx !== i));\n\t};\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 urlTrimmed = url.trim();\n\t// Save needs a real endpoint, but don't flash the error while the field still\n\t// holds its pristine \"https://\" seed — only once the user has typed past it.\n\t// A URL that interpolates variables can't be parsed literally (braces are\n\t// invalid URL characters) — accept it once a scheme + something follows;\n\t// it only becomes a concrete URL after runtime substitution.\n\tconst urlUsesVariables = urlTrimmed.includes('{{variable:');\n\tconst urlOk = urlUsesVariables\n\t\t? /^https?:\\/\\/./.test(urlTrimmed)\n\t\t: isValidHttpUrl(urlTrimmed);\n\tconst isUrlInvalid =\n\t\turlTrimmed.length > 0 && urlTrimmed !== 'https://' && !urlOk;\n\tconst canSave =\n\t\tnameTrimmed.length > 0 &&\n\t\turlOk &&\n\t\t!isDuplicateName &&\n\t\t// The editor's characterLimit is soft (a paste may exceed it, rendered\n\t\t// grey) — gate the save so over-limit instructions can't be persisted.\n\t\tinstructions.length <= charLimits.instructions;\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\telement={element}\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\t{/* VariableInlineField has no invalid state of its own — surface the\n\t\t\t\t URL validation (from main) as a caption below the field. */}\n\t\t\t\t{isUrlInvalid && (\n\t\t\t\t\t<BodyCaption color={COLORS.content.negative}>\n\t\t\t\t\t\tEnter a valid URL (including https://)\n\t\t\t\t\t</BodyCaption>\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<HeaderRow\n\t\t\t\t\t\tkey={headerIdsRef.current[i]}\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","isValidHttpUrl","value","host","RestApiToolPanel","onClose","onSave","initial","onImprove","charLimits","AGENT_CHAR_LIMITS","zIndex","existingNames","variablesData","element","name","setName","useState","instructions","setInstructions","url","setUrl","method","setMethod","headers","setHeaders","requestBody","setRequestBody","responseSample","setResponseSample","saving","setSaving","headerIdsRef","useRef","_h","i","nextHeaderIdRef","patchHeader","patch","prev","idx","addHeader","removeHeader","_","nameTrimmed","isDuplicateName","n","urlTrimmed","urlOk","isUrlInvalid","canSave","handleSave","config","h","result","jsxs","SideModal","jsx","DrawerHeaderText","DRAWER_HEADER_STYLE","DRAWER_BODY_STYLE","Input","ToolInstructionsField","FieldBlock","VariableInlineField","BodyCaption","COLORS","TitleSmall","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, { 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 { BodyCaption, 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/**\n\t * Portal target for the drawer, passed straight to {@link SideModal}\n\t * (default `document.body`). Lets a host render it into its own positioned\n\t * slot rather than as a viewport overlay.\n\t */\n\telement?: Element | DocumentFragment;\n\t/**\n\t * Footer button order, passed straight to {@link SideModal} (default\n\t * `'row'`). Hosts that show this drawer beside their own footer can set\n\t * `'row-reverse'` to line the Cancel / Save order up with it.\n\t */\n\tfooterDirection?: 'row' | 'row-reverse';\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// A callable endpoint needs an explicit http(s):// scheme AND a real, dotted\n// host. `new URL` alone is too lenient — it accepts single-label hosts\n// (\"https://abc\"), a one-slash scheme (\"https:/abc.com\"), and dangling-dot\n// hosts (\"https://.com\" / \"https://abc.\"). Require the `://` prefix on the raw\n// value plus a hostname that is either dotted (example.com) or an IPv4.\nconst isValidHttpUrl = (value: string): boolean => {\n\tif (!/^https?:\\/\\//i.test(value)) {\n\t\treturn false;\n\t}\n\tlet host: string;\n\ttry {\n\t\thost = new URL(value).hostname;\n\t} catch {\n\t\treturn false;\n\t}\n\treturn host.includes('.') && !host.startsWith('.') && !host.endsWith('.');\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\telement,\n\tfooterDirection,\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\t// Stable per-row identity for React keys, parallel to `headers`. The value\n\t// field is an uncontrolled editor, so a row must keep its mount across\n\t// sibling add/remove — index-based keys would remount every row (dropping\n\t// focus mid-edit) or let a deleted row's text linger in a reused neighbour.\n\tconst headerIdsRef = useRef<number[]>(\n\t\t(initial?.headers ?? [{ key: '', value: '' }]).map(\n\t\t\t(_h: RestApiHeader, i: number) => i,\n\t\t),\n\t);\n\tconst nextHeaderIdRef = useRef(headerIdsRef.current.length);\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\theaderIdsRef.current = [...headerIdsRef.current, nextHeaderIdRef.current];\n\t\tnextHeaderIdRef.current += 1;\n\t\tsetHeaders((prev) => [...prev, { key: '', value: '' }]);\n\t};\n\tconst removeHeader = (i: number) => {\n\t\theaderIdsRef.current = headerIdsRef.current.filter((_, idx) => idx !== i);\n\t\tsetHeaders((prev) => prev.filter((_, idx) => idx !== i));\n\t};\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 urlTrimmed = url.trim();\n\t// Save needs a real endpoint, but don't flash the error while the field still\n\t// holds its pristine \"https://\" seed — only once the user has typed past it.\n\t// A URL that interpolates variables can't be parsed literally (braces are\n\t// invalid URL characters) — accept it once a scheme + something follows;\n\t// it only becomes a concrete URL after runtime substitution.\n\tconst urlUsesVariables = urlTrimmed.includes('{{variable:');\n\tconst urlOk = urlUsesVariables\n\t\t? /^https?:\\/\\/./.test(urlTrimmed)\n\t\t: isValidHttpUrl(urlTrimmed);\n\tconst isUrlInvalid =\n\t\turlTrimmed.length > 0 && urlTrimmed !== 'https://' && !urlOk;\n\tconst canSave =\n\t\tnameTrimmed.length > 0 &&\n\t\turlOk &&\n\t\t!isDuplicateName &&\n\t\t// The editor's characterLimit is soft (a paste may exceed it, rendered\n\t\t// grey) — gate the save so over-limit instructions can't be persisted.\n\t\tinstructions.length <= charLimits.instructions;\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\telement={element}\n\t\t\tfooterDirection={footerDirection}\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\t{/* VariableInlineField has no invalid state of its own — surface the\n\t\t\t\t URL validation (from main) as a caption below the field. */}\n\t\t\t\t{isUrlInvalid && (\n\t\t\t\t\t<BodyCaption color={COLORS.content.negative}>\n\t\t\t\t\t\tEnter a valid URL (including https://)\n\t\t\t\t\t</BodyCaption>\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<HeaderRow\n\t\t\t\t\t\tkey={headerIdsRef.current[i]}\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","isValidHttpUrl","value","host","RestApiToolPanel","onClose","onSave","initial","onImprove","charLimits","AGENT_CHAR_LIMITS","zIndex","existingNames","variablesData","element","footerDirection","name","setName","useState","instructions","setInstructions","url","setUrl","method","setMethod","headers","setHeaders","requestBody","setRequestBody","responseSample","setResponseSample","saving","setSaving","headerIdsRef","useRef","_h","i","nextHeaderIdRef","patchHeader","patch","prev","idx","addHeader","removeHeader","_","nameTrimmed","isDuplicateName","n","urlTrimmed","urlOk","isUrlInvalid","canSave","handleSave","config","h","result","jsxs","SideModal","jsx","DrawerHeaderText","DRAWER_HEADER_STYLE","DRAWER_BODY_STYLE","Input","ToolInstructionsField","FieldBlock","VariableInlineField","BodyCaption","COLORS","TitleSmall","Dropdown","opt","v","unwrapDropdownValue","HeaderRow","IconBtn","DeleteIcon","AddHeaderRow","Button","PlusIcon","BikEditor","JsonFieldBox","c","styled"],"mappings":"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"}
|
|
@@ -32,6 +32,12 @@ interface ConfigToolPanelProps {
|
|
|
32
32
|
* slot rather than as a viewport overlay.
|
|
33
33
|
*/
|
|
34
34
|
element?: Element | DocumentFragment;
|
|
35
|
+
/**
|
|
36
|
+
* Footer button order, passed straight to {@link SideModal} (default
|
|
37
|
+
* `'row'`). Hosts that show this drawer beside their own footer can set
|
|
38
|
+
* `'row-reverse'` to line the Cancel / Save order up with it.
|
|
39
|
+
*/
|
|
40
|
+
footerDirection?: 'row' | 'row-reverse';
|
|
35
41
|
/** SideModal stacking order — raise it when opened over another drawer. */
|
|
36
42
|
zIndex?: number;
|
|
37
43
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { jsx as o, jsxs as E } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import j from "../../../assets/icons/cross.svg.js";
|
|
3
|
+
import P, { useState as I, useRef as v } from "react";
|
|
4
|
+
import w from "styled-components";
|
|
5
5
|
import { TitleSmall as O, BodyCaption as F } from "../../TypographyStyle.js";
|
|
6
6
|
import { COLORS as c } from "../../../constants/Theme.js";
|
|
7
|
-
import { FieldBlock as
|
|
8
|
-
import { AGENT_CHAR_LIMITS as
|
|
9
|
-
import { DRAWER_BODY_STYLE as
|
|
10
|
-
import { ToolInstructionsField as
|
|
7
|
+
import { FieldBlock as G } from "../AgentBuilder.styled.js";
|
|
8
|
+
import { AGENT_CHAR_LIMITS as V } from "../constants/charLimits.js";
|
|
9
|
+
import { DRAWER_BODY_STYLE as z, DRAWER_HEADER_STYLE as M, DrawerHeaderText as q } from "./DrawerHeaderText.js";
|
|
10
|
+
import { ToolInstructionsField as H } from "./ToolInstructionsField.js";
|
|
11
11
|
import { VariableTriggerArea as L } from "./VariableTriggerArea.js";
|
|
12
|
-
import { SideModal as
|
|
13
|
-
import { Switch as
|
|
14
|
-
import { Input as
|
|
15
|
-
import { Tag as
|
|
16
|
-
const
|
|
12
|
+
import { SideModal as W } from "../../side-modal/SideModal.js";
|
|
13
|
+
import { Switch as N } from "../../switch/Switch.js";
|
|
14
|
+
import { Input as J } from "../../input/Input.js";
|
|
15
|
+
import { Tag as K } from "../../tag/Tag.js";
|
|
16
|
+
const Q = {
|
|
17
17
|
send_email: {
|
|
18
18
|
title: "Send email",
|
|
19
19
|
subtitle: "Notify someone by email when this agent can't continue. You pick the recipient, the AI drafts the message."
|
|
@@ -30,235 +30,237 @@ const K = {
|
|
|
30
30
|
title: "Add row to sheet",
|
|
31
31
|
subtitle: "Append a row to a Google Sheet. You pick the sheet and its columns, the AI fills them in from the conversation."
|
|
32
32
|
}
|
|
33
|
-
},
|
|
33
|
+
}, U = ({
|
|
34
34
|
chips: n,
|
|
35
35
|
onChange: i,
|
|
36
|
-
placeholder:
|
|
36
|
+
placeholder: h,
|
|
37
37
|
nativeID: u,
|
|
38
|
-
hint:
|
|
39
|
-
variablesData:
|
|
38
|
+
hint: T,
|
|
39
|
+
variablesData: C
|
|
40
40
|
}) => {
|
|
41
|
-
const [l,
|
|
42
|
-
const t = l.split(",").map((
|
|
43
|
-
t.length && i([...n, ...t.filter((
|
|
44
|
-
}, f =
|
|
45
|
-
f.current =
|
|
46
|
-
const
|
|
47
|
-
return /* @__PURE__ */ E(
|
|
41
|
+
const [l, g] = I(""), [R, _] = I(!1), x = v(null), s = v(null), S = () => {
|
|
42
|
+
const t = l.split(",").map((r) => r.trim()).filter(Boolean);
|
|
43
|
+
t.length && i([...n, ...t.filter((r) => !n.includes(r))]), g("");
|
|
44
|
+
}, f = v(S);
|
|
45
|
+
f.current = S;
|
|
46
|
+
const b = v(!1), y = v(!1);
|
|
47
|
+
return /* @__PURE__ */ E(ee, { ref: x, children: [
|
|
48
48
|
/* @__PURE__ */ o(
|
|
49
49
|
L,
|
|
50
50
|
{
|
|
51
51
|
addDataButton: !0,
|
|
52
|
-
variablesData:
|
|
52
|
+
variablesData: C,
|
|
53
53
|
onOpenChange: (t) => {
|
|
54
|
-
|
|
55
|
-
!
|
|
54
|
+
b.current = t, !t && setTimeout(() => {
|
|
55
|
+
!y.current && !x.current?.contains(document.activeElement) && f.current(), y.current = !1;
|
|
56
56
|
}, 0);
|
|
57
57
|
},
|
|
58
|
-
onInsert: (t,
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
58
|
+
onInsert: (t, r) => {
|
|
59
|
+
y.current = !0;
|
|
60
|
+
const m = r ?? l.length;
|
|
61
|
+
g(`${l.slice(0, m)}${t}${l.slice(m)}`), requestAnimationFrame(() => {
|
|
62
|
+
const k = s.current;
|
|
63
|
+
k?.focus(), k?.setSelectionRange(m + t.length, m + t.length);
|
|
64
64
|
});
|
|
65
65
|
},
|
|
66
66
|
children: /* @__PURE__ */ o(
|
|
67
|
-
|
|
67
|
+
te,
|
|
68
68
|
{
|
|
69
69
|
id: u,
|
|
70
70
|
name: u,
|
|
71
|
-
ref:
|
|
71
|
+
ref: s,
|
|
72
72
|
type: "text",
|
|
73
73
|
autoComplete: "off",
|
|
74
|
-
placeholder: R && l === "" && (
|
|
74
|
+
placeholder: R && l === "" && (C?.length ?? 0) > 0 ? "Enter text, type @ to add variable" : h,
|
|
75
75
|
value: l,
|
|
76
|
-
onChange: (t) =>
|
|
77
|
-
onFocus: () =>
|
|
76
|
+
onChange: (t) => g(t.target.value),
|
|
77
|
+
onFocus: () => _(!0),
|
|
78
78
|
onBlur: () => {
|
|
79
|
-
|
|
80
|
-
!
|
|
79
|
+
_(!1), setTimeout(() => {
|
|
80
|
+
!b.current && !x.current?.contains(document.activeElement) && f.current();
|
|
81
81
|
}, 0);
|
|
82
82
|
},
|
|
83
83
|
onKeyDown: (t) => {
|
|
84
|
-
t.key === "Enter" || t.key === "," ? (t.preventDefault(),
|
|
84
|
+
t.key === "Enter" || t.key === "," ? (t.preventDefault(), S()) : t.key === "Backspace" && l === "" && n.length > 0 && i(n.slice(0, -1));
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
)
|
|
88
88
|
}
|
|
89
89
|
),
|
|
90
|
-
|
|
91
|
-
n.length > 0 && /* @__PURE__ */ o(
|
|
92
|
-
|
|
90
|
+
T,
|
|
91
|
+
n.length > 0 && /* @__PURE__ */ o(ne, { children: n.map((t, r) => /* @__PURE__ */ o(
|
|
92
|
+
K,
|
|
93
93
|
{
|
|
94
94
|
tagText: t.replace(/\{\{variable:([^}]+)\}\}/g, "{{$1}}"),
|
|
95
95
|
theme: "light",
|
|
96
96
|
type: "neutral",
|
|
97
97
|
variant: "regular",
|
|
98
|
-
TrailingIcon:
|
|
99
|
-
onTrailingIconClicked: () => i(n.filter((
|
|
98
|
+
TrailingIcon: j,
|
|
99
|
+
onTrailingIconClicked: () => i(n.filter((m, k) => k !== r))
|
|
100
100
|
},
|
|
101
|
-
`${t}-${
|
|
101
|
+
`${t}-${r}`
|
|
102
102
|
)) })
|
|
103
103
|
] });
|
|
104
|
-
},
|
|
105
|
-
const
|
|
104
|
+
}, X = (n, i) => {
|
|
105
|
+
const h = {};
|
|
106
106
|
return Object.keys(n).forEach((u) => {
|
|
107
|
-
|
|
108
|
-
}),
|
|
109
|
-
},
|
|
107
|
+
h[u] = i?.params[u]?.value ?? n[u].value;
|
|
108
|
+
}), h;
|
|
109
|
+
}, Z = (n) => String(n ?? "").split(",").map((i) => i.trim()).filter(Boolean), Se = ({
|
|
110
110
|
toolKey: n,
|
|
111
111
|
template: i,
|
|
112
|
-
onClose:
|
|
112
|
+
onClose: h,
|
|
113
113
|
onSave: u,
|
|
114
|
-
initial:
|
|
115
|
-
onImprove:
|
|
116
|
-
charLimits: l =
|
|
117
|
-
variablesData:
|
|
114
|
+
initial: T,
|
|
115
|
+
onImprove: C,
|
|
116
|
+
charLimits: l = V,
|
|
117
|
+
variablesData: g,
|
|
118
118
|
zIndex: R,
|
|
119
|
-
element:
|
|
119
|
+
element: _,
|
|
120
|
+
footerDirection: x
|
|
120
121
|
}) => {
|
|
121
|
-
const [
|
|
122
|
-
() =>
|
|
123
|
-
), [
|
|
124
|
-
if (!
|
|
122
|
+
const [s, S] = I(
|
|
123
|
+
() => X(i, T)
|
|
124
|
+
), [f, b] = I(!1), y = Object.values(i), t = n === "send_email", r = (e, p) => S((d) => ({ ...d, [e]: p })), m = y.every((e) => !(e.required && String(s[e.key] ?? "").trim() === "" || e.component === "MultilineInput" && String(s[e.key] ?? "").length > l.instructions)), k = async () => {
|
|
125
|
+
if (!m || f)
|
|
125
126
|
return;
|
|
126
127
|
const e = {};
|
|
127
|
-
|
|
128
|
-
e[
|
|
128
|
+
y.forEach((a) => {
|
|
129
|
+
e[a.key] = { ...a, value: s[a.key] };
|
|
129
130
|
});
|
|
130
|
-
const
|
|
131
|
-
id:
|
|
131
|
+
const p = {
|
|
132
|
+
id: T?.id ?? `${n}_${Date.now().toString(36)}`,
|
|
132
133
|
tool_key: n,
|
|
133
134
|
params: e
|
|
134
|
-
},
|
|
135
|
-
if (
|
|
136
|
-
|
|
135
|
+
}, d = u(p);
|
|
136
|
+
if (d && typeof d.then == "function") {
|
|
137
|
+
b(!0);
|
|
137
138
|
try {
|
|
138
|
-
await
|
|
139
|
+
await d;
|
|
139
140
|
} finally {
|
|
140
|
-
|
|
141
|
+
b(!1);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
|
-
},
|
|
144
|
-
const
|
|
144
|
+
}, Y = (e) => {
|
|
145
|
+
const p = `config-tool-${e.key.toLowerCase()}`;
|
|
145
146
|
switch (e.component) {
|
|
146
147
|
// Multi-value email chips — only for the email tool. Slack's KeywordInput
|
|
147
148
|
// (channelId) falls through to the plain input below.
|
|
148
149
|
case "KeywordInput":
|
|
149
|
-
if (!
|
|
150
|
+
if (!t)
|
|
150
151
|
break;
|
|
151
|
-
return /* @__PURE__ */ E(
|
|
152
|
+
return /* @__PURE__ */ E(G, { children: [
|
|
152
153
|
/* @__PURE__ */ o(O, { color: c.content.primary, children: e.title }),
|
|
153
154
|
/* @__PURE__ */ o(
|
|
154
|
-
|
|
155
|
+
U,
|
|
155
156
|
{
|
|
156
|
-
nativeID:
|
|
157
|
+
nativeID: p,
|
|
157
158
|
placeholder: e.hint ?? "",
|
|
158
|
-
chips:
|
|
159
|
-
onChange: (
|
|
160
|
-
variablesData:
|
|
159
|
+
chips: Z(s[e.key]),
|
|
160
|
+
onChange: (a) => r(e.key, a.join(",")),
|
|
161
|
+
variablesData: g,
|
|
161
162
|
hint: /* @__PURE__ */ o(F, { color: c.content.secondary, children: "Press Enter or comma to add multiple emails" })
|
|
162
163
|
}
|
|
163
164
|
)
|
|
164
165
|
] }, e.key);
|
|
165
166
|
case "MultilineInput":
|
|
166
167
|
return /* @__PURE__ */ o(
|
|
167
|
-
|
|
168
|
+
H,
|
|
168
169
|
{
|
|
169
|
-
"data-test":
|
|
170
|
+
"data-test": p,
|
|
170
171
|
label: e.title,
|
|
171
|
-
value: String(
|
|
172
|
-
onChange: (
|
|
172
|
+
value: String(s[e.key] ?? ""),
|
|
173
|
+
onChange: (a) => r(e.key, a),
|
|
173
174
|
maxLength: l.instructions,
|
|
174
175
|
placeholder: e.hint ?? "",
|
|
175
|
-
onImprove:
|
|
176
|
-
variablesData:
|
|
176
|
+
onImprove: C,
|
|
177
|
+
variablesData: g
|
|
177
178
|
},
|
|
178
179
|
e.key
|
|
179
180
|
);
|
|
180
181
|
case "Switch":
|
|
181
|
-
return /* @__PURE__ */ E(
|
|
182
|
-
/* @__PURE__ */ E(
|
|
182
|
+
return /* @__PURE__ */ E(oe, { children: [
|
|
183
|
+
/* @__PURE__ */ E(re, { children: [
|
|
183
184
|
/* @__PURE__ */ o(O, { color: c.content.primary, children: e.title }),
|
|
184
185
|
e.hint && /* @__PURE__ */ o(F, { color: c.content.secondary, children: e.hint })
|
|
185
186
|
] }),
|
|
186
187
|
/* @__PURE__ */ o(
|
|
187
|
-
|
|
188
|
+
N,
|
|
188
189
|
{
|
|
189
|
-
"data-test":
|
|
190
|
-
value: !!
|
|
191
|
-
onValueChange: async (
|
|
190
|
+
"data-test": p,
|
|
191
|
+
value: !!s[e.key],
|
|
192
|
+
onValueChange: async (a) => r(e.key, a)
|
|
192
193
|
}
|
|
193
194
|
)
|
|
194
195
|
] }, e.key);
|
|
195
196
|
}
|
|
196
|
-
const
|
|
197
|
-
|
|
197
|
+
const d = /* @__PURE__ */ o(
|
|
198
|
+
J,
|
|
198
199
|
{
|
|
199
|
-
"data-test":
|
|
200
|
+
"data-test": p,
|
|
200
201
|
variant: "small",
|
|
201
202
|
labelText: e.title,
|
|
202
203
|
labelTextBold: !0,
|
|
203
204
|
placeholder: e.hint ?? "",
|
|
204
|
-
value: String(
|
|
205
|
-
onChangeText: (
|
|
205
|
+
value: String(s[e.key] ?? ""),
|
|
206
|
+
onChangeText: (a) => r(e.key, a)
|
|
206
207
|
}
|
|
207
208
|
);
|
|
208
|
-
return
|
|
209
|
+
return t ? /* @__PURE__ */ o(
|
|
209
210
|
L,
|
|
210
211
|
{
|
|
211
212
|
addDataButton: !0,
|
|
212
|
-
variablesData:
|
|
213
|
-
onInsert: (
|
|
214
|
-
const B = String(
|
|
215
|
-
|
|
213
|
+
variablesData: g,
|
|
214
|
+
onInsert: (a, D) => {
|
|
215
|
+
const B = String(s[e.key] ?? ""), A = D ?? B.length;
|
|
216
|
+
r(
|
|
216
217
|
e.key,
|
|
217
|
-
`${B.slice(0, A)}${
|
|
218
|
+
`${B.slice(0, A)}${a}${B.slice(A)}`
|
|
218
219
|
);
|
|
219
220
|
},
|
|
220
|
-
children:
|
|
221
|
+
children: d
|
|
221
222
|
},
|
|
222
223
|
e.key
|
|
223
|
-
) : /* @__PURE__ */ o(
|
|
224
|
-
}, $ =
|
|
224
|
+
) : /* @__PURE__ */ o(P.Fragment, { children: d }, e.key);
|
|
225
|
+
}, $ = Q[n];
|
|
225
226
|
return /* @__PURE__ */ o(
|
|
226
|
-
|
|
227
|
+
W,
|
|
227
228
|
{
|
|
228
229
|
header: $.title,
|
|
229
230
|
closeOnEscapeKey: !1,
|
|
230
231
|
zIndex: R,
|
|
231
|
-
element:
|
|
232
|
-
|
|
232
|
+
element: _,
|
|
233
|
+
footerDirection: x,
|
|
234
|
+
headerCustomComponent: /* @__PURE__ */ o(q, { title: $.title, subtitle: $.subtitle }),
|
|
233
235
|
hideCrossButton: !1,
|
|
234
236
|
width: "431px",
|
|
235
|
-
headerStyle:
|
|
236
|
-
bodyStyle:
|
|
237
|
-
onClose:
|
|
237
|
+
headerStyle: M,
|
|
238
|
+
bodyStyle: z,
|
|
239
|
+
onClose: h,
|
|
238
240
|
saveButtonProps: {
|
|
239
241
|
"data-test": "config-tool-save",
|
|
240
242
|
buttonText: "Save",
|
|
241
243
|
buttonType: "primary",
|
|
242
244
|
size: "small",
|
|
243
|
-
disabled: !
|
|
244
|
-
isLoading:
|
|
245
|
-
onClick:
|
|
245
|
+
disabled: !m,
|
|
246
|
+
isLoading: f,
|
|
247
|
+
onClick: k
|
|
246
248
|
},
|
|
247
249
|
cancelButtonProps: {
|
|
248
250
|
"data-test": "config-tool-cancel",
|
|
249
251
|
buttonText: "Cancel",
|
|
250
252
|
buttonType: "tertiaryGray",
|
|
251
253
|
size: "small",
|
|
252
|
-
onClick:
|
|
254
|
+
onClick: h
|
|
253
255
|
},
|
|
254
|
-
children:
|
|
256
|
+
children: y.map(Y)
|
|
255
257
|
}
|
|
256
258
|
);
|
|
257
|
-
},
|
|
259
|
+
}, ee = w.div`
|
|
258
260
|
display: flex;
|
|
259
261
|
flex-direction: column;
|
|
260
262
|
gap: 8px;
|
|
261
|
-
`,
|
|
263
|
+
`, te = w.input`
|
|
262
264
|
width: 100%;
|
|
263
265
|
min-width: 0;
|
|
264
266
|
box-sizing: border-box;
|
|
@@ -279,22 +281,22 @@ const K = {
|
|
|
279
281
|
&:focus {
|
|
280
282
|
border-color: ${c.stroke.brand};
|
|
281
283
|
}
|
|
282
|
-
`,
|
|
284
|
+
`, ne = w.div`
|
|
283
285
|
display: flex;
|
|
284
286
|
flex-wrap: wrap;
|
|
285
287
|
gap: 6px;
|
|
286
|
-
`,
|
|
288
|
+
`, oe = w.div`
|
|
287
289
|
display: flex;
|
|
288
290
|
align-items: center;
|
|
289
291
|
justify-content: space-between;
|
|
290
292
|
gap: 12px;
|
|
291
|
-
`,
|
|
293
|
+
`, re = w.div`
|
|
292
294
|
display: flex;
|
|
293
295
|
flex-direction: column;
|
|
294
296
|
gap: 4px;
|
|
295
297
|
min-width: 0;
|
|
296
298
|
`;
|
|
297
299
|
export {
|
|
298
|
-
|
|
300
|
+
Se as ConfigToolPanel
|
|
299
301
|
};
|
|
300
302
|
//# sourceMappingURL=ConfigToolPanel.js.map
|