authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-03-27 20:40:19-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-03-27 22:30:26-07:00
logae4477792c3ad86f158accb18e892e77aa48cf00
treec09ed54383545520ffeafa2d7a511be14c528883
parentb01c9ea08ff53136b25ad5c35f26fcfa1917aaf0
signature Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs

chore: remove remaining JSX in the questions page

drops the ability to do `.client.tsx`

18 files changed, 429 insertions(+), 461 deletions(-)

framework/generate.ts+3-3
...@@ -184,7 +184,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {...@@ -184,7 +184,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {
184 list: pages,184 list: pages,
185 prefix: section.base,185 prefix: section.base,
186 include: [".tsx", ".marko", ".mdo"],186 include: [".tsx", ".marko", ".mdo"],
187 exclude: [".client.ts", ".client.tsx"],187 exclude: [".client.ts"],
188 page: true,188 page: true,
189 },189 },
190 {190 {
...@@ -198,7 +198,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {...@@ -198,7 +198,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {
198 dir: sectionPath("scripts"),198 dir: sectionPath("scripts"),
199 list: scripts,199 list: scripts,
200 prefix: rootPrefix,200 prefix: rootPrefix,
201 include: [".client.ts", ".client.tsx"],201 include: [".client.ts"],
202 page: false,202 page: false,
203 },203 },
204 {204 {
...@@ -206,7 +206,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {...@@ -206,7 +206,7 @@ export async function scanSiteSection(io: Io, section: sg.Section) {
206 list: views,206 list: views,
207 prefix: rootPrefix,207 prefix: rootPrefix,
208 include: [".tsx", ".marko", ".mdo"],208 include: [".tsx", ".marko", ".mdo"],
209 exclude: [".client.ts", ".client.tsx"],209 exclude: [".client.ts"],
210 page: false,210 page: false,
211 },211 },
212 ];212 ];
framework/hot.ts+2-2
...@@ -144,8 +144,8 @@ export function loadEsbuildCode(...@@ -144,8 +144,8 @@ export function loadEsbuildCode(
144144
145export function resolveClientRef(sourcePath: string, ref: string) {145export function resolveClientRef(sourcePath: string, ref: string) {
146 const filePath = resolveFrom(sourcePath, ref);146 const filePath = resolveFrom(sourcePath, ref);
147 if (!filePath.endsWith(".client.ts") && !filePath.endsWith(".client.tsx")) {147 if (!filePath.endsWith(".client.ts")) {
148 throw new Error("addScript must be a .client.ts or .client.tsx");148 throw new Error("addScript must take a .client.ts");
149 }149 }
150 return path.relative(projectSrc, filePath);150 return path.relative(projectSrc, filePath);
151}151}
package-lock.json+129-136
...@@ -11,7 +11,6 @@...@@ -11,7 +11,6 @@
11 "@marko/compiler": "5.39.62",11 "@marko/compiler": "5.39.62",
12 "@types/node": "^24.12.0",12 "@types/node": "^24.12.0",
13 "dashjs": "5.0.3",13 "dashjs": "5.0.3",
14 "devalue": "5.6.4",
15 "esbuild": "0.25.5",14 "esbuild": "0.25.5",
16 "harfbuzzjs": "0.10.1",15 "harfbuzzjs": "0.10.1",
17 "hls.js": "1.6.5",16 "hls.js": "1.6.5",
...@@ -22,7 +21,7 @@...@@ -22,7 +21,7 @@
22 "source-map-support": "0.5.21",21 "source-map-support": "0.5.21",
23 "typescript": "^5.9.3",22 "typescript": "^5.9.3",
24 "unique-names-generator": "4.7.1",23 "unique-names-generator": "4.7.1",
25 "vitest": "^4.0.18",24 "vitest": "4.1.2",
26 "vscode-oniguruma": "2.0.1",25 "vscode-oniguruma": "2.0.1",
27 "vscode-textmate": "9.2.0",26 "vscode-textmate": "9.2.0",
28 "wawoff2": "2.0.1"27 "wawoff2": "2.0.1"
...@@ -670,18 +669,18 @@...@@ -670,18 +669,18 @@
670 }669 }
671 },670 },
672 "node_modules/@oxc-project/types": {671 "node_modules/@oxc-project/types": {
673 "version": "0.120.0",672 "version": "0.122.0",
674 "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz",673 "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz",
675 "integrity": "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==",674 "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==",
676 "license": "MIT",675 "license": "MIT",
677 "funding": {676 "funding": {
678 "url": "https://github.com/sponsors/Boshen"677 "url": "https://github.com/sponsors/Boshen"
679 }678 }
680 },679 },
681 "node_modules/@rolldown/binding-android-arm64": {680 "node_modules/@rolldown/binding-android-arm64": {
682 "version": "1.0.0-rc.10",681 "version": "1.0.0-rc.12",
683 "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.10.tgz",682 "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz",
684 "integrity": "sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==",683 "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==",
685 "cpu": [684 "cpu": [
686 "arm64"685 "arm64"
687 ],686 ],
...@@ -695,9 +694,9 @@...@@ -695,9 +694,9 @@
695 }694 }
696 },695 },
697 "node_modules/@rolldown/binding-darwin-arm64": {696 "node_modules/@rolldown/binding-darwin-arm64": {
698 "version": "1.0.0-rc.10",697 "version": "1.0.0-rc.12",
699 "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.10.tgz",698 "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz",
700 "integrity": "sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==",699 "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==",
701 "cpu": [700 "cpu": [
702 "arm64"701 "arm64"
703 ],702 ],
...@@ -711,9 +710,9 @@...@@ -711,9 +710,9 @@
711 }710 }
712 },711 },
713 "node_modules/@rolldown/binding-darwin-x64": {712 "node_modules/@rolldown/binding-darwin-x64": {
714 "version": "1.0.0-rc.10",713 "version": "1.0.0-rc.12",
715 "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.10.tgz",714 "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz",
716 "integrity": "sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==",715 "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==",
717 "cpu": [716 "cpu": [
718 "x64"717 "x64"
719 ],718 ],
...@@ -727,9 +726,9 @@...@@ -727,9 +726,9 @@
727 }726 }
728 },727 },
729 "node_modules/@rolldown/binding-freebsd-x64": {728 "node_modules/@rolldown/binding-freebsd-x64": {
730 "version": "1.0.0-rc.10",729 "version": "1.0.0-rc.12",
731 "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.10.tgz",730 "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz",
732 "integrity": "sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==",731 "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==",
733 "cpu": [732 "cpu": [
734 "x64"733 "x64"
735 ],734 ],
...@@ -743,9 +742,9 @@...@@ -743,9 +742,9 @@
743 }742 }
744 },743 },
745 "node_modules/@rolldown/binding-linux-arm-gnueabihf": {744 "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
746 "version": "1.0.0-rc.10",745 "version": "1.0.0-rc.12",
747 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.10.tgz",746 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz",
748 "integrity": "sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==",747 "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==",
749 "cpu": [748 "cpu": [
750 "arm"749 "arm"
751 ],750 ],
...@@ -759,9 +758,9 @@...@@ -759,9 +758,9 @@
759 }758 }
760 },759 },
761 "node_modules/@rolldown/binding-linux-arm64-gnu": {760 "node_modules/@rolldown/binding-linux-arm64-gnu": {
762 "version": "1.0.0-rc.10",761 "version": "1.0.0-rc.12",
763 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.10.tgz",762 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz",
764 "integrity": "sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==",763 "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==",
765 "cpu": [764 "cpu": [
766 "arm64"765 "arm64"
767 ],766 ],
...@@ -775,9 +774,9 @@...@@ -775,9 +774,9 @@
775 }774 }
776 },775 },
777 "node_modules/@rolldown/binding-linux-arm64-musl": {776 "node_modules/@rolldown/binding-linux-arm64-musl": {
778 "version": "1.0.0-rc.10",777 "version": "1.0.0-rc.12",
779 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.10.tgz",778 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz",
780 "integrity": "sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==",779 "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==",
781 "cpu": [780 "cpu": [
782 "arm64"781 "arm64"
783 ],782 ],
...@@ -791,9 +790,9 @@...@@ -791,9 +790,9 @@
791 }790 }
792 },791 },
793 "node_modules/@rolldown/binding-linux-ppc64-gnu": {792 "node_modules/@rolldown/binding-linux-ppc64-gnu": {
794 "version": "1.0.0-rc.10",793 "version": "1.0.0-rc.12",
795 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.10.tgz",794 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz",
796 "integrity": "sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==",795 "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==",
797 "cpu": [796 "cpu": [
798 "ppc64"797 "ppc64"
799 ],798 ],
...@@ -807,9 +806,9 @@...@@ -807,9 +806,9 @@
807 }806 }
808 },807 },
809 "node_modules/@rolldown/binding-linux-s390x-gnu": {808 "node_modules/@rolldown/binding-linux-s390x-gnu": {
810 "version": "1.0.0-rc.10",809 "version": "1.0.0-rc.12",
811 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.10.tgz",810 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz",
812 "integrity": "sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==",811 "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==",
813 "cpu": [812 "cpu": [
814 "s390x"813 "s390x"
815 ],814 ],
...@@ -823,9 +822,9 @@...@@ -823,9 +822,9 @@
823 }822 }
824 },823 },
825 "node_modules/@rolldown/binding-linux-x64-gnu": {824 "node_modules/@rolldown/binding-linux-x64-gnu": {
826 "version": "1.0.0-rc.10",825 "version": "1.0.0-rc.12",
827 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.10.tgz",826 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz",
828 "integrity": "sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==",827 "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==",
829 "cpu": [828 "cpu": [
830 "x64"829 "x64"
831 ],830 ],
...@@ -839,9 +838,9 @@...@@ -839,9 +838,9 @@
839 }838 }
840 },839 },
841 "node_modules/@rolldown/binding-linux-x64-musl": {840 "node_modules/@rolldown/binding-linux-x64-musl": {
842 "version": "1.0.0-rc.10",841 "version": "1.0.0-rc.12",
843 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.10.tgz",842 "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz",
844 "integrity": "sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==",843 "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==",
845 "cpu": [844 "cpu": [
846 "x64"845 "x64"
847 ],846 ],
...@@ -855,9 +854,9 @@...@@ -855,9 +854,9 @@
855 }854 }
856 },855 },
857 "node_modules/@rolldown/binding-openharmony-arm64": {856 "node_modules/@rolldown/binding-openharmony-arm64": {
858 "version": "1.0.0-rc.10",857 "version": "1.0.0-rc.12",
859 "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.10.tgz",858 "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz",
860 "integrity": "sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==",859 "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==",
861 "cpu": [860 "cpu": [
862 "arm64"861 "arm64"
863 ],862 ],
...@@ -871,9 +870,9 @@...@@ -871,9 +870,9 @@
871 }870 }
872 },871 },
873 "node_modules/@rolldown/binding-wasm32-wasi": {872 "node_modules/@rolldown/binding-wasm32-wasi": {
874 "version": "1.0.0-rc.10",873 "version": "1.0.0-rc.12",
875 "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.10.tgz",874 "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz",
876 "integrity": "sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==",875 "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==",
877 "cpu": [876 "cpu": [
878 "wasm32"877 "wasm32"
879 ],878 ],
...@@ -887,9 +886,9 @@...@@ -887,9 +886,9 @@
887 }886 }
888 },887 },
889 "node_modules/@rolldown/binding-win32-arm64-msvc": {888 "node_modules/@rolldown/binding-win32-arm64-msvc": {
890 "version": "1.0.0-rc.10",889 "version": "1.0.0-rc.12",
891 "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.10.tgz",890 "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz",
892 "integrity": "sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==",891 "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==",
893 "cpu": [892 "cpu": [
894 "arm64"893 "arm64"
895 ],894 ],
...@@ -903,9 +902,9 @@...@@ -903,9 +902,9 @@
903 }902 }
904 },903 },
905 "node_modules/@rolldown/binding-win32-x64-msvc": {904 "node_modules/@rolldown/binding-win32-x64-msvc": {
906 "version": "1.0.0-rc.10",905 "version": "1.0.0-rc.12",
907 "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.10.tgz",906 "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz",
908 "integrity": "sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==",907 "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==",
909 "cpu": [908 "cpu": [
910 "x64"909 "x64"
911 ],910 ],
...@@ -919,9 +918,9 @@...@@ -919,9 +918,9 @@
919 }918 }
920 },919 },
921 "node_modules/@rolldown/pluginutils": {920 "node_modules/@rolldown/pluginutils": {
922 "version": "1.0.0-rc.10",921 "version": "1.0.0-rc.12",
923 "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.10.tgz",922 "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz",
924 "integrity": "sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==",923 "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==",
925 "license": "MIT"924 "license": "MIT"
926 },925 },
927 "node_modules/@standard-schema/spec": {926 "node_modules/@standard-schema/spec": {
...@@ -987,17 +986,17 @@...@@ -987,17 +986,17 @@
987 }986 }
988 },987 },
989 "node_modules/@vitest/expect": {988 "node_modules/@vitest/expect": {
990 "version": "4.1.0",989 "version": "4.1.2",
991 "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz",990 "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz",
992 "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==",991 "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==",
993 "license": "MIT",992 "license": "MIT",
994 "dependencies": {993 "dependencies": {
995 "@standard-schema/spec": "^1.1.0",994 "@standard-schema/spec": "^1.1.0",
996 "@types/chai": "^5.2.2",995 "@types/chai": "^5.2.2",
997 "@vitest/spy": "4.1.0",996 "@vitest/spy": "4.1.2",
998 "@vitest/utils": "4.1.0",997 "@vitest/utils": "4.1.2",
999 "chai": "^6.2.2",998 "chai": "^6.2.2",
1000 "tinyrainbow": "^3.0.3"999 "tinyrainbow": "^3.1.0"
1001 },1000 },
1002 "funding": {1001 "funding": {
1003 "url": "https://opencollective.com/vitest"1002 "url": "https://opencollective.com/vitest"
...@@ -1013,24 +1012,24 @@...@@ -1013,24 +1012,24 @@
1013 }1012 }
1014 },1013 },
1015 "node_modules/@vitest/pretty-format": {1014 "node_modules/@vitest/pretty-format": {
1016 "version": "4.1.0",1015 "version": "4.1.2",
1017 "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz",1016 "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz",
1018 "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==",1017 "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==",
1019 "license": "MIT",1018 "license": "MIT",
1020 "dependencies": {1019 "dependencies": {
1021 "tinyrainbow": "^3.0.3"1020 "tinyrainbow": "^3.1.0"
1022 },1021 },
1023 "funding": {1022 "funding": {
1024 "url": "https://opencollective.com/vitest"1023 "url": "https://opencollective.com/vitest"
1025 }1024 }
1026 },1025 },
1027 "node_modules/@vitest/runner": {1026 "node_modules/@vitest/runner": {
1028 "version": "4.1.0",1027 "version": "4.1.2",
1029 "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz",1028 "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz",
1030 "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==",1029 "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==",
1031 "license": "MIT",1030 "license": "MIT",
1032 "dependencies": {1031 "dependencies": {
1033 "@vitest/utils": "4.1.0",1032 "@vitest/utils": "4.1.2",
1034 "pathe": "^2.0.3"1033 "pathe": "^2.0.3"
1035 },1034 },
1036 "funding": {1035 "funding": {
...@@ -1038,13 +1037,13 @@...@@ -1038,13 +1037,13 @@
1038 }1037 }
1039 },1038 },
1040 "node_modules/@vitest/snapshot": {1039 "node_modules/@vitest/snapshot": {
1041 "version": "4.1.0",1040 "version": "4.1.2",
1042 "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz",1041 "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz",
1043 "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==",1042 "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==",
1044 "license": "MIT",1043 "license": "MIT",
1045 "dependencies": {1044 "dependencies": {
1046 "@vitest/pretty-format": "4.1.0",1045 "@vitest/pretty-format": "4.1.2",
1047 "@vitest/utils": "4.1.0",1046 "@vitest/utils": "4.1.2",
1048 "magic-string": "^0.30.21",1047 "magic-string": "^0.30.21",
1049 "pathe": "^2.0.3"1048 "pathe": "^2.0.3"
1050 },1049 },
...@@ -1053,23 +1052,23 @@...@@ -1053,23 +1052,23 @@
1053 }1052 }
1054 },1053 },
1055 "node_modules/@vitest/spy": {1054 "node_modules/@vitest/spy": {
1056 "version": "4.1.0",1055 "version": "4.1.2",
1057 "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz",1056 "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz",
1058 "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==",1057 "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==",
1059 "license": "MIT",1058 "license": "MIT",
1060 "funding": {1059 "funding": {
1061 "url": "https://opencollective.com/vitest"1060 "url": "https://opencollective.com/vitest"
1062 }1061 }
1063 },1062 },
1064 "node_modules/@vitest/utils": {1063 "node_modules/@vitest/utils": {
1065 "version": "4.1.0",1064 "version": "4.1.2",
1066 "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz",1065 "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz",
1067 "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==",1066 "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==",
1068 "license": "MIT",1067 "license": "MIT",
1069 "dependencies": {1068 "dependencies": {
1070 "@vitest/pretty-format": "4.1.0",1069 "@vitest/pretty-format": "4.1.2",
1071 "convert-source-map": "^2.0.0",1070 "convert-source-map": "^2.0.0",
1072 "tinyrainbow": "^3.0.3"1071 "tinyrainbow": "^3.1.0"
1073 },1072 },
1074 "funding": {1073 "funding": {
1075 "url": "https://opencollective.com/vitest"1074 "url": "https://opencollective.com/vitest"
...@@ -1257,12 +1256,6 @@...@@ -1257,12 +1256,6 @@
1257 "node": ">=8"1256 "node": ">=8"
1258 }1257 }
1259 },1258 },
1260 "node_modules/devalue": {
1261 "version": "5.6.4",
1262 "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz",
1263 "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==",
1264 "license": "MIT"
1265 },
1266 "node_modules/error-stack-parser": {1259 "node_modules/error-stack-parser": {
1267 "version": "2.1.4",1260 "version": "2.1.4",
1268 "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",1261 "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
...@@ -1952,9 +1945,9 @@...@@ -1952,9 +1945,9 @@
1952 "license": "ISC"1945 "license": "ISC"
1953 },1946 },
1954 "node_modules/picomatch": {1947 "node_modules/picomatch": {
1955 "version": "4.0.3",1948 "version": "4.0.4",
1956 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",1949 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
1957 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",1950 "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
1958 "license": "MIT",1951 "license": "MIT",
1959 "peer": true,1952 "peer": true,
1960 "engines": {1953 "engines": {
...@@ -2056,13 +2049,13 @@...@@ -2056,13 +2049,13 @@
2056 }2049 }
2057 },2050 },
2058 "node_modules/rolldown": {2051 "node_modules/rolldown": {
2059 "version": "1.0.0-rc.10",2052 "version": "1.0.0-rc.12",
2060 "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz",2053 "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz",
2061 "integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==",2054 "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==",
2062 "license": "MIT",2055 "license": "MIT",
2063 "dependencies": {2056 "dependencies": {
2064 "@oxc-project/types": "=0.120.0",2057 "@oxc-project/types": "=0.122.0",
2065 "@rolldown/pluginutils": "1.0.0-rc.10"2058 "@rolldown/pluginutils": "1.0.0-rc.12"
2066 },2059 },
2067 "bin": {2060 "bin": {
2068 "rolldown": "bin/cli.mjs"2061 "rolldown": "bin/cli.mjs"
...@@ -2071,21 +2064,21 @@...@@ -2071,21 +2064,21 @@
2071 "node": "^20.19.0 || >=22.12.0"2064 "node": "^20.19.0 || >=22.12.0"
2072 },2065 },
2073 "optionalDependencies": {2066 "optionalDependencies": {
2074 "@rolldown/binding-android-arm64": "1.0.0-rc.10",2067 "@rolldown/binding-android-arm64": "1.0.0-rc.12",
2075 "@rolldown/binding-darwin-arm64": "1.0.0-rc.10",2068 "@rolldown/binding-darwin-arm64": "1.0.0-rc.12",
2076 "@rolldown/binding-darwin-x64": "1.0.0-rc.10",2069 "@rolldown/binding-darwin-x64": "1.0.0-rc.12",
2077 "@rolldown/binding-freebsd-x64": "1.0.0-rc.10",2070 "@rolldown/binding-freebsd-x64": "1.0.0-rc.12",
2078 "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.10",2071 "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12",
2079 "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.10",2072 "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12",
2080 "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.10",2073 "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12",
2081 "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.10",2074 "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12",
2082 "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.10",2075 "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12",
2083 "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.10",2076 "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12",
2084 "@rolldown/binding-linux-x64-musl": "1.0.0-rc.10",2077 "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12",
2085 "@rolldown/binding-openharmony-arm64": "1.0.0-rc.10",2078 "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12",
2086 "@rolldown/binding-wasm32-wasi": "1.0.0-rc.10",2079 "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12",
2087 "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.10",2080 "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12",
2088 "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.10"2081 "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12"
2089 }2082 }
2090 },2083 },
2091 "node_modules/sax": {2084 "node_modules/sax": {
...@@ -2266,18 +2259,18 @@...@@ -2266,18 +2259,18 @@
2266 }2259 }
2267 },2260 },
2268 "node_modules/vitest": {2261 "node_modules/vitest": {
2269 "version": "4.1.0",2262 "version": "4.1.2",
2270 "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz",2263 "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz",
2271 "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==",2264 "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==",
2272 "license": "MIT",2265 "license": "MIT",
2273 "dependencies": {2266 "dependencies": {
2274 "@vitest/expect": "4.1.0",2267 "@vitest/expect": "4.1.2",
2275 "@vitest/mocker": "4.1.0",2268 "@vitest/mocker": "4.1.2",
2276 "@vitest/pretty-format": "4.1.0",2269 "@vitest/pretty-format": "4.1.2",
2277 "@vitest/runner": "4.1.0",2270 "@vitest/runner": "4.1.2",
2278 "@vitest/snapshot": "4.1.0",2271 "@vitest/snapshot": "4.1.2",
2279 "@vitest/spy": "4.1.0",2272 "@vitest/spy": "4.1.2",
2280 "@vitest/utils": "4.1.0",2273 "@vitest/utils": "4.1.2",
2281 "es-module-lexer": "^2.0.0",2274 "es-module-lexer": "^2.0.0",
2282 "expect-type": "^1.3.0",2275 "expect-type": "^1.3.0",
2283 "magic-string": "^0.30.21",2276 "magic-string": "^0.30.21",
...@@ -2288,8 +2281,8 @@...@@ -2288,8 +2281,8 @@
2288 "tinybench": "^2.9.0",2281 "tinybench": "^2.9.0",
2289 "tinyexec": "^1.0.2",2282 "tinyexec": "^1.0.2",
2290 "tinyglobby": "^0.2.15",2283 "tinyglobby": "^0.2.15",
2291 "tinyrainbow": "^3.0.3",2284 "tinyrainbow": "^3.1.0",
2292 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0",2285 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
2293 "why-is-node-running": "^2.3.0"2286 "why-is-node-running": "^2.3.0"
2294 },2287 },
2295 "bin": {2288 "bin": {
...@@ -2305,13 +2298,13 @@...@@ -2305,13 +2298,13 @@
2305 "@edge-runtime/vm": "*",2298 "@edge-runtime/vm": "*",
2306 "@opentelemetry/api": "^1.9.0",2299 "@opentelemetry/api": "^1.9.0",
2307 "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",2300 "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
2308 "@vitest/browser-playwright": "4.1.0",2301 "@vitest/browser-playwright": "4.1.2",
2309 "@vitest/browser-preview": "4.1.0",2302 "@vitest/browser-preview": "4.1.2",
2310 "@vitest/browser-webdriverio": "4.1.0",2303 "@vitest/browser-webdriverio": "4.1.2",
2311 "@vitest/ui": "4.1.0",2304 "@vitest/ui": "4.1.2",
2312 "happy-dom": "*",2305 "happy-dom": "*",
2313 "jsdom": "*",2306 "jsdom": "*",
2314 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0"2307 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
2315 },2308 },
2316 "peerDependenciesMeta": {2309 "peerDependenciesMeta": {
2317 "@edge-runtime/vm": {2310 "@edge-runtime/vm": {
...@@ -2747,12 +2740,12 @@...@@ -2747,12 +2740,12 @@
2747 }2740 }
2748 },2741 },
2749 "node_modules/vitest/node_modules/@vitest/mocker": {2742 "node_modules/vitest/node_modules/@vitest/mocker": {
2750 "version": "4.1.0",2743 "version": "4.1.2",
2751 "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz",2744 "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz",
2752 "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==",2745 "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==",
2753 "license": "MIT",2746 "license": "MIT",
2754 "dependencies": {2747 "dependencies": {
2755 "@vitest/spy": "4.1.0",2748 "@vitest/spy": "4.1.2",
2756 "estree-walker": "^3.0.3",2749 "estree-walker": "^3.0.3",
2757 "magic-string": "^0.30.21"2750 "magic-string": "^0.30.21"
2758 },2751 },
...@@ -2761,7 +2754,7 @@...@@ -2761,7 +2754,7 @@
2761 },2754 },
2762 "peerDependencies": {2755 "peerDependencies": {
2763 "msw": "^2.4.9",2756 "msw": "^2.4.9",
2764 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0"2757 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
2765 },2758 },
2766 "peerDependenciesMeta": {2759 "peerDependenciesMeta": {
2767 "msw": {2760 "msw": {
...@@ -2787,16 +2780,16 @@...@@ -2787,16 +2780,16 @@
2787 }2780 }
2788 },2781 },
2789 "node_modules/vitest/node_modules/vite": {2782 "node_modules/vitest/node_modules/vite": {
2790 "version": "8.0.1",2783 "version": "8.0.3",
2791 "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.1.tgz",2784 "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz",
2792 "integrity": "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==",2785 "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==",
2793 "license": "MIT",2786 "license": "MIT",
2794 "peer": true,2787 "peer": true,
2795 "dependencies": {2788 "dependencies": {
2796 "lightningcss": "^1.32.0",2789 "lightningcss": "^1.32.0",
2797 "picomatch": "^4.0.3",2790 "picomatch": "^4.0.4",
2798 "postcss": "^8.5.8",2791 "postcss": "^8.5.8",
2799 "rolldown": "1.0.0-rc.10",2792 "rolldown": "1.0.0-rc.12",
2800 "tinyglobby": "^0.2.15"2793 "tinyglobby": "^0.2.15"
2801 },2794 },
2802 "bin": {2795 "bin": {
package.json+1-2
...@@ -7,7 +7,6 @@...@@ -7,7 +7,6 @@
7 "@marko/compiler": "5.39.62",7 "@marko/compiler": "5.39.62",
8 "@types/node": "^24.12.0",8 "@types/node": "^24.12.0",
9 "dashjs": "5.0.3",9 "dashjs": "5.0.3",
10 "devalue": "5.6.4",
11 "esbuild": "0.25.5",10 "esbuild": "0.25.5",
12 "harfbuzzjs": "0.10.1",11 "harfbuzzjs": "0.10.1",
13 "hls.js": "1.6.5",12 "hls.js": "1.6.5",
...@@ -18,7 +17,7 @@...@@ -18,7 +17,7 @@
18 "source-map-support": "0.5.21",17 "source-map-support": "0.5.21",
19 "typescript": "^5.9.3",18 "typescript": "^5.9.3",
20 "unique-names-generator": "4.7.1",19 "unique-names-generator": "4.7.1",
21 "vitest": "^4.0.18",20 "vitest": "4.1.2",
22 "vscode-oniguruma": "2.0.1",21 "vscode-oniguruma": "2.0.1",
23 "vscode-textmate": "9.2.0",22 "vscode-textmate": "9.2.0",
24 "wawoff2": "2.0.1"23 "wawoff2": "2.0.1"
src/blog/drafts/webdev/marko-intro.markodown+1-1
...@@ -330,7 +330,7 @@ client import "./clickable-links.client.ts";...@@ -330,7 +330,7 @@ client import "./clickable-links.client.ts";
330330
331import type { Question } from "#src/q+a/models/Question.ts";331import type { Question } from "#src/q+a/models/Question.ts";
332import { formatQuestionTimestamp, formatQuestionISOTimestamp } from "#src/q+a/format.ts";332import { formatQuestionTimestamp, formatQuestionISOTimestamp } from "#src/q+a/format.ts";
333import { QuestionMarkdown } from "#src/q+a/markdown.tsx";333import { QuestionMarkdown } from "#src/q+a/markdown.ts";
334```334```
335335
336The integration is great, `client import` is quite a magical concept, and I've336The integration is great, `client import` is quite a magical concept, and I've
src/blog/localization.ts created+34
...@@ -0,0 +1,34 @@
1interface LanguageConfig {
2 title: render.Node;
3 return: render.Node;
4 writtenByClover: string;
5 translatedBy: (_: { author: string }) => render.Node;
6 tableOfContents: string;
7}
8type LanguageMap = Record<string, LanguageConfig> & { en: LanguageConfig };
9
10export const languages: LanguageMap = {
11 en: {
12 title: "English",
13 return: "back to the home page",
14 writtenByClover: `clover caruso`, // "written by clover caruso" in other languages
15 translatedBy: ({ author }) => `translated by ${author}`,
16 tableOfContents: "Contents",
17 },
18 ko: {
19 title: "한국어",
20 return: "홈 페이지로 돌아가기",
21 writtenByClover: `clover caruso 작성`,
22 translatedBy: ({ author }) => `${author} 번역`,
23 tableOfContents: "Contents",
24 },
25 es: {
26 title: "Español",
27 return: "volver a la página principal",
28 writtenByClover: "escrito por clover caruso",
29 translatedBy: ({ author }) => `traducido por ${author}`,
30 tableOfContents: "Indice",
31 },
32};
33
34import * as render from "@clo/lib/render";
src/blog/localization.tsx deleted-34
...@@ -1,34 +0,0 @@
1interface LanguageConfig {
2 title: render.Node;
3 return: render.Node;
4 writtenByClover: string;
5 translatedBy: (_: { author: string }) => render.Node;
6 tableOfContents: string;
7}
8type LanguageMap = Record<string, LanguageConfig> & { en: LanguageConfig };
9
10export const languages: LanguageMap = {
11 en: {
12 title: "English",
13 return: "back to the home page",
14 writtenByClover: `clover caruso`, // "written by clover caruso" in other languages
15 translatedBy: ({ author }) => `translated by ${author}`,
16 tableOfContents: "Contents",
17 },
18 ko: {
19 title: "한국어",
20 return: "홈 페이지로 돌아가기",
21 writtenByClover: `clover caruso 작성`,
22 translatedBy: ({ author }) => `${author} 번역`,
23 tableOfContents: "Contents",
24 },
25 es: {
26 title: "Español",
27 return: "volver a la página principal",
28 writtenByClover: "escrito por clover caruso",
29 translatedBy: ({ author }) => `traducido por ${author}`,
30 tableOfContents: "Indice",
31 },
32};
33
34import * as render from "@clo/lib/render";
src/blog/tags/blog-layout.marko+1-1
...@@ -99,7 +99,7 @@ export const markoOnly = true;...@@ -99,7 +99,7 @@ export const markoOnly = true;
9999
100</main>100</main>
101101
102import * as localization from "../localization.tsx";102import * as localization from "../localization.ts";
103import * as markodown from "@clo/markodown";103import * as markodown from "@clo/markodown";
104import Heading from "./heading.marko";104import Heading from "./heading.marko";
105import CodeBlock from "./code-block.marko";105import CodeBlock from "./code-block.marko";
src/file-viewer/cert.pem deleted-23
...@@ -1,23 +0,0 @@
1-----BEGIN CERTIFICATE-----
2MIIDxTCCAq2gAwIBAgIUBaaOXVkkE+6yarNyvzofETb+WLEwDQYJKoZIhvcNAQEL
3BQAwdzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5
4MRUwEwYDVQQKDAxNZWRpYSBTZXJ2ZXIxDzANBgNVBAMMBnplbml0aDEhMB8GCSqG
5SIb3DQEJARYSbWVAcGFwZXJjbG92ZXIubmV0MB4XDTI1MDQyNzIxNTU0MFoXDTM1
6MDQyNTIxNTU0MFowdzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD
7VQQHDARDaXR5MRUwEwYDVQQKDAxNZWRpYSBTZXJ2ZXIxDzANBgNVBAMMBnplbml0
8aDEhMB8GCSqGSIb3DQEJARYSbWVAcGFwZXJjbG92ZXIubmV0MIIBIjANBgkqhkiG
99w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7lLwx8XwsuTeaIxTsHDL+Lx7eblsJ0XylVm
100/iIJS1Mrq6Be9St6vDWK/BWqqAn+MdqzSfLMy8EKazuHKtbTm2vlUIkjw28SoWP
116cRSCLx4hFGbF4tmRO+Bo+/4PpHPnheeolkjJ+CLO87tZ752D9JzjVND+WIj1QO+
12bm+JBIi1TFREPh22/fSZBRpaRgqHcUEhICaiXaufvxQ6eihQfGSe00I7zRzGgnMl
1351xjzkKkXd+r/FwTykd8ScJN25FMVDLsfJR59//geAZXYS25gQ4YL6R8u7ijidlS
14IoDG8N+Fzw7W4yI+y8fIN4W1x/HsjiQ665CuWY3TMYo98OaGwwIDAQABo0kwRzAm
15BgNVHREEHzAdggZ6ZW5pdGiCE25hcy5wYXBlcmNsb3Zlci5uZXQwHQYDVR0OBBYE
16FDXkgNsMYZv1Pr+95RCCk7eHACGOMA0GCSqGSIb3DQEBCwUAA4IBAQB6942odKyD
17TudifxRXbvcVe9LxSd7NimxRZzM5wTgA5KkxQT4CBM2wEPH/7e7Q/8scB9HbH2uP
18f2vixoCM+Z3BWiYHFFk+1pf2myUdiFV2BC9g80txEerRkGLc18V6CdYNJ9wNPkiO
19LW/RzXfEv+sqhaXh8dA46Ruz6SAbmscTMMYW4e9VYR+1p4Sm5UpTxrHzeg21YJKn
20ud8kO1r7RhVgUGzkAzNaIMiBuJqGGdD5yV7Ng5C/DlJ9AAeYu1diM5LkIKjf+/8M
21t/3l4eXS3Lda6+21rDvmfoK4Za6CAhcwgXIpqiRixE2MQNsxZ2XiJBVQHPrh8xYk
22L5fq8KTGFwtd
23-----END CERTIFICATE-----
src/q+a/backend.ts+1-1
...@@ -227,5 +227,5 @@ import { serveAsset } from "#sitegen/assets";...@@ -227,5 +227,5 @@ import { serveAsset } from "#sitegen/assets";
227import type { PendingQuestionData } from "./models/PendingQuestion.ts";227import type { PendingQuestionData } from "./models/PendingQuestion.ts";
228import { Question, QuestionType } from "./models/Question.ts";228import { Question, QuestionType } from "./models/Question.ts";
229import * as view from "#sitegen/view";229import * as view from "#sitegen/view";
230import { getQuestionImage } from "./image.tsx";230import { getQuestionImage } from "./image.ts";
231import { formatQuestionId, questionIdToTimestamp } from "./format.ts";231import { formatQuestionId, questionIdToTimestamp } from "./format.ts";
src/q+a/image.ts created+79
...@@ -0,0 +1,79 @@
1const port = Number(process.env.PORT ?? 3000);
2const width = 768;
3const cacheImageDir = path.resolve(".clover/question_images");
4
5const getBrowser = RefCountedExpirable(
6 () =>
7 playwright.chromium.launch({
8 args: ["--no-sandbox", "--disable-setuid-sandbox"],
9 }),
10 (b) => b.close(),
11);
12
13export async function renderQuestionImage(question: Question) {
14 using sharedBrowser = await getBrowser();
15 const b = sharedBrowser.value;
16 const ctx = await b.newContext({
17 userAgent: "clover-qa-image",
18 deviceScaleFactor: 1.5,
19 });
20 try {
21 const p = await ctx.newPage();
22 await p.setViewportSize({ width, height: 400 });
23 await p.goto(`http://localhost:${port}/q+a/${question.id}`);
24 try {
25 await p.waitForLoadState("networkidle", { timeout: 500 });
26 } catch {}
27 const height = await p.evaluate(() => {
28 document.body.style.background = "#1a1b1c";
29 return document.querySelector("main")!.getBoundingClientRect().height;
30 });
31 return await p.screenshot({
32 type: "png",
33 clip: { x: 0, y: 0, width, height },
34 });
35 } finally {
36 await ctx.close();
37 }
38}
39
40export async function getQuestionImage(
41 question: Question,
42 headOnly: boolean,
43): Promise<Response> {
44 const hash = crypto
45 .createHash("sha1")
46 .update(question.qmid + question.type + question.text)
47 .digest("hex");
48
49 const headers = {
50 "Content-Type": "image/png",
51 "Cache-Control": "public, max-age=31536000",
52 ETag: `"${hash}"`,
53 "Last-Modified": question.date.toUTCString(),
54 };
55
56 if (headOnly) {
57 return new Response(null, { headers });
58 }
59
60 const cachedFilePath = path.join(cacheImageDir, `/${hash}.png`);
61 let buf: Buffer;
62 try {
63 buf = await fs.readFile(cachedFilePath);
64 } catch (e: any) {
65 if (e.code !== "ENOENT") throw e;
66 buf = await renderQuestionImage(question);
67 fs.writeMkdir(cachedFilePath, buf).catch(() => {});
68 }
69
70 return new Response(buf, { headers });
71}
72
73import * as crypto from "node:crypto";
74import * as fs from "#sitegen/fs";
75import * as path from "node:path";
76import { Question } from "#src/q+a/models/Question.ts";
77import { RefCountedExpirable } from "@clo/lib/async";
78import * as view from "#sitegen/view";
79import * as playwright from "playwright";
src/q+a/image.tsx deleted-79
...@@ -1,79 +0,0 @@
1const port = Number(process.env.PORT ?? 3000);
2const width = 768;
3const cacheImageDir = path.resolve(".clover/question_images");
4
5const getBrowser = RefCountedExpirable(
6 () =>
7 playwright.chromium.launch({
8 args: ["--no-sandbox", "--disable-setuid-sandbox"],
9 }),
10 (b) => b.close(),
11);
12
13export async function renderQuestionImage(question: Question) {
14 using sharedBrowser = await getBrowser();
15 const b = sharedBrowser.value;
16 const ctx = await b.newContext({
17 userAgent: "clover-qa-image",
18 deviceScaleFactor: 1.5,
19 });
20 try {
21 const p = await ctx.newPage();
22 await p.setViewportSize({ width, height: 400 });
23 await p.goto(`http://localhost:${port}/q+a/${question.id}`);
24 try {
25 await p.waitForLoadState("networkidle", { timeout: 500 });
26 } catch {}
27 const height = await p.evaluate(() => {
28 document.body.style.background = "#1a1b1c";
29 return document.querySelector("main")!.getBoundingClientRect().height;
30 });
31 return await p.screenshot({
32 type: "png",
33 clip: { x: 0, y: 0, width, height },
34 });
35 } finally {
36 await ctx.close();
37 }
38}
39
40export async function getQuestionImage(
41 question: Question,
42 headOnly: boolean,
43): Promise<Response> {
44 const hash = crypto
45 .createHash("sha1")
46 .update(question.qmid + question.type + question.text)
47 .digest("hex");
48
49 const headers = {
50 "Content-Type": "image/png",
51 "Cache-Control": "public, max-age=31536000",
52 ETag: `"${hash}"`,
53 "Last-Modified": question.date.toUTCString(),
54 };
55
56 if (headOnly) {
57 return new Response(null, { headers });
58 }
59
60 const cachedFilePath = path.join(cacheImageDir, `/${hash}.png`);
61 let buf: Buffer;
62 try {
63 buf = await fs.readFile(cachedFilePath);
64 } catch (e: any) {
65 if (e.code !== "ENOENT") throw e;
66 buf = await renderQuestionImage(question);
67 fs.writeMkdir(cachedFilePath, buf).catch(() => {});
68 }
69
70 return new Response(buf, { headers });
71}
72
73import * as crypto from "node:crypto";
74import * as fs from "#sitegen/fs";
75import * as path from "node:path";
76import { Question } from "#src/q+a/models/Question.ts";
77import { RefCountedExpirable } from "@clo/lib/async";
78import * as view from "#sitegen/view";
79import * as playwright from "playwright";
src/q+a/markdown.ts created+81
...@@ -0,0 +1,81 @@
1function labelledRedaction(contents: string) {
2 return (
3 `<span class="dead">` +
4 (contents === "name"
5 ? "old name"
6 : contents === "username"
7 ? "old username"
8 : contents === "domain"
9 ? "old domain"
10 : null) +
11 `</span>`
12 );
13}
14
15function questionRef(id: string) {
16 if (id.length === 12) id = id.slice(0, 10);
17 const timestamp = questionIdToTimestamp(id);
18 if (!timestamp) {
19 console.error(`Invalid question ref: ${id}`);
20 return `<span class="question-ref">[Invalid Question Ref: #${
21 render.escapeHtmlContent(id)
22 }]</span>`;
23 }
24 return `<a class="custom question-ref" href="/q+a/${id}">${
25 formatQuestionTimestamp(timestamp)
26 }</a>`;
27}
28
29function artifactRef(id: string) {
30 const artifact = artifactMap[id];
31 if (!artifact) {
32 return `<span class="artifact">[Invalid Artifact Ref: @${
33 render.escapeHtmlContent(id)
34 }]</span>`;
35 }
36 const [title, url, type] = artifact;
37 if (!url) {
38 return `<span>${render.escapeHtmlContent(title)}</span>`;
39 }
40 return `<a class="custom artifact artifact-${type}" href="${url}">` +
41 render.escapeHtmlContent(title) +
42 `</a>`;
43}
44
45/** classic esbuild trick: use an impossible to guess key for string magic */
46const uniqueKey = crypto.randomUUID().replaceAll("-", "");
47
48const renderers = {
49 A: artifactRef,
50 Q: questionRef,
51 R: labelledRedaction,
52} as const;
53
54export function renderMarkdown(source: string) {
55 const tagNames: markodown.CloverQuestionExtensions = {
56 question: "q-",
57 artifactRef: uniqueKey + "_A",
58 questionRef: uniqueKey + "_Q",
59 labelledRedaction: uniqueKey + "_R",
60 };
61 const result = markodown.transform({
62 source,
63 markdownOnly: true,
64 cloverExtensions: tagNames,
65 });
66 if (!result.success) {
67 throw new Error(JSON.stringify(result.errors));
68 }
69 return result.text
70 .replace(
71 new RegExp(`<${uniqueKey}_([AQR])>([^<]*)<\\/${uniqueKey}_[AQR]>`, "g"),
72 (_, type: "A" | "Q" | "R", content: string) => {
73 return renderers[type](content);
74 },
75 );
76}
77
78import * as markodown from "@clo/markodown";
79import * as render from "@clo/lib/render";
80import { artifactMap } from "./artifacts.ts";
81import { formatQuestionTimestamp, questionIdToTimestamp } from "./format.ts";
src/q+a/markdown.tsx deleted-81
...@@ -1,81 +0,0 @@
1function labelledRedaction(contents: string) {
2 return (
3 <span class="dead">
4 {contents === "name"
5 ? "old name"
6 : contents === "username"
7 ? "old username"
8 : contents === "domain"
9 ? "old domain"
10 : null}
11 </span>
12 );
13}
14
15function questionRef(id: string) {
16 if (id.length === 12) id = id.slice(0, 10);
17 const timestamp = questionIdToTimestamp(id);
18 if (!timestamp) {
19 console.error(`Invalid question ref: ${id}`);
20 return <span class="question-ref">[Invalid Question Ref: #{id}]</span>;
21 }
22 return (
23 <a class="custom question-ref" href={`/q+a/${id}`}>
24 {formatQuestionTimestamp(timestamp)}
25 </a>
26 );
27}
28
29function artifactRef(id: string) {
30 const artifact = artifactMap[id];
31 if (!artifact) {
32 return <span class="artifact">[Invalid Artifact Ref: @{id}]</span>;
33 }
34 const [title, url, type] = artifact;
35 if (!url) {
36 return <span>{title}</span>;
37 }
38 return (
39 <a class={`custom artifact artifact-${type}`} href={url}>
40 {title}
41 </a>
42 );
43}
44
45/** classic esbuild trick: use an impossible to guess key for string magic */
46const uniqueKey = crypto.randomUUID().replaceAll("-", "");
47
48const renderers = {
49 A: artifactRef,
50 Q: questionRef,
51 R: labelledRedaction,
52} as const;
53
54export function renderMarkdown(source: string) {
55 const tagNames: markodown.CloverQuestionExtensions = {
56 question: "q-",
57 artifactRef: uniqueKey + "_A",
58 questionRef: uniqueKey + "_Q",
59 labelledRedaction: uniqueKey + "_R",
60 };
61 const result = markodown.transform({
62 source,
63 markdownOnly: true,
64 cloverExtensions: tagNames,
65 });
66 if (!result.success) {
67 throw new Error(JSON.stringify(result.errors));
68 }
69 return result.text
70 .replace(
71 new RegExp(`<${uniqueKey}_([AQR])>([^<]*)<\\/${uniqueKey}_[AQR]>`, "g"),
72 (_, type: "A" | "Q" | "R", content: string) => {
73 return render.sync(renderers[type](content)).text;
74 },
75 );
76}
77
78import * as markodown from "@clo/markodown";
79import * as render from "@clo/lib/render";
80import { artifactMap } from "./artifacts.ts";
81import { formatQuestionTimestamp, questionIdToTimestamp } from "./format.ts";
src/q+a/scripts/editor.client.ts created+95
...@@ -0,0 +1,95 @@
1import type { ScriptPayload } from "#src/q+a/views/editor.marko";
2import QuestionRender from "#src/q+a/tags/question.marko";
3
4declare const payload: ScriptPayload;
5const date = new Date(payload.date);
6
7const main = document.getElementById("edit-grid")! as HTMLDivElement;
8const preview = document.getElementById("preview")! as HTMLDivElement;
9const editor = document.getElementById("editor") as HTMLTextAreaElement;
10
11async function updatePreview(text: string) {
12 preview.innerHTML = await QuestionRender.render({
13 question: {
14 id: payload.id,
15 qmid: payload.qmid,
16 text: text,
17 type: payload.type,
18 date,
19 },
20 editor: true,
21 });
22}
23updatePreview(payload.text);
24
25editor.value = payload.text;
26editor.addEventListener("input", () => {
27 updatePreview(editor.value);
28});
29editor.focus();
30
31(globalThis as any).onCommitQuestion = wrapAction(async () => {
32 const text = editor.value;
33 const res = await fetch(`/admin/q+a/${payload.id}`, {
34 method: "PATCH",
35 headers: {
36 Accept: "application/json",
37 "Content-Type": "application/json",
38 },
39 body: JSON.stringify({
40 text,
41 type: payload.type,
42 }),
43 });
44 if (!res.ok) {
45 throw new Error("Failed to update question, status: " + res.status);
46 }
47 if (location.search.includes("return=inbox")) {
48 location.href = "/admin/q+a/inbox";
49 } else {
50 location.href = "/q+a#q" + payload.id;
51 }
52});
53(globalThis as any).onDelete = wrapAction(async () => {
54 if (confirm("Are you sure you want to delete this question?")) {
55 const res = await fetch(`/admin/q+a/${payload.id}`, {
56 method: "DELETE",
57 headers: {
58 Accept: "application/json",
59 },
60 });
61 if (!res.ok) {
62 throw new Error("Failed to delete question, status: " + res.status);
63 }
64 location.href = document.referrer || "/admin/q+a";
65 }
66});
67(globalThis as any).onTypeChange = () => {
68 payload.type = parseInt(
69 (document.getElementById("type") as HTMLSelectElement).value,
70 );
71 updatePreview(view.state.doc.toString());
72};
73
74function wrapAction(cb: () => Promise<void>) {
75 return async () => {
76 main.style.opacity = "0.5";
77 main.style.pointerEvents = "none";
78 const inputs = main.querySelectorAll(
79 "button,select,input",
80 ) as NodeListOf<HTMLButtonElement>;
81 inputs.forEach((b) => {
82 b.disabled = true;
83 });
84 try {
85 await cb();
86 } catch (e: any) {
87 main.style.opacity = "1";
88 main.style.pointerEvents = "auto";
89 inputs.forEach((b) => {
90 b.disabled = false;
91 });
92 alert(e.message);
93 }
94 };
95}
src/q+a/scripts/editor.client.tsx deleted-95
...@@ -1,95 +0,0 @@
1import type { ScriptPayload } from "#src/q+a/views/editor.marko";
2import QuestionRender from "#src/q+a/tags/question.marko";
3
4declare const payload: ScriptPayload;
5const date = new Date(payload.date);
6
7const main = document.getElementById("edit-grid")! as HTMLDivElement;
8const preview = document.getElementById("preview")! as HTMLDivElement;
9const editor = document.getElementById("editor") as HTMLTextAreaElement;
10
11async function updatePreview(text: string) {
12 preview.innerHTML = await QuestionRender.render({
13 question: {
14 id: payload.id,
15 qmid: payload.qmid,
16 text: text,
17 type: payload.type,
18 date,
19 },
20 editor: true,
21 });
22}
23updatePreview(payload.text);
24
25editor.value = payload.text;
26editor.addEventListener("input", () => {
27 updatePreview(editor.value);
28});
29editor.focus();
30
31(globalThis as any).onCommitQuestion = wrapAction(async () => {
32 const text = editor.value;
33 const res = await fetch(`/admin/q+a/${payload.id}`, {
34 method: "PATCH",
35 headers: {
36 Accept: "application/json",
37 "Content-Type": "application/json",
38 },
39 body: JSON.stringify({
40 text,
41 type: payload.type,
42 }),
43 });
44 if (!res.ok) {
45 throw new Error("Failed to update question, status: " + res.status);
46 }
47 if (location.search.includes("return=inbox")) {
48 location.href = "/admin/q+a/inbox";
49 } else {
50 location.href = "/q+a#q" + payload.id;
51 }
52});
53(globalThis as any).onDelete = wrapAction(async () => {
54 if (confirm("Are you sure you want to delete this question?")) {
55 const res = await fetch(`/admin/q+a/${payload.id}`, {
56 method: "DELETE",
57 headers: {
58 Accept: "application/json",
59 },
60 });
61 if (!res.ok) {
62 throw new Error("Failed to delete question, status: " + res.status);
63 }
64 location.href = document.referrer || "/admin/q+a";
65 }
66});
67(globalThis as any).onTypeChange = () => {
68 payload.type = parseInt(
69 (document.getElementById("type") as HTMLSelectElement).value,
70 );
71 updatePreview(view.state.doc.toString());
72};
73
74function wrapAction(cb: () => Promise<void>) {
75 return async () => {
76 main.style.opacity = "0.5";
77 main.style.pointerEvents = "none";
78 const inputs = main.querySelectorAll(
79 "button,select,input",
80 ) as NodeListOf<HTMLButtonElement>;
81 inputs.forEach((b) => {
82 b.disabled = true;
83 });
84 try {
85 await cb();
86 } catch (e: any) {
87 main.style.opacity = "1";
88 main.style.pointerEvents = "auto";
89 inputs.forEach((b) => {
90 b.disabled = false;
91 });
92 alert(e.message);
93 }
94 };
95}
src/q+a/tags/question.marko+1-1
...@@ -39,5 +39,5 @@ export const transitionDate = 1735639200000;...@@ -39,5 +39,5 @@ export const transitionDate = 1735639200000;
3939
40import type { Question } from "#src/q+a/models/Question.ts";40import type { Question } from "#src/q+a/models/Question.ts";
41import { formatQuestionTimestamp, formatQuestionISOTimestamp } from "#src/q+a/format.ts";41import { formatQuestionTimestamp, formatQuestionISOTimestamp } from "#src/q+a/format.ts";
42import * as markdown from "#src/q+a/markdown.tsx";42import * as markdown from "#src/q+a/markdown.ts";
43import { addScript as Script } from "#sitegen"43import { addScript as Script } from "#sitegen"
src/q+a/views/editor.marko+1-2
...@@ -59,9 +59,8 @@ export interface ScriptPayload {...@@ -59,9 +59,8 @@ export interface ScriptPayload {
59 <question editor ...{ question } />59 <question editor ...{ question } />
60 </main>60 </main>
61</div>61</div>
62<html-script>self.payload=${devalue.uneval(payload)}</html-script>62<html-script>self.payload=${JSON.stringify(payload).replaceAll('</script>', '<\\/script>')}</html-script>
63<html-script src="/js/q+a/scripts/editor.js" type="module" />63<html-script src="/js/q+a/scripts/editor.js" type="module" />
6464
65import * as devalue from 'devalue';
66import { type PendingQuestion } from "#src/q+a/models/PendingQuestion.ts";65import { type PendingQuestion } from "#src/q+a/models/PendingQuestion.ts";
67import { Question, QuestionType } from "#src/q+a/models/Question.ts";66import { Question, QuestionType } from "#src/q+a/models/Question.ts";