astro-tractstack 2.0.13 → 2.0.15

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 (28) hide show
  1. package/dist/index.js +40 -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 +25 -8
  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_new.tsx +459 -561
  9. package/templates/src/components/edit/pane/AiPaneGenerator.tsx +19 -82
  10. package/templates/src/components/edit/pane/RestylePaneModal.tsx +573 -0
  11. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +140 -0
  12. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +105 -0
  13. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +395 -0
  14. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +205 -0
  15. package/templates/src/constants/prompts.json +3 -1
  16. package/templates/src/stores/selection.ts +4 -0
  17. package/templates/src/types/compositorTypes.ts +51 -1
  18. package/templates/src/types/tractstack.ts +36 -31
  19. package/templates/src/utils/aai/getTitleSlug.ts +1 -1
  20. package/templates/src/utils/api/brandConfig.ts +8 -2
  21. package/templates/src/utils/api/brandHelpers.ts +4 -0
  22. package/templates/src/utils/compositor/aiPaneParser.ts +32 -84
  23. package/templates/src/utils/compositor/designLibraryHelper.ts +416 -0
  24. package/templates/src/utils/compositor/processMarkdown.ts +1 -1
  25. package/utils/inject-files.ts +40 -0
  26. package/templates/src/components/edit/pane/PageGen.tsx +0 -485
  27. package/templates/src/components/edit/pane/PageGenSelector.tsx +0 -245
  28. package/templates/src/components/edit/pane/PageGenSpecial.tsx +0 -339
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"
@@ -436,6 +448,24 @@ async function w(t, e, c) {
436
448
  src: t("../templates/src/components/edit/pane/AiPanePreview.tsx"),
437
449
  dest: "src/components/edit/pane/AiPanePreview.tsx"
438
450
  },
451
+ {
452
+ src: t(
453
+ "../templates/src/components/edit/pane/steps/CopyInputStep.tsx"
454
+ ),
455
+ dest: "src/components/edit/pane/steps/CopyInputStep.tsx"
456
+ },
457
+ {
458
+ src: t(
459
+ "../templates/src/components/edit/pane/steps/DesignLibraryStep.tsx"
460
+ ),
461
+ dest: "src/components/edit/pane/steps/DesignLibraryStep.tsx"
462
+ },
463
+ {
464
+ src: t(
465
+ "../templates/src/components/edit/pane/steps/AiDesignStep.tsx"
466
+ ),
467
+ dest: "src/components/edit/pane/steps/AiDesignStep.tsx"
468
+ },
439
469
  {
440
470
  src: t(
441
471
  "../templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx"
@@ -624,6 +654,10 @@ async function w(t, e, c) {
624
654
  src: t("../templates/src/utils/compositor/typeGuards.ts"),
625
655
  dest: "src/utils/compositor/typeGuards.ts"
626
656
  },
657
+ {
658
+ src: t("../templates/src/utils/compositor/designLibraryHelper.ts"),
659
+ dest: "src/utils/compositor/designLibraryHelper.ts"
660
+ },
627
661
  {
628
662
  src: t("../templates/src/utils/compositor/domHelpers.ts"),
629
663
  dest: "src/utils/compositor/domHelpers.ts"
@@ -1345,6 +1379,12 @@ async function w(t, e, c) {
1345
1379
  src: t("../templates/src/components/edit/state/SaveModal.tsx"),
1346
1380
  dest: "src/components/edit/state/SaveModal.tsx"
1347
1381
  },
1382
+ {
1383
+ src: t(
1384
+ "../templates/src/components/edit/state/SaveToLibraryModal.tsx"
1385
+ ),
1386
+ dest: "src/components/edit/state/SaveToLibraryModal.tsx"
1387
+ },
1348
1388
  {
1349
1389
  src: t("../templates/src/components/edit/state/StylesMemory.tsx"),
1350
1390
  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.13",
3
+ "version": "2.0.15",
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,8 +30,8 @@ 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
- import PageCreationSelector from '@/components/edit/pane/PageGenSelector';
35
35
  import ConfigPanePanel from '@/components/edit/pane/ConfigPanePanel';
36
36
  import StoryFragmentConfigPanel from '@/components/edit/storyfragment/StoryFragmentConfigPanel';
37
37
  import StoryFragmentTitlePanel from '@/components/edit/storyfragment/StoryFragmentPanel_title';
@@ -45,6 +45,7 @@ import type {
45
45
  BaseNode,
46
46
  FlatNode,
47
47
  } from '@/types/compositorTypes';
48
+ import { PaneAddMode } from '@/types/compositorTypes';
48
49
  import { handleClickEventDefault } from '@/utils/compositor/handleClickEvent';
49
50
  import { selectionStore } from '@/stores/selection';
50
51
  import type { NodeProps, SelectionOrigin } from '@/types/nodeProps';
@@ -88,6 +89,25 @@ function parseCodeHook(node: BaseNode | FlatNode) {
88
89
  return null;
89
90
  }
90
91
 
92
+ // Helper component to safely set the panel mode for an empty page
93
+ const EmptyPageHandler = (props: NodeProps) => {
94
+ const ctx = getCtx(props);
95
+ useEffect(() => {
96
+ ctx.setPaneAddMode(props.nodeId, PaneAddMode.NEW);
97
+ }, []);
98
+
99
+ // Now that the mode is set, render the panel which will read it.
100
+ return (
101
+ <AddPanePanel
102
+ nodeId={props.nodeId}
103
+ first={true}
104
+ ctx={ctx}
105
+ isStoryFragment={true}
106
+ config={props.config!}
107
+ />
108
+ );
109
+ };
110
+
91
111
  const getElement = (
92
112
  node: BaseNode | FlatNode,
93
113
  props: NodeProps
@@ -95,7 +115,6 @@ const getElement = (
95
115
  if (node === undefined) return <></>;
96
116
  const isPreview = getCtx(props).rootNodeId.get() === `tmp`;
97
117
  const hasPanes = useStore(getCtx(props).hasPanes);
98
- const isTemplate = useStore(getCtx(props).isTemplate);
99
118
  const sharedProps = { ...props, nodeId: node.id };
100
119
  const type = getType(node);
101
120
 
@@ -151,12 +170,7 @@ const getElement = (
151
170
  </div>
152
171
  </div>
153
172
  ) : !hasPanes && sf.slug && sf.title && !isPreview ? (
154
- <PageCreationSelector
155
- nodeId={props.nodeId}
156
- ctx={getCtx(props)}
157
- isTemplate={isTemplate}
158
- config={props.config!}
159
- />
173
+ <EmptyPageHandler {...sharedProps} />
160
174
  ) : (
161
175
  <>
162
176
  <PanelVisibilityWrapper
@@ -180,6 +194,9 @@ const getElement = (
180
194
  const toolModeVal = getCtx(props).toolModeValStore.get().value;
181
195
  const paneNodes = getCtx(props).getChildNodeIDs(node.id);
182
196
  const paneNode = node as PaneNode;
197
+ if (toolModeVal === 'designLibrary') {
198
+ return <Pane_DesignLibrary {...sharedProps} />;
199
+ }
183
200
  if (paneNode.isContextPane) {
184
201
  if (!isPreview)
185
202
  getCtx(props).hasTitle.set(!(!paneNode.slug || !paneNode.title));
@@ -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 {