@w6w/ui 0.1.1 → 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 (87) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +7 -5
  3. package/dist/AppPicker.d.ts +48 -2
  4. package/dist/CanvasLayer.d.ts +38 -0
  5. package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-BqXDA35P.js} +2 -2
  6. package/dist/{CodeBlock-Dot3Pd7P.js.map → CodeBlock-BqXDA35P.js.map} +1 -1
  7. package/dist/{CodeBlock-CZu4TFSi.cjs → CodeBlock-CulfbnZD.cjs} +2 -2
  8. package/dist/{CodeBlock-CZu4TFSi.cjs.map → CodeBlock-CulfbnZD.cjs.map} +1 -1
  9. package/dist/ConfirmModal-Ln99S9NT.js +7919 -0
  10. package/dist/ConfirmModal-Ln99S9NT.js.map +1 -0
  11. package/dist/ConfirmModal-fjHW5rN-.cjs +14 -0
  12. package/dist/ConfirmModal-fjHW5rN-.cjs.map +1 -0
  13. package/dist/ConnectionHeader.d.ts +19 -0
  14. package/dist/Copyable-DA6eiU7w.cjs +2 -0
  15. package/dist/Copyable-DA6eiU7w.cjs.map +1 -0
  16. package/dist/{Copyable-1niV9gUa.js → Copyable-F4NTaG77.js} +91 -15
  17. package/dist/Copyable-F4NTaG77.js.map +1 -0
  18. package/dist/EditConnectionModal.d.ts +26 -0
  19. package/dist/JsonEditor.d.ts +8 -0
  20. package/dist/Markdown.d.ts +25 -0
  21. package/dist/ParamsForm.d.ts +7 -1
  22. package/dist/StepBuilderModal.d.ts +23 -5
  23. package/dist/WorkflowFlowEditor.d.ts +14 -3
  24. package/dist/app-pages.d.ts +5 -0
  25. package/dist/canvas-types.d.ts +36 -0
  26. package/dist/canvas-utils.d.ts +44 -0
  27. package/dist/code.cjs +1 -1
  28. package/dist/code.mjs +2 -2
  29. package/dist/components/AuthFieldsForm.d.ts +19 -1
  30. package/dist/components/BudgetMeter.d.ts +15 -0
  31. package/dist/components/CallableList.d.ts +58 -0
  32. package/dist/components/Combobox.d.ts +8 -1
  33. package/dist/components/ConnectionList.d.ts +43 -0
  34. package/dist/components/CredentialPicker.d.ts +35 -0
  35. package/dist/components/DataTable.d.ts +27 -0
  36. package/dist/components/EmptyState.d.ts +9 -0
  37. package/dist/components/ListItem.d.ts +16 -6
  38. package/dist/components/ReliabilityChart.d.ts +10 -0
  39. package/dist/components/ReliabilityList.d.ts +47 -0
  40. package/dist/components/icons.d.ts +1 -1
  41. package/dist/connection-app-meta.d.ts +32 -0
  42. package/dist/dynamic-fields.d.ts +60 -0
  43. package/dist/editor-theme.d.ts +24 -0
  44. package/dist/flow-fanout.d.ts +2 -2
  45. package/dist/flow-types.d.ts +18 -1
  46. package/dist/flow-utils.d.ts +10 -3
  47. package/dist/flow.cjs +2 -2
  48. package/dist/flow.cjs.map +1 -1
  49. package/dist/flow.d.ts +9 -0
  50. package/dist/flow.mjs +1519 -1138
  51. package/dist/flow.mjs.map +1 -1
  52. package/dist/index.cjs +1 -1
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.ts +30 -2
  55. package/dist/index.mjs +2053 -1005
  56. package/dist/index.mjs.map +1 -1
  57. package/dist/note-markdown.d.ts +22 -0
  58. package/dist/provider.d.ts +73 -5
  59. package/dist/reliability-board-layout.d.ts +121 -0
  60. package/dist/resolve-params.d.ts +5 -0
  61. package/dist/run-visuals.d.ts +4 -1
  62. package/dist/step-problems.d.ts +34 -0
  63. package/dist/types.d.ts +117 -3
  64. package/dist/use-dynamic-fields.d.ts +23 -0
  65. package/package.json +5 -3
  66. package/src/styles/_budget-meter.scss +46 -0
  67. package/src/styles/_callable-list.scss +69 -0
  68. package/src/styles/_canvas.scss +318 -0
  69. package/src/styles/_connections.scss +88 -0
  70. package/src/styles/_data-table.scss +55 -0
  71. package/src/styles/_empty-state.scss +21 -0
  72. package/src/styles/_flow.scss +11 -0
  73. package/src/styles/_fx.scss +23 -0
  74. package/src/styles/_layout.scss +10 -0
  75. package/src/styles/_lists.scss +4 -0
  76. package/src/styles/_markdown.scss +20 -0
  77. package/src/styles/_reliability.scss +339 -0
  78. package/src/styles/_tester.scss +1 -14
  79. package/src/styles.css +1142 -17
  80. package/src/styles.scss +8 -0
  81. package/dist/ConfirmModal-8avl9Nv8.cjs +0 -11
  82. package/dist/ConfirmModal-8avl9Nv8.cjs.map +0 -1
  83. package/dist/ConfirmModal-CD-P8mhN.js +0 -6335
  84. package/dist/ConfirmModal-CD-P8mhN.js.map +0 -1
  85. package/dist/Copyable-1niV9gUa.js.map +0 -1
  86. package/dist/Copyable-CYZU1o4X.cjs +0 -2
  87. package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
