astro-tractstack 2.0.12 → 2.0.14

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 (26) hide show
  1. package/dist/index.js +22 -0
  2. package/package.json +1 -1
  3. package/templates/src/client/view.js +5 -0
  4. package/templates/src/components/compositor/Compositor.tsx +3 -2
  5. package/templates/src/components/compositor/Node.tsx +18 -2
  6. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +105 -0
  7. package/templates/src/components/edit/ToolMode.tsx +7 -0
  8. package/templates/src/components/edit/pane/AddPanePanel.tsx +5 -1
  9. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +4 -1
  10. package/templates/src/components/edit/pane/AiPaneGenerator.tsx +264 -94
  11. package/templates/src/components/edit/pane/AiPanePreview.tsx +60 -210
  12. package/templates/src/components/edit/pane/PageGen.tsx +1 -1
  13. package/templates/src/components/edit/pane/PageGenSelector.tsx +4 -0
  14. package/templates/src/components/edit/pane/RestylePaneModal.tsx +573 -0
  15. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +205 -0
  16. package/templates/src/constants/prompts.json +3 -3
  17. package/templates/src/stores/selection.ts +4 -0
  18. package/templates/src/types/compositorTypes.ts +51 -1
  19. package/templates/src/types/tractstack.ts +36 -31
  20. package/templates/src/utils/aai/getTitleSlug.ts +1 -1
  21. package/templates/src/utils/api/brandConfig.ts +8 -2
  22. package/templates/src/utils/api/brandHelpers.ts +4 -0
  23. package/templates/src/utils/compositor/aiPaneParser.ts +39 -13
  24. package/templates/src/utils/compositor/designLibraryHelper.ts +331 -0
  25. package/templates/src/utils/compositor/processMarkdown.ts +1 -1
  26. package/utils/inject-files.ts +22 -0
package/dist/index.js CHANGED
@@ -126,6 +126,12 @@ async function w(t, e, c) {
126
126
  ),
127
127
  dest: "src/components/compositor/nodes/Pane_eraser.tsx"
128
128
  },
129
+ {
130
+ src: t(
131
+ "../templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx"
132
+ ),
133
+ dest: "src/components/compositor/nodes/Pane_DesignLibrary.tsx"
134
+ },
129
135
  {
130
136
  src: t(
131
137
  "../templates/src/components/compositor/nodes/Pane_layout.tsx"
@@ -428,6 +434,12 @@ async function w(t, e, c) {
428
434
  ),
429
435
  dest: "src/components/edit/pane/AddPanePanel_codehook.tsx"
430
436
  },
437
+ {
438
+ src: t(
439
+ "../templates/src/components/edit/pane/RestylePaneModal.tsx"
440
+ ),
441
+ dest: "src/components/edit/pane/RestylePaneModal.tsx"
442
+ },
431
443
  {
432
444
  src: t("../templates/src/components/edit/pane/AiPaneGenerator.tsx"),
433
445
  dest: "src/components/edit/pane/AiPaneGenerator.tsx"
@@ -624,6 +636,10 @@ async function w(t, e, c) {
624
636
  src: t("../templates/src/utils/compositor/typeGuards.ts"),
625
637
  dest: "src/utils/compositor/typeGuards.ts"
626
638
  },
639
+ {
640
+ src: t("../templates/src/utils/compositor/designLibraryHelper.ts"),
641
+ dest: "src/utils/compositor/designLibraryHelper.ts"
642
+ },
627
643
  {
628
644
  src: t("../templates/src/utils/compositor/domHelpers.ts"),
629
645
  dest: "src/utils/compositor/domHelpers.ts"
@@ -1345,6 +1361,12 @@ async function w(t, e, c) {
1345
1361
  src: t("../templates/src/components/edit/state/SaveModal.tsx"),
1346
1362
  dest: "src/components/edit/state/SaveModal.tsx"
1347
1363
  },
1364
+ {
1365
+ src: t(
1366
+ "../templates/src/components/edit/state/SaveToLibraryModal.tsx"
1367
+ ),
1368
+ dest: "src/components/edit/state/SaveToLibraryModal.tsx"
1369
+ },
1348
1370
  {
1349
1371
  src: t("../templates/src/components/edit/state/StylesMemory.tsx"),
1350
1372
  dest: "src/components/edit/state/StylesMemory.tsx"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.12",
3
+ "version": "2.0.14",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -42,6 +42,11 @@ async function sendStateUpdate(data) {
42
42
  return;
43
43
  }
44
44
  const config = window.TractStackApp.getConfig();
45
+
46
+ if (!config.sessionId || !config.storyfragmentId) {
47
+ return;
48
+ }
49
+
45
50
  const url = `${config.backendUrl}/api/v1/state`;
46
51
  const body = { paneId: '', duration: 0, ...data };
47
52
  log('Sending state update to backend.', { url, body });
@@ -284,11 +284,12 @@ export const Compositor = (props: CompositorProps) => {
284
284
 
285
285
  useEffect(() => {
286
286
  fullContentMapStore.set(props.fullContentMap);
287
- hasAssemblyAIStore.set(props.config.HAS_AAI);
287
+ hasAssemblyAIStore.set(props.config?.HAS_AAI || false);
288
288
  urlParamsStore.set(props.urlParams);
289
289
  canonicalURLStore.set(props.fullCanonicalURL);
290
290
  preferredThemeStore.set(props.config.THEME as Theme);
291
- brandColourStore.set(props.config.BRAND_COLOURS);
291
+ if (props.config.BRAND_COLOURS)
292
+ brandColourStore.set(props.config.BRAND_COLOURS);
292
293
  codehookMapStore.set(props.availableCodeHooks);
293
294
  }, [
294
295
  props.fullContentMap,
@@ -30,6 +30,7 @@ import { NodeBasicTag } from './nodes/tagElements/NodeBasicTag';
30
30
  import { NodeBasicTagInsert } from './nodes/tagElements/NodeBasicTag_insert';
31
31
  import { NodeBasicTagEraser } from './nodes/tagElements/NodeBasicTag_eraser';
32
32
  import { NodeBasicTagSettings } from './nodes/tagElements/NodeBasicTag_settings';
33
+ import { Pane_DesignLibrary } from './nodes/Pane_DesignLibrary';
33
34
  import AddPanePanel from '@/components/edit/pane/AddPanePanel';
34
35
  import PageCreationSelector from '@/components/edit/pane/PageGenSelector';
35
36
  import ConfigPanePanel from '@/components/edit/pane/ConfigPanePanel';
@@ -155,6 +156,7 @@ const getElement = (
155
156
  nodeId={props.nodeId}
156
157
  ctx={getCtx(props)}
157
158
  isTemplate={isTemplate}
159
+ config={props.config!}
158
160
  />
159
161
  ) : (
160
162
  <>
@@ -179,6 +181,9 @@ const getElement = (
179
181
  const toolModeVal = getCtx(props).toolModeValStore.get().value;
180
182
  const paneNodes = getCtx(props).getChildNodeIDs(node.id);
181
183
  const paneNode = node as PaneNode;
184
+ if (toolModeVal === 'designLibrary') {
185
+ return <Pane_DesignLibrary {...sharedProps} />;
186
+ }
182
187
  if (paneNode.isContextPane) {
183
188
  if (!isPreview)
184
189
  getCtx(props).hasTitle.set(!(!paneNode.slug || !paneNode.title));
@@ -215,6 +220,7 @@ const getElement = (
215
220
  nodeId={node.id}
216
221
  first={true}
217
222
  ctx={getCtx(props)}
223
+ config={props.config!}
218
224
  isContextPane={true}
219
225
  />
220
226
  </PanelVisibilityWrapper>
@@ -242,7 +248,12 @@ const getElement = (
242
248
  panelType="add"
243
249
  ctx={getCtx(props)}
244
250
  >
245
- <AddPanePanel nodeId={node.id} first={true} ctx={getCtx(props)} />
251
+ <AddPanePanel
252
+ nodeId={node.id}
253
+ first={true}
254
+ ctx={getCtx(props)}
255
+ config={props.config!}
256
+ />
246
257
  </PanelVisibilityWrapper>
247
258
  )}
248
259
  <div className="py-0.5">
@@ -266,7 +277,12 @@ const getElement = (
266
277
  panelType="add"
267
278
  ctx={getCtx(props)}
268
279
  >
269
- <AddPanePanel nodeId={node.id} first={false} ctx={getCtx(props)} />
280
+ <AddPanePanel
281
+ nodeId={node.id}
282
+ first={false}
283
+ ctx={getCtx(props)}
284
+ config={props.config!}
285
+ />
270
286
  </PanelVisibilityWrapper>
271
287
  </>
272
288
  );
@@ -0,0 +1,105 @@
1
+ import { type CSSProperties, useEffect, useState } from 'react';
2
+ import { useStore } from '@nanostores/react';
3
+ import ArchiveBoxArrowDownIcon from '@heroicons/react/24/outline/ArchiveBoxArrowDownIcon';
4
+ import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
5
+ import { viewportKeyStore } from '@/stores/storykeep';
6
+ import { getCtx } from '@/stores/nodes';
7
+ import { RenderChildren } from './RenderChildren';
8
+ import { CodeHookContainer } from './Pane';
9
+ import type { NodeProps } from '@/types/nodeProps';
10
+ import { SaveToLibraryModal } from '@/components/edit/state/SaveToLibraryModal';
11
+ import { RestylePaneModal } from '@/components/edit/pane/RestylePaneModal';
12
+ import { selectionStore } from '@/stores/selection';
13
+
14
+ export const Pane_DesignLibrary = (props: NodeProps) => {
15
+ const ctx = getCtx(props);
16
+
17
+ if (!props.config || !props.config.TENANT_ID) {
18
+ return <></>;
19
+ }
20
+
21
+ const { isRestyleModalOpen } = useStore(selectionStore, {
22
+ keys: ['isRestyleModalOpen'],
23
+ });
24
+
25
+ const wrapperClasses = `grid ${ctx.getNodeClasses(
26
+ props.nodeId,
27
+ viewportKeyStore.get().value
28
+ )}`;
29
+ const contentClasses = 'relative w-full h-auto justify-self-start';
30
+ const contentStyles: CSSProperties = {
31
+ ...ctx.getNodeCSSPropertiesStyles(props.nodeId),
32
+ gridArea: '1/1/1/1',
33
+ };
34
+ const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
35
+ const [children, setChildren] = useState<string[]>([
36
+ ...ctx.getChildNodeIDs(props.nodeId),
37
+ ]);
38
+ const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
39
+ const getPaneId = (): string => `pane-${props.nodeId}`;
40
+
41
+ useEffect(() => {
42
+ const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
43
+ setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
44
+ });
45
+ return unsubscribe;
46
+ }, [props.nodeId, ctx.notifications]);
47
+
48
+ const handleRestyleClick = (e: React.MouseEvent) => {
49
+ e.stopPropagation();
50
+ selectionStore.setKey('paneToRestyleId', props.nodeId);
51
+ selectionStore.setKey('isRestyleModalOpen', true);
52
+ };
53
+
54
+ const handleSaveClick = (e: React.MouseEvent) => {
55
+ e.stopPropagation();
56
+ setIsSaveModalOpen(true);
57
+ };
58
+
59
+ return (
60
+ <div id={getPaneId()} className="pane min-h-16">
61
+ <div id={ctx.getNodeSlug(props.nodeId)} className={wrapperClasses}>
62
+ <div
63
+ className={contentClasses}
64
+ style={contentStyles}
65
+ onClick={(e) => {
66
+ ctx.setClickedNodeId(props.nodeId);
67
+ e.stopPropagation();
68
+ }}
69
+ >
70
+ <div className="absolute left-2 top-2 z-10 flex flex-col gap-y-2">
71
+ <button
72
+ title="Save Pane to Design Library"
73
+ onClick={handleSaveClick}
74
+ className="flex h-10 w-10 items-center justify-center rounded-full bg-cyan-600 p-1.5 shadow-lg hover:bg-cyan-700"
75
+ >
76
+ <ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
77
+ </button>
78
+ <button
79
+ title="Restyle Pane from Design Library"
80
+ onClick={handleRestyleClick}
81
+ className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
82
+ >
83
+ <ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
84
+ </button>
85
+ </div>
86
+ {codeHookPayload ? (
87
+ <CodeHookContainer payload={codeHookPayload} />
88
+ ) : (
89
+ <RenderChildren children={children} nodeProps={props} />
90
+ )}
91
+ </div>
92
+ </div>
93
+ {isSaveModalOpen && (
94
+ <SaveToLibraryModal
95
+ paneId={props.nodeId}
96
+ config={props.config}
97
+ tenantId={props.config.TENANT_ID}
98
+ onClose={() => setIsSaveModalOpen(false)}
99
+ />
100
+ )}
101
+
102
+ {isRestyleModalOpen && <RestylePaneModal config={props.config} />}
103
+ </div>
104
+ );
105
+ };
@@ -3,6 +3,7 @@ import { useStore } from '@nanostores/react';
3
3
  import PencilSquareIcon from '@heroicons/react/24/outline/PencilSquareIcon';
4
4
  import PaintBrushIcon from '@heroicons/react/24/outline/PaintBrushIcon';
5
5
  import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
6
+ import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
6
7
  import ArrowsUpDownIcon from '@heroicons/react/24/outline/ArrowsUpDownIcon';
7
8
  import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
8
9
  import BugAntIcon from '@heroicons/react/24/outline/BugAntIcon';
@@ -45,6 +46,12 @@ const storykeepToolModes = [
45
46
  title: 'Move',
46
47
  description: 'Keyboard accessible re-order',
47
48
  },
49
+ {
50
+ key: 'designLibrary' as const,
51
+ Icon: ArrowPathRoundedSquareIcon,
52
+ title: 'Design Library',
53
+ description: 'Save pane to design library',
54
+ },
48
55
  ] as const;
49
56
 
50
57
  interface StoryKeepToolModeProps {
@@ -5,8 +5,9 @@ import AddPaneNewPanel from './AddPanePanel_new';
5
5
  import AddPaneBreakPanel from './AddPanePanel_break';
6
6
  import AddPaneReUsePanel from './AddPanePanel_reuse';
7
7
  import AddPaneCodeHookPanel from './AddPanePanel_codehook';
8
- import { NodesContext, ROOT_NODE_NAME, getCtx } from '@/stores/nodes'; // Import getCtx
8
+ import { NodesContext, ROOT_NODE_NAME, getCtx } from '@/stores/nodes';
9
9
  import { PaneAddMode } from '@/types/compositorTypes';
10
+ import type { BrandConfig } from '@/types/tractstack';
10
11
 
11
12
  interface AddPanePanelProps {
12
13
  nodeId: string;
@@ -14,6 +15,7 @@ interface AddPanePanelProps {
14
15
  ctx?: NodesContext;
15
16
  isStoryFragment?: boolean;
16
17
  isContextPane?: boolean;
18
+ config?: BrandConfig;
17
19
  }
18
20
 
19
21
  const AddPanePanel = ({
@@ -22,6 +24,7 @@ const AddPanePanel = ({
22
24
  ctx,
23
25
  isStoryFragment = false,
24
26
  isContextPane = false,
27
+ config,
25
28
  }: AddPanePanelProps) => {
26
29
  const [reset, setReset] = useState(false);
27
30
  const lookup = first ? `${nodeId}-0` : nodeId;
@@ -62,6 +65,7 @@ const AddPanePanel = ({
62
65
  ctx={nodesCtx}
63
66
  isStoryFragment={isStoryFragment}
64
67
  isContextPane={isContextPane}
68
+ config={config!}
65
69
  />
66
70
  ) : mode === PaneAddMode.BREAK && !isContextPane ? (
67
71
  <AddPaneBreakPanel
@@ -26,7 +26,7 @@ import {
26
26
  import { templateCategories } from '@/utils/compositor/templateMarkdownStyles';
27
27
  import { AiPaneGenerator } from './AiPaneGenerator';
28
28
  import { AddPaneNewCustomCopy } from './AddPanePanel_newCustomCopy';
29
- import { themes, type Theme } from '@/types/tractstack';
29
+ import { themes, type Theme, type BrandConfig } from '@/types/tractstack';
30
30
  import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
31
31
  import { useStore } from '@nanostores/react';
32
32
 
@@ -37,6 +37,7 @@ interface AddPaneNewPanelProps {
37
37
  ctx?: NodesContext;
38
38
  isStoryFragment?: boolean;
39
39
  isContextPane?: boolean;
40
+ config?: BrandConfig;
40
41
  }
41
42
 
42
43
  interface PreviewPane {
@@ -65,6 +66,7 @@ const AddPaneNewPanel = ({
65
66
  ctx,
66
67
  isStoryFragment = false,
67
68
  isContextPane = false,
69
+ config,
68
70
  }: AddPaneNewPanelProps) => {
69
71
  const brand = useStore(brandColourStore);
70
72
  const hasAssemblyAI = useStore(hasAssemblyAIStore);
@@ -384,6 +386,7 @@ const AddPaneNewPanel = ({
384
386
  ownerId={nodeId}
385
387
  onComplete={handleApplyGeneratedPane}
386
388
  onCancel={() => setMode('template')}
389
+ config={config!}
387
390
  />
388
391
  </div>
389
392
  )}