astro-tractstack 2.1.2 → 2.2.0

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 (131) hide show
  1. package/README.md +54 -266
  2. package/bin/create-tractstack.js +9 -6
  3. package/dist/index.js +109 -71
  4. package/package.json +4 -2
  5. package/templates/css/custom.css +5 -0
  6. package/templates/custom/minimal/CodeHook.astro +1 -0
  7. package/templates/custom/with-examples/CodeHook.astro +1 -0
  8. package/templates/icons/code.svg +18 -0
  9. package/templates/icons/li.svg +4 -0
  10. package/templates/icons/link.svg +22 -0
  11. package/templates/icons/p.svg +3 -0
  12. package/templates/src/client/app.js +80 -1
  13. package/templates/src/components/Footer.astro +1 -1
  14. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +6 -6
  15. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +3 -3
  16. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
  17. package/templates/src/components/codehooks/ListContentSetup.tsx +2 -2
  18. package/templates/src/components/codehooks/ProductCardSetup.tsx +1 -1
  19. package/templates/src/components/codehooks/ProductGridSetup.tsx +2 -2
  20. package/templates/src/components/codehooks/SandboxRegisterForm.tsx +3 -3
  21. package/templates/src/components/compositor/Compositor.tsx +25 -9
  22. package/templates/src/components/compositor/Node.tsx +168 -496
  23. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +1 -0
  24. package/templates/src/components/compositor/elements/SignUp.tsx +1 -1
  25. package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +2 -0
  26. package/templates/src/components/compositor/nodes/CreativePane.tsx +262 -0
  27. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +4 -6
  28. package/templates/src/components/compositor/nodes/GridLayout.tsx +4 -2
  29. package/templates/src/components/compositor/nodes/Markdown.tsx +18 -3
  30. package/templates/src/components/compositor/nodes/Pane.tsx +11 -5
  31. package/templates/src/components/compositor/nodes/RenderChildren.tsx +1 -1
  32. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +5 -5
  33. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +90 -42
  34. package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +2 -0
  35. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +27 -1
  36. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +10 -8
  37. package/templates/src/components/compositor/tools/NodeOverlay.tsx +224 -0
  38. package/templates/src/components/compositor/tools/PaneOverlay.tsx +122 -0
  39. package/templates/src/components/edit/Header.tsx +68 -9
  40. package/templates/src/components/edit/PanelSwitch.tsx +42 -4
  41. package/templates/src/components/edit/SettingsPanel.tsx +2 -3
  42. package/templates/src/components/edit/ToolMode.tsx +1 -31
  43. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +2 -2
  44. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +1 -1
  45. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +193 -659
  46. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +15 -82
  47. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +95 -45
  48. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +137 -49
  49. package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -1
  50. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +375 -0
  51. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +1 -23
  52. package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +327 -0
  53. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +267 -0
  54. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +371 -0
  55. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +201 -76
  56. package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +141 -0
  57. package/templates/src/components/edit/panels/CreativeImagePanel.tsx +435 -0
  58. package/templates/src/components/edit/panels/CreativeLinkPanel.tsx +110 -0
  59. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +1 -1
  60. package/templates/src/components/edit/panels/StyleParentPanel.tsx +118 -126
  61. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +3 -2
  62. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +1 -0
  63. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +3 -1
  64. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +3 -1
  65. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +1 -1
  66. package/templates/src/components/edit/state/SaveModal.tsx +19 -787
  67. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +2 -2
  68. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +1 -1
  69. package/templates/src/components/edit/widgets/BunnyWidget.tsx +5 -5
  70. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +1 -1
  71. package/templates/src/components/edit/widgets/SignupWidget.tsx +1 -1
  72. package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +1 -1
  73. package/templates/src/components/fields/ArtpackImage.tsx +11 -3
  74. package/templates/src/components/fields/BackgroundImage.tsx +8 -0
  75. package/templates/src/components/fields/BackgroundImageWrapper.tsx +15 -9
  76. package/templates/src/components/fields/ImageUpload.tsx +6 -0
  77. package/templates/src/components/form/ActionBuilderField.tsx +15 -5
  78. package/templates/src/components/form/ActionBuilderSlugSelector.tsx +1 -1
  79. package/templates/src/components/form/ColorPicker.tsx +1 -1
  80. package/templates/src/components/form/EnumSelect.tsx +1 -1
  81. package/templates/src/components/form/NumberInput.tsx +1 -1
  82. package/templates/src/components/form/StringArrayInput.tsx +1 -1
  83. package/templates/src/components/form/StringInput.tsx +1 -1
  84. package/templates/src/components/form/UnsavedChangesBar.tsx +1 -1
  85. package/templates/src/components/form/advanced/APIConfigSection.tsx +2 -2
  86. package/templates/src/components/form/advanced/AuthConfigSection.tsx +2 -2
  87. package/templates/src/components/profile/ProfileCreate.tsx +1 -1
  88. package/templates/src/components/profile/ProfileEdit.tsx +1 -1
  89. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +2 -2
  90. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +1 -1
  91. package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +2 -2
  92. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +1 -1
  93. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +6 -6
  94. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +1 -1
  95. package/templates/src/components/storykeep/controls/content/PaneTable.tsx +358 -0
  96. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -1
  97. package/templates/src/constants/prompts.json +18 -10
  98. package/templates/src/constants.ts +3 -0
  99. package/templates/src/hooks/usePaneFragments.ts +60 -0
  100. package/templates/src/lib/session.ts +71 -16
  101. package/templates/src/pages/[...slug].astro +5 -46
  102. package/templates/src/pages/api/css.ts +149 -0
  103. package/templates/src/pages/context/[...contextSlug].astro +1 -0
  104. package/templates/src/pages/maint.astro +1 -1
  105. package/templates/src/pages/storykeep/login.astro +2 -2
  106. package/templates/src/stores/nodes.ts +162 -49
  107. package/templates/src/stores/orphanAnalysis.ts +6 -30
  108. package/templates/src/stores/previews.ts +7 -0
  109. package/templates/src/stores/storykeep.ts +0 -8
  110. package/templates/src/types/compositorTypes.ts +53 -10
  111. package/templates/src/utils/compositor/aiGeneration.ts +93 -0
  112. package/templates/src/utils/compositor/allowInsert.ts +2 -0
  113. package/templates/src/utils/compositor/htmlAst.ts +704 -0
  114. package/templates/src/utils/compositor/nodesHelper.ts +281 -102
  115. package/templates/src/utils/compositor/savePipeline.ts +893 -0
  116. package/templates/src/utils/etl/index.ts +3 -0
  117. package/templates/src/utils/etl/transformer.ts +10 -0
  118. package/templates/src/utils/helpers.ts +101 -0
  119. package/utils/inject-files.ts +100 -62
  120. package/templates/icons/text.svg +0 -6
  121. package/templates/src/components/compositor/NodeWithGuid.tsx +0 -69
  122. package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +0 -33
  123. package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +0 -56
  124. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +0 -269
  125. package/templates/src/components/compositor/nodes/Pane_eraser.tsx +0 -186
  126. package/templates/src/components/compositor/nodes/Pane_layout.tsx +0 -79
  127. package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +0 -26
  128. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +0 -61
  129. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +0 -120
  130. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +0 -62
  131. package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +0 -26
