@chuvenger/devdock 0.4.1 → 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 (42) hide show
  1. package/README.md +166 -14
  2. package/dist/chunk-3MVFATRN.js +60 -0
  3. package/dist/chunk-3MVFATRN.js.map +1 -0
  4. package/dist/{chunk-WVRXVSEL.js → chunk-NXZNFF34.js} +229 -42
  5. package/dist/chunk-NXZNFF34.js.map +1 -0
  6. package/dist/{dock-DOWeFZJ5.d.ts → dock-P6l6pKjS.d.ts} +1 -1
  7. package/dist/{dock-dq6OS0Jm.d.cts → dock-d3HqT25k.d.cts} +1 -1
  8. package/dist/index.cjs +226 -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-BZoH6rW7.d.cts → match-DDSJIRYW.d.cts} +1 -1
  14. package/dist/{match-D1am9aTf.d.ts → match-DwGIF1Xt.d.ts} +1 -1
  15. package/dist/react-router.cjs +267 -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 +268 -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 +226 -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 +226 -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-V99upW.d.cts → types-B_SpF32A.d.cts} +73 -5
  38. package/dist/{types-B-V99upW.d.ts → types-B_SpF32A.d.ts} +73 -5
  39. package/package.json +1 -1
  40. package/dist/chunk-P4ZUS4GB.js +0 -24
  41. package/dist/chunk-P4ZUS4GB.js.map +0 -1
  42. package/dist/chunk-WVRXVSEL.js.map +0 -1
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
 
