204 WP-NN/RB-NN comments named a closed ticket instead of the code they sit next to. git blame already records history and stays correct when code moves; the comment does not. This sweep removes the reference and keeps the sentence, across 95 files in apps/ and libs/ plus the behaviour-spec generator's header text. Eleven references stay: five story files justify an a11y disable per the README's rule, and one line in a11y.mdx documents that convention. Two sentences needed a rewrite, not a deletion, so the reference's meaning survives its removal. behaviour-spec.mdx is regenerated, not hand-edited. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import { Signal, computed, signal } from '@angular/core';
|
|
|
|
export interface History<T> {
|
|
readonly canUndo: Signal<boolean>;
|
|
readonly canRedo: Signal<boolean>;
|
|
/** Push a pre-edit snapshot onto the undo stack and drop the redo stack. */
|
|
record(snapshot: T): void;
|
|
/** Undo: pop the last recorded snapshot and return it (moving `current` onto the redo
|
|
stack); returns undefined and changes nothing when there's nothing to undo. */
|
|
undo(current: T): T | undefined;
|
|
/** Redo: mirror of undo. */
|
|
redo(current: T): T | undefined;
|
|
clear(): void;
|
|
}
|
|
|
|
/**
|
|
* Generic undo/redo history over an immutable "document" value `T`. Elm-store editors
|
|
* restore a returned snapshot by re-dispatching a `Seed`-style Msg — this helper only
|
|
* shuffles references, it never mutates them, so the caller must hold copy-on-write state
|
|
* (every edit produces a fresh value). Both stacks are capped so a long session can't grow
|
|
* unbounded. Extracted from BriefStore's undo/redo; reused by the stamdata
|
|
* editor.
|
|
*/
|
|
export function createHistory<T>(cap = 50): History<T> {
|
|
const past = signal<readonly T[]>([]);
|
|
const future = signal<readonly T[]>([]);
|
|
return {
|
|
canUndo: computed(() => past().length > 0),
|
|
canRedo: computed(() => future().length > 0),
|
|
record(snapshot) {
|
|
past.update((p) => [...p, snapshot].slice(-cap));
|
|
future.set([]);
|
|
},
|
|
undo(current) {
|
|
const p = past();
|
|
if (p.length === 0) return undefined;
|
|
past.set(p.slice(0, -1));
|
|
future.update((f) => [...f, current].slice(-cap));
|
|
return p[p.length - 1];
|
|
},
|
|
redo(current) {
|
|
const f = future();
|
|
if (f.length === 0) return undefined;
|
|
future.set(f.slice(0, -1));
|
|
past.update((p) => [...p, current].slice(-cap));
|
|
return f[f.length - 1];
|
|
},
|
|
clear() {
|
|
past.set([]);
|
|
future.set([]);
|
|
},
|
|
};
|
|
}
|