@neta-art/cohub 12.5.0 → 13.0.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 (76) hide show
  1. package/README.md +35 -13
  2. package/dist/board/editor/editor.d.ts +7 -5
  3. package/dist/board/editor/index.js +1 -1
  4. package/dist/board/editor/interaction.js +1 -1
  5. package/dist/board/export/index.d.ts +1 -6
  6. package/dist/board/export/index.js +16 -10
  7. package/dist/board/export/node/index.d.ts +2 -7
  8. package/dist/board/export/node/index.js +2 -5
  9. package/dist/board/export/scene.d.ts +3 -7
  10. package/dist/board/export/scene.js +22 -26
  11. package/dist/board/model/asset-key.d.ts +5 -2
  12. package/dist/board/model/asset-key.js +13 -5
  13. package/dist/board/model/index.d.ts +5 -4
  14. package/dist/board/model/index.js +4 -3
  15. package/dist/board/model/media-playback.d.ts +4 -6
  16. package/dist/board/model/media-playback.js +4 -4
  17. package/dist/board/model/read.d.ts +7 -0
  18. package/dist/board/model/read.js +18 -0
  19. package/dist/board/model/themes.d.ts +50 -0
  20. package/dist/board/model/themes.js +44 -6
  21. package/dist/board/render/backdrop-layer.d.ts +21 -0
  22. package/dist/board/render/backdrop-layer.js +227 -0
  23. package/dist/board/render/backdrop.d.ts +45 -0
  24. package/dist/board/render/backdrop.js +62 -0
  25. package/dist/board/render/index.d.ts +3 -2
  26. package/dist/board/render/index.js +7 -6
  27. package/dist/board/render/renderers/arrow-card-renderer.js +1 -1
  28. package/dist/board/render/renderers/audio-card-renderer.js +2 -2
  29. package/dist/board/render/renderers/file-card-renderer.js +1 -1
  30. package/dist/board/render/renderers/frame-card-renderer.js +1 -1
  31. package/dist/board/render/renderers/model-card-renderer.js +1 -1
  32. package/dist/board/render/renderers/shape-card-renderer.js +1 -1
  33. package/dist/board/render/renderers/task-card-renderer.js +2 -2
  34. package/dist/board/render/renderers/text-card-renderer.js +2 -2
  35. package/dist/board/render/renderers/video-card-renderer.js +1 -1
  36. package/dist/board/render/theme.d.ts +2 -0
  37. package/dist/board/render/theme.js +4 -2
  38. package/dist/board/replica/index.d.ts +3 -2
  39. package/dist/board/replica/index.js +34 -24
  40. package/dist/board/replica/indexeddb.js +14 -85
  41. package/dist/board/stage/assets.d.ts +4 -3
  42. package/dist/board/stage/assets.js +186 -83
  43. package/dist/board/stage/backdrop.d.ts +25 -0
  44. package/dist/board/stage/backdrop.js +104 -0
  45. package/dist/board/stage/index.d.ts +2 -1
  46. package/dist/board/stage/scene.js +12 -5
  47. package/dist/board/stage/stage.d.ts +7 -2
  48. package/dist/board/stage/stage.js +44 -24
  49. package/dist/board/stage/texture-pool.d.ts +1 -1
  50. package/dist/board/stage/texture-pool.js +4 -5
  51. package/dist/chunks/app-realtime.js +1 -1
  52. package/dist/chunks/environment.d.ts +34 -21
  53. package/dist/chunks/environment.js +4 -3
  54. package/dist/chunks/http-error.js +28 -5
  55. package/dist/chunks/http.d.ts +87 -18
  56. package/dist/chunks/http.js +1855 -59
  57. package/dist/chunks/websocket.d.ts +9 -1
  58. package/dist/http.d.ts +2 -2
  59. package/dist/http.js +1 -1
  60. package/dist/index.d.ts +9 -3
  61. package/dist/index.js +6 -1080
  62. package/dist/indexeddb.js +79 -0
  63. package/dist/media.d.ts +18 -32
  64. package/dist/media.js +81 -67
  65. package/dist/protocol/dist/board-model.d.ts +33 -26
  66. package/dist/protocol/dist/board-model.js +21 -18
  67. package/dist/protocol/dist/board.d.ts +21 -1
  68. package/dist/protocol/dist/board.js +5 -1
  69. package/dist/protocol/dist/core/media.d.ts +32 -9
  70. package/dist/protocol/dist/core/media.js +203 -48
  71. package/dist/protocol/dist/index.d.ts +3 -3
  72. package/dist/voice-input.js +1 -1
  73. package/dist/websocket.js +37 -5
  74. package/package.json +1 -1
  75. package/dist/board/render/board-background.d.ts +0 -18
  76. package/dist/board/render/board-background.js +0 -104
