@w6w/embed 0.1.1 → 0.2.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 (148) hide show
  1. package/README.md +231 -4
  2. package/dist/ConfirmModal-RLbZT5_k.js +181 -0
  3. package/dist/ConfirmModal-RLbZT5_k.js.map +1 -0
  4. package/dist/CreateFunctionModal-DRsFx-FB.js +221 -0
  5. package/dist/CreateFunctionModal-DRsFx-FB.js.map +1 -0
  6. package/dist/DocumentFormModal-BExK1V-B.js +89 -0
  7. package/dist/DocumentFormModal-BExK1V-B.js.map +1 -0
  8. package/dist/ImportSpecModal-BDQIf6rg.js +136 -0
  9. package/dist/ImportSpecModal-BDQIf6rg.js.map +1 -0
  10. package/dist/W6WEmbedProvider.d.ts +22 -4
  11. package/dist/WorkflowEditor-DlbVv6Fs.js +854 -0
  12. package/dist/WorkflowEditor-DlbVv6Fs.js.map +1 -0
  13. package/dist/api-calls--JHyif6a.js +107 -0
  14. package/dist/api-calls--JHyif6a.js.map +1 -0
  15. package/dist/apps-1FNUwbGv.js +77 -0
  16. package/dist/apps-1FNUwbGv.js.map +1 -0
  17. package/dist/callables-3mFrMVG_.js +151 -0
  18. package/dist/callables-3mFrMVG_.js.map +1 -0
  19. package/dist/chrome/index.d.ts +6 -0
  20. package/dist/chrome/workspace.d.ts +29 -1
  21. package/dist/coerce-input-BicYr7nc.js +139 -0
  22. package/dist/coerce-input-BicYr7nc.js.map +1 -0
  23. package/dist/connections-CjjYeRqb.js +78 -0
  24. package/dist/connections-CjjYeRqb.js.map +1 -0
  25. package/dist/document-sources-C0grrtdq.js +86 -0
  26. package/dist/document-sources-C0grrtdq.js.map +1 -0
  27. package/dist/draft/index.d.ts +3 -1
  28. package/dist/draft/use-callable-draft.d.ts +65 -0
  29. package/dist/draft/workflow-save.d.ts +3 -0
  30. package/dist/draft.mjs +9 -5
  31. package/dist/draft.mjs.map +1 -1
  32. package/dist/editor/CallableEditorFrame.d.ts +33 -0
  33. package/dist/editor/EditorShell.d.ts +16 -0
  34. package/dist/editor/EndpointCards.d.ts +56 -0
  35. package/dist/editor/EndpointEditor.d.ts +26 -0
  36. package/dist/editor/FunctionEditor.d.ts +21 -0
  37. package/dist/editor/InputListEditor.d.ts +29 -0
  38. package/dist/editor/TargetSelector.d.ts +30 -0
  39. package/dist/editor/TargetSelector.types.d.ts +62 -0
  40. package/dist/editor/editor-apps.d.ts +8 -2
  41. package/dist/editor/types.d.ts +57 -0
  42. package/dist/editor-apps-BdenSV1o.js +95 -0
  43. package/dist/editor-apps-BdenSV1o.js.map +1 -0
  44. package/dist/editor-tools-RxmY5iyi.js +1279 -0
  45. package/dist/editor-tools-RxmY5iyi.js.map +1 -0
  46. package/dist/editor.mjs +2 -2166
  47. package/dist/editor.mjs.map +1 -1
  48. package/dist/editors/DocumentEditor.d.ts +20 -0
  49. package/dist/editors/doc-format.d.ts +3 -0
  50. package/dist/editors.d.ts +12 -0
  51. package/dist/editors.mjs +1313 -0
  52. package/dist/editors.mjs.map +1 -0
  53. package/dist/embed-status.d.ts +20 -0
  54. package/dist/endpoints-B2ynbZo-.js +227 -0
  55. package/dist/endpoints-B2ynbZo-.js.map +1 -0
  56. package/dist/forms/CreateEndpointModal.d.ts +54 -0
  57. package/dist/forms/CreateFunctionModal.d.ts +55 -0
  58. package/dist/forms/DocumentFormModal.d.ts +8 -0
  59. package/dist/forms/SecretsManager.d.ts +12 -0
  60. package/dist/forms/VarsManager.d.ts +29 -0
  61. package/dist/forms.d.ts +6 -0
  62. package/dist/forms.mjs +407 -0
  63. package/dist/forms.mjs.map +1 -0
  64. package/dist/index.d.ts +3 -1
  65. package/dist/index.mjs +153 -50
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/lib/callable-record.d.ts +21 -0
  68. package/dist/lib/coerce-input.d.ts +6 -0
  69. package/dist/lib/endpoint-editor.d.ts +23 -0
  70. package/dist/lib/index.d.ts +7 -0
  71. package/dist/lib/input-coverage.d.ts +28 -0
  72. package/dist/lib/input-seeding.d.ts +67 -0
  73. package/dist/lib/rail-settings.d.ts +3 -3
  74. package/dist/lib/recent-activity.d.ts +128 -0
  75. package/dist/lib/target-fields.d.ts +168 -0
  76. package/dist/lib/workflow-blank.d.ts +2 -0
  77. package/dist/lib.mjs +70 -48
  78. package/dist/lib.mjs.map +1 -1
  79. package/dist/lists/ActivitySummary.d.ts +29 -0
  80. package/dist/lists/CallableKindList.d.ts +34 -0
  81. package/dist/lists/ConnectionDetail.d.ts +17 -0
  82. package/dist/lists/ConnectionList.d.ts +16 -0
  83. package/dist/lists/DocumentList.d.ts +29 -0
  84. package/dist/lists/EndpointList.d.ts +19 -0
  85. package/dist/lists/FunctionList.d.ts +18 -0
  86. package/dist/lists/ReliabilityBoard.d.ts +23 -0
  87. package/dist/lists/RunHistory.d.ts +11 -0
  88. package/dist/lists/ServiceDetail.d.ts +20 -0
  89. package/dist/lists/WorkflowList.d.ts +19 -0
  90. package/dist/lists/vendor-history.d.ts +40 -0
  91. package/dist/lists.d.ts +20 -0
  92. package/dist/lists.mjs +1185 -0
  93. package/dist/lists.mjs.map +1 -0
  94. package/dist/project-context-BiCjv_E3.js +25 -0
  95. package/dist/project-context-BiCjv_E3.js.map +1 -0
  96. package/dist/project-context.d.ts +15 -1
  97. package/dist/rail/CallableRail.d.ts +12 -0
  98. package/dist/rail/CallableWorkspace.d.ts +16 -0
  99. package/dist/rail/SettingsTool.d.ts +7 -1
  100. package/dist/rail/settings-sections.d.ts +2 -2
  101. package/dist/rail-settings-XYGToh00.js +111 -0
  102. package/dist/rail-settings-XYGToh00.js.map +1 -0
  103. package/dist/rail.d.ts +11 -0
  104. package/dist/rail.mjs +35 -0
  105. package/dist/rail.mjs.map +1 -0
  106. package/dist/repos/api-calls.d.ts +4 -0
  107. package/dist/repos/apps.d.ts +14 -0
  108. package/dist/repos/callables.d.ts +91 -0
  109. package/dist/repos/connections.d.ts +33 -0
  110. package/dist/repos/dashboard.d.ts +4 -0
  111. package/dist/repos/endpoints.d.ts +125 -0
  112. package/dist/repos/functions.d.ts +79 -0
  113. package/dist/repos/index.d.ts +10 -0
  114. package/dist/repos/reliability.d.ts +42 -0
  115. package/dist/repos/saved-tests.d.ts +46 -0
  116. package/dist/repos/spec-export.d.ts +23 -0
  117. package/dist/repos/workflows.d.ts +138 -0
  118. package/dist/repos.mjs +174 -88
  119. package/dist/repos.mjs.map +1 -1
  120. package/dist/settings-rail--GdRMusb.js +33 -0
  121. package/dist/settings-rail--GdRMusb.js.map +1 -0
  122. package/dist/slug-C4ktDPdy.js +315 -0
  123. package/dist/slug-C4ktDPdy.js.map +1 -0
  124. package/dist/spec-export-Cf3VQSQS.js +176 -0
  125. package/dist/spec-export-Cf3VQSQS.js.map +1 -0
  126. package/dist/spec-import-CjmxyZDR.js +465 -0
  127. package/dist/spec-import-CjmxyZDR.js.map +1 -0
  128. package/dist/step-tests-CivICmTq.js +18 -0
  129. package/dist/step-tests-CivICmTq.js.map +1 -0
  130. package/dist/trigger-preview-VPkrryDl.js +120 -0
  131. package/dist/trigger-preview-VPkrryDl.js.map +1 -0
  132. package/dist/use-callable-draft-zHLpcr-b.js +271 -0
  133. package/dist/use-callable-draft-zHLpcr-b.js.map +1 -0
  134. package/dist/{use-workflow-draft-oI02dsP-.js → use-workflow-draft-DzAyUeA6.js} +48 -46
  135. package/dist/use-workflow-draft-DzAyUeA6.js.map +1 -0
  136. package/dist/vault-BSEWhMYC.js +117 -0
  137. package/dist/vault-BSEWhMYC.js.map +1 -0
  138. package/dist/workflow-blank-DyL3_RUA.js +14 -0
  139. package/dist/workflow-blank-DyL3_RUA.js.map +1 -0
  140. package/package.json +21 -5
  141. package/src/styles.css +408 -1
  142. package/dist/project-context-Dbk0Gr1K.js +0 -16
  143. package/dist/project-context-Dbk0Gr1K.js.map +0 -1
  144. package/dist/slug-ByKQgxrf.js +0 -642
  145. package/dist/slug-ByKQgxrf.js.map +0 -1
  146. package/dist/spec-import-HgO7ZkLZ.js +0 -898
  147. package/dist/spec-import-HgO7ZkLZ.js.map +0 -1
  148. package/dist/use-workflow-draft-oI02dsP-.js.map +0 -1
