@tangle-network/sandbox-ui 0.113.7 → 0.115.0

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.
Files changed (107) hide show
  1. package/README.md +40 -0
  2. package/dist/WorkflowGraph-B-NsdYz_.js +1961 -0
  3. package/dist/assets.d.ts +214 -206
  4. package/dist/assets.js +1057 -908
  5. package/dist/auth-CWe17Hly.js +37 -0
  6. package/dist/auth.d.ts +2 -30
  7. package/dist/auth.js +2 -15
  8. package/dist/chat-BOvFAe6p.js +903 -0
  9. package/dist/chat.d.ts +2 -230
  10. package/dist/chat.js +2 -34
  11. package/dist/connectors.d.ts +52 -49
  12. package/dist/connectors.js +595 -643
  13. package/dist/dashboard-CZ3VgNPR.js +4969 -0
  14. package/dist/dashboard.d.ts +4 -1297
  15. package/dist/dashboard.js +4 -152
  16. package/dist/editor.d.ts +2 -1
  17. package/dist/editor.js +2 -31
  18. package/dist/files.d.ts +2 -6
  19. package/dist/files.js +2 -33
  20. package/dist/globals.css +75 -3
  21. package/dist/hooks.d.ts +146 -148
  22. package/dist/hooks.js +222 -55
  23. package/dist/index-CKRaj1pM.d.ts +776 -0
  24. package/dist/index-CXX5IpH2.d.ts +2 -0
  25. package/dist/index-CesPlOyx.d.ts +367 -0
  26. package/dist/index-DPePq7c8.d.ts +1340 -0
  27. package/dist/index-DiyLLZuJ.d.ts +230 -0
  28. package/dist/index-DvmZLMT1.d.ts +30 -0
  29. package/dist/index-PRM636Ex.d.ts +86 -0
  30. package/dist/index.d.ts +21 -33
  31. package/dist/index.js +21 -701
  32. package/dist/info-panel-C9cqDzOL.js +594 -0
  33. package/dist/integrations.d.ts +133 -141
  34. package/dist/integrations.js +491 -673
  35. package/dist/markdown.d.ts +2 -1
  36. package/dist/markdown.js +2 -13
  37. package/dist/model-I-NIxeM-.js +1250 -0
  38. package/dist/model-brand-B9A4H9pJ.d.ts +98 -0
  39. package/dist/model-brand-hMyDTUqB.js +624 -0
  40. package/dist/motion-DkyBRJb9.js +37 -0
  41. package/dist/openui.d.ts +2 -1
  42. package/dist/openui.js +2 -7
  43. package/dist/pages.d.ts +307 -300
  44. package/dist/pages.js +4404 -4181
  45. package/dist/primitives-Dh1vG6J3.js +113 -0
  46. package/dist/primitives.d.ts +2 -74
  47. package/dist/primitives.js +2 -204
  48. package/dist/provider-logo-DjqmConF.js +311 -0
  49. package/dist/run.d.ts +2 -1
  50. package/dist/run.js +2 -23
  51. package/dist/sdk-hooks.d.ts +3 -4
  52. package/dist/sdk-hooks.js +3 -31
  53. package/dist/sidecar-DgH_vQ6m.d.ts +9 -0
  54. package/dist/stores.d.ts +14 -15
  55. package/dist/stores.js +13 -81
  56. package/dist/styles.css +75 -3
  57. package/dist/template-card-L-4gmjx2.d.ts +90 -0
  58. package/dist/terminal-CN9ZgHRt.js +197 -0
  59. package/dist/terminal.d.ts +60 -60
  60. package/dist/terminal.js +2 -10
  61. package/dist/tokens.css +51 -0
  62. package/dist/types.d.ts +4 -11
  63. package/dist/types.js +0 -1
  64. package/dist/use-pty-session-C8cE0adT.js +669 -0
  65. package/dist/use-sidecar-auth-CCTJcrq7.d.ts +145 -0
  66. package/dist/use-sidecar-auth-DkdcufOz.js +742 -0
  67. package/dist/utils-DzWbktWG.js +2 -0
  68. package/dist/utils.d.ts +2 -1
  69. package/dist/utils.js +2 -37
  70. package/dist/workbench-ztU44Jt2.js +8455 -0
  71. package/dist/workbench.d.ts +2 -310
  72. package/dist/workbench.js +2 -44
  73. package/dist/workflows.d.ts +341 -339
  74. package/dist/workflows.js +74 -109
  75. package/dist/workspace-B6AtI6ld.js +2510 -0
  76. package/dist/workspace.d.ts +2 -761
  77. package/dist/workspace.js +2 -51
  78. package/package.json +46 -40
  79. package/dist/WorkflowGraph-2QOORYIR.js +0 -1722
  80. package/dist/chunk-2BUPSB7O.js +0 -0
  81. package/dist/chunk-3KBNPQQF.js +0 -2697
  82. package/dist/chunk-3LIXVEEE.js +0 -279
  83. package/dist/chunk-72UF2H5H.js +0 -707
  84. package/dist/chunk-AI25G5MX.js +0 -944
  85. package/dist/chunk-ANSC5AFC.js +0 -314
  86. package/dist/chunk-AZ3AWMTM.js +0 -8
  87. package/dist/chunk-BGVEHENC.js +0 -10
  88. package/dist/chunk-CZ7CSFO4.js +0 -39
  89. package/dist/chunk-DYXXYGS4.js +0 -4700
  90. package/dist/chunk-EI44GEQ5.js +0 -6
  91. package/dist/chunk-IACRDXER.js +0 -219
  92. package/dist/chunk-IOB2PW5Z.js +0 -55
  93. package/dist/chunk-M4K7ZGR2.js +0 -24
  94. package/dist/chunk-MYZAW4RX.js +0 -665
  95. package/dist/chunk-OMUIPJQZ.js +0 -555
  96. package/dist/chunk-RQZRXKMR.js +0 -398
  97. package/dist/chunk-SOKKTB7W.js +0 -230
  98. package/dist/chunk-TADGS4Q7.js +0 -1010
  99. package/dist/chunk-UCQNOUN3.js +0 -7964
  100. package/dist/chunk-ULPLRBMF.js +0 -34
  101. package/dist/chunk-WID73FPH.js +0 -89
  102. package/dist/chunk-X3YXP7FG.js +0 -38
  103. package/dist/logo-DiAjq759.d.ts +0 -15
  104. package/dist/model-brand-BNnaUKKZ.d.ts +0 -99
  105. package/dist/ports-list-CDIvTNnX.d.ts +0 -17
  106. package/dist/sdk-hooks-Djkjq4cp.d.ts +0 -145
  107. package/dist/template-card-Bn-Bw5xC.d.ts +0 -87
@@ -1,683 +1,635 @@
1
- import {
2
- cn
3
- } from "./chunk-EI44GEQ5.js";
4
- import "./chunk-CZ7CSFO4.js";
5
-
6
- // src/connectors/action-list.tsx
7
- import { Badge, Button, Input } from "@tangle-network/ui/primitives";
8
- import { Check, ChevronDown, ChevronRight, Copy, Search } from "lucide-react";
9
- import { useEffect, useMemo, useRef, useState as useState2 } from "react";
10
-
11
- // src/connectors/integration-step-yaml.ts
1
+ import { t as cn$1 } from "./utils-DzWbktWG.js";
2
+ import { Badge, Button, EmptyState, Input } from "@tangle-network/ui/primitives";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useMemo, useRef, useState } from "react";
5
+ import { focusField } from "@tangle-network/ui/utils";
6
+ import { Blocks, Check, ChevronDown, ChevronRight, Copy, Search } from "lucide-react";
7
+ //#region src/connectors/integration-step-yaml.ts
12
8
  function asSchemaObject(value) {
13
- return typeof value === "object" && value !== null ? value : null;
9
+ return typeof value === "object" && value !== null ? value : null;
14
10
  }
