@chuvenger/devdock 0.6.0 → 0.7.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/README.md +160 -10
- package/dist/{chunk-3MVFATRN.js → chunk-2IQRWZ7T.js} +3 -3
- package/dist/{chunk-3MVFATRN.js.map → chunk-2IQRWZ7T.js.map} +1 -1
- package/dist/{chunk-NXZNFF34.js → chunk-2YX4DI2D.js} +253 -145
- package/dist/chunk-2YX4DI2D.js.map +1 -0
- package/dist/{dock-d3HqT25k.d.cts → dock-C7wZ8pQb.d.cts} +1 -1
- package/dist/{dock-P6l6pKjS.d.ts → dock-DJZuGmih.d.ts} +1 -1
- package/dist/index.cjs +253 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/{match-DwGIF1Xt.d.ts → match-C8MsaP6h.d.ts} +1 -1
- package/dist/{match-DDSJIRYW.d.cts → match-Dw1CSS0G.d.cts} +1 -1
- package/dist/react-router.cjs +250 -143
- package/dist/react-router.cjs.map +1 -1
- package/dist/react-router.d.cts +3 -2
- package/dist/react-router.d.ts +3 -2
- package/dist/react-router.js +2 -2
- package/dist/react.cjs +250 -143
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +3 -2
- package/dist/react.d.ts +3 -2
- package/dist/react.js +2 -2
- package/dist/svelte.cjs +253 -143
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.cts +4 -3
- package/dist/svelte.d.ts +4 -3
- package/dist/svelte.js +2 -2
- package/dist/svelte.js.map +1 -1
- package/dist/sveltekit.cjs +253 -143
- package/dist/sveltekit.cjs.map +1 -1
- package/dist/sveltekit.d.cts +5 -4
- package/dist/sveltekit.d.ts +5 -4
- package/dist/sveltekit.js +2 -2
- package/dist/{types-B_SpF32A.d.cts → types-B8WgSe7o.d.cts} +103 -3
- package/dist/{types-B_SpF32A.d.ts → types-B8WgSe7o.d.ts} +103 -3
- package/package.json +4 -1
- package/dist/chunk-NXZNFF34.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@chuvenger/devdock)
|
|
4
4
|
[](./LICENSE)
|
|
5
5
|
|
|
6
|
-
A tiny
|
|
6
|
+
A tiny dev-only floating button. It sits in a corner of your
|
|
7
7
|
app, **only renders in development**, and opens a popup to jump between dev
|
|
8
8
|
routes and run custom commands.
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ routes and run custom commands.
|
|
|
12
12
|
- ⚡ **Custom commands** — register any action (reset DB, toggle a flag, copy a token…), with live labels and check marks for toggles.
|
|
13
13
|
- 📊 **Custom views** — mount your own UI in the popup for anything a list can't say: a frame-rate meter, a sync status, a segmented control.
|
|
14
14
|
- 🔎 Filter box + keyboard nav (↑/↓/Enter/Esc) and an optional global hotkey.
|
|
15
|
-
- 🎨 Zero CSS to import —
|
|
15
|
+
- 🎨 Zero CSS to import — it brings its own sheet and a high z-index. Theme it with `--devdock-*` custom properties (they cross the shadow boundary, so isolation stays), or merge your own classes into any part with `classNames`.
|
|
16
16
|
- 🛡️ **Shadow DOM isolated** by default — the host page's CSS can't touch it (and vice versa), or `shadow: "inherit"` to let your design system in.
|
|
17
17
|
- 🧩 **Framework-agnostic core** with thin **React** and **Svelte** adapters.
|
|
18
18
|
|
|
@@ -440,11 +440,11 @@ tree-shakes it: `{import.meta.env.DEV && <DevDock … />}`.
|
|
|
440
440
|
|
|
441
441
|
## Styling & isolation
|
|
442
442
|
|
|
443
|
-
The dock
|
|
444
|
-
by default, renders inside an **open shadow root** attached to a
|
|
445
|
-
`document.body`.
|
|
446
|
-
styles can't leak out. The host element carries a `data-devdock`
|
|
447
|
-
every mode.
|
|
443
|
+
The dock ships its own stylesheet — one it injects itself, nothing to
|
|
444
|
+
import, themeable with [custom properties](#theming-it-custom-properties) — and, by default, renders inside an **open shadow root** attached to a
|
|
445
|
+
host element in `document.body`. The host page's CSS can't leak in and the
|
|
446
|
+
dock's styles can't leak out. The host element carries a `data-devdock`
|
|
447
|
+
attribute in every mode.
|
|
448
448
|
|
|
449
449
|
| `shadow` | Where it renders | The page's CSS |
|
|
450
450
|
| -------- | ---------------- | -------------- |
|
|
@@ -453,8 +453,152 @@ every mode.
|
|
|
453
453
|
| `false` | light DOM | applies directly |
|
|
454
454
|
|
|
455
455
|
Use `"inherit"` (or `false`) when a [custom view](#views-and-your-css) renders
|
|
456
|
-
your app's own components and needs its stylesheet
|
|
457
|
-
|
|
456
|
+
your app's own components and needs its stylesheet, or when you're restyling
|
|
457
|
+
the dock with `classNames` below — passing `classNames` switches the default to
|
|
458
|
+
`"inherit"` for you, since a class of yours is defined by a stylesheet that has
|
|
459
|
+
to cross the boundary. Otherwise leave it alone: isolation is the reason the
|
|
460
|
+
dock survives arbitrary host CSS.
|
|
461
|
+
|
|
462
|
+
There are two ways to change how it looks, and they compose:
|
|
463
|
+
|
|
464
|
+
| | Reach | Costs you |
|
|
465
|
+
| --- | --- | --- |
|
|
466
|
+
| [Custom properties](#theming-it-custom-properties) | colours, radii, font, size | nothing — they inherit **through** the shadow boundary, so the dock stays isolated |
|
|
467
|
+
| [`classNames`](#restyling-it-classnames) | anything CSS can say | your stylesheet has to reach the dock, so `shadow` becomes `"inherit"` |
|
|
468
|
+
|
|
469
|
+
Reach for the properties first; drop to `classNames` when you need something
|
|
470
|
+
they don't cover.
|
|
471
|
+
|
|
472
|
+
### Theming it: custom properties
|
|
473
|
+
|
|
474
|
+
Set any of these on `:root`, or on the `[data-devdock]` host to theme one dock,
|
|
475
|
+
and you're done — no `shadow` change, no classes, nothing to import:
|
|
476
|
+
|
|
477
|
+
```css
|
|
478
|
+
:root {
|
|
479
|
+
--devdock-bg: #ffffff;
|
|
480
|
+
--devdock-fg: #0f172a;
|
|
481
|
+
--devdock-accent: #a855f7;
|
|
482
|
+
--devdock-border: #e2e8f0;
|
|
483
|
+
--devdock-radius: 16px;
|
|
484
|
+
--devdock-width: 320px;
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
| Property | Default | What it paints |
|
|
489
|
+
| -------- | ------- | -------------- |
|
|
490
|
+
| `--devdock-font` | system UI stack | The dock's font family. |
|
|
491
|
+
| `--devdock-bg` | `#16181d` | Popup background. |
|
|
492
|
+
| `--devdock-fg` | `#e7e9ee` | Primary text — rows, the input. |
|
|
493
|
+
| `--devdock-muted` | `#8b909c` | The title, a view's label. |
|
|
494
|
+
| `--devdock-subtle` | `#6b7280` | Group headings, route paths, the empty state. |
|
|
495
|
+
| `--devdock-border` | `#2a2e37` | Every border. |
|
|
496
|
+
| `--devdock-accent` | `#2563eb` | Highlighted row, and the button while open. |
|
|
497
|
+
| `--devdock-accent-fg` | `#e7e9ee` | Text on the highlighted row. |
|
|
498
|
+
| `--devdock-button-bg` | `#111317` | The button, closed. |
|
|
499
|
+
| `--devdock-button-fg` | `#fff` | The button's text. |
|
|
500
|
+
| `--devdock-input-bg` | `#0f1115` | The filter input. |
|
|
501
|
+
| `--devdock-success` | `#22c55e` | The status dot and toggle check marks. |
|
|
502
|
+
| `--devdock-radius` | `12px` | Popup corners. |
|
|
503
|
+
| `--devdock-radius-sm` | `8px` | Rows and the input. |
|
|
504
|
+
| `--devdock-width` | `280px` | Popup width. |
|
|
505
|
+
| `--devdock-max-height` | `70vh` | Popup height before it scrolls. |
|
|
506
|
+
| `--devdock-shadow` | `0 12px 40px rgba(0,0,0,.45)` | The popup's shadow. |
|
|
507
|
+
|
|
508
|
+
They're read as `var(--devdock-x, default)` and never declared inside the
|
|
509
|
+
dock, which is what lets a value you set outside win — a declaration of ours
|
|
510
|
+
in the shadow tree would have beaten the one inheriting in.
|
|
511
|
+
|
|
512
|
+
### Restyling it: `classNames`
|
|
513
|
+
|
|
514
|
+
Every part of the dock takes a class of yours, **merged onto** the one it
|
|
515
|
+
already carries rather than replacing it:
|
|
516
|
+
|
|
517
|
+
```tsx
|
|
518
|
+
<DevDock
|
|
519
|
+
classNames={{
|
|
520
|
+
panel: "w-96 max-h-[80vh] bg-white text-slate-900 border-slate-200",
|
|
521
|
+
button: "bg-emerald-600 rounded-md",
|
|
522
|
+
input: "bg-slate-50 border-slate-300",
|
|
523
|
+
row: "rounded-none",
|
|
524
|
+
rowActive: "bg-emerald-100 text-emerald-900",
|
|
525
|
+
}}
|
|
526
|
+
/>
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Each entry takes **anything [`cn`](https://www.npmjs.com/package/cn) takes** —
|
|
530
|
+
a string, an array, a conditional, an object — so a wrapper of your own can
|
|
531
|
+
forward its props straight through without a helper import:
|
|
532
|
+
|
|
533
|
+
```tsx
|
|
534
|
+
export function AppDock({ className }: { className?: string }) {
|
|
535
|
+
return (
|
|
536
|
+
<DevDock
|
|
537
|
+
classNames={{
|
|
538
|
+
panel: ["bg-red-100", className],
|
|
539
|
+
row: ["px-3", compact && "py-1", { "opacity-50": muted }],
|
|
540
|
+
}}
|
|
541
|
+
/>
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Values are merged with [`cn`](https://www.npmjs.com/package/cn), so conflicts
|
|
547
|
+
resolve the way you'd write them by hand: `rowActive: "text-emerald-900"` over
|
|
548
|
+
`row: "text-slate-700"` keeps emerald and **drops** slate, instead of putting
|
|
549
|
+
both in the attribute and letting Tailwind's own sheet order pick the winner.
|
|
550
|
+
The dock's `devdock-*` classes aren't utilities, so they pass through untouched.
|
|
551
|
+
|
|
552
|
+
No `!important`, no `[&_div]` reaching: a plain utility class wins on its own.
|
|
553
|
+
Two things in the dock's sheet make that true, and they're worth knowing about
|
|
554
|
+
because they also say when it *doesn't*:
|
|
555
|
+
|
|
556
|
+
- **It lives in a cascade layer** (`devdock`), declared *after* Tailwind's
|
|
557
|
+
`base` and *before* its `utilities`. Layered rules lose to unlayered ones
|
|
558
|
+
whatever their specificity, so this is what lets a Tailwind v4 utility
|
|
559
|
+
through while keeping a reset in `base` from stripping the dock's chrome.
|
|
560
|
+
- **Each rule is worth exactly one class** — one bare `.devdock-*`, with any
|
|
561
|
+
state selector tucked inside `:where()`. That's what settles ties when the
|
|
562
|
+
layer is off: the sheet goes in first, so a page-level reset
|
|
563
|
+
(`button { … }`, `*`) loses to it and your class beats it.
|
|
564
|
+
|
|
565
|
+
If the page's own CSS is unlayered — Tailwind v3, a hand-written reset —
|
|
566
|
+
turn the layer off with `cssLayer={false}`, or the page's reset will outrank
|
|
567
|
+
the dock itself. (A page with no `@layer` anywhere is unaffected either way.)
|
|
568
|
+
If you run your own layers, name the dock's with `cssLayer="chrome"`: it then
|
|
569
|
+
ships no order statement at all, and you place it in your own `@layer` line,
|
|
570
|
+
where you know the names around it.
|
|
571
|
+
|
|
572
|
+
Not themeable this way: the corner placement, the z-index, and the popup's
|
|
573
|
+
anchoring. Those are computed from `position`/`zIndex` and stay inline.
|
|
574
|
+
|
|
575
|
+
### The parts
|
|
576
|
+
|
|
577
|
+
Each one carries its `devdock-*` class in every mode, so you can also just
|
|
578
|
+
write CSS against them and skip `classNames` entirely.
|
|
579
|
+
|
|
580
|
+
| `classNames` key | Class | What it is |
|
|
581
|
+
| ---------------- | ----- | ---------- |
|
|
582
|
+
| `root` | `.devdock-root` | The fixed wrapper holding the button and the popup. |
|
|
583
|
+
| `button` | `.devdock-button` | The floating button. `[aria-expanded="true"]` while open. |
|
|
584
|
+
| `dot` | `.devdock-dot` | The status dot inside it. |
|
|
585
|
+
| `buttonLabel` | `.devdock-button-label`| The button's text. |
|
|
586
|
+
| `panel` | `.devdock-panel` | The popup. |
|
|
587
|
+
| `title` | `.devdock-title` | Its heading. |
|
|
588
|
+
| `inputWrap` | `.devdock-input-wrap` | The padding around the filter input. |
|
|
589
|
+
| `input` | `.devdock-input` | The filter input. |
|
|
590
|
+
| `body` | `.devdock-body` | The scroller holding the views and the list. |
|
|
591
|
+
| `list` | `.devdock-list` | The routes/commands list. |
|
|
592
|
+
| `section` | `.devdock-section` | One group's wrapper, in the list or around a view. |
|
|
593
|
+
| `group` | `.devdock-group` | A group heading. |
|
|
594
|
+
| `row` | `.devdock-row` | Every row. `[data-active]` on the highlighted one. |
|
|
595
|
+
| `rowActive` | — | Added to the highlighted row, on top of `row`. |
|
|
596
|
+
| `rowLabel` | `.devdock-row-label` | A row's label. |
|
|
597
|
+
| `rowPath` | `.devdock-row-path` | The monospace path on a route row. |
|
|
598
|
+
| `check` | `.devdock-check` | A toggle command's check mark. |
|
|
599
|
+
| `empty` | `.devdock-empty` | The "Nothing here." placeholder. |
|
|
600
|
+
| `viewLabel` | `.devdock-view-label` | A view's own label under a shared heading. |
|
|
601
|
+
| `view` | `.devdock-view` | Each view's host element. |
|
|
458
602
|
|
|
459
603
|
## API
|
|
460
604
|
|
|
@@ -474,12 +618,18 @@ isolation is the reason the dock survives arbitrary host CSS.
|
|
|
474
618
|
| `hotkey` | `string \| null` | `null` | Toggle shortcut, e.g. `"mod+."`. |
|
|
475
619
|
| `zIndex` | `number` | `2147483000` | Base z-index. |
|
|
476
620
|
| `container` | `HTMLElement` | `document.body` | Where to mount. |
|
|
477
|
-
| `shadow` | `boolean \| "inherit"` | `true`
|
|
621
|
+
| `shadow` | `boolean \| "inherit"` | `true`, or `"inherit"` with `classNames` | Render in a shadow root (CSS-isolated). `"inherit"` copies the page's styles in; `false` renders in the light DOM. Read once, at creation. |
|
|
622
|
+
| `classNames` | `DevDockClassNames` | `{}` | [Classes merged onto the dock's parts](#restyling-it-classnames) with `cn`, e.g. `{ panel: ["w-96", className] }`. |
|
|
623
|
+
| `cssLayer` | `string \| false` | `"devdock"` | The cascade layer the dock's stylesheet goes in. A name of your own ships no order statement — place it yourself. `false` for an unlayered page (Tailwind v3). Read once, at creation. |
|
|
478
624
|
|
|
479
625
|
`DevRoute`: `{ path: string; label?: string; group?: string }`
|
|
480
626
|
|
|
481
627
|
`DevCommand`: `{ label: string | (() => string); run: () => void | Promise<void>; checked?: () => boolean; id?: string; group?: string; keepOpen?: boolean }`
|
|
482
628
|
|
|
629
|
+
`DevDockClassNames`: one optional `ClassValue` per part — see [the parts](#the-parts).
|
|
630
|
+
|
|
631
|
+
`ClassValue`: whatever `cn` accepts — `string | number | bigint | boolean | null | undefined | ClassValue[] | Record<string, unknown>`. Falsy entries drop out, conflicting Tailwind classes collapse to the last one.
|
|
632
|
+
|
|
483
633
|
`DevView`: `{ mount: (host: HTMLElement) => void | (() => void); id?: string; label?: string; group?: string; order?: "before" | "after" }` — on the React adapter, `render: () => ReactNode` replaces `mount`.
|
|
484
634
|
|
|
485
635
|
### `@chuvenger/devdock` (core)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { keyViews, createDevDock } from './chunk-
|
|
1
|
+
import { keyViews, createDevDock } from './chunk-2YX4DI2D.js';
|
|
2
2
|
import { useRef, useState, useMemo, useEffect } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -56,5 +56,5 @@ function DevDock({ views = NO_VIEWS, ...options }) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export { DevDock };
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
60
|
-
//# sourceMappingURL=chunk-
|
|
59
|
+
//# sourceMappingURL=chunk-2IQRWZ7T.js.map
|
|
60
|
+
//# sourceMappingURL=chunk-2IQRWZ7T.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/DevDock.tsx"],"names":[],"mappings":";;;;;AA6BA,IAAM,WAA0B,EAAC;AAQ1B,SAAS,QAAQ,EAAE,KAAA,GAAQ,QAAA,EAAU,GAAG,SAAQ,EAAiB;AACtE,EAAA,MAAM,QAAA,GAAW,OAA+B,IAAI,CAAA;AAIpD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA;AAAA,IACxB,0BAAU,GAAA;AAAI,GAChB;AAGA,EAAA,MAAM,KAAA,GAAQ,OAAO,IAAI,CAAA;AAIzB,EAAA,MAAM,KAAA,GAAQ,QAAQ,MAAM,QAAA,CAAS,KAAK,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEpD,EAAA,MAAM,SAAA,GAAY,OAAA;AAAA,IAChB,MACE,KAAA,CAAM,GAAA,CAAI,CAAC,EAAE,GAAA,EAAK,MAAK,MAAO;AAAA,MAC5B,EAAA,EAAI,GAAA;AAAA,MACJ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,KAAA,EAAO,CAAC,IAAA,KAAS;AACf,QAAA,QAAA,CAAS,CAAC,SAAS,IAAI,GAAA,CAAI,IAAI,CAAA,CAAE,GAAA,CAAI,GAAA,EAAK,IAAI,CAAC,CAAA;AAC/C,QAAA,OAAO,MAAM;AACX,UAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACpB,UAAA,QAAA,CAAS,CAAC,IAAA,KAAS;AACjB,YAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,YAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AACf,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA;AAAA,MACF;AAAA,KACF,CAAE,CAAA;AAAA,IACJ,CAAC,KAAK;AAAA,GACR;AAKA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,EAAE,OAAA,EAAS,WAAW,CAAA;AAC5C,EAAA,MAAA,CAAO,OAAA,GAAU,EAAE,OAAA,EAAS,SAAA,EAAU;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,IAAA,MAAM,OAAO,aAAA,CAAc;AAAA,MACzB,GAAG,OAAO,OAAA,CAAQ,OAAA;AAAA,MAClB,KAAA,EAAO,OAAO,OAAA,CAAQ;AAAA,KACvB,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAChB,MAAA,IAAA,CAAK,OAAA,EAAQ;AACb,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,IACrB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,SAAS,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAAA,EAC3D,CAAC,CAAA;AAED,EAAA,uCAEK,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,EAAE,GAAA,EAAK,MAAK,KAAM;AAC5B,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC1B,IAAA,OAAO,OAAO,YAAA,CAAa,IAAA,CAAK,QAAO,EAAG,IAAA,EAAM,GAAG,CAAA,GAAI,IAAA;AAAA,EACzD,CAAC,CAAA,EACH,CAAA;AAEJ","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/react/DevDock.tsx"],"names":[],"mappings":";;;;;AA6BA,IAAM,WAA0B,EAAC;AAQ1B,SAAS,QAAQ,EAAE,KAAA,GAAQ,QAAA,EAAU,GAAG,SAAQ,EAAiB;AACtE,EAAA,MAAM,QAAA,GAAW,OAA+B,IAAI,CAAA;AAIpD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA;AAAA,IACxB,0BAAU,GAAA;AAAI,GAChB;AAGA,EAAA,MAAM,KAAA,GAAQ,OAAO,IAAI,CAAA;AAIzB,EAAA,MAAM,KAAA,GAAQ,QAAQ,MAAM,QAAA,CAAS,KAAK,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEpD,EAAA,MAAM,SAAA,GAAY,OAAA;AAAA,IAChB,MACE,KAAA,CAAM,GAAA,CAAI,CAAC,EAAE,GAAA,EAAK,MAAK,MAAO;AAAA,MAC5B,EAAA,EAAI,GAAA;AAAA,MACJ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,KAAA,EAAO,CAAC,IAAA,KAAS;AACf,QAAA,QAAA,CAAS,CAAC,SAAS,IAAI,GAAA,CAAI,IAAI,CAAA,CAAE,GAAA,CAAI,GAAA,EAAK,IAAI,CAAC,CAAA;AAC/C,QAAA,OAAO,MAAM;AACX,UAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACpB,UAAA,QAAA,CAAS,CAAC,IAAA,KAAS;AACjB,YAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,YAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AACf,YAAA,OAAO,IAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH,CAAA;AAAA,MACF;AAAA,KACF,CAAE,CAAA;AAAA,IACJ,CAAC,KAAK;AAAA,GACR;AAKA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,EAAE,OAAA,EAAS,WAAW,CAAA;AAC5C,EAAA,MAAA,CAAO,OAAA,GAAU,EAAE,OAAA,EAAS,SAAA,EAAU;AAEtC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAChB,IAAA,MAAM,OAAO,aAAA,CAAc;AAAA,MACzB,GAAG,OAAO,OAAA,CAAQ,OAAA;AAAA,MAClB,KAAA,EAAO,OAAO,OAAA,CAAQ;AAAA,KACvB,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,CAAM,OAAA,GAAU,KAAA;AAChB,MAAA,IAAA,CAAK,OAAA,EAAQ;AACb,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,IACrB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,SAAS,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAAA,EAC3D,CAAC,CAAA;AAED,EAAA,uCAEK,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,EAAE,GAAA,EAAK,MAAK,KAAM;AAC5B,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC1B,IAAA,OAAO,OAAO,YAAA,CAAa,IAAA,CAAK,QAAO,EAAG,IAAA,EAAM,GAAG,CAAA,GAAI,IAAA;AAAA,EACzD,CAAC,CAAA,EACH,CAAA;AAEJ","file":"chunk-2IQRWZ7T.js","sourcesContent":["import { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { createDevDock, keyViews } from \"../core/dock\";\nimport type {\n DevDockInstance,\n DevDockOptions,\n DevView,\n} from \"../core/types\";\n\n/**\n * A custom panel rendered by React inside the dock's popup. Same contract as\n * the core's {@link DevView}, with `render` in place of `mount`: the component\n * is portaled into the dock, mounted when the popup opens and unmounted when\n * it closes.\n *\n * A view renders inside the dock's shadow root, which the document's\n * stylesheets don't cross — pass `shadow=\"inherit\"` (or `shadow={false}`) if\n * it uses your app's CSS.\n */\nexport interface DevDockView extends Omit<DevView, \"mount\"> {\n /** The view's content. Called on every render of the host `<DevDock>`. */\n render: () => ReactNode;\n}\n\nexport type DevDockProps = Omit<DevDockOptions, \"views\"> & {\n /** Custom panels rendered inside the popup. See {@link DevDockView}. */\n views?: DevDockView[];\n};\n\nconst NO_VIEWS: DevDockView[] = [];\n\n/**\n * React wrapper around the framework-agnostic core. The dock itself mounts\n * into `document.body` (or `container`) and is torn down on unmount; this\n * component renders only the portals for any `views` you pass, so with none it\n * renders nothing. Safe to leave mounted; it renders nothing in production.\n */\nexport function DevDock({ views = NO_VIEWS, ...options }: DevDockProps) {\n const instance = useRef<DevDockInstance | null>(null);\n // The host elements the core created for our views, keyed the same way it\n // keys them. React draws into these with a portal, which keeps the core free\n // of any React import.\n const [hosts, setHosts] = useState<ReadonlyMap<string, HTMLElement>>(\n () => new Map(),\n );\n // `destroy()` tears views down, which would otherwise ask an unmounting\n // component to set state.\n const alive = useRef(true);\n\n // Keyed exactly the way the core keys the host elements, so each portal\n // finds its own.\n const keyed = useMemo(() => keyViews(views), [views]);\n\n const coreViews = useMemo<DevView[]>(\n () =>\n keyed.map(({ key, view }) => ({\n id: key,\n label: view.label,\n group: view.group,\n order: view.order,\n mount: (host) => {\n setHosts((prev) => new Map(prev).set(key, host));\n return () => {\n if (!alive.current) return;\n setHosts((prev) => {\n const next = new Map(prev);\n next.delete(key);\n return next;\n });\n };\n },\n })),\n [keyed],\n );\n\n // The core reconciles views by id and no-ops when their shape is unchanged,\n // so handing it a fresh array on every render costs nothing and never\n // remounts a view.\n const latest = useRef({ options, coreViews });\n latest.current = { options, coreViews };\n\n useEffect(() => {\n alive.current = true;\n const inst = createDevDock({\n ...latest.current.options,\n views: latest.current.coreViews,\n });\n instance.current = inst;\n return () => {\n alive.current = false;\n inst.destroy();\n instance.current = null;\n };\n }, []);\n\n useEffect(() => {\n instance.current?.update({ ...options, views: coreViews });\n });\n\n return (\n <>\n {keyed.map(({ key, view }) => {\n const host = hosts.get(key);\n return host ? createPortal(view.render(), host, key) : null;\n })}\n </>\n );\n}\n"]}
|