arcane-os 0.35.0 → 0.36.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 +23 -0
- package/README.md +1 -1
- package/docs/reference/inventory/runtime-components.json +13 -6
- package/docs/reference/inventory/runtime-modules.json +28 -4
- package/docs/reference/runtime-components.md +202 -22
- package/docs/reference/runtime-modules.md +109 -1
- package/package.json +2 -2
- package/runtime/arcane/components/data-view.html +18 -2
- package/runtime/arcane/components/file-manager.html +512 -88
- package/runtime/arcane/components/voice-transcription.html +611 -55
- package/runtime/arcane/modules/ComponentContracts.js +32 -1
- package/runtime/arcane/modules/ContinuousVoiceCapture.js +446 -0
- package/runtime/arcane/modules/VoiceCaptureWorklet.js +194 -0
|
@@ -330,6 +330,88 @@
|
|
|
330
330
|
color:var(--danger-color);
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
.file-preview,
|
|
334
|
+
.file-preview-collection{
|
|
335
|
+
display:grid;
|
|
336
|
+
gap:1em;
|
|
337
|
+
min-width:0;
|
|
338
|
+
overflow-wrap:anywhere;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.file-preview h2,
|
|
342
|
+
.file-preview h3,
|
|
343
|
+
.file-preview h4,
|
|
344
|
+
.file-preview p{
|
|
345
|
+
margin-top:0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.file-preview pre{
|
|
349
|
+
margin:0;
|
|
350
|
+
overflow-wrap:anywhere;
|
|
351
|
+
white-space:pre-wrap;
|
|
352
|
+
user-select:text;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.file-preview img{
|
|
356
|
+
max-width:100%;
|
|
357
|
+
height:auto;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.file-preview audio,
|
|
361
|
+
.file-preview iframe{
|
|
362
|
+
width:100%;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.file-preview iframe{
|
|
366
|
+
min-height:60vh;
|
|
367
|
+
border:0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.file-preview table{
|
|
371
|
+
display:block;
|
|
372
|
+
max-width:100%;
|
|
373
|
+
overflow:auto;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.file-preview-conversation{
|
|
377
|
+
display:grid;
|
|
378
|
+
gap:1em;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.file-preview-message{
|
|
382
|
+
display:grid;
|
|
383
|
+
gap:.5em;
|
|
384
|
+
padding:1em;
|
|
385
|
+
border:1px solid var(--border-color);
|
|
386
|
+
border-radius:.5em;
|
|
387
|
+
background:var(--background);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.file-preview-message header,
|
|
391
|
+
.file-preview-message footer{
|
|
392
|
+
display:flex;
|
|
393
|
+
flex-wrap:wrap;
|
|
394
|
+
gap:.75em;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.file-preview-message footer{
|
|
398
|
+
font-size:.85em;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.file-preview-download:focus-visible{
|
|
402
|
+
outline:2px solid var(--focus-color,var(--accent-color));
|
|
403
|
+
outline-offset:.2em;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
</style>
|
|
407
|
+
|
|
408
|
+
<!-- Host preview defaults stay separate from the presentation styles copied into modals. -->
|
|
409
|
+
<style>
|
|
410
|
+
:host{
|
|
411
|
+
--modal-width:min(72rem,calc(100vw - 2rem));
|
|
412
|
+
--modal-max-height:calc(100vh - 2rem);
|
|
413
|
+
--modal-padding:1.25rem;
|
|
414
|
+
}
|
|
333
415
|
</style>
|
|
334
416
|
|
|
335
417
|
<div class="file-manager">
|
|
@@ -373,7 +455,8 @@
|
|
|
373
455
|
).filter(Boolean);
|
|
374
456
|
const fileEntityPromise=import('../entities/File.js');
|
|
375
457
|
|
|
376
|
-
let
|
|
458
|
+
let activePreview=null;
|
|
459
|
+
let markdownModulePromise=null;
|
|
377
460
|
let uploadDirectory='';
|
|
378
461
|
let initialized=false;
|
|
379
462
|
let provider=null;
|
|
@@ -396,6 +479,7 @@
|
|
|
396
479
|
host.loadAll=loadAll;
|
|
397
480
|
host.setProvider=setProvider;
|
|
398
481
|
host.setFilter=setFilter;
|
|
482
|
+
host.setDirectoryFilter=setDirectoryFilter;
|
|
399
483
|
host.select=select;
|
|
400
484
|
host.clearSelection=clearSelection;
|
|
401
485
|
host.destroy=destroy;
|
|
@@ -434,24 +518,66 @@
|
|
|
434
518
|
return [];
|
|
435
519
|
}
|
|
436
520
|
|
|
437
|
-
function
|
|
521
|
+
function filterContext(directory='',fileName=''){
|
|
522
|
+
return {
|
|
523
|
+
directory,
|
|
524
|
+
fileName,
|
|
525
|
+
path:joinProviderPath(directory,fileName),
|
|
526
|
+
layout
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function isVisibleDirectory(directory='',entry=null){
|
|
531
|
+
const candidate=entry||{name:directory,path:directory,kind:'directory'};
|
|
532
|
+
if(configuredHiddenPrefixes().some(function hasHiddenPrefix(prefix){
|
|
533
|
+
return String(candidate.name||'').startsWith(prefix);
|
|
534
|
+
})){
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
return !is.function(host.directoryFilter)
|
|
538
|
+
||host.directoryFilter(candidate,filterContext(directory))!==false;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function isVisibleEntry(entry={},context=null){
|
|
438
542
|
const normalized=is.string(entry)
|
|
439
543
|
?{name:entry,path:entry,kind:'file'}
|
|
440
544
|
:entry;
|
|
545
|
+
const directory=normalized.directory
|
|
546
|
+
||(normalized.kind==='directory'?normalized.path:'');
|
|
547
|
+
const details=context||filterContext(directory,normalized.fileName||normalized.name);
|
|
548
|
+
|
|
549
|
+
if(normalized.kind==='directory'
|
|
550
|
+
&&!isVisibleDirectory(normalized.path||normalized.name,normalized)){
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
441
553
|
const activeFilter=filter||provider?.filter;
|
|
442
554
|
|
|
443
555
|
if(is.function(activeFilter)){
|
|
444
|
-
return activeFilter(normalized)!==false;
|
|
556
|
+
return activeFilter(normalized,details)!==false;
|
|
445
557
|
}
|
|
446
558
|
|
|
447
559
|
const name=String(normalized.name||'');
|
|
448
560
|
return !configuredHiddenPrefixes().some(prefix=>name.startsWith(prefix));
|
|
449
561
|
}
|
|
450
562
|
|
|
451
|
-
function
|
|
452
|
-
return
|
|
563
|
+
function visibleFileNames(fileNames=[],directory=''){
|
|
564
|
+
return fileNames.filter(function isVisibleFileName(fileName){
|
|
565
|
+
return isVisibleEntry(
|
|
566
|
+
{name:fileName,path:fileName,kind:'file'},
|
|
567
|
+
filterContext(directory,fileName)
|
|
568
|
+
);
|
|
569
|
+
});
|
|
453
570
|
}
|
|
454
571
|
|
|
572
|
+
fileModal.addEventListener(
|
|
573
|
+
'modal-closed',
|
|
574
|
+
function closeFilePreview(){
|
|
575
|
+
fileOpenGeneration+=1;
|
|
576
|
+
releasePreview(activePreview,'file-preview-closed');
|
|
577
|
+
},
|
|
578
|
+
{signal:lifecycleController.signal}
|
|
579
|
+
);
|
|
580
|
+
|
|
455
581
|
fileUpload.addEventListener(
|
|
456
582
|
'change',
|
|
457
583
|
()=>uploadFiles().catch(
|
|
@@ -572,6 +698,21 @@
|
|
|
572
698
|
return destroyed||generation!==filterGeneration?null:filter;
|
|
573
699
|
}
|
|
574
700
|
|
|
701
|
+
async function setDirectoryFilter(nextFilter=null){
|
|
702
|
+
if(destroyed){
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
if(nextFilter!==null&&!is.function(nextFilter)){
|
|
706
|
+
throw new TypeError('File manager directory filters must be functions or null.');
|
|
707
|
+
}
|
|
708
|
+
const generation=++filterGeneration;
|
|
709
|
+
host.directoryFilter=nextFilter;
|
|
710
|
+
if(initialized){
|
|
711
|
+
await loadAll();
|
|
712
|
+
}
|
|
713
|
+
return destroyed||generation!==filterGeneration?null:host.directoryFilter;
|
|
714
|
+
}
|
|
715
|
+
|
|
575
716
|
function normalizeProviderPath(path=''){
|
|
576
717
|
return String(path||'')
|
|
577
718
|
.replaceAll('\\','/')
|
|
@@ -632,7 +773,7 @@
|
|
|
632
773
|
for(const candidate of entries){
|
|
633
774
|
const entry=normalizeProviderEntry(candidate,parentPath);
|
|
634
775
|
|
|
635
|
-
if(entry&&isVisibleEntry(entry)&&!unique.has(entry.path)){
|
|
776
|
+
if(entry&&isVisibleEntry(entry,filterContext(parentPath,entry.name))&&!unique.has(entry.path)){
|
|
636
777
|
unique.set(entry.path,entry);
|
|
637
778
|
}
|
|
638
779
|
}
|
|
@@ -665,9 +806,10 @@
|
|
|
665
806
|
}));
|
|
666
807
|
}
|
|
667
808
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
809
|
+
if(!isVisibleDirectory(normalizedPath)){
|
|
810
|
+
return [];
|
|
811
|
+
}
|
|
812
|
+
const files=visibleFileNames(await dbopfs.getAllKeys(normalizedPath),normalizedPath).sort();
|
|
671
813
|
const metadata=await getFilesMetadata(normalizedPath,files);
|
|
672
814
|
|
|
673
815
|
return files.map((fileName,index)=>({
|
|
@@ -1301,7 +1443,7 @@
|
|
|
1301
1443
|
}
|
|
1302
1444
|
|
|
1303
1445
|
async function openDirectory(directory=''){
|
|
1304
|
-
if(destroyed){
|
|
1446
|
+
if(destroyed||!isVisibleDirectory(directory)){
|
|
1305
1447
|
return false;
|
|
1306
1448
|
}
|
|
1307
1449
|
const generation=++directoryOpenGeneration;
|
|
@@ -1315,16 +1457,14 @@
|
|
|
1315
1457
|
},
|
|
1316
1458
|
{signal:lifecycleController.signal}
|
|
1317
1459
|
);
|
|
1318
|
-
if(destroyed||generation!==directoryOpenGeneration){
|
|
1460
|
+
if(destroyed||generation!==directoryOpenGeneration||!isVisibleDirectory(directory)){
|
|
1319
1461
|
return false;
|
|
1320
1462
|
}
|
|
1321
1463
|
const keys=await dbopfs.getAllKeys(directory);
|
|
1322
1464
|
if(destroyed||generation!==directoryOpenGeneration){
|
|
1323
1465
|
return false;
|
|
1324
1466
|
}
|
|
1325
|
-
const files=keys
|
|
1326
|
-
.filter(isVisibleFileName)
|
|
1327
|
-
.sort();
|
|
1467
|
+
const files=visibleFileNames(keys,directory).sort();
|
|
1328
1468
|
const metadata=await getFilesMetadata(directory,files);
|
|
1329
1469
|
if(destroyed||generation!==directoryOpenGeneration){
|
|
1330
1470
|
return false;
|
|
@@ -1382,20 +1522,39 @@
|
|
|
1382
1522
|
if(destroyed){
|
|
1383
1523
|
return false;
|
|
1384
1524
|
}
|
|
1525
|
+
releasePreview(activePreview,'file-preview-replaced');
|
|
1385
1526
|
const generation=++fileOpenGeneration;
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1527
|
+
const preview={
|
|
1528
|
+
controller:new AbortController(),
|
|
1529
|
+
generation,
|
|
1530
|
+
urls:new Set(),
|
|
1531
|
+
media:new Set()
|
|
1532
|
+
};
|
|
1533
|
+
activePreview=preview;
|
|
1534
|
+
let modal;
|
|
1535
|
+
try{
|
|
1536
|
+
modal=await waitForComponent(
|
|
1537
|
+
fileModal,
|
|
1538
|
+
{
|
|
1539
|
+
methods:['populate','open'],
|
|
1540
|
+
property:'ready',
|
|
1541
|
+
event:'modal-ready',
|
|
1542
|
+
signal:preview.controller.signal
|
|
1543
|
+
},
|
|
1544
|
+
{signal:preview.controller.signal}
|
|
1545
|
+
);
|
|
1546
|
+
}catch(error){
|
|
1547
|
+
const current=isCurrentPreview(preview);
|
|
1548
|
+
releasePreview(preview,'file-preview-unavailable');
|
|
1549
|
+
if(current){
|
|
1550
|
+
throw error;
|
|
1551
|
+
}
|
|
1397
1552
|
return false;
|
|
1398
1553
|
}
|
|
1554
|
+
if(!isCurrentPreview(preview)){
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
configureFileModal(modal);
|
|
1399
1558
|
const content=document.createElement('section');
|
|
1400
1559
|
const styles=componentStyles.cloneNode(true);
|
|
1401
1560
|
const heading=document.createElement('h1');
|
|
@@ -1413,18 +1572,17 @@
|
|
|
1413
1572
|
deleteIcon.className='action-icon';
|
|
1414
1573
|
deleteIcon.src=new URL('../img/trash.svg',componentHref).href;
|
|
1415
1574
|
deleteButton.prepend(deleteIcon);
|
|
1416
|
-
deleteButton.
|
|
1417
|
-
|
|
1418
|
-
()=>confirmDelete(directory,fileName,true).catch(
|
|
1575
|
+
deleteButton.onclick=function deletePreviewFile(){
|
|
1576
|
+
return confirmDelete(directory,fileName,true).catch(
|
|
1419
1577
|
error=>{
|
|
1420
1578
|
if(!destroyed){
|
|
1421
1579
|
arcaneLogging.error('Unable to delete file:',error);
|
|
1422
1580
|
}
|
|
1423
1581
|
}
|
|
1424
|
-
)
|
|
1425
|
-
|
|
1426
|
-
);
|
|
1582
|
+
);
|
|
1583
|
+
};
|
|
1427
1584
|
status.innerText='Opening file...';
|
|
1585
|
+
status.setAttribute('role','status');
|
|
1428
1586
|
actions.append(deleteButton);
|
|
1429
1587
|
content.append(
|
|
1430
1588
|
styles,
|
|
@@ -1435,107 +1593,363 @@
|
|
|
1435
1593
|
);
|
|
1436
1594
|
|
|
1437
1595
|
await modal.populate(content,false);
|
|
1438
|
-
if(
|
|
1596
|
+
if(!isCurrentPreview(preview)){
|
|
1439
1597
|
return false;
|
|
1440
1598
|
}
|
|
1441
1599
|
modal.open();
|
|
1442
1600
|
|
|
1443
1601
|
try{
|
|
1444
1602
|
const {default:FileEntity}=await fileEntityPromise;
|
|
1445
|
-
if(
|
|
1603
|
+
if(!isCurrentPreview(preview)){
|
|
1446
1604
|
return false;
|
|
1447
1605
|
}
|
|
1448
1606
|
const file=new FileEntity(fileName,directory);
|
|
1449
1607
|
const fileRef=await file.open();
|
|
1450
|
-
if(
|
|
1608
|
+
if(!isCurrentPreview(preview)){
|
|
1451
1609
|
return false;
|
|
1452
1610
|
}
|
|
1453
1611
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
if(destroyed||generation!==fileOpenGeneration){
|
|
1612
|
+
const rendered=await renderFile(fileRef,{directory,fileName},preview);
|
|
1613
|
+
if(!isCurrentPreview(preview)){
|
|
1614
|
+
releasePreview(preview,'file-preview-superseded');
|
|
1458
1615
|
return false;
|
|
1459
1616
|
}
|
|
1617
|
+
status.remove();
|
|
1460
1618
|
content.append(rendered);
|
|
1461
1619
|
return true;
|
|
1462
1620
|
}catch(error){
|
|
1463
|
-
if(
|
|
1621
|
+
if(isCurrentPreview(preview)){
|
|
1464
1622
|
status.innerText='Unable to open this file.';
|
|
1623
|
+
status.setAttribute('role','alert');
|
|
1465
1624
|
arcaneLogging.error('Unable to open file:',error);
|
|
1466
1625
|
}
|
|
1626
|
+
releasePreview(preview,'file-preview-failed');
|
|
1467
1627
|
return false;
|
|
1468
1628
|
}
|
|
1469
1629
|
}
|
|
1470
1630
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1631
|
+
function configureFileModal(modal){
|
|
1632
|
+
const appearance=getComputedStyle(host);
|
|
1633
|
+
for(const property of ['--modal-width','--modal-max-height','--modal-padding']){
|
|
1634
|
+
if(!modal.style.getPropertyValue(property)){
|
|
1635
|
+
modal.style.setProperty(property,appearance.getPropertyValue(property).trim());
|
|
1636
|
+
}
|
|
1475
1637
|
}
|
|
1638
|
+
}
|
|
1476
1639
|
|
|
1477
|
-
|
|
1478
|
-
|
|
1640
|
+
function isCurrentPreview(preview){
|
|
1641
|
+
return !destroyed
|
|
1642
|
+
&&activePreview===preview
|
|
1643
|
+
&&preview.generation===fileOpenGeneration
|
|
1644
|
+
&&!preview.controller.signal.aborted;
|
|
1645
|
+
}
|
|
1479
1646
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1647
|
+
function releasePreview(preview,reason){
|
|
1648
|
+
if(!preview){
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
preview.controller.abort(reason);
|
|
1652
|
+
for(const element of preview.media){
|
|
1653
|
+
if(element.localName==='audio'){
|
|
1654
|
+
element.pause();
|
|
1655
|
+
}
|
|
1656
|
+
if(element.localName==='iframe'){
|
|
1657
|
+
element.removeAttribute('srcdoc');
|
|
1658
|
+
}
|
|
1659
|
+
element.removeAttribute(element.localName==='a'?'href':'src');
|
|
1660
|
+
if(element.localName==='audio'){
|
|
1661
|
+
element.load();
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
for(const url of preview.urls){
|
|
1665
|
+
URL.revokeObjectURL(url);
|
|
1487
1666
|
}
|
|
1667
|
+
preview.media.clear();
|
|
1668
|
+
preview.urls.clear();
|
|
1669
|
+
if(activePreview===preview){
|
|
1670
|
+
activePreview=null;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1488
1673
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1674
|
+
function previewObjectURL(content,preview){
|
|
1675
|
+
if(!(content instanceof Blob)){
|
|
1676
|
+
throw new TypeError('File preview media and downloads require a Blob or File.');
|
|
1677
|
+
}
|
|
1678
|
+
preview.controller.signal.throwIfAborted();
|
|
1679
|
+
const url=URL.createObjectURL(content);
|
|
1680
|
+
preview.urls.add(url);
|
|
1681
|
+
return url;
|
|
1682
|
+
}
|
|
1491
1683
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1684
|
+
async function renderDescriptor(descriptor,preview,depth=0){
|
|
1685
|
+
preview.controller.signal.throwIfAborted();
|
|
1686
|
+
if(!descriptor||!is.object(descriptor)||is.array(descriptor)){
|
|
1687
|
+
throw new TypeError('File preview descriptors must be objects.');
|
|
1688
|
+
}
|
|
1689
|
+
const rendered=document.createElement('section');
|
|
1690
|
+
rendered.className='file-preview';
|
|
1691
|
+
if(descriptor.title!==undefined){
|
|
1692
|
+
const heading=document.createElement(`h${Math.min(depth+2,6)}`);
|
|
1693
|
+
heading.textContent=descriptor.title;
|
|
1694
|
+
rendered.append(heading);
|
|
1500
1695
|
}
|
|
1501
1696
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1697
|
+
switch(descriptor.kind){
|
|
1698
|
+
case 'text':
|
|
1699
|
+
case 'markdown':
|
|
1700
|
+
case 'html':{
|
|
1701
|
+
if(!is.string(descriptor.content)){
|
|
1702
|
+
throw new TypeError(`File preview ${descriptor.kind} content must be a string.`);
|
|
1703
|
+
}
|
|
1704
|
+
if(descriptor.kind==='text'){
|
|
1705
|
+
rendered.append(createPreformatted(descriptor.content));
|
|
1706
|
+
break;
|
|
1707
|
+
}
|
|
1708
|
+
const body=document.createElement(descriptor.kind==='html'?'iframe':'div');
|
|
1709
|
+
if(descriptor.kind==='markdown'){
|
|
1710
|
+
markdownModulePromise=markdownModulePromise||import('../modules/MD.js');
|
|
1711
|
+
const {default:MD}=await markdownModulePromise;
|
|
1712
|
+
preview.controller.signal.throwIfAborted();
|
|
1713
|
+
body.innerHTML=new MD(descriptor.content).safeRendered;
|
|
1714
|
+
}else{
|
|
1715
|
+
body.title=descriptor.title||'HTML preview';
|
|
1716
|
+
body.srcdoc=descriptor.content;
|
|
1717
|
+
preview.media.add(body);
|
|
1718
|
+
}
|
|
1719
|
+
rendered.append(body);
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
case 'json':
|
|
1723
|
+
rendered.append(createPreformatted(JSON.stringify(descriptor.content,null,4)));
|
|
1724
|
+
break;
|
|
1725
|
+
case 'conversation':{
|
|
1726
|
+
if(!is.array(descriptor.messages)){
|
|
1727
|
+
throw new TypeError('File preview conversation messages must be an array.');
|
|
1728
|
+
}
|
|
1729
|
+
const messages=document.createElement('div');
|
|
1730
|
+
messages.className='file-preview-conversation';
|
|
1731
|
+
const entries=await Promise.all(
|
|
1732
|
+
descriptor.messages.map(async function renderConversationMessage(message){
|
|
1733
|
+
if(!message||!is.string(message.role)||!is.string(message.content)){
|
|
1734
|
+
throw new TypeError('File preview messages require role and content strings.');
|
|
1735
|
+
}
|
|
1736
|
+
const article=document.createElement('article');
|
|
1737
|
+
const header=document.createElement('header');
|
|
1738
|
+
const speaker=document.createElement('strong');
|
|
1739
|
+
const footer=document.createElement('footer');
|
|
1740
|
+
const time=document.createElement('time');
|
|
1741
|
+
article.className='file-preview-message';
|
|
1742
|
+
article.dataset.role=message.role;
|
|
1743
|
+
speaker.textContent=message.name===undefined
|
|
1744
|
+
?message.role
|
|
1745
|
+
:`${message.name} · ${message.role}`;
|
|
1746
|
+
header.append(speaker);
|
|
1747
|
+
if(message.timestamp===undefined||message.timestamp===null){
|
|
1748
|
+
time.textContent='Time unavailable';
|
|
1749
|
+
}else{
|
|
1750
|
+
const timestamp=new Date(message.timestamp);
|
|
1751
|
+
if(is.finite(timestamp.getTime())){
|
|
1752
|
+
time.dateTime=timestamp.toISOString();
|
|
1753
|
+
time.textContent=timestamp.toLocaleString();
|
|
1754
|
+
}else{
|
|
1755
|
+
time.textContent=String(message.timestamp);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
footer.append(time);
|
|
1759
|
+
if(message.status!==undefined){
|
|
1760
|
+
const state=document.createElement('span');
|
|
1761
|
+
state.textContent=message.status;
|
|
1762
|
+
footer.append(state);
|
|
1763
|
+
}
|
|
1764
|
+
const body=await renderDescriptor(
|
|
1765
|
+
{kind:'markdown',content:message.content},
|
|
1766
|
+
preview,
|
|
1767
|
+
depth+1
|
|
1768
|
+
);
|
|
1769
|
+
article.append(header,body,footer);
|
|
1770
|
+
return article;
|
|
1771
|
+
})
|
|
1772
|
+
);
|
|
1773
|
+
preview.controller.signal.throwIfAborted();
|
|
1774
|
+
for(const entry of entries){
|
|
1775
|
+
messages.append(entry);
|
|
1776
|
+
}
|
|
1777
|
+
rendered.append(messages);
|
|
1778
|
+
break;
|
|
1779
|
+
}
|
|
1780
|
+
case 'collection':{
|
|
1781
|
+
if(!is.array(descriptor.items)){
|
|
1782
|
+
throw new TypeError('File preview collection items must be an array.');
|
|
1783
|
+
}
|
|
1784
|
+
const items=await Promise.all(
|
|
1785
|
+
descriptor.items.map(function renderCollectionItem(item){
|
|
1786
|
+
return renderDescriptor(item,preview,depth+1);
|
|
1787
|
+
})
|
|
1788
|
+
);
|
|
1789
|
+
preview.controller.signal.throwIfAborted();
|
|
1790
|
+
const collection=document.createElement('div');
|
|
1791
|
+
collection.className='file-preview-collection';
|
|
1792
|
+
for(const item of items){
|
|
1793
|
+
collection.append(item);
|
|
1794
|
+
}
|
|
1795
|
+
rendered.append(collection);
|
|
1796
|
+
break;
|
|
1797
|
+
}
|
|
1798
|
+
case 'image':
|
|
1799
|
+
case 'audio':
|
|
1800
|
+
case 'pdf':
|
|
1801
|
+
case 'download':{
|
|
1802
|
+
if(descriptor.kind==='download'&&!is.string(descriptor.fileName)){
|
|
1803
|
+
throw new TypeError('File preview downloads require a fileName string.');
|
|
1804
|
+
}
|
|
1805
|
+
if(is.function(descriptor.content)){
|
|
1806
|
+
const item=document.createElement('div');
|
|
1807
|
+
const loadButton=document.createElement('button');
|
|
1808
|
+
const state=document.createElement('p');
|
|
1809
|
+
loadButton.type='button';
|
|
1810
|
+
loadButton.textContent=`Load ${descriptor.title||descriptor.fileName||descriptor.kind}`;
|
|
1811
|
+
state.setAttribute('role','status');
|
|
1812
|
+
loadButton.addEventListener(
|
|
1813
|
+
'click',
|
|
1814
|
+
async function loadPreviewMedia(){
|
|
1815
|
+
loadButton.disabled=true;
|
|
1816
|
+
state.setAttribute('role','status');
|
|
1817
|
+
state.textContent='Loading...';
|
|
1818
|
+
try{
|
|
1819
|
+
const content=await descriptor.content({signal:preview.controller.signal});
|
|
1820
|
+
if(!isCurrentPreview(preview)){
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
item.replaceChildren(createPreviewMedia(descriptor,content,preview));
|
|
1824
|
+
}catch(error){
|
|
1825
|
+
if(isCurrentPreview(preview)){
|
|
1826
|
+
state.setAttribute('role','alert');
|
|
1827
|
+
state.textContent='Unable to load this item.';
|
|
1828
|
+
loadButton.disabled=false;
|
|
1829
|
+
arcaneLogging.error('Unable to load file preview item:',error);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
{signal:preview.controller.signal}
|
|
1834
|
+
);
|
|
1835
|
+
item.append(loadButton,state);
|
|
1836
|
+
rendered.append(item);
|
|
1837
|
+
}else{
|
|
1838
|
+
rendered.append(createPreviewMedia(descriptor,descriptor.content,preview));
|
|
1839
|
+
}
|
|
1840
|
+
break;
|
|
1841
|
+
}
|
|
1842
|
+
default:
|
|
1843
|
+
throw new TypeError(`Unsupported file preview kind: ${String(descriptor.kind)}.`);
|
|
1844
|
+
}
|
|
1845
|
+
return rendered;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function createPreviewMedia(descriptor,content,preview){
|
|
1849
|
+
const tag={image:'img',audio:'audio',pdf:'iframe',download:'a'}[descriptor.kind];
|
|
1850
|
+
const media=document.createElement(tag);
|
|
1851
|
+
const url=previewObjectURL(content,preview);
|
|
1852
|
+
preview.media.add(media);
|
|
1853
|
+
if(descriptor.kind==='download'){
|
|
1854
|
+
media.className='file-preview-download';
|
|
1855
|
+
media.href=url;
|
|
1856
|
+
media.download=descriptor.fileName;
|
|
1857
|
+
media.textContent=descriptor.title||descriptor.fileName;
|
|
1858
|
+
}else{
|
|
1859
|
+
media.src=url;
|
|
1860
|
+
if(descriptor.kind==='image'){
|
|
1861
|
+
media.alt=descriptor.alt??descriptor.title??content.name??'';
|
|
1862
|
+
}else if(descriptor.kind==='audio'){
|
|
1863
|
+
media.controls=true;
|
|
1864
|
+
media.preload='metadata';
|
|
1865
|
+
media.setAttribute('aria-label',descriptor.title||content.name||'Audio preview');
|
|
1866
|
+
}else{
|
|
1867
|
+
media.title=descriptor.title||content.name||'PDF preview';
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return media;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
async function renderFile(fileRef,context={},preview){
|
|
1874
|
+
const details={
|
|
1875
|
+
directory:String(context.directory||''),
|
|
1876
|
+
extension:String(fileRef.ext||''),
|
|
1877
|
+
fileName:String(context.fileName||fileRef.name||''),
|
|
1878
|
+
path:joinProviderPath(context.directory,context.fileName||fileRef.name),
|
|
1879
|
+
mimeType:String(fileRef.mime||fileRef.type||''),
|
|
1880
|
+
signal:preview.controller.signal
|
|
1881
|
+
};
|
|
1882
|
+
const mimeType=details.mimeType.split(';')[0].trim().toLowerCase();
|
|
1883
|
+
if(is.function(host.previewDescriptor)){
|
|
1884
|
+
const descriptor=await host.previewDescriptor(fileRef,details);
|
|
1885
|
+
preview.controller.signal.throwIfAborted();
|
|
1886
|
+
if(descriptor!==null&&descriptor!==undefined){
|
|
1887
|
+
return renderDescriptor(descriptor,preview);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1504
1890
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
:
|
|
1508
|
-
|
|
1891
|
+
if(mimeType.startsWith('image/')||['png','jpg','jpeg','webp','svg'].includes(fileRef.ext)){
|
|
1892
|
+
return renderDescriptor(
|
|
1893
|
+
{kind:'image',content:new Blob([fileRef],{type:fileRef.mime}),alt:fileRef.name},
|
|
1894
|
+
preview
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
if(mimeType.startsWith('audio/')){
|
|
1898
|
+
return renderDescriptor(
|
|
1899
|
+
{kind:'audio',content:new Blob([fileRef],{type:fileRef.mime})},
|
|
1900
|
+
preview
|
|
1901
|
+
);
|
|
1902
|
+
}
|
|
1903
|
+
if(fileRef.ext==='pdf'||mimeType==='application/pdf'){
|
|
1904
|
+
return renderDescriptor(
|
|
1905
|
+
{kind:'pdf',content:new Blob([fileRef],{type:fileRef.mime}),title:fileRef.name},
|
|
1906
|
+
preview
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
if(fileRef.ext==='html'||mimeType==='text/html'){
|
|
1910
|
+
const html=await fileRef.text();
|
|
1911
|
+
preview.controller.signal.throwIfAborted();
|
|
1912
|
+
return renderDescriptor({kind:'html',content:html,title:fileRef.name},preview);
|
|
1913
|
+
}
|
|
1914
|
+
if(fileRef.ext==='md'||mimeType==='text/markdown'){
|
|
1915
|
+
const markdown=await fileRef.text();
|
|
1916
|
+
preview.controller.signal.throwIfAborted();
|
|
1917
|
+
return renderDescriptor({kind:'markdown',content:markdown},preview);
|
|
1509
1918
|
}
|
|
1510
1919
|
|
|
1511
1920
|
const rendered=document.createElement('div');
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
){
|
|
1518
|
-
|
|
1519
|
-
|
|
1921
|
+
rendered.className='file-preview';
|
|
1922
|
+
let value=fileRef.parsed;
|
|
1923
|
+
const transformed=['json','jsonl','ndjson'].includes(fileRef.ext)
|
|
1924
|
+
&&is.function(host.previewTransform);
|
|
1925
|
+
|
|
1926
|
+
if(transformed){
|
|
1927
|
+
value=await host.previewTransform(
|
|
1928
|
+
value,
|
|
1520
1929
|
{
|
|
1521
1930
|
directory:String(context.directory||''),
|
|
1522
1931
|
extension:String(fileRef.ext||''),
|
|
1523
1932
|
fileName:String(context.fileName||fileRef.name||'')
|
|
1524
1933
|
}
|
|
1525
1934
|
);
|
|
1935
|
+
preview.controller.signal.throwIfAborted();
|
|
1526
1936
|
}
|
|
1527
1937
|
|
|
1528
1938
|
switch(fileRef.ext){
|
|
1529
1939
|
case 'json':
|
|
1530
1940
|
rendered.append(
|
|
1531
1941
|
createPreformatted(
|
|
1532
|
-
JSON.stringify(
|
|
1942
|
+
JSON.stringify(value,null,4)
|
|
1533
1943
|
)
|
|
1534
1944
|
);
|
|
1535
1945
|
break;
|
|
1536
1946
|
case 'jsonl':
|
|
1537
1947
|
case 'ndjson':{
|
|
1538
|
-
|
|
1948
|
+
if(!transformed){
|
|
1949
|
+
rendered.append(createPreformatted(await fileRef.text()));
|
|
1950
|
+
break;
|
|
1951
|
+
}
|
|
1952
|
+
const records=is.array(value)?value:[];
|
|
1539
1953
|
|
|
1540
1954
|
for(let i=0;i<records.length;i++){
|
|
1541
1955
|
rendered.append(
|
|
@@ -1551,11 +1965,23 @@
|
|
|
1551
1965
|
break;
|
|
1552
1966
|
}
|
|
1553
1967
|
default:
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1968
|
+
if(mimeType.startsWith('text/')
|
|
1969
|
+
||['application/json','application/javascript','application/xml'].includes(mimeType)
|
|
1970
|
+
||mimeType.endsWith('+json')
|
|
1971
|
+
||mimeType.endsWith('+xml')
|
|
1972
|
+
||['txt','log','css','js','mjs'].includes(fileRef.ext)){
|
|
1973
|
+
rendered.append(createPreformatted(await fileRef.text()));
|
|
1974
|
+
}else{
|
|
1975
|
+
rendered.append(
|
|
1976
|
+
await renderDescriptor(
|
|
1977
|
+
{kind:'download',content:fileRef,fileName:details.fileName},
|
|
1978
|
+
preview
|
|
1979
|
+
)
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1557
1982
|
}
|
|
1558
1983
|
|
|
1984
|
+
preview.controller.signal.throwIfAborted();
|
|
1559
1985
|
return rendered;
|
|
1560
1986
|
}
|
|
1561
1987
|
|
|
@@ -1879,10 +2305,7 @@
|
|
|
1879
2305
|
fileOpenGeneration+=1;
|
|
1880
2306
|
deleteGeneration+=1;
|
|
1881
2307
|
selectionGeneration+=1;
|
|
1882
|
-
|
|
1883
|
-
URL.revokeObjectURL(activeFileURL);
|
|
1884
|
-
activeFileURL='';
|
|
1885
|
-
}
|
|
2308
|
+
releasePreview(activePreview,'file-manager-destroyed');
|
|
1886
2309
|
treeItems.clear();
|
|
1887
2310
|
provider=null;
|
|
1888
2311
|
filter=null;
|
|
@@ -1917,13 +2340,14 @@
|
|
|
1917
2340
|
|
|
1918
2341
|
for(let i=0;i<tableNames.length;i++){
|
|
1919
2342
|
const tableName=tableNames[i];
|
|
2343
|
+
if(!isVisibleDirectory(tableName)){
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
1920
2346
|
const keys=await dbopfs.getAllKeys(tableName);
|
|
1921
2347
|
if(destroyed||sequence!==gridLoadSequence){
|
|
1922
2348
|
return [];
|
|
1923
2349
|
}
|
|
1924
|
-
const files=keys
|
|
1925
|
-
.filter(isVisibleFileName)
|
|
1926
|
-
.sort();
|
|
2350
|
+
const files=visibleFileNames(keys,tableName).sort();
|
|
1927
2351
|
const metadata=layout==='files'
|
|
1928
2352
|
?await getFilesMetadata(tableName,files)
|
|
1929
2353
|
:[];
|