@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.
Files changed (39) hide show
  1. package/README.md +165 -15
  2. package/dist/{chunk-NXZNFF34.js → chunk-7Z3YWV5K.js} +343 -152
  3. package/dist/chunk-7Z3YWV5K.js.map +1 -0
  4. package/dist/{chunk-3MVFATRN.js → chunk-MKYQ666Z.js} +3 -3
  5. package/dist/{chunk-3MVFATRN.js.map → chunk-MKYQ666Z.js.map} +1 -1
  6. package/dist/{dock-P6l6pKjS.d.ts → dock-C8tR26RP.d.ts} +1 -1
  7. package/dist/{dock-d3HqT25k.d.cts → dock-Cxh5FIAv.d.cts} +1 -1
  8. package/dist/index.cjs +344 -149
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +20 -3
  11. package/dist/index.d.ts +20 -3
  12. package/dist/index.js +1 -1
  13. package/dist/{match-DDSJIRYW.d.cts → match-CFB-9KGi.d.cts} +1 -1
  14. package/dist/{match-DwGIF1Xt.d.ts → match-injUeExB.d.ts} +1 -1
  15. package/dist/react-router.cjs +339 -149
  16. package/dist/react-router.cjs.map +1 -1
  17. package/dist/react-router.d.cts +3 -2
  18. package/dist/react-router.d.ts +3 -2
  19. package/dist/react-router.js +2 -2
  20. package/dist/react.cjs +339 -149
  21. package/dist/react.cjs.map +1 -1
  22. package/dist/react.d.cts +3 -2
  23. package/dist/react.d.ts +3 -2
  24. package/dist/react.js +2 -2
  25. package/dist/svelte.cjs +342 -149
  26. package/dist/svelte.cjs.map +1 -1
  27. package/dist/svelte.d.cts +4 -3
  28. package/dist/svelte.d.ts +4 -3
  29. package/dist/svelte.js +2 -2
  30. package/dist/svelte.js.map +1 -1
  31. package/dist/sveltekit.cjs +342 -149
  32. package/dist/sveltekit.cjs.map +1 -1
  33. package/dist/sveltekit.d.cts +5 -4
  34. package/dist/sveltekit.d.ts +5 -4
  35. package/dist/sveltekit.js +2 -2
  36. package/dist/{types-B_SpF32A.d.cts → types-BuYSEeDa.d.cts} +113 -5
  37. package/dist/{types-B_SpF32A.d.ts → types-BuYSEeDa.d.ts} +113 -5
  38. package/package.json +4 -1
  39. package/dist/chunk-NXZNFF34.js.map +0 -1
package/dist/react.cjs CHANGED
@@ -2,26 +2,281 @@
2
2
 
3
3
  var react = require('react');
4
4
  var reactDom = require('react-dom');
5
+ var cn = require('cn');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
7
 
7
8
  // src/react/DevDock.tsx
8
9
 
9
10
  // src/core/hotkey.ts
