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