@netless/slide 1.4.56 → 1.4.58-alpha.0

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,22 @@
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`,不会修改调用方原有的本地交互权限。
5
+ ## changelog
30
6
 
31
- 该策略只在 Slide 的 `mode: "interactive"` 下生效;`sync`、`local` 和 `addLink` mode 会强制使用 `fifo`。这项 mode 保护不能替代调用方的只读权限与 feature flag 判断。
7
+ ### 1.4.58 (2026-09-07)
8
+ * 修复低版本 WebView 中纯色图形显示为黑块的问题
9
+ * 修复切页或销毁 Stage 后无限循环动画回调仍访问已释放 Pixi 对象导致的异常
10
+ * 完善渲染任务取消、失败后的清理与队列推进
32
11
 
33
- 实际发生压缩时,SDK 会通过现有 logger/tracker 记录丢弃分类、保留页码、目标事件接收时间、对应渲染完成时间和追赶输入锁状态;记录中不包含事件 UUID、资源 URL 或 token。
12
+ ### 1.4.57 (2026-08-27)
13
+ * 支持在客户端解码并播放本地 GIF 资源,兼容旧版 Safari
14
+ * 修复 WebGL 切页转场被错误关闭的问题
34
15
 
35
- ## changelog
16
+ ### 1.4.56 (2026-08-25)
17
+ * 修复冻结 (frozen) 恢复 (release) 时画面闪烁问题
18
+ * 完善 Canvas/WebGL 播放器销毁清理,避免状态切换后残留多个 canvas
36
19
 
37
- ### 未发布
20
+ ### 1.4.55 (2026-08-21)
38
21
  * 增加 `latest-pending-render` 同步队列策略,弱网积压时丢弃最后翻页前尚未执行的中间页面操作,并在追赶期间阻止本地输入
39
22
  * 修复 PPT 图形内阴影(`innerShadow`)被错误渲染为不透明黑边的问题
40
23
  * 按转换数据中的颜色、模糊半径、距离和方向渲染柔化内阴影
@@ -1,4 +1,8 @@
1
1
  import EventEmitter from "eventemitter3";
2
+ export declare class RenderTaskCancelledError extends Error {
3
+ readonly code: string;
4
+ constructor(message?: string);
5
+ }
2
6
  export declare class Task {
3
7
  state: "idle" | "start" | "end";
4
8
  index: number;
@@ -6,16 +10,20 @@ export declare class Task {
6
10
  private fn;
7
11
  private eventHub;
8
12
  id: string;
13
+ private cancelled;
9
14
  constructor(index: number, slideIndex: number, fn: () => Promise<void>, eventHub: EventEmitter, id: string);
10
15
  apply(): Promise<void>;
16
+ cancel(): void;
11
17
  }
12
18
  export declare class RenderingTaskManager {
13
19
  eventHub: EventEmitter;
14
20
  private tasks;
15
21
  private index;
22
+ private destroyed;
16
23
  constructor();
24
+ private cancelTask;
17
25
  private replaceIdleTask;
18
- addTask(fn: () => Promise<void>, slideIndex: number, id: string): void;
26
+ addTask(fn: () => Promise<void>, slideIndex: number, id: string): Task | undefined;
19
27
  hasStartTask(): boolean;
20
28
  destroy(): void;
21
29
  }
@@ -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,25 @@ 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
+ }
105
+ if (e_1 instanceof RenderTaskCancelledError) {
106
+ this.cancel();
107
+ this.eventHub.emit("task-cancel", {
108
+ task: this,
109
+ error: e_1,
110
+ });
111
+ return [2 /*return*/];
112
+ }
68
113
  this.eventHub.emit("task-error", {
69
114
  task: this,
70
115
  error: e_1,
@@ -75,6 +120,9 @@ var Task = /** @class */ (function () {
75
120
  });
76
121
  });
77
122
  };
123
+ Task.prototype.cancel = function () {
124
+ this.cancelled = true;
125
+ };
78
126
  return Task;
79
127
  }());
80
128
  export { Task };
@@ -84,6 +132,7 @@ var RenderingTaskManager = /** @class */ (function () {
84
132
  this.eventHub = new EventEmitter();
85
133
  this.tasks = [];
86
134
  this.index = 0;
135
+ this.destroyed = false;
87
136
  this.eventHub.on("task-end", function (task) {
88
137
  var _a;
89
138
  var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
@@ -98,15 +147,38 @@ var RenderingTaskManager = /** @class */ (function () {
98
147
  _this.eventHub.emit("task-end-" + task.id);
99
148
  });
100
149
  this.eventHub.on("task-error", function (_a) {
101
- var task = _a.task;
150
+ var _b;
151
+ var task = _a.task, error = _a.error;
102
152
  var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
153
+ var wasRunning = task.state === "start";
103
154
  if (selfIndex >= 0) {
104
155
  _this.tasks.splice(selfIndex, 1);
105
156
  _this.replaceIdleTask();
157
+ if (wasRunning && !_this.destroyed) {
158
+ (_b = _this.tasks[selfIndex]) === null || _b === void 0 ? void 0 : _b.apply();
159
+ }
106
160
  }
107
- _this.eventHub.emit("task-error-" + task.id);
161
+ _this.eventHub.emit("task-error-" + task.id, error);
162
+ });
163
+ this.eventHub.on("task-cancel", function (_a) {
164
+ var _b;
165
+ var task = _a.task, error = _a.error;
166
+ var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
167
+ var wasRunning = task.state === "start";
168
+ if (selfIndex >= 0) {
169
+ _this.tasks.splice(selfIndex, 1);
170
+ if (wasRunning && !_this.destroyed) {
171
+ (_b = _this.tasks[selfIndex]) === null || _b === void 0 ? void 0 : _b.apply();
172
+ }
173
+ }
174
+ _this.eventHub.emit("task-cancel-" + task.id, error);
108
175
  });
109
176
  }
177
+ RenderingTaskManager.prototype.cancelTask = function (task) {
178
+ var error = new RenderTaskCancelledError();
179
+ task.cancel();
180
+ this.eventHub.emit("task-cancel", { task: task, error: error });
181
+ };
110
182
  RenderingTaskManager.prototype.replaceIdleTask = function () {
111
183
  var _this = this;
112
184
  var _a;
@@ -116,11 +188,19 @@ var RenderingTaskManager = /** @class */ (function () {
116
188
  ids.add(i);
117
189
  }
118
190
  }
119
- Array.from(ids).forEach(function (id) {
120
- _this.tasks.splice(id, 1);
191
+ // Remove from the end so deleting one idle task does not shift the
192
+ // indexes selected for the remaining cancellations.
193
+ Array.from(ids).sort(function (a, b) { return b - a; }).forEach(function (id) {
194
+ var task = _this.tasks.splice(id, 1)[0];
195
+ if (task) {
196
+ _this.cancelTask(task);
197
+ }
121
198
  });
122
199
  };
123
200
  RenderingTaskManager.prototype.addTask = function (fn, slideIndex, id) {
201
+ if (this.destroyed) {
202
+ return undefined;
203
+ }
124
204
  var task = new Task(this.index++, slideIndex, fn, this.eventHub, id);
125
205
  this.tasks.push(task);
126
206
  this.tasks.sort(function (a, b) { return a.index - b.index; });
@@ -128,11 +208,21 @@ var RenderingTaskManager = /** @class */ (function () {
128
208
  if (this.tasks.length === 1) {
129
209
  task.apply();
130
210
  }
211
+ return task;
131
212
  };
132
213
  RenderingTaskManager.prototype.hasStartTask = function () {
133
214
  return this.tasks.some(function (t) { return t.state === "start"; });
134
215
  };
135
216
  RenderingTaskManager.prototype.destroy = function () {
217
+ var _this = this;
218
+ if (this.destroyed) {
219
+ return;
220
+ }
221
+ this.destroyed = true;
222
+ var tasks = this.tasks.splice(0);
223
+ tasks.forEach(function (task) {
224
+ _this.cancelTask(task);
225
+ });
136
226
  this.eventHub.removeAllListeners();
137
227
  };
138
228
  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;
@@ -451,6 +456,9 @@ export declare class Slide extends Slide_base {
451
456
  private resize;
452
457
  private isAnimating;
453
458
  private renderingTaskManager;
459
+ private lifecycle;
460
+ private lifecycleGeneration;
461
+ private renderVisibilityTimers;
454
462
  private isLoading;
455
463
  private isReleasing;
456
464
  private interactive;
@@ -498,6 +506,7 @@ export declare class Slide extends Slide_base {
498
506
  private handleSyncQueueCompactRenderComplete;
499
507
  private handleSyncQueueCatchUpStateChange;
500
508
  private updatePlayerInteractive;
509
+ private observeRenderTask;
501
510
  private recoverHandler;
502
511
  private reportError;
503
512
  initSlideConfig(config: ISlideConfig): ISlideConfig;