@ranx729/ducktape-assets 1.0.0 → 1.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/AGENTS.md +12 -2
- package/README.md +12 -2
- package/catalog.json +794 -760
- package/index.json +5 -5
- package/package.json +3 -3
- package/sets/illuminated-acanthus.json +241 -85
- package/src/copy.mjs +24 -3
- package/src/generate.mjs +12 -2
- package/src/node.mjs +12 -0
- package/src/resolver.mjs +18 -5
package/src/resolver.mjs
CHANGED
|
@@ -100,19 +100,28 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
|
|
|
100
100
|
const { setId, entry, asset } = ctx;
|
|
101
101
|
const maxDpr = maxOf(d ?? dpr, 3);
|
|
102
102
|
const sources = [];
|
|
103
|
+
const files = [];
|
|
104
|
+
const fileOf = (fmt, r) => ({ format: fmt, url: urlOf(entry, r, setId), path: r.path, width: r.w, height: r.h, bytes: r.bytes, sha256: r.sha256, ...(r.package ? { package: r.package } : {}) });
|
|
103
105
|
let fallback;
|
|
106
|
+
let fallbackFmt;
|
|
104
107
|
for (const fmt of formats) {
|
|
105
108
|
const ladder = asset.sources?.[fmt];
|
|
106
109
|
if (!Array.isArray(ladder) || !ladder.length) continue;
|
|
107
110
|
const sel = selectRungs(ladder, width, { maxDpr });
|
|
108
111
|
sources.push({ type: mime(fmt), srcset: sel.rungs.map((r) => `${urlOf(entry, r, setId)} ${r.w}w`).join(', ') });
|
|
112
|
+
files.push(...sel.rungs.map((r) => fileOf(fmt, r)));
|
|
109
113
|
fallback = sel;
|
|
114
|
+
fallbackFmt = fmt;
|
|
110
115
|
}
|
|
111
116
|
if (!fallback) throw new Error(`${id}: none of the formats ${formats.join(', ')} is available`);
|
|
112
117
|
const out = {
|
|
113
118
|
src: urlOf(entry, fallback.src, setId),
|
|
114
119
|
srcset: sources[sources.length - 1].srcset,
|
|
115
120
|
sources,
|
|
121
|
+
// The chosen files with their bytes and sha256, for build scripts that record or verify what they reference.
|
|
122
|
+
// `file` is the one behind `src`; `files` lists every rung of every format in the srcsets.
|
|
123
|
+
file: fileOf(fallbackFmt, fallback.src),
|
|
124
|
+
files,
|
|
116
125
|
sizes: sizes ?? (width ? `${width}px` : '100vw'),
|
|
117
126
|
width: asset.width,
|
|
118
127
|
height: asset.height,
|
|
@@ -197,13 +206,17 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
|
|
|
197
206
|
const { setId, entry, asset } = await get(id);
|
|
198
207
|
const f = asset.render?.frame;
|
|
199
208
|
if (!f) throw new Error(`${id} has no render.frame`);
|
|
209
|
+
// Composed atlases record one percentage; ready-made frames record four (top right bottom left) because a uniform
|
|
210
|
+
// pixel slice is a different share of the width and of the height. The CSS value and the coverage maths follow.
|
|
200
211
|
const slice = f.slice;
|
|
212
|
+
const sliceCss = typeof slice === 'number' ? `${slice}%` : `${slice.top}% ${slice.right}% ${slice.bottom}% ${slice.left}%`;
|
|
213
|
+
const sliceW = typeof slice === 'number' ? slice : slice.left; // share of the atlas width that must cover `thickness`
|
|
201
214
|
const atlas = f.atlas;
|
|
202
215
|
const dprs = dprList(d ?? dpr, [1, 2, 3]);
|
|
203
216
|
const smallest = Object.values(atlas).find(Array.isArray)?.[0];
|
|
204
|
-
thickness ??= smallest ? Math.round((smallest.w *
|
|
205
|
-
// the slice region (
|
|
206
|
-
const need = (x) => (thickness * x * 100) /
|
|
217
|
+
thickness ??= smallest ? Math.round((smallest.w * sliceW) / 100) : 16;
|
|
218
|
+
// the slice region (sliceW% of the atlas width) must map onto `thickness` css px at density x
|
|
219
|
+
const need = (x) => (thickness * x * 100) / sliceW;
|
|
207
220
|
let value;
|
|
208
221
|
if (atlas.svg) value = cssUrl(urlOf(entry, atlas.svg, setId));
|
|
209
222
|
else value = imageSet(entry, setId, atlas, 'w', thickness, dprs, need).set;
|
|
@@ -214,8 +227,8 @@ export function createResolver({ index, base, formats = ['avif', 'webp'], pin =
|
|
|
214
227
|
.map((r) => `${urlOf(entry, r, setId)} ${r.w}w`)
|
|
215
228
|
.join(', ')
|
|
216
229
|
: '';
|
|
217
|
-
const css = [`border-style: solid;`, `border-width: ${thickness}px;`, `border-image: ${value} ${
|
|
218
|
-
return { css, slice, atlasSrcset };
|
|
230
|
+
const css = [`border-style: solid;`, `border-width: ${thickness}px;`, `border-image: ${value} ${sliceCss} round;`].join('\n');
|
|
231
|
+
return { css, slice, sliceCss, atlasSrcset };
|
|
219
232
|
}
|
|
220
233
|
|
|
221
234
|
/** Paths of the files the copy command writes for an asset (all rungs), for flows that import local files. */
|