@pixerate/editor-svelte 0.20.0 → 0.22.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.
Files changed (63) hide show
  1. package/README.md +4 -3
  2. package/dist/canvas/CanvasGuidesLayer.svelte +32 -2
  3. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts +2 -0
  4. package/dist/canvas/CanvasGuidesLayer.svelte.d.ts.map +1 -1
  5. package/dist/canvas/ShapeNode.svelte +57 -0
  6. package/dist/canvas/ShapeNode.svelte.d.ts +21 -0
  7. package/dist/canvas/ShapeNode.svelte.d.ts.map +1 -0
  8. package/dist/canvas/createCanvasArrange.svelte.d.ts +23 -1
  9. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
  10. package/dist/canvas/createCanvasArrange.svelte.js +19 -2
  11. package/dist/canvas/createCanvasMeasure.svelte.d.ts +53 -0
  12. package/dist/canvas/createCanvasMeasure.svelte.d.ts.map +1 -0
  13. package/dist/canvas/createCanvasMeasure.svelte.js +80 -0
  14. package/dist/canvas/index.d.ts +4 -0
  15. package/dist/canvas/index.d.ts.map +1 -1
  16. package/dist/canvas/index.js +6 -0
  17. package/dist/commands/CommandPalette.svelte +129 -0
  18. package/dist/commands/CommandPalette.svelte.d.ts +13 -0
  19. package/dist/commands/CommandPalette.svelte.d.ts.map +1 -0
  20. package/dist/commands/commandShortcuts.svelte.d.ts +30 -0
  21. package/dist/commands/commandShortcuts.svelte.d.ts.map +1 -0
  22. package/dist/commands/commandShortcuts.svelte.js +37 -0
  23. package/dist/commands/index.d.ts +7 -0
  24. package/dist/commands/index.d.ts.map +1 -0
  25. package/dist/commands/index.js +3 -0
  26. package/dist/commands/palette.svelte.d.ts +34 -0
  27. package/dist/commands/palette.svelte.d.ts.map +1 -0
  28. package/dist/commands/palette.svelte.js +61 -0
  29. package/dist/history/createHistoryShortcuts.d.ts +1 -1
  30. package/dist/history/createHistoryShortcuts.d.ts.map +1 -1
  31. package/dist/history/createHistoryShortcuts.js +10 -32
  32. package/dist/history/history.svelte.d.ts +13 -4
  33. package/dist/history/history.svelte.d.ts.map +1 -1
  34. package/dist/history/history.svelte.js +10 -0
  35. package/dist/history/historyDirty.svelte.d.ts +31 -0
  36. package/dist/history/historyDirty.svelte.d.ts.map +1 -0
  37. package/dist/history/historyDirty.svelte.js +30 -0
  38. package/dist/history/index.d.ts +1 -0
  39. package/dist/history/index.d.ts.map +1 -1
  40. package/dist/history/index.js +1 -0
  41. package/dist/image-editor/ImageEditor.svelte +39 -0
  42. package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
  43. package/dist/image-editor/imageEditorState.svelte.d.ts +2 -0
  44. package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -1
  45. package/dist/image-editor/imageEditorState.svelte.js +4 -0
  46. package/dist/index.d.ts +3 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -1
  49. package/dist/review/ReviewToolbar.svelte +15 -0
  50. package/dist/review/ReviewToolbar.svelte.d.ts +4 -0
  51. package/dist/review/ReviewToolbar.svelte.d.ts.map +1 -1
  52. package/dist/spreadsheet/FormulaBar.svelte +12 -1
  53. package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
  54. package/dist/spreadsheet/SpreadsheetContextMenu.svelte +21 -2
  55. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +1 -1
  56. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
  57. package/dist/spreadsheet/SpreadsheetEditor.svelte +48 -6
  58. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  59. package/dist/spreadsheet/SpreadsheetGroupRow.svelte +1 -1
  60. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  61. package/dist/spreadsheet/tableInteractions.d.ts.map +1 -1
  62. package/dist/spreadsheet/tableInteractions.js +2 -1
  63. package/package.json +10 -2
