@ai-matrx/kit 0.8.0 → 0.9.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.
- package/CHANGELOG.md +96 -0
- package/README.md +11 -4
- package/dist/confirm-opener.cjs.map +1 -1
- package/dist/confirm-opener.d.cts +6 -5
- package/dist/confirm-opener.d.ts +6 -5
- package/dist/confirm-opener.js.map +1 -1
- package/dist/index.cjs +53 -334
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +75 -356
- package/dist/index.js.map +1 -1
- package/package.json +4 -16
- package/dist/confirm.cjs +0 -353
- package/dist/confirm.cjs.map +0 -1
- package/dist/confirm.d.cts +0 -139
- package/dist/confirm.d.ts +0 -139
- package/dist/confirm.js +0 -321
- package/dist/confirm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,101 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 — 2026-09-07
|
|
4
|
+
|
|
5
|
+
**THE CONFIRM DIALOG LEAVES THIS PACKAGE.** `@ai-matrx/kit/confirm` is removed.
|
|
6
|
+
The dialog body — `ConfirmDialog` and `ConfirmDialogHost` — now lives in
|
|
7
|
+
**`@ai-matrx/design-system` 0.11.0**. What stays here is
|
|
8
|
+
`@ai-matrx/kit/confirm-opener`: the pure imperative `confirm()`, the host
|
|
9
|
+
registry, and the request queue, unchanged.
|
|
10
|
+
|
|
11
|
+
**Why.** kit's confirm shipped its own inlined AlertDialog wearing
|
|
12
|
+
`data-[state=open]:animate-in … zoom-in-95 … slide-in-from-top-[48%]` — utilities
|
|
13
|
+
the `tailwindcss-animate` / `tw-animate-css` HOST plugin supplies, which a
|
|
14
|
+
node_modules package can neither ship nor require — plus a hardcoded
|
|
15
|
+
`bg-black/80` scrim. kit ships no stylesheet at all, so in three of the four
|
|
16
|
+
consumers the most consequential surface in the product (the one standing between
|
|
17
|
+
a user and a destroyed thing) appeared with no animation, no error, and a scrim
|
|
18
|
+
that ignored the theme. Its unclamped content could also push Cancel and Continue
|
|
19
|
+
below the fold of a short viewport — and an AlertDialog does not dismiss on a
|
|
20
|
+
backdrop click, so a long confirmation was a genuine dead end.
|
|
21
|
+
|
|
22
|
+
kit could not import design-system's 0.10.0 fix: **design-system depends on kit**,
|
|
23
|
+
so importing back is a cycle. Giving kit its own `styles.css` would invent a
|
|
24
|
+
second required-CSS contract no consumer performs — the same silent host contract
|
|
25
|
+
one layer down (C26). So the surface moved to the package that owns the surface,
|
|
26
|
+
exactly as the Tooltip did in tap-target 0.2.0 (census rows 19i / 20a).
|
|
27
|
+
|
|
28
|
+
**No shim.** There is no `./confirm` re-export. One surface, one owner — and the
|
|
29
|
+
packed-tarball canary now asserts the ABSENCE of `./confirm` and of both dropped
|
|
30
|
+
dependencies, so the twin cannot grow back.
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
|
|
34
|
+
- `@ai-matrx/kit/confirm` (subpath, entry, and every export: `ConfirmDialog`,
|
|
35
|
+
`ConfirmDialogProps`, `ConfirmDialogHost`, and the `confirm` re-export).
|
|
36
|
+
- `confirm` / `ConfirmOptions` from the ROOT barrel (`@ai-matrx/kit`) — the
|
|
37
|
+
opener is not re-exported there, because the root barrel is `"use client"`
|
|
38
|
+
stamped and the opener must stay importable from Server Components, API routes
|
|
39
|
+
and Node scripts.
|
|
40
|
+
- Runtime dependencies `@radix-ui/react-alert-dialog` and `tailwind-merge`. Both
|
|
41
|
+
existed only for the dialog; kit's install is that much smaller.
|
|
42
|
+
|
|
43
|
+
### Unchanged
|
|
44
|
+
|
|
45
|
+
- `@ai-matrx/kit/confirm-opener` — `confirm`, `ConfirmOptions`, and the
|
|
46
|
+
`Symbol.for("ai-matrx.kit.confirm-opener-state")` globalThis slot the host
|
|
47
|
+
registry lives on. Every behavior is identical: pre-mount calls queue,
|
|
48
|
+
concurrent calls present one at a time, and with no host mounted a `confirm()`
|
|
49
|
+
promise stays pending forever rather than inventing a silent default.
|
|
50
|
+
|
|
51
|
+
### Consumer action (C28)
|
|
52
|
+
|
|
53
|
+
**Every consumer that mounted the kit confirm host or rendered the kit dialog
|
|
54
|
+
must repoint. Two mechanical edits, no behavior change.**
|
|
55
|
+
|
|
56
|
+
1. **Take `@ai-matrx/design-system` >= 0.11.0** alongside kit >= 0.9.0 (specs stay
|
|
57
|
+
`"latest"` / `workspace:*` per C15 — this is an install, not a spec change).
|
|
58
|
+
|
|
59
|
+
2. **The host** — wherever you mounted it:
|
|
60
|
+
|
|
61
|
+
```diff
|
|
62
|
+
- import { ConfirmDialogHost } from "@ai-matrx/kit/confirm";
|
|
63
|
+
+ import { ConfirmDialogHost } from "@ai-matrx/design-system";
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Next.js hosts that lazy-loaded it for the fragmentation law change only the
|
|
67
|
+
module specifier:
|
|
68
|
+
|
|
69
|
+
```diff
|
|
70
|
+
- dynamic(() => import("@ai-matrx/kit/confirm").then((m) => m.ConfirmDialogHost), { ssr: false })
|
|
71
|
+
+ dynamic(() => import("@ai-matrx/design-system").then((m) => m.ConfirmDialogHost), { ssr: false })
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
3. **The declarative dialog:**
|
|
75
|
+
|
|
76
|
+
```diff
|
|
77
|
+
- import { ConfirmDialog, type ConfirmDialogProps } from "@ai-matrx/kit/confirm";
|
|
78
|
+
+ import { ConfirmDialog, type ConfirmDialogProps } from "@ai-matrx/design-system";
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Props are identical. `portalContainer` still wins; with it omitted the dialog
|
|
82
|
+
now also honours design-system's `PortalContainerProvider`, so a host that
|
|
83
|
+
already provides that container can drop its own wrapper.
|
|
84
|
+
|
|
85
|
+
4. **The imperative `confirm()`** — if you imported it from `@ai-matrx/kit/confirm`
|
|
86
|
+
or from the kit ROOT, repoint to the opener (this is the light-twin entry every
|
|
87
|
+
call site should have been using anyway):
|
|
88
|
+
|
|
89
|
+
```diff
|
|
90
|
+
- import { confirm } from "@ai-matrx/kit/confirm";
|
|
91
|
+
+ import { confirm } from "@ai-matrx/kit/confirm-opener";
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
5. **Load design-system's stylesheet** if you do not already — `@ai-matrx/design-system/styles.css`
|
|
95
|
+
plus `tokens.css`. This is what the move buys you: the confirm surface finally
|
|
96
|
+
animates and scrims from your theme in EVERY consumer, not only in the one whose
|
|
97
|
+
CSS entry happened to load an animation plugin.
|
|
98
|
+
|
|
3
99
|
## 0.8.0 — 2026-09-07
|
|
4
100
|
|
|
5
101
|
**THE HOME FOR THE FLEET'S SHARED FORMATTERS.** Three new pure subpaths —
|
package/README.md
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
The always-include AI Matrx kit: the little primitives every Matrx application speaks,
|
|
4
4
|
one capability per subpath, tree-shaken to what you use. React is the only required
|
|
5
|
-
peer; only
|
|
6
|
-
the capability: `/
|
|
7
|
-
product is the dialog), `/json-format` (`json5` — tolerant parsing is the feature),
|
|
5
|
+
peer; only three subpaths carry runtime dependencies, each because the dependency IS
|
|
6
|
+
the capability: `/json-format` (`json5` — tolerant parsing is the feature),
|
|
8
7
|
`/idb-store` (`idb` — the typed IndexedDB wrapper is the engine), and `/qr` (`jsqr`,
|
|
9
8
|
loaded lazily only when the browser has no native `BarcodeDetector`).
|
|
10
9
|
|
|
10
|
+
**The confirm DIALOG is not here — the opener is.** Since 0.9.0 the body
|
|
11
|
+
(`ConfirmDialog`, `ConfirmDialogHost`) lives in
|
|
12
|
+
[`@ai-matrx/design-system`](../design-system/README.md), which owns the AlertDialog,
|
|
13
|
+
the shipped motion layer and the overlay scrim token. Mount
|
|
14
|
+
`<ConfirmDialogHost />` from there once at your app root, then call `confirm()`
|
|
15
|
+
from `@ai-matrx/kit/confirm-opener` anywhere. One surface, one owner: kit
|
|
16
|
+
re-exports none of the dialog.
|
|
17
|
+
|
|
11
18
|
```bash
|
|
12
19
|
npm install @ai-matrx/kit
|
|
13
20
|
```
|
|
@@ -22,7 +29,7 @@ npm install @ai-matrx/kit
|
|
|
22
29
|
| `@ai-matrx/kit/concurrency` | `runWithConcurrency` | Bounded worker pool over an array with per-item failure isolation and a cooperative `shouldStart()` cancel hook. |
|
|
23
30
|
| `@ai-matrx/kit/text-case` | `formatText`, `createFormatter` | Acronym-aware normalization of messy DB/API strings into title/sentence case with word-replacement overrides. |
|
|
24
31
|
| `@ai-matrx/kit/drafts` | `useDurableDraft` + the local-drafts store | User-authored text that survives anything: write-through localStorage drafts, restore on mount, key-change adoption, cleared only after content durably landed. |
|
|
25
|
-
| `@ai-matrx/kit/confirm` | `confirm`, `
|
|
32
|
+
| `@ai-matrx/kit/confirm-opener` | `confirm`, `ConfirmOptions` | The `window.confirm` replacement's imperative half — pure TS, zero React, zero radix, no `"use client"`, so a Redux thunk, a util or a sync handler can import it at near-zero bundle cost. `await confirm({ title, description, variant: "destructive" })` resolves `true`/`false`; pre-mount calls queue and concurrent calls present one at a time; with no host mounted it stays pending forever rather than inventing a silent default. Needs `<ConfirmDialogHost />` from `@ai-matrx/design-system` mounted once — the two share one registry through a `globalThis` slot. |
|
|
26
33
|
| `@ai-matrx/kit/toast` | `createMatrxToast` | The captured sonner wrapper as a factory: pass your sonner `toast` in, get a drop-in `toast` back whose `.error`/`.warning` also feed your injected error-capture sink, plus `toastErrorAlreadyCaptured` for notices whose failure was already captured upstream. No sonner import — and sonner is not declared as a dependency or peer at all. |
|
|
27
34
|
| `@ai-matrx/kit/invalidation` | `registerInvalidationCallback`, `fireInvalidation` | Zero-import, name-keyed callback registry that breaks import edges into heavy chunk clusters: the heavy cluster registers at its own module init, the ubiquitous module fires by name. Unregistered names are a deliberate no-op; a throwing callback screams and never breaks the caller. Keep your key constants in ONE host module. |
|
|
28
35
|
| `@ai-matrx/kit/delimiter-guard` | `guardMarkdownDelimiters` (+ `guardMathDelimiters`, `guardRunawayLinks`, `reportDelimiterViolations`) | Keeps one stray streamed delimiter from swallowing a whole section: a `$$` that would turn prose into a red KaTeX error span, or an unclosed `[` that would turn a section into one giant link, gets invisibly neutralized while real math and links keep rendering. Pure; report violations loudly via the optional injected capture sink. |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/confirm-opener.ts","../src/confirm/opener.ts"],"sourcesContent":["/**\n * @ai-matrx/kit/confirm-opener — the imperative `confirm()` API alone.\n *\n *
|
|
1
|
+
{"version":3,"sources":["../src/confirm-opener.ts","../src/confirm/opener.ts"],"sourcesContent":["/**\n * @ai-matrx/kit/confirm-opener — the imperative `confirm()` API alone.\n *\n * THE WHOLE CONFIRM SYSTEM THIS PACKAGE SHIPS (since 0.9.0, census row 19i).\n * The dialog BODY — `ConfirmDialog` and `ConfirmDialogHost` — lives in\n * `@ai-matrx/design-system`, which owns the AlertDialog, the shipped motion\n * layer and the `--matrx-overlay-scrim` token this surface needs and kit could\n * neither ship nor import (design-system depends on kit; importing back is a\n * cycle). kit re-exports none of it: one surface, one owner.\n *\n * What is here is the pure half: zero React runtime, zero radix, no\n * \"use client\" banner — statically importable from anywhere (hooks, utils,\n * Redux thunks, sync code) at near-zero bundle cost, which is what lets a host\n * keep hundreds of imperative call sites out of the dialog's chunk.\n *\n * Wiring, end to end:\n * 1. mount `<ConfirmDialogHost />` from `@ai-matrx/design-system` once, near\n * the root of every provider tree;\n * 2. `await confirm({ ... })` from anywhere, importing from HERE.\n * The two find each other through the\n * `Symbol.for(\"ai-matrx.kit.confirm-opener-state\")` globalThis slot, so they\n * observe one registration across every module graph and both packages.\n */\nexport {\n confirm,\n _registerHost,\n _unregisterHost,\n _resetConfirmOpenerState,\n type ConfirmOptions,\n} from \"./confirm/opener\";\n","/**\n * @ai-matrx/kit/confirm-opener — the imperative opener, and since 0.9.0 the\n * ONLY half of the confirm system this package ships.\n *\n * Pure-TS imperative API for the global confirm dialog. Zero React, zero\n * dialog markup — this module is statically importable from anywhere\n * (hooks, utils, Redux thunks, async handlers, sync code, anything).\n *\n * The host (`ConfirmDialogHost`, in `@ai-matrx/design-system`) registers a\n * controller on mount and\n * unregisters on unmount. Calls made before the host has hydrated queue\n * up and resolve as soon as the host is alive — so a destructive action\n * triggered in the first ~50ms after page load still gets a real\n * confirmation, never a silent default-yes/no. With no host ever mounted,\n * a `confirm()` promise stays pending forever (the original's behavior —\n * it never resolves to a silent default).\n *\n * One dialog at a time: concurrent calls queue and present sequentially.\n *\n * Ported verbatim from matrx-frontend\n * `components/dialogs/confirm/confirmDialogOpener.ts`, with ONE structural\n * inversion: the host/queue state lives on `globalThis` under a\n * `Symbol.for` slot instead of module-level variables. With the package\n * built `splitting: false` in dual ESM/CJS format, and the host now living in\n * a DIFFERENT PACKAGE, CJS/ESM each instantiate their own module graph — a module-level variable would\n * silently split the host registration from the callers (the same hazard\n * `@ai-matrx/tap-target` documents for its link registry). Behavior is\n * unchanged; never \"clean this up\" into a module local.\n */\n\nimport type { ReactNode } from \"react\";\n\nexport interface ConfirmOptions {\n title: ReactNode;\n description?: ReactNode | undefined;\n confirmLabel?: string | undefined;\n /** `null` hides the cancel button (acknowledge-only dialogs). */\n cancelLabel?: string | null | undefined;\n variant?: \"default\" | \"destructive\" | undefined;\n}\n\ntype Resolver = (confirmed: boolean) => void;\n\ninterface PendingRequest {\n opts: ConfirmOptions;\n resolve: Resolver;\n}\n\ninterface HostController {\n show: (opts: ConfirmOptions, resolve: Resolver) => void;\n}\n\ninterface OpenerState {\n host: HostController | null;\n queue: PendingRequest[];\n}\n\nconst STATE_SLOT = Symbol.for(\"ai-matrx.kit.confirm-opener-state\");\n\nfunction getState(): OpenerState {\n const holder = globalThis as Record<symbol, OpenerState | undefined>;\n let state = holder[STATE_SLOT];\n if (!state) {\n state = { host: null, queue: [] };\n holder[STATE_SLOT] = state;\n }\n return state;\n}\n\n/** @internal Called by `ConfirmDialogHost` on mount. */\nexport function _registerHost(controller: HostController): void {\n const state = getState();\n state.host = controller;\n while (state.queue.length > 0) {\n const next = state.queue.shift()!;\n controller.show(next.opts, next.resolve);\n }\n}\n\n/** @internal Called by `ConfirmDialogHost` on unmount. */\nexport function _unregisterHost(controller: HostController): void {\n const state = getState();\n if (state.host === controller) state.host = null;\n}\n\n/** @internal Test-only: drop any registered host and pending queue. */\nexport function _resetConfirmOpenerState(): void {\n const state = getState();\n state.host = null;\n state.queue.length = 0;\n}\n\n/**\n * Imperative confirm. Returns a Promise that resolves `true` if the user\n * confirms, `false` if they cancel/dismiss. Replaces `window.confirm`.\n *\n * @example\n * const ok = await confirm({\n * title: \"Delete sandbox\",\n * description: \"This cannot be undone.\",\n * variant: \"destructive\",\n * confirmLabel: \"Delete\",\n * });\n * if (!ok) return;\n */\nexport function confirm(opts: ConfirmOptions): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const state = getState();\n if (state.host) {\n state.host.show(opts, resolve);\n } else {\n state.queue.push({ opts, resolve });\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACyDA,IAAM,aAAa,uBAAO,IAAI,mCAAmC;AAEjE,SAAS,WAAwB;AAC/B,QAAM,SAAS;AACf,MAAI,QAAQ,OAAO,UAAU;AAC7B,MAAI,CAAC,OAAO;AACV,YAAQ,EAAE,MAAM,MAAM,OAAO,CAAC,EAAE;AAChC,WAAO,UAAU,IAAI;AAAA,EACvB;AACA,SAAO;AACT;AAGO,SAAS,cAAc,YAAkC;AAC9D,QAAM,QAAQ,SAAS;AACvB,QAAM,OAAO;AACb,SAAO,MAAM,MAAM,SAAS,GAAG;AAC7B,UAAM,OAAO,MAAM,MAAM,MAAM;AAC/B,eAAW,KAAK,KAAK,MAAM,KAAK,OAAO;AAAA,EACzC;AACF;AAGO,SAAS,gBAAgB,YAAkC;AAChE,QAAM,QAAQ,SAAS;AACvB,MAAI,MAAM,SAAS,WAAY,OAAM,OAAO;AAC9C;AAGO,SAAS,2BAAiC;AAC/C,QAAM,QAAQ,SAAS;AACvB,QAAM,OAAO;AACb,QAAM,MAAM,SAAS;AACvB;AAeO,SAAS,QAAQ,MAAwC;AAC9D,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,MAAM;AACd,YAAM,KAAK,KAAK,MAAM,OAAO;AAAA,IAC/B,OAAO;AACL,YAAM,MAAM,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,IACpC;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @ai-matrx/kit/confirm — imperative opener.
|
|
4
|
+
* @ai-matrx/kit/confirm-opener — the imperative opener, and since 0.9.0 the
|
|
5
|
+
* ONLY half of the confirm system this package ships.
|
|
5
6
|
*
|
|
6
7
|
* Pure-TS imperative API for the global confirm dialog. Zero React, zero
|
|
7
8
|
* dialog markup — this module is statically importable from anywhere
|
|
8
9
|
* (hooks, utils, Redux thunks, async handlers, sync code, anything).
|
|
9
10
|
*
|
|
10
|
-
* The host (`ConfirmDialogHost`) registers a
|
|
11
|
+
* The host (`ConfirmDialogHost`, in `@ai-matrx/design-system`) registers a
|
|
12
|
+
* controller on mount and
|
|
11
13
|
* unregisters on unmount. Calls made before the host has hydrated queue
|
|
12
14
|
* up and resolve as soon as the host is alive — so a destructive action
|
|
13
15
|
* triggered in the first ~50ms after page load still gets a real
|
|
@@ -21,9 +23,8 @@ import { ReactNode } from 'react';
|
|
|
21
23
|
* `components/dialogs/confirm/confirmDialogOpener.ts`, with ONE structural
|
|
22
24
|
* inversion: the host/queue state lives on `globalThis` under a
|
|
23
25
|
* `Symbol.for` slot instead of module-level variables. With the package
|
|
24
|
-
* built `splitting: false` in dual ESM/CJS format,
|
|
25
|
-
*
|
|
26
|
-
* each instantiate their own module graph — a module-level variable would
|
|
26
|
+
* built `splitting: false` in dual ESM/CJS format, and the host now living in
|
|
27
|
+
* a DIFFERENT PACKAGE, CJS/ESM each instantiate their own module graph — a module-level variable would
|
|
27
28
|
* silently split the host registration from the callers (the same hazard
|
|
28
29
|
* `@ai-matrx/tap-target` documents for its link registry). Behavior is
|
|
29
30
|
* unchanged; never "clean this up" into a module local.
|
package/dist/confirm-opener.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @ai-matrx/kit/confirm — imperative opener.
|
|
4
|
+
* @ai-matrx/kit/confirm-opener — the imperative opener, and since 0.9.0 the
|
|
5
|
+
* ONLY half of the confirm system this package ships.
|
|
5
6
|
*
|
|
6
7
|
* Pure-TS imperative API for the global confirm dialog. Zero React, zero
|
|
7
8
|
* dialog markup — this module is statically importable from anywhere
|
|
8
9
|
* (hooks, utils, Redux thunks, async handlers, sync code, anything).
|
|
9
10
|
*
|
|
10
|
-
* The host (`ConfirmDialogHost`) registers a
|
|
11
|
+
* The host (`ConfirmDialogHost`, in `@ai-matrx/design-system`) registers a
|
|
12
|
+
* controller on mount and
|
|
11
13
|
* unregisters on unmount. Calls made before the host has hydrated queue
|
|
12
14
|
* up and resolve as soon as the host is alive — so a destructive action
|
|
13
15
|
* triggered in the first ~50ms after page load still gets a real
|
|
@@ -21,9 +23,8 @@ import { ReactNode } from 'react';
|
|
|
21
23
|
* `components/dialogs/confirm/confirmDialogOpener.ts`, with ONE structural
|
|
22
24
|
* inversion: the host/queue state lives on `globalThis` under a
|
|
23
25
|
* `Symbol.for` slot instead of module-level variables. With the package
|
|
24
|
-
* built `splitting: false` in dual ESM/CJS format,
|
|
25
|
-
*
|
|
26
|
-
* each instantiate their own module graph — a module-level variable would
|
|
26
|
+
* built `splitting: false` in dual ESM/CJS format, and the host now living in
|
|
27
|
+
* a DIFFERENT PACKAGE, CJS/ESM each instantiate their own module graph — a module-level variable would
|
|
27
28
|
* silently split the host registration from the callers (the same hazard
|
|
28
29
|
* `@ai-matrx/tap-target` documents for its link registry). Behavior is
|
|
29
30
|
* unchanged; never "clean this up" into a module local.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/confirm/opener.ts"],"sourcesContent":["/**\n * @ai-matrx/kit/confirm — imperative opener.\n *\n * Pure-TS imperative API for the global confirm dialog. Zero React, zero\n * dialog markup — this module is statically importable from anywhere\n * (hooks, utils, Redux thunks, async handlers, sync code, anything).\n *\n * The host (`ConfirmDialogHost`) registers a controller on mount and\n * unregisters on unmount. Calls made before the host has hydrated queue\n * up and resolve as soon as the host is alive — so a destructive action\n * triggered in the first ~50ms after page load still gets a real\n * confirmation, never a silent default-yes/no. With no host ever mounted,\n * a `confirm()` promise stays pending forever (the original's behavior —\n * it never resolves to a silent default).\n *\n * One dialog at a time: concurrent calls queue and present sequentially.\n *\n * Ported verbatim from matrx-frontend\n * `components/dialogs/confirm/confirmDialogOpener.ts`, with ONE structural\n * inversion: the host/queue state lives on `globalThis` under a\n * `Symbol.for` slot instead of module-level variables. With the package\n * built `splitting: false` in dual ESM/CJS format,
|
|
1
|
+
{"version":3,"sources":["../src/confirm/opener.ts"],"sourcesContent":["/**\n * @ai-matrx/kit/confirm-opener — the imperative opener, and since 0.9.0 the\n * ONLY half of the confirm system this package ships.\n *\n * Pure-TS imperative API for the global confirm dialog. Zero React, zero\n * dialog markup — this module is statically importable from anywhere\n * (hooks, utils, Redux thunks, async handlers, sync code, anything).\n *\n * The host (`ConfirmDialogHost`, in `@ai-matrx/design-system`) registers a\n * controller on mount and\n * unregisters on unmount. Calls made before the host has hydrated queue\n * up and resolve as soon as the host is alive — so a destructive action\n * triggered in the first ~50ms after page load still gets a real\n * confirmation, never a silent default-yes/no. With no host ever mounted,\n * a `confirm()` promise stays pending forever (the original's behavior —\n * it never resolves to a silent default).\n *\n * One dialog at a time: concurrent calls queue and present sequentially.\n *\n * Ported verbatim from matrx-frontend\n * `components/dialogs/confirm/confirmDialogOpener.ts`, with ONE structural\n * inversion: the host/queue state lives on `globalThis` under a\n * `Symbol.for` slot instead of module-level variables. With the package\n * built `splitting: false` in dual ESM/CJS format, and the host now living in\n * a DIFFERENT PACKAGE, CJS/ESM each instantiate their own module graph — a module-level variable would\n * silently split the host registration from the callers (the same hazard\n * `@ai-matrx/tap-target` documents for its link registry). Behavior is\n * unchanged; never \"clean this up\" into a module local.\n */\n\nimport type { ReactNode } from \"react\";\n\nexport interface ConfirmOptions {\n title: ReactNode;\n description?: ReactNode | undefined;\n confirmLabel?: string | undefined;\n /** `null` hides the cancel button (acknowledge-only dialogs). */\n cancelLabel?: string | null | undefined;\n variant?: \"default\" | \"destructive\" | undefined;\n}\n\ntype Resolver = (confirmed: boolean) => void;\n\ninterface PendingRequest {\n opts: ConfirmOptions;\n resolve: Resolver;\n}\n\ninterface HostController {\n show: (opts: ConfirmOptions, resolve: Resolver) => void;\n}\n\ninterface OpenerState {\n host: HostController | null;\n queue: PendingRequest[];\n}\n\nconst STATE_SLOT = Symbol.for(\"ai-matrx.kit.confirm-opener-state\");\n\nfunction getState(): OpenerState {\n const holder = globalThis as Record<symbol, OpenerState | undefined>;\n let state = holder[STATE_SLOT];\n if (!state) {\n state = { host: null, queue: [] };\n holder[STATE_SLOT] = state;\n }\n return state;\n}\n\n/** @internal Called by `ConfirmDialogHost` on mount. */\nexport function _registerHost(controller: HostController): void {\n const state = getState();\n state.host = controller;\n while (state.queue.length > 0) {\n const next = state.queue.shift()!;\n controller.show(next.opts, next.resolve);\n }\n}\n\n/** @internal Called by `ConfirmDialogHost` on unmount. */\nexport function _unregisterHost(controller: HostController): void {\n const state = getState();\n if (state.host === controller) state.host = null;\n}\n\n/** @internal Test-only: drop any registered host and pending queue. */\nexport function _resetConfirmOpenerState(): void {\n const state = getState();\n state.host = null;\n state.queue.length = 0;\n}\n\n/**\n * Imperative confirm. Returns a Promise that resolves `true` if the user\n * confirms, `false` if they cancel/dismiss. Replaces `window.confirm`.\n *\n * @example\n * const ok = await confirm({\n * title: \"Delete sandbox\",\n * description: \"This cannot be undone.\",\n * variant: \"destructive\",\n * confirmLabel: \"Delete\",\n * });\n * if (!ok) return;\n */\nexport function confirm(opts: ConfirmOptions): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const state = getState();\n if (state.host) {\n state.host.show(opts, resolve);\n } else {\n state.queue.push({ opts, resolve });\n }\n });\n}\n"],"mappings":";AAyDA,IAAM,aAAa,uBAAO,IAAI,mCAAmC;AAEjE,SAAS,WAAwB;AAC/B,QAAM,SAAS;AACf,MAAI,QAAQ,OAAO,UAAU;AAC7B,MAAI,CAAC,OAAO;AACV,YAAQ,EAAE,MAAM,MAAM,OAAO,CAAC,EAAE;AAChC,WAAO,UAAU,IAAI;AAAA,EACvB;AACA,SAAO;AACT;AAGO,SAAS,cAAc,YAAkC;AAC9D,QAAM,QAAQ,SAAS;AACvB,QAAM,OAAO;AACb,SAAO,MAAM,MAAM,SAAS,GAAG;AAC7B,UAAM,OAAO,MAAM,MAAM,MAAM;AAC/B,eAAW,KAAK,KAAK,MAAM,KAAK,OAAO;AAAA,EACzC;AACF;AAGO,SAAS,gBAAgB,YAAkC;AAChE,QAAM,QAAQ,SAAS;AACvB,MAAI,MAAM,SAAS,WAAY,OAAM,OAAO;AAC9C;AAGO,SAAS,2BAAiC;AAC/C,QAAM,QAAQ,SAAS;AACvB,QAAM,OAAO;AACb,QAAM,MAAM,SAAS;AACvB;AAeO,SAAS,QAAQ,MAAwC;AAC9D,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,MAAM;AACd,YAAM,KAAK,KAAK,MAAM,OAAO;AAAA,IAC/B,OAAO;AACL,YAAM,MAAM,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,IACpC;AAAA,EACF,CAAC;AACH;","names":[]}
|