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
@@ -1,613 +0,0 @@
1
- /**
2
- * SSE (Server-Sent Events) Client with Critical Debug Logging
3
- */
4
-
5
- const VERBOSE = false;
6
-
7
- // ============================================================================
8
- // GLOBAL STATE
9
- // ============================================================================
10
-
11
- let eventSource = null;
12
- let isHtmxReady = false;
13
- let currentStoryfragmentId = null;
14
- let reconnectAttempts = 0;
15
-
16
- const BASE_RECONNECT_DELAY = 1000;
17
- const MAX_RECONNECT_ATTEMPTS = 5;
18
-
19
- // ============================================================================
20
- // CRITICAL DEBUG LOGGING
21
- // ============================================================================
22
-
23
- function log(...args) {
24
- if (VERBOSE) console.log('🔌 SSE DEBUG:', ...args);
25
- }
26
-
27
- function logCritical(...args) {
28
- if (VERBOSE) console.error('🚨 SSE CRITICAL:', ...args);
29
- }
30
-
31
- function logStoryfragmentChange(action, oldId, newId, source) {
32
- logCritical(`STORYFRAGMENT ${action}:`, {
33
- action,
34
- oldId,
35
- newId,
36
- source,
37
- configValue: window.TRACTSTACK_CONFIG?.storyfragmentId,
38
- timestamp: new Date().toISOString(),
39
- changed: oldId !== newId,
40
- });
41
- }
42
-
43
- function logSSEEvent(eventType, data, willProcess, reason) {
44
- logCritical(`SSE EVENT ${eventType}:`, {
45
- eventType,
46
- data,
47
- currentContext: currentStoryfragmentId,
48
- configContext: window.TRACTSTACK_CONFIG?.storyfragmentId,
49
- willProcess,
50
- reason,
51
- timestamp: new Date().toISOString(),
52
- });
53
- }
54
-
55
- // ============================================================================
56
- // SSE HANDSHAKE
57
- // ============================================================================
58
-
59
- async function performSSEHandshake(sessionId) {
60
- log('=== STARTING SSE HANDSHAKE ===');
61
- log('📤 Session ID provided by server:', sessionId);
62
-
63
- const config = window.TRACTSTACK_CONFIG;
64
- if (!config) {
65
- throw new Error('❌ No TRACTSTACK_CONFIG available for handshake');
66
- }
67
-
68
- log('✅ Config found:', {
69
- backendUrl: config.backendUrl,
70
- tenantId: config.tenantId,
71
- storyfragmentId: config.storyfragmentId,
72
- });
73
-
74
- const existingSession = localStorage.getItem('tractstack_session_id');
75
- log(
76
- '🔍 Checking localStorage for existing session:',
77
- existingSession ? 'found' : 'not found'
78
- );
79
-
80
- if (!existingSession) {
81
- log('â„šī¸ No existing session in localStorage - fresh session');
82
- }
83
-
84
- const profileEmail = localStorage.getItem('tractstack_encrypted_email');
85
- const profileCode = localStorage.getItem('tractstack_encrypted_code');
86
-
87
- log('🔍 Checking localStorage for profile credentials:', {
88
- hasEmail: !!profileEmail,
89
- hasCode: !!profileCode,
90
- });
91
-
92
- if (!profileEmail && !profileCode) {
93
- log('â„šī¸ No profile unlock needed');
94
- }
95
-
96
- const consent = localStorage.getItem('tractstack_consent') || '0';
97
- log('📋 Consent status:', consent);
98
-
99
- const handshakePayload = {
100
- sessionId,
101
- storyfragmentId: config.storyfragmentId,
102
- consent,
103
- };
104
-
105
- if (profileEmail) handshakePayload.encryptedEmail = profileEmail;
106
- if (profileCode) handshakePayload.encryptedCode = profileCode;
107
-
108
- log('📤 Sending handshake payload:', handshakePayload);
109
-
110
- const handshakeUrl = `${config.backendUrl}/api/v1/auth/visit`;
111
- log('🌐 Making handshake request to:', handshakeUrl);
112
-
113
- try {
114
- const response = await fetch(handshakeUrl, {
115
- method: 'POST',
116
- headers: {
117
- 'Content-Type': 'application/json',
118
- 'X-Tenant-ID': config.tenantId,
119
- },
120
- body: JSON.stringify(handshakePayload),
121
- });
122
-
123
- log('📡 Handshake response status:', response.status);
124
- log(
125
- '📡 Handshake response headers:',
126
- Object.fromEntries(response.headers.entries())
127
- );
128
-
129
- if (!response.ok) {
130
- throw new Error(`Handshake failed with status ${response.status}`);
131
- }
132
-
133
- const result = await response.json();
134
- log('đŸ“Ĩ Handshake response received:', result);
135
-
136
- const restorationDetails = {
137
- restored: result.restored || false,
138
- affectedPanes: result.affectedPanes || null,
139
- hasProfile: result.hasProfile,
140
- success: response.ok,
141
- };
142
- log('🔍 Restoration details:', restorationDetails);
143
-
144
- log('💾 Updating localStorage with handshake results...');
145
- localStorage.setItem('tractstack_session_id', result.sessionId);
146
- log('💾 Set session ID:', result.sessionId);
147
- localStorage.setItem('tractstack_fingerprint', result.fingerprint);
148
- log('💾 Set fingerprint:', result.fingerprint);
149
- localStorage.setItem('tractstack_visit', result.visitId);
150
- log('💾 Set visit ID:', result.visitId);
151
- localStorage.setItem('tractstack_consent', result.consent);
152
- log('💾 Set consent:', result.consent);
153
-
154
- if (
155
- result.restored &&
156
- result.affectedPanes &&
157
- result.affectedPanes.length > 0
158
- ) {
159
- log('🔄 State restoration needed. Affected panes:', result.affectedPanes);
160
-
161
- if (window.htmx && isHtmxReady) {
162
- log('✅ HTMX ready, triggering immediate pane refreshes');
163
- result.affectedPanes.forEach((paneId) => {
164
- const element = document.querySelector(`[data-pane-id="${paneId}"]`);
165
- if (element) {
166
- log(`🔄 Restoring pane: ${paneId}`);
167
- window.htmx.trigger(element, 'refresh');
168
- } else {
169
- log(`âš ī¸ Restoration pane element not found: ${paneId}`);
170
- }
171
- });
172
- } else {
173
- log(
174
- 'âš ī¸ HTMX not ready during restoration, setting up delayed refresh'
175
- );
176
- document.addEventListener(
177
- 'astro:page-load',
178
- () => {
179
- if (window.htmx && result.affectedPanes) {
180
- log(
181
- '🔄 HTMX now ready after page load, triggering delayed pane refreshes'
182
- );
183
- result.affectedPanes.forEach((paneId) => {
184
- const element = document.querySelector(
185
- `[data-pane-id="${paneId}"]`
186
- );
187
- if (element) {
188
- log(`🔄 Delayed restoration for pane: ${paneId}`);
189
- window.htmx.trigger(element, 'refresh');
190
- } else {
191
- log(
192
- `âš ī¸ Delayed restoration pane element not found: ${paneId}`
193
- );
194
- }
195
- });
196
- }
197
- },
198
- { once: true }
199
- );
200
- }
201
- } else {
202
- log('â„šī¸ No state restoration needed');
203
- }
204
-
205
- if (window.TRACTSTACK_CONFIG) {
206
- window.TRACTSTACK_CONFIG.session = { isReady: true };
207
- log('✅ Marked session as ready in config');
208
- }
209
-
210
- log('đŸ“ĸ Dispatching tractstack:session-ready event');
211
- window.dispatchEvent(
212
- new CustomEvent('tractstack:session-ready', { detail: result })
213
- );
214
-
215
- log('✅ SSE HANDSHAKE COMPLETE');
216
- return result;
217
- } catch (error) {
218
- log('❌ Handshake request failed:', error);
219
- throw error;
220
- }
221
- }
222
-
223
- // ============================================================================
224
- // SSE CONNECTION
225
- // ============================================================================
226
-
227
- function initializeSSE(sessionId) {
228
- log('=== SSE CONNECTION INITIALIZATION ===');
229
-
230
- if (eventSource) {
231
- log('🔄 Closing existing SSE connection');
232
- eventSource.close();
233
- eventSource = null;
234
- }
235
-
236
- const config = window.TRACTSTACK_CONFIG;
237
- if (!config) {
238
- log('❌ Config not available for SSE connection');
239
- return;
240
- }
241
-
242
- const sseUrl = `${config.backendUrl}/api/v1/auth/sse?sessionId=${sessionId}&storyfragmentId=${config.storyfragmentId}&tenantId=${config.tenantId}`;
243
- log('🌐 Creating SSE connection to:', sseUrl);
244
-
245
- eventSource = new EventSource(sseUrl);
246
-
247
- eventSource.onopen = () => {
248
- log('✅ SSE Connection opened successfully');
249
- reconnectAttempts = 0;
250
- };
251
-
252
- eventSource.addEventListener('connected', (event) => {
253
- try {
254
- const data = JSON.parse(event.data);
255
- log('📡 SSE Connected event received:', data);
256
- } catch (error) {
257
- log('âš ī¸ Failed to parse connected event data:', event.data);
258
- }
259
- });
260
-
261
- eventSource.addEventListener('heartbeat', (event) => {
262
- try {
263
- const data = JSON.parse(event.data);
264
- log('💓 SSE Heartbeat:', data);
265
- } catch (error) {
266
- log('âš ī¸ Failed to parse heartbeat data:', event.data);
267
- }
268
- });
269
-
270
- eventSource.addEventListener('panes_updated', (event) => {
271
- logCritical('📨 PANES_UPDATED EVENT RECEIVED');
272
-
273
- if (!isHtmxReady) {
274
- logSSEEvent('panes_updated', null, false, 'HTMX not ready');
275
- log('âš ī¸ Panes update event arrived before HTMX was ready. Ignoring.');
276
- return;
277
- }
278
-
279
- log('📨 === PANES_UPDATED EVENT ===');
280
-
281
- try {
282
- const data = JSON.parse(event.data);
283
-
284
- logCritical('📨 PANES_UPDATED PARSED:', {
285
- data,
286
- currentStoryfragmentId,
287
- configStoryfragmentId: window.TRACTSTACK_CONFIG?.storyfragmentId,
288
- });
289
-
290
- log('📨 Full panes_updated payload:', data);
291
- log(`📖 Current page storyfragmentId: ${currentStoryfragmentId}`);
292
-
293
- // Handle batch format (new)
294
- if ('updates' in data) {
295
- log('✅ Processing batch updates format');
296
-
297
- for (const update of data.updates) {
298
- logCritical(`🔍 CHECKING UPDATE:`, {
299
- updateStoryfragment: update.storyfragmentId,
300
- currentStoryfragment: currentStoryfragmentId,
301
- configStoryfragment: window.TRACTSTACK_CONFIG?.storyfragmentId,
302
- willProcess: update.storyfragmentId === currentStoryfragmentId,
303
- });
304
-
305
- log(
306
- `🔍 Checking update for storyfragment: ${update.storyfragmentId}`
307
- );
308
-
309
- if (update.storyfragmentId === currentStoryfragmentId) {
310
- logSSEEvent('panes_updated', update, true, 'storyfragment match');
311
- log('✅ Storyfragment matches, processing update');
312
- processStoryfragmentUpdate(update);
313
- } else {
314
- logSSEEvent(
315
- 'panes_updated',
316
- update,
317
- false,
318
- 'storyfragment mismatch'
319
- );
320
- log('âš ī¸ Storyfragment mismatch - ignoring update:', {
321
- eventStoryfragment: update.storyfragmentId,
322
- currentStoryfragment: currentStoryfragmentId,
323
- updateDetails: update,
324
- });
325
- }
326
- }
327
- }
328
- // Handle legacy single-update format
329
- else if ('storyfragmentId' in data) {
330
- log('✅ Processing legacy single update format');
331
-
332
- logCritical(`🔍 LEGACY UPDATE CHECK:`, {
333
- dataStoryfragment: data.storyfragmentId,
334
- currentStoryfragment: currentStoryfragmentId,
335
- willProcess: data.storyfragmentId === currentStoryfragmentId,
336
- });
337
-
338
- if (data.storyfragmentId === currentStoryfragmentId) {
339
- logSSEEvent(
340
- 'panes_updated_legacy',
341
- data,
342
- true,
343
- 'storyfragment match'
344
- );
345
- log('✅ Storyfragment matches, processing updates');
346
- processStoryfragmentUpdate(data);
347
- } else {
348
- logSSEEvent(
349
- 'panes_updated_legacy',
350
- data,
351
- false,
352
- 'storyfragment mismatch'
353
- );
354
- log('âš ī¸ Storyfragment mismatch - ignoring update:', {
355
- eventStoryfragment: data.storyfragmentId,
356
- currentStoryfragment: currentStoryfragmentId,
357
- updateDetails: data,
358
- });
359
- }
360
- } else {
361
- logCritical('❌ UNKNOWN PANES_UPDATED FORMAT:', data);
362
- log('❌ Unknown panes_updated format:', data);
363
- }
364
- } catch (error) {
365
- logCritical('❌ ERROR PROCESSING PANES_UPDATED:', error);
366
- log('❌ Error processing panes_updated event:', error);
367
- }
368
-
369
- log('📨 === PANES_UPDATED EVENT COMPLETE ===');
370
- });
371
-
372
- eventSource.onerror = (error) => {
373
- log('❌ SSE Connection error:', error);
374
- handleReconnection();
375
- };
376
- }
377
-
378
- function processStoryfragmentUpdate(update) {
379
- logCritical('🔄 PROCESSING UPDATE:', {
380
- storyfragmentId: update.storyfragmentId,
381
- affectedPanes: update.affectedPanes,
382
- gotoPaneId: update.gotoPaneId,
383
- currentContext: currentStoryfragmentId,
384
- });
385
-
386
- log('🔄 === PROCESSING STORYFRAGMENT UPDATE ===');
387
- log('📖 Current storyfragment context:', currentStoryfragmentId);
388
- log('📤 Update storyfragment:', update.storyfragmentId);
389
-
390
- const uniquePaneIds = [...new Set(update.affectedPanes)];
391
- log('đŸŽ¯ Unique pane IDs to refresh:', uniquePaneIds);
392
-
393
- if (uniquePaneIds.length === 0) {
394
- log('âš ī¸ No panes to refresh in update');
395
- return;
396
- }
397
-
398
- let refreshedCount = 0;
399
- let errorCount = 0;
400
-
401
- uniquePaneIds.forEach((paneId) => {
402
- const element = document.querySelector(`[data-pane-id="${paneId}"]`);
403
-
404
- if (element && window.htmx) {
405
- log(`🔄 Triggering refresh for pane ${paneId} - Element found: ✅`);
406
- try {
407
- window.htmx.trigger(element, 'refresh');
408
- refreshedCount++;
409
- logCritical(`✅ PANE REFRESHED: ${paneId}`);
410
- } catch (error) {
411
- log(`❌ Failed to trigger HTMX refresh for pane ${paneId}:`, error);
412
- errorCount++;
413
- }
414
- } else {
415
- const issues = [];
416
- if (!element) issues.push('element not found');
417
- if (!window.htmx) issues.push('HTMX not available');
418
-
419
- log(`âš ī¸ Cannot refresh pane ${paneId}: ${issues.join(', ')}`, {
420
- elementFound: !!element,
421
- htmxAvailable: !!window.htmx,
422
- querySelector: `[data-pane-id="${paneId}"]`,
423
- domElementCount: document.querySelectorAll('[data-pane-id]').length,
424
- });
425
- errorCount++;
426
- }
427
- });
428
-
429
- log(`📊 Refresh summary: ${refreshedCount} successful, ${errorCount} failed`);
430
-
431
- if (update.gotoPaneId) {
432
- const targetElement = document.getElementById(`pane-${update.gotoPaneId}`);
433
- if (targetElement) {
434
- log(`📍 Scrolling to target pane: ${update.gotoPaneId}`);
435
- try {
436
- targetElement.scrollIntoView({ behavior: 'smooth' });
437
- log('✅ Scroll completed successfully');
438
- } catch (error) {
439
- log('❌ Scroll failed:', error);
440
- }
441
- } else {
442
- log(`âš ī¸ Target pane element not found: pane-${update.gotoPaneId}`, {
443
- expectedId: `pane-${update.gotoPaneId}`,
444
- availablePaneElements: Array.from(
445
- document.querySelectorAll('[id^="pane-"]')
446
- ).map((el) => el.id),
447
- });
448
- }
449
- }
450
-
451
- log('🔄 === UPDATE PROCESSING COMPLETE ===');
452
- }
453
-
454
- function handleReconnection() {
455
- if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
456
- log(
457
- `❌ Max reconnection attempts (${MAX_RECONNECT_ATTEMPTS}) reached. Giving up.`
458
- );
459
- return;
460
- }
461
-
462
- reconnectAttempts++;
463
- const delay = BASE_RECONNECT_DELAY * Math.pow(2, reconnectAttempts - 1);
464
- log(
465
- `🔄 Attempting reconnection ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS} in ${delay}ms`
466
- );
467
-
468
- setTimeout(() => {
469
- const sessionId = window.TRACTSTACK_CONFIG?.sessionId;
470
- if (sessionId) {
471
- log('🔄 Reconnecting with session ID:', sessionId);
472
- initializeSSE(sessionId);
473
- } else {
474
- log('❌ No session ID available for reconnection');
475
- }
476
- }, delay);
477
- }
478
-
479
- // ============================================================================
480
- // CONTEXT MANAGEMENT
481
- // ============================================================================
482
-
483
- function updateStoryfragmentContext(newStoryfragmentId, source) {
484
- const oldId = currentStoryfragmentId;
485
-
486
- if (currentStoryfragmentId !== newStoryfragmentId) {
487
- logStoryfragmentChange('UPDATE', oldId, newStoryfragmentId, source);
488
- log(
489
- `📖 Context updated [${source}]. Storyfragment changed from ${currentStoryfragmentId} to ${newStoryfragmentId}`
490
- );
491
- currentStoryfragmentId = newStoryfragmentId;
492
- } else {
493
- logStoryfragmentChange('CONFIRM', oldId, newStoryfragmentId, source);
494
- log(
495
- `📖 Context confirmed [${source}]. Still tracking storyfragmentId: ${currentStoryfragmentId}`
496
- );
497
- }
498
- }
499
-
500
- // ============================================================================
501
- // CONNECTION SETUP
502
- // ============================================================================
503
-
504
- function setupSSEConnection() {
505
- log('=== SSE CONNECTION SETUP ===');
506
- log('🚀 First-time SSE initialization');
507
-
508
- const sessionId = window.TRACTSTACK_CONFIG?.sessionId;
509
- log('🔍 Session ID from config:', sessionId);
510
-
511
- if (!sessionId) {
512
- // Changed: Graceful dormancy instead of error
513
- log('â„šī¸ No session ID available - SSE will remain dormant until needed');
514
- log('💤 SSE module loaded but inactive (no session context)');
515
- return;
516
- }
517
-
518
- log('✅ Session ID found, starting handshake process');
519
-
520
- performSSEHandshake(sessionId)
521
- .then(() => {
522
- log('🔌 Handshake complete, initializing SSE connection');
523
- initializeSSE(sessionId);
524
- })
525
- .catch((error) => {
526
- log('❌ Handshake failed, but initializing SSE anyway:', error);
527
- initializeSSE(sessionId);
528
- });
529
- }
530
-
531
- // ============================================================================
532
- // PAGE LIFECYCLE
533
- // ============================================================================
534
-
535
- document.addEventListener('DOMContentLoaded', () => {
536
- log('📄 === DOM CONTENT LOADED EVENT ===');
537
-
538
- if (window.htmx) {
539
- isHtmxReady = true;
540
- log('✅ HTMX is ready after DOM load');
541
- } else {
542
- log('âš ī¸ HTMX not available after DOM load');
543
- }
544
-
545
- if (window.TRACTSTACK_CONFIG?.storyfragmentId) {
546
- const initialId = window.TRACTSTACK_CONFIG.storyfragmentId;
547
- logStoryfragmentChange('INIT', null, initialId, 'DOM_LOADED');
548
- currentStoryfragmentId = initialId;
549
- log(
550
- `📖 Initial context set. Tracking storyfragmentId: ${currentStoryfragmentId}`
551
- );
552
- } else {
553
- log('âš ī¸ No storyfragmentId in config on DOM load');
554
- }
555
-
556
- log('📄 SSE module is persistent across page navigation');
557
- });
558
-
559
- document.addEventListener('astro:page-load', () => {
560
- log('📄 === ASTRO PAGE LOAD EVENT ===');
561
-
562
- if (window.htmx) {
563
- window.htmx.process(document.body);
564
- isHtmxReady = true;
565
- log('✅ Page loaded and processed. HTMX is now READY.');
566
- } else {
567
- log('âš ī¸ HTMX not available after page load');
568
- }
569
-
570
- // Check if SSE should wake up from dormancy
571
- const sessionId = window.TRACTSTACK_CONFIG?.sessionId;
572
- if (sessionId && !eventSource && window.SSE_INITIALIZED) {
573
- log('🌅 SSE was dormant but session ID now available - waking up');
574
- setupSSEConnection();
575
- }
576
-
577
- // Single context update with extensive logging
578
- setTimeout(() => {
579
- const configId = window.TRACTSTACK_CONFIG?.storyfragmentId;
580
- const currentId = currentStoryfragmentId;
581
-
582
- logCritical('ASTRO PAGE LOAD CONTEXT CHECK:', {
583
- configId,
584
- currentId,
585
- willUpdate: configId !== currentId,
586
- configExists: !!window.TRACTSTACK_CONFIG,
587
- configFull: window.TRACTSTACK_CONFIG,
588
- });
589
-
590
- if (configId) {
591
- updateStoryfragmentContext(configId, 'astro:page-load');
592
- } else {
593
- log('âš ī¸ No storyfragmentId in config after page load');
594
- logCritical('NO STORYFRAGMENT IN CONFIG:', window.TRACTSTACK_CONFIG);
595
- }
596
- }, 0);
597
- });
598
-
599
- // ============================================================================
600
- // INITIALIZATION - Use existing SSE_INITIALIZED flag
601
- // ============================================================================
602
-
603
- log('=== SSE MODULE INITIALIZATION ===');
604
-
605
- // Use the existing flag from astro.d.ts
606
- if (!window.SSE_INITIALIZED) {
607
- window.SSE_INITIALIZED = true;
608
- setupSSEConnection();
609
- } else {
610
- log('â„šī¸ SSE already initialized, skipping setup');
611
- }
612
-
613
- log('SSE events module loaded and is persistent.');