@oh-just-another/state 0.61.0 → 0.63.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.
Files changed (259) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/actions/registry.js +1 -1
  4. package/dist/actions/registry.js.map +1 -1
  5. package/dist/constants.d.ts +179 -12
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +150 -12
  8. package/dist/constants.js.map +1 -1
  9. package/dist/editor/animation-scene.d.ts.map +1 -1
  10. package/dist/editor/animation-scene.js +27 -39
  11. package/dist/editor/animation-scene.js.map +1 -1
  12. package/dist/editor/animation.js +1 -1
  13. package/dist/editor/animation.js.map +1 -1
  14. package/dist/editor/applies/arrange-units.d.ts +24 -0
  15. package/dist/editor/applies/arrange-units.d.ts.map +1 -0
  16. package/dist/editor/applies/arrange-units.js +56 -0
  17. package/dist/editor/applies/arrange-units.js.map +1 -0
  18. package/dist/editor/applies/arrange.d.ts +16 -15
  19. package/dist/editor/applies/arrange.d.ts.map +1 -1
  20. package/dist/editor/applies/arrange.js +33 -48
  21. package/dist/editor/applies/arrange.js.map +1 -1
  22. package/dist/editor/applies/create.d.ts +16 -10
  23. package/dist/editor/applies/create.d.ts.map +1 -1
  24. package/dist/editor/applies/create.js +32 -12
  25. package/dist/editor/applies/create.js.map +1 -1
  26. package/dist/editor/applies/edge.d.ts +1 -1
  27. package/dist/editor/applies/edge.d.ts.map +1 -1
  28. package/dist/editor/applies/object-snap.d.ts +87 -0
  29. package/dist/editor/applies/object-snap.d.ts.map +1 -0
  30. package/dist/editor/applies/object-snap.js +198 -0
  31. package/dist/editor/applies/object-snap.js.map +1 -0
  32. package/dist/editor/applies/resize.d.ts +1 -1
  33. package/dist/editor/applies/resize.d.ts.map +1 -1
  34. package/dist/editor/applies/resize.js +6 -4
  35. package/dist/editor/applies/resize.js.map +1 -1
  36. package/dist/editor/applies/selection.d.ts +5 -5
  37. package/dist/editor/applies/selection.d.ts.map +1 -1
  38. package/dist/editor/applies/selection.js +15 -7
  39. package/dist/editor/applies/selection.js.map +1 -1
  40. package/dist/editor/applies/snap-grid.d.ts +1 -1
  41. package/dist/editor/applies/snap-grid.d.ts.map +1 -1
  42. package/dist/editor/container-ops.d.ts +1 -1
  43. package/dist/editor/container-ops.d.ts.map +1 -1
  44. package/dist/{editor-events.d.ts → editor/editor-events.d.ts} +2 -2
  45. package/dist/editor/editor-events.d.ts.map +1 -0
  46. package/dist/{editor-events.js.map → editor/editor-events.js.map} +1 -1
  47. package/dist/editor/event-fanout.d.ts +3 -3
  48. package/dist/editor/event-fanout.d.ts.map +1 -1
  49. package/dist/editor/gesture-tx.d.ts +1 -1
  50. package/dist/editor/gesture-tx.d.ts.map +1 -1
  51. package/dist/editor/hit-test.d.ts +5 -3
  52. package/dist/editor/hit-test.d.ts.map +1 -1
  53. package/dist/editor/hit-test.js +18 -10
  54. package/dist/editor/hit-test.js.map +1 -1
  55. package/dist/editor/interaction-state.d.ts +29 -2
  56. package/dist/editor/interaction-state.d.ts.map +1 -1
  57. package/dist/editor/interaction-state.js +34 -0
  58. package/dist/editor/interaction-state.js.map +1 -1
  59. package/dist/editor/link-handle-drag.d.ts +1 -1
  60. package/dist/editor/link-handle-drag.d.ts.map +1 -1
  61. package/dist/editor/link-handle-drag.js +1 -1
  62. package/dist/editor/link-handle-drag.js.map +1 -1
  63. package/dist/editor/pointer-binding.d.ts.map +1 -1
  64. package/dist/editor/pointer-binding.js +86 -24
  65. package/dist/editor/pointer-binding.js.map +1 -1
  66. package/dist/editor/public/arrange-group.d.ts +6 -5
  67. package/dist/editor/public/arrange-group.d.ts.map +1 -1
  68. package/dist/editor/public/arrange-group.js +39 -23
  69. package/dist/editor/public/arrange-group.js.map +1 -1
  70. package/dist/editor/public/clipboard.d.ts +1 -1
  71. package/dist/editor/public/clipboard.d.ts.map +1 -1
  72. package/dist/editor/public/clipboard.js +2 -2
  73. package/dist/editor/public/clipboard.js.map +1 -1
  74. package/dist/editor/public/cursor.js +1 -1
  75. package/dist/editor/public/cursor.js.map +1 -1
  76. package/dist/editor/public/eraser.js +1 -1
  77. package/dist/editor/public/eraser.js.map +1 -1
  78. package/dist/editor/public/image-insert.d.ts.map +1 -1
  79. package/dist/editor/public/image-insert.js +4 -1
  80. package/dist/editor/public/image-insert.js.map +1 -1
  81. package/dist/editor/public/label-seed.d.ts +12 -0
  82. package/dist/editor/public/label-seed.d.ts.map +1 -0
  83. package/dist/editor/public/label-seed.js +20 -0
  84. package/dist/editor/public/label-seed.js.map +1 -0
  85. package/dist/editor/public/layers.d.ts +1 -1
  86. package/dist/editor/public/layers.d.ts.map +1 -1
  87. package/dist/editor/public/placement.d.ts +6 -1
  88. package/dist/editor/public/placement.d.ts.map +1 -1
  89. package/dist/editor/public/placement.js +47 -17
  90. package/dist/editor/public/placement.js.map +1 -1
  91. package/dist/editor/public/selection-ops.d.ts +1 -1
  92. package/dist/editor/public/selection-ops.d.ts.map +1 -1
  93. package/dist/editor/public/selection-ops.js +31 -2
  94. package/dist/editor/public/selection-ops.js.map +1 -1
  95. package/dist/editor/public/stroke-eraser-coverage.js +1 -1
  96. package/dist/editor/public/stroke-eraser-coverage.js.map +1 -1
  97. package/dist/editor/public/stroke-eraser.js +1 -1
  98. package/dist/editor/public/stroke-eraser.js.map +1 -1
  99. package/dist/editor/public/text-edit.d.ts +4 -2
  100. package/dist/editor/public/text-edit.d.ts.map +1 -1
  101. package/dist/editor/public/text-edit.js +8 -4
  102. package/dist/editor/public/text-edit.js.map +1 -1
  103. package/dist/editor/public/tool-ops.d.ts +10 -7
  104. package/dist/editor/public/tool-ops.d.ts.map +1 -1
  105. package/dist/editor/public/tool-ops.js +141 -14
  106. package/dist/editor/public/tool-ops.js.map +1 -1
  107. package/dist/editor/public/zoom-pan.d.ts +10 -1
  108. package/dist/editor/public/zoom-pan.d.ts.map +1 -1
  109. package/dist/editor/public/zoom-pan.js +49 -0
  110. package/dist/editor/public/zoom-pan.js.map +1 -1
  111. package/dist/editor/render-orchestrator.d.ts +17 -11
  112. package/dist/editor/render-orchestrator.d.ts.map +1 -1
  113. package/dist/editor/render-orchestrator.js +45 -31
  114. package/dist/editor/render-orchestrator.js.map +1 -1
  115. package/dist/editor/resize-helpers.d.ts +1 -1
  116. package/dist/editor/resize-helpers.d.ts.map +1 -1
  117. package/dist/editor/shape-traits.d.ts +1 -1
  118. package/dist/editor/shape-traits.d.ts.map +1 -1
  119. package/dist/editor/shape-traits.js +1 -1
  120. package/dist/editor/shape-traits.js.map +1 -1
  121. package/dist/editor/text-edit.d.ts +24 -11
  122. package/dist/editor/text-edit.d.ts.map +1 -1
  123. package/dist/editor/text-edit.js +225 -45
  124. package/dist/editor/text-edit.js.map +1 -1
  125. package/dist/editor.d.ts +306 -18
  126. package/dist/editor.d.ts.map +1 -1
  127. package/dist/editor.js +1233 -67
  128. package/dist/editor.js.map +1 -1
  129. package/dist/features/built-in-handlers.d.ts.map +1 -0
  130. package/dist/{built-in-handlers.js → features/built-in-handlers.js} +7 -1
  131. package/dist/features/built-in-handlers.js.map +1 -0
  132. package/dist/{clipboard.d.ts → features/clipboard.d.ts} +14 -0
  133. package/dist/features/clipboard.d.ts.map +1 -0
  134. package/dist/{clipboard.js → features/clipboard.js} +1 -1
  135. package/dist/features/clipboard.js.map +1 -0
  136. package/dist/{file-drop.d.ts → features/file-drop.d.ts} +20 -1
  137. package/dist/features/file-drop.d.ts.map +1 -0
  138. package/dist/{file-drop.js → features/file-drop.js} +47 -19
  139. package/dist/features/file-drop.js.map +1 -0
  140. package/dist/features/insert-scene.d.ts +19 -0
  141. package/dist/features/insert-scene.d.ts.map +1 -0
  142. package/dist/features/insert-scene.js +145 -0
  143. package/dist/features/insert-scene.js.map +1 -0
  144. package/dist/features/search.d.ts.map +1 -0
  145. package/dist/features/search.js.map +1 -0
  146. package/dist/helpers/auto-compact.d.ts.map +1 -0
  147. package/dist/{auto-compact.js → helpers/auto-compact.js} +1 -1
  148. package/dist/helpers/auto-compact.js.map +1 -0
  149. package/dist/helpers/auto-layout-scheduler.d.ts.map +1 -0
  150. package/dist/helpers/auto-layout-scheduler.js.map +1 -0
  151. package/dist/helpers/frame-helpers.d.ts.map +1 -0
  152. package/dist/helpers/frame-helpers.js.map +1 -0
  153. package/dist/helpers/group-helpers.d.ts.map +1 -0
  154. package/dist/helpers/group-helpers.js.map +1 -0
  155. package/dist/helpers/util.d.ts.map +1 -0
  156. package/dist/helpers/util.js.map +1 -0
  157. package/dist/index.d.ts +23 -21
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +12 -12
  160. package/dist/index.js.map +1 -1
  161. package/dist/input/dom-events.d.ts.map +1 -0
  162. package/dist/input/dom-events.js.map +1 -0
  163. package/dist/{hotkeys-binding.d.ts → input/hotkeys-binding.d.ts} +2 -2
  164. package/dist/input/hotkeys-binding.d.ts.map +1 -0
  165. package/dist/{hotkeys-binding.js → input/hotkeys-binding.js} +1 -1
  166. package/dist/input/hotkeys-binding.js.map +1 -0
  167. package/dist/input/platform.d.ts.map +1 -0
  168. package/dist/input/platform.js.map +1 -0
  169. package/dist/{handle.d.ts → interaction/handle.d.ts} +1 -1
  170. package/dist/interaction/handle.d.ts.map +1 -0
  171. package/dist/{handle.js → interaction/handle.js} +1 -1
  172. package/dist/interaction/handle.js.map +1 -0
  173. package/dist/interaction/interactive.d.ts.map +1 -0
  174. package/dist/interaction/interactive.js.map +1 -0
  175. package/dist/interaction/machine.d.ts.map +1 -0
  176. package/dist/interaction/machine.js.map +1 -0
  177. package/dist/interaction/modes.d.ts.map +1 -0
  178. package/dist/interaction/modes.js.map +1 -0
  179. package/dist/render/animation-tick.d.ts.map +1 -0
  180. package/dist/render/animation-tick.js.map +1 -0
  181. package/dist/{overlay.d.ts → render/overlay.d.ts} +42 -38
  182. package/dist/render/overlay.d.ts.map +1 -0
  183. package/dist/{overlay.js → render/overlay.js} +180 -65
  184. package/dist/render/overlay.js.map +1 -0
  185. package/dist/selection/link-selection.d.ts.map +1 -0
  186. package/dist/{link-selection.js → selection/link-selection.js} +1 -1
  187. package/dist/selection/link-selection.js.map +1 -0
  188. package/dist/selection/selection.d.ts.map +1 -0
  189. package/dist/{selection.js → selection/selection.js} +1 -1
  190. package/dist/selection/selection.js.map +1 -0
  191. package/package.json +5 -5
  192. package/dist/animation-tick.d.ts.map +0 -1
  193. package/dist/animation-tick.js.map +0 -1
  194. package/dist/auto-compact.d.ts.map +0 -1
  195. package/dist/auto-compact.js.map +0 -1
  196. package/dist/auto-layout-scheduler.d.ts.map +0 -1
  197. package/dist/auto-layout-scheduler.js.map +0 -1
  198. package/dist/built-in-handlers.d.ts.map +0 -1
  199. package/dist/built-in-handlers.js.map +0 -1
  200. package/dist/clipboard.d.ts.map +0 -1
  201. package/dist/clipboard.js.map +0 -1
  202. package/dist/dom-events.d.ts.map +0 -1
  203. package/dist/dom-events.js.map +0 -1
  204. package/dist/editor-events.d.ts.map +0 -1
  205. package/dist/file-drop.d.ts.map +0 -1
  206. package/dist/file-drop.js.map +0 -1
  207. package/dist/frame-helpers.d.ts.map +0 -1
  208. package/dist/frame-helpers.js.map +0 -1
  209. package/dist/group-helpers.d.ts.map +0 -1
  210. package/dist/group-helpers.js.map +0 -1
  211. package/dist/handle.d.ts.map +0 -1
  212. package/dist/handle.js.map +0 -1
  213. package/dist/hotkeys-binding.d.ts.map +0 -1
  214. package/dist/hotkeys-binding.js.map +0 -1
  215. package/dist/interactive.d.ts.map +0 -1
  216. package/dist/interactive.js.map +0 -1
  217. package/dist/link-selection.d.ts.map +0 -1
  218. package/dist/link-selection.js.map +0 -1
  219. package/dist/machine.d.ts.map +0 -1
  220. package/dist/machine.js.map +0 -1
  221. package/dist/modes.d.ts.map +0 -1
  222. package/dist/modes.js.map +0 -1
  223. package/dist/overlay.d.ts.map +0 -1
  224. package/dist/overlay.js.map +0 -1
  225. package/dist/platform.d.ts.map +0 -1
  226. package/dist/platform.js.map +0 -1
  227. package/dist/search.d.ts.map +0 -1
  228. package/dist/search.js.map +0 -1
  229. package/dist/selection.d.ts.map +0 -1
  230. package/dist/selection.js.map +0 -1
  231. package/dist/util.d.ts.map +0 -1
  232. package/dist/util.js.map +0 -1
  233. /package/dist/{editor-events.js → editor/editor-events.js} +0 -0
  234. /package/dist/{built-in-handlers.d.ts → features/built-in-handlers.d.ts} +0 -0
  235. /package/dist/{search.d.ts → features/search.d.ts} +0 -0
  236. /package/dist/{search.js → features/search.js} +0 -0
  237. /package/dist/{auto-compact.d.ts → helpers/auto-compact.d.ts} +0 -0
  238. /package/dist/{auto-layout-scheduler.d.ts → helpers/auto-layout-scheduler.d.ts} +0 -0
  239. /package/dist/{auto-layout-scheduler.js → helpers/auto-layout-scheduler.js} +0 -0
  240. /package/dist/{frame-helpers.d.ts → helpers/frame-helpers.d.ts} +0 -0
  241. /package/dist/{frame-helpers.js → helpers/frame-helpers.js} +0 -0
  242. /package/dist/{group-helpers.d.ts → helpers/group-helpers.d.ts} +0 -0
  243. /package/dist/{group-helpers.js → helpers/group-helpers.js} +0 -0
  244. /package/dist/{util.d.ts → helpers/util.d.ts} +0 -0
  245. /package/dist/{util.js → helpers/util.js} +0 -0
  246. /package/dist/{dom-events.d.ts → input/dom-events.d.ts} +0 -0
  247. /package/dist/{dom-events.js → input/dom-events.js} +0 -0
  248. /package/dist/{platform.d.ts → input/platform.d.ts} +0 -0
  249. /package/dist/{platform.js → input/platform.js} +0 -0
  250. /package/dist/{interactive.d.ts → interaction/interactive.d.ts} +0 -0
  251. /package/dist/{interactive.js → interaction/interactive.js} +0 -0
  252. /package/dist/{machine.d.ts → interaction/machine.d.ts} +0 -0
  253. /package/dist/{machine.js → interaction/machine.js} +0 -0
  254. /package/dist/{modes.d.ts → interaction/modes.d.ts} +0 -0
  255. /package/dist/{modes.js → interaction/modes.js} +0 -0
  256. /package/dist/{animation-tick.d.ts → render/animation-tick.d.ts} +0 -0
  257. /package/dist/{animation-tick.js → render/animation-tick.js} +0 -0
  258. /package/dist/{link-selection.d.ts → selection/link-selection.d.ts} +0 -0
  259. /package/dist/{selection.d.ts → selection/selection.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,94 @@
1
1
  # @oh-just-another/state
2
2
 
3
+ ## 0.63.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f12caa8: "Back to content" jumps to the element nearest the camera instead of fitting the whole scene: new `Editor.revealNearestContent(padding = 80)` (pure `computeRevealNearest` / `nearestElementBounds` in `zoom-pan`) centres that element at the current zoom, zooming out only when it does not fit — a lone small shape is no longer blown up to full screen and a large board is no longer shrunk to a speck. `ResetToContentButton` calls it.
8
+ - 76463dd: Sticky reaction pills and the "+" add-reaction button are now painted by the canvas renderer (single visual source that tracks the shape 1:1 while dragging; pills also reach PNG / SVG exports); the DOM overlay only lays transparent click zones over the same rects (`stickyReactionLayout`) and hosts the emoji picker. Pills keep a constant on-screen size across zoom (low-zoom clamp `STICKY_REACTION_MIN_ZOOM`) and wrap onto new rows inline-block style instead of being dropped. The "+" button is hover-only chrome: shown for the sticky under the idle cursor (`Editor.hoveredStickyId` → `RenderSceneOptions.hoveredElement`), excluded from exports and read-only views. Static exports gained content switches (`RenderSceneOptions.content`, defaults in `EXPORT_CONTENT_DEFAULTS`) toggling sticky reactions / tags / author, wired to "Include in export" checkboxes in the Export… menu and to `downloadPng` / `downloadSvg` / `exportSceneToPng`.
9
+ - e202058: Drop any importable diagram file onto the canvas. `@oh-just-another/importers` now owns the formats table (`DIAGRAM_FORMATS`, `IMPORT_FORMATS`, `EXPORT_FORMATS`, `importSceneFrom`, `exportSceneAs`, `importFormatForFile`) and ships `diagramFileDropHandler` — native JSON, Excalidraw, Mermaid, JSON Canvas, Graphviz DOT and draw.io files are parsed and inserted at the drop point; the `Editor` component registers it by default (listed as "Diagrams" in the drop overlay). New `Editor.insertScene(fragment, worldPoint)` merges a scene fragment — elements, links and binary files, ids remapped, one undo step — into the current scene without replacing it.
10
+ - e66a8a5: Canvas-menu backing APIs. `Editor.preferences` / `setPreferences` (`snapObjects`, `showObjectSize`, `suggestObjectSize`, `wheelMode`) seeded via `EditorOptions.preferences`; `wheelMode` (`auto` / `mouse` / `trackpad`) routes plain wheel events to zoom or pan. `Editor.unlockAll()`, `Editor.createStickyAt(point)`. Saved start view: `Viewport.startView` (exported with the scene, applied when a document loads), `Editor.setCurrentViewAsStart()` / `goToStartView()` / `clearStartView()` / `startView`.
11
+ - 10eac46: Drop overlay: while an OS file is dragged over the canvas the editor shows a dashed frame, a drop glyph with "DROP" and a chip per accepted file kind. `FileDropHandler` gains presentation metadata — `label`, `kind` (`image` · `video` · `scene` · `text` · `data` · `file`) and `formats` — the built-in image and video handlers ship theirs; `Editor.getFileDropHandlers()` lists the registry. New `FileDropOverlay` component and `usePalettePlacement({ onFileDrag })` for hosts composing their own canvas.
12
+ - a6fe14d: Frame size presets. The frame toolbar gained a size-preset dropdown (A4, Letter, 16:9, 4:3, 1:1, Phone, Tablet, Browser — `FRAME_SIZE_PRESETS`, applied via the new `Editor.applyFramePreset`).
13
+ - e2ff8df: Image file tools. The image toolbar (single selection) gained a file-name input (renames the backing `BinaryFile`, undoable), Replace image (swaps the bytes while keeping position / size / crop), Download (original bytes with stored name / mime) and an Alt-text editor backed by the new `ImageElement.alt` field (serialized; surfaced to hosts for accessibility). New editor APIs: `renameBinaryFile`, `setImageAlt`, `replaceImageFile`.
14
+ - 5f08d13: Image shape masks: `ImageElement.mask` clips the drawn box to an ellipse, rounded rect (normalised `radius`) or arbitrary normalised polygon (presets in `IMAGE_MASK_POLYGON_PRESETS`), applied after `crop` and reaching every backend and PNG / SVG exports through the new `RenderTarget.clip`. `Editor.setImageMask(ids, mask | null)` sets/clears it (undoable); the image toolbar gained a mask picker next to Crop: aspect presets (Custom / Original / Circle / Square / Portrait / Landscape / Wide via `Editor.setImageAspectPreset` — centre-crop to the ratio, box refit, Circle adds an ellipse mask) plus shape tiles with a corner-radius slider.
15
+ - 350c6d3: Locked elements are now click-through: the pointer hit-test skips locked (and hidden) shapes and picks whatever lies beneath, instead of letting them shadow the shapes below. Locking a selection drops it. Unlocking moved to the right-click context menu ("Unlock", backed by the new `Editor.lockedElementAt` / `Editor.unlockElement`), and the selection toolbar gained a Lock button plus a "Lock" context-menu entry. `getElementAt` / `getElementAtIndexed` accept an optional `accept` predicate that skips rejected shapes and keeps scanning beneath them.
16
+ - 518a6d1: Object snapping and size assists. Move / resize gestures snap to the edges and centres of nearby shapes (alignment guides on the overlay, `snapObjects` preference), resizing can snap to a nearby shape's width / height with that shape highlighted (`suggestObjectSize`), and a `W × H` readout shows under the shape being resized (`showObjectSize`). `Editor.snapGuides` / `sizeReadout` / `sizeMatch` expose the per-tick state; `snapMoveDeltaToObjects` / `snapResizeDeltaToObjects` are the pure helpers. Guides use the reference look: a dashed alignment line overshooting both shapes, plus ticked distance segments (labelled when `showObjectSize` is on) for the gap between the shapes and for a matched width / height. `buildRoundedRectPath` is exported from `@oh-just-another/renderer-core`. Participation follows the reference: brush strokes never snap (as targets or movers), targets must sit on the 90° grid and be at least `OBJECT_SNAP_MIN_SIZE_PX` on screen, moves pair edge-to-edge / centre-to-centre only, and a multi-selection snaps by its frame edges.
17
+ - b1e08de: "Replace image" is now "Replace media": `Editor.replaceImageFile` accepts GIFs and videos in addition to static images. The shape keeps its position and width (height refits to the new aspect — videos measure via a hidden looping `<video>`, same as the drop handler); animation fields (`animationKind` / `animationData` / `metadata.animated`) are rewritten to match the new media kind, and the crop resets when the media kind changes. The toolbar control's file picker accepts `image/*,video/*`.
18
+ - e6057d1: Double-clicking the rotate grip resets the selection's rotation to 0 (each element about its own centre, one undo step). New `Editor.resetSelectionRotation()`.
19
+ - 2cd199e: Level of detail is decided per element from its size on screen, not from the zoom level. `LodOptions` is now `{ placeholderMaxScreenPx, minTextScreenPx }` (the zoom thresholds `placeholder` / `hideText` are gone): a shape whose longer side is below `placeholderMaxScreenPx` (default 8 px) on screen becomes a flat AABB fill; text — standalone and embedded shape labels, on the resolved font size — below `minTextScreenPx` (default 6 px) is skipped. A huge shape or a giant heading therefore stays fully drawn at 1 % while small ones degrade first. Helpers `screenSizeOf` / `isTextBelowLod` and the `LOD_*_SCREEN_PX` constants are exported. `MIN_ZOOM` drops from 5 % to 1 %.
20
+ - 745d7a9: Embedded shape labels and grouped style controls. Rectangles, ellipses, polygons and block arrows can now carry text inside their body (`ElementBase.label` — full text stack: wrapping, styled runs, lists, highlight; centered by default with `textAlign` / `textBaseline` overrides). Double-click opens the same inline editor text elements use (typing, caret, selection, Tab-nesting all work; an emptied label is stripped on commit). Labels render on every backend and serialize. The wire schema also gained the element-level `locked` / `hidden` flags that previously failed strict validation. The shape toolbar now groups border editing (color / width / dash / corners) and fill (color / opacity) into two popovers.
21
+ - 67b98bb: "Shapes and lines" toolbar button (reference behaviour): replaces the separate rectangle / ellipse / connector buttons with one trigger opening a flyout beside the toolbar — Line / Arrow / Elbow arrow (connector presets: routing + arrowhead, applied to new links AND the live preview via `Editor.armLineTool`), Rectangle / Oval / Rhombus / Triangle (`Editor.armShapeTool` — diamond and triangle draw as inscribed polygons through the same rubber-band gesture), and "More shapes" opening the template library (the standalone library toggle is gone from the dock — "More shapes" is now its only toolbar entry point; `hideLibraryButton` removes that row). Hotkeys R / O / L keep arming the stock tools; any tool switch resets the armed variant.
22
+ - 586b7ed: Sticky auto-fit text. New stickies start in Auto mode (`ShapeLabel.autoFit`): the rendered font size is derived — binary search over the shared text layout, memoized — so the text fills the card and scales with it. Picking an explicit size in the toolbar leaves Auto (reference behaviour); the "Auto" button in the font-size popover returns to it (`Editor.setLabelAutoFit`). The sticky toolbar branch also gained the label text controls (font family, size, style, alignment).
23
+ - d4c2c2f: Sticky notes and emoji elements. New plugin-style scene types: `sticky` (rounded card, background from `style.fill`, text via the shared embedded label with double-click editing, optional author-name strip) and `emoji` (single glyph at a given size). Both render on every backend, serialize through the custom-element schema, and are created from the shape library ("Sticky note" now produces a real sticky; new "Emoji" entry). The selection toolbar gained dedicated branches: sticky — S/M/L size presets, background color/opacity, Show-author toggle; emoji — a glyph picker. New editor APIs: `setStickySize`, `toggleStickyAuthor`, `setEmojiGlyph`.
24
+ - 24c33b3: Switch type now covers the full matrix: shape kinds (rectangle / ellipse / diamond) ↔ text ↔ sticky, any to any, bulk on multi-selection. The user text transplants between carriers (`TextElement.text` ↔ embedded `label`); converting INTO a sticky snaps the fill to the nearest colour of the new `STICKY_PALETTE` (a text's font colour never becomes the card colour); converting FROM a sticky drops its reactions / tags / author. The toolbar's type control gained Text and Sticky targets and now also appears for text and sticky selections.
25
+ - ef7388f: Text lists. Text elements gained per-paragraph attributes (`paragraphs`: bulleted / numbered kind + nesting level) that survive serialization. The layout engine indents list paragraphs, shrinks their wrap budget and keeps caret / click / selection geometry in lockstep; renderers draw derived markers ("•", auto-numbering per nesting level) on every backend. New editor APIs `setParagraphList` / `indentParagraphs` target the inline-edit selection's paragraphs while editing (whole element otherwise); typing keeps attributes aligned (Enter continues a list, deleting a line drops its attrs); Tab / Shift+Tab change nesting during editing. The text toolbar gained a two-row List dropdown (bulleted / ordered + indent ±).
26
+ - d8bf8c1: Zoom menu: clicking the zoom percentage in the bottom bar opens a view menu — Enter / Exit full screen, Hide / Show minimap (`M`), Grid › (None / Dot grid / Line grid), an Object dimensions switch, Fit to screen and zoom presets (50 % … 2000 %). `Editor.setZoom(level)` sets an absolute zoom about the viewport centre. react-ui adds the `Switch` primitive, the `useFullscreen` hook, and `MainMenu` options `ariaLabel` / `placement` / `triggerClassName` / `triggerStyle` plus `MainMenu.Item` `trailing`. The `minimap` prop now seeds the runtime-toggleable minimap.
27
+
28
+ ### Patch Changes
29
+
30
+ - 98070d8: Multi-element commands treat a selected group as one unit: Arrange as grid, Stack, Align, Distribute, Flip and Rotate move / mirror / turn a group's whole subtree together (its footprint is the union of its members) instead of scattering the children or ignoring them. New `ARRANGE_LAYOUT_GAP` constant.
31
+ - 2942fb9: Clicking a link-start dot now spawns a clone with only the source's base look (kind, size, style): the embedded label and sticky reactions / tags / author no longer carry over into the new shape (the hover ghost matches).
32
+ - d0eb799: A right-click / touch long-press now routes the selection before `onLongPress` listeners fire: on empty canvas it clears the selection (the menu is the canvas menu), on an unselected element or link it selects that one, and on a selected element it keeps the current selection.
33
+ - e0e4ea9: Stop rendering after dispose. Async completions (image decode, font load) resolving after a runtime backend switch could schedule a frame onto disposed targets; on WebGL2 the lazy pipeline rebuild then recompiled shaders on the lost context and threw "Ellipse shader compile failed: null" from a promise chain. `Editor` no longer schedules renders once disposed, and `WebGL2Target` draw calls become no-ops after `dispose()`.
34
+
35
+ Also make the "skipped a non-drawable image source" warning signal-only: the image element renderer now silently skips shapes whose handle is dead but rehydratable (`fileId` present — the transient first paint after a scene restore), and rehydration itself reports missing `Scene.files` bytes or decode failures. The renderer warning now fires only when an image really will stay blank.
36
+
37
+ - d658680: Selected shapes no longer get a translucent contour halo under the body — the selection frame + handles are the only selection chrome. `paintElementSelectionHalo` / `ElementHalo` are removed (links keep their halo).
38
+ - 3e5d81f: Floating chrome appears in place, instantly: `floatPanel` keeps a panel invisible until its first position resolves (menus, submenus, popovers no longer paint at the corner and jump), and the selection toolbar reveals with `visibility` instead of a fade. A library drag-to-place is now an element gesture — `Editor.placementId` is set from `beginPlacement` until commit / cancel — so the selection toolbar stays hidden and the minimap defers its repaint while the shape is being dragged in.
39
+ - 06a0625: Recover media dropped with a generic MIME. A file handed over with an empty `File.type` (some drag sources / extension-less downloads) was stored as `application/octet-stream`, and rehydration — which routes image-vs-video decoding by mime — sent it to the wrong decoder, so the shape reloaded blank. Persistence now infers the mime from the filename extension (`inferFileMime`), and rehydration falls back to magic-byte sniffing (`sniffBinaryFileMime` in scene: mp4/webm/ogg/png/jpeg/gif/webp/svg) for already-stored generic entries.
40
+ - 09bc11a: Resize handles are now centred exactly on the selection frame's corners and edge midpoints (`HANDLE_OUTSET` defaults to 0). Group resize / rotate chrome is no longer hit-testable or drawn when nothing in the selection is manipulable (e.g. a locked group).
41
+ - 3019bc7: Inline label editing behaves like a proper text box. The label's visible line window now scrolls to follow the caret (transient `metadata.labelScrollLines`, stripped on commit/cancel and on save), so arrowing to the end of a long label keeps the edited line on screen; selection highlight and the caret are clipped to the shape body. Double-click places a collapsed caret without arming a drag-select (no more accidental part-selection). Emoji now survive the WebGL2 backend: strings containing pictographs take the rasterised-bitmap text path instead of the monochrome MSDF atlas that cannot shape them.
42
+ - 2e2a9e7: Second review pass on shape labels and stickies. Label text is now strictly contained: when not even one line fits the padded body nothing paints outside the shape (no more tile artifacts after growing the font). Double-click places the caret at the click point instead of jumping to the (possibly clipped) text end. Cmd/Ctrl+A inside the inline editor is handled explicitly, removing a race with the selection mirror that made select-all intermittently need a second press. Labels are real rich text: styling with an active selection applies to just that range (styled runs) rather than the whole label. Stickies lost the folded corner (plain sheet with the bottom drop shadow), and emoji reactions became per-user toggles — your own click adds and then removes YOUR reaction (`toggleStickyReaction`, `reactions[].users`), so counters only grow through other collaborators.
43
+ - f46e3da: Rubber-band (lasso) selection picks whole groups: a grouped child inside the marquee selects its group root (respecting an entered group), like a click does — so Arrange / Align / Distribute and friends never scatter a group's children.
44
+ - 58c944b: The minimap's viewport frame follows a drag-pan: `Editor.endPanGesture` now notifies subscribers, so idle-gated observers that skip every notify while `panGesture` is set get the trailing change and repaint once the gesture ends.
45
+ - 7d15a0c: The Shapes and lines flyout marks the armed tool: the row matching the current shape kind or line preset is `menuitemradio` + `aria-checked` with the tonal selected style (stock `draw-edge` reads as Elbow arrow). `armShapeTool` / `armLineTool` notify subscribers once more after the variant is set, so listeners never observe the reset value.
46
+ - 59695d7: Snap the live draw preview to the grid. The rubber-band shown while drawing a rect / ellipse / frame followed the raw cursor and only the final shape snapped on release, so drawing looked like grid snapping was off. The preview now goes through the same snap helper as the final CREATE, matching how move / resize snap live during the gesture.
47
+
48
+ Also restore the live preview for the frame tool: `isDrawingPhase` didn't include `draw-frame`, so no rubber-band appeared while drawing a frame. The frame preview renders as the real frame element (auto-numbered name included), WYSIWYG like rect / ellipse.
49
+
50
+ - 8f8846b: The selection toolbar shows the text controls for every shape type that can carry text (rectangle, ellipse, polygon, block arrow, sticky) even before it has any — they display the defaults the first text will take. `updateLabelStyle` / `updateLabelProps` / `setLabelAutoFit` seed the label on a labelable shape that has none (create-on-write); the inline editor and the style APIs share one `seedLabel`.
51
+ - 22c0f48: Caret and selection highlight now rotate with a rotated text element (or label) while editing, and canvas clicks map to the right glyph through the element rotation. `editingTextOverlay()` exposes the new `EditingTextOverlay` type with `rotation` + `pivot`.
52
+ - e15fa56: Empty text elements show a grey placeholder prompt while being written ("Type something", "Place for text", …): `TEXT_PLACEHOLDERS` (weighted list, a few jokes at low odds) and `pickTextPlaceholder(id)` (deterministic per element id, so the prompt never changes under the caret) live in `@oh-just-another/scene`; the text bounder sizes an empty element by its prompt, so the selection box wraps it and the dirty rect covers it. `TEXT_PLACEHOLDER_COLOR` is exported from renderer-core. Drawn only when `RenderSceneOptions.textPlaceholders` / `ElementRenderContext.textPlaceholders` is set — the editor sets it outside view mode; exports and headless rendering keep empty text blank.
53
+ - 22ecd4b: Switching to a drawing, ink, eraser or laser tool clears the selection (so the selection toolbar goes away with it); `select`, `hand` and `crop` keep it. Leaving crop for another tool abandons the pending crop box.
54
+ - 8163681: First review pass on the toolbar redesign. Locked elements are no longer captured by marquee selection. Embedded shape labels clip to the shape's padded body (extra lines aren't painted), the inline-edit caret sits exactly on its line, and shapes with a label show the full set of text controls (font, size, style, alignment, color, highlight) writing through the new `updateLabelStyle` / `updateLabelProps`. Replacing an image refits the shape's height to the new file's aspect ratio, image resize never mirrors on overshoot, and the crop button matches the toolbar style. Sticky notes look like paper (drop shadow, folded corner), support tags (toolbar editor + on-card pills) and emoji reactions with counters via a bottom-left reaction bar (`StickyReactions` overlay, `setStickyTags` / `addStickyReaction`). The hide-frame feature was removed.
55
+ - Updated dependencies [76463dd]
56
+ - Updated dependencies [e0e4ea9]
57
+ - Updated dependencies [e66a8a5]
58
+ - Updated dependencies [06a0625]
59
+ - Updated dependencies [e2ff8df]
60
+ - Updated dependencies [5f08d13]
61
+ - Updated dependencies [3019bc7]
62
+ - Updated dependencies [2e2a9e7]
63
+ - Updated dependencies [350c6d3]
64
+ - Updated dependencies [518a6d1]
65
+ - Updated dependencies [3f45f83]
66
+ - Updated dependencies [3543dc7]
67
+ - Updated dependencies [2cd199e]
68
+ - Updated dependencies [745d7a9]
69
+ - Updated dependencies [586b7ed]
70
+ - Updated dependencies [d4c2c2f]
71
+ - Updated dependencies [993b46a]
72
+ - Updated dependencies [ef7388f]
73
+ - Updated dependencies [e15fa56]
74
+ - Updated dependencies [8163681]
75
+ - @oh-just-another/renderer-core@0.61.0
76
+ - @oh-just-another/scene@0.62.0
77
+ - @oh-just-another/tokens@0.58.1
78
+ - @oh-just-another/history@0.57.5
79
+
80
+ ## 0.62.0
81
+
82
+ ### Minor Changes
83
+
84
+ - ac128db: Dark theme now restyles the chrome only — the canvas always stays light. Scene colors are raw hex authored against light paper, so a dark canvas silently broke user content; `UI_SURFACE.dark.canvas` and `--du-canvas-bg` are light in every theme, and the bundled color picker always offers the light element palette. Canvas-drawn chrome (selection, handles, anchors, marquee, badges, minimap frame) is unified on the iris accent (`CANVAS_CHROME_ACCENT`, iris9 — the same accent the DOM chrome uses) instead of the ad-hoc `#1a73e8`/`#2563eb` blues. Undeclared CSS variables and stale accent fallbacks in the stylesheet now resolve to the real theme tokens, so the affected popovers follow the active theme.
85
+
86
+ ### Patch Changes
87
+
88
+ - Updated dependencies [ac128db]
89
+ - @oh-just-another/tokens@0.58.0
90
+ - @oh-just-another/renderer-core@0.60.1
91
+
3
92
  ## 0.61.0
4
93
 
5
94
  ### Minor Changes