11
+ /** YAML scalar placeholder for a property schema. A string enum stubs its
12
+ * first string member (a real accepted input beats an empty string, quoted so
13
+ * it stays a string); otherwise — non-string/undefined/object enum values, or
14
+ * no enum — the placeholder is the type's empty value. Non-string enum members
15
+ * are intentionally NOT inlined: `JSON.stringify` on them would emit a
16
+ * type-changing bare token (`true`, `{}`, or even `undefined` for a malformed
17
+ * member), which the type placeholder handles honestly instead. */
15
18
  function placeholderFor(propSchema) {
16
- const schema = asSchemaObject(propSchema);
17
- if (!schema) return '""';
18
- if (Array.isArray(schema.enum)) {
19
- const firstString = schema.enum.find(
20
- (value) => typeof value === "string"
21
- );
22
- if (firstString !== void 0) return JSON.stringify(firstString);
23
- }
24
- const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
25
- switch (type) {
26
- case "number":
27
- case "integer":
28
- return "0";
29
- case "boolean":
30
- return "false";
31
- case "array":
32
- return "[]";
33
- case "object":
34
- return "{}";
35
- default:
36
- return '""';
37
- }
19
+ const schema = asSchemaObject(propSchema);
20
+ if (!schema) return "\"\"";
21
+ if (Array.isArray(schema.enum)) {
22
+ const firstString = schema.enum.find((value) => typeof value === "string");
23
+ if (firstString !== void 0) return JSON.stringify(firstString);
24
+ }
25
+ switch (Array.isArray(schema.type) ? schema.type[0] : schema.type) {
26
+ case "number":
27
+ case "integer": return "0";
28
+ case "boolean": return "false";
29
+ case "array": return "[]";
30
+ case "object": return "{}";
31
+ default: return "\"\"";
32
+ }
38
33
  }
39
- var YAML_RESERVED_SCALAR = /^(?:true|false|null|yes|no|on|off|~)$/i;
34
+ /** YAML 1.1 plain scalars a parser would coerce away from a string —
35
+ * booleans, null, and the empty value. Numbers are caught separately (they
36
+ * have no letter, so the "must contain a letter" rule below rejects them). */
37
+ const YAML_RESERVED_SCALAR = /^(?:true|false|null|yes|no|on|off|~)$/i;
38
+ /** True when `value` is safe to emit as a bare (unquoted) YAML scalar: a plain
39
+ * identifier-ish token that a YAML parser will load back as the same string.
40
+ * Requires at least one letter so pure-numeric tokens (`123`, `1.2`) quote,
41
+ * and rejects reserved words so `true`/`null`/`no` quote — both would
42
+ * otherwise load as a non-string type. */
40
43
  function isBareYamlScalar(value) {
41
- return /^[A-Za-z0-9_.-]+$/.test(value) && /[A-Za-z]/.test(value) && !YAML_RESERVED_SCALAR.test(value);
44
+ return /^[A-Za-z0-9_.-]+$/.test(value) && /[A-Za-z]/.test(value) && !YAML_RESERVED_SCALAR.test(value);
42
45
  }
46
+ /** Bare YAML key when safe, quoted otherwise. Keys use the stricter identifier
47
+ * rule (must start with a letter/underscore) rather than the scalar rule,
48
+ * since a dotted or digit-led key reads poorly unquoted — but a key that spells
49
+ * a reserved scalar word (`true`/`null`/`on`/…) must still be quoted, or a
50
+ * YAML 1.1 parser loads the KEY as a boolean/null instead of the string. */
43
51
  function yamlKey(key) {
44
- return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) && !YAML_RESERVED_SCALAR.test(key) ? key : JSON.stringify(key);
52
+ return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) && !YAML_RESERVED_SCALAR.test(key) ? key : JSON.stringify(key);
45
53
  }
54
+ /**
55
+ * The input fields to stub: the schema's `required` list when it names any
56
+ * (in declaration order), else every top-level property — a schema with no
57
+ * required fields gives no signal to cut, and showing all is honest.
58
+ */
46
59
  function stubFields(inputSchema) {
47
- const schema = asSchemaObject(inputSchema);
48
- const properties = asSchemaObject(schema?.properties);
49
- if (!properties) return [];
50
- const names = Object.keys(properties);
51
- const required = Array.isArray(schema?.required) ? schema.required.filter(
52
- (name) => typeof name === "string" && names.includes(name)
53
- ) : [];
54
- const chosen = required.length > 0 ? required : names;
55
- return chosen.map((name) => [
56
- name,
57
- properties[name]
58
- ]);
60
+ const schema = asSchemaObject(inputSchema);
61
+ const properties = asSchemaObject(schema?.properties);
62
+ if (!properties) return [];
63
+ const names = Object.keys(properties);
64
+ const required = Array.isArray(schema?.required) ? schema.required.filter((name) => typeof name === "string" && names.includes(name)) : [];
65
+ return (required.length > 0 ? required : names).map((name) => [name, properties[name]]);
59
66
  }
67
+ /** The `- integration.invoke:` step block for an action, ready to paste into a
68
+ * workflow's `do:` list. */
60
69
  function integrationStepYaml(path, inputSchema) {
61
- const fields = stubFields(inputSchema);
62
- const pathScalar = isBareYamlScalar(path) ? path : JSON.stringify(path);
63
- const lines = [
64
- "- integration.invoke:",
65
- ` path: ${pathScalar}`,
66
- ...fields.length === 0 ? [" input: {}"] : [
67
- " input:",
68
- ...fields.map(
69
- ([name, propSchema]) => ` ${yamlKey(name)}: ${placeholderFor(propSchema)}`
70
- )
71
- ]
72
- ];
73
- return `${lines.join("\n")}
74
- `;
70
+ const fields = stubFields(inputSchema);
71
+ return `${[
72
+ "- integration.invoke:",
73
+ ` path: ${isBareYamlScalar(path) ? path : JSON.stringify(path)}`,
74
+ ...fields.length === 0 ? [" input: {}"] : [" input:", ...fields.map(([name, propSchema]) => ` ${yamlKey(name)}: ${placeholderFor(propSchema)}`)]
75
+ ].join("\n")}\n`;
75
76
  }
76
-
77
- // src/connectors/schema-table.tsx
78
- import { useState } from "react";
79
- import { jsx, jsxs } from "react/jsx-runtime";
80
- var MAX_DEPTH = 3;
77
+ //#endregion
78
+ //#region src/connectors/schema-table.tsx
79
+ /** Nested rows beyond this depth collapse to their parent's `object` type —
80
+ * deeper shapes are readable in the raw-JSON view. */
81
+ const MAX_DEPTH = 3;
81
82
  function asNode(value) {
82
- return typeof value === "object" && value !== null ? value : null;
83
+ return typeof value === "object" && value !== null ? value : null;
83
84
  }
84
85
  function typeLabel(node) {
85
- const type = Array.isArray(node.type) ? node.type.join(" | ") : node.type;
86
- if (type === "array") {
87
- const items = asNode(node.items);
88
- const itemType = items ? Array.isArray(items.type) ? items.type.join(" | ") : items.type : void 0;
89
- return typeof itemType === "string" ? `array<${itemType}>` : "array";
90
- }
91
- if (typeof type === "string") return type;
92
- if (Array.isArray(node.enum)) return "enum";
93
- return "any";
86
+ const type = Array.isArray(node.type) ? node.type.join(" | ") : node.type;
87
+ if (type === "array") {
88
+ const items = asNode(node.items);
89
+ const itemType = items ? Array.isArray(items.type) ? items.type.join(" | ") : items.type : void 0;
90
+ return typeof itemType === "string" ? `array<${itemType}>` : "array";
91
+ }
92
+ if (typeof type === "string") return type;
93
+ if (Array.isArray(node.enum)) return "enum";
94
+ return "any";
94
95
  }
