astro-tractstack 2.0.28 → 2.0.30
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/codehooks/SandboxAuthWrapper.tsx +30 -4
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +323 -77
- package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +82 -9
- package/templates/src/components/edit/pane/steps/DirectInjectStep.tsx +41 -21
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +68 -34
- package/templates/src/constants/prompts.json +65 -36
- package/templates/src/constants.ts +3 -3
- package/templates/src/pages/sandbox.astro +62 -54
- package/templates/src/stores/nodes.ts +17 -4
- package/templates/src/types/compositorTypes.ts +3 -0
- package/templates/src/types/tractstack.ts +2 -0
- package/templates/src/utils/compositor/designLibraryHelper.ts +8 -3
- package/templates/src/utils/compositor/typeGuards.ts +0 -18
package/package.json
CHANGED
|
@@ -5,22 +5,48 @@ import XMarkIcon from '@heroicons/react/24/solid/XMarkIcon';
|
|
|
5
5
|
import { ProfileStorage } from '@/utils/profileStorage';
|
|
6
6
|
import SandboxRegisterForm from '@/components/codehooks/SandboxRegisterForm';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface SandboxAuthWrapperProps {
|
|
9
|
+
isServerSideAuthenticated: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function SandboxAuthWrapper({
|
|
13
|
+
isServerSideAuthenticated,
|
|
14
|
+
}: SandboxAuthWrapperProps) {
|
|
9
15
|
const [profileExists, setProfileExists] = useState<boolean | null>(null);
|
|
10
16
|
|
|
11
17
|
useEffect(() => {
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
const hasLocalProfile = ProfileStorage.hasProfile();
|
|
19
|
+
|
|
20
|
+
if (hasLocalProfile && !isServerSideAuthenticated) {
|
|
21
|
+
const token = localStorage.getItem('tractstack_profile_token');
|
|
22
|
+
|
|
23
|
+
if (token) {
|
|
24
|
+
ProfileStorage.storeProfileToken(token);
|
|
25
|
+
window.location.reload();
|
|
26
|
+
return;
|
|
27
|
+
} else {
|
|
28
|
+
ProfileStorage.clearProfile();
|
|
29
|
+
setProfileExists(false);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
setProfileExists(hasLocalProfile);
|
|
33
|
+
}
|
|
34
|
+
}, [isServerSideAuthenticated]);
|
|
14
35
|
|
|
15
36
|
const handleRegistrationSuccess = () => {
|
|
16
37
|
setProfileExists(true);
|
|
38
|
+
window.location.reload();
|
|
17
39
|
};
|
|
18
40
|
|
|
19
41
|
const handleClose = () => {
|
|
20
42
|
window.location.href = '/';
|
|
21
43
|
};
|
|
22
44
|
|
|
23
|
-
if (profileExists ===
|
|
45
|
+
if (profileExists === true && isServerSideAuthenticated) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (profileExists === null) {
|
|
24
50
|
return null;
|
|
25
51
|
}
|
|
26
52
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useState, useCallback, useMemo, useEffect } 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';
|
|
@@ -12,7 +12,7 @@ import prompts from '@/constants/prompts.json';
|
|
|
12
12
|
import type { DesignLibraryEntry } from '@/types/tractstack';
|
|
13
13
|
import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
|
|
14
14
|
import { useStore } from '@nanostores/react';
|
|
15
|
-
import { CopyInputStep } from './steps/CopyInputStep';
|
|
15
|
+
import { CopyInputStep, type CopyMode } from './steps/CopyInputStep';
|
|
16
16
|
import { DesignLibraryStep } from './steps/DesignLibraryStep';
|
|
17
17
|
import { AiDesignStep, type AiDesignConfig } from './steps/AiDesignStep';
|
|
18
18
|
import { parseAiPane, parseAiCopyHtml } from '@/utils/compositor/aiPaneParser';
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
convertTemplateToAIShell,
|
|
22
22
|
} from '@/utils/compositor/designLibraryHelper';
|
|
23
23
|
import { DirectInjectStep } from './steps/DirectInjectStep';
|
|
24
|
+
import BooleanToggle from '@/components/form/BooleanToggle';
|
|
25
|
+
import EnumSelect from '@/components/form/EnumSelect';
|
|
24
26
|
|
|
25
27
|
type Step =
|
|
26
28
|
| 'initial'
|
|
@@ -33,7 +35,6 @@ type Step =
|
|
|
33
35
|
| 'directInject';
|
|
34
36
|
|
|
35
37
|
type InitialChoice = 'library' | 'ai' | 'blank';
|
|
36
|
-
type CopyMode = 'prompt' | 'raw';
|
|
37
38
|
type LayoutChoice = 'standard' | 'grid';
|
|
38
39
|
type ColumnPresetKey = 'left' | 'right';
|
|
39
40
|
|
|
@@ -140,12 +141,13 @@ const AddPaneNewPanel = ({
|
|
|
140
141
|
const [layoutChoice, setLayoutChoice] = useState<LayoutChoice>('standard');
|
|
141
142
|
const [error, setError] = useState<string | null>(null);
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
const [selectedPromptId, setSelectedPromptId] = useState<string>('');
|
|
145
|
+
const [isAiStyling, setIsAiStyling] = useState(false);
|
|
146
|
+
|
|
144
147
|
const [copyMode, setCopyMode] = useState<CopyMode>('prompt');
|
|
145
148
|
const [promptValue, setPromptValue] = useState('');
|
|
146
149
|
const [copyValue, setCopyValue] = useState('');
|
|
147
150
|
|
|
148
|
-
// Grid / 2-Column State (Strictly Prompt-Only)
|
|
149
151
|
const [overallPrompt, setOverallPrompt] = useState(
|
|
150
152
|
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.default
|
|
151
153
|
);
|
|
@@ -155,6 +157,8 @@ const AddPaneNewPanel = ({
|
|
|
155
157
|
const [promptValueCol2, setPromptValueCol2] = useState(
|
|
156
158
|
prompts.aiPaneCopyPrompt_2cols.presets.heroDefault.right.prompt
|
|
157
159
|
);
|
|
160
|
+
const [col1Copy, setCol1Copy] = useState('');
|
|
161
|
+
const [col2Copy, setCol2Copy] = useState('');
|
|
158
162
|
|
|
159
163
|
const [selectedLibraryEntry, setSelectedLibraryEntry] =
|
|
160
164
|
useState<DesignLibraryEntry | null>(null);
|
|
@@ -166,6 +170,52 @@ const AddPaneNewPanel = ({
|
|
|
166
170
|
additionalNotes: '',
|
|
167
171
|
});
|
|
168
172
|
|
|
173
|
+
const promptOptions = useMemo(() => {
|
|
174
|
+
return prompts.aiPromptsIndex
|
|
175
|
+
.filter((p) => p.layout === layoutChoice)
|
|
176
|
+
.map((p) => ({ label: p.label, value: p.id }));
|
|
177
|
+
}, [layoutChoice]);
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
if (promptOptions.length > 0) {
|
|
181
|
+
const currentValid = promptOptions.find(
|
|
182
|
+
(p) => p.value === selectedPromptId
|
|
183
|
+
);
|
|
184
|
+
if (!currentValid) {
|
|
185
|
+
setSelectedPromptId(promptOptions[0].value);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, [promptOptions, selectedPromptId]);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
if (!selectedPromptId) return;
|
|
192
|
+
|
|
193
|
+
const activeConfig = prompts.aiPromptsIndex.find(
|
|
194
|
+
(p) => p.id === selectedPromptId
|
|
195
|
+
);
|
|
196
|
+
if (!activeConfig) return;
|
|
197
|
+
|
|
198
|
+
const promptKey = activeConfig.prompts.copy;
|
|
199
|
+
const copyPromptGroup = (prompts as any)[promptKey];
|
|
200
|
+
if (!copyPromptGroup) return;
|
|
201
|
+
|
|
202
|
+
const variant = activeConfig.variants
|
|
203
|
+
? activeConfig.variants[0]
|
|
204
|
+
: 'default';
|
|
205
|
+
|
|
206
|
+
if (layoutChoice === 'standard') {
|
|
207
|
+
const newText = copyPromptGroup[variant] || '';
|
|
208
|
+
setPromptValue(newText);
|
|
209
|
+
} else if (layoutChoice === 'grid') {
|
|
210
|
+
const preset = copyPromptGroup.presets?.[variant];
|
|
211
|
+
if (preset) {
|
|
212
|
+
setOverallPrompt(preset.default || '');
|
|
213
|
+
setPromptValueCol1(preset.left?.prompt || '');
|
|
214
|
+
setPromptValueCol2(preset.right?.prompt || '');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}, [selectedPromptId, layoutChoice]);
|
|
218
|
+
|
|
169
219
|
const handleInitialChoice = (choice: InitialChoice) => {
|
|
170
220
|
setInitialChoice(choice);
|
|
171
221
|
setError(null);
|
|
@@ -279,6 +329,18 @@ const AddPaneNewPanel = ({
|
|
|
279
329
|
setLayoutChoice('standard');
|
|
280
330
|
}
|
|
281
331
|
|
|
332
|
+
if (entry.locked) {
|
|
333
|
+
const liveTemplate = convertStorageToLiveTemplate(entry.template);
|
|
334
|
+
handleApplyTemplate(liveTemplate);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (entry.retain) {
|
|
339
|
+
setCopyMode('original');
|
|
340
|
+
} else {
|
|
341
|
+
setCopyMode('prompt');
|
|
342
|
+
}
|
|
343
|
+
|
|
282
344
|
setStep('copyInput');
|
|
283
345
|
};
|
|
284
346
|
|
|
@@ -295,27 +357,60 @@ const AddPaneNewPanel = ({
|
|
|
295
357
|
const liveTemplate = convertStorageToLiveTemplate(
|
|
296
358
|
selectedLibraryEntry.template
|
|
297
359
|
);
|
|
360
|
+
|
|
361
|
+
if (copyMode === 'original') {
|
|
362
|
+
handleApplyTemplate(liveTemplate);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
298
366
|
const shellResult = convertTemplateToAIShell(liveTemplate);
|
|
299
367
|
const layout = 'Text Only';
|
|
300
368
|
|
|
301
369
|
if (layoutChoice === 'grid' && liveTemplate.gridLayout) {
|
|
370
|
+
if (copyMode === 'raw' && isAiStyling) {
|
|
371
|
+
const activeConfig =
|
|
372
|
+
prompts.aiPromptsIndex.find((p) => p.id === selectedPromptId) ||
|
|
373
|
+
prompts.aiPromptsIndex.find((p) => p.layout === 'grid') ||
|
|
374
|
+
prompts.aiPromptsIndex[0];
|
|
375
|
+
|
|
376
|
+
const stylePromptKey = activeConfig.prompts.style;
|
|
377
|
+
const stylePromptDetails = (prompts as any)[stylePromptKey];
|
|
378
|
+
|
|
379
|
+
const copyResults: string[] = [];
|
|
380
|
+
const rawContents = [col1Copy, col2Copy];
|
|
381
|
+
|
|
382
|
+
for (const rawContent of rawContents) {
|
|
383
|
+
const formattedStylePrompt = stylePromptDetails.user_template
|
|
384
|
+
.replace('{{SHELL_JSON}}', shellResult)
|
|
385
|
+
.replace('{{COPY_INPUT}}', rawContent);
|
|
386
|
+
|
|
387
|
+
const styledResult = await callAskLemurAPI(
|
|
388
|
+
formattedStylePrompt,
|
|
389
|
+
stylePromptDetails.system || '',
|
|
390
|
+
false,
|
|
391
|
+
isSandboxMode
|
|
392
|
+
);
|
|
393
|
+
copyResults.push(styledResult);
|
|
394
|
+
}
|
|
395
|
+
const finalPane = parseAiPane(shellResult, copyResults, layout);
|
|
396
|
+
handleApplyTemplate(finalPane);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (copyMode === 'raw') {
|
|
401
|
+
const nodes = liveTemplate.gridLayout.nodes;
|
|
402
|
+
if (nodes && nodes[0]) nodes[0].markdownBody = col1Copy;
|
|
403
|
+
if (nodes && nodes[1]) nodes[1].markdownBody = col2Copy;
|
|
404
|
+
handleApplyTemplate(liveTemplate);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
302
408
|
const copyPromptDetails = prompts.aiPaneCopyPrompt_2cols;
|
|
303
409
|
const preset = copyPromptDetails.presets.heroDefault;
|
|
304
410
|
const copyResults: string[] = [];
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
prompt: string;
|
|
309
|
-
presetKey: ColumnPresetKey;
|
|
310
|
-
}[] = [
|
|
311
|
-
{
|
|
312
|
-
prompt: promptValueCol1,
|
|
313
|
-
presetKey: 'left',
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
prompt: promptValueCol2,
|
|
317
|
-
presetKey: 'right',
|
|
318
|
-
},
|
|
411
|
+
const promptsToRun = [
|
|
412
|
+
{ prompt: promptValueCol1, presetKey: 'left' as ColumnPresetKey },
|
|
413
|
+
{ prompt: promptValueCol2, presetKey: 'right' as ColumnPresetKey },
|
|
319
414
|
];
|
|
320
415
|
|
|
321
416
|
for (const item of promptsToRun) {
|
|
@@ -339,16 +434,36 @@ const AddPaneNewPanel = ({
|
|
|
339
434
|
);
|
|
340
435
|
copyResults.push(copyResult);
|
|
341
436
|
}
|
|
342
|
-
|
|
343
437
|
const finalPane = parseAiPane(shellResult, copyResults, layout);
|
|
344
438
|
handleApplyTemplate(finalPane);
|
|
345
439
|
} else if (layoutChoice === 'standard' && liveTemplate.markdown) {
|
|
440
|
+
if (copyMode === 'raw' && isAiStyling) {
|
|
441
|
+
const activeConfig =
|
|
442
|
+
prompts.aiPromptsIndex.find((p) => p.id === selectedPromptId) ||
|
|
443
|
+
prompts.aiPromptsIndex[0];
|
|
444
|
+
const stylePromptKey = activeConfig.prompts.style;
|
|
445
|
+
const stylePromptDetails = (prompts as any)[stylePromptKey];
|
|
446
|
+
|
|
447
|
+
const formattedStylePrompt = stylePromptDetails.user_template
|
|
448
|
+
.replace('{{SHELL_JSON}}', shellResult)
|
|
449
|
+
.replace('{{COPY_INPUT}}', copyValue);
|
|
450
|
+
|
|
451
|
+
const styledResult = await callAskLemurAPI(
|
|
452
|
+
formattedStylePrompt,
|
|
453
|
+
stylePromptDetails.system || '',
|
|
454
|
+
false,
|
|
455
|
+
isSandboxMode
|
|
456
|
+
);
|
|
457
|
+
const finalPane = parseAiPane(shellResult, styledResult, layout);
|
|
458
|
+
handleApplyTemplate(finalPane);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
346
462
|
if (copyMode === 'raw') {
|
|
347
463
|
liveTemplate.markdown.markdownBody = copyValue;
|
|
348
464
|
handleApplyTemplate(liveTemplate);
|
|
349
465
|
return;
|
|
350
466
|
}
|
|
351
|
-
|
|
352
467
|
if (copyMode === 'prompt') {
|
|
353
468
|
if (!shellResult || shellResult === '{}') {
|
|
354
469
|
throw new Error(
|
|
@@ -387,6 +502,11 @@ const AddPaneNewPanel = ({
|
|
|
387
502
|
);
|
|
388
503
|
}
|
|
389
504
|
} else if (initialChoice === 'ai') {
|
|
505
|
+
const activeConfig = prompts.aiPromptsIndex.find(
|
|
506
|
+
(p) => p.id === selectedPromptId
|
|
507
|
+
);
|
|
508
|
+
if (!activeConfig) throw new Error('Selected prompt type not found.');
|
|
509
|
+
|
|
390
510
|
let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
|
|
391
511
|
if (aiDesignConfig.baseColor)
|
|
392
512
|
designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
|
|
@@ -396,9 +516,11 @@ const AddPaneNewPanel = ({
|
|
|
396
516
|
designInput += ` Refine with these notes: "${aiDesignConfig.additionalNotes}"`;
|
|
397
517
|
|
|
398
518
|
const layout = 'Text Only';
|
|
519
|
+
const promptMap = prompts as any;
|
|
399
520
|
|
|
400
521
|
if (layoutChoice === 'standard') {
|
|
401
|
-
const
|
|
522
|
+
const shellPromptKey = activeConfig.prompts.shell;
|
|
523
|
+
const shellPromptDetails = promptMap[shellPromptKey];
|
|
402
524
|
const formattedShellPrompt = shellPromptDetails.user_template
|
|
403
525
|
.replace('{{DESIGN_INPUT}}', designInput)
|
|
404
526
|
.replace('{{LAYOUT_TYPE}}', layout);
|
|
@@ -410,7 +532,8 @@ const AddPaneNewPanel = ({
|
|
|
410
532
|
isSandboxMode
|
|
411
533
|
);
|
|
412
534
|
|
|
413
|
-
const
|
|
535
|
+
const copyPromptKey = activeConfig.prompts.copy;
|
|
536
|
+
const copyPromptDetails = promptMap[copyPromptKey];
|
|
414
537
|
const copyInputContent =
|
|
415
538
|
copyMode === 'prompt' ? promptValue : copyValue;
|
|
416
539
|
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
@@ -428,7 +551,8 @@ const AddPaneNewPanel = ({
|
|
|
428
551
|
const finalPane = parseAiPane(shellResult, copyResult, layout);
|
|
429
552
|
handleApplyTemplate(finalPane);
|
|
430
553
|
} else if (layoutChoice === 'grid') {
|
|
431
|
-
const
|
|
554
|
+
const shellPromptKey = activeConfig.prompts.shell;
|
|
555
|
+
const shellPromptDetails = promptMap[shellPromptKey];
|
|
432
556
|
const formattedShellPrompt = shellPromptDetails.user_template
|
|
433
557
|
.replace('{{COPY_INPUT}}', overallPrompt)
|
|
434
558
|
.replace('{{DESIGN_INPUT}}', designInput);
|
|
@@ -440,23 +564,16 @@ const AddPaneNewPanel = ({
|
|
|
440
564
|
isSandboxMode
|
|
441
565
|
);
|
|
442
566
|
|
|
443
|
-
const
|
|
444
|
-
const
|
|
567
|
+
const copyPromptKey = activeConfig.prompts.copy;
|
|
568
|
+
const copyPromptDetails = promptMap[copyPromptKey];
|
|
569
|
+
const preset =
|
|
570
|
+
copyPromptDetails.presets?.[activeConfig.variants[0]] ||
|
|
571
|
+
copyPromptDetails.presets?.heroDefault;
|
|
445
572
|
const copyResults: string[] = [];
|
|
446
573
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
prompt:
|
|
450
|
-
presetKey: ColumnPresetKey;
|
|
451
|
-
}[] = [
|
|
452
|
-
{
|
|
453
|
-
prompt: promptValueCol1,
|
|
454
|
-
presetKey: 'left',
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
prompt: promptValueCol2,
|
|
458
|
-
presetKey: 'right',
|
|
459
|
-
},
|
|
574
|
+
const promptsToRun = [
|
|
575
|
+
{ prompt: promptValueCol1, presetKey: 'left' as ColumnPresetKey },
|
|
576
|
+
{ prompt: promptValueCol2, presetKey: 'right' as ColumnPresetKey },
|
|
460
577
|
];
|
|
461
578
|
|
|
462
579
|
for (const item of promptsToRun) {
|
|
@@ -491,6 +608,8 @@ const AddPaneNewPanel = ({
|
|
|
491
608
|
copyMode,
|
|
492
609
|
promptValue,
|
|
493
610
|
copyValue,
|
|
611
|
+
col1Copy,
|
|
612
|
+
col2Copy,
|
|
494
613
|
overallPrompt,
|
|
495
614
|
promptValueCol1,
|
|
496
615
|
promptValueCol2,
|
|
@@ -499,6 +618,8 @@ const AddPaneNewPanel = ({
|
|
|
499
618
|
layoutChoice,
|
|
500
619
|
selectedLibraryEntry,
|
|
501
620
|
handleApplyTemplate,
|
|
621
|
+
selectedPromptId,
|
|
622
|
+
isAiStyling,
|
|
502
623
|
]);
|
|
503
624
|
|
|
504
625
|
const renderInitialStep = () => (
|
|
@@ -584,50 +705,160 @@ const AddPaneNewPanel = ({
|
|
|
584
705
|
const renderContentStep = () => {
|
|
585
706
|
if (layoutChoice === 'grid') {
|
|
586
707
|
const isGenerateDisabled =
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
708
|
+
copyMode === 'prompt'
|
|
709
|
+
? !overallPrompt.trim() ||
|
|
710
|
+
!promptValueCol1.trim() ||
|
|
711
|
+
!promptValueCol2.trim()
|
|
712
|
+
: copyMode === 'raw'
|
|
713
|
+
? !col1Copy.trim() || !col2Copy.trim()
|
|
714
|
+
: false;
|
|
590
715
|
|
|
591
716
|
return (
|
|
592
717
|
<div className="space-y-4 p-4">
|
|
593
|
-
<
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
This context is applied to both columns.
|
|
605
|
-
</p>
|
|
606
|
-
</div>
|
|
607
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
608
|
-
<div>
|
|
609
|
-
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
610
|
-
Left Column Prompt
|
|
611
|
-
</label>
|
|
612
|
-
<textarea
|
|
613
|
-
value={promptValueCol1}
|
|
614
|
-
onChange={(e) => setPromptValueCol1(e.target.value)}
|
|
615
|
-
rows={4}
|
|
616
|
-
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
718
|
+
<label className="block text-lg font-bold text-gray-800">
|
|
719
|
+
1. Provide Content
|
|
720
|
+
</label>
|
|
721
|
+
|
|
722
|
+
<div className="my-2 flex flex-wrap gap-4">
|
|
723
|
+
<div className="flex items-center space-x-2">
|
|
724
|
+
<input
|
|
725
|
+
type="radio"
|
|
726
|
+
checked={copyMode === 'prompt'}
|
|
727
|
+
onChange={() => setCopyMode('prompt')}
|
|
728
|
+
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
617
729
|
/>
|
|
730
|
+
<label className="text-sm font-bold text-gray-700">Prompt</label>
|
|
618
731
|
</div>
|
|
619
|
-
<div>
|
|
620
|
-
<
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
onChange={(e) => setPromptValueCol2(e.target.value)}
|
|
626
|
-
rows={4}
|
|
627
|
-
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
732
|
+
<div className="flex items-center space-x-2">
|
|
733
|
+
<input
|
|
734
|
+
type="radio"
|
|
735
|
+
checked={copyMode === 'raw'}
|
|
736
|
+
onChange={() => setCopyMode('raw')}
|
|
737
|
+
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
628
738
|
/>
|
|
739
|
+
<label className="text-sm font-bold text-gray-700">
|
|
740
|
+
Manual Markdown
|
|
741
|
+
</label>
|
|
629
742
|
</div>
|
|
743
|
+
{selectedLibraryEntry?.retain && (
|
|
744
|
+
<div className="flex items-center space-x-2">
|
|
745
|
+
<input
|
|
746
|
+
type="radio"
|
|
747
|
+
checked={copyMode === 'original'}
|
|
748
|
+
onChange={() => setCopyMode('original')}
|
|
749
|
+
className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
|
|
750
|
+
/>
|
|
751
|
+
<label className="text-sm font-bold text-gray-700">
|
|
752
|
+
Use Original
|
|
753
|
+
</label>
|
|
754
|
+
</div>
|
|
755
|
+
)}
|
|
630
756
|
</div>
|
|
757
|
+
|
|
758
|
+
{copyMode === 'raw' && initialChoice === 'library' && (
|
|
759
|
+
<div className="mb-4 flex items-center justify-between rounded-lg border border-gray-100 bg-gray-50 p-2">
|
|
760
|
+
<span className="text-sm text-gray-600">
|
|
761
|
+
Style this content with AI?
|
|
762
|
+
</span>
|
|
763
|
+
<div className="flex items-center">
|
|
764
|
+
<BooleanToggle
|
|
765
|
+
label="AI Styles"
|
|
766
|
+
value={isAiStyling}
|
|
767
|
+
onChange={setIsAiStyling}
|
|
768
|
+
size="sm"
|
|
769
|
+
/>
|
|
770
|
+
</div>
|
|
771
|
+
</div>
|
|
772
|
+
)}
|
|
773
|
+
|
|
774
|
+
{copyMode === 'prompt' && (
|
|
775
|
+
<>
|
|
776
|
+
<div className="mb-4">
|
|
777
|
+
<EnumSelect
|
|
778
|
+
label="Section Type"
|
|
779
|
+
value={selectedPromptId}
|
|
780
|
+
onChange={setSelectedPromptId}
|
|
781
|
+
options={promptOptions}
|
|
782
|
+
placeholder="Select a section type..."
|
|
783
|
+
className="w-full"
|
|
784
|
+
/>
|
|
785
|
+
</div>
|
|
786
|
+
<div>
|
|
787
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
788
|
+
Overall Component Brief
|
|
789
|
+
</label>
|
|
790
|
+
<textarea
|
|
791
|
+
value={overallPrompt}
|
|
792
|
+
onChange={(e) => setOverallPrompt(e.target.value)}
|
|
793
|
+
rows={3}
|
|
794
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
795
|
+
/>
|
|
796
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
797
|
+
This context is applied to both columns.
|
|
798
|
+
</p>
|
|
799
|
+
</div>
|
|
800
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
801
|
+
<div>
|
|
802
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
803
|
+
Left Column Prompt
|
|
804
|
+
</label>
|
|
805
|
+
<textarea
|
|
806
|
+
value={promptValueCol1}
|
|
807
|
+
onChange={(e) => setPromptValueCol1(e.target.value)}
|
|
808
|
+
rows={4}
|
|
809
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
810
|
+
/>
|
|
811
|
+
</div>
|
|
812
|
+
<div>
|
|
813
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
814
|
+
Right Column Prompt
|
|
815
|
+
</label>
|
|
816
|
+
<textarea
|
|
817
|
+
value={promptValueCol2}
|
|
818
|
+
onChange={(e) => setPromptValueCol2(e.target.value)}
|
|
819
|
+
rows={4}
|
|
820
|
+
className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
|
|
821
|
+
/>
|
|
822
|
+
</div>
|
|
823
|
+
</div>
|
|
824
|
+
</>
|
|
825
|
+
)}
|
|
826
|
+
|
|
827
|
+
{copyMode === 'raw' && (
|
|
828
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
|
829
|
+
<div>
|
|
830
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
831
|
+
Left Column Markdown
|
|
832
|
+
</label>
|
|
833
|
+
<textarea
|
|
834
|
+
value={col1Copy}
|
|
835
|
+
onChange={(e) => setCol1Copy(e.target.value)}
|
|
836
|
+
rows={8}
|
|
837
|
+
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"
|
|
838
|
+
/>
|
|
839
|
+
</div>
|
|
840
|
+
<div>
|
|
841
|
+
<label className="mb-2 block text-sm font-bold text-gray-700">
|
|
842
|
+
Right Column Markdown
|
|
843
|
+
</label>
|
|
844
|
+
<textarea
|
|
845
|
+
value={col2Copy}
|
|
846
|
+
onChange={(e) => setCol2Copy(e.target.value)}
|
|
847
|
+
rows={8}
|
|
848
|
+
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"
|
|
849
|
+
/>
|
|
850
|
+
</div>
|
|
851
|
+
</div>
|
|
852
|
+
)}
|
|
853
|
+
|
|
854
|
+
{copyMode === 'original' && (
|
|
855
|
+
<div className="rounded-md border border-blue-200 bg-blue-50 p-4 text-blue-700">
|
|
856
|
+
<p className="text-sm">
|
|
857
|
+
The original text saved with this design will be used.
|
|
858
|
+
</p>
|
|
859
|
+
</div>
|
|
860
|
+
)}
|
|
861
|
+
|
|
631
862
|
<div className="flex justify-between">
|
|
632
863
|
<button
|
|
633
864
|
onClick={handleBack}
|
|
@@ -656,11 +887,18 @@ const AddPaneNewPanel = ({
|
|
|
656
887
|
onPromptValueChange={setPromptValue}
|
|
657
888
|
copyValue={copyValue}
|
|
658
889
|
onCopyValueChange={setCopyValue}
|
|
890
|
+
hasRetainedContent={selectedLibraryEntry?.retain}
|
|
659
891
|
defaultPrompt={
|
|
660
892
|
first
|
|
661
893
|
? prompts.aiPaneCopyPrompt.heroDefault
|
|
662
894
|
: prompts.aiPaneCopyPrompt.contentDefault
|
|
663
895
|
}
|
|
896
|
+
promptOptions={promptOptions}
|
|
897
|
+
selectedPromptId={selectedPromptId}
|
|
898
|
+
onSelectedPromptIdChange={setSelectedPromptId}
|
|
899
|
+
isAiStyling={isAiStyling}
|
|
900
|
+
onIsAiStylingChange={setIsAiStyling}
|
|
901
|
+
showStyleToggle={initialChoice === 'library'}
|
|
664
902
|
/>
|
|
665
903
|
<div className="flex justify-between">
|
|
666
904
|
<button
|
|
@@ -672,7 +910,11 @@ const AddPaneNewPanel = ({
|
|
|
672
910
|
<button
|
|
673
911
|
onClick={handleFinalGenerate}
|
|
674
912
|
disabled={
|
|
675
|
-
copyMode === 'prompt'
|
|
913
|
+
copyMode === 'prompt'
|
|
914
|
+
? !promptValue.trim()
|
|
915
|
+
: copyMode === 'raw'
|
|
916
|
+
? !copyValue.trim()
|
|
917
|
+
: false
|
|
676
918
|
}
|
|
677
919
|
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"
|
|
678
920
|
>
|
|
@@ -732,7 +974,11 @@ const AddPaneNewPanel = ({
|
|
|
732
974
|
);
|
|
733
975
|
|
|
734
976
|
const renderDirectInjectStep = () => (
|
|
735
|
-
<DirectInjectStep
|
|
977
|
+
<DirectInjectStep
|
|
978
|
+
onBack={handleBack}
|
|
979
|
+
onCreatePane={handleApplyTemplate}
|
|
980
|
+
layout={layoutChoice}
|
|
981
|
+
/>
|
|
736
982
|
);
|
|
737
983
|
|
|
738
984
|
const renderLoading = () => (
|