ice-web-components 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,10 +27,11 @@ rings and shadows) is drawn by the engine.
27
27
  - **Bootstrap 5 token theme** (plus a dark theme) — swap with one call.
28
28
  - **No name collisions with the engine** — the package’s runtime exports are
29
29
  disjoint from `ice-render`’s (there is a regression test for it).
30
- - **Actually tested** — 723 unit tests (99 suites: form validation, overlay
31
- positioning, keyboard navigation, sort/hover/focus edge cases, and the Minesweeper,
32
- Tetris, Snake, 2048 and CHIP-8 rule/machine models) plus five browser QA suites
33
- (`qa:admin`, `qa:gallery`, `qa:workbench`, `qa:xp`, `qa:arcade` — 208 assertions) that drive the demo pages with
30
+ - **Actually tested** — 749 unit tests (101 suites: form validation, overlay
31
+ positioning, keyboard navigation, sort/hover/focus edge cases, the Minesweeper,
32
+ Tetris, Snake, 2048 and CHIP-8 rule/machine models, the pixel canvas and the undo
33
+ stack) plus six browser QA suites (`qa:admin`, `qa:gallery`, `qa:workbench`, `qa:xp`,
34
+ `qa:arcade`, `qa:pixel` — 232 assertions) that drive the demo pages with
34
35
  real mouse and keyboard events and fail on any console error.
35
36
 
36
37
  ## Quick start
@@ -307,6 +308,52 @@ Under the hood this page is where the engine work happens:
307
308
  > A game page keeps the keyboard for itself; mouse hover still goes through
308
309
  > `ICEHoverManager`.
309
310
 
311
+ ### `pixel-editor.html` — ICE Pixel Studio (a real pixel editor)
312
+
313
+ The other direction: instead of “draw a business screen”, this page is a **tool**.
314
+ The canvas, tool palette, colour swatches and status bar are all components — only the
315
+ pixels themselves are self-drawn, as a single `ICETileMap` node. Pencil, eraser, line,
316
+ rectangle and flood fill, undo/redo, and PNG + SVG export.
317
+
318
+ ![ICE Pixel Studio](docs/images/pixel-editor.png)
319
+
320
+ Two pure models carry it (no canvas involved):
321
+
322
+ ```ts
323
+ import { ICEPixelModel, ICEHistoryModel } from 'ice-web-components';
324
+
325
+ const model = new ICEPixelModel({ rows: 32, cols: 32, palette: PALETTE, background: 0 });
326
+ model.setPixel(4, 4, 1); // live change; returns whether it really changed
327
+ model.drawLine(0, 0, 0, 7, 2); // Bresenham
328
+ model.fill(3, 3, 5); // 4-neighbour flood fill (iterative, no recursion)
329
+ model.commit(); // one commit = one undo step
330
+ model.toSVG({ cellSize: 16 }); // run-length merged SVG string
331
+ model.toRGBA(16); // feed it straight into ImageData for PNG export
332
+ ```
333
+
334
+ The three decisions worth stealing:
335
+
336
+ 1. **History is per *operation*, not per pixel** — a 20-cell drag pushes exactly one
337
+ snapshot (`mousedown` paints, `mouseup` commits). Otherwise undo would need 20
338
+ presses to walk one stroke back, which decides whether a 1024-cell editor is usable.
339
+ `ICEHistoryModel` itself is a plain generic stack (push clears redo, trims to a limit,
340
+ notifies with a reason) — reusable for kanban or table editing too.
341
+ 2. **Preview via the highlight layer, not “draw then undo”** — dragging a line or a
342
+ rectangle lights up `ICETileMap.setHighlights()` from `getLineCells()` /
343
+ `getRectCells()`, and only `mouseup` commits. Preview and painting share the same
344
+ coordinate API, so the preview is exactly what you get (a unit test paints both and
345
+ compares cell by cell).
346
+ 3. **Export is pure** — `toSVG()` merges horizontal runs into single `<rect>`s (the
347
+ 32×32 smiley emits 20 elements, not 1024), and `toRGBA(scale)` hands the page an
348
+ `ImageData` buffer; only the page touches `canvas.toDataURL()`. The QA asserts on the
349
+ data: the PNG check decodes the **IHDR** chunk to prove the bitmap is 512×512.
350
+
351
+ > A component gap this page closed: `ICETileMap` cached `rows`/`cols`/`cellSize` in
352
+ > instance fields, so resizing with only `setState({ rows, cols })` left the internals
353
+ > stale and the next `setTiles` threw (“expected 1024 cells, got 256”). There is now a
354
+ > proper `setSize(rows, cols, cellSize?)` that updates the internals, the state and the
355
+ > default width/height in one go, and clears the old cell data.
356
+
310
357
  ## Components
311
358
 
312
359
  | Group | Components |
@@ -318,12 +365,12 @@ Under the hood this page is where the engine work happens:
318
365
  | Feedback & status | `ICEAlert` `ICEModal` `ICEDrawer` `ICEMessage` `ICENotification` `ICETooltip` `ICEPopover` `ICEPopconfirm` `ICETour` `ICEFloatButton` `ICEEmpty` `ICESkeleton` `ICESpin` `ICEResult` `ICESteps` `ICEOverlayManager` |
319
366
  | Navigation | `ICEMenu` `ICEBreadcrumb` `ICEAnchor` `ICEBackTop` `ICEDropdown` `ICEPagination` `ICETabs` |
320
367
  | Layout & core | `ICEWidget` `ICEContainer` `ICEHoverManager` `ICEFocusManager` `ICEMessageManager` `ICEManager` (`ICEPainter` / `ICELayoutManager` are types) |
321
- | Models | `ICEButtonModel` `ICEToggleModel` `ICEBoundedRangeModel` `ICESelectionModel` `ICEFormModel` `ICETetrisModel` `ICESnakeModel` `ICE2048Model` `ICEChip8Model` `ICEMinesweeperModel` `ICEHighScoreModel` |
368
+ | Models | `ICEButtonModel` `ICEToggleModel` `ICEBoundedRangeModel` `ICESelectionModel` `ICEFormModel` `ICEHistoryModel` `ICEPixelModel` `ICETetrisModel` `ICESnakeModel` `ICE2048Model` `ICEChip8Model` `ICEMinesweeperModel` `ICEHighScoreModel` |
322
369
 
323
370
  Helper functions: `attachTooltip` `attachPopover` `attachPopconfirm` `attachDropdown`
324
371
  `openModal` `openDrawer` `getICEOverlayManager` `getICEFocusManager` `getICEMessageManager`
325
372
  `formatStatisticValue` `formatCountdown` `truncateTextLines` `buildMonthGrid` `formatCalendarDate`
326
- `openImagePreview` `tween` `fadeIn` `fadeOut` `slideIn` `scaleIn` and friends.
373
+ `openImagePreview` `icePixelParseColor` `tween` `fadeIn` `fadeOut` `slideIn` `scaleIn` and friends.
327
374
 
328
375
  ## Theme
329
376
 
@@ -458,6 +505,11 @@ npm run qa:xp
458
505
  # real clicks on the HUD
459
506
  npm run qa:arcade
460
507
 
508
+ # browser QA for examples/pixel-editor.html: drawing with a real mouse (pencil drag,
509
+ # line/rect preview, flood fill, eraser), undo/redo via buttons and Ctrl+Z/Y, and the
510
+ # PNG (IHDR-checked) / SVG exports
511
+ npm run qa:pixel
512
+
461
513
  # docs: regenerate the API reference and check relative links
462
514
  npm run docs
463
515
  ```