diff --git a/packages/react/src/components/CodeMirror6.jsx b/packages/react/src/components/CodeMirror6.jsx
index 72e266f8..c04f422a 100644
--- a/packages/react/src/components/CodeMirror6.jsx
+++ b/packages/react/src/components/CodeMirror6.jsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import _CodeMirror from '@uiw/react-codemirror';
import { EditorView, Decoration } from '@codemirror/view';
import { StateField, StateEffect } from '@codemirror/state';
@@ -83,9 +83,8 @@ const highlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f),
});
-const extensions = [
+const staticExtensions = [
javascript(),
- vim(),
highlightField,
flashField,
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
@@ -99,6 +98,7 @@ export default function CodeMirror({
onViewChanged,
onSelectionChange,
theme,
+ vimMode,
options,
editorDidMount,
}) {
@@ -122,6 +122,7 @@ export default function CodeMirror({
},
[onSelectionChange],
);
+ const extensions = useMemo(() => [...staticExtensions, ...(vimMode ? [vim()] : [])], [vimMode]);
return (
<>
<_CodeMirror
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 90cda99a..5b8e36d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -373,6 +373,7 @@ importers:
'@strudel.cycles/webaudio': workspace:*
'@strudel.cycles/xen': workspace:*
'@supabase/supabase-js': ^1.35.3
+ '@tailwindcss/forms': ^0.5.3
'@tailwindcss/typography': ^0.5.8
'@types/node': ^18.0.0
'@types/react': ^18.0.26
@@ -417,6 +418,7 @@ importers:
'@strudel.cycles/webaudio': link:../packages/webaudio
'@strudel.cycles/xen': link:../packages/xen
'@supabase/supabase-js': 1.35.7
+ '@tailwindcss/forms': 0.5.3_tailwindcss@3.2.4
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.4
'@types/node': 18.11.18
'@types/react': 18.0.27
@@ -3575,6 +3577,15 @@ packages:
string.prototype.matchall: 4.0.8
dev: true
+ /@tailwindcss/forms/0.5.3_tailwindcss@3.2.4:
+ resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
+ dependencies:
+ mini-svg-data-uri: 1.4.4
+ tailwindcss: 3.2.4
+ dev: false
+
/@tailwindcss/typography/0.5.9_tailwindcss@3.2.4:
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==}
peerDependencies:
@@ -9386,6 +9397,11 @@ packages:
engines: {node: '>=4'}
dev: true
+ /mini-svg-data-uri/1.4.4:
+ resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
+ hasBin: true
+ dev: false
+
/minimatch/3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@@ -10428,17 +10444,6 @@ packages:
- supports-color
dev: true
- /postcss-import/14.1.0:
- resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
- engines: {node: '>=10.0.0'}
- peerDependencies:
- postcss: ^8.0.0
- dependencies:
- postcss-value-parser: 4.2.0
- read-cache: 1.0.0
- resolve: 1.22.1
- dev: false
-
/postcss-import/14.1.0_postcss@8.4.21:
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
engines: {node: '>=10.0.0'}
@@ -10449,16 +10454,6 @@ packages:
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.1
- dev: true
-
- /postcss-js/4.0.0:
- resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
- engines: {node: ^12 || ^14 || >= 16}
- peerDependencies:
- postcss: ^8.3.3
- dependencies:
- camelcase-css: 2.0.1
- dev: false
/postcss-js/4.0.0_postcss@8.4.21:
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
@@ -10468,23 +10463,6 @@ packages:
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.21
- dev: true
-
- /postcss-load-config/3.1.4:
- resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
- engines: {node: '>= 10'}
- peerDependencies:
- postcss: '>=8.0.9'
- ts-node: '>=9.0.0'
- peerDependenciesMeta:
- postcss:
- optional: true
- ts-node:
- optional: true
- dependencies:
- lilconfig: 2.0.6
- yaml: 1.10.2
- dev: false
/postcss-load-config/3.1.4_postcss@8.4.21:
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
@@ -10502,15 +10480,6 @@ packages:
postcss: 8.4.21
yaml: 1.10.2
- /postcss-nested/6.0.0:
- resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
- engines: {node: '>=12.0'}
- peerDependencies:
- postcss: ^8.2.14
- dependencies:
- postcss-selector-parser: 6.0.11
- dev: false
-
/postcss-nested/6.0.0_postcss@8.4.21:
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
engines: {node: '>=12.0'}
@@ -10519,7 +10488,6 @@ packages:
dependencies:
postcss: 8.4.21
postcss-selector-parser: 6.0.11
- dev: true
/postcss-selector-parser/6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
@@ -12142,8 +12110,6 @@ packages:
resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==}
engines: {node: '>=12.13.0'}
hasBin: true
- peerDependencies:
- postcss: ^8.0.9
dependencies:
arg: 5.0.2
chokidar: 3.5.3
@@ -12160,10 +12126,10 @@ packages:
object-hash: 3.0.0
picocolors: 1.0.0
postcss: 8.4.21
- postcss-import: 14.1.0
- postcss-js: 4.0.0
- postcss-load-config: 3.1.4
- postcss-nested: 6.0.0
+ postcss-import: 14.1.0_postcss@8.4.21
+ postcss-js: 4.0.0_postcss@8.4.21
+ postcss-load-config: 3.1.4_postcss@8.4.21
+ postcss-nested: 6.0.0_postcss@8.4.21
postcss-selector-parser: 6.0.11
postcss-value-parser: 4.2.0
quick-lru: 5.1.1
diff --git a/website/package.json b/website/package.json
index f8c466d3..cf73f8cb 100644
--- a/website/package.json
+++ b/website/package.json
@@ -34,6 +34,7 @@
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*",
"@supabase/supabase-js": "^1.35.3",
+ "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.8",
"@types/node": "^18.0.0",
"@types/react": "^18.0.26",
diff --git a/website/public/store.mjs b/website/public/store.mjs
new file mode 100644
index 00000000..1efcd91b
--- /dev/null
+++ b/website/public/store.mjs
@@ -0,0 +1,36 @@
+export const storeKey = 'strudel-settings';
+
+export function get(prop) {
+ const state = JSON.parse(localStorage.getItem(storeKey));
+ if (!prop) {
+ return state;
+ }
+ return state[prop];
+}
+
+export function set(next) {
+ localStorage.setItem(storeKey, JSON.stringify(next));
+}
+
+export function updateState(func) {
+ const prev = get();
+ const next = func(prev);
+ set(next);
+ document.dispatchEvent(
+ new CustomEvent(storeKey, {
+ detail: { next, prev },
+ }),
+ );
+}
+
+export function watch(func, prop) {
+ document.addEventListener(storeKey, (e) => {
+ const { prev, next } = e.detail;
+ const hasPropChanged = (p) => next[p] !== prev[p];
+ if (!prop) {
+ func(next);
+ } else if (hasPropChanged(prop)) {
+ func(next[prop]);
+ }
+ });
+}
diff --git a/website/src/components/HeadCommon.astro b/website/src/components/HeadCommon.astro
index 796f96db..dcf9af3b 100644
--- a/website/src/components/HeadCommon.astro
+++ b/website/src/components/HeadCommon.astro
@@ -48,7 +48,8 @@ const { strudelTheme } = settings;
{pwaInfo &&