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
11 changes: 11 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ jobs:
if: ${{ !cancelled() }}
run: npm run check:conventions

# a page that is WRAPPED has to stay wrapped. The manual is not written to
# one column - 39 pages are wrapped, 59 are one line per paragraph - so a
# site-wide column would be a reformat, not a gate. What is decidable is
# the drift: an editor rewrites a paragraph, hands it back as one long
# line, and the next diff of that paragraph is one changed line instead of
# three, so every later correction to it reads as a rewrite. It arrived
# twice in two days on the same page. npm run fix:line-length rewraps.
- name: line length
if: ${{ !cancelled() }}
run: npm run check:line-length

# every complete app class either carries a Run button or a marker on its
# page saying why it cannot run in the playground. The rules that offer
# the button fail towards NOT offering one, so without this an example
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ jobs:
- name: house conventions
if: ${{ !cancelled() }}
run: npm run check:conventions
- name: line length
if: ${{ !cancelled() }}
run: npm run check:line-length
- name: Run-button coverage
if: ${{ !cancelled() }}
run: npm run check:playground
Expand Down
14 changes: 9 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ person reads the page. Do not put "as an AI, …" prose back into `docs/`.
| `docs/.vitepress/playground.mjs` | Decides which fenced ABAP example gets a **Run** button, and wraps the fence; `theme/playground.js` is the browser half |
| `scripts/list-runnable.mjs` | The measurement's worklist: every fenced example that carries a Run button, out of the same `playground.mjs` that decides the button. `--json` adds each example's ABAP verbatim - what the button sends - so the measurement below can be driven rather than clicked |
| `scripts/check-playground.mjs` | The Run-button bookkeeping: every complete app class either gets a button from `playground.mjs` or carries a `<!-- playground: no Run button — … -->` marker above its fence saying why it cannot run; a stale marker fails as loudly as a missing one. `--list` prints the deliberate exclusions with both reasons |
| `scripts/check-line-length.mjs` | The wrapped-page rule and `--fix` (`npm run fix:line-length`), which rewraps a drifted paragraph. `scripts/lib/line-length.mjs` is what a prose line IS here and what a rewrap may touch; pinned by `test/line-length.test.mjs`, which holds the manual to the rule as its last case |
| `scripts/check-conventions.mjs` | The two house conventions the sample corpora gate and this one did not: the view-chain layout in every fenced chain (the linter's `chain-house-layout`, which is opt-in — `check-examples.mjs` writes its config without a `rules` block, so the rule was never emitted), and the three class section blocks in every fenced app class. `--fix` (`npm run fmt:chains`) reformats a drifted chain; the sections are a judgement and stay by hand |
| `scripts/lib/catalogue.mjs` | Parses and counts a sample catalogue, for `link-samples.mjs` and for the figures `generate-llms.mjs` writes into `llms.txt` — from a sibling checkout when one is here, else from the `catalogue.json` each sample repository commits at its root; pinned by `test/catalogue.test.mjs`, because it has stopped matching twice and both times answered wrongly instead of failing |
| `scripts/build-site.mjs` | **What is published.** Writes all 167 pages, the 404, the sitemap and one stylesheet; borrows the bar, its script, `catalogue.css`, `sample.css`, `search.mjs` (bundled INTO `site.js`, so a page loads one module rather than two) and `abap-highlight.mjs` from a built playground checkout or from the published site; refuses to finish on a dead internal link |
Expand Down Expand Up @@ -56,8 +57,8 @@ person reads the page. Do not put "as an AI, …" prose back into `docs/`.
npm run check # test + check:version + docs:build + check:cross-site + check:design + check:images + check:examples + check:conventions + check:playground + check:api-names + check:api-reference + check:samples
```

A documentation repository has no compiler for its prose, but thirteen things
in it are decidable, and all thirteen are decided before a merge:
A documentation repository has no compiler for its prose, but fourteen things
in it are decidable, and all fourteen are decided before a merge:

| | |
|---|---|
Expand All @@ -68,6 +69,7 @@ in it are decidable, and all thirteen are decided before a merge:
| `check:api-names` | every `client->` name on the site — method, parameter, `cs_*` constant — against `z2ui5_if_client` on `main`, plus every `blob/main/` link into the framework's tree, plus **no name from the frozen package** (`src/99`: `z2ui5_cl_util*`, `z2ui5_cl_pop_*`, `z2ui5_cl_xml_view*`, `z2ui5_if_exit`, `z2ui5_if_types`, …) anywhere but on the deprecations page and in the changelog. `check:examples` compiles the fenced blocks that are whole CLASSES; this is the rest of the page: the sentence, the two-line snippet, the constant block a page reproduces, the source link. Four pages taught API that 1.143.0 had deleted and nothing was red |
| `check:api-reference` | the committed client API reference — the generated block in `resources/api.md` and `docs/public/api/client-api.json` — regenerated from `z2ui5_if_client` on `main` and compared byte for byte. Goes stale whenever the interface changes over there and the committed reference still describes the shape before it. `npm run generate:api` rewrites both |
| `check:conventions` | the fenced ABAP against the house style the reader meets next: the view-chain layout, and the three section blocks of an app class. `check:examples` asks whether an example compiles and names real API — both questions about the framework; neither can see that a snippet is written in a different style from every sample. Measured against [samples-controls](https://github.com/abap2UI5/samples-controls) (637 classes, gated, at zero): five chains here showed the reader a different tree than the one that renders, and 57 of 86 app classes carried neither `PROTECTED SECTION.` nor `PRIVATE SECTION.`. What this gate deliberately does NOT take over is the blank-line and `t_arg` continuation rules — those are pattern-lint *warnings* over there and that corpus carries 382 of them |
| `check:line-length` | **a page that is wrapped has to stay wrapped.** The manual is not written to one column and should not be: 39 pages are wrapped, 59 are one line per paragraph, 64 are in between, so a site-wide column would be a reformat of two thirds of the pages rather than a gate. The drift is the decidable part - a page whose prose already sits inside 80 characters may not acquire a line outside it. That is what an editor does to a paragraph it rewrites: it hands it back as ONE long line, invisible in the editor, and from then on every diff of that paragraph is one changed line instead of three, so each later correction reads as a rewrite. It happened twice in two days on the same page. Held: 66 pages; the other 97 are left alone and start being held the day somebody wraps them. Not prose, and the reason each one is not: fenced code, frontmatter, tables (they wrap at the cell), HTML, headings and image alt text (one line by construction), the caption under an image when it repeats that alt text word for word (the two are meant to be comparable at a glance), a line that is only long because of one unbreakable URL, and the generated `samples:` and `api:` blocks (wrapping one by hand is a change the next regeneration undoes). `npm run fix:line-length` rewraps a drifted paragraph - whitespace only, and verified as such: the words are identical and the rendered article is identical, which is the check that caught the two bugs in the rewrapper (it tore the punctuation off a code span, and it swallowed a `:::` container into the paragraph) |
| `check:playground` | every complete app class on the site either carries a **Run** button or a marker on its page saying why it cannot run. The rules that offer the button fail towards *not* offering one, so without this an example nobody ever measured is indistinguishable from an example that can never run — which is exactly how the coverage ledger below went stale. What stays undecidable by CI — does a *buttoned* example actually start — is the measurement the Run-button section describes |
| `check:cross-site` | every link that leaves this deployment for a neighbouring one on the same origin — the playground, the catalogue, the linter's rule pages — carries a `target`. Without it VitePress's router treats the link as a route of THIS site, finds no page behind `/playground/` and renders the 404 *at that URL*, which reads as the other site being broken. Every way out of the manual was in that state at once: both bar items, the Linter rules row in the menu and the Run bar's link. Judges the built HTML, so it runs straight after `docs:build`. What it cannot see is the Run bar's link — built in a browser, in no built page — which is why `test/cross-site.test.mjs` pins that one as source |
| `check:design` | the values the four bars are made of — the seven palette colours, the two type stacks, the two radii — against the copy [abap2UI5/playground](https://github.com/abap2UI5/playground) keeps, in **both** schemes. They are copied by hand on purpose (a stylesheet fetched across two deployments is a request in front of the first paint), and until this gate nothing compared the copies: they agreed because whoever touched one remembered the other. One had already drifted — two font stacks leading with different families, which is the same face on macOS and Windows and two different ones on Linux, so the same four words in the same bar measured 59/122/78/97px here and 65/141/87/110 there. It compares the EFFECTIVE value (a property the dark block does not redeclare keeps its light one), because the two sides switch schemes differently: `.dark` here, `[data-theme]` over there.
Expand All @@ -76,16 +78,18 @@ in it are decidable, and all thirteen are decided before a merge:
| `check:images` | every image under `docs/public`, against the three things a page can afford and the one it cannot: a screenshot is WebP (the PNG captures were 200 to 335 kB each, 2.9 MB across the manual, on pages of 20 kB of text; the same captures as WebP are a fifth of that), a deliverable is one of the PNGs the logo page hands out, nothing is over its budget, and the build can measure every one - an image it cannot size gets no width and height and moves the page when it lands |
| `check:samples` | the **Working Samples** blocks and the source links a page writes by hand, against [abap2UI5/samples](https://github.com/abap2UI5/samples), [samples-controls](https://github.com/abap2UI5/samples-controls) and [samples-stack](https://github.com/abap2UI5/samples-stack) — a page may declare a class of any of the three |

**All four walking gates carry a floor.** A gate that checked nothing reports
**All five walking gates carry a floor.** A gate that checked nothing reports
the same shape as a gate that found nothing wrong — which is precisely how
`check:examples` passed for years on an abaplint config with no rules in it. So
`check:examples`, `check:conventions` and `check:playground` each exit 1 when
their walk finds no example at all, and say which of the fence language, the
page layout or the builder name is the likely cause. `check:cross-site` carries
page layout or the builder name is the likely cause. `check:line-length` exits 1
when no page of the site counts as wrapped, which would mean its fence handling
or its glob stopped matching rather than that the manual went loose. `check:cross-site` carries
the same floor twice over: no HTML in `dist` at all, and no cross-site link on
a site whose bar carries three of them on every page.

The thirteen are written out in **three** places, and all three have to name the
The fourteen are written out in **three** places, and all three have to name the
same set: `package.json`'s `check` script, `.github/workflows/check.yml` for a
pull request, and `.github/workflows/deploy.yml` before the site is published.
`check.yml` runs them in the script's order; `deploy.yml` cannot, because it
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md

All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source of
truth for this repository (how the site is built, the thirteen gates, the playground rule engine, and what may be written where).
truth for this repository (how the site is built, the fourteen gates, the playground rule engine, and what may be written where).

Read `AGENTS.md` before making any change.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ Nothing here is a source of truth about the framework: the API reference, the
sample catalogue and the corpus figures are generated or checked against
`abap2UI5`, `abap2UI5/samples` and its siblings. If a fact is wrong, it is
usually wrong there first. [AGENTS.md](AGENTS.md) is the full contract — how
the site is built, the thirteen gates, and what may be written where.
the site is built, the fourteen gates, and what may be written where.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Every contribution makes the documentation better for the community!
```sh
npm ci
npm run docs:dev # the site, with hot reload
npm run check # what CI runs, all thirteen steps
npm run check # what CI runs, all fourteen steps
```

### What CI checks

A documentation repository has no compiler for its prose, but thirteen things in
it are decidable, and `npm run check` decides all thirteen before a merge — the
A documentation repository has no compiler for its prose, but fourteen things in
it are decidable, and `npm run check` decides all fourteen before a merge — the
prose builds (`docs:build`), the four bars are still made of the same palette,
type and radii as the playground's (`check:design`), every link into a
neighbouring site on this origin
Expand All @@ -45,7 +45,7 @@ passed. Several of these go stale without anybody touching this repository (a
release is published elsewhere, a sample class is renamed elsewhere), which is
why the deploy re-runs them rather than trusting the merge.

**[AGENTS.md](AGENTS.md) describes each of the thirteen**, what a failure means and
**[AGENTS.md](AGENTS.md) describes each of the fourteen**, what a failure means and
which of them need a sibling checkout to say anything at all — read it before
changing anything beyond prose.

Expand Down
4 changes: 2 additions & 2 deletions docs/advanced/insights/22-who-may-start-which-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ instead of a view:

One authorization object with one field, the app class as the value, roles as
usual in PFCG. Nothing here is new to anybody in the room, and that is the
point: the check sits in the class it protects, so a transport carries the app and its
guard together, and nothing on the node has to know which classes exist.
point: the check sits in the class it protects, so a transport carries the app
and its guard together, and nothing on the node has to know which classes exist.

It also holds on the way into an app that the URL never names: a
`nav_app_call( )` from another app arrives as an ordinary roundtrip with no
Expand Down
6 changes: 3 additions & 3 deletions docs/advanced/insights/24-abap-unit-for-a-screen.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ decision in the app rather than in the framework: **the logic does not touch
`client`.**

`main( )` dispatches — [#16](/advanced/insights/16-one-click-one-request). The
methods it dispatches to read data, decide, and change attributes. Only `view_display( )` and the message calls need the
client, so a test calls the other methods directly and looks at the attributes
afterwards:
methods it dispatches to read data, decide, and change attributes. Only
`view_display( )` and the message calls need the client, so a test calls the
other methods directly and looks at the attributes afterwards:

```abap
CLASS zcl_app_overdue DEFINITION PUBLIC.
Expand Down
5 changes: 3 additions & 2 deletions docs/advanced/insights/36-written-for-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ still has a method per control.

**A check that needs no system.** The [linter](/advanced/linter) reconstructs
the UI5 view out of the ABAP that builds it and reports what UI5 does not have.
An agent that can verify its own work stops handing over apps that do not
render — the difference between a helper and a generator of plausible nonsense. The same linter gates the sample repositories.
An agent that can verify its own work stops handing over apps that do not render
— the difference between a helper and a generator of plausible nonsense. The
same linter gates the sample repositories.

**Several hundred worked examples.** The sample catalogs hold a complete,
tested app per pattern — value help, tree, navigation, upload — so *has
Expand Down
4 changes: 2 additions & 2 deletions docs/advanced/linter.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ Two gates run over every file, and they answer different questions.

Everything the view writes is resolved against a **UI5 metadata snapshot** —
every control OpenUI5 ships, with its full member list and types, and every
enum, generated from the OpenUI5 sources. It is instant, needs no browser, and catches the whole
family of *this name does not exist* defects:
enum, generated from the OpenUI5 sources. It is instant, needs no browser, and
catches the whole family of *this name does not exist* defects:

| | |
| --- | --- |
Expand Down
7 changes: 4 additions & 3 deletions docs/advanced/vscode.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,10 @@ can help while the chain is being written rather than after it:
with run, preview and check on it: the list that says which thirty apps a
repository has.
- **Show Examples for this Control** — put the cursor on an `ele( )` call and
the [sample catalog](https://abap2ui5.github.io/playground/samples/) is searched for
working uses of that control, richest first, opening at the line. It reads the catalogs
from `abap2ui5.mcp.reposRoot`, so it needs those checkouts.
the [sample catalog](https://abap2ui5.github.io/playground/samples/) is
searched for working uses of that control, richest first, opening at the line.
It reads the catalogs from `abap2ui5.mcp.reposRoot`, so it needs those
checkouts.

### Starting from a template

Expand Down
3 changes: 2 additions & 1 deletion docs/cookbook/browser_interaction/url_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ samples:
---
# URL Handling

Working with URLs is common — reading parameters from the current URL, opening links in new tabs, or managing browser history.
Working with URLs is common — reading parameters from the current URL, opening
links in new tabs, or managing browser history.

## Read URL Parameters

Expand Down
6 changes: 4 additions & 2 deletions docs/get_started/hello_world.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Open the abap2UI5 startup page in your browser — the page the
[Quickstart](/get_started/quickstart#_3-first-launch) ends on — enter the class
name `ZCL_APP_HELLO_WORLD` in the input field, and launch it.

That is a complete abap2UI5 app: one class, one method, no frontend project and no OData service.
That is a complete abap2UI5 app: one class, one method, no frontend project and
no OData service.

## Starting an App by URL

Expand All @@ -51,7 +52,8 @@ roundtrip — is cataloged with symptom, cause and fix in
[Common Failures](/cookbook/troubleshooting/common_failures).

::: tip **Naming**
Name your own apps in your customer namespace (`Z...`/`Y...`). The `Z2UI5_` prefix is reserved for the framework and its samples.
Name your own apps in your customer namespace (`Z...`/`Y...`). The `Z2UI5_`
prefix is reserved for the framework and its samples.
:::

## A Real Screen, an Event and Data Exchange
Expand Down
Loading
Loading