95
96
  function collectRows(schema, depth, prefix, out) {
96
- const properties = asNode(schema.properties);
97
- if (!properties) return;
98
- const required = new Set(
99
- Array.isArray(schema.required) ? schema.required.filter((n) => typeof n === "string") : []
100
- );
101
- for (const [name, rawProp] of Object.entries(
102
- properties
103
- )) {
104
- const prop = asNode(rawProp) ?? {};
105
- const enumValues = Array.isArray(prop.enum) ? prop.enum.map((v) => JSON.stringify(v)).filter((v) => typeof v === "string") : null;
106
- out.push({
107
- name: prefix ? `${prefix}.${name}` : name,
108
- depth,
109
- type: typeLabel(prop),
110
- required: required.has(name),
111
- description: typeof prop.description === "string" ? prop.description : "",
112
- enumValues
113
- });
114
- if (depth + 1 >= MAX_DEPTH) continue;
115
- const child = typeLabel(prop) === "object" || asNode(prop.properties) ? prop : asNode(prop.items)?.properties ?? null ? asNode(prop.items) : null;
116
- if (child) {
117
- collectRows(child, depth + 1, prefix ? `${prefix}.${name}` : name, out);
118
- }
119
- }
97
+ const properties = asNode(schema.properties);
98
+ if (!properties) return;
99
+ const required = new Set(Array.isArray(schema.required) ? schema.required.filter((n) => typeof n === "string") : []);
100
+ for (const [name, rawProp] of Object.entries(properties)) {
101
+ const prop = asNode(rawProp) ?? {};
102
+ const enumValues = Array.isArray(prop.enum) ? prop.enum.map((v) => JSON.stringify(v)).filter((v) => typeof v === "string") : null;
103
+ out.push({
104
+ name: prefix ? `${prefix}.${name}` : name,
105
+ depth,
106
+ type: typeLabel(prop),
107
+ required: required.has(name),
108
+ description: typeof prop.description === "string" ? prop.description : "",
109
+ enumValues
110
+ });
111
+ if (depth + 1 >= MAX_DEPTH) continue;
112
+ const child = typeLabel(prop) === "object" || asNode(prop.properties) ? prop : asNode(prop.items)?.properties ?? null ? asNode(prop.items) : null;
113
+ if (child) collectRows(child, depth + 1, prefix ? `${prefix}.${name}` : name, out);
114
+ }
120
115
  }
121
116
  function schemaRows(schema) {
122
- const node = asNode(schema);
123
- if (!node || !asNode(node.properties)) return null;
124
- const rows = [];
125
- collectRows(node, 0, "", rows);
126
- return rows;
117
+ const node = asNode(schema);
118
+ if (!node || !asNode(node.properties)) return null;
119
+ const rows = [];
120
+ collectRows(node, 0, "", rows);
121
+ return rows;
127
122
  }
123
+ /** `JSON.stringify` that survives a value it cannot serialize. Schemas arrive
124
+ * over the wire as JSON (acyclic), but the prop is `unknown`, so a hand-built
125
+ * cyclic object must degrade to a readable note in the raw-JSON view rather
126
+ * than throw and take down the host. */
128
127
  function safeJsonStringify(value) {
129
- try {
130
- return JSON.stringify(value, null, 2) ?? "null";
131
- } catch {
132
- return "(schema cannot be shown as JSON)";
133
- }
128
+ try {
129
+ return JSON.stringify(value, null, 2) ?? "null";
130
+ } catch {
131
+ return "(schema cannot be shown as JSON)";
132
+ }
134
133
  }
135
134
  function SchemaTable({ schema, label }) {
136
- const [showRaw, setShowRaw] = useState(false);
137
- const rows = schemaRows(schema);
138
- const rawJson = safeJsonStringify(schema);
139
- return /* @__PURE__ */ jsxs("div", { children: [
140
- /* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center justify-between", children: [
141
- /* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: label }),
142
- rows !== null && rows.length > 0 && /* @__PURE__ */ jsx(
143
- "button",
144
- {
145
- type: "button",
146
- onClick: () => setShowRaw((v) => !v),
147
- className: "text-primary text-xs transition-colors hover:underline",
148
- children: showRaw ? "Field view" : "Raw JSON"
149
- }
150
- )
151
- ] }),
152
- rows === null || rows.length === 0 || showRaw ? rows !== null && rows.length === 0 && !showRaw ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "No fields." }) : /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto rounded-lg bg-surface-container-high p-3 text-xs leading-relaxed", children: rawJson }) : /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
153
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-border border-b bg-surface-container-high", children: [
154
- /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs", children: "Field" }),
155
- /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs", children: "Type" }),
156
- /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs", children: "Description" })
157
- ] }) }),
158
- /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs(
159
- "tr",
160
- {
161
- className: "border-border border-b last:border-b-0",
162
- children: [
163
- /* @__PURE__ */ jsxs(
164
- "td",
165
- {
166
- className: "whitespace-nowrap px-3 py-1.5 align-top font-mono text-foreground text-xs",
167
- style: { paddingLeft: `${0.75 + row.depth * 1}rem` },
168
- children: [
169
- row.name.split(".").at(-1),
170
- row.required && /* @__PURE__ */ jsxs("span", { className: "ml-0.5 text-destructive", title: "Required", children: [
171
- "*",
172
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "required" })
173
- ] })
174
- ]
175
- }
176
- ),
177
- /* @__PURE__ */ jsx("td", { className: "whitespace-nowrap px-3 py-1.5 align-top font-mono text-muted-foreground text-xs", children: row.type }),
178
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 align-top text-muted-foreground text-xs", children: [
179
- row.description,
180
- row.enumValues && /* @__PURE__ */ jsxs("span", { className: row.description ? "ml-1" : "", children: [
181
- "One of: ",
182
- row.enumValues.join(", ")
183
- ] })
184
- ] })
185
- ]
186
- },
187
- row.name
188
- )) })
189
- ] }) })
190
- ] });
135
+ const [showRaw, setShowRaw] = useState(false);
136
+ const rows = schemaRows(schema);
137
+ const rawJson = safeJsonStringify(schema);
138
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
139
+ className: "mb-1.5 flex items-center justify-between",
140
+ children: [/* @__PURE__ */ jsx("span", {
141
+ className: "font-medium text-muted-foreground text-xs uppercase tracking-wide",
142
+ children: label
143
+ }), rows !== null && rows.length > 0 && /* @__PURE__ */ jsx("button", {
144
+ type: "button",
145
+ onClick: () => setShowRaw((v) => !v),
146
+ className: "text-primary text-xs transition-colors hover:underline",
147
+ children: showRaw ? "Field view" : "Raw JSON"
148
+ })]
149
+ }), rows === null || rows.length === 0 || showRaw ? rows !== null && rows.length === 0 && !showRaw ? /* @__PURE__ */ jsx("p", {
150
+ className: "text-muted-foreground text-sm",
151
+ children: "No fields."
152
+ }) : /* @__PURE__ */ jsx("pre", {
153
+ className: "overflow-x-auto rounded-lg bg-surface-container-high p-3 text-xs leading-relaxed",
154
+ children: rawJson
155
+ }) : /* @__PURE__ */ jsx("div", {
156
+ className: "overflow-x-auto rounded-lg border border-border",
157
+ children: /* @__PURE__ */ jsxs("table", {
158
+ className: "w-full text-sm",
159
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
160
+ className: "border-border border-b bg-surface-container-high",
161
+ children: [
162
+ /* @__PURE__ */ jsx("th", {
163
+ className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs",
164
+ children: "Field"
165
+ }),
166
+ /* @__PURE__ */ jsx("th", {
167
+ className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs",
168
+ children: "Type"
169
+ }),
170
+ /* @__PURE__ */ jsx("th", {
171
+ className: "px-3 py-1.5 text-left font-medium text-muted-foreground text-xs",
172
+ children: "Description"
173
+ })
174
+ ]
175
+ }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs("tr", {
176
+ className: "border-border border-b last:border-b-0",
177
+ children: [
178
+ /* @__PURE__ */ jsxs("td", {
179
+ className: "whitespace-nowrap px-3 py-1.5 align-top font-mono text-foreground text-xs",
180
+ style: { paddingLeft: `${.75 + row.depth * 1}rem` },
181
+ children: [row.name.split(".").at(-1), row.required && /* @__PURE__ */ jsxs("span", {
182
+ className: "ml-0.5 text-destructive",
183
+ title: "Required",
184
+ children: ["*", /* @__PURE__ */ jsx("span", {
185
+ className: "sr-only",
186
+ children: "required"
187
+ })]
188
+ })]
189
+ }),
190
+ /* @__PURE__ */ jsx("td", {
191
+ className: "whitespace-nowrap px-3 py-1.5 align-top font-mono text-muted-foreground text-xs",
192
+ children: row.type
193
+ }),
194
+ /* @__PURE__ */ jsxs("td", {
195
+ className: "px-3 py-1.5 align-top text-muted-foreground text-xs",
196
+ children: [row.description, row.enumValues && /* @__PURE__ */ jsxs("span", {
197
+ className: row.description ? "ml-1" : "",
198
+ children: ["One of: ", row.enumValues.join(", ")]
199
+ })]
200
+ })
201
+ ]
202
+ }, row.name)) })]
203
+ })
204
+ })] });
191
205
  }