@@ -0,0 +1,26 @@
1
+ import { ConnectionSummary, ThemeMode } from './types.ts';
2
+ export interface EditConnectionModalProps {
3
+ /** The row being edited. `builtIn` rows have nothing to edit: the modal shows a notice only. */
4
+ connection: Pick<ConnectionSummary, "id" | "appId" | "authKey" | "displayName" | "builtIn" | "credentialId" | "lastTestOk">;
5
+ onClose: () => void;
6
+ /** Fired with the server's updated row. The typed credential is cleared before this runs. */
7
+ onSaved: (updated: ConnectionSummary) => void;
8
+ /** Reserved for icon variants, like the other modals. */
9
+ theme?: ThemeMode;
10
+ }
11
+ /**
12
+ * Edit an existing connection: rename it and/or replace some of its credential fields. Runs on
13
+ * the {@link useW6WApi} adapter alone. The server never returns the stored secret, so credential
14
+ * fields start empty and blank means "keep what is stored" — only the fields the user filled are
15
+ * sent, and the server merges them over the stored credential. OAuth connections can be renamed
16
+ * here; re-authorizing is not part of this modal.
17
+ *
18
+ * A connection that uses a stored credential (`connection.credentialId`), when the host supplies
19
+ * `api.rotateCredential`, is split in two: Save only renames it and changes its binding (never the
20
+ * credential, which other connections may share), and "Rotate credential" is a separate explicit
21
+ * action that replaces the stored secret for every connection using it.
22
+ *
23
+ * Needs `api.updateConnection`. Without it (or for a `builtIn` row) there is no Save button, a
24
+ * one-line notice shows instead, and nothing is called. Hosts should not offer Edit in that case.
25
+ */
26
+ export declare function EditConnectionModal({ connection, onClose, onSaved }: EditConnectionModalProps): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import { ThemeMode } from './types.ts';
2
3
  export interface JsonEditorProps {
3
4
  value: string;
@@ -45,6 +46,13 @@ export interface JsonEditorProps {
45
46
  * this prop existed.
46
47
  */
47
48
  copyable?: boolean;
49
+ /**
50
+ * Content overlaid inside the editor box at its top-right corner, a direct
51
+ * child of `.w6w-json-editor` (same overlay mechanism as `copyable`). The
52
+ * `ParamsForm` json field puts its `ƒx` here. Text keeps right padding so
53
+ * line 1 is not covered.
54
+ */
55
+ overlay?: ReactNode;
48
56
  }
49
57
  /**
50
58
  * JSON editor built on CodeMirror 6. Syntax highlighting, folding, gutter with
@@ -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
@@ -29,6 +31,8 @@ export interface AppsPageState {
29
31
  /** Cursor to send on the NEXT "load more"; absent once the server has no more pages. */
30
32
  cursor?: string;
31
33
  hasMore: boolean;
34
+ /** The server's total for the current query, once a page has carried it. */
35
+ total?: number;
32
36
  error?: string;
33
37
  }
34
38
  export declare function initialAppsPageState(query: AppsPageQuery, generation?: number): AppsPageState;
@@ -65,6 +69,7 @@ export type AppsPageAction =
65
69
  apps: AppSummary[];
66
70
  nextCursor?: string;
