astro-tractstack 2.0.11 → 2.0.13
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 +8 -14
- package/package.json +1 -1
- package/templates/css/storykeep.css +1 -92872
- package/templates/src/components/compositor/Compositor.tsx +14 -6
- package/templates/src/components/compositor/Node.tsx +42 -2
- package/templates/src/components/compositor/nodes/Pane.tsx +0 -5
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +2 -1
- package/templates/src/components/edit/pane/AddPanePanel.tsx +5 -1
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +140 -116
- package/templates/src/components/edit/pane/AiPaneGenerator.tsx +575 -0
- package/templates/src/components/edit/pane/AiPanePreview.tsx +107 -0
- package/templates/src/components/edit/pane/PageGenSelector.tsx +10 -3
- package/templates/src/constants/prompts.json +35 -40
- package/templates/src/stores/nodes.ts +18 -15
- package/templates/src/types/compositorTypes.ts +27 -13
- package/templates/src/utils/compositor/aiPaneParser.ts +613 -0
- package/templates/src/utils/compositor/allowInsert.ts +1 -3
- package/templates/src/utils/compositor/nodesHelper.ts +61 -42
- package/templates/src/utils/compositor/tailwindClasses.ts +200 -70
- package/utils/inject-files.ts +8 -14
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy.tsx +0 -107
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +0 -217
- package/templates/src/components/edit/pane/AddPanePanel_newCopyMode.tsx +0 -109
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { AddPanePanel_newAICopy_modal } from './AddPanePanel_newAICopy_modal';
|
|
3
|
-
import { panePrompts, contextPanePrompts } from '@/constants/prompts.json';
|
|
4
|
-
|
|
5
|
-
interface AddPaneNewAICopyProps {
|
|
6
|
-
onChange: (value: string) => void;
|
|
7
|
-
isContextPane: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const AddPanePanel_newAICopy = ({
|
|
11
|
-
onChange,
|
|
12
|
-
isContextPane,
|
|
13
|
-
}: AddPaneNewAICopyProps) => {
|
|
14
|
-
const [customizedPrompt, setCustomizedPrompt] = useState(
|
|
15
|
-
isContextPane
|
|
16
|
-
? contextPanePrompts.shortformcontext
|
|
17
|
-
: panePrompts.shortformcontext
|
|
18
|
-
);
|
|
19
|
-
const [referenceContext, setReferenceContext] = useState('');
|
|
20
|
-
const [additionalInstructions, setAdditionalInstructions] = useState('');
|
|
21
|
-
const [showModal, setShowModal] = useState(false);
|
|
22
|
-
|
|
23
|
-
const handleGenerate = () => {
|
|
24
|
-
setShowModal(true);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const handleModalClose = () => {
|
|
28
|
-
setShowModal(false);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const handleContentGenerated = (content: string) => {
|
|
32
|
-
onChange(content);
|
|
33
|
-
setShowModal(false);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div className="w-full rounded-lg bg-white shadow">
|
|
38
|
-
<div className="space-y-6 p-6">
|
|
39
|
-
<div>
|
|
40
|
-
<label className="mb-2 block text-sm font-bold text-gray-900">
|
|
41
|
-
Customize Writing Style (Optional)
|
|
42
|
-
</label>
|
|
43
|
-
<textarea
|
|
44
|
-
rows={6}
|
|
45
|
-
className="w-full rounded-md border-gray-300 p-3.5 shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
46
|
-
value={customizedPrompt}
|
|
47
|
-
onChange={(e) => setCustomizedPrompt(e.target.value)}
|
|
48
|
-
maxLength={1000}
|
|
49
|
-
/>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<div>
|
|
53
|
-
<label className="block text-sm font-bold text-gray-900">
|
|
54
|
-
Reference Content – copy and paste dump here; no formatting
|
|
55
|
-
required...
|
|
56
|
-
</label>
|
|
57
|
-
<textarea
|
|
58
|
-
rows={8}
|
|
59
|
-
className="w-full rounded-md border-gray-300 p-3.5 shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
60
|
-
value={referenceContext}
|
|
61
|
-
onChange={(e) => setReferenceContext(e.target.value)}
|
|
62
|
-
maxLength={200000}
|
|
63
|
-
placeholder="Paste your reference content here..."
|
|
64
|
-
/>
|
|
65
|
-
</div>
|
|
66
|
-
|
|
67
|
-
<div>
|
|
68
|
-
<label className="mb-2 block text-sm font-bold text-gray-900">
|
|
69
|
-
Additional Instructions (Optional)
|
|
70
|
-
</label>
|
|
71
|
-
<textarea
|
|
72
|
-
rows={4}
|
|
73
|
-
className="w-full rounded-md border-gray-300 p-3.5 shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
|
|
74
|
-
value={additionalInstructions}
|
|
75
|
-
onChange={(e) => setAdditionalInstructions(e.target.value)}
|
|
76
|
-
maxLength={2000}
|
|
77
|
-
placeholder="Any additional instructions or requirements..."
|
|
78
|
-
/>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div className="flex justify-end">
|
|
82
|
-
<button
|
|
83
|
-
onClick={handleGenerate}
|
|
84
|
-
disabled={!referenceContext.trim()}
|
|
85
|
-
className={`rounded-md px-4 py-2 text-sm font-bold text-white focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2 ${
|
|
86
|
-
referenceContext.trim()
|
|
87
|
-
? 'bg-cyan-600 hover:bg-cyan-700'
|
|
88
|
-
: 'cursor-not-allowed bg-gray-300'
|
|
89
|
-
}`}
|
|
90
|
-
>
|
|
91
|
-
Generate Content
|
|
92
|
-
</button>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<AddPanePanel_newAICopy_modal
|
|
97
|
-
show={showModal}
|
|
98
|
-
onClose={handleModalClose}
|
|
99
|
-
prompt={customizedPrompt}
|
|
100
|
-
referenceContext={referenceContext}
|
|
101
|
-
additionalInstructions={additionalInstructions}
|
|
102
|
-
onChange={handleContentGenerated}
|
|
103
|
-
isContextPane={isContextPane}
|
|
104
|
-
/>
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
};
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { useRef, useState, useEffect } from 'react';
|
|
2
|
-
import { Dialog } from '@ark-ui/react/dialog';
|
|
3
|
-
import { Portal } from '@ark-ui/react/portal';
|
|
4
|
-
import { paneFormatPrompt, formatPrompt } from '@/constants/prompts.json';
|
|
5
|
-
|
|
6
|
-
type GenerationStatus = 'idle' | 'generating' | 'success' | 'error';
|
|
7
|
-
|
|
8
|
-
interface AddPaneNewAICopyModalProps {
|
|
9
|
-
show: boolean;
|
|
10
|
-
onClose: () => void;
|
|
11
|
-
prompt: string;
|
|
12
|
-
referenceContext: string;
|
|
13
|
-
additionalInstructions: string;
|
|
14
|
-
onChange: (value: string) => void;
|
|
15
|
-
isContextPane: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const AddPanePanel_newAICopy_modal = ({
|
|
19
|
-
show,
|
|
20
|
-
onClose,
|
|
21
|
-
prompt,
|
|
22
|
-
referenceContext,
|
|
23
|
-
additionalInstructions,
|
|
24
|
-
onChange,
|
|
25
|
-
isContextPane,
|
|
26
|
-
}: AddPaneNewAICopyModalProps) => {
|
|
27
|
-
const [generationStatus, setGenerationStatus] =
|
|
28
|
-
useState<GenerationStatus>('idle');
|
|
29
|
-
const [error, setError] = useState<string | null>(null);
|
|
30
|
-
const [generatedContent, setGeneratedContent] = useState<string | null>(null);
|
|
31
|
-
const dialogButtonRef = useRef<HTMLButtonElement>(null);
|
|
32
|
-
|
|
33
|
-
const handleModalClose = (details: { open: boolean }) => {
|
|
34
|
-
if (generationStatus === 'generating') {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (generationStatus === 'success' && generatedContent && !details.open) {
|
|
38
|
-
onChange(generatedContent);
|
|
39
|
-
onClose();
|
|
40
|
-
setGenerationStatus('idle');
|
|
41
|
-
setGeneratedContent(null);
|
|
42
|
-
setError(null);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
onClose();
|
|
46
|
-
setGenerationStatus('idle');
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const handleButtonClick = () => {
|
|
50
|
-
if (generationStatus === 'success' && generatedContent) {
|
|
51
|
-
onChange(generatedContent);
|
|
52
|
-
onClose();
|
|
53
|
-
setGenerationStatus('idle');
|
|
54
|
-
setGeneratedContent(null);
|
|
55
|
-
setError(null);
|
|
56
|
-
} else if (generationStatus === 'error') {
|
|
57
|
-
onClose();
|
|
58
|
-
setGenerationStatus('idle');
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const handleGenerate = async () => {
|
|
63
|
-
setGenerationStatus('generating');
|
|
64
|
-
setError(null);
|
|
65
|
-
|
|
66
|
-
const finalPrompt = `${isContextPane ? formatPrompt : paneFormatPrompt}
|
|
67
|
-
|
|
68
|
-
Writing Style Instructions:
|
|
69
|
-
${prompt}
|
|
70
|
-
|
|
71
|
-
Additional Instructions:
|
|
72
|
-
${additionalInstructions}`;
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const goBackend =
|
|
76
|
-
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
77
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
78
|
-
|
|
79
|
-
const response = await fetch(`${goBackend}/api/v1/aai/askLemur`, {
|
|
80
|
-
method: 'POST',
|
|
81
|
-
headers: {
|
|
82
|
-
'Content-Type': 'application/json',
|
|
83
|
-
'X-Tenant-ID': tenantId,
|
|
84
|
-
},
|
|
85
|
-
credentials: 'include',
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
prompt: finalPrompt,
|
|
88
|
-
input_text: referenceContext,
|
|
89
|
-
final_model: 'anthropic/claude-3-5-sonnet',
|
|
90
|
-
temperature: 0.7,
|
|
91
|
-
max_tokens: 4000,
|
|
92
|
-
}),
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
if (!response.ok) {
|
|
96
|
-
throw new Error('Generation failed');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const result = await response.json();
|
|
100
|
-
|
|
101
|
-
if (!result.success || !result.data?.response) {
|
|
102
|
-
throw new Error(result.error || 'Generation failed');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let content = result.data.response;
|
|
106
|
-
if (typeof content === 'object') {
|
|
107
|
-
content = JSON.stringify(content, null, 2);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
setGeneratedContent(content);
|
|
111
|
-
setGenerationStatus('success');
|
|
112
|
-
} catch (err) {
|
|
113
|
-
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
114
|
-
setGenerationStatus('error');
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (show && generationStatus === 'idle') {
|
|
120
|
-
handleGenerate();
|
|
121
|
-
}
|
|
122
|
-
}, [show]);
|
|
123
|
-
|
|
124
|
-
const dialogStyles = `
|
|
125
|
-
[data-part="backdrop"] {
|
|
126
|
-
background-color: rgba(0, 0, 0, 0.3);
|
|
127
|
-
}
|
|
128
|
-
[data-part="content"] {
|
|
129
|
-
background-color: white;
|
|
130
|
-
border-radius: 1rem;
|
|
131
|
-
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
132
|
-
padding: 1.5rem;
|
|
133
|
-
max-width: 42rem;
|
|
134
|
-
width: 100%;
|
|
135
|
-
}
|
|
136
|
-
[data-part="title"] {
|
|
137
|
-
font-size: 1.125rem;
|
|
138
|
-
font-weight: bold;
|
|
139
|
-
color: #1f2937;
|
|
140
|
-
}
|
|
141
|
-
`;
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<>
|
|
145
|
-
<style>{dialogStyles}</style>
|
|
146
|
-
<Dialog.Root open={show} onOpenChange={handleModalClose}>
|
|
147
|
-
<Portal>
|
|
148
|
-
<Dialog.Backdrop className="fixed inset-0" />
|
|
149
|
-
<Dialog.Positioner className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
150
|
-
<Dialog.Content className="overflow-hidden text-left">
|
|
151
|
-
<Dialog.Title className="text-lg font-bold leading-6 text-gray-900">
|
|
152
|
-
{generationStatus === 'error'
|
|
153
|
-
? 'Generation Error'
|
|
154
|
-
: generationStatus === 'success'
|
|
155
|
-
? 'Content Generated'
|
|
156
|
-
: 'Generating Content'}
|
|
157
|
-
</Dialog.Title>
|
|
158
|
-
|
|
159
|
-
<div className="mt-4">
|
|
160
|
-
{generationStatus === 'error' ? (
|
|
161
|
-
<p className="text-sm text-red-600">{error}</p>
|
|
162
|
-
) : generationStatus === 'success' ? (
|
|
163
|
-
<div className="space-y-4">
|
|
164
|
-
<p className="text-sm text-gray-600">
|
|
165
|
-
Content has been generated successfully! Click "Apply
|
|
166
|
-
Content" to use this content with your selected design.
|
|
167
|
-
</p>
|
|
168
|
-
<div className="overflow-y-auto rounded-md border border-gray-200 bg-gray-50 p-4">
|
|
169
|
-
<pre className="whitespace-pre-wrap font-mono text-sm text-gray-800">
|
|
170
|
-
{generatedContent}
|
|
171
|
-
</pre>
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
) : (
|
|
175
|
-
<div className="flex items-center gap-3">
|
|
176
|
-
<div className="h-5 w-5 animate-spin rounded-full border-b-2 border-cyan-600"></div>
|
|
177
|
-
<p className="text-sm text-gray-500">
|
|
178
|
-
Generating your content...
|
|
179
|
-
</p>
|
|
180
|
-
</div>
|
|
181
|
-
)}
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
<div className="mt-6 flex justify-end gap-3">
|
|
185
|
-
{generationStatus !== 'generating' && (
|
|
186
|
-
<button
|
|
187
|
-
type="button"
|
|
188
|
-
className="rounded-md border border-gray-300 bg-gray-100 px-4 py-2 text-sm font-bold text-gray-700 hover:bg-gray-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-500 focus-visible:ring-offset-2"
|
|
189
|
-
onClick={() => {
|
|
190
|
-
onClose();
|
|
191
|
-
setGenerationStatus('idle');
|
|
192
|
-
}}
|
|
193
|
-
>
|
|
194
|
-
Cancel
|
|
195
|
-
</button>
|
|
196
|
-
)}
|
|
197
|
-
<button
|
|
198
|
-
ref={dialogButtonRef}
|
|
199
|
-
type="button"
|
|
200
|
-
className="inline-flex justify-center rounded-md border border-transparent bg-cyan-600 px-4 py-2 text-sm font-bold text-white hover:bg-cyan-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-cyan-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:bg-gray-300"
|
|
201
|
-
onClick={handleButtonClick}
|
|
202
|
-
disabled={generationStatus === 'generating'}
|
|
203
|
-
>
|
|
204
|
-
{generationStatus === 'error'
|
|
205
|
-
? 'Try Again'
|
|
206
|
-
: generationStatus === 'success'
|
|
207
|
-
? 'Apply Content'
|
|
208
|
-
: 'Please Wait...'}
|
|
209
|
-
</button>
|
|
210
|
-
</div>
|
|
211
|
-
</Dialog.Content>
|
|
212
|
-
</Dialog.Positioner>
|
|
213
|
-
</Portal>
|
|
214
|
-
</Dialog.Root>
|
|
215
|
-
</>
|
|
216
|
-
);
|
|
217
|
-
};
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { RadioGroup } from '@ark-ui/react/radio-group';
|
|
2
|
-
import CheckCircleIcon from '@heroicons/react/24/outline/CheckCircleIcon';
|
|
3
|
-
import { hasAssemblyAIStore } from '@/stores/storykeep';
|
|
4
|
-
|
|
5
|
-
export type CopyMode = 'design' | 'ai' | 'custom' | 'blank';
|
|
6
|
-
|
|
7
|
-
interface AddPaneNewCopyModeProps {
|
|
8
|
-
selected: CopyMode;
|
|
9
|
-
onChange: (mode: CopyMode) => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const AddPaneNewCopyMode = ({
|
|
13
|
-
selected,
|
|
14
|
-
onChange,
|
|
15
|
-
}: AddPaneNewCopyModeProps) => {
|
|
16
|
-
const hasAssemblyAI = hasAssemblyAIStore.get();
|
|
17
|
-
|
|
18
|
-
const baseModesConfig = [
|
|
19
|
-
{
|
|
20
|
-
id: 'design',
|
|
21
|
-
name: 'Quick start',
|
|
22
|
-
description: 'Use pre-designed copy templates',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: 'custom',
|
|
26
|
-
name: 'Provide your own Copy',
|
|
27
|
-
description: 'Write your own markdown content',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: 'blank',
|
|
31
|
-
name: 'Blank',
|
|
32
|
-
description: 'Start with a styled blank slate',
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
const aiModeConfig = {
|
|
37
|
-
id: 'ai',
|
|
38
|
-
name: 'Write with AI',
|
|
39
|
-
description: 'Let AI help write your content',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Include "ai" mode only if hasAssemblyAI is true
|
|
43
|
-
const modes = hasAssemblyAI
|
|
44
|
-
? [baseModesConfig[0], aiModeConfig, ...baseModesConfig.slice(1)]
|
|
45
|
-
: baseModesConfig;
|
|
46
|
-
|
|
47
|
-
// Custom styles for the radio group component
|
|
48
|
-
const radioGroupStyles = `
|
|
49
|
-
.radio-control[data-state="unchecked"] .radio-dot {
|
|
50
|
-
background-color: #d1d5db; /* gray-300 */
|
|
51
|
-
}
|
|
52
|
-
.radio-control[data-state="checked"] .radio-dot {
|
|
53
|
-
background-color: #0891b2; /* bg-cyan-600 */
|
|
54
|
-
}
|
|
55
|
-
.radio-control[data-state="checked"] {
|
|
56
|
-
border-color: #0891b2; /* bg-cyan-600 */
|
|
57
|
-
}
|
|
58
|
-
.radio-item[data-state="checked"] {
|
|
59
|
-
background-color: #0891b2; /* bg-cyan-600 */
|
|
60
|
-
color: white;
|
|
61
|
-
}
|
|
62
|
-
.radio-item:hover:not([data-state="checked"]) {
|
|
63
|
-
background-color: #f3f4f6; /* bg-gray-100 */
|
|
64
|
-
}
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<div className="w-full pr-4">
|
|
69
|
-
<style>{radioGroupStyles}</style>
|
|
70
|
-
<div className="max-w-4xl">
|
|
71
|
-
<RadioGroup.Root
|
|
72
|
-
defaultValue={selected}
|
|
73
|
-
onValueChange={(details) => {
|
|
74
|
-
if (details.value) {
|
|
75
|
-
onChange(details.value as CopyMode);
|
|
76
|
-
}
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
<RadioGroup.Label className="sr-only">Copy Mode</RadioGroup.Label>
|
|
80
|
-
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
81
|
-
{modes.map((mode) => (
|
|
82
|
-
<RadioGroup.Item
|
|
83
|
-
key={mode.id}
|
|
84
|
-
value={mode.id}
|
|
85
|
-
className="radio-item relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none"
|
|
86
|
-
>
|
|
87
|
-
<div className="flex w-full items-center justify-between">
|
|
88
|
-
<div className="flex items-center">
|
|
89
|
-
<RadioGroup.ItemText>
|
|
90
|
-
<div className="text-sm">
|
|
91
|
-
<p className="font-bold">{mode.name}</p>
|
|
92
|
-
<span className="inline">{mode.description}</span>
|
|
93
|
-
</div>
|
|
94
|
-
</RadioGroup.ItemText>
|
|
95
|
-
</div>
|
|
96
|
-
<div className="hidden shrink-0 data-[state=checked]:block">
|
|
97
|
-
<CheckCircleIcon className="h-6 w-6" />
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
<RadioGroup.ItemControl className="hidden" />
|
|
101
|
-
<RadioGroup.ItemHiddenInput />
|
|
102
|
-
</RadioGroup.Item>
|
|
103
|
-
))}
|
|
104
|
-
</div>
|
|
105
|
-
</RadioGroup.Root>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
);
|
|
109
|
-
};
|