@netless/slide 1.4.55 → 1.4.57

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.md CHANGED
@@ -2,39 +2,17 @@
2
2
 
3
3
  ## [示例及具体文档参考](https://github.com/netless-io/netless-slide-demo)
4
4
 
5
- ## interactive 客户端的同步队列追最新策略
6
-
7
- `@netless/slide` 默认按 FIFO 顺序执行所有接收到的同步事件。对于需要在弱网积压时优先追赶最新页面的 interactive 客户端,可以显式启用 `latest-pending-render`;可写端和只读端都可以使用:
8
-
9
- ```ts
10
- const slide = new Slide({
11
- // 其他配置
12
- syncEventQueuePolicy: "latest-pending-render",
13
- });
14
-
15
- slide.setSyncEventQueuePolicy("fifo");
16
- slide.setSyncEventQueuePolicy("latest-pending-render");
17
- ```
18
-
19
- 该策略只处理尚未开始执行的 pending 事件:
20
-
21
- - 新的 `renderSlide` 到达时,删除同一资源段内排在它之前的 pending 翻页、`nextStep`、`prevStep`、交互动画和媒体操作。
22
- - 保留最新 `renderSlide`,以及它之后到达的所有操作,并继续按 FIFO 执行。
23
- - `setResource` 和未知事件是硬边界,不会被删除,也不会跨越边界压缩。
24
- - 已经开始执行的事件不会被取消;该策略不保证完全不显示 running 中间页。
25
- - 动态切换策略本身不会清空或重排已有队列;切换后新到达的 `renderSlide` 才会触发压缩。
26
- - latest 策略下开始执行远端 `renderSlide`,或远端事件形成 pending 积压后,Slide 会暂时阻止本地 `nextStep()`、`prevStep()`、`renderSlide()`、`doRenderSlide()` 和画布交互;同步接收、状态恢复和队列执行不受影响。本机事件优先通过现有 Lock 的 UUID 回包识别,并以 `clientId` 作为补充,不会锁住正在操作的当前控制者。
27
- - 临时输入锁在同步队列重新空闲后解除,并恢复调用方最近一次通过 `setInteractive()` 设置的权限;不会把原本的只读端恢复成可写。
28
-
29
- 该能力不会根据 `interactive` 自动开启。调用方应通过自己的灰度开关显式选择;关闭策略时可以恢复为 `fifo`,不会修改调用方原有的本地交互权限。
30
-
31
- 该策略只在 Slide 的 `mode: "interactive"` 下生效;`sync`、`local` 和 `addLink` mode 会强制使用 `fifo`。这项 mode 保护不能替代调用方的只读权限与 feature flag 判断。
5
+ ## changelog
32
6
 
33
- 实际发生压缩时,SDK 会通过现有 logger/tracker 记录丢弃分类、保留页码、目标事件接收时间、对应渲染完成时间和追赶输入锁状态;记录中不包含事件 UUID、资源 URL 或 token。
7
+ ### 1.4.57 (2026-08-27)
8
+ * 支持在客户端解码并播放本地 GIF 资源,兼容旧版 Safari
9
+ * 修复 WebGL 切页转场被错误关闭的问题
34
10
 
35
- ## changelog
11
+ ### 1.4.56 (2026-08-25)
12
+ * 修复冻结 (frozen) 恢复 (release) 时画面闪烁问题
13
+ * 完善 Canvas/WebGL 播放器销毁清理,避免状态切换后残留多个 canvas
36
14
 
37
- ### 未发布
15
+ ### 1.4.55 (2026-08-21)
38
16
  * 增加 `latest-pending-render` 同步队列策略,弱网积压时丢弃最后翻页前尚未执行的中间页面操作,并在追赶期间阻止本地输入
39
17
  * 修复 PPT 图形内阴影(`innerShadow`)被错误渲染为不透明黑边的问题
40
18
  * 按转换数据中的颜色、模糊半径、距离和方向渲染柔化内阴影
