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