fundus 0.3.0 → 0.5.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.
Files changed (219) hide show
  1. package/README.md +67 -2
  2. package/build/client/_app/immutable/assets/2.DLP0pEak.css +1 -0
  3. package/build/client/_app/immutable/assets/2.DLP0pEak.css.br +0 -0
  4. package/build/client/_app/immutable/assets/2.DLP0pEak.css.gz +0 -0
  5. package/build/client/_app/immutable/assets/{3.ChSpCG-T.css → 3.CmXD8k9d.css} +1 -1
  6. package/build/client/_app/immutable/assets/3.CmXD8k9d.css.br +0 -0
  7. package/build/client/_app/immutable/assets/3.CmXD8k9d.css.gz +0 -0
  8. package/build/client/_app/immutable/chunks/B1UabfW7.js +1 -0
  9. package/build/client/_app/immutable/chunks/B1UabfW7.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/B1UabfW7.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/BmCZn5E-.js +108 -0
  12. package/build/client/_app/immutable/chunks/BmCZn5E-.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/BmCZn5E-.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/{BPJdVAz5.js → BoEHtxM8.js} +1 -1
  15. package/build/client/_app/immutable/chunks/BoEHtxM8.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/BoEHtxM8.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/C-T1_s5P.js +1 -0
  18. package/build/client/_app/immutable/chunks/C-T1_s5P.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/C-T1_s5P.js.gz +0 -0
  20. package/build/client/_app/immutable/chunks/{CLcR3DTB.js → CN-G-m-B.js} +1 -1
  21. package/build/client/_app/immutable/chunks/CN-G-m-B.js.br +4 -0
  22. package/build/client/_app/immutable/chunks/CN-G-m-B.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/D5sQSsUv.js +1 -0
  24. package/build/client/_app/immutable/chunks/D5sQSsUv.js.br +0 -0
  25. package/build/client/_app/immutable/chunks/D5sQSsUv.js.gz +0 -0
  26. package/build/client/_app/immutable/chunks/D6VoU_x4.js +1 -0
  27. package/build/client/_app/immutable/chunks/D6VoU_x4.js.br +0 -0
  28. package/build/client/_app/immutable/chunks/D6VoU_x4.js.gz +0 -0
  29. package/build/client/_app/immutable/entry/{app.xrZU0FIS.js → app.fVDDVV_y.js} +2 -2
  30. package/build/client/_app/immutable/entry/app.fVDDVV_y.js.br +0 -0
  31. package/build/client/_app/immutable/entry/app.fVDDVV_y.js.gz +0 -0
  32. package/build/client/_app/immutable/entry/start.BiWgLLju.js +1 -0
  33. package/build/client/_app/immutable/entry/start.BiWgLLju.js.br +1 -0
  34. package/build/client/_app/immutable/entry/start.BiWgLLju.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/0.BLnSe7Ya.js +2 -0
  36. package/build/client/_app/immutable/nodes/0.BLnSe7Ya.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/0.BLnSe7Ya.js.gz +0 -0
  38. package/build/client/_app/immutable/nodes/{1.0KLmHZC4.js → 1.DEw3d5yL.js} +1 -1
  39. package/build/client/_app/immutable/nodes/1.DEw3d5yL.js.br +0 -0
  40. package/build/client/_app/immutable/nodes/1.DEw3d5yL.js.gz +0 -0
  41. package/build/client/_app/immutable/nodes/2.DnVTXPVk.js +3 -0
  42. package/build/client/_app/immutable/nodes/2.DnVTXPVk.js.br +0 -0
  43. package/build/client/_app/immutable/nodes/2.DnVTXPVk.js.gz +0 -0
  44. package/build/client/_app/immutable/nodes/3.1Hco6zU7.js +1 -0
  45. package/build/client/_app/immutable/nodes/3.1Hco6zU7.js.br +0 -0
  46. package/build/client/_app/immutable/nodes/3.1Hco6zU7.js.gz +0 -0
  47. package/build/client/_app/immutable/nodes/{4.CsuRmAsC.js → 4.CKbcoBw2.js} +2 -2
  48. package/build/client/_app/immutable/nodes/4.CKbcoBw2.js.br +0 -0
  49. package/build/client/_app/immutable/nodes/4.CKbcoBw2.js.gz +0 -0
  50. package/build/client/_app/version.json +1 -1
  51. package/build/client/_app/version.json.br +0 -0
  52. package/build/client/_app/version.json.gz +0 -0
  53. package/build/handler.js +7 -7
  54. package/build/index.js +7 -7
  55. package/build/server/chunks/chunks/asset-id.js-CFsW8jeL.js +48 -0
  56. package/build/server/chunks/chunks/asset-id.js-CFsW8jeL.js.map +1 -0
  57. package/build/server/chunks/chunks/{context.js-B77ZAVLi.js → context.js-bXe5hzjI.js} +573 -25
  58. package/build/server/chunks/chunks/context.js-bXe5hzjI.js.map +1 -0
  59. package/build/server/chunks/chunks/{library-store.js-DJ-JRE-g.js → contract.js-sHny3b-s.js} +133 -359
  60. package/build/server/chunks/chunks/contract.js-sHny3b-s.js.map +1 -0
  61. package/build/server/chunks/chunks/{core.js-EA_ysb1J.js → core.js-Clg1CHb6.js} +2 -48
  62. package/build/server/chunks/chunks/core.js-Clg1CHb6.js.map +1 -0
  63. package/build/server/chunks/chunks/{exports.js-ScnpVC8o.js → exports.js-FRj9eAnR.js} +2 -2
  64. package/build/server/chunks/chunks/{exports.js-ScnpVC8o.js.map → exports.js-FRj9eAnR.js.map} +1 -1
  65. package/build/server/chunks/chunks/{http.js-DyoR9XRI.js → http.js--6sDZFfm.js} +35 -3
  66. package/build/server/chunks/chunks/http.js--6sDZFfm.js.map +1 -0
  67. package/build/server/chunks/chunks/{internal.js-D_k3d8Qh.js → internal.js-D2Tk-vi1.js} +3 -3
  68. package/build/server/chunks/chunks/{internal.js-D_k3d8Qh.js.map → internal.js-D2Tk-vi1.js.map} +1 -1
  69. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
  70. package/build/server/chunks/chunks/raw-reconciliation.js-DOFtjkgu.js +343 -0
  71. package/build/server/chunks/chunks/raw-reconciliation.js-DOFtjkgu.js.map +1 -0
  72. package/build/server/chunks/chunks/raw-reconciliation2.js-DFwky2ou.js +11 -0
  73. package/build/server/chunks/chunks/raw-reconciliation2.js-DFwky2ou.js.map +1 -0
  74. package/build/server/chunks/chunks/{shared.js-MLULcRRJ.js → shared.js-hvokB2mY.js} +2 -2
  75. package/build/server/chunks/chunks/{shared.js-MLULcRRJ.js.map → shared.js-hvokB2mY.js.map} +1 -1
  76. package/build/server/chunks/chunks/{utils.js-D64X9Sx8.js → utils.js-VGlYdWt0.js} +3 -3
  77. package/build/server/chunks/chunks/{utils.js-D64X9Sx8.js.map → utils.js-VGlYdWt0.js.map} +1 -1
  78. package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-H4Pq1KAE.js → _server.ts.js-CkSErIdC.js} +7 -5
  79. package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-H4Pq1KAE.js.map → _server.ts.js-CkSErIdC.js.map} +1 -1
  80. package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BI3KHSfE.js → _server.ts.js-Csnt5ISN.js} +11 -6
  81. package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BI3KHSfE.js.map → _server.ts.js-Csnt5ISN.js.map} +1 -1
  82. package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BlNYUbXX.js → _server.ts.js-BVEQaFEu.js} +11 -6
  83. package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-BlNYUbXX.js.map → _server.ts.js-BVEQaFEu.js.map} +1 -1
  84. package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/_server.ts.js-DIaT5h8R.js +31 -0
  85. package/build/server/chunks/entries/endpoints/api/assets/_id_/reimport/_server.ts.js-DIaT5h8R.js.map +1 -0
  86. package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-j-_N540j.js → _server.ts.js-CgCr-jFC.js} +11 -6
  87. package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-j-_N540j.js.map → _server.ts.js-CgCr-jFC.js.map} +1 -1
  88. package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-BHKobzAg.js → _server.ts.js-BCvv4wq4.js} +11 -6
  89. package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-BHKobzAg.js.map → _server.ts.js-BCvv4wq4.js.map} +1 -1
  90. package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-BFKjIOCv.js → _server.ts.js-GbxbM9uI.js} +11 -6
  91. package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-BFKjIOCv.js.map → _server.ts.js-GbxbM9uI.js.map} +1 -1
  92. package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BxZeVw8y.js → _server.ts.js-BGjOvpRm.js} +11 -6
  93. package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-BxZeVw8y.js.map → _server.ts.js-BGjOvpRm.js.map} +1 -1
  94. package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-CO36U_CT.js → _server.ts.js-CJuzJXPs.js} +11 -6
  95. package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-CO36U_CT.js.map → _server.ts.js-CJuzJXPs.js.map} +1 -1
  96. package/build/server/chunks/entries/endpoints/api/imports/_server.ts.js-BYp42p6V.js +37 -0
  97. package/build/server/chunks/entries/endpoints/api/imports/_server.ts.js-BYp42p6V.js.map +1 -0
  98. package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-CBlg1Bkl.js → _server.ts.js-l_tkhnQ-.js} +11 -6
  99. package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-CBlg1Bkl.js.map → _server.ts.js-l_tkhnQ-.js.map} +1 -1
  100. package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DcGLCjIs.js → _server.ts.js-B-VXO3Xr.js} +11 -6
  101. package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-DcGLCjIs.js.map → _server.ts.js-B-VXO3Xr.js.map} +1 -1
  102. package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AqhtjOiL.js → _server.ts.js-DV7s20GV.js} +11 -6
  103. package/build/server/chunks/entries/endpoints/api/raw/adopt/{_server.ts.js-AqhtjOiL.js.map → _server.ts.js-DV7s20GV.js.map} +1 -1
  104. package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CSa6-DcU.js → _server.ts.js-CYgOpWYC.js} +11 -6
  105. package/build/server/chunks/entries/endpoints/api/raw/edited/accept/{_server.ts.js-CSa6-DcU.js.map → _server.ts.js-CYgOpWYC.js.map} +1 -1
  106. package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DnzZUoAo.js → _server.ts.js-7loll6HR.js} +11 -6
  107. package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/{_server.ts.js-DnzZUoAo.js.map → _server.ts.js-7loll6HR.js.map} +1 -1
  108. package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-B0KMprKq.js → _server.ts.js-CdxeTMZj.js} +11 -6
  109. package/build/server/chunks/entries/endpoints/api/reconcile/{_server.ts.js-B0KMprKq.js.map → _server.ts.js-CdxeTMZj.js.map} +1 -1
  110. package/build/server/chunks/entries/endpoints/api/reveal/{_server.ts.js-Dhj5O9Lv.js → _server.ts.js-CZWIskM4.js} +11 -6
  111. package/build/server/chunks/entries/endpoints/api/reveal/_server.ts.js-CZWIskM4.js.map +1 -0
  112. package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-B1N9o1Ne.js → _server.ts.js-CZFfpJkl.js} +11 -6
  113. package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-B1N9o1Ne.js.map → _server.ts.js-CZFfpJkl.js.map} +1 -1
  114. package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-B6HbnRqq.js → _server.ts.js-DB3i4juR.js} +7 -5
  115. package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-B6HbnRqq.js.map → _server.ts.js-DB3i4juR.js.map} +1 -1
  116. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-7wzPyG9v.js → _server.ts.js-BqarqenW.js} +11 -6
  117. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-7wzPyG9v.js.map → _server.ts.js-BqarqenW.js.map} +1 -1
  118. package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-BWztxYge.js → _server.ts.js-CXbrUCmh.js} +11 -6
  119. package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-BWztxYge.js.map → _server.ts.js-CXbrUCmh.js.map} +1 -1
  120. package/build/server/chunks/entries/{hooks.server.js-CVKLksJ1.js → hooks.server.js-6Yf-GF0J.js} +10 -5
  121. package/build/server/chunks/entries/{hooks.server.js-CVKLksJ1.js.map → hooks.server.js-6Yf-GF0J.js.map} +1 -1
  122. package/build/server/chunks/{handler-C22elTeC.js → handler-DHAu99Rv.js} +5 -5
  123. package/build/server/chunks/{handler-C22elTeC.js.map → handler-DHAu99Rv.js.map} +1 -1
  124. package/build/server/chunks/{index.js-CqN1-wfe.js → index.js-Bm1XIAPD.js} +5 -5
  125. package/build/server/chunks/{index.js-CqN1-wfe.js.map → index.js-Bm1XIAPD.js.map} +1 -1
  126. package/build/server/chunks/{manifest.js-5SY_cfDE.js → manifest.js-CCc26-hb.js} +40 -26
  127. package/build/server/chunks/manifest.js-CCc26-hb.js.map +1 -0
  128. package/build/server/chunks/nodes/0.js-Bpzcq6LZ.js +11 -0
  129. package/build/server/chunks/nodes/0.js-Bpzcq6LZ.js.map +1 -0
  130. package/build/server/chunks/nodes/1.js-CXgbNa8h.js +7 -0
  131. package/build/server/chunks/nodes/{1.js-B92oHDrL.js.map → 1.js-CXgbNa8h.js.map} +1 -1
  132. package/build/server/chunks/nodes/2.js-D56ryWAu.js +7 -0
  133. package/build/server/chunks/nodes/2.js-D56ryWAu.js.map +1 -0
  134. package/build/server/chunks/nodes/3.js-DMaZRwrN.js +7 -0
  135. package/build/server/chunks/nodes/{3.js-C1yCh8Tq.js.map → 3.js-DMaZRwrN.js.map} +1 -1
  136. package/build/server/chunks/nodes/4.js-BUgaRCEY.js +7 -0
  137. package/build/server/chunks/nodes/{4.js-B9BEDKwq.js.map → 4.js-BUgaRCEY.js.map} +1 -1
  138. package/dist/cli.js +2 -0
  139. package/dist/config.d.ts +1 -1
  140. package/dist/core/api-types.d.ts +12 -1
  141. package/dist/core/config.d.ts +12 -0
  142. package/dist/core/index.d.ts +3 -2
  143. package/dist/core/library.d.ts +8 -1
  144. package/dist/core/library.js +30 -6
  145. package/dist/server/cli-commands.d.ts +2 -1
  146. package/dist/server/cli-commands.js +155 -34
  147. package/dist/server/config-loader.d.ts +9 -1
  148. package/dist/server/config-loader.js +26 -1
  149. package/dist/server/http.d.ts +1 -0
  150. package/dist/server/http.js +37 -0
  151. package/dist/server/importers/contract.d.ts +29 -0
  152. package/dist/server/importers/contract.js +13 -0
  153. package/dist/server/importers/figma.d.ts +21 -0
  154. package/dist/server/importers/figma.js +386 -0
  155. package/dist/server/importers/file.d.ts +6 -0
  156. package/dist/server/importers/file.js +33 -0
  157. package/dist/server/importers/pinned-https.d.ts +21 -0
  158. package/dist/server/importers/pinned-https.js +70 -0
  159. package/dist/server/importers/registry.d.ts +12 -0
  160. package/dist/server/importers/registry.js +31 -0
  161. package/dist/server/importers.d.ts +2 -0
  162. package/dist/server/importers.js +7 -0
  163. package/dist/server/library-store.d.ts +19 -1
  164. package/dist/server/library-store.js +110 -14
  165. package/dist/server/operations.d.ts +13 -1
  166. package/dist/server/operations.js +159 -23
  167. package/package.json +1 -1
  168. package/build/client/_app/immutable/assets/2.AA5XNK5k.css +0 -1
  169. package/build/client/_app/immutable/assets/2.AA5XNK5k.css.br +0 -0
  170. package/build/client/_app/immutable/assets/2.AA5XNK5k.css.gz +0 -0
  171. package/build/client/_app/immutable/assets/3.ChSpCG-T.css.br +0 -0
  172. package/build/client/_app/immutable/assets/3.ChSpCG-T.css.gz +0 -0
  173. package/build/client/_app/immutable/chunks/BPJdVAz5.js.br +0 -0
  174. package/build/client/_app/immutable/chunks/BPJdVAz5.js.gz +0 -0
  175. package/build/client/_app/immutable/chunks/CLZr1L0m.js +0 -108
  176. package/build/client/_app/immutable/chunks/CLZr1L0m.js.br +0 -0
  177. package/build/client/_app/immutable/chunks/CLZr1L0m.js.gz +0 -0
  178. package/build/client/_app/immutable/chunks/CLcR3DTB.js.br +0 -4
  179. package/build/client/_app/immutable/chunks/CLcR3DTB.js.gz +0 -0
  180. package/build/client/_app/immutable/chunks/DkK6dXFi.js +0 -1
  181. package/build/client/_app/immutable/chunks/DkK6dXFi.js.br +0 -0
  182. package/build/client/_app/immutable/chunks/DkK6dXFi.js.gz +0 -0
  183. package/build/client/_app/immutable/chunks/RVmTMq_g.js +0 -1
  184. package/build/client/_app/immutable/chunks/RVmTMq_g.js.br +0 -0
  185. package/build/client/_app/immutable/chunks/RVmTMq_g.js.gz +0 -0
  186. package/build/client/_app/immutable/chunks/TZFh8bT7.js +0 -1
  187. package/build/client/_app/immutable/chunks/TZFh8bT7.js.br +0 -0
  188. package/build/client/_app/immutable/chunks/TZFh8bT7.js.gz +0 -0
  189. package/build/client/_app/immutable/entry/app.xrZU0FIS.js.br +0 -0
  190. package/build/client/_app/immutable/entry/app.xrZU0FIS.js.gz +0 -0
  191. package/build/client/_app/immutable/entry/start.BT85omvY.js +0 -1
  192. package/build/client/_app/immutable/entry/start.BT85omvY.js.br +0 -0
  193. package/build/client/_app/immutable/entry/start.BT85omvY.js.gz +0 -0
  194. package/build/client/_app/immutable/nodes/0.BjZZ-SXT.js +0 -2
  195. package/build/client/_app/immutable/nodes/0.BjZZ-SXT.js.br +0 -0
  196. package/build/client/_app/immutable/nodes/0.BjZZ-SXT.js.gz +0 -0
  197. package/build/client/_app/immutable/nodes/1.0KLmHZC4.js.br +0 -2
  198. package/build/client/_app/immutable/nodes/1.0KLmHZC4.js.gz +0 -0
  199. package/build/client/_app/immutable/nodes/2.BG8fXI4n.js +0 -3
  200. package/build/client/_app/immutable/nodes/2.BG8fXI4n.js.br +0 -0
  201. package/build/client/_app/immutable/nodes/2.BG8fXI4n.js.gz +0 -0
  202. package/build/client/_app/immutable/nodes/3.DMFh-x1Y.js +0 -1
  203. package/build/client/_app/immutable/nodes/3.DMFh-x1Y.js.br +0 -0
  204. package/build/client/_app/immutable/nodes/3.DMFh-x1Y.js.gz +0 -0
  205. package/build/client/_app/immutable/nodes/4.CsuRmAsC.js.br +0 -0
  206. package/build/client/_app/immutable/nodes/4.CsuRmAsC.js.gz +0 -0
  207. package/build/server/chunks/chunks/context.js-B77ZAVLi.js.map +0 -1
  208. package/build/server/chunks/chunks/core.js-EA_ysb1J.js.map +0 -1
  209. package/build/server/chunks/chunks/http.js-DyoR9XRI.js.map +0 -1
  210. package/build/server/chunks/chunks/library-store.js-DJ-JRE-g.js.map +0 -1
  211. package/build/server/chunks/entries/endpoints/api/reveal/_server.ts.js-Dhj5O9Lv.js.map +0 -1
  212. package/build/server/chunks/manifest.js-5SY_cfDE.js.map +0 -1
  213. package/build/server/chunks/nodes/0.js-CoD1zSiQ.js +0 -11
  214. package/build/server/chunks/nodes/0.js-CoD1zSiQ.js.map +0 -1
  215. package/build/server/chunks/nodes/1.js-B92oHDrL.js +0 -7
  216. package/build/server/chunks/nodes/2.js-COlJ22cI.js +0 -7
  217. package/build/server/chunks/nodes/2.js-COlJ22cI.js.map +0 -1
  218. package/build/server/chunks/nodes/3.js-C1yCh8Tq.js +0 -7
  219. package/build/server/chunks/nodes/4.js-B9BEDKwq.js +0 -7
