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,409 @@
1
+ import { useEffect, useState, useMemo } from 'react';
2
+ import { Select } from '@ark-ui/react/select';
3
+ import { Portal } from '@ark-ui/react/portal';
4
+ import { createListCollection } from '@ark-ui/react/collection';
5
+ import ChevronRightIcon from '@heroicons/react/20/solid/ChevronRightIcon';
6
+ import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon';
7
+ import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
8
+ import BellSlashIcon from '@heroicons/react/24/outline/BellSlashIcon';
9
+ import BoltIcon from '@heroicons/react/24/outline/BoltIcon';
10
+ import ChatBubbleBottomCenterIcon from '@heroicons/react/24/outline/ChatBubbleBottomCenterIcon';
11
+ import { ProfileStorage } from '@/utils/profileStorage';
12
+ import { contactPersona } from '@/constants';
13
+ import type { FormEvent } from 'react';
14
+
15
+ interface ProfileEditProps {
16
+ onSuccess?: () => void;
17
+ onError?: (error: string) => void;
18
+ }
19
+
20
+ async function updateProfile(payload: {
21
+ firstname: string;
22
+ email: string;
23
+ codeword: string;
24
+ persona: string;
25
+ bio: string;
26
+ }) {
27
+ try {
28
+ const sessionData = ProfileStorage.prepareHandshakeData();
29
+ const currentConsent = ProfileStorage.getConsent() || '1'; // default to true
30
+
31
+ const response = await fetch('/api/auth/profile', {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ body: JSON.stringify({
37
+ firstname: payload.firstname,
38
+ email: payload.email,
39
+ codeword: payload.codeword,
40
+ contactPersona: payload.persona,
41
+ shortBio: payload.bio,
42
+ sessionId: sessionData.sessionId,
43
+ consent: currentConsent, // Include actual consent value
44
+ isUpdate: true, // This is an update operation
45
+ }),
46
+ });
47
+
48
+ const result = await response.json();
49
+
50
+ if (!result.success) {
51
+ return { success: false, error: result.error || 'Profile update failed' };
52
+ }
53
+
54
+ // Store updated profile data and tokens - use lowercase field names consistently
55
+ if (result.profile) {
56
+ ProfileStorage.setProfileData({
57
+ firstname: result.profile.firstname || result.profile.Firstname, // handle both cases
58
+ contactPersona:
59
+ result.profile.contactPersona || result.profile.ContactPersona,
60
+ email: result.profile.email || result.profile.Email,
61
+ shortBio: result.profile.shortBio || result.profile.ShortBio,
62
+ });
63
+ }
64
+
65
+ if (result.token) {
66
+ ProfileStorage.storeProfileToken(result.token);
67
+ }
68
+
69
+ if (result.encryptedEmail && result.encryptedCode) {
70
+ ProfileStorage.storeEncryptedCredentials(
71
+ result.encryptedEmail,
72
+ result.encryptedCode
73
+ );
74
+ }
75
+
76
+ if (result.consent) {
77
+ ProfileStorage.storeConsent(result.consent);
78
+ }
79
+
80
+ return { success: true, data: result };
81
+ } catch (e) {
82
+ console.error('Profile update error:', e);
83
+ return { success: false, error: 'Network error occurred' };
84
+ }
85
+ }
86
+
87
+ const classNames = (...classes: (string | undefined | false)[]): string => {
88
+ return classes.filter(Boolean).join(' ');
89
+ };
90
+
91
+ export const ProfileEdit = ({ onSuccess, onError }: ProfileEditProps) => {
92
+ const [submitted, setSubmitted] = useState<boolean | undefined>(undefined);
93
+ const [email, setEmail] = useState('');
94
+ const [firstname, setFirstname] = useState('');
95
+ const [bio, setBio] = useState('');
96
+ const [codeword, setCodeword] = useState('');
97
+ const [saved, setSaved] = useState(false);
98
+ const [isLoading, setIsLoading] = useState(false);
99
+ const [personaSelected, setPersonaSelected] = useState(contactPersona[0]);
100
+
101
+ // Create collection for Ark UI Select
102
+ const personaCollection = useMemo(() => {
103
+ return createListCollection({
104
+ items: contactPersona,
105
+ itemToValue: (item) => item.id,
106
+ itemToString: (item) => item.title,
107
+ });
108
+ }, []);
109
+
110
+ const Icon =
111
+ personaSelected.title === `DMs open`
112
+ ? ChatBubbleBottomCenterIcon
113
+ : personaSelected.title === `Major Updates Only`
114
+ ? ArrowPathRoundedSquareIcon
115
+ : personaSelected.title === `All Updates`
116
+ ? BoltIcon
117
+ : BellSlashIcon;
118
+
119
+ const iconClass =
120
+ personaSelected.title === `DMs open`
121
+ ? `text-green-500`
122
+ : personaSelected.title === `Major Updates Only`
123
+ ? `text-gray-600`
124
+ : personaSelected.title === `All Updates`
125
+ ? `text-orange-500`
126
+ : `text-gray-600`;
127
+
128
+ // Initialize form with current profile data
129
+ useEffect(() => {
130
+ const profileData = ProfileStorage.getProfileData();
131
+
132
+ if (profileData.firstname) setFirstname(profileData.firstname);
133
+ if (profileData.email) setEmail(profileData.email);
134
+ if (profileData.shortBio) setBio(profileData.shortBio);
135
+ if (profileData.contactPersona) {
136
+ const pref = contactPersona.find(
137
+ (p) => p.id === profileData.contactPersona
138
+ );
139
+ if (pref) setPersonaSelected(pref);
140
+ }
141
+ }, []);
142
+
143
+ const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
144
+ e.preventDefault();
145
+ setSubmitted(true);
146
+ setIsLoading(true);
147
+ setSaved(false);
148
+
149
+ if (firstname && codeword && email && personaSelected.id) {
150
+ const result = await updateProfile({
151
+ firstname,
152
+ email,
153
+ codeword,
154
+ bio: bio || '', // Ensure bio is string, not undefined
155
+ persona: personaSelected.id,
156
+ });
157
+
158
+ if (result.success) {
159
+ setSaved(true);
160
+ onSuccess?.();
161
+ } else {
162
+ onError?.(result.error || 'Profile update failed');
163
+ }
164
+ } else {
165
+ console.error('Missing required fields:', {
166
+ firstname,
167
+ email,
168
+ codeword,
169
+ persona: personaSelected.id,
170
+ });
171
+ }
172
+
173
+ setIsLoading(false);
174
+ };
175
+
176
+ const handlePersonaChange = (details: { value: string[] }) => {
177
+ const selectedId = details.value[0];
178
+ if (selectedId) {
179
+ const selected = contactPersona.find((item) => item.id === selectedId);
180
+ if (selected) {
181
+ setPersonaSelected(selected);
182
+ }
183
+ }
184
+ };
185
+
186
+ useEffect(() => {
187
+ if (saved) {
188
+ const timeout = setTimeout(() => setSaved(false), 3000);
189
+ return () => clearTimeout(timeout);
190
+ }
191
+ }, [saved]);
192
+
193
+ // CSS to properly style the select items with hover and selection
194
+ const selectItemStyles = `
195
+ .persona-item[data-highlighted] {
196
+ background-color: #0891b2; /* bg-cyan-600 */
197
+ color: white;
198
+ }
199
+ .persona-item[data-highlighted] .persona-indicator {
200
+ color: white;
201
+ }
202
+ .persona-item[data-state="checked"] .persona-indicator {
203
+ display: flex;
204
+ }
205
+ .persona-item .persona-indicator {
206
+ display: none;
207
+ }
208
+ .persona-item[data-state="checked"] {
209
+ font-weight: bold;
210
+ }
211
+ `;
212
+
213
+ return (
214
+ <>
215
+ <style>{selectItemStyles}</style>
216
+ <h3 className="font-action py-6 text-xl text-blue-600">
217
+ Update your profile
218
+ </h3>
219
+
220
+ <form onSubmit={handleSubmit}>
221
+ <div className="grid grid-cols-3 gap-4">
222
+ <div className="col-span-3 px-4 pt-6 md:col-span-1">
223
+ <label htmlFor="firstname" className="block text-sm text-gray-600">
224
+ First name
225
+ </label>
226
+ <input
227
+ type="text"
228
+ name="firstname"
229
+ id="firstname"
230
+ autoComplete="given-name"
231
+ value={firstname}
232
+ onChange={(e) => setFirstname(e.target.value)}
233
+ disabled={isLoading}
234
+ className={classNames(
235
+ `text-md mt-2 block w-full rounded-md bg-white p-3 shadow-sm focus:border-cyan-600 focus:ring-cyan-600`,
236
+ submitted && firstname === ``
237
+ ? `border-red-500`
238
+ : `border-gray-300`
239
+ )}
240
+ />
241
+ {submitted && firstname === `` && (
242
+ <span className="px-4 text-xs text-red-500">Required field.</span>
243
+ )}
244
+ </div>
245
+
246
+ <div className="col-span-3 px-4 pt-6 md:col-span-2">
247
+ <label htmlFor="email" className="block text-sm text-gray-600">
248
+ Email address
249
+ </label>
250
+ <input
251
+ type="email"
252
+ name="email"
253
+ id="email"
254
+ autoComplete="email"
255
+ value={email}
256
+ onChange={(e) => setEmail(e.target.value)}
257
+ disabled={isLoading}
258
+ className={classNames(
259
+ `text-md mt-2 block w-full rounded-md bg-white p-3 shadow-sm focus:border-cyan-600 focus:ring-cyan-600`,
260
+ submitted && email === `` ? `border-red-500` : `border-gray-300`
261
+ )}
262
+ />
263
+ {submitted && email === `` && (
264
+ <span className="px-4 text-xs text-red-500">Required field.</span>
265
+ )}
266
+ </div>
267
+
268
+ <div className="col-span-3 px-4 pt-6">
269
+ <div className="flex items-center text-sm">
270
+ <div className="pr-8 text-sm text-black">
271
+ <Select.Root
272
+ collection={personaCollection}
273
+ value={[personaSelected.id]}
274
+ onValueChange={handlePersonaChange}
275
+ disabled={isLoading}
276
+ >
277
+ <Select.Label className="mb-2 block text-sm text-gray-600">
278
+ Choose your level of consent:
279
+ </Select.Label>
280
+
281
+ <Select.Control className="relative mt-2">
282
+ <Select.Trigger className="text-md relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left leading-6 text-black shadow-sm ring-1 ring-inset ring-cyan-600 focus:outline-none focus:ring-2 focus:ring-cyan-600">
283
+ <Select.ValueText className="block truncate p-2">
284
+ {personaSelected.title}
285
+ </Select.ValueText>
286
+ <Select.Indicator className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
287
+ <ChevronDownIcon
288
+ className="h-5 w-5 text-gray-400"
289
+ aria-hidden="true"
290
+ />
291
+ </Select.Indicator>
292
+ </Select.Trigger>
293
+ </Select.Control>
294
+
295
+ <Portal>
296
+ <Select.Positioner>
297
+ <Select.Content className="relative z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
298
+ {contactPersona.map((item) => (
299
+ <Select.Item
300
+ key={item.id}
301
+ item={item}
302
+ className="persona-item relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900 hover:bg-cyan-600 hover:text-white"
303
+ >
304
+ <Select.ItemText className="block truncate font-normal">
305
+ {item.title}
306
+ </Select.ItemText>
307
+ <Select.ItemIndicator className="persona-indicator absolute inset-y-0 right-0 hidden items-center pr-4 text-cyan-600">
308
+ <ChevronRightIcon
309
+ className="h-5 w-5"
310
+ aria-hidden="true"
311
+ />
312
+ </Select.ItemIndicator>
313
+ </Select.Item>
314
+ ))}
315
+ </Select.Content>
316
+ </Select.Positioner>
317
+ </Portal>
318
+ </Select.Root>
319
+ </div>
320
+ <div className="ml-4 flex items-center">
321
+ <Icon
322
+ className={classNames(iconClass, `mr-2 h-5 w-5`)}
323
+ aria-hidden="true"
324
+ />
325
+ <span className="text-sm text-gray-600">
326
+ {personaSelected.description}
327
+ </span>
328
+ </div>
329
+ </div>
330
+ </div>
331
+
332
+ <div className="col-span-3 px-4 pt-6">
333
+ <label htmlFor="bio" className="block text-sm text-gray-600">
334
+ {firstname ? (
335
+ <>
336
+ Hello {firstname}. Is there anything else you would like to
337
+ share?
338
+ </>
339
+ ) : (
340
+ <>
341
+ Would you like to share anything else? (Contact preferences;
342
+ company bio; phone number)
343
+ </>
344
+ )}
345
+ </label>
346
+ <div className="mt-2">
347
+ <textarea
348
+ id="bio"
349
+ name="bio"
350
+ rows={3}
351
+ maxLength={280}
352
+ className="text-md block w-full rounded-md border-gray-300 bg-white p-3 shadow-sm focus:border-cyan-600 focus:ring-cyan-600"
353
+ placeholder="Your one-liner bio"
354
+ value={bio}
355
+ onChange={(e) => setBio(e.target.value)}
356
+ disabled={isLoading}
357
+ />
358
+ </div>
359
+ </div>
360
+
361
+ <div className="col-span-1 px-4 pt-6">
362
+ <label htmlFor="codeword" className="block text-sm text-gray-600">
363
+ Enter your secret code word to save changes:
364
+ </label>
365
+ <input
366
+ type="password"
367
+ name="codeword"
368
+ id="codeword"
369
+ autoComplete="off"
370
+ value={codeword}
371
+ onChange={(e) => setCodeword(e.target.value)}
372
+ disabled={isLoading}
373
+ className={classNames(
374
+ `text-md mt-2 block w-full rounded-md bg-white p-3 shadow-sm focus:border-cyan-600 focus:ring-cyan-600`,
375
+ submitted && codeword === ``
376
+ ? `border-red-500`
377
+ : `border-gray-300`
378
+ )}
379
+ />
380
+ {submitted && codeword === `` && (
381
+ <span className="px-4 text-xs text-red-500">Required field.</span>
382
+ )}
383
+ </div>
384
+
385
+ {saved ? (
386
+ <div className="align-center font-action col-span-3 flex justify-center py-12 text-green-600">
387
+ Profile updated successfully!
388
+ </div>
389
+ ) : null}
390
+
391
+ <div className="align-center col-span-3 flex justify-center py-12">
392
+ <button
393
+ type="submit"
394
+ disabled={isLoading}
395
+ className={classNames(
396
+ `font-action rounded-lg px-3.5 py-2.5 text-white transition-all duration-200 hover:rotate-1`,
397
+ isLoading
398
+ ? `cursor-not-allowed bg-gray-400`
399
+ : `bg-black hover:bg-orange-500`
400
+ )}
401
+ >
402
+ {isLoading ? 'Saving...' : 'Update Profile'}
403
+ </button>
404
+ </div>
405
+ </div>
406
+ </form>
407
+ </>
408
+ );
409
+ };
@@ -0,0 +1,255 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { ProfileCreate } from './ProfileCreate';
3
+ import { ProfileEdit } from './ProfileEdit';
4
+ import { ProfileUnlock } from './ProfileUnlock';
5
+ import { ProfileStorage } from '@/utils/profileStorage';
6
+
7
+ // Define profile restoration function for v2 thin client
8
+ async function restoreProfileFromToken(): Promise<boolean> {
9
+ try {
10
+ // Check if we have a profile token from the handshake
11
+ const token = localStorage.getItem('tractstack_profile_token');
12
+ if (!token) {
13
+ return false;
14
+ }
15
+
16
+ // Get tenant ID for headers
17
+ const tenantId = (window as any).TRACTSTACK_CONFIG?.tenantId || 'default';
18
+
19
+ // Call the backend to decode the profile token
20
+ const response = await fetch('/api/auth/decode', {
21
+ method: 'GET',
22
+ headers: {
23
+ Authorization: `Bearer ${token}`,
24
+ 'Content-Type': 'application/json',
25
+ 'X-Tenant-ID': tenantId,
26
+ },
27
+ });
28
+
29
+ if (!response.ok) {
30
+ return false;
31
+ }
32
+
33
+ const result = await response.json();
34
+ if (result.profile) {
35
+ // Store the profile data locally
36
+ ProfileStorage.setProfileData({
37
+ firstname: result.profile.firstname,
38
+ contactPersona: result.profile.contactPersona,
39
+ email: result.profile.email,
40
+ shortBio: result.profile.shortBio,
41
+ });
42
+
43
+ // Mark profile as unlocked
44
+ ProfileStorage.storeProfileToken(token);
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ } catch (error) {
50
+ console.error('Failed to restore profile from token:', error);
51
+ return false;
52
+ }
53
+ }
54
+
55
+ // Fast pass auto-unlock using encrypted credentials
56
+ async function tryFastPassUnlock(): Promise<boolean> {
57
+ try {
58
+ const sessionData = ProfileStorage.prepareHandshakeData();
59
+
60
+ if (!sessionData.encryptedEmail || !sessionData.encryptedCode) {
61
+ return false;
62
+ }
63
+
64
+ // Get tenant ID for headers
65
+ const tenantId = (window as any).TRACTSTACK_CONFIG?.tenantId || 'default';
66
+
67
+ const response = await fetch('/api/auth/profile', {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ 'X-Tenant-ID': tenantId,
72
+ },
73
+ body: JSON.stringify({
74
+ encryptedEmail: sessionData.encryptedEmail,
75
+ encryptedCode: sessionData.encryptedCode,
76
+ sessionId: sessionData.sessionId,
77
+ isUpdate: true,
78
+ }),
79
+ });
80
+
81
+ const result = await response.json();
82
+
83
+ if (result.success && result.profile) {
84
+ // Fast pass successful - store profile data
85
+ ProfileStorage.setProfileData({
86
+ firstname: result.profile.Firstname,
87
+ contactPersona: result.profile.ContactPersona,
88
+ email: result.profile.Email,
89
+ shortBio: result.profile.ShortBio,
90
+ });
91
+
92
+ if (result.token) {
93
+ ProfileStorage.storeProfileToken(result.token);
94
+ }
95
+
96
+ return true;
97
+ }
98
+
99
+ return false;
100
+ } catch (error) {
101
+ console.error('Failed to try fast pass unlock:', error);
102
+ return false;
103
+ }
104
+ }
105
+
106
+ export const ProfileSwitch = () => {
107
+ const [mode, setMode] = useState<'unset' | 'create' | 'unlock' | 'edit'>(
108
+ 'unset'
109
+ );
110
+ const [isLoading, setIsLoading] = useState(true);
111
+
112
+ useEffect(() => {
113
+ const initProfileMode = async () => {
114
+ setIsLoading(true);
115
+
116
+ // Check if we've been directed to show the unlock form
117
+ if (ProfileStorage.shouldShowUnlock()) {
118
+ setMode('unlock');
119
+ ProfileStorage.setShowUnlock(false); // Clear the flag after using it
120
+ setIsLoading(false);
121
+ return;
122
+ }
123
+
124
+ const profileData = ProfileStorage.getProfileData();
125
+ const hasProfile = ProfileStorage.hasProfile();
126
+ const isUnlocked = ProfileStorage.isProfileUnlocked();
127
+ const consent = ProfileStorage.getConsent();
128
+ const hasToken = !!localStorage.getItem('tractstack_profile_token');
129
+ const session = ProfileStorage.getCurrentSession();
130
+
131
+ // Try fast pass auto-unlock if we have consent and encrypted credentials but no profile data
132
+ if (
133
+ consent === '1' &&
134
+ session.encryptedEmail &&
135
+ session.encryptedCode &&
136
+ !hasToken
137
+ ) {
138
+ const fastPassSuccess = await tryFastPassUnlock();
139
+ if (fastPassSuccess) {
140
+ setMode('edit');
141
+ setIsLoading(false);
142
+ return;
143
+ }
144
+ // Fast pass failed, continue with normal flow
145
+ }
146
+
147
+ // If we have a token but no local profile data, restore from token
148
+ if (hasToken && !profileData.firstname) {
149
+ const restored = await restoreProfileFromToken();
150
+ if (restored) {
151
+ setMode('edit');
152
+ setIsLoading(false);
153
+ return;
154
+ }
155
+ }
156
+
157
+ // If we have encrypted credentials but no profile token, show unlock
158
+ if (
159
+ (session.encryptedCode && session.encryptedEmail && !hasToken) ||
160
+ (hasProfile && !isUnlocked)
161
+ ) {
162
+ setMode('unlock');
163
+ }
164
+ // If we have consent but no profile, show create
165
+ else if (consent === '1' && !hasProfile && !hasToken) {
166
+ setMode('create');
167
+ }
168
+ // If we have profile data and are unlocked, show edit
169
+ else if ((isUnlocked || hasToken) && profileData.firstname) {
170
+ setMode('edit');
171
+ }
172
+ // Default case - when everything is cleared
173
+ else {
174
+ setMode('unset');
175
+ }
176
+
177
+ setIsLoading(false);
178
+ };
179
+
180
+ initProfileMode();
181
+ }, []);
182
+
183
+ // Add effect to listen for profile navigation events
184
+ useEffect(() => {
185
+ const handleShowUnlock = () => {
186
+ setMode('unlock');
187
+ ProfileStorage.setShowUnlock(false);
188
+ };
189
+
190
+ const handleShowCreate = () => {
191
+ setMode('create');
192
+ };
193
+
194
+ // Listen for custom events
195
+ window.addEventListener('tractstack:show-unlock', handleShowUnlock);
196
+ window.addEventListener('tractstack:show-create', handleShowCreate);
197
+
198
+ return () => {
199
+ window.removeEventListener('tractstack:show-unlock', handleShowUnlock);
200
+ window.removeEventListener('tractstack:show-create', handleShowCreate);
201
+ };
202
+ }, []);
203
+
204
+ const handleProfileSuccess = () => {
205
+ // Refresh the mode after successful operation
206
+ const profileData = ProfileStorage.getProfileData();
207
+ if (profileData.firstname) {
208
+ setMode('edit');
209
+ }
210
+ };
211
+
212
+ // Show loading state
213
+ if (isLoading) {
214
+ return (
215
+ <div className="py-12">
216
+ <div className="border-myblue/20 bg-mywhite border border-dashed">
217
+ <div className="p-6">
218
+ <div className="flex items-center justify-center py-12">
219
+ <div className="text-mydarkgrey">Loading profile...</div>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ );
225
+ }
226
+
227
+ // Don't render anything if mode is unset
228
+ if (mode === 'unset') {
229
+ return <div />;
230
+ }
231
+
232
+ // Don't render edit mode if we don't have profile data
233
+ if (mode === 'edit' && !ProfileStorage.getProfileData().firstname) {
234
+ return <div />;
235
+ }
236
+
237
+ return (
238
+ <div className="py-12">
239
+ <div className="border-myblue/20 bg-mywhite border border-dashed">
240
+ <div className="p-6">
241
+ {mode === 'create' && (
242
+ <ProfileCreate onSuccess={handleProfileSuccess} />
243
+ )}
244
+ {mode === 'unlock' && (
245
+ <ProfileUnlock
246
+ initialEmail={ProfileStorage.getLastEmail() || undefined}
247
+ onSuccess={handleProfileSuccess}
248
+ />
249
+ )}
250
+ {mode === 'edit' && <ProfileEdit onSuccess={handleProfileSuccess} />}
251
+ </div>
252
+ </div>
253
+ </div>
254
+ );
255
+ };