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,191 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { classNames } from '@/utils/helpers';
3
+ import { TractStackAPI } from '@/utils/api';
4
+ import {
5
+ handleContentSubtabChange,
6
+ restoreTabNavigation,
7
+ } from '@/stores/navigation';
8
+ import ContentBrowser from './controls/content/ContentBrowser';
9
+ import ManageContent from './controls/content/ManageContent';
10
+ import type { FullContentMapItem } from '@/types/tractstack';
11
+
12
+ interface StoryKeepDashboardContentProps {
13
+ fullContentMap: FullContentMapItem[];
14
+ homeSlug: string;
15
+ createMenu: boolean;
16
+ }
17
+
18
+ interface ContentTab {
19
+ id: string;
20
+ name: string;
21
+ }
22
+
23
+ const contentTabs: ContentTab[] = [
24
+ { id: 'webpages', name: 'Web Pages' },
25
+ { id: 'manage', name: 'Manage Content' },
26
+ ];
27
+
28
+ const StoryKeepDashboard_Content = ({
29
+ fullContentMap,
30
+ homeSlug,
31
+ createMenu,
32
+ }: StoryKeepDashboardContentProps) => {
33
+ const [activeContentTab, setActiveContentTab] = useState('webpages');
34
+ const [navigationRestored, setNavigationRestored] = useState(false);
35
+
36
+ // Lightweight analytics data state - only for hotContent
37
+ const [analytics, setAnalytics] = useState<{
38
+ dashboard: {
39
+ hotContent?: Array<{ id: string; totalEvents: number }>;
40
+ } | null;
41
+ isLoading: boolean;
42
+ status: string;
43
+ error: string | null;
44
+ }>({
45
+ dashboard: null,
46
+ isLoading: false,
47
+ status: 'idle',
48
+ error: null,
49
+ });
50
+
51
+ // Restore navigation state when component mounts or when returning to Content tab
52
+ useEffect(() => {
53
+ if (!navigationRestored) {
54
+ const contentNavigation = restoreTabNavigation();
55
+ if (contentNavigation) {
56
+ setActiveContentTab(contentNavigation.subtab);
57
+ }
58
+ setNavigationRestored(true);
59
+ }
60
+ }, [navigationRestored]);
61
+
62
+ // Enhanced content tab change with navigation tracking
63
+ const handleContentTabChange = (tabId: string) => {
64
+ handleContentSubtabChange(tabId as any, setActiveContentTab);
65
+ };
66
+
67
+ // Lightweight content summary fetch with retry logic
68
+ useEffect(() => {
69
+ let retryCount = 0;
70
+ const maxRetries = 2;
71
+
72
+ const fetchContentSummary = async () => {
73
+ try {
74
+ setAnalytics((prev) => ({ ...prev, isLoading: true, error: null }));
75
+
76
+ // Use TractStackAPI like FetchAnalytics does
77
+ const api = new TractStackAPI(
78
+ window.TRACTSTACK_CONFIG?.tenantId || 'default'
79
+ );
80
+
81
+ const response = await api.get('/api/v1/analytics/content-summary');
82
+
83
+ if (!response.success) {
84
+ throw new Error(response.error || 'Failed to fetch content summary');
85
+ }
86
+
87
+ const data = response.data;
88
+
89
+ // Check if we got actual data
90
+ const hasData = data.hotContent && data.hotContent.length > 0;
91
+
92
+ setAnalytics({
93
+ dashboard: { hotContent: data.hotContent || [] },
94
+ isLoading: false,
95
+ status: hasData ? 'complete' : 'empty',
96
+ error: null,
97
+ });
98
+
99
+ // If no data and we have retries left, try again after delay
100
+ if (!hasData && retryCount < maxRetries) {
101
+ retryCount++;
102
+ const delayMs = retryCount === 1 ? 3000 : 6000; // 3s, then 6s
103
+ setTimeout(fetchContentSummary, delayMs);
104
+ }
105
+ } catch (error) {
106
+ console.error('Content summary fetch error:', error);
107
+
108
+ // If we have retries left, try again
109
+ if (retryCount < maxRetries) {
110
+ retryCount++;
111
+ const delayMs = retryCount === 1 ? 3000 : 6000;
112
+ setAnalytics((prev) => ({
113
+ ...prev,
114
+ isLoading: false,
115
+ status: 'retrying',
116
+ error: `Attempt ${retryCount} failed, retrying in ${delayMs / 1000}s...`,
117
+ }));
118
+ setTimeout(fetchContentSummary, delayMs);
119
+ } else {
120
+ // Max retries reached
121
+ setAnalytics({
122
+ dashboard: { hotContent: [] },
123
+ isLoading: false,
124
+ status: 'error',
125
+ error:
126
+ error instanceof Error
127
+ ? error.message
128
+ : 'Failed to load content analytics',
129
+ });
130
+ }
131
+ }
132
+ };
133
+
134
+ fetchContentSummary();
135
+ }, []);
136
+
137
+ const renderContentTabContent = () => {
138
+ switch (activeContentTab) {
139
+ case 'webpages':
140
+ return (
141
+ <ContentBrowser
142
+ analytics={analytics}
143
+ fullContentMap={fullContentMap}
144
+ homeSlug={homeSlug}
145
+ />
146
+ );
147
+ case 'manage':
148
+ return (
149
+ <ManageContent
150
+ fullContentMap={fullContentMap}
151
+ homeSlug={homeSlug}
152
+ createMenu={createMenu}
153
+ />
154
+ );
155
+ default:
156
+ return null;
157
+ }
158
+ };
159
+
160
+ return (
161
+ <div className="w-full">
162
+ {/* Content Sub-Navigation */}
163
+ <div className="mb-6">
164
+ <div className="border-b border-gray-200">
165
+ <nav className="-mb-px flex gap-x-6" aria-label="Content tabs">
166
+ {contentTabs.map((tab) => (
167
+ <button
168
+ key={tab.id}
169
+ onClick={() => handleContentTabChange(tab.id)}
170
+ className={classNames(
171
+ activeContentTab === tab.id
172
+ ? 'border-cyan-500 text-cyan-600'
173
+ : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700',
174
+ 'whitespace-nowrap border-b-2 px-1 py-3 text-sm font-bold'
175
+ )}
176
+ aria-current={activeContentTab === tab.id ? 'page' : undefined}
177
+ >
178
+ {tab.name}
179
+ </button>
180
+ ))}
181
+ </nav>
182
+ </div>
183
+ </div>
184
+
185
+ {/* Content Tab Content */}
186
+ {renderContentTabContent()}
187
+ </div>
188
+ );
189
+ };
190
+
191
+ export default StoryKeepDashboard_Content;
@@ -0,0 +1,71 @@
1
+ import { useStore } from '@nanostores/react';
2
+ import { orphanAnalysisStore } from '@/stores/orphanAnalysis';
3
+ import type { FullContentMapItem } from '@/types/tractstack';
4
+
5
+ interface UsageCellProps {
6
+ itemId: string;
7
+ fullContentMap: FullContentMapItem[];
8
+ usageType: 'storyFragments' | 'menus' | 'beliefs';
9
+ }
10
+
11
+ const UsageCell = ({ itemId, fullContentMap, usageType }: UsageCellProps) => {
12
+ const orphanState = useStore(orphanAnalysisStore);
13
+
14
+ // Force loading until we have actual data - no exceptions
15
+ if (
16
+ orphanState?.data?.status === `loading` ||
17
+ !orphanState ||
18
+ !orphanState.data ||
19
+ !orphanState.data[usageType] ||
20
+ orphanState.isLoading
21
+ ) {
22
+ return (
23
+ <div className="flex items-center gap-2">
24
+ <div
25
+ className="h-2 w-2 animate-pulse rounded-full bg-gray-400"
26
+ style={{
27
+ animationDuration: window.matchMedia(
28
+ '(prefers-reduced-motion: reduce)'
29
+ ).matches
30
+ ? '2s'
31
+ : '1s',
32
+ }}
33
+ />
34
+ </div>
35
+ );
36
+ }
37
+
38
+ const usage = orphanState.data[usageType][itemId] || [];
39
+
40
+ if (usage.length === 0) {
41
+ return (
42
+ <span className="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-bold text-gray-800">
43
+ Unused
44
+ </span>
45
+ );
46
+ }
47
+
48
+ // Get titles for dependencies
49
+ const usageDetails = usage.map((id) => {
50
+ const dependentItem = fullContentMap.find((content) => content.id === id);
51
+ return dependentItem
52
+ ? `${dependentItem.title} (${dependentItem.type})`
53
+ : `Unknown (${id})`;
54
+ });
55
+
56
+ return (
57
+ <div className="group relative">
58
+ <span className="inline-flex cursor-help items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-bold text-green-800">
59
+ {usage.length} dependent{usage.length !== 1 ? 's' : ''}
60
+ </span>
61
+ {/* Tooltip */}
62
+ <div className="invisible absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2 transform whitespace-nowrap rounded-lg bg-gray-900 px-3 py-2 text-sm text-white shadow-lg group-hover:visible">
63
+ Dependencies: {usageDetails.slice(0, 3).join(', ')}
64
+ {usageDetails.length > 3 && ` +${usageDetails.length - 3} more`}
65
+ <div className="absolute left-1/2 top-full -translate-x-1/2 transform border-4 border-transparent border-t-gray-900"></div>
66
+ </div>
67
+ </div>
68
+ );
69
+ };
70
+
71
+ export default UsageCell;
@@ -0,0 +1,378 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useStore } from '@nanostores/react';
3
+ import { useFormState } from '@/hooks/useFormState';
4
+ import {
5
+ convertToLocalState,
6
+ validateBeliefNode,
7
+ beliefStateIntercept,
8
+ addCustomValue,
9
+ removeCustomValue,
10
+ SCALE_OPTIONS,
11
+ getScalePreview,
12
+ } from '@/utils/api/beliefHelpers';
13
+ import { saveBeliefWithStateUpdate } from '@/utils/api/beliefConfig';
14
+ import {
15
+ orphanAnalysisStore,
16
+ loadOrphanAnalysis,
17
+ } from '@/stores/orphanAnalysis';
18
+ import StringInput from '@/components/form/StringInput';
19
+ import EnumSelect from '@/components/form/EnumSelect';
20
+ import UnsavedChangesBar from '@/components/form/UnsavedChangesBar';
21
+ import {
22
+ PlusIcon,
23
+ XMarkIcon,
24
+ LockClosedIcon,
25
+ } from '@heroicons/react/24/outline';
26
+ import type { BeliefNode, BeliefNodeState } from '@/types/tractstack';
27
+
28
+ interface BeliefFormProps {
29
+ belief?: BeliefNode;
30
+ isCreate?: boolean;
31
+ onClose?: (saved: boolean) => void;
32
+ }
33
+
34
+ export default function BeliefForm({
35
+ belief,
36
+ isCreate = false,
37
+ onClose,
38
+ }: BeliefFormProps) {
39
+ const [customValue, setCustomValue] = useState('');
40
+
41
+ // Subscribe to orphan analysis store
42
+ const orphanState = useStore(orphanAnalysisStore);
43
+
44
+ // Load orphan analysis on component mount
45
+ useEffect(() => {
46
+ loadOrphanAnalysis();
47
+ }, []);
48
+
49
+ // Get usage information for this belief
50
+ const getBeliefUsage = (): string[] => {
51
+ if (!belief?.id || !orphanState.data || !orphanState.data.beliefs) {
52
+ return [];
53
+ }
54
+ return orphanState.data.beliefs[belief.id] || [];
55
+ };
56
+
57
+ // Check if belief is in use
58
+ const isBeliefInUse = (): boolean => {
59
+ if (isCreate || !belief?.id) return false;
60
+ return getBeliefUsage().length > 0;
61
+ };
62
+
63
+ const beliefInUse = isBeliefInUse();
64
+ const usageCount = getBeliefUsage().length;
65
+
66
+ // Initialize form state
67
+ const initialState: BeliefNodeState = belief
68
+ ? convertToLocalState(belief)
69
+ : {
70
+ id: '',
71
+ title: '',
72
+ slug: '',
73
+ scale: '',
74
+ customValues: [],
75
+ };
76
+
77
+ const formState = useFormState({
78
+ initialData: initialState,
79
+ interceptor: beliefStateIntercept,
80
+ validator: validateBeliefNode,
81
+ onSave: async (data) => {
82
+ try {
83
+ const updatedState = await saveBeliefWithStateUpdate(
84
+ window.TRACTSTACK_CONFIG?.tenantId || 'default',
85
+ data
86
+ );
87
+
88
+ // Call success callback after save (original pattern)
89
+ setTimeout(() => {
90
+ onClose?.(true);
91
+ }, 1000);
92
+
93
+ return updatedState;
94
+ } catch (error) {
95
+ console.error('Belief save failed:', error);
96
+ throw error;
97
+ }
98
+ },
99
+ unsavedChanges: {
100
+ enableBrowserWarning: true,
101
+ browserWarningMessage: 'Your belief changes will be lost!',
102
+ },
103
+ });
104
+
105
+ const handleAddCustomValue = () => {
106
+ if (!customValue.trim()) return;
107
+
108
+ const newState = addCustomValue(formState.state, customValue);
109
+ formState.updateField('customValues', newState.customValues);
110
+ setCustomValue('');
111
+ };
112
+
113
+ const handleRemoveCustomValue = (index: number) => {
114
+ // Check if this is a newly added value (not saved yet)
115
+ const currentValue = formState.state.customValues[index];
116
+ const originalValues = formState.originalState.customValues || [];
117
+ const isNewValue = !originalValues.includes(currentValue);
118
+
119
+ // Allow removal if:
120
+ // 1. Belief is not in use, OR
121
+ // 2. This is a new value that hasn't been saved yet
122
+ if (!beliefInUse || isNewValue) {
123
+ const newState = removeCustomValue(formState.state, index);
124
+ formState.updateField('customValues', newState.customValues);
125
+ }
126
+ };
127
+
128
+ const handleKeyDown = (e: React.KeyboardEvent) => {
129
+ if (e.key === 'Enter') {
130
+ e.preventDefault();
131
+ handleAddCustomValue();
132
+ }
133
+ };
134
+
135
+ const handleCancel = () => {
136
+ onClose?.(false);
137
+ };
138
+
139
+ const renderScalePreview = () => {
140
+ if (!formState.state.scale || formState.state.scale === 'custom')
141
+ return null;
142
+
143
+ const preview = getScalePreview(formState.state.scale);
144
+ if (!preview) return null;
145
+
146
+ return (
147
+ <div className="mt-4 rounded-md border border-gray-200 bg-gray-50 p-4">
148
+ <h4 className="mb-2 text-sm font-bold text-gray-700">Scale Preview:</h4>
149
+ <div className="flex flex-wrap gap-2">
150
+ {preview.map((option) => (
151
+ <div
152
+ key={option.id}
153
+ className={`rounded-full px-3 py-1 text-sm font-bold text-gray-800 ${option.color}`}
154
+ >
155
+ {option.name}
156
+ </div>
157
+ ))}
158
+ </div>
159
+ </div>
160
+ );
161
+ };
162
+
163
+ const renderUsageWarning = () => {
164
+ if (!beliefInUse) return null;
165
+
166
+ return (
167
+ <div className="rounded-md border border-amber-200 bg-amber-50 p-4">
168
+ <div className="flex">
169
+ <div className="flex-shrink-0">
170
+ <LockClosedIcon className="h-5 w-5 text-amber-400" />
171
+ </div>
172
+ <div className="ml-3">
173
+ <h3 className="text-sm font-bold text-amber-800">
174
+ Belief In Use - Limited Editing
175
+ </h3>
176
+ <div className="mt-2 text-sm text-amber-700">
177
+ <p>
178
+ This belief is currently used by <strong>{usageCount}</strong>{' '}
179
+ item{usageCount !== 1 ? 's' : ''}. Some fields are locked to
180
+ prevent breaking existing content.
181
+ </p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+ );
187
+ };
188
+
189
+ return (
190
+ <div className="space-y-8">
191
+ {/* Header */}
192
+ <div className="border-b border-gray-200 pb-4">
193
+ <h2 className="text-2xl font-bold text-gray-900">
194
+ {isCreate ? 'Create Belief' : 'Edit Belief'}
195
+ </h2>
196
+ <p className="mt-2 text-sm text-gray-600">
197
+ {isCreate
198
+ ? 'Create a new belief to power adaptive content and magic paths.'
199
+ : 'Edit the belief configuration and scale options.'}
200
+ </p>
201
+ </div>
202
+
203
+ {/* Usage Warning */}
204
+ {renderUsageWarning()}
205
+
206
+ {/* Info Box */}
207
+ <div className="rounded-md bg-blue-50 p-4">
208
+ <div className="text-sm text-blue-700">
209
+ <p className="font-bold">What are Beliefs?</p>
210
+ <p className="mt-1">
211
+ Beliefs power "magic paths" and adaptive content. They track visitor
212
+ preferences and enable personalized experiences based on user
213
+ interactions.
214
+ </p>
215
+ <ul className="mt-2 list-inside list-disc space-y-1">
216
+ <li>
217
+ Use <strong>Custom Values</strong> for "Identify As" widgets (one
218
+ value per persona)
219
+ </li>
220
+ <li>
221
+ Use <strong>Yes/No</strong> scale for "Toggle Belief" widgets
222
+ </li>
223
+ <li>Link pane visibility to belief states for adaptive content</li>
224
+ </ul>
225
+ </div>
226
+ </div>
227
+
228
+ {/* Basic Fields */}
229
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
230
+ <StringInput
231
+ value={formState.state.title}
232
+ onChange={(value) => formState.updateField('title', value)}
233
+ label="Title"
234
+ placeholder="Enter belief title"
235
+ error={formState.errors.title}
236
+ required
237
+ />
238
+
239
+ <div className="relative">
240
+ <StringInput
241
+ value={formState.state.slug}
242
+ onChange={(value) => formState.updateField('slug', value)}
243
+ label="Slug"
244
+ placeholder="Enter belief slug"
245
+ error={formState.errors.slug}
246
+ disabled={beliefInUse}
247
+ required
248
+ />
249
+ {beliefInUse && (
250
+ <div className="absolute right-2 top-8 flex items-center">
251
+ <LockClosedIcon className="h-4 w-4 text-gray-400" />
252
+ </div>
253
+ )}
254
+ </div>
255
+ </div>
256
+
257
+ {/* Scale Selection */}
258
+ <div className="space-y-4">
259
+ <div className="relative">
260
+ <EnumSelect
261
+ value={formState.state.scale}
262
+ onChange={(value) => formState.updateField('scale', value)}
263
+ label="Scale Type"
264
+ options={SCALE_OPTIONS}
265
+ error={formState.errors.scale}
266
+ disabled={beliefInUse}
267
+ required
268
+ />
269
+ {beliefInUse && (
270
+ <div className="absolute right-8 top-8 flex items-center">
271
+ <LockClosedIcon className="h-4 w-4 text-gray-400" />
272
+ </div>
273
+ )}
274
+ </div>
275
+
276
+ {renderScalePreview()}
277
+ </div>
278
+
279
+ {/* Custom Values Section */}
280
+ {formState.state.scale === 'custom' && (
281
+ <div className="space-y-4">
282
+ <div>
283
+ <h3 className="text-lg font-bold text-gray-900">Custom Values</h3>
284
+ <p className="text-sm text-gray-600">
285
+ Define custom options for this belief scale.
286
+ </p>
287
+ </div>
288
+
289
+ {/* Add Custom Value */}
290
+ <div className="flex gap-2">
291
+ <div className="flex-1">
292
+ <div className="flex-1">
293
+ <input
294
+ type="text"
295
+ value={customValue}
296
+ onChange={(e) => setCustomValue(e.target.value)}
297
+ onKeyDown={handleKeyDown}
298
+ placeholder="Enter custom value"
299
+ className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-cyan-600 sm:text-sm sm:leading-6"
300
+ />
301
+ </div>
302
+ </div>
303
+ <button
304
+ type="button"
305
+ onClick={handleAddCustomValue}
306
+ disabled={!customValue.trim()}
307
+ className="inline-flex items-center rounded-md bg-cyan-600 px-3 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cyan-600 disabled:cursor-not-allowed disabled:opacity-50"
308
+ >
309
+ <PlusIcon className="h-4 w-4" />
310
+ </button>
311
+ </div>
312
+
313
+ {/* Custom Values List */}
314
+ {formState.state.customValues.length > 0 && (
315
+ <div className="space-y-2">
316
+ {formState.state.customValues.map((value, index) => {
317
+ const originalValues =
318
+ formState.originalState.customValues || [];
319
+ const isNewValue = !originalValues.includes(value);
320
+ const canRemove = !beliefInUse || isNewValue;
321
+
322
+ return (
323
+ <div
324
+ key={index}
325
+ className="flex items-center justify-between rounded-md border border-gray-200 bg-gray-50 px-3 py-2"
326
+ >
327
+ <span className="text-sm text-gray-900">{value}</span>
328
+ <div className="flex items-center gap-2">
329
+ {beliefInUse && !isNewValue && (
330
+ <LockClosedIcon className="h-4 w-4 text-gray-400" />
331
+ )}
332
+ <button
333
+ type="button"
334
+ onClick={() => handleRemoveCustomValue(index)}
335
+ disabled={!canRemove}
336
+ className="text-red-600 hover:text-red-800 disabled:cursor-not-allowed disabled:text-gray-400"
337
+ >
338
+ <XMarkIcon className="h-4 w-4" />
339
+ </button>
340
+ </div>
341
+ </div>
342
+ );
343
+ })}
344
+ </div>
345
+ )}
346
+
347
+ {beliefInUse && (
348
+ <div className="text-sm text-amber-700">
349
+ <p>
350
+ ⚠️ You can add new values, but cannot remove existing ones while
351
+ this belief is in use.
352
+ </p>
353
+ </div>
354
+ )}
355
+ </div>
356
+ )}
357
+
358
+ {/* Save/Cancel Bar */}
359
+ <UnsavedChangesBar
360
+ formState={formState}
361
+ message="You have unsaved belief changes"
362
+ saveLabel="Save Belief"
363
+ cancelLabel="Discard Changes"
364
+ />
365
+
366
+ {/* Cancel Navigation Button */}
367
+ <div className="flex justify-start">
368
+ <button
369
+ type="button"
370
+ onClick={handleCancel}
371
+ className="text-sm font-bold text-gray-600 hover:text-gray-800"
372
+ >
373
+ ← Back to Belief List
374
+ </button>
375
+ </div>
376
+ </div>
377
+ );
378
+ }