diff --git a/index.html b/index.html index 098f57c..7d2e0e7 100644 --- a/index.html +++ b/index.html @@ -24,6 +24,11 @@ resize: vertical; } + textarea, + pre { + font-family: "Fira Code", monospace; + } + #errors { color: red; } @@ -61,7 +66,7 @@

Schemascii Source

CSS

-

Result

+

Result

Messages


@@ -71,32 +76,6 @@ 

More Information

https://github.com/dragoncoder047/schemascii/

- + - \ No newline at end of file + diff --git a/schemascii_example.css b/schemascii_example.css index 617c61d..379ae3e 100644 --- a/schemascii_example.css +++ b/schemascii_example.css @@ -1,69 +1,59 @@ svg.schemascii { background: black; -} - -svg.schemascii .wire polyline { - stroke: var(--sch-color, blue); - stroke-width: 2; - stroke-linecap: round; - stroke-linejoin: round; - transition-duration: 0.2s; - fill: transparent; -} - -svg.schemascii .wire circle { - fill: var(--sch-color, blue); - transition-duration: 0.2s; -} - -svg.schemascii :is(.wire, .component):hover { - --sch-color: lime; -} - -svg.schemascii .component :is(polyline, path, line, polygon, rect, circle):not(.filled) { - stroke: var(--sch-color, red); - stroke-width: 2; - stroke-linecap: round; - transition-duration: 0.2s; - fill: transparent; -} - -svg.schemascii .component :is(polyline, path, line, polygon, rect, circle).filled { - fill: var(--sch-color, red); - stroke: none; - transition-duration: 0.2s; -} - - -svg.schemascii .component .plus :is(polyline, path, line) { - stroke-width: 1; -} - -svg.schemascii .component polygon { - fill: var(--sch-color, red); -} - -svg.schemascii .component text { - fill: white; - transition-duration: 0.2s; -} - -svg.schemascii .component:hover text { - font-weight: bold; -} -svg.schemascii .component tspan:is(.cmp-value, .part-num) { - opacity: 50%; + & .wire polyline { + stroke: var(--sch-color, blue); + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + transition-duration: 0.2s; + fill: transparent; + } + & .wire circle { + fill: var(--sch-color, blue); + transition-duration: 0.2s; + } + & :is(.wire, .component):hover { + --sch-color: lime; + } + & .component :is(polyline, path, line, polygon, rect, circle):not(.filled) { + stroke: var(--sch-color, red); + stroke-width: 2; + stroke-linecap: round; + transition-duration: 0.2s; + fill: transparent; + } + & .component :is(polyline, path, line, polygon, rect, circle).filled { + fill: var(--sch-color, red); + stroke: none; + transition-duration: 0.2s; + } + & .component .plus :is(polyline, path, line) { + stroke-width: 1; + } + & .component polygon { + fill: var(--sch-color, red); + } + & .component text { + fill: white; + transition-duration: 0.2s; + } + & .component:hover text { + font-weight: bold; + } + & .component tspan:is(.cmp-value, .part-num) { + opacity: 50%; + } } @media all and (prefers-color-scheme: light) { svg.schemascii { background: white; - } - svg.schemascii .component text { - fill: black; - } - svg.schemascii :is(.wire, .component):hover { - --sch-color: lime; + & .component text { + fill: black; + } + & :is(.wire, .component):hover { + --sch-color: lime; + } } } diff --git a/scripts/monkeypatch.py b/scripts/monkeypatch.py new file mode 100644 index 0000000..affc0fd --- /dev/null +++ b/scripts/monkeypatch.py @@ -0,0 +1,17 @@ +import sys +if "schemascii" in sys.modules: + del sys.modules["schemascii"] + +import warnings +import schemascii + +print("monkeypatching... ", end="") + +def patched(src): + with warnings.catch_warnings(record=True) as captured_warnings: + out = schemascii.render("", src) + for warn in captured_warnings: + print("warning:", warn.message) + return out + +schemascii.patched_render = patched diff --git a/scripts/web_startup.js b/scripts/web_startup.js new file mode 100644 index 0000000..db6ac5e --- /dev/null +++ b/scripts/web_startup.js @@ -0,0 +1,119 @@ +// cSpell:ignore pyodide pyproject +var pyodide; +var console = document.getElementById("console"); +var errors = document.getElementById("errors"); +var css_box = document.getElementById("css"); +var source = document.getElementById("schemascii"); +var download_button = document.getElementById("download"); +var ver_switcher = document.getElementById("version"); +var style_elem = document.getElementById("custom-css"); +var output = document.getElementById("output"); + +var schemascii; +var monkeysrc; + +var ver_map; + +async function main() { + try { + info("Loading Python... "); + pyodide = await loadPyodide({ stdout: info, stderr: error }); + info("done\nInstalling micropip..."); + await pyodide.loadPackage("micropip", { errorCallback: error, messageCallback: () => { } }); + info("done\nFetching versions... "); + monkeysrc = await fetch("scripts/monkeypatch.py").then(r => r.text()); + var foo = await fetch("https://api.github.com/repos/dragoncoder047/schemascii/contents/dist").then(r => r.json()); + foo = foo.filter(x => x.name.endsWith(".whl")).map(x => x.path); + ver_map = Object.fromEntries(foo.map(x => [/\/schemascii-([\d.]+)-/.exec(x)[1], x])) + var all_versions = Object.keys(ver_map); + //all_versions.push("DEV"); + for (var v of all_versions) { + var o = document.createElement("option"); + o.textContent = o.value = v; + ver_switcher.append(o); + } + var latest_version = await fetch("pyproject.toml").then(r => r.text()).then(r => /version = "([\d.]+)"/.exec(r)[1]); + ver_switcher.value = latest_version; + info(`["${all_versions.join('", "')}"]\nlatest=${latest_version}\n`); + await switch_version(); + + css_box.addEventListener("input", debounce(sync_css)); + source.addEventListener("input", debounce(catched(render))); + download_button.addEventListener("click", download); + ver_switcher.addEventListener("change", acatched(switch_version)); + + css_box.value = await fetch("schemascii_example.css").then(r => r.text()); + sync_css(); + source.removeAttribute("disabled"); + css_box.removeAttribute("disabled"); + console.textContent = "Ready"; + } catch (e) { + error(`\nFATAL ERROR:\n${e.stack}\n`); + throw e; + } +} +function monkeypatch() { + pyodide.runPython(monkeysrc); +} +function info(line) { + console.textContent += line; +} +function error(text) { + errors.textContent += text; +} +function debounce(fun) { + var timeout; + return function () { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(fun.bind(this, arguments), 100); + }; +} +function catched(fun) { + return function () { + try { + fun.call(this, arguments); + } catch (e) { + error(e.stack); + } + }; +} +async function acatched(fun) { + return async function() { + try { + await fun.call(this, arguments); + } catch (e) { + error(e.stack); + } + }; +} +function sync_css() { + style_elem.innerHTML = css_box.value; +} +function render() { + console.textContent = ""; + errors.textContent = ""; + output.innerHTML = schemascii.patched_render(source.value); +} + +async function switch_version() { + source.setAttribute("disabled", true); + info("Installing Schemascii version " + ver_switcher.value + "... ") + await pyodide.pyimport("micropip").install(ver_map[ver_switcher.value]); + monkeypatch(); + schemascii = pyodide.runPython("import schemascii\nschemascii"); + info("done\n"); + output.innerHTML = ""; + source.removeAttribute("disabled"); +} + +function download() { + if (!output.innerHTML) return; + var a = document.createElement("a"); + a.setAttribute("href", URL.createObjectURL(new Blob([output.innerHTML], {"type": "application/svg+xml"}))); + a.setAttribute("download", `schemascii_playground_${new Date().toISOString()}_no_css.svg`); + a.click(); +} + +main(); + +// fetch("https://github.com/dragoncoder047/schemascii/zipball/main/").then(r => r.arrayBuffer()).then(b => pyodide.unpackArchive(b, "zip")); diff --git a/scripts/web_startup.py b/scripts/web_startup.py deleted file mode 100644 index 9e24293..0000000 --- a/scripts/web_startup.py +++ /dev/null @@ -1,99 +0,0 @@ -from pyodide.http import pyfetch as fetch, open_url as get -from pyodide.ffi import create_proxy as event_handler -import asyncio -import functools -import sys -import re -import js -import json -from operator import itemgetter -import warnings -import micropip - - -def debounced(fun): - timeout = None - - @functools.wraps(fun) - def inner(): - nonlocal timeout - if timeout: - js.clearTimeout(timeout) - timeout = js.setTimeout(fun, 100) - - return inner - - -def syncify(fun): - @functools.wraps(fun) - def inner(e): - return asyncio.ensure_future(fun(e)) - return inner - - -@event_handler -@debounced -def sync_css(): - style_elem.innerHTML = css_box.value - - -@event_handler -@debounced -def render_catch_warnings(*args, **kwargs): - import schemascii - console.textContent = "" - errors.textContent = "" - with warnings.catch_warnings(record=True) as captured_warnings: - out = schemascii.render(*args, **kwargs) - for warn in captured_warnings: - print("warning:", warn.message) - return out - - -@event_handler -@syncify -async def switch_version(): - if "schemascii" in sys.modules: - del sys.modules["schemascii"] # Invalidate cache - version = ver_switcher.value - await micropip.install(versions_to_wheel_map[version]) - -output = js.document.getElementById("output") -css_box = js.document.getElementById("css") -console = js.document.getElementById("console") -source = js.document.getElementById("schemascii") -style_elem = js.document.getElementById("custom-css") -errors = js.document.getElementById("errors") -ver_switcher = js.document.getElementById("version") - - -print("Loading all versions... ", end="") -versions_all = json.load( - get("https://api.github.com/repos/dragoncoder047/schemascii/contents/dist")) -versions_to_wheel_map = dict( - zip(map(itemgetter("name"), versions_all), map(itemgetter("path"), versions_all))) -all_versions = list(versions_to_wheel_map.keys()) -all_versions.append("DEV") -latest_version = re.search( - r'''version = "([\d.]+)"''', get("pyproject.toml").read()).group(1) -print(all_versions, "latest =", latest_version) - -for ver in all_versions: - opt = js.document.createElement("option") - opt.value = opt.textContent = ver - ver_switcher.append(opt) - -ver_switcher.value = latest_version -await micropip.install(versions_to_wheel_map[latest_version]) - - -css_source = get("schemascii_example.css").read() -style_elem.textContent = css_source -css_box.value = css_source - -css_box.addEventListener("input", sync_css) -source.addEventListener("input", render_catch_warnings) - -source.removeAttribute("disabled") -css_box.removeAttribute("disabled") -console.textContent = "ready\n"