astro-tractstack 2.0.15 → 2.0.17
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.
- package/dist/index.js +33 -13
- package/package.json +1 -1
- package/templates/custom/with-examples/CodeHook.astro +4 -0
- package/templates/custom/with-examples/SandboxLauncher.tsx +67 -0
- package/templates/env.example +3 -0
- package/templates/src/components/codehooks/SandboxAuthWrapper.tsx +75 -0
- package/templates/src/components/codehooks/SandboxRegisterForm.tsx +202 -0
- package/templates/src/components/compositor/Compositor.tsx +2 -0
- package/templates/src/components/compositor/Node.tsx +6 -1
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +13 -11
- package/templates/src/components/compositor/nodes/Pane_layout.tsx +16 -14
- package/templates/src/components/edit/Header.tsx +8 -2
- package/templates/src/components/edit/PanelSwitch.tsx +4 -4
- package/templates/src/components/edit/pane/AddPanePanel.tsx +3 -0
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +61 -46
- package/templates/src/components/edit/pane/steps/DirectInjectStep.tsx +96 -0
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +10 -8
- package/templates/src/components/edit/state/SaveModal.tsx +41 -0
- package/templates/src/constants.ts +1 -0
- package/templates/src/pages/api/sandbox.ts +86 -0
- package/templates/src/pages/sandbox.astro +137 -0
- package/templates/src/types/nodeProps.ts +1 -0
- package/templates/src/utils/compositor/aiPaneParser.ts +8 -2
- package/templates/src/utils/profileStorage.ts +13 -0
- package/utils/inject-files.ts +33 -14
- package/templates/src/components/edit/pane/AiPaneGenerator.tsx +0 -512
- package/templates/src/components/edit/pane/AiPanePreview.tsx +0 -107
- package/templates/src/utils/aai/getTitleSlug.ts +0 -72
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { ulid } from 'ulid';
|
|
3
|
+
import Layout from '@/layouts/Layout.astro';
|
|
4
|
+
import Header from '@/components/Header.astro';
|
|
5
|
+
import { getFullContentMap } from '@/stores/analytics';
|
|
6
|
+
import { getBrandConfig } from '@/utils/api/brandConfig';
|
|
7
|
+
import { components as codeHookComponents } from '@/custom/CodeHook.astro';
|
|
8
|
+
import StoryKeepHeader from '@/components/edit/Header';
|
|
9
|
+
import StoryKeepToolBar from '@/components/edit/ToolBar';
|
|
10
|
+
import StoryKeepToolMode from '@/components/edit/ToolMode';
|
|
11
|
+
import SettingsPanel from '@/components/edit/SettingsPanel';
|
|
12
|
+
import { Compositor } from '@/components/compositor/Compositor';
|
|
13
|
+
import SandboxAuthWrapper from '@/components/codehooks/SandboxAuthWrapper.tsx';
|
|
14
|
+
import { preHealthCheck } from '@/utils/backend';
|
|
15
|
+
|
|
16
|
+
if (!import.meta.env.PRIVATE_SANDBOX_SECRET) {
|
|
17
|
+
return Astro.redirect('/');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tenantId =
|
|
21
|
+
Astro.locals.tenant?.id || import.meta.env.PUBLIC_TENANTID || 'default';
|
|
22
|
+
|
|
23
|
+
const healthCheckRedirect = await preHealthCheck(tenantId);
|
|
24
|
+
if (healthCheckRedirect !== undefined) {
|
|
25
|
+
return healthCheckRedirect;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const brandConfig = await getBrandConfig(tenantId);
|
|
29
|
+
|
|
30
|
+
const emptyStoryFragment = {
|
|
31
|
+
id: ulid(),
|
|
32
|
+
nodeType: 'StoryFragment' as const,
|
|
33
|
+
parentId: null,
|
|
34
|
+
title: 'Sandbox Page',
|
|
35
|
+
slug: 'sandbox-page',
|
|
36
|
+
paneIds: [],
|
|
37
|
+
isChanged: false,
|
|
38
|
+
created: new Date(),
|
|
39
|
+
changed: new Date(),
|
|
40
|
+
};
|
|
41
|
+
const loadData = {
|
|
42
|
+
storyfragmentNodes: [emptyStoryFragment],
|
|
43
|
+
};
|
|
44
|
+
const title = 'Sandbox - TractStack Editor';
|
|
45
|
+
const storyFragmentID = emptyStoryFragment.id;
|
|
46
|
+
|
|
47
|
+
const fullContentMap = await getFullContentMap(tenantId);
|
|
48
|
+
const urlParams: Record<string, string | boolean> = {};
|
|
49
|
+
for (const [key, value] of Astro.url.searchParams) {
|
|
50
|
+
urlParams[key] = value === '' ? true : value;
|
|
51
|
+
}
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<Layout
|
|
55
|
+
title={title}
|
|
56
|
+
slug="sandbox"
|
|
57
|
+
brandConfig={brandConfig}
|
|
58
|
+
storyfragmentId={storyFragmentID}
|
|
59
|
+
isStoryKeep={true}
|
|
60
|
+
isEditor={true}
|
|
61
|
+
>
|
|
62
|
+
<SandboxAuthWrapper client:load />
|
|
63
|
+
<Header
|
|
64
|
+
title={title}
|
|
65
|
+
slug="sandbox"
|
|
66
|
+
brandConfig={brandConfig}
|
|
67
|
+
isContext={false}
|
|
68
|
+
isStoryKeep={true}
|
|
69
|
+
isEditable={false}
|
|
70
|
+
menu={null}
|
|
71
|
+
/>
|
|
72
|
+
|
|
73
|
+
<section
|
|
74
|
+
id="storykeepHeader"
|
|
75
|
+
role="banner"
|
|
76
|
+
class="z-101 bg-mywhite left-0 right-0 drop-shadow transition-all duration-200"
|
|
77
|
+
>
|
|
78
|
+
<StoryKeepHeader
|
|
79
|
+
slug="sandbox"
|
|
80
|
+
isContext={false}
|
|
81
|
+
isSandboxMode={true}
|
|
82
|
+
client:only="react"
|
|
83
|
+
/>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<div class="flex min-h-screen">
|
|
87
|
+
<StoryKeepToolMode isContext={false} client:only="react" />
|
|
88
|
+
|
|
89
|
+
<main id="mainContent" class="relative flex-1 overflow-x-auto">
|
|
90
|
+
<div class="bg-myblue/20 bg-mylightgrey h-full p-1.5">
|
|
91
|
+
<div
|
|
92
|
+
class="h-fit min-h-screen pb-96"
|
|
93
|
+
style={{
|
|
94
|
+
backgroundImage:
|
|
95
|
+
'repeating-linear-gradient(135deg, transparent, transparent 10px, rgba(0,0,0,0.05) 10px, rgba(0,0,0,0.05) 20px)',
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<Compositor
|
|
99
|
+
id={storyFragmentID}
|
|
100
|
+
nodes={loadData}
|
|
101
|
+
config={brandConfig}
|
|
102
|
+
fullContentMap={fullContentMap}
|
|
103
|
+
fullCanonicalURL="/sandbox"
|
|
104
|
+
urlParams={urlParams}
|
|
105
|
+
availableCodeHooks={Object.keys(codeHookComponents)}
|
|
106
|
+
isSandboxMode={true}
|
|
107
|
+
client:only="react"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</main>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<aside
|
|
115
|
+
id="settingsControls"
|
|
116
|
+
class="z-101 pointer-events-none fixed bottom-16 right-2 flex flex-col items-end gap-2 md:bottom-2"
|
|
117
|
+
>
|
|
118
|
+
<div class="pointer-events-none flex-grow"></div>
|
|
119
|
+
|
|
120
|
+
<div class="pointer-events-auto flex-shrink-0">
|
|
121
|
+
<StoryKeepToolBar client:only="react" />
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="pointer-events-auto max-h-full">
|
|
125
|
+
<SettingsPanel
|
|
126
|
+
config={brandConfig}
|
|
127
|
+
availableCodeHooks={Object.keys(codeHookComponents)}
|
|
128
|
+
client:only="react"
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</aside>
|
|
132
|
+
</Layout>
|
|
133
|
+
|
|
134
|
+
<script>
|
|
135
|
+
import { setupLayoutObservers } from '@/utils/layout';
|
|
136
|
+
document.addEventListener('astro:page-load', setupLayoutObservers);
|
|
137
|
+
</script>
|
|
@@ -60,6 +60,8 @@ let BUTTON_CLASS_LOOKUP: Map<string, { key: string; value: string }> | null =
|
|
|
60
60
|
null;
|
|
61
61
|
|
|
62
62
|
const ALLOWED_TAGS = new Set([
|
|
63
|
+
'ul',
|
|
64
|
+
'li',
|
|
63
65
|
'h2',
|
|
64
66
|
'h3',
|
|
65
67
|
'h4',
|
|
@@ -69,6 +71,7 @@ const ALLOWED_TAGS = new Set([
|
|
|
69
71
|
'em',
|
|
70
72
|
'strong',
|
|
71
73
|
'button',
|
|
74
|
+
'a',
|
|
72
75
|
]);
|
|
73
76
|
|
|
74
77
|
function buildKeyNormalizationLookup(): Map<string, string> {
|
|
@@ -309,7 +312,10 @@ function walkDom(
|
|
|
309
312
|
if (tagName === 'button') {
|
|
310
313
|
let finalParentId = parentId;
|
|
311
314
|
|
|
312
|
-
|
|
315
|
+
const parentDomEl = el.parentNode as Element;
|
|
316
|
+
const parentTagName = parentDomEl?.tagName?.toLowerCase();
|
|
317
|
+
|
|
318
|
+
if (parentId === markdownId || parentTagName === 'li') {
|
|
313
319
|
const pNodeId = ulid();
|
|
314
320
|
const pNode: TemplateNode = {
|
|
315
321
|
id: pNodeId,
|
|
@@ -330,7 +336,7 @@ function walkDom(
|
|
|
330
336
|
id: ulid(),
|
|
331
337
|
nodeType: 'TagElement',
|
|
332
338
|
parentId: finalParentId,
|
|
333
|
-
tagName: 'a',
|
|
339
|
+
tagName: 'a',
|
|
334
340
|
href: '#',
|
|
335
341
|
buttonPayload: {
|
|
336
342
|
...buttonPayload,
|
|
@@ -148,6 +148,13 @@ export class ProfileStorage {
|
|
|
148
148
|
StorageManager.set(this.STORAGE_KEYS.profileToken, token);
|
|
149
149
|
StorageManager.set(this.STORAGE_KEYS.hasProfile, '1');
|
|
150
150
|
StorageManager.set(this.STORAGE_KEYS.unlockedProfile, '1');
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const maxAge = 60 * 60 * 24;
|
|
154
|
+
document.cookie = `tractstack_profile=true; path=/; SameSite=Lax; max-age=${maxAge}`;
|
|
155
|
+
} catch {
|
|
156
|
+
// Silently fail if cookies are blocked
|
|
157
|
+
}
|
|
151
158
|
}
|
|
152
159
|
|
|
153
160
|
/**
|
|
@@ -157,6 +164,12 @@ export class ProfileStorage {
|
|
|
157
164
|
StorageManager.remove(this.STORAGE_KEYS.profileToken);
|
|
158
165
|
StorageManager.remove(this.STORAGE_KEYS.hasProfile);
|
|
159
166
|
StorageManager.remove(this.STORAGE_KEYS.unlockedProfile);
|
|
167
|
+
try {
|
|
168
|
+
document.cookie =
|
|
169
|
+
'tractstack_profile=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
|
|
170
|
+
} catch {
|
|
171
|
+
// Silently fail
|
|
172
|
+
}
|
|
160
173
|
}
|
|
161
174
|
|
|
162
175
|
/**
|
package/utils/inject-files.ts
CHANGED
|
@@ -139,6 +139,18 @@ export async function injectTemplateFiles(
|
|
|
139
139
|
),
|
|
140
140
|
dest: 'src/components/compositor/nodes/Pane_layout.tsx',
|
|
141
141
|
},
|
|
142
|
+
{
|
|
143
|
+
src: resolve(
|
|
144
|
+
'../templates/src/components/codehooks/SandboxAuthWrapper.tsx'
|
|
145
|
+
),
|
|
146
|
+
dest: 'src/components/codehooks/SandboxAuthWrapper.tsx',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
src: resolve(
|
|
150
|
+
'../templates/src/components/codehooks/SandboxRegisterForm.tsx'
|
|
151
|
+
),
|
|
152
|
+
dest: 'src/components/codehooks/SandboxRegisterForm.tsx',
|
|
153
|
+
},
|
|
142
154
|
{
|
|
143
155
|
src: resolve('../templates/src/components/compositor/nodes/Markdown.tsx'),
|
|
144
156
|
dest: 'src/components/compositor/nodes/Markdown.tsx',
|
|
@@ -441,14 +453,6 @@ export async function injectTemplateFiles(
|
|
|
441
453
|
),
|
|
442
454
|
dest: 'src/components/edit/pane/RestylePaneModal.tsx',
|
|
443
455
|
},
|
|
444
|
-
{
|
|
445
|
-
src: resolve('../templates/src/components/edit/pane/AiPaneGenerator.tsx'),
|
|
446
|
-
dest: 'src/components/edit/pane/AiPaneGenerator.tsx',
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
src: resolve('../templates/src/components/edit/pane/AiPanePreview.tsx'),
|
|
450
|
-
dest: 'src/components/edit/pane/AiPanePreview.tsx',
|
|
451
|
-
},
|
|
452
456
|
{
|
|
453
457
|
src: resolve(
|
|
454
458
|
'../templates/src/components/edit/pane/steps/CopyInputStep.tsx'
|
|
@@ -467,6 +471,12 @@ export async function injectTemplateFiles(
|
|
|
467
471
|
),
|
|
468
472
|
dest: 'src/components/edit/pane/steps/AiDesignStep.tsx',
|
|
469
473
|
},
|
|
474
|
+
{
|
|
475
|
+
src: resolve(
|
|
476
|
+
'../templates/src/components/edit/pane/steps/DirectInjectStep.tsx'
|
|
477
|
+
),
|
|
478
|
+
dest: 'src/components/edit/pane/steps/DirectInjectStep.tsx',
|
|
479
|
+
},
|
|
470
480
|
{
|
|
471
481
|
src: resolve(
|
|
472
482
|
'../templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx'
|
|
@@ -603,12 +613,6 @@ export async function injectTemplateFiles(
|
|
|
603
613
|
dest: 'src/stores/selection.ts',
|
|
604
614
|
},
|
|
605
615
|
|
|
606
|
-
// AAI utils
|
|
607
|
-
{
|
|
608
|
-
src: resolve('../templates/src/utils/aai/getTitleSlug.ts'),
|
|
609
|
-
dest: 'src/utils/aai/getTitleSlug.ts',
|
|
610
|
-
},
|
|
611
|
-
|
|
612
616
|
// Compositor utils - etl
|
|
613
617
|
{
|
|
614
618
|
src: resolve('../templates/src/utils/etl/index.ts'),
|
|
@@ -844,6 +848,10 @@ export async function injectTemplateFiles(
|
|
|
844
848
|
),
|
|
845
849
|
dest: 'src/pages/context/[...contextSlug]/edit.astro',
|
|
846
850
|
},
|
|
851
|
+
{
|
|
852
|
+
src: resolve('../templates/src/pages/sandbox.astro'),
|
|
853
|
+
dest: 'src/pages/sandbox.astro',
|
|
854
|
+
},
|
|
847
855
|
{
|
|
848
856
|
src: resolve('../templates/src/pages/storykeep.astro'),
|
|
849
857
|
dest: 'src/pages/storykeep.astro',
|
|
@@ -888,6 +896,10 @@ export async function injectTemplateFiles(
|
|
|
888
896
|
src: resolve('../templates/src/pages/api/tailwind.ts'),
|
|
889
897
|
dest: 'src/pages/api/tailwind.ts',
|
|
890
898
|
},
|
|
899
|
+
{
|
|
900
|
+
src: resolve('../templates/src/pages/api/sandbox.ts'),
|
|
901
|
+
dest: 'src/pages/api/sandbox.ts',
|
|
902
|
+
},
|
|
891
903
|
|
|
892
904
|
// Authentication Pages
|
|
893
905
|
{
|
|
@@ -2156,6 +2168,13 @@ export async function injectTemplateFiles(
|
|
|
2156
2168
|
// Example Components (Conditional)
|
|
2157
2169
|
...(config?.includeExamples
|
|
2158
2170
|
? [
|
|
2171
|
+
{
|
|
2172
|
+
src: resolve(
|
|
2173
|
+
'../templates/custom/with-examples/SandboxLauncher.tsx'
|
|
2174
|
+
),
|
|
2175
|
+
dest: 'src/custom/SandboxLauncher.tsx',
|
|
2176
|
+
protected: true,
|
|
2177
|
+
},
|
|
2159
2178
|
{
|
|
2160
2179
|
src: resolve('../templates/custom/with-examples/CustomHero.astro'),
|
|
2161
2180
|
dest: 'src/custom/CustomHero.astro',
|