@chuvenger/devdock 0.6.0 → 0.8.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 +165 -15
- package/dist/{chunk-NXZNFF34.js → chunk-7Z3YWV5K.js} +343 -152
- package/dist/chunk-7Z3YWV5K.js.map +1 -0
- package/dist/{chunk-3MVFATRN.js → chunk-MKYQ666Z.js} +3 -3
- package/dist/{chunk-3MVFATRN.js.map → chunk-MKYQ666Z.js.map} +1 -1
- package/dist/{dock-P6l6pKjS.d.ts → dock-C8tR26RP.d.ts} +1 -1
- package/dist/{dock-d3HqT25k.d.cts → dock-Cxh5FIAv.d.cts} +1 -1
- package/dist/index.cjs +344 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +1 -1
- package/dist/{match-DDSJIRYW.d.cts → match-CFB-9KGi.d.cts} +1 -1
- package/dist/{match-DwGIF1Xt.d.ts → match-injUeExB.d.ts} +1 -1
- package/dist/react-router.cjs +339 -149
- 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 +339 -149
- 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 +342 -149
- 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 +342 -149
- 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-BuYSEeDa.d.cts} +113 -5
- package/dist/{types-B_SpF32A.d.ts → types-BuYSEeDa.d.ts} +113 -5
- 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
|
|
|
@@ -11,8 +11,8 @@ routes and run custom commands.
|
|
|
11
11
|
- 🧭 **Route jumper** — list routes manually, or auto-detect your `/dev` routes from React Router or SvelteKit.
|
|
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
|
-
-
|
|
15
|
-
- 🎨 Zero CSS to import —
|
|
14
|
+
- ⌨️ **Hotkey out of the box** — ⌥⇧D (Alt+Shift+D elsewhere) toggles the popup, a chord no browser, OS or app tends to want, and the popup's heading says so. Rebind with `hotkey`, or `hotkey={null}` to claim no key. Plus a filter box and keyboard nav (↑/↓/Enter/Esc).
|
|
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
|
|
|
@@ -61,7 +61,6 @@ export function App() {
|
|
|
61
61
|
{ label: "Reset onboarding", run: () => localStorage.clear() },
|
|
62
62
|
{ label: "Log current user", run: async () => console.log(await getUser()) },
|
|
63
63
|
]}
|
|
64
|
-
hotkey="mod+."
|
|
65
64
|
/>
|
|
66
65
|
</>
|
|
67
66
|
);
|
|
@@ -182,7 +181,6 @@ in any Svelte 4/5 app:
|
|
|
182
181
|
const options = {
|
|
183
182
|
routes: [{ path: "/admin", label: "Admin" }],
|
|
184
183
|
commands: [{ label: "Reset", run: () => localStorage.clear() }],
|
|
185
|
-
hotkey: "mod+.",
|
|
186
184
|
onNavigate: (path) => goto(path), // e.g. SvelteKit's goto
|
|
187
185
|
};
|
|
188
186
|
</script>
|
|
@@ -211,7 +209,6 @@ in your app — Vite resolves it at build time — so pass the result in as
|
|
|
211
209
|
const options = {
|
|
212
210
|
modules,
|
|
213
211
|
onNavigate: goto, // client-side navigation instead of a full load
|
|
214
|
-
hotkey: "mod+.",
|
|
215
212
|
};
|
|
216
213
|
</script>
|
|
217
214
|
|
|
@@ -440,11 +437,11 @@ tree-shakes it: `{import.meta.env.DEV && <DevDock … />}`.
|
|
|
440
437
|
|
|
441
438
|
## Styling & isolation
|
|
442
439
|
|
|
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.
|
|
440
|
+
The dock ships its own stylesheet — one it injects itself, nothing to
|
|
441
|
+
import, themeable with [custom properties](#theming-it-custom-properties) — and, by default, renders inside an **open shadow root** attached to a
|
|
442
|
+
host element in `document.body`. The host page's CSS can't leak in and the
|
|
443
|
+
dock's styles can't leak out. The host element carries a `data-devdock`
|
|
444
|
+
attribute in every mode.
|
|
448
445
|
|
|
449
446
|
| `shadow` | Where it renders | The page's CSS |
|
|
450
447
|
| -------- | ---------------- | -------------- |
|
|
@@ -453,8 +450,153 @@ every mode.
|
|
|
453
450
|
| `false` | light DOM | applies directly |
|
|
454
451
|
|
|
455
452
|
Use `"inherit"` (or `false`) when a [custom view](#views-and-your-css) renders
|
|
456
|
-
your app's own components and needs its stylesheet
|
|
457
|
-
|
|
453
|
+
your app's own components and needs its stylesheet, or when you're restyling
|
|
454
|
+
the dock with `classNames` below — passing `classNames` switches the default to
|
|
455
|
+
`"inherit"` for you, since a class of yours is defined by a stylesheet that has
|
|
456
|
+
to cross the boundary. Otherwise leave it alone: isolation is the reason the
|
|
457
|
+
dock survives arbitrary host CSS.
|
|
458
|
+
|
|
459
|
+
There are two ways to change how it looks, and they compose:
|
|
460
|
+
|
|
461
|
+
| | Reach | Costs you |
|
|
462
|
+
| --- | --- | --- |
|
|
463
|
+
| [Custom properties](#theming-it-custom-properties) | colours, radii, font, size | nothing — they inherit **through** the shadow boundary, so the dock stays isolated |
|
|
464
|
+
| [`classNames`](#restyling-it-classnames) | anything CSS can say | your stylesheet has to reach the dock, so `shadow` becomes `"inherit"` |
|
|
465
|
+
|
|
466
|
+
Reach for the properties first; drop to `classNames` when you need something
|
|
467
|
+
they don't cover.
|
|
468
|
+
|
|
469
|
+
### Theming it: custom properties
|
|
470
|
+
|
|
471
|
+
Set any of these on `:root`, or on the `[data-devdock]` host to theme one dock,
|
|
472
|
+
and you're done — no `shadow` change, no classes, nothing to import:
|
|
473
|
+
|
|
474
|
+
```css
|
|
475
|
+
:root {
|
|
476
|
+
--devdock-bg: #ffffff;
|
|
477
|
+
--devdock-fg: #0f172a;
|
|
478
|
+
--devdock-accent: #a855f7;
|
|
479
|
+
--devdock-border: #e2e8f0;
|
|
480
|
+
--devdock-radius: 16px;
|
|
481
|
+
--devdock-width: 320px;
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
| Property | Default | What it paints |
|
|
486
|
+
| -------- | ------- | -------------- |
|
|
487
|
+
| `--devdock-font` | system UI stack | The dock's font family. |
|
|
488
|
+
| `--devdock-bg` | `#16181d` | Popup background. |
|
|
489
|
+
| `--devdock-fg` | `#e7e9ee` | Primary text — rows, the input. |
|
|
490
|
+
| `--devdock-muted` | `#8b909c` | The title, a view's label. |
|
|
491
|
+
| `--devdock-subtle` | `#6b7280` | Group headings, route paths, the empty state. |
|
|
492
|
+
| `--devdock-border` | `#2a2e37` | Every border. |
|
|
493
|
+
| `--devdock-accent` | `#2563eb` | Highlighted row, and the button while open. |
|
|
494
|
+
| `--devdock-accent-fg` | `#e7e9ee` | Text on the highlighted row. |
|
|
495
|
+
| `--devdock-button-bg` | `#111317` | The button, closed. |
|
|
496
|
+
| `--devdock-button-fg` | `#fff` | The button's text. |
|
|
497
|
+
| `--devdock-input-bg` | `#0f1115` | The filter input. |
|
|
498
|
+
| `--devdock-success` | `#22c55e` | The status dot and toggle check marks. |
|
|
499
|
+
| `--devdock-radius` | `12px` | Popup corners. |
|
|
500
|
+
| `--devdock-radius-sm` | `8px` | Rows and the input. |
|
|
501
|
+
| `--devdock-width` | `280px` | Popup width. |
|
|
502
|
+
| `--devdock-max-height` | `70vh` | Popup height before it scrolls. |
|
|
503
|
+
| `--devdock-shadow` | `0 12px 40px rgba(0,0,0,.45)` | The popup's shadow. |
|
|
504
|
+
|
|
505
|
+
They're read as `var(--devdock-x, default)` and never declared inside the
|
|
506
|
+
dock, which is what lets a value you set outside win — a declaration of ours
|
|
507
|
+
in the shadow tree would have beaten the one inheriting in.
|
|
508
|
+
|
|
509
|
+
### Restyling it: `classNames`
|
|
510
|
+
|
|
511
|
+
Every part of the dock takes a class of yours, **merged onto** the one it
|
|
512
|
+
already carries rather than replacing it:
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
<DevDock
|
|
516
|
+
classNames={{
|
|
517
|
+
panel: "w-96 max-h-[80vh] bg-white text-slate-900 border-slate-200",
|
|
518
|
+
button: "bg-emerald-600 rounded-md",
|
|
519
|
+
input: "bg-slate-50 border-slate-300",
|
|
520
|
+
row: "rounded-none",
|
|
521
|
+
rowActive: "bg-emerald-100 text-emerald-900",
|
|
522
|
+
}}
|
|
523
|
+
/>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Each entry takes **anything [`cn`](https://www.npmjs.com/package/cn) takes** —
|
|
527
|
+
a string, an array, a conditional, an object — so a wrapper of your own can
|
|
528
|
+
forward its props straight through without a helper import:
|
|
529
|
+
|
|
530
|
+
```tsx
|
|
531
|
+
export function AppDock({ className }: { className?: string }) {
|
|
532
|
+
return (
|
|
533
|
+
<DevDock
|
|
534
|
+
classNames={{
|
|
535
|
+
panel: ["bg-red-100", className],
|
|
536
|
+
row: ["px-3", compact && "py-1", { "opacity-50": muted }],
|
|
537
|
+
}}
|
|
538
|
+
/>
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Values are merged with [`cn`](https://www.npmjs.com/package/cn), so conflicts
|
|
544
|
+
resolve the way you'd write them by hand: `rowActive: "text-emerald-900"` over
|
|
545
|
+
`row: "text-slate-700"` keeps emerald and **drops** slate, instead of putting
|
|
546
|
+
both in the attribute and letting Tailwind's own sheet order pick the winner.
|
|
547
|
+
The dock's `devdock-*` classes aren't utilities, so they pass through untouched.
|
|
548
|
+
|
|
549
|
+
No `!important`, no `[&_div]` reaching: a plain utility class wins on its own.
|
|
550
|
+
Two things in the dock's sheet make that true, and they're worth knowing about
|
|
551
|
+
because they also say when it *doesn't*:
|
|
552
|
+
|
|
553
|
+
- **It lives in a cascade layer** (`devdock`), declared *after* Tailwind's
|
|
554
|
+
`base` and *before* its `utilities`. Layered rules lose to unlayered ones
|
|
555
|
+
whatever their specificity, so this is what lets a Tailwind v4 utility
|
|
556
|
+
through while keeping a reset in `base` from stripping the dock's chrome.
|
|
557
|
+
- **Each rule is worth exactly one class** — one bare `.devdock-*`, with any
|
|
558
|
+
state selector tucked inside `:where()`. That's what settles ties when the
|
|
559
|
+
layer is off: the sheet goes in first, so a page-level reset
|
|
560
|
+
(`button { … }`, `*`) loses to it and your class beats it.
|
|
561
|
+
|
|
562
|
+
If the page's own CSS is unlayered — Tailwind v3, a hand-written reset —
|
|
563
|
+
turn the layer off with `cssLayer={false}`, or the page's reset will outrank
|
|
564
|
+
the dock itself. (A page with no `@layer` anywhere is unaffected either way.)
|
|
565
|
+
If you run your own layers, name the dock's with `cssLayer="chrome"`: it then
|
|
566
|
+
ships no order statement at all, and you place it in your own `@layer` line,
|
|
567
|
+
where you know the names around it.
|
|
568
|
+
|
|
569
|
+
Not themeable this way: the corner placement, the z-index, and the popup's
|
|
570
|
+
anchoring. Those are computed from `position`/`zIndex` and stay inline.
|
|
571
|
+
|
|
572
|
+
### The parts
|
|
573
|
+
|
|
574
|
+
Each one carries its `devdock-*` class in every mode, so you can also just
|
|
575
|
+
write CSS against them and skip `classNames` entirely.
|
|
576
|
+
|
|
577
|
+
| `classNames` key | Class | What it is |
|
|
578
|
+
| ---------------- | ----- | ---------- |
|
|
579
|
+
| `root` | `.devdock-root` | The fixed wrapper holding the button and the popup. |
|
|
580
|
+
| `button` | `.devdock-button` | The floating button. `[aria-expanded="true"]` while open. |
|
|
581
|
+
| `dot` | `.devdock-dot` | The status dot inside it. |
|
|
582
|
+
| `buttonLabel` | `.devdock-button-label`| The button's text. |
|
|
583
|
+
| `panel` | `.devdock-panel` | The popup. |
|
|
584
|
+
| `title` | `.devdock-title` | Its heading — a row holding the title and the hotkey hint. |
|
|
585
|
+
| `hotkeyHint` | `.devdock-hotkey-hint` | The `<kbd>` in the heading naming the `hotkey`. Hidden with `hotkey: null`. |
|
|
586
|
+
| `inputWrap` | `.devdock-input-wrap` | The padding around the filter input. |
|
|
587
|
+
| `input` | `.devdock-input` | The filter input. |
|
|
588
|
+
| `body` | `.devdock-body` | The scroller holding the views and the list. |
|
|
589
|
+
| `list` | `.devdock-list` | The routes/commands list. |
|
|
590
|
+
| `section` | `.devdock-section` | One group's wrapper, in the list or around a view. |
|
|
591
|
+
| `group` | `.devdock-group` | A group heading. |
|
|
592
|
+
| `row` | `.devdock-row` | Every row. `[data-active]` on the highlighted one. |
|
|
593
|
+
| `rowActive` | — | Added to the highlighted row, on top of `row`. |
|
|
594
|
+
| `rowLabel` | `.devdock-row-label` | A row's label. |
|
|
595
|
+
| `rowPath` | `.devdock-row-path` | The monospace path on a route row. |
|
|
596
|
+
| `check` | `.devdock-check` | A toggle command's check mark. |
|
|
597
|
+
| `empty` | `.devdock-empty` | The "Nothing here." placeholder. |
|
|
598
|
+
| `viewLabel` | `.devdock-view-label` | A view's own label under a shared heading. |
|
|
599
|
+
| `view` | `.devdock-view` | Each view's host element. |
|
|
458
600
|
|
|
459
601
|
## API
|
|
460
602
|
|
|
@@ -471,21 +613,29 @@ isolation is the reason the dock survives arbitrary host CSS.
|
|
|
471
613
|
| `position` | `"bottom-left" \| "bottom-right" \| "top-left" \| "top-right"` | `"bottom-right"` | Corner. |
|
|
472
614
|
| `label` | `string` | `"DEV"` | Button text. |
|
|
473
615
|
| `title` | `string` | `"Dev Menu"` | Popup heading. |
|
|
474
|
-
| `hotkey` | `string \| null` | `
|
|
616
|
+
| `hotkey` | `string \| null` | `"alt+shift+d"` | Toggle shortcut (⌥⇧D / Alt+Shift+D). `"mod"` in a spec is ⌘ on Mac, Ctrl elsewhere. `null` claims no key. |
|
|
475
617
|
| `zIndex` | `number` | `2147483000` | Base z-index. |
|
|
476
618
|
| `container` | `HTMLElement` | `document.body` | Where to mount. |
|
|
477
|
-
| `shadow` | `boolean \| "inherit"` | `true`
|
|
619
|
+
| `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. |
|
|
620
|
+
| `classNames` | `DevDockClassNames` | `{}` | [Classes merged onto the dock's parts](#restyling-it-classnames) with `cn`, e.g. `{ panel: ["w-96", className] }`. |
|
|
621
|
+
| `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
622
|
|
|
479
623
|
`DevRoute`: `{ path: string; label?: string; group?: string }`
|
|
480
624
|
|
|
481
625
|
`DevCommand`: `{ label: string | (() => string); run: () => void | Promise<void>; checked?: () => boolean; id?: string; group?: string; keepOpen?: boolean }`
|
|
482
626
|
|
|
627
|
+
`DevDockClassNames`: one optional `ClassValue` per part — see [the parts](#the-parts).
|
|
628
|
+
|
|
629
|
+
`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.
|
|
630
|
+
|
|
483
631
|
`DevView`: `{ mount: (host: HTMLElement) => void | (() => void); id?: string; label?: string; group?: string; order?: "before" | "after" }` — on the React adapter, `render: () => ReactNode` replaces `mount`.
|
|
484
632
|
|
|
485
633
|
### `@chuvenger/devdock` (core)
|
|
486
634
|
|
|
487
635
|
- `createDevDock(options?): { update(options), destroy() }` — mounts the dock; no-ops during SSR (no `document`).
|
|
488
636
|
- `matchHotkey(spec, event)` — the hotkey matcher (exported for reuse).
|
|
637
|
+
- `formatHotkey(spec)` — a spec as a human reads it: `"⌥⇧D"` on Mac, `"Alt+Shift+D"` elsewhere.
|
|
638
|
+
- `DEFAULT_HOTKEY` — the default spec, `"alt+shift+d"`.
|
|
489
639
|
|
|
490
640
|
### `@chuvenger/devdock/react`
|
|
491
641
|
|