astro-tractstack 2.0.0-rc.9 → 2.0.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 (142) hide show
  1. package/LICENSE +8 -97
  2. package/README.md +7 -5
  3. package/bin/create-tractstack.js +31 -8
  4. package/dist/index.js +106 -29
  5. package/package.json +10 -5
  6. package/templates/css/frontend.css +1 -1
  7. package/templates/custom/minimal/CodeHook.astro +13 -12
  8. package/templates/custom/minimal/CustomRoutes.astro +25 -31
  9. package/templates/custom/with-examples/CodeHook.astro +22 -11
  10. package/templates/custom/with-examples/CustomRoutes.astro +4 -8
  11. package/templates/custom/with-examples/ProductCard.astro +29 -0
  12. package/templates/custom/with-examples/ProductCardWrapper.astro +43 -0
  13. package/templates/custom/with-examples/ProductGrid.astro +64 -0
  14. package/templates/custom/with-examples/pages/Collections.astro +58 -98
  15. package/templates/gitignore +42 -0
  16. package/templates/prettierignore +5 -0
  17. package/templates/prettierrc +19 -0
  18. package/templates/src/client/app.js +127 -0
  19. package/templates/src/client/htmx.min.js +3519 -0
  20. package/templates/src/client/view.js +429 -0
  21. package/templates/src/components/Footer.astro +4 -9
  22. package/templates/src/components/Header.astro +67 -60
  23. package/templates/src/components/Menu.tsx +188 -52
  24. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
  25. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +9 -13
  26. package/templates/src/components/codehooks/EpinetTableView.tsx +11 -7
  27. package/templates/src/components/codehooks/EpinetWrapper.tsx +1 -0
  28. package/templates/src/components/codehooks/FeaturedArticle.astro +105 -0
  29. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +318 -0
  30. package/templates/src/components/codehooks/ListContent.astro +32 -162
  31. package/templates/src/components/codehooks/ListContentSetup.tsx +43 -138
  32. package/templates/src/components/codehooks/ProductCardSetup.tsx +152 -0
  33. package/templates/src/components/codehooks/ProductGridSetup.tsx +274 -0
  34. package/templates/src/components/codehooks/SearchWidget.tsx +453 -0
  35. package/templates/src/components/compositor/Node.tsx +3 -6
  36. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +21 -11
  37. package/templates/src/components/compositor/elements/BunnyVideo.tsx +21 -20
  38. package/templates/src/components/compositor/nodes/Pane.tsx +51 -21
  39. package/templates/src/components/compositor/nodes/RenderChildren.tsx +6 -1
  40. package/templates/src/components/compositor/nodes/Widget.tsx +16 -2
  41. package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +155 -0
  42. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +20 -1
  43. package/templates/src/components/edit/Header.tsx +10 -4
  44. package/templates/src/components/edit/PanelSwitch.tsx +11 -7
  45. package/templates/src/components/edit/SettingsPanel.tsx +29 -18
  46. package/templates/src/components/edit/ToolBar.tsx +1 -28
  47. package/templates/src/components/edit/ToolMode.tsx +45 -32
  48. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +12 -2
  49. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +8 -2
  50. package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +1 -1
  51. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +17 -27
  52. package/templates/src/components/edit/pane/PageGenSelector.tsx +16 -16
  53. package/templates/src/components/edit/pane/PageGenSpecial.tsx +26 -49
  54. package/templates/src/components/edit/pane/PageGen_preview.tsx +17 -2
  55. package/templates/src/components/edit/pane/PanePanel_path.tsx +2 -4
  56. package/templates/src/components/edit/pane/PanePanel_title.tsx +243 -76
  57. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +17 -19
  58. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +48 -37
  59. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +60 -55
  60. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +56 -50
  61. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +54 -47
  62. package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +54 -44
  63. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
  64. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +54 -40
  65. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +3 -3
  66. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +56 -49
  67. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +14 -5
  68. package/templates/src/components/edit/state/SaveModal.tsx +316 -169
  69. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +1 -1
  70. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +56 -55
  71. package/templates/src/components/edit/widgets/BunnyWidget.tsx +538 -59
  72. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +656 -0
  73. package/templates/src/components/edit/widgets/ToggleWidget.tsx +9 -16
  74. package/templates/src/components/fields/ArtpackImage.tsx +4 -1
  75. package/templates/src/components/fields/BackgroundImage.tsx +1 -1
  76. package/templates/src/components/fields/BackgroundImageWrapper.tsx +127 -35
  77. package/templates/src/components/fields/ColorPickerCombo.tsx +66 -62
  78. package/templates/src/components/fields/ImageUpload.tsx +1 -1
  79. package/templates/src/components/fields/ViewportComboBox.tsx +59 -42
  80. package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
  81. package/templates/src/components/form/ActionBuilderField.tsx +306 -87
  82. package/templates/src/components/search/SearchModal.tsx +420 -0
  83. package/templates/src/components/search/SearchResults.tsx +367 -0
  84. package/templates/src/components/search/SearchWrapper.tsx +46 -0
  85. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -1
  86. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +34 -8
  87. package/templates/src/components/storykeep/Dashboard_Branding.tsx +1 -1
  88. package/templates/src/components/storykeep/Dashboard_Content.tsx +6 -0
  89. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
  90. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +38 -34
  91. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +1 -1
  92. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +56 -8
  93. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +18 -3
  94. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
  95. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
  96. package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
  97. package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
  98. package/templates/src/constants/shapes.ts +9 -0
  99. package/templates/src/constants.ts +2121 -16
  100. package/templates/src/hooks/useSearch.ts +228 -0
  101. package/templates/src/layouts/Layout.astro +213 -104
  102. package/templates/src/lib/storyData.ts +4 -1
  103. package/templates/src/pages/[...slug]/edit.astro +14 -14
  104. package/templates/src/pages/[...slug].astro +82 -21
  105. package/templates/src/pages/api/orphan-analysis.ts +0 -1
  106. package/templates/src/pages/api/tailwind.ts +23 -21
  107. package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
  108. package/templates/src/pages/context/[...contextSlug].astro +7 -2
  109. package/templates/src/pages/storykeep/advanced.astro +5 -4
  110. package/templates/src/pages/storykeep/branding.astro +5 -4
  111. package/templates/src/pages/storykeep/content.astro +5 -4
  112. package/templates/src/pages/storykeep/init.astro +40 -1
  113. package/templates/src/pages/storykeep/login.astro +1 -1
  114. package/templates/src/pages/storykeep.astro +5 -4
  115. package/templates/src/stores/nodes.ts +59 -88
  116. package/templates/src/stores/orphanAnalysis.ts +19 -21
  117. package/templates/src/stores/storykeep.ts +7 -0
  118. package/templates/src/types/compositorTypes.ts +6 -0
  119. package/templates/src/types/tractstack.ts +17 -0
  120. package/templates/src/utils/actions/lispLexer.ts +2 -2
  121. package/templates/src/utils/actions/preParse_Action.ts +3 -0
  122. package/templates/src/utils/api/beliefHelpers.ts +12 -36
  123. package/templates/src/utils/api/menuHelpers.ts +2 -2
  124. package/templates/src/utils/api.ts +26 -0
  125. package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
  126. package/templates/src/utils/compositor/allowInsert.ts +5 -3
  127. package/templates/src/utils/compositor/nodesHelper.ts +4 -0
  128. package/templates/src/utils/compositor/processMarkdown.ts +16 -2
  129. package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
  130. package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
  131. package/templates/src/utils/compositor/typeGuards.ts +1 -0
  132. package/templates/src/utils/customHelpers.ts +38 -0
  133. package/templates/src/utils/helpers.ts +2 -2
  134. package/templates/src/utils/layout.ts +65 -144
  135. package/utils/inject-files.ts +95 -18
  136. package/templates/src/client/analytics-events.js +0 -207
  137. package/templates/src/client/belief-events.js +0 -191
  138. package/templates/src/client/sse.js +0 -613
  139. package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
  140. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
  141. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
  142. package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