@@ -0,0 +1,79 @@
1
+ //#region src/indexeddb.ts
2
+ const OPEN_TIMEOUT_MS = 3e3;
3
+ const connections = /* @__PURE__ */ new Map();
4
+ function open(name, version, upgrade) {
5
+ const existing = connections.get(name);
6
+ if (existing) return existing;
7
+ const opening = new Promise((resolve, reject) => {
8
+ const factory = globalThis.indexedDB;
9
+ if (!factory) {
10
+ reject(/* @__PURE__ */ new Error("IndexedDB is unavailable."));
11
+ return;
12
+ }
13
+ let late = false;
14
+ const timer = setTimeout(() => {
15
+ late = true;
16
+ reject(/* @__PURE__ */ new Error("IndexedDB did not open in time."));
17
+ }, OPEN_TIMEOUT_MS);
18
+ const request = factory.open(name, version);
19
+ request.onupgradeneeded = () => upgrade(request.result);
20
+ request.onsuccess = () => {
21
+ clearTimeout(timer);
22
+ const db = request.result;
23
+ if (late) {
24
+ db.close();
25
+ return;
26
+ }
27
+ const forget = () => {
28
+ if (connections.get(name) === opening) connections.delete(name);
29
+ };
30
+ db.onversionchange = () => {
31
+ db.close();
32
+ forget();
33
+ };
34
+ db.onclose = forget;
35
+ resolve(db);
36
+ };
37
+ request.onerror = () => {
38
+ clearTimeout(timer);
39
+ reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB failed to open."));
40
+ };
41
+ });
42
+ connections.set(name, opening);
43
+ opening.catch(() => {
44
+ if (connections.get(name) === opening) connections.delete(name);
45
+ });
46
+ return opening;
47
+ }
48
+ function indexedDb(name, version, upgrade) {
49
+ async function transact(stores, mode, work, retry = true) {
50
+ const db = await open(name, version, upgrade);
51
+ let tx;
52
+ try {
53
+ tx = db.transaction(stores, mode);
54
+ } catch (error) {
55
+ if (!retry) throw error;
56
+ db.close();
57
+ connections.delete(name);
58
+ return transact(stores, mode, work, false);
59
+ }
60
+ return new Promise((resolve, reject) => {
61
+ const result = work(tx);
62
+ tx.oncomplete = () => resolve(result());
63
+ tx.onerror = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed."));
64
+ tx.onabort = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction aborted."));
65
+ });
66
+ }
67
+ return { transact: (stores, mode, work) => transact(stores, mode, work) };
68
+ }
69
+ function deleteByIndex(store, index, key) {
70
+ const request = store.index(index).openKeyCursor(IDBKeyRange.only(key));
71
+ request.onsuccess = () => {
72
+ const cursor = request.result;
73
+ if (!cursor) return;
74
+ store.delete(cursor.primaryKey);
75
+ cursor.continue();
76
+ };
77
+ }
78
+ //#endregion
79
+ export { deleteByIndex, indexedDb };
package/dist/media.d.ts CHANGED
@@ -1,48 +1,34 @@
1
- import { MediaInfo, mediaInfoFromHeaders, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize } from "./protocol/dist/core/media.js";
1
+ import { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, MediaFacts, MediaFit, MediaInfo, MediaSize, MediaSource, MediaVariantSize, VideoFrameOptions, fitsVideoFrames, imageVariantUrl, mediaFacts, mediaInfoFromHeaders, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl } from "./protocol/dist/core/media.js";
2
2
  //#region src/media.d.ts
3
3
  type MediaType = "image" | "video" | "audio";
4
- /**
5
- * `cover` guarantees the short edge (square tiles), `contain` bounds the long
6
- * edge (viewers, textures), `width` bounds only the width, so flowing layouts
7
- * keep the original's rendered size.
8
- */
9
- type MediaFit = "cover" | "contain" | "width";
10
- /** Fixed variant widths keep CDN cache hits high; pick with `mediaVariantSize`. */
11
- declare const MEDIA_VARIANT_SIZES: readonly [192, 384, 768, 1536];
12
- declare const MEDIA_VARIANT_MAX = 1536;
13
- type MediaVariantSize = (typeof MEDIA_VARIANT_SIZES)[number];
14
- /** Smallest variant covering `pixels` device pixels, capped at the largest. */
15
- declare function mediaVariantSize(pixels: number): MediaVariantSize;
16
- /**
17
- * A resized WebP of an image, fitted per `MediaFit`. Never enlarges. GIFs keep
18
- * their original, since a converted variant may lose the animation.
19
- */
20
- declare function imageVariantUrl(url: string, size: MediaVariantSize, fit?: MediaFit): string | null;
21
- /** A still JPEG of a video's first frame, `width` wide. */
22
- declare function videoFrameUrl(url: string, width: MediaVariantSize): string | null;
23
- /**
24
- * The media a CDN variant was derived from, to fall back to when processing
25
- * fails. Only the process parameter is cut, so the rest of the URL (signed
26
- * queries included) stays byte-for-byte.
27
- */
28
- declare function variantSource(url: string): {
4
+ type ResolvedFile = {
29
5
  url: string;
30
- type: "image" | "video";
31
- } | null;
6
+ media?: MediaFacts;
7
+ };
8
+ type ResolveFileOptions = {
9
+ /** `playback` needs a streamable URL; `preview` (the default) may take an inline one. */
10
+ purpose?: "preview" | "playback";
11
+ fresh?: boolean;
12
+ };
32
13
  type MediaPreviewSource = {
33
14
  type: MediaType;
34
15
  url?: string | null;
35
16
  /** Provider cover or poster: art chosen for the work, so it ranks first. */
36
17
  previewUrl?: string | null;
18
+ width?: number | null;
19
+ height?: number | null;
37
20
  };
38
21
  /**
39
- * Preview images to try in order: the provider cover (resized, then as is),
40
- * then a snapshot of the video itself, then the probed first frame.
41
- * An empty list means only the media element itself can render a frame.
22
+ * Preview images to try in order: the provider cover, a CDN frame of the
23
+ * video, then its probed first frame. The CDN frame needs no request, so the
24
+ * first paint never waits and a later probe never swaps the shown still.
42
25
  */
43
26
  declare function mediaPreviewCandidates(media: MediaPreviewSource, options: {
44
27
  size: MediaVariantSize;
45
28
  fit?: MediaFit;
46
29
  }, info?: Pick<MediaInfo, "firstFrameUrl">): string[];
30
+ declare function cachedMediaInfo(url: string): MediaInfo | undefined;
31
+ declare function loadMediaInfo(url: string, type: MediaType): Promise<MediaInfo>;
32
+ declare function loadMediaSize(url: string, type: "image" | "video"): Promise<MediaSize | null>;
47
33
  //#endregion
