arcane-os 0.2.2 → 0.3.0
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 +29 -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 +3230 -397
- package/browser-runtime/ai/browser-speech-providers.mjs +1141 -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 +247 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -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 +27 -27
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1162 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +29 -6
- package/docs/reference/cli.md +117 -9
- 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 +108 -44
- package/docs/reference/inventory/runtime-modules.json +131 -53
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +157 -43
- package/docs/reference/runtime-components.md +258 -83
- package/docs/reference/runtime-modules.md +613 -77
- package/docs/reference/sdk-api.md +1014 -25
- 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 +462 -384
- 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 +639 -98
- 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 +2109 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +720 -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 +470 -0
- 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 +112 -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.mjs +1 -1
|
@@ -353,6 +353,9 @@
|
|
|
353
353
|
|
|
354
354
|
const host=this;
|
|
355
355
|
const {default:waitForComponent}=await import('../modules/WaitForComponent.js');
|
|
356
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
357
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.file-manager',eventTypes:['file-manager-action','file-manager-select','file-manager-open','file-manager-ready']});
|
|
358
|
+
const lifecycleController=new AbortController();
|
|
356
359
|
|
|
357
360
|
const fileManager=host.shadowRoot.querySelector('.file-manager');
|
|
358
361
|
const componentStyles=host.shadowRoot.querySelector('style');
|
|
@@ -377,6 +380,16 @@
|
|
|
377
380
|
let selectedPath='';
|
|
378
381
|
let treeItems=new Map();
|
|
379
382
|
let treeLoadSequence=0;
|
|
383
|
+
let gridLoadSequence=0;
|
|
384
|
+
let providerGeneration=0;
|
|
385
|
+
let filterGeneration=0;
|
|
386
|
+
let treeOpenGeneration=0;
|
|
387
|
+
let directoryOpenGeneration=0;
|
|
388
|
+
let fileOpenGeneration=0;
|
|
389
|
+
let deleteGeneration=0;
|
|
390
|
+
let selectionGeneration=0;
|
|
391
|
+
let operationSequence=0;
|
|
392
|
+
let destroyed=false;
|
|
380
393
|
|
|
381
394
|
host.ready=false;
|
|
382
395
|
host.loadAll=loadAll;
|
|
@@ -384,6 +397,7 @@
|
|
|
384
397
|
host.setFilter=setFilter;
|
|
385
398
|
host.select=select;
|
|
386
399
|
host.clearSelection=clearSelection;
|
|
400
|
+
host.destroy=destroy;
|
|
387
401
|
|
|
388
402
|
Object.defineProperty(
|
|
389
403
|
host,
|
|
@@ -440,7 +454,7 @@
|
|
|
440
454
|
window.addEventListener(
|
|
441
455
|
'dbopfs-ready',
|
|
442
456
|
init,
|
|
443
|
-
{once:true}
|
|
457
|
+
{once:true,signal:lifecycleController.signal}
|
|
444
458
|
);
|
|
445
459
|
|
|
446
460
|
if(window.dbopfs?.ready){
|
|
@@ -450,8 +464,13 @@
|
|
|
450
464
|
fileUpload.addEventListener(
|
|
451
465
|
'change',
|
|
452
466
|
()=>uploadFiles().catch(
|
|
453
|
-
error=>
|
|
454
|
-
|
|
467
|
+
error=>{
|
|
468
|
+
if(!destroyed){
|
|
469
|
+
console.error('Unable to upload files:',error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
),
|
|
473
|
+
{signal:lifecycleController.signal}
|
|
455
474
|
);
|
|
456
475
|
|
|
457
476
|
fileManager.addEventListener(
|
|
@@ -465,7 +484,11 @@
|
|
|
465
484
|
|
|
466
485
|
if(layout==='grid'){
|
|
467
486
|
openDirectory(directoryElement.dataset.directory).catch(
|
|
468
|
-
error=>
|
|
487
|
+
error=>{
|
|
488
|
+
if(!destroyed){
|
|
489
|
+
console.error('Unable to open directory:',error);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
469
492
|
);
|
|
470
493
|
return;
|
|
471
494
|
}
|
|
@@ -474,26 +497,53 @@
|
|
|
474
497
|
return;
|
|
475
498
|
}
|
|
476
499
|
|
|
477
|
-
}
|
|
500
|
+
},
|
|
501
|
+
{signal:lifecycleController.signal}
|
|
478
502
|
);
|
|
479
503
|
|
|
480
|
-
fileManager.addEventListener(
|
|
504
|
+
fileManager.addEventListener(
|
|
505
|
+
'keydown',
|
|
506
|
+
onTreeKeydown,
|
|
507
|
+
{signal:lifecycleController.signal}
|
|
508
|
+
);
|
|
481
509
|
|
|
482
|
-
function
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
name,
|
|
486
|
-
{
|
|
487
|
-
bubbles:true,
|
|
488
|
-
cancelable:cancelable,
|
|
489
|
-
composed:true,
|
|
490
|
-
detail:detail
|
|
491
|
-
}
|
|
492
|
-
)
|
|
493
|
-
);
|
|
510
|
+
function nextOperationId(action='operation'){
|
|
511
|
+
operationSequence+=1;
|
|
512
|
+
return `${events.descriptor.instanceId}:${action}:${operationSequence}`;
|
|
494
513
|
}
|
|
495
514
|
|
|
496
|
-
function
|
|
515
|
+
function publicEventDetail(name,detail={}){
|
|
516
|
+
if(name==='file-manager-action'){
|
|
517
|
+
const publicDetail={action:String(detail.action||''),kind:String(detail.entry?.kind||''),hasEntry:Boolean(detail.path)};
|
|
518
|
+
if(detail.code){
|
|
519
|
+
publicDetail.code=String(detail.code);
|
|
520
|
+
}
|
|
521
|
+
if(detail.reason){
|
|
522
|
+
publicDetail.reason=String(detail.reason);
|
|
523
|
+
}
|
|
524
|
+
return publicDetail;
|
|
525
|
+
}
|
|
526
|
+
if(name==='file-manager-open'){
|
|
527
|
+
return {trigger:String(detail.trigger||''),kind:String(detail.entry?.kind||''),hasEntry:Boolean(detail.path)};
|
|
528
|
+
}
|
|
529
|
+
if(name==='file-manager-select'){
|
|
530
|
+
return {selected:Boolean(detail.path),kind:String(detail.entry?.kind||'')};
|
|
531
|
+
}
|
|
532
|
+
return {};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function emit(name,detail={},cancelable=false,operationId=null){
|
|
536
|
+
if(destroyed){
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
const publication=events.dispatch(name,detail,{publicDetail:publicEventDetail(name,detail),cancelable,operationId});
|
|
540
|
+
if(cancelable&&!publication.accepted){
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,cancelable,composed:true});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function emitAction(action,entry={},detail={},cancelable=false,operationId=null){
|
|
497
547
|
return emit(
|
|
498
548
|
'file-manager-action',
|
|
499
549
|
{
|
|
@@ -502,15 +552,20 @@
|
|
|
502
552
|
entry:entry,
|
|
503
553
|
...detail
|
|
504
554
|
},
|
|
505
|
-
cancelable
|
|
555
|
+
cancelable,
|
|
556
|
+
operationId
|
|
506
557
|
);
|
|
507
558
|
}
|
|
508
559
|
|
|
509
560
|
async function setProvider(nextProvider=null){
|
|
561
|
+
if(destroyed){
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
510
564
|
if(nextProvider!==null&&typeof nextProvider?.list!=='function'){
|
|
511
565
|
throw new TypeError('File manager providers must expose list(path).');
|
|
512
566
|
}
|
|
513
567
|
|
|
568
|
+
const generation=++providerGeneration;
|
|
514
569
|
provider=nextProvider;
|
|
515
570
|
|
|
516
571
|
if(layout==='tree'){
|
|
@@ -521,21 +576,25 @@
|
|
|
521
576
|
}
|
|
522
577
|
}
|
|
523
578
|
|
|
524
|
-
return provider;
|
|
579
|
+
return destroyed||generation!==providerGeneration?null:provider;
|
|
525
580
|
}
|
|
526
581
|
|
|
527
582
|
async function setFilter(nextFilter=null){
|
|
583
|
+
if(destroyed){
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
528
586
|
if(nextFilter!==null&&typeof nextFilter!=='function'){
|
|
529
587
|
throw new TypeError('File manager filters must be functions or null.');
|
|
530
588
|
}
|
|
531
589
|
|
|
590
|
+
const generation=++filterGeneration;
|
|
532
591
|
filter=nextFilter;
|
|
533
592
|
|
|
534
593
|
if(initialized){
|
|
535
594
|
await loadAll();
|
|
536
595
|
}
|
|
537
596
|
|
|
538
|
-
return filter;
|
|
597
|
+
return destroyed||generation!==filterGeneration?null:filter;
|
|
539
598
|
}
|
|
540
599
|
|
|
541
600
|
function normalizeProviderPath(path=''){
|
|
@@ -709,13 +768,17 @@
|
|
|
709
768
|
}
|
|
710
769
|
|
|
711
770
|
if(restoredPath){
|
|
712
|
-
const
|
|
771
|
+
const restoration=select(
|
|
713
772
|
restoredPath,
|
|
714
773
|
{emit:false,focus:false}
|
|
715
774
|
);
|
|
775
|
+
const restorationGeneration=selectionGeneration;
|
|
776
|
+
const restored=await restoration;
|
|
716
777
|
|
|
717
|
-
if(!restored
|
|
718
|
-
|
|
778
|
+
if(!restored
|
|
779
|
+
&&selectionGeneration===restorationGeneration
|
|
780
|
+
&&selectedPath===restoredPath){
|
|
781
|
+
clearSelection({emit:false});
|
|
719
782
|
}
|
|
720
783
|
}
|
|
721
784
|
|
|
@@ -729,7 +792,17 @@
|
|
|
729
792
|
message.className='tree-error';
|
|
730
793
|
message.innerText='Unable to list files.';
|
|
731
794
|
fileManager.append(message);
|
|
732
|
-
emitAction(
|
|
795
|
+
emitAction(
|
|
796
|
+
'error',
|
|
797
|
+
{},
|
|
798
|
+
{
|
|
799
|
+
error,
|
|
800
|
+
code:String(error?.code||'ARCANE_FILE_MANAGER_ROOT_LIST_FAILED'),
|
|
801
|
+
reason:'file-root-list-failed'
|
|
802
|
+
},
|
|
803
|
+
false,
|
|
804
|
+
nextOperationId('root-list-error')
|
|
805
|
+
);
|
|
733
806
|
throw error;
|
|
734
807
|
}finally{
|
|
735
808
|
if(sequence===treeLoadSequence){
|
|
@@ -800,20 +873,35 @@
|
|
|
800
873
|
item.dataset.fileCount='0';
|
|
801
874
|
}
|
|
802
875
|
|
|
803
|
-
row.addEventListener(
|
|
804
|
-
|
|
876
|
+
row.addEventListener(
|
|
877
|
+
'click',
|
|
878
|
+
async()=>{
|
|
879
|
+
const selected=await select(entry.path,{entry:entry,focus:true});
|
|
880
|
+
if(!selected||destroyed){
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
805
883
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
884
|
+
if(entry.kind==='directory'){
|
|
885
|
+
await toggleTreeItem(item);
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
{signal:lifecycleController.signal}
|
|
889
|
+
);
|
|
890
|
+
row.addEventListener(
|
|
891
|
+
'dblclick',
|
|
892
|
+
async event=>{
|
|
893
|
+
if(entry.kind==='file'){
|
|
894
|
+
event.preventDefault();
|
|
895
|
+
await openTreeItem(item,'double-click');
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
{signal:lifecycleController.signal}
|
|
899
|
+
);
|
|
900
|
+
item.addEventListener(
|
|
901
|
+
'focus',
|
|
902
|
+
()=>setRovingTreeItem(item,false),
|
|
903
|
+
{signal:lifecycleController.signal}
|
|
904
|
+
);
|
|
817
905
|
|
|
818
906
|
return item;
|
|
819
907
|
}
|
|
@@ -857,7 +945,7 @@
|
|
|
857
945
|
}
|
|
858
946
|
|
|
859
947
|
async function expandTreeItem(item,{emitEvent=true}={}){
|
|
860
|
-
if(
|
|
948
|
+
if(destroyed||!item||item.dataset.kind!=='directory'){
|
|
861
949
|
return false;
|
|
862
950
|
}
|
|
863
951
|
|
|
@@ -896,16 +984,33 @@
|
|
|
896
984
|
item.dataset.loaded='true';
|
|
897
985
|
updateTreeCounts(item);
|
|
898
986
|
}catch(error){
|
|
987
|
+
if(destroyed||sequence!==treeLoadSequence||!fileManager.contains(item)){
|
|
988
|
+
return false;
|
|
989
|
+
}
|
|
899
990
|
group.replaceChildren();
|
|
900
991
|
const message=document.createElement('li');
|
|
901
992
|
message.className='tree-error';
|
|
902
993
|
message.setAttribute('role','none');
|
|
903
994
|
message.innerText='Unable to list this folder.';
|
|
904
995
|
group.append(message);
|
|
905
|
-
emitAction(
|
|
996
|
+
emitAction(
|
|
997
|
+
'error',
|
|
998
|
+
entry,
|
|
999
|
+
{
|
|
1000
|
+
error,
|
|
1001
|
+
code:String(error?.code||'ARCANE_FILE_MANAGER_DIRECTORY_LIST_FAILED'),
|
|
1002
|
+
reason:'directory-list-failed'
|
|
1003
|
+
},
|
|
1004
|
+
false,
|
|
1005
|
+
nextOperationId('directory-list-error')
|
|
1006
|
+
);
|
|
906
1007
|
return false;
|
|
907
1008
|
}finally{
|
|
908
|
-
|
|
1009
|
+
if(!destroyed
|
|
1010
|
+
&&sequence===treeLoadSequence
|
|
1011
|
+
&&fileManager.contains(item)){
|
|
1012
|
+
item.removeAttribute('aria-busy');
|
|
1013
|
+
}
|
|
909
1014
|
}
|
|
910
1015
|
}
|
|
911
1016
|
|
|
@@ -920,6 +1025,9 @@
|
|
|
920
1025
|
}
|
|
921
1026
|
|
|
922
1027
|
function collapseTreeItem(item,{emitEvent=true}={}){
|
|
1028
|
+
if(destroyed){
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
923
1031
|
const group=item?.querySelector(':scope>.tree-group');
|
|
924
1032
|
|
|
925
1033
|
if(!group){
|
|
@@ -937,6 +1045,9 @@
|
|
|
937
1045
|
}
|
|
938
1046
|
|
|
939
1047
|
async function toggleTreeItem(item){
|
|
1048
|
+
if(destroyed){
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
940
1051
|
if(item?.getAttribute('aria-expanded')==='true'){
|
|
941
1052
|
return collapseTreeItem(item);
|
|
942
1053
|
}
|
|
@@ -994,6 +1105,10 @@
|
|
|
994
1105
|
}
|
|
995
1106
|
|
|
996
1107
|
async function select(path='',options={}){
|
|
1108
|
+
if(destroyed){
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
const generation=++selectionGeneration;
|
|
997
1112
|
const normalizedPath=normalizeProviderPath(path);
|
|
998
1113
|
|
|
999
1114
|
if(!normalizedPath){
|
|
@@ -1006,6 +1121,10 @@
|
|
|
1006
1121
|
if(layout==='tree'){
|
|
1007
1122
|
item=await revealTreePath(normalizedPath);
|
|
1008
1123
|
|
|
1124
|
+
if(destroyed||generation!==selectionGeneration){
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1009
1128
|
if(!item){
|
|
1010
1129
|
return false;
|
|
1011
1130
|
}
|
|
@@ -1040,6 +1159,10 @@
|
|
|
1040
1159
|
}
|
|
1041
1160
|
|
|
1042
1161
|
function clearSelection(options={}){
|
|
1162
|
+
if(destroyed){
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
selectionGeneration+=1;
|
|
1043
1166
|
const changed=Boolean(selectedPath);
|
|
1044
1167
|
selectedPath='';
|
|
1045
1168
|
|
|
@@ -1061,16 +1184,24 @@
|
|
|
1061
1184
|
}
|
|
1062
1185
|
|
|
1063
1186
|
async function openTreeItem(item,trigger='open'){
|
|
1187
|
+
if(destroyed){
|
|
1188
|
+
return false;
|
|
1189
|
+
}
|
|
1190
|
+
const generation=++treeOpenGeneration;
|
|
1064
1191
|
const entry=item?._fileManagerEntry;
|
|
1065
1192
|
|
|
1066
1193
|
if(!entry||entry.kind!=='file'){
|
|
1067
1194
|
return false;
|
|
1068
1195
|
}
|
|
1069
1196
|
|
|
1070
|
-
await select(entry.path,{entry:entry,focus:true});
|
|
1197
|
+
const selected=await select(entry.path,{entry:entry,focus:true});
|
|
1198
|
+
if(!selected||destroyed||generation!==treeOpenGeneration){
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1071
1201
|
const detail={path:entry.path,entry:entry,trigger:trigger};
|
|
1072
|
-
const
|
|
1073
|
-
const
|
|
1202
|
+
const operationId=nextOperationId('open');
|
|
1203
|
+
const openAllowed=emit('file-manager-open',detail,true,operationId);
|
|
1204
|
+
const actionAllowed=emitAction('open',entry,{trigger:trigger},true,operationId);
|
|
1074
1205
|
|
|
1075
1206
|
if(!openAllowed||!actionAllowed||openMode==='event'){
|
|
1076
1207
|
return true;
|
|
@@ -1079,9 +1210,24 @@
|
|
|
1079
1210
|
if(typeof activeTreeProvider().open==='function'){
|
|
1080
1211
|
try{
|
|
1081
1212
|
await activeTreeProvider().open(entry);
|
|
1213
|
+
if(destroyed||generation!==treeOpenGeneration){
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1082
1216
|
}catch(error){
|
|
1083
|
-
|
|
1084
|
-
|
|
1217
|
+
if(!destroyed&&generation===treeOpenGeneration){
|
|
1218
|
+
emitAction(
|
|
1219
|
+
'error',
|
|
1220
|
+
entry,
|
|
1221
|
+
{
|
|
1222
|
+
error,
|
|
1223
|
+
code:String(error?.code||'ARCANE_FILE_MANAGER_ENTRY_OPEN_FAILED'),
|
|
1224
|
+
reason:'file-open-failed'
|
|
1225
|
+
},
|
|
1226
|
+
false,
|
|
1227
|
+
nextOperationId('open-error')
|
|
1228
|
+
);
|
|
1229
|
+
console.error('Unable to open file:',error);
|
|
1230
|
+
}
|
|
1085
1231
|
return false;
|
|
1086
1232
|
}
|
|
1087
1233
|
}
|
|
@@ -1169,18 +1315,34 @@
|
|
|
1169
1315
|
}
|
|
1170
1316
|
|
|
1171
1317
|
async function openDirectory(directory=''){
|
|
1318
|
+
if(destroyed){
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
const generation=++directoryOpenGeneration;
|
|
1172
1322
|
const modal=await waitForComponent(
|
|
1173
1323
|
directoryModal,
|
|
1174
1324
|
{
|
|
1175
1325
|
methods:['populate','open'],
|
|
1176
1326
|
property:'ready',
|
|
1177
|
-
event:'modal-ready'
|
|
1178
|
-
|
|
1327
|
+
event:'modal-ready',
|
|
1328
|
+
signal:lifecycleController.signal
|
|
1329
|
+
},
|
|
1330
|
+
{signal:lifecycleController.signal}
|
|
1179
1331
|
);
|
|
1180
|
-
|
|
1332
|
+
if(destroyed||generation!==directoryOpenGeneration){
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
const keys=await dbopfs.getAllKeys(directory);
|
|
1336
|
+
if(destroyed||generation!==directoryOpenGeneration){
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
const files=keys
|
|
1181
1340
|
.filter(isVisibleFileName)
|
|
1182
1341
|
.sort();
|
|
1183
1342
|
const metadata=await getFilesMetadata(directory,files);
|
|
1343
|
+
if(destroyed||generation!==directoryOpenGeneration){
|
|
1344
|
+
return false;
|
|
1345
|
+
}
|
|
1184
1346
|
const content=document.createElement('section');
|
|
1185
1347
|
const styles=componentStyles.cloneNode(true);
|
|
1186
1348
|
const heading=document.createElement('h1');
|
|
@@ -1195,7 +1357,8 @@
|
|
|
1195
1357
|
uploadButton.innerText='Upload Files';
|
|
1196
1358
|
uploadButton.addEventListener(
|
|
1197
1359
|
'click',
|
|
1198
|
-
()=>selectFiles(directory)
|
|
1360
|
+
()=>selectFiles(directory),
|
|
1361
|
+
{signal:lifecycleController.signal}
|
|
1199
1362
|
);
|
|
1200
1363
|
list.className='directory-file-list';
|
|
1201
1364
|
list.dataset.layout='files';
|
|
@@ -1222,18 +1385,31 @@
|
|
|
1222
1385
|
}
|
|
1223
1386
|
|
|
1224
1387
|
await modal.populate(content,false);
|
|
1388
|
+
if(destroyed||generation!==directoryOpenGeneration){
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1225
1391
|
modal.open();
|
|
1392
|
+
return true;
|
|
1226
1393
|
}
|
|
1227
1394
|
|
|
1228
1395
|
async function openFile(directory='',fileName=''){
|
|
1396
|
+
if(destroyed){
|
|
1397
|
+
return false;
|
|
1398
|
+
}
|
|
1399
|
+
const generation=++fileOpenGeneration;
|
|
1229
1400
|
const modal=await waitForComponent(
|
|
1230
1401
|
fileModal,
|
|
1231
1402
|
{
|
|
1232
1403
|
methods:['populate','open'],
|
|
1233
1404
|
property:'ready',
|
|
1234
|
-
event:'modal-ready'
|
|
1235
|
-
|
|
1405
|
+
event:'modal-ready',
|
|
1406
|
+
signal:lifecycleController.signal
|
|
1407
|
+
},
|
|
1408
|
+
{signal:lifecycleController.signal}
|
|
1236
1409
|
);
|
|
1410
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1411
|
+
return false;
|
|
1412
|
+
}
|
|
1237
1413
|
const content=document.createElement('section');
|
|
1238
1414
|
const styles=componentStyles.cloneNode(true);
|
|
1239
1415
|
const heading=document.createElement('h1');
|
|
@@ -1254,8 +1430,13 @@
|
|
|
1254
1430
|
deleteButton.addEventListener(
|
|
1255
1431
|
'click',
|
|
1256
1432
|
()=>confirmDelete(directory,fileName,true).catch(
|
|
1257
|
-
error=>
|
|
1258
|
-
|
|
1433
|
+
error=>{
|
|
1434
|
+
if(!destroyed){
|
|
1435
|
+
console.error('Unable to delete file:',error);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
),
|
|
1439
|
+
{signal:lifecycleController.signal}
|
|
1259
1440
|
);
|
|
1260
1441
|
status.innerText='Opening file...';
|
|
1261
1442
|
actions.append(deleteButton);
|
|
@@ -1267,13 +1448,22 @@
|
|
|
1267
1448
|
status
|
|
1268
1449
|
);
|
|
1269
1450
|
|
|
1270
|
-
modal.populate(content,false);
|
|
1451
|
+
await modal.populate(content,false);
|
|
1452
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1271
1455
|
modal.open();
|
|
1272
1456
|
|
|
1273
1457
|
try{
|
|
1274
1458
|
const {default:FileEntity}=await fileEntityPromise;
|
|
1459
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1460
|
+
return false;
|
|
1461
|
+
}
|
|
1275
1462
|
const file=new FileEntity(fileName,directory);
|
|
1276
1463
|
const fileRef=await file.open();
|
|
1464
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1277
1467
|
|
|
1278
1468
|
status.remove();
|
|
1279
1469
|
|
|
@@ -1282,7 +1472,13 @@
|
|
|
1282
1472
|
|
|
1283
1473
|
if(!layout){
|
|
1284
1474
|
await dbopfs.delete(directory,fileName);
|
|
1475
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1285
1478
|
await loadAll();
|
|
1479
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1286
1482
|
empty.innerText='File was empty and has been deleted';
|
|
1287
1483
|
}else{
|
|
1288
1484
|
empty.innerText='File is empty';
|
|
@@ -1290,11 +1486,19 @@
|
|
|
1290
1486
|
|
|
1291
1487
|
content.append(empty);
|
|
1292
1488
|
}else{
|
|
1293
|
-
|
|
1489
|
+
const rendered=await renderFile(fileRef,{directory,fileName});
|
|
1490
|
+
if(destroyed||generation!==fileOpenGeneration){
|
|
1491
|
+
return false;
|
|
1492
|
+
}
|
|
1493
|
+
content.append(rendered);
|
|
1294
1494
|
}
|
|
1495
|
+
return true;
|
|
1295
1496
|
}catch(error){
|
|
1296
|
-
|
|
1297
|
-
|
|
1497
|
+
if(!destroyed&&generation===fileOpenGeneration){
|
|
1498
|
+
status.innerText='Unable to open this file.';
|
|
1499
|
+
console.error('Unable to open file:',error);
|
|
1500
|
+
}
|
|
1501
|
+
return false;
|
|
1298
1502
|
}
|
|
1299
1503
|
}
|
|
1300
1504
|
|
|
@@ -1479,8 +1683,9 @@
|
|
|
1479
1683
|
async()=>{
|
|
1480
1684
|
await select(entry.path,{entry:entry});
|
|
1481
1685
|
const detail={path:entry.path,entry:entry,trigger:'click'};
|
|
1482
|
-
const
|
|
1483
|
-
const
|
|
1686
|
+
const operationId=nextOperationId('open');
|
|
1687
|
+
const openAllowed=emit('file-manager-open',detail,true,operationId);
|
|
1688
|
+
const actionAllowed=emitAction('open',entry,{trigger:'click'},true,operationId);
|
|
1484
1689
|
|
|
1485
1690
|
if(!openAllowed||!actionAllowed||openMode==='event'){
|
|
1486
1691
|
return;
|
|
@@ -1489,19 +1694,21 @@
|
|
|
1489
1694
|
openFile(directory,fileName).catch(
|
|
1490
1695
|
error=>console.error('Unable to open file:',error)
|
|
1491
1696
|
);
|
|
1492
|
-
}
|
|
1697
|
+
},
|
|
1698
|
+
{signal:lifecycleController.signal}
|
|
1493
1699
|
);
|
|
1494
1700
|
deleteButton.addEventListener(
|
|
1495
1701
|
'click',
|
|
1496
1702
|
()=>{
|
|
1497
|
-
if(!emitAction('delete',entry,{},true)){
|
|
1703
|
+
if(!emitAction('delete',entry,{},true,nextOperationId('delete'))){
|
|
1498
1704
|
return;
|
|
1499
1705
|
}
|
|
1500
1706
|
|
|
1501
1707
|
confirmDelete(directory,fileName).catch(
|
|
1502
1708
|
error=>console.error('Unable to delete file:',error)
|
|
1503
1709
|
);
|
|
1504
|
-
}
|
|
1710
|
+
},
|
|
1711
|
+
{signal:lifecycleController.signal}
|
|
1505
1712
|
);
|
|
1506
1713
|
|
|
1507
1714
|
openButton.append(fileIcon,fileDetails);
|
|
@@ -1512,18 +1719,23 @@
|
|
|
1512
1719
|
}
|
|
1513
1720
|
|
|
1514
1721
|
async function confirmDelete(directory='',fileName='',closeFile=false){
|
|
1515
|
-
if(
|
|
1722
|
+
if(destroyed||!directory||!fileName){
|
|
1516
1723
|
return false;
|
|
1517
1724
|
}
|
|
1725
|
+
const generation=++deleteGeneration;
|
|
1518
1726
|
|
|
1519
1727
|
const modal=await waitForComponent(
|
|
1520
1728
|
deleteModal,
|
|
1521
1729
|
{
|
|
1522
1730
|
methods:['populate','open','close'],
|
|
1523
1731
|
property:'ready',
|
|
1524
|
-
event:'modal-ready'
|
|
1732
|
+
event:'modal-ready',
|
|
1733
|
+
signal:lifecycleController.signal
|
|
1525
1734
|
}
|
|
1526
1735
|
);
|
|
1736
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1527
1739
|
const content=document.createElement('section');
|
|
1528
1740
|
const heading=document.createElement('h1');
|
|
1529
1741
|
const message=document.createElement('p');
|
|
@@ -1539,7 +1751,8 @@
|
|
|
1539
1751
|
|
|
1540
1752
|
cancelButton.addEventListener(
|
|
1541
1753
|
'click',
|
|
1542
|
-
()=>modal.close(undefined,true)
|
|
1754
|
+
()=>modal.close(undefined,true),
|
|
1755
|
+
{signal:lifecycleController.signal}
|
|
1543
1756
|
);
|
|
1544
1757
|
deleteButton.addEventListener(
|
|
1545
1758
|
'click',
|
|
@@ -1549,24 +1762,39 @@
|
|
|
1549
1762
|
|
|
1550
1763
|
try{
|
|
1551
1764
|
await dbopfs.delete(directory,fileName);
|
|
1765
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1552
1768
|
await modal.close(undefined,true);
|
|
1769
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1553
1772
|
|
|
1554
1773
|
if(closeFile&&typeof fileModal.close==='function'){
|
|
1555
1774
|
await fileModal.close(undefined,true);
|
|
1775
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1556
1778
|
}
|
|
1557
1779
|
|
|
1558
1780
|
await loadAll();
|
|
1781
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1559
1784
|
|
|
1560
1785
|
if(layout==='grid'){
|
|
1561
1786
|
await openDirectory(directory);
|
|
1562
1787
|
}
|
|
1563
1788
|
}catch(error){
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1789
|
+
if(!destroyed&&generation===deleteGeneration){
|
|
1790
|
+
message.innerText=`Unable to delete ${fileName}.`;
|
|
1791
|
+
cancelButton.disabled=false;
|
|
1792
|
+
deleteButton.disabled=false;
|
|
1793
|
+
console.error('Unable to delete file:',error);
|
|
1794
|
+
}
|
|
1568
1795
|
}
|
|
1569
|
-
}
|
|
1796
|
+
},
|
|
1797
|
+
{signal:lifecycleController.signal}
|
|
1570
1798
|
);
|
|
1571
1799
|
|
|
1572
1800
|
content.append(
|
|
@@ -1577,6 +1805,9 @@
|
|
|
1577
1805
|
);
|
|
1578
1806
|
|
|
1579
1807
|
await modal.populate(content,false);
|
|
1808
|
+
if(destroyed||generation!==deleteGeneration){
|
|
1809
|
+
return false;
|
|
1810
|
+
}
|
|
1580
1811
|
modal.open();
|
|
1581
1812
|
|
|
1582
1813
|
return true;
|
|
@@ -1592,6 +1823,9 @@
|
|
|
1592
1823
|
}
|
|
1593
1824
|
|
|
1594
1825
|
async function uploadFiles(){
|
|
1826
|
+
if(destroyed){
|
|
1827
|
+
return false;
|
|
1828
|
+
}
|
|
1595
1829
|
const files=Array.from(fileUpload.files||[]);
|
|
1596
1830
|
const directory=uploadDirectory;
|
|
1597
1831
|
fileUpload.value='';
|
|
@@ -1601,6 +1835,9 @@
|
|
|
1601
1835
|
}
|
|
1602
1836
|
|
|
1603
1837
|
const {default:FileEntity}=await fileEntityPromise;
|
|
1838
|
+
if(destroyed){
|
|
1839
|
+
return false;
|
|
1840
|
+
}
|
|
1604
1841
|
|
|
1605
1842
|
const results=await Promise.allSettled(
|
|
1606
1843
|
files.map(
|
|
@@ -1612,8 +1849,14 @@
|
|
|
1612
1849
|
}
|
|
1613
1850
|
)
|
|
1614
1851
|
);
|
|
1852
|
+
if(destroyed){
|
|
1853
|
+
return false;
|
|
1854
|
+
}
|
|
1615
1855
|
|
|
1616
1856
|
await loadAll();
|
|
1857
|
+
if(destroyed){
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1617
1860
|
|
|
1618
1861
|
if(layout==='grid'){
|
|
1619
1862
|
await openDirectory(directory);
|
|
@@ -1626,10 +1869,11 @@
|
|
|
1626
1869
|
if(failed){
|
|
1627
1870
|
throw failed.reason;
|
|
1628
1871
|
}
|
|
1872
|
+
return true;
|
|
1629
1873
|
}
|
|
1630
1874
|
|
|
1631
1875
|
async function init(){
|
|
1632
|
-
if(initialized){
|
|
1876
|
+
if(initialized||destroyed){
|
|
1633
1877
|
return;
|
|
1634
1878
|
}
|
|
1635
1879
|
|
|
@@ -1640,38 +1884,88 @@
|
|
|
1640
1884
|
await fileEntityPromise;
|
|
1641
1885
|
}
|
|
1642
1886
|
await loadAll();
|
|
1887
|
+
if(destroyed){
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1643
1890
|
host.ready=true;
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
);
|
|
1891
|
+
const publication=events.dispatch('file-manager-ready',{}, {publicDetail:{ready:true}});
|
|
1892
|
+
projectArcaneDOMEvent(host,publication.occurrence);
|
|
1647
1893
|
}catch(error){
|
|
1648
|
-
|
|
1649
|
-
|
|
1894
|
+
if(!destroyed){
|
|
1895
|
+
initialized=false;
|
|
1896
|
+
console.error('Unable to initialize file manager:',error);
|
|
1897
|
+
}
|
|
1650
1898
|
}
|
|
1651
1899
|
}
|
|
1652
1900
|
|
|
1901
|
+
function destroy(){
|
|
1902
|
+
if(destroyed){
|
|
1903
|
+
return false;
|
|
1904
|
+
}
|
|
1905
|
+
destroyed=true;
|
|
1906
|
+
initialized=false;
|
|
1907
|
+
host.ready=false;
|
|
1908
|
+
lifecycleController.abort('file-manager-destroyed');
|
|
1909
|
+
treeLoadSequence+=1;
|
|
1910
|
+
gridLoadSequence+=1;
|
|
1911
|
+
providerGeneration+=1;
|
|
1912
|
+
filterGeneration+=1;
|
|
1913
|
+
treeOpenGeneration+=1;
|
|
1914
|
+
directoryOpenGeneration+=1;
|
|
1915
|
+
fileOpenGeneration+=1;
|
|
1916
|
+
deleteGeneration+=1;
|
|
1917
|
+
selectionGeneration+=1;
|
|
1918
|
+
if(activeFileURL){
|
|
1919
|
+
URL.revokeObjectURL(activeFileURL);
|
|
1920
|
+
activeFileURL='';
|
|
1921
|
+
}
|
|
1922
|
+
treeItems.clear();
|
|
1923
|
+
provider=null;
|
|
1924
|
+
filter=null;
|
|
1925
|
+
directoryModal.destroy?.();
|
|
1926
|
+
fileModal.destroy?.();
|
|
1927
|
+
deleteModal.destroy?.();
|
|
1928
|
+
events.dispose();
|
|
1929
|
+
return true;
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1653
1932
|
async function loadAll(){
|
|
1933
|
+
if(destroyed){
|
|
1934
|
+
return [];
|
|
1935
|
+
}
|
|
1936
|
+
selectionGeneration+=1;
|
|
1654
1937
|
if(layout==='tree'){
|
|
1655
1938
|
return loadTree();
|
|
1656
1939
|
}
|
|
1940
|
+
const sequence=++gridLoadSequence;
|
|
1657
1941
|
|
|
1658
1942
|
let tableNames=dirs;
|
|
1659
1943
|
|
|
1660
1944
|
if(!tableNames?.length){
|
|
1661
1945
|
tableNames=await dbopfs.getTableNames(Boolean(layout));
|
|
1662
1946
|
}
|
|
1947
|
+
if(destroyed||sequence!==gridLoadSequence){
|
|
1948
|
+
return [];
|
|
1949
|
+
}
|
|
1663
1950
|
|
|
1664
1951
|
tableNames.sort();
|
|
1665
1952
|
fileManager.replaceChildren();
|
|
1666
1953
|
|
|
1667
1954
|
for(let i=0;i<tableNames.length;i++){
|
|
1668
1955
|
const tableName=tableNames[i];
|
|
1669
|
-
const
|
|
1956
|
+
const keys=await dbopfs.getAllKeys(tableName);
|
|
1957
|
+
if(destroyed||sequence!==gridLoadSequence){
|
|
1958
|
+
return [];
|
|
1959
|
+
}
|
|
1960
|
+
const files=keys
|
|
1670
1961
|
.filter(isVisibleFileName)
|
|
1671
1962
|
.sort();
|
|
1672
1963
|
const metadata=layout==='files'
|
|
1673
1964
|
?await getFilesMetadata(tableName,files)
|
|
1674
1965
|
:[];
|
|
1966
|
+
if(destroyed||sequence!==gridLoadSequence){
|
|
1967
|
+
return [];
|
|
1968
|
+
}
|
|
1675
1969
|
const directory=document.createElement('ul');
|
|
1676
1970
|
const folder=document.createElement('li');
|
|
1677
1971
|
const folderIcon=document.createElement('img');
|
|
@@ -1711,5 +2005,6 @@
|
|
|
1711
2005
|
|
|
1712
2006
|
fileManager.append(directory);
|
|
1713
2007
|
}
|
|
2008
|
+
return tableNames;
|
|
1714
2009
|
}
|
|
1715
2010
|
</script>
|