@hozu/devtools 0.16.0 → 0.17.1
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.
- package/dist/overlay/app.js +272 -32
- package/dist/overlay/app.js.map +1 -1
- package/dist/overlay/assets.d.ts +44 -0
- package/dist/overlay/assets.d.ts.map +1 -0
- package/dist/overlay/assets.js +303 -0
- package/dist/overlay/assets.js.map +1 -0
- package/dist/overlay/look.d.ts +3 -1
- package/dist/overlay/look.d.ts.map +1 -1
- package/dist/overlay/look.js +124 -35
- package/dist/overlay/look.js.map +1 -1
- package/dist/overlay/measure.d.ts +18 -0
- package/dist/overlay/measure.d.ts.map +1 -0
- package/dist/overlay/measure.js +33 -0
- package/dist/overlay/measure.js.map +1 -0
- package/dist/overlay/notes.js +1 -1
- package/dist/overlay/notes.js.map +1 -1
- package/dist/overlay/style.d.ts +2 -2
- package/dist/overlay/style.d.ts.map +1 -1
- package/dist/overlay/style.js +89 -0
- package/dist/overlay/style.js.map +1 -1
- package/dist/plain.d.ts.map +1 -1
- package/dist/plain.js +6 -5
- package/dist/plain.js.map +1 -1
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +17 -3
- package/dist/prompt.js.map +1 -1
- package/dist/theme.d.ts +5 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +102 -3
- package/dist/theme.js.map +1 -1
- package/package.json +2 -2
package/dist/overlay/app.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { describeFor, friendlyName, questionFor } from '../plain.js';
|
|
2
2
|
import { joinRequests, labelOf, openRequestsLine, requestMarkdown, scopesFor, } from '../prompt.js';
|
|
3
3
|
import { held, hold, node, one, page, remove, save, saved, theme, tree } from './api.js';
|
|
4
|
+
import { assetsBoard } from './assets.js';
|
|
4
5
|
import { h, read, write } from './dom.js';
|
|
5
6
|
import { drawer } from './effects.js';
|
|
6
7
|
import { previewLabel, renderLayers } from './layers.js';
|
|
7
8
|
import { logo } from './logo.js';
|
|
8
9
|
import { lookSection, preview } from './look.js';
|
|
10
|
+
import { distances, sizeOf } from './measure.js';
|
|
9
11
|
import { agentNotes } from './notes.js';
|
|
10
12
|
import { css, outlineCss } from './style.js';
|
|
11
13
|
import { firstText, previewText, restoreText, textSection } from './text.js';
|
|
@@ -31,6 +33,9 @@ const state = {
|
|
|
31
33
|
};
|
|
32
34
|
const persist = () => write(key, state);
|
|
33
35
|
const host = document.createElement('hozu-devtools');
|
|
36
|
+
host.setAttribute('data-lenis-prevent', '');
|
|
37
|
+
for (const type of ['wheel', 'touchstart', 'touchmove', 'keydown'])
|
|
38
|
+
host.addEventListener(type, (event) => event.stopPropagation(), { passive: true });
|
|
34
39
|
const shadow = host.attachShadow({ mode: 'open' });
|
|
35
40
|
const root = h('div', { class: 'root' });
|
|
36
41
|
const hover = h('div', { class: 'box', hidden: true }, [h('div', { class: 'tag' })]);
|
|
@@ -59,7 +64,119 @@ const api = drawer({
|
|
|
59
64
|
},
|
|
60
65
|
closed: () => { },
|
|
61
66
|
});
|
|
62
|
-
|
|
67
|
+
const screenCookie = 'hozu-dev-preview';
|
|
68
|
+
const screenOf = () => {
|
|
69
|
+
const raw = new RegExp(`(?:^|;\\s*)${screenCookie}=([^;]+)`).exec(document.cookie)?.[1];
|
|
70
|
+
try {
|
|
71
|
+
return raw ? decodeURIComponent(raw) : null;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const setScreen = (value) => {
|
|
78
|
+
document.cookie = value
|
|
79
|
+
? `${screenCookie}=${encodeURIComponent(value)}; path=/; SameSite=Lax`
|
|
80
|
+
: `${screenCookie}=; path=/; max-age=0; SameSite=Lax`;
|
|
81
|
+
};
|
|
82
|
+
const showKey = 'hozu-devtools:assets-show';
|
|
83
|
+
function showInstances(c) {
|
|
84
|
+
const here = c.uses.flatMap((u) => [...doc.querySelectorAll(`[data-hz="${CSS.escape(u.node)}"]`)]);
|
|
85
|
+
if (!here.length) {
|
|
86
|
+
const page = c.uses.flatMap((u) => u.pages).find((p) => !p.params);
|
|
87
|
+
if (!page)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
sessionStorage.setItem(showKey, c.id);
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
93
|
+
win.location.href = page.path;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
assets.close();
|
|
97
|
+
state.mode = 'select';
|
|
98
|
+
state.picks = [
|
|
99
|
+
...state.picks.filter(hasContent),
|
|
100
|
+
...here.map((el) => ({
|
|
101
|
+
id: el.getAttribute('data-hz') ?? '',
|
|
102
|
+
index: indexOf(el),
|
|
103
|
+
note: '',
|
|
104
|
+
scope: 'component',
|
|
105
|
+
visible: visibleOf(el),
|
|
106
|
+
})),
|
|
107
|
+
];
|
|
108
|
+
state.active = state.picks.length - 1;
|
|
109
|
+
state.panel = 'inspector';
|
|
110
|
+
persist();
|
|
111
|
+
renderDock();
|
|
112
|
+
drawPicks();
|
|
113
|
+
void renderPanel();
|
|
114
|
+
}
|
|
115
|
+
const assets = assetsBoard({
|
|
116
|
+
plain: state.audience === 'builder',
|
|
117
|
+
theme: () => theme(),
|
|
118
|
+
stylesheets: () => [...doc.querySelectorAll('link[rel="stylesheet"]')].map((l) => l.href).filter(Boolean),
|
|
119
|
+
show: (c) => showInstances(c),
|
|
120
|
+
change: (c, note) => {
|
|
121
|
+
const first = c.uses[0];
|
|
122
|
+
if (!first)
|
|
123
|
+
return;
|
|
124
|
+
state.picks.push({ id: first.node, index: 0, note, scope: 'component', visible: '' });
|
|
125
|
+
state.active = state.picks.length - 1;
|
|
126
|
+
persist();
|
|
127
|
+
assets.close();
|
|
128
|
+
renderDock();
|
|
129
|
+
open('changes', null, 'draft');
|
|
130
|
+
},
|
|
131
|
+
screen: (route, path, name) => {
|
|
132
|
+
setScreen(`${route}:${name}`);
|
|
133
|
+
if (win.location.pathname === path)
|
|
134
|
+
win.location.reload();
|
|
135
|
+
else
|
|
136
|
+
win.location.href = path;
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
async function screensSection() {
|
|
140
|
+
const res = await fetch(`/_hozu/dev/previews?path=${encodeURIComponent(win.location.pathname)}`, {
|
|
141
|
+
cache: 'no-store',
|
|
142
|
+
}).catch(() => null);
|
|
143
|
+
const out = res?.ok
|
|
144
|
+
? (await res.json())
|
|
145
|
+
: null;
|
|
146
|
+
if (!out?.route || !out.previews.length)
|
|
147
|
+
return null;
|
|
148
|
+
return h('div', { class: 'sec' }, [
|
|
149
|
+
h('div', { class: 'label' }, ['Previews (previews.ts)']),
|
|
150
|
+
...out.previews.map((p) => {
|
|
151
|
+
const value = `${out.route}:${p.name}`;
|
|
152
|
+
const on = out.current === value;
|
|
153
|
+
return h('div', { class: 'look' }, [
|
|
154
|
+
h('span', { class: 'what' }, [p.name]),
|
|
155
|
+
h('button', {
|
|
156
|
+
class: on ? 'link done' : 'link',
|
|
157
|
+
type: 'button',
|
|
158
|
+
onclick: () => {
|
|
159
|
+
setScreen(on ? null : value);
|
|
160
|
+
win.location.reload();
|
|
161
|
+
},
|
|
162
|
+
}, [on ? 'Exit' : 'Preview']),
|
|
163
|
+
]);
|
|
164
|
+
}),
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
167
|
+
root.append(bench, panel, dock, api.el, assets.board);
|
|
168
|
+
try {
|
|
169
|
+
const pending = sessionStorage.getItem(showKey);
|
|
170
|
+
if (pending) {
|
|
171
|
+
sessionStorage.removeItem(showKey);
|
|
172
|
+
void assets.catalog().then((list) => {
|
|
173
|
+
const c = list?.find((x) => x.id === pending);
|
|
174
|
+
if (c)
|
|
175
|
+
setTimeout(() => showInstances(c), 300);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch { }
|
|
63
180
|
document.documentElement.append(host);
|
|
64
181
|
const darkScheme = matchMedia('(prefers-color-scheme: dark)');
|
|
65
182
|
function applyTheme() {
|
|
@@ -72,6 +189,8 @@ let win = window;
|
|
|
72
189
|
let frameEl = null;
|
|
73
190
|
const frames = h('div');
|
|
74
191
|
let outline = null;
|
|
192
|
+
let measuring = false;
|
|
193
|
+
const measure = h('div', { class: 'measure', hidden: true });
|
|
75
194
|
const agent = agentNotes({
|
|
76
195
|
doc: () => doc,
|
|
77
196
|
win: () => win,
|
|
@@ -89,7 +208,7 @@ function mountOutline(target) {
|
|
|
89
208
|
outline?.remove();
|
|
90
209
|
const el = target.createElement('hozu-devtools-outline');
|
|
91
210
|
el.style.cssText = 'all:initial;position:fixed;inset:0;pointer-events:none;z-index:2147483646';
|
|
92
|
-
el.attachShadow({ mode: 'open' }).append(h('style', {}, [outlineCss]), frames, boxes, agent.layer, hover);
|
|
211
|
+
el.attachShadow({ mode: 'open' }).append(h('style', {}, [outlineCss]), frames, boxes, agent.layer, hover, measure);
|
|
93
212
|
target.documentElement.append(el);
|
|
94
213
|
outline = el;
|
|
95
214
|
}
|
|
@@ -131,6 +250,9 @@ function place(box, el) {
|
|
|
131
250
|
target.style.width = `${r.width + 4}px`;
|
|
132
251
|
target.style.height = `${r.height + 4}px`;
|
|
133
252
|
}
|
|
253
|
+
const size = box.querySelector(':scope > .size');
|
|
254
|
+
if (size)
|
|
255
|
+
size.textContent = sizeOf(r);
|
|
134
256
|
const tag = box.firstElementChild;
|
|
135
257
|
if (!tag?.classList.contains('tag'))
|
|
136
258
|
return;
|
|
@@ -175,6 +297,37 @@ async function showHover(el, force = false) {
|
|
|
175
297
|
if (hovered === el)
|
|
176
298
|
tag.replaceChildren(...tagText(n, el));
|
|
177
299
|
}
|
|
300
|
+
function drawMeasure() {
|
|
301
|
+
const active = state.picks[state.active];
|
|
302
|
+
const from = active ? elementOf(active) : hovered;
|
|
303
|
+
const to = active ? hovered : (hovered?.parentElement?.closest('[data-hz]') ?? null);
|
|
304
|
+
if (!measuring || state.mode !== 'select' || !from || !to || from === to) {
|
|
305
|
+
measure.hidden = true;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const a = from.getBoundingClientRect();
|
|
309
|
+
const b = to.getBoundingClientRect();
|
|
310
|
+
const px = (n) => `${n}px`;
|
|
311
|
+
const target = h('div', { class: 'm-target' });
|
|
312
|
+
Object.assign(target.style, { left: px(b.left), top: px(b.top), width: px(b.width), height: px(b.height) });
|
|
313
|
+
const parts = [target];
|
|
314
|
+
for (const l of distances(a, b)) {
|
|
315
|
+
const line = h('div', { class: `m-line ${l.axis}` });
|
|
316
|
+
const label = h('div', { class: 'm-label' }, [String(Math.round(l.to - l.from))]);
|
|
317
|
+
const mid = (l.from + l.to) / 2;
|
|
318
|
+
if (l.axis === 'h') {
|
|
319
|
+
Object.assign(line.style, { left: px(l.from), top: px(l.at), width: px(l.to - l.from) });
|
|
320
|
+
Object.assign(label.style, { left: px(mid), top: px(l.at) });
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
Object.assign(line.style, { left: px(l.at), top: px(l.from), height: px(l.to - l.from) });
|
|
324
|
+
Object.assign(label.style, { left: px(l.at), top: px(mid) });
|
|
325
|
+
}
|
|
326
|
+
parts.push(line, label);
|
|
327
|
+
}
|
|
328
|
+
measure.replaceChildren(...parts);
|
|
329
|
+
measure.hidden = false;
|
|
330
|
+
}
|
|
178
331
|
function drawPicks() {
|
|
179
332
|
for (const p of state.picks) {
|
|
180
333
|
preview(elementOf(p), p.style ?? []);
|
|
@@ -185,6 +338,7 @@ function drawPicks() {
|
|
|
185
338
|
boxes.replaceChildren(...state.picks.map((p, i) => {
|
|
186
339
|
const box = framed(h('div', { class: 'box selected' }, [
|
|
187
340
|
state.picks.length > 1 ? h('div', { class: 'badge' }, [String(i + 1)]) : null,
|
|
341
|
+
h('div', { class: 'size' }),
|
|
188
342
|
]), true);
|
|
189
343
|
place(box, state.mode === 'select' ? elementOf(p) : null);
|
|
190
344
|
return box;
|
|
@@ -203,6 +357,7 @@ function frame() {
|
|
|
203
357
|
}
|
|
204
358
|
if (hovered && (state.mode === 'select' || forced))
|
|
205
359
|
place(hover, hovered);
|
|
360
|
+
drawMeasure();
|
|
206
361
|
agent.frame();
|
|
207
362
|
requestAnimationFrame(frame);
|
|
208
363
|
}
|
|
@@ -272,12 +427,13 @@ function listen(target) {
|
|
|
272
427
|
return;
|
|
273
428
|
const mouse = event;
|
|
274
429
|
const clicked = markedFrom(event);
|
|
275
|
-
const el =
|
|
430
|
+
const el = clicked;
|
|
276
431
|
if (!el)
|
|
277
432
|
return;
|
|
278
433
|
select({ id: el.getAttribute('data-hz') ?? '', index: indexOf(el), visible: visibleOf(el) }, mouse.shiftKey);
|
|
279
434
|
}, { capture: true, signal });
|
|
280
435
|
target.addEventListener('pointermove', (event) => {
|
|
436
|
+
measuring = event.altKey;
|
|
281
437
|
if (state.mode !== 'select')
|
|
282
438
|
return;
|
|
283
439
|
const el = ours(event) ? null : markedFrom(event);
|
|
@@ -285,30 +441,60 @@ function listen(target) {
|
|
|
285
441
|
void showHover(el);
|
|
286
442
|
}, { capture: true, passive: true, signal });
|
|
287
443
|
target.addEventListener('keydown', onKey, { capture: true, signal });
|
|
444
|
+
target.addEventListener('keyup', (event) => {
|
|
445
|
+
measuring = event.altKey;
|
|
446
|
+
}, { capture: true, signal });
|
|
447
|
+
target.addEventListener('blur', () => {
|
|
448
|
+
measuring = false;
|
|
449
|
+
}, { signal });
|
|
288
450
|
return stop;
|
|
289
451
|
}
|
|
452
|
+
/** The marked parts directly inside `parent` (or the page), in document order. */
|
|
453
|
+
function partsIn(parent) {
|
|
454
|
+
const all = [...(parent ?? doc).querySelectorAll('[data-hz]')];
|
|
455
|
+
return all.filter((e) => (e.parentElement?.closest('[data-hz]') ?? null) === parent);
|
|
456
|
+
}
|
|
290
457
|
function walk(direction) {
|
|
291
458
|
const active = state.picks[state.active];
|
|
292
459
|
if (!active)
|
|
293
460
|
return;
|
|
294
461
|
const el = elementOf(active);
|
|
295
462
|
const own = el?.getAttribute('data-hz') === active.id;
|
|
463
|
+
const beside = () => {
|
|
464
|
+
if (!el || !own)
|
|
465
|
+
return null;
|
|
466
|
+
const list = partsIn(el.parentElement?.closest('[data-hz]') ?? null).filter((e) => {
|
|
467
|
+
const b = e.getBoundingClientRect();
|
|
468
|
+
return e === el || (b.width > 0 && b.height > 0);
|
|
469
|
+
});
|
|
470
|
+
const at = list.indexOf(el);
|
|
471
|
+
return list[(at + (direction === 'next' ? 1 : -1) + list.length) % list.length] ?? null;
|
|
472
|
+
};
|
|
296
473
|
const next = direction === 'up'
|
|
297
474
|
? own
|
|
298
475
|
? el?.parentElement?.closest('[data-hz]')
|
|
299
476
|
: el
|
|
300
|
-
:
|
|
301
|
-
?
|
|
302
|
-
|
|
303
|
-
|
|
477
|
+
: direction === 'down'
|
|
478
|
+
? own
|
|
479
|
+
? el?.querySelector('[data-hz]')
|
|
480
|
+
: null
|
|
481
|
+
: beside();
|
|
482
|
+
if (!next || ((direction === 'next' || direction === 'previous') && next === el))
|
|
304
483
|
return;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
484
|
+
const moved = { id: next.getAttribute('data-hz') ?? '', index: indexOf(next), visible: visibleOf(next) };
|
|
485
|
+
if (hasContent(active)) {
|
|
486
|
+
const existing = state.picks.findIndex((p) => p.id === moved.id && p.index === moved.index);
|
|
487
|
+
if (existing >= 0)
|
|
488
|
+
state.active = existing;
|
|
489
|
+
else {
|
|
490
|
+
state.picks.push({ ...moved, note: '', scope: null });
|
|
491
|
+
state.active = state.picks.length - 1;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
preview(el, []);
|
|
496
|
+
state.picks[state.active] = { ...moved, note: '', scope: null };
|
|
497
|
+
}
|
|
312
498
|
persist();
|
|
313
499
|
drawPicks();
|
|
314
500
|
void renderPanel();
|
|
@@ -325,6 +511,8 @@ function setMode(mode) {
|
|
|
325
511
|
const typingIn = (event) => event.composedPath().some((t) => ['TEXTAREA', 'INPUT', 'SELECT'].includes(t.tagName));
|
|
326
512
|
function onKey(event) {
|
|
327
513
|
const typing = typingIn(event);
|
|
514
|
+
if (event.key === 'Escape' && assets.isOpen())
|
|
515
|
+
return assets.close();
|
|
328
516
|
if (event.altKey && event.shiftKey && event.code === 'KeyS') {
|
|
329
517
|
event.preventDefault();
|
|
330
518
|
return setMode(state.mode === 'select' ? 'browse' : 'select');
|
|
@@ -340,12 +528,27 @@ function onKey(event) {
|
|
|
340
528
|
persist();
|
|
341
529
|
return void renderPanel();
|
|
342
530
|
}
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
state.
|
|
346
|
-
|
|
347
|
-
|
|
531
|
+
if (event.key === 'Alt') {
|
|
532
|
+
measuring = true;
|
|
533
|
+
if (state.mode === 'select')
|
|
534
|
+
event.preventDefault();
|
|
535
|
+
return;
|
|
348
536
|
}
|
|
537
|
+
if (state.mode !== 'select' || !state.picks.length || event.composedPath().includes(host))
|
|
538
|
+
return;
|
|
539
|
+
const step = event.key === 'ArrowUp' || (event.key === 'Enter' && event.shiftKey)
|
|
540
|
+
? 'up'
|
|
541
|
+
: event.key === 'ArrowDown' || event.key === 'Enter'
|
|
542
|
+
? 'down'
|
|
543
|
+
: event.key === 'Tab'
|
|
544
|
+
? event.shiftKey
|
|
545
|
+
? 'previous'
|
|
546
|
+
: 'next'
|
|
547
|
+
: null;
|
|
548
|
+
if (!step)
|
|
549
|
+
return;
|
|
550
|
+
event.preventDefault();
|
|
551
|
+
walk(step);
|
|
349
552
|
}
|
|
350
553
|
let listening = listen(window);
|
|
351
554
|
const reload = () => win.location.reload();
|
|
@@ -440,6 +643,12 @@ function renderDock() {
|
|
|
440
643
|
void renderPanel();
|
|
441
644
|
},
|
|
442
645
|
}, ['Layers']),
|
|
646
|
+
h('button', {
|
|
647
|
+
class: 'act',
|
|
648
|
+
type: 'button',
|
|
649
|
+
title: 'Every component of the app, its variants, previews and uses; and the design tokens',
|
|
650
|
+
onclick: () => void assets.open(),
|
|
651
|
+
}, ['Assets']),
|
|
443
652
|
h('button', {
|
|
444
653
|
class: 'act',
|
|
445
654
|
type: 'button',
|
|
@@ -455,7 +664,7 @@ function renderDock() {
|
|
|
455
664
|
state.panel = state.picks.length ? 'inspector' : null;
|
|
456
665
|
openBench();
|
|
457
666
|
},
|
|
458
|
-
}, ['
|
|
667
|
+
}, ['Frame']),
|
|
459
668
|
h('button', {
|
|
460
669
|
class: 'act gear',
|
|
461
670
|
type: 'button',
|
|
@@ -467,6 +676,17 @@ function renderDock() {
|
|
|
467
676
|
void renderPanel();
|
|
468
677
|
},
|
|
469
678
|
}, ['⚙']),
|
|
679
|
+
screenOf()
|
|
680
|
+
? h('button', {
|
|
681
|
+
class: 'previewing',
|
|
682
|
+
type: 'button',
|
|
683
|
+
title: 'Exit the preview from previews.ts',
|
|
684
|
+
onclick: () => {
|
|
685
|
+
setScreen(null);
|
|
686
|
+
win.location.reload();
|
|
687
|
+
},
|
|
688
|
+
}, [`Screen: ${screenOf().slice(screenOf().indexOf(':') + 1)}`, ' · Exit'])
|
|
689
|
+
: null,
|
|
470
690
|
held()
|
|
471
691
|
? h('button', {
|
|
472
692
|
class: 'previewing',
|
|
@@ -475,11 +695,12 @@ function renderDock() {
|
|
|
475
695
|
onclick: () => hold(null, reload),
|
|
476
696
|
}, [h('span', { 'data-preview': true }, ['Preview']), ' · Exit'])
|
|
477
697
|
: null,
|
|
478
|
-
state.mode === 'select' && !held()
|
|
698
|
+
state.mode === 'select' && !held() && !state.panel
|
|
479
699
|
? h('div', { class: 'tip', role: 'note' }, [
|
|
480
700
|
h('span', {}, [h('kbd', {}, ['Click']), ' a part to select it']),
|
|
481
701
|
h('span', {}, [h('kbd', {}, ['Shift']), '+click adds another']),
|
|
482
|
-
h('span', {}, [h('kbd', {}, ['
|
|
702
|
+
h('span', {}, [h('kbd', {}, ['⇧ Enter']), ' picks its parent']),
|
|
703
|
+
h('span', {}, [h('kbd', {}, ['Alt']), ' measures']),
|
|
483
704
|
h('span', {}, [h('kbd', {}, ['Esc']), ' stops']),
|
|
484
705
|
])
|
|
485
706
|
: null,
|
|
@@ -972,6 +1193,7 @@ function developerSections(n, active) {
|
|
|
972
1193
|
];
|
|
973
1194
|
}
|
|
974
1195
|
async function renderPanel() {
|
|
1196
|
+
dock.querySelector('.tip')?.toggleAttribute('hidden', !!state.panel);
|
|
975
1197
|
placePanel();
|
|
976
1198
|
if (state.panel === 'changes')
|
|
977
1199
|
return state.opened ? renderRequest(state.opened) : renderChanges();
|
|
@@ -979,8 +1201,13 @@ async function renderPanel() {
|
|
|
979
1201
|
return renderSettings();
|
|
980
1202
|
if (state.panel === 'notes')
|
|
981
1203
|
return agent.render(panel);
|
|
982
|
-
if (state.panel === 'layers')
|
|
983
|
-
|
|
1204
|
+
if (state.panel === 'layers') {
|
|
1205
|
+
await renderLayers(panel, await tree(win.location.pathname), layersHost(() => open(state.picks.length ? 'inspector' : null)));
|
|
1206
|
+
const screens = await screensSection();
|
|
1207
|
+
if (screens)
|
|
1208
|
+
panel.append(screens);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
984
1211
|
const active = state.picks[state.active];
|
|
985
1212
|
if (state.panel !== 'inspector' || !active) {
|
|
986
1213
|
panel.hidden = state.view !== 'workbench';
|
|
@@ -1065,7 +1292,7 @@ async function renderChanges() {
|
|
|
1065
1292
|
const done = h('button', {
|
|
1066
1293
|
class: 'link done',
|
|
1067
1294
|
type: 'button',
|
|
1068
|
-
title: '
|
|
1295
|
+
title: 'Resolved or not needed: remove it',
|
|
1069
1296
|
onclick: async () => {
|
|
1070
1297
|
if (!armed) {
|
|
1071
1298
|
armed = true;
|
|
@@ -1075,7 +1302,7 @@ async function renderChanges() {
|
|
|
1075
1302
|
await remove(r.number);
|
|
1076
1303
|
void renderPanel();
|
|
1077
1304
|
},
|
|
1078
|
-
}, ['
|
|
1305
|
+
}, ['Resolve']);
|
|
1079
1306
|
return h('div', { class: 'req' }, [
|
|
1080
1307
|
h('span', { class: 'n' }, [r.number]),
|
|
1081
1308
|
h('button', { class: 't open-req', type: 'button', onclick: () => open('changes', r.number, 'saved') }, [r.title]),
|
|
@@ -1088,7 +1315,7 @@ async function renderChanges() {
|
|
|
1088
1315
|
message ? h('div', { class: 'notice' }, message) : null,
|
|
1089
1316
|
...(list.length
|
|
1090
1317
|
? [...list].reverse().map(row)
|
|
1091
|
-
: [h('div', { class: 'empty' }, ['No open requests.
|
|
1318
|
+
: [h('div', { class: 'empty' }, ['No open requests. Resolved ones are removed.'])]),
|
|
1092
1319
|
list.length
|
|
1093
1320
|
? h('div', { class: 'sec' }, [
|
|
1094
1321
|
h('div', { class: 'choice' }, [
|
|
@@ -1200,7 +1427,7 @@ async function renderRequest(number) {
|
|
|
1200
1427
|
renderDock();
|
|
1201
1428
|
open('changes', null, 'saved');
|
|
1202
1429
|
},
|
|
1203
|
-
}, ['
|
|
1430
|
+
}, ['Resolve · remove']);
|
|
1204
1431
|
panel.replaceChildren(h('div', { class: 'head' }, [
|
|
1205
1432
|
h('button', { class: 'back', type: 'button', onclick: () => open('changes', null, 'saved') }, [
|
|
1206
1433
|
'← Saved',
|
|
@@ -1299,7 +1526,10 @@ function renderSettings() {
|
|
|
1299
1526
|
['Alt+Shift+S', 'Select on or off'],
|
|
1300
1527
|
['Click', 'Select'],
|
|
1301
1528
|
['Shift+click', 'Add to the request'],
|
|
1302
|
-
['
|
|
1529
|
+
['Shift+Enter', 'Select the area around it'],
|
|
1530
|
+
['Enter', 'Select inside it'],
|
|
1531
|
+
['Tab / Shift+Tab', 'Next / previous part beside it'],
|
|
1532
|
+
['Alt (hold)', 'Measure: W × H and the distance to the part under the pointer'],
|
|
1303
1533
|
['Double-click', 'Select a text'],
|
|
1304
1534
|
['↑ ↓', 'Around it / inside it'],
|
|
1305
1535
|
['Esc', 'Back to Browse'],
|
|
@@ -1316,6 +1546,10 @@ const left = h('div', { class: 'bench-left' });
|
|
|
1316
1546
|
const stage = h('div', { class: 'stage' });
|
|
1317
1547
|
const sizer = h('div', { class: 'sizer' });
|
|
1318
1548
|
const bar = h('div', { class: 'bench-bar' });
|
|
1549
|
+
addEventListener('keydown', (event) => {
|
|
1550
|
+
if (state.view === 'workbench' && win !== window)
|
|
1551
|
+
onKey(event);
|
|
1552
|
+
}, true);
|
|
1319
1553
|
function retarget(frame) {
|
|
1320
1554
|
listening.abort();
|
|
1321
1555
|
const w = frame?.contentWindow ?? null;
|
|
@@ -1418,7 +1652,13 @@ function renderBar() {
|
|
|
1418
1652
|
'aria-pressed': api.isOpen() ? 'true' : 'false',
|
|
1419
1653
|
onclick: () => api.toggle(),
|
|
1420
1654
|
}, ['API']),
|
|
1421
|
-
h('button', {
|
|
1655
|
+
h('button', {
|
|
1656
|
+
class: 'act',
|
|
1657
|
+
type: 'button',
|
|
1658
|
+
title: 'Every component of the app, its variants, previews and uses; and the design tokens',
|
|
1659
|
+
onclick: () => void assets.open(),
|
|
1660
|
+
}, ['Assets']),
|
|
1661
|
+
h('button', { class: 'act exit', type: 'button', onclick: () => closeBench() }, ['Exit frame']),
|
|
1422
1662
|
]));
|
|
1423
1663
|
}
|
|
1424
1664
|
async function renderLeft() {
|
|
@@ -1435,7 +1675,7 @@ function openBench() {
|
|
|
1435
1675
|
bench.hidden = false;
|
|
1436
1676
|
const frame = h('iframe', {
|
|
1437
1677
|
src: location.href,
|
|
1438
|
-
title: 'The page in
|
|
1678
|
+
title: 'The page in Frame',
|
|
1439
1679
|
'data-hozu-bench': true,
|
|
1440
1680
|
});
|
|
1441
1681
|
frame.addEventListener('load', () => {
|
|
@@ -1506,7 +1746,7 @@ addEventListener('resize', layout);
|
|
|
1506
1746
|
addEventListener('keydown', (event) => {
|
|
1507
1747
|
if (event.key === 'Escape' && !typingIn(event) && bench.classList.contains('layers-open'))
|
|
1508
1748
|
closeLayers();
|
|
1509
|
-
});
|
|
1749
|
+
}, true);
|
|
1510
1750
|
renderDock();
|
|
1511
1751
|
drawPicks();
|
|
1512
1752
|
void renderPanel();
|