@ahmedrowaihi/pdf-forge-preview 1.0.0-canary.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 (351) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-path-routes-manifest.json +7 -0
  3. package/.next/build/chunks/[root-of-the-server]__12fb5caf._.js +233 -0
  4. package/.next/build/chunks/[root-of-the-server]__12fb5caf._.js.map +8 -0
  5. package/.next/build/chunks/[root-of-the-server]__242deb00._.js +500 -0
  6. package/.next/build/chunks/[root-of-the-server]__242deb00._.js.map +11 -0
  7. package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_ad9a1eec._.js +13 -0
  8. package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_ad9a1eec._.js.map +5 -0
  9. package/.next/build/chunks/[turbopack]_runtime.js +795 -0
  10. package/.next/build/chunks/[turbopack]_runtime.js.map +10 -0
  11. package/.next/build/chunks/node_modules__pnpm_806d01c0._.js +6758 -0
  12. package/.next/build/chunks/node_modules__pnpm_806d01c0._.js.map +47 -0
  13. package/.next/build/package.json +1 -0
  14. package/.next/build/postcss.js +6 -0
  15. package/.next/build/postcss.js.map +5 -0
  16. package/.next/build-manifest.json +20 -0
  17. package/.next/diagnostics/build-diagnostics.json +6 -0
  18. package/.next/diagnostics/framework.json +1 -0
  19. package/.next/export-marker.json +6 -0
  20. package/.next/fallback-build-manifest.json +12 -0
  21. package/.next/images-manifest.json +66 -0
  22. package/.next/next-minimal-server.js.nft.json +1 -0
  23. package/.next/next-server.js.nft.json +1 -0
  24. package/.next/package.json +1 -0
  25. package/.next/prerender-manifest.json +65 -0
  26. package/.next/required-server-files.js +163 -0
  27. package/.next/required-server-files.json +163 -0
  28. package/.next/routes-manifest.json +77 -0
  29. package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
  30. package/.next/server/app/_global-error/page/build-manifest.json +17 -0
  31. package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
  32. package/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
  33. package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
  34. package/.next/server/app/_global-error/page.js +11 -0
  35. package/.next/server/app/_global-error/page.js.map +5 -0
  36. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  37. package/.next/server/app/_global-error/page_client-reference-manifest.js +2 -0
  38. package/.next/server/app/_global-error.html +2 -0
  39. package/.next/server/app/_global-error.meta +15 -0
  40. package/.next/server/app/_global-error.rsc +13 -0
  41. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
  42. package/.next/server/app/_global-error.segments/_full.segment.rsc +13 -0
  43. package/.next/server/app/_global-error.segments/_head.segment.rsc +6 -0
  44. package/.next/server/app/_global-error.segments/_index.segment.rsc +4 -0
  45. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  46. package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
  47. package/.next/server/app/_not-found/page/build-manifest.json +17 -0
  48. package/.next/server/app/_not-found/page/next-font-manifest.json +16 -0
  49. package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
  50. package/.next/server/app/_not-found/page/server-reference-manifest.json +20 -0
  51. package/.next/server/app/_not-found/page.js +14 -0
  52. package/.next/server/app/_not-found/page.js.map +5 -0
  53. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  54. package/.next/server/app/_not-found/page_client-reference-manifest.js +2 -0
  55. package/.next/server/app/favicon.ico/route/app-paths-manifest.json +3 -0
  56. package/.next/server/app/favicon.ico/route/build-manifest.json +11 -0
  57. package/.next/server/app/favicon.ico/route.js +6 -0
  58. package/.next/server/app/favicon.ico/route.js.map +5 -0
  59. package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
  60. package/.next/server/app/favicon.ico.body +0 -0
  61. package/.next/server/app/favicon.ico.meta +1 -0
  62. package/.next/server/app/page/app-paths-manifest.json +3 -0
  63. package/.next/server/app/page/build-manifest.json +17 -0
  64. package/.next/server/app/page/next-font-manifest.json +16 -0
  65. package/.next/server/app/page/react-loadable-manifest.json +1 -0
  66. package/.next/server/app/page/server-reference-manifest.json +20 -0
  67. package/.next/server/app/page.js +17 -0
  68. package/.next/server/app/page.js.map +5 -0
  69. package/.next/server/app/page.js.nft.json +1 -0
  70. package/.next/server/app/page_client-reference-manifest.js +2 -0
  71. package/.next/server/app/preview/[...slug]/page/app-paths-manifest.json +3 -0
  72. package/.next/server/app/preview/[...slug]/page/build-manifest.json +17 -0
  73. package/.next/server/app/preview/[...slug]/page/next-font-manifest.json +16 -0
  74. package/.next/server/app/preview/[...slug]/page/react-loadable-manifest.json +1 -0
  75. package/.next/server/app/preview/[...slug]/page/server-reference-manifest.json +65 -0
  76. package/.next/server/app/preview/[...slug]/page.js +19 -0
  77. package/.next/server/app/preview/[...slug]/page.js.map +5 -0
  78. package/.next/server/app/preview/[...slug]/page.js.nft.json +1 -0
  79. package/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +2 -0
  80. package/.next/server/app-paths-manifest.json +7 -0
  81. package/.next/server/chunks/730ea_preview-server__next-internal_server_app_favicon_ico_route_actions_a71a8ae7.js +3 -0
  82. package/.next/server/chunks/730ea_preview-server__next-internal_server_app_favicon_ico_route_actions_a71a8ae7.js.map +1 -0
  83. package/.next/server/chunks/[externals]_next_dist_a6d89067._.js +3 -0
  84. package/.next/server/chunks/[externals]_next_dist_a6d89067._.js.map +1 -0
  85. package/.next/server/chunks/[root-of-the-server]__a62cd78d._.js +21 -0
  86. package/.next/server/chunks/[root-of-the-server]__a62cd78d._.js.map +1 -0
  87. package/.next/server/chunks/[turbopack]_runtime.js +795 -0
  88. package/.next/server/chunks/[turbopack]_runtime.js.map +10 -0
  89. package/.next/server/chunks/ssr/730ea_preview-server__next-internal_server_app__global-error_page_actions_986e2de5.js +3 -0
  90. package/.next/server/chunks/ssr/730ea_preview-server__next-internal_server_app__global-error_page_actions_986e2de5.js.map +1 -0
  91. package/.next/server/chunks/ssr/[root-of-the-server]__025eaae9._.js +3 -0
  92. package/.next/server/chunks/ssr/[root-of-the-server]__025eaae9._.js.map +1 -0
  93. package/.next/server/chunks/ssr/[root-of-the-server]__1536282c._.js +4 -0
  94. package/.next/server/chunks/ssr/[root-of-the-server]__1536282c._.js.map +1 -0
  95. package/.next/server/chunks/ssr/[root-of-the-server]__15cf9d36._.js +3 -0
  96. package/.next/server/chunks/ssr/[root-of-the-server]__15cf9d36._.js.map +1 -0
  97. package/.next/server/chunks/ssr/[root-of-the-server]__450f653e._.js +3 -0
  98. package/.next/server/chunks/ssr/[root-of-the-server]__450f653e._.js.map +1 -0
  99. package/.next/server/chunks/ssr/[root-of-the-server]__4874d0d7._.js +3 -0
  100. package/.next/server/chunks/ssr/[root-of-the-server]__4874d0d7._.js.map +1 -0
  101. package/.next/server/chunks/ssr/[root-of-the-server]__49771cdc._.js +3 -0
  102. package/.next/server/chunks/ssr/[root-of-the-server]__49771cdc._.js.map +1 -0
  103. package/.next/server/chunks/ssr/[root-of-the-server]__9ff74047._.js +3 -0
  104. package/.next/server/chunks/ssr/[root-of-the-server]__9ff74047._.js.map +1 -0
  105. package/.next/server/chunks/ssr/[root-of-the-server]__a9be37b1._.js +3 -0
  106. package/.next/server/chunks/ssr/[root-of-the-server]__a9be37b1._.js.map +1 -0
  107. package/.next/server/chunks/ssr/[root-of-the-server]__aeefe74e._.js +3 -0
  108. package/.next/server/chunks/ssr/[root-of-the-server]__aeefe74e._.js.map +1 -0
  109. package/.next/server/chunks/ssr/[root-of-the-server]__b13738d2._.js +3 -0
  110. package/.next/server/chunks/ssr/[root-of-the-server]__b13738d2._.js.map +1 -0
  111. package/.next/server/chunks/ssr/[root-of-the-server]__bb428c83._.js +4 -0
  112. package/.next/server/chunks/ssr/[root-of-the-server]__bb428c83._.js.map +1 -0
  113. package/.next/server/chunks/ssr/[root-of-the-server]__c61333e3._.js +10 -0
  114. package/.next/server/chunks/ssr/[root-of-the-server]__c61333e3._.js.map +1 -0
  115. package/.next/server/chunks/ssr/[root-of-the-server]__cb0c7b1a._.js +4 -0
  116. package/.next/server/chunks/ssr/[root-of-the-server]__cb0c7b1a._.js.map +1 -0
  117. package/.next/server/chunks/ssr/[root-of-the-server]__dafcae4c._.js +3 -0
  118. package/.next/server/chunks/ssr/[root-of-the-server]__dafcae4c._.js.map +1 -0
  119. package/.next/server/chunks/ssr/[root-of-the-server]__e47d0c07._.js +3 -0
  120. package/.next/server/chunks/ssr/[root-of-the-server]__e47d0c07._.js.map +1 -0
  121. package/.next/server/chunks/ssr/[root-of-the-server]__ecac8617._.js +31 -0
  122. package/.next/server/chunks/ssr/[root-of-the-server]__ecac8617._.js.map +1 -0
  123. package/.next/server/chunks/ssr/[root-of-the-server]__fd4bda25._.js +3 -0
  124. package/.next/server/chunks/ssr/[root-of-the-server]__fd4bda25._.js.map +1 -0
  125. package/.next/server/chunks/ssr/[turbopack]_runtime.js +795 -0
  126. package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +10 -0
  127. package/.next/server/chunks/ssr/_19494208._.js +19 -0
  128. package/.next/server/chunks/ssr/_19494208._.js.map +1 -0
  129. package/.next/server/chunks/ssr/_810f54e9._.js +4 -0
  130. package/.next/server/chunks/ssr/_810f54e9._.js.map +1 -0
  131. package/.next/server/chunks/ssr/_aa01e67e._.js +4 -0
  132. package/.next/server/chunks/ssr/_aa01e67e._.js.map +1 -0
  133. package/.next/server/chunks/ssr/_bf10718b._.js +4 -0
  134. package/.next/server/chunks/ssr/_bf10718b._.js.map +1 -0
  135. package/.next/server/chunks/ssr/_c57c12df._.js +4 -0
  136. package/.next/server/chunks/ssr/_c57c12df._.js.map +1 -0
  137. package/.next/server/chunks/ssr/_d9b57cb8._.js +5 -0
  138. package/.next/server/chunks/ssr/_d9b57cb8._.js.map +1 -0
  139. package/.next/server/chunks/ssr/_e2a766a8._.js +3 -0
  140. package/.next/server/chunks/ssr/_e2a766a8._.js.map +1 -0
  141. package/.next/server/chunks/ssr/_e2b86512._.js +8 -0
  142. package/.next/server/chunks/ssr/_e2b86512._.js.map +1 -0
  143. package/.next/server/chunks/ssr/_ee8ea3aa._.js +19 -0
  144. package/.next/server/chunks/ssr/_ee8ea3aa._.js.map +1 -0
  145. package/.next/server/chunks/ssr/e8449_next_dist_5870db32._.js +3 -0
  146. package/.next/server/chunks/ssr/e8449_next_dist_5870db32._.js.map +1 -0
  147. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_forbidden_5542069c.js +3 -0
  148. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_forbidden_5542069c.js.map +1 -0
  149. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_global-error_d0870f3a.js +3 -0
  150. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_global-error_d0870f3a.js.map +1 -0
  151. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_unauthorized_4c01c8d5.js +3 -0
  152. package/.next/server/chunks/ssr/e8449_next_dist_client_components_builtin_unauthorized_4c01c8d5.js.map +1 -0
  153. package/.next/server/chunks/ssr/e8449_next_dist_client_components_d90ace34._.js +3 -0
  154. package/.next/server/chunks/ssr/e8449_next_dist_client_components_d90ace34._.js.map +1 -0
  155. package/.next/server/chunks/ssr/e8449_next_dist_esm_af7aafb8._.js +6 -0
  156. package/.next/server/chunks/ssr/e8449_next_dist_esm_af7aafb8._.js.map +1 -0
  157. package/.next/server/chunks/ssr/e8449_next_dist_esm_build_templates_app-page_f16f0848.js +4 -0
  158. package/.next/server/chunks/ssr/e8449_next_dist_esm_build_templates_app-page_f16f0848.js.map +1 -0
  159. package/.next/server/chunks/ssr/e8449_next_dist_f0d8a2cc._.js +4 -0
  160. package/.next/server/chunks/ssr/e8449_next_dist_f0d8a2cc._.js.map +1 -0
  161. package/.next/server/chunks/ssr/node_modules__pnpm_37510d96._.js +5 -0
  162. package/.next/server/chunks/ssr/node_modules__pnpm_37510d96._.js.map +1 -0
  163. package/.next/server/chunks/ssr/packages_preview-server_src_app_897ecf1c._.js +3 -0
  164. package/.next/server/chunks/ssr/packages_preview-server_src_app_897ecf1c._.js.map +1 -0
  165. package/.next/server/chunks/ssr/packages_preview-server_src_e17a2a9b._.js +3 -0
  166. package/.next/server/chunks/ssr/packages_preview-server_src_e17a2a9b._.js.map +1 -0
  167. package/.next/server/functions-config-manifest.json +4 -0
  168. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  169. package/.next/server/middleware-build-manifest.js +21 -0
  170. package/.next/server/middleware-manifest.json +6 -0
  171. package/.next/server/next-font-manifest.js +1 -0
  172. package/.next/server/next-font-manifest.json +34 -0
  173. package/.next/server/pages/500.html +2 -0
  174. package/.next/server/pages-manifest.json +3 -0
  175. package/.next/server/server-reference-manifest.js +1 -0
  176. package/.next/server/server-reference-manifest.json +80 -0
  177. package/.next/static/cdYIhKFtJ0GB-yJK5ywz_/_buildManifest.js +11 -0
  178. package/.next/static/cdYIhKFtJ0GB-yJK5ywz_/_clientMiddlewareManifest.json +1 -0
  179. package/.next/static/cdYIhKFtJ0GB-yJK5ywz_/_ssgManifest.js +1 -0
  180. package/.next/static/chunks/0464c7ff175ee6ff.js +1 -0
  181. package/.next/static/chunks/10468413db24762a.js +14 -0
  182. package/.next/static/chunks/2de338262e51ef94.js +3 -0
  183. package/.next/static/chunks/37adc260f85da877.js +1 -0
  184. package/.next/static/chunks/521eee9903bc4d1f.js +5 -0
  185. package/.next/static/chunks/6051bd38272cb442.js +14 -0
  186. package/.next/static/chunks/8d433d4b9d701456.js +1 -0
  187. package/.next/static/chunks/92ba72595aad9df6.js +1 -0
  188. package/.next/static/chunks/959ed978a6e89a66.js +1 -0
  189. package/.next/static/chunks/a6dad97d9634a72d.js +1 -0
  190. package/.next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  191. package/.next/static/chunks/a6db6456c5b75734.js +1 -0
  192. package/.next/static/chunks/c3ece0a7e3e07076.js +1 -0
  193. package/.next/static/chunks/d1b29a74f6814a03.css +3 -0
  194. package/.next/static/chunks/f7ec22614fe1c1fe.js +1 -0
  195. package/.next/static/chunks/turbopack-e062a8e6d6034eb0.js +4 -0
  196. package/.next/static/media/1bffadaabf893a1e-s.7cd81963.woff2 +0 -0
  197. package/.next/static/media/2bbe8d2671613f1f-s.76dcb0b2.woff2 +0 -0
  198. package/.next/static/media/2c55a0e60120577a-s.2a48534a.woff2 +0 -0
  199. package/.next/static/media/5476f68d60460930-s.c995e352.woff2 +0 -0
  200. package/.next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2 +0 -0
  201. package/.next/static/media/9c72aa0f40e4eef8-s.18a48cbc.woff2 +0 -0
  202. package/.next/static/media/SFMonoBold-s.p.b90ec775.otf +0 -0
  203. package/.next/static/media/SFMonoHeavy-s.p.545fe93b.otf +0 -0
  204. package/.next/static/media/SFMonoLight-s.p.7c5363a6.otf +0 -0
  205. package/.next/static/media/SFMonoMedium-s.p.a4fc9904.otf +0 -0
  206. package/.next/static/media/SFMonoRegular-s.p.04ea7bf3.otf +0 -0
  207. package/.next/static/media/SFMonoSemibold-s.p.2d2ddb43.otf +0 -0
  208. package/.next/static/media/ad66f9afd8947f86-s.7a40eb73.woff2 +0 -0
  209. package/.next/static/media/favicon.678eb597.ico +0 -0
  210. package/.next/static/media/logo.22a370b0.png +0 -0
  211. package/.next/trace +1 -0
  212. package/.next/trace-build +1 -0
  213. package/.next/turbopack +0 -0
  214. package/.next/types/routes.d.ts +58 -0
  215. package/.next/types/validator.ts +70 -0
  216. package/CHANGELOG.md +12 -0
  217. package/LICENSE.md +8 -0
  218. package/index.mjs +17 -0
  219. package/jsx-runtime/jsx-dev-runtime.js +26 -0
  220. package/module-punycode.d.ts +3 -0
  221. package/next-env.d.ts +6 -0
  222. package/next.config.mjs +15 -0
  223. package/package.json +78 -0
  224. package/postcss.config.js +5 -0
  225. package/readme.md +33 -0
  226. package/scripts/build-preview-server.mts +25 -0
  227. package/scripts/dev.mts +57 -0
  228. package/scripts/seed.mts +36 -0
  229. package/src/actions/export-single-template.ts +74 -0
  230. package/src/actions/get-template-path-from-slug.ts +32 -0
  231. package/src/actions/get-templates-directory-metadata-action.ts +19 -0
  232. package/src/actions/render-template-by-path.tsx +313 -0
  233. package/src/actions/safe-action.ts +15 -0
  234. package/src/animated-icons-data/help.json +1082 -0
  235. package/src/animated-icons-data/link.json +1309 -0
  236. package/src/animated-icons-data/load.json +443 -0
  237. package/src/animated-icons-data/mail.json +1320 -0
  238. package/src/app/env.ts +14 -0
  239. package/src/app/favicon.ico +0 -0
  240. package/src/app/fonts/SFMono/SFMonoBold.otf +0 -0
  241. package/src/app/fonts/SFMono/SFMonoBoldItalic.otf +0 -0
  242. package/src/app/fonts/SFMono/SFMonoHeavy.otf +0 -0
  243. package/src/app/fonts/SFMono/SFMonoHeavyItalic.otf +0 -0
  244. package/src/app/fonts/SFMono/SFMonoLight.otf +0 -0
  245. package/src/app/fonts/SFMono/SFMonoLightItalic.otf +0 -0
  246. package/src/app/fonts/SFMono/SFMonoMedium.otf +0 -0
  247. package/src/app/fonts/SFMono/SFMonoMediumItalic.otf +0 -0
  248. package/src/app/fonts/SFMono/SFMonoRegular.otf +0 -0
  249. package/src/app/fonts/SFMono/SFMonoRegularItalic.otf +0 -0
  250. package/src/app/fonts/SFMono/SFMonoSemibold.otf +0 -0
  251. package/src/app/fonts/SFMono/SFMonoSemiboldItalic.otf +0 -0
  252. package/src/app/fonts.ts +39 -0
  253. package/src/app/globals.css +136 -0
  254. package/src/app/layout.tsx +46 -0
  255. package/src/app/logo.png +0 -0
  256. package/src/app/page.tsx +52 -0
  257. package/src/app/preview/[...slug]/download-button.tsx +138 -0
  258. package/src/app/preview/[...slug]/error-overlay.tsx +58 -0
  259. package/src/app/preview/[...slug]/page.tsx +90 -0
  260. package/src/app/preview/[...slug]/preview.tsx +249 -0
  261. package/src/app/preview/[...slug]/template-frame.tsx +68 -0
  262. package/src/components/button.tsx +101 -0
  263. package/src/components/code-container.tsx +169 -0
  264. package/src/components/code-snippet.tsx +9 -0
  265. package/src/components/code.tsx +185 -0
  266. package/src/components/heading.tsx +113 -0
  267. package/src/components/icons/icon-arrow-down.tsx +16 -0
  268. package/src/components/icons/icon-base.tsx +26 -0
  269. package/src/components/icons/icon-bug.tsx +19 -0
  270. package/src/components/icons/icon-button.tsx +23 -0
  271. package/src/components/icons/icon-check.tsx +19 -0
  272. package/src/components/icons/icon-clipboard.tsx +40 -0
  273. package/src/components/icons/icon-cloud-alert.tsx +18 -0
  274. package/src/components/icons/icon-cloud-check.tsx +17 -0
  275. package/src/components/icons/icon-download.tsx +19 -0
  276. package/src/components/icons/icon-file.tsx +19 -0
  277. package/src/components/icons/icon-folder-open.tsx +19 -0
  278. package/src/components/icons/icon-folder.tsx +18 -0
  279. package/src/components/icons/icon-hide-sidebar.tsx +23 -0
  280. package/src/components/icons/icon-image.tsx +19 -0
  281. package/src/components/icons/icon-info.tsx +18 -0
  282. package/src/components/icons/icon-link.tsx +14 -0
  283. package/src/components/icons/icon-loader.tsx +16 -0
  284. package/src/components/icons/icon-monitor.tsx +19 -0
  285. package/src/components/icons/icon-moon.tsx +16 -0
  286. package/src/components/icons/icon-phone.tsx +26 -0
  287. package/src/components/icons/icon-reload.tsx +18 -0
  288. package/src/components/icons/icon-source.tsx +19 -0
  289. package/src/components/icons/icon-stamp.tsx +14 -0
  290. package/src/components/icons/icon-sun.tsx +74 -0
  291. package/src/components/icons/icon-warning.tsx +31 -0
  292. package/src/components/index.ts +7 -0
  293. package/src/components/logo.tsx +41 -0
  294. package/src/components/resizable-wrapper.tsx +269 -0
  295. package/src/components/shell.tsx +95 -0
  296. package/src/components/sidebar/file-tree-directory-children.tsx +142 -0
  297. package/src/components/sidebar/file-tree-directory.tsx +92 -0
  298. package/src/components/sidebar/file-tree.tsx +31 -0
  299. package/src/components/sidebar/index.ts +1 -0
  300. package/src/components/sidebar/sidebar.tsx +46 -0
  301. package/src/components/text.tsx +99 -0
  302. package/src/components/toolbar/checking-results.tsx +150 -0
  303. package/src/components/toolbar/code-preview-line-link.tsx +39 -0
  304. package/src/components/toolbar/results-table.tsx +0 -0
  305. package/src/components/toolbar/results.tsx +52 -0
  306. package/src/components/toolbar/toolbar-button.tsx +52 -0
  307. package/src/components/toolbar/use-cached-state.ts +36 -0
  308. package/src/components/toolbar.tsx +182 -0
  309. package/src/components/tooltip-content.tsx +31 -0
  310. package/src/components/tooltip.tsx +19 -0
  311. package/src/components/topbar/active-view-toggle-group.tsx +86 -0
  312. package/src/components/topbar/emulated-dark-mode-toggle.tsx +58 -0
  313. package/src/components/topbar/view-size-controls.tsx +173 -0
  314. package/src/components/topbar.tsx +59 -0
  315. package/src/contexts/preview.tsx +91 -0
  316. package/src/contexts/templates.tsx +57 -0
  317. package/src/contexts/toolbar.tsx +22 -0
  318. package/src/hooks/use-clamped-state.ts +24 -0
  319. package/src/hooks/use-fragment-identifier.ts +14 -0
  320. package/src/hooks/use-hot-reload.ts +31 -0
  321. package/src/hooks/use-rendering-metadata.ts +37 -0
  322. package/src/hooks/use-template-rendering-result.ts +58 -0
  323. package/src/utils/cn.ts +6 -0
  324. package/src/utils/constants.ts +8 -0
  325. package/src/utils/contains-template.ts +52 -0
  326. package/src/utils/convert-stack-with-sourcemap.ts +79 -0
  327. package/src/utils/copy-text-to-clipboard.ts +7 -0
  328. package/src/utils/create-jsx-runtime.ts +47 -0
  329. package/src/utils/esbuild/escape-string-for-regex.ts +3 -0
  330. package/src/utils/esbuild/renderring-utilities-exporter.ts +64 -0
  331. package/src/utils/get-line-and-column-from-offset.ts +11 -0
  332. package/src/utils/get-template-component.ts +158 -0
  333. package/src/utils/get-templates-directory-metadata.ts +195 -0
  334. package/src/utils/index.ts +6 -0
  335. package/src/utils/language-map.ts +7 -0
  336. package/src/utils/load-stream.ts +15 -0
  337. package/src/utils/register-spinner-autostopping.ts +28 -0
  338. package/src/utils/result.ts +49 -0
  339. package/src/utils/run-bundled-code.ts +86 -0
  340. package/src/utils/sanitize.ts +6 -0
  341. package/src/utils/sleep.ts +3 -0
  342. package/src/utils/snake-to-camel.ts +5 -0
  343. package/src/utils/static-node-modules-for-vm.ts +93 -0
  344. package/src/utils/style-text.ts +11 -0
  345. package/src/utils/types/as.ts +26 -0
  346. package/src/utils/types/error-object.ts +11 -0
  347. package/src/utils/types/hot-reload-change.ts +13 -0
  348. package/src/utils/types/template.ts +8 -0
  349. package/src/utils/unreachable.ts +8 -0
  350. package/templates/.gitkeep +0 -0
  351. package/tsconfig.json +47 -0
