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.
@@ -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.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.2"
85
+ "const": "0.2.3"
86
86
  },
87
87
  "source": {
88
88
  "type": "object",