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
|
@@ -234,6 +234,9 @@
|
|
|
234
234
|
<script type="module">
|
|
235
235
|
const host=this;
|
|
236
236
|
const root=host.shadowRoot;
|
|
237
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
238
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.app-bar',eventTypes:['app-bar-ready']});
|
|
239
|
+
const lifecycleController=new AbortController();
|
|
237
240
|
const nav=root.querySelector('nav');
|
|
238
241
|
const brandLink=root.querySelector('#brandLink');
|
|
239
242
|
const navigationSlot=root.querySelector('#navigationSlot');
|
|
@@ -242,11 +245,13 @@
|
|
|
242
245
|
const statusTones=new Set(['default','success','warning','error','muted']);
|
|
243
246
|
|
|
244
247
|
let navigation=[];
|
|
248
|
+
let destroyed=false;
|
|
245
249
|
|
|
246
250
|
host.setNavigation=setNavigation;
|
|
247
251
|
host.setActiveRoute=setActiveRoute;
|
|
248
252
|
host.setStatus=setStatus;
|
|
249
253
|
host.refresh=refresh;
|
|
254
|
+
host.destroy=destroy;
|
|
250
255
|
|
|
251
256
|
Object.defineProperties(
|
|
252
257
|
host,
|
|
@@ -269,9 +274,9 @@
|
|
|
269
274
|
}
|
|
270
275
|
);
|
|
271
276
|
|
|
272
|
-
navigationSlot.addEventListener('slotchange',updateCurrent);
|
|
273
|
-
window.addEventListener('hashchange',onLocationChange);
|
|
274
|
-
window.addEventListener('popstate',onLocationChange);
|
|
277
|
+
navigationSlot.addEventListener('slotchange',updateCurrent,{signal:lifecycleController.signal});
|
|
278
|
+
window.addEventListener('hashchange',onLocationChange,{signal:lifecycleController.signal});
|
|
279
|
+
window.addEventListener('popstate',onLocationChange,{signal:lifecycleController.signal});
|
|
275
280
|
|
|
276
281
|
const observer=new MutationObserver(
|
|
277
282
|
records=>{
|
|
@@ -322,6 +327,9 @@
|
|
|
322
327
|
}
|
|
323
328
|
|
|
324
329
|
function setNavigation(items=[]){
|
|
330
|
+
if(destroyed){
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
325
333
|
navigation=normalizeNavigation(items);
|
|
326
334
|
generatedNavigation.replaceChildren(
|
|
327
335
|
...navigation.map(
|
|
@@ -395,6 +403,9 @@
|
|
|
395
403
|
}
|
|
396
404
|
|
|
397
405
|
function setActiveRoute(route=''){
|
|
406
|
+
if(destroyed){
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
398
409
|
if(route){
|
|
399
410
|
host.setAttribute('data-active-route',String(route).replace(/^#/,''));
|
|
400
411
|
}else{
|
|
@@ -424,6 +435,9 @@
|
|
|
424
435
|
}
|
|
425
436
|
|
|
426
437
|
function setStatus(message='',tone='default'){
|
|
438
|
+
if(destroyed){
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
427
441
|
if(message===null||message===''){
|
|
428
442
|
host.removeAttribute('data-status');
|
|
429
443
|
}else{
|
|
@@ -443,26 +457,33 @@
|
|
|
443
457
|
}
|
|
444
458
|
|
|
445
459
|
function refresh(){
|
|
460
|
+
if(destroyed){
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
446
463
|
syncBrand();
|
|
447
464
|
syncStatus();
|
|
448
465
|
updateCurrent();
|
|
449
466
|
return host;
|
|
450
467
|
}
|
|
451
468
|
|
|
469
|
+
function destroy(){
|
|
470
|
+
if(destroyed){
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
destroyed=true;
|
|
474
|
+
lifecycleController.abort();
|
|
475
|
+
observer.disconnect();
|
|
476
|
+
events.dispose();
|
|
477
|
+
host.ready=false;
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
|
|
452
481
|
syncBrand();
|
|
453
482
|
syncStatus();
|
|
454
483
|
readNavigationAttribute();
|
|
455
484
|
updateCurrent();
|
|
456
485
|
|
|
457
486
|
host.ready=true;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
'app-bar-ready',
|
|
461
|
-
{
|
|
462
|
-
detail:{appBar:host},
|
|
463
|
-
bubbles:true,
|
|
464
|
-
composed:true
|
|
465
|
-
}
|
|
466
|
-
)
|
|
467
|
-
);
|
|
487
|
+
const readyPublication=events.dispatch('app-bar-ready',{appBar:host},{publicDetail:{ready:true}});
|
|
488
|
+
projectArcaneDOMEvent(host,readyPublication.occurrence,{bubbles:true,composed:true});
|
|
468
489
|
</script>
|
|
@@ -300,6 +300,9 @@
|
|
|
300
300
|
<script type="module">
|
|
301
301
|
const host=this;
|
|
302
302
|
const root=host.shadowRoot;
|
|
303
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
304
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.assistant-panel',eventTypes:['assistant-opened','assistant-closed','assistant-send','assistant-clear','assistant-ready']});
|
|
305
|
+
const lifecycleController=new AbortController();
|
|
303
306
|
const shell=root.querySelector('.assistant-shell');
|
|
304
307
|
const panel=root.querySelector('.panel');
|
|
305
308
|
const stateRegion=root.querySelector('.state');
|
|
@@ -311,10 +314,18 @@
|
|
|
311
314
|
const backdrop=root.querySelector('.backdrop');
|
|
312
315
|
const validStates=new Set(['ready','empty','pending','streaming','error']);
|
|
313
316
|
const handledSubmits=new WeakSet();
|
|
314
|
-
const pendingDisabledState=new
|
|
317
|
+
const pendingDisabledState=new Map();
|
|
315
318
|
|
|
316
319
|
let previousFocus=null;
|
|
317
320
|
let opened=host.hasAttribute('data-open');
|
|
321
|
+
let operationSequence=0;
|
|
322
|
+
let destroyed=false;
|
|
323
|
+
const reservedProjectionKeys=Object.freeze([
|
|
324
|
+
'occurrenceId',
|
|
325
|
+
'arcaneSource',
|
|
326
|
+
'instanceId',
|
|
327
|
+
'operationId'
|
|
328
|
+
]);
|
|
318
329
|
|
|
319
330
|
host.open=open;
|
|
320
331
|
host.close=close;
|
|
@@ -324,6 +335,29 @@
|
|
|
324
335
|
host.setState=setState;
|
|
325
336
|
host.focusComposer=focusComposer;
|
|
326
337
|
host.scrollToEnd=scrollToEnd;
|
|
338
|
+
host.destroy=destroy;
|
|
339
|
+
|
|
340
|
+
function nextOperationId(kind){
|
|
341
|
+
operationSequence+=1;
|
|
342
|
+
return `${events.descriptor.instanceId}:${kind}:${operationSequence}`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function compatibilityDetail(input={}){
|
|
346
|
+
const detail={...input};
|
|
347
|
+
for(const key of reservedProjectionKeys){
|
|
348
|
+
delete detail[key];
|
|
349
|
+
}
|
|
350
|
+
return detail;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function emit(type,detail={},publicDetail={},options={}){
|
|
354
|
+
if(destroyed){
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
const publication=events.dispatch(type,detail,{publicDetail,operationId:options.operationId||null,cancelable:Boolean(options.cancelable)});
|
|
358
|
+
if(options.cancelable&&!publication.accepted) return false;
|
|
359
|
+
return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true,cancelable:Boolean(options.cancelable)});
|
|
360
|
+
}
|
|
327
361
|
|
|
328
362
|
Object.defineProperties(
|
|
329
363
|
host,
|
|
@@ -349,15 +383,15 @@
|
|
|
349
383
|
}
|
|
350
384
|
);
|
|
351
385
|
|
|
352
|
-
closeButton.addEventListener('click',()=>close());
|
|
353
|
-
clearButton.addEventListener('click',()=>clear());
|
|
354
|
-
backdrop.addEventListener('click',()=>close());
|
|
355
|
-
root.addEventListener('submit',onComposerSubmit);
|
|
356
|
-
host.addEventListener('submit',onComposerSubmit);
|
|
357
|
-
host.addEventListener('click',onHostAction);
|
|
358
|
-
document.addEventListener('keydown',onDocumentKeydown);
|
|
359
|
-
messagesSlot.addEventListener('slotchange',syncState);
|
|
360
|
-
composerSlot.addEventListener('slotchange',syncComposerState);
|
|
386
|
+
closeButton.addEventListener('click',()=>close(),{signal:lifecycleController.signal});
|
|
387
|
+
clearButton.addEventListener('click',()=>clear(),{signal:lifecycleController.signal});
|
|
388
|
+
backdrop.addEventListener('click',()=>close(),{signal:lifecycleController.signal});
|
|
389
|
+
root.addEventListener('submit',onComposerSubmit,{signal:lifecycleController.signal});
|
|
390
|
+
host.addEventListener('submit',onComposerSubmit,{signal:lifecycleController.signal});
|
|
391
|
+
host.addEventListener('click',onHostAction,{signal:lifecycleController.signal});
|
|
392
|
+
document.addEventListener('keydown',onDocumentKeydown,{signal:lifecycleController.signal});
|
|
393
|
+
messagesSlot.addEventListener('slotchange',syncState,{signal:lifecycleController.signal});
|
|
394
|
+
composerSlot.addEventListener('slotchange',syncComposerState,{signal:lifecycleController.signal});
|
|
361
395
|
|
|
362
396
|
const observer=new MutationObserver(
|
|
363
397
|
records=>{
|
|
@@ -400,6 +434,9 @@
|
|
|
400
434
|
}
|
|
401
435
|
|
|
402
436
|
function setOpened(next,options={}){
|
|
437
|
+
if(destroyed){
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
403
440
|
const changed=next!==opened;
|
|
404
441
|
|
|
405
442
|
if(next&&changed){
|
|
@@ -415,16 +452,7 @@
|
|
|
415
452
|
return false;
|
|
416
453
|
}
|
|
417
454
|
|
|
418
|
-
host.
|
|
419
|
-
new CustomEvent(
|
|
420
|
-
next?'assistant-opened':'assistant-closed',
|
|
421
|
-
{
|
|
422
|
-
detail:{assistant:host,layout:host.layout},
|
|
423
|
-
bubbles:true,
|
|
424
|
-
composed:true
|
|
425
|
-
}
|
|
426
|
-
)
|
|
427
|
-
);
|
|
455
|
+
emit(next?'assistant-opened':'assistant-closed',{assistant:host,layout:host.layout},{opened:next,layout:host.layout});
|
|
428
456
|
|
|
429
457
|
if(next&&options.focus!==false){
|
|
430
458
|
requestAnimationFrame(()=>focusComposer());
|
|
@@ -503,6 +531,9 @@
|
|
|
503
531
|
}
|
|
504
532
|
|
|
505
533
|
function setState(next='ready',message=''){
|
|
534
|
+
if(destroyed){
|
|
535
|
+
return shell.dataset.state;
|
|
536
|
+
}
|
|
506
537
|
if(next==='auto'||next===null){
|
|
507
538
|
host.removeAttribute('data-state');
|
|
508
539
|
host.removeAttribute('data-state-message');
|
|
@@ -523,16 +554,25 @@
|
|
|
523
554
|
function syncComposerState(){
|
|
524
555
|
const disabled=shell.dataset.state==='pending';
|
|
525
556
|
const controls=composerControls();
|
|
557
|
+
const currentControls=new Set(controls);
|
|
558
|
+
for(const [control,original] of pendingDisabledState){
|
|
559
|
+
if(!disabled||!currentControls.has(control)){
|
|
560
|
+
restoreComposerControl(control,original);
|
|
561
|
+
pendingDisabledState.delete(control);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
526
564
|
controls.forEach(control=>{
|
|
527
565
|
if(control.matches('button,input,textarea,select')){
|
|
528
566
|
if(disabled&&!pendingDisabledState.has(control)){
|
|
529
|
-
pendingDisabledState.set(
|
|
567
|
+
pendingDisabledState.set(
|
|
568
|
+
control,
|
|
569
|
+
{
|
|
570
|
+
disabled:control.disabled,
|
|
571
|
+
ariaDisabled:control.getAttribute('aria-disabled')
|
|
572
|
+
}
|
|
573
|
+
);
|
|
530
574
|
control.disabled=true;
|
|
531
575
|
control.setAttribute('aria-disabled','true');
|
|
532
|
-
}else if(!disabled&&pendingDisabledState.has(control)){
|
|
533
|
-
control.disabled=pendingDisabledState.get(control);
|
|
534
|
-
control.toggleAttribute('aria-disabled',control.disabled);
|
|
535
|
-
pendingDisabledState.delete(control);
|
|
536
576
|
}
|
|
537
577
|
}
|
|
538
578
|
});
|
|
@@ -551,7 +591,26 @@
|
|
|
551
591
|
return controls;
|
|
552
592
|
}
|
|
553
593
|
|
|
594
|
+
function restoreComposerControl(control,original){
|
|
595
|
+
control.disabled=original.disabled;
|
|
596
|
+
if(original.ariaDisabled===null){
|
|
597
|
+
control.removeAttribute('aria-disabled');
|
|
598
|
+
}else{
|
|
599
|
+
control.setAttribute('aria-disabled',original.ariaDisabled);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function restoreComposerControls(){
|
|
604
|
+
for(const [control,original] of pendingDisabledState){
|
|
605
|
+
restoreComposerControl(control,original);
|
|
606
|
+
}
|
|
607
|
+
pendingDisabledState.clear();
|
|
608
|
+
}
|
|
609
|
+
|
|
554
610
|
function focusComposer(){
|
|
611
|
+
if(destroyed){
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
555
614
|
const controls=composerControls();
|
|
556
615
|
const target=controls.find(
|
|
557
616
|
element=>element.matches?.('textarea,input:not([type="hidden"]),[contenteditable="true"]')&&!element.disabled
|
|
@@ -562,6 +621,9 @@
|
|
|
562
621
|
}
|
|
563
622
|
|
|
564
623
|
function scrollToEnd(options={}){
|
|
624
|
+
if(destroyed){
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
565
627
|
const messages=root.querySelector('.messages');
|
|
566
628
|
messages.scrollTo({
|
|
567
629
|
top:messages.scrollHeight,
|
|
@@ -595,37 +657,23 @@
|
|
|
595
657
|
}
|
|
596
658
|
|
|
597
659
|
function send(message='',context={}){
|
|
660
|
+
if(destroyed){
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
598
663
|
const normalized=String(message??'').trim();
|
|
599
664
|
|
|
600
665
|
if(!normalized){
|
|
601
666
|
return false;
|
|
602
667
|
}
|
|
603
668
|
|
|
604
|
-
return host.
|
|
605
|
-
new CustomEvent(
|
|
606
|
-
'assistant-send',
|
|
607
|
-
{
|
|
608
|
-
detail:{...context,message:normalized,assistant:host},
|
|
609
|
-
bubbles:true,
|
|
610
|
-
composed:true,
|
|
611
|
-
cancelable:true
|
|
612
|
-
}
|
|
613
|
-
)
|
|
614
|
-
);
|
|
669
|
+
return emit('assistant-send',{...compatibilityDetail(context),message:normalized,assistant:host},{messageLength:normalized.length,layout:host.layout},{cancelable:true,operationId:nextOperationId('send')});
|
|
615
670
|
}
|
|
616
671
|
|
|
617
672
|
function clear(detail={}){
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
detail:{...detail,assistant:host},
|
|
623
|
-
bubbles:true,
|
|
624
|
-
composed:true,
|
|
625
|
-
cancelable:true
|
|
626
|
-
}
|
|
627
|
-
)
|
|
628
|
-
);
|
|
673
|
+
if(destroyed){
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
return emit('assistant-clear',{...compatibilityDetail(detail),assistant:host},{layout:host.layout},{cancelable:true,operationId:nextOperationId('clear')});
|
|
629
677
|
}
|
|
630
678
|
|
|
631
679
|
function onHostAction(event){
|
|
@@ -695,6 +743,20 @@
|
|
|
695
743
|
}
|
|
696
744
|
}
|
|
697
745
|
|
|
746
|
+
function destroy(){
|
|
747
|
+
if(destroyed){
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
restoreComposerControls();
|
|
751
|
+
destroyed=true;
|
|
752
|
+
lifecycleController.abort();
|
|
753
|
+
observer.disconnect();
|
|
754
|
+
returnFocus();
|
|
755
|
+
events.dispose();
|
|
756
|
+
host.ready=false;
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
|
|
698
760
|
syncLabels();
|
|
699
761
|
syncLayout();
|
|
700
762
|
syncOpenedState();
|
|
@@ -702,14 +764,5 @@
|
|
|
702
764
|
syncComposerState();
|
|
703
765
|
|
|
704
766
|
host.ready=true;
|
|
705
|
-
host
|
|
706
|
-
new CustomEvent(
|
|
707
|
-
'assistant-ready',
|
|
708
|
-
{
|
|
709
|
-
detail:{assistant:host},
|
|
710
|
-
bubbles:true,
|
|
711
|
-
composed:true
|
|
712
|
-
}
|
|
713
|
-
)
|
|
714
|
-
);
|
|
767
|
+
emit('assistant-ready',{assistant:host},{ready:true});
|
|
715
768
|
</script>
|
|
@@ -1,8 +1,11 @@
|
|
|
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;max-width:34rem}.calculator{display:grid;gap:var(--arcane-space-3);padding:var(--arcane-space-4)}.display{display:grid;gap:var(--arcane-space-2)}.display input{font:600 1.3rem/1.4 ui-monospace,monospace;text-align:right}.result{min-height:2.5rem;margin:0;text-align:right;font-size:1.8rem;font-weight:750;overflow-wrap:anywhere}.keys{display:grid;gap:var(--arcane-space-2);grid-template-columns:repeat(5,1fr)}.keys button{min-width:0;padding:.7rem .35rem}.status{min-height:1.4rem;margin:0;color:var(--arcane-muted)}@media(max-width:28rem){.keys{grid-template-columns:repeat(4,1fr)}}</style>
|
|
3
3
|
<section class="calculator arcane-card"><div class="display arcane-field"><label for="expression">Expression</label><input id="expression" inputmode="decimal" autocomplete="off" spellcheck="false" value=""><p id="result" class="result" aria-live="polite">0</p></div><div id="keys" class="keys" aria-label="Calculator keypad"></div><p id="status" class="status" role="status"></p></section>
|
|
4
|
-
<script type="module">const host=this,root=host.shadowRoot,input=root.querySelector('#expression'),result=root.querySelector('#result'),status=root.querySelector('#status'),keys=root.querySelector('#keys');const componentURL=new URL(host.getAttribute('href')||'./arcane/components/calculator.html',document.baseURI);const {default:CalculatorEngine}=await import(new URL('../modules/CalculatorEngine.js',componentURL));const engine=new CalculatorEngine(),definitions=['7','8','9','/','sqrt(','4','5','6','*','^','1','2','3','-','%','0','.','(',')','+','pi','e','C','⌫','='];
|
|
4
|
+
<script type="module">const host=this,root=host.shadowRoot,input=root.querySelector('#expression'),result=root.querySelector('#result'),status=root.querySelector('#status'),keys=root.querySelector('#keys');const componentURL=new URL(host.getAttribute('href')||'./arcane/components/calculator.html',document.baseURI);const {default:CalculatorEngine}=await import(new URL('../modules/CalculatorEngine.js',componentURL));const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');const events=createArcaneEventSource(host,{source:'arcane.component.calculator',eventTypes:['calculation-complete','calculation-error','calculator-ready']}),lifecycleController=new AbortController();const engine=new CalculatorEngine(),definitions=['7','8','9','/','sqrt(','4','5','6','*','^','1','2','3','-','%','0','.','(',')','+','pi','e','C','⌫','='];let destroyed=false,operationSequence=0;
|
|
5
5
|
for(const value of definitions){const button=document.createElement('button');button.type='button';button.className=value==='='?'arcane-button':'arcane-button arcane-button--secondary';button.textContent=value;button.dataset.value=value;keys.append(button)}
|
|
6
|
-
keys.addEventListener('click',event=>{const value=event.target.closest('[data-value]')?.dataset.value;if(!value)return;if(value==='C'){input.value='';result.textContent='0';status.textContent='';return}if(value==='⌫'){input.value=input.value.slice(0,-1);return}if(value==='='){calculate();return}input.value+=value;input.focus()});input.addEventListener('keydown',event=>{if(event.key==='Enter'){event.preventDefault();calculate()}});
|
|
7
|
-
function
|
|
8
|
-
|
|
6
|
+
keys.addEventListener('click',event=>{const value=event.target.closest('[data-value]')?.dataset.value;if(!value)return;if(value==='C'){input.value='';result.textContent='0';status.textContent='';return}if(value==='⌫'){input.value=input.value.slice(0,-1);return}if(value==='='){calculate();return}input.value+=value;input.focus()},{signal:lifecycleController.signal});input.addEventListener('keydown',event=>{if(event.key==='Enter'){event.preventDefault();calculate()}},{signal:lifecycleController.signal});
|
|
7
|
+
function nextOperationId(){operationSequence+=1;return `${events.descriptor.instanceId}:calculate:${operationSequence}`}
|
|
8
|
+
function emit(type,detail={},publicDetail={},operationId=null){if(destroyed)return false;const publication=events.dispatch(type,detail,{publicDetail,operationId});return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true})}
|
|
9
|
+
function calculate(){if(destroyed)return false;const operationId=nextOperationId();try{const calculation=engine.calculate(input.value);result.textContent=new Intl.NumberFormat(undefined,{maximumSignificantDigits:14}).format(calculation.result);status.textContent='';emit('calculation-complete',{calculation},{succeeded:true},operationId)}catch(error){const code=String(error?.code||'ARCANE_CALCULATOR_EXPRESSION_EVALUATION_FAILED'),reason='expression-evaluation-failed';status.textContent=error.message;emit('calculation-error',{error,expression:input.value,code,reason},{code,reason,expressionLength:input.value.length},operationId)}}
|
|
10
|
+
function destroy(){if(destroyed)return false;destroyed=true;lifecycleController.abort('calculator-destroyed');engine.destroy();events.dispose();host.ready=false;return true}
|
|
11
|
+
host.calculate=calculate;host.destroy=destroy;host.ready=true;emit('calculator-ready',{}, {ready:true});</script>
|
|
@@ -172,6 +172,9 @@
|
|
|
172
172
|
normalizeChartOptions,
|
|
173
173
|
normalizeChartRows
|
|
174
174
|
}=await import(moduleURL('ComponentContracts.js'));
|
|
175
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
176
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.chart',eventTypes:['chart-remove','chart-ready']});
|
|
177
|
+
const lifecycleController=new AbortController();
|
|
175
178
|
const title=host.shadowRoot.querySelector('#chartTitle');
|
|
176
179
|
const info=host.shadowRoot.querySelector('#chartInfo');
|
|
177
180
|
const plot=host.shadowRoot.querySelector('#plot');
|
|
@@ -190,6 +193,8 @@
|
|
|
190
193
|
let selectedIndex=-1;
|
|
191
194
|
let resizeFrame=0;
|
|
192
195
|
let renderGeneration=0;
|
|
196
|
+
let operationSequence=0;
|
|
197
|
+
let destroyed=false;
|
|
193
198
|
let options=normalizeChartOptions(optionsFromDataset());
|
|
194
199
|
|
|
195
200
|
host.addData=addData;
|
|
@@ -203,18 +208,22 @@
|
|
|
203
208
|
host,
|
|
204
209
|
{
|
|
205
210
|
chartStyle:{
|
|
211
|
+
configurable:true,
|
|
206
212
|
get:()=>options.style,
|
|
207
213
|
set:value=>void configure({style:value})
|
|
208
214
|
},
|
|
209
215
|
chartTitle:{
|
|
216
|
+
configurable:true,
|
|
210
217
|
get:()=>options.title,
|
|
211
218
|
set:value=>void configure({title:value})
|
|
212
219
|
},
|
|
213
220
|
dataSet:{
|
|
221
|
+
configurable:true,
|
|
214
222
|
get:()=>rows.map(row=>row.slice()),
|
|
215
223
|
set:value=>void update(value)
|
|
216
224
|
},
|
|
217
225
|
options:{
|
|
226
|
+
configurable:true,
|
|
218
227
|
get:getOptions,
|
|
219
228
|
set:value=>void configure(value)
|
|
220
229
|
}
|
|
@@ -223,21 +232,22 @@
|
|
|
223
232
|
|
|
224
233
|
removeButton.addEventListener(
|
|
225
234
|
'click',
|
|
226
|
-
()=>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
235
|
+
()=>{
|
|
236
|
+
const operationId=`${events.descriptor.instanceId}:remove:${++operationSequence}`;
|
|
237
|
+
const publication=events.dispatch('chart-remove',{key:options.key},{operationId,publicDetail:{key:options.key}});
|
|
238
|
+
projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true});
|
|
239
|
+
},
|
|
240
|
+
{signal:lifecycleController.signal}
|
|
241
|
+
);
|
|
242
|
+
plot.addEventListener(
|
|
243
|
+
'keydown',
|
|
244
|
+
navigatePoints,
|
|
245
|
+
{signal:lifecycleController.signal}
|
|
236
246
|
);
|
|
237
|
-
plot.addEventListener('keydown',navigatePoints);
|
|
238
247
|
plot.addEventListener(
|
|
239
248
|
'pointerleave',
|
|
240
|
-
()=>updateSelectedValue(selectedIndex,'selected')
|
|
249
|
+
()=>updateSelectedValue(selectedIndex,'selected'),
|
|
250
|
+
{signal:lifecycleController.signal}
|
|
241
251
|
);
|
|
242
252
|
|
|
243
253
|
const resizeObserver=typeof ResizeObserver==='function'
|
|
@@ -286,6 +296,9 @@
|
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
async function configure(input={}){
|
|
299
|
+
if(destroyed){
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
289
302
|
const suppliedData=Object.prototype.hasOwnProperty.call(input||{},'data')
|
|
290
303
|
||Object.prototype.hasOwnProperty.call(input||{},'dataset')
|
|
291
304
|
||Object.prototype.hasOwnProperty.call(input||{},'datasets');
|
|
@@ -302,18 +315,27 @@
|
|
|
302
315
|
}
|
|
303
316
|
|
|
304
317
|
async function populate(data=[],input={}){
|
|
318
|
+
if(destroyed){
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
305
321
|
options=normalizeChartOptions(input,options);
|
|
306
322
|
renderOptions();
|
|
307
323
|
return update(data);
|
|
308
324
|
}
|
|
309
325
|
|
|
310
326
|
async function update(data=[]){
|
|
327
|
+
if(destroyed){
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
311
330
|
rows=normalizeChartRows(data,options);
|
|
312
331
|
options={...options,data};
|
|
313
332
|
return renderRows();
|
|
314
333
|
}
|
|
315
334
|
|
|
316
335
|
async function renderRows(){
|
|
336
|
+
if(destroyed){
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
317
339
|
const generation=++renderGeneration;
|
|
318
340
|
selectedIndex=rows.length-1;
|
|
319
341
|
|
|
@@ -329,11 +351,20 @@
|
|
|
329
351
|
plot.classList.remove('hidden');
|
|
330
352
|
chartValue.classList.remove('hidden');
|
|
331
353
|
empty.classList.add('hidden');
|
|
332
|
-
|
|
354
|
+
let library;
|
|
355
|
+
try{
|
|
356
|
+
library=uPlot||await loadChartLibrary();
|
|
357
|
+
}catch(error){
|
|
358
|
+
if(destroyed||generation!==renderGeneration){
|
|
359
|
+
return host.dataSet;
|
|
360
|
+
}
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
333
363
|
|
|
334
|
-
if(generation!==renderGeneration){
|
|
364
|
+
if(destroyed||generation!==renderGeneration){
|
|
335
365
|
return host.dataSet;
|
|
336
366
|
}
|
|
367
|
+
uPlot=library;
|
|
337
368
|
if(chart){
|
|
338
369
|
chart.setData(toChartData(rows));
|
|
339
370
|
updateSelectedValue(selectedIndex,'latest');
|
|
@@ -344,6 +375,9 @@
|
|
|
344
375
|
}
|
|
345
376
|
|
|
346
377
|
async function addData(data=[]){
|
|
378
|
+
if(destroyed){
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
347
381
|
const additions=normalizeChartRows(data,options);
|
|
348
382
|
const merged=new Map(rows.concat(additions).map(row=>[row[0],row]));
|
|
349
383
|
rows=[...merged.values()].sort((left,right)=>left[0]-right[0]);
|
|
@@ -633,6 +667,11 @@
|
|
|
633
667
|
}
|
|
634
668
|
|
|
635
669
|
function destroy(){
|
|
670
|
+
if(destroyed){
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
destroyed=true;
|
|
674
|
+
lifecycleController.abort();
|
|
636
675
|
renderGeneration++;
|
|
637
676
|
resizeObserver?.disconnect();
|
|
638
677
|
if(!resizeObserver){
|
|
@@ -644,12 +683,14 @@
|
|
|
644
683
|
}
|
|
645
684
|
destroyChart();
|
|
646
685
|
rows=[];
|
|
686
|
+
events.dispose();
|
|
687
|
+
host.ready=false;
|
|
688
|
+
return true;
|
|
647
689
|
}
|
|
648
690
|
|
|
649
691
|
renderOptions();
|
|
650
692
|
updateSelectedValue(-1);
|
|
651
693
|
host.ready=true;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
);
|
|
694
|
+
const readyPublication=events.dispatch('chart-ready',{}, {publicDetail:{ready:true}});
|
|
695
|
+
projectArcaneDOMEvent(host,readyPublication.occurrence,{bubbles:true,composed:true});
|
|
655
696
|
</script>
|