astro-tractstack 2.0.28 → 2.0.30

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.
@@ -26,7 +26,6 @@ if (healthCheckRedirect !== undefined) {
26
26
  }
27
27
 
28
28
  const brandConfig = await getBrandConfig(tenantId);
29
-
30
29
  const emptyStoryFragment = {
31
30
  id: ulid(),
32
31
  nodeType: 'StoryFragment' as const,
@@ -43,12 +42,15 @@ const loadData = {
43
42
  };
44
43
  const title = 'Sandbox - TractStack Editor';
45
44
  const storyFragmentID = emptyStoryFragment.id;
46
-
47
45
  const fullContentMap = await getFullContentMap(tenantId);
48
46
  const urlParams: Record<string, string | boolean> = {};
49
47
  for (const [key, value] of Astro.url.searchParams) {
50
48
  urlParams[key] = value === '' ? true : value;
51
49
  }
50
+
51
+ const hasProfile = Astro.request.headers
52
+ .get('cookie')
53
+ ?.includes('tractstack_profile=true');
52
54
  ---
53
55
 
54
56
  <Layout
@@ -59,7 +61,7 @@ for (const [key, value] of Astro.url.searchParams) {
59
61
  isStoryKeep={true}
60
62
  isEditor={true}
61
63
  >
62
- <SandboxAuthWrapper client:load />
64
+ <SandboxAuthWrapper client:load isServerSideAuthenticated={!!hasProfile} />
63
65
  <Header
64
66
  title={title}
65
67
  slug="sandbox"
@@ -70,65 +72,71 @@ for (const [key, value] of Astro.url.searchParams) {
70
72
  menu={null}
71
73
  />
72
74
 
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
- }}
75
+ {
76
+ hasProfile && (
77
+ <>
78
+ <section
79
+ id="storykeepHeader"
80
+ role="banner"
81
+ class="z-101 bg-mywhite left-0 right-0 drop-shadow transition-all duration-200"
97
82
  >
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)}
83
+ <StoryKeepHeader
84
+ slug="sandbox"
85
+ isContext={false}
106
86
  isSandboxMode={true}
107
87
  client:only="react"
108
88
  />
89
+ </section>
90
+
91
+ <div class="flex min-h-screen">
92
+ <StoryKeepToolMode isContext={false} client:only="react" />
93
+
94
+ <main id="mainContent" class="relative flex-1 overflow-x-auto">
95
+ <div class="bg-myblue/20 bg-mylightgrey h-full p-1.5">
96
+ <div
97
+ class="h-fit min-h-screen pb-96"
98
+ style={{
99
+ backgroundImage:
100
+ 'repeating-linear-gradient(135deg, transparent, transparent 10px, rgba(0,0,0,0.05) 10px, rgba(0,0,0,0.05) 20px)',
101
+ }}
102
+ >
103
+ <Compositor
104
+ id={storyFragmentID}
105
+ nodes={loadData}
106
+ config={brandConfig}
107
+ fullContentMap={fullContentMap}
108
+ fullCanonicalURL="/sandbox"
109
+ urlParams={urlParams}
110
+ availableCodeHooks={Object.keys(codeHookComponents)}
111
+ isSandboxMode={true}
112
+ client:only="react"
113
+ />
114
+ </div>
115
+ </div>
116
+ </main>
109
117
  </div>
110
- </div>
111
- </main>
112
- </div>
113
118
 
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
+ <aside
120
+ id="settingsControls"
121
+ class="z-101 pointer-events-none fixed bottom-16 right-2 flex flex-col items-end gap-2 md:bottom-2"
122
+ >
123
+ <div class="pointer-events-none flex-grow" />
119
124
 
120
- <div class="pointer-events-auto flex-shrink-0">
121
- <StoryKeepToolBar client:only="react" />
122
- </div>
125
+ <div class="pointer-events-auto flex-shrink-0">
126
+ <StoryKeepToolBar client:only="react" />
127
+ </div>
123
128
 
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>
129
+ <div class="pointer-events-auto max-h-full">
130
+ <SettingsPanel
131
+ config={brandConfig}
132
+ availableCodeHooks={Object.keys(codeHookComponents)}
133
+ client:only="react"
134
+ />
135
+ </div>
136
+ </aside>
137
+ </>
138
+ )
139
+ }
132
140
  </Layout>
133
141
 
134
142
  <script>
