@svgrid/enterprise 3.0.0 → 3.0.2

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 (390) hide show
  1. package/README.md +95 -95
  2. package/dist/ExpressionEditorHarness.svelte.d.ts +2 -2
  3. package/dist/SvAdvancedFilter.svelte.d.ts +2 -2
  4. package/dist/SvAlertRuleEditor.svelte.d.ts +2 -2
  5. package/dist/SvAlertsManager.svelte.d.ts +3 -3
  6. package/dist/SvAlertsPanel.svelte.d.ts +1 -1
  7. package/dist/SvAuthGate.svelte +115 -115
  8. package/dist/SvAuthGate.svelte.d.ts +1 -1
  9. package/dist/SvBoard.svelte.d.ts +1 -1
  10. package/dist/SvExportMenu.svelte +553 -553
  11. package/dist/SvExportMenu.svelte.d.ts +2 -2
  12. package/dist/SvExpressionEditor.svelte.d.ts +2 -2
  13. package/dist/SvFileInput.svelte +113 -113
  14. package/dist/SvFormulaBar.svelte +606 -0
  15. package/dist/SvFormulaBar.svelte.d.ts +62 -0
  16. package/dist/SvGridAlerts.svelte.d.ts +2 -2
  17. package/dist/SvGridBoard.svelte +2358 -2358
  18. package/dist/SvGridBulkEditDrawer.svelte.d.ts +1 -1
  19. package/dist/SvGridEditPanel.svelte +1157 -1157
  20. package/dist/SvGridEditPanel.svelte.d.ts +3 -3
  21. package/dist/SvGridMasterDetail.svelte +116 -116
  22. package/dist/SvGridMasterDetail.svelte.d.ts +1 -1
  23. package/dist/SvGridScheduler.svelte +5358 -5358
  24. package/dist/SvImportDialog.svelte +1020 -1020
  25. package/dist/SvImportDialog.svelte.d.ts +2 -2
  26. package/dist/SvLookupInput.svelte +180 -180
  27. package/dist/SvLookupInput.svelte.d.ts +1 -1
  28. package/dist/SvPivotDesigner.svelte +2041 -1990
  29. package/dist/SvPivotDesigner.svelte.d.ts +2 -2
  30. package/dist/SvRecordDetail.svelte.d.ts +1 -1
  31. package/dist/SvRibbonIcon.svelte +46 -0
  32. package/dist/SvRibbonIcon.svelte.d.ts +17 -0
  33. package/dist/SvSchedule.svelte.d.ts +1 -1
  34. package/dist/SvSchemaChart.svelte +236 -234
  35. package/dist/SvSchemaChart.svelte.d.ts +2 -2
  36. package/dist/SvSchemaDashboard.svelte +130 -130
  37. package/dist/SvSchemaDashboard.svelte.d.ts +3 -3
  38. package/dist/SvSheet.svelte +4114 -0
  39. package/dist/SvSheet.svelte.d.ts +142 -0
  40. package/dist/SvSheetComment.svelte +98 -0
  41. package/dist/SvSheetComment.svelte.d.ts +22 -0
  42. package/dist/SvSheetConditionalFormat.svelte +162 -0
  43. package/dist/SvSheetConditionalFormat.svelte.d.ts +14 -0
  44. package/dist/SvSheetDataValidation.svelte +249 -0
  45. package/dist/SvSheetDataValidation.svelte.d.ts +14 -0
  46. package/dist/SvSheetFilterMenu.svelte +217 -0
  47. package/dist/SvSheetFilterMenu.svelte.d.ts +17 -0
  48. package/dist/SvSheetFindReplace.svelte +132 -0
  49. package/dist/SvSheetFindReplace.svelte.d.ts +10 -0
  50. package/dist/SvSheetFormatCells.svelte +421 -0
  51. package/dist/SvSheetFormatCells.svelte.d.ts +17 -0
  52. package/dist/SvSheetGoalSeek.svelte +144 -0
  53. package/dist/SvSheetGoalSeek.svelte.d.ts +12 -0
  54. package/dist/SvSheetInsertFunction.svelte +147 -0
  55. package/dist/SvSheetInsertFunction.svelte.d.ts +8 -0
  56. package/dist/SvSheetListPicker.svelte +89 -0
  57. package/dist/SvSheetListPicker.svelte.d.ts +16 -0
  58. package/dist/SvSheetManageRules.svelte +185 -0
  59. package/dist/SvSheetManageRules.svelte.d.ts +16 -0
  60. package/dist/SvSheetNameManager.svelte +225 -0
  61. package/dist/SvSheetNameManager.svelte.d.ts +11 -0
  62. package/dist/SvSheetPasteSpecial.svelte +105 -0
  63. package/dist/SvSheetPasteSpecial.svelte.d.ts +12 -0
  64. package/dist/SvSheetRemoveDuplicates.svelte +149 -0
  65. package/dist/SvSheetRemoveDuplicates.svelte.d.ts +12 -0
  66. package/dist/SvSheetRibbon.svelte +1201 -0
  67. package/dist/SvSheetRibbon.svelte.d.ts +34 -0
  68. package/dist/SvSheetSizeDialog.svelte +58 -0
  69. package/dist/SvSheetSizeDialog.svelte.d.ts +11 -0
  70. package/dist/SvSheetTabs.svelte +441 -0
  71. package/dist/SvSheetTabs.svelte.d.ts +36 -0
  72. package/dist/SvSheetTabs.test-harness.svelte.d.ts +17 -0
  73. package/dist/SvSheetTabs.test-harness.svelte.js +22 -0
  74. package/dist/SvSheetTextToColumns.svelte +172 -0
  75. package/dist/SvSheetTextToColumns.svelte.d.ts +13 -0
  76. package/dist/SvSheetValidationAlert.svelte +83 -0
  77. package/dist/SvSheetValidationAlert.svelte.d.ts +15 -0
  78. package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -1
  79. package/dist/advanced-filter-enable.js +3 -3
  80. package/dist/alerts/alert-engine-attach.d.ts +2 -2
  81. package/dist/alerts/alert-engine-attach.js +3 -3
  82. package/dist/alerts/alert-engine.d.ts +1 -1
  83. package/dist/alerts/alert-engine.js +2 -2
  84. package/dist/alerts/alert-formats.d.ts +1 -1
  85. package/dist/alerts/alert-formats.js +1 -1
  86. package/dist/alerts/alert-observer.d.ts +3 -3
  87. package/dist/alerts/alert-observer.js +1 -1
  88. package/dist/alerts/alert-storage.d.ts +1 -1
  89. package/dist/alerts/alert-store.svelte.d.ts +1 -1
  90. package/dist/alerts/alert-types.d.ts +2 -2
  91. package/dist/alerts.js +2 -2
  92. package/dist/board.js +2 -2
  93. package/dist/cdn/svgrid-enterprise.svelte-external.js +19046 -5428
  94. package/dist/designer/assets/GridMenus-BlsvL_aU.js +8 -0
  95. package/dist/designer/assets/SvChartMenu-BYO9cYdy.css +1 -0
  96. package/dist/designer/assets/SvChartMenu-D9hEeHar.js +1 -0
  97. package/dist/designer/assets/SvDateRangeInput-BBRzrQeg.js +1 -0
  98. package/dist/designer/assets/SvDateRangeInput-C0iCCZ8o.css +1 -0
  99. package/dist/designer/assets/SvDateTimePicker-DARvKUL2.js +1 -0
  100. package/dist/designer/assets/SvDateTimePicker-DPv09DIT.css +1 -0
  101. package/dist/designer/assets/{preload-helper-CG0n22O7.js → SvGrid.helpers-BgT-gnCo.js} +1 -1
  102. package/dist/designer/assets/SvGridCellEditor-C7uQXYds.js +4 -0
  103. package/dist/designer/assets/SvGridChart-B8j1SSy3.js +3 -0
  104. package/dist/designer/assets/SvGridChart-BgLuPO7I.css +1 -0
  105. package/dist/designer/assets/SvGridChartBuilder-08YWWeqY.css +1 -0
  106. package/dist/designer/assets/SvGridChartBuilder-kvyIU_Rl.js +1 -0
  107. package/dist/designer/assets/SvGridChartPanel-4Pc8y5yO.css +1 -0
  108. package/dist/designer/assets/SvGridChartPanel-pT6ITc2P.js +4 -0
  109. package/dist/designer/assets/SvGridChartView-7msfnWZT.css +1 -0
  110. package/dist/designer/assets/SvGridChartView-bg281UA1.js +1 -0
  111. package/dist/designer/assets/SvGridDropdown-Bb3HH2Aj.js +1 -0
  112. package/dist/designer/assets/SvGridDropdown-CcidB3rm.css +1 -0
  113. package/dist/designer/assets/SvListBox-BDS66lmZ.js +110 -0
  114. package/dist/designer/assets/SvListBox-BXfyqy4a.css +1 -0
  115. package/dist/designer/assets/SvMenuList-DJ47ba1h.css +1 -0
  116. package/dist/designer/assets/SvMenuList-Dgq2D5VN.js +1 -0
  117. package/dist/designer/assets/SvTabs-DKmwb5p1.css +1 -0
  118. package/dist/designer/assets/SvTabs-UFCMTHXS.js +1 -0
  119. package/dist/designer/assets/cell-formatting-wr5wm7US.js +1 -0
  120. package/dist/designer/assets/chart-cIPUsbM5.js +1 -0
  121. package/dist/designer/assets/chart-export-B6bkQ3X8.js +11 -0
  122. package/dist/designer/assets/chart-export-pdf-BbA28-gt.js +11 -0
  123. package/dist/designer/assets/chart-panel-messages-DtTxpBUU.js +1 -0
  124. package/dist/designer/assets/chart-summary-Czu_5epb.js +1 -0
  125. package/dist/designer/assets/chart-trend-D5I5VEe8.js +1 -0
  126. package/dist/designer/assets/chart-validate-BMqM-qkF.js +1 -0
  127. package/dist/designer/assets/column-resize-BYDzR1Ve.js +1 -0
  128. package/dist/designer/assets/date-format-B8JbEtIG.js +1 -0
  129. package/dist/designer/assets/date-format-DKS-z0xS.css +1 -0
  130. package/dist/designer/assets/editor-contract-Bpoa7A5g.js +1 -0
  131. package/dist/designer/assets/export-format-D-eP4b2l.js +6 -0
  132. package/dist/designer/assets/focus-trap-CAreAhlU.js +1 -0
  133. package/dist/designer/assets/index-CMFgAoVV.css +1 -0
  134. package/dist/designer/assets/{index-zUBix-JQ.js → index-DwU7CAMm.js} +2985 -141
  135. package/dist/designer/assets/list-option-BP_dWS66.js +1 -0
  136. package/dist/designer/assets/preload-helper-HclGiUj8.js +1 -0
  137. package/dist/designer/assets/row-resize-DLktcqUK.js +1 -0
  138. package/dist/designer/assets/src-CITpcm6b.js +1 -0
  139. package/dist/designer/assets/src-EdI3Xqds.js +58 -0
  140. package/dist/designer/index.html +32 -23
  141. package/dist/export-conditional.d.ts +1 -1
  142. package/dist/export-pdf.d.ts +89 -0
  143. package/dist/export-pdf.js +130 -0
  144. package/dist/export-print.js +19 -19
  145. package/dist/export-xls.d.ts +2 -2
  146. package/dist/export.d.ts +2 -2
  147. package/dist/export.js +23 -16
  148. package/dist/expressions/builder-tree.d.ts +2 -2
  149. package/dist/expressions/builder-tree.js +1 -1
  150. package/dist/expressions/compile.d.ts +1 -1
  151. package/dist/import.js +1 -1
  152. package/dist/index.d.ts +116 -53
  153. package/dist/index.js +117 -50
  154. package/dist/install.d.ts +4 -4
  155. package/dist/install.js +16 -11
  156. package/dist/license.d.ts +3 -2
  157. package/dist/license.js +9 -4
  158. package/dist/node/studio.js +187 -116
  159. package/dist/pivot-chart.d.ts +26 -2
  160. package/dist/pivot-chart.js +25 -69
  161. package/dist/pivot-designer.d.ts +1 -1
  162. package/dist/pivot-enable.js +3 -3
  163. package/dist/print.d.ts +1 -1
  164. package/dist/print.js +4 -4
  165. package/dist/scheduler-config.d.ts +5 -5
  166. package/dist/scheduler-freebusy.d.ts +1 -1
  167. package/dist/scheduler-freebusy.js +1 -1
  168. package/dist/scheduler.js +2 -2
  169. package/dist/schema-designer.d.ts +1 -1
  170. package/dist/schema-designer.js +2 -2
  171. package/dist/selection-bar.js +2 -2
  172. package/dist/sheet/address.d.ts +43 -0
  173. package/dist/sheet/address.js +81 -0
  174. package/dist/sheet/ast.d.ts +94 -0
  175. package/dist/sheet/ast.js +56 -0
  176. package/dist/sheet/auto-filter.d.ts +66 -0
  177. package/dist/sheet/auto-filter.js +147 -0
  178. package/dist/sheet/autocomplete.d.ts +42 -0
  179. package/dist/sheet/autocomplete.js +162 -0
  180. package/dist/sheet/coerce.d.ts +31 -0
  181. package/dist/sheet/coerce.js +122 -0
  182. package/dist/sheet/commands.d.ts +89 -0
  183. package/dist/sheet/commands.js +230 -0
  184. package/dist/sheet/comments.d.ts +29 -0
  185. package/dist/sheet/comments.js +73 -0
  186. package/dist/sheet/conditional-formats.d.ts +144 -0
  187. package/dist/sheet/conditional-formats.js +316 -0
  188. package/dist/sheet/deps.d.ts +60 -0
  189. package/dist/sheet/deps.js +199 -0
  190. package/dist/sheet/document.d.ts +150 -0
  191. package/dist/sheet/document.js +264 -0
  192. package/dist/sheet/edit-keys.d.ts +14 -0
  193. package/dist/sheet/edit-keys.js +40 -0
  194. package/dist/sheet/entry.d.ts +31 -0
  195. package/dist/sheet/entry.js +76 -0
  196. package/dist/sheet/evaluate.d.ts +61 -0
  197. package/dist/sheet/evaluate.js +337 -0
  198. package/dist/sheet/find-replace.d.ts +62 -0
  199. package/dist/sheet/find-replace.js +152 -0
  200. package/dist/sheet/format-store.d.ts +99 -0
  201. package/dist/sheet/format-store.js +233 -0
  202. package/dist/sheet/freeze.d.ts +54 -0
  203. package/dist/sheet/freeze.js +58 -0
  204. package/dist/sheet/function-catalog.d.ts +10 -0
  205. package/dist/sheet/function-catalog.js +94 -0
  206. package/dist/sheet/functions.d.ts +28 -0
  207. package/dist/sheet/functions.js +362 -0
  208. package/dist/sheet/goal-seek.d.ts +73 -0
  209. package/dist/sheet/goal-seek.js +153 -0
  210. package/dist/sheet/index.d.ts +53 -0
  211. package/dist/sheet/index.js +53 -0
  212. package/dist/sheet/merges.d.ts +71 -0
  213. package/dist/sheet/merges.js +137 -0
  214. package/dist/sheet/names.d.ts +31 -0
  215. package/dist/sheet/names.js +87 -0
  216. package/dist/sheet/navigate.d.ts +52 -0
  217. package/dist/sheet/navigate.js +135 -0
  218. package/dist/sheet/number-format.d.ts +63 -0
  219. package/dist/sheet/number-format.js +508 -0
  220. package/dist/sheet/palette.d.ts +19 -0
  221. package/dist/sheet/palette.js +84 -0
  222. package/dist/sheet/parse.d.ts +13 -0
  223. package/dist/sheet/parse.js +108 -0
  224. package/dist/sheet/paste-special.d.ts +164 -0
  225. package/dist/sheet/paste-special.js +592 -0
  226. package/dist/sheet/protection.d.ts +21 -0
  227. package/dist/sheet/protection.js +48 -0
  228. package/dist/sheet/rects.d.ts +45 -0
  229. package/dist/sheet/rects.js +112 -0
  230. package/dist/sheet/refs.d.ts +82 -0
  231. package/dist/sheet/refs.js +266 -0
  232. package/dist/sheet/ribbon-icons.d.ts +31 -0
  233. package/dist/sheet/ribbon-icons.js +350 -0
  234. package/dist/sheet/ribbon.d.ts +173 -0
  235. package/dist/sheet/ribbon.js +753 -0
  236. package/dist/sheet/shortcuts.d.ts +164 -0
  237. package/dist/sheet/shortcuts.js +623 -0
  238. package/dist/sheet/structure.d.ts +87 -0
  239. package/dist/sheet/structure.js +166 -0
  240. package/dist/sheet/tables.d.ts +70 -0
  241. package/dist/sheet/tables.js +147 -0
  242. package/dist/sheet/tokenize.d.ts +54 -0
  243. package/dist/sheet/tokenize.js +304 -0
  244. package/dist/sheet/transforms.d.ts +76 -0
  245. package/dist/sheet/transforms.js +154 -0
  246. package/dist/sheet/validation.d.ts +92 -0
  247. package/dist/sheet/validation.js +241 -0
  248. package/dist/sheet/workbook.d.ts +72 -0
  249. package/dist/sheet/workbook.js +409 -0
  250. package/dist/sheet-enable.d.ts +6 -0
  251. package/dist/sheet-enable.js +46 -0
  252. package/dist/sources/aggregate.d.ts +1 -1
  253. package/dist/sources/auth-supabase.js +1 -1
  254. package/dist/sources/dashboard.d.ts +2 -2
  255. package/dist/sources/dashboard.js +1 -1
  256. package/dist/sources/index.d.ts +13 -13
  257. package/dist/sources/index.js +13 -13
  258. package/dist/sources/realtime-supabase.js +1 -1
  259. package/dist/sources/relation-lookup.d.ts +1 -1
  260. package/dist/sources/relation-lookup.js +1 -1
  261. package/dist/sources/rest-adapters.d.ts +1 -1
  262. package/dist/sources/rest-adapters.js +1 -1
  263. package/dist/sources/rest.d.ts +2 -2
  264. package/dist/sources/rest.js +3 -3
  265. package/dist/sources/supabase.d.ts +2 -2
  266. package/dist/sources/supabase.js +3 -3
  267. package/dist/sources/with-entity-rules.d.ts +1 -1
  268. package/dist/sources/with-entity-rules.js +1 -1
  269. package/dist/sources/with-relation-labels.d.ts +1 -1
  270. package/dist/studio/emit-project.js +2161 -2127
  271. package/dist/studio/emit-schema.js +369 -369
  272. package/dist/studio/index.d.ts +1 -1
  273. package/dist/studio/index.js +1 -1
  274. package/dist/studio/introspect-db.js +45 -45
  275. package/dist/studio/project.d.ts +10 -0
  276. package/dist/studio/samples/crm.js +6 -6
  277. package/dist/studio/samples/fleet.js +7 -7
  278. package/dist/studio/samples/insurance.js +9 -9
  279. package/dist/studio/samples/inventory.js +12 -12
  280. package/dist/studio/samples/projects.js +5 -5
  281. package/dist/studio/samples/support.js +8 -8
  282. package/dist/studio/scaffold-app.js +104 -104
  283. package/dist/studio/scaffold.js +151 -151
  284. package/dist/studio/ui-components.d.ts +7 -0
  285. package/dist/studio/ui-components.generated.js +63 -1
  286. package/dist/studio/ui-components.js +35 -0
  287. package/dist/sveltekit/in-memory.d.ts +3 -3
  288. package/dist/sveltekit/in-memory.js +4 -4
  289. package/dist/sveltekit/index.d.ts +6 -6
  290. package/dist/sveltekit/index.js +5 -5
  291. package/dist/sveltekit/query-plan.d.ts +1 -1
  292. package/dist/sveltekit/sql-source.d.ts +4 -4
  293. package/dist/sveltekit/sql-source.js +4 -4
  294. package/dist/sveltekit/sql.d.ts +1 -1
  295. package/dist/sveltekit/transport.d.ts +2 -2
  296. package/dist/sveltekit/transport.js +1 -1
  297. package/dist/upgrade-prompt.js +25 -25
  298. package/dist/version.d.ts +1 -1
  299. package/dist/version.js +1 -1
  300. package/package.json +12 -4
  301. package/dist/designer/assets/GridMenus-Cgilexpk.js +0 -8
  302. package/dist/designer/assets/SvDateRangeInput-BV5OXYQ2.css +0 -1
  303. package/dist/designer/assets/SvDateRangeInput-CVn-yqrQ.js +0 -1
  304. package/dist/designer/assets/SvDateTimePicker-BdioTrJQ.js +0 -1
  305. package/dist/designer/assets/SvDateTimePicker-CY8TK4BT.css +0 -1
  306. package/dist/designer/assets/SvField-BF-9TJSd.css +0 -1
  307. package/dist/designer/assets/SvField-BsqiA5me.js +0 -1
  308. package/dist/designer/assets/SvGridCellEditor-dLY7_kk9.js +0 -2
  309. package/dist/designer/assets/SvGridChart-BEDNTdc5.js +0 -2
  310. package/dist/designer/assets/SvGridChart-Vu4Nvjiv.css +0 -1
  311. package/dist/designer/assets/SvGridChartPanel-x3lzSxuf.js +0 -10
  312. package/dist/designer/assets/SvGridChartView-CAKhPELi.js +0 -1
  313. package/dist/designer/assets/SvGridChartView-DX9HfkBR.css +0 -1
  314. package/dist/designer/assets/SvGridDropdown-CAYG7Y-O.js +0 -1
  315. package/dist/designer/assets/SvGridDropdown-Crwb2P3Q.css +0 -1
  316. package/dist/designer/assets/SvListBox-CVYQcLX9.js +0 -1
  317. package/dist/designer/assets/SvListBox-DgN8kvjt.css +0 -1
  318. package/dist/designer/assets/chart-DHEgt4lN.js +0 -1
  319. package/dist/designer/assets/column-resize-C_inr5zT.js +0 -1
  320. package/dist/designer/assets/date-format-4DJvNtG2.js +0 -1
  321. package/dist/designer/assets/date-format-Dzy4mTGh.css +0 -1
  322. package/dist/designer/assets/easing-zaypKrr0.js +0 -3
  323. package/dist/designer/assets/export-format-DwjuTfrQ.js +0 -6
  324. package/dist/designer/assets/index-Cp7fDJ3b.css +0 -1
  325. package/dist/designer/assets/js-scroller.svelte-Bhxqb3-c.js +0 -110
  326. package/dist/designer/assets/row-resize-B0FWHcNr.js +0 -1
  327. package/dist/designer/assets/src-B7DNcoQK.css +0 -1
  328. package/dist/designer/assets/src-BoYyigAC.js +0 -2896
  329. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +0 -1
  330. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +0 -178
  331. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js +0 -3875
  332. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js.map +0 -1
  333. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js +0 -21
  334. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js.map +0 -1
  335. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js +0 -1938
  336. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js.map +0 -1
  337. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js +0 -205
  338. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js.map +0 -1
  339. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js +0 -18
  340. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js.map +0 -1
  341. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js +0 -264
  342. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js.map +0 -1
  343. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js +0 -697
  344. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js.map +0 -1
  345. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +0 -3
  346. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +0 -134
  347. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +0 -1
  348. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +0 -55
  349. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +0 -1
  350. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +0 -2
  351. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +0 -46843
  352. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +0 -1
  353. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +0 -237
  354. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +0 -1
  355. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +0 -2
  356. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +0 -4
  357. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +0 -1
  358. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +0 -7
  359. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +0 -4
  360. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +0 -1
  361. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +0 -5
  362. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +0 -1
  363. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +0 -5
  364. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +0 -1
  365. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +0 -5
  366. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +0 -1
  367. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +0 -6
  368. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +0 -83
  369. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +0 -1
  370. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +0 -559
  371. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +0 -1
  372. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +0 -24
  373. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +0 -1
  374. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +0 -122
  375. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +0 -1
  376. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +0 -2
  377. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +0 -89
  378. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +0 -1
  379. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +0 -241
  380. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +0 -1
  381. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js +0 -4938
  382. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js.map +0 -1
  383. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js +0 -495
  384. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js.map +0 -1
  385. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js +0 -190
  386. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js.map +0 -1
  387. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js +0 -8
  388. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js.map +0 -1
  389. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js +0 -138
  390. package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js.map +0 -1
