@neta-art/cohub 12.1.1 → 12.3.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 +85 -8
- package/dist/board/editor/clipboard.js +1 -1
- package/dist/board/editor/creation.js +1 -1
- package/dist/board/editor/document.d.ts +3 -1
- package/dist/board/editor/document.js +8 -6
- package/dist/board/editor/editor.d.ts +8 -3
- package/dist/board/editor/editor.js +8 -2
- package/dist/board/editor/index.d.ts +3 -3
- package/dist/board/editor/index.js +2 -2
- package/dist/board/editor/interaction.d.ts +23 -2
- package/dist/board/editor/interaction.js +93 -4
- package/dist/board/editor/query.d.ts +1 -1
- package/dist/board/editor/query.js +3 -7
- package/dist/board/editor/selection.js +1 -1
- package/dist/board/editor/snapshots.js +5 -57
- package/dist/board/editor/tool.d.ts +4 -1
- package/dist/board/editor/tool.js +6 -1
- package/dist/board/export/index.d.ts +2 -2
- package/dist/board/export/index.js +1 -1
- package/dist/board/export/node/fonts.js +4 -0
- package/dist/board/export/node/index.d.ts +2 -0
- package/dist/board/export/node/index.js +8 -1
- package/dist/board/export/node/scripts.js +1 -1
- package/dist/board/export/scene.d.ts +2 -2
- package/dist/board/export/scene.js +2 -2
- package/dist/board/model/asset-key.d.ts +24 -0
- package/dist/board/model/asset-key.js +104 -0
- package/dist/board/model/export-assets.d.ts +7 -3
- package/dist/board/model/export-assets.js +34 -13
- package/dist/board/model/index.d.ts +9 -7
- package/dist/board/model/index.js +22 -20
- package/dist/board/model/items/create.js +3 -3
- package/dist/board/model/items/definition.d.ts +9 -1
- package/dist/board/model/items/index.d.ts +2 -2
- package/dist/board/model/items/registry.d.ts +2 -1
- package/dist/board/model/items/registry.js +1 -0
- package/dist/board/model/media-sizes.d.ts +28 -0
- package/dist/board/model/media-sizes.js +64 -0
- package/dist/board/model/palette.d.ts +1 -0
- package/dist/board/model/palette.js +20 -130
- package/dist/board/model/sampling.js +1 -1
- package/dist/board/model/text-metrics.d.ts +28 -3
- package/dist/board/model/text-metrics.js +36 -2
- package/dist/board/model/themes.d.ts +495 -0
- package/dist/board/model/themes.js +500 -0
- package/dist/board/render/card-tag.js +2 -2
- package/dist/board/render/index.d.ts +11 -7
- package/dist/board/render/index.js +9 -5
- package/dist/board/render/labels.d.ts +15 -0
- package/dist/board/render/labels.js +14 -0
- package/dist/board/render/lod.d.ts +30 -0
- package/dist/board/render/lod.js +33 -0
- package/dist/board/render/media-interaction.d.ts +5 -2
- package/dist/board/render/media-interaction.js +15 -3
- package/dist/board/render/palette.js +2 -25
- package/dist/board/render/renderers/arrow-card-renderer.js +7 -5
- package/dist/board/render/renderers/audio-card-renderer.js +3 -2
- package/dist/board/render/renderers/board-renderer-registry.d.ts +22 -1
- package/dist/board/render/renderers/board-renderer-registry.js +9 -4
- package/dist/board/render/renderers/custom-card-renderer.js +5 -5
- package/dist/board/render/renderers/draw-card-renderer.js +1 -1
- package/dist/board/render/renderers/far-plate.d.ts +14 -1
- package/dist/board/render/renderers/far-plate.js +31 -8
- package/dist/board/render/renderers/file-card-renderer.js +49 -31
- package/dist/board/render/renderers/frame-card-renderer.js +9 -3
- package/dist/board/render/renderers/image-card-renderer.js +34 -8
- package/dist/board/render/renderers/model-card-renderer.js +6 -6
- package/dist/board/render/renderers/shape-card-renderer.js +25 -24
- package/dist/board/render/renderers/task-card-renderer.js +9 -10
- package/dist/board/render/renderers/text-card-renderer.js +86 -41
- package/dist/board/render/renderers/video-card-renderer.js +5 -4
- package/dist/board/render/script-export.js +1 -1
- package/dist/board/render/script-host.js +3 -3
- package/dist/board/render/style.d.ts +8 -2
- package/dist/board/render/style.js +8 -2
- package/dist/board/render/surface.d.ts +5 -0
- package/dist/board/render/surface.js +13 -3
- package/dist/board/render/text-edit.d.ts +32 -0
- package/dist/board/render/text-edit.js +134 -0
- package/dist/board/render/text-fit.d.ts +1 -3
- package/dist/board/render/text-fit.js +6 -49
- package/dist/board/render/text-measurement.d.ts +25 -1
- package/dist/board/render/text-measurement.js +157 -19
- package/dist/board/render/text-resolution.d.ts +1 -6
- package/dist/board/render/text-resolution.js +1 -10
- package/dist/board/render/theme.d.ts +15 -0
- package/dist/board/render/theme.js +15 -0
- package/dist/board/replica/index.d.ts +4 -2
- package/dist/board/replica/index.js +28 -17
- package/dist/board/replica/indexeddb.d.ts +16 -0
- package/dist/board/replica/indexeddb.js +155 -0
- package/dist/board/stage/assets.d.ts +28 -5
- package/dist/board/stage/assets.js +293 -102
- package/dist/board/stage/bench.d.ts +35 -0
- package/dist/board/stage/bench.js +153 -0
- package/dist/board/stage/device.d.ts +41 -0
- package/dist/board/stage/device.js +62 -0
- package/dist/board/stage/frame-budget.d.ts +19 -0
- package/dist/board/stage/frame-budget.js +69 -0
- package/dist/board/stage/idle.d.ts +4 -0
- package/dist/board/stage/idle.js +26 -0
- package/dist/board/stage/index.d.ts +11 -7
- package/dist/board/stage/index.js +7 -4
- package/dist/board/stage/lru.d.ts +13 -9
- package/dist/board/stage/lru.js +38 -20
- package/dist/board/stage/presses.d.ts +21 -0
- package/dist/board/stage/presses.js +66 -0
- package/dist/board/stage/scene.d.ts +15 -1
- package/dist/board/stage/scene.js +248 -141
- package/dist/board/stage/stage.d.ts +28 -2
- package/dist/board/stage/stage.js +404 -112
- package/dist/board/stage/texture-pool.d.ts +10 -1
- package/dist/board/stage/texture-pool.js +72 -2
- package/dist/chunks/environment.d.ts +6 -0
- package/dist/chunks/http.d.ts +9 -1
- package/dist/chunks/http.js +791 -4
- package/dist/index.d.ts +52 -1
- package/dist/index.js +202 -862
- package/dist/media.d.ts +3 -27
- package/dist/media.js +3 -82
- package/dist/protocol/dist/board-constants.js +3 -3
- package/dist/protocol/dist/board-layout.d.ts +17 -3
- package/dist/protocol/dist/board-layout.js +68 -11
- package/dist/protocol/dist/board-model.d.ts +15 -1
- package/dist/protocol/dist/board-model.js +12 -2
- package/dist/protocol/dist/board-patch.js +1 -1
- package/dist/protocol/dist/board-schema.d.ts +2 -1
- package/dist/protocol/dist/board-schema.js +4 -1
- package/dist/protocol/dist/core/media.d.ts +35 -1
- package/dist/protocol/dist/core/media.js +127 -1
- package/dist/protocol/dist/index.d.ts +3 -3
- package/package.json +4 -2
- package/dist/board/model/image-key.d.ts +0 -9
- package/dist/board/model/image-key.js +0 -33
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
//#region src/board/stage/bench.ts
|
|
2
|
+
const STEP = {
|
|
3
|
+
name: "step",
|
|
4
|
+
frames: 1,
|
|
5
|
+
rest: 3e3,
|
|
6
|
+
wheel: { deltaY: 960 }
|
|
7
|
+
};
|
|
8
|
+
const BOARD_BENCH_PHASES = [
|
|
9
|
+
{
|
|
10
|
+
name: "zoom in",
|
|
11
|
+
frames: 15,
|
|
12
|
+
wheel: {
|
|
13
|
+
deltaY: -10,
|
|
14
|
+
ctrlKey: true
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "close",
|
|
19
|
+
frames: 12,
|
|
20
|
+
wheel: {
|
|
21
|
+
deltaY: -10,
|
|
22
|
+
ctrlKey: true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
STEP,
|
|
26
|
+
STEP,
|
|
27
|
+
STEP,
|
|
28
|
+
{
|
|
29
|
+
name: "pan close",
|
|
30
|
+
frames: 60,
|
|
31
|
+
wheel: {
|
|
32
|
+
deltaX: -24,
|
|
33
|
+
deltaY: 16
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "zoom out",
|
|
38
|
+
frames: 12,
|
|
39
|
+
wheel: {
|
|
40
|
+
deltaY: 10,
|
|
41
|
+
ctrlKey: true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "pan",
|
|
46
|
+
frames: 120,
|
|
47
|
+
wheel: { deltaX: 24 }
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "overview",
|
|
51
|
+
frames: 16,
|
|
52
|
+
wheel: {
|
|
53
|
+
deltaY: 10,
|
|
54
|
+
ctrlKey: true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
const SETTLE_LIMIT_MS = 2e4;
|
|
59
|
+
const LONG_FRAME_MS = 50;
|
|
60
|
+
function percentiles(values) {
|
|
61
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
62
|
+
const at = (ratio) => sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * ratio))] ?? 0;
|
|
63
|
+
return {
|
|
64
|
+
p50: at(.5),
|
|
65
|
+
p95: at(.95),
|
|
66
|
+
max: sorted[sorted.length - 1] ?? 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const nextFrame = () => new Promise((resolve) => requestAnimationFrame(resolve));
|
|
70
|
+
async function settle(stage, since) {
|
|
71
|
+
const times = {};
|
|
72
|
+
for (;;) {
|
|
73
|
+
const now = await nextFrame();
|
|
74
|
+
const { waiting, settling, loading } = stage.stats();
|
|
75
|
+
const at = now - since;
|
|
76
|
+
if (times.shown === void 0 && waiting === 0) times.shown = at;
|
|
77
|
+
if (times.ready === void 0 && !settling) times.ready = at;
|
|
78
|
+
if (times.loaded === void 0 && !settling && loading === 0) times.loaded = at;
|
|
79
|
+
if (times.loaded !== void 0 || at >= SETTLE_LIMIT_MS) return {
|
|
80
|
+
shown: times.shown ?? SETTLE_LIMIT_MS,
|
|
81
|
+
ready: times.ready ?? SETTLE_LIMIT_MS,
|
|
82
|
+
loaded: times.loaded ?? SETTLE_LIMIT_MS
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function measureFrameWork() {
|
|
87
|
+
const original = globalThis.requestAnimationFrame;
|
|
88
|
+
let work = 0;
|
|
89
|
+
globalThis.requestAnimationFrame = (callback) => original((time) => {
|
|
90
|
+
const start = performance.now();
|
|
91
|
+
try {
|
|
92
|
+
callback(time);
|
|
93
|
+
} finally {
|
|
94
|
+
work += performance.now() - start;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
take: () => {
|
|
99
|
+
const taken = work;
|
|
100
|
+
work = 0;
|
|
101
|
+
return taken;
|
|
102
|
+
},
|
|
103
|
+
stop: () => {
|
|
104
|
+
globalThis.requestAnimationFrame = original;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function runBoardStageBench(stage, { since = performance.now(), phases = BOARD_BENCH_PHASES } = {}) {
|
|
109
|
+
await stage.ready;
|
|
110
|
+
const canvas = stage.renderer?.canvas;
|
|
111
|
+
if (!canvas) throw new Error("Board stage has no canvas.");
|
|
112
|
+
const open = await settle(stage, since);
|
|
113
|
+
const rect = canvas.getBoundingClientRect();
|
|
114
|
+
const work = measureFrameWork();
|
|
115
|
+
try {
|
|
116
|
+
const reports = [];
|
|
117
|
+
for (const phase of phases) {
|
|
118
|
+
if (phase.rest) await new Promise((resolve) => setTimeout(resolve, phase.rest));
|
|
119
|
+
const intervals = [];
|
|
120
|
+
const costs = [];
|
|
121
|
+
let previous = await nextFrame();
|
|
122
|
+
for (let frame = 0; frame < phase.frames; frame += 1) {
|
|
123
|
+
canvas.dispatchEvent(new WheelEvent("wheel", {
|
|
124
|
+
bubbles: true,
|
|
125
|
+
cancelable: true,
|
|
126
|
+
clientX: rect.left + rect.width / 2,
|
|
127
|
+
clientY: rect.top + rect.height / 2,
|
|
128
|
+
...phase.wheel
|
|
129
|
+
}));
|
|
130
|
+
work.take();
|
|
131
|
+
const now = await nextFrame();
|
|
132
|
+
costs.push(work.take());
|
|
133
|
+
intervals.push(now - previous);
|
|
134
|
+
previous = now;
|
|
135
|
+
}
|
|
136
|
+
reports.push({
|
|
137
|
+
name: phase.name,
|
|
138
|
+
frame: percentiles(intervals),
|
|
139
|
+
work: percentiles(costs),
|
|
140
|
+
long: intervals.filter((interval) => interval > LONG_FRAME_MS).length,
|
|
141
|
+
...await settle(stage, previous)
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
open,
|
|
146
|
+
phases: reports
|
|
147
|
+
};
|
|
148
|
+
} finally {
|
|
149
|
+
work.stop();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { BOARD_BENCH_PHASES, runBoardStageBench };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BoardLod } from "../render/lod.js";
|
|
2
|
+
import { LruBudget } from "./lru.js";
|
|
3
|
+
//#region src/board/stage/device.d.ts
|
|
4
|
+
type BoardDeviceTier = "low" | "standard" | "high";
|
|
5
|
+
type BoardDeviceSignals = {
|
|
6
|
+
phone?: boolean;
|
|
7
|
+
memory?: number;
|
|
8
|
+
cores?: number;
|
|
9
|
+
saveData?: boolean;
|
|
10
|
+
slowNetwork?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type BoardDeviceBudget = {
|
|
13
|
+
tier: BoardDeviceTier;
|
|
14
|
+
lod: BoardLod;
|
|
15
|
+
lru: LruBudget;
|
|
16
|
+
concurrency: number;
|
|
17
|
+
prefetch: number;
|
|
18
|
+
};
|
|
19
|
+
declare const BOARD_DEVICE_TIERS: readonly BoardDeviceTier[];
|
|
20
|
+
type BoardDeviceEnvironment = {
|
|
21
|
+
navigator?: Navigator & {
|
|
22
|
+
deviceMemory?: number;
|
|
23
|
+
connection?: {
|
|
24
|
+
saveData?: boolean;
|
|
25
|
+
effectiveType?: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
matchMedia?: (query: string) => {
|
|
29
|
+
matches: boolean;
|
|
30
|
+
};
|
|
31
|
+
screen?: {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
declare function readBoardDeviceSignals(environment?: BoardDeviceEnvironment): BoardDeviceSignals;
|
|
37
|
+
declare function boardDeviceTier({ phone, memory, cores }: BoardDeviceSignals): BoardDeviceTier;
|
|
38
|
+
declare function boardDeviceBudget(signals: BoardDeviceSignals, tier?: BoardDeviceTier): BoardDeviceBudget;
|
|
39
|
+
declare function defaultBoardDeviceBudget(): BoardDeviceBudget;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { BOARD_DEVICE_TIERS, BoardDeviceBudget, BoardDeviceEnvironment, BoardDeviceSignals, BoardDeviceTier, boardDeviceBudget, boardDeviceTier, defaultBoardDeviceBudget, readBoardDeviceSignals };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { COMPACT_BOARD_LOD, DEFAULT_BOARD_LOD } from "../render/lod.js";
|
|
2
|
+
import { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET } from "./lru.js";
|
|
3
|
+
//#region src/board/stage/device.ts
|
|
4
|
+
const BUDGETS = {
|
|
5
|
+
low: {
|
|
6
|
+
lod: COMPACT_BOARD_LOD,
|
|
7
|
+
lru: COMPACT_LRU_BUDGET,
|
|
8
|
+
concurrency: 4,
|
|
9
|
+
prefetch: 0
|
|
10
|
+
},
|
|
11
|
+
standard: {
|
|
12
|
+
lod: DEFAULT_BOARD_LOD,
|
|
13
|
+
lru: DEFAULT_LRU_BUDGET,
|
|
14
|
+
concurrency: 6,
|
|
15
|
+
prefetch: .5
|
|
16
|
+
},
|
|
17
|
+
high: {
|
|
18
|
+
lod: DEFAULT_BOARD_LOD,
|
|
19
|
+
lru: {
|
|
20
|
+
maxCount: 4096,
|
|
21
|
+
maxBytes: 402653184
|
|
22
|
+
},
|
|
23
|
+
concurrency: 8,
|
|
24
|
+
prefetch: 1
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const BOARD_DEVICE_TIERS = Object.keys(BUDGETS);
|
|
28
|
+
const PHONE_SHORT_SIDE = 600;
|
|
29
|
+
function readBoardDeviceSignals(environment = globalThis) {
|
|
30
|
+
const { navigator: device, matchMedia, screen } = environment;
|
|
31
|
+
if (!device || !matchMedia || !screen) return {};
|
|
32
|
+
const connection = device.connection;
|
|
33
|
+
return {
|
|
34
|
+
phone: matchMedia("(pointer: coarse)").matches && !matchMedia("(any-pointer: fine)").matches && Math.min(screen.width, screen.height) < PHONE_SHORT_SIDE,
|
|
35
|
+
memory: device.deviceMemory,
|
|
36
|
+
cores: device.hardwareConcurrency,
|
|
37
|
+
saveData: Boolean(connection?.saveData),
|
|
38
|
+
slowNetwork: /^(slow-2g|2g|3g)$/.test(connection?.effectiveType ?? "")
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function boardDeviceTier({ phone, memory, cores }) {
|
|
42
|
+
if (memory !== void 0 && memory <= 2 || cores !== void 0 && cores <= 2) return "low";
|
|
43
|
+
if (phone) return memory !== void 0 && memory >= 8 ? "standard" : "low";
|
|
44
|
+
if (memory !== void 0 && memory <= 4 && cores !== void 0 && cores <= 4) return "low";
|
|
45
|
+
if (memory !== void 0 && memory >= 8 && cores !== void 0 && cores >= 8) return "high";
|
|
46
|
+
return "standard";
|
|
47
|
+
}
|
|
48
|
+
function boardDeviceBudget(signals, tier = boardDeviceTier(signals)) {
|
|
49
|
+
const budget = BUDGETS[tier];
|
|
50
|
+
return {
|
|
51
|
+
tier,
|
|
52
|
+
...budget,
|
|
53
|
+
prefetch: signals.saveData || signals.slowNetwork ? 0 : budget.prefetch
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let detected;
|
|
57
|
+
function defaultBoardDeviceBudget() {
|
|
58
|
+
detected ??= boardDeviceBudget(readBoardDeviceSignals());
|
|
59
|
+
return detected;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { BOARD_DEVICE_TIERS, boardDeviceBudget, boardDeviceTier, defaultBoardDeviceBudget, readBoardDeviceSignals };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/board/stage/frame-budget.d.ts
|
|
2
|
+
type BoardFrameWork = "materialize" | "plate" | "resolution";
|
|
3
|
+
type BoardFrameBudget = {
|
|
4
|
+
allow(kind: BoardFrameWork): number;
|
|
5
|
+
spend(kind: BoardFrameWork, units: number, ms: number): void;
|
|
6
|
+
};
|
|
7
|
+
type BoardFrameClock = BoardFrameBudget & {
|
|
8
|
+
begin(frameTime: number, options: {
|
|
9
|
+
moving: boolean;
|
|
10
|
+
chained: boolean;
|
|
11
|
+
}): void;
|
|
12
|
+
readonly interval: number;
|
|
13
|
+
};
|
|
14
|
+
declare function fixedBoardFrameBudget(): BoardFrameBudget & {
|
|
15
|
+
reset(): void;
|
|
16
|
+
};
|
|
17
|
+
declare function createBoardFrameClock(now?: () => number): BoardFrameClock;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { BoardFrameBudget, BoardFrameClock, BoardFrameWork, createBoardFrameClock, fixedBoardFrameBudget };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/board/stage/frame-budget.ts
|
|
2
|
+
const INITIAL = {
|
|
3
|
+
materialize: 48,
|
|
4
|
+
plate: 2400,
|
|
5
|
+
resolution: 32
|
|
6
|
+
};
|
|
7
|
+
const KINDS = Object.keys(INITIAL);
|
|
8
|
+
const MIN_SHARE = 1 / 8;
|
|
9
|
+
const LATE_FRAMES = 1.5;
|
|
10
|
+
const MOVING_SHARE = .25;
|
|
11
|
+
const RESTING_SHARE = .6;
|
|
12
|
+
const COST_SMOOTHING = .3;
|
|
13
|
+
const DEFAULT_INTERVAL = 1e3 / 60;
|
|
14
|
+
const MIN_INTERVAL = 4;
|
|
15
|
+
const INTERVAL_DRIFT = 1.01;
|
|
16
|
+
const perKind = (value) => ({
|
|
17
|
+
materialize: value,
|
|
18
|
+
plate: value,
|
|
19
|
+
resolution: value
|
|
20
|
+
});
|
|
21
|
+
function fixedBoardFrameBudget() {
|
|
22
|
+
let done = perKind(0);
|
|
23
|
+
return {
|
|
24
|
+
allow: (kind) => Math.max(0, INITIAL[kind] - done[kind]),
|
|
25
|
+
spend(kind, units) {
|
|
26
|
+
done[kind] += units;
|
|
27
|
+
},
|
|
28
|
+
reset() {
|
|
29
|
+
done = perKind(0);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createBoardFrameClock(now = () => performance.now()) {
|
|
34
|
+
let interval = DEFAULT_INTERVAL;
|
|
35
|
+
let previous = NaN;
|
|
36
|
+
let deadline = Number.POSITIVE_INFINITY;
|
|
37
|
+
let late = false;
|
|
38
|
+
const cost = perKind(0);
|
|
39
|
+
for (const kind of KINDS) cost[kind] = DEFAULT_INTERVAL * MOVING_SHARE / INITIAL[kind];
|
|
40
|
+
const last = perKind(0);
|
|
41
|
+
let done = perKind(0);
|
|
42
|
+
return {
|
|
43
|
+
get interval() {
|
|
44
|
+
return interval;
|
|
45
|
+
},
|
|
46
|
+
begin(frameTime, { moving, chained }) {
|
|
47
|
+
const delta = frameTime - previous;
|
|
48
|
+
previous = frameTime;
|
|
49
|
+
late = moving && chained && delta > interval * LATE_FRAMES;
|
|
50
|
+
if (chained && delta >= MIN_INTERVAL) interval = Math.min(interval * INTERVAL_DRIFT, delta);
|
|
51
|
+
deadline = now() + interval * (moving ? MOVING_SHARE : RESTING_SHARE);
|
|
52
|
+
for (const kind of KINDS) if (done[kind] > 0) last[kind] = done[kind];
|
|
53
|
+
done = perKind(0);
|
|
54
|
+
},
|
|
55
|
+
allow(kind) {
|
|
56
|
+
const floor = Math.ceil(INITIAL[kind] * MIN_SHARE);
|
|
57
|
+
const fit = Math.floor((deadline - now()) / cost[kind]);
|
|
58
|
+
const ceiling = late ? Math.floor(last[kind] / 2) : Math.max(INITIAL[kind], last[kind] * 2);
|
|
59
|
+
return Math.max(done[kind] === 0 ? floor : 0, Math.min(fit, ceiling - done[kind]));
|
|
60
|
+
},
|
|
61
|
+
spend(kind, units, ms) {
|
|
62
|
+
if (units <= 0) return;
|
|
63
|
+
done[kind] += units;
|
|
64
|
+
cost[kind] += (Math.max(ms, .001) / units - cost[kind]) * COST_SMOOTHING;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { createBoardFrameClock, fixedBoardFrameBudget };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/board/stage/idle.ts
|
|
2
|
+
function scheduleBoardIdle(task, delay) {
|
|
3
|
+
const scheduler = globalThis.scheduler;
|
|
4
|
+
if (scheduler?.postTask) {
|
|
5
|
+
const controller = new AbortController();
|
|
6
|
+
scheduler.postTask(task, {
|
|
7
|
+
priority: "background",
|
|
8
|
+
delay,
|
|
9
|
+
signal: controller.signal
|
|
10
|
+
}).catch((error) => {
|
|
11
|
+
if (!controller.signal.aborted) throw error;
|
|
12
|
+
});
|
|
13
|
+
return () => controller.abort();
|
|
14
|
+
}
|
|
15
|
+
let idle = 0;
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
if (typeof requestIdleCallback === "function") idle = requestIdleCallback(task, { timeout: 1e3 });
|
|
18
|
+
else task();
|
|
19
|
+
}, delay);
|
|
20
|
+
return () => {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
if (idle) cancelIdleCallback(idle);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { scheduleBoardIdle };
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, selectLruEvictions } from "./lru.js";
|
|
2
|
-
import { BoardImageTexturePool, boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, unloadBoardImageTexture } from "./texture-pool.js";
|
|
3
|
-
import { BoardAssetManager, BoardAssetManagerOptions, BoardModelPreviewRequest, BoardModelPreviews,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, LruTrim, planLruTrim, selectLruEvictions } from "./lru.js";
|
|
2
|
+
import { BoardImageTexturePool, boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, loadFittedImageTexture, readTextureTone, shrinkBoardTexture, unloadBoardImageTexture } from "./texture-pool.js";
|
|
3
|
+
import { BoardAssetChange, BoardAssetManager, BoardAssetManagerOptions, BoardAssetPrefetch, BoardModelPreviewRequest, BoardModelPreviews, createBoardAssetManager } from "./assets.js";
|
|
4
|
+
import { BOARD_LONG_PRESS_MS, BoardStagePress } from "./presses.js";
|
|
5
|
+
import { BoardFrameBudget, BoardFrameWork } from "./frame-budget.js";
|
|
6
6
|
import { BoardLiveStroke, BoardScene, BoardSceneNode, SceneSyncInput, createBoardScene } from "./scene.js";
|
|
7
7
|
import { BoardItemView, BoardItemViewContext, BoardItemViewInstance, BoardItemViewLayer, createBoardItemViewLayer } from "./views.js";
|
|
8
|
-
import { BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageTheme, mountBoardStage } from "./stage.js";
|
|
8
|
+
import { BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageStats, BoardStageTheme, mountBoardStage } from "./stage.js";
|
|
9
|
+
import { BOARD_BENCH_PHASES, BoardBenchPercentiles, BoardBenchPhase, BoardBenchPhaseReport, BoardBenchReport, BoardBenchSettle, runBoardStageBench } from "./bench.js";
|
|
10
|
+
import { BOARD_DEVICE_TIERS, BoardDeviceBudget, BoardDeviceEnvironment, BoardDeviceSignals, BoardDeviceTier, boardDeviceBudget, boardDeviceTier, defaultBoardDeviceBudget, readBoardDeviceSignals } from "./device.js";
|
|
11
|
+
import { toBoardPointerEvent } from "./pointer.js";
|
|
12
|
+
import { createBoardPointerGestures } from "./pointer-gestures.js";
|
|
9
13
|
import { BoardOverlayInput, drawBoardOverlay, expandFrame } from "./overlay.js";
|
|
10
|
-
export { BoardAssetManager, BoardAssetManagerOptions, BoardImageTexturePool, BoardItemView, BoardItemViewContext, BoardItemViewInstance, BoardItemViewLayer, BoardLiveStroke, BoardModelPreviewRequest, BoardModelPreviews, BoardOverlayInput, BoardScene, BoardSceneNode, BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageTheme, COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, SceneSyncInput,
|
|
14
|
+
export { BOARD_BENCH_PHASES, BOARD_DEVICE_TIERS, BOARD_LONG_PRESS_MS, BoardAssetChange, BoardAssetManager, BoardAssetManagerOptions, BoardAssetPrefetch, BoardBenchPercentiles, BoardBenchPhase, BoardBenchPhaseReport, BoardBenchReport, BoardBenchSettle, BoardDeviceBudget, BoardDeviceEnvironment, BoardDeviceSignals, BoardDeviceTier, type BoardFrameBudget, type BoardFrameWork, BoardImageTexturePool, BoardItemView, BoardItemViewContext, BoardItemViewInstance, BoardItemViewLayer, BoardLiveStroke, BoardModelPreviewRequest, BoardModelPreviews, BoardOverlayInput, BoardScene, BoardSceneNode, BoardStage, BoardStageOptions, BoardStagePointerPhase, type BoardStagePress, BoardStageStats, BoardStageTheme, COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, LruTrim, SceneSyncInput, boardDeviceBudget, boardDeviceTier, boardImageTexturePool, createBoardAssetManager, createBoardImageTexturePool, createBoardItemViewLayer, createBoardPointerGestures, createBoardScene, defaultBoardDeviceBudget, drawBoardOverlay, expandFrame, loadBoardImageTexture, loadFittedImageTexture, mountBoardStage, planLruTrim, readBoardDeviceSignals, readTextureTone, runBoardStageBench, selectLruEvictions, shrinkBoardTexture, toBoardPointerEvent, unloadBoardImageTexture };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, selectLruEvictions } from "./lru.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, planLruTrim, selectLruEvictions } from "./lru.js";
|
|
2
|
+
import { BOARD_DEVICE_TIERS, boardDeviceBudget, boardDeviceTier, defaultBoardDeviceBudget, readBoardDeviceSignals } from "./device.js";
|
|
3
|
+
import { boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, loadFittedImageTexture, readTextureTone, shrinkBoardTexture, unloadBoardImageTexture } from "./texture-pool.js";
|
|
4
|
+
import { createBoardAssetManager } from "./assets.js";
|
|
5
|
+
import { BOARD_BENCH_PHASES, runBoardStageBench } from "./bench.js";
|
|
4
6
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
5
7
|
import { createBoardPointerGestures } from "./pointer-gestures.js";
|
|
6
8
|
import { createBoardScene } from "./scene.js";
|
|
9
|
+
import { BOARD_LONG_PRESS_MS } from "./presses.js";
|
|
7
10
|
import { drawBoardOverlay, expandFrame } from "./overlay.js";
|
|
8
11
|
import { createBoardItemViewLayer } from "./views.js";
|
|
9
12
|
import { mountBoardStage } from "./stage.js";
|
|
10
|
-
export { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET,
|
|
13
|
+
export { BOARD_BENCH_PHASES, BOARD_DEVICE_TIERS, BOARD_LONG_PRESS_MS, COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, boardDeviceBudget, boardDeviceTier, boardImageTexturePool, createBoardAssetManager, createBoardImageTexturePool, createBoardItemViewLayer, createBoardPointerGestures, createBoardScene, defaultBoardDeviceBudget, drawBoardOverlay, expandFrame, loadBoardImageTexture, loadFittedImageTexture, mountBoardStage, planLruTrim, readBoardDeviceSignals, readTextureTone, runBoardStageBench, selectLruEvictions, shrinkBoardTexture, toBoardPointerEvent, unloadBoardImageTexture };
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* they enter a "cooling" pool (reference count zero but still on the GPU) so a
|
|
7
7
|
* quick pan back is instant instead of re-fetching. The pool is bounded by
|
|
8
8
|
* both entry count and approximate GPU bytes; when a budget is exceeded the least
|
|
9
|
-
* recently used entries
|
|
10
|
-
* release-vs-churn trade-off trivial to test.
|
|
9
|
+
* recently used entries shrink or leave first. Keeping this decision pure makes
|
|
10
|
+
* the release-vs-churn trade-off trivial to test.
|
|
11
11
|
*/
|
|
12
12
|
type LruEntry = {
|
|
13
13
|
key: string;
|
|
@@ -15,19 +15,23 @@ type LruEntry = {
|
|
|
15
15
|
lastUsedAt: number;
|
|
16
16
|
/** Approximate GPU footprint in bytes. */
|
|
17
17
|
bytes: number;
|
|
18
|
+
shrunkBytes?: number;
|
|
18
19
|
};
|
|
19
20
|
type LruBudget = {
|
|
20
21
|
maxCount: number;
|
|
21
22
|
maxBytes: number;
|
|
22
23
|
};
|
|
23
|
-
/** Default cooling-pool ceiling: 512 images or ~192 MB of GPU memory. */
|
|
24
|
-
declare const DEFAULT_LRU_BUDGET: LruBudget;
|
|
25
|
-
declare const COMPACT_LRU_BUDGET: LruBudget;
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* an empty array when the pool already fits.
|
|
25
|
+
* Default cooling-pool ceiling: 2048 images or ~192 MB of GPU memory. Bytes
|
|
26
|
+
* bind first for full-size textures; shrunk copies let many more stay warm.
|
|
30
27
|
*/
|
|
28
|
+
declare const DEFAULT_LRU_BUDGET: LruBudget;
|
|
29
|
+
declare const COMPACT_LRU_BUDGET: LruBudget;
|
|
30
|
+
type LruTrim = {
|
|
31
|
+
shrink: string[];
|
|
32
|
+
evict: string[];
|
|
33
|
+
};
|
|
34
|
+
declare function planLruTrim(entries: LruEntry[], budget: LruBudget): LruTrim;
|
|
31
35
|
declare function selectLruEvictions(entries: LruEntry[], budget: LruBudget): string[];
|
|
32
36
|
//#endregion
|
|
33
|
-
export { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, selectLruEvictions };
|
|
37
|
+
export { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, LruTrim, planLruTrim, selectLruEvictions };
|
package/dist/board/stage/lru.js
CHANGED
|
@@ -1,33 +1,51 @@
|
|
|
1
1
|
//#region src/board/stage/lru.ts
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Default cooling-pool ceiling: 2048 images or ~192 MB of GPU memory. Bytes
|
|
4
|
+
* bind first for full-size textures; shrunk copies let many more stay warm.
|
|
5
|
+
*/
|
|
3
6
|
const DEFAULT_LRU_BUDGET = {
|
|
4
|
-
maxCount:
|
|
7
|
+
maxCount: 2048,
|
|
5
8
|
maxBytes: 201326592
|
|
6
9
|
};
|
|
7
10
|
const COMPACT_LRU_BUDGET = {
|
|
8
|
-
maxCount:
|
|
11
|
+
maxCount: 1024,
|
|
9
12
|
maxBytes: 100663296
|
|
10
13
|
};
|
|
11
|
-
|
|
12
|
-
* Given the unreferenced (cooling) entries and a budget, return the keys to
|
|
13
|
-
* evict — oldest first — so the remaining pool fits within both limits. Returns
|
|
14
|
-
* an empty array when the pool already fits.
|
|
15
|
-
*/
|
|
16
|
-
function selectLruEvictions(entries, budget) {
|
|
17
|
-
if (entries.length === 0) return [];
|
|
18
|
-
let count = entries.length;
|
|
14
|
+
function planLruTrim(entries, budget) {
|
|
19
15
|
let bytes = 0;
|
|
20
16
|
for (const entry of entries) bytes += entry.bytes;
|
|
21
|
-
|
|
17
|
+
const over = entries.length - budget.maxCount;
|
|
18
|
+
if (over <= 0 && bytes <= budget.maxBytes) return {
|
|
19
|
+
shrink: [],
|
|
20
|
+
evict: []
|
|
21
|
+
};
|
|
22
22
|
const sorted = [...entries].sort((a, b) => a.lastUsedAt - b.lastUsedAt);
|
|
23
|
-
const evict =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
bytes
|
|
23
|
+
const evict = sorted.slice(0, Math.max(0, over));
|
|
24
|
+
const kept = sorted.slice(evict.length);
|
|
25
|
+
for (const entry of evict) bytes -= entry.bytes;
|
|
26
|
+
const shrink = /* @__PURE__ */ new Set();
|
|
27
|
+
for (const entry of kept) {
|
|
28
|
+
if (bytes <= budget.maxBytes) break;
|
|
29
|
+
if (entry.shrunkBytes === void 0 || entry.shrunkBytes >= entry.bytes) continue;
|
|
30
|
+
shrink.add(entry);
|
|
31
|
+
bytes -= entry.bytes - entry.shrunkBytes;
|
|
32
|
+
}
|
|
33
|
+
for (const entry of kept) {
|
|
34
|
+
if (bytes <= budget.maxBytes) break;
|
|
35
|
+
bytes -= shrink.delete(entry) ? entry.shrunkBytes : entry.bytes;
|
|
36
|
+
evict.push(entry);
|
|
29
37
|
}
|
|
30
|
-
return
|
|
38
|
+
return {
|
|
39
|
+
shrink: [...shrink].map(({ key }) => key),
|
|
40
|
+
evict: evict.map(({ key }) => key)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function selectLruEvictions(entries, budget) {
|
|
44
|
+
return planLruTrim(entries.map(({ key, lastUsedAt, bytes }) => ({
|
|
45
|
+
key,
|
|
46
|
+
lastUsedAt,
|
|
47
|
+
bytes
|
|
48
|
+
})), budget).evict;
|
|
31
49
|
}
|
|
32
50
|
//#endregion
|
|
33
|
-
export { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, selectLruEvictions };
|
|
51
|
+
export { COMPACT_LRU_BUDGET, DEFAULT_LRU_BUDGET, planLruTrim, selectLruEvictions };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/board/stage/presses.d.ts
|
|
2
|
+
declare const BOARD_LONG_PRESS_MS = 450;
|
|
3
|
+
type BoardStagePress = {
|
|
4
|
+
pointerId: number;
|
|
5
|
+
pointerType: string;
|
|
6
|
+
clientX: number;
|
|
7
|
+
clientY: number;
|
|
8
|
+
};
|
|
9
|
+
declare function boardStagePress(event: PointerEvent): BoardStagePress;
|
|
10
|
+
declare function createBoardLongPress(fire: (press: BoardStagePress) => void): {
|
|
11
|
+
start(event: PointerEvent): void;
|
|
12
|
+
move(event: PointerEvent): void;
|
|
13
|
+
end(event: PointerEvent): void;
|
|
14
|
+
cancel: () => void;
|
|
15
|
+
};
|
|
16
|
+
declare function createGhostClickGuard(view: Window | null): {
|
|
17
|
+
arm(): void;
|
|
18
|
+
dispose: () => void | undefined;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { BOARD_LONG_PRESS_MS, BoardStagePress, boardStagePress, createBoardLongPress, createGhostClickGuard };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import "../editor/tool.js";
|
|
2
|
+
//#region src/board/stage/presses.ts
|
|
3
|
+
const BOARD_LONG_PRESS_MS = 450;
|
|
4
|
+
const GHOST_CLICK_MS = 700;
|
|
5
|
+
function boardStagePress(event) {
|
|
6
|
+
return {
|
|
7
|
+
pointerId: event.pointerId,
|
|
8
|
+
pointerType: event.pointerType,
|
|
9
|
+
clientX: event.clientX,
|
|
10
|
+
clientY: event.clientY
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function createBoardLongPress(fire) {
|
|
14
|
+
let pending = null;
|
|
15
|
+
const cancel = () => {
|
|
16
|
+
if (pending) clearTimeout(pending.timer);
|
|
17
|
+
pending = null;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
start(event) {
|
|
21
|
+
if (pending) return cancel();
|
|
22
|
+
if (event.pointerType === "mouse" || event.button !== 0) return;
|
|
23
|
+
const press = boardStagePress(event);
|
|
24
|
+
pending = {
|
|
25
|
+
press,
|
|
26
|
+
timer: setTimeout(() => {
|
|
27
|
+
pending = null;
|
|
28
|
+
fire(press);
|
|
29
|
+
}, 450)
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
move(event) {
|
|
33
|
+
if (!pending || event.pointerId !== pending.press.pointerId) return;
|
|
34
|
+
const { clientX, clientY } = pending.press;
|
|
35
|
+
if (Math.hypot(event.clientX - clientX, event.clientY - clientY) > 8) cancel();
|
|
36
|
+
},
|
|
37
|
+
end(event) {
|
|
38
|
+
if (pending?.press.pointerId === event.pointerId) cancel();
|
|
39
|
+
},
|
|
40
|
+
cancel
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function createGhostClickGuard(view) {
|
|
44
|
+
let disarm = null;
|
|
45
|
+
return {
|
|
46
|
+
arm() {
|
|
47
|
+
disarm?.();
|
|
48
|
+
if (!view) return;
|
|
49
|
+
const drop = (event) => {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.stopImmediatePropagation();
|
|
52
|
+
disarm?.();
|
|
53
|
+
};
|
|
54
|
+
const timer = setTimeout(() => disarm?.(), GHOST_CLICK_MS);
|
|
55
|
+
view.addEventListener("click", drop, true);
|
|
56
|
+
disarm = () => {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
view.removeEventListener("click", drop, true);
|
|
59
|
+
disarm = null;
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
dispose: () => disarm?.()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { BOARD_LONG_PRESS_MS, boardStagePress, createBoardLongPress, createGhostClickGuard };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { BoardDrawPoint } from "../../protocol/dist/board-model.js";
|
|
2
|
+
import { Rect } from "../model/geometry.js";
|
|
2
3
|
import { BoardScene as BoardScene$1, BoardSceneItem } from "../model/scene.js";
|
|
3
4
|
import "../model/index.js";
|
|
5
|
+
import { BoardLod } from "../render/lod.js";
|
|
4
6
|
import { BoardCardRendererResolver, BoardRenderContext } from "../render/renderers/board-renderer-registry.js";
|
|
5
7
|
import "../render/index.js";
|
|
8
|
+
import { BoardFrameBudget } from "./frame-budget.js";
|
|
6
9
|
import { Container, Graphics } from "pixi.js";
|
|
7
10
|
//#region src/board/stage/scene.d.ts
|
|
8
11
|
type BoardLiveStroke = {
|
|
@@ -22,12 +25,15 @@ type SceneSyncInput = {
|
|
|
22
25
|
globalSig: string;
|
|
23
26
|
staleIds?: ReadonlySet<string>;
|
|
24
27
|
structureVersion: number;
|
|
25
|
-
geometryVersion: number;
|
|
26
28
|
gestureActive: boolean;
|
|
27
29
|
focus?: {
|
|
28
30
|
x: number;
|
|
29
31
|
y: number;
|
|
30
32
|
};
|
|
33
|
+
/** The world area on screen; live cards outside it skip rendering. */
|
|
34
|
+
view?: Rect;
|
|
35
|
+
lod?: BoardLod;
|
|
36
|
+
budget?: BoardFrameBudget;
|
|
31
37
|
};
|
|
32
38
|
type BoardSceneNode = {
|
|
33
39
|
item: BoardSceneItem;
|
|
@@ -37,10 +43,18 @@ type BoardScene = {
|
|
|
37
43
|
sync: (input: SceneSyncInput) => void;
|
|
38
44
|
getNode: (itemId: string) => BoardSceneNode | null;
|
|
39
45
|
holders: (key: string) => ReadonlySet<string>;
|
|
46
|
+
repaintFar: (keys: ReadonlySet<string>, assetKey: (item: BoardSceneItem) => string | null) => void;
|
|
40
47
|
readonly liveVersion: number;
|
|
41
48
|
liveIds: () => ReadonlySet<string>;
|
|
49
|
+
/** Live cards and cards drawn on far plates. */
|
|
50
|
+
counts: () => {
|
|
51
|
+
live: number;
|
|
52
|
+
far: number;
|
|
53
|
+
};
|
|
54
|
+
readonly settling: boolean;
|
|
42
55
|
readonly animated: boolean;
|
|
43
56
|
tick: (clock: number) => number | null;
|
|
57
|
+
redrawText: () => void;
|
|
44
58
|
destroy: (context: BoardRenderContext) => void;
|
|
45
59
|
};
|
|
46
60
|
declare function createBoardScene(options: {
|