Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/electron-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ jobs:
with:
fetch-depth: 0 # --generate-notes needs history to diff the tag against

# Before anything is built: the tag has to name the version the repository
# states, in package.json and in the citations. `npm version x.y.z` makes
# the commit and the tag together, so this only fails for a tag made by
# hand -- the way the tags once reached v3.1.3 while the repository, the
# website's About and the README's citation all still said 2.9.0.
- name: Check the tag matches the repository's version
run: node scripts/sync-version.mjs --check --expect "${GITHUB_REF_NAME#v}"

- env:
GH_TOKEN: ${{ github.token }}
run: |
Expand Down Expand Up @@ -79,13 +87,6 @@ jobs:

- run: npm ci

# Keeps installer filenames (…Setup 1.2.3.exe) in step with the tag being
# released, without needing a version-bump commit before every tag.
- name: Sync package version to tag
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: npm version --no-git-tag-version --allow-same-version "${GITHUB_REF_NAME#v}"

- name: Build web bundle
run: npm run build

Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ authors:
- family-names: Chaubey
given-names: Shreyan
alias: thethinkmachine
version: 2.9.0
version: 3.1.3
license: LicenseRef-PolyForm-Noncommercial-1.0.0
repository-code: "https://github.com/thethinkmachine/AutomataStudio"
url: "https://thethinkmachine.github.io/AutomataStudio/"
Expand Down
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ npm run cli -- --help # the `automata` command line, from source (bin: cli/
npm run cli:build # vite build --config vite.cli.config.js -> dist-cli/ (runs under plain node)
npm run cli:docs # docs/cli-reference.md and the guide's output blocks, from the real commands
npm run media:cli # re-film the guide's terminal clips from docs/media/tapes/ (-- <tape> for one)
npm version 3.2.0 # release: bump, sync the citations, commit and tag v3.2.0 in one step
git push --follow-tags # ... and push the commit with its tag, which builds the release
```

`automata` is the app's engine from a terminal — run, test, trace, lint, compare, convert and grade machines; HOA/BA/JFLAP formats; learning; an MCP server; and Turing-machine halting proofs. `electron:build` builds `dist-cli/` too, and the installed app ships it with launchers in `resources/cli/`. See [The command line](.claude/skills/cli/SKILL.md).
Expand All @@ -32,6 +34,8 @@ npm run media:cli # re-film the guide's terminal clips from docs/media/

`npm run bench` is also run by hand and never gates CI: timings move with the machine, so a build that failed on them would fail at random. Run it before and after a change to the engine, the player, the canvas or the space-time diagram; it marks a case only when it moved by more than its own noise, and marks a changed verdict whatever the timing did. See [The benchmark](.claude/skills/perf/SKILL.md).

**A release is `npm version`, never a hand-made tag.** package.json is the version's one source: the website's About and `automata --version` read it at build time, and CITATION.cff and the README's two citations repeat it as text. `npm version` runs [scripts/sync-version.mjs](scripts/sync-version.mjs) as its `version` hook, so the bump, those copies and the tag land in one commit; [tests/version.test.js](tests/version.test.js) fails when they disagree, and the release workflow refuses a tag that does not match package.json before it builds anything. Tags used to be the only place the version lived — the workflow rewrote package.json on the runner and never committed it — and they reached v3.1.3 while the website and the citation still said 2.9.0.

CI: `.github/workflows/deploy.yml` publishes `dist/` to GitHub Pages on push to `main`. `.github/workflows/electron-build.yml` packages win/mac/linux installers on every push and publishes a GitHub Release for `v*` tags. `.github/workflows/library-rebuild.yml` runs the tests and then starts the machine library's Publish workflow when `main` changes anything the library build reads (`js/`, `scripts/library/`) — the library is built by this repo's `main`, but its own workflows only run on its own events. It needs a `LIBRARY_DISPATCH_TOKEN` secret, and says so rather than failing when there is none.

The package is `"type": "module"`. The two Electron entry points are CommonJS and carry a `.cjs` extension for that reason ([electron/main.cjs](electron/main.cjs), [electron/preload.cjs](electron/preload.cjs)).
Expand Down Expand Up @@ -343,4 +347,3 @@ It also **records `document` listeners with their phase**, and `dispatchDocument

- `js/examples/*.json` are fetched at runtime by name, so they are copied verbatim into `dist/` by a small plugin in [vite.config.js](vite.config.js) rather than hashed as bundler assets.
- The build no longer runs `javascript-obfuscator`. It roughly doubled the bundle and cost ~26% time-to-interactive on a project whose source is public; the desktop build already skipped it. Re-add it as a Vite plugin if that trade is wanted back.
- `exportOpenSamples` in `export-ui.js` has no callers.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# AutomataStudio