package/README.md CHANGED
@@ -15,14 +15,81 @@ stay MIT.
15
15
  npm i @w6w/embed react react-dom
16
16
  ```
17
17
 
18
- That is the only w6w package you install. `@w6w/sdk`, `@w6w/react`, `@w6w/ui` and
19
- `@tanstack/react-query` come with it as dependencies; `react` and `react-dom` (18 or later) are
18
+ This is the only w6w package most hosts install. `@w6w/sdk`, `@w6w/react`, `@w6w/ui` and
19
+ `@tanstack/react-query` come with it as dependencies; install `@w6w/ui` yourself too (`^0.2.0`, so
20
+ there is one copy) only if you write depth 2 below, since a strict resolver such as pnpm does not let
21
+ host code import a transitive dependency; `react` and `react-dom` (18 or later) are
20
22
  yours. Import the one stylesheet once, at your app's root:
21
23
 
22
24
  ```ts
23
25
  import "@w6w/embed/styles.css"; // @w6w/ui's styles, React Flow base included
24
26
  ```
25
27
 
28
+ ## Entries
29
+
30
+ Every import path the published package exposes. Each carries `"use client"`, so a Next.js server
31
+ page can import it.
32
+
33
+ | Import | What it holds |
34
+ | --- | --- |
35
+ | `@w6w/embed` | `W6WEmbedProvider`, `useEmbedStatus`, `useEmbedProject`, `EmbedAuthError`. |
36
+ | `@w6w/embed/editor` | `WorkflowEditor` alone, loaded lazily. |
37
+ | `@w6w/embed/editors` | The callable editors: `WorkflowEditor`, `FunctionEditor`, `EndpointEditor`, `DocumentEditor`. |
38
+ | `@w6w/embed/lists` | The connected lists and detail views: `FunctionList`, `EndpointList`, `WorkflowList`, `ReliabilityBoard`, `ActivitySummary`, `RunHistory`, `DocumentList`, `ServiceDetail`, `ConnectionList`, `ConnectionDetail`. |
39
+ | `@w6w/embed/forms` | The configuration forms: `CreateFunctionModal`, `CreateEndpointModal`, `VarsManager`, `SecretsManager`. |
40
+ | `@w6w/embed/rail` | `CallableWorkspace` and `CallableRail`, to place the editor's rail yourself. |
41
+ | `@w6w/embed/repos` | The data hooks the components read through (`useFunctionsQuery`, `useAppActionsQuery`, ...), on the provider's one cache. |
42
+ | `@w6w/embed/draft` | The headless draft lifecycle (autosave, flush on hide, the stale-write token). No UI. |
43
+ | `@w6w/embed/lib` | Pure helpers the editor shares with Studio. |
44
+ | `@w6w/embed/styles.css` | The one stylesheet: `@w6w/ui`'s styles, React Flow base included. |
45
+
46
+ ## Three depths
47
+
48
+ Use as much of the package as you need.
49
+
50
+ **1. Drop in the component.** It fetches, renders and writes on its own:
51
+
52
+ ```tsx
53
+ import { FunctionList } from "@w6w/embed/lists";
54
+
55
+ <FunctionList onOpen={(id) => router.push(`/functions/${id}`)} />;
56
+ ```
57
+
58
+ **2. Keep `@w6w/ui`'s presentational component and feed it from an embed hook.** You own the rows:
59
+
60
+ ```tsx
61
+ import { CallableList } from "@w6w/ui";
62
+ import { useFunctionsQuery } from "@w6w/embed/repos";
63
+
64
+ function MyFunctions() {
65
+ const { data = [], isLoading } = useFunctionsQuery();
66
+ return (
67
+ <CallableList
68
+ title="Functions"
69
+ loading={isLoading}
70
+ rows={data.map((f) => ({
71
+ id: f.id,
72
+ title: f.displayName || f.key,
73
+ archived: f.archivedAt != null,
74
+ }))}
75
+ onOpen={(id) => router.push(`/functions/${id}`)}
76
+ />
77
+ );
78
+ }
79
+ ```
80
+
81
+ **3. Use the hook alone** and draw everything yourself:
82
+
83
+ ```tsx
84
+ import { useFunctionsQuery } from "@w6w/embed/repos";
85
+
86
+ const { data } = useFunctionsQuery();
87
+ return <ul>{data?.map((f) => <li key={f.id}>{f.displayName || f.key}</li>)}</ul>;
88
+ ```
89
+
90
+ Depth 2 imports `@w6w/ui` directly, so your host must list it (see [Install](#install)); depth 3 does not. All three read
91
+ the provider's one cache (see [One cache](#one-cache)).
92
+
26
93
  ## The provider
27
94
 
28
95
  ```tsx
@@ -52,6 +119,26 @@ never reaches the browser. The recipe is in the guide at `/guides/embed/`.
52
119
  next one.
