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
|
@@ -4,10 +4,17 @@
|
|
|
4
4
|
</style>
|
|
5
5
|
<section class="shell"><header id="heading" class="heading" hidden><div><h2 id="title"></h2><p id="participants"></p></div><span id="badge" class="badge"></span></header><div id="messages" class="messages"><div class="blank"><div><strong>Select a conversation</strong><p>Choose a thread from the inbox, or connect a service in settings.</p></div></div></div><form id="composer" class="composer" hidden><label class="arcane-field"><span class="sr-only">Reply</span><textarea id="body" required placeholder="Write a reply"></textarea></label><div class="composer-row"><p id="status" class="status" role="status" aria-live="polite"></p><button class="arcane-button" type="submit">Send</button></div></form></section>
|
|
6
6
|
<script type="module">
|
|
7
|
-
const host=this,root=host.shadowRoot,heading=root.querySelector('#heading'),messages=root.querySelector('#messages'),composer=root.querySelector('#composer'),body=root.querySelector('#body'),status=root.querySelector('#status');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const host=this,root=host.shadowRoot,heading=root.querySelector('#heading'),messages=root.querySelector('#messages'),composer=root.querySelector('#composer'),body=root.querySelector('#body'),status=root.querySelector('#status');
|
|
8
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
9
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.conversation-view',eventTypes:['communication-send','communication-advisory-action','conversation-view-ready']});
|
|
10
|
+
const lifecycleController=new AbortController();
|
|
11
|
+
let thread=null,advisories={},destroyed=false,operationSequence=0;
|
|
12
|
+
function nextOperationId(kind){operationSequence+=1;return `${events.descriptor.instanceId}:${kind}:${operationSequence}`}
|
|
13
|
+
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})}
|
|
14
|
+
host.setConversation=(next,items=[],options={})=>{if(destroyed)return false;thread=next||null;advisories=options?.advisories instanceof Map?options.advisories:new Map();heading.hidden=!thread;composer.hidden=!thread;if(!thread){messages.innerHTML='<div class="blank"><div><strong>Select a conversation</strong><p>Choose a thread from the inbox, or connect a service in settings.</p></div></div>';return}root.querySelector('#title').textContent=thread.title;root.querySelector('#participants').textContent=(thread.participants||[]).join(', ');root.querySelector('#badge').textContent=`${thread.channel} · ${thread.providerId}`;render(items)};
|
|
15
|
+
host.setBusy=value=>{if(destroyed)return false;for(const control of composer.elements) control.disabled=Boolean(value)};host.setStatus=value=>{if(destroyed)return false;status.textContent=String(value||'')};
|
|
16
|
+
composer.addEventListener('submit',event=>{event.preventDefault();const value=body.value.trim();if(!value)return;emit('communication-send',{thread,body:value},{hasThread:Boolean(thread),bodyLength:value.length},nextOperationId('send'))},{signal:lifecycleController.signal});
|
|
17
|
+
function render(items){messages.replaceChildren(...Array.from(items||[]).map(item=>{const article=document.createElement('article'),text=document.createElement('p'),footer=document.createElement('footer'),time=document.createElement('time'),state=document.createElement('span');article.className=`message ${item.direction}`;text.textContent=item.body||item.subject||'';time.dateTime=item.timestamp;time.textContent=new Intl.DateTimeFormat(undefined,{dateStyle:'short',timeStyle:'short'}).format(new Date(item.timestamp));state.textContent=item.status;footer.append(time,state);article.append(text,footer);const advisory=advisories.get(item);if(advisory){const note=document.createElement('aside'),title=document.createElement('strong'),summary=document.createElement('p');note.className='advisory';note.dataset.level=String(advisory.level||'caution');note.setAttribute('role','note');title.textContent=String(advisory.title||'Content advisory');summary.textContent=String(advisory.summary||'Review this message carefully.');note.append(title,summary);const signals=Array.from(advisory.signals||[]).map(String).filter(Boolean).slice(0,8);if(signals.length){const list=document.createElement('ul');for(const signal of signals){const entry=document.createElement('li');entry.textContent=signal;list.append(entry)}note.append(list)}if(advisory.actionLabel){const action=document.createElement('button');action.type='button';action.className='arcane-button arcane-button--tertiary';action.textContent=String(advisory.actionLabel);action.addEventListener('click',()=>emit('communication-advisory-action',{thread,message:item,advisory},{hasThread:Boolean(thread),level:String(advisory.level||'caution')},nextOperationId('advisory-action')),{signal:lifecycleController.signal});note.append(action)}article.append(note)}return article}));messages.scrollTop=messages.scrollHeight;}
|
|
18
|
+
function destroy(){if(destroyed)return false;destroyed=true;lifecycleController.abort();thread=null;advisories={};events.dispose();host.ready=false;return true}
|
|
19
|
+
host.clearComposer=()=>{if(destroyed)return false;body.value='';body.focus()};host.destroy=destroy;host.ready=true;emit('conversation-view-ready',{}, {ready:true});
|
|
13
20
|
</script>
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
normalizeDashboardOptions,
|
|
24
24
|
normalizeDashboardVisibility
|
|
25
25
|
}=await import(moduleURL('ComponentContracts.js'));
|
|
26
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
27
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.dashboard-config',eventTypes:['dashboard-config-closed','dashboard-config-opened','dashboard-config-change','dashboard-config-ready']});
|
|
28
|
+
const lifecycleController=new AbortController();
|
|
26
29
|
const openButton=host.shadowRoot.querySelector('#openConfig');
|
|
27
30
|
const configModal=host.shadowRoot.querySelector('#configModal');
|
|
28
31
|
|
|
@@ -30,9 +33,22 @@
|
|
|
30
33
|
let visibility={};
|
|
31
34
|
let content=null;
|
|
32
35
|
let labels=normalizeDashboardOptions(optionsFromDataset()).labels;
|
|
36
|
+
let operationSequence=0;
|
|
37
|
+
let operationGeneration=0;
|
|
38
|
+
let destroyed=false;
|
|
39
|
+
|
|
40
|
+
function emit(type,detail={},publicDetail={},options={}){
|
|
41
|
+
if(destroyed){
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const publication=events.dispatch(type,detail,{publicDetail,operationId:options.operationId||null,cancelable:Boolean(options.cancelable)});
|
|
45
|
+
if(options.cancelable&&!publication.accepted) return false;
|
|
46
|
+
return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true,cancelable:Boolean(options.cancelable)});
|
|
47
|
+
}
|
|
33
48
|
|
|
34
49
|
host.close=closeConfig;
|
|
35
50
|
host.configure=configure;
|
|
51
|
+
host.destroy=destroy;
|
|
36
52
|
host.getChartOptions=getChartOptions;
|
|
37
53
|
host.getEffectiveVisibility=getEffectiveVisibility;
|
|
38
54
|
host.open=openConfig;
|
|
@@ -42,34 +58,33 @@
|
|
|
42
58
|
Object.defineProperties(
|
|
43
59
|
host,
|
|
44
60
|
{
|
|
45
|
-
definitions:{get:getDefinitions,set:setDefinitions},
|
|
46
|
-
effectiveVisibility:{get:getEffectiveVisibility},
|
|
47
|
-
options:{get:getOptions,set:configure},
|
|
48
|
-
visibility:{get:getVisibility,set:setVisibility}
|
|
61
|
+
definitions:{configurable:true,get:getDefinitions,set:setDefinitions},
|
|
62
|
+
effectiveVisibility:{configurable:true,get:getEffectiveVisibility},
|
|
63
|
+
options:{configurable:true,get:getOptions,set:configure},
|
|
64
|
+
visibility:{configurable:true,get:getVisibility,set:setVisibility}
|
|
49
65
|
}
|
|
50
66
|
);
|
|
51
67
|
|
|
52
68
|
openButton.addEventListener(
|
|
53
69
|
'click',
|
|
54
70
|
()=>openConfig().catch(
|
|
55
|
-
error=>
|
|
56
|
-
|
|
71
|
+
error=>{
|
|
72
|
+
if(!destroyed){
|
|
73
|
+
console.error('Unable to configure dashboard:',error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
{signal:lifecycleController.signal}
|
|
57
78
|
);
|
|
58
79
|
configModal.addEventListener(
|
|
59
80
|
'modal-closed',
|
|
60
|
-
()=>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
effectiveVisibility:getEffectiveVisibility(),
|
|
68
|
-
visibility:getVisibility()
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
)
|
|
72
|
-
)
|
|
81
|
+
()=>emit(
|
|
82
|
+
'dashboard-config-closed',
|
|
83
|
+
{effectiveVisibility:getEffectiveVisibility(),visibility:getVisibility()},
|
|
84
|
+
{definitionCount:definitions.length},
|
|
85
|
+
{operationId:`${events.descriptor.instanceId}:close:${++operationSequence}`}
|
|
86
|
+
),
|
|
87
|
+
{signal:lifecycleController.signal}
|
|
73
88
|
);
|
|
74
89
|
|
|
75
90
|
function optionsFromDataset(){
|
|
@@ -84,6 +99,9 @@
|
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
function configure(input={}){
|
|
102
|
+
if(destroyed){
|
|
103
|
+
return getOptions();
|
|
104
|
+
}
|
|
87
105
|
const normalized=normalizeDashboardOptions(
|
|
88
106
|
input,
|
|
89
107
|
{definitions,labels,visibility}
|
|
@@ -97,6 +115,9 @@
|
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
function setDefinitions(values=[]){
|
|
118
|
+
if(destroyed){
|
|
119
|
+
return getDefinitions();
|
|
120
|
+
}
|
|
100
121
|
definitions=normalizeDashboardDefinitions(values);
|
|
101
122
|
renderTrigger();
|
|
102
123
|
refreshContent();
|
|
@@ -104,6 +125,9 @@
|
|
|
104
125
|
}
|
|
105
126
|
|
|
106
127
|
function setVisibility(values={}){
|
|
128
|
+
if(destroyed){
|
|
129
|
+
return getVisibility();
|
|
130
|
+
}
|
|
107
131
|
visibility=normalizeDashboardVisibility(values);
|
|
108
132
|
syncOptions();
|
|
109
133
|
return getVisibility();
|
|
@@ -143,50 +167,60 @@
|
|
|
143
167
|
}
|
|
144
168
|
|
|
145
169
|
async function openConfig(){
|
|
146
|
-
if(
|
|
170
|
+
if(destroyed||!definitions.length){
|
|
147
171
|
return false;
|
|
148
172
|
}
|
|
173
|
+
const generation=++operationGeneration;
|
|
149
174
|
const modal=await waitForComponent(
|
|
150
175
|
configModal,
|
|
151
176
|
{
|
|
152
177
|
methods:['populate','open'],
|
|
153
178
|
property:'ready',
|
|
154
|
-
event:'modal-ready'
|
|
179
|
+
event:'modal-ready',
|
|
180
|
+
signal:lifecycleController.signal
|
|
155
181
|
}
|
|
156
182
|
);
|
|
183
|
+
if(destroyed||generation!==operationGeneration){
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
157
186
|
if(!content){
|
|
158
187
|
content=createContent();
|
|
159
188
|
await modal.populate(content,false);
|
|
189
|
+
if(destroyed||generation!==operationGeneration){
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
160
192
|
}
|
|
161
193
|
refreshContent();
|
|
162
|
-
modal.open();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
bubbles:true,
|
|
168
|
-
composed:true,
|
|
169
|
-
detail:{
|
|
170
|
-
effectiveVisibility:getEffectiveVisibility(),
|
|
171
|
-
visibility:getVisibility()
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
)
|
|
175
|
-
);
|
|
194
|
+
const opened=await modal.open();
|
|
195
|
+
if(destroyed||generation!==operationGeneration||opened!==true){
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
emit('dashboard-config-opened',{effectiveVisibility:getEffectiveVisibility(),visibility:getVisibility()},{definitionCount:definitions.length},{operationId:`${events.descriptor.instanceId}:open:${++operationSequence}`});
|
|
176
199
|
return true;
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
async function closeConfig(){
|
|
203
|
+
if(destroyed){
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const generation=++operationGeneration;
|
|
180
207
|
const modal=await waitForComponent(
|
|
181
208
|
configModal,
|
|
182
209
|
{
|
|
183
210
|
methods:['close'],
|
|
184
211
|
property:'ready',
|
|
185
|
-
event:'modal-ready'
|
|
212
|
+
event:'modal-ready',
|
|
213
|
+
signal:lifecycleController.signal
|
|
186
214
|
}
|
|
187
215
|
);
|
|
188
|
-
|
|
189
|
-
|
|
216
|
+
if(destroyed||generation!==operationGeneration){
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
const closed=await modal.close();
|
|
220
|
+
if(destroyed||generation!==operationGeneration){
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
return closed===true;
|
|
190
224
|
}
|
|
191
225
|
|
|
192
226
|
function createContent(){
|
|
@@ -205,7 +239,7 @@
|
|
|
205
239
|
empty.dataset.dashboardConfigEmpty='';
|
|
206
240
|
items.className='chart-options';
|
|
207
241
|
items.dataset.dashboardConfigOptions='';
|
|
208
|
-
items.addEventListener('change',changeVisibility);
|
|
242
|
+
items.addEventListener('change',changeVisibility,{signal:lifecycleController.signal});
|
|
209
243
|
section.append(styles,heading,message,empty,items);
|
|
210
244
|
return section;
|
|
211
245
|
}
|
|
@@ -280,22 +314,14 @@
|
|
|
280
314
|
const previous=isVisible(definition);
|
|
281
315
|
const next={...visibility,[definition.key]:checkbox.checked};
|
|
282
316
|
const effective=effectiveDashboardVisibility(definitions,next);
|
|
283
|
-
|
|
317
|
+
operationSequence+=1;
|
|
318
|
+
const accepted=emit(
|
|
284
319
|
'dashboard-config-change',
|
|
285
|
-
{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
composed:true,
|
|
289
|
-
detail:{
|
|
290
|
-
definition:cloneDefinition(definition),
|
|
291
|
-
effectiveVisibility:effective,
|
|
292
|
-
key:definition.key,
|
|
293
|
-
visibility:{...next},
|
|
294
|
-
visible:checkbox.checked
|
|
295
|
-
}
|
|
296
|
-
}
|
|
320
|
+
{definition:cloneDefinition(definition),effectiveVisibility:effective,key:definition.key,visibility:{...next},visible:checkbox.checked},
|
|
321
|
+
{key:definition.key,visible:checkbox.checked},
|
|
322
|
+
{cancelable:true,operationId:`${events.descriptor.instanceId}:change:${operationSequence}`}
|
|
297
323
|
);
|
|
298
|
-
if(!
|
|
324
|
+
if(!accepted){
|
|
299
325
|
checkbox.checked=previous;
|
|
300
326
|
return;
|
|
301
327
|
}
|
|
@@ -330,12 +356,23 @@
|
|
|
330
356
|
};
|
|
331
357
|
}
|
|
332
358
|
|
|
359
|
+
function destroy(){
|
|
360
|
+
if(destroyed){
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
destroyed=true;
|
|
364
|
+
operationGeneration+=1;
|
|
365
|
+
lifecycleController.abort('dashboard-config-destroyed');
|
|
366
|
+
configModal.destroy?.();
|
|
367
|
+
definitions=[];
|
|
368
|
+
visibility={};
|
|
369
|
+
content=null;
|
|
370
|
+
events.dispose();
|
|
371
|
+
host.ready=false;
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
|
|
333
375
|
renderTrigger();
|
|
334
376
|
host.ready=true;
|
|
335
|
-
|
|
336
|
-
new CustomEvent(
|
|
337
|
-
'dashboard-config-ready',
|
|
338
|
-
{bubbles:true,composed:true}
|
|
339
|
-
)
|
|
340
|
-
);
|
|
377
|
+
emit('dashboard-config-ready',{}, {ready:true});
|
|
341
378
|
</script>
|
|
@@ -18,34 +18,62 @@
|
|
|
18
18
|
const maintenanceModal=host.shadowRoot.querySelector('#maintenanceModal');
|
|
19
19
|
const {default:waitForComponent}=await import('../modules/WaitForComponent.js');
|
|
20
20
|
const {clearEmptyChatsAndMemories}=await import('../modules/DataMaintenance.js?v=3');
|
|
21
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
22
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.data-maintenance',eventTypes:['data-maintenance-complete','data-maintenance-ready']});
|
|
23
|
+
const lifecycleController=new AbortController();
|
|
21
24
|
|
|
22
25
|
let content=null;
|
|
26
|
+
let openGeneration=0;
|
|
27
|
+
let clearGeneration=0;
|
|
28
|
+
let operationSequence=0;
|
|
29
|
+
let destroyed=false;
|
|
23
30
|
|
|
24
31
|
host.open=openMaintenance;
|
|
32
|
+
host.destroy=destroy;
|
|
25
33
|
|
|
26
34
|
openButton.addEventListener(
|
|
27
35
|
'click',
|
|
28
36
|
()=>openMaintenance().catch(
|
|
29
|
-
error=>
|
|
30
|
-
|
|
37
|
+
error=>{
|
|
38
|
+
if(!destroyed){
|
|
39
|
+
console.error('Unable to open maintenance:',error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
),
|
|
43
|
+
{signal:lifecycleController.signal}
|
|
31
44
|
);
|
|
32
45
|
|
|
33
46
|
async function openMaintenance(){
|
|
47
|
+
if(destroyed){
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const generation=++openGeneration;
|
|
34
51
|
const modal=await waitForComponent(
|
|
35
52
|
maintenanceModal,
|
|
36
53
|
{
|
|
37
54
|
methods:['populate','open'],
|
|
38
55
|
property:'ready',
|
|
39
|
-
event:'modal-ready'
|
|
56
|
+
event:'modal-ready',
|
|
57
|
+
signal:lifecycleController.signal
|
|
40
58
|
}
|
|
41
59
|
);
|
|
60
|
+
if(destroyed||generation!==openGeneration){
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
42
63
|
|
|
43
64
|
if(!content){
|
|
44
65
|
content=createContent();
|
|
45
66
|
await modal.populate(content,false);
|
|
67
|
+
if(destroyed||generation!==openGeneration){
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
46
70
|
}
|
|
47
71
|
|
|
48
|
-
modal.open();
|
|
72
|
+
const opened=await modal.open();
|
|
73
|
+
if(destroyed||generation!==openGeneration){
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return opened===true;
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
function createContent(){
|
|
@@ -64,11 +92,19 @@
|
|
|
64
92
|
clearButton.addEventListener(
|
|
65
93
|
'click',
|
|
66
94
|
async function clearChats(){
|
|
95
|
+
if(destroyed){
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const generation=++clearGeneration;
|
|
99
|
+
const operationId=`${events.descriptor.instanceId}:clear:${++operationSequence}`;
|
|
67
100
|
clearButton.disabled=true;
|
|
68
101
|
status.innerText='Checking chat logs...';
|
|
69
102
|
|
|
70
103
|
try{
|
|
71
104
|
const result=await clearEmptyChatsAndMemories();
|
|
105
|
+
if(destroyed||generation!==clearGeneration){
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
72
108
|
|
|
73
109
|
if(result.deletedChats||result.deletedMemories){
|
|
74
110
|
status.innerText=`Deleted ${result.deletedChats} empty ${result.deletedChats===1?'chat':'chats'} and ${result.deletedMemories} empty or associated ${result.deletedMemories===1?'memory':'memories'}.`;
|
|
@@ -80,19 +116,25 @@
|
|
|
80
116
|
status.innerText+=` ${result.failed} could not be deleted.`;
|
|
81
117
|
}
|
|
82
118
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
119
|
+
const publication=events.dispatch(
|
|
120
|
+
'data-maintenance-complete',
|
|
121
|
+
result,
|
|
122
|
+
{operationId,publicDetail:{deletedChats:Number(result.deletedChats||0),deletedMemories:Number(result.deletedMemories||0),failed:Number(result.failed||0)}}
|
|
88
123
|
);
|
|
124
|
+
projectArcaneDOMEvent(host,publication.occurrence);
|
|
89
125
|
}catch(error){
|
|
126
|
+
if(destroyed||generation!==clearGeneration){
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
90
129
|
status.innerText='Unable to complete data maintenance.';
|
|
91
130
|
console.error('Unable to clear empty chats and memories:',error);
|
|
92
131
|
}finally{
|
|
93
|
-
|
|
132
|
+
if(!destroyed&&generation===clearGeneration){
|
|
133
|
+
clearButton.disabled=false;
|
|
134
|
+
}
|
|
94
135
|
}
|
|
95
|
-
}
|
|
136
|
+
},
|
|
137
|
+
{signal:lifecycleController.signal}
|
|
96
138
|
);
|
|
97
139
|
|
|
98
140
|
section.append(
|
|
@@ -105,8 +147,21 @@
|
|
|
105
147
|
return section;
|
|
106
148
|
}
|
|
107
149
|
|
|
150
|
+
function destroy(){
|
|
151
|
+
if(destroyed){
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
destroyed=true;
|
|
155
|
+
openGeneration+=1;
|
|
156
|
+
clearGeneration+=1;
|
|
157
|
+
lifecycleController.abort('data-maintenance-destroyed');
|
|
158
|
+
maintenanceModal.destroy?.();
|
|
159
|
+
events.dispose();
|
|
160
|
+
host.ready=false;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
108
164
|
host.ready=true;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
165
|
+
const readyPublication=events.dispatch('data-maintenance-ready',{}, {publicDetail:{ready:true}});
|
|
166
|
+
projectArcaneDOMEvent(host,readyPublication.occurrence);
|
|
112
167
|
</script>
|
|
@@ -18,25 +18,43 @@
|
|
|
18
18
|
const openButton=host.shadowRoot.querySelector('#openData');
|
|
19
19
|
const dataModal=host.shadowRoot.querySelector('#dataModal');
|
|
20
20
|
const {default:waitForComponent}=await import('../modules/WaitForComponent.js');
|
|
21
|
+
const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');
|
|
22
|
+
const events=createArcaneEventSource(host,{source:'arcane.component.data-view',eventTypes:['data-view-ready']});
|
|
23
|
+
const lifecycleController=new AbortController();
|
|
21
24
|
|
|
22
25
|
let content=null;
|
|
23
26
|
let fileManager=null;
|
|
27
|
+
let operationGeneration=0;
|
|
28
|
+
let destroyed=false;
|
|
24
29
|
|
|
25
30
|
host.beforeOpen=typeof host.beforeOpen==='function'
|
|
26
31
|
?host.beforeOpen
|
|
27
32
|
:async function beforeOpen(){};
|
|
28
33
|
host.open=openData;
|
|
34
|
+
host.destroy=destroy;
|
|
29
35
|
|
|
30
36
|
openButton.innerText=host.dataset.button||'View Data';
|
|
31
37
|
openButton.addEventListener(
|
|
32
38
|
'click',
|
|
33
39
|
()=>openData().catch(
|
|
34
|
-
error=>
|
|
35
|
-
|
|
40
|
+
error=>{
|
|
41
|
+
if(!destroyed){
|
|
42
|
+
console.error('Unable to open data view:',error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
{signal:lifecycleController.signal}
|
|
36
47
|
);
|
|
37
48
|
|
|
38
49
|
async function openData(){
|
|
50
|
+
if(destroyed){
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const generation=++operationGeneration;
|
|
39
54
|
await host.beforeOpen();
|
|
55
|
+
if(destroyed||generation!==operationGeneration){
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
40
58
|
|
|
41
59
|
const reload=Boolean(content);
|
|
42
60
|
|
|
@@ -45,9 +63,13 @@
|
|
|
45
63
|
{
|
|
46
64
|
methods:['populate','open'],
|
|
47
65
|
property:'ready',
|
|
48
|
-
event:'modal-ready'
|
|
66
|
+
event:'modal-ready',
|
|
67
|
+
signal:lifecycleController.signal
|
|
49
68
|
}
|
|
50
69
|
);
|
|
70
|
+
if(destroyed||generation!==operationGeneration){
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
51
73
|
|
|
52
74
|
if(!content){
|
|
53
75
|
content=document.createElement('section');
|
|
@@ -67,6 +89,9 @@
|
|
|
67
89
|
|
|
68
90
|
content.append(heading,fileManager);
|
|
69
91
|
await modal.populate(content,false);
|
|
92
|
+
if(destroyed||generation!==operationGeneration){
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
const manager=await waitForComponent(
|
|
@@ -74,19 +99,40 @@
|
|
|
74
99
|
{
|
|
75
100
|
methods:['loadAll'],
|
|
76
101
|
property:'ready',
|
|
77
|
-
event:'file-manager-ready'
|
|
102
|
+
event:'file-manager-ready',
|
|
103
|
+
signal:lifecycleController.signal
|
|
78
104
|
}
|
|
79
105
|
);
|
|
106
|
+
if(destroyed||generation!==operationGeneration){
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
80
109
|
|
|
81
110
|
if(reload){
|
|
82
111
|
await manager.loadAll();
|
|
112
|
+
if(destroyed||generation!==operationGeneration){
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
83
115
|
}
|
|
84
116
|
|
|
85
117
|
modal.open();
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function destroy(){
|
|
122
|
+
if(destroyed){
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
destroyed=true;
|
|
126
|
+
operationGeneration+=1;
|
|
127
|
+
lifecycleController.abort('data-view-destroyed');
|
|
128
|
+
fileManager?.destroy?.();
|
|
129
|
+
dataModal.destroy?.();
|
|
130
|
+
events.dispose();
|
|
131
|
+
host.ready=false;
|
|
132
|
+
return true;
|
|
86
133
|
}
|
|
87
134
|
|
|
88
135
|
host.ready=true;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
136
|
+
const readyPublication=events.dispatch('data-view-ready',{}, {publicDetail:{ready:true}});
|
|
137
|
+
projectArcaneDOMEvent(host,readyPublication.occurrence);
|
|
92
138
|
</script>
|