192
-
193
- // src/connectors/action-list.tsx
194
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
195
- var RISK_BADGES = {
196
- read: { label: "read", variant: "success" },
197
- write: { label: "write", variant: "warning" },
198
- destructive: { label: "destructive", variant: "destructive" },
199
- unknown: { label: "unclassified", variant: "outline" }
206
+ //#endregion
207
+ //#region src/connectors/action-list.tsx
208
+ const RISK_BADGES = {
209
+ read: {
210
+ label: "read",
211
+ variant: "success"
212
+ },
213
+ write: {
214
+ label: "write",
215
+ variant: "warning"
216
+ },
217
+ destructive: {
218
+ label: "destructive",
219
+ variant: "destructive"
220
+ },
221
+ unknown: {
222
+ label: "unclassified",
223
+ variant: "outline"
224
+ }
200
225
  };
201
226
  function RiskBadge({ risk }) {
202
- const badge = RISK_BADGES[risk ?? "unknown"] ?? RISK_BADGES.unknown;
203
- return /* @__PURE__ */ jsx2(Badge, { variant: badge.variant, children: badge.label });
227
+ const badge = RISK_BADGES[risk ?? "unknown"] ?? RISK_BADGES.unknown;
228
+ return /* @__PURE__ */ jsx(Badge, {
229
+ variant: badge.variant,
230
+ children: badge.label
231
+ });
204
232
  }
205
- function CopyButton({
206
- text,
207
- label,
208
- title
209
- }) {
210
- const [state, setState] = useState2("idle");
211
- const timerRef = useRef(null);
212
- useEffect(
213
- () => () => {
214
- if (timerRef.current) clearTimeout(timerRef.current);
215
- },
216
- []
217
- );
218
- const copy = async () => {
219
- let next;
220
- try {
221
- await navigator.clipboard.writeText(text);
222
- next = "copied";
223
- } catch (err) {
224
- console.warn("Clipboard write failed:", err);
225
- next = "failed";
226
- }
227
- setState(next);
228
- if (timerRef.current) clearTimeout(timerRef.current);
229
- timerRef.current = setTimeout(() => setState("idle"), 2e3);
230
- };
231
- return /* @__PURE__ */ jsxs2(Button, { type: "button", variant: "outline", size: "sm", onClick: copy, title, children: [
232
- state === "copied" ? /* @__PURE__ */ jsx2(Check, { className: "h-3.5 w-3.5 text-[var(--surface-success-text)]" }) : /* @__PURE__ */ jsx2(Copy, { className: "h-3.5 w-3.5" }),
233
- state === "copied" ? "Copied" : state === "failed" ? "Copy failed" : label
234
- ] });
233
+ /** Copy-to-clipboard button with a transient confirmation. `navigator.clipboard`
234
+ * is unavailable in insecure contexts (and can reject on a permission denial),
235
+ * so a failure surfaces a visible "Copy failed" state rather than reverting
236
+ * silently — the user learns the copy didn't happen and can select the text
237
+ * manually. */
238
+ function CopyButton({ text, label, title }) {
239
+ const [state, setState] = useState("idle");
240
+ const timerRef = useRef(null);
241
+ useEffect(() => () => {
242
+ if (timerRef.current) clearTimeout(timerRef.current);
243
+ }, []);
244
+ const copy = async () => {
245
+ let next;
246
+ try {
247
+ await navigator.clipboard.writeText(text);
248
+ next = "copied";
249
+ } catch (err) {
250
+ console.warn("Clipboard write failed:", err);
251
+ next = "failed";
252
+ }
253
+ setState(next);
254
+ if (timerRef.current) clearTimeout(timerRef.current);
255
+ timerRef.current = setTimeout(() => setState("idle"), 2e3);
256
+ };
257
+ return /* @__PURE__ */ jsxs(Button, {
258
+ type: "button",
259
+ variant: "outline",
260
+ size: "sm",
261
+ onClick: copy,
262
+ title,
263
+ children: [state === "copied" ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-[var(--surface-success-text)]" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }), state === "copied" ? "Copied" : state === "failed" ? "Copy failed" : label]
264
+ });
235
265
  }
266
+ /** Many catalog action titles repeat the provider ("GitHub: Issues Create");
267
+ * on the provider's own page that prefix is noise, so strip it. */
236
268
  function actionDisplayTitle(action, providerTitle) {
237
- const title = action.title ?? action.path;
238
- const prefix = `${providerTitle.toLowerCase()}: `;
239
- return title.toLowerCase().startsWith(prefix) ? title.slice(prefix.length) : title;
269
+ const title = action.title ?? action.path;
270
+ const prefix = `${providerTitle.toLowerCase()}: `;
271
+ return title.toLowerCase().startsWith(prefix) ? title.slice(prefix.length) : title;
240
272
  }
241
- function ActionRow({
242
- action,
243
- title,
244
- expanded,
245
- onToggle,
246
- onBuildWithAssistant
247
- }) {
248
- return /* @__PURE__ */ jsxs2("li", { children: [
249
- /* @__PURE__ */ jsxs2(
250
- "button",
251
- {
252
- type: "button",
253
- onClick: onToggle,
254
- "aria-expanded": expanded,
255
- className: "flex w-full items-center justify-between gap-4 p-4 text-left transition-colors hover:bg-surface-container-high",
256
- children: [
257
- /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-center gap-3", children: [
258
- expanded ? /* @__PURE__ */ jsx2(
259
- ChevronDown,
260
- {
261
- "aria-hidden": "true",
262
- className: "h-4 w-4 shrink-0 text-muted-foreground"
263
- }
264
- ) : /* @__PURE__ */ jsx2(
265
- ChevronRight,
266
- {
267
- "aria-hidden": "true",
268
- className: "h-4 w-4 shrink-0 text-muted-foreground"
269
- }
270
- ),
271
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
272
- /* @__PURE__ */ jsx2("p", { className: "font-medium text-foreground", children: title }),
273
- !expanded && action.description && /* @__PURE__ */ jsx2("p", { className: "truncate text-muted-foreground text-sm", children: action.description })
274
- ] })
275
- ] }),
276
- /* @__PURE__ */ jsx2(RiskBadge, { risk: action.risk })
277
- ]
278
- }
279
- ),
280
- expanded && /* @__PURE__ */ jsxs2("div", { className: "space-y-4 border-border border-t bg-surface-container-high/40 p-4 pl-11", children: [
281
- action.description && /* @__PURE__ */ jsx2("p", { className: "text-muted-foreground text-sm", children: action.description }),
282
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-2", children: [
283
- /* @__PURE__ */ jsx2("code", { className: "rounded bg-surface-container-high px-2 py-1 font-mono text-sm", children: action.path }),
284
- /* @__PURE__ */ jsx2(CopyButton, { text: action.path, label: "Copy path" })
285
- ] }),
286
- /* @__PURE__ */ jsx2(SchemaTable, { schema: action.inputSchema, label: "Input" }),
287
- action.outputSchema != null && /* @__PURE__ */ jsxs2("details", { children: [
288
- /* @__PURE__ */ jsxs2("summary", { className: "cursor-pointer text-muted-foreground text-sm transition-colors hover:text-foreground", children: [
289
- "Output shape (for",
290
- " ",
291
- /* @__PURE__ */ jsx2("code", { className: "rounded bg-surface-container-high px-1 py-0.5 font-mono text-xs", children: "${steps[N]\u2026}" }),
292
- " ",
293
- "expressions)"
294
- ] }),
295
- /* @__PURE__ */ jsx2("div", { className: "mt-2", children: /* @__PURE__ */ jsx2(SchemaTable, { schema: action.outputSchema, label: "Output" }) })
296
- ] }),
297
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-2", children: [
298
- /* @__PURE__ */ jsx2(
299
- CopyButton,
300
- {
301
- text: integrationStepYaml(action.path, action.inputSchema),
302
- label: "Copy step YAML",
303
- title: "A ready-to-paste integration.invoke step for a workflow definition's do: list"
304
- }
305
- ),
306
- onBuildWithAssistant && /* @__PURE__ */ jsx2(
307
- Button,
308
- {
309
- type: "button",
310
- size: "sm",
311
- onClick: () => onBuildWithAssistant(action),
312
- children: "Build with assistant"
313
- }
314
- )
315
- ] })
316
- ] })
317
- ] });
273
+ /** One expandable action row: title + risk collapsed; path, schemas, and the
274
+ * copy/build affordances expanded. */
275
+ function ActionRow({ action, title, expanded, onToggle, onBuildWithAssistant }) {
276
+ return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsxs("button", {
277
+ type: "button",
278
+ onClick: onToggle,
279
+ "aria-expanded": expanded,
280
+ className: "flex w-full items-center justify-between gap-4 p-4 text-left transition-colors hover:bg-surface-container-high",
281
+ children: [/* @__PURE__ */ jsxs("div", {
282
+ className: "flex min-w-0 items-center gap-3",
283
+ children: [expanded ? /* @__PURE__ */ jsx(ChevronDown, {
284
+ "aria-hidden": "true",
285
+ className: "h-4 w-4 shrink-0 text-muted-foreground"
286
+ }) : /* @__PURE__ */ jsx(ChevronRight, {
287
+ "aria-hidden": "true",
288
+ className: "h-4 w-4 shrink-0 text-muted-foreground"
289
+ }), /* @__PURE__ */ jsxs("div", {
290
+ className: "min-w-0",
291
+ children: [/* @__PURE__ */ jsx("p", {
292
+ className: "font-medium text-foreground",
293
+ children: title
294
+ }), !expanded && action.description && /* @__PURE__ */ jsx("p", {
295
+ className: "truncate text-muted-foreground text-sm",
296
+ children: action.description
297
+ })]
298
+ })]
299
+ }), /* @__PURE__ */ jsx(RiskBadge, { risk: action.risk })]
300
+ }), expanded && /* @__PURE__ */ jsxs("div", {
301
+ className: "space-y-4 border-border border-t bg-surface-container-high/40 p-4 pl-11",
302
+ children: [
303
+ action.description && /* @__PURE__ */ jsx("p", {
304
+ className: "text-muted-foreground text-sm",
305
+ children: action.description
306
+ }),
307
+ /* @__PURE__ */ jsxs("div", {
308
+ className: "flex flex-wrap items-center gap-2",
309
+ children: [/* @__PURE__ */ jsx("code", {
310
+ className: "rounded bg-surface-container-high px-2 py-1 font-mono text-sm",
311
+ children: action.path
312
+ }), /* @__PURE__ */ jsx(CopyButton, {
313
+ text: action.path,
314
+ label: "Copy path"
315
+ })]
316
+ }),
317
+ /* @__PURE__ */ jsx(SchemaTable, {
318
+ schema: action.inputSchema,
319
+ label: "Input"
320
+ }),
321
+ action.outputSchema != null && /* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsxs("summary", {
322
+ className: "cursor-pointer text-muted-foreground text-sm transition-colors hover:text-foreground",
323
+ children: [
324
+ "Output shape (for",
325
+ " ",
326
+ /* @__PURE__ */ jsx("code", {
327
+ className: "rounded bg-surface-container-high px-1 py-0.5 font-mono text-xs",
328
+ children: "${steps[N]…}"
329
+ }),
330
+ " ",
331
+ "expressions)"
332
+ ]
333
+ }), /* @__PURE__ */ jsx("div", {
334
+ className: "mt-2",
335
+ children: /* @__PURE__ */ jsx(SchemaTable, {
336
+ schema: action.outputSchema,
337
+ label: "Output"
338
+ })
339
+ })] }),
340
+ /* @__PURE__ */ jsxs("div", {
341
+ className: "flex flex-wrap items-center gap-2",
342
+ children: [/* @__PURE__ */ jsx(CopyButton, {
343
+ text: integrationStepYaml(action.path, action.inputSchema),
344
+ label: "Copy step YAML",
345
+ title: "A ready-to-paste integration.invoke step for a workflow definition's do: list"
346
+ }), onBuildWithAssistant && /* @__PURE__ */ jsx(Button, {
347
+ type: "button",
348
+ size: "sm",
349
+ onClick: () => onBuildWithAssistant(action),
350
+ children: "Build with assistant"
351
+ })]
352
+ })
353
+ ]
354
+ })] });
318
355
  }
