mirror of
https://github.com/ParkerTenBroeck/automata.git
synced 2026-06-07 05:28:45 -04:00
Merge branch 'main' into gh-pages
This commit is contained in:
commit
b3e2af0be2
11 changed files with 473 additions and 125 deletions
|
|
@ -48,12 +48,45 @@
|
||||||
<div class="hSplit styleOnly" title="Drag to resize canvas height"></div>
|
<div class="hSplit styleOnly" title="Drag to resize canvas height"></div>
|
||||||
|
|
||||||
<div class="flexCenter sidePadding" style="font-size: calc(16px);font-weight: bold;color: var(--fg-1)">
|
<div class="flexCenter sidePadding" style="font-size: calc(16px);font-weight: bold;color: var(--fg-1)">
|
||||||
<span style="margin-right: 0.5em;">Status: </span><span style="color: var(--fg-2)" id="simulationStatus">N/A</span>
|
<span style="margin-right: 0.5em;">Status: </span><span style="color: var(--fg-2)"
|
||||||
|
id="simulationStatus">N/A</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexCenter sidePadding">
|
<div class="flexCenter sidePadding">
|
||||||
<input id="machineInput" type="text" class="test-input" placeholder="Enter machine input…" />
|
<input id="machineInput" type="text" class="test-input" placeholder="Enter machine input…" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="hSplit styleOnly" title="Drag to resize canvas height"></div>
|
||||||
|
|
||||||
|
<div class="sidePadding scroll">
|
||||||
|
<div class="pathsGrid">
|
||||||
|
|
||||||
|
<details class="group group-accepted">
|
||||||
|
<summary class="groupTitle" open>
|
||||||
|
<span>Accepted</span>
|
||||||
|
<span class="count" id="acceptedCount">0</span>
|
||||||
|
</summary>
|
||||||
|
<div class="groupBody" id="acceptedPaths"></div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="group group-running" open>
|
||||||
|
<summary class="groupTitle">
|
||||||
|
<span>Running</span>
|
||||||
|
<span class="count" id="runningCount">0</span>
|
||||||
|
</summary>
|
||||||
|
<div class="groupBody" id="runningPaths"></div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="group group-rejected" open>
|
||||||
|
<summary class="groupTitle">
|
||||||
|
<span>Rejected</span>
|
||||||
|
<span class="count" id="rejectedCount">0</span>
|
||||||
|
</summary>
|
||||||
|
<div class="groupBody" id="rejectedPaths"></div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
2
web/root/src/constants.ts
Normal file
2
web/root/src/constants.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const EPSILON: string = "ε"
|
||||||
|
export const DELTA: string = "δ"
|
||||||
|
|
@ -8,5 +8,6 @@ import "./visualizer.ts"
|
||||||
import "./editor.ts"
|
import "./editor.ts"
|
||||||
import "./simulation.ts"
|
import "./simulation.ts"
|
||||||
import "./terminal.ts"
|
import "./terminal.ts"
|
||||||
|
import "./paths.ts"
|
||||||
|
|
||||||
bus.emit("begin", undefined);
|
bus.emit("begin", undefined);
|
||||||
147
web/root/src/paths.ts
Normal file
147
web/root/src/paths.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
import { bus } from "./bus.ts";
|
||||||
|
import { DELTA } from "./constants.ts";
|
||||||
|
import type { Sim } from "./simulation.ts";
|
||||||
|
import type { FaState } from "./simulation/fa.ts";
|
||||||
|
import type { PdaState } from "./simulation/pda.ts";
|
||||||
|
import type { TmState } from "./simulation/tm.ts";
|
||||||
|
|
||||||
|
type AnyState = {
|
||||||
|
repr: string;
|
||||||
|
path: readonly unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderFaPath(state: FaState, index: number) {
|
||||||
|
const details = document.createElement("details");
|
||||||
|
details.className = "pathItem";
|
||||||
|
|
||||||
|
const summary = document.createElement("summary");
|
||||||
|
summary.className = "pathHeader";
|
||||||
|
summary.innerHTML = `
|
||||||
|
<span>${state.repr}</span>
|
||||||
|
<span class="pathMeta">
|
||||||
|
<span>steps: ${state.path.length}</span>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const steps = document.createElement("div");
|
||||||
|
steps.className = "steps";
|
||||||
|
|
||||||
|
for (let i = 0; i < state.path.length; i++) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "stepLine";
|
||||||
|
const step = state.path[i];
|
||||||
|
div.textContent = `${i + 1}. ${DELTA}(${step.from_state}, ${step.from_letter}) = ${step.state}`;
|
||||||
|
steps.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
details.appendChild(summary);
|
||||||
|
details.appendChild(steps);
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPdaPath(state: PdaState, index: number) {
|
||||||
|
const details = document.createElement("details");
|
||||||
|
details.className = "pathItem";
|
||||||
|
|
||||||
|
const summary = document.createElement("summary");
|
||||||
|
summary.className = "pathHeader";
|
||||||
|
summary.innerHTML = `
|
||||||
|
<span>${state.repr}</span>
|
||||||
|
<span class="pathMeta">
|
||||||
|
<span>steps: ${state.path.length}</span>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const steps = document.createElement("div");
|
||||||
|
steps.className = "steps";
|
||||||
|
|
||||||
|
for (let i = 0; i < state.path.length; i++) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "stepLine";
|
||||||
|
const step = state.path[i];
|
||||||
|
div.textContent = `${i + 1}. ${DELTA}(${step.from_state}, ${step.from_letter}, , ${step.from_stack}) = (${step.state}, [ ${step.stack.join(" ")} ])`;
|
||||||
|
steps.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
details.appendChild(summary);
|
||||||
|
details.appendChild(steps);
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTmPath(state: TmState, index: number) {
|
||||||
|
const details = document.createElement("details");
|
||||||
|
details.className = "pathItem";
|
||||||
|
|
||||||
|
const summary = document.createElement("summary");
|
||||||
|
summary.className = "pathHeader";
|
||||||
|
summary.innerHTML = `
|
||||||
|
<span>${state.repr}</span>
|
||||||
|
<span class="pathMeta">
|
||||||
|
<span>steps: ${state.path.length}</span>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const steps = document.createElement("div");
|
||||||
|
steps.className = "steps";
|
||||||
|
|
||||||
|
for (let i = 0; i < state.path.length; i++) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "stepLine";
|
||||||
|
const step = state.path[i];
|
||||||
|
div.textContent = `${i + 1}. ${DELTA}(${step.from_state}, ${step.from_symbol}) = (${step.state}, ${step.symbol}, ${step.direction})`;
|
||||||
|
steps.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
details.appendChild(summary);
|
||||||
|
details.appendChild(steps);
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.on("automata/sim/after_step", ({simulation}) => {
|
||||||
|
renderPaths(simulation)
|
||||||
|
})
|
||||||
|
|
||||||
|
bus.on("automata/sim/update", simulation => {
|
||||||
|
if(simulation){
|
||||||
|
renderPaths(simulation)
|
||||||
|
}else{
|
||||||
|
renderPaths(undefined)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function renderPaths(sim: Sim | undefined) {
|
||||||
|
const acceptedEl = document.getElementById("acceptedPaths")!;
|
||||||
|
const runningEl = document.getElementById("runningPaths")!;
|
||||||
|
const rejectedEl = document.getElementById("rejectedPaths")!;
|
||||||
|
|
||||||
|
const acceptedCount = document.getElementById("acceptedCount")!;
|
||||||
|
const runningCount = document.getElementById("runningCount")!;
|
||||||
|
const rejectedCount = document.getElementById("rejectedCount")!;
|
||||||
|
|
||||||
|
acceptedEl.innerHTML = "";
|
||||||
|
runningEl.innerHTML = "";
|
||||||
|
rejectedEl.innerHTML = "";
|
||||||
|
|
||||||
|
acceptedCount.textContent = String(sim?.accepted.length ?? 0);
|
||||||
|
runningCount.textContent = String(sim?.paths.length ?? 0);
|
||||||
|
rejectedCount.textContent = String(sim?.rejected.length ?? 0);
|
||||||
|
|
||||||
|
if(!sim)return;
|
||||||
|
switch (sim.machine.type){
|
||||||
|
case "fa":
|
||||||
|
sim.accepted.forEach((s, i) => acceptedEl.appendChild(renderFaPath(s as FaState, i)));
|
||||||
|
sim.paths.forEach((s, i) => runningEl.appendChild(renderFaPath(s as FaState, i)));
|
||||||
|
sim.rejected.forEach((s, i) => rejectedEl.appendChild(renderFaPath(s as FaState, i)));
|
||||||
|
break;
|
||||||
|
case "pda":
|
||||||
|
sim.accepted.forEach((s, i) => acceptedEl.appendChild(renderPdaPath(s as PdaState, i)));
|
||||||
|
sim.paths.forEach((s, i) => runningEl.appendChild(renderPdaPath(s as PdaState, i)));
|
||||||
|
sim.rejected.forEach((s, i) => rejectedEl.appendChild(renderPdaPath(s as PdaState, i)));
|
||||||
|
break;
|
||||||
|
case "tm":
|
||||||
|
sim.accepted.forEach((s, i) => acceptedEl.appendChild(renderTmPath(s as TmState, i)));
|
||||||
|
sim.paths.forEach((s, i) => runningEl.appendChild(renderTmPath(s as TmState, i)));
|
||||||
|
sim.rejected.forEach((s, i) => rejectedEl.appendChild(renderTmPath(s as TmState, i)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,12 +34,13 @@ export let automaton: Machine = {
|
||||||
bus.on("compiled", ({ machine }) => {
|
bus.on("compiled", ({ machine }) => {
|
||||||
if (machine) {
|
if (machine) {
|
||||||
try {
|
try {
|
||||||
bus.emit("controls/sim/clear", undefined);
|
|
||||||
automaton = parse_machine_from_json(machine);
|
automaton = parse_machine_from_json(machine);
|
||||||
bus.emit("automata/update", automaton);
|
bus.emit("automata/update", automaton);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
bus.emit("controls/sim/clear", undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bus.on("controls/sim/clear", (_) => {
|
bus.on("controls/sim/clear", (_) => {
|
||||||
|
|
@ -83,20 +84,21 @@ bus.on("automata/sim/update", simulation => {
|
||||||
simulationStatus.innerText = "N/A"
|
simulationStatus.innerText = "N/A"
|
||||||
simulationStatus.style.color = "var(--fg-2)";
|
simulationStatus.style.color = "var(--fg-2)";
|
||||||
}else{
|
}else{
|
||||||
simulationStatus.innerText = "Pending"
|
update_status(simulation.status())
|
||||||
simulationStatus.style.color = "var(--warning)";
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bus.on("automata/sim/after_step", ({result}) => {
|
bus.on("automata/sim/after_step", ({result}) => {
|
||||||
if (result === "pending"){
|
update_status(result)
|
||||||
|
});
|
||||||
|
function update_status(status: SimStepResult){
|
||||||
|
if (status === "pending"){
|
||||||
simulationStatus.innerText = "Pending"
|
simulationStatus.innerText = "Pending"
|
||||||
simulationStatus.style.color = "var(--warning)";
|
simulationStatus.style.color = "var(--warning)";
|
||||||
}else if (result==="accept"){
|
}else if (status==="accept"){
|
||||||
simulationStatus.innerText = "Accepted"
|
simulationStatus.innerText = "Accepted"
|
||||||
simulationStatus.style.color = "var(--success)";
|
simulationStatus.style.color = "var(--success)";
|
||||||
}else if (result==="reject"){
|
}else if (status==="reject"){
|
||||||
simulationStatus.innerText = "Rejected"
|
simulationStatus.innerText = "Rejected"
|
||||||
simulationStatus.style.color = "var(--error)";
|
simulationStatus.style.color = "var(--error)";
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@ import type {
|
||||||
FaTransTo,
|
FaTransTo,
|
||||||
State,
|
State,
|
||||||
} from "../automata.ts";
|
} from "../automata.ts";
|
||||||
|
import { EPSILON } from "../constants.ts";
|
||||||
import { SimStepResult } from "../simulation.ts";
|
import { SimStepResult } from "../simulation.ts";
|
||||||
|
|
||||||
|
|
||||||
|
export type Step = FaTransTo & {from_state: State, from_letter: string}
|
||||||
export type FaState = {
|
export type FaState = {
|
||||||
readonly state: State;
|
readonly state: State;
|
||||||
readonly position: number;
|
readonly position: number;
|
||||||
|
|
@ -12,7 +15,7 @@ export type FaState = {
|
||||||
readonly accepted: boolean;
|
readonly accepted: boolean;
|
||||||
readonly repr: string;
|
readonly repr: string;
|
||||||
|
|
||||||
readonly path: readonly FaTransTo[];
|
readonly path: readonly Step[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type Initializer<T> = { -readonly [P in keyof T]?: T[P] | undefined };
|
type Initializer<T> = { -readonly [P in keyof T]?: T[P] | undefined };
|
||||||
|
|
@ -65,19 +68,19 @@ export class FaSim {
|
||||||
this.init_state(state);
|
this.init_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private transition(from: FaState, to: FaTransTo, consume: boolean) {
|
private transition(from: FaState, to: FaTransTo, letter: string|undefined) {
|
||||||
const state: Initializer<FaState> = {
|
const state: Initializer<FaState> = {
|
||||||
state: to.state,
|
state: to.state,
|
||||||
position: from.position + (consume ? 1 : 0),
|
position: from.position + (letter ? 1 : 0),
|
||||||
path: from.path.concat([to]),
|
path: from.path.concat([{from_state: from.state, from_letter: letter??EPSILON, ...to}]),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init_state(state);
|
this.init_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
step(): SimStepResult {
|
step(): SimStepResult {
|
||||||
if (this.accepted.length !== 0) return "accept";
|
|
||||||
if (this.paths.length === 0) return "reject";
|
|
||||||
|
|
||||||
const paths = this.paths;
|
const paths = this.paths;
|
||||||
this.paths = [];
|
this.paths = [];
|
||||||
|
|
@ -93,7 +96,7 @@ export class FaSim {
|
||||||
|
|
||||||
// epsilon transitions
|
// epsilon transitions
|
||||||
const eps = letterMap.get(null) ?? [];
|
const eps = letterMap.get(null) ?? [];
|
||||||
for (const to of eps) this.transition(from, to, false);
|
for (const to of eps) this.transition(from, to, undefined);
|
||||||
|
|
||||||
// consuming transitions
|
// consuming transitions
|
||||||
if (from.position >= this.input.length) {
|
if (from.position >= this.input.length) {
|
||||||
|
|
@ -103,13 +106,17 @@ export class FaSim {
|
||||||
|
|
||||||
const ch = this.input.charAt(from.position);
|
const ch = this.input.charAt(from.position);
|
||||||
const trs = letterMap.get(ch) ?? [];
|
const trs = letterMap.get(ch) ?? [];
|
||||||
for (const to of trs) this.transition(from, to, true);
|
for (const to of trs) this.transition(from, to, ch);
|
||||||
|
|
||||||
if (eps.length === 0 && trs.length === 0) {
|
if (eps.length === 0 && trs.length === 0) {
|
||||||
this.rejected.push(from);
|
this.rejected.push(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
status(): SimStepResult {
|
||||||
if (this.accepted.length !== 0) return "accept";
|
if (this.accepted.length !== 0) return "accept";
|
||||||
if (this.paths.length === 0) return "reject";
|
if (this.paths.length === 0) return "reject";
|
||||||
return "pending";
|
return "pending";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ import type {
|
||||||
Symbol
|
Symbol
|
||||||
} from "../automata.ts";
|
} from "../automata.ts";
|
||||||
import { SimStepResult } from "../simulation.ts";
|
import { SimStepResult } from "../simulation.ts";
|
||||||
|
import { EPSILON } from "../constants.ts";
|
||||||
|
|
||||||
|
|
||||||
|
export type Step = PdaTransTo & {from_state: State, from_letter: string, from_stack: Symbol}
|
||||||
export type PdaState = {
|
export type PdaState = {
|
||||||
readonly state: State;
|
readonly state: State;
|
||||||
readonly stack: Symbol[];
|
readonly stack: Symbol[];
|
||||||
|
|
@ -14,7 +17,7 @@ export type PdaState = {
|
||||||
readonly accepted: boolean;
|
readonly accepted: boolean;
|
||||||
readonly repr: string;
|
readonly repr: string;
|
||||||
|
|
||||||
readonly path: readonly PdaTransTo[];
|
readonly path: readonly Step[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type Initializer<T> = { -readonly [P in keyof T]?: T[P] | undefined };
|
type Initializer<T> = { -readonly [P in keyof T]?: T[P] | undefined };
|
||||||
|
|
@ -80,7 +83,7 @@ export class PdaSim {
|
||||||
this.init_state(state);
|
this.init_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private transition(from: PdaState, to: PdaTransTo, consume: boolean) {
|
private transition(from: PdaState, to: PdaTransTo, letter: string|undefined) {
|
||||||
const stackCopy = from.stack.slice(0, from.stack.length - 1); // pop off top
|
const stackCopy = from.stack.slice(0, from.stack.length - 1); // pop off top
|
||||||
const nextStack = stackCopy.concat(to.stack);
|
const nextStack = stackCopy.concat(to.stack);
|
||||||
if (nextStack.length == 0) {
|
if (nextStack.length == 0) {
|
||||||
|
|
@ -91,16 +94,14 @@ export class PdaSim {
|
||||||
const state: Initializer<PdaState> = {
|
const state: Initializer<PdaState> = {
|
||||||
state: to.state,
|
state: to.state,
|
||||||
stack: nextStack,
|
stack: nextStack,
|
||||||
position: from.position + (consume ? 1 : 0),
|
position: from.position + (letter ? 1 : 0),
|
||||||
path: from.path.concat([to]),
|
path: from.path.concat([{from_state: from.state, from_letter: letter??EPSILON, from_stack: from.stack[from.stack.length-1], ...to}]),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init_state(state);
|
this.init_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
step(): SimStepResult {
|
step(): SimStepResult {
|
||||||
if (this.accepted.length !== 0) return "accept";
|
|
||||||
if (this.paths.length === 0) return "reject";
|
|
||||||
|
|
||||||
const paths = this.paths;
|
const paths = this.paths;
|
||||||
this.paths = [];
|
this.paths = [];
|
||||||
|
|
@ -118,7 +119,7 @@ export class PdaSim {
|
||||||
// epsilon transitions
|
// epsilon transitions
|
||||||
const epsilon_transitions = letterMap.get(null) ?? [];
|
const epsilon_transitions = letterMap.get(null) ?? [];
|
||||||
for (const to of epsilon_transitions) {
|
for (const to of epsilon_transitions) {
|
||||||
this.transition(from, to, false);
|
this.transition(from, to, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (from.position >= this.input.length) {
|
if (from.position >= this.input.length) {
|
||||||
|
|
@ -132,13 +133,17 @@ export class PdaSim {
|
||||||
|
|
||||||
const transitions = letterMap.get(ch) ?? [];
|
const transitions = letterMap.get(ch) ?? [];
|
||||||
for (const to of transitions) {
|
for (const to of transitions) {
|
||||||
this.transition(from, to, true);
|
this.transition(from, to, ch);
|
||||||
}
|
}
|
||||||
if (epsilon_transitions.length == 0 && transitions.length == 0){
|
if (epsilon_transitions.length == 0 && transitions.length == 0){
|
||||||
this.rejected.push(from);
|
this.rejected.push(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
status(): SimStepResult {
|
||||||
if (this.accepted.length !== 0) return "accept";
|
if (this.accepted.length !== 0) return "accept";
|
||||||
if (this.paths.length === 0) return "reject";
|
if (this.paths.length === 0) return "reject";
|
||||||
return "pending";
|
return "pending";
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import type {
|
||||||
import { SimStepResult } from "../simulation.ts";
|
import { SimStepResult } from "../simulation.ts";
|
||||||
|
|
||||||
|
|
||||||
|
export type Step = TmTransTo & {from_state: State, from_symbol: Symbol}
|
||||||
export type TmState = {
|
export type TmState = {
|
||||||
readonly state: State;
|
readonly state: State;
|
||||||
readonly tape: Symbol[];
|
readonly tape: Symbol[];
|
||||||
|
|
@ -15,7 +16,7 @@ export type TmState = {
|
||||||
readonly accepted: boolean;
|
readonly accepted: boolean;
|
||||||
readonly repr: string;
|
readonly repr: string;
|
||||||
|
|
||||||
readonly path: readonly TmTransTo[];
|
readonly path: readonly Step[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ export class TmSim {
|
||||||
state: to.state,
|
state: to.state,
|
||||||
accepted: this.machine.final_states.has(to.state),
|
accepted: this.machine.final_states.has(to.state),
|
||||||
|
|
||||||
path: from.path.concat([to]),
|
path: from.path.concat([{from_state: from.state, from_symbol: from.tape[from.head], ...to}]),
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (to.direction) {
|
switch (to.direction) {
|
||||||
|
|
@ -102,8 +103,6 @@ export class TmSim {
|
||||||
}
|
}
|
||||||
|
|
||||||
step(): SimStepResult {
|
step(): SimStepResult {
|
||||||
if (this.accepted.length != 0) return "accept";
|
|
||||||
if (this.paths.length == 0) return "reject";
|
|
||||||
|
|
||||||
const paths: TmState[] = this.paths;
|
const paths: TmState[] = this.paths;
|
||||||
this.paths = [];
|
this.paths = [];
|
||||||
|
|
@ -122,8 +121,12 @@ export class TmSim {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.accepted.length != 0) return "accept";
|
return this.status();
|
||||||
if (this.paths.length == 0) return "reject";
|
}
|
||||||
|
|
||||||
|
status(): SimStepResult {
|
||||||
|
if (this.accepted.length !== 0) return "accept";
|
||||||
|
if (this.paths.length === 0) return "reject";
|
||||||
return "pending";
|
return "pending";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -307,9 +307,7 @@ function createGraph(): vis.Network {
|
||||||
});
|
});
|
||||||
|
|
||||||
network.on('deselectEdge', item => {
|
network.on('deselectEdge', item => {
|
||||||
console.log(item);
|
|
||||||
for (const edge of item.previousSelection.edges){
|
for (const edge of item.previousSelection.edges){
|
||||||
console.log(edge);
|
|
||||||
dehighlight_from_edge_id(edge.id)
|
dehighlight_from_edge_id(edge.id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -320,9 +318,7 @@ function createGraph(): vis.Network {
|
||||||
});
|
});
|
||||||
|
|
||||||
network.on('deselectNode', item => {
|
network.on('deselectNode', item => {
|
||||||
console.log(item);
|
|
||||||
for (const node of item.previousSelection.nodes){
|
for (const node of item.previousSelection.nodes){
|
||||||
console.log(node);
|
|
||||||
dehighlight_from_node_id(node.id)
|
dehighlight_from_node_id(node.id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
147
web/root/style/paths.scss
Normal file
147
web/root/style/paths.scss
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
/* ===== Panel ===== */
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
border: 1px solid var(--separator-bg);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background: var(--bg-1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelTitle {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--fg-0);
|
||||||
|
|
||||||
|
background: var(--bg-2);
|
||||||
|
border-bottom: 1px solid var(--separator-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelTitle::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Layout ===== */
|
||||||
|
|
||||||
|
.pathsGrid {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Groups ===== */
|
||||||
|
|
||||||
|
.group {
|
||||||
|
border: 1px solid var(--separator-bg);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--bg-1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.groupTitle {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-2);
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--fg-0);
|
||||||
|
|
||||||
|
background: var(--bg-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.groupTitle::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
color: var(--fg-muted);
|
||||||
|
background: var(--bg-1);
|
||||||
|
border: 1px solid var(--separator-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.groupBody {
|
||||||
|
padding: var(--space-3);
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Path cards ===== */
|
||||||
|
|
||||||
|
.pathItem {
|
||||||
|
border: 1px solid var(--separator-bg);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--bg-1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pathHeader {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
|
||||||
|
color: var(--fg-0);
|
||||||
|
background: var(--bg-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pathHeader::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pathMeta {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Steps ===== */
|
||||||
|
|
||||||
|
.steps {
|
||||||
|
padding: var(--space-2) var(--space-3) var(--space-3);
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepLine {
|
||||||
|
font: 500 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
|
||||||
|
color: var(--fg-1);
|
||||||
|
background: var(--bg-2);
|
||||||
|
|
||||||
|
padding: var(--space-1) var(--space-2);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
|
||||||
|
// overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Semantic accents ===== */
|
||||||
|
|
||||||
|
.group-accepted {
|
||||||
|
border-color: color-mix(in srgb, var(--success) 35%, var(--separator-bg));
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-running {
|
||||||
|
border-color: color-mix(in srgb, var(--accent) 35%, var(--separator-bg));
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-rejected {
|
||||||
|
border-color: color-mix(in srgb, var(--error) 35%, var(--separator-bg));
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
@use "loading.scss";
|
@use "loading.scss";
|
||||||
@use "controls.scss";
|
@use "controls.scss";
|
||||||
@use "themes.scss";
|
@use "themes.scss";
|
||||||
|
@use "paths.scss";
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
|
@ -34,6 +35,10 @@ body {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll{
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.flexCol{
|
.flexCol{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue