@vortexm/vjt 0.1.14 → 0.1.16
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.js +537 -89
- package/dist/lib/network.d.ts +5 -2
- package/dist/lib/types.d.ts +5 -0
- package/dist/lib/voice-runtime.d.ts +12 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3865,7 +3865,7 @@ var NetworkRuntime = class {
|
|
|
3865
3865
|
this.rerenderRoot = options.rerenderRoot;
|
|
3866
3866
|
}
|
|
3867
3867
|
connectSseStreams() {
|
|
3868
|
-
if (typeof window === "undefined" ||
|
|
3868
|
+
if (typeof window === "undefined" || this.eventSources.length > 0) {
|
|
3869
3869
|
return;
|
|
3870
3870
|
}
|
|
3871
3871
|
for (const config of this.sseConfigs) {
|
|
@@ -3877,26 +3877,10 @@ var NetworkRuntime = class {
|
|
|
3877
3877
|
logRuntimeError("sseConfig", new Error("Blocked unsafe SSE url"), { url: config.url });
|
|
3878
3878
|
continue;
|
|
3879
3879
|
}
|
|
3880
|
-
const
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
if (!eventDefinition?.name) {
|
|
3884
|
-
continue;
|
|
3885
|
-
}
|
|
3886
|
-
source.addEventListener(eventDefinition.name, (event) => {
|
|
3887
|
-
void this.handleSseEvent(eventDefinition, event).catch((error) => {
|
|
3888
|
-
logRuntimeError("handleSseEvent", error, {
|
|
3889
|
-
eventName: eventDefinition.name,
|
|
3890
|
-
url: safeUrl
|
|
3891
|
-
});
|
|
3892
|
-
});
|
|
3893
|
-
});
|
|
3880
|
+
const connection = this.createSseConnection(config, safeUrl);
|
|
3881
|
+
if (connection) {
|
|
3882
|
+
this.eventSources.push(connection);
|
|
3894
3883
|
}
|
|
3895
|
-
source.onerror = (event) => {
|
|
3896
|
-
logRuntimeError("sseConnection", event, {
|
|
3897
|
-
url: config.url
|
|
3898
|
-
});
|
|
3899
|
-
};
|
|
3900
3884
|
}
|
|
3901
3885
|
}
|
|
3902
3886
|
async executeRequest(requestName, currentValue) {
|
|
@@ -4099,21 +4083,297 @@ var NetworkRuntime = class {
|
|
|
4099
4083
|
return this.stringifyPrimitive(value);
|
|
4100
4084
|
}
|
|
4101
4085
|
}
|
|
4102
|
-
|
|
4086
|
+
createSseConnection(config, safeUrl) {
|
|
4087
|
+
if (typeof fetch === "function" && typeof AbortController !== "undefined" && typeof TextDecoder !== "undefined" && typeof ReadableStream !== "undefined") {
|
|
4088
|
+
return this.createFetchSseConnection(config, safeUrl);
|
|
4089
|
+
}
|
|
4090
|
+
if (typeof EventSource !== "undefined") {
|
|
4091
|
+
return this.createNativeSseConnection(config, safeUrl);
|
|
4092
|
+
}
|
|
4093
|
+
logRuntimeError("sseConnection", new Error("SSE is not supported in this browser"), {
|
|
4094
|
+
url: safeUrl
|
|
4095
|
+
});
|
|
4096
|
+
return null;
|
|
4097
|
+
}
|
|
4098
|
+
createNativeSseConnection(config, safeUrl) {
|
|
4099
|
+
const source = new EventSource(safeUrl);
|
|
4100
|
+
source.onopen = () => {
|
|
4101
|
+
logRuntimeDebug(this.debugLogging, "sse-open", {
|
|
4102
|
+
url: safeUrl,
|
|
4103
|
+
transport: "event-source"
|
|
4104
|
+
});
|
|
4105
|
+
};
|
|
4106
|
+
source.onmessage = (event) => {
|
|
4107
|
+
const payload = typeof event.data === "string" ? event.data : "";
|
|
4108
|
+
logRuntimeDebug(this.debugLogging, "sse-message", {
|
|
4109
|
+
url: safeUrl,
|
|
4110
|
+
transport: "event-source",
|
|
4111
|
+
eventName: "message",
|
|
4112
|
+
payloadLength: payload.length
|
|
4113
|
+
});
|
|
4114
|
+
};
|
|
4115
|
+
for (const eventDefinition of config.events ?? []) {
|
|
4116
|
+
if (!eventDefinition?.name) {
|
|
4117
|
+
continue;
|
|
4118
|
+
}
|
|
4119
|
+
source.addEventListener(eventDefinition.name, (event) => {
|
|
4120
|
+
void this.handleSseEvent(eventDefinition, event.data ?? "", safeUrl).catch((error) => {
|
|
4121
|
+
logRuntimeError("handleSseEvent", error, {
|
|
4122
|
+
eventName: eventDefinition.name,
|
|
4123
|
+
url: safeUrl
|
|
4124
|
+
});
|
|
4125
|
+
});
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
source.onerror = (event) => {
|
|
4129
|
+
logRuntimeError("sseConnection", event, {
|
|
4130
|
+
url: config.url,
|
|
4131
|
+
transport: "event-source"
|
|
4132
|
+
});
|
|
4133
|
+
};
|
|
4134
|
+
logRuntimeDebug(this.debugLogging, "sse-connect", {
|
|
4135
|
+
url: safeUrl,
|
|
4136
|
+
transport: "event-source"
|
|
4137
|
+
});
|
|
4138
|
+
return {
|
|
4139
|
+
close: () => {
|
|
4140
|
+
source.close();
|
|
4141
|
+
}
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
createFetchSseConnection(config, safeUrl) {
|
|
4145
|
+
let closed = false;
|
|
4146
|
+
let reconnectDelayMs = 2e3;
|
|
4147
|
+
let reconnectTimeoutId = null;
|
|
4148
|
+
let activeAbortController = null;
|
|
4149
|
+
let lastEventId = "";
|
|
4150
|
+
let connectionAttempt = 0;
|
|
4151
|
+
const clearReconnectTimeout = () => {
|
|
4152
|
+
if (reconnectTimeoutId !== null && typeof window !== "undefined") {
|
|
4153
|
+
window.clearTimeout(reconnectTimeoutId);
|
|
4154
|
+
}
|
|
4155
|
+
reconnectTimeoutId = null;
|
|
4156
|
+
};
|
|
4157
|
+
const scheduleReconnect = (reason) => {
|
|
4158
|
+
if (closed || typeof window === "undefined") {
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
logRuntimeDebug(this.debugLogging, "sse-reconnect-scheduled", {
|
|
4162
|
+
url: safeUrl,
|
|
4163
|
+
transport: "fetch",
|
|
4164
|
+
delayMs: reconnectDelayMs,
|
|
4165
|
+
reason,
|
|
4166
|
+
lastEventId
|
|
4167
|
+
});
|
|
4168
|
+
clearReconnectTimeout();
|
|
4169
|
+
reconnectTimeoutId = window.setTimeout(() => {
|
|
4170
|
+
reconnectTimeoutId = null;
|
|
4171
|
+
void connectLoop().catch((error) => {
|
|
4172
|
+
logRuntimeError("sseConnection", error, {
|
|
4173
|
+
url: safeUrl,
|
|
4174
|
+
transport: "fetch"
|
|
4175
|
+
});
|
|
4176
|
+
});
|
|
4177
|
+
}, reconnectDelayMs);
|
|
4178
|
+
};
|
|
4179
|
+
const flushSseChunk = (eventName, dataLines) => {
|
|
4180
|
+
if (!dataLines.length) {
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
const payload = dataLines.join("\n");
|
|
4184
|
+
const matchingDefinitions = (config.events ?? []).filter((definition) => definition?.name === eventName);
|
|
4185
|
+
logRuntimeDebug(this.debugLogging, "sse-dispatch", {
|
|
4186
|
+
url: safeUrl,
|
|
4187
|
+
transport: "fetch",
|
|
4188
|
+
eventName,
|
|
4189
|
+
payloadLength: payload.length,
|
|
4190
|
+
matchedHandlers: matchingDefinitions.length,
|
|
4191
|
+
lastEventId
|
|
4192
|
+
});
|
|
4193
|
+
if (!matchingDefinitions.length) {
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
for (const eventDefinition of matchingDefinitions) {
|
|
4197
|
+
void this.handleSseEvent(eventDefinition, payload, safeUrl).catch((error) => {
|
|
4198
|
+
logRuntimeError("handleSseEvent", error, {
|
|
4199
|
+
eventName,
|
|
4200
|
+
url: safeUrl
|
|
4201
|
+
});
|
|
4202
|
+
});
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
const processSseText = (chunk) => {
|
|
4206
|
+
let eventName = "message";
|
|
4207
|
+
let dataLines = [];
|
|
4208
|
+
const normalized = chunk.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
4209
|
+
const lines = normalized.split("\n");
|
|
4210
|
+
for (const line of lines) {
|
|
4211
|
+
if (!line) {
|
|
4212
|
+
flushSseChunk(eventName, dataLines);
|
|
4213
|
+
eventName = "message";
|
|
4214
|
+
dataLines = [];
|
|
4215
|
+
continue;
|
|
4216
|
+
}
|
|
4217
|
+
if (line.startsWith(":")) {
|
|
4218
|
+
continue;
|
|
4219
|
+
}
|
|
4220
|
+
const separatorIndex = line.indexOf(":");
|
|
4221
|
+
const field = separatorIndex >= 0 ? line.slice(0, separatorIndex) : line;
|
|
4222
|
+
let value = separatorIndex >= 0 ? line.slice(separatorIndex + 1) : "";
|
|
4223
|
+
if (value.startsWith(" ")) {
|
|
4224
|
+
value = value.slice(1);
|
|
4225
|
+
}
|
|
4226
|
+
switch (field) {
|
|
4227
|
+
case "event":
|
|
4228
|
+
eventName = value || "message";
|
|
4229
|
+
break;
|
|
4230
|
+
case "data":
|
|
4231
|
+
dataLines.push(value);
|
|
4232
|
+
break;
|
|
4233
|
+
case "id":
|
|
4234
|
+
if (!value.includes("\0")) {
|
|
4235
|
+
lastEventId = value;
|
|
4236
|
+
}
|
|
4237
|
+
break;
|
|
4238
|
+
case "retry": {
|
|
4239
|
+
const parsedDelay = Number.parseInt(value, 10);
|
|
4240
|
+
if (Number.isFinite(parsedDelay) && parsedDelay >= 0) {
|
|
4241
|
+
reconnectDelayMs = parsedDelay;
|
|
4242
|
+
}
|
|
4243
|
+
break;
|
|
4244
|
+
}
|
|
4245
|
+
default:
|
|
4246
|
+
break;
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
flushSseChunk(eventName, dataLines);
|
|
4250
|
+
};
|
|
4251
|
+
const findEventBoundary = (value) => value.search(/\r?\n\r?\n/);
|
|
4252
|
+
const connectLoop = async () => {
|
|
4253
|
+
if (closed) {
|
|
4254
|
+
return;
|
|
4255
|
+
}
|
|
4256
|
+
connectionAttempt += 1;
|
|
4257
|
+
activeAbortController = new AbortController();
|
|
4258
|
+
let reconnectReason = "stream-ended";
|
|
4259
|
+
try {
|
|
4260
|
+
logRuntimeDebug(this.debugLogging, "sse-connect-attempt", {
|
|
4261
|
+
url: safeUrl,
|
|
4262
|
+
transport: "fetch",
|
|
4263
|
+
attempt: connectionAttempt,
|
|
4264
|
+
lastEventId
|
|
4265
|
+
});
|
|
4266
|
+
const headers = {
|
|
4267
|
+
Accept: "text/event-stream"
|
|
4268
|
+
};
|
|
4269
|
+
if (lastEventId) {
|
|
4270
|
+
headers["Last-Event-ID"] = lastEventId;
|
|
4271
|
+
}
|
|
4272
|
+
const response = await fetch(safeUrl, {
|
|
4273
|
+
method: "GET",
|
|
4274
|
+
headers,
|
|
4275
|
+
cache: "no-store",
|
|
4276
|
+
credentials: "same-origin",
|
|
4277
|
+
signal: activeAbortController.signal
|
|
4278
|
+
});
|
|
4279
|
+
if (!response.ok) {
|
|
4280
|
+
throw new Error(`SSE connection failed: ${response.status} ${response.statusText}`);
|
|
4281
|
+
}
|
|
4282
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
4283
|
+
if (!contentType.toLowerCase().includes("text/event-stream")) {
|
|
4284
|
+
throw new Error(`SSE response has unexpected content-type: ${contentType || "<empty>"}`);
|
|
4285
|
+
}
|
|
4286
|
+
logRuntimeDebug(this.debugLogging, "sse-open", {
|
|
4287
|
+
url: safeUrl,
|
|
4288
|
+
transport: "fetch",
|
|
4289
|
+
status: response.status,
|
|
4290
|
+
contentType,
|
|
4291
|
+
attempt: connectionAttempt,
|
|
4292
|
+
lastEventId,
|
|
4293
|
+
requestUsesLastEventIdHeader: Boolean(lastEventId)
|
|
4294
|
+
});
|
|
4295
|
+
if (!response.body || typeof response.body.getReader !== "function") {
|
|
4296
|
+
throw new Error("SSE streaming is not available in fetch response body");
|
|
4297
|
+
}
|
|
4298
|
+
const reader = response.body.getReader();
|
|
4299
|
+
const decoder = new TextDecoder();
|
|
4300
|
+
let buffer = "";
|
|
4301
|
+
let receivedChunkCount = 0;
|
|
4302
|
+
let receivedByteCount = 0;
|
|
4303
|
+
while (!closed) {
|
|
4304
|
+
const { done, value } = await reader.read();
|
|
4305
|
+
if (done) {
|
|
4306
|
+
break;
|
|
4307
|
+
}
|
|
4308
|
+
receivedChunkCount += 1;
|
|
4309
|
+
receivedByteCount += value?.byteLength ?? 0;
|
|
4310
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4311
|
+
logRuntimeDebug(this.debugLogging, "sse-chunk", {
|
|
4312
|
+
url: safeUrl,
|
|
4313
|
+
transport: "fetch",
|
|
4314
|
+
chunkIndex: receivedChunkCount,
|
|
4315
|
+
chunkBytes: value?.byteLength ?? 0,
|
|
4316
|
+
totalBytes: receivedByteCount,
|
|
4317
|
+
bufferedChars: buffer.length
|
|
4318
|
+
});
|
|
4319
|
+
let boundaryIndex = findEventBoundary(buffer);
|
|
4320
|
+
while (boundaryIndex >= 0) {
|
|
4321
|
+
const rawChunk = buffer.slice(0, boundaryIndex);
|
|
4322
|
+
processSseText(rawChunk);
|
|
4323
|
+
buffer = buffer.slice(boundaryIndex).replace(/^\r?\n\r?\n/, "");
|
|
4324
|
+
boundaryIndex = findEventBoundary(buffer);
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
buffer += decoder.decode();
|
|
4328
|
+
if (buffer.trim()) {
|
|
4329
|
+
processSseText(buffer);
|
|
4330
|
+
}
|
|
4331
|
+
} catch (error) {
|
|
4332
|
+
if (closed || error instanceof DOMException && error.name === "AbortError") {
|
|
4333
|
+
return;
|
|
4334
|
+
}
|
|
4335
|
+
reconnectReason = error instanceof Error ? error.message : "stream-error";
|
|
4336
|
+
logRuntimeError("sseConnection", error, {
|
|
4337
|
+
url: safeUrl,
|
|
4338
|
+
transport: "fetch"
|
|
4339
|
+
});
|
|
4340
|
+
} finally {
|
|
4341
|
+
activeAbortController = null;
|
|
4342
|
+
}
|
|
4343
|
+
scheduleReconnect(reconnectReason);
|
|
4344
|
+
};
|
|
4345
|
+
const connection = {
|
|
4346
|
+
close: () => {
|
|
4347
|
+
closed = true;
|
|
4348
|
+
clearReconnectTimeout();
|
|
4349
|
+
activeAbortController?.abort();
|
|
4350
|
+
activeAbortController = null;
|
|
4351
|
+
}
|
|
4352
|
+
};
|
|
4353
|
+
void connectLoop().catch((error) => {
|
|
4354
|
+
logRuntimeError("sseConnection", error, {
|
|
4355
|
+
url: safeUrl,
|
|
4356
|
+
transport: "fetch"
|
|
4357
|
+
});
|
|
4358
|
+
});
|
|
4359
|
+
return connection;
|
|
4360
|
+
}
|
|
4361
|
+
async handleSseEvent(eventDefinition, rawData, url) {
|
|
4103
4362
|
let parsedMessage = {};
|
|
4104
4363
|
try {
|
|
4105
|
-
parsedMessage =
|
|
4364
|
+
parsedMessage = rawData ? JSON.parse(rawData) : {};
|
|
4106
4365
|
} catch {
|
|
4107
|
-
parsedMessage = { value:
|
|
4366
|
+
parsedMessage = { value: rawData ?? "" };
|
|
4108
4367
|
}
|
|
4109
4368
|
const message = eventDefinition.message ? sanitizeSchemaValue(eventDefinition.message, parsedMessage, `sse.${eventDefinition.name}`) : (() => {
|
|
4110
4369
|
throw new Error(`SSE event ${eventDefinition.name} must define message schema`);
|
|
4111
4370
|
})();
|
|
4112
4371
|
logRuntimeDebug(this.debugLogging, "sse", {
|
|
4113
4372
|
eventName: eventDefinition.name,
|
|
4114
|
-
raw:
|
|
4373
|
+
raw: rawData,
|
|
4115
4374
|
parsedMessage,
|
|
4116
|
-
message
|
|
4375
|
+
message,
|
|
4376
|
+
url
|
|
4117
4377
|
});
|
|
4118
4378
|
if (!eventDefinition.onEvent?.length) {
|
|
4119
4379
|
return;
|
|
@@ -4479,6 +4739,10 @@ var ReferenceRuntime = class {
|
|
|
4479
4739
|
return readPath(state, ["value"]);
|
|
4480
4740
|
case "isHidden":
|
|
4481
4741
|
return state.isHidden ?? true;
|
|
4742
|
+
case "isScrolledToTop":
|
|
4743
|
+
return state.isScrolledToTop ?? true;
|
|
4744
|
+
case "isScrolledToBottom":
|
|
4745
|
+
return state.isScrolledToBottom ?? true;
|
|
4482
4746
|
case "url":
|
|
4483
4747
|
return resolveInlineI18nValue(this.host, state.url ?? "");
|
|
4484
4748
|
case "base64":
|
|
@@ -5869,8 +6133,9 @@ var MAX_RECORDING_MS = 10 * 60 * 1e3;
|
|
|
5869
6133
|
var MAX_LISTENING_SILENCE_MS = 60 * 60 * 1e3;
|
|
5870
6134
|
var SILENCE_STOP_MS = 2e3;
|
|
5871
6135
|
var DEFAULT_SPEECH_THRESHOLD = 0.035;
|
|
5872
|
-
var
|
|
5873
|
-
var
|
|
6136
|
+
var LISTENING_TARGET_PRE_ROLL_MS = 300;
|
|
6137
|
+
var LISTENING_SEGMENT_MS = 1e3;
|
|
6138
|
+
var LISTENING_SEGMENT_STEP_MS = 500;
|
|
5874
6139
|
var RECORDING_MIME_CANDIDATES = [
|
|
5875
6140
|
"audio/webm;codecs=opus",
|
|
5876
6141
|
"audio/webm",
|
|
@@ -5951,10 +6216,10 @@ var VoiceRuntime = class {
|
|
|
5951
6216
|
listenAnalyser = null;
|
|
5952
6217
|
listenSource = null;
|
|
5953
6218
|
listenFrameId = null;
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
6219
|
+
listeningIdleSessions = [];
|
|
6220
|
+
listeningLaneTimeoutIds = [];
|
|
6221
|
+
listeningLaneIntervalIds = [];
|
|
6222
|
+
listeningPromotedSession = null;
|
|
5958
6223
|
listeningCaptureActive = false;
|
|
5959
6224
|
lastSpeechAt = 0;
|
|
5960
6225
|
listeningStartedAt = 0;
|
|
@@ -6076,14 +6341,16 @@ var VoiceRuntime = class {
|
|
|
6076
6341
|
analyser.fftSize = 2048;
|
|
6077
6342
|
const source = context.createMediaStreamSource(stream);
|
|
6078
6343
|
source.connect(analyser);
|
|
6079
|
-
this.startListeningRecorder(stream);
|
|
6080
6344
|
this.listening = true;
|
|
6081
6345
|
this.listenContext = context;
|
|
6082
6346
|
this.listenAnalyser = analyser;
|
|
6083
6347
|
this.listenSource = source;
|
|
6348
|
+
this.listeningIdleSessions = [];
|
|
6349
|
+
this.listeningPromotedSession = null;
|
|
6084
6350
|
this.lastSpeechAt = 0;
|
|
6085
6351
|
this.listeningStartedAt = performance.now();
|
|
6086
6352
|
this.speechEventTriggered = false;
|
|
6353
|
+
this.startListeningRecorderSchedule(stream);
|
|
6087
6354
|
const sampleBuffer = new Uint8Array(analyser.fftSize);
|
|
6088
6355
|
const step = async () => {
|
|
6089
6356
|
if (!this.listening || !this.listenAnalyser) {
|
|
@@ -6147,9 +6414,8 @@ var VoiceRuntime = class {
|
|
|
6147
6414
|
if (this.listeningCaptureActive) {
|
|
6148
6415
|
await this.stopListeningCapture();
|
|
6149
6416
|
}
|
|
6150
|
-
this.
|
|
6151
|
-
this.
|
|
6152
|
-
this.listeningCaptureChunks = [];
|
|
6417
|
+
this.clearListeningRecorderSchedule();
|
|
6418
|
+
await this.discardListeningIdleSessions();
|
|
6153
6419
|
this.listeningCaptureActive = false;
|
|
6154
6420
|
this.recordingStartedFromListening = false;
|
|
6155
6421
|
this.releaseInputStreamIfIdle();
|
|
@@ -6231,7 +6497,6 @@ var VoiceRuntime = class {
|
|
|
6231
6497
|
async handleRecordingError(error) {
|
|
6232
6498
|
logRuntimeError("voice.recording", error);
|
|
6233
6499
|
this.listeningCaptureActive = false;
|
|
6234
|
-
this.listeningCaptureChunks = [];
|
|
6235
6500
|
this.recordingStartedFromListening = false;
|
|
6236
6501
|
await this.triggerSystemEvent("onRecordingError", this.normalizeErrorMessage(error));
|
|
6237
6502
|
}
|
|
@@ -6277,89 +6542,194 @@ var VoiceRuntime = class {
|
|
|
6277
6542
|
this.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
6278
6543
|
return this.mediaStream;
|
|
6279
6544
|
}
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6545
|
+
startListeningRecorderSchedule(stream) {
|
|
6546
|
+
this.clearListeningRecorderSchedule();
|
|
6547
|
+
this.launchListeningRecorder(stream);
|
|
6548
|
+
const delayedStart = window.setTimeout(() => {
|
|
6549
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6550
|
+
return;
|
|
6551
|
+
}
|
|
6552
|
+
this.launchListeningRecorder(stream);
|
|
6553
|
+
const laneB = window.setInterval(() => {
|
|
6554
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6557
|
+
this.launchListeningRecorder(stream);
|
|
6558
|
+
}, LISTENING_SEGMENT_MS);
|
|
6559
|
+
this.listeningLaneIntervalIds.push(laneB);
|
|
6560
|
+
}, LISTENING_SEGMENT_STEP_MS);
|
|
6561
|
+
this.listeningLaneTimeoutIds.push(delayedStart);
|
|
6562
|
+
const laneA = window.setInterval(() => {
|
|
6563
|
+
if (!this.listening || this.listeningCaptureActive) {
|
|
6564
|
+
return;
|
|
6565
|
+
}
|
|
6566
|
+
this.launchListeningRecorder(stream);
|
|
6567
|
+
}, LISTENING_SEGMENT_MS);
|
|
6568
|
+
this.listeningLaneIntervalIds.push(laneA);
|
|
6569
|
+
}
|
|
6570
|
+
clearListeningRecorderSchedule() {
|
|
6571
|
+
for (const timeoutId of this.listeningLaneTimeoutIds) {
|
|
6572
|
+
window.clearTimeout(timeoutId);
|
|
6573
|
+
}
|
|
6574
|
+
this.listeningLaneTimeoutIds = [];
|
|
6575
|
+
for (const intervalId of this.listeningLaneIntervalIds) {
|
|
6576
|
+
window.clearInterval(intervalId);
|
|
6283
6577
|
}
|
|
6578
|
+
this.listeningLaneIntervalIds = [];
|
|
6579
|
+
}
|
|
6580
|
+
launchListeningRecorder(stream) {
|
|
6581
|
+
const session = this.createListeningRecorderSession(stream);
|
|
6582
|
+
this.listeningIdleSessions.push(session);
|
|
6583
|
+
session.recorder.start();
|
|
6584
|
+
session.stopTimeoutId = window.setTimeout(() => {
|
|
6585
|
+
if (!session.promoted && session.recorder.state !== "inactive") {
|
|
6586
|
+
session.recorder.stop();
|
|
6587
|
+
}
|
|
6588
|
+
}, LISTENING_SEGMENT_MS);
|
|
6589
|
+
}
|
|
6590
|
+
createListeningRecorderSession(stream) {
|
|
6284
6591
|
if (typeof MediaRecorder === "undefined") {
|
|
6285
6592
|
throw new Error("MediaRecorder is not supported in this browser");
|
|
6286
6593
|
}
|
|
6287
6594
|
const preferredMimeType = this.getPreferredRecordingMimeType();
|
|
6288
6595
|
const options = preferredMimeType ? { mimeType: preferredMimeType } : void 0;
|
|
6289
6596
|
const recorder = options ? new MediaRecorder(stream, options) : new MediaRecorder(stream);
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6597
|
+
let resolveStopped = () => {
|
|
6598
|
+
};
|
|
6599
|
+
const stopped = new Promise((resolve) => {
|
|
6600
|
+
resolveStopped = resolve;
|
|
6601
|
+
});
|
|
6602
|
+
const session = {
|
|
6603
|
+
recorder,
|
|
6604
|
+
mimeType: recorder.mimeType || preferredMimeType || "audio/webm",
|
|
6605
|
+
startedAt: performance.now(),
|
|
6606
|
+
chunks: [],
|
|
6607
|
+
stopTimeoutId: null,
|
|
6608
|
+
promoted: false,
|
|
6609
|
+
discard: false,
|
|
6610
|
+
stopped,
|
|
6611
|
+
resolveStopped
|
|
6612
|
+
};
|
|
6294
6613
|
recorder.ondataavailable = (event) => {
|
|
6295
|
-
if (
|
|
6296
|
-
|
|
6297
|
-
}
|
|
6298
|
-
const timestamp = performance.now();
|
|
6299
|
-
if (this.listeningCaptureActive) {
|
|
6300
|
-
this.listeningCaptureChunks.push(event.data);
|
|
6301
|
-
return;
|
|
6302
|
-
}
|
|
6303
|
-
this.listeningPreRollChunks.push({ data: event.data, timestamp });
|
|
6304
|
-
const cutoff = timestamp - LISTENING_PRE_ROLL_MS;
|
|
6305
|
-
while (this.listeningPreRollChunks.length > 0 && this.listeningPreRollChunks[0].timestamp < cutoff) {
|
|
6306
|
-
this.listeningPreRollChunks.shift();
|
|
6614
|
+
if (event.data && event.data.size > 0) {
|
|
6615
|
+
session.chunks.push(event.data);
|
|
6307
6616
|
}
|
|
6308
6617
|
};
|
|
6309
6618
|
recorder.onerror = (event) => {
|
|
6310
|
-
void this.handleRecordingError(event.error ?? new Error("Unknown listening
|
|
6619
|
+
void this.handleRecordingError(event.error ?? new Error("Unknown listening recording error"));
|
|
6311
6620
|
};
|
|
6312
|
-
recorder.
|
|
6621
|
+
recorder.onstop = () => {
|
|
6622
|
+
void this.handleListeningRecorderStop(session);
|
|
6623
|
+
};
|
|
6624
|
+
return session;
|
|
6313
6625
|
}
|
|
6314
|
-
|
|
6315
|
-
|
|
6626
|
+
beginListeningCapture() {
|
|
6627
|
+
const winner = this.selectListeningRecorderWinner();
|
|
6628
|
+
if (!winner) {
|
|
6316
6629
|
return;
|
|
6317
6630
|
}
|
|
6318
|
-
const recorder = this.listeningRecorder;
|
|
6319
|
-
this.listeningRecorder = null;
|
|
6320
|
-
recorder.ondataavailable = null;
|
|
6321
|
-
recorder.onerror = null;
|
|
6322
|
-
if (recorder.state !== "inactive") {
|
|
6323
|
-
recorder.stop();
|
|
6324
|
-
}
|
|
6325
|
-
}
|
|
6326
|
-
beginListeningCapture() {
|
|
6327
6631
|
this.listeningCaptureActive = true;
|
|
6328
6632
|
this.recordingStartedFromListening = true;
|
|
6329
|
-
this.
|
|
6330
|
-
|
|
6633
|
+
this.listeningPromotedSession = winner;
|
|
6634
|
+
winner.promoted = true;
|
|
6635
|
+
winner.discard = false;
|
|
6636
|
+
if (winner.stopTimeoutId !== null) {
|
|
6637
|
+
window.clearTimeout(winner.stopTimeoutId);
|
|
6638
|
+
winner.stopTimeoutId = null;
|
|
6639
|
+
}
|
|
6640
|
+
this.clearListeningRecorderSchedule();
|
|
6641
|
+
for (const session of this.listeningIdleSessions) {
|
|
6642
|
+
if (session === winner) {
|
|
6643
|
+
continue;
|
|
6644
|
+
}
|
|
6645
|
+
void this.discardListeningSession(session);
|
|
6646
|
+
}
|
|
6647
|
+
const preRollMs = Math.max(0, Math.round(performance.now() - winner.startedAt));
|
|
6331
6648
|
logRuntimeDebug(this.debugLogging, "voice-recording-started", {
|
|
6332
|
-
mimeType:
|
|
6649
|
+
mimeType: winner.mimeType,
|
|
6333
6650
|
fromListening: true,
|
|
6334
|
-
preRollMs
|
|
6335
|
-
preRollChunks: this.listeningCaptureChunks.length
|
|
6651
|
+
preRollMs
|
|
6336
6652
|
});
|
|
6337
6653
|
void this.triggerSystemEvent("onRecordingStarted", null);
|
|
6338
6654
|
}
|
|
6655
|
+
selectListeningRecorderWinner() {
|
|
6656
|
+
const activeSessions = this.listeningIdleSessions.filter((session) => session.recorder.state !== "inactive");
|
|
6657
|
+
if (activeSessions.length === 0) {
|
|
6658
|
+
return null;
|
|
6659
|
+
}
|
|
6660
|
+
const targetStartedAt = performance.now() - LISTENING_TARGET_PRE_ROLL_MS;
|
|
6661
|
+
const suitable = activeSessions.filter((session) => session.startedAt <= targetStartedAt).sort((left, right) => right.startedAt - left.startedAt);
|
|
6662
|
+
if (suitable.length > 0) {
|
|
6663
|
+
return suitable[0];
|
|
6664
|
+
}
|
|
6665
|
+
return activeSessions.sort((left, right) => left.startedAt - right.startedAt)[0] ?? null;
|
|
6666
|
+
}
|
|
6339
6667
|
async stopListeningCapture() {
|
|
6340
|
-
if (!this.listeningCaptureActive) {
|
|
6668
|
+
if (!this.listeningCaptureActive || !this.listeningPromotedSession) {
|
|
6341
6669
|
return;
|
|
6342
6670
|
}
|
|
6671
|
+
const session = this.listeningPromotedSession;
|
|
6343
6672
|
this.listeningCaptureActive = false;
|
|
6344
|
-
|
|
6345
|
-
|
|
6673
|
+
this.listeningPromotedSession = null;
|
|
6674
|
+
if (session.recorder.state !== "inactive") {
|
|
6675
|
+
session.recorder.stop();
|
|
6676
|
+
}
|
|
6677
|
+
await session.stopped;
|
|
6678
|
+
}
|
|
6679
|
+
async discardListeningSession(session) {
|
|
6680
|
+
session.discard = true;
|
|
6681
|
+
if (session.stopTimeoutId !== null) {
|
|
6682
|
+
window.clearTimeout(session.stopTimeoutId);
|
|
6683
|
+
session.stopTimeoutId = null;
|
|
6684
|
+
}
|
|
6685
|
+
if (session.recorder.state !== "inactive") {
|
|
6686
|
+
session.recorder.stop();
|
|
6687
|
+
}
|
|
6688
|
+
await session.stopped;
|
|
6689
|
+
}
|
|
6690
|
+
async discardListeningIdleSessions() {
|
|
6691
|
+
const sessions = [...this.listeningIdleSessions];
|
|
6692
|
+
for (const session of sessions) {
|
|
6693
|
+
if (session === this.listeningPromotedSession) {
|
|
6694
|
+
continue;
|
|
6695
|
+
}
|
|
6696
|
+
await this.discardListeningSession(session);
|
|
6697
|
+
}
|
|
6698
|
+
this.listeningIdleSessions = this.listeningPromotedSession ? [this.listeningPromotedSession] : [];
|
|
6699
|
+
}
|
|
6700
|
+
async handleListeningRecorderStop(session) {
|
|
6701
|
+
if (session.stopTimeoutId !== null) {
|
|
6702
|
+
window.clearTimeout(session.stopTimeoutId);
|
|
6703
|
+
session.stopTimeoutId = null;
|
|
6704
|
+
}
|
|
6705
|
+
this.listeningIdleSessions = this.listeningIdleSessions.filter((entry) => entry !== session);
|
|
6346
6706
|
try {
|
|
6347
|
-
|
|
6707
|
+
if (session.discard || !session.promoted) {
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6710
|
+
const blob = new Blob(session.chunks, { type: session.mimeType });
|
|
6348
6711
|
const audioData = await blobToBase64(blob);
|
|
6349
6712
|
logRuntimeDebug(this.debugLogging, "voice-recording-stopped", {
|
|
6350
|
-
mimeType:
|
|
6713
|
+
mimeType: session.mimeType,
|
|
6351
6714
|
size: blob.size,
|
|
6352
|
-
fromListening: true
|
|
6353
|
-
preRollMs: LISTENING_PRE_ROLL_MS
|
|
6715
|
+
fromListening: true
|
|
6354
6716
|
});
|
|
6355
6717
|
await this.triggerSystemEvent("onRecordingStopped", {
|
|
6356
6718
|
audioData,
|
|
6357
|
-
mimeType:
|
|
6719
|
+
mimeType: session.mimeType
|
|
6358
6720
|
});
|
|
6359
6721
|
} catch (error) {
|
|
6360
6722
|
await this.handleRecordingError(error);
|
|
6361
6723
|
} finally {
|
|
6362
|
-
|
|
6724
|
+
session.resolveStopped();
|
|
6725
|
+
if (session.promoted) {
|
|
6726
|
+
this.recordingStartedFromListening = false;
|
|
6727
|
+
if (this.listening && this.mediaStream?.active) {
|
|
6728
|
+
this.listeningStartedAt = performance.now();
|
|
6729
|
+
this.speechEventTriggered = false;
|
|
6730
|
+
this.startListeningRecorderSchedule(this.mediaStream);
|
|
6731
|
+
}
|
|
6732
|
+
}
|
|
6363
6733
|
}
|
|
6364
6734
|
}
|
|
6365
6735
|
clearRecordingTimeout() {
|
|
@@ -6372,9 +6742,6 @@ var VoiceRuntime = class {
|
|
|
6372
6742
|
if (this.listening) {
|
|
6373
6743
|
return;
|
|
6374
6744
|
}
|
|
6375
|
-
if (this.listeningRecorder && this.listeningRecorder.state !== "inactive") {
|
|
6376
|
-
return;
|
|
6377
|
-
}
|
|
6378
6745
|
if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
|
|
6379
6746
|
return;
|
|
6380
6747
|
}
|
|
@@ -7617,6 +7984,19 @@ function isGeneratedElementId(value) {
|
|
|
7617
7984
|
function isStepperEdit(node) {
|
|
7618
7985
|
return node.type === "number" || node.type === "float";
|
|
7619
7986
|
}
|
|
7987
|
+
function isScrollableWidgetNode(node) {
|
|
7988
|
+
switch (node.widget) {
|
|
7989
|
+
case "list":
|
|
7990
|
+
case "grid-view":
|
|
7991
|
+
case "listbox":
|
|
7992
|
+
case "combobox":
|
|
7993
|
+
return true;
|
|
7994
|
+
case "container-layout":
|
|
7995
|
+
return normalizeBoolean(node.verticallyScrollable, false);
|
|
7996
|
+
default:
|
|
7997
|
+
return false;
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
7620
8000
|
function getDateFormat(node) {
|
|
7621
8001
|
return node.format === "yyyy-MM-dd" ? "yyyy-MM-dd" : DEFAULT_DATE_FORMAT;
|
|
7622
8002
|
}
|
|
@@ -7953,6 +8333,7 @@ var RuntimeRenderer = class {
|
|
|
7953
8333
|
this.attachInputBehavior(this.root);
|
|
7954
8334
|
this.attachWidgetEvents(this.root);
|
|
7955
8335
|
restoreElementState(this.root, preservedState, this.stateByKey);
|
|
8336
|
+
this.syncAllScrollStateFlags();
|
|
7956
8337
|
this.applyPendingScrollAction();
|
|
7957
8338
|
this.applyPendingCursorAction();
|
|
7958
8339
|
this.activeContextMenu = adjustContextMenuPosition(this.root, this.activeContextMenu);
|
|
@@ -8571,7 +8952,9 @@ var RuntimeRenderer = class {
|
|
|
8571
8952
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8572
8953
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8573
8954
|
elements: deepClone(node.elements ?? []),
|
|
8574
|
-
selected: toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? -1
|
|
8955
|
+
selected: toFiniteNumber3(node.item) ?? toFiniteNumber3(node.selected) ?? -1,
|
|
8956
|
+
isScrolledToTop: true,
|
|
8957
|
+
isScrolledToBottom: true
|
|
8575
8958
|
};
|
|
8576
8959
|
break;
|
|
8577
8960
|
case "listbox":
|
|
@@ -8583,7 +8966,9 @@ var RuntimeRenderer = class {
|
|
|
8583
8966
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8584
8967
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8585
8968
|
listboxElements: deepClone(node.elements ?? []),
|
|
8586
|
-
selected: toFiniteNumber3(node.selected) ?? 0
|
|
8969
|
+
selected: toFiniteNumber3(node.selected) ?? 0,
|
|
8970
|
+
isScrolledToTop: true,
|
|
8971
|
+
isScrolledToBottom: true
|
|
8587
8972
|
};
|
|
8588
8973
|
break;
|
|
8589
8974
|
case "combobox":
|
|
@@ -8595,7 +8980,9 @@ var RuntimeRenderer = class {
|
|
|
8595
8980
|
enabled: normalizeBoolean(node.enabled, true),
|
|
8596
8981
|
definitionSignature: buildDefinitionSignature(node.elements ?? []),
|
|
8597
8982
|
comboboxElements: deepClone(node.elements ?? []),
|
|
8598
|
-
selected: toFiniteNumber3(node.selected) ?? 0
|
|
8983
|
+
selected: toFiniteNumber3(node.selected) ?? 0,
|
|
8984
|
+
isScrolledToTop: true,
|
|
8985
|
+
isScrolledToBottom: true
|
|
8599
8986
|
};
|
|
8600
8987
|
break;
|
|
8601
8988
|
case "radio-group":
|
|
@@ -8647,7 +9034,9 @@ var RuntimeRenderer = class {
|
|
|
8647
9034
|
widget: node.widget,
|
|
8648
9035
|
id: stateId,
|
|
8649
9036
|
name: node.name,
|
|
8650
|
-
enabled: normalizeBoolean(node.enabled, true)
|
|
9037
|
+
enabled: normalizeBoolean(node.enabled, true),
|
|
9038
|
+
isScrolledToTop: isScrollableWidgetNode(node) ? true : void 0,
|
|
9039
|
+
isScrolledToBottom: isScrollableWidgetNode(node) ? true : void 0
|
|
8651
9040
|
};
|
|
8652
9041
|
break;
|
|
8653
9042
|
}
|
|
@@ -9104,6 +9493,19 @@ var RuntimeRenderer = class {
|
|
|
9104
9493
|
redispatchUnderlyingClick: (x, y) => this.redispatchUnderlyingClick(x, y),
|
|
9105
9494
|
runActions: (actions, inputValue, context) => this.actionRuntime.runActions(actions, inputValue, context)
|
|
9106
9495
|
});
|
|
9496
|
+
for (const element of Array.from(root.querySelectorAll("[data-widget-key]"))) {
|
|
9497
|
+
const key = element.dataset.widgetKey;
|
|
9498
|
+
if (!key) {
|
|
9499
|
+
continue;
|
|
9500
|
+
}
|
|
9501
|
+
const node = this.nodeByKey.get(key);
|
|
9502
|
+
if (!node || !isScrollableWidgetNode(node)) {
|
|
9503
|
+
continue;
|
|
9504
|
+
}
|
|
9505
|
+
element.addEventListener("scroll", () => {
|
|
9506
|
+
this.syncScrollStateForElement(element, key);
|
|
9507
|
+
}, { passive: true });
|
|
9508
|
+
}
|
|
9107
9509
|
}
|
|
9108
9510
|
updateOwningListElementDescriptor(element, mutate) {
|
|
9109
9511
|
const listElement = element.closest(".vjt-list-element");
|
|
@@ -9422,6 +9824,7 @@ var RuntimeRenderer = class {
|
|
|
9422
9824
|
}
|
|
9423
9825
|
if (typeof window === "undefined") {
|
|
9424
9826
|
initialElement.scrollTop = Math.max(0, Math.min(targetTop, getMaxScrollTop(initialElement)));
|
|
9827
|
+
this.syncScrollStateForReference(reference, initialElement);
|
|
9425
9828
|
return;
|
|
9426
9829
|
}
|
|
9427
9830
|
if (this.activeScrollAnimationFrameId !== null) {
|
|
@@ -9433,6 +9836,7 @@ var RuntimeRenderer = class {
|
|
|
9433
9836
|
const delta = clampedTargetTop - startTop;
|
|
9434
9837
|
if (Math.abs(delta) < 1) {
|
|
9435
9838
|
initialElement.scrollTop = clampedTargetTop;
|
|
9839
|
+
this.syncScrollStateForReference(reference, initialElement);
|
|
9436
9840
|
return;
|
|
9437
9841
|
}
|
|
9438
9842
|
const durationMs = 300;
|
|
@@ -9448,10 +9852,12 @@ var RuntimeRenderer = class {
|
|
|
9448
9852
|
const progress = Math.min(1, elapsed / durationMs);
|
|
9449
9853
|
const liveTargetTop = Math.max(0, Math.min(clampedTargetTop, getMaxScrollTop(liveElement)));
|
|
9450
9854
|
liveElement.scrollTop = startTop + (liveTargetTop - startTop) * easeInOutCubic(progress);
|
|
9855
|
+
this.syncScrollStateForReference(reference, liveElement);
|
|
9451
9856
|
if (progress < 1) {
|
|
9452
9857
|
this.activeScrollAnimationFrameId = window.requestAnimationFrame(tick);
|
|
9453
9858
|
} else {
|
|
9454
9859
|
liveElement.scrollTop = liveTargetTop;
|
|
9860
|
+
this.syncScrollStateForReference(reference, liveElement);
|
|
9455
9861
|
this.activeScrollAnimationFrameId = null;
|
|
9456
9862
|
}
|
|
9457
9863
|
};
|
|
@@ -9505,6 +9911,48 @@ var RuntimeRenderer = class {
|
|
|
9505
9911
|
}
|
|
9506
9912
|
return null;
|
|
9507
9913
|
}
|
|
9914
|
+
syncAllScrollStateFlags() {
|
|
9915
|
+
if (!(this.root instanceof HTMLElement)) {
|
|
9916
|
+
return;
|
|
9917
|
+
}
|
|
9918
|
+
for (const element of Array.from(this.root.querySelectorAll("[data-widget-key]"))) {
|
|
9919
|
+
const key = element.dataset.widgetKey;
|
|
9920
|
+
if (!key) {
|
|
9921
|
+
continue;
|
|
9922
|
+
}
|
|
9923
|
+
const node = this.nodeByKey.get(key);
|
|
9924
|
+
if (!node || !isScrollableWidgetNode(node)) {
|
|
9925
|
+
continue;
|
|
9926
|
+
}
|
|
9927
|
+
this.syncScrollStateForElement(element, key);
|
|
9928
|
+
}
|
|
9929
|
+
}
|
|
9930
|
+
syncScrollStateForReference(reference, element) {
|
|
9931
|
+
const widgetId = reference.split(".")[0]?.trim();
|
|
9932
|
+
if (!widgetId) {
|
|
9933
|
+
return;
|
|
9934
|
+
}
|
|
9935
|
+
const targetElement = element ?? this.findScrollableWidgetElement(reference);
|
|
9936
|
+
if (!(targetElement instanceof HTMLElement)) {
|
|
9937
|
+
return;
|
|
9938
|
+
}
|
|
9939
|
+
const key = targetElement.dataset.widgetKey ?? widgetId;
|
|
9940
|
+
this.syncScrollStateForElement(targetElement, key);
|
|
9941
|
+
}
|
|
9942
|
+
syncScrollStateForElement(element, key) {
|
|
9943
|
+
const state = this.stateByKey.get(key) ?? this.stateById.get(key);
|
|
9944
|
+
if (!state) {
|
|
9945
|
+
return;
|
|
9946
|
+
}
|
|
9947
|
+
const maxScrollTop = Math.max(0, element.scrollHeight - element.clientHeight);
|
|
9948
|
+
if (maxScrollTop <= 1) {
|
|
9949
|
+
state.isScrolledToTop = true;
|
|
9950
|
+
state.isScrolledToBottom = true;
|
|
9951
|
+
return;
|
|
9952
|
+
}
|
|
9953
|
+
state.isScrolledToTop = element.scrollTop <= 1;
|
|
9954
|
+
state.isScrolledToBottom = element.scrollTop >= maxScrollTop - 1;
|
|
9955
|
+
}
|
|
9508
9956
|
toScrollableIndex(value) {
|
|
9509
9957
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
9510
9958
|
return Math.max(0, Math.trunc(value));
|
package/dist/lib/network.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionDefinition, PrimitiveRequestType, RequestMapInput, RequestSchema, SseConfig } from './types.js';
|
|
1
|
+
import type { ActionDefinition, ClosableRuntimeResource, PrimitiveRequestType, RequestMapInput, RequestSchema, SseConfig } from './types.js';
|
|
2
2
|
type ActionRunner = (actions: ActionDefinition[], inputValue: unknown, context: {
|
|
3
3
|
currentValue: unknown;
|
|
4
4
|
responseValue?: unknown;
|
|
@@ -8,7 +8,7 @@ type NetworkRuntimeOptions = {
|
|
|
8
8
|
requestsMap: RequestMapInput;
|
|
9
9
|
sseConfigs: SseConfig[];
|
|
10
10
|
backendUrl?: string;
|
|
11
|
-
eventSources:
|
|
11
|
+
eventSources: ClosableRuntimeResource[];
|
|
12
12
|
runActions: ActionRunner;
|
|
13
13
|
rerenderRoot: () => Promise<void>;
|
|
14
14
|
};
|
|
@@ -28,6 +28,9 @@ export declare class NetworkRuntime {
|
|
|
28
28
|
private createRequestError;
|
|
29
29
|
private toRequestErrorPayload;
|
|
30
30
|
coercePrimitiveSchemaValue(type: PrimitiveRequestType, value: unknown): unknown;
|
|
31
|
+
private createSseConnection;
|
|
32
|
+
private createNativeSseConnection;
|
|
33
|
+
private createFetchSseConnection;
|
|
31
34
|
private handleSseEvent;
|
|
32
35
|
}
|
|
33
36
|
export {};
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
|
6
6
|
export type WidgetEventName = 'onClick' | 'onUserValueChange' | 'onRefresh' | 'onEnter' | 'onShiftEnter' | 'onControlEnter' | 'onPaste';
|
|
7
7
|
export type SystemEventName = 'onBeforeRender' | 'onAfterRender' | 'onBeforeNavigate' | 'onAfterNavigate' | 'onLayoutSwitchToMobile' | 'onLayoutSwitchToDesktop' | 'onSpeechDetected' | 'onRecordingStarted' | 'onRecordingStopped' | 'onRecordingError' | 'onListeningError' | 'onListeringError' | 'onPlayFinished' | 'onPlayingStopped';
|
|
8
8
|
export type PrimitiveRequestType = 'int' | 'float' | 'boolean' | 'string';
|
|
9
|
+
export type ClosableRuntimeResource = {
|
|
10
|
+
close: () => void;
|
|
11
|
+
};
|
|
9
12
|
export type RouteDefinition = {
|
|
10
13
|
path: string;
|
|
11
14
|
ui: string;
|
|
@@ -128,6 +131,8 @@ export type WidgetState = {
|
|
|
128
131
|
modalHeight?: number;
|
|
129
132
|
activeTab?: number;
|
|
130
133
|
isHidden?: boolean;
|
|
134
|
+
isScrolledToTop?: boolean;
|
|
135
|
+
isScrolledToBottom?: boolean;
|
|
131
136
|
};
|
|
132
137
|
export type RuntimeSnapshot = {
|
|
133
138
|
stateByKey: Array<[string, WidgetState]>;
|
|
@@ -16,10 +16,10 @@ export declare class VoiceRuntime {
|
|
|
16
16
|
private listenAnalyser;
|
|
17
17
|
private listenSource;
|
|
18
18
|
private listenFrameId;
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private
|
|
22
|
-
private
|
|
19
|
+
private listeningIdleSessions;
|
|
20
|
+
private listeningLaneTimeoutIds;
|
|
21
|
+
private listeningLaneIntervalIds;
|
|
22
|
+
private listeningPromotedSession;
|
|
23
23
|
private listeningCaptureActive;
|
|
24
24
|
private lastSpeechAt;
|
|
25
25
|
private listeningStartedAt;
|
|
@@ -44,10 +44,16 @@ export declare class VoiceRuntime {
|
|
|
44
44
|
private normalizePlayablePayload;
|
|
45
45
|
private getPreferredRecordingMimeType;
|
|
46
46
|
private ensureInputStream;
|
|
47
|
-
private
|
|
48
|
-
private
|
|
47
|
+
private startListeningRecorderSchedule;
|
|
48
|
+
private clearListeningRecorderSchedule;
|
|
49
|
+
private launchListeningRecorder;
|
|
50
|
+
private createListeningRecorderSession;
|
|
49
51
|
private beginListeningCapture;
|
|
52
|
+
private selectListeningRecorderWinner;
|
|
50
53
|
private stopListeningCapture;
|
|
54
|
+
private discardListeningSession;
|
|
55
|
+
private discardListeningIdleSessions;
|
|
56
|
+
private handleListeningRecorderStop;
|
|
51
57
|
private clearRecordingTimeout;
|
|
52
58
|
private releaseInputStreamIfIdle;
|
|
53
59
|
private stopStreamTracks;
|