@@ -0,0 +1,13 @@
1
+ export interface FrozenMediaPlaybackState {
2
+ type: "pause" | "play";
3
+ time: number;
4
+ }
5
+ export declare type FrozenMediaTimes = {
6
+ [key: string]: number;
7
+ };
8
+ export declare function captureFrozenMediaTimes(mediaState: {
9
+ [key: string]: FrozenMediaPlaybackState;
10
+ }, frozenTime: number): FrozenMediaTimes;
11
+ export declare function applyFrozenMediaTimes(mediaState: {
12
+ [key: string]: FrozenMediaPlaybackState;
13
+ }, frozenMediaTimes: FrozenMediaTimes, releaseTime: number): void;
@@ -0,0 +1,18 @@
1
+ export function captureFrozenMediaTimes(mediaState, frozenTime) {
2
+ var frozenMediaTimes = Object.create(null);
3
+ for (var key in mediaState) {
4
+ if (mediaState[key].type === "play") {
5
+ frozenMediaTimes[key] = frozenTime;
6
+ }
7
+ }
8
+ return frozenMediaTimes;
9
+ }
10
+ export function applyFrozenMediaTimes(mediaState, frozenMediaTimes, releaseTime) {
11
+ for (var key in mediaState) {
12
+ var media = mediaState[key];
13
+ var frozenTime = frozenMediaTimes[key];
14
+ if (media.type === "play" && frozenTime !== undefined) {
15
+ media.time = releaseTime - (Math.max(frozenTime, media.time) - media.time);
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ declare type VisibilityStyle = {
2
+ visibility: string;
3
+ };
4
+ declare type DisplayStyle = {
5
+ display: string;
6
+ };
7
+ declare type RenderSlidePlayer = {
8
+ view?: {
9
+ style: VisibilityStyle;
10
+ } | null;
11
+ };
12
+ declare type RenderSlideElement = {
13
+ style: VisibilityStyle & DisplayStyle;
14
+ };
15
+ /**
16
+ * Reveal a completed live render and remove its fallback image.
17
+ * Frozen and release states own visibility while their player transition is active.
18
+ */
19
+ export declare function updateRenderSlideVisibility(isReleasing: boolean, player: RenderSlidePlayer | undefined, frame: RenderSlideElement, cacheImage: RenderSlideElement): void;
20
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reveal a completed live render and remove its fallback image.
3
+ * Frozen and release states own visibility while their player transition is active.
4
+ */
5
+ export function updateRenderSlideVisibility(isReleasing, player, frame, cacheImage) {
6
+ if (isReleasing || !(player === null || player === void 0 ? void 0 : player.view)) {
7
+ return;
8
+ }
9
+ if (player.view.style.visibility === "hidden") {
10
+ player.view.style.visibility = "visible";
11
+ }
12
+ if (frame.style.visibility === "hidden") {
13
+ frame.style.visibility = "visible";
14
+ }
15
+ cacheImage.style.display = "none";
16
+ }
@@ -1,3 +1,18 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ if (typeof b !== "function" && b !== null)
10
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
+ extendStatics(d, b);
12
+ function __() { this.constructor = d; }
13
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
+ };
15
+ })();
1
16
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
17
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
18
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -35,11 +50,24 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
50
  }
36
51
  };
37
52
  import EventEmitter from "eventemitter3";
