@pixerate/editor 0.145.0 → 0.147.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 (135) hide show
  1. package/README.md +4 -2
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/dist/agent/index.cjs +36 -34
  4. package/dist/agent/index.js +7 -5
  5. package/dist/animation/index.cjs +5 -3
  6. package/dist/animation/index.js +4 -2
  7. package/dist/animation-editor/index.cjs +62 -60
  8. package/dist/animation-editor/index.js +6 -4
  9. package/dist/canvas/index.cjs +36 -34
  10. package/dist/canvas/index.js +19 -17
  11. package/dist/chart/index.cjs +88 -86
  12. package/dist/chart/index.js +7 -5
  13. package/dist/{chunk-WBJ6BG4R.js → chunk-2AQL4JHM.js} +6 -6
  14. package/dist/{chunk-HLMF3E53.cjs → chunk-2WDZI6QD.cjs} +15 -15
  15. package/dist/{chunk-ZNHB5RG6.js → chunk-37RLSNQ3.js} +2 -2
  16. package/dist/{chunk-5WOY52O6.cjs → chunk-4YQKRHMI.cjs} +8 -8
  17. package/dist/chunk-6ONMQ6WF.js +113 -0
  18. package/dist/{chunk-L434J57V.cjs → chunk-6QTON3VW.cjs} +16 -16
  19. package/dist/chunk-AA34BA7G.js +1913 -0
  20. package/dist/{chunk-QX25HWFL.js → chunk-AY65VQSU.js} +1 -1
  21. package/dist/{chunk-ONZJD2OD.cjs → chunk-BROJYMWU.cjs} +57 -153
  22. package/dist/{chunk-YM75DN54.js → chunk-DSZCDNTM.js} +1 -1
  23. package/dist/{chunk-PUR6BZFA.cjs → chunk-DVLMW3GB.cjs} +53 -53
  24. package/dist/{chunk-ALUWD3FG.js → chunk-EIUTJPEL.js} +1 -1
  25. package/dist/{chunk-X65C4DWV.js → chunk-EQ4SF4X4.js} +1 -1
  26. package/dist/{chunk-77SG47WT.js → chunk-FLOGD3QB.js} +1 -1
  27. package/dist/chunk-HRLT5BYO.js +133 -0
  28. package/dist/{chunk-AJSW5GMP.js → chunk-HVQVHUTV.js} +19 -115
  29. package/dist/{chunk-UR37VCIR.cjs → chunk-I4AHLP7F.cjs} +16 -35
  30. package/dist/chunk-IPJ3TEYR.cjs +113 -0
  31. package/dist/chunk-KC7HB5GF.cjs +133 -0
  32. package/dist/chunk-KY3FYKP3.js +52 -0
  33. package/dist/chunk-LEUZ4FRC.cjs +52 -0
  34. package/dist/{chunk-H6FX45CP.cjs → chunk-LIXLD433.cjs} +37 -37
  35. package/dist/{chunk-6GAYGQDC.js → chunk-O5ML2I6O.js} +3 -3
  36. package/dist/{chunk-KI2VBONS.js → chunk-OET45HIR.js} +2 -2
  37. package/dist/{chunk-XNZWATSB.cjs → chunk-QCEQIRJQ.cjs} +85 -85
  38. package/dist/{chunk-HRI3ZMDM.cjs → chunk-RHXW4TA5.cjs} +3 -3
  39. package/dist/{chunk-UUTTJK77.cjs → chunk-SUF6WRXR.cjs} +2 -2
  40. package/dist/{chunk-RN767PO6.js → chunk-SUVZJ2CP.js} +15 -34
  41. package/dist/{chunk-FLUTRPLY.cjs → chunk-TLMRGLQQ.cjs} +16 -16
  42. package/dist/{chunk-DAS7M4RY.js → chunk-UILRJQFD.js} +2 -2
  43. package/dist/chunk-UKCJ43YL.cjs +1913 -0
  44. package/dist/{chunk-XQ33O5BC.cjs → chunk-X35BV5YT.cjs} +44 -44
  45. package/dist/{chunk-6HMEHNWW.cjs → chunk-XTU6QO2W.cjs} +26 -26
  46. package/dist/{chunk-VM6JYWXQ.js → chunk-ZQUANEK4.js} +2 -2
  47. package/dist/cms/index.cjs +5 -3
  48. package/dist/cms/index.js +18 -16
  49. package/dist/color/contrast.d.cts +14 -0
  50. package/dist/color/contrast.d.ts +14 -0
  51. package/dist/color/convert.d.cts +30 -0
  52. package/dist/color/convert.d.ts +30 -0
  53. package/dist/color/dual.d.cts +98 -0
  54. package/dist/color/dual.d.ts +98 -0
  55. package/dist/color/format.d.cts +12 -0
  56. package/dist/color/format.d.ts +12 -0
  57. package/dist/color/gamut.d.cts +12 -0
  58. package/dist/color/gamut.d.ts +12 -0
  59. package/dist/color/harmony.d.cts +22 -0
  60. package/dist/color/harmony.d.ts +22 -0
  61. package/dist/color/index.cjs +115 -0
  62. package/dist/color/index.d.cts +18 -0
  63. package/dist/color/index.d.ts +18 -0
  64. package/dist/color/index.js +115 -0
  65. package/dist/color/mix.d.cts +11 -0
  66. package/dist/color/mix.d.ts +11 -0
  67. package/dist/color/named.d.cts +5 -0
  68. package/dist/color/named.d.ts +5 -0
  69. package/dist/color/parse.d.cts +11 -0
  70. package/dist/color/parse.d.ts +11 -0
  71. package/dist/color/picker.d.cts +52 -0
  72. package/dist/color/picker.d.ts +52 -0
  73. package/dist/color/recent.d.cts +8 -0
  74. package/dist/color/recent.d.ts +8 -0
  75. package/dist/color/tools.d.cts +15 -0
  76. package/dist/color/tools.d.ts +15 -0
  77. package/dist/color/types.d.cts +41 -0
  78. package/dist/color/types.d.ts +41 -0
  79. package/dist/color/vision.d.cts +36 -0
  80. package/dist/color/vision.d.ts +36 -0
  81. package/dist/diagram/editorDocument.d.cts +12 -0
  82. package/dist/diagram/editorDocument.d.ts +12 -0
  83. package/dist/diagram/index.cjs +35 -1
  84. package/dist/diagram/index.d.cts +1 -0
  85. package/dist/diagram/index.d.ts +1 -0
  86. package/dist/diagram/index.js +34 -0
  87. package/dist/export/index.cjs +6 -4
  88. package/dist/export/index.js +5 -3
  89. package/dist/extensions/index.cjs +10 -8
  90. package/dist/extensions/index.js +9 -7
  91. package/dist/gradient/controller.d.cts +109 -0
  92. package/dist/gradient/controller.d.ts +109 -0
  93. package/dist/gradient/editor.d.cts +38 -0
  94. package/dist/gradient/editor.d.ts +38 -0
  95. package/dist/gradient/index.cjs +1549 -0
  96. package/dist/gradient/index.d.cts +5 -0
  97. package/dist/gradient/index.d.ts +5 -0
  98. package/dist/gradient/index.js +1549 -0
  99. package/dist/gradient/model.d.cts +94 -0
  100. package/dist/gradient/model.d.ts +94 -0
  101. package/dist/gradient/tools.d.cts +11 -0
  102. package/dist/gradient/tools.d.ts +11 -0
  103. package/dist/gradient/types.d.cts +44 -0
  104. package/dist/gradient/types.d.ts +44 -0
  105. package/dist/image-editor/index.cjs +649 -647
  106. package/dist/image-editor/index.js +6 -4
  107. package/dist/index.cjs +19 -17
  108. package/dist/index.js +16 -14
  109. package/dist/paint-editor/color.d.cts +3 -13
  110. package/dist/paint-editor/color.d.ts +3 -13
  111. package/dist/paint-editor/index.cjs +23 -68
  112. package/dist/paint-editor/index.js +15 -60
  113. package/dist/persistence/documentStore.d.cts +51 -0
  114. package/dist/persistence/documentStore.d.ts +51 -0
  115. package/dist/persistence/documentTools.d.cts +35 -0
  116. package/dist/persistence/documentTools.d.ts +35 -0
  117. package/dist/persistence/editorAutosave.d.cts +33 -0
  118. package/dist/persistence/editorAutosave.d.ts +33 -0
  119. package/dist/persistence/editorDocument.d.cts +118 -0
  120. package/dist/persistence/editorDocument.d.ts +118 -0
  121. package/dist/persistence/index.cjs +294 -1
  122. package/dist/persistence/index.d.cts +4 -0
  123. package/dist/persistence/index.d.ts +4 -0
  124. package/dist/persistence/index.js +293 -0
  125. package/dist/vector/index.cjs +950 -948
  126. package/dist/vector/index.js +5 -3
  127. package/dist/video-editor/index.cjs +5 -3
  128. package/dist/video-editor/index.js +4 -2
  129. package/package.json +21 -1
  130. package/skills/README.md +1 -0
  131. package/skills/pixerate-agent-tools/SKILL.md +21 -0
  132. package/skills/pixerate-color/SKILL.md +69 -0
  133. package/skills/pixerate-diagram/SKILL.md +13 -0
  134. package/dist/chunk-45VRG4RB.js +0 -65
  135. package/dist/chunk-5FXOR7F2.cjs +0 -65
@@ -1,3 +1,12 @@
1
+ import {
2
+ EDITOR_DOCUMENT_FORMAT,
3
+ EditorDocumentLoadError,
4
+ defineEditorDocument,
5
+ isEditorDocumentFile,
6
+ parseEditorDocument,
7
+ serializeEditorDocument,
8
+ toEditorDocumentFile
9
+ } from "../chunk-HRLT5BYO.js";
1
10
  import {
2
11
  createLoadReport,
3
12
  hasLoadProblems,
@@ -651,10 +660,288 @@ function createRevisionTracker(options = {}) {
651
660
  }
652
661
  };
653
662
  }
663
+
664
+ // src/persistence/editorAutosave.ts
665
+ function createEditorAutosave(doc, options) {
666
+ const { dirty, key, ...rest } = options;
667
+ const revisions = createRevisionTracker({ history: doc, dirty });
668
+ const controller = createAutosaveController({
669
+ ...rest,
670
+ key: key ?? doc.kind,
671
+ getSnapshot: () => toEditorDocumentFile(doc),
672
+ deserialize: (text) => parseEditorDocument(text, { kind: doc.kind }),
673
+ revision: revisions.revision,
674
+ savedRevision: revisions.savedRevision
675
+ });
676
+ const unsubscribe = revisions.subscribe(() => controller.notify());
677
+ return {
678
+ controller,
679
+ revisions,
680
+ checkRecovery: () => controller.checkRecovery(),
681
+ restore(recovery) {
682
+ if (!recovery.snapshot) throw recovery.error ?? new Error("The recovered snapshot could not be read.");
683
+ const report = doc.loadDocument(recovery.snapshot, { history: "reset" });
684
+ controller.ready();
685
+ return report;
686
+ },
687
+ async discard() {
688
+ await controller.discardRecovery();
689
+ controller.ready();
690
+ },
691
+ async markClean() {
692
+ revisions.markSaved();
693
+ await controller.markClean();
694
+ },
695
+ destroy() {
696
+ unsubscribe();
697
+ revisions.destroy();
698
+ controller.destroy();
699
+ }
700
+ };
701
+ }
702
+
703
+ // src/persistence/documentStore.ts
704
+ function slugifyDocumentName(name) {
705
+ const slug = name.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
706
+ return slug || "untitled";
707
+ }
708
+ function createEditorDocumentStore(options) {
709
+ const { storage } = options;
710
+ const ns = options.namespace ?? "pixerate.documents";
711
+ const now = options.now ?? Date.now;
712
+ const indexKey = `${ns}:index`;
713
+ const fileKey = (key) => `${ns}:file:${key}`;
714
+ let index = null;
715
+ let queue = Promise.resolve();
716
+ const run = (task) => {
717
+ const result = queue.then(task, task);
718
+ queue = result.catch(() => void 0);
719
+ return result;
720
+ };
721
+ const loadIndex = async () => {
722
+ if (index) return index;
723
+ const raw = await storage.get(indexKey);
724
+ let items = [];
725
+ if (raw) {
726
+ try {
727
+ const data = JSON.parse(raw);
728
+ if (data && Array.isArray(data.items)) {
729
+ items = data.items.filter((item) => item && typeof item.key === "string" && typeof item.kind === "string");
730
+ }
731
+ } catch {
732
+ items = [];
733
+ }
734
+ }
735
+ index = items;
736
+ return index;
737
+ };
738
+ const saveIndex = async (items) => {
739
+ await storage.set(indexKey, JSON.stringify({ version: 1, items }));
740
+ index = items;
741
+ };
742
+ const newestFirst = (a, b) => a.savedAt < b.savedAt ? 1 : a.savedAt > b.savedAt ? -1 : 0;
743
+ const readFile = async (key) => {
744
+ const raw = await storage.get(fileKey(key));
745
+ return raw === null ? null : parseEditorDocument(raw);
746
+ };
747
+ return {
748
+ save: (doc, name) => run(async () => {
749
+ const trimmed = name.trim() || "Untitled";
750
+ const entry = {
751
+ key: `${doc.kind}/${slugifyDocumentName(trimmed)}`,
752
+ kind: doc.kind,
753
+ name: trimmed,
754
+ formatVersion: doc.formatVersion,
755
+ savedAt: new Date(now()).toISOString()
756
+ };
757
+ await storage.set(fileKey(entry.key), JSON.stringify(toEditorDocumentFile(doc, trimmed)));
758
+ await saveIndex([...(await loadIndex()).filter((item) => item.key !== entry.key), entry]);
759
+ return entry;
760
+ }),
761
+ list: (kind) => run(async () => (await loadIndex()).filter((item) => !kind || item.kind === kind).sort(newestFirst)),
762
+ read: (key) => run(() => readFile(key)),
763
+ open: (doc, key, openOptions = {}) => run(async () => {
764
+ const entry = (await loadIndex()).find((item) => item.key === key);
765
+ const file = entry ? await readFile(key) : null;
766
+ if (!entry || !file) {
767
+ const keys = (await loadIndex()).filter((item) => item.kind === doc.kind).sort(newestFirst).map((item) => item.key);
768
+ throw new EditorDocumentLoadError(
769
+ `There is no saved document "${key}". ${keys.length ? `Saved ${doc.kind} documents: ${keys.join(", ")}.` : `No ${doc.kind} documents are saved yet.`}`
770
+ );
771
+ }
772
+ const report = doc.loadDocument(file, { history: openOptions.history ?? "reset" });
773
+ return { ...entry, report };
774
+ }),
775
+ remove: (key) => run(async () => {
776
+ const items = await loadIndex();
777
+ if (!items.some((item) => item.key === key)) return false;
778
+ await saveIndex(items.filter((item) => item.key !== key));
779
+ await storage.remove(fileKey(key));
780
+ return true;
781
+ })
782
+ };
783
+ }
784
+
785
+ // src/persistence/documentTools.ts
786
+ var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
787
+ var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
788
+ var REPLACE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
789
+ var OVERWRITE = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false };
790
+ var object = (properties, required = []) => ({
791
+ type: "object",
792
+ properties,
793
+ ...required.length ? { required } : {},
794
+ additionalProperties: false
795
+ });
796
+ var reportOut = (report) => ({
797
+ ...report.migratedFrom !== null ? { migratedFrom: report.migratedFrom } : {},
798
+ ...report.problems.length ? { problems: report.problems } : {},
799
+ ...report.unknownKeys.length ? { unknownKeys: report.unknownKeys } : {}
800
+ });
801
+ function createDocumentToolProvider(doc, options = {}) {
802
+ const { kind } = doc;
803
+ const noun = options.noun ?? kind.replace(/_/g, " ");
804
+ const maxChars = options.maxChars ?? 2e5;
805
+ const store = options.store;
806
+ const writable = () => {
807
+ const reason = doc.readOnly();
808
+ if (reason) throw new Error(`${reason} Read tools still work; write tools are refused.`);
809
+ };
810
+ const historyOut = () => {
811
+ const h = doc.history.getState();
812
+ return {
813
+ revision: doc.revision(),
814
+ canUndo: h.canUndo,
815
+ canRedo: h.canRedo,
816
+ ...h.undoName ? { undoName: h.undoName } : {},
817
+ ...h.redoName ? { redoName: h.redoName } : {}
818
+ };
819
+ };
820
+ const loaded = (report) => ({
821
+ revision: doc.revision(),
822
+ undoable: doc.recordsLoads,
823
+ ...reportOut(report)
824
+ });
825
+ const get = {
826
+ name: `get_${kind}_document`,
827
+ title: `Get the ${noun} document`,
828
+ description: `Returns the whole ${noun} as plain JSON: { kind, formatVersion, revision, document }. Use it to keep a copy, compare versions or pass the document to load_${kind}_document later. For reading or editing parts of the ${noun}, prefer the editor's own read tools: this result can be large.`,
829
+ inputSchema: object({}),
830
+ annotations: { title: `Get the ${noun} document`, ...READ },
831
+ handler: () => {
832
+ const document = doc.getDocument();
833
+ const size = JSON.stringify(document)?.length ?? 0;
834
+ if (size > maxChars) {
835
+ throw new Error(`The ${noun} document is ${size} characters of JSON, over the ${maxChars} limit for this tool. Use the editor's read tools to look at parts of it.`);
836
+ }
837
+ return { kind, formatVersion: doc.formatVersion, revision: doc.revision(), document };
838
+ }
839
+ };
840
+ const load = {
841
+ name: `load_${kind}_document`,
842
+ title: `Load a ${noun} document`,
843
+ description: `Replaces the whole ${noun} with a document from get_${kind}_document (its "document" value, or the whole result as a saved file). The document is checked first: when it does not fit, nothing changes and the error says why. Returns { revision, undoable, migratedFrom?, problems?, unknownKeys? }; undoable: true means undo_${kind} brings the previous ${noun} back.`,
844
+ inputSchema: object(
845
+ {
846
+ document: {
847
+ type: "object",
848
+ description: `The ${noun} document: the "document" value from get_${kind}_document, or a saved file ({ format, kind, formatVersion, document }).`
849
+ }
850
+ },
851
+ ["document"]
852
+ ),
853
+ annotations: { title: `Load a ${noun} document`, ...REPLACE },
854
+ handler: ({ document }) => {
855
+ writable();
856
+ return loaded(doc.loadDocument(document, { history: "record" }));
857
+ }
858
+ };
859
+ const undo = {
860
+ name: `undo_${kind}`,
861
+ title: `Undo a ${noun} edit`,
862
+ description: `Undoes the last ${noun} edit (one tool call or one user gesture). Returns { undone, revision, canUndo, canRedo, undoName?, redoName? }; undone is the name of the step that was undone.`,
863
+ inputSchema: object({}),
864
+ annotations: { title: `Undo a ${noun} edit`, ...WRITE },
865
+ handler: () => {
866
+ writable();
867
+ const name = doc.history.getState().undoName;
868
+ if (!doc.history.getState().canUndo || !doc.history.undo()) throw new Error("Nothing to undo.");
869
+ return { undone: name ?? true, ...historyOut() };
870
+ }
871
+ };
872
+ const redo = {
873
+ name: `redo_${kind}`,
874
+ title: `Redo a ${noun} edit`,
875
+ description: `Redoes the last undone ${noun} edit. Returns { redone, revision, canUndo, canRedo, undoName?, redoName? }; redone is the name of the step that was redone.`,
876
+ inputSchema: object({}),
877
+ annotations: { title: `Redo a ${noun} edit`, ...WRITE },
878
+ handler: () => {
879
+ writable();
880
+ const name = doc.history.getState().redoName;
881
+ if (!doc.history.getState().canRedo || !doc.history.redo()) throw new Error("Nothing to redo.");
882
+ return { redone: name ?? true, ...historyOut() };
883
+ }
884
+ };
885
+ const storeTools = (s) => [
886
+ {
887
+ name: `save_${kind}_document`,
888
+ title: `Save the ${noun}`,
889
+ description: `Saves the current ${noun} under a name in the host's document storage. Saving the same name again replaces that saved copy. Returns { key, name, savedAt, revision }; pass key to open_saved_${kind}_document to load it again.`,
890
+ inputSchema: object({
891
+ name: {
892
+ type: "string",
893
+ minLength: 1,
894
+ maxLength: 200,
895
+ description: `Name to save under (default: the ${noun}'s current name, or "Untitled ${noun}").`
896
+ }
897
+ }),
898
+ annotations: { title: `Save the ${noun}`, ...OVERWRITE },
899
+ handler: async ({ name }) => {
900
+ const entry = await s.save(doc, name ?? options.name?.() ?? `Untitled ${noun}`);
901
+ return { key: entry.key, name: entry.name, savedAt: entry.savedAt, revision: doc.revision() };
902
+ }
903
+ },
904
+ {
905
+ name: `list_saved_${kind}_documents`,
906
+ title: `List saved ${noun} documents`,
907
+ description: `Lists the ${noun} documents in the host's document storage, newest first. Returns { documents: [{ key, name, savedAt, formatVersion }] }.`,
908
+ inputSchema: object({}),
909
+ annotations: { title: `List saved ${noun} documents`, ...READ },
910
+ handler: async () => ({
911
+ documents: (await s.list(kind)).map(({ key, name, savedAt, formatVersion }) => ({ key, name, savedAt, formatVersion }))
912
+ })
913
+ },
914
+ {
915
+ name: `open_saved_${kind}_document`,
916
+ title: `Open a saved ${noun}`,
917
+ description: `Replaces the current ${noun} with a saved one. Unsaved changes stay reachable through undo_${kind} when undoable is true. Returns { key, name, revision, undoable, migratedFrom?, problems?, unknownKeys? }.`,
918
+ inputSchema: object({ key: { type: "string", minLength: 1, description: `A key from list_saved_${kind}_documents or save_${kind}_document.` } }, ["key"]),
919
+ annotations: { title: `Open a saved ${noun}`, ...REPLACE },
920
+ handler: async ({ key }) => {
921
+ writable();
922
+ const entry = await s.open(doc, key, { history: "record" });
923
+ return { key: entry.key, name: entry.name, ...loaded(entry.report) };
924
+ }
925
+ }
926
+ ];
927
+ return {
928
+ id: options.id ?? `${kind}-document`,
929
+ ...options.namespace !== void 0 ? { namespace: options.namespace } : {},
930
+ title: options.title ?? `${noun.charAt(0).toUpperCase()}${noun.slice(1)} document`,
931
+ description: `Get, load${options.history === false ? "" : ", undo, redo"}${store ? ", save and open" : ""} the whole ${noun}.`,
932
+ ...options.skill ? { skill: options.skill } : {},
933
+ tools: [get, load, ...options.history === false ? [] : [undo, redo], ...store ? storeTools(store) : []]
934
+ };
935
+ }
654
936
  export {
937
+ EDITOR_DOCUMENT_FORMAT,
938
+ EditorDocumentLoadError,
655
939
  InjectedStorageError,
656
940
  StorageUnavailableError,
657
941
  createAutosaveController,
942
+ createDocumentToolProvider,
943
+ createEditorAutosave,
944
+ createEditorDocumentStore,
658
945
  createIndexedDbAdapter,
659
946
  createLoadReport,
660
947
  createLocalStorageAdapter,
@@ -662,15 +949,21 @@ export {
662
949
  createOpfsAdapter,
663
950
  createRecoveryPolicy,
664
951
  createRevisionTracker,
952
+ defineEditorDocument,
665
953
  exponentialBackoff,
666
954
  formatRecoveryMessage,
667
955
  hasLoadProblems,
956
+ isEditorDocumentFile,
668
957
  isIndexedDbAvailable,
669
958
  isLocalStorageAvailable,
670
959
  isOpfsAvailable,
671
960
  loadDocument,
672
961
  loadSections,
673
962
  migrateDocument,
963
+ parseEditorDocument,
674
964
  recoveryMetaKey,
965
+ serializeEditorDocument,
966
+ slugifyDocumentName,
967
+ toEditorDocumentFile,
675
968
  withStorageFailures
676
969
  };