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
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ jobs:
gjsify workspace @learn6502/examples build
gjsify workspace @learn6502/learn build

# The tutorial is rendered into three unrelated artifacts and only
# app-gnome fails loudly on a broken one — at runtime, in whatever
# paragraph the reader opens. This validates all three. It rebuilds
# `packages/learn/dist` itself rather than trusting the step above:
# checking whatever artifact happens to be on disk is the defect that
# emptied the catalogs on 2026-09-03.
- name: Validate generated tutorial artifacts
run: gjsify workspace @learn6502/learn check

- name: Type check (gjsify tsc under GJS)
run: |
gjsify workspace @learn6502/core check
Expand Down
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ Principles: maximize code reuse via `common-ui`/`6502` packages; keep platform c
| vite-plugin-gettext | `packages/vite-plugin-gettext/` | Vite plugin for gettext localization | TypeScript |
| vite-plugin-blueprint | `packages/vite-plugin-blueprint/` | Vite plugin for Blueprint `.blp` files | TypeScript |

## Package scripts

A `build` or `check` script must be a **chain**, never a lone `gjsify …` command. Measured on the
pinned CLI (0.16.3, `runScript` in `@gjsify/cli/dist/cli.gjs.mjs`): when a script body tokenizes
as a single plain `gjsify …` command the CLI dispatches it in-process and then exits with
`process.exitCode ?? 0` — `gjsify build` and `gjsify run` report failure without setting it, so
the script exits 0 while its work failed, and every caller above it sees green. Any shell operator
in the body disables that path. `gjsify tsc` is unaffected (it spawns `gjs`).

This is why `learn`, `translations` and `examples` have no `:gen`/`:run` sub-scripts. Splitting a
chain back into single-command steps disarms the gate silently: it was how
`gjsify workspace @learn6502/translations check` reported "All translations passed" over catalogs
it had just emptied. Fix belongs upstream in gjsify; until then, keep the chains.

## TypeScript

Applies to all `.ts`/`.tsx` files.
Expand Down Expand Up @@ -317,6 +331,24 @@ deployed to GitHub Pages); dev = Vite + HMR (`vite.app.config.ts`). The classic
skilldrick Jekyll tutorial was removed in the app-web rewrite.
Focus: browser compatibility, responsive design, leverage `core` + `common-ui`.

## Learn — tutorial content

Applies to `packages/learn/` (`tutorial.mdx`, `quick-help.mdx`, `tsx/`).

One MDX source, three artifacts, three consumers: `dist/*.ui` (app-gnome, via `Gtk.Builder`),
`dist/*.ns.xml` (app-android) and `dist/*.html` (app-web) — plus `packages/translations`, which
extracts the tutorial's translatable strings from `dist/*.ui`.

**HTML entities are not markup here.** `Gtk.Label` renders an empty string when its markup fails
to parse, and Pango knows only `& < > " '` plus numeric references. Write the
character itself — `–`, `×`, a real non-breaking space — never `–`, `×` or ` `:
those cost the reader the entire paragraph, silently.

**Validate:** `gjsify workspace @learn6502/learn check` (rebuilds `dist/`, then checks XML
well-formedness, the element vocabulary, TRANSLATORS comments, label markup and code literals
across all three targets — also run in CI). Details in
[packages/learn/README.md](packages/learn/README.md).

## Translations

Applies to all `.po` files in `packages/translations/`.
Expand Down
4 changes: 1 addition & 3 deletions packages/examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
"./examples": "./examples.ts"
},
"scripts": {
"build:gen": "gjsify build build.ts --app gjs --outfile dist/build.js",
"build:run": "gjsify run dist/build.js",
"build": "gjsify run build:gen && gjsify run build:run"
"build": "gjsify build build.ts --app gjs --outfile dist/build.js && gjsify run dist/build.js"
},
"devDependencies": {
"@types/node": "^25.9.1",
Expand Down
50 changes: 46 additions & 4 deletions packages/learn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ The main tutorial content is stored in `tutorial.mdx`, which is based on Nick Mo

## Transformation Capabilities

The package includes tools to transform the MDX content into different formats:
Each MDX document is rendered into one artifact per platform, and each artifact has exactly one
consumer:

- **GNOME Application**: The content is transformed into GNOME Blueprint UI files (`.ui`) for use in the native GNOME application
- **Web Version**: Planned support for generating web-compatible content
- **Android App**: Potential future support for Android application content
| Artifact | Consumer | Loaded by |
| ----------------------- | ----------- | ----------------------------------------------------- |
| `dist/*.ui` | app-gnome | `Gtk.Builder`, as a `MdxView` template |
| `dist/*.ns.xml` | app-android | NativeScript `Builder.load` (copied to `app/mdx/`) |
| `dist/*.html` | app-web | imported by the web tutorial view |

`packages/translations` is a fourth consumer: its `xgettext` run extracts the tutorial's
translatable strings from `dist/*.ui`, which is why `@learn6502/translations`'s build builds this
package first.

## Development

Expand All @@ -32,6 +39,41 @@ gjsify run build

This will generate the necessary output files in the `dist/` directory.

### Checking

```bash
gjsify workspace @learn6502/learn check
```

`check.js` clears and rebuilds `dist/`, then validates the generated artifacts structurally — no
byte snapshot, so it survives reformatting the emitter but still fails on the changes that cost a
reader something. The clear is part of the rule, not tidiness: with the previous run's output
still on disk, an emitter that stops writing a target is read as one that wrote the same thing
again. CI runs it on every pull request. It asserts that:

- all six artifacts were written;
- the `.ui` and `.ns.xml` are well-formed XML with the expected root, and contain only elements
and object classes their consumer can resolve — `Gtk.Builder` and NativeScript's `Builder.load`
both refuse the whole document otherwise;
- every translatable label carries its `TRANSLATORS:` comment, which is the only context a
translator gets (the catalogs are generated with `noLocation`);
- every label is markup Pango accepts. A `Gtk.Label` whose markup fails to parse renders as an
empty string, so an HTML-only entity such as `–` costs the reader the whole paragraph —
write the character itself (`–`, `×`) in the MDX;
- the `.ui` carries every paragraph the `.html` does. The `.ui` is what `xgettext` builds the
catalogs from, so prose that stops reaching it stops existing for every translator, and nothing
downstream can miss what is no longer there — the `.html`, rendered from the same MDX in the
same run, is the witness;
- the same code literals reach all three targets. They are what the reader retypes into the
editor, and each target encodes them differently (`<tt>`, an escaped `w:SourceView`, `<code>`).

The rules run after their own self-tests: the markup rules against fragments each parser accepts
or rejects, and the extractors against shapes the emitter is allowed to produce. An extractor that
quietly matches less than it should is the worse failure of the two, because it shrinks the set a
rule compares against instead of reporting anything.

It says nothing about whether the tutorial is *correct*; that still needs a reader.

## License

- The tutorial content is licensed under the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/)
Expand Down
Loading
Loading