@w6w/ui 0.0.0-stage → 0.1.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 (143) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +260 -2
  4. package/dist/ActionTestForm.d.ts +76 -0
  5. package/dist/AddConnectionModal.d.ts +24 -0
  6. package/dist/AppPicker.d.ts +83 -0
  7. package/dist/CodeBlock-CZu4TFSi.cjs +15 -0
  8. package/dist/CodeBlock-CZu4TFSi.cjs.map +1 -0
  9. package/dist/CodeBlock-Dot3Pd7P.js +2889 -0
  10. package/dist/CodeBlock-Dot3Pd7P.js.map +1 -0
  11. package/dist/CodeBlock.d.ts +65 -0
  12. package/dist/CodeEditor.d.ts +42 -0
  13. package/dist/ConfirmModal-DJPYXE9t.cjs +11 -0
  14. package/dist/ConfirmModal-DJPYXE9t.cjs.map +1 -0
  15. package/dist/ConfirmModal-sY1tzkBO.js +6335 -0
  16. package/dist/ConfirmModal-sY1tzkBO.js.map +1 -0
  17. package/dist/Copyable-1niV9gUa.js +771 -0
  18. package/dist/Copyable-1niV9gUa.js.map +1 -0
  19. package/dist/Copyable-CYZU1o4X.cjs +2 -0
  20. package/dist/Copyable-CYZU1o4X.cjs.map +1 -0
  21. package/dist/JsonEditor.d.ts +60 -0
  22. package/dist/NodeConfigForm.d.ts +70 -0
  23. package/dist/ParamsForm.d.ts +56 -0
  24. package/dist/PropertyEntryForm.d.ts +56 -0
  25. package/dist/RetryPolicyFields.d.ts +18 -0
  26. package/dist/StepBuilderModal.d.ts +375 -0
  27. package/dist/TriggerFillForm.d.ts +52 -0
  28. package/dist/WaitEditor.d.ts +6 -0
  29. package/dist/WorkflowFlowEditor.d.ts +119 -0
  30. package/dist/YamlEditor.d.ts +46 -0
  31. package/dist/app-categories.d.ts +38 -0
  32. package/dist/app-pages.d.ts +111 -0
  33. package/dist/code.cjs +2 -0
  34. package/dist/code.cjs.map +1 -0
  35. package/dist/code.d.ts +29 -0
  36. package/dist/code.mjs +7 -0
  37. package/dist/code.mjs.map +1 -0
  38. package/dist/components/ApiCallsPanel.d.ts +41 -0
  39. package/dist/components/AppIcon.d.ts +25 -0
  40. package/dist/components/AuthFieldsForm.d.ts +22 -0
  41. package/dist/components/Combobox.d.ts +24 -0
  42. package/dist/components/ConfirmModal.d.ts +33 -0
  43. package/dist/components/Copyable.d.ts +51 -0
  44. package/dist/components/CopyableText.d.ts +40 -0
  45. package/dist/components/DeleteButton.d.ts +16 -0
  46. package/dist/components/EditButton.d.ts +10 -0
  47. package/dist/components/ExecutionDetail.d.ts +70 -0
  48. package/dist/components/ExecutionFilters.d.ts +59 -0
  49. package/dist/components/ExecutionList.d.ts +60 -0
  50. package/dist/components/ExecutionLogPanel.d.ts +98 -0
  51. package/dist/components/ExecutionStats.d.ts +47 -0
  52. package/dist/components/ExpressionEditorModal.d.ts +42 -0
  53. package/dist/components/ExpressionInput.d.ts +36 -0
  54. package/dist/components/ExpressionOptions.d.ts +185 -0
  55. package/dist/components/HealthStatusPill.d.ts +25 -0
  56. package/dist/components/HistoryTimeline.d.ts +56 -0
  57. package/dist/components/Icon.d.ts +34 -0
  58. package/dist/components/IconButton.d.ts +60 -0
  59. package/dist/components/InfoTip.d.ts +15 -0
  60. package/dist/components/InternalIcon.d.ts +19 -0
  61. package/dist/components/ListItem.d.ts +25 -0
  62. package/dist/components/Modal.d.ts +29 -0
  63. package/dist/components/RepoSyncIndicator.d.ts +29 -0
  64. package/dist/components/ResolvedParams.d.ts +31 -0
  65. package/dist/components/ServerResources.d.ts +47 -0
  66. package/dist/components/StepStatusPill.d.ts +51 -0
  67. package/dist/components/UptimeStrip.d.ts +56 -0
  68. package/dist/components/execution-format.d.ts +24 -0
  69. package/dist/components/expression-dom.d.ts +154 -0
  70. package/dist/components/expression-template.d.ts +86 -0
  71. package/dist/components/history-scale.d.ts +33 -0
  72. package/dist/components/icons.d.ts +41 -0
  73. package/dist/components/resolved-clip.d.ts +42 -0
  74. package/dist/components/server-resources-format.d.ts +68 -0
  75. package/dist/components/server-resources.fixture.d.ts +9 -0
  76. package/dist/components/use-copy.d.ts +14 -0
  77. package/dist/createW6WApi.d.ts +31 -0
  78. package/dist/flow-connect.d.ts +138 -0
  79. package/dist/flow-fanout.d.ts +15 -0
  80. package/dist/flow-types.d.ts +356 -0
  81. package/dist/flow-utils.d.ts +284 -0
  82. package/dist/flow.cjs +3 -0
  83. package/dist/flow.cjs.map +1 -0
  84. package/dist/flow.d.ts +22 -0
  85. package/dist/flow.mjs +1977 -0
  86. package/dist/flow.mjs.map +1 -0
  87. package/dist/index.cjs +2 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.d.ts +94 -0
  90. package/dist/index.mjs +1811 -0
  91. package/dist/index.mjs.map +1 -0
  92. package/dist/oauth-popup.d.ts +17 -0
  93. package/dist/output-shape.d.ts +58 -0
  94. package/dist/provider.d.ts +420 -0
  95. package/dist/resolve-params.d.ts +112 -0
  96. package/dist/resolved-json.d.ts +21 -0
  97. package/dist/run-visuals.d.ts +90 -0
  98. package/dist/step-preview-state.d.ts +143 -0
  99. package/dist/theme.d.ts +32 -0
  100. package/dist/trigger-fields.d.ts +56 -0
  101. package/dist/types.d.ts +413 -0
  102. package/dist/ui.css +1 -0
  103. package/dist/use-app-action-defs.d.ts +7 -0
  104. package/dist/use-enter-submit.d.ts +23 -0
  105. package/dist/use-seed-sources.d.ts +16 -0
  106. package/package.json +111 -3
  107. package/src/code.css +542 -0
  108. package/src/styles/_buttons.scss +38 -0
  109. package/src/styles/_chips.scss +210 -0
  110. package/src/styles/_code.scss +139 -0
  111. package/src/styles/_copyable.scss +147 -0
  112. package/src/styles/_data-vars.scss +38 -0
  113. package/src/styles/_execution-history.scss +282 -0
  114. package/src/styles/_execution-log.scss +178 -0
  115. package/src/styles/_expression-field.scss +65 -0
  116. package/src/styles/_expression-inline.scss +63 -0
  117. package/src/styles/_expression-modal.scss +292 -0
  118. package/src/styles/_fields.scss +161 -0
  119. package/src/styles/_flow.scss +217 -0
  120. package/src/styles/_fx.scss +158 -0
  121. package/src/styles/_health.scss +46 -0
  122. package/src/styles/_history-timeline.scss +156 -0
  123. package/src/styles/_icon-button.scss +22 -0
  124. package/src/styles/_icon.scss +15 -0
  125. package/src/styles/_icons.scss +31 -0
  126. package/src/styles/_incoming-state.scss +46 -0
  127. package/src/styles/_info-tip.scss +9 -0
  128. package/src/styles/_layout.scss +6 -0
  129. package/src/styles/_lists.scss +64 -0
  130. package/src/styles/_modal.scss +125 -0
  131. package/src/styles/_multiselect.scss +69 -0
  132. package/src/styles/_repo-sync-indicator.scss +99 -0
  133. package/src/styles/_scale.scss +70 -0
  134. package/src/styles/_sections.scss +36 -0
  135. package/src/styles/_server-resources.scss +163 -0
  136. package/src/styles/_step-builder.scss +376 -0
  137. package/src/styles/_step-config.scss +195 -0
  138. package/src/styles/_tabs.scss +54 -0
  139. package/src/styles/_tester.scss +111 -0
  140. package/src/styles/_tokens.scss +141 -0
  141. package/src/styles/_uptime.scss +61 -0
  142. package/src/styles.css +4208 -0
  143. package/src/styles.scss +61 -0
