arcane-os 0.2.3 → 0.3.1
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 +30 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
- package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +3242 -391
- package/browser-runtime/ai/browser-speech-providers.mjs +1209 -157
- package/browser-runtime/ai/browser-speech.mjs +2 -0
- package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
- package/browser-runtime/ai/model-controller.mjs +285 -95
- package/browser-runtime/ai/speech-worker-client.mjs +249 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2322 -167
- package/browser-runtime/event-manager.mjs +1097 -1
- package/docs/architecture.md +2 -2
- package/docs/event-manager.md +155 -27
- package/docs/reference/README.md +18 -20
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1205 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +2 -2
- package/docs/reference/behavioral-testing.md +28 -5
- package/docs/reference/cli.md +115 -7
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/event-manager.md +577 -32
- package/docs/reference/inventory/package-api.json +478 -2
- package/docs/reference/inventory/runtime-components.json +86 -37
- package/docs/reference/inventory/runtime-modules.json +126 -49
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +185 -33
- package/docs/reference/runtime-components.md +192 -80
- package/docs/reference/runtime-modules.md +589 -70
- package/docs/reference/sdk-api.md +1017 -24
- package/package.json +5 -4
- package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
- package/runtime/arcane/components/app-bar.html +34 -13
- package/runtime/arcane/components/assistant-panel.html +110 -57
- package/runtime/arcane/components/calculator.html +7 -4
- package/runtime/arcane/components/chart.html +58 -17
- package/runtime/arcane/components/chat.html +606 -136
- package/runtime/arcane/components/conversation-view.html +13 -6
- package/runtime/arcane/components/dashboard-config.html +96 -59
- package/runtime/arcane/components/data-maintenance.html +69 -14
- package/runtime/arcane/components/data-view.html +53 -7
- package/runtime/arcane/components/directory-picker.html +118 -32
- package/runtime/arcane/components/document-inspector.html +47 -10
- package/runtime/arcane/components/file-drop.html +81 -35
- package/runtime/arcane/components/file-inspector.html +72 -22
- package/runtime/arcane/components/file-manager.html +374 -79
- package/runtime/arcane/components/integration-settings.html +12 -5
- package/runtime/arcane/components/local-ai-status.html +48 -19
- package/runtime/arcane/components/markdown-document.html +161 -68
- package/runtime/arcane/components/markdown-editor.html +110 -33
- package/runtime/arcane/components/media-embed.html +8 -5
- package/runtime/arcane/components/modal.html +15 -5
- package/runtime/arcane/components/output-panel.html +28 -23
- package/runtime/arcane/components/preferences-form.html +22 -4
- package/runtime/arcane/components/record-timeline.html +18 -2
- package/runtime/arcane/components/relationship-board.html +23 -3
- package/runtime/arcane/components/screen-capture.html +10 -4
- package/runtime/arcane/components/source-code-viewer.html +76 -9
- package/runtime/arcane/components/source-explanation.html +23 -3
- package/runtime/arcane/components/speech.html +453 -131
- package/runtime/arcane/components/summary-strip.html +22 -11
- package/runtime/arcane/components/table.html +39 -21
- package/runtime/arcane/components/task-progress.html +79 -21
- package/runtime/arcane/components/terminal-workspace.html +7 -4
- package/runtime/arcane/components/theme-editor.html +7 -3
- package/runtime/arcane/components/unified-inbox.html +9 -4
- package/runtime/arcane/components/voice-transcription.html +244 -72
- package/runtime/arcane/components/weather-widget.html +5 -3
- package/runtime/arcane/components/web-navigator.html +48 -8
- package/runtime/arcane/entities/Chat.js +1 -1
- package/runtime/arcane/entities/User.js +110 -23
- package/runtime/arcane/modules/AI.js +2117 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +756 -13
- package/runtime/arcane/modules/AIRuntimeState.js +109 -52
- package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
- package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
- package/runtime/arcane/modules/CalculatorEngine.js +63 -3
- package/runtime/arcane/modules/CommunicationAppController.js +588 -28
- package/runtime/arcane/modules/CommunicationHub.js +590 -10
- package/runtime/arcane/modules/ComponentContracts.js +235 -37
- package/runtime/arcane/modules/ConversationTimebox.js +152 -33
- package/runtime/arcane/modules/DBLS.js +40 -7
- package/runtime/arcane/modules/DBOPFS.js +35 -11
- package/runtime/arcane/modules/DataMaintenance.js +12 -2
- package/runtime/arcane/modules/Errors.js +65 -7
- package/runtime/arcane/modules/HTMLImport.js +198 -14
- package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
- package/runtime/arcane/modules/Mail.js +738 -115
- package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
- package/runtime/arcane/modules/MailTransport.mjs +197 -39
- package/runtime/arcane/modules/Ollama.js +36 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
- package/runtime/arcane/modules/PreferenceStore.js +367 -33
- package/runtime/arcane/modules/RecordReviewStore.js +322 -23
- package/runtime/arcane/modules/ScreenCapture.js +1397 -15
- package/runtime/arcane/modules/SpeechPlayback.js +438 -41
- package/runtime/arcane/modules/TerminalClient.js +277 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
- package/runtime/arcane/modules/ThemeManager.js +39 -7
- package/runtime/arcane/modules/TimeGuard.js +131 -20
- package/runtime/arcane/modules/WaitForComponent.js +386 -33
- package/schemas/arcane-lock.schema.json +2 -2
- package/src/cli/main.mjs +435 -9
- package/src/event-manager.mjs +1097 -1
- package/src/import-map.mjs +21 -3
- package/src/index.mjs +13 -0
- package/src/installed-sdk-runtime.mjs +130 -0
- package/src/mail-api.mjs +22 -0
- package/src/mail-credentials.mjs +667 -0
- package/src/mail-server.mjs +1769 -0
- package/src/mail.mjs +261 -0
- package/src/sdk-browser-runtime.mjs +85 -41
- package/src/testing-loader.mjs +7 -0
- package/src/toolchain.mjs +3 -0
- package/src/workspace-runtime.mjs +804 -22
- package/src/workspace.mjs +1 -1
|
@@ -60,7 +60,29 @@
|
|
|
60
60
|
</section>
|
|
61
61
|
|
|
62
62
|
<script type="module">
|
|
63
|
+
const {
|
|
64
|
+
createArcaneEventSource,
|
|
65
|
+
projectArcaneDOMEvent
|
|
66
|
+
}=await import('arcane-os/event-manager');
|
|
67
|
+
|
|
63
68
|
const host=this;
|
|
69
|
+
const events=createArcaneEventSource(
|
|
70
|
+
host,
|
|
71
|
+
{
|
|
72
|
+
source:'arcane.component.voice-transcription',
|
|
73
|
+
eventTypes:[
|
|
74
|
+
'voice-transcription-ready',
|
|
75
|
+
'voice-transcription-state',
|
|
76
|
+
'voice-transcription-segment',
|
|
77
|
+
'voice-transcription-change',
|
|
78
|
+
'voice-transcription-complete',
|
|
79
|
+
'speech-transcription-complete',
|
|
80
|
+
'speech-transcription-cancelled',
|
|
81
|
+
'speech-stt-activation-request',
|
|
82
|
+
'speech-stt-activation-error'
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
64
86
|
const componentURL=new URL(
|
|
65
87
|
host.getAttribute('href')||'./arcane/components/voice-transcription.html',
|
|
66
88
|
document.baseURI
|
|
@@ -94,9 +116,72 @@
|
|
|
94
116
|
let transcript='';
|
|
95
117
|
let sessionGeneration=0;
|
|
96
118
|
let transcriptionAbortController=null;
|
|
119
|
+
let eventOperationSequence=0;
|
|
120
|
+
let voiceOperationId=null;
|
|
97
121
|
let destroyed=false;
|
|
98
122
|
let options=normalizeVoiceOptions(optionsFromDataset());
|
|
99
123
|
|
|
124
|
+
function nextVoiceOperationId(kind){
|
|
125
|
+
eventOperationSequence+=1;
|
|
126
|
+
return `${events.descriptor.instanceId}:${kind}:${eventOperationSequence}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function currentVoiceOperationId(kind='transcription'){
|
|
130
|
+
voiceOperationId=voiceOperationId||nextVoiceOperationId(kind);
|
|
131
|
+
return voiceOperationId;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function canonicalSTTCancellationReason(reason){
|
|
135
|
+
if(reason==='runtime-unready'){
|
|
136
|
+
return 'stt-role-unready';
|
|
137
|
+
}
|
|
138
|
+
if(reason==='stt-provider-request-cancelled'){
|
|
139
|
+
return 'stt-provider-transcription-cancelled';
|
|
140
|
+
}
|
|
141
|
+
return String(reason||'stt-transcription-cancelled');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function dispatchVoiceEvent(
|
|
145
|
+
type,
|
|
146
|
+
compatibilityDetail={},
|
|
147
|
+
{
|
|
148
|
+
publicDetail={},
|
|
149
|
+
operationId=null,
|
|
150
|
+
beginOperation=null,
|
|
151
|
+
bubbles=false,
|
|
152
|
+
composed=false,
|
|
153
|
+
cancelable=false
|
|
154
|
+
}={}
|
|
155
|
+
){
|
|
156
|
+
if(destroyed&&type!=='speech-transcription-cancelled'){
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if(beginOperation!==null){
|
|
160
|
+
voiceOperationId=nextVoiceOperationId(beginOperation);
|
|
161
|
+
}
|
|
162
|
+
const publication=events.dispatch(
|
|
163
|
+
type,
|
|
164
|
+
compatibilityDetail,
|
|
165
|
+
{
|
|
166
|
+
publicDetail,
|
|
167
|
+
operationId:operationId||currentVoiceOperationId(),
|
|
168
|
+
cancelable
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
if(!publication.accepted){
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return projectArcaneDOMEvent(
|
|
175
|
+
host,
|
|
176
|
+
publication.occurrence,
|
|
177
|
+
{
|
|
178
|
+
bubbles,
|
|
179
|
+
composed,
|
|
180
|
+
cancelable
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
100
185
|
host.clear=clear;
|
|
101
186
|
host.completeTranscription=completeStream;
|
|
102
187
|
host.configure=configure;
|
|
@@ -118,16 +203,24 @@
|
|
|
118
203
|
}
|
|
119
204
|
);
|
|
120
205
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
206
|
+
const lifecycleListenerOptions=Object.freeze(
|
|
207
|
+
{signal:runtimeStateAbortController.signal}
|
|
208
|
+
);
|
|
209
|
+
startButton.addEventListener('click',startRecording,lifecycleListenerOptions);
|
|
210
|
+
stopButton.addEventListener('click',stopRecording,lifecycleListenerOptions);
|
|
211
|
+
completeButton.addEventListener('click',completeStream,lifecycleListenerOptions);
|
|
212
|
+
window.addEventListener(
|
|
213
|
+
'pagehide',
|
|
214
|
+
destroy,
|
|
215
|
+
{once:true,signal:runtimeStateAbortController.signal}
|
|
216
|
+
);
|
|
125
217
|
|
|
126
218
|
const sttActivationController=createSTTActivationController(
|
|
127
219
|
{
|
|
128
220
|
host,
|
|
129
221
|
button:sttActivationButton,
|
|
130
|
-
onChange:renderState
|
|
222
|
+
onChange:renderState,
|
|
223
|
+
eventSource:events
|
|
131
224
|
}
|
|
132
225
|
);
|
|
133
226
|
|
|
@@ -178,8 +271,20 @@
|
|
|
178
271
|
}
|
|
179
272
|
|
|
180
273
|
function configure(input={}){
|
|
274
|
+
if(destroyed){
|
|
275
|
+
return getOptions();
|
|
276
|
+
}
|
|
181
277
|
const setInitial=Object.prototype.hasOwnProperty.call(input||{},'initialValue');
|
|
182
|
-
|
|
278
|
+
const nextOptions=normalizeVoiceOptions(input,options);
|
|
279
|
+
if(!setInitial){
|
|
280
|
+
if(supersedeForConfigurationReplacement()){
|
|
281
|
+
reportSTTCancellation(
|
|
282
|
+
'configuration-replaced',
|
|
283
|
+
'Transcription configuration replaced.'
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
options=nextOptions;
|
|
183
288
|
renderOptions();
|
|
184
289
|
if(setInitial){
|
|
185
290
|
setValue(options.initialValue);
|
|
@@ -203,7 +308,9 @@
|
|
|
203
308
|
|
|
204
309
|
const generation=++sessionGeneration;
|
|
205
310
|
chunks=[];
|
|
206
|
-
setState('starting',options.messages.requesting
|
|
311
|
+
setState('starting',options.messages.requesting,{
|
|
312
|
+
beginOperation:'transcription'
|
|
313
|
+
});
|
|
207
314
|
if(!recordingStartIsCurrent(generation,'starting')){
|
|
208
315
|
return rejectRecordingStart(generation);
|
|
209
316
|
}
|
|
@@ -293,7 +400,7 @@
|
|
|
293
400
|
}
|
|
294
401
|
|
|
295
402
|
function stopRecording(){
|
|
296
|
-
if(state!=='recording'||recorder?.state!=='recording'){
|
|
403
|
+
if(destroyed||state!=='recording'||recorder?.state!=='recording'){
|
|
297
404
|
return false;
|
|
298
405
|
}
|
|
299
406
|
const generation=sessionGeneration;
|
|
@@ -321,6 +428,15 @@
|
|
|
321
428
|
setState('error',options.messages.emptyAudio);
|
|
322
429
|
return false;
|
|
323
430
|
}
|
|
431
|
+
if(sttRole.state!=='ready'||sttRole.busy){
|
|
432
|
+
reportSTTCancellation(
|
|
433
|
+
sttRole.busy?'stt-role-busy':'runtime-unready',
|
|
434
|
+
sttRole.busy
|
|
435
|
+
?'Transcription stopped because the selected service is busy.'
|
|
436
|
+
:'Transcription stopped because the selected service became unavailable.'
|
|
437
|
+
);
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
324
440
|
|
|
325
441
|
try{
|
|
326
442
|
controller=new AbortController();
|
|
@@ -345,6 +461,7 @@
|
|
|
345
461
|
if(controller.signal.aborted
|
|
346
462
|
||generation!==sessionGeneration
|
|
347
463
|
||sttRole.state!=='ready'
|
|
464
|
+
||sttRole.busy
|
|
348
465
|
||destroyed){
|
|
349
466
|
return false;
|
|
350
467
|
}
|
|
@@ -393,7 +510,8 @@
|
|
|
393
510
|
detail,
|
|
394
511
|
options.persist
|
|
395
512
|
?options.messages.saved
|
|
396
|
-
:options.messages.transcribed
|
|
513
|
+
:options.messages.transcribed,
|
|
514
|
+
currentVoiceOperationId()
|
|
397
515
|
);
|
|
398
516
|
}catch(error){
|
|
399
517
|
if(generation!==sessionGeneration||destroyed){
|
|
@@ -419,25 +537,40 @@
|
|
|
419
537
|
generation,
|
|
420
538
|
expectedState,
|
|
421
539
|
detail,
|
|
422
|
-
message
|
|
540
|
+
message,
|
|
541
|
+
operationId=null
|
|
423
542
|
){
|
|
424
543
|
if(!isCurrentVoiceOperation(generation,expectedState)){
|
|
425
544
|
return false;
|
|
426
545
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
546
|
+
dispatchVoiceEvent(
|
|
547
|
+
'speech-transcription-complete',
|
|
548
|
+
detail,
|
|
549
|
+
{
|
|
550
|
+
bubbles:true,
|
|
551
|
+
composed:true,
|
|
552
|
+
operationId,
|
|
553
|
+
publicDetail:{
|
|
554
|
+
segmentCharacterCount:detail.text.length,
|
|
555
|
+
transcriptCharacterCount:detail.transcript.length
|
|
556
|
+
}
|
|
557
|
+
}
|
|
432
558
|
);
|
|
433
559
|
if(!isCurrentVoiceOperation(generation,expectedState)){
|
|
434
560
|
return false;
|
|
435
561
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
562
|
+
dispatchVoiceEvent(
|
|
563
|
+
'voice-transcription-segment',
|
|
564
|
+
detail,
|
|
565
|
+
{
|
|
566
|
+
bubbles:true,
|
|
567
|
+
composed:true,
|
|
568
|
+
operationId,
|
|
569
|
+
publicDetail:{
|
|
570
|
+
segmentCharacterCount:detail.text.length,
|
|
571
|
+
transcriptCharacterCount:detail.transcript.length
|
|
572
|
+
}
|
|
573
|
+
}
|
|
441
574
|
);
|
|
442
575
|
if(!isCurrentVoiceOperation(generation,expectedState)){
|
|
443
576
|
return false;
|
|
@@ -459,10 +592,12 @@
|
|
|
459
592
|
return options.transcribe(file,context);
|
|
460
593
|
}
|
|
461
594
|
if(typeof globalThis.ai?.fetchSTT!=='function'){
|
|
462
|
-
await import(moduleURL('AI.js
|
|
595
|
+
await import(moduleURL('AI.js'));
|
|
463
596
|
}
|
|
464
597
|
if(typeof globalThis.ai?.fetchSTT!=='function'){
|
|
465
|
-
|
|
598
|
+
const error=new Error('Speech transcription is not configured.');
|
|
599
|
+
error.code='ARCANE_VOICE_TRANSCRIPTION_STT_RUNTIME_METHOD_UNAVAILABLE';
|
|
600
|
+
throw error;
|
|
466
601
|
}
|
|
467
602
|
return globalThis.ai.fetchSTT(file,undefined,signal);
|
|
468
603
|
}
|
|
@@ -510,7 +645,9 @@
|
|
|
510
645
|
const complete=options.onComplete||(
|
|
511
646
|
typeof host.complete==='function'?host.complete.bind(host):null
|
|
512
647
|
);
|
|
513
|
-
setState('completing',options.messages.completing
|
|
648
|
+
setState('completing',options.messages.completing,{
|
|
649
|
+
beginOperation:'completion'
|
|
650
|
+
});
|
|
514
651
|
if(!isCurrentVoiceOperation(generation,'completing')){
|
|
515
652
|
return false;
|
|
516
653
|
}
|
|
@@ -521,15 +658,16 @@
|
|
|
521
658
|
if(!isCurrentVoiceOperation(generation,'completing')){
|
|
522
659
|
return false;
|
|
523
660
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
661
|
+
dispatchVoiceEvent(
|
|
662
|
+
'voice-transcription-complete',
|
|
663
|
+
{transcript:completionTranscript},
|
|
664
|
+
{
|
|
665
|
+
bubbles:true,
|
|
666
|
+
composed:true,
|
|
667
|
+
publicDetail:{
|
|
668
|
+
transcriptCharacterCount:completionTranscript.length
|
|
531
669
|
}
|
|
532
|
-
|
|
670
|
+
}
|
|
533
671
|
);
|
|
534
672
|
if(!isCurrentVoiceOperation(generation,'completing')){
|
|
535
673
|
return false;
|
|
@@ -563,7 +701,28 @@
|
|
|
563
701
|
return true;
|
|
564
702
|
}
|
|
565
703
|
|
|
704
|
+
function supersedeForConfigurationReplacement(){
|
|
705
|
+
const hadActiveOperation=[
|
|
706
|
+
'starting',
|
|
707
|
+
'recording',
|
|
708
|
+
'transcribing',
|
|
709
|
+
'saving',
|
|
710
|
+
'completing'
|
|
711
|
+
].includes(state)||Boolean(transcriptionAbortController);
|
|
712
|
+
if(!hadActiveOperation){
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
sessionGeneration+=1;
|
|
716
|
+
transcriptionAbortController?.abort();
|
|
717
|
+
transcriptionAbortController=null;
|
|
718
|
+
releaseMicrophone();
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
|
|
566
722
|
function setValue(value=''){
|
|
723
|
+
if(destroyed){
|
|
724
|
+
return transcript;
|
|
725
|
+
}
|
|
567
726
|
const operationSuperseded=
|
|
568
727
|
supersedeForTranscriptReplacement();
|
|
569
728
|
transcript=`${value??''}`;
|
|
@@ -576,24 +735,23 @@
|
|
|
576
735
|
}else if(state!=='recording'){
|
|
577
736
|
setState('idle',options.messages.ready);
|
|
578
737
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
detail:{transcript}
|
|
738
|
+
dispatchVoiceEvent(
|
|
739
|
+
'voice-transcription-change',
|
|
740
|
+
{transcript},
|
|
741
|
+
{
|
|
742
|
+
beginOperation:'transcript-change',
|
|
743
|
+
bubbles:true,
|
|
744
|
+
composed:true,
|
|
745
|
+
publicDetail:{
|
|
746
|
+
transcriptCharacterCount:transcript.length
|
|
589
747
|
}
|
|
590
|
-
|
|
748
|
+
}
|
|
591
749
|
);
|
|
592
750
|
return transcript;
|
|
593
751
|
}
|
|
594
752
|
|
|
595
753
|
function clear(){
|
|
596
|
-
if(
|
|
754
|
+
if(destroyed||!['complete','error','idle'].includes(state)){
|
|
597
755
|
return false;
|
|
598
756
|
}
|
|
599
757
|
transcript='';
|
|
@@ -646,10 +804,17 @@
|
|
|
646
804
|
}
|
|
647
805
|
}
|
|
648
806
|
|
|
649
|
-
function setState(nextState,message=''){
|
|
807
|
+
function setState(nextState,message='',{beginOperation=null}={}){
|
|
808
|
+
if(destroyed){
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
if(beginOperation!==null){
|
|
812
|
+
voiceOperationId=nextVoiceOperationId(beginOperation);
|
|
813
|
+
}
|
|
650
814
|
state=nextState;
|
|
651
815
|
stateMessage=message||nextState;
|
|
652
816
|
renderState();
|
|
817
|
+
return true;
|
|
653
818
|
}
|
|
654
819
|
|
|
655
820
|
function synchronizeAIRuntimeState(snapshot){
|
|
@@ -683,18 +848,18 @@
|
|
|
683
848
|
return true;
|
|
684
849
|
}
|
|
685
850
|
|
|
686
|
-
function reportSTTCancellation(reason,message){
|
|
851
|
+
function reportSTTCancellation(reason,message,operationId=null){
|
|
687
852
|
state='idle';
|
|
688
853
|
stateMessage=message;
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}
|
|
697
|
-
|
|
854
|
+
dispatchVoiceEvent(
|
|
855
|
+
'speech-transcription-cancelled',
|
|
856
|
+
Object.freeze({reason}),
|
|
857
|
+
{
|
|
858
|
+
bubbles:true,
|
|
859
|
+
composed:true,
|
|
860
|
+
operationId:operationId||currentVoiceOperationId(),
|
|
861
|
+
publicDetail:{reason:canonicalSTTCancellationReason(reason)}
|
|
862
|
+
}
|
|
698
863
|
);
|
|
699
864
|
if(!destroyed){
|
|
700
865
|
renderState();
|
|
@@ -738,21 +903,23 @@
|
|
|
738
903
|
||['loading','unloading'].includes(sttRole.state)
|
|
739
904
|
)
|
|
740
905
|
);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
906
|
+
dispatchVoiceEvent(
|
|
907
|
+
'voice-transcription-state',
|
|
908
|
+
Object.freeze(
|
|
744
909
|
{
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
{
|
|
749
|
-
message:status.innerText,
|
|
750
|
-
state,
|
|
751
|
-
stt:sttRole
|
|
752
|
-
}
|
|
753
|
-
)
|
|
910
|
+
message:status.innerText,
|
|
911
|
+
state,
|
|
912
|
+
stt:sttRole
|
|
754
913
|
}
|
|
755
|
-
)
|
|
914
|
+
),
|
|
915
|
+
{
|
|
916
|
+
bubbles:true,
|
|
917
|
+
composed:true,
|
|
918
|
+
publicDetail:{
|
|
919
|
+
state,
|
|
920
|
+
sttState:sttRole.state
|
|
921
|
+
}
|
|
922
|
+
}
|
|
756
923
|
);
|
|
757
924
|
}
|
|
758
925
|
|
|
@@ -820,6 +987,7 @@
|
|
|
820
987
|
stateMessage='Transcription unavailable.';
|
|
821
988
|
host.ready=false;
|
|
822
989
|
renderState();
|
|
990
|
+
events.dispose();
|
|
823
991
|
return true;
|
|
824
992
|
}
|
|
825
993
|
|
|
@@ -835,11 +1003,15 @@
|
|
|
835
1003
|
}
|
|
836
1004
|
if(!destroyed){
|
|
837
1005
|
host.ready=true;
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1006
|
+
dispatchVoiceEvent(
|
|
1007
|
+
'voice-transcription-ready',
|
|
1008
|
+
{},
|
|
1009
|
+
{
|
|
1010
|
+
beginOperation:'ready',
|
|
1011
|
+
bubbles:true,
|
|
1012
|
+
composed:true,
|
|
1013
|
+
publicDetail:{ready:true}
|
|
1014
|
+
}
|
|
843
1015
|
);
|
|
844
1016
|
}
|
|
845
1017
|
</script>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<link rel="stylesheet" href="./arcane/css/theme.css?v=1"><link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
|
|
2
2
|
<style>:host{display:block}.widget{display:grid;gap:var(--arcane-space-4)}.current{align-items:center;display:grid;gap:var(--arcane-space-4);grid-template-columns:auto 1fr auto}.symbol{font-size:clamp(2.5rem,8vw,5rem);line-height:1}.temperature{font-size:clamp(2.2rem,7vw,4.5rem);font-weight:760;line-height:1}.location h2,.location p{margin:0}.location p,.details{color:var(--arcane-muted)}.details{display:grid;gap:var(--arcane-space-1);text-align:right}.forecast{display:grid;gap:var(--arcane-space-2);grid-template-columns:repeat(7,minmax(5.5rem,1fr));overflow:auto}.day{display:grid;gap:var(--arcane-space-2);padding:var(--arcane-space-3);text-align:center;background:var(--arcane-surface-muted);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-medium)}.day strong{white-space:nowrap}.day-symbol{font-size:1.8rem}.empty{padding:var(--arcane-space-6);text-align:center;color:var(--arcane-muted)}.footer{display:flex;align-items:center;justify-content:space-between;gap:var(--arcane-space-3)}@media(max-width:36rem){.current{grid-template-columns:auto 1fr}.details{grid-column:1/-1;text-align:left;grid-template-columns:repeat(3,1fr)}}</style>
|
|
3
3
|
<section class="widget arcane-card"><div id="empty" class="empty"><strong>Weather is ready for a location</strong><p>Search for a city or postal code to load current conditions and the forecast.</p></div><div id="content" hidden><div class="arcane-card__body current"><span id="symbol" class="symbol" aria-hidden="true"></span><div class="location"><h2 id="location" class="arcane-section-heading"></h2><p id="condition"></p><div><span id="temperature" class="temperature"></span></div></div><div class="details"><span id="feels"></span><span id="humidity"></span><span id="wind"></span></div></div><div id="forecast" class="arcane-card__body forecast"></div><footer class="arcane-card__footer footer"><span id="source" class="arcane-help"></span><button id="refresh" class="arcane-button arcane-button--secondary" type="button">Refresh</button></footer></div></section>
|
|
4
|
-
<script type="module">const host=this,root=host.shadowRoot,empty=root.querySelector('#empty'),content=root.querySelector('#content'),forecast=root.querySelector('#forecast');let weather=null;const conditions=new Map([[0,['Clear','☀️']],[1,['Mostly clear','🌤️']],[2,['Partly cloudy','⛅']],[3,['Overcast','☁️']],[45,['Fog','🌫️']],[48,['Freezing fog','🌫️']],[51,['Light drizzle','🌦️']],[53,['Drizzle','🌦️']],[55,['Heavy drizzle','🌧️']],[61,['Light rain','🌦️']],[63,['Rain','🌧️']],[65,['Heavy rain','🌧️']],[71,['Light snow','🌨️']],[73,['Snow','🌨️']],[75,['Heavy snow','❄️']],[80,['Rain showers','🌦️']],[81,['Rain showers','🌧️']],[82,['Heavy showers','⛈️']],[95,['Thunderstorm','⛈️']],[96,['Thunderstorm with hail','⛈️']],[99,['Severe thunderstorm','⛈️']]]);
|
|
5
|
-
|
|
4
|
+
<script type="module">const host=this,root=host.shadowRoot,empty=root.querySelector('#empty'),content=root.querySelector('#content'),forecast=root.querySelector('#forecast');const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');const events=createArcaneEventSource(host,{source:'arcane.component.weather-widget',eventTypes:['weather-refresh','weather-widget-ready']});let weather=null,destroyed=false;const conditions=new Map([[0,['Clear','☀️']],[1,['Mostly clear','🌤️']],[2,['Partly cloudy','⛅']],[3,['Overcast','☁️']],[45,['Fog','🌫️']],[48,['Freezing fog','🌫️']],[51,['Light drizzle','🌦️']],[53,['Drizzle','🌦️']],[55,['Heavy drizzle','🌧️']],[61,['Light rain','🌦️']],[63,['Rain','🌧️']],[65,['Heavy rain','🌧️']],[71,['Light snow','🌨️']],[73,['Snow','🌨️']],[75,['Heavy snow','❄️']],[80,['Rain showers','🌦️']],[81,['Rain showers','🌧️']],[82,['Heavy showers','⛈️']],[95,['Thunderstorm','⛈️']],[96,['Thunderstorm with hail','⛈️']],[99,['Severe thunderstorm','⛈️']]]);
|
|
5
|
+
function emit(type,detail={},publicDetail={}){if(destroyed)return false;const publication=events.dispatch(type,detail,{publicDetail});return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true})}
|
|
6
|
+
host.destroy=()=>{if(destroyed)return false;destroyed=true;host.ready=false;events.dispose();return true};
|
|
7
|
+
host.setWeather=setWeather;host.clear=()=>{weather=null;empty.hidden=false;content.hidden=true};root.querySelector('#refresh').addEventListener('click',()=>emit('weather-refresh',{weather},{hasWeather:Boolean(weather)}));
|
|
6
8
|
function info(code,isDay=true){const value=conditions.get(Number(code))||['Conditions unavailable','🌡️'];if(!isDay&&Number(code)<=1)return ['Clear night','🌙'];return value}
|
|
7
9
|
function setWeather(value){weather=value;if(!value)return host.clear();empty.hidden=true;content.hidden=false;const current=value.current,[label,symbol]=info(current.weatherCode,current.isDay);root.querySelector('#symbol').textContent=symbol;root.querySelector('#location').textContent=[value.location.name,value.location.region,value.location.country].filter(Boolean).join(', ');root.querySelector('#condition').textContent=label;root.querySelector('#temperature').textContent=`${Math.round(current.temperature)}${current.temperatureUnit}`;root.querySelector('#feels').textContent=`Feels ${Math.round(current.apparentTemperature)}${current.temperatureUnit}`;root.querySelector('#humidity').textContent=`Humidity ${Math.round(current.humidity)}%`;root.querySelector('#wind').textContent=`Wind ${Math.round(current.windSpeed)} ${current.windUnit}`;root.querySelector('#source').textContent=`${value.source||'Weather source'} · updated ${new Intl.DateTimeFormat(undefined,{timeStyle:'short'}).format(new Date(value.fetchedAt))}`;forecast.replaceChildren(...Array.from(value.daily||[]).map(day=>{const section=document.createElement('section'),name=document.createElement('strong'),icon=document.createElement('span'),temperatures=document.createElement('span'),rain=document.createElement('span');section.className='day';name.textContent=new Intl.DateTimeFormat(undefined,{weekday:'short'}).format(new Date(day.date));icon.className='day-symbol';icon.textContent=info(day.weatherCode)[1];temperatures.textContent=`${Math.round(day.temperatureMax)}° / ${Math.round(day.temperatureMin)}°`;rain.textContent=`${Math.round(day.precipitationProbability)}% rain`;section.append(name,icon,temperatures,rain);return section}));return weather}
|
|
8
|
-
host.ready=true;
|
|
10
|
+
host.ready=true;emit('weather-widget-ready',{}, {ready:true});</script>
|
|
@@ -61,6 +61,8 @@ button{min-width:2.5rem}
|
|
|
61
61
|
<script type="module">
|
|
62
62
|
const host=this;
|
|
63
63
|
const root=host.shadowRoot;
|
|
64
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
65
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.web-navigator',eventTypes:['web-navigation-blocked','web-navigate','web-open-external','web-navigator-ready']});
|
|
64
66
|
const address=root.querySelector('#address');
|
|
65
67
|
const frame=root.querySelector('#frame');
|
|
66
68
|
const blocked=root.querySelector('#blocked');
|
|
@@ -72,6 +74,20 @@ let history=[];
|
|
|
72
74
|
let position=-1;
|
|
73
75
|
let navigationGuard=null;
|
|
74
76
|
let navigationSequence=0;
|
|
77
|
+
let eventOperationSequence=0;
|
|
78
|
+
let destroyed=false;
|
|
79
|
+
|
|
80
|
+
function emit(type,detail={},publicDetail={}){
|
|
81
|
+
if(destroyed)return false;
|
|
82
|
+
const operationId=
|
|
83
|
+
`${events.instanceId}:${type}:${(++eventOperationSequence).toString(36)}`;
|
|
84
|
+
const publication=events.dispatch(
|
|
85
|
+
type,
|
|
86
|
+
detail,
|
|
87
|
+
{operationId,publicDetail:{...publicDetail,reason:type}}
|
|
88
|
+
);
|
|
89
|
+
return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true});
|
|
90
|
+
}
|
|
75
91
|
|
|
76
92
|
function normalize(value){
|
|
77
93
|
let text=String(value||'').trim();
|
|
@@ -143,7 +159,7 @@ function showBlocked(url,decision){
|
|
|
143
159
|
root.querySelector('#blockedEvidence').textContent=[decision.ruleId&&`Rule ${decision.ruleId}`,decision.policyGeneration&&`Policy generation ${decision.policyGeneration}`,decision.decisionId&&`Decision ${decision.decisionId}`,decision.decidedAt].filter(Boolean).join(' · ');
|
|
144
160
|
status.textContent='Blocked by the Arcane global deny policy.';
|
|
145
161
|
blocked.focus();
|
|
146
|
-
|
|
162
|
+
emit('web-navigation-blocked',{url,decision},{blocked:true,ruleId:String(decision.ruleId||''),policyGeneration:decision.policyGeneration??null});
|
|
147
163
|
}
|
|
148
164
|
|
|
149
165
|
function showFrame(url){
|
|
@@ -151,17 +167,18 @@ function showFrame(url){
|
|
|
151
167
|
frame.hidden=false;
|
|
152
168
|
frame.src=url;
|
|
153
169
|
status.textContent='Loading…';
|
|
154
|
-
|
|
170
|
+
emit('web-navigate',{url},{intent:'embedded'});
|
|
155
171
|
}
|
|
156
172
|
|
|
157
173
|
async function navigate(value,{record=true}={}){
|
|
174
|
+
if(destroyed)return '';
|
|
158
175
|
const sequence=++navigationSequence;
|
|
159
176
|
try{
|
|
160
177
|
const url=normalize(value);
|
|
161
178
|
address.value=url;
|
|
162
179
|
status.textContent='Checking the global deny policy…';
|
|
163
180
|
const decision=await evaluate(url,'embedded');
|
|
164
|
-
if(sequence!==navigationSequence)return'';
|
|
181
|
+
if(destroyed||sequence!==navigationSequence)return'';
|
|
165
182
|
recordHistory(url,record);
|
|
166
183
|
updateButtons();
|
|
167
184
|
if(decision.blocked){
|
|
@@ -177,26 +194,29 @@ async function navigate(value,{record=true}={}){
|
|
|
177
194
|
}
|
|
178
195
|
|
|
179
196
|
async function openExternal(){
|
|
197
|
+
if(destroyed)return false;
|
|
180
198
|
const sequence=++navigationSequence;
|
|
181
199
|
try{
|
|
182
200
|
const url=normalize(currentUrl()||address.value||homeUrl);
|
|
183
201
|
status.textContent='Checking the global deny policy…';
|
|
184
202
|
const decision=await evaluate(url,'external');
|
|
185
|
-
if(sequence!==navigationSequence)return;
|
|
203
|
+
if(destroyed||sequence!==navigationSequence)return false;
|
|
186
204
|
if(decision.blocked){
|
|
187
205
|
showBlocked(url,decision);
|
|
188
206
|
return;
|
|
189
207
|
}
|
|
190
208
|
status.textContent='Handing the allowed address to the system browser…';
|
|
191
|
-
|
|
209
|
+
emit('web-open-external',{url},{intent:'external'});
|
|
210
|
+
return true;
|
|
192
211
|
}catch(error){
|
|
193
|
-
if(sequence===navigationSequence)status.textContent=error.message;
|
|
212
|
+
if(!destroyed&&sequence===navigationSequence)status.textContent=error.message;
|
|
213
|
+
return false;
|
|
194
214
|
}
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
function ownNavigation(operation){
|
|
198
218
|
operation.catch(function reportUnexpectedNavigationFailure(error){
|
|
199
|
-
status.textContent=error.message;
|
|
219
|
+
if(!destroyed)status.textContent=error.message;
|
|
200
220
|
});
|
|
201
221
|
}
|
|
202
222
|
|
|
@@ -208,6 +228,7 @@ function move(amount){
|
|
|
208
228
|
}
|
|
209
229
|
|
|
210
230
|
function configure(options){
|
|
231
|
+
if(destroyed)return false;
|
|
211
232
|
if(options?.homeUrl)homeUrl=normalize(options.homeUrl);
|
|
212
233
|
navigationGuard=typeof options?.navigationGuard==='function'?options.navigationGuard:null;
|
|
213
234
|
if(options?.initialUrl)ownNavigation(navigate(options.initialUrl));
|
|
@@ -223,7 +244,26 @@ function handleBackClick(){move(-1);}
|
|
|
223
244
|
function handleForwardClick(){move(1);}
|
|
224
245
|
function handleFrameLoad(){if(blocked.hidden)status.textContent='Loaded. If the page is blank, its site does not permit embedded viewing.';}
|
|
225
246
|
|
|
247
|
+
function destroy(){
|
|
248
|
+
if(destroyed)return false;
|
|
249
|
+
destroyed=true;
|
|
250
|
+
navigationSequence+=1;
|
|
251
|
+
navigationGuard=null;
|
|
252
|
+
host.ready=false;
|
|
253
|
+
root.querySelector('#addressForm').removeEventListener('submit',handleAddressSubmit);
|
|
254
|
+
root.querySelector('#home').removeEventListener('click',handleHomeClick);
|
|
255
|
+
root.querySelector('#reload').removeEventListener('click',handleReloadClick);
|
|
256
|
+
root.querySelector('#external').removeEventListener('click',handleExternalClick);
|
|
257
|
+
back.removeEventListener('click',handleBackClick);
|
|
258
|
+
forward.removeEventListener('click',handleForwardClick);
|
|
259
|
+
frame.removeEventListener('load',handleFrameLoad);
|
|
260
|
+
frame.removeAttribute('src');
|
|
261
|
+
events.dispose();
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
226
265
|
host.configure=configure;
|
|
266
|
+
host.destroy=destroy;
|
|
227
267
|
host.navigate=navigate;
|
|
228
268
|
host.currentUrl=currentUrl;
|
|
229
269
|
root.querySelector('#addressForm').addEventListener('submit',handleAddressSubmit);
|
|
@@ -235,5 +275,5 @@ forward.addEventListener('click',handleForwardClick);
|
|
|
235
275
|
frame.addEventListener('load',handleFrameLoad);
|
|
236
276
|
updateButtons();
|
|
237
277
|
host.ready=true;
|
|
238
|
-
|
|
278
|
+
emit('web-navigator-ready',{}, {ready:true});
|
|
239
279
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Is from '../../node_modules/strong-type/index.js';
|
|
2
2
|
import '../modules/DBOPFS.js';
|
|
3
|
-
import '../modules/AI.js
|
|
3
|
+
import '../modules/AI.js';
|
|
4
4
|
import {hasUserEntry} from '../modules/ChatRecords.js';
|
|
5
5
|
import {normalizeMemoryContent} from '../modules/MemoryRecords.js';
|
|
6
6
|
|