319
- function ConnectorActionList({
320
- actions,
321
- providerTitle,
322
- onBuildWithAssistant,
323
- maybeTruncated,
324
- className
325
- }) {
326
- const [filter, setFilter] = useState2("");
327
- const [expandedPath, setExpandedPath] = useState2(null);
328
- const decorated = useMemo(
329
- () => actions.map((action) => ({
330
- action,
331
- title: actionDisplayTitle(action, providerTitle)
332
- })).sort((a, b) => a.title.localeCompare(b.title)),
333
- [actions, providerTitle]
334
- );
335
- const filtered = useMemo(() => {
336
- const q = filter.trim().toLowerCase();
337
- if (!q) return decorated;
338
- return decorated.filter(
339
- ({ action, title }) => title.toLowerCase().includes(q) || action.path.toLowerCase().includes(q) || (action.description ?? "").toLowerCase().includes(q)
340
- );
341
- }, [decorated, filter]);
342
- if (actions.length === 0) {
343
- return /* @__PURE__ */ jsx2(
344
- "section",
345
- {
346
- className: cn("rounded-lg border border-border bg-card p-6", className),
347
- children: /* @__PURE__ */ jsx2("p", { className: "text-center text-muted-foreground text-sm", children: "This connector doesn't expose any invokable actions yet." })
348
- }
349
- );
350
- }
351
- return /* @__PURE__ */ jsxs2(
352
- "section",
353
- {
354
- className: cn(
355
- "overflow-hidden rounded-lg border border-border bg-card",
356
- className
357
- ),
358
- children: [
359
- /* @__PURE__ */ jsx2("div", { className: "border-border border-b p-4", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-center gap-3", children: [
360
- /* @__PURE__ */ jsxs2("div", { className: "relative min-w-[12rem] flex-1", children: [
361
- /* @__PURE__ */ jsx2(
362
- Search,
363
- {
364
- "aria-hidden": "true",
365
- className: "pointer-events-none absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-muted-foreground"
366
- }
367
- ),
368
- /* @__PURE__ */ jsx2(
369
- Input,
370
- {
371
- type: "search",
372
- value: filter,
373
- onChange: (e) => setFilter(e.target.value),
374
- placeholder: "Filter actions",
375
- "aria-label": "Filter actions",
376
- className: "pl-8"
377
- }
378
- )
379
- ] }),
380
- /* @__PURE__ */ jsxs2(
381
- "span",
382
- {
383
- role: "status",
384
- "aria-live": "polite",
385
- className: "text-muted-foreground text-sm",
386
- children: [
387
- filtered.length,
388
- " of ",
389
- actions.length,
390
- maybeTruncated ? "+" : "",
391
- " actions"
392
- ]
393
- }
394
- )
395
- ] }) }),
396
- filtered.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "p-6 text-center", children: [
397
- /* @__PURE__ */ jsx2("p", { className: "text-muted-foreground text-sm", children: "No actions match your filter." }),
398
- /* @__PURE__ */ jsx2(
399
- Button,
400
- {
401
- type: "button",
402
- variant: "outline",
403
- size: "sm",
404
- onClick: () => setFilter(""),
405
- className: "mt-4",
406
- children: "Clear filter"
407
- }
408
- )
409
- ] }) : /* @__PURE__ */ jsx2("ul", { className: "divide-y divide-border", children: filtered.map(({ action, title }) => /* @__PURE__ */ jsx2(
410
- ActionRow,
411
- {
412
- action,
413
- title,
414
- expanded: expandedPath === action.path,
415
- onToggle: () => setExpandedPath(
416
- (current) => current === action.path ? null : action.path
417
- ),
418
- onBuildWithAssistant
419
- },
420
- action.path
421
- )) })
422
- ]
423
- }
424
- );
356
+ /**
357
+ * Searchable list of a connector's invokable actions. Each row expands to show
358
+ * the action's path, input/output schemas, and copy/build affordances. Purely
359
+ * presentational — the host supplies the actions and the assistant hook.
360
+ */
361
+ function ConnectorActionList({ actions, providerTitle, onBuildWithAssistant, maybeTruncated, className }) {
362
+ const [filter, setFilter] = useState("");
363
+ const [expandedPath, setExpandedPath] = useState(null);
364
+ const decorated = useMemo(() => actions.map((action) => ({
365
+ action,
366
+ title: actionDisplayTitle(action, providerTitle)
367
+ })).sort((a, b) => a.title.localeCompare(b.title)), [actions, providerTitle]);
368
+ const filtered = useMemo(() => {
369
+ const q = filter.trim().toLowerCase();
370
+ if (!q) return decorated;
371
+ return decorated.filter(({ action, title }) => title.toLowerCase().includes(q) || action.path.toLowerCase().includes(q) || (action.description ?? "").toLowerCase().includes(q));
372
+ }, [decorated, filter]);
373
+ if (actions.length === 0) return /* @__PURE__ */ jsx("section", {
374
+ className: cn$1("rounded-lg border border-border bg-card p-6", className),
375
+ children: /* @__PURE__ */ jsx("p", {
376
+ className: "text-center text-muted-foreground text-sm",
377
+ children: "This connector doesn't expose any invokable actions yet."
378
+ })
379
+ });
380
+ return /* @__PURE__ */ jsxs("section", {
381
+ className: cn$1("overflow-hidden rounded-lg border border-border bg-card", className),
382
+ children: [/* @__PURE__ */ jsx("div", {
383
+ className: "border-border border-b p-4",
384
+ children: /* @__PURE__ */ jsxs("div", {
385
+ className: "flex flex-wrap items-center gap-3",
386
+ children: [/* @__PURE__ */ jsxs("div", {
387
+ className: "relative min-w-[12rem] flex-1",
388
+ children: [/* @__PURE__ */ jsx(Search, {
389
+ "aria-hidden": "true",
390
+ className: "pointer-events-none absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-muted-foreground"
391
+ }), /* @__PURE__ */ jsx(Input, {
392
+ type: "search",
393
+ value: filter,
394
+ onChange: (e) => setFilter(e.target.value),
395
+ placeholder: "Filter actions",
396
+ "aria-label": "Filter actions",
397
+ className: "pl-8"
398
+ })]
399
+ }), /* @__PURE__ */ jsxs("span", {
400
+ role: "status",
401
+ "aria-live": "polite",
402
+ className: "text-muted-foreground text-sm",
403
+ children: [
404
+ filtered.length,
405
+ " of ",
406
+ actions.length,
407
+ maybeTruncated ? "+" : "",
408
+ " actions"
409
+ ]
410
+ })]
411
+ })
412
+ }), filtered.length === 0 ? /* @__PURE__ */ jsxs("div", {
413
+ className: "p-6 text-center",
414
+ children: [/* @__PURE__ */ jsx("p", {
415
+ className: "text-muted-foreground text-sm",
416
+ children: "No actions match your filter."
417
+ }), /* @__PURE__ */ jsx(Button, {
418
+ type: "button",
419
+ variant: "outline",
420
+ size: "sm",
421
+ onClick: () => setFilter(""),
422
+ className: "mt-4",
423
+ children: "Clear filter"
424
+ })]
425
+ }) : /* @__PURE__ */ jsx("ul", {
426
+ className: "divide-y divide-border",
427
+ children: filtered.map(({ action, title }) => /* @__PURE__ */ jsx(ActionRow, {
428
+ action,
429
+ title,
430
+ expanded: expandedPath === action.path,
431
+ onToggle: () => setExpandedPath((current) => current === action.path ? null : action.path),
432
+ onBuildWithAssistant
433
+ }, action.path))
434
+ })]
435
+ });
425
436
  }