@@ -0,0 +1,30 @@
1
+ import type { CommandRegistry, CommandResult, CommandSpec } from "@pixerate/editor/commands";
2
+ type MaybeGetter<T> = T | (() => T);
3
+ export interface CommandShortcutsOptions<TContext = any> {
4
+ /** Element or window to listen on (default: `window`). */
5
+ target?: Window | HTMLElement | null;
6
+ /** Active scopes, most specific first; a getter is read on every keydown. */
7
+ scopes?: MaybeGetter<string | readonly string[] | undefined>;
8
+ /** Context for the commands (default: the registry's); a getter is read on every keydown. */
9
+ context?: MaybeGetter<TContext | undefined>;
10
+ /** Fire even while typing in inputs / contenteditable (default: per binding). */
11
+ allowInEditable?: boolean;
12
+ /** Called after a shortcut ran a command. */
13
+ onRun?: (result: CommandResult) => void;
14
+ }
15
+ /**
16
+ * Runs the registry's commands from their keyboard shortcuts (`registry.handleKeyDown`).
17
+ * Typing in inputs and editors is never hijacked, and disabled commands leave the event
18
+ * alone. Returns `{ destroy }`.
19
+ */
20
+ export declare function createCommandShortcuts<TContext = any>(registry: CommandRegistry<TContext>, options?: CommandShortcutsOptions<TContext>): {
21
+ destroy: () => void;
22
+ };
23
+ /**
24
+ * Registers commands for the lifetime of the calling component (call it during
25
+ * component initialisation). Pass a getter to react to changes: when it returns a
26
+ * different array, the old commands are unregistered.
27
+ */
28
+ export declare function useRegisterCommands<TContext = any>(registry: MaybeGetter<CommandRegistry<TContext> | null | undefined>, specs: MaybeGetter<readonly CommandSpec<TContext>[] | null | undefined>): void;
29
+ export {};
30
+ //# sourceMappingURL=commandShortcuts.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commandShortcuts.svelte.d.ts","sourceRoot":"","sources":["../../src/commands/commandShortcuts.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7F,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAKpC,MAAM,WAAW,uBAAuB,CAAC,QAAQ,GAAG,GAAG;IACrD,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACrC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAC7D,6FAA6F;IAC7F,OAAO,CAAC,EAAE,WAAW,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC5C,iFAAiF;IACjF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,GAAG,GAAG,EACnD,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,EACnC,OAAO,GAAE,uBAAuB,CAAC,QAAQ,CAAM,GAC9C;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAczB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,GAAG,EAChD,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EACnE,KAAK,EAAE,WAAW,CAAC,SAAS,WAAW,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACtE,IAAI,CAON"}
@@ -0,0 +1,37 @@
1
+ const read = (value) => typeof value === "function" ? value() : value;
2
+ /**
3
+ * Runs the registry's commands from their keyboard shortcuts (`registry.handleKeyDown`).
4
+ * Typing in inputs and editors is never hijacked, and disabled commands leave the event
5
+ * alone. Returns `{ destroy }`.
6
+ */
7
+ export function createCommandShortcuts(registry, options = {}) {
8
+ const target = options.target ?? (typeof window !== "undefined" ? window : null);
9
+ if (!target)
10
+ return { destroy: () => { } };
11
+ const onKeyDown = (event) => {
12
+ const context = options.context === undefined ? undefined : read(options.context);
13
+ const result = registry.handleKeyDown(event, {
14
+ scopes: options.scopes === undefined ? undefined : read(options.scopes),
15
+ ...(options.allowInEditable !== undefined ? { allowInEditable: options.allowInEditable } : {}),
16
+ ...(context !== undefined ? { context } : {}),
17
+ });
18
+ if (result)
19
+ options.onRun?.(result);
20
+ };
21
+ target.addEventListener("keydown", onKeyDown);
22
+ return { destroy: () => target.removeEventListener("keydown", onKeyDown) };
23
+ }
24
+ /**
25
+ * Registers commands for the lifetime of the calling component (call it during
26
+ * component initialisation). Pass a getter to react to changes: when it returns a
27
+ * different array, the old commands are unregistered.
28
+ */
29
+ export function useRegisterCommands(registry, specs) {
30
+ $effect(() => {
31
+ const currentRegistry = read(registry);
32
+ const currentSpecs = read(specs);
33
+ if (!currentRegistry || !currentSpecs || currentSpecs.length === 0)
34
+ return;
35
+ return currentRegistry.register(currentSpecs);
36
+ });
37
+ }
@@ -0,0 +1,7 @@
1
+ import type { ComponentProps } from "svelte";
2
+ import type CommandPaletteComponent from "./CommandPalette.svelte";
3
+ export { default as CommandPalette } from "./CommandPalette.svelte";
4
+ export * from "./palette.svelte.js";
5
+ export * from "./commandShortcuts.svelte.js";
6
+ export type CommandPaletteProps = ComponentProps<typeof CommandPaletteComponent>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,uBAAuB,MAAM,yBAAyB,CAAC;AAEnE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAE7C,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { default as CommandPalette } from "./CommandPalette.svelte";
2
+ export * from "./palette.svelte.js";
3
+ export * from "./commandShortcuts.svelte.js";
@@ -0,0 +1,34 @@
1
+ import { type CommandPaletteController, type CommandPaletteItem, type CommandPaletteOptions, type CommandRegistry, type CommandResult } from "@pixerate/editor/commands";
2
+ export interface CreateCommandPaletteOptions<TContext = any> extends CommandPaletteOptions<TContext> {
3
+ /** Chord that toggles the palette from anywhere, also while typing (e.g. `Mod+K`). */
4
+ hotkey?: string;
5
+ /** Element or window the hotkey listens on (default: `window`). */
6
+ hotkeyTarget?: Window | HTMLElement | null;
7
+ }
8
+ export interface SvelteCommandPalette {
9
+ readonly open: boolean;
10
+ readonly query: string;
11
+ readonly activeIndex: number;
12
+ readonly items: CommandPaletteItem[];
13
+ readonly recents: string[];
14
+ readonly lastResult: CommandResult | null;
15
+ readonly controller: CommandPaletteController;
16
+ setOpen(open: boolean): void;
17
+ toggle(): void;
18
+ setQuery(query: string): void;
19
+ setActiveIndex(index: number): void;
20
+ move(delta: number): void;
21
+ run(id: string): CommandResult | null;
22
+ runActive(): CommandResult | null;
23
+ handleKeyDown(event: KeyboardEvent): boolean;
24
+ /** Stops following the registry and removes the hotkey listener. */
25
+ destroy(): void;
26
+ }
27
+ /**
28
+ * Reactive command palette state over a `CommandRegistry` (Svelte 5 runes): ranked rows
29
+ * with shortcuts and disabled reasons, active row and recents. Render it with
30
+ * `<CommandPalette palette={…} />` or your own markup. Call `destroy()` when done
31
+ * (e.g. in `onDestroy`).
32
+ */
33
+ export declare function createCommandPalette<TContext = any>(registry: CommandRegistry<TContext>, options?: CreateCommandPaletteOptions<TContext>): SvelteCommandPalette;
34
+ //# sourceMappingURL=palette.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.svelte.d.ts","sourceRoot":"","sources":["../../src/commands/palette.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAE1B,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,2BAA2B,CAAC,QAAQ,GAAG,GAAG,CAAE,SAAQ,qBAAqB,CAAC,QAAQ,CAAC;IAClG,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC;IAC9C,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,MAAM,IAAI,IAAI,CAAC;IACf,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IACtC,SAAS,IAAI,aAAa,GAAG,IAAI,CAAC;IAClC,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAC7C,oEAAoE;IACpE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,GAAG,GAAG,EACjD,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,EACnC,OAAO,GAAE,2BAA2B,CAAC,QAAQ,CAAM,GAClD,oBAAoB,CAsDtB"}
@@ -0,0 +1,61 @@
1
+ import { createCommandPaletteController, matchPaletteHotkey, } from "@pixerate/editor/commands";
2
+ /**
3
+ * Reactive command palette state over a `CommandRegistry` (Svelte 5 runes): ranked rows
4
+ * with shortcuts and disabled reasons, active row and recents. Render it with
5
+ * `<CommandPalette palette={…} />` or your own markup. Call `destroy()` when done
6
+ * (e.g. in `onDestroy`).
7
+ */
8
+ export function createCommandPalette(registry, options = {}) {
9
+ const controller = createCommandPaletteController(registry, options);
10
+ let state = $state.raw(controller.getState());
11
+ const unsubscribe = controller.subscribe((next) => {
12
+ state = next;
13
+ });
14
+ let removeHotkey = () => { };
15
+ const { hotkey } = options;
16
+ const target = options.hotkeyTarget ?? (typeof window !== "undefined" ? window : null);
17
+ if (hotkey && target) {
18
+ const onKeyDown = (event) => {
19
+ const e = event;
20
+ if (e.defaultPrevented || !matchPaletteHotkey(e, hotkey, options.platform))
21
+ return;
22
+ e.preventDefault();
23
+ controller.toggle();
24
+ };
25
+ target.addEventListener("keydown", onKeyDown);
26
+ removeHotkey = () => target.removeEventListener("keydown", onKeyDown);
27
+ }
28
+ return {
29
+ get open() {
30
+ return state.open;
31
+ },
32
+ get query() {
33
+ return state.query;
34
+ },
35
+ get activeIndex() {
36
+ return state.activeIndex;
37
+ },
38
+ get items() {
39
+ return state.items;
40
+ },
41
+ get recents() {
42
+ return state.recents;
43
+ },
44
+ get lastResult() {
45
+ return state.lastResult;
46
+ },
47
+ controller,
48
+ setOpen: controller.setOpen,
49
+ toggle: controller.toggle,
50
+ setQuery: controller.setQuery,
51
+ setActiveIndex: controller.setActiveIndex,
52
+ move: controller.move,
53
+ run: controller.run,
54
+ runActive: controller.runActive,
55
+ handleKeyDown: controller.handleKeyDown,
56
+ destroy() {
57
+ unsubscribe();
58
+ removeHotkey();
59
+ },
60
+ };
61
+ }
@@ -1,4 +1,4 @@
1
- import { HistoryManager } from "@pixerate/editor/history";
1
+ import type { HistoryManager } from "@pixerate/editor/history";
2
2
  export interface HistoryShortcutsOptions {
3
3
  /**
4
4
  * Whether keyboard shortcuts are enabled. Defaults to true.
@@ -1 +1 @@
1
- {"version":3,"file":"createHistoryShortcuts.d.ts","sourceRoot":"","sources":["../../src/history/createHistoryShortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,uBAA4B,GACpC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAqEzB"}
1
+ {"version":3,"file":"createHistoryShortcuts.d.ts","sourceRoot":"","sources":["../../src/history/createHistoryShortcuts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,uBAA4B,GACpC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAqCzB"}
@@ -1,3 +1,4 @@
1
+ import { matchHistoryShortcut } from "@pixerate/editor/commands";
1
2
  /**
2
3
  * Attaches keyboard listeners for Undo (Mod+Z) and Redo (Mod+Shift+Z / Mod+Y).
3
4
  * Returns an object with a `destroy()` function for lifecycle cleanup.
@@ -13,39 +14,16 @@ export function createHistoryShortcuts(manager, options = {}) {
13
14
  }
14
15
  const handleKeyDown = (event) => {
15
16
  const e = event;
16
- const isMac = typeof navigator !== "undefined" &&
17
- /Mac|iPod|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
18
- const modifier = isMac ? e.metaKey : e.ctrlKey;
19
- if (!modifier)
20
- return;
21
- if (ignoreEditableElements && typeof document !== "undefined") {
22
- const targetEl = (e.target || document.activeElement);
23
- if (targetEl && targetEl.tagName) {
24
- const tagName = targetEl.tagName.toLowerCase();
25
- const isInput = tagName === "input" || tagName === "textarea";
26
- const isContentEditable = targetEl.isContentEditable ||
27
- targetEl.getAttribute?.("contenteditable") === "true";
28
- if (isInput || isContentEditable) {
29
- return;
30
- }
31
- }
17
+ // Default bindings from the command system: Mod+Z, Mod+Shift+Z and (off macOS) Mod+Y,
18
+ // matched by `event.code` with `Mod` = ⌘ on macOS and Ctrl elsewhere.
19
+ const action = matchHistoryShortcut(e, { allowInEditable: !ignoreEditableElements });
20
+ if (action === "undo" && manager.getState().canUndo) {
21
+ e.preventDefault();
22
+ manager.undo();
32
23
  }
33
- const key = e.key?.toLowerCase();
34
- // Undo: Mod+Z (without Shift)
35
- if (key === "z" && !e.shiftKey) {
36
- if (manager.getState().canUndo) {
37
- e.preventDefault();
38
- manager.undo();
39
- }
40
- return;
41
- }
42
- // Redo: Mod+Shift+Z or (on non-Mac) Mod+Y
43
- const isRedoKey = (key === "z" && e.shiftKey) || (!isMac && key === "y" && !e.shiftKey);
44
- if (isRedoKey) {
45
- if (manager.getState().canRedo) {
46
- e.preventDefault();
47
- manager.redo();
48
- }
24
+ else if (action === "redo" && manager.getState().canRedo) {
25
+ e.preventDefault();
26
+ manager.redo();
49
27
  }
50
28
  };
51
29
  eventTarget.addEventListener("keydown", handleKeyDown);
@@ -1,14 +1,23 @@
1
- import { HistoryManager, HistoryState, Command, HistoryManagerOptions } from "@pixerate/editor/history";
1
+ import { HistoryManager, HistoryState, HistoryEntry, HistoryMark, MergedExecutionOptions, Command, HistoryManagerOptions } from "@pixerate/editor/history";
2
2
  export interface SvelteHistory extends HistoryState {
3
3
  readonly manager: HistoryManager;
4
+ /** History panel rows (undo steps, then redo steps). */
5
+ readonly entries: HistoryEntry[];
4
6
  undo(): boolean;
5
7
  redo(): boolean;
6
8
  clear(): void;
7
9
  execute(command: Command): void;
8
- executeMerged(command: Command, options?: {
9
- windowMs?: number;
10
- }): void;
10
+ executeMerged(command: Command, options?: MergedExecutionOptions): void;
11
11
  batch(name: string, fn: () => void): void;
12
+ /** Undoes / redoes until the entry at `index` is current (`-1` = before all). */
13
+ goTo(index: number): number;
14
+ /** Captures the current point, e.g. when a drag starts. */
15
+ mark(): HistoryMark;
16
+ /** Takes back every step since `mark` without leaving redo (Escape-cancel). */
17
+ revertTo(mark: HistoryMark): number;
18
+ /** Joins every merged execute into one step until `endGesture`. */
19
+ beginGesture(key: string): void;
20
+ endGesture(): void;
12
21
  subscribe(run: (value: HistoryState) => void): () => void;
13
22
  }
