From 312eabddec91b9ffb49ecd0ace31fa7e38787012 Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Tue, 18 Aug 2026 20:25:59 +0100 Subject: [PATCH 1/9] codemirror: add onedark theme and dedupe deps --- package.json | 2 +- src/routes/playground/Editor.svelte | 17 ++++++------- yarn.lock | 38 +++++++++++++++++++---------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 56548dc..4c848ce 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@codemirror/lang-python": "^6.2.1", - "@codemirror/view": "^6.43.1", + "@codemirror/theme-one-dark": "^6.1.3", "@eslint/js": "^10.0.1", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.49.1", diff --git a/src/routes/playground/Editor.svelte b/src/routes/playground/Editor.svelte index f1bfac2..43e7b2a 100644 --- a/src/routes/playground/Editor.svelte +++ b/src/routes/playground/Editor.svelte @@ -9,21 +9,18 @@ let loading = $state(true); onMount(async () => { - const [{ basicSetup }, { EditorView }, { python }] = await Promise.all([ - import("codemirror"), - import("@codemirror/view"), - import("@codemirror/lang-python"), - ]); + const [{ basicSetup, EditorView }, { python }, { oneDark }] = + await Promise.all([ + import("codemirror"), + import("@codemirror/lang-python"), + import("@codemirror/theme-one-dark"), + ]); loading = false; - const theme = EditorView.baseTheme({ - "&": { height: "100%", "max-height": "100%", width: "100%" }, - }); - const view = new EditorView({ doc: value, - extensions: [basicSetup, theme, python()], + extensions: [basicSetup, oneDark, python()], parent: element, dispatchTransactions: (txs, view) => { diff --git a/yarn.lock b/yarn.lock index c982be4..3eb2159 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,8 +43,8 @@ __metadata: linkType: hard "@codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.8.0": - version: 6.12.3 - resolution: "@codemirror/language@npm:6.12.3" + version: 6.12.4 + resolution: "@codemirror/language@npm:6.12.4" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.23.0" @@ -52,7 +52,7 @@ __metadata: "@lezer/highlight": "npm:^1.0.0" "@lezer/lr": "npm:^1.0.0" style-mod: "npm:^4.0.0" - checksum: 10c0/682d476f35f187a8d2f59f9b2e561045353c87736e5774b5b8dd22ade7deb3c85b9d3cdde988b00aafb345732144c134adf45dadab067430aaee159abcc456a4 + checksum: 10c0/1b704a66f618d96eddf5937a41996e1ab42a70b5333a90b768e5539c25b7ab822e91a93e66b14d47a886cd73b3e3e22ddf77b8f11fb2f496ebd7ba010f3c4deb languageName: node linkType: hard @@ -78,24 +78,36 @@ __metadata: languageName: node linkType: hard -"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.6.0": - version: 6.6.0 - resolution: "@codemirror/state@npm:6.6.0" +"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.6.0, @codemirror/state@npm:^6.7.0": + version: 6.7.1 + resolution: "@codemirror/state@npm:6.7.1" dependencies: "@marijn/find-cluster-break": "npm:^1.0.0" - checksum: 10c0/4e5d6ddd28fc642e8d749fe80a7dd278afd547e4d93c562eaab6f23bdf25bb12a94226db3efab6321d458c531eb6d357184809f3704aee419fe3230d53f16e24 + checksum: 10c0/2490b87759d1f4f27f4573a084414bf30bf9ed5dccaa75485334a2d132c039f2255e3bca564fb07c6760a16bf86cc29532475d9fb75cfdb0771b330fe71be4e7 languageName: node linkType: hard -"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.37.0, @codemirror/view@npm:^6.42.0, @codemirror/view@npm:^6.43.1": - version: 6.43.1 - resolution: "@codemirror/view@npm:6.43.1" +"@codemirror/theme-one-dark@npm:^6.1.3": + version: 6.1.3 + resolution: "@codemirror/theme-one-dark@npm:6.1.3" dependencies: - "@codemirror/state": "npm:^6.6.0" + "@codemirror/language": "npm:^6.0.0" + "@codemirror/state": "npm:^6.0.0" + "@codemirror/view": "npm:^6.0.0" + "@lezer/highlight": "npm:^1.0.0" + checksum: 10c0/de8483c69911bcd61a19679384de663ced9c8bed3c776f08581a8b724e9f456a17053b1cf6e9d1f2a475fa6bc42e905ec8ba1ee0a8b55213d18087d9d9150317 + languageName: node + linkType: hard + +"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.37.0, @codemirror/view@npm:^6.42.0": + version: 6.43.8 + resolution: "@codemirror/view@npm:6.43.8" + dependencies: + "@codemirror/state": "npm:^6.7.0" crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10c0/1ef96bfc756703f6675957793a3ea0f9aef8b2014172c15fc4063b58cefd6974688148eb25c1f2277d6b59d7ca9cb7f66d05e7b6e8c15d68bf050795c902fc18 + checksum: 10c0/6cbd2254ee31024fa204519602fe916776d8a4952100cc2bcc7a683e69641ef7eaab0a3af754b2204efe9cd4c7f7f4f6e3feeccd703ad483e7aa0138371021da languageName: node linkType: hard @@ -2062,7 +2074,7 @@ __metadata: resolution: "purepy-website@workspace:." dependencies: "@codemirror/lang-python": "npm:^6.2.1" - "@codemirror/view": "npm:^6.43.1" + "@codemirror/theme-one-dark": "npm:^6.1.3" "@eslint/js": "npm:^10.0.1" "@sveltejs/adapter-static": "npm:^3.0.10" "@sveltejs/kit": "npm:^2.49.1" From eae67cfbd9c1e36f39cbef8b2c143d2bb140fb7f Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Tue, 18 Aug 2026 21:57:35 +0100 Subject: [PATCH 2/9] layout: move landing page inside its own group, so playground doesn't inherit its layout --- src/routes/{ => (home)}/+layout.svelte | 0 src/routes/{ => (home)}/+page.svelte | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/routes/{ => (home)}/+layout.svelte (100%) rename src/routes/{ => (home)}/+page.svelte (100%) diff --git a/src/routes/+layout.svelte b/src/routes/(home)/+layout.svelte similarity index 100% rename from src/routes/+layout.svelte rename to src/routes/(home)/+layout.svelte diff --git a/src/routes/+page.svelte b/src/routes/(home)/+page.svelte similarity index 100% rename from src/routes/+page.svelte rename to src/routes/(home)/+page.svelte From fa9d13f0e24ff1cf1ad83f21ba661a2ed8b7cc16 Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Tue, 18 Aug 2026 22:02:11 +0100 Subject: [PATCH 3/9] layout: install tailwind --- package.json | 2 + src/app.css | 1 + src/routes/+layout.svelte | 6 + vite.config.ts | 3 +- yarn.lock | 386 +++++++++++++++++++++++++++++++++++++- 5 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 src/app.css create mode 100644 src/routes/+layout.svelte diff --git a/package.json b/package.json index 4c848ce..74c730a 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.49.1", "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@tailwindcss/vite": "^4.3.3", "@types/node": "^24", "codemirror": "^6.0.2", "eslint": "^10.4.1", @@ -30,6 +31,7 @@ "pyodide": "^314.0.0", "svelte": "^5.56.3", "svelte-check": "^4.3.4", + "tailwindcss": "^4.3.3", "typescript": "^5.9.3", "typescript-eslint": "^8.60.1", "vite": "^7.2.6", diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/src/app.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..1ed3806 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,6 @@ + + +{@render children()} diff --git a/vite.config.ts b/vite.config.ts index 4d2bed6..c309c9d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,5 @@ import { sveltekit } from "@sveltejs/kit/vite"; +import tailwindcss from "@tailwindcss/vite"; import { dirname, join } from "path"; import { fileURLToPath } from "url"; import { defineConfig } from "vite"; @@ -28,5 +29,5 @@ export function viteStaticCopyPyodide() { export default defineConfig({ optimizeDeps: { exclude: ["pyodide"] }, - plugins: [sveltekit(), viteStaticCopyPyodide()], + plugins: [tailwindcss(), sveltekit(), viteStaticCopyPyodide()], }); diff --git a/yarn.lock b/yarn.lock index 3eb2159..9974e3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -111,6 +111,34 @@ __metadata: languageName: node linkType: hard +"@emnapi/core@npm:^1.11.1": + version: 1.11.3 + resolution: "@emnapi/core@npm:1.11.3" + dependencies: + "@emnapi/wasi-threads": "npm:1.2.3" + tslib: "npm:^2.4.0" + checksum: 10c0/4ca08d349a82d5d2887ccc9e12df630877b0412ddcd59b9faee61e3c3947ccead27a18257a18bfe17abdf2b0709857808ad75d423ac49edd50c32fb140a7ed6e + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.11.1": + version: 1.11.3 + resolution: "@emnapi/runtime@npm:1.11.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/a00f1020fefb9d4145c367f93a9fddb383a00da8ffd7871e20b19659890379b83aeecb9f84d7d0eda5456343f4a09eb05b0acb5153b0d3d889539dedb1ed87c3 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.2.3, @emnapi/wasi-threads@npm:^1.2.2": + version: 1.2.3 + resolution: "@emnapi/wasi-threads@npm:1.2.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/5aed84bc5dbe867973b20406ca1ed95661a4892ecaef80378fdf2d37424b3f7b9c428df8a3e1d82b783a3345a530032bf049e699e1f113ea52203c3e9b4e6ce5 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.27.7": version: 0.27.7 resolution: "@esbuild/aix-ppc64@npm:0.27.7" @@ -429,7 +457,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/remapping@npm:^2.3.4": +"@jridgewell/remapping@npm:^2.3.4, @jridgewell/remapping@npm:^2.3.5": version: 2.3.5 resolution: "@jridgewell/remapping@npm:2.3.5" dependencies: @@ -506,6 +534,18 @@ __metadata: languageName: node linkType: hard +"@napi-rs/wasm-runtime@npm:^1.1.4": + version: 1.2.2 + resolution: "@napi-rs/wasm-runtime@npm:1.2.2" + dependencies: + "@tybys/wasm-util": "npm:^0.10.3" + peerDependencies: + "@emnapi/core": ^1.7.1 || ^2.0.0-alpha.3 + "@emnapi/runtime": ^1.7.1 || ^2.0.0-alpha.3 + checksum: 10c0/670ff8359761660f58d95a29fe22ac959d2c295675144fe9bc35118b0e723d1e0ac192df9896ba428eb13930cf0893b632606b70ccaea259fd6eca1836c2eb09 + languageName: node + linkType: hard + "@polka/url@npm:^1.0.0-next.24": version: 1.0.0-next.29 resolution: "@polka/url@npm:1.0.0-next.29" @@ -782,6 +822,179 @@ __metadata: languageName: node linkType: hard +"@tailwindcss/node@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/node@npm:4.3.3" + dependencies: + "@jridgewell/remapping": "npm:^2.3.5" + enhanced-resolve: "npm:^5.24.1" + jiti: "npm:^2.7.0" + lightningcss: "npm:1.32.0" + magic-string: "npm:^0.30.21" + source-map-js: "npm:^1.2.1" + tailwindcss: "npm:4.3.3" + checksum: 10c0/0123669be5b3e78d42b0e10cb34d547fb8574c8e7dc7a97a141c8a4ab4215852b5b1b99b97763e898e1da1493ee8424cb72e28225cc4876828c95156f94d7f0e + languageName: node + linkType: hard + +"@tailwindcss/oxide-android-arm64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-android-arm64@npm:4.3.3" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-arm64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.3.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-x64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-darwin-x64@npm:4.3.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-freebsd-x64@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.3.3" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-musl@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.3.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-gnu@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.3.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-musl@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.3.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-wasm32-wasi@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.3.3" + dependencies: + "@emnapi/core": "npm:^1.11.1" + "@emnapi/runtime": "npm:^1.11.1" + "@emnapi/wasi-threads": "npm:^1.2.2" + "@napi-rs/wasm-runtime": "npm:^1.1.4" + "@tybys/wasm-util": "npm:^0.10.2" + tslib: "npm:^2.8.1" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-x64-msvc@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.3.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide@npm:4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/oxide@npm:4.3.3" + dependencies: + "@tailwindcss/oxide-android-arm64": "npm:4.3.3" + "@tailwindcss/oxide-darwin-arm64": "npm:4.3.3" + "@tailwindcss/oxide-darwin-x64": "npm:4.3.3" + "@tailwindcss/oxide-freebsd-x64": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.3.3" + "@tailwindcss/oxide-linux-arm64-musl": "npm:4.3.3" + "@tailwindcss/oxide-linux-x64-gnu": "npm:4.3.3" + "@tailwindcss/oxide-linux-x64-musl": "npm:4.3.3" + "@tailwindcss/oxide-wasm32-wasi": "npm:4.3.3" + "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.3.3" + "@tailwindcss/oxide-win32-x64-msvc": "npm:4.3.3" + dependenciesMeta: + "@tailwindcss/oxide-android-arm64": + optional: true + "@tailwindcss/oxide-darwin-arm64": + optional: true + "@tailwindcss/oxide-darwin-x64": + optional: true + "@tailwindcss/oxide-freebsd-x64": + optional: true + "@tailwindcss/oxide-linux-arm-gnueabihf": + optional: true + "@tailwindcss/oxide-linux-arm64-gnu": + optional: true + "@tailwindcss/oxide-linux-arm64-musl": + optional: true + "@tailwindcss/oxide-linux-x64-gnu": + optional: true + "@tailwindcss/oxide-linux-x64-musl": + optional: true + "@tailwindcss/oxide-wasm32-wasi": + optional: true + "@tailwindcss/oxide-win32-arm64-msvc": + optional: true + "@tailwindcss/oxide-win32-x64-msvc": + optional: true + checksum: 10c0/6c38b25226dad252347451ed8953a998432b51f1e068b25e021215b886ccf8bcf3af58faf5f4ab50368091e657ca2fa341df1deca6101651eddb567122f2acb5 + languageName: node + linkType: hard + +"@tailwindcss/vite@npm:^4.3.3": + version: 4.3.3 + resolution: "@tailwindcss/vite@npm:4.3.3" + dependencies: + "@tailwindcss/node": "npm:4.3.3" + "@tailwindcss/oxide": "npm:4.3.3" + tailwindcss: "npm:4.3.3" + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + checksum: 10c0/bb1fe12f7a0bb8f6d6142f6bb2e96060ee853902314ef838e8d7f50cf30cc4b07adb7c2c86be2164e0d62eb581b8cc4df2d9aa13b4449f597a67f73c5772d549 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.2, @tybys/wasm-util@npm:^0.10.3": + version: 0.10.3 + resolution: "@tybys/wasm-util@npm:0.10.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/fd2bd2a79c6cd8c79ed1cf7a0fa375c64589264c88a27acaf9756d556b453ea222b62a4f68dd2fbb8b3a78b6bab3b1f4fb2431b6afc6aeda8344b53a521a1cd3 + languageName: node + linkType: hard + "@types/cookie@npm:^0.6.0": version: 0.6.0 resolution: "@types/cookie@npm:0.6.0" @@ -1178,6 +1391,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.3": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + "devalue@npm:^5.8.1": version: 5.8.1 resolution: "devalue@npm:5.8.1" @@ -1185,6 +1405,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.24.1": + version: 5.24.5 + resolution: "enhanced-resolve@npm:5.24.5" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.3.3" + checksum: 10c0/76c32ce5485ecea0ef808c9289bc6f7c49ce85142943598d89a23a846f2909556d3dc6db97a007442a48c185baad5514c1c76d20a2656497f9511d13ccfab73b + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1622,7 +1852,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -1705,6 +1935,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^2.7.0": + version: 2.7.0 + resolution: "jiti@npm:2.7.0" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/1b1e2310a490dce1aeea3da5f5dfe18273516c20ce48be2e98eb8ea452d5f3dcc8fd0cfd6d28b4052a24c5dbab6e3089b2d7e79f0bce7915b10d750929563c42 + languageName: node + linkType: hard + "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -1759,6 +1998,126 @@ __metadata: languageName: node linkType: hard +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/70945bd55097af46fc9fab7f5ed09cd5869d85940a2acab7ee06d0117004a1d68155708a2d462531cea2fc3c67aefc9333a7068c80b0b78dd404c16838809e03 + languageName: node + linkType: hard + "lilconfig@npm:^2.0.5": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" @@ -2079,6 +2438,7 @@ __metadata: "@sveltejs/adapter-static": "npm:^3.0.10" "@sveltejs/kit": "npm:^2.49.1" "@sveltejs/vite-plugin-svelte": "npm:^6.2.1" + "@tailwindcss/vite": "npm:^4.3.3" "@types/node": "npm:^24" codemirror: "npm:^6.0.2" eslint: "npm:^10.4.1" @@ -2090,6 +2450,7 @@ __metadata: pyodide: "npm:^314.0.0" svelte: "npm:^5.56.3" svelte-check: "npm:^4.3.4" + tailwindcss: "npm:^4.3.3" typescript: "npm:^5.9.3" typescript-eslint: "npm:^8.60.1" vite: "npm:^7.2.6" @@ -2343,6 +2704,20 @@ __metadata: languageName: node linkType: hard +"tailwindcss@npm:4.3.3, tailwindcss@npm:^4.3.3": + version: 4.3.3 + resolution: "tailwindcss@npm:4.3.3" + checksum: 10c0/7e9cd7553cd890ffa5350efb0ca8971ba5435257d0f98bf0714994ac8f1fee5c7cdf9a9da47fe83e8ac995d728410837ac49d3ef5ea66b5afe1db312086ffae2 + languageName: node + linkType: hard + +"tapable@npm:^2.3.3": + version: 2.3.3 + resolution: "tapable@npm:2.3.3" + checksum: 10c0/47992e861053f861154e92fb4a98ac4ab47b6463717e60792dd1e8c755da0c4964cd8bb68c308a9066d6da89000b6310457b4d5d985c30de4ccc29066068cc17 + languageName: node + linkType: hard + "tar@npm:^7.5.4": version: 7.5.16 resolution: "tar@npm:7.5.16" @@ -2391,6 +2766,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.4.0, tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" From 2d14602c6c3f69a85ede9411fac2ad7fce881aed Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 00:20:07 +0100 Subject: [PATCH 4/9] wip: design --- src/routes/+layout.svelte | 2 +- src/routes/playground/+page.svelte | 82 ++++++++++++++++++++++------- src/routes/playground/Editor.svelte | 14 ++++- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1ed3806..3fa208a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,5 @@ diff --git a/src/routes/playground/+page.svelte b/src/routes/playground/+page.svelte index 0bd4508..f55114b 100644 --- a/src/routes/playground/+page.svelte +++ b/src/routes/playground/+page.svelte @@ -5,6 +5,9 @@ import { Stdout } from "./stdout.svelte"; import { onMount } from "svelte"; + let terminalHeight = $state(undefined); + let totalHeight = $state(undefined); + let stdout = new Stdout(); let purepy: PurePy | undefined = undefined; @@ -51,32 +54,75 @@ } }; + const onmousedown = () => { + const mousemove = (e) => { + terminalHeight = innerHeight - e.clientY - 2; + }; + + const mouseup = () => { + window.removeEventListener("mouseup", mouseup); + window.removeEventListener("mousemove", mousemove); + }; + + window.addEventListener("mouseup", mouseup); + window.addEventListener("mousemove", mousemove); + }; + const onupdate = (update: string) => { src_unsaved = update; }; - + -
-
+
+
+ + + +
+ +
-
-
- {#each $state.eager(stdout.lines) as line, index (index)} -

- {line.content} -

- {/each} + +
+
+ +
+
+ {#each $state.eager(stdout.lines) as line, index (index)} +

+ {line.content} +

+ {/each} +
- {#if is_ready && !is_running} - - {/if} -
diff --git a/src/routes/playground/Editor.svelte b/src/routes/playground/Editor.svelte index 43e7b2a..5af1b7a 100644 --- a/src/routes/playground/Editor.svelte +++ b/src/routes/playground/Editor.svelte @@ -20,7 +20,17 @@ const view = new EditorView({ doc: value, - extensions: [basicSetup, oneDark, python()], + extensions: [ + basicSetup, + oneDark, + EditorView.theme({ + "&": { + height: "100%", + fontSize: "14px", + }, + }), + python(), + ], parent: element, dispatchTransactions: (txs, view) => { @@ -33,7 +43,7 @@ }); -
+
{#if loading} Loading... {/if} From baf445c5a83840bdd431440a5a5acef3a93163eb Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 09:38:46 +0100 Subject: [PATCH 5/9] build out design, organise components, some robustness --- src/{routes => lib}/playground/Editor.svelte | 14 +- src/lib/playground/Terminal.svelte | 20 +++ src/lib/playground/TerminalDragger.svelte | 52 +++++++ src/lib/playground/Toolbar.svelte | 33 +++++ src/lib/playground/components/Kbd.svelte | 9 ++ .../components/ToolbarButton.svelte | 16 +++ .../playground/components/ToolbarItem.svelte | 9 ++ .../playground/components/ToolbarLink.svelte | 27 ++++ .../playground/lib}/purepy.ts | 11 +- .../playground/lib}/stdout.svelte.ts | 0 src/routes/playground/+page.svelte | 127 +++++++----------- 11 files changed, 238 insertions(+), 80 deletions(-) rename src/{routes => lib}/playground/Editor.svelte (81%) create mode 100644 src/lib/playground/Terminal.svelte create mode 100644 src/lib/playground/TerminalDragger.svelte create mode 100644 src/lib/playground/Toolbar.svelte create mode 100644 src/lib/playground/components/Kbd.svelte create mode 100644 src/lib/playground/components/ToolbarButton.svelte create mode 100644 src/lib/playground/components/ToolbarItem.svelte create mode 100644 src/lib/playground/components/ToolbarLink.svelte rename src/{routes/playground => lib/playground/lib}/purepy.ts (91%) rename src/{routes/playground => lib/playground/lib}/stdout.svelte.ts (100%) diff --git a/src/routes/playground/Editor.svelte b/src/lib/playground/Editor.svelte similarity index 81% rename from src/routes/playground/Editor.svelte rename to src/lib/playground/Editor.svelte index 5af1b7a..ea1dbf4 100644 --- a/src/routes/playground/Editor.svelte +++ b/src/lib/playground/Editor.svelte @@ -1,4 +1,5 @@ diff --git a/src/lib/playground/Terminal.svelte b/src/lib/playground/Terminal.svelte new file mode 100644 index 0000000..77c300d --- /dev/null +++ b/src/lib/playground/Terminal.svelte @@ -0,0 +1,20 @@ + + +
+
+ {#each $state.eager(stdout.lines) as line, index (index)} +

+ {line.content} +

+ {/each} +
+
diff --git a/src/lib/playground/TerminalDragger.svelte b/src/lib/playground/TerminalDragger.svelte new file mode 100644 index 0000000..05b3192 --- /dev/null +++ b/src/lib/playground/TerminalDragger.svelte @@ -0,0 +1,52 @@ + + + + + diff --git a/src/lib/playground/Toolbar.svelte b/src/lib/playground/Toolbar.svelte new file mode 100644 index 0000000..573565b --- /dev/null +++ b/src/lib/playground/Toolbar.svelte @@ -0,0 +1,33 @@ + + +
+ + + + + +

PurePy

+ + + Check + + Run + ⌘S + +
diff --git a/src/lib/playground/components/Kbd.svelte b/src/lib/playground/components/Kbd.svelte new file mode 100644 index 0000000..69915ca --- /dev/null +++ b/src/lib/playground/components/Kbd.svelte @@ -0,0 +1,9 @@ + + +
+ {@render children()} +
diff --git a/src/lib/playground/components/ToolbarButton.svelte b/src/lib/playground/components/ToolbarButton.svelte new file mode 100644 index 0000000..567b332 --- /dev/null +++ b/src/lib/playground/components/ToolbarButton.svelte @@ -0,0 +1,16 @@ + + + diff --git a/src/lib/playground/components/ToolbarItem.svelte b/src/lib/playground/components/ToolbarItem.svelte new file mode 100644 index 0000000..285c656 --- /dev/null +++ b/src/lib/playground/components/ToolbarItem.svelte @@ -0,0 +1,9 @@ + + +
+ {@render children()} +
diff --git a/src/lib/playground/components/ToolbarLink.svelte b/src/lib/playground/components/ToolbarLink.svelte new file mode 100644 index 0000000..2e8da00 --- /dev/null +++ b/src/lib/playground/components/ToolbarLink.svelte @@ -0,0 +1,27 @@ + + + + + {@render children()} + + {#if external} + + + + + {/if} + + diff --git a/src/routes/playground/purepy.ts b/src/lib/playground/lib/purepy.ts similarity index 91% rename from src/routes/playground/purepy.ts rename to src/lib/playground/lib/purepy.ts index 0359fd2..01a9802 100644 --- a/src/routes/playground/purepy.ts +++ b/src/lib/playground/lib/purepy.ts @@ -76,7 +76,7 @@ export class PurePy { `), ); - evaluate = (src: string) => { + parse_and_check = (src: string) => { const path = this.write_file("whatever.purepy", src); const parse_result = this.parse(path); @@ -89,6 +89,15 @@ export class PurePy { return check_result; } + return { success: true, error: null } as const; + }; + + evaluate = (src: string) => { + const check_result = this.parse_and_check(src); + if (!check_result.success) { + return check_result; + } + const result = this.run(src); return { success: true, output: result } as const; }; diff --git a/src/routes/playground/stdout.svelte.ts b/src/lib/playground/lib/stdout.svelte.ts similarity index 100% rename from src/routes/playground/stdout.svelte.ts rename to src/lib/playground/lib/stdout.svelte.ts diff --git a/src/routes/playground/+page.svelte b/src/routes/playground/+page.svelte index f55114b..c709462 100644 --- a/src/routes/playground/+page.svelte +++ b/src/routes/playground/+page.svelte @@ -1,30 +1,59 @@ - - -
-
- - - + + +
+
+
-
+
-
-
- -
-
- {#each $state.eager(stdout.lines) as line, index (index)} -

- {line.content} -

- {/each} -
-
+
+ (terminalHeight = height)} /> +
From 6f209a42d3f8a9f5523445e84481bfc531e7a5bb Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 12:05:48 +0100 Subject: [PATCH 6/9] design: various improvements, and add a status bar element --- src/lib/playground/Editor.svelte | 10 +- src/lib/playground/StatusBar.svelte | 35 ++++++ src/lib/playground/Terminal.svelte | 22 ++-- src/lib/playground/TerminalDragger.svelte | 10 +- src/lib/playground/Toolbar.svelte | 6 +- .../playground/components/StatusItem.svelte | 46 +++++++ src/lib/playground/lib/purepy.ts | 5 - src/lib/playground/lib/state.ts | 12 ++ src/routes/playground/+page.svelte | 118 ++++++++++++++---- 9 files changed, 214 insertions(+), 50 deletions(-) create mode 100644 src/lib/playground/StatusBar.svelte create mode 100644 src/lib/playground/components/StatusItem.svelte create mode 100644 src/lib/playground/lib/state.ts diff --git a/src/lib/playground/Editor.svelte b/src/lib/playground/Editor.svelte index ea1dbf4..9bbb5c6 100644 --- a/src/lib/playground/Editor.svelte +++ b/src/lib/playground/Editor.svelte @@ -2,9 +2,13 @@ import type { EditorView } from "codemirror"; import { onMount } from "svelte"; - type Props = { value: string; onupdate: (value: string) => void }; + type Props = { + value: string; + onupdate: (value: string) => void; + onload: () => void; + }; - let { value, onupdate }: Props = $props(); + let { value, onupdate, onload }: Props = $props(); let element: HTMLDivElement; let view: EditorView | undefined; @@ -41,6 +45,8 @@ onupdate(view.state.doc.toString()); }, }); + + onload(); }; onMount(() => { diff --git a/src/lib/playground/StatusBar.svelte b/src/lib/playground/StatusBar.svelte new file mode 100644 index 0000000..624f01c --- /dev/null +++ b/src/lib/playground/StatusBar.svelte @@ -0,0 +1,35 @@ + + +
+
+

+ {#if status.has_unsaved_changes} + Unsaved changes + {:else} + Up to date + {/if} +

+
+ +
+ + +
+
diff --git a/src/lib/playground/Terminal.svelte b/src/lib/playground/Terminal.svelte index 77c300d..194b825 100644 --- a/src/lib/playground/Terminal.svelte +++ b/src/lib/playground/Terminal.svelte @@ -2,19 +2,19 @@ import { type Stdout } from "$lib/playground/lib/stdout.svelte"; type Props = { terminalHeight: number | undefined; stdout: Stdout }; let { terminalHeight, stdout }: Props = $props(); + + const MIN_HEIGHT = 128; + + let height = $derived(terminalHeight ?? MIN_HEIGHT);
-
- {#each $state.eager(stdout.lines) as line, index (index)} -

- {line.content} -

- {/each} -
+ {#each $state.eager(stdout.lines) as line, index (index)} +

+ {line.content} +

+ {/each}
diff --git a/src/lib/playground/TerminalDragger.svelte b/src/lib/playground/TerminalDragger.svelte index 05b3192..93ecc66 100644 --- a/src/lib/playground/TerminalDragger.svelte +++ b/src/lib/playground/TerminalDragger.svelte @@ -10,15 +10,17 @@ // // TODO: check mobile. there is a touchdown or similar event. pointer down might // be more approrpriate and cover both. - const onmousedown = (_: MouseEvent) => { + // + const onmousedown = () => { const onmousemove = ({ clientY }: MouseEvent) => { if (availableHeight === undefined) { return; } // the height of the window, offset by the mouse y position, - // minus the height of this dragger element itself - const proposedHeight = availableHeight - clientY - 16; + // minus the height of this dragger element itself, minus + // the statusbar height + const proposedHeight = availableHeight - clientY - 16 - 24; // these aren't strictly necessary, but it will make it easier // to not break anything here @@ -33,7 +35,7 @@ setHeight(boundedHeight); }; - const onmouseup = (_: MouseEvent) => { + const onmouseup = () => { window.removeEventListener("mouseup", onmouseup); window.removeEventListener("mousemove", onmousemove); }; diff --git a/src/lib/playground/Toolbar.svelte b/src/lib/playground/Toolbar.svelte index 573565b..4b5a9b7 100644 --- a/src/lib/playground/Toolbar.svelte +++ b/src/lib/playground/Toolbar.svelte @@ -3,7 +3,7 @@ import ToolbarButton from "./components/ToolbarButton.svelte"; import ToolbarLink from "./components/ToolbarLink.svelte"; - let { check, run, is_running } = $props(); + let { check, run, is_busy } = $props();
@@ -25,8 +25,8 @@ > - Check - + Check + Run ⌘S diff --git a/src/lib/playground/components/StatusItem.svelte b/src/lib/playground/components/StatusItem.svelte new file mode 100644 index 0000000..448d677 --- /dev/null +++ b/src/lib/playground/components/StatusItem.svelte @@ -0,0 +1,46 @@ + + +
+
+ {#if success === true} + + + + {:else if success === false} + + + + {/if} +
+ + {#if success !== null} +

+ {type} + {time}ms +

+ {/if} +
diff --git a/src/lib/playground/lib/purepy.ts b/src/lib/playground/lib/purepy.ts index 01a9802..3f1cc59 100644 --- a/src/lib/playground/lib/purepy.ts +++ b/src/lib/playground/lib/purepy.ts @@ -93,11 +93,6 @@ export class PurePy { }; evaluate = (src: string) => { - const check_result = this.parse_and_check(src); - if (!check_result.success) { - return check_result; - } - const result = this.run(src); return { success: true, output: result } as const; }; diff --git a/src/lib/playground/lib/state.ts b/src/lib/playground/lib/state.ts new file mode 100644 index 0000000..55bf43d --- /dev/null +++ b/src/lib/playground/lib/state.ts @@ -0,0 +1,12 @@ +// TODO: something more sensible +export type Status = { + has_unsaved_changes: boolean; + check_success: boolean | null; + eval_success: boolean | null; + is_running_check: boolean; + is_running_evaluate: boolean; + is_pyodide_ready: boolean; + is_codemirror_ready: boolean; + check_time: number; + eval_time: number; +}; diff --git a/src/routes/playground/+page.svelte b/src/routes/playground/+page.svelte index c709462..43922c9 100644 --- a/src/routes/playground/+page.svelte +++ b/src/routes/playground/+page.svelte @@ -3,6 +3,7 @@ import CodeMirror from "$lib/playground/Editor.svelte"; import { PurePy } from "$lib/playground/lib/purepy"; import { Stdout } from "$lib/playground/lib/stdout.svelte"; + import StatusBar from "$lib/playground/StatusBar.svelte"; import Terminal from "$lib/playground/Terminal.svelte"; import TerminalDragger from "$lib/playground/TerminalDragger.svelte"; import Toolbar from "$lib/playground/Toolbar.svelte"; @@ -13,22 +14,60 @@ let stdout = new Stdout(); let purepy: PurePy | undefined = undefined; - let src_saved: string = $state(example_py); - let src_unsaved: string = $state(example_py); - // let has_unsaved_changes = $derived(src_saved !== src_unsaved); - let is_running: boolean = $state(false); - let is_ready: boolean = $state(false); + // TODO: once we are happy we have all the state we need, + // we should simplify this!! + + let src_saved = $state(example_py); + let src_unsaved = $state(example_py); + let has_unsaved_changes = $derived(src_saved !== src_unsaved); + + let is_running_check = $state(false); + let is_running_evaluate = $state(false); + let is_running = $derived(is_running_check || is_running_evaluate); + + let is_pyodide_ready = $state(false); + let is_codemirror_ready = $state(false); + let is_busy = $derived( + !is_pyodide_ready || !is_codemirror_ready || is_running, + ); + + let check_success: boolean | null = $state(null); + let eval_success: boolean | null = $state(null); + + let check_time = $state(0); + let eval_time = $state(0); + + const status = $derived({ + has_unsaved_changes, + check_success, + eval_success, + is_running_check, + is_running_evaluate, + is_pyodide_ready, + is_codemirror_ready, + check_time, + eval_time, + }); + + const save = () => { + if (!has_unsaved_changes) { + // nothing to do... + return; + } + src_saved = src_unsaved; + check_success = null; + eval_success = null; + }; // all of our actions are very fast, so it is okay to defer // 'saving' if an action is running, but we may want to change this // later. const statefully = (fn: (purepy: PurePy) => void) => { - if (is_running || !is_ready || purepy === undefined) { + if (is_busy || purepy === undefined) { console.error("I'm in a bad state..."); return; } is_running = true; - src_saved = src_unsaved; try { fn(purepy); } catch (error) { @@ -40,35 +79,59 @@ } }; + // don't use this unless you are inside a `statefully` already + const _check_saved = (purepy: PurePy) => { + const start_t = Date.now(); + const { success, error } = purepy.parse_and_check(src_saved); + check_time = Date.now() - start_t; + check_success = success; + if (!success) { + stdout.write_err(error.msg); + } + return success; + }; + const check = () => statefully((purepy) => { - const result = purepy.parse_and_check(src_saved); - - if (!result.success) { - stdout.write_err(result.error.msg); - return; - } - - stdout.write(`Check ok!`); + save(); + const success = _check_saved(purepy); + stdout.write(success ? `Check ok!` : `Check failed!`); }); const run = () => statefully((purepy) => { - const result = purepy.evaluate(src_saved); - - if (!result.success) { - stdout.write_err(result.error.msg); - return; + save(); + const check_ok = _check_saved(purepy); + if (!check_ok) { + // what should we do here? should we even check here? + // should we refuse to run? + stdout.write("Check failed, running anyway:"); } - stdout.write(`Result: ${result.output}`); + stdout.write("--- stdout ---"); + + // TODO: this is always successful, but surely not, + // we should catch runtime errors! + const start_t = Date.now(); + const { success, output } = purepy.evaluate(src_saved); + eval_time = Date.now() - start_t; + eval_success = success; + + stdout.write("--- result ---"); + + // if the program ends in a value expression, we get that + // value here (otherwise undefined) + const result = output === undefined ? "" : output; + stdout.write(`${result}`); + + stdout.write("---"); }); onMount(async () => { stdout.write("Loading PurePy..."); purepy = await PurePy.load(); purepy.attach_stdout(stdout); - is_ready = true; + is_pyodide_ready = true; stdout.write("Ready"); stdout.write("Press Cmd/Ctrl + S or use the button below to run"); }); @@ -89,15 +152,20 @@
- +
-
- +
+ (is_codemirror_ready = true)} + value={example_py} + {onupdate} + />
(terminalHeight = height)} /> +
From 7d7b20e070efcfbbd4e24e495471089fd2ef794f Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 12:30:25 +0100 Subject: [PATCH 7/9] design: slicker dragger --- src/lib/playground/TerminalDragger.svelte | 25 ++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/lib/playground/TerminalDragger.svelte b/src/lib/playground/TerminalDragger.svelte index 93ecc66..5ba0931 100644 --- a/src/lib/playground/TerminalDragger.svelte +++ b/src/lib/playground/TerminalDragger.svelte @@ -20,7 +20,7 @@ // the height of the window, offset by the mouse y position, // minus the height of this dragger element itself, minus // the statusbar height - const proposedHeight = availableHeight - clientY - 16 - 24; + const proposedHeight = availableHeight - clientY - 24; // these aren't strictly necessary, but it will make it easier // to not break anything here @@ -50,5 +50,24 @@ + class="h-px bg-gray-700 hover:bg-gray-600 active:bg-blue-900 flex justify-center items-center cursor-ns-resize group" +> +
+
+ + + +
+
+ From 6d9a37c1c06e37326c9fd1f9cfd5260c49b8f0b2 Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 18:04:31 +0100 Subject: [PATCH 8/9] handle SyntaxError during parse, and display object (dict) outputs on run --- src/lib/playground/lib/purepy.ts | 27 ++++++++++++++++++++++----- src/routes/playground/+page.svelte | 4 +++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/lib/playground/lib/purepy.ts b/src/lib/playground/lib/purepy.ts index 3f1cc59..7cd2514 100644 --- a/src/lib/playground/lib/purepy.ts +++ b/src/lib/playground/lib/purepy.ts @@ -8,7 +8,7 @@ import reasons_py from "$lib/assets/reasons.py?raw"; import { type PyodideAPI } from "pyodide"; import type { Stdout } from "./stdout.svelte"; -const ParseError = z.object({ +const PurePyError = z.object({ msg: z.string(), line: z.optional(z.number()), col: z.optional(z.number()), @@ -22,7 +22,7 @@ const capture_err = (fn: () => unknown) => { return { success: true } as const; } - const error = ParseError.parse(result); + const error = PurePyError.parse(result); return { success: false, error } as const; }; @@ -55,15 +55,32 @@ export class PurePy { }; run = (src: string) => { - const result: unknown = this.pyodide.runPython(src); - return result; + try { + const result: unknown = this.pyodide.runPython(src); + return result; + } catch (error) { + // something went wrong in Python/Pyodide + console.error(error); + return { + msg: "Unhandled exception (see console)", + }; + } }; parse = (path: string) => capture_err(() => this.run(` import parse - parse.check_file("${path}") + def fn(): + try: + parse.check_file("${path}") + except SyntaxError as err: + return { + "msg": "Syntax error", + "line": err.lineno, + "col": err.offset + } + fn() `), ); diff --git a/src/routes/playground/+page.svelte b/src/routes/playground/+page.svelte index 43922c9..b173866 100644 --- a/src/routes/playground/+page.svelte +++ b/src/routes/playground/+page.svelte @@ -122,7 +122,9 @@ // if the program ends in a value expression, we get that // value here (otherwise undefined) const result = output === undefined ? "" : output; - stdout.write(`${result}`); + // TODO: figure out the possible, sensible output types and handle them properly, + // in the meantime at least avoid [object Object] + stdout.write(typeof result === 'object' ? JSON.stringify(result) : `${result}`); stdout.write("---"); }); From 8177db965b0a7cf29e4889187ee6180560fc85ee Mon Sep 17 00:00:00 2001 From: Jacob Pake Date: Wed, 19 Aug 2026 18:49:05 +0100 Subject: [PATCH 9/9] formatting --- src/routes/playground/+page.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/playground/+page.svelte b/src/routes/playground/+page.svelte index b173866..43cae4f 100644 --- a/src/routes/playground/+page.svelte +++ b/src/routes/playground/+page.svelte @@ -124,7 +124,9 @@ const result = output === undefined ? "" : output; // TODO: figure out the possible, sensible output types and handle them properly, // in the meantime at least avoid [object Object] - stdout.write(typeof result === 'object' ? JSON.stringify(result) : `${result}`); + stdout.write( + typeof result === "object" ? JSON.stringify(result) : `${result}`, + ); stdout.write("---"); });