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