53
120
  - The provider owns its `QueryClient`; it does not use, and does not need, one from your app.
54
121
  - `ready={false}` holds every read until you have a user; `project` sets the default project.
122
+ - `project={{ use: "default" }}` uses the account's default project instead of an id you know. It
123
+ is looked up once per identity (one `GET /projects`, the row with `isDefault`), never the first
124
+ row of the list. `useEmbedProject()` returns `{ project, status, error? }` with `status` one of
125
+ `"resolving" | "ready" | "error"`: while resolving, `project` is `null` and editors stay in
126
+ their "project required" state; if the lookup fails or no row is the default, `status` is
127
+ `"error"` and nothing falls back to another project. A plain `project="proj_123"` is `"ready"`
128
+ at once; omitted, it is `{ project: null, status: "ready" }`.
129
+ - `identityKey={null}` means nobody is signed in: the provider renders `fallback` (default
130
+ nothing) and mounts none of the embed, so no session is requested and no API call is made.
131
+ Going `null` to an identity starts at once; going from an identity to `null` first writes any
132
+ pending draft into the account that made it, then shows `fallback`.
133
+ - `useEmbedStatus()` returns `{ status, error }` with `status` `"connecting" | "ready" | "error"`.
134
+ The session is acquired by the first request a mounted surface makes, so **without `eager` the
135
+ status stays `"connecting"` until then**. `eager` acquires the session when the provider
136
+ mounts. A token your backend minted but the API refused (401) is `"error"` with an
137
+ `EmbedAuthError` (`cause` is the SDK `ApiError`) until the next acquisition starts or the
138
+ identity changes; a failing `getSession` is `"error"` with its own error. With
139
+ `project={{ use: "default" }}` the lookup itself acquires a session on mount (so `getSession` is
140
+ called even without `eager`), and the client is rebuilt once the project resolves, which acquires
141
+ a session again: the status goes `connecting`, `ready`, `connecting`.
55
142
 
56
143
  ### Next.js server components
57
144
 
@@ -125,6 +212,15 @@ optional (default: a no-op) and hands navigation (back to the list, or to anothe
125
212
  router; `slots` lets you place your own elements in the editor chrome; `initialRunId` opens the
126
213
  editor on a run. The editor must render inside `<W6WEmbedProvider>`.
127
214
 
215
+ ### Placing the rail yourself
216
+
217
+ By default the editor renders its own right rail (`rail` omitted or `true`). Pass `rail={false}` to
218
+ render no rail and place it elsewhere: wrap the editor in `<CallableWorkspace kind="workflow"
219
+ id={workflowId}>` and put `<CallableRail kind="workflow" id={workflowId} />` anywhere inside it,
220
+ such as a drawer. Both come from `@w6w/embed/rail`; the rail then edits the same draft the editor
221
+ saves. Without an enclosing workspace, `rail={false}` simply has no rail. See the
222
+ [components reference](../../docs/embed/components.md).
223
+
128
224
  ### The project rule
129
225
 
130
226
  The editor takes its project from its `project` prop, else from the provider's `project`. Saves
@@ -147,6 +243,117 @@ comes from the nearest `[data-theme]` ancestor: the provider's `theme` prop puts
147
243
  tree. Omit `theme` and the editor follows `data-theme` on `<html>` if you set it, else the OS
148
244
  preference. The editor fills its parent (`width` and `height` 100%), so give its container a height.
149
245
 
246
+ The layout rules every embedded surface follows (it fills its parent, navigates by callback, and keeps
247
+ every class under that root) are in [`docs/layout.md`](docs/layout.md), a repo doc not shipped in the package.
248
+
249
+ ## Function editor
250
+
251
+ | Component | Import | What it is |
252
+ | --- | --- | --- |
253
+ | `<FunctionEditor functionId project? onNavigate? slots? rail? tools?>` | `@w6w/embed/editors` | One Function: Interface (name, key, description), Inputs, Implementation, Test invoke, with History, a JSON toggle and Save in the header. |
254
+
255
+ Saving, the JSON toggle's Save and the rail's Error handling all write the same draft, and every
256
+ write carries the stored `updatedAt`. If the Function changed on the server since it was opened, the
257
+ save is refused with an inline "Reload from server" control and is never retried. `rail` defaults to
258
+ `true`; `rail={false}` publishes into an enclosing `<CallableWorkspace kind="function" id>` for the
259
+ same id (the rail's saves then use that workspace's `project`). `project` only scopes the expression
260
+ rail, since a Function belongs to the account. There is no Aliases section; add one through `tools`.
261
+ See the [components reference](../../docs/embed/components.md).
262
+
263
+ ## Lists
264
+
265
+ `@w6w/embed/lists` holds the connected lists (`FunctionList`, `EndpointList`, `WorkflowList`, `ReliabilityBoard`, `ActivitySummary`, `RunHistory`, `DocumentList`, `ServiceDetail`, `ConnectionList`, `ConnectionDetail`). Each reads through the same provider as the editor and
266
+ writes through it, so a host mounts nothing else.
267
+
268
+ | Component | What it does |
269
+ | --- | --- |
270
+ | `<FunctionList project? onOpen? getHref? onCreated? slots?>`, `<EndpointList>`, `<WorkflowList>` | The account's Functions, Endpoints and the project's Workflows, each with a Show-archived toggle, New and Import, and rows that are buttons, or real links with `getHref`. `onOpen(id, event)` and `onCreated(id)` hand navigation to your router. Per-prop details, including `EndpointList` and `WorkflowList`, are in the [components reference](../../docs/embed/components.md). |
271
+ | `<ConnectionList onOpen? getHref? addLabel? slots?>` | Your connections with Add, Edit and Delete. Delete asks first. Edit appears only when the API behind the provider can save a connection, which `<W6WEmbedProvider>` always can. `getHref` makes each row a real link. `slots.actions` replaces the Add button (pass `null` to remove it), `slots.header` and `slots.empty` replace the heading and the empty state. Built-in "no credential" rows have no Edit, Delete or link. |
272
+ | `<ConnectionDetail connectionId onOpenAction onBack? slots?>` | One connection: Test connection, Edit, the app's actions with their saved tests, and recent runs. `onOpenAction(actionKey)` fires for an action and `onOpenAction(actionKey, savedTestId)` for a saved test; `onBack` shows a back button when given. A missing connection shows a "not found" state. |
273
+
274
+ ```tsx
275
+ import { ConnectionDetail, ConnectionList } from "@w6w/embed/lists";
276
+
277
+ <ConnectionList onOpen={(id) => router.push(`/connections/${id}`)} />;
278
+ <ConnectionDetail connectionId={id} onOpenAction={(key, testId) => {/* your router */}} />;
279
+ ```
280
+
281
+ `@w6w/embed/lists` holds the connected list components. `<ReliabilityBoard project? variant? limit?
282
+ days? showChart? getHref? onOpen? showAllHref? onShowAll? slots?>` shows one project's service
283
+ reliability: it loads the project's services itself (membership first, then status in serial batches
284
+ of at most ten) and draws them with `@w6w/ui`'s `ReliabilityList`. Icons come from the rows, so it
285
+ makes no app-catalog request. `days` defaults to 30; `limit` only trims what is shown. Omit `project`
286
+ to use the provider's; `null` or `""` makes no request and asks for a project.
287
+
288
+ `<ActivitySummary getHref? onOpen? slots?>` shows the account's run statistics and recent activity:
289
+ "Today" and "Last 7 days" headline cards, "Activity over time" and "Recent activity". It is
290
+ account-wide (it takes no `project`), and it resolves each row's function or workflow name against
291
+ the account's own lists (the workflow list follows the client's default project when the provider
292
+ names one, so a workflow outside it stays plain text). The name is a link only when it resolves: `getHref` makes it an `<a href>`,
293
+ `onOpen` alone makes it a button, and with neither it stays text. A click calls
294
+ `onOpen(target, event)` and is never cancelled by the component. It makes two `/dashboard/stats`
295
+ requests on purpose: "Today" is cut at the viewer's local midnight (`from`, `bucket=day`), and the
296
+ server only groups by `day` or `week`, cutting at midnight in the database's timezone, so one
297
+ request cannot give a local day. `<RunHistory scope? showKind? filters? onFiltersChange?
298
+ onOpenInEditor?>` shows the execution history for the provider's project (or the `scope` you pass);
299
+ omit `filters` and it keeps them itself, pass them and you own them. See the
300
+ [components reference](../../docs/embed/components.md).
301
+
302
+ `<ServiceDetail appId project? days? backHref? onBack? slots?>` shows one service's reliability
303
+ drill-down: health, error mix, the API's own status, daily history and recent calls. It makes one
304
+ single-service status request (`apps=<appId>`, never the whole board) plus one call-log request.
305
+ `days` defaults to 30; omit `project` to use
306
+ the provider's, and with none it makes no request. When the service has no row for the project it
307
+ shows a not-found card whose back link is an `<a href>` with `backHref`, a `<button>` with only
308
+ `onBack`, and absent with neither; a click calls `onBack` and is never cancelled.
309
+
310
+ ## Configuration forms
311
+
312
+ `@w6w/embed/forms` holds the connected forms (`VarsManager`, `SecretsManager`, and the `CreateFunctionModal` / `CreateEndpointModal` dialogs the lists open). Each is one self-contained element (root class
313
+ `w6w-embed-root`) that reads and writes through the provider's client.
314
+
315
+ | Component | Scope | Props |
316
+ | --- | --- | --- |
317
+ | `<VarsManager>` | one project | `project?: string \| null`, `slots?` |
318
+ | `<SecretsManager>` | the whole account (no project) | `slots?` |
319
+
320
+ - `VarsManager` lists, adds, edits and deletes typed variables (`string`, `number`, `boolean`,
321
+ `json`). Omit `project` to use the `<W6WEmbedProvider project>`. `null` or `""` means no project:
322
+ nothing is requested and "Add" is disabled. Every request names the project it acts on.
323
+ - `SecretsManager` is write-only. A secret's value is typed into a form and sent; it is never
324
+ shown again, never held in the query cache, and never passed to a prop or callback. Rotating with
325
+ a blank value changes only the description. Secrets belong to the account, so the component takes
326
+ no project.
327
+ - `slots`: `{ header?, actions?, empty? }`. Leave a slot out for the default (the heading, the
328
+ "+ Add ..." button, "No ... yet."), pass `null` for nothing, or pass a node. `actions` also
329
+ accepts a function, `({ openCreate, canCreate }) => ReactNode`, so a host that supplies its own
330
+ button can still open the create form.
331
+
332
+ ## Documents
333
+
334
+ | Component | Import | What it is |
335
+ | --- | --- | --- |
336
+ | `<DocumentList>` | `@w6w/embed/lists` | A project's documents: add, edit in place, delete. |
337
+ | `<DocumentEditor documentId>` | `@w6w/embed/editors` | One document: its key and content, with Edit and Delete. |
338
+
339
+ Both take `project` (omitted: the provider's project; `null` or `""`: nothing is read and writes
340
+ refuse) and `slots` (omitted: the default, `null`: nothing, a node: replaces). Documents the server
341
+ shares in from a repository show no Edit or Delete. The embed has no repository sync and no "synced
342
+ from" markers; a host that wants them adds them through the list's row slots (`lead`,
343
+ `sharedActions`, `footer`). Details are in the components reference.
344
+
345
+ ## Endpoint editor
346
+
347
+ | Component | Import | What it is |
348
+ | --- | --- | --- |
349
+ | `<EndpointEditor endpointId project? onNavigate? slots? rail? tools?>` | `@w6w/embed/editors` | One Endpoint: Identity, the callable URL, Input, Target, Security, Test invoke, History and a JSON toggle, with a Save at the top and the bottom. |
350
+
351
+ Every write is one save carrying the stored `updatedAt`; if someone else saved first, the editor says
352
+ so inline with a Reload and never retries. Secret fields are write-only: a stored secret shows
353
+ "stored — type to replace" and is kept unless you type a new one. There is no Aliases section.
354
+ `rail={false}` inside a `<CallableWorkspace>` for the same id publishes into that workspace. Details
355
+ are in the components reference.
356
+
150
357
  ## Console exclusions
