astro-tractstack 2.0.19 → 2.0.21

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 (54) hide show
  1. package/dist/index.js +6 -32
  2. package/package.json +1 -1
  3. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
  4. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
  5. package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
  6. package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
  7. package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
  8. package/templates/src/components/compositor/Compositor.tsx +3 -6
  9. package/templates/src/components/compositor/Node.tsx +13 -32
  10. package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
  11. package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
  12. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
  13. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
  14. package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
  15. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
  16. package/templates/src/components/edit/PanelSwitch.tsx +3 -24
  17. package/templates/src/components/edit/SettingsPanel.tsx +0 -1
  18. package/templates/src/components/edit/ToolMode.tsx +6 -14
  19. package/templates/src/components/edit/pane/AddPanePanel.tsx +58 -25
  20. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +140 -133
  21. package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
  22. package/templates/src/components/edit/pane/RestylePaneModal.tsx +231 -282
  23. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
  24. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -13
  25. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
  26. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
  27. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
  28. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
  29. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
  30. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
  31. package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
  32. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
  33. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
  34. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
  35. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +29 -16
  36. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
  37. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
  38. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
  39. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
  40. package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
  41. package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
  42. package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
  43. package/templates/src/stores/nodes.ts +14 -6
  44. package/templates/src/stores/storykeep.ts +3 -3
  45. package/templates/src/types/compositorTypes.ts +2 -0
  46. package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
  47. package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
  48. package/templates/src/utils/compositor/designLibraryHelper.ts +523 -203
  49. package/templates/src/utils/helpers.ts +5 -4
  50. package/utils/inject-files.ts +6 -32
  51. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
  52. package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
  53. package/templates/src/utils/compositor/processMarkdown.ts +0 -445
  54. package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
@@ -1,4 +1,4 @@
1
- import { useState, useMemo } from 'react';
1
+ import { useState, useEffect, useMemo, useRef } from 'react';
2
2
  import { useStore } from '@nanostores/react';
3
3
  import {
4
4
  Dialog,
@@ -15,6 +15,7 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
15
15
  import { selectionStore } from '@/stores/selection';
16
16
  import { getCtx, NodesContext } from '@/stores/nodes';
17
17
  import { createEmptyStorykeep } from '@/utils/compositor/nodesHelper';
18
+ import { brandConfigStore } from '@/stores/storykeep';
18
19
  import {
19
20
  extractPaneCopy,
20
21
  mergeCopyIntoTemplate,
@@ -24,10 +25,10 @@ import type {
24
25
  PaneNode,
25
26
  StoragePane,
26
27
  TemplatePane,
27
- TemplateMarkdown, // Added import
28
- BaseNode, // Added import
28
+ TemplateMarkdown,
29
+ BaseNode,
29
30
  } from '@/types/compositorTypes';
30
- import type { BrandConfig, DesignLibraryEntry } from '@/types/tractstack';
31
+ import type { DesignLibraryEntry } from '@/types/tractstack';
31
32
  import {
32
33
  PaneSnapshotGenerator,
33
34
  type SnapshotData,
@@ -44,13 +45,11 @@ const VERBOSE = false;
44
45
 
45
46
  interface TemplatePreviewItemProps {
46
47
  template: TemplatePane;
47
- config: BrandConfig;
48
48
  onClick: () => void;
49
49
  }
50
50
 
51
51
  const TemplatePreviewItem = ({
52
52
  template,
53
- config,
54
53
  onClick,
55
54
  }: TemplatePreviewItemProps) => {
56
55
  const [previewState, setPreviewState] = useState<{
@@ -60,7 +59,6 @@ const TemplatePreviewItem = ({
60
59
  } | null>(null);
61
60
 
62
61
  const fragmentRequest = useMemo((): PanePreviewRequest[] => {
63
- // This preview logic is correct: it creates a *temporary* context.
64
62
  const ctx = new NodesContext();
65
63
  ctx.addNode(createEmptyStorykeep('tmp'));
66
64
  ctx.addTemplatePane('tmp', template);
@@ -111,7 +109,6 @@ const TemplatePreviewItem = ({
111
109
  id={template.id}
112
110
  htmlString={previewState.htmlFragment}
113
111
  outputWidth={800}
114
- config={config}
115
112
  onComplete={(_id, data) => handleSnapshotComplete(data)}
116
113
  onError={(_id, err) =>
117
114
  setPreviewState((prev) =>
@@ -141,16 +138,13 @@ const TemplatePreviewItem = ({
141
138
  );
142
139
  };
143
140
 
144
- interface RestylePaneModalProps {
145
- config: BrandConfig;
146
- }
147
-
148
- export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
141
+ export const RestylePaneModal = () => {
149
142
  const ctx = getCtx();
150
143
  const { isRestyleModalOpen, paneToRestyleId } = useStore(selectionStore, {
151
144
  keys: ['isRestyleModalOpen', 'paneToRestyleId'],
152
145
  });
153
- const designLibrary = config?.DESIGN_LIBRARY || [];
146
+ const designLibrary = brandConfigStore.get()?.DESIGN_LIBRARY || [];
147
+ const contentRef = useRef<HTMLDivElement>(null);
154
148
 
155
149
  const [selectedCategory, setSelectedCategory] = useState<string>('all');
156
150
  const [searchTerm, setSearchTerm] = useState('');
@@ -163,6 +157,33 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
163
157
  return ['all', ...Array.from(allCategories)];
164
158
  }, [designLibrary]);
165
159
 
160
+ const [targetMarkdownCount, setTargetMarkdownCount] = useState<number>(0);
161
+
162
+ useEffect(() => {
163
+ if (!paneToRestyleId || !isRestyleModalOpen) {
164
+ setTargetMarkdownCount(0);
165
+ return;
166
+ }
167
+ const paneChildIds = ctx.getChildNodeIDs(paneToRestyleId);
168
+ const nodesMap = ctx.allNodes.get();
169
+ const gridNodeId = paneChildIds.find(
170
+ (id) => nodesMap.get(id)?.nodeType === 'GridLayoutNode'
171
+ );
172
+ if (gridNodeId) {
173
+ const columns = ctx.getChildNodeIDs(gridNodeId);
174
+ setTargetMarkdownCount(columns.length);
175
+ return;
176
+ }
177
+ const markdownNodeId = paneChildIds.find(
178
+ (id) => nodesMap.get(id)?.nodeType === 'Markdown'
179
+ );
180
+ if (markdownNodeId) {
181
+ setTargetMarkdownCount(1);
182
+ return;
183
+ }
184
+ setTargetMarkdownCount(0);
185
+ }, [paneToRestyleId, isRestyleModalOpen]);
186
+
166
187
  const originalPaneData = useMemo(() => {
167
188
  if (!paneToRestyleId) return null;
168
189
  const paneNode = ctx.allNodes.get().get(paneToRestyleId) as PaneNode;
@@ -189,9 +210,10 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
189
210
  return designLibrary.filter(
190
211
  (entry: DesignLibraryEntry) =>
191
212
  (selectedCategory === 'all' || entry.category === selectedCategory) &&
192
- entry.title.toLowerCase().includes(searchTerm.toLowerCase())
213
+ entry.title.toLowerCase().includes(searchTerm.toLowerCase()) &&
214
+ entry.markdownCount === targetMarkdownCount
193
215
  );
194
- }, [designLibrary, selectedCategory, searchTerm]);
216
+ }, [designLibrary, selectedCategory, searchTerm, targetMarkdownCount]);
195
217
 
196
218
  const paginatedEntries = useMemo(() => {
197
219
  const start = (currentPage - 1) * PAGE_SIZE;
@@ -230,123 +252,64 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
230
252
  setSelectedCategory('all');
231
253
  };
232
254
 
233
- const handleSelectTemplate = (template: TemplatePane) => {
234
- if (VERBOSE)
235
- console.log(
236
- '%cDEBUG: handleSelectTemplate CLICKED (Hollow & Replace)',
237
- 'color: #00A; font-weight: bold;',
238
- {
239
- templateToApply: template,
240
- originalPaneData: originalPaneData,
241
- }
242
- );
255
+ const handleDialogStateChange = (details: { open: boolean }) => {
256
+ if (!details.open) {
257
+ handleClose();
258
+ }
259
+ };
243
260
 
261
+ const handleSelectTemplate = (template: TemplatePane) => {
244
262
  if (!originalPaneData) {
245
- console.error(
246
- '%cDEBUG: handleSelectTemplate FAILED: originalPaneData is null.',
247
- 'color: red; font-weight: bold;'
248
- );
249
263
  return;
250
264
  }
251
265
 
252
266
  const originalPane = originalPaneData.paneNode;
253
267
  const originalPaneId = originalPane.id;
254
268
 
255
- if (VERBOSE)
256
- console.log(
257
- `%cDEBUG: STEP 1 - HOLLOWING OUT original pane ${originalPaneId}`,
258
- 'color: #A0A; font-weight: bold;'
259
- );
260
- const oldChildrenNodes = ctx
261
- .getChildNodeIDs(originalPaneId)
262
- .map((id) => ctx.allNodes.get().get(id));
263
- if (VERBOSE)
264
- console.log(
265
- '%cDEBUG: Original pane children BEFORE delete:',
266
- oldChildrenNodes
267
- );
268
-
269
- const deletedChildren = ctx.deleteChildren(originalPaneId); // This deletes *children*, not the pane itself
270
-
271
- const childrenAfterDelete = ctx.getChildNodeIDs(originalPaneId);
272
- if (VERBOSE) {
273
- console.log(
274
- `%cDEBUG: Deleted ${deletedChildren.length} old child nodes.`,
275
- 'color: #A0A;'
276
- );
277
- console.log(
278
- `%cDEBUG: Original pane children IDs AFTER delete: [${childrenAfterDelete.join(', ')}]`,
279
- 'color: #A0A;'
280
- );
281
- console.log(
282
- `%cDEBUG: STEP 2 - REFILLING pane with new nodes...`,
283
- 'color: #0A0; font-weight: bold;'
284
- );
285
- }
269
+ ctx.deleteChildren(originalPaneId);
286
270
 
287
271
  const newNodesToAdd: BaseNode[] = [];
288
272
  const newMarkdown = template.markdown as TemplateMarkdown | undefined;
273
+ const newGridLayout = template.gridLayout;
289
274
  const newBgPane = template.bgPane;
290
275
 
291
276
  if (newMarkdown) {
292
- // Re-parent the new Markdown node to the original pane
293
277
  newMarkdown.parentId = originalPaneId;
294
278
  newNodesToAdd.push(newMarkdown);
295
-
296
- // The markdown.nodes are already parented to the newMarkdown.id, which is correct.
297
- // We just need to add them to the context.
298
279
  if (newMarkdown.nodes) {
299
280
  newNodesToAdd.push(...newMarkdown.nodes);
300
281
  }
301
- if (VERBOSE) {
302
- console.log(`%cDEBUG: Prepared new Markdown node:`, newMarkdown);
303
- console.log(
304
- `%cDEBUG: Prepared ${newMarkdown.nodes?.length || 0} new markdown sub-nodes:`,
305
- newMarkdown.nodes
306
- );
282
+ }
283
+
284
+ if (newGridLayout) {
285
+ newGridLayout.parentId = originalPaneId;
286
+ newNodesToAdd.push(newGridLayout);
287
+
288
+ if (newGridLayout.nodes) {
289
+ newGridLayout.nodes.forEach((column) => {
290
+ column.parentId = newGridLayout.id;
291
+ newNodesToAdd.push(column);
292
+
293
+ if (column.nodes) {
294
+ newNodesToAdd.push(...column.nodes);
295
+ }
296
+ });
307
297
  }
308
298
  }
309
299
 
310
300
  if (newBgPane) {
311
- // Re-parent the new BgPane node to the original pane
312
301
  newBgPane.parentId = originalPaneId;
313
302
  newNodesToAdd.push(newBgPane);
314
- if (VERBOSE) console.log(`%cDEBUG: Prepared new BgPane:`, newBgPane);
315
303
  }
316
304
 
317
- ctx.addNodes(newNodesToAdd); // This adds all nodes AND links them in parentNodes map
305
+ ctx.addNodes(newNodesToAdd);
318
306
 
319
- const childrenAfterAdd = ctx.getChildNodeIDs(originalPaneId);
320
- const childrenNodesAfterAdd = childrenAfterAdd.map((id) =>
321
- ctx.allNodes.get().get(id)
322
- );
323
- if (VERBOSE) {
324
- console.log(
325
- `%cDEBUG: Original pane children IDs AFTER add: [${childrenAfterAdd.join(', ')}]`,
326
- 'color: #0A0;'
327
- );
328
- console.log(
329
- `%cDEBUG: Original pane children nodes AFTER add:`,
330
- childrenNodesAfterAdd
331
- );
332
- console.log(
333
- `%cDEBUG: STEP 3 - UPDATING original pane properties...`,
334
- 'color: #00F; font-weight: bold;'
335
- );
336
- }
337
-
338
- // We must get a fresh reference from the store to modify
339
307
  const paneToUpdate = ctx.allNodes.get().get(originalPaneId) as PaneNode;
340
308
 
341
309
  if (!paneToUpdate) {
342
- console.error(
343
- `%cDEBUG: FAILED TO FIND PANE ${originalPaneId} IN STORE FOR FINAL UPDATE.`,
344
- 'color: red; font-weight: bold;'
345
- );
346
310
  return;
347
311
  }
348
312
 
349
- // Copy all style/config properties from the template, but keep the original ID, parentId, slug, title
350
313
  paneToUpdate.bgColour = template.bgColour;
351
314
  paneToUpdate.isDecorative = template.isDecorative;
352
315
  paneToUpdate.heightOffsetDesktop = template.heightOffsetDesktop;
@@ -355,25 +318,9 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
355
318
  paneToUpdate.heightRatioDesktop = template.heightRatioDesktop;
356
319
  paneToUpdate.heightRatioMobile = template.heightRatioMobile;
357
320
  paneToUpdate.heightRatioTablet = template.heightRatioTablet;
358
- paneToUpdate.isChanged = true; // Mark as dirty
359
-
360
- if (VERBOSE)
361
- console.log(
362
- `%cDEBUG: Calling modifyNodes with this pane object:`,
363
- paneToUpdate
364
- );
365
- ctx.modifyNodes([paneToUpdate]); // This will save the changes and notify the UI
321
+ paneToUpdate.isChanged = true;
366
322
 
367
- if (VERBOSE) {
368
- console.log(
369
- '%cDEBUG: handleSelectTemplate FINISHED.',
370
- 'color: #00A; font-weight: bold;'
371
- );
372
- console.log(
373
- '%cDEBUG: Notifying ROOT_NODE to force re-render.',
374
- 'color: green; font-weight: bold;'
375
- );
376
- }
323
+ ctx.modifyNodes([paneToUpdate]);
377
324
  ctx.notifyNode('root');
378
325
 
379
326
  handleClose();
@@ -400,177 +347,179 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
400
347
  );
401
348
 
402
349
  return (
403
- <Dialog.Root open={isRestyleModalOpen} onOpenChange={handleClose} modal>
404
- <Portal>
405
- <Dialog.Backdrop className="z-103 fixed inset-0 bg-black/70" />
406
- <Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
407
- <Dialog.Content
408
- className="flex flex-col rounded-lg bg-white shadow-2xl"
409
- style={{ maxHeight: '90vw', width: '90vw' }}
410
- >
411
- <header className="flex items-center justify-between border-b p-4">
412
- <Dialog.Title className="text-xl font-bold">
413
- Restyle Pane from Design Library
414
- </Dialog.Title>
415
- <Dialog.CloseTrigger
416
- type="button"
417
- className="rounded-full p-1 text-gray-600 hover:bg-gray-100"
418
- >
419
- <XMarkIcon className="h-6 w-6" />
420
- </Dialog.CloseTrigger>
421
- </header>
422
-
423
- <nav className="flex items-center gap-x-4 border-b bg-gray-50 p-4">
424
- <Select.Root
425
- collection={selectCollection}
426
- value={[selectedCategory]}
427
- onValueChange={(details: SelectValueChangeDetails) =>
428
- setSelectedCategory(details.value[0])
350
+ <Dialog.Root
351
+ open={isRestyleModalOpen}
352
+ onOpenChange={handleDialogStateChange}
353
+ modal={false}
354
+ >
355
+ <Dialog.Backdrop className="z-103 fixed inset-0 bg-black/70" />
356
+ <Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
357
+ <Dialog.Content
358
+ ref={contentRef}
359
+ className="flex flex-col rounded-lg bg-white shadow-2xl"
360
+ style={{ maxHeight: '90vw', width: '90vw' }}
361
+ >
362
+ <header className="flex items-center justify-between border-b p-4">
363
+ <Dialog.Title className="text-xl font-bold">
364
+ Restyle Pane from Design Library
365
+ </Dialog.Title>
366
+ <Dialog.CloseTrigger
367
+ type="button"
368
+ className="rounded-full p-1 text-gray-600 hover:bg-gray-100"
369
+ >
370
+ <XMarkIcon className="h-6 w-6" />
371
+ </Dialog.CloseTrigger>
372
+ </header>
373
+
374
+ <nav className="flex items-center gap-x-4 border-b bg-gray-50 p-4">
375
+ <Select.Root
376
+ collection={selectCollection}
377
+ value={[selectedCategory]}
378
+ onValueChange={(details: SelectValueChangeDetails) =>
379
+ setSelectedCategory(details.value[0])
380
+ }
381
+ className="w-48"
382
+ positioning={{ gutter: 4 }}
383
+ >
384
+ <Select.Label className="mb-1 text-sm font-bold">
385
+ Category
386
+ </Select.Label>
387
+ <Select.Control>
388
+ <Select.Trigger className="flex w-full items-center justify-between rounded border bg-white p-2 text-left">
389
+ <Select.ValueText />
390
+ <Select.Indicator>▼</Select.Indicator>
391
+ </Select.Trigger>
392
+ </Select.Control>
393
+ <Portal>
394
+ <Select.Positioner>
395
+ <Select.Content className="z-105 rounded border bg-white shadow-lg">
396
+ {categories.map((c) => (
397
+ <Select.Item
398
+ key={c}
399
+ item={{ label: c, value: c }}
400
+ className="cursor-pointer p-2 hover:bg-gray-100"
401
+ >
402
+ <Select.ItemText>{c}</Select.ItemText>
403
+ </Select.Item>
404
+ ))}
405
+ </Select.Content>
406
+ </Select.Positioner>
407
+ </Portal>
408
+ </Select.Root>
409
+
410
+ <Combobox.Root
411
+ collection={comboboxCollection}
412
+ onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
413
+ setSearchTerm(e.inputValue)
414
+ }
415
+ className="flex-1"
416
+ positioning={{ gutter: 4 }}
417
+ >
418
+ <Combobox.Label className="mb-1 text-sm font-bold">
419
+ Filter by Title
420
+ </Combobox.Label>
421
+ <Combobox.Control>
422
+ <Combobox.Input
423
+ placeholder="Search by title..."
424
+ className="w-full rounded border p-2"
425
+ />
426
+ </Combobox.Control>
427
+ <Portal>
428
+ <Combobox.Positioner>
429
+ <Combobox.Content className="z-105 rounded border bg-white shadow-lg">
430
+ {filteredEntries.map((entry: DesignLibraryEntry) => (
431
+ <Combobox.Item
432
+ key={entry.title}
433
+ item={entry}
434
+ className="cursor-pointer p-2 hover:bg-gray-100"
435
+ >
436
+ <Combobox.ItemText>{entry.title}</Combobox.ItemText>
437
+ </Combobox.Item>
438
+ ))}
439
+ </Combobox.Content>
440
+ </Combobox.Positioner>
441
+ </Portal>
442
+ </Combobox.Root>
443
+ </nav>
444
+
445
+ <main className="flex-1 overflow-y-auto bg-gray-100 p-6">
446
+ {mergedTemplates.length === 0 ? (
447
+ <div className="flex h-full items-center justify-center">
448
+ <p className="text-gray-500">No designs found.</p>
449
+ </div>
450
+ ) : (
451
+ <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
452
+ {mergedTemplates.map(({ template }) => (
453
+ <TemplatePreviewItem
454
+ key={template.id}
455
+ template={template}
456
+ onClick={() => handleSelectTemplate(template)}
457
+ />
458
+ ))}
459
+ </div>
460
+ )}
461
+ </main>
462
+
463
+ {totalPages > 1 && (
464
+ <footer className="flex items-center justify-center border-t p-4">
465
+ <Pagination.Root
466
+ count={totalPages * PAGE_SIZE}
467
+ pageSize={PAGE_SIZE}
468
+ siblingCount={1}
469
+ page={currentPage}
470
+ onPageChange={(details: PaginationPageChangeDetails) =>
471
+ setCurrentPage(details.page)
429
472
  }
430
- className="w-48"
431
- positioning={{ gutter: 4 }}
473
+ className="flex items-center gap-x-2"
432
474
  >
433
- <Select.Label className="mb-1 text-sm font-bold">
434
- Category
435
- </Select.Label>
436
- <Select.Control>
437
- <Select.Trigger className="flex w-full items-center justify-between rounded border bg-white p-2 text-left">
438
- <Select.ValueText />
439
- <Select.Indicator>▼</Select.Indicator>
440
- </Select.Trigger>
441
- </Select.Control>
442
- <Portal>
443
- <Select.Positioner>
444
- <Select.Content className="z-105 rounded border bg-white shadow-lg">
445
- {categories.map((c) => (
446
- <Select.Item
447
- key={c}
448
- item={{ label: c, value: c }}
449
- className="cursor-pointer p-2 hover:bg-gray-100"
475
+ <Pagination.PrevTrigger
476
+ type="button"
477
+ className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
478
+ disabled={currentPage === 1}
479
+ >
480
+ Previous
481
+ </Pagination.PrevTrigger>
482
+ <Pagination.Context>
483
+ {(pagination) =>
484
+ pagination.pages.map((page, index: number) =>
485
+ page.type === 'page' ? (
486
+ <Pagination.Item
487
+ key={index}
488
+ {...page}
489
+ type="page"
490
+ className={classNames(
491
+ 'flex h-9 w-9 items-center justify-center rounded text-sm',
492
+ page.value === currentPage
493
+ ? 'bg-blue-600 font-bold text-white'
494
+ : 'hover:bg-gray-100'
495
+ )}
450
496
  >
451
- <Select.ItemText>{c}</Select.ItemText>
452
- </Select.Item>
453
- ))}
454
- </Select.Content>
455
- </Select.Positioner>
456
- </Portal>
457
- </Select.Root>
458
-
459
- <Combobox.Root
460
- collection={comboboxCollection}
461
- onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
462
- setSearchTerm(e.inputValue)
463
- }
464
- className="flex-1"
465
- positioning={{ gutter: 4 }}
466
- >
467
- <Combobox.Label className="mb-1 text-sm font-bold">
468
- Filter by Title
469
- </Combobox.Label>
470
- <Combobox.Control>
471
- <Combobox.Input
472
- placeholder="Search by title..."
473
- className="w-full rounded border p-2"
474
- />
475
- </Combobox.Control>
476
- <Portal>
477
- <Combobox.Positioner>
478
- <Combobox.Content className="z-105 rounded border bg-white shadow-lg">
479
- {filteredEntries.map((entry: DesignLibraryEntry) => (
480
- <Combobox.Item
481
- key={entry.title}
482
- item={entry}
483
- className="cursor-pointer p-2 hover:bg-gray-100"
497
+ {page.value}
498
+ </Pagination.Item>
499
+ ) : (
500
+ <Pagination.Ellipsis
501
+ key={index}
502
+ index={index}
503
+ className="px-2 text-sm"
484
504
  >
485
- <Combobox.ItemText>{entry.title}</Combobox.ItemText>
486
- </Combobox.Item>
487
- ))}
488
- </Combobox.Content>
489
- </Combobox.Positioner>
490
- </Portal>
491
- </Combobox.Root>
492
- </nav>
493
-
494
- <main className="flex-1 overflow-y-auto bg-gray-100 p-6">
495
- {mergedTemplates.length === 0 ? (
496
- <div className="flex h-full items-center justify-center">
497
- <p className="text-gray-500">No designs found.</p>
498
- </div>
499
- ) : (
500
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
501
- {mergedTemplates.map(({ template }) => (
502
- <TemplatePreviewItem
503
- key={template.id}
504
- template={template}
505
- config={config}
506
- onClick={() => handleSelectTemplate(template)}
507
- />
508
- ))}
509
- </div>
510
- )}
511
- </main>
512
-
513
- {totalPages > 1 && (
514
- <footer className="flex items-center justify-center border-t p-4">
515
- <Pagination.Root
516
- count={totalPages * PAGE_SIZE}
517
- pageSize={PAGE_SIZE}
518
- siblingCount={1}
519
- page={currentPage}
520
- onPageChange={(details: PaginationPageChangeDetails) =>
521
- setCurrentPage(details.page)
505
+ ...
506
+ </Pagination.Ellipsis>
507
+ )
508
+ )
522
509
  }
523
- className="flex items-center gap-x-2"
510
+ </Pagination.Context>
511
+ <Pagination.NextTrigger
512
+ type="button"
513
+ className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
514
+ disabled={currentPage === totalPages}
524
515
  >
525
- <Pagination.PrevTrigger
526
- type="button"
527
- className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
528
- disabled={currentPage === 1}
529
- >
530
- Previous
531
- </Pagination.PrevTrigger>
532
- <Pagination.Context>
533
- {(pagination) =>
534
- pagination.pages.map((page, index: number) =>
535
- page.type === 'page' ? (
536
- <Pagination.Item
537
- key={index}
538
- {...page}
539
- type="page"
540
- className={classNames(
541
- 'flex h-9 w-9 items-center justify-center rounded text-sm',
542
- page.value === currentPage
543
- ? 'bg-blue-600 font-bold text-white'
544
- : 'hover:bg-gray-100'
545
- )}
546
- >
547
- {page.value}
548
- </Pagination.Item>
549
- ) : (
550
- <Pagination.Ellipsis
551
- key={index}
552
- index={index}
553
- className="px-2 text-sm"
554
- >
555
- ...
556
- </Pagination.Ellipsis>
557
- )
558
- )
559
- }
560
- </Pagination.Context>
561
- <Pagination.NextTrigger
562
- type="button"
563
- className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
564
- disabled={currentPage === totalPages}
565
- >
566
- Next
567
- </Pagination.NextTrigger>
568
- </Pagination.Root>
569
- </footer>
570
- )}
571
- </Dialog.Content>
572
- </Dialog.Positioner>
573
- </Portal>
516
+ Next
517
+ </Pagination.NextTrigger>
518
+ </Pagination.Root>
519
+ </footer>
520
+ )}
521
+ </Dialog.Content>
522
+ </Dialog.Positioner>
574
523
  </Dialog.Root>
575
524
  );
576
525
  };
@@ -1,5 +1,4 @@
1
1
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
2
- import type { BrandConfig } from '@/types/tractstack';
3
2
 
4
3
  export interface AiDesignConfig {
5
4
  harmony: string;
@@ -10,7 +9,6 @@ export interface AiDesignConfig {
10
9
  }
11
10
 
12
11
  interface AiDesignStepProps {
13
- config: BrandConfig;
14
12
  designConfig: AiDesignConfig;
15
13
  onDesignConfigChange: (newConfig: AiDesignConfig) => void;
16
14
  }
@@ -24,7 +22,6 @@ const harmonyOptions = [
24
22
  const themeOptions = ['Light', 'Dark', 'Bright', 'Muted', 'Pastel', 'Earthy'];
25
23
 
26
24
  export const AiDesignStep = ({
27
- config,
28
25
  designConfig,
29
26
  onDesignConfigChange,
30
27
  }: AiDesignStepProps) => {
@@ -71,7 +68,6 @@ export const AiDesignStep = ({
71
68
  <div>
72
69
  <ColorPickerCombo
73
70
  title="Base Color (Optional)"
74
- config={config}
75
71
  defaultColor={designConfig.baseColor}
76
72
  onColorChange={(color) => updateField('baseColor', color)}
77
73
  allowNull={true}
@@ -80,7 +76,6 @@ export const AiDesignStep = ({
80
76
  <div>
81
77
  <ColorPickerCombo
82
78
  title="Accent Color (Optional)"
83
- config={config}
84
79
  defaultColor={designConfig.accentColor}
85
80
  onColorChange={(color) => updateField('accentColor', color)}
86
81
  allowNull={true}