@designtools/shadows 0.1.10 → 0.1.11
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/inject/selection.js +332 -0
- package/package.json +2 -2
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
let selectionMode = false;
|
|
2
|
+
let highlightOverlay = null;
|
|
3
|
+
let tooltip = null;
|
|
4
|
+
let selectedOverlay = null;
|
|
5
|
+
let hoveredElement = null;
|
|
6
|
+
let selectedElement = null;
|
|
7
|
+
let selectedDomPath = null;
|
|
8
|
+
let overlayRafId = null;
|
|
9
|
+
const previewBackups = /* @__PURE__ */ new Map();
|
|
10
|
+
function createOverlays() {
|
|
11
|
+
highlightOverlay = document.createElement("div");
|
|
12
|
+
highlightOverlay.id = "tool-highlight";
|
|
13
|
+
Object.assign(highlightOverlay.style, {
|
|
14
|
+
position: "fixed",
|
|
15
|
+
pointerEvents: "none",
|
|
16
|
+
border: "2px solid #3b82f6",
|
|
17
|
+
backgroundColor: "rgba(59, 130, 246, 0.08)",
|
|
18
|
+
borderRadius: "2px",
|
|
19
|
+
zIndex: "99999",
|
|
20
|
+
display: "none",
|
|
21
|
+
transition: "all 0.1s ease"
|
|
22
|
+
});
|
|
23
|
+
document.body.appendChild(highlightOverlay);
|
|
24
|
+
tooltip = document.createElement("div");
|
|
25
|
+
tooltip.id = "tool-tooltip";
|
|
26
|
+
Object.assign(tooltip.style, {
|
|
27
|
+
position: "fixed",
|
|
28
|
+
pointerEvents: "none",
|
|
29
|
+
backgroundColor: "#1e1e2e",
|
|
30
|
+
color: "#cdd6f4",
|
|
31
|
+
padding: "3px 8px",
|
|
32
|
+
borderRadius: "4px",
|
|
33
|
+
fontSize: "11px",
|
|
34
|
+
fontFamily: "ui-monospace, monospace",
|
|
35
|
+
zIndex: "100000",
|
|
36
|
+
display: "none",
|
|
37
|
+
whiteSpace: "nowrap",
|
|
38
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
|
|
39
|
+
});
|
|
40
|
+
document.body.appendChild(tooltip);
|
|
41
|
+
selectedOverlay = document.createElement("div");
|
|
42
|
+
selectedOverlay.id = "tool-selected";
|
|
43
|
+
Object.assign(selectedOverlay.style, {
|
|
44
|
+
position: "fixed",
|
|
45
|
+
pointerEvents: "none",
|
|
46
|
+
border: "2px solid #f59e0b",
|
|
47
|
+
backgroundColor: "rgba(245, 158, 11, 0.06)",
|
|
48
|
+
borderRadius: "2px",
|
|
49
|
+
zIndex: "99998",
|
|
50
|
+
display: "none"
|
|
51
|
+
});
|
|
52
|
+
document.body.appendChild(selectedOverlay);
|
|
53
|
+
}
|
|
54
|
+
function getElementName(el) {
|
|
55
|
+
const slot = el.getAttribute("data-slot");
|
|
56
|
+
if (slot) {
|
|
57
|
+
return slot.charAt(0).toUpperCase() + slot.slice(1);
|
|
58
|
+
}
|
|
59
|
+
return `<${el.tagName.toLowerCase()}>`;
|
|
60
|
+
}
|
|
61
|
+
function getDomPath(el) {
|
|
62
|
+
const parts = [];
|
|
63
|
+
let current = el;
|
|
64
|
+
while (current && current !== document.body) {
|
|
65
|
+
let selector = current.tagName.toLowerCase();
|
|
66
|
+
const slot = current.getAttribute("data-slot");
|
|
67
|
+
if (slot) {
|
|
68
|
+
selector = `[data-slot="${slot}"]`;
|
|
69
|
+
} else if (current.id) {
|
|
70
|
+
selector += `#${current.id}`;
|
|
71
|
+
} else if (current.className && typeof current.className === "string") {
|
|
72
|
+
const cls = current.className.split(" ")[0];
|
|
73
|
+
if (cls) selector += `.${cls}`;
|
|
74
|
+
}
|
|
75
|
+
parts.unshift(selector);
|
|
76
|
+
current = current.parentElement;
|
|
77
|
+
}
|
|
78
|
+
return parts.join(" > ");
|
|
79
|
+
}
|
|
80
|
+
function extractElementData(el) {
|
|
81
|
+
const computed = getComputedStyle(el);
|
|
82
|
+
const rect = el.getBoundingClientRect();
|
|
83
|
+
const relevantProps = [
|
|
84
|
+
"color",
|
|
85
|
+
"backgroundColor",
|
|
86
|
+
"borderColor",
|
|
87
|
+
"borderRadius",
|
|
88
|
+
"padding",
|
|
89
|
+
"margin",
|
|
90
|
+
"gap",
|
|
91
|
+
"fontSize",
|
|
92
|
+
"fontWeight",
|
|
93
|
+
"lineHeight",
|
|
94
|
+
"letterSpacing",
|
|
95
|
+
"display",
|
|
96
|
+
"flexDirection",
|
|
97
|
+
"alignItems",
|
|
98
|
+
"justifyContent",
|
|
99
|
+
"width",
|
|
100
|
+
"height",
|
|
101
|
+
"boxShadow"
|
|
102
|
+
];
|
|
103
|
+
const computedStyles = {};
|
|
104
|
+
for (const prop of relevantProps) {
|
|
105
|
+
computedStyles[prop] = computed.getPropertyValue(
|
|
106
|
+
prop.replace(/([A-Z])/g, "-$1").toLowerCase()
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const attributes = {};
|
|
110
|
+
for (const attr of Array.from(el.attributes)) {
|
|
111
|
+
if (attr.name.startsWith("data-")) {
|
|
112
|
+
attributes[attr.name] = attr.value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
tag: el.tagName.toLowerCase(),
|
|
117
|
+
className: (el.getAttribute("class") || "").trim(),
|
|
118
|
+
dataSlot: el.getAttribute("data-slot"),
|
|
119
|
+
dataVariant: el.getAttribute("data-variant"),
|
|
120
|
+
dataSize: el.getAttribute("data-size"),
|
|
121
|
+
computedStyles,
|
|
122
|
+
boundingRect: rect,
|
|
123
|
+
domPath: getDomPath(el),
|
|
124
|
+
textContent: (el.textContent || "").trim().slice(0, 100),
|
|
125
|
+
attributes
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function positionOverlay(overlay, rect) {
|
|
129
|
+
Object.assign(overlay.style, {
|
|
130
|
+
left: `${rect.left}px`,
|
|
131
|
+
top: `${rect.top}px`,
|
|
132
|
+
width: `${rect.width}px`,
|
|
133
|
+
height: `${rect.height}px`,
|
|
134
|
+
display: "block"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function findSelectableElement(target) {
|
|
138
|
+
let el = target;
|
|
139
|
+
while (el && el !== document.body) {
|
|
140
|
+
if (el.getAttribute("data-slot")) return el;
|
|
141
|
+
el = el.parentElement;
|
|
142
|
+
}
|
|
143
|
+
return target;
|
|
144
|
+
}
|
|
145
|
+
function onMouseMove(e) {
|
|
146
|
+
if (!selectionMode || !highlightOverlay || !tooltip) return;
|
|
147
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
148
|
+
if (!el || el === highlightOverlay || el === tooltip || el === selectedOverlay) return;
|
|
149
|
+
const selectable = findSelectableElement(el);
|
|
150
|
+
if (selectable === hoveredElement) return;
|
|
151
|
+
hoveredElement = selectable;
|
|
152
|
+
const rect = selectable.getBoundingClientRect();
|
|
153
|
+
positionOverlay(highlightOverlay, rect);
|
|
154
|
+
const name = getElementName(selectable);
|
|
155
|
+
tooltip.textContent = name;
|
|
156
|
+
tooltip.style.display = "block";
|
|
157
|
+
tooltip.style.left = `${rect.left}px`;
|
|
158
|
+
tooltip.style.top = `${Math.max(0, rect.top - 24)}px`;
|
|
159
|
+
}
|
|
160
|
+
function onMouseLeave() {
|
|
161
|
+
if (!highlightOverlay || !tooltip) return;
|
|
162
|
+
highlightOverlay.style.display = "none";
|
|
163
|
+
tooltip.style.display = "none";
|
|
164
|
+
hoveredElement = null;
|
|
165
|
+
}
|
|
166
|
+
function onClick(e) {
|
|
167
|
+
if (!selectionMode) return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
e.stopPropagation();
|
|
170
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
171
|
+
if (!el || el === highlightOverlay || el === tooltip || el === selectedOverlay) return;
|
|
172
|
+
const selectable = findSelectableElement(el);
|
|
173
|
+
selectElement(selectable);
|
|
174
|
+
}
|
|
175
|
+
function selectElement(el) {
|
|
176
|
+
selectedElement = el;
|
|
177
|
+
selectedDomPath = getDomPath(el);
|
|
178
|
+
const data = extractElementData(el);
|
|
179
|
+
if (selectedOverlay) {
|
|
180
|
+
positionOverlay(selectedOverlay, data.boundingRect);
|
|
181
|
+
}
|
|
182
|
+
startOverlayTracking();
|
|
183
|
+
window.parent.postMessage(
|
|
184
|
+
{ type: "tool:elementSelected", data },
|
|
185
|
+
"*"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function reselectCurrentElement() {
|
|
189
|
+
if (!selectedDomPath) return;
|
|
190
|
+
const el = document.querySelector(selectedDomPath);
|
|
191
|
+
if (el) {
|
|
192
|
+
selectedElement = el;
|
|
193
|
+
const data = extractElementData(el);
|
|
194
|
+
if (selectedOverlay) {
|
|
195
|
+
positionOverlay(selectedOverlay, data.boundingRect);
|
|
196
|
+
}
|
|
197
|
+
window.parent.postMessage(
|
|
198
|
+
{ type: "tool:elementSelected", data },
|
|
199
|
+
"*"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function startOverlayTracking() {
|
|
204
|
+
if (overlayRafId) cancelAnimationFrame(overlayRafId);
|
|
205
|
+
let lastRect = "";
|
|
206
|
+
function tick() {
|
|
207
|
+
if (selectedElement && selectedOverlay) {
|
|
208
|
+
if (!document.contains(selectedElement)) {
|
|
209
|
+
if (selectedDomPath) {
|
|
210
|
+
const newEl = document.querySelector(selectedDomPath);
|
|
211
|
+
if (newEl) {
|
|
212
|
+
selectedElement = newEl;
|
|
213
|
+
reselectCurrentElement();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (selectedElement && document.contains(selectedElement)) {
|
|
218
|
+
const rect = selectedElement.getBoundingClientRect();
|
|
219
|
+
const key = `${rect.left},${rect.top},${rect.width},${rect.height}`;
|
|
220
|
+
if (key !== lastRect) {
|
|
221
|
+
lastRect = key;
|
|
222
|
+
positionOverlay(selectedOverlay, rect);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
overlayRafId = requestAnimationFrame(tick);
|
|
227
|
+
}
|
|
228
|
+
tick();
|
|
229
|
+
}
|
|
230
|
+
function onMessage(e) {
|
|
231
|
+
const msg = e.data;
|
|
232
|
+
if (!msg || !msg.type || !msg.type.startsWith("tool:")) return;
|
|
233
|
+
switch (msg.type) {
|
|
234
|
+
case "tool:enterSelectionMode":
|
|
235
|
+
selectionMode = true;
|
|
236
|
+
document.body.style.cursor = "crosshair";
|
|
237
|
+
break;
|
|
238
|
+
case "tool:exitSelectionMode":
|
|
239
|
+
selectionMode = false;
|
|
240
|
+
document.body.style.cursor = "";
|
|
241
|
+
if (highlightOverlay) highlightOverlay.style.display = "none";
|
|
242
|
+
if (tooltip) tooltip.style.display = "none";
|
|
243
|
+
hoveredElement = null;
|
|
244
|
+
break;
|
|
245
|
+
case "tool:setProperty":
|
|
246
|
+
document.documentElement.style.setProperty(msg.token, msg.value);
|
|
247
|
+
break;
|
|
248
|
+
case "tool:previewShadow": {
|
|
249
|
+
let previewStyle = document.getElementById("tool-shadow-preview");
|
|
250
|
+
if (!previewStyle) {
|
|
251
|
+
previewStyle = document.createElement("style");
|
|
252
|
+
previewStyle.id = "tool-shadow-preview";
|
|
253
|
+
document.head.appendChild(previewStyle);
|
|
254
|
+
}
|
|
255
|
+
const cls = msg.className;
|
|
256
|
+
const val = msg.value;
|
|
257
|
+
if (val === "none" || val === "0 0 #0000") {
|
|
258
|
+
previewStyle.textContent = `.${CSS.escape(cls)} { --tw-shadow: 0 0 #0000 !important; box-shadow: none !important; }`;
|
|
259
|
+
} else {
|
|
260
|
+
previewStyle.textContent = `.${CSS.escape(cls)} { --tw-shadow: ${val} !important; }`;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case "tool:previewClass": {
|
|
265
|
+
const target = document.querySelector(msg.elementPath);
|
|
266
|
+
if (target) {
|
|
267
|
+
if (!previewBackups.has(target)) {
|
|
268
|
+
previewBackups.set(target, target.getAttribute("class") || "");
|
|
269
|
+
}
|
|
270
|
+
const currentClass = target.getAttribute("class") || "";
|
|
271
|
+
target.setAttribute(
|
|
272
|
+
"class",
|
|
273
|
+
currentClass.replace(msg.oldClass, msg.newClass)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case "tool:revertPreview":
|
|
279
|
+
for (const [el, backup] of previewBackups) {
|
|
280
|
+
el.setAttribute("class", backup);
|
|
281
|
+
}
|
|
282
|
+
previewBackups.clear();
|
|
283
|
+
break;
|
|
284
|
+
case "tool:reselectElement":
|
|
285
|
+
reselectCurrentElement();
|
|
286
|
+
break;
|
|
287
|
+
case "tool:setTheme":
|
|
288
|
+
if (msg.theme === "dark") {
|
|
289
|
+
document.documentElement.classList.add("dark");
|
|
290
|
+
} else {
|
|
291
|
+
document.documentElement.classList.remove("dark");
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function notifyPathChanged() {
|
|
297
|
+
const fullPath = window.location.pathname + window.location.search + window.location.hash;
|
|
298
|
+
const appPath = fullPath.startsWith("/proxy") ? fullPath.slice(6) || "/" : fullPath;
|
|
299
|
+
window.parent.postMessage({ type: "tool:pathChanged", path: appPath }, "*");
|
|
300
|
+
}
|
|
301
|
+
function interceptNavigation() {
|
|
302
|
+
document.addEventListener("click", (e) => {
|
|
303
|
+
if (selectionMode) return;
|
|
304
|
+
const anchor = e.target.closest("a");
|
|
305
|
+
if (!anchor) return;
|
|
306
|
+
const href = anchor.getAttribute("href");
|
|
307
|
+
if (!href) return;
|
|
308
|
+
if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("#") || href.startsWith("javascript:") || href.startsWith("/proxy/")) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (href.startsWith("/")) {
|
|
312
|
+
e.preventDefault();
|
|
313
|
+
window.location.href = `/proxy${href}`;
|
|
314
|
+
}
|
|
315
|
+
}, false);
|
|
316
|
+
window.addEventListener("popstate", () => notifyPathChanged());
|
|
317
|
+
notifyPathChanged();
|
|
318
|
+
}
|
|
319
|
+
function init() {
|
|
320
|
+
createOverlays();
|
|
321
|
+
interceptNavigation();
|
|
322
|
+
document.addEventListener("mousemove", onMouseMove, true);
|
|
323
|
+
document.addEventListener("mouseleave", onMouseLeave);
|
|
324
|
+
document.addEventListener("click", onClick, true);
|
|
325
|
+
window.addEventListener("message", onMessage);
|
|
326
|
+
window.parent.postMessage({ type: "tool:injectedReady" }, "*");
|
|
327
|
+
}
|
|
328
|
+
if (document.readyState === "loading") {
|
|
329
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
330
|
+
} else {
|
|
331
|
+
init();
|
|
332
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@designtools/shadows",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Visual shadow editing CLI — scan, preview, and edit box-shadow values in your project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "CC-BY-NC-4.0",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"dev": "tsx src/cli.ts",
|
|
13
13
|
"dev:client": "vite dev src/client",
|
|
14
|
-
"build": "vite build src/client && tsup",
|
|
14
|
+
"build": "vite build src/client && tsup && node scripts/build-inject.mjs",
|
|
15
15
|
"prepublishOnly": "npm run build"
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|