diff --git a/web/root/assets/icon.jpg b/web/root/assets/icon.jpg
new file mode 100644
index 0000000..f5d649c
Binary files /dev/null and b/web/root/assets/icon.jpg differ
diff --git a/web/root/index.html b/web/root/index.html
index 5c0c855..7964d62 100644
--- a/web/root/index.html
+++ b/web/root/index.html
@@ -6,6 +6,7 @@
Automata
+
diff --git a/web/root/src/theme.ts b/web/root/src/theme.ts
index 88f7625..8dd0244 100644
--- a/web/root/src/theme.ts
+++ b/web/root/src/theme.ts
@@ -1,10 +1,4 @@
-import { getGraphTheme, invalidateGraphThemeCache, network } from "./visualizer.ts";
-
-function cssVar(name: string, fallback = ""): string {
- return getComputedStyle(document.documentElement)
- .getPropertyValue(name)
- .trim() || fallback;
-}
+import { updateGraphTheme } from "./visualizer.ts";
const themeBtn = document.getElementById("themeToggle") as HTMLButtonElement;
@@ -28,66 +22,25 @@ function setTheme(theme: Theme) {
// update button label
themeBtn.textContent = theme === "dark" ? "🌙 Dark" : "☀️ Light";
- applyGraphTheme();
+ updateGraphTheme();
}
+
+setTheme(getPreferredTheme());
+
+themeBtn.addEventListener("click", toggleTheme);
function toggleTheme() {
const current = (document.documentElement.dataset.theme as Theme) || "dark";
setTheme(current === "dark" ? "light" : "dark");
}
-// init
-setTheme(getPreferredTheme());
-// click handler
-themeBtn.addEventListener("click", toggleTheme);
-// optional: respond to OS theme changes (only if user hasn't chosen a theme)
globalThis.window.matchMedia?.("(prefers-color-scheme: light)")
?.addEventListener("change", () => {
- if (localStorage.getItem("theme")) return; // user has chosen, don't override
+ if (localStorage.getItem("theme")) return;
setTheme(getPreferredTheme());
});
-function applyGraphTheme() {
- invalidateGraphThemeCache();
-
- network.setOptions({
- nodes: {
- font: {
- color: cssVar("--graph-node-text"),
- bold: {
- color: cssVar("--fg-1"),
- mod: ''
- },
- },
- },
- edges: {
- labelHighlightBold: true,
- font: {
- align: "top",
- size: getGraphTheme().edge_font_size,
- color: cssVar("--fg-0"),
- strokeColor: cssVar("--bg-0"),
- bold: {
- color: cssVar("--fg-1"),
- size: getGraphTheme().edge_font_size,
- mod: ''
- },
- },
- color: {
- color: cssVar("--graph-edge"),
- highlight: cssVar("--graph-edge-active"),
- hover: cssVar("--graph-edge-hover"),
- },
- shadow: {
- enabled: true,
- color: cssVar("--bg-2")
- }
- },
- });
-}
-
-
diff --git a/web/root/src/visualizer.ts b/web/root/src/visualizer.ts
index 0e9712b..526d123 100644
--- a/web/root/src/visualizer.ts
+++ b/web/root/src/visualizer.ts
@@ -6,6 +6,106 @@ import * as vis from "npm:vis-network/standalone";
export const nodes = new vis.DataSet();
export const edges = new vis.DataSet();
+
+type Color = string;
+type GraphTheme = {
+ bg_0: Color;
+ bg_1: Color;
+ bg_2: Color;
+ fg_0: Color;
+ fg_1: Color;
+ fg_2: Color;
+
+ node_anchor: Color;
+ node_border: Color;
+ current_node_border: Color;
+
+ edge: Color;
+ edge_hover: Color;
+ edge_active: Color;
+
+ edge_font_size: number,
+ node_font_size: number,
+};
+
+let _graphTheme: GraphTheme | null = null;
+
+function invalidateGraphThemeCache() {
+ _graphTheme = null;
+}
+
+function getGraphTheme(): GraphTheme {
+ function cssVar(name: string, fallback = ""): string {
+ return getComputedStyle(document.documentElement)
+ .getPropertyValue(name)
+ .trim() || fallback;
+ }
+
+ if (_graphTheme) return _graphTheme;
+
+ _graphTheme = {
+ bg_0: cssVar("--graph-bg-0"),
+ bg_1: cssVar("--graph-bg-1"),
+ bg_2: cssVar("--graph-bg-2"),
+ fg_0: cssVar("--graph-fg-0"),
+ fg_1: cssVar("--graph-fg-1"),
+ fg_2: cssVar("--graph-fg-2"),
+
+ node_anchor: cssVar("--graph-node-anchor"),
+ node_border: cssVar("--graph-node-border"),
+ current_node_border: cssVar("--graph-current-node-border"),
+
+ edge: cssVar("--graph-edge"),
+ edge_hover: cssVar("--graph-edge-hover"),
+ edge_active: cssVar("--graph-edge-active"),
+
+ edge_font_size: Number(cssVar("--graph-edge-font-size")),
+ node_font_size: Number(cssVar("--graph-node-font-size")),
+ };
+
+ return _graphTheme;
+}
+
+export function updateGraphTheme() {
+ invalidateGraphThemeCache();
+ const gt = getGraphTheme();
+
+ network.setOptions({
+ nodes: {
+ font: {
+ color: gt.fg_0,
+ bold: {
+ color: gt.fg_1,
+ mod: ''
+ },
+ },
+ },
+ edges: {
+ labelHighlightBold: true,
+ font: {
+ align: "top",
+ size: gt.edge_font_size,
+ color: gt.fg_0,
+ strokeColor: gt.bg_0,
+ bold: {
+ color: gt.fg_1,
+ size: gt.edge_font_size,
+ mod: ''
+ },
+ },
+ color: {
+ color: gt.edge,
+ hover: gt.edge_hover,
+ highlight: gt.edge_active,
+ },
+ shadow: {
+ enabled: false,
+ }
+ },
+ });
+}
+
+
type StateId = string;
type GraphDef = {
initial: StateId;
@@ -163,58 +263,6 @@ function createGraph(): vis.Network {
return network;
}
-export type GraphTheme = {
- bg_0: string;
- bg_1: string;
- bg_2: string;
- fg_0: string;
-
- anchor: string;
- selected: string;
- node: string;
- current: string;
- edge: string;
- glow: string;
- edge_font_size: number,
-};
-
-let _graphTheme: GraphTheme | null = null;
-
-export function invalidateGraphThemeCache() {
- _graphTheme = null;
-}
-
-export function getGraphTheme(): GraphTheme {
- function cssVar(name: string, fallback = ""): string {
- return getComputedStyle(document.documentElement)
- .getPropertyValue(name)
- .trim() || fallback;
- }
-
- if (_graphTheme) return _graphTheme;
-
- _graphTheme = {
- bg_0: cssVar("--bg-0"),
- bg_1: cssVar("--bg-1"),
- bg_2: cssVar("--bg-2"),
- fg_0: cssVar("--fg-0"),
-
- selected: cssVar("--bg-2"),
-
- node: cssVar("--focus"),
- current: cssVar("--success"),
-
- anchor: cssVar("--warning"),
-
- edge: cssVar("--graph-edge", "rgba(201,209,217,0.55)"),
-
- glow: cssVar("--accent", "#79c0ff"),
- edge_font_size: 10,
- };
-
- return _graphTheme;
-}
-
function renderNode({
ctx,
id,
@@ -236,8 +284,8 @@ function renderNode({
const isFinal = id === "q1"; // <-- change if your schema differs
const isActive = id === "q0"; // <-- change if your schema differs
- const fill = selected ? t.glow : hover ? t.bg_1 : t.bg_0;
- const stroke = isActive ? t.current : t.node;
+ const fill = selected ? t.bg_2 : hover ? t.bg_1 : t.bg_0;
+ const stroke = isActive ? t.current_node_border : t.node_border;
const emphasis = (selected ? 1 : 0) + (hover ? 0.6 : 0);
@@ -306,7 +354,7 @@ function renderNode({
const physicsOff = node.physics === false;
if (physicsOff) {
- drawPinIndicator(ctx, x, y, r, t.anchor);
+ drawPinIndicator(ctx, x, y, r, t.node_anchor);
}
ctx.restore();
diff --git a/web/root/src/wasm.ts b/web/root/src/wasm.ts
index 74f9084..80bf4a2 100644
--- a/web/root/src/wasm.ts
+++ b/web/root/src/wasm.ts
@@ -9,7 +9,7 @@ try{
wasm.init();
}catch(e){
console.error("Failed to start: " + e);
- document.getElementById("the_canvas_id")!.remove();
+ document.getElementById("app")!.remove();
document.getElementById("center_text")!.innerHTML = `
An error occurred during loading:
@@ -20,6 +20,7 @@ try{
Make sure you use a modern browser with WebGL and WASM enabled.
`;
+ throw e;
}
export default wasm
\ No newline at end of file
diff --git a/web/root/style/themes.scss b/web/root/style/themes.scss
index 58710a4..ac86de9 100644
--- a/web/root/style/themes.scss
+++ b/web/root/style/themes.scss
@@ -29,6 +29,25 @@
--dur-med: 160ms;
--dur-slow: 240ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
+
+
+ --graph-bg-0: var(--bg-0);
+ --graph-bg-1: var(--bg-1);
+ --graph-bg-2: var(--bg-2);
+ --graph-fg-0: var(--fg-0);
+ --graph-fg-1: var(--fg-1);
+ --graph-fg-2: var(--fg-2);
+
+ --graph-node-font-size: 14;
+ --graph-edge-font-size: 10;
+
+ --graph-node-border: var(--focus);
+ --graph-current-node-border: var(--success);
+ --graph-node-anchor: var(--warning);
+
+ --graph-edge: var(--fg-muted);
+ --graph-edge-hover: var(--accent);
+ --graph-edge-active: var(--focus);
}
:root[data-theme="dark"] {
@@ -52,20 +71,6 @@
--warning: #f2cc60;
--error: #f85149;
- --graph-bg: var(--bg-0);
-
- --graph-node-bg: #1f6feb;
- --graph-node-border: #388bfd;
- --graph-node-text: var(--fg-0);
-
- --graph-node-active-bg: #79c0ff;
- --graph-node-active-border: #ff0000;
-
- --graph-edge: rgba(201, 209, 217, 0.55);
- --graph-edge-hover: rgba(201, 209, 217, 0.864);
- --graph-edge-active: var(--accent);
-
-
--ansi-fg-30: #0b0f14; /* black */
--ansi-fg-31: #ff7b72; /* red */
--ansi-fg-32: #7ee787; /* green */
@@ -115,21 +120,6 @@
--warning: #9a6700;
--error: #cf222e;
- --graph-bg: var(--bg-0);
-
- --graph-node-bg: #1f6feb;
- --graph-node-border: #0969da;
- --graph-node-text: #ffffff;
-
- --graph-node-active-bg: #54aeff;
- --graph-node-active-border: #0969da;
-
- --graph-edge: rgba(31, 41, 55, 0.45);
- --graph-edge-hover: #0969da;
- --graph-edge-active: #54aeff;
-
-
-
--ansi-fg-30: #111827; /* black */
--ansi-fg-31: #b42318; /* red */
--ansi-fg-32: #1a7f37; /* green */
diff --git a/web/tools/build.ts b/web/tools/build.ts
index bc88fe0..fa4834d 100644
--- a/web/tools/build.ts
+++ b/web/tools/build.ts
@@ -14,11 +14,14 @@ async function run(cmd: string[], cwd?: string) {
}
}
-// Clean dist
+console.log("clean dist...");
await Deno.remove(DIST, { recursive: true }).catch(() => {});
await Deno.mkdir(DIST, { recursive: true });
+console.log("copy assets");
+await copyFolder(new URL("assets/", ROOT), DIST);
+
console.log("compiling scss...");
const result = sass.compile(String(new URL("style/style.scss", ROOT).pathname), {
style: "compressed",
@@ -40,3 +43,23 @@ if (!bundleRes.success) {
}
console.log("Build complete: dist/");
+
+
+
+export async function copyFolder(
+ srcDir: URL,
+ destDir: URL,
+): Promise {
+ await Deno.mkdir(destDir, { recursive: true });
+
+ for await (const entry of Deno.readDir(srcDir)) {
+ const srcPath = new URL(entry.name, srcDir.href);
+ const destPath = new URL(entry.name, destDir.href);
+
+ if (entry.isDirectory) {
+ await copyFolder(srcPath, destPath);
+ } else if (entry.isFile) {
+ await Deno.copyFile(srcPath, destPath);
+ }
+ }
+}
\ No newline at end of file