@w6w/ui 0.2.0 → 0.2.11

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 (78) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/AppPicker.d.ts +47 -2
  3. package/dist/CanvasLayer.d.ts +38 -0
  4. package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-BqXDA35P.js} +2 -2
  5. package/dist/{CodeBlock-Dot3Pd7P.js.map → CodeBlock-BqXDA35P.js.map} +1 -1
  6. package/dist/{CodeBlock-CZu4TFSi.cjs → CodeBlock-CulfbnZD.cjs} +2 -2
  7. package/dist/{CodeBlock-CZu4TFSi.cjs.map → CodeBlock-CulfbnZD.cjs.map} +1 -1
  8. package/dist/ConfirmModal-Ln99S9NT.js +7919 -0
  9. package/dist/ConfirmModal-Ln99S9NT.js.map +1 -0
  10. package/dist/ConfirmModal-fjHW5rN-.cjs +14 -0
  11. package/dist/ConfirmModal-fjHW5rN-.cjs.map +1 -0
  12. package/dist/Copyable-DA6eiU7w.cjs +2 -0
  13. package/dist/Copyable-DA6eiU7w.cjs.map +1 -0
  14. package/dist/{Copyable-1niV9gUa.js → Copyable-F4NTaG77.js} +91 -15
  15. package/dist/Copyable-F4NTaG77.js.map +1 -0
  16. package/dist/EditConnectionModal.d.ts +6 -1
  17. package/dist/JsonEditor.d.ts +8 -0
  18. package/dist/Markdown.d.ts +25 -0
  19. package/dist/ParamsForm.d.ts +7 -1
  20. package/dist/StepBuilderModal.d.ts +23 -5
  21. package/dist/WorkflowFlowEditor.d.ts +14 -3
  22. package/dist/app-pages.d.ts +2 -0
  23. package/dist/canvas-types.d.ts +36 -0
  24. package/dist/canvas-utils.d.ts +44 -0
  25. package/dist/code.cjs +1 -1
  26. package/dist/code.mjs +2 -2
  27. package/dist/components/AuthFieldsForm.d.ts +11 -0
  28. package/dist/components/BudgetMeter.d.ts +15 -0
  29. package/dist/components/CallableList.d.ts +3 -2
  30. package/dist/components/Combobox.d.ts +8 -1
  31. package/dist/components/ConnectionList.d.ts +2 -3
  32. package/dist/components/CredentialPicker.d.ts +35 -0
  33. package/dist/components/DataTable.d.ts +27 -0
  34. package/dist/components/EmptyState.d.ts +9 -0
  35. package/dist/components/icons.d.ts +1 -1
  36. package/dist/dynamic-fields.d.ts +60 -0
  37. package/dist/editor-theme.d.ts +24 -0
  38. package/dist/flow-fanout.d.ts +2 -2
  39. package/dist/flow-types.d.ts +18 -1
  40. package/dist/flow-utils.d.ts +10 -3
  41. package/dist/flow.cjs +2 -2
  42. package/dist/flow.cjs.map +1 -1
  43. package/dist/flow.d.ts +9 -0
  44. package/dist/flow.mjs +1519 -1138
  45. package/dist/flow.mjs.map +1 -1
  46. package/dist/index.cjs +1 -1
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.ts +14 -2
  49. package/dist/index.mjs +1362 -1123
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/note-markdown.d.ts +22 -0
  52. package/dist/provider.d.ts +56 -7
  53. package/dist/resolve-params.d.ts +5 -0
  54. package/dist/run-visuals.d.ts +4 -1
  55. package/dist/step-problems.d.ts +34 -0
  56. package/dist/types.d.ts +109 -3
  57. package/dist/use-dynamic-fields.d.ts +23 -0
  58. package/package.json +4 -2
  59. package/src/styles/_budget-meter.scss +46 -0
  60. package/src/styles/_callable-list.scss +15 -6
  61. package/src/styles/_canvas.scss +318 -0
  62. package/src/styles/_connections.scss +2 -14
  63. package/src/styles/_data-table.scss +55 -0
  64. package/src/styles/_empty-state.scss +21 -0
  65. package/src/styles/_flow.scss +11 -0
  66. package/src/styles/_fx.scss +23 -0
  67. package/src/styles/_layout.scss +10 -0
  68. package/src/styles/_markdown.scss +20 -0
  69. package/src/styles/_tester.scss +1 -14
  70. package/src/styles.css +572 -39
  71. package/src/styles.scss +5 -0
  72. package/dist/ConfirmModal-ChoiGy2K.cjs +0 -11
  73. package/dist/ConfirmModal-ChoiGy2K.cjs.map +0 -1
  74. package/dist/ConfirmModal-Wx3i8F1n.js +0 -6359
  75. package/dist/ConfirmModal-Wx3i8F1n.js.map +0 -1
  76. package/dist/Copyable-1niV9gUa.js.map +0 -1
  77. package/dist/Copyable-CYZU1o4X.cjs +0 -2
  78. package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
