@netless/app-presentation 0.1.10 → 0.1.11
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-zh.md +42 -1
- package/README.md +47 -1
- package/dist/index.d.ts +20 -5
- package/dist/index.global.js +424 -119
- package/dist/index.js +424 -119
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +424 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/app-presentation.ts +423 -117
- package/src/camera-options.ts +19 -0
- package/src/camera-reference.ts +81 -0
package/dist/index.js
CHANGED
|
@@ -1884,10 +1884,80 @@ var Scrollbar = class {
|
|
|
1884
1884
|
}
|
|
1885
1885
|
};
|
|
1886
1886
|
|
|
1887
|
+
// src/camera-options.ts
|
|
1888
|
+
function getCameraScaleRange(fitScale, maxCameraScale, disableCameraTransform) {
|
|
1889
|
+
return {
|
|
1890
|
+
minScale: fitScale,
|
|
1891
|
+
maxScale: fitScale * (disableCameraTransform ? 1 : maxCameraScale)
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
function shouldDisableDeviceCameraTransform(options) {
|
|
1895
|
+
return Boolean(options.disableCameraTransform || options.disableDeviceCameraTransform);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// src/camera-reference.ts
|
|
1899
|
+
function isValidSize(size) {
|
|
1900
|
+
return Boolean(
|
|
1901
|
+
size && Number.isFinite(size.width) && Number.isFinite(size.height) && size.width > 0 && size.height > 0
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
function isValidSharedViewport(viewport) {
|
|
1905
|
+
return Boolean(
|
|
1906
|
+
viewport && Number.isFinite(viewport.originX) && Number.isFinite(viewport.originY) && isValidSize(viewport)
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
function getCameraReferenceSize(originSize, pageSize) {
|
|
1910
|
+
return isValidSize(originSize) ? originSize : pageSize;
|
|
1911
|
+
}
|
|
1912
|
+
function fitPageSizeToOrigin(pageSize, originSize) {
|
|
1913
|
+
if (!isValidSize(pageSize) || !isValidSize(originSize))
|
|
1914
|
+
return __spreadValues({}, pageSize);
|
|
1915
|
+
const ratio = Math.min(
|
|
1916
|
+
originSize.width / pageSize.width,
|
|
1917
|
+
originSize.height / pageSize.height
|
|
1918
|
+
);
|
|
1919
|
+
if (!(Number.isFinite(ratio) && ratio > 0))
|
|
1920
|
+
return __spreadValues({}, pageSize);
|
|
1921
|
+
return {
|
|
1922
|
+
width: pageSize.width * ratio,
|
|
1923
|
+
height: pageSize.height * ratio
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
function getFitScale(viewSize, referenceSize) {
|
|
1927
|
+
if (!isValidSize(viewSize) || !isValidSize(referenceSize))
|
|
1928
|
+
return;
|
|
1929
|
+
const scale = Math.min(
|
|
1930
|
+
viewSize.width / referenceSize.width,
|
|
1931
|
+
viewSize.height / referenceSize.height
|
|
1932
|
+
);
|
|
1933
|
+
return Number.isFinite(scale) && scale > 0 ? scale : void 0;
|
|
1934
|
+
}
|
|
1935
|
+
function cameraToSharedViewport(camera, viewSize, referenceSize) {
|
|
1936
|
+
const fitScale = getFitScale(viewSize, referenceSize);
|
|
1937
|
+
if (fitScale === void 0 || !Number.isFinite(camera.centerX) || !Number.isFinite(camera.centerY) || !Number.isFinite(camera.scale) || camera.scale <= 0) {
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
const normalizedScale = camera.scale / fitScale;
|
|
1941
|
+
const width = referenceSize.width / normalizedScale;
|
|
1942
|
+
const height = referenceSize.height / normalizedScale;
|
|
1943
|
+
return {
|
|
1944
|
+
originX: camera.centerX - width / 2,
|
|
1945
|
+
originY: camera.centerY - height / 2,
|
|
1946
|
+
width,
|
|
1947
|
+
height
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1887
1951
|
// src/app-presentation.ts
|
|
1888
1952
|
var import_debounce2 = __toESM(require_debounce());
|
|
1889
1953
|
var emptySceneName = "$$empty$$";
|
|
1890
|
-
var
|
|
1954
|
+
var ORIGIN_SIZE_COORDINATE_VERSION = 2;
|
|
1955
|
+
var ppt2page = (ppt, name, originSize) => {
|
|
1956
|
+
if (!ppt)
|
|
1957
|
+
return null;
|
|
1958
|
+
const size = fitPageSizeToOrigin(ppt, originSize);
|
|
1959
|
+
return __spreadProps(__spreadValues({}, size), { src: ppt.src, thumbnail: ppt.previewURL, name });
|
|
1960
|
+
};
|
|
1891
1961
|
var createLogger = (room) => {
|
|
1892
1962
|
if (room && room.logger) {
|
|
1893
1963
|
return (...args) => room.logger.info(...args);
|
|
@@ -1895,6 +1965,55 @@ var createLogger = (room) => {
|
|
|
1895
1965
|
return (...args) => console.log(...args);
|
|
1896
1966
|
}
|
|
1897
1967
|
};
|
|
1968
|
+
var createDiagnosticLogger = (context) => {
|
|
1969
|
+
var _a;
|
|
1970
|
+
const createAppLogger = context.createLogger;
|
|
1971
|
+
if (typeof createAppLogger === "function") {
|
|
1972
|
+
return createAppLogger.call(context, "camera", { debounceTime: 300, maxWaitTime: 2e3 });
|
|
1973
|
+
}
|
|
1974
|
+
const roomLogger = (_a = context.getRoom()) == null ? void 0 : _a.logger;
|
|
1975
|
+
const prefix = `[Presentation][${context.appId}][camera]`;
|
|
1976
|
+
const emit = (level, event, ...data) => {
|
|
1977
|
+
try {
|
|
1978
|
+
const printer = roomLogger == null ? void 0 : roomLogger[level];
|
|
1979
|
+
if (typeof printer === "function")
|
|
1980
|
+
printer.call(roomLogger, `${prefix}[${event}]`, ...data);
|
|
1981
|
+
} catch (e) {
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
const debouncedByEvent = /* @__PURE__ */ new Map();
|
|
1985
|
+
const debouncedInfo = (event, payload) => {
|
|
1986
|
+
let emitDebounced = debouncedByEvent.get(event);
|
|
1987
|
+
if (!emitDebounced) {
|
|
1988
|
+
emitDebounced = (0, import_debounce2.default)(
|
|
1989
|
+
(nextPayload) => emit("info", event, nextPayload),
|
|
1990
|
+
300,
|
|
1991
|
+
{ maxWait: 2e3 }
|
|
1992
|
+
);
|
|
1993
|
+
debouncedByEvent.set(event, emitDebounced);
|
|
1994
|
+
}
|
|
1995
|
+
emitDebounced(payload);
|
|
1996
|
+
};
|
|
1997
|
+
return {
|
|
1998
|
+
info: (event, payload) => emit("info", event, payload),
|
|
1999
|
+
warn: (event, payload) => emit("warn", event, payload),
|
|
2000
|
+
error: (event, error, payload) => emit("error", event, error, payload),
|
|
2001
|
+
debouncedInfo,
|
|
2002
|
+
flush: () => debouncedByEvent.forEach((logger) => logger.flush())
|
|
2003
|
+
};
|
|
2004
|
+
};
|
|
2005
|
+
var safeResourceLocation = (value) => {
|
|
2006
|
+
if (value.startsWith("data:"))
|
|
2007
|
+
return "data:[omitted]";
|
|
2008
|
+
if (value.startsWith("blob:"))
|
|
2009
|
+
return "blob:[omitted]";
|
|
2010
|
+
try {
|
|
2011
|
+
const url = new URL(value);
|
|
2012
|
+
return `${url.origin}${url.pathname}`;
|
|
2013
|
+
} catch (e) {
|
|
2014
|
+
return value.split("?")[0];
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
1898
2017
|
var scenesEqual = (scenes1, scenes2) => {
|
|
1899
2018
|
if (!scenes1 || !scenes2) {
|
|
1900
2019
|
return false;
|
|
@@ -1911,25 +2030,43 @@ var NetlessAppPresentation = {
|
|
|
1911
2030
|
kind: "Presentation",
|
|
1912
2031
|
setup(context) {
|
|
1913
2032
|
var _a, _b, _c;
|
|
2033
|
+
const diagnosticLogger = createDiagnosticLogger(context);
|
|
1914
2034
|
const view = context.getView();
|
|
1915
2035
|
if (!view)
|
|
1916
2036
|
throw new Error("[Presentation]: no whiteboard view, make sure you have added options.scenePath in addApp()");
|
|
1917
|
-
const
|
|
2037
|
+
const options = context.getAppOptions() || {};
|
|
2038
|
+
const room = context.getRoom();
|
|
2039
|
+
const log = options.log || createLogger(room);
|
|
2040
|
+
const roomLogger = room == null ? void 0 : room.logger;
|
|
2041
|
+
const warn = (...data) => (roomLogger == null ? void 0 : roomLogger.warn) ? roomLogger.warn(...data) : log(...data);
|
|
2042
|
+
const configuredOriginSize = context.storage.state.originSize;
|
|
2043
|
+
const originSize = isValidSize(configuredOriginSize) ? { width: configuredOriginSize.width, height: configuredOriginSize.height } : void 0;
|
|
2044
|
+
if (configuredOriginSize != null && !originSize) {
|
|
2045
|
+
warn(`[Presentation] originSize should contain finite positive width and height, got ${JSON.stringify(configuredOriginSize)}`);
|
|
2046
|
+
}
|
|
2047
|
+
const useOriginSizeCoordinates = Boolean(
|
|
2048
|
+
originSize && (context.isAddApp || context.storage.state._originSizeCoordinateVersion === ORIGIN_SIZE_COORDINATE_VERSION)
|
|
2049
|
+
);
|
|
2050
|
+
if (originSize && context.isAddApp && context.getIsWritable()) {
|
|
2051
|
+
context.storage.setState({
|
|
2052
|
+
_originSizeCoordinateVersion: ORIGIN_SIZE_COORDINATE_VERSION
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
const pages = (_a = context.getScenes()) == null ? void 0 : _a.map(({ ppt, name }) => ppt2page(ppt, name, useOriginSizeCoordinates ? originSize : void 0)).filter(Boolean);
|
|
1918
2056
|
if (!pages || pages.length === 0)
|
|
1919
2057
|
throw new Error("[Presentation]: empty scenes, make sure you have added options.scenes in addApp()");
|
|
1920
2058
|
if (pages[0].src.startsWith("ppt"))
|
|
1921
2059
|
throw new Error("[Presentation]: legacy dynamic PPT is unsupported, please use the projector converter and @netless/slide to render it");
|
|
1922
2060
|
const scenePath = context.getInitScenePath();
|
|
1923
|
-
const options = context.getAppOptions() || {};
|
|
1924
2061
|
let maxCameraScale = (_b = options.maxCameraScale) != null ? _b : 3;
|
|
1925
2062
|
if (!(Number.isFinite(maxCameraScale) && maxCameraScale > 0)) {
|
|
1926
|
-
|
|
2063
|
+
warn(`[Presentation] maxCameraScale should be a positive number, got ${options.maxCameraScale}`);
|
|
1927
2064
|
maxCameraScale = 3;
|
|
1928
2065
|
}
|
|
1929
|
-
const log = options.log || createLogger(context.getRoom());
|
|
1930
2066
|
log(`[Presentation] new ${context.appId}`);
|
|
1931
2067
|
const dispose2 = disposableStore();
|
|
1932
2068
|
dispose2.add(() => log(`[Presentation] dispose ${context.appId}`));
|
|
2069
|
+
dispose2.add(() => diagnosticLogger.flush());
|
|
1933
2070
|
const view$$ = context.createStorage("view", { uid: "", originX: 0, originY: 0, width: 0, height: 0 });
|
|
1934
2071
|
const _addScenePathListener = (name, listener) => {
|
|
1935
2072
|
const windowManger = context.manager.windowManger;
|
|
@@ -1963,58 +2100,15 @@ var NetlessAppPresentation = {
|
|
|
1963
2100
|
dispose2.add(() => {
|
|
1964
2101
|
pageIndex$.dispose();
|
|
1965
2102
|
});
|
|
1966
|
-
if (context.isAddApp) {
|
|
1967
|
-
if (pages.length > 100)
|
|
1968
|
-
console.warn(`[Presentation]: too many pages (${pages.length}), may cause performance issues`);
|
|
1969
|
-
let redirectResolve = void 0;
|
|
1970
|
-
const room2 = context.getRoom();
|
|
1971
|
-
if (room2 && room2.isWritable) {
|
|
1972
|
-
const scenes = room2.entireScenes()[scenePath];
|
|
1973
|
-
if (pageIndex$.value < 0 || pageIndex$.value >= pages.length) {
|
|
1974
|
-
throw new Error(`[Presentation] Invalid page index: ${pageIndex$.value}, scenes length: ${pages.length}`);
|
|
1975
|
-
}
|
|
1976
|
-
new Promise((resolve) => {
|
|
1977
|
-
const { name, ppt } = scenes[pageIndex$.value];
|
|
1978
|
-
redirectResolve = resolve;
|
|
1979
|
-
const _scenes = pages.map((p, index) => {
|
|
1980
|
-
var _a2;
|
|
1981
|
-
return {
|
|
1982
|
-
name: (_a2 = p.name) != null ? _a2 : String(index + 1),
|
|
1983
|
-
ppt: { width: p.width, height: p.height, src: p.src }
|
|
1984
|
-
};
|
|
1985
|
-
});
|
|
1986
|
-
if (!scenesEqual(scenes, _scenes)) {
|
|
1987
|
-
room2.removeScenes(scenePath);
|
|
1988
|
-
room2.putScenes(scenePath, _scenes);
|
|
1989
|
-
}
|
|
1990
|
-
if (name === _scenes[pageIndex$.value].name && !ppt) {
|
|
1991
|
-
context.addPage({ scene: { name: emptySceneName } }).then(() => {
|
|
1992
|
-
log(`[Presentation] setup setScenePath ${scenePath}/${emptySceneName}`);
|
|
1993
|
-
context.setScenePath(`${scenePath}/${emptySceneName}`).then(() => {
|
|
1994
|
-
redirectResolve && redirectResolve(true);
|
|
1995
|
-
});
|
|
1996
|
-
});
|
|
1997
|
-
} else {
|
|
1998
|
-
redirectResolve && redirectResolve(false);
|
|
1999
|
-
}
|
|
2000
|
-
}).then(async (bol) => {
|
|
2001
|
-
await syncPage(pageIndex$.value, room2.logger);
|
|
2002
|
-
if (bol) {
|
|
2003
|
-
log(`[Presentation] setup removeScenes ${scenePath}/${emptySceneName}`);
|
|
2004
|
-
room2.removeScenes(`${scenePath}/${emptySceneName}`);
|
|
2005
|
-
}
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
2103
|
const me = ((_c = context.getRoom()) == null ? void 0 : _c.uid) || context.getDisplayer().observerId + "";
|
|
2010
2104
|
let throttleSyncView = 0;
|
|
2011
2105
|
const syncPage = async (index, logger) => {
|
|
2012
2106
|
var _a2;
|
|
2013
2107
|
if (!context.getIsWritable())
|
|
2014
|
-
return;
|
|
2108
|
+
return false;
|
|
2015
2109
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2016
2110
|
if (!scenes)
|
|
2017
|
-
return;
|
|
2111
|
+
return false;
|
|
2018
2112
|
const p = pages[index];
|
|
2019
2113
|
const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
|
|
2020
2114
|
if (!scenes.some((scene) => scene.name === name)) {
|
|
@@ -2024,57 +2118,149 @@ var NetlessAppPresentation = {
|
|
|
2024
2118
|
logger.info(`[Presentation] syncPage ${scenePath}/${name}`);
|
|
2025
2119
|
}
|
|
2026
2120
|
await context.setScenePath(`${scenePath}/${name}`);
|
|
2121
|
+
return true;
|
|
2027
2122
|
};
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2123
|
+
const prepareScenes = async () => {
|
|
2124
|
+
if (!context.isAddApp)
|
|
2125
|
+
return;
|
|
2126
|
+
if (pages.length > 100)
|
|
2127
|
+
warn(`[Presentation]: too many pages (${pages.length}), may cause performance issues`);
|
|
2128
|
+
if (!room || !room.isWritable)
|
|
2129
|
+
return;
|
|
2130
|
+
if (pageIndex$.value < 0 || pageIndex$.value >= pages.length) {
|
|
2131
|
+
throw new Error(`[Presentation] Invalid page index: ${pageIndex$.value}, scenes length: ${pages.length}`);
|
|
2132
|
+
}
|
|
2133
|
+
const scenes = room.entireScenes()[scenePath];
|
|
2134
|
+
if (!scenes || !scenes[pageIndex$.value]) {
|
|
2135
|
+
throw new Error(`[Presentation]: no initial scene found at ${scenePath}, page index: ${pageIndex$.value}`);
|
|
2136
|
+
}
|
|
2137
|
+
const { name, ppt } = scenes[pageIndex$.value];
|
|
2138
|
+
const nextScenes = pages.map((page, index) => {
|
|
2139
|
+
var _a2;
|
|
2140
|
+
return {
|
|
2141
|
+
name: (_a2 = page.name) != null ? _a2 : String(index + 1),
|
|
2142
|
+
ppt: { width: page.width, height: page.height, src: page.src }
|
|
2143
|
+
};
|
|
2144
|
+
});
|
|
2145
|
+
if (!scenesEqual(scenes, nextScenes)) {
|
|
2146
|
+
room.removeScenes(scenePath);
|
|
2147
|
+
room.putScenes(scenePath, nextScenes);
|
|
2148
|
+
}
|
|
2149
|
+
const shouldRedirect = name === nextScenes[pageIndex$.value].name && !ppt;
|
|
2150
|
+
if (shouldRedirect) {
|
|
2151
|
+
await context.addPage({ scene: { name: emptySceneName } });
|
|
2152
|
+
log(`[Presentation] setup setScenePath ${scenePath}/${emptySceneName}`);
|
|
2153
|
+
await context.setScenePath(`${scenePath}/${emptySceneName}`);
|
|
2154
|
+
}
|
|
2155
|
+
await syncPage(pageIndex$.value, room.logger);
|
|
2156
|
+
if (shouldRedirect) {
|
|
2157
|
+
log(`[Presentation] setup removeScenes ${scenePath}/${emptySceneName}`);
|
|
2158
|
+
room.removeScenes(`${scenePath}/${emptySceneName}`);
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
const prepareScenesPromise = prepareScenes();
|
|
2162
|
+
const canJumpPage = (index) => {
|
|
2030
2163
|
if (!context.getIsWritable()) {
|
|
2031
|
-
|
|
2164
|
+
warn("[Presentation]: no permission, make sure you have test room.isWritable");
|
|
2032
2165
|
return false;
|
|
2033
2166
|
}
|
|
2034
2167
|
if (!(0 <= index && index < pages.length)) {
|
|
2035
|
-
|
|
2168
|
+
warn(`[Presentation]: page ${index + 1} out of bounds [1, ${pages.length}]`);
|
|
2036
2169
|
return false;
|
|
2037
2170
|
}
|
|
2038
2171
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2039
2172
|
if (!scenes) {
|
|
2040
|
-
|
|
2173
|
+
warn(`[Presentation]: no scenes found at ${scenePath}, make sure you have added options.scenePath in addApp()`);
|
|
2041
2174
|
return false;
|
|
2042
2175
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
syncPage(index)
|
|
2176
|
+
return true;
|
|
2177
|
+
};
|
|
2178
|
+
const jumpPage = (index) => {
|
|
2179
|
+
if (!canJumpPage(index))
|
|
2180
|
+
return false;
|
|
2181
|
+
void syncPage(index).catch((error) => {
|
|
2182
|
+
warn("[Presentation]: failed to sync page", error);
|
|
2183
|
+
diagnosticLogger.error("jumpPage.failed", error, {
|
|
2184
|
+
index,
|
|
2185
|
+
pageIndex: pageIndex$.value,
|
|
2186
|
+
focusScenePath: view.focusScenePath
|
|
2187
|
+
});
|
|
2188
|
+
});
|
|
2049
2189
|
return true;
|
|
2050
2190
|
};
|
|
2051
2191
|
const prevPage = () => jumpPage(pageIndex$.value - 1);
|
|
2052
2192
|
const nextPage = () => jumpPage(pageIndex$.value + 1);
|
|
2193
|
+
const jumpPageAsync = async (index) => {
|
|
2194
|
+
if (!canJumpPage(index))
|
|
2195
|
+
return false;
|
|
2196
|
+
try {
|
|
2197
|
+
return await syncPage(index);
|
|
2198
|
+
} catch (error) {
|
|
2199
|
+
diagnosticLogger.error("jumpPageAsync.failed", error, {
|
|
2200
|
+
index,
|
|
2201
|
+
pageIndex: pageIndex$.value,
|
|
2202
|
+
focusScenePath: view.focusScenePath
|
|
2203
|
+
});
|
|
2204
|
+
throw error;
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
const prevPageAsync = () => jumpPageAsync(pageIndex$.value - 1);
|
|
2208
|
+
const nextPageAsync = () => jumpPageAsync(pageIndex$.value + 1);
|
|
2053
2209
|
const pageState = () => ({ index: pageIndex$.value, length: pages.length });
|
|
2054
2210
|
const scaleDocsToFit = () => {
|
|
2055
|
-
const
|
|
2056
|
-
if (
|
|
2211
|
+
const page = app.page();
|
|
2212
|
+
if (page && isValidSize(page)) {
|
|
2213
|
+
const referenceSize = getCameraReferenceSize(originSize, page);
|
|
2214
|
+
if (originSize) {
|
|
2215
|
+
const fitScale = getFitScale(view.size, referenceSize);
|
|
2216
|
+
if (!fitScale)
|
|
2217
|
+
return;
|
|
2218
|
+
const { minScale, maxScale } = getCameraScaleRange(
|
|
2219
|
+
fitScale,
|
|
2220
|
+
maxCameraScale,
|
|
2221
|
+
options.disableCameraTransform
|
|
2222
|
+
);
|
|
2223
|
+
view.setCameraBound({
|
|
2224
|
+
damping: 1,
|
|
2225
|
+
maxContentMode: () => maxScale,
|
|
2226
|
+
minContentMode: () => minScale,
|
|
2227
|
+
centerX: 0,
|
|
2228
|
+
centerY: 0,
|
|
2229
|
+
width: page.width,
|
|
2230
|
+
height: page.height
|
|
2231
|
+
});
|
|
2232
|
+
if (isValidSharedViewport(view$$.state)) {
|
|
2233
|
+
syncViewFromRemote(true);
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2057
2237
|
view.moveCameraToContain({
|
|
2058
|
-
originX: -width / 2,
|
|
2059
|
-
originY: -height / 2,
|
|
2060
|
-
width,
|
|
2061
|
-
height,
|
|
2238
|
+
originX: -referenceSize.width / 2,
|
|
2239
|
+
originY: -referenceSize.height / 2,
|
|
2240
|
+
width: referenceSize.width,
|
|
2241
|
+
height: referenceSize.height,
|
|
2062
2242
|
animationMode: "immediately"
|
|
2063
2243
|
});
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2244
|
+
if (!originSize) {
|
|
2245
|
+
const { minScale, maxScale } = getCameraScaleRange(
|
|
2246
|
+
view.camera.scale,
|
|
2247
|
+
maxCameraScale,
|
|
2248
|
+
options.disableCameraTransform
|
|
2249
|
+
);
|
|
2250
|
+
view.setCameraBound({
|
|
2251
|
+
damping: 1,
|
|
2252
|
+
maxContentMode: () => maxScale,
|
|
2253
|
+
minContentMode: () => minScale,
|
|
2254
|
+
centerX: 0,
|
|
2255
|
+
centerY: 0,
|
|
2256
|
+
width: page.width,
|
|
2257
|
+
height: page.height
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2075
2260
|
syncViewFromRemote(true);
|
|
2076
2261
|
}
|
|
2077
2262
|
};
|
|
2263
|
+
let pendingMoveCameraRequest;
|
|
2078
2264
|
const syncView = () => {
|
|
2079
2265
|
if (context.getIsWritable()) {
|
|
2080
2266
|
if (options.debounceSync) {
|
|
@@ -2083,18 +2269,31 @@ var NetlessAppPresentation = {
|
|
|
2083
2269
|
}
|
|
2084
2270
|
if (throttleSyncView > 0)
|
|
2085
2271
|
return;
|
|
2086
|
-
const
|
|
2087
|
-
if (
|
|
2272
|
+
const page = app.page();
|
|
2273
|
+
if (page && isValidSize(page)) {
|
|
2088
2274
|
throttleSyncView = setTimeout(() => {
|
|
2089
2275
|
throttleSyncView = 0;
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2276
|
+
try {
|
|
2277
|
+
const { camera, size } = view;
|
|
2278
|
+
const referenceSize = getCameraReferenceSize(originSize, page);
|
|
2279
|
+
const viewport = cameraToSharedViewport(camera, size, referenceSize);
|
|
2280
|
+
if (viewport)
|
|
2281
|
+
view$$.setState(__spreadValues({ uid: me }, viewport));
|
|
2282
|
+
if (pendingMoveCameraRequest) {
|
|
2283
|
+
diagnosticLogger.debouncedInfo(
|
|
2284
|
+
"moveCamera",
|
|
2285
|
+
getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
|
|
2286
|
+
);
|
|
2287
|
+
pendingMoveCameraRequest = void 0;
|
|
2288
|
+
}
|
|
2289
|
+
} catch (error) {
|
|
2290
|
+
diagnosticLogger.error(
|
|
2291
|
+
"syncView.failed",
|
|
2292
|
+
error,
|
|
2293
|
+
getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
|
|
2294
|
+
);
|
|
2295
|
+
pendingMoveCameraRequest = void 0;
|
|
2296
|
+
}
|
|
2098
2297
|
}, 50);
|
|
2099
2298
|
}
|
|
2100
2299
|
}
|
|
@@ -2102,6 +2301,7 @@ var NetlessAppPresentation = {
|
|
|
2102
2301
|
dispose2.add(() => {
|
|
2103
2302
|
clearTimeout(throttleSyncView);
|
|
2104
2303
|
throttleSyncView = 0;
|
|
2304
|
+
pendingMoveCameraRequest = void 0;
|
|
2105
2305
|
});
|
|
2106
2306
|
const syncViewFromRemote = (force = false, animate = false) => {
|
|
2107
2307
|
const { uid, originX, originY, width, height } = view$$.state;
|
|
@@ -2122,13 +2322,63 @@ var NetlessAppPresentation = {
|
|
|
2122
2322
|
};
|
|
2123
2323
|
const box = context.getBox();
|
|
2124
2324
|
const app = dispose2.add(createPresentation(box, pages, jumpPage, pageIndex$, view, options.thumbnail, options.useClipView));
|
|
2125
|
-
app.contentDOM.dataset.appPresentationVersion = "0.1.
|
|
2325
|
+
app.contentDOM.dataset.appPresentationVersion = "0.1.11";
|
|
2126
2326
|
app.scaleDocsToFit = scaleDocsToFit;
|
|
2127
2327
|
app.log = log;
|
|
2328
|
+
app.warn = warn;
|
|
2329
|
+
const getCameraDiagnosticState = (reason, requestedCamera) => {
|
|
2330
|
+
const page = app.page();
|
|
2331
|
+
const pageSize = page && isValidSize(page) ? { width: page.width, height: page.height } : void 0;
|
|
2332
|
+
const referenceSize = pageSize ? getCameraReferenceSize(originSize, pageSize) : void 0;
|
|
2333
|
+
const viewSize = { width: view.size.width, height: view.size.height };
|
|
2334
|
+
const viewCamera = {
|
|
2335
|
+
centerX: view.camera.centerX,
|
|
2336
|
+
centerY: view.camera.centerY,
|
|
2337
|
+
scale: view.camera.scale
|
|
2338
|
+
};
|
|
2339
|
+
const sharedViewport = __spreadValues({}, view$$.state);
|
|
2340
|
+
const originScale = referenceSize ? getFitScale(viewSize, referenceSize) : void 0;
|
|
2341
|
+
const normalizedScale = originScale && originScale > 0 ? viewCamera.scale / originScale : void 0;
|
|
2342
|
+
const sharedScaleX = referenceSize && sharedViewport.width > 0 ? referenceSize.width / sharedViewport.width : void 0;
|
|
2343
|
+
const sharedScaleY = referenceSize && sharedViewport.height > 0 ? referenceSize.height / sharedViewport.height : void 0;
|
|
2344
|
+
return {
|
|
2345
|
+
reason,
|
|
2346
|
+
requestedCamera,
|
|
2347
|
+
storageOriginSize: context.storage.state.originSize,
|
|
2348
|
+
sharedViewport,
|
|
2349
|
+
pageSize,
|
|
2350
|
+
referenceSize,
|
|
2351
|
+
viewSize,
|
|
2352
|
+
viewCamera,
|
|
2353
|
+
originScale,
|
|
2354
|
+
normalizedScale,
|
|
2355
|
+
sharedScaleX,
|
|
2356
|
+
sharedScaleY,
|
|
2357
|
+
focusScenePath: view.focusScenePath,
|
|
2358
|
+
isWritable: context.getIsWritable()
|
|
2359
|
+
};
|
|
2360
|
+
};
|
|
2361
|
+
let didReportInitializedCamera = false;
|
|
2362
|
+
const reportInitializedCamera = (source) => {
|
|
2363
|
+
if (didReportInitializedCamera || !isValidSize(view.size) || !isValidSize(app.page()))
|
|
2364
|
+
return;
|
|
2365
|
+
didReportInitializedCamera = true;
|
|
2366
|
+
diagnosticLogger.info("initialize", __spreadValues({
|
|
2367
|
+
source
|
|
2368
|
+
}, getCameraDiagnosticState("initialize")));
|
|
2369
|
+
};
|
|
2370
|
+
if (originSize) {
|
|
2371
|
+
let previousPageIndex = pageIndex$.value;
|
|
2372
|
+
dispose2.add(pageIndex$.subscribe((nextPageIndex) => {
|
|
2373
|
+
if (nextPageIndex === previousPageIndex)
|
|
2374
|
+
return;
|
|
2375
|
+
previousPageIndex = nextPageIndex;
|
|
2376
|
+
scaleDocsToFit();
|
|
2377
|
+
}));
|
|
2378
|
+
}
|
|
2128
2379
|
if (options.justDocsViewReadonly) {
|
|
2129
2380
|
app.setDocsViewReadonly(true);
|
|
2130
2381
|
}
|
|
2131
|
-
const room = context.getRoom();
|
|
2132
2382
|
const goToPageByClick = () => {
|
|
2133
2383
|
var _a2, _b2, _c2, _d;
|
|
2134
2384
|
const currentApplianceName = (_d = (_c2 = (_b2 = (_a2 = context.getRoom()) == null ? void 0 : _a2.state) == null ? void 0 : _b2.memberState) == null ? void 0 : _c2.currentApplianceName) != null ? _d : "";
|
|
@@ -2143,13 +2393,17 @@ var NetlessAppPresentation = {
|
|
|
2143
2393
|
});
|
|
2144
2394
|
}
|
|
2145
2395
|
context.mountView(app.whiteboardDOM);
|
|
2146
|
-
if (options
|
|
2396
|
+
if (shouldDisableDeviceCameraTransform(options)) {
|
|
2147
2397
|
view.disableCameraTransform = true;
|
|
2148
2398
|
}
|
|
2149
2399
|
scaleDocsToFit();
|
|
2150
2400
|
dispose2.make(() => {
|
|
2151
|
-
|
|
2152
|
-
|
|
2401
|
+
const onSizeUpdated = () => {
|
|
2402
|
+
scaleDocsToFit();
|
|
2403
|
+
reportInitializedCamera("onSizeUpdated");
|
|
2404
|
+
};
|
|
2405
|
+
view.callbacks.on("onSizeUpdated", onSizeUpdated);
|
|
2406
|
+
return () => view.callbacks.off("onSizeUpdated", onSizeUpdated);
|
|
2153
2407
|
});
|
|
2154
2408
|
if (options.viewport && context.isAddApp && app.page()) {
|
|
2155
2409
|
const page = app.page();
|
|
@@ -2168,20 +2422,20 @@ var NetlessAppPresentation = {
|
|
|
2168
2422
|
return () => view.callbacks.off("onCameraUpdatedByDevice", syncView);
|
|
2169
2423
|
});
|
|
2170
2424
|
syncViewFromRemote(true);
|
|
2425
|
+
reportInitializedCamera("setup");
|
|
2171
2426
|
dispose2.add(context.emitter.on("writableChange", (isWritable) => {
|
|
2172
2427
|
app.setReadonly(!isWritable);
|
|
2173
2428
|
}));
|
|
2174
2429
|
const getOriginScale = () => {
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
|
|
2430
|
+
const page = app.page();
|
|
2431
|
+
if (!page || !isValidSize(page))
|
|
2432
|
+
return 0;
|
|
2433
|
+
return getFitScale(view.size, getCameraReferenceSize(originSize, page)) || 0;
|
|
2178
2434
|
};
|
|
2179
2435
|
const getScale = () => {
|
|
2180
2436
|
return view.camera.scale;
|
|
2181
2437
|
};
|
|
2182
|
-
const
|
|
2183
|
-
var _a2, _b2;
|
|
2184
|
-
(_b2 = (_a2 = room == null ? void 0 : room.calibrationTimestamp) == null ? void 0 : _a2.toString()) != null ? _b2 : Date.now().toString();
|
|
2438
|
+
const screenshotCurrentPage = async (_context, _width, _height) => {
|
|
2185
2439
|
const currentPage = pages[pageIndex$.value];
|
|
2186
2440
|
if (!currentPage) {
|
|
2187
2441
|
throw new Error("[Presentation]: current page not found");
|
|
@@ -2191,8 +2445,11 @@ var NetlessAppPresentation = {
|
|
|
2191
2445
|
img.width = width;
|
|
2192
2446
|
img.height = height;
|
|
2193
2447
|
img.crossOrigin = "Anonymous";
|
|
2194
|
-
await new Promise((resolve) => {
|
|
2195
|
-
img.onload = resolve;
|
|
2448
|
+
await new Promise((resolve, reject) => {
|
|
2449
|
+
img.onload = () => resolve();
|
|
2450
|
+
img.onerror = () => reject(new Error(
|
|
2451
|
+
`[Presentation]: failed to load screenshot page image: ${safeResourceLocation(src)}`
|
|
2452
|
+
));
|
|
2196
2453
|
img.src = src;
|
|
2197
2454
|
});
|
|
2198
2455
|
_context.drawImage(img, 0, 0, width, height, 0, 0, _width || width, _height || height);
|
|
@@ -2215,6 +2472,18 @@ var NetlessAppPresentation = {
|
|
|
2215
2472
|
});
|
|
2216
2473
|
}
|
|
2217
2474
|
};
|
|
2475
|
+
const screenshotCurrentPageAsync = async (_context, _width, _height) => {
|
|
2476
|
+
try {
|
|
2477
|
+
await screenshotCurrentPage(_context, _width, _height);
|
|
2478
|
+
} catch (error) {
|
|
2479
|
+
diagnosticLogger.error("screenshotCurrentPage.failed", error, {
|
|
2480
|
+
pageIndex: pageIndex$.value,
|
|
2481
|
+
outputSize: { width: _width, height: _height },
|
|
2482
|
+
camera: getCameraDiagnosticState("initialize")
|
|
2483
|
+
});
|
|
2484
|
+
throw error;
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2218
2487
|
let scrollbar;
|
|
2219
2488
|
if (options.useScrollbar) {
|
|
2220
2489
|
dispose2.make(() => {
|
|
@@ -2230,14 +2499,28 @@ var NetlessAppPresentation = {
|
|
|
2230
2499
|
});
|
|
2231
2500
|
}
|
|
2232
2501
|
const moveCamera = (camera) => {
|
|
2233
|
-
|
|
2234
|
-
if (!
|
|
2235
|
-
throw new Error("[Presentation]: moveCamera must be called
|
|
2502
|
+
try {
|
|
2503
|
+
if (!context.getIsWritable()) {
|
|
2504
|
+
throw new Error("[Presentation]: moveCamera must be called in writable room");
|
|
2236
2505
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2506
|
+
pendingMoveCameraRequest = __spreadValues({}, camera);
|
|
2507
|
+
if (scrollbar) {
|
|
2508
|
+
scrollbar.moveCamera(camera);
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
view.moveCamera(__spreadProps(__spreadValues({}, camera), {
|
|
2512
|
+
animationMode: "immediately"
|
|
2513
|
+
}));
|
|
2514
|
+
syncView();
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
pendingMoveCameraRequest = void 0;
|
|
2517
|
+
diagnosticLogger.error(
|
|
2518
|
+
"moveCamera.failed",
|
|
2519
|
+
error,
|
|
2520
|
+
getCameraDiagnosticState("moveCamera", camera)
|
|
2521
|
+
);
|
|
2522
|
+
throw error;
|
|
2239
2523
|
}
|
|
2240
|
-
throw new Error("[Presentation]: moveCamera must be called in writable room");
|
|
2241
2524
|
};
|
|
2242
2525
|
context.emitter.on("destroy", () => dispose2());
|
|
2243
2526
|
const reportProgress = (progress, result) => {
|
|
@@ -2252,8 +2535,11 @@ var NetlessAppPresentation = {
|
|
|
2252
2535
|
} catch (e) {
|
|
2253
2536
|
}
|
|
2254
2537
|
const data = await fetch(url);
|
|
2255
|
-
if (!data.ok)
|
|
2256
|
-
throw new Error(
|
|
2538
|
+
if (!data.ok) {
|
|
2539
|
+
throw new Error(
|
|
2540
|
+
`[Presentation]: failed to fetch ${safeResourceLocation(url)}, status: ${data.status} ${data.statusText}`
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2257
2543
|
const blob = await data.blob();
|
|
2258
2544
|
const reader = new FileReader();
|
|
2259
2545
|
return new Promise((resolve, reject) => {
|
|
@@ -2262,7 +2548,7 @@ var NetlessAppPresentation = {
|
|
|
2262
2548
|
reader.readAsDataURL(blob);
|
|
2263
2549
|
});
|
|
2264
2550
|
};
|
|
2265
|
-
const
|
|
2551
|
+
const toPdfInternal = async () => {
|
|
2266
2552
|
var _a2;
|
|
2267
2553
|
const MAX = 1920;
|
|
2268
2554
|
const firstPage = pages[0];
|
|
@@ -2277,6 +2563,9 @@ var NetlessAppPresentation = {
|
|
|
2277
2563
|
pdfWidth = Math.floor(width * pdfHeight / height);
|
|
2278
2564
|
}
|
|
2279
2565
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2566
|
+
if (!scenes) {
|
|
2567
|
+
throw new Error(`[Presentation]: no scenes found while exporting PDF: ${scenePath}`);
|
|
2568
|
+
}
|
|
2280
2569
|
const stage_canvas = document.createElement("canvas");
|
|
2281
2570
|
stage_canvas.width = pdfWidth;
|
|
2282
2571
|
stage_canvas.height = pdfHeight;
|
|
@@ -2298,11 +2587,14 @@ var NetlessAppPresentation = {
|
|
|
2298
2587
|
const { width: width2, height: height2, src } = p;
|
|
2299
2588
|
const url = await base64url(src);
|
|
2300
2589
|
const img = document.createElement("img");
|
|
2301
|
-
await new Promise((resolve) => {
|
|
2302
|
-
img.onload = resolve;
|
|
2590
|
+
await new Promise((resolve, reject) => {
|
|
2591
|
+
img.onload = () => resolve();
|
|
2592
|
+
img.onerror = () => reject(new Error(
|
|
2593
|
+
`[Presentation]: failed to load PDF page image, page index: ${index}`
|
|
2594
|
+
));
|
|
2303
2595
|
img.src = url;
|
|
2304
2596
|
});
|
|
2305
|
-
stage.drawImage(img, 0, 0);
|
|
2597
|
+
stage.drawImage(img, 0, 0, width2, height2);
|
|
2306
2598
|
wb.clearRect(0, 0, pdfWidth, pdfHeight);
|
|
2307
2599
|
const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
|
|
2308
2600
|
if (scenes.some((scene) => scene.name == name)) {
|
|
@@ -2329,7 +2621,7 @@ var NetlessAppPresentation = {
|
|
|
2329
2621
|
});
|
|
2330
2622
|
stage.drawImage(wb_img, 0, 0, pdfWidth, pdfHeight);
|
|
2331
2623
|
} catch (err) {
|
|
2332
|
-
|
|
2624
|
+
warn(err);
|
|
2333
2625
|
}
|
|
2334
2626
|
}
|
|
2335
2627
|
const output = stage_canvas.toDataURL("image/jpeg", 0.6);
|
|
@@ -2345,10 +2637,22 @@ var NetlessAppPresentation = {
|
|
|
2345
2637
|
const title = box.title;
|
|
2346
2638
|
return reportProgress(100, { pdf: data, title });
|
|
2347
2639
|
};
|
|
2640
|
+
const toPdf = async () => {
|
|
2641
|
+
try {
|
|
2642
|
+
return await toPdfInternal();
|
|
2643
|
+
} catch (error) {
|
|
2644
|
+
diagnosticLogger.error("toPdf.failed", error, {
|
|
2645
|
+
pageIndex: pageIndex$.value,
|
|
2646
|
+
pageCount: pages.length,
|
|
2647
|
+
focusScenePath: view.focusScenePath
|
|
2648
|
+
});
|
|
2649
|
+
throw error;
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2348
2652
|
dispose2.add(listen(window, "message", (ev) => {
|
|
2349
2653
|
if (ev.data && ev.data.type == "@netless/_request_save_pdf_" && ev.data.appId == context.appId) {
|
|
2350
2654
|
toPdf().catch((err) => {
|
|
2351
|
-
|
|
2655
|
+
warn(err);
|
|
2352
2656
|
reportProgress(100, null);
|
|
2353
2657
|
});
|
|
2354
2658
|
}
|
|
@@ -2363,7 +2667,7 @@ var NetlessAppPresentation = {
|
|
|
2363
2667
|
scrollbar.setReadonly(bol);
|
|
2364
2668
|
}
|
|
2365
2669
|
};
|
|
2366
|
-
const controller = { app, view, context, jumpPage, prevPage, nextPage, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale, getScale, getPageSize, screenshotCurrentPageAsync };
|
|
2670
|
+
const controller = { app, view, context, jumpPage, prevPage, nextPage, jumpPageAsync, prevPageAsync, nextPageAsync, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale, getScale, getPageSize, screenshotCurrentPageAsync };
|
|
2367
2671
|
dispose2.add(listen(window, "message", (ev) => {
|
|
2368
2672
|
if (ev.data === "@netless/_presentation_") {
|
|
2369
2673
|
if (typeof window !== "undefined")
|
|
@@ -2375,7 +2679,7 @@ var NetlessAppPresentation = {
|
|
|
2375
2679
|
console.log(controller);
|
|
2376
2680
|
}
|
|
2377
2681
|
}));
|
|
2378
|
-
return controller;
|
|
2682
|
+
return prepareScenesPromise.then(() => controller);
|
|
2379
2683
|
}
|
|
2380
2684
|
};
|
|
2381
2685
|
var AppPresentation = class extends Presentation {
|
|
@@ -2387,6 +2691,7 @@ var AppPresentation = class extends Presentation {
|
|
|
2387
2691
|
super.updateImage();
|
|
2388
2692
|
}
|
|
2389
2693
|
onNewPageIndex(index, origin) {
|
|
2694
|
+
var _a;
|
|
2390
2695
|
if (origin === "keydown" && this.box && !this.box.focus)
|
|
2391
2696
|
return;
|
|
2392
2697
|
if (this.log)
|
|
@@ -2394,7 +2699,7 @@ var AppPresentation = class extends Presentation {
|
|
|
2394
2699
|
if (0 <= index && index < this.pages.length) {
|
|
2395
2700
|
this.jumpPage(index);
|
|
2396
2701
|
} else {
|
|
2397
|
-
|
|
2702
|
+
(_a = this.warn) == null ? void 0 : _a.call(this, `[Presentation]: page index ${index} out of bounds [0, ${this.pages.length - 1}]`);
|
|
2398
2703
|
}
|
|
2399
2704
|
}
|
|
2400
2705
|
};
|
|
@@ -2441,7 +2746,7 @@ var install = (register, options = {}) => {
|
|
|
2441
2746
|
};
|
|
2442
2747
|
|
|
2443
2748
|
// src/index.ts
|
|
2444
|
-
var version = "0.1.
|
|
2749
|
+
var version = "0.1.11";
|
|
2445
2750
|
|
|
2446
2751
|
exports.NetlessAppPresentation = NetlessAppPresentation;
|
|
2447
2752
|
exports.Presentation = Presentation;
|