151
358
 
152
359
  Your host code makes no `client.console` call. The package uses a public `@w6w/sdk` operation
@@ -159,9 +366,29 @@ with its reason.
159
366
  | `src/repos/workflows.ts` | `console.workflows.upsert` | creating a workflow in a named project has no public operation |
160
367
  | `src/lib/history-query.ts` | `console.workflows.getRun`; raw `GET /history`, `GET /history/stats`, `GET /invocations/:id` | no public history or invocation operation |
161
368
  | `src/repos/step-tests.ts` | `console.stepTests.list`, `.save`, `.recordRun` | no public operation |
162
- | `src/repos/vault.ts` | `console.vault.list`, `.seal`, `.create`, `.update`, `.delete` | vault is outside the public contract; used to add and seal a secret from the expression editor |
369
+ | `src/repos/vault.ts` | `console.vault.list`, `.seal`, `.create`, `.update`, `.delete` | vault is outside the public contract; used by `SecretsManager` (`@w6w/embed/forms`) to list, add, rotate and delete a secret, and to seal one from the expression editor |
163
370
  | `src/repos/documents.ts` | raw `POST /documents/:id/sync` | no public operation |
164
371
  | `src/repos/spec-import.ts` | raw `POST /spec/import` | no public operation (Settings import) |
165
- | `src/repos/vars.ts` | raw `GET /vars`, `POST /vars`, `PATCH /vars/:id`, `DELETE /vars/:id` | the SDK's `vars` methods take no options argument, so they cannot send `?project=` |
372
+ | `src/repos/vars.ts` | raw `GET /vars`, `POST /vars`, `PATCH /vars/:id`, `DELETE /vars/:id` | the SDK's `vars` methods take no options argument, so they cannot send `?project=`; `VarsManager` (`@w6w/embed/forms`) needs it on every call |
166
373
  | `src/repos/versions.ts` | raw `GET /:c/:id/versions`, `GET /:c/:id/versions/:v`, `POST /:c/:id/publish`, `POST /:c/:id/:x`, `POST /:c/:id/versions/:v/revert`, `POST /:c/:id/versions/:v/:x` | the endpoint arm has no public version operation; the function arm of invoke-version keeps the raw invocation frame, which `functions.runVersion` unwraps |