426
-
427
- // src/connectors/catalog-list.tsx
428
- import { Button as Button2, EmptyState, Input as Input2 } from "@tangle-network/ui/primitives";
429
- import { Blocks, ChevronRight as ChevronRight2, Search as Search2 } from "lucide-react";
430
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState3 } from "react";
431
- import { focusField } from "@tangle-network/ui/utils";
432
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
437
+ //#endregion
438
+ //#region src/connectors/catalog-list.tsx
433
439
  function optionsFor(entries, pick) {
434
- const map = /* @__PURE__ */ new Map();
435
- for (const entry of entries) {
436
- const { value, label } = pick(entry);
437
- if (value) map.set(value, label ?? value);
438
- }
439
- return [...map.entries()].map(([value, label]) => ({ value, label })).sort((a, b) => a.label.localeCompare(b.label));
440
+ const map = /* @__PURE__ */ new Map();
441
+ for (const entry of entries) {
442
+ const { value, label } = pick(entry);
443
+ if (value) map.set(value, label ?? value);
444
+ }
445
+ return [...map.entries()].map(([value, label]) => ({
446
+ value,
447
+ label
448
+ })).sort((a, b) => a.label.localeCompare(b.label));
440
449
  }
441
450
  function rowDetail(entry) {
442
- const parts = [];
443
- if (entry.category) parts.push(entry.categoryLabel ?? entry.category);
444
- if (entry.authKind) parts.push(entry.authKindLabel ?? entry.authKind);
445
- if (entry.actionCount != null) {
446
- parts.push(
447
- `${entry.actionCount} ${entry.actionCount === 1 ? "action" : "actions"}`
448
- );
449
- }
450
- if (entry.triggerCount != null && entry.triggerCount > 0) {
451
- parts.push(
452
- `${entry.triggerCount} trigger ${entry.triggerCount === 1 ? "event" : "events"}`
453
- );
454
- }
455
- return parts.join(" \xB7 ");
451
+ const parts = [];
452
+ if (entry.category) parts.push(entry.categoryLabel ?? entry.category);
453
+ if (entry.authKind) parts.push(entry.authKindLabel ?? entry.authKind);
454
+ if (entry.actionCount != null) parts.push(`${entry.actionCount} ${entry.actionCount === 1 ? "action" : "actions"}`);
455
+ if (entry.triggerCount != null && entry.triggerCount > 0) parts.push(`${entry.triggerCount} trigger ${entry.triggerCount === 1 ? "event" : "events"}`);
456
+ return parts.join(" · ");
456
457
  }
458
+ /** Neutral leading chip used when the host supplies no brand icon. */
457
459
  function FallbackIcon() {
458
- return /* @__PURE__ */ jsx3("div", { className: "flex size-9 shrink-0 items-center justify-center rounded-lg bg-surface-container-high", children: /* @__PURE__ */ jsx3(Blocks, { className: "size-5 text-muted-foreground" }) });
460
+ return /* @__PURE__ */ jsx("div", {
461
+ className: "flex size-9 shrink-0 items-center justify-center rounded-lg bg-surface-container-high",
462
+ children: /* @__PURE__ */ jsx(Blocks, { className: "size-5 text-muted-foreground" })
463
+ });
459
464
  }
