@chuvenger/devdock 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +171 -16
  2. package/dist/chunk-3MVFATRN.js +60 -0
  3. package/dist/chunk-3MVFATRN.js.map +1 -0
  4. package/dist/{chunk-F2KWDPMA.js → chunk-NXZNFF34.js} +238 -42
  5. package/dist/chunk-NXZNFF34.js.map +1 -0
  6. package/dist/{dock-CvTtAelh.d.ts → dock-P6l6pKjS.d.ts} +1 -1
  7. package/dist/{dock-Du6Im767.d.cts → dock-d3HqT25k.d.cts} +1 -1
  8. package/dist/index.cjs +235 -39
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +1 -1
  13. package/dist/{match-B6LN1RRP.d.cts → match-DDSJIRYW.d.cts} +1 -1
  14. package/dist/{match-C-mY8W8T.d.ts → match-DwGIF1Xt.d.ts} +1 -1
  15. package/dist/react-router.cjs +276 -47
  16. package/dist/react-router.cjs.map +1 -1
  17. package/dist/react-router.d.cts +2 -2
  18. package/dist/react-router.d.ts +2 -2
  19. package/dist/react-router.js +2 -2
  20. package/dist/react.cjs +277 -47
  21. package/dist/react.cjs.map +1 -1
  22. package/dist/react.d.cts +28 -8
  23. package/dist/react.d.ts +28 -8
  24. package/dist/react.js +2 -2
  25. package/dist/svelte.cjs +235 -39
  26. package/dist/svelte.cjs.map +1 -1
  27. package/dist/svelte.d.cts +3 -3
  28. package/dist/svelte.d.ts +3 -3
  29. package/dist/svelte.js +2 -2
  30. package/dist/svelte.js.map +1 -1
  31. package/dist/sveltekit.cjs +235 -39
  32. package/dist/sveltekit.cjs.map +1 -1
  33. package/dist/sveltekit.d.cts +4 -4
  34. package/dist/sveltekit.d.ts +4 -4
  35. package/dist/sveltekit.js +2 -2
  36. package/dist/sveltekit.js.map +1 -1
  37. package/dist/types-B_SpF32A.d.cts +157 -0
  38. package/dist/types-B_SpF32A.d.ts +157 -0
  39. package/package.json +1 -1
  40. package/dist/chunk-D5IQBV2K.js +0 -24
  41. package/dist/chunk-D5IQBV2K.js.map +0 -1
  42. package/dist/chunk-F2KWDPMA.js.map +0 -1
  43. package/dist/types-fhhRek-e.d.cts +0 -88
  44. package/dist/types-fhhRek-e.d.ts +0 -88
package/dist/react.cjs CHANGED
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ var reactDom = require('react-dom');
5
+ var jsxRuntime = require('react/jsx-runtime');
4
6
 
5
7
  // src/react/DevDock.tsx
6
8
 
@@ -23,7 +25,16 @@ function matchHotkey(spec, e) {
23
25
  }
24
26
 
25
27
  // src/core/dock.ts
