@lvl99dotcom/paintcanvas 0.7.0 → 0.8.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 +1 -0
- package/dist/lib/assets/index.d.ts +92 -0
- package/dist/lib/assets/index.js +106 -0
- package/dist/lib/assets/schema.d.ts +151 -0
- package/dist/lib/assets/schema.js +76 -0
- package/dist/lib/brushes/presetFile/index.d.ts +6 -27
- package/dist/lib/brushes/presetFile/index.js +34 -47
- package/dist/lib/brushes/types/index.d.ts +2 -11
- package/dist/lib/classes/AssetSync/index.d.ts +45 -0
- package/dist/lib/classes/AssetSync/index.js +80 -0
- package/dist/lib/classes/ColourPalettes/index.d.ts +83 -0
- package/dist/lib/classes/ColourPalettes/index.js +165 -0
- package/dist/lib/classes/CustomPresets/index.d.ts +14 -15
- package/dist/lib/classes/CustomPresets/index.js +25 -10
- package/dist/lib/classes/LayerAutosave/index.d.ts +9 -2
- package/dist/lib/classes/LayerAutosave/index.js +16 -6
- package/dist/lib/classes/PaintCanvas/index.d.ts +111 -23
- package/dist/lib/classes/PaintCanvas/index.js +219 -58
- package/dist/lib/classes/Palette/index.d.ts +3 -2
- package/dist/lib/classes/Palette/index.js +3 -2
- package/dist/lib/classes/ProfileGraphs/index.d.ts +13 -2
- package/dist/lib/classes/ProfileGraphs/index.js +22 -2
- package/dist/lib/classes/RemoteDocumentAutosave/index.d.ts +73 -0
- package/dist/lib/classes/RemoteDocumentAutosave/index.js +119 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +7 -3
- package/dist/lib/classes/SettingsAutosave/index.js +28 -7
- package/dist/lib/classes/SettingsExtensions/index.d.ts +67 -0
- package/dist/lib/classes/SettingsExtensions/index.js +88 -0
- package/dist/lib/document/index.d.ts +61 -4
- package/dist/lib/document/index.js +173 -4
- package/dist/lib/document/schema.d.ts +154 -39
- package/dist/lib/document/schema.js +91 -25
- package/dist/lib/messages/schema.d.ts +176 -74
- package/dist/lib/messages/schema.js +2 -1
- package/dist/lib/palette/index.d.ts +8 -42
- package/dist/lib/palette/index.js +26 -59
- package/dist/lib/palette/schema.d.ts +111 -22
- package/dist/lib/palette/schema.js +51 -26
- package/dist/lib/presets/index.d.ts +1 -9
- package/dist/lib/presets/index.js +4 -10
- package/dist/lib/presets/schema.d.ts +118 -73
- package/dist/lib/presets/schema.js +28 -60
- package/dist/lib/profiles/profileFile/index.d.ts +10 -0
- package/dist/lib/profiles/profileFile/index.js +19 -0
- package/dist/lib/profiles/schema.d.ts +96 -3
- package/dist/lib/profiles/schema.js +31 -7
- package/dist/lib/schemas/index.d.ts +42 -0
- package/dist/lib/schemas/index.js +60 -0
- package/dist/lib/settings/index.d.ts +37 -22
- package/dist/lib/settings/index.js +78 -37
- package/dist/lib/settings/schema.d.ts +198 -76
- package/dist/lib/settings/schema.js +92 -86
- package/dist/lib/storage/index.d.ts +82 -3
- package/dist/lib/storage/index.js +82 -5
- package/dist/lib/validation/validators.generated.d.ts +19 -8
- package/dist/lib/validation/validators.generated.js +44716 -18900
- package/dist/lib/version/index.js +1 -1
- package/dist/locales/en-GB.js +23 -5
- package/dist/locales/en-US.js +23 -5
- package/dist/locales/fr.js +23 -5
- package/dist/locales/ja.js +23 -5
- package/dist/locales/ko.js +23 -5
- package/dist/locales/zh-Hans.js +23 -5
- package/dist/locales/zh-Hant.js +23 -5
- package/dist/package.js +1 -1
- package/dist/testing/app.d.ts +6 -2
- package/dist/testing/app.js +7 -2
- package/dist/testing/documentStore.d.ts +26 -0
- package/dist/testing/documentStore.js +31 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +6 -2
- package/dist/ui/elements/PaintCanvas/index.js +20 -6
- package/dist/ui/elements/PcAppSettings/index.d.ts +5 -6
- package/dist/ui/elements/PcAppSettings/index.js +17 -27
- package/dist/ui/elements/PcPanelBrushes/index.d.ts +1 -1
- package/dist/ui/elements/PcPanelBrushes/index.js +22 -28
- package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
- package/dist/ui/elements/PcPanelColours/index.d.ts +56 -19
- package/dist/ui/elements/PcPanelColours/index.js +251 -104
- package/dist/ui/elements/PcPanelColours/style.js +1 -1
- package/dist/ui/elements/PcStats/index.d.ts +8 -4
- package/dist/ui/elements/PcStats/index.js +39 -5
- package/locales/en-GB.json +23 -5
- package/locales/en-US.json +23 -5
- package/locales/fr.json +23 -5
- package/locales/ja.json +23 -5
- package/locales/ko.json +23 -5
- package/locales/zh-Hans.json +23 -5
- package/locales/zh-Hant.json +23 -5
- package/package.json +2 -1
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { formatErrors } from "../presets/index.js";
|
|
2
2
|
import { validateDocument } from "../validation/validators.generated.js";
|
|
3
|
-
import {
|
|
3
|
+
import { base64ToBytes, bytesToBase64 } from "../image/index.js";
|
|
4
|
+
import { compareVersions, majorVersion, parseSchemaUrl } from "../schemas/index.js";
|
|
5
|
+
import { AssetFileError } from "../assets/index.js";
|
|
6
|
+
import { DATE_TIME_PATTERN, DOCUMENT_BLEND_MODES, DOCUMENT_SCHEMA_VERSION, DOCUMENT_TYPE, MAX_DOCUMENT_AUTHORS, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_NAME_LENGTH, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, THUMBNAIL_SIZE, documentSchema } from "./schema.js";
|
|
4
7
|
// src/lib/document/index.ts
|
|
8
|
+
/** The `format` of documents from before versioned schemas */
|
|
9
|
+
var LEGACY_DOCUMENT_FORMAT = "paintcanvas-document";
|
|
10
|
+
/** Name of a new document, or one from before documents had names */
|
|
11
|
+
var UNTITLED_DOCUMENT_NAME = "Untitled";
|
|
5
12
|
/**
|
|
6
|
-
* Check a document (e.g. read from a file) against `documentSchema`, and that its selected layer exists.
|
|
13
|
+
* Check a document file (e.g. read from a file) against `documentSchema`, and that its selected layer exists.
|
|
7
14
|
*
|
|
8
15
|
* @example
|
|
9
16
|
* isValidDocument(JSON.parse(text)).valid; // true
|
|
@@ -13,7 +20,7 @@ var isValidDocument = (input) => {
|
|
|
13
20
|
valid: false,
|
|
14
21
|
errors: formatErrors(validateDocument.errors)
|
|
15
22
|
};
|
|
16
|
-
if (input.
|
|
23
|
+
if (!input.layers.some((layer) => layer.id === input.activeLayer)) return {
|
|
17
24
|
valid: false,
|
|
18
25
|
errors: ["/activeLayer must be one of the layers"]
|
|
19
26
|
};
|
|
@@ -22,4 +29,166 @@ var isValidDocument = (input) => {
|
|
|
22
29
|
value: input
|
|
23
30
|
};
|
|
24
31
|
};
|
|
25
|
-
|
|
32
|
+
/**
|
|
33
|
+
* An author's details: their name alone becomes `{ name }`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* authorDetails("Ada"); // { name: "Ada" }
|
|
37
|
+
*/
|
|
38
|
+
var authorDetails = (author) => typeof author === "string" ? { name: author } : { ...author };
|
|
39
|
+
/**
|
|
40
|
+
* Whether two authors are the same person: by DID when both have one, otherwise by handle when both have one,
|
|
41
|
+
* otherwise by name.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* sameAuthor({ name: "Ada", handle: "ada.test" }, { name: "Ada L", handle: "ada.test" }); // true
|
|
45
|
+
*/
|
|
46
|
+
var sameAuthor = (a, b) => {
|
|
47
|
+
if (a.did && b.did) return a.did === b.did;
|
|
48
|
+
if (a.handle && b.handle) return a.handle === b.handle;
|
|
49
|
+
return a.name === b.name;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* A document's authors with another added at the end, unless they're already one of them.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* withAuthor([{ name: "Ada" }], "Ann"); // [{ name: "Ada" }, { name: "Ann" }]
|
|
56
|
+
*/
|
|
57
|
+
var withAuthor = (authors, author) => {
|
|
58
|
+
const details = authorDetails(author);
|
|
59
|
+
return authors.some((other) => sameAuthor(other, details)) ? [...authors] : [...authors, details];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Details for a new document: a new ID, a name, an author and the time now.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* newDocumentInfo("Ada"); // { id: "1b9d…", name: "Untitled", author: [{ name: "Ada" }], dateCreated: "2026-…", … }
|
|
66
|
+
*/
|
|
67
|
+
var newDocumentInfo = (author, name = UNTITLED_DOCUMENT_NAME, now = /* @__PURE__ */ new Date()) => ({
|
|
68
|
+
id: crypto.randomUUID(),
|
|
69
|
+
name,
|
|
70
|
+
author: author === void 0 ? [] : [authorDetails(author)],
|
|
71
|
+
dateCreated: now.toISOString(),
|
|
72
|
+
dateOpened: now.toISOString()
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* A PNG blob as a data URL.
|
|
76
|
+
*/
|
|
77
|
+
var toDataUrl = async (image) => `data:image/png;base64,${bytesToBase64(new Uint8Array(await image.arrayBuffer()))}`;
|
|
78
|
+
/**
|
|
79
|
+
* A PNG data URL as a blob.
|
|
80
|
+
*/
|
|
81
|
+
var toBlob = (dataUrl) => new Blob([base64ToBytes(dataUrl.slice(dataUrl.indexOf(",") + 1))], { type: "image/png" });
|
|
82
|
+
/**
|
|
83
|
+
* A document with each image changed by a function.
|
|
84
|
+
*/
|
|
85
|
+
var mapImages = async (document, change) => {
|
|
86
|
+
const { layers, external, thumbnail, ...rest } = document;
|
|
87
|
+
return {
|
|
88
|
+
...rest,
|
|
89
|
+
layers: await Promise.all(layers.map(async (layer) => ({
|
|
90
|
+
...layer,
|
|
91
|
+
content: await change(layer.content)
|
|
92
|
+
}))),
|
|
93
|
+
external: await Promise.all(external.map(async (sheet) => ({
|
|
94
|
+
...sheet,
|
|
95
|
+
content: await change(sheet.content)
|
|
96
|
+
}))),
|
|
97
|
+
...thumbnail ? { thumbnail: {
|
|
98
|
+
...thumbnail,
|
|
99
|
+
content: await change(thumbnail.content)
|
|
100
|
+
} } : {}
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* A document as a file (JSON), with its images as PNG data URLs.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* JSON.stringify(await documentToFile(await canvas.createDocument()));
|
|
108
|
+
*/
|
|
109
|
+
var documentToFile = async (document) => ({
|
|
110
|
+
$schema: documentSchema.$id,
|
|
111
|
+
"@type": DOCUMENT_TYPE,
|
|
112
|
+
...await mapImages(document, toDataUrl)
|
|
113
|
+
});
|
|
114
|
+
/** Whether a value is a plain object */
|
|
115
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
116
|
+
/**
|
|
117
|
+
* A document file from before versioned schemas (`format` and `version: 1`) in the current version: its layers get
|
|
118
|
+
* IDs, it gets new details (named `UNTITLED_DOCUMENT_NAME`, opened and made now), and the selected layer is named by ID.
|
|
119
|
+
*/
|
|
120
|
+
var migrateLegacyDocument = (data, now) => {
|
|
121
|
+
const layers = Array.isArray(data.layers) ? data.layers : [];
|
|
122
|
+
const ids = layers.map(() => crypto.randomUUID());
|
|
123
|
+
const info = newDocumentInfo(void 0, UNTITLED_DOCUMENT_NAME, now);
|
|
124
|
+
return {
|
|
125
|
+
$schema: documentSchema.$id,
|
|
126
|
+
"@type": DOCUMENT_TYPE,
|
|
127
|
+
...info,
|
|
128
|
+
width: data.width,
|
|
129
|
+
height: data.height,
|
|
130
|
+
layers: layers.map((layer, i) => ({
|
|
131
|
+
id: ids[i],
|
|
132
|
+
name: layer?.name,
|
|
133
|
+
visible: layer?.visible,
|
|
134
|
+
opacity: layer?.opacity,
|
|
135
|
+
blendingMode: layer?.blendMode,
|
|
136
|
+
alphaLock: layer?.alphaLock,
|
|
137
|
+
content: layer?.image
|
|
138
|
+
})),
|
|
139
|
+
activeLayer: ids[data.activeLayer] ?? "",
|
|
140
|
+
external: Array.isArray(data.external) ? data.external.map((sheet) => ({
|
|
141
|
+
source: sheet?.source,
|
|
142
|
+
content: sheet?.image
|
|
143
|
+
})) : data.external
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Read a document file (e.g. parsed from JSON), with its images as PNG blobs. Files from before versioned schemas are
|
|
148
|
+
* migrated. Throws an `AssetFileError`: `invalid` if it isn't a document or doesn't match its schema, `unsupported` if
|
|
149
|
+
* it's a version the app can't read.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* await canvas.openDocument(documentFromFile(JSON.parse(text)));
|
|
153
|
+
*/
|
|
154
|
+
var documentFromFile = (data, now = /* @__PURE__ */ new Date()) => {
|
|
155
|
+
if (!isRecord(data)) throw new AssetFileError("invalid");
|
|
156
|
+
let file = data;
|
|
157
|
+
const named = parseSchemaUrl(data.$schema);
|
|
158
|
+
if (named) {
|
|
159
|
+
if (named.name !== "document") throw new AssetFileError("invalid");
|
|
160
|
+
if (majorVersion(named.version) !== majorVersion("1.0.0") || compareVersions(named.version, "1.0.0") > 0) throw new AssetFileError("unsupported");
|
|
161
|
+
} else if (data.format === "paintcanvas-document" && data.version === 1) file = migrateLegacyDocument(data, now);
|
|
162
|
+
else throw new AssetFileError("invalid");
|
|
163
|
+
const result = isValidDocument(file);
|
|
164
|
+
if (!result.valid) throw new AssetFileError("invalid");
|
|
165
|
+
const document = { ...result.value };
|
|
166
|
+
delete document.$schema;
|
|
167
|
+
delete document["@type"];
|
|
168
|
+
const { layers, external, thumbnail, ...rest } = document;
|
|
169
|
+
return {
|
|
170
|
+
...rest,
|
|
171
|
+
layers: layers.map((layer) => ({
|
|
172
|
+
...layer,
|
|
173
|
+
content: toBlob(layer.content)
|
|
174
|
+
})),
|
|
175
|
+
external: external.map((sheet) => ({
|
|
176
|
+
...sheet,
|
|
177
|
+
content: toBlob(sheet.content)
|
|
178
|
+
})),
|
|
179
|
+
...thumbnail ? { thumbnail: {
|
|
180
|
+
...thumbnail,
|
|
181
|
+
content: toBlob(thumbnail.content)
|
|
182
|
+
} } : {}
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* The name of the file a document is exported to, starting with a prefix (see `PaintCanvasNames.filePrefix`).
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* documentFileName("Sunset at sea"); // "paintcanvas-sunset-at-sea.json"
|
|
190
|
+
*/
|
|
191
|
+
var documentFileName = (name, prefix = "paintcanvas") => {
|
|
192
|
+
return `${prefix}-${name.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-|-$/g, "") || "document"}.json`;
|
|
193
|
+
};
|
|
194
|
+
export { DATE_TIME_PATTERN, DOCUMENT_BLEND_MODES, DOCUMENT_SCHEMA_VERSION, DOCUMENT_TYPE, LEGACY_DOCUMENT_FORMAT, MAX_DOCUMENT_AUTHORS, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_NAME_LENGTH, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, THUMBNAIL_SIZE, UNTITLED_DOCUMENT_NAME, authorDetails, documentFileName, documentFromFile, documentSchema, documentToFile, isValidDocument, newDocumentInfo, sameAuthor, withAuthor };
|
|
@@ -1,59 +1,147 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { type AssetAuthorDetails } from "../assets/schema.ts";
|
|
2
|
+
/** Version of the document schema */
|
|
3
|
+
export declare const DOCUMENT_SCHEMA_VERSION = "1.0.0";
|
|
4
|
+
/** `@type` of a document */
|
|
5
|
+
export declare const DOCUMENT_TYPE = "PaintCanvas/Document";
|
|
5
6
|
/** Largest canvas width or height a document may have */
|
|
6
7
|
export declare const MAX_DOCUMENT_SIZE = 10000;
|
|
7
8
|
/** Most layers in a document (a canvas may allow fewer, see `maxLayers`) */
|
|
8
9
|
export declare const MAX_DOCUMENT_LAYERS = 100;
|
|
9
10
|
/** Most drawings from elsewhere in a document */
|
|
10
11
|
export declare const MAX_DOCUMENT_SHEETS = 100;
|
|
11
|
-
/**
|
|
12
|
+
/** Most authors of a document */
|
|
13
|
+
export declare const MAX_DOCUMENT_AUTHORS = 100;
|
|
14
|
+
/** Longest document or layer name (characters) */
|
|
15
|
+
export declare const MAX_DOCUMENT_NAME_LENGTH = 100;
|
|
16
|
+
/** Longest side of a document's thumbnail (pixels) */
|
|
17
|
+
export declare const THUMBNAIL_SIZE = 200;
|
|
18
|
+
/** Longest image in a document file (characters): a PNG data URL of up to 100 MB */
|
|
12
19
|
export declare const MAX_DOCUMENT_IMAGE_LENGTH: number;
|
|
13
|
-
/**
|
|
20
|
+
/** Blending modes a layer may have (see `BlendMode`) */
|
|
14
21
|
export declare const DOCUMENT_BLEND_MODES: readonly ["source-over", "screen", "lighten", "multiply", "darken", "overlay"];
|
|
15
|
-
/** A
|
|
16
|
-
export interface
|
|
22
|
+
/** A document's details, kept with the drawing */
|
|
23
|
+
export interface DocumentInfo {
|
|
24
|
+
/** A UUID */
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
/** Who made it: whoever started it, then everyone who saved it since */
|
|
28
|
+
author: AssetAuthorDetails[];
|
|
29
|
+
/** When it was started (ISO 8601 date and time) */
|
|
30
|
+
dateCreated: string;
|
|
31
|
+
/** When it was last opened (ISO 8601 date and time) */
|
|
32
|
+
dateOpened: string;
|
|
33
|
+
}
|
|
34
|
+
/** A layer in a document, with its pixels as an image (`T`) the size of the canvas */
|
|
35
|
+
export interface DocumentLayer<T> {
|
|
36
|
+
id: string;
|
|
17
37
|
name: string;
|
|
18
38
|
visible: boolean;
|
|
19
39
|
opacity: number;
|
|
20
|
-
|
|
40
|
+
blendingMode: (typeof DOCUMENT_BLEND_MODES)[number];
|
|
21
41
|
alphaLock: boolean;
|
|
22
|
-
|
|
23
|
-
image: string;
|
|
42
|
+
content: T;
|
|
24
43
|
}
|
|
25
|
-
/** A drawing from elsewhere in a document (see `ExternalSheets`) */
|
|
26
|
-
export interface DocumentSheet {
|
|
44
|
+
/** A drawing from elsewhere in a document (see `ExternalSheets`), with its pixels as an image (`T`) */
|
|
45
|
+
export interface DocumentSheet<T> {
|
|
27
46
|
/** Where it came from */
|
|
28
47
|
source: string;
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
content: T;
|
|
49
|
+
}
|
|
50
|
+
/** A small picture of a document, at most `THUMBNAIL_SIZE` pixels on its longest side */
|
|
51
|
+
export interface DocumentThumbnail<T> {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
content: T;
|
|
31
55
|
}
|
|
32
|
-
/** A
|
|
33
|
-
export interface
|
|
34
|
-
format: typeof DOCUMENT_FORMAT;
|
|
35
|
-
version: typeof DOCUMENT_VERSION;
|
|
56
|
+
/** A document with its images as `T` */
|
|
57
|
+
export interface DocumentOf<T> extends DocumentInfo {
|
|
36
58
|
width: number;
|
|
37
59
|
height: number;
|
|
38
60
|
/** The user's layers, top first */
|
|
39
|
-
layers: DocumentLayer[];
|
|
40
|
-
/**
|
|
41
|
-
activeLayer:
|
|
61
|
+
layers: DocumentLayer<T>[];
|
|
62
|
+
/** ID of the selected layer */
|
|
63
|
+
activeLayer: string;
|
|
42
64
|
/** Drawings from elsewhere, top first */
|
|
43
|
-
external: DocumentSheet[];
|
|
65
|
+
external: DocumentSheet<T>[];
|
|
66
|
+
thumbnail?: DocumentThumbnail<T>;
|
|
67
|
+
}
|
|
68
|
+
/** A document as the app and stores use it: images are PNG blobs */
|
|
69
|
+
export type DrawingDocument = DocumentOf<Blob>;
|
|
70
|
+
/** A document as a file (JSON): images are PNG data URLs */
|
|
71
|
+
export interface DocumentFile extends DocumentOf<string> {
|
|
72
|
+
$schema: string;
|
|
73
|
+
"@type": typeof DOCUMENT_TYPE;
|
|
44
74
|
}
|
|
45
|
-
/**
|
|
75
|
+
/** Matches an ISO 8601 date and time, e.g. `2026-11-11T09:30:00.000Z` */
|
|
76
|
+
export declare const DATE_TIME_PATTERN = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$";
|
|
77
|
+
/** JSON schema for a document file */
|
|
46
78
|
export declare const documentSchema: {
|
|
47
|
-
readonly $
|
|
79
|
+
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
80
|
+
readonly $id: string;
|
|
81
|
+
readonly "@type": "PaintCanvas/Document";
|
|
48
82
|
readonly type: "object";
|
|
49
83
|
readonly additionalProperties: false;
|
|
50
|
-
readonly required: readonly ["
|
|
84
|
+
readonly required: readonly ["$schema", "@type", "id", "name", "author", "dateCreated", "dateOpened", "width", "height", "layers", "activeLayer", "external"];
|
|
51
85
|
readonly properties: {
|
|
52
|
-
readonly
|
|
53
|
-
readonly
|
|
86
|
+
readonly $schema: {
|
|
87
|
+
readonly type: "string";
|
|
88
|
+
readonly minLength: 1;
|
|
89
|
+
readonly maxLength: 2048;
|
|
54
90
|
};
|
|
55
|
-
readonly
|
|
56
|
-
readonly const:
|
|
91
|
+
readonly "@type": {
|
|
92
|
+
readonly const: "PaintCanvas/Document";
|
|
93
|
+
};
|
|
94
|
+
readonly id: {
|
|
95
|
+
readonly type: "string";
|
|
96
|
+
readonly pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
97
|
+
};
|
|
98
|
+
readonly name: {
|
|
99
|
+
readonly type: "string";
|
|
100
|
+
readonly minLength: 1;
|
|
101
|
+
readonly maxLength: 100;
|
|
102
|
+
readonly pattern: "\\S";
|
|
103
|
+
};
|
|
104
|
+
readonly author: {
|
|
105
|
+
readonly type: "array";
|
|
106
|
+
readonly maxItems: 100;
|
|
107
|
+
readonly items: {
|
|
108
|
+
readonly type: "object";
|
|
109
|
+
readonly additionalProperties: false;
|
|
110
|
+
readonly required: readonly ["name"];
|
|
111
|
+
readonly properties: {
|
|
112
|
+
readonly did: {
|
|
113
|
+
readonly type: "string";
|
|
114
|
+
readonly maxLength: 2048;
|
|
115
|
+
readonly pattern: "^did:[a-z]+:[a-zA-Z0-9._:%-]+$";
|
|
116
|
+
};
|
|
117
|
+
readonly name: {
|
|
118
|
+
readonly type: "string";
|
|
119
|
+
readonly minLength: 1;
|
|
120
|
+
readonly maxLength: 100;
|
|
121
|
+
};
|
|
122
|
+
readonly handle: {
|
|
123
|
+
readonly type: "string";
|
|
124
|
+
readonly minLength: 1;
|
|
125
|
+
readonly maxLength: 100;
|
|
126
|
+
};
|
|
127
|
+
readonly email: {
|
|
128
|
+
readonly type: "string";
|
|
129
|
+
readonly maxLength: 254;
|
|
130
|
+
};
|
|
131
|
+
readonly homepage: {
|
|
132
|
+
readonly type: "string";
|
|
133
|
+
readonly maxLength: 2048;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
readonly dateCreated: {
|
|
139
|
+
readonly type: "string";
|
|
140
|
+
readonly pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$";
|
|
141
|
+
};
|
|
142
|
+
readonly dateOpened: {
|
|
143
|
+
readonly type: "string";
|
|
144
|
+
readonly pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$";
|
|
57
145
|
};
|
|
58
146
|
readonly width: {
|
|
59
147
|
readonly type: "integer";
|
|
@@ -72,12 +160,17 @@ export declare const documentSchema: {
|
|
|
72
160
|
readonly items: {
|
|
73
161
|
readonly type: "object";
|
|
74
162
|
readonly additionalProperties: false;
|
|
75
|
-
readonly required: readonly ["name", "visible", "opacity", "
|
|
163
|
+
readonly required: readonly ["id", "name", "visible", "opacity", "blendingMode", "alphaLock", "content"];
|
|
76
164
|
readonly properties: {
|
|
77
|
-
readonly
|
|
165
|
+
readonly id: {
|
|
78
166
|
readonly type: "string";
|
|
79
167
|
readonly minLength: 1;
|
|
168
|
+
readonly maxLength: 100;
|
|
169
|
+
};
|
|
170
|
+
readonly name: {
|
|
80
171
|
readonly maxLength: 64;
|
|
172
|
+
readonly type: "string";
|
|
173
|
+
readonly minLength: 1;
|
|
81
174
|
readonly pattern: "\\S";
|
|
82
175
|
};
|
|
83
176
|
readonly visible: {
|
|
@@ -88,13 +181,13 @@ export declare const documentSchema: {
|
|
|
88
181
|
readonly minimum: 0;
|
|
89
182
|
readonly maximum: 1;
|
|
90
183
|
};
|
|
91
|
-
readonly
|
|
184
|
+
readonly blendingMode: {
|
|
92
185
|
readonly enum: readonly ["source-over", "screen", "lighten", "multiply", "darken", "overlay"];
|
|
93
186
|
};
|
|
94
187
|
readonly alphaLock: {
|
|
95
188
|
readonly type: "boolean";
|
|
96
189
|
};
|
|
97
|
-
readonly
|
|
190
|
+
readonly content: {
|
|
98
191
|
readonly type: "string";
|
|
99
192
|
readonly pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$";
|
|
100
193
|
readonly maxLength: number;
|
|
@@ -103,9 +196,9 @@ export declare const documentSchema: {
|
|
|
103
196
|
};
|
|
104
197
|
};
|
|
105
198
|
readonly activeLayer: {
|
|
106
|
-
readonly type: "
|
|
107
|
-
readonly
|
|
108
|
-
readonly
|
|
199
|
+
readonly type: "string";
|
|
200
|
+
readonly minLength: 1;
|
|
201
|
+
readonly maxLength: 100;
|
|
109
202
|
};
|
|
110
203
|
readonly external: {
|
|
111
204
|
readonly type: "array";
|
|
@@ -113,14 +206,14 @@ export declare const documentSchema: {
|
|
|
113
206
|
readonly items: {
|
|
114
207
|
readonly type: "object";
|
|
115
208
|
readonly additionalProperties: false;
|
|
116
|
-
readonly required: readonly ["source", "
|
|
209
|
+
readonly required: readonly ["source", "content"];
|
|
117
210
|
readonly properties: {
|
|
118
211
|
readonly source: {
|
|
119
212
|
readonly type: "string";
|
|
120
213
|
readonly minLength: 1;
|
|
121
214
|
readonly maxLength: 2048;
|
|
122
215
|
};
|
|
123
|
-
readonly
|
|
216
|
+
readonly content: {
|
|
124
217
|
readonly type: "string";
|
|
125
218
|
readonly pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$";
|
|
126
219
|
readonly maxLength: number;
|
|
@@ -128,5 +221,27 @@ export declare const documentSchema: {
|
|
|
128
221
|
};
|
|
129
222
|
};
|
|
130
223
|
};
|
|
224
|
+
readonly thumbnail: {
|
|
225
|
+
readonly type: "object";
|
|
226
|
+
readonly additionalProperties: false;
|
|
227
|
+
readonly required: readonly ["width", "height", "content"];
|
|
228
|
+
readonly properties: {
|
|
229
|
+
readonly width: {
|
|
230
|
+
readonly type: "integer";
|
|
231
|
+
readonly minimum: 1;
|
|
232
|
+
readonly maximum: 200;
|
|
233
|
+
};
|
|
234
|
+
readonly height: {
|
|
235
|
+
readonly type: "integer";
|
|
236
|
+
readonly minimum: 1;
|
|
237
|
+
readonly maximum: 200;
|
|
238
|
+
};
|
|
239
|
+
readonly content: {
|
|
240
|
+
readonly type: "string";
|
|
241
|
+
readonly pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$";
|
|
242
|
+
readonly maxLength: number;
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
};
|
|
131
246
|
};
|
|
132
247
|
};
|
|
@@ -1,17 +1,25 @@
|
|
|
1
|
+
import { UUID_PATTERN, authorDetailsSchema, fileSchemaUrlSchema } from "../assets/schema.js";
|
|
2
|
+
import { SCHEMA_DIALECT, schemaUrl } from "../schemas/index.js";
|
|
1
3
|
// src/lib/document/schema.ts
|
|
2
|
-
/**
|
|
3
|
-
var
|
|
4
|
-
/**
|
|
5
|
-
var
|
|
4
|
+
/** Version of the document schema */
|
|
5
|
+
var DOCUMENT_SCHEMA_VERSION = "1.0.0";
|
|
6
|
+
/** `@type` of a document */
|
|
7
|
+
var DOCUMENT_TYPE = "PaintCanvas/Document";
|
|
6
8
|
/** Largest canvas width or height a document may have */
|
|
7
9
|
var MAX_DOCUMENT_SIZE = 1e4;
|
|
8
10
|
/** Most layers in a document (a canvas may allow fewer, see `maxLayers`) */
|
|
9
11
|
var MAX_DOCUMENT_LAYERS = 100;
|
|
10
12
|
/** Most drawings from elsewhere in a document */
|
|
11
13
|
var MAX_DOCUMENT_SHEETS = 100;
|
|
12
|
-
/**
|
|
14
|
+
/** Most authors of a document */
|
|
15
|
+
var MAX_DOCUMENT_AUTHORS = 100;
|
|
16
|
+
/** Longest document or layer name (characters) */
|
|
17
|
+
var MAX_DOCUMENT_NAME_LENGTH = 100;
|
|
18
|
+
/** Longest side of a document's thumbnail (pixels) */
|
|
19
|
+
var THUMBNAIL_SIZE = 200;
|
|
20
|
+
/** Longest image in a document file (characters): a PNG data URL of up to 100 MB */
|
|
13
21
|
var MAX_DOCUMENT_IMAGE_LENGTH = Math.ceil(104857600 / 3) * 4 + 22;
|
|
14
|
-
/**
|
|
22
|
+
/** Blending modes a layer may have (see `BlendMode`) */
|
|
15
23
|
var DOCUMENT_BLEND_MODES = [
|
|
16
24
|
"source-over",
|
|
17
25
|
"screen",
|
|
@@ -20,6 +28,7 @@ var DOCUMENT_BLEND_MODES = [
|
|
|
20
28
|
"darken",
|
|
21
29
|
"overlay"
|
|
22
30
|
];
|
|
31
|
+
/** A PNG data URL */
|
|
23
32
|
var image = {
|
|
24
33
|
type: "string",
|
|
25
34
|
pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$",
|
|
@@ -30,14 +39,33 @@ var size = {
|
|
|
30
39
|
minimum: 1,
|
|
31
40
|
maximum: MAX_DOCUMENT_SIZE
|
|
32
41
|
};
|
|
33
|
-
|
|
42
|
+
var name = {
|
|
43
|
+
type: "string",
|
|
44
|
+
minLength: 1,
|
|
45
|
+
maxLength: 100,
|
|
46
|
+
pattern: "\\S"
|
|
47
|
+
};
|
|
48
|
+
/** Matches an ISO 8601 date and time, e.g. `2026-11-11T09:30:00.000Z` */
|
|
49
|
+
var DATE_TIME_PATTERN = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$";
|
|
50
|
+
var date = {
|
|
51
|
+
type: "string",
|
|
52
|
+
pattern: DATE_TIME_PATTERN
|
|
53
|
+
};
|
|
54
|
+
/** JSON schema for a document file */
|
|
34
55
|
var documentSchema = {
|
|
35
|
-
$
|
|
56
|
+
$schema: SCHEMA_DIALECT,
|
|
57
|
+
$id: schemaUrl("document", DOCUMENT_SCHEMA_VERSION),
|
|
58
|
+
"@type": DOCUMENT_TYPE,
|
|
36
59
|
type: "object",
|
|
37
60
|
additionalProperties: false,
|
|
38
61
|
required: [
|
|
39
|
-
"
|
|
40
|
-
"
|
|
62
|
+
"$schema",
|
|
63
|
+
"@type",
|
|
64
|
+
"id",
|
|
65
|
+
"name",
|
|
66
|
+
"author",
|
|
67
|
+
"dateCreated",
|
|
68
|
+
"dateOpened",
|
|
41
69
|
"width",
|
|
42
70
|
"height",
|
|
43
71
|
"layers",
|
|
@@ -45,8 +73,20 @@ var documentSchema = {
|
|
|
45
73
|
"external"
|
|
46
74
|
],
|
|
47
75
|
properties: {
|
|
48
|
-
|
|
49
|
-
|
|
76
|
+
$schema: fileSchemaUrlSchema,
|
|
77
|
+
"@type": { const: DOCUMENT_TYPE },
|
|
78
|
+
id: {
|
|
79
|
+
type: "string",
|
|
80
|
+
pattern: UUID_PATTERN
|
|
81
|
+
},
|
|
82
|
+
name,
|
|
83
|
+
author: {
|
|
84
|
+
type: "array",
|
|
85
|
+
maxItems: 100,
|
|
86
|
+
items: authorDetailsSchema
|
|
87
|
+
},
|
|
88
|
+
dateCreated: date,
|
|
89
|
+
dateOpened: date,
|
|
50
90
|
width: size,
|
|
51
91
|
height: size,
|
|
52
92
|
layers: {
|
|
@@ -57,19 +97,23 @@ var documentSchema = {
|
|
|
57
97
|
type: "object",
|
|
58
98
|
additionalProperties: false,
|
|
59
99
|
required: [
|
|
100
|
+
"id",
|
|
60
101
|
"name",
|
|
61
102
|
"visible",
|
|
62
103
|
"opacity",
|
|
63
|
-
"
|
|
104
|
+
"blendingMode",
|
|
64
105
|
"alphaLock",
|
|
65
|
-
"
|
|
106
|
+
"content"
|
|
66
107
|
],
|
|
67
108
|
properties: {
|
|
68
|
-
|
|
109
|
+
id: {
|
|
69
110
|
type: "string",
|
|
70
111
|
minLength: 1,
|
|
71
|
-
maxLength:
|
|
72
|
-
|
|
112
|
+
maxLength: 100
|
|
113
|
+
},
|
|
114
|
+
name: {
|
|
115
|
+
...name,
|
|
116
|
+
maxLength: 64
|
|
73
117
|
},
|
|
74
118
|
visible: { type: "boolean" },
|
|
75
119
|
opacity: {
|
|
@@ -77,16 +121,16 @@ var documentSchema = {
|
|
|
77
121
|
minimum: 0,
|
|
78
122
|
maximum: 1
|
|
79
123
|
},
|
|
80
|
-
|
|
124
|
+
blendingMode: { enum: DOCUMENT_BLEND_MODES },
|
|
81
125
|
alphaLock: { type: "boolean" },
|
|
82
|
-
image
|
|
126
|
+
content: image
|
|
83
127
|
}
|
|
84
128
|
}
|
|
85
129
|
},
|
|
86
130
|
activeLayer: {
|
|
87
|
-
type: "
|
|
88
|
-
|
|
89
|
-
|
|
131
|
+
type: "string",
|
|
132
|
+
minLength: 1,
|
|
133
|
+
maxLength: 100
|
|
90
134
|
},
|
|
91
135
|
external: {
|
|
92
136
|
type: "array",
|
|
@@ -94,17 +138,39 @@ var documentSchema = {
|
|
|
94
138
|
items: {
|
|
95
139
|
type: "object",
|
|
96
140
|
additionalProperties: false,
|
|
97
|
-
required: ["source", "
|
|
141
|
+
required: ["source", "content"],
|
|
98
142
|
properties: {
|
|
99
143
|
source: {
|
|
100
144
|
type: "string",
|
|
101
145
|
minLength: 1,
|
|
102
146
|
maxLength: 2048
|
|
103
147
|
},
|
|
104
|
-
image
|
|
148
|
+
content: image
|
|
105
149
|
}
|
|
106
150
|
}
|
|
151
|
+
},
|
|
152
|
+
thumbnail: {
|
|
153
|
+
type: "object",
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
required: [
|
|
156
|
+
"width",
|
|
157
|
+
"height",
|
|
158
|
+
"content"
|
|
159
|
+
],
|
|
160
|
+
properties: {
|
|
161
|
+
width: {
|
|
162
|
+
type: "integer",
|
|
163
|
+
minimum: 1,
|
|
164
|
+
maximum: 200
|
|
165
|
+
},
|
|
166
|
+
height: {
|
|
167
|
+
type: "integer",
|
|
168
|
+
minimum: 1,
|
|
169
|
+
maximum: 200
|
|
170
|
+
},
|
|
171
|
+
content: image
|
|
172
|
+
}
|
|
107
173
|
}
|
|
108
174
|
}
|
|
109
175
|
};
|
|
110
|
-
export { DOCUMENT_BLEND_MODES,
|
|
176
|
+
export { DATE_TIME_PATTERN, DOCUMENT_BLEND_MODES, DOCUMENT_SCHEMA_VERSION, DOCUMENT_TYPE, MAX_DOCUMENT_AUTHORS, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_NAME_LENGTH, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, THUMBNAIL_SIZE, documentSchema };
|