getting the hang of the editContext api

This commit is contained in:
Chris Kruining 2025-02-24 17:01:47 +11:00
parent 213a1f7ae7
commit 4fb7405466
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
11 changed files with 587 additions and 134 deletions

View file

@ -4,29 +4,14 @@ import devtools from 'solid-devtools/vite';
export default defineConfig({ export default defineConfig({
vite: { vite: {
resolve: {
alias: [
{ find: '@', replacement: 'F:\\Github\\calque\\node_modules\\' },
],
},
html: { html: {
cspNonce: 'KAAS_IS_AWESOME', cspNonce: 'KAAS_IS_AWESOME',
}, },
// css: {
// postcss: {
// },
// },
plugins: [ plugins: [
devtools({ devtools({
autoname: true, autoname: true,
}), }),
solidSvg(), solidSvg(),
{
name: 'temp',
configResolved(config) {
console.log(config.resolve.alias);
},
}
], ],
}, },
solid: { solid: {

View file

@ -6,17 +6,19 @@
"dependencies": { "dependencies": {
"@solid-primitives/clipboard": "^1.6.0", "@solid-primitives/clipboard": "^1.6.0",
"@solid-primitives/destructure": "^0.2.0", "@solid-primitives/destructure": "^0.2.0",
"@solid-primitives/event-listener": "^2.4.0",
"@solid-primitives/i18n": "^2.2.0", "@solid-primitives/i18n": "^2.2.0",
"@solid-primitives/scheduled": "^1.5.0", "@solid-primitives/scheduled": "^1.5.0",
"@solid-primitives/selection": "^0.1.0", "@solid-primitives/selection": "^0.1.1",
"@solid-primitives/storage": "^4.3.1", "@solid-primitives/storage": "^4.3.1",
"@solid-primitives/timer": "^1.4.0", "@solid-primitives/timer": "^1.4.0",
"@solidjs/meta": "^0.29.4", "@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3", "@solidjs/router": "^0.15.3",
"@solidjs/start": "^1.1.0", "@solidjs/start": "^1.1.1",
"dexie": "^4.0.11", "dexie": "^4.0.11",
"flag-icons": "^7.3.2", "flag-icons": "^7.3.2",
"iterator-helpers-polyfill": "^3.0.1", "iterator-helpers-polyfill": "^3.0.1",
"rehype-dom-parse": "^5.0.2",
"rehype-parse": "^9.0.1", "rehype-parse": "^9.0.1",
"rehype-remark": "^10.0.0", "rehype-remark": "^10.0.0",
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
@ -33,7 +35,7 @@
"vinxi": "^0.5.3", "vinxi": "^0.5.3",
}, },
"devDependencies": { "devDependencies": {
"@happy-dom/global-registrator": "^17.0.3", "@happy-dom/global-registrator": "^17.1.1",
"@sinonjs/fake-timers": "^14.0.0", "@sinonjs/fake-timers": "^14.0.0",
"@solidjs/testing-library": "^0.8.10", "@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
@ -167,7 +169,7 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.20.2", "", { "os": "win32", "cpu": "x64" }, "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ=="], "@esbuild/win32-x64": ["@esbuild/win32-x64@0.20.2", "", { "os": "win32", "cpu": "x64" }, "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ=="],
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@17.0.3", "", { "dependencies": { "happy-dom": "^17.0.3" } }, "sha512-isCCWywZq8XPE3A5y7pRyFIsAgij+3eVXgQNCbexGRP00/+nctmf4SfQxC3vV3MmEaOXaNj7IiiSC0BtSHQZgg=="], "@happy-dom/global-registrator": ["@happy-dom/global-registrator@17.1.1", "", { "dependencies": { "happy-dom": "^17.1.1" } }, "sha512-if4TVRU4SnQwpOC9pN/a892nXPpctqER/rYIS9E/YsqwpaPANhMjFM7/+Ibd748gP6OuDJbspQ0axFrCscI1og=="],
"@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="], "@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="],
@ -347,7 +349,7 @@
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-RVw24IRNh1FQ4DCMb3OahB70tXIwc5vH8nhR4nNPsXwUPQeuOkLsDI5BlxaPk0vyZgqw9lDpufgI3HnPwplgDw=="], "@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-RVw24IRNh1FQ4DCMb3OahB70tXIwc5vH8nhR4nNPsXwUPQeuOkLsDI5BlxaPk0vyZgqw9lDpufgI3HnPwplgDw=="],
"@solid-primitives/selection": ["@solid-primitives/selection@0.1.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-LtqSZVVascQpBeIHS2RZ8UNvLjCa/aDEqrd9WQtqhz14vkeHAwBP33THdWDYn55b6UaOfWQlVYni3r4NfHXK0w=="], "@solid-primitives/selection": ["@solid-primitives/selection@0.1.1", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-BPkTf8sW/t7GmcDj1A0Fa0xsyCjxh4D0qUAlcbxDkyAH4pG0bfuJ4wCKX+2qgmG/bkBQHiK/UexJtFhlQ1MS4Q=="],
"@solid-primitives/static-store": ["@solid-primitives/static-store@0.0.8", "", { "dependencies": { "@solid-primitives/utils": "^6.2.3" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ZecE4BqY0oBk0YG00nzaAWO5Mjcny8Fc06CdbXadH9T9lzq/9GefqcSe/5AtdXqjvY/DtJ5C6CkcjPZO0o/eqg=="], "@solid-primitives/static-store": ["@solid-primitives/static-store@0.0.8", "", { "dependencies": { "@solid-primitives/utils": "^6.2.3" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ZecE4BqY0oBk0YG00nzaAWO5Mjcny8Fc06CdbXadH9T9lzq/9GefqcSe/5AtdXqjvY/DtJ5C6CkcjPZO0o/eqg=="],
@ -363,15 +365,15 @@
"@solidjs/router": ["@solidjs/router@0.15.3", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw=="], "@solidjs/router": ["@solidjs/router@0.15.3", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw=="],
"@solidjs/start": ["@solidjs/start@1.1.0", "", { "dependencies": { "@tanstack/server-functions-plugin": "^1.99.5", "@vinxi/plugin-directives": "^0.5.0", "@vinxi/server-components": "^0.5.0", "defu": "^6.1.2", "error-stack-parser": "^2.1.4", "html-to-image": "^1.11.11", "radix3": "^1.1.0", "seroval": "^1.0.2", "seroval-plugins": "^1.0.2", "shiki": "^1.26.1", "source-map-js": "^1.0.2", "terracotta": "^1.0.4", "tinyglobby": "^0.2.2", "vite-plugin-solid": "^2.11.1" }, "peerDependencies": { "vinxi": "^0.5.3" } }, "sha512-7MNhNVt8uF7tdvLkvJhj4357vg3Ha+yqJP8XhQ6IbSZbsyk/xMkYmfc1h6w4GWiWZ5tn1DvS1uqGXjLFbKRy6g=="], "@solidjs/start": ["@solidjs/start@1.1.1", "", { "dependencies": { "@tanstack/server-functions-plugin": "^1.103.1", "@vinxi/plugin-directives": "^0.5.0", "@vinxi/server-components": "^0.5.0", "defu": "^6.1.2", "error-stack-parser": "^2.1.4", "html-to-image": "^1.11.11", "radix3": "^1.1.0", "seroval": "^1.0.2", "seroval-plugins": "^1.0.2", "shiki": "^1.26.1", "source-map-js": "^1.0.2", "terracotta": "^1.0.4", "tinyglobby": "^0.2.2", "vite-plugin-solid": "^2.11.1" }, "peerDependencies": { "vinxi": "^0.5.3" } }, "sha512-vJuXJlhHvP/hSdKQ+iuvBU2bw0S+IKQYOyldnRoCvrX7Nmu1p3npnACSlhNNkN06IqSX3MVde8D8Lr9xXEMjRQ=="],
"@solidjs/testing-library": ["@solidjs/testing-library@0.8.10", "", { "dependencies": { "@testing-library/dom": "^10.4.0" }, "peerDependencies": { "@solidjs/router": ">=0.9.0", "solid-js": ">=1.0.0" }, "optionalPeers": ["@solidjs/router"] }, "sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ=="], "@solidjs/testing-library": ["@solidjs/testing-library@0.8.10", "", { "dependencies": { "@testing-library/dom": "^10.4.0" }, "peerDependencies": { "@solidjs/router": ">=0.9.0", "solid-js": ">=1.0.0" }, "optionalPeers": ["@solidjs/router"] }, "sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ=="],
"@tanstack/directive-functions-plugin": ["@tanstack/directive-functions-plugin@1.102.2", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-utils": "^1.102.2", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "babel-dead-code-elimination": "^1.0.9", "dedent": "^1.5.3", "tiny-invariant": "^1.3.3" } }, "sha512-wM4ovyuYx0rBvcaR9ay+CtuPK8AEqFv6rF4LbPqHx7EufzBi7aJt70RqPdHt3bP3dcEuJbcRGFxthZ+WUt/Q5Q=="], "@tanstack/directive-functions-plugin": ["@tanstack/directive-functions-plugin@1.106.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-utils": "^1.102.2", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "babel-dead-code-elimination": "^1.0.9", "dedent": "^1.5.3", "tiny-invariant": "^1.3.3" } }, "sha512-eAK+8tGl+ZZimROdnuHoAc1MVKvmQWh7WTQbh531607NQAuD/7TgbTAiSBfTJYW8qeGovrSp/qq+dnhjqTy3xQ=="],
"@tanstack/router-utils": ["@tanstack/router-utils@1.102.2", "", { "dependencies": { "@babel/generator": "^7.26.8", "@babel/parser": "^7.26.8", "ansis": "^3.11.0", "diff": "^7.0.0" } }, "sha512-Uwl2nbrxhCzviaHHBLNPhSC/OMpZLdOTxTJndUSsXTzWUP4IoQcVmngaIsxi9iriE3ArC1VXuanUAkfGmimNOQ=="], "@tanstack/router-utils": ["@tanstack/router-utils@1.102.2", "", { "dependencies": { "@babel/generator": "^7.26.8", "@babel/parser": "^7.26.8", "ansis": "^3.11.0", "diff": "^7.0.0" } }, "sha512-Uwl2nbrxhCzviaHHBLNPhSC/OMpZLdOTxTJndUSsXTzWUP4IoQcVmngaIsxi9iriE3ArC1VXuanUAkfGmimNOQ=="],
"@tanstack/server-functions-plugin": ["@tanstack/server-functions-plugin@1.102.2", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/directive-functions-plugin": "1.102.2", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "babel-dead-code-elimination": "^1.0.9", "dedent": "^1.5.3", "tiny-invariant": "^1.3.3" } }, "sha512-l7nPcYjUaiO4ogjfE5hAPsuByFsn/5LqtdsC7f0I1VVgMxHp08OVVRH2rrgdR24ehRr3RkrUQoFviVyBSxcfCA=="], "@tanstack/server-functions-plugin": ["@tanstack/server-functions-plugin@1.106.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/directive-functions-plugin": "1.106.0", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "babel-dead-code-elimination": "^1.0.9", "dedent": "^1.5.3", "tiny-invariant": "^1.3.3" } }, "sha512-uFqiICV0b9t7jluQLroUt9/2PayEKu4hWTS5nezWsW8kNaK0Pw4avdLK+8mYjYuJLBhuQrQZ/mSEMqdJYgYv0Q=="],
"@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="], "@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="],
@ -795,7 +797,7 @@
"h3": ["h3@1.13.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": ">=0.2.0 <0.4.0", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg=="], "h3": ["h3@1.13.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": ">=0.2.0 <0.4.0", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg=="],
"happy-dom": ["happy-dom@17.0.3", "", { "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" } }, "sha512-1vWCwpeguN02wQF8kGeaj69FDX19bXKQXmyUKcE+O0WLY0uhS0RPTLCJR8Omy8hrjMHwV3dUJ24JUrK07aOA9Q=="], "happy-dom": ["happy-dom@17.1.1", "", { "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" } }, "sha512-OSTkBlmD/6Do7gCd7nZB5iFq1bF9VQg/iFmjHmxvVX2S1UiOpo6sT+aFNnu3XUsB8hCZb9+GZ0G1g1TaMiAggw=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
@ -803,6 +805,8 @@
"hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="], "hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="],
"hast-util-from-dom": ["hast-util-from-dom@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hastscript": "^9.0.0", "web-namespaces": "^2.0.0" } }, "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q=="],
"hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
"hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
@ -1183,6 +1187,8 @@
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
"rehype-dom-parse": ["rehype-dom-parse@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-dom": "^5.0.0", "unified": "^11.0.0" } }, "sha512-8CqP11KaqvtWsMqVEC2yM3cZWZsDNqqpr8nPvogjraLuh45stabgcpXadCAxu1n6JaUNJ/Xr3GIqXP7okbNqLg=="],
"rehype-minify-whitespace": ["rehype-minify-whitespace@6.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0" } }, "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw=="], "rehype-minify-whitespace": ["rehype-minify-whitespace@6.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0" } }, "sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw=="],
"rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="],
@ -1523,8 +1529,6 @@
"@solid-primitives/resize-observer/@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.0", "", { "dependencies": { "@solid-primitives/utils": "^6.3.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-6Coau0Kv/dF83UQpbBzc+gnJafOQAPe2jCbB4jmTK5UocsR5cWmFBVRm3kin+nZFVaO4WkuELw0cKANWgTVh8Q=="], "@solid-primitives/resize-observer/@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.0", "", { "dependencies": { "@solid-primitives/utils": "^6.3.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-6Coau0Kv/dF83UQpbBzc+gnJafOQAPe2jCbB4jmTK5UocsR5cWmFBVRm3kin+nZFVaO4WkuELw0cKANWgTVh8Q=="],
"@solidjs/start/vite-plugin-solid": ["vite-plugin-solid@2.11.1", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-X9vbbK6AOOA6yxSsNl1VTuUq5y4BG9AR6Z5F/J1ZC2VO7ll8DlSCbOL+RcZXlRbxn0ptE6OI5832nGQhq4yXKQ=="],
"@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="],
"@testing-library/dom/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@testing-library/dom/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],

View file

@ -8,17 +8,19 @@
"dependencies": { "dependencies": {
"@solid-primitives/clipboard": "^1.6.0", "@solid-primitives/clipboard": "^1.6.0",
"@solid-primitives/destructure": "^0.2.0", "@solid-primitives/destructure": "^0.2.0",
"@solid-primitives/event-listener": "^2.4.0",
"@solid-primitives/i18n": "^2.2.0", "@solid-primitives/i18n": "^2.2.0",
"@solid-primitives/scheduled": "^1.5.0", "@solid-primitives/scheduled": "^1.5.0",
"@solid-primitives/selection": "^0.1.0", "@solid-primitives/selection": "^0.1.1",
"@solid-primitives/storage": "^4.3.1", "@solid-primitives/storage": "^4.3.1",
"@solid-primitives/timer": "^1.4.0", "@solid-primitives/timer": "^1.4.0",
"@solidjs/meta": "^0.29.4", "@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.3", "@solidjs/router": "^0.15.3",
"@solidjs/start": "^1.1.0", "@solidjs/start": "^1.1.1",
"dexie": "^4.0.11", "dexie": "^4.0.11",
"flag-icons": "^7.3.2", "flag-icons": "^7.3.2",
"iterator-helpers-polyfill": "^3.0.1", "iterator-helpers-polyfill": "^3.0.1",
"rehype-dom-parse": "^5.0.2",
"rehype-parse": "^9.0.1", "rehype-parse": "^9.0.1",
"rehype-remark": "^10.0.0", "rehype-remark": "^10.0.0",
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
@ -35,7 +37,7 @@
"vinxi": "^0.5.3" "vinxi": "^0.5.3"
}, },
"devDependencies": { "devDependencies": {
"@happy-dom/global-registrator": "^17.0.3", "@happy-dom/global-registrator": "^17.1.1",
"@sinonjs/fake-timers": "^14.0.0", "@sinonjs/fake-timers": "^14.0.0",
"@solidjs/testing-library": "^0.8.10", "@solidjs/testing-library": "^0.8.10",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",

View file

@ -1,8 +1,8 @@
import { Component, createEffect, createMemo, createSignal, For, onMount, untrack } from 'solid-js'; import { Component, createEffect, createMemo, createSignal, For, on, untrack } from 'solid-js';
import { debounce } from '@solid-primitives/scheduled';
import { createSelection, getTextNodes } from '@solid-primitives/selection'; import { createSelection, getTextNodes } from '@solid-primitives/selection';
import { createSource } from '~/features/source';
import { isServer } from 'solid-js/web'; import { isServer } from 'solid-js/web';
import { createEditContext } from '~/features/edit-context';
import { createSource } from '~/features/source';
import css from './textarea.module.css'; import css from './textarea.module.css';
interface TextareaProps { interface TextareaProps {
@ -16,75 +16,25 @@ interface TextareaProps {
} }
export function Textarea(props: TextareaProps) { export function Textarea(props: TextareaProps) {
const [selection, setSelection] = createSelection();
const [editorRef, setEditorRef] = createSignal<HTMLElement>(); const [editorRef, setEditorRef] = createSignal<HTMLElement>();
const source = createSource(() => props.value);
const source = createSource(props.value); const [text] = createEditContext(editorRef, () => source.out);
createEffect(() => { createEffect(() => {
props.oninput?.(source.in); props.oninput?.(source.in);
}); });
createEffect(() => { createEffect(on(() => [editorRef(), source.spellingErrors] as const, ([ref, errors]) => {
source.in = props.value; createHighlights(ref, 'spelling-error', errors);
}); }));
const mutate = debounce(() => { createEffect(on(() => [editorRef(), source.grammarErrors] as const, ([ref, errors]) => {
const [, start, end] = selection(); createHighlights(ref, 'grammar-error', errors);
const ref = editorRef(); }));
if (ref) { createEffect(on(() => [editorRef(), source.queryResults] as const, ([ref, errors]) => {
source.out = ref.innerHTML; createHighlights(ref, 'search-results', errors);
}));
ref.style.height = `1px`;
ref.style.height = `${2 + ref.scrollHeight}px`;
setSelection([ref, start, end]);
}
}, 300);
onMount(() => {
const ref = editorRef()!;
console.log(EditContext);
const context = new EditContext({
text: source.out,
});
const sub = (event) => context.addEventListener(event, (e: Event) => console.log(event, e));
sub('textupdate');
sub('textformatupdate');
sub('characterboundupdate');
console.log(context);
ref.editContext = context;
const resize = () => context.updateControlBounds(ref.getBoundingClientRect());
window.addEventListener('resize', resize);
resize();
// new MutationObserver(mutate).observe(ref, {
// subtree: true,
// childList: true,
// characterData: true,
// });
});
createEffect(() => {
createHighlights(editorRef()!, 'spelling-error', source.spellingErrors);
});
createEffect(() => {
createHighlights(editorRef()!, 'grammar-error', source.grammarErrors);
});
createEffect(() => {
createHighlights(editorRef()!, 'search-results', source.queryResults);
});
return <> return <>
<Suggestions /> <Suggestions />
@ -94,7 +44,7 @@ export function Textarea(props: TextareaProps) {
class={`${css.textarea} ${props.class}`} class={`${css.textarea} ${props.class}`}
dir="auto" dir="auto"
lang={props.lang} lang={props.lang}
innerHTML={source.out} innerHTML={text()}
data-placeholder={props.placeholder ?? ''} data-placeholder={props.placeholder ?? ''}
on:keydown={e => e.stopPropagation()} on:keydown={e => e.stopPropagation()}
on:pointerdown={e => e.stopPropagation()} on:pointerdown={e => e.stopPropagation()}
@ -176,20 +126,7 @@ const findMarkerNode = (node: Node | null) => {
return node; return node;
}; };
const spellChecker = checker(/\w+/gi);
const grammarChecker = checker(/\w+\s+\w+/gi);
function checker(regex: RegExp) {
return (subject: string, lang: string): [number, number][] => {
// return [];
const threshold = .75//.99;
return Array.from<RegExpExecArray>(subject.matchAll(regex)).filter(() => Math.random() >= threshold).map(({ 0: match, index }) => {
return [index, index + match.length] as const;
});
}
}
const createHighlights = (node: Node, type: string, ranges: [number, number][]) => { const createHighlights = (node: Node, type: string, ranges: [number, number][]) => {
queueMicrotask(() => { queueMicrotask(() => {

View file

@ -0,0 +1,324 @@
import { createEventListenerMap, DocumentEventListener, WindowEventListener } from "@solid-primitives/event-listener";
import { Accessor, createEffect, createMemo, onMount } from "solid-js";
import { createStore } from "solid-js/store";
import { isServer } from "solid-js/web";
import { createSelection, getTextNodes } from "@solid-primitives/selection";
import { visit } from "unist-util-visit";
import type { Root, Text } from 'hast';
import { unified } from "unified";
import rehypeParse from "rehype-parse";
type EditContext = [Accessor<string>];
export function createEditContext(ref: Accessor<HTMLElement | undefined>, value: Accessor<string>): EditContext {
if (isServer) {
return [createMemo(() => value())];
}
if (!("EditContext" in window)) {
throw new Error('`EditContext` is not implemented');
}
const context = new EditContext({
text: value(),
});
const [store, setStore] = createStore({
text: value(),
isComposing: false,
// Bounds
characterBounds: new Array<DOMRect>(),
controlBounds: new DOMRect(),
selectionBounds: new DOMRect(),
});
const ast = createMemo(() => unified().use(rehypeParse).parse(store.text));
const indices = createMemo(() => {
const root = ref();
if (!root) {
return [];
}
const nodes = getTextNodes(root);
const indices: { node: Node, text: { start: number, end: number }, html: { start: number, end: number } }[] = [];
let index = 0;
visit(ast(), n => n.type === 'text', (node) => {
const { position, value } = node as Text;
const end = index + value.length;
if (position) {
indices.push({ node: nodes.shift()!, text: { start: index, end }, html: { start: position.start.offset!, end: position.end.offset! } });
}
index = end;
});
return indices;
});
const [selection, setSelection] = createSelection();
createEffect(() => {
console.log(indices());
});
createEventListenerMap<any>(context, {
textupdate(e: TextUpdateEvent) {
const { updateRangeStart: start, updateRangeEnd: end } = e;
setStore('text', `${store.text.slice(0, start)}${e.text}${store.text.slice(end)}`);
updateSelection(toRange(ref()!, start, end));
setTimeout(() => {
console.log('hmmm', e, start, end);
context.updateSelection(start, end);
setSelection([ref()!, start, end]);
}, 1000);
},
compositionstart() {
setStore('isComposing', true);
},
compositionend() {
setStore('isComposing', false);
},
characterboundsupdate(e: CharacterBoundsUpdateEvent) {
context.updateCharacterBounds(e.rangeStart, []);
},
textformatupdate(e: TextFormatUpdateEvent) {
const formats = e.getTextFormats();
for (const format of formats) {
console.log(format);
}
},
});
function updateControlBounds() {
context.updateControlBounds(ref()!.getBoundingClientRect());
}
function updateSelection(range: Range) {
const [start, end] = toIndices(ref()!, range);
let index = 0;
let mappedStart = -1;
let mappedEnd = -1;
visit(ast(), n => n.type === 'text', (node) => {
const { position, value } = node as Text;
if (position) {
if (index <= start && (index + value.length) >= start) {
mappedStart = position.start.offset! + range.startOffset;
}
if (index <= end && (index + value.length) >= end) {
mappedEnd = position.start.offset! + range.endOffset;
}
}
index += value.length;
});
context.updateSelection(mappedStart, mappedEnd);
context.updateSelectionBounds(range.getBoundingClientRect());
setSelection([ref()!, start, end]);
}
WindowEventListener({
onresize() {
updateControlBounds()
},
});
createEventListenerMap(() => ref()!, {
keydown(e: KeyboardEvent) {
// keyCode === 229 is a special code that indicates an IME event.
// https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event#keydown_events_with_ime
if (e.keyCode === 229) {
return;
}
const start = context.selectionStart;
const end = context.selectionEnd;
if (e.key === 'Tab') {
e.preventDefault();
context.updateText(start, end, '\t');
// updateSelection(start + 1, start + 1);
} else if (e.key === 'Enter') {
context.updateText(start, end, '\n');
// updateSelection(start + 1, start + 1);
}
},
});
DocumentEventListener({
onSelectionchange(e) {
const selection = document.getSelection()!;
if (selection.rangeCount < 1) {
return;
}
if (document.activeElement !== ref()) {
return;
}
updateSelection(selection.getRangeAt(0)!);
},
});
onMount(() => {
updateControlBounds();
});
createEffect((last?: HTMLElement) => {
if (last !== undefined) {
last.editContext = undefined;
}
const el = ref();
if (el === undefined) {
return;
}
el.editContext = context;
return el;
});
createEffect(() => {
context.updateText(0, 0, value());
});
return [createMemo(() => store.text)];
}
declare global {
interface HTMLElement {
editContext?: EditContext;
}
interface TextFormat {
readonly rangeStart: number;
readonly rangeEnd: number;
readonly underlineStyle: 'none' | 'solid' | 'double' | 'dotted' | 'sadhed' | 'wavy';
readonly underlineThickness: 'none' | 'thin' | 'thick';
}
interface CharacterBoundsUpdateEvent extends Event {
readonly rangeStart: number;
readonly rangeEnd: number;
}
interface TextFormatUpdateEvent extends Event {
getTextFormats(): TextFormat[];
}
interface TextUpdateEvent extends Event {
readonly updateRangeStart: number;
readonly updateRangeEnd: number;
readonly text: string;
readonly selectionStart: number;
readonly selectionEnd: number;
}
interface EditContextEventMap {
characterboundsupdate: CharacterBoundsUpdateEvent;
compositionstart: Event;
compositionend: Event;
textformatupdate: TextFormatUpdateEvent;
textupdate: TextUpdateEvent;
}
interface EditContext extends EventTarget {
readonly text: string;
readonly selectionStart: number;
readonly selectionEnd: number;
readonly characterBoundsRangeStart: number;
oncharacterboundsupdate?: (event: CharacterBoundsUpdateEvent) => any;
oncompositionstart?: (event: Event) => any;
oncompositionend?: (event: Event) => any;
ontextformatupdate?: (event: TextFormatUpdateEvent) => any;
ontextupdate?: (event: TextUpdateEvent) => any;
attachedElements(): [HTMLElement];
characterBounds(): DOMRect[];
updateText(rangeStart: number, rangeEnd: number, text: string): void;
updateSelection(start: number, end: number): void;
updateControlBounds(controlBounds: DOMRect): void;
updateSelectionBounds(selectionBounds: DOMRect): void;
updateCharacterBounds(rangeStart: number, characterBounds: DOMRect[]): void;
addEventListener<K extends keyof EditContextEventMap>(type: K, listener: (this: EditContext, ev: EditContextEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
}
interface EditContextConstructor {
new(): EditContext;
new(options: Partial<{ text: string, selectionStart: number, selectionEnd: number }>): EditContext;
readonly prototype: EditContext;
}
var EditContext: EditContextConstructor;
}
const offsetOf = (node: Node, nodes: Node[]) => nodes.slice(0, nodes.indexOf(node)).reduce((t, n) => t + n.textContent!.length, 0);
const toRange = (root: Node, start: number, end: number): Range => {
let index = 0;
let startNode = null;
let endNode = null;
for (const node of getTextNodes(root)) {
const length = node.textContent!.length;
if (index <= start && (index + length) >= start) {
startNode = [node, Math.abs(end - index)] as const;
}
if (index <= end && (index + length) >= end) {
endNode = [node, Math.abs(end - index)] as const;
}
if (startNode !== null && endNode !== null) {
break;
}
index += length;
}
const range = new Range();
if (startNode !== null) {
range.setStart(...startNode);
}
if (endNode !== null) {
range.setEnd(...endNode);
}
return range;
};
const toIndices = (node: Node, range: Range): [number, number] => {
const nodes = getTextNodes(node);
const start = offsetOf(range.startContainer, nodes) + range.startOffset;
const end = offsetOf(range.endContainer, nodes) + range.endOffset;
return [start, end];
};

View file

@ -0,0 +1,3 @@
export { createEditContext } from './context';

View file

@ -0,0 +1,194 @@
const WHITESPACE = [" ", "\n", "\t"];
function getOpenTagName(htmlString: string, pos: number) {
let tagName = "";
let char = htmlString.charAt(pos);
while (char !== ">" && char !== " " && char !== "/" && char !== "") {
tagName += char;
char = htmlString.charAt(++pos);
}
return tagName;
}
function getCloseTagName(htmlString: string, pos: number) {
let tagName = "";
let char = htmlString.charAt(pos);
while (char !== ">" && char !== "") {
tagName += char;
char = htmlString.charAt(++pos);
}
return tagName;
}
function getWhiteSpace(htmlString: string, pos: number) {
let whitespace = "";
let char = htmlString.charAt(pos);
while (WHITESPACE.includes(char) && char !== "") {
whitespace += char;
char = htmlString.charAt(++pos);
}
return whitespace;
}
function getAttributeName(htmlString: string, pos: number) {
let attributeName = "";
let char = htmlString.charAt(pos);
while (char !== "=" && char !== " " && char !== ">" && char !== "") {
attributeName += char;
char = htmlString.charAt(++pos);
}
return attributeName;
}
function getAttributeValue(htmlString: string, pos: number, quote: string) {
let attributeValue = "";
let char = htmlString.charAt(pos);
const isAtEnd = (c) => {
if (quote) {
return c === quote || c === "";
}
return c === " " || c === ">" || c === "/" || c === "";
};
while (!isAtEnd(char)) {
attributeValue += char;
char = htmlString.charAt(++pos);
}
return attributeValue;
}
function getText(htmlString: string, pos: number) {
let text = "";
let char = htmlString.charAt(pos);
while (char !== "<" && char !== "") {
text += char;
char = htmlString.charAt(++pos);
}
return text;
}
export function tokenizeHTML(htmlString: string) {
let pos = 0;
let isInTag = false;
let isInAttribute = false;
let isAfterAttributeEqual = false;
const tokens = [];
while (pos < htmlString.length) {
const char = htmlString.charAt(pos);
const nextChar = htmlString.charAt(pos + 1);
if (char === "<" && nextChar !== "/" && !isInTag && !isInAttribute) {
isInTag = true;
tokens.push({ type: "openTagStart", value: "<", pos });
pos++;
const tagName = getOpenTagName(htmlString, pos);
tokens.push({ type: "tagName", value: tagName, pos });
pos += tagName.length;
continue;
}
if (WHITESPACE.includes(char) && isInTag) {
const whitespace = getWhiteSpace(htmlString, pos);
tokens.push({ type: "whitespace", value: whitespace, pos });
pos += whitespace.length;
isInAttribute = false;
continue;
}
if (char === ">" && isInTag && !isInAttribute) {
isInTag = false;
tokens.push({ type: "openTagEnd", value: ">", pos });
pos++;
continue;
}
if (isInTag && !isInAttribute && char === "/" && nextChar === ">") {
isInTag = false;
tokens.push({ type: "selfClose", value: "/>", pos });
pos += 2;
continue;
}
if (isInTag && !isInAttribute) {
isInAttribute = true;
const attributeName = getAttributeName(htmlString, pos);
tokens.push({ type: "attributeName", value: attributeName, pos });
pos += attributeName.length;
if (htmlString.charAt(pos) !== "=" && htmlString.charAt(pos) !== "'" && htmlString.charAt(pos) !== '"') {
isInAttribute = false;
}
continue;
}
if (char === "=" && isInAttribute && isInTag) {
isAfterAttributeEqual = true;
tokens.push({ type: "equal", value: "=", pos });
pos++;
continue;
}
if (isAfterAttributeEqual && isInAttribute && isInTag) {
const hasQuote = char === "'" || char === '"';
const quote = hasQuote ? char : "";
if (hasQuote) {
tokens.push({ type: "quoteStart", value: quote, pos });
pos++;
}
const attributeValue = getAttributeValue(htmlString, pos, quote);
tokens.push({ type: "attributeValue", value: attributeValue, pos });
pos += attributeValue.length;
if (hasQuote && htmlString.charAt(pos) === quote) {
tokens.push({ type: "quoteEnd", value: quote, pos });
pos++;
}
isInAttribute = false;
isAfterAttributeEqual = false;
continue;
}
if (!isInTag && char === "<" && nextChar === "/") {
tokens.push({ type: "closeTagStart", value: "</", pos });
pos += 2;
const tagName = getCloseTagName(htmlString, pos);
tokens.push({ type: "tagName", value: tagName, pos });
pos += tagName.length;
if (htmlString.charAt(pos) === ">") {
tokens.push({ type: "closeTagEnd", value: ">", pos });
pos++;
}
continue;
}
if (!isInTag) {
const text = getText(htmlString, pos);
tokens.push({ type: "text", value: text, pos });
pos += text.length;
continue;
}
}
return tokens;
}

View file

@ -1,10 +1,9 @@
import { Accessor, Component, createEffect, createMemo, createSignal, For, JSX, Show, untrack } from "solid-js"; import { Accessor, Component, createEffect, createMemo, createSignal, For, JSX, Show, untrack } from "solid-js";
import { decode, Mutation } from "~/utilities"; import { Mutation } from "~/utilities";
import { Column, GridApi as GridCompApi, Grid as GridComp } from "~/components/grid"; import { Column, GridApi as GridCompApi, Grid as GridComp } from "~/components/grid";
import { createDataSet, DataSetNode, DataSetRowNode } from "~/features/dataset"; import { createDataSet, DataSetNode, DataSetRowNode } from "~/features/dataset";
import { SelectionItem } from "../selectable"; import { SelectionItem } from "../selectable";
import { useI18n } from "../i18n"; import { useI18n } from "../i18n";
import { debounce } from "@solid-primitives/scheduled";
import css from "./grid.module.css" import css from "./grid.module.css"
import { Textarea } from "~/components/textarea"; import { Textarea } from "~/components/textarea";

View file

@ -1,4 +1,4 @@
import { Accessor, children, Component, createContext, createEffect, createMemo, createResource, createSignal, For, InitializedResource, JSX, onCleanup, ParentComponent, Setter, Show, useContext } from "solid-js"; import { Accessor, Component, createContext, createMemo, createResource, createSignal, For, JSX, onCleanup, ParentComponent, Setter, Show, useContext } from "solid-js";
import { AiFillFile, AiFillFolder, AiFillFolderOpen } from "solid-icons/ai"; import { AiFillFile, AiFillFolder, AiFillFolderOpen } from "solid-icons/ai";
import { SelectionProvider, selectable } from "~/features/selectable"; import { SelectionProvider, selectable } from "~/features/selectable";
import { debounce } from "@solid-primitives/scheduled"; import { debounce } from "@solid-primitives/scheduled";

View file

@ -1,15 +1,17 @@
import { createEffect, onMount } from "solid-js"; import { Accessor, createEffect } from "solid-js";
import { createStore } from "solid-js/store"; import { createStore } from "solid-js/store";
import { unified } from 'unified' import { unified } from 'unified'
import { Text, Root } from 'hast'
import { visit } from "unist-util-visit"; import { visit } from "unist-util-visit";
import { decode } from "~/utilities"; import { decode } from "~/utilities";
import remarkParse from 'remark-parse' import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype' import remarkRehype from 'remark-rehype'
import remarkStringify from 'remark-stringify' import remarkStringify from 'remark-stringify'
import rehypeParse from 'rehype-parse' import rehypeParse from 'rehype-parse'
import rehypeDomParse from 'rehype-dom-parse'
import rehypeRemark from 'rehype-remark' import rehypeRemark from 'rehype-remark'
import rehypeStringify from 'rehype-stringify' import rehypeStringify from 'rehype-stringify'
import type { Text, Root } from 'hast'
import { isServer } from "solid-js/web";
interface SourceStore { interface SourceStore {
in: string; in: string;
@ -34,29 +36,12 @@ export interface Source {
// TODO :: make this configurable, right now we can only do markdown <--> html. // TODO :: make this configurable, right now we can only do markdown <--> html.
const inToOutProcessor = unified().use(remarkParse).use(remarkRehype).use(rehypeStringify); const inToOutProcessor = unified().use(remarkParse).use(remarkRehype).use(rehypeStringify);
const outToInProcessor = unified().use(rehypeParse).use(rehypeRemark).use(remarkStringify, { bullet: '-' }); const outToInProcessor = unified().use(isServer ? rehypeParse : rehypeDomParse).use(rehypeRemark).use(remarkStringify, { bullet: '-' });
export function createSource(initalValue: string): Source { export function createSource(value: Accessor<string>): Source {
const ast = inToOutProcessor.runSync(inToOutProcessor.parse(initalValue)); const [store, setStore] = createStore<SourceStore>({ in: '', out: '', plain: '', query: '', metadata: { spellingErrors: [], grammarErrors: [], queryResults: [] } });
const out = String(inToOutProcessor.stringify(ast));
const plain = String(unified().use(plainTextStringify).stringify(ast));
const [store, setStore] = createStore<SourceStore>({ in: initalValue, out, plain, query: '', metadata: { spellingErrors: [], grammarErrors: [], queryResults: [] } }); const src: Source = {
createEffect(() => {
const value = store.plain;
setStore('metadata', {
spellingErrors: spellChecker(value, ''),
grammarErrors: grammarChecker(value, ''),
});
});
createEffect(() => {
setStore('metadata', 'queryResults', findMatches(store.plain, store.query).toArray());
});
return {
get in() { get in() {
return store.in; return store.in;
}, },
@ -102,6 +87,26 @@ export function createSource(initalValue: string): Source {
return store.metadata.queryResults; return store.metadata.queryResults;
}, },
}; };
createEffect(() => {
src.in = value();
});
src.in = value();
createEffect(() => {
const value = store.plain;
setStore('metadata', {
spellingErrors: spellChecker(value, ''),
grammarErrors: grammarChecker(value, ''),
});
});
createEffect(() => {
setStore('metadata', 'queryResults', findMatches(store.plain, store.query).toArray());
});
return src;
} }
function plainTextStringify() { function plainTextStringify() {

View file

@ -1,7 +1,7 @@
import { Sidebar } from '~/components/sidebar'; import { Sidebar } from '~/components/sidebar';
import { CellEditor, Column, DataSetGroupNode, DataSetNode, DataSetRowNode, Grid, GridApi } from '~/components/grid'; import { CellEditor, Column, DataSetGroupNode, DataSetNode, DataSetRowNode, Grid, GridApi } from '~/components/grid';
import { people, Person } from './experimental.data'; import { people, Person } from './experimental.data';
import { Component, createEffect, createMemo, createSignal, For, Match, Switch } from 'solid-js'; import { Component, createEffect, createMemo, createSignal, For } from 'solid-js';
import { MutarionKind, Mutation } from '~/utilities'; import { MutarionKind, Mutation } from '~/utilities';
import { Table } from '~/components/table'; import { Table } from '~/components/table';
import { createDataSet } from '~/features/dataset'; import { createDataSet } from '~/features/dataset';