@@ -1,5 +1,5 @@
1
1
  import { useState, useMemo, useEffect } from 'react';
2
- import { CheckIcon } from '@heroicons/react/20/solid';
2
+ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
3
3
  import { savePaneToLibrary } from '@/utils/compositor/designLibraryHelper';
4
4
  import { convertToBackendFormat } from '@/utils/api/brandHelpers';
5
5
  import StringInput from '@/components/form/StringInput';
@@ -165,7 +165,7 @@ export function SaveToLibraryModal({
165
165
  id="category-select"
166
166
  value={selectedCategory}
167
167
  onChange={(e) => setSelectedCategory(e.target.value)}
168
- className="sm:text-sm mt-1 block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-cyan-500 focus:outline-none focus:ring-cyan-500"
168
+ className="mt-1 block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 md:text-sm"
169
169
  >
170
170
  {categories.map((cat) => (
171
171
  <option key={cat} value={cat}>
@@ -282,7 +282,7 @@ const StoryFragmentMenuPanel = ({
282
282
  />
283
283
  </Combobox.Trigger>
284
284
  </div>
285
- <Combobox.Content className="sm:text-sm absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
285
+ <Combobox.Content className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
286
286
  {filteredMenus.length === 0 && query !== '' ? (
287
287
  <div className="relative cursor-default select-none px-4 py-2 text-mydarkgrey">
288
288
  Nothing found.
@@ -382,7 +382,7 @@ function BunnyWidget({ node, onUpdate }: BunnyWidgetProps) {
382
382
  </Dialog.Title>
383
383
  </div>
384
384
  <div className="flex-1 overflow-y-auto p-4">
385
- <div className="lg:grid-cols-2 grid grid-cols-1 gap-4">
385
+ <div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
386
386
  <div className="rounded-lg border border-gray-200 bg-white shadow-sm">
387
387
  <div className="flex items-center justify-between border-b border-gray-200 p-3">
388
388
  <h3 className="text-base font-bold text-gray-900">
@@ -433,7 +433,7 @@ function BunnyWidget({ node, onUpdate }: BunnyWidgetProps) {
433
433
  onBlur={() =>
434
434
  handleUpdate(videoId, title, chapters)
435
435
  }
436
- className={`sm:text-sm mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm ${formErrors[`title-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
436
+ className={`mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm md:text-sm ${formErrors[`title-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
437
437
  />
438
438
  </div>
439
439
  <div>
@@ -451,7 +451,7 @@ function BunnyWidget({ node, onUpdate }: BunnyWidgetProps) {
451
451
  onBlur={() =>
452
452
  handleUpdate(videoId, title, chapters)
453
453
  }
454
- className="sm:text-sm mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
454
+ className="mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 md:text-sm"
455
455
  />
456
456
  </div>
457
457
  <div className="grid grid-cols-2 gap-2">
@@ -472,7 +472,7 @@ function BunnyWidget({ node, onUpdate }: BunnyWidgetProps) {
472
472
  onBlur={() =>
473
473
  handleUpdate(videoId, title, chapters)
474
474
  }
475
- className={`sm:text-sm mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm ${formErrors[`startTime-${index}`] || formErrors[`overlap-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
475
+ className={`mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm md:text-sm ${formErrors[`startTime-${index}`] || formErrors[`overlap-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
476
476
  />
477
477
  </div>
478
478
  <div>
@@ -491,7 +491,7 @@ function BunnyWidget({ node, onUpdate }: BunnyWidgetProps) {
491
491
  onBlur={() =>
492
492
  handleUpdate(videoId, title, chapters)
493
493
  }
494
- className={`sm:text-sm mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm ${formErrors[`endTime-${index}`] || formErrors[`overlap-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
494
+ className={`mt-1 block w-full rounded-md border-gray-300 px-2 py-1 shadow-sm md:text-sm ${formErrors[`endTime-${index}`] || formErrors[`overlap-${index}`] ? 'border-red-300' : 'focus:border-cyan-500 focus:ring-cyan-500'}`}
495
495
  />
496
496
  </div>
497
497
  </div>
@@ -621,7 +621,7 @@ export default function InteractiveDisclosureWidget({
621
621
  <h3 className="font-bold text-gray-800">
622
622
  Widget Styles
623
623
  </h3>
624
- <div className="sm:grid-cols-3 grid grid-cols-1 gap-4">
624
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
625
625
  <div>
626
626
  <ColorPickerCombo
627
627
  title="Background Color"
@@ -135,7 +135,7 @@ function SignupWidget({ node, onUpdate }: SignupWidgetProps) {
135
135
  />
136
136
  </Combobox.Trigger>
137
137
  </div>
138
- <Combobox.Content className="sm:text-sm absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
138
+ <Combobox.Content className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
139
139
  {collection.items.length === 0 ? (
140
140
  <div className="relative cursor-default select-none px-4 py-2 text-gray-700">
141
141
  Nothing found.
@@ -270,7 +270,7 @@ export default function ActionBuilderTimeSelector({
270
270
  </Combobox.Trigger>
271
271
  </div>
272
272
 
273
- <Combobox.Content className="sm:text-sm absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
273
+ <Combobox.Content className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
274
274
  {availableVideos.length > 0 ? (
275
275
  collection.items.length === 0 ? (
276
276
  <div className="relative cursor-default select-none px-4 py-2 text-gray-700">
@@ -9,7 +9,7 @@ import SwatchIcon from '@heroicons/react/24/outline/SwatchIcon';
9
9
  import ChevronUpDownIcon from '@heroicons/react/24/outline/ChevronUpDownIcon';
10
10
  import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
11
11
  import { getCtx } from '@/stores/nodes';
12
- import { hasArtpacksStore } from '@/stores/storykeep';
12
+ import { hasArtpacksStore, settingsPanelStore } from '@/stores/storykeep';
13
13
  import { cloneDeep } from '@/utils/helpers';
14
14
  import type { ArtpackImageNode, PaneNode } from '@/types/compositorTypes';
15
15
 
@@ -157,7 +157,15 @@ const ArtpackImage = ({ paneId, onUpdate }: ArtpackImageProps) => {
157
157
  ctx.modifyNodes([updatedPaneNode]);
158
158
  setArtpackNode(updatedArtNode);
159
159
  setIsModalOpen(false);
160
+ const currentSignal = settingsPanelStore.get();
161
+ if (currentSignal) {
162
+ settingsPanelStore.set({
163
+ ...currentSignal,
164
+ editLock: Date.now(),
165
+ });
166
+ }
160
167
  onUpdate();
168
+ ctx.notifyNode('root');
161
169
  };
162
170
 
163
171
  const handleRemoveImage = () => {
@@ -395,7 +403,7 @@ const ArtpackImage = ({ paneId, onUpdate }: ArtpackImageProps) => {
395
403
  />
396
404
  </Combobox.Trigger>
397
405
  </div>
398
- <Combobox.Content className="sm:text-sm absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
406
+ <Combobox.Content className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
399
407
  {collection.items.length === 0 ? (
400
408
  <div className="relative cursor-default select-none px-4 py-2 text-gray-700">
401
409
  No collections found.
@@ -429,7 +437,7 @@ const ArtpackImage = ({ paneId, onUpdate }: ArtpackImageProps) => {
429
437
  <label className="mb-2 block text-sm font-bold text-mydarkgrey">
430
438
  Select Image from {selectedCollection}
431
439
  </label>
432
- <div className="sm:grid-cols-3 grid grid-cols-2 gap-4 p-2 md:grid-cols-4">
440
+ <div className="grid grid-cols-2 gap-4 p-2 md:grid-cols-3">
433
441
  {availableImages.map((image) => (
434
442
  <div
435
443
  key={image}
@@ -9,6 +9,7 @@ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
9
9
  import ChevronUpDownIcon from '@heroicons/react/24/outline/ChevronUpDownIcon';
10
10
  import { getCtx } from '@/stores/nodes';
11
11
  import { cloneDeep } from '@/utils/helpers';
12
+ import { settingsPanelStore } from '@/stores/storykeep';
12
13
  import type {
13
14
  ImageFileNode,
14
15
  BgImageNode,
@@ -166,6 +167,13 @@ const BackgroundImage = ({ paneId, onUpdate }: BackgroundImageProps) => {
166
167
  ctx.modifyNodes([updatedPaneNode]);
167
168
  setBgImageNode(updatedBgNode);
168
169
  setLocalAltDescription(updatedBgNode.alt || '');
170
+ const currentSignal = settingsPanelStore.get();
171
+ if (currentSignal) {
172
+ settingsPanelStore.set({
173
+ ...currentSignal,
174
+ editLock: Date.now(),
175
+ });
176
+ }
169
177
  onUpdate();
170
178
  ctx.notifyNode('root');
171
179
  };
@@ -17,6 +17,7 @@ import { isArtpackImageNode } from '@/utils/compositor/typeGuards';
17
17
 
18
18
  export interface BackgroundImageWrapperProps {
19
19
  paneId: string;
20
+ isGrid: boolean;
20
21
  }
21
22
 
22
23
  const CheckIcon = () => (
@@ -49,7 +50,10 @@ const ChevronDownIcon = () => (
49
50
  </svg>
50
51
  );
51
52
 
52
- const BackgroundImageWrapper = ({ paneId }: BackgroundImageWrapperProps) => {
53
+ const BackgroundImageWrapper = ({
54
+ paneId,
55
+ isGrid,
56
+ }: BackgroundImageWrapperProps) => {
53
57
  const ctx = getCtx();
54
58
  const allNodes = useStore(ctx.allNodes);
55
59
  const $artpacks = useStore(hasArtpacksStore);
@@ -113,13 +117,15 @@ const BackgroundImageWrapper = ({ paneId }: BackgroundImageWrapperProps) => {
113
117
  const position = bgNode?.position || 'background';
114
118
  const size = bgNode?.size || 'equal';
115
119
 
116
- const positionOptions = [
117
- { label: 'Background', value: 'background' },
118
- { label: 'Left', value: 'left' },
119
- { label: 'Right', value: 'right' },
120
- { label: 'Left Bleed', value: 'leftBleed' },
121
- { label: 'Right Bleed', value: 'rightBleed' },
122
- ];
120
+ const positionOptions = isGrid
121
+ ? [{ label: 'Background', value: 'background' }]
122
+ : [
123
+ { label: 'Background', value: 'background' },
124
+ { label: 'Left', value: 'left' },
125
+ { label: 'Right', value: 'right' },
126
+ { label: 'Left Bleed', value: 'leftBleed' },
127
+ { label: 'Right Bleed', value: 'rightBleed' },
128
+ ];
123
129
 
124
130
  const collection = useMemo(
125
131
  () =>
@@ -163,7 +169,7 @@ const BackgroundImageWrapper = ({ paneId }: BackgroundImageWrapperProps) => {
163
169
  allowNull={true}
164
170
  />
165
171
  {!bgNode && (
166
- <div className="sm:flex-row flex flex-col gap-4">
172
+ <div className="flex flex-col gap-4 md:flex-row">
167
173
  <div className="flex-1">
168
174
  <h4 className="mb-2 text-sm font-bold text-gray-700">
169
175
  Background Image
@@ -21,6 +21,8 @@ export interface ImageParams {
21
21
  srcSet?: string;
22
22
  altDescription: string;
23
23
  base64Data?: string;
24
+ collection?: string | null;
25
+ image?: string | null;
24
26
  }
25
27
 
26
28
  interface ImageUploadProps {
@@ -144,6 +146,8 @@ export const ImageUpload = ({
144
146
  src: selected.src,
145
147
  srcSet: selected.srcSet,
146
148
  altDescription: selected.altDescription || missingAlt,
149
+ collection: null,
150
+ image: null,
147
151
  });
148
152
 
149
153
  // Update the node for existing files
@@ -192,6 +196,8 @@ export const ImageUpload = ({
192
196
  fileId: ulid(),
193
197
  src: base64, // Use base64 as src for immediate display
194
198
  altDescription: defaultAlt,
199
+ collection: null,
200
+ image: null,
195
201
  });
196
202
 
197
203
  // Update the node with base64Data
@@ -15,6 +15,7 @@ interface ActionBuilderFieldProps {
15
15
  label?: string;
16
16
  error?: string;
17
17
  slug?: string;
18
+ restriction?: 'navigation' | 'action';
18
19
  }
19
20
 
20
21
  const parseActionLisp = (
@@ -42,6 +43,7 @@ export default function ActionBuilderField({
42
43
  label = 'Action',
43
44
  error,
44
45
  slug,
46
+ restriction,
45
47
  }: ActionBuilderFieldProps) {
46
48
  const [command, setCommand] = useState<ActionCommand>('goto');
47
49
  const [params, setParams] = useState('');
@@ -109,6 +111,8 @@ export default function ActionBuilderField({
109
111
  };
110
112
 
111
113
  const handleCommandChange = (newCommand: ActionCommand) => {
114
+ if (restriction === 'navigation' && newCommand !== 'goto') return;
115
+ if (restriction === 'action' && newCommand === 'goto') return;
112
116
  setCommand(newCommand);
113
117
  setParams('');
114
118
  };
@@ -183,11 +187,17 @@ export default function ActionBuilderField({
183
187
  onChange={(e) => handleCommandChange(e.target.value as ActionCommand)}
184
188
  className="w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-cyan-700 focus:ring-cyan-700"
185
189
  >
186
- {Object.entries(ACTION_COMMANDS).map(([key, data]) => (
187
- <option key={key} value={key}>
188
- {data.name}
189
- </option>
190
- ))}
190
+ {Object.entries(ACTION_COMMANDS)
191
+ .filter(([key]) => {
192
+ if (restriction === 'navigation') return key === 'goto';
193
+ if (restriction === 'action') return key !== 'goto';
194
+ return true;
195
+ })
196
+ .map(([key, data]) => (
197
+ <option key={key} value={key}>
198
+ {data.name}
199
+ </option>
200
+ ))}
191
201
  </select>
192
202
  <p className="mt-1 text-sm text-gray-500">
193
203
  {ACTION_COMMANDS[command]?.description}
@@ -151,7 +151,7 @@ const ActionBuilderSlugSelector = ({
151
151
  </Combobox.Trigger>
152
152
  </div>
153
153
 
154
- <Combobox.Content className="sm:text-sm absolute z-10 mt-1 max-h-60 w-full max-w-md overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
154
+ <Combobox.Content className="absolute z-10 mt-1 max-h-60 w-full max-w-md overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
155
155
  {filteredItems.length === 0 ? (
156
156
  <div className="relative cursor-default select-none px-4 py-2 text-gray-700">
157
157
  {query ? 'No matching items found' : 'No items available'}
@@ -127,7 +127,7 @@ const ColorPicker = ({
127
127
  disabled
128
128
  ? 'cursor-not-allowed bg-gray-50 text-gray-500'
129
129
  : 'bg-white',
130
- 'sm:text-sm sm:leading-6'
130
+ 'md:text-sm md:leading-6'
131
131
  )}
132
132
  />
133
133
  </div>
@@ -56,7 +56,7 @@ const EnumSelect = ({
56
56
  'block w-fit min-w-48 rounded-md border-0 px-3 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset',
57
57
  error ? 'ring-red-300 focus:ring-red-500' : 'focus:ring-cyan-600',
58
58
  disabled ? 'cursor-not-allowed bg-gray-50 text-gray-500' : 'bg-white',
59
- 'sm:text-sm sm:leading-6'
59
+ 'md:text-sm md:leading-6'
60
60
  )}
61
61
  >
62
62
  {(allowEmpty || !value) && (
@@ -83,7 +83,7 @@ const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
83
83
  disabled
84
84
  ? 'cursor-not-allowed bg-gray-50 text-gray-500'
85
85
  : 'bg-white',
86
- 'sm:text-sm sm:leading-6'
86
+ 'md:text-sm md:leading-6'
87
87
  )}
88
88
  />
89
89
  {error && (
@@ -140,7 +140,7 @@ const StringArrayInput = ({
140
140
  className={classNames(
141
141
  'block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset',
142
142
  error ? 'ring-red-300 focus:ring-red-500' : 'focus:ring-cyan-600',
143
- 'sm:text-sm sm:leading-6'
143
+ 'md:text-sm md:leading-6'
144
144
  )}
145
145
  />
146
146
  )}
@@ -70,7 +70,7 @@ const StringInput = forwardRef<HTMLInputElement, StringInputProps>(
70
70
  disabled
71
71
  ? 'cursor-not-allowed bg-gray-50 text-gray-500'
72
72
  : 'bg-white',
73
- 'sm:text-sm sm:leading-6'
73
+ 'md:text-sm md:leading-6'
74
74
  )}
75
75
  />
76
76
  {error && (
@@ -147,7 +147,7 @@ export default function UnsavedChangesBar<T>({
147
147
  <div className="absolute inset-0 bg-black/10 backdrop-blur-sm" />
148
148
 
149
149
  {/* Main content bar */}
150
- <div className="sm:px-6 lg:px-8 relative mx-auto max-w-7xl px-4 py-4">
150
+ <div className="relative mx-auto max-w-7xl px-4 py-4 md:px-6 xl:px-8">
151
151
  <div
152
152
  className={`flex items-center justify-between rounded-lg border px-4 py-3 shadow-lg ${styling.bgColor} ${styling.borderColor}`}
153
153
  >
@@ -19,8 +19,8 @@ export default function APIConfigSection({
19
19
  const aaiConfigured = status?.aaiAPIKeySet;
20
20
 
21
21
  return (
22
- <div className="sm:rounded-lg bg-white shadow">
23
- <div className="sm:p-6 px-4 py-5">
22
+ <div className="bg-white shadow md:rounded-lg">
23
+ <div className="px-4 py-5 md:p-6">
24
24
  <div className="flex items-center justify-between">
25
25
  <h3 className="text-base font-bold leading-6 text-gray-900">
26
26
  API Configuration
@@ -20,8 +20,8 @@ export default function AuthConfigSection({
20
20
  const editorConfigured = status?.editorPasswordSet;
21
21
 
22
22
  return (
23
- <div className="sm:rounded-lg bg-white shadow">
24
- <div className="sm:p-6 px-4 py-5">
23
+ <div className="bg-white shadow md:rounded-lg">
24
+ <div className="px-4 py-5 md:p-6">
25
25
  <h3 className="text-base font-bold leading-6 text-gray-900">
26
26
  Authentication Configuration
27
27
  </h3>
@@ -332,7 +332,7 @@ export const ProfileCreate = ({ onSuccess, onError }: ProfileCreateProps) => {
332
332
 
333
333
  <Portal>
334
334
  <Select.Positioner>
335
- <Select.Content className="sm:text-sm relative z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
335
+ <Select.Content className="relative z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
336
336
  {contactPersona.map((item) => (
337
337
  <Select.Item
338
338
  key={item.id}
@@ -294,7 +294,7 @@ export const ProfileEdit = ({ onSuccess, onError }: ProfileEditProps) => {
294
294
 
295
295
  <Portal>
296
296
  <Select.Positioner>
297
- <Select.Content className="sm:text-sm relative z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
297
+ <Select.Content className="relative z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none md:text-sm">
298
298
  {contactPersona.map((item) => (
299
299
  <Select.Item
300
300
  key={item.id}
@@ -100,8 +100,8 @@ export default function StoryKeepDashboard_Advanced({
100
100
  const icon = status?.tursoEnabled ? '☁️' : '📁';
101
101
 
102
102
  return (
103
- <div className="sm:rounded-lg bg-white shadow">
104
- <div className="sm:p-6 px-4 py-5">
103
+ <div className="bg-white shadow md:rounded-lg">
104
+ <div className="px-4 py-5 md:p-6">
105
105
  <h3 className="text-base font-bold leading-6 text-gray-900">
106
106
  Database Configuration
107
107
  </h3>
@@ -288,7 +288,7 @@ export default function BeliefForm({
288
288
  onChange={(e) => handleCustomValueChange(e.target.value)}
289
289
  onKeyDown={handleKeyDown}
290
290
  placeholder="Enter custom value"
291
- className={`sm:text-sm sm:leading-6 block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset placeholder:text-gray-400 focus:ring-2 focus:ring-inset ${
291
+ className={`block w-full rounded-md border-0 px-3 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset placeholder:text-gray-400 focus:ring-2 focus:ring-inset md:text-sm md:leading-6 ${
292
292
  customValueError
293
293
  ? 'ring-red-500 focus:ring-red-600'
294
294
  : 'ring-gray-300 focus:ring-cyan-600'
@@ -47,7 +47,7 @@ const ContentSummary = ({ fullContentMap }: ContentSummaryProps) => {
47
47
  <h2 className="mb-4 text-xl font-bold text-gray-900">
48
48
  Content Overview
49
49
  </h2>
50
- <div className="sm:grid-cols-4 lg:grid-cols-8 grid grid-cols-2 gap-4">
50
+ <div className="grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-8">
51
51
  <div className="text-center">
52
52
  <div className="text-2xl font-bold text-cyan-600">
53
53
  {contentStats.storyfragments}
@@ -89,7 +89,7 @@ const ContentSummary = ({ fullContentMap }: ContentSummaryProps) => {
89
89
  {/* Quick Actions */}
90
90
  <div className="rounded-lg bg-white p-6 shadow">
91
91
  <h2 className="mb-4 text-xl font-bold text-gray-900">Quick Actions</h2>
92
- <div className="sm:grid-cols-4 grid grid-cols-2 gap-4">
92
+ <div className="grid grid-cols-2 gap-4 md:grid-cols-4">
93
93
  <a
94
94
  href="/create/edit"
95
95
  className="rounded-lg border-2 border-dashed border-gray-300 p-4 text-center hover:border-cyan-500 hover:text-cyan-600"
@@ -195,7 +195,7 @@ const KnownResourceTable = ({
195
195
  <td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
196
196
  {fieldCount} {fieldCount === 1 ? 'field' : 'fields'}
197
197
  </td>
198
- <td className="sm:pr-6 relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-bold">
198
+ <td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-bold md:pr-6">
199
199
  <div className="flex items-center justify-end space-x-2">
200
200
  {/* Edit button */}
201
201
  <button
@@ -11,6 +11,7 @@ import { getBeliefById } from '@/utils/api/beliefConfig';
11
11
  import { getResource } from '@/utils/api/resourceConfig';
12
12
  import ContentSummary from './ContentSummary';
13
13
  import StoryFragmentTable from './StoryFragmentTable';
14
+ import PaneTable from './PaneTable';
14
15
  import MenuTable from './MenuTable';
15
16
  import MenuForm from './MenuForm';
16
17
  import BeliefTable from './BeliefTable';
@@ -45,7 +46,7 @@ const staticContentManagementTabs: ContentManagementTab[] = [
45
46
  { id: 'menus', name: 'Menus' },
46
47
  { id: 'resources', name: 'Resources' },
47
48
  { id: 'beliefs', name: 'Beliefs' },
48
- //{ id: 'panes', name: 'Panes' },
49
+ { id: 'panes', name: 'Panes' },
49
50
  //{ id: 'epinets', name: 'Epinets' },
50
51
  //{ id: 'files', name: 'Files' },
51
52
  ];
@@ -313,11 +314,10 @@ const ManageContent = ({
313
314
 
314
315
  case 'panes':
315
316
  return (
316
- <div className="rounded-lg bg-white p-8 text-center shadow">
317
- <div className="text-lg text-gray-600">
318
- Panes management - Coming soon
319
- </div>
320
- </div>
317
+ <PaneTable
318
+ fullContentMap={currentContentMap}
319
+ onRefresh={refreshData}
320
+ />
321
321
  );
322
322
 
323
323
  case 'menus':
@@ -212,7 +212,7 @@ export default function MenuForm({
212
212
  </div>
213
213
  </div>
214
214
 
215
- <div className="sm:grid-cols-2 grid grid-cols-1 gap-4">
215
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
216
216
  <StringInput
217
217
  label="Link Name"
218
218
  value={link.name}