@@ -236,14 +236,14 @@ function schemasModule(entries) {
236
236
  return {
237
237
  path: 'src/lib/schemas.ts',
238
238
  description: 'One EntitySchema per entity, generated from your schema. Add computed fields, hooks, or validators here.',
239
- contents: `/**
240
- * Generated from your schema by \`npm create @svgrid/studio --from\`. Each
241
- * EntitySchema drives the grid columns, the edit form, and validation. Add a
242
- * field here (and to its type) and it shows up in the grid and the form.
243
- */
244
- import type { EntitySchema } from '@svgrid/enterprise'
245
-
246
- ${body}
239
+ contents: `/**
240
+ * Generated from your schema by \`npm create @svgrid/studio --from\`. Each
241
+ * EntitySchema drives the grid columns, the edit form, and validation. Add a
242
+ * field here (and to its type) and it shows up in the grid and the form.
243
+ */
244
+ import type { EntitySchema } from '@svgrid/enterprise'
245
+
246
+ ${body}
247
247
  `,
248
248
  };
249
249
  }
@@ -365,22 +365,22 @@ function sqlRouteFile(schema, table, dialect, feat = {}) {
365
365
  return {
366
366
  path: `src/routes/api/${n.route}/+server.ts`,
367
367
  description: `Connected API route for ${n.label} (SQL via DATABASE_URL). Runs server-side.`,
368
- contents: `${driver.imports}
369
- import { env } from '$env/dynamic/private'
370
- import { createKitHandlers, createSqlDataSource } from '@svgrid/enterprise'
371
- import { ${n.schemaVar}, type ${n.type} } from '$lib/schemas'${accessImport}${auditImport}${tenantImport}
372
-
373
- ${driver.setup}
374
-
375
- const source = createSqlDataSource<${n.type}>({
376
- schema: ${n.schemaVar},
377
- table: ${JSON.stringify(table)},${feat.dbSchema ? `\n dbSchema: ${JSON.stringify(feat.dbSchema)},` : ''}${dialectLiteral ? `\n dialect: ${dialectLiteral},` : ''}
378
- execute: async (text, params) => {
379
- ${driver.exec}
380
- },
381
- })
382
-
383
- ${handlers}
368
+ contents: `${driver.imports}
369
+ import { env } from '$env/dynamic/private'
370
+ import { createKitHandlers, createSqlDataSource } from '@svgrid/enterprise'
371
+ import { ${n.schemaVar}, type ${n.type} } from '$lib/schemas'${accessImport}${auditImport}${tenantImport}
372
+
373
+ ${driver.setup}
374
+
375
+ const source = createSqlDataSource<${n.type}>({
376
+ schema: ${n.schemaVar},
377
+ table: ${JSON.stringify(table)},${feat.dbSchema ? `\n dbSchema: ${JSON.stringify(feat.dbSchema)},` : ''}${dialectLiteral ? `\n dialect: ${dialectLiteral},` : ''}
378
+ execute: async (text, params) => {
379
+ ${driver.exec}
380
+ },
381
+ })
382
+
383
+ ${handlers}
384
384
  `,
385
385
  };
386
386
  }