11
+ var isMac = () => typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
12
+ var KEY_CODES = {
13
+ ".": "Period",
14
+ ",": "Comma",
15
+ "/": "Slash",
16
+ "\\": "Backslash",
17
+ ";": "Semicolon",
18
+ "'": "Quote",
19
+ "[": "BracketLeft",
20
+ "]": "BracketRight",
21
+ "-": "Minus",
22
+ "=": "Equal",
23
+ "`": "Backquote",
24
+ " ": "Space",
25
+ space: "Space"
26
+ };
27
+ var codeFor = (key) => {
28
+ if (/^[a-z]$/.test(key)) return `Key${key.toUpperCase()}`;
29
+ if (/^[0-9]$/.test(key)) return `Digit${key}`;
30
+ return KEY_CODES[key];
31
+ };
10
32
  function matchHotkey(spec, e) {
11
33
  const parts = spec.toLowerCase().split("+").map((p) => p.trim()).filter(Boolean);
12
34
  if (parts.length === 0) return false;
13
35
  const key = parts[parts.length - 1];
14
36
  const mods = new Set(parts.slice(0, -1));
15
- const isMac = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
16
- const wantMeta = mods.has("meta") || mods.has("cmd") || mods.has("mod") && isMac;
17
- const wantCtrl = mods.has("ctrl") || mods.has("control") || mods.has("mod") && !isMac;
37
+ const mac = isMac();
38
+ const wantMeta = mods.has("meta") || mods.has("cmd") || mods.has("mod") && mac;
39
+ const wantCtrl = mods.has("ctrl") || mods.has("control") || mods.has("mod") && !mac;
18
40
  const wantShift = mods.has("shift");
19
41
  const wantAlt = mods.has("alt") || mods.has("option");
20
42
  if (e.metaKey !== wantMeta) return false;
21
43
  if (e.ctrlKey !== wantCtrl) return false;
22
44
  if (e.shiftKey !== wantShift) return false;
23
45
  if (e.altKey !== wantAlt) return false;
24
- return e.key.toLowerCase() === key;
46
+ if (e.key.toLowerCase() === key) return true;
47
+ return wantAlt && e.code === codeFor(key);
48
+ }
49
+ var DEFAULT_HOTKEY = "alt+shift+d";
50
+ var MAC_SYMBOL = {
51
+ meta: "\u2318",
52
+ cmd: "\u2318",
53
+ ctrl: "\u2303",
54
+ control: "\u2303",
55
+ shift: "\u21E7",
56
+ alt: "\u2325",
57
+ option: "\u2325"
58
+ };
59
+ var PC_NAME = {
60
+ meta: "Win",
61
+ cmd: "Win",
62
+ ctrl: "Ctrl",
63
+ control: "Ctrl",
64
+ shift: "Shift",
65
+ alt: "Alt",
66
+ option: "Alt"
67
+ };
68
+ function formatHotkey(spec) {
69
+ const parts = spec.toLowerCase().split("+").map((p) => p.trim()).filter(Boolean);
70
+ if (parts.length === 0) return "";
71
+ const key = parts[parts.length - 1];
72
+ const mods = parts.slice(0, -1);
73
+ const mac = isMac();
74
+ const label = key.length === 1 ? key.toUpperCase() : key;
75
+ const shown = mods.map(
76
+ (m) => mac ? MAC_SYMBOL[m] ?? (m === "mod" ? "\u2318" : m) : PC_NAME[m] ?? (m === "mod" ? "Ctrl" : m)
77
+ );
78
+ return mac ? [...shown, label].join("") : [...shown, label].join("+");
79
+ }
80
+
81
+ // src/core/theme.ts
82
+ var partClass = {
83
+ root: "devdock-root",
84
+ button: "devdock-button",
85
+ dot: "devdock-dot",
86
+ buttonLabel: "devdock-button-label",
87
+ panel: "devdock-panel",
88
+ title: "devdock-title",
89
+ hotkeyHint: "devdock-hotkey-hint",
90
+ inputWrap: "devdock-input-wrap",
91
+ input: "devdock-input",
92
+ body: "devdock-body",
93
+ list: "devdock-list",
94
+ section: "devdock-section",
95
+ group: "devdock-group",
96
+ row: "devdock-row",
97
+ rowActive: "",
98
+ rowLabel: "devdock-row-label",
99
+ rowPath: "devdock-row-path",
100
+ check: "devdock-check",
101
+ empty: "devdock-empty",
102
+ viewLabel: "devdock-view-label",
103
+ view: "devdock-view"
104
+ };
105
+ var STYLE_MARKER = "data-devdock-styles";
106
+ var FONT = "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif";
107
+ var BASE_CSS = `
108
+ .devdock-root { font-family: var(--devdock-font, ${FONT}); }
109
+
110
+ .devdock-button {
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: 6px;
114
+ padding: 8px 12px;
115
+ font-size: 12px;
116
+ font-weight: 700;
117
+ letter-spacing: 0.6px;
118
+ color: var(--devdock-button-fg, #fff);
119
+ background: var(--devdock-button-bg, #111317);
120
+ border: 1px solid var(--devdock-border, #2a2e37);
121
+ border-radius: 999px;
122
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
123
+ cursor: pointer;
124
+ }
125
+ .devdock-button:where([aria-expanded="true"]) {
126
+ background: var(--devdock-accent, #2563eb);
127
+ }
128
+
129
+ .devdock-dot {
130
+ width: 8px;
131
+ height: 8px;
132
+ border-radius: 999px;
133
+ background: var(--devdock-success, #22c55e);
134
+ display: inline-block;
135
+ }
136
+
137
+ .devdock-panel {
138
+ width: var(--devdock-width, 280px);
139
+ max-height: var(--devdock-max-height, 70vh);
140
+ display: flex;
141
+ flex-direction: column;
142
+ background: var(--devdock-bg, #16181d);
143
+ color: var(--devdock-fg, #e7e9ee);
144
+ border: 1px solid var(--devdock-border, #2a2e37);
145
+ border-radius: var(--devdock-radius, 12px);
146
+ box-shadow: var(--devdock-shadow, 0 12px 40px rgba(0, 0, 0, 0.45));
147
+ overflow: hidden;
148
+ }
149
+
150
+ .devdock-title {
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: space-between;
154
+ gap: 8px;
155
+ padding: 10px 12px;
156
+ border-bottom: 1px solid var(--devdock-border, #2a2e37);
157
+ font-size: 12px;
158
+ font-weight: 600;
159
+ letter-spacing: 0.4px;
160
+ text-transform: uppercase;
161
+ color: var(--devdock-muted, #8b909c);
162
+ }
163
+
164
+ .devdock-hotkey-hint {
165
+ flex-shrink: 0;
166
+ padding: 1px 5px;
167
+ font-family: inherit;
168
+ font-size: 10px;
169
+ font-weight: 600;
170
+ /* The heading's tracking and casing are for words, not for a key cap. */
171
+ letter-spacing: 0;
172
+ text-transform: none;
173
+ color: var(--devdock-subtle, #6b7280);
174
+ border: 1px solid var(--devdock-border, #2a2e37);
175
+ border-radius: var(--devdock-radius-xs, 4px);
176
+ }
177
+
178
+ .devdock-input-wrap { padding: 8px; }
179
+
180
+ .devdock-input {
181
+ width: 100%;
182
+ box-sizing: border-box;
183
+ padding: 8px 10px;
184
+ font-size: 13px;
185
+ color: var(--devdock-fg, #e7e9ee);
186
+ background: var(--devdock-input-bg, #0f1115);
187
+ border: 1px solid var(--devdock-border, #2a2e37);
188
+ border-radius: var(--devdock-radius-sm, 8px);
189
+ outline: none;
190
+ }
191
+
192
+ .devdock-body {
193
+ overflow-y: auto;
194
+ display: flex;
195
+ flex-direction: column;
196
+ }
197
+
198
+ .devdock-list { padding: 0 6px 8px; }
199
+
200
+ .devdock-section { margin-top: 6px; }
201
+
202
+ .devdock-group {
203
+ padding: 6px 8px 2px;
204
+ font-size: 10px;
205
+ font-weight: 700;
206
+ letter-spacing: 0.6px;
207
+ text-transform: uppercase;
208
+ color: var(--devdock-subtle, #6b7280);
209
+ }
210
+
211
+ .devdock-row {
212
+ display: flex;
213
+ align-items: center;
214
+ justify-content: space-between;
215
+ gap: 8px;
216
+ width: 100%;
217
+ text-align: left;
218
+ padding: 8px 10px;
219
+ font-size: 13px;
220
+ color: var(--devdock-fg, #e7e9ee);
221
+ background: transparent;
222
+ border: none;
223
+ border-radius: var(--devdock-radius-sm, 8px);
224
+ cursor: pointer;
225
+ }
226
+ .devdock-row:where([data-active]) {
227
+ background: var(--devdock-accent, #2563eb);
228
+ color: var(--devdock-accent-fg, #e7e9ee);
229
+ }
230
+
231
+ .devdock-row-label {
232
+ overflow: hidden;
233
+ text-overflow: ellipsis;
234
+ white-space: nowrap;
235
+ }
236
+
237
+ .devdock-row-path {
238
+ font-size: 11px;
239
+ color: var(--devdock-subtle, #6b7280);
240
+ font-family: ui-monospace, SFMono-Regular, monospace;
241
+ flex-shrink: 0;
242
+ }
243
+ :where(.devdock-row[data-active]) .devdock-row-path {
244
+ /* Whatever colour the row ended up with, stepped back: currentColor in the
245
+ color property means the inherited one, so this tracks the row however it
246
+ was set - a token, a class of yours - instead of guessing at the accent. */
247
+ color: color-mix(in oklab, currentColor 75%, transparent);
248
+ }
249
+
250
+ .devdock-check {
251
+ flex-shrink: 0;
252
+ width: 12px;
253
+ text-align: center;
254
+ color: var(--devdock-success, #22c55e);
255
+ }
256
+
257
+ .devdock-empty {
258
+ padding: 12px;
259
+ font-size: 13px;
260
+ color: var(--devdock-subtle, #6b7280);
261
+ }
262
+
263
+ .devdock-view-label {
264
+ padding: 4px 8px 0;
265
+ font-size: 11px;
266
+ color: var(--devdock-muted, #8b909c);
267
+ }
268
+
269
+ .devdock-view { padding: 2px 8px 6px; }
270
+ `;
271
+ var DEFAULT_LAYER = "devdock";
272
+ function dockStyles(layer) {
273
+ if (!layer) return BASE_CSS;
274
+ const block = `@layer ${layer} {${BASE_CSS}}
275
+ `;
276
+ if (layer !== DEFAULT_LAYER) return block;
277
+ const order = `@layer theme, base, components, ${layer}, utilities;`;
278
+ return `${order}
279
+ ${block}`;
25
280
  }
