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,123 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { transformLivePaneForPreview } from '@/utils/etl';
3
+ import type { NodesContext } from '@/stores/nodes';
4
+
5
+ export interface PanePreviewRequest {
6
+ id: string;
7
+ ctx: NodesContext;
8
+ }
9
+
10
+ export interface PaneFragmentResult {
11
+ id: string;
12
+ htmlString: string;
13
+ error?: string;
14
+ }
15
+
16
+ export interface PanesPreviewGeneratorProps {
17
+ requests: PanePreviewRequest[];
18
+ onComplete: (results: PaneFragmentResult[]) => void;
19
+ onError?: (error: string) => void;
20
+ }
21
+
22
+ export const PanesPreviewGenerator = ({
23
+ requests,
24
+ onComplete,
25
+ onError,
26
+ }: PanesPreviewGeneratorProps) => {
27
+ const [isGenerating, setIsGenerating] = useState(false);
28
+
29
+ useEffect(() => {
30
+ if (requests.length === 0) return;
31
+ if (isGenerating) return;
32
+
33
+ const generateFragments = async () => {
34
+ setIsGenerating(true);
35
+
36
+ try {
37
+ const previewPayloads = [];
38
+ const requestMap = new Map<string, string>();
39
+
40
+ for (const request of requests) {
41
+ const rootNodeId = request.ctx.rootNodeId.get();
42
+
43
+ let actualPaneId = rootNodeId;
44
+ const rootNode = request.ctx.allNodes.get().get(rootNodeId);
45
+
46
+ if (rootNode?.nodeType === 'StoryFragment') {
47
+ const allNodes = Array.from(request.ctx.allNodes.get().values());
48
+ const childPanes = allNodes.filter(
49
+ (node) => node.nodeType === 'Pane' && node.parentId === rootNodeId
50
+ );
51
+
52
+ if (childPanes.length > 0) {
53
+ actualPaneId = childPanes[0].id;
54
+ } else {
55
+ throw new Error(
56
+ `No Pane found under StoryFragment ${rootNodeId}`
57
+ );
58
+ }
59
+ }
60
+
61
+ const previewPayload = transformLivePaneForPreview(
62
+ request.ctx,
63
+ actualPaneId
64
+ );
65
+
66
+ previewPayloads.push(previewPayload);
67
+ requestMap.set(previewPayload.id, request.id);
68
+ }
69
+
70
+ const goBackend =
71
+ import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
72
+ const response = await fetch(`${goBackend}/api/v1/fragments/preview`, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ 'X-Tenant-ID': import.meta.env.PUBLIC_TENANTID || 'default',
77
+ },
78
+ body: JSON.stringify({ panes: previewPayloads }),
79
+ });
80
+
81
+ if (!response.ok) {
82
+ throw new Error(`Preview API failed: ${response.status}`);
83
+ }
84
+
85
+ const { fragments, errors } = await response.json();
86
+
87
+ const results: PaneFragmentResult[] = [];
88
+
89
+ for (const [paneId, requestId] of requestMap.entries()) {
90
+ if (fragments[paneId]) {
91
+ results.push({
92
+ id: requestId,
93
+ htmlString: fragments[paneId],
94
+ });
95
+ } else if (errors && errors[paneId]) {
96
+ results.push({
97
+ id: requestId,
98
+ htmlString: '',
99
+ error: errors[paneId],
100
+ });
101
+ } else {
102
+ results.push({
103
+ id: requestId,
104
+ htmlString: '',
105
+ error: 'No fragment returned',
106
+ });
107
+ }
108
+ }
109
+
110
+ onComplete(results);
111
+ } catch (error) {
112
+ console.error('Batch fragment generation failed:', error);
113
+ onError?.(error instanceof Error ? error.message : 'Unknown error');
114
+ } finally {
115
+ setIsGenerating(false);
116
+ }
117
+ };
118
+
119
+ generateFragments();
120
+ }, [requests, isGenerating, onComplete, onError]);
121
+
122
+ return null;
123
+ };
@@ -0,0 +1,154 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { NodesContext } from '@/stores/nodes';
3
+ import { createEmptyStorykeep } from '@/utils/compositor/nodesHelper';
4
+ import { getTemplateVisualBreakPane } from '@/utils/compositor/TemplatePanes';
5
+ import {
6
+ PanesPreviewGenerator,
7
+ type PanePreviewRequest,
8
+ type PaneFragmentResult,
9
+ } from '@/components/compositor/preview/PanesPreviewGenerator';
10
+ import {
11
+ PaneSnapshotGenerator,
12
+ type SnapshotData,
13
+ } from '@/components/compositor/preview/PaneSnapshotGenerator';
14
+
15
+ interface VisualBreakPreviewProps {
16
+ bgColour: string;
17
+ fillColour: string;
18
+ variant?: string; // Optional variant name for the visual break
19
+ height?: number; // Optional height for the container
20
+ }
21
+
22
+ // The state is managed as a single object since this component only ever handles one preview at a time.
23
+ // This is slightly simpler than managing an array with a single item.
24
+ type PreviewState = {
25
+ htmlFragment?: string;
26
+ snapshot?: SnapshotData;
27
+ error?: string;
28
+ };
29
+
30
+ /**
31
+ * Renders a preview of a single visual break variant.
32
+ *
33
+ * This component uses a modern two-step process for efficiency:
34
+ * 1. It uses PanesPreviewGenerator to fetch an HTML fragment of the break.
35
+ * 2. It then uses PaneSnapshotGenerator to convert that HTML into an image snapshot.
36
+ */
37
+ export const VisualBreakPreview = ({
38
+ bgColour,
39
+ fillColour,
40
+ variant = 'cutwide2', // Default to cutwide2 as it's a commonly used break
41
+ height = 120, // Default height that works well for most breaks
42
+ }: VisualBreakPreviewProps) => {
43
+ const [previewState, setPreviewState] = useState<PreviewState | null>(null);
44
+ const [fragmentRequest, setFragmentRequest] = useState<PanePreviewRequest[]>(
45
+ []
46
+ );
47
+
48
+ useEffect(() => {
49
+ // Reset state whenever the props change to trigger a full regeneration
50
+ setPreviewState(null);
51
+
52
+ // STEP 1: Create a temporary NodesContext for the preview.
53
+ const ctx = new NodesContext();
54
+ ctx.addNode(createEmptyStorykeep('tmp')); // Add root node
55
+
56
+ // Get the template for the specified variant and apply the dynamic colours
57
+ const template = getTemplateVisualBreakPane(variant);
58
+ if (template) {
59
+ if (template.bgColour) template.bgColour = bgColour;
60
+ if (template.bgPane && template.bgPane.type === 'visual-break') {
61
+ if (template.bgPane.breakDesktop) {
62
+ template.bgPane.breakDesktop.svgFill = fillColour;
63
+ }
64
+ if (template.bgPane.breakTablet) {
65
+ template.bgPane.breakTablet.svgFill = fillColour;
66
+ }
67
+ if (template.bgPane.breakMobile) {
68
+ template.bgPane.breakMobile.svgFill = fillColour;
69
+ }
70
+ }
71
+ ctx.addTemplatePane('tmp', template); // Add the template to the context
72
+ }
73
+
74
+ // Prepare a request for the PanesPreviewGenerator to get the HTML.
75
+ setFragmentRequest([{ id: 'visual-break-preview', ctx }]);
76
+ }, [variant, bgColour, fillColour]);
77
+
78
+ // Handler for when the HTML fragment has been generated
79
+ const handleFragmentComplete = (results: PaneFragmentResult[]) => {
80
+ const result = results[0];
81
+ if (result?.htmlString) {
82
+ setPreviewState({ htmlFragment: result.htmlString });
83
+ } else {
84
+ setPreviewState({
85
+ error: result?.error || 'Failed to generate HTML fragment.',
86
+ });
87
+ }
88
+ setFragmentRequest([]); // Clear the request to prevent re-fetching
89
+ };
90
+
91
+ // Handler for when the image snapshot has been generated from the HTML.
92
+ // The 'id' parameter is unused here as we only manage one snapshot at a time.
93
+ const handleSnapshotComplete = (data: SnapshotData) => {
94
+ setPreviewState((prev) => (prev ? { ...prev, snapshot: data } : null));
95
+ };
96
+
97
+ // Display a pulsing placeholder while the process is running
98
+ if (!previewState) {
99
+ return <div className="my-4 h-12 animate-pulse bg-gray-200" />;
100
+ }
101
+
102
+ // Display an error message if something went wrong
103
+ if (previewState.error) {
104
+ return (
105
+ <div className="flex items-center justify-center rounded-md border border-red-200 bg-red-50 p-4 text-sm text-red-700">
106
+ Preview could not be generated: {previewState.error}
107
+ </div>
108
+ );
109
+ }
110
+
111
+ return (
112
+ <div
113
+ className="relative w-full overflow-hidden"
114
+ style={!previewState.snapshot ? { height: `${height}px` } : undefined}
115
+ >
116
+ {/* STEP 2: Render the generator to fetch the HTML fragment. This component renders nothing itself. */}
117
+ {fragmentRequest.length > 0 && (
118
+ <PanesPreviewGenerator
119
+ requests={fragmentRequest}
120
+ onComplete={handleFragmentComplete}
121
+ onError={(err) => setPreviewState({ error: err })}
122
+ />
123
+ )}
124
+
125
+ {/* STEP 3: Once HTML is available, render the snapshot generator to create the image. This component also renders nothing. */}
126
+ {previewState.htmlFragment && !previewState.snapshot && (
127
+ <PaneSnapshotGenerator
128
+ id="visual-break-snapshot"
129
+ htmlString={previewState.htmlFragment}
130
+ outputWidth={800} // Matches the original output width
131
+ onComplete={(_id, data) => handleSnapshotComplete(data)}
132
+ onError={(_id, err) =>
133
+ setPreviewState((prev) =>
134
+ prev ? { ...prev, error: err } : { error: err }
135
+ )
136
+ }
137
+ />
138
+ )}
139
+
140
+ {/* STEP 4: Once the snapshot is complete, display the final image. */}
141
+ {previewState.snapshot && (
142
+ <div className="w-full">
143
+ <img
144
+ src={previewState.snapshot.imageData}
145
+ alt={`Visual break ${variant}`}
146
+ className="w-full"
147
+ />
148
+ </div>
149
+ )}
150
+ </div>
151
+ );
152
+ };
153
+
154
+ export default VisualBreakPreview;
@@ -0,0 +1,181 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useStore } from '@nanostores/react';
3
+ import { navigate } from 'astro:transitions/client';
4
+ import ArrowUturnLeftIcon from '@heroicons/react/24/outline/ArrowUturnLeftIcon';
5
+ import ArrowUturnRightIcon from '@heroicons/react/24/outline/ArrowUturnRightIcon';
6
+ import ViewfinderCircleIcon from '@heroicons/react/24/outline/ViewfinderCircleIcon';
7
+ import DevicePhoneMobileIcon from '@heroicons/react/24/outline/DevicePhoneMobileIcon';
8
+ import DeviceTabletIcon from '@heroicons/react/24/outline/DeviceTabletIcon';
9
+ import ComputerDesktopIcon from '@heroicons/react/24/outline/ComputerDesktopIcon';
10
+ import GlobeAltIcon from '@heroicons/react/24/outline/GlobeAltIcon';
11
+ import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
12
+ import {
13
+ viewportModeStore,
14
+ setViewportMode,
15
+ settingsPanelStore,
16
+ } from '@/stores/storykeep';
17
+ import { getCtx, ROOT_NODE_NAME } from '@/stores/nodes';
18
+ import SaveModal from '@/components/edit/state/SaveModal';
19
+
20
+ interface StoryKeepHeaderProps {
21
+ slug: string;
22
+ isContext: boolean;
23
+ }
24
+
25
+ const StoryKeepHeader = ({ slug, isContext = false }: StoryKeepHeaderProps) => {
26
+ const viewport = useStore(viewportModeStore);
27
+ const ctx = getCtx();
28
+ const hasTitle = useStore(ctx.hasTitle);
29
+ const hasPanes = useStore(ctx.hasPanes);
30
+ const [canUndo, setCanUndo] = useState(false);
31
+ const [canRedo, setCanRedo] = useState(false);
32
+ const [showSaveModal, setShowSaveModal] = useState(false);
33
+
34
+ useEffect(() => {
35
+ const updateUndoRedo = () => {
36
+ setCanUndo(ctx.history.canUndo());
37
+ setCanRedo(ctx.history.canRedo());
38
+ };
39
+ ctx.history.headIndex.listen(updateUndoRedo);
40
+ ctx.history.history.listen(updateUndoRedo);
41
+ }, [ctx.history]);
42
+
43
+ const handleSave = () => {
44
+ setShowSaveModal(true);
45
+ };
46
+
47
+ const handleCloseSaveModal = () => {
48
+ setShowSaveModal(false);
49
+ };
50
+
51
+ const handleUndo = () => {
52
+ settingsPanelStore.set(null);
53
+ ctx.history.undo();
54
+ ctx.notifyNode(ROOT_NODE_NAME);
55
+ };
56
+
57
+ const handleRedo = () => {
58
+ settingsPanelStore.set(null);
59
+ ctx.history.redo();
60
+ ctx.notifyNode(ROOT_NODE_NAME);
61
+ };
62
+
63
+ const handleVisitPage = () => {
64
+ if (canUndo) {
65
+ if (
66
+ confirm(
67
+ 'You have unsaved changes. Do you want to visit the page anyway?'
68
+ )
69
+ ) {
70
+ const previewUrl = !isContext ? `/${slug}` : `/context/${slug}`;
71
+ navigate(previewUrl);
72
+ }
73
+ } else {
74
+ const previewUrl = !isContext ? `/${slug}` : `/context/${slug}`;
75
+ navigate(previewUrl);
76
+ }
77
+ };
78
+
79
+ const activeIconClassName =
80
+ '-rotate-2 w-8 h-8 text-white rounded bg-myblue p-1';
81
+ const iconClassName =
82
+ 'w-8 h-8 text-myblue hover:text-myblue hover:bg-gray-200 rounded-xl hover:rounded bg-white p-1 cursor-pointer transition-all';
83
+
84
+ // Viewport options and their corresponding icons
85
+ const viewportOptions = [
86
+ { value: 'auto', Icon: ViewfinderCircleIcon, title: 'Auto Viewport' },
87
+ { value: 'mobile', Icon: DevicePhoneMobileIcon, title: 'Mobile Viewport' },
88
+ { value: 'tablet', Icon: DeviceTabletIcon, title: 'Tablet Viewport' },
89
+ { value: 'desktop', Icon: ComputerDesktopIcon, title: 'Desktop Viewport' },
90
+ ];
91
+
92
+ if (!hasTitle && !hasPanes) return null;
93
+
94
+ return (
95
+ <>
96
+ <div className="flex flex-wrap items-center justify-center gap-x-4 gap-y-2 p-2">
97
+ {/* Viewport Section with stacked label */}
98
+ <div className="flex flex-col items-center">
99
+ <span className="text-xs font-medium text-gray-600">Viewport:</span>
100
+ <span className="text-xs text-gray-700">{viewport}</span>
101
+ </div>
102
+
103
+ <div className="flex flex-wrap items-center justify-center gap-1">
104
+ {viewportOptions.map(({ value, Icon, title }) => (
105
+ <button
106
+ key={value}
107
+ onClick={() =>
108
+ setViewportMode(
109
+ value as 'auto' | 'mobile' | 'tablet' | 'desktop'
110
+ )
111
+ }
112
+ title={title}
113
+ className={
114
+ viewport === value ? activeIconClassName : iconClassName
115
+ }
116
+ >
117
+ <Icon />
118
+ </button>
119
+ ))}
120
+ </div>
121
+
122
+ {/* Visit Page Icon */}
123
+ <div className="relative">
124
+ <button
125
+ onClick={handleVisitPage}
126
+ title="Visit Page"
127
+ className={`${iconClassName} relative`}
128
+ >
129
+ <GlobeAltIcon />
130
+ {canUndo && (
131
+ <ExclamationTriangleIcon className="absolute -right-1 -top-1 h-3 w-3 rounded-full bg-white text-amber-500" />
132
+ )}
133
+ </button>
134
+ </div>
135
+
136
+ {(canUndo || canRedo) && (
137
+ <div className="flex flex-wrap items-center justify-center gap-2">
138
+ <ArrowUturnLeftIcon
139
+ title="Undo"
140
+ style={{
141
+ visibility: canUndo ? 'visible' : 'hidden',
142
+ display: canUndo ? 'block' : 'none',
143
+ }}
144
+ className={iconClassName}
145
+ onClick={handleUndo}
146
+ />
147
+ <ArrowUturnRightIcon
148
+ title="Redo"
149
+ style={{
150
+ visibility: canRedo ? 'visible' : 'hidden',
151
+ display: canRedo ? 'block' : 'none',
152
+ }}
153
+ className={iconClassName}
154
+ onClick={handleRedo}
155
+ />
156
+ </div>
157
+ )}
158
+
159
+ {canUndo && (
160
+ <div className="flex flex-wrap items-center justify-center gap-2 text-sm">
161
+ <button
162
+ onClick={handleSave}
163
+ className="bg-myblue font-action hover:bg-myorange rounded-md px-3.5 py-1.5 font-bold text-white"
164
+ >
165
+ Save
166
+ </button>
167
+ </div>
168
+ )}
169
+ </div>
170
+
171
+ <SaveModal
172
+ slug={slug}
173
+ isContext={isContext}
174
+ show={showSaveModal}
175
+ onClose={handleCloseSaveModal}
176
+ />
177
+ </>
178
+ );
179
+ };
180
+
181
+ export default StoryKeepHeader;