astro-tractstack 2.0.11 → 2.0.12
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 +28 -0
- 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_new.tsx +136 -115
- package/templates/src/components/edit/pane/AiPaneGenerator.tsx +405 -0
- package/templates/src/components/edit/pane/AiPanePreview.tsx +257 -0
- package/templates/src/components/edit/pane/PageGenSelector.tsx +6 -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 +587 -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
|
@@ -387,12 +387,6 @@ export const Compositor = (props: CompositorProps) => {
|
|
|
387
387
|
const ctx = getCtx(props);
|
|
388
388
|
const range = $selection;
|
|
389
389
|
|
|
390
|
-
if ($selection.pendingAction === 'style') {
|
|
391
|
-
if (VERBOSE) console.log(LOG_PREFIX + 'useEffect acting on: style');
|
|
392
|
-
await ctx.wrapRangeInSpan(range as SelectionStoreState, 'span');
|
|
393
|
-
resetSelectionStore();
|
|
394
|
-
}
|
|
395
|
-
|
|
396
390
|
if ($selection.pendingAction === 'link') {
|
|
397
391
|
if (VERBOSE) console.log(LOG_PREFIX + 'useEffect acting on: link');
|
|
398
392
|
const newAnchorNodeId = await ctx.wrapRangeInAnchor(
|
|
@@ -402,6 +396,20 @@ export const Compositor = (props: CompositorProps) => {
|
|
|
402
396
|
ctx.handleInsertSignal('a', newAnchorNodeId);
|
|
403
397
|
}
|
|
404
398
|
resetSelectionStore();
|
|
399
|
+
} else if ($selection.pendingAction === 'style') {
|
|
400
|
+
if (VERBOSE) console.log(LOG_PREFIX + 'useEffect acting on: style');
|
|
401
|
+
const newSpanNodeId = await ctx.wrapRangeInSpan(
|
|
402
|
+
range as SelectionStoreState,
|
|
403
|
+
'span'
|
|
404
|
+
);
|
|
405
|
+
if (newSpanNodeId) {
|
|
406
|
+
settingsPanelStore.set({
|
|
407
|
+
action: 'style-element',
|
|
408
|
+
nodeId: newSpanNodeId,
|
|
409
|
+
expanded: true,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
resetSelectionStore();
|
|
405
413
|
}
|
|
406
414
|
ctx.notifyNode('root');
|
|
407
415
|
};
|
|
@@ -45,6 +45,8 @@ import type {
|
|
|
45
45
|
BaseNode,
|
|
46
46
|
FlatNode,
|
|
47
47
|
} from '@/types/compositorTypes';
|
|
48
|
+
import { handleClickEventDefault } from '@/utils/compositor/handleClickEvent';
|
|
49
|
+
import { selectionStore } from '@/stores/selection';
|
|
48
50
|
import type { NodeProps, SelectionOrigin } from '@/types/nodeProps';
|
|
49
51
|
|
|
50
52
|
const VERBOSE = false;
|
|
@@ -291,6 +293,31 @@ const getElement = (
|
|
|
291
293
|
viewportKeyStore.get().value
|
|
292
294
|
);
|
|
293
295
|
|
|
296
|
+
const handleElementClick = (e: MouseEvent<HTMLElement>) => {
|
|
297
|
+
// 1. ALWAYS stop the event from bubbling up to the Pane.
|
|
298
|
+
e.stopPropagation();
|
|
299
|
+
|
|
300
|
+
// 2. Check the selection store. The handleMouseUp in Compositor.tsx
|
|
301
|
+
// has already run by the time this 'click' event fires.
|
|
302
|
+
//
|
|
303
|
+
// - If it was a drag, Compositor.tsx set 'isActive: true' (line 267).
|
|
304
|
+
// - If it was a click, Compositor.tsx called 'resetSelectionStore'
|
|
305
|
+
// (line 242), so 'isActive: false'.
|
|
306
|
+
//
|
|
307
|
+
const { isActive } = selectionStore.get();
|
|
308
|
+
|
|
309
|
+
if (isActive) {
|
|
310
|
+
// A drag just finished. The user's intent was to select text.
|
|
311
|
+
// Do NOT open the panel.
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 3. 'isActive' was false. This was a genuine click.
|
|
316
|
+
// We can safely open the settings panel.
|
|
317
|
+
// 'node' is already in scope from the getElement function.
|
|
318
|
+
handleClickEventDefault(node as FlatNode, true);
|
|
319
|
+
};
|
|
320
|
+
|
|
294
321
|
const handleMouseDown = (e: MouseEvent<HTMLElement>) => {
|
|
295
322
|
if (VERBOSE)
|
|
296
323
|
console.log('[Node.tsx] handleMouseDown FIRED', { event: e });
|
|
@@ -346,6 +373,7 @@ const getElement = (
|
|
|
346
373
|
{
|
|
347
374
|
className: className,
|
|
348
375
|
onMouseDown: handleMouseDown,
|
|
376
|
+
onClick: handleElementClick,
|
|
349
377
|
'data-node-id': node.id,
|
|
350
378
|
style: { userSelect: 'none' },
|
|
351
379
|
},
|
|
@@ -294,11 +294,6 @@ const Pane = memo(
|
|
|
294
294
|
(prevProps: NodeProps, nextProps: NodeProps) => {
|
|
295
295
|
const isEqual =
|
|
296
296
|
prevProps.nodeId === nextProps.nodeId && prevProps.ctx === nextProps.ctx;
|
|
297
|
-
if (!isEqual) {
|
|
298
|
-
console.log(
|
|
299
|
-
` !! Pane rerender triggered by props change: ${prevProps.nodeId} -> ${nextProps.nodeId}`
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
297
|
return isEqual;
|
|
303
298
|
}
|
|
304
299
|
);
|
|
@@ -379,8 +379,9 @@ export const NodeBasicTag = (props: NodeTagProps) => {
|
|
|
379
379
|
.filter(
|
|
380
380
|
(childNode): childNode is FlatNode =>
|
|
381
381
|
'tagName' in childNode &&
|
|
382
|
-
['a', 'button'].includes(childNode.tagName as string)
|
|
382
|
+
['a', 'button', 'span'].includes(childNode.tagName as string)
|
|
383
383
|
) as FlatNode[];
|
|
384
|
+
if (VERBOSE) console.log('originalNodes to save:', originalNodes);
|
|
384
385
|
|
|
385
386
|
const parsedNodes = processRichTextToNodes(
|
|
386
387
|
currentContent,
|
|
@@ -24,13 +24,11 @@ import {
|
|
|
24
24
|
hasAssemblyAIStore,
|
|
25
25
|
} from '@/stores/storykeep';
|
|
26
26
|
import { templateCategories } from '@/utils/compositor/templateMarkdownStyles';
|
|
27
|
-
import {
|
|
28
|
-
import { AddPaneNewCopyMode, type CopyMode } from './AddPanePanel_newCopyMode';
|
|
27
|
+
import { AiPaneGenerator } from './AiPaneGenerator';
|
|
29
28
|
import { AddPaneNewCustomCopy } from './AddPanePanel_newCustomCopy';
|
|
30
|
-
import { getTitleSlug } from '@/utils/aai/getTitleSlug';
|
|
31
|
-
import { fullContentMapStore } from '@/stores/storykeep';
|
|
32
29
|
import { themes, type Theme } from '@/types/tractstack';
|
|
33
|
-
import { PaneAddMode } from '@/types/compositorTypes';
|
|
30
|
+
import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
|
|
31
|
+
import { useStore } from '@nanostores/react';
|
|
34
32
|
|
|
35
33
|
interface AddPaneNewPanelProps {
|
|
36
34
|
nodeId: string;
|
|
@@ -58,47 +56,46 @@ interface TemplateCategory {
|
|
|
58
56
|
|
|
59
57
|
const ITEMS_PER_PAGE = 8;
|
|
60
58
|
|
|
59
|
+
type Mode = 'template' | 'custom' | 'ai';
|
|
60
|
+
|
|
61
61
|
const AddPaneNewPanel = ({
|
|
62
62
|
nodeId,
|
|
63
63
|
first,
|
|
64
|
-
setMode,
|
|
64
|
+
setMode: setParentMode, // Renamed prop to avoid conflict with local state
|
|
65
65
|
ctx,
|
|
66
66
|
isStoryFragment = false,
|
|
67
67
|
isContextPane = false,
|
|
68
68
|
}: AddPaneNewPanelProps) => {
|
|
69
|
-
const brand = brandColourStore
|
|
70
|
-
const hasAssemblyAI = hasAssemblyAIStore
|
|
71
|
-
const [
|
|
69
|
+
const brand = useStore(brandColourStore);
|
|
70
|
+
const hasAssemblyAI = useStore(hasAssemblyAIStore);
|
|
71
|
+
const [mode, setMode] = useState<Mode>('template'); // Local mode state
|
|
72
72
|
const [customMarkdown, setCustomMarkdown] = useState<string>(`...`);
|
|
73
73
|
const [previews, setPreviews] = useState<PreviewPane[]>([]);
|
|
74
74
|
const [currentPage, setCurrentPage] = useState(0);
|
|
75
75
|
const [renderedPages, setRenderedPages] = useState<Set<number>>(new Set());
|
|
76
76
|
const [selectedTheme, setSelectedTheme] = useState<Theme>(
|
|
77
|
-
preferredThemeStore
|
|
77
|
+
useStore(preferredThemeStore)
|
|
78
78
|
);
|
|
79
79
|
const [useOddVariant, setUseOddVariant] = useState(false);
|
|
80
80
|
const [selectedCategory, setSelectedCategory] = useState<TemplateCategory>(
|
|
81
81
|
templateCategories[isContextPane ? 1 : first ? 4 : 0]
|
|
82
82
|
);
|
|
83
83
|
const [isInserting, setIsInserting] = useState(false);
|
|
84
|
-
const [aiContentGenerated, setAiContentGenerated] = useState(false);
|
|
85
84
|
const [fragmentsToGenerate, setFragmentsToGenerate] = useState<
|
|
86
85
|
PanePreviewRequest[]
|
|
87
86
|
>([]);
|
|
88
|
-
const shouldShowDesigns = copyMode !== 'ai' || aiContentGenerated;
|
|
89
87
|
|
|
90
88
|
const categoryCollection = useMemo(() => {
|
|
91
|
-
const categories =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: templateCategories;
|
|
89
|
+
const categories = isContextPane
|
|
90
|
+
? [templateCategories[1]]
|
|
91
|
+
: templateCategories;
|
|
95
92
|
|
|
96
93
|
return createListCollection({
|
|
97
94
|
items: categories,
|
|
98
95
|
itemToValue: (item) => item.id,
|
|
99
96
|
itemToString: (item) => item.title,
|
|
100
97
|
});
|
|
101
|
-
}, [
|
|
98
|
+
}, [isContextPane]);
|
|
102
99
|
|
|
103
100
|
const themesCollection = useMemo(() => {
|
|
104
101
|
return createListCollection({
|
|
@@ -109,38 +106,25 @@ const AddPaneNewPanel = ({
|
|
|
109
106
|
}, []);
|
|
110
107
|
|
|
111
108
|
const filteredTemplates = useMemo(() => {
|
|
112
|
-
if (copyMode === `ai` || isContextPane)
|
|
113
|
-
return templateCategories[1].getTemplates(
|
|
114
|
-
selectedTheme,
|
|
115
|
-
brand,
|
|
116
|
-
useOddVariant
|
|
117
|
-
);
|
|
118
|
-
if (isContextPane)
|
|
119
|
-
return templateCategories[1].getTemplates(
|
|
120
|
-
selectedTheme,
|
|
121
|
-
brand,
|
|
122
|
-
useOddVariant
|
|
123
|
-
);
|
|
124
109
|
return selectedCategory.getTemplates(selectedTheme, brand, useOddVariant);
|
|
125
|
-
}, [selectedTheme, useOddVariant, selectedCategory,
|
|
110
|
+
}, [selectedTheme, useOddVariant, selectedCategory, brand]);
|
|
126
111
|
|
|
127
112
|
useEffect(() => {
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
113
|
+
if (isContextPane) {
|
|
114
|
+
setSelectedCategory(templateCategories[1]);
|
|
115
|
+
} else if (first) {
|
|
116
|
+
setSelectedCategory(templateCategories[4]);
|
|
117
|
+
} else {
|
|
118
|
+
setSelectedCategory(templateCategories[0]);
|
|
119
|
+
}
|
|
120
|
+
}, [isContextPane, first]);
|
|
137
121
|
|
|
138
122
|
useEffect(() => {
|
|
139
123
|
const newPreviews = filteredTemplates.map((template, index: number) => {
|
|
140
|
-
const
|
|
141
|
-
|
|
124
|
+
const previewCtx = new NodesContext();
|
|
125
|
+
previewCtx.addNode(createEmptyStorykeep('tmp'));
|
|
142
126
|
const thisTemplate =
|
|
143
|
-
|
|
127
|
+
mode === 'custom'
|
|
144
128
|
? {
|
|
145
129
|
...template,
|
|
146
130
|
markdown: template.markdown && {
|
|
@@ -149,13 +133,13 @@ const AddPaneNewPanel = ({
|
|
|
149
133
|
},
|
|
150
134
|
}
|
|
151
135
|
: template;
|
|
152
|
-
|
|
153
|
-
return { ctx, template: thisTemplate, index };
|
|
136
|
+
previewCtx.addTemplatePane('tmp', thisTemplate);
|
|
137
|
+
return { ctx: previewCtx, template: thisTemplate, index };
|
|
154
138
|
});
|
|
155
139
|
setPreviews(newPreviews);
|
|
156
140
|
setCurrentPage(0);
|
|
157
141
|
setRenderedPages(new Set());
|
|
158
|
-
}, [filteredTemplates, customMarkdown,
|
|
142
|
+
}, [filteredTemplates, customMarkdown, mode]);
|
|
159
143
|
|
|
160
144
|
const totalPages = Math.ceil(previews.length / ITEMS_PER_PAGE);
|
|
161
145
|
|
|
@@ -223,81 +207,47 @@ const AddPaneNewPanel = ({
|
|
|
223
207
|
});
|
|
224
208
|
};
|
|
225
209
|
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
nodeId: string,
|
|
229
|
-
first: boolean
|
|
230
|
-
) => {
|
|
231
|
-
if (isInserting) return;
|
|
210
|
+
const handleApplyTemplate = async (template: any) => {
|
|
211
|
+
if (isInserting || !ctx) return;
|
|
232
212
|
setIsInserting(true);
|
|
233
213
|
|
|
234
214
|
try {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
template?.markdown?.markdownBody &&
|
|
238
|
-
template.markdown.markdownBody.trim() !== '...' &&
|
|
239
|
-
template.markdown.markdownBody.trim().length > 0;
|
|
240
|
-
|
|
241
|
-
const insertTemplate = [`blank`, `custom`].includes(copyMode)
|
|
215
|
+
const insertTemplate =
|
|
216
|
+
mode === 'custom'
|
|
242
217
|
? {
|
|
243
218
|
...cloneDeep(template),
|
|
244
219
|
markdown: template.markdown && {
|
|
245
220
|
...template.markdown,
|
|
246
|
-
markdownBody:
|
|
221
|
+
markdownBody: customMarkdown,
|
|
247
222
|
},
|
|
248
223
|
}
|
|
249
224
|
: cloneDeep(template);
|
|
250
225
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
) {
|
|
266
|
-
|
|
267
|
-
.get()
|
|
268
|
-
.filter((item) => ['Pane', 'StoryFragment'].includes(item.type))
|
|
269
|
-
.map((item) => item.slug);
|
|
270
|
-
|
|
271
|
-
const titleSlugResult = await getTitleSlug(
|
|
272
|
-
markdownContent,
|
|
273
|
-
existingSlugs
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
if (titleSlugResult) {
|
|
277
|
-
insertTemplate.title = titleSlugResult.title;
|
|
278
|
-
insertTemplate.slug = titleSlugResult.slug;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const ownerId =
|
|
283
|
-
isStoryFragment || isContextPane
|
|
284
|
-
? nodeId
|
|
285
|
-
: ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
|
|
286
|
-
|
|
287
|
-
if (isContextPane) {
|
|
288
|
-
insertTemplate.isContextPane = true;
|
|
289
|
-
ctx.addContextTemplatePane(ownerId, insertTemplate);
|
|
290
|
-
} else {
|
|
291
|
-
const newPaneId = ctx.addTemplatePane(
|
|
226
|
+
insertTemplate.title = '';
|
|
227
|
+
insertTemplate.slug = '';
|
|
228
|
+
|
|
229
|
+
const ownerId =
|
|
230
|
+
isStoryFragment || isContextPane
|
|
231
|
+
? nodeId
|
|
232
|
+
: ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
|
|
233
|
+
|
|
234
|
+
if (isContextPane) {
|
|
235
|
+
insertTemplate.isContextPane = true;
|
|
236
|
+
await ctx.applyAtomicUpdate(async (tmpCtx) => {
|
|
237
|
+
tmpCtx.addContextTemplatePane(ownerId, insertTemplate);
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
await ctx.applyAtomicUpdate(async (tmpCtx) => {
|
|
241
|
+
tmpCtx.addTemplatePane(
|
|
292
242
|
ownerId,
|
|
293
243
|
insertTemplate,
|
|
294
244
|
nodeId,
|
|
295
245
|
first ? 'before' : 'after'
|
|
296
246
|
);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
setMode(PaneAddMode.DEFAULT, false);
|
|
247
|
+
});
|
|
248
|
+
ctx.notifyNode(`root`);
|
|
300
249
|
}
|
|
250
|
+
setParentMode(PaneAddMode.DEFAULT, false);
|
|
301
251
|
} catch (error) {
|
|
302
252
|
console.error('Error inserting template:', error);
|
|
303
253
|
} finally {
|
|
@@ -305,6 +255,39 @@ const AddPaneNewPanel = ({
|
|
|
305
255
|
}
|
|
306
256
|
};
|
|
307
257
|
|
|
258
|
+
const handleApplyGeneratedPane = async (pane: TemplatePane) => {
|
|
259
|
+
if (isInserting || !ctx) return;
|
|
260
|
+
setIsInserting(true);
|
|
261
|
+
try {
|
|
262
|
+
const ownerId =
|
|
263
|
+
isStoryFragment || isContextPane
|
|
264
|
+
? nodeId
|
|
265
|
+
: ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
|
|
266
|
+
|
|
267
|
+
if (isContextPane) {
|
|
268
|
+
pane.isContextPane = true;
|
|
269
|
+
await ctx.applyAtomicUpdate(async (tmpCtx) => {
|
|
270
|
+
tmpCtx.addContextTemplatePane(ownerId, pane);
|
|
271
|
+
});
|
|
272
|
+
} else {
|
|
273
|
+
await ctx.applyAtomicUpdate(async (tmpCtx) => {
|
|
274
|
+
tmpCtx.addTemplatePane(
|
|
275
|
+
ownerId,
|
|
276
|
+
pane,
|
|
277
|
+
nodeId,
|
|
278
|
+
first ? 'before' : 'after'
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
ctx.notifyNode(`root`);
|
|
282
|
+
}
|
|
283
|
+
setParentMode(PaneAddMode.DEFAULT, false);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('Error applying generated pane:', error);
|
|
286
|
+
} finally {
|
|
287
|
+
setIsInserting(false);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
308
291
|
const handleThemeChange = (details: { value: string[] }) => {
|
|
309
292
|
const newTheme = details.value[0] as Theme;
|
|
310
293
|
if (newTheme) {
|
|
@@ -338,8 +321,9 @@ const AddPaneNewPanel = ({
|
|
|
338
321
|
<style>{customStyles}</style>
|
|
339
322
|
<div className="group flex w-full gap-1 rounded-md bg-white p-1.5">
|
|
340
323
|
<button
|
|
341
|
-
onClick={() =>
|
|
324
|
+
onClick={() => setParentMode(PaneAddMode.DEFAULT, first)}
|
|
342
325
|
className="w-fit rounded bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-gray-200 focus:bg-gray-200"
|
|
326
|
+
type="button"
|
|
343
327
|
>
|
|
344
328
|
← Go Back
|
|
345
329
|
</button>
|
|
@@ -348,11 +332,45 @@ const AddPaneNewPanel = ({
|
|
|
348
332
|
<div className="font-action flex-none rounded px-2 py-2.5 text-sm font-bold text-cyan-700 shadow-sm">
|
|
349
333
|
+ Design New Pane
|
|
350
334
|
</div>
|
|
335
|
+
<div className="flex items-center space-x-2 rounded-lg bg-gray-100 p-1">
|
|
336
|
+
<button
|
|
337
|
+
onClick={() => setMode('template')}
|
|
338
|
+
className={`rounded-md px-3 py-1 text-sm font-medium transition-colors ${
|
|
339
|
+
mode === 'template'
|
|
340
|
+
? 'bg-white text-cyan-700 shadow'
|
|
341
|
+
: 'text-gray-600 hover:text-gray-800'
|
|
342
|
+
}`}
|
|
343
|
+
type="button"
|
|
344
|
+
>
|
|
345
|
+
Use Template
|
|
346
|
+
</button>
|
|
347
|
+
<button
|
|
348
|
+
onClick={() => setMode('custom')}
|
|
349
|
+
className={`rounded-md px-3 py-1 text-sm font-medium transition-colors ${
|
|
350
|
+
mode === 'custom'
|
|
351
|
+
? 'bg-white text-cyan-700 shadow'
|
|
352
|
+
: 'text-gray-600 hover:text-gray-800'
|
|
353
|
+
}`}
|
|
354
|
+
type="button"
|
|
355
|
+
>
|
|
356
|
+
Paste Markdown
|
|
357
|
+
</button>
|
|
358
|
+
{hasAssemblyAI && (
|
|
359
|
+
<button
|
|
360
|
+
onClick={() => setMode('ai')}
|
|
361
|
+
className={`rounded-md px-3 py-1 text-sm font-medium transition-colors ${
|
|
362
|
+
mode === 'ai'
|
|
363
|
+
? 'rounded-md border border-transparent bg-cyan-600 px-3 py-1.5 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:ring-offset-2'
|
|
364
|
+
: 'text-gray-600 hover:text-gray-800'
|
|
365
|
+
}`}
|
|
366
|
+
type="button"
|
|
367
|
+
>
|
|
368
|
+
✨ Generate with AI
|
|
369
|
+
</button>
|
|
370
|
+
)}
|
|
371
|
+
</div>
|
|
351
372
|
|
|
352
|
-
{
|
|
353
|
-
<AddPaneNewCopyMode selected={copyMode} onChange={setCopyMode} />
|
|
354
|
-
)}
|
|
355
|
-
{copyMode === 'custom' && (
|
|
373
|
+
{mode === 'custom' && (
|
|
356
374
|
<div className="mt-4 w-full">
|
|
357
375
|
<AddPaneNewCustomCopy
|
|
358
376
|
value={customMarkdown}
|
|
@@ -360,18 +378,19 @@ const AddPaneNewPanel = ({
|
|
|
360
378
|
/>
|
|
361
379
|
</div>
|
|
362
380
|
)}
|
|
363
|
-
{
|
|
381
|
+
{mode === 'ai' && (
|
|
364
382
|
<div className="mt-4 w-full">
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
383
|
+
<AiPaneGenerator
|
|
384
|
+
ownerId={nodeId}
|
|
385
|
+
onComplete={handleApplyGeneratedPane}
|
|
386
|
+
onCancel={() => setMode('template')}
|
|
368
387
|
/>
|
|
369
388
|
</div>
|
|
370
389
|
)}
|
|
371
390
|
</div>
|
|
372
391
|
</div>
|
|
373
392
|
|
|
374
|
-
{
|
|
393
|
+
{mode !== 'ai' && (
|
|
375
394
|
<>
|
|
376
395
|
<h3 className="font-action px-3.5 pb-1.5 pt-4 text-xl font-bold text-black">
|
|
377
396
|
1. Template design settings
|
|
@@ -521,8 +540,7 @@ const AddPaneNewPanel = ({
|
|
|
521
540
|
onClick={
|
|
522
541
|
isInserting
|
|
523
542
|
? undefined
|
|
524
|
-
: () =>
|
|
525
|
-
handleTemplateInsert(preview.template, nodeId, first)
|
|
543
|
+
: () => handleApplyTemplate(preview.template)
|
|
526
544
|
}
|
|
527
545
|
className={`bg-mywhite group relative w-full rounded-sm shadow-inner ${
|
|
528
546
|
isInserting
|
|
@@ -588,6 +606,7 @@ const AddPaneNewPanel = ({
|
|
|
588
606
|
onClick={() => handlePageChange(currentPage - 1)}
|
|
589
607
|
disabled={currentPage === 0}
|
|
590
608
|
className="rounded bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-gray-200 focus:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-50"
|
|
609
|
+
type="button"
|
|
591
610
|
>
|
|
592
611
|
Previous
|
|
593
612
|
</button>
|
|
@@ -601,6 +620,7 @@ const AddPaneNewPanel = ({
|
|
|
601
620
|
? 'bg-cyan-700 text-white'
|
|
602
621
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
|
603
622
|
}`}
|
|
623
|
+
type="button"
|
|
604
624
|
>
|
|
605
625
|
{index + 1}
|
|
606
626
|
</button>
|
|
@@ -610,6 +630,7 @@ const AddPaneNewPanel = ({
|
|
|
610
630
|
onClick={() => handlePageChange(currentPage + 1)}
|
|
611
631
|
disabled={currentPage === totalPages - 1}
|
|
612
632
|
className="rounded bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-gray-200 focus:bg-gray-200 disabled:cursor-not-allowed disabled:opacity-50"
|
|
633
|
+
type="button"
|
|
613
634
|
>
|
|
614
635
|
Next
|
|
615
636
|
</button>
|