astro-tractstack 2.0.9 → 2.0.10

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 (40) hide show
  1. package/dist/index.js +4 -6
  2. package/package.json +1 -1
  3. package/templates/css/custom.css +0 -6
  4. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +1 -1
  5. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +2 -1
  6. package/templates/src/components/codehooks/ProductGridSetup.tsx +4 -4
  7. package/templates/src/components/compositor/Compositor.tsx +335 -16
  8. package/templates/src/components/compositor/Node.tsx +86 -6
  9. package/templates/src/components/compositor/nodes/RenderChildren.tsx +3 -6
  10. package/templates/src/components/compositor/nodes/tagElements/NodeA.tsx +2 -1
  11. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +11 -19
  12. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +70 -17
  13. package/templates/src/components/compositor/nodes/tagElements/NodeButton.tsx +1 -1
  14. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +78 -8
  15. package/templates/src/components/edit/SettingsPanel.tsx +1 -1
  16. package/templates/src/components/edit/ToolMode.tsx +93 -22
  17. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +2 -1
  18. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +2 -1
  19. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +1 -1
  20. package/templates/src/components/edit/pane/PageGen_preview.tsx +2 -1
  21. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +9 -5
  22. package/templates/src/components/edit/state/SaveModal.tsx +84 -14
  23. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +2 -2
  24. package/templates/src/components/search/SearchModal.tsx +2 -1
  25. package/templates/src/components/search/SearchResults.tsx +2 -1
  26. package/templates/src/components/search/SearchWrapper.tsx +1 -1
  27. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +1 -1
  28. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +3 -5
  29. package/templates/src/components/storykeep/controls/content/BeliefTable.tsx +1 -1
  30. package/templates/src/components/storykeep/controls/content/MenuTable.tsx +1 -1
  31. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +1 -1
  32. package/templates/src/components/widgets/ImpressionWrapper.tsx +1 -1
  33. package/templates/src/hooks/useFormState.ts +3 -4
  34. package/templates/src/stores/nodes.ts +627 -21
  35. package/templates/src/stores/selection.ts +41 -0
  36. package/templates/src/types/compositorTypes.ts +1 -0
  37. package/templates/src/types/nodeProps.ts +12 -0
  38. package/templates/src/utils/compositor/nodesHelper.ts +2 -2
  39. package/utils/inject-files.ts +4 -6
  40. package/templates/src/components/compositor/elements/PlayButton.tsx +0 -19
@@ -8,7 +8,6 @@ import {
8
8
  transformStoryFragmentForSave,
9
9
  } from '@/utils/etl/index';