@@ -58,6 +60,27 @@ function el(tag, styles, props) {
58
60
  if (props) Object.assign(node, props);
59
61
  return node;
60
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
+ }
61
84
  function cornerStyle(position) {
62
85
  const gap = "16px";
63
86
  switch (position) {
@@ -81,6 +104,15 @@ function panelAnchor(position) {
81
104
  };
82
105
  }
83
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
+ }
84
116
  function createDevDock(initial = {}) {
85
117
  if (typeof document === "undefined") {
86
118
  return { update() {
@@ -94,9 +126,12 @@ function createDevDock(initial = {}) {
94
126
  let mounted = false;
95
127
  const host = el("div");
96
128
  host.setAttribute("data-devdock", "");
97
- const useShadow = (initial.shadow ?? true) && typeof host.attachShadow === "function";
129
+ const shadowMode = initial.shadow ?? true;
130
+ const useShadow = shadowMode !== false && typeof host.attachShadow === "function";
98
131
  const shadowRoot = useShadow ? host.attachShadow({ mode: "open" }) : null;
99
132
  const mountPoint = shadowRoot ?? host;
133
+ const styleSlot = shadowRoot && shadowMode === "inherit" ? el("div", { display: "contents" }) : null;
134
+ if (styleSlot) mountPoint.appendChild(styleSlot);
100
135
  const root = el("div", { fontFamily: FONT });
101
136
  mountPoint.appendChild(root);
102
137
  const panel = el("div", {
@@ -135,8 +170,19 @@ function createDevDock(initial = {}) {
135
170
  });
136
171
  input.placeholder = "Filter\u2026";
137
172
  inputWrap.appendChild(input);
138
- const list = el("div", { overflowY: "auto", padding: "0 6px 8px" });
139
- 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);
140
186
  const button = el("button", {
141
187
  display: "inline-flex",
142
188
  alignItems: "center",
@@ -177,20 +223,35 @@ function createDevDock(initial = {}) {
177
223
  const buildItems = () => {
178
224
  const out = [];
179
225
  (opts.routes ?? []).forEach((r, i) => {
226
+ const key = `route:${r.path}:${i}`;
227
+ const label = r.label ?? r.path;
180
228
  out.push({
181
229
  kind: "route",
182
- key: `route:${r.path}:${i}`,
183
- label: r.label ?? r.path,
230
+ key,
231
+ label,
184
232
  group: r.group ?? "Routes",
233
+ sig: [key, r.group ?? "Routes", label, r.path].join("\0"),
185
234
  route: r
186
235
  });
187
236
  });
188
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";
189
243
  out.push({
190
244
  kind: "command",
191
- key: `cmd:${c.id ?? c.label}:${i}`,
192
- label: c.label,
193
- 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,
194
255
  command: c
195
256
  });
196
257
  });
@@ -218,9 +279,17 @@ function createDevDock(initial = {}) {
218
279
  };
219
280
  let rows = [];
220
281
  let rowsSig = null;
221
- const itemSig = (it) => [it.key, it.group, it.label, it.kind === "route" ? it.route.path : ""].join(
222
- "\0"
223
- );
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
+ };
224
293
  const paintActive = () => {
225
294
  rows.forEach((row, idx) => {
226
295
  const isActive = idx === active;
@@ -242,7 +311,9 @@ function createDevDock(initial = {}) {
242
311
  if (sig === rowsSig) {
243
312
  filtered.forEach((it, idx) => {
244
313
  const row = rows[idx];
245
- if (row) row.item = it;
314
+ if (!row) return;
315
+ row.item = it;
316
+ paintRow(row);
246
317
  });
247
318
  paintActive();
248
319
  return;
@@ -268,20 +339,7 @@ function createDevDock(initial = {}) {
268
339
  }
269
340
  for (const [groupName, groupItems] of groups) {
270
341
  const section = el("div", { marginTop: "6px" });
271
- section.appendChild(
272
- el(
273
- "div",
274
- {
275
- padding: "6px 8px 2px",
276
- fontSize: "10px",
277
- fontWeight: "700",
278
- letterSpacing: "0.6px",
279
- textTransform: "uppercase",
280
- color: "#6b7280"
281
- },
282
- { textContent: groupName }
283
- )
284
- );
342
+ section.appendChild(sectionHeading(groupName));
285
343
  for (const it of groupItems) {
286
344
  const idx = filtered.indexOf(it);
287
345
  const row = el("button", {
@@ -300,15 +358,11 @@ function createDevDock(initial = {}) {
300
358
  cursor: "pointer"
301
359
  });
302
360
  row.type = "button";
303
- const labelSpan = el(
304
- "span",
305
- {
306
- overflow: "hidden",
307
- textOverflow: "ellipsis",
308
- whiteSpace: "nowrap"
309
- },
310
- { textContent: it.label }
311
- );
361
+ const labelSpan = el("span", {
362
+ overflow: "hidden",
363
+ textOverflow: "ellipsis",
364
+ whiteSpace: "nowrap"
365
+ });
312
366
  row.appendChild(labelSpan);
313
367
  let pathSpan = null;
314
368
  if (it.kind === "route" && it.route.path !== it.label) {
@@ -324,8 +378,20 @@ function createDevDock(initial = {}) {
324
378
  );
325
379
  row.appendChild(pathSpan);
326
380
  }
327
- 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 };
328
393
  rows[idx] = rec;
394
+ paintRow(rec);
329
395
  row.addEventListener("mouseenter", () => setActive(idx));
330
396
  row.addEventListener("click", () => void runItem(rec.item));
331
397
  section.appendChild(row);
@@ -334,6 +400,120 @@ function createDevDock(initial = {}) {
334
400
  }
335
401
  paintActive();
336
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
+ };
337
517
  const renderShell = () => {
338
518
  const position = opts.position ?? "bottom-right";
339
519
  const zIndex = opts.zIndex ?? 2147483e3;
@@ -363,9 +543,12 @@ function createDevDock(initial = {}) {
363
543
  panel.isConnected || root.insertBefore(panel, button);
364
544
  renderShell();
365
545
  renderList();
546
+ syncStyles();
547
+ renderViews();
366
548
  requestAnimationFrame(() => input.focus());
367
- } else if (panel.isConnected) {
368
- panel.remove();
549
+ } else {
550
+ unmountViews();
551
+ if (panel.isConnected) panel.remove();
369
552
  }
370
553
  renderShell();
371
554
  }
@@ -412,11 +595,14 @@ function createDevDock(initial = {}) {
412
595
  if (mounted) return;
413
596
  (opts.container ?? document.body).appendChild(host);
414
597
  addListeners();
598
+ syncStyles();
599
+ watchStyles();
415
600
  mounted = true;
416
601
  };
417
602
  const unmount = () => {
418
603
  if (!mounted) return;
419
604
  removeListeners();
605
+ unwatchStyles();
420
606
  host.remove();
421
607
  mounted = false;
422
608
  };
@@ -428,7 +614,10 @@ function createDevDock(initial = {}) {
428
614
  if (enabled()) {
429
615
  mount();
430
616
  renderShell();
431
- if (open) renderList();
617
+ if (open) {
618
+ renderList();
619
+ renderViews();
620
+ }
432
621
  } else {
433
622
  setOpen(false);
434
623
  unmount();
@@ -440,24 +629,56 @@ function createDevDock(initial = {}) {
440
629
  }
441
630
  };
442
631
  }
443
-
444
- // src/react/DevDock.tsx
445
- function DevDock(props) {
632
+ var NO_VIEWS = [];
633
+ function DevDock({ views = NO_VIEWS, ...options }) {
446
634
  const instance = react.useRef(null);
447
- const latest = react.useRef(props);
448
- 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 };
449
662
  react.useEffect(() => {
450
- const inst = createDevDock(latest.current);
663
+ alive.current = true;
664
+ const inst = createDevDock({
665
+ ...latest.current.options,
666
+ views: latest.current.coreViews
667
+ });
451
668
  instance.current = inst;
452
669
  return () => {
670
+ alive.current = false;
453
671
  inst.destroy();
454
672
  instance.current = null;
455
673
  };
456
674
  }, []);
457
675
  react.useEffect(() => {
458
- instance.current?.update(props);
676
+ instance.current?.update({ ...options, views: coreViews });
459
677
  });
460
- 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
+ }) });
461
682
  }
462
683
 
463
684
  exports.DevDock = DevDock;