astro-tractstack 2.0.43 → 2.0.44
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/package.json +1 -1
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +156 -343
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +7 -5
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +1 -4
- package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +238 -96
package/package.json
CHANGED
|
@@ -22,16 +22,13 @@ import {
|
|
|
22
22
|
} from '@/utils/compositor/designLibraryHelper';
|
|
23
23
|
import { DirectInjectStep } from './steps/DirectInjectStep';
|
|
24
24
|
import BooleanToggle from '@/components/form/BooleanToggle';
|
|
25
|
-
import EnumSelect from '@/components/form/EnumSelect';
|
|
26
25
|
import type { StoryFragmentNode } from '@/types/compositorTypes';
|
|
27
26
|
import { TractStackAPI } from '@/utils/api';
|
|
28
27
|
|
|
29
28
|
type Step =
|
|
30
29
|
| 'initial'
|
|
31
|
-
| '
|
|
30
|
+
| 'dashboard'
|
|
32
31
|
| 'designLibrary'
|
|
33
|
-
| 'layoutChoice'
|
|
34
|
-
| 'aiDesign'
|
|
35
32
|
| 'loading'
|
|
36
33
|
| 'error'
|
|
37
34
|
| 'directInject';
|
|
@@ -63,7 +60,6 @@ const callAskLemurAPI = async (
|
|
|
63
60
|
let resultData: any;
|
|
64
61
|
|
|
65
62
|
if (isSandboxMode) {
|
|
66
|
-
// Sandbox mode still uses local fetch, but we pass the correct tenant ID
|
|
67
63
|
const response = await fetch(`/api/sandbox`, {
|
|
68
64
|
method: 'POST',
|
|
69
65
|
headers: { 'Content-Type': 'application/json', 'X-Tenant-ID': tenantId },
|
|
@@ -80,9 +76,8 @@ const callAskLemurAPI = async (
|
|
|
80
76
|
if (!json.success) {
|
|
81
77
|
throw new Error(json.error || 'Sandbox generation failed');
|
|
82
78
|
}
|
|
83
|
-
resultData = json.data;
|
|
79
|
+
resultData = json.data;
|
|
84
80
|
} else {
|
|
85
|
-
// Production mode: Use the robust API class
|
|
86
81
|
const response = await api.post('/api/v1/aai/askLemur', requestBody);
|
|
87
82
|
|
|
88
83
|
if (!response.success) {
|
|
@@ -90,9 +85,7 @@ const callAskLemurAPI = async (
|
|
|
90
85
|
response.error || 'Generation failed to return valid response.'
|
|
91
86
|
);
|
|
92
87
|
}
|
|
93
|
-
|
|
94
|
-
// TractStackAPI unwraps the 'data' field automatically
|
|
95
|
-
resultData = response.data; // { response: ... }
|
|
88
|
+
resultData = response.data;
|
|
96
89
|
}
|
|
97
90
|
|
|
98
91
|
if (!resultData?.response) {
|
|
@@ -137,6 +130,7 @@ const AddPaneNewPanel = ({
|
|
|
137
130
|
}: AddPaneNewPanelProps) => {
|
|
138
131
|
const ctx = providedCtx || getCtx();
|
|
139
132
|
const hasAssemblyAI = useStore(hasAssemblyAIStore);
|
|
133
|
+
|
|
140
134
|
const [step, setStep] = useState<Step>('initial');
|
|
141
135
|
const [initialChoice, setInitialChoice] = useState<InitialChoice | null>(
|
|
142
136
|
null
|
|
@@ -144,6 +138,10 @@ const AddPaneNewPanel = ({
|
|
|
144
138
|
const [layoutChoice, setLayoutChoice] = useState<LayoutChoice>('standard');
|
|
145
139
|
const [error, setError] = useState<string | null>(null);
|
|
146
140
|
|
|
141
|
+
const [topic, setTopic] = useState('');
|
|
142
|
+
const [showAdvancedPrompts, setShowAdvancedPrompts] = useState(false);
|
|
143
|
+
const [showStyles, setShowStyles] = useState(false);
|
|
144
|
+
|
|
147
145
|
const [selectedPromptId, setSelectedPromptId] = useState<string>('');
|
|
148
146
|
const [isAiStyling, setIsAiStyling] = useState(false);
|
|
149
147
|
|
|
@@ -151,15 +149,9 @@ const AddPaneNewPanel = ({
|
|
|
151
149
|
const [promptValue, setPromptValue] = useState('');
|
|
152
150
|
const [copyValue, setCopyValue] = useState('');
|
|
153
151
|
|
|
154
|
-
const [overallPrompt, setOverallPrompt] = useState(
|
|
155
|
-
|
|
156
|
-
);
|
|
157
|
-
const [promptValueCol1, setPromptValueCol1] = useState(
|
|
158
|
-
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.left.prompt
|
|
159
|
-
);
|
|
160
|
-
const [promptValueCol2, setPromptValueCol2] = useState(
|
|
161
|
-
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.right.prompt
|
|
162
|
-
);
|
|
152
|
+
const [overallPrompt, setOverallPrompt] = useState('');
|
|
153
|
+
const [promptValueCol1, setPromptValueCol1] = useState('');
|
|
154
|
+
const [promptValueCol2, setPromptValueCol2] = useState('');
|
|
163
155
|
const [col1Copy, setCol1Copy] = useState('');
|
|
164
156
|
const [col2Copy, setCol2Copy] = useState('');
|
|
165
157
|
|
|
@@ -206,18 +198,23 @@ const AddPaneNewPanel = ({
|
|
|
206
198
|
? activeConfig.variants[0]
|
|
207
199
|
: 'default';
|
|
208
200
|
|
|
201
|
+
const injectTopic = (template: string) => {
|
|
202
|
+
if (!template) return '';
|
|
203
|
+
return template.replace('[topic]', topic.trim() || '[topic]');
|
|
204
|
+
};
|
|
205
|
+
|
|
209
206
|
if (layoutChoice === 'standard') {
|
|
210
|
-
const
|
|
211
|
-
setPromptValue(
|
|
207
|
+
const baseText = copyPromptGroup[variant] || '';
|
|
208
|
+
setPromptValue(injectTopic(baseText));
|
|
212
209
|
} else if (layoutChoice === 'grid') {
|
|
213
210
|
const preset = copyPromptGroup.presets?.[variant];
|
|
214
211
|
if (preset) {
|
|
215
|
-
setOverallPrompt(preset.default || '');
|
|
212
|
+
setOverallPrompt(injectTopic(preset.default || ''));
|
|
216
213
|
setPromptValueCol1(preset.left?.prompt || '');
|
|
217
214
|
setPromptValueCol2(preset.right?.prompt || '');
|
|
218
215
|
}
|
|
219
216
|
}
|
|
220
|
-
}, [selectedPromptId, layoutChoice]);
|
|
217
|
+
}, [selectedPromptId, layoutChoice, topic]);
|
|
221
218
|
|
|
222
219
|
const handleInitialChoice = (choice: InitialChoice) => {
|
|
223
220
|
setInitialChoice(choice);
|
|
@@ -228,32 +225,22 @@ const AddPaneNewPanel = ({
|
|
|
228
225
|
} else if (choice === 'library') {
|
|
229
226
|
setStep('designLibrary');
|
|
230
227
|
} else if (choice === 'ai') {
|
|
231
|
-
setStep('
|
|
228
|
+
setStep('dashboard');
|
|
232
229
|
}
|
|
233
230
|
};
|
|
234
231
|
|
|
235
|
-
const handleLayoutChoice = (choice: LayoutChoice) => {
|
|
236
|
-
setLayoutChoice(choice);
|
|
237
|
-
setStep('aiDesign');
|
|
238
|
-
};
|
|
239
|
-
|
|
240
232
|
const handleBack = () => {
|
|
241
233
|
setError(null);
|
|
242
|
-
if (step === '
|
|
243
|
-
if (initialChoice === 'library') {
|
|
244
|
-
setStep('designLibrary');
|
|
245
|
-
} else if (initialChoice === 'ai') {
|
|
246
|
-
setStep('aiDesign');
|
|
247
|
-
} else {
|
|
248
|
-
setStep('initial');
|
|
249
|
-
}
|
|
250
|
-
} else if (step === 'aiDesign') {
|
|
251
|
-
setStep('layoutChoice');
|
|
252
|
-
} else if (step === 'directInject') {
|
|
253
|
-
setStep('aiDesign');
|
|
254
|
-
} else if (step === 'layoutChoice') {
|
|
234
|
+
if (step === 'dashboard') {
|
|
255
235
|
setStep('initial');
|
|
256
|
-
|
|
236
|
+
setTopic('');
|
|
237
|
+
setShowAdvancedPrompts(false);
|
|
238
|
+
setShowStyles(false);
|
|
239
|
+
} else if (
|
|
240
|
+
step === 'designLibrary' ||
|
|
241
|
+
step === 'error' ||
|
|
242
|
+
step === 'directInject'
|
|
243
|
+
) {
|
|
257
244
|
setStep('initial');
|
|
258
245
|
}
|
|
259
246
|
};
|
|
@@ -280,10 +267,6 @@ const AddPaneNewPanel = ({
|
|
|
280
267
|
handleApplyTemplate(blankTemplate);
|
|
281
268
|
};
|
|
282
269
|
|
|
283
|
-
const handleAiDesignContinue = () => {
|
|
284
|
-
setStep('copyInput');
|
|
285
|
-
};
|
|
286
|
-
|
|
287
270
|
const handleApplyTemplate = async (template: any) => {
|
|
288
271
|
if (!ctx) return;
|
|
289
272
|
try {
|
|
@@ -347,8 +330,7 @@ const AddPaneNewPanel = ({
|
|
|
347
330
|
} else {
|
|
348
331
|
setCopyMode('prompt');
|
|
349
332
|
}
|
|
350
|
-
|
|
351
|
-
setStep('copyInput');
|
|
333
|
+
setStep('dashboard');
|
|
352
334
|
};
|
|
353
335
|
|
|
354
336
|
const handleFinalGenerate = useCallback(async () => {
|
|
@@ -571,39 +553,47 @@ const AddPaneNewPanel = ({
|
|
|
571
553
|
isSandboxMode
|
|
572
554
|
);
|
|
573
555
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for (const item of promptsToRun) {
|
|
587
|
-
const columnPreset = preset[item.presetKey];
|
|
588
|
-
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
589
|
-
.replace('{{SHELL_JSON}}', shellResult)
|
|
590
|
-
.replace('{{COPY_INPUT}}', overallPrompt)
|
|
591
|
-
.replace('{{COLUMN_PROMPT}}', item.prompt)
|
|
592
|
-
.replace('{{DESIGN_INPUT}}', designInput)
|
|
593
|
-
.replace('{{LAYOUT_TYPE}}', layout)
|
|
594
|
-
.replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
|
|
556
|
+
if (copyMode === 'raw') {
|
|
557
|
+
const rawContents = [col1Copy, col2Copy];
|
|
558
|
+
const finalPane = parseAiPane(shellResult, rawContents, layout);
|
|
559
|
+
handleApplyTemplate(finalPane);
|
|
560
|
+
} else {
|
|
561
|
+
const copyPromptKey = activeConfig.prompts.copy;
|
|
562
|
+
const copyPromptDetails = promptMap[copyPromptKey];
|
|
563
|
+
const preset =
|
|
564
|
+
copyPromptDetails.presets?.[activeConfig.variants[0]] ||
|
|
565
|
+
copyPromptDetails.presets?.heroDefault;
|
|
566
|
+
const copyResults: string[] = [];
|
|
595
567
|
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
568
|
+
const promptsToRun = [
|
|
569
|
+
{ prompt: promptValueCol1, presetKey: 'left' as ColumnPresetKey },
|
|
570
|
+
{
|
|
571
|
+
prompt: promptValueCol2,
|
|
572
|
+
presetKey: 'right' as ColumnPresetKey,
|
|
573
|
+
},
|
|
574
|
+
];
|
|
575
|
+
|
|
576
|
+
for (const item of promptsToRun) {
|
|
577
|
+
const columnPreset = preset[item.presetKey];
|
|
578
|
+
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
579
|
+
.replace('{{SHELL_JSON}}', shellResult)
|
|
580
|
+
.replace('{{COPY_INPUT}}', overallPrompt)
|
|
581
|
+
.replace('{{COLUMN_PROMPT}}', item.prompt)
|
|
582
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
583
|
+
.replace('{{LAYOUT_TYPE}}', layout)
|
|
584
|
+
.replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
|
|
585
|
+
|
|
586
|
+
const copyResult = await callAskLemurAPI(
|
|
587
|
+
formattedCopyPrompt,
|
|
588
|
+
copyPromptDetails.system || '',
|
|
589
|
+
false,
|
|
590
|
+
isSandboxMode
|
|
591
|
+
);
|
|
592
|
+
copyResults.push(copyResult);
|
|
593
|
+
}
|
|
594
|
+
const finalPane = parseAiPane(shellResult, copyResults, layout);
|
|
595
|
+
handleApplyTemplate(finalPane);
|
|
603
596
|
}
|
|
604
|
-
|
|
605
|
-
const finalPane = parseAiPane(shellResult, copyResults, layout);
|
|
606
|
-
handleApplyTemplate(finalPane);
|
|
607
597
|
}
|
|
608
598
|
}
|
|
609
599
|
} catch (err: any) {
|
|
@@ -671,235 +661,62 @@ const AddPaneNewPanel = ({
|
|
|
671
661
|
</div>
|
|
672
662
|
);
|
|
673
663
|
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
<
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
onClick={handleBack}
|
|
704
|
-
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"
|
|
705
|
-
>
|
|
706
|
-
← Back
|
|
707
|
-
</button>
|
|
708
|
-
</div>
|
|
709
|
-
</div>
|
|
710
|
-
);
|
|
711
|
-
|
|
712
|
-
const renderContentStep = () => {
|
|
713
|
-
if (layoutChoice === 'grid') {
|
|
714
|
-
const isGenerateDisabled =
|
|
715
|
-
copyMode === 'prompt'
|
|
716
|
-
? !overallPrompt.trim() ||
|
|
717
|
-
!promptValueCol1.trim() ||
|
|
718
|
-
!promptValueCol2.trim()
|
|
719
|
-
: copyMode === 'raw'
|
|
720
|
-
? !col1Copy.trim() || !col2Copy.trim()
|
|
721
|
-
: false;
|
|
722
|
-
|
|
723
|
-
return (
|
|
724
|
-
<div className="space-y-4 p-4">
|
|
725
|
-
<label className="block text-lg font-bold text-gray-800">
|
|
726
|
-
1. Provide Content
|
|
727
|
-
</label>
|
|
728
|
-
|
|
729
|
-
<div className="my-2 flex flex-wrap gap-4">
|
|
730
|
-
<div className="flex items-center space-x-2">
|
|
731
|
-
<input
|
|
732
|
-
type="radio"
|
|
733
|
-
checked={copyMode === 'prompt'}
|
|
734
|
-
onChange={() => setCopyMode('prompt')}
|
|
735
|
-
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
736
|
-
/>
|
|
737
|
-
<label className="text-sm font-bold text-gray-700">Prompt</label>
|
|
738
|
-
</div>
|
|
739
|
-
<div className="flex items-center space-x-2">
|
|
740
|
-
<input
|
|
741
|
-
type="radio"
|
|
742
|
-
checked={copyMode === 'raw'}
|
|
743
|
-
onChange={() => setCopyMode('raw')}
|
|
744
|
-
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
745
|
-
/>
|
|
746
|
-
<label className="text-sm font-bold text-gray-700">
|
|
747
|
-
Manual Markdown
|
|
748
|
-
</label>
|
|
749
|
-
</div>
|
|
750
|
-
{selectedLibraryEntry?.retain && (
|
|
751
|
-
<div className="flex items-center space-x-2">
|
|
752
|
-
<input
|
|
753
|
-
type="radio"
|
|
754
|
-
checked={copyMode === 'original'}
|
|
755
|
-
onChange={() => setCopyMode('original')}
|
|
756
|
-
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
757
|
-
/>
|
|
758
|
-
<label className="text-sm font-bold text-gray-700">
|
|
759
|
-
Use Original
|
|
760
|
-
</label>
|
|
761
|
-
</div>
|
|
762
|
-
)}
|
|
763
|
-
</div>
|
|
764
|
-
|
|
765
|
-
{copyMode === 'raw' && initialChoice === 'library' && (
|
|
766
|
-
<div className="mb-4 flex items-center justify-between rounded-lg border border-gray-100 bg-gray-50 p-2">
|
|
767
|
-
<span className="text-sm text-gray-600">
|
|
768
|
-
Style this content with AI?
|
|
769
|
-
</span>
|
|
770
|
-
<div className="flex items-center">
|
|
771
|
-
<BooleanToggle
|
|
772
|
-
label="AI Styles"
|
|
773
|
-
value={isAiStyling}
|
|
774
|
-
onChange={setIsAiStyling}
|
|
775
|
-
size="sm"
|
|
776
|
-
/>
|
|
777
|
-
</div>
|
|
778
|
-
</div>
|
|
779
|
-
)}
|
|
780
|
-
|
|
781
|
-
{copyMode === 'prompt' && (
|
|
782
|
-
<>
|
|
783
|
-
<div className="mb-4">
|
|
784
|
-
<EnumSelect
|
|
785
|
-
label="Section Type"
|
|
786
|
-
value={selectedPromptId}
|
|
787
|
-
onChange={setSelectedPromptId}
|
|
788
|
-
options={promptOptions}
|
|
789
|
-
placeholder="Select a section type..."
|
|
790
|
-
className="w-full"
|
|
791
|
-
/>
|
|
792
|
-
</div>
|
|
793
|
-
<div>
|
|
794
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
795
|
-
Overall Component Brief
|
|
796
|
-
</label>
|
|
797
|
-
<textarea
|
|
798
|
-
value={overallPrompt}
|
|
799
|
-
onChange={(e) => setOverallPrompt(e.target.value)}
|
|
800
|
-
rows={3}
|
|
801
|
-
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
802
|
-
/>
|
|
803
|
-
<p className="mt-1 text-xs text-gray-500">
|
|
804
|
-
This context is applied to both columns.
|
|
805
|
-
</p>
|
|
806
|
-
</div>
|
|
807
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
808
|
-
<div>
|
|
809
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
810
|
-
Left Column Prompt
|
|
811
|
-
</label>
|
|
812
|
-
<textarea
|
|
813
|
-
value={promptValueCol1}
|
|
814
|
-
onChange={(e) => setPromptValueCol1(e.target.value)}
|
|
815
|
-
rows={4}
|
|
816
|
-
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
817
|
-
/>
|
|
818
|
-
</div>
|
|
819
|
-
<div>
|
|
820
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
821
|
-
Right Column Prompt
|
|
822
|
-
</label>
|
|
823
|
-
<textarea
|
|
824
|
-
value={promptValueCol2}
|
|
825
|
-
onChange={(e) => setPromptValueCol2(e.target.value)}
|
|
826
|
-
rows={4}
|
|
827
|
-
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
828
|
-
/>
|
|
829
|
-
</div>
|
|
830
|
-
</div>
|
|
831
|
-
</>
|
|
832
|
-
)}
|
|
833
|
-
|
|
834
|
-
{copyMode === 'raw' && (
|
|
835
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
836
|
-
<div>
|
|
837
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
838
|
-
Left Column Markdown
|
|
839
|
-
</label>
|
|
840
|
-
<textarea
|
|
841
|
-
value={col1Copy}
|
|
842
|
-
onChange={(e) => setCol1Copy(e.target.value)}
|
|
843
|
-
rows={8}
|
|
844
|
-
className="block w-full rounded-md border-gray-300 p-2 font-mono text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
845
|
-
/>
|
|
846
|
-
</div>
|
|
847
|
-
<div>
|
|
848
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
849
|
-
Right Column Markdown
|
|
850
|
-
</label>
|
|
851
|
-
<textarea
|
|
852
|
-
value={col2Copy}
|
|
853
|
-
onChange={(e) => setCol2Copy(e.target.value)}
|
|
854
|
-
rows={8}
|
|
855
|
-
className="block w-full rounded-md border-gray-300 p-2 font-mono text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
856
|
-
/>
|
|
857
|
-
</div>
|
|
858
|
-
</div>
|
|
859
|
-
)}
|
|
860
|
-
|
|
861
|
-
{copyMode === 'original' && (
|
|
862
|
-
<div className="rounded-md border border-blue-200 bg-blue-50 p-4 text-blue-700">
|
|
863
|
-
<p className="text-sm">
|
|
864
|
-
The original text saved with this design will be used.
|
|
865
|
-
</p>
|
|
664
|
+
const renderDashboard = () => {
|
|
665
|
+
return (
|
|
666
|
+
<div className="space-y-6 p-4">
|
|
667
|
+
{/* Layout Selection */}
|
|
668
|
+
{initialChoice === 'ai' && (
|
|
669
|
+
<div className="flex justify-center border-b pb-4">
|
|
670
|
+
<div className="inline-flex rounded-lg bg-gray-100 p-1">
|
|
671
|
+
<button
|
|
672
|
+
onClick={() => setLayoutChoice('standard')}
|
|
673
|
+
className={`flex items-center space-x-2 rounded-md px-4 py-2 text-sm font-bold transition-all ${
|
|
674
|
+
layoutChoice === 'standard'
|
|
675
|
+
? 'bg-white text-cyan-700 shadow-sm'
|
|
676
|
+
: 'text-gray-500 hover:text-gray-900'
|
|
677
|
+
}`}
|
|
678
|
+
>
|
|
679
|
+
<DocumentIcon className="h-5 w-5" />
|
|
680
|
+
<span>Standard Layout</span>
|
|
681
|
+
</button>
|
|
682
|
+
<button
|
|
683
|
+
onClick={() => setLayoutChoice('grid')}
|
|
684
|
+
className={`flex items-center space-x-2 rounded-md px-4 py-2 text-sm font-bold transition-all ${
|
|
685
|
+
layoutChoice === 'grid'
|
|
686
|
+
? 'bg-white text-cyan-700 shadow-sm'
|
|
687
|
+
: 'text-gray-500 hover:text-gray-900'
|
|
688
|
+
}`}
|
|
689
|
+
>
|
|
690
|
+
<SquaresPlusIcon className="h-5 w-5" />
|
|
691
|
+
<span>2-Column Grid</span>
|
|
692
|
+
</button>
|
|
866
693
|
</div>
|
|
867
|
-
)}
|
|
868
|
-
|
|
869
|
-
<div className="flex justify-between">
|
|
870
|
-
<button
|
|
871
|
-
onClick={handleBack}
|
|
872
|
-
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"
|
|
873
|
-
>
|
|
874
|
-
← Back
|
|
875
|
-
</button>
|
|
876
|
-
<button
|
|
877
|
-
onClick={handleFinalGenerate}
|
|
878
|
-
disabled={isGenerateDisabled}
|
|
879
|
-
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"
|
|
880
|
-
>
|
|
881
|
-
✨ Generate Pane
|
|
882
|
-
</button>
|
|
883
694
|
</div>
|
|
884
|
-
|
|
885
|
-
);
|
|
886
|
-
}
|
|
695
|
+
)}
|
|
887
696
|
|
|
888
|
-
return (
|
|
889
|
-
<div className="space-y-4 p-4">
|
|
890
697
|
<CopyInputStep
|
|
698
|
+
layoutChoice={layoutChoice}
|
|
891
699
|
copyMode={copyMode}
|
|
892
700
|
onCopyModeChange={setCopyMode}
|
|
701
|
+
topic={topic}
|
|
702
|
+
onTopicChange={setTopic}
|
|
703
|
+
showAdvancedPrompts={showAdvancedPrompts}
|
|
704
|
+
onShowAdvancedPromptsChange={setShowAdvancedPrompts}
|
|
893
705
|
promptValue={promptValue}
|
|
894
706
|
onPromptValueChange={setPromptValue}
|
|
895
707
|
copyValue={copyValue}
|
|
896
708
|
onCopyValueChange={setCopyValue}
|
|
709
|
+
overallPrompt={overallPrompt}
|
|
710
|
+
onOverallPromptChange={setOverallPrompt}
|
|
711
|
+
promptValueCol1={promptValueCol1}
|
|
712
|
+
onPromptValueCol1Change={setPromptValueCol1}
|
|
713
|
+
promptValueCol2={promptValueCol2}
|
|
714
|
+
onPromptValueCol2Change={setPromptValueCol2}
|
|
715
|
+
col1Copy={col1Copy}
|
|
716
|
+
onCol1CopyChange={setCol1Copy}
|
|
717
|
+
col2Copy={col2Copy}
|
|
718
|
+
onCol2CopyChange={setCol2Copy}
|
|
897
719
|
hasRetainedContent={selectedLibraryEntry?.retain}
|
|
898
|
-
defaultPrompt={
|
|
899
|
-
first
|
|
900
|
-
? prompts.aiPaneCopyPrompt.heroDefault
|
|
901
|
-
: prompts.aiPaneCopyPrompt.contentDefault
|
|
902
|
-
}
|
|
903
720
|
promptOptions={promptOptions}
|
|
904
721
|
selectedPromptId={selectedPromptId}
|
|
905
722
|
onSelectedPromptIdChange={setSelectedPromptId}
|
|
@@ -907,27 +724,61 @@ const AddPaneNewPanel = ({
|
|
|
907
724
|
onIsAiStylingChange={setIsAiStyling}
|
|
908
725
|
showStyleToggle={initialChoice === 'library'}
|
|
909
726
|
/>
|
|
910
|
-
|
|
727
|
+
|
|
728
|
+
{initialChoice === 'ai' && (
|
|
729
|
+
<>
|
|
730
|
+
<div className="my-4 flex items-center">
|
|
731
|
+
<BooleanToggle
|
|
732
|
+
label="Customize Styles"
|
|
733
|
+
value={showStyles}
|
|
734
|
+
onChange={setShowStyles}
|
|
735
|
+
size="sm"
|
|
736
|
+
/>
|
|
737
|
+
</div>
|
|
738
|
+
|
|
739
|
+
{showStyles && (
|
|
740
|
+
<div className="rounded-lg border border-gray-100 bg-gray-50 p-4">
|
|
741
|
+
<AiDesignStep
|
|
742
|
+
designConfig={aiDesignConfig}
|
|
743
|
+
onDesignConfigChange={setAiDesignConfig}
|
|
744
|
+
/>
|
|
745
|
+
</div>
|
|
746
|
+
)}
|
|
747
|
+
</>
|
|
748
|
+
)}
|
|
749
|
+
|
|
750
|
+
<div className="flex justify-between pt-4">
|
|
911
751
|
<button
|
|
912
752
|
onClick={handleBack}
|
|
913
753
|
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"
|
|
914
754
|
>
|
|
915
|
-
|
|
755
|
+
Cancel
|
|
916
756
|
</button>
|
|
917
757
|
<button
|
|
918
758
|
onClick={handleFinalGenerate}
|
|
919
759
|
disabled={
|
|
920
760
|
copyMode === 'prompt'
|
|
921
|
-
? !promptValue.trim()
|
|
761
|
+
? !promptValue.trim() && !overallPrompt.trim()
|
|
922
762
|
: copyMode === 'raw'
|
|
923
|
-
? !copyValue.trim()
|
|
763
|
+
? !copyValue.trim() && (!col1Copy.trim() || !col2Copy.trim())
|
|
924
764
|
: false
|
|
925
765
|
}
|
|
926
|
-
className="rounded-md bg-cyan-600 px-
|
|
766
|
+
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"
|
|
927
767
|
>
|
|
928
768
|
✨ Generate Pane
|
|
929
769
|
</button>
|
|
930
770
|
</div>
|
|
771
|
+
|
|
772
|
+
{initialChoice === 'ai' && !isSandboxMode && (
|
|
773
|
+
<div className="text-center text-sm text-gray-600">
|
|
774
|
+
<button
|
|
775
|
+
onClick={() => setStep('directInject')}
|
|
776
|
+
className="font-bold text-cyan-700 underline hover:text-cyan-900 focus:outline-none"
|
|
777
|
+
>
|
|
778
|
+
Direct Inject
|
|
779
|
+
</button>
|
|
780
|
+
</div>
|
|
781
|
+
)}
|
|
931
782
|
</div>
|
|
932
783
|
);
|
|
933
784
|
};
|
|
@@ -946,40 +797,6 @@ const AddPaneNewPanel = ({
|
|
|
946
797
|
</div>
|
|
947
798
|
);
|
|
948
799
|
|
|
949
|
-
const renderAiDesignStep = () => (
|
|
950
|
-
<div className="space-y-4 p-4">
|
|
951
|
-
<AiDesignStep
|
|
952
|
-
designConfig={aiDesignConfig}
|
|
953
|
-
onDesignConfigChange={setAiDesignConfig}
|
|
954
|
-
/>
|
|
955
|
-
<div className="flex justify-between">
|
|
956
|
-
<button
|
|
957
|
-
onClick={handleBack}
|
|
958
|
-
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"
|
|
959
|
-
>
|
|
960
|
-
← Back
|
|
961
|
-
</button>
|
|
962
|
-
<button
|
|
963
|
-
onClick={handleAiDesignContinue}
|
|
964
|
-
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700"
|
|
965
|
-
>
|
|
966
|
-
Continue →
|
|
967
|
-
</button>
|
|
968
|
-
</div>
|
|
969
|
-
{initialChoice === `ai` && !isSandboxMode && (
|
|
970
|
-
<div className="mt-6 text-center text-sm text-gray-600">
|
|
971
|
-
ADVANCED:{' '}
|
|
972
|
-
<button
|
|
973
|
-
onClick={() => setStep('directInject')}
|
|
974
|
-
className="font-bold text-cyan-700 underline hover:text-cyan-900 focus:outline-none"
|
|
975
|
-
>
|
|
976
|
-
Direct Inject
|
|
977
|
-
</button>
|
|
978
|
-
</div>
|
|
979
|
-
)}
|
|
980
|
-
</div>
|
|
981
|
-
);
|
|
982
|
-
|
|
983
800
|
const renderDirectInjectStep = () => (
|
|
984
801
|
<DirectInjectStep
|
|
985
802
|
onBack={handleBack}
|
|
@@ -1013,14 +830,10 @@ const AddPaneNewPanel = ({
|
|
|
1013
830
|
switch (step) {
|
|
1014
831
|
case 'initial':
|
|
1015
832
|
return renderInitialStep();
|
|
1016
|
-
case '
|
|
1017
|
-
return
|
|
1018
|
-
case 'copyInput':
|
|
1019
|
-
return renderContentStep();
|
|
833
|
+
case 'dashboard':
|
|
834
|
+
return renderDashboard();
|
|
1020
835
|
case 'designLibrary':
|
|
1021
836
|
return renderDesignLibraryStep();
|
|
1022
|
-
case 'aiDesign':
|
|
1023
|
-
return renderAiDesignStep();
|
|
1024
837
|
case 'loading':
|
|
1025
838
|
return renderLoading();
|
|
1026
839
|
case 'directInject':
|
|
@@ -211,7 +211,8 @@ export const RestylePaneModal = () => {
|
|
|
211
211
|
(entry: DesignLibraryEntry) =>
|
|
212
212
|
(selectedCategory === 'all' || entry.category === selectedCategory) &&
|
|
213
213
|
entry.title.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
|
214
|
-
entry.markdownCount === targetMarkdownCount
|
|
214
|
+
entry.markdownCount === targetMarkdownCount &&
|
|
215
|
+
!entry.locked
|
|
215
216
|
);
|
|
216
217
|
}, [designLibrary, selectedCategory, searchTerm, targetMarkdownCount]);
|
|
217
218
|
|
|
@@ -350,14 +351,15 @@ export const RestylePaneModal = () => {
|
|
|
350
351
|
<Dialog.Root
|
|
351
352
|
open={isRestyleModalOpen}
|
|
352
353
|
onOpenChange={handleDialogStateChange}
|
|
353
|
-
modal={
|
|
354
|
+
modal={true}
|
|
355
|
+
preventScroll={true}
|
|
354
356
|
>
|
|
355
|
-
<Dialog.Backdrop className="z-103 fixed inset-0 bg-black
|
|
357
|
+
<Dialog.Backdrop className="z-103 fixed inset-0 bg-black bg-opacity-75" />
|
|
356
358
|
<Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
|
|
357
359
|
<Dialog.Content
|
|
358
360
|
ref={contentRef}
|
|
359
|
-
className="flex flex-col rounded-lg bg-white shadow-2xl"
|
|
360
|
-
style={{ maxHeight: '
|
|
361
|
+
className="flex max-w-5xl flex-col rounded-lg bg-white shadow-2xl xl:max-w-7xl"
|
|
362
|
+
style={{ maxHeight: '90vh', width: '90vw' }}
|
|
361
363
|
>
|
|
362
364
|
<header className="flex items-center justify-between border-b p-4">
|
|
363
365
|
<Dialog.Title className="text-xl font-bold">
|
|
@@ -33,10 +33,7 @@ export const AiDesignStep = ({
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<div className="space-y-6
|
|
37
|
-
<label className="block text-lg font-bold text-gray-800">
|
|
38
|
-
2. Configure AI Design
|
|
39
|
-
</label>
|
|
36
|
+
<div className="space-y-6">
|
|
40
37
|
<div>
|
|
41
38
|
<label className="block text-base font-bold text-gray-800">
|
|
42
39
|
Color Harmony
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
1
|
import BooleanToggle from '@/components/form/BooleanToggle';
|
|
3
2
|
import EnumSelect from '@/components/form/EnumSelect';
|
|
4
3
|
|
|
@@ -10,13 +9,32 @@ interface PromptOption {
|
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
interface CopyInputStepProps {
|
|
12
|
+
layoutChoice: 'standard' | 'grid';
|
|
13
13
|
copyMode: CopyMode;
|
|
14
14
|
onCopyModeChange: (mode: CopyMode) => void;
|
|
15
|
+
|
|
16
|
+
topic: string;
|
|
17
|
+
onTopicChange: (value: string) => void;
|
|
18
|
+
|
|
19
|
+
showAdvancedPrompts: boolean;
|
|
20
|
+
onShowAdvancedPromptsChange: (value: boolean) => void;
|
|
21
|
+
|
|
15
22
|
promptValue: string;
|
|
16
23
|
onPromptValueChange: (value: string) => void;
|
|
17
24
|
copyValue: string;
|
|
18
25
|
onCopyValueChange: (value: string) => void;
|
|
19
|
-
|
|
26
|
+
|
|
27
|
+
overallPrompt: string;
|
|
28
|
+
onOverallPromptChange: (value: string) => void;
|
|
29
|
+
promptValueCol1: string;
|
|
30
|
+
onPromptValueCol1Change: (value: string) => void;
|
|
31
|
+
promptValueCol2: string;
|
|
32
|
+
onPromptValueCol2Change: (value: string) => void;
|
|
33
|
+
col1Copy: string;
|
|
34
|
+
onCol1CopyChange: (value: string) => void;
|
|
35
|
+
col2Copy: string;
|
|
36
|
+
onCol2CopyChange: (value: string) => void;
|
|
37
|
+
|
|
20
38
|
hasRetainedContent?: boolean;
|
|
21
39
|
promptOptions: PromptOption[];
|
|
22
40
|
selectedPromptId: string;
|
|
@@ -27,13 +45,27 @@ interface CopyInputStepProps {
|
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
export const CopyInputStep = ({
|
|
48
|
+
layoutChoice,
|
|
30
49
|
copyMode,
|
|
31
50
|
onCopyModeChange,
|
|
51
|
+
topic,
|
|
52
|
+
onTopicChange,
|
|
53
|
+
showAdvancedPrompts,
|
|
54
|
+
onShowAdvancedPromptsChange,
|
|
32
55
|
promptValue,
|
|
33
56
|
onPromptValueChange,
|
|
34
57
|
copyValue,
|
|
35
58
|
onCopyValueChange,
|
|
36
|
-
|
|
59
|
+
overallPrompt,
|
|
60
|
+
onOverallPromptChange,
|
|
61
|
+
promptValueCol1,
|
|
62
|
+
onPromptValueCol1Change,
|
|
63
|
+
promptValueCol2,
|
|
64
|
+
onPromptValueCol2Change,
|
|
65
|
+
col1Copy,
|
|
66
|
+
onCol1CopyChange,
|
|
67
|
+
col2Copy,
|
|
68
|
+
onCol2CopyChange,
|
|
37
69
|
hasRetainedContent = false,
|
|
38
70
|
promptOptions,
|
|
39
71
|
selectedPromptId,
|
|
@@ -42,77 +74,182 @@ export const CopyInputStep = ({
|
|
|
42
74
|
onIsAiStylingChange,
|
|
43
75
|
showStyleToggle = true,
|
|
44
76
|
}: CopyInputStepProps) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
const renderModeSelection = () => (
|
|
78
|
+
<div className="my-2 flex flex-wrap gap-4">
|
|
79
|
+
<div className="flex items-center space-x-2">
|
|
80
|
+
<input
|
|
81
|
+
type="radio"
|
|
82
|
+
id="copy-prompt-mode"
|
|
83
|
+
name="copyModeOptions"
|
|
84
|
+
value="prompt"
|
|
85
|
+
checked={copyMode === 'prompt'}
|
|
86
|
+
onChange={(e) => onCopyModeChange(e.target.value as CopyMode)}
|
|
87
|
+
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
88
|
+
/>
|
|
89
|
+
<label
|
|
90
|
+
htmlFor="copy-prompt-mode"
|
|
91
|
+
className="text-sm font-bold text-gray-700"
|
|
92
|
+
>
|
|
93
|
+
Write a prompt
|
|
94
|
+
</label>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="flex items-center space-x-2">
|
|
97
|
+
<input
|
|
98
|
+
type="radio"
|
|
99
|
+
id="copy-raw-mode"
|
|
100
|
+
name="copyModeOptions"
|
|
101
|
+
value="raw"
|
|
102
|
+
checked={copyMode === 'raw'}
|
|
103
|
+
onChange={(e) => onCopyModeChange(e.target.value as CopyMode)}
|
|
104
|
+
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
105
|
+
/>
|
|
106
|
+
<label
|
|
107
|
+
htmlFor="copy-raw-mode"
|
|
108
|
+
className="text-sm font-bold text-gray-700"
|
|
109
|
+
>
|
|
110
|
+
Provide Copy (Markdown)
|
|
111
|
+
</label>
|
|
112
|
+
</div>
|
|
113
|
+
{hasRetainedContent && (
|
|
57
114
|
<div className="flex items-center space-x-2">
|
|
58
115
|
<input
|
|
59
116
|
type="radio"
|
|
60
|
-
id="copy-
|
|
117
|
+
id="copy-original-mode"
|
|
61
118
|
name="copyModeOptions"
|
|
62
|
-
value="
|
|
63
|
-
checked={copyMode === '
|
|
119
|
+
value="original"
|
|
120
|
+
checked={copyMode === 'original'}
|
|
64
121
|
onChange={(e) => onCopyModeChange(e.target.value as CopyMode)}
|
|
65
122
|
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
66
123
|
/>
|
|
67
124
|
<label
|
|
68
|
-
htmlFor="copy-
|
|
125
|
+
htmlFor="copy-original-mode"
|
|
69
126
|
className="text-sm font-bold text-gray-700"
|
|
70
127
|
>
|
|
71
|
-
|
|
128
|
+
Use Original
|
|
72
129
|
</label>
|
|
73
130
|
</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const renderPromptMode = () => (
|
|
136
|
+
<>
|
|
137
|
+
<div className="mb-4">
|
|
138
|
+
<EnumSelect
|
|
139
|
+
label="Section Type"
|
|
140
|
+
value={selectedPromptId}
|
|
141
|
+
onChange={onSelectedPromptIdChange}
|
|
142
|
+
options={promptOptions}
|
|
143
|
+
placeholder="Select a type..."
|
|
144
|
+
className="w-full"
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div className="mb-4">
|
|
149
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
150
|
+
Topic / Context
|
|
151
|
+
</label>
|
|
152
|
+
<textarea
|
|
153
|
+
value={topic}
|
|
154
|
+
onChange={(e) => onTopicChange(e.target.value)}
|
|
155
|
+
placeholder="e.g. a SaaS product for team collaboration"
|
|
156
|
+
rows={2}
|
|
157
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<div className="mb-4 flex items-center">
|
|
162
|
+
<BooleanToggle
|
|
163
|
+
label="Advanced: Edit Full Prompts"
|
|
164
|
+
value={showAdvancedPrompts}
|
|
165
|
+
onChange={onShowAdvancedPromptsChange}
|
|
166
|
+
size="sm"
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{showAdvancedPrompts && (
|
|
171
|
+
<div className="mt-4 space-y-4 rounded-md border border-gray-200 bg-white p-4">
|
|
172
|
+
{layoutChoice === 'standard' ? (
|
|
173
|
+
<div>
|
|
174
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
175
|
+
Full Prompt
|
|
176
|
+
</label>
|
|
177
|
+
<textarea
|
|
178
|
+
value={promptValue}
|
|
179
|
+
onChange={(e) => onPromptValueChange(e.target.value)}
|
|
180
|
+
rows={4}
|
|
181
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
182
|
+
/>
|
|
183
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
184
|
+
Leave [topic] as it will be replaced with your prompt.
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
) : (
|
|
188
|
+
<>
|
|
189
|
+
<div>
|
|
190
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
191
|
+
Overall Component Brief
|
|
192
|
+
</label>
|
|
193
|
+
<textarea
|
|
194
|
+
value={overallPrompt}
|
|
195
|
+
onChange={(e) => onOverallPromptChange(e.target.value)}
|
|
196
|
+
rows={3}
|
|
197
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
201
|
+
<div>
|
|
202
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
203
|
+
Left Column Prompt
|
|
204
|
+
</label>
|
|
205
|
+
<textarea
|
|
206
|
+
value={promptValueCol1}
|
|
207
|
+
onChange={(e) => onPromptValueCol1Change(e.target.value)}
|
|
208
|
+
rows={4}
|
|
209
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
<div>
|
|
213
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
214
|
+
Right Column Prompt
|
|
215
|
+
</label>
|
|
216
|
+
<textarea
|
|
217
|
+
value={promptValueCol2}
|
|
218
|
+
onChange={(e) => onPromptValueCol2Change(e.target.value)}
|
|
219
|
+
rows={4}
|
|
220
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
221
|
+
/>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</>
|
|
225
|
+
)}
|
|
90
226
|
</div>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
227
|
+
)}
|
|
228
|
+
</>
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const renderRawMode = () => (
|
|
232
|
+
<>
|
|
233
|
+
<div className="mb-2 flex items-center justify-between">
|
|
234
|
+
<p className="text-sm text-gray-500">
|
|
235
|
+
Provide your raw copy here. Use Markdown.
|
|
236
|
+
</p>
|
|
237
|
+
{showStyleToggle && (
|
|
238
|
+
<div className="flex items-center">
|
|
239
|
+
<BooleanToggle
|
|
240
|
+
label="Style with AI"
|
|
241
|
+
value={isAiStyling}
|
|
242
|
+
onChange={onIsAiStylingChange}
|
|
243
|
+
size="sm"
|
|
101
244
|
/>
|
|
102
|
-
<label
|
|
103
|
-
htmlFor="copy-original-mode"
|
|
104
|
-
className="text-sm font-bold text-gray-700"
|
|
105
|
-
>
|
|
106
|
-
Use Original
|
|
107
|
-
</label>
|
|
108
245
|
</div>
|
|
109
246
|
)}
|
|
110
247
|
</div>
|
|
111
248
|
|
|
112
|
-
{
|
|
249
|
+
{isAiStyling && (
|
|
113
250
|
<div className="mb-4">
|
|
114
251
|
<EnumSelect
|
|
115
|
-
label="Section Type"
|
|
252
|
+
label="Section Type (for Styling)"
|
|
116
253
|
value={selectedPromptId}
|
|
117
254
|
onChange={onSelectedPromptIdChange}
|
|
118
255
|
options={promptOptions}
|
|
@@ -122,49 +259,54 @@ export const CopyInputStep = ({
|
|
|
122
259
|
</div>
|
|
123
260
|
)}
|
|
124
261
|
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</p>
|
|
147
|
-
{showStyleToggle && (
|
|
148
|
-
<div className="flex items-center">
|
|
149
|
-
<BooleanToggle
|
|
150
|
-
label="Style with AI"
|
|
151
|
-
value={isAiStyling}
|
|
152
|
-
onChange={onIsAiStylingChange}
|
|
153
|
-
size="sm"
|
|
154
|
-
/>
|
|
155
|
-
</div>
|
|
156
|
-
)}
|
|
262
|
+
{layoutChoice === 'standard' ? (
|
|
263
|
+
<textarea
|
|
264
|
+
id="raw-copy"
|
|
265
|
+
value={copyValue}
|
|
266
|
+
onChange={(e) => onCopyValueChange(e.target.value)}
|
|
267
|
+
placeholder="## My Awesome Headline..."
|
|
268
|
+
rows={6}
|
|
269
|
+
className="block w-full rounded-md border-gray-300 p-2 font-mono text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
270
|
+
/>
|
|
271
|
+
) : (
|
|
272
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
273
|
+
<div>
|
|
274
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
275
|
+
Left Column Markdown
|
|
276
|
+
</label>
|
|
277
|
+
<textarea
|
|
278
|
+
value={col1Copy}
|
|
279
|
+
onChange={(e) => onCol1CopyChange(e.target.value)}
|
|
280
|
+
rows={8}
|
|
281
|
+
className="block w-full rounded-md border-gray-300 p-2 font-mono text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
282
|
+
/>
|
|
157
283
|
</div>
|
|
158
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
284
|
+
<div>
|
|
285
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
286
|
+
Right Column Markdown
|
|
287
|
+
</label>
|
|
288
|
+
<textarea
|
|
289
|
+
value={col2Copy}
|
|
290
|
+
onChange={(e) => onCol2CopyChange(e.target.value)}
|
|
291
|
+
rows={8}
|
|
292
|
+
className="block w-full rounded-md border-gray-300 p-2 font-mono text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
167
296
|
)}
|
|
297
|
+
</>
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<>
|
|
302
|
+
<label className="block text-lg font-bold text-gray-800">
|
|
303
|
+
Content Configuration
|
|
304
|
+
</label>
|
|
305
|
+
|
|
306
|
+
{renderModeSelection()}
|
|
307
|
+
|
|
308
|
+
{copyMode === 'prompt' && renderPromptMode()}
|
|
309
|
+
{copyMode === 'raw' && renderRawMode()}
|
|
168
310
|
|
|
169
311
|
{copyMode === 'original' && (
|
|
170
312
|
<div className="rounded-md border border-blue-200 bg-blue-50 p-4 text-blue-700">
|
|
@@ -173,6 +315,6 @@ export const CopyInputStep = ({
|
|
|
173
315
|
</p>
|
|
174
316
|
</div>
|
|
175
317
|
)}
|
|
176
|
-
|
|
318
|
+
</>
|
|
177
319
|
);
|
|
178
320
|
};
|