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
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,41 @@ jobs:
- run: npm run typecheck
- run: npx vite build

website:
# The same build the Pages workflow deploys, run here so a pull request
# cannot rot it: the wasm target, the packed examples, the typecheck and
# the bundle.
name: Website
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: wasm-pack
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: apps/web/package-lock.json

- name: The runtime holds to its promises on the wasm target too
run: cargo clippy -p hyperlab-web --target wasm32-unknown-unknown --all-targets

- name: Pack the example stacks as single files
run: cargo run -p hyperlab-persistence --example pack_single_files -- apps/web/public/examples

- name: Build the site
working-directory: apps/web
run: |
npm ci
npm run wasm
npm run format:check
npm run build

desktop:
name: Desktop shell (${{ matrix.os }})
runs-on: ${{ matrix.os }}
Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Website

# Builds the site in apps/web — the retro landing page, and the whole
# application compiled to WebAssembly — and deploys it to GitHub Pages.
#
# One-time setup: the repository's Settings ▸ Pages ▸ Source must say
# "GitHub Actions", or the deploy step has nowhere to put the artifact.

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# One deployment at a time, and a new push makes the queued one irrelevant —
# but a deployment already running is left to finish.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
name: Build the site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
# A prebuilt binary, for the same reason cargo-audit is one in ci.yml.
- uses: taiki-e/install-action@v2
with:
tool: wasm-pack
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: apps/web/package-lock.json

- name: Pack the example stacks as single files
run: cargo run -p hyperlab-persistence --example pack_single_files -- apps/web/public/examples

- name: Build the WebAssembly runtime
working-directory: apps/web
run: |
npm ci
npm run wasm

- name: Build the site
working-directory: apps/web
run: npm run build

- uses: actions/upload-pages-artifact@v3
with:
path: apps/web/dist

deploy:
name: Deploy to GitHub Pages
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ dist/

# Tauri's build output, which lives inside the shell crate
apps/desktop/src-tauri/target/

# The website's generated assets: the WebAssembly package wasm-pack writes,
# and the example stacks packed as single files. Both are built in CI.
apps/web/wasm-pkg/
apps/web/public/examples/
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ does, the entry says so and says what to do about it.
Each entry is written for the person deciding whether to upgrade, so it
describes what is now possible rather than which files moved.

## Unreleased

HyperLab now runs in a browser, whole:
**[jgalego.github.io/HyperLab](https://jgalego.github.io/HyperLab/)**.

- **The website.** A retro landing page with a short history of HyperCard,
and a playground that is the full application — the same renderer the
desktop draws, over the same runtime compiled to WebAssembly. The six
examples are one click away, scripts block on real dialogs, and the map,
the message box, the inspector and undo all work as they do at home.
- **A stack still gets out.** Everything the desktop exports the browser
exports: the cards as a PDF, the map as a PNG, the whole stack as a web
page, and as a Decker deck. Words drawn inside artwork keep their
typeface, which a page has to be handed because it cannot read the
machine's own.
- **Files travel.** The playground opens and downloads stacks as the
single-file JSON the desktop already reads and writes, so a stack moves
between the two by moving one file.
- **Bring your own model, or none.** AI ▸ Settings in the browser takes
Anthropic, OpenAI, OpenRouter, anything OpenAI-compatible, or the built-in
mock. A key is kept in the browser's own storage, is never readable back,
and travels only to the provider it belongs to — the site is static files,
with no server of its own.
- The Todo example's list now ends with a return, so the first item anyone
adds lands on its own line instead of gluing itself to the last one.

For integrators: `hyperlab-persistence` gained in-memory single-file
functions (`single_file_string`, `stack_from_single_file`);
`hyperlab-ai-providers` gained a default-on `native` feature — without it
the crate is only the wire protocol, for hosts that bring their own
transport; and `hyperlab_stack::set_clock` lets a host name the clock on
platforms where the standard one traps, which WebAssembly in a browser is.

## 0.2.0 — 2026-08-01

A stack that cannot leave HyperLab is a stack you cannot show anyone. This
Expand Down
23 changes: 21 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,43 @@ whoever is reading the code in thirty years.
cargo test # the core crates: no GUI toolchain needed
cd apps/desktop && npm install
npm run tauri dev # the application

cd apps/web && npm install
npm run prepare:assets && npm run dev # the website — see docs/website.md
```

The core crates deliberately do not depend on Tauri, so `cargo test` at the
repository root works on any machine. If you are only touching the runtime,
the parser or persistence, you never need the desktop toolchain.
the parser or persistence, you never need the desktop toolchain. The website
needs Rust's `wasm32-unknown-unknown` target and
[wasm-pack](https://rustwasm.github.io/wasm-pack/), and nothing else.

## Before you open a pull request

```sh
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps

cd apps/desktop
npm run typecheck && npm run lint && npm run format:check
```

CI runs exactly these, so if they pass locally they will pass there.
And if you touched anything the website builds on — the core crates, the
desktop's components, or `apps/web` itself:

```sh
cargo clippy -p hyperlab-web --target wasm32-unknown-unknown --all-targets

cd apps/web
npm run prepare:assets # the wasm package and the example stacks
npm run format:check && npm run build
```

CI runs exactly these, so if they pass locally they will pass there. Do not
skip the documentation build: parts of `hyperlab-web` are compiled only for
wasm, and a doc link to one of them resolves everywhere except there.

## What good looks like here

Expand Down
33 changes: 33 additions & 0 deletions Cargo.lock

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

16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ Cards, stacks, fields, buttons, scripts and message passing are all still
here, and a stack stays inspectable and editable while it runs — the two
qualities that made the original worth learning in the first place.

🌐 **Try it right now, in your browser:**
**[jgalego.github.io/HyperLab](https://jgalego.github.io/HyperLab/)** — the
full application compiled to WebAssembly, with the six examples one click
away. Nothing to install, and nothing you make there leaves your machine
unless you ask a model about it.

🕯️ **Bill Atkinson (1951–2025)** wrote QuickDraw, MacPaint and HyperCard, and
insisted the last of them ship free with every Mac. Millions of people built
something with it, and a great many of them never thought of themselves as
Expand Down Expand Up @@ -67,6 +73,12 @@ card back](docs/demo.gif)

<h2 id="getting-started">🚀 Getting Started</h2>

🌐 **The website** — [jgalego.github.io/HyperLab](https://jgalego.github.io/HyperLab/)
is the whole application running in your browser, plus a short history of the
idea it comes from. A stack you download there opens in the desktop
application, and the other way round: it is the same file. How the site works
is written down in [`docs/website.md`](docs/website.md).

🖥️ **The application** — a `.dmg`, an `.msi`, and a `.deb`, `.rpm` or
`.AppImage`, on the
[releases page](https://github.com/JGalego/HyperLab/releases). Nothing phones
Expand Down Expand Up @@ -194,8 +206,12 @@ crates/
_hyperscript — where it goes next
decker/ a stack as a Decker
deck — where else it goes
web/ the same commands as
the shell, but as
WebAssembly — how it runs in a browser

apps/desktop/ the Tauri shell and the React renderer
apps/web/ the website: the same renderer over crates/web
docs/ architecture, roadmap, HyperTalk reference
examples/ six stacks, which are also tests
```
Expand Down
5 changes: 5 additions & 0 deletions apps/web/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 90,
"trailingComma": "all"
}
Loading
Loading