astro-tractstack 2.0.18 → 2.0.20
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.
- package/dist/index.js +6 -32
- package/package.json +1 -1
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
- package/templates/src/components/compositor/Compositor.tsx +3 -6
- package/templates/src/components/compositor/Node.tsx +13 -32
- package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
- package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
- package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +1 -0
- package/templates/src/components/edit/PanelSwitch.tsx +3 -24
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/ToolMode.tsx +6 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +45 -25
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +277 -70
- package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +7 -14
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -11
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +0 -1
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
- package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +27 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
- package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
- package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
- package/templates/src/constants/prompts.json +22 -1
- package/templates/src/stores/nodes.ts +297 -222
- package/templates/src/stores/storykeep.ts +3 -3
- package/templates/src/types/compositorTypes.ts +21 -1
- package/templates/src/types/tractstack.ts +1 -0
- package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
- package/templates/src/utils/compositor/aiPaneParser.ts +265 -83
- package/templates/src/utils/compositor/designLibraryHelper.ts +252 -26
- package/templates/src/utils/helpers.ts +5 -4
- package/utils/inject-files.ts +6 -32
- package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
- package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
- package/templates/src/utils/compositor/processMarkdown.ts +0 -445
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
|
@@ -3,11 +3,13 @@ import { useState, useCallback } from 'react';
|
|
|
3
3
|
import DocumentPlusIcon from '@heroicons/react/24/outline/DocumentPlusIcon';
|
|
4
4
|
import SparklesIcon from '@heroicons/react/24/outline/SparklesIcon';
|
|
5
5
|
import SwatchIcon from '@heroicons/react/24/outline/SwatchIcon';
|
|
6
|
+
import SquaresPlusIcon from '@heroicons/react/24/outline/SquaresPlusIcon';
|
|
7
|
+
import DocumentIcon from '@heroicons/react/24/outline/DocumentIcon';
|
|
6
8
|
import { NodesContext, getCtx } from '@/stores/nodes';
|
|
7
9
|
import { cloneDeep } from '@/utils/helpers';
|
|
8
10
|
import { hasAssemblyAIStore } from '@/stores/storykeep';
|
|
9
11
|
import prompts from '@/constants/prompts.json';
|
|
10
|
-
import type {
|
|
12
|
+
import type { DesignLibraryEntry } from '@/types/tractstack';
|
|
11
13
|
import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
|
|
12
14
|
import { useStore } from '@nanostores/react';
|
|
13
15
|
import { CopyInputStep } from './steps/CopyInputStep';
|
|
@@ -25,6 +27,7 @@ type Step =
|
|
|
25
27
|
| 'initial'
|
|
26
28
|
| 'copyInput'
|
|
27
29
|
| 'designLibrary'
|
|
30
|
+
| 'layoutChoice'
|
|
28
31
|
| 'aiDesign'
|
|
29
32
|
| 'loading'
|
|
30
33
|
| 'error'
|
|
@@ -32,6 +35,8 @@ type Step =
|
|
|
32
35
|
|
|
33
36
|
type InitialChoice = 'library' | 'ai' | 'blank';
|
|
34
37
|
type CopyMode = 'prompt' | 'raw';
|
|
38
|
+
type LayoutChoice = 'standard' | 'grid';
|
|
39
|
+
type ColumnPresetKey = 'left' | 'right';
|
|
35
40
|
|
|
36
41
|
interface GenerationResponse {
|
|
37
42
|
success: boolean;
|
|
@@ -115,7 +120,6 @@ interface AddPaneNewPanelProps {
|
|
|
115
120
|
ctx?: NodesContext;
|
|
116
121
|
isStoryFragment?: boolean;
|
|
117
122
|
isContextPane?: boolean;
|
|
118
|
-
config?: BrandConfig;
|
|
119
123
|
isSandboxMode?: boolean;
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -126,7 +130,6 @@ const AddPaneNewPanel = ({
|
|
|
126
130
|
ctx: providedCtx,
|
|
127
131
|
isStoryFragment = false,
|
|
128
132
|
isContextPane = false,
|
|
129
|
-
config,
|
|
130
133
|
isSandboxMode = false,
|
|
131
134
|
}: AddPaneNewPanelProps) => {
|
|
132
135
|
const ctx = providedCtx || getCtx();
|
|
@@ -135,10 +138,23 @@ const AddPaneNewPanel = ({
|
|
|
135
138
|
const [initialChoice, setInitialChoice] = useState<InitialChoice | null>(
|
|
136
139
|
null
|
|
137
140
|
);
|
|
141
|
+
const [layoutChoice, setLayoutChoice] = useState<LayoutChoice>('standard');
|
|
138
142
|
const [error, setError] = useState<string | null>(null);
|
|
143
|
+
|
|
139
144
|
const [copyMode, setCopyMode] = useState<CopyMode>('prompt');
|
|
140
145
|
const [promptValue, setPromptValue] = useState('');
|
|
141
146
|
const [copyValue, setCopyValue] = useState('');
|
|
147
|
+
|
|
148
|
+
const [overallPrompt, setOverallPrompt] = useState(
|
|
149
|
+
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.default
|
|
150
|
+
);
|
|
151
|
+
const [copyModeCol1, setCopyModeCol1] = useState<CopyMode>('prompt');
|
|
152
|
+
const [promptValueCol1, setPromptValueCol1] = useState('');
|
|
153
|
+
const [copyValueCol1, setCopyValueCol1] = useState('');
|
|
154
|
+
const [copyModeCol2, setCopyModeCol2] = useState<CopyMode>('prompt');
|
|
155
|
+
const [promptValueCol2, setPromptValueCol2] = useState('');
|
|
156
|
+
const [copyValueCol2, setCopyValueCol2] = useState('');
|
|
157
|
+
|
|
142
158
|
const [selectedLibraryEntry, setSelectedLibraryEntry] =
|
|
143
159
|
useState<DesignLibraryEntry | null>(null);
|
|
144
160
|
const [aiDesignConfig, setAiDesignConfig] = useState<AiDesignConfig>({
|
|
@@ -158,10 +174,15 @@ const AddPaneNewPanel = ({
|
|
|
158
174
|
} else if (choice === 'library') {
|
|
159
175
|
setStep('designLibrary');
|
|
160
176
|
} else if (choice === 'ai') {
|
|
161
|
-
setStep('
|
|
177
|
+
setStep('layoutChoice');
|
|
162
178
|
}
|
|
163
179
|
};
|
|
164
180
|
|
|
181
|
+
const handleLayoutChoice = (choice: LayoutChoice) => {
|
|
182
|
+
setLayoutChoice(choice);
|
|
183
|
+
setStep('aiDesign');
|
|
184
|
+
};
|
|
185
|
+
|
|
165
186
|
const handleBack = () => {
|
|
166
187
|
setError(null);
|
|
167
188
|
if (step === 'copyInput') {
|
|
@@ -172,13 +193,13 @@ const AddPaneNewPanel = ({
|
|
|
172
193
|
} else {
|
|
173
194
|
setStep('initial');
|
|
174
195
|
}
|
|
196
|
+
} else if (step === 'aiDesign') {
|
|
197
|
+
setStep('layoutChoice');
|
|
175
198
|
} else if (step === 'directInject') {
|
|
176
199
|
setStep('aiDesign');
|
|
177
|
-
} else if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
step === 'error'
|
|
181
|
-
) {
|
|
200
|
+
} else if (step === 'layoutChoice') {
|
|
201
|
+
setStep('initial');
|
|
202
|
+
} else if (step === 'designLibrary' || step === 'error') {
|
|
182
203
|
setStep('initial');
|
|
183
204
|
}
|
|
184
205
|
};
|
|
@@ -214,7 +235,7 @@ const AddPaneNewPanel = ({
|
|
|
214
235
|
setStep('copyInput');
|
|
215
236
|
};
|
|
216
237
|
|
|
217
|
-
const handleApplyTemplate = async (template:
|
|
238
|
+
const handleApplyTemplate = async (template: any) => {
|
|
218
239
|
if (!ctx) return;
|
|
219
240
|
try {
|
|
220
241
|
const insertTemplate = cloneDeep(template);
|
|
@@ -326,38 +347,102 @@ const AddPaneNewPanel = ({
|
|
|
326
347
|
if (aiDesignConfig.additionalNotes)
|
|
327
348
|
designInput += ` Refine with these notes: "${aiDesignConfig.additionalNotes}"`;
|
|
328
349
|
|
|
329
|
-
const shellPromptDetails = prompts.aiPaneShellPrompt;
|
|
330
|
-
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
331
350
|
const layout = 'Text Only';
|
|
332
351
|
|
|
333
|
-
|
|
334
|
-
.
|
|
335
|
-
.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
352
|
+
if (layoutChoice === 'standard') {
|
|
353
|
+
const shellPromptDetails = prompts.aiPaneShellPrompt;
|
|
354
|
+
const formattedShellPrompt = shellPromptDetails.user_template
|
|
355
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
356
|
+
.replace('{{LAYOUT_TYPE}}', layout);
|
|
357
|
+
|
|
358
|
+
const shellResult = await callAskLemurAPI(
|
|
359
|
+
formattedShellPrompt,
|
|
360
|
+
shellPromptDetails.system || '',
|
|
361
|
+
true,
|
|
362
|
+
isSandboxMode
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
366
|
+
const copyInputContent =
|
|
367
|
+
copyMode === 'prompt' ? promptValue : copyValue;
|
|
368
|
+
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
369
|
+
.replace('{{COPY_INPUT}}', copyInputContent)
|
|
370
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
371
|
+
.replace('{{LAYOUT_TYPE}}', layout)
|
|
372
|
+
.replace('{{SHELL_JSON}}', shellResult);
|
|
373
|
+
|
|
374
|
+
const copyResult = await callAskLemurAPI(
|
|
375
|
+
formattedCopyPrompt,
|
|
376
|
+
copyPromptDetails.system || '',
|
|
377
|
+
false,
|
|
378
|
+
isSandboxMode
|
|
379
|
+
);
|
|
380
|
+
const finalPane = parseAiPane(shellResult, copyResult, layout);
|
|
381
|
+
handleApplyTemplate(finalPane);
|
|
382
|
+
} else if (layoutChoice === 'grid') {
|
|
383
|
+
const shellPromptDetails = prompts.aiPaneShellPrompt_2cols;
|
|
384
|
+
const formattedShellPrompt = shellPromptDetails.user_template
|
|
385
|
+
.replace('{{COPY_INPUT}}', overallPrompt)
|
|
386
|
+
.replace('{{DESIGN_INPUT}}', designInput);
|
|
387
|
+
|
|
388
|
+
const shellResult = await callAskLemurAPI(
|
|
389
|
+
formattedShellPrompt,
|
|
390
|
+
shellPromptDetails.system || '',
|
|
391
|
+
true,
|
|
392
|
+
isSandboxMode
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const copyPromptDetails = prompts.aiPaneCopyPrompt_2cols;
|
|
396
|
+
const preset = copyPromptDetails.presets.heroDefault;
|
|
397
|
+
const copyResults: string[] = [];
|
|
398
|
+
|
|
399
|
+
const promptsToRun: {
|
|
400
|
+
prompt: string;
|
|
401
|
+
copy: string;
|
|
402
|
+
mode: CopyMode;
|
|
403
|
+
presetKey: ColumnPresetKey;
|
|
404
|
+
}[] = [
|
|
405
|
+
{
|
|
406
|
+
prompt: promptValueCol1,
|
|
407
|
+
copy: copyValueCol1,
|
|
408
|
+
mode: copyModeCol1,
|
|
409
|
+
presetKey: 'left',
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
prompt: promptValueCol2,
|
|
413
|
+
copy: copyValueCol2,
|
|
414
|
+
mode: copyModeCol2,
|
|
415
|
+
presetKey: 'right',
|
|
416
|
+
},
|
|
417
|
+
];
|
|
418
|
+
|
|
419
|
+
for (const item of promptsToRun) {
|
|
420
|
+
if (item.mode === 'raw') {
|
|
421
|
+
copyResults.push(item.copy);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const columnPreset = preset[item.presetKey];
|
|
426
|
+
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
427
|
+
.replace('{{SHELL_JSON}}', shellResult)
|
|
428
|
+
.replace('{{COPY_INPUT}}', overallPrompt)
|
|
429
|
+
.replace('{{COLUMN_PROMPT}}', item.prompt)
|
|
430
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
431
|
+
.replace('{{LAYOUT_TYPE}}', layout)
|
|
432
|
+
.replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
|
|
433
|
+
|
|
434
|
+
const copyResult = await callAskLemurAPI(
|
|
435
|
+
formattedCopyPrompt,
|
|
436
|
+
copyPromptDetails.system || '',
|
|
437
|
+
false,
|
|
438
|
+
isSandboxMode
|
|
439
|
+
);
|
|
440
|
+
copyResults.push(copyResult);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const finalPane = parseAiPane(shellResult, copyResults, layout);
|
|
444
|
+
handleApplyTemplate(finalPane);
|
|
445
|
+
}
|
|
361
446
|
}
|
|
362
447
|
} catch (err: any) {
|
|
363
448
|
setError(err.message || 'Failed to generate AI pane.');
|
|
@@ -368,8 +453,16 @@ const AddPaneNewPanel = ({
|
|
|
368
453
|
copyMode,
|
|
369
454
|
promptValue,
|
|
370
455
|
copyValue,
|
|
456
|
+
overallPrompt,
|
|
457
|
+
copyModeCol1,
|
|
458
|
+
promptValueCol1,
|
|
459
|
+
copyValueCol1,
|
|
460
|
+
copyModeCol2,
|
|
461
|
+
promptValueCol2,
|
|
462
|
+
copyValueCol2,
|
|
371
463
|
isSandboxMode,
|
|
372
464
|
initialChoice,
|
|
465
|
+
layoutChoice,
|
|
373
466
|
selectedLibraryEntry,
|
|
374
467
|
handleApplyTemplate,
|
|
375
468
|
]);
|
|
@@ -416,41 +509,157 @@ const AddPaneNewPanel = ({
|
|
|
416
509
|
</div>
|
|
417
510
|
);
|
|
418
511
|
|
|
419
|
-
const
|
|
420
|
-
<div className="
|
|
421
|
-
<
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
onPromptValueChange={setPromptValue}
|
|
426
|
-
copyValue={copyValue}
|
|
427
|
-
onCopyValueChange={setCopyValue}
|
|
428
|
-
defaultPrompt={
|
|
429
|
-
first
|
|
430
|
-
? prompts.aiPaneCopyPrompt.heroDefault
|
|
431
|
-
: prompts.aiPaneCopyPrompt.contentDefault
|
|
432
|
-
}
|
|
433
|
-
/>
|
|
434
|
-
<div className="flex justify-between">
|
|
512
|
+
const renderLayoutChoiceStep = () => (
|
|
513
|
+
<div className="p-4">
|
|
514
|
+
<h3 className="font-action mb-4 text-center text-xl font-bold text-gray-800">
|
|
515
|
+
Choose a Layout Structure
|
|
516
|
+
</h3>
|
|
517
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
435
518
|
<button
|
|
436
|
-
onClick={
|
|
437
|
-
className="
|
|
519
|
+
onClick={() => handleLayoutChoice('standard')}
|
|
520
|
+
className="group flex flex-col items-center space-y-3 rounded-lg border bg-white p-6 text-center shadow-sm transition-all hover:border-cyan-600 hover:shadow-lg"
|
|
438
521
|
>
|
|
439
|
-
|
|
522
|
+
<DocumentIcon className="h-10 w-10 text-gray-500 transition-colors group-hover:text-cyan-600" />
|
|
523
|
+
<h4 className="font-bold text-gray-800">Standard Layout</h4>
|
|
524
|
+
<p className="text-sm text-gray-600">
|
|
525
|
+
A single, continuous column of content.
|
|
526
|
+
</p>
|
|
440
527
|
</button>
|
|
441
528
|
<button
|
|
442
|
-
onClick={
|
|
443
|
-
|
|
444
|
-
copyMode === 'prompt' ? !promptValue.trim() : !copyValue.trim()
|
|
445
|
-
}
|
|
446
|
-
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
|
|
529
|
+
onClick={() => handleLayoutChoice('grid')}
|
|
530
|
+
className="group flex flex-col items-center space-y-3 rounded-lg border bg-white p-6 text-center shadow-sm transition-all hover:border-cyan-600 hover:shadow-lg"
|
|
447
531
|
>
|
|
448
|
-
|
|
532
|
+
<SquaresPlusIcon className="h-10 w-10 text-gray-500 transition-colors group-hover:text-cyan-600" />
|
|
533
|
+
<h4 className="font-bold text-gray-800">2-Column Grid</h4>
|
|
534
|
+
<p className="text-sm text-gray-600">
|
|
535
|
+
Side-by-side content that stacks on mobile.
|
|
536
|
+
</p>
|
|
537
|
+
</button>
|
|
538
|
+
</div>
|
|
539
|
+
<div className="mt-6 flex justify-center">
|
|
540
|
+
<button
|
|
541
|
+
onClick={handleBack}
|
|
542
|
+
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"
|
|
543
|
+
>
|
|
544
|
+
← Back
|
|
449
545
|
</button>
|
|
450
546
|
</div>
|
|
451
547
|
</div>
|
|
452
548
|
);
|
|
453
549
|
|
|
550
|
+
const renderContentStep = () => {
|
|
551
|
+
if (layoutChoice === 'grid') {
|
|
552
|
+
const isGenerateDisabled =
|
|
553
|
+
(copyModeCol1 === 'prompt' && !promptValueCol1.trim()) ||
|
|
554
|
+
(copyModeCol1 === 'raw' && !copyValueCol1.trim()) ||
|
|
555
|
+
(copyModeCol2 === 'prompt' && !promptValueCol2.trim()) ||
|
|
556
|
+
(copyModeCol2 === 'raw' && !copyValueCol2.trim()) ||
|
|
557
|
+
!overallPrompt.trim();
|
|
558
|
+
|
|
559
|
+
return (
|
|
560
|
+
<div className="space-y-4 p-4">
|
|
561
|
+
<div>
|
|
562
|
+
<h4 className="mb-2 block text-sm font-bold text-gray-700">
|
|
563
|
+
Overall Component Brief
|
|
564
|
+
</h4>
|
|
565
|
+
<textarea
|
|
566
|
+
value={overallPrompt}
|
|
567
|
+
onChange={(e) => setOverallPrompt(e.target.value)}
|
|
568
|
+
placeholder="e.g., A compelling hero section for a website about Tract Stack..."
|
|
569
|
+
rows={3}
|
|
570
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
571
|
+
/>
|
|
572
|
+
</div>
|
|
573
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
574
|
+
<div>
|
|
575
|
+
<h4 className="mb-2 block text-sm font-bold text-gray-700">
|
|
576
|
+
Left Column Content
|
|
577
|
+
</h4>
|
|
578
|
+
<CopyInputStep
|
|
579
|
+
copyMode={copyModeCol1}
|
|
580
|
+
onCopyModeChange={setCopyModeCol1}
|
|
581
|
+
promptValue={promptValueCol1}
|
|
582
|
+
onPromptValueChange={setPromptValueCol1}
|
|
583
|
+
copyValue={copyValueCol1}
|
|
584
|
+
onCopyValueChange={setCopyValueCol1}
|
|
585
|
+
defaultPrompt={
|
|
586
|
+
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.left.prompt
|
|
587
|
+
}
|
|
588
|
+
/>
|
|
589
|
+
</div>
|
|
590
|
+
<div>
|
|
591
|
+
<h4 className="mb-2 block text-sm font-bold text-gray-700">
|
|
592
|
+
Right Column Content
|
|
593
|
+
</h4>
|
|
594
|
+
<CopyInputStep
|
|
595
|
+
copyMode={copyModeCol2}
|
|
596
|
+
onCopyModeChange={setCopyModeCol2}
|
|
597
|
+
promptValue={promptValueCol2}
|
|
598
|
+
onPromptValueChange={setPromptValueCol2}
|
|
599
|
+
copyValue={copyValueCol2}
|
|
600
|
+
onCopyValueChange={setCopyValueCol2}
|
|
601
|
+
defaultPrompt={
|
|
602
|
+
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.right
|
|
603
|
+
.prompt
|
|
604
|
+
}
|
|
605
|
+
/>
|
|
606
|
+
</div>
|
|
607
|
+
</div>
|
|
608
|
+
<div className="flex justify-between">
|
|
609
|
+
<button
|
|
610
|
+
onClick={handleBack}
|
|
611
|
+
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"
|
|
612
|
+
>
|
|
613
|
+
← Back
|
|
614
|
+
</button>
|
|
615
|
+
<button
|
|
616
|
+
onClick={handleFinalGenerate}
|
|
617
|
+
disabled={isGenerateDisabled}
|
|
618
|
+
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
|
|
619
|
+
>
|
|
620
|
+
✨ Generate Pane
|
|
621
|
+
</button>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<div className="space-y-4 p-4">
|
|
629
|
+
<CopyInputStep
|
|
630
|
+
copyMode={copyMode}
|
|
631
|
+
onCopyModeChange={setCopyMode}
|
|
632
|
+
promptValue={promptValue}
|
|
633
|
+
onPromptValueChange={setPromptValue}
|
|
634
|
+
copyValue={copyValue}
|
|
635
|
+
onCopyValueChange={setCopyValue}
|
|
636
|
+
defaultPrompt={
|
|
637
|
+
first
|
|
638
|
+
? prompts.aiPaneCopyPrompt.heroDefault
|
|
639
|
+
: prompts.aiPaneCopyPrompt.contentDefault
|
|
640
|
+
}
|
|
641
|
+
/>
|
|
642
|
+
<div className="flex justify-between">
|
|
643
|
+
<button
|
|
644
|
+
onClick={handleBack}
|
|
645
|
+
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"
|
|
646
|
+
>
|
|
647
|
+
← Back
|
|
648
|
+
</button>
|
|
649
|
+
<button
|
|
650
|
+
onClick={handleFinalGenerate}
|
|
651
|
+
disabled={
|
|
652
|
+
copyMode === 'prompt' ? !promptValue.trim() : !copyValue.trim()
|
|
653
|
+
}
|
|
654
|
+
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
|
|
655
|
+
>
|
|
656
|
+
✨ Generate Pane
|
|
657
|
+
</button>
|
|
658
|
+
</div>
|
|
659
|
+
</div>
|
|
660
|
+
);
|
|
661
|
+
};
|
|
662
|
+
|
|
454
663
|
const renderDesignLibraryStep = () => (
|
|
455
664
|
<div className="space-y-4 p-4">
|
|
456
665
|
<div className="flex justify-start">
|
|
@@ -461,17 +670,13 @@ const AddPaneNewPanel = ({
|
|
|
461
670
|
← Back to Choice
|
|
462
671
|
</button>
|
|
463
672
|
</div>
|
|
464
|
-
<DesignLibraryStep
|
|
465
|
-
config={config!}
|
|
466
|
-
onSelect={handleDesignLibrarySelect}
|
|
467
|
-
/>
|
|
673
|
+
<DesignLibraryStep onSelect={handleDesignLibrarySelect} />
|
|
468
674
|
</div>
|
|
469
675
|
);
|
|
470
676
|
|
|
471
677
|
const renderAiDesignStep = () => (
|
|
472
678
|
<div className="space-y-4 p-4">
|
|
473
679
|
<AiDesignStep
|
|
474
|
-
config={config!}
|
|
475
680
|
designConfig={aiDesignConfig}
|
|
476
681
|
onDesignConfigChange={setAiDesignConfig}
|
|
477
682
|
/>
|
|
@@ -532,6 +737,8 @@ const AddPaneNewPanel = ({
|
|
|
532
737
|
switch (step) {
|
|
533
738
|
case 'initial':
|
|
534
739
|
return renderInitialStep();
|
|
740
|
+
case 'layoutChoice':
|
|
741
|
+
return renderLayoutChoiceStep();
|
|
535
742
|
case 'copyInput':
|
|
536
743
|
return renderContentStep();
|
|
537
744
|
case 'designLibrary':
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { getCtx, type NodesContext } from '@/stores/nodes';
|
|
3
|
+
import { PaneAddMode, type StoragePane } from '@/types/compositorTypes';
|
|
4
|
+
import {
|
|
5
|
+
remapPaneIds,
|
|
6
|
+
convertStorageToLiveTemplate,
|
|
7
|
+
} from '@/utils/compositor/designLibraryHelper';
|
|
8
|
+
|
|
9
|
+
interface AddPanePanelPasteProps {
|
|
10
|
+
nodeId: string;
|
|
11
|
+
first: boolean;
|
|
12
|
+
setMode: (mode: PaneAddMode, reset?: boolean) => void;
|
|
13
|
+
ctx?: NodesContext;
|
|
14
|
+
isStoryFragment?: boolean;
|
|
15
|
+
isContextPane?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const AddPanePanel_paste = ({
|
|
19
|
+
nodeId,
|
|
20
|
+
first,
|
|
21
|
+
setMode,
|
|
22
|
+
ctx: providedCtx,
|
|
23
|
+
isStoryFragment,
|
|
24
|
+
isContextPane,
|
|
25
|
+
}: AddPanePanelPasteProps) => {
|
|
26
|
+
const [jsonInput, setJsonInput] = useState('');
|
|
27
|
+
const [error, setError] = useState<string | null>(null);
|
|
28
|
+
const ctx = providedCtx || getCtx();
|
|
29
|
+
|
|
30
|
+
const handleCreate = () => {
|
|
31
|
+
setError(null);
|
|
32
|
+
if (!jsonInput.trim()) {
|
|
33
|
+
setError('Paste content cannot be empty.');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const parsedPane = JSON.parse(jsonInput) as StoragePane;
|
|
39
|
+
if (parsedPane.nodeType !== 'Pane') {
|
|
40
|
+
throw new Error('Pasted content is not a valid Pane object.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const remappedPane = remapPaneIds(parsedPane);
|
|
44
|
+
const liveTemplate = convertStorageToLiveTemplate(remappedPane);
|
|
45
|
+
|
|
46
|
+
const ownerId =
|
|
47
|
+
isStoryFragment || isContextPane
|
|
48
|
+
? nodeId
|
|
49
|
+
: ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
|
|
50
|
+
|
|
51
|
+
ctx.addTemplatePane(
|
|
52
|
+
ownerId,
|
|
53
|
+
liveTemplate,
|
|
54
|
+
nodeId,
|
|
55
|
+
first ? 'before' : 'after'
|
|
56
|
+
);
|
|
57
|
+
ctx.notifyNode('root');
|
|
58
|
+
setMode(PaneAddMode.DEFAULT, true);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const message =
|
|
61
|
+
err instanceof Error
|
|
62
|
+
? err.message
|
|
63
|
+
: 'An unknown error occurred during parsing.';
|
|
64
|
+
setError(`Invalid Pane JSON: ${message}`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="p-2 shadow-inner">
|
|
70
|
+
<div className="rounded-md border bg-gray-50 p-4">
|
|
71
|
+
<div className="flex items-center justify-between pb-4">
|
|
72
|
+
<div className="flex items-center gap-2">
|
|
73
|
+
<button
|
|
74
|
+
onClick={() => setMode(PaneAddMode.DEFAULT)}
|
|
75
|
+
className="w-fit rounded bg-gray-200 px-3 py-1 text-sm text-gray-800 transition-colors hover:bg-gray-300"
|
|
76
|
+
>
|
|
77
|
+
← Go Back
|
|
78
|
+
</button>
|
|
79
|
+
<h3 className="font-action text-sm font-bold text-cyan-700">
|
|
80
|
+
Paste Pane
|
|
81
|
+
</h3>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="space-y-4">
|
|
85
|
+
<p className="text-sm text-gray-600">
|
|
86
|
+
Paste the JSON content of a copied pane into the text area below.
|
|
87
|
+
</p>
|
|
88
|
+
<textarea
|
|
89
|
+
value={jsonInput}
|
|
90
|
+
onChange={(e) => setJsonInput(e.target.value)}
|
|
91
|
+
placeholder="Paste pane JSON here..."
|
|
92
|
+
className="h-48 w-full rounded-md border border-gray-300 bg-white p-2 font-mono text-xs focus:border-cyan-500 focus:ring-cyan-500"
|
|
93
|
+
spellCheck={false}
|
|
94
|
+
/>
|
|
95
|
+
{error && <p className="text-sm text-red-600">{error}</p>}
|
|
96
|
+
<div className="flex justify-end">
|
|
97
|
+
<button
|
|
98
|
+
onClick={handleCreate}
|
|
99
|
+
disabled={!jsonInput.trim()}
|
|
100
|
+
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
|
|
101
|
+
>
|
|
102
|
+
Create Pane from Paste
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default AddPanePanel_paste;
|
|
@@ -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,
|
|
28
|
-
BaseNode,
|
|
28
|
+
TemplateMarkdown,
|
|
29
|
+
BaseNode,
|
|
29
30
|
} from '@/types/compositorTypes';
|
|
30
|
-
import type {
|
|
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<{
|
|
@@ -111,7 +110,6 @@ const TemplatePreviewItem = ({
|
|
|
111
110
|
id={template.id}
|
|
112
111
|
htmlString={previewState.htmlFragment}
|
|
113
112
|
outputWidth={800}
|
|
114
|
-
config={config}
|
|
115
113
|
onComplete={(_id, data) => handleSnapshotComplete(data)}
|
|
116
114
|
onError={(_id, err) =>
|
|
117
115
|
setPreviewState((prev) =>
|
|
@@ -141,16 +139,12 @@ const TemplatePreviewItem = ({
|
|
|
141
139
|
);
|
|
142
140
|
};
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
config: BrandConfig;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
142
|
+
export const RestylePaneModal = () => {
|
|
149
143
|
const ctx = getCtx();
|
|
150
144
|
const { isRestyleModalOpen, paneToRestyleId } = useStore(selectionStore, {
|
|
151
145
|
keys: ['isRestyleModalOpen', 'paneToRestyleId'],
|
|
152
146
|
});
|
|
153
|
-
const designLibrary =
|
|
147
|
+
const designLibrary = brandConfigStore.get()?.DESIGN_LIBRARY || [];
|
|
154
148
|
|
|
155
149
|
const [selectedCategory, setSelectedCategory] = useState<string>('all');
|
|
156
150
|
const [searchTerm, setSearchTerm] = useState('');
|
|
@@ -406,7 +400,7 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
406
400
|
<Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
|
|
407
401
|
<Dialog.Content
|
|
408
402
|
className="flex flex-col rounded-lg bg-white shadow-2xl"
|
|
409
|
-
style={{
|
|
403
|
+
style={{ maxHeight: '90vw', width: '90vw' }}
|
|
410
404
|
>
|
|
411
405
|
<header className="flex items-center justify-between border-b p-4">
|
|
412
406
|
<Dialog.Title className="text-xl font-bold">
|
|
@@ -502,7 +496,6 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
502
496
|
<TemplatePreviewItem
|
|
503
497
|
key={template.id}
|
|
504
498
|
template={template}
|
|
505
|
-
config={config}
|
|
506
499
|
onClick={() => handleSelectTemplate(template)}
|
|
507
500
|
/>
|
|
508
501
|
))}
|