astro-tractstack 2.1.3 → 2.2.1

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 (128) hide show
  1. package/README.md +54 -266
  2. package/bin/create-tractstack.js +9 -6
  3. package/dist/index.js +109 -71
  4. package/package.json +4 -2
  5. package/templates/css/custom.css +5 -0
  6. package/templates/icons/code.svg +18 -0
  7. package/templates/icons/li.svg +4 -0
  8. package/templates/icons/link.svg +22 -0
  9. package/templates/icons/p.svg +3 -0
  10. package/templates/src/client/app.js +80 -1
  11. package/templates/src/components/Footer.astro +1 -1
  12. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +6 -6
  13. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +3 -3
  14. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
  15. package/templates/src/components/codehooks/ListContentSetup.tsx +2 -2
  16. package/templates/src/components/codehooks/ProductCardSetup.tsx +1 -1
  17. package/templates/src/components/codehooks/ProductGridSetup.tsx +2 -2
  18. package/templates/src/components/codehooks/SandboxRegisterForm.tsx +3 -3
  19. package/templates/src/components/compositor/Compositor.tsx +25 -9
  20. package/templates/src/components/compositor/Node.tsx +168 -496
  21. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +1 -0
  22. package/templates/src/components/compositor/elements/SignUp.tsx +1 -1
  23. package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +2 -0
  24. package/templates/src/components/compositor/nodes/CreativePane.tsx +262 -0
  25. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +4 -6
  26. package/templates/src/components/compositor/nodes/GridLayout.tsx +4 -2
  27. package/templates/src/components/compositor/nodes/Markdown.tsx +18 -3
  28. package/templates/src/components/compositor/nodes/Pane.tsx +11 -5
  29. package/templates/src/components/compositor/nodes/RenderChildren.tsx +1 -1
  30. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +5 -5
  31. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +90 -42
  32. package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +2 -0
  33. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +27 -1
  34. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +10 -8
  35. package/templates/src/components/compositor/tools/NodeOverlay.tsx +224 -0
  36. package/templates/src/components/compositor/tools/PaneOverlay.tsx +122 -0
  37. package/templates/src/components/edit/Header.tsx +68 -9
  38. package/templates/src/components/edit/PanelSwitch.tsx +42 -4
  39. package/templates/src/components/edit/SettingsPanel.tsx +2 -3
  40. package/templates/src/components/edit/ToolMode.tsx +1 -31
  41. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +2 -2
  42. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +1 -1
  43. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +193 -659
  44. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +15 -82
  45. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +95 -45
  46. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +137 -49
  47. package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -1
  48. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +375 -0
  49. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +1 -23
  50. package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +327 -0
  51. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +267 -0
  52. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +371 -0
  53. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +201 -76
  54. package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +141 -0
  55. package/templates/src/components/edit/panels/CreativeImagePanel.tsx +435 -0
  56. package/templates/src/components/edit/panels/CreativeLinkPanel.tsx +110 -0
  57. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +1 -1
  58. package/templates/src/components/edit/panels/StyleParentPanel.tsx +118 -126
  59. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +3 -2
  60. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +1 -0
  61. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +3 -1
  62. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +3 -1
  63. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +1 -1
  64. package/templates/src/components/edit/state/SaveModal.tsx +19 -787
  65. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +2 -2
  66. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +1 -1
  67. package/templates/src/components/edit/widgets/BunnyWidget.tsx +5 -5
  68. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +1 -1
  69. package/templates/src/components/edit/widgets/SignupWidget.tsx +1 -1
  70. package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +1 -1
  71. package/templates/src/components/fields/ArtpackImage.tsx +11 -3
  72. package/templates/src/components/fields/BackgroundImage.tsx +8 -0
  73. package/templates/src/components/fields/BackgroundImageWrapper.tsx +15 -9
  74. package/templates/src/components/fields/ImageUpload.tsx +6 -0
  75. package/templates/src/components/form/ActionBuilderField.tsx +15 -5
  76. package/templates/src/components/form/ActionBuilderSlugSelector.tsx +1 -1
  77. package/templates/src/components/form/ColorPicker.tsx +1 -1
  78. package/templates/src/components/form/EnumSelect.tsx +1 -1
  79. package/templates/src/components/form/NumberInput.tsx +1 -1
  80. package/templates/src/components/form/StringArrayInput.tsx +1 -1
  81. package/templates/src/components/form/StringInput.tsx +1 -1
  82. package/templates/src/components/form/UnsavedChangesBar.tsx +1 -1
  83. package/templates/src/components/form/advanced/APIConfigSection.tsx +2 -2
  84. package/templates/src/components/form/advanced/AuthConfigSection.tsx +2 -2
  85. package/templates/src/components/profile/ProfileCreate.tsx +1 -1
  86. package/templates/src/components/profile/ProfileEdit.tsx +1 -1
  87. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +2 -2
  88. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +1 -1
  89. package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +2 -2
  90. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +1 -1
  91. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +6 -6
  92. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +1 -1
  93. package/templates/src/components/storykeep/controls/content/PaneTable.tsx +358 -0
  94. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -1
  95. package/templates/src/constants/prompts.json +18 -10
  96. package/templates/src/constants.ts +3 -0
  97. package/templates/src/hooks/usePaneFragments.ts +60 -0
  98. package/templates/src/lib/session.ts +71 -16
  99. package/templates/src/pages/[...slug].astro +4 -46
  100. package/templates/src/pages/api/css.ts +149 -0
  101. package/templates/src/pages/maint.astro +1 -1
  102. package/templates/src/pages/storykeep/login.astro +2 -2
  103. package/templates/src/stores/nodes.ts +162 -49
  104. package/templates/src/stores/orphanAnalysis.ts +6 -30
  105. package/templates/src/stores/previews.ts +7 -0
  106. package/templates/src/stores/storykeep.ts +0 -8
  107. package/templates/src/types/compositorTypes.ts +53 -10
  108. package/templates/src/utils/compositor/aiGeneration.ts +93 -0
  109. package/templates/src/utils/compositor/allowInsert.ts +2 -0
  110. package/templates/src/utils/compositor/htmlAst.ts +704 -0
  111. package/templates/src/utils/compositor/nodesHelper.ts +281 -102
  112. package/templates/src/utils/compositor/savePipeline.ts +893 -0
  113. package/templates/src/utils/etl/index.ts +3 -0
  114. package/templates/src/utils/etl/transformer.ts +10 -0
  115. package/templates/src/utils/helpers.ts +101 -0
  116. package/utils/inject-files.ts +100 -62
  117. package/templates/icons/text.svg +0 -6
  118. package/templates/src/components/compositor/NodeWithGuid.tsx +0 -69
  119. package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +0 -33
  120. package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +0 -56
  121. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +0 -269
  122. package/templates/src/components/compositor/nodes/Pane_eraser.tsx +0 -186
  123. package/templates/src/components/compositor/nodes/Pane_layout.tsx +0 -79
  124. package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +0 -26
  125. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +0 -61
  126. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +0 -120
  127. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +0 -62
  128. package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +0 -26