@@ -8,7 +8,7 @@ import { existsSync } from 'node:fs';
8
8
  import { mkdir, readFile, rename, rm, unlink, writeFile } from 'node:fs/promises';
9
9
  import { dirname, extname, join } from 'node:path';
10
10
  import { createHash } from 'node:crypto';
11
- import { assetIdError, collectAllReferences, createEmptyLibrary, folderNameError, folderNameOf, folderOfRawPath, folderPathError, isFolderOrDescendant, joinFolderPath, manifestColorError, manifestEmojiError, parseLibrary, referenceErrors, normalizeFolderPath, parentFolderPath, portableFolderPathKey, rawFilePathError, rawFileNameOfPath, replaceFolderPrefix, manifestNameError, serializeLibrary } from '../core/index.js';
11
+ import { assetIdError, collectAllReferences, canonicalizeParameters, createEmptyLibrary, folderNameError, folderNameOf, folderOfRawPath, folderPathError, isFolderOrDescendant, joinFolderPath, manifestColorError, manifestEmojiError, parseLibrary, referenceErrors, normalizeFolderPath, parentFolderPath, portableFolderPathKey, rawFilePathError, rawFileNameOfPath, replaceFolderPrefix, manifestNameError, serializeLibrary } from '../core/index.js';
12
12
  import { manifestModuleFileName } from './codegen.js';
