astro-tractstack 2.0.16 → 2.0.18
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 +24 -0
- package/package.json +1 -1
- package/templates/custom/with-examples/SandboxLauncher.tsx +11 -9
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -1
- package/templates/src/components/compositor/Compositor.tsx +1 -0
- package/templates/src/components/compositor/Node.tsx +41 -17
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +9 -6
- package/templates/src/components/compositor/nodes/GridLayout.tsx +124 -0
- package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +33 -0
- package/templates/src/components/compositor/nodes/Markdown.tsx +67 -37
- package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +56 -0
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +1 -1
- package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +8 -2
- package/templates/src/components/edit/PanelSwitch.tsx +232 -75
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +3 -3
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +184 -151
- package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -7
- package/templates/src/components/edit/pane/PanePanel_impression.tsx +1 -1
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +8 -5
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +6 -6
- package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +3 -3
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -4
- package/templates/src/components/edit/pane/steps/DirectInjectStep.tsx +96 -0
- package/templates/src/components/edit/panels/StyleElementPanel.tsx +11 -4
- package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +8 -8
- package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +14 -4
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +16 -4
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +8 -3
- package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +5 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +7 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +5 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +530 -171
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +77 -42
- package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +38 -22
- package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +171 -66
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +166 -98
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +7 -3
- package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +6 -2
- package/templates/src/components/edit/state/SaveModal.tsx +10 -2
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +6 -6
- package/templates/src/components/fields/PaneBreakShapeSelector.tsx +1 -1
- package/templates/src/components/widgets/ImpressionWrapper.tsx +4 -1
- package/templates/src/constants/prompts.json +1 -1
- package/templates/src/constants.ts +1 -0
- package/templates/src/stores/nodes.ts +110 -33
- package/templates/src/stores/storykeep.ts +3 -1
- package/templates/src/types/compositorTypes.ts +37 -2
- package/templates/src/utils/compositor/TemplateNodes.ts +8 -0
- package/templates/src/utils/compositor/aiPaneParser.ts +8 -2
- package/templates/src/utils/compositor/nodesHelper.ts +229 -0
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +40 -1
- package/templates/src/utils/compositor/typeGuards.ts +7 -0
- package/templates/src/utils/etl/extractor.ts +1 -5
- package/templates/src/utils/etl/index.ts +1 -0
- package/templates/src/utils/etl/transformer.ts +70 -25
- package/utils/inject-files.ts +24 -0
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
mergeCopyIntoTemplate,
|
|
20
20
|
convertTemplateToAIShell,
|
|
21
21
|
} from '@/utils/compositor/designLibraryHelper';
|
|
22
|
+
import { DirectInjectStep } from './steps/DirectInjectStep';
|
|
22
23
|
|
|
23
24
|
type Step =
|
|
24
25
|
| 'initial'
|
|
@@ -26,7 +27,8 @@ type Step =
|
|
|
26
27
|
| 'designLibrary'
|
|
27
28
|
| 'aiDesign'
|
|
28
29
|
| 'loading'
|
|
29
|
-
| 'error'
|
|
30
|
+
| 'error'
|
|
31
|
+
| 'directInject';
|
|
30
32
|
|
|
31
33
|
type InitialChoice = 'library' | 'ai' | 'blank';
|
|
32
34
|
type CopyMode = 'prompt' | 'raw';
|
|
@@ -137,6 +139,8 @@ const AddPaneNewPanel = ({
|
|
|
137
139
|
const [copyMode, setCopyMode] = useState<CopyMode>('prompt');
|
|
138
140
|
const [promptValue, setPromptValue] = useState('');
|
|
139
141
|
const [copyValue, setCopyValue] = useState('');
|
|
142
|
+
const [selectedLibraryEntry, setSelectedLibraryEntry] =
|
|
143
|
+
useState<DesignLibraryEntry | null>(null);
|
|
140
144
|
const [aiDesignConfig, setAiDesignConfig] = useState<AiDesignConfig>({
|
|
141
145
|
harmony: 'Analogous',
|
|
142
146
|
baseColor: '',
|
|
@@ -151,42 +155,48 @@ const AddPaneNewPanel = ({
|
|
|
151
155
|
|
|
152
156
|
if (choice === 'blank') {
|
|
153
157
|
handleBlankSlate();
|
|
154
|
-
} else {
|
|
155
|
-
setStep('
|
|
158
|
+
} else if (choice === 'library') {
|
|
159
|
+
setStep('designLibrary');
|
|
160
|
+
} else if (choice === 'ai') {
|
|
161
|
+
setStep('aiDesign');
|
|
156
162
|
}
|
|
157
163
|
};
|
|
158
164
|
|
|
159
165
|
const handleBack = () => {
|
|
160
166
|
setError(null);
|
|
161
167
|
if (step === 'copyInput') {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
setStep('designLibrary');
|
|
171
|
-
} else if (initialChoice === 'ai') {
|
|
168
|
+
if (initialChoice === 'library') {
|
|
169
|
+
setStep('designLibrary');
|
|
170
|
+
} else if (initialChoice === 'ai') {
|
|
171
|
+
setStep('aiDesign');
|
|
172
|
+
} else {
|
|
173
|
+
setStep('initial');
|
|
174
|
+
}
|
|
175
|
+
} else if (step === 'directInject') {
|
|
172
176
|
setStep('aiDesign');
|
|
177
|
+
} else if (
|
|
178
|
+
step === 'designLibrary' ||
|
|
179
|
+
step === 'aiDesign' ||
|
|
180
|
+
step === 'error'
|
|
181
|
+
) {
|
|
182
|
+
setStep('initial');
|
|
173
183
|
}
|
|
174
184
|
};
|
|
175
185
|
|
|
176
186
|
const handleBlankSlate = () => {
|
|
177
187
|
const blankTemplate: TemplatePane = {
|
|
178
|
-
id: '',
|
|
188
|
+
id: '',
|
|
179
189
|
nodeType: 'Pane',
|
|
180
|
-
parentId: '',
|
|
181
|
-
title: '
|
|
190
|
+
parentId: '',
|
|
191
|
+
title: '',
|
|
182
192
|
slug: '',
|
|
183
193
|
isDecorative: false,
|
|
184
194
|
markdown: {
|
|
185
|
-
id: '',
|
|
195
|
+
id: '',
|
|
186
196
|
nodeType: 'Markdown',
|
|
187
|
-
parentId: '',
|
|
197
|
+
parentId: '',
|
|
188
198
|
type: 'markdown',
|
|
189
|
-
markdownId: '',
|
|
199
|
+
markdownId: '',
|
|
190
200
|
defaultClasses: {},
|
|
191
201
|
parentClasses: [],
|
|
192
202
|
nodes: [],
|
|
@@ -195,138 +205,25 @@ const AddPaneNewPanel = ({
|
|
|
195
205
|
handleApplyTemplate(blankTemplate);
|
|
196
206
|
};
|
|
197
207
|
|
|
198
|
-
const handleDesignLibrarySelect =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const liveTemplate = convertStorageToLiveTemplate(
|
|
202
|
-
mergeCopyIntoTemplate(entry.template, []) // Start with blank copy
|
|
203
|
-
);
|
|
204
|
-
if (liveTemplate.markdown) {
|
|
205
|
-
liveTemplate.markdown.markdownBody = copyValue;
|
|
206
|
-
}
|
|
207
|
-
handleApplyTemplate(liveTemplate);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// This flow is for "Design Library + Write a Prompt" (Hybrid AI)
|
|
212
|
-
if (copyMode === 'prompt') {
|
|
213
|
-
setError(null);
|
|
214
|
-
setStep('loading');
|
|
215
|
-
try {
|
|
216
|
-
// 1. Get the full, rich template from the library
|
|
217
|
-
const liveTemplate = convertStorageToLiveTemplate(entry.template);
|
|
218
|
-
if (!liveTemplate.markdown) {
|
|
219
|
-
throw new Error(
|
|
220
|
-
'The selected design library item is not compatible with this workflow as it has no markdown section.'
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// 2. Create the simplified shell for the AI
|
|
225
|
-
const shellJson = convertTemplateToAIShell(liveTemplate);
|
|
226
|
-
if (!shellJson || shellJson === '{}') {
|
|
227
|
-
throw new Error(
|
|
228
|
-
'Could not generate a valid AI shell from this design.'
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 3. Get the AI to write copy based on the shell and prompt
|
|
233
|
-
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
234
|
-
const layout = 'Text Only';
|
|
235
|
-
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
236
|
-
.replace('{{COPY_INPUT}}', promptValue)
|
|
237
|
-
.replace(
|
|
238
|
-
'{{DESIGN_INPUT}}',
|
|
239
|
-
"N/A - Use the provided Shell JSON's design."
|
|
240
|
-
)
|
|
241
|
-
.replace('{{LAYOUT_TYPE}}', layout)
|
|
242
|
-
.replace('{{SHELL_JSON}}', shellJson);
|
|
243
|
-
|
|
244
|
-
const copyResult = await callAskLemurAPI(
|
|
245
|
-
formattedCopyPrompt,
|
|
246
|
-
copyPromptDetails.system || '',
|
|
247
|
-
false,
|
|
248
|
-
isSandboxMode
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
// 4. Parse ONLY the AI-generated HTML into content nodes
|
|
252
|
-
const newNodes = parseAiCopyHtml(copyResult, liveTemplate.markdown.id);
|
|
253
|
-
|
|
254
|
-
// 5. Create the final pane by cloning the original rich template
|
|
255
|
-
const finalPane = cloneDeep(liveTemplate);
|
|
256
|
-
|
|
257
|
-
// 6. Inject the new AI content, preserving the original rich design
|
|
258
|
-
finalPane.markdown!.nodes = newNodes;
|
|
259
|
-
|
|
260
|
-
// 7. Apply the complete, correctly merged pane
|
|
261
|
-
handleApplyTemplate(finalPane);
|
|
262
|
-
} catch (err: any) {
|
|
263
|
-
setError(err.message || 'Failed to generate AI copy for this design.');
|
|
264
|
-
setStep('error');
|
|
265
|
-
}
|
|
266
|
-
}
|
|
208
|
+
const handleDesignLibrarySelect = (entry: DesignLibraryEntry) => {
|
|
209
|
+
setSelectedLibraryEntry(entry);
|
|
210
|
+
setStep('copyInput');
|
|
267
211
|
};
|
|
268
212
|
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
|
|
274
|
-
if (aiDesignConfig.baseColor)
|
|
275
|
-
designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
|
|
276
|
-
if (aiDesignConfig.accentColor)
|
|
277
|
-
designInput += ` Use **${aiDesignConfig.accentColor}** as an accent color.`;
|
|
278
|
-
if (aiDesignConfig.additionalNotes)
|
|
279
|
-
designInput += ` Refine with these notes: "${aiDesignConfig.additionalNotes}"`;
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
const shellPromptDetails = prompts.aiPaneShellPrompt;
|
|
283
|
-
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
284
|
-
const layout = 'Text Only'; // Hardcoded for this simplified AI path
|
|
285
|
-
|
|
286
|
-
const formattedShellPrompt = shellPromptDetails.user_template
|
|
287
|
-
.replace('{{DESIGN_INPUT}}', designInput)
|
|
288
|
-
.replace('{{LAYOUT_TYPE}}', layout);
|
|
289
|
-
|
|
290
|
-
const shellResult = await callAskLemurAPI(
|
|
291
|
-
formattedShellPrompt,
|
|
292
|
-
shellPromptDetails.system || '',
|
|
293
|
-
true,
|
|
294
|
-
isSandboxMode
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
const copyInputContent = copyMode === 'prompt' ? promptValue : copyValue;
|
|
298
|
-
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
299
|
-
.replace('{{COPY_INPUT}}', copyInputContent)
|
|
300
|
-
.replace('{{DESIGN_INPUT}}', designInput)
|
|
301
|
-
.replace('{{LAYOUT_TYPE}}', layout)
|
|
302
|
-
.replace('{{SHELL_JSON}}', shellResult);
|
|
303
|
-
|
|
304
|
-
const copyResult = await callAskLemurAPI(
|
|
305
|
-
formattedCopyPrompt,
|
|
306
|
-
copyPromptDetails.system || '',
|
|
307
|
-
false,
|
|
308
|
-
isSandboxMode
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
const finalPane = parseAiPane(shellResult, copyResult, layout);
|
|
312
|
-
handleApplyTemplate(finalPane);
|
|
313
|
-
} catch (err: any) {
|
|
314
|
-
setError(err.message || 'Failed to generate AI pane.');
|
|
315
|
-
setStep('error');
|
|
316
|
-
}
|
|
317
|
-
}, [aiDesignConfig, copyMode, promptValue, copyValue, isSandboxMode]);
|
|
213
|
+
const handleAiDesignContinue = () => {
|
|
214
|
+
setStep('copyInput');
|
|
215
|
+
};
|
|
318
216
|
|
|
319
217
|
const handleApplyTemplate = async (template: TemplatePane) => {
|
|
320
218
|
if (!ctx) return;
|
|
321
219
|
try {
|
|
322
220
|
const insertTemplate = cloneDeep(template);
|
|
323
|
-
insertTemplate.title = insertTemplate.title || 'New Pane';
|
|
324
|
-
insertTemplate.slug = insertTemplate.slug || '';
|
|
325
|
-
|
|
326
221
|
const ownerId =
|
|
327
222
|
isStoryFragment || isContextPane
|
|
328
223
|
? nodeId
|
|
329
224
|
: ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
|
|
225
|
+
insertTemplate.title = '';
|
|
226
|
+
insertTemplate.slug = '';
|
|
330
227
|
|
|
331
228
|
if (isContextPane) {
|
|
332
229
|
insertTemplate.isContextPane = true;
|
|
@@ -356,7 +253,126 @@ const AddPaneNewPanel = ({
|
|
|
356
253
|
}
|
|
357
254
|
};
|
|
358
255
|
|
|
359
|
-
|
|
256
|
+
const handleFinalGenerate = useCallback(async () => {
|
|
257
|
+
setError(null);
|
|
258
|
+
setStep('loading');
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
if (initialChoice === 'library') {
|
|
262
|
+
if (!selectedLibraryEntry) {
|
|
263
|
+
throw new Error('No design library item was selected.');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (copyMode === 'raw') {
|
|
267
|
+
const liveTemplate = convertStorageToLiveTemplate(
|
|
268
|
+
mergeCopyIntoTemplate(selectedLibraryEntry.template, [])
|
|
269
|
+
);
|
|
270
|
+
if (liveTemplate.markdown) {
|
|
271
|
+
liveTemplate.markdown.markdownBody = copyValue;
|
|
272
|
+
}
|
|
273
|
+
handleApplyTemplate(liveTemplate);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (copyMode === 'prompt') {
|
|
278
|
+
const liveTemplate = convertStorageToLiveTemplate(
|
|
279
|
+
selectedLibraryEntry.template
|
|
280
|
+
);
|
|
281
|
+
if (!liveTemplate.markdown) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
'The selected design library item is not compatible with this workflow as it has no markdown section.'
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const shellJson = convertTemplateToAIShell(liveTemplate);
|
|
288
|
+
if (!shellJson || shellJson === '{}') {
|
|
289
|
+
throw new Error(
|
|
290
|
+
'Could not generate a valid AI shell from this design.'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
295
|
+
const layout = 'Text Only';
|
|
296
|
+
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
297
|
+
.replace('{{COPY_INPUT}}', promptValue)
|
|
298
|
+
.replace(
|
|
299
|
+
'{{DESIGN_INPUT}}',
|
|
300
|
+
"N/A - Use the provided Shell JSON's design."
|
|
301
|
+
)
|
|
302
|
+
.replace('{{LAYOUT_TYPE}}', layout)
|
|
303
|
+
.replace('{{SHELL_JSON}}', shellJson);
|
|
304
|
+
|
|
305
|
+
const copyResult = await callAskLemurAPI(
|
|
306
|
+
formattedCopyPrompt,
|
|
307
|
+
copyPromptDetails.system || '',
|
|
308
|
+
false,
|
|
309
|
+
isSandboxMode
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const newNodes = parseAiCopyHtml(
|
|
313
|
+
copyResult,
|
|
314
|
+
liveTemplate.markdown.id
|
|
315
|
+
);
|
|
316
|
+
const finalPane = cloneDeep(liveTemplate);
|
|
317
|
+
finalPane.markdown!.nodes = newNodes;
|
|
318
|
+
handleApplyTemplate(finalPane);
|
|
319
|
+
}
|
|
320
|
+
} else if (initialChoice === 'ai') {
|
|
321
|
+
let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
|
|
322
|
+
if (aiDesignConfig.baseColor)
|
|
323
|
+
designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
|
|
324
|
+
if (aiDesignConfig.accentColor)
|
|
325
|
+
designInput += ` Use **${aiDesignConfig.accentColor}** as an accent color.`;
|
|
326
|
+
if (aiDesignConfig.additionalNotes)
|
|
327
|
+
designInput += ` Refine with these notes: "${aiDesignConfig.additionalNotes}"`;
|
|
328
|
+
|
|
329
|
+
const shellPromptDetails = prompts.aiPaneShellPrompt;
|
|
330
|
+
const copyPromptDetails = prompts.aiPaneCopyPrompt;
|
|
331
|
+
const layout = 'Text Only';
|
|
332
|
+
|
|
333
|
+
const formattedShellPrompt = shellPromptDetails.user_template
|
|
334
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
335
|
+
.replace('{{LAYOUT_TYPE}}', layout);
|
|
336
|
+
|
|
337
|
+
const shellResult = await callAskLemurAPI(
|
|
338
|
+
formattedShellPrompt,
|
|
339
|
+
shellPromptDetails.system || '',
|
|
340
|
+
true,
|
|
341
|
+
isSandboxMode
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const copyInputContent =
|
|
345
|
+
copyMode === 'prompt' ? promptValue : copyValue;
|
|
346
|
+
const formattedCopyPrompt = copyPromptDetails.user_template
|
|
347
|
+
.replace('{{COPY_INPUT}}', copyInputContent)
|
|
348
|
+
.replace('{{DESIGN_INPUT}}', designInput)
|
|
349
|
+
.replace('{{LAYOUT_TYPE}}', layout)
|
|
350
|
+
.replace('{{SHELL_JSON}}', shellResult);
|
|
351
|
+
|
|
352
|
+
const copyResult = await callAskLemurAPI(
|
|
353
|
+
formattedCopyPrompt,
|
|
354
|
+
copyPromptDetails.system || '',
|
|
355
|
+
false,
|
|
356
|
+
isSandboxMode
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const finalPane = parseAiPane(shellResult, copyResult, layout);
|
|
360
|
+
handleApplyTemplate(finalPane);
|
|
361
|
+
}
|
|
362
|
+
} catch (err: any) {
|
|
363
|
+
setError(err.message || 'Failed to generate AI pane.');
|
|
364
|
+
setStep('error');
|
|
365
|
+
}
|
|
366
|
+
}, [
|
|
367
|
+
aiDesignConfig,
|
|
368
|
+
copyMode,
|
|
369
|
+
promptValue,
|
|
370
|
+
copyValue,
|
|
371
|
+
isSandboxMode,
|
|
372
|
+
initialChoice,
|
|
373
|
+
selectedLibraryEntry,
|
|
374
|
+
handleApplyTemplate,
|
|
375
|
+
]);
|
|
360
376
|
|
|
361
377
|
const renderInitialStep = () => (
|
|
362
378
|
<div className="p-4">
|
|
@@ -369,7 +385,7 @@ const AddPaneNewPanel = ({
|
|
|
369
385
|
className="group flex flex-col items-center space-y-3 rounded-lg border bg-white p-6 text-center shadow-sm transition-all hover:border-cyan-600 hover:shadow-lg"
|
|
370
386
|
>
|
|
371
387
|
<SwatchIcon className="h-10 w-10 text-gray-500 transition-colors group-hover:text-cyan-600" />
|
|
372
|
-
<h4 className="font-
|
|
388
|
+
<h4 className="font-bold text-gray-800">Use Design Library</h4>
|
|
373
389
|
<p className="text-sm text-gray-600">
|
|
374
390
|
Start with a pre-made design and add your own content.
|
|
375
391
|
</p>
|
|
@@ -380,7 +396,7 @@ const AddPaneNewPanel = ({
|
|
|
380
396
|
className="group flex flex-col items-center space-y-3 rounded-lg border bg-white p-6 text-center shadow-sm transition-all hover:border-cyan-600 hover:shadow-lg"
|
|
381
397
|
>
|
|
382
398
|
<SparklesIcon className="h-10 w-10 text-gray-500 transition-colors group-hover:text-cyan-600" />
|
|
383
|
-
<h4 className="font-
|
|
399
|
+
<h4 className="font-bold text-gray-800">Design with AI</h4>
|
|
384
400
|
<p className="text-sm text-gray-600">
|
|
385
401
|
Let AI generate a complete design and copy from your prompt.
|
|
386
402
|
</p>
|
|
@@ -391,7 +407,7 @@ const AddPaneNewPanel = ({
|
|
|
391
407
|
className="group flex flex-col items-center space-y-3 rounded-lg border bg-white p-6 text-center shadow-sm transition-all hover:border-cyan-600 hover:shadow-lg"
|
|
392
408
|
>
|
|
393
409
|
<DocumentPlusIcon className="h-10 w-10 text-gray-500 transition-colors group-hover:text-cyan-600" />
|
|
394
|
-
<h4 className="font-
|
|
410
|
+
<h4 className="font-bold text-gray-800">Blank Slate</h4>
|
|
395
411
|
<p className="text-sm text-gray-600">
|
|
396
412
|
Add a simple, empty pane to build from scratch.
|
|
397
413
|
</p>
|
|
@@ -423,13 +439,13 @@ const AddPaneNewPanel = ({
|
|
|
423
439
|
← Back
|
|
424
440
|
</button>
|
|
425
441
|
<button
|
|
426
|
-
onClick={
|
|
442
|
+
onClick={handleFinalGenerate}
|
|
427
443
|
disabled={
|
|
428
444
|
copyMode === 'prompt' ? !promptValue.trim() : !copyValue.trim()
|
|
429
445
|
}
|
|
430
446
|
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
|
|
431
447
|
>
|
|
432
|
-
|
|
448
|
+
✨ Generate Pane
|
|
433
449
|
</button>
|
|
434
450
|
</div>
|
|
435
451
|
</div>
|
|
@@ -442,7 +458,7 @@ const AddPaneNewPanel = ({
|
|
|
442
458
|
onClick={handleBack}
|
|
443
459
|
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"
|
|
444
460
|
>
|
|
445
|
-
← Back to
|
|
461
|
+
← Back to Choice
|
|
446
462
|
</button>
|
|
447
463
|
</div>
|
|
448
464
|
<DesignLibraryStep
|
|
@@ -467,17 +483,32 @@ const AddPaneNewPanel = ({
|
|
|
467
483
|
← Back
|
|
468
484
|
</button>
|
|
469
485
|
<button
|
|
470
|
-
onClick={
|
|
486
|
+
onClick={handleAiDesignContinue}
|
|
471
487
|
className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700"
|
|
472
488
|
>
|
|
473
|
-
|
|
489
|
+
Continue →
|
|
474
490
|
</button>
|
|
475
491
|
</div>
|
|
492
|
+
{initialChoice === `ai` && !isSandboxMode && (
|
|
493
|
+
<div className="mt-6 text-center text-sm text-gray-600">
|
|
494
|
+
ADVANCED:{' '}
|
|
495
|
+
<button
|
|
496
|
+
onClick={() => setStep('directInject')}
|
|
497
|
+
className="font-bold text-cyan-700 underline hover:text-cyan-900 focus:outline-none"
|
|
498
|
+
>
|
|
499
|
+
Direct Inject
|
|
500
|
+
</button>
|
|
501
|
+
</div>
|
|
502
|
+
)}
|
|
476
503
|
</div>
|
|
477
504
|
);
|
|
478
505
|
|
|
506
|
+
const renderDirectInjectStep = () => (
|
|
507
|
+
<DirectInjectStep onBack={handleBack} onCreatePane={handleApplyTemplate} />
|
|
508
|
+
);
|
|
509
|
+
|
|
479
510
|
const renderLoading = () => (
|
|
480
|
-
<div className="flex min-h-
|
|
511
|
+
<div className="flex min-h-80 flex-col items-center justify-center space-y-4 p-6">
|
|
481
512
|
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-cyan-600"></div>
|
|
482
513
|
<p className="text-sm text-gray-600">Generating AI Pane...</p>
|
|
483
514
|
<p className="text-xs text-gray-500">This may take a moment.</p>
|
|
@@ -509,6 +540,8 @@ const AddPaneNewPanel = ({
|
|
|
509
540
|
return renderAiDesignStep();
|
|
510
541
|
case 'loading':
|
|
511
542
|
return renderLoading();
|
|
543
|
+
case 'directInject':
|
|
544
|
+
return renderDirectInjectStep();
|
|
512
545
|
case 'error':
|
|
513
546
|
return renderError();
|
|
514
547
|
default:
|
|
@@ -539,7 +572,7 @@ const AddPaneNewPanel = ({
|
|
|
539
572
|
</>
|
|
540
573
|
)}
|
|
541
574
|
</div>
|
|
542
|
-
<div className="min-h-
|
|
575
|
+
<div className="min-h-96 rounded-md border bg-gray-50">
|
|
543
576
|
{renderStep()}
|
|
544
577
|
</div>
|
|
545
578
|
</div>
|
|
@@ -310,7 +310,7 @@ const AddPaneReUsePanel = ({
|
|
|
310
310
|
<style>{comboboxItemStyles}</style>
|
|
311
311
|
<div className="w-full rounded-md bg-white p-1.5">
|
|
312
312
|
<div className="flex flex-wrap items-center gap-2">
|
|
313
|
-
<div className="flex min-w-
|
|
313
|
+
<div className="flex min-w-48 flex-wrap items-center gap-2">
|
|
314
314
|
<button
|
|
315
315
|
onClick={() => setMode(PaneAddMode.DEFAULT)}
|
|
316
316
|
className="w-fit flex-none rounded bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-gray-200 focus:bg-gray-200"
|
|
@@ -323,7 +323,7 @@ const AddPaneReUsePanel = ({
|
|
|
323
323
|
</div>
|
|
324
324
|
</div>
|
|
325
325
|
|
|
326
|
-
<div className="min-w-
|
|
326
|
+
<div className="min-w-72 flex-1">
|
|
327
327
|
<Combobox.Root
|
|
328
328
|
collection={collection}
|
|
329
329
|
value={[]}
|
|
@@ -9,11 +9,7 @@ import {
|
|
|
9
9
|
isContextPaneNode,
|
|
10
10
|
hasBeliefPayload,
|
|
11
11
|
} from '@/utils/compositor/typeGuards';
|
|
12
|
-
import {
|
|
13
|
-
settingsPanelStore,
|
|
14
|
-
viewportKeyStore,
|
|
15
|
-
fullContentMapStore,
|
|
16
|
-
} from '@/stores/storykeep';
|
|
12
|
+
import { settingsPanelStore, fullContentMapStore } from '@/stores/storykeep';
|
|
17
13
|
import { getCtx } from '@/stores/nodes';
|
|
18
14
|
import PaneTitlePanel from './PanePanel_title';
|
|
19
15
|
import PaneMagicPathPanel from './PanePanel_path';
|
|
@@ -34,8 +30,6 @@ const ConfigPanePanel = ({ nodeId }: ConfigPanePanelProps) => {
|
|
|
34
30
|
const isActiveMode =
|
|
35
31
|
activePaneMode.panel === 'settings' && activePaneMode.paneId === nodeId;
|
|
36
32
|
const $contentMap = useStore(fullContentMapStore);
|
|
37
|
-
const $viewportKey = useStore(viewportKeyStore);
|
|
38
|
-
const isMobile = $viewportKey.value === `mobile`;
|
|
39
33
|
|
|
40
34
|
const allNodes = ctx.allNodes.get();
|
|
41
35
|
const paneNode = allNodes.get(nodeId) as PaneNode;
|
|
@@ -200,7 +200,7 @@ const PaneImpressionPanel = ({ nodeId, setMode }: PaneImpressionPanelProps) => {
|
|
|
200
200
|
onChange={(e) => handleInputChange('body', e.target.value)}
|
|
201
201
|
onBlur={handleBlur}
|
|
202
202
|
placeholder="Enter impression body text"
|
|
203
|
-
className={`${commonInputClass} min-h-
|
|
203
|
+
className={`${commonInputClass} min-h-24`}
|
|
204
204
|
/>
|
|
205
205
|
</div>
|
|
206
206
|
|
|
@@ -130,7 +130,7 @@ const TemplatePreviewItem = ({
|
|
|
130
130
|
)}
|
|
131
131
|
</div>
|
|
132
132
|
<div className="p-3">
|
|
133
|
-
<h3 className="truncate font-
|
|
133
|
+
<h3 className="truncate font-bold" title={template.title}>
|
|
134
134
|
{template.title}
|
|
135
135
|
</h3>
|
|
136
136
|
<p className="text-sm text-gray-600">
|
|
@@ -404,9 +404,12 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
404
404
|
<Portal>
|
|
405
405
|
<Dialog.Backdrop className="z-103 fixed inset-0 bg-black/70" />
|
|
406
406
|
<Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
|
|
407
|
-
<Dialog.Content
|
|
407
|
+
<Dialog.Content
|
|
408
|
+
className="flex flex-col rounded-lg bg-white shadow-2xl"
|
|
409
|
+
style={{ height: '90vw', width: '90vw' }}
|
|
410
|
+
>
|
|
408
411
|
<header className="flex items-center justify-between border-b p-4">
|
|
409
|
-
<Dialog.Title className="text-xl font-
|
|
412
|
+
<Dialog.Title className="text-xl font-bold">
|
|
410
413
|
Restyle Pane from Design Library
|
|
411
414
|
</Dialog.Title>
|
|
412
415
|
<Dialog.CloseTrigger
|
|
@@ -427,7 +430,7 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
427
430
|
className="w-48"
|
|
428
431
|
positioning={{ gutter: 4 }}
|
|
429
432
|
>
|
|
430
|
-
<Select.Label className="mb-1 text-sm font-
|
|
433
|
+
<Select.Label className="mb-1 text-sm font-bold">
|
|
431
434
|
Category
|
|
432
435
|
</Select.Label>
|
|
433
436
|
<Select.Control>
|
|
@@ -461,7 +464,7 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
461
464
|
className="flex-1"
|
|
462
465
|
positioning={{ gutter: 4 }}
|
|
463
466
|
>
|
|
464
|
-
<Combobox.Label className="mb-1 text-sm font-
|
|
467
|
+
<Combobox.Label className="mb-1 text-sm font-bold">
|
|
465
468
|
Filter by Title
|
|
466
469
|
</Combobox.Label>
|
|
467
470
|
<Combobox.Control>
|
|
@@ -37,11 +37,11 @@ export const AiDesignStep = ({
|
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
39
|
<div className="space-y-6 rounded-lg bg-gray-50 p-4 shadow-inner">
|
|
40
|
-
<label className="block text-lg font-
|
|
40
|
+
<label className="block text-lg font-bold text-gray-800">
|
|
41
41
|
2. Configure AI Design
|
|
42
42
|
</label>
|
|
43
43
|
<div>
|
|
44
|
-
<label className="block text-base font-
|
|
44
|
+
<label className="block text-base font-bold text-gray-800">
|
|
45
45
|
Color Harmony
|
|
46
46
|
</label>
|
|
47
47
|
<div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
|
|
@@ -58,7 +58,7 @@ export const AiDesignStep = ({
|
|
|
58
58
|
/>
|
|
59
59
|
<label
|
|
60
60
|
htmlFor={`harmony-${option}`}
|
|
61
|
-
className="text-sm font-
|
|
61
|
+
className="text-sm font-bold text-gray-700"
|
|
62
62
|
>
|
|
63
63
|
{option}
|
|
64
64
|
</label>
|
|
@@ -89,7 +89,7 @@ export const AiDesignStep = ({
|
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
91
|
<div>
|
|
92
|
-
<label className="block text-base font-
|
|
92
|
+
<label className="block text-base font-bold text-gray-800">
|
|
93
93
|
Theme / Mood
|
|
94
94
|
</label>
|
|
95
95
|
<div className="mt-2 flex flex-wrap gap-x-4 gap-y-2">
|
|
@@ -106,7 +106,7 @@ export const AiDesignStep = ({
|
|
|
106
106
|
/>
|
|
107
107
|
<label
|
|
108
108
|
htmlFor={`theme-${option}`}
|
|
109
|
-
className="text-sm font-
|
|
109
|
+
className="text-sm font-bold text-gray-700"
|
|
110
110
|
>
|
|
111
111
|
{option}
|
|
112
112
|
</label>
|
|
@@ -118,7 +118,7 @@ export const AiDesignStep = ({
|
|
|
118
118
|
<div>
|
|
119
119
|
<label
|
|
120
120
|
htmlFor="additional-notes"
|
|
121
|
-
className="block text-base font-
|
|
121
|
+
className="block text-base font-bold text-gray-800"
|
|
122
122
|
>
|
|
123
123
|
Additional Design Notes (Optional)
|
|
124
124
|
</label>
|
|
@@ -30,7 +30,7 @@ export const CopyInputStep = ({
|
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
32
|
<div className="space-y-4 rounded-lg bg-gray-50 p-4 shadow-inner">
|
|
33
|
-
<label className="block text-lg font-
|
|
33
|
+
<label className="block text-lg font-bold text-gray-800">
|
|
34
34
|
1. Provide Content
|
|
35
35
|
</label>
|
|
36
36
|
<div className="my-2 flex space-x-4">
|
|
@@ -46,7 +46,7 @@ export const CopyInputStep = ({
|
|
|
46
46
|
/>
|
|
47
47
|
<label
|
|
48
48
|
htmlFor="copy-prompt-mode"
|
|
49
|
-
className="text-sm font-
|
|
49
|
+
className="text-sm font-bold text-gray-700"
|
|
50
50
|
>
|
|
51
51
|
Write a prompt
|
|
52
52
|
</label>
|
|
@@ -63,7 +63,7 @@ export const CopyInputStep = ({
|
|
|
63
63
|
/>
|
|
64
64
|
<label
|
|
65
65
|
htmlFor="copy-raw-mode"
|
|
66
|
-
className="text-sm font-
|
|
66
|
+
className="text-sm font-bold text-gray-700"
|
|
67
67
|
>
|
|
68
68
|
Provide Copy (Markdown)
|
|
69
69
|
</label>
|
|
@@ -139,7 +139,7 @@ const TemplatePreviewItem = ({
|
|
|
139
139
|
</div>
|
|
140
140
|
</div>
|
|
141
141
|
<div className="flex-grow p-3">
|
|
142
|
-
<h3 className="truncate font-
|
|
142
|
+
<h3 className="truncate font-bold" title={title}>
|
|
143
143
|
{title}
|
|
144
144
|
</h3>
|
|
145
145
|
<p className="text-sm capitalize text-gray-600">{category}</p>
|
|
@@ -226,7 +226,7 @@ export const DesignLibraryStep = ({
|
|
|
226
226
|
|
|
227
227
|
return (
|
|
228
228
|
<div className="flex h-full flex-col space-y-4 rounded-lg bg-gray-50 p-4 shadow-inner">
|
|
229
|
-
<label className="block text-lg font-
|
|
229
|
+
<label className="block text-lg font-bold text-gray-800">
|
|
230
230
|
2. Choose a Design
|
|
231
231
|
</label>
|
|
232
232
|
|
|
@@ -241,7 +241,7 @@ export const DesignLibraryStep = ({
|
|
|
241
241
|
className="w-48"
|
|
242
242
|
positioning={{ gutter: 4 }}
|
|
243
243
|
>
|
|
244
|
-
<Select.Label className="mb-1 text-sm font-
|
|
244
|
+
<Select.Label className="mb-1 text-sm font-bold text-gray-700">
|
|
245
245
|
Category
|
|
246
246
|
</Select.Label>
|
|
247
247
|
<Select.Control>
|
|
@@ -280,7 +280,7 @@ export const DesignLibraryStep = ({
|
|
|
280
280
|
className="flex-1"
|
|
281
281
|
positioning={{ gutter: 4 }}
|
|
282
282
|
>
|
|
283
|
-
<Combobox.Label className="mb-1 text-sm font-
|
|
283
|
+
<Combobox.Label className="mb-1 text-sm font-bold text-gray-700">
|
|
284
284
|
Filter by Title
|
|
285
285
|
</Combobox.Label>
|
|
286
286
|
<Combobox.Control>
|