10
10
  import {
11
- fullContentMapStore,
12
11
  getPendingImageOperation,
13
12
  clearPendingImageOperation,
14
13
  pendingHomePageSlugStore,
@@ -55,6 +54,13 @@ const PROGRESS_PHASES = {
55
54
  FINALIZATION: 10,
56
55
  };
57
56
 
57
+ const INDETERMINATE_STAGES: SaveStage[] = [
58
+ 'SAVING_PANES',
59
+ 'LINKING_FILES',
60
+ 'PROCESSING_STYLES',
61
+ 'UPDATING_HOME_PAGE',
62
+ ];
63
+
58
64
  export default function SaveModal({
59
65
  show,
60
66
  slug,
@@ -72,6 +78,8 @@ export default function SaveModal({
72
78
  const [debugMessages, setDebugMessages] = useState<string[]>([]);
73
79
  const isSaving = useRef(false);
74
80
  const [isNavigating, setIsNavigating] = useState(false);
81
+ const [isIndeterminateStage, setIsIndeterminateStage] = useState(false);
82
+ const [ellipsis, setEllipsis] = useState('...');
75
83
  const isCreateMode = slug === 'create';
76
84
  const pendingHomePageSlug = pendingHomePageSlugStore.get();
77
85
  const goBackend =
@@ -83,6 +91,15 @@ export default function SaveModal({
83
91
  setDebugMessages((prev) => [...prev, `${timestamp}: ${message}`]);
84
92
  };
85
93
 
94
+ useEffect(() => {
95
+ if (isIndeterminateStage) {
96
+ const interval = setInterval(() => {
97
+ setEllipsis((prev) => (prev.length < 3 ? prev + '.' : '.'));
98
+ }, 500);
99
+ return () => clearInterval(interval);
100
+ }
101
+ }, [isIndeterminateStage]);
102
+
86
103
  useEffect(() => {
87
104
  if (!show) {
88
105
  setStage('PREPARING');
@@ -90,6 +107,7 @@ export default function SaveModal({
90
107
  setDebugMessages([]);
91
108
  setShowDebug(false);
92
109
  isSaving.current = false;
110
+ setIsIndeterminateStage(false);
93
111
  return;
94
112
  }
95
113
 
@@ -329,6 +347,7 @@ export default function SaveModal({
329
347
 
330
348
  if (dirtyPanes.length > 0) {
331
349
  setStage('SAVING_PANES');
350
+ setIsIndeterminateStage(true);
332
351
  setStageProgress({
333
352
  currentStep: 0,
334
353
  totalSteps: dirtyPanes.length,
@@ -420,6 +439,8 @@ export default function SaveModal({
420
439
  : 'Unknown bulk save error';
421
440
  addDebugMessage(`Bulk pane save failed: ${errorMsg}`);
422
441
  throw new Error(`Failed to save panes in bulk: ${errorMsg}`);
442
+ } finally {
443
+ setIsIndeterminateStage(false);
423
444
  }
424
445
 
425
446
  setStageProgress({
@@ -524,6 +545,7 @@ export default function SaveModal({
524
545
 
525
546
  if (dirtyPanes.length > 0) {
526
547
  setStage('LINKING_FILES');
548
+ setIsIndeterminateStage(true);
527
549
  setProgress(baseFinalizationProgress);
528
550
  addDebugMessage('Starting file-pane relationship linking...');
529
551
 
@@ -574,9 +596,11 @@ export default function SaveModal({
574
596
  } else {
575
597
  addDebugMessage('No file relationships to link');
576
598
  }
599
+ setIsIndeterminateStage(false);
577
600
  }
578
601
 
579
602
  setStage('PROCESSING_STYLES');
603
+ setIsIndeterminateStage(true);
580
604
  setProgress(
581
605
  baseFinalizationProgress + PROGRESS_PHASES.FINALIZATION / 2
582
606
  );
@@ -639,10 +663,13 @@ export default function SaveModal({
639
663
  error instanceof Error ? error.message : 'Unknown error';
640
664
  addDebugMessage(`Styles processing failed: ${errorMsg}`);
641
665
  throw new Error(`Failed to process styles: ${errorMsg}`);
666
+ } finally {
667
+ setIsIndeterminateStage(false);
642
668
  }
643
669
 
644
670
  if (pendingHomePageSlug) {
645
671
  setStage('UPDATING_HOME_PAGE');
672
+ setIsIndeterminateStage(true);
646
673
  setProgress(
647
674
  baseFinalizationProgress + (PROGRESS_PHASES.FINALIZATION - 2)
648
675
  );
@@ -697,6 +724,8 @@ export default function SaveModal({
697
724
  error instanceof Error ? error.message : 'Unknown error';
698
725
  addDebugMessage(`Home page update failed: ${errorMsg}`);
699
726
  throw new Error(`Failed to update home page: ${errorMsg}`);
727
+ } finally {
728
+ setIsIndeterminateStage(false);
700
729
  }
701
730
  }
702
731
 
@@ -711,6 +740,7 @@ export default function SaveModal({
711
740
  : 'Unknown error occurred';
712
741
  setError(errorMessage);
713
742
  addDebugMessage(`Save process failed: ${errorMessage}`);
743
+ setIsIndeterminateStage(false);
714
744
  } finally {
715
745
  isSaving.current = false;
716
746
  }
@@ -736,30 +766,47 @@ export default function SaveModal({
736
766
  const modeText = isContext ? 'Context Pane' : 'Story Fragment';
737
767
  const actionText = isCreateMode ? 'Creating' : 'Updating';
738
768
 
769
+ let description = '';
739
770
  switch (stage) {
740
771
  case 'PREPARING':
741
- return `Preparing ${actionText.toLowerCase()} ${modeText.toLowerCase()}...`;
772
+ description = `Preparing ${actionText.toLowerCase()} ${modeText.toLowerCase()}...`;
773
+ break;
742
774
  case 'SAVING_PENDING_FILES':
743
- return `Uploading files${getProgressText()}`;
775
+ description = `Uploading files${getProgressText()}`;
776
+ break;
744
777
  case 'PROCESSING_OG_IMAGES':
745
- return `Processing social images${getProgressText()}`;
778
+ description = `Processing social images${getProgressText()}`;
779
+ break;
746
780
  case 'SAVING_PANES':
747
- return `${actionText} pane content...${getProgressText()}`;
781
+ description = `${actionText} pane content...`;
782
+ break;
748
783
  case 'SAVING_STORY_FRAGMENTS':
749
- return `${actionText} story fragments...${getProgressText()}`;
784
+ description = `${actionText} story fragments...${getProgressText()}`;
785
+ break;
750
786
  case 'LINKING_FILES':
751
- return 'Linking file relationships...';
787
+ description = 'Linking file relationships...';
788
+ break;
752
789
  case 'PROCESSING_STYLES':
753
- return 'Processing styles...';
790
+ description = 'Processing styles...';
791
+ break;
754
792
  case 'UPDATING_HOME_PAGE':
755
- return 'Updating home page...';
793
+ description = 'Updating home page...';
794
+ break;
756
795
  case 'COMPLETED':
757
- return `${actionText} ${modeText.toLowerCase()} completed successfully!`;
796
+ description = `${actionText} ${modeText.toLowerCase()} completed successfully!`;
797
+ break;
758
798
  case 'ERROR':
759
- return `Error: ${error}`;
799
+ description = `Error: ${error}`;
800
+ break;
760
801
  default:
761
- return '';
802
+ description = '';
803
+ }
804
+
805
+ if (isIndeterminateStage && INDETERMINATE_STAGES.includes(stage)) {
806
+ return description.replace(/\.\.\.$/, '') + ellipsis;
762
807
  }
808
+
809
+ return description;
763
810
  };
764
811
 
765
812
  const handleOpenChange = (details: { open: boolean }) => {
@@ -832,6 +879,28 @@ export default function SaveModal({
832
879
  preventScroll={true}
833
880
  >
834
881
  <Portal>
882
+ <style>
883
+ {`
884
+ @keyframes stripes-move {
885
+ from { background-position: 40px 0; }
886
+ to { background-position: 0 0; }
887
+ }
888
+ .animate-stripes {
889
+ background-image: linear-gradient(
890
+ 45deg,
891
+ rgba(255, 255, 255, 0.15) 25%,
892
+ transparent 25%,
893
+ transparent 50%,
894
+ rgba(255, 255, 255, 0.15) 50%,
895
+ rgba(255, 255, 255, 0.15) 75%,
896
+ transparent 75%,
897
+ transparent
898
+ );
899
+ background-size: 40px 40px;
900
+ animation: stripes-move 2s linear infinite;
901
+ }
902
+ `}
903
+ </style>
835
904
  <Dialog.Backdrop
836
905
  className="fixed inset-0 bg-black bg-opacity-75"
837
906
  style={{ zIndex: 9005 }}
@@ -875,11 +944,12 @@ export default function SaveModal({
875
944
  </span>
876
945
  )}
877
946
  </div>
878
- <div className="h-2 w-full rounded-full bg-gray-200">
947
+ <div className="h-2 w-full overflow-hidden rounded-full bg-gray-200">
948
+ {' '}
879
949
  <div
880
950
  className={`h-2 rounded-full transition-all duration-300 ${
881
951
  stage === 'ERROR' ? 'bg-red-500' : 'bg-green-500'
882
- }`}
952
+ } ${isIndeterminateStage ? 'animate-stripes' : ''}`}
883
953
  style={{ width: `${progress}%` }}
884
954
  />
885
955
  </div>
@@ -517,7 +517,7 @@ export default function InteractiveDisclosureWidget({
517
517
  <button
518
518
  type="button"
519
519
  onClick={() => handleModeChange('belief')}
520
- className={`relative inline-flex items-center rounded-l-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 focus:z-10 ${
520
+ className={`relative inline-flex items-center rounded-l-md px-3 py-2 text-sm font-bold ring-1 ring-inset ring-gray-300 focus:z-10 ${
521
521
  mode === 'belief'
522
522
  ? 'bg-cyan-600 text-white'
523
523
  : 'bg-white text-gray-900 hover:bg-gray-50'
@@ -528,7 +528,7 @@ export default function InteractiveDisclosureWidget({
528
528
  <button
529
529
  type="button"
530
530
  onClick={() => handleModeChange('open')}
531
- className={`relative -ml-px inline-flex items-center rounded-r-md px-3 py-2 text-sm font-semibold ring-1 ring-inset ring-gray-300 focus:z-10 ${
531
+ className={`relative -ml-px inline-flex items-center rounded-r-md px-3 py-2 text-sm font-bold ring-1 ring-inset ring-gray-300 focus:z-10 ${
532
532
  mode === 'open'
533
533
  ? 'bg-cyan-600 text-white'
534
534
  : 'bg-white text-gray-900 hover:bg-gray-50'
@@ -8,7 +8,8 @@ import {
8
8
  } from 'react';
9
9
  import { Dialog } from '@ark-ui/react/dialog';
10
10
  import { Portal } from '@ark-ui/react/portal';
11
- import { MagnifyingGlassIcon, XMarkIcon } from '@heroicons/react/24/outline';
11
+ import MagnifyingGlassIcon from '@heroicons/react/24/outline/MagnifyingGlassIcon';
12
+ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
12
13
  import { useSearch } from '@/hooks/useSearch';
13
14
  import SearchResults from './SearchResults';
14
15
  import type {
@@ -1,6 +1,7 @@
1
1
  import { useState, useMemo } from 'react';
2
2
  import { Pagination } from '@ark-ui/react/pagination';
3
- import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
3
+ import ChevronRightIcon from '@heroicons/react/24/outline/ChevronRightIcon';
4
+ import ChevronLeftIcon from '@heroicons/react/24/outline/ChevronLeftIcon';
4
5
  import type { CategorizedResults, FTSResult } from '@/types/tractstack';
5
6
  import type { FullContentMapItem } from '@/types/tractstack';
6
7
  import {
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
2
+ import MagnifyingGlassIcon from '@heroicons/react/24/outline/MagnifyingGlassIcon';
3
3
  import { initSearch } from '@/utils/customHelpers';
4
4
  import SearchModal from './SearchModal';
5
5
  import type { FullContentMapItem } from '@/types/tractstack';
@@ -3,7 +3,7 @@ import type { ReactNode } from 'react';
3
3
  import { useStore } from '@nanostores/react';
4
4
  import { epinetCustomFilters } from '@/stores/analytics';
5
5
  import { classNames } from '@/utils/helpers';
6
- import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
6
+ import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon';
7
7
  import DashboardActivity from './Dashboard_Activity';
8
8
  import SankeyDiagram from '../codehooks/SankeyDiagram';
9
9
  import EpinetDurationSelector from '../codehooks/EpinetDurationSelector';
@@ -1,5 +1,8 @@
1
1
  import { useState, useEffect } from 'react';
2
2
  import { useStore } from '@nanostores/react';
3
+ import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
4
+ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
5
+ import LockClosedIcon from '@heroicons/react/24/outline/LockClosedIcon';
3
6
  import { useFormState } from '@/hooks/useFormState';
4
7
  import {
5
8
  convertToLocalState,
@@ -18,11 +21,6 @@ import {
18
21
  import StringInput from '@/components/form/StringInput';
19
22
  import EnumSelect from '@/components/form/EnumSelect';
20
23
  import UnsavedChangesBar from '@/components/form/UnsavedChangesBar';
21
- import {
22
- PlusIcon,
23
- XMarkIcon,
24
- LockClosedIcon,
25
- } from '@heroicons/react/24/outline';
26
24
  import type { BeliefNode, BeliefNodeState } from '@/types/tractstack';
27
25
 
28
26
  interface BeliefFormProps {
@@ -204,7 +204,7 @@ export default function BeliefTable({
204
204
  )}
205
205
  </div>
206
206
  ) : (
207
- <div className="overflow-x-auto">
207
+ <div className="overflow-hidden">
208
208
  <table className="min-w-full divide-y divide-gray-200">
209
209
  <thead className="bg-gray-50">
210
210
  <tr>
@@ -217,7 +217,7 @@ export default function MenuTable({
217
217
  )}
218
218
  </div>
219
219
  ) : (
220
- <div className="overflow-x-auto">
220
+ <div className="overflow-hidden">
221
221
  <table className="min-w-full divide-y divide-gray-200">
222
222
  <thead className="bg-gray-50">
223
223
  <tr>
@@ -241,7 +241,7 @@ const StoryFragmentTable = ({
241
241
  )}
242
242
  </div>
243
243
  ) : (
244
- <div className="overflow-x-auto">
244
+ <div className="overflow-hidden">
245
245
  <table className="min-w-full divide-y divide-gray-200">
246
246
  <thead className="bg-gray-50">
247
247
  <tr>
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect, useRef } from 'react';
2
- import { XMarkIcon } from '@heroicons/react/24/outline';
2
+ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
3
3
  import Impression from './Impression';
4
4
  import type { ImpressionNode } from '@/types/compositorTypes';
5
5
  import type { BrandConfig } from '@/types/tractstack';
@@ -37,12 +37,12 @@ export interface FormStateReturn<T> {
37
37
  updateField: (field: keyof T, value: any) => void;
38
38
  save: () => Promise<void>;
39
39
  cancel: () => void;
40
- resetToState: (newState: T) => void; // NEW: Reset form to new baseline state
40
+ resetToState: (newState: T) => void;
41
41
  isDirty: boolean;
42
42
  isValid: boolean;
43
43
  errors: FieldErrors;
44
- saveState: SaveState; // NEW: Track save operation state
45
- errorMessage: string | null; // NEW: Save error message
44
+ saveState: SaveState;
45
+ errorMessage: string | null;
46
46
  }
47
47
 
48
48
  /**
@@ -179,7 +179,6 @@ export function useFormState<T>(
179
179
  setErrorMessage(null);
180
180
  }, [originalState]);
181
181
 
182
- // NEW: Reset to new state (for external state updates)
183
182
  const resetToState = useCallback((newState: T) => {
184
183
  setOriginalState(newState);
185
184
  setState(newState);