@@ -0,0 +1,98 @@
1
+ import { ReactNode } from 'react';
2
+ import { StepMeta } from '../provider.tsx';
3
+ import { StepStatus } from './StepStatusPill.tsx';
4
+ /**
5
+ * A step's or run's failure, already normalised by the HOST from the workflow
6
+ * spec's `StepError` (`code`/`message`/`phase`/`retryable`). The panel never
7
+ * parses an `unknown` error itself — same "host owns the data" split as the
8
+ * rest of this file. Rendered in `TriggerFillForm`'s error visual: a
9
+ * `.w6w-result.w6w-error` box, an optional `<code>` badge line, then the message.
10
+ */
11
+ export interface ExecutionLogError {
12
+ /** `StepError.code`. Optional: the host may normalise an error that carries no code. */
13
+ code?: string;
14
+ /** The human-readable reason. Never empty when the host supplies it. */
15
+ message: string;
16
+ /** `StepError.phase`, when known. Carried for the host; not rendered. */
17
+ phase?: string;
18
+ /** `StepError.retryable`, when known. Carried for the host; not rendered. */
19
+ retryable?: boolean;
20
+ }
21
+ /**
22
+ * One entry of `RunState.stepErrors` — a step failure the run recorded and
23
+ * moved past (`onError: "continue-record"`, or a failure an error edge
24
+ * handled), so the run itself may still have succeeded.
25
+ */
26
+ export interface ExecutionLogRecordedError {
27
+ /** The step the error belongs to. */
28
+ stepId: string;
29
+ /** Human-readable step name. Falls back to `stepId` when rendered. */
30
+ label?: string;
31
+ error: ExecutionLogError;
32
+ }
33
+ /**
34
+ * One step's display row — an already-mapped, already-resolved slice of a
35
+ * `RunState`/`StepExecution` (workflow spec). The HOST maps its own run
36
+ * state into this shape (same "host owns the data, the panel is
37
+ * presentational" split `readOnly` already establishes elsewhere in this
38
+ * package) — this component never fetches, polls, or reaches for an SDK.
39
+ */
40
+ export interface ExecutionLogStep {
41
+ /** Stable id for this row — typically the step id from the workflow definition. */
42
+ id: string;
43
+ /** Human-readable step name. Falls back to `id` when omitted. */
44
+ label?: string;
45
+ status: StepStatus;
46
+ /** ISO-8601 timestamp the step started, once it has. */
47
+ startedAt?: string;
48
+ /** ISO-8601 timestamp the step finished, once it has. */
49
+ finishedAt?: string;
50
+ /** The resolved input sent to the step's action, once it is known. */
51
+ input?: unknown;
52
+ /** The step's output, once it has one. */
53
+ output?: unknown;
54
+ /**
55
+ * What ran (`StepExecution.meta`): app, version, action and connection id. Shown as a
56
+ * Metadata block after Output. Absent or `null` ⇒ no block at all (R-12: no filler).
57
+ */
58
+ meta?: StepMeta | null;
59
+ /**
60
+ * Why the step failed (`StepExecution.error`, "present on failed"). Rendered
61
+ * below the row's box and outside its lazy `<details>` body, so the reason
62
+ * reads without expanding the row.
63
+ */
64
+ error?: ExecutionLogError;
65
+ }
66
+ export interface ExecutionLogPanelProps {
67
+ /** The run's steps, rendered in exactly this order — never re-sorted. */
68
+ steps: ExecutionLogStep[];
69
+ /** Shown instead of the list when `steps` is empty. */
70
+ emptyLabel?: ReactNode;
71
+ /**
72
+ * Supplied ⇒ the panel grows a header row carrying a dismiss control that
73
+ * calls this and nothing else (T1.1.1). Omitted ⇒ no header row at all, no
74
+ * extra DOM node. The panel still owns no run state — it only reports the
75
+ * click; the host decides what dismissing means.
76
+ */
77
+ onDismiss?: () => void;
78
+ /**
79
+ * The run's own failure (`RunState.error`). Rendered below the dismiss head
80
+ * and above the steps — in the empty branch too, since a run that failed
81
+ * before any step ran has nothing else to say why.
82
+ */
83
+ runError?: ExecutionLogError;
84
+ /**
85
+ * Step failures the run recorded and moved past (`RunState.stepErrors`),
86
+ * one error block each, in the order given, after `runError`.
87
+ */
88
+ stepErrors?: ExecutionLogRecordedError[];
89
+ }
90
+ /**
91
+ * Read-only log of a workflow run's steps, in the order given: each row is
92
+ * the step's status pill, its timing, and its resolved input/output.
93
+ *
94
+ * A pure display leaf — no data fetching, no polling, no layout media
95
+ * queries (the host docks the panel; see `_scale.scss`'s breakpoint
96
+ * docstring). Fills whatever box its host gives it.
97
+ */
98
+ export declare function ExecutionLogPanel({ steps, emptyLabel, onDismiss, runError, stepErrors, }: ExecutionLogPanelProps): import("react").JSX.Element;
@@ -0,0 +1,47 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * The aggregate numbers of a history view — the same rows the list shows,
4
+ * rolled up (D1: stats are computed over the same visibility the list uses, so
5
+ * the two can never disagree about who may see what).
6
+ *
7
+ * `succeeded`/`canceled` are carried even though the strip renders no card for
8
+ * them today: the host's payload has them, the breakdown is what a host needs
9
+ * for a chart or a chip row, and a host re-deriving them from the list would be
10
+ * computing the same total a second time.
11
+ */
12
+ export interface ExecutionStatsValue {
13
+ /** Every run in the current filter — the denominator of `successRate`. */
14
+ total: number;
15
+ succeeded: number;
16
+ failed: number;
17
+ canceled: number;
18
+ /** Runs that are queued or running right now. `> 0` is what makes the
19
+ * `In flight` card exist at all. */
20
+ inFlight: number;
21
+ /** `0…1`, or `null` when the host has no rate to report (no runs at all —
22
+ * a rate over zero runs is not 0%). */
23
+ successRate: number | null;
24
+ /** Mean wall-clock duration of the FINISHED runs, or `null` when none has
25
+ * finished. */
26
+ avgDurationMs: number | null;
27
+ }
28
+ export interface ExecutionStatsProps {
29
+ /** The aggregate, or `null` while nothing has loaded yet — rendered as `—`
30
+ * per card rather than as a spinner, so the strip never changes height. */
31
+ stats: ExecutionStatsValue | null;
32
+ /** Caption above the cards, e.g. the window they cover ("Last 7 days").
33
+ * Omitted ⇒ no node at all. */
34
+ label?: ReactNode;
35
+ }
36
+ /**
37
+ * A headline strip of run aggregates: one big value over a muted label per
38
+ * card.
39
+ *
40
+ * Promotes studio `DashboardPage.tsx`'s page-local `StatCard` into `ui`, with
41
+ * two differences that come from this being a shared component rather than one
42
+ * page's private helper: the values are pre-formatted here (a rate as `67%`, a
43
+ * duration through `formatDurationMs`) so every consumer renders the same
44
+ * string, and each card carries `data-stat-value` so a host can assert on the
45
+ * value without re-parsing its own formatting.
46
+ */
47
+ export declare function ExecutionStats({ stats, label }: ExecutionStatsProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import { ExprValue, SecretValue } from '../types.ts';
2
+ import { ExpressionOptions } from './ExpressionOptions.tsx';
3
+ /**
4
+ * A near-full-screen editor for an expression value. Left: the data sources in
5
+ * scope (variables, secrets, and the workflow state leading to this step) — one
6
+ * click inserts a colored TAG (chip) at the caret. Right: the expression editor
7
+ * (top, where tags read distinctly from plain text) over the `{{ }}` template
8
+ * form that gets saved plus a live Result pane (bottom) that previews the
9
+ * expression against user-supplied sample values for the injected scope.
10
+ *
11
+ * The value it saves is the same `ExprValue | string` the inline field and the
12
+ * engine already use (see `expression-template.ts` / `expression-dom.ts`).
13
+ */
14
+ export interface ExpressionEditorModalProps {
15
+ value: ExprValue | string | SecretValue | undefined;
16
+ masked?: boolean;
17
+ /**
18
+ * The field being edited holds multi-line text (a `text`-typed param, or one
19
+ * flagged `config.multiline`). Enter then inserts a literal `"\n"` at the
20
+ * caret instead of being swallowed, and `aria-multiline` follows. Omitted ⇒
21
+ * single-line, as every existing consumer expects.
22
+ */
23
+ multiline?: boolean;
24
+ options: ExpressionOptions;
25
+ /** Field name shown in the modal title. */
26
+ fieldLabel?: string;
27
+ /** Expression mode's commit. Not used in `mode="json-binding"` (see `onBind`). */
28
+ onSave?: (next: ExprValue | string) => void;
29
+ onClose: () => void;
30
+ /**
31
+ * `"json-binding"`: pick ONE upstream step output for a whole `json`/`group`
32
+ * param. The rail shows only the Workflow state slot, each step gets an
33
+ * "Entire output" leaf, and a click calls `onBind({ $: ref })` (the plain
34
+ * one-key object, never an `ExprValue`) and closes. No chips editor, no
35
+ * Save, no template/Result panes. Absent ⇒ expression mode, unchanged.
36
+ */
37
+ mode?: "json-binding";
38
+ onBind?: (next: {
39
+ $: string;
40
+ }) => void;
41
+ }
42
+ export declare function ExpressionEditorModal({ value, masked, multiline, options, fieldLabel, onSave, onClose, mode, onBind, }: ExpressionEditorModalProps): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { ExprValue, SecretValue } from '../types.ts';
2
+ /**
3
+ * ExpressionInput — a single field that reads like a textbox but whose contents
4
+ * are inline SEGMENTS: plain text plus var/secret/expr chips (Notion/Zapier
5
+ * blocks). You can type directly; to browse the data sources in scope
6
+ * (variables, secrets, upstream step outputs) and build a reference, open the
7
+ * full editor via the ƒx button — see {@link ExpressionEditorModal}.
8
+ *
9
+ * Value in ↔ out: incoming `value` is a plain string, an `ExprValue`, or a
10
+ * sealed `SecretValue`. `onChange` emits the leanest faithful form — a plain
11
+ * string when pure-text, an `ExprValue` for mixed content, or a sealed
12
+ * `SecretValue` once a masked field is encrypted.
13
+ */
14
+ export interface ExpressionInputProps {
15
+ value: ExprValue | string | SecretValue | undefined;
16
+ onChange: (next: ExprValue | string | SecretValue) => void;
17
+ placeholder?: string;
18
+ /** Mask typed text as dots + seal on blur (used for secret-typed params). */
19
+ masked?: boolean;
20
+ /**
21
+ * The underlying param holds multi-line text (a `text`-typed param, or one
22
+ * flagged `config.multiline` — the same fields that render a `textarea`).
23
+ * Enter then inserts a literal `"\n"` at the caret instead of being swallowed,
24
+ * and `aria-multiline` follows. Omitted ⇒ single-line, as every existing
25
+ * consumer expects.
26
+ */
27
+ multiline?: boolean;
28
+ readOnly?: boolean;
29
+ /** Picker data; falls back to the nearest `ExpressionOptionsProvider`. */
30
+ options?: {
31
+ vars?: string[];
32
+ secrets?: string[];
33
+ };
34
+ "aria-label"?: string;
35
+ }
36
+ export declare function ExpressionInput({ value, onChange, placeholder, masked, multiline, readOnly, options, "aria-label": ariaLabel, }: ExpressionInputProps): import("react").JSX.Element;
@@ -0,0 +1,185 @@
1
+ import { ReactNode } from 'react';
2
+ import { SecretValue } from '../types.ts';
3
+ /**
4
+ * Drop the step being edited (or drafted, not yet persisted) from its own rail
5
+ * sources — a step cannot reference its own output.
6
+ */
7
+ export declare function excludeSelfStep(steps: ExpressionStepSource[], selfId: string | null | undefined): ExpressionStepSource[];
8
+ /** An upstream step whose output this field can reference (`steps.<id>.output`). */
9
+ export interface ExpressionStepSource {
10
+ /** Step id — the key under `steps` in the run scope. */
11
+ id: string;
12
+ /** Human label (defaults to the id). */
13
+ label?: string;
14
+ /**
15
+ * The step's output field keys, when any are known (a manual trigger's
16
+ * configured `fields`, a built-in node's static output, or an app action's
17
+ * shape — see `outputsFrom`). Each entry feeds exactly one ref —
18
+ * `steps.<id>.output.<key>` — which is the canonical form the engine resolves
19
+ * (`RunScope.steps`; `core/rfcs/workflow.md` §"Expressions", `core/rfcs/node-types.md`
20
+ * "Executing a trigger node yields the run's start payload … which downstream
21
+ * nodes read as `steps.<triggerId>.output`").
22
+ *
23
+ * `key` is carried **verbatim**: a display-shortened key would build a ref
24
+ * that renders fine in the editor and evaluates to empty at run time, with no
25
+ * error. `label` is for display only and must never be substituted into a ref.
26
+ *
27
+ * **Omitted** (not `[]`) when the step declares no outputs, so a consumer can
28
+ * tell "nothing declared" from "declared none" without a special case.
29
+ */
30
+ outputs?: {
31
+ key: string;
32
+ label?: string;
33
+ path?: boolean;
34
+ }[];
35
+ /**
36
+ * Where `outputs` came from, for an app-action step: the action's declared
37
+ * `output`, its `sample`, or the step's last test run (`output-shape.ts`).
38
+ * An entry with `path: true` is a dot path into nested output (`start.utc`),
39
+ * checked segment by segment; without it the key is one literal key.
40
+ */
41
+ outputsFrom?: "declared" | "sample" | "test";
42
+ /**
43
+ * `true` when the step is an app action that declares no output shape and has
44
+ * no test run yet — the picker tells the author to run a test to see fields.
45
+ */
46
+ needsTest?: boolean;
47
+ /**
48
+ * `true` for an EXTERNAL app-action step only (R-7): the picker then offers a fixed
49
+ * Metadata group (`steps.<id>.meta.app|version|action|connectionId`). **Omitted, not
50
+ * `false`**, for triggers and every `@w6w/*` node. Carries no metadata VALUE: the group
51
+ * is just labels, and the value exists only at run time.
52
+ */
53
+ hasMeta?: boolean;
54
+ }
55
+ /**
56
+ * A document contributing pickable refs (`documents.<key>`, and one
57
+ * `documents.<key>.<field>` per top-level field when it qualifies).
58
+ *
59
+ * A document contributes field sub-entries **iff**: `doc.format === "json"`
60
+ * AND `JSON.parse(doc.content)` succeeds into a plain object, OR
61
+ * `doc.format === "yaml"` AND npm `yaml`'s `parse(doc.content)` succeeds into
62
+ * a plain object — where *plain object* means
63
+ * `typeof v === "object" && v !== null && !Array.isArray(v)`. This mirrors
64
+ * the engine's own gate: `documentsSeed`
65
+ * (`packages/server/packages/api/run-seed.ts`, read-only, out of scope) is the
66
+ * authority for each format — `format === "json"` or `format === "yaml"`,
67
+ * then a `try/catch` parse, falling back to the raw content string on either
68
+ * a non-matching format or a throw. `text`/`markdown`/`html` documents, and a
69
+ * `json`/`yaml` document whose parse throws or yields a non-plain-object
70
+ * (array/scalar), are excluded — offering a field ref outside this gate would
71
+ * fail at run time with `render_ref_unresolved`, because the run scope itself
72
+ * never parsed that document into an object.
73
+ *
74
+ * The two parsers (`@std/yaml` server-side, npm `yaml` here) are not
75
+ * identical for every input — they diverge on tab-indented content and on an
76
+ * unresolvable custom tag, both accepted, tested debt (D-3) — but agree on
77
+ * everything else, including a plain JSON object (JSON is a subset of YAML).
78
+ */
79
+ export interface ExpressionDocumentSource {
80
+ /** Document key — the key under `documents` in the run scope. */
81
+ key: string;
82
+ /**
83
+ * The document's top-level field keys, when the gate above holds. Each
84
+ * entry feeds exactly one ref — `documents.<key>.<field>` — with `key`
85
+ * carried **verbatim**: a display-shortened key would build a ref that
86
+ * renders fine in the editor and evaluates to empty at run time, with no
87
+ * error.
88
+ *
89
+ * **Omitted** (not `[]`) when the document contributes no fields, so a
90
+ * consumer can tell "nothing declared" from "declared none" without a
91
+ * special case — mirrors {@link ExpressionStepSource.outputs}'s own rule.
92
+ */
93
+ fields?: {
94
+ key: string;
95
+ }[];
96
+ }
97
+ /** Known variable/secret names offered in an ExpressionInput's insert menu. */
98
+ export interface ExpressionOptions {
99
+ vars?: string[];
100
+ secrets?: string[];
101
+ /**
102
+ * Function/run input keys in scope (`inputs.<name>`). Present when the field
103
+ * is edited inside a Function (the engine resolves these from `RunScope.inputs`);
104
+ * omitted for a standalone field.
105
+ */
106
+ inputs?: string[];
107
+ /**
108
+ * Documents in scope (`documents.<key>`, plus `documents.<key>.<field>` for
109
+ * each document that qualifies — see {@link ExpressionDocumentSource}). A
110
+ * store-independent affordance: whatever the host passes is offered as
111
+ * insertable chips. Omitted when no documents are available.
112
+ */
113
+ documents?: ExpressionDocumentSource[];
114
+ /**
115
+ * The workflow state leading to this step: upstream steps whose output is in
116
+ * scope (`steps.<id>.output`). Present only in a workflow context; omitted for
117
+ * a standalone field.
118
+ */
119
+ steps?: ExpressionStepSource[];
120
+ /** Whether a trigger event is in scope (`trigger.event`). */
121
+ hasTrigger?: boolean;
122
+ /**
123
+ * Design-time sample values the Result preview substitutes for known refs,
124
+ * keyed by FULL ref (`"vars.from_email"`, `"documents.test"`). Seeded by the
125
+ * host from the project's real vars/documents so the preview shows actual
126
+ * values instead of the raw `{{ ref }}` template. Optional — a user-typed
127
+ * Sample value still overrides, and unknown refs keep the raw fallback.
128
+ */
129
+ sampleValues?: Record<string, unknown>;
130
+ /**
131
+ * Seal a typed secret value into an at-rest `SecretValue` envelope via the
132
+ * host (the client has no key). Provided by studio (`POST /vault/seal`); when
133
+ * present, a secret-typed field encrypts on blur so its clear text never
134
+ * lands in the workflow/config JSON. Absent → the value stays a plain string
135
+ * and the server encrypts it on receive instead.
136
+ */
137
+ sealSecret?: (value: string) => Promise<SecretValue>;
138
+ /**
139
+ * Create a new named variable via the host (`POST /vars`-shaped: the host
140
+ * persists it, `ui` never fetches). Powers the rail's "+ Add" control next
141
+ * to the Variables group label. Absent ⇒ the control is not rendered.
142
+ */
143
+ createVar?: (input: {
144
+ name: string;
145
+ value: string;
146
+ description?: string;
147
+ }) => Promise<void>;
148
+ /**
149
+ * Create a new named secret via the host (`POST /vault`-shaped, plaintext
150
+ * value — the server encrypts it, `ui` never sees a key). Powers the rail's
151
+ * "+ Add" control next to the Secrets group label. Absent ⇒ the control is
152
+ * not rendered.
153
+ */
154
+ createSecret?: (input: {
155
+ name: string;
156
+ value: string;
157
+ description?: string;
158
+ }) => Promise<void>;
159
+ }
160
+ export interface ExpressionOptionsProviderProps {
161
+ /**
162
+ * The scope this level CONTRIBUTES — not the whole scope. Shallow-merged over
163
+ * whatever is inherited (see the module doc): keys here win, keys absent are
164
+ * inherited, and an explicit `undefined` removes an inherited key for this
165
+ * subtree.
166
+ */
167
+ value: ExpressionOptions;
168
+ children: ReactNode;
169
+ }
170
+ /**
171
+ * Contribute expression scope to every `ExpressionInput` below.
172
+ *
173
+ * Layers over the inherited scope rather than replacing it, so an app shell can
174
+ * provide vars/secrets/documents once and a page can add its `inputs` without
175
+ * restating them — and without a field somewhere in the page missing the rail
176
+ * because a prop was not threaded to it. See the module doc for the model.
177
+ */
178
+ export declare function ExpressionOptionsProvider({ value, children }: ExpressionOptionsProviderProps): import("react").JSX.Element;
179
+ /**
180
+ * The scope in effect here — every contributing provider above, merged.
181
+ *
182
+ * Empty by default so a standalone `ExpressionInput` (no provider) still works
183
+ * — authors just type names by hand.
184
+ */
185
+ export declare function useExpressionOptions(): ExpressionOptions;
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * The four health states a probe can report — a thin LOCAL copy of the RFC's
4
+ * `HealthReport.state` union. `@w6w/ui` deliberately keeps a small literal type
5
+ * here rather than depend on `@w6w/types`, so the display component stays a
6
+ * pure-presentation leaf with no transport/spec coupling.
7
+ */
8
+ export type HealthPillState = "ok" | "degraded" | "down" | "unknown";
9
+ export interface HealthStatusPillProps {
10
+ /** Which health state to render — drives the colour + default label. */
11
+ state: HealthPillState;
12
+ /** Override the visible text (defaults to a humanised form of `state`). */
13
+ label?: ReactNode;
14
+ /** Accessible label for the pill (falls back to the visible text). */
15
+ ariaLabel?: string;
16
+ }
17
+ /**
18
+ * A small, theme-aware status pill for a single health state.
19
+ *
20
+ * Renders a coloured dot + label using the `--w6w-health-*` tokens, so a host
21
+ * can restyle every state by overriding those custom properties (see
22
+ * `styles.css`). Colour is never the only signal — the text label carries the
23
+ * same meaning for accessibility.
24
+ */
25
+ export declare function HealthStatusPill({ state, label, ariaLabel }: HealthStatusPillProps): import("react").JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { UptimeCellState, UptimeDay } from './UptimeStrip.tsx';
2
+ /** The calendar window both lanes are plotted against. Both bounds inclusive. */
3
+ export interface HistoryWindow {
4
+ /** Inclusive first calendar day, `YYYY-MM-DD`. */
5
+ from: string;
6
+ /** Inclusive last calendar day, `YYYY-MM-DD`. */
7
+ to: string;
8
+ }
9
+ /** One entry in the vendor's incident feed. */
10
+ export interface VendorIncidentBar {
11
+ id?: string;
12
+ title: string;
13
+ state: UptimeCellState;
14
+ /** ISO instant. Absent = a point-in-time update, not a span. */
15
+ startedAt?: string;
16
+ updatedAt?: string;
17
+ /** ISO instant. Absent WITH `startedAt` present = still open. */
18
+ resolvedAt?: string;
19
+ components?: string[];
20
+ link?: string;
21
+ }
22
+ export interface HistoryTimelineProps {
23
+ window: HistoryWindow;
24
+ /** Execution lane, oldest first, one entry per calendar day. */
25
+ days: UptimeDay[];
26
+ /** Vendor lane. `undefined` = this vendor publishes no incident history at all. */
27
+ incidents?: VendorIncidentBar[];
28
+ /** Overrides the vendor lane's empty text. */
29
+ vendorEmptyLabel?: string;
30
+ /** Clock injection for tests and for an open incident's right edge. Defaults to `Date.now()`. */
31
+ nowMs?: number;
32
+ ariaLabel?: string;
33
+ }
34
+ /**
35
+ * Two time series — our per-day execution history and the vendor's incident
36
+ * history — plotted on one shared x-axis, so "our calls failed *during* that
37
+ * vendor incident" is a visible alignment rather than a claim.
38
+ *
39
+ * Purely presentational, same discipline as `UptimeStrip`: `days` is already
40
+ * one already-decided state per calendar day (no sort, no reverse, no gap
41
+ * fill), and `incidents` is already the vendor's own feed. This component
42
+ * places them; it makes no judgement about what counts as an incident.
43
+ *
44
+ * `incidents === undefined` ("this vendor publishes no incident history") and
45
+ * `incidents === []` ("nothing happened in this window") are different facts
46
+ * and render different empty-row text — Stripe is the first, a quiet Slack
47
+ * window is the second. Either way the axis and the execution lane render in
48
+ * full, at full width: a vendor with no feed never shrinks the half of the
49
+ * chart that does have data.
50
+ *
51
+ * Severity is encoded twice, same rule as `UptimeStrip` and `HealthStatusPill`:
52
+ * a per-state class AND a geometry difference (fill height on a day cell,
53
+ * clip markers on a bar), never colour alone. Every cell and every bar
54
+ * carries a `title`.
55
+ */
56
+ export declare function HistoryTimeline({ window: win, days, incidents, vendorEmptyLabel, nowMs, ariaLabel, }: HistoryTimelineProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { IconName } from './icons.tsx';
2
+ export interface IconProps {
3
+ /** Which glyph. Required — this component carries no default icon. */
4
+ name: IconName;
5
+ /** Rendered width AND height, in px. Default 16, the size every rail glyph and
6
+ * icon button in the tree already draws at. */
7
+ size?: number;
8
+ /**
9
+ * Accessible name. Omit it for the common case — a decorative glyph inside a
10
+ * button that already has one — and the `<svg>` is `aria-hidden`; pass it and
11
+ * the glyph becomes `role="img"` with that `aria-label`, for an icon standing
12
+ * alone as content.
13
+ */
14
+ label?: string;
15
+ /** Merged with `w6w-icon`. */
16
+ className?: string;
17
+ }
18
+ /**
19
+ * One glyph from the shared set (`./icons.tsx`), sized by prop and coloured by
20
+ * `currentColor` — so a caller colours it with `color` on any ancestor, and it
21
+ * inherits hover/disabled/theme states for free.
22
+ *
23
+ * Accessibility is a two-way switch, never an absent middle: with no `label` the
24
+ * glyph is decorative (`aria-hidden`, `focusable="false"`) for the common case
25
+ * inside an already-labelled button; with a `label` it is `role="img"` with that
26
+ * name. There is no third state, because an unlabelled `<svg>` that is not
27
+ * hidden is exactly the noise a screen reader announces as "graphic".
28
+ *
29
+ * The four ARIA attributes are written out as plain expressions rather than
30
+ * spread from a conditional object: `biome check`'s `a11y/noSvgWithoutTitle`
31
+ * reads the JSX attributes it can see, and a spread hides them (measured —
32
+ * "Alternative text title element cannot be empty" on the spread form only).
33
+ */
34
+ export declare function Icon(props: IconProps): import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ import { ReactNode } from 'react';
2
+ import { IconName } from './icons.tsx';
3
+ export interface IconButtonProps {
4
+ /** Accessible name — ALWAYS rendered as `aria-label`. Required: an icon-only
5
+ * button with no name has no safe default (unlike `Copyable`'s `label`,
6
+ * which is always "Copy"). */
7
+ label: string;
8
+ /** The glyph, as caller-supplied markup. Still the primary path — pass this
9
+ * or `icon` (both given ⇒ `children` wins). */
10
+ children?: ReactNode;
11
+ /** The glyph, by name from the shared set (`./icons.tsx`) — e.g. `"history"`.
12
+ * Renders `<Icon name={icon} size={iconSize ?? 16} />`, decorative, so this
13
+ * button's `label` stays the only thing announced. */
14
+ icon?: IconName;
15
+ /** Size handed to `Icon`. Default 16. No effect without `icon`. */
16
+ iconSize?: number;
17
+ /** Tooltip override. Defaults to `label`; `aria-label` is ALWAYS `label`, so
18
+ * the two may legitimately differ (see the docblock). */
19
+ title?: string;
20
+ /** Required — an icon-only button that can't fire has no reason to exist. */
21
+ onClick: () => void;
22
+ disabled?: boolean;
23
+ className?: string;
24
+ /** Caller opts into danger styling; `IconButton` itself has no "delete" concept. */
25
+ danger?: boolean;
26
+ /** Passed straight through, for a toggle button. */
27
+ "aria-pressed"?: boolean;
28
+ /** Passed straight through, for a disclosure button. */
29
+ "aria-expanded"?: boolean;
30
+ /** Passed straight through, for a disclosure button naming the region it controls. */
31
+ "aria-controls"?: string;
32
+ "data-testid"?: string;
33
+ }
34
+ /**
35
+ * The primitive icon-only button: a caller-supplied glyph, a required
36
+ * accessible name, native `disabled`, and a merged `className`.
37
+ *
38
+ * `EditButton`/`DeleteButton` build on this and bundle one canonical glyph
39
+ * each — this component itself carries no glyph and no "edit"/"delete"
40
+ * meaning, only the `danger` styling hook.
41
+ *
42
+ * The glyph comes from one of two places. `children` is the original path and
43
+ * still works unchanged; `icon` names one from the shared set (`./icons.tsx`)
44
+ * and is rendered through `Icon`, so a caller no longer has to redraw (or
45
+ * import) an SVG for a glyph the set already holds. Given both, `children`
46
+ * wins — the named prop is the convenience, never a way to hide a caller's own
47
+ * markup.
48
+ *
49
+ * `label` is always the accessible name, and `title` overrides ONLY the
50
+ * tooltip. The two legitimately differ: studio's history button is named
51
+ * "Execution history" while, with nothing executed yet, its tooltip reads "No
52
+ * executions yet" — the tooltip says why the control is inert to a sighted
53
+ * user, the name stays the action, and a screen reader must keep hearing the
54
+ * latter.
55
+ *
56
+ * `ref` reaches the `<button>` itself (`forwardRef` — React 18, so the
57
+ * ref-as-prop syntax is not available), which is what a caller needs to move
58
+ * focus or measure the control.
59
+ */
60
+ export declare const IconButton: import('react').ForwardRefExoticComponent<IconButtonProps & import('react').RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ export interface InfoTipProps {
3
+ /** Accessible name AND tooltip of the toggle button (e.g. "About client secrets"). */
4
+ label: string;
5
+ /** The explanation, rendered only while expanded. */
6
+ children: ReactNode;
7
+ className?: string;
8
+ }
9
+ /**
10
+ * A small "i" button that reveals a muted explanation block on click — for
11
+ * copy that would otherwise clutter a modal. Collapsed by default; the button
12
+ * carries `aria-expanded` and `aria-controls` pointing at the block, and the
13
+ * block is not rendered at all while collapsed.
14
+ */
15
+ export declare function InfoTip({ label, children, className }: InfoTipProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * InternalIcon — an internal pseudo-app's glyph on a rounded tile, sized and
3
+ * shaped to match `AppIcon`'s image tile so internal (`@w6w/*`) nodes and app
4
+ * nodes read as the same family everywhere they appear (canvas, add-step
5
+ * picker, edit modal). The glyph markup comes from the node def
6
+ * (`InternalNodeDef.icon` / `internalNodeIcon`) — static, in-repo SVG (no user
7
+ * input). It strokes with the accent color and tracks the active theme.
8
+ *
9
+ * Deliberately NOT the shared `Icon` (`./Icon.tsx`), despite owning an `<svg>`:
10
+ * this is a DATA-DRIVEN per-app identity mark — its content is a markup string
11
+ * off the node def, one glyph per internal app, not a fixed UI glyph with a name
12
+ * in the set (`./icons.tsx`). Unifying them would mean changing
13
+ * `InternalNodeDef` to carry a name instead of its markup, which is a data-model
14
+ * change, not an icon migration.
15
+ */
16
+ export declare function InternalIcon({ icon, size }: {
17
+ icon: string;
18
+ size?: number;
19
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ListItemProps {
3
+ /** Primary label — prominent, single-line with ellipsis overflow. */
4
+ title: ReactNode;
5
+ /** Secondary label under the title — muted, small, truncates. */
6
+ subtitle?: ReactNode;
7
+ /** Leading slot (e.g. an `<AppIcon />` or status glyph). */
8
+ icon?: ReactNode;
9
+ /** Trailing slot pushed to the right (e.g. a badge or chevron). */
10
+ trailing?: ReactNode;
11
+ /** Highlights the row as selected/current. Reflected as `aria-pressed` when clickable. */
12
+ active?: boolean;
13
+ /** When provided, the row renders as a focusable, keyboard-activatable button. */
14
+ onClick?: () => void;
15
+ /** Accessible label for the clickable row (falls back to the `title` DOM text). */
16
+ ariaLabel?: string;
17
+ }
18
+ /**
19
+ * A clean, reusable list row: leading icon · title over subtitle · trailing slot.
20
+ *
21
+ * Mirrors the look/behaviour of the saved-test button rows it replaces. When
22
+ * `onClick` is given it renders a `<button type="button">` (focusable, Enter/Space
23
+ * activatable) with `aria-pressed={active}`; otherwise a plain, non-interactive row.
24
+ */
25
+ export declare function ListItem({ title, subtitle, icon, trailing, active, onClick, ariaLabel, }: ListItemProps): import("react").JSX.Element;