Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History

README.md

Outline

daslang.io — local development

The site published at daslang.io and deployed by .github/workflows/pages.yml. This README is the canonical guide for running each surface locally — pages.yml references the same commands and treats this file as the source of truth.

What's in site/

site/
├── index.html              # landing — hand-authored
├── downloads.html          # downloads & links — hand-authored
├── files/
│   ├── forge.css           # tokens + components (source of truth) — includes responsive rules + mobile nav
│   ├── forge.js            # sample switcher · install tabs · bench · news (skips CM init on mobile)
│   ├── highlight.js        # daslang tokenizer (hero + blog code blocks)
│   ├── runner.js           # mini-playground WASM shim
│   ├── cm/                 # CodeMirror bundle: codemirror.min.{js,css}, simple-mode.js,
│   │                       # daslang-keywords.js, daslang-mode.js, cm-forge.css (Forge theme)
│   ├── lz-string.min.js    # share URL compressor (used by /playground/)
│   ├── daslang.svg            # master logo (single path, currentColor-tinted)
│   ├── daslang-favicon.svg    # yellow-on-black tile favicon
│   ├── daslang.ico            # multi-size .ico fallback
│   ├── profile_results.json   # fetched by CI from borisbat/dasProfile (gitignored)
│   ├── news.json              # generated by build_blog.py (gitignored)
│   └── wasm/                  # daslang_static.{js,wasm} from CI (gitignored)
├── blog/
│   ├── _posts/*.md         # 25 posts (source of truth — Markdown w/ Hexo extensions)
│   ├── template.html       # Forge chrome (used by build_blog.py for posts + index)
│   ├── build_blog.py       # generates blog/, news/, changelist.html, news.json
│   ├── index.html          # GENERATED — post listing
│   ├── <slug>.html         # GENERATED — one per post
│   └── feed.xml            # GENERATED — Atom feed
├── _news/*.md              # short news entries (one .md per item, body optional)
├── changelist.html         # GENERATED by build_blog.py — full news list
├── news/<slug>.html        # GENERATED — micro-pages for news entries with bodies
├── playground/
│   ├── index.html              # Forge nav + IDE body (mobile gate at top of <body>)
│   ├── forge-skin.css          # CSS overrides on web/examples/ui/src/main.css + mobile notice + splitter
│   ├── playground-init.js      # URL-hash dispatch (#code= legacy / #z= multi-file)
│   ├── playground-tabs.js      # multi-file state, tab strip, autosave to localStorage
│   ├── playground-share.js     # ↗ share popover, is.gd shortener
│   ├── playground-splitter.js  # draggable code | output column splitter
│   ├── samples/                # multi-file sample bundles (gitignored, mirrored from web/examples/ui/samples)
│   └── *.{js,css}              # other vendored bits from web/examples/ui/src/ for local-dev (gitignored)
├── tests/
│   └── playground/             # Playwright e2e suite (28 specs, ~5 s no-WASM)
└── doc/                        # Sphinx HTML output (gitignored, deployed by CI)

Sources (forge.css, *.md, *.py, template.html, *.html hand-authored) are committed. Build artifacts (blog/<slug>.html, news/, changelist.html, files/wasm/, files/news.json, files/profile_results.json, doc/) are generated and gitignored — see site/.gitignore.

Surface map (what runs where)

Surface Desktop Mobile (<768 px)
Landing hero CodeMirror editor with sample picker, ▶ run, ↗ playground handoff Static <pre><code class="language-daslang"> block, no CM init. Hero buttons + sample tabs hidden via CSS.
/playground/ Full multi-file IDE — tabs, splitter, share via #z= hash, ▶ run via WASM "Open this on a laptop" notice. pageInit is overridden before WASM fetch — daslang_static.{js,wasm} is never requested.
/blog/ Full post + Disqus comments at the bottom (shortname https-borisbat-github-io-dascf-blog, identifier = slug, Auto theme picks dark via :root { color-scheme: dark }) Same. Disqus is responsive.
Nav Inline links: docs · benchmarks · downloads · blog · community + v0.6.3, github ↗, install ≡ hamburger toggles a dropdown panel with the same links. v0.6.3 chip is hidden at <480 px.

The mobile fallbacks are pure CSS + a tiny synchronous gate script — no UA sniffing, no JS feature-detection. Viewport-width is the only signal. Open Chrome DevTools → device toolbar (Cmd+Shift+M) and reload to test, or browse from a phone on the same WiFi using your LAN IP (ipconfig getifaddr en0).

Quick start (landing only)

cd site && python3 -m http.server 8000
open http://localhost:8000/

The landing page works on its own (no build step). Open DevTools — the benchmarks section, install-tab toggle, and hero sample switcher should all work without errors. The hero ▶ run button is degraded until you build the WASM artifact (see below).

Building the blog + news + changelist

pip install --user markdown   # one time
python3 site/blog/build_blog.py \
    --posts site/blog/_posts \
    --news site/_news \
    --template site/blog/template.html \
    --out site/

This regenerates:

  • site/blog/index.html, site/blog/<slug>.html for all 25 posts
  • site/blog/feed.xml (Atom)
  • site/changelist.html (full news list)
  • site/news/<slug>.html for news entries with bodies
  • site/files/news.json (top 8 for landing § 05)

Then re-run the http.server. Refresh.

Adding a new blog post

# 1. Create a Markdown file under site/blog/_posts/<slug>.md with front matter:
cat > site/blog/_posts/<slug>.md <<'EOF'
---
title: Your post title
date: 2026-05-13 14:30:00
tags:
    - daslang
    - performance
---
Body in Markdown. Use ```daslang fenced blocks for code. <!-- more -->
Content after the marker only shows on the post page, not on the index.

Cross-link another post: {% post_link other-slug %}
EOF

# 2. Rebuild (see above)
python3 site/blog/build_blog.py --posts site/blog/_posts --news site/_news \
    --template site/blog/template.html --out site/

# 3. Push — pages.yml rebuilds and deploys.

Adding a short news entry (one-liner on the landing)

# Create a file under site/_news/<slug>.md (no body for a one-liner):
cat > site/_news/<slug>.md <<'EOF'
---
date: 2026-05-13
tag: 0.6.2
title: One-line description
link: https://github.com/.../releases/tag/v0.6.2
---
EOF

# Rebuild — same command as the blog. The entry lands in the top-5 § 05 feed
# on the landing AND in the full /changelist.html. If you add body content
# after the closing ---, build_blog.py also emits /news/<slug>.html.

Sphinx docs (retokened to Forge)

# Local sphinx-build inside the project's venv.
sphinx-build -W --keep-going -b html doc/source build/site
open build/site/index.html

(Memory note: on Boris's machine, an alternative path is /Users/borisbatkin/Library/Python/3.11/bin/sphinx-build, version 7.2.6.)

Verify: dark #0d0c0a background, amber #e8a13a accent on links and the active sidebar item, JetBrains Mono code blocks, the > typographic logo in the top-left. Build must complete with -W (zero warnings).

Mini playground (hero "▶ run")

The mini playground needs the WASM artifact at site/files/wasm/. Build it once (then pip install-free; subsequent ninja rebuilds are fast):

# Install emcc (see web/README.md), then:
source ~/emsdk/emsdk_env.sh
emcmake cmake -S web -B web/cmake_temp -G Ninja -DCMAKE_BUILD_TYPE=Release && cmake --build web/cmake_temp
# stage_site_playground auto-drops daslang_static.{js,wasm} into site/files/wasm/.

Re-run the http.server. The hero ▶ run button should now compile and run the sample. Edits to the code block re-tokenize on input; click ▶ run to re-execute.

Full playground (/playground/)

The full IDE is web/examples/ui/ (CodeMirror, samples picker, multi-file). For local preview, vendor its source files into site/playground/ and copy the sample bundles:

# stage_site_playground (from the WASM build above) auto-vendors UI + samples
# + daslang_static.{js,wasm} into site/playground/.
# Forge skin auto-loads from site/playground/forge-skin.css.
# CodeMirror itself + the Forge CM theme load from /files/cm/ (already in site/).

JIT (wasm) engine

Optional: build precompiled .wasm per sample so the playground's "JIT (wasm)" radio works. all_wasm auto-stages them into site/playground/samples/examples/.

cmake -B build -DDAS_LLVM_DISABLED=OFF -DDAS_BUILD_WASM=ON && cmake --build build --target all_wasm

Open http://localhost:8000/playground/. Forge nav at the top, multi-file tab strip + Select example + ↗ share + ▶ run on the left of a unified toolbar, clear on the right. Below: code | output split by a draggable handle (drag to resize, double-click to reset to 50/50). Layout is one container — code and output share a single outer border.

Multi-file workflow

  • Tabs: main.das is the fixed entry point — non-renameable, non-deletable. + adds untitled1.das, untitled2.das, … Double-click a tab name to rename (validates .das suffix, rejects duplicates). × deletes (confirms if non-empty).
  • Run: writes every open file to MEMFS, then Module.callMain(['main.das']). require utils in main.das resolves against MEMFS as a normal filesystem lookup, so multi-file modules / macros work out of the box (see the "Macros (multi-file)" sample).
  • Share (↗ share): builds location + '#z=' + LZString.compressToEncodedURIComponent(JSON.stringify(state)). The popover offers a clipboard copy and an is.gd shorten button. Opening a #z=... URL in a fresh browser restores the full multi-file state. The legacy #code=<percent> hash (emitted by the landing hero's ↗ playground handoff) is still accepted and routes the content into main.das.
  • Autosave: 250 ms debounced write of the full state to localStorage['daslang.playground.state.v1']. Restored on next load only if neither #code= nor #z= is present in the URL.
  • Splitter position is persisted to localStorage['daslang.playground.splitLeftPct'].

Full _site preview (everything stitched together)

Replicate pages.yml end-to-end locally:

mkdir -p _site
cp site/*.html _site/
cp site/robots.txt _site/
cp -r site/files _site/files

# Blog
python3 site/blog/build_blog.py \
    --posts site/blog/_posts \
    --news site/_news \
    --template site/blog/template.html \
    --out _site/

# Playground (assumes web/ has been built — see above)
mkdir -p _site/playground _site/files/wasm
cp -r web/examples/ui/src/* _site/playground/
cp web/output/daslang_static.{js,wasm} _site/playground/
cp web/output/daslang_static.{js,wasm} _site/files/wasm/
cp site/playground/index.html _site/playground/index.html
cp site/playground/{forge-skin,cm-forge}.css _site/playground/

# Sphinx (requires daslang binary for das2rst — see doc/ build instructions)
sphinx-build -b html doc/source _site/doc

cd _site && python3 -m http.server 8000

Click through: landing → blog → a post → docs (Sphinx) → playground → run a sample. No broken links, consistent Forge chrome on every page.

Updating dasProfile snapshot

The benchmark numbers come from borisbat/dasProfile/profile_results.json. CI fetches the latest on every publish. Local dev:

curl -sSL -o site/files/profile_results.json \
    https://raw.githubusercontent.com/borisbat/dasProfile/main/profile_results.json

The benchmarks chart on the landing reads it directly — no rebuild needed.

Playwright e2e suite (site/tests/playground/)

28 specs covering: dropdowns, tab strip CRUD, multi-file persistence, share-URL round-trip, splitter drag, hero ↗ playground handoff, mobile gate (asserts WASM is not fetched at narrow viewports). 5 are tagged @wasm and only run when the WASM artifact is present.

# Start the dev server from site/ (so paths resolve like prod).
cd site && python3 -m http.server 8765 &

# In another shell:
cd site/tests/playground
npm ci                                  # one-time
npx playwright install chromium         # one-time
npx playwright test --grep-invert "@wasm"   # ~5 s, no WASM needed
npx playwright test                     # full suite, requires WASM at site/playground/

CI runs the no-WASM subset on every PR via .github/workflows/playground-e2e.yml. The @wasm-tagged specs are gated on the WASM build being present locally — no dedicated CI tier yet.

Common gotchas

  • Hero ▶ run shows "WASM runtime not deployed" — you haven't built the WASM artifact yet. Either follow "Mini playground" above, or just preview the static landing without it (the run button is the only thing that won't work).
  • /playground/ shows a blank page — you forgot to cp web/examples/ui/src/* site/playground/. main.js, main.css, and jquery-3.6.0.min.js are needed; CodeMirror itself lives at site/files/cm/ and is already in the repo.
  • Mobile gate doesn't fire — open Chrome DevTools, toggle the device toolbar (Cmd+Shift+M), pick a phone preset, then hard-refresh (Cmd+Shift+R). The mobile detector is a synchronous script that runs once on page load — toggling the toolbar after load doesn't retrigger it.
  • Blog code blocks aren't syntax-highlighted — make sure site/files/highlight.js is loaded. The blog template includes it; if you built with --out /tmp/foo/ and serve from there, your asset paths may diverge from the ../files/ prefix the template uses.
  • /changelist.html is empty — re-run build_blog.py; it generates the changelist from site/_news/*.md.

How CI differs from local dev

The CI flow in .github/workflows/pages.yml mirrors the steps above. Differences worth knowing:

  • CI installs emsdk fresh every run (cached across runs by GitHub).
  • CI fetches profile_results.json on each publish.
  • CI builds Sphinx with -W (treats warnings as errors). Local builds should do the same before pushing.
  • CI uploads to GitHub Pages via actions/deploy-pages@v4.

If something works locally but fails on CI, walk through the workflow file side-by-side with this README — the commands should match.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.