@pixerate/editor 0.27.0 → 0.29.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 +44 -2
- package/dist/HistoryManager-DWSLd6Tf.d.ts +150 -0
- package/dist/HistoryManager-DeQQMBCj.d.cts +150 -0
- package/dist/agent/index.cjs +20 -427
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +4 -342
- package/dist/agent/index.d.ts +4 -342
- package/dist/agent/index.js +14 -421
- package/dist/agent/index.js.map +1 -1
- package/dist/animation/index.cjs +62 -0
- package/dist/animation/index.cjs.map +1 -0
- package/dist/animation/index.d.cts +226 -0
- package/dist/animation/index.d.ts +226 -0
- package/dist/animation/index.js +62 -0
- package/dist/animation/index.js.map +1 -0
- package/dist/{builtins-nMXh9sT3.d.cts → builtins-DxC3aUR2.d.cts} +2 -2
- package/dist/{builtins-haN82vjT.d.ts → builtins-NGxNxQfJ.d.ts} +2 -2
- package/dist/canvas/index.cjs +177 -743
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +6 -289
- package/dist/canvas/index.d.ts +6 -289
- package/dist/canvas/index.js +49 -615
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-36BLROHV.js +438 -0
- package/dist/chunk-36BLROHV.js.map +1 -0
- package/dist/{chunk-JT7AXQTE.js → chunk-3RPOW4CS.js} +7 -7
- package/dist/{chunk-AGR7EVRW.cjs → chunk-6MDQKHRH.cjs} +6 -6
- package/dist/{chunk-AGR7EVRW.cjs.map → chunk-6MDQKHRH.cjs.map} +1 -1
- package/dist/chunk-A6PHCAGX.cjs +87 -0
- package/dist/chunk-A6PHCAGX.cjs.map +1 -0
- package/dist/{chunk-HRMEDT2L.js → chunk-D6AVOTSD.js} +6 -85
- package/dist/chunk-D6AVOTSD.js.map +1 -0
- package/dist/{chunk-ARRTP33I.js → chunk-D6S5M7HV.js} +2 -2
- package/dist/{chunk-ARRTP33I.js.map → chunk-D6S5M7HV.js.map} +1 -1
- package/dist/{chunk-6IO7WUSU.js → chunk-DDADOHJW.js} +17 -17
- package/dist/{chunk-CPOKFNBG.cjs → chunk-I6BZB5ZH.cjs} +2 -2
- package/dist/{chunk-CPOKFNBG.cjs.map → chunk-I6BZB5ZH.cjs.map} +1 -1
- package/dist/{chunk-4BVP64JJ.cjs → chunk-K2UYKJ6M.cjs} +24 -103
- package/dist/chunk-K2UYKJ6M.cjs.map +1 -0
- package/dist/chunk-PIIAZV3L.cjs +244 -0
- package/dist/chunk-PIIAZV3L.cjs.map +1 -0
- package/dist/chunk-PWJARCI4.js +637 -0
- package/dist/chunk-PWJARCI4.js.map +1 -0
- package/dist/{chunk-G2I4QJOT.cjs → chunk-QKLRAA4T.cjs} +3 -244
- package/dist/chunk-QKLRAA4T.cjs.map +1 -0
- package/dist/{chunk-2H7ZHPEJ.js → chunk-QVZZGT4X.js} +1 -242
- package/dist/chunk-QVZZGT4X.js.map +1 -0
- package/dist/chunk-TGD52FEZ.js +87 -0
- package/dist/chunk-TGD52FEZ.js.map +1 -0
- package/dist/chunk-TTYCN6ZH.cjs +637 -0
- package/dist/chunk-TTYCN6ZH.cjs.map +1 -0
- package/dist/{chunk-YK5TATOF.cjs → chunk-U7VGDSNY.cjs} +3 -3
- package/dist/{chunk-YK5TATOF.cjs.map → chunk-U7VGDSNY.cjs.map} +1 -1
- package/dist/chunk-V7CC7RZ6.cjs +438 -0
- package/dist/chunk-V7CC7RZ6.cjs.map +1 -0
- package/dist/chunk-YK57D7PW.js +244 -0
- package/dist/chunk-YK57D7PW.js.map +1 -0
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +2 -2
- package/dist/commands/index.d.ts +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/easing-B6iAnJHk.d.cts +74 -0
- package/dist/easing-B6iAnJHk.d.ts +74 -0
- package/dist/extensions/index.cjs +5 -5
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +4 -4
- package/dist/generation/index.cjs +4 -4
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.js +3 -3
- package/dist/generation/index.js.map +1 -1
- package/dist/history/index.cjs +4 -2
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +3 -149
- package/dist/history/index.d.ts +3 -149
- package/dist/history/index.js +5 -3
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index.cjs +24 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +112 -110
- package/dist/index.js.map +1 -1
- package/dist/parameters/index.cjs +1283 -0
- package/dist/parameters/index.cjs.map +1 -0
- package/dist/parameters/index.d.cts +595 -0
- package/dist/parameters/index.d.ts +595 -0
- package/dist/parameters/index.js +1283 -0
- package/dist/parameters/index.js.map +1 -0
- package/dist/spreadsheet/index.cjs +4 -4
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.js +8 -8
- package/dist/tokens/index.cjs +4 -2
- package/dist/tokens/index.cjs.map +1 -1
- package/dist/tokens/index.js +6 -4
- package/dist/tools/index.cjs +31 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +343 -0
- package/dist/tools/index.d.ts +343 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/{types-GT5a5YAX.d.cts → types-DsiXGmjo.d.cts} +1 -1
- package/dist/{types-GT5a5YAX.d.ts → types-DsiXGmjo.d.ts} +1 -1
- package/package.json +16 -1
- package/dist/chunk-2H7ZHPEJ.js.map +0 -1
- package/dist/chunk-4BVP64JJ.cjs.map +0 -1
- package/dist/chunk-G2I4QJOT.cjs.map +0 -1
- package/dist/chunk-HRMEDT2L.js.map +0 -1
- /package/dist/{chunk-JT7AXQTE.js.map → chunk-3RPOW4CS.js.map} +0 -0
- /package/dist/{chunk-6IO7WUSU.js.map → chunk-DDADOHJW.js.map} +0 -0
|
@@ -514,249 +514,8 @@ function createHistoryManager(options = {}) {
|
|
|
514
514
|
return new HistoryManager(options);
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
-
// src/dirty.ts
|
|
518
|
-
function defaultIsEqual(a, b) {
|
|
519
|
-
if (Object.is(a, b)) return true;
|
|
520
|
-
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
524
|
-
if (a.length !== b.length) return false;
|
|
525
|
-
for (let i = 0; i < a.length; i++) {
|
|
526
|
-
if (!defaultIsEqual(a[i], b[i])) return false;
|
|
527
|
-
}
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
if (Array.isArray(a) !== Array.isArray(b)) {
|
|
531
|
-
return false;
|
|
532
|
-
}
|
|
533
|
-
const keysA = Object.keys(a);
|
|
534
|
-
const keysB = Object.keys(b);
|
|
535
|
-
if (keysA.length !== keysB.length) return false;
|
|
536
|
-
for (const key of keysA) {
|
|
537
|
-
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
538
|
-
if (!defaultIsEqual(a[key], b[key])) return false;
|
|
539
|
-
}
|
|
540
|
-
return true;
|
|
541
|
-
}
|
|
542
|
-
var isThenable = (value) => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
543
|
-
var DirtyTracker = class {
|
|
544
|
-
baseline;
|
|
545
|
-
isEqual;
|
|
546
|
-
normalize;
|
|
547
|
-
normalizedBaseline = null;
|
|
548
|
-
/** `isDirty` result as of a revision (and baseline version). */
|
|
549
|
-
cache = null;
|
|
550
|
-
baselineVersion = 0;
|
|
551
|
-
suppressDepth = 0;
|
|
552
|
-
hasValue = false;
|
|
553
|
-
lastValue;
|
|
554
|
-
dirtyState = false;
|
|
555
|
-
listeners = /* @__PURE__ */ new Set();
|
|
556
|
-
constructor(initialBaseline, options) {
|
|
557
|
-
this.baseline = initialBaseline;
|
|
558
|
-
if (typeof options === "function") {
|
|
559
|
-
this.isEqual = options;
|
|
560
|
-
} else {
|
|
561
|
-
this.isEqual = options?.isEqual ?? defaultIsEqual;
|
|
562
|
-
this.normalize = options?.normalize;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Returns current baseline value.
|
|
567
|
-
*/
|
|
568
|
-
getBaseline() {
|
|
569
|
-
return this.baseline;
|
|
570
|
-
}
|
|
571
|
-
changeBaseline(value) {
|
|
572
|
-
this.baseline = value;
|
|
573
|
-
this.baselineVersion++;
|
|
574
|
-
this.normalizedBaseline = null;
|
|
575
|
-
this.cache = null;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Sets a new baseline value without triggering any dirty state.
|
|
579
|
-
* If a value is being tracked (`update`), `dirty` is recomputed against it.
|
|
580
|
-
*/
|
|
581
|
-
setBaseline(value) {
|
|
582
|
-
this.changeBaseline(value);
|
|
583
|
-
if (this.hasValue) {
|
|
584
|
-
this.setDirtyState(this.compare(this.lastValue));
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
compare(current) {
|
|
588
|
-
if (!this.normalize) return !this.isEqual(this.baseline, current);
|
|
589
|
-
this.normalizedBaseline ??= { value: this.normalize(this.baseline) };
|
|
590
|
-
return !this.isEqual(
|
|
591
|
-
this.normalizedBaseline.value,
|
|
592
|
-
this.normalize(current)
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
/**
|
|
596
|
-
* Checks whether the given value differs from the baseline.
|
|
597
|
-
* With a `revision` (e.g. `HistoryManager.getRevision()`), the result is
|
|
598
|
-
* cached and reused for the same revision until the baseline changes.
|
|
599
|
-
*/
|
|
600
|
-
isDirty(current, revision) {
|
|
601
|
-
if (revision === void 0) return this.compare(current);
|
|
602
|
-
const cache = this.cache;
|
|
603
|
-
if (cache && cache.revision === revision && cache.baseline === this.baselineVersion) {
|
|
604
|
-
return cache.dirty;
|
|
605
|
-
}
|
|
606
|
-
const dirty = this.compare(current);
|
|
607
|
-
this.cache = { revision, baseline: this.baselineVersion, dirty };
|
|
608
|
-
return dirty;
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Resets the baseline to the provided current value (or clears dirty status).
|
|
612
|
-
*/
|
|
613
|
-
reset(current) {
|
|
614
|
-
this.changeBaseline(current);
|
|
615
|
-
this.hasValue = true;
|
|
616
|
-
this.lastValue = current;
|
|
617
|
-
this.setDirtyState(false);
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* Records the current value and returns whether it is dirty. While
|
|
621
|
-
* suppressed (`suppress`), the value becomes the new baseline instead, so
|
|
622
|
-
* programmatic loads and restores never mark the document dirty.
|
|
623
|
-
*/
|
|
624
|
-
update(current, revision) {
|
|
625
|
-
this.hasValue = true;
|
|
626
|
-
this.lastValue = current;
|
|
627
|
-
if (this.suppressDepth > 0) {
|
|
628
|
-
this.changeBaseline(current);
|
|
629
|
-
this.setDirtyState(false);
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
const dirty = this.isDirty(current, revision);
|
|
633
|
-
this.setDirtyState(dirty);
|
|
634
|
-
return dirty;
|
|
635
|
-
}
|
|
636
|
-
/** Whether the last value passed to `update` differs from the baseline. */
|
|
637
|
-
get dirty() {
|
|
638
|
-
return this.dirtyState;
|
|
639
|
-
}
|
|
640
|
-
/** Whether `suppress` is currently running. */
|
|
641
|
-
get suppressed() {
|
|
642
|
-
return this.suppressDepth > 0;
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Marks the document saved: `current` (default: the last value passed to
|
|
646
|
-
* `update`) becomes the baseline.
|
|
647
|
-
*/
|
|
648
|
-
markClean(...args) {
|
|
649
|
-
if (args.length > 0) {
|
|
650
|
-
this.reset(args[0]);
|
|
651
|
-
} else if (this.hasValue) {
|
|
652
|
-
this.reset(this.lastValue);
|
|
653
|
-
} else {
|
|
654
|
-
this.setDirtyState(false);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Runs `fn` without marking anything dirty: every `update` during it
|
|
659
|
-
* rebaselines. Re-entrant (depth counter). If `fn` returns a promise, the
|
|
660
|
-
* suppression lasts until it settles.
|
|
661
|
-
*/
|
|
662
|
-
suppress(fn) {
|
|
663
|
-
this.suppressDepth++;
|
|
664
|
-
let result;
|
|
665
|
-
try {
|
|
666
|
-
result = fn();
|
|
667
|
-
} catch (error) {
|
|
668
|
-
this.suppressDepth--;
|
|
669
|
-
throw error;
|
|
670
|
-
}
|
|
671
|
-
if (isThenable(result)) {
|
|
672
|
-
const release = () => {
|
|
673
|
-
this.suppressDepth--;
|
|
674
|
-
};
|
|
675
|
-
return result.then(
|
|
676
|
-
(value) => {
|
|
677
|
-
release();
|
|
678
|
-
return value;
|
|
679
|
-
},
|
|
680
|
-
(error) => {
|
|
681
|
-
release();
|
|
682
|
-
throw error;
|
|
683
|
-
}
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
this.suppressDepth--;
|
|
687
|
-
return result;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Subscribes to changes of `dirty`. The listener is called immediately with
|
|
691
|
-
* the current value. Returns an unsubscribe function.
|
|
692
|
-
*/
|
|
693
|
-
subscribe(listener) {
|
|
694
|
-
this.listeners.add(listener);
|
|
695
|
-
listener(this.dirtyState);
|
|
696
|
-
return () => {
|
|
697
|
-
this.listeners.delete(listener);
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
setDirtyState(dirty) {
|
|
701
|
-
if (dirty === this.dirtyState) return;
|
|
702
|
-
this.dirtyState = dirty;
|
|
703
|
-
this.listeners.forEach((listener) => {
|
|
704
|
-
try {
|
|
705
|
-
listener(dirty);
|
|
706
|
-
} catch (err) {
|
|
707
|
-
console.error("[DirtyTracker] Error in listener:", err);
|
|
708
|
-
}
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
};
|
|
712
|
-
function createDirtyTracker(initialBaseline, options) {
|
|
713
|
-
return new DirtyTracker(initialBaseline, options);
|
|
714
|
-
}
|
|
715
|
-
function withoutDirty(tracker, fn) {
|
|
716
|
-
return tracker.suppress(fn);
|
|
717
|
-
}
|
|
718
|
-
function bindDirtyToHistory(options) {
|
|
719
|
-
const { history, getValue } = options;
|
|
720
|
-
const tracker = options.tracker ?? new DirtyTracker(getValue(), {
|
|
721
|
-
isEqual: options.isEqual,
|
|
722
|
-
normalize: options.normalize
|
|
723
|
-
});
|
|
724
|
-
const unsubscribe = history.subscribe(() => {
|
|
725
|
-
tracker.update(getValue(), history.getRevision?.());
|
|
726
|
-
});
|
|
727
|
-
return {
|
|
728
|
-
tracker,
|
|
729
|
-
get dirty() {
|
|
730
|
-
return tracker.dirty;
|
|
731
|
-
},
|
|
732
|
-
markClean: () => tracker.markClean(getValue()),
|
|
733
|
-
suppress: (fn) => {
|
|
734
|
-
const rebaseline = () => {
|
|
735
|
-
if (!tracker.suppressed) tracker.reset(getValue());
|
|
736
|
-
};
|
|
737
|
-
const result = tracker.suppress(fn);
|
|
738
|
-
if (isThenable(result)) {
|
|
739
|
-
return result.then((value) => {
|
|
740
|
-
rebaseline();
|
|
741
|
-
return value;
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
rebaseline();
|
|
745
|
-
return result;
|
|
746
|
-
},
|
|
747
|
-
refresh: () => tracker.update(getValue()),
|
|
748
|
-
subscribe: (listener) => tracker.subscribe(listener),
|
|
749
|
-
destroy: unsubscribe
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
|
|
753
517
|
export {
|
|
754
|
-
defaultIsEqual,
|
|
755
|
-
DirtyTracker,
|
|
756
|
-
createDirtyTracker,
|
|
757
|
-
withoutDirty,
|
|
758
|
-
bindDirtyToHistory,
|
|
759
518
|
HistoryManager,
|
|
760
519
|
createHistoryManager
|
|
761
520
|
};
|
|
762
|
-
//# sourceMappingURL=chunk-
|
|
521
|
+
//# sourceMappingURL=chunk-QVZZGT4X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/history/HistoryManager.ts"],"sourcesContent":["// Portions ported from filmcraft-main/crates/engine/src/lib.rs, soundcraft-main/crates/engine/src/lib.rs\n// and photocraft-main/crates/ops/src/lib.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors).\n// See THIRD_PARTY_NOTICES.md.\nimport {\n Command,\n HistoryEntry,\n HistoryManagerOptions,\n HistoryMark,\n HistoryState,\n MergedExecutionOptions,\n SnapshotCommandOptions,\n} from \"./types\";\n\n/** One reversible piece of a step. A gesture step holds one part per distinct command. */\ninterface Part {\n undo: () => void;\n redo: () => void;\n tag?: string;\n bytes: number;\n}\n\n/** One undo step. */\ninterface Entry {\n id: number;\n name: string;\n tag?: string;\n timestamp: number;\n parts: Part[];\n bytes: number;\n}\n\nconst sumBytes = (parts: Part[]) => parts.reduce((n, p) => n + p.bytes, 0);\n\nfunction undoParts(parts: Part[]): void {\n for (let i = parts.length - 1; i >= 0; i--) parts[i].undo();\n}\n\nfunction redoParts(parts: Part[]): void {\n for (let i = 0; i < parts.length; i++) parts[i].redo();\n}\n\n/**\n * UI-agnostic History and Undo/Redo manager supporting command closures,\n * snapshot diffs, transaction batching, high-frequency event coalescing,\n * keyed gestures, undo marks, a memory budget and a History-panel listing.\n */\nexport class HistoryManager {\n private undoStack: Entry[] = [];\n private redoStack: Entry[] = [];\n private readonly maxDepth: number;\n private readonly maxBytes: number;\n private readonly sizeOf?: (command: Command) => number;\n private isApplying = false;\n private batchDepth = 0;\n private currentBatch: Command[] | null = null;\n private listeners: Set<(state: HistoryState) => void> = new Set();\n private onStateChange?: (state: HistoryState) => void;\n private cachedState: HistoryState;\n private cachedEntries: HistoryEntry[] | null = null;\n\n private nextId = 1;\n private revision = 0;\n /** Bumped by `clear()`; marks from an earlier epoch are stale. */\n private epoch = 0;\n /** Number of steps dropped from the bottom (depth / byte budget) so far. */\n private dropped = 0;\n /** Key of the gesture that the top step belongs to (see `executeMerged`). */\n private gestureKey: string | null = null;\n private gestureEntryId: number | null = null;\n /** Key every merged execute takes while a gesture is held (`beginGesture`). */\n private heldKey: string | null = null;\n\n constructor(options: HistoryManagerOptions = {}) {\n this.maxDepth = options.maxDepth ?? 50;\n this.maxBytes = Math.max(0, options.maxBytes ?? 0);\n this.sizeOf = options.sizeOf;\n this.onStateChange = options.onStateChange;\n this.cachedState = this.computeState();\n }\n\n private computeState(): HistoryState {\n const canUndo = this.undoStack.length > 0;\n const canRedo = this.redoStack.length > 0;\n return {\n canUndo,\n canRedo,\n undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : \"\",\n redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : \"\",\n undoCount: this.undoStack.length,\n redoCount: this.redoStack.length,\n };\n }\n\n /**\n * Returns a snapshot of the current history state.\n * Cached to maintain referential equality across renders for useSyncExternalStore.\n */\n public getState(): HistoryState {\n return this.cachedState;\n }\n\n /**\n * Monotonic counter bumped on every history change (execute, merge, undo,\n * redo, revert, clear). Useful as a cache key, e.g. for `DirtyTracker`.\n */\n public getRevision(): number {\n return this.revision;\n }\n\n private notify(): void {\n this.revision++;\n this.cachedState = this.computeState();\n this.cachedEntries = null;\n const state = this.cachedState;\n this.onStateChange?.(state);\n this.listeners.forEach((listener) => {\n try {\n listener(state);\n } catch (err) {\n console.error(\"[HistoryManager] Error in state listener:\", err);\n }\n });\n }\n\n private measure(command: Command): number {\n const n = this.sizeOf ? this.sizeOf(command) : (command.byteSize ?? 0);\n return Number.isFinite(n) && n > 0 ? n : 0;\n }\n\n private makeEntry(command: Command, timestamp: number): Entry {\n const part: Part = {\n undo: command.undo,\n redo: command.redo ?? command.execute,\n tag: command.tag,\n bytes: this.measure(command),\n };\n return {\n id: this.nextId++,\n name: command.name,\n tag: command.tag,\n timestamp,\n parts: [part],\n bytes: part.bytes,\n };\n }\n\n /** Pushes a step, clears redo and applies the depth and byte limits. */\n private record(entry: Entry): void {\n this.undoStack.push(entry);\n while (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n this.dropped++;\n }\n this.redoStack = [];\n this.trim();\n }\n\n /**\n * Keeps the stacks within `maxBytes`: the oldest undo steps that do not fit\n * are dropped, but the most recent undo step is always kept.\n */\n private trim(): void {\n if (this.maxBytes <= 0 || this.undoStack.length <= 1) return;\n let total = this.getByteSize();\n while (total > this.maxBytes && this.undoStack.length > 1) {\n total -= this.undoStack.shift()!.bytes;\n this.dropped++;\n }\n }\n\n /** Approximate bytes held by the undo and redo stacks (see `maxBytes`). */\n public getByteSize(): number {\n let total = 0;\n for (const e of this.undoStack) total += e.bytes;\n for (const e of this.redoStack) total += e.bytes;\n return total;\n }\n\n private resetGesture(): void {\n this.gestureKey = null;\n this.gestureEntryId = null;\n }\n\n /**\n * Executes a command and pushes it onto the undo stack.\n * Clears the redo stack and ends the current gesture.\n */\n public execute(command: Command): void {\n if (this.isApplying) {\n command.execute();\n return;\n }\n\n if (this.batchDepth > 0 && this.currentBatch) {\n command.execute();\n this.currentBatch.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n return;\n }\n\n command.execute();\n this.record(this.makeEntry(command, command.timestamp ?? Date.now()));\n this.resetGesture();\n this.notify();\n }\n\n /**\n * Executes a command as part of a continuous interaction (slider scrub, node drag).\n *\n * - With `options.key` (or while a gesture is held with `beginGesture`):\n * consecutive merged executes with the same key fold into one undo step,\n * however long the gesture lasts. Recording any other command, undo, redo,\n * `revertTo`, `goTo`, `clear` or `endGesture()` ends the gesture.\n * - Without a key: coalesces with the previous command if they share the same\n * `tag` and occurred within `windowMs` (default: 300ms).\n */\n public executeMerged(\n command: Command,\n options: MergedExecutionOptions = {},\n ): void {\n const now = Date.now();\n const key = this.heldKey ?? options.key;\n\n if (key !== undefined) {\n if (this.isApplying || this.batchDepth > 0) {\n this.execute({ ...command, timestamp: now });\n return;\n }\n const top = this.undoStack[this.undoStack.length - 1];\n if (top && this.gestureKey === key && this.gestureEntryId === top.id) {\n command.execute();\n const redo = command.redo ?? command.execute;\n const bytes = this.measure(command);\n const last = top.parts[top.parts.length - 1];\n if (last.tag !== undefined && last.tag === command.tag) {\n // Same (absolute) command again: keep the pre-gesture undo, take the new redo.\n top.parts[top.parts.length - 1] = { undo: last.undo, redo, tag: last.tag, bytes };\n } else {\n top.parts.push({ undo: command.undo, redo, tag: command.tag, bytes });\n }\n top.bytes = sumBytes(top.parts);\n top.timestamp = now;\n this.redoStack = [];\n this.trim();\n this.notify();\n return;\n }\n this.execute({ ...command, timestamp: now });\n const pushed = this.undoStack[this.undoStack.length - 1];\n if (pushed) {\n this.gestureKey = key;\n this.gestureEntryId = pushed.id;\n }\n return;\n }\n\n const windowMs = options.windowMs ?? 300;\n\n if (\n command.tag &&\n this.undoStack.length > 0 &&\n this.batchDepth === 0 &&\n !this.isApplying\n ) {\n const top = this.undoStack[this.undoStack.length - 1];\n const elapsed = top.timestamp ? now - top.timestamp : Infinity;\n\n if (top.tag === command.tag && elapsed <= windowMs) {\n command.execute();\n // Replace top with merged command keeping the original initial undo\n const initialParts = top.parts;\n const bytes = this.measure(command);\n this.undoStack[this.undoStack.length - 1] = {\n id: top.id,\n name: command.name,\n tag: command.tag,\n timestamp: now,\n parts: [\n {\n undo: () => undoParts(initialParts),\n redo: command.redo ?? command.execute,\n tag: command.tag,\n bytes,\n },\n ],\n bytes,\n };\n this.redoStack = [];\n this.resetGesture();\n this.trim();\n this.notify();\n return;\n }\n }\n\n this.execute({ ...command, timestamp: now });\n }\n\n /**\n * Holds a gesture: until `endGesture()`, every `executeMerged` call (whatever\n * its own key or tag) joins one undo step, e.g. an automation pass and\n * whatever is dragged during it. Commands recorded with `execute` still end\n * the current step; the next merged execute then starts a new one.\n */\n public beginGesture(key: string): void {\n if (this.heldKey === key) return;\n this.heldKey = key;\n this.resetGesture();\n }\n\n /** Ends the current (keyed or held) gesture; the next merged execute starts a new step. */\n public endGesture(): void {\n this.heldKey = null;\n this.resetGesture();\n }\n\n /** The key of the gesture currently merging into the top step, if any. */\n public getGestureKey(): string | null {\n return this.heldKey ?? this.gestureKey;\n }\n\n /**\n * Batches multiple commands executed within `fn` into a single atomic undo entry.\n */\n public batch(name: string, fn: () => void): void {\n if (this.isApplying) {\n fn();\n return;\n }\n\n if (this.batchDepth > 0) {\n // Already inside a batch; execute directly\n fn();\n return;\n }\n\n this.batchDepth++;\n this.currentBatch = [];\n\n try {\n fn();\n } catch (error) {\n const executed = this.currentBatch ?? [];\n this.batchDepth--;\n this.currentBatch = null;\n this.rollback(executed);\n throw error;\n }\n\n this.batchDepth--;\n const batchCommands: Command[] = this.currentBatch ?? [];\n this.currentBatch = null;\n\n if (batchCommands.length > 0) {\n const run = () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n };\n const bytes = batchCommands.reduce((n, c) => n + this.measure(c), 0);\n this.record({\n id: this.nextId++,\n name,\n timestamp: Date.now(),\n parts: [\n {\n undo: () => {\n for (let i = batchCommands.length - 1; i >= 0; i--) {\n batchCommands[i].undo();\n }\n },\n redo: run,\n bytes,\n },\n ],\n bytes,\n });\n this.resetGesture();\n this.notify();\n }\n }\n\n /**\n * Reverts already-executed commands of an aborted batch, newest first.\n * Commands executed by the undo closures themselves are not recorded.\n */\n private rollback(commands: Command[]): void {\n const wasApplying = this.isApplying;\n this.isApplying = true;\n try {\n for (let i = commands.length - 1; i >= 0; i--) {\n try {\n commands[i].undo();\n } catch (err) {\n console.error(\n `[HistoryManager] Error rolling back \"${commands[i].name}\" of a failed batch:`,\n err,\n );\n }\n }\n } finally {\n this.isApplying = wasApplying;\n }\n }\n\n /** Undoes the top step (caller holds `isApplying`). Throws leave it on the stack. */\n private undoOne(): void {\n const entry = this.undoStack[this.undoStack.length - 1];\n undoParts(entry.parts);\n this.undoStack.pop();\n this.redoStack.push(entry);\n }\n\n private redoOne(): void {\n const entry = this.redoStack[this.redoStack.length - 1];\n redoParts(entry.parts);\n this.redoStack.pop();\n this.undoStack.push(entry);\n }\n\n /**\n * Reverts the most recent command on the undo stack.\n * Returns true if a command was undone, false otherwise.\n */\n public undo(): boolean {\n if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n this.resetGesture();\n try {\n // If undo throws, the command stays on the undo stack and the error propagates.\n this.undoOne();\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Re-applies the most recent undone command on the redo stack.\n * Returns true if a command was redone, false otherwise.\n */\n public redo(): boolean {\n if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n this.resetGesture();\n try {\n // If redo throws, the command stays on the redo stack and the error propagates.\n this.redoOne();\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Captures the current point in the history (its top step), for `revertTo`.\n */\n public mark(): HistoryMark {\n const top = this.undoStack[this.undoStack.length - 1];\n return {\n topId: top ? top.id : null,\n nextId: this.nextId,\n epoch: this.epoch,\n dropped: this.dropped,\n };\n }\n\n /** Whether `mark` still points into the undo history. */\n public isMarkValid(mark: HistoryMark): boolean {\n if (mark.epoch !== this.epoch) return false;\n if (mark.topId === null) return mark.dropped === this.dropped;\n return this.undoStack.some((e) => e.id === mark.topId);\n }\n\n /**\n * Takes back every step made since `mark` (merged or not), e.g. to cancel a\n * drag with Escape: the state returns to how it was at `mark`, and neither\n * undo nor redo keeps the reverted steps. Does nothing when `mark` has left\n * the history (undone past, dropped by the limits, or cleared).\n * Returns how many steps were taken back.\n */\n public revertTo(mark: HistoryMark): number {\n if (this.isApplying || this.batchDepth > 0 || !this.isMarkValid(mark)) {\n return 0;\n }\n const redoBefore = this.redoStack.length;\n let n = 0;\n this.isApplying = true;\n this.resetGesture();\n try {\n while (this.undoStack.length > 0) {\n const top = this.undoStack[this.undoStack.length - 1];\n if (top.id === mark.topId) break;\n undoParts(top.parts);\n this.undoStack.pop();\n n++;\n }\n } finally {\n this.isApplying = false;\n // Steps made after the mark are gone for good, including undone ones.\n this.redoStack =\n n > 0 ? [] : this.redoStack.filter((e) => e.id < mark.nextId);\n if (n > 0 || this.redoStack.length !== redoBefore) this.notify();\n }\n return n;\n }\n\n /**\n * Lists the steps for a History panel: undo steps oldest first, then redo\n * steps (the next redo first). Cached until the history changes.\n */\n public getEntries(): HistoryEntry[] {\n if (this.cachedEntries) return this.cachedEntries;\n const u = this.undoStack.length;\n const entries: HistoryEntry[] = this.undoStack.map((e, index) => ({\n id: e.id,\n label: e.name,\n index,\n current: index === u - 1,\n undone: false,\n }));\n for (let i = this.redoStack.length - 1; i >= 0; i--) {\n const e = this.redoStack[i];\n entries.push({\n id: e.id,\n label: e.name,\n index: entries.length,\n current: false,\n undone: true,\n });\n }\n this.cachedEntries = entries;\n return entries;\n }\n\n /**\n * Undoes or redoes until the step at `index` (from `getEntries`) is the\n * current one; `-1` undoes everything. Returns how many steps were applied.\n */\n public goTo(index: number): number {\n if (this.isApplying || this.batchDepth > 0) return 0;\n const total = this.undoStack.length + this.redoStack.length;\n const target = Math.max(0, Math.min(total, Math.floor(index) + 1));\n if (target === this.undoStack.length) return 0;\n let n = 0;\n this.isApplying = true;\n this.resetGesture();\n try {\n while (this.undoStack.length > target) {\n this.undoOne();\n n++;\n }\n while (this.undoStack.length < target) {\n this.redoOne();\n n++;\n }\n } finally {\n this.isApplying = false;\n this.notify();\n }\n return n;\n }\n\n /**\n * Clears both undo and redo stacks.\n */\n public clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.epoch++;\n this.resetGesture();\n this.notify();\n }\n\n /**\n * Subscribes to history state changes.\n * The listener is immediately invoked with the current state.\n * Returns an unsubscribe function.\n */\n public subscribe(listener: (state: HistoryState) => void): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Utility helper to create a Command from before/after state snapshots.\n */\n public static createSnapshotCommand<T>(\n options: SnapshotCommandOptions<T>,\n ): Command {\n return {\n name: options.name,\n tag: options.tag,\n execute: () => options.apply(options.after),\n undo: () => options.apply(options.before),\n redo: () => options.apply(options.after),\n };\n }\n}\n\n/**\n * Creates and initializes a new HistoryManager instance.\n */\nexport function createHistoryManager(\n options: HistoryManagerOptions = {},\n): HistoryManager {\n return new HistoryManager(options);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/tokens/color.ts
|
|
2
|
+
var HEX = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
3
|
+
var RGB = /^rgba?\(([^()]*)\)$/i;
|
|
4
|
+
var clamp01 = (v) => Math.min(1, Math.max(0, v));
|
|
5
|
+
var byte = (v) => Math.round(clamp01(v) * 255);
|
|
6
|
+
var hex2 = (n) => n.toString(16).padStart(2, "0");
|
|
7
|
+
function rgbaToHex(r, g, b, a = 1) {
|
|
8
|
+
const head = `#${hex2(byte(r))}${hex2(byte(g))}${hex2(byte(b))}`;
|
|
9
|
+
const alpha = byte(a);
|
|
10
|
+
return alpha === 255 ? head : head + hex2(alpha);
|
|
11
|
+
}
|
|
12
|
+
function hexToRgba(hex) {
|
|
13
|
+
const normal = normalizeTokenColor(hex);
|
|
14
|
+
if (normal === null) return null;
|
|
15
|
+
const n = (i) => parseInt(normal.slice(i, i + 2), 16) / 255;
|
|
16
|
+
return { r: n(1), g: n(3), b: n(5), a: normal.length === 9 ? n(7) : 1 };
|
|
17
|
+
}
|
|
18
|
+
function channel(part, scale) {
|
|
19
|
+
const pct = part.endsWith("%");
|
|
20
|
+
const n = Number(pct ? part.slice(0, -1) : part);
|
|
21
|
+
if (!Number.isFinite(n)) return null;
|
|
22
|
+
return pct ? n / 100 : n / scale;
|
|
23
|
+
}
|
|
24
|
+
function normalizeTokenColor(input) {
|
|
25
|
+
if (typeof input === "string") {
|
|
26
|
+
const text = input.trim();
|
|
27
|
+
if (text.length > 200) return null;
|
|
28
|
+
const lower = text.toLowerCase();
|
|
29
|
+
if (lower === "transparent") return "#00000000";
|
|
30
|
+
if (lower === "black") return "#000000";
|
|
31
|
+
if (lower === "white") return "#ffffff";
|
|
32
|
+
const hex = HEX.exec(text);
|
|
33
|
+
if (hex) {
|
|
34
|
+
let digits = hex[1].toLowerCase();
|
|
35
|
+
if (digits.length <= 4) digits = [...digits].map((c) => c + c).join("");
|
|
36
|
+
if (digits.length === 8 && digits.endsWith("ff")) digits = digits.slice(0, 6);
|
|
37
|
+
return `#${digits}`;
|
|
38
|
+
}
|
|
39
|
+
const rgb = RGB.exec(text);
|
|
40
|
+
if (rgb) {
|
|
41
|
+
const [colour, alphaPart] = rgb[1].split("/");
|
|
42
|
+
const parts = colour.split(/[\s,]+/).filter(Boolean);
|
|
43
|
+
let alpha = alphaPart?.trim();
|
|
44
|
+
if (parts.length === 4 && alpha === void 0) alpha = parts.pop();
|
|
45
|
+
if (parts.length !== 3) return null;
|
|
46
|
+
const channels = parts.map((p) => channel(p, 255));
|
|
47
|
+
const a = alpha === void 0 || alpha === "" ? 1 : channel(alpha, 1);
|
|
48
|
+
if (channels.some((c) => c === null) || a === null) return null;
|
|
49
|
+
return rgbaToHex(channels[0], channels[1], channels[2], a);
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (typeof input === "object" && input !== null) {
|
|
54
|
+
const obj = input;
|
|
55
|
+
if (Array.isArray(obj.components)) {
|
|
56
|
+
const space = obj.colorSpace;
|
|
57
|
+
if (space === void 0 || space === "srgb") {
|
|
58
|
+
const [r2, g2, b2] = obj.components;
|
|
59
|
+
const a2 = obj.alpha === void 0 ? 1 : obj.alpha;
|
|
60
|
+
if ([r2, g2, b2, a2].every((v) => typeof v === "number" && Number.isFinite(v))) {
|
|
61
|
+
return rgbaToHex(r2, g2, b2, a2);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (typeof obj.hex === "string") {
|
|
65
|
+
const base = normalizeTokenColor(obj.hex);
|
|
66
|
+
if (base === null) return null;
|
|
67
|
+
const a2 = typeof obj.alpha === "number" ? obj.alpha : 1;
|
|
68
|
+
const c = hexToRgba(base);
|
|
69
|
+
return rgbaToHex(c.r, c.g, c.b, a2);
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const { r, g, b } = obj;
|
|
74
|
+
const a = obj.a === void 0 ? 1 : obj.a;
|
|
75
|
+
if ([r, g, b, a].every((v) => typeof v === "number" && Number.isFinite(v))) {
|
|
76
|
+
return rgbaToHex(r, g, b, a);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
rgbaToHex,
|
|
84
|
+
hexToRgba,
|
|
85
|
+
normalizeTokenColor
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=chunk-TGD52FEZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tokens/color.ts"],"sourcesContent":["/**\n * Colour parsing for colour tokens. Colours are stored as lowercase hex: `#rrggbb`, or\n * `#rrggbbaa` when not opaque. Accepted input: `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`,\n * `rgb()` / `rgba()` (numbers or percentages, comma or space separated, `/ alpha`),\n * `transparent`, `black`, `white`, `{ r, g, b, a? }` with channels in 0–1 (Figma JSON) and\n * W3C colour objects (`{ colorSpace: 'srgb', components: [r, g, b], alpha?, hex? }`).\n */\n\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;\n// Anchored with one character-class run: linear time.\nconst RGB = /^rgba?\\(([^()]*)\\)$/i;\n\nconst clamp01 = (v: number) => Math.min(1, Math.max(0, v));\nconst byte = (v: number) => Math.round(clamp01(v) * 255);\nconst hex2 = (n: number) => n.toString(16).padStart(2, '0');\n\n/** `#rrggbb` / `#rrggbbaa` from 0–1 channels. */\nexport function rgbaToHex(r: number, g: number, b: number, a = 1): string {\n\tconst head = `#${hex2(byte(r))}${hex2(byte(g))}${hex2(byte(b))}`;\n\tconst alpha = byte(a);\n\treturn alpha === 255 ? head : head + hex2(alpha);\n}\n\n/** 0–1 channels of a normalised hex colour. */\nexport function hexToRgba(hex: string): { r: number; g: number; b: number; a: number } | null {\n\tconst normal = normalizeTokenColor(hex);\n\tif (normal === null) return null;\n\tconst n = (i: number) => parseInt(normal.slice(i, i + 2), 16) / 255;\n\treturn { r: n(1), g: n(3), b: n(5), a: normal.length === 9 ? n(7) : 1 };\n}\n\nfunction channel(part: string, scale: number): number | null {\n\tconst pct = part.endsWith('%');\n\tconst n = Number(pct ? part.slice(0, -1) : part);\n\tif (!Number.isFinite(n)) return null;\n\treturn pct ? n / 100 : n / scale;\n}\n\n/** The colour as normalised hex, or `null` when it is not a colour this module reads. */\nexport function normalizeTokenColor(input: unknown): string | null {\n\tif (typeof input === 'string') {\n\t\tconst text = input.trim();\n\t\tif (text.length > 200) return null;\n\t\tconst lower = text.toLowerCase();\n\t\tif (lower === 'transparent') return '#00000000';\n\t\tif (lower === 'black') return '#000000';\n\t\tif (lower === 'white') return '#ffffff';\n\t\tconst hex = HEX.exec(text);\n\t\tif (hex) {\n\t\t\tlet digits = hex[1].toLowerCase();\n\t\t\tif (digits.length <= 4) digits = [...digits].map((c) => c + c).join('');\n\t\t\tif (digits.length === 8 && digits.endsWith('ff')) digits = digits.slice(0, 6);\n\t\t\treturn `#${digits}`;\n\t\t}\n\t\tconst rgb = RGB.exec(text);\n\t\tif (rgb) {\n\t\t\tconst [colour, alphaPart] = rgb[1].split('/');\n\t\t\tconst parts = colour.split(/[\\s,]+/).filter(Boolean);\n\t\t\tlet alpha: string | undefined = alphaPart?.trim();\n\t\t\tif (parts.length === 4 && alpha === undefined) alpha = parts.pop();\n\t\t\tif (parts.length !== 3) return null;\n\t\t\tconst channels = parts.map((p) => channel(p, 255));\n\t\t\tconst a = alpha === undefined || alpha === '' ? 1 : channel(alpha, 1);\n\t\t\tif (channels.some((c) => c === null) || a === null) return null;\n\t\t\treturn rgbaToHex(channels[0]!, channels[1]!, channels[2]!, a);\n\t\t}\n\t\treturn null;\n\t}\n\tif (typeof input === 'object' && input !== null) {\n\t\tconst obj = input as Record<string, unknown>;\n\t\tif (Array.isArray(obj.components)) {\n\t\t\tconst space = obj.colorSpace;\n\t\t\tif (space === undefined || space === 'srgb') {\n\t\t\t\tconst [r, g, b] = obj.components as unknown[];\n\t\t\t\tconst a = obj.alpha === undefined ? 1 : obj.alpha;\n\t\t\t\tif ([r, g, b, a].every((v) => typeof v === 'number' && Number.isFinite(v))) {\n\t\t\t\t\treturn rgbaToHex(r as number, g as number, b as number, a as number);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof obj.hex === 'string') {\n\t\t\t\tconst base = normalizeTokenColor(obj.hex);\n\t\t\t\tif (base === null) return null;\n\t\t\t\tconst a = typeof obj.alpha === 'number' ? obj.alpha : 1;\n\t\t\t\tconst c = hexToRgba(base)!;\n\t\t\t\treturn rgbaToHex(c.r, c.g, c.b, a);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tconst { r, g, b } = obj;\n\t\tconst a = obj.a === undefined ? 1 : obj.a;\n\t\tif ([r, g, b, a].every((v) => typeof v === 'number' && Number.isFinite(v))) {\n\t\t\treturn rgbaToHex(r as number, g as number, b as number, a as number);\n\t\t}\n\t}\n\treturn null;\n}\n"],"mappings":"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","names":["r","g","b","a"]}
|