Skip to content
Open
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
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,24 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts

- name: Setup Elixir and OTP
uses: erlef/setup-beam@v1
with:
elixir-version: "1.20.4"
otp-version: "29.0.6"

- name: Install evaluator dependencies
working-directory: popcorn
run: mix deps.get --check-locked

- name: Check
run: |
pnpm lint
pnpm typecheck
pnpm build

- name: Install Chromium
run: pnpm exec playwright install --with-deps chromium

- name: Test homepage examples on a static server
run: pnpm test:popcorn
10 changes: 10 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts

- name: Setup Elixir and OTP
uses: erlef/setup-beam@v1
with:
elixir-version: "1.20.4"
otp-version: "29.0.6"

- name: Install evaluator dependencies
working-directory: popcorn
run: mix deps.get --check-locked

- name: Build Astro site
run: pnpm run build

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# build output
dist/
.llm/
popcorn/_build/
popcorn/deps/
popcorn/public/
# generated types
.astro/

Expand Down
14 changes: 2 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,8 @@ fixes; use `pnpm lint` for the equivalent read-only check.

## Deployment

- GitHub Pages: see `BUNDLE_AVM.md`, `.github/workflows/deploy.yml`, and the
`coi-serviceworker` setup.

## Popcorn (homepage hero editor)

- AVM bundle lives at `public/bundle.avm` (~7 MB, committed). Rebuild recipe
in `BUNDLE_AVM.md`.
- Vite plugin is conditionally registered in `astro.config.ts` based on the
bundle's presence so dev setups without it still build.
- The runtime requires `Cross-Origin-Opener-Policy: same-origin` +
`Cross-Origin-Embedder-Policy: require-corp` headers. On hosts that can't
set them, the `coi-serviceworker` workaround is used.
- GitHub Pages and Popcorn setup: see `README.md` and
`.github/workflows/deploy.yml`.

## Reveal animation system

Expand Down
80 changes: 0 additions & 80 deletions BUNDLE_AVM.md

This file was deleted.

29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Static Astro site, content authored in Markdown, deployed with GitHub Pages.
learning resources and version data
- Shiki for code highlighting: prose via the markdown pipeline, the
homepage snippets at build time (`src/utilities/elixir-highlight.ts`)
- [Popcorn](https://github.com/software-mansion/popcorn) (AtomVM + Elixir
- [Popcorn](https://github.com/software-mansion/popcorn) (Erlang/OTP + Elixir
in WebAssembly) powering the interactive hero code editor
- Biome for lint and format
- Playwright for smoke, visual and accessibility tests
Expand All @@ -24,13 +24,15 @@ Static Astro site, content authored in Markdown, deployed with GitHub Pages.

```bash
pnpm install
pnpm dev # http://localhost:4321
mise install --cd popcorn
mise exec --cd popcorn -- mix deps.get --check-locked
mise exec elixir@1.20.4-otp-29 erlang@29.0.6 -- pnpm dev
```

The Popcorn hero editor uses the AVM bundle at `public/bundle.avm`. The
file is committed (~7 MB) so the editor "just works" after `pnpm install`.
If it's missing the editor gracefully falls back to pre-canned outputs.
Regeneration instructions live in [`BUNDLE_AVM.md`](./BUNDLE_AVM.md).
Use the same Elixir/OTP toolchain for `pnpm build`, `pnpm preview`, and
`pnpm typecheck`. Popcorn's Vite plugin compiles and packages the evaluator
in `popcorn/`; no generated runtime binaries are committed. Building its
Brotli dependency also requires `make` and a C compiler.

## Scripts

Expand All @@ -43,6 +45,7 @@ Regeneration instructions live in [`BUNDLE_AVM.md`](./BUNDLE_AVM.md).
| `pnpm lint` | Biome lint only |
| `pnpm typecheck` | `astro check` (TypeScript across `.astro` and `.ts(x)`) |
| `pnpm test` | All Playwright specs |
| `pnpm test:popcorn` | Built homepage examples |
| `pnpm test:smoke` | Route smoke checks |
| `pnpm test:visual` | Visual regression snapshots |
| `pnpm test:a11y` | axe-core accessibility scan |
Expand All @@ -56,10 +59,9 @@ elixir-website/
├── biome.json
├── tsconfig.json
├── package.json
├── BUNDLE_AVM.md # how to (re)build public/bundle.avm
├── AGENTS.md # conventions for AI assistants
├── public/ # favicon, OG fallback, robots.txt,
│ # bundle.avm (Popcorn runtime)
├── popcorn/ # evaluator, locked dependencies and toolchain
├── public/ # favicon, OG fallback, robots.txt, isolation worker
├── scripts/ # Deployment and SVG optimization utilities
├── src/
│ ├── assets/ # fonts, icons, logos, images
Expand Down Expand Up @@ -180,9 +182,12 @@ GitHub Pages builds the site through `.github/workflows/deploy.yml`. The
workflow builds the canonical `https://elixir-lang.org` site and deploys the
generated `dist` directory.

The Popcorn WASM hero editor needs cross-origin isolation. Because GitHub
Pages cannot configure COOP/COEP response headers, production builds load
`public/coi-serviceworker.js` from `BaseLayout.astro`.
Publish the whole `dist/` tree, including Popcorn's generated runtime assets.
The runtime requires cross-origin isolation. On GitHub Pages, the first Run
registers `public/coi-serviceworker.js` and reloads once, preserving edits and
the requested execution. The worker persists across visits and requires
HTTPS (or localhost). Hosts that set `Cross-Origin-Opener-Policy: same-origin`
and `Cross-Origin-Embedder-Policy: require-corp` need no worker or reload.

Preview builds emit `noindex, nofollow`. Set `PROD=true` only in the official
deployment workflow to allow search engines to index the site.
Expand Down
3 changes: 2 additions & 1 deletion astro.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import mdx from "@astrojs/mdx";
import react from "@astrojs/react";
import sitemap from "@astrojs/sitemap";
import { popcorn } from "@swmansion/popcorn/vite";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig, fontProviders } from "astro/config";
import { toAstroRedirects } from "./src/data/redirects";
Expand Down Expand Up @@ -50,6 +51,6 @@ export default defineConfig({
},
integrations: [react(), mdx(), sitemap()],
vite: {
plugins: [tailwindcss()],
plugins: [tailwindcss(), popcorn({ rootDir: "./popcorn" })],
},
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lint": "pnpm exec biome check",
"typecheck": "astro check",
"test": "playwright test",
"test:popcorn": "playwright test --config playwright.popcorn.config.ts",
"test:smoke": "playwright test tests/smoke.spec.ts",
"test:visual": "playwright test tests/visual.spec.ts",
"test:a11y": "playwright test tests/a11y.spec.ts",
Expand All @@ -27,6 +28,7 @@
"@shikijs/langs": "3.23.0",
"@shikijs/themes": "3.23.0",
"@shikijs/types": "3.23.0",
"@swmansion/popcorn": "1.0.2",
"@tailwindcss/typography": "0.5.19",
"@tailwindcss/vite": "4.1.14",
"astro": "5.16.13",
Expand Down
22 changes: 22 additions & 0 deletions playwright.popcorn.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineConfig } from "@playwright/test";
import config from "./playwright.config";