@@ -0,0 +1,25 @@
1
+ export interface MarkdownProps {
2
+ content: string;
3
+ className?: string;
4
+ }
5
+ /**
6
+ * Pass a URL through only when its *parsed* protocol is allowlisted; relative,
7
+ * unparseable and every other scheme (javascript:, data:, vbscript:, any casing
8
+ * or whitespace trick the URL parser normalises) collapse to "".
9
+ */
10
+ export declare function safeUrl(url: string): string;
11
+ /**
12
+ * A leading `---` YAML block and the Markdown after it. Rendered as Markdown the block would
13
+ * come out as a rule plus a heading; it is metadata, so `Markdown` shows it in a collapsed
14
+ * section instead. Kept as text, not parsed — this package carries no YAML parser.
15
+ */
16
+ export declare function splitFrontmatter(text: string): {
17
+ frontmatter: string | null;
18
+ body: string;
19
+ };
20
+ /**
21
+ * Renders imported, attacker-controllable note content. No `rehype-raw`: raw HTML
22
+ * is dropped (`skipHtml`), images are not rendered, and links are protocol-allowlisted.
23
+ * A leading YAML frontmatter block becomes a collapsed section above the body.
24
+ */
25
+ export declare function Markdown({ content, className }: MarkdownProps): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { DynamicContext } from './dynamic-fields.ts';
1
2
  import { ActionParam } from './types.ts';
2
3
  /**
3
4
  * Evaluate a param's `showIf` predicate. `getValue` resolves a sibling field's
@@ -29,6 +30,11 @@ export interface ParamsFormProps {
29
30
  /** Fired with the next values object on every edit. */
30
31
  onChange: (values: Record<string, unknown>) => void;
31
32
  readOnly?: boolean;
33
+ /**
34
+ * Opt-in context for `options: { source: "resolveFields" }` params. Absent (or
35
+ * the host has no `resolveActionFields`) = those params render as plain inputs.
36
+ */
37
+ dynamic?: DynamicContext;
32
38
  }
33
39
  /**
34
40
  * Renders an action's declared params as a form. Required params are always
@@ -40,7 +46,7 @@ export interface ParamsFormProps {
40
46
  * expression bindings (`{ $: "steps.x.output.y" }`) authors drop to the JSON
41
47
  * view; this form deals in literals.
42
48
  */
43
- export declare function ParamsForm({ params, values, onChange, readOnly }: ParamsFormProps): import("react").JSX.Element;
49
+ export declare function ParamsForm({ params, values, onChange, readOnly, dynamic }: ParamsFormProps): import("react").JSX.Element;
44
50
  /**
45
51
  * One typed key/value entry in a `vars` param. `type` is a real value type only:
46
52
  * `expression` is a binding *mode* (see {@link FxField}/`ExprValue`), not a value
@@ -2,7 +2,7 @@ import { NodeConfig } from './NodeConfigForm.tsx';
2
2
  import { ExpressionStepSource } from './components/ExpressionOptions.tsx';
3
3
  import { InternalNodeDef } from './flow-types.ts';
4
4
  import { StepStartState } from './provider.tsx';
5
- import { ActionParam, AppSummary, FunctionSummary, ThemeMode, WorkflowSummary } from './types.ts';
5
+ import { ActionParam, AppSummary, FunctionSummary, ThemeMode, WorkflowDetail, WorkflowSummary } from './types.ts';
6
6
  /** The step the builder emits — the editor assigns the final `id`. `NodeConfig`
7
7
  * carries the base settings (retry / onError / notes) set on the Config view. */
