primitive-app 1.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 (462) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +1202 -0
  3. package/dist/PrimitiveNotFound.vue.d.ts +4 -0
  4. package/dist/PrimitiveNotFound.vue.d.ts.map +1 -0
  5. package/dist/components/auth/PrimitiveLogin.vue.d.ts +108 -0
  6. package/dist/components/auth/PrimitiveLogin.vue.d.ts.map +1 -0
  7. package/dist/components/auth/PrimitiveLogout.vue.d.ts +32 -0
  8. package/dist/components/auth/PrimitiveLogout.vue.d.ts.map +1 -0
  9. package/dist/components/auth/PrimitiveOauthCallback.vue.d.ts +39 -0
  10. package/dist/components/auth/PrimitiveOauthCallback.vue.d.ts.map +1 -0
  11. package/dist/components/debug-suite/EphemeralDocService.d.ts +46 -0
  12. package/dist/components/debug-suite/EphemeralDocService.d.ts.map +1 -0
  13. package/dist/components/debug-suite/PrimitiveDebuggingSuite.vue.d.ts +15 -0
  14. package/dist/components/debug-suite/PrimitiveDebuggingSuite.vue.d.ts.map +1 -0
  15. package/dist/components/debug-suite/PrimitiveTestRunner.vue.d.ts +19 -0
  16. package/dist/components/debug-suite/PrimitiveTestRunner.vue.d.ts.map +1 -0
  17. package/dist/components/debug-suite/document-debugger/DocumentDebuggerIndex.vue.d.ts +4 -0
  18. package/dist/components/debug-suite/document-debugger/DocumentDebuggerIndex.vue.d.ts.map +1 -0
  19. package/dist/components/debug-suite/document-debugger/DocumentDebuggerModel.vue.d.ts +4 -0
  20. package/dist/components/debug-suite/document-debugger/DocumentDebuggerModel.vue.d.ts.map +1 -0
  21. package/dist/components/debug-suite/document-debugger/DocumentSidebar.vue.d.ts +48 -0
  22. package/dist/components/debug-suite/document-debugger/DocumentSidebar.vue.d.ts.map +1 -0
  23. package/dist/components/debug-suite/document-debugger/dialogs/MassDeleteDocsDialog.vue.d.ts +28 -0
  24. package/dist/components/debug-suite/document-debugger/dialogs/MassDeleteDocsDialog.vue.d.ts.map +1 -0
  25. package/dist/components/debug-suite/document-debugger/dialogs/MassDeleteRecordsDialog.vue.d.ts +32 -0
  26. package/dist/components/debug-suite/document-debugger/dialogs/MassDeleteRecordsDialog.vue.d.ts.map +1 -0
  27. package/dist/components/debug-suite/document-debugger/types.d.ts +67 -0
  28. package/dist/components/debug-suite/document-debugger/types.d.ts.map +1 -0
  29. package/dist/components/debug-suite/document-debugger/utils.d.ts +38 -0
  30. package/dist/components/debug-suite/document-debugger/utils.d.ts.map +1 -0
  31. package/dist/components/debug-suite/pages/DebugSuiteLayout.vue.d.ts +15 -0
  32. package/dist/components/debug-suite/pages/DebugSuiteLayout.vue.d.ts.map +1 -0
  33. package/dist/components/debug-suite/pages/DebuggingSuiteDocuments.vue.d.ts +4 -0
  34. package/dist/components/debug-suite/pages/DebuggingSuiteDocuments.vue.d.ts.map +1 -0
  35. package/dist/components/debug-suite/pages/DebuggingSuiteDocumentsModel.vue.d.ts +4 -0
  36. package/dist/components/debug-suite/pages/DebuggingSuiteDocumentsModel.vue.d.ts.map +1 -0
  37. package/dist/components/debug-suite/pages/DebuggingSuiteHome.vue.d.ts +4 -0
  38. package/dist/components/debug-suite/pages/DebuggingSuiteHome.vue.d.ts.map +1 -0
  39. package/dist/components/debug-suite/pages/DebuggingSuiteTests.vue.d.ts +4 -0
  40. package/dist/components/debug-suite/pages/DebuggingSuiteTests.vue.d.ts.map +1 -0
  41. package/dist/components/debug-suite/testTypes.d.ts +18 -0
  42. package/dist/components/debug-suite/testTypes.d.ts.map +1 -0
  43. package/dist/components/documents/PrimitiveManageDocuments.vue.d.ts +4 -0
  44. package/dist/components/documents/PrimitiveManageDocuments.vue.d.ts.map +1 -0
  45. package/dist/components/documents/PrimitiveShareDocumentDialog.vue.d.ts +26 -0
  46. package/dist/components/documents/PrimitiveShareDocumentDialog.vue.d.ts.map +1 -0
  47. package/dist/components/documents/PrimitiveSingleDocumentSwitcher.vue.d.ts +4 -0
  48. package/dist/components/documents/PrimitiveSingleDocumentSwitcher.vue.d.ts.map +1 -0
  49. package/dist/components/navigation/PrimitiveAppBreadcrumb.vue.d.ts +4 -0
  50. package/dist/components/navigation/PrimitiveAppBreadcrumb.vue.d.ts.map +1 -0
  51. package/dist/components/navigation/PrimitiveAppSidebarHeader.vue.d.ts +4 -0
  52. package/dist/components/navigation/PrimitiveAppSidebarHeader.vue.d.ts.map +1 -0
  53. package/dist/components/navigation/PrimitiveBottomNav.vue.d.ts +4 -0
  54. package/dist/components/navigation/PrimitiveBottomNav.vue.d.ts.map +1 -0
  55. package/dist/components/navigation/PrimitiveNavigationBadge.vue.d.ts +14 -0
  56. package/dist/components/navigation/PrimitiveNavigationBadge.vue.d.ts.map +1 -0
  57. package/dist/components/navigation/PrimitiveSidebarNav.vue.d.ts +4 -0
  58. package/dist/components/navigation/PrimitiveSidebarNav.vue.d.ts.map +1 -0
  59. package/dist/components/navigation/PrimitiveUserMenu.vue.d.ts +35 -0
  60. package/dist/components/navigation/PrimitiveUserMenu.vue.d.ts.map +1 -0
  61. package/dist/components/shared/DeleteConfirmationDialog.vue.d.ts +55 -0
  62. package/dist/components/shared/DeleteConfirmationDialog.vue.d.ts.map +1 -0
  63. package/dist/components/shared/PrimitiveLogoSpinner.vue.d.ts +22 -0
  64. package/dist/components/shared/PrimitiveLogoSpinner.vue.d.ts.map +1 -0
  65. package/dist/components/shared/PrimitiveSkeletonGate.vue.d.ts +34 -0
  66. package/dist/components/shared/PrimitiveSkeletonGate.vue.d.ts.map +1 -0
  67. package/dist/components/ui/avatar/Avatar.vue.d.ts +18 -0
  68. package/dist/components/ui/avatar/Avatar.vue.d.ts.map +1 -0
  69. package/dist/components/ui/avatar/AvatarFallback.vue.d.ts +19 -0
  70. package/dist/components/ui/avatar/AvatarFallback.vue.d.ts.map +1 -0
  71. package/dist/components/ui/avatar/AvatarImage.vue.d.ts +15 -0
  72. package/dist/components/ui/avatar/AvatarImage.vue.d.ts.map +1 -0
  73. package/dist/components/ui/avatar/index.d.ts +4 -0
  74. package/dist/components/ui/avatar/index.d.ts.map +1 -0
  75. package/dist/components/ui/badge/Badge.vue.d.ts +21 -0
  76. package/dist/components/ui/badge/Badge.vue.d.ts.map +1 -0
  77. package/dist/components/ui/badge/index.d.ts +7 -0
  78. package/dist/components/ui/badge/index.d.ts.map +1 -0
  79. package/dist/components/ui/breadcrumb/Breadcrumb.vue.d.ts +18 -0
  80. package/dist/components/ui/breadcrumb/Breadcrumb.vue.d.ts.map +1 -0
  81. package/dist/components/ui/breadcrumb/BreadcrumbEllipsis.vue.d.ts +18 -0
  82. package/dist/components/ui/breadcrumb/BreadcrumbEllipsis.vue.d.ts.map +1 -0
  83. package/dist/components/ui/breadcrumb/BreadcrumbItem.vue.d.ts +18 -0
  84. package/dist/components/ui/breadcrumb/BreadcrumbItem.vue.d.ts.map +1 -0
  85. package/dist/components/ui/breadcrumb/BreadcrumbLink.vue.d.ts +21 -0
  86. package/dist/components/ui/breadcrumb/BreadcrumbLink.vue.d.ts.map +1 -0
  87. package/dist/components/ui/breadcrumb/BreadcrumbList.vue.d.ts +18 -0
  88. package/dist/components/ui/breadcrumb/BreadcrumbList.vue.d.ts.map +1 -0
  89. package/dist/components/ui/breadcrumb/BreadcrumbPage.vue.d.ts +18 -0
  90. package/dist/components/ui/breadcrumb/BreadcrumbPage.vue.d.ts.map +1 -0
  91. package/dist/components/ui/breadcrumb/BreadcrumbSeparator.vue.d.ts +18 -0
  92. package/dist/components/ui/breadcrumb/BreadcrumbSeparator.vue.d.ts.map +1 -0
  93. package/dist/components/ui/breadcrumb/index.d.ts +8 -0
  94. package/dist/components/ui/breadcrumb/index.d.ts.map +1 -0
  95. package/dist/components/ui/button/Button.vue.d.ts +24 -0
  96. package/dist/components/ui/button/Button.vue.d.ts.map +1 -0
  97. package/dist/components/ui/button/index.d.ts +8 -0
  98. package/dist/components/ui/button/index.d.ts.map +1 -0
  99. package/dist/components/ui/calendar/Calendar.vue.d.ts +43 -0
  100. package/dist/components/ui/calendar/Calendar.vue.d.ts.map +1 -0
  101. package/dist/components/ui/calendar/CalendarCell.vue.d.ts +19 -0
  102. package/dist/components/ui/calendar/CalendarCell.vue.d.ts.map +1 -0
  103. package/dist/components/ui/calendar/CalendarCellTrigger.vue.d.ts +21 -0
  104. package/dist/components/ui/calendar/CalendarCellTrigger.vue.d.ts.map +1 -0
  105. package/dist/components/ui/calendar/CalendarGrid.vue.d.ts +19 -0
  106. package/dist/components/ui/calendar/CalendarGrid.vue.d.ts.map +1 -0
  107. package/dist/components/ui/calendar/CalendarGridBody.vue.d.ts +15 -0
  108. package/dist/components/ui/calendar/CalendarGridBody.vue.d.ts.map +1 -0
  109. package/dist/components/ui/calendar/CalendarGridHead.vue.d.ts +19 -0
  110. package/dist/components/ui/calendar/CalendarGridHead.vue.d.ts.map +1 -0
  111. package/dist/components/ui/calendar/CalendarGridRow.vue.d.ts +19 -0
  112. package/dist/components/ui/calendar/CalendarGridRow.vue.d.ts.map +1 -0
  113. package/dist/components/ui/calendar/CalendarHeadCell.vue.d.ts +19 -0
  114. package/dist/components/ui/calendar/CalendarHeadCell.vue.d.ts.map +1 -0
  115. package/dist/components/ui/calendar/CalendarHeader.vue.d.ts +19 -0
  116. package/dist/components/ui/calendar/CalendarHeader.vue.d.ts.map +1 -0
  117. package/dist/components/ui/calendar/CalendarHeading.vue.d.ts +20 -0
  118. package/dist/components/ui/calendar/CalendarHeading.vue.d.ts.map +1 -0
  119. package/dist/components/ui/calendar/CalendarNextButton.vue.d.ts +19 -0
  120. package/dist/components/ui/calendar/CalendarNextButton.vue.d.ts.map +1 -0
  121. package/dist/components/ui/calendar/CalendarPrevButton.vue.d.ts +19 -0
  122. package/dist/components/ui/calendar/CalendarPrevButton.vue.d.ts.map +1 -0
  123. package/dist/components/ui/calendar/index.d.ts +14 -0
  124. package/dist/components/ui/calendar/index.d.ts.map +1 -0
  125. package/dist/components/ui/card/Card.vue.d.ts +18 -0
  126. package/dist/components/ui/card/Card.vue.d.ts.map +1 -0
  127. package/dist/components/ui/card/CardAction.vue.d.ts +18 -0
  128. package/dist/components/ui/card/CardAction.vue.d.ts.map +1 -0
  129. package/dist/components/ui/card/CardContent.vue.d.ts +18 -0
  130. package/dist/components/ui/card/CardContent.vue.d.ts.map +1 -0
  131. package/dist/components/ui/card/CardDescription.vue.d.ts +18 -0
  132. package/dist/components/ui/card/CardDescription.vue.d.ts.map +1 -0
  133. package/dist/components/ui/card/CardFooter.vue.d.ts +18 -0
  134. package/dist/components/ui/card/CardFooter.vue.d.ts.map +1 -0
  135. package/dist/components/ui/card/CardHeader.vue.d.ts +18 -0
  136. package/dist/components/ui/card/CardHeader.vue.d.ts.map +1 -0
  137. package/dist/components/ui/card/CardTitle.vue.d.ts +18 -0
  138. package/dist/components/ui/card/CardTitle.vue.d.ts.map +1 -0
  139. package/dist/components/ui/card/index.d.ts +8 -0
  140. package/dist/components/ui/card/index.d.ts.map +1 -0
  141. package/dist/components/ui/carousel/Carousel.vue.d.ts +38 -0
  142. package/dist/components/ui/carousel/Carousel.vue.d.ts.map +1 -0
  143. package/dist/components/ui/carousel/CarouselContent.vue.d.ts +15 -0
  144. package/dist/components/ui/carousel/CarouselContent.vue.d.ts.map +1 -0
  145. package/dist/components/ui/carousel/CarouselItem.vue.d.ts +15 -0
  146. package/dist/components/ui/carousel/CarouselItem.vue.d.ts.map +1 -0
  147. package/dist/components/ui/carousel/CarouselNext.vue.d.ts +23 -0
  148. package/dist/components/ui/carousel/CarouselNext.vue.d.ts.map +1 -0
  149. package/dist/components/ui/carousel/CarouselPrevious.vue.d.ts +23 -0
  150. package/dist/components/ui/carousel/CarouselPrevious.vue.d.ts.map +1 -0
  151. package/dist/components/ui/carousel/index.d.ts +8 -0
  152. package/dist/components/ui/carousel/index.d.ts.map +1 -0
  153. package/dist/components/ui/carousel/interface.d.ts +21 -0
  154. package/dist/components/ui/carousel/interface.d.ts.map +1 -0
  155. package/dist/components/ui/carousel/useCarousel.d.ts +21 -0
  156. package/dist/components/ui/carousel/useCarousel.d.ts.map +1 -0
  157. package/dist/components/ui/checkbox/Checkbox.vue.d.ts +26 -0
  158. package/dist/components/ui/checkbox/Checkbox.vue.d.ts.map +1 -0
  159. package/dist/components/ui/checkbox/index.d.ts +2 -0
  160. package/dist/components/ui/checkbox/index.d.ts.map +1 -0
  161. package/dist/components/ui/collapsible/Collapsible.vue.d.ts +21 -0
  162. package/dist/components/ui/collapsible/Collapsible.vue.d.ts.map +1 -0
  163. package/dist/components/ui/collapsible/CollapsibleContent.vue.d.ts +15 -0
  164. package/dist/components/ui/collapsible/CollapsibleContent.vue.d.ts.map +1 -0
  165. package/dist/components/ui/collapsible/CollapsibleTrigger.vue.d.ts +15 -0
  166. package/dist/components/ui/collapsible/CollapsibleTrigger.vue.d.ts.map +1 -0
  167. package/dist/components/ui/collapsible/index.d.ts +4 -0
  168. package/dist/components/ui/collapsible/index.d.ts.map +1 -0
  169. package/dist/components/ui/dialog/Dialog.vue.d.ts +22 -0
  170. package/dist/components/ui/dialog/Dialog.vue.d.ts.map +1 -0
  171. package/dist/components/ui/dialog/DialogClose.vue.d.ts +15 -0
  172. package/dist/components/ui/dialog/DialogClose.vue.d.ts.map +1 -0
  173. package/dist/components/ui/dialog/DialogContent.vue.d.ts +36 -0
  174. package/dist/components/ui/dialog/DialogContent.vue.d.ts.map +1 -0
  175. package/dist/components/ui/dialog/DialogDescription.vue.d.ts +19 -0
  176. package/dist/components/ui/dialog/DialogDescription.vue.d.ts.map +1 -0
  177. package/dist/components/ui/dialog/DialogFooter.vue.d.ts +18 -0
  178. package/dist/components/ui/dialog/DialogFooter.vue.d.ts.map +1 -0
  179. package/dist/components/ui/dialog/DialogHeader.vue.d.ts +18 -0
  180. package/dist/components/ui/dialog/DialogHeader.vue.d.ts.map +1 -0
  181. package/dist/components/ui/dialog/DialogOverlay.vue.d.ts +19 -0
  182. package/dist/components/ui/dialog/DialogOverlay.vue.d.ts.map +1 -0
  183. package/dist/components/ui/dialog/DialogScrollContent.vue.d.ts +33 -0
  184. package/dist/components/ui/dialog/DialogScrollContent.vue.d.ts.map +1 -0
  185. package/dist/components/ui/dialog/DialogTitle.vue.d.ts +19 -0
  186. package/dist/components/ui/dialog/DialogTitle.vue.d.ts.map +1 -0
  187. package/dist/components/ui/dialog/DialogTrigger.vue.d.ts +15 -0
  188. package/dist/components/ui/dialog/DialogTrigger.vue.d.ts.map +1 -0
  189. package/dist/components/ui/dialog/index.d.ts +11 -0
  190. package/dist/components/ui/dialog/index.d.ts.map +1 -0
  191. package/dist/components/ui/dropdown-menu/DropdownMenu.vue.d.ts +21 -0
  192. package/dist/components/ui/dropdown-menu/DropdownMenu.vue.d.ts.map +1 -0
  193. package/dist/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +27 -0
  194. package/dist/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts.map +1 -0
  195. package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts +33 -0
  196. package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts.map +1 -0
  197. package/dist/components/ui/dropdown-menu/DropdownMenuGroup.vue.d.ts +15 -0
  198. package/dist/components/ui/dropdown-menu/DropdownMenuGroup.vue.d.ts.map +1 -0
  199. package/dist/components/ui/dropdown-menu/DropdownMenuItem.vue.d.ts +23 -0
  200. package/dist/components/ui/dropdown-menu/DropdownMenuItem.vue.d.ts.map +1 -0
  201. package/dist/components/ui/dropdown-menu/DropdownMenuLabel.vue.d.ts +20 -0
  202. package/dist/components/ui/dropdown-menu/DropdownMenuLabel.vue.d.ts.map +1 -0
  203. package/dist/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts +19 -0
  204. package/dist/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts.map +1 -0
  205. package/dist/components/ui/dropdown-menu/DropdownMenuRadioItem.vue.d.ts +25 -0
  206. package/dist/components/ui/dropdown-menu/DropdownMenuRadioItem.vue.d.ts.map +1 -0
  207. package/dist/components/ui/dropdown-menu/DropdownMenuSeparator.vue.d.ts +9 -0
  208. package/dist/components/ui/dropdown-menu/DropdownMenuSeparator.vue.d.ts.map +1 -0
  209. package/dist/components/ui/dropdown-menu/DropdownMenuShortcut.vue.d.ts +18 -0
  210. package/dist/components/ui/dropdown-menu/DropdownMenuShortcut.vue.d.ts.map +1 -0
  211. package/dist/components/ui/dropdown-menu/DropdownMenuSub.vue.d.ts +21 -0
  212. package/dist/components/ui/dropdown-menu/DropdownMenuSub.vue.d.ts.map +1 -0
  213. package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +35 -0
  214. package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts.map +1 -0
  215. package/dist/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +20 -0
  216. package/dist/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts.map +1 -0
  217. package/dist/components/ui/dropdown-menu/DropdownMenuTrigger.vue.d.ts +15 -0
  218. package/dist/components/ui/dropdown-menu/DropdownMenuTrigger.vue.d.ts.map +1 -0
  219. package/dist/components/ui/dropdown-menu/index.d.ts +16 -0
  220. package/dist/components/ui/dropdown-menu/index.d.ts.map +1 -0
  221. package/dist/components/ui/empty/Empty.vue.d.ts +18 -0
  222. package/dist/components/ui/empty/Empty.vue.d.ts.map +1 -0
  223. package/dist/components/ui/empty/EmptyContent.vue.d.ts +18 -0
  224. package/dist/components/ui/empty/EmptyContent.vue.d.ts.map +1 -0
  225. package/dist/components/ui/empty/EmptyDescription.vue.d.ts +18 -0
  226. package/dist/components/ui/empty/EmptyDescription.vue.d.ts.map +1 -0
  227. package/dist/components/ui/empty/EmptyHeader.vue.d.ts +18 -0
  228. package/dist/components/ui/empty/EmptyHeader.vue.d.ts.map +1 -0
  229. package/dist/components/ui/empty/EmptyMedia.vue.d.ts +20 -0
  230. package/dist/components/ui/empty/EmptyMedia.vue.d.ts.map +1 -0
  231. package/dist/components/ui/empty/EmptyTitle.vue.d.ts +18 -0
  232. package/dist/components/ui/empty/EmptyTitle.vue.d.ts.map +1 -0
  233. package/dist/components/ui/empty/index.d.ts +12 -0
  234. package/dist/components/ui/empty/index.d.ts.map +1 -0
  235. package/dist/components/ui/input/Input.vue.d.ts +14 -0
  236. package/dist/components/ui/input/Input.vue.d.ts.map +1 -0
  237. package/dist/components/ui/input/index.d.ts +2 -0
  238. package/dist/components/ui/input/index.d.ts.map +1 -0
  239. package/dist/components/ui/label/Label.vue.d.ts +19 -0
  240. package/dist/components/ui/label/Label.vue.d.ts.map +1 -0
  241. package/dist/components/ui/label/index.d.ts +2 -0
  242. package/dist/components/ui/label/index.d.ts.map +1 -0
  243. package/dist/components/ui/native-select/NativeSelect.vue.d.ts +24 -0
  244. package/dist/components/ui/native-select/NativeSelect.vue.d.ts.map +1 -0
  245. package/dist/components/ui/native-select/NativeSelectOptGroup.vue.d.ts +402 -0
  246. package/dist/components/ui/native-select/NativeSelectOptGroup.vue.d.ts.map +1 -0
  247. package/dist/components/ui/native-select/NativeSelectOption.vue.d.ts +406 -0
  248. package/dist/components/ui/native-select/NativeSelectOption.vue.d.ts.map +1 -0
  249. package/dist/components/ui/native-select/index.d.ts +4 -0
  250. package/dist/components/ui/native-select/index.d.ts.map +1 -0
  251. package/dist/components/ui/popover/Popover.vue.d.ts +22 -0
  252. package/dist/components/ui/popover/Popover.vue.d.ts.map +1 -0
  253. package/dist/components/ui/popover/PopoverAnchor.vue.d.ts +15 -0
  254. package/dist/components/ui/popover/PopoverAnchor.vue.d.ts.map +1 -0
  255. package/dist/components/ui/popover/PopoverContent.vue.d.ts +36 -0
  256. package/dist/components/ui/popover/PopoverContent.vue.d.ts.map +1 -0
  257. package/dist/components/ui/popover/PopoverTrigger.vue.d.ts +15 -0
  258. package/dist/components/ui/popover/PopoverTrigger.vue.d.ts.map +1 -0
  259. package/dist/components/ui/popover/index.d.ts +5 -0
  260. package/dist/components/ui/popover/index.d.ts.map +1 -0
  261. package/dist/components/ui/select/Select.vue.d.ts +25 -0
  262. package/dist/components/ui/select/Select.vue.d.ts.map +1 -0
  263. package/dist/components/ui/select/SelectContent.vue.d.ts +29 -0
  264. package/dist/components/ui/select/SelectContent.vue.d.ts.map +1 -0
  265. package/dist/components/ui/select/SelectGroup.vue.d.ts +15 -0
  266. package/dist/components/ui/select/SelectGroup.vue.d.ts.map +1 -0
  267. package/dist/components/ui/select/SelectItem.vue.d.ts +21 -0
  268. package/dist/components/ui/select/SelectItem.vue.d.ts.map +1 -0
  269. package/dist/components/ui/select/SelectItemText.vue.d.ts +15 -0
  270. package/dist/components/ui/select/SelectItemText.vue.d.ts.map +1 -0
  271. package/dist/components/ui/select/SelectLabel.vue.d.ts +19 -0
  272. package/dist/components/ui/select/SelectLabel.vue.d.ts.map +1 -0
  273. package/dist/components/ui/select/SelectScrollDownButton.vue.d.ts +19 -0
  274. package/dist/components/ui/select/SelectScrollDownButton.vue.d.ts.map +1 -0
  275. package/dist/components/ui/select/SelectScrollUpButton.vue.d.ts +19 -0
  276. package/dist/components/ui/select/SelectScrollUpButton.vue.d.ts.map +1 -0
  277. package/dist/components/ui/select/SelectSeparator.vue.d.ts +9 -0
  278. package/dist/components/ui/select/SelectSeparator.vue.d.ts.map +1 -0
  279. package/dist/components/ui/select/SelectTrigger.vue.d.ts +22 -0
  280. package/dist/components/ui/select/SelectTrigger.vue.d.ts.map +1 -0
  281. package/dist/components/ui/select/SelectValue.vue.d.ts +15 -0
  282. package/dist/components/ui/select/SelectValue.vue.d.ts.map +1 -0
  283. package/dist/components/ui/select/index.d.ts +12 -0
  284. package/dist/components/ui/select/index.d.ts.map +1 -0
  285. package/dist/components/ui/separator/Separator.vue.d.ts +12 -0
  286. package/dist/components/ui/separator/Separator.vue.d.ts.map +1 -0
  287. package/dist/components/ui/separator/index.d.ts +2 -0
  288. package/dist/components/ui/separator/index.d.ts.map +1 -0
  289. package/dist/components/ui/sheet/Sheet.vue.d.ts +22 -0
  290. package/dist/components/ui/sheet/Sheet.vue.d.ts.map +1 -0
  291. package/dist/components/ui/sheet/SheetClose.vue.d.ts +15 -0
  292. package/dist/components/ui/sheet/SheetClose.vue.d.ts.map +1 -0
  293. package/dist/components/ui/sheet/SheetContent.vue.d.ts +36 -0
  294. package/dist/components/ui/sheet/SheetContent.vue.d.ts.map +1 -0
  295. package/dist/components/ui/sheet/SheetDescription.vue.d.ts +19 -0
  296. package/dist/components/ui/sheet/SheetDescription.vue.d.ts.map +1 -0
  297. package/dist/components/ui/sheet/SheetFooter.vue.d.ts +18 -0
  298. package/dist/components/ui/sheet/SheetFooter.vue.d.ts.map +1 -0
  299. package/dist/components/ui/sheet/SheetHeader.vue.d.ts +18 -0
  300. package/dist/components/ui/sheet/SheetHeader.vue.d.ts.map +1 -0
  301. package/dist/components/ui/sheet/SheetOverlay.vue.d.ts +19 -0
  302. package/dist/components/ui/sheet/SheetOverlay.vue.d.ts.map +1 -0
  303. package/dist/components/ui/sheet/SheetTitle.vue.d.ts +19 -0
  304. package/dist/components/ui/sheet/SheetTitle.vue.d.ts.map +1 -0
  305. package/dist/components/ui/sheet/SheetTrigger.vue.d.ts +15 -0
  306. package/dist/components/ui/sheet/SheetTrigger.vue.d.ts.map +1 -0
  307. package/dist/components/ui/sheet/index.d.ts +9 -0
  308. package/dist/components/ui/sheet/index.d.ts.map +1 -0
  309. package/dist/components/ui/sidebar/Sidebar.vue.d.ts +23 -0
  310. package/dist/components/ui/sidebar/Sidebar.vue.d.ts.map +1 -0
  311. package/dist/components/ui/sidebar/SidebarContent.vue.d.ts +18 -0
  312. package/dist/components/ui/sidebar/SidebarContent.vue.d.ts.map +1 -0
  313. package/dist/components/ui/sidebar/SidebarFooter.vue.d.ts +18 -0
  314. package/dist/components/ui/sidebar/SidebarFooter.vue.d.ts.map +1 -0
  315. package/dist/components/ui/sidebar/SidebarGroup.vue.d.ts +18 -0
  316. package/dist/components/ui/sidebar/SidebarGroup.vue.d.ts.map +1 -0
  317. package/dist/components/ui/sidebar/SidebarGroupAction.vue.d.ts +19 -0
  318. package/dist/components/ui/sidebar/SidebarGroupAction.vue.d.ts.map +1 -0
  319. package/dist/components/ui/sidebar/SidebarGroupContent.vue.d.ts +18 -0
  320. package/dist/components/ui/sidebar/SidebarGroupContent.vue.d.ts.map +1 -0
  321. package/dist/components/ui/sidebar/SidebarGroupLabel.vue.d.ts +19 -0
  322. package/dist/components/ui/sidebar/SidebarGroupLabel.vue.d.ts.map +1 -0
  323. package/dist/components/ui/sidebar/SidebarHeader.vue.d.ts +18 -0
  324. package/dist/components/ui/sidebar/SidebarHeader.vue.d.ts.map +1 -0
  325. package/dist/components/ui/sidebar/SidebarInput.vue.d.ts +18 -0
  326. package/dist/components/ui/sidebar/SidebarInput.vue.d.ts.map +1 -0
  327. package/dist/components/ui/sidebar/SidebarInset.vue.d.ts +18 -0
  328. package/dist/components/ui/sidebar/SidebarInset.vue.d.ts.map +1 -0
  329. package/dist/components/ui/sidebar/SidebarMenu.vue.d.ts +18 -0
  330. package/dist/components/ui/sidebar/SidebarMenu.vue.d.ts.map +1 -0
  331. package/dist/components/ui/sidebar/SidebarMenuAction.vue.d.ts +22 -0
  332. package/dist/components/ui/sidebar/SidebarMenuAction.vue.d.ts.map +1 -0
  333. package/dist/components/ui/sidebar/SidebarMenuBadge.vue.d.ts +18 -0
  334. package/dist/components/ui/sidebar/SidebarMenuBadge.vue.d.ts.map +1 -0
  335. package/dist/components/ui/sidebar/SidebarMenuButton.vue.d.ts +25 -0
  336. package/dist/components/ui/sidebar/SidebarMenuButton.vue.d.ts.map +1 -0
  337. package/dist/components/ui/sidebar/SidebarMenuButtonChild.vue.d.ts +27 -0
  338. package/dist/components/ui/sidebar/SidebarMenuButtonChild.vue.d.ts.map +1 -0
  339. package/dist/components/ui/sidebar/SidebarMenuItem.vue.d.ts +18 -0
  340. package/dist/components/ui/sidebar/SidebarMenuItem.vue.d.ts.map +1 -0
  341. package/dist/components/ui/sidebar/SidebarMenuSkeleton.vue.d.ts +9 -0
  342. package/dist/components/ui/sidebar/SidebarMenuSkeleton.vue.d.ts.map +1 -0
  343. package/dist/components/ui/sidebar/SidebarMenuSub.vue.d.ts +18 -0
  344. package/dist/components/ui/sidebar/SidebarMenuSub.vue.d.ts.map +1 -0
  345. package/dist/components/ui/sidebar/SidebarMenuSubButton.vue.d.ts +24 -0
  346. package/dist/components/ui/sidebar/SidebarMenuSubButton.vue.d.ts.map +1 -0
  347. package/dist/components/ui/sidebar/SidebarMenuSubItem.vue.d.ts +18 -0
  348. package/dist/components/ui/sidebar/SidebarMenuSubItem.vue.d.ts.map +1 -0
  349. package/dist/components/ui/sidebar/SidebarProvider.vue.d.ts +27 -0
  350. package/dist/components/ui/sidebar/SidebarProvider.vue.d.ts.map +1 -0
  351. package/dist/components/ui/sidebar/SidebarRail.vue.d.ts +18 -0
  352. package/dist/components/ui/sidebar/SidebarRail.vue.d.ts.map +1 -0
  353. package/dist/components/ui/sidebar/SidebarSeparator.vue.d.ts +18 -0
  354. package/dist/components/ui/sidebar/SidebarSeparator.vue.d.ts.map +1 -0
  355. package/dist/components/ui/sidebar/SidebarTrigger.vue.d.ts +8 -0
  356. package/dist/components/ui/sidebar/SidebarTrigger.vue.d.ts.map +1 -0
  357. package/dist/components/ui/sidebar/index.d.ts +38 -0
  358. package/dist/components/ui/sidebar/index.d.ts.map +1 -0
  359. package/dist/components/ui/sidebar/utils.d.ts +57 -0
  360. package/dist/components/ui/sidebar/utils.d.ts.map +1 -0
  361. package/dist/components/ui/skeleton/Skeleton.vue.d.ts +8 -0
  362. package/dist/components/ui/skeleton/Skeleton.vue.d.ts.map +1 -0
  363. package/dist/components/ui/skeleton/index.d.ts +2 -0
  364. package/dist/components/ui/skeleton/index.d.ts.map +1 -0
  365. package/dist/components/ui/switch/Switch.vue.d.ts +25 -0
  366. package/dist/components/ui/switch/Switch.vue.d.ts.map +1 -0
  367. package/dist/components/ui/switch/index.d.ts +2 -0
  368. package/dist/components/ui/switch/index.d.ts.map +1 -0
  369. package/dist/components/ui/table/Table.vue.d.ts +18 -0
  370. package/dist/components/ui/table/Table.vue.d.ts.map +1 -0
  371. package/dist/components/ui/table/TableBody.vue.d.ts +18 -0
  372. package/dist/components/ui/table/TableBody.vue.d.ts.map +1 -0
  373. package/dist/components/ui/table/TableCaption.vue.d.ts +18 -0
  374. package/dist/components/ui/table/TableCaption.vue.d.ts.map +1 -0
  375. package/dist/components/ui/table/TableCell.vue.d.ts +18 -0
  376. package/dist/components/ui/table/TableCell.vue.d.ts.map +1 -0
  377. package/dist/components/ui/table/TableEmpty.vue.d.ts +21 -0
  378. package/dist/components/ui/table/TableEmpty.vue.d.ts.map +1 -0
  379. package/dist/components/ui/table/TableFooter.vue.d.ts +18 -0
  380. package/dist/components/ui/table/TableFooter.vue.d.ts.map +1 -0
  381. package/dist/components/ui/table/TableHead.vue.d.ts +18 -0
  382. package/dist/components/ui/table/TableHead.vue.d.ts.map +1 -0
  383. package/dist/components/ui/table/TableHeader.vue.d.ts +18 -0
  384. package/dist/components/ui/table/TableHeader.vue.d.ts.map +1 -0
  385. package/dist/components/ui/table/TableRow.vue.d.ts +18 -0
  386. package/dist/components/ui/table/TableRow.vue.d.ts.map +1 -0
  387. package/dist/components/ui/table/index.d.ts +10 -0
  388. package/dist/components/ui/table/index.d.ts.map +1 -0
  389. package/dist/components/ui/table/utils.d.ts +4 -0
  390. package/dist/components/ui/table/utils.d.ts.map +1 -0
  391. package/dist/components/ui/textarea/Textarea.vue.d.ts +14 -0
  392. package/dist/components/ui/textarea/Textarea.vue.d.ts.map +1 -0
  393. package/dist/components/ui/textarea/index.d.ts +2 -0
  394. package/dist/components/ui/textarea/index.d.ts.map +1 -0
  395. package/dist/components/ui/tooltip/Tooltip.vue.d.ts +21 -0
  396. package/dist/components/ui/tooltip/Tooltip.vue.d.ts.map +1 -0
  397. package/dist/components/ui/tooltip/TooltipContent.vue.d.ts +27 -0
  398. package/dist/components/ui/tooltip/TooltipContent.vue.d.ts.map +1 -0
  399. package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts +17 -0
  400. package/dist/components/ui/tooltip/TooltipProvider.vue.d.ts.map +1 -0
  401. package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts +15 -0
  402. package/dist/components/ui/tooltip/TooltipTrigger.vue.d.ts.map +1 -0
  403. package/dist/components/ui/tooltip/index.d.ts +5 -0
  404. package/dist/components/ui/tooltip/index.d.ts.map +1 -0
  405. package/dist/composables/useJsBaoDataLoader.d.ts +79 -0
  406. package/dist/composables/useJsBaoDataLoader.d.ts.map +1 -0
  407. package/dist/composables/useTheme.d.ts +34 -0
  408. package/dist/composables/useTheme.d.ts.map +1 -0
  409. package/dist/createPrimitiveApp.d.ts +74 -0
  410. package/dist/createPrimitiveApp.d.ts.map +1 -0
  411. package/dist/index.d.ts +45 -0
  412. package/dist/index.d.ts.map +1 -0
  413. package/dist/index.js +29781 -0
  414. package/dist/index.umd.cjs +8 -0
  415. package/dist/layouts/PrimitiveAppLayout.vue.d.ts +34 -0
  416. package/dist/layouts/PrimitiveAppLayout.vue.d.ts.map +1 -0
  417. package/dist/layouts/PrimitiveLoginLayout.vue.d.ts +24 -0
  418. package/dist/layouts/PrimitiveLoginLayout.vue.d.ts.map +1 -0
  419. package/dist/layouts/PrimitiveStaticLayout.vue.d.ts +17 -0
  420. package/dist/layouts/PrimitiveStaticLayout.vue.d.ts.map +1 -0
  421. package/dist/lib/logger.d.ts +20 -0
  422. package/dist/lib/logger.d.ts.map +1 -0
  423. package/dist/lib/routeOrUrl.d.ts +25 -0
  424. package/dist/lib/routeOrUrl.d.ts.map +1 -0
  425. package/dist/lib/utils.d.ts +3 -0
  426. package/dist/lib/utils.d.ts.map +1 -0
  427. package/dist/models/UserPref.d.ts +10 -0
  428. package/dist/models/UserPref.d.ts.map +1 -0
  429. package/dist/models/generated/index.d.ts +3 -0
  430. package/dist/models/generated/index.d.ts.map +1 -0
  431. package/dist/models/generated/relationships.d.ts +2 -0
  432. package/dist/models/generated/relationships.d.ts.map +1 -0
  433. package/dist/pages/PrimitiveNotFound.vue.d.ts +4 -0
  434. package/dist/pages/PrimitiveNotFound.vue.d.ts.map +1 -0
  435. package/dist/router/primitiveRouter.d.ts +105 -0
  436. package/dist/router/primitiveRouter.d.ts.map +1 -0
  437. package/dist/services/JsBaoClientService.d.ts +85 -0
  438. package/dist/services/JsBaoClientService.d.ts.map +1 -0
  439. package/dist/stores/appConfigStore.d.ts +98 -0
  440. package/dist/stores/appConfigStore.d.ts.map +1 -0
  441. package/dist/stores/breadcrumbsStore.d.ts +52 -0
  442. package/dist/stores/breadcrumbsStore.d.ts.map +1 -0
  443. package/dist/stores/documentDebuggerStore.d.ts +43 -0
  444. package/dist/stores/documentDebuggerStore.d.ts.map +1 -0
  445. package/dist/stores/jsBaoDocumentsStore.d.ts +320 -0
  446. package/dist/stores/jsBaoDocumentsStore.d.ts.map +1 -0
  447. package/dist/stores/multiDocumentStore.d.ts +68 -0
  448. package/dist/stores/multiDocumentStore.d.ts.map +1 -0
  449. package/dist/stores/navigationStore.d.ts +192 -0
  450. package/dist/stores/navigationStore.d.ts.map +1 -0
  451. package/dist/stores/singleDocumentStore.d.ts +109 -0
  452. package/dist/stores/singleDocumentStore.d.ts.map +1 -0
  453. package/dist/stores/userStore.d.ts +142 -0
  454. package/dist/stores/userStore.d.ts.map +1 -0
  455. package/dist/types/app.d.ts +23 -0
  456. package/dist/types/app.d.ts.map +1 -0
  457. package/dist/types/navigation.d.ts +143 -0
  458. package/dist/types/navigation.d.ts.map +1 -0
  459. package/dist/types/router.d.ts +14 -0
  460. package/dist/types/router.d.ts.map +1 -0
  461. package/package.json +107 -0
  462. package/src/style.css +164 -0
