atproto explorer

switch to codemirror

+252 -60
+6 -1
package.json
··· 36 36 "@atcute/tangled": "^1.0.5", 37 37 "@atcute/tid": "^1.0.2", 38 38 "@atcute/uint8array": "^1.0.4", 39 + "@codemirror/lang-json": "^6.0.2", 40 + "@codemirror/lint": "^6.8.5", 41 + "@codemirror/state": "^6.5.2", 42 + "@fsegurai/codemirror-theme-basic-dark": "^6.2.2", 43 + "@fsegurai/codemirror-theme-basic-light": "^6.2.2", 39 44 "@mary/exif-rm": "jsr:^0.2.2", 40 45 "@skyware/firehose": "^0.5.2", 41 46 "@solidjs/meta": "^0.29.4", 42 47 "@solidjs/router": "^0.15.3", 48 + "codemirror": "^6.0.2", 43 49 "hls.js": "^1.6.11", 44 - "monaco-editor": "^0.52.2", 45 50 "solid-js": "^1.9.9" 46 51 }, 47 52 "packageManager": "pnpm@10.12.2+sha512.a32540185b964ee30bb4e979e405adc6af59226b438ee4cc19f9e8773667a66d302f5bfee60a39d3cac69e35e4b96e708a71dd002b7e9359c4112a1722ac323f",
+184 -8
pnpm-lock.yaml
··· 56 56 '@atcute/uint8array': 57 57 specifier: ^1.0.4 58 58 version: 1.0.4 59 + '@codemirror/lang-json': 60 + specifier: ^6.0.2 61 + version: 6.0.2 62 + '@codemirror/lint': 63 + specifier: ^6.8.5 64 + version: 6.8.5 65 + '@codemirror/state': 66 + specifier: ^6.5.2 67 + version: 6.5.2 68 + '@fsegurai/codemirror-theme-basic-dark': 69 + specifier: ^6.2.2 70 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)(@lezer/highlight@1.2.1) 71 + '@fsegurai/codemirror-theme-basic-light': 72 + specifier: ^6.2.2 73 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)(@lezer/highlight@1.2.1) 59 74 '@mary/exif-rm': 60 75 specifier: jsr:^0.2.2 61 76 version: '@jsr/mary__exif-rm@0.2.2' ··· 68 83 '@solidjs/router': 69 84 specifier: ^0.15.3 70 85 version: 0.15.3(solid-js@1.9.9) 86 + codemirror: 87 + specifier: ^6.0.2 88 + version: 6.0.2 71 89 hls.js: 72 90 specifier: ^1.6.11 73 91 version: 1.6.11 74 - monaco-editor: 75 - specifier: ^0.52.2 76 - version: 0.52.2 77 92 solid-js: 78 93 specifier: ^1.9.9 79 94 version: 1.9.9 ··· 261 276 '@badrap/valita@0.4.6': 262 277 resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} 263 278 engines: {node: '>= 18'} 279 + 280 + '@codemirror/autocomplete@6.18.7': 281 + resolution: {integrity: sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==} 282 + 283 + '@codemirror/commands@6.8.1': 284 + resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==} 285 + 286 + '@codemirror/lang-json@6.0.2': 287 + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} 288 + 289 + '@codemirror/language@6.11.3': 290 + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} 291 + 292 + '@codemirror/lint@6.8.5': 293 + resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} 294 + 295 + '@codemirror/search@6.5.11': 296 + resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} 297 + 298 + '@codemirror/state@6.5.2': 299 + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} 300 + 301 + '@codemirror/view@6.38.2': 302 + resolution: {integrity: sha512-bTWAJxL6EOFLPzTx+O5P5xAO3gTqpatQ2b/ARQ8itfU/v2LlpS3pH2fkL0A3E/Fx8Y2St2KES7ZEV0sHTsSW/A==} 264 303 265 304 '@esbuild/aix-ppc64@0.23.1': 266 305 resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} ··· 562 601 cpu: [x64] 563 602 os: [win32] 564 603 604 + '@fsegurai/codemirror-theme-basic-dark@6.2.2': 605 + resolution: {integrity: sha512-cVK4VheF7ZkuV0sfy20lmH2S7Q2xIfKoqN2HdU5rpGH8mZM2LVG9Tl+oHT0XNPpsWFqNAAKLzjYFw0IPX95Biw==} 606 + peerDependencies: 607 + '@codemirror/language': ^6.0.0 608 + '@codemirror/state': ^6.0.0 609 + '@codemirror/view': ^6.0.0 610 + '@lezer/highlight': ^1.0.0 611 + 612 + '@fsegurai/codemirror-theme-basic-light@6.2.2': 613 + resolution: {integrity: sha512-zFtJ6VwwEeZ/HAXMYdcJz6+DdW1aQkngFwbD3diku79cctpTglCWH49KRFO8Mifjzwylsynm7dLyOUnGhIu0NQ==} 614 + peerDependencies: 615 + '@codemirror/language': ^6.0.0 616 + '@codemirror/state': ^6.0.0 617 + '@codemirror/view': ^6.0.0 618 + '@lezer/highlight': ^1.0.0 619 + 565 620 '@iconify-json/lucide@1.2.66': 566 621 resolution: {integrity: sha512-TrhmfThWY2FHJIckjz7g34gUx3+cmja61DcHNdmu0rVDBQHIjPMYO1O8mMjoDSqIXEllz9wDZxCqT3lFuI+f/A==} 567 622 ··· 599 654 '@jsr/mary__exif-rm@0.2.2': 600 655 resolution: {integrity: sha512-+ZpLaC+1CyqWhH608Sqd6/yTG0pOlokn2tCXha7s1SMQ+GLKo4Nn/PskTeeP9Pt+6gNYSu6ednoSlRvXb2ZGxg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__exif-rm/0.2.2.tgz} 601 656 657 + '@lezer/common@1.2.3': 658 + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} 659 + 660 + '@lezer/highlight@1.2.1': 661 + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} 662 + 663 + '@lezer/json@1.0.3': 664 + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} 665 + 666 + '@lezer/lr@1.4.2': 667 + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} 668 + 669 + '@marijn/find-cluster-break@1.0.2': 670 + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} 671 + 602 672 '@noble/secp256k1@2.3.0': 603 673 resolution: {integrity: sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==} 604 674 ··· 859 929 resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} 860 930 engines: {node: '>=18'} 861 931 932 + codemirror@6.0.2: 933 + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} 934 + 862 935 confbox@0.1.8: 863 936 resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} 864 937 ··· 867 940 868 941 convert-source-map@2.0.0: 869 942 resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 943 + 944 + crelt@1.0.6: 945 + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} 870 946 871 947 csstype@3.1.3: 872 948 resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} ··· 1067 1143 mlly@1.8.0: 1068 1144 resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} 1069 1145 1070 - monaco-editor@0.52.2: 1071 - resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} 1072 - 1073 1146 ms@2.1.3: 1074 1147 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1075 1148 ··· 1219 1292 resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1220 1293 engines: {node: '>=0.10.0'} 1221 1294 1295 + style-mod@4.1.2: 1296 + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} 1297 + 1222 1298 tailwindcss@4.1.12: 1223 1299 resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} 1224 1300 ··· 1320 1396 vite: 1321 1397 optional: true 1322 1398 1399 + w3c-keyname@2.2.8: 1400 + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} 1401 + 1323 1402 yallist@3.1.1: 1324 1403 resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 1325 1404 ··· 1556 1635 1557 1636 '@badrap/valita@0.4.6': {} 1558 1637 1638 + '@codemirror/autocomplete@6.18.7': 1639 + dependencies: 1640 + '@codemirror/language': 6.11.3 1641 + '@codemirror/state': 6.5.2 1642 + '@codemirror/view': 6.38.2 1643 + '@lezer/common': 1.2.3 1644 + 1645 + '@codemirror/commands@6.8.1': 1646 + dependencies: 1647 + '@codemirror/language': 6.11.3 1648 + '@codemirror/state': 6.5.2 1649 + '@codemirror/view': 6.38.2 1650 + '@lezer/common': 1.2.3 1651 + 1652 + '@codemirror/lang-json@6.0.2': 1653 + dependencies: 1654 + '@codemirror/language': 6.11.3 1655 + '@lezer/json': 1.0.3 1656 + 1657 + '@codemirror/language@6.11.3': 1658 + dependencies: 1659 + '@codemirror/state': 6.5.2 1660 + '@codemirror/view': 6.38.2 1661 + '@lezer/common': 1.2.3 1662 + '@lezer/highlight': 1.2.1 1663 + '@lezer/lr': 1.4.2 1664 + style-mod: 4.1.2 1665 + 1666 + '@codemirror/lint@6.8.5': 1667 + dependencies: 1668 + '@codemirror/state': 6.5.2 1669 + '@codemirror/view': 6.38.2 1670 + crelt: 1.0.6 1671 + 1672 + '@codemirror/search@6.5.11': 1673 + dependencies: 1674 + '@codemirror/state': 6.5.2 1675 + '@codemirror/view': 6.38.2 1676 + crelt: 1.0.6 1677 + 1678 + '@codemirror/state@6.5.2': 1679 + dependencies: 1680 + '@marijn/find-cluster-break': 1.0.2 1681 + 1682 + '@codemirror/view@6.38.2': 1683 + dependencies: 1684 + '@codemirror/state': 6.5.2 1685 + crelt: 1.0.6 1686 + style-mod: 4.1.2 1687 + w3c-keyname: 2.2.8 1688 + 1559 1689 '@esbuild/aix-ppc64@0.23.1': 1560 1690 optional: true 1561 1691 ··· 1706 1836 '@esbuild/win32-x64@0.25.9': 1707 1837 optional: true 1708 1838 1839 + '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)(@lezer/highlight@1.2.1)': 1840 + dependencies: 1841 + '@codemirror/language': 6.11.3 1842 + '@codemirror/state': 6.5.2 1843 + '@codemirror/view': 6.38.2 1844 + '@lezer/highlight': 1.2.1 1845 + 1846 + '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)(@lezer/highlight@1.2.1)': 1847 + dependencies: 1848 + '@codemirror/language': 6.11.3 1849 + '@codemirror/state': 6.5.2 1850 + '@codemirror/view': 6.38.2 1851 + '@lezer/highlight': 1.2.1 1852 + 1709 1853 '@iconify-json/lucide@1.2.66': 1710 1854 dependencies: 1711 1855 '@iconify/types': 2.0.0 ··· 1758 1902 1759 1903 '@jsr/mary__exif-rm@0.2.2': {} 1760 1904 1905 + '@lezer/common@1.2.3': {} 1906 + 1907 + '@lezer/highlight@1.2.1': 1908 + dependencies: 1909 + '@lezer/common': 1.2.3 1910 + 1911 + '@lezer/json@1.0.3': 1912 + dependencies: 1913 + '@lezer/common': 1.2.3 1914 + '@lezer/highlight': 1.2.1 1915 + '@lezer/lr': 1.4.2 1916 + 1917 + '@lezer/lr@1.4.2': 1918 + dependencies: 1919 + '@lezer/common': 1.2.3 1920 + 1921 + '@marijn/find-cluster-break@1.0.2': {} 1922 + 1761 1923 '@noble/secp256k1@2.3.0': {} 1762 1924 1763 1925 '@rollup/rollup-android-arm-eabi@4.50.0': ··· 1966 2128 1967 2129 chownr@3.0.0: {} 1968 2130 2131 + codemirror@6.0.2: 2132 + dependencies: 2133 + '@codemirror/autocomplete': 6.18.7 2134 + '@codemirror/commands': 6.8.1 2135 + '@codemirror/language': 6.11.3 2136 + '@codemirror/lint': 6.8.5 2137 + '@codemirror/search': 6.5.11 2138 + '@codemirror/state': 6.5.2 2139 + '@codemirror/view': 6.38.2 2140 + 1969 2141 confbox@0.1.8: {} 1970 2142 1971 2143 confbox@0.2.2: {} 1972 2144 1973 2145 convert-source-map@2.0.0: {} 2146 + 2147 + crelt@1.0.6: {} 1974 2148 1975 2149 csstype@3.1.3: {} 1976 2150 ··· 2164 2338 pkg-types: 1.3.1 2165 2339 ufo: 1.6.1 2166 2340 2167 - monaco-editor@0.52.2: {} 2168 - 2169 2341 ms@2.1.3: {} 2170 2342 2171 2343 nanoevents@9.1.0: {} ··· 2269 2441 2270 2442 source-map-js@1.2.1: {} 2271 2443 2444 + style-mod@4.1.2: {} 2445 + 2272 2446 tailwindcss@4.1.12: {} 2273 2447 2274 2448 tapable@2.2.3: {} ··· 2343 2517 vitefu@1.1.1(vite@7.1.4(@types/node@22.13.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.19.2)): 2344 2518 optionalDependencies: 2345 2519 vite: 7.1.4(@types/node@22.13.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.19.2) 2520 + 2521 + w3c-keyname@2.2.8: {} 2346 2522 2347 2523 yallist@3.1.1: {} 2348 2524
+14 -23
src/components/create.tsx
··· 1 1 import { createSignal, Show } from "solid-js"; 2 2 import { Client } from "@atcute/client"; 3 3 import { agent } from "../components/login.jsx"; 4 - import { editor, Editor } from "../components/editor.jsx"; 5 - import * as monaco from "monaco-editor"; 4 + import { Editor, editorView } from "../components/editor.jsx"; 6 5 import Tooltip from "./tooltip.jsx"; 7 6 import { useNavigate, useParams } from "@solidjs/router"; 8 7 import { remove } from "@mary/exif-rm"; ··· 17 16 const [openDialog, setOpenDialog] = createSignal(false); 18 17 const [notice, setNotice] = createSignal(""); 19 18 const [uploading, setUploading] = createSignal(false); 20 - let model: monaco.editor.IModel; 21 19 let formRef!: HTMLFormElement; 22 20 23 21 const placeholder = () => { ··· 44 42 const validate = formData.get("validate")?.toString(); 45 43 let record: any; 46 44 try { 47 - record = JSON.parse(model.getValue()); 45 + record = JSON.parse(editorView.state.doc.toString()); 48 46 } catch (e: any) { 49 47 setNotice(e.message); 50 48 return; ··· 71 69 }; 72 70 73 71 const editRecord = async (formData: FormData) => { 74 - const record = model.getValue(); 72 + const record = editorView.state.doc.toString(); 75 73 const validate = 76 74 formData.get("validate")?.toString() === "true" ? true 77 75 : formData.get("validate")?.toString() === "false" ? false ··· 79 77 if (!record) return; 80 78 const rpc = new Client({ handler: agent()! }); 81 79 try { 82 - const editedRecord = JSON.parse(record.toString()); 80 + const editedRecord = JSON.parse(record); 83 81 if (formData.get("recreate")) { 84 82 const res = await rpc.post("com.atproto.repo.applyWrites", { 85 83 input: { ··· 155 153 setNotice(res.data.error); 156 154 return; 157 155 } 158 - editor.executeEdits("editor", [ 159 - { 160 - range: editor.getSelection() as monaco.IRange, 161 - text: JSON.stringify(res.data.blob, null, 2), 156 + editorView.dispatch({ 157 + changes: { 158 + from: editorView.state.selection.main.head, 159 + insert: JSON.stringify(res.data.blob, null, 2), 162 160 }, 163 - ]); 164 - editor.trigger("editor", "editor.action.formatDocument", {}); 165 - }; 166 - 167 - const createModel = () => { 168 - if (!model) 169 - model = monaco.editor.createModel( 170 - JSON.stringify(props.create ? placeholder() : props.record, null, 2), 171 - "json", 172 - ); 161 + }); 173 162 }; 174 163 175 164 return ( 176 165 <> 177 - <Modal open={openDialog()} onClose={() => setOpenDialog(false)}> 166 + <Modal open={openDialog()} onClose={() => setOpenDialog(false)} closeOnClick={false}> 178 167 <div class="dark:bg-dark-800/70 dark:shadow-dark-900/80 absolute top-12 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-200/70 p-2 text-neutral-900 shadow-md backdrop-blur-xs transition-opacity duration-300 sm:w-xl sm:p-4 lg:w-[48rem] dark:border-neutral-700 dark:text-neutral-200 starting:opacity-0"> 179 168 <div class="mb-2 flex w-full justify-between"> 180 169 <div class="flex items-center gap-1 font-semibold"> ··· 251 240 </div> 252 241 </div> 253 242 </div> 254 - <Editor model={model!} /> 243 + <Editor 244 + content={JSON.stringify(props.create ? placeholder() : props.record, null, 2)} 245 + /> 255 246 <div class="flex flex-col gap-2"> 256 247 <Show when={notice()}> 257 248 <div class="text-red-500 dark:text-red-400">{notice()}</div> ··· 283 274 <button 284 275 class={`flex items-center p-1 hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700 ${props.create ? "rounded-lg" : "rounded-sm"}`} 285 276 onclick={() => { 286 - createModel(); 277 + setNotice(""); 287 278 setOpenDialog(true); 288 279 }} 289 280 >
+46 -27
src/components/editor.tsx
··· 1 - import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; 2 - import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; 3 - import * as monaco from "monaco-editor"; 4 - import { onMount } from "solid-js"; 1 + import { onCleanup, onMount } from "solid-js"; 2 + import { basicSetup, EditorView } from "codemirror"; 3 + import { json, jsonParseLinter } from "@codemirror/lang-json"; 4 + import { linter } from "@codemirror/lint"; 5 + import { basicLight } from "@fsegurai/codemirror-theme-basic-light"; 6 + import { basicDark } from "@fsegurai/codemirror-theme-basic-dark"; 7 + import { Compartment } from "@codemirror/state"; 8 + 9 + export let editorView: EditorView; 5 10 6 - const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 1; 11 + const Editor = (props: { content: string }) => { 12 + let editorDiv!: HTMLDivElement; 13 + let themeColor = new Compartment(); 7 14 8 - self.MonacoEnvironment = { 9 - getWorker(_, label) { 10 - if (label === "json") return new jsonWorker(); 11 - return new editorWorker(); 12 - }, 13 - }; 15 + const themeEvent = () => { 16 + editorView.dispatch({ 17 + effects: themeColor.reconfigure( 18 + window.matchMedia("(prefers-color-scheme: dark)").matches ? basicDark : basicLight, 19 + ), 20 + }); 21 + }; 14 22 15 - let editor: monaco.editor.IStandaloneCodeEditor; 23 + onMount(() => { 24 + const theme = EditorView.theme({ 25 + ".cm-content": { 26 + fontFamily: "'Roboto Mono', monospace", 27 + fontSize: "12px", 28 + }, 29 + ".cm-scroller": { 30 + overflow: "auto", 31 + maxHeight: "20rem", 32 + }, 33 + }); 16 34 17 - const Editor = (props: { model: monaco.editor.IModel }) => { 18 - let editorDiv!: HTMLDivElement; 35 + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeEvent); 19 36 20 - onMount(() => { 21 - editor = monaco.editor.create(editorDiv, { 22 - minimap: { enabled: false }, 23 - theme: document.documentElement.classList.contains("dark") ? "vs-dark" : "vs", 24 - model: props.model, 25 - wordWrap: "on", 26 - automaticLayout: true, 27 - fontFamily: "Roboto Mono", 28 - lineNumbers: isTouchDevice ? "off" : "on", 29 - fontSize: 12, 37 + editorView = new EditorView({ 38 + doc: props.content, 39 + parent: editorDiv, 40 + extensions: [ 41 + basicSetup, 42 + theme, 43 + json(), 44 + linter(jsonParseLinter()), 45 + themeColor.of(document.documentElement.classList.contains("dark") ? basicDark : basicLight), 46 + ], 30 47 }); 31 48 }); 32 49 33 - return ( 34 - <div ref={editorDiv} class="dark:shadow-dark-900/80 h-[20rem] shadow-sm sm:h-[24rem]"></div> 50 + onCleanup(() => 51 + window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeEvent), 35 52 ); 53 + 54 + return <div ref={editorDiv} class="dark:shadow-dark-900/80 shadow-sm"></div>; 36 55 }; 37 56 38 - export { Editor, editor }; 57 + export { Editor };
+2 -1
src/components/modal.tsx
··· 3 3 export interface ModalProps extends Pick<ComponentProps<"svg">, "children"> { 4 4 open?: boolean; 5 5 onClose?: () => void; 6 + closeOnClick?: boolean; 6 7 } 7 8 8 9 export const Modal = (props: ModalProps) => { ··· 18 19 onCleanup(() => node.close()); 19 20 }} 20 21 onClick={(ev) => { 21 - if (ev.target === ev.currentTarget) { 22 + if ((props.closeOnClick ?? true) && ev.target === ev.currentTarget) { 22 23 if (props.onClose) props.onClose(); 23 24 } 24 25 }}