@netless/window-manager 0.4.0-canary.9 → 0.4.2
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/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/window-manager.iml +12 -0
- package/.vscode/settings.json +1 -0
- package/CHANGELOG.md +43 -2
- package/README.md +3 -0
- package/dist/App/MagixEvent/index.d.ts +29 -0
- package/dist/App/Storage/index.d.ts +19 -6
- package/dist/App/Storage/typings.d.ts +1 -0
- package/dist/AppContext.d.ts +39 -17
- package/dist/AppListener.d.ts +2 -0
- package/dist/AppManager.d.ts +22 -8
- package/dist/AppProxy.d.ts +5 -5
- package/dist/AttributesDelegate.d.ts +2 -2
- package/dist/BoxManager.d.ts +6 -4
- package/dist/BuiltinApps.d.ts +0 -1
- package/dist/Cursor/Cursor.d.ts +10 -12
- package/dist/Cursor/index.d.ts +6 -16
- package/dist/Helper.d.ts +1 -0
- package/dist/Register/index.d.ts +5 -0
- package/dist/Register/storage.d.ts +5 -1
- package/dist/Utils/AppCreateQueue.d.ts +11 -0
- package/dist/Utils/Common.d.ts +4 -1
- package/dist/Utils/RoomHacker.d.ts +3 -3
- package/dist/View/MainView.d.ts +4 -3
- package/dist/constants.d.ts +5 -2
- package/dist/index.d.ts +32 -6
- package/dist/index.es.js +41 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +41 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/typings.d.ts +2 -2
- package/docs/advanced.md +53 -0
- package/docs/api.md +79 -6
- package/docs/concept.md +9 -0
- package/docs/replay.md +40 -0
- package/package.json +8 -9
- package/src/App/MagixEvent/index.ts +68 -0
- package/src/App/Storage/index.ts +89 -43
- package/src/App/Storage/typings.ts +4 -2
- package/src/AppContext.ts +61 -24
- package/src/AppListener.ts +27 -8
- package/src/AppManager.ts +231 -70
- package/src/AppProxy.ts +40 -29
- package/src/AttributesDelegate.ts +2 -2
- package/src/BoxManager.ts +33 -19
- package/src/BuiltinApps.ts +0 -1
- package/src/ContainerResizeObserver.ts +3 -3
- package/src/Cursor/Cursor.svelte +25 -21
- package/src/Cursor/Cursor.ts +25 -38
- package/src/Cursor/icons.ts +2 -0
- package/src/Cursor/index.ts +45 -139
- package/src/Helper.ts +12 -1
- package/src/Register/index.ts +32 -17
- package/src/Register/loader.ts +28 -13
- package/src/Register/storage.ts +6 -1
- package/src/Utils/AppCreateQueue.ts +54 -0
- package/src/Utils/Common.ts +35 -2
- package/src/Utils/RoomHacker.ts +33 -18
- package/src/View/MainView.ts +19 -12
- package/src/View/ViewManager.ts +1 -2
- package/src/constants.ts +6 -2
- package/src/image/laser-pointer-cursor.svg +17 -0
- package/src/index.ts +150 -33
- package/src/shim.d.ts +2 -1
- package/src/style.css +6 -1
- package/src/typings.ts +2 -2
- package/vite.config.js +7 -4
- package/dist/Base/Context.d.ts +0 -12
- package/dist/Base/index.d.ts +0 -7
- package/src/Base/Context.ts +0 -45
- package/src/Base/index.ts +0 -10
package/src/AppProxy.ts
CHANGED
@@ -3,15 +3,12 @@ import { AppAttributes, AppEvents, Events } from "./constants";
|
|
3
3
|
import { AppContext } from "./AppContext";
|
4
4
|
import { appRegister } from "./Register";
|
5
5
|
import { autorun } from "white-web-sdk";
|
6
|
+
import { BoxManagerNotFoundError } from "./Utils/error";
|
7
|
+
import { debounce, get } from "lodash";
|
6
8
|
import { emitter } from "./index";
|
7
9
|
import { Fields } from "./AttributesDelegate";
|
8
|
-
import {
|
10
|
+
import { entireScenes, getScenePath, removeScenes, setScenePath, setViewFocusScenePath } from "./Utils/Common";
|
9
11
|
import { log } from "./Utils/log";
|
10
|
-
import {
|
11
|
-
setScenePath,
|
12
|
-
setViewFocusScenePath,
|
13
|
-
getScenePath
|
14
|
-
} from "./Utils/Common";
|
15
12
|
import type {
|
16
13
|
AppEmitterEvent,
|
17
14
|
AppInitState,
|
@@ -23,10 +20,9 @@ import type { SceneState, View, SceneDefinition } from "white-web-sdk";
|
|
23
20
|
import type { AppManager } from "./AppManager";
|
24
21
|
import type { NetlessApp } from "./typings";
|
25
22
|
import type { ReadonlyTeleBox } from "@netless/telebox-insider";
|
26
|
-
import { Base } from "./Base";
|
27
|
-
import { BoxManagerNotFoundError } from "./Utils/error";
|
28
23
|
|
29
|
-
export class AppProxy
|
24
|
+
export class AppProxy {
|
25
|
+
public kind: string;
|
30
26
|
public id: string;
|
31
27
|
public scenePath?: string;
|
32
28
|
public appEmitter: Emittery<AppEmitterEvent>;
|
@@ -36,7 +32,8 @@ export class AppProxy extends Base {
|
|
36
32
|
private boxManager = this.manager.boxManager;
|
37
33
|
private appProxies = this.manager.appProxies;
|
38
34
|
private viewManager = this.manager.viewManager;
|
39
|
-
private
|
35
|
+
private store = this.manager.store;
|
36
|
+
|
40
37
|
public isAddApp: boolean;
|
41
38
|
private status: "normal" | "destroyed" = "normal";
|
42
39
|
private stateKey: string;
|
@@ -45,11 +42,10 @@ export class AppProxy extends Base {
|
|
45
42
|
|
46
43
|
constructor(
|
47
44
|
private params: BaseInsertParams,
|
48
|
-
manager: AppManager,
|
45
|
+
private manager: AppManager,
|
49
46
|
appId: string,
|
50
47
|
isAddApp: boolean
|
51
48
|
) {
|
52
|
-
super(manager);
|
53
49
|
this.kind = params.kind;
|
54
50
|
this.id = appId;
|
55
51
|
this.stateKey = `${this.id}_state`;
|
@@ -71,7 +67,7 @@ export class AppProxy extends Base {
|
|
71
67
|
if (options) {
|
72
68
|
this.scenePath = options.scenePath;
|
73
69
|
if (this.appAttributes?.isDynamicPPT && this.scenePath) {
|
74
|
-
this.scenes = this.manager.displayer
|
70
|
+
this.scenes = entireScenes(this.manager.displayer)[this.scenePath];
|
75
71
|
} else {
|
76
72
|
this.scenes = options.scenes;
|
77
73
|
}
|
@@ -113,9 +109,7 @@ export class AppProxy extends Base {
|
|
113
109
|
this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
|
114
110
|
}
|
115
111
|
|
116
|
-
public async baseInsertApp(
|
117
|
-
skipUpdate = false,
|
118
|
-
): Promise<{ appId: string; app: NetlessApp }> {
|
112
|
+
public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
|
119
113
|
const params = this.params;
|
120
114
|
if (!params.kind) {
|
121
115
|
throw new Error("[WindowManager]: kind require");
|
@@ -123,30 +117,27 @@ export class AppProxy extends Base {
|
|
123
117
|
const appImpl = await appRegister.appClasses.get(params.kind)?.();
|
124
118
|
const appParams = appRegister.registered.get(params.kind);
|
125
119
|
if (appImpl) {
|
126
|
-
await this.setupApp(
|
120
|
+
await this.setupApp(
|
121
|
+
this.id,
|
122
|
+
skipUpdate,
|
123
|
+
appImpl,
|
124
|
+
params.options,
|
125
|
+
appParams?.appOptions
|
126
|
+
);
|
127
127
|
} else {
|
128
128
|
throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
|
129
129
|
}
|
130
|
-
this.
|
130
|
+
this.boxManager?.updateManagerRect();
|
131
131
|
return {
|
132
132
|
appId: this.id,
|
133
133
|
app: appImpl,
|
134
134
|
};
|
135
135
|
}
|
136
136
|
|
137
|
-
private focusApp() {
|
138
|
-
this.focusBox();
|
139
|
-
this.store.setMainViewFocusPath(this.manager.mainView);
|
140
|
-
}
|
141
|
-
|
142
137
|
public get box(): ReadonlyTeleBox | undefined {
|
143
138
|
return this.boxManager?.getBox(this.id);
|
144
139
|
}
|
145
140
|
|
146
|
-
public focusBox() {
|
147
|
-
this.boxManager?.focusBox({ appId: this.id });
|
148
|
-
}
|
149
|
-
|
150
141
|
private async setupApp(
|
151
142
|
appId: string,
|
152
143
|
skipUpdate: boolean,
|
@@ -174,7 +165,7 @@ export class AppProxy extends Base {
|
|
174
165
|
// 延迟执行 setup, 防止初始化的属性没有更新成功
|
175
166
|
const result = await app.setup(context);
|
176
167
|
this.appResult = result;
|
177
|
-
appRegister.notifyApp(
|
168
|
+
appRegister.notifyApp(this.kind, "created", { appId, result });
|
178
169
|
this.afterSetupApp(boxInitState);
|
179
170
|
this.fixMobileSize();
|
180
171
|
}, 50);
|
@@ -186,6 +177,10 @@ export class AppProxy extends Base {
|
|
186
177
|
canOperate: this.manager.canOperate,
|
187
178
|
smartPosition: this.isAddApp,
|
188
179
|
});
|
180
|
+
if (this.isAddApp && this.box) {
|
181
|
+
this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
|
182
|
+
this.boxManager.focusBox({ appId }, false);
|
183
|
+
}
|
189
184
|
} catch (error: any) {
|
190
185
|
console.error(error);
|
191
186
|
throw new Error(`[WindowManager]: app setup error: ${error.message}`);
|
@@ -317,7 +312,7 @@ export class AppProxy extends Base {
|
|
317
312
|
}
|
318
313
|
});
|
319
314
|
});
|
320
|
-
this.manager.refresher?.add(this.stateKey,() => {
|
315
|
+
this.manager.refresher?.add(this.stateKey, () => {
|
321
316
|
return autorun(() => {
|
322
317
|
const appState = this.appAttributes?.state;
|
323
318
|
if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
|
@@ -325,8 +320,20 @@ export class AppProxy extends Base {
|
|
325
320
|
}
|
326
321
|
});
|
327
322
|
});
|
323
|
+
this.manager.refresher?.add(`${appId}-fullPath`, () => {
|
324
|
+
return autorun(() => {
|
325
|
+
const fullPath = this.appAttributes?.fullPath;
|
326
|
+
this.setFocusScenePathHandler(fullPath);
|
327
|
+
});
|
328
|
+
});
|
328
329
|
};
|
329
330
|
|
331
|
+
private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
|
332
|
+
if (this.view && fullPath && fullPath !== this.view?.focusScenePath) {
|
333
|
+
setViewFocusScenePath(this.view, fullPath);
|
334
|
+
}
|
335
|
+
}, 50);
|
336
|
+
|
330
337
|
public setScenePath(): void {
|
331
338
|
if (!this.manager.canOperate) return;
|
332
339
|
const fullScenePath = this.getFullScenePath();
|
@@ -365,6 +372,9 @@ export class AppProxy extends Base {
|
|
365
372
|
}
|
366
373
|
if (cleanAttrs) {
|
367
374
|
this.store.cleanAppAttributes(this.id);
|
375
|
+
if (this.scenePath) {
|
376
|
+
removeScenes(this.manager.room, this.scenePath);
|
377
|
+
}
|
368
378
|
}
|
369
379
|
this.appProxies.delete(this.id);
|
370
380
|
|
@@ -372,6 +382,7 @@ export class AppProxy extends Base {
|
|
372
382
|
this.manager.appStatus.delete(this.id);
|
373
383
|
this.manager.refresher?.remove(this.id);
|
374
384
|
this.manager.refresher?.remove(this.stateKey);
|
385
|
+
this.manager.refresher?.remove(`${this.id}-fullPath`);
|
375
386
|
}
|
376
387
|
|
377
388
|
public close(): Promise<void> {
|
@@ -123,7 +123,7 @@ export class AttributesDelegate {
|
|
123
123
|
return this.getAppAttributes(id)?.options?.scenePath;
|
124
124
|
}
|
125
125
|
|
126
|
-
public getMainViewScenePath() {
|
126
|
+
public getMainViewScenePath(): string | undefined {
|
127
127
|
return this.attributes["_mainScenePath"];
|
128
128
|
}
|
129
129
|
|
@@ -159,7 +159,7 @@ export class AttributesDelegate {
|
|
159
159
|
this.context.safeSetAttributes({ [Fields.MainViewSize]: { ...size } });
|
160
160
|
}
|
161
161
|
|
162
|
-
public setAppFocus(appId: string, focus: boolean) {
|
162
|
+
public setAppFocus = (appId: string, focus: boolean) => {
|
163
163
|
if (focus) {
|
164
164
|
this.context.safeSetAttributes({ [Fields.Focus]: appId });
|
165
165
|
} else {
|
package/src/BoxManager.ts
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
|
2
|
-
import { debounce
|
2
|
+
import { debounce } from "lodash";
|
3
|
+
import { emitter, WindowManager } from "./index";
|
3
4
|
import {
|
4
|
-
TELE_BOX_MANAGER_EVENT,
|
5
5
|
TELE_BOX_STATE,
|
6
6
|
TeleBoxCollector,
|
7
7
|
TeleBoxManager,
|
8
8
|
} from "@netless/telebox-insider";
|
9
|
-
import { emitter, WindowManager } from "./index";
|
10
9
|
import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
|
11
10
|
import type {
|
12
11
|
TeleBoxManagerUpdateConfig,
|
@@ -15,6 +14,7 @@ import type {
|
|
15
14
|
TeleBoxManagerConfig,
|
16
15
|
TeleBoxColorScheme,
|
17
16
|
TeleBoxRect,
|
17
|
+
TeleBoxConfig,
|
18
18
|
} from "@netless/telebox-insider";
|
19
19
|
import type Emittery from "emittery";
|
20
20
|
import type { NetlessApp } from "./typings";
|
@@ -57,6 +57,7 @@ export type BoxManagerContext = {
|
|
57
57
|
canOperate: () => boolean;
|
58
58
|
notifyContainerRectUpdate: (rect: TeleBoxRect) => void;
|
59
59
|
cleanFocus: () => void;
|
60
|
+
setAppFocus: (appId: string) => void;
|
60
61
|
};
|
61
62
|
|
62
63
|
export const createBoxManager = (
|
@@ -74,6 +75,7 @@ export const createBoxManager = (
|
|
74
75
|
notifyContainerRectUpdate: (rect: TeleBoxRect) =>
|
75
76
|
manager.appManager?.notifyContainerRectUpdate(rect),
|
76
77
|
cleanFocus: () => manager.appManager?.store.cleanFocus(),
|
78
|
+
setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
|
77
79
|
callbacks,
|
78
80
|
emitter,
|
79
81
|
},
|
@@ -90,17 +92,32 @@ export class BoxManager {
|
|
90
92
|
) {
|
91
93
|
const { emitter, callbacks } = context;
|
92
94
|
this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
|
96
|
+
// 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
|
97
|
+
this.teleBoxManager._state$.reaction(state => {
|
98
|
+
callbacks.emit("boxStateChange", state);
|
99
|
+
emitter.emit("boxStateChange", state);
|
100
|
+
});
|
101
|
+
|
102
|
+
this.teleBoxManager._darkMode$.reaction(darkMode => {
|
103
|
+
callbacks.emit("darkModeChange", darkMode);
|
98
104
|
});
|
105
|
+
this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
|
106
|
+
callbacks.emit("prefersColorSchemeChange", colorScheme);
|
107
|
+
});
|
108
|
+
|
109
|
+
// events.on 的值则会根据 skipUpdate 来决定是否触发回调
|
99
110
|
this.teleBoxManager.events.on("minimized", minimized => {
|
100
111
|
this.context.safeSetAttributes({ minimized });
|
101
112
|
if (minimized) {
|
102
113
|
this.context.cleanFocus();
|
103
114
|
this.blurAllBox();
|
115
|
+
} else {
|
116
|
+
const topBox = this.getTopBox();
|
117
|
+
if (topBox) {
|
118
|
+
this.context.setAppFocus(topBox.id);
|
119
|
+
this.focusBox({ appId: topBox.id }, false);
|
120
|
+
}
|
104
121
|
}
|
105
122
|
});
|
106
123
|
this.teleBoxManager.events.on("maximized", maximized => {
|
@@ -136,12 +153,6 @@ export class BoxManager {
|
|
136
153
|
}
|
137
154
|
}
|
138
155
|
});
|
139
|
-
this.teleBoxManager.events.on("dark_mode", darkMode => {
|
140
|
-
callbacks.emit("darkModeChange", darkMode);
|
141
|
-
});
|
142
|
-
this.teleBoxManager.events.on("prefers_color_scheme", colorScheme => {
|
143
|
-
callbacks.emit("prefersColorSchemeChange", colorScheme);
|
144
|
-
});
|
145
156
|
this.teleBoxManager.events.on("z_index", box => {
|
146
157
|
this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
|
147
158
|
});
|
@@ -150,7 +161,7 @@ export class BoxManager {
|
|
150
161
|
|
151
162
|
private playgroundSizeChangeListener = () => {
|
152
163
|
this.updateManagerRect();
|
153
|
-
}
|
164
|
+
};
|
154
165
|
|
155
166
|
private get mainView() {
|
156
167
|
return this.context.getMainView();
|
@@ -281,8 +292,7 @@ export class BoxManager {
|
|
281
292
|
}
|
282
293
|
|
283
294
|
public getTopBox(): ReadonlyTeleBox | undefined {
|
284
|
-
|
285
|
-
return maxBy(boxes, "zIndex");
|
295
|
+
return this.teleBoxManager.topBox;
|
286
296
|
}
|
287
297
|
|
288
298
|
public updateBoxState(state?: AppInitState): void {
|
@@ -359,9 +369,9 @@ export class BoxManager {
|
|
359
369
|
this.teleBoxManager.updateAll(config);
|
360
370
|
}
|
361
371
|
|
362
|
-
public setMaximized(maximized: boolean) {
|
372
|
+
public setMaximized(maximized: boolean, skipUpdate = true): void {
|
363
373
|
if (maximized !== this.maximized) {
|
364
|
-
this.teleBoxManager.setMaximized(maximized,
|
374
|
+
this.teleBoxManager.setMaximized(maximized, skipUpdate);
|
365
375
|
}
|
366
376
|
}
|
367
377
|
|
@@ -379,6 +389,10 @@ export class BoxManager {
|
|
379
389
|
}
|
380
390
|
}
|
381
391
|
|
392
|
+
public updateBox(id: string, payload: TeleBoxConfig, skipUpdate = true): void {
|
393
|
+
this.teleBoxManager.update(id, payload, skipUpdate);
|
394
|
+
}
|
395
|
+
|
382
396
|
public setReadonly(readonly: boolean) {
|
383
397
|
this.teleBoxManager.setReadonly(readonly);
|
384
398
|
}
|
package/src/BuiltinApps.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import AppDocsViewer from "@netless/app-docs-viewer";
|
2
2
|
import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
|
3
3
|
import { WindowManager } from "./index";
|
4
|
-
import "@netless/app-docs-viewer/dist/style.css";
|
5
4
|
|
6
5
|
export const setupBuiltin = () => {
|
7
6
|
if (WindowManager.debug) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
|
2
2
|
import { WindowManager } from "./index";
|
3
|
-
import type { EmitterType} from "./index";
|
3
|
+
import type { EmitterType } from "./index";
|
4
4
|
|
5
5
|
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
6
6
|
|
@@ -13,7 +13,7 @@ export class ContainerResizeObserver {
|
|
13
13
|
container: HTMLElement,
|
14
14
|
sizer: HTMLElement,
|
15
15
|
wrapper: HTMLDivElement,
|
16
|
-
emitter: EmitterType
|
16
|
+
emitter: EmitterType
|
17
17
|
) {
|
18
18
|
const containerResizeObserver = new ContainerResizeObserver(emitter);
|
19
19
|
containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
|
@@ -31,7 +31,7 @@ export class ContainerResizeObserver {
|
|
31
31
|
const containerRect = entries[0]?.contentRect;
|
32
32
|
if (containerRect) {
|
33
33
|
this.updateSizer(containerRect, sizer, wrapper);
|
34
|
-
this.emitter.emit("playgroundSizeChange", containerRect)
|
34
|
+
this.emitter.emit("playgroundSizeChange", containerRect);
|
35
35
|
}
|
36
36
|
});
|
37
37
|
|
package/src/Cursor/Cursor.svelte
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import { isEmpty } from "lodash";
|
3
|
+
import { ApplianceNames } from "white-web-sdk";
|
3
4
|
|
4
5
|
export let cursorName: string;
|
5
6
|
export let tagName: string;
|
@@ -19,6 +20,7 @@
|
|
19
20
|
$: hasTagName = !isEmpty(tagName);
|
20
21
|
$: hasAvatar = !isEmpty(avatar);
|
21
22
|
$: display = visible ? "initial" : "none";
|
23
|
+
$: isLaserPointer = appliance === ApplianceNames.laserPointer;
|
22
24
|
|
23
25
|
const computedAvatarStyle = () => {
|
24
26
|
return Object.entries({
|
@@ -36,28 +38,30 @@
|
|
36
38
|
<div
|
37
39
|
class="netless-window-manager-cursor-mid"
|
38
40
|
style="transform: translateX({x}px) translateY({y}px);display: {display}"
|
39
|
-
>
|
40
|
-
|
41
|
-
<div
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
{
|
57
|
-
|
58
|
-
|
41
|
+
>
|
42
|
+
{#if !isLaserPointer}
|
43
|
+
<div class="netless-window-manager-cursor-name">
|
44
|
+
<div
|
45
|
+
class={theme}
|
46
|
+
style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
|
47
|
+
>
|
48
|
+
{#if hasAvatar}
|
49
|
+
<img
|
50
|
+
class="netless-window-manager-cursor-selector-avatar"
|
51
|
+
style={computedAvatarStyle()}
|
52
|
+
src={avatar}
|
53
|
+
alt="avatar"
|
54
|
+
/>
|
55
|
+
{/if}
|
56
|
+
<span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;max-width: 80px">{cursorName}</span>
|
57
|
+
{#if hasTagName}
|
58
|
+
<span class="netless-window-manager-cursor-tag-name" style="background-color: {cursorTagBackgroundColor}">
|
59
|
+
{tagName}
|
60
|
+
</span>
|
61
|
+
{/if}
|
62
|
+
</div>
|
59
63
|
</div>
|
60
|
-
|
64
|
+
{/if}
|
61
65
|
<div class="cursor-image-wrapper">
|
62
66
|
<img class="netless-window-manager-cursor-{appliance}-image" {src} alt={appliance} />
|
63
67
|
</div>
|
package/src/Cursor/Cursor.ts
CHANGED
@@ -1,42 +1,34 @@
|
|
1
|
-
import App from
|
2
|
-
import { ApplianceMap } from
|
3
|
-
import { ApplianceNames } from
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import { get, omit } from 'lodash';
|
7
|
-
import type { Position } from '../AttributesDelegate';
|
1
|
+
import App from "./Cursor.svelte";
|
2
|
+
import { ApplianceMap } from "./icons";
|
3
|
+
import { ApplianceNames } from "white-web-sdk";
|
4
|
+
import { omit } from "lodash";
|
5
|
+
import type { Position } from "../AttributesDelegate";
|
8
6
|
import type { RoomMember } from "white-web-sdk";
|
9
7
|
import type { CursorManager } from "./index";
|
10
8
|
import type { SvelteComponent } from "svelte";
|
11
|
-
import {
|
12
|
-
import type { AppManager } from '../AppManager';
|
9
|
+
import type { AppManager } from "../AppManager";
|
13
10
|
|
14
11
|
export type Payload = {
|
15
|
-
[key: string]: any
|
16
|
-
}
|
17
|
-
|
12
|
+
[key: string]: any;
|
13
|
+
};
|
18
14
|
|
19
|
-
export class Cursor
|
15
|
+
export class Cursor {
|
20
16
|
private member?: RoomMember;
|
21
17
|
private timer?: number;
|
22
18
|
private component?: SvelteComponent;
|
23
19
|
|
24
20
|
constructor(
|
25
|
-
manager: AppManager,
|
26
|
-
addCursorChangeListener: (uid: string, callback: (position: Position, state: CursorState) => void) => void,
|
27
|
-
private cursors: any,
|
21
|
+
private manager: AppManager,
|
28
22
|
private memberId: string,
|
29
23
|
private cursorManager: CursorManager,
|
30
|
-
private wrapper?: HTMLElement
|
24
|
+
private wrapper?: HTMLElement
|
31
25
|
) {
|
32
|
-
|
33
|
-
this.setMember();
|
26
|
+
this.updateMember();
|
34
27
|
this.createCursor();
|
35
|
-
addCursorChangeListener(this.memberId, this.onCursorChange);
|
36
28
|
this.autoHidden();
|
37
29
|
}
|
38
30
|
|
39
|
-
|
31
|
+
public move = (position: Position) => {
|
40
32
|
if (position.type === "main") {
|
41
33
|
const rect = this.cursorManager.wrapperRect;
|
42
34
|
if (this.component && rect) {
|
@@ -45,7 +37,6 @@ export class Cursor extends Base {
|
|
45
37
|
}
|
46
38
|
} else {
|
47
39
|
const focusView = this.cursorManager.focusView;
|
48
|
-
// TODO 可以存一个当前 focusView 的 Rect 这样只有 focus 切换的时候才调用 getBoundingClientRect
|
49
40
|
const viewRect = focusView?.divElement?.getBoundingClientRect();
|
50
41
|
const viewCamera = focusView?.camera;
|
51
42
|
if (focusView && viewRect && viewCamera && this.component) {
|
@@ -53,10 +44,11 @@ export class Cursor extends Base {
|
|
53
44
|
this.moveCursor(position, viewRect, focusView);
|
54
45
|
}
|
55
46
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
47
|
+
};
|
48
|
+
|
49
|
+
public leave = () => {
|
50
|
+
this.hide();
|
51
|
+
};
|
60
52
|
|
61
53
|
private moveCursor(cursor: Position, rect: DOMRect, view: any) {
|
62
54
|
const { x, y, type } = cursor;
|
@@ -124,21 +116,12 @@ export class Cursor extends Base {
|
|
124
116
|
}
|
125
117
|
}
|
126
118
|
|
127
|
-
public get cursorState(): CursorState | undefined {
|
128
|
-
return get(this.cursors, [this.memberId, Fields.CursorState]);
|
129
|
-
}
|
130
|
-
|
131
|
-
public get cursorPosition(): Position | undefined {
|
132
|
-
return get(this.cursors, [this.memberId, Fields.Position]);
|
133
|
-
}
|
134
|
-
|
135
119
|
private autoHidden() {
|
136
120
|
if (this.timer) {
|
137
121
|
clearTimeout(this.timer);
|
138
122
|
}
|
139
123
|
this.timer = window.setTimeout(() => {
|
140
124
|
this.hide();
|
141
|
-
this.store.updateCursorState(this.memberId, CursorState.Leave);
|
142
125
|
}, 1000 * 10); // 10 秒钟自动隐藏
|
143
126
|
}
|
144
127
|
|
@@ -175,9 +158,10 @@ export class Cursor extends Base {
|
|
175
158
|
}
|
176
159
|
}
|
177
160
|
|
178
|
-
public
|
179
|
-
this.member = this.
|
161
|
+
public updateMember() {
|
162
|
+
this.member = this.manager.findMemberByUid(this.memberId);
|
180
163
|
this.updateComponent();
|
164
|
+
return this.member;
|
181
165
|
}
|
182
166
|
|
183
167
|
private updateComponent() {
|
@@ -188,13 +172,16 @@ export class Cursor extends Base {
|
|
188
172
|
if (this.component) {
|
189
173
|
this.component.$destroy();
|
190
174
|
}
|
191
|
-
this.manager.refresher?.remove(this.memberId);
|
192
175
|
this.cursorManager.cursorInstances.delete(this.memberId);
|
176
|
+
if (this.timer) {
|
177
|
+
clearTimeout(this.timer);
|
178
|
+
}
|
193
179
|
}
|
194
180
|
|
195
181
|
public hide() {
|
196
182
|
if (this.component) {
|
197
183
|
this.component.$set({ visible: false });
|
184
|
+
this.destroy();
|
198
185
|
}
|
199
186
|
}
|
200
187
|
}
|
package/src/Cursor/icons.ts
CHANGED
@@ -4,6 +4,7 @@ import selector from "../image/selector-cursor.png";
|
|
4
4
|
import eraser from "../image/eraser-cursor.png";
|
5
5
|
import shape from "../image/shape-cursor.svg";
|
6
6
|
import text from "../image/text-cursor.svg";
|
7
|
+
import laser from "../image/laser-pointer-cursor.svg";
|
7
8
|
|
8
9
|
export const ApplianceMap: {
|
9
10
|
[key: string]: string;
|
@@ -13,4 +14,5 @@ export const ApplianceMap: {
|
|
13
14
|
[ApplianceNames.eraser]: eraser,
|
14
15
|
[ApplianceNames.shape]: shape,
|
15
16
|
[ApplianceNames.text]: text,
|
17
|
+
[ApplianceNames.laserPointer]: laser,
|
16
18
|
};
|