8
8
  export interface BuiltStep extends NodeConfig {
@@ -27,7 +27,7 @@ export interface StepBuilderModalProps {
27
27
  * `onAdd` — keeps the already-added node current without minting a second
28
28
  * one. `id` is the id `onAdd` returned. Progressive commit (mint-then-update)
29
29
  * only engages when this is supplied; omitted callers (the Functions/
30
- * Endpoints pickers) keep the original one-shot "Add step" behavior.
30
+ * Endpoints pickers) keep the original one-shot "Add node" behavior.
31
31
  */
32
32
  onDraftChange?: (id: string, step: BuiltStep) => void;
33
33
  theme?: ThemeMode;
@@ -59,7 +59,7 @@ export interface StepBuilderModalProps {
59
59
  * value lives instead of making the author re-find the tab.
60
60
  */
61
61
  initialTab?: "connected" | "functions" | "workflows";
62
- /** Modal heading. Defaults to "Add a step". */
62
+ /** Modal heading. Defaults to "Add a node". */
63
63
  title?: string;
64
64
  /**
65
65
  * Workflow-step context, when the builder is opened for a step that already
@@ -114,6 +114,11 @@ export interface StepBuilderModalProps {
114
114
  * and the selected-app header omits the ` · v<version>` suffix for it.
115
115
  */
116
116
  appsFilter?: (app: AppSummary) => boolean;
117
+ /**
118
+ * Server-side id allow-list for the Apps-tab picker (forwarded as `ids` to the
119
+ * paged fetch), so the footer total equals the cards shown.
120
+ */
121
+ appsIds?: string[];
117
122
  /**
118
123
  * Stop at "a connection is chosen" — no Action, no Configure/Test. Renders
119
124
  * {@link AppConnectionOnlyConfig} in place of the usual `AppStepConfig`
@@ -126,6 +131,13 @@ export interface StepBuilderModalProps {
126
131
  * selection alone. Only meaningful with {@link StepBuilderModalProps.connectionOnly}.
127
132
  */
128
133
  onConnected?: (connectionId: string, app: AppSummary) => void;
134
+ /**
135
+ * Callable ids (`fn_…` / `wf_…`) the Functions and Workflows tabs, and the
136
+ * "Ready to use" tab, never offer — matched by id equality only. A Function
137
+ * editor passes its own id: a Function bound to itself has no state change,
138
+ * so the only outcome is an infinite loop. Absent ⇒ nothing is excluded.
139
+ */
140
+ excludeCallableIds?: readonly string[];
129
141
  }
130
142
  /** The four representations of the Configure tab: form, full-step JSON,
131
143
  * params-only JSON, node settings. */
@@ -155,7 +167,7 @@ export declare function ConfigViewToggle({ view, onChange, disabled, views, }: {
155
167
  *
156
168
  * Data + IO come from `useW6WApi()`, so mount it under `<W6WUIProvider>`.
157
169
  */
158
- export declare function StepBuilderModal({ onClose, onAdd, onDraftChange, theme, appsOnly, callables, initialTab, title, workflowId, stepId, upstreamSteps, initialApp, initialAction, initialConnection, initialWith, appsFilter, connectionOnly, onConnected, }: StepBuilderModalProps): import("react").JSX.Element;
170
+ export declare function StepBuilderModal({ onClose, onAdd, onDraftChange, theme, appsOnly, callables, initialTab, title, workflowId, stepId, upstreamSteps, initialApp, initialAction, initialConnection, initialWith, appsFilter, appsIds, connectionOnly, onConnected, excludeCallableIds, }: StepBuilderModalProps): import("react").JSX.Element;
159
171
  /**
160
172
  * Config form for a chosen internal node — its schema rendered through the same
161
173
  * `ParamsForm` as app actions, seeded with the node's defaults. Emits the built
@@ -171,6 +183,7 @@ export declare function ControlStepConfig({ node, onAdd, onClose, onDraftChange,
171
183
  /** The new step's known graph ancestors — see {@link StepBuilderModalProps.upstreamSteps}. */
172
184
  upstreamSteps?: ExpressionStepSource[];
173
185
  }): import("react").JSX.Element;
186
+ export declare function triggerFieldsOf(wf: WorkflowDetail): ActionParam[];
174
187
  /**
175
188
  * Config → test → add flow for a picked Function/Workflow target (D-12,
176
189
  * D-15). Mints a single `@w6w/call` step — `family` only decides which
@@ -331,7 +344,12 @@ export declare function clampMinutes(raw: string, min: number): number;
331
344
  *
332
345
  * Exported for tests only (not re-exported from the package index).
333
346
  */
334
- export declare function useReadyToUse(callables: readonly ("function" | "workflow")[], appsFilter?: (app: AppSummary) => boolean): ReadyToUse;
347
+ export declare function useReadyToUse(callables: readonly ("function" | "workflow")[], appsFilter?: (app: AppSummary) => boolean, excludeIds?: readonly string[], appsIds?: readonly string[]): ReadyToUse;
348
+ /** A copy of `items` without any entry whose `id` equals one of `ids` (id
349
+ * equality only — never name or position). No `ids` ⇒ an unchanged copy. */
350
+ export declare function withoutIds<T extends {
351
+ id: string;
352
+ }>(items: readonly T[], ids?: readonly string[]): T[];
335
353
  export declare function AppStepConfig({ appId, app, onAdd, onClose, onDraftChange, onChangeApp, theme, workflowId, stepId, upstreamSteps, initialAction, initialConnection, initialWith, }: {
336
354
  appId: string;
337
355
  app?: AppSummary;
@@ -1,6 +1,7 @@
1
1
  import { ExpressionOptions, ExpressionStepSource } from './components/ExpressionOptions.tsx';
2
2
  import { FlowStep, FlowWorkflow } from './flow-types.ts';
3
3
  import { RunState } from './run-visuals.ts';
4
+ import { StepProblem, StepProblems } from './step-problems.ts';
4
5
  import { AppSummary } from './types.ts';
5
6
  export interface WorkflowFlowEditorProps {
6
7
  /** The workflow being edited. The editor re-derives layout when this changes identity. */
@@ -59,6 +60,14 @@ export interface WorkflowFlowEditorProps {
59
60
  * back off a node.
60
61
  */
61
62
  runState?: RunState;
63
+ /**
64
+ * Host-detected per-step problems, keyed by step id — an app step whose bound
65
+ * connection no longer exists gets an error ring + tooltip. Omitted ⇒ the
66
+ * canvas renders exactly as today. The host owns detection; this editor never
67
+ * infers a problem itself. Repaints live (same `value.id`) and is never
68
+ * persisted: `flowToWorkflow` never reads `data.stepProblem`.
69
+ */
70
+ stepProblems?: StepProblems;
62
71
  /**
63
72
  * Fired the instant a step test starts — the ▶ collect form's Run button, or
64
73
  * `StepEditModal`'s footer Test button — before the invoke goes out, so a host
@@ -95,14 +104,14 @@ type EditView = "props" | "json" | "settings";
95
104
  * - Auto-layouted on load (topological columns + sibling rows).
96
105
  * - Nodes render differently for action steps vs. control steps
97
106
  * (`uses.app === "@w6w/control"`).
98
- * - `+ Step` opens a guided builder (pick app → connection → action → params,
107
+ * - `+ Node` opens a guided builder (pick app → connection → action → params,
99
108
  * or a flow control). Drag nodes to reposition; connect handles to add edges.
100
109
  * - Selecting a node reveals a toolbar above it: Edit / Duplicate / Delete.
101
110
  * - Edit opens a modal with a Form ⇄ JSON toggle for that step.
102
111
  * - Every meaningful change fires `onChange` with an updated Workflow.
103
112
  */
104
113
  export declare function WorkflowFlowEditor(props: WorkflowFlowEditorProps): import("react").JSX.Element;
105
- export declare function StepEditModal({ workflowId, step: initialStep, upstreamSteps, onChange, onClose, readOnly, initialView, fanOutCount, onTestRun, }: {
114
+ export declare function StepEditModal({ workflowId, step: initialStep, upstreamSteps, onChange, onClose, readOnly, initialView, fanOutCount, onTestRun, stepProblem, }: {
106
115
  workflowId: string;
107
116
  step: FlowStep;
108
117
  /** Graph ancestors of this step (from `upstreamStateSources`); the incoming-state picker seeds from their saved tests. */
@@ -111,9 +120,11 @@ export declare function StepEditModal({ workflowId, step: initialStep, upstreamS
111
120
  onClose: () => void;
112
121
  readOnly?: boolean;
113
122
  initialView?: EditView;
114
- /** Most outgoing edges this step has in one lane; "Run next steps" needs >= 2. */
123
+ /** Most outgoing edges this node has in one lane; "Run next nodes" needs >= 2. */
115
124
  fanOutCount?: number;
116
125
  /** Fired the instant the footer Test button is clicked, before the test runs (T1.1.1). */
117
126
  onTestRun?: () => void;
127
+ /** Host-supplied problem for this step; a dangling connection locks Configure/Test. */
128
+ stepProblem?: StepProblem;
118
129
  }): import("react").JSX.Element;
119
130
  export {};
@@ -5,6 +5,8 @@ export interface AppsPageQuery {
5
5
  q: string;
6
6
  /** Server-side category filter (e.g. `"ai"` for the AI tab), or absent. */
7
7
  category?: string;
8
+ /** Server-side id allow-list, or absent. */
9
+ ids?: string[];
8
10
  }
9
11
  /**
10
12
  * Two queries are the same fetch target when both fields match exactly
@@ -0,0 +1,36 @@
1
+ /** Workflow canvas (notes + sections); hand-mirror of core's `Workflow.canvas`. */
2
+ export interface CanvasNote {
3
+ id: string;
4
+ color: string;
5
+ position: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ size: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ content: string;
14
+ }
15
+ export interface CanvasSection {
16
+ id: string;
17
+ title: string;
18
+ color: string;
19
+ position: {
20
+ x: number;
21
+ y: number;
22
+ };
23
+ size: {
24
+ width: number;
25
+ height: number;
26
+ };
27
+ /** Member node ids; a node belongs to at most one section. */
28
+ nodes: string[];
29
+ }
30
+ export interface FlowCanvas {
31
+ notes?: CanvasNote[];
32
+ sections?: CanvasSection[];
33
+ }
34
+ export declare const MAX_NOTES = 200;
35
+ export declare const MAX_SECTIONS = 200;
36
+ export declare const MAX_NOTE_BYTES = 10240;
@@ -0,0 +1,44 @@
1
+ import { FlowCanvas } from './canvas-types.ts';
2
+ /** Live geometry keyed by note/section id. */
3
+ export interface LiveGeometry {
4
+ [id: string]: {
5
+ position?: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ size?: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ };
14
+ }
15
+ /** Round every stored number to an integer. */
16
+ export declare function roundGeometry<T extends {
17
+ x: number;
18
+ y: number;
19
+ } | {
20
+ width: number;
21
+ height: number;
22
+ }>(g: T): T;
23
+ /**
24
+ * Stamp live geometry onto the stored canvas, rounded to integers. When
25
+ * `savePosition === false` the stored canvas is returned untouched (mirrors
26
+ * `flowToWorkflow`'s `withPosition`): not written is not erased.
27
+ */
28
+ export declare function withCanvasGeometry(canvas: FlowCanvas, savePosition: boolean, live?: LiveGeometry): FlowCanvas;
29
+ /** Remove a deleted node id from every section. */
30
+ export declare function pruneSectionMembers(canvas: FlowCanvas, deletedNodeId: string): FlowCanvas;
31
+ /** Map a renamed node id in every section. */
32
+ export declare function renameSectionMember(canvas: FlowCanvas, from: string, to: string): FlowCanvas;
33
+ /** Put `nodeIds` into `sectionId`, removing them from any other section (at most one each). */
34
+ export declare function assignMembership(canvas: FlowCanvas, sectionId: string, nodeIds: string[]): FlowCanvas;
35
+ /** Ids of every section containing the node. */
36
+ export declare function sectionsContaining(canvas: FlowCanvas, nodeId: string): string[];
37
+ /** Enforce the caps: 200 notes, 200 sections, 10240 UTF-8 bytes per note content. */
38
+ export declare function clampCanvas(canvas: FlowCanvas): FlowCanvas;
39
+ /**
40
+ * The canvas to emit. Geometry is stamped (rounded) from the live canvas when `savePosition` is
41
+ * on; when off, every item already in `stored` keeps its STORED position/size (live edits render
42
+ * but are not written — mirrors node positions). Items new this session keep their live geometry.
43
+ */
44
+ export declare function persistCanvas(canvas: FlowCanvas, stored: FlowCanvas | undefined, savePosition: boolean): FlowCanvas;
package/dist/code.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./CodeBlock-CZu4TFSi.cjs"),o=require("./Copyable-CYZU1o4X.cjs");exports.CodeBlock=e.CodeBlock;exports.Copyable=o.Copyable;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./CodeBlock-CulfbnZD.cjs"),o=require("./Copyable-DA6eiU7w.cjs");exports.CodeBlock=e.CodeBlock;exports.Copyable=o.Copyable;
2
2
  //# sourceMappingURL=code.cjs.map
package/dist/code.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { C as r } from "./CodeBlock-Dot3Pd7P.js";
3
- import { C as a } from "./Copyable-1niV9gUa.js";
2
+ import { C as r } from "./CodeBlock-BqXDA35P.js";
3
+ import { C as a } from "./Copyable-F4NTaG77.js";
4
4
  export {
5
5
  r as CodeBlock,
6
6
  a as Copyable
@@ -21,6 +21,17 @@ interface Props {
21
21
  */
22
22
  mode?: "create" | "edit";
23
23
  }
24
+ /**
25
+ * Parses every filled `json` field of `values` (they are held as raw text while typing). Returns
26
+ * the values with those fields parsed, or an `error` naming the offending field by label. The
27
+ * error NEVER contains the typed text or the parser's message (which can quote it): a pasted key
28
+ * file that is almost valid must not be echoed back into the page.
29
+ */
30
+ export declare function parseJsonFields(fields: AuthField[], values: Record<string, unknown>): {
31
+ values: Record<string, unknown>;
32
+ } | {
33
+ error: string;
34
+ };
24
35
  /**
25
36
  * Renders the form for an Auth method's `fields` declaration. Field shape
26
37
  * comes from the app's manifest so no app-specific knowledge is needed here.
@@ -0,0 +1,15 @@
1
+ export interface BudgetMeterProps {
2
+ /** Accessible name and the visible caption. */
3
+ label: string;
4
+ /** Amount already consumed. */
5
+ used: number;
6
+ /** Upper bound `used` is measured against. */
7
+ limit: number;
8
+ /** Amount held back but not yet spent. The segment renders only when > 0. */
9
+ reserved?: number;
10
+ /** Formats every number the meter prints (visible text and aria-valuetext). */
11
+ format?: (n: number) => string;
12
+ className?: string;
13
+ testId?: string;
14
+ }
15
+ export declare function BudgetMeter({ label, used, limit, reserved, format, className, testId, }: BudgetMeterProps): import("react").JSX.Element;
@@ -14,7 +14,7 @@ export interface CallableListProps {
14
14
  rows: readonly CallableListRow[];
15
15
  title: ReactNode;
16
16
  description?: ReactNode;
17
- /** Shown when `rows` is empty (total, not the visible subset) and not `loading`. */
17
+ /** Shown (inside an EmptyState) when no rows exist at all and not `loading`. */
18
18
  emptyCopy?: ReactNode;
19
19
  /** Label of the New button. Defaults to "New". */
20
20
  newLabel?: string;
@@ -52,6 +52,7 @@ export interface CallableListProps {
52
52
  /**
53
53
  * A presentational list of callables (Functions, Endpoints, Workflows): rows in, callbacks out.
54
54
  * Owns only the archived filter and its toggle; it knows no router, query, modal or kind.
55
- * Rows render through {@link ListItem}: a button with `onOpen`, an anchor with `getHref`.
55
+ * Rows render through {@link DataTable}: a click handler with `onOpen`, anchors with `getHref`.
56
+ * No visible rows renders an {@link EmptyState}; when only archived rows exist it offers "Show archived".
56
57
  */
57
58
  export declare function CallableList({ rows, title, description, emptyCopy, newLabel, openRowTitle, testIds, loading, error, onCreate, creating, onImport, onOpen, getHref, showArchived: showArchivedProp, onShowArchivedChange, slots, }: CallableListProps): import("react").JSX.Element;
@@ -15,10 +15,17 @@ export interface ComboboxProps {
15
15
  forceSelection?: boolean;
16
16
  /** Estimated option height in px, used by `auto` mode. Default 32. */
17
17
  optionHeight?: number;
18
+ /**
19
+ * Cap for the open list, in px — the list scrolls past it instead of growing.
20
+ * Default 240 (the shared value). A host inside an already-scrolling container
21
+ * (a modal body) passes a smaller one so the popup cannot add a second scroller.
22
+ */
23
+ listMaxHeight?: number;
18
24
  className?: string;
19
25
  }
20
26
  /** Combobox is chosen when the list would take more than 60% of the viewport. */
21
27
  export declare function shouldUseCombobox(count: number, optionHeight: number, viewportHeight: number): boolean;
22
28
  /** Case-insensitive substring match on label and value. */
23
29
  export declare function filterOptions(options: ComboboxOption[], query: string): ComboboxOption[];
24
- export declare function Combobox({ options, value, onChange, placeholder, disabled, "aria-label": ariaLabel, mode, forceSelection, optionHeight, className, }: ComboboxProps): import("react").JSX.Element;
30
+ export declare const DEFAULT_LIST_MAX_HEIGHT = 240;
31
+ export declare function Combobox({ options, value, onChange, placeholder, disabled, "aria-label": ariaLabel, mode, forceSelection, optionHeight, listMaxHeight, className, }: ComboboxProps): import("react").JSX.Element;
@@ -33,9 +33,8 @@ export interface ConnectionListProps {
33
33
  };
34
34
  }
35
35
  /**
36
- * A presentational list of connections: rows in, callbacks out; it needs no provider. Each row is
37
- * a {@link ListItem} (app icon, name, auth key, state, an `invalid` tag after a failed test) with
38
- * Edit and Delete buttons as SIBLINGS of its link, never inside it.
36
+ * A presentational list of connections: rows in, callbacks out; it needs no provider. Rows render
37
+ * through {@link DataTable} (name/app, status, and an Edit/Delete column that is never a link).
39
38
  *
40
39
  * A `builtIn` row (the server's synthetic "no credential" entry) is a plain non-interactive item:
41
40
  * no link, no `onOpen`, no Edit, no Delete, because the server 404s get/test/delete on its id.
@@ -0,0 +1,35 @@
1
+ import { AuthField, ConnectionBinding, CredentialSummary, ServiceAccountConfig } from '../types.ts';
2
+ export interface CredentialPickerProps {
3
+ /** Credentials to offer. Pass them already filtered; this component renders them as given. */
4
+ credentials: CredentialSummary[];
5
+ /** `"existing"` picks one of `credentials`; `"new"` is for the caller's create form. */
6
+ mode: "existing" | "new";
7
+ onModeChange: (mode: "existing" | "new") => void;
8
+ /** The chosen credential id (`""` for none). Only used in `"existing"` mode. */
9
+ selectedId: string;
10
+ onSelect: (id: string) => void;
11
+ /** False hides the "Use existing" choice (nothing to choose from). Default true. */
12
+ canUseExisting?: boolean;
13
+ /** The contract the credential must satisfy, shown as a hint. */
14
+ contract?: string;
15
+ }
16
+ /**
17
+ * The Use existing / Create new switch of a connection's Credential step, plus the list of stored
18
+ * credentials. Presentational: no API access, no state of its own. Each option shows the
19
+ * credential's name and its non-secret `display` values, never a payload. The "Create new" form
20
+ * itself (name + fields) is the caller's, rendered beneath this.
21
+ */
22
+ export declare function CredentialPicker({ credentials, mode, onModeChange, selectedId, onSelect, canUseExisting, contract, }: CredentialPickerProps): import("react").JSX.Element;
23
+ /**
24
+ * The binding fields a `serviceAccount` auth asks for on a connection. `delegatedUser` is always
25
+ * labelled "Impersonated user" (the person the credential acts as, never the connection's owner),
26
+ * is absent when impersonation is `"none"` even if declared, and is synthesised when impersonation
27
+ * is allowed but the auth declares no such field.
28
+ */
29
+ export declare function bindingFieldsFor(sa: ServiceAccountConfig): AuthField[];
30
+ /**
31
+ * Maps typed binding values to the wire `binding`: `delegatedUser` stays top-level, every other key
32
+ * goes under `settings`, blank values are dropped, and an empty result is `undefined`. `scopes` is
33
+ * never sent (the server applies the auth's declared scopes).
34
+ */
35
+ export declare function buildBinding(fields: AuthField[], values: Record<string, unknown>): ConnectionBinding | undefined;
@@ -0,0 +1,27 @@
1
+ import { MouseEvent, ReactNode } from 'react';
2
+ export interface DataTableColumn<Row> {
3
+ key: string;
4
+ header: ReactNode;
5
+ /** Omitted ⇒ the cell shows `String(row[key] ?? "")`. */
6
+ render?: (row: Row) => ReactNode;
7
+ width?: string;
8
+ align?: "left" | "right" | "center";
9
+ /** With `getRowHref`, wrap this column's cells in an anchor. Default true. */
10
+ link?: boolean;
11
+ }
12
+ export interface DataTableProps<Row> {
13
+ columns: readonly DataTableColumn<Row>[];
14
+ rows: readonly Row[];
15
+ rowKey: (row: Row) => string;
16
+ /** Row click. The event is passed through untouched; the host may `preventDefault`. */
17
+ onRowClick?: (row: Row, e: MouseEvent<Element>) => void;
18
+ /** When set, every cell of a `link !== false` column wraps its content in `<a href>`. */
19
+ getRowHref?: (row: Row) => string | undefined;
20
+ /** Rendered in place of the body when there are no rows and not `loading`. */
21
+ empty?: ReactNode;
22
+ loading?: boolean;
23
+ className?: string;
24
+ testId?: string;
25
+ rowTestId?: (row: Row) => string | undefined;
26
+ }
27
+ export declare function DataTable<Row>(props: DataTableProps<Row>): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ export interface EmptyStateProps {
3
+ title?: ReactNode;
4
+ children?: ReactNode;
5
+ action?: ReactNode;
6
+ className?: string;
7
+ testId?: string;
8
+ }
9
+ export declare function EmptyState({ title, children, action, className, testId }: EmptyStateProps): import("react").JSX.Element;
@@ -4,7 +4,7 @@ import { ReactNode } from 'react';
4
4
  * never for the studio component that happened to draw it, so a caller asks for
5
5
  * `icon="history"` rather than `icon="history-icon-tsx"`.
6
6
  */
7
- export type IconName = "edit" | "edit-2" | "delete" | "copy" | "check" | "close" | "history" | "info" | "clock" | "json" | "code" | "sync" | "cloud-download" | "eye" | "play" | "stop" | "warning" | "file-text" | "settings" | "github" | "user" | "users" | "logout" | "id-card" | "dashboard" | "pulse" | "workflow" | "globe" | "lambda" | "sliders" | "file" | "plug" | "grid" | "folder" | "git-branch" | "coin" | "building" | "server" | "price-tag" | "lang-node" | "terminal" | "lang-python" | "lang-curl" | "sun" | "moon" | "circle-half" | "layout" | "run" | "trash" | "maximize" | "form" | "braces" | "settings-2";
7
+ export type IconName = "edit" | "edit-2" | "delete" | "copy" | "check" | "close" | "history" | "info" | "clock" | "json" | "code" | "sync" | "cloud-download" | "eye" | "play" | "stop" | "warning" | "file-text" | "settings" | "github" | "user" | "bot" | "users" | "logout" | "id-card" | "dashboard" | "pulse" | "workflow" | "globe" | "lambda" | "sliders" | "file" | "plug" | "grid" | "folder" | "git-branch" | "coin" | "building" | "server" | "price-tag" | "lang-node" | "terminal" | "lang-python" | "lang-curl" | "sun" | "moon" | "circle-half" | "layout" | "run" | "trash" | "maximize" | "form" | "braces" | "settings-2" | "sticky-note" | "frame" | "bold" | "grip" | "chevron-down";
8
8
  /**
9
9
  * Which attributes the shared `<svg>` root carries — i.e. how the glyph is
10
10
  * painted. The sources are not uniform, and forcing them into one shape would
@@ -0,0 +1,60 @@
1
+ import { ActionParam, ParamOption, ResolveFieldsBody } from './types.ts';
2
+ /** Dynamic-field context a ParamsForm host passes. */
3
+ export interface DynamicContext {
4
+ appId: string;
5
+ actionKey: string;
6
+ version?: string;
7
+ connectionId?: string;
8
+ appName?: string;
9
+ }
10
+ /** Form values split per D-5: resolvable go to `form`, expression/secret/`$`-object keys to `unresolved`. */
11
+ export interface FormSplit {
12
+ form: Record<string, unknown>;
13
+ unresolved: string[];
14
+ }
15
+ /** One planned call: the body minus `connectionId`/`version` (the hook adds them). */
16
+ export type PlannedRequest = Pick<ResolveFieldsBody, "reason" | "form" | "previous" | "changed" | "fields" | "unresolved">;
17
+ /** Outcome of reconciling a stored value against a returned option list. */
18
+ export interface Reconciled {
19
+ action: "keep" | "flag" | "clear";
20
+ /** Hint to show when `action === "flag"`. */
21
+ message?: string;
22
+ }
23
+ /** D-5: expression, secret, or a plain object with own key `$` can't be sent as a literal. */
24
+ export declare function isUnresolvedValue(v: unknown): boolean;
25
+ declare function allowCustomOf(p: ActionParam): boolean;
26
+ /** Params that request form-resolved options (top level + sections only). */
27
+ export declare function dynamicParams(params: ActionParam[]): ActionParam[];
28
+ /**
29
+ * Split flattened `values` (entered, else declared default) into `form` and
30
+ * `unresolved` (expression, secret, or plain object with own key `$`).
31
+ */
32
+ export declare function splitForm(params: ActionParam[], values: Record<string, unknown>): FormSplit;
33
+ /**
34
+ * Plan the call for `reason`. `init`: every resolveFields field whose
35
+ * `refreshOn` keys are all resolved. `change`: only fields whose `refreshOn`
36
+ * contains `changed[0]` (and whose keys are all resolved). Returns null when no call is due.
37
+ * `previous` (raw pre-commit values) is split the same way and sent as `previous`.
38
+ */
39
+ export declare function planRequests(params: ActionParam[], values: Record<string, unknown>, reason: "init" | "change", changed?: string[], previous?: Record<string, unknown>): PlannedRequest | null;
40
+ /** Cache key `(app, version, action, connection, field, refreshOn values)`. */
41
+ export declare function cacheKey(ctx: DynamicContext, param: ActionParam, form: Record<string, unknown>): string;
42
+ /** Only `cache: "none"` opts out of the in-memory cache. */
43
+ export declare function isCacheable(param: ActionParam): boolean;
44
+ /** True when two requests' `fields` share a key (the newer aborts the older). */
45
+ export declare function fieldsOverlap(a: readonly string[], b: readonly string[]): boolean;
46
+ /**
47
+ * Why a dynamic field is NOT requested (A6), with the hint to show, or null
48
+ * when it is requestable.
49
+ */
50
+ export declare function noFetchReason(param: ActionParam, params: ActionParam[], values: Record<string, unknown>, ctx: Pick<DynamicContext, "connectionId">): {
51
+ reason: "no-connection" | "unresolved-dependency";
52
+ hint: string;
53
+ } | null;
54
+ /** D-6: keep / flag / clear a stored value given the returned options. */
55
+ export declare function reconcileValue(value: unknown, options: ParamOption[], opts: {
56
+ allowCustom: boolean;
57
+ reason: "init" | "change";
58
+ appLabel: string;
59
+ }): Reconciled;
60
+ export { allowCustomOf };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The ONE `EditorView.theme` spec shared by the three CodeMirror editors
3
+ * (`JsonEditor`, `CodeEditor`, `YamlEditor`) — previously copy-pasted verbatim
4
+ * into each of them.
5
+ *
6
+ * Plain object, **no imports**, so `node --test` asserts it directly. Every
7
+ * colour/spacing value is a `--w6w-*` custom property so the editors inherit
8
+ * the consumer's palette.
9
+ *
10
+ * Focus mirrors `styles/_fields.scss` (`.w6w-field input:focus { outline: none;
11
+ * border-color: var(--w6w-accent) }`) instead of drawing an accent ring. The
12
+ * rule must STAY with an explicit `outline: "none"` — deleting it falls back to
13
+ * CodeMirror's base theme (`outline: 1px dotted #212121` in
14
+ * `@codemirror/view`), which is still an outline.
15
+ *
16
+ * `".cm-scroller"`'s `borderRadius: "7px"` (8px − the 1px `"&"` border) clips
17
+ * the gutters and active line to the rounded border. `"&"` never gets
18
+ * `overflow: hidden`, which would clip lint/hover tooltips mounted inside the
19
+ * editor.
20
+ *
21
+ * `".cm-content"` only sets the vertical longhands: `styles/_fx.scss` owns
22
+ * `padding-right` while the ƒx/copy overlay is present.
23
+ */
24
+ export declare const EDITOR_THEME_SPEC: Record<string, Record<string, string>>;
@@ -2,7 +2,7 @@ import { Edge } from '@xyflow/react';
2
2
  import { FlowStep } from './flow-types.ts';
3
3
  export type FanOutMode = "sequential" | "parallel";
4
4
  /**
5
- * `count` is the largest number of outgoing edges the step has in any ONE lane
5
+ * `count` is the largest number of outgoing edges the node has in any ONE lane
6
6
  * (success / error); fan-out only means something at `count >= 2`.
7
7
  */
8
8
  export declare function fanOutState(stepId: string, edges: readonly Edge[]): {
@@ -11,5 +11,5 @@ export declare function fanOutState(stepId: string, edges: readonly Edge[]): {
11
11
  };
12
12
  /** Sequential is the default, so it is expressed by the key's ABSENCE. */
13
13
  export declare function setFanOut(step: FlowStep, mode: FanOutMode): FlowStep;
14
- /** Card-badge wording: how many next steps run, and in which mode. */
14
+ /** Card-badge wording: how many next nodes run, and in which mode. */
15
15
  export declare function fanOutLabel(count: number, mode: FanOutMode): string;
@@ -1,3 +1,4 @@
1
+ import { FlowCanvas } from './canvas-types.ts';
1
2
  import { EdgeRunState } from './run-visuals.ts';
2
3
  import { ActionParam } from './types.ts';
3
4
  export interface FlowStep {
@@ -151,8 +152,9 @@ export interface FlowWorkflow {
151
152
  required?: boolean;
152
153
  default?: unknown;
153
154
  }>;
154
- steps: FlowStep[];
155
+ nodes: FlowStep[];
155
156
  edges?: FlowEdge[];
157
+ canvas?: FlowCanvas;
156
158
  /**
157
159
  * Authoring-time presentation and preferences for this workflow (core
158
160
  * `rfcs/workflow.md` · "Amendment — 2026-07-29: authoring presentation
@@ -209,6 +211,15 @@ export interface FlowWorkflow {
209
211
  };
210
212
  };
211
213
  }
214
+ /**
215
+ * What `workflowToFlow` accepts: a definition in either shape — the canonical
216
+ * `nodes[]` or the legacy `steps[]`. Both present is refused at read time.
217
+ */
218
+ export type FlowWorkflowInput = Omit<FlowWorkflow, "nodes"> & ({
219
+ nodes: FlowStep[];
220
+ } | {
221
+ steps: FlowStep[];
222
+ });
212
223
  /** Reserved namespace for internal pseudo-apps. */
213
224
  export declare const INTERNAL_APP_PREFIX = "@w6w/";
214
225
  /** Engine-native flow control (if/foreach/parallel/wait). */
@@ -241,6 +252,12 @@ export declare const CALL_APP = "@w6w/call";
241
252
  * host-side, so there is no `project` param (C-6).
242
253
  */
243
254
  export declare const DOCUMENT_APP = "@w6w/document";
255
+ /**
256
+ * Call a model through a named route / read a connection's models and
257
+ * allowance. Host node, like {@link DOCUMENT_APP}; the params mirror the
258
+ * `@w6w/model` internal-app manifest (pinned by `model-node-parity.test.ts`).
259
+ */
260
+ export declare const MODEL_APP = "@w6w/model";
244
261
  /**
245
262
  * Compile a Handlebars `template` string against a resolved `values` object
246
263
  * (core rfcs/node-types.md · F-3 amendment, "the `@w6w/template` host node").