@rahmantech/litedesign 0.2.1 → 0.2.2
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/extension/content.js +68 -4
- package/dist/extension/core.iife.js +1 -1
- package/dist/extension/manifest.json +1 -1
- package/dist/extension/popup.js +32 -7
- package/dist/extension/sw.js +33 -0
- package/dist/index.js +55 -11
- package/package.json +2 -2
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
+
// packages/extension/src/diff.ts
|
|
4
|
+
function signatureOf(node) {
|
|
5
|
+
return `${node.tag}|${node.text}`;
|
|
6
|
+
}
|
|
7
|
+
function nodeSignatures(graph) {
|
|
8
|
+
return graph.nodes.map(signatureOf);
|
|
9
|
+
}
|
|
10
|
+
function changedNodes(before, after) {
|
|
11
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const signature of before) counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
13
|
+
const changed = [];
|
|
14
|
+
for (const node of after.nodes) {
|
|
15
|
+
const signature = signatureOf(node);
|
|
16
|
+
const seen = counts.get(signature) ?? 0;
|
|
17
|
+
if (seen > 0) counts.set(signature, seen - 1);
|
|
18
|
+
else changed.push(node);
|
|
19
|
+
}
|
|
20
|
+
return changed;
|
|
21
|
+
}
|
|
22
|
+
|
|
3
23
|
// packages/extension/src/labels.ts
|
|
4
24
|
var STORE_KEY = "litedesign-labels";
|
|
5
25
|
function pageKey() {
|
|
@@ -129,6 +149,7 @@
|
|
|
129
149
|
|
|
130
150
|
// packages/extension/src/store.ts
|
|
131
151
|
var STORAGE_KEY = "litedesign";
|
|
152
|
+
var SNAPSHOT_KEY = "litedesign-snapshot";
|
|
132
153
|
var DEFAULT_LAYERS = [...LAYERS];
|
|
133
154
|
var DEFAULT_GRID = { step: 8, axis: "both" };
|
|
134
155
|
var AXES = ["vertical", "horizontal", "both"];
|
|
@@ -186,6 +207,30 @@
|
|
|
186
207
|
function isLayerName(value) {
|
|
187
208
|
return typeof value === "string" && LAYERS.includes(value);
|
|
188
209
|
}
|
|
210
|
+
function loadSnapshot() {
|
|
211
|
+
try {
|
|
212
|
+
const raw = sessionStorage.getItem(SNAPSHOT_KEY);
|
|
213
|
+
if (raw === null) return null;
|
|
214
|
+
const parsed = JSON.parse(raw);
|
|
215
|
+
if (!Array.isArray(parsed)) return null;
|
|
216
|
+
const signatures = parsed.filter((entry) => typeof entry === "string");
|
|
217
|
+
return signatures.length === parsed.length ? signatures : null;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function saveSnapshot(graph) {
|
|
223
|
+
try {
|
|
224
|
+
sessionStorage.setItem(SNAPSHOT_KEY, JSON.stringify(nodeSignatures(graph)));
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function clearSnapshot() {
|
|
229
|
+
try {
|
|
230
|
+
sessionStorage.removeItem(SNAPSHOT_KEY);
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
189
234
|
|
|
190
235
|
// packages/extension/src/toolbar.ts
|
|
191
236
|
var STYLE = `
|
|
@@ -524,18 +569,21 @@
|
|
|
524
569
|
}
|
|
525
570
|
const stored = await loadState();
|
|
526
571
|
const layers = options.layers ?? stored.layers;
|
|
572
|
+
let autoFocus = null;
|
|
527
573
|
if (window.__litedesign_state__ === "active") {
|
|
528
574
|
window.__litedesign_layers__?.(layers);
|
|
529
575
|
} else {
|
|
530
576
|
window.__litedesign_state__ = "starting";
|
|
531
577
|
try {
|
|
532
|
-
mount(core2, layers, stored.grid);
|
|
578
|
+
autoFocus = mount(core2, layers, stored.grid);
|
|
533
579
|
} catch (error) {
|
|
534
580
|
window.__litedesign_state__ = void 0;
|
|
535
581
|
throw error;
|
|
536
582
|
}
|
|
537
583
|
}
|
|
538
|
-
if (options.focus !== void 0)
|
|
584
|
+
if (options.focus !== void 0 && autoFocus === null) {
|
|
585
|
+
window.__litedesign_set_focus__?.(options.focus);
|
|
586
|
+
}
|
|
539
587
|
return {
|
|
540
588
|
enabled: overlayHost() !== void 0,
|
|
541
589
|
layers,
|
|
@@ -570,17 +618,22 @@
|
|
|
570
618
|
window.__litedesign_set_focus__ = void 0;
|
|
571
619
|
window.__litedesign_focus_selector__ = void 0;
|
|
572
620
|
window.__litedesign_state__ = void 0;
|
|
621
|
+
clearSnapshot();
|
|
573
622
|
void saveState({ enabled: false, layers, focus: null, grid });
|
|
574
623
|
};
|
|
575
624
|
const refresh = () => {
|
|
625
|
+
const before2 = nodeSignatures(graph);
|
|
576
626
|
graph = core2.buildGraph(root2);
|
|
577
627
|
if (focusId !== null && !graph.nodes.some((node) => node.id === focusId)) {
|
|
578
628
|
focusSelector = null;
|
|
579
629
|
focusId = null;
|
|
580
630
|
handle?.setFocus(null);
|
|
581
631
|
}
|
|
632
|
+
const [firstChanged2] = changedNodes(before2, graph);
|
|
633
|
+
if (firstChanged2 !== void 0) setFocus(firstChanged2.selector);
|
|
582
634
|
handle?.update(graph);
|
|
583
635
|
toolbar.setStats(graph.nodes.length, graph.issues.length, graph.truncated);
|
|
636
|
+
saveSnapshot(graph);
|
|
584
637
|
};
|
|
585
638
|
const setLayers = (next) => {
|
|
586
639
|
layers = next;
|
|
@@ -707,6 +760,15 @@
|
|
|
707
760
|
labels = next;
|
|
708
761
|
applyMarks();
|
|
709
762
|
});
|
|
763
|
+
const before = loadSnapshot();
|
|
764
|
+
const changed = before !== null ? changedNodes(before, graph) : [];
|
|
765
|
+
let autoFocus = null;
|
|
766
|
+
const [firstChanged] = changed;
|
|
767
|
+
if (firstChanged !== void 0) {
|
|
768
|
+
autoFocus = firstChanged.selector;
|
|
769
|
+
setFocus(firstChanged.selector);
|
|
770
|
+
}
|
|
771
|
+
saveSnapshot(graph);
|
|
710
772
|
mountedHost = handle.host;
|
|
711
773
|
window.__litedesign_layers__ = setLayers;
|
|
712
774
|
window.__litedesign_set_focus__ = setFocus;
|
|
@@ -714,6 +776,7 @@
|
|
|
714
776
|
window.__litedesign_teardown__ = teardown;
|
|
715
777
|
window.__litedesign_state__ = "active";
|
|
716
778
|
void saveState({ enabled: true, layers, focus: focusSelector, grid });
|
|
779
|
+
return autoFocus;
|
|
717
780
|
}
|
|
718
781
|
|
|
719
782
|
// packages/extension/src/bridge.ts
|
|
@@ -740,9 +803,10 @@
|
|
|
740
803
|
switch (action) {
|
|
741
804
|
case "ping":
|
|
742
805
|
return { ok: true, result: true };
|
|
743
|
-
case "toggle":
|
|
806
|
+
case "toggle": {
|
|
744
807
|
await boot(core2);
|
|
745
|
-
return { ok: true };
|
|
808
|
+
return { ok: true, result: { enabled: window.__litedesign_state__ === "active" } };
|
|
809
|
+
}
|
|
746
810
|
case "overlay":
|
|
747
811
|
return {
|
|
748
812
|
ok: true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "litedesign",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Grid, gap, and alignment on the real web page.",
|
|
6
6
|
"permissions": ["activeTab", "scripting", "storage"],
|
|
7
7
|
"host_permissions": ["http://*/*", "https://*/*"],
|
package/dist/extension/popup.js
CHANGED
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
var watchButton = element("watch");
|
|
7
7
|
var overlayButton = element("overlay");
|
|
8
8
|
var tab;
|
|
9
|
-
watchButton.addEventListener("click", () => void
|
|
9
|
+
watchButton.addEventListener("click", () => void toggleWatch());
|
|
10
10
|
overlayButton.addEventListener("click", () => void toggleOverlay());
|
|
11
11
|
void init();
|
|
12
|
+
document.addEventListener("visibilitychange", () => {
|
|
13
|
+
if (!document.hidden) void init();
|
|
14
|
+
});
|
|
12
15
|
async function init() {
|
|
13
16
|
const [active] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
14
17
|
tab = active;
|
|
@@ -17,14 +20,22 @@
|
|
|
17
20
|
overlayButton.disabled = active?.id === void 0;
|
|
18
21
|
await refresh();
|
|
19
22
|
}
|
|
20
|
-
async function
|
|
23
|
+
async function toggleWatch() {
|
|
21
24
|
if (tab?.id === void 0) return;
|
|
22
|
-
const
|
|
25
|
+
const status = await ask({ type: "status" });
|
|
26
|
+
if (!status.ok) {
|
|
27
|
+
setStatus(status.error, true);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const watching = status.result.page?.tabId === tab.id;
|
|
31
|
+
const reply = await ask(
|
|
32
|
+
watching ? { type: "unwatch", tabId: tab.id } : { type: "watch", tabId: tab.id }
|
|
33
|
+
);
|
|
23
34
|
if (!reply.ok) {
|
|
24
35
|
setStatus(reply.error, true);
|
|
25
36
|
return;
|
|
26
37
|
}
|
|
27
|
-
pageLine.textContent = reply.result.url;
|
|
38
|
+
if (!watching && reply.result !== null) pageLine.textContent = reply.result.url;
|
|
28
39
|
await refresh();
|
|
29
40
|
}
|
|
30
41
|
async function toggleOverlay() {
|
|
@@ -34,7 +45,12 @@
|
|
|
34
45
|
setStatus(reply.error, true);
|
|
35
46
|
return;
|
|
36
47
|
}
|
|
37
|
-
|
|
48
|
+
await refresh();
|
|
49
|
+
if (reply.result.enabled) {
|
|
50
|
+
window.close();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
setStatus("overlay off");
|
|
38
54
|
}
|
|
39
55
|
async function refresh() {
|
|
40
56
|
const reply = await ask({ type: "status" });
|
|
@@ -43,14 +59,23 @@
|
|
|
43
59
|
return;
|
|
44
60
|
}
|
|
45
61
|
const status = reply.result;
|
|
62
|
+
if (!status.bridge) scheduleRecheck();
|
|
46
63
|
const watching = tab?.id !== void 0 && status.page?.tabId === tab.id;
|
|
47
|
-
watchButton.textContent = watching ? "
|
|
48
|
-
watchButton.disabled = tab?.id === void 0
|
|
64
|
+
watchButton.textContent = watching ? "Unwatch this tab" : "Watch this tab";
|
|
65
|
+
watchButton.disabled = tab?.id === void 0;
|
|
49
66
|
setStatus(
|
|
50
67
|
status.bridge ? `bridge online \xB7 port ${status.port}` : "bridge offline \xB7 run: litedesign ws start",
|
|
51
68
|
!status.bridge
|
|
52
69
|
);
|
|
53
70
|
}
|
|
71
|
+
function scheduleRecheck() {
|
|
72
|
+
if (recheckTimer !== void 0) return;
|
|
73
|
+
recheckTimer = setTimeout(() => {
|
|
74
|
+
recheckTimer = void 0;
|
|
75
|
+
void refresh();
|
|
76
|
+
}, 1e3);
|
|
77
|
+
}
|
|
78
|
+
var recheckTimer;
|
|
54
79
|
function setStatus(text, bad = false) {
|
|
55
80
|
statusLine.textContent = text;
|
|
56
81
|
statusLine.classList.toggle("bad", bad);
|
package/dist/extension/sw.js
CHANGED
|
@@ -46,6 +46,9 @@ async function answer(message) {
|
|
|
46
46
|
case "watch":
|
|
47
47
|
if (typeof request.tabId !== "number") throw new Error("watch needs a tab id");
|
|
48
48
|
return { ok: true, result: await watch(request.tabId) };
|
|
49
|
+
case "unwatch":
|
|
50
|
+
if (typeof request.tabId !== "number") throw new Error("unwatch needs a tab id");
|
|
51
|
+
return { ok: true, result: await unwatch(request.tabId) };
|
|
49
52
|
case "overlay":
|
|
50
53
|
if (typeof request.tabId !== "number") throw new Error("overlay needs a tab id");
|
|
51
54
|
return { ok: true, result: await toggleOverlay(request.tabId) };
|
|
@@ -74,6 +77,18 @@ async function watch(tabId) {
|
|
|
74
77
|
await applyStatus(tabId);
|
|
75
78
|
return page;
|
|
76
79
|
}
|
|
80
|
+
async function unwatch(tabId) {
|
|
81
|
+
if (current?.tabId !== tabId) return null;
|
|
82
|
+
try {
|
|
83
|
+
await chrome.tabs.sendMessage(tabId, { action: "overlay", payload: { enabled: false } });
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
current = void 0;
|
|
87
|
+
await savePage(void 0);
|
|
88
|
+
send({ type: "bye" });
|
|
89
|
+
await applyStatus();
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
77
92
|
async function toggleOverlay(tabId) {
|
|
78
93
|
if (current?.tabId !== tabId) await watch(tabId);
|
|
79
94
|
return toTab("toggle");
|
|
@@ -87,7 +102,25 @@ chrome.tabs.onRemoved.addListener((tabId) => {
|
|
|
87
102
|
chrome.tabs.onUpdated.addListener((tabId, info) => {
|
|
88
103
|
if (info.status !== "complete" || current?.tabId !== tabId) return;
|
|
89
104
|
void ensureContent(tabId).catch(() => void 0);
|
|
105
|
+
void refreshWatchedPage(tabId).catch(() => void 0);
|
|
90
106
|
});
|
|
107
|
+
async function refreshWatchedPage(tabId) {
|
|
108
|
+
const tab = await chrome.tabs.get(tabId);
|
|
109
|
+
if (current?.tabId !== tabId) return;
|
|
110
|
+
if (tab.url === current.url) return;
|
|
111
|
+
if (!isSupported(tab.url)) {
|
|
112
|
+
current = void 0;
|
|
113
|
+
await savePage(void 0);
|
|
114
|
+
send({ type: "bye" });
|
|
115
|
+
await applyStatus();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const page = { tabId, url: tab.url ?? "", title: tab.title ?? "" };
|
|
119
|
+
current = page;
|
|
120
|
+
await savePage(page);
|
|
121
|
+
send({ type: "hello", ...page, version: chrome.runtime.getManifest().version });
|
|
122
|
+
await applyStatus();
|
|
123
|
+
}
|
|
91
124
|
function connect() {
|
|
92
125
|
if (socket !== void 0 && socket.readyState <= WebSocket.OPEN) return;
|
|
93
126
|
const next = new WebSocket(`ws://127.0.0.1:${port}`);
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ function isMode(value) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// packages/cli/src/version.ts
|
|
47
|
-
var VERSION = "0.2.
|
|
47
|
+
var VERSION = "0.2.2";
|
|
48
48
|
|
|
49
49
|
// packages/cli/src/ws.ts
|
|
50
50
|
import { spawn } from "node:child_process";
|
|
@@ -1089,6 +1089,7 @@ function contrastRatio(a, b) {
|
|
|
1089
1089
|
return (lighter + 0.05) / (darker + 0.05);
|
|
1090
1090
|
}
|
|
1091
1091
|
var SPACING_SCALE = 4;
|
|
1092
|
+
var OVERLAY_HOST_ID = "__litedesign_overlay__";
|
|
1092
1093
|
function defineRule(id, run2) {
|
|
1093
1094
|
return Object.assign(run2, { id });
|
|
1094
1095
|
}
|
|
@@ -1329,7 +1330,14 @@ var ExtensionDriver = class {
|
|
|
1329
1330
|
}
|
|
1330
1331
|
async open(url) {
|
|
1331
1332
|
this.#target = url;
|
|
1332
|
-
|
|
1333
|
+
const cached = this.#hub.find(url);
|
|
1334
|
+
if (cached !== void 0) {
|
|
1335
|
+
try {
|
|
1336
|
+
await this.#hub.request(cached.tabId, "ping", void 0);
|
|
1337
|
+
return;
|
|
1338
|
+
} catch {
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1333
1341
|
if (!this.#hub.hasClient()) throw new AwaitingUserError(url);
|
|
1334
1342
|
const page = await this.#hub.requestAny("attach");
|
|
1335
1343
|
if (!sameUrl(page.url, url)) throw new MismatchError(url, page.url);
|
|
@@ -1413,6 +1421,10 @@ import { existsSync as existsSync5 } from "node:fs";
|
|
|
1413
1421
|
import path4 from "node:path";
|
|
1414
1422
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1415
1423
|
var DEFAULT_VIEWPORT = { width: 1280, height: 800 };
|
|
1424
|
+
var POLL_MS = 150;
|
|
1425
|
+
function sleep(ms) {
|
|
1426
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1427
|
+
}
|
|
1416
1428
|
var PlaywrightDriver = class {
|
|
1417
1429
|
#playwright;
|
|
1418
1430
|
#corePath;
|
|
@@ -1431,6 +1443,25 @@ var PlaywrightDriver = class {
|
|
|
1431
1443
|
await page.goto(url, { waitUntil: "load" });
|
|
1432
1444
|
await this.#inject(page);
|
|
1433
1445
|
}
|
|
1446
|
+
/**
|
|
1447
|
+
* "Ready" means the page was read, not that `open` once succeeded: the tab
|
|
1448
|
+
* can be closed or navigated away after that. The loop checks liveness and
|
|
1449
|
+
* the URL the session asked for.
|
|
1450
|
+
*/
|
|
1451
|
+
async waitForPage(url, timeoutMs) {
|
|
1452
|
+
for (let waited = 0; waited < timeoutMs; waited += POLL_MS) {
|
|
1453
|
+
const page = this.#page;
|
|
1454
|
+
if (page !== void 0 && !page.isClosed() && sameUrl(page.url(), url)) {
|
|
1455
|
+
return { status: "ready", url: page.url() };
|
|
1456
|
+
}
|
|
1457
|
+
const current2 = this.#page;
|
|
1458
|
+
if (current2 !== void 0 && !current2.isClosed()) {
|
|
1459
|
+
return { status: "mismatch", url: current2.url() };
|
|
1460
|
+
}
|
|
1461
|
+
await sleep(POLL_MS);
|
|
1462
|
+
}
|
|
1463
|
+
return { status: "timeout" };
|
|
1464
|
+
}
|
|
1434
1465
|
async graph() {
|
|
1435
1466
|
const page = await this.#requirePage();
|
|
1436
1467
|
await this.#inject(page);
|
|
@@ -1520,10 +1551,11 @@ var PlaywrightDriver = class {
|
|
|
1520
1551
|
}
|
|
1521
1552
|
/**
|
|
1522
1553
|
* Labels are marks the human makes through the toolbar; a headless page has
|
|
1523
|
-
* no human in it, so
|
|
1554
|
+
* no human in it. `null` says "cannot know", so `inspect` can report the
|
|
1555
|
+
* truth instead of a `count: 0` that reads as "no labels pinned".
|
|
1524
1556
|
*/
|
|
1525
1557
|
async labels() {
|
|
1526
|
-
return
|
|
1558
|
+
return null;
|
|
1527
1559
|
}
|
|
1528
1560
|
async overlay(options) {
|
|
1529
1561
|
const page = await this.#requirePage();
|
|
@@ -1536,7 +1568,7 @@ var PlaywrightDriver = class {
|
|
|
1536
1568
|
target.__litedesign_handle__ = void 0;
|
|
1537
1569
|
if (!input.enabled) {
|
|
1538
1570
|
target.__litedesign_focus__ = null;
|
|
1539
|
-
return {
|
|
1571
|
+
return { applied: false, focus: null };
|
|
1540
1572
|
}
|
|
1541
1573
|
const graph = target.__litedesign.buildGraph(document.body);
|
|
1542
1574
|
const handle = target.__litedesign.render(graph, { layers: input.layers });
|
|
@@ -1553,11 +1585,19 @@ var PlaywrightDriver = class {
|
|
|
1553
1585
|
element?.scrollIntoView({ block: "center", inline: "center", behavior: "instant" });
|
|
1554
1586
|
}
|
|
1555
1587
|
}
|
|
1556
|
-
return {
|
|
1588
|
+
return { applied: true, focus };
|
|
1557
1589
|
},
|
|
1558
1590
|
{ enabled: options.enabled, layers, focus: options.focus }
|
|
1559
1591
|
);
|
|
1560
|
-
|
|
1592
|
+
const attached = await page.evaluate(
|
|
1593
|
+
(hostId) => document.getElementById(hostId) !== null,
|
|
1594
|
+
OVERLAY_HOST_ID
|
|
1595
|
+
);
|
|
1596
|
+
return {
|
|
1597
|
+
enabled: attached,
|
|
1598
|
+
layers: attached ? layers : [],
|
|
1599
|
+
focus: attached ? result.focus : null
|
|
1600
|
+
};
|
|
1561
1601
|
}
|
|
1562
1602
|
async close() {
|
|
1563
1603
|
await this.#context?.close().catch(() => void 0);
|
|
@@ -1723,9 +1763,6 @@ async function waitForPage(timeoutMs) {
|
|
|
1723
1763
|
}
|
|
1724
1764
|
const expected = session.url;
|
|
1725
1765
|
const driver = session.driver;
|
|
1726
|
-
if (driver.waitForPage === void 0) {
|
|
1727
|
-
return { status: "ready", url: expected, expected };
|
|
1728
|
-
}
|
|
1729
1766
|
const result = await driver.waitForPage(expected, timeoutMs);
|
|
1730
1767
|
if (result.status === "ready") {
|
|
1731
1768
|
session.ready = true;
|
|
@@ -1791,6 +1828,7 @@ function formatTree(graph, maxLines = MAX_TREE_LINES) {
|
|
|
1791
1828
|
return lines.join("\n");
|
|
1792
1829
|
}
|
|
1793
1830
|
function labelsSummary(marks) {
|
|
1831
|
+
if (marks === null) return null;
|
|
1794
1832
|
const names = [];
|
|
1795
1833
|
for (const mark of marks) {
|
|
1796
1834
|
if (!names.includes(mark.label)) names.push(mark.label);
|
|
@@ -1883,7 +1921,7 @@ async function inspectTool(input) {
|
|
|
1883
1921
|
nodes: graph.nodes.length,
|
|
1884
1922
|
issues: graph.issues.length,
|
|
1885
1923
|
truncated: graph.truncated,
|
|
1886
|
-
labels
|
|
1924
|
+
labels: labels ?? "unknown: this driver cannot read the toolbar"
|
|
1887
1925
|
},
|
|
1888
1926
|
tree: formatTree(graph)
|
|
1889
1927
|
};
|
|
@@ -1896,6 +1934,12 @@ async function inspectTool(input) {
|
|
|
1896
1934
|
async function labelsTool() {
|
|
1897
1935
|
const driver = requirePage();
|
|
1898
1936
|
const labels = await driver.labels();
|
|
1937
|
+
if (labels === null) {
|
|
1938
|
+
return textResult({
|
|
1939
|
+
labels: null,
|
|
1940
|
+
message: "the active driver cannot see the toolbar (headless page). Open the page in your own Chrome to read labels."
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1899
1943
|
return textResult({
|
|
1900
1944
|
labels,
|
|
1901
1945
|
count: labels.length
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rahmantech/litedesign",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Grid, gap, and alignment on the real web page, plus an MCP bridge for agents.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Muhammad Abdurrahman",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"zod": "^4.6.7"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@litedesign/core": "0.2.
|
|
25
|
+
"@litedesign/core": "0.2.2",
|
|
26
26
|
"@types/ws": "^8.18.2"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|