mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-06 21:24:06 -04:00
semi finalized theming
This commit is contained in:
parent
c12f7b325f
commit
620415c824
14 changed files with 728 additions and 224 deletions
|
|
@ -28,17 +28,45 @@
|
||||||
<div class="vSplit" style="--split-default: 20%" title="Drag to resize canvas width"></div>
|
<div class="vSplit" style="--split-default: 20%" title="Drag to resize canvas width"></div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
meow
|
<button id="themeToggle" class="btn btn-grey" title="Toggle light/dark">
|
||||||
|
🌙 Dark
|
||||||
|
</button>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="hSplit" style="--split-default: 50%" title="Drag to resize canvas height"></div>
|
<div class="hSplit" style="--split-default: 50%" title="Drag to resize canvas height"></div>
|
||||||
|
|
||||||
<div style="padding-bottom: 10px;">
|
<div style="padding-bottom: 10px">
|
||||||
<div>
|
<div class="controls" style="background: var(--bg-0)">
|
||||||
<button id="togglePhysics">Toggle Physics</button>
|
<button id="togglePhysics" class="btn btn-toggle active" title="Toggle physics layout">
|
||||||
<button id="resetLayout">Reset Layout</button>
|
Physics: ON
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="resetLayout" class="btn btn-grey" title="Re-enable physics on nodes and reset layout">
|
||||||
|
Reset Layout
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span class="spacer"></span>
|
||||||
|
|
||||||
|
<button id="resetSim" class="btn btn-blue" title="Stop and reset simulation">
|
||||||
|
⟲ Reset
|
||||||
|
</button>
|
||||||
|
<button id="playPause" class="btn btn-green" title="Run / pause simulation">
|
||||||
|
▶ Play
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="step" class="btn btn-grey" title="Advance one step">
|
||||||
|
Step
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<label class="speed">
|
||||||
|
Speed
|
||||||
|
<input id="speed" type="range" min="1" max="60" value="10" />
|
||||||
|
<span id="speedLabel">10x</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="hSplit styleOnly" title="Drag to resize canvas height"></div>
|
||||||
|
|
||||||
<div style="height:100%">
|
<div style="height:100%">
|
||||||
<div class="vscroll">
|
<div class="vscroll">
|
||||||
<div id="editor" class="editor"></div>
|
<div id="editor" class="editor"></div>
|
||||||
|
|
|
||||||
116
web/root/src/controls.ts
Normal file
116
web/root/src/controls.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
import {nodes, edges, network} from "./visualizer.ts"
|
||||||
|
|
||||||
|
const togglePhysicsBtn = document.getElementById("togglePhysics") as HTMLButtonElement;
|
||||||
|
const resetLayoutBtn = document.getElementById("resetLayout") as HTMLButtonElement;
|
||||||
|
const playPauseBtn = document.getElementById("playPause") as HTMLButtonElement;
|
||||||
|
const stepBtn = document.getElementById("step") as HTMLButtonElement;
|
||||||
|
const speedSlider = document.getElementById("speed") as HTMLInputElement;
|
||||||
|
const speedLabel = document.getElementById("speedLabel") as HTMLSpanElement;
|
||||||
|
const resetSimBtn = document.getElementById("resetSim") as HTMLButtonElement;
|
||||||
|
|
||||||
|
|
||||||
|
function stepSimulation(): void {
|
||||||
|
console.log("step");
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSimulation(): void {
|
||||||
|
console.log("reset");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Physics toggle (styled label) ----
|
||||||
|
function setPhysicsButtonUI(enabled: boolean) {
|
||||||
|
togglePhysicsBtn.classList.toggle("active", enabled);
|
||||||
|
togglePhysicsBtn.textContent = enabled ? "Physics: ON" : "Physics: OFF";
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePhysicsBtn.onclick = () => {
|
||||||
|
const enabled = !togglePhysicsBtn.classList.contains("active");
|
||||||
|
setPhysicsButtonUI(enabled);
|
||||||
|
network.setOptions({ physics: { enabled } });
|
||||||
|
};
|
||||||
|
|
||||||
|
setPhysicsButtonUI(togglePhysicsBtn.classList.contains("active"));
|
||||||
|
|
||||||
|
resetLayoutBtn.onclick = () => {
|
||||||
|
try {
|
||||||
|
nodes.forEach((n) => {
|
||||||
|
n.physics = true;
|
||||||
|
n.x = undefined;
|
||||||
|
n.y = undefined;
|
||||||
|
});
|
||||||
|
network.setData({ nodes, edges });
|
||||||
|
} catch {
|
||||||
|
// Last resort
|
||||||
|
network.setData({ nodes, edges });
|
||||||
|
}
|
||||||
|
|
||||||
|
// If physics button is OFF, keep it OFF (don’t surprise the user)
|
||||||
|
const physicsEnabled = togglePhysicsBtn.classList.contains("active");
|
||||||
|
network.setOptions({ physics: { enabled: physicsEnabled } });
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- Play/Pause + Speed ----
|
||||||
|
let running = false;
|
||||||
|
let timer: number | null = null;
|
||||||
|
|
||||||
|
// speed slider is "steps per second"
|
||||||
|
function getStepsPerSecond() {
|
||||||
|
return Math.max(1, Math.min(60, Number(speedSlider.value) || 10));
|
||||||
|
}
|
||||||
|
function updateSpeedUI() {
|
||||||
|
speedLabel.textContent = `${getStepsPerSecond()}×`;
|
||||||
|
}
|
||||||
|
updateSpeedUI();
|
||||||
|
|
||||||
|
speedSlider.addEventListener("input", () => {
|
||||||
|
updateSpeedUI();
|
||||||
|
if (running) restartTimer();
|
||||||
|
});
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
if (timer !== null) {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartTimer() {
|
||||||
|
stopTimer();
|
||||||
|
const sps = getStepsPerSecond();
|
||||||
|
const intervalMs = Math.round(1000 / sps);
|
||||||
|
|
||||||
|
timer = globalThis.window.setInterval(() => {
|
||||||
|
// If your step can throw, keep the interval alive:
|
||||||
|
try { stepSimulation(); } catch (e) { console.error(e); }
|
||||||
|
}, intervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRunning(on: boolean) {
|
||||||
|
running = on;
|
||||||
|
playPauseBtn.textContent = running ? "⏸ Pause" : "▶ Play";
|
||||||
|
playPauseBtn.classList.toggle("btn-primary", !running);
|
||||||
|
playPauseBtn.classList.toggle("btn-secondary", running);
|
||||||
|
|
||||||
|
// Disable step while running (optional, but feels nice)
|
||||||
|
stepBtn.disabled = running;
|
||||||
|
|
||||||
|
if (running) restartTimer();
|
||||||
|
else stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
playPauseBtn.onclick = () => setRunning(!running);
|
||||||
|
|
||||||
|
stepBtn.onclick = () => {
|
||||||
|
stepSimulation();
|
||||||
|
};
|
||||||
|
|
||||||
|
resetSimBtn.onclick = () => {
|
||||||
|
// Stop if running
|
||||||
|
if (running) setRunning(false);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
resetSimulation();
|
||||||
|
|
||||||
|
// Optional: re-enable Step after reset
|
||||||
|
stepBtn.disabled = false;
|
||||||
|
};
|
||||||
|
|
@ -8,13 +8,13 @@ import {
|
||||||
ViewPlugin,
|
ViewPlugin,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
|
highlightActiveLine
|
||||||
} from "npm:@codemirror/view";
|
} from "npm:@codemirror/view";
|
||||||
|
|
||||||
import { EditorState, StateField, Text } from "npm:@codemirror/state";
|
import { EditorState, StateField, Text } from "npm:@codemirror/state";
|
||||||
import { defaultKeymap, history, historyKeymap } from "npm:@codemirror/commands";
|
import { defaultKeymap, history, historyKeymap } from "npm:@codemirror/commands";
|
||||||
import { bracketMatching, indentOnInput } from "npm:@codemirror/language";
|
import { bracketMatching, indentOnInput } from "npm:@codemirror/language";
|
||||||
import { closeBrackets } from "npm:@codemirror/autocomplete";
|
import { closeBrackets } from "npm:@codemirror/autocomplete";
|
||||||
import { oneDark } from "npm:@codemirror/theme-one-dark";
|
|
||||||
|
|
||||||
|
|
||||||
import wasm from "./wasm.ts"
|
import wasm from "./wasm.ts"
|
||||||
|
|
@ -292,9 +292,9 @@ const state = EditorState.create({
|
||||||
history(),
|
history(),
|
||||||
indentOnInput(),
|
indentOnInput(),
|
||||||
bracketMatching(),
|
bracketMatching(),
|
||||||
|
highlightActiveLine(),
|
||||||
closeBrackets(),
|
closeBrackets(),
|
||||||
keymap.of([...defaultKeymap, ...historyKeymap]),
|
keymap.of([...defaultKeymap, ...historyKeymap]),
|
||||||
oneDark,
|
|
||||||
|
|
||||||
analysisField,
|
analysisField,
|
||||||
diagHover,
|
diagHover,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
import "./editor.ts"
|
import "./editor.ts"
|
||||||
import "./visualizer.ts"
|
import "./visualizer.ts"
|
||||||
import "./splitters.ts"
|
import "./splitters.ts"
|
||||||
|
import "./controls.ts"
|
||||||
|
import "./theme.ts"
|
||||||
|
|
@ -65,7 +65,7 @@ function setFlexFill(pane: HTMLElement) {
|
||||||
|
|
||||||
export function enableFlexSplitters() {
|
export function enableFlexSplitters() {
|
||||||
// Horizontal: A | hSplit | B (top/split/bottom)
|
// Horizontal: A | hSplit | B (top/split/bottom)
|
||||||
for (const splitter of document.querySelectorAll<HTMLElement>(".hSplit")) {
|
for (const splitter of document.querySelectorAll<HTMLElement>(".hSplit:not(.styleOnly)")) {
|
||||||
const parent = splitter.parentElement as HTMLElement | null;
|
const parent = splitter.parentElement as HTMLElement | null;
|
||||||
if (!parent) continue;
|
if (!parent) continue;
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ export function enableFlexSplitters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical: A | vSplit | B (left/split/right)
|
// Vertical: A | vSplit | B (left/split/right)
|
||||||
for (const splitter of document.querySelectorAll<HTMLElement>(".vSplit")) {
|
for (const splitter of document.querySelectorAll<HTMLElement>(".vSplit:not(.styleOnly)")) {
|
||||||
const parent = splitter.parentElement as HTMLElement | null;
|
const parent = splitter.parentElement as HTMLElement | null;
|
||||||
if (!parent) continue;
|
if (!parent) continue;
|
||||||
|
|
||||||
|
|
@ -158,7 +158,7 @@ export function enableFlexSplitters() {
|
||||||
// --split-default: 30% (right pane width)
|
// --split-default: 30% (right pane width)
|
||||||
// --split-min-a: 220px (min left)
|
// --split-min-a: 220px (min left)
|
||||||
// --split-min-b: 220px (min right)
|
// --split-min-b: 220px (min right)
|
||||||
const defPct = getVarPct(splitter, "--split-default", 30);
|
const defPct = getVarPct(splitter, "--split-default", 50);
|
||||||
const minA = getVarPx(splitter, "--split-min-a", 220);
|
const minA = getVarPx(splitter, "--split-min-a", 220);
|
||||||
const minB = getVarPx(splitter, "--split-min-b", 220);
|
const minB = getVarPx(splitter, "--split-min-b", 220);
|
||||||
|
|
||||||
|
|
|
||||||
75
web/root/src/theme.ts
Normal file
75
web/root/src/theme.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { network } from "./visualizer.ts";
|
||||||
|
|
||||||
|
function cssVar(name: string, fallback = ""): string {
|
||||||
|
return getComputedStyle(document.documentElement)
|
||||||
|
.getPropertyValue(name)
|
||||||
|
.trim() || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeBtn = document.getElementById("themeToggle") as HTMLButtonElement;
|
||||||
|
|
||||||
|
type Theme = "dark" | "light";
|
||||||
|
|
||||||
|
function getPreferredTheme(): Theme {
|
||||||
|
// 1) saved preference
|
||||||
|
const saved = localStorage.getItem("theme");
|
||||||
|
if (saved === "dark" || saved === "light") return saved;
|
||||||
|
|
||||||
|
// 2) OS preference
|
||||||
|
const prefersLight = globalThis.window.matchMedia?.(
|
||||||
|
"(prefers-color-scheme: light)",
|
||||||
|
)?.matches;
|
||||||
|
return prefersLight ? "light" : "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTheme(theme: Theme) {
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
|
||||||
|
// update button label
|
||||||
|
themeBtn.textContent = theme === "dark" ? "🌙 Dark" : "☀️ Light";
|
||||||
|
applyGraphTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
setTheme(getPreferredTheme());
|
||||||
|
});
|
||||||
|
|
||||||
|
export function applyGraphTheme() {
|
||||||
|
network.setOptions({
|
||||||
|
nodes: {
|
||||||
|
color: {
|
||||||
|
background: cssVar("--graph-node-bg"),
|
||||||
|
border: cssVar("--graph-node-border"),
|
||||||
|
highlight: {
|
||||||
|
background: cssVar("--graph-node-active-bg"),
|
||||||
|
border: cssVar("--graph-node-active-border"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
color: cssVar("--graph-node-text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
color: {
|
||||||
|
color: cssVar("--graph-edge"),
|
||||||
|
highlight: cssVar("--graph-edge-active"),
|
||||||
|
hover: cssVar("--graph-edge-hover"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -3,11 +3,8 @@
|
||||||
// deno-lint-ignore no-import-prefix
|
// deno-lint-ignore no-import-prefix
|
||||||
import * as vis from "npm:vis-network/standalone";
|
import * as vis from "npm:vis-network/standalone";
|
||||||
|
|
||||||
|
export const nodes = new vis.DataSet<vis.Node>();
|
||||||
|
export const edges = new vis.DataSet<vis.Edge>();
|
||||||
const nodes = new vis.DataSet<vis.Node>();
|
|
||||||
const edges = new vis.DataSet<vis.Edge>();
|
|
||||||
|
|
||||||
|
|
||||||
const automaton = {
|
const automaton = {
|
||||||
states: ["q0", "q1"],
|
states: ["q0", "q1"],
|
||||||
|
|
@ -18,29 +15,29 @@ const automaton = {
|
||||||
{
|
{
|
||||||
from: "q0",
|
from: "q0",
|
||||||
to: "q0",
|
to: "q0",
|
||||||
label: "ε, z0 → A z0\n"
|
label: "ε, z0 → A z0\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "q0",
|
from: "q0",
|
||||||
to: "q0",
|
to: "q0",
|
||||||
label: "ε, z0 → B z0"
|
label: "ε, z0 → B z0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "q0",
|
from: "q0",
|
||||||
to: "q1",
|
to: "q1",
|
||||||
label: "ε, z0 → z0"
|
label: "ε, z0 → z0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "q1",
|
from: "q1",
|
||||||
to: "q1",
|
to: "q1",
|
||||||
label: "a, A → ε"
|
label: "a, A → ε",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "q1",
|
from: "q1",
|
||||||
to: "q1",
|
to: "q1",
|
||||||
label: "b, B → ε"
|
label: "b, B → ε",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderNode({
|
function renderNode({
|
||||||
|
|
@ -57,7 +54,6 @@ function renderNode({
|
||||||
ctx.save();
|
ctx.save();
|
||||||
const r = style.size;
|
const r = style.size;
|
||||||
|
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(x, y, r, 0, 2 * Math.PI);
|
ctx.arc(x, y, r, 0, 2 * Math.PI);
|
||||||
ctx.fillStyle = "red";
|
ctx.fillStyle = "red";
|
||||||
|
|
@ -67,29 +63,27 @@ function renderNode({
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "black";
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = "center";
|
||||||
ctx.fillText(label, x, y, r);
|
ctx.fillText(label, x, y, r);
|
||||||
|
|
||||||
|
ctx.textAlign = "center";
|
||||||
ctx.textAlign = 'center';
|
ctx.strokeStyle = "white";
|
||||||
ctx.strokeStyle = 'white';
|
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "black";
|
||||||
let cy = y - (r + 10);
|
let cy = y - (r + 10);
|
||||||
for (const part of "meow[]\nbeeep".split("\n").reverse()) {
|
for (const part of "meow[]\nbeeep".split("\n").reverse()) {
|
||||||
const metrics = ctx.measureText(part);
|
const metrics = ctx.measureText(part);
|
||||||
cy -= metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
|
cy -= metrics.actualBoundingBoxAscent +
|
||||||
|
metrics.actualBoundingBoxDescent;
|
||||||
ctx.strokeText(part, x, cy);
|
ctx.strokeText(part, x, cy);
|
||||||
ctx.fillText(part, x, cy);
|
ctx.fillText(part, x, cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
},
|
},
|
||||||
nodeDimensions: { width: 20, height: 20 },
|
nodeDimensions: { width: 20, height: 20 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Populate nodes
|
// Populate nodes
|
||||||
for (const state of automaton.states) {
|
for (const state of automaton.states) {
|
||||||
nodes.add({
|
nodes.add({
|
||||||
|
|
@ -104,24 +98,31 @@ automaton.transitions.forEach((t, i) => {
|
||||||
id: `e${i}`,
|
id: `e${i}`,
|
||||||
from: t.from,
|
from: t.from,
|
||||||
to: t.to,
|
to: t.to,
|
||||||
label: t.label
|
label: t.label,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function chosen_edge(
|
||||||
function chosen_edge(_: vis.ChosenNodeValues, id: vis.IdType,selected: boolean, hovered: boolean) {
|
_: vis.ChosenNodeValues,
|
||||||
console.log("edge", id, selected, hovered)
|
id: vis.IdType,
|
||||||
|
selected: boolean,
|
||||||
|
hovered: boolean,
|
||||||
|
) {
|
||||||
|
console.log("edge", id, selected, hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
function chosen_node(_: vis.ChosenNodeValues, id: vis.IdType,selected: boolean, hovered: boolean) {
|
function chosen_node(
|
||||||
console.log("node", id, selected, hovered)
|
_: vis.ChosenNodeValues,
|
||||||
|
id: vis.IdType,
|
||||||
|
selected: boolean,
|
||||||
|
hovered: boolean,
|
||||||
|
) {
|
||||||
|
console.log("node", id, selected, hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const network: vis.Network = createGraph();
|
||||||
const network: vis.Network = createGraph();
|
|
||||||
|
|
||||||
function createGraph(): vis.Network {
|
function createGraph(): vis.Network {
|
||||||
|
|
||||||
const container = document.getElementById("graph")!;
|
const container = document.getElementById("graph")!;
|
||||||
|
|
||||||
const network = new vis.Network(
|
const network = new vis.Network(
|
||||||
|
|
@ -129,13 +130,15 @@ function createGraph(): vis.Network {
|
||||||
{ nodes, edges },
|
{ nodes, edges },
|
||||||
{
|
{
|
||||||
layout: { improvedLayout: true },
|
layout: { improvedLayout: true },
|
||||||
autoResize: true,
|
|
||||||
width: "99%",
|
|
||||||
physics: {
|
physics: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
solver: "barnesHut",
|
solver: "barnesHut",
|
||||||
barnesHut: { gravitationalConstant: -8000, springLength: 120, springConstant: 0.04 },
|
barnesHut: {
|
||||||
stabilization: { iterations: 200 }
|
gravitationalConstant: -8000,
|
||||||
|
springLength: 120,
|
||||||
|
springConstant: 0.04,
|
||||||
|
},
|
||||||
|
stabilization: { iterations: 200 },
|
||||||
},
|
},
|
||||||
interaction: {
|
interaction: {
|
||||||
dragNodes: true,
|
dragNodes: true,
|
||||||
|
|
@ -149,7 +152,7 @@ function createGraph(): vis.Network {
|
||||||
color: {
|
color: {
|
||||||
background: "#1f6feb",
|
background: "#1f6feb",
|
||||||
border: "#79c0ff",
|
border: "#79c0ff",
|
||||||
highlight: { background: "#388bfd", border: "#a5d6ff" }
|
highlight: { background: "#388bfd", border: "#a5d6ff" },
|
||||||
},
|
},
|
||||||
// @ts-expect-error bad library
|
// @ts-expect-error bad library
|
||||||
chosen: {
|
chosen: {
|
||||||
|
|
@ -162,7 +165,7 @@ function createGraph(): vis.Network {
|
||||||
edges: {
|
edges: {
|
||||||
chosen: {
|
chosen: {
|
||||||
// @ts-expect-error bad library
|
// @ts-expect-error bad library
|
||||||
edge: chosen_edge
|
edge: chosen_edge,
|
||||||
},
|
},
|
||||||
arrowStrikethrough: false,
|
arrowStrikethrough: false,
|
||||||
font: { align: "middle", color: "#000000ff" },
|
font: { align: "middle", color: "#000000ff" },
|
||||||
|
|
@ -170,18 +173,17 @@ function createGraph(): vis.Network {
|
||||||
// @ts-expect-error bad library
|
// @ts-expect-error bad library
|
||||||
smooth: { type: "dynamic" },
|
smooth: { type: "dynamic" },
|
||||||
arrows: "to",
|
arrows: "to",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
vis.DataSet
|
vis.DataSet;
|
||||||
|
|
||||||
network.on("doubleClick", (params: any) => {
|
network.on("doubleClick", (params: any) => {
|
||||||
|
for (const node_id of params.nodes) {
|
||||||
for (const node_id of params.nodes){
|
|
||||||
// @ts-expect-error bad library
|
// @ts-expect-error bad library
|
||||||
const node: vis.Node = nodes.get(node_id)!;
|
const node: vis.Node = nodes.get(node_id)!;
|
||||||
node.physics = !node.physics;
|
node.physics = !node.physics;
|
||||||
nodes.update(node)
|
nodes.update(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
99
web/root/style/controls.scss
Normal file
99
web/root/style/controls.scss
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-2);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls .spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
appearance: none;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
color: var(--fg-0);
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font:
|
||||||
|
600 13px/1.1
|
||||||
|
var(--font-ui);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
transform 0.04s ease,
|
||||||
|
background var(--dur-med) var(--ease-standard),
|
||||||
|
border-color var(--dur-med) var(--ease-standard),
|
||||||
|
opacity var(--dur-med) var(--ease-standard);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.10);
|
||||||
|
border-color: rgba(255, 255, 255, 0.20);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-green {
|
||||||
|
background: color-mix(in srgb, var(--success) 18%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--success) 35%, transparent);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color-mix(in srgb, var(--success) 26%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blue {
|
||||||
|
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color-mix(in srgb, var(--accent) 22%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-grey {
|
||||||
|
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--accent) 28%, transparent);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle.active {
|
||||||
|
background: color-mix(in srgb, var(--warning) 14%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--warning) 30%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.speed {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-1);
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font: 600 12.5px var(--font-ui);
|
||||||
|
color: var(--fg-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.speed input[type="range"] {
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.speed #speedLabel {
|
||||||
|
min-width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
@ -1,126 +1,131 @@
|
||||||
@use "tooltip.scss";
|
@use "tooltip.scss";
|
||||||
|
|
||||||
|
/* Editor layout */
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-scroller {
|
||||||
|
overflow-y: auto !important;
|
||||||
|
background: var(--bg-0);
|
||||||
|
}
|
||||||
|
|
||||||
.cm-editor {
|
.cm-editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: var(--bg-1);
|
||||||
|
color: var(--fg-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-scroller {
|
.cm-gutters {
|
||||||
overflow-y: auto !important;
|
background: var(--bg-2) !important;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
border-right: 1px solid color-mix(in srgb, var(--fg-muted) 20%, transparent)!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-lineNumbers .cm-gutterElement {
|
||||||
|
padding: 0 10px 0 6px;
|
||||||
.diag {
|
font-family: var(--font-mono);
|
||||||
margin: 0;
|
font-size: 12px;
|
||||||
padding-left: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.diag li {
|
.cm-activeLine {
|
||||||
margin: 6px 0;
|
background: color-mix(in srgb, var(--accent) 6%, transparent)!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Syntax colors via CSS classes applied by decorations --- */
|
.cm-activeLineGutter {
|
||||||
|
background: color-mix(in srgb, var(--accent) 8%, transparent)!important;
|
||||||
|
color: var(--fg-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-cursor {
|
||||||
|
border-left: 2px solid var(--accent)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-focused .cm-cursor {
|
||||||
|
border-left-color: var(--accent)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Syntax colors */
|
||||||
|
|
||||||
.tok-comment {
|
.tok-comment {
|
||||||
color: #1a7b24;
|
color: color-mix(in srgb, var(--success) 65%, var(--fg-muted));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-keyword {
|
.tok-keyword {
|
||||||
color: #b99400;
|
color: var(--warning);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-error {
|
.tok-error {
|
||||||
color: #ff0505;
|
color: var(--error);
|
||||||
font-weight: 1000;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-ident {
|
.tok-ident {
|
||||||
color: #90d4e0;
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-brace {
|
.tok-brace {
|
||||||
color: #d73a49;
|
color: var(--error);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-punc {
|
.tok-punc {
|
||||||
color: #ffffff;
|
color: var(--fg-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tok-string {
|
.tok-string {
|
||||||
color: #03621e;
|
color: color-mix(in srgb, var(--success) 75%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rainbow bracket depth classes */
|
/* Rainbow brackets */
|
||||||
|
|
||||||
.rb-0 {
|
.rb-0 {
|
||||||
color: #a35;
|
color: color-mix(in srgb, var(--error) 85%, transparent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-1 {
|
.rb-1 {
|
||||||
color: #ed0;
|
color: color-mix(in srgb, var(--warning) 85%, transparent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-2 {
|
.rb-2 {
|
||||||
color: #9d5;
|
color: color-mix(in srgb, var(--success) 85%, transparent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-3 {
|
.rb-3 {
|
||||||
color: #2cb;
|
color: color-mix(in srgb, var(--accent) 85%, transparent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-4 {
|
.rb-4 {
|
||||||
color: #36b;
|
color: color-mix(in srgb, var(--focus) 85%, transparent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rb-5 {
|
.rb-5 {
|
||||||
color: #639;
|
color: color-mix(in srgb, var(--accent) 60%, var(--fg-muted));
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Severity underline styles*/
|
||||||
|
|
||||||
|
|
||||||
/* Optional: diagnostics panel coloring */
|
|
||||||
.diag li.error {
|
|
||||||
color: #d73a49;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diag li.warning {
|
|
||||||
color: #b08800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diag li.info {
|
|
||||||
color: #0366d6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Severity underline styles */
|
|
||||||
.cm-diag-error {
|
.cm-diag-error {
|
||||||
text-decoration: underline wavy #d73a49;
|
text-decoration: underline wavy var(--error);
|
||||||
/* red */
|
|
||||||
text-underline-offset: 2px;
|
text-underline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-diag-warning {
|
.cm-diag-warning {
|
||||||
text-decoration: underline wavy #ffd33d;
|
text-decoration: underline wavy var(--warning);
|
||||||
/* yellow */
|
|
||||||
text-underline-offset: 2px;
|
text-underline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-diag-info {
|
.cm-diag-info {
|
||||||
text-decoration: underline wavy #79c0ff;
|
text-decoration: underline wavy var(--accent);
|
||||||
/* cyan-ish */
|
|
||||||
text-underline-offset: 2px;
|
text-underline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
@use "editor.scss";
|
@use "editor.scss";
|
||||||
@use "terminal.scss";
|
@use "terminal.scss";
|
||||||
@use "loading.scss";
|
@use "loading.scss";
|
||||||
|
@use "controls.scss";
|
||||||
|
@use "themes.scss";
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: system-ui, sans-serif;
|
color: var(--fg-0);
|
||||||
|
font-family: var(--font-ui);
|
||||||
background: #909090;
|
background: #909090;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,12 +18,13 @@ body {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background-color: var(--bg-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph {
|
.graph {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #111;
|
background: var(--graph-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vscroll {
|
.vscroll {
|
||||||
|
|
@ -29,18 +33,28 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hSplit {
|
.hSplit {
|
||||||
cursor: row-resize;
|
:not( .styleOnly){
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
height: 8px;
|
height: 8px;
|
||||||
background: rgba(255, 255, 255, 0.06);
|
background: var(--separator-bg);
|
||||||
|
transition:
|
||||||
|
background var(--dur-med) var(--ease-standard),
|
||||||
|
box-shadow var(--dur-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vSplit {
|
.vSplit {
|
||||||
|
:not( .styleOnly){
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
|
}
|
||||||
width: 8px;
|
width: 8px;
|
||||||
background: rgba(255, 255, 255, 0.06);
|
background: var(--separator-bg);
|
||||||
|
transition:
|
||||||
|
background var(--dur-med) var(--ease-standard),
|
||||||
|
box-shadow var(--dur-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hSplit:hover,
|
.hSplit:hover:not(.styleOnly),
|
||||||
.vSplit:hover {
|
.vSplit:hover:not(.styleOnly) {
|
||||||
background: rgba(121, 192, 255, 0.25);
|
background: var(--separator-hover);
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
.terminal {
|
.terminal {
|
||||||
background: #0b0f14;
|
background: var(--bg-1);
|
||||||
color: #c9d1d9;
|
color: var(--fg-0);
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
font-family: var(--font-mono);
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
@ -18,31 +18,31 @@
|
||||||
.ansi-bold { font-weight: 700; }
|
.ansi-bold { font-weight: 700; }
|
||||||
.ansi-dim { opacity: 0.7; }
|
.ansi-dim { opacity: 0.7; }
|
||||||
|
|
||||||
/* Foreground colors (standard + bright) */
|
/* Foreground colors */
|
||||||
.ansi-fg-30 { color: #0b0f14; } /* black */
|
.ansi-fg-30 { color: var(--ansi-fg-30); }
|
||||||
.ansi-fg-31 { color: #ff7b72; } /* red */
|
.ansi-fg-31 { color: var(--ansi-fg-31); }
|
||||||
.ansi-fg-32 { color: #7ee787; } /* green */
|
.ansi-fg-32 { color: var(--ansi-fg-32); }
|
||||||
.ansi-fg-33 { color: #f2cc60; } /* yellow */
|
.ansi-fg-33 { color: var(--ansi-fg-33); }
|
||||||
.ansi-fg-34 { color: #79c0ff; } /* blue */
|
.ansi-fg-34 { color: var(--ansi-fg-34); }
|
||||||
.ansi-fg-35 { color: #d2a8ff; } /* magenta */
|
.ansi-fg-35 { color: var(--ansi-fg-35); }
|
||||||
.ansi-fg-36 { color: #a5d6ff; } /* cyan */
|
.ansi-fg-36 { color: var(--ansi-fg-36); }
|
||||||
.ansi-fg-37 { color: #c9d1d9; } /* white */
|
.ansi-fg-37 { color: var(--ansi-fg-37); }
|
||||||
|
|
||||||
.ansi-fg-90 { color: #6e7681; } /* bright black / gray */
|
.ansi-fg-90 { color: var(--ansi-fg-90); }
|
||||||
.ansi-fg-91 { color: #ffa198; }
|
.ansi-fg-91 { color: var(--ansi-fg-91); }
|
||||||
.ansi-fg-92 { color: #a6f3a6; }
|
.ansi-fg-92 { color: var(--ansi-fg-92); }
|
||||||
.ansi-fg-93 { color: #ffe082; }
|
.ansi-fg-93 { color: var(--ansi-fg-93); }
|
||||||
.ansi-fg-94 { color: #a5d6ff; }
|
.ansi-fg-94 { color: var(--ansi-fg-94); }
|
||||||
.ansi-fg-95 { color: #e3b8ff; }
|
.ansi-fg-95 { color: var(--ansi-fg-95); }
|
||||||
.ansi-fg-96 { color: #c7f0ff; }
|
.ansi-fg-96 { color: var(--ansi-fg-96); }
|
||||||
.ansi-fg-97 { color: #ffffff; }
|
.ansi-fg-97 { color: var(--ansi-fg-97); }
|
||||||
|
|
||||||
/* Background colors (optional) */
|
/* Background colors */
|
||||||
.ansi-bg-40 { background: #0b0f14; }
|
.ansi-bg-40 { background: var(--ansi-bg-40); }
|
||||||
.ansi-bg-41 { background: rgba(255, 123, 114, 0.22); }
|
.ansi-bg-41 { background: var(--ansi-bg-41); }
|
||||||
.ansi-bg-42 { background: rgba(126, 231, 135, 0.18); }
|
.ansi-bg-42 { background: var(--ansi-bg-42); }
|
||||||
.ansi-bg-43 { background: rgba(242, 204, 96, 0.18); }
|
.ansi-bg-43 { background: var(--ansi-bg-43); }
|
||||||
.ansi-bg-44 { background: rgba(121, 192, 255, 0.18); }
|
.ansi-bg-44 { background: var(--ansi-bg-44); }
|
||||||
.ansi-bg-45 { background: rgba(210, 168, 255, 0.18); }
|
.ansi-bg-45 { background: var(--ansi-bg-45); }
|
||||||
.ansi-bg-46 { background: rgba(165, 214, 255, 0.18); }
|
.ansi-bg-46 { background: var(--ansi-bg-46); }
|
||||||
.ansi-bg-47 { background: rgba(201, 209, 217, 0.10); }
|
.ansi-bg-47 { background: var(--ansi-bg-47); }
|
||||||
159
web/root/style/themes.scss
Normal file
159
web/root/style/themes.scss
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root:not([data-theme]) {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--space-0: 0;
|
||||||
|
--space-1: 4px;
|
||||||
|
--space-2: 8px;
|
||||||
|
--space-3: 12px;
|
||||||
|
--space-4: 16px;
|
||||||
|
--space-5: 24px;
|
||||||
|
--space-6: 32px;
|
||||||
|
|
||||||
|
--radius-sm: 6px;
|
||||||
|
--radius-md: 10px;
|
||||||
|
--radius-lg: 14px;
|
||||||
|
|
||||||
|
--font-ui:
|
||||||
|
ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
|
||||||
|
--font-mono:
|
||||||
|
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono";
|
||||||
|
|
||||||
|
--dur-fast: 80ms;
|
||||||
|
--dur-med: 160ms;
|
||||||
|
--dur-slow: 240ms;
|
||||||
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
--bg-0: #0d1117;
|
||||||
|
--bg-1: #161b22;
|
||||||
|
--bg-2: #21262d;
|
||||||
|
|
||||||
|
--fg-0: #e6edf3;
|
||||||
|
--fg-1: #c9d1d9;
|
||||||
|
--fg-muted: #8b949e;
|
||||||
|
|
||||||
|
--separator-bg: rgba(255, 255, 255, 0.06);
|
||||||
|
--separator-hover: rgba(121, 192, 255, 0.28);
|
||||||
|
--separator-active: rgba(121, 192, 255, 0.45);
|
||||||
|
|
||||||
|
--accent: #79c0ff;
|
||||||
|
--focus: #388bfd;
|
||||||
|
--success: #2ea043;
|
||||||
|
--warning: #f2cc60;
|
||||||
|
--error: #f85149;
|
||||||
|
|
||||||
|
--graph-bg: var(--bg-0);
|
||||||
|
|
||||||
|
--graph-node-bg: #1f6feb;
|
||||||
|
--graph-node-border: #388bfd;
|
||||||
|
--graph-node-text: #e6edf3;
|
||||||
|
|
||||||
|
--graph-node-active-bg: #79c0ff;
|
||||||
|
--graph-node-active-border: #a5d6ff;
|
||||||
|
|
||||||
|
--graph-edge: rgba(201, 209, 217, 0.55);
|
||||||
|
--graph-edge-hover: #79c0ff;
|
||||||
|
--graph-edge-active: #a5d6ff;
|
||||||
|
|
||||||
|
|
||||||
|
--ansi-fg-30: #0b0f14; /* black */
|
||||||
|
--ansi-fg-31: #ff7b72; /* red */
|
||||||
|
--ansi-fg-32: #7ee787; /* green */
|
||||||
|
--ansi-fg-33: #f2cc60; /* yellow */
|
||||||
|
--ansi-fg-34: #79c0ff; /* blue */
|
||||||
|
--ansi-fg-35: #d2a8ff; /* magenta */
|
||||||
|
--ansi-fg-36: #a5d6ff; /* cyan */
|
||||||
|
--ansi-fg-37: #c9d1d9; /* white */
|
||||||
|
|
||||||
|
--ansi-fg-90: #6e7681; /* bright black / gray */
|
||||||
|
--ansi-fg-91: #ffa198;
|
||||||
|
--ansi-fg-92: #a6f3a6;
|
||||||
|
--ansi-fg-93: #ffe082;
|
||||||
|
--ansi-fg-94: #a5d6ff;
|
||||||
|
--ansi-fg-95: #e3b8ff;
|
||||||
|
--ansi-fg-96: #c7f0ff;
|
||||||
|
--ansi-fg-97: #ffffff;
|
||||||
|
|
||||||
|
--ansi-bg-40: #0b0f14;
|
||||||
|
--ansi-bg-41: rgba(255, 123, 114, 0.22);
|
||||||
|
--ansi-bg-42: rgba(126, 231, 135, 0.18);
|
||||||
|
--ansi-bg-43: rgba(242, 204, 96, 0.18);
|
||||||
|
--ansi-bg-44: rgba(121, 192, 255, 0.18);
|
||||||
|
--ansi-bg-45: rgba(210, 168, 255, 0.18);
|
||||||
|
--ansi-bg-46: rgba(165, 214, 255, 0.18);
|
||||||
|
--ansi-bg-47: rgba(201, 209, 217, 0.10);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
|
||||||
|
--bg-0: #f6f8fa;
|
||||||
|
--bg-1: #ffffff;
|
||||||
|
--bg-2: #eaeef2;
|
||||||
|
|
||||||
|
--fg-0: #0b1220;
|
||||||
|
--fg-1: #1f2937;
|
||||||
|
--fg-muted: #5b6472;
|
||||||
|
|
||||||
|
--separator-bg: rgba(0, 0, 0, 0.06);
|
||||||
|
--separator-hover: rgba(9, 105, 218, 0.28);
|
||||||
|
--separator-active: rgba(9, 105, 218, 0.45);
|
||||||
|
|
||||||
|
--accent: #1f6feb;
|
||||||
|
--focus: #0969da;
|
||||||
|
--success: #1a7f37;
|
||||||
|
--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 */
|
||||||
|
--ansi-fg-33: #9a6700; /* yellow */
|
||||||
|
--ansi-fg-34: #0969da; /* blue */
|
||||||
|
--ansi-fg-35: #8250df; /* magenta */
|
||||||
|
--ansi-fg-36: #0550ae; /* cyan */
|
||||||
|
--ansi-fg-37: #1f2937; /* white (dark text) */
|
||||||
|
|
||||||
|
--ansi-fg-90: #6b7280; /* bright black / gray */
|
||||||
|
--ansi-fg-91: #cf222e;
|
||||||
|
--ansi-fg-92: #1f883d;
|
||||||
|
--ansi-fg-93: #9a6700;
|
||||||
|
--ansi-fg-94: #0969da;
|
||||||
|
--ansi-fg-95: #8250df;
|
||||||
|
--ansi-fg-96: #0550ae;
|
||||||
|
--ansi-fg-97: #030712;
|
||||||
|
|
||||||
|
--ansi-bg-40: #ffffff;
|
||||||
|
--ansi-bg-41: rgba(180, 35, 24, 0.16);
|
||||||
|
--ansi-bg-42: rgba(26, 127, 55, 0.14);
|
||||||
|
--ansi-bg-43: rgba(154, 103, 0, 0.14);
|
||||||
|
--ansi-bg-44: rgba(9, 105, 218, 0.14);
|
||||||
|
--ansi-bg-45: rgba(130, 80, 223, 0.14);
|
||||||
|
--ansi-bg-46: rgba(5, 80, 174, 0.14);
|
||||||
|
--ansi-bg-47: rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
@ -1,32 +1,37 @@
|
||||||
.tipTitle.error {
|
.tipTitle {
|
||||||
color: #d73a49;
|
font-weight: 700;
|
||||||
}
|
margin-bottom: 4px;
|
||||||
|
|
||||||
.tipTitle.warning {
|
&.error {
|
||||||
color: #ffd33d;
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipTitle.info {
|
&.warning {
|
||||||
color: #79c0ff;
|
color: var(--warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.cm-tooltip.cm-tooltip-hover {
|
.cm-tooltip.cm-tooltip-hover {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid color-mix(in srgb, var(--fg-muted) 35%, transparent);
|
||||||
background: black;
|
background: var(--bg-1);
|
||||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
|
color: var(--fg-0);
|
||||||
border-radius: 10px;
|
|
||||||
|
box-shadow:
|
||||||
|
0 8px 30px rgba(0, 0, 0, 0.25);
|
||||||
|
|
||||||
|
border-radius: var(--radius-md);
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
|
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipTitle {
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tipBody {
|
.tipBody {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
color: var(--fg-1);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue