@netless/app-slide 0.2.106-beta.4 → 0.2.106
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/dist/index.d.ts +18 -7
- package/dist/main.cjs.js +138 -138
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +429 -240
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +135 -135
- package/dist/main.iife.js.map +1 -1
- package/package.json +2 -2
- package/src/SlideController/index.ts +177 -97
- package/src/SlideDocsViewer/index.ts +20 -12
- package/src/index.ts +48 -37
- package/src/utils/freezer.ts +81 -13
- package/src/utils/logger.ts +35 -19
package/src/utils/freezer.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ReadonlyTeleBox, RegisterParams } from "@netless/window-manager";
|
|
2
|
-
import { log } from "./logger";
|
|
1
|
+
import type { AppContext, ReadonlyTeleBox, RegisterParams } from "@netless/window-manager";
|
|
2
|
+
import { log, logger } from "./logger";
|
|
3
3
|
|
|
4
4
|
export interface FreezableSlide {
|
|
5
|
-
freeze: () => void
|
|
6
|
-
unfreeze: () => void
|
|
5
|
+
freeze: () => void | Promise<void>;
|
|
6
|
+
unfreeze: () => void | Promise<void>;
|
|
7
|
+
context?: AppContext<any, any, any>;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export let useFreezer = false;
|
|
@@ -22,54 +23,121 @@ const inspect = (arr: string[]) => {
|
|
|
22
23
|
return "[" + arr + "]";
|
|
23
24
|
};
|
|
24
25
|
|
|
26
|
+
const FreezerRetryDelay = 500;
|
|
27
|
+
|
|
25
28
|
export const apps = {
|
|
26
29
|
map: new Map<string, FreezableSlide>(),
|
|
27
30
|
boxes: new Map<string, ReadonlyTeleBox>(),
|
|
28
31
|
queue: [] as string[],
|
|
29
|
-
|
|
32
|
+
focusedAppId: undefined as string | undefined,
|
|
33
|
+
runTransition(
|
|
34
|
+
appId: string,
|
|
35
|
+
slide: FreezableSlide,
|
|
36
|
+
action: "freeze" | "unfreeze",
|
|
37
|
+
retried = false
|
|
38
|
+
) {
|
|
39
|
+
try {
|
|
40
|
+
const result = action === "freeze" ? slide.freeze() : slide.unfreeze();
|
|
41
|
+
void Promise.resolve(result).catch(error =>
|
|
42
|
+
this.onTransitionFailure(appId, slide, action, error, retried)
|
|
43
|
+
);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
void Promise.resolve().then(() =>
|
|
46
|
+
this.onTransitionFailure(appId, slide, action, error, retried)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
onTransitionFailure(
|
|
51
|
+
appId: string,
|
|
52
|
+
slide: FreezableSlide,
|
|
53
|
+
action: "freeze" | "unfreeze",
|
|
54
|
+
error: unknown,
|
|
55
|
+
retried: boolean
|
|
56
|
+
) {
|
|
57
|
+
logger.warn(`[Slide] freezer: ${action} failed`, appId, error);
|
|
58
|
+
if (this.map.get(appId) !== slide) return;
|
|
59
|
+
// A failed freeze still occupies a cache slot until a later validation succeeds.
|
|
60
|
+
if (action === "freeze" && !this.queue.includes(appId)) this.queue.push(appId);
|
|
61
|
+
if (retried) return;
|
|
62
|
+
if (action === "freeze" ? this.focusedAppId === appId : this.focusedAppId !== appId) return;
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
if (this.map.get(appId) !== slide) return;
|
|
65
|
+
if (action === "freeze") {
|
|
66
|
+
if (this.focusedAppId !== appId && this.queue.length > FreezerLength) {
|
|
67
|
+
this.validateQueue(true);
|
|
68
|
+
}
|
|
69
|
+
} else if (this.focusedAppId === appId) {
|
|
70
|
+
this.runTransition(appId, slide, action, true);
|
|
71
|
+
}
|
|
72
|
+
}, FreezerRetryDelay);
|
|
73
|
+
},
|
|
74
|
+
validateQueue(retried = false) {
|
|
30
75
|
// queue = [5, 4, 3, 2, 1]
|
|
31
76
|
this.queue.sort((a, b) => {
|
|
32
77
|
const za = this.boxes.get(a)?.zIndex ?? 0;
|
|
33
78
|
const zb = this.boxes.get(b)?.zIndex ?? 0;
|
|
34
79
|
return -(za - zb);
|
|
35
80
|
});
|
|
36
|
-
|
|
81
|
+
const validated = [...this.queue];
|
|
82
|
+
const frozen: string[] = [];
|
|
83
|
+
const missing: string[] = [];
|
|
37
84
|
while (this.queue.length > FreezerLength) {
|
|
38
85
|
const appId = this.queue.pop() as string;
|
|
39
86
|
const slide = this.map.get(appId);
|
|
40
87
|
if (slide) {
|
|
41
|
-
|
|
42
|
-
|
|
88
|
+
this.runTransition(appId, slide, "freeze", retried);
|
|
89
|
+
frozen.push(appId);
|
|
90
|
+
} else {
|
|
91
|
+
missing.push(appId);
|
|
43
92
|
}
|
|
44
93
|
}
|
|
94
|
+
return { validated, frozen, missing };
|
|
45
95
|
},
|
|
46
96
|
set(appId: string, slide: FreezableSlide, box: ReadonlyTeleBox) {
|
|
47
|
-
log("[Slide] freezer: add", appId, inspect(this.queue));
|
|
48
97
|
this.map.set(appId, slide);
|
|
49
98
|
this.boxes.set(appId, box);
|
|
50
99
|
if (!this.queue.includes(appId)) {
|
|
51
100
|
this.queue.unshift(appId);
|
|
52
101
|
}
|
|
53
|
-
this.validateQueue();
|
|
102
|
+
const { validated, frozen, missing } = this.validateQueue();
|
|
103
|
+
log(
|
|
104
|
+
"[Slide] freezer: add",
|
|
105
|
+
appId,
|
|
106
|
+
inspect(this.queue),
|
|
107
|
+
"validate",
|
|
108
|
+
inspect(validated),
|
|
109
|
+
...(frozen.length ? ["freeze-requested", inspect(frozen)] : []),
|
|
110
|
+
...(missing.length ? ["missing", inspect(missing)] : [])
|
|
111
|
+
);
|
|
54
112
|
},
|
|
55
113
|
delete(appId: string) {
|
|
56
114
|
this.map.delete(appId);
|
|
57
115
|
this.boxes.delete(appId);
|
|
58
116
|
this.queue = this.queue.filter(id => id !== appId);
|
|
117
|
+
if (this.focusedAppId === appId) this.focusedAppId = undefined;
|
|
59
118
|
log("[Slide] freezer: delete", appId, inspect(this.queue));
|
|
60
119
|
},
|
|
61
120
|
focus(appId: string) {
|
|
121
|
+
this.focusedAppId = appId;
|
|
62
122
|
const slide = this.map.get(appId);
|
|
63
123
|
const index = this.queue.indexOf(appId);
|
|
64
124
|
if (index > -1) {
|
|
65
125
|
this.queue.splice(index, 1);
|
|
66
126
|
}
|
|
67
127
|
this.queue.unshift(appId);
|
|
68
|
-
this.validateQueue();
|
|
69
|
-
log("[Slide] freezer: focus", appId, inspect(this.queue));
|
|
128
|
+
const { validated, frozen, missing } = this.validateQueue();
|
|
70
129
|
if (slide) {
|
|
71
|
-
slide
|
|
130
|
+
this.runTransition(appId, slide, "unfreeze");
|
|
72
131
|
}
|
|
132
|
+
log(
|
|
133
|
+
slide ? "[Slide] freezer: focus" : "[Slide] freezer: focus-missing",
|
|
134
|
+
appId,
|
|
135
|
+
inspect(this.queue),
|
|
136
|
+
"validate",
|
|
137
|
+
inspect(validated),
|
|
138
|
+
...(frozen.length ? ["freeze-requested", inspect(frozen)] : []),
|
|
139
|
+
...(missing.length ? ["missing", inspect(missing)] : [])
|
|
140
|
+
);
|
|
73
141
|
},
|
|
74
142
|
};
|
|
75
143
|
|
package/src/utils/logger.ts
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type { AppContext
|
|
2
|
+
import type { AppContext } from "@netless/window-manager";
|
|
3
3
|
import type { SlideController } from "../SlideController";
|
|
4
4
|
import { isObj } from "./helpers";
|
|
5
5
|
|
|
6
6
|
type DebugMessage = { slide: boolean | "__instance" | "__debug" };
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type WindowManagerLogger = {
|
|
9
9
|
debug: (...args: any) => void;
|
|
10
10
|
info: (...args: any) => void;
|
|
11
11
|
warn: (...args: any) => void;
|
|
12
|
+
error: (...args: any) => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type LogLevel = keyof WindowManagerLogger;
|
|
16
|
+
|
|
17
|
+
const getAppLogger = (context: AppContext<any, any, any>): WindowManagerLogger | undefined => {
|
|
18
|
+
try {
|
|
19
|
+
return context.getWindowManager()?.Logger as WindowManagerLogger | undefined;
|
|
20
|
+
} catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const reportAppLog = (appLogger: WindowManagerLogger | null, level: LogLevel, ...args: any[]) => {
|
|
26
|
+
try {
|
|
27
|
+
if (appLogger) appLogger[level](...args);
|
|
28
|
+
else if (level === "info" || level === "debug") console.log(...args);
|
|
29
|
+
else console[level](...args);
|
|
30
|
+
} catch {
|
|
31
|
+
// Diagnostics must not affect App setup or resource cleanup.
|
|
32
|
+
}
|
|
12
33
|
};
|
|
13
34
|
|
|
14
35
|
class Logger {
|
|
@@ -33,7 +54,8 @@ class Logger {
|
|
|
33
54
|
|
|
34
55
|
public level: "debug" | "verbose" = import.meta.env.DEV ? "verbose" : "debug";
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
/** Legacy field name; the value comes from WindowManager.Logger. */
|
|
58
|
+
public roomLogger: WindowManagerLogger | null = null;
|
|
37
59
|
|
|
38
60
|
constructor(public enable: boolean) {
|
|
39
61
|
this.initialize();
|
|
@@ -48,27 +70,21 @@ class Logger {
|
|
|
48
70
|
}
|
|
49
71
|
|
|
50
72
|
log(...args: any[]) {
|
|
51
|
-
if (this.roomLogger)
|
|
52
|
-
this.roomLogger.info(...args);
|
|
53
|
-
} else if (this.enable) {
|
|
54
|
-
console.log(...args);
|
|
55
|
-
}
|
|
73
|
+
if (this.roomLogger || this.enable) reportAppLog(this.roomLogger, "info", ...args);
|
|
56
74
|
}
|
|
57
75
|
|
|
58
76
|
verbose(...args: any[]) {
|
|
59
|
-
if (this.roomLogger) {
|
|
60
|
-
this.roomLogger
|
|
61
|
-
} else if (this.enable && this.level === "verbose") {
|
|
62
|
-
console.log(...args);
|
|
77
|
+
if (this.roomLogger || (this.enable && this.level === "verbose")) {
|
|
78
|
+
reportAppLog(this.roomLogger, "debug", ...args);
|
|
63
79
|
}
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
warn(...args: any[]) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
reportAppLog(this.roomLogger, "warn", ...args);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
error(...args: any[]) {
|
|
87
|
+
reportAppLog(this.roomLogger, "error", ...args);
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
/**
|
|
@@ -101,6 +117,6 @@ class Logger {
|
|
|
101
117
|
export const logger = /** @__PURE__ */ new Logger(import.meta.env.DEV);
|
|
102
118
|
export const log = /** @__PURE__ */ logger.log.bind(logger);
|
|
103
119
|
export const verbose = /** @__PURE__ */ logger.verbose.bind(logger);
|
|
104
|
-
export const setRoomLogger = (
|
|
105
|
-
logger.roomLogger = (
|
|
120
|
+
export const setRoomLogger = (context: AppContext<any, any, any>) => {
|
|
121
|
+
logger.roomLogger = getAppLogger(context) ?? null;
|
|
106
122
|
};
|