48
- export { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, MediaFit, type MediaInfo, MediaPreviewSource, MediaType, MediaVariantSize, imageVariantUrl, mediaInfoFromHeaders, mediaPreviewCandidates, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl };
34
+ export { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, type MediaFacts, type MediaFit, type MediaInfo, MediaPreviewSource, type MediaSize, type MediaSource, MediaType, type MediaVariantSize, ResolveFileOptions, ResolvedFile, type VideoFrameOptions, cachedMediaInfo, fitsVideoFrames, imageVariantUrl, loadMediaInfo, loadMediaSize, mediaFacts, mediaInfoFromHeaders, mediaPreviewCandidates, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl };
package/dist/media.js CHANGED
@@ -1,69 +1,9 @@
1
- import { mediaHost, mediaInfoFromHeaders, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize } from "./protocol/dist/core/media.js";
1
+ import { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, fitsVideoFrames, imageVariantUrl, mediaFacts, mediaInfoFromHeaders, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl } from "./protocol/dist/core/media.js";
2
2
  //#region src/media.ts
3
3
  /**
4
- * Remote media delivery. Previews and metadata resolve through ordered
5
- * strategies — the cheapest trustworthy source first, each later one only
6
- * filling what the earlier ones left — so every consumer degrades the same way.
7
- *
8
- * URL rewrites happen locally without a request, so they apply only to hosts
9
- * known to serve Aliyun OSS processing; any other URL is used as is.
10
- */
11
- const RESIZE = {
12
- cover: (size) => `m_mfit,w_${size},h_${size}`,
13
- contain: (size) => `m_lfit,w_${size},h_${size}`,
14
- width: (size) => `w_${size}`
15
- };
16
- /** Snapshots render at the requested width, upscaling small videos; cap it. */
17
- const VIDEO_FRAME_MAX_WIDTH = 768;
18
- /** Fixed variant widths keep CDN cache hits high; pick with `mediaVariantSize`. */
19
- const MEDIA_VARIANT_SIZES = [
20
- 192,
21
- 384,
22
- 768,
23
- 1536
24
- ];
25
- const MEDIA_VARIANT_MAX = 1536;
26
- /** Smallest variant covering `pixels` device pixels, capped at the largest. */
27
- function mediaVariantSize(pixels) {
28
- return MEDIA_VARIANT_SIZES.find((size) => size >= pixels) ?? 1536;
29
- }
30
- function ossHost(url) {
31
- const host = mediaHost(url);
32
- return host?.transform === "oss" ? host : void 0;
33
- }
34
- /**
35
- * A resized WebP of an image, fitted per `MediaFit`. Never enlarges. GIFs keep
36
- * their original, since a converted variant may lose the animation.
37
- */
38
- function imageVariantUrl(url, size, fit = "contain") {
39
- if (!ossHost(url) || /\.gif$/i.test(new URL(url).pathname)) return null;
40
- return ossProcessUrl(url, `image/resize,${RESIZE[fit](size)}/quality,q_82/format,webp`);
41
- }
42
- /** A still JPEG of a video's first frame, `width` wide. */
43
- function videoFrameUrl(url, width) {
44
- if (!ossHost(url)) return null;
45
- return ossProcessUrl(url, `video/snapshot,t_0,f_jpg,w_${width},m_fast`);
46
- }
47
- /**
48
- * The media a CDN variant was derived from, to fall back to when processing
49
- * fails. Only the process parameter is cut, so the rest of the URL (signed
50
- * queries included) stays byte-for-byte.
51
- */
52
- function variantSource(url) {
53
- const match = /([?&])x-oss-process=([^&#]*)/.exec(url);
54
- if (!match) return null;
55
- const [segment, separator, process = ""] = match;
56
- const head = url.slice(0, match.index);
57
- const tail = url.slice(match.index + segment.length);
58
- return {
59
- url: separator === "?" && tail.startsWith("&") ? `${head}?${tail.slice(1)}` : head + tail,
60
- type: process.startsWith("video/") ? "video" : "image"
61
- };
62
- }
63
- /**
64
- * Preview images to try in order: the provider cover (resized, then as is),
65
- * then a snapshot of the video itself, then the probed first frame.
66
- * An empty list means only the media element itself can render a frame.
4
+ * Preview images to try in order: the provider cover, a CDN frame of the
5
+ * video, then its probed first frame. The CDN frame needs no request, so the
6
+ * first paint never waits and a later probe never swaps the shown still.
67
7
  */
68
8
  function mediaPreviewCandidates(media, options, info) {
69
9
  const candidates = [];
@@ -77,11 +17,85 @@ function mediaPreviewCandidates(media, options, info) {
77
17
  };
78
18
  addImage(media.type === "image" ? media.url : media.previewUrl);
79
19
  if (media.type === "video" && media.url) {
80
- const width = options.fit === "cover" ? options.size * 2 : options.size;
81
- add(videoFrameUrl(media.url, mediaVariantSize(Math.min(width, VIDEO_FRAME_MAX_WIDTH))));
20
+ const natural = media.width && media.height ? {
21
+ width: media.width,
22
+ height: media.height
23
+ } : null;
24
+ add(videoFrameUrl(media.url, options.size, {
25
+ fit: options.fit,
26
+ natural
27
+ }));
82
28
  addImage(info?.firstFrameUrl);
83
29
  }
84
30
  return candidates;
85
31
  }
32
+ const INFO_CONCURRENCY = 4;
33
+ const INFO_LIMIT = 2e3;
34
+ const EMPTY_INFO_RETRY_MS = 6e4;
35
+ const infoCache = /* @__PURE__ */ new Map();
36
+ const infoRequests = /* @__PURE__ */ new Map();
37
+ const infoWaiting = [];
38
+ let infoActive = 0;
39
+ function cachedMediaInfo(url) {
40
+ const entry = infoCache.get(url);
41
+ if (!entry) return void 0;
42
+ infoCache.delete(url);
43
+ if (entry.expiresAt <= Date.now()) return void 0;
44
+ infoCache.set(url, entry);
45
+ return entry.info;
46
+ }
47
+ async function withInfoSlot(run) {
48
+ if (infoActive < INFO_CONCURRENCY) infoActive += 1;
49
+ else await new Promise((resolve) => infoWaiting.push(resolve));
50
+ try {
51
+ return await run();
52
+ } finally {
53
+ const next = infoWaiting.shift();
54
+ if (next) next();
55
+ else infoActive -= 1;
56
+ }
57
+ }
58
+ function loadMediaInfo(url, type) {
59
+ const hit = cachedMediaInfo(url);
60
+ if (hit) return Promise.resolve(hit);
61
+ let request = infoRequests.get(url);
62
+ if (!request) {
63
+ request = withInfoSlot(() => probeMediaInfo(url, { type })).then((info) => {
64
+ const empty = Object.keys(info).length === 0;
65
+ infoCache.set(url, {
66
+ info,
67
+ expiresAt: empty ? Date.now() + EMPTY_INFO_RETRY_MS : Number.POSITIVE_INFINITY
68
+ });
69
+ if (infoCache.size > INFO_LIMIT) infoCache.delete(infoCache.keys().next().value);
70
+ return info;
71
+ }).finally(() => infoRequests.delete(url));
72
+ infoRequests.set(url, request);
73
+ }
74
+ return request;
75
+ }
76
+ const sizeRequests = /* @__PURE__ */ new Map();
77
+ function loadMediaSize(url, type) {
78
+ let request = sizeRequests.get(url);
79
+ if (!request) {
80
+ request = loadMediaInfo(url, type).then(async ({ width, height }) => {
81
+ if (width && height) return {
82
+ width,
83
+ height
84
+ };
85
+ if (type !== "video") return null;
86
+ const frame = await withInfoSlot(() => probeVideoFrameSize(url)).catch(() => ({}));
87
+ return frame.width && frame.height ? {
88
+ width: frame.width,
89
+ height: frame.height
90
+ } : null;
91
+ });
92
+ sizeRequests.set(url, request);
93
+ if (sizeRequests.size > INFO_LIMIT) sizeRequests.delete(sizeRequests.keys().next().value);
94
+ request.then((size) => {
95
+ if (!size && sizeRequests.get(url) === request) sizeRequests.delete(url);
96
+ });
97
+ }
98
+ return request;
99
+ }
86
100
  //#endregion
87
- export { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, imageVariantUrl, mediaInfoFromHeaders, mediaPreviewCandidates, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl };
101
+ export { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, cachedMediaInfo, fitsVideoFrames, imageVariantUrl, loadMediaInfo, loadMediaSize, mediaFacts, mediaInfoFromHeaders, mediaPreviewCandidates, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl };
@@ -2201,52 +2201,59 @@ declare const BoardAnimationSchema: z.ZodObject<{
2201
2201
  type BoardAnimation = z.infer<typeof BoardAnimationSchema>;
2202
2202
  declare const BOARD_ENTER_PRESETS: readonly ["fade-in", "rise", "drop", "pop", "deal"];
2203
2203
  type BoardEnterPreset = (typeof BOARD_ENTER_PRESETS)[number];
2204
+ declare const BOARD_BACKGROUND_PATTERNS: readonly ["none", "dots", "grid"];
2205
+ type BoardBackgroundPattern = (typeof BOARD_BACKGROUND_PATTERNS)[number];
2206
+ declare const BOARD_BACKGROUND_FITS: readonly ["cover", "contain", "repeat"];
2207
+ type BoardBackgroundFit = (typeof BOARD_BACKGROUND_FITS)[number];
2204
2208
  declare const BoardBackgroundSchema: z.ZodObject<{
2205
- kind: z.ZodDefault<z.ZodEnum<{
2206
- dots: "dots";
2207
- grid: "grid";
2208
- image: "image";
2209
- solid: "solid";
2210
- }>>;
2211
2209
  color: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
2212
2210
  light: z.ZodString;
2213
2211
  dark: z.ZodString;
2214
2212
  }, z.core.$strict>]>>;
2215
- imageUrl: z.ZodOptional<z.ZodString>;
2216
- fit: z.ZodOptional<z.ZodEnum<{
2217
- contain: "contain";
2218
- cover: "cover";
2219
- repeat: "repeat";
2213
+ pattern: z.ZodOptional<z.ZodEnum<{
2214
+ dots: "dots";
2215
+ grid: "grid";
2216
+ none: "none";
2220
2217
  }>>;
2221
- opacity: z.ZodOptional<z.ZodNumber>;
2218
+ image: z.ZodOptional<z.ZodObject<{
2219
+ src: z.ZodString;
2220
+ fit: z.ZodDefault<z.ZodEnum<{
2221
+ contain: "contain";
2222
+ cover: "cover";
2223
+ repeat: "repeat";
2224
+ }>>;
2225
+ opacity: z.ZodOptional<z.ZodNumber>;
2226
+ }, z.core.$strict>>;
2222
2227
  }, z.core.$strict>;
2228
+ type BoardBackground = z.infer<typeof BoardBackgroundSchema>;
2223
2229
  declare const BOARD_GRID_MIN_SIZE = 4;
2224
2230
  declare const BOARD_GRID_MAX_SIZE = 512;
2225
2231
  declare const BOARD_GRID_DEFAULT_SIZE = 24;
2226
2232
  declare const BoardSettingsSchema: z.ZodObject<{
2227
2233
  title: z.ZodOptional<z.ZodString>;
2228
2234
  background: z.ZodDefault<z.ZodObject<{
2229
- kind: z.ZodDefault<z.ZodEnum<{
2230
- dots: "dots";
2231
- grid: "grid";
2232
- image: "image";
2233
- solid: "solid";
2234
- }>>;
2235
2235
  color: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
2236
2236
  light: z.ZodString;
2237
2237
  dark: z.ZodString;
2238
2238
  }, z.core.$strict>]>>;
2239
- imageUrl: z.ZodOptional<z.ZodString>;
2240
- fit: z.ZodOptional<z.ZodEnum<{
2241
- contain: "contain";
2242
- cover: "cover";
2243
- repeat: "repeat";
2239
+ pattern: z.ZodOptional<z.ZodEnum<{
2240
+ dots: "dots";
2241
+ grid: "grid";
2242
+ none: "none";
2244
2243
  }>>;
2245
- opacity: z.ZodOptional<z.ZodNumber>;
2244
+ image: z.ZodOptional<z.ZodObject<{
2245
+ src: z.ZodString;
2246
+ fit: z.ZodDefault<z.ZodEnum<{
2247
+ contain: "contain";
2248
+ cover: "cover";
2249
+ repeat: "repeat";
2250
+ }>>;
2251
+ opacity: z.ZodOptional<z.ZodNumber>;
2252
+ }, z.core.$strict>>;
2246
2253
  }, z.core.$strict>>;
2247
2254
  grid: z.ZodOptional<z.ZodObject<{
2248
- visible: z.ZodDefault<z.ZodBoolean>;
2249
2255
  size: z.ZodDefault<z.ZodNumber>;
2256
+ snap: z.ZodDefault<z.ZodBoolean>;
2250
2257
  }, z.core.$strict>>;
2251
2258
  enter: z.ZodOptional<z.ZodObject<{
2252
2259
  preset: z.ZodEnum<{
@@ -2288,4 +2295,4 @@ type BoardDelta = {
2288
2295
  };
2289
2296
  declare function isEmptyBoardDelta(delta: BoardDelta): boolean;
2290
2297
  //#endregion
2291
- export { BOARD_ANCHOR_SIDES, BOARD_ARROW_ROUTES, BOARD_CAMERA_TARGET, BOARD_COLOR_TOKENS, BOARD_DASHES, BOARD_DERIVED_SIZE_TYPES, BOARD_EFFECT_KINDS, BOARD_EFFECT_KIND_INFO, BOARD_ENTER_PRESETS, BOARD_EXTENSION_TYPE_PATTERN, BOARD_GRID_DEFAULT_SIZE, BOARD_GRID_MAX_SIZE, BOARD_GRID_MIN_SIZE, BOARD_ID_MAX_LENGTH, BOARD_ITEM_SCHEMAS, BOARD_ITEM_TYPES, BOARD_PLAY_MODES, BOARD_SHAPE_GEOMETRIES, BOARD_TASK_ARTIFACT_LIMIT, BOARD_TEXT_ALIGNS, BoardAnchorSide, BoardAnimation, BoardAnimationHeader, BoardAnimationHeaderSchema, BoardAnimationSchema, BoardArrowAnchor, BoardArrowAnchorSchema, BoardArrowBinding, BoardArrowBindingSchema, BoardArrowEnd, BoardArrowEndSchema, BoardArrowItem, BoardArrowItemSchema, BoardArrowRoute, BoardAudioItem, BoardAudioItemSchema, BoardBackgroundSchema, BoardBuiltinItem, BoardBuiltinItemType, BoardColor, BoardColorSchema, BoardColorToken, BoardCropSchema, BoardCustomItem, BoardCustomItemSchema, BoardDash, BoardDelta, BoardDiagnostic, BoardDocument, BoardDrawItem, BoardDrawItemSchema, BoardDrawPoint, BoardDrawPointSchema, BoardEaseSchema, BoardEffectItem, BoardEffectItemSchema, BoardEffectKind, BoardEnterPreset, BoardExtensionItem, BoardExtensionItemSchema, BoardFileBackedItem, BoardFileItem, BoardFileItemSchema, BoardFrameItem, BoardFrameItemSchema, BoardIdSchema, BoardImageItem, BoardImageItemSchema, BoardItem, BoardItemPreview, BoardItemPreviewSchema, BoardItemType, BoardKeySchema, BoardKeyframe, BoardKeyframeSchema, BoardMarker, BoardMarkerSchema, BoardMediaOrigin, BoardMediaOriginSchema, BoardMediaSnapshot, BoardMediaSnapshotSchema, BoardMediaSource, BoardMediaSourceSchema, BoardPatch, BoardPatchSchema, BoardPlayMode, BoardScript, BoardScriptSchema, BoardSettings, BoardSettingsSchema, BoardShapeGeometry, BoardShapeItem, BoardShapeItemSchema, BoardSize, BoardSizeSchema, BoardStyle, BoardStyleSchema, BoardTaskArtifact, BoardTaskArtifactSchema, BoardTaskItem, BoardTaskItemSchema, BoardTaskSnapshot, BoardTaskSnapshotSchema, BoardTextItem, BoardTextItemSchema, BoardTrack, BoardTrackSchema, BoardVec2, BoardVec2Schema, BoardVideoItem, BoardVideoItemSchema, boardItemPreview, boardItemSchema, emptyBoardDocument, hasAuthoredSize, isBoardColorString, isBoardEase, isBuiltinItem, isBuiltinItemType, isEmptyBoardDelta, isFileBackedItem, isMediaItem, isSafeBoardSourcePath, parseBoardItem, parseBoardMediaSource };
2298
+ export { BOARD_ANCHOR_SIDES, BOARD_ARROW_ROUTES, BOARD_BACKGROUND_FITS, BOARD_BACKGROUND_PATTERNS, BOARD_CAMERA_TARGET, BOARD_COLOR_TOKENS, BOARD_DASHES, BOARD_DERIVED_SIZE_TYPES, BOARD_EFFECT_KINDS, BOARD_EFFECT_KIND_INFO, BOARD_ENTER_PRESETS, BOARD_EXTENSION_TYPE_PATTERN, BOARD_GRID_DEFAULT_SIZE, BOARD_GRID_MAX_SIZE, BOARD_GRID_MIN_SIZE, BOARD_ID_MAX_LENGTH, BOARD_ITEM_SCHEMAS, BOARD_ITEM_TYPES, BOARD_PLAY_MODES, BOARD_SHAPE_GEOMETRIES, BOARD_TASK_ARTIFACT_LIMIT, BOARD_TEXT_ALIGNS, BoardAnchorSide, BoardAnimation, BoardAnimationHeader, BoardAnimationHeaderSchema, BoardAnimationSchema, BoardArrowAnchor, BoardArrowAnchorSchema, BoardArrowBinding, BoardArrowBindingSchema, BoardArrowEnd, BoardArrowEndSchema, BoardArrowItem, BoardArrowItemSchema, BoardArrowRoute, BoardAudioItem, BoardAudioItemSchema, BoardBackground, BoardBackgroundFit, BoardBackgroundPattern, BoardBackgroundSchema, BoardBuiltinItem, BoardBuiltinItemType, BoardColor, BoardColorSchema, BoardColorToken, BoardCropSchema, BoardCustomItem, BoardCustomItemSchema, BoardDash, BoardDelta, BoardDiagnostic, BoardDocument, BoardDrawItem, BoardDrawItemSchema, BoardDrawPoint, BoardDrawPointSchema, BoardEaseSchema, BoardEffectItem, BoardEffectItemSchema, BoardEffectKind, BoardEnterPreset, BoardExtensionItem, BoardExtensionItemSchema, BoardFileBackedItem, BoardFileItem, BoardFileItemSchema, BoardFrameItem, BoardFrameItemSchema, BoardIdSchema, BoardImageItem, BoardImageItemSchema, BoardItem, BoardItemPreview, BoardItemPreviewSchema, BoardItemType, BoardKeySchema, BoardKeyframe, BoardKeyframeSchema, BoardMarker, BoardMarkerSchema, BoardMediaOrigin, BoardMediaOriginSchema, BoardMediaSnapshot, BoardMediaSnapshotSchema, BoardMediaSource, BoardMediaSourceSchema, BoardPatch, BoardPatchSchema, BoardPlayMode, BoardScript, BoardScriptSchema, BoardSettings, BoardSettingsSchema, BoardShapeGeometry, BoardShapeItem, BoardShapeItemSchema, BoardSize, BoardSizeSchema, BoardStyle, BoardStyleSchema, BoardTaskArtifact, BoardTaskArtifactSchema, BoardTaskItem, BoardTaskItemSchema, BoardTaskSnapshot, BoardTaskSnapshotSchema, BoardTextItem, BoardTextItemSchema, BoardTrack, BoardTrackSchema, BoardVec2, BoardVec2Schema, BoardVideoItem, BoardVideoItemSchema, boardItemPreview, boardItemSchema, emptyBoardDocument, hasAuthoredSize, isBoardColorString, isBoardEase, isBuiltinItem, isBuiltinItemType, isEmptyBoardDelta, isFileBackedItem, isMediaItem, isSafeBoardSourcePath, parseBoardItem, parseBoardMediaSource };
@@ -574,31 +574,34 @@ const BOARD_ENTER_PRESETS = [
574
574
  "pop",
575
575
  "deal"
576
576
  ];
577
+ const BOARD_BACKGROUND_PATTERNS = [
578
+ "none",
579
+ "dots",
580
+ "grid"
581
+ ];
582
+ const BOARD_BACKGROUND_FITS = [
583
+ "cover",
584
+ "contain",
585
+ "repeat"
586
+ ];
577
587
  const BoardBackgroundSchema = z.object({
578
- kind: z.enum([
579
- "solid",
580
- "dots",
581
- "grid",
582
- "image"
583
- ]).default("dots"),
584
588
  color: BoardColorSchema.optional(),
585
- imageUrl: z.string().url().max(4096).optional().describe("Background image for kind image."),
586
- fit: z.enum([
587
- "cover",
588
- "contain",
589
- "repeat"
590
- ]).optional(),
591
- opacity: ratio.optional()
592
- }).strict();
589
+ pattern: z.enum(BOARD_BACKGROUND_PATTERNS).optional().describe("Spaced by grid.size; omit to follow the theme."),
590
+ image: z.object({
591
+ src: BoardMediaSourceSchema,
592
+ fit: z.enum(BOARD_BACKGROUND_FITS).default("cover").describe("cover and contain fill the view; repeat tiles the Board at one board unit per image pixel."),
593
+ opacity: ratio.optional()
594
+ }).strict().optional()
595
+ }).strict().describe("Layers drawn under every item: color, then image, then pattern. Without a color or image the theme's backdrop shows.");
593
596
  const BOARD_GRID_MIN_SIZE = 4;
594
597
  const BOARD_GRID_MAX_SIZE = 512;
595
598
  const BOARD_GRID_DEFAULT_SIZE = 24;
596
599
  const BoardSettingsSchema = z.object({
597
600
  title: z.string().min(1).max(255).optional(),
598
- background: BoardBackgroundSchema.default({ kind: "dots" }),
601
+ background: BoardBackgroundSchema.default({}),
599
602
  grid: z.object({
600
- visible: z.boolean().default(false).describe("Show the grid overlay."),
601
- size: finite.min(4).max(512).default(24)
603
+ size: finite.min(4).max(512).default(24).describe("Pattern spacing and snap step, in board units."),
604
+ snap: z.boolean().default(false).describe("Snap moved and resized items to the grid.")
602
605
  }).strict().optional(),
603
606
  enter: z.object({
604
607
  preset: z.enum(BOARD_ENTER_PRESETS),
@@ -622,4 +625,4 @@ function isEmptyBoardDelta(delta) {
622
625
  return !delta.board && Object.keys(delta.items ?? {}).length === 0 && Object.keys(delta.animations ?? {}).length === 0 && Object.keys(delta.tracks ?? {}).length === 0;
623
626
  }
624
627
  //#endregion
625
- export { BOARD_ANCHOR_SIDES, BOARD_ARROW_ROUTES, BOARD_CAMERA_TARGET, BOARD_COLOR_TOKENS, BOARD_DASHES, BOARD_DERIVED_SIZE_TYPES, BOARD_EFFECT_KINDS, BOARD_EFFECT_KIND_INFO, BOARD_ENTER_PRESETS, BOARD_EXTENSION_TYPE_PATTERN, BOARD_GRID_DEFAULT_SIZE, BOARD_GRID_MAX_SIZE, BOARD_GRID_MIN_SIZE, BOARD_ID_MAX_LENGTH, BOARD_ITEM_SCHEMAS, BOARD_ITEM_TYPES, BOARD_PLAY_MODES, BOARD_SHAPE_GEOMETRIES, BOARD_TASK_ARTIFACT_LIMIT, BOARD_TEXT_ALIGNS, BoardAnimationHeaderSchema, BoardAnimationSchema, BoardArrowAnchorSchema, BoardArrowBindingSchema, BoardArrowEndSchema, BoardArrowItemSchema, BoardAudioItemSchema, BoardBackgroundSchema, BoardColorSchema, BoardCropSchema, BoardCustomItemSchema, BoardDrawItemSchema, BoardDrawPointSchema, BoardEaseSchema, BoardEffectItemSchema, BoardExtensionItemSchema, BoardFileItemSchema, BoardFrameItemSchema, BoardIdSchema, BoardImageItemSchema, BoardItemPreviewSchema, BoardKeySchema, BoardKeyframeSchema, BoardMarkerSchema, BoardMediaOriginSchema, BoardMediaSnapshotSchema, BoardMediaSourceSchema, BoardPatchSchema, BoardScriptSchema, BoardSettingsSchema, BoardShapeItemSchema, BoardSizeSchema, BoardStyleSchema, BoardTaskArtifactSchema, BoardTaskItemSchema, BoardTaskSnapshotSchema, BoardTextItemSchema, BoardTrackSchema, BoardVec2Schema, BoardVideoItemSchema, boardItemPreview, boardItemSchema, emptyBoardDocument, hasAuthoredSize, isBoardColorString, isBoardEase, isBuiltinItem, isBuiltinItemType, isEmptyBoardDelta, isFileBackedItem, isMediaItem, isSafeBoardSourcePath, parseBoardItem, parseBoardMediaSource };
628
+ export { BOARD_ANCHOR_SIDES, BOARD_ARROW_ROUTES, BOARD_BACKGROUND_FITS, BOARD_BACKGROUND_PATTERNS, BOARD_CAMERA_TARGET, BOARD_COLOR_TOKENS, BOARD_DASHES, BOARD_DERIVED_SIZE_TYPES, BOARD_EFFECT_KINDS, BOARD_EFFECT_KIND_INFO, BOARD_ENTER_PRESETS, BOARD_EXTENSION_TYPE_PATTERN, BOARD_GRID_DEFAULT_SIZE, BOARD_GRID_MAX_SIZE, BOARD_GRID_MIN_SIZE, BOARD_ID_MAX_LENGTH, BOARD_ITEM_SCHEMAS, BOARD_ITEM_TYPES, BOARD_PLAY_MODES, BOARD_SHAPE_GEOMETRIES, BOARD_TASK_ARTIFACT_LIMIT, BOARD_TEXT_ALIGNS, BoardAnimationHeaderSchema, BoardAnimationSchema, BoardArrowAnchorSchema, BoardArrowBindingSchema, BoardArrowEndSchema, BoardArrowItemSchema, BoardAudioItemSchema, BoardBackgroundSchema, BoardColorSchema, BoardCropSchema, BoardCustomItemSchema, BoardDrawItemSchema, BoardDrawPointSchema, BoardEaseSchema, BoardEffectItemSchema, BoardExtensionItemSchema, BoardFileItemSchema, BoardFrameItemSchema, BoardIdSchema, BoardImageItemSchema, BoardItemPreviewSchema, BoardKeySchema, BoardKeyframeSchema, BoardMarkerSchema, BoardMediaOriginSchema, BoardMediaSnapshotSchema, BoardMediaSourceSchema, BoardPatchSchema, BoardScriptSchema, BoardSettingsSchema, BoardShapeItemSchema, BoardSizeSchema, BoardStyleSchema, BoardTaskArtifactSchema, BoardTaskItemSchema, BoardTaskSnapshotSchema, BoardTextItemSchema, BoardTrackSchema, BoardVec2Schema, BoardVideoItemSchema, boardItemPreview, boardItemSchema, emptyBoardDocument, hasAuthoredSize, isBoardColorString, isBoardEase, isBuiltinItem, isBuiltinItemType, isEmptyBoardDelta, isFileBackedItem, isMediaItem, isSafeBoardSourcePath, parseBoardItem, parseBoardMediaSource };
@@ -14,6 +14,25 @@ type BoardManifest = z.infer<typeof BoardManifestSchema>;
14
14
  declare function parseBoardManifest(input: string | unknown): BoardManifest;
15
15
  declare function serializeBoardManifest(manifest: BoardManifest): string;
16
16
  declare function isBoardPath(path: string): boolean;
17
+ declare const BoardReadInputSchema: z.ZodObject<{
18
+ only: z.ZodOptional<z.ZodArray<z.ZodEnum<{
19
+ animations: "animations";
20
+ items: "items";
21
+ }>>>;
22
+ rect: z.ZodOptional<z.ZodObject<{
23
+ x: z.ZodNumber;
24
+ y: z.ZodNumber;
25
+ width: z.ZodNumber;
26
+ height: z.ZodNumber;
27
+ }, z.core.$strict>>;
28
+ within: z.ZodOptional<z.ZodString>;
29
+ items: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
+ animations: z.ZodOptional<z.ZodArray<z.ZodString>>;
31
+ limit: z.ZodOptional<z.ZodNumber>;
32
+ cursor: z.ZodOptional<z.ZodString>;
33
+ since: z.ZodOptional<z.ZodNumber>;
34
+ }, z.core.$strict>;
35
+ type BoardReadInput = z.infer<typeof BoardReadInputSchema>;
17
36
  type BoardReadResult = {
18
37
  id: string;
19
38
  title: string;
@@ -24,6 +43,7 @@ type BoardReadResult = {
24
43
  animations?: Record<string, Record<string, unknown>>;
25
44
  playback: BoardPlaybackSnapshot | null;
26
45
  next?: string;
46
+ changes?: BoardDelta[];
27
47
  };
28
48
  type BoardChangeSummary = {
29
49
  board: boolean;
@@ -94,4 +114,4 @@ declare const BoardPlaybackCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
94
114
  }, z.core.$strict>], "type">;
95
115
  type BoardPlaybackCommand = z.infer<typeof BoardPlaybackCommandSchema>;
96
116
  //#endregion
97
- export { BOARD_CLIPBOARD_KIND, BOARD_CLIPBOARD_MIME, BoardApplyResult, BoardChangeSummary, BoardHistoryPage, BoardManifest, BoardManifestSchema, type BoardPatch, BoardPlaybackCommand, BoardPlaybackCommandSchema, BoardPlaybackSnapshot, BoardPlaybackStatus, BoardReadResult, BoardTransactionRecord, isBoardPath, parseBoardManifest, serializeBoardManifest };
117
+ export { BOARD_CLIPBOARD_KIND, BOARD_CLIPBOARD_MIME, BoardApplyResult, BoardChangeSummary, BoardHistoryPage, BoardManifest, BoardManifestSchema, type BoardPatch, BoardPlaybackCommand, BoardPlaybackCommandSchema, BoardPlaybackSnapshot, BoardPlaybackStatus, BoardReadInput, BoardReadInputSchema, BoardReadResult, BoardTransactionRecord, isBoardPath, parseBoardManifest, serializeBoardManifest };
@@ -50,10 +50,14 @@ z.object({
50
50
  items: z.array(z.string().min(1)).max(5e4).optional(),
51
51
  animations: z.array(z.string().min(1)).max(1e4).optional(),
52
52
  limit: z.number().int().min(1).max(5e4).optional(),
53
- cursor: z.string().min(1).optional()
53
+ cursor: z.string().min(1).optional(),
54
+ since: z.number().int().nonnegative().optional()
54
55
  }).strict().refine((read) => !(read.cursor && (read.rect || read.within)), {
55
56
  path: ["cursor"],
56
57
  message: "cursor pages by id, so it cannot be combined with rect or within"
58
+ }).refine((read) => read.since === void 0 || Object.entries(read).every(([key, value]) => key === "since" || value === void 0), {
59
+ path: ["since"],
60
+ message: "since syncs the whole Board, so it cannot be combined with other read options"
57
61
  });
58
62
  z.object({
59
63
  patch: BoardPatchSchema,
@@ -1,6 +1,30 @@
1
1
  //#region ../protocol/dist/core/media.d.ts
2
+ type MediaFit = "cover" | "contain" | "width";
3
+ type MediaSize = {
4
+ width: number;
5
+ height: number;
6
+ };
7
+ type MediaSource = {
8
+ url: string;
9
+ type: "image" | "video";
10
+ };
11
+ /** Fixed variant edges keep CDN cache hits high; pick one with `mediaVariantSize`. */
12
+ declare const MEDIA_VARIANT_SIZES: readonly [192, 384, 768, 1536];
13
+ declare const MEDIA_VARIANT_MAX = 1536;
14
+ type MediaVariantSize = (typeof MEDIA_VARIANT_SIZES)[number];
15
+ /** Smallest variant covering `pixels` device pixels, capped at the largest. */
16
+ declare function mediaVariantSize(pixels: number): MediaVariantSize;
2
17
  /** Append an OSS process to `url`, keeping its query and hash. */
3
18
  declare function ossProcessUrl(url: string, process: string): string;
19
+ declare function imageVariantUrl(url: string, size: MediaVariantSize, fit?: MediaFit): string | null;
20
+ type VideoFrameOptions = {
21
+ fit?: MediaFit;
22
+ natural?: MediaSize | null;
23
+ aspect?: number | null;
24
+ };
25
+ declare function videoFrameUrl(url: string, size: MediaVariantSize, options?: VideoFrameOptions): string | null;
26
+ declare function fitsVideoFrames(url: string): boolean;
27
+ declare function variantSource(url: string): MediaSource | null;
4
28
  type MediaInfo = {
5
29
  width?: number;
6
30
  height?: number;
@@ -11,6 +35,12 @@ type MediaInfo = {
11
35
  mimeType?: string;
12
36
  bytes?: number;
13
37
  };
38
+ type MediaFacts = {
39
+ width: number;
40
+ height: number;
41
+ durationMs?: number;
42
+ };
43
+ declare function mediaFacts(facts: Partial<MediaFacts> | null | undefined): MediaFacts | null;
14
44
  /** Media facts from response headers; `x-oss-meta-*` need CORS exposure in browsers. */
15
45
  declare function mediaInfoFromHeaders(headers: Headers, url: string): MediaInfo;
16
46
  declare function mergeMediaInfo(primary: MediaInfo, fallback: MediaInfo): MediaInfo;
@@ -20,10 +50,6 @@ type ProbeOptions = {
20
50
  signal?: AbortSignal;
21
51
  };
22
52
  declare function probeMediaInfo(url: string, options: ProbeOptions): Promise<MediaInfo>;
23
- /**
24
- * A video's dimensions, read from the header of its first frame rendered as a
25
- * full-size PNG by OSS; only the first 24 bytes are read. Throws on failure.
26
- */
27
53
  declare function probeVideoFrameSize(url: string, options?: {
28
54
  fetch?: typeof globalThis.fetch;
29
55
  signal?: AbortSignal;
@@ -31,9 +57,6 @@ declare function probeVideoFrameSize(url: string, options?: {
31
57
  declare function probeMediaSize(url: string, type: "image" | "video", options?: {
32
58
  fetch?: typeof globalThis.fetch;
33
59
  signal?: AbortSignal;
34
- }): Promise<{
35
- width: number;
36
- height: number;
37
- } | null>;
60
+ }): Promise<MediaSize | null>;
38
61
  //#endregion
39
- export { MediaInfo, mediaInfoFromHeaders, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize };
62
+ export { MEDIA_VARIANT_MAX, MEDIA_VARIANT_SIZES, MediaFacts, MediaFit, MediaInfo, MediaSize, MediaSource, MediaVariantSize, VideoFrameOptions, fitsVideoFrames, imageVariantUrl, mediaFacts, mediaInfoFromHeaders, mediaVariantSize, mergeMediaInfo, ossProcessUrl, probeMediaInfo, probeMediaSize, probeVideoFrameSize, variantSource, videoFrameUrl };