astro-tractstack 2.0.0-rc.9 → 2.0.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 (142) hide show
  1. package/LICENSE +8 -97
  2. package/README.md +7 -5
  3. package/bin/create-tractstack.js +31 -8
  4. package/dist/index.js +106 -29
  5. package/package.json +10 -5
  6. package/templates/css/frontend.css +1 -1
  7. package/templates/custom/minimal/CodeHook.astro +13 -12
  8. package/templates/custom/minimal/CustomRoutes.astro +25 -31
  9. package/templates/custom/with-examples/CodeHook.astro +22 -11
  10. package/templates/custom/with-examples/CustomRoutes.astro +4 -8
  11. package/templates/custom/with-examples/ProductCard.astro +29 -0
  12. package/templates/custom/with-examples/ProductCardWrapper.astro +43 -0
  13. package/templates/custom/with-examples/ProductGrid.astro +64 -0
  14. package/templates/custom/with-examples/pages/Collections.astro +58 -98
  15. package/templates/gitignore +42 -0
  16. package/templates/prettierignore +5 -0
  17. package/templates/prettierrc +19 -0
  18. package/templates/src/client/app.js +127 -0
  19. package/templates/src/client/htmx.min.js +3519 -0
  20. package/templates/src/client/view.js +429 -0
  21. package/templates/src/components/Footer.astro +4 -9
  22. package/templates/src/components/Header.astro +67 -60
  23. package/templates/src/components/Menu.tsx +188 -52
  24. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
  25. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +9 -13
  26. package/templates/src/components/codehooks/EpinetTableView.tsx +11 -7
  27. package/templates/src/components/codehooks/EpinetWrapper.tsx +1 -0
  28. package/templates/src/components/codehooks/FeaturedArticle.astro +105 -0
  29. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +318 -0
  30. package/templates/src/components/codehooks/ListContent.astro +32 -162
  31. package/templates/src/components/codehooks/ListContentSetup.tsx +43 -138
  32. package/templates/src/components/codehooks/ProductCardSetup.tsx +152 -0
  33. package/templates/src/components/codehooks/ProductGridSetup.tsx +274 -0
  34. package/templates/src/components/codehooks/SearchWidget.tsx +453 -0
  35. package/templates/src/components/compositor/Node.tsx +3 -6
  36. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +21 -11
  37. package/templates/src/components/compositor/elements/BunnyVideo.tsx +21 -20
  38. package/templates/src/components/compositor/nodes/Pane.tsx +51 -21
  39. package/templates/src/components/compositor/nodes/RenderChildren.tsx +6 -1
  40. package/templates/src/components/compositor/nodes/Widget.tsx +16 -2
  41. package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +155 -0
  42. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +20 -1
  43. package/templates/src/components/edit/Header.tsx +10 -4
  44. package/templates/src/components/edit/PanelSwitch.tsx +11 -7
  45. package/templates/src/components/edit/SettingsPanel.tsx +29 -18
  46. package/templates/src/components/edit/ToolBar.tsx +1 -28
  47. package/templates/src/components/edit/ToolMode.tsx +45 -32
  48. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +12 -2
  49. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +8 -2
  50. package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +1 -1
  51. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +17 -27
  52. package/templates/src/components/edit/pane/PageGenSelector.tsx +16 -16
  53. package/templates/src/components/edit/pane/PageGenSpecial.tsx +26 -49
  54. package/templates/src/components/edit/pane/PageGen_preview.tsx +17 -2
  55. package/templates/src/components/edit/pane/PanePanel_path.tsx +2 -4
  56. package/templates/src/components/edit/pane/PanePanel_title.tsx +243 -76
  57. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +17 -19
  58. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +48 -37
  59. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +60 -55
  60. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +56 -50
  61. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +54 -47
  62. package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +54 -44
  63. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
  64. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +54 -40
  65. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +3 -3
  66. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +56 -49
  67. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +14 -5
  68. package/templates/src/components/edit/state/SaveModal.tsx +316 -169
  69. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +1 -1
  70. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +56 -55
  71. package/templates/src/components/edit/widgets/BunnyWidget.tsx +538 -59
  72. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +656 -0
  73. package/templates/src/components/edit/widgets/ToggleWidget.tsx +9 -16
  74. package/templates/src/components/fields/ArtpackImage.tsx +4 -1
  75. package/templates/src/components/fields/BackgroundImage.tsx +1 -1
  76. package/templates/src/components/fields/BackgroundImageWrapper.tsx +127 -35
  77. package/templates/src/components/fields/ColorPickerCombo.tsx +66 -62
  78. package/templates/src/components/fields/ImageUpload.tsx +1 -1
  79. package/templates/src/components/fields/ViewportComboBox.tsx +59 -42
  80. package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
  81. package/templates/src/components/form/ActionBuilderField.tsx +306 -87
  82. package/templates/src/components/search/SearchModal.tsx +420 -0
  83. package/templates/src/components/search/SearchResults.tsx +367 -0
  84. package/templates/src/components/search/SearchWrapper.tsx +46 -0
  85. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -1
  86. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +34 -8
  87. package/templates/src/components/storykeep/Dashboard_Branding.tsx +1 -1
  88. package/templates/src/components/storykeep/Dashboard_Content.tsx +6 -0
  89. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
  90. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +38 -34
  91. package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +1 -1
  92. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +56 -8
  93. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +18 -3
  94. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
  95. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
  96. package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
  97. package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
  98. package/templates/src/constants/shapes.ts +9 -0
  99. package/templates/src/constants.ts +2121 -16
  100. package/templates/src/hooks/useSearch.ts +228 -0
  101. package/templates/src/layouts/Layout.astro +213 -104
  102. package/templates/src/lib/storyData.ts +4 -1
  103. package/templates/src/pages/[...slug]/edit.astro +14 -14
  104. package/templates/src/pages/[...slug].astro +82 -21
  105. package/templates/src/pages/api/orphan-analysis.ts +0 -1
  106. package/templates/src/pages/api/tailwind.ts +23 -21
  107. package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
  108. package/templates/src/pages/context/[...contextSlug].astro +7 -2
  109. package/templates/src/pages/storykeep/advanced.astro +5 -4
  110. package/templates/src/pages/storykeep/branding.astro +5 -4
  111. package/templates/src/pages/storykeep/content.astro +5 -4
  112. package/templates/src/pages/storykeep/init.astro +40 -1
  113. package/templates/src/pages/storykeep/login.astro +1 -1
  114. package/templates/src/pages/storykeep.astro +5 -4
  115. package/templates/src/stores/nodes.ts +59 -88
  116. package/templates/src/stores/orphanAnalysis.ts +19 -21
  117. package/templates/src/stores/storykeep.ts +7 -0
  118. package/templates/src/types/compositorTypes.ts +6 -0
  119. package/templates/src/types/tractstack.ts +17 -0
  120. package/templates/src/utils/actions/lispLexer.ts +2 -2
  121. package/templates/src/utils/actions/preParse_Action.ts +3 -0
  122. package/templates/src/utils/api/beliefHelpers.ts +12 -36
  123. package/templates/src/utils/api/menuHelpers.ts +2 -2
  124. package/templates/src/utils/api.ts +26 -0
  125. package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
  126. package/templates/src/utils/compositor/allowInsert.ts +5 -3
  127. package/templates/src/utils/compositor/nodesHelper.ts +4 -0
  128. package/templates/src/utils/compositor/processMarkdown.ts +16 -2
  129. package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
  130. package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
  131. package/templates/src/utils/compositor/typeGuards.ts +1 -0
  132. package/templates/src/utils/customHelpers.ts +38 -0
  133. package/templates/src/utils/helpers.ts +2 -2
  134. package/templates/src/utils/layout.ts +65 -144
  135. package/utils/inject-files.ts +95 -18
  136. package/templates/src/client/analytics-events.js +0 -207
  137. package/templates/src/client/belief-events.js +0 -191
  138. package/templates/src/client/sse.js +0 -613
  139. package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
  140. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
  141. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
  142. package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