14
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"history.svelte.d.ts","sourceRoot":"","sources":["../../src/history/history.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,OAAO,EAEP,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,OAAO,CAAC;IAChB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,gBAAgB,CAAC,EAAE,cAAc,GAAG,qBAAqB,GACxD,aAAa,CA2Cf"}
1
+ {"version":3,"file":"history.svelte.d.ts","sourceRoot":"","sources":["../../src/history/history.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,OAAO,EAEP,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;IACjC,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,OAAO,CAAC;IAChB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACxE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC1C,iFAAiF;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,IAAI,IAAI,WAAW,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAAC;IACpC,mEAAmE;IACnE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IACnB,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,gBAAgB,CAAC,EAAE,cAAc,GAAG,qBAAqB,GACxD,aAAa,CAqDf"}
@@ -32,12 +32,22 @@ export function createHistory(managerOrOptions) {
32
32
  get manager() {
33
33
  return manager;
34
34
  },
35
+ get entries() {
36
+ // Reading `state` makes the getter reactive; getEntries() is cached per change.
37
+ void state;
38
+ return manager.getEntries();
39
+ },
35
40
  undo: () => manager.undo(),
36
41
  redo: () => manager.redo(),
37
42
  clear: () => manager.clear(),
38
43
  execute: (cmd) => manager.execute(cmd),
39
44
  executeMerged: (cmd, opts) => manager.executeMerged(cmd, opts),
40
45
  batch: (name, fn) => manager.batch(name, fn),
46
+ goTo: (index) => manager.goTo(index),
47
+ mark: () => manager.mark(),
48
+ revertTo: (m) => manager.revertTo(m),
49
+ beginGesture: (key) => manager.beginGesture(key),
50
+ endGesture: () => manager.endGesture(),
41
51
  subscribe: (run) => manager.subscribe(run),
42
52
  };
43
53
  }
