f177ee90a45a0a39bdff149272e2fb7754a553f3
13
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2c19c89878 |
Let variables, fields, ports and list items be dragged into order (#310)
Variables of a behavior, the fields of a struct, the ports of a start or end node and the items of a list could only be added and removed. Changing their order meant removing and adding them again. Now each of them is dragged by the mark in front of its row. - sortBlocks (ui/blockDrag.ts) makes the rows of one column sortable. A block is every row it covers, so a struct moves with its fields and a list item with its nested list. The block is dimmed while it is carried, an accent line shows where it lands, and a scrolled panel follows the pointer at its edge. Less than 4 px stays a click. - The arithmetic is pure and lives in ui/sortable.ts: blockGap, gapLineY, dropSlot and edgeScroll. dropIndex of the tile strip moved there too, so the reorder maths sits in one file. - MoveVariableCommand, MoveStructFieldCommand and MovePortCommand build on the existing MoveCommand. Undo moves back. - Lists need no command. InlineList replaces the list as a whole, as it does for every other edit of its items. - The grip is the kind mark, for a list item its number. Moving only happens among siblings: a field stays in its struct. - On a Struct asset the move runs through WithStructTypes. Typed variables, Pack/Expand ports and split outputs follow the new order. - Values and edges hang on names, so only the order changes. Nodes running a behavior read their ports from the start node, the new order reaches them without a copy. - Not sortable by design: the variables of a placed object, the fields of a typed struct (moved on the Struct asset) and the picked ports of Variables/Properties nodes. - Test hook __gm.inspectorMark(label) finds the mark of a row. Tests: - Gate on melektaus-atx: 235 files, 3572 tests, CHECK EXIT 0 (+9). - tests/core/reorder.test.ts: a variable, a field two levels deep, a Struct asset reaching a typed variable and Pack, and start ports reaching the calling node with edges and result kept. - e2e 22: ALL PASSED, 39 checks (+4, sum 1739). 22f-iii drags a variable past a struct of several rows, then a field inside it. - Counter-probes: dropSlot by top edge instead of middle and a field move that ignores its path fail 3 unit tests. With sortBlocks switched off, the three order checks of 22f-iii fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cf6043a066 |
Show a wheel step or Fit as a short zoom transition (#309)
The wheel and the Fit button used to jump to the new view. Now the view takes a short way there, 200 ms by default, everywhere the editor zooms. The zoom steps stay the same, only the change is shown. - viewTween: tweenView is the way between two views as a pure function. The scale moves by equal ratios, and the world point that sits at the same place on screen in both views stays there. After a wheel step that is the point under the pointer. - ViewTween drives it on the ticker. A surface hands it reading, writing and drawing its own view and wraps its wheel and Fit code in change(). A step during a transition starts from where that one was heading, so quick steps add up to exactly as many steps. - Anything else that moves the view ends the transition. A pan jumps to the target first, and saving a view saves the target. - Wired into FreeViewport (Scene, Container, animation preview), SheetCanvas (Texture, Curve, Pixel art, Vector art), GraphCanvas, panAndZoom (Tile board, plugin node board) and the timeline. - Menu > General settings... sets the time in ms, 0 jumps at once. It is kept in this browser like the panel sizes, not in the project, and it is not an undo step. - A document that opens still comes up framed at once. The animation preview frames a new subject itself instead of calling fit(). - The pixel art readout names the step the view is heading for. Measured in the scene view at 200 ms on melektaus-atx, where every frame of the transition is a full redraw: - 500 sprites: redraw 6.7 ms, 16 frames, mean 18 ms - 1000 sprites: redraw 13.0 ms, 9 frames, mean 27 ms - 2000 sprites: redraw 32.2 ms, 5 frames, mean 58 ms Tests: - The harness sets 0 ms on every load, so the existing modules read the step and not the way. - e2e 46i samples one wheel step at 1000 ms a frame later: zoom 1.033 between 1 and 2, the point under the pointer kept at (134, -40). It lands on the view a 0 ms jump reaches. - e2e 46 and 49 expect the new menu entry. - Gate (atx): 234 files, 3563 tests, CHECK EXIT 0 (+15) - e2e 46,49 (atx): ALL PASSED, 43 checks (+3, sum 1735) - Three counter-checks fail view-tween.test.ts as expected. e2e 46i fails when the scene view does not carry its centre along. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
32a0af6e2c |
Put the text and boxes of the interface on device pixels
On a screen scaled to 125 % or 150 % the editor looked soft beside a page the browser draws itself. The layout counts in whole CSS pixels, and most of those fall between two device pixels there. The GPU averages both: text loses its edges, and a 1 px line becomes a pixel and a half of mixed colour. Now text and boxes are rounded to device pixels, and a line is one device pixel wide. - ui/pixelGrid.ts holds the rule. crispText() makes a text with Pixi's roundPixels, crisp() does the same for a Graphics of upright boxes, and hairline() is the width of one device pixel. - Every text of the interface is made with crispText() instead of new Text(). - crisp() only rounds where the scale is not a whole number. At 100 % and 200 % nothing changes. - drawControlBox, drawHeaderBar and drawSectionBand draw their lines a hairline wide and flush with the edge. The widgets that draw lines of their own do the same. - Two bands share exactly the line they draw, and a column still ends on a whole pixel. - The stage is nudged by 1/32 px where the scale is not whole. At 150 % every odd pixel is an exact half, and without the nudge one band came out a row shorter than its neighbours. - Curves, icons and everything of the game stay unrounded. Rounding their points would bend them. - The harness takes deviceScaleFactor as an option, and the hooks deviceColumn, edgeContrast and pixelGrid read the frame as the screen gets it. Not in here: the subpixel smoothing a browser gives its own text. Pixi draws a text into a transparent picture first, and there only greyscale smoothing is possible. Measured at 150 %, edge contrast before and after: top bar 31.5 to 47.8, the lists on the left 68.6 to 86.4, inspector 27.6 to 38.4. At 100 % it is 46.5, 90.1 and 37.9, and the picture there is the same as before pixel for pixel, apart from the fps display. Tests: - Gate on melektaus-atx: 206 files, 3114 tests (3104 + 10), exit 0 - Full run on melektaus-atx: 1655 of 1656 checks passed, smokes 24/19/21. The one that failed was a condition of the new step 62 itself: it scrolled an inspector that had nothing to scroll. The step now makes the window lower for that reading. - e2e 61,62 after that fix: 14 checks. The full run was not repeated, and CI is switched off for now. - Ten unit tests, nine in tests/ui/pixel-grid.test.ts - Eight e2e checks in the new step 62, which runs at 150 % - Eleven unit counter-probes fail tests - Four browser counter-probes fail their checks. They ran before the fix to step 62 and were not repeated for it. - Screenshots looked at for 125 % and 150 % Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bc688f3efc |
Refactoring round 7: split BehaviorSystem, fewer copies (#85)
Recurring cleanup round over the code since round 6 (
|
||
|
|
021a6dcb56 |
Zoom the tile tab in the same steps as the scene view
The tile tab zoomed freely, so the pixels of a tile could end up
unevenly wide at odd factors. Scene view, container tab and animation
preview already snap to FREE_ZOOM_STEPS. The tile tab now does the
same, so every pixel editor zooms alike.
- The wheel steps through FREE_ZOOM_STEPS, counted in device pixels.
The board position is rounded to whole device pixels.
- New wheelSteps() in src/viewport/view.ts turns wheel events into
steps: one notch is one step, a trackpad gathers 50 px per step.
FreeViewport uses it too, so the rule exists only once.
- Fit takes the largest step at which the whole board fits. Refitting
on a new tab or board size stays as before.
- panAndZoom takes an optional zoom rule instead of limits. Graph and
node board keep zooming freely.
- A saved view from before the steps snaps to the nearest step.
- The cap parameter of frameBoxes from
|
||
|
|
4027afc228 |
Let Fit fill the tile tab instead of stopping at 100 percent
Fit in the tile tab left the board small in the middle of the tab. It used frameBoxes, which never zooms past 1 so that two graph nodes do not blow up to fill the screen. The tile board is measured in image pixels, though, and with 16 px tiles it is only about 345 x 186 px. - frameBoxes takes a new cap parameter, default 1. The graph and the node board keep their behaviour. - The tile tab passes its zoom limit of 6, so Fit fills the tab up to 600 percent. - TileCanvas remembers whether the view is still the one Fit set. While it is, a change in the size of the tab or of the board fits again. After a pan or zoom by hand the view stays until Fit. Found on the way: switching the image to another tile size (48 to 16 px) kept the old zoom, and the board shrank to a third. The refit above covers that too. Tests: - ./check.sh: 161 files, 2370 tests, CHECK EXIT 0 (was 2369). - New unit test in graph-framing: with a raised cap Fit fills the view up to the limit. The graph test for "not past 100 %" holds. - e2e 39: ALL PASSED, 31 checks (was 29). Fit fills the tab at 48 px (zoom 0.41) and fits again after the switch to 16 px (zoom 1.24). - Counter probes: a cap of 1 and no refit on a new board size each fail exactly the 16 px check (zoom 1 and zoom 0.41). - e2e sum in the README is 1384, computed, not yet seen in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
af6ac12ab5 |
Show sharp tile preview labels and paths with tunnels and crossings
The mask labels under the tile preview sat inside the zoomed board. Pixi rendered them at 10 px and stretched them with the zoom, and each label was shrunk to the width of its tile on its own. They came out blurry and in different sizes. The example map also only showed a ring, so narrow paths and crossings were never previewed. - The labels live in their own layer in screen space and are re-rendered at the fitting font size on every zoom. - All labels share one size, at most the 10 px of the theme. Below 6 px they are hidden. - A label has two lines: the sides on top, the corners dimmed below. - The example map keeps the ring cell for cell and adds a network of 1 tile wide paths below it. It has straight 1xN and Nx1 pieces, curves, two T junctions, a crossing and a dead end. - The map stands next to the grid in a wide panel and below it in a tall one. Fit picks the layout with the larger tiles, and only Fit changes it, so zooming does not make the board jump. - The new hook __gm.tile.captions() reports size, scale and lines. Found on the way: the taller map made Fit zoom out further, and 3 pixel checks on the 16 px terrain tiles failed. The side by side layout fixed that. Tests: - ./check.sh: 160 files, 2364 tests, CHECK EXIT 0 (was 2363). - e2e 39: ALL PASSED, 29 checks (was 28). - New unit test: the example map has straights, a curve, a crossing with four notched corners, a T junction and a dead end. - Counter check: with the labels back in the zoomed board the new caption check fails (scales [0.648, 1]). - The e2e sum in the README is now 1381. It is calculated only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0e8585665a |
Load the 500 weight and draw missing glyphs as SVG icons (#214)
The theme asks for Source Sans 3 at weight 500, but fonts.ts only
loaded 400, 600 and 700. The browser synthesised the 500 text. The
issue comment also asked for the symbols the font lacks to become SVG
icons. A scan of src/ against the font's cmap found 20 such characters
in 30 places.
- fonts.ts loads source-sans-3-latin-500-normal.woff2 as well.
- Menus draw their ticks and the "current" dot as icons. The dot is a
new MenuItem.current field and no longer part of the label text.
- Inspector action buttons (reset, apply, fit width/height) are
icons. The menu arrow behind them is the drawn Dropdown chevron.
- Kind marks come from a table in markIcons.ts. Picture kinds use an
icon, # and Aa stay text because the font has them.
- Scene marks in the asset panel, sound play/stop and the transition
arrows are icons too.
- Apply is an icon although the font has the arrow. It sits next to
the reset icon and would look different as a glyph.
- A new Icon widget draws on redraw like Button, so Node tests need no
DOMParser.
- Tests find icons through the SVG id. The new hook
__gm.locateIcons(id) and the harness helpers findIcon/clickIcon
replace locate() on the old characters.
Findings:
- Weight 700 is loaded but the theme never uses it. Left as is.
- The old scene-mark check in module 35 accepted any play glyph on
screen. It now checks the mark on the start scene's row.
- Module 44 needs the textures from module 37 and fails without it.
Tests:
- ./check.sh: 145 files, 2187 tests, exit 0 (before 144 / 2143).
- New tests/ui/fonts.test.ts: every theme weight is loaded, and every
character in src/ exists in every shipped face. The cmap reader
matches fontTools (231 characters).
- Icon tests cover the 19 new files. Each kind mark is either an
icon or text, never both.
- e2e 1-16,22,30,34,35,43,44: 586 green, 0 FAIL, then 44 stopped
for lack of module 37. e2e 1-16,37,44,47,48: 618 green.
e2e 1-14 on the final build: 454 green.
- Two new e2e checks: loaded weights in 14, a reset icon pixel in 04.
- Counter-probes all fail as expected: 500 removed, a play glyph put
back, the struct icon removed (one unit test each). In the browser,
500 removed and reset icon hidden fail exactly the two new checks.
- Editor-suite check sum 1312 (1310 measured by CI at
|
||
|
|
6a2b06dd4b |
Texture fills show in the vector tab as soon as they load (#196)
After signing out and back in, a texture fill stayed in its base colour until something else redrew the vector tab. The texture library is a singleton that outlives the editor. Four widgets subscribed to it and never unsubscribed. After a new sign-in the first loaded texture called a destroyed canvas, which threw and stopped the loop before the vector tab heard about the image. - TextureCanvas, TileCanvas, ShaderPreview and WorldViewport now unsubscribe in destroy(). This is the pattern FontPreview and VectorArtCanvas already use. WorldViewport leaves both the texture and the font library. - FileLibrary.notifyChange catches errors per listener. A broken listener no longer starves the others. The error is still logged as "[textures] a listener failed". - e2e 47i no longer forces a redraw. It waits up to 3 s for the tab to show the texture on its own. Findings: - The trigger was e2e module 45 (sign out, sign in), not the texture fill. My note in #196 that the browser showed no exception was wrong. The counter-check shows it clearly. Tests: - ./check.sh: 137 files, 2051 tests, exit 0 (before 136 / 2049) - 2 new unit tests in tests/viewport/file-library.test.ts - GM_STEPS=45,47 after a fresh seed: ALL PASSED, 71 checks - Counter-check with the old source and the new check: 1 FAIL (shape stays #0000ff) plus the TypeError. Old FileLibrary.ts fails the first unit test. - No full e2e run locally. The check count is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cc285af10a |
Tabs are open documents: Scene, Game and Plugins stay as fixed tabs, every other asset opens in its own tab (double click or Enter in the asset panel, on creation, from the list button), several assets of one kind can be open at once, a tab is labelled with its kind dimmed before the name, cut at 160 px and renamed with its asset, closes with its x or a middle click and goes away when its asset is deleted; a tab always shows its own document instead of following the selection, the picker in the tab head is gone, zoom and scroll are kept per document for the session and dropped on close, a double click on a call node stays in the same tab with breadcrumbs per tab; one editor per kind is switched between documents, the placement rules live in the pure module documents.ts, and the test hook showTab stays as a compatibility path; new e2e module 44 documents (#171)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6d7d0005cf |
The tab headers are one TabHeader, the node board and the tile board pan and zoom through one helper, reordering tile pictures and tilemap layers goes through one moveItem, and a tilemap cell is one named type (#85)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bfbcbe604c |
The tile list in the tilemap section is a palette of pictures: each tile shows its lone tile (the four convex corners for floor and wall, the picture itself for none) with its name below, laid out with the image-picker grid (#139)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
732f6518ae |
The autotile asset is renamed to tile and gains a plain None (1 × 1) kind, which is now the default; older projects load their autotiles as floor tiles (#131)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |