Tabs are open documents: the tab row in Concepts, opening assets in Behaviors, Containers, Animations, Tiles, fixed plugin tabs, Architecture, check count 1085 (#171)
1 parent
ff256e8b92
commit
a95b3efbfe
8 files changed
+68
-44
No files matched your search
+2
-1
@@ -49,7 +49,8 @@ right half is the start of the way, the left half its end, a round half a slow o
|
||||
linear, a circle ease in-out, a half-round key ease in or ease out, a square a step. Player and
|
||||
preview evaluate it in one place, so both show the same curve.
|
||||
|
||||
**The Animation tab.** The header picks the animation, what it **animates** (the root), **+ Row**
|
||||
**The Animation tab.** Each animation opens in a tab of its own. The header picks what it
|
||||
**animates** (the root), **+ Row**
|
||||
(a dialog: the root or one of its objects, then a property of that; the inspector's
|
||||
*Change target…* reopens it to move a row's keys to another property of the same kind), **Play**
|
||||
/ **Pause** and **Fit**. Below the header the **preview** shows the root at the playhead, drawn
|
||||
|
||||
+16
-13
@@ -44,20 +44,19 @@
|
||||
measures from it — only what is drawn is shortened — and a subclass that puts something else
|
||||
inside the control takes its room off `labelSlot()`, so a long value is cut rather than run
|
||||
underneath the chevron. Given a `width`, a button is exactly that wide and truncates instead:
|
||||
the Behavior, Texture, Animation and Container pickers keep their size whatever they show. The
|
||||
tab row has no floor at all: it is label-wide at half the theme padding, and `TabStrip` drops
|
||||
the Animation root and Tile kind pickers keep their size whatever they show. The
|
||||
tab row has no floor at all: it is label-wide at half the theme padding, a document's name stops
|
||||
at 160 px (`DocumentTab`), and `TabStrip` drops
|
||||
what does not fit into an overflow menu rather than letting it spill (the rule is
|
||||
`tabRowLayout` in `editor/tabRow.ts`, so it is checkable without a browser; `__gm.tabRow()`
|
||||
reports what is in the row and what went away).
|
||||
reports what is in the row, what went away and the whole order).
|
||||
A button that can draw its label bold — on an accent fill: primary,
|
||||
danger, a toggled tab — is measured bold, or a dialog's `Delete` comes out as `Del…`.
|
||||
The padding is an inset rather than a claim, so a 24 px icon button spends it on the glyph
|
||||
instead of truncating it away; a button that switches between label forms (`↺` and `↺ ▾`) asks
|
||||
for `minWidth` and grows. `__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
|
||||
direction — a button too narrow to draw any of its label — and the `pickerWidth()` of
|
||||
`__gm.graph` / `__gm.texture` / `__gm.container` catch a picker that resizes itself around its
|
||||
content (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.
|
||||
- **PHP 8** JSON API (`api/`) storing one JSON file per project in `data/projects/`, with
|
||||
atomic writes and an integer revision that rejects stale saves (HTTP 409).
|
||||
@@ -104,7 +103,11 @@ src/editor/ App → Editor (layout + ticker), PlaySession (the run),
|
||||
fileImport (drop/paste) → assetImport (one upload pipeline, an ImportKind per sort) ← textureImport / fontImport / soundImport,
|
||||
FilePicker (the shared "+ → Texture / Font / Sound" dialog; ImagePicker/FontPicker/SoundPicker only describe their row),
|
||||
framing (fit/marquee/snap maths), FormDialog (the small modal forms: label column, Cancel + confirm, one open at a time), AssetTabEditor (the shared shell of the asset tabs), TabHeader (the bar across the top of a tab – asset tabs, Plugins tab, NodeBoard),
|
||||
TabStrip + tabRow (the tab row and what it does when the tabs outgrow it; plugin tabs are appended at run time)
|
||||
TabStrip + tabRow (the tab row and what it does when the tabs outgrow it; plugin tabs are inserted at run time),
|
||||
DocumentTab (a document's tab: kind, name, close cross), documents (#171: where a new document's tab goes, which one shows
|
||||
after a close, a tab going on to another document — pure, so checkable without a browser).
|
||||
One editor per kind serves every open document of that kind: `Editor` points it at the one on
|
||||
screen (`AssetTabEditor.showAsset`), and it keeps each document's view through `saveView`/`restoreView`
|
||||
src/editor/plugins/ startPlugins (list, import, host) → loader (PluginManager: order, checks, switching on and off, what became of each file), pluginApi (API v1, the per-plugin scope), PluginsPane (the Plugins tab), NodeBoard (api.ui)
|
||||
src/editor/viewport/ WorldViewport base → FreeViewport (a camera of its own: fit, pan, wheel zoom) → SceneViewport, PreviewCanvas;
|
||||
GameViewport follows the game's camera instead — editing tools, hence not in src/viewport/
|
||||
@@ -115,19 +118,19 @@ src/editor/inspector/ Inspector panel, host (what a section builds into), field
|
||||
src/editor/panels/ AssetPanel, assetMenu (the two groups of the + menu), HierarchyPanel, Toolbar,
|
||||
TilemapPanel (tools, tiles and layers of the one selected tilemap, beside the scene view, #142;
|
||||
painting itself is SceneViewport)
|
||||
src/editor/graph/ GraphEditor (header + picker), 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,
|
||||
NodeView, NodePalette + paletteEntries (which also ranks a search), folding (hidden links, collapsed nodes), Breadcrumb + trail (the path into nested behaviors), beads (the run's pulses),
|
||||
AnnotationView + annotationDrag (groups and comments: what a drag carries, each thing moved once)
|
||||
src/editor/container/ ContainerEditor (header + picker), ContainerCanvas (a SceneViewport on a template)
|
||||
src/editor/texture/ TextureEditor (header + picker), TextureCanvas (picture + frames), SliceDialog (a FormDialog), sheetView (the grid maths),
|
||||
src/editor/container/ ContainerEditor (header), ContainerCanvas (a SceneViewport on a template)
|
||||
src/editor/texture/ TextureEditor (header), TextureCanvas (picture + frames), SliceDialog (a FormDialog), sheetView (the grid maths),
|
||||
ImagePicker + imageGrid (the "+ → Texture" dialog), checker (the transparency board)
|
||||
src/editor/animation/ AnimationEditor (header + picker, preview over timeline), PreviewCanvas (the root at the playhead, a FreeViewport
|
||||
src/editor/animation/ AnimationEditor (header, preview over timeline), PreviewCanvas (the root at the playhead, a FreeViewport
|
||||
over animationPreview.ts), TimelineCanvas (rows, keys, ruler, clipboard), timelineView (its geometry),
|
||||
RowDialog + FramesDialog (FormDialogs)
|
||||
src/editor/tile/ TileEditor (header + picker + kind), TileCanvas (every tile of the block and a sample map, animated),
|
||||
src/editor/tile/ TileEditor (header + kind), TileCanvas (every tile of the block and a sample map, animated),
|
||||
PictureStrip (the pictures as animation steps; dropIndex is the reorder maths),
|
||||
TilePalette (the tiles as a grid of pictures in the tilemap panel, #139)
|
||||
src/editor/statemachine/ StateMachineEditor (header + picker, on the graph canvas)
|
||||
src/editor/statemachine/ StateMachineEditor (header, on the graph canvas)
|
||||
src/editor/particles/ ParticlesEditor (graph canvas left of a preview panel, splitter between them),
|
||||
ParticlePreview (the panel: the asset's own particles, wearing its own rows)
|
||||
src/editor/list/ ListEditor (a row per item), slot (which list is being edited, and how it is written back)
|
||||
|
||||
+8
-9
@@ -184,8 +184,9 @@ shown on the node and editable in the inspector.
|
||||
Three ways in and out of a call: **drag** a behavior out of the asset panel onto the graph and it
|
||||
lands as a call where you let go; **double-click** a call node to open the graph it runs. The
|
||||
way down stays above the graph as a **breadcrumb** (`Game > Move > Other`): click a crumb to go
|
||||
back there; the first one returns to where you started and the bar goes away. Opening a behavior
|
||||
any other way — the asset panel, the scene, the picker — ends the trail. And a call that cannot
|
||||
back there; the first one returns to where you started and the bar goes away. Following a call
|
||||
stays in the same tab, and the trail belongs to that tab: a behavior opened any other way comes
|
||||
up in a tab of its own, without a trail, and switching back brings the trail back with it. And a call that cannot
|
||||
do anything **says so on the node** — no behavior picked, the behavior deleted,
|
||||
or no start node in it. The last one is the one worth naming: without a start node there is no
|
||||
way in, so the call is skipped and the flow simply carries on at its `exec` output. A missing
|
||||
@@ -299,8 +300,7 @@ shown on the node and editable in the inspector.
|
||||
- All of it is drawing only: `groups` and `notes` on the graph, saved with the project, read by
|
||||
no interpreter. A graph that has neither stores neither array, so an old document means exactly
|
||||
what it says. Every graph tab has them — behaviors, state machines, shaders, particles.
|
||||
- **Header bar** (the same one the Texture and Container tabs have): the behavior picker,
|
||||
**Fit** (which zooms out far enough to hold every node, measuring each by its own drawn
|
||||
- **Header bar** (the same one the Texture and Container tabs have): **Fit** (which zooms out far enough to hold every node, measuring each by its own drawn
|
||||
height, **and every group and comment with them** — a fit that framed the nodes alone would cut
|
||||
the box drawn around them, and a graph holding nothing but a comment would frame the empty
|
||||
origin; `editor/framing.ts`), and the size of the graph as "N nodes · M links". It stops zooming
|
||||
@@ -310,11 +310,10 @@ shown on the node and editable in the inspector.
|
||||
switching it on never shuffles a graph that is already laid out, and a node nobody drags never
|
||||
moves. Every node of a selection snaps **for itself**, so dragging a group is how you line one
|
||||
up; the offset is a decision about rectangles and lives with the others in `editor/framing.ts`.
|
||||
- **Selection-following, not selection-bound**: selecting a behavior asset, a placed behavior
|
||||
or one of its nodes shows that graph; a selection about anything else (a sprite, nothing at
|
||||
all) leaves the tab on the behavior you were editing, because checking a sprite's position
|
||||
must not empty your work. Only a behavior that no longer exists is dropped, and then the tab
|
||||
falls back to another one — the picker is how you switch on purpose.
|
||||
- **One tab per behavior**: each behavior opens as a document of its own (double-click in the
|
||||
asset panel), several at once, each keeping its zoom and scroll for the session. A selection
|
||||
never changes what a tab shows; selecting a node of another graph brings that graph's tab up.
|
||||
A deleted behavior closes its tab.
|
||||
- **Live view**: Play always switches to the Game tab; switch to Behavior while playing and
|
||||
the exec edges and nodes that just ran light up — the same canvas draws editor and game, so
|
||||
this costs nothing extra. Each fire of an exec edge also sends a bead along it (0.3 s, at most
|
||||
|
||||
+31
-9
@@ -45,9 +45,10 @@ rate. The **Assets** panel's `+` creates an asset and opens its tab; its menu li
|
||||
two alphabetical groups, first what can be placed into a scene, then what only lives in the library
|
||||
(#43) — the line between them answers the question that follows right after "which type?".
|
||||
A click on a row only
|
||||
selects (the inspector shows it, no tab moves); a **double-click** opens the tab the asset lives
|
||||
in — Scene, Behavior, State machine, Shader, Particles, Texture, Animation or Container — and on a scene
|
||||
also makes it the active one. `Enter` does the same once the list has keyboard focus (`Tab`).
|
||||
selects (the inspector shows it, no tab moves); a **double-click** opens the asset as a document
|
||||
in a tab of its own (see **The tab row**) — a behavior, state machine, shader, particles,
|
||||
texture, animation, tile or container — and on a scene switches to the Scene tab and makes it the
|
||||
active one. `Enter` does the same once the list has keyboard focus (`Tab`).
|
||||
A sprite, camera, text or collider has no tab of its own, so a double-click on one does nothing.
|
||||
Right-clicking a row offers *Set as active scene*, *Set as start scene* / *Clear start scene*,
|
||||
*Create sprite from texture*, *Create animation from texture* and *Delete…*. In
|
||||
@@ -57,12 +58,33 @@ The asset panel and **Scene objects** share the left column; the divider between
|
||||
the height from one to the other, and dragged all the way either keeps its header. The split is a
|
||||
share of the column, so a taller or shorter window keeps it.
|
||||
|
||||
**The tab row.** The centre column carries one button per tab. When the column is too narrow to
|
||||
hold them all — a small window, or the side panels dragged wide — the ones that do not fit move
|
||||
behind a `»` button at the end of the row, which lists them by name; picking one opens it and
|
||||
brings it into the row. The tab you are editing is always in the row: if it has to, the row gives
|
||||
up every other tab to keep it there. Nothing is ever drawn past the column's edge, where it would
|
||||
sit under the inspector and take clicks meant for it.
|
||||
**The tab row.** A tab is an **open document**, as in an IDE (#171). **Scene**, **Game** and
|
||||
**Plugins** are fixed and cannot be closed; a plugin's own tab follows them for as long as the
|
||||
plugin is on. Everything else opens as a document: double-click (or `Enter`) an asset in the
|
||||
asset panel, create one, or open a list from its row. An open document only comes up again, a new
|
||||
one gets its tab right of the document on screen (or last, from a fixed tab). Several assets of
|
||||
one kind can be open at once — two textures, three behaviors — each with its own tab.
|
||||
|
||||
- The tab reads the kind in muted type, then the name: `Behavior · Player`, `Texture · Player`,
|
||||
so assets of the same name stay apart. A long name is cut with an ellipsis. Renaming an asset
|
||||
renames its tab.
|
||||
- A document tab always shows its own asset. Selecting something elsewhere never changes it; there
|
||||
is no picker in the tab's header any more. Selecting what lives only inside a document — a node,
|
||||
a frame, an object of a container — brings that document up.
|
||||
- The **close cross** at the end of the tab, or a **middle click**, closes it; nothing is lost, every edit is in the
|
||||
project already. Closing the tab on screen shows its left neighbour (the right one when it was
|
||||
first), the Scene tab when none is left. Deleting an asset closes its tab.
|
||||
- Each document keeps its **zoom and scroll** for the session: switch between two textures and
|
||||
both are where you left them. Following a call node into another behavior stays in the same
|
||||
tab, and the breadcrumb stays with that tab.
|
||||
- Open tabs are not remembered: a loaded project starts on the Scene tab.
|
||||
|
||||
When the column is too narrow to hold every tab — a small window, many documents, or the side
|
||||
panels dragged wide — the ones that do not fit move behind a `»` button at the end of the row,
|
||||
which lists them by kind and name; picking one opens it and brings it into the row. The tab you
|
||||
are editing is always in the row: if it has to, the row gives up every other tab to keep it there.
|
||||
Nothing is ever drawn past the column's edge, where it would sit under the inspector and take
|
||||
clicks meant for it.
|
||||
|
||||
**The console.** Under whichever centre tab is showing sits the **Console** panel, always
|
||||
visible and resized by the divider above it — dragged all the way shut it keeps its own header
|
||||
|
||||
+6
-7
@@ -11,15 +11,14 @@ the template, with the same drag-to-parent, context menu and folding as a scene;
|
||||
a scene asset and a container asset are both `ObjectHolder`s, and every object command takes a
|
||||
`holderId` that is either one.
|
||||
|
||||
**Header bar**: the container picker (searchable), **Fit** (frames the content, origin always
|
||||
included) and the size of the template as "N objects". Placing needs no button of its own: an
|
||||
**Header bar**: **Fit** (frames the content, origin always included) and the size of the
|
||||
template as "N objects". Placing needs no button of its own: an
|
||||
asset dragged from the panel onto this view goes into the template, and a container dragged onto
|
||||
its own template is refused while it hovers rather than at the drop.
|
||||
Like the Behavior tab it follows the selection without being bound to it — the container asset, one of
|
||||
its placed copies or one of its template objects shows it, anything else leaves the tab on what you
|
||||
were editing, and only a deleted container makes it fall back to another one. Selecting a template
|
||||
*object* also switches to the tab, the rule a graph node and a texture frame already follow: it
|
||||
exists in no other view. Selecting the asset itself does not; double-clicking it does.
|
||||
Each container opens as a document in a tab of its own (see *The tab row* in
|
||||
[Concepts](Concepts)); several can be open at once. Selecting a template *object* brings its
|
||||
container's tab up, the rule a graph node and a texture frame already follow: it exists in no other
|
||||
view. Selecting the asset itself does not; double-clicking it does.
|
||||
|
||||
Placing a container anywhere creates **one** object. Its content is never copied: `resolveObjects`
|
||||
(`src/core/model/instance.ts`) weaves the template in whenever the holder is read, one level per
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
|
||||
# code is lost (tail's is reported), so a red run reads as green
|
||||
npm run build # writes dist/ and regenerates index.html
|
||||
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1076 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1085 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
|
||||
./e2e.sh start [GM_STEPS] # the same run detached, see below
|
||||
./e2e.sh status # live progress, or the last run's result
|
||||
@@ -64,7 +64,7 @@ The e2e run is a list of steps in `tools/e2e/` (hierarchy, inspector, play, asse
|
||||
graph, parenting, colliders, containers, text/enabled, menus, textures, frames, overflow, aspect,
|
||||
container tab, run-in-editor, pause, nested behaviors, key repeat, list tab, struct variable,
|
||||
variable nodes, property nodes, animations, state machines, console, shaders, transforms, fonts,
|
||||
particles, shared scope, render on change, sounds, load scene, graph groups, nine-slice, plugins, tiles, tilemaps) that build on each other's
|
||||
particles, shared scope, render on change, sounds, load scene, graph groups, nine-slice, plugins, tiles, tilemaps, physics, shapes, queries, documents) that build on each other's
|
||||
scene state and share the CDP driver in `tools/lib/harness.mjs` — `press`, `undo`, `play`,
|
||||
`object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it
|
||||
exercises.
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ A switch takes effect at once, without reloading the editor:
|
||||
| `api.version` | `1` |
|
||||
| `api.project.assets()` | The assets of the open project in list order, as `{ id, name, type }` – copies, so writing to them changes nothing. |
|
||||
| `api.project.on('change', fn)` | Calls `fn` whenever the asset list changes: an asset created, renamed or deleted, by hand, by undo or by loading another project. A change that leaves ids, names and types alone (moving an object, editing a property) does not call it. Returns a function that unsubscribes. A listener that throws is reported and stays subscribed. Any event but `'change'` throws. |
|
||||
| `api.tabs.add({ id, label, create })` | Adds a tab after the built-in ones. `create()` is called once and must return a widget from `api.ui`. Returns `false`, with a console warning, when the id is taken – by a built-in tab (`scene`, `graph`, …) or a plugin that came earlier; `create` is not called then. A missing `id` or `label`, or a `create` that is not a function, throws. |
|
||||
| `api.tabs.add({ id, label, create })` | Adds a tab after the fixed ones (Scene, Game, Plugins), ahead of the open documents; it stays for as long as the plugin is on. `create()` is called once and must return a widget from `api.ui`. Returns `false`, with a console warning, when the id is taken – by a built-in tab (`scene`, `graph`, …) or a plugin that came earlier; `create` is not called then. A missing `id` or `label`, or a `create` that is not a function, throws. |
|
||||
| `new api.ui.NodeBoard()` | A read-only node graph: `setNodes([{ id, title, x, y }])` replaces what it shows, `fit()` frames it. It has a **Fit** button and a node count above the canvas; a drag with any button pans, the wheel zooms about the pointer. The nodes are drawn by the graph editor's own node view, empty but for their title – no ports, no editing, no selection. The first `setNodes` with anything in it frames the nodes once the tab has a size; after that the view stays where the user put it. A title that is not text is empty, a position that is not a number is 0, and something that is not a list throws. |
|
||||
|
||||
## The demo plugin
|
||||
|
||||
+2
-2
@@ -76,8 +76,8 @@ Kind, rate, playback, the solid switch and the collision scale are ordinary prop
|
||||
|
||||
## The tab
|
||||
|
||||
- The **picker** in the header chooses the tile, the one beside it the kind, **Fit** frames the
|
||||
board. The readout on the right says kind, tile size, pictures, rate and how many tiles the block
|
||||
- Each tile opens in a tab of its own. The **picker** in the header chooses the kind, **Fit**
|
||||
frames the board. The readout on the right says kind, tile size, pictures, rate and how many tiles the block
|
||||
produces — or *not usable yet* with the reason written under the board.
|
||||
- The **board** shows every tile the block can produce, captioned with the neighbours that ask for
|
||||
it: `NESW-nw` is "joined on all four sides, with a notch towards the north-west", `·` is a lone
|
||||
|
||||
Reference in new issue
Block a user