13
13
  import { descriptorsByType, serverPluginForType, serverPluginsByType } from './plugins.js';
14
14
  import { isProxyFileNameFor } from './processing/processor.js';
@@ -114,9 +114,12 @@ export class LibraryStore {
114
114
  * the owning plugin's default parameters. Returns the updated library.
115
115
  */
116
116
  async ingest(options) {
117
- return this.#enqueue(() => this.#ingest(options));
117
+ return this.createAsset(options);
118
118
  }
119
- async #ingest(options) {
119
+ async createAsset(options, prepareParameters) {
120
+ return this.#enqueue(() => this.#createAsset(options, prepareParameters));
121
+ }
122
+ async #createAsset(options, prepareParameters) {
120
123
  const idProblem = assetIdError(options.id);
121
124
  if (idProblem)
122
125
  throw new Error(idProblem);
@@ -131,21 +134,57 @@ export class LibraryStore {
131
134
  }
132
135
  const folder = options.folder === undefined ? '' : normalizeFolderPath(options.folder);
133
136
  this.#requireFolder(library, folder);
137
+ const manifests = [...new Set(options.manifests ?? [])].sort();
138
+ for (const manifest of manifests) {
139
+ if (!Object.hasOwn(library.manifests, manifest)) {
140
+ throw new Error(`Unknown manifest "${manifest}".`);
141
+ }
142
+ }
134
143
  const rawDirectory = join(this.#config.rawDir, folder);
135
144
  await mkdir(rawDirectory, { recursive: true });
136
145
  const storedName = this.#uniqueRawName(sanitizeFileName(options.fileName), folder);
137
146
  const storedPath = folder === '' ? storedName : `${folder}/${storedName}`;
138
- await writeFile(join(this.#config.rawDir, storedPath), options.bytes);
139
- const record = {
140
- type: options.type,
141
- raw: storedPath,
142
- rawHash: sha256Hex(options.bytes),
143
- parameters: plugin.descriptor.defaultParameters(plugin.pluginConfig(this.#config)),
144
- manifests: []
145
- };
146
- library.assets[options.id] = record;
147
- await this.write(library);
148
- return library;
147
+ const finalPath = join(this.#config.rawDir, storedPath);
148
+ const temporaryPath = join(rawDirectory, `.fundus-create-${crypto.randomUUID()}.tmp`);
149
+ let installed = false;
150
+ try {
151
+ await writeFile(temporaryPath, options.bytes);
152
+ let parameters = plugin.descriptor.validateParameters(plugin.descriptor.defaultParameters(plugin.pluginConfig(this.#config)));
153
+ if (prepareParameters) {
154
+ parameters = plugin.descriptor.validateParameters(await prepareParameters({ rawPath: temporaryPath, rawFileName: storedName, parameters }));
155
+ }
156
+ const record = {
157
+ type: options.type,
158
+ raw: storedPath,
159
+ rawHash: sha256Hex(options.bytes),
160
+ parameters,
161
+ manifests
162
+ };
163
+ if (options.importSource) {
164
+ record.importSource = structuredClone(options.importSource);
165
+ library.version = 2;
166
+ }
167
+ library.assets[options.id] = record;
168
+ // Validate the complete candidate before installing the final raw path.
169
+ parseLibrary(serializeLibrary(library));
170
+ await rename(temporaryPath, finalPath);
171
+ installed = true;
172
+ await this.write(library);
173
+ return library;
174
+ }
175
+ catch (cause) {
176
+ const cleanupErrors = [];
177
+ if (installed)
178
+ await unlink(finalPath).catch((error) => cleanupErrors.push(error));
179
+ await unlink(temporaryPath).catch((error) => {
180
+ if (error.code !== 'ENOENT')
181
+ cleanupErrors.push(error);
182
+ });
183
+ if (cleanupErrors.length > 0) {
184
+ throw new AggregateError([cause, ...cleanupErrors], 'Asset creation failed and cleanup was incomplete.');
185
+ }
186
+ throw cause;
187
+ }
149
188
  }
150
189
  /**
151
190
  * Replace one asset's raw file while preserving its identity and authored
@@ -166,6 +205,7 @@ export class LibraryStore {
166
205
  }
167
206
  const previousRaw = record.raw;
168
207
  const previousHash = record.rawHash;
208
+ const previousSource = record.importSource;
169
209
  const folder = folderOfRawPath(previousRaw);
170
210
  const previousName = rawFileNameOfPath(previousRaw);
171
211
  const storedName = sanitizedName === previousName ? previousName : this.#uniqueRawName(sanitizedName, folder);
@@ -188,11 +228,14 @@ export class LibraryStore {
188
228
  replacementInstalled = true;
189
229
  record.raw = nextRaw;
190
230
  record.rawHash = sha256Hex(options.bytes);
231
+ delete record.importSource;
191
232
  await this.write(library);
192
233
  }
193
234
  catch (cause) {
194
235
  record.raw = previousRaw;
195
236
  record.rawHash = previousHash;
237
+ if (previousSource)
238
+ record.importSource = previousSource;
196
239
  if (replacementInstalled)
197
240
  await unlink(nextPath).catch(() => { });
198
241
  if (previousStaged)
@@ -204,6 +247,59 @@ export class LibraryStore {
204
247
  await unlink(backupPath).catch(() => { });
205
248
  return library;
206
249
  }
250
+ /** CAS-protected replacement used only by a repeatable import source. */
251
+ async replaceRawFromImportSource(id, bytes, expected, nextSource) {
252
+ return this.#enqueue(async () => {
253
+ const library = await this.read();
254
+ const record = this.#requireAsset(library, id);
255
+ if (record.raw !== expected.raw ||
256
+ record.rawHash !== expected.rawHash ||
257
+ record.type !== expected.type ||
258
+ !record.importSource ||
259
+ canonicalizeParameters(record.importSource) !== expected.canonicalSource) {
260
+ throw new Error('stale-import');
261
+ }
262
+ const absolutePath = join(this.#config.rawDir, record.raw);
263
+ const { verifyStableRawFile } = await import('./raw-reconciliation.js');
264
+ try {
265
+ await verifyStableRawFile(absolutePath, expected.rawHash);
266
+ }
267
+ catch {
268
+ throw new Error('stale-import');
269
+ }
270
+ const token = crypto.randomUUID();
271
+ const temporaryPath = `${absolutePath}.fundus-reimport-${token}.tmp`;
272
+ const backupPath = `${absolutePath}.fundus-reimport-${token}`;
273
+ const previousHash = record.rawHash;
274
+ const previousSource = structuredClone(record.importSource);
275
+ let staged = false;
276
+ let installed = false;
277
+ try {
278
+ await writeFile(temporaryPath, bytes);
279
+ await rename(absolutePath, backupPath);
280
+ staged = true;
281
+ await rename(temporaryPath, absolutePath);
282
+ installed = true;
283
+ record.rawHash = sha256Hex(bytes);
284
+ if (nextSource)
285
+ record.importSource = structuredClone(nextSource);
286
+ await this.write(library);
287
+ }
288
+ catch (cause) {
289
+ record.rawHash = previousHash;
290
+ record.importSource = previousSource;
291
+ if (installed)
292
+ await unlink(absolutePath).catch(() => { });
293
+ if (staged)
294
+ await rename(backupPath, absolutePath);
295
+ await unlink(temporaryPath).catch(() => { });
296
+ throw cause;
297
+ }
298
+ if (staged)
299
+ await unlink(backupPath).catch(() => { });
300
+ return library;
301
+ });
302
+ }
207
303
  /** Replace an asset's parameters (validated by the owning plugin). */
208
304
  async updateParameters(id, parameters) {
209
305
  return this.#enqueue(() => this.#updateParameters(id, parameters));
@@ -2,6 +2,7 @@ import { type FundusStateDto, type PatchAssetRequestDto, type PatchManifestReque
2
2
  import type { ResolvedFundusConfig } from './config-loader.js';
3
3
  import { LibraryStore, type IngestOptions, type ReplaceRawFileOptions } from './library-store.js';
4
4
  import type { ServerPluginRegistry } from './plugins/contract.js';
5
+ import { SourceImporterRegistry } from './importers/registry.js';
5
6
  /**
6
7
  * The CLI's `--parameters` merge semantics (M7): a SHALLOW merge at the top
7
8
  * level onto the current parameters — a nested object value replaces the whole
@@ -17,9 +18,16 @@ export interface AssetChangeOptions {
17
18
  /** Replacement for the explicit manifest membership set (declarative). */
18
19
  manifests?: string[];
19
20
  }
21
+ export interface ImportAssetOptions extends AssetChangeOptions {
22
+ importer: string;
23
+ input: unknown;
24
+ type: string;
25
+ id: string;
26
+ folder?: string;
27
+ }
20
28
  export declare class FundusOperations {
21
29
  #private;
22
- constructor(config: ResolvedFundusConfig, registry?: ServerPluginRegistry);
30
+ constructor(config: ResolvedFundusConfig, registry?: ServerPluginRegistry, importerRegistry?: SourceImporterRegistry);
23
31
  get config(): ResolvedFundusConfig;
24
32
  get store(): LibraryStore;
25
33
  /** Read-only state snapshot, serialized with mutations but never processing or writing. */
@@ -42,6 +50,10 @@ export declare class FundusOperations {
42
50
  * rolls the whole ingest back, so a failed ingest leaves nothing behind.
43
51
  */
44
52
  ingest(options: IngestOptions & AssetChangeOptions): Promise<FundusStateDto>;
53
+ /** Materialize a source outside the operation queue, then atomically create the asset. */
54
+ importAsset(options: ImportAssetOptions): Promise<FundusStateDto>;
55
+ /** Re-materialize a persisted source with record and on-disk CAS protection. */
56
+ reimportAsset(id: string, input?: unknown): Promise<FundusStateDto>;
45
57
  /** Apply a PATCH body: parameters and/or manifests and/or a rename — in that order. */
46
58
  patchAsset(id: string, body: PatchAssetRequestDto): Promise<FundusStateDto>;
47
59
  moveAsset(id: string, folder: string): Promise<FundusStateDto>;
@@ -6,7 +6,7 @@
6
6
  * operation has exactly one implementation and there is no other save path.
7
7
  */
8
8
  import { join } from 'node:path';
9
- import { assetIdError, allDeliveredAssetIds, collectAllReferences, compareCodeUnits, computeLibraryMembership, folderOfRawPath, parentFolderPath, portableFolderPathKey, proposeAssetId, rawFileNameOfPath, typesForFileName } from '../core/index.js';
9
+ import { assetIdError, allDeliveredAssetIds, canonicalizeParameters, collectAllReferences, compareCodeUnits, computeLibraryMembership, folderOfRawPath, normalizeFolderPath, parentFolderPath, portableFolderPathKey, proposeAssetId, rawFileNameOfPath, typesForFileName } from '../core/index.js';
10
10
  import { LibraryStore } from './library-store.js';
11
11
  import { generateManifestModules } from './codegen.js';
12
12
  import { descriptorsByType, serverPluginForType, serverPluginsByType } from './plugins.js';
@@ -14,6 +14,8 @@ import { processAssets, readCurrentProxy } from './processing/processor.js';
14
14
  import { encodeRelativePathForUrl } from './folder-filesystem.js';
15
15
  import { classifyRawDirectory, verifyStableRawFile } from './raw-reconciliation.js';
16
16
  import { rawSafetyIssues } from './raw-safety.js';
17
+ import { serverImporters } from './importers.js';
18
+ import { SourceImportError } from './importers/contract.js';
17
19
  /**
18
20
  * The CLI's `--parameters` merge semantics (M7): a SHALLOW merge at the top
19
21
  * level onto the current parameters — a nested object value replaces the whole
@@ -44,12 +46,14 @@ export class FundusOperations {
44
46
  #config;
45
47
  #registry;
46
48
  #store;
49
+ #importers;
47
50
  #operationQueue = Promise.resolve();
48
51
  #pipelineQueue = Promise.resolve();
49
- constructor(config, registry = serverPluginsByType) {
52
+ constructor(config, registry = serverPluginsByType, importerRegistry = serverImporters) {
50
53
  this.#config = config;
51
54
  this.#registry = registry;
52
55
  this.#store = new LibraryStore(config, registry);
56
+ this.#importers = importerRegistry;
53
57
  }
54
58
  get config() {
55
59
  return this.#config;
@@ -198,6 +202,7 @@ export class FundusOperations {
198
202
  record.type = type;
199
203
  record.rawHash = edited.hash;
200
204
  record.parameters = parameters;
205
+ delete record.importSource;
201
206
  return edited.assetId;
202
207
  });
203
208
  await this.#store.deleteProxiesOf(result.value);
@@ -217,6 +222,7 @@ export class FundusOperations {
217
222
  throw new Error(`The moved file "${edited.path}" is not compatible with its current ${record.type} type; choose a compatible type and process it.`);
218
223
  }
219
224
  candidate.assets[edited.assetId].rawHash = edited.hash;
225
+ delete candidate.assets[edited.assetId].importSource;
220
226
  return edited.assetId;
221
227
  });
222
228
  await this.#store.deleteProxiesOf(result.value);
@@ -233,36 +239,161 @@ export class FundusOperations {
233
239
  * rolls the whole ingest back, so a failed ingest leaves nothing behind.
234
240
  */
235
241
  ingest(options) {
236
- return this.#enqueueOperation(() => this.#ingest(options));
242
+ return this.#enqueueOperation(async () => {
243
+ const importer = this.#importers.require('file');
244
+ const input = importer.validateInput({ fileName: options.fileName, bytes: options.bytes });
245
+ const raw = await importer.importRaw(input, {
246
+ config: this.#config,
247
+ fetch: globalThis.fetch
248
+ });
249
+ return this.#createImportedAsset(raw, options);
250
+ });
237
251
  }
238
- async #ingest(options) {
239
- let library = await this.#store.ingest(options);
240
- try {
241
- const plugin = serverPluginForType(options.type, this.#registry);
252
+ /** Materialize a source outside the operation queue, then atomically create the asset. */
253
+ async importAsset(options) {
254
+ const preflight = await this.#enqueueOperation(async () => {
255
+ const library = await this.#store.read();
256
+ this.#validateImportTarget(options, library);
257
+ if (Object.hasOwn(library.assets, options.id)) {
258
+ throw new SourceImportError('invalid-input', `An asset with id "${options.id}" already exists.`);
259
+ }
260
+ const importer = this.#importers.require(options.importer);
261
+ return { importer, input: importer.validateInput(options.input) };
262
+ });
263
+ const raw = await preflight.importer.importRaw(preflight.input, {
264
+ config: this.#config,
265
+ fetch: globalThis.fetch
266
+ });
267
+ return this.#enqueueOperation(async () => {
268
+ this.#validateImportedRaw(raw, options.importer);
269
+ return this.#createImportedAsset(raw, options);
270
+ });
271
+ }
272
+ /** Re-materialize a persisted source with record and on-disk CAS protection. */
273
+ async reimportAsset(id, input) {
274
+ const snapshot = await this.#enqueueOperation(async () => {
275
+ const library = await this.#store.read();
276
+ this.#requireAsset(library, id);
277
+ const record = library.assets[id];
278
+ if (!record.importSource) {
279
+ throw new SourceImportError('invalid-input', `Asset "${id}" has no repeatable import source.`);
280
+ }
281
+ const resolved = this.#importers.requireReimport(record.importSource);
282
+ try {
283
+ await verifyStableRawFile(join(this.#config.rawDir, record.raw), record.rawHash);
284
+ }
285
+ catch {
286
+ throw new SourceImportError('stale-import', 'The local raw file changed. Apply or discard that edit before re-importing.');
287
+ }
288
+ return {
289
+ importer: resolved.importer,
290
+ parameters: resolved.parameters,
291
+ input: input === undefined ? undefined : resolved.importer.validateInput(input),
292
+ expected: {
293
+ raw: record.raw,
294
+ rawHash: record.rawHash,
295
+ type: record.type,
296
+ canonicalSource: canonicalizeParameters(record.importSource)
297
+ }
298
+ };
299
+ });
300
+ const context = { config: this.#config, fetch: globalThis.fetch };
301
+ const raw = snapshot.input === undefined
302
+ ? await snapshot.importer.reimportRaw(snapshot.parameters, context)
303
+ : await snapshot.importer.importRaw(snapshot.input, context);
304
+ if (snapshot.input !== undefined && !raw.importSource) {
305
+ throw new SourceImportError('invalid-response', 'The source importer did not return provenance for the updated source.');
306
+ }
307
+ return this.#enqueueOperation(async () => {
308
+ this.#validateImportedRaw(raw, snapshot.importer.kind);
309
+ const plugin = serverPluginForType(snapshot.expected.type, this.#registry);
310
+ if (!plugin.descriptor.accepts.includes(raw.extension)) {
311
+ throw new SourceImportError('invalid-response', `The ${snapshot.expected.type} type does not accept ".${raw.extension}" files.`);
312
+ }
313
+ let library;
314
+ try {
315
+ library = await this.#store.replaceRawFromImportSource(id, raw.bytes, snapshot.expected, raw.importSource);
316
+ }
317
+ catch (cause) {
318
+ if (cause instanceof Error && cause.message === 'stale-import') {
319
+ throw new SourceImportError('stale-import', 'The asset or local raw file changed while Figma was exporting. Nothing was replaced.');
320
+ }
321
+ throw cause;
322
+ }
323
+ return this.#afterMutation(library);
324
+ });
325
+ }
326
+ async #createImportedAsset(raw, options) {
327
+ this.#validateImportedRaw(raw);
328
+ const fileName = raw.suggestedFileName ?? `${options.id}.${raw.extension}`;
329
+ const plugin = serverPluginForType(options.type, this.#registry);
330
+ const library = await this.#store.createAsset({
331
+ fileName,
332
+ bytes: raw.bytes,
333
+ type: options.type,
334
+ id: options.id,
335
+ folder: options.folder,
336
+ manifests: options.manifests,
337
+ importSource: raw.importSource
338
+ }, async ({ rawPath, rawFileName, parameters }) => {
339
+ let next = parameters;
242
340
  if (plugin.seedIngestParameters) {
243
- const record = library.assets[options.id];
244
341
  const seeded = await plugin.seedIngestParameters({
245
- rawPath: join(this.#config.rawDir, record.raw),
246
- rawFileName: rawFileNameOfPath(record.raw),
247
- parameters: plugin.descriptor.validateParameters(record.parameters),
342
+ rawPath,
343
+ rawFileName,
344
+ parameters: next,
248
345
  config: this.#config
249
346
  });
250
- if (seeded !== undefined) {
251
- library = await this.#store.updateParameters(options.id, seeded);
252
- }
347
+ if (seeded !== undefined)
348
+ next = seeded;
253
349
  }
254
- if (options.parametersPatch !== undefined || options.manifests !== undefined) {
255
- library = await this.#applyAssetChanges(library, options.id, {
256
- parametersPatch: options.parametersPatch,
257
- manifests: options.manifests
258
- });
350
+ if (options.parametersPatch !== undefined) {
351
+ next = mergeParameterPatch(next, options.parametersPatch);
352
+ }
353
+ return plugin.descriptor.validateParameters(next);
354
+ });
355
+ return this.#afterMutation(library);
356
+ }
357
+ #validateImportTarget(options, library) {
358
+ if (typeof options.importer !== 'string' ||
359
+ typeof options.id !== 'string' ||
360
+ typeof options.type !== 'string') {
361
+ throw new SourceImportError('invalid-input', 'Import requires importer, type, and id strings.');
362
+ }
363
+ const problem = assetIdError(options.id);
364
+ if (problem)
365
+ throw new SourceImportError('invalid-input', problem);
366
+ serverPluginForType(options.type, this.#registry);
367
+ if (options.folder !== undefined && typeof options.folder !== 'string') {
368
+ throw new SourceImportError('invalid-input', 'Import folder must be a string.');
369
+ }
370
+ const folder = normalizeFolderPath(options.folder ?? '');
371
+ if (folder !== '' && !library.folders.includes(folder)) {
372
+ throw new SourceImportError('invalid-input', `Unknown folder "${folder}".`);
373
+ }
374
+ if (options.manifests !== undefined &&
375
+ (!Array.isArray(options.manifests) ||
376
+ options.manifests.some((value) => typeof value !== 'string'))) {
377
+ throw new SourceImportError('invalid-input', 'Import manifests must be an array of strings.');
378
+ }
379
+ for (const manifest of new Set(options.manifests ?? [])) {
380
+ if (!Object.hasOwn(library.manifests, manifest)) {
381
+ throw new SourceImportError('invalid-input', `Unknown manifest "${manifest}".`);
259
382
  }
260
383
  }
261
- catch (cause) {
262
- await this.#store.deleteAsset(options.id);
263
- throw cause;
384
+ }
385
+ #validateImportedRaw(raw, importerKind) {
386
+ if (!(raw.bytes instanceof Uint8Array) || !/^[a-z0-9]+$/.test(raw.extension)) {
387
+ throw new SourceImportError('invalid-response', 'The source importer returned invalid raw data.');
388
+ }
389
+ if (raw.importSource) {
390
+ if (importerKind && raw.importSource.importer !== importerKind) {
391
+ throw new SourceImportError('invalid-response', 'The source importer returned mismatched provenance.');
392
+ }
393
+ this.#importers
394
+ .require(raw.importSource.importer)
395
+ .validateSourceParameters(raw.importSource.parameters);
264
396
  }
265
- return this.#afterMutation(library);
266
397
  }
267
398
  /** Apply a PATCH body: parameters and/or manifests and/or a rename — in that order. */
268
399
  patchAsset(id, body) {
@@ -546,6 +677,9 @@ export class FundusOperations {
546
677
  warnings.push('In no manifest and not referenced by any delivered asset — ' +
547
678
  'nothing is processed or generated for it.');
548
679
  }
680
+ if (record.importSource && !this.#importers.has(record.importSource.importer)) {
681
+ warnings.push(`Importer "${record.importSource.importer}" is unavailable; the local raw file remains usable.`);
682
+ }
549
683
  let proxy = null;
550
684
  if (delivered.has(assetId)) {
551
685
  try {
@@ -572,6 +706,7 @@ export class FundusOperations {
572
706
  folder: folderOfRawPath(record.raw),
573
707
  rawUrl: `/files/raw/${encodeRelativePathForUrl(record.raw)}?v=${record.rawHash.slice(0, 12)}`,
574
708
  rawHash: record.rawHash,
709
+ importSource: record.importSource,
575
710
  parameters: record.parameters,
576
711
  manifests: record.manifests,
577
712
  passiveManifests: passiveManifestsByAsset.get(assetId) ?? [],
@@ -614,6 +749,7 @@ export class FundusOperations {
614
749
  return {
615
750
  library,
616
751
  assets,
752
+ importDefaults: { figma: { scale: this.#config.import.figma.defaults.scale } },
617
753
  issues,
618
754
  presetNames,
619
755
  manifestMemberships,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fundus",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- .context-menu.svelte-109nkvw{z-index:200;background:var(--panel-raised);border:1px solid var(--border);border-radius:6px;width:168px;padding:4px;position:fixed;box-shadow:0 8px 24px #00000073}button.svelte-109nkvw{width:100%;color:var(--text);font:inherit;text-align:left;background:0 0;border:0;border-radius:4px;padding:6px 8px}button.svelte-109nkvw:hover,button.svelte-109nkvw:focus-visible{background:var(--accent-soft);outline:none}button.danger.svelte-109nkvw{color:var(--danger)}hr.svelte-109nkvw{background:var(--border);border:0;height:1px;margin:4px 8px}.manifest-label.svelte-1b4m5oj{align-items:center;gap:4px;min-width:0;display:inline-flex}.emoji.svelte-1b4m5oj{flex:none;line-height:1}.name.svelte-1b4m5oj{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.chip.svelte-1b4m5oj{background:color-mix(in srgb, var(--manifest-color) 20%, transparent);color:var(--manifest-color);border-radius:4px;padding:0 5px;font-size:10px}.chip.passive.svelte-1b4m5oj{border:1px dashed var(--manifest-color);background:0 0}.library-view.svelte-1uha8ag{flex:1;min-height:0;display:flex}.page.svelte-1uha8ag{flex:1;min-height:0;padding:16px;position:relative;overflow-y:auto}.selection-bar.svelte-1uha8ag,.filter-bar.svelte-1uha8ag{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.selection-bar.svelte-1uha8ag{margin-bottom:8px}.filter-bar.svelte-1uha8ag{margin-bottom:16px}.selection-summary.svelte-1uha8ag{color:var(--accent);white-space:nowrap;font-weight:600}.toggle.svelte-1uha8ag{color:var(--text-dim);align-items:center;gap:6px;display:inline-flex}.search.svelte-1uha8ag{align-items:center;gap:5px;width:220px;padding:0 6px;display:flex}.search.svelte-1uha8ag:focus-within{border-color:var(--accent)}.search-icon.svelte-1uha8ag{fill:none;width:14px;height:14px;stroke:var(--text-faint);stroke-width:1.5px;stroke-linecap:round;flex:none}.search-input.svelte-1uha8ag{min-width:0;height:100%;color:var(--text);font:inherit;background:0 0;border:0;outline:none;flex:1;padding:0}.search.svelte-1uha8ag kbd:where(.svelte-1uha8ag){border:1px solid var(--border);color:var(--text-faint);border-radius:3px;flex:none;padding:1px 4px;font:9px/14px system-ui,sans-serif}.import.svelte-1uha8ag{background:var(--accent);color:var(--text);margin-left:auto}.section.svelte-1uha8ag{margin-bottom:24px}.section.svelte-1uha8ag:last-child{margin-bottom:0}.section-emoji.svelte-1uha8ag{margin-right:6px}.grid.svelte-1uha8ag,.folder-grid.svelte-1uha8ag{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;display:grid}.folder-card.svelte-1uha8ag{background:var(--panel);border:1px solid var(--border-soft);min-width:0;height:60px;color:var(--text);text-align:left;border-radius:8px;align-items:center;gap:10px;padding:8px 10px;display:flex}.folder-card.svelte-1uha8ag:hover,.folder-card.svelte-1uha8ag:focus-visible{border-color:var(--accent);outline:none}.folder-card.drop-target.svelte-1uha8ag{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 1px var(--accent)}.folder-card.dragging-folder.svelte-1uha8ag{opacity:.55}.folder-card-icon.svelte-1uha8ag{color:var(--text-dim);flex:none;align-items:center;display:flex}.folder-card-details.svelte-1uha8ag{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.folder-card-details.svelte-1uha8ag strong:where(.svelte-1uha8ag){text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.folder-card-details.svelte-1uha8ag span:where(.svelte-1uha8ag),.folder-card-arrow.svelte-1uha8ag{color:var(--text-faint)}.folder-card-arrow.svelte-1uha8ag{flex:none;font-size:18px}.card.svelte-1uha8ag{background:var(--panel);border:1px solid var(--border-soft);border-radius:8px;flex-direction:column;gap:8px;padding:8px;display:flex;position:relative}.card.selected.svelte-1uha8ag{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.asset-select.svelte-1uha8ag{z-index:1;width:16px;height:16px;accent-color:var(--accent);position:absolute;top:12px;left:12px}.card.svelte-1uha8ag:hover{border-color:var(--accent)}.thumbnail.svelte-1uha8ag{border-radius:var(--radius);justify-content:center;align-items:center;height:96px;display:flex;overflow:hidden}.meta.svelte-1uha8ag{align-items:center;gap:6px;min-width:0;display:flex}.id.svelte-1uha8ag{text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.type-badge.svelte-1uha8ag{color:var(--text-dim);border:1px solid var(--border);border-radius:4px;flex:none;margin-left:auto;padding:0 5px;font-size:10px}.badges.svelte-1uha8ag{flex-wrap:wrap;gap:4px;min-height:16px;display:flex}.badge.svelte-1uha8ag{border-radius:4px;padding:0 5px;font-size:10px}.badge.warning.svelte-1uha8ag{background:var(--warning-soft);color:var(--warning)}.empty.svelte-1uha8ag{color:var(--text-dim);text-align:center;padding:48px}.dialog-row.svelte-1uha8ag{align-items:center;gap:8px;display:flex}.dialog-row.svelte-1uha8ag span:where(.svelte-1uha8ag){width:44px;color:var(--text-dim)}.dialog-row.svelte-1uha8ag input:where(.svelte-1uha8ag){flex:1}.dialog-text.svelte-1uha8ag,.dialog-warning.svelte-1uha8ag{margin:8px 0 0}.dialog-warning.svelte-1uha8ag{color:var(--warning)}.drop-hint.svelte-1uha8ag{border:2px dashed var(--accent);color:var(--accent);pointer-events:none;background:#0d99ff14;border-radius:12px;justify-content:center;align-items:center;font-size:14px;display:flex;position:fixed;inset:48px 8px 8px}