@@ -0,0 +1,31 @@
1
+ import { DirtyTracker, type DirtyTrackerOptions, type HistoryManager } from "@pixerate/editor/history";
2
+ export interface CreateHistoryDirtyOptions<T> extends DirtyTrackerOptions<T> {
3
+ /** The history whose changes re-check the document (a `createHistory().manager`). */
4
+ history: HistoryManager;
5
+ /** Reads the current document value. */
6
+ getValue: () => T;
7
+ /** Existing tracker to drive. */
8
+ tracker?: DirtyTracker<T>;
9
+ }
10
+ export interface SvelteHistoryDirty<T> {
11
+ /** Whether the document differs from the saved state (reactive). */
12
+ readonly isDirty: boolean;
13
+ /** Marks the current document as saved. */
14
+ markClean(): void;
15
+ /** Runs a load / restore without marking the document dirty (re-entrant). */
16
+ suppress<R>(fn: () => R): R;
17
+ /** Re-checks after a change made outside the history. */
18
+ refresh(): boolean;
19
+ readonly tracker: DirtyTracker<T>;
20
+ /** Stops following the history. */
21
+ destroy(): void;
22
+ }
23
+ /**
24
+ * Svelte 5 rune: dirty-vs-saved tracking bound to a HistoryManager. Undoing
25
+ * back to the saved state makes the document clean again, so a navigation
26
+ * guard fed with `isDirty` stops prompting.
27
+ *
28
+ * Usage: `createNavigationGuard({ isDirty: () => dirty.isDirty })`.
29
+ */
30
+ export declare function createHistoryDirty<T>(options: CreateHistoryDirtyOptions<T>): SvelteHistoryDirty<T>;
31
+ //# sourceMappingURL=historyDirty.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyDirty.svelte.d.ts","sourceRoot":"","sources":["../../src/history/historyDirty.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAEZ,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,yBAAyB,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC1E,qFAAqF;IACrF,OAAO,EAAE,cAAc,CAAC;IACxB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClB,iCAAiC;IACjC,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,IAAI,IAAI,CAAC;IAClB,6EAA6E;IAC7E,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,mCAAmC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,GACpC,kBAAkB,CAAC,CAAC,CAAC,CAsBvB"}
@@ -0,0 +1,30 @@
1
+ import { bindDirtyToHistory, } from "@pixerate/editor/history";
2
+ /**
3
+ * Svelte 5 rune: dirty-vs-saved tracking bound to a HistoryManager. Undoing
4
+ * back to the saved state makes the document clean again, so a navigation
5
+ * guard fed with `isDirty` stops prompting.
6
+ *
7
+ * Usage: `createNavigationGuard({ isDirty: () => dirty.isDirty })`.
8
+ */
9
+ export function createHistoryDirty(options) {
10
+ const binding = bindDirtyToHistory(options);
11
+ let isDirty = $state(binding.dirty);
12
+ const unsubscribe = binding.subscribe((dirty) => {
13
+ isDirty = dirty;
14
+ });
15
+ return {
16
+ get isDirty() {
17
+ return isDirty;
18
+ },
19
+ markClean: () => binding.markClean(),
20
+ suppress: (fn) => binding.suppress(fn),
21
+ refresh: () => binding.refresh(),
22
+ get tracker() {
23
+ return binding.tracker;
24
+ },
25
+ destroy() {
26
+ unsubscribe();
27
+ binding.destroy();
28
+ },
29
+ };
30
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./history.svelte.js";
2
2
  export * from "./createHistoryShortcuts.js";
3
+ export * from "./historyDirty.svelte.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/history/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from "./history.svelte.js";
2
2
  export * from "./createHistoryShortcuts.js";
3
+ export * from "./historyDirty.svelte.js";
@@ -16,6 +16,8 @@
16
16
  getCropCursor,
17
17
  calculateCropDrag,
18
18
  hitTestAnnotation,
19
+ normalizeProcessVersion,
20
+ LATEST_PROCESS_VERSION,
19
21
  } from '@pixerate/editor/image-editor';
