Describe the 500 weight and the SVG icons (#214)

- Architecture: fonts load 400, 500, 600 and 700. Icons, markIcons and
  the chevron are described.
- Development: tests find icons through __gm.locateIcons(id).
- Behaviors, Scenes, Sounds: kind, scene and sound marks are described
  instead of written as glyphs.
- Development: editor-suite check sum 1312.
admin committed 2026-09-23 08:13:41 +02:00
1 parent db8b6428d3
commit fa9e6a5540
5 files changed
+28 -11

No files matched your search

+10 -4
@@ -59,9 +59,10 @@
`labelToken()` and `measureToken()`, which a subclass overrides (`TabButton` draws and measures `labelToken()` and `measureToken()`, which a subclass overrides (`TabButton` draws and measures
plain). The box behind the label is `drawBackground()`, likewise overridable: `TabButton` paints plain). The box behind the label is `drawBackground()`, likewise overridable: `TabButton` paints
an index card there, rounded above and square below (#176). an index card there, rounded above and square below (#176).
The padding is an inset rather than a claim, so a 24 px icon button spends it on the glyph The padding is an inset rather than a claim, so a 24 px icon button spends it on the icon
instead of truncating it away; a button that switches between label forms (`↺` and `↺ ▾`) asks instead of truncating it away; a row action that switches between its plain and its menu form
for `minWidth` and grows. `__gm.textOverflows()` guards the whole class of bug: it reports any (the reset picture alone, or with a chevron) asks for `minWidth` and grows by the chevron's room
(`trailingRoom()`, which `Dropdown` uses for its chevron too). `__gm.textOverflows()` guards the whole class of bug: it reports any
visible text drawn wider than the widget owning it, `__gm.labelsDropped()` guards the other visible text drawn wider than the widget owning it, `__gm.labelsDropped()` guards the other
direction — a button too narrow to draw any of its label (e2e step 14). direction — a button too narrow to draw any of its label (e2e step 14).
- **Vite + TypeScript**, **Vitest** for core, toolkit and editor logic. - **Vite + TypeScript**, **Vitest** for core, toolkit and editor logic.
@@ -106,7 +107,10 @@ src/runtime/physics/ engine (loads Rapier once, reports its state, converts be
normal, impulse and point; whether a dynamic body lands on or passes a one-way collider, normal, impulse and point; whether a dynamic body lands on or passes a one-way collider,
decided before each step for the contact hook; the push a `Move` gives a dynamic body, and decided before each step for the contact hook; the push a `Move` gives a dynamic body, and
what the physics nodes reach through `NodeContext.physics`) what the physics nodes reach through `NodeContext.physics`)
src/ui/ widget toolkit (generic, knows nothing about assets); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, kept apart so it is measured without a canvas) src/ui/ widget toolkit (generic, knows nothing about assets); fonts (the faces the editor ships:
Source Sans 3 in 400, 500, 600 and 700, Source Code Pro 400, Latin only, #202/#214), icons +
Icon (the menu's tick boxes and current-entry dot as SVG files, and a widget that shows one file
on its own: a symbol the Latin subset lacks is never written as a character, #214); SortableList + sortable (the layer list of every editor that has one: rows dragged into order, eye, duplicate, cross, rename in place; the index arithmetic of a list shown upside down, kept apart so it is measured without a canvas)
src/viewport/ view (transforms), items (what to draw), symbols (how to draw it), src/viewport/ view (transforms), items (what to draw), symbols (how to draw it),
symbolIcons (the SVG files in icons/, parsed once, in the theme's colours, #175), symbolIcons (the SVG files in icons/, parsed once, in the theme's colours, #175),
FileLibrary (load once, never retry a failure, tell the listeners) ← TextureLibrary, FontLibrary; FileLibrary (load once, never retry a failure, tell the listeners) ← TextureLibrary, FontLibrary;
@@ -169,6 +173,8 @@ src/editor/panels/ AssetPanel, assetGroups (groups by type and their order,
assetMenu (the two groups of the + menu), HierarchyPanel, assetMenu (the two groups of the + menu), HierarchyPanel,
Toolbar + toolbarIcons (five icon buttons, no captions: menu, undo, redo, play, pause, #183), Toolbar + toolbarIcons (five icon buttons, no captions: menu, undo, redo, play, pause, #183),
headerIcons (the pictures of the tab headers' switches: the magnet, #194), headerIcons (the pictures of the tab headers' switches: the magnet, #194),
markIcons (the kind marks, the row actions reset, apply and the two fit arrows, and a
transition's in/out arrow — they were characters the bundled font does not have, #214),
TilemapPanel (tools, tiles and layers of the one selected tilemap, beside the scene view, #142; TilemapPanel (tools, tiles and layers of the one selected tilemap, beside the scene view, #142;
painting itself is SceneViewport) painting itself is SceneViewport)
src/editor/graph/ GraphEditor (header, breadcrumb), GraphCanvas, graphView (zoom limits, grid, panAndZoom — shared with NodeBoard and the Tile tab's TileCanvas), EdgeLayer (curves and fold marks), graphMenus, src/editor/graph/ GraphEditor (header, breadcrumb), GraphCanvas, graphView (zoom limits, grid, panAndZoom — shared with NodeBoard and the Tile tab's TileCanvas), EdgeLayer (curves and fold marks), graphMenus,
+3 -2
@@ -52,8 +52,9 @@ shown on the node and editable in the inspector.
because no tab may reach into the `Editor` to switch itself on. An **unconnected `list` input** because no tab may reach into the `Editor` to switch itself on. An **unconnected `list` input**
of a node is the same button and the same tab: its inline value lives in the very same params of a node is the same button and the same tab: its inline value lives in the very same params
bag a setting does, and the slot stops resolving the moment an edge feeds the port. What an item holds bag a setting does, and the slot stops resolving the moment an edge feeds the port. What an item holds
is **its own** and is chosen there: the **kind mark** in front of a row (`#`, `Aa`, `✓`, `◧`, `▦`, `⊞`) is **its own** and is chosen there: the **kind mark** in front of a row (`#` for a number, `Aa` for a text, a
is the picker — a click opens the menu, with no chevron, since the mark has to stay as narrow as small picture for every other kind: a tick for a boolean, a half-filled square for a color, a grid
for a texture, a square with a cross for a struct) is the picker — a click opens the menu, with no chevron, since the mark has to stay as narrow as
its column — and `+ Item ▾` asks for the kind of the one it appends. Because the kind lives in the its column — and `+ Item ▾` asks for the kind of the one it appends. Because the kind lives in the
value, every such change is an ordinary **value edit**: one command, one undo step, and no value, every such change is an ordinary **value edit**: one command, one undo step, and no
neighbouring item touched. A **struct item** carries its own shape the same way: its fields are neighbouring item touched. A **struct item** carries its own shape the same way: its fields are
+11 -1
@@ -13,7 +13,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
npm run build # writes dist/ and regenerates index.html npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output ./build.sh # the same under `set -o pipefail`, for callers that pipe the output
php tools/make-user.php <name> <password> # an account; every tool signs in (see Accounts) php tools/make-user.php <name> <password> # an account; every tool signs in (see Accounts)
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1310 editor checks, screenshots in /tmp/gm-e2e-*.png node tools/seed-e2e.mjs && node tools/e2e.mjs # 1312 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result ./e2e.sh status # live progress, or the last run's result
@@ -121,6 +121,16 @@ the whole answer there, and no colour check can see an order.
The node palette has the same property, and `__gm.graph.paletteRows()` is its witness: the rows all The node palette has the same property, and `__gm.graph.paletteRows()` is its witness: the rows all
look alike, only their sequence says whether the search ranked them (#43). look alike, only their sequence says whether the search ranked them (#43).
**A symbol is a picture, so a step finds it as one** (#214). The reset and apply buttons, the fit
arrows, the kind marks, the tick boxes and the scene marks used to be characters, and `locate`
found them by that character. They are SVG files now, because the Latin font the editor ships has none of those glyphs.
A file that carries an `id` names its graphics `icon:<id>`, and `__gm.locateIcons(id)` lists every
such picture on screen as `{ x, y, menu }`: `menu` says whether the button it sits on opens a menu.
In a step, `t.findIcon(id, menu)` and `t.clickIcon(id, menu)` do the same as `find` and `clickText`.
A menu's tick boxes are read through `t.menuTicked()` as `{ label, ticked }`. The unit test
`tests/ui/fonts.test.ts` reads the character map straight out of the font files and fails on any
character in `src/` that one of the shipped faces lacks.
**What no colour check can see: whether anything was drawn at all.** `__gm.pixelAt` re-renders the **What no colour check can see: whether anything was drawn at all.** `__gm.pixelAt` re-renders the
stage on demand, so it answers correctly on a screen that has been frozen for a minute — which is stage on demand, so it answers correctly on a screen that has been frozen for a minute — which is
exactly the failure mode of drawing on change instead of every frame (see *Architecture*). The exactly the failure mode of drawing on change instead of every frame (see *Architecture*). The
+3 -3
@@ -6,13 +6,13 @@ Those are three different questions, and this page is about keeping them apart.
The **active scene** is what the editor shows: the Scene tab draws it, the hierarchy lists it, a The **active scene** is what the editor shows: the Scene tab draws it, the hierarchy lists it, a
dropped asset lands in it. It is set by double-clicking a scene in the Assets panel, or through dropped asset lands in it. It is set by double-clicking a scene in the Assets panel, or through
*Set as active scene* in its context menu — the toolbar has no scene picker since #183. `●` marks *Set as active scene* in its context menu — the toolbar has no scene picker since #183. A dot marks
it in the list. Both ways stay open while a run is on: they only move the editor's view and never it in the list. Both ways stay open while a run is on: they only move the editor's view and never
touch the running world. touch the running world.
The **start scene** is where a run begins — the title screen, the first level, whatever a player The **start scene** is where a run begins — the title screen, the first level, whatever a player
would see first. It is named on the **project**, not on the scene asset: right-click a scene in would see first. It is named on the **project**, not on the scene asset: right-click a scene in
the Assets panel → **Set as start scene**. `▶` marks it, and the same place offers **Clear start the Assets panel → **Set as start scene**. The play triangle marks it, and the same place offers **Clear start
scene** to take the name back. A project that never names one has none, and then the two questions scene** to take the name back. A project that never names one has none, and then the two questions
have the same answer. have the same answer.
@@ -29,7 +29,7 @@ It stands in the settings dialog beside the project name and the collision layer
belong to the project — the dialog is the one place these are edited, not the split it says. belong to the project — the dialog is the one place these are edited, not the split it says.
It is captioned with the mode it stands in — *Start scene* or *Active scene*, worded exactly like It is captioned with the mode it stands in — *Start scene* or *Active scene*, worded exactly like
the entries of its own menu, so that what you picked and what you read back are the same words. the entries of its own menu, so that what you picked and what you read back are the same words.
The start scene is marked `▶` in the Assets panel, the active one `●`. The start scene is marked with the play triangle in the Assets panel, the active one with a dot.
The point of the two modes is that a game with a title screen is otherwise tedious to work on: The point of the two modes is that a game with a title screen is otherwise tedious to work on:
every run would begin at the title, three clicks away from the level you are editing. *Active every run would begin at the title, three clicks away from the level you are editing. *Active
+1 -1
@@ -9,7 +9,7 @@ file that fails does not stop the rest. The asset is named after the file ("jump
an `.m4a` as `audio/mp4`, `audio/x-m4a` or as nothing at all. an `.m4a` as `audio/mp4`, `audio/x-m4a` or as nothing at all.
**+ → Sound** asks the other question and opens the picker: **every sound the server already **+ → Sound** asks the other question and opens the picker: **every sound the server already
holds**, one row each. A sound has nothing to show, so the row carries a **▶** at its left that holds**, one row each. A sound has nothing to show, so the row carries a **play triangle** at its left that
plays it — pressing that is a preview and picks nothing. Picking a row and pressing **Use sound** plays it — pressing that is a preview and picks nothing. Picking a row and pressing **Use sound**
makes the asset; **Upload…** is the way in for a file the server does not have yet. A row makes the asset; **Upload…** is the way in for a file the server does not have yet. A row
captioned with an asset name is one this project already uses, and picking it uses that asset captioned with an asset name is one this project already uses, and picking it uses that asset