@@ -1,13 +1,22 @@
1
+ const VERBOSE = false;
2
+
1
3
  /**
2
4
  * Calls the backend's /api/v1/auth/visit endpoint to create a new session.
3
5
  * This function is decoupled from Astro-specific objects.
4
6
  * @param tenantId The tenant ID to use for the backend request.
5
- * @returns A promise that resolves to the new session ID from the backend.
7
+ * @returns A promise that resolves to the new session object from the backend.
6
8
  */
7
- export async function createBackendSession(tenantId: string): Promise<string> {
9
+ export async function createBackendSession(
10
+ tenantId: string
11
+ ): Promise<{ sessionId: string; fingerprintId?: string }> {
8
12
  const goBackend =
9
13
  import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
10
14
 
15
+ if (VERBOSE)
16
+ console.log(
17
+ `[session.ts] createBackendSession called for tenant: ${tenantId}`
18
+ );
19
+
11
20
  try {
12
21
  const response = await fetch(`${goBackend}/api/v1/auth/visit`, {
13
22
  method: 'POST',
@@ -28,18 +37,20 @@ export async function createBackendSession(tenantId: string): Promise<string> {
28
37
  }
29
38
 
30
39
  const result = await response.json();
40
+ if (VERBOSE)
41
+ console.log(`[session.ts] createBackendSession success:`, result);
42
+
31
43
  if (!result.sessionId) {
32
44
  console.error('Backend did not return a sessionId on warming', result);
33
45
  throw new Error('Backend did not return a sessionId');
34
46
  }
35
- return result.sessionId;
47
+ return {
48
+ sessionId: result.sessionId,
49
+ fingerprintId: result.fingerprint,
50
+ };
36
51
  } catch (error) {
37
- console.warn(
38
- 'Backend session creation failed, using client-side SSR fallback for session ID'
39
- );
40
- return `ssr-fallback-${Date.now()}-${Math.random()
41
- .toString(36)
42
- .substring(2, 11)}`;
52
+ console.error('Failed to initialize backend session:', error);
53
+ throw error;
43
54
  }
44
55
  }
45
56
 
@@ -58,10 +69,26 @@ export async function getOrSetSessionId(
58
69
  ): Promise<string> {
59
70
  // Check if we already have a session ID in the cookie
60
71
  let sessionId = astro.cookies.get('tractstack_session_id')?.value;
72
+ const fingerprintId = astro.cookies.get('tractstack_fingerprint')?.value;
73
+
74
+ if (VERBOSE)
75
+ console.log(`[session.ts] getOrSetSessionId - Cookies Found:`, {
76
+ sessionId,
77
+ fingerprintId,
78
+ });
61
79
 
62
80
  if (sessionId) {
63
81
  // Validate session exists in backend before using it
64
- const isValid = await validateSessionWithBackend(sessionId, tenantId);
82
+ // We pass fingerprintId to allow the backend to restore the session from DB if RAM is empty
83
+ const isValid = await validateSessionWithBackend(
84
+ sessionId,
85
+ tenantId,
86
+ fingerprintId
87
+ );
88
+
89
+ if (VERBOSE)
90
+ console.log(`[session.ts] Session Validation Result:`, isValid);
91
+
65
92
  if (!isValid) {
66
93
  console.warn(`Session ${sessionId} invalid, creating new session`);
67
94
  sessionId = ''; // Force new session creation
@@ -69,8 +96,12 @@ export async function getOrSetSessionId(
69
96
  }
70
97
 
71
98
  if (!sessionId) {
99
+ if (VERBOSE)
100
+ console.log(`[session.ts] No valid session found. Creating new one...`);
101
+
72
102
  // Call backend to generate collision-free session ID AND warm session
73
- sessionId = await createBackendSession(tenantId);
103
+ const sessionData = await createBackendSession(tenantId);
104
+ sessionId = sessionData.sessionId;
74
105
 
75
106
  // Set cookie with backend-provided session ID
76
107
  astro.cookies.set('tractstack_session_id', sessionId, {
@@ -81,9 +112,20 @@ export async function getOrSetSessionId(
81
112
  maxAge: 60 * 60 * 24, // 24 hours
82
113
  });
83
114
 
84
- // console.log(`Created new session: ${sessionId}`);
85
- //} else {
86
- // console.log(`Using existing session: ${sessionId}`);
115
+ if (sessionData.fingerprintId) {
116
+ astro.cookies.set('tractstack_fingerprint', sessionData.fingerprintId, {
117
+ httpOnly: false,
118
+ secure: import.meta.env.PROD,
119
+ sameSite: 'lax',
120
+ path: '/',
121
+ maxAge: 60 * 60 * 24 * 365, // 1 year persistence
122
+ });
123
+ }
124
+ if (VERBOSE)
125
+ console.log(`[session.ts] New Cookies Set:`, {
126
+ sessionId,
127
+ fingerprintId: sessionData.fingerprintId,
128
+ });
87
129
  }
88
130
 
89
131
  return sessionId;
@@ -95,13 +137,19 @@ export async function getOrSetSessionId(
95
137
  */
96
138
  async function validateSessionWithBackend(
97
139
  sessionId: string,
98
- tenantId: string
140
+ tenantId: string,
141
+ fingerprintId?: string
99
142
  ): Promise<boolean> {
100
143
  const goBackend =
101
144
  import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
102
145
 
146
+ if (VERBOSE)
147
+ console.log(`[session.ts] Validating session with backend...`, {
148
+ sessionId,
149
+ fingerprintId,
150
+ });
151
+
103
152
  try {
104
- // Use a lightweight endpoint to check if session exists
105
153
  const response = await fetch(`${goBackend}/api/v1/auth/visit`, {
106
154
  method: 'POST',
107
155
  headers: {
@@ -110,10 +158,16 @@ async function validateSessionWithBackend(
110
158
  },
111
159
  body: JSON.stringify({
112
160
  sessionId: sessionId,
161
+ fingerprintId: fingerprintId, // Explicitly pass for restoration (fixes SSR Cookie Gap)
113
162
  }),
114
163
  });
115
164
 
116
165
  if (!response.ok) {
166
+ if (VERBOSE)
167
+ console.log(
168
+ `[session.ts] Validation returned non-200 status:`,
169
+ response.status
170
+ );
117
171
  return false;
118
172
  }
119
173
 
@@ -121,6 +175,7 @@ async function validateSessionWithBackend(
121
175
  return result.success === true;
122
176
  } catch (error) {
123
177
  console.warn('Session validation failed:', error);
178
+ if (VERBOSE) console.log(`[session.ts] Validation network error:`, error);
124
179
  return false;
125
180
  }
126
181
  }
@@ -2,7 +2,6 @@
2
2
  import Layout from '@/layouts/Layout.astro';
3
3
  import CodeHook from '@/custom/CodeHook.astro';
4
4
  import { getBrandConfig } from '@/utils/api/brandConfig';
5
- import { handleFailedResponse } from '@/utils/backend';
6
5
  import { getFullContentMap } from '@/stores/analytics';
7
6
  import { getOrSetSessionId } from '@/lib/session';
8
7
  import { getStoryData } from '@/lib/storyData';
@@ -23,11 +22,10 @@ if (slug && (slug.startsWith('images/') || slug.startsWith('media/'))) {
23
22
  }
24
23
 
25
24
  const lookup = slug || '';
26
- const sessionId = await getOrSetSessionId(Astro, tenantId);
27
- const goBackend = import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
28
25
 
29
- let storyData;
26
+ let storyData, sessionId;
30
27
  try {
28
+ sessionId = await getOrSetSessionId(Astro, tenantId);
31
29
  storyData = await getStoryData(Astro, lookup, sessionId, tenantId);
32
30
  } catch (error) {
33
31
  if (error instanceof Response && error.status === 404) {
@@ -52,47 +50,7 @@ if (paneIds.length === 0) {
52
50
  return Astro.redirect('/storykeep');
53
51
  }
54
52
 
55
- let fragmentsData: Record<string, string> = {};
56
- try {
57
- const batchResponse = await fetch(`${goBackend}/api/v1/fragments/panes`, {
58
- method: 'POST',
59
- headers: {
60
- 'Content-Type': 'application/json',
61
- 'X-Tenant-ID': tenantId,
62
- 'X-StoryFragment-ID': storyfragmentId,
63
- 'X-TractStack-Session-ID': sessionId,
64
- },
65
- body: JSON.stringify({ paneIds }),
66
- });
67
-
68
- if (batchResponse.ok) {
69
- const batchData = await batchResponse.json();
70
- fragmentsData = batchData.fragments || {};
71
- if (batchData.errors) {
72
- Object.entries(batchData.errors).forEach(([paneId, error]) => {
73
- fragmentsData[paneId] =
74
- `<div class="error">Failed to load pane ${paneId}: ${error}</div>`;
75
- });
76
- }
77
- } else {
78
- const failedFragmentsResponse = await handleFailedResponse(
79
- batchResponse,
80
- goBackend,
81
- tenantId,
82
- Astro.url.pathname
83
- );
84
- if (failedFragmentsResponse) {
85
- return failedFragmentsResponse;
86
- }
87
- }
88
- } catch (error) {
89
- console.error('Error fetching fragments:', error);
90
- return Astro.redirect(
91
- `/maint?from=${encodeURIComponent(Astro.url.pathname)}`
92
- );
93
- }
94
-
95
- if (!fragmentsData) {
53
+ if (!storyData.fragments) {
96
54
  return Astro.redirect(
97
55
  `/maint?from=${encodeURIComponent(Astro.url.pathname)}`
98
56
  );
@@ -203,7 +161,7 @@ paneIds.forEach((paneId: string) => {
203
161
  </div>
204
162
  </div>
205
163
  ) : (
206
- <Fragment set:html={fragmentsData[paneId] || ''} />
164
+ <Fragment set:html={storyData.fragments[paneId] || ''} />
207
165
  )}
208
166
  </div>
209
167
  </div>
@@ -0,0 +1,149 @@
1
+ import type { APIRoute } from '@/types/astro';
2
+ import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss';
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import postcss, { type Rule, type AtRule } from 'postcss';
6
+ import selectorParser, {
7
+ type Root,
8
+ type ClassName,
9
+ } from 'postcss-selector-parser';
10
+
11
+ export const POST: APIRoute = async ({ request }) => {
12
+ try {
13
+ const { html, css: userCss } = await request.json();
14
+
15
+ const configPath = path.join(process.cwd(), 'tailwind.config.cjs');
16
+ const configContent = await fs.readFile(configPath, 'utf-8');
17
+
18
+ const tailwindConfig = new Function(
19
+ 'module',
20
+ 'exports',
21
+ configContent + '; return module.exports;'
22
+ )({ exports: {} }, {});
23
+
24
+ const tailwindCss = createTailwindcss({
25
+ tailwindConfig: {
26
+ ...tailwindConfig,
27
+ content: [{ raw: html, extension: 'html' }],
28
+ corePlugins: { preflight: false },
29
+ },
30
+ });
31
+
32
+ const generatedCss = await tailwindCss.generateStylesFromContent(
33
+ `@tailwind utilities;`,
34
+ [html]
35
+ );
36
+
37
+ const combinedCss = `${generatedCss}\n${userCss || ''}`;
38
+
39
+ const classMap: Record<string, string> = {};
40
+ const ruleMap: Record<string, string> = {};
41
+ const cssBuffer: { hash: string; body: string }[] = [];
42
+ const mediaBuckets: { minWidth: number; hash: string; body: string }[] = [];
43
+
44
+ const hashString = (str: string) => {
45
+ let hash = 5381,
46
+ i = str.length;
47
+ while (i) hash = (hash * 33) ^ str.charCodeAt(--i);
48
+ return (hash >>> 0).toString(16);
49
+ };
50
+
51
+ const hashPlugin = () => {
52
+ return {
53
+ postcssPlugin: 'hash-styles',
54
+ Root(root: any) {
55
+ root.walkRules((rule: Rule) => {
56
+ const ruleHash = `t8k-${hashString(rule.toString())}`;
57
+
58
+ const transformSelector = selectorParser((selectors: Root) => {
59
+ selectors.walkClasses((classNode: ClassName) => {
60
+ const rawClass = classNode.value;
61
+
62
+ if (!classMap[rawClass]) {
63
+ classMap[rawClass] = ruleHash;
64
+ } else if (!classMap[rawClass].includes(ruleHash)) {
65
+ classMap[rawClass] += ' ' + ruleHash;
66
+ }
67
+
68
+ classNode.value = ruleHash;
69
+ });
70
+ });
71
+
72
+ try {
73
+ rule.selector = transformSelector.processSync(rule.selector);
74
+ } catch (e) {
75
+ // Skip invalid selectors from user input
76
+ return;
77
+ }
78
+
79
+ ruleMap[ruleHash] = rule.toString();
80
+
81
+ let bucketWidth: number | undefined;
82
+ if (
83
+ rule.parent &&
84
+ rule.parent.type === 'atrule' &&
85
+ (rule.parent as AtRule).name === 'media'
86
+ ) {
87
+ const params = (rule.parent as AtRule).params;
88
+ const match = params.match(/min-width:\s*(\d+)px/);
89
+ if (match) bucketWidth = parseInt(match[1], 10);
90
+ }
91
+
92
+ const ruleEntry = {
93
+ hash: ruleHash,
94
+ body: rule.toString(),
95
+ };
96
+
97
+ if (bucketWidth !== undefined) {
98
+ mediaBuckets.push({ minWidth: bucketWidth, ...ruleEntry });
99
+ } else {
100
+ cssBuffer.push(ruleEntry);
101
+ }
102
+ });
103
+ },
104
+ };
105
+ };
106
+ hashPlugin.postcss = true;
107
+
108
+ const result = await postcss([hashPlugin()]).process(combinedCss, {
109
+ from: undefined,
110
+ });
111
+
112
+ const generateSnapshot = (targetBreakPoint: number) => {
113
+ const applicableMedia = mediaBuckets
114
+ .filter((b) => b.minWidth <= targetBreakPoint)
115
+ .sort((a, b) => a.minWidth - b.minWidth);
116
+
117
+ return [...cssBuffer, ...applicableMedia]
118
+ .map((r) => r.body)
119
+ .join('\n')
120
+ .replace(/(\d*\.?\d+)(vw|vh)/gi, (_, n) => `${n}%`);
121
+ };
122
+
123
+ const viewportCss = {
124
+ xs: generateSnapshot(0),
125
+ md: generateSnapshot(801),
126
+ xl: generateSnapshot(1367),
127
+ };
128
+
129
+ return new Response(
130
+ JSON.stringify({
131
+ success: true,
132
+ css: result.css,
133
+ viewportCss,
134
+ classMap,
135
+ ruleMap,
136
+ }),
137
+ { status: 200, headers: { 'Content-Type': 'application/json' } }
138
+ );
139
+ } catch (error) {
140
+ console.error('CSS Compilation Error:', error);
141
+ return new Response(
142
+ JSON.stringify({
143
+ success: false,
144
+ error: error instanceof Error ? error.message : 'Unknown error',
145
+ }),
146
+ { status: 500, headers: { 'Content-Type': 'application/json' } }
147
+ );
148
+ }
149
+ };
@@ -27,7 +27,7 @@ const wordmark = getAssetPath(brandConfig?.WORDMARK, '/brand/wordmark.svg');
27
27
  <link rel="stylesheet" href="/styles/storykeep.css" />
28
28
  </head>
29
29
  <body class="h-full">
30
- <div class="sm:px-6 lg:px-8 flex min-h-full flex-col justify-center py-12">
30
+ <div class="flex min-h-full flex-col justify-center py-12 md:px-6 xl:px-8">
31
31
  <div class="mx-auto pb-6">
32
32
  <div class="flex flex-col items-center justify-center gap-4">
33
33
  <div class="h-16 w-auto">
@@ -48,7 +48,7 @@ const mainStylesUrl = isDev
48
48
  <link rel="stylesheet" href={mainStylesUrl} />
49
49
  </head>
50
50
  <body class="h-full">
51
- <div class="sm:px-6 lg:px-8 flex min-h-full flex-col justify-center py-12">
51
+ <div class="flex min-h-full flex-col justify-center py-12 md:px-6 xl:px-8">
52
52
  <div class="mx-auto pb-6">
53
53
  <!-- Logo and Wordmark -->
54
54
  <div class="flex flex-col items-center justify-center gap-4">
@@ -123,7 +123,7 @@ const mainStylesUrl = isDev
123
123
  name="password"
124
124
  type="password"
125
125
  required
126
- class="sm:text-sm sm:leading-6 block w-full rounded-md border-0 px-3 py-1.5 text-mydarkgrey shadow-sm ring-1 ring-inset ring-mylightgrey placeholder:text-mylightgrey focus:ring-2 focus:ring-inset focus:ring-myorange"
126
+ class="block w-full rounded-md border-0 px-3 py-1.5 text-mydarkgrey shadow-sm ring-1 ring-inset ring-mylightgrey placeholder:text-mylightgrey focus:ring-2 focus:ring-inset focus:ring-myorange md:text-sm md:leading-6"
127
127
  />
128
128
  </div>
129
129
  </div>