20
22
  import { onDestroy, untrack } from 'svelte';
21
23
  import { createReactiveImageEditor, type ReactiveImageEditor } from './imageEditorState.svelte.js';
@@ -47,6 +49,9 @@
47
49
  const internalEditor = createReactiveImageEditor();
48
50
  const editor = $derived(propEditor || internalEditor);
49
51
  onDestroy(() => internalEditor.destroy());
52
+ const legacyProcess = $derived(
53
+ normalizeProcessVersion(editor.state.adjustments.process) < LATEST_PROCESS_VERSION
54
+ );
50
55
 
51
56
  let canvasEl = $state<HTMLCanvasElement | null>(null);
52
57
  let containerEl = $state<HTMLDivElement | null>(null);
@@ -961,6 +966,40 @@
961
966
  />
962
967
  </div>
963
968
 
969
+ <!-- Tint (process 2 white balance) -->
970
+ <div>
971
+ <div class="flex justify-between text-slate-400 mb-1">
972
+ <span>Tint</span>
973
+ <span class="font-mono">{editor.state.adjustments.tint ?? 0}</span>
974
+ </div>
975
+ <input
976
+ type="range"
977
+ min="-100"
978
+ max="100"
979
+ aria-label="Tint"
980
+ disabled={legacyProcess}
981
+ value={editor.state.adjustments.tint ?? 0}
982
+ oninput={(e) => editor.setAdjustments({ tint: Number((e.target as HTMLInputElement).value) })}
983
+ class="w-full accent-indigo-500 disabled:opacity-40"
984
+ />
985
+ </div>
986
+
987
+ {#if legacyProcess}
988
+ <div
989
+ class="flex items-center justify-between gap-2 rounded border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-200"
990
+ data-testid="image-editor-process-notice"
991
+ >
992
+ <span>Legacy rendering (process 1). Tint needs the current process.</span>
993
+ <button
994
+ type="button"
995
+ onclick={() => editor.upgradeProcess()}
996
+ class="shrink-0 rounded bg-amber-500/20 px-2 py-0.5 hover:bg-amber-500/30"
997
+ >
998
+ Update
999
+ </button>
1000
+ </div>
1001
+ {/if}
1002
+
964
1003
  <!-- Blur -->
965
1004
  <div>
966
1005
  <div class="flex justify-between text-slate-400 mb-1">
@@ -1 +1 @@
1
- {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AASzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AA+hCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAWzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAojCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -14,6 +14,8 @@ export declare function createReactiveImageEditor(options?: ImageEditorOptions):
14
14
  flipVertical(): void;
15
15
  setAdjustments(partial: Partial<ImageAdjustments>, commitToHistory?: boolean): void;
16
16
  resetAdjustments(): void;
17
+ /** Update to Current Process: re-render the adjustments with the latest process (one undo step). */
18
+ upgradeProcess(): boolean;
17
19
  setDepthMask(partial: Partial<DepthMaskOptions>): void;
18
20
  resetDepthMask(): void;
19
21
  addAnnotation(ann: CreateAnnotationPayload): string;
@@ -1 +1 @@
1
- {"version":3,"file":"imageEditorState.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/imageEditorState.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EAChB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAuB;;;;;kBA0BxD,eAAe;kBAGf,OAAO,GAAG,IAAI;2BAGL,WAAW;;oBAMlB,MAAM;yBAGD,MAAM;;;4BASH,OAAO,CAAC,gBAAgB,CAAC;;0BAM3B,OAAO,CAAC,gBAAgB,CAAC;;uBAM5B,uBAAuB;yBAGrB,MAAM,WAAW,OAAO,CAAC,UAAU,CAAC;yBAGpC,MAAM;;yBAMN,MAAM,GAAG,IAAI;;;;oBAYlB,MAAM;qBAGL,MAAM;;kBAMT,MAAM;gBAGR,UAAU;4BAGE,OAAO;mBAGhB,MAAM,GAAG,gBAAgB;qBAGvB,aAAa;kBAGhB,aAAa;;mBAMZ,0BAA0B;sBAGvB;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;yBAG5C;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;2BAG7C,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;oCAGhC,MAAM;4CAGE,MAAM;IAG9C,8EAA8E;;EAKjF;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"imageEditorState.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/imageEditorState.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EAChB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,kBAAuB;;;;;kBA0BxD,eAAe;kBAGf,OAAO,GAAG,IAAI;2BAGL,WAAW;;oBAMlB,MAAM;yBAGD,MAAM;;;4BASH,OAAO,CAAC,gBAAgB,CAAC;;IAMjD,oGAAoG;;0BAI9E,OAAO,CAAC,gBAAgB,CAAC;;uBAM5B,uBAAuB;yBAGrB,MAAM,WAAW,OAAO,CAAC,UAAU,CAAC;yBAGpC,MAAM;;yBAMN,MAAM,GAAG,IAAI;;;;oBAYlB,MAAM;qBAGL,MAAM;;kBAMT,MAAM;gBAGR,UAAU;4BAGE,OAAO;mBAGhB,MAAM,GAAG,gBAAgB;qBAGvB,aAAa;kBAGhB,aAAa;;mBAMZ,0BAA0B;sBAGvB;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;yBAG5C;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;2BAG7C,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC;oCAGhC,MAAM;4CAGE,MAAM;IAG9C,8EAA8E;;EAKjF;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
@@ -53,6 +53,10 @@ export function createReactiveImageEditor(options = {}) {
53
53
  resetAdjustments() {
54
54
  controller.resetAdjustments();
55
55
  },
56
+ /** Update to Current Process: re-render the adjustments with the latest process (one undo step). */
57
+ upgradeProcess() {
58
+ return controller.upgradeProcess();
59
+ },
56
60
  setDepthMask(partial) {
57
61
  controller.setDepthMask(partial);
58
62
  },
package/dist/index.d.ts CHANGED
@@ -11,8 +11,8 @@ export * from "./history/index.js";
11
11
  export * from "./fileImport.svelte.js";
12
12
  export * from "./agentTools.svelte.js";
13
13
  export * from "./outline.svelte.js";
14
- export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, } from "@pixerate/editor";
15
- export type { DirtyTrackerOptions, Command, HistoryState, HistoryManagerOptions, } from "@pixerate/editor";
14
+ export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, bindDirtyToHistory, withoutDirty, } from "@pixerate/editor";
15
+ export type { DirtyTrackerOptions, Command, HistoryState, HistoryManagerOptions, HistoryEntry, HistoryMark, MergedExecutionOptions, HistoryDirtyBinding, BindDirtyToHistoryOptions, } from "@pixerate/editor";
16
16
  export type EditableTextNodeEditorProps = ComponentProps<typeof EditableTextNodeEditorComponent>;
17
17
  export type BubbleMenuProps = ComponentProps<typeof BubbleMenuComponent>;
18
18
  export type TemplateRendererProps = ComponentProps<typeof TemplateRendererComponent>;
@@ -28,4 +28,5 @@ export type { PromptValues, PromptDiagnostic, VariableFormField, } from "@pixera
28
28
  export * from "./spreadsheet/index.js";
29
29
  export * from "./image-editor/index.js";
30
30
  export * from "./review/index.js";
31
+ export * from "./commands/index.js";
31
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,KAAK,+BAA+B,MAAM,iCAAiC,CAAC;AACnF,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,+BAA+B,CACvC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAChD,OAAO,yBAAyB,CACjC,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE7E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEhE,YAAY,EACV,QAAQ,EACR,eAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,KAAK,+BAA+B,MAAM,iCAAiC,CAAC;AACnF,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,yBAAyB,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAClD,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,2BAA2B,GAAG,cAAc,CACtD,OAAO,+BAA+B,CACvC,CAAC;AACF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAChD,OAAO,yBAAyB,CACjC,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE7E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEhE,YAAY,EACV,QAAQ,EACR,eAAe,EACf,KAAK,EACL,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ export * from "./history/index.js";
5
5
  export * from "./fileImport.svelte.js";
6
6
  export * from "./agentTools.svelte.js";
7
7
  export * from "./outline.svelte.js";
8
- export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, } from "@pixerate/editor";
8
+ export { DirtyTracker, createDirtyTracker, defaultIsEqual, HistoryManager, createHistoryManager, bindDirtyToHistory, withoutDirty, } from "@pixerate/editor";
9
9
  export { default as EditableTextNodeEditor } from "./EditableTextNodeEditor.svelte";
10
10
  export { default as BubbleMenu } from "./BubbleMenu.svelte";
11
11
  export { default as TemplateRenderer } from "./TemplateRenderer.svelte";
@@ -14,3 +14,4 @@ export { default as OutlinePanel } from "./OutlinePanel.svelte";
14
14
  export * from "./spreadsheet/index.js";
15
15
  export * from "./image-editor/index.js";
16
16
  export * from "./review/index.js";
17
+ export * from "./commands/index.js";
@@ -5,11 +5,17 @@
5
5
  interface Props {
6
6
  editor?: Editor | null;
7
7
  class?: string;
8
+ /** Show the current change's author and description. Defaults to `true`. */
9
+ showDescription?: boolean;
10
+ /** Class of the description label. */
11
+ descriptionClass?: string;
8
12
  }
9
13
 
10
14
  let {
11
15
  editor = null,
12
16
  class: className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm",
17
+ showDescription = true,
18
+ descriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground",
13
19
  }: Props = $props();
14
20
 
15
21
  const buttonClass =
@@ -60,6 +66,15 @@
60
66
  >
61
67
  ›
62
68
  </button>
69
+ {#if showDescription && current}
70
+ <span
71
+ class={descriptionClass}
72
+ data-change-type={current.type}
73
+ title={`${current.author.name}: ${current.description}`}
74
+ >
75
+ {current.author.name ? `${current.author.name}: ` : ""}{current.description}
76
+ </span>
77
+ {/if}
63
78
  <span class="mx-1 h-4 w-px bg-border" aria-hidden="true"></span>
64
79
  <button
65
80
  type="button"
@@ -2,6 +2,10 @@ import type { Editor } from "@tiptap/core";
2
2
  interface Props {
3
3
  editor?: Editor | null;
4
4
  class?: string;
5
+ /** Show the current change's author and description. Defaults to `true`. */
6
+ showDescription?: boolean;
7
+ /** Class of the description label. */
8
+ descriptionClass?: string;
5
9
  }
6
10
  declare const ReviewToolbar: import("svelte").Component<Props, {}, "">;
7
11
  type ReviewToolbar = ReturnType<typeof ReviewToolbar>;
@@ -1 +1 @@
1
- {"version":3,"file":"ReviewToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/review/ReviewToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAIzC,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0DH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ReviewToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/review/ReviewToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAIzC,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAiEH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -46,6 +46,15 @@
46
46
  return value;
47
47
  });
48
48
 
49
+ // A spilled cell shows its anchor's formula greyed out (it has no formula of its own).
50
+ let spillFormula = $derived.by(() => {
51
+ if (!sheetState || sheetState.editingCell || !sheetState.activeCell || effectiveValue) return '';
52
+ void sheetState.document;
53
+ const { row, col } = sheetState.activeCell;
54
+ const spill = sheetState.controller.getSpillAt(row, col);
55
+ return spill && (spill.anchor.row !== row || spill.anchor.col !== col) ? spill.formula : '';
56
+ });
57
+
49
58
  function handleKeyDown(e: KeyboardEvent) {
50
59
  // Keys typed here must not reach the grid's navigation handler.
51
60
  e.stopPropagation();
@@ -101,7 +110,9 @@
101
110
  placeholder="Enter a value or formula (e.g. =SUM(A1:A5))"
102
111
  aria-label="Formula bar"
103
112
  bind:this={inputEl}
104
- value={effectiveValue}
113
+ data-spill-formula={spillFormula ? 'true' : undefined}
114
+ style={spillFormula ? 'color: var(--muted-foreground, #94a3b8);' : undefined}
115
+ value={effectiveValue || spillFormula}
105
116
  oninput={handleInput}
106
117
  onkeydown={handleKeyDown}
107
118
  />