@@ -0,0 +1,274 @@
1
+ import { useState, useMemo, useEffect, useRef } from 'react';
2
+ import {
3
+ RadioGroup,
4
+ type RadioGroup as RadioGroupNamespace,
5
+ } from '@ark-ui/react/radio-group';
6
+ import { Combobox } from '@ark-ui/react/combobox';
7
+ import { Portal } from '@ark-ui/react/portal';
8
+ import { createListCollection } from '@ark-ui/react/collection';
9
+ import { useStore } from '@nanostores/react';
10
+ import { fullContentMapStore } from '@/stores/storykeep';
11
+ import { getCtx } from '@/stores/nodes';
12
+ import CheckCircleIcon from '@heroicons/react/20/solid/CheckCircleIcon';
13
+ import type { PaneNode } from '@/types/compositorTypes';
14
+ import type { BrandConfig } from '@/types/tractstack';
15
+
16
+ interface ProductGridSetupProps {
17
+ nodeId: string;
18
+ params: Record<string, any> | null;
19
+ config: BrandConfig;
20
+ }
21
+
22
+ const modes = [
23
+ {
24
+ id: 'all',
25
+ title: 'All Products',
26
+ description: 'Display all products from the catalog.',
27
+ },
28
+ {
29
+ id: 'type',
30
+ title: 'By Product Type',
31
+ description: 'Filter products by a specific type.',
32
+ },
33
+ {
34
+ id: 'specific',
35
+ title: 'Specific Products',
36
+ description: 'Manually select individual products.',
37
+ },
38
+ ];
39
+
40
+ export const ProductGridSetup = (props: ProductGridSetupProps) => {
41
+ const { nodeId, params } = props;
42
+ const ctx = getCtx();
43
+ const $contentMap = useStore(fullContentMapStore);
44
+ const isInitialMount = useRef(true);
45
+
46
+ const products = useMemo(() => {
47
+ return $contentMap
48
+ .filter(
49
+ (item) => item.type === 'Resource' && item.categorySlug === 'product'
50
+ )
51
+ .map((item) => ({ label: item.title, value: item.slug }));
52
+ }, [$contentMap]);
53
+
54
+ const productCollection = useMemo(() => {
55
+ return createListCollection({
56
+ items: products,
57
+ itemToValue: (item) => item.value,
58
+ itemToString: (item) => item.label,
59
+ });
60
+ }, [products]);
61
+
62
+ const [selectionMode, setSelectionMode] = useState<
63
+ 'all' | 'type' | 'specific'
64
+ >(() => {
65
+ if (params?.slugs !== undefined) return 'specific';
66
+ if (params?.productType !== undefined) return 'type';
67
+ return 'all';
68
+ });
69
+
70
+ const [productType, setProductType] = useState(
71
+ () => params?.productType || ''
72
+ );
73
+
74
+ const [selectedItems, setSelectedItems] = useState<
75
+ { label: string; value: string }[]
76
+ >(() => {
77
+ if (params?.slugs !== undefined) {
78
+ const slugs =
79
+ typeof params.slugs === 'string' && params.slugs
80
+ ? params.slugs.split(',')
81
+ : [];
82
+ return products.filter((p) => slugs.includes(p.value));
83
+ }
84
+ return [];
85
+ });
86
+
87
+ const [showSelector, setShowSelector] = useState(false);
88
+
89
+ useEffect(() => {
90
+ if (isInitialMount.current) {
91
+ isInitialMount.current = false;
92
+ return;
93
+ }
94
+
95
+ const constructPayload = () => {
96
+ if (selectionMode === 'all') {
97
+ return { category: 'product' };
98
+ }
99
+ if (selectionMode === 'type') {
100
+ return { category: 'product', productType: productType };
101
+ }
102
+ if (selectionMode === 'specific') {
103
+ const slugs = selectedItems.map((item) => item.value).join(',');
104
+ if (slugs) {
105
+ return { slugs };
106
+ }
107
+ return {}; // Return empty if no slugs are selected
108
+ }
109
+ return {};
110
+ };
111
+
112
+ const timeoutId = setTimeout(() => {
113
+ const paneNode = ctx.allNodes.get().get(nodeId) as PaneNode;
114
+ if (!paneNode) return;
115
+
116
+ const updatedPaneNode = {
117
+ ...paneNode,
118
+ codeHookPayload: {
119
+ target: paneNode.codeHookPayload?.target,
120
+ options: JSON.stringify(constructPayload()),
121
+ },
122
+ isChanged: true,
123
+ };
124
+ ctx.modifyNodes([updatedPaneNode]);
125
+ }, 500);
126
+
127
+ return () => clearTimeout(timeoutId);
128
+ }, [selectionMode, productType, selectedItems]);
129
+
130
+ const handleModeChange = (
131
+ details: RadioGroupNamespace.ValueChangeDetails
132
+ ) => {
133
+ if (details.value) {
134
+ setSelectionMode(details.value as 'all' | 'type' | 'specific');
135
+ setShowSelector(false);
136
+ }
137
+ };
138
+
139
+ const handleMultiSelectChange = (details: { value: string[] }) => {
140
+ const newSelection = products.filter((p) =>
141
+ details.value.includes(p.value)
142
+ );
143
+ setSelectedItems(newSelection);
144
+ };
145
+
146
+ const radioGroupStyles = `
147
+ .radio-item[data-state="checked"] { background-color: #f0f9ff; border-color: #0284c7; }
148
+ .radio-item[data-state="checked"] .check-icon { display: flex; }
149
+ .radio-item .check-icon { display: none; }
150
+ `;
151
+
152
+ return (
153
+ <div className="space-y-4 p-2">
154
+ <style>{radioGroupStyles}</style>
155
+ <h3 className="font-bold text-gray-800">Product Grid Configuration</h3>
156
+
157
+ <RadioGroup.Root
158
+ className="grid grid-cols-1 gap-4"
159
+ defaultValue={selectionMode}
160
+ onValueChange={handleModeChange}
161
+ >
162
+ {modes.map((option) => (
163
+ <RadioGroup.Item
164
+ key={option.id}
165
+ value={option.id}
166
+ className="radio-item relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none"
167
+ >
168
+ <div className="flex w-full items-center justify-between">
169
+ <div className="flex items-center">
170
+ <RadioGroup.ItemControl className="hidden" />
171
+ <div className="flex flex-col">
172
+ <RadioGroup.ItemText className="block text-sm font-bold text-gray-900">
173
+ {option.title}
174
+ </RadioGroup.ItemText>
175
+ <RadioGroup.ItemText className="flex items-center text-sm text-gray-500">
176
+ {option.description}
177
+ </RadioGroup.ItemText>
178
+ </div>
179
+ </div>
180
+ <div className="check-icon hidden shrink-0">
181
+ <CheckCircleIcon className="h-5 w-5 text-cyan-600" />
182
+ </div>
183
+ </div>
184
+ <RadioGroup.ItemHiddenInput />
185
+ </RadioGroup.Item>
186
+ ))}
187
+ </RadioGroup.Root>
188
+
189
+ {selectionMode === 'type' && (
190
+ <div className="space-y-2 rounded-md border p-3">
191
+ <label
192
+ htmlFor="productType"
193
+ className="text-sm font-medium text-gray-700"
194
+ >
195
+ Product Type
196
+ </label>
197
+ <input
198
+ type="text"
199
+ id="productType"
200
+ value={productType}
201
+ onChange={(e) => setProductType(e.target.value)}
202
+ placeholder="e.g., 'electronics'"
203
+ className="w-full rounded-md border-gray-300 px-3 py-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
204
+ />
205
+ </div>
206
+ )}
207
+
208
+ {selectionMode === 'specific' && (
209
+ <div className="rounded-md border bg-gray-50 p-3">
210
+ <div className="flex items-center justify-between">
211
+ <div>
212
+ <p className="text-sm font-medium text-gray-600">
213
+ Selected Products
214
+ </p>
215
+ <p className="font-bold text-gray-900">
216
+ {selectedItems.length} item(s) selected
217
+ </p>
218
+ </div>
219
+ <button
220
+ type="button"
221
+ onClick={() => setShowSelector(!showSelector)}
222
+ className="rounded bg-white px-3 py-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
223
+ >
224
+ {showSelector ? 'Close' : 'Change Selection'}
225
+ </button>
226
+ </div>
227
+
228
+ {showSelector && (
229
+ <div className="mt-4 space-y-2">
230
+ <Combobox.Root
231
+ collection={productCollection}
232
+ value={selectedItems.map((item) => item.value)}
233
+ onValueChange={handleMultiSelectChange}
234
+ multiple
235
+ lazyMount
236
+ unmountOnExit
237
+ >
238
+ <Combobox.Label className="text-sm font-medium text-gray-700">
239
+ Find products to include
240
+ </Combobox.Label>
241
+ <Combobox.Control>
242
+ <Combobox.Input
243
+ className="w-full rounded-md border-gray-300 px-3 py-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
244
+ placeholder="Search products..."
245
+ />
246
+ </Combobox.Control>
247
+ <Portal>
248
+ <Combobox.Positioner>
249
+ <Combobox.Content className="z-50 max-h-60 overflow-y-auto rounded-md border bg-white shadow-lg">
250
+ {products.map((item) => (
251
+ <Combobox.Item
252
+ key={item.value}
253
+ item={item}
254
+ className="relative flex cursor-pointer select-none items-center px-4 py-2 text-gray-900 data-[highlighted]:bg-cyan-600 data-[highlighted]:text-white"
255
+ >
256
+ <Combobox.ItemText>{item.label}</Combobox.ItemText>
257
+ <Combobox.ItemIndicator className="ml-auto">
258
+ <CheckCircleIcon className="h-5 w-5" />
259
+ </Combobox.ItemIndicator>
260
+ </Combobox.Item>
261
+ ))}
262
+ </Combobox.Content>
263
+ </Combobox.Positioner>
264
+ </Portal>
265
+ </Combobox.Root>
266
+ </div>
267
+ )}
268
+ </div>
269
+ )}
270
+ </div>
271
+ );
272
+ };
273
+
274
+ export default ProductGridSetup;