foss-design 0.9.7 → 0.9.8
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/cli.js +48 -12
- package/dist/viewer/assets/{index-Cg9mKtjz.js → index-6h0AwUgn.js} +49 -49
- package/dist/viewer/assets/{index-oN46u7re.css → index-DnxkAHbH.css} +1 -1
- package/dist/viewer/index.html +2 -2
- package/dist/viewer-lib/index.js +51 -2
- package/dist/viewer-lib/styles.css +11 -1
- package/dist/viewer-lib/types/viewer/src/ui/menu.d.ts +12 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3033,8 +3033,16 @@ import fs20 from "fs";
|
|
|
3033
3033
|
import os3 from "os";
|
|
3034
3034
|
|
|
3035
3035
|
// src/server/snapshots.ts
|
|
3036
|
+
import { createHash as createHash5 } from "crypto";
|
|
3036
3037
|
import fs17 from "fs";
|
|
3037
3038
|
import path17 from "path";
|
|
3039
|
+
function sourceHash(file) {
|
|
3040
|
+
try {
|
|
3041
|
+
return createHash5("sha1").update(fs17.readFileSync(file)).digest("hex").slice(0, 16);
|
|
3042
|
+
} catch {
|
|
3043
|
+
return null;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3038
3046
|
var SAFE = /^@?[a-z0-9][a-z0-9_-]*$/i;
|
|
3039
3047
|
function isSafeSegment(value) {
|
|
3040
3048
|
return SAFE.test(value);
|
|
@@ -3050,6 +3058,12 @@ var SnapshotStore = class {
|
|
|
3050
3058
|
metaFile(canvas) {
|
|
3051
3059
|
return path17.join(this.dir, canvas, "meta.json");
|
|
3052
3060
|
}
|
|
3061
|
+
/** Written aside and renamed: a run stopped mid-write leaves the old file, not half of one. */
|
|
3062
|
+
writeMeta(canvas, data) {
|
|
3063
|
+
const temp = `${this.metaFile(canvas)}.${process.pid}.tmp`;
|
|
3064
|
+
fs17.writeFileSync(temp, JSON.stringify(data));
|
|
3065
|
+
fs17.renameSync(temp, this.metaFile(canvas));
|
|
3066
|
+
}
|
|
3053
3067
|
load(canvas) {
|
|
3054
3068
|
const cached = this.meta.get(canvas);
|
|
3055
3069
|
if (cached) return cached;
|
|
@@ -3084,7 +3098,21 @@ var SnapshotStore = class {
|
|
|
3084
3098
|
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return null;
|
|
3085
3099
|
return this.load(canvas)[id2]?.[theme2] ?? null;
|
|
3086
3100
|
}
|
|
3087
|
-
|
|
3101
|
+
/** The source hash recorded with the snapshot; undefined for snapshots taken before hashes were kept. */
|
|
3102
|
+
source(canvas, id2, theme2) {
|
|
3103
|
+
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return void 0;
|
|
3104
|
+
return this.load(canvas)[id2]?.sources?.[theme2];
|
|
3105
|
+
}
|
|
3106
|
+
/** Record `hash` for a snapshot taken before hashes were kept: from now on it counts as that content's. */
|
|
3107
|
+
adopt(canvas, id2, theme2, hash) {
|
|
3108
|
+
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) return;
|
|
3109
|
+
const data = this.load(canvas);
|
|
3110
|
+
const entry = data[id2];
|
|
3111
|
+
if (!entry?.[theme2]) return;
|
|
3112
|
+
entry.sources = { ...entry.sources, [theme2]: hash };
|
|
3113
|
+
this.writeMeta(canvas, data);
|
|
3114
|
+
}
|
|
3115
|
+
save(canvas, id2, theme2, png, height, source) {
|
|
3088
3116
|
if (!isSafeSegment(canvas) || !isSafeSegment(id2)) throw new Error("bad snapshot key");
|
|
3089
3117
|
const dir = path17.join(this.dir, canvas);
|
|
3090
3118
|
fs17.mkdirSync(dir, { recursive: true });
|
|
@@ -3093,10 +3121,12 @@ var SnapshotStore = class {
|
|
|
3093
3121
|
fs17.renameSync(`${file}.${process.pid}.tmp`, file);
|
|
3094
3122
|
const data = this.load(canvas);
|
|
3095
3123
|
const version = Date.now();
|
|
3096
|
-
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
|
|
3124
|
+
const previous = data[id2];
|
|
3125
|
+
const sources = { ...previous?.sources };
|
|
3126
|
+
if (source) sources[theme2] = source;
|
|
3127
|
+
else delete sources[theme2];
|
|
3128
|
+
data[id2] = { ...previous, [theme2]: version, ...height ? { height } : {}, sources };
|
|
3129
|
+
this.writeMeta(canvas, data);
|
|
3100
3130
|
return { url: this.url(canvas, id2, theme2, version) };
|
|
3101
3131
|
}
|
|
3102
3132
|
/** Absolute path of `<id>.<theme>.png`, or of its thumbnail for `<id>.<theme>.thumb.png` (made on first use); null when missing. */
|
|
@@ -3353,13 +3383,18 @@ function staleSnapshots(paths, canvasIds, filter = {}) {
|
|
|
3353
3383
|
if (filter.screens && !filter.screens.has(job.id)) continue;
|
|
3354
3384
|
const file = files.get(job.id);
|
|
3355
3385
|
if (!file || !fs20.existsSync(file)) continue;
|
|
3356
|
-
const
|
|
3386
|
+
const hash = sourceHash(file);
|
|
3357
3387
|
const pinned = inDark.get(job.id) === job.theme;
|
|
3358
3388
|
const themes = pinned ? [job.theme] : THEMES2;
|
|
3359
|
-
const screen = { id: job.id };
|
|
3360
3389
|
for (const theme2 of themes) {
|
|
3361
|
-
|
|
3362
|
-
|
|
3390
|
+
if (!store.version(id2, job.id, theme2)) {
|
|
3391
|
+
stale.push({ canvas: id2, screen: job.id, theme: theme2, source: hash });
|
|
3392
|
+
continue;
|
|
3393
|
+
}
|
|
3394
|
+
const recorded = store.source(id2, job.id, theme2);
|
|
3395
|
+
if (recorded === void 0) {
|
|
3396
|
+
if (hash) store.adopt(id2, job.id, theme2, hash);
|
|
3397
|
+
} else if (recorded !== hash) stale.push({ canvas: id2, screen: job.id, theme: theme2, source: hash });
|
|
3363
3398
|
}
|
|
3364
3399
|
}
|
|
3365
3400
|
}
|
|
@@ -3391,7 +3426,7 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3391
3426
|
const line = () => `Snapshots ${done}/${targets.length} (both themes, as the viewer and the cloud show them)`;
|
|
3392
3427
|
progress2.update(line());
|
|
3393
3428
|
try {
|
|
3394
|
-
await mapLimit([...targets], TABS, async ({ canvas, screen, theme: theme2 }) => {
|
|
3429
|
+
await mapLimit([...targets], TABS, async ({ canvas, screen, theme: theme2, source }) => {
|
|
3395
3430
|
const job = canvasFrames(docOf(canvas), url, theme2, { id: screen })[0];
|
|
3396
3431
|
if (job) {
|
|
3397
3432
|
const page = await browser.newPage({
|
|
@@ -3404,7 +3439,7 @@ async function takeSnapshots(paths, targets, preview, progress2) {
|
|
|
3404
3439
|
if (report.ready) {
|
|
3405
3440
|
const png = await page.screenshot({ fullPage: job.fullPage });
|
|
3406
3441
|
const height = job.fullPage ? await page.evaluate(() => document.documentElement.scrollHeight) : void 0;
|
|
3407
|
-
store.save(canvas, screen, theme2, png, height);
|
|
3442
|
+
store.save(canvas, screen, theme2, png, height, source);
|
|
3408
3443
|
} else failed.push(`${canvas}/${screen} (${theme2})`);
|
|
3409
3444
|
} catch {
|
|
3410
3445
|
failed.push(`${canvas}/${screen} (${theme2})`);
|
|
@@ -4236,7 +4271,8 @@ async function handleApi(ctx, req, res, url) {
|
|
|
4236
4271
|
try {
|
|
4237
4272
|
const body = await readBody(req);
|
|
4238
4273
|
if (body.subarray(0, 8).toString("hex") !== "89504e470d0a1a0a") throw new Error("not a PNG");
|
|
4239
|
-
const
|
|
4274
|
+
const file = project.source(canvas, id2)?.file;
|
|
4275
|
+
const { url: snapshotUrl } = snapshots.save(canvas, id2, theme2, body, height, file ? sourceHash(file) : null);
|
|
4240
4276
|
project.invalidateCanvas(canvas);
|
|
4241
4277
|
events.send({ type: "snapshot", canvas, id: id2, theme: theme2, url: snapshotUrl, height });
|
|
4242
4278
|
sendJson(res, 200, { url: snapshotUrl });
|