astro-tractstack 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (427) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +56 -0
  3. package/astro.d.ts +64 -0
  4. package/bin/create-tractstack.js +483 -0
  5. package/dist/config.js +80 -0
  6. package/dist/index.js +2129 -0
  7. package/package.json +89 -0
  8. package/templates/artpacks/kCz/captainBreakfast_1080px.webp +0 -0
  9. package/templates/artpacks/kCz/captainBreakfast_1920px.webp +0 -0
  10. package/templates/artpacks/kCz/captainBreakfast_600px.webp +0 -0
  11. package/templates/artpacks/kCz/cleanDrips_1080px.webp +0 -0
  12. package/templates/artpacks/kCz/cleanDrips_1920px.webp +0 -0
  13. package/templates/artpacks/kCz/cleanDrips_600px.webp +0 -0
  14. package/templates/artpacks/kCz/crispwaves_1080px.webp +0 -0
  15. package/templates/artpacks/kCz/crispwaves_1920px.webp +0 -0
  16. package/templates/artpacks/kCz/crispwaves_600px.webp +0 -0
  17. package/templates/artpacks/kCz/dragonSkin_1080px.webp +0 -0
  18. package/templates/artpacks/kCz/dragonSkin_1920px.webp +0 -0
  19. package/templates/artpacks/kCz/dragonSkin_600px.webp +0 -0
  20. package/templates/artpacks/kCz/dragon_1080px.webp +0 -0
  21. package/templates/artpacks/kCz/dragon_1920px.webp +0 -0
  22. package/templates/artpacks/kCz/dragon_600px.webp +0 -0
  23. package/templates/artpacks/kCz/nightcity_1080px.webp +0 -0
  24. package/templates/artpacks/kCz/nightcity_1920px.webp +0 -0
  25. package/templates/artpacks/kCz/nightcity_600px.webp +0 -0
  26. package/templates/artpacks/kCz/pattern1_1080px.webp +0 -0
  27. package/templates/artpacks/kCz/pattern1_1920px.webp +0 -0
  28. package/templates/artpacks/kCz/pattern1_600px.webp +0 -0
  29. package/templates/artpacks/kCz/pattern2_1080px.webp +0 -0
  30. package/templates/artpacks/kCz/pattern2_1920px.webp +0 -0
  31. package/templates/artpacks/kCz/pattern2_600px.webp +0 -0
  32. package/templates/artpacks/kCz/skindrips_1080px.webp +0 -0
  33. package/templates/artpacks/kCz/skindrips_1920px.webp +0 -0
  34. package/templates/artpacks/kCz/skindrips_600px.webp +0 -0
  35. package/templates/artpacks/kCz/slimetime_1080px.webp +0 -0
  36. package/templates/artpacks/kCz/slimetime_1920px.webp +0 -0
  37. package/templates/artpacks/kCz/slimetime_600px.webp +0 -0
  38. package/templates/artpacks/kCz/snake_1080px.webp +0 -0
  39. package/templates/artpacks/kCz/snake_1920px.webp +0 -0
  40. package/templates/artpacks/kCz/snake_600px.webp +0 -0
  41. package/templates/artpacks/kCz/toxicshock_1080px.webp +0 -0
  42. package/templates/artpacks/kCz/toxicshock_1920px.webp +0 -0
  43. package/templates/artpacks/kCz/toxicshock_600px.webp +0 -0
  44. package/templates/artpacks/kCz/tractstack_1080px.webp +0 -0
  45. package/templates/artpacks/kCz/tractstack_1920px.webp +0 -0
  46. package/templates/artpacks/kCz/tractstack_600px.webp +0 -0
  47. package/templates/artpacks/kCz/tripdrips_1080px.webp +0 -0
  48. package/templates/artpacks/kCz/tripdrips_1920px.webp +0 -0
  49. package/templates/artpacks/kCz/tripdrips_600px.webp +0 -0
  50. package/templates/artpacks/kCz/wavedrips_1080px.webp +0 -0
  51. package/templates/artpacks/kCz/wavedrips_1920px.webp +0 -0
  52. package/templates/artpacks/kCz/wavedrips_600px.webp +0 -0
  53. package/templates/artpacks/t8k/beach_1080px.webp +0 -0
  54. package/templates/artpacks/t8k/beach_1920px.webp +0 -0
  55. package/templates/artpacks/t8k/beach_600px.webp +0 -0
  56. package/templates/artpacks/t8k/blast_1080px.webp +0 -0
  57. package/templates/artpacks/t8k/blast_1920px.webp +0 -0
  58. package/templates/artpacks/t8k/blast_600px.webp +0 -0
  59. package/templates/artpacks/t8k/bokeh_1080px.webp +0 -0
  60. package/templates/artpacks/t8k/bokeh_1920px.webp +0 -0
  61. package/templates/artpacks/t8k/bokeh_600px.webp +0 -0
  62. package/templates/artpacks/t8k/cartoon_1080px.webp +0 -0
  63. package/templates/artpacks/t8k/cartoon_1920px.webp +0 -0
  64. package/templates/artpacks/t8k/cartoon_600px.webp +0 -0
  65. package/templates/artpacks/t8k/darkeggshell_1080px.webp +0 -0
  66. package/templates/artpacks/t8k/darkeggshell_1920px.webp +0 -0
  67. package/templates/artpacks/t8k/darkeggshell_600px.webp +0 -0
  68. package/templates/artpacks/t8k/explosion_1080px.webp +0 -0
  69. package/templates/artpacks/t8k/explosion_1920px.webp +0 -0
  70. package/templates/artpacks/t8k/explosion_600px.webp +0 -0
  71. package/templates/artpacks/t8k/floral_1080px.webp +0 -0
  72. package/templates/artpacks/t8k/floral_1920px.webp +0 -0
  73. package/templates/artpacks/t8k/floral_600px.webp +0 -0
  74. package/templates/artpacks/t8k/flower_1080px.webp +0 -0
  75. package/templates/artpacks/t8k/flower_1920px.webp +0 -0
  76. package/templates/artpacks/t8k/flower_600px.webp +0 -0
  77. package/templates/artpacks/t8k/foliage_1080px.webp +0 -0
  78. package/templates/artpacks/t8k/foliage_1920px.webp +0 -0
  79. package/templates/artpacks/t8k/foliage_600px.webp +0 -0
  80. package/templates/artpacks/t8k/mist_1080px.webp +0 -0
  81. package/templates/artpacks/t8k/mist_1920px.webp +0 -0
  82. package/templates/artpacks/t8k/mist_600px.webp +0 -0
  83. package/templates/artpacks/t8k/portal_1080px.webp +0 -0
  84. package/templates/artpacks/t8k/portal_1920px.webp +0 -0
  85. package/templates/artpacks/t8k/portal_600px.webp +0 -0
  86. package/templates/artpacks/t8k/storytime_1080px.webp +0 -0
  87. package/templates/artpacks/t8k/storytime_1920px.webp +0 -0
  88. package/templates/artpacks/t8k/storytime_600px.webp +0 -0
  89. package/templates/artpacks/t8k/tacky_1080px.webp +0 -0
  90. package/templates/artpacks/t8k/tacky_1920px.webp +0 -0
  91. package/templates/artpacks/t8k/tacky_600px.webp +0 -0
  92. package/templates/artpacks/t8k/wallpaper_1080px.webp +0 -0
  93. package/templates/artpacks/t8k/wallpaper_1920px.webp +0 -0
  94. package/templates/artpacks/t8k/wallpaper_600px.webp +0 -0
  95. package/templates/brand/favicon.ico +0 -0
  96. package/templates/brand/logo.svg +19 -0
  97. package/templates/brand/static.jpg +0 -0
  98. package/templates/brand/wordmark.svg +4 -0
  99. package/templates/css/custom.css +51 -0
  100. package/templates/css/frontend.css +3519 -0
  101. package/templates/css/storykeep.css +92872 -0
  102. package/templates/custom/minimal/CodeHook.astro +53 -0
  103. package/templates/custom/minimal/CustomRoutes.astro +46 -0
  104. package/templates/custom/with-examples/CodeHook.astro +49 -0
  105. package/templates/custom/with-examples/CustomHero.astro +13 -0
  106. package/templates/custom/with-examples/CustomRoutes.astro +39 -0
  107. package/templates/custom/with-examples/pages/Collections.astro +110 -0
  108. package/templates/env.example +8 -0
  109. package/templates/fonts/Inter-Black.woff2 +0 -0
  110. package/templates/fonts/Inter-Bold.woff2 +0 -0
  111. package/templates/fonts/Inter-Regular.woff2 +0 -0
  112. package/templates/icons/h2.svg +1 -0
  113. package/templates/icons/h3.svg +1 -0
  114. package/templates/icons/h4.svg +1 -0
  115. package/templates/icons/h5.svg +1 -0
  116. package/templates/icons/image.svg +7 -0
  117. package/templates/icons/text.svg +6 -0
  118. package/templates/socials/codepen.svg +1 -0
  119. package/templates/socials/discord.svg +1 -0
  120. package/templates/socials/facebook.svg +1 -0
  121. package/templates/socials/github.svg +1 -0
  122. package/templates/socials/instagram.svg +1 -0
  123. package/templates/socials/linkedin.svg +1 -0
  124. package/templates/socials/mail.svg +1 -0
  125. package/templates/socials/rumble.svg +1 -0
  126. package/templates/socials/tiktok.svg +1 -0
  127. package/templates/socials/twitch.svg +1 -0
  128. package/templates/socials/twitter.svg +1 -0
  129. package/templates/socials/x.svg +1 -0
  130. package/templates/socials/youtube.svg +1 -0
  131. package/templates/src/client/analytics-events.ts +213 -0
  132. package/templates/src/client/belief-events.ts +205 -0
  133. package/templates/src/client/sse.ts +667 -0
  134. package/templates/src/components/Footer.astro +246 -0
  135. package/templates/src/components/Fragment.astro +70 -0
  136. package/templates/src/components/Header.astro +458 -0
  137. package/templates/src/components/Menu.tsx +196 -0
  138. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +692 -0
  139. package/templates/src/components/codehooks/BunnyVideoWrapper.astro +78 -0
  140. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +1020 -0
  141. package/templates/src/components/codehooks/EpinetTableView.tsx +594 -0
  142. package/templates/src/components/codehooks/EpinetWrapper.tsx +424 -0
  143. package/templates/src/components/codehooks/FeaturedContent.astro +273 -0
  144. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +738 -0
  145. package/templates/src/components/codehooks/ListContent.astro +460 -0
  146. package/templates/src/components/codehooks/ListContentSetup.tsx +649 -0
  147. package/templates/src/components/codehooks/SankeyDiagram.tsx +359 -0
  148. package/templates/src/components/compositor/Compositor.tsx +144 -0
  149. package/templates/src/components/compositor/Node.tsx +415 -0
  150. package/templates/src/components/compositor/NodeWithGuid.tsx +25 -0
  151. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +87 -0
  152. package/templates/src/components/compositor/elements/Belief.tsx +148 -0
  153. package/templates/src/components/compositor/elements/BgImage.tsx +118 -0
  154. package/templates/src/components/compositor/elements/BgVisualBreak.tsx +102 -0
  155. package/templates/src/components/compositor/elements/BunnyVideo.tsx +63 -0
  156. package/templates/src/components/compositor/elements/IdentifyAs.tsx +66 -0
  157. package/templates/src/components/compositor/elements/PlayButton.tsx +19 -0
  158. package/templates/src/components/compositor/elements/SignUp.tsx +179 -0
  159. package/templates/src/components/compositor/elements/Svg.tsx +33 -0
  160. package/templates/src/components/compositor/elements/ToggleBelief.tsx +36 -0
  161. package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +33 -0
  162. package/templates/src/components/compositor/nodes/BgPaneWrapper.tsx +35 -0
  163. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +189 -0
  164. package/templates/src/components/compositor/nodes/Markdown.tsx +179 -0
  165. package/templates/src/components/compositor/nodes/Pane.tsx +277 -0
  166. package/templates/src/components/compositor/nodes/Pane_eraser.tsx +69 -0
  167. package/templates/src/components/compositor/nodes/Pane_layout.tsx +77 -0
  168. package/templates/src/components/compositor/nodes/RenderChildren.tsx +19 -0
  169. package/templates/src/components/compositor/nodes/StoryFragment.tsx +35 -0
  170. package/templates/src/components/compositor/nodes/TagElement.tsx +14 -0
  171. package/templates/src/components/compositor/nodes/Widget.tsx +115 -0
  172. package/templates/src/components/compositor/nodes/tagElements/NodeA.tsx +4 -0
  173. package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +26 -0
  174. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +248 -0
  175. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +684 -0
  176. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +62 -0
  177. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +120 -0
  178. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +62 -0
  179. package/templates/src/components/compositor/nodes/tagElements/NodeButton.tsx +5 -0
  180. package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +26 -0
  181. package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +28 -0
  182. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +18 -0
  183. package/templates/src/components/compositor/nodes/tagElements/TabIndicator.tsx +51 -0
  184. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +128 -0
  185. package/templates/src/components/compositor/preview/ListContentPreview.tsx +213 -0
  186. package/templates/src/components/compositor/preview/OgImagePreview.tsx +223 -0
  187. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +199 -0
  188. package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +123 -0
  189. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +154 -0
  190. package/templates/src/components/edit/Header.tsx +181 -0
  191. package/templates/src/components/edit/PanelSwitch.tsx +446 -0
  192. package/templates/src/components/edit/SettingsPanel.tsx +70 -0
  193. package/templates/src/components/edit/ToolBar.tsx +101 -0
  194. package/templates/src/components/edit/ToolMode.tsx +121 -0
  195. package/templates/src/components/edit/context/ContextPaneConfig.tsx +91 -0
  196. package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +174 -0
  197. package/templates/src/components/edit/context/ContextPaneConfig_title.tsx +186 -0
  198. package/templates/src/components/edit/pane/AddPanePanel.tsx +136 -0
  199. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +470 -0
  200. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +264 -0
  201. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +623 -0
  202. package/templates/src/components/edit/pane/AddPanePanel_newAICopy.tsx +107 -0
  203. package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +217 -0
  204. package/templates/src/components/edit/pane/AddPanePanel_newCopyMode.tsx +109 -0
  205. package/templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx +39 -0
  206. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +445 -0
  207. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +245 -0
  208. package/templates/src/components/edit/pane/PageGen.tsx +485 -0
  209. package/templates/src/components/edit/pane/PageGenSelector.tsx +238 -0
  210. package/templates/src/components/edit/pane/PageGenSpecial.tsx +362 -0
  211. package/templates/src/components/edit/pane/PageGen_preview.tsx +495 -0
  212. package/templates/src/components/edit/pane/PanePanel_impression.tsx +258 -0
  213. package/templates/src/components/edit/pane/PanePanel_path.tsx +268 -0
  214. package/templates/src/components/edit/pane/PanePanel_slug.tsx +219 -0
  215. package/templates/src/components/edit/pane/PanePanel_title.tsx +142 -0
  216. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +182 -0
  217. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +439 -0
  218. package/templates/src/components/edit/panels/StyleElementPanel.tsx +177 -0
  219. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +349 -0
  220. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +159 -0
  221. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +320 -0
  222. package/templates/src/components/edit/panels/StyleImagePanel.tsx +460 -0
  223. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +296 -0
  224. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +153 -0
  225. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +312 -0
  226. package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +273 -0
  227. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +301 -0
  228. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +132 -0
  229. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +313 -0
  230. package/templates/src/components/edit/panels/StyleLinkPanel.tsx +346 -0
  231. package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +265 -0
  232. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +240 -0
  233. package/templates/src/components/edit/panels/StyleLinkPanel_remove.tsx +94 -0
  234. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +110 -0
  235. package/templates/src/components/edit/panels/StyleParentPanel.tsx +263 -0
  236. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +275 -0
  237. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +112 -0
  238. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +87 -0
  239. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +141 -0
  240. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +428 -0
  241. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +292 -0
  242. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +190 -0
  243. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +152 -0
  244. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +308 -0
  245. package/templates/src/components/edit/state/SaveModal.tsx +811 -0
  246. package/templates/src/components/edit/state/StylesMemory.tsx +310 -0
  247. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +289 -0
  248. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +320 -0
  249. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +888 -0
  250. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +269 -0
  251. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_title.tsx +190 -0
  252. package/templates/src/components/edit/widgets/BeliefWidget.tsx +183 -0
  253. package/templates/src/components/edit/widgets/BunnyWidget.tsx +134 -0
  254. package/templates/src/components/edit/widgets/IdentifyAsWidget.tsx +193 -0
  255. package/templates/src/components/edit/widgets/SignupWidget.tsx +177 -0
  256. package/templates/src/components/edit/widgets/ToggleWidget.tsx +152 -0
  257. package/templates/src/components/edit/widgets/YouTubeWidget.tsx +65 -0
  258. package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +353 -0
  259. package/templates/src/components/fields/ArtpackImage.tsx +480 -0
  260. package/templates/src/components/fields/BackgroundImage.tsx +530 -0
  261. package/templates/src/components/fields/BackgroundImageWrapper.tsx +192 -0
  262. package/templates/src/components/fields/BooleanParam.tsx +67 -0
  263. package/templates/src/components/fields/BunnyMomentSelector.tsx +56 -0
  264. package/templates/src/components/fields/ColorPickerCombo.tsx +284 -0
  265. package/templates/src/components/fields/ImageUpload.tsx +405 -0
  266. package/templates/src/components/fields/MultiParam.tsx +75 -0
  267. package/templates/src/components/fields/PaneBreakCollectionSelector.tsx +97 -0
  268. package/templates/src/components/fields/PaneBreakShapeSelector.tsx +134 -0
  269. package/templates/src/components/fields/SelectedTailwindClass.tsx +44 -0
  270. package/templates/src/components/fields/SingleParam.tsx +73 -0
  271. package/templates/src/components/fields/ViewportComboBox.tsx +252 -0
  272. package/templates/src/components/form/ActionBuilderField.tsx +282 -0
  273. package/templates/src/components/form/ActionBuilderSlugSelector.tsx +182 -0
  274. package/templates/src/components/form/BooleanToggle.tsx +94 -0
  275. package/templates/src/components/form/ColorPicker.tsx +153 -0
  276. package/templates/src/components/form/DateTimeInput.tsx +638 -0
  277. package/templates/src/components/form/EnumSelect.tsx +88 -0
  278. package/templates/src/components/form/FileUpload.tsx +465 -0
  279. package/templates/src/components/form/MagicPathBuilder.tsx +546 -0
  280. package/templates/src/components/form/NumberInput.tsx +101 -0
  281. package/templates/src/components/form/ParagraphArrayInput.tsx +207 -0
  282. package/templates/src/components/form/StringArrayInput.tsx +163 -0
  283. package/templates/src/components/form/StringInput.tsx +88 -0
  284. package/templates/src/components/form/UnsavedChangesBar.tsx +295 -0
  285. package/templates/src/components/form/advanced/APIConfigSection.tsx +69 -0
  286. package/templates/src/components/form/advanced/AuthConfigSection.tsx +97 -0
  287. package/templates/src/components/form/brand/BrandAssetsSection.tsx +93 -0
  288. package/templates/src/components/form/brand/BrandColorsSection.tsx +201 -0
  289. package/templates/src/components/form/brand/SEOSection.tsx +101 -0
  290. package/templates/src/components/form/brand/SiteConfigSection.tsx +61 -0
  291. package/templates/src/components/form/brand/SocialLinksSection.tsx +393 -0
  292. package/templates/src/components/profile/ProfileConsent.tsx +65 -0
  293. package/templates/src/components/profile/ProfileCreate.tsx +462 -0
  294. package/templates/src/components/profile/ProfileEdit.tsx +409 -0
  295. package/templates/src/components/profile/ProfileSwitch.tsx +255 -0
  296. package/templates/src/components/profile/ProfileUnlock.tsx +221 -0
  297. package/templates/src/components/storykeep/Dashboard.tsx +160 -0
  298. package/templates/src/components/storykeep/Dashboard_Activity.tsx +56 -0
  299. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +165 -0
  300. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +451 -0
  301. package/templates/src/components/storykeep/Dashboard_Branding.tsx +95 -0
  302. package/templates/src/components/storykeep/Dashboard_Content.tsx +191 -0
  303. package/templates/src/components/storykeep/controls/UsageCell.tsx +71 -0
  304. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +378 -0
  305. package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +329 -0
  306. package/templates/src/components/storykeep/controls/content/ContentBrowser.tsx +385 -0
  307. package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +149 -0
  308. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +397 -0
  309. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +260 -0
  310. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +439 -0
  311. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +239 -0
  312. package/templates/src/components/storykeep/controls/content/MenuTable.tsx +332 -0
  313. package/templates/src/components/storykeep/controls/content/ResourceBulkIngest.tsx +724 -0
  314. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +355 -0
  315. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +222 -0
  316. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +482 -0
  317. package/templates/src/components/storykeep/state/BrandingWrapper.tsx +42 -0
  318. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +350 -0
  319. package/templates/src/components/storykeep/widgets/ResponsiveLine.tsx +319 -0
  320. package/templates/src/components/storykeep/widgets/Wizard.tsx +278 -0
  321. package/templates/src/components/tenant/RegistrationForm.tsx +447 -0
  322. package/templates/src/components/widgets/BunnyVideoHero.astro +775 -0
  323. package/templates/src/components/widgets/Impression.tsx +102 -0
  324. package/templates/src/components/widgets/ImpressionWrapper.tsx +214 -0
  325. package/templates/src/constants/beliefs.ts +61 -0
  326. package/templates/src/constants/brandThemes.ts +133 -0
  327. package/templates/src/constants/prompts.json +55 -0
  328. package/templates/src/constants/shapes.ts +556 -0
  329. package/templates/src/constants/stopWords.ts +116 -0
  330. package/templates/src/constants/tailwindColors.json +344 -0
  331. package/templates/src/constants.ts +274 -0
  332. package/templates/src/hooks/useFormState.ts +203 -0
  333. package/templates/src/layouts/Layout.astro +290 -0
  334. package/templates/src/lib/session.ts +126 -0
  335. package/templates/src/lib/storyData.ts +56 -0
  336. package/templates/src/middleware.ts +52 -0
  337. package/templates/src/pages/404.astro +54 -0
  338. package/templates/src/pages/[...slug]/edit.astro +216 -0
  339. package/templates/src/pages/[...slug].astro +148 -0
  340. package/templates/src/pages/api/auth/decode.ts +101 -0
  341. package/templates/src/pages/api/auth/login.ts +122 -0
  342. package/templates/src/pages/api/auth/logout.ts +37 -0
  343. package/templates/src/pages/api/auth/profile.ts +76 -0
  344. package/templates/src/pages/api/orphan-analysis.ts +106 -0
  345. package/templates/src/pages/api/tailwind.ts +116 -0
  346. package/templates/src/pages/collections/[param1].astro +65 -0
  347. package/templates/src/pages/context/[...contextSlug]/edit.astro +207 -0
  348. package/templates/src/pages/context/[...contextSlug].astro +161 -0
  349. package/templates/src/pages/llms.txt.ts +122 -0
  350. package/templates/src/pages/maint.astro +183 -0
  351. package/templates/src/pages/media/[...slug].astro +67 -0
  352. package/templates/src/pages/robots.txt.ts +36 -0
  353. package/templates/src/pages/sandbox/activate.astro +258 -0
  354. package/templates/src/pages/sandbox/register.astro +44 -0
  355. package/templates/src/pages/sandbox/success.astro +179 -0
  356. package/templates/src/pages/sitemap.xml.ts +119 -0
  357. package/templates/src/pages/storykeep/advanced.astro +69 -0
  358. package/templates/src/pages/storykeep/branding.astro +57 -0
  359. package/templates/src/pages/storykeep/content.astro +71 -0
  360. package/templates/src/pages/storykeep/init.astro +36 -0
  361. package/templates/src/pages/storykeep/login.astro +266 -0
  362. package/templates/src/pages/storykeep/logout.astro +84 -0
  363. package/templates/src/pages/storykeep/profile.astro +98 -0
  364. package/templates/src/pages/storykeep.astro +81 -0
  365. package/templates/src/stores/analytics.ts +171 -0
  366. package/templates/src/stores/backend.ts +16 -0
  367. package/templates/src/stores/navigation.ts +149 -0
  368. package/templates/src/stores/nodes.ts +2390 -0
  369. package/templates/src/stores/nodesHistory.ts +85 -0
  370. package/templates/src/stores/notificationSystem.ts +41 -0
  371. package/templates/src/stores/orphanAnalysis.ts +409 -0
  372. package/templates/src/stores/storykeep.ts +247 -0
  373. package/templates/src/types/astro.ts +86 -0
  374. package/templates/src/types/compositorTypes.ts +456 -0
  375. package/templates/src/types/formTypes.ts +281 -0
  376. package/templates/src/types/multiTenant.ts +77 -0
  377. package/templates/src/types/nodeProps.ts +66 -0
  378. package/templates/src/types/tractstack.ts +445 -0
  379. package/templates/src/utils/aai/getTitleSlug.ts +72 -0
  380. package/templates/src/utils/actions/actionButton.ts +101 -0
  381. package/templates/src/utils/actions/lispLexer.ts +57 -0
  382. package/templates/src/utils/actions/preParse_Action.ts +85 -0
  383. package/templates/src/utils/actions/preParse_Bunny.ts +50 -0
  384. package/templates/src/utils/actions/preParse_Clicked.ts +87 -0
  385. package/templates/src/utils/actions/preParse_Impression.ts +71 -0
  386. package/templates/src/utils/api/advancedConfig.ts +66 -0
  387. package/templates/src/utils/api/advancedHelpers.ts +134 -0
  388. package/templates/src/utils/api/beliefConfig.ts +87 -0
  389. package/templates/src/utils/api/beliefHelpers.ts +196 -0
  390. package/templates/src/utils/api/brandConfig.ts +126 -0
  391. package/templates/src/utils/api/brandHelpers.ts +155 -0
  392. package/templates/src/utils/api/fileHelpers.ts +306 -0
  393. package/templates/src/utils/api/menuConfig.ts +57 -0
  394. package/templates/src/utils/api/menuHelpers.ts +156 -0
  395. package/templates/src/utils/api/resourceConfig.ts +158 -0
  396. package/templates/src/utils/api/resourceHelpers.ts +72 -0
  397. package/templates/src/utils/api/tenantConfig.ts +97 -0
  398. package/templates/src/utils/api/tenantHelpers.ts +172 -0
  399. package/templates/src/utils/api.ts +183 -0
  400. package/templates/src/utils/auth.ts +150 -0
  401. package/templates/src/utils/backend.ts +243 -0
  402. package/templates/src/utils/compositor/TemplateMarkdowns.ts +118 -0
  403. package/templates/src/utils/compositor/TemplateNodes.ts +138 -0
  404. package/templates/src/utils/compositor/TemplatePanes.ts +100 -0
  405. package/templates/src/utils/compositor/allowInsert.ts +100 -0
  406. package/templates/src/utils/compositor/domHelpers.ts +37 -0
  407. package/templates/src/utils/compositor/handleClickEvent.ts +131 -0
  408. package/templates/src/utils/compositor/nodesHelper.ts +491 -0
  409. package/templates/src/utils/compositor/nodesMarkdownGenerator.ts +292 -0
  410. package/templates/src/utils/compositor/processMarkdown.ts +431 -0
  411. package/templates/src/utils/compositor/reduceNodesClassNames.ts +192 -0
  412. package/templates/src/utils/compositor/tailwindClasses.ts +1795 -0
  413. package/templates/src/utils/compositor/tailwindColors.ts +227 -0
  414. package/templates/src/utils/compositor/templateMarkdownStyles.ts +1265 -0
  415. package/templates/src/utils/compositor/typeGuards.ts +193 -0
  416. package/templates/src/utils/etl/extractor.ts +119 -0
  417. package/templates/src/utils/etl/index.ts +88 -0
  418. package/templates/src/utils/etl/loader.ts +36 -0
  419. package/templates/src/utils/etl/transformer.ts +286 -0
  420. package/templates/src/utils/helpers.ts +435 -0
  421. package/templates/src/utils/layout.ts +209 -0
  422. package/templates/src/utils/profileStorage.ts +306 -0
  423. package/templates/src/utils/useInterval.ts +27 -0
  424. package/templates/tailwind.config.cjs +169 -0
  425. package/utils/create-resolver.ts +10 -0
  426. package/utils/inject-files.ts +2140 -0
  427. package/utils/validate-config.ts +43 -0