@@ -2,6 +2,7 @@ import { ulid } from 'ulid';
2
2
  import {
3
3
  TemplateBeliefNode,
4
4
  TemplateBunnyNode,
5
+ TemplateDisclosureNode,
5
6
  TemplateEmailSignUpNode,
6
7
  TemplateH2Node,
7
8
  TemplateH3Node,
@@ -56,6 +57,9 @@ export const getTemplateNode = (value: ToolAddMode): TemplateNode => {
56
57
  case 'identify':
57
58
  templateNode = cloneDeep(TemplateIdentifyAsNode);
58
59
  break;
60
+ case 'interactiveDisclosure':
61
+ templateNode = cloneDeep(TemplateDisclosureNode);
62
+ break;
59
63
  case 'p':
60
64
  default:
61
65
  templateNode = cloneDeep(TemplatePNode);
@@ -1,6 +1,8 @@
1
1
  import { ulid } from 'ulid';
2
2
  import { NodesContext } from '@/stores/nodes';
3
3
  import { fullContentMapStore } from '@/stores/storykeep';
4
+ import { tailwindToHex } from './tailwindColors';
5
+ import { SvgBreaks } from '@/constants/shapes';
4
6
  import { findUniqueSlug } from '@/utils/helpers';
5
7
  import type {
6
8
  PageDesign,
@@ -276,8 +278,20 @@ export async function createPagePanes(
276
278
  const aboveColor = abovePane?.bgColour || 'white';
277
279
  const nextContentPane = design.contentDesign(!useOdd);
278
280
  const belowColor = nextContentPane.bgColour;
279
- breakTemplate.bgColour = aboveColor;
280
- const svgFill = belowColor;
281
+ const breakData = breakTemplate.bgPane?.breakDesktop;
282
+ const shapeName = breakData
283
+ ? `${breakData.collection}${breakData.image}`
284
+ : '';
285
+ const isFlipped = (shapeName && SvgBreaks[shapeName]?.flipped) || false;
286
+
287
+ breakTemplate.bgColour = tailwindToHex(
288
+ isFlipped ? belowColor : aboveColor,
289
+ null
290
+ );
291
+ const svgFill = tailwindToHex(
292
+ isFlipped ? aboveColor : belowColor,
293
+ null
294
+ );
281
295
  if (breakTemplate.bgPane) {
282
296
  if (breakTemplate.bgPane.breakDesktop) {
283
297
  breakTemplate.bgPane.breakDesktop.svgFill = svgFill;
@@ -85,6 +85,10 @@ const reduceClassName = (
85
85
  if (useKeyAsClass && typeof v === 'string')
86
86
  return `${modifier}${applyPrefix(v)}`;
87
87
  if (typeof v === 'string' || typeof v === 'number') {
88
+ // Add check for empty string
89
+ if (typeof v === 'string' && v.trim() === '') {
90
+ return '';
91
+ }
88
92
  if (typeof v === 'string' && v.startsWith('-')) {
89
93
  return `${modifier}-${applyPrefix(`${thisSelector}${v}`)}`;
90
94
  }
@@ -360,6 +360,7 @@ export function getJustCopyDesign(
360
360
  baseClasses[2].mobile.maxW = '3xl';
361
361
  if (bordered) {
362
362
  baseClasses[2].mobile.rounded = 'lg';
363
+ baseClasses[2].mobile.shadow = 'md';
363
364
  baseClasses[2].mobile.bgCOLOR = getColor(
364
365
  {
365
366
  light: useOdd ? 'brand-2' : 'white',
@@ -371,7 +372,6 @@ export function getJustCopyDesign(
371
372
  },
372
373
  theme
373
374
  );
374
- baseClasses[2].mobile.shadow = 'md';
375
375
  }
376
376
  break;
377
377
  }
@@ -385,6 +385,7 @@ export function getJustCopyDesign(
385
385
  baseClasses[2].mobile.textWRAP = 'balance';
386
386
  if (bordered) {
387
387
  baseClasses[2].mobile.rounded = 'lg';
388
+ baseClasses[2].mobile.shadow = 'md';
388
389
  baseClasses[2].mobile.bgCOLOR = getColor(
389
390
  {
390
391
  light: useOdd ? 'brand-2' : 'white',
@@ -396,7 +397,6 @@ export function getJustCopyDesign(
396
397
  },
397
398
  theme
398
399
  );
399
- baseClasses[2].mobile.shadow = 'md';
400
400
  }
401
401
  break;
402
402
  }
@@ -408,6 +408,7 @@ export function getJustCopyDesign(
408
408
  baseClasses[2].mobile.textWRAP = 'pretty';
409
409
  if (bordered) {
410
410
  baseClasses[2].mobile.rounded = 'lg';
411
+ baseClasses[2].mobile.shadow = 'md';
411
412
  baseClasses[2].mobile.bgCOLOR = getColor(
412
413
  {
413
414
  light: useOdd ? 'brand-2' : 'white',
@@ -419,7 +420,6 @@ export function getJustCopyDesign(
419
420
  },
420
421
  theme
421
422
  );
422
- baseClasses[2].mobile.shadow = 'md';
423
423
  }
424
424
  }
425
425
  }
@@ -507,6 +507,7 @@ export function getSubTitleDesign(
507
507
  baseClasses[2].mobile.maxW = '3xl';
508
508
  if (bordered) {
509
509
  baseClasses[2].mobile.rounded = 'lg';
510
+ baseClasses[2].mobile.shadow = 'md';
510
511
  baseClasses[2].mobile.bgCOLOR = getColor(
511
512
  {
512
513
  light: useOdd ? 'brand-2' : 'white',
@@ -518,7 +519,6 @@ export function getSubTitleDesign(
518
519
  },
519
520
  theme
520
521
  );
521
- baseClasses[2].mobile.shadow = 'md';
522
522
  }
523
523
  break;
524
524
  }
@@ -557,6 +557,7 @@ export function getSubTitleDesign(
557
557
  baseClasses[2].mobile.textWRAP = 'balance';
558
558
  if (bordered) {
559
559
  baseClasses[2].mobile.rounded = 'lg';
560
+ baseClasses[2].mobile.shadow = 'md';
560
561
  baseClasses[2].mobile.bgCOLOR = getColor(
561
562
  {
562
563
  light: useOdd ? 'brand-2' : 'white',
@@ -568,7 +569,6 @@ export function getSubTitleDesign(
568
569
  },
569
570
  theme
570
571
  );
571
- baseClasses[2].mobile.shadow = 'md';
572
572
  }
573
573
  break;
574
574
  }
@@ -605,6 +605,7 @@ export function getSubTitleDesign(
605
605
  baseClasses[2].mobile.textWRAP = 'pretty';
606
606
  baseClasses[2].mobile.maxW = '3xl';
607
607
  if (bordered) {
608
+ baseClasses[2].mobile.shadow = 'md';
608
609
  baseClasses[2].mobile.bgCOLOR = getColor(
609
610
  {
610
611
  light: useOdd ? 'brand-2' : 'white',
@@ -616,7 +617,6 @@ export function getSubTitleDesign(
616
617
  },
617
618
  theme
618
619
  );
619
- baseClasses[2].mobile.shadow = 'md';
620
620
  }
621
621
  break;
622
622
  }
@@ -653,6 +653,7 @@ export function getSubTitleDesign(
653
653
  baseClasses[2].mobile.textWRAP = 'balance';
654
654
  if (bordered) {
655
655
  baseClasses[2].mobile.rounded = 'lg';
656
+ baseClasses[2].mobile.shadow = 'md';
656
657
  baseClasses[2].mobile.bgCOLOR = getColor(
657
658
  {
658
659
  light: useOdd ? 'brand-2' : 'white',
@@ -664,7 +665,6 @@ export function getSubTitleDesign(
664
665
  },
665
666
  theme
666
667
  );
667
- baseClasses[2].mobile.shadow = 'md';
668
668
  }
669
669
  break;
670
670
  }
@@ -700,6 +700,7 @@ export function getSubTitleDesign(
700
700
  baseClasses[2].mobile.textALIGN = 'left';
701
701
  baseClasses[2].mobile.textWRAP = 'pretty';
702
702
  if (bordered) {
703
+ baseClasses[2].mobile.shadow = 'md';
703
704
  baseClasses[2].mobile.bgCOLOR = getColor(
704
705
  {
705
706
  light: useOdd ? 'brand-2' : 'white',
@@ -711,7 +712,6 @@ export function getSubTitleDesign(
711
712
  },
712
713
  theme
713
714
  );
714
- baseClasses[2].mobile.shadow = 'md';
715
715
  }
716
716
  break;
717
717
  }
@@ -748,6 +748,7 @@ export function getSubTitleDesign(
748
748
  baseClasses[2].mobile.textWRAP = 'pretty';
749
749
  if (bordered) {
750
750
  baseClasses[2].mobile.rounded = 'lg';
751
+ baseClasses[2].mobile.shadow = 'md';
751
752
  baseClasses[2].mobile.bgCOLOR = getColor(
752
753
  {
753
754
  light: useOdd ? 'brand-2' : 'white',
@@ -759,7 +760,6 @@ export function getSubTitleDesign(
759
760
  },
760
761
  theme
761
762
  );
762
- baseClasses[2].mobile.shadow = 'md';
763
763
  }
764
764
  }
765
765
  }
@@ -994,6 +994,7 @@ export const getIntroDesign = (
994
994
  parentClasses[2].mobile.textWRAP = 'balance';
995
995
  if (bordered) {
996
996
  parentClasses[2].mobile.rounded = 'lg';
997
+ parentClasses[2].mobile.shadow = 'lg';
997
998
  parentClasses[2].mobile.bgCOLOR = getColor(
998
999
  {
999
1000
  light: useOdd ? 'brand-2' : 'white',
@@ -1005,7 +1006,6 @@ export const getIntroDesign = (
1005
1006
  },
1006
1007
  theme
1007
1008
  );
1008
- parentClasses[2].mobile.shadow = 'lg';
1009
1009
  }
1010
1010
  break;
1011
1011
  }
@@ -1019,6 +1019,7 @@ export const getIntroDesign = (
1019
1019
  parentClasses[2].mobile.textWRAP = 'pretty';
1020
1020
  parentClasses[2].mobile.maxW = '3xl';
1021
1021
  if (bordered) {
1022
+ parentClasses[2].mobile.shadow = 'lg';
1022
1023
  parentClasses[2].mobile.bgCOLOR = getColor(
1023
1024
  {
1024
1025
  light: useOdd ? 'brand-2' : 'white',
@@ -1030,7 +1031,6 @@ export const getIntroDesign = (
1030
1031
  },
1031
1032
  theme
1032
1033
  );
1033
- parentClasses[2].mobile.shadow = 'lg';
1034
1034
  }
1035
1035
  break;
1036
1036
  }
@@ -1041,6 +1041,7 @@ export const getIntroDesign = (
1041
1041
  (parentClasses[2].mobile.textALIGN = 'left');
1042
1042
  parentClasses[2].mobile.textWRAP = 'pretty';
1043
1043
  if (bordered) {
1044
+ parentClasses[2].mobile.shadow = 'lg';
1044
1045
  parentClasses[2].mobile.bgCOLOR = getColor(
1045
1046
  {
1046
1047
  light: useOdd ? 'brand-2' : 'white',
@@ -1052,7 +1053,6 @@ export const getIntroDesign = (
1052
1053
  },
1053
1054
  theme
1054
1055
  );
1055
- parentClasses[2].mobile.shadow = 'lg';
1056
1056
  }
1057
1057
  }
1058
1058
  }
@@ -1101,6 +1101,7 @@ export const getImageHeroSectionDefault = (
1101
1101
  parentClasses[2].mobile.textALIGN = 'left';
1102
1102
  parentClasses[2].mobile.textWRAP = 'pretty';
1103
1103
  if (bordered) {
1104
+ parentClasses[2].mobile.shadow = 'lg';
1104
1105
  parentClasses[2].mobile.bgCOLOR = getColor(
1105
1106
  {
1106
1107
  light: useOdd ? 'brand-2' : 'white',
@@ -1112,7 +1113,6 @@ export const getImageHeroSectionDefault = (
1112
1113
  },
1113
1114
  theme
1114
1115
  );
1115
- parentClasses[2].mobile.shadow = 'lg';
1116
1116
  }
1117
1117
 
1118
1118
  return {
@@ -136,6 +136,7 @@ export function isValidTag(tagName: string): tagName is Tag {
136
136
  'yt',
137
137
  'bunny',
138
138
  'belief',
139
+ 'interactiveDisclosure',
139
140
  'identify',
140
141
  'toggle',
141
142
  'code',
@@ -0,0 +1,38 @@
1
+ // URL Helper: Strip category prefix from slug
2
+ // e.g., "people-bleako" -> "bleako"
3
+ export function getCleanSlug(categorySlug: string, fullSlug: string): string {
4
+ const prefix = `${categorySlug}-`;
5
+ return fullSlug.startsWith(prefix) ? fullSlug.slice(prefix.length) : fullSlug;
6
+ }
7
+
8
+ // Build proper URL for resource
9
+ // e.g., category="people", slug="people-bleako" -> "/people/bleako"
10
+ export function getResourceUrl(categorySlug: string, fullSlug: string): string {
11
+ const cleanSlug = getCleanSlug(categorySlug, fullSlug);
12
+ return `/${categorySlug}/${cleanSlug}`;
13
+ }
14
+
15
+ // Image Helper: Placeholder implementation
16
+ export function getResourceImage(
17
+ id: string,
18
+ slug: string,
19
+ category: string
20
+ ): string {
21
+ console.log(`please define getResourceImage`, id, slug, category);
22
+ return '/static.jpg';
23
+ }
24
+
25
+ export function getResourceDescription(
26
+ id: string,
27
+ slug: string,
28
+ category: string
29
+ ): string | null {
30
+ console.log(`please define getResourceDescription`, id, slug, category);
31
+ return null;
32
+ }
33
+
34
+ // Initialize search data - override in custom implementation
35
+ export function initSearch(): void {
36
+ // Default implementation does nothing
37
+ // Override this function in your custom implementation to load search data
38
+ }
@@ -328,8 +328,8 @@ export function useDropdownDirection(
328
328
  const spaceAbove = rect.top;
329
329
  const newOpenAbove = spaceBelow < 300 && spaceAbove > spaceBelow;
330
330
  const newMaxHeight = newOpenAbove
331
- ? Math.min(spaceAbove - 10, 300)
332
- : Math.min(spaceBelow - 10, 300);
331
+ ? Math.max(0, Math.min(spaceAbove - 20, 400))
332
+ : Math.max(0, Math.min(spaceBelow - 20, 400));
333
333
  setState({ openAbove: newOpenAbove, maxHeight: newMaxHeight });
334
334
  }
335
335
  }, [triggerRef]);
@@ -1,188 +1,116 @@
1
1
  import {
2
2
  settingsPanelOpenStore,
3
+ settingsPanelStore,
3
4
  headerPositionStore,
4
5
  setHeaderPosition,
5
6
  setMobileHeaderFaded,
6
7
  } from '@/stores/storykeep';
8
+ import { debounce } from '@/utils/helpers';
7
9
 
8
- // Track whether initial scroll adjustment has been made for settings panel
9
10
  let hasScrolledForSettingsPanel = false;
10
11
 
11
- /**
12
- * Sets up CSS custom properties for dynamic layout positioning
13
- */
14
- export function setupLayoutStyles(): void {
15
- // Calculate and set CSS custom properties for positioning
16
- const updateBottomOffset = () => {
17
- const mobileNavHeight = window.innerWidth < 801 ? 80 : 0;
18
- const padding = 16;
19
- const offset = `${mobileNavHeight + padding}px`;
20
-
21
- document.documentElement.style.setProperty(
22
- '--bottom-right-controls-bottom-offset',
23
- offset
24
- );
25
- };
12
+ // Replace your existing setupPaneObserver with this one.
13
+ function setupPaneObserver() {
14
+ let currentObserver: IntersectionObserver | null = null;
26
15
 
27
- // Set initial values
28
- updateBottomOffset();
16
+ settingsPanelStore.subscribe((signalValue) => {
17
+ if (currentObserver) {
18
+ currentObserver.disconnect();
19
+ currentObserver = null;
20
+ }
29
21
 
30
- // Update on resize
31
- window.addEventListener('resize', updateBottomOffset);
22
+ if (signalValue && signalValue.nodeId) {
23
+ setTimeout(() => {
24
+ const { nodeId } = signalValue;
25
+
26
+ const targetElement =
27
+ document.getElementById(`pane-${nodeId}`) ||
28
+ document.querySelector(`[data-node-id="${nodeId}"]`);
29
+
30
+ if (targetElement) {
31
+ currentObserver = new IntersectionObserver(
32
+ ([entry]) => {
33
+ const signal = settingsPanelStore.get();
34
+ const now = Date.now();
35
+ if (signal?.editLock && now - signal.editLock < 100) {
36
+ return;
37
+ }
38
+ if (!entry.isIntersecting) {
39
+ settingsPanelStore.set(null);
40
+ }
41
+ },
42
+ { threshold: 0 }
43
+ );
44
+ currentObserver.observe(targetElement);
45
+ }
46
+ }, 100);
47
+ }
48
+ });
32
49
  }
33
50
 
34
- /**
35
- * Sets up scroll observers for header positioning behavior
36
- */
37
51
  export function setupLayoutObservers(): void {
38
- const header = document.getElementById('storykeepHeader');
39
- const toolModeNav = document.getElementById('mainNav');
40
- const mainContent = document.getElementById('mainContent');
52
+ const storykeepHeader = document.getElementById('storykeepHeader');
41
53
  const settingsControls = document.getElementById('settingsControls');
42
54
  const standardHeader = document.querySelector('header');
43
55
 
44
- if (!header) return;
56
+ if (!storykeepHeader || !settingsControls || !standardHeader) return;
45
57
 
46
- let headerHeight = 0;
47
- const updateHeaderHeight = () => {
48
- if (standardHeader) {
49
- headerHeight = standardHeader.offsetHeight;
50
- }
58
+ let standardHeaderHeight = 0;
59
+ const updateStandardHeaderHeight = () => {
60
+ standardHeaderHeight = standardHeader.offsetHeight;
51
61
  };
52
62
 
53
- const updateSettingsMargin = () => {
54
- if (settingsControls && header) {
55
- const storyKeepHeaderHeight = header.offsetHeight;
56
- const viewportHeight = window.innerHeight;
57
- const bottomOffset = window.innerWidth < 801 ? 96 : 16; // Mobile nav + padding
58
-
59
- // Set top margin to avoid StoryKeep header overlap only
60
- settingsControls.style.marginTop = `${storyKeepHeaderHeight + 20}px`;
61
-
62
- // Set max height for inner scroll
63
- const maxHeight =
64
- viewportHeight - storyKeepHeaderHeight - bottomOffset - 20;
65
- settingsControls.style.maxHeight = `${maxHeight}px`;
66
- }
63
+ const updatePanelPosition = () => {
64
+ const headerRect = storykeepHeader.getBoundingClientRect();
65
+ const panelTop = headerRect.bottom;
66
+ settingsControls.style.top = `${panelTop}px`;
67
67
  };
68
68
 
69
69
  const handleScroll = () => {
70
70
  const scrollY = window.scrollY;
71
- const shouldBeSticky = scrollY > headerHeight;
72
-
73
- // Only update header position if it actually needs to change
71
+ const shouldBeSticky = scrollY > standardHeaderHeight;
74
72
  const currentPosition = headerPositionStore.get();
75
73
  const newPosition = shouldBeSticky ? 'sticky' : 'normal';
76
74
 
77
75
  if (currentPosition !== newPosition) {
78
76
  setHeaderPosition(newPosition);
79
-
80
- if (shouldBeSticky) {
81
- if (header) {
82
- const headerHeight = header.offsetHeight;
83
- // Add padding to body to prevent layout shift
84
- document.body.style.paddingTop = `${headerHeight}px`;
85
-
86
- header.style.position = 'fixed';
87
- header.style.top = '0';
88
- header.style.left = '0';
89
- header.style.right = '0';
90
- header.style.zIndex = '101';
91
- }
92
- } else {
93
- if (header) {
94
- // Remove padding when header is not fixed
95
- document.body.style.paddingTop = '';
96
-
97
- header.style.position = '';
98
- header.style.top = '';
99
- header.style.left = '';
100
- header.style.right = '';
101
- header.style.zIndex = '';
102
- }
103
- }
104
- }
105
-
106
- // Update tool mode nav position and main content margin
107
- if (toolModeNav && window.innerWidth >= 801) {
108
77
  if (shouldBeSticky) {
109
- // On desktop, make nav fixed when header is sticky
110
- toolModeNav.classList.remove('md:static');
111
- toolModeNav.classList.add('md:fixed');
112
- toolModeNav.style.top = '60px'; // Below fixed header
113
- toolModeNav.style.left = '0';
114
-
115
- // Add margin to main content when nav becomes fixed (nav no longer takes flex space)
116
- if (mainContent) {
117
- mainContent.classList.add('md:ml-16');
118
- }
78
+ document.body.style.paddingTop = `${storykeepHeader.offsetHeight}px`;
79
+ storykeepHeader.style.position = 'fixed';
80
+ storykeepHeader.style.top = '0';
119
81
  } else {
120
- // Normal static positioning when header is visible
121
- toolModeNav.classList.remove('md:fixed');
122
- toolModeNav.classList.add('md:static');
123
- toolModeNav.style.top = '';
124
- toolModeNav.style.left = '';
125
-
126
- // Remove margin from main content when nav is static (nav takes flex space naturally)
127
- if (mainContent) {
128
- mainContent.classList.remove('md:ml-16');
129
- }
82
+ document.body.style.paddingTop = '';
83
+ storykeepHeader.style.position = '';
84
+ storykeepHeader.style.top = '';
130
85
  }
131
86
  }
132
87
  };
133
88
 
134
- // Handle resize events
135
- const handleResize = () => {
136
- updateHeaderHeight();
137
- updateSettingsMargin();
138
-
139
- // Handle desktop/mobile breakpoint transitions
140
- const isMobile = window.innerWidth < 801;
141
-
142
- if (isMobile && toolModeNav && mainContent) {
143
- // Force reset to mobile layout
144
- toolModeNav.classList.remove('md:fixed', 'md:static');
145
- toolModeNav.style.top = '';
146
- toolModeNav.style.left = '';
147
-
148
- // Remove desktop margin
149
- mainContent.classList.remove('md:ml-16');
150
- }
151
-
152
- // Re-run scroll logic to handle desktop/mobile transitions
89
+ const debouncedUpdate = debounce(() => {
90
+ updateStandardHeaderHeight();
153
91
  handleScroll();
154
- };
92
+ updatePanelPosition();
93
+ }, 50);
155
94
 
156
- // Listen for settings panel state changes
157
95
  const handleSettingsPanelChange = () => {
158
- const isSettingsOpen = settingsPanelOpenStore.get();
159
-
160
- // Reset scroll flag when panel state changes
161
- if (!isSettingsOpen) {
96
+ if (!settingsPanelOpenStore.get()) {
162
97
  hasScrolledForSettingsPanel = false;
163
98
  }
164
99
  };
165
100
 
166
- // Set up event listeners
167
- window.addEventListener('scroll', handleScroll, { passive: true });
168
- window.addEventListener('resize', handleResize);
169
-
170
- // Subscribe to settings panel state changes
101
+ window.addEventListener('scroll', debouncedUpdate, { passive: true });
102
+ window.addEventListener('resize', debouncedUpdate);
171
103
  settingsPanelOpenStore.subscribe(handleSettingsPanelChange);
172
104
 
173
- // Initial setup
174
- updateHeaderHeight();
175
- updateSettingsMargin();
105
+ setupPaneObserver();
106
+
107
+ updateStandardHeaderHeight();
176
108
  handleScroll();
109
+ updatePanelPosition();
177
110
  }
178
111
 
179
- /**
180
- * Handle settings panel mobile behavior
181
- * This is called when the settings panel is toggled
182
- */
183
112
  export function handleSettingsPanelMobile(isOpen: boolean): void {
184
113
  const isMobile = window.innerWidth < 801;
185
-
186
114
  if (!isMobile) return;
187
115
 
188
116
  if (isOpen) {
@@ -190,19 +118,12 @@ export function handleSettingsPanelMobile(isOpen: boolean): void {
190
118
  const headerHeight = header?.offsetHeight || 0;
191
119
  const currentScrollY = window.scrollY;
192
120
 
193
- // Only scroll if we're near the top and haven't already scrolled
194
121
  if (currentScrollY <= headerHeight && !hasScrolledForSettingsPanel) {
195
- window.scrollTo({
196
- top: headerHeight + 10,
197
- behavior: 'smooth',
198
- });
122
+ window.scrollTo({ top: headerHeight + 10, behavior: 'smooth' });
199
123
  hasScrolledForSettingsPanel = true;
200
124
  }
201
-
202
- // Fade the header
203
125
  setMobileHeaderFaded(true);
204
126
  } else {
205
- // Unfade the header and reset scroll flag
206
127
  setMobileHeaderFaded(false);
207
128
  hasScrolledForSettingsPanel = false;
208
129
  }