package/README.md ADDED
@@ -0,0 +1,1202 @@
1
+ # primitive-app
2
+
3
+ Primitive App is a **Vue 3–first helper library** that wires a Vue + Pinia + vue-router application into **js-bao-wss-client** with a cohesive set of:
4
+
5
+ - **Bootstrapping helpers** (`createPrimitiveApp`)
6
+ - **Routing & auth helpers** (`createPrimitiveRouter`, `PrimitiveRouterMeta`)
7
+ - **Stores** (app config, user, navigation, breadcrumbs, document stores)
8
+ - **Layouts & components** (`PrimitiveAppLayout`, auth layouts, navigation & document shells)
9
+ - **Data-loading composables** (`useJsBaoDataLoader`)
10
+ - **Debugging & test harness utilities** (Document Debugger, Test Runner)
11
+
12
+ It is designed to be used in apps that already provide:
13
+
14
+ - Vue 3
15
+ - Pinia
16
+ - vue-router
17
+ - Tailwind (or compatible utility CSS)
18
+ - `js-bao-wss-client` (and `js-bao` models in your app)
19
+
20
+ For concrete examples, see the demo app available at https://primitive-app-demo.primitive.app/
21
+
22
+ ## It's recommended that apps wanting to use this library start by cloning https://github.com/Primitive-Labs/primitive-app-template
23
+
24
+ ## Documentation
25
+
26
+ For guides and API reference docs, see **Primitive Docs**: https://primitive-labs.github.io/primitive-docs/
27
+
28
+ ## High-level architecture
29
+
30
+ At a high level, `primitive-app` provides:
31
+
32
+ - **Bootstrapping**
33
+ - `createPrimitiveApp` – mount your Vue app, wire in Pinia and vue-router, set up js-bao, and initialize shared stores.
34
+ - **Routing & auth**
35
+ - `createPrimitiveRouter`, `AuthLevel`, `PrimitiveRouterMeta` – auth-guarded routes with breadcrumb metadata.
36
+ - **Stores**
37
+ - Configuration: `useAppConfigStore`, `useNavigationStore`, `useBreadcrumbsStore`
38
+ - User: `useUserStore`
39
+ - Documents: `useJsBaoDocumentsStore`, `useSingleDocumentStore`, `useMultiDocumentStore`
40
+ - **Layouts & components**
41
+ - `PrimitiveAppLayout`, `PrimitiveLoginLayout`, `PrimitiveStaticLayout`, navigation components, document management components, shared utilities.
42
+ - **Data loading**
43
+ - `useJsBaoDataLoader` – standard pattern for loading data (typically js-bao models) with subscriptions and a document readiness gate.
44
+ - **Debugging & test harness**
45
+ - `PrimitiveDebuggingSuite`, `PrimitiveTestRunner`, `DocumentDebugger`, and related pages for app-level integration tests and data inspection.
46
+
47
+ ---
48
+
49
+ ## Bootstrapping with `createPrimitiveApp`
50
+
51
+ The recommended way to start a new app is to let `primitive-app` own the **initialization order**:
52
+
53
+ 1. Initialize js-bao and the user store.
54
+ 2. Initialize app config.
55
+ 3. Wire the document store (single-document, multi-document, or none).
56
+ 4. Wire breadcrumbs to the router.
57
+ 5. Wire navigation to the router.
58
+ 6. Mount your root component.
59
+
60
+ ```ts
61
+ // src/main.ts
62
+ import { createPrimitiveApp } from "primitive-app";
63
+ import App from "./App.vue";
64
+ import router from "./router/routes";
65
+ import { getAppConfig, getSingleDocumentConfig } from "./config/appConfig";
66
+ import { getJsBaoConfig, getLogLevel } from "./config/envConfig";
67
+ import { getNavigationConfig } from "./config/navigationConfig";
68
+
69
+ void createPrimitiveApp({
70
+ mainComponent: App,
71
+ router,
72
+ getAppConfig,
73
+ getJsBaoConfig,
74
+ getNavigationConfig,
75
+ getSingleDocumentConfig,
76
+ loginUrl: "/login",
77
+ getLogLevel,
78
+ });
79
+ ```
80
+
81
+ ### `PrimitiveAppBootstrapOptions`
82
+
83
+ `createPrimitiveApp` accepts:
84
+
85
+ - **mainComponent**: your root Vue component (often `App.vue`).
86
+ - **router**: a `vue-router` instance (typically created via `createPrimitiveRouter`, see below).
87
+ - **getAppConfig**: `() => InitializeAppConfigOptions`
88
+ - **getJsBaoConfig**: `() => JsBaoClientOptions` (from `js-bao-wss-client`)
89
+ - **getNavigationConfig?**: `() => NavigationConfig`
90
+ - **getSingleDocumentConfig?**: `() => InitializeSingleDocumentOptions`
91
+ - **loginUrl**: path to your login route (e.g. `"/login"`), used by auth flows.
92
+ - **getLogLevel?**: `() => LogLevel` – overrides internal logging defaults.
93
+ - **mountTarget?**: CSS selector or DOM element (defaults to `"#app"`).
94
+
95
+ > **Note:** The `documentStoreMode` in your `getAppConfig` determines which document store(s) are initialized:
96
+ >
97
+ > - `DocumentStoreMode.None`: No document store is initialized.
98
+ > - `DocumentStoreMode.SingleDocument` or `DocumentStoreMode.SingleDocumentWithSwitching`: Requires `getSingleDocumentConfig`.
99
+ > - `DocumentStoreMode.MultiDoc`: Initializes `jsBaoDocumentsStore` on auth; apps register collections dynamically via `multiDocumentStore.registerCollection()`.
100
+
101
+ ---
102
+
103
+ ## Routing & auth: `createPrimitiveRouter` and `PrimitiveRouterMeta`
104
+
105
+ Primitive App supplies an opinionated `vue-router` factory that:
106
+
107
+ - **Enforces auth** based on route metadata.
108
+ - **Redirects unauthenticated users** to a configured login route or URL.
109
+ - **Redirects non-admin users** away from admin-only routes.
110
+ - **Provides breadcrumb metadata** for the breadcrumbs store and layouts.
111
+
112
+ ### Defining routes with `primitiveRouterMeta`
113
+
114
+ ```ts
115
+ // src/router/routes.ts
116
+ import type { RouteRecordRaw } from "vue-router";
117
+ import {
118
+ createPrimitiveRouter,
119
+ PrimitiveAppLayout,
120
+ PrimitiveLoginLayout,
121
+ PrimitiveStaticLayout,
122
+ } from "primitive-app";
123
+ import LoginPage from "@/pages/LoginPage.vue";
124
+ import HomePage from "@/pages/HomePage.vue";
125
+ import TermsPage from "@/pages/TermsPage.vue";
126
+
127
+ const routes: RouteRecordRaw[] = [
128
+ {
129
+ path: "/",
130
+ component: PrimitiveAppLayout,
131
+ children: [
132
+ {
133
+ path: "",
134
+ name: "home",
135
+ component: HomePage,
136
+ meta: {
137
+ primitiveRouterMeta: {
138
+ requireAuth: "member", // "none" | "member" | "admin"
139
+ breadcrumb: { title: "Home" },
140
+ },
141
+ },
142
+ },
143
+ {
144
+ path: "routing/:slug",
145
+ name: "routing-slug",
146
+ component: () => import("@/pages/RoutingExamplePage.vue"),
147
+ meta: {
148
+ primitiveRouterMeta: {
149
+ requireAuth: "member",
150
+ breadcrumb: {
151
+ title: "Routing",
152
+ generator: (params) => String(params.slug ?? "Routing"),
153
+ },
154
+ },
155
+ },
156
+ },
157
+ ],
158
+ },
159
+ {
160
+ path: "/",
161
+ component: PrimitiveLoginLayout,
162
+ children: [{ path: "login", name: "login", component: LoginPage }],
163
+ },
164
+ {
165
+ path: "/",
166
+ component: PrimitiveStaticLayout,
167
+ children: [
168
+ {
169
+ path: "terms-of-service",
170
+ name: "terms-of-service",
171
+ component: TermsPage,
172
+ },
173
+ ],
174
+ },
175
+ ];
176
+
177
+ const router = createPrimitiveRouter({
178
+ routes,
179
+ // or loginUrl: "https://auth.example.com/login"
180
+ loginRouteName: "login",
181
+ });
182
+
183
+ export default router;
184
+ ```
185
+
186
+ ### Auth behavior
187
+
188
+ - If `primitiveRouterMeta.requireAuth` is:
189
+ - `"none"`: route is public.
190
+ - `"member"`: user must be authenticated.
191
+ - `"admin"`: user must be authenticated and `useUserStore().isAdmin === true`.
192
+ - For member/admin routes:
193
+ - Unauthenticated users are redirected to the login route or URL with a `continueURL` query parameter.
194
+ - Non-admin users on admin routes are redirected to the app's `homeRouteName` from `useAppConfigStore`.
195
+
196
+ Breadcrumb metadata is consumed by `useBreadcrumbsStore` and `PrimitiveAppLayout` to render breadcrumb trails and page titles.
197
+
198
+ ---
199
+
200
+ ## Stores
201
+
202
+ Primitive App provides several Pinia stores organized into three categories:
203
+
204
+ 1. **Configuration stores** – Static app configuration and UI state
205
+ 2. **User store** – Authentication and user preferences
206
+ 3. **Document stores** – js-bao document management
207
+
208
+ ### Configuration stores
209
+
210
+ #### App config store: `useAppConfigStore`
211
+
212
+ The app config store centralizes static configuration:
213
+
214
+ | Option | Type | Description |
215
+ | --------------------- | ------------------- | ------------------------------------------------ |
216
+ | `appName` | `string` | Display name for the app and default page titles |
217
+ | `appIcon` | `Component` | Vue component rendered in login/static layouts |
218
+ | `homeRouteName` | `RouteRecordName` | Route used as the "home" destination |
219
+ | `loginRouteName` | `RouteRecordName` | Named login route |
220
+ | `documentStoreMode` | `DocumentStoreMode` | Controls document store behavior (see below) |
221
+ | `pageTitleFormatter?` | `function` | Custom page title formatter |
222
+ | `loadingComponent?` | `Component` | Global loading component used by layouts |
223
+
224
+ **DocumentStoreMode options:**
225
+
226
+ - `DocumentStoreMode.None` – No document store initialized
227
+ - `DocumentStoreMode.SingleDocument` – Single document per user, no switching UI
228
+ - `DocumentStoreMode.SingleDocumentWithSwitching` – Single active document with switching/sharing UI
229
+ - `DocumentStoreMode.MultiDoc` – Multiple document collections managed dynamically
230
+
231
+ Example config:
232
+
233
+ ```ts
234
+ // src/config/appConfig.ts
235
+ import appIconUrl from "@/assets/app-icon.png";
236
+ import { DocumentStoreMode, type AppIconComponent } from "primitive-app";
237
+ import { defineComponent, h } from "vue";
238
+ import type { RouteLocationNormalizedLoaded } from "vue-router";
239
+
240
+ const AppIcon: AppIconComponent = defineComponent({
241
+ name: "AppIcon",
242
+ setup() {
243
+ return () => h("img", { src: appIconUrl, alt: "App Icon" });
244
+ },
245
+ });
246
+
247
+ function defaultPageTitleFormatter(input: {
248
+ breadcrumb: string | null;
249
+ appName: string;
250
+ route: RouteLocationNormalizedLoaded;
251
+ }): string {
252
+ if (input.breadcrumb && input.breadcrumb.trim().length > 0) {
253
+ return `${input.breadcrumb} : ${input.appName}`;
254
+ }
255
+ return input.appName;
256
+ }
257
+
258
+ export function getAppConfig() {
259
+ return {
260
+ appName: "My Primitive App",
261
+ homeRouteName: "home",
262
+ loginRouteName: "login",
263
+ appIcon: AppIcon,
264
+ documentStoreMode: DocumentStoreMode.SingleDocumentWithSwitching,
265
+ pageTitleFormatter: defaultPageTitleFormatter,
266
+ } as const;
267
+ }
268
+ ```
269
+
270
+ > `createPrimitiveApp` calls `useAppConfigStore().initialize(getAppConfig())` during bootstrap and sets up a reactive `document.title` using the provided formatter.
271
+
272
+ #### Navigation store: `useNavigationStore`
273
+
274
+ The navigation store drives sidebar, bottom navigation, and user menu:
275
+
276
+ **Configuration (`NavigationConfig`):**
277
+
278
+ | Option | Type | Description |
279
+ | ------------------------------- | ------------------------------- | ----------------------------------------- |
280
+ | `navOptions?.overflowMode` | `NavigationOverflowMode` | How to handle overflow items |
281
+ | `navOptions?.maxVisibleTabs` | `number` | Maximum tabs in bottom nav (default: 5) |
282
+ | `navOptions?.mobileNavEnabled` | `boolean` | Enable mobile bottom nav (default: true) |
283
+ | `navOptions?.mobileBackEnabled` | `boolean` | Enable mobile back button (default: true) |
284
+ | `navItems` | `Record<string, NavItemConfig>` | Navigation item definitions |
285
+
286
+ **NavItemConfig options:**
287
+
288
+ | Option | Type | Description |
289
+ | ------------------ | -------------------------------------- | ------------------------------------------------- |
290
+ | `key` | `string` | Unique identifier for the item |
291
+ | `navTitle` | `string` | Display title |
292
+ | `navGroup` | `"main" \| "secondary" \| "user-menu"` | Navigation group |
293
+ | `routeName?` | `string` | Vue router route name |
294
+ | `routeParams?` | `object` | Route parameters |
295
+ | `externalHref?` | `string` | External URL |
296
+ | `target?` | `string` | Link target (e.g., "\_blank") |
297
+ | `icon?` | `Component` | Icon component |
298
+ | `parentKey?` | `string` | Parent item key (for nesting) |
299
+ | `navHeader?` | `string` | Header text for grouped items |
300
+ | `matchRouteNames?` | `string[]` | Additional routes that activate this item |
301
+ | `mobilePriority?` | `number` | Priority for bottom nav (lower = higher priority) |
302
+ | `hidden?` | `boolean` | Hide this item |
303
+
304
+ Example:
305
+
306
+ ```ts
307
+ // src/config/navigationConfig.ts
308
+ import { NavigationOverflowMode, type NavigationConfig } from "primitive-app";
309
+ import { BookOpen, Settings, LogOut } from "lucide-vue-next";
310
+
311
+ export function getNavigationConfig(): NavigationConfig {
312
+ return {
313
+ navOptions: {
314
+ overflowMode: NavigationOverflowMode.Always,
315
+ maxVisibleTabs: 3,
316
+ mobileNavEnabled: true,
317
+ mobileBackEnabled: true,
318
+ },
319
+ navItems: {
320
+ gettingStarted: {
321
+ key: "gettingStarted",
322
+ navTitle: "Getting Started",
323
+ navGroup: "main",
324
+ routeName: "getting-started",
325
+ icon: BookOpen,
326
+ mobilePriority: 1,
327
+ },
328
+ settings: {
329
+ key: "settings",
330
+ navTitle: "Settings",
331
+ navGroup: "user-menu",
332
+ routeName: "settings",
333
+ icon: Settings,
334
+ },
335
+ logout: {
336
+ key: "logout",
337
+ navTitle: "Log out",
338
+ navGroup: "user-menu",
339
+ routeName: "logout",
340
+ icon: LogOut,
341
+ mobilePriority: 2,
342
+ },
343
+ },
344
+ } as const;
345
+ }
346
+ ```
347
+
348
+ **Dynamic navigation actions:**
349
+
350
+ ```ts
351
+ const navigation = useNavigationStore();
352
+
353
+ // Add items dynamically
354
+ navigation.addNavItems({ ... });
355
+
356
+ // Remove items
357
+ navigation.removeNavItems(['itemKey']);
358
+
359
+ // Show/hide items
360
+ navigation.hideNavItems(['itemKey']);
361
+ navigation.showNavItems(['itemKey']);
362
+
363
+ // Badges
364
+ navigation.setCountBadge('itemKey', 5);
365
+ navigation.setDotBadge('itemKey');
366
+ navigation.clearBadge('itemKey');
367
+ ```
368
+
369
+ `PrimitiveAppLayout`, `PrimitiveSidebarNav`, `PrimitiveBottomNav`, and `PrimitiveUserMenu` all consume this store.
370
+
371
+ #### Breadcrumbs store: `useBreadcrumbsStore`
372
+
373
+ The breadcrumbs store maintains an array of breadcrumb segments derived from route metadata:
374
+
375
+ - **State:** `segments: BreadcrumbSegment[]` – array of `{ label, href }` objects
376
+ - **Actions:**
377
+ - `initialize({ router })` – Wire to Vue Router (called by `createPrimitiveApp`)
378
+ - `refreshCurrentRoute()` – Re-generate breadcrumbs for the current route
379
+
380
+ Use `refreshCurrentRoute()` when other reactive state that influences breadcrumb generators changes (e.g., user display name).
381
+
382
+ ### User store: `useUserStore`
383
+
384
+ `useUserStore` wires your app into js-bao auth and user profile management.
385
+
386
+ **State:**
387
+
388
+ | Property | Type | Description |
389
+ | ----------------- | --------------------- | ----------------------------- |
390
+ | `currentUser` | `UserProfile \| null` | Current user profile |
391
+ | `isAuthenticated` | `boolean` | Whether user is authenticated |
392
+ | `isAdmin` | `boolean` (computed) | Whether user has admin role |
393
+ | `isOnline` | `boolean` | Network connectivity status |
394
+ | `isInitialized` | `boolean` | Whether store has initialized |
395
+
396
+ **Actions:**
397
+
398
+ | Method | Description |
399
+ | --------------------------------------------------- | ----------------------------------- |
400
+ | `initialize({ loginUrl })` | Initialize auth and event listeners |
401
+ | `login(continueURL?)` | Start OAuth flow |
402
+ | `handleOAuthCallback(defaultContinueUrl, loginUrl)` | Process OAuth callback |
403
+ | `logout(redirectTo?)` | Log out and optionally redirect |
404
+
405
+ **User preferences:**
406
+
407
+ Preferences are stored via the internal `UserPref` js-bao model in a per-user root document:
408
+
409
+ ```ts
410
+ const user = useUserStore();
411
+
412
+ // Get a preference with default value
413
+ const theme = user.getPref<string>("theme", "light");
414
+
415
+ // Set a preference
416
+ await user.setPref("theme", "dark");
417
+
418
+ // Delete a preference
419
+ await user.deletePref("theme");
420
+
421
+ // Get all preferences
422
+ const allPrefs = user.getAllPrefs();
423
+
424
+ // Clear all preferences
425
+ await user.clearAllPrefs();
426
+ ```
427
+
428
+ ### Document stores
429
+
430
+ Primitive App provides three document stores that work together to manage js-bao documents. Understanding their relationship is key to choosing the right pattern for your app.
431
+
432
+ #### How document stores relate
433
+
434
+ ```
435
+ +---------------------------------------------+
436
+ | jsBaoDocumentsStore |
437
+ | (All accessible documents & invitations) |
438
+ +---------------------------------------------+
439
+ / \
440
+ / \
441
+ +------------------------+ +------------------------+
442
+ | singleDocumentStore | | multiDocumentStore |
443
+ | (One active doc) | | (Tag-based |
444
+ | | | collections) |
445
+ +------------------------+ +------------------------+
446
+ ```
447
+
448
+ - **`jsBaoDocumentsStore`**: The foundational reactive store that tracks all documents the user has access to, plus pending invitations. It handles document metadata events from js-bao and maintains the authoritative list.
449
+ - **`singleDocumentStore`**: Built on top of `jsBaoDocumentsStore`. Manages a single "active" document at a time. Ideal for simple apps.
450
+ - **`multiDocumentStore`**: Built on top of `jsBaoDocumentsStore`. Manages multiple document collections based on tags. Ideal for complex apps with varied sharing needs.
451
+
452
+ #### Choosing a document store pattern
453
+
454
+ **1. SingleDocument mode** – Simple apps without sharing needs
455
+
456
+ Best for: Personal apps, simple tools, single-user data.
457
+
458
+ ```ts
459
+ // appConfig.ts
460
+ documentStoreMode: DocumentStoreMode.SingleDocument;
461
+ ```
462
+
463
+ - Each user automatically gets a single document created for them.
464
+ - No document switching UI is shown.
465
+ - Data is conceptually like a single database per user.
466
+
467
+ **2. SingleDocumentWithSwitching mode** – Google Docs-like sharing pattern
468
+
469
+ Best for: Apps with clear data "containers" that users might share.
470
+
471
+ ```ts
472
+ // appConfig.ts
473
+ documentStoreMode: DocumentStoreMode.SingleDocumentWithSwitching;
474
+ ```
475
+
476
+ - Users automatically get a default document created.
477
+ - Users can create additional documents, each of which can be shared with others.
478
+ - Only one document is active at a time.
479
+ - Built-in UI for document switching, sharing invitations, and management.
480
+ - Examples: One document per "company", splitting "personal" from "shared" data.
481
+
482
+ Configuration:
483
+
484
+ ```ts
485
+ export function getSingleDocumentConfig() {
486
+ return {
487
+ userVisibleDocumentName: "Project",
488
+ userVisibleDocumentNamePlural: "Projects",
489
+ defaultDocumentTitle: "My First Project",
490
+ manageDocumentsRouteName: "projects-manage",
491
+ } as const;
492
+ }
493
+ ```
494
+
495
+ **3. MultiDoc mode** – Complex sharing and data modeling
496
+
497
+ Best for: Apps with complex sharing needs, multiple data sets, or large data volumes.
498
+
499
+ ```ts
500
+ // appConfig.ts
501
+ documentStoreMode: DocumentStoreMode.MultiDoc;
502
+ ```
503
+
504
+ - Multiple documents can be open simultaneously.
505
+ - Documents are organized into "collections" based on tags.
506
+ - Collections are registered dynamically at runtime.
507
+ - Supports auto-opening documents and auto-accepting invitations per collection.
508
+ - Recommended when: sharing different data sets with different users, data exceeds ~10MB per document, need to search across multiple documents.
509
+
510
+ Usage:
511
+
512
+ ```ts
513
+ import { useMultiDocumentStore } from "primitive-app";
514
+
515
+ const multiDoc = useMultiDocumentStore();
516
+
517
+ // Register a collection (typically on component mount)
518
+ await multiDoc.registerCollection({
519
+ name: "workspaces",
520
+ tag: "workspace",
521
+ autoOpen: true, // Auto-open matching documents
522
+ autoAcceptInvites: true, // Auto-accept invitations for this collection
523
+ });
524
+
525
+ // Access documents in a collection
526
+ const workspaces = computed(() => multiDoc.collections["workspaces"] ?? []);
527
+
528
+ // Check collection readiness
529
+ const isReady = multiDoc.getCollectionReadyRef("workspaces");
530
+
531
+ // Create a document in a collection
532
+ const newDoc = await multiDoc.createDocument("workspaces", "New Workspace");
533
+
534
+ // Unregister when done
535
+ await multiDoc.unregisterCollection("workspaces");
536
+ ```
537
+
538
+ #### jsBaoDocumentsStore API
539
+
540
+ The base store for all document operations:
541
+
542
+ **State:**
543
+
544
+ | Property | Type | Description |
545
+ | ---------------------- | ---------------------- | --------------------------- |
546
+ | `documents` | `TrackedDocument[]` | All accessible documents |
547
+ | `pendingInvitations` | `DocumentInvitation[]` | Pending sharing invitations |
548
+ | `openDocumentIds` | `Set<string>` | Currently open document IDs |
549
+ | `documentListLoaded` | `boolean` | Document list has loaded |
550
+ | `invitationListLoaded` | `boolean` | Invitation list has loaded |
551
+ | `isReady` | `boolean` (computed) | Both lists have loaded |
552
+
553
+ **Actions:**
554
+
555
+ | Method | Description |
556
+ | --------------------------------------------- | ------------------------------ |
557
+ | `initialize()` | Load documents and invitations |
558
+ | `reset()` | Clear state and unsubscribe |
559
+ | `refreshDocuments()` | Reload document list |
560
+ | `openDocument(id)` | Open a document |
561
+ | `closeDocument(id)` | Close a document |
562
+ | `createDocument(title, tags?)` | Create a new document |
563
+ | `renameDocument(id, title)` | Rename a document |
564
+ | `deleteDocument(id)` | Delete a document |
565
+ | `shareDocument(id, email, permission)` | Share with another user |
566
+ | `refreshPendingInvitations()` | Reload invitations |
567
+ | `acceptInvitation(documentId)` | Accept an invitation |
568
+ | `declineInvitation(documentId, invitationId)` | Decline an invitation |
569
+
570
+ #### singleDocumentStore API
571
+
572
+ Manages a single active document:
573
+
574
+ **State:**
575
+
576
+ | Property | Type | Description |
577
+ | ------------------------- | ------------------------- | -------------------------- |
578
+ | `currentDocumentId` | `string \| null` | Active document ID |
579
+ | `currentDocumentMetadata` | `TrackedDocument \| null` | Active document metadata |
580
+ | `isReady` | `boolean` | Document is open and ready |
581
+ | `isCurrentDocReadOnly` | `boolean` | User has read-only access |
582
+
583
+ **Actions:**
584
+
585
+ | Method | Description |
586
+ | --------------------- | ------------------------------ |
587
+ | `initialize(options)` | Initialize with config |
588
+ | `reset()` | Clear state |
589
+ | `switchDocument(id)` | Switch to a different document |
590
+
591
+ #### multiDocumentStore API
592
+
593
+ Manages multiple document collections:
594
+
595
+ **State:**
596
+
597
+ | Property | Type | Description |
598
+ | ------------- | ----------------------------------- | ------------------------------- |
599
+ | `collections` | `Record<string, TrackedDocument[]>` | Documents grouped by collection |
600
+
601
+ **Actions:**
602
+
603
+ | Method | Description |
604
+ | ----------------------------------- | ------------------------------- |
605
+ | `registerCollection(config)` | Register a new collection |
606
+ | `unregisterCollection(name)` | Unregister a collection |
607
+ | `getCollection(name)` | Get documents in a collection |
608
+ | `isCollectionRegistered(name)` | Check if collection exists |
609
+ | `isCollectionReady(name)` | Check if collection is ready |
610
+ | `getCollectionReadyRef(name)` | Get reactive readiness ref |
611
+ | `createDocument(collection, title)` | Create document in collection |
612
+ | `isDocumentReady(id)` | Check if document is open |
613
+ | `getDocumentReadyRef(idRef)` | Get reactive document readiness |
614
+ | `reset()` | Clear all collections |
615
+
616
+ ---
617
+
618
+ ## `PrimitiveAppLayout` usage patterns
619
+
620
+ `PrimitiveAppLayout` is the main authenticated **app shell**. It:
621
+
622
+ - Renders a sidebar (desktop), breadcrumb header, and optional bottom nav (mobile).
623
+ - Integrates with:
624
+ - `useNavigationStore` (sidebar items, bottom nav, back behavior).
625
+ - `useBreadcrumbsStore` (breadcrumbs and page title).
626
+ - `useUserStore` (user menu, online status).
627
+ - `useAppConfigStore` and `DocumentStoreMode` (document switching vs static header).
628
+ - `useSingleDocumentStore` (optional single-document switcher).
629
+ - Shows a service worker "update required" banner and an optional worktree label banner (for dev workflows).
630
+
631
+ You can adopt it at different levels of complexity:
632
+
633
+ ### 1) Use it as-is (configure via stores and route metadata)
634
+
635
+ This is the simplest path – use `PrimitiveAppLayout` as the root layout for authenticated routes, and configure:
636
+
637
+ - App config (`getAppConfig`)
638
+ - Document store (`getSingleDocumentConfig` + `DocumentStoreMode`)
639
+ - Navigation (`getNavigationConfig`)
640
+ - Routing meta (`primitiveRouterMeta`)
641
+
642
+ Example (excerpt):
643
+
644
+ ```ts
645
+ // src/router/routes.ts
646
+ const routes: RouteRecordRaw[] = [
647
+ {
648
+ path: "/",
649
+ component: PrimitiveAppLayout,
650
+ children: [
651
+ {
652
+ path: "",
653
+ name: "home",
654
+ component: () => import("@/pages/HomePage.vue"),
655
+ meta: {
656
+ primitiveRouterMeta: {
657
+ requireAuth: "member",
658
+ breadcrumb: { title: "Home" },
659
+ },
660
+ },
661
+ },
662
+ ],
663
+ },
664
+ // login + static sections...
665
+ ];
666
+ ```
667
+
668
+ You do **not** pass any props to `PrimitiveAppLayout` – it reads state from the shared stores.
669
+
670
+ ### 2) Wrap it in a layout that performs extra data loading or dynamic tweaks
671
+
672
+ Wrap `PrimitiveAppLayout` in your own layout to:
673
+
674
+ - Run data loaders (`useJsBaoDataLoader`, app-specific composables).
675
+ - Dynamically modify navigation (`navigationStore.addNavItems`, badges).
676
+ - Add theming or additional chrome around the shell.
677
+
678
+ ```vue
679
+ <!-- src/layouts/ThemedPrimitiveAppLayout.vue -->
680
+ <script setup lang="ts">
681
+ import { PrimitiveAppLayout, useNavigationStore } from "primitive-app";
682
+ import { onMounted } from "vue";
683
+
684
+ const navigation = useNavigationStore();
685
+
686
+ onMounted(() => {
687
+ navigation.addNavItems({
688
+ recentItems: {
689
+ key: "recentItems",
690
+ navTitle: "Recent Items",
691
+ navGroup: "main",
692
+ routeName: "recent-items",
693
+ mobilePriority: 2,
694
+ },
695
+ });
696
+ });
697
+ </script>
698
+
699
+ <template>
700
+ <div class="min-h-screen bg-background text-foreground">
701
+ <PrimitiveAppLayout>
702
+ <router-view />
703
+ </PrimitiveAppLayout>
704
+ </div>
705
+ </template>
706
+ ```
707
+
708
+ Routes now use `ThemedPrimitiveAppLayout` instead of `PrimitiveAppLayout` directly.
709
+
710
+ ### 3) Override sidebar / bottom nav content via slots
711
+
712
+ `PrimitiveAppLayout` exposes:
713
+
714
+ - `#sidebar` – replaces the entire sidebar area.
715
+ - `#bottomNav` – replaces the mobile bottom navigation bar.
716
+ - Default slot – main content area (which typically renders `<router-view />`).
717
+
718
+ This lets you keep the **overall shell behavior** (SW banner, worktree label, mobile back header, padding) while providing your own navigation components.
719
+
720
+ ```vue
721
+ <!-- src/layouts/CustomShellLayout.vue -->
722
+ <script setup lang="ts">
723
+ import {
724
+ PrimitiveAppLayout,
725
+ PrimitiveUserMenu,
726
+ useNavigationStore,
727
+ useUserStore,
728
+ } from "primitive-app";
729
+ import { computed } from "vue";
730
+
731
+ const navigation = useNavigationStore();
732
+ const user = useUserStore();
733
+
734
+ const mainNavItems = computed(() => navigation.navMain);
735
+ const userMenuItems = computed(() => navigation.userMenuItems);
736
+ </script>
737
+
738
+ <template>
739
+ <PrimitiveAppLayout>
740
+ <template #sidebar>
741
+ <aside class="flex h-full flex-col border-r bg-background">
742
+ <header class="p-4 border-b">
743
+ <h1 class="text-sm font-semibold tracking-tight">
744
+ My Custom Sidebar
745
+ </h1>
746
+ </header>
747
+
748
+ <nav class="flex-1 overflow-y-auto p-2 space-y-1">
749
+ <RouterLink
750
+ v-for="item in mainNavItems"
751
+ :key="item.title"
752
+ :to="item.url"
753
+ class="block px-3 py-2 rounded-md text-sm hover:bg-accent"
754
+ >
755
+ {{ item.title }}
756
+ </RouterLink>
757
+ </nav>
758
+
759
+ <footer class="border-t p-2">
760
+ <PrimitiveUserMenu
761
+ :current-user="user.currentUser"
762
+ :is-online="user.isOnline"
763
+ :user-menu-items="userMenuItems"
764
+ />
765
+ </footer>
766
+ </aside>
767
+ </template>
768
+
769
+ <template #bottomNav>
770
+ <nav class="fixed inset-x-0 bottom-0 border-t bg-background md:hidden">
771
+ <!-- your own mobile tab bar implementation -->
772
+ </nav>
773
+ </template>
774
+
775
+ <router-view />
776
+ </PrimitiveAppLayout>
777
+ </template>
778
+ ```
779
+
780
+ You can also reconstruct the _default_ layout but insert custom elements (e.g. banners, diagnostics) inside the slots.
781
+
782
+ ### 4) Build a completely custom layout
783
+
784
+ Finally, you can skip `PrimitiveAppLayout` entirely and build your own shell using the underlying stores and components:
785
+
786
+ - `useAppConfigStore`, `useUserStore`, `useNavigationStore`, `useBreadcrumbsStore`, `useSingleDocumentStore`
787
+ - Components: `PrimitiveSidebarNav`, `PrimitiveAppBreadcrumb`, `PrimitiveUserMenu`, `PrimitiveBottomNav`, `PrimitiveSingleDocumentSwitcher`, etc.
788
+
789
+ ```vue
790
+ <!-- src/layouts/MyFullyCustomLayout.vue -->
791
+ <script setup lang="ts">
792
+ import {
793
+ PrimitiveAppBreadcrumb,
794
+ PrimitiveSidebarNav,
795
+ PrimitiveUserMenu,
796
+ useAppConfigStore,
797
+ useNavigationStore,
798
+ useUserStore,
799
+ } from "primitive-app";
800
+
801
+ const appConfig = useAppConfigStore();
802
+ const navigation = useNavigationStore();
803
+ const user = useUserStore();
804
+ </script>
805
+
806
+ <template>
807
+ <div class="min-h-screen grid grid-cols-[240px_1fr]">
808
+ <aside class="border-r bg-background flex flex-col">
809
+ <header class="p-4 border-b">
810
+ <span class="font-semibold text-sm">
811
+ {{ appConfig.appName() }}
812
+ </span>
813
+ </header>
814
+
815
+ <main class="flex-1 overflow-y-auto">
816
+ <PrimitiveSidebarNav />
817
+ </main>
818
+
819
+ <footer class="border-t p-2">
820
+ <PrimitiveUserMenu
821
+ :current-user="user.currentUser"
822
+ :is-online="user.isOnline"
823
+ :user-menu-items="navigation.userMenuItems"
824
+ />
825
+ </footer>
826
+ </aside>
827
+
828
+ <section class="flex flex-col">
829
+ <header class="h-12 flex items-center border-b px-4">
830
+ <PrimitiveAppBreadcrumb />
831
+ </header>
832
+
833
+ <main class="flex-1 overflow-y-auto p-4">
834
+ <router-view />
835
+ </main>
836
+ </section>
837
+ </div>
838
+ </template>
839
+ ```
840
+
841
+ This is useful if you have strong visual/UX requirements or are gradually adopting `primitive-app` in an existing application.
842
+
843
+ ---
844
+
845
+ ## Data loading with `useJsBaoDataLoader`
846
+
847
+ `useJsBaoDataLoader` provides a standardized pattern for:
848
+
849
+ - Loading data asynchronously (typically via js-bao models).
850
+ - Watching a "document ready" gate (e.g., from `useSingleDocumentStore`).
851
+ - Reacting to changes in query params.
852
+ - Subscribing to js-bao models and automatically reloading when data changes.
853
+ - Integrating with skeleton components like `PrimitiveSkeletonGate`.
854
+
855
+ ### API overview
856
+
857
+ ```ts
858
+ import { useJsBaoDataLoader } from "primitive-app";
859
+
860
+ const { data, initialDataLoaded, reload } = useJsBaoDataLoader<Data, Query>({
861
+ subscribeTo: [
862
+ /* models with Model.subscribe(cb) */
863
+ ],
864
+ queryParams, // ref/computed or plain value; null disables query-driven reloads
865
+ documentReady, // ref/computed/boolean gate
866
+ loadData: async ({ queryParams }) => {
867
+ /* ... */
868
+ },
869
+ pauseUpdates, // optional ref/computed/boolean
870
+ debounceMs: 50, // optional debounce for reloads
871
+ onError: (error) => {}, // optional error handler
872
+ });
873
+ ```
874
+
875
+ - **Result:**
876
+ - `data: Ref<Data | null>` – last successfully loaded value.
877
+ - `initialDataLoaded: Ref<boolean>` – becomes `true` after first successful load while `documentReady` is `true`.
878
+ - `reload(): void` – manually schedule a reload (respecting debounce and gates).
879
+
880
+ ### Example with js-bao models and the single-document store
881
+
882
+ ```ts
883
+ // src/pages/ProductsPage.vue (script setup)
884
+ import {
885
+ PrimitiveSkeletonGate,
886
+ useJsBaoDataLoader,
887
+ useSingleDocumentStore,
888
+ } from "primitive-app";
889
+ import { Product } from "@/models/Product";
890
+ import { storeToRefs } from "pinia";
891
+ import { computed, ref } from "vue";
892
+
893
+ type PageData = { products: Product[] };
894
+
895
+ const singleDoc = useSingleDocumentStore();
896
+ const { isReady: documentReady } = storeToRefs(singleDoc);
897
+
898
+ const queryParams = ref({}); // "load all" for this example
899
+
900
+ const { data, initialDataLoaded, reload } = useJsBaoDataLoader<PageData>({
901
+ subscribeTo: [Product],
902
+ queryParams,
903
+ documentReady,
904
+ loadData: async () => {
905
+ const result = await Product.query(queryParams.value);
906
+ return { products: (result.data || []) as Product[] };
907
+ },
908
+ onError: (err) => {
909
+ console.error("Error loading products", err);
910
+ },
911
+ });
912
+
913
+ const products = computed(() => data.value?.products ?? []);
914
+ ```
915
+
916
+ ```vue
917
+ <!-- template -->
918
+ <PrimitiveSkeletonGate :is-ready="initialDataLoaded">
919
+ <template #skeleton>
920
+ <!-- skeleton UI -->
921
+ </template>
922
+
923
+ <ul>
924
+ <li v-for="product in products" :key="product.id">
925
+ {{ product.name }}
926
+ </li>
927
+ </ul>
928
+ </PrimitiveSkeletonGate>
929
+ ```
930
+
931
+ ### Example without js-bao (simple async data)
932
+
933
+ ```ts
934
+ import { useJsBaoDataLoader, PrimitiveSkeletonGate } from "primitive-app";
935
+ import { ref } from "vue";
936
+
937
+ type DemoData = { message: string };
938
+
939
+ const simulateSlowLoad = async (): Promise<DemoData> => {
940
+ await new Promise((resolve) => setTimeout(resolve, 800));
941
+ return { message: "Loaded after a short delay." };
942
+ };
943
+
944
+ const { data, initialDataLoaded } = useJsBaoDataLoader<DemoData>({
945
+ subscribeTo: [],
946
+ queryParams: ref({}),
947
+ documentReady: ref(true),
948
+ loadData: simulateSlowLoad,
949
+ });
950
+ ```
951
+
952
+ ---
953
+
954
+ ## Auth, navigation, documents, and debug components
955
+
956
+ Primitive App includes higher-level components that sit on top of the stores:
957
+
958
+ - **Auth components**
959
+ - `PrimitiveLogin`, `PrimitiveLogout`, `PrimitiveOauthCallback`
960
+ - **Navigation components**
961
+ - `PrimitiveAppBreadcrumb`, `PrimitiveSidebarNav`, `PrimitiveBottomNav`,
962
+ `PrimitiveUserMenu`, `PrimitiveNavigationBadge`
963
+ - **Document components**
964
+ - `PrimitiveManageDocuments`, `PrimitiveShareDocumentDialog`,
965
+ `PrimitiveSingleDocumentSwitcher`
966
+ - **Shared components**
967
+ - `PrimitiveLogoSpinner`, `PrimitiveSkeletonGate`, `DeleteConfirmationDialog`
968
+ - **Debug suite**
969
+ - `PrimitiveDebuggingSuite`, `PrimitiveTestRunner`, `DocumentDebugger`,
970
+ `DebugSuiteLayout`, plus debug pages you can mount on a `/debug` route.
971
+
972
+ ---
973
+
974
+ ## Debugging suite
975
+
976
+ The debugging suite provides tools for testing and inspecting your app during development:
977
+
978
+ ### Document Debugger
979
+
980
+ The Document Debugger is a powerful tool for inspecting and managing js-bao documents and model data:
981
+
982
+ - **Document management**: View all documents, create new documents, rename, delete, and switch between documents
983
+ - **Model inspection**: Browse all registered js-bao models and their records
984
+ - **Record CRUD**: Create, read, update, and delete records directly
985
+ - **Filtering & sorting**: Filter and sort records by any field
986
+ - **Mass operations**: Bulk delete records or documents
987
+ - **Schema inspection**: View model fields, types, indexes, relationships, and unique constraints
988
+
989
+ The Document Debugger uses its own document context independent of the app's main document stores, so you can inspect data without affecting the app state.
990
+
991
+ ### Test Runner
992
+
993
+ The test harness runs app-level integration tests. Tests are grouped into `PrimitiveTestGroup` objects and registered with the `DebugSuiteLayout`.
994
+
995
+ #### Defining test groups
996
+
997
+ Create a file for your test groups (e.g., `src/tests/myTests.ts`):
998
+
999
+ ```ts
1000
+ // src/tests/myTests.ts
1001
+ import type { PrimitiveTestGroup } from "primitive-app";
1002
+ import { useUserStore, useSingleDocumentStore } from "primitive-app";
1003
+
1004
+ // Helper to format pass/fail scores (the runner parses this format)
1005
+ function formatScore(passed: number, total: number): string {
1006
+ const percentage = total === 0 ? 100 : (passed / total) * 100;
1007
+ return `${passed}/${total} (${percentage.toFixed(1)}%)`;
1008
+ }
1009
+
1010
+ export const userPreferencesTestGroup: PrimitiveTestGroup = {
1011
+ name: "User Preferences",
1012
+ mode: "offline", // "offline" (default) or "online" – controls network requirement
1013
+ tests: [
1014
+ {
1015
+ id: "user-pref-read-all",
1016
+ name: "can read all user preferences without error",
1017
+ async run(log?: (m: string) => void): Promise<string> {
1018
+ const user = useUserStore();
1019
+
1020
+ log?.("Reading all preferences from userStore...");
1021
+ const prefs = user.getAllPrefs();
1022
+ log?.(`Retrieved ${Object.keys(prefs).length} preference(s)`);
1023
+
1024
+ // Returning a score string indicates success
1025
+ return formatScore(1, 1);
1026
+ },
1027
+ },
1028
+ {
1029
+ id: "user-pref-default",
1030
+ name: "getPref returns default value for missing key",
1031
+ async run(log?: (m: string) => void): Promise<string> {
1032
+ const user = useUserStore();
1033
+ const defaultValue = "fallback";
1034
+
1035
+ log?.(`Requesting missing key with default "${defaultValue}"...`);
1036
+ const value = user.getPref<string>("nonexistent_key", defaultValue);
1037
+
1038
+ // Throwing an Error fails the test
1039
+ if (value !== defaultValue) {
1040
+ throw new Error(`Expected "${defaultValue}", got "${value}"`);
1041
+ }
1042
+
1043
+ log?.("Default value returned correctly");
1044
+ return formatScore(1, 1);
1045
+ },
1046
+ },
1047
+ ],
1048
+ };
1049
+
1050
+ export const documentTestGroup: PrimitiveTestGroup = {
1051
+ name: "Document Store",
1052
+ mode: "offline",
1053
+ tests: [
1054
+ {
1055
+ id: "doc-store-ready",
1056
+ name: "single document store becomes ready",
1057
+ async run(log?: (m: string) => void): Promise<string> {
1058
+ const docStore = useSingleDocumentStore();
1059
+
1060
+ log?.(`Document store ready: ${docStore.isReady}`);
1061
+ if (!docStore.isReady) {
1062
+ throw new Error("Expected document store to be ready");
1063
+ }
1064
+
1065
+ return formatScore(1, 1);
1066
+ },
1067
+ },
1068
+ ],
1069
+ };
1070
+
1071
+ // Export all test groups as an array
1072
+ export const appTestGroups: PrimitiveTestGroup[] = [
1073
+ userPreferencesTestGroup,
1074
+ documentTestGroup,
1075
+ ];
1076
+ ```
1077
+
1078
+ #### Test function conventions
1079
+
1080
+ - **`id`**: Unique identifier for the test (used for selection state).
1081
+ - **`name`**: Human-readable test name displayed in the UI.
1082
+ - **`run(log?)`**: Async function that executes the test.
1083
+ - Use `log?.("message")` to output progress to the test runner.
1084
+ - Return a score string like `"1/1 (100.0%)"` to indicate success (the runner parses this format).
1085
+ - Throw an `Error` to fail the test.
1086
+ - **`mode`** (on the group): `"offline"` (default) runs tests without requiring network; `"online"` requires network connectivity.
1087
+
1088
+ #### Registering tests with routes
1089
+
1090
+ Mount the debug suite on a `/debug` route and pass your test groups to `DebugSuiteLayout`:
1091
+
1092
+ ```ts
1093
+ // src/router/routes.ts
1094
+ import {
1095
+ createPrimitiveRouter,
1096
+ DebugSuiteLayout,
1097
+ DebuggingSuiteHome,
1098
+ DebuggingSuiteTests,
1099
+ DebuggingSuiteDocuments,
1100
+ } from "primitive-app";
1101
+ import { appTestGroups } from "@/tests/myTests";
1102
+ import type { RouteRecordRaw } from "vue-router";
1103
+
1104
+ const routes: RouteRecordRaw[] = [
1105
+ // ... your app routes ...
1106
+
1107
+ // Debug suite (admin-only recommended)
1108
+ {
1109
+ path: "/debug",
1110
+ component: DebugSuiteLayout,
1111
+ props: {
1112
+ testGroups: appTestGroups,
1113
+ appName: "My App",
1114
+ },
1115
+ meta: {
1116
+ primitiveRouterMeta: {
1117
+ requireAuth: "admin", // or "member" for broader access
1118
+ },
1119
+ },
1120
+ children: [
1121
+ {
1122
+ path: "",
1123
+ name: "debug-home",
1124
+ component: DebuggingSuiteHome,
1125
+ },
1126
+ {
1127
+ path: "test",
1128
+ name: "debug-test",
1129
+ component: DebuggingSuiteTests,
1130
+ },
1131
+ {
1132
+ path: "documents",
1133
+ name: "debug-documents",
1134
+ component: DebuggingSuiteDocuments,
1135
+ },
1136
+ ],
1137
+ },
1138
+ ];
1139
+
1140
+ const router = createPrimitiveRouter({
1141
+ routes,
1142
+ loginRouteName: "login",
1143
+ });
1144
+
1145
+ export default router;
1146
+ ```
1147
+
1148
+ The debug suite provides:
1149
+
1150
+ - **Home page** (`DebuggingSuiteHome`): Overview and quick actions.
1151
+ - **Test page** (`DebuggingSuiteTests`): Select and run tests, view results and logs.
1152
+ - **Documents page** (`DebuggingSuiteDocuments`): Inspect and manage js-bao documents via the Document Debugger.
1153
+
1154
+ ---
1155
+
1156
+ ## js-bao integration & logging
1157
+
1158
+ ### JsBao client service
1159
+
1160
+ Primitive App centralizes js-bao client setup via:
1161
+
1162
+ - `initializeJsBao(config: JsBaoClientOptions)` – called once at startup.
1163
+ - `jsBaoClientService.getClientAsync()` – used internally by stores to access the client.
1164
+
1165
+ `config.models` from your app are merged with the library's internal models (such as `UserPref`) before initializing the client.
1166
+
1167
+ ### Logging
1168
+
1169
+ `primitive-app` ships with a scoped logging utility:
1170
+
1171
+ - `primitiveAppBaseLogger`
1172
+ - `createLogger({ level, scope })`
1173
+ - `LogLevel` (`"debug" | "info" | "warn" | "error" | "none"`)
1174
+
1175
+ Example:
1176
+
1177
+ ```ts
1178
+ import { createLogger } from "primitive-app";
1179
+
1180
+ const logger = createLogger({
1181
+ level: import.meta.env.DEV ? "debug" : "info",
1182
+ scope: ["MyApp", "ProductsPage"],
1183
+ });
1184
+
1185
+ logger.debug("Loaded products", { count: 42 });
1186
+ ```
1187
+
1188
+ ---
1189
+
1190
+ ## Reference and further examples
1191
+
1192
+ For a more complete, real-world example, refer to:
1193
+
1194
+ - **Demo app (`primitive-app-demo`)**
1195
+ - `src/main.ts` – bootstrapping with `createPrimitiveApp`
1196
+ - `src/router/routes.ts` – routes + `createPrimitiveRouter`
1197
+ - `src/config/appConfig.ts`, `src/config/envConfig.ts`, `src/config/navigationConfig.ts`
1198
+ - `src/pages/GettingStarted*` – focused pages on configuration, theming, routing, navigation, data loading, utilities
1199
+ - **Template app (`primitive-app-template`)**
1200
+ - Minimal starter following the same patterns, useful for new apps.
1201
+
1202
+ For a deeper, implementation-level map of the library (especially for automation or AI agents), see [`AGENTS.md`](./AGENTS.md).