fundus 0.27.0 → 0.28.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/README.md +12 -4
- package/build/client/_app/immutable/assets/{3.OulZ54CG.css → 3.Dfl33dj0.css} +1 -1
- package/build/client/_app/immutable/assets/3.Dfl33dj0.css.br +0 -0
- package/build/client/_app/immutable/assets/3.Dfl33dj0.css.gz +0 -0
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js +1 -0
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bsj1ssvc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{B8-iUbxt.js → OL1Dh1oP.js} +2 -2
- package/build/client/_app/immutable/chunks/OL1Dh1oP.js.br +0 -0
- package/build/client/_app/immutable/chunks/OL1Dh1oP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DTsuHg02.js → QlQ-jeIO.js} +1 -1
- package/build/client/_app/immutable/chunks/QlQ-jeIO.js.br +2 -0
- package/build/client/_app/immutable/chunks/QlQ-jeIO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/apKHI4vq.js +2 -0
- package/build/client/_app/immutable/chunks/apKHI4vq.js.br +0 -0
- package/build/client/_app/immutable/chunks/apKHI4vq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/g2rma9Ym.js +1 -0
- package/build/client/_app/immutable/chunks/g2rma9Ym.js.br +0 -0
- package/build/client/_app/immutable/chunks/g2rma9Ym.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CIuRQnTG.js → app.DpZM49_s.js} +2 -2
- package/build/client/_app/immutable/entry/app.DpZM49_s.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DpZM49_s.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js +1 -0
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js.br +0 -0
- package/build/client/_app/immutable/entry/start.B3EMVPEN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.D0OQowHt.js → 0.Wqvn8XD-.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Wqvn8XD-.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Wqvn8XD-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.CJmLUeSL.js → 1.Z-UvvLkP.js} +1 -1
- package/build/client/_app/immutable/nodes/{1.CJmLUeSL.js.br → 1.Z-UvvLkP.js.br} +0 -0
- package/build/client/_app/immutable/nodes/1.Z-UvvLkP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.lu89W_qJ.js +4 -0
- package/build/client/_app/immutable/nodes/2.lu89W_qJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.lu89W_qJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.Dt32EAoq.js +1 -0
- package/build/client/_app/immutable/nodes/3.Dt32EAoq.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.Dt32EAoq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.DxAj27VZ.js → 4.WipzcX8_.js} +1 -1
- package/build/client/_app/immutable/nodes/4.WipzcX8_.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.WipzcX8_.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/handler.js +7 -7
- package/build/index.js +7 -7
- package/build/server/chunks/chunks/{context.js-Bd8bqgno.js → context.js-BZSZNuAd.js} +5 -5
- package/build/server/chunks/chunks/{context.js-Bd8bqgno.js.map → context.js-BZSZNuAd.js.map} +1 -1
- package/build/server/chunks/chunks/{contract.js-CvgjfKhk.js → contract.js-B4JdXFgm.js} +33 -38
- package/build/server/chunks/chunks/contract.js-B4JdXFgm.js.map +1 -0
- package/build/server/chunks/chunks/{core.js-z3Oyfjut.js → core.js-BHxa3VBo.js} +31 -2
- package/build/server/chunks/chunks/core.js-BHxa3VBo.js.map +1 -0
- package/build/server/chunks/chunks/{exports.js-DAZwf-PG.js → exports.js-D0dALD2Q.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-DAZwf-PG.js.map → exports.js-D0dALD2Q.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-BDlmMiVn.js → http.js-DYYKvppy.js} +2 -2
- package/build/server/chunks/chunks/{http.js-BDlmMiVn.js.map → http.js-DYYKvppy.js.map} +1 -1
- package/build/server/chunks/chunks/{internal.js-Ct2VLuOf.js → internal.js-CJkB_gzV.js} +3 -3
- package/build/server/chunks/chunks/{internal.js-Ct2VLuOf.js.map → internal.js-CJkB_gzV.js.map} +1 -1
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{manifest-membership.js-dUxJtQsv.js → manifest-membership.js-BkrPsM_r.js} +63 -4
- package/build/server/chunks/chunks/manifest-membership.js-BkrPsM_r.js.map +1 -0
- package/build/server/chunks/chunks/{raw-reconciliation.js-CVpSamHq.js → raw-reconciliation.js-DPnNXe0b.js} +3 -3
- package/build/server/chunks/chunks/raw-reconciliation.js-DPnNXe0b.js.map +1 -0
- package/build/server/chunks/chunks/{raw-reconciliation2.js-d0VZiZIt.js → raw-reconciliation2.js-DKOpJ8if.js} +2 -2
- package/build/server/chunks/chunks/{raw-reconciliation2.js-d0VZiZIt.js.map → raw-reconciliation2.js-DKOpJ8if.js.map} +1 -1
- package/build/server/chunks/chunks/{shared.js-7W9eRLAO.js → shared.js-D3ZDVUlh.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-7W9eRLAO.js.map → shared.js-D3ZDVUlh.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-DOyEfB14.js → utils.js-CqWW_v-5.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-DOyEfB14.js.map → utils.js-CqWW_v-5.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-D1lzCgKx.js → _server.ts.js-Dw2MW5Tm.js} +6 -6
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-D1lzCgKx.js.map → _server.ts.js-Dw2MW5Tm.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-B_exlIvG.js → _server.ts.js-GQDuJGug.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-B_exlIvG.js.map → _server.ts.js-GQDuJGug.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BVRoG0ks.js → _server.ts.js-JbLSv7Xx.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BVRoG0ks.js.map → _server.ts.js-JbLSv7Xx.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-CGl_1qOp.js → _server.ts.js-BPae-BYF.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/{_server.ts.js-CGl_1qOp.js.map → _server.ts.js-BPae-BYF.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/{_server.ts.js-Dc5XtD59.js → _server.ts.js-BafrrUxu.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/_id_/retag/{_server.ts.js-Dc5XtD59.js.map → _server.ts.js-BafrrUxu.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-D18kmCae.js → _server.ts.js-UOlSNEi9.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-D18kmCae.js.map → _server.ts.js-UOlSNEi9.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-Dma9az04.js → _server.ts.js-QE1vUuAx.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-Dma9az04.js.map → _server.ts.js-QE1vUuAx.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-Du28ZFF6.js → _server.ts.js-DIVfppGt.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-Du28ZFF6.js.map → _server.ts.js-DIVfppGt.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/events/{_server.ts.js-CaQbcAKe.js → _server.ts.js-B6R_kqP8.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/events/{_server.ts.js-CaQbcAKe.js.map → _server.ts.js-B6R_kqP8.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BIdR0hXk.js → _server.ts.js-CertbDsH.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BIdR0hXk.js.map → _server.ts.js-CertbDsH.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-DoYqBtnH.js → _server.ts.js-DEIF3Hu2.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-DoYqBtnH.js.map → _server.ts.js-DEIF3Hu2.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-ydm9S1XX.js → _server.ts.js-DNFsNGr4.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/imports/{_server.ts.js-ydm9S1XX.js.map → _server.ts.js-DNFsNGr4.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-BEAasQO4.js → _server.ts.js-DDW8XRPe.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-BEAasQO4.js.map → _server.ts.js-DDW8XRPe.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-CXINoU3b.js → _server.ts.js-C222-xli.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-CXINoU3b.js.map → _server.ts.js-C222-xli.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-CvuzMBDw.js → _server.ts.js-CI1P1uyd.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-CvuzMBDw.js.map → _server.ts.js-CI1P1uyd.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-kgHGsyn-.js → _server.ts.js-DpiUwaSh.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-kgHGsyn-.js.map → _server.ts.js-DpiUwaSh.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-CzS37BdF.js → _server.ts.js-DT0VPqPT.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-CzS37BdF.js.map → _server.ts.js-DT0VPqPT.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-BXE_mCX3.js → _server.ts.js-DBpHM42g.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-BXE_mCX3.js.map → _server.ts.js-DBpHM42g.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-DM8Nj2U6.js → _server.ts.js-Cxu5b6Au.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-DM8Nj2U6.js.map → _server.ts.js-Cxu5b6Au.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-C0a_L0JL.js → _server.ts.js-W4dhMo8Y.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-C0a_L0JL.js.map → _server.ts.js-W4dhMo8Y.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-CavHY0Ie.js → _server.ts.js-DJ_adxbT.js} +7 -7
- package/build/server/chunks/entries/endpoints/api/usage/{_server.ts.js-CavHY0Ie.js.map → _server.ts.js-DJ_adxbT.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-c03akA1c.js → _server.ts.js-BisoHihD.js} +6 -6
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-c03akA1c.js.map → _server.ts.js-BisoHihD.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-CKKJ-v3P.js → _server.ts.js-D4jLLuko.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-CKKJ-v3P.js.map → _server.ts.js-D4jLLuko.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-DiZ3D46Z.js → _server.ts.js-Dfv2ItjT.js} +7 -7
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-DiZ3D46Z.js.map → _server.ts.js-Dfv2ItjT.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-DwnveOre.js → hooks.server.js-oHJX5INm.js} +6 -6
- package/build/server/chunks/entries/{hooks.server.js-DwnveOre.js.map → hooks.server.js-oHJX5INm.js.map} +1 -1
- package/build/server/chunks/{handler-ZL0S9fLH.js → handler-BAe0c9yw.js} +5 -5
- package/build/server/chunks/{handler-ZL0S9fLH.js.map → handler-BAe0c9yw.js.map} +1 -1
- package/build/server/chunks/{index.js-Ddbaaikg.js → index.js-DUYD4JB0.js} +5 -5
- package/build/server/chunks/{index.js-Ddbaaikg.js.map → index.js-DUYD4JB0.js.map} +1 -1
- package/build/server/chunks/{manifest.js-D3J7G7mh.js → manifest.js-zwun-T7O.js} +31 -31
- package/build/server/chunks/{manifest.js-D3J7G7mh.js.map → manifest.js-zwun-T7O.js.map} +1 -1
- package/build/server/chunks/nodes/{0.js-DjvM5GIw.js → 0.js-BmkXwDRY.js} +2 -2
- package/build/server/chunks/nodes/{0.js-DjvM5GIw.js.map → 0.js-BmkXwDRY.js.map} +1 -1
- package/build/server/chunks/nodes/1.js-xbOwEHCW.js +7 -0
- package/build/server/chunks/nodes/{1.js-D_KuDyBR.js.map → 1.js-xbOwEHCW.js.map} +1 -1
- package/build/server/chunks/nodes/{2.js-CEThjyv1.js → 2.js-BuNw1vTZ.js} +2 -2
- package/build/server/chunks/nodes/{2.js-CEThjyv1.js.map → 2.js-BuNw1vTZ.js.map} +1 -1
- package/build/server/chunks/nodes/3.js-Dpr_DvNU.js +7 -0
- package/build/server/chunks/nodes/{3.js-7gq-At5G.js.map → 3.js-Dpr_DvNU.js.map} +1 -1
- package/build/server/chunks/nodes/4.js-14Lypd4o.js +7 -0
- package/build/server/chunks/nodes/{4.js-DsoSGC53.js.map → 4.js-14Lypd4o.js.map} +1 -1
- package/dist/core/asset-id.d.ts +3 -2
- package/dist/core/asset-id.js +63 -2
- package/dist/core/figma-tag-script.d.ts +19 -6
- package/dist/core/figma-tag-script.js +35 -23
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/core/manifest-entry.d.ts +7 -0
- package/dist/core/plugins/slice.d.ts +12 -0
- package/dist/core/plugins/slice.js +26 -0
- package/dist/runtime/geometry.d.ts +4 -2
- package/dist/runtime/geometry.js +14 -12
- package/dist/server/cli-figma-tags.js +12 -5
- package/dist/server/figma-tags.js +32 -6
- package/dist/server/plugins/contract.d.ts +5 -2
- package/dist/server/plugins/slice.js +14 -31
- package/dist/server/processing/processor.d.ts +1 -1
- package/dist/server/processing/processor.js +3 -3
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/3.OulZ54CG.css.br +0 -0
- package/build/client/_app/immutable/assets/3.OulZ54CG.css.gz +0 -0
- package/build/client/_app/immutable/chunks/B8-iUbxt.js.br +0 -0
- package/build/client/_app/immutable/chunks/B8-iUbxt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DItbpj5-.js +0 -2
- package/build/client/_app/immutable/chunks/DItbpj5-.js.br +0 -0
- package/build/client/_app/immutable/chunks/DItbpj5-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DTsuHg02.js.br +0 -0
- package/build/client/_app/immutable/chunks/DTsuHg02.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DUBuA2Dm.js +0 -1
- package/build/client/_app/immutable/chunks/DUBuA2Dm.js.br +0 -0
- package/build/client/_app/immutable/chunks/DUBuA2Dm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Pe5EQzUq.js +0 -1
- package/build/client/_app/immutable/chunks/Pe5EQzUq.js.br +0 -0
- package/build/client/_app/immutable/chunks/Pe5EQzUq.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CIuRQnTG.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CIuRQnTG.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CaIV0LUo.js +0 -1
- package/build/client/_app/immutable/entry/start.CaIV0LUo.js.br +0 -1
- package/build/client/_app/immutable/entry/start.CaIV0LUo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.D0OQowHt.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D0OQowHt.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CJmLUeSL.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CxPn7qxz.js +0 -4
- package/build/client/_app/immutable/nodes/2.CxPn7qxz.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CxPn7qxz.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CK4enFv6.js +0 -1
- package/build/client/_app/immutable/nodes/3.CK4enFv6.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CK4enFv6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.DxAj27VZ.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DxAj27VZ.js.gz +0 -0
- package/build/server/chunks/chunks/contract.js-CvgjfKhk.js.map +0 -1
- package/build/server/chunks/chunks/core.js-z3Oyfjut.js.map +0 -1
- package/build/server/chunks/chunks/manifest-membership.js-dUxJtQsv.js.map +0 -1
- package/build/server/chunks/chunks/raw-reconciliation.js-CVpSamHq.js.map +0 -1
- package/build/server/chunks/nodes/1.js-D_KuDyBR.js +0 -7
- package/build/server/chunks/nodes/3.js-7gq-At5G.js +0 -7
- package/build/server/chunks/nodes/4.js-DsoSGC53.js +0 -7
|
@@ -121,20 +121,32 @@ export async function loadTaggableNode(figma, nodeId) {
|
|
|
121
121
|
return node;
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* Every other node carrying `tag`, across every page. Scripts start with only
|
|
125
|
+
* the first page loaded, and `findAll` on an unloaded page silently misses
|
|
126
|
+
* content, so each page is loaded first.
|
|
127
127
|
*/
|
|
128
|
-
export async function
|
|
128
|
+
export async function findTagHolders(figma, tag, exceptNodeId) {
|
|
129
|
+
const holders = [];
|
|
129
130
|
for (const page of figma.root.children) {
|
|
130
131
|
await page.loadAsync();
|
|
131
|
-
const
|
|
132
|
+
const found = page.findAll((candidate) => candidate.id !== exceptNodeId &&
|
|
132
133
|
!isInstanceSublayerId(candidate.id) &&
|
|
133
134
|
candidate.getSharedPluginData(FIGMA_TAG_NAMESPACE, FIGMA_TAG_KEY) === tag);
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
for (const node of found)
|
|
136
|
+
holders.push({ node, page });
|
|
136
137
|
}
|
|
137
|
-
return
|
|
138
|
+
return holders;
|
|
139
|
+
}
|
|
140
|
+
/** Clear the node's tag and its Fundus annotation; returns whether an annotation was removed. */
|
|
141
|
+
export function clearFundusTag(node) {
|
|
142
|
+
node.setSharedPluginData(FIGMA_TAG_NAMESPACE, FIGMA_TAG_KEY, '');
|
|
143
|
+
if (!supportsAnnotations(node)) {
|
|
144
|
+
node.setSharedPluginData(FIGMA_TAG_NAMESPACE, FIGMA_ANNOTATION_LABEL_KEY, '');
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
const before = node.annotations.length;
|
|
148
|
+
stripFundusAnnotation(node);
|
|
149
|
+
return node.annotations.length < before;
|
|
138
150
|
}
|
|
139
151
|
/**
|
|
140
152
|
* Tag a node, re-checking the live document right before writing: the CLI's
|
|
@@ -149,10 +161,13 @@ export async function setFundusTag(figma, input) {
|
|
|
149
161
|
if (!input.force && previous !== '') {
|
|
150
162
|
throw new Error(`Node "${node.name}" (${node.id}) already carries tag "${previous}"; rerun prepare-set-tag with --force to replace it.`);
|
|
151
163
|
}
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
|
|
164
|
+
const holders = await findTagHolders(figma, input.tag, node.id);
|
|
165
|
+
if (!input.force && holders.length > 0) {
|
|
166
|
+
const [holder] = holders;
|
|
167
|
+
throw new Error(`Tag "${input.tag}" is already used by "${holder.node.name}" (${holder.node.id}) on page "${holder.page.name}"; choose another tag, or rerun prepare-set-tag with --force to move it here.`);
|
|
155
168
|
}
|
|
169
|
+
for (const holder of holders)
|
|
170
|
+
clearFundusTag(holder.node);
|
|
156
171
|
node.setSharedPluginData(FIGMA_TAG_NAMESPACE, FIGMA_TAG_KEY, input.tag);
|
|
157
172
|
const annotated = supportsAnnotations(node);
|
|
158
173
|
if (annotated)
|
|
@@ -163,7 +178,12 @@ export async function setFundusTag(figma, input) {
|
|
|
163
178
|
nodeName: node.name,
|
|
164
179
|
tag: input.tag,
|
|
165
180
|
previousTag: previous === '' ? null : previous,
|
|
166
|
-
annotated
|
|
181
|
+
annotated,
|
|
182
|
+
movedFrom: holders.map((holder) => ({
|
|
183
|
+
nodeId: holder.node.id,
|
|
184
|
+
nodeName: holder.node.name,
|
|
185
|
+
page: holder.page.name
|
|
186
|
+
}))
|
|
167
187
|
};
|
|
168
188
|
}
|
|
169
189
|
export async function deleteFundusTag(figma, input) {
|
|
@@ -174,16 +194,7 @@ export async function deleteFundusTag(figma, input) {
|
|
|
174
194
|
? `Node "${node.name}" (${node.id}) carries no Fundus tag.`
|
|
175
195
|
: `Node "${node.name}" (${node.id}) carries tag "${current}", not "${input.tag}"; rerun prepare-delete-tag.`);
|
|
176
196
|
}
|
|
177
|
-
|
|
178
|
-
let annotationRemoved = false;
|
|
179
|
-
if (supportsAnnotations(node)) {
|
|
180
|
-
const before = node.annotations.length;
|
|
181
|
-
stripFundusAnnotation(node);
|
|
182
|
-
annotationRemoved = node.annotations.length < before;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
node.setSharedPluginData(FIGMA_TAG_NAMESPACE, FIGMA_ANNOTATION_LABEL_KEY, '');
|
|
186
|
-
}
|
|
197
|
+
const annotationRemoved = clearFundusTag(node);
|
|
187
198
|
return {
|
|
188
199
|
ok: true,
|
|
189
200
|
nodeId: node.id,
|
|
@@ -208,7 +219,8 @@ const SCRIPT_FUNCTIONS = [
|
|
|
208
219
|
stripFundusAnnotation,
|
|
209
220
|
pageOfNode,
|
|
210
221
|
loadTaggableNode,
|
|
211
|
-
|
|
222
|
+
findTagHolders,
|
|
223
|
+
clearFundusTag,
|
|
212
224
|
setFundusTag,
|
|
213
225
|
deleteFundusTag
|
|
214
226
|
];
|
package/dist/core/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { createEmptyLibrary, parseLibrary, serializeLibrary } from './library.js
|
|
|
3
3
|
export { rawFileNameError, rawFilePathError, portableRawPathKey, isFundusTransactionName } from './raw-path.js';
|
|
4
4
|
export { ROOT_FOLDER, FOLDER_SENTINEL, folderNameError, folderPathError, normalizeFolderPath, portableFolderPathKey, parentFolderPath, folderNameOf, joinFolderPath, folderOfRawPath, rawFileNameOfPath, isFolderOrDescendant, replaceFolderPrefix } from './folder-path.js';
|
|
5
5
|
export { RESERVED_ASSET_IDS, isValidAssetId, assetIdError, manifestNameError } from './asset-id.js';
|
|
6
|
-
export type { SetFigmaTagInput, SetFigmaTagResult, DeleteFigmaTagInput, DeleteFigmaTagResult } from './figma-tag-script.js';
|
|
6
|
+
export type { SetFigmaTagInput, SetFigmaTagResult, FigmaTagHolder, DeleteFigmaTagInput, DeleteFigmaTagResult } from './figma-tag-script.js';
|
|
7
7
|
export { buildSetTagScript, buildDeleteTagScript, isInstanceSublayerId } from './figma-tag-script.js';
|
|
8
8
|
export type { ManifestColor } from './manifest-appearance.js';
|
|
9
9
|
export { MANIFEST_COLORS, MANIFEST_EMOJIS, isManifestColor, isManifestEmoji, resolveManifestColor, manifestColorError, manifestEmojiError } from './manifest-appearance.js';
|
|
@@ -31,7 +31,7 @@ export type { AssetTypePluginDescriptor } from './plugins/contract.js';
|
|
|
31
31
|
export type { ImageFormat, ImagePreset, ImagePluginConfig, ImageParameters } from './plugins/image.js';
|
|
32
32
|
export { imagePlugin, validateImagePreset, IMAGE_BUILT_IN_PRESETS, RASTER_IMAGE_EXTENSIONS } from './plugins/image.js';
|
|
33
33
|
export type { SliceCompression, SliceParameters, SlicePluginConfig } from './plugins/slice.js';
|
|
34
|
-
export { processedSliceInsets, slicePlugin, SLICE_BUILT_IN_PRESETS } from './plugins/slice.js';
|
|
34
|
+
export { processedSliceInsets, sliceEntryGeometry, slicePlugin, SLICE_BUILT_IN_PRESETS } from './plugins/slice.js';
|
|
35
35
|
export type { CustomFfmpegVideoPreset, FfmpegArgument, FfmpegArgumentTokens, FfmpegCommandPaths, FfmpegPathToken, ResolvedCustomFfmpegVideoPreset, ResolvedVideoPreset, StructuredVideoPreset, VideoPreset, VideoPluginConfig, VideoParameters } from './plugins/video.js';
|
|
36
36
|
export { isCustomFfmpegVideoPreset, materializeFfmpegCommand, videoPlugin, validateVideoPreset, validateVideoParameters, VIDEO_BUILT_IN_PRESETS, VIDEO_EXTENSIONS } from './plugins/video.js';
|
|
37
37
|
export type { ChromaKeyVideoParameters } from './plugins/chroma-key-video.js';
|
package/dist/core/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { isAssetReference } from './asset-reference.js';
|
|
|
15
15
|
export { collectAllReferences, referenceErrors, referenceClosure, topologicalReferenceOrder } from './reference-graph.js';
|
|
16
16
|
export { computeLibraryMembership, allDeliveredAssetIds } from './manifest-membership.js';
|
|
17
17
|
export { imagePlugin, validateImagePreset, IMAGE_BUILT_IN_PRESETS, RASTER_IMAGE_EXTENSIONS } from './plugins/image.js';
|
|
18
|
-
export { processedSliceInsets, slicePlugin, SLICE_BUILT_IN_PRESETS } from './plugins/slice.js';
|
|
18
|
+
export { processedSliceInsets, sliceEntryGeometry, slicePlugin, SLICE_BUILT_IN_PRESETS } from './plugins/slice.js';
|
|
19
19
|
export { isCustomFfmpegVideoPreset, materializeFfmpegCommand, videoPlugin, validateVideoPreset, validateVideoParameters, VIDEO_BUILT_IN_PRESETS, VIDEO_EXTENSIONS } from './plugins/video.js';
|
|
20
20
|
export { chromaKeyVideoPlugin } from './plugins/chroma-key-video.js';
|
|
21
21
|
export { audioPlugin, validateAudioPreset, AUDIO_BUILT_IN_PRESETS, AUDIO_EXTENSIONS } from './plugins/audio.js';
|
|
@@ -43,6 +43,13 @@ export interface SliceManifestEntry extends AssetIdentified {
|
|
|
43
43
|
/** Pixel dimensions of the delivered image the insets are measured in. */
|
|
44
44
|
sourceWidth: number;
|
|
45
45
|
sourceHeight: number;
|
|
46
|
+
/**
|
|
47
|
+
* Pixel dimensions before stretch compression; present only when compression
|
|
48
|
+
* shrank the delivered image. Layout sizing uses these, painting samples the
|
|
49
|
+
* delivered `sourceWidth` × `sourceHeight`.
|
|
50
|
+
*/
|
|
51
|
+
uncompressedWidth?: number;
|
|
52
|
+
uncompressedHeight?: number;
|
|
46
53
|
}
|
|
47
54
|
export interface VideoManifestEntry extends AssetIdentified {
|
|
48
55
|
kind: 'video';
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* preset). No `targetWidth`: fixed caps keep their authored source-pixel size.
|
|
6
6
|
*/
|
|
7
7
|
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
8
|
+
import type { SliceManifestEntry } from '../manifest-entry.js';
|
|
8
9
|
import { type SliceInsets, type SliceMode } from '../slice-data.js';
|
|
9
10
|
import type { ImagePluginConfig, ImagePreset } from './image.js';
|
|
10
11
|
export interface SliceParameters {
|
|
@@ -39,8 +40,19 @@ export declare function processedSliceInsets(options: {
|
|
|
39
40
|
width: number;
|
|
40
41
|
height: number;
|
|
41
42
|
}): SliceInsets;
|
|
43
|
+
interface Dimensions {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The geometry fields of a slice entry delivered at `delivered` pixels from a
|
|
49
|
+
* `raw` original — shared by codegen and the editor preview so both describe
|
|
50
|
+
* the image identically.
|
|
51
|
+
*/
|
|
52
|
+
export declare function sliceEntryGeometry(parameters: SliceParameters, delivered: Dimensions, raw: Dimensions): Pick<SliceManifestEntry, 'slicing' | 'sourceWidth' | 'sourceHeight' | 'uncompressedWidth' | 'uncompressedHeight'>;
|
|
42
53
|
/** Same shape as the image plugin's config: named output-format presets. */
|
|
43
54
|
export type SlicePluginConfig = ImagePluginConfig;
|
|
44
55
|
/** Built-in presets, always available; host config merges over them. */
|
|
45
56
|
export declare const SLICE_BUILT_IN_PRESETS: Record<string, ImagePreset>;
|
|
46
57
|
export declare const slicePlugin: AssetTypePluginDescriptor<SliceParameters, SlicePluginConfig>;
|
|
58
|
+
export {};
|
|
@@ -30,6 +30,32 @@ export function processedSliceInsets(options) {
|
|
|
30
30
|
const bottom = clamp(desiredBottom, 0, options.height - top);
|
|
31
31
|
return { top, right, bottom, left };
|
|
32
32
|
}
|
|
33
|
+
function hasAnySide(insets) {
|
|
34
|
+
if (!insets)
|
|
35
|
+
return false;
|
|
36
|
+
return insets.top > 0 || insets.right > 0 || insets.bottom > 0 || insets.left > 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The geometry fields of a slice entry delivered at `delivered` pixels from a
|
|
40
|
+
* `raw` original — shared by codegen and the editor preview so both describe
|
|
41
|
+
* the image identically.
|
|
42
|
+
*/
|
|
43
|
+
export function sliceEntryGeometry(parameters, delivered, raw) {
|
|
44
|
+
// Slices are never resized, so differing dimensions mean compression shrank the image.
|
|
45
|
+
const compressed = delivered.width !== raw.width || delivered.height !== raw.height;
|
|
46
|
+
return {
|
|
47
|
+
slicing: {
|
|
48
|
+
mode: parameters.mode,
|
|
49
|
+
insets: processedSliceInsets({ ...parameters, ...delivered }),
|
|
50
|
+
pixelRatio: parameters.pixelRatio,
|
|
51
|
+
// An all-zero overdraw is semantically "none" — keep the entry minimal.
|
|
52
|
+
...(hasAnySide(parameters.overdraw) ? { overdraw: parameters.overdraw } : {})
|
|
53
|
+
},
|
|
54
|
+
sourceWidth: delivered.width,
|
|
55
|
+
sourceHeight: delivered.height,
|
|
56
|
+
...(compressed ? { uncompressedWidth: raw.width, uncompressedHeight: raw.height } : {})
|
|
57
|
+
};
|
|
58
|
+
}
|
|
33
59
|
/** Built-in presets, always available; host config merges over them. */
|
|
34
60
|
// Deliberately separate from image presets: each namespace feeds its own job hash.
|
|
35
61
|
export const SLICE_BUILT_IN_PRESETS = {
|
|
@@ -27,7 +27,7 @@ import type { SliceInsets, SliceManifestEntry } from '../core/index.js';
|
|
|
27
27
|
* The manifest-entry fields the pure layout math needs — a structural subset
|
|
28
28
|
* of `SliceManifestEntry`, so hand-built fixtures stay minimal.
|
|
29
29
|
*/
|
|
30
|
-
export type SliceLayoutEntry = Pick<SliceManifestEntry, 'sourceWidth' | 'sourceHeight' | 'slicing'>;
|
|
30
|
+
export type SliceLayoutEntry = Pick<SliceManifestEntry, 'sourceWidth' | 'sourceHeight' | 'uncompressedWidth' | 'uncompressedHeight' | 'slicing'>;
|
|
31
31
|
/**
|
|
32
32
|
* The insets that actually apply for the entry's mode: a 3-slice mode zeroes out
|
|
33
33
|
* the axis it does not use (and `one` uses none at all), so callers never have
|
|
@@ -53,7 +53,9 @@ export interface SliceLayoutGeometry {
|
|
|
53
53
|
* image minus its baked-in overdraw, divided by the export `pixelRatio`. The
|
|
54
54
|
* core is what fills the layout box when `Slice` renders the entry; the
|
|
55
55
|
* overdraw band paints outside the box (like a CSS `box-shadow`), so these are
|
|
56
|
-
* the sizes to lay the element out at.
|
|
56
|
+
* the sizes to lay the element out at. Stretch compression is a delivery
|
|
57
|
+
* optimization, so the authored `uncompressedWidth` / `uncompressedHeight`
|
|
58
|
+
* take precedence over the delivered source size.
|
|
57
59
|
*
|
|
58
60
|
* The math is identical for all modes; the mode decides how a consumer should
|
|
59
61
|
* use the result:
|
package/dist/runtime/geometry.js
CHANGED
|
@@ -35,15 +35,15 @@ export function effectiveOverdraw(entry) {
|
|
|
35
35
|
return { top, right, bottom, left };
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* The layout box —
|
|
39
|
-
* Never smaller than one
|
|
40
|
-
* a remaining box).
|
|
38
|
+
* The layout box — an image of `width × height` pixels minus the entry's
|
|
39
|
+
* effective overdraw — in those pixels. Never smaller than one pixel per axis
|
|
40
|
+
* (`effectiveOverdraw` guarantees a remaining box).
|
|
41
41
|
*/
|
|
42
|
-
function
|
|
42
|
+
function coreSize(entry, width, height) {
|
|
43
43
|
const overdraw = effectiveOverdraw(entry);
|
|
44
44
|
return {
|
|
45
|
-
width: Math.max(1,
|
|
46
|
-
height: Math.max(1,
|
|
45
|
+
width: Math.max(1, width - overdraw.left - overdraw.right),
|
|
46
|
+
height: Math.max(1, height - overdraw.top - overdraw.bottom)
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
@@ -51,7 +51,9 @@ function sourceLayoutSize(entry) {
|
|
|
51
51
|
* image minus its baked-in overdraw, divided by the export `pixelRatio`. The
|
|
52
52
|
* core is what fills the layout box when `Slice` renders the entry; the
|
|
53
53
|
* overdraw band paints outside the box (like a CSS `box-shadow`), so these are
|
|
54
|
-
* the sizes to lay the element out at.
|
|
54
|
+
* the sizes to lay the element out at. Stretch compression is a delivery
|
|
55
|
+
* optimization, so the authored `uncompressedWidth` / `uncompressedHeight`
|
|
56
|
+
* take precedence over the delivered source size.
|
|
55
57
|
*
|
|
56
58
|
* The math is identical for all modes; the mode decides how a consumer should
|
|
57
59
|
* use the result:
|
|
@@ -72,12 +74,12 @@ function sourceLayoutSize(entry) {
|
|
|
72
74
|
* no float artifacts leak into style output.
|
|
73
75
|
*/
|
|
74
76
|
export function sliceLayoutGeometry(entry) {
|
|
75
|
-
const
|
|
77
|
+
const core = coreSize(entry, entry.uncompressedWidth ?? entry.sourceWidth, entry.uncompressedHeight ?? entry.sourceHeight);
|
|
76
78
|
const pixelRatio = entry.slicing.pixelRatio > 0 ? entry.slicing.pixelRatio : 1;
|
|
77
79
|
return {
|
|
78
|
-
width:
|
|
79
|
-
height:
|
|
80
|
-
aspectRatio: `${
|
|
80
|
+
width: core.width / pixelRatio,
|
|
81
|
+
height: core.height / pixelRatio,
|
|
82
|
+
aspectRatio: `${core.width} / ${core.height}`
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
@@ -94,7 +96,7 @@ export function computeOverdrawDestination(entry, boxWidth, boxHeight) {
|
|
|
94
96
|
return overdraw;
|
|
95
97
|
}
|
|
96
98
|
const pixelRatio = entry.slicing.pixelRatio > 0 ? entry.slicing.pixelRatio : 1;
|
|
97
|
-
const sourceLayout =
|
|
99
|
+
const sourceLayout = coreSize(entry, entry.sourceWidth, entry.sourceHeight);
|
|
98
100
|
let horizontalScale;
|
|
99
101
|
let verticalScale;
|
|
100
102
|
if (entry.slicing.mode === 'three-horizontal') {
|
|
@@ -32,16 +32,23 @@ const PREPARE_SET_TAG_HELP = `fundus figma prepare-set-tag — prepare a use_fig
|
|
|
32
32
|
Usage: fundus figma prepare-set-tag --node-id <id> --tag <tag>
|
|
33
33
|
[--file <key|link>] [--force] [--json]
|
|
34
34
|
|
|
35
|
-
Refuses, listing the file's existing tags, when another node
|
|
36
|
-
the tag
|
|
37
|
-
|
|
35
|
+
Refuses unless --force, listing the file's existing tags, when another node
|
|
36
|
+
already carries the tag; --force moves the tag (and its annotation) from those
|
|
37
|
+
nodes to this one, so it stays unique. Also refuses unless --force when the
|
|
38
|
+
node already carries a tag (the same one or another) or a Fundus asset with
|
|
39
|
+
that id exists from another source. An asset imported from this very node by
|
|
40
|
+
node id is not a conflict: tagging prepares its move to the tag
|
|
41
|
+
(\`fundus asset replace <tag> --from figma --figma-tag <tag>\` afterwards keeps
|
|
42
|
+
its id, parameters, and manifests).
|
|
38
43
|
|
|
39
44
|
Flags:
|
|
40
45
|
--node-id <id> the node to tag: 12:34 or 12-34
|
|
41
46
|
--tag <tag> the Fundus asset id to store on the node (camelCase)
|
|
42
47
|
--file <key|link> Figma file key or link; default:
|
|
43
48
|
import.figma.defaultFileKey from fundus.config.ts
|
|
44
|
-
--force replace the node's current tag,
|
|
49
|
+
--force always set the tag: replace the node's current tag,
|
|
50
|
+
rewrite the same tag, move it from other nodes, or tag
|
|
51
|
+
despite a Fundus asset from another source
|
|
45
52
|
--json print {"ok": true, "fileKey", "nodeId", "tag",
|
|
46
53
|
"previousTag", "warnings", "next", "code"}; a refusal
|
|
47
54
|
prints {"ok": false, "error": {"message"}, "existingTags"}
|
|
@@ -50,7 +57,7 @@ Flags:
|
|
|
50
57
|
|
|
51
58
|
Without --json the script goes to stdout, everything else to stderr.
|
|
52
59
|
The script returns {"ok": true, "nodeId", "nodeName", "tag", "previousTag",
|
|
53
|
-
"annotated"}. Afterwards: \`fundus asset ingest --from figma --figma-tag <tag>\`.
|
|
60
|
+
"annotated", "movedFrom": [{"nodeId", "nodeName", "page"}]}. Afterwards: \`fundus asset ingest --from figma --figma-tag <tag>\`.
|
|
54
61
|
|
|
55
62
|
Example:
|
|
56
63
|
fundus figma prepare-set-tag --file 'https://www.figma.com/design/abc/UI' \\
|
|
@@ -37,6 +37,13 @@ function tagListing(nodes) {
|
|
|
37
37
|
function describeNode(node) {
|
|
38
38
|
return `"${node.nodeName}" (${node.nodeId}) on page "${node.page}"`;
|
|
39
39
|
}
|
|
40
|
+
/** Whether `record` is a node-mode Figma import of `nodeId` in `fileKey`. */
|
|
41
|
+
function isNodeImportOf(record, fileKey, nodeId) {
|
|
42
|
+
if (record.importSource?.importer !== 'figma')
|
|
43
|
+
return false;
|
|
44
|
+
const source = validateFigmaSourceParameters(record.importSource.parameters);
|
|
45
|
+
return source.mode !== 'tag' && source.fileKey === fileKey && source.nodeId === nodeId;
|
|
46
|
+
}
|
|
40
47
|
/** The library asset imported from `tag` in `fileKey`, if any. */
|
|
41
48
|
function tagImportedAsset(library, fileKey, tag) {
|
|
42
49
|
const record = library.assets[tag];
|
|
@@ -55,12 +62,31 @@ export function planSetFigmaTag(request, { library, document }) {
|
|
|
55
62
|
const nodes = figmaDocumentNodes(document);
|
|
56
63
|
const existingTags = tagListing(nodes);
|
|
57
64
|
const warnings = [];
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
|
|
65
|
+
const holders = nodes.filter((node) => node.tag === tag && node.nodeId !== nodeId);
|
|
66
|
+
if (holders.length > 0) {
|
|
67
|
+
const described = holders.map(describeNode).join(', ');
|
|
68
|
+
if (!force) {
|
|
69
|
+
throw new FigmaTagConflictError(`Tag "${tag}" is already used by ${described} — choose another tag, or pass --force to move it to node ${nodeId} (existing tags are listed).`, existingTags);
|
|
70
|
+
}
|
|
71
|
+
warnings.push(`Moves tag "${tag}" from ${described} to node ${nodeId}.`);
|
|
61
72
|
}
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
const existing = Object.hasOwn(library.assets, tag) ? library.assets[tag] : null;
|
|
74
|
+
if (existing && tagImportedAsset(library, fileKey, tag) === null) {
|
|
75
|
+
// A replace without --scale falls back to the config default and resets a
|
|
76
|
+
// slice's pixelRatio to it, so keep the scale a Figma asset was imported at.
|
|
77
|
+
const savedScale = existing.importSource?.importer === 'figma'
|
|
78
|
+
? ` --scale ${validateFigmaSourceParameters(existing.importSource.parameters).scale}`
|
|
79
|
+
: '';
|
|
80
|
+
const replace = `\`fundus asset replace ${tag} --from figma --figma-tag ${tag} --figma-file ${fileKey}${savedScale}\``;
|
|
81
|
+
if (isNodeImportOf(existing, fileKey, nodeId)) {
|
|
82
|
+
warnings.push(`Fundus asset "${tag}" is imported from this node by id — afterwards run ${replace} to move it to the tag (id, parameters, folder, and manifests stay).`);
|
|
83
|
+
}
|
|
84
|
+
else if (!force) {
|
|
85
|
+
throw new FigmaTagConflictError(`A Fundus asset "${tag}" already exists and is imported neither from this tag nor from this node in file "${fileKey}" — choose another tag, or pass --force to tag the node anyway and then run ${replace}, which replaces the asset's current source.`, existingTags);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
warnings.push(`Fundus asset "${tag}" already exists from another source — afterwards run ${replace} to switch it to this tag; that replaces its current source.`);
|
|
89
|
+
}
|
|
64
90
|
}
|
|
65
91
|
const node = nodes.find((candidate) => candidate.nodeId === nodeId) ?? null;
|
|
66
92
|
if (!node) {
|
|
@@ -83,7 +109,7 @@ export function planSetFigmaTag(request, { library, document }) {
|
|
|
83
109
|
}
|
|
84
110
|
warnings.push(`Replaces tag "${previousTag}" on node ${nodeId}.${followUp}`);
|
|
85
111
|
}
|
|
86
|
-
if (tagImportedAsset(library, fileKey, tag) !== null &&
|
|
112
|
+
if (tagImportedAsset(library, fileKey, tag) !== null && previousTag !== tag) {
|
|
87
113
|
warnings.push(`Fundus asset "${tag}" is imported from this tag; its next \`fundus asset reimport ${tag}\` resolves to this node.`);
|
|
88
114
|
}
|
|
89
115
|
return {
|
|
@@ -15,6 +15,9 @@ export interface ProxyOutputInfo {
|
|
|
15
15
|
height?: number;
|
|
16
16
|
/** Media duration; present for video and audio proxies. */
|
|
17
17
|
durationMs?: number;
|
|
18
|
+
/** Raw source pixel dimensions; present for slice proxies (pre-compression layout size). */
|
|
19
|
+
rawWidth?: number;
|
|
20
|
+
rawHeight?: number;
|
|
18
21
|
}
|
|
19
22
|
/** One delivered asset's current proxy: file identity plus output metadata. */
|
|
20
23
|
export interface AssetProxyInfo extends ProxyOutputInfo {
|
|
@@ -101,7 +104,7 @@ export interface ServerAssetTypePlugin<TParameters = unknown, TPreset = unknown>
|
|
|
101
104
|
/** Run the processing job (sharp/ffmpeg): write the proxy, report its metadata. */
|
|
102
105
|
process(job: ServerProcessingJob<TParameters, TPreset>): Promise<ProxyOutputInfo>;
|
|
103
106
|
/** Probe an existing proxy file's output metadata (the up-to-date path). */
|
|
104
|
-
readProxyInfo(proxyPath: string, config: ResolvedFundusConfig): Promise<ProxyOutputInfo>;
|
|
107
|
+
readProxyInfo(proxyPath: string, config: ResolvedFundusConfig, rawPath: string): Promise<ProxyOutputInfo>;
|
|
105
108
|
/**
|
|
106
109
|
* Build the generated manifest entry. Reference fields stay
|
|
107
110
|
* `{ $asset }` markers — codegen's generic inline walk resolves them.
|
|
@@ -113,4 +116,4 @@ export type ServerPluginRegistry = Record<string, ServerAssetTypePlugin>;
|
|
|
113
116
|
/** Resolve a named preset out of a plugin's merged preset map, or throw listing the known names. */
|
|
114
117
|
export declare function resolveNamedPreset<TPreset>(label: string, presetName: string, presets: Record<string, TPreset>): TPreset;
|
|
115
118
|
/** A proxy-info field a given entry kind cannot do without. */
|
|
116
|
-
export declare function requireProxyField(proxy: AssetProxyInfo, field:
|
|
119
|
+
export declare function requireProxyField(proxy: AssetProxyInfo, field: keyof ProxyOutputInfo): number;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* redundant pixels from the stretchable regions. The slicing data itself is
|
|
5
5
|
* entry-only.
|
|
6
6
|
*/
|
|
7
|
-
import { SLICE_BUILT_IN_PRESETS, defaultInsetsForSize,
|
|
7
|
+
import { SLICE_BUILT_IN_PRESETS, defaultInsetsForSize, sliceEntryGeometry, slicePlugin, validateImagePreset } from '../../core/index.js';
|
|
8
8
|
import { readImageDimensions } from '../processing/image-processing.js';
|
|
9
9
|
import { processSliceImageFile } from '../processing/slice-processing.js';
|
|
10
10
|
import { extensionForImageFormat } from './image.js';
|
|
@@ -21,10 +21,10 @@ export const sliceConfigDefinition = {
|
|
|
21
21
|
})
|
|
22
22
|
})
|
|
23
23
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
24
|
+
/** Proxy info plus the raw dimensions the entry's pre-compression layout size needs. */
|
|
25
|
+
async function withRawDimensions(output, rawPath) {
|
|
26
|
+
const [info, raw] = await Promise.all([output, readImageDimensions(rawPath)]);
|
|
27
|
+
return { ...info, rawWidth: raw.width, rawHeight: raw.height };
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Best-effort guess of the asset's export scale from a `@2x` / `@3x` suffix in
|
|
@@ -53,7 +53,7 @@ export const sliceServerPlugin = {
|
|
|
53
53
|
pixelRatio: detectPixelRatio(rawFileName)
|
|
54
54
|
};
|
|
55
55
|
},
|
|
56
|
-
process: (job) => processSliceImageFile({
|
|
56
|
+
process: (job) => withRawDimensions(processSliceImageFile({
|
|
57
57
|
sourcePath: job.sourcePath,
|
|
58
58
|
targetPath: job.targetPath,
|
|
59
59
|
preset: job.preset,
|
|
@@ -61,29 +61,12 @@ export const sliceServerPlugin = {
|
|
|
61
61
|
insets: job.parameters.insets,
|
|
62
62
|
overdraw: job.parameters.overdraw,
|
|
63
63
|
compression: job.parameters.compression
|
|
64
|
-
}),
|
|
65
|
-
readProxyInfo: (proxyPath) => readImageDimensions(proxyPath),
|
|
66
|
-
buildManifestEntry: ({ parameters, src, proxy }) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
...parameters,
|
|
73
|
-
width: sourceWidth,
|
|
74
|
-
height: sourceHeight
|
|
75
|
-
}),
|
|
76
|
-
pixelRatio: parameters.pixelRatio,
|
|
77
|
-
// An all-zero overdraw is semantically "none" — keep the entry minimal.
|
|
78
|
-
...(hasAnySide(parameters.overdraw) ? { overdraw: parameters.overdraw } : {})
|
|
79
|
-
};
|
|
80
|
-
return {
|
|
81
|
-
kind: 'slice',
|
|
82
|
-
src,
|
|
83
|
-
byteSize: proxy.fileSize,
|
|
84
|
-
slicing,
|
|
85
|
-
sourceWidth,
|
|
86
|
-
sourceHeight
|
|
87
|
-
};
|
|
88
|
-
}
|
|
64
|
+
}), job.sourcePath),
|
|
65
|
+
readProxyInfo: (proxyPath, _config, rawPath) => withRawDimensions(readImageDimensions(proxyPath), rawPath),
|
|
66
|
+
buildManifestEntry: ({ parameters, src, proxy }) => ({
|
|
67
|
+
kind: 'slice',
|
|
68
|
+
src,
|
|
69
|
+
byteSize: proxy.fileSize,
|
|
70
|
+
...sliceEntryGeometry(parameters, { width: requireProxyField(proxy, 'width'), height: requireProxyField(proxy, 'height') }, { width: requireProxyField(proxy, 'rawWidth'), height: requireProxyField(proxy, 'rawHeight') })
|
|
71
|
+
})
|
|
89
72
|
};
|
|
@@ -34,7 +34,7 @@ export declare function expectedProxyRelativePath(assetId: string, record: Asset
|
|
|
34
34
|
* Output metadata of an existing proxy file, probed per the owning plugin's
|
|
35
35
|
* strategy: image-backed proxies via sharp, media proxies via ffprobe.
|
|
36
36
|
*/
|
|
37
|
-
export declare function readProxyOutputInfo(
|
|
37
|
+
export declare function readProxyOutputInfo(record: AssetRecord, proxyPath: string, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<ProxyOutputInfo>;
|
|
38
38
|
/** Read one asset's current proxy projection, or null when its expected file is absent. */
|
|
39
39
|
export declare function readCurrentProxy(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<AssetProxyInfo | null>;
|
|
40
40
|
/**
|
|
@@ -62,8 +62,8 @@ export function expectedProxyRelativePath(assetId, record, config, registry = se
|
|
|
62
62
|
* Output metadata of an existing proxy file, probed per the owning plugin's
|
|
63
63
|
* strategy: image-backed proxies via sharp, media proxies via ffprobe.
|
|
64
64
|
*/
|
|
65
|
-
export async function readProxyOutputInfo(
|
|
66
|
-
return serverPluginForType(type, registry).readProxyInfo(proxyPath, config);
|
|
65
|
+
export async function readProxyOutputInfo(record, proxyPath, config, registry = serverPluginsByType) {
|
|
66
|
+
return serverPluginForType(record.type, registry).readProxyInfo(proxyPath, config, join(config.rawDir, record.raw));
|
|
67
67
|
}
|
|
68
68
|
/** Read one asset's current proxy projection, or null when its expected file is absent. */
|
|
69
69
|
export async function readCurrentProxy(assetId, record, config, registry = serverPluginsByType) {
|
|
@@ -73,7 +73,7 @@ export async function readCurrentProxy(assetId, record, config, registry = serve
|
|
|
73
73
|
if (!existsSync(proxyPath))
|
|
74
74
|
return null;
|
|
75
75
|
const [outputInfo, proxyStat] = await Promise.all([
|
|
76
|
-
readProxyOutputInfo(record
|
|
76
|
+
readProxyOutputInfo(record, proxyPath, config, registry),
|
|
77
77
|
stat(proxyPath)
|
|
78
78
|
]);
|
|
79
79
|
return { assetId, fileName, relativePath, ...outputInfo, fileSize: proxyStat.size };
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{V as e,nt as t,tt as n}from"./DLEDBLL1.js";var r=class extends Error{status;constructor(e,t){super(t),this.name=`ApiResponseError`,this.status=e}};async function i(e){if(!e.ok){let t=`${e.status} ${e.statusText}`;try{let n=await e.json();n.error?.message&&(t=n.error.message)}catch{}throw new r(e.status,t)}return await e.json()}async function a(e){if(!e.ok){let t=`${e.status} ${e.statusText}`;try{let n=await e.json();n.error?.message&&(t=n.error.message)}catch{}throw new r(e.status,t)}}async function o(e){if(!e.ok)throw await a(e),new r(e.status,e.statusText);return await e.json()}async function s(){let e=await fetch(`/api/usage`,{method:`HEAD`,cache:`no-store`}),t=e.headers.get(`x-fundus-usage-capability`);if(e.status===204&&t===`available`)return!0;if(e.status===404&&t===`unavailable`)return!1;throw new r(e.status,`Could not discover usage capability (${e.status}, ${t??`missing header`}).`)}async function c(e={}){let t=new URLSearchParams;e.assetId&&t.set(`assetId`,e.assetId),e.forceFresh&&t.set(`fresh`,`1`);let n=t.size>0?`?${t}`:``;return o(await fetch(`/api/usage${n}`,{cache:`no-store`}))}async function l(e){await a(await fetch(`/api/usage`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e})}))}async function u(){return i(await fetch(`/api/state`,{cache:`no-store`}))}async function d(){return i(await fetch(`/api/reconcile`,{method:`POST`,cache:`no-store`,headers:{"Content-Type":`application/json`},body:JSON.stringify({})}))}async function f(e,t,n,r=[]){return i(await fetch(`/api/raw/adopt`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e,id:t,type:n,manifests:r})}))}async function ee(e,t){return i(await fetch(`/api/raw/edited/accept`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e,type:t})}))}async function te(e){return i(await fetch(`/api/raw/edited/dismiss`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:e})}))}async function ne(e,t,n,r,a=[]){let o=new FormData;o.set(`file`,e),o.set(`type`,t),o.set(`id`,n),o.set(`folder`,r);for(let e of a)o.append(`manifests`,e);return i(await fetch(`/api/assets`,{method:`POST`,body:o}))}async function re(e){let t=e.mode===`tag`?{importer:`figma`,input:{mode:`tag`,tag:e.tag,format:`png`,...e.fileKey?{fileKey:e.fileKey}:{},scale:e.scale},type:e.type,id:e.tag,folder:e.folder,manifests:e.manifests}:{importer:`figma`,input:{url:e.url,format:`png`,scale:e.scale},type:e.type,id:e.id,folder:e.folder,manifests:e.manifests};return i(await fetch(`/api/imports`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}))}async function ie(e,t){return i(await fetch(`/api/assets/${encodeURIComponent(e)}/reimport`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({...t?{input:{...t,format:`png`}}:{}})}))}async function ae(e,t){return i(await fetch(`/api/assets/${encodeURIComponent(e)}/retag`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}))}async function oe(e,t){let n=new FormData;return n.set(`file`,t),i(await fetch(`/api/assets/${encodeURIComponent(e)}/raw`,{method:`PUT`,body:n}))}async function se(e,t){let n=t.mode===`tag`?{mode:`tag`,tag:t.tag,format:`png`,...t.fileKey?{fileKey:t.fileKey}:{},scale:t.scale}:{url:t.url,scale:t.scale,format:`png`};return i(await fetch(`/api/assets/${encodeURIComponent(e)}/raw`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({importer:`figma`,input:n})}))}async function ce(e){return i(await fetch(`/api/folders`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})}))}async function le(e,t){return i(await fetch(`/api/folders/${encodeURIComponent(e)}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:t})}))}async function ue(e,t){return i(await fetch(`/api/folders/${encodeURIComponent(e)}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({parent:t})}))}async function de(e){return i(await fetch(`/api/folders/${encodeURIComponent(e)}`,{method:`DELETE`}))}async function fe(e,t){return i(await fetch(`/api/assets/${encodeURIComponent(e)}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}))}async function pe(e,t){return i(await fetch(`/api/assets/move`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({assetIds:e,folder:t})}))}async function me(e){return i(await fetch(`/api/assets/${encodeURIComponent(e)}`,{method:`DELETE`}))}async function he(e){return i(await fetch(`/api/assets/delete`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({assetIds:e})}))}async function ge(e,t){return a(await fetch(`/api/reveal`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({assetId:e,kind:t})}))}async function _e(e){return a(await fetch(`/api/reveal`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({folder:e})}))}async function ve(e){return i(await fetch(`/api/manifests`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:e})}))}async function ye(e){return i(await fetch(`/api/manifests/${encodeURIComponent(e)}`,{method:`DELETE`}))}async function be(e,t){return i(await fetch(`/api/manifests/${encodeURIComponent(e)}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)}))}var xe=class{#e=null;#t=null;#n=0;#r=[];get value(){return this.#t}replaceBase(e){this.#e=e,this.#a()}mergeBase(e){this.#e!==null&&(this.#e=e(this.#e),this.#a())}add(e,t=[]){let n=++this.#n;return this.#r.push({id:n,update:e,dependencies:t}),this.#t!==null&&(this.#t=e(this.#t)),n}confirm(e,t,n){let r=this.#r.findIndex(t=>t.id===e);if(r===-1)return;let i=this.#r[r],a=this.#e;if(a===null){this.#e=t,this.#i(e),this.#a();return}this.#e=n(i.update(a)),this.#i(e),r===0&&this.#t!==null?this.#t=n(this.#t):this.#a()}reject(e){this.#i(e),this.#a()}rejectAffectedFrom(e,t){this.#r=this.#r.filter(n=>n.id<e||n.id>e&&!p(n.dependencies,t)),this.#a()}#i(e){this.#r=this.#r.filter(t=>t.id!==e)}#a(){let e=this.#e;if(e===null){this.#t=null;return}let t=e;for(let e of this.#r)t=e.update(t);this.#t=t}};function p(e,t){return e.some(e=>t.some(t=>{if(e===t)return!0;if(!e.startsWith(`folder:`)||!t.startsWith(`folder:`))return!1;let n=e.slice(7),r=t.slice(7);return n.startsWith(`${r}/`)||r.startsWith(`${n}/`)}))}var Se=/[\u0000-\u001f\u007f]/,Ce=/[<>:"|?*]/,we=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i;function Te(e){return e.length===0?`Folder names must not be empty.`:e===e.trim()?e===`.`||e===`..`?`"${e}" is not a valid folder name.`:e.includes(`/`)||e.includes(`\\`)?`Folder names must not contain path separators.`:Se.test(e)?`Folder names must not contain control characters.`:Ce.test(e)?`Folder names must not contain Windows-reserved characters (: * ? " < > |).`:e.endsWith(`.`)?`Folder names must not end with a dot.`:e.toLocaleLowerCase(`en-US`)===`root`?`"${e}" is reserved for the implicit Fundus root folder.`:e.toLocaleLowerCase(`en-US`)===`.gitkeep`?`"${e}" is reserved for Fundus folder management.`:we.test(e)?`"${e}" is a reserved filesystem name.`:null:`Folder names must not start or end with whitespace.`}function Ee(e){return e.split(`/`).map(e=>e.normalize(`NFC`)).join(`/`)}function m(e){return Ee(e).toLocaleLowerCase(`en-US`)}function h(e){let t=e.lastIndexOf(`/`);return t===-1?``:e.slice(0,t)}function g(e){let t=e.lastIndexOf(`/`);return t===-1?e:e.slice(t+1)}function _(e,t){return e===``?t:`${e}/${t}`}function v(e){return h(e)}function y(e){return g(e)}function b(e,t){return e===t||e.startsWith(`${t}/`)}function x(e,t,n){return e===t?n:`${n}${e.slice(t.length)}`}var S=`[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}`;RegExp(`^(?:\\.fundus-replace-${S}\\.tmp|\\.fundus-delete(?:-assets)?-${S})$`,`i`),RegExp(`\\.fundus-(?:replace-)?${S}$`,`i`);var C=[`preload`,`release`,`entries`,`constructor`,`prototype`,`hasOwnProperty`,`isPrototypeOf`,`propertyIsEnumerable`,`toLocaleString`,`toString`,`valueOf`],w=/^[a-z][A-Za-z0-9]*$/;function De(e){return w.test(e)?C.includes(e)?`"${e}" is reserved (it collides with a Manifest member name).`:null:`"${e}" is not a valid asset id — use a camelCase identifier starting with a lowercase letter (e.g. "navigationPanel").`}function Oe(e){return w.test(e)?C.includes(e)?`"${e}" is reserved (it collides with a Manifest member name).`:null:`"${e}" is not a valid manifest name — use a camelCase identifier starting with a lowercase letter (e.g. "mainCatalog").`}var T={type:`audio`,accepts:[`mp3`,`wav`,`m4a`,`ogg`,`flac`],defaultParameters:()=>({preset:`default`}),validateParameters(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Audio parameters must be an object.`);let t=e;if(typeof t.preset!=`string`||t.preset.length===0)throw Error(`Audio parameters need a non-empty string "preset".`);return{preset:t.preset}}};function ke(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&typeof e.$asset==`string`&&e.$asset!==``}var E={keyColor:[0,1,0],colorCutoff:.2,colorFeathering:.33,maskFeathering:1,sharpening:.5,despill:1,despillLuminanceAdd:.2,despillCoverage:1},Ae=[`colorCutoff`,`colorFeathering`,`maskFeathering`,`sharpening`,`despill`,`despillLuminanceAdd`];function D(e){return Math.min(1,Math.max(0,e))}function je(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`"chromaKey" must be an object.`);let t=e,n=t.keyColor;if(!Array.isArray(n)||n.length!==3||n.some(e=>typeof e!=`number`||!Number.isFinite(e)))throw Error(`"chromaKey.keyColor" must be an array of three numbers (RGB, 0–1 each).`);let r={keyColor:[D(n[0]),D(n[1]),D(n[2])]};for(let e of Ae){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))throw Error(`"chromaKey.${e}" must be a number (0–1).`);r[e]=D(n)}let i=t.despillCoverage;if(i!==void 0){if(typeof i!=`number`||!Number.isFinite(i))throw Error(`"chromaKey.despillCoverage" must be a number (0–1).`);r.despillCoverage=D(i)}return r}var O=[`mp4`,`mov`,`webm`,`m4v`];function k(e,t){if(typeof t!=`object`||!t||Array.isArray(t))throw Error(`${e} parameters must be an object.`);let n=t;if(typeof n.preset!=`string`||n.preset.length===0)throw Error(`${e} parameters need a non-empty string "preset".`);if(n.maxWidth!==void 0&&(typeof n.maxWidth!=`number`||!Number.isInteger(n.maxWidth)||n.maxWidth<=0))throw Error(`${e} "maxWidth" must be a positive integer when set.`);return{preset:n.preset,...n.maxWidth===void 0?{}:{maxWidth:n.maxWidth}}}var A={type:`video`,accepts:[...O],defaultParameters:()=>({preset:`default`}),validateParameters(e){return k(`Video`,e)}};function j(e,t){if(t!==void 0){if(!ke(t))throw Error(`"${e}" must be an asset reference ({ "$asset": "<id>" }).`);return{$asset:t.$asset}}}var M={type:`chroma-key-video`,accepts:[...O],defaultParameters:()=>({preset:`default`,chromaKey:{...E,keyColor:[...E.keyColor]}}),validateParameters(e){let t=k(`Chroma-key-video`,e),n=e,r=je(n.chromaKey),i=j(`mask`,n.mask),a=j(`fallback`,n.fallback);return{...t,chromaKey:r,...i===void 0?{}:{mask:i},...a===void 0?{}:{fallback:a}}},collectReferences(e){let t=[];return e.mask&&t.push({field:`mask`,assetId:e.mask.$asset,expectedTypes:[`image`]}),e.fallback&&t.push({field:`fallback`,assetId:e.fallback.$asset,expectedTypes:[`image`]}),t},processingParameters(e){return{preset:e.preset,...e.maxWidth===void 0?{}:{maxWidth:e.maxWidth}}}},N=[`png`,`jpg`,`jpeg`,`webp`,`avif`],P={type:`image`,accepts:[...N],defaultParameters:()=>({preset:`default`}),validateParameters(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Image parameters must be an object.`);let t=e;if(typeof t.preset!=`string`||t.preset.length===0)throw Error(`Image parameters need a non-empty string "preset".`);if(t.targetWidth!==void 0&&(typeof t.targetWidth!=`number`||!Number.isInteger(t.targetWidth)||t.targetWidth<=0))throw Error(`Image "targetWidth" must be a positive integer when set.`);return{preset:t.preset,...t.targetWidth===void 0?{}:{targetWidth:t.targetWidth}}}},F=[`nine`,`three-horizontal`,`three-vertical`,`one`];function I(){return{top:0,right:0,bottom:0,left:0}}function L(e){return{horizontal:e===`nine`||e===`three-horizontal`,vertical:e===`nine`||e===`three-vertical`}}function R(e,t,n,r){return t===`left`?n-e.right:t===`right`?n-e.left:t===`top`?r-e.bottom:r-e.top}function Me(e,t,n,r){return R(e,t,n,r)-1}function z(e,t,n){return Math.max(t,Math.min(n,e))}function Ne(e){let t=L(e.mode),n=e.overdraw??{top:0,right:0,bottom:0,left:0},r=t.horizontal&&(e.compression?.x??0)>0,i=t.vertical&&(e.compression?.y??0)>0,a=r?Math.max(e.insets.left,n.left):e.insets.left,o=r?Math.max(e.insets.right,n.right):e.insets.right,s=i?Math.max(e.insets.top,n.top):e.insets.top,c=i?Math.max(e.insets.bottom,n.bottom):e.insets.bottom,l=z(a,0,e.width),u=z(o,0,e.width-l),d=z(s,0,e.height);return{top:d,right:u,bottom:z(c,0,e.height-d),left:l}}function B(e,t){if(typeof t!=`object`||!t||Array.isArray(t))throw Error(`Slice ${e} must be an object with top/right/bottom/left.`);let n=t,r=[`top`,`right`,`bottom`,`left`],i=I();for(let t of r){let r=n[t];if(typeof r!=`number`||!Number.isInteger(r)||r<0)throw Error(`Slice ${e}.${t} must be a non-negative integer.`);i[t]=r}return i}function Pe(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Slice compression must be an object with x/y percentages.`);let t=e,n={x:0,y:0};for(let e of[`x`,`y`]){let r=t[e];if(typeof r!=`number`||!Number.isInteger(r)||r<0||r>100)throw Error(`Slice compression.${e} must be an integer from 0 to 100.`);n[e]=r}return n}var V={type:`slice`,accepts:[...N],defaultParameters:()=>({mode:`nine`,insets:I(),pixelRatio:1,preset:`default`}),validateParameters(e){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Slice parameters must be an object.`);let t=e;if(typeof t.mode!=`string`||!F.includes(t.mode))throw Error(`Slice "mode" must be one of ${F.map(e=>`'${e}'`).join(` | `)}.`);let n=B(`insets`,t.insets);if(typeof t.pixelRatio!=`number`||!Number.isInteger(t.pixelRatio)||t.pixelRatio<1)throw Error(`Slice "pixelRatio" must be an integer ≥ 1.`);let r=t.overdraw===void 0?void 0:B(`overdraw`,t.overdraw),i=t.compression===void 0?void 0:Pe(t.compression);if(typeof t.preset!=`string`||t.preset.length===0)throw Error(`Slice parameters need a non-empty string "preset".`);return{mode:t.mode,insets:n,pixelRatio:t.pixelRatio,...r===void 0?{}:{overdraw:r},...i===void 0?{}:{compression:i},preset:t.preset}}},H=new class{#e=new Map;register(e){if(this.#e.has(e.type))throw Error(`Plugin type "${e.type}" is already registered.`);this.#e.set(e.type,e)}has(e){return this.#e.has(e)}values(){return[...this.#e.values()]}types(){return[...this.#e.keys()]}project(e,t){let n=Object.create(null);for(let r of this.#e.keys()){if(!Object.hasOwn(e,r))throw Error(`Registered plugin "${r}" has no ${t}.`);n[r]=e[r]}return n}};H.register(P),H.register(V),H.register(A),H.register(M),H.register(T);var U=H.values();function Fe(e=U){return[...new Set(e.flatMap(e=>e.accepts))].sort().map(e=>`.${e}`)}function Ie(e,t=U){return t.find(t=>t.type===e)?.accepts.map(e=>`.${e}`).sort()??[]}function Le(e,t=U){let n=e.slice(e.lastIndexOf(`.`)+1).toLowerCase();return t.filter(e=>e.accepts.includes(n)).map(e=>e.type)}function W(e){return K(e)}function G(e,t){return e<t?-1:+(e>t)}function K(e){if(e===void 0)return`null`;if(e===null||typeof e==`number`||typeof e==`boolean`||typeof e==`string`)return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(K).join(`,`)}]`;if(typeof e==`object`){let t=e,n=[];for(let e of Object.keys(t).sort())t[e]!==void 0&&n.push(`${JSON.stringify(e)}:${K(t[e])}`);return`{${n.join(`,`)}}`}throw Error(`Cannot canonicalize a ${typeof e} value.`)}function Re(e,t){let n={};for(let[r,i]of Object.entries(e.assets)){n[r]=[];let e=t[i.type];if(e?.collectReferences)try{n[r]=e.collectReferences(e.validateParameters(i.parameters))}catch{}}return n}function ze(e,t){let n=new Set,r=[...e];for(;r.length>0;){let e=r.pop();if(!(n.has(e)||!Object.hasOwn(t,e))){n.add(e);for(let n of t[e])r.push(n.assetId)}}return n}function Be(e,t){let n=new Set(Object.keys(e.manifests));for(let t of Object.values(e.assets))for(let e of t.manifests)n.add(e);let r={};for(let i of[...n].sort(G)){let n=Object.entries(e.assets).filter(([,e])=>e.manifests.includes(i)).map(([e])=>e).sort(G),a=new Set(n),o=new Set(n),s=new Map;for(let e of n)for(let n of ze([e],t)){if(o.add(n),a.has(n))continue;let t=s.get(n)??[];t.push(e),s.set(n,t)}r[i]={explicitMemberIds:n,passiveMembers:[...s.entries()].map(([e,t])=>({assetId:e,via:t})).sort((e,t)=>G(e.assetId,t.assetId)),deliveredAssetIds:o}}return r}function Ve(e,t,n,r){return n&&q(n,r)?`committed`:t&&q(t,r)?`rolled-back`:`unknown`}function q(e,t){return W(e)===W(t)}var J=Object.fromEntries(H.values().map(e=>[e.type,e]));function He(e,t,n){let r=e.library.assets[t]?.type,i=r?J[r]:void 0;if(!i)return[`asset:${t}`];try{let e=i.validateParameters(n),r=i.collectReferences?.(e)??[];return[...new Set([`asset:${t}`,...r.map(e=>`asset:${e.assetId}`)])]}catch{return[`asset:${t}`]}}function Y(e,t){let n=new Map(t.assets.map(e=>[e.id,e])),r={...e,assets:e.assets.map(e=>{let t=n.get(e.id);return t?{...e,rawUrl:t.rawUrl,rawHash:t.rawHash,gitStatus:t.gitStatus,importSource:t.importSource,proxy:t.proxy,warnings:t.warnings,duplicateIds:t.duplicateIds,passiveManifests:t.passiveManifests,references:t.references}:e}),gitBranch:t.gitBranch,importDefaults:t.importDefaults,issues:t.issues,presetNames:t.presetNames,presetCapabilities:t.presetCapabilities,rawIngestionQueue:t.rawIngestionQueue};return X(r,r.library)}function Ue(e,t){let n=structuredClone(e.library);n.folders=[...t.library.folders];for(let[e,r]of Object.entries(n.assets)){let i=t.library.assets[e];i?r.raw=i.raw:delete n.assets[e]}return Y(X(e,n),t)}function X(e,t,n=new Map){let r=new Map(e.assets.map(e=>[e.id,e])),i=new Map([...n.entries()].map(([e,t])=>[t,e])),a=Re(t,J),o=Be(t,a),s=We(o),c=new Map;for(let[e,t]of Object.entries(a))for(let n of t){let t=c.get(n.assetId)??[];t.push({field:n.field,assetId:e}),c.set(n.assetId,t)}let l=Object.entries(t.assets).map(([e,o])=>{let l=r.get(i.get(e)??e);return l?{...l,id:e,raw:o.raw,folder:v(o.raw),parameters:o.parameters,manifests:o.manifests,duplicateIds:l.duplicateIds.map(e=>n.get(e)??e).filter(e=>Object.hasOwn(t.assets,e)).sort(),passiveManifests:s.get(e)??[],references:{outgoing:a[e].map(e=>({field:e.field,assetId:e.assetId})),incoming:c.get(e)??[]}}:null}).filter(e=>e!==null).sort((e,t)=>e.id.localeCompare(t.id)),u=new Map(l.map(e=>[e.id,e.proxy?.fileSize??0])),d=Object.fromEntries(Object.keys(t.manifests).map(e=>{let t=o[e];return[e,{explicitMemberIds:t.explicitMemberIds,passiveMembers:t.passiveMembers}]})),f=Object.fromEntries(Object.keys(t.manifests).map(e=>[e,[...o[e].deliveredAssetIds].reduce((e,t)=>e+(u.get(t)??0),0)]));return{...e,library:t,assets:l,manifestMemberships:d,manifestDeliveredBytes:f,issues:e.issues.map(e=>({...e,...e.assetId&&n.has(e.assetId)?{assetId:n.get(e.assetId)}:{}}))}}function We(e){let t=new Map;for(let[n,r]of Object.entries(e))for(let e of r.passiveMembers){let{assetId:r,via:i}=e,a=t.get(r)??[];a.push({manifest:n,via:i}),t.set(r,a)}for(let e of t.values())e.sort((e,t)=>e.manifest.localeCompare(t.manifest));return t}function Z(e){return t=>{let n=structuredClone(t.library);return X(t,n,e(n)??new Map)}}function Ge(e,t){return Z(n=>{let r=n.assets[e];if(!r)return;t.parameters!==void 0&&(r.parameters=structuredClone(t.parameters)),t.manifests!==void 0&&(r.manifests=[...new Set(t.manifests)].sort());let i=t.renameTo??e;if(!(i!==e&&n.assets[i]))return i!==e&&(delete n.assets[e],n.assets[i]=r),t.folder!==void 0&&(r.raw=_(t.folder,y(r.raw))),i===e?void 0:new Map([[e,i]])})}function Ke(e,t){return Z(n=>{for(let r of new Set(e)){let e=n.assets[r];e&&(e.raw=_(t,y(e.raw)))}})}function qe(e){return Z(t=>{for(let n of new Set(e))delete t.assets[n]})}function Je(e){return Z(t=>{t.folders.includes(e)||t.folders.push(e),t.folders.sort()})}function Q(e,t){return Z(n=>{let r=n.folders.filter(t=>b(t,e)).map(n=>x(n,e,t)),i=new Set(n.folders.filter(t=>!b(t,e)).map(m));if(!r.some(e=>i.has(m(e)))){n.folders=n.folders.map(n=>b(n,e)?x(n,e,t):n).sort();for(let r of Object.values(n.assets)){let n=v(r.raw);b(n,e)&&(r.raw=_(x(n,e,t),y(r.raw)))}}})}function Ye(e,t){return Q(e,_(h(e),t))}function Xe(e,t){return Q(e,_(t,g(e)))}function Ze(e){return Z(t=>{t.folders=t.folders.filter(t=>!b(t,e));for(let[n,r]of Object.entries(t.assets))b(v(r.raw),e)&&delete t.assets[n]})}function Qe(e){return Z(t=>{Object.hasOwn(t.manifests,e)||(t.manifests[e]={})})}function $e(e,t){return Z(n=>{let r=n.manifests[e];if(r&&(t.color!==void 0&&(t.color===null?delete r.color:r.color=t.color),t.emoji!==void 0&&(t.emoji===null?delete r.emoji:r.emoji=t.emoji),!(t.renameTo===void 0||t.renameTo===e)&&!n.manifests[t.renameTo])){delete n.manifests[e],n.manifests[t.renameTo]=r;for(let r of Object.values(n.assets))r.manifests=r.manifests.map(n=>n===e?t.renameTo:n).sort()}})}function et(e){return Z(t=>{delete t.manifests[e];for(let n of Object.values(t.assets))n.manifests=n.manifests.filter(t=>t!==e)})}var tt=class{#e=Promise.resolve();#t=new Map;enqueue(e){let t=this.#e.then(e,e);return this.#e=t.then(()=>{},()=>{}),t}coalesce(e,t){let n=this.#t.get(e);if(n)return n;let r=this.enqueue(t).finally(()=>{this.#t.get(e)===r&&this.#t.delete(e)});return this.#t.set(e,r),r}},nt=class{#e=null;#t;constructor(e){this.#t=e}discover(e){if(this.#e)return this.#e;let t=(async()=>{try{this.#t({available:await e(),error:null})}catch(e){this.#t({available:null,error:e instanceof Error?e.message:String(e)})}})();return this.#e=t.finally(()=>{this.#e=null}),this.#e}};function rt(e,t,n){return e??t??(n.length>0?n.join(`
|
|
2
|
-
`):null)}var it=new class{#e=new tt;#t=new xe;#n=0;#r=0;#i=[];#a=t(null);get state(){return e(this.#a)}set state(e){n(this.#a,e,!0)}#o=t(!1);get saving(){return e(this.#o)}set saving(e){n(this.#o,e,!0)}#s=t(null);get error(){return e(this.#s)}set error(e){n(this.#s,e,!0)}#c=t(null);get usageAvailable(){return e(this.#c)}set usageAvailable(e){n(this.#c,e,!0)}#l=t(null);get usageCapabilityError(){return e(this.#l)}set usageCapabilityError(e){n(this.#l,e,!0)}#u=t(0);get usageRefreshRevision(){return e(this.#u)}set usageRefreshRevision(e){n(this.#u,e,!0)}#d=new nt(e=>{this.usageAvailable=e.available,this.usageCapabilityError=e.error});#f=t(!1);get loaded(){return e(this.#f)}set loaded(e){n(this.#f,e,!0)}async refresh(){return this.#p(u,{rebaseEditableState:!0})}discoverUsageCapability(){return this.#d.discover(s)}reconcile(){return this.#p(d,{coalesce:!0})}async reconcileOnFocus(){let e=this.usageRefreshRevision;await this.reconcile(),this.usageRefreshRevision===e&&this.usageRefreshRevision++}connectLibraryEvents(){if(typeof EventSource>`u`)return()=>{};let e=new EventSource(`/api/events`),t=()=>void this.reconcile();return e.addEventListener(`library-changed`,t),e.addEventListener(`open`,t),()=>{e.removeEventListener(`library-changed`,t),e.removeEventListener(`open`,t),e.close()}}#p(e,t={}){let n=async()=>{try{let n=await e();this.#t.value===null||t.rebaseEditableState?this.#t.replaceBase(n):this.#t.mergeBase(e=>Ue(e,n)),this.#h(),this.error=null}catch(e){this.error=e instanceof Error?e.message:String(e)}finally{this.loaded=!0}};return t.coalesce?this.#e.coalesce(`reconcile`,n):this.#e.enqueue(n)}mutate(e,t,n){return this.#m(e,t,n)}mutateFromServer(e,t){return this.#m(e,void 0,t)}#m(e,t,n={dependencies:[]}){let r=++this.#r,i=this.#t.value?.library,a=t?this.#t.add(t,n.dependencies):null,o=a===null?void 0:this.#t.value?.library;return a!==null&&this.#h(),this.#n+=1,this.saving=!0,this.#e.enqueue(async()=>{this.error=null;try{if(this.#i.some(e=>e.failedMutationId<r&&r<=e.throughMutationId&&p(n.dependencies,e.dependencies)))throw a!==null&&(this.#t.reject(a),this.#h()),new $;let t=await e();a===null?this.#t.replaceBase(t):this.#t.confirm(a,t,e=>Y(e,t)),this.#h()}catch(e){if(e instanceof $)throw this.error=e.message,e;let t=null;try{t=await u();let s=Ve(e,i,o,t.library);if(s===`committed`&&a!==null){let e=t;this.#t.confirm(a,e,t=>Y(t,e)),this.#h(),this.error=null;return}this.#t.replaceBase(t),s===`rolled-back`&&a!==null&&n.invalidatesOnFailure?.length?(this.#i.push({failedMutationId:r,throughMutationId:this.#r,dependencies:n.invalidatesOnFailure}),this.#t.rejectAffectedFrom(a,n.invalidatesOnFailure)):a!==null&&this.#t.reject(a)}catch{a!==null&&this.#t.reject(a)}throw this.#h(),this.error=e instanceof Error?e.message:String(e),e}finally{--this.#n,this.saving=this.#n>0,this.#n===0&&(this.#i=[])}})}#h(){this.state=this.#t.value}get manifestNames(){return Object.keys(this.state?.library.manifests??{}).sort()}manifestAppearance(e){return this.state?.library.manifests[e]??{}}get generalIssues(){return this.state?.issues.filter(e=>e.assetId===void 0).map(e=>e.message)??[]}assetById(e){return this.state?.assets.find(t=>t.id===e)??null}},$=class extends Error{constructor(){super(`Change was cancelled because an earlier structural save failed.`)}};export{fe as $,Oe as A,f as B,L as C,E as D,A as E,_ as F,de as G,ve as H,h as I,c as J,ye as K,m as L,g as M,v as N,T as O,b as P,l as Q,x as R,Me as S,M as T,me as U,ce as V,he as W,pe as X,re as Y,ue as Z,H as _,Qe as a,ae as at,I as b,et as c,ne as ct,Ge as d,be as et,$e as f,Le as g,Ie as h,Je as i,se as it,Te as j,De as k,Ke as l,Fe as m,rt as n,le as nt,qe as o,ge as ot,Ye as p,te as q,He as r,oe as rt,Ze as s,_e as st,it as t,ie as tt,Xe as u,Ne as v,P as w,R as x,V as y,ee as z};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|