@@ -0,0 +1,52 @@
1
+ import { motion } from 'framer-motion';
2
+ import { cn } from '../../utils';
3
+ import { Tooltip } from '../tooltip';
4
+
5
+ interface ToolbarButtonProps extends React.ComponentProps<'button'> {
6
+ children: React.ReactNode;
7
+ active?: boolean;
8
+ tooltip?: React.ReactNode;
9
+ delayDuration?: number;
10
+ }
11
+
12
+ export const ToolbarButton = ({
13
+ children,
14
+ className,
15
+ active,
16
+ tooltip,
17
+ delayDuration = 500,
18
+ ...props
19
+ }: ToolbarButtonProps) => {
20
+ return (
21
+ <Tooltip.Provider>
22
+ <Tooltip delayDuration={delayDuration}>
23
+ <Tooltip.Trigger asChild>
24
+ <button
25
+ type="button"
26
+ {...props}
27
+ className={cn(
28
+ 'h-full w-fit font-regular flex text-sm text-slate-10 items-center align-middle justify-center px-1 gap-2 relative',
29
+ 'hover:text-slate-12 transition-colors',
30
+ active && 'data-[state=active]:text-cyan-11',
31
+ className,
32
+ )}
33
+ >
34
+ {children}
35
+ {active ? (
36
+ <motion.span
37
+ className="-bottom-px absolute rounded-xs left-0 w-full bg-cyan-11 h-px"
38
+ layoutId="active-toolbar-button"
39
+ transition={{
40
+ type: 'spring',
41
+ bounce: 0.2,
42
+ duration: 0.6,
43
+ }}
44
+ />
45
+ ) : null}
46
+ </button>
47
+ </Tooltip.Trigger>
48
+ {tooltip ? <Tooltip.Content>{tooltip}</Tooltip.Content> : null}
49
+ </Tooltip>
50
+ </Tooltip.Provider>
51
+ );
52
+ };
@@ -0,0 +1,36 @@
1
+ import { useSyncExternalStore } from 'react';
2
+
3
+ export const useCachedState = <T>(key: string) => {
4
+ let value: T | undefined;
5
+ if (
6
+ 'localStorage' in global &&
7
+ typeof global.localStorage.getItem === 'function'
8
+ ) {
9
+ const storedValue = global.localStorage.getItem(key);
10
+ if (storedValue !== null && storedValue !== 'undefined') {
11
+ try {
12
+ value = JSON.parse(storedValue) as T;
13
+ } catch (_exception) {
14
+ console.warn(
15
+ 'Failed to load stored value for',
16
+ key,
17
+ 'with value',
18
+ storedValue,
19
+ );
20
+ }
21
+ }
22
+ }
23
+
24
+ return [
25
+ useSyncExternalStore(
26
+ () => () => {},
27
+ () => value,
28
+ () => undefined,
29
+ ),
30
+ function setValue(newValue: T | undefined) {
31
+ if ('localStorage' in global) {
32
+ global.localStorage.setItem(key, JSON.stringify(newValue));
33
+ }
34
+ },
35
+ ] as const;
36
+ };
@@ -0,0 +1,182 @@
1
+ 'use client';
2
+
3
+ import * as Tabs from '@radix-ui/react-tabs';
4
+ import { LayoutGroup } from 'framer-motion';
5
+ import { usePathname, useRouter, useSearchParams } from 'next/navigation';
6
+ import type { ComponentProps } from 'react';
7
+ import * as React from 'react';
8
+ import { usePreviewContext } from '../contexts/preview';
9
+ import { cn } from '../utils';
10
+ import { IconArrowDown } from './icons/icon-arrow-down';
11
+ import { IconCheck } from './icons/icon-check';
12
+ import { IconInfo } from './icons/icon-info';
13
+ import { ToolbarButton } from './toolbar/toolbar-button';
14
+
15
+ export type ToolbarTabValue = 'linter';
16
+
17
+ export const useToolbarState = () => {
18
+ const searchParams = useSearchParams();
19
+
20
+ const activeTab = (searchParams.get('toolbar-panel') ?? undefined) as
21
+ | ToolbarTabValue
22
+ | undefined;
23
+
24
+ return {
25
+ activeTab,
26
+
27
+ toggled: activeTab !== undefined,
28
+ };
29
+ };
30
+
31
+ const ToolbarInner = () => {
32
+ const pathname = usePathname();
33
+ const searchParams = useSearchParams();
34
+ const router = useRouter();
35
+
36
+ const { activeTab, toggled } = useToolbarState();
37
+
38
+ const setActivePanelValue = (newValue: ToolbarTabValue | undefined) => {
39
+ const params = new URLSearchParams(searchParams);
40
+ if (newValue === undefined) {
41
+ params.delete('toolbar-panel');
42
+ } else {
43
+ params.set('toolbar-panel', newValue);
44
+ }
45
+ router.push(`${pathname}?${params.toString()}${location.hash}`);
46
+ };
47
+
48
+ const id = React.useId();
49
+
50
+ return (
51
+ <div
52
+ data-toggled={toggled}
53
+ className={cn(
54
+ 'absolute bottom-0 left-0 right-0',
55
+ 'border-t border-slate-6 group/toolbar text-xs text-slate-11 h-52 transition-transform',
56
+ 'data-[toggled=false]:translate-y-42.5',
57
+ )}
58
+ >
59
+ <Tabs.Root
60
+ value={activeTab ?? ''}
61
+ onValueChange={(newValue) => {
62
+ setActivePanelValue(newValue as ToolbarTabValue);
63
+ }}
64
+ asChild
65
+ >
66
+ <div className="flex flex-col h-full">
67
+ <Tabs.List className="flex gap-4 px-4 border-b border-solid border-slate-6 h-10 w-full shrink-0">
68
+ <LayoutGroup id={`toolbar-${id}`}>
69
+ <Tabs.Trigger asChild value="linter">
70
+ <ToolbarButton active={activeTab === 'linter'}>
71
+ Linter
72
+ </ToolbarButton>
73
+ </Tabs.Trigger>
74
+ </LayoutGroup>
75
+ <div className="flex gap-0.5 ml-auto">
76
+ <ToolbarButton
77
+ delayDuration={0}
78
+ tooltip={
79
+ activeTab === 'linter'
80
+ ? 'PDF-specific linting features are coming soon. This will check for page breaks, print CSS issues, font loading problems, and more.'
81
+ : 'Info'
82
+ }
83
+ >
84
+ <IconInfo size={24} />
85
+ </ToolbarButton>
86
+ <ToolbarButton
87
+ tooltip="Toggle toolbar"
88
+ onClick={() => {
89
+ if (activeTab === undefined) {
90
+ setActivePanelValue('linter');
91
+ } else {
92
+ setActivePanelValue(undefined);
93
+ }
94
+ }}
95
+ >
96
+ <IconArrowDown
97
+ size={24}
98
+ className="transition-transform group-data-[toggled=false]/toolbar:rotate-180"
99
+ />
100
+ </ToolbarButton>
101
+ </div>
102
+ </Tabs.List>
103
+
104
+ <div className="grow transition-opacity opacity-100 group-data-[toggled=false]/toolbar:opacity-0 overflow-y-auto pr-3 pl-4 pt-3">
105
+ <Tabs.Content value="linter">
106
+ <SuccessWrapper>
107
+ <SuccessIcon />
108
+ <SuccessTitle>PDF Linter Coming Soon</SuccessTitle>
109
+ <SuccessDescription>
110
+ We're working on PDF-specific linting features that will check
111
+ for page breaks, print CSS issues, font loading problems, and
112
+ more. Stay tuned!
113
+ </SuccessDescription>
114
+ </SuccessWrapper>
115
+ </Tabs.Content>
116
+ </div>
117
+ </div>
118
+ </Tabs.Root>
119
+ </div>
120
+ );
121
+ };
122
+
123
+ const SuccessWrapper = ({ children }: { children: React.ReactNode }) => {
124
+ return (
125
+ <div className="flex flex-col items-center justify-center pt-8">
126
+ {children}
127
+ </div>
128
+ );
129
+ };
130
+
131
+ const SuccessIcon = () => {
132
+ return (
133
+ <div className="relative mb-8 flex items-center justify-center">
134
+ <div className="h-16 w-16 rounded-full bg-linear-to-br from-green-300/20 opacity-80 to-emerald-500/30 blur-md absolute m-auto left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
135
+ <div className="h-12 w-12 rounded-full bg-linear-to-br from-green-400/80 opacity-10 to-emerald-600/80 absolute m-auto left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 shadow-lg" />
136
+ <div className="h-10 w-10 rounded-full bg-linear-to-br from-green-400 to-emerald-600 flex items-center justify-center absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]">
137
+ <IconCheck size={24} className="text-white drop-shadow-xs" />
138
+ </div>
139
+ </div>
140
+ );
141
+ };
142
+
143
+ const SuccessTitle = ({
144
+ children,
145
+ className,
146
+ ...props
147
+ }: ComponentProps<'h3'>) => {
148
+ return (
149
+ <h3
150
+ className={cn('text-slate-12 font-medium text-base mb-1', className)}
151
+ {...props}
152
+ >
153
+ {children}
154
+ </h3>
155
+ );
156
+ };
157
+
158
+ const SuccessDescription = ({
159
+ children,
160
+ className,
161
+ ...props
162
+ }: ComponentProps<'p'>) => {
163
+ return (
164
+ <p
165
+ className={cn(
166
+ 'text-slate-11 text-sm text-center max-w-[320px]',
167
+ className,
168
+ )}
169
+ {...props}
170
+ >
171
+ {children}
172
+ </p>
173
+ );
174
+ };
175
+
176
+ export function Toolbar() {
177
+ const { renderedTemplateMetadata } = usePreviewContext();
178
+
179
+ if (renderedTemplateMetadata === undefined) return null;
180
+
181
+ return <ToolbarInner />;
182
+ }
@@ -0,0 +1,31 @@
1
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
+ import * as React from 'react';
3
+ import { cn } from '../utils';
4
+
5
+ type ContentElement = React.ComponentRef<typeof TooltipPrimitive.Content>;
6
+ type ContentProps = React.ComponentPropsWithoutRef<
7
+ typeof TooltipPrimitive.Content
8
+ >;
9
+
10
+ export type TooltipProps = ContentProps;
11
+
12
+ export const TooltipContent = React.forwardRef<
13
+ ContentElement,
14
+ Readonly<TooltipProps>
15
+ >(({ sideOffset = 6, children, ...props }, forwardedRef) => (
16
+ <TooltipPrimitive.Portal>
17
+ <TooltipPrimitive.Content
18
+ {...props}
19
+ className={cn(
20
+ 'z-20 rounded-md border border-slate-6 bg-black px-3 py-2 text-white text-xs',
21
+ 'font-sans max-w-60',
22
+ )}
23
+ ref={forwardedRef}
24
+ sideOffset={sideOffset}
25
+ >
26
+ {children}
27
+ </TooltipPrimitive.Content>
28
+ </TooltipPrimitive.Portal>
29
+ ));
30
+
31
+ TooltipContent.displayName = 'TooltipContent';
@@ -0,0 +1,19 @@
1
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
2
+ import type * as React from 'react';
3
+ import { TooltipContent } from './tooltip-content';
4
+
5
+ type RootProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>;
6
+
7
+ export type TooltipProps = RootProps;
8
+
9
+ export const TooltipRoot: React.FC<Readonly<TooltipProps>> = ({
10
+ children,
11
+ ...props
12
+ }) => <TooltipPrimitive.Root {...props}>{children}</TooltipPrimitive.Root>;
13
+
14
+ export const Tooltip = Object.assign(TooltipRoot, {
15
+ Arrow: TooltipPrimitive.TooltipArrow,
16
+ Provider: TooltipPrimitive.TooltipProvider,
17
+ Content: TooltipContent,
18
+ Trigger: TooltipPrimitive.TooltipTrigger,
19
+ });
@@ -0,0 +1,86 @@
1
+ import * as ToggleGroup from '@radix-ui/react-toggle-group';
2
+ import { motion } from 'framer-motion';
3
+ import { cn } from '../../utils';
4
+ import { tabTransition } from '../../utils/constants';
5
+ import { IconMonitor } from '../icons/icon-monitor';
6
+ import { IconSource } from '../icons/icon-source';
7
+ import { Tooltip } from '../tooltip';
8
+
9
+ interface ActiveViewToggleGroupProps {
10
+ activeView: string;
11
+ setActiveView: (view: string) => void;
12
+ }
13
+
14
+ export const ActiveViewToggleGroup = ({
15
+ activeView,
16
+ setActiveView,
17
+ }: ActiveViewToggleGroupProps) => {
18
+ return (
19
+ <ToggleGroup.Root
20
+ aria-label="View mode"
21
+ className="lg:absolute lg:left-1/2 lg:-translate-x-1/2 inline-block items-center bg-slate-2 border border-slate-6 rounded-md overflow-hidden h-[36px]"
22
+ onValueChange={(value) => {
23
+ if (value) setActiveView(value);
24
+ }}
25
+ type="single"
26
+ value={activeView}
27
+ >
28
+ <ToggleGroup.Item value="preview">
29
+ <Tooltip>
30
+ <Tooltip.Trigger asChild>
31
+ <div
32
+ className={cn(
33
+ 'w-9 flex items-center py-2 transition ease-in-out duration-200 relative hover:text-slate-12',
34
+ {
35
+ 'text-slate-11': activeView !== 'preview',
36
+ 'text-slate-12': activeView === 'preview',
37
+ },
38
+ )}
39
+ >
40
+ {activeView === 'preview' && (
41
+ <motion.span
42
+ animate={{ opacity: 1 }}
43
+ className="absolute left-0 right-0 top-0 bottom-0 bg-slate-4"
44
+ exit={{ opacity: 0 }}
45
+ initial={{ opacity: 0 }}
46
+ layoutId="topbar-tabs"
47
+ transition={tabTransition}
48
+ />
49
+ )}
50
+ <IconMonitor className="m-auto" />
51
+ </div>
52
+ </Tooltip.Trigger>
53
+ <Tooltip.Content>Preview</Tooltip.Content>
54
+ </Tooltip>
55
+ </ToggleGroup.Item>
56
+ <ToggleGroup.Item value="source">
57
+ <Tooltip>
58
+ <Tooltip.Trigger asChild>
59
+ <div
60
+ className={cn(
61
+ 'w-9 flex py-2 transition ease-in-out duration-200 relative hover:text-slate-12',
62
+ {
63
+ 'text-slate-11': activeView !== 'source',
64
+ 'text-slate-12': activeView === 'source',
65
+ },
66
+ )}
67
+ >
68
+ {activeView === 'source' && (
69
+ <motion.span
70
+ animate={{ opacity: 1 }}
71
+ className="absolute left-0 right-0 top-0 bottom-0 bg-slate-4"
72
+ exit={{ opacity: 0 }}
73
+ initial={{ opacity: 0 }}
74
+ layoutId="topbar-tabs"
75
+ transition={tabTransition}
76
+ />
77
+ )}
78
+ <IconSource className="m-auto" />
79
+ </div>
80
+ </Tooltip.Trigger>
81
+ <Tooltip.Content>Code</Tooltip.Content>
82
+ </Tooltip>
83
+ </ToggleGroup.Item>
84
+ </ToggleGroup.Root>
85
+ );
86
+ };
@@ -0,0 +1,58 @@
1
+ import * as Toggle from '@radix-ui/react-toggle';
2
+ import { cn } from '../../utils';
3
+ import { IconMoon } from '../icons/icon-moon';
4
+ import { IconSun } from '../icons/icon-sun';
5
+ import { Tooltip } from '../tooltip';
6
+
7
+ interface EmulatedDarkModeToggleProps {
8
+ enabled: boolean;
9
+ onChange: (enabled: boolean) => unknown;
10
+ }
11
+
12
+ export const EmulatedDarkModeToggle = ({
13
+ enabled,
14
+ onChange,
15
+ }: EmulatedDarkModeToggleProps) => {
16
+ return (
17
+ <Tooltip>
18
+ <Tooltip.Trigger asChild>
19
+ <Toggle.Root
20
+ value="dark"
21
+ className={cn(
22
+ 'relative w-9 h-9 flex items-center justify-center border border-slate-6 text-sm rounded-lg transition duration-200 ease-in-out',
23
+ 'text-slate-11 hover:text-slate-12 aria-pressed:bg-slate-4',
24
+ )}
25
+ pressed={enabled}
26
+ onPressedChange={() => onChange(!enabled)}
27
+ >
28
+ <div className="relative w-5 h-5">
29
+ <div
30
+ className={cn(
31
+ 'absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out',
32
+ enabled
33
+ ? 'opacity-0 scale-50 rotate-90'
34
+ : 'opacity-100 scale-100 rotate-0',
35
+ )}
36
+ >
37
+ <IconMoon />
38
+ </div>
39
+ <div
40
+ className={cn(
41
+ 'absolute inset-0 flex items-center justify-center transition-all duration-300 ease-in-out',
42
+ enabled
43
+ ? 'opacity-100 scale-100 rotate-0'
44
+ : 'opacity-0 scale-50 -rotate-90',
45
+ )}
46
+ >
47
+ <IconSun />
48
+ </div>
49
+ </div>
50
+ </Toggle.Root>
51
+ </Tooltip.Trigger>
52
+ <Tooltip.Content>
53
+ When enabled, inverts colors in the preview to emulate dark mode
54
+ rendering.
55
+ </Tooltip.Content>
56
+ </Tooltip>
57
+ );
58
+ };
@@ -0,0 +1,173 @@
1
+ import * as Popover from '@radix-ui/react-popover';
2
+ import * as React from 'react';
3
+ import { cn } from '../../utils';
4
+ import { IconArrowDown } from '../icons/icon-arrow-down';
5
+ import { Tooltip } from '../tooltip';
6
+
7
+ interface ViewDimensions {
8
+ width: number;
9
+ height: number;
10
+ }
11
+
12
+ interface ViewSizeControlsProps {
13
+ minWidth: number;
14
+ minHeight: number;
15
+ viewWidth: number;
16
+ setViewWidth: (width: number) => void;
17
+ viewHeight: number;
18
+ setViewHeight: (height: number) => void;
19
+ }
20
+
21
+ interface PresetOption {
22
+ name: string;
23
+ dimensions: ViewDimensions;
24
+ icon: React.ReactNode;
25
+ }
26
+
27
+ /**
28
+ * A4 dimensions in pixels at 96 DPI (standard web resolution)
29
+ * A4 = 210mm × 297mm = 8.27" × 11.69" = 794px × 1123px at 96 DPI
30
+ */
31
+ const A4_WIDTH = 794;
32
+ const A4_HEIGHT = 1123;
33
+
34
+ export const VIEW_PRESETS: PresetOption[] = [
35
+ {
36
+ name: 'A4',
37
+ dimensions: { width: A4_WIDTH, height: A4_HEIGHT },
38
+ icon: (
39
+ <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
40
+ <path
41
+ d="M3.5 2C3.22386 2 3 2.22386 3 2.5V12.5C3 12.7761 3.22386 13 3.5 13H11.5C11.7761 13 12 12.7761 12 12.5V2.5C12 2.22386 11.7761 2 11.5 2H3.5ZM2 2.5C2 1.67157 2.67157 1 3.5 1H11.5C12.3284 1 13 1.67157 13 2.5V12.5C13 13.3284 12.3284 14 11.5 14H3.5C2.67157 14 2 13.3284 2 12.5V2.5ZM4 3.5C4 3.22386 4.22386 3 4.5 3H10.5C10.7761 3 11 3.22386 11 3.5C11 3.77614 10.7761 4 10.5 4H4.5C4.22386 4 4 3.77614 4 3.5ZM4.5 5C4.22386 5 4 5.22386 4 5.5C4 5.77614 4.22386 6 4.5 6H10.5C10.7761 6 11 5.77614 11 5.5C11 5.22386 10.7761 5 10.5 5H4.5ZM4 7.5C4 7.22386 4.22386 7 4.5 7H10.5C10.7761 7 11 7.22386 11 7.5C11 7.77614 10.7761 8 10.5 8H4.5C4.22386 8 4 7.77614 4 7.5ZM4.5 9C4.22386 9 4 9.22386 4 9.5C4 9.77614 4.22386 10 4.5 10H10.5C10.7761 10 11 9.77614 11 9.5C11 9.22386 10.7761 9 10.5 9H4.5Z"
42
+ fill="currentColor"
43
+ fillRule="evenodd"
44
+ clipRule="evenodd"
45
+ />
46
+ </svg>
47
+ ),
48
+ },
49
+ {
50
+ name: 'Desktop',
51
+ dimensions: { width: 1024, height: 600 },
52
+ icon: (
53
+ <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
54
+ <path
55
+ d="M1 3.25C1 3.11193 1.11193 3 1.25 3H13.75C13.8881 3 14 3.11193 14 3.25V10.75C14 10.8881 13.8881 11 13.75 11H1.25C1.11193 11 1 10.8881 1 10.75V3.25ZM1.25 2C0.559643 2 0 2.55964 0 3.25V10.75C0 11.4404 0.559644 12 1.25 12H5.07341L4.82991 13.2986C4.76645 13.6371 5.02612 13.95 5.37049 13.95H9.62951C9.97389 13.95 10.2336 13.6371 10.1701 13.2986L9.92659 12H13.75C14.4404 12 15 11.4404 15 10.75V3.25C15 2.55964 14.4404 2 13.75 2H1.25ZM9.01091 12H5.98909L5.79222 13.05H9.20778L9.01091 12Z"
56
+ fill="currentColor"
57
+ fillRule="evenodd"
58
+ clipRule="evenodd"
59
+ />
60
+ </svg>
61
+ ),
62
+ },
63
+ ];
64
+
65
+ export const ViewSizeControls = ({
66
+ minWidth,
67
+ minHeight,
68
+ viewWidth,
69
+ viewHeight,
70
+ setViewWidth,
71
+ setViewHeight,
72
+ }: ViewSizeControlsProps) => {
73
+ const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
74
+ const [internalWidth, setInternalWidth] = React.useState(viewWidth);
75
+ const [internalHeight, setInternalHeight] = React.useState(viewHeight);
76
+
77
+ const handlePresetSelect = (dimensions: ViewDimensions) => {
78
+ setViewWidth(dimensions.width);
79
+ setViewHeight(dimensions.height);
80
+ };
81
+
82
+ React.useEffect(() => {
83
+ setInternalWidth(viewWidth);
84
+ setInternalHeight(viewHeight);
85
+ }, [viewWidth, viewHeight]);
86
+
87
+ return (
88
+ <div className="relative flex h-9 w-fit overflow-hidden rounded-lg border border-slate-6 text-sm transition-colors duration-300 ease-in-out">
89
+ {VIEW_PRESETS.map((preset) => (
90
+ <Tooltip key={preset.name}>
91
+ <Tooltip.Trigger asChild>
92
+ <button
93
+ key={preset.name}
94
+ onClick={() => handlePresetSelect(preset.dimensions)}
95
+ className={cn(
96
+ 'relative flex items-center justify-center w-9 transition-colors hover:text-slate-12',
97
+ {
98
+ 'bg-slate-4': viewWidth === preset.dimensions.width,
99
+ },
100
+ )}
101
+ type="button"
102
+ >
103
+ {preset.icon}
104
+ </button>
105
+ </Tooltip.Trigger>
106
+ <Tooltip.Content>{preset.name}</Tooltip.Content>
107
+ </Tooltip>
108
+ ))}
109
+
110
+ <Popover.Root open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
111
+ <Popover.Trigger asChild>
112
+ <button
113
+ type="button"
114
+ className="relative flex items-center justify-center overflow-hidden w-9 text-slate-11 text-sm leading-none outline-hidden transition-colors ease-linear focus-within:text-slate-12 hover:text-slate-12 focus:text-slate-12"
115
+ >
116
+ <span className="sr-only">View presets</span>
117
+ <IconArrowDown
118
+ className={cn(
119
+ 'transform transition-transform duration-200 ease-[cubic-bezier(.36,.66,.6,1)]',
120
+ {
121
+ '-rotate-180': isDropdownOpen,
122
+ },
123
+ )}
124
+ />
125
+ </button>
126
+ </Popover.Trigger>
127
+ <Popover.Portal>
128
+ <Popover.Content
129
+ align="end"
130
+ className="flex min-w-48 flex-col gap-2 rounded-md border border-slate-8 border-solid bg-black px-2 py-2 text-white"
131
+ sideOffset={5}
132
+ >
133
+ <div className="flex w-full items-center justify-between text-sm gap-2">
134
+ <span className="font-medium text-slate-11 text-xs">Width</span>
135
+ <input
136
+ type="number"
137
+ value={internalWidth}
138
+ onChange={(e) => {
139
+ const value = Number(e.target.value);
140
+
141
+ setInternalWidth(value);
142
+
143
+ if (value >= minWidth) {
144
+ setViewWidth(value);
145
+ }
146
+ }}
147
+ className="w-20 appearance-none rounded-lg border border-slate-6 bg-slate-5 px-1 py-1 text-sm text-slate-12 placeholder-slate-10 outline-hidden transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
148
+ />
149
+ </div>
150
+
151
+ <div className="flex w-full items-center justify-between text-sm gap-2">
152
+ <span className="font-medium text-slate-11 text-xs">Height</span>
153
+ <input
154
+ type="number"
155
+ value={internalHeight}
156
+ onChange={(e) => {
157
+ const value = Number(e.target.value);
158
+
159
+ setInternalHeight(value);
160
+
161
+ if (value >= minHeight) {
162
+ setViewHeight(value);
163
+ }
164
+ }}
165
+ className="w-20 appearance-none rounded-lg border border-slate-6 bg-slate-5 px-1 py-1 text-sm text-slate-12 placeholder-slate-10 outline-hidden transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
166
+ />
167
+ </div>
168
+ </Popover.Content>
169
+ </Popover.Portal>
170
+ </Popover.Root>
171
+ </div>
172
+ );
173
+ };