@opendaw/studio-core 0.0.102 → 0.0.103
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/OfflineEngineRenderer.d.ts +3 -1
- package/dist/OfflineEngineRenderer.d.ts.map +1 -1
- package/dist/OfflineEngineRenderer.js +10 -2
- package/dist/capture/CaptureMidi.js +1 -1
- package/dist/capture/RecordAutomation.d.ts +6 -0
- package/dist/capture/RecordAutomation.d.ts.map +1 -0
- package/dist/capture/RecordAutomation.js +139 -0
- package/dist/capture/Recording.d.ts +3 -0
- package/dist/capture/Recording.d.ts.map +1 -1
- package/dist/capture/Recording.js +16 -36
- package/dist/capture/index.d.ts +1 -0
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +1 -0
- package/dist/offline-engine.js +1 -1
- package/dist/offline-engine.js.map +4 -4
- package/dist/processors.js +8 -8
- package/dist/processors.js.map +3 -3
- package/dist/project/Project.d.ts +1 -0
- package/dist/project/Project.d.ts.map +1 -1
- package/dist/project/Project.js +7 -5
- package/dist/project/ProjectApi.d.ts +3 -1
- package/dist/project/ProjectApi.d.ts.map +1 -1
- package/dist/project/ProjectApi.js +26 -16
- package/dist/ui/clipboard/ClipboardManager.d.ts.map +1 -1
- package/dist/ui/clipboard/ClipboardManager.js +34 -2
- package/dist/ui/clipboard/types/AudioUnitsClipboardHandler.d.ts.map +1 -1
- package/dist/ui/clipboard/types/AudioUnitsClipboardHandler.js +6 -0
- package/dist/ui/clipboard/types/RegionsClipboardHandler.d.ts.map +1 -1
- package/dist/ui/clipboard/types/RegionsClipboardHandler.js +4 -0
- package/dist/ui/menu/MenuItems.d.ts +2 -2
- package/dist/ui/menu/MenuItems.d.ts.map +1 -1
- package/dist/ui/timeline/RegionClipResolver.d.ts +2 -2
- package/dist/ui/timeline/RegionClipResolver.d.ts.map +1 -1
- package/dist/ui/timeline/RegionClipResolver.js +40 -37
- package/dist/ui/timeline/RegionClipResolver.test.js +400 -0
- package/dist/ysync/YService.d.ts.map +1 -1
- package/dist/ysync/YService.js +1 -2
- package/package.json +15 -15
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { RegionClipResolver } from "./RegionClipResolver";
|
|
3
3
|
const createMask = (position, complete) => ({ type: "range", position, complete });
|
|
4
|
+
const createRegion = (position, duration, isSelected = false) => ({
|
|
5
|
+
position,
|
|
6
|
+
duration,
|
|
7
|
+
complete: position + duration,
|
|
8
|
+
isSelected
|
|
9
|
+
});
|
|
10
|
+
const runCreateTasks = (regions, masks, showOrigin = false) => {
|
|
11
|
+
const maxComplete = masks.reduce((max, mask) => Math.max(max, mask.complete), 0);
|
|
12
|
+
return RegionClipResolver.createTasksFromMasks(regions, maxComplete, masks, showOrigin);
|
|
13
|
+
};
|
|
4
14
|
describe("RegionClipResolver.sortAndJoinMasks", () => {
|
|
5
15
|
it("should handle single mask", () => {
|
|
6
16
|
const masks = [createMask(0, 10)];
|
|
@@ -164,3 +174,393 @@ describe("RegionClipResolver.sortAndJoinMasks", () => {
|
|
|
164
174
|
expect(result[3]).toEqual(createMask(60, 70));
|
|
165
175
|
});
|
|
166
176
|
});
|
|
177
|
+
describe("RegionClipResolver.createTasksFromMasks", () => {
|
|
178
|
+
describe("single mask", () => {
|
|
179
|
+
it("should delete region fully contained in mask", () => {
|
|
180
|
+
const region = createRegion(5, 5);
|
|
181
|
+
const tasks = runCreateTasks([region], [createMask(0, 20)]);
|
|
182
|
+
expect(tasks).toHaveLength(1);
|
|
183
|
+
expect(tasks[0].type).toBe("delete");
|
|
184
|
+
expect(tasks[0].region).toBe(region);
|
|
185
|
+
});
|
|
186
|
+
it("should delete region exactly matching mask", () => {
|
|
187
|
+
const region = createRegion(5, 10);
|
|
188
|
+
const tasks = runCreateTasks([region], [createMask(5, 15)]);
|
|
189
|
+
expect(tasks).toHaveLength(1);
|
|
190
|
+
expect(tasks[0].type).toBe("delete");
|
|
191
|
+
});
|
|
192
|
+
it("should separate region spanning entire mask", () => {
|
|
193
|
+
const region = createRegion(0, 30);
|
|
194
|
+
const tasks = runCreateTasks([region], [createMask(10, 20)]);
|
|
195
|
+
expect(tasks).toHaveLength(1);
|
|
196
|
+
expect(tasks[0].type).toBe("separate");
|
|
197
|
+
if (tasks[0].type === "separate") {
|
|
198
|
+
expect(tasks[0].begin).toBe(10);
|
|
199
|
+
expect(tasks[0].end).toBe(20);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
it("should trim start when region begins within mask", () => {
|
|
203
|
+
const region = createRegion(5, 20);
|
|
204
|
+
const tasks = runCreateTasks([region], [createMask(0, 10)]);
|
|
205
|
+
expect(tasks).toHaveLength(1);
|
|
206
|
+
expect(tasks[0].type).toBe("start");
|
|
207
|
+
if (tasks[0].type === "start") {
|
|
208
|
+
expect(tasks[0].position).toBe(10);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
it("should trim end when region ends within mask", () => {
|
|
212
|
+
const region = createRegion(0, 15);
|
|
213
|
+
const tasks = runCreateTasks([region], [createMask(10, 20)]);
|
|
214
|
+
expect(tasks).toHaveLength(1);
|
|
215
|
+
expect(tasks[0].type).toBe("complete");
|
|
216
|
+
if (tasks[0].type === "complete") {
|
|
217
|
+
expect(tasks[0].position).toBe(10);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
it("should skip region entirely before mask", () => {
|
|
221
|
+
const region = createRegion(0, 5);
|
|
222
|
+
const tasks = runCreateTasks([region], [createMask(10, 20)]);
|
|
223
|
+
expect(tasks).toHaveLength(0);
|
|
224
|
+
});
|
|
225
|
+
it("should skip region entirely after mask (within maxComplete)", () => {
|
|
226
|
+
const region = createRegion(25, 5);
|
|
227
|
+
const tasks = runCreateTasks([region], [createMask(10, 20)], false);
|
|
228
|
+
expect(tasks).toHaveLength(0);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe("selected region filtering", () => {
|
|
232
|
+
it("should skip selected regions when showOrigin is false", () => {
|
|
233
|
+
const region = createRegion(5, 5, true);
|
|
234
|
+
const tasks = runCreateTasks([region], [createMask(0, 20)], false);
|
|
235
|
+
expect(tasks).toHaveLength(0);
|
|
236
|
+
});
|
|
237
|
+
it("should include selected regions when showOrigin is true", () => {
|
|
238
|
+
const region = createRegion(5, 5, true);
|
|
239
|
+
const tasks = runCreateTasks([region], [createMask(0, 20)], true);
|
|
240
|
+
expect(tasks).toHaveLength(1);
|
|
241
|
+
expect(tasks[0].type).toBe("delete");
|
|
242
|
+
});
|
|
243
|
+
it("should always include unselected regions regardless of showOrigin", () => {
|
|
244
|
+
const region = createRegion(5, 5, false);
|
|
245
|
+
const tasksFalse = runCreateTasks([region], [createMask(0, 20)], false);
|
|
246
|
+
const tasksTrue = runCreateTasks([region], [createMask(0, 20)], true);
|
|
247
|
+
expect(tasksFalse).toHaveLength(1);
|
|
248
|
+
expect(tasksTrue).toHaveLength(1);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
describe("multiple regions with single mask", () => {
|
|
252
|
+
it("should create tasks for each overlapping region", () => {
|
|
253
|
+
const region1 = createRegion(0, 5);
|
|
254
|
+
const region2 = createRegion(5, 10);
|
|
255
|
+
const region3 = createRegion(20, 5);
|
|
256
|
+
const tasks = runCreateTasks([region1, region2, region3], [createMask(3, 12)]);
|
|
257
|
+
expect(tasks).toHaveLength(2);
|
|
258
|
+
expect(tasks[0].region).toBe(region1);
|
|
259
|
+
expect(tasks[0].type).toBe("complete");
|
|
260
|
+
expect(tasks[1].region).toBe(region2);
|
|
261
|
+
expect(tasks[1].type).toBe("start");
|
|
262
|
+
});
|
|
263
|
+
it("should handle mixed task types across regions", () => {
|
|
264
|
+
const region1 = createRegion(0, 30);
|
|
265
|
+
const region2 = createRegion(8, 4);
|
|
266
|
+
const region3 = createRegion(15, 20);
|
|
267
|
+
const tasks = runCreateTasks([region1, region2, region3], [createMask(5, 20)]);
|
|
268
|
+
expect(tasks).toHaveLength(3);
|
|
269
|
+
expect(tasks[0].type).toBe("separate");
|
|
270
|
+
expect(tasks[0].region).toBe(region1);
|
|
271
|
+
expect(tasks[1].type).toBe("delete");
|
|
272
|
+
expect(tasks[1].region).toBe(region2);
|
|
273
|
+
expect(tasks[2].type).toBe("start");
|
|
274
|
+
expect(tasks[2].region).toBe(region3);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
describe("two non-adjacent masks (bug #676 scenario)", () => {
|
|
278
|
+
it("should create right-to-left tasks for region spanning both masks", () => {
|
|
279
|
+
const region = createRegion(0, 30);
|
|
280
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
281
|
+
expect(tasks).toHaveLength(2);
|
|
282
|
+
expect(tasks[0].type).toBe("separate");
|
|
283
|
+
if (tasks[0].type === "separate") {
|
|
284
|
+
expect(tasks[0].begin).toBe(15);
|
|
285
|
+
expect(tasks[0].end).toBe(20);
|
|
286
|
+
}
|
|
287
|
+
expect(tasks[1].type).toBe("separate");
|
|
288
|
+
if (tasks[1].type === "separate") {
|
|
289
|
+
expect(tasks[1].begin).toBe(5);
|
|
290
|
+
expect(tasks[1].end).toBe(10);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
it("should handle region overlapping both: complete on right, separate on left", () => {
|
|
294
|
+
const region = createRegion(3, 15);
|
|
295
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
296
|
+
expect(tasks).toHaveLength(2);
|
|
297
|
+
expect(tasks[0].type).toBe("complete");
|
|
298
|
+
if (tasks[0].type === "complete") {
|
|
299
|
+
expect(tasks[0].position).toBe(15);
|
|
300
|
+
}
|
|
301
|
+
expect(tasks[1].type).toBe("separate");
|
|
302
|
+
if (tasks[1].type === "separate") {
|
|
303
|
+
expect(tasks[1].begin).toBe(5);
|
|
304
|
+
expect(tasks[1].end).toBe(10);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
it("should handle region overlapping both: start on left, separate on right", () => {
|
|
308
|
+
const region = createRegion(7, 18);
|
|
309
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
310
|
+
expect(tasks).toHaveLength(2);
|
|
311
|
+
expect(tasks[0].type).toBe("separate");
|
|
312
|
+
if (tasks[0].type === "separate") {
|
|
313
|
+
expect(tasks[0].begin).toBe(15);
|
|
314
|
+
expect(tasks[0].end).toBe(20);
|
|
315
|
+
}
|
|
316
|
+
expect(tasks[1].type).toBe("start");
|
|
317
|
+
if (tasks[1].type === "start") {
|
|
318
|
+
expect(tasks[1].position).toBe(10);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
it("should handle region overlapping both: complete on right, start on left", () => {
|
|
322
|
+
const region = createRegion(7, 12);
|
|
323
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
324
|
+
expect(tasks).toHaveLength(2);
|
|
325
|
+
expect(tasks[0].type).toBe("complete");
|
|
326
|
+
if (tasks[0].type === "complete") {
|
|
327
|
+
expect(tasks[0].position).toBe(15);
|
|
328
|
+
}
|
|
329
|
+
expect(tasks[1].type).toBe("start");
|
|
330
|
+
if (tasks[1].type === "start") {
|
|
331
|
+
expect(tasks[1].position).toBe(10);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
it("should only create one task for region overlapping single mask", () => {
|
|
335
|
+
const region = createRegion(6, 3);
|
|
336
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
337
|
+
expect(tasks).toHaveLength(1);
|
|
338
|
+
expect(tasks[0].type).toBe("delete");
|
|
339
|
+
expect(tasks[0].region).toBe(region);
|
|
340
|
+
});
|
|
341
|
+
it("should handle multiple regions with two masks", () => {
|
|
342
|
+
const region1 = createRegion(0, 30);
|
|
343
|
+
const regionInGap = createRegion(12, 2);
|
|
344
|
+
const regionInMask = createRegion(6, 3);
|
|
345
|
+
const tasks = runCreateTasks([region1, regionInMask, regionInGap], [createMask(5, 10), createMask(15, 20)]);
|
|
346
|
+
expect(tasks).toHaveLength(3);
|
|
347
|
+
expect(tasks[0].type).toBe("separate");
|
|
348
|
+
expect(tasks[0].region).toBe(region1);
|
|
349
|
+
expect(tasks[1].type).toBe("separate");
|
|
350
|
+
expect(tasks[1].region).toBe(region1);
|
|
351
|
+
expect(tasks[2].type).toBe("delete");
|
|
352
|
+
expect(tasks[2].region).toBe(regionInMask);
|
|
353
|
+
const gapTasks = tasks.filter(task => task.region === regionInGap);
|
|
354
|
+
expect(gapTasks).toHaveLength(0);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
describe("three non-adjacent masks", () => {
|
|
358
|
+
it("should create three tasks for region spanning all masks", () => {
|
|
359
|
+
const region = createRegion(0, 35);
|
|
360
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20), createMask(25, 30)]);
|
|
361
|
+
expect(tasks).toHaveLength(3);
|
|
362
|
+
expect(tasks[0].type).toBe("separate");
|
|
363
|
+
if (tasks[0].type === "separate") {
|
|
364
|
+
expect(tasks[0].begin).toBe(25);
|
|
365
|
+
expect(tasks[0].end).toBe(30);
|
|
366
|
+
}
|
|
367
|
+
expect(tasks[1].type).toBe("separate");
|
|
368
|
+
if (tasks[1].type === "separate") {
|
|
369
|
+
expect(tasks[1].begin).toBe(15);
|
|
370
|
+
expect(tasks[1].end).toBe(20);
|
|
371
|
+
}
|
|
372
|
+
expect(tasks[2].type).toBe("separate");
|
|
373
|
+
if (tasks[2].type === "separate") {
|
|
374
|
+
expect(tasks[2].begin).toBe(5);
|
|
375
|
+
expect(tasks[2].end).toBe(10);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
it("should handle region overlapping only the last two masks", () => {
|
|
379
|
+
const region = createRegion(13, 22);
|
|
380
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20), createMask(25, 30)]);
|
|
381
|
+
expect(tasks).toHaveLength(2);
|
|
382
|
+
expect(tasks[0].type).toBe("separate");
|
|
383
|
+
if (tasks[0].type === "separate") {
|
|
384
|
+
expect(tasks[0].begin).toBe(25);
|
|
385
|
+
expect(tasks[0].end).toBe(30);
|
|
386
|
+
}
|
|
387
|
+
expect(tasks[1].type).toBe("separate");
|
|
388
|
+
if (tasks[1].type === "separate") {
|
|
389
|
+
expect(tasks[1].begin).toBe(15);
|
|
390
|
+
expect(tasks[1].end).toBe(20);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe("edge cases", () => {
|
|
395
|
+
it("should produce no tasks for empty region list", () => {
|
|
396
|
+
const tasks = runCreateTasks([], [createMask(0, 10)]);
|
|
397
|
+
expect(tasks).toHaveLength(0);
|
|
398
|
+
});
|
|
399
|
+
it("should produce no tasks when no regions overlap any mask", () => {
|
|
400
|
+
const region = createRegion(20, 5);
|
|
401
|
+
const tasks = runCreateTasks([region], [createMask(0, 10)]);
|
|
402
|
+
expect(tasks).toHaveLength(0);
|
|
403
|
+
});
|
|
404
|
+
it("should handle region touching mask boundary without overlap", () => {
|
|
405
|
+
const region = createRegion(0, 10);
|
|
406
|
+
const tasks = runCreateTasks([region], [createMask(10, 20)]);
|
|
407
|
+
expect(tasks).toHaveLength(0);
|
|
408
|
+
});
|
|
409
|
+
it("should handle region starting exactly at mask start", () => {
|
|
410
|
+
const region = createRegion(5, 10);
|
|
411
|
+
const tasks = runCreateTasks([region], [createMask(5, 20)]);
|
|
412
|
+
expect(tasks).toHaveLength(1);
|
|
413
|
+
expect(tasks[0].type).toBe("delete");
|
|
414
|
+
});
|
|
415
|
+
it("should handle region ending exactly at mask end", () => {
|
|
416
|
+
const region = createRegion(0, 20);
|
|
417
|
+
const tasks = runCreateTasks([region], [createMask(5, 20)]);
|
|
418
|
+
expect(tasks).toHaveLength(1);
|
|
419
|
+
expect(tasks[0].type).toBe("complete");
|
|
420
|
+
});
|
|
421
|
+
it("should handle multiple regions where some are skipped (selected) and some processed", () => {
|
|
422
|
+
const selected = createRegion(5, 5, true);
|
|
423
|
+
const unselected = createRegion(12, 5);
|
|
424
|
+
const tasks = runCreateTasks([selected, unselected], [createMask(0, 20)], false);
|
|
425
|
+
expect(tasks).toHaveLength(1);
|
|
426
|
+
expect(tasks[0].region).toBe(unselected);
|
|
427
|
+
});
|
|
428
|
+
it("should handle region spanning gap between two masks without overlapping either", () => {
|
|
429
|
+
const region = createRegion(11, 3);
|
|
430
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
431
|
+
expect(tasks).toHaveLength(0);
|
|
432
|
+
});
|
|
433
|
+
it("should break early for regions past maxComplete", () => {
|
|
434
|
+
const region1 = createRegion(5, 5);
|
|
435
|
+
const region2 = createRegion(50, 5);
|
|
436
|
+
const tasks = runCreateTasks([region1, region2], [createMask(0, 20)]);
|
|
437
|
+
expect(tasks).toHaveLength(1);
|
|
438
|
+
expect(tasks[0].region).toBe(region1);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
describe("task ordering for right-to-left multi-mask processing", () => {
|
|
442
|
+
it("should produce tasks in right-to-left order for execution correctness", () => {
|
|
443
|
+
const region = createRegion(3, 22);
|
|
444
|
+
const masks = [createMask(5, 10), createMask(15, 20)];
|
|
445
|
+
const tasks = runCreateTasks([region], masks);
|
|
446
|
+
expect(tasks).toHaveLength(2);
|
|
447
|
+
if (tasks[0].type === "separate") {
|
|
448
|
+
expect(tasks[0].begin).toBe(15);
|
|
449
|
+
}
|
|
450
|
+
if (tasks[1].type === "separate") {
|
|
451
|
+
expect(tasks[1].begin).toBe(5);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
it("should produce complete before separate (right-to-left) for region ending in right mask", () => {
|
|
455
|
+
const region = createRegion(3, 16);
|
|
456
|
+
const masks = [createMask(5, 10), createMask(15, 20)];
|
|
457
|
+
const tasks = runCreateTasks([region], masks);
|
|
458
|
+
expect(tasks).toHaveLength(2);
|
|
459
|
+
expect(tasks[0].type).toBe("complete");
|
|
460
|
+
expect(tasks[1].type).toBe("separate");
|
|
461
|
+
});
|
|
462
|
+
it("should produce separate before start (right-to-left) for region starting in left mask", () => {
|
|
463
|
+
const region = createRegion(7, 18);
|
|
464
|
+
const masks = [createMask(5, 10), createMask(15, 20)];
|
|
465
|
+
const tasks = runCreateTasks([region], masks);
|
|
466
|
+
expect(tasks).toHaveLength(2);
|
|
467
|
+
expect(tasks[0].type).toBe("separate");
|
|
468
|
+
expect(tasks[1].type).toBe("start");
|
|
469
|
+
});
|
|
470
|
+
it("should produce complete then start for region within gap boundaries", () => {
|
|
471
|
+
const region = createRegion(7, 12);
|
|
472
|
+
const masks = [createMask(5, 10), createMask(15, 20)];
|
|
473
|
+
const tasks = runCreateTasks([region], masks);
|
|
474
|
+
expect(tasks).toHaveLength(2);
|
|
475
|
+
expect(tasks[0].type).toBe("complete");
|
|
476
|
+
if (tasks[0].type === "complete") {
|
|
477
|
+
expect(tasks[0].position).toBe(15);
|
|
478
|
+
}
|
|
479
|
+
expect(tasks[1].type).toBe("start");
|
|
480
|
+
if (tasks[1].type === "start") {
|
|
481
|
+
expect(tasks[1].position).toBe(10);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
describe("execution correctness (simulated right-to-left processing)", () => {
|
|
486
|
+
it("separate + separate: region [3,25] with masks [5,10] and [15,20] produces valid segments", () => {
|
|
487
|
+
const region = createRegion(3, 22);
|
|
488
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
489
|
+
expect(tasks).toHaveLength(2);
|
|
490
|
+
// Right-to-left execution:
|
|
491
|
+
// 1. separate at [15,20]: region [3,25] -> [3,15] + new [20,25]
|
|
492
|
+
// 2. separate at [5,10]: region [3,15] -> [3,5] + new [10,15]
|
|
493
|
+
// Expected segments: [3,5], [10,15], [20,25]
|
|
494
|
+
// Verify task 1 (rightmost) is a separate at [15,20]
|
|
495
|
+
expect(tasks[0].type).toBe("separate");
|
|
496
|
+
if (tasks[0].type === "separate") {
|
|
497
|
+
expect(tasks[0].begin).toBe(15);
|
|
498
|
+
expect(tasks[0].end).toBe(20);
|
|
499
|
+
// After this, region.complete (originally 25) would become 15.
|
|
500
|
+
// complete - end = 25 - 20 = 5 > 0, valid.
|
|
501
|
+
}
|
|
502
|
+
// Verify task 2 (leftmost) is a separate at [5,10]
|
|
503
|
+
expect(tasks[1].type).toBe("separate");
|
|
504
|
+
if (tasks[1].type === "separate") {
|
|
505
|
+
expect(tasks[1].begin).toBe(5);
|
|
506
|
+
expect(tasks[1].end).toBe(10);
|
|
507
|
+
// After task 1, region is [3,15]. complete - end = 15 - 10 = 5 > 0, valid.
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
it("complete + separate: region [3,18] with masks [5,10] and [15,20] produces valid segments", () => {
|
|
511
|
+
const region = createRegion(3, 15);
|
|
512
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
513
|
+
expect(tasks).toHaveLength(2);
|
|
514
|
+
// 1. complete at 15: region [3,18] -> [3,15]
|
|
515
|
+
// 2. separate at [5,10]: region [3,15] -> [3,5] + new [10,15]
|
|
516
|
+
expect(tasks[0].type).toBe("complete");
|
|
517
|
+
if (tasks[0].type === "complete") {
|
|
518
|
+
expect(tasks[0].position).toBe(15);
|
|
519
|
+
}
|
|
520
|
+
expect(tasks[1].type).toBe("separate");
|
|
521
|
+
if (tasks[1].type === "separate") {
|
|
522
|
+
expect(tasks[1].begin).toBe(5);
|
|
523
|
+
expect(tasks[1].end).toBe(10);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
it("separate + start: region [7,25] with masks [5,10] and [15,20] produces valid segments", () => {
|
|
527
|
+
const region = createRegion(7, 18);
|
|
528
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
529
|
+
expect(tasks).toHaveLength(2);
|
|
530
|
+
// 1. separate at [15,20]: region [7,25] -> [7,15] + new [20,25]
|
|
531
|
+
// 2. start at 10: region [7,15] -> [10,15]
|
|
532
|
+
expect(tasks[0].type).toBe("separate");
|
|
533
|
+
if (tasks[0].type === "separate") {
|
|
534
|
+
expect(tasks[0].begin).toBe(15);
|
|
535
|
+
expect(tasks[0].end).toBe(20);
|
|
536
|
+
}
|
|
537
|
+
expect(tasks[1].type).toBe("start");
|
|
538
|
+
if (tasks[1].type === "start") {
|
|
539
|
+
expect(tasks[1].position).toBe(10);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
it("complete + start: region [5,20] with masks [5,10] and [15,20] leaves gap only", () => {
|
|
543
|
+
const region = createRegion(5, 15);
|
|
544
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
545
|
+
expect(tasks).toHaveLength(2);
|
|
546
|
+
// 1. complete at 15: region [5,20] -> [5,15]
|
|
547
|
+
// 2. start at 10: region [5,15] -> [10,15]
|
|
548
|
+
expect(tasks[0].type).toBe("complete");
|
|
549
|
+
expect(tasks[1].type).toBe("start");
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
describe("region fully within one mask of multiple masks", () => {
|
|
553
|
+
it("should delete region within first mask only", () => {
|
|
554
|
+
const region = createRegion(6, 3);
|
|
555
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
556
|
+
expect(tasks).toHaveLength(1);
|
|
557
|
+
expect(tasks[0].type).toBe("delete");
|
|
558
|
+
});
|
|
559
|
+
it("should delete region within second mask only", () => {
|
|
560
|
+
const region = createRegion(16, 3);
|
|
561
|
+
const tasks = runCreateTasks([region], [createMask(5, 10), createMask(15, 20)]);
|
|
562
|
+
expect(tasks).toHaveLength(1);
|
|
563
|
+
expect(tasks[0].type).toBe("delete");
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"YService.d.ts","sourceRoot":"","sources":["../../src/ysync/YService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAyC,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"YService.d.ts","sourceRoot":"","sources":["../../src/ysync/YService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAyC,MAAM,kBAAkB,CAAA;AAKvF,OAAO,EAAC,OAAO,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAA;AAOhE,yBAAiB,QAAQ,CAAC;IAMf,MAAM,eAAe,GAAU,YAAY,MAAM,CAAC,OAAO,CAAC,EAC3B,KAAK,UAAU,EACf,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CA4DvE,CAAA;CACJ"}
|
package/dist/ysync/YService.js
CHANGED
|
@@ -3,11 +3,10 @@ import { BoxGraph } from "@opendaw/lib-box";
|
|
|
3
3
|
import { Promises } from "@opendaw/lib-runtime";
|
|
4
4
|
import { BoxIO, UserInterfaceBox } from "@opendaw/studio-boxes";
|
|
5
5
|
import { ProjectSkeleton } from "@opendaw/studio-adapters";
|
|
6
|
+
import { Project, ProjectMigration } from "../project";
|
|
6
7
|
import { YSync } from "./YSync";
|
|
7
8
|
import * as Y from "yjs";
|
|
8
9
|
import { WebsocketProvider } from "y-websocket";
|
|
9
|
-
import { Project } from "../project";
|
|
10
|
-
import { ProjectMigration } from "../project/ProjectMigration";
|
|
11
10
|
// https://inspector.yjs.dev/
|
|
12
11
|
export var YService;
|
|
13
12
|
(function (YService) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendaw/studio-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
4
4
|
"license": "LGPL-3.0-or-later",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"test": "vitest run --config vitest.config.ts"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@opendaw/lib-box": "^0.0.
|
|
37
|
-
"@opendaw/lib-dawproject": "^0.0.
|
|
38
|
-
"@opendaw/lib-dom": "^0.0.
|
|
39
|
-
"@opendaw/lib-dsp": "^0.0.
|
|
40
|
-
"@opendaw/lib-fusion": "^0.0.
|
|
41
|
-
"@opendaw/lib-runtime": "^0.0.
|
|
42
|
-
"@opendaw/lib-std": "^0.0.
|
|
36
|
+
"@opendaw/lib-box": "^0.0.72",
|
|
37
|
+
"@opendaw/lib-dawproject": "^0.0.57",
|
|
38
|
+
"@opendaw/lib-dom": "^0.0.73",
|
|
39
|
+
"@opendaw/lib-dsp": "^0.0.71",
|
|
40
|
+
"@opendaw/lib-fusion": "^0.0.77",
|
|
41
|
+
"@opendaw/lib-runtime": "^0.0.69",
|
|
42
|
+
"@opendaw/lib-std": "^0.0.68",
|
|
43
43
|
"@opendaw/nam-wasm": "^1.0.3",
|
|
44
|
-
"@opendaw/studio-adapters": "^0.0.
|
|
45
|
-
"@opendaw/studio-boxes": "^0.0.
|
|
46
|
-
"@opendaw/studio-enums": "^0.0.
|
|
44
|
+
"@opendaw/studio-adapters": "^0.0.82",
|
|
45
|
+
"@opendaw/studio-boxes": "^0.0.74",
|
|
46
|
+
"@opendaw/studio-enums": "^0.0.63",
|
|
47
47
|
"dropbox": "^10.34.0",
|
|
48
48
|
"y-websocket": "^1.4.5",
|
|
49
49
|
"yjs": "^13.6.27",
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"@ffmpeg/ffmpeg": "^0.12.15",
|
|
58
58
|
"@ffmpeg/util": "^0.12.2",
|
|
59
59
|
"@opendaw/eslint-config": "^0.0.27",
|
|
60
|
-
"@opendaw/studio-core-processors": "^0.0.
|
|
61
|
-
"@opendaw/studio-core-workers": "^0.0.
|
|
62
|
-
"@opendaw/studio-forge-boxes": "^0.0.
|
|
60
|
+
"@opendaw/studio-core-processors": "^0.0.85",
|
|
61
|
+
"@opendaw/studio-core-workers": "^0.0.77",
|
|
62
|
+
"@opendaw/studio-forge-boxes": "^0.0.74",
|
|
63
63
|
"@opendaw/typescript-config": "^0.0.28"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "5e97da8bbe6085b04c34b9ff7e816b22225bf915"
|
|
66
66
|
}
|