28
+ var viteDevFlag = () => {
29
+ try {
30
+ if (typeof undefined.DEV === "boolean") return undefined.DEV;
31
+ } catch {
32
+ }
33
+ return void 0;
34
+ };
26
35
  var isDevBuild = () => {
36
+ const vite = viteDevFlag();
37
+ if (vite !== void 0) return vite;
27
38
  try {
28
39
  return process.env.NODE_ENV !== "production";
29
40
  } catch {
@@ -49,6 +60,27 @@ function el(tag, styles, props) {
49
60
  if (props) Object.assign(node, props);
50
61
  return node;
51
62
  }
63
+ function sectionHeading(text) {
64
+ return el(
65
+ "div",
66
+ {
67
+ padding: "6px 8px 2px",
68
+ fontSize: "10px",
69
+ fontWeight: "700",
70
+ letterSpacing: "0.6px",
71
+ textTransform: "uppercase",
72
+ color: "#6b7280"
73
+ },
74
+ { textContent: text }
75
+ );
76
+ }
77
+ function viewLabel(text) {
78
+ return el(
79
+ "div",
80
+ { padding: "4px 8px 0", fontSize: "11px", color: "#8b909c" },
81
+ { textContent: text }
82
+ );
83
+ }
52
84
  function cornerStyle(position) {
53
85
  const gap = "16px";
54
86
  switch (position) {
@@ -72,6 +104,15 @@ function panelAnchor(position) {
72
104
  };
73
105
  }
74
106
  var FONT = "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif";
107
+ function keyViews(views) {
108
+ const used = /* @__PURE__ */ new Set();
109
+ return views.map((view, i) => {
110
+ let key = view.id ?? `#${i}`;
111
+ while (used.has(key)) key = `${key}#${i}`;
112
+ used.add(key);
113
+ return { view, key };
114
+ });
115
+ }
75
116
  function createDevDock(initial = {}) {
76
117
  if (typeof document === "undefined") {
77
118
  return { update() {
@@ -85,9 +126,12 @@ function createDevDock(initial = {}) {
85
126
  let mounted = false;
86
127
  const host = el("div");
87
128
  host.setAttribute("data-devdock", "");
88
- const useShadow = (initial.shadow ?? true) && typeof host.attachShadow === "function";
129
+ const shadowMode = initial.shadow ?? true;
130
+ const useShadow = shadowMode !== false && typeof host.attachShadow === "function";
89
131
  const shadowRoot = useShadow ? host.attachShadow({ mode: "open" }) : null;
90
132
  const mountPoint = shadowRoot ?? host;
133
+ const styleSlot = shadowRoot && shadowMode === "inherit" ? el("div", { display: "contents" }) : null;
134
+ if (styleSlot) mountPoint.appendChild(styleSlot);
91
135
  const root = el("div", { fontFamily: FONT });
92
136
  mountPoint.appendChild(root);
93
137
  const panel = el("div", {
@@ -126,8 +170,19 @@ function createDevDock(initial = {}) {
126
170
  });
127
171
  input.placeholder = "Filter\u2026";
128
172
  inputWrap.appendChild(input);
129
- const list = el("div", { overflowY: "auto", padding: "0 6px 8px" });
130
- panel.append(heading, inputWrap, list);
173
+ const list = el("div", { padding: "0 6px 8px" });
174
+ list.setAttribute("data-devdock-list", "");
175
+ const viewsBefore = el("div");
176
+ viewsBefore.setAttribute("data-devdock-views", "before");
177
+ const viewsAfter = el("div");
178
+ viewsAfter.setAttribute("data-devdock-views", "after");
179
+ const body = el("div", {
180
+ overflowY: "auto",
181
+ display: "flex",
182
+ flexDirection: "column"
183
+ });
184
+ body.append(viewsBefore, list, viewsAfter);
185
+ panel.append(heading, inputWrap, body);
131
186
  const button = el("button", {
132
187
  display: "inline-flex",
133
188
  alignItems: "center",
@@ -168,20 +223,35 @@ function createDevDock(initial = {}) {
168
223
  const buildItems = () => {
169
224
  const out = [];
170
225
  (opts.routes ?? []).forEach((r, i) => {
226
+ const key = `route:${r.path}:${i}`;
227
+ const label = r.label ?? r.path;
171
228
  out.push({
172
229
  kind: "route",
173
- key: `route:${r.path}:${i}`,
174
- label: r.label ?? r.path,
230
+ key,
231
+ label,
175
232
  group: r.group ?? "Routes",
233
+ sig: [key, r.group ?? "Routes", label, r.path].join("\0"),
176
234
  route: r
177
235
  });
178
236
  });
179
237
  (opts.commands ?? []).forEach((c, i) => {
238
+ const live = typeof c.label === "function";
239
+ const label = typeof c.label === "function" ? c.label() : c.label;
240
+ const checked = c.checked ? c.checked() === true : null;
241
+ const key = `cmd:${c.id ?? (live ? "" : label)}:${i}`;
242
+ const group = c.group ?? "Commands";
180
243
  out.push({
181
244
  kind: "command",
182
- key: `cmd:${c.id ?? c.label}:${i}`,
183
- label: c.label,
184
- group: c.group ?? "Commands",
245
+ key,
246
+ label,
247
+ group,
248
+ sig: [
249
+ key,
250
+ group,
251
+ live ? "\0live" : label,
252
+ checked === null ? "" : "\0checkable"
253
+ ].join("\0"),
254
+ checked,
185
255
  command: c
186
256
  });
187
257
  });
@@ -209,9 +279,17 @@ function createDevDock(initial = {}) {
209
279
  };
210
280
  let rows = [];
211
281
  let rowsSig = null;
212
- const itemSig = (it) => [it.key, it.group, it.label, it.kind === "route" ? it.route.path : ""].join(
213
- "\0"
214
- );
282
+ const itemSig = (it) => it.sig;
283
+ const paintRow = (row) => {
284
+ const it = row.item;
285
+ if (row.labelSpan.textContent !== it.label)
286
+ row.labelSpan.textContent = it.label;
287
+ if (row.checkSpan && it.kind === "command") {
288
+ const on = it.checked === true;
289
+ row.checkSpan.textContent = on ? "\u2713" : "";
290
+ row.node.setAttribute("aria-checked", String(on));
291
+ }
292
+ };
215
293
  const paintActive = () => {
216
294
  rows.forEach((row, idx) => {
217
295
  const isActive = idx === active;
@@ -233,7 +311,9 @@ function createDevDock(initial = {}) {
233
311
  if (sig === rowsSig) {
234
312
  filtered.forEach((it, idx) => {
235
313
  const row = rows[idx];
236
- if (row) row.item = it;
314
+ if (!row) return;
315
+ row.item = it;
316
+ paintRow(row);
237
317
  });
238
318
  paintActive();
239
319
  return;
@@ -259,20 +339,7 @@ function createDevDock(initial = {}) {
259
339
  }
260
340
  for (const [groupName, groupItems] of groups) {
261
341
  const section = el("div", { marginTop: "6px" });
262
- section.appendChild(
263
- el(
264
- "div",
265
- {
266
- padding: "6px 8px 2px",
267
- fontSize: "10px",
268
- fontWeight: "700",
269
- letterSpacing: "0.6px",
270
- textTransform: "uppercase",
271
- color: "#6b7280"
272
- },
273
- { textContent: groupName }
274
- )
275
- );
342
+ section.appendChild(sectionHeading(groupName));
276
343
  for (const it of groupItems) {
277
344
  const idx = filtered.indexOf(it);
278
345
  const row = el("button", {
@@ -291,15 +358,11 @@ function createDevDock(initial = {}) {
291
358
  cursor: "pointer"
292
359
  });
293
360
  row.type = "button";
294
- const labelSpan = el(
295
- "span",
296
- {
297
- overflow: "hidden",
298
- textOverflow: "ellipsis",
299
- whiteSpace: "nowrap"
300
- },
301
- { textContent: it.label }
302
- );
361
+ const labelSpan = el("span", {
362
+ overflow: "hidden",
363
+ textOverflow: "ellipsis",
364
+ whiteSpace: "nowrap"
365
+ });
303
366
  row.appendChild(labelSpan);
304
367
  let pathSpan = null;
305
368
  if (it.kind === "route" && it.route.path !== it.label) {
@@ -315,8 +378,20 @@ function createDevDock(initial = {}) {
315
378
  );
316
379
  row.appendChild(pathSpan);
317
380
  }
318
- const rec = { item: it, node: row, pathSpan };
381
+ let checkSpan = null;
382
+ if (it.kind === "command" && it.checked !== null) {
383
+ row.setAttribute("role", "checkbox");
384
+ checkSpan = el("span", {
385
+ flexShrink: "0",
386
+ width: "12px",
387
+ textAlign: "center",
388
+ color: "#22c55e"
389
+ });
390
+ row.appendChild(checkSpan);
391
+ }
392
+ const rec = { item: it, node: row, labelSpan, pathSpan, checkSpan };
319
393
  rows[idx] = rec;
394
+ paintRow(rec);
320
395
  row.addEventListener("mouseenter", () => setActive(idx));
321
396
  row.addEventListener("click", () => void runItem(rec.item));
322
397
  section.appendChild(row);
@@ -325,6 +400,120 @@ function createDevDock(initial = {}) {
325
400
  }
326
401
  paintActive();
327
402
  };
403
+ const mountedViews = /* @__PURE__ */ new Map();
404
+ let viewsSig = null;
405
+ const viewOrder = (v) => v.order === "after" ? "after" : "before";
406
+ const unmountViews = () => {
407
+ for (const mv of mountedViews.values()) {
408
+ mv.teardown?.();
409
+ mv.host.remove();
410
+ }
411
+ mountedViews.clear();
412
+ viewsBefore.replaceChildren();
413
+ viewsAfter.replaceChildren();
414
+ viewsSig = null;
415
+ };
416
+ const renderViews = () => {
417
+ const keyed = keyViews(opts.views ?? []);
418
+ const sig = keyed.map(
419
+ ({ view, key }) => [key, viewOrder(view), view.group ?? "", view.label ?? ""].join(
420
+ "\0"
421
+ )
422
+ ).join("\n");
423
+ if (sig === viewsSig) return;
424
+ viewsSig = sig;
425
+ const live = new Set(keyed.map(({ key }) => key));
426
+ for (const [key, mv] of mountedViews) {
427
+ if (live.has(key)) continue;
428
+ mv.teardown?.();
429
+ mv.host.remove();
430
+ mountedViews.delete(key);
431
+ }
432
+ viewsBefore.replaceChildren();
433
+ viewsAfter.replaceChildren();
434
+ const pending = [];
435
+ for (const placement of ["before", "after"]) {
436
+ const container = placement === "before" ? viewsBefore : viewsAfter;
437
+ const here = keyed.filter(({ view }) => viewOrder(view) === placement);
438
+ const sections = /* @__PURE__ */ new Map();
439
+ for (const entry of here) {
440
+ const groupKey = entry.view.group ?? `\0${entry.key}`;
441
+ const section = sections.get(groupKey);
442
+ if (section) section.items.push(entry);
443
+ else
444
+ sections.set(groupKey, {
445
+ heading: entry.view.group ?? entry.view.label,
446
+ items: [entry]
447
+ });
448
+ }
449
+ for (const section of sections.values()) {
450
+ const wrap = el("div", { marginTop: "6px" });
451
+ if (section.heading) wrap.appendChild(sectionHeading(section.heading));
452
+ for (const { view, key } of section.items) {
453
+ if (view.group != null && view.label)
454
+ wrap.appendChild(viewLabel(view.label));
455
+ const existing = mountedViews.get(key);
456
+ if (existing) {
457
+ wrap.appendChild(existing.host);
458
+ continue;
459
+ }
460
+ const viewHost = el("div", { padding: "2px 8px 6px" });
461
+ viewHost.setAttribute("data-devdock-view", view.id ?? "");
462
+ wrap.appendChild(viewHost);
463
+ pending.push({ view, key, host: viewHost });
464
+ }
465
+ container.appendChild(wrap);
466
+ }
467
+ }
468
+ for (const { view, key, host: viewHost } of pending) {
469
+ const mv = { host: viewHost };
470
+ mountedViews.set(key, mv);
471
+ const teardown = view.mount(viewHost);
472
+ if (typeof teardown === "function") mv.teardown = teardown;
473
+ }
474
+ };
475
+ let clonesSig = null;
476
+ const syncStyles = () => {
477
+ if (!styleSlot || !shadowRoot) return;
478
+ const adopted = document.adoptedStyleSheets;
479
+ if (Array.isArray(adopted)) {
480
+ try {
481
+ const sr = shadowRoot;
482
+ sr.adoptedStyleSheets = [...adopted];
483
+ } catch {
484
+ }
485
+ }
486
+ const sheets = [
487
+ ...document.querySelectorAll('style, link[rel~="stylesheet" i]')
488
+ ];
489
+ const sig = sheets.map((n) => `${n.tagName}\0${n.getAttribute("href") ?? n.textContent}`).join("\n");
490
+ if (sig === clonesSig) return;
491
+ clonesSig = sig;
492
+ styleSlot.replaceChildren(...sheets.map((n) => n.cloneNode(true)));
493
+ };
494
+ let styleObserver = null;
495
+ let syncQueued = false;
496
+ const watchStyles = () => {
497
+ if (!styleSlot || styleObserver || !document.head || typeof MutationObserver === "undefined")
498
+ return;
499
+ styleObserver = new MutationObserver(() => {
500
+ if (syncQueued) return;
501
+ syncQueued = true;
502
+ queueMicrotask(() => {
503
+ syncQueued = false;
504
+ syncStyles();
505
+ });
506
+ });
507
+ styleObserver.observe(document.head, {
508
+ childList: true,
509
+ subtree: true,
510
+ characterData: true
511
+ });
512
+ };
513
+ const unwatchStyles = () => {
514
+ styleObserver?.disconnect();
515
+ styleObserver = null;
516
+ };
328
517
  const renderShell = () => {
329
518
  const position = opts.position ?? "bottom-right";
330
519
  const zIndex = opts.zIndex ?? 2147483e3;
@@ -354,9 +543,12 @@ function createDevDock(initial = {}) {
354
543
  panel.isConnected || root.insertBefore(panel, button);
355
544
  renderShell();
356
545
  renderList();
546
+ syncStyles();
547
+ renderViews();
357
548
  requestAnimationFrame(() => input.focus());
358
- } else if (panel.isConnected) {
359
- panel.remove();
549
+ } else {
550
+ unmountViews();
551
+ if (panel.isConnected) panel.remove();
360
552
  }
361
553
  renderShell();
362
554
  }
@@ -403,11 +595,14 @@ function createDevDock(initial = {}) {
403
595
  if (mounted) return;
404
596
  (opts.container ?? document.body).appendChild(host);
405
597
  addListeners();
598
+ syncStyles();
599
+ watchStyles();
406
600
  mounted = true;
407
601
  };
408
602
  const unmount = () => {
409
603
  if (!mounted) return;
410
604
  removeListeners();
605
+ unwatchStyles();
411
606
  host.remove();
412
607
  mounted = false;
413
608
  };
@@ -419,7 +614,10 @@ function createDevDock(initial = {}) {
419
614
  if (enabled()) {
420
615
  mount();
421
616
  renderShell();
422
- if (open) renderList();
617
+ if (open) {
618
+ renderList();
619
+ renderViews();
620
+ }
423
621
  } else {
424
622
  setOpen(false);
425
623
  unmount();
@@ -431,24 +629,56 @@ function createDevDock(initial = {}) {
431
629
  }
432
630
  };
433
631
  }
434
-
435
- // src/react/DevDock.tsx
436
- function DevDock(props) {
632
+ var NO_VIEWS = [];
633
+ function DevDock({ views = NO_VIEWS, ...options }) {
437
634
  const instance = react.useRef(null);
438
- const latest = react.useRef(props);
439
- latest.current = props;
635
+ const [hosts, setHosts] = react.useState(
636
+ () => /* @__PURE__ */ new Map()
637
+ );
638
+ const alive = react.useRef(true);
639
+ const keyed = react.useMemo(() => keyViews(views), [views]);
640
+ const coreViews = react.useMemo(
641
+ () => keyed.map(({ key, view }) => ({
642
+ id: key,
643
+ label: view.label,
644
+ group: view.group,
645
+ order: view.order,
646
+ mount: (host) => {
647
+ setHosts((prev) => new Map(prev).set(key, host));
648
+ return () => {
649
+ if (!alive.current) return;
650
+ setHosts((prev) => {
651
+ const next = new Map(prev);
652
+ next.delete(key);
653
+ return next;
654
+ });
655
+ };
656
+ }
657
+ })),
658
+ [keyed]
659
+ );
660
+ const latest = react.useRef({ options, coreViews });
661
+ latest.current = { options, coreViews };
440
662
  react.useEffect(() => {
441
- const inst = createDevDock(latest.current);
663
+ alive.current = true;
664
+ const inst = createDevDock({
665
+ ...latest.current.options,
666
+ views: latest.current.coreViews
667
+ });
442
668
  instance.current = inst;
443
669
  return () => {
670
+ alive.current = false;
444
671
  inst.destroy();
445
672
  instance.current = null;
446
673
  };
447
674
  }, []);
448
675
  react.useEffect(() => {
449
- instance.current?.update(props);
676
+ instance.current?.update({ ...options, views: coreViews });
450
677
  });
451
- return null;
678
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: keyed.map(({ key, view }) => {
679
+ const host = hosts.get(key);
680
+ return host ? reactDom.createPortal(view.render(), host, key) : null;
681
+ }) });
452
682
  }
453
683
 
454
684
  exports.DevDock = DevDock;