astro-tractstack 2.0.0-rc.8 → 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 (141) hide show
  1. package/LICENSE +8 -97
  2. package/README.md +7 -5
  3. package/bin/create-tractstack.js +35 -11
  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_Content.tsx +6 -0
  88. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
  89. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +37 -33
  90. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +55 -7
  91. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +17 -2
  92. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
  93. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
  94. package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
  95. package/templates/src/components/tenant/RegistrationForm.tsx +1 -1
  96. package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
  97. package/templates/src/constants/shapes.ts +9 -0
  98. package/templates/src/constants.ts +2121 -16
  99. package/templates/src/hooks/useSearch.ts +228 -0
  100. package/templates/src/layouts/Layout.astro +213 -104
  101. package/templates/src/lib/storyData.ts +4 -1
  102. package/templates/src/pages/[...slug]/edit.astro +14 -14
  103. package/templates/src/pages/[...slug].astro +82 -21
  104. package/templates/src/pages/api/orphan-analysis.ts +0 -1
  105. package/templates/src/pages/api/tailwind.ts +23 -21
  106. package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
  107. package/templates/src/pages/context/[...contextSlug].astro +7 -2
  108. package/templates/src/pages/storykeep/advanced.astro +5 -4
  109. package/templates/src/pages/storykeep/branding.astro +5 -4
  110. package/templates/src/pages/storykeep/content.astro +5 -4
  111. package/templates/src/pages/storykeep/init.astro +40 -1
  112. package/templates/src/pages/storykeep/login.astro +1 -1
  113. package/templates/src/pages/storykeep.astro +5 -4
  114. package/templates/src/stores/nodes.ts +59 -88
  115. package/templates/src/stores/orphanAnalysis.ts +19 -21
  116. package/templates/src/stores/storykeep.ts +7 -0
  117. package/templates/src/types/compositorTypes.ts +6 -0
  118. package/templates/src/types/tractstack.ts +17 -0
  119. package/templates/src/utils/actions/lispLexer.ts +2 -2
  120. package/templates/src/utils/actions/preParse_Action.ts +3 -0
  121. package/templates/src/utils/api/beliefHelpers.ts +12 -36
  122. package/templates/src/utils/api/menuHelpers.ts +2 -2
  123. package/templates/src/utils/api.ts +26 -0
  124. package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
  125. package/templates/src/utils/compositor/allowInsert.ts +5 -3
  126. package/templates/src/utils/compositor/nodesHelper.ts +4 -0
  127. package/templates/src/utils/compositor/processMarkdown.ts +16 -2
  128. package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
  129. package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
  130. package/templates/src/utils/compositor/typeGuards.ts +1 -0
  131. package/templates/src/utils/customHelpers.ts +38 -0
  132. package/templates/src/utils/helpers.ts +2 -2
  133. package/templates/src/utils/layout.ts +65 -144
  134. package/utils/inject-files.ts +95 -18
  135. package/templates/src/client/analytics-events.js +0 -207
  136. package/templates/src/client/belief-events.js +0 -191
  137. package/templates/src/client/sse.js +0 -613
  138. package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
  139. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
  140. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
  141. package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
@@ -29,7 +29,15 @@ export async function injectTemplateFiles(
29
29
  dest: 'env.example',
30
30
  },
31
31
  {
32
- src: resolve('../templates/.gitignore'),
32
+ src: resolve('../templates/prettierrc'),
33
+ dest: '.prettierrc',
34
+ },
35
+ {
36
+ src: resolve('../templates/prettierignore'),
37
+ dest: '.prettierignore',
38
+ },
39
+ {
40
+ src: resolve('../templates/gitignore'),
33
41
  dest: '.gitignore',
34
42
  },
