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