53
+ var RenderTaskCancelledError = /** @class */ (function (_super) {
54
+ __extends(RenderTaskCancelledError, _super);
55
+ function RenderTaskCancelledError(message) {
56
+ if (message === void 0) { message = "render task cancelled"; }
57
+ var _this = _super.call(this, message) || this;
58
+ _this.code = "RENDER_TASK_CANCELLED";
59
+ _this.name = "AbortError";
60
+ return _this;
61
+ }
62
+ return RenderTaskCancelledError;
63
+ }(Error));
64
+ export { RenderTaskCancelledError };
38
65
  var Task = /** @class */ (function () {
39
66
  function Task(index, slideIndex, fn, eventHub, id) {
40
67
  this.state = "idle";
41
68
  this.index = -1;
42
69
  this.slideIndex = -1;
70
+ this.cancelled = false;
43
71
  this.fn = fn;
44
72
  this.index = index;
45
73
  this.slideIndex = slideIndex;
@@ -52,6 +80,9 @@ var Task = /** @class */ (function () {
52
80
  return __generator(this, function (_a) {
53
81
  switch (_a.label) {
54
82
  case 0:
83
+ if (this.cancelled) {
84
+ return [2 /*return*/];
85
+ }
55
86
  this.eventHub.emit("task-start", this);
56
87
  _a.label = 1;
57
88
  case 1:
@@ -60,11 +91,17 @@ var Task = /** @class */ (function () {
60
91
  return [4 /*yield*/, this.fn()];
61
92
  case 2:
62
93
  _a.sent();
94
+ if (this.cancelled) {
95
+ return [2 /*return*/];
96
+ }
63
97
  this.state = "end";
64
98
  this.eventHub.emit("task-end", this);
65
99
  return [3 /*break*/, 4];
66
100
  case 3:
67
101
  e_1 = _a.sent();
102
+ if (this.cancelled) {
103
+ return [2 /*return*/];
104
+ }
68
105
  this.eventHub.emit("task-error", {
69
106
  task: this,
70
107
  error: e_1,
@@ -75,6 +112,9 @@ var Task = /** @class */ (function () {
75
112
  });
76
113
  });
77
114
  };
115
+ Task.prototype.cancel = function () {
116
+ this.cancelled = true;
117
+ };
78
118
  return Task;
79
119
  }());
80
120
  export { Task };
@@ -84,6 +124,7 @@ var RenderingTaskManager = /** @class */ (function () {
84
124
  this.eventHub = new EventEmitter();
85
125
  this.tasks = [];
86
126
  this.index = 0;
127
+ this.destroyed = false;
87
128
  this.eventHub.on("task-end", function (task) {
88
129
  var _a;
89
130
  var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
@@ -98,15 +139,28 @@ var RenderingTaskManager = /** @class */ (function () {
98
139
  _this.eventHub.emit("task-end-" + task.id);
99
140
  });
100
141
  this.eventHub.on("task-error", function (_a) {
101
- var task = _a.task;
142
+ var task = _a.task, error = _a.error;
102
143
  var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
103
144
  if (selfIndex >= 0) {
104
145
  _this.tasks.splice(selfIndex, 1);
105
146
  _this.replaceIdleTask();
106
147
  }
107
- _this.eventHub.emit("task-error-" + task.id);
148
+ _this.eventHub.emit("task-error-" + task.id, error);
149
+ });
150
+ this.eventHub.on("task-cancel", function (_a) {
151
+ var task = _a.task, error = _a.error;
152
+ var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
153
+ if (selfIndex >= 0) {
154
+ _this.tasks.splice(selfIndex, 1);
155
+ }
156
+ _this.eventHub.emit("task-cancel-" + task.id, error);
108
157
  });
109
158
  }
159
+ RenderingTaskManager.prototype.cancelTask = function (task) {
160
+ var error = new RenderTaskCancelledError();
161
+ task.cancel();
162
+ this.eventHub.emit("task-cancel", { task: task, error: error });
163
+ };
110
164
  RenderingTaskManager.prototype.replaceIdleTask = function () {
111
165
  var _this = this;
112
166
  var _a;
@@ -116,11 +170,19 @@ var RenderingTaskManager = /** @class */ (function () {
116
170
  ids.add(i);
117
171
  }
118
172
  }
119
- Array.from(ids).forEach(function (id) {
120
- _this.tasks.splice(id, 1);
173
+ // Remove from the end so deleting one idle task does not shift the
174
+ // indexes selected for the remaining cancellations.
175
+ Array.from(ids).sort(function (a, b) { return b - a; }).forEach(function (id) {
176
+ var task = _this.tasks.splice(id, 1)[0];
177
+ if (task) {
178
+ _this.cancelTask(task);
179
+ }
121
180
  });
122
181
  };
123
182
  RenderingTaskManager.prototype.addTask = function (fn, slideIndex, id) {
183
+ if (this.destroyed) {
184
+ return undefined;
185
+ }
124
186
  var task = new Task(this.index++, slideIndex, fn, this.eventHub, id);
125
187
  this.tasks.push(task);
126
188
  this.tasks.sort(function (a, b) { return a.index - b.index; });
@@ -128,11 +190,21 @@ var RenderingTaskManager = /** @class */ (function () {
128
190
  if (this.tasks.length === 1) {
129
191
  task.apply();
130
192
  }
193
+ return task;
131
194
  };
132
195
  RenderingTaskManager.prototype.hasStartTask = function () {
133
196
  return this.tasks.some(function (t) { return t.state === "start"; });
134
197
  };
135
198
  RenderingTaskManager.prototype.destroy = function () {
199
+ var _this = this;
200
+ if (this.destroyed) {
201
+ return;
202
+ }
203
+ this.destroyed = true;
204
+ var tasks = this.tasks.splice(0);
205
+ tasks.forEach(function (task) {
206
+ _this.cancelTask(task);
207
+ });
136
208
  this.eventHub.removeAllListeners();
137
209
  };
138
210
  return RenderingTaskManager;
package/lib/Slide.d.ts CHANGED
@@ -5,6 +5,7 @@ import { AliTrackLogger } from "./AliTrackLogger";
5
5
  import { preloadResource } from "./global";
6
6
  export { ErrorType, PreloadCancelledError } from "@netless/ppt-player";
7
7
  export type { SyncEventQueuePolicy } from "./SyncTaskManager";
8
+ export declare const LOCAL_GIF_RUNTIME_CAPABILITY = "netless-slide-local-gif-production-client-geometry";
8
9
  export declare type UrlInterrupter = (url: string) => Promise<string>;
9
10
  export interface ILoaderDelegate {
10
11
  /**
@@ -179,6 +180,10 @@ export interface ISlideRenderOptions {
179
180
  */
180
181
  transitionResolutionLevel?: number;
181
182
  antialias?: boolean;
183
+ /** Maximum compressed bytes accepted for one client-decoded GIF. */
184
+ localGifMaxCompressedBytes?: number;
185
+ /** Maximum estimated GIF working set for one cached slide. */
186
+ localGifMaxWorkingSetBytes?: number;
182
187
  }
183
188
  export interface INavigatorDelegate {
184
189
  gotoPage?: (index: number) => void;
@@ -452,6 +457,7 @@ export declare class Slide extends Slide_base {
452
457
  private isAnimating;
453
458
  private renderingTaskManager;
454
459
  private isLoading;
460
+ private isReleasing;
455
461
  private interactive;
456
462
  private syncCatchUpInputBlocked;
457
463
  private anchor;