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.
Files changed (65) hide show
  1. package/dist/index.js +24 -0
  2. package/package.json +1 -1
  3. package/templates/custom/with-examples/SandboxLauncher.tsx +11 -9
  4. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
  5. package/templates/src/components/codehooks/ListContentSetup.tsx +1 -1
  6. package/templates/src/components/compositor/Compositor.tsx +1 -0
  7. package/templates/src/components/compositor/Node.tsx +41 -17
  8. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +9 -6
  9. package/templates/src/components/compositor/nodes/GridLayout.tsx +124 -0
  10. package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +33 -0
  11. package/templates/src/components/compositor/nodes/Markdown.tsx +67 -37
  12. package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +56 -0
  13. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +1 -1
  14. package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +8 -2
  15. package/templates/src/components/edit/PanelSwitch.tsx +232 -75
  16. package/templates/src/components/edit/SettingsPanel.tsx +0 -1
  17. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +3 -3
  18. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +184 -151
  19. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +2 -2
  20. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -7
  21. package/templates/src/components/edit/pane/PanePanel_impression.tsx +1 -1
  22. package/templates/src/components/edit/pane/RestylePaneModal.tsx +8 -5
  23. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +6 -6
  24. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +3 -3
  25. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -4
  26. package/templates/src/components/edit/pane/steps/DirectInjectStep.tsx +96 -0
  27. package/templates/src/components/edit/panels/StyleElementPanel.tsx +11 -4
  28. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +8 -8
  29. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +14 -4
  30. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +16 -4
  31. package/templates/src/components/edit/panels/StyleImagePanel.tsx +8 -3
  32. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +9 -2
  33. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +5 -2
  34. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +5 -2
  35. package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +7 -3
  36. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +9 -2
  37. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +5 -2
  38. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +5 -2
  39. package/templates/src/components/edit/panels/StyleParentPanel.tsx +530 -171
  40. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +77 -42
  41. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +38 -22
  42. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +171 -66
  43. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +166 -98
  44. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +7 -3
  45. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +9 -2
  46. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +5 -2
  47. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +6 -2
  48. package/templates/src/components/edit/state/SaveModal.tsx +10 -2
  49. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +6 -6
  50. package/templates/src/components/fields/PaneBreakShapeSelector.tsx +1 -1
  51. package/templates/src/components/widgets/ImpressionWrapper.tsx +4 -1
  52. package/templates/src/constants/prompts.json +1 -1
  53. package/templates/src/constants.ts +1 -0
  54. package/templates/src/stores/nodes.ts +110 -33
  55. package/templates/src/stores/storykeep.ts +3 -1
  56. package/templates/src/types/compositorTypes.ts +37 -2
  57. package/templates/src/utils/compositor/TemplateNodes.ts +8 -0
  58. package/templates/src/utils/compositor/aiPaneParser.ts +8 -2
  59. package/templates/src/utils/compositor/nodesHelper.ts +229 -0
  60. package/templates/src/utils/compositor/reduceNodesClassNames.ts +40 -1
  61. package/templates/src/utils/compositor/typeGuards.ts +7 -0
  62. package/templates/src/utils/etl/extractor.ts +1 -5
  63. package/templates/src/utils/etl/index.ts +1 -0
  64. package/templates/src/utils/etl/transformer.ts +70 -25
  65. 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('copyInput');
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
- setStep('initial');
163
- } else if (step === 'designLibrary' || step === 'aiDesign' || 'error') {
164
- setStep('copyInput');
165
- }
166
- };
167
-
168
- const handleCopyContinue = () => {
169
- if (initialChoice === 'library') {
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: '', // ctx will assign
188
+ id: '',
179
189
  nodeType: 'Pane',
180
- parentId: '', // ctx will assign
181
- title: 'New Pane',
190
+ parentId: '',
191
+ title: '',
182
192
  slug: '',
183
193
  isDecorative: false,
184
194
  markdown: {
185
- id: '', // ctx will assign
195
+ id: '',
186
196
  nodeType: 'Markdown',
187
- parentId: '', // ctx will assign
197
+ parentId: '',
188
198
  type: 'markdown',
189
- markdownId: '', // ctx will assign
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 = async (entry: DesignLibraryEntry) => {
199
- // This flow is for "Design Library + Provide Copy"
200
- if (copyMode === 'raw') {
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 handleAiDesignGenerate = useCallback(async () => {
270
- setError(null);
271
- setStep('loading');
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
- // --- Render Logic ---
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-semibold text-gray-800">Use Design Library</h4>
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-semibold text-gray-800">Design with AI</h4>
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-semibold text-gray-800">Blank Slate</h4>
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={handleCopyContinue}
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
- Continue
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 Content
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={handleAiDesignGenerate}
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
- Generate with AI
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-[300px] flex-col items-center justify-center space-y-4 p-6">
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-[400px] rounded-md border bg-gray-50">
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-[200px] flex-wrap items-center gap-2">
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-[300px] flex-1">
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-[100px]`}
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-semibold" title={template.title}>
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 className="flex h-[90vh] w-[90vw] flex-col rounded-lg bg-white shadow-2xl">
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-semibold">
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-medium">
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-medium">
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-semibold text-gray-800">
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-semibold text-gray-800">
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-medium text-gray-700"
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-semibold text-gray-800">
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-medium text-gray-700"
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-semibold text-gray-800"
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-semibold text-gray-800">
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-medium text-gray-700"
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-medium text-gray-700"
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-semibold" title={title}>
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-semibold text-gray-800">
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-medium text-gray-700">
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-medium text-gray-700">
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>