374
+ | `src/repos/callables.ts` | `console.functions.list`, `.get`, `.upsert`; `console.endpoints.list`, `.get`, `.upsert`; `console.workflows.get`, `.upsert`, `.archive`, `.delete`; the function and endpoint archive (POST /:c/:id/archive) and delete (DELETE /:c/:id) go through the client's plain `request()` with a computed path | endpoints have no public list, read or write operation (the public SDK exposes only `endpoints.run`), and no public archive or delete; the function arm is not a missing-operation case: public `functions.list/get/create/update/archive` exist, but these calls moved verbatim from Studio, whose editor hydrates from the console `get` (which splices `valid`), and migrating them to the public operations is deferred; the workflow arm keeps the typed console calls its rail already used |
375
+ | `src/repos/functions.ts` | `console.functions.list`, `.get`, `.upsert` | moved verbatim from Studio and still on the console calls (the stored-definition read splices the server's `valid`; the guarded save sends `ifUnmodifiedSince`); public `functions.list/get/update` exist and migrating to them is deferred |
376
+ | `src/repos/endpoints.ts` | `console.endpoints.get`, `.upsert`, `.invoke` | endpoints have no public operation; the save keeps its own `mutationFn` because the 201 carries the rendered `invokeUrls[]` |
377
+ | `src/draft/use-callable-draft.ts` | `console.functions.upsert`, `console.endpoints.upsert` | the callable draft's guarded save (`ifUnmodifiedSince`) for the Function and Endpoint editors; endpoints have no public write operation, and the Function save is moved verbatim from Studio |
378
+ | `src/repos/spec-export.ts` | raw `GET /:c/:id/spec` | no public export operation (Settings export) |
379
+ | `src/repos/apps.ts` | `console.apps.getActions`, `console.apps.get` | the per-app Actions list and the app detail (version, source, actions) have no public operation; same `GET /apps/:id` payload the canvas adapter serves |
380
+ | `src/repos/connections.ts` | `console.connections.listForApp`, `.create`, `.update`, `.test` | no public per-app connection list, create, update or test; the list itself is the public `connections.list` |
381
+ | `src/repos/connections.ts` | `console.connections.get`, `.delete` | public `connections.get/delete` exist; these moved verbatim from Studio and still use the console calls, and migrating them is deferred |
382
+ | `src/repos/saved-tests.ts` | `console.savedTests.list`, `.listTestRuns`, `.create`, `.update`, `.delete`, `.recordTestRun` | saved tests and tester run history have no public operation |
383
+ | `src/repos/reliability.ts` | `console.reliability.listMembers`, `console.reliability.list` | no public reliability operation; the board's membership discovery and serial status batches |
384
+ | `src/repos/api-calls.ts` | `console.apiCalls.list` | no public operation; `ServiceDetail`'s recent-calls log |
167
385
  | `@w6w/react` `createW6WUiAdapter` (canvas) | `console.apps.*`, saved and step tests, subscriptions | the adapter's own call sites, used by the canvas from `@w6w/ui/flow` |
386
+
387
+ ## One cache
388
+
389
+ Inside `<W6WEmbedProvider>`, read data through the `@w6w/embed/repos` hooks (`useFunctionsQuery`,
390
+ `useAppActionsQuery`, `useConnectionsForAppQuery`, ...), not through `@w6w/react`'s read hooks. The
391
+ repos hooks share the provider's identity-scoped TanStack `QueryClient` under fixed query keys, so a
392
+ save in one surface refreshes every other surface that shows the same record. `@w6w/react`'s read
393
+ hooks keep their own `useAsync` state per hook call, which is a second cache: the two disagree after
394
+ a write and neither invalidates the other.
@@ -0,0 +1,181 @@
1
+ import { jsx as e, jsxs as i, Fragment as u } from "react/jsx-runtime";
2
+ import { b as W, u as E } from "./slug-C4ktDPdy.js";
3
+ import { IconButton as S, Modal as _, CodeBlock as B } from "@w6w/ui";
4
+ import { useRef as L, useEffect as x, useState as N } from "react";
5
+ function R({
6
+ title: o,
7
+ onClose: n,
8
+ children: r,
9
+ wide: s = !1,
10
+ xl: m = !1,
11
+ headerRight: c
12
+ }) {
13
+ const p = L(null);
14
+ return x(() => {
15
+ const d = p.current;
16
+ if (!d) return;
17
+ d.open || d.showModal();
18
+ const f = (t) => {
19
+ t.preventDefault(), n();
20
+ }, b = (t) => {
21
+ if (t.target !== d) return;
22
+ const a = d.getBoundingClientRect();
23
+ (t.clientX < a.left || t.clientX > a.right || t.clientY < a.top || t.clientY > a.bottom) && n();
24
+ };
25
+ return d.addEventListener("cancel", f), d.addEventListener("click", b), () => {
26
+ d.removeEventListener("cancel", f), d.removeEventListener("click", b);
27
+ };
28
+ }, [n]), /* @__PURE__ */ e("div", { className: "modal-backdrop", children: /* @__PURE__ */ i(
29
+ "dialog",
30
+ {
31
+ ref: p,
32
+ className: `modal${s ? " modal-wide" : ""}${m ? " modal-xl" : ""}`,
33
+ "aria-label": o,
34
+ children: [
35
+ c ? /* @__PURE__ */ i("div", { className: "modal-head", children: [
36
+ /* @__PURE__ */ e("h3", { children: o }),
37
+ c
38
+ ] }) : /* @__PURE__ */ e("h3", { children: o }),
39
+ r
40
+ ]
41
+ }
42
+ ) });
43
+ }
44
+ const y = [
45
+ { id: "node", label: "Node", language: "TypeScript", grammar: "typescript", icon: "lang-node" },
46
+ { id: "cli", label: "CLI", language: "Shell", grammar: "bash", icon: "terminal" },
47
+ { id: "python", label: "Python", language: "Python", grammar: "python", icon: "lang-python" },
48
+ { id: "curl", label: "curl", language: "Shell", grammar: "bash", icon: "lang-curl" }
49
+ ];
50
+ function F(o) {
51
+ const n = W(o);
52
+ return n ? y.filter((r) => typeof n[r.id] == "string") : [];
53
+ }
54
+ function M({
55
+ resource: o,
56
+ triggers: n,
57
+ wrap: r = !1,
58
+ maxHeight: s = "420px",
59
+ initialTab: m
60
+ }) {
61
+ var v, C, w;
62
+ const [c, p] = N(m ?? "node"), [d, f] = N(0), b = !!n && n.length > 0, t = b ? n[Math.min(d, n.length - 1)] ?? n[0] : null, a = W(o, t == null ? void 0 : t.fields), g = a ? y.filter((l) => typeof a[l.id] == "string") : [], h = g.some((l) => l.id === c) ? c : ((v = g[0]) == null ? void 0 : v.id) ?? "node", k = (a == null ? void 0 : a[h]) ?? "";
63
+ return a === null ? /* @__PURE__ */ e("p", { className: "muted", children: E(o) }) : /* @__PURE__ */ i(u, { children: [
64
+ b && t && /* @__PURE__ */ i("div", { className: "subpanel", style: { marginTop: 0, paddingTop: 0, borderTop: "none" }, children: [
65
+ /* @__PURE__ */ i("div", { "data-testid": "trigger-panel", children: [
66
+ /* @__PURE__ */ e("div", { className: "code-tabs", children: n.map((l, T) => /* @__PURE__ */ e(
67
+ "button",
68
+ {
69
+ type: "button",
70
+ className: `code-tab${T === d ? " active" : ""}`,
71
+ "data-testid": "trigger-tab",
72
+ onClick: () => f(T),
73
+ children: l.label
74
+ },
75
+ l.id
76
+ )) }),
77
+ t.fields.length === 0 && /* @__PURE__ */ e("p", { className: "muted small", children: "This trigger declares no fields." })
78
+ ] }),
79
+ n.length > 1 && /* @__PURE__ */ i("p", { className: "muted small", "data-testid": "trigger-multi-note", children: [
80
+ "This workflow has ",
81
+ n.length,
82
+ " manual triggers — the same input reaches every one of them identically; there is no per-trigger routing."
83
+ ] })
84
+ ] }),
85
+ /* @__PURE__ */ e("div", { className: "code-tabs", children: g.map((l) => /* @__PURE__ */ e(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ className: `code-tab${h === l.id ? " active" : ""}`,
90
+ onClick: () => p(l.id),
91
+ children: l.label
92
+ },
93
+ l.id
94
+ )) }),
95
+ /* @__PURE__ */ e(
96
+ B,
97
+ {
98
+ code: k,
99
+ language: ((C = y.find((l) => l.id === h)) == null ? void 0 : C.grammar) ?? "plaintext",
100
+ maxHeight: s,
101
+ wrap: r,
102
+ "aria-label": `${((w = y.find((l) => l.id === h)) == null ? void 0 : w.label) ?? h} snippet`,
103
+ copyable: !0
104
+ }
105
+ ),
106
+ /* @__PURE__ */ e("p", { className: "muted small", style: { marginBottom: 0 }, children: h === "node" ? /* @__PURE__ */ i(u, { children: [
107
+ /* @__PURE__ */ e("code", { children: "npm i @w6w/sdk" }),
108
+ " · set ",
109
+ /* @__PURE__ */ e("code", { children: "W6W_BASE_URL" }),
110
+ " and ",
111
+ /* @__PURE__ */ e("code", { children: "W6W_TOKEN" }),
112
+ " · operation ",
113
+ /* @__PURE__ */ e("code", { children: a.operation })
114
+ ] }) : h === "cli" ? /* @__PURE__ */ i(u, { children: [
115
+ /* @__PURE__ */ e("code", { children: "npm i -g @w6w/cli" }),
116
+ " · set ",
117
+ /* @__PURE__ */ e("code", { children: "W6W_BASE_URL" }),
118
+ " and",
119
+ " ",
120
+ /* @__PURE__ */ e("code", { children: "W6W_TOKEN" }),
121
+ " (or pass ",
122
+ /* @__PURE__ */ e("code", { children: "--base-url" }),
123
+ " / ",
124
+ /* @__PURE__ */ e("code", { children: "--token" }),
125
+ ") · operation ",
126
+ /* @__PURE__ */ e("code", { children: a.operation })
127
+ ] }) : h === "python" ? /* @__PURE__ */ i(u, { children: [
128
+ /* @__PURE__ */ e("code", { children: "pip install w6w" }),
129
+ " · set ",
130
+ /* @__PURE__ */ e("code", { children: "W6W_BASE_URL" }),
131
+ " and ",
132
+ /* @__PURE__ */ e("code", { children: "W6W_TOKEN" }),
133
+ " ",
134
+ "· operation ",
135
+ /* @__PURE__ */ e("code", { children: a.operation })
136
+ ] }) : /* @__PURE__ */ i(u, { children: [
137
+ "No client library — any HTTP client will do. Set ",
138
+ /* @__PURE__ */ e("code", { children: "W6W_BASE_URL" }),
139
+ " to your w6w base URL (for an Endpoint, the URL above is the one this page is showing); substitute a w6w token for ",
140
+ /* @__PURE__ */ e("code", { children: "$W6W_TOKEN" }),
141
+ " and send the declared input as the JSON body."
142
+ ] }) })
143
+ ] });
144
+ }
145
+ function I({
146
+ resource: o,
147
+ label: n = "Call from code",
148
+ triggers: r,
149
+ initialTab: s
150
+ }) {
151
+ const [m, c] = N(!1);
152
+ return /* @__PURE__ */ i(u, { children: [
153
+ /* @__PURE__ */ e(S, { icon: "code", label: n, onClick: () => c(!0) }),
154
+ m && /* @__PURE__ */ e(_, { title: "Call from code", size: "wide", onClose: () => c(!1), children: /* @__PURE__ */ e(M, { resource: o, triggers: r, initialTab: s }) })
155
+ ] });
156
+ }
157
+ function K({
158
+ title: o,
159
+ message: n,
160
+ onConfirm: r,
161
+ onClose: s,
162
+ confirmLabel: m = "Confirm",
163
+ cancelLabel: c = "Cancel",
164
+ destructive: p = !0
165
+ }) {
166
+ return /* @__PURE__ */ i(R, { title: o, onClose: s, children: [
167
+ /* @__PURE__ */ e("div", { className: "modal-body", children: /* @__PURE__ */ e("p", { className: "muted small", children: n }) }),
168
+ /* @__PURE__ */ i("div", { className: "modal-actions", children: [
169
+ /* @__PURE__ */ e("button", { type: "button", className: "btn ghost", onClick: s, children: c }),
170
+ /* @__PURE__ */ e("button", { type: "button", className: p ? "btn danger" : "btn", onClick: r, children: m })
171
+ ] })
172
+ ] });
173
+ }
174
+ export {
175
+ I as C,
176
+ R as M,
177
+ K as a,
178
+ M as b,
179
+ F as s
180
+ };
181
+ //# sourceMappingURL=ConfirmModal-RLbZT5_k.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmModal-RLbZT5_k.js","sources":["../src/chrome/Modal.tsx","../src/chrome/CallFromCodeButton.tsx","../src/chrome/ConfirmModal.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\n\ninterface ModalProps {\n title: string;\n onClose: () => void;\n children: React.ReactNode;\n /** Widen the dialog (e.g. for a JSON editor that needs room). */\n wide?: boolean;\n /** Widen further still (e.g. for the plan comparison grid). */\n xl?: boolean;\n /** Trailing control on the title row (e.g. a close `IconButton`). Given,\n * the title and this node share one `.modal-head` row; absent, the title\n * renders as a direct child exactly as before. */\n headerRight?: React.ReactNode;\n}\n\n/**\n * Small dialog modal. Built on the native <dialog> element so it gets focus\n * trapping, Esc-dismiss, and accessibility semantics for free. A button\n * positioned over the backdrop catches outside-clicks to close.\n */\nexport function Modal({\n title,\n onClose,\n children,\n wide = false,\n xl = false,\n headerRight,\n}: ModalProps) {\n const ref = useRef<HTMLDialogElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (!el.open) el.showModal();\n const onCancel = (e: Event) => {\n e.preventDefault();\n onClose();\n };\n // A `showModal()` dialog + its ::backdrop sit in the top layer, above any\n // sibling overlay — so catch outside clicks on the dialog itself: backdrop\n // clicks target the dialog, and land outside its box.\n const onClick = (e: MouseEvent) => {\n if (e.target !== el) return;\n const r = el.getBoundingClientRect();\n const outside =\n e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;\n if (outside) onClose();\n };\n el.addEventListener(\"cancel\", onCancel);\n el.addEventListener(\"click\", onClick);\n return () => {\n el.removeEventListener(\"cancel\", onCancel);\n el.removeEventListener(\"click\", onClick);\n };\n }, [onClose]);\n\n return (\n <div className=\"modal-backdrop\">\n <dialog\n ref={ref}\n className={`modal${wide ? \" modal-wide\" : \"\"}${xl ? \" modal-xl\" : \"\"}`}\n aria-label={title}\n >\n {headerRight ? (\n <div className=\"modal-head\">\n <h3>{title}</h3>\n {headerRight}\n </div>\n ) : (\n <h3>{title}</h3>\n )}\n {children}\n </dialog>\n </div>\n );\n}\n","import {\n type SnippetCallable,\n type TriggerPreview,\n buildSnippet,\n unavailableReason,\n} from \"@w6w/embed/lib\";\nimport { CodeBlock, type CodeLanguage, IconButton, type IconName, Modal } from \"@w6w/ui\";\nimport { useState } from \"react\";\n\n/**\n * \"Call from code\" — the button that shows how to reach the thing you are\n * looking at from the Node SDK (`@w6w/sdk`), the CLI (`w6w`), or the Python\n * SDK (`w6w`, PyPI).\n *\n * Mounted on the pages whose resource the wrapper contract actually covers.\n * Everything shown comes from `../lib/sdk-snippets.ts`, which builds against\n * `packages/wrappers/endpoints.json` — this component owns presentation only\n * and never composes an API call of its own.\n *\n * A modal rather than an inline block: the snippet is reference material a\n * reader wants once and then dismisses, and inlining it would push the page's\n * real content (a function's inputs, a workflow's steps) below the fold for\n * everyone who is not currently writing a client.\n */\n\n/**\n * Tabs, in the order the contract lists the wrappers — then `curl`, which is\n * not a wrapper at all and therefore comes last.\n *\n * The strip renders only the tabs the BUILT snippet actually carries\n * (`Snippet.curl` is optional): a Function, Endpoint or Workflow shows all\n * four tabs, while a Connection, Var or Document — which have no fixed\n * single-resource route to curl against — still show exactly the three\n * Node/CLI/Python tabs they show today.\n */\nconst TABS = [\n { id: \"node\", label: \"Node\", language: \"TypeScript\", grammar: \"typescript\", icon: \"lang-node\" },\n { id: \"cli\", label: \"CLI\", language: \"Shell\", grammar: \"bash\", icon: \"terminal\" },\n { id: \"python\", label: \"Python\", language: \"Python\", grammar: \"python\", icon: \"lang-python\" },\n { id: \"curl\", label: \"curl\", language: \"Shell\", grammar: \"bash\", icon: \"lang-curl\" },\n] as const satisfies readonly {\n id: string;\n label: string;\n language: string;\n grammar: CodeLanguage;\n /** The tab's glyph, by name from `@w6w/ui`'s icon set (`icons.tsx`). Every\n * language mark this file used to draw — its own `NodeGlyph`/`CliGlyph`/\n * `PythonGlyph`/`CurlGlyph` — is one of those entries, copied verbatim, so\n * the tab strip and the rail's language picker draw the same four shapes\n * from one place. */\n icon: IconName;\n}[];\n\n/**\n * The tab table, exported so a caller can render its OWN affordance per\n * language without re-deriving the set. `CodeTool`'s rail panel uses it to draw\n * one icon per language and open this component's modal on that language.\n *\n * Exported as the table rather than as a list of ids on purpose: label, grammar\n * and glyph belong to the same row, and a second place that decided \"which\n * languages exist\" would be a second place to forget `curl`.\n */\nexport const SNIPPET_TABS = TABS;\n\ntype TabId = (typeof TABS)[number][\"id\"];\nexport type SnippetTabId = TabId;\n\n/**\n * Which language tabs a resource actually has — the same filter the strip\n * below applies, hoisted so a caller can ask BEFORE rendering.\n *\n * `curl` is emitted only where a public URL exists and was handed in (today: a\n * Function, a Workflow, or an Endpoint that knows its own server-rendered URL),\n * so a var, document or connection yields exactly Node/CLI/Python. Trigger\n * fields are deliberately not a parameter: they change what the snippet SAYS,\n * never which languages it is available in.\n *\n * Empty when the resource has no snippet at all — the caller should then show\n * {@link unavailableReason} rather than an empty row of icons.\n */\nexport function snippetTabsFor(resource: SnippetCallable): readonly (typeof TABS)[number][] {\n const snippet = buildSnippet(resource);\n return snippet ? TABS.filter((t) => typeof snippet[t.id] === \"string\") : [];\n}\n\n/**\n * The headless body — everything that renders the snippet itself: the\n * trigger-tab strip, the language-tab strip, the `CodeBlock`, and the per-tab\n * install-hint paragraph. No button, no `Modal`. Split out (T2.1.2) so\n * `CodeTool.Panel` (`../ui/rail/CodeTool.tsx`) can render this INSIDE the\n * right rail's own already-open panel, without a second trigger button or a\n * Modal-on-top-of-a-panel double-wrap — the rail's strip + panel disclosure\n * IS the trigger; this component only needs to draw what's inside it.\n * `CallFromCodeButton` below is unchanged in outward shape: the same button +\n * `Modal`, now rendering this body inside.\n */\nexport function CallFromCodeBody({\n resource,\n triggers,\n wrap = false,\n maxHeight = \"420px\",\n initialTab,\n}: {\n resource: SnippetCallable;\n /**\n * The workflow's declared manual (`@w6w/trigger`) triggers, one entry per\n * step, in declared order — optional so every other `CallFromCodeBody`\n * caller (function, endpoint, connection, var, document) is unaffected.\n * Both call sites derive this from a workflow definition already in scope\n * (T2.2.1, HITL-2/HITL-4 pinned); nothing here fetches it. The selected\n * trigger's declared fields are passed straight into `buildSnippet` and\n * rendered INSIDE the generated code — required fields as active code with\n * a type-derived placeholder, optional fields commented out (D-3/D-4) — so\n * there is nothing left to duplicate in a panel above it.\n */\n triggers?: TriggerPreview[];\n /**\n * Soft-wrap the code box instead of scrolling it horizontally. Default OFF\n * — see the `CodeBlock` call below for why off is right wherever there is\n * room, and why the right rail is the place where it is not.\n */\n wrap?: boolean;\n /** The code box's max height. Default `\"420px\"`, the modal's size. */\n maxHeight?: string;\n /**\n * Which language to open on. Seeds the selection ONCE, at mount — this is a\n * starting point, not a controlled value, so the strip stays clickable\n * afterwards.\n *\n * That \"once\" is load-bearing for the rail: `CodeTool` mounts this inside a\n * modal that is conditionally rendered, so picking Python, closing, and\n * picking Node re-mounts and re-seeds. A tab this resource does not carry\n * falls back to the first one it does, via `activeTab` below.\n */\n initialTab?: TabId;\n}) {\n const [tab, setTab] = useState<TabId>(initialTab ?? \"node\");\n const [triggerIndex, setTriggerIndex] = useState(0);\n const hasTriggers = !!triggers && triggers.length > 0;\n const selectedTrigger = hasTriggers\n ? (triggers[Math.min(triggerIndex, triggers.length - 1)] ?? triggers[0])\n : null;\n const snippet = buildSnippet(resource, selectedTrigger?.fields);\n // Only the tabs this snippet carries. `curl` is emitted for a Function,\n // Workflow, or an Endpoint that knows its own server-rendered URL — a var,\n // document or connection still renders exactly Node/CLI/Python.\n const tabs = snippet ? TABS.filter((t) => typeof snippet[t.id] === \"string\") : [];\n // Never leave the selection pointing at a tab that is not on the strip — the\n // component is mounted once per page and `tab` outlives a resource change.\n const activeTab: TabId = tabs.some((t) => t.id === tab) ? tab : (tabs[0]?.id ?? \"node\");\n const code = snippet?.[activeTab] ?? \"\";\n\n if (snippet === null) return <p className=\"muted\">{unavailableReason(resource)}</p>;\n\n return (\n <>\n {hasTriggers && selectedTrigger && (\n <div className=\"subpanel\" style={{ marginTop: 0, paddingTop: 0, borderTop: \"none\" }}>\n {/* Reuses `.code-tabs`/`.code-tab` — the modal's own existing\n Node/CLI/Python strip pattern (HITL-2: \"mirroring the\n modal's existing Node/CLI/Python strip\"). Nested INSIDE\n the trigger panel so the language tab-strip assertion\n below (which filters out anything under\n `[data-testid='trigger-panel']`) still sees exactly\n Node/CLI/Python. The selected trigger's fields are no\n longer listed HERE — they are rendered inside the code\n below (required active, optional commented; D-3/D-4),\n so a list here would only duplicate it. */}\n <div data-testid=\"trigger-panel\">\n <div className=\"code-tabs\">\n {triggers.map((t, i) => (\n <button\n key={t.id}\n type=\"button\"\n className={`code-tab${i === triggerIndex ? \" active\" : \"\"}`}\n data-testid=\"trigger-tab\"\n onClick={() => setTriggerIndex(i)}\n >\n {t.label}\n </button>\n ))}\n </div>\n {selectedTrigger.fields.length === 0 && (\n <p className=\"muted small\">This trigger declares no fields.</p>\n )}\n </div>\n {triggers.length > 1 && (\n <p className=\"muted small\" data-testid=\"trigger-multi-note\">\n This workflow has {triggers.length} manual triggers — the same input reaches every one\n of them identically; there is no per-trigger routing.\n </p>\n )}\n </div>\n )}\n\n <div className=\"code-tabs\">\n {tabs.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n className={`code-tab${activeTab === t.id ? \" active\" : \"\"}`}\n onClick={() => setTab(t.id)}\n >\n {t.label}\n </button>\n ))}\n </div>\n\n {/* `wrap` is a CALLER decision, defaulting OFF. Off is right wherever\n there is room: the CLI snippet's `\\`-continued lines are chosen line\n breaks, and soft-wrapping blurs them into the accidental ones, so a\n long Node line scrolls instead.\n It is wrong in the right RAIL, whose panel is ~395px wide — there\n every snippet scrolls horizontally, every time, and a snippet you\n have to drag sideways to read is not a snippet you can read. The\n rail passes `wrap`; the modal (which has the room) does not. The\n copy affordance lives inside the code box itself (`copyable`) — no\n local `CopyButton`, no `.code-tabs-spacer`. */}\n <CodeBlock\n code={code}\n language={TABS.find((t) => t.id === activeTab)?.grammar ?? \"plaintext\"}\n maxHeight={maxHeight}\n wrap={wrap}\n aria-label={`${TABS.find((t) => t.id === activeTab)?.label ?? activeTab} snippet`}\n copyable\n />\n\n {/* One arm per tab, and `python` is now EXPLICIT: it used to be\n the trailing `else`, so a fourth tab would silently have\n inherited \"pip install w6w\". The `curl` arm names no\n `operation` — raw HTTP maps to no wrapper-contract operation,\n and claiming one would be a promise the contract does not\n make. */}\n <p className=\"muted small\" style={{ marginBottom: 0 }}>\n {activeTab === \"node\" ? (\n <>\n <code>npm i @w6w/sdk</code> · set <code>W6W_BASE_URL</code> and <code>W6W_TOKEN</code> ·\n operation <code>{snippet.operation}</code>\n </>\n ) : activeTab === \"cli\" ? (\n <>\n <code>npm i -g @w6w/cli</code> · set <code>W6W_BASE_URL</code> and{\" \"}\n <code>W6W_TOKEN</code> (or pass <code>--base-url</code> / <code>--token</code>) ·\n operation <code>{snippet.operation}</code>\n </>\n ) : activeTab === \"python\" ? (\n <>\n <code>pip install w6w</code> · set <code>W6W_BASE_URL</code> and <code>W6W_TOKEN</code>{\" \"}\n · operation <code>{snippet.operation}</code>\n </>\n ) : (\n <>\n No client library — any HTTP client will do. Set <code>W6W_BASE_URL</code> to your w6w\n base URL (for an Endpoint, the URL above is the one this page is showing); substitute a\n w6w token for <code>$W6W_TOKEN</code> and send the declared input as the JSON body.\n </>\n )}\n </p>\n </>\n );\n}\n\nexport function CallFromCodeButton({\n resource,\n label = \"Call from code\",\n triggers,\n initialTab,\n}: {\n resource: SnippetCallable;\n label?: string;\n /** See {@link CallFromCodeBody}'s own doc — passed straight through. */\n triggers?: TriggerPreview[];\n /** See {@link CallFromCodeBody}'s own doc — passed straight through. */\n initialTab?: TabId;\n}) {\n const [open, setOpen] = useState(false);\n\n return (\n <>\n <IconButton icon=\"code\" label={label} onClick={() => setOpen(true)} />\n\n {open && (\n <Modal title=\"Call from code\" size=\"wide\" onClose={() => setOpen(false)}>\n <CallFromCodeBody resource={resource} triggers={triggers} initialTab={initialTab} />\n </Modal>\n )}\n </>\n );\n}\n","import { Modal } from \"./Modal.tsx\";\n\ninterface ConfirmModalProps {\n title: string;\n message: string;\n /** Fired when the user confirms. The caller clears its pending state via onClose. */\n onConfirm: () => void;\n /** Fired on Cancel, Esc, or outside-click — dismisses without confirming. */\n onClose: () => void;\n confirmLabel?: string;\n cancelLabel?: string;\n /** Style the confirm button as destructive (default true — this dialog gates dangerous actions). */\n destructive?: boolean;\n}\n\n/**\n * A reusable confirmation dialog built on our <dialog>-based Modal, so it inherits\n * focus-trap, Esc-dismiss, and outside-click close. Replaces the browser's native\n * confirmation prompt for destructive actions: Confirm fires `onConfirm`;\n * Cancel/Esc/outside-click fires `onClose` only (no confirmation).\n */\nexport function ConfirmModal({\n title,\n message,\n onConfirm,\n onClose,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n destructive = true,\n}: ConfirmModalProps) {\n return (\n <Modal title={title} onClose={onClose}>\n <div className=\"modal-body\">\n <p className=\"muted small\">{message}</p>\n </div>\n <div className=\"modal-actions\">\n <button type=\"button\" className=\"btn ghost\" onClick={onClose}>\n {cancelLabel}\n </button>\n <button type=\"button\" className={destructive ? \"btn danger\" : \"btn\"} onClick={onConfirm}>\n {confirmLabel}\n </button>\n </div>\n </Modal>\n );\n}\n"],"names":["Modal","title","onClose","children","wide","xl","headerRight","ref","useRef","useEffect","el","onCancel","e","onClick","r","jsx","jsxs","TABS","snippetTabsFor","resource","snippet","buildSnippet","t","CallFromCodeBody","triggers","wrap","maxHeight","initialTab","tab","setTab","useState","triggerIndex","setTriggerIndex","hasTriggers","selectedTrigger","tabs","activeTab","_a","code","unavailableReason","Fragment","i","CodeBlock","_b","_c","CallFromCodeButton","label","open","setOpen","IconButton","ConfirmModal","message","onConfirm","confirmLabel","cancelLabel","destructive"],"mappings":"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"}