foss-design 0.5.0 → 0.6.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/cli.js +544 -361
- package/dist/runtime/index.js +121 -6
- package/dist/viewer-lib/types/src/shared/types.d.ts +7 -0
- package/package.json +1 -1
package/dist/runtime/index.js
CHANGED
|
@@ -467,6 +467,77 @@ var FrameInspector = class {
|
|
|
467
467
|
};
|
|
468
468
|
var SWALLOWED = ["pointerdown", "mousedown", "pointerup", "mouseup", "dblclick", "submit"];
|
|
469
469
|
|
|
470
|
+
// src/runtime/public.ts
|
|
471
|
+
var ATTRS = ["src", "srcset", "poster", "href", "xlink:href"];
|
|
472
|
+
var LOADS_HREF = /* @__PURE__ */ new Set(["link", "image", "use", "feimage"]);
|
|
473
|
+
function servePublicFolder(folder) {
|
|
474
|
+
if (!folder?.files.length) return;
|
|
475
|
+
const files = new Set(folder.files);
|
|
476
|
+
const base = new URL(folder.base, location.href);
|
|
477
|
+
const resolve = (value) => {
|
|
478
|
+
if (!value.startsWith("/") || value.startsWith("//")) return null;
|
|
479
|
+
const [, pathname = "", suffix = ""] = /^([^?#]*)(.*)$/.exec(value) ?? [];
|
|
480
|
+
let rel;
|
|
481
|
+
try {
|
|
482
|
+
rel = decodeURIComponent(pathname.slice(1));
|
|
483
|
+
} catch {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
return files.has(rel) ? new URL(pathname.slice(1), base).href + suffix : null;
|
|
487
|
+
};
|
|
488
|
+
const rewrite = (name, value) => {
|
|
489
|
+
if (name !== "srcset") return resolve(value);
|
|
490
|
+
let changed = false;
|
|
491
|
+
const next = value.split(",").map((candidate) => {
|
|
492
|
+
const [url = "", ...rest] = candidate.trim().split(/\s+/);
|
|
493
|
+
const resolved = resolve(url);
|
|
494
|
+
if (!resolved) return candidate;
|
|
495
|
+
changed = true;
|
|
496
|
+
return [resolved, ...rest].join(" ");
|
|
497
|
+
}).join(", ");
|
|
498
|
+
return changed ? next : null;
|
|
499
|
+
};
|
|
500
|
+
const applies = (element, name) => name.endsWith("href") ? LOADS_HREF.has(element.localName.toLowerCase()) : true;
|
|
501
|
+
const setAttribute = Element.prototype.setAttribute;
|
|
502
|
+
Element.prototype.setAttribute = function(name, value) {
|
|
503
|
+
const lower = name.toLowerCase();
|
|
504
|
+
const next = ATTRS.includes(lower) && applies(this, lower) ? rewrite(lower, String(value)) : null;
|
|
505
|
+
setAttribute.call(this, name, next ?? value);
|
|
506
|
+
};
|
|
507
|
+
const fix = (element) => {
|
|
508
|
+
for (const name of ATTRS) {
|
|
509
|
+
const value = element.getAttribute(name);
|
|
510
|
+
if (value === null || !applies(element, name)) continue;
|
|
511
|
+
const next = rewrite(name, value);
|
|
512
|
+
if (next !== null) setAttribute.call(element, name, next);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
const fixTree = (root) => {
|
|
516
|
+
fix(root);
|
|
517
|
+
for (const element of root.querySelectorAll("[src], [srcset], [poster], [href]")) fix(element);
|
|
518
|
+
};
|
|
519
|
+
new MutationObserver((records) => {
|
|
520
|
+
for (const record of records) {
|
|
521
|
+
if (record.type === "attributes") fix(record.target);
|
|
522
|
+
for (const node of record.addedNodes) if (node.nodeType === Node.ELEMENT_NODE) fixTree(node);
|
|
523
|
+
}
|
|
524
|
+
}).observe(document, {
|
|
525
|
+
subtree: true,
|
|
526
|
+
childList: true,
|
|
527
|
+
attributes: true,
|
|
528
|
+
attributeFilter: ["src", "srcset", "poster", "href"]
|
|
529
|
+
});
|
|
530
|
+
if (document.documentElement) fixTree(document.documentElement);
|
|
531
|
+
const fetch2 = window.fetch.bind(window);
|
|
532
|
+
window.fetch = (input, init) => {
|
|
533
|
+
const raw = typeof input === "string" ? input : input instanceof URL ? input.href : null;
|
|
534
|
+
let path = raw;
|
|
535
|
+
if (raw && input instanceof URL && input.origin === location.origin) path = input.pathname + input.search;
|
|
536
|
+
const resolved = path ? resolve(path) : null;
|
|
537
|
+
return fetch2(resolved ?? input, init);
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
470
541
|
// src/runtime/typography.tsx
|
|
471
542
|
import { useEffect, useSyncExternalStore } from "react";
|
|
472
543
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -566,6 +637,8 @@ var config = window.__DESIGN__ ?? { key: "", canvas: "", id: "", props: {} };
|
|
|
566
637
|
var embedded = window.parent !== window;
|
|
567
638
|
var query = new URLSearchParams(window.location.search);
|
|
568
639
|
var capture = query.has("capture");
|
|
640
|
+
var frameUrl = location.pathname + location.search + location.hash;
|
|
641
|
+
servePublicFolder(config.public);
|
|
569
642
|
var theme = config.theme ?? (query.get("theme") === "dark" ? "dark" : "light");
|
|
570
643
|
var themeListeners = /* @__PURE__ */ new Set();
|
|
571
644
|
function applyTheme(next) {
|
|
@@ -669,9 +742,14 @@ function watchSize() {
|
|
|
669
742
|
window.addEventListener("load", queue);
|
|
670
743
|
queue();
|
|
671
744
|
}
|
|
745
|
+
var touched = false;
|
|
746
|
+
var inspecting = false;
|
|
747
|
+
var loaded = false;
|
|
748
|
+
var signalled = false;
|
|
749
|
+
var holds = 0;
|
|
672
750
|
var snapshotTimer;
|
|
673
751
|
function scheduleSnapshot() {
|
|
674
|
-
if (!config.snapshots || !embedded || capture) return;
|
|
752
|
+
if (!config.snapshots || !embedded || capture || touched || !signalled) return;
|
|
675
753
|
clearTimeout(snapshotTimer);
|
|
676
754
|
snapshotTimer = setTimeout(() => {
|
|
677
755
|
const run = () => void takeSnapshot();
|
|
@@ -680,6 +758,7 @@ function scheduleSnapshot() {
|
|
|
680
758
|
}, 1800);
|
|
681
759
|
}
|
|
682
760
|
async function takeSnapshot() {
|
|
761
|
+
if (touched) return;
|
|
683
762
|
try {
|
|
684
763
|
const { domToBlob } = await import("modern-screenshot");
|
|
685
764
|
const width = window.innerWidth;
|
|
@@ -691,7 +770,7 @@ async function takeSnapshot() {
|
|
|
691
770
|
type: "image/png",
|
|
692
771
|
backgroundColor: getComputedStyle(document.body).backgroundColor
|
|
693
772
|
});
|
|
694
|
-
if (!blob) return;
|
|
773
|
+
if (!blob || touched) return;
|
|
695
774
|
const [canvas, id] = [config.canvas, config.id].map(encodeURIComponent);
|
|
696
775
|
await fetch(`/api/snapshots/${canvas}/${id}?theme=${theme}&height=${config.autoHeight ? height : ""}`, {
|
|
697
776
|
method: "POST",
|
|
@@ -748,12 +827,31 @@ function keepLinksInFrame() {
|
|
|
748
827
|
const original = history[name].bind(history);
|
|
749
828
|
history[name] = (data, unused, url) => {
|
|
750
829
|
original(data, unused, url);
|
|
751
|
-
if (url === void 0 || url === null) return;
|
|
830
|
+
if (url === void 0 || url === null || !signalled) return;
|
|
752
831
|
const raw = String(url);
|
|
753
832
|
report(new URL(raw, location.href), false, raw, true);
|
|
754
833
|
};
|
|
755
834
|
}
|
|
756
835
|
}
|
|
836
|
+
function signalReady() {
|
|
837
|
+
if (signalled || !loaded || holds > 0) return;
|
|
838
|
+
signalled = true;
|
|
839
|
+
window.__DESIGN_READY__ = true;
|
|
840
|
+
post({ type: "ready" });
|
|
841
|
+
scheduleSnapshot();
|
|
842
|
+
}
|
|
843
|
+
function holdReady() {
|
|
844
|
+
holds++;
|
|
845
|
+
window.__DESIGN_HOLDS__ = holds;
|
|
846
|
+
let released = false;
|
|
847
|
+
return () => {
|
|
848
|
+
if (released) return;
|
|
849
|
+
released = true;
|
|
850
|
+
holds--;
|
|
851
|
+
window.__DESIGN_HOLDS__ = holds;
|
|
852
|
+
requestAnimationFrame(() => requestAnimationFrame(signalReady));
|
|
853
|
+
};
|
|
854
|
+
}
|
|
757
855
|
var booted = false;
|
|
758
856
|
var hmrBound = false;
|
|
759
857
|
var canvasHost = false;
|
|
@@ -772,6 +870,7 @@ function boot() {
|
|
|
772
870
|
} else if (data.type === "inspect") {
|
|
773
871
|
inspector ??= new FrameInspector(post);
|
|
774
872
|
if (data.tokens) inspector.setTokens(data.tokens);
|
|
873
|
+
inspecting = !!data.on;
|
|
775
874
|
if (data.on) inspector.enable();
|
|
776
875
|
else inspector.disable();
|
|
777
876
|
} else if (data.type === "inspect-select") inspector?.select(data.ref);
|
|
@@ -799,6 +898,19 @@ function boot() {
|
|
|
799
898
|
if (event.key === "Escape" && !event.defaultPrevented) post({ type: "keydown", code: "Escape" });
|
|
800
899
|
});
|
|
801
900
|
const isModifier = (name) => name === "Meta" || name === "Control";
|
|
901
|
+
const touch = (event) => {
|
|
902
|
+
if (!event.isTrusted || inspecting) return;
|
|
903
|
+
touched = true;
|
|
904
|
+
clearTimeout(snapshotTimer);
|
|
905
|
+
};
|
|
906
|
+
window.addEventListener("pointerdown", touch, true);
|
|
907
|
+
window.addEventListener(
|
|
908
|
+
"keydown",
|
|
909
|
+
(event) => {
|
|
910
|
+
if (!isModifier(event.key) && event.key !== "Shift" && event.key !== "Alt") touch(event);
|
|
911
|
+
},
|
|
912
|
+
true
|
|
913
|
+
);
|
|
802
914
|
window.addEventListener(
|
|
803
915
|
"keydown",
|
|
804
916
|
(event) => {
|
|
@@ -821,9 +933,8 @@ function boot() {
|
|
|
821
933
|
recordError(String(reason?.message ?? reason ?? "Unhandled rejection"));
|
|
822
934
|
});
|
|
823
935
|
const ready = () => {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
scheduleSnapshot();
|
|
936
|
+
loaded = true;
|
|
937
|
+
signalReady();
|
|
827
938
|
};
|
|
828
939
|
const whenBodyReady = () => {
|
|
829
940
|
if (embedded) watchSize();
|
|
@@ -840,6 +951,9 @@ function hmr(hot) {
|
|
|
840
951
|
post({ type: "updated" });
|
|
841
952
|
scheduleSnapshot();
|
|
842
953
|
});
|
|
954
|
+
hot.on("vite:beforeFullReload", () => {
|
|
955
|
+
History.prototype.replaceState.call(history, history.state, "", frameUrl);
|
|
956
|
+
});
|
|
843
957
|
hot.on("vite:error", (payload) => {
|
|
844
958
|
const err = payload.err;
|
|
845
959
|
recordError(err?.message ?? "Build error");
|
|
@@ -893,6 +1007,7 @@ export {
|
|
|
893
1007
|
boot,
|
|
894
1008
|
go,
|
|
895
1009
|
hmr,
|
|
1010
|
+
holdReady,
|
|
896
1011
|
mount,
|
|
897
1012
|
useScreen,
|
|
898
1013
|
useTheme
|
|
@@ -370,4 +370,11 @@ export type ViewerMessage = {
|
|
|
370
370
|
type: 'inspect-select';
|
|
371
371
|
ref: number | null;
|
|
372
372
|
};
|
|
373
|
+
/** design.json `public` as a frame sees it. */
|
|
374
|
+
export interface PublicFolder {
|
|
375
|
+
/** Where the folder's files are, relative to the frame's URL. */
|
|
376
|
+
base: string;
|
|
377
|
+
/** Its files, relative to the folder, with forward slashes. */
|
|
378
|
+
files: string[];
|
|
379
|
+
}
|
|
373
380
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foss-design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Design canvases and design systems for coding agents: live, animated, clickable screens on an infinite canvas, served locally from a .design folder.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|