@grundyjs/algiviz 0.11.0 → 0.11.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.2 — 2026-11-05
4
+
5
+ - Avoid argument-count limits when scaling large arrays in the Canvas renderer.
6
+ - Isolate Canvas composite, filter and shadow settings alongside transform/alpha;
7
+ restore caller settings even when drawing throws.
8
+ - Reject invalid frame progress before drawing; preserve the zero-sized bitmap no-op.
9
+ - Require a valid light/dark theme at runtime and allow explicitly undefined colors
10
+ with exactOptionalPropertyTypes. Preserve the createSortRenderer compatibility alias.
11
+ - Document Canvas coordinates, context ownership, scene adaptation, color priority
12
+ and error behavior in English and Russian; add renderer and packed-consumer regressions.
13
+
14
+ ## 0.11.1 — 2026-11-05
15
+
16
+ - Validate both numeric positions in array swap/shift before reading or mutating
17
+ the trace, rejecting invalid JavaScript arguments without losing item identities
18
+ or changing snapshots, counters, markers or the next step index.
19
+ - Allow explicitly undefined sorted-region inputs with exactOptionalPropertyTypes,
20
+ matching the existing runtime behavior of preserving the current marker.
21
+ - Document array guarantees in English and Russian: fixed length, one held item,
22
+ trace-local IDs, immutable snapshots, counters and author-supplied sorted markers.
23
+ - Add an application-owned object lifecycle example and expand playback completion,
24
+ strict type and installed-package consumer checks. No new public exports.
25
+
3
26
  ## 0.11.0 — 2026-11-02
4
27
 
5
28
  - Add createStatePlayer() for lazy playback of plain immutable states without
