@ayden-fc2/riffle-bridge-web 1.0.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 +264 -0
- package/dist/index.d.mts +719 -0
- package/dist/index.d.ts +719 -0
- package/dist/index.js +942 -0
- package/dist/index.mjs +899 -0
- package/package.json +47 -0
- package/src/bridge.ts +157 -0
- package/src/controllers/index.ts +527 -0
- package/src/core.ts +236 -0
- package/src/index.ts +94 -0
- package/src/tweaks.ts +383 -0
- package/src/types.ts +295 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AudioController: () => AudioController,
|
|
24
|
+
BridgeCore: () => BridgeCore,
|
|
25
|
+
CameraController: () => CameraController,
|
|
26
|
+
ConfigController: () => ConfigController,
|
|
27
|
+
DeviceController: () => DeviceController,
|
|
28
|
+
FileStorageController: () => FileStorageController,
|
|
29
|
+
HapticController: () => HapticController,
|
|
30
|
+
MicrophoneController: () => MicrophoneController,
|
|
31
|
+
RiffleBridge: () => RiffleBridge,
|
|
32
|
+
RiffleBridgeTweaks: () => RiffleBridgeTweaks,
|
|
33
|
+
SensorController: () => SensorController,
|
|
34
|
+
TweakValue: () => TweakValue,
|
|
35
|
+
Utils: () => Utils,
|
|
36
|
+
createTweaks: () => createTweaks,
|
|
37
|
+
getBridgeCore: () => getBridgeCore,
|
|
38
|
+
getRiffleBridge: () => getRiffleBridge,
|
|
39
|
+
riffleBridgeTweaks: () => riffleBridgeTweaks
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(index_exports);
|
|
42
|
+
|
|
43
|
+
// src/core.ts
|
|
44
|
+
var TIMEOUT_CONFIG = {
|
|
45
|
+
fileStorage: {
|
|
46
|
+
downloadAndSave: 3e4,
|
|
47
|
+
downloadWithCache: 3e4,
|
|
48
|
+
takePhoto: 15e3,
|
|
49
|
+
pickFromGallery: 15e3,
|
|
50
|
+
default: 1e4
|
|
51
|
+
},
|
|
52
|
+
device: {
|
|
53
|
+
default: 8e3
|
|
54
|
+
},
|
|
55
|
+
default: {
|
|
56
|
+
default: 5e3
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function getTimeout(module2, method) {
|
|
60
|
+
const moduleConfig = TIMEOUT_CONFIG[module2] || TIMEOUT_CONFIG.default;
|
|
61
|
+
return moduleConfig[method] || moduleConfig.default || 5e3;
|
|
62
|
+
}
|
|
63
|
+
var BridgeCore = class {
|
|
64
|
+
constructor() {
|
|
65
|
+
this.messageId = 0;
|
|
66
|
+
this.initialized = false;
|
|
67
|
+
this.sensorHandlers = /* @__PURE__ */ new Map();
|
|
68
|
+
this.init();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 初始化 Bridge
|
|
72
|
+
*/
|
|
73
|
+
init() {
|
|
74
|
+
if (this.initialized) return;
|
|
75
|
+
if (!window.__RIFFLE_CALLBACKS__) {
|
|
76
|
+
window.__RIFFLE_CALLBACKS__ = /* @__PURE__ */ new Map();
|
|
77
|
+
}
|
|
78
|
+
window.handleNativeResponse = this.handleResponse.bind(this);
|
|
79
|
+
window.handleSensorData = this.handleSensorData.bind(this);
|
|
80
|
+
window.__RIFFLE_SENSOR_HANDLERS__ = this.sensorHandlers;
|
|
81
|
+
this.initialized = true;
|
|
82
|
+
console.log("[RiffleBridge] Core initialized");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 检查是否在 React Native WebView 环境中
|
|
86
|
+
*/
|
|
87
|
+
isAvailable() {
|
|
88
|
+
return typeof window !== "undefined" && typeof window.ReactNativeWebView !== "undefined";
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 等待 Bridge 就绪
|
|
92
|
+
*/
|
|
93
|
+
async waitForReady(timeout = 3e3) {
|
|
94
|
+
if (this.isAvailable()) return true;
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
const check = () => {
|
|
98
|
+
if (this.isAvailable()) {
|
|
99
|
+
resolve(true);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (Date.now() - startTime > timeout) {
|
|
103
|
+
resolve(false);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
setTimeout(check, 50);
|
|
107
|
+
};
|
|
108
|
+
check();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 确保在 React Native WebView 环境中运行
|
|
113
|
+
* @throws Error 如果不在 RN WebView 环境中
|
|
114
|
+
*/
|
|
115
|
+
ensureAvailable() {
|
|
116
|
+
if (!this.isAvailable()) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"[RiffleBridge] Not available. This SDK requires React Native WebView environment.\nPlease ensure:\n1. Your app is running inside a React Native WebView\n2. Native side has injected the minimal bridge script\n3. window.ReactNativeWebView is available"
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 发送消息到 Native
|
|
124
|
+
* @throws Error 如果不在 RN WebView 环境中
|
|
125
|
+
*/
|
|
126
|
+
async send(module2, method, params = {}) {
|
|
127
|
+
this.ensureAvailable();
|
|
128
|
+
const id = `msg_${++this.messageId}_${Date.now()}`;
|
|
129
|
+
const timeout = getTimeout(module2, method);
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
window.__RIFFLE_CALLBACKS__.set(id, (response) => {
|
|
132
|
+
if (response.success) {
|
|
133
|
+
resolve(response.data);
|
|
134
|
+
} else {
|
|
135
|
+
reject(new Error(response.error || "Unknown error"));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const message = {
|
|
139
|
+
id,
|
|
140
|
+
module: module2,
|
|
141
|
+
method,
|
|
142
|
+
params,
|
|
143
|
+
timestamp: Date.now()
|
|
144
|
+
};
|
|
145
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
if (window.__RIFFLE_CALLBACKS__.has(id)) {
|
|
148
|
+
window.__RIFFLE_CALLBACKS__.delete(id);
|
|
149
|
+
reject(new Error(`Request timeout (${timeout / 1e3}s) for ${module2}.${method}`));
|
|
150
|
+
}
|
|
151
|
+
}, timeout);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 处理 Native 响应
|
|
156
|
+
*/
|
|
157
|
+
handleResponse(responseStr) {
|
|
158
|
+
try {
|
|
159
|
+
const response = JSON.parse(responseStr);
|
|
160
|
+
const callback = window.__RIFFLE_CALLBACKS__?.get(response.id);
|
|
161
|
+
if (callback) {
|
|
162
|
+
window.__RIFFLE_CALLBACKS__.delete(response.id);
|
|
163
|
+
callback(response);
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error("[RiffleBridge] Failed to handle response:", error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 处理传感器数据
|
|
171
|
+
*/
|
|
172
|
+
handleSensorData(data) {
|
|
173
|
+
const { sensor, values } = data;
|
|
174
|
+
const handlers = this.sensorHandlers.get(sensor);
|
|
175
|
+
if (handlers) {
|
|
176
|
+
handlers.forEach((handler) => {
|
|
177
|
+
try {
|
|
178
|
+
handler(values);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error(`[RiffleBridge] Sensor callback error (${sensor}):`, error);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 订阅传感器数据
|
|
187
|
+
*/
|
|
188
|
+
onSensorData(sensor, callback) {
|
|
189
|
+
if (!this.sensorHandlers.has(sensor)) {
|
|
190
|
+
this.sensorHandlers.set(sensor, /* @__PURE__ */ new Set());
|
|
191
|
+
}
|
|
192
|
+
this.sensorHandlers.get(sensor).add(callback);
|
|
193
|
+
return () => {
|
|
194
|
+
this.sensorHandlers.get(sensor)?.delete(callback);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 清除所有传感器订阅
|
|
199
|
+
*/
|
|
200
|
+
clearSensorSubscriptions() {
|
|
201
|
+
this.sensorHandlers.clear();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var bridgeCoreInstance = null;
|
|
205
|
+
function getBridgeCore() {
|
|
206
|
+
if (!bridgeCoreInstance) {
|
|
207
|
+
bridgeCoreInstance = new BridgeCore();
|
|
208
|
+
}
|
|
209
|
+
return bridgeCoreInstance;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/controllers/index.ts
|
|
213
|
+
var HapticController = class {
|
|
214
|
+
constructor(core) {
|
|
215
|
+
this.core = core;
|
|
216
|
+
}
|
|
217
|
+
async light() {
|
|
218
|
+
await this.core.send("haptic", "light");
|
|
219
|
+
}
|
|
220
|
+
async medium() {
|
|
221
|
+
await this.core.send("haptic", "medium");
|
|
222
|
+
}
|
|
223
|
+
async heavy() {
|
|
224
|
+
await this.core.send("haptic", "heavy");
|
|
225
|
+
}
|
|
226
|
+
async success() {
|
|
227
|
+
await this.core.send("haptic", "success");
|
|
228
|
+
}
|
|
229
|
+
async warning() {
|
|
230
|
+
await this.core.send("haptic", "warning");
|
|
231
|
+
}
|
|
232
|
+
async error() {
|
|
233
|
+
await this.core.send("haptic", "error");
|
|
234
|
+
}
|
|
235
|
+
async selection() {
|
|
236
|
+
await this.core.send("haptic", "selection");
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 播放震动序列
|
|
240
|
+
*/
|
|
241
|
+
async sequence(types, interval = 200) {
|
|
242
|
+
for (let i = 0; i < types.length; i++) {
|
|
243
|
+
const type = types[i];
|
|
244
|
+
const method = this[type];
|
|
245
|
+
if (typeof method === "function") {
|
|
246
|
+
await method.call(this);
|
|
247
|
+
}
|
|
248
|
+
if (i < types.length - 1) {
|
|
249
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 根据强度震动
|
|
255
|
+
*/
|
|
256
|
+
async intensity(level) {
|
|
257
|
+
if (level <= 0.3) return this.light();
|
|
258
|
+
if (level <= 0.7) return this.medium();
|
|
259
|
+
return this.heavy();
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var SensorController = class {
|
|
263
|
+
constructor(core) {
|
|
264
|
+
this.core = core;
|
|
265
|
+
this.isActive = false;
|
|
266
|
+
this.activeTypes = [];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 启动传感器
|
|
270
|
+
*/
|
|
271
|
+
async start(options = {}) {
|
|
272
|
+
const {
|
|
273
|
+
types = ["accelerometer", "gyroscope", "magnetometer", "barometer"],
|
|
274
|
+
interval = 100
|
|
275
|
+
} = options;
|
|
276
|
+
const result = await this.core.send(
|
|
277
|
+
"sensors",
|
|
278
|
+
"start",
|
|
279
|
+
{ types, updateInterval: interval }
|
|
280
|
+
);
|
|
281
|
+
this.isActive = true;
|
|
282
|
+
this.activeTypes = types;
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* 停止传感器
|
|
287
|
+
*/
|
|
288
|
+
async stop() {
|
|
289
|
+
const result = await this.core.send("sensors", "stop");
|
|
290
|
+
this.isActive = false;
|
|
291
|
+
this.activeTypes = [];
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* 监听加速度计数据
|
|
296
|
+
*/
|
|
297
|
+
onAccelerometer(callback) {
|
|
298
|
+
return this.core.onSensorData("accelerometer", callback);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 监听陀螺仪数据
|
|
302
|
+
*/
|
|
303
|
+
onGyroscope(callback) {
|
|
304
|
+
return this.core.onSensorData("gyroscope", callback);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 监听磁力计数据
|
|
308
|
+
*/
|
|
309
|
+
onMagnetometer(callback) {
|
|
310
|
+
return this.core.onSensorData("magnetometer", callback);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 监听气压计数据
|
|
314
|
+
*/
|
|
315
|
+
onBarometer(callback) {
|
|
316
|
+
return this.core.onSensorData("barometer", callback);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 获取当前状态
|
|
320
|
+
*/
|
|
321
|
+
getStatus() {
|
|
322
|
+
return { isActive: this.isActive, activeTypes: this.activeTypes };
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
var DeviceController = class {
|
|
326
|
+
constructor(core) {
|
|
327
|
+
this.core = core;
|
|
328
|
+
}
|
|
329
|
+
async getDeviceInfo() {
|
|
330
|
+
return this.core.send("device", "getDeviceInfo");
|
|
331
|
+
}
|
|
332
|
+
async getBatteryInfo() {
|
|
333
|
+
return this.core.send("device", "getBatteryInfo");
|
|
334
|
+
}
|
|
335
|
+
async getNetworkInfo() {
|
|
336
|
+
return this.core.send("device", "getNetworkInfo");
|
|
337
|
+
}
|
|
338
|
+
async getSystemInfo() {
|
|
339
|
+
return this.core.send("device", "getSystemInfo");
|
|
340
|
+
}
|
|
341
|
+
async getAppInfo() {
|
|
342
|
+
return this.core.send("device", "getAppInfo");
|
|
343
|
+
}
|
|
344
|
+
async getScreenInfo() {
|
|
345
|
+
return this.core.send("device", "getScreenInfo");
|
|
346
|
+
}
|
|
347
|
+
async getAllInfo() {
|
|
348
|
+
return this.core.send("device", "getAllInfo");
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
var FileStorageController = class {
|
|
352
|
+
constructor(core) {
|
|
353
|
+
this.core = core;
|
|
354
|
+
}
|
|
355
|
+
async init() {
|
|
356
|
+
return this.core.send("fileStorage", "init");
|
|
357
|
+
}
|
|
358
|
+
async getStorageStats() {
|
|
359
|
+
return this.core.send("fileStorage", "getStorageStats");
|
|
360
|
+
}
|
|
361
|
+
async takePhoto() {
|
|
362
|
+
return this.core.send("fileStorage", "takePhoto");
|
|
363
|
+
}
|
|
364
|
+
async pickFromGallery() {
|
|
365
|
+
return this.core.send("fileStorage", "pickFromGallery");
|
|
366
|
+
}
|
|
367
|
+
async saveFile(sourceUri, filename, subfolder) {
|
|
368
|
+
return this.core.send("fileStorage", "saveFile", {
|
|
369
|
+
sourceUri,
|
|
370
|
+
filename,
|
|
371
|
+
subfolder
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
async listFiles(subfolder) {
|
|
375
|
+
return this.core.send("fileStorage", "listFiles", { subfolder });
|
|
376
|
+
}
|
|
377
|
+
async downloadAndSave(url, filename, subfolder) {
|
|
378
|
+
return this.core.send("fileStorage", "downloadAndSave", {
|
|
379
|
+
url,
|
|
380
|
+
downloadFilename: filename,
|
|
381
|
+
downloadSubfolder: subfolder
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
async getLocalFileByUrl(url) {
|
|
385
|
+
return this.core.send("fileStorage", "getLocalFileByUrl", {
|
|
386
|
+
checkUrl: url
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
async clearAppFiles() {
|
|
390
|
+
return this.core.send("fileStorage", "clearAppFiles");
|
|
391
|
+
}
|
|
392
|
+
async deleteFile(filename, subfolder) {
|
|
393
|
+
return this.core.send("fileStorage", "deleteFile", {
|
|
394
|
+
deleteFilename: filename,
|
|
395
|
+
deleteSubfolder: subfolder
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
async loadUrlMappings() {
|
|
399
|
+
return this.core.send("fileStorage", "loadUrlMappings");
|
|
400
|
+
}
|
|
401
|
+
async downloadWithCache(url, checkCache = true) {
|
|
402
|
+
return this.core.send("fileStorage", "downloadWithCache", {
|
|
403
|
+
downloadUrl: url,
|
|
404
|
+
checkCache
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
async clearUrlMappings() {
|
|
408
|
+
return this.core.send("fileStorage", "clearUrlMappings");
|
|
409
|
+
}
|
|
410
|
+
async removeUrlMapping(url) {
|
|
411
|
+
return this.core.send("fileStorage", "removeUrlMapping", { removeUrl: url });
|
|
412
|
+
}
|
|
413
|
+
async addCustomFileMapping(customKey, localUri) {
|
|
414
|
+
return this.core.send("fileStorage", "addCustomFileMapping", {
|
|
415
|
+
customKey,
|
|
416
|
+
localPath: localUri
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
async readAsBase64(uriOrKey) {
|
|
420
|
+
return this.core.send("fileStorage", "readAsBase64", { uriOrKey });
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var AudioController = class {
|
|
424
|
+
constructor(core, fileStorage) {
|
|
425
|
+
this.core = core;
|
|
426
|
+
this.fileStorage = fileStorage;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* 解析 URI,返回可播放的实际路径
|
|
430
|
+
*/
|
|
431
|
+
async resolveUri(uri) {
|
|
432
|
+
if (!uri) throw new Error("URI is required");
|
|
433
|
+
if (uri.startsWith("file://")) {
|
|
434
|
+
return uri;
|
|
435
|
+
}
|
|
436
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
437
|
+
const cached = await this.fileStorage.getLocalFileByUrl(uri);
|
|
438
|
+
if (cached?.exists && cached.uri) {
|
|
439
|
+
return cached.uri;
|
|
440
|
+
}
|
|
441
|
+
throw new Error(`File mapping not found: ${uri}`);
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
const cached = await this.fileStorage.getLocalFileByUrl(uri);
|
|
445
|
+
if (cached?.exists && cached.uri) {
|
|
446
|
+
return cached.uri;
|
|
447
|
+
}
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
return uri;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* 播放音频(智能解析 URI)
|
|
454
|
+
*/
|
|
455
|
+
async playOnline(options) {
|
|
456
|
+
const { uri, ...playOptions } = options;
|
|
457
|
+
let resolvedUri = await this.resolveUri(uri);
|
|
458
|
+
if (uri.startsWith("http") && resolvedUri === uri) {
|
|
459
|
+
try {
|
|
460
|
+
const downloadResult = await this.fileStorage.downloadWithCache(uri);
|
|
461
|
+
if (downloadResult?.uri) {
|
|
462
|
+
resolvedUri = downloadResult.uri;
|
|
463
|
+
}
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.warn("[AudioController] Cache failed, using online URL:", e);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return this.core.send("audio", "playOnline", {
|
|
469
|
+
...playOptions,
|
|
470
|
+
uri: resolvedUri
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
async pause() {
|
|
474
|
+
return this.core.send("audio", "pause");
|
|
475
|
+
}
|
|
476
|
+
async resume() {
|
|
477
|
+
return this.core.send("audio", "resume");
|
|
478
|
+
}
|
|
479
|
+
async stop() {
|
|
480
|
+
return this.core.send("audio", "stop");
|
|
481
|
+
}
|
|
482
|
+
async setVolume(volume) {
|
|
483
|
+
return this.core.send("audio", "setVolume", { volume });
|
|
484
|
+
}
|
|
485
|
+
async setRate(rate) {
|
|
486
|
+
return this.core.send("audio", "setRate", { rate });
|
|
487
|
+
}
|
|
488
|
+
async getStatus() {
|
|
489
|
+
return this.core.send("audio", "getStatus");
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
var CameraController = class {
|
|
493
|
+
constructor(core) {
|
|
494
|
+
this.core = core;
|
|
495
|
+
}
|
|
496
|
+
async open(options = {}) {
|
|
497
|
+
const { facing = "back" } = options;
|
|
498
|
+
return this.core.send("camera", "open", { facing });
|
|
499
|
+
}
|
|
500
|
+
async close() {
|
|
501
|
+
return this.core.send("camera", "close");
|
|
502
|
+
}
|
|
503
|
+
async toggleFacing() {
|
|
504
|
+
return this.core.send("camera", "toggleFacing");
|
|
505
|
+
}
|
|
506
|
+
async takePhoto() {
|
|
507
|
+
return this.core.send("camera", "takePhoto");
|
|
508
|
+
}
|
|
509
|
+
async setFilter(filter) {
|
|
510
|
+
return this.core.send("camera", "setFilter", { filter });
|
|
511
|
+
}
|
|
512
|
+
async setFlash(enabled) {
|
|
513
|
+
return this.core.send("camera", "setFlash", { enabled });
|
|
514
|
+
}
|
|
515
|
+
async toggleFlash() {
|
|
516
|
+
return this.core.send("camera", "toggleFlash");
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var MicrophoneController = class {
|
|
520
|
+
constructor(core) {
|
|
521
|
+
this.core = core;
|
|
522
|
+
this.isRecording = false;
|
|
523
|
+
}
|
|
524
|
+
async requestPermission() {
|
|
525
|
+
return this.core.send("microphone", "requestPermission");
|
|
526
|
+
}
|
|
527
|
+
async start(options = {}) {
|
|
528
|
+
const { enableMonitoring = true } = options;
|
|
529
|
+
const result = await this.core.send("microphone", "start", {
|
|
530
|
+
enableMonitoring
|
|
531
|
+
});
|
|
532
|
+
this.isRecording = true;
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
async stop() {
|
|
536
|
+
const result = await this.core.send("microphone", "stop");
|
|
537
|
+
this.isRecording = false;
|
|
538
|
+
return result;
|
|
539
|
+
}
|
|
540
|
+
async pause() {
|
|
541
|
+
return this.core.send("microphone", "pause");
|
|
542
|
+
}
|
|
543
|
+
async resume() {
|
|
544
|
+
return this.core.send("microphone", "resume");
|
|
545
|
+
}
|
|
546
|
+
async getStatus() {
|
|
547
|
+
return this.core.send("microphone", "getStatus");
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* 监听音量数据
|
|
551
|
+
*/
|
|
552
|
+
onVolumeData(callback) {
|
|
553
|
+
return this.core.onSensorData("microphone_volume", callback);
|
|
554
|
+
}
|
|
555
|
+
getRecordingState() {
|
|
556
|
+
return this.isRecording;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
var ConfigController = class {
|
|
560
|
+
constructor(core) {
|
|
561
|
+
this.core = core;
|
|
562
|
+
this.currentConfig = null;
|
|
563
|
+
}
|
|
564
|
+
async updateParams(configData) {
|
|
565
|
+
this.currentConfig = configData;
|
|
566
|
+
return this.core.send("config", "updateParams", configData);
|
|
567
|
+
}
|
|
568
|
+
getCurrentConfig() {
|
|
569
|
+
return this.currentConfig;
|
|
570
|
+
}
|
|
571
|
+
async resetToDefaults() {
|
|
572
|
+
return this.core.send("config", "resetToDefaults");
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/bridge.ts
|
|
577
|
+
var Utils = class {
|
|
578
|
+
/**
|
|
579
|
+
* 计算向量模长
|
|
580
|
+
*/
|
|
581
|
+
static magnitude(x, y, z) {
|
|
582
|
+
return Math.sqrt(x * x + y * y + z * z);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* 获取倾斜方向
|
|
586
|
+
*/
|
|
587
|
+
static getTiltDirection(x, y, threshold = 2) {
|
|
588
|
+
if (Math.abs(x) > Math.abs(y)) {
|
|
589
|
+
return x > threshold ? "right" : x < -threshold ? "left" : "center";
|
|
590
|
+
} else {
|
|
591
|
+
return y > threshold ? "forward" : y < -threshold ? "backward" : "center";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* 获取摇晃强度
|
|
596
|
+
*/
|
|
597
|
+
static getShakeIntensity(x, y, z) {
|
|
598
|
+
const magnitude = this.magnitude(x, y, z);
|
|
599
|
+
if (magnitude > 15) return "strong";
|
|
600
|
+
if (magnitude > 8) return "medium";
|
|
601
|
+
if (magnitude > 3) return "light";
|
|
602
|
+
return "none";
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
var RiffleBridge = class {
|
|
606
|
+
constructor() {
|
|
607
|
+
/** 工具函数 */
|
|
608
|
+
this.utils = Utils;
|
|
609
|
+
this.core = getBridgeCore();
|
|
610
|
+
this.haptic = new HapticController(this.core);
|
|
611
|
+
this.sensor = new SensorController(this.core);
|
|
612
|
+
this.device = new DeviceController(this.core);
|
|
613
|
+
this.fileStorage = new FileStorageController(this.core);
|
|
614
|
+
this.audio = new AudioController(this.core, this.fileStorage);
|
|
615
|
+
this.camera = new CameraController(this.core);
|
|
616
|
+
this.microphone = new MicrophoneController(this.core);
|
|
617
|
+
this.config = new ConfigController(this.core);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* 检查 Bridge 是否可用
|
|
621
|
+
*/
|
|
622
|
+
isAvailable() {
|
|
623
|
+
return this.core.isAvailable();
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* 等待 Bridge 就绪
|
|
627
|
+
*/
|
|
628
|
+
async waitForReady(timeout) {
|
|
629
|
+
return this.core.waitForReady(timeout);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* 获取 SDK 版本
|
|
633
|
+
*/
|
|
634
|
+
getVersion() {
|
|
635
|
+
return "1.0.0";
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* 便捷方法:震动
|
|
639
|
+
*/
|
|
640
|
+
async vibrate(type = "light") {
|
|
641
|
+
const method = this.haptic[type];
|
|
642
|
+
if (typeof method === "function") {
|
|
643
|
+
await method.call(this.haptic);
|
|
644
|
+
} else {
|
|
645
|
+
throw new Error(`Unsupported haptic type: ${type}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var riffleBridgeInstance = null;
|
|
650
|
+
function getRiffleBridge() {
|
|
651
|
+
if (!riffleBridgeInstance) {
|
|
652
|
+
riffleBridgeInstance = new RiffleBridge();
|
|
653
|
+
}
|
|
654
|
+
return riffleBridgeInstance;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/tweaks.ts
|
|
658
|
+
var TweakValue = class {
|
|
659
|
+
constructor(_key, _config, syncCallback = null) {
|
|
660
|
+
this._key = _key;
|
|
661
|
+
this._config = _config;
|
|
662
|
+
this._listeners = /* @__PURE__ */ new Set();
|
|
663
|
+
this._value = _config.value;
|
|
664
|
+
this._syncCallback = syncCallback;
|
|
665
|
+
}
|
|
666
|
+
/** 获取当前值 */
|
|
667
|
+
get value() {
|
|
668
|
+
return this._value;
|
|
669
|
+
}
|
|
670
|
+
/** 获取配置 */
|
|
671
|
+
get config() {
|
|
672
|
+
return this._config;
|
|
673
|
+
}
|
|
674
|
+
/** 获取键名 */
|
|
675
|
+
get key() {
|
|
676
|
+
return this._key;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* 设置值
|
|
680
|
+
* @param newValue 新值
|
|
681
|
+
* @param skipSync 是否跳过同步到 Native
|
|
682
|
+
*/
|
|
683
|
+
set(newValue, skipSync = false) {
|
|
684
|
+
if (this._value !== newValue) {
|
|
685
|
+
this._value = newValue;
|
|
686
|
+
this._notifyListeners();
|
|
687
|
+
if (!skipSync && this._syncCallback) {
|
|
688
|
+
this._syncCallback();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* 内部设置(不触发同步,用于接收 Native 更新)
|
|
694
|
+
*/
|
|
695
|
+
_setInternal(newValue) {
|
|
696
|
+
if (this._value !== newValue) {
|
|
697
|
+
this._value = newValue;
|
|
698
|
+
this._notifyListeners();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* 订阅值变化
|
|
703
|
+
*/
|
|
704
|
+
subscribe(listener) {
|
|
705
|
+
this._listeners.add(listener);
|
|
706
|
+
return () => this._listeners.delete(listener);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* 监听值变化
|
|
710
|
+
*/
|
|
711
|
+
onChange(callback) {
|
|
712
|
+
return this.subscribe(() => callback(this._value));
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* React Hook: 获取响应式值
|
|
716
|
+
* 需要传入 React 实例或使用 createTweaks 时传入
|
|
717
|
+
*/
|
|
718
|
+
useState(react) {
|
|
719
|
+
const ReactRef = react || _globalReact;
|
|
720
|
+
if (!ReactRef?.useSyncExternalStore) {
|
|
721
|
+
console.warn("[RiffleBridgeTweaks] React.useSyncExternalStore not available, returning static value");
|
|
722
|
+
return this._value;
|
|
723
|
+
}
|
|
724
|
+
return ReactRef.useSyncExternalStore(
|
|
725
|
+
(callback) => this.subscribe(callback),
|
|
726
|
+
() => this._value,
|
|
727
|
+
() => this._value
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
_notifyListeners() {
|
|
731
|
+
this._listeners.forEach((listener) => {
|
|
732
|
+
try {
|
|
733
|
+
listener();
|
|
734
|
+
} catch (e) {
|
|
735
|
+
console.error("[RiffleBridgeTweaks] Listener error:", e);
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
var _globalReact = null;
|
|
741
|
+
var RiffleBridgeTweaks = class {
|
|
742
|
+
constructor(config) {
|
|
743
|
+
this._tweaks = /* @__PURE__ */ new Map();
|
|
744
|
+
this._syncDebounceTimer = null;
|
|
745
|
+
this._config = config;
|
|
746
|
+
const debouncedSync = () => {
|
|
747
|
+
if (this._syncDebounceTimer) {
|
|
748
|
+
clearTimeout(this._syncDebounceTimer);
|
|
749
|
+
}
|
|
750
|
+
this._syncDebounceTimer = setTimeout(() => {
|
|
751
|
+
this._syncToNative();
|
|
752
|
+
}, 50);
|
|
753
|
+
};
|
|
754
|
+
for (const [key, cfg] of Object.entries(config)) {
|
|
755
|
+
this._tweaks.set(key, new TweakValue(key, cfg, debouncedSync));
|
|
756
|
+
}
|
|
757
|
+
this._setupMessageListener();
|
|
758
|
+
this._setupGlobalCallbacks();
|
|
759
|
+
this._syncToNative();
|
|
760
|
+
console.log("[RiffleBridgeTweaks] Initialized");
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* 获取指定键的配置项
|
|
764
|
+
*/
|
|
765
|
+
get(key) {
|
|
766
|
+
return this._tweaks.get(key);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* 获取所有值
|
|
770
|
+
*/
|
|
771
|
+
getData() {
|
|
772
|
+
const data = {};
|
|
773
|
+
for (const [key, tweak] of this._tweaks) {
|
|
774
|
+
data[key] = tweak.value;
|
|
775
|
+
}
|
|
776
|
+
return data;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* 获取完整配置(含元数据)
|
|
780
|
+
*/
|
|
781
|
+
toJSON() {
|
|
782
|
+
const result = {};
|
|
783
|
+
for (const [key, tweak] of this._tweaks) {
|
|
784
|
+
result[key] = { ...tweak.config, value: tweak.value };
|
|
785
|
+
}
|
|
786
|
+
return result;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* 批量更新值
|
|
790
|
+
*/
|
|
791
|
+
update(updates) {
|
|
792
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
793
|
+
const tweak = this._tweaks.get(key);
|
|
794
|
+
if (tweak) {
|
|
795
|
+
tweak.set(value);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
this._syncToNative();
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* 重置为默认值
|
|
802
|
+
*/
|
|
803
|
+
reset() {
|
|
804
|
+
for (const [key, tweak] of this._tweaks) {
|
|
805
|
+
tweak.set(this._config[key].value);
|
|
806
|
+
}
|
|
807
|
+
this._syncToNative();
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* 获取所有配置键名
|
|
811
|
+
*/
|
|
812
|
+
keys() {
|
|
813
|
+
return Array.from(this._tweaks.keys());
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* 监听任意参数变化
|
|
817
|
+
*/
|
|
818
|
+
onAnyChange(callback) {
|
|
819
|
+
const unsubscribes = [];
|
|
820
|
+
for (const [key, tweak] of this._tweaks) {
|
|
821
|
+
unsubscribes.push(tweak.onChange((value) => callback(key, value)));
|
|
822
|
+
}
|
|
823
|
+
return () => unsubscribes.forEach((unsub) => unsub());
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* 设置消息监听
|
|
827
|
+
*/
|
|
828
|
+
_setupMessageListener() {
|
|
829
|
+
if (typeof window === "undefined") return;
|
|
830
|
+
window.addEventListener("message", (event) => {
|
|
831
|
+
const data = event.data;
|
|
832
|
+
if (!data || typeof data !== "object") return;
|
|
833
|
+
const { type, payload } = data;
|
|
834
|
+
if (type === "tweak-update" && payload) {
|
|
835
|
+
const { key, value } = payload;
|
|
836
|
+
const tweak = this._tweaks.get(key);
|
|
837
|
+
if (tweak) {
|
|
838
|
+
tweak._setInternal(value);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (type === "tweaks-update" && payload) {
|
|
842
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
843
|
+
const tweak = this._tweaks.get(key);
|
|
844
|
+
if (tweak) {
|
|
845
|
+
tweak._setInternal(value);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* 设置全局回调函数(兼容旧版 Native 调用方式)
|
|
853
|
+
*/
|
|
854
|
+
_setupGlobalCallbacks() {
|
|
855
|
+
if (typeof window === "undefined") return;
|
|
856
|
+
const self = this;
|
|
857
|
+
window.updateConfigFromNative = function(key, value) {
|
|
858
|
+
const tweak = self._tweaks.get(key);
|
|
859
|
+
if (tweak) {
|
|
860
|
+
tweak._setInternal(value);
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
window.applyConfigFromNative = function(allData) {
|
|
864
|
+
for (const [key, value] of Object.entries(allData)) {
|
|
865
|
+
const tweak = self._tweaks.get(key);
|
|
866
|
+
if (tweak) {
|
|
867
|
+
tweak._setInternal(value);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
window.resetConfigToDefaults = function() {
|
|
872
|
+
for (const [key, tweak] of self._tweaks) {
|
|
873
|
+
tweak._setInternal(self._config[key].value);
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* 同步配置到 Native
|
|
879
|
+
*/
|
|
880
|
+
_syncToNative() {
|
|
881
|
+
try {
|
|
882
|
+
const bridge = getRiffleBridge();
|
|
883
|
+
if (!bridge.isAvailable()) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
bridge.config.updateParams({
|
|
887
|
+
tweaks: this.toJSON(),
|
|
888
|
+
data: this.getData(),
|
|
889
|
+
timestamp: Date.now()
|
|
890
|
+
});
|
|
891
|
+
} catch (e) {
|
|
892
|
+
console.error("[RiffleBridgeTweaks] Sync failed:", e);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
function createTweaks(config, options = {}) {
|
|
897
|
+
if (options.React) {
|
|
898
|
+
_globalReact = options.React;
|
|
899
|
+
}
|
|
900
|
+
const instance = new RiffleBridgeTweaks(config);
|
|
901
|
+
return new Proxy(instance, {
|
|
902
|
+
get(target, prop) {
|
|
903
|
+
if (prop in target) {
|
|
904
|
+
const value = target[prop];
|
|
905
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
906
|
+
}
|
|
907
|
+
return target.get(prop);
|
|
908
|
+
},
|
|
909
|
+
ownKeys(target) {
|
|
910
|
+
return target.keys();
|
|
911
|
+
},
|
|
912
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
913
|
+
if (target.keys().includes(prop)) {
|
|
914
|
+
return { enumerable: true, configurable: true };
|
|
915
|
+
}
|
|
916
|
+
return void 0;
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
function riffleBridgeTweaks(config, options = {}) {
|
|
921
|
+
return createTweaks(config, options);
|
|
922
|
+
}
|
|
923
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
924
|
+
0 && (module.exports = {
|
|
925
|
+
AudioController,
|
|
926
|
+
BridgeCore,
|
|
927
|
+
CameraController,
|
|
928
|
+
ConfigController,
|
|
929
|
+
DeviceController,
|
|
930
|
+
FileStorageController,
|
|
931
|
+
HapticController,
|
|
932
|
+
MicrophoneController,
|
|
933
|
+
RiffleBridge,
|
|
934
|
+
RiffleBridgeTweaks,
|
|
935
|
+
SensorController,
|
|
936
|
+
TweakValue,
|
|
937
|
+
Utils,
|
|
938
|
+
createTweaks,
|
|
939
|
+
getBridgeCore,
|
|
940
|
+
getRiffleBridge,
|
|
941
|
+
riffleBridgeTweaks
|
|
942
|
+
});
|