arcane-os 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
--arcane-speech-padding-block-start: .65rem;
|
|
6
6
|
--arcane-speech-padding-inline: .75rem;
|
|
7
7
|
display: grid;
|
|
8
|
-
grid-template-columns: auto minmax(0, 1fr) max-content
|
|
8
|
+
grid-template-columns: auto minmax(0, 1fr) max-content;
|
|
9
9
|
align-items: center;
|
|
10
10
|
gap: var(--arcane-space-3, .75rem);
|
|
11
11
|
box-sizing: border-box;
|
|
@@ -94,16 +94,8 @@
|
|
|
94
94
|
color: var(--arcane-danger, var(--danger-color));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
#endButton {
|
|
98
|
-
grid-column: 3;
|
|
99
|
-
justify-self: end;
|
|
100
|
-
background: var(--arcane-danger, var(--danger-color));
|
|
101
|
-
border-color: var(--arcane-danger, var(--danger-color));
|
|
102
|
-
color: var(--arcane-action-text, var(--button-text-color, var(--text-color)));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
97
|
#muteButton {
|
|
106
|
-
grid-column:
|
|
98
|
+
grid-column: 3;
|
|
107
99
|
justify-self: end;
|
|
108
100
|
float: none;
|
|
109
101
|
background: var(--arcane-secondary, var(--secondary-color));
|
|
@@ -125,11 +117,6 @@
|
|
|
125
117
|
grid-column: 2 / span 2;
|
|
126
118
|
}
|
|
127
119
|
|
|
128
|
-
#endButton {
|
|
129
|
-
grid-row: 1;
|
|
130
|
-
grid-column: 2;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
120
|
#muteButton {
|
|
134
121
|
grid-row: 1;
|
|
135
122
|
grid-column: 3;
|
|
@@ -142,10 +129,10 @@
|
|
|
142
129
|
<button id="sttActivationButton" type="button" hidden disabled>Start transcription</button>
|
|
143
130
|
<button id="stopButton" type="button" class="hidden" disabled>Send</button>
|
|
144
131
|
<p id="speechStatus" class="speech_status" role="status" aria-live="polite"></p>
|
|
145
|
-
<button id="endButton" type="button" class="end">End</button>
|
|
146
132
|
<button id="muteButton" type="button" class="mute" aria-pressed="true" aria-label="Voice unavailable" disabled>Voice unavailable</button>
|
|
147
133
|
|
|
148
134
|
<script type="module">
|
|
135
|
+
const completeValue = (value) => value;
|
|
149
136
|
const {
|
|
150
137
|
createArcaneEventSource,
|
|
151
138
|
projectArcaneDOMEvent
|
|
@@ -187,7 +174,6 @@
|
|
|
187
174
|
const recordButton = shadowRoot.querySelector('#recordButton');
|
|
188
175
|
const sttActivationButton = shadowRoot.querySelector('#sttActivationButton');
|
|
189
176
|
const stopButton = shadowRoot.querySelector('#stopButton');
|
|
190
|
-
const endButton = shadowRoot.querySelector('#endButton');
|
|
191
177
|
const muteButton = shadowRoot.querySelector('#muteButton');
|
|
192
178
|
const speechStatus = shadowRoot.querySelector('#speechStatus');
|
|
193
179
|
const silenceDuration = 0;
|
|
@@ -260,17 +246,23 @@
|
|
|
260
246
|
|
|
261
247
|
function publicSpeechErrorFields(error, boundaryCode) {
|
|
262
248
|
let causeCode = '';
|
|
249
|
+
let message = '';
|
|
263
250
|
try {
|
|
264
251
|
causeCode = typeof error?.code === 'string'
|
|
265
|
-
? error.code
|
|
252
|
+
? error.code
|
|
253
|
+
: '';
|
|
254
|
+
message = typeof error?.message === 'string'
|
|
255
|
+
? error.message
|
|
266
256
|
: '';
|
|
267
257
|
} catch {}
|
|
268
|
-
return
|
|
258
|
+
return completeValue(
|
|
269
259
|
{
|
|
260
|
+
error,
|
|
270
261
|
code: boundaryCode,
|
|
271
262
|
...(causeCode && causeCode !== boundaryCode
|
|
272
263
|
? { causeCode }
|
|
273
|
-
: {})
|
|
264
|
+
: {}),
|
|
265
|
+
...(message ? { message } : {})
|
|
274
266
|
}
|
|
275
267
|
);
|
|
276
268
|
}
|
|
@@ -327,7 +319,7 @@
|
|
|
327
319
|
}
|
|
328
320
|
);
|
|
329
321
|
|
|
330
|
-
const lifecycleListenerOptions =
|
|
322
|
+
const lifecycleListenerOptions = completeValue(
|
|
331
323
|
{ signal: runtimeStateAbortController.signal }
|
|
332
324
|
);
|
|
333
325
|
recordButton.addEventListener('pointerdown', handleRecordPointerDown, lifecycleListenerOptions);
|
|
@@ -339,7 +331,6 @@
|
|
|
339
331
|
recordButton.addEventListener('blur', handleRecordBlur, lifecycleListenerOptions);
|
|
340
332
|
stopButton.addEventListener('click', stop, lifecycleListenerOptions);
|
|
341
333
|
muteButton.addEventListener('click', muteToggle, lifecycleListenerOptions);
|
|
342
|
-
endButton.addEventListener('click', submitCompletionCommand, lifecycleListenerOptions);
|
|
343
334
|
window.addEventListener(
|
|
344
335
|
'pagehide',
|
|
345
336
|
destroy,
|
|
@@ -352,8 +343,8 @@
|
|
|
352
343
|
signal: runtimeStateAbortController.signal
|
|
353
344
|
}
|
|
354
345
|
);
|
|
355
|
-
if (!host.initialMuted
|
|
356
|
-
|
|
346
|
+
if (!host.initialMuted) {
|
|
347
|
+
applyConfiguredMutedState(false);
|
|
357
348
|
}
|
|
358
349
|
synchronizeAIMutedState();
|
|
359
350
|
renderControls();
|
|
@@ -385,7 +376,7 @@
|
|
|
385
376
|
);
|
|
386
377
|
|
|
387
378
|
function unavailableRole(role) {
|
|
388
|
-
return
|
|
379
|
+
return completeValue(
|
|
389
380
|
{
|
|
390
381
|
role,
|
|
391
382
|
state: 'unavailable',
|
|
@@ -458,6 +449,13 @@
|
|
|
458
449
|
if (microphoneWasAvailable && !host.availability.microphone) {
|
|
459
450
|
cancelSTTOperation('microphone-unavailable');
|
|
460
451
|
}
|
|
452
|
+
if (
|
|
453
|
+
!microphoneWasAvailable
|
|
454
|
+
&& host.availability.microphone
|
|
455
|
+
&& localStatus?.owner === 'microphone'
|
|
456
|
+
) {
|
|
457
|
+
localStatus = null;
|
|
458
|
+
}
|
|
461
459
|
applyRoleTransitions(previousSTTRole, previousTTSRole);
|
|
462
460
|
return { ...host.availability };
|
|
463
461
|
}
|
|
@@ -482,9 +480,12 @@
|
|
|
482
480
|
if (muted) {
|
|
483
481
|
requestUserMute();
|
|
484
482
|
} else {
|
|
485
|
-
pendingUnmute =
|
|
486
|
-
host.muted =
|
|
487
|
-
|
|
483
|
+
pendingUnmute = true;
|
|
484
|
+
host.muted = true;
|
|
485
|
+
if (!settleSuccessfulUnmute()) {
|
|
486
|
+
synchronizeAIMutedState();
|
|
487
|
+
continuePendingUnmute();
|
|
488
|
+
}
|
|
488
489
|
}
|
|
489
490
|
renderControls();
|
|
490
491
|
renderStatus();
|
|
@@ -495,6 +496,12 @@
|
|
|
495
496
|
const previousTTSRole = ttsRole;
|
|
496
497
|
sttRole = snapshot.roles.stt;
|
|
497
498
|
ttsRole = snapshot.roles.tts;
|
|
499
|
+
if (sttRole.state === 'error' && previousSTTRole.state !== 'error') {
|
|
500
|
+
console.error('Arcane transcription runtime failed.', sttRole.error);
|
|
501
|
+
}
|
|
502
|
+
if (ttsRole.state === 'error' && previousTTSRole.state !== 'error') {
|
|
503
|
+
console.error('Arcane voice runtime failed.', ttsRole.error);
|
|
504
|
+
}
|
|
498
505
|
clearSettledTTSIntent();
|
|
499
506
|
applyRoleTransitions(previousSTTRole, previousTTSRole);
|
|
500
507
|
}
|
|
@@ -524,6 +531,7 @@
|
|
|
524
531
|
}
|
|
525
532
|
|
|
526
533
|
settleSuccessfulUnmute();
|
|
534
|
+
continuePendingUnmute();
|
|
527
535
|
|
|
528
536
|
synchronizeAIMutedState();
|
|
529
537
|
renderControls();
|
|
@@ -742,19 +750,21 @@
|
|
|
742
750
|
if (!progress) {
|
|
743
751
|
return fallback;
|
|
744
752
|
}
|
|
745
|
-
|
|
746
|
-
const amount = progress.total === null
|
|
747
|
-
? `${progress.completed} ${progress.unit}`
|
|
748
|
-
: `${progress.completed} of ${progress.total} ${progress.unit}`;
|
|
749
|
-
const heartbeat = progress.heartbeat ? ', active heartbeat' : '';
|
|
750
|
-
return `${progress.phase}, ${amount}${heartbeat}`;
|
|
753
|
+
return progress.phase;
|
|
751
754
|
}
|
|
752
755
|
|
|
753
756
|
function visibleErrorMessage(error, fallback) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
757
|
+
try {
|
|
758
|
+
if (error?.userSafe === true) {
|
|
759
|
+
const message = typeof error.userMessage === 'string'
|
|
760
|
+
? error.userMessage
|
|
761
|
+
: error.message;
|
|
762
|
+
if (typeof message === 'string' && message.trim()) {
|
|
763
|
+
return message;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} catch {}
|
|
767
|
+
return fallback;
|
|
758
768
|
}
|
|
759
769
|
|
|
760
770
|
async function observeMicrophonePermission() {
|
|
@@ -880,11 +890,6 @@
|
|
|
880
890
|
stop();
|
|
881
891
|
}
|
|
882
892
|
|
|
883
|
-
function submitCompletionCommand(event) {
|
|
884
|
-
event.target.disabled = true;
|
|
885
|
-
void transcribe('End now, run all tool functions.');
|
|
886
|
-
}
|
|
887
|
-
|
|
888
893
|
function muteToggle() {
|
|
889
894
|
if (pendingTTSIntent === 'load' || ttsRole.state === 'loading') {
|
|
890
895
|
requestUserMute();
|
|
@@ -982,6 +987,25 @@
|
|
|
982
987
|
return true;
|
|
983
988
|
}
|
|
984
989
|
|
|
990
|
+
function continuePendingUnmute() {
|
|
991
|
+
if (!pendingUnmute || destroyed || !selectedRole(ttsRole)) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
if (
|
|
995
|
+
pendingTTSIntent === 'load'
|
|
996
|
+
|| activeTTSIntent?.action === 'load'
|
|
997
|
+
|| ttsRole.state === 'loading'
|
|
998
|
+
) {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
if (ttsRole.state !== 'unloaded') {
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
void requestUserUnmute();
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
985
1009
|
async function requestTTSIntent(action, reason) {
|
|
986
1010
|
if (destroyed) {
|
|
987
1011
|
return false;
|
|
@@ -991,7 +1015,7 @@
|
|
|
991
1015
|
}
|
|
992
1016
|
const generation = ++ttsIntentGeneration;
|
|
993
1017
|
const operationId = nextSpeechOperationId(`tts-${action}`);
|
|
994
|
-
const intent =
|
|
1018
|
+
const intent = completeValue({ action, generation, operationId });
|
|
995
1019
|
activeTTSIntent = intent;
|
|
996
1020
|
ttsOperationId = operationId;
|
|
997
1021
|
pendingTTSIntent = action === 'load' && ttsRole.state === 'ready'
|
|
@@ -1054,6 +1078,7 @@
|
|
|
1054
1078
|
`ARCANE_SPEECH_TTS_${action.toUpperCase()}_REJECTED`
|
|
1055
1079
|
);
|
|
1056
1080
|
const reason = `tts-${action}-rejected`;
|
|
1081
|
+
console.error(`Arcane voice ${action} request failed.`, error);
|
|
1057
1082
|
dispatchSpeechEvent(
|
|
1058
1083
|
'speech-tts-lifecycle-error',
|
|
1059
1084
|
{
|
|
@@ -1084,43 +1109,58 @@
|
|
|
1084
1109
|
if (destroyed) {
|
|
1085
1110
|
return false;
|
|
1086
1111
|
}
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1112
|
+
const failures = {
|
|
1113
|
+
synthesis: {
|
|
1114
|
+
code: 'ARCANE_SPEECH_TTS_SYNTHESIS_REQUEST_REJECTED',
|
|
1115
|
+
message: 'Speech synthesis failed.',
|
|
1116
|
+
reason: 'tts-synthesis-rejected'
|
|
1117
|
+
},
|
|
1118
|
+
decode: {
|
|
1119
|
+
code: 'ARCANE_SPEECH_TTS_DECODE_REJECTED',
|
|
1120
|
+
message: 'Speech audio could not be decoded.',
|
|
1121
|
+
reason: 'tts-decode-rejected'
|
|
1122
|
+
},
|
|
1123
|
+
'playback-start': {
|
|
1124
|
+
code: 'ARCANE_SPEECH_TTS_PLAYBACK_START_REJECTED',
|
|
1125
|
+
message: 'Speech playback could not start.',
|
|
1126
|
+
reason: 'tts-playback-start-rejected'
|
|
1127
|
+
},
|
|
1128
|
+
'playback-resume': {
|
|
1129
|
+
code: 'ARCANE_SPEECH_TTS_PLAYBACK_RESUME_REJECTED',
|
|
1130
|
+
message: 'Speech playback could not resume.',
|
|
1131
|
+
reason: 'tts-playback-resume-rejected'
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
const normalizedBoundary = Object.hasOwn(failures, boundary)
|
|
1135
|
+
? boundary
|
|
1136
|
+
: 'synthesis';
|
|
1137
|
+
const failure = failures[normalizedBoundary];
|
|
1138
|
+
const message = visibleErrorMessage(error, failure.message);
|
|
1094
1139
|
host.muted = true;
|
|
1095
1140
|
stopTTSPlayback();
|
|
1096
1141
|
localStatus = {
|
|
1097
|
-
message
|
|
1142
|
+
message,
|
|
1098
1143
|
tone: 'error'
|
|
1099
1144
|
};
|
|
1100
|
-
const errorFields = publicSpeechErrorFields(
|
|
1101
|
-
|
|
1102
|
-
playbackRejected
|
|
1103
|
-
? 'ARCANE_SPEECH_TTS_PLAYBACK_RESUME_REJECTED'
|
|
1104
|
-
: 'ARCANE_SPEECH_TTS_SYNTHESIS_REQUEST_REJECTED'
|
|
1105
|
-
);
|
|
1106
|
-
const reason = playbackRejected
|
|
1107
|
-
? 'tts-playback-resume-rejected'
|
|
1108
|
-
: 'tts-synthesis-rejected';
|
|
1145
|
+
const errorFields = publicSpeechErrorFields(error, failure.code);
|
|
1146
|
+
console.error(`Arcane ${normalizedBoundary} speech operation failed.`, error);
|
|
1109
1147
|
dispatchSpeechEvent(
|
|
1110
1148
|
'speech-synthesis-error',
|
|
1111
1149
|
{
|
|
1150
|
+
boundary: normalizedBoundary,
|
|
1112
1151
|
...errorFields,
|
|
1113
1152
|
error,
|
|
1114
1153
|
message,
|
|
1115
|
-
reason
|
|
1154
|
+
reason: failure.reason
|
|
1116
1155
|
},
|
|
1117
1156
|
{
|
|
1118
1157
|
bubbles: true,
|
|
1119
1158
|
composed: true,
|
|
1120
|
-
operationId: nextSpeechOperationId(
|
|
1159
|
+
operationId: nextSpeechOperationId(`tts-${normalizedBoundary}`),
|
|
1121
1160
|
publicDetail: {
|
|
1161
|
+
boundary: normalizedBoundary,
|
|
1122
1162
|
...errorFields,
|
|
1123
|
-
reason
|
|
1163
|
+
reason: failure.reason
|
|
1124
1164
|
}
|
|
1125
1165
|
}
|
|
1126
1166
|
);
|
|
@@ -1156,7 +1196,7 @@
|
|
|
1156
1196
|
|
|
1157
1197
|
void Promise.resolve(aiRuntime.resumeAudio()).catch(
|
|
1158
1198
|
function reportTTSResumeFailure(error) {
|
|
1159
|
-
reportTTSError(error, 'playback');
|
|
1199
|
+
reportTTSError(error, 'playback-resume');
|
|
1160
1200
|
}
|
|
1161
1201
|
);
|
|
1162
1202
|
}
|
|
@@ -1170,7 +1210,7 @@
|
|
|
1170
1210
|
{
|
|
1171
1211
|
operationId,
|
|
1172
1212
|
publicDetail: {
|
|
1173
|
-
|
|
1213
|
+
text: transcription
|
|
1174
1214
|
}
|
|
1175
1215
|
}
|
|
1176
1216
|
);
|
|
@@ -1214,11 +1254,14 @@
|
|
|
1214
1254
|
['NotAllowedError', 'NotFoundError', 'NotReadableError', 'SecurityError']
|
|
1215
1255
|
.includes(error?.name)
|
|
1216
1256
|
) {
|
|
1217
|
-
setAvailability({ microphone: false });
|
|
1218
1257
|
localStatus = {
|
|
1219
1258
|
message: 'Microphone access is unavailable. Text chat is still available.',
|
|
1220
|
-
tone: 'error'
|
|
1259
|
+
tone: 'error',
|
|
1260
|
+
owner: 'microphone'
|
|
1221
1261
|
};
|
|
1262
|
+
if (microphonePermissionStatus?.state === 'denied') {
|
|
1263
|
+
setAvailability({ microphone: false });
|
|
1264
|
+
}
|
|
1222
1265
|
dispatchSpeechEvent(
|
|
1223
1266
|
'speech-microphone-unavailable',
|
|
1224
1267
|
{ reason: error.name },
|
|
@@ -1466,6 +1509,7 @@
|
|
|
1466
1509
|
'ARCANE_SPEECH_STT_TRANSCRIPTION_REQUEST_REJECTED'
|
|
1467
1510
|
);
|
|
1468
1511
|
const reason = 'stt-transcription-rejected';
|
|
1512
|
+
console.error('Arcane speech transcription failed.', error);
|
|
1469
1513
|
dispatchSpeechEvent(
|
|
1470
1514
|
'speech-transcription-error',
|
|
1471
1515
|
{
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
|
|
2
2
|
<style>
|
|
3
3
|
:host{display:block;height:100%;min-height:18rem;--terminal-bg:var(--background,rgb(244, 246, 251));--terminal-fg:var(--text-color,rgb(23, 34, 56));--terminal-muted:var(--muted-text-color,rgb(95, 107, 130));--terminal-accent:var(--focus-color,rgb(118, 87, 213));--terminal-danger:var(--danger-color,rgb(165, 56, 56));--terminal-font:ui-monospace,"Cascadia Code","SFMono-Regular",Consolas,monospace;--terminal-font-size:14px;--terminal-line-height:1.55;--terminal-opacity:1}
|
|
4
|
-
*{box-sizing:border-box}.workspace{background:color-mix(in srgb,var(--terminal-bg) calc(var(--terminal-opacity)*100%),transparent);border:1px solid color-mix(in srgb,var(--terminal-accent) 20%,transparent);border-radius:.85rem;color:var(--terminal-fg);display:grid;grid-template-rows:auto auto minmax(0,1fr) auto;height:100%;overflow:
|
|
4
|
+
*{box-sizing:border-box}.workspace{background:color-mix(in srgb,var(--terminal-bg) calc(var(--terminal-opacity)*100%),transparent);border:1px solid color-mix(in srgb,var(--terminal-accent) 20%,transparent);border-radius:.85rem;color:var(--terminal-fg);display:grid;grid-template-rows:auto auto minmax(0,1fr) auto;height:100%;overflow:visible;box-shadow:0 1.5rem 5rem rgba(0,0,0,.28)}
|
|
5
5
|
.tabs,.toolbar{align-items:center;background:color-mix(in srgb,var(--terminal-bg) 88%,white 12%);display:flex;gap:.35rem;min-width:0;padding:.45rem .65rem}.tabs{border-bottom:1px solid color-mix(in srgb,var(--terminal-fg) 10%,transparent)}
|
|
6
|
-
.tab{align-items:center;background:transparent;border:1px solid transparent;border-radius:.45rem;color:var(--terminal-muted);display:flex;font:600 .78rem/1 var(--terminal-font);gap:.35rem;
|
|
6
|
+
.tab{align-items:center;background:transparent;border:1px solid transparent;border-radius:.45rem;color:var(--terminal-muted);display:flex;font:600 .78rem/1 var(--terminal-font);gap:.35rem;padding:.55rem .65rem}.tab[aria-selected="true"]{background:color-mix(in srgb,var(--terminal-accent) 12%,transparent);border-color:color-mix(in srgb,var(--terminal-accent) 30%,transparent);color:var(--terminal-fg)}.tab-label{overflow-wrap:anywhere;white-space:normal}.tab-close{border:0;background:transparent;color:inherit;cursor:pointer;padding:0 .15rem}.new-session,.tool{background:transparent;border:1px solid transparent;border-radius:.4rem;color:var(--terminal-muted);cursor:pointer;font:600 .78rem/1 var(--terminal-font);padding:.55rem}.new-session:hover,.tool:hover{border-color:color-mix(in srgb,var(--terminal-accent) 25%,transparent);color:var(--terminal-fg)}
|
|
7
7
|
.toolbar{border-bottom:1px solid color-mix(in srgb,var(--terminal-fg) 8%,transparent);justify-content:flex-end}.connection{align-items:center;display:flex;gap:.4rem;margin-right:auto;color:var(--terminal-muted);font:600 .72rem/1 var(--terminal-font);text-transform:uppercase;letter-spacing:.08em}.connection::before{background:var(--terminal-muted);border-radius:50%;content:"";height:.48rem;width:.48rem}.connection[data-state="running"]::before{background:var(--terminal-accent);box-shadow:0 0 .7rem var(--terminal-accent)}.connection[data-state="error"]::before,.connection[data-state="exited"]::before{background:var(--terminal-danger)}
|
|
8
8
|
.search{background:var(--terminal-bg);border:1px solid color-mix(in srgb,var(--terminal-fg) 16%,transparent);border-radius:.4rem;color:var(--terminal-fg);font:inherit;max-width:15rem;padding:.4rem .55rem}.search[hidden]{display:none}
|
|
9
9
|
.output{font:var(--terminal-font-size)/var(--terminal-line-height) var(--terminal-font);overflow:auto;overscroll-behavior:contain;padding:1rem 1.1rem;scrollbar-color:color-mix(in srgb,var(--terminal-accent) 30%,transparent) transparent;white-space:pre-wrap;word-break:break-word}.entry{min-height:1em}.entry.command{color:var(--terminal-accent);font-weight:650}.entry.system{color:var(--terminal-muted)}.entry.error{color:var(--terminal-danger)}mark{background:color-mix(in srgb,var(--terminal-accent) 38%,transparent);color:inherit}
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
function addSession(session){sessions.set(session.id,{...session,entries:session.entries||[]});if(!activeId)activeId=session.id;renderTabs();render();setState(session.state||'starting');}
|
|
51
51
|
function removeSession(id){sessions.delete(id);if(activeId===id)activeId=sessions.keys().next().value||'';renderTabs();render();}
|
|
52
52
|
function activateSession(id){if(!sessions.has(id))return;activeId=id;renderTabs();render();setState(sessions.get(id).state||'running');input.focus();emit('terminal-session-select',{sessionId:id});}
|
|
53
|
-
function append(text,kind='output',sessionId=activeId){const session=sessions.get(sessionId);if(!session)return;session.entries.push({text:String(text??''),kind});if(
|
|
53
|
+
function append(text,kind='output',sessionId=activeId){const session=sessions.get(sessionId);if(!session)return;session.entries.push({text:String(text??''),kind});if(sessionId===activeId)render(true);}
|
|
54
54
|
function clear(sessionId=activeId){const session=sessions.get(sessionId);if(session)session.entries=[];render();emit('terminal-clear',{sessionId});}
|
|
55
55
|
function setState(state,id=activeId){const session=sessions.get(id);if(session)session.state=state;if(id===activeId){connection.dataset.state=state;connection.textContent=state;}}
|
|
56
56
|
function setTheme(theme={}){for(const [key,value] of Object.entries(theme)){const name=`--terminal-${key.replace(/[A-Z]/g,match=>`-${match.toLowerCase()}`)}`;host.style.setProperty(name,String(value));}}
|
|
57
|
-
function submit(){const line=input.value
|
|
57
|
+
function submit(){const line=input.value;if(!line.trim())return;if(history.at(-1)!==line)history.push(line);historyIndex=history.length;append(`${promptLabel.textContent} ${line}\n`,'command');input.value='';resizeInput();emit('terminal-submit',{sessionId:activeId,line,history:[...history]});}
|
|
58
58
|
function recall(direction){if(!history.length)return;historyIndex=Math.max(0,Math.min(history.length,historyIndex+direction));input.value=historyIndex===history.length?'':history[historyIndex];resizeInput();input.setSelectionRange(input.value.length,input.value.length);}
|
|
59
59
|
function complete(){const prefix=input.value.split(/\s/).at(-1).toLowerCase();const matches=completions.filter(value=>value.toLowerCase().startsWith(prefix));if(matches.length===1)input.value=input.value.slice(0,-prefix.length)+matches[0];else if(matches.length>1)append(`${matches.join(' ')}\n`,'system');resizeInput();}
|
|
60
60
|
function renderTabs(){const fragment=document.createDocumentFragment();for(const session of sessions.values()){const button=document.createElement('button');button.className='tab';button.type='button';button.setAttribute('role','tab');button.setAttribute('aria-selected',String(session.id===activeId));button.innerHTML=`<span class="tab-label"></span><span class="tab-close" aria-label="Close session">×</span>`;button.querySelector('.tab-label').textContent=session.title||session.shell||'Terminal';button.onclick=event=>{if(event.target.classList.contains('tab-close'))emit('terminal-session-close',{sessionId:session.id});else activateSession(session.id);};fragment.append(button);}const add=document.createElement('button');add.className='new-session';add.type='button';add.textContent='+';add.title='New terminal session';add.onclick=()=>emit('terminal-session-new');fragment.append(add);tabs.replaceChildren(fragment);}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<header class="arcane-card__header intro"><h2 class="arcane-section-heading">Create your own skin</h2><p class="arcane-help">Choose a small set of safe design tokens. Compatible Arcane apps use the same saved skin.</p></header>
|
|
7
7
|
<div class="arcane-card__body editor">
|
|
8
8
|
<div class="identity">
|
|
9
|
-
<div class="arcane-field"><label for="themeName">Skin name</label><input id="themeName" name="name"
|
|
9
|
+
<div class="arcane-field"><label for="themeName">Skin name</label><input id="themeName" name="name" required></div>
|
|
10
10
|
<div class="arcane-field"><label for="themeScheme">Starting contrast</label><select id="themeScheme" name="scheme"><option value="light">Light</option><option value="dark">Dark</option></select></div>
|
|
11
11
|
</div>
|
|
12
12
|
<div id="colors" class="colors"></div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
|
|
2
2
|
<style>
|
|
3
|
-
:host{display:block;min-width:0}.toolbar{display:grid;gap:.75rem;padding:1rem;border-bottom:1px solid var(--border-color,#ccd4df)}.toolbar-row{display:flex;gap:.5rem}.toolbar input{min-width:0;width:100%}.filters{display:flex;gap:.4rem
|
|
3
|
+
:host{display:block;min-width:0}.toolbar{display:grid;gap:.75rem;padding:1rem;border-bottom:1px solid var(--border-color,#ccd4df)}.toolbar-row{display:flex;gap:.5rem}.toolbar input{min-width:0;width:100%}.filters{display:flex;flex-wrap:wrap;gap:.4rem}.filters button{overflow-wrap:anywhere}.list{display:grid}.thread{display:grid;gap:.35rem;width:100%;padding:1rem;border:0;border-bottom:1px solid var(--border-color,#ccd4df);color:inherit;background:transparent;text-align:left}.thread:hover,.thread[aria-current="true"]{background:var(--secondary-color,#e8edf4)}.thread:focus-visible{outline:.18rem solid var(--focus-color,#7657d5);outline-offset:-.2rem}.line{align-items:center;display:flex;flex-wrap:wrap;gap:.5rem;justify-content:space-between}.title{font-weight:750;overflow-wrap:anywhere}.preview{color:var(--muted-text-color,#607080);overflow-wrap:anywhere}.meta{align-items:center;display:flex;flex-wrap:wrap;gap:.4rem;font-size:.75rem}.badge{border:1px solid var(--border-color,#ccd4df);border-radius:999px;padding:.15rem .45rem;text-transform:uppercase}.unread{display:grid;place-items:center;min-width:1.4rem;height:1.4rem;border-radius:999px;color:var(--button-text-color,#fff);background:var(--focus-color,#7657d5);font-weight:800}.empty{padding:2rem;text-align:center}.empty p{color:var(--muted-text-color,#607080)}
|
|
4
4
|
</style>
|
|
5
5
|
<section aria-label="Unified inbox">
|
|
6
6
|
<div class="toolbar"><div class="toolbar-row"><input id="search" type="search" placeholder="Search conversations" aria-label="Search conversations"><button id="refresh" class="arcane-button arcane-button--tertiary" type="button">Refresh</button></div><div id="filters" class="filters" aria-label="Channel filters"></div></div>
|
|
@@ -20,6 +20,6 @@ root.querySelector('#refresh').addEventListener('click',()=>emit('inbox-refresh'
|
|
|
20
20
|
search.addEventListener('input',render);
|
|
21
21
|
function renderFilters(values){const names=['all',...new Set(values||[])];filters.replaceChildren(...names.map(name=>{const button=document.createElement('button');button.type='button';button.className='arcane-button arcane-button--tertiary';button.textContent=name==='all'?'All':name.toUpperCase();button.setAttribute('aria-pressed',String(channel===name));button.addEventListener('click',()=>{channel=name;renderFilters(values);render()});return button}))}
|
|
22
22
|
function render(){const term=search.value.trim().toLowerCase();const visible=threads.filter(item=>(channel==='all'||item.channel===channel)&&(!term||`${item.title} ${item.preview} ${(item.participants||[]).join(' ')}`.toLowerCase().includes(term)));empty.hidden=visible.length>0;list.replaceChildren(...visible.map(makeThread));}
|
|
23
|
-
function makeThread(item){const button=document.createElement('button');button.type='button';button.className='thread';button.setAttribute('aria-current',String(item.id===activeId));const top=document.createElement('span'),title=document.createElement('span'),time=document.createElement('time'),preview=document.createElement('span'),meta=document.createElement('span'),badge=document.createElement('span'),provider=document.createElement('span');top.className='line';title.className='title';title.textContent=item.title;time.dateTime=item.updatedAt;time.textContent=new Intl.DateTimeFormat(undefined,{month:'short',day:'numeric'}).format(new Date(item.updatedAt));preview.className='preview';preview.textContent=item.preview||'No preview';meta.className='meta';badge.className='badge';badge.textContent=item.channel;provider.textContent=providers[item.providerId]?.label||item.providerId;top.append(title,time);meta.append(badge,provider);if(item.unreadCount){const unread=document.createElement('span');unread.className='unread';unread.textContent=String(item.unreadCount);meta.append(unread)}button.append(top,preview,meta);button.addEventListener('click',()=>emit('thread-select',
|
|
23
|
+
function makeThread(item){const button=document.createElement('button');button.type='button';button.className='thread';button.setAttribute('aria-current',String(item.id===activeId));const top=document.createElement('span'),title=document.createElement('span'),time=document.createElement('time'),preview=document.createElement('span'),meta=document.createElement('span'),badge=document.createElement('span'),provider=document.createElement('span');top.className='line';title.className='title';title.textContent=item.title;time.dateTime=item.updatedAt;time.textContent=new Intl.DateTimeFormat(undefined,{month:'short',day:'numeric'}).format(new Date(item.updatedAt));preview.className='preview';preview.textContent=item.preview||'No preview';meta.className='meta';badge.className='badge';badge.textContent=item.channel;provider.textContent=providers[item.providerId]?.label||item.providerId;top.append(title,time);meta.append(badge,provider);if(item.unreadCount){const unread=document.createElement('span');unread.className='unread';unread.textContent=String(item.unreadCount);meta.append(unread)}button.append(top,preview,meta);const detail={thread:item};button.addEventListener('click',()=>emit('thread-select',detail,detail));return button;}
|
|
24
24
|
host.ready=true;emit('unified-inbox-ready',{}, {ready:true});
|
|
25
25
|
</script>
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
</section>
|
|
61
61
|
|
|
62
62
|
<script type="module">
|
|
63
|
+
const completeValue=(value)=>value;
|
|
63
64
|
const {
|
|
64
65
|
createArcaneEventSource,
|
|
65
66
|
projectArcaneDOMEvent
|
|
@@ -203,7 +204,7 @@
|
|
|
203
204
|
}
|
|
204
205
|
);
|
|
205
206
|
|
|
206
|
-
const lifecycleListenerOptions=
|
|
207
|
+
const lifecycleListenerOptions=completeValue(
|
|
207
208
|
{signal:runtimeStateAbortController.signal}
|
|
208
209
|
);
|
|
209
210
|
startButton.addEventListener('click',startRecording,lifecycleListenerOptions);
|
|
@@ -225,7 +226,7 @@
|
|
|
225
226
|
);
|
|
226
227
|
|
|
227
228
|
function unavailableRole(role){
|
|
228
|
-
return
|
|
229
|
+
return completeValue(
|
|
229
230
|
{
|
|
230
231
|
role,
|
|
231
232
|
state:'unavailable',
|
|
@@ -465,8 +466,8 @@
|
|
|
465
466
|
||destroyed){
|
|
466
467
|
return false;
|
|
467
468
|
}
|
|
468
|
-
const segment=typeof result==='string'?result
|
|
469
|
-
if(
|
|
469
|
+
const segment=typeof result==='string'?result:'';
|
|
470
|
+
if(segment.length===0){
|
|
470
471
|
setState('idle',options.messages.noSpeech);
|
|
471
472
|
return false;
|
|
472
473
|
}
|
|
@@ -551,8 +552,8 @@
|
|
|
551
552
|
composed:true,
|
|
552
553
|
operationId,
|
|
553
554
|
publicDetail:{
|
|
554
|
-
|
|
555
|
-
|
|
555
|
+
text:detail.text,
|
|
556
|
+
transcript:detail.transcript
|
|
556
557
|
}
|
|
557
558
|
}
|
|
558
559
|
);
|
|
@@ -567,8 +568,8 @@
|
|
|
567
568
|
composed:true,
|
|
568
569
|
operationId,
|
|
569
570
|
publicDetail:{
|
|
570
|
-
|
|
571
|
-
|
|
571
|
+
text:detail.text,
|
|
572
|
+
transcript:detail.transcript
|
|
572
573
|
}
|
|
573
574
|
}
|
|
574
575
|
);
|
|
@@ -637,7 +638,7 @@
|
|
|
637
638
|
}
|
|
638
639
|
|
|
639
640
|
async function completeStream(){
|
|
640
|
-
if(destroyed
|
|
641
|
+
if(destroyed||transcript.length===0||!['idle','error'].includes(state)){
|
|
641
642
|
return false;
|
|
642
643
|
}
|
|
643
644
|
const generation=sessionGeneration;
|
|
@@ -665,7 +666,7 @@
|
|
|
665
666
|
bubbles:true,
|
|
666
667
|
composed:true,
|
|
667
668
|
publicDetail:{
|
|
668
|
-
|
|
669
|
+
transcript:completionTranscript
|
|
669
670
|
}
|
|
670
671
|
}
|
|
671
672
|
);
|
|
@@ -743,7 +744,7 @@
|
|
|
743
744
|
bubbles:true,
|
|
744
745
|
composed:true,
|
|
745
746
|
publicDetail:{
|
|
746
|
-
|
|
747
|
+
transcript
|
|
747
748
|
}
|
|
748
749
|
}
|
|
749
750
|
);
|
|
@@ -765,7 +766,7 @@
|
|
|
765
766
|
transcriptElement.querySelectorAll('a').forEach(
|
|
766
767
|
link=>{
|
|
767
768
|
link.target='_blank';
|
|
768
|
-
link.rel
|
|
769
|
+
link.removeAttribute('rel');
|
|
769
770
|
}
|
|
770
771
|
);
|
|
771
772
|
}
|
|
@@ -820,6 +821,9 @@
|
|
|
820
821
|
function synchronizeAIRuntimeState(snapshot){
|
|
821
822
|
const previousRole=sttRole;
|
|
822
823
|
sttRole=snapshot.roles.stt;
|
|
824
|
+
if(sttRole.state==='error'&&previousRole.state!=='error'){
|
|
825
|
+
console.error('Arcane transcription runtime failed.',sttRole.error);
|
|
826
|
+
}
|
|
823
827
|
sttActivationController.synchronize(sttRole);
|
|
824
828
|
if(previousRole.state==='ready'&&sttRole.state!=='ready'){
|
|
825
829
|
if(cancelSTTOperation('runtime-unready')){
|
|
@@ -853,7 +857,7 @@
|
|
|
853
857
|
stateMessage=message;
|
|
854
858
|
dispatchVoiceEvent(
|
|
855
859
|
'speech-transcription-cancelled',
|
|
856
|
-
|
|
860
|
+
completeValue({reason}),
|
|
857
861
|
{
|
|
858
862
|
bubbles:true,
|
|
859
863
|
composed:true,
|
|
@@ -886,7 +890,7 @@
|
|
|
886
890
|
startButton.setAttribute('aria-label',startButton.title);
|
|
887
891
|
stopButton.disabled=state!=='recording';
|
|
888
892
|
completeButton.disabled=destroyed
|
|
889
|
-
|
|
893
|
+
||transcript.length===0
|
|
890
894
|
||!['idle','error'].includes(state);
|
|
891
895
|
sttActivationButton.hidden=destroyed||!sttActivationController.visible;
|
|
892
896
|
sttActivationButton.disabled=destroyed
|
|
@@ -905,7 +909,7 @@
|
|
|
905
909
|
);
|
|
906
910
|
dispatchVoiceEvent(
|
|
907
911
|
'voice-transcription-state',
|
|
908
|
-
|
|
912
|
+
completeValue(
|
|
909
913
|
{
|
|
910
914
|
message:status.innerText,
|
|
911
915
|
state,
|
|
@@ -915,10 +919,7 @@
|
|
|
915
919
|
{
|
|
916
920
|
bubbles:true,
|
|
917
921
|
composed:true,
|
|
918
|
-
publicDetail:{
|
|
919
|
-
state,
|
|
920
|
-
sttState:sttRole.state
|
|
921
|
-
}
|
|
922
|
+
publicDetail:{message:status.innerText,state,stt:sttRole}
|
|
922
923
|
}
|
|
923
924
|
);
|
|
924
925
|
}
|
|
@@ -958,8 +959,17 @@
|
|
|
958
959
|
}
|
|
959
960
|
|
|
960
961
|
function visibleErrorMessage(error,fallback){
|
|
961
|
-
|
|
962
|
-
|
|
962
|
+
try{
|
|
963
|
+
if(error?.userSafe===true){
|
|
964
|
+
const message=typeof error.userMessage==='string'
|
|
965
|
+
?error.userMessage
|
|
966
|
+
:error.message;
|
|
967
|
+
if(typeof message==='string'&&message.trim()){
|
|
968
|
+
return message;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}catch{}
|
|
972
|
+
return fallback;
|
|
963
973
|
}
|
|
964
974
|
|
|
965
975
|
function getOptions(){
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
export default class Calculation{
|
|
2
2
|
constructor({expression,result,createdAt=new Date()}={}){
|
|
3
|
-
this.expression=String(expression??'')
|
|
4
|
-
if(!this.expression
|
|
3
|
+
this.expression=String(expression??'');
|
|
4
|
+
if(!this.expression.trim()) throw new TypeError('Calculation expressions must contain text.');
|
|
5
5
|
this.result=Number(result);
|
|
6
6
|
if(!Number.isFinite(this.result)) throw new RangeError('Calculation result must be finite.');
|
|
7
7
|
const instant=new Date(createdAt);
|
|
8
8
|
if(Number.isNaN(instant.valueOf())) throw new TypeError('Calculation time is invalid.');
|
|
9
9
|
this.createdAt=instant.toISOString();
|
|
10
|
-
Object.freeze(this);
|
|
11
10
|
}
|
|
12
11
|
toJSON(){return {...this};}
|
|
13
12
|
}
|