@opendaw/lib-box 0.0.63 → 0.0.64

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/dist/editing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BoxGraph } from "./graph";
2
- import { Editing, Maybe, Option, SyncProvider } from "@opendaw/lib-std";
2
+ import { Editing, Maybe, Observer, Option, Subscription, SyncProvider } from "@opendaw/lib-std";
3
3
  export interface ModificationProcess {
4
4
  approve(): void;
5
5
  revert(): void;
@@ -8,12 +8,15 @@ export declare class BoxEditing implements Editing {
8
8
  #private;
9
9
  constructor(graph: BoxGraph);
10
10
  get graph(): BoxGraph;
11
+ subscribe(observer: Observer<void>): Subscription;
11
12
  markSaved(): void;
12
13
  hasUnsavedChanges(): boolean;
13
14
  isEmpty(): boolean;
14
15
  clear(): void;
15
16
  undo(): boolean;
16
17
  redo(): boolean;
18
+ canUndo(): boolean;
19
+ canRedo(): boolean;
17
20
  mustModify(): boolean;
18
21
  modify<R>(modifier: SyncProvider<Maybe<R>>, mark?: boolean): Option<R>;
19
22
  beginModification(): ModificationProcess;
@@ -1 +1 @@
1
- {"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAAiB,OAAO,EAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAC,MAAM,kBAAkB,CAAA;AAqB1F,MAAM,WAAW,mBAAmB;IAChC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;CACjB;AAED,qBAAa,UAAW,YAAW,OAAO;;gBAW1B,KAAK,EAAE,QAAQ;IAI3B,IAAI,KAAK,IAAI,QAAQ,CAAqB;IAE1C,SAAS,IAAI,IAAI;IAKjB,iBAAiB,IAAI,OAAO;IAM5B,OAAO,IAAI,OAAO;IAElB,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,OAAO;IAUf,IAAI,IAAI,OAAO;IAcf,UAAU,IAAI,OAAO;IAErB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IAyB5E,iBAAiB,IAAI,mBAAmB;IAkCxC,IAAI,IAAI,IAAI;IAYZ,YAAY,IAAI,IAAI;IAMpB,OAAO,IAAI,IAAI;CAGlB"}
1
+ {"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAGH,OAAO,EAEP,KAAK,EAEL,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EACf,MAAM,kBAAkB,CAAA;AAqBzB,MAAM,WAAW,mBAAmB;IAChC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;CACjB;AAED,qBAAa,UAAW,YAAW,OAAO;;gBAY1B,KAAK,EAAE,QAAQ;IAM3B,IAAI,KAAK,IAAI,QAAQ,CAAqB;IAE1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY;IAIjD,SAAS,IAAI,IAAI;IAKjB,iBAAiB,IAAI,OAAO;IAM5B,OAAO,IAAI,OAAO;IAElB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,OAAO;IAWf,IAAI,IAAI,OAAO;IASf,OAAO,IAAI,OAAO;IAKlB,OAAO,IAAI,OAAO;IAkBlB,UAAU,IAAI,OAAO;IAErB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IA2B5E,iBAAiB,IAAI,mBAAmB;IAmCxC,IAAI,IAAI,IAAI;IAYZ,YAAY,IAAI,IAAI;IAOpB,OAAO,IAAI,IAAI;CAGlB"}
package/dist/editing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Arrays, assert, Option } from "@opendaw/lib-std";
1
+ import { Arrays, assert, Notifier, Option } from "@opendaw/lib-std";
2
2
  class Modification {
3
3
  #updates;
4
4
  constructor(updates) { this.#updates = updates; }
@@ -17,6 +17,7 @@ export class BoxEditing {
17
17
  #graph;
18
18
  #pending = [];
19
19
  #marked = [];
20
+ #notifier;
20
21
  #modifying = false;
21
22
  #inProcess = false;
22
23
  #disabled = false;
@@ -24,8 +25,12 @@ export class BoxEditing {
24
25
  #savedHistoryIndex = 0; // -1 = saved state was spliced away, >= 0 = valid saved position
25
26
  constructor(graph) {
26
27
  this.#graph = graph;
28
+ this.#notifier = new Notifier();
27
29
  }
28
30
  get graph() { return this.#graph; }
31
+ subscribe(observer) {
32
+ return this.#notifier.subscribe(observer);
33
+ }
29
34
  markSaved() {
30
35
  if (this.#pending.length > 0) {
31
36
  this.mark();
@@ -48,44 +53,65 @@ export class BoxEditing {
48
53
  Arrays.clear(this.#marked);
49
54
  this.#historyIndex = 0;
50
55
  this.#savedHistoryIndex = 0;
56
+ this.#notifier.notify();
51
57
  }
52
58
  undo() {
53
- if (this.#disabled) {
59
+ if (!this.canUndo()) {
54
60
  return false;
55
61
  }
56
62
  if (this.#pending.length > 0) {
57
63
  this.mark();
58
64
  }
59
- if (this.#historyIndex === 0) {
60
- return false;
61
- }
65
+ console.debug("undo");
62
66
  const modifications = this.#marked[--this.#historyIndex];
63
67
  modifications.toReversed().forEach(step => step.inverse(this.#graph));
64
68
  this.#graph.edges().validateRequirements();
69
+ this.#notifier.notify();
65
70
  return true;
66
71
  }
67
72
  redo() {
73
+ if (!this.canRedo()) {
74
+ return false;
75
+ }
76
+ console.debug("redo");
77
+ this.#marked[this.#historyIndex++].forEach(step => step.forward(this.#graph));
78
+ this.#graph.edges().validateRequirements();
79
+ this.#notifier.notify();
80
+ return true;
81
+ }
82
+ canUndo() {
68
83
  if (this.#disabled) {
69
84
  return false;
70
85
  }
71
- if (this.#historyIndex === this.#marked.length) {
86
+ return this.#historyIndex !== 0 || this.#pending.length > 0;
87
+ }
88
+ canRedo() {
89
+ if (this.#disabled) {
72
90
  return false;
73
91
  }
74
- if (this.#pending.length > 0) {
75
- console.warn("redo while having pending updates?");
92
+ if (this.#historyIndex === this.#marked.length) {
76
93
  return false;
77
94
  }
78
- this.#marked[this.#historyIndex++].forEach(step => step.forward(this.#graph));
79
- this.#graph.edges().validateRequirements();
80
- return true;
95
+ return this.#pending.length <= 0;
81
96
  }
82
- // TODO This is an option to clarify, if user actions meant to be run by a modifier or not.
83
- // See ParameterWrapper. Not the nicest solution. Probably coming back to this sooner or later.
97
+ // TODO This method exists to handle bidirectional sync between UI state and Box state.
98
+ // Problem: When a Box field changes (e.g., during undo/redo), reactive subscriptions may fire
99
+ // and attempt to call modify() to sync the UI state back to the Box. But since undo/redo
100
+ // already has a transaction open (via Modification.inverse/forward calling beginTransaction
101
+ // directly), calling modify() would fail with "Transaction already in progress".
102
+ // Current workaround: Callers check mustModify() before calling modify(). If false (transaction
103
+ // already open), they either skip the call or call setValue directly without recording history.
104
+ // See: EditWrapper.forValue, EditWrapper.forAutomatableParameter, TransportGroup loop sync.
105
+ // Better solution: Consider having Modification.inverse/forward use the same #modifying flag
106
+ // as modify(), or introduce a unified "modification context" that both undo/redo and user
107
+ // actions share. This would allow modify() to detect it's being called reactively during
108
+ // undo/redo and handle it internally, rather than requiring all callers to guard with mustModify().
84
109
  mustModify() { return !this.#graph.inTransaction(); }
85
110
  modify(modifier, mark = true) {
86
111
  assert(!this.#inProcess, "Cannot call modify while a modification process is running");
87
112
  if (this.#modifying) {
88
113
  // Nested modify call - just execute without separate recording
114
+ this.#notifier.notify();
89
115
  return Option.wrap(modifier());
90
116
  }
91
117
  if (mark && this.#pending.length > 0) {
@@ -108,6 +134,7 @@ export class BoxEditing {
108
134
  if (mark) {
109
135
  this.mark();
110
136
  }
137
+ this.#notifier.notify();
111
138
  return Option.wrap(result);
112
139
  }
113
140
  beginModification() {
@@ -130,6 +157,7 @@ export class BoxEditing {
130
157
  this.#inProcess = false;
131
158
  this.#graph.edges().validateRequirements();
132
159
  this.mark();
160
+ this.#notifier.notify();
133
161
  },
134
162
  revert: () => {
135
163
  this.#graph.endTransaction();
@@ -162,6 +190,7 @@ export class BoxEditing {
162
190
  }
163
191
  this.#pending.reverse().forEach(modification => modification.inverse(this.#graph));
164
192
  this.#pending.length = 0;
193
+ this.#notifier.notify();
165
194
  }
166
195
  disable() {
167
196
  this.#disabled = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendaw/lib-box",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "sideEffects": false,
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,12 +23,12 @@
23
23
  "test": "vitest run"
24
24
  },
25
25
  "dependencies": {
26
- "@opendaw/lib-runtime": "^0.0.62",
27
- "@opendaw/lib-std": "^0.0.62"
26
+ "@opendaw/lib-runtime": "^0.0.63",
27
+ "@opendaw/lib-std": "^0.0.63"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@opendaw/eslint-config": "^0.0.27",
31
31
  "@opendaw/typescript-config": "^0.0.28"
32
32
  },
33
- "gitHead": "100e759378d7150e6eb7165232d8c4a1fbb77bbc"
33
+ "gitHead": "db2b1bd172607177fef63077c2eed9b7cbec167f"
34
34
  }