26
281
 
27
282
  // src/core/dock.ts
@@ -54,33 +309,12 @@ var isDevHost = () => {
54
309
  if (!host) return true;
55
310
  return host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host === "::1" || host === "0.0.0.0" || isLocalIPv4(host);
56
311
  };
57
- function el(tag, styles, props) {
312
+ function el(tag, props) {
58
313
  const node = document.createElement(tag);
59
- if (styles) Object.assign(node.style, styles);
60
314
  if (props) Object.assign(node, props);
61
315
  return node;
62
316
  }
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
- }
317
+ var PARTS = Object.keys(partClass);
84
318
  function cornerStyle(position) {
85
319
  const gap = "16px";
86
320
  switch (position) {
@@ -103,7 +337,6 @@ function panelAnchor(position) {
103
337
  [isRight ? "right" : "left"]: "0"
104
338
  };
105
339
  }
106
- var FONT = "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif";
107
340
  function keyViews(views) {
108
341
  const used = /* @__PURE__ */ new Set();
109
342
  return views.map((view, i) => {
@@ -120,96 +353,72 @@ function createDevDock(initial = {}) {
120
353
  } };
121
354
  }
122
355
  let opts = { ...initial };
356
+ const cls = (...parts) => cn.cn(parts.map((part) => [partClass[part], opts.classNames?.[part]]));
357
+ const classSig = () => PARTS.map((p) => cn.cn(opts.classNames?.[p])).join("\0");
123
358
  let open = false;
124
359
  let query = "";
125
360
  let active = 0;
126
361
  let mounted = false;
127
362
  const host = el("div");
128
363
  host.setAttribute("data-devdock", "");
129
- const shadowMode = initial.shadow ?? true;
364
+ const shadowMode = initial.shadow ?? (initial.classNames ? "inherit" : true);
130
365
  const useShadow = shadowMode !== false && typeof host.attachShadow === "function";
131
366
  const shadowRoot = useShadow ? host.attachShadow({ mode: "open" }) : null;
132
367
  const mountPoint = shadowRoot ?? host;
133
- const styleSlot = shadowRoot && shadowMode === "inherit" ? el("div", { display: "contents" }) : null;
134
- if (styleSlot) mountPoint.appendChild(styleSlot);
135
- const root = el("div", { fontFamily: FONT });
136
- mountPoint.appendChild(root);
137
- const panel = el("div", {
138
- width: "280px",
139
- maxHeight: "min(70vh, 480px)",
140
- display: "flex",
141
- flexDirection: "column",
142
- background: "#16181d",
143
- color: "#e7e9ee",
144
- border: "1px solid #2a2e37",
145
- borderRadius: "12px",
146
- boxShadow: "0 12px 40px rgba(0,0,0,0.45)",
147
- overflow: "hidden"
368
+ const baseStyle = el("style", {
369
+ textContent: dockStyles(initial.cssLayer ?? DEFAULT_LAYER)
148
370
  });
371
+ baseStyle.setAttribute(STYLE_MARKER, "");
372
+ if (shadowRoot) mountPoint.appendChild(baseStyle);
373
+ const styleSlot = shadowRoot && shadowMode === "inherit" ? el("div") : null;
374
+ if (styleSlot) {
375
+ styleSlot.style.display = "contents";
376
+ mountPoint.appendChild(styleSlot);
377
+ }
378
+ const root = el("div");
379
+ mountPoint.appendChild(root);
380
+ const panel = el("div");
149
381
  panel.setAttribute("role", "dialog");
150
- const heading = el("div", {
151
- padding: "10px 12px",
152
- borderBottom: "1px solid #2a2e37",
153
- fontSize: "12px",
154
- fontWeight: "600",
155
- letterSpacing: "0.4px",
156
- textTransform: "uppercase",
157
- color: "#8b909c"
158
- });
159
- const inputWrap = el("div", { padding: "8px" });
160
- const input = el("input", {
161
- width: "100%",
162
- boxSizing: "border-box",
163
- padding: "8px 10px",
164
- fontSize: "13px",
165
- color: "#e7e9ee",
166
- background: "#0f1115",
167
- border: "1px solid #2a2e37",
168
- borderRadius: "8px",
169
- outline: "none"
170
- });
382
+ const heading = el("div");
383
+ const headingText = el("span");
384
+ const hotkeyHint = el("kbd");
385
+ heading.append(headingText, hotkeyHint);
386
+ const inputWrap = el("div");
387
+ const input = el("input");
171
388
  input.placeholder = "Filter\u2026";
172
389
  inputWrap.appendChild(input);
173
- const list = el("div", { padding: "0 6px 8px" });
390
+ const list = el("div");
174
391
  list.setAttribute("data-devdock-list", "");
175
392
  const viewsBefore = el("div");
176
393
  viewsBefore.setAttribute("data-devdock-views", "before");
177
394
  const viewsAfter = el("div");
178
395
  viewsAfter.setAttribute("data-devdock-views", "after");
179
- const body = el("div", {
180
- overflowY: "auto",
181
- display: "flex",
182
- flexDirection: "column"
183
- });
396
+ const body = el("div");
184
397
  body.append(viewsBefore, list, viewsAfter);
185
398
  panel.append(heading, inputWrap, body);
186
- const button = el("button", {
187
- display: "inline-flex",
188
- alignItems: "center",
189
- gap: "6px",
190
- padding: "8px 12px",
191
- fontSize: "12px",
192
- fontWeight: "700",
193
- letterSpacing: "0.6px",
194
- color: "#fff",
195
- background: "#111317",
196
- border: "1px solid #2a2e37",
197
- borderRadius: "999px",
198
- boxShadow: "0 4px 14px rgba(0,0,0,0.35)",
199
- cursor: "pointer"
200
- });
399
+ const button = el("button");
201
400
  button.type = "button";
202
401
  button.setAttribute("aria-label", "Toggle dev menu");
203
- const dot = el("span", {
204
- width: "8px",
205
- height: "8px",
206
- borderRadius: "999px",
207
- background: "#22c55e",
208
- display: "inline-block"
209
- });
402
+ const dot = el("span");
210
403
  const buttonLabel = el("span");
211
404
  button.append(dot, buttonLabel);
212
405
  root.appendChild(button);
406
+ const sectionHeading = (text) => el("div", { className: cls("group"), textContent: text });
407
+ const viewLabel = (text) => el("div", { className: cls("viewLabel"), textContent: text });
408
+ const paintClasses = () => {
409
+ root.className = cls("root");
410
+ button.className = cls("button");
411
+ dot.className = cls("dot");
412
+ buttonLabel.className = cls("buttonLabel");
413
+ panel.className = cls("panel");
414
+ heading.className = cls("title");
415
+ hotkeyHint.className = cls("hotkeyHint");
416
+ inputWrap.className = cls("inputWrap");
417
+ input.className = cls("input");
418
+ body.className = cls("body");
419
+ list.className = cls("list");
420
+ };
421
+ const hotkey = () => opts.hotkey === void 0 ? DEFAULT_HOTKEY : opts.hotkey;
213
422
  const enabled = () => {
214
423
  const e = opts.enabled;
215
424
  if (typeof e === "function") return e();
@@ -293,9 +502,9 @@ function createDevDock(initial = {}) {
293
502
  const paintActive = () => {
294
503
  rows.forEach((row, idx) => {
295
504
  const isActive = idx === active;
296
- row.node.style.background = isActive ? "#2563eb" : "transparent";
297
- if (row.pathSpan)
298
- row.pathSpan.style.color = isActive ? "#cfe0ff" : "#6b7280";
505
+ if (isActive) row.node.setAttribute("data-active", "");
506
+ else row.node.removeAttribute("data-active");
507
+ row.node.className = isActive ? cls("row", "rowActive") : cls("row");
299
508
  });
300
509
  };
301
510
  const setActive = (next, scroll = false) => {
@@ -307,7 +516,7 @@ function createDevDock(initial = {}) {
307
516
  const renderList = () => {
308
517
  const filtered = getFiltered();
309
518
  active = Math.min(active, Math.max(0, filtered.length - 1));
310
- const sig = filtered.map(itemSig).join("\n");
519
+ const sig = [classSig(), ...filtered.map(itemSig)].join("\n");
311
520
  if (sig === rowsSig) {
312
521
  filtered.forEach((it, idx) => {
313
522
  const row = rows[idx];
@@ -323,11 +532,7 @@ function createDevDock(initial = {}) {
323
532
  list.replaceChildren();
324
533
  if (filtered.length === 0) {
325
534
  list.appendChild(
326
- el(
327
- "div",
328
- { padding: "12px", fontSize: "13px", color: "#6b7280" },
329
- { textContent: "Nothing here." }
330
- )
535
+ el("div", { className: cls("empty"), textContent: "Nothing here." })
331
536
  );
332
537
  return;
333
538
  }
@@ -338,55 +543,26 @@ function createDevDock(initial = {}) {
338
543
  groups.set(it.group, arr);
339
544
  }
340
545
  for (const [groupName, groupItems] of groups) {
341
- const section = el("div", { marginTop: "6px" });
546
+ const section = el("div", { className: cls("section") });
342
547
  section.appendChild(sectionHeading(groupName));
343
548
  for (const it of groupItems) {
344
549
  const idx = filtered.indexOf(it);
345
- const row = el("button", {
346
- display: "flex",
347
- alignItems: "center",
348
- justifyContent: "space-between",
349
- gap: "8px",
350
- width: "100%",
351
- textAlign: "left",
352
- padding: "8px 10px",
353
- fontSize: "13px",
354
- color: "#e7e9ee",
355
- background: "transparent",
356
- border: "none",
357
- borderRadius: "8px",
358
- cursor: "pointer"
359
- });
550
+ const row = el("button", { className: cls("row") });
360
551
  row.type = "button";
361
- const labelSpan = el("span", {
362
- overflow: "hidden",
363
- textOverflow: "ellipsis",
364
- whiteSpace: "nowrap"
365
- });
552
+ const labelSpan = el("span", { className: cls("rowLabel") });
366
553
  row.appendChild(labelSpan);
367
554
  let pathSpan = null;
368
555
  if (it.kind === "route" && it.route.path !== it.label) {
369
- pathSpan = el(
370
- "span",
371
- {
372
- fontSize: "11px",
373
- color: "#6b7280",
374
- fontFamily: "ui-monospace, SFMono-Regular, monospace",
375
- flexShrink: "0"
376
- },
377
- { textContent: it.route.path }
378
- );
556
+ pathSpan = el("span", {
557
+ className: cls("rowPath"),
558
+ textContent: it.route.path
559
+ });
379
560
  row.appendChild(pathSpan);
380
561
  }
381
562
  let checkSpan = null;
382
563
  if (it.kind === "command" && it.checked !== null) {
383
564
  row.setAttribute("role", "checkbox");
384
- checkSpan = el("span", {
385
- flexShrink: "0",
386
- width: "12px",
387
- textAlign: "center",
388
- color: "#22c55e"
389
- });
565
+ checkSpan = el("span", { className: cls("check") });
390
566
  row.appendChild(checkSpan);
391
567
  }
392
568
  const rec = { item: it, node: row, labelSpan, pathSpan, checkSpan };
@@ -415,11 +591,14 @@ function createDevDock(initial = {}) {
415
591
  };
416
592
  const renderViews = () => {
417
593
  const keyed = keyViews(opts.views ?? []);
418
- const sig = keyed.map(
419
- ({ view, key }) => [key, viewOrder(view), view.group ?? "", view.label ?? ""].join(
420
- "\0"
594
+ const sig = [
595
+ classSig(),
596
+ ...keyed.map(
597
+ ({ view, key }) => [key, viewOrder(view), view.group ?? "", view.label ?? ""].join(
598
+ "\0"
599
+ )
421
600
  )
422
- ).join("\n");
601
+ ].join("\n");
423
602
  if (sig === viewsSig) return;
424
603
  viewsSig = sig;
425
604
  const live = new Set(keyed.map(({ key }) => key));
@@ -447,7 +626,7 @@ function createDevDock(initial = {}) {
447
626
  });
448
627
  }
449
628
  for (const section of sections.values()) {
450
- const wrap = el("div", { marginTop: "6px" });
629
+ const wrap = el("div", { className: cls("section") });
451
630
  if (section.heading) wrap.appendChild(sectionHeading(section.heading));
452
631
  for (const { view, key } of section.items) {
453
632
  if (view.group != null && view.label)
@@ -457,7 +636,7 @@ function createDevDock(initial = {}) {
457
636
  wrap.appendChild(existing.host);
458
637
  continue;
459
638
  }
460
- const viewHost = el("div", { padding: "2px 8px 6px" });
639
+ const viewHost = el("div", { className: cls("view") });
461
640
  viewHost.setAttribute("data-devdock-view", view.id ?? "");
462
641
  wrap.appendChild(viewHost);
463
642
  pending.push({ view, key, host: viewHost });
@@ -484,7 +663,9 @@ function createDevDock(initial = {}) {
484
663
  }
485
664
  }
486
665
  const sheets = [
487
- ...document.querySelectorAll('style, link[rel~="stylesheet" i]')
666
+ ...document.querySelectorAll(
667
+ `style:not([${STYLE_MARKER}]), link[rel~="stylesheet" i]`
668
+ )
488
669
  ];
489
670
  const sig = sheets.map((n) => `${n.tagName}\0${n.getAttribute("href") ?? n.textContent}`).join("\n");
490
671
  if (sig === clonesSig) return;
@@ -526,12 +707,17 @@ function createDevDock(initial = {}) {
526
707
  right: ""
527
708
  });
528
709
  Object.assign(root.style, cornerStyle(position));
529
- heading.textContent = opts.title ?? "Dev Menu";
710
+ headingText.textContent = opts.title ?? "Dev Menu";
530
711
  panel.setAttribute("aria-label", opts.title ?? "Dev Menu");
531
712
  Object.assign(panel.style, panelAnchor(position));
532
713
  buttonLabel.textContent = opts.label ?? "DEV";
533
- button.style.background = open ? "#2563eb" : "#111317";
714
+ const spec = hotkey();
715
+ const chord = spec ? formatHotkey(spec) : "";
716
+ hotkeyHint.textContent = chord;
717
+ hotkeyHint.hidden = !chord;
718
+ button.title = chord ? `${opts.title ?? "Dev Menu"} (${chord})` : opts.title ?? "Dev Menu";
534
719
  button.setAttribute("aria-expanded", String(open));
720
+ paintClasses();
535
721
  };
536
722
  function setOpen(next) {
537
723
  if (open === next) return;
@@ -578,7 +764,8 @@ function createDevDock(initial = {}) {
578
764
  if (open && !e.composedPath().includes(root)) setOpen(false);
579
765
  };
580
766
  const onKeyDown = (e) => {
581
- if (opts.hotkey && matchHotkey(opts.hotkey, e)) {
767
+ const spec = hotkey();
768
+ if (spec && matchHotkey(spec, e)) {
582
769
  e.preventDefault();
583
770
  setOpen(!open);
584
771
  }
@@ -593,6 +780,8 @@ function createDevDock(initial = {}) {
593
780
  };
594
781
  const mount = () => {
595
782
  if (mounted) return;
783
+ if (!shadowRoot)
784
+ (document.head ?? document.documentElement).prepend(baseStyle);
596
785
  (opts.container ?? document.body).appendChild(host);
597
786
  addListeners();
598
787
  syncStyles();
@@ -603,6 +792,7 @@ function createDevDock(initial = {}) {
603
792
  if (!mounted) return;
604
793
  removeListeners();
605
794
  unwatchStyles();
795
+ if (!shadowRoot) baseStyle.remove();
606
796
  host.remove();
607
797
  mounted = false;
608
798
  };