const baseURL = process.env.BASE_URL || "http://127.0.0.1:4322";

export default defineConfig({
...config,
testMatch: "popcorn.spec.ts",
workers: 2,
use: { ...config.use, baseURL },
projects: config.projects?.filter((project) =>
["chromium-desktop", "mobile-s"].includes(project.name ?? ""),
),
webServer: process.env.BASE_URL
? undefined
: {
command:
"python3 -m http.server 4322 --bind 127.0.0.1 --directory dist",
url: baseURL,
reuseExistingServer: !process.env.CI,
},
});
23 changes: 23 additions & 0 deletions pnpm-lock.yaml

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

3 changes: 3 additions & 0 deletions popcorn/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
inputs: ["mix.exs", "{config,lib}/**/*.{ex,exs}"]
]
10 changes: 10 additions & 0 deletions popcorn/lib/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule WebsiteExamples.Application do
use Application

@impl true
def start(_type, _args) do
# The output pane displays plain text, not terminal escape sequences.
Application.put_env(:elixir, :ansi_enabled, false)
Supervisor.start_link([WebsiteExamples.Evaluator], strategy: :one_for_one)
end
end
39 changes: 39 additions & 0 deletions popcorn/lib/evaluator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule WebsiteExamples.Evaluator do
use GenServer

def start_link(_args), do: GenServer.start_link(__MODULE__, nil, name: :evaluator)

@impl true
def init(_args), do: {:ok, nil}

@impl true
def handle_call(["eval", source], _from, state) do
task = Task.async(fn -> evaluate(source) end)

result =
case Task.yield(task, 10_000) || Task.shutdown(task, :brutal_kill) do
{:ok, result} -> result
nil -> %{error: "Execution timed out after 10 seconds."}
end

{:reply, result, state}
end

defp evaluate(source) do
{value, _bindings} = Code.eval_string(source, [], file: "example.exs")
%{value: inspect(value, pretty: true, width: 50)}
rescue
error -> %{error: Exception.format(:error, error, user_stacktrace(__STACKTRACE__))}
catch
kind, reason -> %{error: Exception.format(kind, reason, user_stacktrace(__STACKTRACE__))}
end

defp user_stacktrace(stacktrace) do
Enum.take_while(stacktrace, fn
{:elixir, _, _, _} -> false
{Code, :validated_eval_string, _, _} -> false
{__MODULE__, _, _, _} -> false
_ -> true
end)
end
end
3 changes: 3 additions & 0 deletions popcorn/mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tools]
elixir = "1.20.4-otp-29"
erlang = "29.0.6"
16 changes: 16 additions & 0 deletions popcorn/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule WebsiteExamples.MixProject do
use Mix.Project

def project do
[
app: :website_examples,
version: "0.1.0",
elixir: "~> 1.20",
deps: [{:popcorn, "== 1.0.1"}]
]
end

def application do
[mod: {WebsiteExamples.Application, []}]
end
end
Loading