arcane-os 0.2.2 → 0.2.3
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 +12 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +1 -1
- package/docs/architecture.md +2 -2
- package/docs/reference/README.md +24 -24
- package/docs/reference/ai/browser-speech.md +1 -1
- package/docs/reference/ai/browser-wasm.md +1 -1
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +1 -1
- package/docs/reference/cli.md +3 -3
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/inventory/runtime-components.json +25 -10
- package/docs/reference/inventory/runtime-modules.json +8 -7
- package/docs/reference/protocols.md +27 -23
- package/docs/reference/runtime-components.md +70 -7
- package/docs/reference/runtime-modules.md +33 -11
- package/docs/reference/sdk-api.md +8 -8
- package/package.json +1 -1
- package/runtime/ARCANE_RUNTIME_RELEASE.json +9 -9
- package/runtime/arcane/components/speech.html +9 -253
- package/runtime/arcane/components/voice-transcription.html +437 -68
- package/runtime/arcane/modules/ComponentContracts.js +272 -0
- package/schemas/arcane-lock.schema.json +2 -2
|
@@ -327,6 +327,275 @@ function effectiveDashboardVisibility(definitions=[],visibility={}){
|
|
|
327
327
|
);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
function createSTTActivationController({
|
|
331
|
+
host,
|
|
332
|
+
button,
|
|
333
|
+
onChange,
|
|
334
|
+
EventClass=CustomEvent
|
|
335
|
+
}){
|
|
336
|
+
let role=null;
|
|
337
|
+
let requestPending=false;
|
|
338
|
+
let requestError='';
|
|
339
|
+
let requestGeneration=0;
|
|
340
|
+
let destroyed=false;
|
|
341
|
+
|
|
342
|
+
function visibleError(error,fallback){
|
|
343
|
+
const message=typeof error?.message==='string'
|
|
344
|
+
?error.message.trim()
|
|
345
|
+
:'';
|
|
346
|
+
return (message||fallback).slice(0,240);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function cancellation(error){
|
|
350
|
+
return error?.name==='AbortError'
|
|
351
|
+
||[
|
|
352
|
+
'ARCANE_AI_REQUEST_ABORTED',
|
|
353
|
+
'ARCANE_AI_OPERATION_SUPERSEDED'
|
|
354
|
+
].includes(error?.code);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function selected(){
|
|
358
|
+
return typeof role?.providerId==='string'
|
|
359
|
+
&&role.providerId.length>0
|
|
360
|
+
&&typeof role?.modelId==='string'
|
|
361
|
+
&&role.modelId.length>0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function action(){
|
|
365
|
+
if(!selected()){
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
if(role.state==='loading'){
|
|
369
|
+
return 'unload';
|
|
370
|
+
}
|
|
371
|
+
if(!role.busy&&['unloaded','error'].includes(role.state)){
|
|
372
|
+
return 'load';
|
|
373
|
+
}
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function label(){
|
|
378
|
+
if(requestPending){
|
|
379
|
+
return 'Requesting…';
|
|
380
|
+
}
|
|
381
|
+
if(role?.state==='loading'){
|
|
382
|
+
return 'Cancel loading';
|
|
383
|
+
}
|
|
384
|
+
if(role?.state==='unloading'){
|
|
385
|
+
return 'Canceling…';
|
|
386
|
+
}
|
|
387
|
+
if(role?.state==='error'){
|
|
388
|
+
return 'Try again';
|
|
389
|
+
}
|
|
390
|
+
return 'Start transcription';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function title(){
|
|
394
|
+
if(requestPending){
|
|
395
|
+
return 'Requesting transcription activation.';
|
|
396
|
+
}
|
|
397
|
+
if(role?.state==='loading'){
|
|
398
|
+
return 'Cancel loading the selected transcription service.';
|
|
399
|
+
}
|
|
400
|
+
if(role?.state==='unloading'){
|
|
401
|
+
return 'The selected transcription service is being released.';
|
|
402
|
+
}
|
|
403
|
+
if(role?.state==='error'){
|
|
404
|
+
return requestError||visibleError(
|
|
405
|
+
role.error,
|
|
406
|
+
'Retry loading the selected transcription service.'
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return 'Start the selected transcription service.';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function formatProgress(progress,fallback){
|
|
413
|
+
if(!progress){
|
|
414
|
+
return fallback;
|
|
415
|
+
}
|
|
416
|
+
const amount=progress.total===null
|
|
417
|
+
?`${progress.completed} ${progress.unit}`
|
|
418
|
+
:`${progress.completed} of ${progress.total} ${progress.unit}`;
|
|
419
|
+
const heartbeat=progress.heartbeat?', active heartbeat':'';
|
|
420
|
+
return `${progress.phase}, ${amount}${heartbeat}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function status(){
|
|
424
|
+
if(requestError){
|
|
425
|
+
return `Transcription activation error: ${requestError}.`;
|
|
426
|
+
}
|
|
427
|
+
if(requestPending){
|
|
428
|
+
return 'Transcription activation requested.';
|
|
429
|
+
}
|
|
430
|
+
if(role?.state==='ready'){
|
|
431
|
+
return role.busy?'Transcription busy.':'Transcription ready.';
|
|
432
|
+
}
|
|
433
|
+
if(role?.state==='loading'){
|
|
434
|
+
return `Transcription ${formatProgress(role.progress,'loading')}; Cancel is available.`;
|
|
435
|
+
}
|
|
436
|
+
if(role?.state==='unloading'){
|
|
437
|
+
return `Transcription ${formatProgress(role.progress,'releasing')}.`;
|
|
438
|
+
}
|
|
439
|
+
if(role?.state==='error'){
|
|
440
|
+
return `Transcription error: ${visibleError(role.error,'Unknown error')}.`;
|
|
441
|
+
}
|
|
442
|
+
if(role?.state==='disposed'){
|
|
443
|
+
return 'Transcription disposed.';
|
|
444
|
+
}
|
|
445
|
+
if(role?.state==='unloaded'&&selected()){
|
|
446
|
+
return 'Transcription selected and waiting to load.';
|
|
447
|
+
}
|
|
448
|
+
return 'Transcription unavailable.';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function visible(){
|
|
452
|
+
return selected()
|
|
453
|
+
&&role.state!=='ready'
|
|
454
|
+
&&['unloaded','loading','unloading','error'].includes(role.state);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async function request(nextAction){
|
|
458
|
+
if(destroyed||requestPending||action()!==nextAction){
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
const generation=++requestGeneration;
|
|
462
|
+
requestError='';
|
|
463
|
+
const intent=Object.freeze(
|
|
464
|
+
{
|
|
465
|
+
role:'stt',
|
|
466
|
+
action:nextAction,
|
|
467
|
+
reason:'user'
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
const activationRequest=Object.freeze({intent,state:role});
|
|
471
|
+
const activationEvent=new EventClass(
|
|
472
|
+
'speech-stt-activation-request',
|
|
473
|
+
{
|
|
474
|
+
bubbles:true,
|
|
475
|
+
composed:true,
|
|
476
|
+
cancelable:true,
|
|
477
|
+
detail:activationRequest
|
|
478
|
+
}
|
|
479
|
+
);
|
|
480
|
+
requestPending=true;
|
|
481
|
+
const accepted=host.dispatchEvent(activationEvent);
|
|
482
|
+
if(!accepted
|
|
483
|
+
||destroyed
|
|
484
|
+
||generation!==requestGeneration
|
|
485
|
+
||action()!==nextAction){
|
|
486
|
+
if(!destroyed&&generation===requestGeneration){
|
|
487
|
+
requestPending=false;
|
|
488
|
+
onChange();
|
|
489
|
+
}
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
try{
|
|
493
|
+
onChange();
|
|
494
|
+
if(destroyed
|
|
495
|
+
||generation!==requestGeneration
|
|
496
|
+
||action()!==nextAction){
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
await host.requestSTTActivation(intent);
|
|
500
|
+
if(destroyed||generation!==requestGeneration){
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
return true;
|
|
504
|
+
}catch(error){
|
|
505
|
+
if(destroyed||generation!==requestGeneration){
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
if(nextAction==='load'
|
|
509
|
+
&&cancellation(error)
|
|
510
|
+
&&['unloaded','unloading'].includes(role?.state)){
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
const message=visibleError(
|
|
514
|
+
error,
|
|
515
|
+
`The transcription ${nextAction} request failed.`
|
|
516
|
+
);
|
|
517
|
+
requestError=message;
|
|
518
|
+
host.dispatchEvent(
|
|
519
|
+
new EventClass(
|
|
520
|
+
'speech-stt-activation-error',
|
|
521
|
+
{
|
|
522
|
+
bubbles:true,
|
|
523
|
+
composed:true,
|
|
524
|
+
detail:Object.freeze(
|
|
525
|
+
{
|
|
526
|
+
request:activationRequest,
|
|
527
|
+
error,
|
|
528
|
+
message
|
|
529
|
+
}
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
return false;
|
|
535
|
+
}finally{
|
|
536
|
+
if(!destroyed&&generation===requestGeneration){
|
|
537
|
+
requestPending=false;
|
|
538
|
+
try{
|
|
539
|
+
onChange();
|
|
540
|
+
}catch(error){
|
|
541
|
+
console.error('Unable to render STT activation state:',error);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function activateSelectedSTT(){
|
|
548
|
+
const nextAction=action();
|
|
549
|
+
if(nextAction){
|
|
550
|
+
void request(nextAction);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function synchronize(nextRole){
|
|
555
|
+
if(destroyed){
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if(role?.state!==nextRole.state
|
|
559
|
+
||role?.providerId!==nextRole.providerId
|
|
560
|
+
||role?.modelId!==nextRole.modelId
|
|
561
|
+
||role?.loaded!==nextRole.loaded
|
|
562
|
+
||role?.busy!==nextRole.busy
|
|
563
|
+
||role?.operationId!==nextRole.operationId){
|
|
564
|
+
requestGeneration+=1;
|
|
565
|
+
requestError='';
|
|
566
|
+
requestPending=false;
|
|
567
|
+
}
|
|
568
|
+
role=nextRole;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function destroy(){
|
|
572
|
+
if(destroyed){
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
destroyed=true;
|
|
576
|
+
requestGeneration+=1;
|
|
577
|
+
requestPending=false;
|
|
578
|
+
button.removeEventListener('click',activateSelectedSTT);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
button.addEventListener('click',activateSelectedSTT);
|
|
582
|
+
return Object.freeze(
|
|
583
|
+
{
|
|
584
|
+
get action(){return action();},
|
|
585
|
+
get error(){return requestError;},
|
|
586
|
+
get label(){return label();},
|
|
587
|
+
get pending(){return requestPending;},
|
|
588
|
+
get selected(){return selected();},
|
|
589
|
+
get status(){return status();},
|
|
590
|
+
get title(){return title();},
|
|
591
|
+
get visible(){return visible();},
|
|
592
|
+
request,
|
|
593
|
+
synchronize,
|
|
594
|
+
destroy
|
|
595
|
+
}
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
330
599
|
const VOICE_LABELS=Object.freeze({
|
|
331
600
|
complete:'Complete Transcription',
|
|
332
601
|
description:'Record one or more segments. Each segment is transcribed after you press Stop.',
|
|
@@ -337,6 +606,7 @@ const VOICE_LABELS=Object.freeze({
|
|
|
337
606
|
});
|
|
338
607
|
|
|
339
608
|
const VOICE_MESSAGES=Object.freeze({
|
|
609
|
+
cancelled:'Transcription canceled.',
|
|
340
610
|
complete:'Complete.',
|
|
341
611
|
completeError:'Unable to complete this transcription.',
|
|
342
612
|
completing:'Completing transcription...',
|
|
@@ -354,6 +624,7 @@ const VOICE_MESSAGES=Object.freeze({
|
|
|
354
624
|
transcribed:'Transcription added. Record another segment or complete.',
|
|
355
625
|
transcribeError:'Unable to transcribe this recording.',
|
|
356
626
|
transcribing:'Transcribing this segment...',
|
|
627
|
+
transcriptReplaced:'Transcript replaced.',
|
|
357
628
|
unsupported:'Audio recording is not supported by this browser.'
|
|
358
629
|
});
|
|
359
630
|
|
|
@@ -574,6 +845,7 @@ export {
|
|
|
574
845
|
VOICE_MESSAGES,
|
|
575
846
|
appendTranscription,
|
|
576
847
|
applyMarkdownFormat,
|
|
848
|
+
createSTTActivationController,
|
|
577
849
|
effectiveDashboardVisibility,
|
|
578
850
|
normalizeChartOptions,
|
|
579
851
|
normalizeChartRows,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"const": "arcane-os"
|
|
29
29
|
},
|
|
30
30
|
"version": {
|
|
31
|
-
"const": "0.2.
|
|
31
|
+
"const": "0.2.3"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
},
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"const": "arcane-sdk-browser-runtime-v1"
|
|
83
83
|
},
|
|
84
84
|
"sdkVersion": {
|
|
85
|
-
"const": "0.2.
|
|
85
|
+
"const": "0.2.3"
|
|
86
86
|
},
|
|
87
87
|
"source": {
|
|
88
88
|
"type": "object",
|