@netless/app-presentation 0.1.10 → 0.1.12
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 +17 -5
- package/dist/index.global.js +415 -119
- package/dist/index.js +415 -119
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +415 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/app-presentation.ts +409 -117
- package/src/camera-options.ts +19 -0
- package/src/camera-reference.ts +81 -0
package/dist/index.mjs
CHANGED
|
@@ -1880,10 +1880,79 @@ 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 ppt2page = (ppt, name) =>
|
|
1950
|
+
var ppt2page = (ppt, name, originSize) => {
|
|
1951
|
+
if (!ppt)
|
|
1952
|
+
return null;
|
|
1953
|
+
const size = fitPageSizeToOrigin(ppt, originSize);
|
|
1954
|
+
return __spreadProps(__spreadValues({}, size), { src: ppt.src, thumbnail: ppt.previewURL, name });
|
|
1955
|
+
};
|
|
1887
1956
|
var createLogger = (room) => {
|
|
1888
1957
|
if (room && room.logger) {
|
|
1889
1958
|
return (...args) => room.logger.info(...args);
|
|
@@ -1891,6 +1960,55 @@ var createLogger = (room) => {
|
|
|
1891
1960
|
return (...args) => console.log(...args);
|
|
1892
1961
|
}
|
|
1893
1962
|
};
|
|
1963
|
+
var createDiagnosticLogger = (context) => {
|
|
1964
|
+
var _a;
|
|
1965
|
+
const createAppLogger = context.createLogger;
|
|
1966
|
+
if (typeof createAppLogger === "function") {
|
|
1967
|
+
return createAppLogger.call(context, "camera", { debounceTime: 300, maxWaitTime: 2e3 });
|
|
1968
|
+
}
|
|
1969
|
+
const roomLogger = (_a = context.getRoom()) == null ? void 0 : _a.logger;
|
|
1970
|
+
const prefix = `[Presentation][${context.appId}][camera]`;
|
|
1971
|
+
const emit = (level, event, ...data) => {
|
|
1972
|
+
try {
|
|
1973
|
+
const printer = roomLogger == null ? void 0 : roomLogger[level];
|
|
1974
|
+
if (typeof printer === "function")
|
|
1975
|
+
printer.call(roomLogger, `${prefix}[${event}]`, ...data);
|
|
1976
|
+
} catch (e) {
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
const debouncedByEvent = /* @__PURE__ */ new Map();
|
|
1980
|
+
const debouncedInfo = (event, payload) => {
|
|
1981
|
+
let emitDebounced = debouncedByEvent.get(event);
|
|
1982
|
+
if (!emitDebounced) {
|
|
1983
|
+
emitDebounced = (0, import_debounce2.default)(
|
|
1984
|
+
(nextPayload) => emit("info", event, nextPayload),
|
|
1985
|
+
300,
|
|
1986
|
+
{ maxWait: 2e3 }
|
|
1987
|
+
);
|
|
1988
|
+
debouncedByEvent.set(event, emitDebounced);
|
|
1989
|
+
}
|
|
1990
|
+
emitDebounced(payload);
|
|
1991
|
+
};
|
|
1992
|
+
return {
|
|
1993
|
+
info: (event, payload) => emit("info", event, payload),
|
|
1994
|
+
warn: (event, payload) => emit("warn", event, payload),
|
|
1995
|
+
error: (event, error, payload) => emit("error", event, error, payload),
|
|
1996
|
+
debouncedInfo,
|
|
1997
|
+
flush: () => debouncedByEvent.forEach((logger) => logger.flush())
|
|
1998
|
+
};
|
|
1999
|
+
};
|
|
2000
|
+
var safeResourceLocation = (value) => {
|
|
2001
|
+
if (value.startsWith("data:"))
|
|
2002
|
+
return "data:[omitted]";
|
|
2003
|
+
if (value.startsWith("blob:"))
|
|
2004
|
+
return "blob:[omitted]";
|
|
2005
|
+
try {
|
|
2006
|
+
const url = new URL(value);
|
|
2007
|
+
return `${url.origin}${url.pathname}`;
|
|
2008
|
+
} catch (e) {
|
|
2009
|
+
return value.split("?")[0];
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
1894
2012
|
var scenesEqual = (scenes1, scenes2) => {
|
|
1895
2013
|
if (!scenes1 || !scenes2) {
|
|
1896
2014
|
return false;
|
|
@@ -1907,25 +2025,35 @@ var NetlessAppPresentation = {
|
|
|
1907
2025
|
kind: "Presentation",
|
|
1908
2026
|
setup(context) {
|
|
1909
2027
|
var _a, _b, _c;
|
|
2028
|
+
const diagnosticLogger = createDiagnosticLogger(context);
|
|
1910
2029
|
const view = context.getView();
|
|
1911
2030
|
if (!view)
|
|
1912
2031
|
throw new Error("[Presentation]: no whiteboard view, make sure you have added options.scenePath in addApp()");
|
|
1913
|
-
const
|
|
2032
|
+
const options = context.getAppOptions() || {};
|
|
2033
|
+
const room = context.getRoom();
|
|
2034
|
+
const log = options.log || createLogger(room);
|
|
2035
|
+
const roomLogger = room == null ? void 0 : room.logger;
|
|
2036
|
+
const warn = (...data) => (roomLogger == null ? void 0 : roomLogger.warn) ? roomLogger.warn(...data) : log(...data);
|
|
2037
|
+
const configuredOriginSize = context.storage.state.originSize;
|
|
2038
|
+
const originSize = isValidSize(configuredOriginSize) ? { width: configuredOriginSize.width, height: configuredOriginSize.height } : void 0;
|
|
2039
|
+
if (configuredOriginSize != null && !originSize) {
|
|
2040
|
+
warn(`[Presentation] originSize should contain finite positive width and height, got ${JSON.stringify(configuredOriginSize)}`);
|
|
2041
|
+
}
|
|
2042
|
+
const pages = (_a = context.getScenes()) == null ? void 0 : _a.map(({ ppt, name }) => ppt2page(ppt, name, originSize)).filter(Boolean);
|
|
1914
2043
|
if (!pages || pages.length === 0)
|
|
1915
2044
|
throw new Error("[Presentation]: empty scenes, make sure you have added options.scenes in addApp()");
|
|
1916
2045
|
if (pages[0].src.startsWith("ppt"))
|
|
1917
2046
|
throw new Error("[Presentation]: legacy dynamic PPT is unsupported, please use the projector converter and @netless/slide to render it");
|
|
1918
2047
|
const scenePath = context.getInitScenePath();
|
|
1919
|
-
const options = context.getAppOptions() || {};
|
|
1920
2048
|
let maxCameraScale = (_b = options.maxCameraScale) != null ? _b : 3;
|
|
1921
2049
|
if (!(Number.isFinite(maxCameraScale) && maxCameraScale > 0)) {
|
|
1922
|
-
|
|
2050
|
+
warn(`[Presentation] maxCameraScale should be a positive number, got ${options.maxCameraScale}`);
|
|
1923
2051
|
maxCameraScale = 3;
|
|
1924
2052
|
}
|
|
1925
|
-
const log = options.log || createLogger(context.getRoom());
|
|
1926
2053
|
log(`[Presentation] new ${context.appId}`);
|
|
1927
2054
|
const dispose2 = disposableStore();
|
|
1928
2055
|
dispose2.add(() => log(`[Presentation] dispose ${context.appId}`));
|
|
2056
|
+
dispose2.add(() => diagnosticLogger.flush());
|
|
1929
2057
|
const view$$ = context.createStorage("view", { uid: "", originX: 0, originY: 0, width: 0, height: 0 });
|
|
1930
2058
|
const _addScenePathListener = (name, listener) => {
|
|
1931
2059
|
const windowManger = context.manager.windowManger;
|
|
@@ -1959,58 +2087,15 @@ var NetlessAppPresentation = {
|
|
|
1959
2087
|
dispose2.add(() => {
|
|
1960
2088
|
pageIndex$.dispose();
|
|
1961
2089
|
});
|
|
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
2090
|
const me = ((_c = context.getRoom()) == null ? void 0 : _c.uid) || context.getDisplayer().observerId + "";
|
|
2006
2091
|
let throttleSyncView = 0;
|
|
2007
2092
|
const syncPage = async (index, logger) => {
|
|
2008
2093
|
var _a2;
|
|
2009
2094
|
if (!context.getIsWritable())
|
|
2010
|
-
return;
|
|
2095
|
+
return false;
|
|
2011
2096
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2012
2097
|
if (!scenes)
|
|
2013
|
-
return;
|
|
2098
|
+
return false;
|
|
2014
2099
|
const p = pages[index];
|
|
2015
2100
|
const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
|
|
2016
2101
|
if (!scenes.some((scene) => scene.name === name)) {
|
|
@@ -2020,57 +2105,149 @@ var NetlessAppPresentation = {
|
|
|
2020
2105
|
logger.info(`[Presentation] syncPage ${scenePath}/${name}`);
|
|
2021
2106
|
}
|
|
2022
2107
|
await context.setScenePath(`${scenePath}/${name}`);
|
|
2108
|
+
return true;
|
|
2023
2109
|
};
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2110
|
+
const prepareScenes = async () => {
|
|
2111
|
+
if (!context.isAddApp)
|
|
2112
|
+
return;
|
|
2113
|
+
if (pages.length > 100)
|
|
2114
|
+
warn(`[Presentation]: too many pages (${pages.length}), may cause performance issues`);
|
|
2115
|
+
if (!room || !room.isWritable)
|
|
2116
|
+
return;
|
|
2117
|
+
if (pageIndex$.value < 0 || pageIndex$.value >= pages.length) {
|
|
2118
|
+
throw new Error(`[Presentation] Invalid page index: ${pageIndex$.value}, scenes length: ${pages.length}`);
|
|
2119
|
+
}
|
|
2120
|
+
const scenes = room.entireScenes()[scenePath];
|
|
2121
|
+
if (!scenes || !scenes[pageIndex$.value]) {
|
|
2122
|
+
throw new Error(`[Presentation]: no initial scene found at ${scenePath}, page index: ${pageIndex$.value}`);
|
|
2123
|
+
}
|
|
2124
|
+
const { name, ppt } = scenes[pageIndex$.value];
|
|
2125
|
+
const nextScenes = pages.map((page, index) => {
|
|
2126
|
+
var _a2;
|
|
2127
|
+
return {
|
|
2128
|
+
name: (_a2 = page.name) != null ? _a2 : String(index + 1),
|
|
2129
|
+
ppt: { width: page.width, height: page.height, src: page.src }
|
|
2130
|
+
};
|
|
2131
|
+
});
|
|
2132
|
+
if (!scenesEqual(scenes, nextScenes)) {
|
|
2133
|
+
room.removeScenes(scenePath);
|
|
2134
|
+
room.putScenes(scenePath, nextScenes);
|
|
2135
|
+
}
|
|
2136
|
+
const shouldRedirect = name === nextScenes[pageIndex$.value].name && !ppt;
|
|
2137
|
+
if (shouldRedirect) {
|
|
2138
|
+
await context.addPage({ scene: { name: emptySceneName } });
|
|
2139
|
+
log(`[Presentation] setup setScenePath ${scenePath}/${emptySceneName}`);
|
|
2140
|
+
await context.setScenePath(`${scenePath}/${emptySceneName}`);
|
|
2141
|
+
}
|
|
2142
|
+
await syncPage(pageIndex$.value, room.logger);
|
|
2143
|
+
if (shouldRedirect) {
|
|
2144
|
+
log(`[Presentation] setup removeScenes ${scenePath}/${emptySceneName}`);
|
|
2145
|
+
room.removeScenes(`${scenePath}/${emptySceneName}`);
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
const prepareScenesPromise = prepareScenes();
|
|
2149
|
+
const canJumpPage = (index) => {
|
|
2026
2150
|
if (!context.getIsWritable()) {
|
|
2027
|
-
|
|
2151
|
+
warn("[Presentation]: no permission, make sure you have test room.isWritable");
|
|
2028
2152
|
return false;
|
|
2029
2153
|
}
|
|
2030
2154
|
if (!(0 <= index && index < pages.length)) {
|
|
2031
|
-
|
|
2155
|
+
warn(`[Presentation]: page ${index + 1} out of bounds [1, ${pages.length}]`);
|
|
2032
2156
|
return false;
|
|
2033
2157
|
}
|
|
2034
2158
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2035
2159
|
if (!scenes) {
|
|
2036
|
-
|
|
2160
|
+
warn(`[Presentation]: no scenes found at ${scenePath}, make sure you have added options.scenePath in addApp()`);
|
|
2037
2161
|
return false;
|
|
2038
2162
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
syncPage(index)
|
|
2163
|
+
return true;
|
|
2164
|
+
};
|
|
2165
|
+
const jumpPage = (index) => {
|
|
2166
|
+
if (!canJumpPage(index))
|
|
2167
|
+
return false;
|
|
2168
|
+
void syncPage(index).catch((error) => {
|
|
2169
|
+
warn("[Presentation]: failed to sync page", error);
|
|
2170
|
+
diagnosticLogger.error("jumpPage.failed", error, {
|
|
2171
|
+
index,
|
|
2172
|
+
pageIndex: pageIndex$.value,
|
|
2173
|
+
focusScenePath: view.focusScenePath
|
|
2174
|
+
});
|
|
2175
|
+
});
|
|
2045
2176
|
return true;
|
|
2046
2177
|
};
|
|
2047
2178
|
const prevPage = () => jumpPage(pageIndex$.value - 1);
|
|
2048
2179
|
const nextPage = () => jumpPage(pageIndex$.value + 1);
|
|
2180
|
+
const jumpPageAsync = async (index) => {
|
|
2181
|
+
if (!canJumpPage(index))
|
|
2182
|
+
return false;
|
|
2183
|
+
try {
|
|
2184
|
+
return await syncPage(index);
|
|
2185
|
+
} catch (error) {
|
|
2186
|
+
diagnosticLogger.error("jumpPageAsync.failed", error, {
|
|
2187
|
+
index,
|
|
2188
|
+
pageIndex: pageIndex$.value,
|
|
2189
|
+
focusScenePath: view.focusScenePath
|
|
2190
|
+
});
|
|
2191
|
+
throw error;
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
const prevPageAsync = () => jumpPageAsync(pageIndex$.value - 1);
|
|
2195
|
+
const nextPageAsync = () => jumpPageAsync(pageIndex$.value + 1);
|
|
2049
2196
|
const pageState = () => ({ index: pageIndex$.value, length: pages.length });
|
|
2050
2197
|
const scaleDocsToFit = () => {
|
|
2051
|
-
const
|
|
2052
|
-
if (
|
|
2198
|
+
const page = app.page();
|
|
2199
|
+
if (page && isValidSize(page)) {
|
|
2200
|
+
const referenceSize = getCameraReferenceSize(originSize, page);
|
|
2201
|
+
if (originSize) {
|
|
2202
|
+
const fitScale = getFitScale(view.size, referenceSize);
|
|
2203
|
+
if (!fitScale)
|
|
2204
|
+
return;
|
|
2205
|
+
const { minScale, maxScale } = getCameraScaleRange(
|
|
2206
|
+
fitScale,
|
|
2207
|
+
maxCameraScale,
|
|
2208
|
+
options.disableCameraTransform
|
|
2209
|
+
);
|
|
2210
|
+
view.setCameraBound({
|
|
2211
|
+
damping: 1,
|
|
2212
|
+
maxContentMode: () => maxScale,
|
|
2213
|
+
minContentMode: () => minScale,
|
|
2214
|
+
centerX: 0,
|
|
2215
|
+
centerY: 0,
|
|
2216
|
+
width: page.width,
|
|
2217
|
+
height: page.height
|
|
2218
|
+
});
|
|
2219
|
+
if (isValidSharedViewport(view$$.state)) {
|
|
2220
|
+
syncViewFromRemote(true);
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2053
2224
|
view.moveCameraToContain({
|
|
2054
|
-
originX: -width / 2,
|
|
2055
|
-
originY: -height / 2,
|
|
2056
|
-
width,
|
|
2057
|
-
height,
|
|
2225
|
+
originX: -referenceSize.width / 2,
|
|
2226
|
+
originY: -referenceSize.height / 2,
|
|
2227
|
+
width: referenceSize.width,
|
|
2228
|
+
height: referenceSize.height,
|
|
2058
2229
|
animationMode: "immediately"
|
|
2059
2230
|
});
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2231
|
+
if (!originSize) {
|
|
2232
|
+
const { minScale, maxScale } = getCameraScaleRange(
|
|
2233
|
+
view.camera.scale,
|
|
2234
|
+
maxCameraScale,
|
|
2235
|
+
options.disableCameraTransform
|
|
2236
|
+
);
|
|
2237
|
+
view.setCameraBound({
|
|
2238
|
+
damping: 1,
|
|
2239
|
+
maxContentMode: () => maxScale,
|
|
2240
|
+
minContentMode: () => minScale,
|
|
2241
|
+
centerX: 0,
|
|
2242
|
+
centerY: 0,
|
|
2243
|
+
width: page.width,
|
|
2244
|
+
height: page.height
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2071
2247
|
syncViewFromRemote(true);
|
|
2072
2248
|
}
|
|
2073
2249
|
};
|
|
2250
|
+
let pendingMoveCameraRequest;
|
|
2074
2251
|
const syncView = () => {
|
|
2075
2252
|
if (context.getIsWritable()) {
|
|
2076
2253
|
if (options.debounceSync) {
|
|
@@ -2079,18 +2256,31 @@ var NetlessAppPresentation = {
|
|
|
2079
2256
|
}
|
|
2080
2257
|
if (throttleSyncView > 0)
|
|
2081
2258
|
return;
|
|
2082
|
-
const
|
|
2083
|
-
if (
|
|
2259
|
+
const page = app.page();
|
|
2260
|
+
if (page && isValidSize(page)) {
|
|
2084
2261
|
throttleSyncView = setTimeout(() => {
|
|
2085
2262
|
throttleSyncView = 0;
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2263
|
+
try {
|
|
2264
|
+
const { camera, size } = view;
|
|
2265
|
+
const referenceSize = getCameraReferenceSize(originSize, page);
|
|
2266
|
+
const viewport = cameraToSharedViewport(camera, size, referenceSize);
|
|
2267
|
+
if (viewport)
|
|
2268
|
+
view$$.setState(__spreadValues({ uid: me }, viewport));
|
|
2269
|
+
if (pendingMoveCameraRequest) {
|
|
2270
|
+
diagnosticLogger.debouncedInfo(
|
|
2271
|
+
"moveCamera",
|
|
2272
|
+
getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
|
|
2273
|
+
);
|
|
2274
|
+
pendingMoveCameraRequest = void 0;
|
|
2275
|
+
}
|
|
2276
|
+
} catch (error) {
|
|
2277
|
+
diagnosticLogger.error(
|
|
2278
|
+
"syncView.failed",
|
|
2279
|
+
error,
|
|
2280
|
+
getCameraDiagnosticState("moveCamera", pendingMoveCameraRequest)
|
|
2281
|
+
);
|
|
2282
|
+
pendingMoveCameraRequest = void 0;
|
|
2283
|
+
}
|
|
2094
2284
|
}, 50);
|
|
2095
2285
|
}
|
|
2096
2286
|
}
|
|
@@ -2098,6 +2288,7 @@ var NetlessAppPresentation = {
|
|
|
2098
2288
|
dispose2.add(() => {
|
|
2099
2289
|
clearTimeout(throttleSyncView);
|
|
2100
2290
|
throttleSyncView = 0;
|
|
2291
|
+
pendingMoveCameraRequest = void 0;
|
|
2101
2292
|
});
|
|
2102
2293
|
const syncViewFromRemote = (force = false, animate = false) => {
|
|
2103
2294
|
const { uid, originX, originY, width, height } = view$$.state;
|
|
@@ -2118,13 +2309,63 @@ var NetlessAppPresentation = {
|
|
|
2118
2309
|
};
|
|
2119
2310
|
const box = context.getBox();
|
|
2120
2311
|
const app = dispose2.add(createPresentation(box, pages, jumpPage, pageIndex$, view, options.thumbnail, options.useClipView));
|
|
2121
|
-
app.contentDOM.dataset.appPresentationVersion = "0.1.
|
|
2312
|
+
app.contentDOM.dataset.appPresentationVersion = "0.1.12";
|
|
2122
2313
|
app.scaleDocsToFit = scaleDocsToFit;
|
|
2123
2314
|
app.log = log;
|
|
2315
|
+
app.warn = warn;
|
|
2316
|
+
const getCameraDiagnosticState = (reason, requestedCamera) => {
|
|
2317
|
+
const page = app.page();
|
|
2318
|
+
const pageSize = page && isValidSize(page) ? { width: page.width, height: page.height } : void 0;
|
|
2319
|
+
const referenceSize = pageSize ? getCameraReferenceSize(originSize, pageSize) : void 0;
|
|
2320
|
+
const viewSize = { width: view.size.width, height: view.size.height };
|
|
2321
|
+
const viewCamera = {
|
|
2322
|
+
centerX: view.camera.centerX,
|
|
2323
|
+
centerY: view.camera.centerY,
|
|
2324
|
+
scale: view.camera.scale
|
|
2325
|
+
};
|
|
2326
|
+
const sharedViewport = __spreadValues({}, view$$.state);
|
|
2327
|
+
const originScale = referenceSize ? getFitScale(viewSize, referenceSize) : void 0;
|
|
2328
|
+
const normalizedScale = originScale && originScale > 0 ? viewCamera.scale / originScale : void 0;
|
|
2329
|
+
const sharedScaleX = referenceSize && sharedViewport.width > 0 ? referenceSize.width / sharedViewport.width : void 0;
|
|
2330
|
+
const sharedScaleY = referenceSize && sharedViewport.height > 0 ? referenceSize.height / sharedViewport.height : void 0;
|
|
2331
|
+
return {
|
|
2332
|
+
reason,
|
|
2333
|
+
requestedCamera,
|
|
2334
|
+
storageOriginSize: context.storage.state.originSize,
|
|
2335
|
+
sharedViewport,
|
|
2336
|
+
pageSize,
|
|
2337
|
+
referenceSize,
|
|
2338
|
+
viewSize,
|
|
2339
|
+
viewCamera,
|
|
2340
|
+
originScale,
|
|
2341
|
+
normalizedScale,
|
|
2342
|
+
sharedScaleX,
|
|
2343
|
+
sharedScaleY,
|
|
2344
|
+
focusScenePath: view.focusScenePath,
|
|
2345
|
+
isWritable: context.getIsWritable()
|
|
2346
|
+
};
|
|
2347
|
+
};
|
|
2348
|
+
let didReportInitializedCamera = false;
|
|
2349
|
+
const reportInitializedCamera = (source) => {
|
|
2350
|
+
if (didReportInitializedCamera || !isValidSize(view.size) || !isValidSize(app.page()))
|
|
2351
|
+
return;
|
|
2352
|
+
didReportInitializedCamera = true;
|
|
2353
|
+
diagnosticLogger.info("initialize", __spreadValues({
|
|
2354
|
+
source
|
|
2355
|
+
}, getCameraDiagnosticState("initialize")));
|
|
2356
|
+
};
|
|
2357
|
+
if (originSize) {
|
|
2358
|
+
let previousPageIndex = pageIndex$.value;
|
|
2359
|
+
dispose2.add(pageIndex$.subscribe((nextPageIndex) => {
|
|
2360
|
+
if (nextPageIndex === previousPageIndex)
|
|
2361
|
+
return;
|
|
2362
|
+
previousPageIndex = nextPageIndex;
|
|
2363
|
+
scaleDocsToFit();
|
|
2364
|
+
}));
|
|
2365
|
+
}
|
|
2124
2366
|
if (options.justDocsViewReadonly) {
|
|
2125
2367
|
app.setDocsViewReadonly(true);
|
|
2126
2368
|
}
|
|
2127
|
-
const room = context.getRoom();
|
|
2128
2369
|
const goToPageByClick = () => {
|
|
2129
2370
|
var _a2, _b2, _c2, _d;
|
|
2130
2371
|
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 +2380,17 @@ var NetlessAppPresentation = {
|
|
|
2139
2380
|
});
|
|
2140
2381
|
}
|
|
2141
2382
|
context.mountView(app.whiteboardDOM);
|
|
2142
|
-
if (options
|
|
2383
|
+
if (shouldDisableDeviceCameraTransform(options)) {
|
|
2143
2384
|
view.disableCameraTransform = true;
|
|
2144
2385
|
}
|
|
2145
2386
|
scaleDocsToFit();
|
|
2146
2387
|
dispose2.make(() => {
|
|
2147
|
-
|
|
2148
|
-
|
|
2388
|
+
const onSizeUpdated = () => {
|
|
2389
|
+
scaleDocsToFit();
|
|
2390
|
+
reportInitializedCamera("onSizeUpdated");
|
|
2391
|
+
};
|
|
2392
|
+
view.callbacks.on("onSizeUpdated", onSizeUpdated);
|
|
2393
|
+
return () => view.callbacks.off("onSizeUpdated", onSizeUpdated);
|
|
2149
2394
|
});
|
|
2150
2395
|
if (options.viewport && context.isAddApp && app.page()) {
|
|
2151
2396
|
const page = app.page();
|
|
@@ -2164,20 +2409,20 @@ var NetlessAppPresentation = {
|
|
|
2164
2409
|
return () => view.callbacks.off("onCameraUpdatedByDevice", syncView);
|
|
2165
2410
|
});
|
|
2166
2411
|
syncViewFromRemote(true);
|
|
2412
|
+
reportInitializedCamera("setup");
|
|
2167
2413
|
dispose2.add(context.emitter.on("writableChange", (isWritable) => {
|
|
2168
2414
|
app.setReadonly(!isWritable);
|
|
2169
2415
|
}));
|
|
2170
2416
|
const getOriginScale = () => {
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
|
|
2417
|
+
const page = app.page();
|
|
2418
|
+
if (!page || !isValidSize(page))
|
|
2419
|
+
return 0;
|
|
2420
|
+
return getFitScale(view.size, getCameraReferenceSize(originSize, page)) || 0;
|
|
2174
2421
|
};
|
|
2175
2422
|
const getScale = () => {
|
|
2176
2423
|
return view.camera.scale;
|
|
2177
2424
|
};
|
|
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();
|
|
2425
|
+
const screenshotCurrentPage = async (_context, _width, _height) => {
|
|
2181
2426
|
const currentPage = pages[pageIndex$.value];
|
|
2182
2427
|
if (!currentPage) {
|
|
2183
2428
|
throw new Error("[Presentation]: current page not found");
|
|
@@ -2187,8 +2432,11 @@ var NetlessAppPresentation = {
|
|
|
2187
2432
|
img.width = width;
|
|
2188
2433
|
img.height = height;
|
|
2189
2434
|
img.crossOrigin = "Anonymous";
|
|
2190
|
-
await new Promise((resolve) => {
|
|
2191
|
-
img.onload = resolve;
|
|
2435
|
+
await new Promise((resolve, reject) => {
|
|
2436
|
+
img.onload = () => resolve();
|
|
2437
|
+
img.onerror = () => reject(new Error(
|
|
2438
|
+
`[Presentation]: failed to load screenshot page image: ${safeResourceLocation(src)}`
|
|
2439
|
+
));
|
|
2192
2440
|
img.src = src;
|
|
2193
2441
|
});
|
|
2194
2442
|
_context.drawImage(img, 0, 0, width, height, 0, 0, _width || width, _height || height);
|
|
@@ -2211,6 +2459,18 @@ var NetlessAppPresentation = {
|
|
|
2211
2459
|
});
|
|
2212
2460
|
}
|
|
2213
2461
|
};
|
|
2462
|
+
const screenshotCurrentPageAsync = async (_context, _width, _height) => {
|
|
2463
|
+
try {
|
|
2464
|
+
await screenshotCurrentPage(_context, _width, _height);
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
diagnosticLogger.error("screenshotCurrentPage.failed", error, {
|
|
2467
|
+
pageIndex: pageIndex$.value,
|
|
2468
|
+
outputSize: { width: _width, height: _height },
|
|
2469
|
+
camera: getCameraDiagnosticState("initialize")
|
|
2470
|
+
});
|
|
2471
|
+
throw error;
|
|
2472
|
+
}
|
|
2473
|
+
};
|
|
2214
2474
|
let scrollbar;
|
|
2215
2475
|
if (options.useScrollbar) {
|
|
2216
2476
|
dispose2.make(() => {
|
|
@@ -2226,14 +2486,28 @@ var NetlessAppPresentation = {
|
|
|
2226
2486
|
});
|
|
2227
2487
|
}
|
|
2228
2488
|
const moveCamera = (camera) => {
|
|
2229
|
-
|
|
2230
|
-
if (!
|
|
2231
|
-
throw new Error("[Presentation]: moveCamera must be called
|
|
2489
|
+
try {
|
|
2490
|
+
if (!context.getIsWritable()) {
|
|
2491
|
+
throw new Error("[Presentation]: moveCamera must be called in writable room");
|
|
2232
2492
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2493
|
+
pendingMoveCameraRequest = __spreadValues({}, camera);
|
|
2494
|
+
if (scrollbar) {
|
|
2495
|
+
scrollbar.moveCamera(camera);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
view.moveCamera(__spreadProps(__spreadValues({}, camera), {
|
|
2499
|
+
animationMode: "immediately"
|
|
2500
|
+
}));
|
|
2501
|
+
syncView();
|
|
2502
|
+
} catch (error) {
|
|
2503
|
+
pendingMoveCameraRequest = void 0;
|
|
2504
|
+
diagnosticLogger.error(
|
|
2505
|
+
"moveCamera.failed",
|
|
2506
|
+
error,
|
|
2507
|
+
getCameraDiagnosticState("moveCamera", camera)
|
|
2508
|
+
);
|
|
2509
|
+
throw error;
|
|
2235
2510
|
}
|
|
2236
|
-
throw new Error("[Presentation]: moveCamera must be called in writable room");
|
|
2237
2511
|
};
|
|
2238
2512
|
context.emitter.on("destroy", () => dispose2());
|
|
2239
2513
|
const reportProgress = (progress, result) => {
|
|
@@ -2248,8 +2522,11 @@ var NetlessAppPresentation = {
|
|
|
2248
2522
|
} catch (e) {
|
|
2249
2523
|
}
|
|
2250
2524
|
const data = await fetch(url);
|
|
2251
|
-
if (!data.ok)
|
|
2252
|
-
throw new Error(
|
|
2525
|
+
if (!data.ok) {
|
|
2526
|
+
throw new Error(
|
|
2527
|
+
`[Presentation]: failed to fetch ${safeResourceLocation(url)}, status: ${data.status} ${data.statusText}`
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2253
2530
|
const blob = await data.blob();
|
|
2254
2531
|
const reader = new FileReader();
|
|
2255
2532
|
return new Promise((resolve, reject) => {
|
|
@@ -2258,7 +2535,7 @@ var NetlessAppPresentation = {
|
|
|
2258
2535
|
reader.readAsDataURL(blob);
|
|
2259
2536
|
});
|
|
2260
2537
|
};
|
|
2261
|
-
const
|
|
2538
|
+
const toPdfInternal = async () => {
|
|
2262
2539
|
var _a2;
|
|
2263
2540
|
const MAX = 1920;
|
|
2264
2541
|
const firstPage = pages[0];
|
|
@@ -2273,6 +2550,9 @@ var NetlessAppPresentation = {
|
|
|
2273
2550
|
pdfWidth = Math.floor(width * pdfHeight / height);
|
|
2274
2551
|
}
|
|
2275
2552
|
const scenes = context.getDisplayer().entireScenes()[scenePath];
|
|
2553
|
+
if (!scenes) {
|
|
2554
|
+
throw new Error(`[Presentation]: no scenes found while exporting PDF: ${scenePath}`);
|
|
2555
|
+
}
|
|
2276
2556
|
const stage_canvas = document.createElement("canvas");
|
|
2277
2557
|
stage_canvas.width = pdfWidth;
|
|
2278
2558
|
stage_canvas.height = pdfHeight;
|
|
@@ -2294,11 +2574,14 @@ var NetlessAppPresentation = {
|
|
|
2294
2574
|
const { width: width2, height: height2, src } = p;
|
|
2295
2575
|
const url = await base64url(src);
|
|
2296
2576
|
const img = document.createElement("img");
|
|
2297
|
-
await new Promise((resolve) => {
|
|
2298
|
-
img.onload = resolve;
|
|
2577
|
+
await new Promise((resolve, reject) => {
|
|
2578
|
+
img.onload = () => resolve();
|
|
2579
|
+
img.onerror = () => reject(new Error(
|
|
2580
|
+
`[Presentation]: failed to load PDF page image, page index: ${index}`
|
|
2581
|
+
));
|
|
2299
2582
|
img.src = url;
|
|
2300
2583
|
});
|
|
2301
|
-
stage.drawImage(img, 0, 0);
|
|
2584
|
+
stage.drawImage(img, 0, 0, width2, height2);
|
|
2302
2585
|
wb.clearRect(0, 0, pdfWidth, pdfHeight);
|
|
2303
2586
|
const name = (_a2 = p.name) != null ? _a2 : String(index + 1);
|
|
2304
2587
|
if (scenes.some((scene) => scene.name == name)) {
|
|
@@ -2325,7 +2608,7 @@ var NetlessAppPresentation = {
|
|
|
2325
2608
|
});
|
|
2326
2609
|
stage.drawImage(wb_img, 0, 0, pdfWidth, pdfHeight);
|
|
2327
2610
|
} catch (err) {
|
|
2328
|
-
|
|
2611
|
+
warn(err);
|
|
2329
2612
|
}
|
|
2330
2613
|
}
|
|
2331
2614
|
const output = stage_canvas.toDataURL("image/jpeg", 0.6);
|
|
@@ -2341,10 +2624,22 @@ var NetlessAppPresentation = {
|
|
|
2341
2624
|
const title = box.title;
|
|
2342
2625
|
return reportProgress(100, { pdf: data, title });
|
|
2343
2626
|
};
|
|
2627
|
+
const toPdf = async () => {
|
|
2628
|
+
try {
|
|
2629
|
+
return await toPdfInternal();
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
diagnosticLogger.error("toPdf.failed", error, {
|
|
2632
|
+
pageIndex: pageIndex$.value,
|
|
2633
|
+
pageCount: pages.length,
|
|
2634
|
+
focusScenePath: view.focusScenePath
|
|
2635
|
+
});
|
|
2636
|
+
throw error;
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2344
2639
|
dispose2.add(listen(window, "message", (ev) => {
|
|
2345
2640
|
if (ev.data && ev.data.type == "@netless/_request_save_pdf_" && ev.data.appId == context.appId) {
|
|
2346
2641
|
toPdf().catch((err) => {
|
|
2347
|
-
|
|
2642
|
+
warn(err);
|
|
2348
2643
|
reportProgress(100, null);
|
|
2349
2644
|
});
|
|
2350
2645
|
}
|
|
@@ -2359,7 +2654,7 @@ var NetlessAppPresentation = {
|
|
|
2359
2654
|
scrollbar.setReadonly(bol);
|
|
2360
2655
|
}
|
|
2361
2656
|
};
|
|
2362
|
-
const controller = { app, view, context, jumpPage, prevPage, nextPage, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale, getScale, getPageSize, screenshotCurrentPageAsync };
|
|
2657
|
+
const controller = { app, view, context, jumpPage, prevPage, nextPage, jumpPageAsync, prevPageAsync, nextPageAsync, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale, getScale, getPageSize, screenshotCurrentPageAsync };
|
|
2363
2658
|
dispose2.add(listen(window, "message", (ev) => {
|
|
2364
2659
|
if (ev.data === "@netless/_presentation_") {
|
|
2365
2660
|
if (typeof window !== "undefined")
|
|
@@ -2371,7 +2666,7 @@ var NetlessAppPresentation = {
|
|
|
2371
2666
|
console.log(controller);
|
|
2372
2667
|
}
|
|
2373
2668
|
}));
|
|
2374
|
-
return controller;
|
|
2669
|
+
return prepareScenesPromise.then(() => controller);
|
|
2375
2670
|
}
|
|
2376
2671
|
};
|
|
2377
2672
|
var AppPresentation = class extends Presentation {
|
|
@@ -2383,6 +2678,7 @@ var AppPresentation = class extends Presentation {
|
|
|
2383
2678
|
super.updateImage();
|
|
2384
2679
|
}
|
|
2385
2680
|
onNewPageIndex(index, origin) {
|
|
2681
|
+
var _a;
|
|
2386
2682
|
if (origin === "keydown" && this.box && !this.box.focus)
|
|
2387
2683
|
return;
|
|
2388
2684
|
if (this.log)
|
|
@@ -2390,7 +2686,7 @@ var AppPresentation = class extends Presentation {
|
|
|
2390
2686
|
if (0 <= index && index < this.pages.length) {
|
|
2391
2687
|
this.jumpPage(index);
|
|
2392
2688
|
} else {
|
|
2393
|
-
|
|
2689
|
+
(_a = this.warn) == null ? void 0 : _a.call(this, `[Presentation]: page index ${index} out of bounds [0, ${this.pages.length - 1}]`);
|
|
2394
2690
|
}
|
|
2395
2691
|
}
|
|
2396
2692
|
};
|
|
@@ -2437,7 +2733,7 @@ var install = (register, options = {}) => {
|
|
|
2437
2733
|
};
|
|
2438
2734
|
|
|
2439
2735
|
// src/index.ts
|
|
2440
|
-
var version = "0.1.
|
|
2736
|
+
var version = "0.1.12";
|
|
2441
2737
|
|
|
2442
2738
|
export { NetlessAppPresentation, Presentation, Scrollbar, NetlessAppPresentation as default, install, version };
|
|
2443
2739
|
//# sourceMappingURL=index.mjs.map
|