@lexingtonthemes/astro-image-inspector 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -7
- package/dist/toolbar-app.d.ts.map +1 -1
- package/dist/toolbar-app.js +121 -31
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,YAAY,EAAE,qBAAqB,EAAE,CAAC;AAEtC,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,OAAO,GAAE,qBAA0B,GAClC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,YAAY,EAAE,qBAAqB,EAAE,CAAC;AAEtC,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,OAAO,GAAE,qBAA0B,GAClC,gBAAgB,CAyBlB"}
|
package/dist/index.js
CHANGED
|
@@ -4,15 +4,20 @@ export default function imageInspector(options = {}) {
|
|
|
4
4
|
return {
|
|
5
5
|
name: "@lexingtonthemes/astro-image-inspector",
|
|
6
6
|
hooks: {
|
|
7
|
-
"astro:config:setup"({ addDevToolbarApp, command }) {
|
|
7
|
+
"astro:config:setup"({ addDevToolbarApp, command, logger }) {
|
|
8
8
|
if (!enabled || command !== "dev")
|
|
9
9
|
return;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
try {
|
|
11
|
+
addDevToolbarApp({
|
|
12
|
+
id: "astro-image-inspector",
|
|
13
|
+
name: appName,
|
|
14
|
+
icon: "image",
|
|
15
|
+
entrypoint: new URL("./toolbar-app.js", import.meta.url),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
logger.warn(`[astro-image-inspector] Could not add dev toolbar app: ${err instanceof Error ? err.message : String(err)}. Toolbar will show without Image Inspector.`);
|
|
20
|
+
}
|
|
16
21
|
},
|
|
17
22
|
},
|
|
18
23
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar-app.d.ts","sourceRoot":"","sources":["../src/toolbar-app.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"toolbar-app.d.ts","sourceRoot":"","sources":["../src/toolbar-app.ts"],"names":[],"mappings":";AAkSA,wBAA2B"}
|
package/dist/toolbar-app.js
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import { defineToolbarApp } from "astro/toolbar";
|
|
2
2
|
const OVERLAY_ID = "astro-image-inspector-overlay";
|
|
3
3
|
const PANEL_ID = "astro-image-inspector-panel";
|
|
4
|
+
function gcd(a, b) {
|
|
5
|
+
a = Math.round(Math.abs(a));
|
|
6
|
+
b = Math.round(Math.abs(b));
|
|
7
|
+
if (b === 0)
|
|
8
|
+
return a;
|
|
9
|
+
return gcd(b, a % b);
|
|
10
|
+
}
|
|
11
|
+
function aspectRatioLabel(w, h) {
|
|
12
|
+
if (h <= 0)
|
|
13
|
+
return "—";
|
|
14
|
+
const d = gcd(w, h);
|
|
15
|
+
if (d === 0)
|
|
16
|
+
return "—";
|
|
17
|
+
return `${w / d}:${h / d}`;
|
|
18
|
+
}
|
|
19
|
+
function formatFromSrc(src) {
|
|
20
|
+
const match = src.match(/\.(jpe?g|png|gif|webp|avif|svg)(\?|$)/i);
|
|
21
|
+
return match ? match[1].toLowerCase() : "—";
|
|
22
|
+
}
|
|
4
23
|
function getImageInfo(img) {
|
|
5
24
|
const rect = img.getBoundingClientRect();
|
|
6
25
|
const nw = img.naturalWidth || 0;
|
|
7
26
|
const nh = img.naturalHeight || 0;
|
|
8
27
|
const rw = Math.round(rect.width);
|
|
9
28
|
const rh = Math.round(rect.height);
|
|
29
|
+
const src = img.currentSrc || img.src || "";
|
|
10
30
|
const warnings = [];
|
|
11
31
|
if (nw > 0 && nh > 0) {
|
|
12
32
|
if (rw > nw || rh > nh)
|
|
@@ -17,8 +37,10 @@ function getImageInfo(img) {
|
|
|
17
37
|
if (!img.hasAttribute("width") && !img.hasAttribute("height")) {
|
|
18
38
|
warnings.push("No width/height");
|
|
19
39
|
}
|
|
40
|
+
const srcset = img.srcset || "";
|
|
41
|
+
const srcsetCount = srcset ? srcset.split(",").length : 0;
|
|
20
42
|
return {
|
|
21
|
-
src
|
|
43
|
+
src,
|
|
22
44
|
naturalWidth: nw,
|
|
23
45
|
naturalHeight: nh,
|
|
24
46
|
renderedWidth: rw,
|
|
@@ -26,6 +48,15 @@ function getImageInfo(img) {
|
|
|
26
48
|
loading: img.loading || "auto",
|
|
27
49
|
decoding: img.decoding || "auto",
|
|
28
50
|
warnings,
|
|
51
|
+
aspectIntrinsic: aspectRatioLabel(nw, nh),
|
|
52
|
+
aspectRendered: aspectRatioLabel(rw, rh),
|
|
53
|
+
format: formatFromSrc(src),
|
|
54
|
+
alt: img.alt !== undefined && img.alt !== "" ? img.alt : "(missing)",
|
|
55
|
+
fetchPriority: img.fetchPriority || "auto",
|
|
56
|
+
sizes: img.sizes?.trim() || "(none)",
|
|
57
|
+
srcsetCount,
|
|
58
|
+
attrWidth: img.getAttribute("width") ?? "—",
|
|
59
|
+
attrHeight: img.getAttribute("height") ?? "—",
|
|
29
60
|
};
|
|
30
61
|
}
|
|
31
62
|
function ensureOverlay() {
|
|
@@ -80,41 +111,87 @@ function hidePanel() {
|
|
|
80
111
|
if (el)
|
|
81
112
|
el.style.display = "none";
|
|
82
113
|
}
|
|
83
|
-
function showOverlay(rect) {
|
|
114
|
+
function showOverlay(rect, hasWarnings) {
|
|
84
115
|
const el = ensureOverlay();
|
|
85
116
|
el.style.display = "block";
|
|
86
117
|
el.style.top = `${rect.top + window.scrollY}px`;
|
|
87
118
|
el.style.left = `${rect.left + window.scrollX}px`;
|
|
88
119
|
el.style.width = `${rect.width}px`;
|
|
89
120
|
el.style.height = `${rect.height}px`;
|
|
90
|
-
el.style.borderColor =
|
|
121
|
+
el.style.borderColor = hasWarnings
|
|
122
|
+
? "rgba(251, 191, 36, 0.9)"
|
|
123
|
+
: "rgba(34, 197, 94, 0.8)";
|
|
91
124
|
}
|
|
92
125
|
function showPanel(info) {
|
|
93
126
|
const el = ensurePanel();
|
|
94
127
|
el.style.display = "block";
|
|
95
|
-
const srcShort = info.src.length >
|
|
128
|
+
const srcShort = info.src.length > 52 ? info.src.slice(0, 49) + "…" : info.src;
|
|
96
129
|
const warnText = info.warnings.length > 0
|
|
97
130
|
? `<span style="color: #fbbf24;">${info.warnings.join(", ")}</span>`
|
|
98
131
|
: "none";
|
|
132
|
+
const altShort = info.alt.length > 40 ? info.alt.slice(0, 37) + "…" : info.alt;
|
|
133
|
+
const sizesShort = info.sizes.length > 36 ? info.sizes.slice(0, 33) + "…" : info.sizes;
|
|
99
134
|
el.innerHTML = `
|
|
100
|
-
<div
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
135
|
+
<div style="display: grid; gap: 6px;">
|
|
136
|
+
<div><strong>src</strong><br/><span style="word-break: break-all;">${srcShort}</span></div>
|
|
137
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
|
138
|
+
<div><strong>natural</strong><br/>${info.naturalWidth}×${info.naturalHeight}</div>
|
|
139
|
+
<div><strong>rendered</strong><br/>${info.renderedWidth}×${info.renderedHeight}</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
|
142
|
+
<div><strong>aspect (int)</strong><br/>${info.aspectIntrinsic}</div>
|
|
143
|
+
<div><strong>aspect (out)</strong><br/>${info.aspectRendered}</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div><strong>format</strong> ${info.format} · <strong>loading</strong> ${info.loading} · <strong>decoding</strong> ${info.decoding}</div>
|
|
146
|
+
${info.fetchPriority !== "auto" ? `<div><strong>fetchpriority</strong> ${info.fetchPriority}</div>` : ""}
|
|
147
|
+
<div><strong>alt</strong><br/><span style="color: ${info.alt === "(missing)" ? "#f87171" : "inherit"}">${altShort}</span></div>
|
|
148
|
+
${info.srcsetCount > 0 ? `<div><strong>srcset</strong> ${info.srcsetCount} source(s)</div>` : ""}
|
|
149
|
+
${info.sizes !== "(none)" ? `<div><strong>sizes</strong><br/>${sizesShort}</div>` : ""}
|
|
150
|
+
<div><strong>width/height attrs</strong> ${info.attrWidth} × ${info.attrHeight}</div>
|
|
151
|
+
<div><strong>warnings</strong> ${warnText}</div>
|
|
152
|
+
<div style="display: flex; gap: 8px; margin-top: 6px;">
|
|
153
|
+
<button type="button" data-action="copy" style="padding: 4px 8px; font-size: 11px; cursor: pointer; border-radius: 4px; border: 1px solid #64748b; background: #334155; color: #e2e8f0;">Copy src</button>
|
|
154
|
+
<button type="button" data-action="open" style="padding: 4px 8px; font-size: 11px; cursor: pointer; border-radius: 4px; border: 1px solid #64748b; background: #334155; color: #e2e8f0;">Open in tab</button>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
105
157
|
`;
|
|
158
|
+
el.querySelectorAll("[data-action]").forEach((btn) => {
|
|
159
|
+
btn.addEventListener("click", (e) => {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
if (btn.dataset.action === "copy") {
|
|
162
|
+
void navigator.clipboard.writeText(info.src);
|
|
163
|
+
btn.textContent = "Copied!";
|
|
164
|
+
setTimeout(() => (btn.textContent = "Copy src"), 1200);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
window.open(info.src, "_blank", "noopener");
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
106
171
|
}
|
|
172
|
+
const INSTRUCTION_TEXT = "Hover an image to inspect it.";
|
|
107
173
|
function enableInspector(windowContent) {
|
|
108
|
-
windowContent.textContent =
|
|
174
|
+
windowContent.textContent = INSTRUCTION_TEXT;
|
|
175
|
+
windowContent.style.display = "";
|
|
176
|
+
const showInstruction = () => {
|
|
177
|
+
windowContent.textContent = INSTRUCTION_TEXT;
|
|
178
|
+
windowContent.style.display = "";
|
|
179
|
+
};
|
|
180
|
+
const hideInstruction = () => {
|
|
181
|
+
windowContent.textContent = "";
|
|
182
|
+
windowContent.style.display = "none";
|
|
183
|
+
};
|
|
109
184
|
const onMouseOver = (event) => {
|
|
110
185
|
const target = event.target;
|
|
111
186
|
if (!(target instanceof HTMLImageElement)) {
|
|
112
187
|
hideOverlay();
|
|
113
188
|
hidePanel();
|
|
189
|
+
showInstruction();
|
|
114
190
|
return;
|
|
115
191
|
}
|
|
192
|
+
hideInstruction();
|
|
116
193
|
const info = getImageInfo(target);
|
|
117
|
-
showOverlay(target.getBoundingClientRect());
|
|
194
|
+
showOverlay(target.getBoundingClientRect(), info.warnings.length > 0);
|
|
118
195
|
showPanel(info);
|
|
119
196
|
};
|
|
120
197
|
const onMouseOut = (event) => {
|
|
@@ -123,6 +200,7 @@ function enableInspector(windowContent) {
|
|
|
123
200
|
return;
|
|
124
201
|
hideOverlay();
|
|
125
202
|
hidePanel();
|
|
203
|
+
showInstruction();
|
|
126
204
|
};
|
|
127
205
|
document.addEventListener("mouseover", onMouseOver, true);
|
|
128
206
|
document.addEventListener("mouseout", onMouseOut, true);
|
|
@@ -133,25 +211,37 @@ function enableInspector(windowContent) {
|
|
|
133
211
|
hidePanel();
|
|
134
212
|
};
|
|
135
213
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
canvas.appendChild(windowEl);
|
|
144
|
-
let cleanup = null;
|
|
145
|
-
app.onToggled(({ state }) => {
|
|
146
|
-
if (cleanup) {
|
|
147
|
-
cleanup();
|
|
148
|
-
cleanup = null;
|
|
149
|
-
}
|
|
150
|
-
if (!state) {
|
|
214
|
+
function createApp() {
|
|
215
|
+
return defineToolbarApp({
|
|
216
|
+
init(canvas, app) {
|
|
217
|
+
try {
|
|
218
|
+
const windowEl = document.createElement("astro-dev-toolbar-window");
|
|
219
|
+
const content = document.createElement("div");
|
|
220
|
+
content.style.cssText = "padding: 12px; font-size: 14px;";
|
|
151
221
|
content.textContent = "Toggle on and hover an image to inspect it.";
|
|
152
|
-
|
|
222
|
+
windowEl.appendChild(content);
|
|
223
|
+
canvas.appendChild(windowEl);
|
|
224
|
+
let cleanup = null;
|
|
225
|
+
app.onToggled(({ state }) => {
|
|
226
|
+
if (cleanup) {
|
|
227
|
+
cleanup();
|
|
228
|
+
cleanup = null;
|
|
229
|
+
}
|
|
230
|
+
if (!state) {
|
|
231
|
+
content.textContent =
|
|
232
|
+
"Toggle on and hover an image to inspect it.";
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
cleanup = enableInspector(content);
|
|
236
|
+
});
|
|
153
237
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
const msg = document.createElement("div");
|
|
240
|
+
msg.style.cssText = "padding: 12px; font-size: 12px; color: #f87171;";
|
|
241
|
+
msg.textContent = `Image Inspector failed to load: ${err instanceof Error ? err.message : String(err)}`;
|
|
242
|
+
canvas.appendChild(msg);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
export default createApp();
|
package/package.json
CHANGED