@@ -3199,6 +3199,9 @@ export class NodesContext {
3199
3199
  const allOriginalNodes: TemplateNode[] = [];
3200
3200
  const columnNodes: TemplateMarkdown[] = [];
3201
3201
 
3202
+ // Instantiate generator for column markdown parsing
3203
+ const markdownGen = new MarkdownGenerator(this);
3204
+
3202
3205
  duplicatedGrid.nodes?.forEach((originalColumn) => {
3203
3206
  const newColumn = cloneDeep(originalColumn);
3204
3207
  newColumn.id = ulid();
@@ -3206,12 +3209,22 @@ export class NodesContext {
3206
3209
  oldToNewIdMap.set(originalColumn.id, newColumn.id);
3207
3210
  columnNodes.push(newColumn);
3208
3211
 
3209
- originalColumn.nodes?.forEach((colNode) => {
3210
- allOriginalNodes.push(colNode);
3211
- });
3212
+ if (originalColumn.markdownBody) {
3213
+ const columnContentNodes = markdownGen.markdownToFlatNodes(
3214
+ originalColumn.markdownBody,
3215
+ newColumn.id
3216
+ ) as TemplateNode[];
3217
+ // Add generated nodes directly to allNodes
3218
+ allNodes.push(...columnContentNodes);
3219
+ } else {
3220
+ // Standard flow: collect existing nodes for remapping
3221
+ originalColumn.nodes?.forEach((colNode) => {
3222
+ allOriginalNodes.push(colNode);
3223
+ });
3224
+ }
3212
3225
  });
3213
3226
 
3214
- // Second pass: Clone all descendant nodes
3227
+ // Second pass: Clone all descendant nodes (only those from the standard flow)
3215
3228
  const allClonedDescendants = allOriginalNodes.map((originalNode) => {
3216
3229
  const newNode = cloneDeep(originalNode);
3217
3230
  newNode.id = ulid();
@@ -281,12 +281,15 @@ export interface MarkdownPaneFragmentNode extends PaneFragmentNode {
281
281
  parentClasses?: ParentClassesPayload;
282
282
  parentCss?: string[];
283
283
  gridClasses?: DefaultClassValue;
284
+ gridCss?: string;
284
285
  }
285
286
 
286
287
  export interface GridLayoutNode extends PaneFragmentNode {
287
288
  nodeType: 'GridLayoutNode';
288
289
  type: 'grid-layout';
289
290
  parentClasses?: ParentClassesPayload;
291
+ parentCss?: string;
292
+ gridCss?: string;
290
293
  defaultClasses?: Record<
291
294
  string,
292
295
  {
@@ -4,6 +4,8 @@ export type DesignLibraryEntry = {
4
4
  category: string;
5
5
  title: string;
6
6
  markdownCount: number;
7
+ retain?: boolean;
8
+ locked?: boolean;
7
9
  template: StoragePane;
8
10
  };
9
11
 
@@ -62,7 +62,7 @@ function convertLiveNodeToStorageNode(
62
62
  fileId: copyMode === 'retain' ? node.fileId : undefined,
63
63
  buttonPayload: copyMode === 'retain' ? node.buttonPayload : undefined,
64
64
  codeHookParams: copyMode === 'retain' ? node.codeHookParams : undefined,
65
- elementCss: copyMode === 'retain' ? node.elementCss : undefined,
65
+ copy: copyMode === 'retain' ? node.copy : undefined,
66
66
  };
67
67
 
68
68
  const childIds = ctx.getChildNodeIDs(node.id);
@@ -500,7 +500,8 @@ function convertLivePaneToStoragePane(
500
500
  : [],
501
501
  };
502
502
  } else if (gridLayoutNode) {
503
- const { id, parentId, isChanged, ...restOfGrid } = gridLayoutNode;
503
+ const { id, parentId, isChanged, parentCss, gridCss, ...restOfGrid } =
504
+ gridLayoutNode;
504
505
  storageGridLayout = {
505
506
  ...restOfGrid,
506
507
  nodes: ctx
@@ -517,6 +518,7 @@ function convertLivePaneToStoragePane(
517
518
  isChanged,
518
519
  markdownId,
519
520
  parentCss,
521
+ gridCss,
520
522
  ...restOfColumn
521
523
  } = columnNode;
522
524
 
@@ -584,10 +586,11 @@ export async function savePaneToLibrary(
584
586
  title: string;
585
587
  category: string;
586
588
  copyMode: CopyMode;
589
+ locked?: boolean;
587
590
  }
588
591
  ): Promise<BrandConfigState | null> {
589
592
  const ctx = getCtx();
590
- const { title, category, copyMode } = formData;
593
+ const { title, category, copyMode, locked } = formData;
591
594
 
592
595
  const newStoragePane = convertLivePaneToStoragePane(paneId, ctx, {
593
596
  title,
@@ -613,6 +616,8 @@ export async function savePaneToLibrary(
613
616
  title: title,
614
617
  markdownCount: actualMarkdownCount,
615
618
  template: newStoragePane,
619
+ retain: copyMode === 'retain',
620
+ locked: !!locked,
616
621
  };
617
622
 
618
623
  const currentState: BrandConfigState = convertToLocalState(config);
@@ -93,24 +93,6 @@ export const isMarkdownPaneFragmentNode = (
93
93
  );
94
94
  };
95
95
 
96
- interface WidgetNode extends FlatNode {
97
- tagName: 'code';
98
- codeHookParams: (string | string[])[];
99
- copy: string;
100
- }
101
-
102
- export const isWidgetNode = (
103
- node: BaseNode | FlatNode | null
104
- ): node is WidgetNode => {
105
- return (
106
- node !== null &&
107
- 'tagName' in node &&
108
- node.tagName === 'code' &&
109
- 'codeHookParams' in node &&
110
- Array.isArray(node.codeHookParams)
111
- );
112
- };
113
-
114
96
  export function hasTagName(
115
97
  node: BaseNode | null | undefined
116
98
  ): node is FlatNode {