@@ -0,0 +1,266 @@
1
+ ---
2
+ import { isAuthenticated } from '@/utils/auth';
3
+ import { preHealthCheck } from '@/utils/backend';
4
+ import { getBrandConfig } from '@/utils/api/brandConfig';
5
+ import { freshInstallStore } from '@/stores/backend';
6
+
7
+ const tenantId =
8
+ Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
9
+
10
+ const healthCheckRedirect = await preHealthCheck(tenantId);
11
+ if (healthCheckRedirect !== undefined) {
12
+ return healthCheckRedirect;
13
+ }
14
+
15
+ const url = new URL(Astro.request.url);
16
+ const redirectPath = url.searchParams.get('redirect') || '/storykeep';
17
+ const forceLogin = url.searchParams.get('force') === 'true';
18
+
19
+ if (isAuthenticated(Astro) && !forceLogin) {
20
+ return Astro.redirect(redirectPath);
21
+ }
22
+
23
+ const isDev = import.meta.env.DEV;
24
+ const isInitialized = !freshInstallStore.get().needsSetup;
25
+ const brandConfig = await getBrandConfig(tenantId);
26
+ const cssBasePath = isInitialized ? '/media/css' : '/styles';
27
+ const getAssetPath = (configPath: string, fallback: string) => {
28
+ // Always prioritize brandConfig values when they exist
29
+ if (configPath && configPath !== '') {
30
+ return configPath;
31
+ }
32
+ return fallback;
33
+ };
34
+ const logo = getAssetPath(brandConfig?.LOGO, '/brand/logo.svg');
35
+ const wordmark = getAssetPath(brandConfig?.WORDMARK, '/brand/wordmark.svg');
36
+ const mainStylesUrl = isDev
37
+ ? `${cssBasePath}/storykeep.css`
38
+ : `${cssBasePath}/frontend.css`;
39
+ ---
40
+
41
+ <!doctype html>
42
+ <html lang="en" class="bg-mywhite h-full">
43
+ <head>
44
+ <meta charset="UTF-8" />
45
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
46
+ <title>Login | TractStack</title>
47
+ <link rel="stylesheet" href={`${cssBasePath}/custom.css`} />
48
+ <link rel="stylesheet" href={mainStylesUrl} />
49
+ </head>
50
+ <body class="h-full">
51
+ <div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
52
+ <div class="mx-auto pb-6">
53
+ <!-- Logo and Wordmark -->
54
+ <div class="flex flex-col items-center justify-center gap-4">
55
+ <div class="h-16 w-auto">
56
+ <img
57
+ src={logo}
58
+ class="pointer-events-none h-full w-auto"
59
+ alt="Logo"
60
+ />
61
+ </div>
62
+ <div class="h-16 w-auto">
63
+ <img
64
+ src={wordmark}
65
+ class="pointer-events-none h-full w-auto max-w-48 md:max-w-72"
66
+ alt="Wordmark"
67
+ />
68
+ </div>
69
+ </div>
70
+
71
+ <h2
72
+ class="text-mydarkgrey mt-6 text-center text-2xl font-bold leading-9 tracking-tight"
73
+ >
74
+ Sign in to your Storykeep
75
+ </h2>
76
+ </div>
77
+
78
+ <div class="mx-auto">
79
+ <div class="rounded-lg bg-white px-6 py-12 shadow-inner">
80
+ <!-- Error message -->
81
+ <div
82
+ id="loginError"
83
+ class="bg-myred/10 mb-6 rounded-md p-4"
84
+ style="display: none;"
85
+ >
86
+ <div class="flex">
87
+ <div class="flex-shrink-0">
88
+ <svg
89
+ class="text-myred h-5 w-5"
90
+ viewBox="0 0 20 20"
91
+ fill="currentColor"
92
+ aria-hidden="true"
93
+ >
94
+ <path
95
+ fill-rule="evenodd"
96
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
97
+ clip-rule="evenodd"></path>
98
+ </svg>
99
+ </div>
100
+ <div class="ml-3">
101
+ <h3 class="text-myred text-sm font-bold">
102
+ Invalid credentials
103
+ </h3>
104
+ <div class="text-myred/90 mt-2 text-sm">
105
+ <p>Please check your password and try again.</p>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- Login form -->
112
+ <div id="loginForm" class="space-y-6">
113
+ <div>
114
+ <label
115
+ for="password"
116
+ class="text-mydarkgrey block text-sm font-bold leading-6"
117
+ >
118
+ Password
119
+ </label>
120
+ <div class="mt-2">
121
+ <input
122
+ id="password"
123
+ name="password"
124
+ type="password"
125
+ required
126
+ class="text-mydarkgrey ring-mylightgrey placeholder:text-mylightgrey focus:ring-myorange block w-full rounded-md border-0 px-3 py-1.5 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6"
127
+ />
128
+ </div>
129
+ </div>
130
+
131
+ <input
132
+ type="hidden"
133
+ id="redirectPath"
134
+ name="redirect"
135
+ value={redirectPath}
136
+ />
137
+
138
+ <div>
139
+ <button
140
+ type="button"
141
+ id="submitButton"
142
+ class="bg-myblue hover:bg-myorange focus-visible:outline-myorange flex w-full justify-center rounded-md px-3 py-1.5 text-sm font-bold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
143
+ >
144
+ Sign in
145
+ </button>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Success message -->
150
+ <div id="loginSuccess" class="space-y-6" style="display: none;">
151
+ <div class="rounded-md bg-green-50 p-4">
152
+ <div class="flex">
153
+ <div class="flex-shrink-0">
154
+ <svg
155
+ class="h-5 w-5 text-green-400"
156
+ viewBox="0 0 20 20"
157
+ fill="currentColor"
158
+ aria-hidden="true"
159
+ >
160
+ <path
161
+ fill-rule="evenodd"
162
+ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.236 4.53L7.53 10.25a.75.75 0 00-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
163
+ clip-rule="evenodd"></path>
164
+ </svg>
165
+ </div>
166
+ <div class="ml-3">
167
+ <h3 class="text-sm font-bold text-green-800">
168
+ Login successful!
169
+ </h3>
170
+ <div class="mt-2 text-sm text-green-700">
171
+ <p>Role: <span id="userRole"></span></p>
172
+ <p>Redirecting...</p>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <script is:inline>
183
+ document.addEventListener('DOMContentLoaded', function () {
184
+ const submitButton = document.getElementById('submitButton');
185
+ const passwordInput = document.getElementById('password');
186
+ const redirectInput = document.getElementById('redirectPath');
187
+ const loginErrorDiv = document.getElementById('loginError');
188
+ const loginFormDiv = document.getElementById('loginForm');
189
+ const loginSuccessDiv = document.getElementById('loginSuccess');
190
+ const userRoleSpan = document.getElementById('userRole');
191
+
192
+ if (submitButton && passwordInput && redirectInput) {
193
+ const buttonText = submitButton.textContent.trim();
194
+
195
+ submitButton.addEventListener('click', async function () {
196
+ // Hide error message
197
+ if (loginErrorDiv) loginErrorDiv.style.display = 'none';
198
+
199
+ // Show loading state
200
+ submitButton.disabled = true;
201
+ submitButton.textContent = 'Signing in...';
202
+
203
+ const password = passwordInput.value;
204
+ const redirectPath = redirectInput.value;
205
+
206
+ if (!password) {
207
+ if (loginErrorDiv) loginErrorDiv.style.display = 'block';
208
+ submitButton.disabled = false;
209
+ submitButton.textContent = buttonText;
210
+ return;
211
+ }
212
+
213
+ try {
214
+ const response = await fetch('/api/auth/login', {
215
+ method: 'POST',
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ Accept: 'application/json',
219
+ },
220
+ body: JSON.stringify({
221
+ password,
222
+ redirect: redirectPath,
223
+ }),
224
+ });
225
+
226
+ const result = await response.json();
227
+
228
+ if (result.success) {
229
+ // Show success message with role
230
+ if (loginFormDiv) loginFormDiv.style.display = 'none';
231
+ if (loginSuccessDiv) loginSuccessDiv.style.display = 'block';
232
+ if (userRoleSpan)
233
+ userRoleSpan.textContent = result.role || 'authenticated';
234
+
235
+ // Console log for debugging
236
+ console.log('Login successful:', result);
237
+ console.log('User role:', result.role);
238
+
239
+ // Redirect after short delay
240
+ setTimeout(() => {
241
+ window.location.href = result.redirect || redirectPath;
242
+ }, 1500);
243
+ } else {
244
+ if (loginErrorDiv) loginErrorDiv.style.display = 'block';
245
+ submitButton.disabled = false;
246
+ submitButton.textContent = buttonText;
247
+ }
248
+ } catch (error) {
249
+ console.error('Login error:', error);
250
+ if (loginErrorDiv) loginErrorDiv.style.display = 'block';
251
+ submitButton.disabled = false;
252
+ submitButton.textContent = buttonText;
253
+ }
254
+ });
255
+
256
+ passwordInput.addEventListener('keypress', function (event) {
257
+ if (event.key === 'Enter') {
258
+ event.preventDefault();
259
+ submitButton.click();
260
+ }
261
+ });
262
+ }
263
+ });
264
+ </script>
265
+ </body>
266
+ </html>
@@ -0,0 +1,84 @@
1
+ ---
2
+ // Cookie-based logout - server-side cookie removal
3
+ // Clears HTTP-only admin/editor auth cookies
4
+ ---
5
+
6
+ <!doctype html>
7
+ <html lang="en">
8
+ <head>
9
+ <meta charset="UTF-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
+ <title>Logging out...</title>
12
+ <style>
13
+ body {
14
+ font-family: system-ui, sans-serif;
15
+ display: flex;
16
+ justify-content: center;
17
+ align-items: center;
18
+ min-height: 100vh;
19
+ margin: 0;
20
+ background-color: #f3f4f6;
21
+ }
22
+ .logout-container {
23
+ text-align: center;
24
+ background: white;
25
+ padding: 2rem;
26
+ border-radius: 0.5rem;
27
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
28
+ }
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <div class="logout-container">
33
+ <h1>Logging out...</h1>
34
+ <p>Please wait while we clear your session.</p>
35
+ </div>
36
+
37
+ <script>
38
+ // Clear authentication data
39
+ async function logout() {
40
+ try {
41
+ // Call server-side logout API to clear HTTP-only cookies
42
+ const response = await fetch('/api/auth/logout', {
43
+ method: 'POST',
44
+ headers: {
45
+ 'Content-Type': 'application/json',
46
+ },
47
+ });
48
+
49
+ const result = await response.json();
50
+
51
+ if (result.success) {
52
+ console.log('StoryKeep: Admin logout successful');
53
+ } else {
54
+ console.error('StoryKeep: Admin logout failed:', result.error);
55
+ }
56
+
57
+ // Also clear any remaining TractStack user profile data
58
+ const tractStackKeys = [];
59
+ for (let i = 0; i < localStorage.length; i++) {
60
+ const key = localStorage.key(i);
61
+ if (key && key.startsWith('tractstack_')) {
62
+ tractStackKeys.push(key);
63
+ }
64
+ }
65
+ tractStackKeys.forEach((key) => localStorage.removeItem(key));
66
+
67
+ console.log('TractStack: Complete logout finished');
68
+
69
+ // Redirect to home page
70
+ setTimeout(() => {
71
+ window.location.href = '/';
72
+ }, 1000);
73
+ } catch (error) {
74
+ console.error('Logout error:', error);
75
+ // Still redirect even if logout API fails
76
+ window.location.href = '/';
77
+ }
78
+ }
79
+
80
+ // Execute logout immediately
81
+ logout();
82
+ </script>
83
+ </body>
84
+ </html>
@@ -0,0 +1,98 @@
1
+ ---
2
+ import Layout from '@/layouts/Layout.astro';
3
+ import { ProfileSwitch } from '@/components/profile/ProfileSwitch';
4
+ import { ProfileConsent } from '@/components/profile/ProfileConsent';
5
+ import { preHealthCheck } from '@/utils/backend';
6
+
7
+ const tenantId =
8
+ Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
9
+
10
+ const healthCheckRedirect = await preHealthCheck(tenantId);
11
+ if (healthCheckRedirect !== undefined) {
12
+ return healthCheckRedirect;
13
+ }
14
+
15
+ const config = {
16
+ title: 'Configure your Session',
17
+ description: 'Manage your TractStack profile and session preferences',
18
+ };
19
+ ---
20
+
21
+ <Layout
22
+ title={config.title}
23
+ slug="profile"
24
+ isContext={false}
25
+ isStoryKeep={false}
26
+ isEditable={false}
27
+ >
28
+ <main id="main-content">
29
+ <div class="px-6 py-24 font-sans text-xl">
30
+ <div class="mx-auto max-w-3xl">
31
+ <div class="space-y-6">
32
+ <p class="text-black">
33
+ This website has been "pressed" with Tract Stack. As you read and
34
+ click around it will open up like a choose-your-own adventure book.
35
+ </p>
36
+ <p class="text-black">
37
+ Without your consent, no personal information will be collected from
38
+ your device.
39
+ </p>
40
+
41
+ <h3 class="inline-flex items-center pt-12 text-xl text-gray-700">
42
+ Allow Tract Stack to retain memory of your session and preferences
43
+ <span
44
+ class="pl-2 text-gray-400"
45
+ title="We will use a few kilobytes of localStorage in your browser."
46
+ >
47
+ <svg
48
+ class="h-5 w-5"
49
+ fill="none"
50
+ stroke="currentColor"
51
+ viewBox="0 0 24 24"
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ >
54
+ <path
55
+ stroke-linecap="round"
56
+ stroke-linejoin="round"
57
+ stroke-width="2"
58
+ d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
59
+ ></path>
60
+ </svg>
61
+ </span>
62
+ </h3>
63
+
64
+ <!-- Consent Toggle using Ark UI -->
65
+ <div class="py-4">
66
+ <ProfileConsent client:load />
67
+ </div>
68
+
69
+ <!-- Profile Switch Component -->
70
+ <div class="pt-6">
71
+ <ProfileSwitch client:load />
72
+ </div>
73
+
74
+ <div class="pt-8 text-sm text-gray-600">
75
+ <p class="pb-4">
76
+ By enabling session memory, Tract Stack can provide a personalized
77
+ reading experience. This includes:
78
+ </p>
79
+ <ul class="space-y-2 pl-6 text-base">
80
+ <li class="list-disc">
81
+ Remembering your reading progress and preferences
82
+ </li>
83
+ <li class="list-disc">
84
+ Showing content tailored to your interests
85
+ </li>
86
+ <li class="list-disc">Maintaining your session across visits</li>
87
+ </ul>
88
+ <p class="pt-4 text-xs">
89
+ All data is stored locally in your browser. No personal
90
+ information is transmitted to external servers without your
91
+ explicit consent.
92
+ </p>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </main>
98
+ </Layout>
@@ -0,0 +1,81 @@
1
+ ---
2
+ import Layout from '@/layouts/Layout.astro';
3
+ import StoryKeepDashboard from '@/components/storykeep/Dashboard';
4
+ import StoryKeepDashboard_Analytics from '@/components/storykeep/Dashboard_Analytics';
5
+ import { requireAdminOrEditor, isAuthenticated, isAdmin } from '@/utils/auth';
6
+ import { getFullContentMap } from '@/stores/analytics';
7
+ import { getBrandConfig } from '@/utils/api/brandConfig';
8
+ import { preHealthCheck } from '@/utils/backend';
9
+
10
+ const tenantId =
11
+ Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
12
+
13
+ const healthCheckRedirect = await preHealthCheck(tenantId);
14
+ if (healthCheckRedirect !== undefined) {
15
+ return healthCheckRedirect;
16
+ }
17
+
18
+ const authCheck = requireAdminOrEditor(Astro);
19
+ if (authCheck) {
20
+ return authCheck;
21
+ }
22
+ const userIsAuthenticated = isAuthenticated(Astro);
23
+ const userIsAdmin = isAdmin(Astro);
24
+ const role = userIsAdmin ? `admin` : userIsAuthenticated ? `editor` : null;
25
+
26
+ const brandConfig = await getBrandConfig(
27
+ Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default'
28
+ );
29
+ const initializing = !brandConfig.SITE_INIT;
30
+
31
+ // Redirect to branding page during initial setup
32
+ if (initializing) {
33
+ return Astro.redirect('/storykeep/branding');
34
+ }
35
+
36
+ const title = 'Analytics | StoryKeep';
37
+
38
+ let fullContentMap;
39
+ let homeSlug = 'hello';
40
+
41
+ try {
42
+ fullContentMap = await getFullContentMap(
43
+ Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default'
44
+ );
45
+ homeSlug = fullContentMap.find((item) => item.isHome)?.slug || 'hello';
46
+ } catch (error) {
47
+ return Astro.redirect(
48
+ `/maint?from=${encodeURIComponent(Astro.url.pathname)}`
49
+ );
50
+ }
51
+ ---
52
+
53
+ <Layout title={title} isStoryKeep={true} slug="storykeep">
54
+ <main id="main-content" class="min-h-screen w-full">
55
+ <div class="p-3.5 md:p-8">
56
+ <StoryKeepDashboard
57
+ client:only="react"
58
+ fullContentMap={fullContentMap}
59
+ homeSlug={homeSlug}
60
+ activeTab="analytics"
61
+ role={role}
62
+ initializing={initializing}
63
+ />
64
+
65
+ <!-- Only show Analytics content when home page has title -->
66
+ {
67
+ fullContentMap.find((item) => item.slug === homeSlug)?.title?.trim() ? (
68
+ <StoryKeepDashboard_Analytics
69
+ client:only="react"
70
+ fullContentMap={fullContentMap}
71
+ initializing={initializing}
72
+ />
73
+ ) : (
74
+ <div class="mt-8 text-gray-500">
75
+ <p>Complete the setup wizard above to unlock Analytics.</p>
76
+ </div>
77
+ )
78
+ }
79
+ </div>
80
+ </main>
81
+ </Layout>
@@ -0,0 +1,171 @@
1
+ import { atom } from 'nanostores';
2
+ import { TractStackAPI } from '@/utils/api';
3
+
4
+ // Internal tenant-keyed storage
5
+ const tenantEpinetCustomFilters = atom<
6
+ Record<
7
+ string,
8
+ {
9
+ enabled: boolean;
10
+ visitorType: 'all' | 'anonymous' | 'known';
11
+ selectedUserId: string | null;
12
+ startTimeUTC: string | null;
13
+ endTimeUTC: string | null;
14
+ userCounts: Array<{ id: string; count: number; isKnown: boolean }>;
15
+ hourlyNodeActivity: Record<
16
+ string,
17
+ Record<
18
+ string,
19
+ {
20
+ events: Record<string, number>;
21
+ visitorIds: string[];
22
+ }
23
+ >
24
+ >;
25
+ }
26
+ >
27
+ >({});
28
+
29
+ const tenantFullContentMaps = atom<
30
+ Record<
31
+ string,
32
+ {
33
+ data: any[];
34
+ lastUpdated: number;
35
+ }
36
+ >
37
+ >({});
38
+
39
+ // Helper to get current tenant ID
40
+ function getCurrentTenantId(): string {
41
+ if (typeof window !== 'undefined' && window.TRACTSTACK_CONFIG?.tenantId) {
42
+ return window.TRACTSTACK_CONFIG.tenantId;
43
+ }
44
+ return import.meta.env.PUBLIC_TENANTID || 'default';
45
+ }
46
+
47
+ // Default filter state
48
+ const defaultEpinetFilters = {
49
+ enabled: false,
50
+ visitorType: 'all' as 'all' | 'anonymous' | 'known',
51
+ selectedUserId: null,
52
+ startTimeUTC: null,
53
+ endTimeUTC: null,
54
+ userCounts: [],
55
+ hourlyNodeActivity: {},
56
+ };
57
+
58
+ // Create tenant-aware atoms that work with useStore
59
+ const createEpinetFiltersStore = () => {
60
+ const store = {
61
+ get: () => {
62
+ const tenantId = getCurrentTenantId();
63
+ return tenantEpinetCustomFilters.get()[tenantId] || defaultEpinetFilters;
64
+ },
65
+
66
+ set: (tenantId: string, updates: any) => {
67
+ const currentFilters =
68
+ tenantEpinetCustomFilters.get()[tenantId] || defaultEpinetFilters;
69
+ tenantEpinetCustomFilters.set({
70
+ ...tenantEpinetCustomFilters.get(),
71
+ [tenantId]: {
72
+ ...currentFilters,
73
+ ...updates,
74
+ },
75
+ });
76
+ },
77
+
78
+ subscribe: (callback: (value: any) => void) => {
79
+ const tenantId = getCurrentTenantId();
80
+ return tenantEpinetCustomFilters.subscribe((filters) => {
81
+ callback(filters[tenantId] || defaultEpinetFilters);
82
+ });
83
+ },
84
+
85
+ // Required nanostore properties for useStore
86
+ lc: 0,
87
+ listen: function (callback: any) {
88
+ return this.subscribe(callback);
89
+ },
90
+ notify: function () {},
91
+ off: function () {},
92
+ get value() {
93
+ return this.get();
94
+ },
95
+ };
96
+
97
+ return store;
98
+ };
99
+
100
+ const createFullContentMapStore = () => {
101
+ const store = {
102
+ get: () => {
103
+ const tenantId = getCurrentTenantId();
104
+ return tenantFullContentMaps.get()[tenantId] || null;
105
+ },
106
+
107
+ set: (tenantId: string, data: { data: any[]; lastUpdated: number }) => {
108
+ tenantFullContentMaps.set({
109
+ ...tenantFullContentMaps.get(),
110
+ [tenantId]: data,
111
+ });
112
+ },
113
+
114
+ subscribe: (callback: (value: any) => void) => {
115
+ const tenantId = getCurrentTenantId();
116
+ return tenantFullContentMaps.subscribe((maps) => {
117
+ callback(maps[tenantId] || null);
118
+ });
119
+ },
120
+
121
+ // Required nanostore properties for useStore
122
+ lc: 0,
123
+ listen: function (callback: any) {
124
+ return this.subscribe(callback);
125
+ },
126
+ notify: function () {},
127
+ off: function () {},
128
+ get value() {
129
+ return this.get();
130
+ },
131
+ };
132
+
133
+ return store;
134
+ };
135
+
136
+ export const epinetCustomFilters = createEpinetFiltersStore();
137
+ export const fullContentMapStore = createFullContentMapStore();
138
+
139
+ export async function getFullContentMap(tenantId: string): Promise<any[]> {
140
+ const api = new TractStackAPI(tenantId);
141
+
142
+ // Check tenant-specific cache
143
+ const cached = tenantFullContentMaps.get()[tenantId];
144
+
145
+ try {
146
+ const response = await api.getContentMapWithTimestamp(cached?.lastUpdated);
147
+
148
+ if (response.success && response.data) {
149
+ // Update tenant-specific cache
150
+ const newData = {
151
+ data: response.data.data,
152
+ lastUpdated: response.data.lastUpdated,
153
+ };
154
+
155
+ tenantFullContentMaps.set({
156
+ ...tenantFullContentMaps.get(),
157
+ [tenantId]: newData,
158
+ });
159
+
160
+ return newData.data;
161
+ } else {
162
+ const errorMsg = response.error || '';
163
+ if (errorMsg.includes('304')) {
164
+ return cached?.data || [];
165
+ }
166
+ }
167
+ } catch (error) {
168
+ console.error('Failed to fetch content map:', error);
169
+ }
170
+ return cached?.data || [];
171
+ }