astro-tractstack 2.1.3 → 2.2.1

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 (128) 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/icons/code.svg +18 -0
  7. package/templates/icons/li.svg +4 -0
  8. package/templates/icons/link.svg +22 -0
  9. package/templates/icons/p.svg +3 -0
  10. package/templates/src/client/app.js +80 -1
  11. package/templates/src/components/Footer.astro +1 -1
  12. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +6 -6
  13. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +3 -3
  14. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
  15. package/templates/src/components/codehooks/ListContentSetup.tsx +2 -2
  16. package/templates/src/components/codehooks/ProductCardSetup.tsx +1 -1
  17. package/templates/src/components/codehooks/ProductGridSetup.tsx +2 -2
  18. package/templates/src/components/codehooks/SandboxRegisterForm.tsx +3 -3
  19. package/templates/src/components/compositor/Compositor.tsx +25 -9
  20. package/templates/src/components/compositor/Node.tsx +168 -496
  21. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +1 -0
  22. package/templates/src/components/compositor/elements/SignUp.tsx +1 -1
  23. package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +2 -0
  24. package/templates/src/components/compositor/nodes/CreativePane.tsx +262 -0
  25. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +4 -6
  26. package/templates/src/components/compositor/nodes/GridLayout.tsx +4 -2
  27. package/templates/src/components/compositor/nodes/Markdown.tsx +18 -3
  28. package/templates/src/components/compositor/nodes/Pane.tsx +11 -5
  29. package/templates/src/components/compositor/nodes/RenderChildren.tsx +1 -1
  30. package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +5 -5
  31. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +90 -42
  32. package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +2 -0
  33. package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +27 -1
  34. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +10 -8
  35. package/templates/src/components/compositor/tools/NodeOverlay.tsx +224 -0
  36. package/templates/src/components/compositor/tools/PaneOverlay.tsx +122 -0
  37. package/templates/src/components/edit/Header.tsx +68 -9
  38. package/templates/src/components/edit/PanelSwitch.tsx +42 -4
  39. package/templates/src/components/edit/SettingsPanel.tsx +2 -3
  40. package/templates/src/components/edit/ToolMode.tsx +1 -31
  41. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +2 -2
  42. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +1 -1
  43. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +193 -659
  44. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +15 -82
  45. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +95 -45
  46. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +137 -49
  47. package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -1
  48. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +375 -0
  49. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +1 -23
  50. package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +327 -0
  51. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +267 -0
  52. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +371 -0
  53. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +201 -76
  54. package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +141 -0
  55. package/templates/src/components/edit/panels/CreativeImagePanel.tsx +435 -0
  56. package/templates/src/components/edit/panels/CreativeLinkPanel.tsx +110 -0
  57. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +1 -1
  58. package/templates/src/components/edit/panels/StyleParentPanel.tsx +118 -126
  59. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +3 -2
  60. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +1 -0
  61. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +3 -1
  62. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +3 -1
  63. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +1 -1
  64. package/templates/src/components/edit/state/SaveModal.tsx +19 -787
  65. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +2 -2
  66. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +1 -1
  67. package/templates/src/components/edit/widgets/BunnyWidget.tsx +5 -5
  68. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +1 -1
  69. package/templates/src/components/edit/widgets/SignupWidget.tsx +1 -1
  70. package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +1 -1
  71. package/templates/src/components/fields/ArtpackImage.tsx +11 -3
  72. package/templates/src/components/fields/BackgroundImage.tsx +8 -0
  73. package/templates/src/components/fields/BackgroundImageWrapper.tsx +15 -9
  74. package/templates/src/components/fields/ImageUpload.tsx +6 -0
  75. package/templates/src/components/form/ActionBuilderField.tsx +15 -5
  76. package/templates/src/components/form/ActionBuilderSlugSelector.tsx +1 -1
  77. package/templates/src/components/form/ColorPicker.tsx +1 -1
  78. package/templates/src/components/form/EnumSelect.tsx +1 -1
  79. package/templates/src/components/form/NumberInput.tsx +1 -1
  80. package/templates/src/components/form/StringArrayInput.tsx +1 -1
  81. package/templates/src/components/form/StringInput.tsx +1 -1
  82. package/templates/src/components/form/UnsavedChangesBar.tsx +1 -1
  83. package/templates/src/components/form/advanced/APIConfigSection.tsx +2 -2
  84. package/templates/src/components/form/advanced/AuthConfigSection.tsx +2 -2
  85. package/templates/src/components/profile/ProfileCreate.tsx +1 -1
  86. package/templates/src/components/profile/ProfileEdit.tsx +1 -1
  87. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +2 -2
  88. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +1 -1
  89. package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +2 -2
  90. package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +1 -1
  91. package/templates/src/components/storykeep/controls/content/ManageContent.tsx +6 -6
  92. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +1 -1
  93. package/templates/src/components/storykeep/controls/content/PaneTable.tsx +358 -0
  94. package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -1
  95. package/templates/src/constants/prompts.json +18 -10
  96. package/templates/src/constants.ts +3 -0
  97. package/templates/src/hooks/usePaneFragments.ts +60 -0
  98. package/templates/src/lib/session.ts +71 -16
  99. package/templates/src/pages/[...slug].astro +4 -46
  100. package/templates/src/pages/api/css.ts +149 -0
  101. package/templates/src/pages/maint.astro +1 -1
  102. package/templates/src/pages/storykeep/login.astro +2 -2
  103. package/templates/src/stores/nodes.ts +162 -49
  104. package/templates/src/stores/orphanAnalysis.ts +6 -30
  105. package/templates/src/stores/previews.ts +7 -0
  106. package/templates/src/stores/storykeep.ts +0 -8
  107. package/templates/src/types/compositorTypes.ts +53 -10
  108. package/templates/src/utils/compositor/aiGeneration.ts +93 -0
  109. package/templates/src/utils/compositor/allowInsert.ts +2 -0
  110. package/templates/src/utils/compositor/htmlAst.ts +704 -0
  111. package/templates/src/utils/compositor/nodesHelper.ts +281 -102
  112. package/templates/src/utils/compositor/savePipeline.ts +893 -0
  113. package/templates/src/utils/etl/index.ts +3 -0
  114. package/templates/src/utils/etl/transformer.ts +10 -0
  115. package/templates/src/utils/helpers.ts +101 -0
  116. package/utils/inject-files.ts +100 -62
  117. package/templates/icons/text.svg +0 -6
  118. package/templates/src/components/compositor/NodeWithGuid.tsx +0 -69
  119. package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +0 -33
  120. package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +0 -56
  121. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +0 -269
  122. package/templates/src/components/compositor/nodes/Pane_eraser.tsx +0 -186
  123. package/templates/src/components/compositor/nodes/Pane_layout.tsx +0 -79
  124. package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +0 -26
  125. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +0 -61
  126. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +0 -120
  127. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +0 -62
  128. package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +0 -26
