astro-tractstack 2.0.0-rc.60 → 2.0.0-rc.62

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 (32) hide show
  1. package/dist/index.js +12 -0
  2. package/package.json +1 -1
  3. package/templates/src/components/Menu.tsx +192 -52
  4. package/templates/src/components/compositor/Node.tsx +1 -4
  5. package/templates/src/components/compositor/nodes/Pane.tsx +1 -1
  6. package/templates/src/components/compositor/nodes/Widget.tsx +15 -1
  7. package/templates/src/components/edit/PanelSwitch.tsx +2 -1
  8. package/templates/src/components/edit/SettingsPanel.tsx +1 -1
  9. package/templates/src/components/edit/ToolBar.tsx +1 -28
  10. package/templates/src/components/edit/ToolMode.tsx +5 -52
  11. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
  12. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +1 -3
  13. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +12 -5
  14. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +469 -0
  15. package/templates/src/components/fields/ColorPickerCombo.tsx +4 -14
  16. package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
  17. package/templates/src/components/form/ActionBuilderField.tsx +289 -89
  18. package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
  19. package/templates/src/constants.ts +2120 -15
  20. package/templates/src/layouts/Layout.astro +5 -0
  21. package/templates/src/pages/[...slug]/edit.astro +3 -10
  22. package/templates/src/pages/context/[...contextSlug]/edit.astro +1 -2
  23. package/templates/src/stores/nodes.ts +0 -51
  24. package/templates/src/types/compositorTypes.ts +5 -0
  25. package/templates/src/utils/actions/lispLexer.ts +2 -2
  26. package/templates/src/utils/api/menuHelpers.ts +2 -2
  27. package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
  28. package/templates/src/utils/compositor/allowInsert.ts +5 -3
  29. package/templates/src/utils/compositor/nodesHelper.ts +4 -0
  30. package/templates/src/utils/compositor/typeGuards.ts +1 -0
  31. package/templates/src/utils/layout.ts +0 -29
  32. package/utils/inject-files.ts +12 -0
package/dist/index.js CHANGED
@@ -229,6 +229,12 @@ async function w(t, e, c) {
229
229
  src: t("../templates/src/components/edit/widgets/BeliefWidget.tsx"),
230
230
  dest: "src/components/edit/widgets/BeliefWidget.tsx"
231
231
  },