@@ -405,27 +405,27 @@ function pgliteBootstrap(tables, seed) {
405
405
  const rows = curated ?? seed.get(schema.name) ?? [];
406
406
  seeds.push(` await _pgSeed(${JSON.stringify(table)}, ${JSON.stringify(rows)}, ${JSON.stringify(schema.fields.map((f) => f.field))})`);
407
407
  }
408
- return `// Embedded Postgres (PGlite), persisted in the browser (IndexedDB) - a real,
409
- // persistent database with ZERO backend setup. To go to production, bind these
410
- // entities to a hosted SQL source (same schema) instead.
411
- const pg = new PGlite('idb://svgrid-studio')
412
- const pgReady = (async () => {
413
- await pg.exec(\`
414
- ${ddls.join('\n ')}
415
- \`)
416
- ${seeds.join('\n')}
417
- })()
418
- async function _pgSeed(table: string, rows: Record<string, unknown>[], cols: string[]): Promise<void> {
419
- if (!rows.length) return
420
- const existing = await pg.query(\`select count(*)::int as n from "\${table}"\`)
421
- if (((existing.rows[0] as { n?: number } | undefined)?.n ?? 0) > 0) return // already seeded
422
- for (const row of rows) {
423
- await pg.query(
424
- \`insert into "\${table}" (\${cols.map((c) => '"' + c + '"').join(', ')}) values (\${cols.map((_, i) => '$' + (i + 1)).join(', ')})\`,
425
- cols.map((c) => { const v = row[c]; return v != null && typeof v === 'object' ? JSON.stringify(v) : (v ?? null) }),
426
- )
427
- }
428
- }
408
+ return `// Embedded Postgres (PGlite), persisted in the browser (IndexedDB) - a real,
409
+ // persistent database with ZERO backend setup. To go to production, bind these
410
+ // entities to a hosted SQL source (same schema) instead.
411
+ const pg = new PGlite('idb://svgrid-studio')
412
+ const pgReady = (async () => {
413
+ await pg.exec(\`
414
+ ${ddls.join('\n ')}
415
+ \`)
416
+ ${seeds.join('\n')}
417
+ })()
418
+ async function _pgSeed(table: string, rows: Record<string, unknown>[], cols: string[]): Promise<void> {
419
+ if (!rows.length) return
420
+ const existing = await pg.query(\`select count(*)::int as n from "\${table}"\`)
421
+ if (((existing.rows[0] as { n?: number } | undefined)?.n ?? 0) > 0) return // already seeded
422
+ for (const row of rows) {
423
+ await pg.query(
424
+ \`insert into "\${table}" (\${cols.map((c) => '"' + c + '"').join(', ')}) values (\${cols.map((_, i) => '$' + (i + 1)).join(', ')})\`,
425
+ cols.map((c) => { const v = row[c]; return v != null && typeof v === 'object' ? JSON.stringify(v) : (v ?? null) }),
426
+ )
427
+ }
428
+ }
429
429
  `;
430
430
  }
431
431
  /** Normalize a source dialect to a DDL family (supabase = postgres, turso = sqlite). */
@@ -579,22 +579,22 @@ function dataModule(entries, seed, sources, supabaseConn) {
579
579
  const file = {
580
580
  path: 'src/lib/data.ts',
581
581
  description: 'Data sources per entity (in-memory / REST / SQL / Supabase / PGlite) + a searchable lookup per relation.',
582
- contents: `/**
583
- * Data sources. Each entity binds to its own backend - in-memory (seeded), a
584
- * REST endpoint, a SQL table, Supabase, or embedded Postgres (PGlite). In-memory
585
- * + PGlite run with no setup; SQL / Supabase read their connection from \`./connections\`.
586
- */
587
- import { ${[...entImports].join(', ')} } from '@svgrid/enterprise'
588
- import { ${schemaImports}, ${typeImports} } from './schemas'${connImportLine}${pgliteImportLine}
589
-
590
- ${pgliteBoot}${stores}
591
- ${lookups.length ? `\n// Searchable pickers for relation (foreign-key) fields.\n${lookups.join('\n')}\n` : ''}
592
- // Grids show the related label (not the raw id) via withRelationLabels.
593
- ${sourceLines}
594
-
595
- // New-row ids continue after the seeded rows.
596
- let seq = ${SEED_COUNT}
597
- export const nextId = (prefix: string) => \`\${prefix}\${++seq}\`
582
+ contents: `/**
583
+ * Data sources. Each entity binds to its own backend - in-memory (seeded), a
584
+ * REST endpoint, a SQL table, Supabase, or embedded Postgres (PGlite). In-memory
585
+ * + PGlite run with no setup; SQL / Supabase read their connection from \`./connections\`.
586
+ */
587
+ import { ${[...entImports].join(', ')} } from '@svgrid/enterprise'
588
+ import { ${schemaImports}, ${typeImports} } from './schemas'${connImportLine}${pgliteImportLine}
589
+
590
+ ${pgliteBoot}${stores}
591
+ ${lookups.length ? `\n// Searchable pickers for relation (foreign-key) fields.\n${lookups.join('\n')}\n` : ''}
592
+ // Grids show the related label (not the raw id) via withRelationLabels.
593
+ ${sourceLines}
594
+
595
+ // New-row ids continue after the seeded rows.
596
+ let seq = ${SEED_COUNT}
597
+ export const nextId = (prefix: string) => \`\${prefix}\${++seq}\`
598
598
  `,
599
599
  };
600
600
  return { file, needs };
@@ -610,18 +610,18 @@ function connectionsModule(needs) {
610
610
  return {
611
611
  path: 'src/lib/connections.ts',
612
612
  description: 'Supabase client for Supabase-bound entities (reads PUBLIC_SUPABASE_* env vars).',
613
- contents: `import { createClient } from '@supabase/supabase-js'
614
- import { env } from '$env/dynamic/public'
615
- import type { SupabaseClientLike } from '@svgrid/enterprise'
616
-
617
- /**
618
- * Supabase client. The anon key is public (browser-safe) - access is protected by
619
- * your Row Level Security policies. Requires \`@supabase/supabase-js\`.
620
- */
621
- ${hint}export const supabaseClient = createClient(
622
- env.PUBLIC_SUPABASE_URL ?? '',
623
- env.PUBLIC_SUPABASE_ANON_KEY ?? '',
624
- ) as unknown as SupabaseClientLike
613
+ contents: `import { createClient } from '@supabase/supabase-js'
614
+ import { env } from '$env/dynamic/public'
615
+ import type { SupabaseClientLike } from '@svgrid/enterprise'
616
+
617
+ /**
618
+ * Supabase client. The anon key is public (browser-safe) - access is protected by
619
+ * your Row Level Security policies. Requires \`@supabase/supabase-js\`.
620
+ */
621
+ ${hint}export const supabaseClient = createClient(
622
+ env.PUBLIC_SUPABASE_URL ?? '',
623
+ env.PUBLIC_SUPABASE_ANON_KEY ?? '',
624
+ ) as unknown as SupabaseClientLike
625
625
  `,
626
626
  };
627
627
  }
@@ -643,14 +643,14 @@ export function entityScreenPage(schema, route, title) {
643
643
  return {
644
644
  path: `src/routes/${r}/+page.svelte`,
645
645
  description: `CRUD screen for ${t}.`,
646
- contents: `<script lang="ts">
647
- import EntityScreen from '../lib/EntityScreen.svelte'
648
- import { ${n.schemaVar} } from '$lib/schemas'
649
- import { ${dataImports} } from '$lib/data'
650
- </script>
651
-
652
- <h1 class="st__title">${t}</h1>
653
- <EntityScreen schema={${n.schemaVar}} source={${n.sourceVar}}${lookupsProp} newId={() => nextId('${n.idPrefix}')} />
646
+ contents: `<script lang="ts">
647
+ import EntityScreen from '../lib/EntityScreen.svelte'
648
+ import { ${n.schemaVar} } from '$lib/schemas'
649
+ import { ${dataImports} } from '$lib/data'
650
+ </script>
651
+
652
+ <h1 class="st__title">${t}</h1>
653
+ <EntityScreen schema={${n.schemaVar}} source={${n.sourceVar}}${lookupsProp} newId={() => nextId('${n.idPrefix}')} />
654
654
  `,
655
655
  };
656
656
  }
@@ -681,24 +681,24 @@ export function layoutFile(nav, opts = {}) {
681
681
  ? `{#if !item.id || canScreen($currentRole, item.id)}${anchor}{/if}`
682
682
  : anchor;
683
683
  const localeSwitcher = opts.i18n
684
- ? `\n <select class="sv-app__locale" aria-label="Language" onchange={(e) => currentLocale.set(e.currentTarget.value as typeof $currentLocale)}>
685
- {#each locales as loc (loc)}<option value={loc} selected={loc === $currentLocale}>{loc}</option>{/each}
684
+ ? `\n <select class="sv-app__locale" aria-label="Language" onchange={(e) => currentLocale.set(e.currentTarget.value as typeof $currentLocale)}>
685
+ {#each locales as loc (loc)}<option value={loc} selected={loc === $currentLocale}>{loc}</option>{/each}
686
686
  </select>`
687
687
  : '';
688
688
  const toolbarOn = shell.toolbar !== false && nav.length > 0;
689
689
  // Built-in light/dark switcher (a sun/moon button). Lives in the toolbar's tools
690
690
  // cluster when the toolbar is on, else trails the nav links. Only rendered when
691
691
  // both token sets are available (i.e. from the full Studio project emitter).
692
- const themeToggleBtn = `<button type="button" class="sv-app__tool sv-app__theme" aria-label="Toggle color theme" title="Toggle light / dark" onclick={toggleTheme}>
693
- {#if theme === 'dark'}<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>{:else}<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>{/if}
692
+ const themeToggleBtn = `<button type="button" class="sv-app__tool sv-app__theme" aria-label="Toggle color theme" title="Toggle light / dark" onclick={toggleTheme}>
693
+ {#if theme === 'dark'}<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>{:else}<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>{/if}
694
694
  </button>`;
695
695
  // When there's no toolbar, the toggle trails the nav in its own inline slot.
696
696
  const navToggleSlot = hasSwitch && !toolbarOn ? `\n <div class="sv-app__navtools">${themeToggleBtn}</div>` : '';
697
697
  // Clicking any nav link closes the mobile drawer.
698
- const linksMarkup = `<nav class="sv-app__links" onclick={() => (navOpen = false)}>
699
- {#each nav as item (item.href)}
700
- ${linkGate}
701
- {/each}
698
+ const linksMarkup = `<nav class="sv-app__links" onclick={() => (navOpen = false)}>
699
+ {#each nav as item (item.href)}
700
+ ${linkGate}
701
+ {/each}
702
702
  </nav>${localeSwitcher}${navToggleSlot}`;
703
703
  const footMarkup = footer ? `\n <span class="sv-app__foot">{footer}</span>` : '';
704
704
  const footConst = footer ? `\n const footer = ${JSON.stringify(footer)}` : '';
@@ -706,19 +706,19 @@ export function layoutFile(nav, opts = {}) {
706
706
  // off-canvas drawer when collapsed or on tablet/phone (<= 1024px). The collapse
707
707
  // choice persists; tablet/phone start collapsed. top-nav and bottom-nav are
708
708
  // always-visible bars with no collapse/drawer state.
709
- const sideState = style !== 'sidebar' ? '' : `
710
- let collapsed = $state(false)
711
- let narrow = $state(false)
712
- const drawer = $derived(collapsed || narrow)
713
- function toggleNav() { collapsed = !collapsed; try { localStorage.setItem('svapp:nav', collapsed ? 'c' : 'e') } catch (_) { /* storage blocked */ } }
714
- $effect(() => {
715
- // Tablet / phone (<= 1024px) collapse to a drawer by default.
716
- const mq = window.matchMedia('(max-width: 1024px)')
717
- const sync = () => (narrow = mq.matches)
718
- sync()
719
- try { const s = localStorage.getItem('svapp:nav'); if (s) collapsed = s === 'c' } catch (_) { /* storage blocked */ }
720
- mq.addEventListener('change', sync)
721
- return () => mq.removeEventListener('change', sync)
709
+ const sideState = style !== 'sidebar' ? '' : `
710
+ let collapsed = $state(false)
711
+ let narrow = $state(false)
712
+ const drawer = $derived(collapsed || narrow)
713
+ function toggleNav() { collapsed = !collapsed; try { localStorage.setItem('svapp:nav', collapsed ? 'c' : 'e') } catch (_) { /* storage blocked */ } }
714
+ $effect(() => {
715
+ // Tablet / phone (<= 1024px) collapse to a drawer by default.
716
+ const mq = window.matchMedia('(max-width: 1024px)')
717
+ const sync = () => (narrow = mq.matches)
718
+ sync()
719
+ try { const s = localStorage.getItem('svapp:nav'); if (s) collapsed = s === 'c' } catch (_) { /* storage blocked */ }
720
+ mq.addEventListener('change', sync)
721
+ return () => mq.removeEventListener('change', sync)
722
722
  })`;
723
723
  // Brand: a company logo image (data URL / URL) when set, else the app name.
724
724
  const logoConst = shell.logo ? `\n const logo = ${JSON.stringify(shell.logo)}` : '';
@@ -729,15 +729,15 @@ export function layoutFile(nav, opts = {}) {
729
729
  // Collapsed bar (tablet / phone, or a collapsed desktop sidebar): a hamburger +
730
730
  // brand. The nav slides in as an off-canvas drawer. On desktop the "dock" button
731
731
  // pins the sidebar back open.
732
- const mobileBar = `<header class="sv-app__mobilebar">
733
- <button type="button" class="sv-app__burger" aria-label="Toggle navigation" aria-expanded={navOpen} onclick={() => (navOpen = !navOpen)}>
734
- <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
735
- </button>
736
- ${brandLink}
737
- {#if !narrow}<button type="button" class="sv-app__dock" aria-label="Dock sidebar" title="Dock sidebar" onclick={toggleNav}>
738
- <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6-6 6"/></svg>
739
- </button>{/if}
740
- </header>
732
+ const mobileBar = `<header class="sv-app__mobilebar">
733
+ <button type="button" class="sv-app__burger" aria-label="Toggle navigation" aria-expanded={navOpen} onclick={() => (navOpen = !navOpen)}>
734
+ <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
735
+ </button>
736
+ ${brandLink}
737
+ {#if !narrow}<button type="button" class="sv-app__dock" aria-label="Dock sidebar" title="Dock sidebar" onclick={toggleNav}>
738
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6-6 6"/></svg>
739
+ </button>{/if}
740
+ </header>
741
741
  {#if navOpen}<button type="button" class="sv-app__backdrop" aria-label="Close navigation" onclick={() => (navOpen = false)}></button>{/if}`;
742
742
  // App-chrome toolbar (docked at the top of the content area for both layouts):
743
743
  // a functional quick-search over the app's screens + an account cluster. Reads as
@@ -746,274 +746,274 @@ export function layoutFile(nav, opts = {}) {
746
746
  const brandSlug = brand.toLowerCase().replace(/[^a-z0-9]+/g, '') || 'app';
747
747
  const resultLabel = opts.i18n ? `{r.id ? $t('nav.' + r.id, r.label) : r.label}` : '{r.label}';
748
748
  const toolbarMarkup = toolbarOn
749
- ? `<div class="sv-app__toolbar">
750
- <div class="sv-app__search">
751
- <svg class="sv-app__search-ic" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg>
752
- <input class="sv-app__search-in" type="search" placeholder="Search {brand}…" bind:value={q} bind:this={searchEl} aria-label="Search screens" />
753
- <kbd class="sv-app__kbd" aria-hidden="true">⌘K</kbd>
754
- {#if results.length}
755
- <div class="sv-app__results">
756
- {#each results as r (r.href)}
757
- <a class="sv-app__result" href={r.href} onclick={() => (q = '')}>${resultLabel}</a>
758
- {/each}
759
- </div>
760
- {/if}
761
- </div>
762
- <div class="sv-app__tools">
763
- ${hasSwitch ? themeToggleBtn : ''}
764
- <div class="sv-app__pop">
765
- <button type="button" class="sv-app__tool" aria-label="Notifications" aria-expanded={bellOpen} onclick={() => { bellOpen = !bellOpen; menuOpen = false }}>
766
- <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
767
- <span class="sv-app__dot"></span>
768
- </button>
769
- {#if bellOpen}
770
- <div class="sv-app__menu sv-app__menu--notif" role="menu">
771
- <div class="sv-app__menu-head">Notifications</div>
772
- {#each notifications as n (n.title)}
773
- <div class="sv-app__notif" role="menuitem"><span class="sv-app__notif-dot" style:background={n.color}></span><div class="sv-app__notif-body"><strong>{n.title}</strong><span>{n.time}</span></div></div>
774
- {/each}
775
- <button type="button" class="sv-app__menu-foot" onclick={() => (bellOpen = false)}>Mark all as read</button>
776
- </div>
777
- {/if}
778
- </div>
779
- <div class="sv-app__pop">
780
- <button type="button" class="sv-app__avatar" title={brand} aria-label="Account" aria-expanded={menuOpen} onclick={() => { menuOpen = !menuOpen; bellOpen = false }}>{initials}</button>
781
- {#if menuOpen}
782
- <div class="sv-app__menu sv-app__menu--acct" role="menu">
783
- <div class="sv-app__acct-head"><span class="sv-app__avatar sv-app__avatar--lg" aria-hidden="true">{initials}</span><div class="sv-app__acct-id"><strong>${opts.auth ? '{data?.user?.name ?? brand}' : '{brand}'}</strong><span>${opts.auth ? '{data?.user?.email ?? acctEmail}' : '{acctEmail}'}</span></div></div>
784
- <a class="sv-app__menu-item" href="/" role="menuitem" onclick={() => (menuOpen = false)}>Dashboard</a>
749
+ ? `<div class="sv-app__toolbar">
750
+ <div class="sv-app__search">
751
+ <svg class="sv-app__search-ic" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg>
752
+ <input class="sv-app__search-in" type="search" placeholder="Search {brand}…" bind:value={q} bind:this={searchEl} aria-label="Search screens" />
753
+ <kbd class="sv-app__kbd" aria-hidden="true">⌘K</kbd>
754
+ {#if results.length}
755
+ <div class="sv-app__results">
756
+ {#each results as r (r.href)}
757
+ <a class="sv-app__result" href={r.href} onclick={() => (q = '')}>${resultLabel}</a>
758
+ {/each}
759
+ </div>
760
+ {/if}
761
+ </div>
762
+ <div class="sv-app__tools">
763
+ ${hasSwitch ? themeToggleBtn : ''}
764
+ <div class="sv-app__pop">
765
+ <button type="button" class="sv-app__tool" aria-label="Notifications" aria-expanded={bellOpen} onclick={() => { bellOpen = !bellOpen; menuOpen = false }}>
766
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
767
+ <span class="sv-app__dot"></span>
768
+ </button>
769
+ {#if bellOpen}
770
+ <div class="sv-app__menu sv-app__menu--notif" role="menu">
771
+ <div class="sv-app__menu-head">Notifications</div>
772
+ {#each notifications as n (n.title)}
773
+ <div class="sv-app__notif" role="menuitem"><span class="sv-app__notif-dot" style:background={n.color}></span><div class="sv-app__notif-body"><strong>{n.title}</strong><span>{n.time}</span></div></div>
774
+ {/each}
775
+ <button type="button" class="sv-app__menu-foot" onclick={() => (bellOpen = false)}>Mark all as read</button>
776
+ </div>
777
+ {/if}
778
+ </div>
779
+ <div class="sv-app__pop">
780
+ <button type="button" class="sv-app__avatar" title={brand} aria-label="Account" aria-expanded={menuOpen} onclick={() => { menuOpen = !menuOpen; bellOpen = false }}>{initials}</button>
781
+ {#if menuOpen}
782
+ <div class="sv-app__menu sv-app__menu--acct" role="menu">
783
+ <div class="sv-app__acct-head"><span class="sv-app__avatar sv-app__avatar--lg" aria-hidden="true">{initials}</span><div class="sv-app__acct-id"><strong>${opts.auth ? '{data?.user?.name ?? brand}' : '{brand}'}</strong><span>${opts.auth ? '{data?.user?.email ?? acctEmail}' : '{acctEmail}'}</span></div></div>
784
+ <a class="sv-app__menu-item" href="/" role="menuitem" onclick={() => (menuOpen = false)}>Dashboard</a>
785
785
  ${opts.authAccount
786
786
  ? `<a class="sv-app__menu-item" href="/account" role="menuitem" onclick={() => (menuOpen = false)}>Account</a>`
787
- : `<button type="button" class="sv-app__menu-item" role="menuitem" onclick={() => (menuOpen = false)}>Profile</button>
788
- <button type="button" class="sv-app__menu-item" role="menuitem" onclick={() => (menuOpen = false)}>Settings</button>`}
787
+ : `<button type="button" class="sv-app__menu-item" role="menuitem" onclick={() => (menuOpen = false)}>Profile</button>
788
+ <button type="button" class="sv-app__menu-item" role="menuitem" onclick={() => (menuOpen = false)}>Settings</button>`}
789
789
  ${opts.auth
790
790
  ? `<form method="POST" action="/logout" class="sv-app__signout"><button type="submit" class="sv-app__menu-item sv-app__menu-item--danger" role="menuitem">Sign out</button></form>`
791
791
  : opts.supabaseAuth
792
792
  ? '' // Supabase Auth: the real sign-out lives on SvAuthGate's own bar, so no placeholder here.
793
- : `<button type="button" class="sv-app__menu-item sv-app__menu-item--danger" role="menuitem" onclick={() => (menuOpen = false)}>Sign out</button>`}
794
- </div>
795
- {/if}
796
- </div>
797
- </div>
798
- {#if bellOpen || menuOpen}<button type="button" class="sv-app__scrim" tabindex="-1" aria-label="Close menus" onclick={() => { bellOpen = false; menuOpen = false }}></button>{/if}
793
+ : `<button type="button" class="sv-app__menu-item sv-app__menu-item--danger" role="menuitem" onclick={() => (menuOpen = false)}>Sign out</button>`}
794
+ </div>
795
+ {/if}
796
+ </div>
797
+ </div>
798
+ {#if bellOpen || menuOpen}<button type="button" class="sv-app__scrim" tabindex="-1" aria-label="Close menus" onclick={() => { bellOpen = false; menuOpen = false }}></button>{/if}
799
799
  </div>\n `
800
800
  : '';
801
801
  // Supabase Auth: gate the whole app behind a client-side sign-in. SvAuthGate shows
802
802
  // the sign-in form when signed out, and the app + a "signed in as … / Sign out" bar
803
803
  // when signed in. Reads the shared client from $lib/connections.
804
804
  const content = opts.supabaseAuth
805
- ? `<SvAuthGate client={supabaseClient as unknown as SupabaseAuthClientLike} title={\`Sign in to \${brand}\`}>
806
- {@render children()}
805
+ ? `<SvAuthGate client={supabaseClient as unknown as SupabaseAuthClientLike} title={\`Sign in to \${brand}\`}>
806
+ {@render children()}
807
807
  </SvAuthGate>`
808
808
  : `{@render children()}`;
809
- const mainMarkup = `<main class="sv-app__main">
810
- ${toolbarMarkup}<div class="sv-app__content">
811
- ${content}
812
- </div>
809
+ const mainMarkup = `<main class="sv-app__main">
810
+ ${toolbarMarkup}<div class="sv-app__content">
811
+ ${content}
812
+ </div>
813
813
  </main>`;
814
814
  const body = style === 'top-nav'
815
- ? `<div class="sv-app sv-app--top${appClsAttr}">
816
- <header class="sv-app__bar">
817
- ${brandLink}
818
- ${linksMarkup}
819
- </header>
820
- ${mainMarkup}${footer ? `\n <footer class="sv-app__footbar">{footer}</footer>` : ''}
815
+ ? `<div class="sv-app sv-app--top${appClsAttr}">
816
+ <header class="sv-app__bar">
817
+ ${brandLink}
818
+ ${linksMarkup}
819
+ </header>
820
+ ${mainMarkup}${footer ? `\n <footer class="sv-app__footbar">{footer}</footer>` : ''}
821
821
  </div>`
822
822
  : style === 'bottom-nav'
823
- ? `<div class="sv-app sv-app--bottom${appClsAttr}">
824
- ${mainMarkup}
825
- <footer class="sv-app__bar sv-app__bar--bottom">
826
- ${brandLink}
827
- ${linksMarkup}
828
- </footer>
823
+ ? `<div class="sv-app sv-app--bottom${appClsAttr}">
824
+ ${mainMarkup}
825
+ <footer class="sv-app__bar sv-app__bar--bottom">
826
+ ${brandLink}
827
+ ${linksMarkup}
828
+ </footer>
829
829
  </div>`
830
- : `<div class="sv-app sv-app--side${right ? ' sv-app--right' : ''}${appClsAttr}" class:is-drawer={drawer} class:is-navopen={navOpen}>
831
- ${mobileBar}
832
- <aside class="sv-app__side">
833
- <div class="sv-app__sidehead">
834
- ${brandLink}
835
- <button type="button" class="sv-app__collapse" aria-label="Collapse sidebar" title="Collapse sidebar" onclick={toggleNav}>
836
- <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6l-6 6 6 6"/></svg>
837
- </button>
838
- </div>
839
- ${linksMarkup}${footMarkup}
840
- </aside>
841
- ${mainMarkup}
830
+ : `<div class="sv-app sv-app--side${right ? ' sv-app--right' : ''}${appClsAttr}" class:is-drawer={drawer} class:is-navopen={navOpen}>
831
+ ${mobileBar}
832
+ <aside class="sv-app__side">
833
+ <div class="sv-app__sidehead">
834
+ ${brandLink}
835
+ <button type="button" class="sv-app__collapse" aria-label="Collapse sidebar" title="Collapse sidebar" onclick={toggleNav}>
836
+ <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6l-6 6 6 6"/></svg>
837
+ </button>
838
+ </div>
839
+ ${linksMarkup}${footMarkup}
840
+ </aside>
841
+ ${mainMarkup}
842
842
  </div>`;
843
843
  const styles = style === 'top-nav'
844
- ? ` .sv-app--top { display: flex; flex-direction: column; min-height: 100vh; }
845
- .sv-app__bar { display: flex; align-items: center; gap: 20px; padding: 12px 22px; border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
846
- .sv-app__links { display: flex; flex-direction: row; gap: 4px; flex-wrap: wrap; }
847
- .sv-app__footbar { padding: 12px 22px; border-top: 1px solid var(--sg-border, #e6e8ec); color: var(--sg-muted, #94a3b8); font-size: 13px; }
848
- /* Mobile: the bar stacks and its links scroll horizontally. */
849
- @media (max-width: 640px) {
850
- .sv-app__bar { flex-direction: column; align-items: stretch; gap: 10px; padding: 10px 14px; }
851
- .sv-app__links { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
852
- .sv-app__link { white-space: nowrap; }
844
+ ? ` .sv-app--top { display: flex; flex-direction: column; min-height: 100vh; }
845
+ .sv-app__bar { display: flex; align-items: center; gap: 20px; padding: 12px 22px; border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
846
+ .sv-app__links { display: flex; flex-direction: row; gap: 4px; flex-wrap: wrap; }
847
+ .sv-app__footbar { padding: 12px 22px; border-top: 1px solid var(--sg-border, #e6e8ec); color: var(--sg-muted, #94a3b8); font-size: 13px; }
848
+ /* Mobile: the bar stacks and its links scroll horizontally. */
849
+ @media (max-width: 640px) {
850
+ .sv-app__bar { flex-direction: column; align-items: stretch; gap: 10px; padding: 10px 14px; }
851
+ .sv-app__links { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
852
+ .sv-app__link { white-space: nowrap; }
853
853
  }`
854
854
  : style === 'bottom-nav'
855
- ? ` .sv-app--bottom { display: flex; flex-direction: column; min-height: 100vh; }
856
- .sv-app__bar--bottom {
857
- position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
858
- display: flex; align-items: center; gap: 20px; padding: 12px 22px calc(12px + env(safe-area-inset-bottom, 0px));
859
- border-top: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec));
860
- background: var(--sg-header-bg, #f8fafc);
861
- }
862
- .sv-app__bar--bottom .sv-app__links { display: flex; flex-direction: row; gap: 4px; flex-wrap: wrap; }
863
- /* Clears the fixed bottom bar so page content never renders underneath it. */
864
- .sv-app--bottom .sv-app__main { padding-bottom: 70px; }
865
- /* Mobile: brand drops out so the tab strip stays roomy; ties overflow to a horizontal scroll if there are many screens. */
866
- @media (max-width: 640px) {
867
- .sv-app__bar--bottom { gap: 10px; padding: 6px 12px calc(6px + env(safe-area-inset-bottom, 0px)); }
868
- .sv-app__bar--bottom .sv-app__brand { display: none; }
869
- .sv-app__bar--bottom .sv-app__links { flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start; -webkit-overflow-scrolling: touch; }
870
- .sv-app__bar--bottom .sv-app__link { white-space: nowrap; }
855
+ ? ` .sv-app--bottom { display: flex; flex-direction: column; min-height: 100vh; }
856
+ .sv-app__bar--bottom {
857
+ position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
858
+ display: flex; align-items: center; gap: 20px; padding: 12px 22px calc(12px + env(safe-area-inset-bottom, 0px));
859
+ border-top: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec));
860
+ background: var(--sg-header-bg, #f8fafc);
861
+ }
862
+ .sv-app__bar--bottom .sv-app__links { display: flex; flex-direction: row; gap: 4px; flex-wrap: wrap; }
863
+ /* Clears the fixed bottom bar so page content never renders underneath it. */
864
+ .sv-app--bottom .sv-app__main { padding-bottom: 70px; }
865
+ /* Mobile: brand drops out so the tab strip stays roomy; ties overflow to a horizontal scroll if there are many screens. */
866
+ @media (max-width: 640px) {
867
+ .sv-app__bar--bottom { gap: 10px; padding: 6px 12px calc(6px + env(safe-area-inset-bottom, 0px)); }
868
+ .sv-app__bar--bottom .sv-app__brand { display: none; }
869
+ .sv-app__bar--bottom .sv-app__links { flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start; -webkit-overflow-scrolling: touch; }
870
+ .sv-app__bar--bottom .sv-app__link { white-space: nowrap; }
871
871
  }`
872
- : ` .sv-app--side { display: grid; grid-template-columns: 240px minmax(0, 1fr); min-height: 100vh; }
873
- .sv-app--side.sv-app--right { grid-template-columns: minmax(0, 1fr) 240px; }
874
- .sv-app--right .sv-app__side { order: 2; border-right: 0; border-left: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); }
875
- .sv-app__side { display: flex; flex-direction: column; gap: 4px; padding: 20px 14px; border-right: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
876
- .sv-app__sidehead { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
877
- .sv-app__collapse, .sv-app__dock { display: inline-flex; padding: 5px; border: 0; border-radius: 8px; background: transparent; color: var(--sg-muted, #64748b); cursor: pointer; }
878
- .sv-app__collapse:hover, .sv-app__dock:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 8%, transparent); color: var(--sg-fg, #0f172a); }
879
- .sv-app__mobilebar { display: none; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
880
- .sv-app__mobilebar .sv-app__dock { margin-left: auto; }
881
- .sv-app__burger { display: inline-flex; padding: 6px; border: 0; border-radius: 8px; background: transparent; color: var(--sg-fg, #0f172a); cursor: pointer; }
882
- .sv-app__backdrop { position: fixed; inset: 0; z-index: 55; border: 0; background: rgba(15, 23, 42, 0.4); cursor: pointer; }
883
- .sv-app__links { display: flex; flex-direction: column; gap: 2px; }
884
- .sv-app__foot { margin-top: auto; padding-top: 14px; color: var(--sg-muted, #94a3b8); font-size: 12px; }
885
- /* Drawer mode: tablet / phone (<= 1024px) or a user-collapsed desktop sidebar.
886
- Driven by the .is-drawer class (JS matchMedia + the collapse toggle), so the
887
- sidebar docks on wide screens and slides in as an off-canvas drawer otherwise. */
888
- .sv-app--side.is-drawer, .sv-app--side.is-drawer.sv-app--right { display: block; }
889
- .sv-app--side.is-drawer .sv-app__mobilebar { display: flex; }
890
- .sv-app--side.is-drawer .sv-app__side {
891
- position: fixed; top: 0; bottom: 0; left: 0; z-index: 60; width: 264px; max-width: 82vw;
892
- transform: translateX(-100%); transition: transform 0.24s ease; box-shadow: 0 0 40px rgba(15, 23, 42, 0.28);
893
- }
894
- .sv-app--side.is-drawer.sv-app--right .sv-app__side { left: auto; right: 0; transform: translateX(100%); border-left: 0; }
895
- .sv-app--side.is-drawer.is-navopen .sv-app__side { transform: translateX(0); }
896
- .sv-app--side.is-drawer .sv-app__sidehead .sv-app__brand { display: none; } /* brand already in the top bar */
872
+ : ` .sv-app--side { display: grid; grid-template-columns: 240px minmax(0, 1fr); min-height: 100vh; }
873
+ .sv-app--side.sv-app--right { grid-template-columns: minmax(0, 1fr) 240px; }
874
+ .sv-app--right .sv-app__side { order: 2; border-right: 0; border-left: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); }
875
+ .sv-app__side { display: flex; flex-direction: column; gap: 4px; padding: 20px 14px; border-right: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
876
+ .sv-app__sidehead { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
877
+ .sv-app__collapse, .sv-app__dock { display: inline-flex; padding: 5px; border: 0; border-radius: 8px; background: transparent; color: var(--sg-muted, #64748b); cursor: pointer; }
878
+ .sv-app__collapse:hover, .sv-app__dock:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 8%, transparent); color: var(--sg-fg, #0f172a); }
879
+ .sv-app__mobilebar { display: none; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 16%, var(--sg-border, #e6e8ec)); background: var(--sg-header-bg, #f8fafc); }
880
+ .sv-app__mobilebar .sv-app__dock { margin-left: auto; }
881
+ .sv-app__burger { display: inline-flex; padding: 6px; border: 0; border-radius: 8px; background: transparent; color: var(--sg-fg, #0f172a); cursor: pointer; }
882
+ .sv-app__backdrop { position: fixed; inset: 0; z-index: 55; border: 0; background: rgba(15, 23, 42, 0.4); cursor: pointer; }
883
+ .sv-app__links { display: flex; flex-direction: column; gap: 2px; }
884
+ .sv-app__foot { margin-top: auto; padding-top: 14px; color: var(--sg-muted, #94a3b8); font-size: 12px; }
885
+ /* Drawer mode: tablet / phone (<= 1024px) or a user-collapsed desktop sidebar.
886
+ Driven by the .is-drawer class (JS matchMedia + the collapse toggle), so the
887
+ sidebar docks on wide screens and slides in as an off-canvas drawer otherwise. */
888
+ .sv-app--side.is-drawer, .sv-app--side.is-drawer.sv-app--right { display: block; }
889
+ .sv-app--side.is-drawer .sv-app__mobilebar { display: flex; }
890
+ .sv-app--side.is-drawer .sv-app__side {
891
+ position: fixed; top: 0; bottom: 0; left: 0; z-index: 60; width: 264px; max-width: 82vw;
892
+ transform: translateX(-100%); transition: transform 0.24s ease; box-shadow: 0 0 40px rgba(15, 23, 42, 0.28);
893
+ }
894
+ .sv-app--side.is-drawer.sv-app--right .sv-app__side { left: auto; right: 0; transform: translateX(100%); border-left: 0; }
895
+ .sv-app--side.is-drawer.is-navopen .sv-app__side { transform: translateX(0); }
896
+ .sv-app--side.is-drawer .sv-app__sidehead .sv-app__brand { display: none; } /* brand already in the top bar */
897
897
  .sv-app--side.is-drawer .sv-app__collapse { display: none; } /* dock lives in the top bar when drawered */`;
898
898
  return {
899
899
  path: 'src/routes/+layout.svelte',
900
900
  description: `App shell (${style}): nav linking every screen.`,
901
- contents: `<script lang="ts">
902
- import '../app.css'
903
- import '../custom.css'
904
- import { page } from '$app/state'${opts.access ? `\n import { currentRole, canScreen } from '../lib/access'` : ''}${opts.i18n ? `\n import { t, currentLocale, locales } from '../lib/i18n'` : ''}${opts.auth ? `\n import type { LayoutData } from './$types'` : ''}${opts.supabaseAuth ? `\n import { SvAuthGate, type SupabaseAuthClientLike } from '@svgrid/enterprise'\n import { supabaseClient } from '../lib/connections'` : ''}
905
-
906
- let { children${opts.auth ? ', data' : ''} }${opts.auth ? ": { children: import('svelte').Snippet; data: LayoutData }" : ''} = $props()
907
- const nav = ${JSON.stringify(links)}
908
- const brand = ${JSON.stringify(brand)}${footConst}${logoConst}
909
- let navOpen = $state(false)${opts.auth && opts.access ? `\n // Seed the client role store from the signed-in session (server-resolved).\n $effect(() => { if (data?.role) currentRole.set(data.role as never) })` : ''}
910
- // Close the mobile drawer whenever the route changes.
911
- $effect(() => { void page.url.pathname; navOpen = false })${hasSwitch ? `
912
- // Light/dark switcher: defaults to the mode picked in Studio, then honours the
913
- // visitor's saved choice. Applies via [data-theme] on <html> (see the token
914
- // sets in app.css), and persists per browser.
915
- let theme = $state<'light' | 'dark'>('${defaultMode}')
916
- function applyTheme(t: 'light' | 'dark') {
917
- theme = t
918
- try { document.documentElement.dataset.theme = t } catch (_) { /* no DOM */ }
919
- try { localStorage.setItem('svapp:theme', t) } catch (_) { /* storage blocked */ }
920
- }
921
- function toggleTheme() { applyTheme(theme === 'dark' ? 'light' : 'dark') }
922
- $effect(() => {
923
- let t: 'light' | 'dark' = '${defaultMode}'
924
- try { const s = localStorage.getItem('svapp:theme'); if (s === 'light' || s === 'dark') t = s } catch (_) { /* storage blocked */ }
925
- applyTheme(t)
926
- })` : ''}${sideState}${toolbarOn ? `
927
- // App-chrome quick-search: filter the screens by label as you type.
928
- const initials = ${JSON.stringify(initials)}
929
- const acctEmail = ${JSON.stringify(`admin@${brandSlug}.com`)}
930
- let q = $state('')
931
- const results = $derived(q.trim() ? nav.filter((n) => n.label.toLowerCase().includes(q.trim().toLowerCase())).slice(0, 6) : [])
932
- let bellOpen = $state(false)
933
- let menuOpen = $state(false)
934
- let searchEl: HTMLInputElement | null = $state(null)
935
- const notifications = [
936
- { title: 'Welcome to ' + brand, time: 'just now', color: 'var(--sg-accent, #6366f1)' },
937
- { title: 'Your workspace is ready', time: '2m ago', color: '#10b981' },
938
- { title: 'Sample data loaded', time: '5m ago', color: '#f59e0b' },
939
- ]
940
- // Cmd/Ctrl+K focuses search; Escape closes the menus.
941
- $effect(() => {
942
- function onKey(e: KeyboardEvent) {
943
- if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); searchEl?.focus() }
944
- else if (e.key === 'Escape') { bellOpen = false; menuOpen = false }
945
- }
946
- window.addEventListener('keydown', onKey)
947
- return () => window.removeEventListener('keydown', onKey)
948
- })
949
- // Close both menus on route change.
950
- $effect(() => { void page.url.pathname; bellOpen = false; menuOpen = false })` : ''}
951
- </script>
952
-
953
- ${opts.auth ? `{#if ${JSON.stringify(opts.authRoutes ?? ['/login'])}.includes(page.url.pathname)}
954
- {@render children()}
955
- {:else}
956
- ${body}
957
- {/if}` : body}
958
-
959
- <style>
960
- ${styles}
961
- .sv-app__brand { display: inline-flex; align-items: center; font-weight: 700; font-size: 15px; color: var(--sg-fg, #0f172a); text-decoration: none; padding: 4px 8px; }
962
- .sv-app__logo { max-height: 34px; max-width: 160px; width: auto; object-fit: contain; display: block; }
963
- .sv-app__link { padding: 7px 10px; border-radius: 8px; color: var(--sg-fg, #334155); text-decoration: none; font-size: 14px; }
964
- .sv-app__link:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 6%, transparent); }
965
- .sv-app__link.is-active { background: color-mix(in srgb, var(--sg-accent, #4f46e5) 14%, transparent); color: var(--sg-accent, #4f46e5); font-weight: 600; }
966
- .sv-app__main { display: flex; flex-direction: column; min-width: 0; }
967
- .sv-app__content { padding: 24px 28px; min-width: 0; }
968
- /* App-chrome toolbar: a sticky product header with quick-search + account cluster. */
969
- .sv-app__toolbar { position: sticky; top: 0; z-index: 40; display: flex; align-items: center; gap: 16px; padding: 10px 28px; background: color-mix(in srgb, var(--sg-header-bg, #f8fafc) 86%, transparent); backdrop-filter: blur(8px); border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 10%, var(--sg-border, #e6e8ec)); }
970
- .sv-app__search { position: relative; flex: 1; max-width: 440px; display: flex; align-items: center; }
971
- .sv-app__search-ic { position: absolute; left: 11px; color: var(--sg-muted, #94a3b8); pointer-events: none; }
972
- .sv-app__search-in { width: 100%; font: inherit; font-size: 13.5px; padding: 7px 46px 7px 34px; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); }
973
- .sv-app__search-in::-webkit-search-cancel-button { -webkit-appearance: none; }
974
- .sv-app__search-in:focus { outline: none; border-color: var(--sg-accent, #6366f1); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent, #6366f1) 18%, transparent); }
975
- .sv-app__results { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20; display: flex; flex-direction: column; padding: 5px; background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 10px; box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.25); }
976
- .sv-app__result { padding: 7px 10px; border-radius: 7px; font-size: 13.5px; color: var(--sg-fg, #334155); text-decoration: none; }
977
- .sv-app__result:hover { background: color-mix(in srgb, var(--sg-accent, #6366f1) 12%, transparent); color: var(--sg-accent, #6366f1); }
978
- .sv-app__tools { margin-left: auto; display: flex; align-items: center; gap: 10px; }
979
- .sv-app__tool { position: relative; display: inline-flex; padding: 7px; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-muted, #64748b); cursor: pointer; }
980
- .sv-app__tool:hover { color: var(--sg-fg, #0f172a); border-color: color-mix(in srgb, var(--sg-fg, #0f172a) 20%, var(--sg-border, #e6e8ec)); }
981
- .sv-app__dot { position: absolute; top: 5px; right: 5px; width: 6px; height: 6px; border-radius: 50%; background: var(--sg-accent, #6366f1); box-shadow: 0 0 0 2px var(--sg-bg, #fff); }
982
- .sv-app__avatar { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: 50%; font: inherit; font-size: 12px; font-weight: 700; color: #fff; background: var(--sg-accent, #6366f1); letter-spacing: 0.02em; cursor: pointer; }
983
- .sv-app__kbd { position: absolute; right: 8px; font-size: 10.5px; font-weight: 600; color: var(--sg-muted, #94a3b8); background: color-mix(in srgb, var(--sg-fg, #0f172a) 6%, transparent); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 5px; padding: 1px 5px; pointer-events: none; }
984
- .sv-app__pop { position: relative; }
985
- .sv-app__scrim { position: fixed; inset: 0; z-index: 44; border: 0; padding: 0; background: transparent; cursor: default; }
986
- .sv-app__menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 46; min-width: 232px; padding: 6px; background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 11px; box-shadow: 0 16px 40px -10px rgba(15, 23, 42, 0.3); }
987
- .sv-app__menu-head { padding: 6px 10px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--sg-muted, #94a3b8); }
988
- .sv-app__notif { display: flex; gap: 9px; padding: 8px 10px; border-radius: 8px; }
989
- .sv-app__notif:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 5%, transparent); }
990
- .sv-app__notif-dot { flex: none; width: 8px; height: 8px; margin-top: 5px; border-radius: 50%; }
991
- .sv-app__notif-body { display: flex; flex-direction: column; min-width: 0; }
992
- .sv-app__notif-body strong { font-size: 13px; font-weight: 600; color: var(--sg-fg, #0f172a); }
993
- .sv-app__notif-body span { font-size: 11.5px; color: var(--sg-muted, #94a3b8); }
994
- .sv-app__menu-foot { width: 100%; margin-top: 4px; padding: 8px; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--sg-accent, #6366f1); background: none; border: none; border-top: 1px solid var(--sg-border, #eef0f3); cursor: pointer; }
995
- .sv-app__acct-head { display: flex; align-items: center; gap: 10px; padding: 8px 10px 10px; border-bottom: 1px solid var(--sg-border, #eef0f3); margin-bottom: 4px; }
996
- .sv-app__avatar--lg { width: 38px; height: 38px; font-size: 14px; }
997
- .sv-app__acct-id { display: flex; flex-direction: column; min-width: 0; }
998
- .sv-app__acct-id strong { font-size: 13.5px; color: var(--sg-fg, #0f172a); }
999
- .sv-app__acct-id span { font-size: 12px; color: var(--sg-muted, #94a3b8); overflow: hidden; text-overflow: ellipsis; }
1000
- .sv-app__menu-item { display: block; width: 100%; text-align: left; padding: 8px 10px; font: inherit; font-size: 13px; color: var(--sg-fg, #334155); background: none; border: none; border-radius: 8px; text-decoration: none; cursor: pointer; }
1001
- .sv-app__menu-item:hover { background: color-mix(in srgb, var(--sg-accent, #6366f1) 10%, transparent); color: var(--sg-accent, #6366f1); }
1002
- .sv-app__menu-item--danger:hover { background: color-mix(in srgb, #ef4444 12%, transparent); color: #ef4444; }
1003
- .sv-app__signout { margin: 0; display: block; }
1004
- .sv-app__signout .sv-app__menu-item { width: 100%; }
1005
- .sv-app__locale { margin-top: 10px; padding: 5px 8px; font: inherit; font-size: 12.5px; color: var(--sg-fg, #0f172a); background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 8px; }
1006
- .sv-app__theme { align-items: center; justify-content: center; cursor: pointer; }
1007
- /* No-toolbar fallback slot: trails the nav, pushed to the far edge in row bars. */
1008
- .sv-app__navtools { display: flex; align-items: center; }
1009
- .sv-app--top .sv-app__navtools, .sv-app__bar--bottom .sv-app__navtools { margin-left: auto; }
1010
- /* Mobile bar + drawer chrome (hidden on desktop; media queries above switch it on) */
1011
- .sv-app__mobilebar { display: none; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--sg-border, #e6e8ec); background: var(--sg-header-bg, #f8fafc); position: sticky; top: 0; z-index: 50; }
1012
- .sv-app__burger { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); cursor: pointer; }
1013
- .sv-app__backdrop { position: fixed; inset: 0; z-index: 55; border: 0; padding: 0; background: rgba(15, 23, 42, 0.42); cursor: pointer; }
1014
- @media (max-width: 860px) { .sv-app__content { padding: 18px 16px; } .sv-app__toolbar { padding: 9px 16px; } }
1015
- @media (max-width: 640px) { .sv-app__content { padding: 14px 12px; } .sv-app__toolbar { padding: 8px 12px; gap: 10px; } .sv-app__tool { display: none; } }
1016
- </style>
901
+ contents: `<script lang="ts">
902
+ import '../app.css'
903
+ import '../custom.css'
904
+ import { page } from '$app/state'${opts.access ? `\n import { currentRole, canScreen } from '../lib/access'` : ''}${opts.i18n ? `\n import { t, currentLocale, locales } from '../lib/i18n'` : ''}${opts.auth ? `\n import type { LayoutData } from './$types'` : ''}${opts.supabaseAuth ? `\n import { SvAuthGate, type SupabaseAuthClientLike } from '@svgrid/enterprise'\n import { supabaseClient } from '../lib/connections'` : ''}
905
+
906
+ let { children${opts.auth ? ', data' : ''} }${opts.auth ? ": { children: import('svelte').Snippet; data: LayoutData }" : ''} = $props()
907
+ const nav = ${JSON.stringify(links)}
908
+ const brand = ${JSON.stringify(brand)}${footConst}${logoConst}
909
+ let navOpen = $state(false)${opts.auth && opts.access ? `\n // Seed the client role store from the signed-in session (server-resolved).\n $effect(() => { if (data?.role) currentRole.set(data.role as never) })` : ''}
910
+ // Close the mobile drawer whenever the route changes.
911
+ $effect(() => { void page.url.pathname; navOpen = false })${hasSwitch ? `
912
+ // Light/dark switcher: defaults to the mode picked in Studio, then honours the
913
+ // visitor's saved choice. Applies via [data-theme] on <html> (see the token
914
+ // sets in app.css), and persists per browser.
915
+ let theme = $state<'light' | 'dark'>('${defaultMode}')
916
+ function applyTheme(t: 'light' | 'dark') {
917
+ theme = t
918
+ try { document.documentElement.dataset.theme = t } catch (_) { /* no DOM */ }
919
+ try { localStorage.setItem('svapp:theme', t) } catch (_) { /* storage blocked */ }
920
+ }
921
+ function toggleTheme() { applyTheme(theme === 'dark' ? 'light' : 'dark') }
922
+ $effect(() => {
923
+ let t: 'light' | 'dark' = '${defaultMode}'
924
+ try { const s = localStorage.getItem('svapp:theme'); if (s === 'light' || s === 'dark') t = s } catch (_) { /* storage blocked */ }
925
+ applyTheme(t)
926
+ })` : ''}${sideState}${toolbarOn ? `
927
+ // App-chrome quick-search: filter the screens by label as you type.
928
+ const initials = ${JSON.stringify(initials)}
929
+ const acctEmail = ${JSON.stringify(`admin@${brandSlug}.com`)}
930
+ let q = $state('')
931
+ const results = $derived(q.trim() ? nav.filter((n) => n.label.toLowerCase().includes(q.trim().toLowerCase())).slice(0, 6) : [])
932
+ let bellOpen = $state(false)
933
+ let menuOpen = $state(false)
934
+ let searchEl: HTMLInputElement | null = $state(null)
935
+ const notifications = [
936
+ { title: 'Welcome to ' + brand, time: 'just now', color: 'var(--sg-accent, #6366f1)' },
937
+ { title: 'Your workspace is ready', time: '2m ago', color: '#10b981' },
938
+ { title: 'Sample data loaded', time: '5m ago', color: '#f59e0b' },
939
+ ]
940
+ // Cmd/Ctrl+K focuses search; Escape closes the menus.
941
+ $effect(() => {
942
+ function onKey(e: KeyboardEvent) {
943
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); searchEl?.focus() }
944
+ else if (e.key === 'Escape') { bellOpen = false; menuOpen = false }
945
+ }
946
+ window.addEventListener('keydown', onKey)
947
+ return () => window.removeEventListener('keydown', onKey)
948
+ })
949
+ // Close both menus on route change.
950
+ $effect(() => { void page.url.pathname; bellOpen = false; menuOpen = false })` : ''}
951
+ </script>
952
+
953
+ ${opts.auth ? `{#if ${JSON.stringify(opts.authRoutes ?? ['/login'])}.includes(page.url.pathname)}
954
+ {@render children()}
955
+ {:else}
956
+ ${body}
957
+ {/if}` : body}
958
+
959
+ <style>
960
+ ${styles}
961
+ .sv-app__brand { display: inline-flex; align-items: center; font-weight: 700; font-size: 15px; color: var(--sg-fg, #0f172a); text-decoration: none; padding: 4px 8px; }
962
+ .sv-app__logo { max-height: 34px; max-width: 160px; width: auto; object-fit: contain; display: block; }
963
+ .sv-app__link { padding: 7px 10px; border-radius: 8px; color: var(--sg-fg, #334155); text-decoration: none; font-size: 14px; }
964
+ .sv-app__link:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 6%, transparent); }
965
+ .sv-app__link.is-active { background: color-mix(in srgb, var(--sg-accent, #4f46e5) 14%, transparent); color: var(--sg-accent, #4f46e5); font-weight: 600; }
966
+ .sv-app__main { display: flex; flex-direction: column; min-width: 0; }
967
+ .sv-app__content { padding: 24px 28px; min-width: 0; }
968
+ /* App-chrome toolbar: a sticky product header with quick-search + account cluster. */
969
+ .sv-app__toolbar { position: sticky; top: 0; z-index: 40; display: flex; align-items: center; gap: 16px; padding: 10px 28px; background: color-mix(in srgb, var(--sg-header-bg, #f8fafc) 86%, transparent); backdrop-filter: blur(8px); border-bottom: 1px solid color-mix(in srgb, var(--sg-fg, #0f172a) 10%, var(--sg-border, #e6e8ec)); }
970
+ .sv-app__search { position: relative; flex: 1; max-width: 440px; display: flex; align-items: center; }
971
+ .sv-app__search-ic { position: absolute; left: 11px; color: var(--sg-muted, #94a3b8); pointer-events: none; }
972
+ .sv-app__search-in { width: 100%; font: inherit; font-size: 13.5px; padding: 7px 46px 7px 34px; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); }
973
+ .sv-app__search-in::-webkit-search-cancel-button { -webkit-appearance: none; }
974
+ .sv-app__search-in:focus { outline: none; border-color: var(--sg-accent, #6366f1); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sg-accent, #6366f1) 18%, transparent); }
975
+ .sv-app__results { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20; display: flex; flex-direction: column; padding: 5px; background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 10px; box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.25); }
976
+ .sv-app__result { padding: 7px 10px; border-radius: 7px; font-size: 13.5px; color: var(--sg-fg, #334155); text-decoration: none; }
977
+ .sv-app__result:hover { background: color-mix(in srgb, var(--sg-accent, #6366f1) 12%, transparent); color: var(--sg-accent, #6366f1); }
978
+ .sv-app__tools { margin-left: auto; display: flex; align-items: center; gap: 10px; }
979
+ .sv-app__tool { position: relative; display: inline-flex; padding: 7px; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-muted, #64748b); cursor: pointer; }
980
+ .sv-app__tool:hover { color: var(--sg-fg, #0f172a); border-color: color-mix(in srgb, var(--sg-fg, #0f172a) 20%, var(--sg-border, #e6e8ec)); }
981
+ .sv-app__dot { position: absolute; top: 5px; right: 5px; width: 6px; height: 6px; border-radius: 50%; background: var(--sg-accent, #6366f1); box-shadow: 0 0 0 2px var(--sg-bg, #fff); }
982
+ .sv-app__avatar { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: 50%; font: inherit; font-size: 12px; font-weight: 700; color: #fff; background: var(--sg-accent, #6366f1); letter-spacing: 0.02em; cursor: pointer; }
983
+ .sv-app__kbd { position: absolute; right: 8px; font-size: 10.5px; font-weight: 600; color: var(--sg-muted, #94a3b8); background: color-mix(in srgb, var(--sg-fg, #0f172a) 6%, transparent); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 5px; padding: 1px 5px; pointer-events: none; }
984
+ .sv-app__pop { position: relative; }
985
+ .sv-app__scrim { position: fixed; inset: 0; z-index: 44; border: 0; padding: 0; background: transparent; cursor: default; }
986
+ .sv-app__menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 46; min-width: 232px; padding: 6px; background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 11px; box-shadow: 0 16px 40px -10px rgba(15, 23, 42, 0.3); }
987
+ .sv-app__menu-head { padding: 6px 10px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--sg-muted, #94a3b8); }
988
+ .sv-app__notif { display: flex; gap: 9px; padding: 8px 10px; border-radius: 8px; }
989
+ .sv-app__notif:hover { background: color-mix(in srgb, var(--sg-fg, #0f172a) 5%, transparent); }
990
+ .sv-app__notif-dot { flex: none; width: 8px; height: 8px; margin-top: 5px; border-radius: 50%; }
991
+ .sv-app__notif-body { display: flex; flex-direction: column; min-width: 0; }
992
+ .sv-app__notif-body strong { font-size: 13px; font-weight: 600; color: var(--sg-fg, #0f172a); }
993
+ .sv-app__notif-body span { font-size: 11.5px; color: var(--sg-muted, #94a3b8); }
994
+ .sv-app__menu-foot { width: 100%; margin-top: 4px; padding: 8px; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--sg-accent, #6366f1); background: none; border: none; border-top: 1px solid var(--sg-border, #eef0f3); cursor: pointer; }
995
+ .sv-app__acct-head { display: flex; align-items: center; gap: 10px; padding: 8px 10px 10px; border-bottom: 1px solid var(--sg-border, #eef0f3); margin-bottom: 4px; }
996
+ .sv-app__avatar--lg { width: 38px; height: 38px; font-size: 14px; }
997
+ .sv-app__acct-id { display: flex; flex-direction: column; min-width: 0; }
998
+ .sv-app__acct-id strong { font-size: 13.5px; color: var(--sg-fg, #0f172a); }
999
+ .sv-app__acct-id span { font-size: 12px; color: var(--sg-muted, #94a3b8); overflow: hidden; text-overflow: ellipsis; }
1000
+ .sv-app__menu-item { display: block; width: 100%; text-align: left; padding: 8px 10px; font: inherit; font-size: 13px; color: var(--sg-fg, #334155); background: none; border: none; border-radius: 8px; text-decoration: none; cursor: pointer; }
1001
+ .sv-app__menu-item:hover { background: color-mix(in srgb, var(--sg-accent, #6366f1) 10%, transparent); color: var(--sg-accent, #6366f1); }
1002
+ .sv-app__menu-item--danger:hover { background: color-mix(in srgb, #ef4444 12%, transparent); color: #ef4444; }
1003
+ .sv-app__signout { margin: 0; display: block; }
1004
+ .sv-app__signout .sv-app__menu-item { width: 100%; }
1005
+ .sv-app__locale { margin-top: 10px; padding: 5px 8px; font: inherit; font-size: 12.5px; color: var(--sg-fg, #0f172a); background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e6e8ec); border-radius: 8px; }
1006
+ .sv-app__theme { align-items: center; justify-content: center; cursor: pointer; }
1007
+ /* No-toolbar fallback slot: trails the nav, pushed to the far edge in row bars. */
1008
+ .sv-app__navtools { display: flex; align-items: center; }
1009
+ .sv-app--top .sv-app__navtools, .sv-app__bar--bottom .sv-app__navtools { margin-left: auto; }
1010
+ /* Mobile bar + drawer chrome (hidden on desktop; media queries above switch it on) */
1011
+ .sv-app__mobilebar { display: none; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--sg-border, #e6e8ec); background: var(--sg-header-bg, #f8fafc); position: sticky; top: 0; z-index: 50; }
1012
+ .sv-app__burger { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0; border: 1px solid var(--sg-border, #e6e8ec); border-radius: 9px; background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); cursor: pointer; }
1013
+ .sv-app__backdrop { position: fixed; inset: 0; z-index: 55; border: 0; padding: 0; background: rgba(15, 23, 42, 0.42); cursor: pointer; }
1014
+ @media (max-width: 860px) { .sv-app__content { padding: 18px 16px; } .sv-app__toolbar { padding: 9px 16px; } }
1015
+ @media (max-width: 640px) { .sv-app__content { padding: 14px 12px; } .sv-app__toolbar { padding: 8px 12px; gap: 10px; } .sv-app__tool { display: none; } }
1016
+ </style>
1017
1017
  `,
1018
1018
  };
1019
1019
  }
@@ -1028,24 +1028,24 @@ export function homeFile(nav) {
1028
1028
  return {
1029
1029
  path: 'src/routes/+page.svelte',
1030
1030
  description: 'Home: redirects to the primary dashboard.',
1031
- contents: `<script lang="ts">
1032
- import { onMount } from 'svelte'
1033
- import { goto } from '$app/navigation'
1034
- const home = ${JSON.stringify(first)}
1035
- onMount(() => { goto(home, { replaceState: true }) })
1036
- </script>
1037
-
1038
- <div class="st-home">
1039
- <div class="st-home__spinner" aria-hidden="true"></div>
1040
- <p>Opening your workspace… <a href={home}>Continue</a></p>
1041
- </div>
1042
-
1043
- <style>
1044
- .st-home { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; min-height: 52vh; color: var(--sg-muted, #64748b); font-size: 14px; }
1045
- .st-home__spinner { width: 26px; height: 26px; border-radius: 50%; border: 3px solid var(--sg-border, #e6e8ec); border-top-color: var(--sg-accent, #6366f1); animation: st-home-spin 0.7s linear infinite; }
1046
- .st-home a { color: var(--sg-accent, #6366f1); font-weight: 600; }
1047
- @keyframes st-home-spin { to { transform: rotate(360deg); } }
1048
- </style>
1031
+ contents: `<script lang="ts">
1032
+ import { onMount } from 'svelte'
1033
+ import { goto } from '$app/navigation'
1034
+ const home = ${JSON.stringify(first)}
1035
+ onMount(() => { goto(home, { replaceState: true }) })
1036
+ </script>
1037
+
1038
+ <div class="st-home">
1039
+ <div class="st-home__spinner" aria-hidden="true"></div>
1040
+ <p>Opening your workspace… <a href={home}>Continue</a></p>
1041
+ </div>
1042
+
1043
+ <style>
1044
+ .st-home { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; min-height: 52vh; color: var(--sg-muted, #64748b); font-size: 14px; }
1045
+ .st-home__spinner { width: 26px; height: 26px; border-radius: 50%; border: 3px solid var(--sg-border, #e6e8ec); border-top-color: var(--sg-accent, #6366f1); animation: st-home-spin 0.7s linear infinite; }
1046
+ .st-home a { color: var(--sg-accent, #6366f1); font-weight: 600; }
1047
+ @keyframes st-home-spin { to { transform: rotate(360deg); } }
1048
+ </style>
1049
1049
  `,
1050
1050
  };
1051
1051
  }