astro-tractstack 2.0.30 → 2.0.32

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 CHANGED
@@ -10,7 +10,7 @@ function b(t) {
10
10
  }
11
11
  function g(t, e) {
12
12
  e.info("TractStack configuration applied"), t.enableMultiTenant && e.info("Multi-tenant mode enabled"), t.includeExamples && e.info("Example components will be included");
13
- const c = process.env.PUBLIC_GO_BACKEND, r = process.env.PUBLIC_TENANTID;
13
+ const c = process.env.PUBLIC_GO_BACKEND, o = process.env.PUBLIC_TENANTID;
14
14
  if (!c)
15
15
  e.warn("PUBLIC_GO_BACKEND not set - this will be required at runtime");
16
16
  else
@@ -19,11 +19,11 @@ function g(t, e) {
19
19
  } catch {
20
20
  e.error(`PUBLIC_GO_BACKEND is not a valid URL: ${c}`);
21
21
  }
22
- return r ? /^[a-zA-Z0-9_-]+$/.test(r) ? e.info(`Tenant ID validated: ${r}`) : e.error(`PUBLIC_TENANTID contains invalid characters: ${r}`) : e.warn("PUBLIC_TENANTID not set - this will be required at runtime"), t;
22
+ return o ? /^[a-zA-Z0-9_-]+$/.test(o) ? e.info(`Tenant ID validated: ${o}`) : e.error(`PUBLIC_TENANTID contains invalid characters: ${o}`) : e.warn("PUBLIC_TENANTID not set - this will be required at runtime"), t;
23
23
  }