232
+ {
233
+ src: t(
234
+ "../templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx"
235
+ ),
236
+ dest: "src/components/edit/widgets/InteractiveDisclosureWidget.tsx"
237
+ },
232
238
  {
233
239
  src: t(
234
240
  "../templates/src/components/edit/widgets/IdentifyAsWidget.tsx"
@@ -988,6 +994,12 @@ async function w(t, e, c) {
988
994
  src: t("../templates/src/components/form/ActionBuilderField.tsx"),
989
995
  dest: "src/components/form/ActionBuilderField.tsx"
990
996
  },
997
+ {
998
+ src: t(
999
+ "../templates/src/components/form/ActionBuilderBeliefSelector.tsx"
1000
+ ),
1001
+ dest: "src/components/form/ActionBuilderBeliefSelector.tsx"
1002
+ },
991
1003
  {
992
1004
  src: t("../templates/src/components/form/MagicPathBuilder.tsx"),
993
1005
  dest: "src/components/form/MagicPathBuilder.tsx"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.0-rc.60",
3
+ "version": "2.0.0-rc.62",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -2,7 +2,8 @@ import { Menu } from '@ark-ui/react';
2
2
  import { Portal } from '@ark-ui/react/portal';
3
3
  import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon';
4
4
  import { lispLexer } from '@/utils/actions/lispLexer';
5
- import { preParseAction } from '@/utils/actions//preParse_Action';
5
+ import { preParseAction } from '@/utils/actions/preParse_Action';
6
+ import type { LispToken } from '@/types/compositorTypes';
6
7
 
7
8
  // CSS to style the menu items with hover and selection states
8
9
  const menuStyles = `
@@ -46,9 +47,10 @@ interface MenuDatum {
46
47
  optionsPayload: MenuLink[];
47
48
  }
48
49
 
49
- interface MenuLinkDatum extends MenuLink {
50
- to: string;
51
- internal: boolean;
50
+ interface ProcessedMenuLinkDatum extends MenuLink {
51
+ renderAs: 'a' | 'button' | 'span';
52
+ href?: string;
53
+ htmxVals?: string;
52
54
  }
53
55
 
54
56
  interface MenuProps {
@@ -62,7 +64,77 @@ const MenuComponent = (props: MenuProps) => {
62
64
  const { payload, slug, isContext, brandConfig } = props;
63
65
  const thisPayload = payload.optionsPayload;
64
66
 
65
- // Process featured and additional links
67
+ // Helper function to process menu links - MODIFIED to build the correct hx-vals payload
68
+ function processMenuLink(e: MenuLink): ProcessedMenuLinkDatum {
69
+ const item = { ...e } as ProcessedMenuLinkDatum;
70
+ const actionLisp = item.actionLisp?.trim();
71
+
72
+ if (!actionLisp) {
73
+ item.renderAs = 'span';
74
+ return item;
75
+ }
76
+
77
+ try {
78
+ if (actionLisp.startsWith('(goto')) {
79
+ const tokens = lispLexer(actionLisp);
80
+ const to = preParseAction(tokens, slug, isContext, brandConfig);
81
+ item.renderAs = 'a';
82
+ item.href = to || '#';
83
+ return item;
84
+ }
85
+
86
+ if (
87
+ actionLisp.startsWith('(declare') ||
88
+ actionLisp.startsWith('(identifyAs')
89
+ ) {
90
+ const tokens = lispLexer(actionLisp);
91
+ const commandExpression = (
92
+ tokens?.[0] as LispToken[]
93
+ )?.[0] as LispToken[];
94
+ const command = commandExpression?.[0] as string;
95
+ const parameters = commandExpression?.[1] as (string | number)[];
96
+ const beliefId = parameters?.[0];
97
+ const value = parameters?.[1];
98
+
99
+ if (command && beliefId !== undefined && value !== undefined) {
100
+ let hxValsMap: { [key: string]: string } = {};
101
+
102
+ // CORRECTED: Build the hx-vals payload to match server expectations.
103
+ if (command === 'declare') {
104
+ hxValsMap = {
105
+ beliefId: String(beliefId),
106
+ beliefType: 'Belief', // This was the missing required field.
107
+ beliefValue: String(value), // Key changed from beliefVerb to beliefValue.
108
+ };
109
+ } else if (command === 'identifyAs') {
110
+ hxValsMap = {
111
+ beliefId: String(beliefId),
112
+ beliefType: 'Belief', // This was the missing required field.
113
+ beliefVerb: 'IDENTIFY_AS', // This is specific to identifyAs.
114
+ beliefObject: String(value),
115
+ };
116
+ }
117
+
118
+ if (Object.keys(hxValsMap).length > 0) {
119
+ item.renderAs = 'button';
120
+ item.htmxVals = JSON.stringify(hxValsMap);
121
+ return item;
122
+ }
123
+ }
124
+ }
125
+ } catch (error) {
126
+ console.error(
127
+ `Failed to process menu item for action: ${actionLisp}`,
128
+ error
129
+ );
130
+ }
131
+
132
+ // Fallback for unknown commands or parsing failures
133
+ item.renderAs = 'span';
134
+ return item;
135
+ }
136
+
137
+ // Process featured and additional links using the modified helper
66
138
  const featuredLinks = thisPayload
67
139
  .filter((e: MenuLink) => e.featured)
68
140
  .map(processMenuLink);
@@ -70,19 +142,48 @@ const MenuComponent = (props: MenuProps) => {
70
142
  .filter((e: MenuLink) => !e.featured)
71
143
  .map(processMenuLink);
72
144
 
73
- // Helper function to process menu links
74
- function processMenuLink(e: MenuLink): MenuLinkDatum {
75
- const item = { ...e } as MenuLinkDatum;
76
- const thisPayload = lispLexer(e.actionLisp);
77
- const to = preParseAction(thisPayload, slug, isContext, brandConfig);
78
- if (typeof to === `string`) {
79
- item.to = to;
80
- item.internal = true;
81
- } else if (typeof to === `object`) {
82
- item.to = to[0];
145
+ // Helper component to render either a link or a button, avoiding repetition.
146
+ const InteractiveMenuItem = ({ item }: { item: ProcessedMenuLinkDatum }) => {
147
+ if (item.renderAs === 'button') {
148
+ return (
149
+ <button
150
+ type="button"
151
+ className="text-mydarkgrey focus:ring-myblue block text-2xl font-bold leading-6 hover:text-black hover:underline hover:decoration-dashed hover:decoration-4 hover:underline-offset-4 focus:text-black focus:outline-none focus:ring-2"
152
+ title={item.description}
153
+ aria-label={`${item.name} - ${item.description}`}
154
+ hx-post="/api/v1/state"
155
+ hx-swap="none"
156
+ hx-vals={item.htmxVals}
157
+ >
158
+ {item.name}
159
+ </button>
160
+ );
161
+ }
162
+
163
+ if (item.renderAs === 'a') {
164
+ return (
165
+ <a
166
+ href={item.href}
167
+ className="text-mydarkgrey focus:ring-myblue block text-2xl font-bold leading-6 hover:text-black hover:underline hover:decoration-dashed hover:decoration-4 hover:underline-offset-4 focus:text-black focus:outline-none focus:ring-2"
168
+ title={item.description}
169
+ aria-label={`${item.name} - ${item.description}`}
170
+ >
171
+ {item.name}
172
+ </a>
173
+ );
83
174
  }
84
- return item;
85
- }
175
+
176
+ // Fallback for 'span'
177
+ return (
178
+ <span
179
+ className="text-mydarkgrey block text-2xl font-bold leading-6 opacity-50"
180
+ title={item.description}
181
+ aria-label={`${item.name} - ${item.description}`}
182
+ >
183
+ {item.name}
184
+ </span>
185
+ );
186
+ };
86
187
 
87
188
  return (
88
189
  <>
@@ -90,16 +191,9 @@ const MenuComponent = (props: MenuProps) => {
90
191
 
91
192
  {/* Desktop Navigation */}
92
193
  <nav className="font-action ml-6 hidden flex-wrap items-center justify-end space-x-3 md:flex md:space-x-6">
93
- {featuredLinks.map((item: MenuLinkDatum) => (
194
+ {featuredLinks.map((item: ProcessedMenuLinkDatum) => (
94
195
  <div key={item.name} className="relative py-1.5">
95
- <a
96
- href={item.to}
97
- className="text-mydarkgrey focus:ring-myblue block text-2xl font-bold leading-6 hover:text-black hover:underline hover:decoration-dashed hover:decoration-4 hover:underline-offset-4 focus:text-black focus:outline-none focus:ring-2"
98
- title={item.description}
99
- aria-label={`${item.name} - ${item.description}`}
100
- >
101
- {item.name}
102
- </a>
196
+ <InteractiveMenuItem item={item} />
103
197
  </div>
104
198
  ))}
105
199
  </nav>
@@ -122,21 +216,42 @@ const MenuComponent = (props: MenuProps) => {
122
216
  <div className="text-md ring-mydarkgrey/5 flex-auto overflow-hidden rounded-3xl bg-white p-4 leading-6 shadow-lg ring-1">
123
217
  {/* Featured Links Section */}
124
218
  <div className="px-8">
125
- {featuredLinks.map((item: MenuLinkDatum) => (
219
+ {featuredLinks.map((item: ProcessedMenuLinkDatum) => (
126
220
  <Menu.Item
127
221
  key={item.name}
128
222
  value={item.name}
129
223
  className="menu-item hover:bg-mygreen/20 group relative flex gap-x-6 rounded-lg p-4"
130
224
  >
131
225
  <div>
132
- <a
133
- href={item.to}
134
- className="font-action text-myblack text-xl hover:text-black focus:text-black focus:outline-none"
135
- aria-label={`${item.name} - ${item.description}`}
136
- >
137
- {item.name}
138
- <span className="absolute inset-0" />
139
- </a>
226
+ {item.renderAs === 'button' ? (
227
+ <button
228
+ type="button"
229
+ className="font-action text-myblack text-xl hover:text-black focus:text-black focus:outline-none"
230
+ aria-label={`${item.name} - ${item.description}`}
231
+ hx-post="/api/v1/state"
232
+ hx-swap="none"
233
+ hx-vals={item.htmxVals}
234
+ >
235
+ {item.name}
236
+ <span className="absolute inset-0" />
237
+ </button>
238
+ ) : item.renderAs === 'a' ? (
239
+ <a
240
+ href={item.href}
241
+ className="font-action text-myblack text-xl hover:text-black focus:text-black focus:outline-none"
242
+ aria-label={`${item.name} - ${item.description}`}
243
+ >
244
+ {item.name}
245
+ <span className="absolute inset-0" />
246
+ </a>
247
+ ) : (
248
+ <span
249
+ className="font-action text-myblack text-xl opacity-50"
250
+ aria-label={`${item.name} - ${item.description}`}
251
+ >
252
+ {item.name}
253
+ </span>
254
+ )}
140
255
  <p className="text-mydarkgrey mt-1">
141
256
  {item.description}
142
257
  </p>
@@ -161,24 +276,49 @@ const MenuComponent = (props: MenuProps) => {
161
276
  className="mt-6 space-y-6"
162
277
  aria-labelledby="additional-links-heading"
163
278
  >
164
- {additionalLinks.map((item: MenuLinkDatum) => (
165
- <li key={item.name} className="relative">
166
- <Menu.Item
167
- value={item.name}
168
- className="menu-item block w-full text-left"
169
- >
170
- <a
171
- href={item.to}
172
- className="text-mydarkgrey block truncate rounded p-2 text-sm font-bold leading-6 hover:text-black focus:text-black focus:underline focus:outline-none"
173
- title={item.description}
174
- aria-label={`${item.name} - ${item.description}`}
279
+ {additionalLinks.map(
280
+ (item: ProcessedMenuLinkDatum) => (
281
+ <li key={item.name} className="relative">
282
+ <Menu.Item
283
+ value={item.name}
284
+ className="menu-item block w-full text-left"
175
285
  >
176
- {item.name}
177
- <span className="absolute inset-0" />
178
- </a>
179
- </Menu.Item>
180
- </li>
181
- ))}
286
+ {item.renderAs === 'button' ? (
287
+ <button
288
+ type="button"
289
+ className="text-mydarkgrey block truncate rounded p-2 text-sm font-bold leading-6 hover:text-black focus:text-black focus:underline focus:outline-none"
290
+ title={item.description}
291
+ aria-label={`${item.name} - ${item.description}`}
292
+ hx-post="/api/v1/state"
293
+ hx-swap="none"
294
+ hx-vals={item.htmxVals}
295
+ >
296
+ {item.name}
297
+ <span className="absolute inset-0" />
298
+ </button>
299
+ ) : item.renderAs === 'a' ? (
300
+ <a
301
+ href={item.href}
302
+ className="text-mydarkgrey block truncate rounded p-2 text-sm font-bold leading-6 hover:text-black focus:text-black focus:underline focus:outline-none"
303
+ title={item.description}
304
+ aria-label={`${item.name} - ${item.description}`}
305
+ >
306
+ {item.name}
307
+ <span className="absolute inset-0" />
308
+ </a>
309
+ ) : (
310
+ <span
311
+ className="text-mydarkgrey block truncate rounded p-2 text-sm font-bold leading-6 opacity-50"
312
+ title={item.description}
313
+ aria-label={`${item.name} - ${item.description}`}
314
+ >
315
+ {item.name}
316
+ </span>
317
+ )}
318
+ </Menu.Item>
319
+ </li>
320
+ )
321
+ )}
182
322
  </ul>
183
323
  </div>
184
324
  )}
@@ -30,6 +30,7 @@ import StoryFragmentConfigPanel from '@/components/edit/storyfragment/StoryFragm
30
30
  import StoryFragmentTitlePanel from '@/components/edit/storyfragment/StoryFragmentPanel_title';
31
31
  import ContextPanePanel from '@/components/edit/context/ContextPaneConfig';
32
32
  import ContextPaneTitlePanel from '@/components/edit/context/ContextPaneConfig_title';
33
+ import { regexpHook } from '@/constants';
33
34
  import type {
34
35
  StoryFragmentNode,
35
36
  PaneNode,
@@ -40,8 +41,6 @@ import type { NodeProps } from '@/types/nodeProps';
40
41
 
41
42
  function parseCodeHook(node: BaseNode | FlatNode) {
42
43
  if ('codeHookParams' in node && Array.isArray(node.codeHookParams)) {
43
- const regexpHook =
44
- /^(identifyAs|youtube|bunny|bunnyContext|toggle|resource|belief|signup)\((.*)\)$/;
45
44
  const hookMatch = node.copy?.match(regexpHook);
46
45
 
47
46
  if (!hookMatch) return null;
@@ -58,8 +57,6 @@ function parseCodeHook(node: BaseNode | FlatNode) {
58
57
  const firstChild = node.children[0];
59
58
  if (!firstChild?.value) return null;
60
59
 
61
- const regexpHook =
62
- /(identifyAs|youtube|bunny|bunnyContext|toggle|resource|belief|signup)\((.*?)\)/;
63
60
  const regexpValues = /((?:[^\\|]+|\\\|?)+)/g;
64
61
  const hookMatch = firstChild.value.match(regexpHook);
65
62
 
@@ -32,7 +32,7 @@ const CodeHookContainer = ({
32
32
  <Fragment key={key}>
33
33
  <span className="min-w-24 font-bold text-gray-600">{key}:</span>
34
34
  <div className="ml-2 flex flex-wrap gap-1">
35
- {value.split('|').map((item, index) => (
35
+ {value.split(/,|\|/).map((item, index) => (
36
36
  <span
37
37
  key={index}
38
38
  className="inline-block rounded bg-gray-200 px-2 py-0.5 text-xs text-gray-800"
@@ -83,8 +83,22 @@ const getWidgetElement = (
83
83
  </div>
84
84
  ) : null;
85
85
 
86
+ case 'interactiveDisclosure':
87
+ return (
88
+ <div className={`${classNames} pointer-events-none`}>
89
+ <div className="rounded-md border-2 border-dashed border-gray-300 bg-gray-50 p-4 text-center">
90
+ <p className="text-sm font-bold text-gray-700">
91
+ Interactive Disclosure
92
+ </p>
93
+ <p className="mt-1 text-xs text-gray-500">
94
+ Belief Trigger: <code className="font-bold">{value1}</code>
95
+ </p>
96
+ </div>
97
+ </div>
98
+ );
99
+
86
100
  default:
87
- return null;
101
+ return <div>Widget {hook} not found.</div>;
88
102
  }
89
103
  };
90
104
 
@@ -313,7 +313,8 @@ const PanelSwitch = ({
313
313
  break;
314
314
 
315
315
  case 'style-code-config':
316
- if (clickedNode) return <StyleWidgetConfigPanel node={clickedNode} />;
316
+ if (clickedNode)
317
+ return <StyleWidgetConfigPanel node={clickedNode} config={config} />;
317
318
  break;
318
319
 
319
320
  case 'style-code-add':
@@ -33,7 +33,7 @@ const SettingsPanel = ({ config, availableCodeHooks }: SettingsPanelProps) => {
33
33
  animation: window.matchMedia(
34
34
  '(prefers-reduced-motion: no-preference)'
35
35
  ).matches
36
- ? 'fadeInFromHalf 150ms ease-in'
36
+ ? 'fadeInFromHalf 450ms ease-in'
37
37
  : 'none',
38
38
  '--fade-start': '0.5',
39
39
  '--fade-end': '1',
@@ -2,37 +2,10 @@ import { useStore } from '@nanostores/react';
2
2
  import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
3
3
  import { getCtx } from '@/stores/nodes';
4
4
  import { toggleSettingsPanel } from '@/stores/storykeep';
5
+ import { toolAddModeTitles, toolAddModes } from '@/constants';
5
6
 
6
7
  import type { ToolAddMode } from '@/types/compositorTypes';
7
8
 
8
- const toolAddModeTitles: Record<ToolAddMode, string> = {
9
- p: 'Paragraph',
10
- h2: 'Heading 2',
11
- h3: 'Heading 3',
12
- h4: 'Heading 4',
13
- img: 'Image',
14
- signup: 'Email Sign-up Widget',
15
- yt: 'YouTube Video',
16
- bunny: 'Bunny Video',
17
- belief: 'Belief Select',
18
- identify: 'Identity As',
19
- toggle: 'Toggle Belief',
20
- };
21
-
22
- const toolAddModes: ToolAddMode[] = [
23
- 'p',
24
- 'h2',
25
- 'h3',
26
- 'h4',
27
- 'img',
28
- 'signup',
29
- 'yt',
30
- 'bunny',
31
- 'belief',
32
- 'identify',
33
- 'toggle',
34
- ];
35
-
36
9
  const AddElementsPanel = ({
37
10
  currentToolAddMode,
38
11
  }: {
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from 'react';
1
+ import { useRef, useEffect } from 'react';
2
2
  import { useStore } from '@nanostores/react';
3
3
  import PencilSquareIcon from '@heroicons/react/24/outline/PencilSquareIcon';
4
4
  import PaintBrushIcon from '@heroicons/react/24/outline/PaintBrushIcon';
@@ -8,11 +8,8 @@ import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
8
8
  import BugAntIcon from '@heroicons/react/24/outline/BugAntIcon';
9
9
  import { settingsPanelStore } from '@/stores/storykeep';
10
10
  import { getCtx } from '@/stores/nodes';
11
- import { debounce } from '@/utils/helpers';
12
11
  import type { ToolModeVal } from '@/types/compositorTypes';
13
12
 
14
- const SHORT_THRESHOLD = 650;
15
-
16
13
  const storykeepToolModes = [
17
14
  {
18
15
  key: 'styles' as const,
@@ -84,34 +81,16 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
84
81
  const handleEscapeKey = (event: KeyboardEvent) => {
85
82
  if (event.key === 'Escape') {
86
83
  ctx.toolModeValStore.set({ value: 'text' });
84
+ ctx.notifyNode('root');
87
85
  }
88
86
  };
89
87
 
90
- const toolModeNav = navRef.current;
91
-
92
- // If the <nav> element hasn't been rendered yet, do nothing.
93
- // The hook will re-run when hasTitle/hasPanes changes and it does render.
94
- if (!toolModeNav) {
95
- return;
96
- }
97
-
98
- const updateToolbarLayout = debounce(() => {
99
- const isWideAndShort =
100
- window.innerWidth >= 801 && window.innerHeight <= SHORT_THRESHOLD;
101
- toolModeNav.classList.toggle('is-compact-widget', isWideAndShort);
102
- }, 50);
103
-
104
88
  document.addEventListener('keydown', handleEscapeKey);
105
- window.addEventListener('resize', updateToolbarLayout);
106
-
107
- updateToolbarLayout(); // Initial check
108
89
 
109
90
  return () => {
110
91
  document.removeEventListener('keydown', handleEscapeKey);
111
- window.removeEventListener('resize', updateToolbarLayout);
112
92
  };
113
- // This dependency array is the key. The effect will re-run when the render conditions change.
114
- }, [ctx, hasTitle, hasPanes, isContext]);
93
+ }, [ctx]);
115
94
 
116
95
  if (!hasTitle || (!hasPanes && !isContext)) {
117
96
  return null;
@@ -119,38 +98,12 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
119
98
 
120
99
  return (
121
100
  <>
122
- <style>{`
123
- #mainNav.is-compact-widget {
124
- position: fixed;
125
- bottom: 0.25rem;
126
- left: 0rem;
127
- top: auto;
128
- right: auto;
129
- height: auto;
130
- width: auto;
131
- padding: 0.5rem;
132
- border-radius: 0 0.75rem 0.75rem 0;
133
- box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
134
- background-color: rgba(252, 252, 252, 0.7);
135
- backdrop-filter: blur(4px);
136
- border: 1px solid rgba(0, 0, 0, 0.05);
137
- }
138
- #mainNav.is-compact-widget > div {
139
- flex-direction: row;
140
- flex-wrap: nowrap;
141
- align-items: center;
142
- gap: 1rem;
143
- margin: 0;
144
- padding: 0;
145
- height: auto;
146
- }
147
- `}</style>
148
101
  <nav
149
102
  id="mainNav"
150
103
  ref={navRef}
151
- className="z-102 bg-mywhite fixed bottom-0 left-0 right-0 pt-1.5 md:sticky md:bottom-auto md:left-0 md:top-24 md:h-screen md:w-16 md:pt-0"
104
+ className="z-102 bg-mywhite md:bg-mywhite/70 fixed bottom-0 left-0 right-0 p-1.5 md:bottom-2 md:right-auto md:h-auto md:w-auto md:rounded-r-xl md:border md:border-black/5 md:p-2 md:shadow-lg md:backdrop-blur-sm"
152
105
  >
153
- <div className="flex flex-wrap justify-around gap-4 py-0.5 md:mt-0 md:flex-col md:items-center md:gap-8 md:space-x-0 md:space-y-2 md:py-2">
106
+ <div className="flex flex-wrap justify-around gap-4 py-0.5 md:flex-nowrap md:justify-start md:gap-4 md:p-0">
154
107
  <div className="text-mydarkgrey text-center text-sm font-bold">
155
108
  mode:
156
109
  <div className="font-action text-myblue pt-1.5 text-center text-xs">