package/README.md CHANGED
@@ -102,8 +102,8 @@ the [local setup instructions](https://github.com/urffin/algiviz/blob/master/doc
102
102
  - **Scene:** describes objects with stable IDs, types and application-owned data.
103
103
  - **Handler:** chooses geometry and drawing behavior, including interpolation.
104
104
 
105
- Do not mutate emitted states. Players require an explicit terminal step;
106
- call `dispose()` when removing a visualization. Algorithms, layouts and animation
105
+ Do not mutate emitted states. `createStatePlayer` completes on source exhaustion;
106
+ `createPlayer` requires an explicit terminal step. Call `dispose()` when removing a visualization. Algorithms, layouts and animation
107
107
  scheduling belong to the application.
108
108
 
109
109
  ## Playback modes
@@ -1,7 +1,8 @@
1
1
  import type { Item, ArrayStep } from "./types.js";
2
+ /** Omitted or undefined markers preserve their current values. */
2
3
  export interface SortedRegions {
3
- readonly sortedPrefixLength?: number;
4
- readonly sortedSuffixLength?: number;
4
+ readonly sortedPrefixLength?: number | undefined;
5
+ readonly sortedSuffixLength?: number | undefined;
5
6
  }
6
7
  export interface ArrayTrace {
7
8
  readonly length: number;
@@ -54,6 +54,8 @@ export function createArrayTrace(values) {
54
54
  },
55
55
  swap(left, right, update) {
56
56
  active();
57
+ index(left);
58
+ index(right);
57
59
  const a = item(left), b = item(right);
58
60
  if (left === right)
59
61
  throw new RangeError("Swap requires distinct positions");
@@ -74,8 +76,9 @@ export function createArrayTrace(values) {
74
76
  },
75
77
  shift(from, to, update) {
76
78
  active();
77
- const moved = item(from);
79
+ index(from);
78
80
  index(to);
81
+ const moved = item(from);
79
82
  if (slots[to] !== null)
80
83
  throw new Error("Shift destination must be empty");
81
84
  regions(update);
@@ -8,7 +8,7 @@ export type ArrayObjects = {
8
8
  held: boolean;
9
9
  };
10
10
  };
11
- /** Adapts array identities to the same public scene contract used by custom objects. */
11
+ /** Adapts immutable array snapshots; freezes scene wrappers but retains Item references. */
12
12
  export declare function arrayScene(state: ArraySnapshot): Scene<ArrayObjects>;
13
13
  /** Canvas color strings for the semantic roles in the array renderer. */
14
14
  export interface ArrayPalette {
@@ -25,10 +25,10 @@ export type ArrayPaletteOverrides = {
25
25
  };
26
26
  export interface ArrayRendererOptions {
27
27
  readonly theme: "light" | "dark";
28
- readonly colors?: ArrayPaletteOverrides;
28
+ readonly colors?: ArrayPaletteOverrides | undefined;
29
29
  }
30
30
  export { createArrayRenderer as createSortRenderer };
31
- /** Stateless full-frame renderer. Bitmap dimensions determine layout and video size. */
31
+ /** Full-frame bitmap renderer; the caller owns sizing and must provide an unclipped context. */
32
32
  export declare function createArrayRenderer(options: ArrayRendererOptions): Readonly<{
33
33
  render(ctx: CanvasRenderingContext2D, frame: ArrayFrame): void;
34
34
  }>;
@@ -1,5 +1,5 @@
1
1
  import { createSceneRenderer, createSceneProjector } from "../scene/index.js";
2
- /** Adapts array identities to the same public scene contract used by custom objects. */
2
+ /** Adapts immutable array snapshots; freezes scene wrappers but retains Item references. */
3
3
  export function arrayScene(state) {
4
4
  return Object.freeze({
5
5
  objects: Object.freeze([...positions(state)].map(([id, data]) => Object.freeze({ id, type: "bar", data: Object.freeze(data) })))
@@ -34,9 +34,12 @@ function positions(state) {
34
34
  result.set(state.held.id, { item: state.held, index: state.slots.indexOf(null), held: true });
35
35
  return result;
36
36
  }
37
- /** Stateless full-frame renderer. Bitmap dimensions determine layout and video size. */
37
+ /** Full-frame bitmap renderer; the caller owns sizing and must provide an unclipped context. */
38
38
  export function createArrayRenderer(options) {
39
- const colors = { ...palettes[options.theme] };
39
+ const theme = options?.theme;
40
+ if (theme !== "light" && theme !== "dark")
41
+ throw new TypeError("Array renderer theme must be light or dark");
42
+ const colors = { ...palettes[theme] };
40
43
  for (const key of Object.keys(colors)) {
41
44
  const color = options.colors?.[key];
42
45
  if (color !== undefined)
@@ -48,6 +51,9 @@ export function createArrayRenderer(options) {
48
51
  const { width, height } = ctx.canvas;
49
52
  if (width <= 0 || height <= 0)
50
53
  return;
54
+ if (!Number.isFinite(frame.progress) || frame.progress < 0 || frame.progress > 1) {
55
+ throw new RangeError("Array frame progress must be between zero and one");
56
+ }
51
57
  const projected = projectFrame(frame);
52
58
  const items = projected.current.objects;
53
59
  const count = frame.current.slots.length;
@@ -55,7 +61,9 @@ export function createArrayRenderer(options) {
55
61
  const cell = (width - 2 * margin) / Math.max(count, 1);
56
62
  const barWidth = cell * 0.7;
57
63
  const available = height * 0.48;
58
- const maxMagnitude = Math.max(1, ...items.map(({ data }) => Math.abs(data.item.value)));
64
+ let maxMagnitude = 1;
65
+ for (const { data } of items)
66
+ maxMagnitude = Math.max(maxMagnitude, Math.abs(data.item.value));
59
67
  const baseline = height * 0.69;
60
68
  const fontSize = Math.max(9, Math.min(20, cell * 0.45, width * 0.035));
61
69
  const progress = frame.progress;
@@ -65,6 +73,12 @@ export function createArrayRenderer(options) {
65
73
  try {
66
74
  ctx.setTransform(1, 0, 0, 1, 0, 0);
67
75
  ctx.globalAlpha = 1;
76
+ ctx.globalCompositeOperation = "source-over";
77
+ ctx.filter = "none";
78
+ ctx.shadowColor = "rgba(0, 0, 0, 0)";
79
+ ctx.shadowBlur = 0;
80
+ ctx.shadowOffsetX = 0;
81
+ ctx.shadowOffsetY = 0;
68
82
  ctx.fillStyle = colors.background;
69
83
  ctx.fillRect(0, 0, width, height);
70
84
  ctx.font = `${Math.max(12, Math.min(24, width * 0.045))}px sans-serif`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grundyjs/algiviz",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "TypeScript toolkit for custom algorithm visualizations with step-by-step playback and extensible rendering",
5
5
  "type": "module",
6
6
  "files": [
package/ru/README.md CHANGED
@@ -100,7 +100,8 @@ document.querySelector('#next').onclick = () => player.nextStep();
100
100
  - **Сцена** описывает объекты: стабильный `id`, тип и собственные данные.
101
101
  - **Обработчик** выбирает геометрию и способ рисования. Плавность — его решение.
102
102
 
103
- Состояния после выдачи нельзя изменять. Плееру нужен явный завершающий шаг;
103
+ Состояния после выдачи нельзя изменять. `createStatePlayer` завершается при исчерпании
104
+ источника; `createPlayer` требует явного завершающего шага;
104
105
  при удалении визуализации вызовите `dispose()`. Алгоритмы, раскладка и цикл
105
106
  анимации принадлежат приложению.
106
107