24
24
  async function w(t, e, c) {
25
25
  e.info("TractStack: Injecting template files");
26
- const r = [
26
+ const o = [
27
27
  // Core Configuration
28
28
  {
29
29
  src: t("../templates/env.example"),
@@ -2053,38 +2053,12 @@ async function w(t, e, c) {
2053
2053
  src: t("../templates/socials/youtube.svg"),
2054
2054
  dest: "public/socials/youtube.svg"
2055
2055
  },
2056
- // Multi-Tenant Features
2057
- {
2058
- src: t("../templates/src/components/tenant/RegistrationForm.tsx"),
2059
- dest: "src/components/tenant/RegistrationForm.tsx"
2060
- },
2061
- {
2062
- src: t("../templates/src/utils/api/tenantConfig.ts"),
2063
- dest: "src/utils/api/tenantConfig.ts"
2064
- },
2065
- {
2066
- src: t("../templates/src/utils/api/tenantHelpers.ts"),
2067
- dest: "src/utils/api/tenantHelpers.ts"
2068
- },
2069
2056
  // Multi-Tenant Features (Conditional)
2070
2057
  ...c?.enableMultiTenant ? [
2071
2058
  // Middleware
2072
2059
  {
2073
2060
  src: t("../templates/src/middleware.ts"),
2074
2061
  dest: "src/middleware.ts"
2075
- },
2076
- // Pages
2077
- {
2078
- src: t("../templates/src/pages/sandbox/register.astro"),
2079
- dest: "src/pages/sandbox/register.astro"
2080
- },
2081
- {
2082
- src: t("../templates/src/pages/sandbox/activate.astro"),
2083
- dest: "src/pages/sandbox/activate.astro"
2084
- },
2085
- {
2086
- src: t("../templates/src/pages/sandbox/success.astro"),
2087
- dest: "src/pages/sandbox/success.astro"
2088
2062
  }
2089
2063
  ] : [],
2090
2064
  // Multi-Tenant Types (Always included due to plan reference)
@@ -2164,12 +2138,12 @@ async function w(t, e, c) {
2164
2138
  }
2165
2139
  ] : []
2166
2140
  ];
2167
- for (const s of r)
2141
+ for (const s of o)
2168
2142
  try {
2169
2143
  const p = i(s.dest);
2170
2144
  n(p) || x(p, { recursive: !0 });
2171
- const o = !s.protected && (s.dest === "tailwind.config.cjs" || s.dest.startsWith("src/components/codehooks/") || s.dest.startsWith("src/components/widgets/") || s.dest.startsWith("src/") || s.dest.startsWith("public/client/") || s.dest === ".gitignore");
2172
- if (!n(s.dest) || o)
2145
+ const r = !s.protected && (s.dest === "tailwind.config.cjs" || s.dest.startsWith("src/components/codehooks/") || s.dest.startsWith("src/components/widgets/") || s.dest.startsWith("src/") || s.dest.startsWith("public/client/") || s.dest === ".gitignore");
2146
+ if (!n(s.dest) || r)
2173
2147
  if (n(s.src))
2174
2148
  k(s.src, s.dest), e.info(`Updated ${s.dest}`);
2175
2149
  else {
@@ -2178,8 +2152,8 @@ async function w(t, e, c) {
2178
2152
  }
2179
2153
  else s.protected ? e.info(`Protected: ${s.dest} (skipped overwrite)`) : e.info(`Skipped existing ${s.dest}`);
2180
2154
  } catch (p) {
2181
- const o = p instanceof Error ? p.message : String(p);
2182
- e.error(`Failed to create ${s.dest}: ${o}`);
2155
+ const r = p instanceof Error ? p.message : String(p);
2156
+ e.error(`Failed to create ${s.dest}: ${r}`);
2183
2157
  }
2184
2158
  }
2185
2159
  function _(t) {
@@ -2198,7 +2172,7 @@ function C(t = {}) {
2198
2172
  return {
2199
2173
  name: "astro-tractstack",
2200
2174
  hooks: {
2201
- "astro:config:setup": async ({ config: c, updateConfig: r, logger: s }) => {
2175
+ "astro:config:setup": async ({ config: c, updateConfig: o, logger: s }) => {
2202
2176
  g(t, s);
2203
2177
  const p = t.enableMultiTenant || !1;
2204
2178
  if (s.info(
@@ -2218,7 +2192,7 @@ function C(t = {}) {
2218
2192
  ), new Error(
2219
2193
  "TractStack requires an SSR adapter. Please add @astrojs/node adapter to your astro.config.mjs"
2220
2194
  );
2221
- r({
2195
+ o({
2222
2196
  vite: {
2223
2197
  define: {
2224
2198
  __TRACTSTACK_VERSION__: JSON.stringify("2.0.0-alpha.1"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.30",
3
+ "version": "2.0.32",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,6 +16,8 @@ import {
16
16
  hasArtpacksStore,
17
17
  settingsPanelStore,
18
18
  brandConfigStore,
19
+ viewportModeStore,
20
+ setViewportMode,
19
21
  } from '@/stores/storykeep';
20
22
  import { getCtx, ROOT_NODE_NAME, type NodesContext } from '@/stores/nodes';
21
23
  import { stopLoadingAnimation } from '@/utils/helpers';
@@ -284,6 +286,12 @@ export const Compositor = (props: CompositorProps) => {
284
286
  selectionOrigin.current = null;
285
287
  };
286
288
 
289
+ useEffect(() => {
290
+ if (viewportModeStore.get() === 'auto') {
291
+ setViewportMode('auto');
292
+ }
293
+ }, []);
294
+
287
295
  useEffect(() => {
288
296
  fullContentMapStore.set(props.fullContentMap);
289
297
  hasAssemblyAIStore.set(props.config?.HAS_AAI || false);
@@ -9,26 +9,53 @@ import { getCtx } from '@/stores/nodes';
9
9
  import { RenderChildren } from './RenderChildren';
10
10
  import { CodeHookContainer } from './Pane';
11
11
  import type { NodeProps } from '@/types/nodeProps';
12
+ import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
12
13
  import { SaveToLibraryModal } from '@/components/edit/state/SaveToLibraryModal';
13
14
  import { RestylePaneModal } from '@/components/edit/pane/RestylePaneModal';
14
15
  import { selectionStore } from '@/stores/selection';
15
16
  import { copyPaneToClipboard } from '@/utils/compositor/designLibraryHelper';
16
17
 
18
+ function getSizeClasses(
19
+ size: string,
20
+ side: 'image' | 'content',
21
+ viewport: string
22
+ ): string {
23
+ if (viewport === 'mobile') {
24
+ return 'w-full';
25
+ }
26
+ switch (size) {
27
+ case 'narrow':
28
+ return side === 'image' ? 'w-1/3' : 'w-2/3';
29
+ case 'wide':
30
+ return side === 'image' ? 'w-2/3' : 'w-1/3';
31
+ default:
32
+ return 'w-1/2';
33
+ }
34
+ }
35
+
17
36
  export const Pane_DesignLibrary = (props: NodeProps) => {
18
37
  const ctx = getCtx(props);
19
-
20
38
  const { isRestyleModalOpen } = useStore(selectionStore, {
21
39
  keys: ['isRestyleModalOpen'],
22
40
  });
23
-
24
- const wrapperClasses = `grid ${ctx.getNodeClasses(
25
- props.nodeId,
41
+ const [currentViewport, setCurrentViewport] = useState(
26
42
  viewportKeyStore.get().value
27
- )}`;
43
+ );
44
+
45
+ useEffect(() => {
46
+ const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
47
+ setCurrentViewport(newViewport.value);
48
+ });
49
+ return () => unsubscribeViewport();
50
+ }, []);
51
+
52
+ const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
28
53
  const contentClasses = 'relative w-full h-auto justify-self-start';
29
54
  const contentStyles: CSSProperties = {
30
55
  ...ctx.getNodeCSSPropertiesStyles(props.nodeId),
31
56
  gridArea: '1/1/1/1',
57
+ position: 'relative',
58
+ zIndex: 1,
32
59
  };
33
60
  const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
34
61
  const [children, setChildren] = useState<string[]>([
@@ -36,11 +63,14 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
36
63
  ]);
37
64
  const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
38
65
  const [wasCopied, setWasCopied] = useState(false);
66
+ const [renderCount, setRenderCount] = useState(0);
67
+
39
68
  const getPaneId = (): string => `pane-${props.nodeId}`;
40
69
 
41
70
  useEffect(() => {
42
71
  const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
43
72
  setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
73
+ setRenderCount((prev) => prev + 1);
44
74
  });
45
75
  return unsubscribe;
46
76
  }, [props.nodeId, ctx.notifications]);
@@ -65,54 +95,145 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
65
95
  }
66
96
  };
67
97
 
98
+ const Buttons = () => (
99
+ <div className="absolute left-2 top-2 z-10 flex flex-row gap-x-2">
100
+ {!props.isSandboxMode && (
101
+ <button
102
+ title="Save Pane to Design Library"
103
+ onClick={handleSaveClick}
104
+ className="flex h-10 w-10 items-center justify-center rounded-full bg-cyan-600 p-1.5 shadow-lg hover:bg-cyan-700"
105
+ >
106
+ <ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
107
+ </button>
108
+ )}
109
+ <button
110
+ title="Restyle Pane from Design Library"
111
+ onClick={handleRestyleClick}
112
+ className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
113
+ >
114
+ <ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
115
+ </button>
116
+ <button
117
+ title="Copy Pane Design to Clipboard"
118
+ onClick={handleCopyToClipboard}
119
+ className={`flex h-10 w-10 items-center justify-center rounded-full p-1.5 shadow-lg transition-colors ${
120
+ wasCopied ? 'bg-green-500' : 'bg-gray-600 hover:bg-gray-700'
121
+ }`}
122
+ >
123
+ {wasCopied ? (
124
+ <CheckIcon className="h-7 w-7 text-white" />
125
+ ) : (
126
+ <ArrowDownTrayIcon className="h-7 w-7 text-white" />
127
+ )}
128
+ </button>
129
+ </div>
130
+ );
131
+
132
+ const allNodes = ctx.allNodes.get();
133
+ const bgNode = children
134
+ .map((id) => allNodes.get(id))
135
+ .find(
136
+ (node) =>
137
+ node?.nodeType === 'BgPane' &&
138
+ 'type' in node &&
139
+ (node.type === 'background-image' || node.type === 'artpack-image')
140
+ ) as (BgImageNode | ArtpackImageNode) | undefined;
141
+
142
+ const useFlexLayout =
143
+ bgNode &&
144
+ (bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
145
+ const deferFlexLayout =
146
+ bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
147
+
148
+ const flexDirection =
149
+ currentViewport === 'mobile'
150
+ ? 'flex-col'
151
+ : bgNode?.position === 'rightBleed'
152
+ ? 'flex-row-reverse'
153
+ : 'flex-row';
154
+
68
155
  return (
69
156
  <div id={getPaneId()} className="pane min-h-16">
70
- <div id={ctx.getNodeSlug(props.nodeId)} className={wrapperClasses}>
71
- <div
72
- className={contentClasses}
73
- style={contentStyles}
74
- onClick={(e) => {
75
- ctx.setClickedNodeId(props.nodeId);
76
- e.stopPropagation();
77
- }}
78
- >
79
- <div className="absolute left-2 top-2 z-10 flex flex-row gap-x-2">
80
- {!props.isSandboxMode && (
81
- <button
82
- title="Save Pane to Design Library"
83
- onClick={handleSaveClick}
84
- className="flex h-10 w-10 items-center justify-center rounded-full bg-cyan-600 p-1.5 shadow-lg hover:bg-cyan-700"
85
- >
86
- <ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
87
- </button>
88
- )}
89
- <button
90
- title="Restyle Pane from Design Library"
91
- onClick={handleRestyleClick}
92
- className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
157
+ <div
158
+ id={ctx.getNodeSlug(props.nodeId)}
159
+ className={useFlexLayout ? '' : wrapperClasses}
160
+ >
161
+ {codeHookPayload ? (
162
+ <div className={contentClasses} style={contentStyles}>
163
+ <Buttons />
164
+ <CodeHookContainer payload={codeHookPayload} />
165
+ </div>
166
+ ) : useFlexLayout ? (
167
+ <div
168
+ className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
169
+ >
170
+ <div
171
+ className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
93
172
  >
94
- <ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
95
- </button>
96
- <button
97
- title="Copy Pane Design to Clipboard"
98
- onClick={handleCopyToClipboard}
99
- className={`flex h-10 w-10 items-center justify-center rounded-full p-1.5 shadow-lg transition-colors ${
100
- wasCopied ? 'bg-green-500' : 'bg-gray-600 hover:bg-gray-700'
101
- }`}
173
+ <RenderChildren
174
+ children={children.filter((id) => {
175
+ const node = allNodes.get(id);
176
+ return node?.nodeType === 'BgPane';
177
+ })}
178
+ nodeProps={props}
179
+ key={`bg-children-${props.nodeId}-${renderCount}`}
180
+ />
181
+ </div>
182
+ <div
183
+ className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
184
+ style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
185
+ onClick={(e) => {
186
+ ctx.setClickedNodeId(props.nodeId);
187
+ e.stopPropagation();
188
+ }}
102
189
  >
103
- {wasCopied ? (
104
- <CheckIcon className="h-7 w-7 text-white" />
105
- ) : (
106
- <ArrowDownTrayIcon className="h-7 w-7 text-white" />
107
- )}
108
- </button>
190
+ <Buttons />
191
+ <RenderChildren
192
+ children={children.filter((id) => {
193
+ const node = allNodes.get(id);
194
+ return node?.nodeType !== 'BgPane';
195
+ })}
196
+ nodeProps={props}
197
+ key={`content-children-${props.nodeId}-${renderCount}`}
198
+ />
199
+ </div>
109
200
  </div>
110
- {codeHookPayload ? (
111
- <CodeHookContainer payload={codeHookPayload} />
112
- ) : (
113
- <RenderChildren children={children} nodeProps={props} />
114
- )}
115
- </div>
201
+ ) : deferFlexLayout ? (
202
+ <div
203
+ className={contentClasses}
204
+ style={contentStyles}
205
+ onClick={(e) => {
206
+ ctx.setClickedNodeId(props.nodeId);
207
+ e.stopPropagation();
208
+ }}
209
+ >
210
+ <Buttons />
211
+ <RenderChildren
212
+ children={children.filter((id) => {
213
+ const node = allNodes.get(id);
214
+ return node?.nodeType !== 'BgPane';
215
+ })}
216
+ nodeProps={props}
217
+ key={`content-children-${props.nodeId}-${renderCount}`}
218
+ />
219
+ </div>
220
+ ) : (
221
+ <div
222
+ className={contentClasses}
223
+ style={contentStyles}
224
+ onClick={(e) => {
225
+ ctx.setClickedNodeId(props.nodeId);
226
+ e.stopPropagation();
227
+ }}
228
+ >
229
+ <Buttons />
230
+ <RenderChildren
231
+ children={children}
232
+ nodeProps={props}
233
+ key={`render-children-${props.nodeId}-${renderCount}`}
234
+ />
235
+ </div>
236
+ )}
116
237
  </div>
117
238
  {isSaveModalOpen && (
118
239
  <SaveToLibraryModal
@@ -5,64 +5,181 @@ import { getCtx } from '@/stores/nodes';
5
5
  import { RenderChildren } from './RenderChildren';
6
6
  import { CodeHookContainer } from './Pane';
7
7
  import type { NodeProps } from '@/types/nodeProps';
8
+ import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
9
+
10
+ function getSizeClasses(
11
+ size: string,
12
+ side: 'image' | 'content',
13
+ viewport: string
14
+ ): string {
15
+ if (viewport === 'mobile') {
16
+ return 'w-full';
17
+ }
18
+ switch (size) {
19
+ case 'narrow':
20
+ return side === 'image' ? 'w-1/3' : 'w-2/3';
21
+ case 'wide':
22
+ return side === 'image' ? 'w-2/3' : 'w-1/3';
23
+ default:
24
+ return 'w-1/2';
25
+ }
26
+ }
8
27
 
9
28
  export const PaneEraser = (props: NodeProps) => {
10
- const wrapperClasses = `grid ${getCtx(props).getNodeClasses(props.nodeId, viewportKeyStore.get().value)}`;
29
+ const ctx = getCtx(props);
30
+ const [currentViewport, setCurrentViewport] = useState(
31
+ viewportKeyStore.get().value
32
+ );
33
+
34
+ useEffect(() => {
35
+ const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
36
+ setCurrentViewport(newViewport.value);
37
+ });
38
+ return () => unsubscribeViewport();
39
+ }, []);
40
+
41
+ const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
11
42
  const contentClasses = 'relative w-full h-auto justify-self-start';
12
43
  const contentStyles: CSSProperties = {
13
- ...getCtx(props).getNodeCSSPropertiesStyles(props.nodeId),
44
+ ...ctx.getNodeCSSPropertiesStyles(props.nodeId),
14
45
  gridArea: '1/1/1/1',
46
+ position: 'relative',
47
+ zIndex: 1,
15
48
  };
16
- const codeHookPayload = getCtx(props).getNodeCodeHookPayload(props.nodeId);
49
+ const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
17
50
  const [children, setChildren] = useState<string[]>([
18
- ...getCtx(props).getChildNodeIDs(props.nodeId),
51
+ ...ctx.getChildNodeIDs(props.nodeId),
19
52
  ]);
53
+ const [renderCount, setRenderCount] = useState(0);
20
54
 
21
55
  const getPaneId = (): string => `pane-${props.nodeId}`;
22
56
 
23
57
  useEffect(() => {
24
- const unsubscribe = getCtx(props).notifications.subscribe(
25
- props.nodeId,
26
- () => {
27
- console.log(
28
- 'notification received data update for pane node: ' + props.nodeId
29
- );
30
- setChildren([...getCtx(props).getChildNodeIDs(props.nodeId)]);
31
- }
32
- );
58
+ const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
59
+ setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
60
+ setRenderCount((prev) => prev + 1);
61
+ });
33
62
  return unsubscribe;
34
- }, []);
63
+ }, [props.nodeId, ctx.notifications]);
64
+
65
+ const DeleteButton = () => (
66
+ <button
67
+ title="Delete Pane"
68
+ onClick={(e) => {
69
+ ctx.setClickedNodeId(props.nodeId);
70
+ e.stopPropagation();
71
+ }}
72
+ className="absolute right-2 top-2 z-10 rounded-full bg-red-700 p-1.5 hover:bg-black"
73
+ >
74
+ <TrashIcon className="h-10 w-10 text-white" />
75
+ </button>
76
+ );
77
+
78
+ const allNodes = ctx.allNodes.get();
79
+ const bgNode = children
80
+ .map((id) => allNodes.get(id))
81
+ .find(
82
+ (node) =>
83
+ node?.nodeType === 'BgPane' &&
84
+ 'type' in node &&
85
+ (node.type === 'background-image' || node.type === 'artpack-image')
86
+ ) as (BgImageNode | ArtpackImageNode) | undefined;
87
+
88
+ const useFlexLayout =
89
+ bgNode &&
90
+ (bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
91
+ const deferFlexLayout =
92
+ bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
93
+
94
+ const flexDirection =
95
+ currentViewport === 'mobile'
96
+ ? 'flex-col'
97
+ : bgNode?.position === 'rightBleed'
98
+ ? 'flex-row-reverse'
99
+ : 'flex-row';
35
100
 
36
101
  return (
37
102
  <div id={getPaneId()} className="pane min-h-16">
38
103
  <div
39
- id={getCtx(props).getNodeSlug(props.nodeId)}
40
- className={wrapperClasses}
104
+ id={ctx.getNodeSlug(props.nodeId)}
105
+ className={useFlexLayout ? '' : wrapperClasses}
41
106
  >
42
- <div
43
- className={contentClasses}
44
- style={contentStyles}
45
- onClick={(e) => {
46
- getCtx(props).setClickedNodeId(props.nodeId);
47
- e.stopPropagation();
48
- }}
49
- >
50
- <button
51
- title="Delete Pane"
107
+ {codeHookPayload ? (
108
+ <div className={contentClasses} style={contentStyles}>
109
+ <DeleteButton />
110
+ <CodeHookContainer payload={codeHookPayload} />
111
+ </div>
112
+ ) : useFlexLayout ? (
113
+ <div
114
+ className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
115
+ >
116
+ <div
117
+ className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
118
+ >
119
+ <RenderChildren
120
+ children={children.filter((id) => {
121
+ const node = allNodes.get(id);
122
+ return node?.nodeType === 'BgPane';
123
+ })}
124
+ nodeProps={props}
125
+ key={`bg-children-${props.nodeId}-${renderCount}`}
126
+ />
127
+ </div>
128
+ <div
129
+ className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
130
+ style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
131
+ onClick={(e) => {
132
+ ctx.setClickedNodeId(props.nodeId);
133
+ e.stopPropagation();
134
+ }}
135
+ >
136
+ <DeleteButton />
137
+ <RenderChildren
138
+ children={children.filter((id) => {
139
+ const node = allNodes.get(id);
140
+ return node?.nodeType !== 'BgPane';
141
+ })}
142
+ nodeProps={props}
143
+ key={`content-children-${props.nodeId}-${renderCount}`}
144
+ />
145
+ </div>
146
+ </div>
147
+ ) : deferFlexLayout ? (
148
+ <div
149
+ className={contentClasses}
150
+ style={contentStyles}
52
151
  onClick={(e) => {
53
- getCtx(props).setClickedNodeId(props.nodeId);
152
+ ctx.setClickedNodeId(props.nodeId);
54
153
  e.stopPropagation();
55
154
  }}
56
- className="absolute right-2 top-2 z-10 rounded-full bg-red-700 p-1.5 hover:bg-black"
57
155
  >
58
- <TrashIcon className="h-10 w-10 text-white" />
59
- </button>
60
- {codeHookPayload ? (
61
- <CodeHookContainer payload={codeHookPayload} />
62
- ) : (
63
- <RenderChildren children={children} nodeProps={props} />
64
- )}
65
- </div>
156
+ <DeleteButton />
157
+ <RenderChildren
158
+ children={children.filter((id) => {
159
+ const node = allNodes.get(id);
160
+ return node?.nodeType !== 'BgPane';
161
+ })}
162
+ nodeProps={props}
163
+ key={`content-children-${props.nodeId}-${renderCount}`}
164
+ />
165
+ </div>
166
+ ) : (
167
+ <div
168
+ className={contentClasses}
169
+ style={contentStyles}
170
+ onClick={(e) => {
171
+ ctx.setClickedNodeId(props.nodeId);
172
+ e.stopPropagation();
173
+ }}
174
+ >
175
+ <DeleteButton />
176
+ <RenderChildren
177
+ children={children}
178
+ nodeProps={props}
179
+ key={`render-children-${props.nodeId}-${renderCount}`}
180
+ />
181
+ </div>
182
+ )}
66
183
  </div>
67
184
  </div>
68
185
  );
@@ -35,6 +35,7 @@ import type {
35
35
  MenuNode,
36
36
  NodeType,
37
37
  PaneFragmentNode,
38
+ BgImageNode,
38
39
  PaneNode,
39
40
  StoryFragmentNode,
40
41
  Tag,
@@ -3264,6 +3265,15 @@ export class NodesContext {
3264
3265
  breakMobile: visualBreakPane.breakMobile,
3265
3266
  };
3266
3267
  allNodes.push(bgPaneNode);
3268
+ } else if (paneTemplate.bgPane.type === 'background-image') {
3269
+ const bgImagePane = paneTemplate.bgPane as BgImageNode;
3270
+ const bgPaneNode: BgImageNode = {
3271
+ ...bgImagePane,
3272
+ id: bgPaneId,
3273
+ nodeType: 'BgPane',
3274
+ parentId: newPaneId,
3275
+ };
3276
+ allNodes.push(bgPaneNode);
3267
3277
  } else if (paneTemplate.bgPane.type === 'artpack-image') {
3268
3278
  const artpackBgPane = paneTemplate.bgPane as ArtpackImageNode;
3269
3279
  const bgPaneNode: ArtpackImageNode = {
@@ -78,7 +78,8 @@ export function getUserRole(astro: any): 'admin' | 'editor' | null {
78
78
  */
79
79
  export function requireAdmin(astro: any): Response | undefined {
80
80
  if (!isAdmin(astro)) {
81
- return astro.redirect('/storykeep/login');
81
+ const target = encodeURIComponent(astro.url.pathname + astro.url.search);
82
+ return astro.redirect(`/storykeep/login?redirect=${target}`);
82
83
  }
83
84
  }
84
85
 
@@ -88,7 +89,8 @@ export function requireAdmin(astro: any): Response | undefined {
88
89
  */
89
90
  export function requireEditor(astro: any): Response | undefined {
90
91
  if (!isEditor(astro)) {
91
- return astro.redirect('/storykeep/login');
92
+ const target = encodeURIComponent(astro.url.pathname + astro.url.search);
93
+ return astro.redirect(`/storykeep/login?redirect=${target}`);
92
94
  }
93
95
  }
94
96
 
@@ -98,7 +100,8 @@ export function requireEditor(astro: any): Response | undefined {
98
100
  */
99
101
  export function requireAdminOrEditor(astro: any): Response | undefined {
100
102
  if (!isAuthenticated(astro)) {
101
- return astro.redirect('/storykeep/login');
103
+ const target = encodeURIComponent(astro.url.pathname + astro.url.search);
104
+ return astro.redirect(`/storykeep/login?redirect=${target}`);
102
105
  }
103
106
  }
104
107