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
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ on:
pull_request:

jobs:
changes:
name: Detect changed paths
runs-on: ubuntu-latest
outputs:
playground: ${{ steps.filter.outputs.playground }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
playground:
- 'tools/playground/**'

reference-go:
name: Build reference/ + validate conformance fixtures
runs-on: ubuntu-latest
Expand All @@ -31,3 +45,22 @@ jobs:
# catches a wrong implementation.
# TODO: once Resolve/Progress are implemented, extend go test's
# coverage to conformance/{resolve,progress}/ the same way.

playground-web:
name: Build tools/playground/
needs: changes
if: needs.changes.outputs.playground == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: tools/playground
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: tools/playground/package-lock.json
- run: npm ci
- run: npm run typecheck
- run: npm run build
21 changes: 20 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ built out: nothing needs them yet.
| `bindings/` | Per-language wrappers around `reference/`. Placeholders — don't build speculatively. |
| `examples/` | Real `.owl` programs, for reading. |
| `docs/` | User-facing tutorials. Not written yet. |
| `tools/playground/` | Web REPL. Not built yet, low priority. |
| `tools/playground/` | Browser IDE for OWL projects (file tree, tabs, split view). MVP scaffold — no compiler wired in yet. |
| `grammar/` | Deliberately empty — no parser-generator artifact, since `reference/` hand-writes its parser. |

## `reference/` — where the actual code lives
Expand Down Expand Up @@ -66,6 +66,25 @@ A hand-written recursive-descent parser is the deliberate choice over a
parser generator (ANTLR/pigeon/etc.) — `grammar.ebnf`'s productions don't
need one, and it keeps the toolchain to just Go.

## `tools/playground/` — workout IDE (MVP)

A React + TypeScript + Vite browser app: file tree, tabs (opening a
`.md` file shows two tabs — source and rendered preview), and up to
three resizable split panes. State lives in React and is mirrored to
the browser's `localStorage`, so a project survives closing and
reopening the tab — but it's per-browser only, nothing syncs across
devices yet. **No compiler is wired in** — `.json` files are just
files a user creates, not the output of compiling an `.owl` file. A
new project's file tree starts empty; there is deliberately no seeding
from this repo's `examples/`.

The top bar has two intentionally non-functional stubs — "Publish"
(future: push the project to a not-yet-built marketplace) and "Send to
Phone" (future: hand the project to the phone app via a QR code) — so
their UI shape exists ahead of the backend work they depend on. See
[`tools/playground/README.md`](tools/playground/README.md) for the
full current scope and how to run it.

## Building and testing

```sh
Expand Down
3 changes: 3 additions & 0 deletions tools/playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
*.local
88 changes: 77 additions & 11 deletions tools/playground/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,79 @@
# tools/playground/

A web REPL: paste an OWL program, see it parsed, compiled to canonical
form, and (given some `state`) resolved into a session.

**Lower priority given the chosen architecture.** The product doesn't
need a browser-side compiler — compilation happens server-side in Go (see
[`../../reference/README.md`](../../reference/README.md)) and the phone
just receives JSON. If built, this would most simply be a small static
page that calls a local instance of the `owlc` CLI's HTTP-server mode (or
a thin dev-only endpoint) rather than compiling in-browser via WASM — no
need for Go's `js/wasm` target unless client-side compilation becomes a
real requirement later.
A browser-based IDE for authoring OWL workout projects: a file tree,
tabbed editing, and up to three side-by-side panes. React + TypeScript
+ Vite.

## Current scope (MVP)

- **File tree** (left sidebar) — flat list of in-memory files, each an
`.owl`, `.json`, or `.md` file. A new project starts **empty**; use
"+ New File" to create files (extension determines kind). Files
support rename (double-click) and delete.
- **Tabs** — clicking a file opens it in the focused pane. `.owl` and
`.json` files open as a single source tab; `.md` files open as
**two** tabs, "name.md" (raw source, editable) and "name.md
(Preview)" (rendered markdown, via `react-markdown` + `remark-gfm`).
- **Split view** — 1 to 3 vertical panes ("Split" / "Close Pane" in
each pane's tab bar). Any file can be opened independently in any
pane.
- **Editing** — plain monospace `<textarea>` per file, held in React
state (`src/state/WorkspaceContext.tsx`) and syntax-highlighted for
`.owl`/`.json` (see `src/utils/highlight.ts`).
- **Persistence** — the whole workspace (files, open tabs, split
layout, pane widths) is saved to the browser's `localStorage`
(`src/state/persistence.ts`) on every change and restored on load, so
closing and reopening the tab picks up where you left off. This is
per-browser only — nothing is synced to a server or another device,
and clearing site data wipes it. Stored data is versioned and
validated on load; anything that doesn't parse or doesn't match the
current shape is discarded rather than crashing the app.

**No compiler is wired in.** `.json` files are just files a user
pastes or types into — there is no "Compile" action turning an `.owl`
source into canonical JSON yet. See "Future work" below.

## Not built yet (visible stubs)

The top bar has two buttons that render disabled with a "Coming soon"
tooltip — they fix the UI shape for later work without functioning yet:

- **Publish** — will publish the current project to the (not yet
built) marketplace.
- **Send to Phone** — will hand the current project to the
OpenWorkout phone app via a QR code.

Neither has any backend, marketplace, or QR logic behind it today.

## Future work

- **Compilation.** Per the root [`CLAUDE.md`](../../CLAUDE.md) and
[`reference/README.md`](../../reference/README.md), compilation runs
server-side in Go — the intended shape here is a "Compile" action
that calls a local `owlc` HTTP-server mode (or a thin dev-only
endpoint), not an in-browser/WASM compiler. If client-side
`Compile`/`Resolve` ever becomes a real requirement, that's what
[`bindings/js/`](../../bindings/js/) is the placeholder for — nothing
in this app imports it today.
- **Forking.** Opening a project tree "forked" from someone else's
published program (once Publish/marketplace exist) — today, every
project starts empty. The file/pane state (`src/types.ts`,
`src/state/WorkspaceContext.tsx`) is a plain in-memory model, so
seeding it from fetched files instead of starting empty is a small
extension, not a redesign.
- **A real code editor.** CodeMirror or Monaco are the natural upgrades
over the current hand-rolled highlighted-`<textarea>` overlay, when
that's worth the dependency weight.
- **Cross-device / account-backed persistence.** Today's `localStorage`
save is per-browser only; syncing a project across devices needs a
backend, which is really the same future work as Publish.

## Running locally

```sh
cd tools/playground
npm install
npm run dev # dev server with hot reload
npm run typecheck # tsc --noEmit
npm run build # typecheck + production build to dist/
```
12 changes: 12 additions & 0 deletions tools/playground/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OWL Playground</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading