@rahmantech/litedesign 0.0.0-stage → 0.1.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.
- package/LICENSE +201 -0
- package/README.md +86 -2
- package/dist/extension/config.json +3 -0
- package/dist/extension/content.js +471 -0
- package/dist/extension/content.js.map +7 -0
- package/dist/extension/core.iife.js +1048 -0
- package/dist/extension/manifest.json +16 -0
- package/dist/extension/popup.html +73 -0
- package/dist/extension/popup.js +71 -0
- package/dist/extension/popup.js.map +7 -0
- package/dist/extension/sw.js +348 -0
- package/dist/extension/sw.js.map +7 -0
- package/dist/index.js +2088 -0
- package/dist/index.js.map +7 -0
- package/package.json +37 -3
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// packages/extension/src/observer.ts
|
|
4
|
+
var DEFAULT_DEBOUNCE = 150;
|
|
5
|
+
function observeChanges(options) {
|
|
6
|
+
const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE;
|
|
7
|
+
let timer;
|
|
8
|
+
const schedule = () => {
|
|
9
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
10
|
+
timer = setTimeout(() => {
|
|
11
|
+
timer = void 0;
|
|
12
|
+
options.onChange();
|
|
13
|
+
}, debounceMs);
|
|
14
|
+
};
|
|
15
|
+
const mutation = new MutationObserver(schedule);
|
|
16
|
+
mutation.observe(options.root, {
|
|
17
|
+
childList: true,
|
|
18
|
+
subtree: true,
|
|
19
|
+
attributes: true,
|
|
20
|
+
attributeFilter: ["class", "style", "hidden"]
|
|
21
|
+
});
|
|
22
|
+
const resize = new ResizeObserver(schedule);
|
|
23
|
+
resize.observe(options.root);
|
|
24
|
+
const onResize = () => schedule();
|
|
25
|
+
const onNavigate = () => schedule();
|
|
26
|
+
window.addEventListener("resize", onResize);
|
|
27
|
+
window.addEventListener("popstate", onNavigate);
|
|
28
|
+
window.addEventListener("hashchange", onNavigate);
|
|
29
|
+
return {
|
|
30
|
+
stop() {
|
|
31
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
32
|
+
mutation.disconnect();
|
|
33
|
+
resize.disconnect();
|
|
34
|
+
window.removeEventListener("resize", onResize);
|
|
35
|
+
window.removeEventListener("popstate", onNavigate);
|
|
36
|
+
window.removeEventListener("hashchange", onNavigate);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// packages/extension/src/types.ts
|
|
42
|
+
var LAYER_NAMES = {
|
|
43
|
+
grid: true,
|
|
44
|
+
guide: true,
|
|
45
|
+
gap: true,
|
|
46
|
+
issue: true,
|
|
47
|
+
hover: true
|
|
48
|
+
};
|
|
49
|
+
var LAYERS = Object.keys(LAYER_NAMES);
|
|
50
|
+
var TOGGLE_LAYERS = [
|
|
51
|
+
"grid",
|
|
52
|
+
"guide",
|
|
53
|
+
"gap",
|
|
54
|
+
"issue"
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// packages/extension/src/store.ts
|
|
58
|
+
var STORAGE_KEY = "litedesign";
|
|
59
|
+
var DEFAULT_LAYERS = [...LAYERS];
|
|
60
|
+
function defaultLayers() {
|
|
61
|
+
return [...DEFAULT_LAYERS];
|
|
62
|
+
}
|
|
63
|
+
async function loadState() {
|
|
64
|
+
if (!hasStorage()) return { enabled: false, layers: defaultLayers() };
|
|
65
|
+
try {
|
|
66
|
+
const stored = await chrome.storage.local.get(STORAGE_KEY);
|
|
67
|
+
return parseState(stored[STORAGE_KEY]);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.warn("[litedesign] could not read preferences", error);
|
|
70
|
+
return { enabled: false, layers: defaultLayers() };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function saveState(state) {
|
|
74
|
+
if (!hasStorage()) return;
|
|
75
|
+
try {
|
|
76
|
+
await chrome.storage.local.set({ [STORAGE_KEY]: state });
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.warn("[litedesign] could not save preferences", error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function hasStorage() {
|
|
82
|
+
return typeof chrome !== "undefined" && chrome.storage?.local !== void 0;
|
|
83
|
+
}
|
|
84
|
+
function parseState(value) {
|
|
85
|
+
if (typeof value !== "object" || value === null) {
|
|
86
|
+
return { enabled: false, layers: defaultLayers() };
|
|
87
|
+
}
|
|
88
|
+
const record = value;
|
|
89
|
+
return {
|
|
90
|
+
// A value written before this flag existed means "not known to be on".
|
|
91
|
+
enabled: record.enabled === true,
|
|
92
|
+
layers: parseLayers(record.layers)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function parseLayers(value) {
|
|
96
|
+
if (!Array.isArray(value)) return defaultLayers();
|
|
97
|
+
const valid = [...new Set(value.filter(isLayerName))];
|
|
98
|
+
return valid.includes("hover") ? valid : [...valid, "hover"];
|
|
99
|
+
}
|
|
100
|
+
function isLayerName(value) {
|
|
101
|
+
return typeof value === "string" && LAYERS.includes(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// packages/extension/src/toolbar.ts
|
|
105
|
+
var STYLE = `
|
|
106
|
+
.bar {
|
|
107
|
+
position: absolute;
|
|
108
|
+
top: 12px;
|
|
109
|
+
right: 12px;
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 6px;
|
|
113
|
+
padding: 6px 8px;
|
|
114
|
+
border-radius: 10px;
|
|
115
|
+
background: rgba(17, 17, 17, 0.94);
|
|
116
|
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
|
|
117
|
+
color: #e5e5e5;
|
|
118
|
+
font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
119
|
+
pointer-events: auto;
|
|
120
|
+
user-select: none;
|
|
121
|
+
}
|
|
122
|
+
.brand {
|
|
123
|
+
padding: 0 2px;
|
|
124
|
+
color: #ffffff;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
letter-spacing: 0.02em;
|
|
127
|
+
}
|
|
128
|
+
.stats {
|
|
129
|
+
padding: 0 4px;
|
|
130
|
+
color: #a3a3a3;
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
}
|
|
133
|
+
button {
|
|
134
|
+
padding: 5px 8px;
|
|
135
|
+
border: 0;
|
|
136
|
+
border-radius: 6px;
|
|
137
|
+
background: #262626;
|
|
138
|
+
color: #d4d4d4;
|
|
139
|
+
font: inherit;
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
}
|
|
142
|
+
button:hover {
|
|
143
|
+
background: #404040;
|
|
144
|
+
}
|
|
145
|
+
button.on {
|
|
146
|
+
background: #3b82f6;
|
|
147
|
+
color: #ffffff;
|
|
148
|
+
}
|
|
149
|
+
button[data-action='close'] {
|
|
150
|
+
padding: 5px 7px;
|
|
151
|
+
background: transparent;
|
|
152
|
+
color: #a3a3a3;
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
}
|
|
155
|
+
button[data-action='close']:hover {
|
|
156
|
+
background: #404040;
|
|
157
|
+
color: #ffffff;
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
function createToolbar(options) {
|
|
161
|
+
const root2 = document.createElement("div");
|
|
162
|
+
root2.dataset.litedesignUi = "";
|
|
163
|
+
const style = document.createElement("style");
|
|
164
|
+
style.textContent = STYLE;
|
|
165
|
+
const bar = document.createElement("div");
|
|
166
|
+
bar.className = "bar";
|
|
167
|
+
const brand = document.createElement("span");
|
|
168
|
+
brand.className = "brand";
|
|
169
|
+
brand.textContent = "litedesign";
|
|
170
|
+
const stats = document.createElement("span");
|
|
171
|
+
stats.className = "stats";
|
|
172
|
+
stats.textContent = "measuring\u2026";
|
|
173
|
+
let enabled = new Set(options.layers);
|
|
174
|
+
const isEnabled = (layer) => enabled.has(layer);
|
|
175
|
+
bar.append(brand, stats);
|
|
176
|
+
const buttons = /* @__PURE__ */ new Map();
|
|
177
|
+
for (const layer of TOGGLE_LAYERS) {
|
|
178
|
+
const button = document.createElement("button");
|
|
179
|
+
button.type = "button";
|
|
180
|
+
button.dataset.layer = layer;
|
|
181
|
+
button.textContent = layer;
|
|
182
|
+
button.title = `Toggle ${layer} layer`;
|
|
183
|
+
button.addEventListener("click", () => options.onToggle(layer, !isEnabled(layer)));
|
|
184
|
+
buttons.set(layer, button);
|
|
185
|
+
bar.append(button);
|
|
186
|
+
}
|
|
187
|
+
const close = document.createElement("button");
|
|
188
|
+
close.type = "button";
|
|
189
|
+
close.dataset.action = "close";
|
|
190
|
+
close.textContent = "\xD7";
|
|
191
|
+
close.title = "Close litedesign";
|
|
192
|
+
close.setAttribute("aria-label", "Close litedesign");
|
|
193
|
+
close.addEventListener("click", () => options.onClose());
|
|
194
|
+
bar.append(close);
|
|
195
|
+
root2.append(style, bar);
|
|
196
|
+
const setLayers = (layers) => {
|
|
197
|
+
enabled = new Set(layers);
|
|
198
|
+
for (const [layer, button] of buttons) {
|
|
199
|
+
const on = enabled.has(layer);
|
|
200
|
+
button.setAttribute("aria-pressed", String(on));
|
|
201
|
+
button.classList.toggle("on", on);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
setLayers(options.layers);
|
|
205
|
+
return {
|
|
206
|
+
element: root2,
|
|
207
|
+
setStats(nodes, issues, truncated) {
|
|
208
|
+
stats.textContent = `${nodes} nodes \xB7 ${issues} issues${truncated ? " \xB7 truncated" : ""}`;
|
|
209
|
+
},
|
|
210
|
+
setLayers
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// packages/extension/src/boot.ts
|
|
215
|
+
var mountedHost;
|
|
216
|
+
function overlayHost() {
|
|
217
|
+
return mountedHost;
|
|
218
|
+
}
|
|
219
|
+
async function boot(core2) {
|
|
220
|
+
if (window.__litedesign_state__ === "active") {
|
|
221
|
+
window.__litedesign_teardown__?.();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (window.__litedesign_state__ === "starting") return;
|
|
225
|
+
window.__litedesign_state__ = "starting";
|
|
226
|
+
const state = await loadState();
|
|
227
|
+
if (window.__litedesign_state__ !== "starting") return;
|
|
228
|
+
try {
|
|
229
|
+
mount(core2, state.layers);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
window.__litedesign_state__ = void 0;
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function applyOverlay(core2, options) {
|
|
236
|
+
if (!options.enabled) {
|
|
237
|
+
window.__litedesign_teardown__?.();
|
|
238
|
+
return { enabled: false, layers: [] };
|
|
239
|
+
}
|
|
240
|
+
const layers = options.layers ?? (await loadState()).layers;
|
|
241
|
+
if (window.__litedesign_state__ === "active") {
|
|
242
|
+
window.__litedesign_layers__?.(layers);
|
|
243
|
+
return { enabled: true, layers };
|
|
244
|
+
}
|
|
245
|
+
window.__litedesign_state__ = "starting";
|
|
246
|
+
try {
|
|
247
|
+
mount(core2, layers);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
window.__litedesign_state__ = void 0;
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
return { enabled: overlayHost() !== void 0, layers };
|
|
253
|
+
}
|
|
254
|
+
function mount(core2, initial) {
|
|
255
|
+
const root2 = document.body ?? document.documentElement;
|
|
256
|
+
let layers = initial;
|
|
257
|
+
let graph = core2.buildGraph(root2);
|
|
258
|
+
let handle;
|
|
259
|
+
let observer;
|
|
260
|
+
let toolbar;
|
|
261
|
+
const teardown = () => {
|
|
262
|
+
observer?.stop();
|
|
263
|
+
handle?.destroy();
|
|
264
|
+
handle = void 0;
|
|
265
|
+
observer = void 0;
|
|
266
|
+
mountedHost = void 0;
|
|
267
|
+
window.__litedesign_teardown__ = void 0;
|
|
268
|
+
window.__litedesign_layers__ = void 0;
|
|
269
|
+
window.__litedesign_state__ = void 0;
|
|
270
|
+
void saveState({ enabled: false, layers });
|
|
271
|
+
};
|
|
272
|
+
const refresh = () => {
|
|
273
|
+
graph = core2.buildGraph(root2);
|
|
274
|
+
handle?.update(graph);
|
|
275
|
+
toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated);
|
|
276
|
+
};
|
|
277
|
+
const setLayers = (next) => {
|
|
278
|
+
layers = next;
|
|
279
|
+
handle?.setLayers(layers);
|
|
280
|
+
toolbar.setLayers(layers);
|
|
281
|
+
void saveState({ enabled: true, layers });
|
|
282
|
+
};
|
|
283
|
+
toolbar = createToolbar({
|
|
284
|
+
layers,
|
|
285
|
+
onToggle(layer, enabled) {
|
|
286
|
+
setLayers(
|
|
287
|
+
enabled ? [.../* @__PURE__ */ new Set([...layers, layer])] : layers.filter((name) => name !== layer)
|
|
288
|
+
);
|
|
289
|
+
},
|
|
290
|
+
onClose: teardown
|
|
291
|
+
});
|
|
292
|
+
toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated);
|
|
293
|
+
handle = core2.render(graph, { layers, toolbar: toolbar.element });
|
|
294
|
+
observer = observeChanges({ root: root2, onChange: refresh });
|
|
295
|
+
mountedHost = handle.host;
|
|
296
|
+
window.__litedesign_layers__ = setLayers;
|
|
297
|
+
window.__litedesign_teardown__ = teardown;
|
|
298
|
+
window.__litedesign_state__ = "active";
|
|
299
|
+
void saveState({ enabled: true, layers });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// packages/extension/src/bridge.ts
|
|
303
|
+
var TEMP_LAYERS = ["grid", "guide", "gap", "issue"];
|
|
304
|
+
var FULL_PAGE = "full-page screenshots need the Playwright driver. The extension can only capture the visible viewport.";
|
|
305
|
+
var suspended;
|
|
306
|
+
function installBridge(core2) {
|
|
307
|
+
window.__litedesign_toggle__ = () => void boot(core2);
|
|
308
|
+
if (window.__litedesign_bridge__ === true) return;
|
|
309
|
+
window.__litedesign_bridge__ = true;
|
|
310
|
+
if (typeof chrome === "undefined" || chrome.runtime?.onMessage === void 0) return;
|
|
311
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
312
|
+
void respond(core2, message).then(sendResponse, (error) => {
|
|
313
|
+
sendResponse({ ok: false, error: messageOf(error) });
|
|
314
|
+
});
|
|
315
|
+
return true;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async function respond(core2, raw) {
|
|
319
|
+
if (typeof raw !== "object" || raw === null) throw new Error("invalid message");
|
|
320
|
+
const message = raw;
|
|
321
|
+
const action = typeof message.action === "string" ? message.action : "";
|
|
322
|
+
const payload = message.payload ?? {};
|
|
323
|
+
switch (action) {
|
|
324
|
+
case "ping":
|
|
325
|
+
return { ok: true, result: true };
|
|
326
|
+
case "toggle":
|
|
327
|
+
await boot(core2);
|
|
328
|
+
return { ok: true };
|
|
329
|
+
case "overlay":
|
|
330
|
+
return {
|
|
331
|
+
ok: true,
|
|
332
|
+
result: await applyOverlay(core2, {
|
|
333
|
+
enabled: payload.enabled !== false,
|
|
334
|
+
layers: payload.layers
|
|
335
|
+
})
|
|
336
|
+
};
|
|
337
|
+
case "graph":
|
|
338
|
+
return { ok: true, result: core2.buildGraph(root()) };
|
|
339
|
+
case "measure":
|
|
340
|
+
return { ok: true, result: measure(core2, payload.selector) };
|
|
341
|
+
case "screenshot_prepare":
|
|
342
|
+
return { ok: true, result: await prepareScreenshot(core2, payload) };
|
|
343
|
+
case "annotate_prepare":
|
|
344
|
+
return { ok: true, result: await prepareAnnotation(core2, payload) };
|
|
345
|
+
case "capture_cleanup":
|
|
346
|
+
await cleanup();
|
|
347
|
+
return { ok: true };
|
|
348
|
+
default:
|
|
349
|
+
throw new Error(`unknown action: ${action || "(none)"}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function measure(core2, selector) {
|
|
353
|
+
if (selector === void 0 || selector === "") throw new Error("measure needs a selector");
|
|
354
|
+
const graph = core2.buildGraph(root());
|
|
355
|
+
const node = core2.resolveNode(graph, selector);
|
|
356
|
+
if (node === null) throw new Error(`selector "${selector}" did not match any element on the page`);
|
|
357
|
+
const gaps = graph.gaps.filter((gap) => gap.a === node.id || gap.b === node.id).map((gap) => ({
|
|
358
|
+
to: gap.a === node.id ? gap.b : gap.a,
|
|
359
|
+
axis: gap.axis,
|
|
360
|
+
distance: gap.distance,
|
|
361
|
+
kind: gap.kind
|
|
362
|
+
}));
|
|
363
|
+
return {
|
|
364
|
+
id: node.id,
|
|
365
|
+
selector: node.selector,
|
|
366
|
+
tag: node.tag,
|
|
367
|
+
rect: node.rect,
|
|
368
|
+
styles: node.styles,
|
|
369
|
+
gaps
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async function prepareScreenshot(core2, payload) {
|
|
373
|
+
if (payload.fullPage === true) throw new Error(FULL_PAGE);
|
|
374
|
+
const dpr = window.devicePixelRatio || 1;
|
|
375
|
+
const rect = await prepareElement(core2, payload.selector);
|
|
376
|
+
suspend();
|
|
377
|
+
return { rect, dpr };
|
|
378
|
+
}
|
|
379
|
+
async function prepareAnnotation(core2, payload) {
|
|
380
|
+
if (payload.fullPage === true) throw new Error(FULL_PAGE);
|
|
381
|
+
const dpr = window.devicePixelRatio || 1;
|
|
382
|
+
const rect = await prepareElement(core2, payload.selector);
|
|
383
|
+
let graph = core2.buildGraph(root());
|
|
384
|
+
if (payload.rules !== void 0 && payload.rules.length > 0) {
|
|
385
|
+
const wanted = new Set(payload.rules);
|
|
386
|
+
graph = { ...graph, issues: graph.issues.filter((issue) => wanted.has(issue.rule)) };
|
|
387
|
+
}
|
|
388
|
+
const handle = core2.render(graph, { layers: payload.layers ?? TEMP_LAYERS });
|
|
389
|
+
suspend(handle);
|
|
390
|
+
await frames(2);
|
|
391
|
+
return { rect, dpr };
|
|
392
|
+
}
|
|
393
|
+
async function cleanup() {
|
|
394
|
+
const current = suspended;
|
|
395
|
+
if (current === void 0) return;
|
|
396
|
+
suspended = void 0;
|
|
397
|
+
current.handle?.destroy();
|
|
398
|
+
showUserOverlay(current.hidden);
|
|
399
|
+
}
|
|
400
|
+
function suspend(handle) {
|
|
401
|
+
const previous = suspended;
|
|
402
|
+
previous?.handle?.destroy();
|
|
403
|
+
showUserOverlay(previous?.hidden);
|
|
404
|
+
suspended = handle === void 0 ? { hidden: hideUserOverlay() } : { handle, hidden: hideUserOverlay() };
|
|
405
|
+
}
|
|
406
|
+
function hideUserOverlay() {
|
|
407
|
+
const element = overlayHost();
|
|
408
|
+
if (element === void 0) return void 0;
|
|
409
|
+
const display = element.style.display;
|
|
410
|
+
element.style.display = "none";
|
|
411
|
+
return { element, display };
|
|
412
|
+
}
|
|
413
|
+
function showUserOverlay(hidden) {
|
|
414
|
+
if (hidden !== void 0) hidden.element.style.display = hidden.display;
|
|
415
|
+
}
|
|
416
|
+
async function prepareElement(core2, selector) {
|
|
417
|
+
if (selector === void 0 || selector === "") return void 0;
|
|
418
|
+
const graph = core2.buildGraph(root());
|
|
419
|
+
const node = core2.resolveNode(graph, selector);
|
|
420
|
+
if (node === null) throw new Error(`selector "${selector}" did not match any element on the page`);
|
|
421
|
+
let element = null;
|
|
422
|
+
try {
|
|
423
|
+
element = document.querySelector(node.selector);
|
|
424
|
+
} catch {
|
|
425
|
+
element = null;
|
|
426
|
+
}
|
|
427
|
+
if (element === null) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
`selector "${selector}" is inside a shadow root. Measure it, but capture the whole viewport instead.`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
element.scrollIntoView({ block: "center", inline: "center" });
|
|
433
|
+
await frames(1);
|
|
434
|
+
const rect = element.getBoundingClientRect();
|
|
435
|
+
return { x: rect.left, y: rect.top, w: rect.width, h: rect.height };
|
|
436
|
+
}
|
|
437
|
+
function root() {
|
|
438
|
+
return document.body ?? document.documentElement;
|
|
439
|
+
}
|
|
440
|
+
function frames(count) {
|
|
441
|
+
return new Promise((resolve) => {
|
|
442
|
+
let left = count;
|
|
443
|
+
const tick = () => {
|
|
444
|
+
left -= 1;
|
|
445
|
+
if (left <= 0) resolve();
|
|
446
|
+
else requestAnimationFrame(tick);
|
|
447
|
+
};
|
|
448
|
+
requestAnimationFrame(tick);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
function messageOf(error) {
|
|
452
|
+
if (error instanceof Error && error.message !== "") return error.message;
|
|
453
|
+
if (typeof error === "string" && error !== "") return error;
|
|
454
|
+
return "unexpected error";
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// packages/extension/src/content.ts
|
|
458
|
+
var core = window.__litedesign;
|
|
459
|
+
if (core === void 0) {
|
|
460
|
+
console.warn("[litedesign] core not found; core.iife.js must be injected first");
|
|
461
|
+
} else {
|
|
462
|
+
installBridge(core);
|
|
463
|
+
void restore(core);
|
|
464
|
+
}
|
|
465
|
+
async function restore(core2) {
|
|
466
|
+
const state = await loadState();
|
|
467
|
+
if (!state.enabled) return;
|
|
468
|
+
await applyOverlay(core2, { enabled: true, layers: state.layers });
|
|
469
|
+
}
|
|
470
|
+
})();
|
|
471
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/observer.ts", "../src/types.ts", "../src/store.ts", "../src/toolbar.ts", "../src/boot.ts", "../src/bridge.ts", "../src/content.ts"],
|
|
4
|
+
"sourcesContent": ["export type ObserverOptions = {\n root: Element\n debounceMs?: number\n onChange(): void\n}\n\nexport type Observer = {\n stop(): void\n}\n\nconst DEFAULT_DEBOUNCE = 150\n\n/**\n * Rebuilds after the page changes. MutationObserver catches DOM edits,\n * ResizeObserver catches layout-only changes, and window resize catches\n * viewport reflow. All paths share one debounce so a burst of mutations\n * produces one measurement pass.\n */\nexport function observeChanges(options: ObserverOptions): Observer {\n const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const schedule = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n\n timer = setTimeout(() => {\n timer = undefined\n options.onChange()\n }, debounceMs)\n }\n\n const mutation = new MutationObserver(schedule)\n mutation.observe(options.root, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['class', 'style', 'hidden'],\n })\n\n const resize = new ResizeObserver(schedule)\n resize.observe(options.root)\n\n const onResize = (): void => schedule()\n const onNavigate = (): void => schedule()\n\n window.addEventListener('resize', onResize)\n window.addEventListener('popstate', onNavigate)\n window.addEventListener('hashchange', onNavigate)\n\n return {\n stop(): void {\n if (timer !== undefined) clearTimeout(timer)\n mutation.disconnect()\n resize.disconnect()\n window.removeEventListener('resize', onResize)\n window.removeEventListener('popstate', onNavigate)\n window.removeEventListener('hashchange', onNavigate)\n },\n }\n}\n", "import type { Graph, GraphNode, LayerName, OverlayHandle, OverlayOptions } from '@litedesign/core'\n\nexport type { Graph, GraphNode, LayerName, OverlayHandle }\n\n/**\n * The injected core is a global, not an import. This interface is the only\n * thing the extension knows about it; everything else comes from `core/` types\n * so there is no second source of truth.\n */\nexport type Core = {\n buildGraph(root?: Element | null): Graph\n resolveNode(graph: Graph, selector: string): GraphNode | null\n render(graph: Graph, options?: OverlayOptions): OverlayHandle\n VERSION: string\n}\n\n/** Exhaustive by construction: adding a layer to core breaks this object. */\nconst LAYER_NAMES: Record<LayerName, true> = {\n grid: true,\n guide: true,\n gap: true,\n issue: true,\n hover: true,\n}\n\nexport const LAYERS = Object.keys(LAYER_NAMES) as LayerName[]\n\n/**\n * What the popup shows: whether the worker holds a socket to the bridge, on\n * which port, and which tab it currently measures.\n */\nexport type BridgeStatus = {\n bridge: boolean\n port: number\n page: { tabId: number; url: string; title: string } | null\n}\n\n/** Layers the toolbar can switch. Hover inspection stays on. */\nexport const TOGGLE_LAYERS = [\n 'grid',\n 'guide',\n 'gap',\n 'issue',\n] as const satisfies readonly LayerName[]\nexport type ToggleLayer = (typeof TOGGLE_LAYERS)[number]\n\ndeclare global {\n interface Window {\n __litedesign?: Core\n __litedesign_state__?: 'starting' | 'active'\n __litedesign_teardown__?: () => void\n /** Replaces the visible layers without remounting. */\n __litedesign_layers__?: (layers: LayerName[]) => void\n /** True once the content-script bridge is listening. */\n __litedesign_bridge__?: boolean\n /** Toggle the user overlay. The service worker sends `toggle` here. */\n __litedesign_toggle__?: () => void\n }\n}\n", "import { LAYERS, type LayerName } from './types.js'\n\nconst STORAGE_KEY = 'litedesign'\n\nconst DEFAULT_LAYERS: LayerName[] = [...LAYERS]\n\n/**\n * What survives a reload: whether the overlay was on, and which layers it\n * showed. The fixture server reloads the page on every save, so without this\n * the grid disappears each time a file changes.\n */\nexport type OverlayState = {\n enabled: boolean\n layers: LayerName[]\n}\n\nexport function defaultLayers(): LayerName[] {\n return [...DEFAULT_LAYERS]\n}\n\nexport async function loadState(): Promise<OverlayState> {\n if (!hasStorage()) return { enabled: false, layers: defaultLayers() }\n\n try {\n const stored = await chrome.storage.local.get(STORAGE_KEY)\n return parseState(stored[STORAGE_KEY])\n } catch (error) {\n // Preferences are a convenience; a storage failure must not hide the\n // overlay. The user sees defaults instead.\n console.warn('[litedesign] could not read preferences', error)\n return { enabled: false, layers: defaultLayers() }\n }\n}\n\nexport async function saveState(state: OverlayState): Promise<void> {\n if (!hasStorage()) return\n\n try {\n await chrome.storage.local.set({ [STORAGE_KEY]: state })\n } catch (error) {\n // The current session keeps working with in-memory state.\n console.warn('[litedesign] could not save preferences', error)\n }\n}\n\nfunction hasStorage(): boolean {\n return typeof chrome !== 'undefined' && chrome.storage?.local !== undefined\n}\n\nfunction parseState(value: unknown): OverlayState {\n if (typeof value !== 'object' || value === null) {\n return { enabled: false, layers: defaultLayers() }\n }\n\n const record = value as { enabled?: unknown; layers?: unknown }\n\n return {\n // A value written before this flag existed means \"not known to be on\".\n enabled: record.enabled === true,\n layers: parseLayers(record.layers),\n }\n}\n\nfunction parseLayers(value: unknown): LayerName[] {\n if (!Array.isArray(value)) return defaultLayers()\n\n const valid = [...new Set(value.filter(isLayerName))]\n return valid.includes('hover') ? valid : [...valid, 'hover']\n}\n\nfunction isLayerName(value: unknown): value is LayerName {\n return typeof value === 'string' && (LAYERS as readonly string[]).includes(value)\n}\n", "import { type LayerName, TOGGLE_LAYERS, type ToggleLayer } from './types.js'\n\nexport type ToolbarOptions = {\n layers: LayerName[]\n onToggle(layer: ToggleLayer, enabled: boolean): void\n onClose(): void\n}\n\nexport type Toolbar = {\n element: HTMLElement\n setStats(nodes: number, issues: number, truncated: boolean): void\n setLayers(layers: LayerName[]): void\n}\n\nconst STYLE = `\n .bar {\n position: absolute;\n top: 12px;\n right: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-radius: 10px;\n background: rgba(17, 17, 17, 0.94);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);\n color: #e5e5e5;\n font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;\n pointer-events: auto;\n user-select: none;\n }\n .brand {\n padding: 0 2px;\n color: #ffffff;\n font-weight: 600;\n letter-spacing: 0.02em;\n }\n .stats {\n padding: 0 4px;\n color: #a3a3a3;\n white-space: nowrap;\n }\n button {\n padding: 5px 8px;\n border: 0;\n border-radius: 6px;\n background: #262626;\n color: #d4d4d4;\n font: inherit;\n cursor: pointer;\n }\n button:hover {\n background: #404040;\n }\n button.on {\n background: #3b82f6;\n color: #ffffff;\n }\n button[data-action='close'] {\n padding: 5px 7px;\n background: transparent;\n color: #a3a3a3;\n font-size: 14px;\n }\n button[data-action='close']:hover {\n background: #404040;\n color: #ffffff;\n }\n`\n\nexport function createToolbar(options: ToolbarOptions): Toolbar {\n const root = document.createElement('div')\n root.dataset.litedesignUi = ''\n\n const style = document.createElement('style')\n style.textContent = STYLE\n\n const bar = document.createElement('div')\n bar.className = 'bar'\n\n const brand = document.createElement('span')\n brand.className = 'brand'\n brand.textContent = 'litedesign'\n\n const stats = document.createElement('span')\n stats.className = 'stats'\n stats.textContent = 'measuring\u2026'\n\n let enabled = new Set<LayerName>(options.layers)\n const isEnabled = (layer: ToggleLayer): boolean => enabled.has(layer)\n\n bar.append(brand, stats)\n\n const buttons = new Map<ToggleLayer, HTMLButtonElement>()\n\n for (const layer of TOGGLE_LAYERS) {\n const button = document.createElement('button')\n button.type = 'button'\n button.dataset.layer = layer\n button.textContent = layer\n button.title = `Toggle ${layer} layer`\n button.addEventListener('click', () => options.onToggle(layer, !isEnabled(layer)))\n\n buttons.set(layer, button)\n bar.append(button)\n }\n\n const close = document.createElement('button')\n close.type = 'button'\n close.dataset.action = 'close'\n close.textContent = '\u00D7'\n close.title = 'Close litedesign'\n close.setAttribute('aria-label', 'Close litedesign')\n close.addEventListener('click', () => options.onClose())\n\n bar.append(close)\n root.append(style, bar)\n\n const setLayers = (layers: LayerName[]): void => {\n enabled = new Set(layers)\n\n for (const [layer, button] of buttons) {\n const on = enabled.has(layer)\n button.setAttribute('aria-pressed', String(on))\n button.classList.toggle('on', on)\n }\n }\n\n setLayers(options.layers)\n\n return {\n element: root,\n setStats(nodes: number, issues: number, truncated: boolean): void {\n stats.textContent = `${nodes} nodes \u00B7 ${issues} issues${truncated ? ' \u00B7 truncated' : ''}`\n },\n setLayers,\n }\n}\n", "import { type Observer, observeChanges } from './observer.js'\nimport { loadState, saveState } from './store.js'\nimport { createToolbar, type Toolbar } from './toolbar.js'\nimport type { Core, Graph, LayerName, OverlayHandle } from './types.js'\n\n/**\n * The host of the overlay that is on screen. The bridge hides it during a\n * capture instead of tearing it down, so the user's layer selection survives\n * the screenshot.\n */\nlet mountedHost: HTMLElement | undefined\n\nexport function overlayHost(): HTMLElement | undefined {\n return mountedHost\n}\n\n/**\n * Idempotent entry point. Chrome may inject the bundle again into the same tab,\n * and a fresh module scope forgets everything, so the state lives on `window`.\n *\n * - idle: mount\n * - starting: ignore the click, storage is still reading\n * - active: tear down\n */\nexport async function boot(core: Core): Promise<void> {\n if (window.__litedesign_state__ === 'active') {\n window.__litedesign_teardown__?.()\n return\n }\n\n if (window.__litedesign_state__ === 'starting') return\n\n window.__litedesign_state__ = 'starting'\n\n const state = await loadState()\n\n // The user may have clicked again while storage was reading.\n if (window.__litedesign_state__ !== 'starting') return\n\n try {\n mount(core, state.layers)\n } catch (error) {\n window.__litedesign_state__ = undefined\n throw error\n }\n}\n\n/**\n * MCP control. `boot` toggles because a click means \"flip it\"; this sets the\n * state, because the agent says exactly what it wants to see.\n *\n * Returns what the page is actually showing afterwards, so the tool can report\n * a reading instead of echoing the request.\n */\nexport async function applyOverlay(\n core: Core,\n options: { enabled: boolean; layers?: LayerName[] },\n): Promise<{ enabled: boolean; layers: LayerName[] }> {\n if (!options.enabled) {\n window.__litedesign_teardown__?.()\n return { enabled: false, layers: [] }\n }\n\n const layers = options.layers ?? (await loadState()).layers\n\n if (window.__litedesign_state__ === 'active') {\n window.__litedesign_layers__?.(layers)\n return { enabled: true, layers }\n }\n\n window.__litedesign_state__ = 'starting'\n\n try {\n mount(core, layers)\n } catch (error) {\n window.__litedesign_state__ = undefined\n throw error\n }\n\n return { enabled: overlayHost() !== undefined, layers }\n}\n\nfunction mount(core: Core, initial: LayerName[]): void {\n // `document.body` can be missing if the user clicks before parsing finishes;\n // documentElement is always there and catches the body once it appears.\n const root = document.body ?? document.documentElement\n\n let layers = initial\n let graph: Graph = core.buildGraph(root)\n let handle: OverlayHandle | undefined\n let observer: Observer | undefined\n let toolbar: Toolbar\n\n const teardown = (): void => {\n observer?.stop()\n handle?.destroy()\n handle = undefined\n observer = undefined\n mountedHost = undefined\n window.__litedesign_teardown__ = undefined\n window.__litedesign_layers__ = undefined\n window.__litedesign_state__ = undefined\n void saveState({ enabled: false, layers })\n }\n\n const refresh = (): void => {\n graph = core.buildGraph(root)\n handle?.update(graph)\n toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated)\n }\n\n const setLayers = (next: LayerName[]): void => {\n layers = next\n handle?.setLayers(layers)\n toolbar.setLayers(layers)\n void saveState({ enabled: true, layers })\n }\n\n toolbar = createToolbar({\n layers,\n onToggle(layer, enabled): void {\n setLayers(\n enabled ? [...new Set([...layers, layer])] : layers.filter((name) => name !== layer),\n )\n },\n onClose: teardown,\n })\n\n toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated)\n\n handle = core.render(graph, { layers, toolbar: toolbar.element })\n observer = observeChanges({ root, onChange: refresh })\n\n mountedHost = handle.host\n window.__litedesign_layers__ = setLayers\n window.__litedesign_teardown__ = teardown\n window.__litedesign_state__ = 'active'\n\n // A reload has to know the overlay was on, otherwise the next page load\n // starts clean and the grid vanishes on every save.\n void saveState({ enabled: true, layers })\n}\n", "import { applyOverlay, boot, overlayHost } from './boot.js'\nimport type { Core, Graph, LayerName, OverlayHandle } from './types.js'\n\ntype Payload = {\n selector?: string\n rules?: string[]\n layers?: LayerName[]\n fullPage?: boolean\n enabled?: boolean\n}\n\ntype Rect = { x: number; y: number; w: number; h: number }\n\ntype Prepared = { rect?: Rect; dpr: number }\n\ntype Response = { ok: true; result?: unknown } | { ok: false; error: string }\n\nconst TEMP_LAYERS: LayerName[] = ['grid', 'guide', 'gap', 'issue']\n\nconst FULL_PAGE =\n 'full-page screenshots need the Playwright driver. The extension can only capture the visible viewport.'\n\ntype Hidden = { element: HTMLElement; display: string }\n\n/**\n * While a capture is running the user's overlay is hidden. `handle` is the\n * temporary overlay for an annotated capture; `hidden` is the user's overlay\n * with the display value it had before the capture.\n */\nlet suspended: { handle?: OverlayHandle; hidden?: Hidden } | undefined\n\n/**\n * Installs the content-script side of the bridge. Safe to call on every\n * injection: Chrome may inject the bundle more than once into the same tab.\n */\nexport function installBridge(core: Core): void {\n window.__litedesign_toggle__ = () => void boot(core)\n\n if (window.__litedesign_bridge__ === true) return\n window.__litedesign_bridge__ = true\n\n // Tests inject this bundle into a normal page, where `chrome` does not exist.\n if (typeof chrome === 'undefined' || chrome.runtime?.onMessage === undefined) return\n\n chrome.runtime.onMessage.addListener((message: unknown, _sender, sendResponse) => {\n void respond(core, message).then(sendResponse, (error: unknown) => {\n sendResponse({ ok: false, error: messageOf(error) })\n })\n\n // Keep the channel open for the async response.\n return true\n })\n}\n\nasync function respond(core: Core, raw: unknown): Promise<Response> {\n if (typeof raw !== 'object' || raw === null) throw new Error('invalid message')\n\n const message = raw as { action?: unknown; payload?: unknown }\n const action = typeof message.action === 'string' ? message.action : ''\n const payload = (message.payload ?? {}) as Payload\n\n switch (action) {\n case 'ping':\n return { ok: true, result: true }\n case 'toggle':\n await boot(core)\n return { ok: true }\n case 'overlay':\n return {\n ok: true,\n result: await applyOverlay(core, {\n enabled: payload.enabled !== false,\n layers: payload.layers,\n }),\n }\n case 'graph':\n return { ok: true, result: core.buildGraph(root()) }\n case 'measure':\n return { ok: true, result: measure(core, payload.selector) }\n case 'screenshot_prepare':\n return { ok: true, result: await prepareScreenshot(core, payload) }\n case 'annotate_prepare':\n return { ok: true, result: await prepareAnnotation(core, payload) }\n case 'capture_cleanup':\n await cleanup()\n return { ok: true }\n default:\n throw new Error(`unknown action: ${action || '(none)'}`)\n }\n}\n\nfunction measure(core: Core, selector: string | undefined): unknown {\n if (selector === undefined || selector === '') throw new Error('measure needs a selector')\n\n const graph = core.buildGraph(root())\n const node = core.resolveNode(graph, selector)\n if (node === null) throw new Error(`selector \"${selector}\" did not match any element on the page`)\n\n const gaps = graph.gaps\n .filter((gap) => gap.a === node.id || gap.b === node.id)\n .map((gap) => ({\n to: gap.a === node.id ? gap.b : gap.a,\n axis: gap.axis,\n distance: gap.distance,\n kind: gap.kind,\n }))\n\n return {\n id: node.id,\n selector: node.selector,\n tag: node.tag,\n rect: node.rect,\n styles: node.styles,\n gaps,\n }\n}\n\nasync function prepareScreenshot(core: Core, payload: Payload): Promise<Prepared> {\n if (payload.fullPage === true) throw new Error(FULL_PAGE)\n\n const dpr = window.devicePixelRatio || 1\n const rect = await prepareElement(core, payload.selector)\n suspend()\n\n return { rect, dpr }\n}\n\nasync function prepareAnnotation(core: Core, payload: Payload): Promise<Prepared> {\n if (payload.fullPage === true) throw new Error(FULL_PAGE)\n\n const dpr = window.devicePixelRatio || 1\n const rect = await prepareElement(core, payload.selector)\n\n let graph: Graph = core.buildGraph(root())\n if (payload.rules !== undefined && payload.rules.length > 0) {\n const wanted = new Set(payload.rules)\n graph = { ...graph, issues: graph.issues.filter((issue) => wanted.has(issue.rule)) }\n }\n\n const handle = core.render(graph, { layers: payload.layers ?? TEMP_LAYERS })\n suspend(handle)\n await frames(2)\n\n return { rect, dpr }\n}\n\nasync function cleanup(): Promise<void> {\n const current = suspended\n if (current === undefined) return\n\n suspended = undefined\n current.handle?.destroy()\n showUserOverlay(current.hidden)\n}\n\n/**\n * Hides the user's overlay for the duration of a capture. It used to tear the\n * overlay down and remount it afterwards from storage, which meant a capture\n * could bring back a different layer selection than the one on screen. A\n * previous capture's overlay is replaced, not stacked.\n */\nfunction suspend(handle?: OverlayHandle): void {\n const previous = suspended\n previous?.handle?.destroy()\n showUserOverlay(previous?.hidden)\n\n suspended =\n handle === undefined ? { hidden: hideUserOverlay() } : { handle, hidden: hideUserOverlay() }\n}\n\nfunction hideUserOverlay(): Hidden | undefined {\n const element = overlayHost()\n if (element === undefined) return undefined\n\n const display = element.style.display\n element.style.display = 'none'\n\n return { element, display }\n}\n\nfunction showUserOverlay(hidden: Hidden | undefined): void {\n if (hidden !== undefined) hidden.element.style.display = hidden.display\n}\n\nasync function prepareElement(core: Core, selector: string | undefined): Promise<Rect | undefined> {\n if (selector === undefined || selector === '') return undefined\n\n const graph = core.buildGraph(root())\n const node = core.resolveNode(graph, selector)\n if (node === null) throw new Error(`selector \"${selector}\" did not match any element on the page`)\n\n let element: Element | null = null\n\n try {\n element = document.querySelector(node.selector)\n } catch {\n element = null\n }\n\n if (element === null) {\n throw new Error(\n `selector \"${selector}\" is inside a shadow root. Measure it, but capture the whole viewport instead.`,\n )\n }\n\n element.scrollIntoView({ block: 'center', inline: 'center' })\n await frames(1)\n\n const rect = element.getBoundingClientRect()\n return { x: rect.left, y: rect.top, w: rect.width, h: rect.height }\n}\n\nfunction root(): Element {\n return document.body ?? document.documentElement\n}\n\nfunction frames(count: number): Promise<void> {\n return new Promise((resolve) => {\n let left = count\n\n const tick = (): void => {\n left -= 1\n if (left <= 0) resolve()\n else requestAnimationFrame(tick)\n }\n\n requestAnimationFrame(tick)\n })\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error && error.message !== '') return error.message\n if (typeof error === 'string' && error !== '') return error\n return 'unexpected error'\n}\n", "import { applyOverlay } from './boot.js'\nimport { installBridge } from './bridge.js'\nimport { loadState } from './store.js'\nimport type { Core } from './types.js'\n\nconst core: Core | undefined = window.__litedesign\n\nif (core === undefined) {\n console.warn('[litedesign] core not found; core.iife.js must be injected first')\n} else {\n installBridge(core)\n void restore(core)\n}\n\n/**\n * A reload drops the overlay with the rest of the DOM. If it was on when the\n * page went away, put it back, so a hot reload during an edit does not\n * silently clear the grid.\n */\nasync function restore(core: Core): Promise<void> {\n const state = await loadState()\n if (!state.enabled) return\n\n await applyOverlay(core, { enabled: true, layers: state.layers })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["root", "core", "root", "core", "core"]
|
|
7
|
+
}
|