@neta-art/cohub 12.2.0 → 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 +77 -3
- 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 +7 -3
- package/dist/board/editor/editor.js +4 -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/tool.d.ts +4 -1
- package/dist/board/editor/tool.js +6 -1
- package/dist/board/export/index.js +1 -1
- package/dist/board/export/node/scripts.js +1 -1
- package/dist/board/export/scene.js +1 -1
- package/dist/board/model/asset-key.d.ts +9 -1
- package/dist/board/model/asset-key.js +15 -3
- package/dist/board/model/index.d.ts +5 -5
- package/dist/board/model/index.js +18 -18
- package/dist/board/model/items/create.js +2 -2
- 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/sampling.js +1 -1
- package/dist/board/model/text-metrics.d.ts +21 -1
- package/dist/board/model/text-metrics.js +24 -1
- package/dist/board/model/themes.d.ts +15 -8
- package/dist/board/model/themes.js +15 -10
- package/dist/board/render/index.d.ts +8 -6
- package/dist/board/render/index.js +7 -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 +9 -1
- package/dist/board/render/lod.js +7 -1
- package/dist/board/render/media-interaction.d.ts +5 -2
- package/dist/board/render/media-interaction.js +15 -3
- package/dist/board/render/renderers/arrow-card-renderer.js +7 -5
- package/dist/board/render/renderers/audio-card-renderer.js +1 -1
- package/dist/board/render/renderers/board-renderer-registry.d.ts +12 -1
- package/dist/board/render/renderers/board-renderer-registry.js +9 -4
- package/dist/board/render/renderers/draw-card-renderer.js +1 -1
- package/dist/board/render/renderers/far-plate.d.ts +4 -1
- package/dist/board/render/renderers/far-plate.js +5 -1
- package/dist/board/render/renderers/file-card-renderer.js +46 -26
- package/dist/board/render/renderers/frame-card-renderer.js +9 -3
- package/dist/board/render/renderers/image-card-renderer.js +2 -2
- package/dist/board/render/renderers/model-card-renderer.js +5 -5
- package/dist/board/render/renderers/shape-card-renderer.js +1 -1
- package/dist/board/render/renderers/task-card-renderer.js +2 -2
- package/dist/board/render/renderers/video-card-renderer.js +3 -3
- 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 +1 -0
- package/dist/board/render/style.js +3 -1
- 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 +4 -43
- package/dist/board/render/text-measurement.d.ts +12 -2
- package/dist/board/render/text-measurement.js +55 -10
- 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 +16 -4
- package/dist/board/stage/assets.js +155 -30
- 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 +9 -5
- package/dist/board/stage/index.js +5 -2
- package/dist/board/stage/presses.d.ts +21 -0
- package/dist/board/stage/presses.js +66 -0
- package/dist/board/stage/scene.d.ts +4 -0
- package/dist/board/stage/scene.js +45 -13
- package/dist/board/stage/stage.d.ts +17 -2
- package/dist/board/stage/stage.js +314 -95
- package/dist/board/stage/texture-pool.d.ts +6 -4
- package/dist/board/stage/texture-pool.js +42 -8
- 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 +65 -733
- 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.js +4 -0
- package/dist/protocol/dist/index.d.ts +2 -2
- package/package.json +2 -1
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
|
|
2
2
|
import { applyMatrix, invertMatrix } from "../../protocol/dist/board-layout.js";
|
|
3
|
-
import { pointToWorld, screenPoint, stableCullRect, visibleWorldRect, worldPoint } from "../model/geometry.js";
|
|
4
|
-
import { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, validateBoardScriptCommit } from "../model/script-scene.js";
|
|
5
|
-
import { boardScriptTarget } from "../model/script-hit.js";
|
|
3
|
+
import { itemBounds, pointToWorld, rectsIntersect, screenPoint, stableCullRect, visibleWorldRect, worldPoint } from "../model/geometry.js";
|
|
6
4
|
import { createBoardAnimationSampler, resolveBoardAnimationCamera } from "../model/sampling.js";
|
|
7
|
-
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
8
|
-
import { boardTextureEdge } from "../model/asset-key.js";
|
|
9
5
|
import { pickBoardColor, resolveItemColor } from "../model/palette.js";
|
|
10
|
-
import { elementClientToLocal } from "../model/client-geometry.js";
|
|
11
|
-
import { isCreationBoardTool } from "../editor/tool.js";
|
|
12
|
-
import { resizeCursorForHandle } from "../editor/selection-transform.js";
|
|
13
6
|
import { parseBoardCssColor } from "../render/css-color.js";
|
|
14
7
|
import { itemAlpha } from "../render/palette.js";
|
|
8
|
+
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
15
9
|
import { onBoardTextFontsChange } from "../render/text-measurement.js";
|
|
10
|
+
import { boardLodRatio, boardLodSize } from "../render/lod.js";
|
|
16
11
|
import { createBoardBackground, updateBoardBackground } from "../render/board-background.js";
|
|
17
12
|
import { getBoardResolution } from "../render/text-resolution.js";
|
|
18
13
|
import { DEFAULT_BOARD_RENDER_LABELS, getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
14
|
+
import { boardScriptScope, composeBoardScriptItems, prepareBoardScriptOutput, validateBoardScriptCommit } from "../model/script-scene.js";
|
|
15
|
+
import { boardScriptTarget } from "../model/script-hit.js";
|
|
19
16
|
import { createBoardScriptHost } from "../render/script-host.js";
|
|
20
17
|
import { defaultBoardRenderStyle } from "../render/style.js";
|
|
21
18
|
import { boardRenderTheme } from "../render/theme.js";
|
|
22
|
-
import {
|
|
19
|
+
import { boardTextureEdge } from "../model/asset-key.js";
|
|
20
|
+
import { elementClientToLocal } from "../model/client-geometry.js";
|
|
21
|
+
import { isCreationBoardTool } from "../editor/tool.js";
|
|
22
|
+
import { resizeCursorForHandle } from "../editor/selection-transform.js";
|
|
23
23
|
import { createBoardPlayer } from "../player/index.js";
|
|
24
|
+
import { createBoardScriptObservers } from "../model/script-observers.js";
|
|
25
|
+
import { defaultBoardDeviceBudget } from "./device.js";
|
|
26
|
+
import { scheduleBoardIdle } from "./idle.js";
|
|
24
27
|
import { boardCanvasSize, measureBoardCanvasBox } from "./canvas-size.js";
|
|
25
28
|
import { observeDisplayDensity } from "./display-density.js";
|
|
29
|
+
import { createBoardFrameClock } from "./frame-budget.js";
|
|
26
30
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
27
31
|
import { createBoardPointerGestures } from "./pointer-gestures.js";
|
|
28
32
|
import { createBoardScene } from "./scene.js";
|
|
29
33
|
import { scheduleBoardMaintenance } from "./maintenance.js";
|
|
34
|
+
import { boardStagePress, createBoardLongPress, createGhostClickGuard } from "./presses.js";
|
|
30
35
|
import { createBoardScriptPointerRouter } from "./script-pointer.js";
|
|
31
36
|
import { drawBoardOverlay } from "./overlay.js";
|
|
32
37
|
import { createTextResolutionTracker } from "./text-resolution-tracker.js";
|
|
@@ -55,6 +60,15 @@ const TEXTURE_SETTLE_MS = 150;
|
|
|
55
60
|
const ADOPT_PIXELS_PER_FRAME = 4194304;
|
|
56
61
|
const LOAD_LEAD_MS = 300;
|
|
57
62
|
const CULL_PADDING = 96;
|
|
63
|
+
const PREFETCH_REST_MS = 200;
|
|
64
|
+
const PREFETCH_LIMIT = 96;
|
|
65
|
+
const FRAME_SAMPLES = 120;
|
|
66
|
+
const ANTIALIAS_MAX_DENSITY = 3;
|
|
67
|
+
const FRAME_SYNC = 1;
|
|
68
|
+
const FRAME_RENDER = 2;
|
|
69
|
+
const FRAME_CARDS = 4;
|
|
70
|
+
const FRAME_SCRIPTS = 8;
|
|
71
|
+
const FRAME_RESIZE = 16;
|
|
58
72
|
const ROTATE_CURSOR = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36L21 8M21 3v5h-5' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36L21 8M21 3v5h-5' fill='none' stroke='%231d1d1f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 12 12, crosshair`;
|
|
59
73
|
const defaultStyles = /* @__PURE__ */ new Map();
|
|
60
74
|
function styleOf(theme) {
|
|
@@ -106,14 +120,23 @@ function mountBoardStage(host, options) {
|
|
|
106
120
|
let stopDensity = null;
|
|
107
121
|
let active = true;
|
|
108
122
|
let disposed = false;
|
|
109
|
-
let
|
|
110
|
-
let
|
|
111
|
-
let
|
|
112
|
-
let
|
|
123
|
+
let frameHandle = 0;
|
|
124
|
+
let frameWork = 0;
|
|
125
|
+
let inFrame = false;
|
|
126
|
+
let chained = false;
|
|
127
|
+
const clock = createBoardFrameClock();
|
|
128
|
+
const device = defaultBoardDeviceBudget();
|
|
129
|
+
const currentLod = () => options.lod?.() ?? device.lod;
|
|
130
|
+
const frameTimes = new Float32Array(FRAME_SAMPLES);
|
|
131
|
+
let frameCount = 0;
|
|
132
|
+
let frameCamera = null;
|
|
113
133
|
let cardTickTimer = null;
|
|
114
134
|
let cardTickDue = 0;
|
|
135
|
+
let prefetchKey = "";
|
|
136
|
+
let prefetchMovedAt = 0;
|
|
137
|
+
let prefetchContext = null;
|
|
138
|
+
let cancelPrefetch = null;
|
|
115
139
|
const staleIds = /* @__PURE__ */ new Set();
|
|
116
|
-
let resizeFrame = 0;
|
|
117
140
|
let canvasBox = null;
|
|
118
141
|
let cardEpoch = 0;
|
|
119
142
|
let fontEpoch = 0;
|
|
@@ -124,6 +147,13 @@ function mountBoardStage(host, options) {
|
|
|
124
147
|
};
|
|
125
148
|
let playbackCamera = null;
|
|
126
149
|
let cursor = "";
|
|
150
|
+
let lastContext = null;
|
|
151
|
+
let hoveredHit = null;
|
|
152
|
+
const ghostClicks = createGhostClickGuard(host.ownerDocument.defaultView);
|
|
153
|
+
const longPress = createBoardLongPress((press) => {
|
|
154
|
+
const item = editor.itemAt(pointToWorld(localPoint(press.clientX, press.clientY), renderCamera()));
|
|
155
|
+
if (item) options.onLongPress?.(item, press);
|
|
156
|
+
});
|
|
127
157
|
const renderCamera = () => playbackCamera ?? editor.camera;
|
|
128
158
|
const views = createBoardItemViewLayer(host, editor, options.views ?? [], renderCamera, scheduleTick);
|
|
129
159
|
const player = createBoardPlayer({ resolveColor: (color) => {
|
|
@@ -206,6 +236,7 @@ function mountBoardStage(host, options) {
|
|
|
206
236
|
};
|
|
207
237
|
}
|
|
208
238
|
const arrivals = /* @__PURE__ */ new Set();
|
|
239
|
+
const toned = /* @__PURE__ */ new Set();
|
|
209
240
|
function adoptArrivals() {
|
|
210
241
|
let pixels = 0;
|
|
211
242
|
for (const key of arrivals) {
|
|
@@ -298,13 +329,15 @@ function mountBoardStage(host, options) {
|
|
|
298
329
|
assetKey: assets.assetKey,
|
|
299
330
|
getTexture: (key) => assets.getTexture(key),
|
|
300
331
|
hasError: (key) => assets.hasError(key),
|
|
332
|
+
assetTone: (key) => assets.getTone(key),
|
|
301
333
|
fileState: (path) => options.fileState?.(path) ?? "ok",
|
|
302
334
|
acquireTexture: (key, item) => {
|
|
303
335
|
assets.acquire(key);
|
|
304
336
|
assets.requestItem(item, boardTextureEdge(item, textureScale()));
|
|
305
337
|
if (assets.getTexture(key)) naturalIds.add(item.id);
|
|
306
338
|
},
|
|
307
|
-
releaseTexture: (key) => assets.release(key)
|
|
339
|
+
releaseTexture: (key) => assets.release(key),
|
|
340
|
+
hoveredHit
|
|
308
341
|
};
|
|
309
342
|
}
|
|
310
343
|
function syncBackground(current) {
|
|
@@ -474,6 +507,36 @@ function mountBoardStage(host, options) {
|
|
|
474
507
|
if (style.selection.dash > 0 && selected.length > 0 && !(options.reducedMotion?.() ?? false)) scheduleMarch(style);
|
|
475
508
|
else stopMarch();
|
|
476
509
|
}
|
|
510
|
+
function opensOnTap(item) {
|
|
511
|
+
return !editor.selection.includes(item.id) && editor.registry.activation(item) === "tap";
|
|
512
|
+
}
|
|
513
|
+
function hitAt(item, point) {
|
|
514
|
+
if (!lastContext || !scene?.getNode(item.id)) return null;
|
|
515
|
+
return resolveRenderer(item, lastContext).hitAt?.(item, point, lastContext) ?? null;
|
|
516
|
+
}
|
|
517
|
+
function hoverHit(input) {
|
|
518
|
+
if (!options.onHit) return;
|
|
519
|
+
const item = input && editor.interaction.type === "idle" && editor.hoverId ? editor.itemById(editor.hoverId) : null;
|
|
520
|
+
const hit = item && input ? hitAt(item, input.world) : null;
|
|
521
|
+
const next = item && hit ? {
|
|
522
|
+
itemId: item.id,
|
|
523
|
+
action: hit.action,
|
|
524
|
+
cursor: hit.cursor ?? "pointer"
|
|
525
|
+
} : null;
|
|
526
|
+
if (next?.itemId === hoveredHit?.itemId && next?.action === hoveredHit?.action) return;
|
|
527
|
+
for (const id of [hoveredHit?.itemId, next?.itemId]) if (id) staleIds.add(id);
|
|
528
|
+
hoveredHit = next;
|
|
529
|
+
scheduleTick();
|
|
530
|
+
}
|
|
531
|
+
function pressHit(event, input) {
|
|
532
|
+
if (!options.onHit || event.button !== 0) return false;
|
|
533
|
+
const item = editor.itemAt(input.world);
|
|
534
|
+
const hit = item ? hitAt(item, input.world) : null;
|
|
535
|
+
if (!item || !hit || options.onHit(item, hit.action, boardStagePress(event)) === false) return false;
|
|
536
|
+
event.preventDefault();
|
|
537
|
+
if (event.pointerType !== "mouse") ghostClicks.arm();
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
477
540
|
function cursorFor() {
|
|
478
541
|
if (scriptMode === "interact" && scriptHover) {
|
|
479
542
|
const output = scriptOutputs.get(scriptHover.owner);
|
|
@@ -488,49 +551,191 @@ function mountBoardStage(host, options) {
|
|
|
488
551
|
case "draggingArrowHandle":
|
|
489
552
|
case "brushing": return "crosshair";
|
|
490
553
|
}
|
|
491
|
-
if (
|
|
554
|
+
if (interaction.type === "pressing") return "pointer";
|
|
555
|
+
if (editor.spaceHeld) return "grab";
|
|
556
|
+
if (hoveredHit) return hoveredHit.cursor;
|
|
492
557
|
const control = editor.hoveredTransformControl;
|
|
493
558
|
if (control?.kind === "resize") return resizeCursorForHandle(control.handle, editor.selectionTransform?.frame.rotation ?? 0);
|
|
494
559
|
if (control?.kind === "rotate") return ROTATE_CURSOR;
|
|
495
|
-
if (isCreationBoardTool(editor.tool)) return "crosshair";
|
|
496
560
|
const hovered = editor.hoverId ? editor.itemById(editor.hoverId) : null;
|
|
561
|
+
if (hovered && (editor.tool === "select" || editor.tool === "hand") && opensOnTap(hovered)) return "pointer";
|
|
562
|
+
if (editor.tool === "hand") return "grab";
|
|
563
|
+
if (isCreationBoardTool(editor.tool)) return "crosshair";
|
|
497
564
|
return hovered && !hovered.locked && editor.registry.capabilities(hovered).canMove ? "move" : "default";
|
|
498
565
|
}
|
|
566
|
+
function requestFrame(work) {
|
|
567
|
+
if (!active || disposed) return;
|
|
568
|
+
frameWork |= work;
|
|
569
|
+
if (!frameHandle && !inFrame) {
|
|
570
|
+
chained = false;
|
|
571
|
+
frameHandle = requestAnimationFrame(runFrame);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function cancelFrames() {
|
|
575
|
+
cancelAnimationFrame(frameHandle);
|
|
576
|
+
frameHandle = 0;
|
|
577
|
+
frameWork = 0;
|
|
578
|
+
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
579
|
+
cardTickTimer = null;
|
|
580
|
+
}
|
|
581
|
+
function runFrame(now) {
|
|
582
|
+
frameHandle = 0;
|
|
583
|
+
if (!app) return;
|
|
584
|
+
const start = performance.now();
|
|
585
|
+
let work = frameWork;
|
|
586
|
+
frameWork = 0;
|
|
587
|
+
inFrame = true;
|
|
588
|
+
try {
|
|
589
|
+
const camera = renderCamera();
|
|
590
|
+
const moving = editor.gestureActive || !frameCamera || camera.x !== frameCamera.x || camera.y !== frameCamera.y || camera.zoom !== frameCamera.zoom;
|
|
591
|
+
frameCamera = camera;
|
|
592
|
+
clock.begin(now, {
|
|
593
|
+
moving,
|
|
594
|
+
chained
|
|
595
|
+
});
|
|
596
|
+
if (work & FRAME_RESIZE && !applyResize()) work &= -17;
|
|
597
|
+
const synced = Boolean(work & 17);
|
|
598
|
+
if (synced) sync();
|
|
599
|
+
else if (work & FRAME_SCRIPTS) {
|
|
600
|
+
syncScripts();
|
|
601
|
+
if (scriptMode !== "paused" && scripts?.animated) frameWork |= FRAME_SCRIPTS;
|
|
602
|
+
}
|
|
603
|
+
if (work & FRAME_CARDS && !synced && scene) {
|
|
604
|
+
const next = scene.tick(performance.now());
|
|
605
|
+
work |= FRAME_RENDER;
|
|
606
|
+
if (next !== null) scheduleCardTick(next);
|
|
607
|
+
}
|
|
608
|
+
if ((work | frameWork) & FRAME_RENDER) {
|
|
609
|
+
frameWork &= -3;
|
|
610
|
+
app.render();
|
|
611
|
+
}
|
|
612
|
+
} finally {
|
|
613
|
+
inFrame = false;
|
|
614
|
+
frameTimes[frameCount++ % FRAME_SAMPLES] = performance.now() - start;
|
|
615
|
+
if (frameWork && active && !disposed) {
|
|
616
|
+
chained = true;
|
|
617
|
+
frameHandle = requestAnimationFrame(runFrame);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function waitingCards() {
|
|
622
|
+
if (!scene || surface.width === 0) return 0;
|
|
623
|
+
const view = visibleWorldRect(renderCamera(), surface.width, surface.height);
|
|
624
|
+
let waiting = 0;
|
|
625
|
+
for (const id of scene.liveIds()) {
|
|
626
|
+
const item = editor.scene.get(id);
|
|
627
|
+
const key = item && assets.assetKey(item);
|
|
628
|
+
if (key && !assets.hasError(key) && !assets.getTexture(key) && rectsIntersect(itemBounds(item.frame), view)) waiting += 1;
|
|
629
|
+
}
|
|
630
|
+
return waiting;
|
|
631
|
+
}
|
|
632
|
+
function frameStats() {
|
|
633
|
+
const samples = Array.from(frameTimes.subarray(0, Math.min(frameCount, FRAME_SAMPLES))).sort((a, b) => a - b);
|
|
634
|
+
const at = (ratio) => samples[Math.min(samples.length - 1, Math.floor(samples.length * ratio))] ?? 0;
|
|
635
|
+
return {
|
|
636
|
+
p50: at(.5),
|
|
637
|
+
p95: at(.95),
|
|
638
|
+
max: samples.at(-1) ?? 0
|
|
639
|
+
};
|
|
640
|
+
}
|
|
499
641
|
function scheduleRender() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
642
|
+
requestFrame(FRAME_RENDER);
|
|
643
|
+
}
|
|
644
|
+
function scheduleTick() {
|
|
645
|
+
requestFrame(FRAME_SYNC);
|
|
646
|
+
}
|
|
647
|
+
function scheduleScriptTick() {
|
|
648
|
+
requestFrame(FRAME_SCRIPTS);
|
|
649
|
+
}
|
|
650
|
+
function resize() {
|
|
651
|
+
requestFrame(FRAME_RESIZE);
|
|
652
|
+
}
|
|
653
|
+
function flushResize() {
|
|
654
|
+
if (!active || disposed) return;
|
|
655
|
+
cancelAnimationFrame(frameHandle);
|
|
656
|
+
frameWork |= FRAME_RESIZE;
|
|
657
|
+
chained = false;
|
|
658
|
+
runFrame(performance.now());
|
|
659
|
+
chained = false;
|
|
505
660
|
}
|
|
506
661
|
function scheduleCardTick(wait) {
|
|
507
662
|
if (!active || disposed) return;
|
|
508
663
|
const due = performance.now() + wait;
|
|
509
|
-
if (
|
|
664
|
+
if (frameWork & FRAME_CARDS || cardTickTimer !== null && cardTickDue <= due) return;
|
|
510
665
|
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
511
666
|
cardTickTimer = null;
|
|
512
667
|
if (wait > 0) {
|
|
513
668
|
cardTickDue = due;
|
|
514
669
|
cardTickTimer = setTimeout(() => {
|
|
515
670
|
cardTickTimer = null;
|
|
516
|
-
|
|
671
|
+
requestFrame(FRAME_CARDS);
|
|
517
672
|
}, wait);
|
|
518
673
|
return;
|
|
519
674
|
}
|
|
520
|
-
|
|
521
|
-
cardTickFrame = 0;
|
|
522
|
-
if (!scene) return;
|
|
523
|
-
const next = scene.tick(performance.now());
|
|
524
|
-
scheduleRender();
|
|
525
|
-
if (next !== null) scheduleCardTick(next);
|
|
526
|
-
});
|
|
675
|
+
requestFrame(FRAME_CARDS);
|
|
527
676
|
}
|
|
528
|
-
function
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
677
|
+
function schedulePrefetch(camera, context) {
|
|
678
|
+
const margin = assets.prefetch ? options.prefetch ?? device.prefetch : 0;
|
|
679
|
+
if (margin <= 0 || surface.width === 0 || surface.height === 0) return;
|
|
680
|
+
const key = `${camera.x}|${camera.y}|${camera.zoom}|${surface.width}|${surface.height}|${editor.structureVersion}`;
|
|
681
|
+
if (key === prefetchKey) return;
|
|
682
|
+
prefetchKey = key;
|
|
683
|
+
prefetchContext = context;
|
|
684
|
+
prefetchMovedAt = performance.now();
|
|
685
|
+
if (!cancelPrefetch) awaitRest(margin, PREFETCH_REST_MS);
|
|
686
|
+
}
|
|
687
|
+
function awaitRest(margin, delay) {
|
|
688
|
+
cancelPrefetch = scheduleBoardIdle(() => {
|
|
689
|
+
cancelPrefetch = null;
|
|
690
|
+
const remaining = PREFETCH_REST_MS - (performance.now() - prefetchMovedAt);
|
|
691
|
+
if (remaining > 0) awaitRest(margin, remaining);
|
|
692
|
+
else if (prefetchContext) prefetch(margin, prefetchContext);
|
|
693
|
+
}, delay);
|
|
694
|
+
}
|
|
695
|
+
function stopPrefetch() {
|
|
696
|
+
cancelPrefetch?.();
|
|
697
|
+
cancelPrefetch = null;
|
|
698
|
+
prefetchKey = "";
|
|
699
|
+
prefetchContext = null;
|
|
700
|
+
assets.prefetch?.([]);
|
|
701
|
+
}
|
|
702
|
+
function prefetch(margin, context) {
|
|
703
|
+
if (!scene || !active || disposed) return;
|
|
704
|
+
if (editor.gestureActive) {
|
|
705
|
+
prefetchKey = "";
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const camera = renderCamera();
|
|
709
|
+
const view = visibleWorldRect(camera, surface.width, surface.height);
|
|
710
|
+
const culled = stableCullRect(view);
|
|
711
|
+
const padX = view.width * margin;
|
|
712
|
+
const padY = view.height * margin;
|
|
713
|
+
const ring = {
|
|
714
|
+
x: culled.x - padX,
|
|
715
|
+
y: culled.y - padY,
|
|
716
|
+
width: culled.width + padX * 2,
|
|
717
|
+
height: culled.height + padY * 2
|
|
718
|
+
};
|
|
719
|
+
const centerX = view.x + view.width / 2;
|
|
720
|
+
const centerY = view.y + view.height / 2;
|
|
721
|
+
const lod = currentLod();
|
|
722
|
+
const scale = textureScale();
|
|
723
|
+
const wanted = [];
|
|
724
|
+
for (const id of editor.idsInRect(ring)) {
|
|
725
|
+
const item = editor.scene.get(id);
|
|
726
|
+
if (!item || rectsIntersect(itemBounds(item.frame), culled)) continue;
|
|
727
|
+
const renderer = resolveRenderer(item, context);
|
|
728
|
+
if (renderer.renderFar && boardLodRatio(renderer, boardLodSize(renderer, item), camera.zoom, lod) < 1) continue;
|
|
729
|
+
const { frame } = item;
|
|
730
|
+
const distance = (frame.x + frame.width / 2 - centerX) ** 2 + (frame.y + frame.height / 2 - centerY) ** 2;
|
|
731
|
+
wanted.push({
|
|
732
|
+
item,
|
|
733
|
+
edge: boardTextureEdge(item, scale),
|
|
734
|
+
distance
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
wanted.sort((a, b) => a.distance - b.distance);
|
|
738
|
+
assets.prefetch?.(wanted.slice(0, PREFETCH_LIMIT));
|
|
534
739
|
}
|
|
535
740
|
function cancelScriptPointers(owner, retained) {
|
|
536
741
|
scriptPointers.cancelScripts((pointerId, target) => {
|
|
@@ -559,15 +764,6 @@ function mountBoardStage(host, options) {
|
|
|
559
764
|
}));
|
|
560
765
|
scripts.sync(editor.document, editor.scene, camera, scriptTime, scriptMode === "interact", visibleIds());
|
|
561
766
|
}
|
|
562
|
-
function scheduleScriptTick() {
|
|
563
|
-
if (scriptTickFrame || !active || disposed) return;
|
|
564
|
-
scriptTickFrame = requestAnimationFrame(() => {
|
|
565
|
-
scriptTickFrame = 0;
|
|
566
|
-
if (tickFrame) return;
|
|
567
|
-
syncScripts();
|
|
568
|
-
if (scriptMode !== "paused" && scripts?.animated) scheduleScriptTick();
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
767
|
function createScripts() {
|
|
572
768
|
return createBoardScriptHost({
|
|
573
769
|
host,
|
|
@@ -686,8 +882,13 @@ function mountBoardStage(host, options) {
|
|
|
686
882
|
const center = pointToWorld(screenPoint(surface.width / 2, surface.height / 2), camera);
|
|
687
883
|
assets.focus(loadFocus(center, camera));
|
|
688
884
|
adoptArrivals();
|
|
885
|
+
if (toned.size > 0) {
|
|
886
|
+
scene?.repaintFar(toned, assets.assetKey);
|
|
887
|
+
toned.clear();
|
|
888
|
+
}
|
|
689
889
|
textResolution.update(textureScale());
|
|
690
890
|
const context = renderContext(current, renderScene, frame.time);
|
|
891
|
+
lastContext = context;
|
|
691
892
|
if (animationId && effectSampler) {
|
|
692
893
|
context.time = editor.playhead?.time ?? frame.time;
|
|
693
894
|
context.frameAt = effectSampler.frameAt;
|
|
@@ -723,9 +924,11 @@ function mountBoardStage(host, options) {
|
|
|
723
924
|
gestureActive: editor.gestureActive && editor.interaction.type !== "panning",
|
|
724
925
|
focus: center,
|
|
725
926
|
view: ids ? cullView(camera) : void 0,
|
|
726
|
-
lod:
|
|
927
|
+
lod: currentLod(),
|
|
928
|
+
budget: clock
|
|
727
929
|
});
|
|
728
930
|
staleIds.clear();
|
|
931
|
+
schedulePrefetch(camera, context);
|
|
729
932
|
if (scene.liveVersion !== liveVersion) {
|
|
730
933
|
liveVersion = scene.liveVersion;
|
|
731
934
|
options.onLiveChange?.(scene.liveIds());
|
|
@@ -749,29 +952,26 @@ function mountBoardStage(host, options) {
|
|
|
749
952
|
const cardWait = scene.tick(performance.now());
|
|
750
953
|
if (cardWait !== null) scheduleCardTick(cardWait);
|
|
751
954
|
}
|
|
752
|
-
function
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
sync();
|
|
774
|
-
});
|
|
955
|
+
function applyResize() {
|
|
956
|
+
if (!app) return false;
|
|
957
|
+
const size = {
|
|
958
|
+
width: host.clientWidth,
|
|
959
|
+
height: host.clientHeight
|
|
960
|
+
};
|
|
961
|
+
if (size.width <= 0 || size.height <= 0) return false;
|
|
962
|
+
const density = host.ownerDocument.defaultView?.devicePixelRatio ?? 1;
|
|
963
|
+
const canvas = boardCanvasSize(canvasBox ?? {
|
|
964
|
+
...size,
|
|
965
|
+
density,
|
|
966
|
+
device: null
|
|
967
|
+
}, density);
|
|
968
|
+
if (canvas.width !== app.screen.width || canvas.height !== app.screen.height || canvas.resolution !== app.renderer.resolution) app.renderer.resize(canvas.width, canvas.height, canvas.resolution);
|
|
969
|
+
if (size.width !== surface.width || size.height !== surface.height) {
|
|
970
|
+
surface = size;
|
|
971
|
+
editor.surfaceSize = surface;
|
|
972
|
+
options.onSurfaceChange?.(surface);
|
|
973
|
+
}
|
|
974
|
+
return true;
|
|
775
975
|
}
|
|
776
976
|
const localPoint = (x, y) => elementClientToLocal(host)(x, y);
|
|
777
977
|
const inViews = (event) => event.target instanceof Node && views.element.contains(event.target);
|
|
@@ -853,8 +1053,11 @@ function mountBoardStage(host, options) {
|
|
|
853
1053
|
return;
|
|
854
1054
|
}
|
|
855
1055
|
if (options.onPointer?.("down", event, input) === false) return;
|
|
1056
|
+
if (pressHit(event, input)) return;
|
|
856
1057
|
pointers.start(event);
|
|
857
1058
|
editor.pointerDown(input);
|
|
1059
|
+
if (options.onLongPress && editor.itemAt(input.world)) longPress.start(event);
|
|
1060
|
+
else longPress.cancel();
|
|
858
1061
|
}
|
|
859
1062
|
function handlePointerMove(event) {
|
|
860
1063
|
if (!active || pointers.has(event.pointerId) || inViews(event)) return;
|
|
@@ -863,21 +1066,28 @@ function mountBoardStage(host, options) {
|
|
|
863
1066
|
function routePointerMove(event) {
|
|
864
1067
|
const input = toInput(event);
|
|
865
1068
|
if (scriptPointer("move", event, input)) return;
|
|
1069
|
+
longPress.move(event);
|
|
866
1070
|
if (options.onPointer?.("move", event, input) === false) return;
|
|
867
1071
|
editor.pointerMove(input);
|
|
1072
|
+
hoverHit(input);
|
|
868
1073
|
}
|
|
869
1074
|
function handlePointerUp(event) {
|
|
870
1075
|
const input = toInput(event);
|
|
871
1076
|
if (scriptPointer("up", event, input)) return;
|
|
1077
|
+
longPress.end(event);
|
|
872
1078
|
if (options.onPointer?.("up", event, input) === false) return;
|
|
873
1079
|
editor.pointerUp(input);
|
|
874
|
-
if (event.type === "pointercancel" || event.pointerType !== "mouse")
|
|
1080
|
+
if (event.type === "pointercancel" || event.pointerType !== "mouse") {
|
|
1081
|
+
editor.pointerLeave();
|
|
1082
|
+
hoverHit(null);
|
|
1083
|
+
}
|
|
875
1084
|
}
|
|
876
1085
|
function handlePointerLeave(event) {
|
|
877
1086
|
if (event.buttons !== 0) return;
|
|
878
1087
|
scriptPointer("leave", event, toInput(event));
|
|
879
1088
|
if (options.onPointer?.("leave", event, toInput(event)) === false) return;
|
|
880
1089
|
editor.pointerLeave();
|
|
1090
|
+
hoverHit(null);
|
|
881
1091
|
}
|
|
882
1092
|
function handleWheel(event) {
|
|
883
1093
|
if (inViews(event)) return;
|
|
@@ -896,7 +1106,7 @@ function mountBoardStage(host, options) {
|
|
|
896
1106
|
return;
|
|
897
1107
|
}
|
|
898
1108
|
if (item && item.type !== "frame" && item.type !== "arrow") return;
|
|
899
|
-
const label = editor.labelItemAt(point);
|
|
1109
|
+
const label = editor.labelItemAt(point, styleOf(theme()).frameLabelSize);
|
|
900
1110
|
if (label) editor.editingId = label.id;
|
|
901
1111
|
else if (!item) editor.beginTextDraft(point);
|
|
902
1112
|
}
|
|
@@ -914,27 +1124,40 @@ function mountBoardStage(host, options) {
|
|
|
914
1124
|
["dblclick", handleDoubleClick]
|
|
915
1125
|
];
|
|
916
1126
|
const stopEditor = editor.subscribe(LIVE_CHANNELS, scheduleTick);
|
|
1127
|
+
const stopActivations = editor.subscribeActivations(({ itemId, pointerType, selected }) => {
|
|
1128
|
+
const item = editor.itemById(itemId);
|
|
1129
|
+
if (!item || !options.onActivate) return;
|
|
1130
|
+
if (pointerType !== "mouse") ghostClicks.arm();
|
|
1131
|
+
options.onActivate(item, {
|
|
1132
|
+
itemId,
|
|
1133
|
+
pointerType,
|
|
1134
|
+
selected
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
917
1137
|
const stopFonts = onBoardTextFontsChange(() => {
|
|
918
1138
|
editor.relayoutText();
|
|
919
1139
|
fontEpoch += 1;
|
|
920
1140
|
scene?.redrawText();
|
|
921
1141
|
scheduleTick();
|
|
922
1142
|
});
|
|
923
|
-
const stopAssets = assets.subscribe((key) => {
|
|
924
|
-
arrivals.add(key);
|
|
1143
|
+
const stopAssets = assets.subscribe((key, change) => {
|
|
1144
|
+
if (change === "texture") arrivals.add(key);
|
|
1145
|
+
else if (scene?.counts().far) toned.add(key);
|
|
1146
|
+
else return;
|
|
925
1147
|
scheduleTick();
|
|
926
1148
|
});
|
|
927
1149
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
928
1150
|
const entry = entries[entries.length - 1];
|
|
929
1151
|
if (entry) canvasBox = measureBoardCanvasBox(entry, host.ownerDocument.defaultView?.devicePixelRatio ?? 1);
|
|
930
|
-
|
|
1152
|
+
flushResize();
|
|
931
1153
|
});
|
|
932
1154
|
return {
|
|
933
1155
|
ready: (async () => {
|
|
934
1156
|
const instance = new Application();
|
|
1157
|
+
const density = host.ownerDocument.defaultView?.devicePixelRatio ?? 1;
|
|
935
1158
|
try {
|
|
936
1159
|
await instance.init({
|
|
937
|
-
antialias:
|
|
1160
|
+
antialias: density < ANTIALIAS_MAX_DENSITY,
|
|
938
1161
|
backgroundAlpha: 0,
|
|
939
1162
|
width: host.clientWidth,
|
|
940
1163
|
height: host.clientHeight,
|
|
@@ -1042,16 +1265,8 @@ function mountBoardStage(host, options) {
|
|
|
1042
1265
|
stopMarch();
|
|
1043
1266
|
stopRefine();
|
|
1044
1267
|
cancelScriptPointers();
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
cancelAnimationFrame(renderFrame);
|
|
1048
|
-
cancelAnimationFrame(tickFrame);
|
|
1049
|
-
cancelAnimationFrame(cardTickFrame);
|
|
1050
|
-
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
1051
|
-
renderFrame = 0;
|
|
1052
|
-
tickFrame = 0;
|
|
1053
|
-
cardTickFrame = 0;
|
|
1054
|
-
cardTickTimer = null;
|
|
1268
|
+
cancelFrames();
|
|
1269
|
+
stopPrefetch();
|
|
1055
1270
|
return;
|
|
1056
1271
|
}
|
|
1057
1272
|
player.reset();
|
|
@@ -1065,7 +1280,7 @@ function mountBoardStage(host, options) {
|
|
|
1065
1280
|
},
|
|
1066
1281
|
isMaterialized: (id) => Boolean(scene?.getNode(id)),
|
|
1067
1282
|
stats() {
|
|
1068
|
-
const { textures, cooling, bytes } = assets.stats();
|
|
1283
|
+
const { textures, cooling, bytes, loading, prefetching } = assets.stats();
|
|
1069
1284
|
return {
|
|
1070
1285
|
...scene?.counts() ?? {
|
|
1071
1286
|
live: 0,
|
|
@@ -1073,7 +1288,13 @@ function mountBoardStage(host, options) {
|
|
|
1073
1288
|
},
|
|
1074
1289
|
textures,
|
|
1075
1290
|
cooling,
|
|
1076
|
-
textureBytes: bytes
|
|
1291
|
+
textureBytes: bytes,
|
|
1292
|
+
loading: loading ?? 0,
|
|
1293
|
+
prefetching: prefetching ?? 0,
|
|
1294
|
+
waiting: waitingCards(),
|
|
1295
|
+
settling: scene?.settling ?? false,
|
|
1296
|
+
frame: frameStats(),
|
|
1297
|
+
interval: clock.interval
|
|
1077
1298
|
};
|
|
1078
1299
|
},
|
|
1079
1300
|
toWorld: (clientX, clientY) => pointToWorld(localPoint(clientX, clientY), renderCamera()),
|
|
@@ -1090,16 +1311,14 @@ function mountBoardStage(host, options) {
|
|
|
1090
1311
|
stopMaintenance?.();
|
|
1091
1312
|
stopDensity?.();
|
|
1092
1313
|
stopEditor();
|
|
1314
|
+
stopActivations();
|
|
1315
|
+
longPress.cancel();
|
|
1316
|
+
ghostClicks.dispose();
|
|
1093
1317
|
stopFonts();
|
|
1094
1318
|
stopAssets();
|
|
1095
1319
|
resizeObserver.disconnect();
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
cancelAnimationFrame(renderFrame);
|
|
1099
|
-
cancelAnimationFrame(tickFrame);
|
|
1100
|
-
cancelAnimationFrame(cardTickFrame);
|
|
1101
|
-
if (cardTickTimer !== null) clearTimeout(cardTickTimer);
|
|
1102
|
-
cardTickTimer = null;
|
|
1320
|
+
cancelFrames();
|
|
1321
|
+
stopPrefetch();
|
|
1103
1322
|
for (const [type, listener] of listeners) host.removeEventListener(type, listener);
|
|
1104
1323
|
editor.setPlayedItems(/* @__PURE__ */ new Map());
|
|
1105
1324
|
editor.setScriptItems(/* @__PURE__ */ new Map());
|
|
@@ -8,12 +8,14 @@ type BoardImageTexturePool = {
|
|
|
8
8
|
declare function loadBoardImageTexture(url: string): Promise<Texture>;
|
|
9
9
|
declare function unloadBoardImageTexture(url: string, texture: Texture | null): Promise<void>;
|
|
10
10
|
/**
|
|
11
|
-
* Decode an image no larger than `edge` along its long side
|
|
12
|
-
* without CDN variants. The texture is unshared; destroy
|
|
11
|
+
* Decode an image no larger than `edge` along its long side (short side with
|
|
12
|
+
* `cover`), for sources without CDN variants. The texture is unshared; destroy
|
|
13
|
+
* it to release it.
|
|
13
14
|
*/
|
|
14
|
-
declare function loadFittedImageTexture(url: string, edge: number): Promise<Texture>;
|
|
15
|
+
declare function loadFittedImageTexture(url: string, edge: number, cover?: boolean): Promise<Texture>;
|
|
15
16
|
/** A smaller unshared copy of a decoded texture, or null when it cannot be read back. */
|
|
16
17
|
declare function shrinkBoardTexture(texture: Texture, edge: number): Promise<Texture | null>;
|
|
18
|
+
declare function readTextureTone(texture: Texture): Promise<number | null>;
|
|
17
19
|
/**
|
|
18
20
|
* Share image textures by resolved URL without sharing Board ownership.
|
|
19
21
|
*
|
|
@@ -28,4 +30,4 @@ declare function createBoardImageTexturePool(options?: {
|
|
|
28
30
|
}): BoardImageTexturePool;
|
|
29
31
|
declare const boardImageTexturePool: BoardImageTexturePool;
|
|
30
32
|
//#endregion
|
|
31
|
-
export { BoardImageTexturePool, boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, loadFittedImageTexture, shrinkBoardTexture, unloadBoardImageTexture };
|
|
33
|
+
export { BoardImageTexturePool, boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, loadFittedImageTexture, readTextureTone, shrinkBoardTexture, unloadBoardImageTexture };
|