35
43
  {
@@ -222,6 +230,12 @@ export async function injectTemplateFiles(
222
230
  src: resolve('../templates/src/components/edit/widgets/BeliefWidget.tsx'),
223
231
  dest: 'src/components/edit/widgets/BeliefWidget.tsx',
224
232
  },
233
+ {
234
+ src: resolve(
235
+ '../templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx'
236
+ ),
237
+ dest: 'src/components/edit/widgets/InteractiveDisclosureWidget.tsx',
238
+ },
225
239
  {
226
240
  src: resolve(
227
241
  '../templates/src/components/edit/widgets/IdentifyAsWidget.tsx'
@@ -471,10 +485,6 @@ export async function injectTemplateFiles(
471
485
  src: resolve('../templates/src/components/edit/pane/PanePanel_path.tsx'),
472
486
  dest: 'src/components/edit/pane/PanePanel_path.tsx',
473
487
  },
474
- {
475
- src: resolve('../templates/src/components/edit/pane/PanePanel_slug.tsx'),
476
- dest: 'src/components/edit/pane/PanePanel_slug.tsx',
477
- },
478
488
  {
479
489
  src: resolve('../templates/src/components/edit/pane/PanePanel_title.tsx'),
480
490
  dest: 'src/components/edit/pane/PanePanel_title.tsx',
@@ -546,9 +556,9 @@ export async function injectTemplateFiles(
546
556
  },
547
557
  {
548
558
  src: resolve(
549
- '../templates/src/components/compositor/preview/FeaturedContentPreview.tsx'
559
+ '../templates/src/components/compositor/preview/FeaturedArticlePreview.tsx'
550
560
  ),
551
- dest: 'src/components/compositor/preview/FeaturedContentPreview.tsx',
561
+ dest: 'src/components/compositor/preview/FeaturedArticlePreview.tsx',
552
562
  },
553
563
 
554
564
  // Server side stores
@@ -902,7 +912,23 @@ export async function injectTemplateFiles(
902
912
  src: resolve('../templates/src/components/Fragment.astro'),
903
913
  dest: 'src/components/Fragment.astro',
904
914
  },
905
-
915
+ // Search Components
916
+ {
917
+ src: resolve('../templates/src/components/search/SearchWrapper.tsx'),
918
+ dest: 'src/components/search/SearchWrapper.tsx',
919
+ },
920
+ {
921
+ src: resolve('../templates/src/components/search/SearchModal.tsx'),
922
+ dest: 'src/components/search/SearchModal.tsx',
923
+ },
924
+ {
925
+ src: resolve('../templates/src/components/search/SearchResults.tsx'),
926
+ dest: 'src/components/search/SearchResults.tsx',
927
+ },
928
+ {
929
+ src: resolve('../templates/src/hooks/useSearch.ts'),
930
+ dest: 'src/hooks/useSearch.ts',
931
+ },
906
932
  // Profile Components
907
933
  {
908
934
  src: resolve('../templates/src/components/profile/ProfileConsent.tsx'),
@@ -982,6 +1008,12 @@ export async function injectTemplateFiles(
982
1008
  src: resolve('../templates/src/components/form/ActionBuilderField.tsx'),
983
1009
  dest: 'src/components/form/ActionBuilderField.tsx',
984
1010
  },
1011
+ {
1012
+ src: resolve(
1013
+ '../templates/src/components/form/ActionBuilderBeliefSelector.tsx'
1014
+ ),
1015
+ dest: 'src/components/form/ActionBuilderBeliefSelector.tsx',
1016
+ },
985
1017
  {
986
1018
  src: resolve('../templates/src/components/form/MagicPathBuilder.tsx'),
987
1019
  dest: 'src/components/form/MagicPathBuilder.tsx',
@@ -1038,6 +1070,12 @@ export async function injectTemplateFiles(
1038
1070
  },
1039
1071
 
1040
1072
  // StoryKeep Dashboard Components
1073
+ {
1074
+ src: resolve(
1075
+ '../templates/src/components/storykeep/StoryKeepBackdrop.astro'
1076
+ ),
1077
+ dest: 'src/components/storykeep/StoryKeepBackdrop.astro',
1078
+ },
1041
1079
  {
1042
1080
  src: resolve('../templates/src/components/storykeep/Dashboard.tsx'),
1043
1081
  dest: 'src/components/storykeep/Dashboard.tsx',
@@ -1212,17 +1250,21 @@ export async function injectTemplateFiles(
1212
1250
  src: resolve('../templates/src/components/codehooks/SankeyDiagram.tsx'),
1213
1251
  dest: 'src/components/codehooks/SankeyDiagram.tsx',
1214
1252
  },
1253
+ {
1254
+ src: resolve('../templates/src/components/codehooks/SearchWidget.tsx'),
1255
+ dest: 'src/components/codehooks/SearchWidget.tsx',
1256
+ },
1215
1257
  {
1216
1258
  src: resolve(
1217
- '../templates/src/components/codehooks/FeaturedContent.astro'
1259
+ '../templates/src/components/codehooks/FeaturedArticle.astro'
1218
1260
  ),
1219
- dest: 'src/components/codehooks/FeaturedContent.astro',
1261
+ dest: 'src/components/codehooks/FeaturedArticle.astro',
1220
1262
  },
1221
1263
  {
1222
1264
  src: resolve(
1223
- '../templates/src/components/codehooks/FeaturedContentSetup.tsx'
1265
+ '../templates/src/components/codehooks/FeaturedArticleSetup.tsx'
1224
1266
  ),
1225
- dest: 'src/components/codehooks/FeaturedContentSetup.tsx',
1267
+ dest: 'src/components/codehooks/FeaturedArticleSetup.tsx',
1226
1268
  },
1227
1269
  {
1228
1270
  src: resolve('../templates/src/components/codehooks/ListContent.astro'),
@@ -1234,6 +1276,18 @@ export async function injectTemplateFiles(
1234
1276
  ),
1235
1277
  dest: 'src/components/codehooks/ListContentSetup.tsx',
1236
1278
  },
1279
+ {
1280
+ src: resolve(
1281
+ '../templates/src/components/codehooks/ProductCardSetup.tsx'
1282
+ ),
1283
+ dest: 'src/components/codehooks/ProductCardSetup.tsx',
1284
+ },
1285
+ {
1286
+ src: resolve(
1287
+ '../templates/src/components/codehooks/ProductGridSetup.tsx'
1288
+ ),
1289
+ dest: 'src/components/codehooks/ProductGridSetup.tsx',
1290
+ },
1237
1291
  {
1238
1292
  src: resolve(
1239
1293
  '../templates/src/components/codehooks/BunnyVideoWrapper.astro'
@@ -1271,16 +1325,16 @@ export async function injectTemplateFiles(
1271
1325
 
1272
1326
  // Client Scripts
1273
1327
  {
1274
- src: resolve('../templates/src/client/sse.js'),
1275
- dest: 'public/client/sse.js',
1328
+ src: resolve('../templates/src/client/htmx.min.js'),
1329
+ dest: 'public/client/htmx.min.js',
1276
1330
  },
1277
1331
  {
1278
- src: resolve('../templates/src/client/belief-events.js'),
1279
- dest: 'public/client/belief-events.js',
1332
+ src: resolve('../templates/src/client/view.js'),
1333
+ dest: 'public/client/view.js',
1280
1334
  },
1281
1335
  {
1282
- src: resolve('../templates/src/client/analytics-events.js'),
1283
- dest: 'public/client/analytics-events.js',
1336
+ src: resolve('../templates/src/client/app.js'),
1337
+ dest: 'public/client/app.js',
1284
1338
  },
1285
1339
 
1286
1340
  // StoryKeep Editor (add new section)
@@ -2044,6 +2098,11 @@ export async function injectTemplateFiles(
2044
2098
  dest: 'src/custom/CustomRoutes.astro',
2045
2099
  protected: true,
2046
2100
  },
2101
+ {
2102
+ src: resolve('../templates/src/utils/customHelpers.ts'),
2103
+ dest: 'src/utils/customHelpers.ts',
2104
+ protected: true,
2105
+ },
2047
2106
 
2048
2107
  // Example Components (Conditional)
2049
2108
  ...(config?.includeExamples
@@ -2053,6 +2112,23 @@ export async function injectTemplateFiles(
2053
2112
  dest: 'src/custom/CustomHero.astro',
2054
2113
  protected: true,
2055
2114
  },
2115
+ {
2116
+ src: resolve('../templates/custom/with-examples/ProductGrid.astro'),
2117
+ dest: 'src/custom/ProductGrid.astro',
2118
+ protected: true,
2119
+ },
2120
+ {
2121
+ src: resolve(
2122
+ '../templates/custom/with-examples/ProductCardWrapper.astro'
2123
+ ),
2124
+ dest: 'src/custom/ProductCardWrapper.astro',
2125
+ protected: true,
2126
+ },
2127
+ {
2128
+ src: resolve('../templates/custom/with-examples/ProductCard.astro'),
2129
+ dest: 'src/custom/ProductCard.astro',
2130
+ protected: true,
2131
+ },
2056
2132
  {
2057
2133
  src: resolve(
2058
2134
  '../templates/custom/with-examples/pages/Collections.astro'
@@ -2084,6 +2160,7 @@ export async function injectTemplateFiles(
2084
2160
  file.dest.startsWith('src/components/codehooks/') ||
2085
2161
  file.dest.startsWith('src/components/widgets/') ||
2086
2162
  file.dest.startsWith('src/') ||
2163
+ file.dest.startsWith('public/client/') ||
2087
2164
  file.dest === '.gitignore');
2088
2165
 
2089
2166
  if (!existsSync(file.dest) || shouldOverwrite) {
@@ -1,207 +0,0 @@
1
- const THRESHOLD_GLOSSED = 7000; // 7 seconds in ms
2
- const THRESHOLD_READ = 42000; // 42 seconds in ms
3
- const VERBOSE = false;
4
-
5
- const paneViewTimes = new Map();
6
- let hasTrackedEntered =
7
- localStorage.getItem('tractstack_entered_tracked') === 'true';
8
- let currentStoryfragmentId = null;
9
- let isPageInitialized = false;
10
- let globalObserver = null;
11
-
12
- function waitForSessionReady() {
13
- return new Promise((resolve) => {
14
- if (window.TRACTSTACK_CONFIG?.session?.isReady) {
15
- resolve();
16
- } else {
17
- window.addEventListener('tractstack:session-ready', () => resolve(), {
18
- once: true,
19
- });
20
- }
21
- });
22
- }
23
-
24
- export async function initAnalyticsTracking(storyfragmentId) {
25
- if (isPageInitialized) return;
26
- isPageInitialized = true;
27
-
28
- if (VERBOSE)
29
- console.log('📊 ANALYTICS: Initializing tracking for page view.');
30
- currentStoryfragmentId = storyfragmentId || null;
31
- initPaneVisibilityTracking();
32
- await waitForSessionReady();
33
- trackEnteredEvent();
34
- trackPageViewedEvent();
35
- }
36
-
37
- function trackEnteredEvent() {
38
- if (!hasTrackedEntered && currentStoryfragmentId) {
39
- sendAnalyticsEvent({
40
- contentId: currentStoryfragmentId,
41
- contentType: 'StoryFragment',
42
- eventVerb: 'ENTERED',
43
- });
44
- hasTrackedEntered = true;
45
- localStorage.setItem('tractstack_entered_tracked', 'true');
46
- }
47
- }
48
-
49
- function trackPageViewedEvent() {
50
- if (currentStoryfragmentId) {
51
- // This event is now PURELY for analytics. It no longer triggers
52
- // a synchronization workaround on the backend.
53
- sendAnalyticsEvent({
54
- contentId: currentStoryfragmentId,
55
- contentType: 'StoryFragment',
56
- eventVerb: 'PAGEVIEWED',
57
- });
58
- }
59
- }
60
-
61
- function initPaneVisibilityTracking() {
62
- if (globalObserver) {
63
- globalObserver.disconnect();
64
- }
65
- globalObserver = new IntersectionObserver(
66
- (entries) => {
67
- entries.forEach((entry) => {
68
- const paneId = getPaneIdFromElement(entry.target);
69
- if (!paneId) return;
70
- if (entry.isIntersecting) {
71
- if (!paneViewTimes.has(paneId)) {
72
- paneViewTimes.set(paneId, Date.now());
73
- }
74
- } else {
75
- const startTime = paneViewTimes.get(paneId);
76
- if (startTime) {
77
- const duration = Date.now() - startTime;
78
- paneViewTimes.delete(paneId);
79
- let eventVerb = null;
80
- if (duration >= THRESHOLD_READ) eventVerb = 'READ';
81
- else if (duration >= THRESHOLD_GLOSSED) eventVerb = 'GLOSSED';
82
- if (eventVerb) {
83
- sendAnalyticsEvent({
84
- contentId: paneId,
85
- contentType: 'Pane',
86
- eventVerb,
87
- duration,
88
- });
89
- }
90
- }
91
- }
92
- });
93
- },
94
- { threshold: 0.1, rootMargin: '0px' }
95
- );
96
- observeAllPanes();
97
- }
98
-
99
- function observeAllPanes() {
100
- if (!globalObserver) return;
101
- const panes = document.querySelectorAll('[data-pane-id]');
102
- panes.forEach((pane) => globalObserver.observe(pane));
103
- }
104
-
105
- function setupLifecycleListeners() {
106
- if (window.ANALYTICS_INITIALIZED) return;
107
- window.ANALYTICS_INITIALIZED = true;
108
-
109
- if (VERBOSE)
110
- console.log(
111
- '📊 ANALYTICS: Setting up lifecycle listeners for the first time.'
112
- );
113
-
114
- window.addEventListener('beforeunload', () => {
115
- flushPendingPaneEvents();
116
- });
117
-
118
- document.addEventListener('astro:page-load', () => {
119
- isPageInitialized = false;
120
- flushPendingPaneEvents();
121
- if (window.TRACTSTACK_CONFIG?.storyfragmentId) {
122
- initAnalyticsTracking(window.TRACTSTACK_CONFIG.storyfragmentId);
123
- }
124
- });
125
-
126
- if (document.readyState === 'complete') {
127
- if (window.TRACTSTACK_CONFIG?.storyfragmentId) {
128
- initAnalyticsTracking(window.TRACTSTACK_CONFIG.storyfragmentId);
129
- }
130
- } else {
131
- document.addEventListener(
132
- 'DOMContentLoaded',
133
- () => {
134
- if (window.TRACTSTACK_CONFIG?.storyfragmentId) {
135
- initAnalyticsTracking(window.TRACTSTACK_CONFIG.storyfragmentId);
136
- }
137
- },
138
- { once: true }
139
- );
140
- }
141
- }
142
-
143
- function flushPendingPaneEvents() {
144
- if (paneViewTimes.size === 0) return;
145
- const flushTime = Date.now();
146
- paneViewTimes.forEach((startTime, paneId) => {
147
- const duration = flushTime - startTime;
148
- let eventVerb = null;
149
- if (duration >= THRESHOLD_READ) eventVerb = 'READ';
150
- else if (duration >= THRESHOLD_GLOSSED) eventVerb = 'GLOSSED';
151
- if (eventVerb) {
152
- sendAnalyticsEvent({
153
- contentId: paneId,
154
- contentType: 'Pane',
155
- eventVerb,
156
- duration,
157
- });
158
- }
159
- });
160
- paneViewTimes.clear();
161
- }
162
-
163
- async function sendAnalyticsEvent(event) {
164
- try {
165
- const config = window.TRACTSTACK_CONFIG;
166
- if (!config || !config.sessionId) return; // Use server-provided session ID
167
-
168
- const sessionId = config.sessionId;
169
- const formData = {
170
- beliefId: event.contentId,
171
- beliefType: event.contentType,
172
- beliefValue: event.eventVerb,
173
- paneId: '',
174
- };
175
-
176
- if (event.duration !== undefined) {
177
- formData.duration = event.duration.toString();
178
- }
179
-
180
- await fetch(`${config.backendUrl}/api/v1/state`, {
181
- method: 'POST',
182
- headers: {
183
- 'Content-Type': 'application/x-www-form-urlencoded',
184
- 'X-Tenant-ID': config.tenantId,
185
- 'X-TractStack-Session-ID': sessionId,
186
- 'X-StoryFragment-ID': config.storyfragmentId,
187
- },
188
- body: new URLSearchParams(formData),
189
- });
190
- } catch (error) {
191
- console.error('❌ API ERROR: Analytics event failed', error, event);
192
- }
193
- }
194
-
195
- function getPaneIdFromElement(element) {
196
- return element.getAttribute('data-pane-id');
197
- }
198
-
199
- window.initAnalyticsTracking = initAnalyticsTracking;
200
-
201
- setupLifecycleListeners();
202
-
203
- if (VERBOSE) {
204
- console.log(
205
- '📊 ANALYTICS: Analytics events module loaded and is persistent.'
206
- );
207
- }
@@ -1,191 +0,0 @@
1
- const VERBOSE = false;
2
-
3
- // This function now contains all essential one-time and recurring HTMX setup.
4
- function configureHtmx() {
5
- if (!window.htmx) return;
6
-
7
- if (!window.HTMX_CONFIGURED) {
8
- window.htmx.config.selfRequestsOnly = false;
9
- window.HTMX_CONFIGURED = true;
10
- }
11
-
12
- window.htmx.on(document.body, 'htmx:configRequest', function (evt) {
13
- const config = window.TRACTSTACK_CONFIG;
14
- if (!config || !config.sessionId) return; // Check for config and session ID
15
-
16
- if (evt.detail.path && evt.detail.path.startsWith('/api/v1/')) {
17
- evt.detail.path = config.backendUrl + evt.detail.path;
18
- }
19
-
20
- // MODIFIED: Use session ID from the global config object
21
- const sessionId = config.sessionId;
22
- evt.detail.headers['X-Tenant-ID'] = config.tenantId;
23
- evt.detail.headers['X-StoryFragment-ID'] = config.storyfragmentId;
24
- if (sessionId) {
25
- evt.detail.headers['X-TractStack-Session-ID'] = sessionId;
26
- }
27
- });
28
- }
29
-
30
- const pageBeliefs = {};
31
- let activeStoryfragmentId = null;
32
-
33
- function waitForSessionReady() {
34
- return new Promise((resolve) => {
35
- // This event is fired by sse.ts after the handshake is complete.
36
- if (window.TRACTSTACK_CONFIG?.session?.isReady) {
37
- resolve();
38
- } else {
39
- window.addEventListener('tractstack:session-ready', () => resolve(), {
40
- once: true,
41
- });
42
- }
43
- });
44
- }
45
-
46
- function initializeBeliefs() {
47
- if (window.BELIEF_INITIALIZED) {
48
- return;
49
- }
50
- window.BELIEF_INITIALIZED = true;
51
-
52
- if (VERBOSE)
53
- console.log(
54
- '🔧 BELIEF: First-time initialization of belief handlers and HTMX config.'
55
- );
56
-
57
- configureHtmx(); // Run config on initial load.
58
-
59
- document.addEventListener('change', function (event) {
60
- const target = event.target;
61
- if (
62
- target.matches &&
63
- (target.matches('select[data-belief-id]') ||
64
- target.matches('input[type="checkbox"][data-belief-id]'))
65
- ) {
66
- handleBeliefChange(target);
67
- }
68
- });
69
- }
70
-
71
- async function handleBeliefChange(element) {
72
- const beliefId = element.getAttribute('data-belief-id');
73
- const beliefType = element.getAttribute('data-belief-type');
74
- const paneId = element.getAttribute('data-pane-id');
75
-
76
- if (!beliefId || !beliefType) {
77
- if (VERBOSE)
78
- console.error('🔴 BELIEF: Missing required attributes on', element.id);
79
- return;
80
- }
81
-
82
- let beliefValue;
83
- if (element.type === 'checkbox') {
84
- beliefValue = element.checked ? 'BELIEVES_YES' : 'BELIEVES_NO';
85
- } else {
86
- beliefValue = element.value;
87
- }
88
-
89
- if (VERBOSE)
90
- console.log('🔄 BELIEF: Widget changed', {
91
- beliefId,
92
- beliefType,
93
- beliefValue,
94
- paneId,
95
- });
96
-
97
- trackBeliefState(beliefId, beliefValue);
98
-
99
- // Pass the current page's beliefs to the backend
100
- await sendBeliefUpdate({
101
- beliefId,
102
- beliefType,
103
- beliefValue,
104
- paneId: paneId || '',
105
- });
106
- }
107
-
108
- async function sendBeliefUpdate(data) {
109
- await waitForSessionReady();
110
-
111
- try {
112
- const config = window.TRACTSTACK_CONFIG;
113
- if (!config || !config.sessionId) return; // Check for config and session ID
114
-
115
- if (VERBOSE)
116
- console.log('🚨 FRONTEND DEBUG: Sending belief update with headers:', {
117
- 'X-StoryFragment-ID': config.storyfragmentId,
118
- beliefData: data,
119
- currentSSEContext: 'check sse.ts logs',
120
- });
121
-
122
- const sessionId = config.sessionId;
123
- const headers = {
124
- 'Content-Type': 'application/x-www-form-urlencoded',
125
- 'X-Tenant-ID': config.tenantId,
126
- 'X-StoryFragment-ID': config.storyfragmentId,
127
- 'X-TractStack-Session-ID': sessionId,
128
- };
129
-
130
- if (VERBOSE)
131
- console.log('📡 BELIEF: Session ready, sending update.', { data });
132
-
133
- const response = await fetch(`${config.backendUrl}/api/v1/state`, {
134
- method: 'POST',
135
- headers: headers,
136
- body: new URLSearchParams(data),
137
- });
138
-
139
- if (!response.ok) {
140
- throw new Error(`HTTP error! status: ${response.status}`);
141
- }
142
- } catch (error) {
143
- const errorMessage =
144
- error instanceof Error ? error.message : 'Unknown error';
145
- if (VERBOSE)
146
- console.error('🔴 BELIEF: Update failed', {
147
- error: errorMessage,
148
- beliefId: data.beliefId,
149
- });
150
- }
151
- }
152
-
153
- function trackBeliefState(beliefId, beliefValue) {
154
- if (!activeStoryfragmentId) return;
155
-
156
- if (!pageBeliefs[activeStoryfragmentId]) {
157
- pageBeliefs[activeStoryfragmentId] = {};
158
- }
159
- pageBeliefs[activeStoryfragmentId][beliefId] = beliefValue;
160
- if (VERBOSE)
161
- console.log(`📝 BELIEF: Tracked state for page ${activeStoryfragmentId}`, {
162
- ...pageBeliefs[activeStoryfragmentId],
163
- });
164
- }
165
-
166
- function setActiveStoryFragment() {
167
- if (window.TRACTSTACK_CONFIG?.storyfragmentId) {
168
- activeStoryfragmentId = window.TRACTSTACK_CONFIG.storyfragmentId;
169
- if (VERBOSE)
170
- console.log(
171
- `📖 BELIEF: Active story fragment set to ${activeStoryfragmentId}`
172
- );
173
- // Ensure a state object exists for the newly active page
174
- if (!pageBeliefs[activeStoryfragmentId]) {
175
- pageBeliefs[activeStoryfragmentId] = {};
176
- }
177
- }
178
- }
179
-
180
- initializeBeliefs();
181
-
182
- document.addEventListener('astro:page-load', () => {
183
- configureHtmx();
184
- setActiveStoryFragment();
185
- });
186
-
187
- // Also set the active story on the very first page load.
188
- document.addEventListener('DOMContentLoaded', setActiveStoryFragment);
189
-
190
- if (VERBOSE)
191
- console.log('🔧 BELIEF: Belief events module loaded and is persistent.');