67
71
  requestedCursor?: string;
72
+ total?: number;
68
73
  } | {
69
74
  type: "page-failed";
70
75
  generation: number;
@@ -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
@@ -13,10 +13,28 @@ interface Props {
13
13
  enterSubmitProps?: {
14
14
  onKeyDown: KeyboardEventHandler<HTMLElement>;
15
15
  };
16
+ /**
17
+ * `"create"` (default) seeds each field from its manifest `default`. `"edit"` is for replacing
18
+ * part of a stored credential: it never seeds `default`, gives secret inputs the placeholder
19
+ * `•••••• (unchanged)`, and reports an emptied number input as `""` (not `0`) so the caller can
20
+ * treat blank as "keep what is stored".
21
+ */
22
+ mode?: "create" | "edit";
16
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
+ };
17
35
  /**
18
36
  * Renders the form for an Auth method's `fields` declaration. Field shape
19
37
  * comes from the app's manifest so no app-specific knowledge is needed here.
20
38
  */
21
- export declare function AuthFieldsForm({ fields, values, onChange, enterSubmitProps }: Props): import("react").JSX.Element;
39
+ export declare function AuthFieldsForm({ fields, values, onChange, enterSubmitProps, mode, }: Props): import("react").JSX.Element;
22
40
  export {};
@@ -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;
@@ -0,0 +1,58 @@
1
+ import { MouseEvent, ReactNode } from 'react';
2
+ /** One row of a {@link CallableList}. The host decides `archived`; the list never learns a kind. */
3
+ export interface CallableListRow {
4
+ id: string;
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ archived: boolean;
8
+ /** Extra badges, rendered before the archived badge in the trailing slot. */
9
+ badges?: ReactNode;
10
+ /** Trailing content; sits inside the row's anchor when `getHref` is set, so keep it non-interactive. */
11
+ trailing?: ReactNode;
12
+ }
13
+ export interface CallableListProps {
14
+ rows: readonly CallableListRow[];
15
+ title: ReactNode;
16
+ description?: ReactNode;
17
+ /** Shown (inside an EmptyState) when no rows exist at all and not `loading`. */
18
+ emptyCopy?: ReactNode;
19
+ /** Label of the New button. Defaults to "New". */
20
+ newLabel?: string;
21
+ /** Tooltip (`title` attribute) on each openable row. */
22
+ openRowTitle?: string;
23
+ testIds?: {
24
+ showArchived?: string;
25
+ importOpen?: string;
26
+ archivedBadge?: string;
27
+ };
28
+ loading?: boolean;
29
+ error?: string | null;
30
+ /** The New button renders only when this is set. */
31
+ onCreate?: () => void;
32
+ /** New button is disabled and reads "Creating…" while true. */
33
+ creating?: boolean;
34
+ /** The Import button renders only when this is set. */
35
+ onImport?: () => void;
36
+ /** Row click. The event is passed through untouched; the host may `preventDefault`. */
37
+ onOpen?: (id: string, event: MouseEvent<Element>) => void;
38
+ /** When set, each row is a real `<a href>`. */
39
+ getHref?: (id: string) => string;
40
+ /** Controlled "Show archived". Omit for an uncontrolled toggle that starts off. */
41
+ showArchived?: boolean;
42
+ onShowArchivedChange?: (next: boolean) => void;
43
+ slots?: {
44
+ /** Replaces the default title/description block (the Show-archived toggle stays). */
45
+ header?: ReactNode;
46
+ /** Rendered before Import/New. */
47
+ actions?: ReactNode;
48
+ /** Replaces `emptyCopy`. */
49
+ empty?: ReactNode;
50
+ };
51
+ }
52
+ /**
53
+ * A presentational list of callables (Functions, Endpoints, Workflows): rows in, callbacks out.
54
+ * Owns only the archived filter and its toggle; it knows no router, query, modal or kind.
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".
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;
@@ -0,0 +1,43 @@
1
+ import { MouseEvent, ReactNode } from 'react';
2
+ import { InlineAppMeta } from '../connection-app-meta.ts';
3
+ import { ConnectionSummary } from '../types.ts';
4
+ export interface ConnectionListProps {
5
+ connections: readonly ConnectionSummary[];
6
+ /**
7
+ * App meta by id, used for rows whose own inline `app` is `undefined` (an older server).
8
+ * Rows with an inline `app` object, or `null`, never consult it.
9
+ */
10
+ appsById?: ReadonlyMap<string, InlineAppMeta>;
11
+ loading?: boolean;
12
+ error?: string | null;
13
+ /** Row click. The event is passed through untouched; the host may `preventDefault`. */
14
+ onOpen?: (id: string, event: MouseEvent<Element>) => void;
15
+ /** When set, each openable row is a real `<a href>`. */
16
+ getHref?: (id: string) => string;
17
+ /**
18
+ * Renders an Edit button per row. Pass it only when the host's adapter has `updateConnection`
19
+ * (otherwise the modal it opens has nothing to save with).
20
+ */
21
+ onEdit?: (connection: ConnectionSummary) => void;
22
+ /** Renders a Delete button per row. Confirmation and the delete call are the host's. */
23
+ onDelete?: (connection: ConnectionSummary) => void;
24
+ /** Disables every Delete button while a delete is in flight. */
25
+ busy?: boolean;
26
+ slots?: {
27
+ /** Heading block above the rows. */
28
+ header?: ReactNode;
29
+ /** Trailing action(s) next to the header (e.g. an "Add" button). */
30
+ actions?: ReactNode;
31
+ /** Shown when there are no connections and the list is not `loading`. */
32
+ empty?: ReactNode;
33
+ };
34
+ }
35
+ /**
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).
38
+ *
39
+ * A `builtIn` row (the server's synthetic "no credential" entry) is a plain non-interactive item:
40
+ * no link, no `onOpen`, no Edit, no Delete, because the server 404s get/test/delete on its id.
41
+ * Detection is `row.builtIn === true`, never an id prefix.
42
+ */
43
+ export declare function ConnectionList({ connections, appsById, loading, error, onOpen, getHref, onEdit, onDelete, busy, slots, }: ConnectionListProps): import("react").JSX.Element;
@@ -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;
@@ -1,4 +1,4 @@
1
- import { ReactNode } from 'react';
1
+ import { MouseEvent, ReactNode } from 'react';
2
2
  export interface ListItemProps {
3
3
  /** Primary label — prominent, single-line with ellipsis overflow. */
4
4
  title: ReactNode;
@@ -6,12 +6,21 @@ export interface ListItemProps {
6
6
  subtitle?: ReactNode;
7
7
  /** Leading slot (e.g. an `<AppIcon />` or status glyph). */
8
8
  icon?: ReactNode;
9
- /** Trailing slot pushed to the right (e.g. a badge or chevron). */
9
+ /**
10
+ * Trailing slot pushed to the right (e.g. a badge or chevron). With `href` it sits INSIDE the
11
+ * anchor, so it must stay non-interactive (no buttons or links).
12
+ */
10
13
  trailing?: ReactNode;
11
14
  /** Highlights the row as selected/current. Reflected as `aria-pressed` when clickable. */
12
15
  active?: boolean;
13
- /** When provided, the row renders as a focusable, keyboard-activatable button. */
14
- onClick?: () => void;
16
+ /**
17
+ * When provided, the row renders as a focusable, keyboard-activatable button — or, with `href`,
18
+ * still fires on a click of the anchor. Receives the click event; ListItem never calls
19
+ * `preventDefault`, so the host decides whether the browser navigates.
20
+ */
21
+ onClick?: (event: MouseEvent<HTMLElement>) => void;
22
+ /** When provided, the row renders as a real `<a href>` (middle-click, copy link, new tab). */
23
+ href?: string;
15
24
  /** Accessible label for the clickable row (falls back to the `title` DOM text). */
16
25
  ariaLabel?: string;
17
26
  }
@@ -20,6 +29,7 @@ export interface ListItemProps {
20
29
  *
21
30
  * Mirrors the look/behaviour of the saved-test button rows it replaces. When
22
31
  * `onClick` is given it renders a `<button type="button">` (focusable, Enter/Space
23
- * activatable) with `aria-pressed={active}`; otherwise a plain, non-interactive row.
32
+ * activatable) with `aria-pressed={active}`; with `href` an `<a href>` (the click still reaches
33
+ * `onClick`); otherwise a plain, non-interactive row.
24
34
  */
25
- export declare function ListItem({ title, subtitle, icon, trailing, active, onClick, ariaLabel, }: ListItemProps): import("react").JSX.Element;
35
+ export declare function ListItem({ title, subtitle, icon, trailing, active, onClick, href, ariaLabel, }: ListItemProps): import("react").JSX.Element;