astro-tractstack 2.0.14 → 2.0.15

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.
@@ -1,10 +1,11 @@
1
1
  import { useState, useCallback } from 'react';
2
- import { AiPanePreview } from './AiPanePreview';
3
- import type { TemplatePane } from '@/types/compositorTypes';
4
2
  import prompts from '@/constants/prompts.json';
5
3
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
4
+ import { AiPanePreview } from './AiPanePreview';
5
+ import { CopyInputStep } from './steps/CopyInputStep';
6
6
  import { parseAiPane } from '@/utils/compositor/aiPaneParser';
7
7
  import { classNames } from '@/utils/helpers';
8
+ import type { TemplatePane } from '@/types/compositorTypes';
8
9
  import type { BrandConfig } from '@/types/tractstack';
9
10
 
10
11
  interface AiPaneGeneratorProps {
@@ -300,7 +301,7 @@ export function AiPaneGenerator({
300
301
  <div>
301
302
  <label
302
303
  htmlFor="shell-json"
303
- className="block text-lg font-semibold text-gray-800"
304
+ className="block text-lg font-bold text-gray-800"
304
305
  >
305
306
  Shell JSON Payload
306
307
  </label>
@@ -317,7 +318,7 @@ export function AiPaneGenerator({
317
318
  <div>
318
319
  <label
319
320
  htmlFor="copy-html"
320
- className="block text-lg font-semibold text-gray-800"
321
+ className="block text-lg font-bold text-gray-800"
321
322
  >
322
323
  Copy HTML Payload
323
324
  </label>
@@ -361,7 +362,7 @@ export function AiPaneGenerator({
361
362
  return (
362
363
  <div className="space-y-6 p-4">
363
364
  <div>
364
- <label className="block text-lg font-semibold text-gray-800">
365
+ <label className="block text-lg font-bold text-gray-800">
365
366
  Color Harmony
366
367
  </label>
367
368
  <div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
@@ -378,7 +379,7 @@ export function AiPaneGenerator({
378
379
  />
379
380
  <label
380
381
  htmlFor={`harmony-${option}`}
381
- className="text-sm font-medium text-gray-700"
382
+ className="text-sm font-bold text-gray-700"
382
383
  >
383
384
  {option}
384
385
  </label>
@@ -409,7 +410,7 @@ export function AiPaneGenerator({
409
410
  </div>
410
411
 
411
412
  <div>
412
- <label className="block text-lg font-semibold text-gray-800">
413
+ <label className="block text-lg font-bold text-gray-800">
413
414
  Theme / Mood
414
415
  </label>
415
416
  <div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
@@ -426,7 +427,7 @@ export function AiPaneGenerator({
426
427
  />
427
428
  <label
428
429
  htmlFor={`theme-${option}`}
429
- className="text-sm font-medium text-gray-700"
430
+ className="text-sm font-bold text-gray-700"
430
431
  >
431
432
  {option}
432
433
  </label>
@@ -438,7 +439,7 @@ export function AiPaneGenerator({
438
439
  <div>
439
440
  <label
440
441
  htmlFor="additional-notes"
441
- className="block text-lg font-semibold text-gray-800"
442
+ className="block text-lg font-bold text-gray-800"
442
443
  >
443
444
  Additional Design Notes (Optional)
444
445
  </label>
@@ -456,78 +457,14 @@ export function AiPaneGenerator({
456
457
  />
457
458
  </div>
458
459
 
459
- <div>
460
- <label className="block text-lg font-semibold text-gray-800">
461
- Provide Content
462
- </label>
463
- <div className="my-2 flex space-x-4">
464
- <div className="flex items-center space-x-2">
465
- <input
466
- type="radio"
467
- id="copy-prompt-mode"
468
- name="copyModeOptions"
469
- value="prompt"
470
- checked={copyMode === 'prompt'}
471
- onChange={(e) => setCopyMode(e.target.value as CopyMode)}
472
- className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
473
- />
474
- <label
475
- htmlFor="copy-prompt-mode"
476
- className="text-sm font-medium text-gray-700"
477
- >
478
- Write a prompt
479
- </label>
480
- </div>
481
- <div className="flex items-center space-x-2">
482
- <input
483
- type="radio"
484
- id="copy-raw-mode"
485
- name="copyModeOptions"
486
- value="raw"
487
- checked={copyMode === 'raw'}
488
- onChange={(e) => setCopyMode(e.target.value as CopyMode)}
489
- className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
490
- />
491
- <label
492
- htmlFor="copy-raw-mode"
493
- className="text-sm font-medium text-gray-700"
494
- >
495
- Provide Copy
496
- </label>
497
- </div>
498
- </div>
499
-
500
- {copyMode === 'prompt' ? (
501
- <>
502
- <p className="mb-2 text-sm text-gray-500">
503
- Let the AI write the copy based on your prompt.
504
- </p>
505
- <textarea
506
- id="copy-prompt"
507
- value={copyPrompt}
508
- onChange={(e) => setCopyPrompt(e.target.value)}
509
- placeholder="Enter copy prompt..."
510
- rows={4}
511
- className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
512
- />
513
- </>
514
- ) : (
515
- <>
516
- <p className="mb-2 text-sm text-gray-500">
517
- Provide your raw copy text here. The AI will structure and style
518
- it.
519
- </p>
520
- <textarea
521
- id="raw-copy"
522
- value={rawCopy}
523
- onChange={(e) => setRawCopy(e.target.value)}
524
- placeholder="Paste or type your copy text..."
525
- rows={6}
526
- className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
527
- />
528
- </>
529
- )}
530
- </div>
460
+ <CopyInputStep
461
+ copyMode={copyMode}
462
+ onCopyModeChange={setCopyMode}
463
+ promptValue={copyPrompt}
464
+ onPromptValueChange={setCopyPrompt}
465
+ copyValue={rawCopy}
466
+ onCopyValueChange={setRawCopy}
467
+ />
531
468
 
532
469
  {error && <p className="text-sm text-red-600">{error}</p>}
533
470
 
@@ -0,0 +1,140 @@
1
+ import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
2
+ import type { BrandConfig } from '@/types/tractstack';
3
+
4
+ export interface AiDesignConfig {
5
+ harmony: string;
6
+ baseColor: string;
7
+ accentColor: string;
8
+ theme: string;
9
+ additionalNotes: string;
10
+ }
11
+
12
+ interface AiDesignStepProps {
13
+ config: BrandConfig;
14
+ designConfig: AiDesignConfig;
15
+ onDesignConfigChange: (newConfig: AiDesignConfig) => void;
16
+ }
17
+
18
+ const harmonyOptions = [
19
+ 'Analogous',
20
+ 'Monochromatic',
21
+ 'Complementary',
22
+ 'Triadic',
23
+ ];
24
+ const themeOptions = ['Light', 'Dark', 'Bright', 'Muted', 'Pastel', 'Earthy'];
25
+
26
+ export const AiDesignStep = ({
27
+ config,
28
+ designConfig,
29
+ onDesignConfigChange,
30
+ }: AiDesignStepProps) => {
31
+ const updateField = <K extends keyof AiDesignConfig>(
32
+ field: K,
33
+ value: AiDesignConfig[K]
34
+ ) => {
35
+ onDesignConfigChange({ ...designConfig, [field]: value });
36
+ };
37
+
38
+ return (
39
+ <div className="space-y-6 rounded-lg bg-gray-50 p-4 shadow-inner">
40
+ <label className="block text-lg font-semibold text-gray-800">
41
+ 2. Configure AI Design
42
+ </label>
43
+ <div>
44
+ <label className="block text-base font-semibold text-gray-800">
45
+ Color Harmony
46
+ </label>
47
+ <div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
48
+ {harmonyOptions.map((option) => (
49
+ <div key={option} className="flex items-center space-x-2">
50
+ <input
51
+ type="radio"
52
+ id={`harmony-${option}`}
53
+ name="harmonyOptions"
54
+ value={option}
55
+ checked={designConfig.harmony === option}
56
+ onChange={(e) => updateField('harmony', e.target.value)}
57
+ className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
58
+ />
59
+ <label
60
+ htmlFor={`harmony-${option}`}
61
+ className="text-sm font-medium text-gray-700"
62
+ >
63
+ {option}
64
+ </label>
65
+ </div>
66
+ ))}
67
+ </div>
68
+ </div>
69
+
70
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
71
+ <div>
72
+ <ColorPickerCombo
73
+ title="Base Color (Optional)"
74
+ config={config}
75
+ defaultColor={designConfig.baseColor}
76
+ onColorChange={(color) => updateField('baseColor', color)}
77
+ allowNull={true}
78
+ />
79
+ </div>
80
+ <div>
81
+ <ColorPickerCombo
82
+ title="Accent Color (Optional)"
83
+ config={config}
84
+ defaultColor={designConfig.accentColor}
85
+ onColorChange={(color) => updateField('accentColor', color)}
86
+ allowNull={true}
87
+ />
88
+ </div>
89
+ </div>
90
+
91
+ <div>
92
+ <label className="block text-base font-semibold text-gray-800">
93
+ Theme / Mood
94
+ </label>
95
+ <div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
96
+ {themeOptions.map((option) => (
97
+ <div key={option} className="flex items-center space-x-2">
98
+ <input
99
+ type="radio"
100
+ id={`theme-${option}`}
101
+ name="themeOptions"
102
+ value={option}
103
+ checked={designConfig.theme === option}
104
+ onChange={(e) => updateField('theme', e.target.value)}
105
+ className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
106
+ />
107
+ <label
108
+ htmlFor={`theme-${option}`}
109
+ className="text-sm font-medium text-gray-700"
110
+ >
111
+ {option}
112
+ </label>
113
+ </div>
114
+ ))}
115
+ </div>
116
+ </div>
117
+
118
+ <div>
119
+ <label
120
+ htmlFor="additional-notes"
121
+ className="block text-base font-semibold text-gray-800"
122
+ >
123
+ Additional Design Notes (Optional)
124
+ </label>
125
+ <p className="mb-2 mt-1 text-sm text-gray-500">
126
+ Add specific requests like "use rounded corners", "add subtle
127
+ texture".
128
+ </p>
129
+ <textarea
130
+ id="additional-notes"
131
+ value={designConfig.additionalNotes}
132
+ onChange={(e) => updateField('additionalNotes', e.target.value)}
133
+ placeholder="Enter additional notes..."
134
+ rows={3}
135
+ className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
136
+ />
137
+ </div>
138
+ </div>
139
+ );
140
+ };
@@ -0,0 +1,105 @@
1
+ import { useEffect } from 'react';
2
+
3
+ type CopyMode = 'prompt' | 'raw';
4
+
5
+ interface CopyInputStepProps {
6
+ copyMode: CopyMode;
7
+ onCopyModeChange: (mode: CopyMode) => void;
8
+ promptValue: string;
9
+ onPromptValueChange: (value: string) => void;
10
+ copyValue: string;
11
+ onCopyValueChange: (value: string) => void;
12
+ defaultPrompt?: string;
13
+ }
14
+
15
+ export const CopyInputStep = ({
16
+ copyMode,
17
+ onCopyModeChange,
18
+ promptValue,
19
+ onPromptValueChange,
20
+ copyValue,
21
+ onCopyValueChange,
22
+ defaultPrompt,
23
+ }: CopyInputStepProps) => {
24
+ useEffect(() => {
25
+ // Pre-populate the prompt field if a default is provided and the field is empty
26
+ if (defaultPrompt && !promptValue) {
27
+ onPromptValueChange(defaultPrompt);
28
+ }
29
+ }, [defaultPrompt, promptValue, onPromptValueChange]);
30
+
31
+ return (
32
+ <div className="space-y-4 rounded-lg bg-gray-50 p-4 shadow-inner">
33
+ <label className="block text-lg font-semibold text-gray-800">
34
+ 1. Provide Content
35
+ </label>
36
+ <div className="my-2 flex space-x-4">
37
+ <div className="flex items-center space-x-2">
38
+ <input
39
+ type="radio"
40
+ id="copy-prompt-mode"
41
+ name="copyModeOptions"
42
+ value="prompt"
43
+ checked={copyMode === 'prompt'}
44
+ onChange={(e) => onCopyModeChange(e.target.value as CopyMode)}
45
+ className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
46
+ />
47
+ <label
48
+ htmlFor="copy-prompt-mode"
49
+ className="text-sm font-medium text-gray-700"
50
+ >
51
+ Write a prompt
52
+ </label>
53
+ </div>
54
+ <div className="flex items-center space-x-2">
55
+ <input
56
+ type="radio"
57
+ id="copy-raw-mode"
58
+ name="copyModeOptions"
59
+ value="raw"
60
+ checked={copyMode === 'raw'}
61
+ onChange={(e) => onCopyModeChange(e.target.value as CopyMode)}
62
+ className="h-4 w-4 border-gray-300 text-cyan-600 focus:ring-cyan-500"
63
+ />
64
+ <label
65
+ htmlFor="copy-raw-mode"
66
+ className="text-sm font-medium text-gray-700"
67
+ >
68
+ Provide Copy (Markdown)
69
+ </label>
70
+ </div>
71
+ </div>
72
+
73
+ {copyMode === 'prompt' ? (
74
+ <>
75
+ <p className="mb-2 text-sm text-gray-500">
76
+ Let the AI write the copy based on your prompt.
77
+ </p>
78
+ <textarea
79
+ id="copy-prompt"
80
+ value={promptValue}
81
+ onChange={(e) => onPromptValueChange(e.target.value)}
82
+ placeholder="e.g., A hero section for a SaaS product that helps teams collaborate..."
83
+ rows={4}
84
+ className="block w-full rounded-md border-gray-300 p-2 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 sm:text-sm"
85
+ />
86
+ </>
87
+ ) : (
88
+ <>
89
+ <p className="mb-2 text-sm text-gray-500">
90
+ Provide your raw copy here. Use Markdown for formatting (e.g., ##
91
+ Headline, **bold**).
92
+ </p>
93
+ <textarea
94
+ id="raw-copy"
95
+ value={copyValue}
96
+ onChange={(e) => onCopyValueChange(e.target.value)}
97
+ placeholder="## My Awesome Headline..."
98
+ rows={6}
99
+ 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"
100
+ />
101
+ </>
102
+ )}
103
+ </div>
104
+ );
105
+ };