**An IDE for designing, simulating and analysing automata.**
**An IDE for designing, simulating and analysing automata and formal languages.**

Draw a machine, run it step by step, derive its language, put it through the textbook
constructions, and take it out as a diagram, LaTeX or working code. It covers 30
Expand Down Expand Up @@ -335,14 +335,14 @@ please cite it. GitHub's **"Cite this repository"** button (from
author = {Chaubey, Shreyan},
title = {{AutomataStudio}: An {IDE} for Designing, Simulating and Analysing Automata},
year = {2026},
version = {2.9.0},
version = {3.1.3},
url = {https://github.com/thethinkmachine/AutomataStudio},
note = {Software}
}
```

> Chaubey, S. (2026). *AutomataStudio: An IDE for designing, simulating and analysing
> automata* (Version 2.9.0) [Computer software].
> automata* (Version 3.1.3) [Computer software].
> https://github.com/thethinkmachine/AutomataStudio

Cite the version you actually used, so that others can reproduce your results. If you
Expand Down
94 changes: 89 additions & 5 deletions css/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,29 @@ body:has(.rpanel.is-empty) .hdr-right {
centre. Centring both puts the mark's middle within 0.5px of the wordmark's
cap-height middle, which is the alignment the eye actually reads. */
.logo-wrapper {
/* A button only so it is focusable and opens About; it must not look like
one. `font` comes first because the shorthand resets line-height, and
the stack below is measured at line-height 1. The padding gives the
focus ring room around the revealed byline, and the negative margin hands
the width back so the lockup sits exactly where the bare div used to. */
font: inherit;
display: inline-flex;
align-items: center;
gap: 6px;
line-height: 1;
cursor: default;
user-select: none;
padding: 6px 8px;
margin: 0 -8px;
border: 0;
border-radius: 8px;
background: none;
color: inherit;
cursor: pointer;
}

.logo-wrapper:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 0;
}

/* The <use> in index.html pulls in #logo-mark, whose strokes are `currentColor`
Expand All @@ -229,7 +246,7 @@ body:has(.rpanel.is-empty) .hdr-right {
/* margin: 0 because the About dialog's copy of this lockup is the <h2> that
names the dialog — it has no .modal-title to carry that job — and a UA
heading margin there would push the byline off the wordmark. The header's
copy is a div and never had one to reset. */
copy is a span and never had one to reset. */
.logo {
font-family: var(--sans);
font-size: 1.15rem;
Expand All @@ -247,14 +264,81 @@ body:has(.rpanel.is-empty) .hdr-right {
}

.logo-author {
font-family: var(--mono);
font-size: 0.6rem;
font-family: var(--sans);
font-size: 0.68rem;
font-weight: 500;
color: var(--text3);
white-space: nowrap;
letter-spacing: 0.04em;
letter-spacing: 0.01em;
padding-left: 2px;
}

/* ─── The byline reveal ───
Hover or focus turns the lockup into the About dialog's header: wordmark
over byline. Only transforms and opacity move, so nothing reflows — the
byline hangs below the wordmark out of flow, adding no width or height, and
the whole stack lifts 6px so the pair centres on the mark the way the
single wordmark did. 6px is half of the byline's 2px gap plus its 0.68rem
line, rounded.

The enter waits 60ms and the leave does not: a pointer sweeping across the
header (or towards the window controls in the desktop app) should not
flicker the name on its way past. Hover is gated on `(hover: hover)`
because a touch browser keeps :hover stuck on after a tap, which would
leave the byline showing behind the About dialog it just opened. */
.logo-stack {
position: relative;
display: flex;
flex-direction: column;
transition: transform .18s cubic-bezier(.2, .7, .2, 1);
}

.logo-byline {
position: absolute;
left: 0;
top: calc(100% + 2px);
padding-left: 0;
opacity: 0;
transform: translateY(3px);
transition: opacity .14s ease-out, transform .18s cubic-bezier(.2, .7, .2, 1);
pointer-events: none;
}

.logo-wrapper:focus-visible .logo-stack {
transform: translateY(-6px);
}

.logo-wrapper:focus-visible .logo-byline {
opacity: 1;
transform: none;
}

@media (hover: hover) {
.logo-wrapper:hover .logo-stack {
transform: translateY(-6px);
transition-delay: 60ms;
}

.logo-wrapper:hover .logo-byline {
opacity: 1;
transform: none;
transition-delay: 60ms;
}
}

/* Reduced motion keeps the fade and drops the movement: the stack still has
to make room, so it jumps there instead of travelling. */
@media (prefers-reduced-motion: reduce) {
.logo-stack {
transition: none;
}

.logo-byline {
transform: none;
transition: opacity .14s ease-out;
}
}

/* ─── HEADER MENU (More, and the Explore group inside it) ───
Anchored to its trigger rather than the viewport, so it overrides the
position:fixed that .ctx uses for cursor-placed context menus. The .ctx-
Expand Down
6 changes: 4 additions & 2 deletions css/mobile.css
Original file line number Diff line number Diff line change
Expand Up @@ -1475,9 +1475,11 @@

/* Below this the wordmark is 100px of a 390px header spent saying something
the mark already says. The mark stays — it is the logo, and an app with no
mark anywhere reads as a page that failed to finish loading. */
mark anywhere reads as a page that failed to finish loading. The byline
goes with it, since it hangs off the wordmark; the mark alone still opens
About, where the credit is. */
@media (max-width: 520px) {
.hdr-left .logo {
.hdr-left .logo-stack {
display: none;
}

Expand Down
4 changes: 3 additions & 1 deletion electron/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,9 @@ function buildMenu() {
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
// The app's own About, not Electron's stock panel: the Help menu opens
// this one too, and the stock panel has no author, licence or blurb.
{ label: 'About AutomataStudio', click: () => sendMenuAction('about') },
{ type: 'separator' },
{ label: "Install 'automata' Command in PATH", click: () => installCliCommand() },
{ label: "Uninstall 'automata' Command from PATH", click: () => uninstallCliCommand() },
Expand Down
19 changes: 14 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
gtag('config', 'G-R1LKJ6JFEF');
</script>
<title>AutomataStudio</title>
<meta name="description" content="An IDE for designing, simulating and analysing automata and formal languages.">
<link rel="icon" type="image/svg+xml" href="svgs/favicon.svg">
<meta name="theme-color" content="#eef0f4">
<link
Expand Down Expand Up @@ -82,10 +83,19 @@
are auxiliary and open as overlays via the Tools menu. -->
<header>
<div class="hdr-left">
<div class="logo-wrapper">
<!-- The lockup is the way to About. Hovering or focusing it shows the
About dialog's own header in miniature — wordmark over byline — so
what the reveal previews is what the click opens. The byline is
absolutely placed under the wordmark: it adds no width, so the tab
strip beside the logo never moves. Its text is pinned to the About
dialog's by tests/markup.test.js. -->
<button type="button" class="logo-wrapper" onclick="openAboutModal()" aria-label="About AutomataStudio, by Shreyan Chaubey">
<svg class="logo-mark" aria-hidden="true"><use href="#logo-mark" /></svg>
<div class="logo">Automata<em>Studio</em></div>
</div>
<span class="logo-stack" aria-hidden="true">
<span class="logo">Automata<em>Studio</em></span>
<span class="logo-author logo-byline">by Shreyan Chaubey</span>
</span>
</button>
</div>

<div class="tab-bar-wrap" id="tab-bar-wrap">
Expand Down Expand Up @@ -2427,8 +2437,7 @@ <h2 class="logo" id="about-modal-title">Automata<em>Studio</em></h2>
<div class="logo-author">Shreyan Chaubey</div>
</div>
</div>
<p class="modal-hint about-blurb">A visual editor and simulator for finite automata, pushdown
automata, Turing machines and their grammars.</p>
<p class="modal-hint about-blurb">An IDE for designing, simulating and analysing automata and formal languages.</p>
<div class="modal-row">
<span class="modal-lbl">Version</span>
<span class="about-val" id="about-version">&mdash;</span>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "automatastudio",
"version": "2.9.0",
"description": "A web-based visualizer and simulator for finite state machines, pushdown automata, and Turing machines.",
"version": "3.1.3",
"description": "An IDE for designing, simulating and analysing automata and formal languages.",
"author": {
"name": "Shreyan C",
"name": "Shreyan Chaubey",
"email": "shreyan.chaubey@gmail.com"
},
"main": "electron/main.cjs",
Expand Down Expand Up @@ -31,6 +31,7 @@
"dev": "vite",
"build": "vite build",
"test": "node --conditions=browser --conditions=development --test",
"version": "node scripts/sync-version.mjs && git add CITATION.cff README.md",
"cli": "node cli/automata.mjs",
"cli:build": "vite build --config vite.cli.config.js",
"cli:docs": "node --conditions=browser --conditions=development cli/gen-docs.mjs",
Expand All @@ -50,6 +51,7 @@
"build": {
"appId": "com.automataplayground.app",
"productName": "AutomataStudio",
"copyright": "Copyright (c) 2026 Shreyan Chaubey",
"publish": [
{
"provider": "github",
Expand Down
Loading
Loading