@@ -0,0 +1,371 @@
1
+ import { useState, useCallback, useMemo, useEffect } from 'react';
2
+ import prompts from '@/constants/prompts.json';
3
+ import { parseAiPane } from '@/utils/compositor/aiPaneParser';
4
+ import { callAskLemurAPI } from '@/utils/compositor/aiGeneration';
5
+ import { CopyInputStep, type CopyMode } from './CopyInputStep';
6
+ import { AiDesignStep, type AiDesignConfig } from './AiDesignStep';
7
+ import BooleanToggle from '@/components/form/BooleanToggle';
8
+ import type { TemplatePane } from '@/types/compositorTypes';
9
+
10
+ type LayoutChoice = 'standard' | 'grid';
11
+ type ColumnPresetKey = 'left' | 'right';
12
+
13
+ interface AiStandardDesignStepProps {
14
+ layoutChoice: LayoutChoice;
15
+ onBack: () => void;
16
+ onCreatePane: (template: TemplatePane) => void;
17
+ isSandboxMode?: boolean;
18
+ onDirectInject: () => void;
19
+ }
20
+
21
+ export const AiStandardDesignStep = ({
22
+ layoutChoice,
23
+ onBack,
24
+ onCreatePane,
25
+ isSandboxMode = false,
26
+ onDirectInject,
27
+ }: AiStandardDesignStepProps) => {
28
+ const [isGenerating, setIsGenerating] = useState(false);
29
+ const [error, setError] = useState<string | null>(null);
30
+ const [showColors, setShowColors] = useState(false);
31
+
32
+ const [copyMode, setCopyMode] = useState<CopyMode>('prompt');
33
+ const [topic, setTopic] = useState('');
34
+ const [showAdvancedPrompts, setShowAdvancedPrompts] = useState(false);
35
+ const [additionalNotes, setAdditionalNotes] = useState('');
36
+
37
+ const [promptValue, setPromptValue] = useState('');
38
+ const [copyValue, setCopyValue] = useState('');
39
+
40
+ const [overallPrompt, setOverallPrompt] = useState('');
41
+ const [promptValueCol1, setPromptValueCol1] = useState('');
42
+ const [promptValueCol2, setPromptValueCol2] = useState('');
43
+ const [col1Copy, setCol1Copy] = useState('');
44
+ const [col2Copy, setCol2Copy] = useState('');
45
+
46
+ const [masterShellSystem, setMasterShellSystem] = useState('');
47
+ const [masterShellUser, setMasterShellUser] = useState('');
48
+ const [masterCopySystem, setMasterCopySystem] = useState('');
49
+ const [masterCopyUser, setMasterCopyUser] = useState('');
50
+
51
+ const [aiDesignConfig, setAiDesignConfig] = useState<AiDesignConfig>({
52
+ harmony: 'Analogous',
53
+ baseColor: '',
54
+ accentColor: '',
55
+ theme: 'Light',
56
+ });
57
+
58
+ const [selectedPromptId, setSelectedPromptId] = useState<string>('');
59
+ const [isAiStyling, setIsAiStyling] = useState(false);
60
+
61
+ const promptOptions = useMemo(() => {
62
+ return prompts.aiPromptsIndex
63
+ .filter((p) => p.layout === layoutChoice)
64
+ .map((p) => ({ label: p.label, value: p.id }));
65
+ }, [layoutChoice]);
66
+
67
+ useEffect(() => {
68
+ if (promptOptions.length > 0) {
69
+ const currentValid = promptOptions.find(
70
+ (p) => p.value === selectedPromptId
71
+ );
72
+ if (!currentValid) {
73
+ setSelectedPromptId(promptOptions[0].value);
74
+ }
75
+ }
76
+ }, [promptOptions, selectedPromptId]);
77
+
78
+ useEffect(() => {
79
+ if (!selectedPromptId) return;
80
+
81
+ const activeConfig = prompts.aiPromptsIndex.find(
82
+ (p) => p.id === selectedPromptId
83
+ );
84
+ if (!activeConfig) return;
85
+
86
+ const promptKey = activeConfig.prompts.copy;
87
+ const shellKey = activeConfig.prompts.shell;
88
+ const copyPromptGroup = (prompts as any)[promptKey];
89
+ const shellPromptGroup = (prompts as any)[shellKey];
90
+
91
+ if (shellPromptGroup) {
92
+ setMasterShellSystem(shellPromptGroup.system || '');
93
+ setMasterShellUser(shellPromptGroup.user_template || '');
94
+ }
95
+ if (copyPromptGroup) {
96
+ setMasterCopySystem(copyPromptGroup.system || '');
97
+ setMasterCopyUser(copyPromptGroup.user_template || '');
98
+ }
99
+
100
+ if (!copyPromptGroup) return;
101
+
102
+ const variant = activeConfig.variants
103
+ ? activeConfig.variants[0]
104
+ : 'default';
105
+
106
+ if (layoutChoice === 'standard') {
107
+ const baseText = copyPromptGroup[variant] || '';
108
+ setPromptValue(baseText);
109
+ } else if (layoutChoice === 'grid') {
110
+ const preset = copyPromptGroup.presets?.[variant];
111
+ if (preset) {
112
+ setOverallPrompt(preset.default || '');
113
+ setPromptValueCol1(preset.left?.prompt || '');
114
+ setPromptValueCol2(preset.right?.prompt || '');
115
+ }
116
+ }
117
+ }, [selectedPromptId, layoutChoice]);
118
+
119
+ const handleGenerate = useCallback(async () => {
120
+ setError(null);
121
+ setIsGenerating(true);
122
+
123
+ try {
124
+ const activeConfig = prompts.aiPromptsIndex.find(
125
+ (p) => p.id === selectedPromptId
126
+ );
127
+ if (!activeConfig) throw new Error('Selected prompt type not found.');
128
+
129
+ let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
130
+ if (aiDesignConfig.baseColor)
131
+ designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
132
+ if (aiDesignConfig.accentColor)
133
+ designInput += ` Use **${aiDesignConfig.accentColor}** as an accent color.`;
134
+ if (additionalNotes)
135
+ designInput += ` Refine with these notes: "${additionalNotes}"`;
136
+
137
+ const layoutType = 'Text Only';
138
+ const promptMap = prompts as any;
139
+
140
+ const injectTopic = (text: string) => {
141
+ return text.replace('{{TOPIC}}', topic.trim());
142
+ };
143
+
144
+ let formattedShellPrompt = masterShellUser
145
+ .replace('{{DESIGN_INPUT}}', designInput)
146
+ .replace('{{LAYOUT_TYPE}}', layoutType);
147
+
148
+ if (layoutChoice === 'grid') {
149
+ const finalOverallPrompt = injectTopic(overallPrompt);
150
+ formattedShellPrompt = formattedShellPrompt.replace(
151
+ '{{COPY_INPUT}}',
152
+ finalOverallPrompt
153
+ );
154
+ }
155
+
156
+ const shellResult = await callAskLemurAPI({
157
+ prompt: formattedShellPrompt,
158
+ context: masterShellSystem,
159
+ expectJson: true,
160
+ isSandboxMode,
161
+ });
162
+
163
+ if (layoutChoice === 'standard') {
164
+ const copyInputContent =
165
+ copyMode === 'prompt' ? injectTopic(promptValue) : copyValue;
166
+
167
+ const formattedCopyPrompt = masterCopyUser
168
+ .replace('{{COPY_INPUT}}', copyInputContent)
169
+ .replace('{{DESIGN_INPUT}}', designInput)
170
+ .replace('{{LAYOUT_TYPE}}', layoutType)
171
+ .replace('{{SHELL_JSON}}', shellResult);
172
+
173
+ const copyResult = await callAskLemurAPI({
174
+ prompt: formattedCopyPrompt,
175
+ context: masterCopySystem,
176
+ expectJson: false,
177
+ isSandboxMode,
178
+ });
179
+
180
+ const finalPane = parseAiPane(shellResult, copyResult, layoutType);
181
+ onCreatePane(finalPane);
182
+ } else if (layoutChoice === 'grid') {
183
+ if (copyMode === 'raw') {
184
+ const rawContents = [col1Copy, col2Copy];
185
+ const finalPane = parseAiPane(shellResult, rawContents, layoutType);
186
+ onCreatePane(finalPane);
187
+ } else {
188
+ const copyPromptKey = activeConfig.prompts.copy;
189
+ const copyPromptDetails = promptMap[copyPromptKey];
190
+ const preset =
191
+ copyPromptDetails.presets?.[activeConfig.variants[0]] ||
192
+ copyPromptDetails.presets?.heroDefault;
193
+ const copyResults: string[] = [];
194
+
195
+ const promptsToRun = [
196
+ { prompt: promptValueCol1, presetKey: 'left' as ColumnPresetKey },
197
+ { prompt: promptValueCol2, presetKey: 'right' as ColumnPresetKey },
198
+ ];
199
+
200
+ for (const item of promptsToRun) {
201
+ const columnPreset = preset[item.presetKey];
202
+
203
+ const formattedCopyPrompt = masterCopyUser
204
+ .replace('{{SHELL_JSON}}', shellResult)
205
+ .replace('{{COPY_INPUT}}', injectTopic(overallPrompt))
206
+ .replace('{{COLUMN_PROMPT}}', item.prompt)
207
+ .replace('{{DESIGN_INPUT}}', designInput)
208
+ .replace('{{LAYOUT_TYPE}}', layoutType)
209
+ .replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
210
+
211
+ const copyResult = await callAskLemurAPI({
212
+ prompt: formattedCopyPrompt,
213
+ context: masterCopySystem,
214
+ expectJson: false,
215
+ isSandboxMode,
216
+ });
217
+ copyResults.push(copyResult);
218
+ }
219
+ const finalPane = parseAiPane(shellResult, copyResults, layoutType);
220
+ onCreatePane(finalPane);
221
+ }
222
+ }
223
+ } catch (err: any) {
224
+ console.error('AI Generation Error:', err);
225
+ setError(err.message || 'Failed to generate AI pane.');
226
+ } finally {
227
+ setIsGenerating(false);
228
+ }
229
+ }, [
230
+ aiDesignConfig,
231
+ additionalNotes,
232
+ copyMode,
233
+ promptValue,
234
+ copyValue,
235
+ col1Copy,
236
+ col2Copy,
237
+ overallPrompt,
238
+ promptValueCol1,
239
+ promptValueCol2,
240
+ isSandboxMode,
241
+ layoutChoice,
242
+ selectedPromptId,
243
+ onCreatePane,
244
+ masterShellUser,
245
+ masterShellSystem,
246
+ masterCopyUser,
247
+ masterCopySystem,
248
+ topic,
249
+ ]);
250
+
251
+ if (isGenerating) {
252
+ return (
253
+ <div className="flex min-h-96 flex-col items-center justify-center space-y-4 p-6">
254
+ <div className="h-8 w-8 animate-spin rounded-full border-b-2 border-cyan-600"></div>
255
+ <p className="text-sm text-gray-600">Generating Design & Content...</p>
256
+ <p className="text-xs text-gray-500">
257
+ Creating a unique layout from scratch.
258
+ </p>
259
+ </div>
260
+ );
261
+ }
262
+
263
+ return (
264
+ <div className="space-y-6 p-4">
265
+ <CopyInputStep
266
+ layoutChoice={layoutChoice}
267
+ copyMode={copyMode}
268
+ onCopyModeChange={setCopyMode}
269
+ topic={topic}
270
+ onTopicChange={setTopic}
271
+ showAdvancedPrompts={showAdvancedPrompts}
272
+ onShowAdvancedPromptsChange={setShowAdvancedPrompts}
273
+ promptValue={promptValue}
274
+ onPromptValueChange={setPromptValue}
275
+ copyValue={copyValue}
276
+ onCopyValueChange={setCopyValue}
277
+ overallPrompt={overallPrompt}
278
+ onOverallPromptChange={setOverallPrompt}
279
+ promptValueCol1={promptValueCol1}
280
+ onPromptValueCol1Change={setPromptValueCol1}
281
+ promptValueCol2={promptValueCol2}
282
+ onPromptValueCol2Change={setPromptValueCol2}
283
+ col1Copy={col1Copy}
284
+ onCol1CopyChange={setCol1Copy}
285
+ col2Copy={col2Copy}
286
+ onCol2CopyChange={setCol2Copy}
287
+ hasRetainedContent={false}
288
+ promptOptions={promptOptions}
289
+ selectedPromptId={selectedPromptId}
290
+ onSelectedPromptIdChange={setSelectedPromptId}
291
+ isAiStyling={isAiStyling}
292
+ onIsAiStylingChange={setIsAiStyling}
293
+ showStyleToggle={true}
294
+ onDirectInject={onDirectInject}
295
+ masterShellSystem={masterShellSystem}
296
+ setMasterShellSystem={setMasterShellSystem}
297
+ masterShellUser={masterShellUser}
298
+ setMasterShellUser={setMasterShellUser}
299
+ masterCopySystem={masterCopySystem}
300
+ setMasterCopySystem={setMasterCopySystem}
301
+ masterCopyUser={masterCopyUser}
302
+ setMasterCopyUser={setMasterCopyUser}
303
+ >
304
+ <div className="border-t pt-4">
305
+ <div>
306
+ <label
307
+ htmlFor="dashboard-notes"
308
+ className="block text-sm font-bold text-gray-700"
309
+ >
310
+ Design Notes
311
+ </label>
312
+ <textarea
313
+ id="dashboard-notes"
314
+ rows={2}
315
+ className="mt-1 block w-full rounded-md border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
316
+ placeholder="e.g. Clean, minimal, high contrast..."
317
+ value={additionalNotes}
318
+ onChange={(e) => setAdditionalNotes(e.target.value)}
319
+ />
320
+ </div>
321
+
322
+ <div className="my-4 flex items-center">
323
+ <BooleanToggle
324
+ label="Customize Colors"
325
+ value={showColors}
326
+ onChange={setShowColors}
327
+ size="sm"
328
+ />
329
+ </div>
330
+
331
+ {showColors && (
332
+ <div className="rounded-lg border border-gray-100 bg-gray-50 p-4">
333
+ <AiDesignStep
334
+ designConfig={aiDesignConfig}
335
+ onDesignConfigChange={setAiDesignConfig}
336
+ />
337
+ </div>
338
+ )}
339
+ </div>
340
+ </CopyInputStep>
341
+
342
+ {error && (
343
+ <div className="rounded-md bg-red-50 p-3 text-sm text-red-700">
344
+ {error}
345
+ </div>
346
+ )}
347
+
348
+ <div className="flex justify-between border-t pt-4">
349
+ <button
350
+ onClick={onBack}
351
+ className="rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-bold text-gray-700 shadow-sm hover:bg-gray-50"
352
+ >
353
+ Cancel
354
+ </button>
355
+ <button
356
+ onClick={handleGenerate}
357
+ disabled={
358
+ copyMode === 'prompt'
359
+ ? !promptValue.trim() && !overallPrompt.trim()
360
+ : copyMode === 'raw'
361
+ ? !copyValue.trim() && (!col1Copy.trim() || !col2Copy.trim())
362
+ : false
363
+ }
364
+ className="rounded-md bg-cyan-600 px-6 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
365
+ >
366
+ ✨ Generate Pane
367
+ </button>
368
+ </div>
369
+ </div>
370
+ );
371
+ };