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