@dev-lines/core 0.1.0 → 0.2.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/README.md +18 -6
- package/dist/chunk-6KQDYHPD.js +869 -0
- package/dist/index.d.ts +27 -6
- package/dist/index.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +4 -3
- package/dist/chunk-MUK3OXWT.js +0 -492
package/dist/index.d.ts
CHANGED
|
@@ -7,13 +7,18 @@
|
|
|
7
7
|
* - optional padding edges (paddingX in from each side)
|
|
8
8
|
* - depth-colored outlines around layout containers (flex/grid)
|
|
9
9
|
* - labels: per-box chips (tag · size · layout · custom name) and guide readouts
|
|
10
|
-
* -
|
|
10
|
+
* - box model: padding + flex/grid gap bands on the hovered/locked box
|
|
11
|
+
* - inspect: lock onto a box and walk the tree with the arrow keys
|
|
12
|
+
* - hold Alt: spacing overlay from the hovered/locked box to its containing box
|
|
11
13
|
*
|
|
12
14
|
* Everything lives in one fixed overlay layer with pointer-events: none, so the
|
|
13
|
-
* host app is never touched and teardown is total.
|
|
15
|
+
* host app is never touched and teardown is total. The optional toolbar pill
|
|
16
|
+
* (`toolbar: true`) is the one interactive element, mounted beside the overlay.
|
|
14
17
|
*
|
|
15
18
|
* Commands (when enabled): O toggle outlines · G toggle guides · L cycle labels ·
|
|
16
|
-
*
|
|
19
|
+
* I lock/unlock inspect (arrows walk the tree) · B toggle box-model bands ·
|
|
20
|
+
* C copy handle · Esc unlock/disable · hold Alt for distances.
|
|
21
|
+
* Toggle the overlay with mod+shift+L.
|
|
17
22
|
*/
|
|
18
23
|
type LabelField = "name" | "tag" | "size" | "display" | "depth";
|
|
19
24
|
type LabelMode = "off" | "hover" | "all";
|
|
@@ -28,6 +33,8 @@ interface DevLinesOptions {
|
|
|
28
33
|
lineColor?: string;
|
|
29
34
|
paddingColor?: string;
|
|
30
35
|
measureColor?: string;
|
|
36
|
+
/** Color for flex/grid gap bands. Default "138 224 108" (green). */
|
|
37
|
+
gapColor?: string;
|
|
31
38
|
depthColors?: string[];
|
|
32
39
|
sections?: boolean;
|
|
33
40
|
sectionSelector?: string;
|
|
@@ -36,8 +43,16 @@ interface DevLinesOptions {
|
|
|
36
43
|
nameAttr?: string;
|
|
37
44
|
/** Derive a name (id → aria-label → data-component/testid → heading) when none is set. Default true. */
|
|
38
45
|
autoName?: boolean;
|
|
39
|
-
/** Key to copy the hovered box's handle (name · selector · tag
|
|
46
|
+
/** Key to copy the hovered box's handle (name · selector · tag · size · display · padding · gap). Default "c". */
|
|
40
47
|
copyKey?: string | null;
|
|
48
|
+
/** Key to lock/unlock inspect on the hovered box. Arrow keys then walk the tree. Default "i". */
|
|
49
|
+
inspectKey?: string | null;
|
|
50
|
+
/** Show padding + gap bands on the hovered/locked box. Default true. */
|
|
51
|
+
boxModel?: boolean;
|
|
52
|
+
/** Key to toggle the box-model bands. Default "b". */
|
|
53
|
+
boxModelKey?: string | null;
|
|
54
|
+
/** Mount the on-page toolbar pill (power · guides · outlines · labels · copy · settings). Default false. */
|
|
55
|
+
toolbar?: boolean;
|
|
41
56
|
shortcut?: string | null;
|
|
42
57
|
labelKey?: string | null;
|
|
43
58
|
persist?: boolean;
|
|
@@ -51,15 +66,21 @@ interface DevLinesController {
|
|
|
51
66
|
cycleLabels(mode?: LabelMode): void;
|
|
52
67
|
toggleOutlines(on?: boolean): void;
|
|
53
68
|
toggleGuides(on?: boolean): void;
|
|
54
|
-
/**
|
|
69
|
+
/** Toggle the padding/gap bands on the hovered/locked box. */
|
|
70
|
+
toggleBoxModel(on?: boolean): void;
|
|
71
|
+
/** Lock inspect onto an element (null unlocks; no argument toggles on the hovered box). */
|
|
72
|
+
inspect(el?: Element | null): void;
|
|
73
|
+
/** Live-update visual options (colors, contentWidth, paddingX, labels) and redraw. */
|
|
55
74
|
update(options: Partial<DevLinesOptions>): void;
|
|
56
|
-
/** Copy the hovered (or last-hovered) box's handle for agent handoff. */
|
|
75
|
+
/** Copy the locked/hovered (or last-hovered) box's handle for agent handoff. */
|
|
57
76
|
copy(): void;
|
|
58
77
|
getState(): {
|
|
59
78
|
enabled: boolean;
|
|
60
79
|
outlines: boolean;
|
|
61
80
|
guides: boolean;
|
|
62
81
|
labels: LabelMode;
|
|
82
|
+
boxModel: boolean;
|
|
83
|
+
inspected: Element | null;
|
|
63
84
|
};
|
|
64
85
|
refresh(): void;
|
|
65
86
|
destroy(): void;
|
package/dist/index.js
CHANGED
package/dist/react.js
CHANGED
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-lines/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Framework-agnostic layout debug overlay: center/width/padding guides + depth-colored section outlines, labels, and distances. Zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wa-de (https://www.wa-de.org)",
|
|
7
7
|
"homepage": "https://lines.wiki",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/w-ade/dev-lines.git",
|
|
11
11
|
"directory": "packages/core"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/
|
|
14
|
+
"url": "https://github.com/w-ade/dev-lines/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"debug",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsup src/index.ts src/react.tsx --format esm --dts --clean",
|
|
49
|
+
"build:site": "tsup src/index.ts --format esm --clean false -d ../../demo && mv ../../demo/index.js ../../demo/dev-lines.js",
|
|
49
50
|
"dev": "tsup src/index.ts src/react.tsx --format esm --dts --watch"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
package/dist/chunk-MUK3OXWT.js
DELETED
|
@@ -1,492 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
var STORAGE_KEY = "dev-lines:enabled";
|
|
3
|
-
var Z = 2147483600;
|
|
4
|
-
var DEFAULT_DEPTH_COLORS = [
|
|
5
|
-
"29 29 29",
|
|
6
|
-
// #1d1d1d neutral (graphite)
|
|
7
|
-
"155 164 183",
|
|
8
|
-
// #9ba4b7 neutral (gray)
|
|
9
|
-
"167 125 255",
|
|
10
|
-
// #a77dff purple
|
|
11
|
-
"245 104 104",
|
|
12
|
-
// #f56868 coral
|
|
13
|
-
"237 139 0",
|
|
14
|
-
// #ed8b00 orange
|
|
15
|
-
"241 223 56",
|
|
16
|
-
// #f1df38 yellow
|
|
17
|
-
"138 224 108"
|
|
18
|
-
// #8ae06c green
|
|
19
|
-
];
|
|
20
|
-
var DEFAULTS = {
|
|
21
|
-
lineColor: "245 104 104",
|
|
22
|
-
// #f56868 coral
|
|
23
|
-
paddingColor: "167 125 255",
|
|
24
|
-
// #a77dff purple
|
|
25
|
-
measureColor: "237 139 0",
|
|
26
|
-
// #ed8b00 orange
|
|
27
|
-
sections: true,
|
|
28
|
-
shortcut: "mod+shift+l",
|
|
29
|
-
labelKey: "l",
|
|
30
|
-
copyKey: "c",
|
|
31
|
-
nameAttr: "data-devlines-name",
|
|
32
|
-
autoName: true,
|
|
33
|
-
persist: true,
|
|
34
|
-
start: false
|
|
35
|
-
};
|
|
36
|
-
var DEFAULT_LABELS = {
|
|
37
|
-
mode: "hover",
|
|
38
|
-
show: ["name", "tag", "size", "display"],
|
|
39
|
-
guides: true
|
|
40
|
-
};
|
|
41
|
-
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
42
|
-
function createDevLines(options = {}) {
|
|
43
|
-
if (!isBrowser) return noopController();
|
|
44
|
-
const opts = { ...DEFAULTS, ...options };
|
|
45
|
-
let depthColors = options.depthColors ?? DEFAULT_DEPTH_COLORS;
|
|
46
|
-
const labels = { ...DEFAULT_LABELS, ...options.labels };
|
|
47
|
-
let enabled = false;
|
|
48
|
-
let root = null;
|
|
49
|
-
let sectionsLayer = null;
|
|
50
|
-
let guidesLayer = null;
|
|
51
|
-
let labelsLayer = null;
|
|
52
|
-
let measureLayer = null;
|
|
53
|
-
let scheduled = false;
|
|
54
|
-
let ro = null;
|
|
55
|
-
let mo = null;
|
|
56
|
-
let boxes = [];
|
|
57
|
-
let pointer = null;
|
|
58
|
-
let hoverBox = null;
|
|
59
|
-
let lastHoverBox = null;
|
|
60
|
-
let outlinesOn = opts.sections;
|
|
61
|
-
let guidesOn = true;
|
|
62
|
-
let altDown = false;
|
|
63
|
-
const rgba = (t, a) => `rgb(${t} / ${a})`;
|
|
64
|
-
const colorForDepth = (d) => depthColors[Math.min(d, depthColors.length - 1)];
|
|
65
|
-
function line(css) {
|
|
66
|
-
const el = document.createElement("div");
|
|
67
|
-
Object.assign(el.style, { position: "absolute", ...css });
|
|
68
|
-
return el;
|
|
69
|
-
}
|
|
70
|
-
function chip(text, colorTriplet, left, top) {
|
|
71
|
-
const el = document.createElement("div");
|
|
72
|
-
el.textContent = text;
|
|
73
|
-
Object.assign(el.style, {
|
|
74
|
-
position: "absolute",
|
|
75
|
-
left: `${Math.max(0, left)}px`,
|
|
76
|
-
top: `${Math.max(0, top)}px`,
|
|
77
|
-
font: '500 10px/1.4 "Geist Mono", ui-monospace, monospace',
|
|
78
|
-
letterSpacing: "0.01em",
|
|
79
|
-
color: "#fff",
|
|
80
|
-
background: rgba(colorTriplet, 0.92),
|
|
81
|
-
padding: "1px 5px",
|
|
82
|
-
borderRadius: "4px",
|
|
83
|
-
whiteSpace: "nowrap",
|
|
84
|
-
pointerEvents: "none",
|
|
85
|
-
transform: "translateZ(0)"
|
|
86
|
-
});
|
|
87
|
-
return el;
|
|
88
|
-
}
|
|
89
|
-
function buildGuides() {
|
|
90
|
-
const layer = document.createElement("div");
|
|
91
|
-
Object.assign(layer.style, { position: "absolute", inset: "0" });
|
|
92
|
-
const c = rgba(opts.lineColor, 0.75);
|
|
93
|
-
layer.appendChild(line({ top: "0", bottom: "0", left: "50%", width: "1px", transform: "translateX(-50%)", background: c }));
|
|
94
|
-
layer.appendChild(line({ left: "0", right: "0", top: "50%", height: "1px", transform: "translateY(-50%)", background: c }));
|
|
95
|
-
if (opts.contentWidth && opts.contentWidth > 0) {
|
|
96
|
-
const half = opts.contentWidth / 2;
|
|
97
|
-
layer.appendChild(line({ top: "0", bottom: "0", left: "50%", width: "1px", transform: `translateX(${-half}px)`, background: c }));
|
|
98
|
-
layer.appendChild(line({ top: "0", bottom: "0", left: "50%", width: "1px", transform: `translateX(${half}px)`, background: c }));
|
|
99
|
-
}
|
|
100
|
-
if (opts.paddingX && opts.paddingX > 0) {
|
|
101
|
-
const p = rgba(opts.paddingColor, 0.75);
|
|
102
|
-
layer.appendChild(line({ top: "0", bottom: "0", left: `${opts.paddingX}px`, width: "1px", background: p }));
|
|
103
|
-
layer.appendChild(line({ top: "0", bottom: "0", right: `${opts.paddingX}px`, width: "1px", background: p }));
|
|
104
|
-
}
|
|
105
|
-
return layer;
|
|
106
|
-
}
|
|
107
|
-
function isContainer(el) {
|
|
108
|
-
if (el.getAttribute("data-devlines") === "outline") return true;
|
|
109
|
-
const display = getComputedStyle(el).display;
|
|
110
|
-
return display.includes("flex") || display.includes("grid");
|
|
111
|
-
}
|
|
112
|
-
function collect() {
|
|
113
|
-
const out = [];
|
|
114
|
-
const walk = (el, depth) => {
|
|
115
|
-
if (el === root || el.getAttribute("data-devlines") === "ignore") return;
|
|
116
|
-
const hit = isContainer(el);
|
|
117
|
-
if (hit) out.push({ el, depth });
|
|
118
|
-
const next = hit ? depth + 1 : depth;
|
|
119
|
-
for (const child of el.children) walk(child, next);
|
|
120
|
-
};
|
|
121
|
-
if (opts.sectionSelector) {
|
|
122
|
-
document.querySelectorAll(opts.sectionSelector).forEach((el) => out.push({ el, depth: 0 }));
|
|
123
|
-
} else {
|
|
124
|
-
for (const child of document.body.children) walk(child, 0);
|
|
125
|
-
}
|
|
126
|
-
return out;
|
|
127
|
-
}
|
|
128
|
-
function drawSections() {
|
|
129
|
-
if (!sectionsLayer) return;
|
|
130
|
-
sectionsLayer.replaceChildren();
|
|
131
|
-
boxes = [];
|
|
132
|
-
if (outlinesOn) {
|
|
133
|
-
for (const { el, depth } of collect()) {
|
|
134
|
-
const rect = el.getBoundingClientRect();
|
|
135
|
-
if (rect.width === 0 || rect.height === 0) continue;
|
|
136
|
-
boxes.push({ el, depth, rect });
|
|
137
|
-
const color = colorForDepth(depth);
|
|
138
|
-
const box = document.createElement("div");
|
|
139
|
-
Object.assign(box.style, {
|
|
140
|
-
position: "absolute",
|
|
141
|
-
left: `${rect.left}px`,
|
|
142
|
-
top: `${rect.top}px`,
|
|
143
|
-
width: `${rect.width}px`,
|
|
144
|
-
height: `${rect.height}px`,
|
|
145
|
-
boxShadow: `inset 0 0 0 1px ${rgba(color, 0.6)}`
|
|
146
|
-
});
|
|
147
|
-
sectionsLayer.appendChild(box);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
hoverBox = pointer ? boxAt(pointer.x, pointer.y) : null;
|
|
151
|
-
drawLabels();
|
|
152
|
-
drawMeasure();
|
|
153
|
-
}
|
|
154
|
-
function nameOf(el) {
|
|
155
|
-
const explicit = el.getAttribute(opts.nameAttr);
|
|
156
|
-
if (explicit) return explicit;
|
|
157
|
-
if (!opts.autoName) return null;
|
|
158
|
-
if (el.id) return el.id;
|
|
159
|
-
const aria = el.getAttribute("aria-label");
|
|
160
|
-
if (aria) return aria;
|
|
161
|
-
const comp = el.getAttribute("data-component") || el.getAttribute("data-testid");
|
|
162
|
-
if (comp) return comp;
|
|
163
|
-
const h = el.querySelector("h1,h2,h3,h4,h5,h6");
|
|
164
|
-
const t = h?.textContent?.trim();
|
|
165
|
-
if (t) return t.length > 24 ? t.slice(0, 24) + "\u2026" : t;
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
function selectorFor(el) {
|
|
169
|
-
if (el.id) return `#${CSS.escape(el.id)}`;
|
|
170
|
-
const parts = [];
|
|
171
|
-
let node = el;
|
|
172
|
-
while (node && node !== document.body) {
|
|
173
|
-
const cur = node;
|
|
174
|
-
if (cur.id) {
|
|
175
|
-
parts.unshift(`#${CSS.escape(cur.id)}`);
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
let sel = cur.tagName.toLowerCase();
|
|
179
|
-
const parent = cur.parentElement;
|
|
180
|
-
if (parent) {
|
|
181
|
-
const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
|
|
182
|
-
if (sameTag.length > 1) sel += `:nth-of-type(${sameTag.indexOf(cur) + 1})`;
|
|
183
|
-
}
|
|
184
|
-
parts.unshift(sel);
|
|
185
|
-
node = parent;
|
|
186
|
-
}
|
|
187
|
-
return parts.join(" > ") || el.tagName.toLowerCase();
|
|
188
|
-
}
|
|
189
|
-
function labelText(el, depth, rect) {
|
|
190
|
-
const parts = [];
|
|
191
|
-
for (const f of labels.show) {
|
|
192
|
-
if (f === "name") {
|
|
193
|
-
const nm = nameOf(el);
|
|
194
|
-
if (nm) parts.push(nm);
|
|
195
|
-
} else if (f === "tag") parts.push(`<${el.tagName.toLowerCase()}>`);
|
|
196
|
-
else if (f === "size") parts.push(`${Math.round(rect.width)}\xD7${Math.round(rect.height)}`);
|
|
197
|
-
else if (f === "display") {
|
|
198
|
-
const d = getComputedStyle(el).display;
|
|
199
|
-
parts.push(d.includes("grid") ? "grid" : d.includes("flex") ? "flex" : d);
|
|
200
|
-
} else if (f === "depth") parts.push(`d${depth}`);
|
|
201
|
-
}
|
|
202
|
-
return parts.join(" \xB7 ");
|
|
203
|
-
}
|
|
204
|
-
function boxAt(x, y) {
|
|
205
|
-
let best = null;
|
|
206
|
-
for (const b of boxes) {
|
|
207
|
-
const r = b.rect;
|
|
208
|
-
if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {
|
|
209
|
-
if (!best || b.depth > best.depth) best = b;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return best;
|
|
213
|
-
}
|
|
214
|
-
function ancestorOf(target) {
|
|
215
|
-
let best = null;
|
|
216
|
-
for (const b of boxes) {
|
|
217
|
-
if (b === target || b.depth >= target.depth) continue;
|
|
218
|
-
const r = b.rect, t = target.rect;
|
|
219
|
-
if (r.left <= t.left && r.top <= t.top && r.right >= t.right && r.bottom >= t.bottom) {
|
|
220
|
-
if (!best || b.depth > best.depth) best = b;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return best;
|
|
224
|
-
}
|
|
225
|
-
function drawLabels() {
|
|
226
|
-
if (!labelsLayer) return;
|
|
227
|
-
labelsLayer.replaceChildren();
|
|
228
|
-
if (labels.guides && guidesOn) {
|
|
229
|
-
const cx = window.innerWidth / 2;
|
|
230
|
-
labelsLayer.appendChild(chip(`${Math.round(cx)}`, opts.lineColor, cx + 4, 4));
|
|
231
|
-
if (opts.contentWidth && opts.contentWidth > 0) {
|
|
232
|
-
const half = opts.contentWidth / 2;
|
|
233
|
-
labelsLayer.appendChild(chip(`\u2212${Math.round(half)}`, opts.lineColor, cx - half + 4, 22));
|
|
234
|
-
labelsLayer.appendChild(chip(`+${Math.round(half)}`, opts.lineColor, cx + half + 4, 22));
|
|
235
|
-
}
|
|
236
|
-
if (opts.paddingX && opts.paddingX > 0) {
|
|
237
|
-
labelsLayer.appendChild(chip(`${Math.round(opts.paddingX)}`, opts.paddingColor, opts.paddingX + 4, 4));
|
|
238
|
-
labelsLayer.appendChild(chip(`${Math.round(opts.paddingX)}`, opts.paddingColor, window.innerWidth - opts.paddingX + 4, 4));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (!outlinesOn) return;
|
|
242
|
-
if (labels.mode === "all") {
|
|
243
|
-
for (const b of boxes) labelsLayer.appendChild(chip(labelText(b.el, b.depth, b.rect), colorForDepth(b.depth), b.rect.left, b.rect.top));
|
|
244
|
-
} else if (labels.mode === "hover" && hoverBox) {
|
|
245
|
-
labelsLayer.appendChild(chip(labelText(hoverBox.el, hoverBox.depth, hoverBox.rect), colorForDepth(hoverBox.depth), hoverBox.rect.left, hoverBox.rect.top));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
function measureSeg(x1, y1, x2, y2, value) {
|
|
249
|
-
if (!measureLayer || value < 1) return;
|
|
250
|
-
const horizontal = y1 === y2;
|
|
251
|
-
measureLayer.appendChild(line({
|
|
252
|
-
left: `${Math.min(x1, x2)}px`,
|
|
253
|
-
top: `${Math.min(y1, y2)}px`,
|
|
254
|
-
width: horizontal ? `${Math.abs(x2 - x1)}px` : "1px",
|
|
255
|
-
height: horizontal ? "1px" : `${Math.abs(y2 - y1)}px`,
|
|
256
|
-
background: rgba(opts.measureColor, 0.9)
|
|
257
|
-
}));
|
|
258
|
-
const mx = horizontal ? (x1 + x2) / 2 : x1 + 3;
|
|
259
|
-
const my = horizontal ? y1 + 3 : (y1 + y2) / 2;
|
|
260
|
-
measureLayer.appendChild(chip(`${Math.round(value)}`, opts.measureColor, mx, my));
|
|
261
|
-
}
|
|
262
|
-
function drawMeasure() {
|
|
263
|
-
if (!measureLayer) return;
|
|
264
|
-
measureLayer.replaceChildren();
|
|
265
|
-
if (!altDown || !hoverBox || !outlinesOn) return;
|
|
266
|
-
const t = hoverBox.rect;
|
|
267
|
-
const anc = ancestorOf(hoverBox);
|
|
268
|
-
const c = anc ? anc.rect : new DOMRect(0, 0, window.innerWidth, window.innerHeight);
|
|
269
|
-
const midX = (t.left + t.right) / 2;
|
|
270
|
-
const midY = (t.top + t.bottom) / 2;
|
|
271
|
-
measureSeg(midX, c.top, midX, t.top, t.top - c.top);
|
|
272
|
-
measureSeg(midX, t.bottom, midX, c.bottom, c.bottom - t.bottom);
|
|
273
|
-
measureSeg(c.left, midY, t.left, midY, t.left - c.left);
|
|
274
|
-
measureSeg(t.right, midY, c.right, midY, c.right - t.right);
|
|
275
|
-
}
|
|
276
|
-
function flashCopied(rect) {
|
|
277
|
-
if (!root) return;
|
|
278
|
-
const el = chip("copied \u2713", "34 197 94", rect.left, rect.top - 2);
|
|
279
|
-
root.appendChild(el);
|
|
280
|
-
setTimeout(() => el.remove(), 900);
|
|
281
|
-
}
|
|
282
|
-
function copyHandle() {
|
|
283
|
-
const target = hoverBox ?? lastHoverBox;
|
|
284
|
-
if (!target) return;
|
|
285
|
-
const el = target.el;
|
|
286
|
-
const name = nameOf(el);
|
|
287
|
-
const text = `${name ? `"${name}" \u2014 ` : ""}${selectorFor(el)} (<${el.tagName.toLowerCase()}>)`;
|
|
288
|
-
navigator.clipboard?.writeText(text).then(() => flashCopied(target.rect)).catch(() => {
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
function schedule() {
|
|
292
|
-
if (scheduled) return;
|
|
293
|
-
scheduled = true;
|
|
294
|
-
requestAnimationFrame(() => {
|
|
295
|
-
scheduled = false;
|
|
296
|
-
drawSections();
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
function onPointerMove(e) {
|
|
300
|
-
pointer = { x: e.clientX, y: e.clientY };
|
|
301
|
-
hoverBox = boxAt(e.clientX, e.clientY);
|
|
302
|
-
if (hoverBox) lastHoverBox = hoverBox;
|
|
303
|
-
drawLabels();
|
|
304
|
-
drawMeasure();
|
|
305
|
-
}
|
|
306
|
-
function enable() {
|
|
307
|
-
if (enabled) return;
|
|
308
|
-
enabled = true;
|
|
309
|
-
root = document.createElement("div");
|
|
310
|
-
root.setAttribute("data-devlines", "ignore");
|
|
311
|
-
Object.assign(root.style, { position: "fixed", inset: "0", zIndex: String(Z), pointerEvents: "none" });
|
|
312
|
-
sectionsLayer = document.createElement("div");
|
|
313
|
-
guidesLayer = buildGuides();
|
|
314
|
-
labelsLayer = document.createElement("div");
|
|
315
|
-
measureLayer = document.createElement("div");
|
|
316
|
-
for (const l of [sectionsLayer, labelsLayer, measureLayer]) Object.assign(l.style, { position: "absolute", inset: "0" });
|
|
317
|
-
guidesLayer.style.display = guidesOn ? "" : "none";
|
|
318
|
-
root.append(sectionsLayer, guidesLayer, labelsLayer, measureLayer);
|
|
319
|
-
document.body.appendChild(root);
|
|
320
|
-
window.addEventListener("scroll", schedule, true);
|
|
321
|
-
window.addEventListener("resize", schedule);
|
|
322
|
-
window.addEventListener("pointermove", onPointerMove, true);
|
|
323
|
-
ro = new ResizeObserver(schedule);
|
|
324
|
-
ro.observe(document.documentElement);
|
|
325
|
-
mo = new MutationObserver(schedule);
|
|
326
|
-
mo.observe(document.body, { childList: true, subtree: true, attributes: true });
|
|
327
|
-
drawSections();
|
|
328
|
-
if (opts.persist) try {
|
|
329
|
-
localStorage.setItem(STORAGE_KEY, "1");
|
|
330
|
-
} catch {
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
function disable() {
|
|
334
|
-
if (!enabled) return;
|
|
335
|
-
enabled = false;
|
|
336
|
-
altDown = false;
|
|
337
|
-
window.removeEventListener("scroll", schedule, true);
|
|
338
|
-
window.removeEventListener("resize", schedule);
|
|
339
|
-
window.removeEventListener("pointermove", onPointerMove, true);
|
|
340
|
-
ro?.disconnect();
|
|
341
|
-
mo?.disconnect();
|
|
342
|
-
ro = mo = null;
|
|
343
|
-
pointer = hoverBox = lastHoverBox = null;
|
|
344
|
-
root?.remove();
|
|
345
|
-
root = sectionsLayer = guidesLayer = labelsLayer = measureLayer = null;
|
|
346
|
-
boxes = [];
|
|
347
|
-
if (opts.persist) try {
|
|
348
|
-
localStorage.setItem(STORAGE_KEY, "0");
|
|
349
|
-
} catch {
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
function toggle() {
|
|
353
|
-
enabled ? disable() : enable();
|
|
354
|
-
}
|
|
355
|
-
function cycleLabels(mode) {
|
|
356
|
-
const order = ["off", "hover", "all"];
|
|
357
|
-
labels.mode = mode ?? order[(order.indexOf(labels.mode) + 1) % order.length];
|
|
358
|
-
drawLabels();
|
|
359
|
-
}
|
|
360
|
-
function toggleOutlines(on) {
|
|
361
|
-
outlinesOn = on ?? !outlinesOn;
|
|
362
|
-
drawSections();
|
|
363
|
-
}
|
|
364
|
-
function toggleGuides(on) {
|
|
365
|
-
guidesOn = on ?? !guidesOn;
|
|
366
|
-
if (guidesLayer) guidesLayer.style.display = guidesOn ? "" : "none";
|
|
367
|
-
drawLabels();
|
|
368
|
-
}
|
|
369
|
-
function update(next) {
|
|
370
|
-
if (next.lineColor !== void 0) opts.lineColor = next.lineColor;
|
|
371
|
-
if (next.paddingColor !== void 0) opts.paddingColor = next.paddingColor;
|
|
372
|
-
if (next.measureColor !== void 0) opts.measureColor = next.measureColor;
|
|
373
|
-
if (next.depthColors !== void 0) depthColors = next.depthColors;
|
|
374
|
-
if (next.contentWidth !== void 0) opts.contentWidth = next.contentWidth;
|
|
375
|
-
if (next.paddingX !== void 0) opts.paddingX = next.paddingX;
|
|
376
|
-
if (root && guidesLayer) {
|
|
377
|
-
const fresh = buildGuides();
|
|
378
|
-
fresh.style.display = guidesOn ? "" : "none";
|
|
379
|
-
root.replaceChild(fresh, guidesLayer);
|
|
380
|
-
guidesLayer = fresh;
|
|
381
|
-
}
|
|
382
|
-
drawSections();
|
|
383
|
-
}
|
|
384
|
-
function typingInField() {
|
|
385
|
-
const t = document.activeElement;
|
|
386
|
-
return !!t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable);
|
|
387
|
-
}
|
|
388
|
-
function matchesShortcut(e, combo) {
|
|
389
|
-
const parts = combo.toLowerCase().split("+");
|
|
390
|
-
const key = parts[parts.length - 1];
|
|
391
|
-
const mod = e.metaKey || e.ctrlKey;
|
|
392
|
-
return e.key.toLowerCase() === key && parts.includes("mod") === mod && parts.includes("shift") === e.shiftKey && parts.includes("alt") === e.altKey;
|
|
393
|
-
}
|
|
394
|
-
function onKey(e) {
|
|
395
|
-
if (typingInField()) return;
|
|
396
|
-
if (opts.shortcut && matchesShortcut(e, opts.shortcut)) {
|
|
397
|
-
e.preventDefault();
|
|
398
|
-
toggle();
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
if (!enabled) return;
|
|
402
|
-
if (e.key === "Alt" && !altDown) {
|
|
403
|
-
altDown = true;
|
|
404
|
-
drawMeasure();
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
408
|
-
const k = e.key.toLowerCase();
|
|
409
|
-
if (k === "escape") {
|
|
410
|
-
e.preventDefault();
|
|
411
|
-
disable();
|
|
412
|
-
} else if (k === "o") {
|
|
413
|
-
e.preventDefault();
|
|
414
|
-
toggleOutlines();
|
|
415
|
-
} else if (k === "g") {
|
|
416
|
-
e.preventDefault();
|
|
417
|
-
toggleGuides();
|
|
418
|
-
} else if (opts.labelKey && k === opts.labelKey.toLowerCase()) {
|
|
419
|
-
e.preventDefault();
|
|
420
|
-
cycleLabels();
|
|
421
|
-
} else if (opts.copyKey && k === opts.copyKey.toLowerCase()) {
|
|
422
|
-
e.preventDefault();
|
|
423
|
-
copyHandle();
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
function onKeyUp(e) {
|
|
427
|
-
if (e.key === "Alt" && altDown) {
|
|
428
|
-
altDown = false;
|
|
429
|
-
drawMeasure();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
window.addEventListener("keydown", onKey);
|
|
433
|
-
window.addEventListener("keyup", onKeyUp);
|
|
434
|
-
let startOn = opts.start;
|
|
435
|
-
if (opts.persist) {
|
|
436
|
-
try {
|
|
437
|
-
const saved = localStorage.getItem(STORAGE_KEY);
|
|
438
|
-
if (saved === "1") startOn = true;
|
|
439
|
-
else if (saved === "0") startOn = false;
|
|
440
|
-
} catch {
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (startOn) enable();
|
|
444
|
-
return {
|
|
445
|
-
enable,
|
|
446
|
-
disable,
|
|
447
|
-
toggle,
|
|
448
|
-
isEnabled: () => enabled,
|
|
449
|
-
cycleLabels,
|
|
450
|
-
toggleOutlines,
|
|
451
|
-
toggleGuides,
|
|
452
|
-
update,
|
|
453
|
-
copy: copyHandle,
|
|
454
|
-
getState: () => ({ enabled, outlines: outlinesOn, guides: guidesOn, labels: labels.mode }),
|
|
455
|
-
refresh: drawSections,
|
|
456
|
-
destroy() {
|
|
457
|
-
disable();
|
|
458
|
-
window.removeEventListener("keydown", onKey);
|
|
459
|
-
window.removeEventListener("keyup", onKeyUp);
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
function noopController() {
|
|
464
|
-
return {
|
|
465
|
-
enable() {
|
|
466
|
-
},
|
|
467
|
-
disable() {
|
|
468
|
-
},
|
|
469
|
-
toggle() {
|
|
470
|
-
},
|
|
471
|
-
isEnabled: () => false,
|
|
472
|
-
cycleLabels() {
|
|
473
|
-
},
|
|
474
|
-
toggleOutlines() {
|
|
475
|
-
},
|
|
476
|
-
toggleGuides() {
|
|
477
|
-
},
|
|
478
|
-
update() {
|
|
479
|
-
},
|
|
480
|
-
copy() {
|
|
481
|
-
},
|
|
482
|
-
getState: () => ({ enabled: false, outlines: false, guides: false, labels: "off" }),
|
|
483
|
-
refresh() {
|
|
484
|
-
},
|
|
485
|
-
destroy() {
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export {
|
|
491
|
-
createDevLines
|
|
492
|
-
};
|