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,37 @@
1
+ export function smoothScrollToPane(
2
+ thisPane: HTMLElement,
3
+ offset: number = 20,
4
+ updateInterval: number = 50
5
+ ): NodeJS.Timeout | undefined {
6
+ const timeout = setTimeout(() => {
7
+ setTimeout(() => {
8
+ const viewportHeight = window.innerHeight;
9
+ const viewportTop = window.scrollY;
10
+ const viewportBottom = viewportTop + viewportHeight;
11
+ const paneTop = thisPane.offsetTop;
12
+ const PROXIMITY_THRESHOLD = viewportHeight;
13
+
14
+ if (Math.abs(paneTop - viewportBottom) < PROXIMITY_THRESHOLD) {
15
+ thisPane.classList.add('motion-safe:animate-fadeInUp');
16
+
17
+ window.scrollTo({
18
+ top: paneTop - offset,
19
+ behavior: 'smooth',
20
+ });
21
+ }
22
+ }, 0);
23
+ }, updateInterval);
24
+
25
+ return timeout;
26
+ }
27
+
28
+ export function dispatchEvent(event: CustomEvent) {
29
+ document.dispatchEvent(event);
30
+ }
31
+
32
+ export function dispatchUpdateVideoEvent(startTime: string, videoId?: string) {
33
+ const event = new CustomEvent('updateVideo', {
34
+ detail: { startTime, videoId },
35
+ });
36
+ dispatchEvent(event);
37
+ }
@@ -0,0 +1,131 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { PaneFragmentNode, FlatNode } from '@/types/compositorTypes';
3
+ import { settingsPanelStore, styleElementInfoStore } from '@/stores/storykeep';
4
+
5
+ function isVisualBreakNode(node: FlatNode): boolean {
6
+ return (
7
+ node.nodeType === 'BgPane' &&
8
+ 'type' in node &&
9
+ (node as PaneFragmentNode).type === 'visual-break'
10
+ );
11
+ }
12
+
13
+ function isCodeHookPane(node: FlatNode): boolean {
14
+ return node.nodeType === 'Pane' && 'codeHookTarget' in node;
15
+ }
16
+
17
+ export function handleClickEventDefault(
18
+ node: FlatNode,
19
+ expanded: boolean,
20
+ parentLayer?: number | null,
21
+ minimized?: boolean
22
+ ) {
23
+ if (!node?.nodeType) return;
24
+
25
+ const panelProps: any = {
26
+ nodeId: node.id,
27
+ };
28
+
29
+ if (minimized === true) {
30
+ panelProps.minimized = true;
31
+ } else if (expanded) {
32
+ panelProps.expanded = true;
33
+ }
34
+
35
+ if (parentLayer !== undefined && parentLayer !== null) {
36
+ panelProps.layer = parentLayer;
37
+ }
38
+
39
+ switch (node.nodeType) {
40
+ case 'BgPane': {
41
+ if (isVisualBreakNode(node)) {
42
+ settingsPanelStore.set({
43
+ action: 'style-break',
44
+ ...panelProps,
45
+ });
46
+ } else {
47
+ console.log('unhandled BgPane type');
48
+ settingsPanelStore.set(null);
49
+ }
50
+ break;
51
+ }
52
+
53
+ case 'Pane':
54
+ if (isCodeHookPane(node))
55
+ settingsPanelStore.set({
56
+ action: 'setup-codehook',
57
+ ...panelProps,
58
+ });
59
+ else {
60
+ settingsPanelStore.set({
61
+ action: 'style-parent',
62
+ ...panelProps,
63
+ });
64
+ }
65
+ break;
66
+
67
+ case 'Markdown':
68
+ settingsPanelStore.set({
69
+ action: 'style-parent',
70
+ ...panelProps,
71
+ });
72
+ break;
73
+
74
+ case 'TagElement': {
75
+ if (!('tagName' in node)) return;
76
+
77
+ switch (node.tagName) {
78
+ case 'code':
79
+ settingsPanelStore.set({
80
+ action: 'style-widget',
81
+ ...panelProps,
82
+ });
83
+ break;
84
+ case 'p':
85
+ case 'h2':
86
+ case 'h3':
87
+ case 'h4':
88
+ case 'h5':
89
+ case 'ol':
90
+ styleElementInfoStore.set({
91
+ markdownParentId: node.parentId,
92
+ tagName: node.tagName,
93
+ overrideNodeId: null,
94
+ className: null,
95
+ });
96
+ settingsPanelStore.set({
97
+ action: 'style-element',
98
+ ...panelProps,
99
+ });
100
+ break;
101
+ case 'img':
102
+ settingsPanelStore.set({
103
+ action: 'style-image',
104
+ ...panelProps,
105
+ });
106
+ break;
107
+ case 'li':
108
+ settingsPanelStore.set({
109
+ action: 'style-li-element',
110
+ ...panelProps,
111
+ });
112
+ break;
113
+ case 'a':
114
+ case 'button':
115
+ settingsPanelStore.set({
116
+ action: 'style-link',
117
+ ...panelProps,
118
+ });
119
+ break;
120
+ default:
121
+ console.log(`also missed on: ${node.tagName}`);
122
+ settingsPanelStore.set(null);
123
+ }
124
+ break;
125
+ }
126
+
127
+ default:
128
+ console.log(`missed on: ${node.nodeType}`, node);
129
+ settingsPanelStore.set(null);
130
+ }
131
+ }
@@ -0,0 +1,491 @@
1
+ import { ulid } from 'ulid';
2
+ import {
3
+ TemplateBeliefNode,
4
+ TemplateBunnyNode,
5
+ TemplateEmailSignUpNode,
6
+ TemplateH2Node,
7
+ TemplateH3Node,
8
+ TemplateH4Node,
9
+ TemplateIdentifyAsNode,
10
+ TemplateImgNode,
11
+ TemplatePNode,
12
+ TemplateToggleNode,
13
+ TemplateYoutubeNode,
14
+ } from './TemplateNodes';
15
+ import { getCtx, NodesContext } from '@/stores/nodes';
16
+ import { cloneDeep } from '@/utils/helpers';
17
+ import type {
18
+ BaseNode,
19
+ FlatNode,
20
+ StoryFragmentNode,
21
+ TemplateNode,
22
+ ToolAddMode,
23
+ } from '@/types/compositorTypes';
24
+ import type { NodeTagProps } from '@/types/nodeProps';
25
+
26
+ export const getTemplateNode = (value: ToolAddMode): TemplateNode => {
27
+ let templateNode: TemplateNode;
28
+ switch (value) {
29
+ case 'h2':
30
+ templateNode = cloneDeep(TemplateH2Node);
31
+ break;
32
+ case 'h3':
33
+ templateNode = cloneDeep(TemplateH3Node);
34
+ break;
35
+ case 'h4':
36
+ templateNode = cloneDeep(TemplateH4Node);
37
+ break;
38
+ case 'img':
39
+ templateNode = cloneDeep(TemplateImgNode);
40
+ break;
41
+ case 'toggle':
42
+ templateNode = cloneDeep(TemplateToggleNode);
43
+ break;
44
+ case 'yt':
45
+ templateNode = cloneDeep(TemplateYoutubeNode);
46
+ break;
47
+ case 'belief':
48
+ templateNode = cloneDeep(TemplateBeliefNode);
49
+ break;
50
+ case 'bunny':
51
+ templateNode = cloneDeep(TemplateBunnyNode);
52
+ break;
53
+ case 'signup':
54
+ templateNode = cloneDeep(TemplateEmailSignUpNode);
55
+ break;
56
+ case 'identify':
57
+ templateNode = cloneDeep(TemplateIdentifyAsNode);
58
+ break;
59
+ case 'p':
60
+ default:
61
+ templateNode = cloneDeep(TemplatePNode);
62
+ break;
63
+ }
64
+ return templateNode;
65
+ };
66
+
67
+ const forbiddenEditTags = new Set<string>(['em', 'strong', 'ol', 'ul']);
68
+
69
+ export const canEditText = (props: NodeTagProps): boolean => {
70
+ const nodeId = props.nodeId;
71
+
72
+ const self = getCtx(props).allNodes.get().get(nodeId) as FlatNode;
73
+ if (self.tagName === 'a') return false;
74
+
75
+ const parentIsButton = getCtx(props).getParentNodeByTagNames(nodeId, ['a']);
76
+ if (parentIsButton?.length > 0) return false;
77
+
78
+ if (forbiddenEditTags.has(props.tagName)) return false;
79
+ return true;
80
+ };
81
+
82
+ export function parseMarkdownToNodes(
83
+ html: string,
84
+ parentId: string
85
+ ): FlatNode[] {
86
+ // Generate a base timestamp for unique IDs
87
+ let uniqueCounter = Date.now();
88
+
89
+ // Clean input text - handle special characters and markdown-like syntax
90
+ html = html
91
+ .replace(/&nbsp;|\u00A0/g, ' ')
92
+ .replace(/&amp;/g, '&')
93
+ .replace(/<br>/g, '');
94
+
95
+ // Handle wiki-style [[link]] - add a unique placeholder href
96
+ html = html.replace(/\[\[([^\]]+?)\]\]/g, (_, content) => {
97
+ return `<a href="#placeholder-${uniqueCounter++}">${content}</a>`;
98
+ });
99
+
100
+ // Handle markdown-style [text](url) links
101
+ html = html.replace(/\[([^\]]+?)\]\(([^)]+?)\)/g, (_, text, url) => {
102
+ return `<a href="${url}">${text}</a>`;
103
+ });
104
+
105
+ // Handle other markdown formatting - avoid processing inside existing tags
106
+ html = html
107
+ .replace(
108
+ /(?<!<a[^>]*?>[^<]*)\*\*(.+?)\*\*(?![^<]*?<\/a>)/g,
109
+ '<strong>$1</strong>'
110
+ )
111
+ .replace(/(?<!<a[^>]*?>[^<]*)\*(.+?)\*(?![^<]*?<\/a>)/g, '<em>$1</em>');
112
+
113
+ // Use browser's DOM parser instead of character-by-character parsing
114
+ const parser = new DOMParser();
115
+ const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html');
116
+
117
+ // Process the DOM tree
118
+ return extractNodesFromDOM(
119
+ doc.body.firstElementChild as HTMLElement,
120
+ parentId
121
+ );
122
+ }
123
+
124
+ function extractNodesFromDOM(
125
+ element: HTMLElement,
126
+ parentId: string
127
+ ): FlatNode[] {
128
+ const result: FlatNode[] = [];
129
+
130
+ // Process each child node
131
+ Array.from(element.childNodes).forEach((child) => {
132
+ if (child.nodeType === Node.TEXT_NODE) {
133
+ // Handle text nodes - preserve text content but strip zero-width spaces
134
+ let text = child.textContent;
135
+
136
+ // Only skip if null or undefined, but keep empty strings and whitespace
137
+ if (text !== null && text !== undefined) {
138
+ // Remove zero-width spaces from beginning and end
139
+ text = text.replace(/^\u200B+|\u200B+$/g, '');
140
+
141
+ result.push({
142
+ id: ulid(),
143
+ parentId,
144
+ nodeType: 'TagElement',
145
+ tagName: 'text',
146
+ copy: text,
147
+ } as FlatNode);
148
+ }
149
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
150
+ const elem = child as HTMLElement;
151
+ const tagName = elem.tagName.toLowerCase();
152
+
153
+ // Skip any remaining space marker spans
154
+ if (
155
+ tagName === 'span' &&
156
+ (elem.classList.contains('space-marker') ||
157
+ elem.getAttribute('style')?.includes('font-size: 0px'))
158
+ ) {
159
+ return;
160
+ }
161
+
162
+ // Create node for this element
163
+ const nodeId = ulid();
164
+ const node: FlatNode = {
165
+ id: nodeId,
166
+ parentId,
167
+ nodeType: 'TagElement',
168
+ tagName,
169
+ };
170
+
171
+ // Handle special attributes for different tags
172
+ if (tagName === 'a') {
173
+ // Process anchor tags
174
+ node.href =
175
+ (elem as HTMLAnchorElement).getAttribute('href') || undefined;
176
+
177
+ // Save classes for the link
178
+ const className = elem.getAttribute('class');
179
+ if (className) {
180
+ node.elementCss = className;
181
+ }
182
+
183
+ // Update attribute name for editable links
184
+ if (
185
+ elem.hasAttribute('data-space-protected') ||
186
+ elem.hasAttribute('data-editable-link')
187
+ ) {
188
+ (node as any)['data-editable-link'] = 'true';
189
+ }
190
+ } else if (tagName === 'button') {
191
+ // Process button tags - preserve all attributes
192
+ const className = elem.getAttribute('class');
193
+ if (className) {
194
+ node.elementCss = className;
195
+ }
196
+
197
+ // Update attribute name for editable buttons
198
+ if (
199
+ elem.hasAttribute('data-space-protected') ||
200
+ elem.hasAttribute('data-editable-button')
201
+ ) {
202
+ (node as any)['data-editable-button'] = 'true';
203
+ }
204
+
205
+ // Copy all data attributes
206
+ Array.from(elem.attributes).forEach((attr) => {
207
+ if (
208
+ attr.name.startsWith('data-') &&
209
+ attr.name !== 'data-space-protected' &&
210
+ attr.name !== 'data-editable-button'
211
+ ) {
212
+ (node as any)[attr.name] = attr.value;
213
+ }
214
+ });
215
+ } else if (tagName === 'img') {
216
+ // Process image tags
217
+ node.src = (elem as HTMLImageElement).getAttribute('src') || undefined;
218
+ node.alt = (elem as HTMLImageElement).getAttribute('alt') || undefined;
219
+ }
220
+
221
+ // Add this node
222
+ result.push(node);
223
+
224
+ // Process children recursively with the new nodeId as parent
225
+ const childNodes = extractNodesFromDOM(elem, nodeId);
226
+ result.push(...childNodes);
227
+ }
228
+ });
229
+
230
+ return result;
231
+ }
232
+
233
+ export function findLinkDestinationInHtml(html: string): string | null {
234
+ const parser = new DOMParser();
235
+ const doc = parser.parseFromString(html, 'text/html');
236
+ const linkElement = doc.querySelector('a');
237
+ return linkElement ? linkElement.getAttribute('href') : null;
238
+ }
239
+
240
+ export function moveNodeAtLocationInContext(
241
+ oldParentNodes: string[],
242
+ originalIdx: number,
243
+ newLocationNode: BaseNode,
244
+ insertNodeId: string,
245
+ nodeId: string,
246
+ location: 'before' | 'after',
247
+ node: BaseNode,
248
+ ctx: NodesContext
249
+ ) {
250
+ if (oldParentNodes) {
251
+ oldParentNodes.splice(originalIdx, 1);
252
+ }
253
+
254
+ const newLocationParentNodes = ctx.getChildNodeIDs(
255
+ newLocationNode.parentId || ''
256
+ );
257
+ // now grab parent nodes, check if we have inner node
258
+ if (
259
+ insertNodeId &&
260
+ newLocationParentNodes &&
261
+ newLocationParentNodes?.indexOf(insertNodeId) !== -1
262
+ ) {
263
+ const spliceIdx = newLocationParentNodes.indexOf(nodeId);
264
+ if (spliceIdx !== -1) {
265
+ newLocationParentNodes.splice(newLocationParentNodes.indexOf(nodeId), 1);
266
+ }
267
+ if (location === 'before') {
268
+ newLocationParentNodes.splice(
269
+ newLocationParentNodes.indexOf(insertNodeId),
270
+ 0,
271
+ nodeId
272
+ );
273
+ } else {
274
+ newLocationParentNodes.splice(
275
+ newLocationParentNodes.indexOf(insertNodeId) + 1,
276
+ 0,
277
+ nodeId
278
+ );
279
+ }
280
+ }
281
+
282
+ if (node.nodeType === 'Pane') {
283
+ const storyFragmentId = ctx.getClosestNodeTypeFromId(
284
+ node.id,
285
+ 'StoryFragment'
286
+ );
287
+ const storyFragment = ctx.allNodes
288
+ .get()
289
+ .get(storyFragmentId) as StoryFragmentNode;
290
+ if (storyFragment) {
291
+ const spliceIdx = storyFragment.paneIds.indexOf(nodeId);
292
+ if (spliceIdx !== -1) {
293
+ storyFragment.paneIds.splice(spliceIdx, 1);
294
+ }
295
+ if (location === 'before') {
296
+ storyFragment.paneIds.splice(
297
+ storyFragment.paneIds.indexOf(insertNodeId),
298
+ 0,
299
+ nodeId
300
+ );
301
+ } else {
302
+ storyFragment.paneIds.splice(
303
+ storyFragment.paneIds.indexOf(insertNodeId) + 1,
304
+ 0,
305
+ nodeId
306
+ );
307
+ }
308
+ }
309
+ }
310
+ node.parentId = newLocationNode.parentId;
311
+ }
312
+
313
+ export function createEmptyStorykeep(id: string) {
314
+ return {
315
+ id,
316
+ nodeType: 'StoryFragment',
317
+ parentId: null,
318
+ isChanged: false,
319
+ paneIds: [],
320
+ changed: undefined,
321
+ slug: 'temp',
322
+ title: 'temp',
323
+ impressions: [],
324
+ created: undefined,
325
+ menuId: undefined,
326
+ socialImagePath: undefined,
327
+ tailwindBgColour: undefined,
328
+ } as StoryFragmentNode;
329
+ }
330
+
331
+ /**
332
+ * Processes HTML content into a flat node structure, preserving formatting and interactive elements.
333
+ * @param html The raw HTML string to process
334
+ * @param parentId The parent node's ID
335
+ * @param originalNodes Optional array of original nodes to match interactive elements against
336
+ * @param onInsertSignal Optional callback to signal insertion of new interactive elements
337
+ * @returns Array of parsed FlatNode objects
338
+ */
339
+ export function processRichTextToNodes(
340
+ html: string,
341
+ parentId: string,
342
+ originalNodes: FlatNode[] = [],
343
+ onInsertSignal?: (tagName: string, nodeId: string) => void
344
+ ): FlatNode[] {
345
+ const parsedNodes = parseMarkdownToNodes(html, parentId);
346
+
347
+ if (parsedNodes.length === 0) return [];
348
+
349
+ // Process each node to restore interactive element properties
350
+ parsedNodes.forEach((node) => {
351
+ if (['a', 'button'].includes(node.tagName)) {
352
+ const matchingOriginalNode = findMatchingNode(node, originalNodes);
353
+
354
+ if (matchingOriginalNode) {
355
+ // Preserve properties from the original node
356
+ node.buttonPayload = matchingOriginalNode.buttonPayload;
357
+ if (node.tagName === 'a' && matchingOriginalNode.href) {
358
+ node.href = matchingOriginalNode.href;
359
+ }
360
+ } else if (onInsertSignal) {
361
+ // New interactive element detected, trigger insert signal
362
+ onInsertSignal(node.tagName, node.id);
363
+ }
364
+ }
365
+ });
366
+
367
+ return parsedNodes;
368
+ }
369
+
370
+ /**
371
+ * Finds a matching node from a list of original nodes based on tag-specific criteria.
372
+ * @param newNode The newly parsed node to match
373
+ * @param originalNodes Array of original nodes to search through
374
+ * @returns Matching FlatNode or undefined if no match found
375
+ */
376
+ export function findMatchingNode(
377
+ newNode: FlatNode,
378
+ originalNodes: FlatNode[]
379
+ ): FlatNode | undefined {
380
+ if (newNode.tagName === 'a' && newNode.href) {
381
+ // Exact href match for links
382
+ const hrefMatch = originalNodes.find(
383
+ (node) => node.tagName === 'a' && node.href === newNode.href
384
+ );
385
+ if (hrefMatch) return hrefMatch;
386
+
387
+ // Domain-based partial match
388
+ const partialMatch = originalNodes.find((node) => {
389
+ if (node.tagName !== 'a' || !node.href || !newNode.href) return false;
390
+ try {
391
+ const origDomain = new URL(node.href).hostname;
392
+ const newDomain = new URL(newNode.href).hostname;
393
+ return origDomain === newDomain;
394
+ } catch {
395
+ return false;
396
+ }
397
+ });
398
+ if (partialMatch) return partialMatch;
399
+ }
400
+
401
+ if (newNode.tagName === 'button') {
402
+ const newText = getNodeText(newNode);
403
+ const buttonMatches = originalNodes.filter(
404
+ (node) => node.tagName === 'button'
405
+ );
406
+
407
+ for (const button of buttonMatches) {
408
+ const buttonText = getNodeText(button);
409
+ if (
410
+ buttonText.includes(newText) ||
411
+ newText.includes(buttonText) ||
412
+ calculateSimilarity(buttonText, newText) > 0.7
413
+ ) {
414
+ return button;
415
+ }
416
+ }
417
+
418
+ if (
419
+ buttonMatches.length === 1 &&
420
+ originalNodes.filter((n) => n.tagName === 'button').length === 1
421
+ ) {
422
+ return buttonMatches[0];
423
+ }
424
+ }
425
+
426
+ return undefined;
427
+ }
428
+
429
+ /**
430
+ * Retrieves the text content of a node, recursively including child nodes.
431
+ * @param node The node to extract text from
432
+ * @param ctx Optional NodesContext for accessing child nodes
433
+ * @returns The combined text content
434
+ */
435
+ export function getNodeText(node: FlatNode, ctx?: NodesContext): string {
436
+ const context = ctx || getCtx();
437
+ if (node.copy) return node.copy;
438
+
439
+ const childIds = context.getChildNodeIDs(node.id);
440
+ if (childIds.length === 0) return '';
441
+
442
+ return childIds
443
+ .map((id) => {
444
+ const childNode = context.allNodes.get().get(id) as FlatNode;
445
+ return childNode ? getNodeText(childNode, context) : '';
446
+ })
447
+ .join(' ')
448
+ .trim();
449
+ }
450
+
451
+ /**
452
+ * Calculates the similarity between two strings (0 to 1, where 1 is identical).
453
+ * @param a First string
454
+ * @param b Second string
455
+ * @returns Similarity score
456
+ */
457
+ export function calculateSimilarity(a: string, b: string): number {
458
+ if (a === b) return 1.0;
459
+ if (a.length === 0 || b.length === 0) return 0.0;
460
+
461
+ const aChars = new Set(a.toLowerCase());
462
+ const bChars = new Set(b.toLowerCase());
463
+ const intersection = new Set([...aChars].filter((x) => bChars.has(x)));
464
+ const union = new Set([...aChars, ...bChars]);
465
+
466
+ return intersection.size / union.size;
467
+ }
468
+
469
+ export function extractClassesFromNodes(dirtyNodes: BaseNode[]): string[] {
470
+ const uniqueClasses = new Set<string>();
471
+
472
+ dirtyNodes.forEach((node) => {
473
+ // Extract from parentCss arrays (like legacy getTailwindWhitelist)
474
+ if ('parentCss' in node && Array.isArray(node.parentCss)) {
475
+ node.parentCss.forEach((classString: string) => {
476
+ classString.split(' ').forEach((className: string) => {
477
+ if (className.trim()) uniqueClasses.add(className.trim());
478
+ });
479
+ });
480
+ }
481
+
482
+ // Extract from elementCss strings (like legacy getTailwindWhitelist)
483
+ if ('elementCss' in node && typeof node.elementCss === 'string') {
484
+ node.elementCss.split(' ').forEach((className: string) => {
485
+ if (className.trim()) uniqueClasses.add(className.trim());
486
+ });
487
+ }
488
+ });
489
+
490
+ return Array.from(uniqueClasses);
491
+ }