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