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,121 @@
1
+ import { useEffect } from 'react';
2
+ import { useStore } from '@nanostores/react';
3
+ import PencilSquareIcon from '@heroicons/react/24/outline/PencilSquareIcon';
4
+ import PaintBrushIcon from '@heroicons/react/24/outline/PaintBrushIcon';
5
+ import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
6
+ import ArrowsUpDownIcon from '@heroicons/react/24/outline/ArrowsUpDownIcon';
7
+ import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
8
+ import BugAntIcon from '@heroicons/react/24/outline/BugAntIcon';
9
+ import { settingsPanelStore } from '@/stores/storykeep';
10
+ import { getCtx } from '@/stores/nodes';
11
+ import type { ToolModeVal } from '@/types/compositorTypes';
12
+
13
+ const storykeepToolModes = [
14
+ {
15
+ key: 'styles' as const,
16
+ Icon: PaintBrushIcon,
17
+ title: 'Styles',
18
+ description: 'Click to edit styles',
19
+ },
20
+ {
21
+ key: 'text' as const,
22
+ Icon: PencilSquareIcon,
23
+ title: 'Write',
24
+ description: 'Click to edit text',
25
+ },
26
+ {
27
+ key: 'insert' as const,
28
+ Icon: PlusIcon,
29
+ title: 'Add',
30
+ description: 'Add new element, e.g. paragraph or image',
31
+ },
32
+ {
33
+ key: 'eraser' as const,
34
+ Icon: TrashIcon,
35
+ title: 'Eraser',
36
+ description: 'Erase any element(s)',
37
+ },
38
+ {
39
+ key: 'move' as const,
40
+ Icon: ArrowsUpDownIcon,
41
+ title: 'Move',
42
+ description: 'Keyboard accessible re-order',
43
+ },
44
+ {
45
+ key: 'debug' as const,
46
+ Icon: BugAntIcon,
47
+ title: 'Debug',
48
+ description: 'Debug node ids',
49
+ },
50
+ ] as const;
51
+
52
+ interface StoryKeepToolModeProps {
53
+ isContext: boolean;
54
+ }
55
+
56
+ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
57
+ //const signal = useStore(settingsPanelStore);
58
+ const ctx = getCtx();
59
+ const { value: toolModeVal } = useStore(ctx.toolModeValStore);
60
+
61
+ const hasTitle = useStore(ctx.hasTitle);
62
+ const hasPanes = useStore(ctx.hasPanes);
63
+
64
+ const className =
65
+ 'w-8 h-8 py-1 rounded-xl bg-white text-myblue hover:bg-mygreen/20 hover:text-black hover:rotate-3 cursor-pointer transition-all';
66
+ const classNameActive = 'w-8 h-8 py-1.5 rounded-md bg-myblue text-white';
67
+
68
+ const currentToolMode =
69
+ storykeepToolModes.find((mode) => mode.key === toolModeVal) ??
70
+ storykeepToolModes[0];
71
+
72
+ const handleClick = (mode: ToolModeVal) => {
73
+ settingsPanelStore.set(null);
74
+ ctx.toolModeValStore.set({ value: mode });
75
+ ctx.showGuids.set(mode === `debug`);
76
+ ctx.notifyNode('root');
77
+ };
78
+
79
+ // Escape key listener
80
+ useEffect(() => {
81
+ const handleEscapeKey = (event: KeyboardEvent) => {
82
+ if (event.key === 'Escape') {
83
+ ctx.toolModeValStore.set({ value: 'text' });
84
+ console.log('Tool mode reset to text via Escape');
85
+ }
86
+ };
87
+ document.addEventListener('keydown', handleEscapeKey);
88
+ return () => {
89
+ document.removeEventListener('keydown', handleEscapeKey);
90
+ };
91
+ }, [ctx]);
92
+
93
+ if (!hasTitle || (!hasPanes && !isContext)) return null;
94
+
95
+ return (
96
+ <nav
97
+ id="mainNav"
98
+ className="z-102 bg-mywhite fixed bottom-0 left-0 right-0 pt-1.5 md:sticky md:bottom-auto md:left-0 md:top-24 md:h-screen md:w-16 md:pt-0"
99
+ >
100
+ <div className="flex flex-wrap justify-around gap-4 py-3.5 md:mt-0 md:flex-col md:items-center md:gap-8 md:space-x-0 md:space-y-2 md:py-2">
101
+ <div className="text-mydarkgrey h-16 text-center text-sm font-bold">
102
+ mode:
103
+ <div className="font-action text-myblue pt-1.5 text-center text-xs">
104
+ {currentToolMode.title}
105
+ </div>
106
+ </div>
107
+ {storykeepToolModes.map(({ key, Icon, description }) => (
108
+ <div title={description} key={key}>
109
+ {key === toolModeVal ? (
110
+ <Icon className={classNameActive} />
111
+ ) : (
112
+ <Icon className={className} onClick={() => handleClick(key)} />
113
+ )}
114
+ </div>
115
+ ))}
116
+ </div>
117
+ </nav>
118
+ );
119
+ };
120
+
121
+ export default StoryKeepToolMode;
@@ -0,0 +1,91 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useStore } from '@nanostores/react';
3
+ import { getCtx } from '@/stores/nodes';
4
+ import { settingsPanelStore } from '@/stores/storykeep';
5
+ import ContextPaneTitlePanel from './ContextPaneConfig_title';
6
+ import ContextPaneSlugPanel from './ContextPaneConfig_slug';
7
+ import { ContextPaneMode, type PaneNode } from '@/types/compositorTypes';
8
+
9
+ const ContextPaneConfig = ({ nodeId }: { nodeId: string }) => {
10
+ const [isNodeAvailable, setIsNodeAvailable] = useState(false);
11
+ const [paneNode, setPaneNode] = useState<PaneNode | null>(null);
12
+
13
+ const nodesCtx = getCtx();
14
+ const activePaneMode = useStore(nodesCtx.activePaneMode);
15
+
16
+ const isActive =
17
+ activePaneMode.panel === 'context' && activePaneMode.paneId === nodeId;
18
+
19
+ const mode =
20
+ isActive && activePaneMode.mode
21
+ ? (activePaneMode.mode as ContextPaneMode)
22
+ : ContextPaneMode.DEFAULT;
23
+
24
+ const setMode = (newMode: ContextPaneMode) => {
25
+ nodesCtx.setPanelMode(nodeId, 'context', newMode);
26
+ settingsPanelStore.set(null);
27
+ };
28
+
29
+ useEffect(() => {
30
+ const checkNode = () => {
31
+ const ctx = getCtx();
32
+ const allNodes = ctx.allNodes.get();
33
+ const node = allNodes.get(nodeId) as PaneNode;
34
+
35
+ if (node) {
36
+ setPaneNode(node);
37
+ setIsNodeAvailable(true);
38
+ }
39
+ };
40
+
41
+ checkNode();
42
+
43
+ const intervalId = setInterval(() => {
44
+ if (!isNodeAvailable) {
45
+ checkNode();
46
+ } else {
47
+ clearInterval(intervalId);
48
+ }
49
+ }, 100);
50
+
51
+ return () => {
52
+ clearInterval(intervalId);
53
+ };
54
+ }, [nodeId, isNodeAvailable]);
55
+
56
+ if (!isNodeAvailable || !paneNode) {
57
+ return null;
58
+ }
59
+
60
+ if (mode === ContextPaneMode.TITLE) {
61
+ return <ContextPaneTitlePanel nodeId={nodeId} setMode={setMode} />;
62
+ } else if (mode === ContextPaneMode.SLUG) {
63
+ return <ContextPaneSlugPanel nodeId={nodeId} setMode={setMode} />;
64
+ }
65
+
66
+ return (
67
+ <div className="mb-4">
68
+ <div className="w-full rounded-b-md bg-white p-4">
69
+ <div className="flex flex-wrap items-center gap-2">
70
+ {/* Title control */}
71
+ <button
72
+ onClick={() => setMode(ContextPaneMode.TITLE)}
73
+ className="text-md min-h-9 rounded border border-cyan-200 bg-white px-3 text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
74
+ >
75
+ Title: <span className="font-bold">{paneNode.title}</span>
76
+ </button>
77
+
78
+ {/* Slug control */}
79
+ <button
80
+ onClick={() => setMode(ContextPaneMode.SLUG)}
81
+ className="text-md h-9 rounded border border-cyan-200 bg-white px-3 text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
82
+ >
83
+ Slug: <span className="font-bold">{paneNode.slug}</span>
84
+ </button>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ );
89
+ };
90
+
91
+ export default ContextPaneConfig;
@@ -0,0 +1,174 @@
1
+ import { useState, useEffect } from 'react';
2
+ import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
3
+ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
4
+ import { getCtx } from '@/stores/nodes';
5
+ import { cloneDeep } from '@/utils/helpers';
6
+ import { ContextPaneMode, type PaneNode } from '@/types/compositorTypes';
7
+
8
+ interface PaneSlugPanelProps {
9
+ nodeId: string;
10
+ setMode: (mode: ContextPaneMode) => void;
11
+ }
12
+
13
+ const PaneSlugPanel = ({ nodeId, setMode }: PaneSlugPanelProps) => {
14
+ const [slug, setSlug] = useState('');
15
+ const [isValid, setIsValid] = useState(false);
16
+ const [warning, setWarning] = useState(false);
17
+ const [charCount, setCharCount] = useState(0);
18
+
19
+ const ctx = getCtx();
20
+ const allNodes = ctx.allNodes.get();
21
+ const paneNode = allNodes.get(nodeId) as PaneNode;
22
+ if (!paneNode) return null;
23
+
24
+ useEffect(() => {
25
+ setSlug(paneNode.slug);
26
+ setCharCount(paneNode.slug.length);
27
+ }, [paneNode.slug]);
28
+
29
+ const validateSlug = (value: string): string => {
30
+ // Convert to lowercase and replace spaces with hyphens
31
+ return (
32
+ value
33
+ .toLowerCase()
34
+ // Replace spaces and underscores with hyphens
35
+ .replace(/[\s_]+/g, '-')
36
+ // Remove any characters that aren't alphanumeric or hyphens
37
+ .replace(/[^a-z0-9-]/g, '')
38
+ );
39
+ // Replace multiple consecutive hyphens with a single hyphen
40
+ //.replace(/-+/g, '-')
41
+ // Remove hyphens from start and end
42
+ //.replace(/^-+|-+$/g, '');
43
+ };
44
+
45
+ const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {
46
+ const newSlug = validateSlug(e.target.value);
47
+ if (newSlug.length <= 75) {
48
+ // Prevent more than 75 chars
49
+ setSlug(newSlug);
50
+ setCharCount(newSlug.length);
51
+ setIsValid(newSlug.length >= 3 && newSlug.length <= 60);
52
+ setWarning(newSlug.length > 60 && newSlug.length <= 75);
53
+ }
54
+ };
55
+
56
+ const handleSlugBlur = () => {
57
+ if (slug.length >= 3) {
58
+ // Only update if meets minimum length
59
+ const ctx = getCtx();
60
+ const updatedNode = { ...cloneDeep(paneNode), slug, isChanged: true };
61
+ ctx.modifyNodes([updatedNode]);
62
+ }
63
+ };
64
+
65
+ return (
66
+ <div className="group mb-4 w-full rounded-b-md bg-white px-1.5 py-6">
67
+ <div className="px-3.5">
68
+ <div className="mb-4 flex justify-between">
69
+ <h3 className="text-lg font-bold">Slug (url)</h3>
70
+ <button
71
+ onClick={() => setMode(ContextPaneMode.DEFAULT)}
72
+ className="text-myblue hover:text-black"
73
+ >
74
+ ← Go Back
75
+ </button>
76
+ </div>
77
+
78
+ <div className="relative max-w-96">
79
+ <input
80
+ type="text"
81
+ value={slug}
82
+ onChange={handleSlugChange}
83
+ onBlur={handleSlugBlur}
84
+ onKeyDown={(e) => {
85
+ if (e.key === 'Enter') {
86
+ e.currentTarget.blur();
87
+ }
88
+ }}
89
+ className={`w-full rounded-md border px-2 py-1 pr-16 ${
90
+ charCount < 3
91
+ ? 'border-red-500 bg-red-50'
92
+ : isValid
93
+ ? 'border-green-500 bg-green-50'
94
+ : warning
95
+ ? 'border-yellow-500 bg-yellow-50'
96
+ : 'border-gray-300'
97
+ }`}
98
+ placeholder="Enter URL slug (3-60 characters recommended)"
99
+ />
100
+ <div className="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-2">
101
+ {charCount < 3 ? (
102
+ <ExclamationTriangleIcon className="h-5 w-5 text-red-500" />
103
+ ) : isValid ? (
104
+ <CheckIcon className="h-5 w-5 text-green-500" />
105
+ ) : warning ? (
106
+ <ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />
107
+ ) : null}
108
+ <span
109
+ className={`text-sm ${
110
+ charCount < 3
111
+ ? 'text-red-500'
112
+ : isValid
113
+ ? 'text-green-500'
114
+ : warning
115
+ ? 'text-yellow-500'
116
+ : 'text-gray-500'
117
+ }`}
118
+ >
119
+ {charCount}/75
120
+ </span>
121
+ </div>
122
+ </div>
123
+ <div className="mt-4 text-lg">
124
+ <div className="text-gray-600">
125
+ Create a clean, descriptive URL slug that helps users and search
126
+ engines understand the page content.
127
+ <ul className="ml-4 mt-1">
128
+ <li>
129
+ <CheckIcon className="inline h-4 w-4" /> Use hyphens to separate
130
+ words
131
+ </li>
132
+ <li>
133
+ <CheckIcon className="inline h-4 w-4" /> Keep it short and
134
+ descriptive
135
+ </li>
136
+ <li>
137
+ <CheckIcon className="inline h-4 w-4" /> Use only lowercase
138
+ letters, numbers, and hyphens
139
+ </li>
140
+ <li>
141
+ <CheckIcon className="inline h-4 w-4" /> Avoid special
142
+ characters or spaces
143
+ </li>
144
+ </ul>
145
+ </div>
146
+ <div className="py-4">
147
+ {charCount < 3 && (
148
+ <span className="text-red-500">
149
+ Slug must be at least 3 characters
150
+ </span>
151
+ )}
152
+ {charCount >= 3 && charCount < 5 && (
153
+ <span className="text-gray-500">
154
+ Consider adding more characters for better description
155
+ </span>
156
+ )}
157
+ {warning && (
158
+ <span className="text-yellow-500">
159
+ Slug is getting long - consider shortening it
160
+ </span>
161
+ )}
162
+ {isValid && charCount >= 5 && (
163
+ <span className="text-green-500">
164
+ Good URL length and format!
165
+ </span>
166
+ )}
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ );
172
+ };
173
+
174
+ export default PaneSlugPanel;
@@ -0,0 +1,186 @@
1
+ import { useState, useEffect, type ChangeEvent } from 'react';
2
+ import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
3
+ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
4
+ import { cloneDeep, titleToSlug, findUniqueSlug } from '@/utils/helpers';
5
+ import { getCtx } from '@/stores/nodes';
6
+ import { fullContentMapStore } from '@/stores/storykeep';
7
+ import { ContextPaneMode, type PaneNode } from '@/types/compositorTypes';
8
+
9
+ interface ContextPaneTitlePanelProps {
10
+ nodeId: string;
11
+ setMode?: (mode: ContextPaneMode) => void;
12
+ }
13
+
14
+ const ContextPaneTitlePanel = ({
15
+ nodeId,
16
+ setMode,
17
+ }: ContextPaneTitlePanelProps) => {
18
+ const [title, setTitle] = useState('');
19
+ const [isValid, setIsValid] = useState(false);
20
+ const [warning, setWarning] = useState(false);
21
+ const [charCount, setCharCount] = useState(0);
22
+
23
+ const ctx = getCtx();
24
+ const allNodes = ctx.allNodes.get();
25
+ const paneNode = allNodes.get(nodeId) as PaneNode;
26
+ if (!paneNode) return null;
27
+
28
+ useEffect(() => {
29
+ setTitle(paneNode.title);
30
+ setCharCount(paneNode.title.length);
31
+ }, [paneNode.title]);
32
+
33
+ const handleTitleChange = (e: ChangeEvent<HTMLInputElement>) => {
34
+ const newTitle = e.target.value;
35
+ if (newTitle.length <= 70) {
36
+ // Prevent more than 70 chars
37
+ setTitle(newTitle);
38
+ setCharCount(newTitle.length);
39
+ setIsValid(newTitle.length >= 35 && newTitle.length <= 60);
40
+ setWarning(newTitle.length > 60 && newTitle.length <= 70);
41
+ }
42
+ };
43
+
44
+ const handleTitleBlur = () => {
45
+ if (title.length >= 10) {
46
+ // Only update if meets minimum length
47
+ const ctx = getCtx();
48
+ const existingSlugs = fullContentMapStore
49
+ .get()
50
+ .filter((item) => ['Pane', 'StoryFragment'].includes(item.type))
51
+ .map((item) => item.slug);
52
+ const newSlug =
53
+ paneNode.slug === ``
54
+ ? findUniqueSlug(titleToSlug(title), existingSlugs)
55
+ : null;
56
+ const updatedNode = cloneDeep({
57
+ ...paneNode,
58
+ title,
59
+ ...(newSlug ? { slug: newSlug } : {}),
60
+ isChanged: true,
61
+ });
62
+ ctx.modifyNodes([updatedNode]);
63
+ }
64
+ };
65
+
66
+ return (
67
+ <div className="group mb-4 w-full rounded-b-md bg-white px-1.5 py-6">
68
+ <div className="px-3.5">
69
+ <div className="mb-4 flex justify-between">
70
+ <h3 className="text-lg font-bold">Page Title</h3>
71
+ {setMode && (
72
+ <button
73
+ onClick={() => setMode && setMode(ContextPaneMode.DEFAULT)}
74
+ className="text-myblue hover:text-black"
75
+ >
76
+ ← Go Back
77
+ </button>
78
+ )}
79
+ </div>
80
+
81
+ <div className="relative">
82
+ <input
83
+ type="text"
84
+ value={title}
85
+ onChange={handleTitleChange}
86
+ onBlur={handleTitleBlur}
87
+ onKeyDown={(e) => {
88
+ if (e.key === 'Enter') {
89
+ e.currentTarget.blur();
90
+ }
91
+ }}
92
+ className={`w-full rounded-md border px-2 py-1 pr-16 ${
93
+ charCount < 10
94
+ ? 'border-red-500 bg-red-50'
95
+ : isValid
96
+ ? 'border-green-500 bg-green-50'
97
+ : warning
98
+ ? 'border-yellow-500 bg-yellow-50'
99
+ : 'border-gray-300'
100
+ }`}
101
+ placeholder="Enter page title (35-60 characters recommended)"
102
+ />
103
+ <div className="absolute right-2 top-1/2 flex -translate-y-1/2 items-center gap-2">
104
+ {charCount < 10 ? (
105
+ <ExclamationTriangleIcon className="h-5 w-5 text-red-500" />
106
+ ) : isValid ? (
107
+ <CheckIcon className="h-5 w-5 text-green-500" />
108
+ ) : warning ? (
109
+ <ExclamationTriangleIcon className="h-5 w-5 text-yellow-500" />
110
+ ) : null}
111
+ <span
112
+ className={`text-sm ${
113
+ charCount < 10
114
+ ? 'text-red-500'
115
+ : isValid
116
+ ? 'text-green-500'
117
+ : warning
118
+ ? 'text-yellow-500'
119
+ : 'text-gray-500'
120
+ }`}
121
+ >
122
+ {charCount}/70
123
+ </span>
124
+ </div>
125
+ </div>
126
+ <div className="mt-2 flex items-center gap-2">
127
+ <button
128
+ onClick={handleTitleBlur}
129
+ disabled={title.length < 10}
130
+ className={`rounded bg-cyan-700 px-3 py-1 text-sm text-white transition-colors hover:bg-cyan-800 ${
131
+ title !== paneNode.title ? 'inline-flex' : 'hidden'
132
+ } items-center ${title.length < 10 ? 'cursor-not-allowed opacity-50' : ''}`}
133
+ >
134
+ <CheckIcon className="mr-1 h-4 w-4" />
135
+ Save
136
+ </button>
137
+ <span
138
+ className={`text-sm ${title !== paneNode.title ? 'inline' : 'hidden'} text-gray-500`}
139
+ >
140
+ or press Enter
141
+ </span>
142
+ </div>
143
+ <div className="mt-4 text-lg">
144
+ <div className="text-gray-600">
145
+ Write a clear, descriptive title that accurately represents your
146
+ page content.
147
+ <ul className="ml-4 mt-1">
148
+ <li>
149
+ <CheckIcon className="inline h-4 w-4" /> Include relevant
150
+ keywords
151
+ </li>
152
+ <li>
153
+ <CheckIcon className="inline h-4 w-4" /> Avoid unnecessary words
154
+ like "welcome to" or "the"
155
+ </li>
156
+ <li>
157
+ <CheckIcon className="inline h-4 w-4" /> Unique titles across
158
+ your website
159
+ </li>
160
+ </ul>
161
+ </div>
162
+ <div className="py-4">
163
+ {charCount < 10 && (
164
+ <span className="text-red-500">
165
+ Title must be at least 10 characters
166
+ </span>
167
+ )}
168
+ {charCount >= 10 && charCount < 35 && (
169
+ <span className="text-gray-500">
170
+ Add {35 - charCount} more characters for optimal length
171
+ </span>
172
+ )}
173
+ {warning && (
174
+ <span className="text-yellow-500">Title is getting long</span>
175
+ )}
176
+ {isValid && (
177
+ <span className="text-green-500">Perfect title length!</span>
178
+ )}
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ );
184
+ };
185
+
186
+ export default ContextPaneTitlePanel;