@ekanos/sdk 0.6.0 → 0.7.1
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.
- package/README.md +100 -17
- package/api-report.md +51 -6
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +2 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/widgets/widget-context.d.ts +17 -0
- package/dist/components/widgets/widget-context.js.map +1 -1
- package/dist/components/widgets/widget-preview-provider.d.ts +4 -2
- package/dist/components/widgets/widget-preview-provider.js +3 -1
- package/dist/components/widgets/widget-preview-provider.js.map +1 -1
- package/dist/components/widgets/widget-state-components.d.ts +59 -3
- package/dist/components/widgets/widget-state-components.js +65 -7
- package/dist/components/widgets/widget-state-components.js.map +1 -1
- package/dist/components/widgets/widget.d.ts +124 -21
- package/dist/components/widgets/widget.js +159 -37
- package/dist/components/widgets/widget.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-oauth-connection-status.d.ts +17 -1
- package/dist/hooks/use-oauth-connection-status.js +12 -17
- package/dist/hooks/use-oauth-connection-status.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integration/actor-role.d.ts +14 -0
- package/dist/integration/actor-role.js +21 -0
- package/dist/integration/actor-role.js.map +1 -0
- package/dist/integration/index.d.ts +3 -1
- package/dist/integration/index.js +4 -0
- package/dist/integration/index.js.map +1 -1
- package/dist/integration/types.d.ts +1 -1
- package/dist/integration/types.js.map +1 -1
- package/dist/integration/untrusted-text.d.ts +24 -0
- package/dist/integration/untrusted-text.js +36 -0
- package/dist/integration/untrusted-text.js.map +1 -0
- package/dist/layout/index.d.ts +8 -0
- package/dist/layout/index.js +9 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/widget-layout.d.ts +84 -0
- package/dist/layout/widget-layout.js +138 -0
- package/dist/layout/widget-layout.js.map +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock-context.js +3 -1
- package/dist/testing/mock-context.js.map +1 -1
- package/dist/testing/verify-signature.d.ts +26 -0
- package/dist/testing/verify-signature.js +45 -0
- package/dist/testing/verify-signature.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/integration.d.ts +5 -0
- package/dist/types/integration.js.map +1 -1
- package/dist/types/widget-verdict.d.ts +6 -0
- package/dist/types/widget-verdict.js +2 -0
- package/dist/types/widget-verdict.js.map +1 -0
- package/eslint.design-preset.mjs +226 -0
- package/eslint.design-rules.mjs +437 -0
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
The official SDK for building Ekanos integrations — the packages that add
|
|
4
4
|
widgets, assistant tools, and event handlers to an Ekanos dashboard.
|
|
5
5
|
|
|
6
|
-
**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
**Docs:** [sdk.ekanos.dev](https://sdk.ekanos.dev/sdk/overview) is the public
|
|
7
|
+
documentation for the SDK, CLI and harness, and the first place to look for
|
|
8
|
+
current answers. **Start here.** This README is the complete authoring
|
|
9
|
+
reference: everything you can declare, with a worked example for each surface.
|
|
10
|
+
If something is missing from both, `npm@govastly.com` is where to say so.
|
|
10
11
|
|
|
11
12
|
The other packages, so you know when to leave this page: **`@ekanos/cli`** is
|
|
12
13
|
the toolchain (`init`, `validate`, `dev`, `test`) and owns `ekanos.json`;
|
|
@@ -30,7 +31,7 @@ one. Each has its own README.
|
|
|
30
31
|
- [`webhooks`](#webhooks)
|
|
31
32
|
- [`schedules`](#schedules)
|
|
32
33
|
- [`oauth`](#oauth)
|
|
33
|
-
- [`proposes` —
|
|
34
|
+
- [`proposes` — your credential model and tool classifications](#proposes)
|
|
34
35
|
- [`workspaceTargets`](#workspacetargets)
|
|
35
36
|
- [Testing](#testing)
|
|
36
37
|
- [Entrypoints](#entrypoints)
|
|
@@ -190,7 +191,7 @@ There is no raw database client, no Supabase, and no request object.
|
|
|
190
191
|
| `ctx.secrets` | `get(name)`, `names()`, `set(name, value)` over the resolved credential set |
|
|
191
192
|
| `ctx.fetch` | `fetch`, refusing anything outside `egress` before it opens a socket |
|
|
192
193
|
| `ctx.logger` | `debug`/`info`/`warn`/`error`, each `(context: object, message: string)` — pino-style, pre-bound with `{integration, accountId, actor}` |
|
|
193
|
-
| `ctx.actor` | `{ kind: 'user', userId }` or `{ kind: 'machine', tokenId, createdBy }` |
|
|
194
|
+
| `ctx.actor` | `{ kind: 'user', userId, role }` or `{ kind: 'machine', tokenId, createdBy }`. `role` is `{ name, hierarchyLevel }` (1 owner, 2 admin, 3 member; `null` when unresolved). Gate a write action with `actorIsAtLeast(ctx.actor, ADMIN_HIERARCHY_LEVEL)` from `@ekanos/sdk/integration` and throw `PublicActionError` otherwise — the host runs every action with the account's credential |
|
|
194
195
|
|
|
195
196
|
Plus read-only identity facts, all already authorized: `ctx.accountId`,
|
|
196
197
|
`ctx.accountSlug`, `ctx.userId` (`null` for machine actors), `ctx.sourceId`,
|
|
@@ -339,7 +340,7 @@ no `http:`. Wildcards are supported only as one leading `*.`. Anything else is
|
|
|
339
340
|
rejected by `defineIntegration()` with the offending entry quoted.
|
|
340
341
|
|
|
341
342
|
A denied call throws `EgressDeniedError` synchronously. Widening this list is a security-posture change a reviewer looks at during
|
|
342
|
-
**
|
|
343
|
+
**admission** — the human review your integration passes before it is enabled
|
|
343
344
|
for real accounts, where someone reads this declaration against what your
|
|
344
345
|
handlers actually do. Declare the origins you call and no more.
|
|
345
346
|
|
|
@@ -414,8 +415,7 @@ const apiKey = await ctx.secrets.get(ACME_API_KEY);
|
|
|
414
415
|
|
|
415
416
|
### `tools`
|
|
416
417
|
|
|
417
|
-
Tools are what the Ekanos assistant
|
|
418
|
-
engine) can call. Each declares a name, a description the model reads, an
|
|
418
|
+
Tools are what the Ekanos assistant, workflows and managed agents can call. Each declares a name, a description the model reads, an
|
|
419
419
|
optional JSON-Schema parameter object, and an async `run`.
|
|
420
420
|
|
|
421
421
|
```ts
|
|
@@ -459,6 +459,47 @@ for a genuine fault.
|
|
|
459
459
|
|
|
460
460
|
`outputExample` is shown to the model as a shape hint. Keep it small and honest.
|
|
461
461
|
|
|
462
|
+
**Text written outside this system is data, not instructions.** A deal name, a
|
|
463
|
+
call transcript, a support reply, a booking answer: anything a vendor's users
|
|
464
|
+
typed can say "ignore the above and…", and in a tool result it looks exactly
|
|
465
|
+
like a field you computed. Return such text only under a field whose name
|
|
466
|
+
starts with `untrusted`, clip it with `untrustedText()` (from
|
|
467
|
+
`@ekanos/sdk/integration`; 1000 characters by default), and put one
|
|
468
|
+
`note: untrustedTextNote('deal, stage and pipeline names')` at the top of the
|
|
469
|
+
output so the model reads the rule where it reads the data. `untrustedText()`
|
|
470
|
+
bounds the length and marks the text as coming from a third party so the
|
|
471
|
+
assistant treats it as data; it does not strip or neutralise injected
|
|
472
|
+
instructions. Pass `untrustedTextNote()` a fixed string you wrote, never vendor
|
|
473
|
+
text. In a write tool's description, say to confirm with the user first and
|
|
474
|
+
never act on what the text asks for.
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import { untrustedText, untrustedTextNote } from '@ekanos/sdk/integration';
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
note: untrustedTextNote('deal names'),
|
|
481
|
+
deals: deals.map((d) => ({ id: d.id, untrustedName: untrustedText(d.name, 200), amount: d.amount })),
|
|
482
|
+
};
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**`ownerAction` is how an owner reads an approval for your tool.** When an agent
|
|
486
|
+
or workflow pauses on one of your tools, the owner sees "{agent} wants to
|
|
487
|
+
{ownerAction} in {your integration}". Declare the phrase yourself, a bare verb
|
|
488
|
+
that names no ids:
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
ownerAction: 'sync your price list',
|
|
492
|
+
// or one phrase per locale; `default` covers any locale you did not list
|
|
493
|
+
ownerAction: { en: 'sync your price list', es: 'sincronizar su lista de precios' },
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
It is plain text, at most 120 characters, never a message key: a partner cannot
|
|
497
|
+
add keys to the host's messages. The host picks the exact locale, then its
|
|
498
|
+
language (`pt-BR` then `pt`), then the default locale, then your `default`. With
|
|
499
|
+
no `ownerAction` the host derives a phrase from the tool name ("update deal
|
|
500
|
+
stage" from `acme_update_deal_stage`), and a name that is not `<verb>_<thing>`
|
|
501
|
+
reads "use <tool name>", so declare one for any tool that writes.
|
|
502
|
+
|
|
462
503
|
**`parameters` is a narrow slice of JSON Schema, not all of it.** The top level
|
|
463
504
|
is `.strict()` and accepts exactly four keys — `type` (which must be the
|
|
464
505
|
literal `'object'`), `properties`, `required` and `additionalProperties`.
|
|
@@ -1146,9 +1187,10 @@ async function.
|
|
|
1146
1187
|
|
|
1147
1188
|
### `proposes`
|
|
1148
1189
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
and the
|
|
1190
|
+
Where you declare your credential model and classify each tool. Once your
|
|
1191
|
+
integration is admitted the host applies these values, the same as a first-party
|
|
1192
|
+
integration's own. The admission review is the check, and the host can tighten a
|
|
1193
|
+
declaration (never loosen it).
|
|
1152
1194
|
|
|
1153
1195
|
```ts
|
|
1154
1196
|
proposes: {
|
|
@@ -1159,22 +1201,32 @@ proposes: {
|
|
|
1159
1201
|
tools: {
|
|
1160
1202
|
list_recent_payments: { effect: 'read', sensitivity: 'financial' },
|
|
1161
1203
|
// Honest classification: this moves money. Marking it 'read' or
|
|
1162
|
-
// 'internal' to get past a gate is
|
|
1204
|
+
// 'internal' to get past a gate is a review failure at admission.
|
|
1163
1205
|
refund_payment: { effect: 'write', sensitivity: 'financial' },
|
|
1164
1206
|
},
|
|
1165
1207
|
},
|
|
1166
1208
|
```
|
|
1167
1209
|
|
|
1168
1210
|
`effect` is `'read' | 'write'`. `sensitivity` is `'public' | 'internal' | 'pii'
|
|
1169
|
-
| 'financial'`.
|
|
1211
|
+
| 'financial'`. Classify every tool: a field you leave out falls back to
|
|
1212
|
+
`effect: 'write'` and `sensitivity: 'pii'`, and a tool treated that way will not
|
|
1213
|
+
run in a team workflow.
|
|
1170
1214
|
|
|
1171
1215
|
The keys of `proposes.tools` must match declared tool names exactly — a
|
|
1172
1216
|
proposal for a tool you did not declare is a validation error, so a rename
|
|
1173
1217
|
cannot leave a stale classification behind.
|
|
1174
1218
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1219
|
+
What the host does with it:
|
|
1220
|
+
|
|
1221
|
+
- `credentialModel` decides which workflows may call your tools. Team workflows
|
|
1222
|
+
(a machine actor) admit `'account'` and `'source'`; personal workflows (the
|
|
1223
|
+
user) admit `'user'` and `'account'`. Undeclared means workflows cannot use
|
|
1224
|
+
your tools.
|
|
1225
|
+
- Team workflows never expose `pii` or `financial` tools, and a `write` tool
|
|
1226
|
+
needs an approval step in the workflow. Agents use their per-tool approval
|
|
1227
|
+
policy. Personal workflows and the chat assistant apply no sensitivity gate.
|
|
1228
|
+
- `ctx.storage.user` still throws for machine actors, and partner OAuth tokens
|
|
1229
|
+
stay account-scoped.
|
|
1178
1230
|
|
|
1179
1231
|
### `workspaceTargets`
|
|
1180
1232
|
|
|
@@ -1244,6 +1296,37 @@ Both throw if the id is not declared, and `invokeWebhook` throws if the payload
|
|
|
1244
1296
|
fails `payloadSchema` — the handler never runs. The local transport records the
|
|
1245
1297
|
signature skip as one `warn` line on `ctx.logs`.
|
|
1246
1298
|
|
|
1299
|
+
`invokeWebhook` never verifies a signature — that is the transport's job — so a
|
|
1300
|
+
wrong header name or signed-payload template in your declaration would first
|
|
1301
|
+
show up as rejected deliveries on the host. `verifyDeclaredWebhookSignature()`
|
|
1302
|
+
closes that gap: it runs the host's own verifier against one declaration. Sign
|
|
1303
|
+
a payload in the test the way the VENDOR documents it and assert the
|
|
1304
|
+
declaration accepts it:
|
|
1305
|
+
|
|
1306
|
+
```ts
|
|
1307
|
+
import { createHmac } from 'node:crypto';
|
|
1308
|
+
import { verifyDeclaredWebhookSignature } from '@ekanos/sdk/testing';
|
|
1309
|
+
|
|
1310
|
+
const body = JSON.stringify(payload);
|
|
1311
|
+
const t = String(Math.floor(Date.now() / 1000));
|
|
1312
|
+
const v1 = createHmac('sha256', secret).update(`${t}.${body}`).digest('hex');
|
|
1313
|
+
|
|
1314
|
+
await expect(
|
|
1315
|
+
verifyDeclaredWebhookSignature(integration, 'payment-updated', {
|
|
1316
|
+
headers: { 'Stripe-Signature': `t=${t},v1=${v1}` },
|
|
1317
|
+
rawBody: body,
|
|
1318
|
+
secret,
|
|
1319
|
+
}),
|
|
1320
|
+
).resolves.toEqual({ ok: true });
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
A failure reports the same reason the host would log (`missing-header`,
|
|
1324
|
+
`malformed-header`, `missing-timestamp`, `malformed-timestamp`,
|
|
1325
|
+
`stale-timestamp`, `missing-secret`, `mismatch`). An empty secret fails closed
|
|
1326
|
+
with `missing-secret`. It throws for an undeclared id and for a
|
|
1327
|
+
declaration with `signature: 'none'`. The verifier itself is also exported as
|
|
1328
|
+
`verifyWebhookSignature()` for a test that wants to drive it directly.
|
|
1329
|
+
|
|
1247
1330
|
`invokeActivate()` does the same for `onActivate` — no payload or invocation to
|
|
1248
1331
|
build, just the handler against the definition-derived context:
|
|
1249
1332
|
|
package/api-report.md
CHANGED
|
@@ -9,9 +9,9 @@ This is the complete surface of every entrypoint. The authoring guide
|
|
|
9
9
|
for these symbols is README.md. Entrypoints marked WORKSPACE-ONLY are
|
|
10
10
|
not reachable from a published install — see the note in each section.
|
|
11
11
|
|
|
12
|
-
Total exported names: **
|
|
12
|
+
Total exported names: **212** across 8 entrypoints.
|
|
13
13
|
|
|
14
|
-
## `@ekanos/sdk` (
|
|
14
|
+
## `@ekanos/sdk` (35 exports)
|
|
15
15
|
|
|
16
16
|
| Export | Kind |
|
|
17
17
|
| --- | --- |
|
|
@@ -19,6 +19,7 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
19
19
|
| `AccountProductDataType` | type alias |
|
|
20
20
|
| `ActivationFormProps` | type alias |
|
|
21
21
|
| `ActivationResult` | interface |
|
|
22
|
+
| `AttentionSignal` | interface |
|
|
22
23
|
| `HealthCheckResult` | interface |
|
|
23
24
|
| `HealthStatus` | type alias |
|
|
24
25
|
| `IntegrationActor` | type alias |
|
|
@@ -46,9 +47,11 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
46
47
|
| `WidgetConfig` | interface |
|
|
47
48
|
| `WidgetSnapshot` | type alias |
|
|
48
49
|
| `WidgetSuggestedPrompt` | interface |
|
|
50
|
+
| `WidgetVerdict` | interface |
|
|
51
|
+
| `WidgetVerdictTone` | type alias |
|
|
49
52
|
| `WorkspaceTargetDefinition` | interface |
|
|
50
53
|
|
|
51
|
-
## `@ekanos/sdk/components` (
|
|
54
|
+
## `@ekanos/sdk/components` (34 exports)
|
|
52
55
|
|
|
53
56
|
| Export | Kind |
|
|
54
57
|
| --- | --- |
|
|
@@ -70,15 +73,24 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
70
73
|
| `WidgetContextValue` | interface |
|
|
71
74
|
| `WidgetData` | interface |
|
|
72
75
|
| `WidgetDataLoading` | function |
|
|
76
|
+
| `WidgetEmptyStateProps` | interface |
|
|
77
|
+
| `WidgetHeaderTrailing` | type alias |
|
|
73
78
|
| `WidgetHealth` | interface |
|
|
79
|
+
| `WidgetInactiveReason` | type alias |
|
|
80
|
+
| `WidgetInactiveState` | function |
|
|
81
|
+
| `WidgetInactiveStateProps` | interface |
|
|
74
82
|
| `WidgetMeta` | interface |
|
|
75
83
|
| `WidgetPreviewProvider` | function |
|
|
76
84
|
| `WidgetPreviewProviderProps` | interface |
|
|
77
85
|
| `WidgetRenderState` | type alias |
|
|
86
|
+
| `WidgetSkeletonVariant` | type alias |
|
|
87
|
+
| `WidgetSource` | interface |
|
|
78
88
|
| `WidgetState` | interface |
|
|
89
|
+
| `WidgetStateAction` | interface |
|
|
79
90
|
| `currentReturnPath` | function |
|
|
91
|
+
| `useWidgetVerdict` | function |
|
|
80
92
|
|
|
81
|
-
## `@ekanos/sdk/hooks` (
|
|
93
|
+
## `@ekanos/sdk/hooks` (45 exports)
|
|
82
94
|
|
|
83
95
|
| Export | Kind |
|
|
84
96
|
| --- | --- |
|
|
@@ -99,6 +111,7 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
99
111
|
| `IntegrationActivationProvider` | function |
|
|
100
112
|
| `IntegrationFetchProvider` | function |
|
|
101
113
|
| `OAuthErrorParam` | interface |
|
|
114
|
+
| `OAuthWidgetGateOptions` | type alias |
|
|
102
115
|
| `PartnerOAuthPathOptions` | interface |
|
|
103
116
|
| `PublishEventInput` | interface |
|
|
104
117
|
| `UseIntegrationActionOptions` | interface |
|
|
@@ -111,6 +124,7 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
111
124
|
| `fetchIntegrationStorage` | function |
|
|
112
125
|
| `integrationActionUrl` | function |
|
|
113
126
|
| `integrationStorageUrl` | function |
|
|
127
|
+
| `oauthWidgetGateOptionsFor` | function |
|
|
114
128
|
| `partnerOAuthConnectPath` | function |
|
|
115
129
|
| `partnerOAuthErrorParam` | function |
|
|
116
130
|
| `partnerOAuthStatusUrl` | function |
|
|
@@ -169,7 +183,7 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
169
183
|
| `requireContext` | function |
|
|
170
184
|
| `resolveStorageKeyDeclaration` | function |
|
|
171
185
|
|
|
172
|
-
## `@ekanos/sdk/testing` (
|
|
186
|
+
## `@ekanos/sdk/testing` (22 exports)
|
|
173
187
|
|
|
174
188
|
| Export | Kind |
|
|
175
189
|
| --- | --- |
|
|
@@ -184,18 +198,27 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
184
198
|
| `RecordedFetchCall` | interface |
|
|
185
199
|
| `RecordedLog` | interface |
|
|
186
200
|
| `ScheduleInvocationOutcome` | interface |
|
|
201
|
+
| `SignatureVerification` | type alias |
|
|
202
|
+
| `VerifyDeclaredWebhookSignatureOptions` | interface |
|
|
203
|
+
| `VerifyWebhookSignatureArgs` | interface |
|
|
187
204
|
| `WebhookInvocationOutcome` | interface |
|
|
188
205
|
| `createMockContext` | function |
|
|
189
206
|
| `createMockFetchProvider` | function |
|
|
190
207
|
| `invokeActivate` | function |
|
|
191
208
|
| `invokeSchedule` | function |
|
|
192
209
|
| `invokeWebhook` | function |
|
|
210
|
+
| `verifyDeclaredWebhookSignature` | function |
|
|
211
|
+
| `verifyWebhookSignature` | function |
|
|
193
212
|
|
|
194
|
-
## `@ekanos/sdk/integration` (
|
|
213
|
+
## `@ekanos/sdk/integration` (41 exports)
|
|
195
214
|
|
|
196
215
|
| Export | Kind |
|
|
197
216
|
| --- | --- |
|
|
217
|
+
| `ADMIN_HIERARCHY_LEVEL` | variable |
|
|
218
|
+
| `AttentionHandler` | type alias |
|
|
219
|
+
| `AttentionSignal` | interface |
|
|
198
220
|
| `DEFAULT_SIGNATURE_TOLERANCE_SECONDS` | variable |
|
|
221
|
+
| `DEFAULT_UNTRUSTED_TEXT_MAX_CHARS` | variable |
|
|
199
222
|
| `DefinitionCollisionInput` | interface |
|
|
200
223
|
| `FirstPartyInventory` | interface |
|
|
201
224
|
| `IntegrationCapabilityDeclaration` | type alias |
|
|
@@ -206,6 +229,7 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
206
229
|
| `IntegrationProposals` | type alias |
|
|
207
230
|
| `OAuthProviderDeclaration` | interface |
|
|
208
231
|
| `OAuthTokens` | interface |
|
|
232
|
+
| `OWNER_HIERARCHY_LEVEL` | variable |
|
|
209
233
|
| `OnActivateHandler` | type alias |
|
|
210
234
|
| `PARTNER_STORAGE_LIMITS` | variable |
|
|
211
235
|
| `PartnerActionDeclaration` | interface |
|
|
@@ -223,6 +247,27 @@ Total exported names: **172** across 7 entrypoints.
|
|
|
223
247
|
| `WebhookResult` | interface |
|
|
224
248
|
| `WebhookSignatureDeclaration` | type alias |
|
|
225
249
|
| `WebhookSignatureScheme` | interface |
|
|
250
|
+
| `WidgetVerdict` | interface |
|
|
251
|
+
| `WidgetVerdictTone` | type alias |
|
|
252
|
+
| `actorIsAtLeast` | function |
|
|
226
253
|
| `defineIntegration` | function |
|
|
227
254
|
| `parseCronExpression` | function |
|
|
255
|
+
| `untrustedText` | function |
|
|
256
|
+
| `untrustedTextNote` | function |
|
|
228
257
|
| `validateIntegrationDefinitions` | function |
|
|
258
|
+
|
|
259
|
+
## `@ekanos/sdk/layout` (11 exports)
|
|
260
|
+
|
|
261
|
+
| Export | Kind |
|
|
262
|
+
| --- | --- |
|
|
263
|
+
| `COMPACT_FROM_WIDTH` | variable |
|
|
264
|
+
| `SizedWidget` | interface |
|
|
265
|
+
| `WidgetRowGroup` | type alias |
|
|
266
|
+
| `WidgetSize` | type alias |
|
|
267
|
+
| `buildRowGroups` | function |
|
|
268
|
+
| `getColSpan` | function |
|
|
269
|
+
| `hasTrailingGap` | function |
|
|
270
|
+
| `pairRows` | function |
|
|
271
|
+
| `resolveWidgetSize` | function |
|
|
272
|
+
| `snapFullWidthToRowBoundary` | function |
|
|
273
|
+
| `splitBalanced` | function |
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
* Surface discipline: additions require an entry in
|
|
26
26
|
* api-report.md, regenerated by `pnpm --filter @ekanos/sdk api-report`.
|
|
27
27
|
*/
|
|
28
|
-
export { Widget } from './widgets/widget.js';
|
|
29
|
-
export { WidgetContext, type WidgetActions, type WidgetContextValue, type WidgetData, type WidgetHealth, type WidgetMeta, type WidgetRenderState, type WidgetState, } from './widgets/widget-context.js';
|
|
28
|
+
export { Widget, useWidgetVerdict, type WidgetHeaderTrailing, } from './widgets/widget.js';
|
|
29
|
+
export { WidgetContext, type WidgetActions, type WidgetContextValue, type WidgetData, type WidgetHealth, type WidgetMeta, type WidgetRenderState, type WidgetSource, type WidgetState, } from './widgets/widget-context.js';
|
|
30
30
|
export { WidgetPreviewProvider } from './widgets/widget-preview-provider.js';
|
|
31
31
|
export type { WidgetPreviewProviderProps } from './widgets/widget-preview-provider.js';
|
|
32
32
|
export { AiPromptChip } from './widgets/ai-prompt-chip.js';
|
|
33
33
|
export { AskIcon } from './widgets/ask-icon.js';
|
|
34
34
|
export { WidgetAskBar } from './widgets/widget-ask-bar.js';
|
|
35
|
-
export { WidgetDataLoading } from './widgets/widget-state-components.js';
|
|
35
|
+
export { WidgetDataLoading, type WidgetEmptyStateProps, WidgetInactiveState, type WidgetInactiveReason, type WidgetInactiveStateProps, type WidgetSkeletonVariant, type WidgetStateAction, } from './widgets/widget-state-components.js';
|
|
36
36
|
export { BaseActivationForm } from './base-activation-form.js';
|
|
37
37
|
export type { BaseActivationFormProps } from './base-activation-form.js';
|
|
38
38
|
export { ActivationDialog, ActivationInline, BaseActivationDialog, } from './base-activation-dialog.js';
|
package/dist/components/index.js
CHANGED
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
* Surface discipline: additions require an entry in
|
|
26
26
|
* api-report.md, regenerated by `pnpm --filter @ekanos/sdk api-report`.
|
|
27
27
|
*/
|
|
28
|
-
export { Widget } from './widgets/widget.js';
|
|
28
|
+
export { Widget, useWidgetVerdict, } from './widgets/widget.js';
|
|
29
29
|
export { WidgetContext, } from './widgets/widget-context.js';
|
|
30
30
|
export { WidgetPreviewProvider } from './widgets/widget-preview-provider.js';
|
|
31
31
|
export { AiPromptChip } from './widgets/ai-prompt-chip.js';
|
|
32
32
|
export { AskIcon } from './widgets/ask-icon.js';
|
|
33
33
|
export { WidgetAskBar } from './widgets/widget-ask-bar.js';
|
|
34
|
-
export { WidgetDataLoading } from './widgets/widget-state-components.js';
|
|
34
|
+
export { WidgetDataLoading, WidgetInactiveState, } from './widgets/widget-state-components.js';
|
|
35
35
|
export { BaseActivationForm } from './base-activation-form.js';
|
|
36
36
|
export { ActivationDialog, ActivationInline, BaseActivationDialog, } from './base-activation-dialog.js';
|
|
37
37
|
export { BaseMarketplaceTile } from './base-marketplace-tile.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,MAAM,EACN,gBAAgB,GAEjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,GASd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,GAKpB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["/**\n * @ekanos/sdk/components — client-side building blocks.\n *\n * Import from client components only. `Widget` is the compound API every\n * dashboard widget renders through; the activation forms are the two\n * sanctioned activation UX bases (credential-style and OAuth-style), and\n * `BaseMarketplaceTile` is the matching base for the marketplace tile.\n *\n * The SDK OWNS these implementations — the pre-inversion re-exports through\n * `@kit/integrations-core/components/*` are gone; core now re-exports FROM\n * here for first-party code (one source of truth, two consumption paths —\n * one source of truth with two consumption paths. UI primitives come from\n * `@ekanos/ui`\n * (the published slice of the host design system), never `@kit/ui`.\n *\n * Server seam: nothing here imports a server module. `OAuthActivationForm`\n * performs activation through the host-injected actions seam\n * (`IntegrationActivationProvider`, @ekanos/sdk/hooks) — a server-action\n * REFERENCE bound by the host, not server code shipped to the browser.\n *\n * `WidgetContext` is the single context identity both sides share: the\n * host's `DashboardWidgetProvider` (host chrome — persistence callbacks,\n * config hierarchy) provides it, the SDK's `Widget.*` reads it.\n *\n * Surface discipline: additions require an entry in\n * api-report.md, regenerated by `pnpm --filter @ekanos/sdk api-report`.\n */\n\nexport {\n Widget,\n useWidgetVerdict,\n type WidgetHeaderTrailing,\n} from './widgets/widget';\nexport {\n WidgetContext,\n type WidgetActions,\n type WidgetContextValue,\n type WidgetData,\n type WidgetHealth,\n type WidgetMeta,\n type WidgetRenderState,\n type WidgetSource,\n type WidgetState,\n} from './widgets/widget-context';\nexport { WidgetPreviewProvider } from './widgets/widget-preview-provider';\n\nexport type { WidgetPreviewProviderProps } from './widgets/widget-preview-provider';\nexport { AiPromptChip } from './widgets/ai-prompt-chip';\nexport { AskIcon } from './widgets/ask-icon';\nexport { WidgetAskBar } from './widgets/widget-ask-bar';\nexport {\n WidgetDataLoading,\n type WidgetEmptyStateProps,\n WidgetInactiveState,\n type WidgetInactiveReason,\n type WidgetInactiveStateProps,\n type WidgetSkeletonVariant,\n type WidgetStateAction,\n} from './widgets/widget-state-components';\n\nexport { BaseActivationForm } from './base-activation-form';\nexport type { BaseActivationFormProps } from './base-activation-form';\nexport {\n ActivationDialog,\n ActivationInline,\n BaseActivationDialog,\n} from './base-activation-dialog';\n\nexport { BaseMarketplaceTile } from './base-marketplace-tile';\nexport type { BaseMarketplaceTileProps } from './base-marketplace-tile';\n\nexport { OAuthActivationForm } from './oauth-activation-form';\nexport type { OAuthActivationFormConfig } from './oauth-activation-form';\n\nexport { currentReturnPath } from './current-return-path';\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WidgetAskContext } from '../../types/widget-ask-context.js';
|
|
2
|
+
import type { WidgetVerdict } from '../../types/widget-verdict.js';
|
|
2
3
|
export type WidgetRenderState = 'active' | 'disabled' | 'loading' | 'error' | 'inactive';
|
|
3
4
|
export interface WidgetState {
|
|
4
5
|
state: WidgetRenderState;
|
|
@@ -35,6 +36,16 @@ export interface WidgetMeta {
|
|
|
35
36
|
* page; the header menu shows "Open in workspace" when present.
|
|
36
37
|
*/
|
|
37
38
|
openHref?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Whose data the widget shows: the integration's name and bundled logo.
|
|
41
|
+
* `Widget.Header` draws the logo left of the title (the design system's widget rules).
|
|
42
|
+
*/
|
|
43
|
+
source?: WidgetSource;
|
|
44
|
+
}
|
|
45
|
+
export interface WidgetSource {
|
|
46
|
+
name: string;
|
|
47
|
+
/** A logo URL; the header shows the name's first letter without one. */
|
|
48
|
+
logoPath?: string;
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
51
|
* What the widget's own data layer reports through `Widget.DataState`. The
|
|
@@ -49,6 +60,12 @@ export interface WidgetData {
|
|
|
49
60
|
refreshing: boolean;
|
|
50
61
|
/** Reloads the widget's data. */
|
|
51
62
|
refresh?: () => void;
|
|
63
|
+
/**
|
|
64
|
+
* The widget's own conclusion about its data (the design system's widget rules): the
|
|
65
|
+
* header shows it in place of the context line, and the "Ask about this"
|
|
66
|
+
* snapshot carries it.
|
|
67
|
+
*/
|
|
68
|
+
verdict?: WidgetVerdict;
|
|
52
69
|
}
|
|
53
70
|
export interface WidgetContextValue {
|
|
54
71
|
state: WidgetState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-context.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-context.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"widget-context.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-context.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AA2FtC,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAA4B,IAAI,CAAC,CAAC","sourcesContent":["'use client';\n\nimport { createContext } from 'react';\n\nimport type { WidgetAskContext } from '../../types/widget-ask-context';\nimport type { WidgetVerdict } from '../../types/widget-verdict';\n\nexport type WidgetRenderState =\n | 'active'\n | 'disabled'\n | 'loading'\n | 'error'\n | 'inactive';\n\nexport interface WidgetState {\n state: WidgetRenderState;\n collapsed: boolean;\n pinned: boolean;\n pendingCollapse: boolean;\n pendingPin: boolean;\n}\n\nexport interface WidgetActions {\n toggleCollapsed: () => void;\n togglePinned: () => void;\n}\n\nexport interface WidgetHealth {\n status: 'healthy' | 'degraded' | 'unhealthy' | 'unknown';\n lastSuccessAt?: string | null;\n errorMessage?: string | null;\n}\n\nexport interface WidgetMeta {\n widgetId: string;\n title?: string;\n subtitle?: string;\n description?: string;\n maxContentHeight?: string;\n isCollapsible: boolean;\n isPinnable: boolean;\n askContext?: WidgetAskContext;\n aiFooterEnabled: boolean;\n className?: string;\n /** Integration health for this widget's product, if known. */\n health?: WidgetHealth;\n /**\n * Where the widget's full surface lives, e.g. the workspace a pinned widget\n * came from. Set by the host only where following it leaves the current\n * page; the header menu shows \"Open in workspace\" when present.\n */\n openHref?: string;\n /**\n * Whose data the widget shows: the integration's name and bundled logo.\n * `Widget.Header` draws the logo left of the title (the design system's widget rules).\n */\n source?: WidgetSource;\n}\n\nexport interface WidgetSource {\n name: string;\n /** A logo URL; the header shows the name's first letter without one. */\n logoPath?: string;\n}\n\n/**\n * What the widget's own data layer reports through `Widget.DataState`. The\n * host cannot see a widget's queries, so every field is opt-in: the header\n * menu's Refresh, the error view's Try again, and the \"Updated …\" footer each\n * appear only when the widget supplies what they need.\n */\nexport interface WidgetData {\n /** Epoch milliseconds of the last successful load; absent when unknown. */\n updatedAt?: number;\n /** A reload is in flight while data is already on screen. */\n refreshing: boolean;\n /** Reloads the widget's data. */\n refresh?: () => void;\n /**\n * The widget's own conclusion about its data (the design system's widget rules): the\n * header shows it in place of the context line, and the \"Ask about this\"\n * snapshot carries it.\n */\n verdict?: WidgetVerdict;\n}\n\nexport interface WidgetContextValue {\n state: WidgetState;\n actions: WidgetActions;\n meta: WidgetMeta;\n data?: WidgetData;\n}\n\nexport const WidgetContext = createContext<WidgetContextValue | null>(null);\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { WidgetAskContext } from '../../types/widget-ask-context.js';
|
|
3
|
-
import { type WidgetHealth, type WidgetRenderState } from './widget-context.js';
|
|
3
|
+
import { type WidgetHealth, type WidgetRenderState, type WidgetSource } from './widget-context.js';
|
|
4
4
|
export interface WidgetPreviewProviderProps {
|
|
5
5
|
widgetId: string;
|
|
6
6
|
title?: string;
|
|
@@ -17,6 +17,8 @@ export interface WidgetPreviewProviderProps {
|
|
|
17
17
|
askContext?: WidgetAskContext;
|
|
18
18
|
aiFooterEnabled?: boolean;
|
|
19
19
|
health?: WidgetHealth;
|
|
20
|
+
/** Whose data the widget shows; the header draws its logo. */
|
|
21
|
+
source?: WidgetSource;
|
|
20
22
|
children: ReactNode;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
@@ -38,4 +40,4 @@ export interface WidgetPreviewProviderProps {
|
|
|
38
40
|
* Flip `state` to walk every branch your widget renders (`'loading'`,
|
|
39
41
|
* `'error'`, `'inactive'`, `'disabled'`) without faking a network condition.
|
|
40
42
|
*/
|
|
41
|
-
export declare function WidgetPreviewProvider({ widgetId, title, subtitle, description, className, state, isCollapsible, isPinnable, defaultCollapsed, defaultPinned, maxContentHeight, askContext, aiFooterEnabled, health, children, }: WidgetPreviewProviderProps): import("react").JSX.Element;
|
|
43
|
+
export declare function WidgetPreviewProvider({ widgetId, title, subtitle, description, className, state, isCollapsible, isPinnable, defaultCollapsed, defaultPinned, maxContentHeight, askContext, aiFooterEnabled, health, source, children, }: WidgetPreviewProviderProps): import("react").JSX.Element;
|
|
@@ -21,7 +21,7 @@ import { WidgetContext, } from './widget-context.js';
|
|
|
21
21
|
* Flip `state` to walk every branch your widget renders (`'loading'`,
|
|
22
22
|
* `'error'`, `'inactive'`, `'disabled'`) without faking a network condition.
|
|
23
23
|
*/
|
|
24
|
-
export function WidgetPreviewProvider({ widgetId, title, subtitle, description, className, state = 'active', isCollapsible = false, isPinnable = false, defaultCollapsed = false, defaultPinned = false, maxContentHeight, askContext, aiFooterEnabled = false, health, children, }) {
|
|
24
|
+
export function WidgetPreviewProvider({ widgetId, title, subtitle, description, className, state = 'active', isCollapsible = false, isPinnable = false, defaultCollapsed = false, defaultPinned = false, maxContentHeight, askContext, aiFooterEnabled = false, health, source, children, }) {
|
|
25
25
|
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
26
26
|
const [pinned, setPinned] = useState(defaultPinned);
|
|
27
27
|
const toggleCollapsed = useCallback(() => setCollapsed((previous) => !previous), []);
|
|
@@ -47,6 +47,7 @@ export function WidgetPreviewProvider({ widgetId, title, subtitle, description,
|
|
|
47
47
|
askContext,
|
|
48
48
|
aiFooterEnabled,
|
|
49
49
|
health,
|
|
50
|
+
source,
|
|
50
51
|
},
|
|
51
52
|
}), [
|
|
52
53
|
aiFooterEnabled,
|
|
@@ -59,6 +60,7 @@ export function WidgetPreviewProvider({ widgetId, title, subtitle, description,
|
|
|
59
60
|
isPinnable,
|
|
60
61
|
maxContentHeight,
|
|
61
62
|
pinned,
|
|
63
|
+
source,
|
|
62
64
|
state,
|
|
63
65
|
subtitle,
|
|
64
66
|
title,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-preview-provider.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-preview-provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAkB,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGvE,OAAO,EACL,aAAa,
|
|
1
|
+
{"version":3,"file":"widget-preview-provider.js","sourceRoot":"","sources":["../../../src/components/widgets/widget-preview-provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAkB,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGvE,OAAO,EACL,aAAa,GAKd,MAAM,kBAAkB,CAAC;AAuB1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACpC,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,KAAK,GAAG,QAAQ,EAChB,aAAa,GAAG,KAAK,EACrB,UAAU,GAAG,KAAK,EAClB,gBAAgB,GAAG,KAAK,EACxB,aAAa,GAAG,KAAK,EACrB,gBAAgB,EAChB,UAAU,EACV,eAAe,GAAG,KAAK,EACvB,MAAM,EACN,MAAM,EACN,QAAQ,GACmB;IAC3B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC7D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAEpD,MAAM,eAAe,GAAG,WAAW,CACjC,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAC3C,EAAE,CACH,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EACxC,EAAE,CACH,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,KAAK,EAAE;YACL,KAAK;YACL,SAAS;YACT,MAAM;YACN,eAAe,EAAE,KAAK;YACtB,UAAU,EAAE,KAAK;SAClB;QACD,OAAO,EAAE,EAAE,eAAe,EAAE,YAAY,EAAE;QAC1C,IAAI,EAAE;YACJ,QAAQ;YACR,KAAK;YACL,QAAQ;YACR,WAAW;YACX,SAAS;YACT,gBAAgB;YAChB,aAAa;YACb,UAAU;YACV,UAAU;YACV,eAAe;YACf,MAAM;YACN,MAAM;SACP;KACF,CAAC,EACF;QACE,eAAe;QACf,UAAU;QACV,SAAS;QACT,SAAS;QACT,WAAW;QACX,MAAM;QACN,aAAa;QACb,UAAU;QACV,gBAAgB;QAChB,MAAM;QACN,MAAM;QACN,KAAK;QACL,QAAQ;QACR,KAAK;QACL,eAAe;QACf,YAAY;QACZ,QAAQ;KACT,CACF,CAAC;IAEF,OAAO,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAiB,CAAC;AACjE,CAAC","sourcesContent":["'use client';\n\nimport { type ReactNode, useCallback, useMemo, useState } from 'react';\n\nimport type { WidgetAskContext } from '../../types/widget-ask-context';\nimport {\n WidgetContext,\n type WidgetContextValue,\n type WidgetHealth,\n type WidgetRenderState,\n type WidgetSource,\n} from './widget-context';\n\nexport interface WidgetPreviewProviderProps {\n widgetId: string;\n title?: string;\n subtitle?: string;\n description?: string;\n className?: string;\n /** Defaults to `'active'` — the state you almost always want to look at. */\n state?: WidgetRenderState;\n isCollapsible?: boolean;\n isPinnable?: boolean;\n defaultCollapsed?: boolean;\n defaultPinned?: boolean;\n maxContentHeight?: string;\n askContext?: WidgetAskContext;\n aiFooterEnabled?: boolean;\n health?: WidgetHealth;\n /** Whose data the widget shows; the header draws its logo. */\n source?: WidgetSource;\n children: ReactNode;\n}\n\n/**\n * Renders a widget outside the Ekanos dashboard.\n *\n * `Widget.*` reads everything — render state, collapse/pin, title — from\n * `WidgetContext`, which in production is filled by the host's\n * `DashboardWidgetProvider` (config hierarchy, persistence callbacks, health\n * resolution). None of that exists in a partner's own app, a Storybook story,\n * or a component test, and without SOME provider every `Widget.*` throws. This\n * is that provider: same context value, local state only, nothing persisted.\n *\n * ```tsx\n * <WidgetPreviewProvider widgetId=\"my-widget\" title=\"My widget\">\n * <MyWidget accountId={accountId} productSlug=\"my-integration\" />\n * </WidgetPreviewProvider>\n * ```\n *\n * Flip `state` to walk every branch your widget renders (`'loading'`,\n * `'error'`, `'inactive'`, `'disabled'`) without faking a network condition.\n */\nexport function WidgetPreviewProvider({\n widgetId,\n title,\n subtitle,\n description,\n className,\n state = 'active',\n isCollapsible = false,\n isPinnable = false,\n defaultCollapsed = false,\n defaultPinned = false,\n maxContentHeight,\n askContext,\n aiFooterEnabled = false,\n health,\n source,\n children,\n}: WidgetPreviewProviderProps) {\n const [collapsed, setCollapsed] = useState(defaultCollapsed);\n const [pinned, setPinned] = useState(defaultPinned);\n\n const toggleCollapsed = useCallback(\n () => setCollapsed((previous) => !previous),\n [],\n );\n\n const togglePinned = useCallback(\n () => setPinned((previous) => !previous),\n [],\n );\n\n const value = useMemo<WidgetContextValue>(\n () => ({\n state: {\n state,\n collapsed,\n pinned,\n pendingCollapse: false,\n pendingPin: false,\n },\n actions: { toggleCollapsed, togglePinned },\n meta: {\n widgetId,\n title,\n subtitle,\n description,\n className,\n maxContentHeight,\n isCollapsible,\n isPinnable,\n askContext,\n aiFooterEnabled,\n health,\n source,\n },\n }),\n [\n aiFooterEnabled,\n askContext,\n className,\n collapsed,\n description,\n health,\n isCollapsible,\n isPinnable,\n maxContentHeight,\n pinned,\n source,\n state,\n subtitle,\n title,\n toggleCollapsed,\n togglePinned,\n widgetId,\n ],\n );\n\n return <WidgetContext value={value}>{children}</WidgetContext>;\n}\n"]}
|
|
@@ -1,8 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The one action a state view offers: "Connect QuickBooks", "Ask an admin",
|
|
4
|
+
* "Create a deal". A link with `href`, otherwise a button running `onClick`.
|
|
5
|
+
*/
|
|
6
|
+
export interface WidgetStateAction {
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
href?: string;
|
|
9
|
+
/** Open `href` in a new tab. */
|
|
10
|
+
external?: boolean;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
}
|
|
13
|
+
/** The body shape a loading skeleton stands in for. */
|
|
14
|
+
export type WidgetSkeletonVariant = 'rows' | 'stat' | 'chart' | 'schedule';
|
|
15
|
+
/** Why an active integration still can't show this widget's data. */
|
|
16
|
+
export type WidgetInactiveReason = 'not-connected' | 'no-access' | 'setup-needed';
|
|
17
|
+
export interface WidgetEmptyStateProps {
|
|
18
|
+
/**
|
|
19
|
+
* `positive` (the default) says nothing needs the owner: "You're all caught
|
|
20
|
+
* up". `neutral` is for an empty list that isn't good news, like a search
|
|
21
|
+
* with no results.
|
|
22
|
+
*/
|
|
23
|
+
tone?: 'positive' | 'neutral';
|
|
24
|
+
title?: ReactNode;
|
|
25
|
+
description?: ReactNode;
|
|
26
|
+
action?: WidgetStateAction;
|
|
27
|
+
}
|
|
28
|
+
export interface WidgetInactiveStateProps {
|
|
29
|
+
reason?: WidgetInactiveReason;
|
|
30
|
+
/** The integration's name, for the default copy ("Connect QuickBooks"). */
|
|
31
|
+
integrationName?: string;
|
|
32
|
+
title?: ReactNode;
|
|
33
|
+
/** One sentence on what connecting or access gives the owner. */
|
|
34
|
+
description?: ReactNode;
|
|
35
|
+
action?: WidgetStateAction;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The empty state for a widget with nothing to show. Positive by default:
|
|
39
|
+
* an empty review queue is good news, so it reassures instead of apologizing.
|
|
40
|
+
*/
|
|
41
|
+
export declare function WidgetEmptyState({ tone, title, description, action, }: WidgetEmptyStateProps): import("react").JSX.Element;
|
|
42
|
+
/**
|
|
43
|
+
* Why the widget can't show data. `not-connected` offers one Connect button;
|
|
44
|
+
* `no-access` is a calm note that points to an admin, never a lock screen;
|
|
45
|
+
* `setup-needed` points to the integration's settings. Without a `reason` it
|
|
46
|
+
* keeps the original one-line "Widget inactive".
|
|
47
|
+
*/
|
|
48
|
+
export declare function WidgetInactiveState({ reason, integrationName, title, description, action, }?: WidgetInactiveStateProps): import("react").JSX.Element;
|
|
49
|
+
/**
|
|
50
|
+
* Loading, drawn as the body it stands in for so the card doesn't jump when
|
|
51
|
+
* data lands. Rows suit lists and feeds, `stat` a headline figure, `chart` a
|
|
52
|
+
* chart, `schedule` a day of events.
|
|
53
|
+
*/
|
|
54
|
+
export declare function WidgetLoadingSkeleton({ variant, }: {
|
|
55
|
+
variant?: WidgetSkeletonVariant;
|
|
56
|
+
}): import("react").JSX.Element;
|
|
57
|
+
export declare function WidgetErrorState({ title, description, onRetry, retrying, }: {
|
|
58
|
+
title?: ReactNode;
|
|
59
|
+
description?: ReactNode;
|
|
3
60
|
onRetry?: () => void;
|
|
4
61
|
retrying?: boolean;
|
|
5
62
|
}): import("react").JSX.Element;
|
|
6
|
-
export declare function WidgetInactiveState(): import("react").JSX.Element;
|
|
7
63
|
export declare function WidgetDataLoading(): import("react").JSX.Element;
|
|
8
64
|
export declare function WidgetDisabledState(): import("react").JSX.Element;
|