460
- function FilterSelect({
461
- value,
462
- onChange,
463
- options,
464
- allLabel,
465
- ariaLabel
466
- }) {
467
- return /* @__PURE__ */ jsxs3(
468
- "select",
469
- {
470
- value,
471
- onChange: (e) => onChange(e.target.value),
472
- "aria-label": ariaLabel,
473
- className: `rounded-lg border bg-card px-3 py-2 text-foreground text-sm ${focusField}`,
474
- children: [
475
- /* @__PURE__ */ jsx3("option", { value: "", children: allLabel }),
476
- options.map((option) => /* @__PURE__ */ jsx3("option", { value: option.value, children: option.label }, option.value))
477
- ]
478
- }
479
- );
465
+ function FilterSelect({ value, onChange, options, allLabel, ariaLabel }) {
466
+ return /* @__PURE__ */ jsxs("select", {
467
+ value,
468
+ onChange: (e) => onChange(e.target.value),
469
+ "aria-label": ariaLabel,
470
+ className: `rounded-lg border bg-card px-3 py-2 text-foreground text-sm ${focusField}`,
471
+ children: [/* @__PURE__ */ jsx("option", {
472
+ value: "",
473
+ children: allLabel
474
+ }), options.map((option) => /* @__PURE__ */ jsx("option", {
475
+ value: option.value,
476
+ children: option.label
477
+ }, option.value))]
478
+ });
480
479
  }
481
- function ConnectorCatalogList({
482
- entries,
483
- getConnectorHref,
484
- onOpenConnector,
485
- renderIcon,
486
- onRequestIntegration,
487
- className
488
- }) {
489
- const [search, setSearch] = useState3("");
490
- const [categoryFilter, setCategoryFilter] = useState3("");
491
- const [authFilter, setAuthFilter] = useState3("");
492
- const categoryOptions = useMemo2(
493
- () => optionsFor(entries, (e) => ({
494
- value: e.category,
495
- label: e.categoryLabel
496
- })),
497
- [entries]
498
- );
499
- const authOptions = useMemo2(
500
- () => optionsFor(entries, (e) => ({
501
- value: e.authKind,
502
- label: e.authKindLabel
503
- })),
504
- [entries]
505
- );
506
- const filtered = useMemo2(() => {
507
- const q = search.trim().toLowerCase();
508
- return entries.filter((entry) => {
509
- if (categoryFilter && entry.category !== categoryFilter) return false;
510
- if (authFilter && entry.authKind !== authFilter) return false;
511
- if (!q) return true;
512
- return entry.title.toLowerCase().includes(q) || entry.providerId.toLowerCase().includes(q);
513
- }).sort((a, b) => a.title.localeCompare(b.title));
514
- }, [entries, search, categoryFilter, authFilter]);
515
- const clearFilters = () => {
516
- setSearch("");
517
- setCategoryFilter("");
518
- setAuthFilter("");
519
- };
520
- useEffect2(() => {
521
- if (categoryFilter && !categoryOptions.some((o) => o.value === categoryFilter)) {
522
- setCategoryFilter("");
523
- }
524
- }, [categoryOptions, categoryFilter]);
525
- useEffect2(() => {
526
- if (authFilter && !authOptions.some((o) => o.value === authFilter)) {
527
- setAuthFilter("");
528
- }
529
- }, [authOptions, authFilter]);
530
- return /* @__PURE__ */ jsxs3(
531
- "section",
532
- {
533
- className: cn(
534
- "overflow-hidden rounded-lg border border-border bg-card",
535
- className
536
- ),
537
- children: [
538
- entries.length > 0 && /* @__PURE__ */ jsx3("div", { className: "border-border border-b p-4", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-3", children: [
539
- /* @__PURE__ */ jsxs3("div", { className: "relative min-w-[15rem] flex-1", children: [
540
- /* @__PURE__ */ jsx3(
541
- Search2,
542
- {
543
- "aria-hidden": "true",
544
- className: "pointer-events-none absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-muted-foreground"
545
- }
546
- ),
547
- /* @__PURE__ */ jsx3(
548
- Input2,
549
- {
550
- type: "search",
551
- value: search,
552
- onChange: (e) => setSearch(e.target.value),
553
- placeholder: "Search connectors",
554
- "aria-label": "Search connectors",
555
- className: "pl-8"
556
- }
557
- )
558
- ] }),
559
- categoryOptions.length > 1 && /* @__PURE__ */ jsx3(
560
- FilterSelect,
561
- {
562
- value: categoryFilter,
563
- onChange: setCategoryFilter,
564
- options: categoryOptions,
565
- allLabel: "All categories",
566
- ariaLabel: "Filter by category"
567
- }
568
- ),
569
- authOptions.length > 1 && /* @__PURE__ */ jsx3(
570
- FilterSelect,
571
- {
572
- value: authFilter,
573
- onChange: setAuthFilter,
574
- options: authOptions,
575
- allLabel: "All auth",
576
- ariaLabel: "Filter by auth type"
577
- }
578
- ),
579
- /* @__PURE__ */ jsxs3(
580
- "span",
581
- {
582
- role: "status",
583
- "aria-live": "polite",
584
- className: "text-muted-foreground text-sm",
585
- children: [
586
- filtered.length,
587
- " of ",
588
- entries.length,
589
- " connectors"
590
- ]
591
- }
592
- ),
593
- onRequestIntegration && /* @__PURE__ */ jsx3(
594
- "button",
595
- {
596
- type: "button",
597
- onClick: () => onRequestIntegration(search.trim()),
598
- className: "text-[var(--accent-text)] text-sm transition-colors hover:underline",
599
- children: "Request an integration"
600
- }
601
- )
602
- ] }) }),
603
- entries.length === 0 ? /* @__PURE__ */ jsx3(
604
- EmptyState,
605
- {
606
- title: "No connectors",
607
- description: "No connectors are available yet."
608
- }
609
- ) : filtered.length === 0 ? /* @__PURE__ */ jsx3(
610
- EmptyState,
611
- {
612
- title: "No matches",
613
- description: "No connectors match your filters.",
614
- action: /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
615
- /* @__PURE__ */ jsx3(
616
- Button2,
617
- {
618
- type: "button",
619
- variant: "outline",
620
- size: "sm",
621
- onClick: clearFilters,
622
- children: "Clear filters"
623
- }
624
- ),
625
- onRequestIntegration && /* @__PURE__ */ jsxs3(
626
- Button2,
627
- {
628
- type: "button",
629
- size: "sm",
630
- onClick: () => onRequestIntegration(search.trim()),
631
- children: [
632
- "Request ",
633
- search.trim() ? `"${search.trim()}"` : "an integration"
634
- ]
635
- }
636
- )
637
- ] })
638
- }
639
- ) : /* @__PURE__ */ jsx3("ul", { className: "divide-y divide-border", children: filtered.map((entry) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsxs3(
640
- "a",
641
- {
642
- href: getConnectorHref(entry.providerId),
643
- onClick: (e) => {
644
- if (onOpenConnector && e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) {
645
- e.preventDefault();
646
- onOpenConnector(entry.providerId);
647
- }
648
- },
649
- className: "flex items-center justify-between gap-4 p-4 transition-colors hover:bg-surface-container-high",
650
- children: [
651
- /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-3", children: [
652
- renderIcon ? renderIcon(entry.providerId) : /* @__PURE__ */ jsx3(FallbackIcon, {}),
653
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
654
- /* @__PURE__ */ jsx3("p", { className: "font-medium text-foreground", children: entry.title }),
655
- /* @__PURE__ */ jsx3("p", { className: "truncate text-muted-foreground text-sm", children: rowDetail(entry) })
656
- ] })
657
- ] }),
658
- /* @__PURE__ */ jsxs3("div", { className: "flex shrink-0 items-center gap-3", children: [
659
- entry.connected && /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground text-sm", children: [
660
- /* @__PURE__ */ jsx3("span", { className: "size-2 rounded-full bg-[var(--surface-success-text)]" }),
661
- "Connected"
662
- ] }),
663
- /* @__PURE__ */ jsx3(
664
- ChevronRight2,
665
- {
666
- "aria-hidden": "true",
667
- className: "h-4 w-4 text-muted-foreground"
668
- }
669
- )
670
- ] })
671
- ]
672
- }
673
- ) }, entry.providerId)) })
674
- ]
675
- }
676
- );
480
+ /**
481
+ * Searchable/filterable browse list over every connector the host exposes.
482
+ * Each row links into the connector's detail surface; connect/disconnect and
483
+ * data fetching stay with the host — this list is for discovery.
484
+ */
485
+ function ConnectorCatalogList({ entries, getConnectorHref, onOpenConnector, renderIcon, onRequestIntegration, className }) {
486
+ const [search, setSearch] = useState("");
487
+ const [categoryFilter, setCategoryFilter] = useState("");
488
+ const [authFilter, setAuthFilter] = useState("");
489
+ const categoryOptions = useMemo(() => optionsFor(entries, (e) => ({
490
+ value: e.category,
491
+ label: e.categoryLabel
492
+ })), [entries]);
493
+ const authOptions = useMemo(() => optionsFor(entries, (e) => ({
494
+ value: e.authKind,
495
+ label: e.authKindLabel
496
+ })), [entries]);
497
+ const filtered = useMemo(() => {
498
+ const q = search.trim().toLowerCase();
499
+ return entries.filter((entry) => {
500
+ if (categoryFilter && entry.category !== categoryFilter) return false;
501
+ if (authFilter && entry.authKind !== authFilter) return false;
502
+ if (!q) return true;
503
+ return entry.title.toLowerCase().includes(q) || entry.providerId.toLowerCase().includes(q);
504
+ }).sort((a, b) => a.title.localeCompare(b.title));
505
+ }, [
506
+ entries,
507
+ search,
508
+ categoryFilter,
509
+ authFilter
510
+ ]);
511
+ const clearFilters = () => {
512
+ setSearch("");
513
+ setCategoryFilter("");
514
+ setAuthFilter("");
515
+ };
516
+ useEffect(() => {
517
+ if (categoryFilter && !categoryOptions.some((o) => o.value === categoryFilter)) setCategoryFilter("");
518
+ }, [categoryOptions, categoryFilter]);
519
+ useEffect(() => {
520
+ if (authFilter && !authOptions.some((o) => o.value === authFilter)) setAuthFilter("");
521
+ }, [authOptions, authFilter]);
522
+ return /* @__PURE__ */ jsxs("section", {
523
+ className: cn$1("overflow-hidden rounded-lg border border-border bg-card", className),
524
+ children: [entries.length > 0 && /* @__PURE__ */ jsx("div", {
525
+ className: "border-border border-b p-4",
526
+ children: /* @__PURE__ */ jsxs("div", {
527
+ className: "flex flex-wrap items-center gap-3",
528
+ children: [
529
+ /* @__PURE__ */ jsxs("div", {
530
+ className: "relative min-w-[15rem] flex-1",
531
+ children: [/* @__PURE__ */ jsx(Search, {
532
+ "aria-hidden": "true",
533
+ className: "pointer-events-none absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-muted-foreground"
534
+ }), /* @__PURE__ */ jsx(Input, {
535
+ type: "search",
536
+ value: search,
537
+ onChange: (e) => setSearch(e.target.value),
538
+ placeholder: "Search connectors",
539
+ "aria-label": "Search connectors",
540
+ className: "pl-8"
541
+ })]
542
+ }),
543
+ categoryOptions.length > 1 && /* @__PURE__ */ jsx(FilterSelect, {
544
+ value: categoryFilter,
545
+ onChange: setCategoryFilter,
546
+ options: categoryOptions,
547
+ allLabel: "All categories",
548
+ ariaLabel: "Filter by category"
549
+ }),
550
+ authOptions.length > 1 && /* @__PURE__ */ jsx(FilterSelect, {
551
+ value: authFilter,
552
+ onChange: setAuthFilter,
553
+ options: authOptions,
554
+ allLabel: "All auth",
555
+ ariaLabel: "Filter by auth type"
556
+ }),
557
+ /* @__PURE__ */ jsxs("span", {
558
+ role: "status",
559
+ "aria-live": "polite",
560
+ className: "text-muted-foreground text-sm",
561
+ children: [
562
+ filtered.length,
563
+ " of ",
564
+ entries.length,
565
+ " connectors"
566
+ ]
567
+ }),
568
+ onRequestIntegration && /* @__PURE__ */ jsx("button", {
569
+ type: "button",
570
+ onClick: () => onRequestIntegration(search.trim()),
571
+ className: "text-[var(--accent-text)] text-sm transition-colors hover:underline",
572
+ children: "Request an integration"
573
+ })
574
+ ]
575
+ })
576
+ }), entries.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
577
+ title: "No connectors",
578
+ description: "No connectors are available yet."
579
+ }) : filtered.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
580
+ title: "No matches",
581
+ description: "No connectors match your filters.",
582
+ action: /* @__PURE__ */ jsxs("div", {
583
+ className: "flex flex-wrap items-center justify-center gap-2",
584
+ children: [/* @__PURE__ */ jsx(Button, {
585
+ type: "button",
586
+ variant: "outline",
587
+ size: "sm",
588
+ onClick: clearFilters,
589
+ children: "Clear filters"
590
+ }), onRequestIntegration && /* @__PURE__ */ jsxs(Button, {
591
+ type: "button",
592
+ size: "sm",
593
+ onClick: () => onRequestIntegration(search.trim()),
594
+ children: ["Request ", search.trim() ? `"${search.trim()}"` : "an integration"]
595
+ })]
596
+ })
597
+ }) : /* @__PURE__ */ jsx("ul", {
598
+ className: "divide-y divide-border",
599
+ children: filtered.map((entry) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
600
+ href: getConnectorHref(entry.providerId),
601
+ onClick: (e) => {
602
+ if (onOpenConnector && e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) {
603
+ e.preventDefault();
604
+ onOpenConnector(entry.providerId);
605
+ }
606
+ },
607
+ className: "flex items-center justify-between gap-4 p-4 transition-colors hover:bg-surface-container-high",
608
+ children: [/* @__PURE__ */ jsxs("div", {
609
+ className: "flex min-w-0 items-center gap-3",
610
+ children: [renderIcon ? renderIcon(entry.providerId) : /* @__PURE__ */ jsx(FallbackIcon, {}), /* @__PURE__ */ jsxs("div", {
611
+ className: "min-w-0",
612
+ children: [/* @__PURE__ */ jsx("p", {
613
+ className: "font-medium text-foreground",
614
+ children: entry.title
615
+ }), /* @__PURE__ */ jsx("p", {
616
+ className: "truncate text-muted-foreground text-sm",
617
+ children: rowDetail(entry)
618
+ })]
619
+ })]
620
+ }), /* @__PURE__ */ jsxs("div", {
621
+ className: "flex shrink-0 items-center gap-3",
622
+ children: [entry.connected && /* @__PURE__ */ jsxs("span", {
623
+ className: "inline-flex items-center gap-1.5 text-muted-foreground text-sm",
624
+ children: [/* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-[var(--surface-success-text)]" }), "Connected"]
625
+ }), /* @__PURE__ */ jsx(ChevronRight, {
626
+ "aria-hidden": "true",
627
+ className: "h-4 w-4 text-muted-foreground"
628
+ })]
629
+ })]
630
+ }) }, entry.providerId))
631
+ })]
632
+ });
677
633
  }
678
- export {
679
- ConnectorActionList,
680
- ConnectorCatalogList,
681
- SchemaTable,
682
- integrationStepYaml
683
- };
634
+ //#endregion
635
+ export { ConnectorActionList, ConnectorCatalogList, SchemaTable, integrationStepYaml };