astro-tractstack 2.0.0-rc.9 → 2.0.1
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/LICENSE +8 -97
- package/README.md +7 -5
- package/bin/create-tractstack.js +31 -8
- package/dist/index.js +106 -29
- package/package.json +10 -5
- package/templates/css/frontend.css +1 -1
- package/templates/custom/minimal/CodeHook.astro +13 -12
- package/templates/custom/minimal/CustomRoutes.astro +25 -31
- package/templates/custom/with-examples/CodeHook.astro +22 -11
- package/templates/custom/with-examples/CustomRoutes.astro +4 -8
- package/templates/custom/with-examples/ProductCard.astro +29 -0
- package/templates/custom/with-examples/ProductCardWrapper.astro +43 -0
- package/templates/custom/with-examples/ProductGrid.astro +64 -0
- package/templates/custom/with-examples/pages/Collections.astro +58 -98
- package/templates/gitignore +42 -0
- package/templates/prettierignore +5 -0
- package/templates/prettierrc +19 -0
- package/templates/src/client/app.js +127 -0
- package/templates/src/client/htmx.min.js +3519 -0
- package/templates/src/client/view.js +429 -0
- package/templates/src/components/Footer.astro +4 -9
- package/templates/src/components/Header.astro +67 -60
- package/templates/src/components/Menu.tsx +188 -52
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +9 -13
- package/templates/src/components/codehooks/EpinetTableView.tsx +11 -7
- package/templates/src/components/codehooks/EpinetWrapper.tsx +10 -9
- package/templates/src/components/codehooks/FeaturedArticle.astro +105 -0
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +318 -0
- package/templates/src/components/codehooks/ListContent.astro +32 -162
- package/templates/src/components/codehooks/ListContentSetup.tsx +43 -138
- package/templates/src/components/codehooks/ProductCardSetup.tsx +152 -0
- package/templates/src/components/codehooks/ProductGridSetup.tsx +274 -0
- package/templates/src/components/codehooks/SearchWidget.tsx +453 -0
- package/templates/src/components/compositor/Node.tsx +3 -6
- package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +21 -11
- package/templates/src/components/compositor/elements/BunnyVideo.tsx +21 -20
- package/templates/src/components/compositor/nodes/Pane.tsx +51 -21
- package/templates/src/components/compositor/nodes/RenderChildren.tsx +6 -1
- package/templates/src/components/compositor/nodes/Widget.tsx +16 -2
- package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +155 -0
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +20 -1
- package/templates/src/components/edit/Header.tsx +10 -4
- package/templates/src/components/edit/PanelSwitch.tsx +11 -7
- package/templates/src/components/edit/SettingsPanel.tsx +29 -18
- package/templates/src/components/edit/ToolBar.tsx +1 -28
- package/templates/src/components/edit/ToolMode.tsx +45 -32
- package/templates/src/components/edit/pane/AddPanePanel_break.tsx +12 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +8 -2
- package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +1 -1
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +17 -27
- package/templates/src/components/edit/pane/PageGenSelector.tsx +16 -16
- package/templates/src/components/edit/pane/PageGenSpecial.tsx +26 -49
- package/templates/src/components/edit/pane/PageGen_preview.tsx +17 -2
- package/templates/src/components/edit/pane/PanePanel_path.tsx +2 -4
- package/templates/src/components/edit/pane/PanePanel_title.tsx +243 -76
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +17 -19
- package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +48 -37
- package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +60 -55
- package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +56 -50
- package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +54 -47
- package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +54 -44
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +54 -40
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +3 -3
- package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +56 -49
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +14 -5
- package/templates/src/components/edit/state/SaveModal.tsx +316 -169
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +1 -1
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +56 -55
- package/templates/src/components/edit/widgets/BunnyWidget.tsx +538 -59
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +656 -0
- package/templates/src/components/edit/widgets/ToggleWidget.tsx +9 -16
- package/templates/src/components/fields/ArtpackImage.tsx +4 -1
- package/templates/src/components/fields/BackgroundImage.tsx +1 -1
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +127 -35
- package/templates/src/components/fields/ColorPickerCombo.tsx +66 -62
- package/templates/src/components/fields/ImageUpload.tsx +1 -1
- package/templates/src/components/fields/ViewportComboBox.tsx +59 -42
- package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
- package/templates/src/components/form/ActionBuilderField.tsx +306 -87
- package/templates/src/components/search/SearchModal.tsx +420 -0
- package/templates/src/components/search/SearchResults.tsx +367 -0
- package/templates/src/components/search/SearchWrapper.tsx +46 -0
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Analytics.tsx +34 -8
- package/templates/src/components/storykeep/Dashboard_Branding.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Content.tsx +6 -0
- package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +38 -34
- package/templates/src/components/storykeep/controls/content/KnownResourceForm.tsx +1 -1
- package/templates/src/components/storykeep/controls/content/MenuForm.tsx +56 -8
- package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +18 -3
- package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
- package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
- package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
- package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
- package/templates/src/constants/shapes.ts +9 -0
- package/templates/src/constants.ts +2121 -16
- package/templates/src/hooks/useSearch.ts +228 -0
- package/templates/src/layouts/Layout.astro +213 -104
- package/templates/src/lib/storyData.ts +4 -1
- package/templates/src/pages/[...slug]/edit.astro +14 -14
- package/templates/src/pages/[...slug].astro +82 -21
- package/templates/src/pages/api/orphan-analysis.ts +0 -1
- package/templates/src/pages/api/tailwind.ts +23 -21
- package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
- package/templates/src/pages/context/[...contextSlug].astro +7 -2
- package/templates/src/pages/storykeep/advanced.astro +5 -4
- package/templates/src/pages/storykeep/branding.astro +5 -4
- package/templates/src/pages/storykeep/content.astro +5 -4
- package/templates/src/pages/storykeep/init.astro +40 -1
- package/templates/src/pages/storykeep/login.astro +1 -1
- package/templates/src/pages/storykeep.astro +5 -4
- package/templates/src/stores/nodes.ts +59 -88
- package/templates/src/stores/orphanAnalysis.ts +19 -21
- package/templates/src/stores/storykeep.ts +7 -0
- package/templates/src/types/compositorTypes.ts +6 -0
- package/templates/src/types/tractstack.ts +17 -0
- package/templates/src/utils/actions/lispLexer.ts +2 -2
- package/templates/src/utils/actions/preParse_Action.ts +3 -0
- package/templates/src/utils/api/beliefHelpers.ts +12 -36
- package/templates/src/utils/api/menuHelpers.ts +2 -2
- package/templates/src/utils/api.ts +26 -0
- package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
- package/templates/src/utils/compositor/allowInsert.ts +5 -3
- package/templates/src/utils/compositor/nodesHelper.ts +4 -0
- package/templates/src/utils/compositor/processMarkdown.ts +16 -2
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
- package/templates/src/utils/compositor/typeGuards.ts +1 -0
- package/templates/src/utils/customHelpers.ts +38 -0
- package/templates/src/utils/helpers.ts +2 -2
- package/templates/src/utils/layout.ts +65 -144
- package/utils/inject-files.ts +95 -18
- package/templates/src/client/analytics-events.js +0 -207
- package/templates/src/client/belief-events.js +0 -191
- package/templates/src/client/sse.js +0 -613
- package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
- package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
- package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
- package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
2
|
-
import { Combobox } from '@ark-ui/react';
|
|
1
|
+
import { useState, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import { Combobox, Portal } from '@ark-ui/react';
|
|
3
3
|
import { createListCollection } from '@ark-ui/react/collection';
|
|
4
4
|
import ChevronUpDownIcon from '@heroicons/react/24/outline/ChevronUpDownIcon';
|
|
5
5
|
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
|
|
6
6
|
import { settingsPanelStore } from '@/stores/storykeep';
|
|
7
7
|
import { tailwindClasses } from '@/utils/compositor/tailwindClasses';
|
|
8
|
+
import { useDropdownDirection } from '@/utils/helpers';
|
|
8
9
|
import type { BasePanelProps } from '@/types/compositorTypes';
|
|
9
10
|
|
|
10
11
|
// Recommended styles for button states
|
|
@@ -57,6 +58,8 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
57
58
|
const [query, setQuery] = useState('');
|
|
58
59
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
59
60
|
const [selectedStyle, setSelectedStyle] = useState<string | null>(null);
|
|
61
|
+
const comboboxRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
const { openAbove } = useDropdownDirection(comboboxRef);
|
|
60
63
|
|
|
61
64
|
if (!node?.tagName || (node.tagName !== 'a' && node.tagName !== 'button'))
|
|
62
65
|
return null;
|
|
@@ -64,7 +67,6 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
64
67
|
const currentClasses = new Set<string>();
|
|
65
68
|
const isHoverMode = settingsPanelStore.get()?.action?.endsWith('-hover');
|
|
66
69
|
|
|
67
|
-
// Get existing button or hover classes
|
|
68
70
|
if (node.buttonPayload) {
|
|
69
71
|
const classes = isHoverMode
|
|
70
72
|
? node.buttonPayload.buttonHoverClasses
|
|
@@ -76,7 +78,6 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
76
78
|
|
|
77
79
|
const styles = getFilteredStyles(showAdvanced, currentClasses);
|
|
78
80
|
|
|
79
|
-
// Create filtered collection for combobox
|
|
80
81
|
const styleCollection = useMemo(() => {
|
|
81
82
|
const filteredStyles =
|
|
82
83
|
query === ''
|
|
@@ -94,7 +95,6 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
94
95
|
});
|
|
95
96
|
}, [styles, query]);
|
|
96
97
|
|
|
97
|
-
// Get appropriate recommended styles
|
|
98
98
|
const recommendedStyles = isHoverMode ? HOVER_STYLES : BUTTON_STYLES;
|
|
99
99
|
const availableRecommendedStyles = recommendedStyles.filter(
|
|
100
100
|
(style) => !currentClasses.has(style.key)
|
|
@@ -143,7 +143,6 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
143
143
|
});
|
|
144
144
|
};
|
|
145
145
|
|
|
146
|
-
// CSS to properly style the combobox items with hover and selection
|
|
147
146
|
const comboboxItemStyles = `
|
|
148
147
|
.style-item[data-highlighted] {
|
|
149
148
|
background-color: #0891b2; /* bg-cyan-600 */
|
|
@@ -164,7 +163,7 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
164
163
|
`;
|
|
165
164
|
|
|
166
165
|
return (
|
|
167
|
-
<div className="
|
|
166
|
+
<div className="max-w-md space-y-4">
|
|
168
167
|
<style>{comboboxItemStyles}</style>
|
|
169
168
|
|
|
170
169
|
<div className="flex flex-row flex-nowrap justify-between">
|
|
@@ -199,46 +198,57 @@ const StyleLinkPanelAdd = ({ node }: BasePanelProps) => {
|
|
|
199
198
|
loopFocus={true}
|
|
200
199
|
openOnKeyPress={true}
|
|
201
200
|
composite={true}
|
|
201
|
+
positioning={{
|
|
202
|
+
placement: openAbove ? 'top' : 'bottom',
|
|
203
|
+
gutter: 4,
|
|
204
|
+
sameWidth: true,
|
|
205
|
+
}}
|
|
202
206
|
>
|
|
203
|
-
<
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<Combobox.Trigger className="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
210
|
-
<ChevronUpDownIcon
|
|
211
|
-
className="text-mydarkgrey h-5 w-5"
|
|
212
|
-
aria-hidden="true"
|
|
207
|
+
<Combobox.Control ref={comboboxRef}>
|
|
208
|
+
<div className="relative">
|
|
209
|
+
<Combobox.Input
|
|
210
|
+
className="border-mydarkgrey focus:border-myblue focus:ring-myblue w-full rounded-md py-2 pl-3 pr-10 text-xl shadow-sm"
|
|
211
|
+
placeholder="Search styles..."
|
|
212
|
+
autoComplete="off"
|
|
213
213
|
/>
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
<Combobox.Trigger className="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
215
|
+
<ChevronUpDownIcon
|
|
216
|
+
className="text-mydarkgrey h-5 w-5"
|
|
217
|
+
aria-hidden="true"
|
|
218
|
+
/>
|
|
219
|
+
</Combobox.Trigger>
|
|
220
|
+
</div>
|
|
221
|
+
</Combobox.Control>
|
|
216
222
|
|
|
217
|
-
<
|
|
218
|
-
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
223
|
+
<Portal>
|
|
224
|
+
<Combobox.Positioner style={{ zIndex: 1002 }}>
|
|
225
|
+
<Combobox.Content className="absolute z-50 mt-1 max-h-64 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
226
|
+
{styleCollection.items.length === 0 ? (
|
|
227
|
+
<div className="text-mydarkgrey relative cursor-default select-none px-4 py-2">
|
|
228
|
+
Nothing found.
|
|
229
|
+
</div>
|
|
230
|
+
) : (
|
|
231
|
+
styleCollection.items.map((style) => (
|
|
232
|
+
<Combobox.Item
|
|
233
|
+
key={style.key}
|
|
234
|
+
item={style}
|
|
235
|
+
className="style-item relative cursor-default select-none py-2 pl-10 pr-4 text-black"
|
|
236
|
+
>
|
|
237
|
+
<span className="block truncate">
|
|
238
|
+
{style.title}
|
|
239
|
+
<span className="ml-2 text-sm opacity-60">
|
|
240
|
+
{style.className}
|
|
241
|
+
</span>
|
|
242
|
+
</span>
|
|
243
|
+
<span className="style-indicator absolute inset-y-0 left-0 flex items-center pl-3 text-cyan-600">
|
|
244
|
+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
|
245
|
+
</span>
|
|
246
|
+
</Combobox.Item>
|
|
247
|
+
))
|
|
248
|
+
)}
|
|
249
|
+
</Combobox.Content>
|
|
250
|
+
</Combobox.Positioner>
|
|
251
|
+
</Portal>
|
|
242
252
|
</Combobox.Root>
|
|
243
253
|
</div>
|
|
244
254
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { settingsPanelStore, fullContentMapStore } from '@/stores/storykeep';
|
|
3
3
|
import { getCtx } from '@/stores/nodes';
|
|
4
4
|
import { cloneDeep } from '@/utils/helpers';
|
|
@@ -6,10 +6,9 @@ import { lispLexer } from '@/utils/actions/lispLexer';
|
|
|
6
6
|
import { preParseAction } from '@/utils/actions/preParse_Action';
|
|
7
7
|
import { preParseBunny } from '@/utils/actions/preParse_Bunny';
|
|
8
8
|
import ActionBuilderField from '@/components/form/ActionBuilderField';
|
|
9
|
-
import BunnyMomentSelector from '@/components/fields/BunnyMomentSelector';
|
|
10
9
|
import { GOTO_TARGETS } from '@/constants';
|
|
11
10
|
import type { BrandConfig } from '@/types/tractstack';
|
|
12
|
-
import type { FlatNode } from '@/types/compositorTypes';
|
|
11
|
+
import type { FlatNode, LispToken } from '@/types/compositorTypes';
|
|
13
12
|
|
|
14
13
|
interface StyleLinkConfigPanelProps {
|
|
15
14
|
node: FlatNode;
|
|
@@ -23,7 +22,6 @@ const StyleLinkConfigPanel = ({ node, config }: StyleLinkConfigPanelProps) => {
|
|
|
23
22
|
|
|
24
23
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
25
24
|
const [callbackPayload, setCallbackPayload] = useState('');
|
|
26
|
-
const [actionType, setActionType] = useState<'goto' | 'bunnyMoment'>('goto');
|
|
27
25
|
|
|
28
26
|
const ctx = getCtx();
|
|
29
27
|
const allNodes = ctx.allNodes.get();
|
|
@@ -42,122 +40,143 @@ const StyleLinkConfigPanel = ({ node, config }: StyleLinkConfigPanelProps) => {
|
|
|
42
40
|
useEffect(() => {
|
|
43
41
|
const currentPayload = node.buttonPayload?.callbackPayload || '';
|
|
44
42
|
setCallbackPayload(currentPayload);
|
|
45
|
-
setActionType(
|
|
46
|
-
currentPayload.startsWith('(bunnyMoment') ? 'bunnyMoment' : 'goto'
|
|
47
|
-
);
|
|
48
43
|
setIsInitialized(true);
|
|
49
44
|
}, [node]);
|
|
50
45
|
|
|
51
|
-
const updateNode = (
|
|
46
|
+
const updateNode = useCallback(() => {
|
|
52
47
|
try {
|
|
53
48
|
const linkNode = cloneDeep(allNodes.get(node.id)) as FlatNode;
|
|
54
|
-
if (!linkNode || !markdownId)
|
|
55
|
-
|
|
56
|
-
const lexedPayload = lispLexer(newCallbackPayload);
|
|
57
|
-
let targetUrl = null;
|
|
58
|
-
if (newCallbackPayload.startsWith('(goto')) {
|
|
59
|
-
targetUrl =
|
|
60
|
-
lexedPayload && preParseAction(lexedPayload, slug, isContext, config);
|
|
49
|
+
if (!linkNode || !markdownId) {
|
|
50
|
+
return;
|
|
61
51
|
}
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
buttonClasses: {},
|
|
67
|
-
buttonHoverClasses: {},
|
|
68
|
-
callbackPayload: '',
|
|
52
|
+
|
|
53
|
+
const existingButtonPayload = linkNode.buttonPayload;
|
|
54
|
+
const newButtonPayload: FlatNode['buttonPayload'] = {
|
|
55
|
+
callbackPayload: callbackPayload,
|
|
56
|
+
buttonClasses: existingButtonPayload?.buttonClasses || {},
|
|
57
|
+
buttonHoverClasses: existingButtonPayload?.buttonHoverClasses || {},
|
|
69
58
|
};
|
|
70
59
|
|
|
71
|
-
|
|
60
|
+
const [tokens] = lispLexer(callbackPayload);
|
|
61
|
+
delete linkNode.href;
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
callbackPayload.startsWith('(declare') ||
|
|
65
|
+
callbackPayload.startsWith('(identifyAs')
|
|
66
|
+
) {
|
|
72
67
|
linkNode.tagName = 'button';
|
|
73
|
-
|
|
68
|
+
} else if (callbackPayload.startsWith('(bunnyMoment')) {
|
|
69
|
+
linkNode.tagName = 'button';
|
|
70
|
+
newButtonPayload.bunnyPayload =
|
|
71
|
+
(tokens && preParseBunny([tokens])) ?? undefined;
|
|
72
|
+
} else if (callbackPayload.startsWith('(goto')) {
|
|
73
|
+
const targetUrl =
|
|
74
|
+
tokens && preParseAction([tokens], slug, isContext, config);
|
|
75
|
+
const isExternalUrl =
|
|
76
|
+
typeof targetUrl === 'string' && targetUrl.startsWith('https://');
|
|
77
|
+
|
|
78
|
+
linkNode.href = targetUrl || '#';
|
|
79
|
+
linkNode.tagName = !targetUrl ? 'button' : 'a';
|
|
80
|
+
if (isExternalUrl) {
|
|
81
|
+
newButtonPayload.isExternalUrl = true;
|
|
82
|
+
}
|
|
74
83
|
} else {
|
|
75
|
-
linkNode.
|
|
76
|
-
linkNode.tagName = !targetUrl || bunnyPayload ? 'button' : 'a';
|
|
84
|
+
linkNode.tagName = 'button';
|
|
77
85
|
}
|
|
78
86
|
|
|
79
|
-
linkNode.buttonPayload =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
linkNode.buttonPayload = newButtonPayload;
|
|
88
|
+
|
|
89
|
+
const currentSignal = settingsPanelStore.get();
|
|
90
|
+
if (currentSignal) {
|
|
91
|
+
settingsPanelStore.set({
|
|
92
|
+
...currentSignal,
|
|
93
|
+
editLock: Date.now(),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
87
96
|
|
|
88
97
|
ctx.modifyNodes([{ ...linkNode, isChanged: true }]);
|
|
89
98
|
} catch (error) {
|
|
90
|
-
console.error('Error
|
|
99
|
+
console.error('Error in updateNode:', error);
|
|
91
100
|
}
|
|
92
|
-
}
|
|
101
|
+
}, [
|
|
102
|
+
allNodes,
|
|
103
|
+
config,
|
|
104
|
+
ctx,
|
|
105
|
+
isContext,
|
|
106
|
+
markdownId,
|
|
107
|
+
node.id,
|
|
108
|
+
slug,
|
|
109
|
+
callbackPayload,
|
|
110
|
+
]);
|
|
93
111
|
|
|
94
112
|
useEffect(() => {
|
|
95
|
-
if (!isInitialized)
|
|
96
|
-
|
|
97
|
-
if (callbackPayload.startsWith('(bunnyMoment')) {
|
|
98
|
-
const match = callbackPayload.match(
|
|
99
|
-
/\(bunnyMoment\s+\(\s*([^\s]+)\s+(\d+)\s*\)\)/
|
|
100
|
-
);
|
|
101
|
-
if (match && match[1] && match[2]) {
|
|
102
|
-
updateNode(callbackPayload);
|
|
103
|
-
}
|
|
113
|
+
if (!isInitialized) {
|
|
104
114
|
return;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
|
-
const match = callbackPayload.match(/\(goto\s+\(([^)]+)\)/);
|
|
108
|
-
if (!match) return;
|
|
109
|
-
|
|
110
|
-
const parts = match[1].split(' ').filter(Boolean);
|
|
111
|
-
if (parts.length === 0) return;
|
|
112
|
-
|
|
113
|
-
const target = parts[0];
|
|
114
|
-
const targetConfig = GOTO_TARGETS[target];
|
|
115
|
-
if (!targetConfig) return;
|
|
116
|
-
|
|
117
117
|
let isComplete = false;
|
|
118
118
|
|
|
119
|
-
if (
|
|
120
|
-
isComplete = parts.length > 1;
|
|
121
|
-
} else if (targetConfig.subcommands) {
|
|
122
|
-
isComplete = parts.length > 1;
|
|
123
|
-
} else if (targetConfig.requiresParam) {
|
|
124
|
-
if (targetConfig.requiresSecondParam) {
|
|
125
|
-
isComplete = parts.length > 2;
|
|
126
|
-
} else {
|
|
127
|
-
isComplete = parts.length > 1;
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
119
|
+
if (callbackPayload.trim() === '') {
|
|
130
120
|
isComplete = true;
|
|
121
|
+
} else {
|
|
122
|
+
const [tokens] = lispLexer(callbackPayload);
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
Array.isArray(tokens) &&
|
|
126
|
+
tokens.length > 0 &&
|
|
127
|
+
Array.isArray(tokens[0])
|
|
128
|
+
) {
|
|
129
|
+
const mainExpression = tokens[0] as LispToken[];
|
|
130
|
+
|
|
131
|
+
if (mainExpression.length > 1) {
|
|
132
|
+
// Must have a command AND parameters
|
|
133
|
+
const command = mainExpression[0] as string;
|
|
134
|
+
const parameters = mainExpression[1] as LispToken[];
|
|
135
|
+
|
|
136
|
+
if (Array.isArray(parameters)) {
|
|
137
|
+
if (command === 'declare' || command === 'identifyAs') {
|
|
138
|
+
if (parameters.length === 2 && parameters[0] && parameters[1]) {
|
|
139
|
+
isComplete = true;
|
|
140
|
+
}
|
|
141
|
+
} else if (command === 'bunnyMoment') {
|
|
142
|
+
if (parameters.length === 2) {
|
|
143
|
+
isComplete = true;
|
|
144
|
+
}
|
|
145
|
+
} else if (command === 'goto') {
|
|
146
|
+
const target = parameters[0] as string;
|
|
147
|
+
const targetConfig = GOTO_TARGETS[target];
|
|
148
|
+
if (targetConfig) {
|
|
149
|
+
if (target === 'url') {
|
|
150
|
+
isComplete =
|
|
151
|
+
parameters.length > 1 &&
|
|
152
|
+
String(parameters[1]).includes('.');
|
|
153
|
+
} else if (targetConfig.subcommands) {
|
|
154
|
+
isComplete = parameters.length > 1;
|
|
155
|
+
} else if (targetConfig.requiresParam) {
|
|
156
|
+
if (targetConfig.requiresSecondParam) {
|
|
157
|
+
isComplete = parameters.length > 2;
|
|
158
|
+
} else {
|
|
159
|
+
isComplete = parameters.length > 1;
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
isComplete = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
131
169
|
}
|
|
132
170
|
|
|
133
171
|
if (isComplete) {
|
|
134
|
-
updateNode(
|
|
172
|
+
updateNode();
|
|
135
173
|
}
|
|
136
|
-
}, [
|
|
137
|
-
callbackPayload,
|
|
138
|
-
isInitialized,
|
|
139
|
-
node.id,
|
|
140
|
-
config,
|
|
141
|
-
allNodes,
|
|
142
|
-
ctx,
|
|
143
|
-
markdownId,
|
|
144
|
-
slug,
|
|
145
|
-
isContext,
|
|
146
|
-
]);
|
|
174
|
+
}, [callbackPayload, isInitialized, updateNode]);
|
|
147
175
|
|
|
148
176
|
const handleChange = (value: string) => {
|
|
149
177
|
setCallbackPayload(value);
|
|
150
178
|
};
|
|
151
179
|
|
|
152
|
-
const handleActionTypeChange = (type: 'goto' | 'bunnyMoment') => {
|
|
153
|
-
setActionType(type);
|
|
154
|
-
if (type === 'bunnyMoment') {
|
|
155
|
-
setCallbackPayload('(bunnyMoment ( ))');
|
|
156
|
-
} else {
|
|
157
|
-
setCallbackPayload('');
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
180
|
const handleCloseConfig = () => {
|
|
162
181
|
settingsPanelStore.set({
|
|
163
182
|
nodeId: node.id,
|
|
@@ -166,28 +185,6 @@ const StyleLinkConfigPanel = ({ node, config }: StyleLinkConfigPanelProps) => {
|
|
|
166
185
|
});
|
|
167
186
|
};
|
|
168
187
|
|
|
169
|
-
const renderActionBuilder = () => {
|
|
170
|
-
switch (actionType) {
|
|
171
|
-
case 'bunnyMoment':
|
|
172
|
-
return (
|
|
173
|
-
<BunnyMomentSelector
|
|
174
|
-
value={callbackPayload}
|
|
175
|
-
onChange={handleChange}
|
|
176
|
-
/>
|
|
177
|
-
);
|
|
178
|
-
case 'goto':
|
|
179
|
-
default:
|
|
180
|
-
return (
|
|
181
|
-
<ActionBuilderField
|
|
182
|
-
slug={slug}
|
|
183
|
-
value={callbackPayload}
|
|
184
|
-
onChange={handleChange}
|
|
185
|
-
contentMap={fullContentMapStore.get()}
|
|
186
|
-
/>
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
188
|
return (
|
|
192
189
|
<div className="relative">
|
|
193
190
|
<div className="w-full max-w-md space-y-4">
|
|
@@ -201,36 +198,14 @@ const StyleLinkConfigPanel = ({ node, config }: StyleLinkConfigPanelProps) => {
|
|
|
201
198
|
</button>
|
|
202
199
|
</div>
|
|
203
200
|
|
|
204
|
-
<div className="mb-4 space-y-2">
|
|
205
|
-
<label className="block text-sm text-gray-700">Action Type</label>
|
|
206
|
-
<select
|
|
207
|
-
value={actionType}
|
|
208
|
-
onChange={(e) =>
|
|
209
|
-
handleActionTypeChange(e.target.value as 'goto' | 'bunnyMoment')
|
|
210
|
-
}
|
|
211
|
-
className="mt-1 block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
|
|
212
|
-
>
|
|
213
|
-
<option value="goto">Navigation Action</option>
|
|
214
|
-
<option value="bunnyMoment">Video Moment Action</option>
|
|
215
|
-
</select>
|
|
216
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
217
|
-
{actionType === 'goto'
|
|
218
|
-
? 'Create a link to navigate to another page or section'
|
|
219
|
-
: 'Jump to a specific moment in a video on this page'}
|
|
220
|
-
</p>
|
|
221
|
-
</div>
|
|
222
|
-
|
|
223
201
|
<div className="space-y-2">
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
{renderActionBuilder()}
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
202
|
+
<ActionBuilderField
|
|
203
|
+
slug={slug}
|
|
204
|
+
value={callbackPayload}
|
|
205
|
+
onChange={handleChange}
|
|
206
|
+
contentMap={fullContentMapStore.get()}
|
|
207
|
+
label="Action Configuration"
|
|
208
|
+
/>
|
|
234
209
|
</div>
|
|
235
210
|
</div>
|
|
236
211
|
</div>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
2
|
-
import { Combobox } from '@ark-ui/react';
|
|
1
|
+
import { useState, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import { Combobox, Portal } from '@ark-ui/react';
|
|
3
3
|
import { createListCollection } from '@ark-ui/react/collection';
|
|
4
4
|
import ChevronUpDownIcon from '@heroicons/react/24/outline/ChevronUpDownIcon';
|
|
5
5
|
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
|
|
6
6
|
import { settingsPanelStore } from '@/stores/storykeep';
|
|
7
7
|
import { tailwindClasses } from '@/utils/compositor/tailwindClasses';
|
|
8
8
|
import { isMarkdownPaneFragmentNode } from '@/utils/compositor/typeGuards';
|
|
9
|
+
import { useDropdownDirection } from '@/utils/helpers';
|
|
9
10
|
import type { BasePanelProps, PaneFragmentNode } from '@/types/compositorTypes';
|
|
10
11
|
|
|
11
12
|
const RECOMMENDED_STYLES = [
|
|
@@ -57,6 +58,8 @@ const StyleParentPanelAdd = ({ node, layer }: BasePanelProps) => {
|
|
|
57
58
|
const [query, setQuery] = useState('');
|
|
58
59
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
59
60
|
const [selectedStyle, setSelectedStyle] = useState<string | null>(null);
|
|
61
|
+
const comboboxRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
const { openAbove } = useDropdownDirection(comboboxRef);
|
|
60
63
|
|
|
61
64
|
const paneFragmentNode = node as PaneFragmentNode | null;
|
|
62
65
|
|
|
@@ -174,7 +177,7 @@ const StyleParentPanelAdd = ({ node, layer }: BasePanelProps) => {
|
|
|
174
177
|
`;
|
|
175
178
|
|
|
176
179
|
return (
|
|
177
|
-
<div className="
|
|
180
|
+
<div className="max-w-md space-y-4">
|
|
178
181
|
<style>{comboboxItemStyles}</style>
|
|
179
182
|
|
|
180
183
|
<div className="flex flex-row flex-nowrap justify-between">
|
|
@@ -209,46 +212,57 @@ const StyleParentPanelAdd = ({ node, layer }: BasePanelProps) => {
|
|
|
209
212
|
loopFocus={true}
|
|
210
213
|
openOnKeyPress={true}
|
|
211
214
|
composite={true}
|
|
215
|
+
positioning={{
|
|
216
|
+
placement: openAbove ? 'top' : 'bottom',
|
|
217
|
+
gutter: 4,
|
|
218
|
+
sameWidth: true,
|
|
219
|
+
}}
|
|
212
220
|
>
|
|
213
|
-
<
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<Combobox.Trigger className="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
220
|
-
<ChevronUpDownIcon
|
|
221
|
-
className="text-mydarkgrey h-5 w-5"
|
|
222
|
-
aria-hidden="true"
|
|
221
|
+
<Combobox.Control ref={comboboxRef}>
|
|
222
|
+
<div className="relative">
|
|
223
|
+
<Combobox.Input
|
|
224
|
+
className="border-mydarkgrey focus:border-myblue focus:ring-myblue w-full rounded-md py-2 pl-3 pr-10 text-xl shadow-sm"
|
|
225
|
+
placeholder="Search styles..."
|
|
226
|
+
autoComplete="off"
|
|
223
227
|
/>
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
<Combobox.Trigger className="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
229
|
+
<ChevronUpDownIcon
|
|
230
|
+
className="text-mydarkgrey h-5 w-5"
|
|
231
|
+
aria-hidden="true"
|
|
232
|
+
/>
|
|
233
|
+
</Combobox.Trigger>
|
|
234
|
+
</div>
|
|
235
|
+
</Combobox.Control>
|
|
226
236
|
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
237
|
+
<Portal>
|
|
238
|
+
<Combobox.Positioner style={{ zIndex: 1002 }}>
|
|
239
|
+
<Combobox.Content className="max-h-64 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
240
|
+
{collection.items.length === 0 ? (
|
|
241
|
+
<div className="text-mydarkgrey relative cursor-default select-none px-4 py-2">
|
|
242
|
+
Nothing found.
|
|
243
|
+
</div>
|
|
244
|
+
) : (
|
|
245
|
+
collection.items.map((style) => (
|
|
246
|
+
<Combobox.Item
|
|
247
|
+
key={style.key}
|
|
248
|
+
item={style}
|
|
249
|
+
className="style-item relative cursor-default select-none py-2 pl-10 pr-4 text-black"
|
|
250
|
+
>
|
|
251
|
+
<span className="block truncate">
|
|
252
|
+
{style.title}
|
|
253
|
+
<span className="ml-2 text-sm opacity-60">
|
|
254
|
+
{style.className}
|
|
255
|
+
</span>
|
|
256
|
+
</span>
|
|
257
|
+
<span className="style-indicator absolute inset-y-0 left-0 flex items-center pl-3 text-cyan-600">
|
|
258
|
+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
|
259
|
+
</span>
|
|
260
|
+
</Combobox.Item>
|
|
261
|
+
))
|
|
262
|
+
)}
|
|
263
|
+
</Combobox.Content>
|
|
264
|
+
</Combobox.Positioner>
|
|
265
|
+
</Portal>
|
|
252
266
|
</Combobox.Root>
|
|
253
267
|
</div>
|
|
254
268
|
|
|
@@ -4,7 +4,8 @@ import { settingsPanelStore } from '@/stores/storykeep';
|
|
|
4
4
|
import { StylesMemory } from '@/components/edit/state/StylesMemory';
|
|
5
5
|
import SelectedTailwindClass from '@/components/fields/SelectedTailwindClass';
|
|
6
6
|
import { isMarkdownPaneFragmentNode } from '@/utils/compositor/typeGuards';
|
|
7
|
-
import { widgetMeta } from '@/constants';
|
|
7
|
+
import { regexpHook, widgetMeta } from '@/constants';
|
|
8
|
+
import { getCtx } from '@/stores/nodes';
|
|
8
9
|
import type {
|
|
9
10
|
FlatNode,
|
|
10
11
|
MarkdownPaneFragmentNode,
|
|
@@ -41,8 +42,6 @@ const StyleWidgetPanel = ({
|
|
|
41
42
|
const outerOverrideClasses = outerContainerNode.overrideClasses;
|
|
42
43
|
|
|
43
44
|
// Extract the widget type from the node's copy
|
|
44
|
-
const regexpHook =
|
|
45
|
-
/^(identifyAs|youtube|bunny|bunnyContext|toggle|resource|belief|signup)\((.*)\)$/;
|
|
46
45
|
const hookMatch = node.copy?.match(regexpHook);
|
|
47
46
|
const widgetId = hookMatch ? hookMatch[1] : 'unknown';
|
|
48
47
|
const widgetName = widgetMeta[widgetId]?.title || `Widget`;
|
|
@@ -271,6 +270,7 @@ const StyleWidgetPanel = ({
|
|
|
271
270
|
};
|
|
272
271
|
|
|
273
272
|
const handleWidgetConfig = () => {
|
|
273
|
+
getCtx().toolModeValStore.set({ value: 'styles' });
|
|
274
274
|
settingsPanelStore.set({
|
|
275
275
|
action: `style-code-config`,
|
|
276
276
|
nodeId: node.id,
|