arcane-os 0.1.2 → 0.2.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 +17 -0
- package/NOTICE +5 -3
- package/README.md +73 -24
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +67 -18
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +16 -5
- package/browser-runtime/ai/browser-kokoro-worker.mjs +3 -0
- package/browser-runtime/ai/browser-speech-artifacts.mjs +1108 -0
- package/browser-runtime/ai/browser-speech-providers.mjs +475 -0
- package/browser-runtime/ai/browser-speech.mjs +9 -0
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1537 -167
- package/browser-runtime/ai/browser-wasm.mjs +46 -1
- package/browser-runtime/ai/browser-whisper-worker.mjs +3 -0
- package/browser-runtime/ai/browser-wllama-runtime.mjs +677 -132
- package/browser-runtime/ai/model-controller.mjs +138 -12
- package/browser-runtime/ai/speech-worker-client.mjs +207 -0
- package/browser-runtime/ai/speech-worker-runtime.mjs +516 -0
- package/browser-runtime/ai/wllama/index.mjs +389 -0
- package/docs/architecture.md +132 -22
- package/docs/reference/README.md +1 -1
- package/docs/reference/ai/browser-wasm.md +101 -42
- package/docs/reference/availability-and-normalization.md +19 -5
- package/docs/reference/behavioral-testing.md +18 -5
- package/docs/reference/cli.md +2 -2
- package/docs/reference/inventory/package-api.json +14 -14
- package/docs/reference/protocols.md +4 -4
- package/docs/reference/sdk-api.md +68 -38
- package/docs/work-amplification.md +8 -4
- package/package.json +7 -3
- package/runtime/ARCANE_RUNTIME_RELEASE.json +50 -20
- package/runtime/arcane/components/chat.html +280 -62
- package/runtime/arcane/components/speech.html +1113 -265
- package/runtime/arcane/entities/Chat.js +246 -43
- package/runtime/arcane/modules/AI.js +713 -162
- package/runtime/arcane/modules/AIProviderRuntime.js +2289 -0
- package/runtime/arcane/modules/AIRuntimeState.js +872 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +293 -27
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +682 -0
- package/runtime/arcane/modules/DocumentLexicalSearch.js +292 -0
- package/runtime/arcane/modules/PersistentAIChatSession.js +268 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +25 -206
- package/schemas/arcane-lock.schema.json +6 -4
- package/src/cli/main.mjs +14 -2
- package/src/constants.mjs +1 -1
- package/src/dev-server.mjs +244 -13
- package/src/import-map.mjs +59 -1
- package/src/packager/core.mjs +2 -2
- package/src/runtime.mjs +14 -4
- package/src/sdk-browser-runtime.mjs +28 -75
- package/src/templates/workspace-template.mjs +4 -4
- package/src/toolchain.mjs +3 -0
- package/src/workspace-runtime.mjs +1 -1
- package/src/workspace.mjs +1 -1
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
<link rel="stylesheet" href="./arcane/css/layout.css?v=3" />
|
|
2
2
|
<style>
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
height: 100%;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
min-height: 0;
|
|
8
|
+
--arcane-chat-gap: var(--arcane-space-3, .75rem);
|
|
9
|
+
--arcane-chat-radius: var(--arcane-radius-large, 1rem);
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
.chat_area {
|
|
4
13
|
display: flex;
|
|
5
14
|
flex-direction: column;
|
|
6
15
|
justify-content: flex-end;
|
|
7
|
-
|
|
8
|
-
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
width: min(100%, 72rem);
|
|
18
|
+
height: 100%;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
margin-inline: auto;
|
|
21
|
+
padding: clamp(.65rem, 1.5vw, 1.25rem);
|
|
22
|
+
gap: var(--arcane-chat-gap);
|
|
9
23
|
overflow: hidden;
|
|
10
24
|
}
|
|
11
25
|
|
|
@@ -114,80 +128,128 @@
|
|
|
114
128
|
border:0;
|
|
115
129
|
}
|
|
116
130
|
|
|
117
|
-
.
|
|
131
|
+
.chat_output {
|
|
118
132
|
flex:1 1 auto;
|
|
133
|
+
box-sizing: border-box;
|
|
134
|
+
width: 100%;
|
|
119
135
|
min-height:0;
|
|
136
|
+
margin: 0;
|
|
137
|
+
padding: clamp(1rem, 2vw, 1.5rem);
|
|
120
138
|
list-style: none;
|
|
121
139
|
overflow-y: auto;
|
|
122
|
-
|
|
140
|
+
scrollbar-gutter: stable;
|
|
141
|
+
border: 1px solid var(--arcane-border, var(--border-color));
|
|
142
|
+
border-radius: var(--arcane-chat-radius);
|
|
143
|
+
background: var(--arcane-surface-raised, var(--modal-background));
|
|
144
|
+
box-shadow: var(--arcane-shadow-medium, 0 .65rem 1.8rem var(--shadow-color));
|
|
123
145
|
}
|
|
124
146
|
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
margin: 0 0 1em;
|
|
147
|
+
.chat_output > li {
|
|
148
|
+
box-sizing: border-box;
|
|
128
149
|
width: auto;
|
|
129
|
-
max-width:
|
|
150
|
+
max-width: min(92%, 48rem);
|
|
151
|
+
margin: 0 0 .85rem;
|
|
152
|
+
padding: .85rem 1rem;
|
|
130
153
|
clear: both;
|
|
154
|
+
border: 1px solid var(--arcane-border, var(--border-color));
|
|
155
|
+
border-radius: calc(var(--arcane-chat-radius) - .1rem);
|
|
156
|
+
overflow-wrap: anywhere;
|
|
131
157
|
}
|
|
132
158
|
|
|
133
|
-
.
|
|
134
|
-
width:
|
|
159
|
+
.chat_output > li.ai {
|
|
160
|
+
width: auto;
|
|
135
161
|
float: left;
|
|
136
|
-
|
|
162
|
+
background: var(--arcane-surface, var(--background));
|
|
163
|
+
border-start-start-radius: var(--arcane-radius-small, .3rem);
|
|
137
164
|
}
|
|
138
165
|
|
|
139
|
-
|
|
140
|
-
.chat_area li.user {
|
|
141
|
-
text-align: right;
|
|
166
|
+
.chat_output > li.user {
|
|
142
167
|
float: right;
|
|
168
|
+
background: var(--arcane-action, var(--primary-color));
|
|
169
|
+
color: var(--arcane-action-text, var(--button-text-color, var(--text-color)));
|
|
170
|
+
border-color: var(--arcane-action, var(--primary-color));
|
|
171
|
+
border-start-end-radius: var(--arcane-radius-small, .3rem);
|
|
172
|
+
text-align: right;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.chat_output li ul,
|
|
176
|
+
.chat_output li ol {
|
|
177
|
+
display: block;
|
|
178
|
+
min-height: auto;
|
|
179
|
+
margin: .55rem 0 .55rem 1.35rem;
|
|
180
|
+
padding: 0;
|
|
181
|
+
overflow: visible;
|
|
182
|
+
list-style: revert;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.chat_output li li {
|
|
186
|
+
float: none;
|
|
187
|
+
width: auto;
|
|
188
|
+
max-width: none;
|
|
189
|
+
margin: .28rem 0;
|
|
190
|
+
padding: 0;
|
|
191
|
+
border: 0;
|
|
192
|
+
border-radius: 0;
|
|
193
|
+
background: transparent;
|
|
194
|
+
color: inherit;
|
|
195
|
+
text-align: start;
|
|
143
196
|
}
|
|
144
197
|
|
|
145
198
|
.chat_input_area {
|
|
146
199
|
display: grid;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
200
|
+
box-sizing: border-box;
|
|
201
|
+
width: 100%;
|
|
202
|
+
padding: var(--arcane-space-3, .7rem);
|
|
203
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: var(--arcane-space-2, .6rem);
|
|
206
|
+
border: 1px solid var(--arcane-border, var(--border-color));
|
|
207
|
+
border-radius: var(--arcane-chat-radius);
|
|
208
|
+
background: var(--arcane-surface-raised, var(--modal-background));
|
|
209
|
+
box-shadow: var(--arcane-shadow-small, 0 .35rem 1rem var(--shadow-color));
|
|
150
210
|
}
|
|
151
211
|
|
|
152
212
|
.chat_input {
|
|
153
|
-
width: calc(100% - 1em);
|
|
154
|
-
min-height: 48px;
|
|
155
|
-
max-height: 160px;
|
|
156
|
-
padding: 1.1em 1em;
|
|
157
|
-
margin: 0;
|
|
158
|
-
border:2px solid var(--border-color);
|
|
159
|
-
border-radius: 0.5em;
|
|
160
|
-
font-family: Arial, sans-serif;
|
|
161
|
-
font-size: 16px;
|
|
162
|
-
line-height: 20px;
|
|
163
213
|
box-sizing: border-box;
|
|
214
|
+
width: 100%;
|
|
215
|
+
min-height: 3rem;
|
|
216
|
+
max-height: 10rem;
|
|
217
|
+
margin: 0;
|
|
218
|
+
padding: .82rem .9rem;
|
|
219
|
+
border: 1px solid var(--arcane-border, var(--border-color));
|
|
220
|
+
border-radius: var(--arcane-radius-medium, .7rem);
|
|
221
|
+
background: var(--arcane-surface, var(--background));
|
|
222
|
+
color: var(--text-color);
|
|
223
|
+
font: inherit;
|
|
224
|
+
font-size: 1rem;
|
|
225
|
+
line-height: 1.35;
|
|
164
226
|
resize: none;
|
|
165
227
|
overflow-y: auto;
|
|
166
228
|
}
|
|
167
229
|
|
|
168
|
-
.chat_input:focus {
|
|
169
|
-
outline
|
|
170
|
-
outline-offset
|
|
171
|
-
border-color:var(--focus-color);
|
|
230
|
+
.chat_input:focus-visible {
|
|
231
|
+
outline: .18em solid var(--arcane-focus, var(--focus-color));
|
|
232
|
+
outline-offset: .12em;
|
|
233
|
+
border-color: var(--arcane-focus, var(--focus-color));
|
|
172
234
|
}
|
|
173
235
|
|
|
174
236
|
.chat_submit,
|
|
175
237
|
button.chat_upload {
|
|
176
238
|
display:grid;
|
|
177
239
|
place-items:center;
|
|
178
|
-
width:
|
|
179
|
-
height:
|
|
240
|
+
width:3rem;
|
|
241
|
+
height:3rem;
|
|
180
242
|
padding:0;
|
|
181
243
|
margin:0;
|
|
182
|
-
background-color:var(--primary-color);
|
|
183
|
-
border:.2em solid var(--primary-color);
|
|
184
|
-
border-radius
|
|
244
|
+
background-color:var(--arcane-action, var(--primary-color));
|
|
245
|
+
border:.2em solid var(--arcane-action, var(--primary-color));
|
|
246
|
+
border-radius:var(--arcane-radius-medium, .7rem);
|
|
185
247
|
cursor: pointer;
|
|
186
248
|
outline: none;
|
|
187
|
-
box-shadow:0 .18em .45em var(--shadow-color);
|
|
188
|
-
align-self:
|
|
249
|
+
box-shadow:var(--arcane-shadow-small, 0 .18em .45em var(--shadow-color));
|
|
250
|
+
align-self:center;
|
|
189
251
|
transition:background-color 180ms,border-color 180ms,box-shadow 180ms,transform 120ms;
|
|
190
|
-
color:var(--button-text-color,var(--text-color));
|
|
252
|
+
color:var(--arcane-action-text, var(--button-text-color,var(--text-color)));
|
|
191
253
|
}
|
|
192
254
|
|
|
193
255
|
.chat_submit::before,
|
|
@@ -221,12 +283,59 @@
|
|
|
221
283
|
|
|
222
284
|
.chat_submit:hover,
|
|
223
285
|
button.chat_upload:hover {
|
|
224
|
-
background-color:var(--secondary-color);
|
|
225
|
-
border-color:var(--secondary-color);
|
|
226
|
-
color:var(--text-color);
|
|
286
|
+
background-color:var(--arcane-secondary, var(--secondary-color));
|
|
287
|
+
border-color:var(--arcane-secondary, var(--secondary-color));
|
|
288
|
+
color:var(--arcane-secondary-text, var(--text-color));
|
|
227
289
|
box-shadow:0 .25em .6em var(--shadow-color);
|
|
228
290
|
}
|
|
229
291
|
|
|
292
|
+
.chat_submit:focus-visible,
|
|
293
|
+
button.chat_upload:focus-visible {
|
|
294
|
+
outline: .2rem solid var(--arcane-focus, var(--focus-color));
|
|
295
|
+
outline-offset: .18rem;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#speech {
|
|
299
|
+
display: grid;
|
|
300
|
+
flex: 0 0 auto;
|
|
301
|
+
min-width: 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.controls {
|
|
305
|
+
display: flex;
|
|
306
|
+
justify-content: flex-end;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.controls select {
|
|
310
|
+
width: min(100%, 18rem);
|
|
311
|
+
min-height: 2.65rem;
|
|
312
|
+
padding: .55rem .75rem;
|
|
313
|
+
border: 1px solid var(--arcane-border, var(--border-color));
|
|
314
|
+
border-radius: var(--arcane-radius-medium, .7rem);
|
|
315
|
+
background: var(--arcane-surface-raised, var(--modal-background));
|
|
316
|
+
color: var(--text-color);
|
|
317
|
+
font: inherit;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.controls select:focus-visible {
|
|
321
|
+
outline: .2rem solid var(--arcane-focus, var(--focus-color));
|
|
322
|
+
outline-offset: .18rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@media (max-width: 44rem) {
|
|
326
|
+
.chat_area {
|
|
327
|
+
padding: var(--arcane-space-2, .5rem);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.chat_output > li {
|
|
331
|
+
max-width: 96%;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.controls select {
|
|
335
|
+
width: 100%;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
230
339
|
|
|
231
340
|
:host(.journal){
|
|
232
341
|
.chat_output {
|
|
@@ -518,11 +627,13 @@
|
|
|
518
627
|
const [
|
|
519
628
|
{default:MD},
|
|
520
629
|
{default:FileEntity},
|
|
521
|
-
{ConversationSubmissionBarrier,requireConversationTimeboxDelivery}
|
|
630
|
+
{ConversationSubmissionBarrier,requireConversationTimeboxDelivery},
|
|
631
|
+
{subscribeAIRuntimeState}
|
|
522
632
|
]=await Promise.all([
|
|
523
633
|
import('../modules/MD.js'),
|
|
524
634
|
import('../entities/File.js'),
|
|
525
|
-
import('../modules/ConversationTimebox.js')
|
|
635
|
+
import('../modules/ConversationTimebox.js'),
|
|
636
|
+
import('../modules/AIRuntimeState.js')
|
|
526
637
|
]);
|
|
527
638
|
|
|
528
639
|
host.language=host.language||'English';
|
|
@@ -532,7 +643,10 @@
|
|
|
532
643
|
?speech.initialMuted
|
|
533
644
|
:true;
|
|
534
645
|
let initialSpeechMuteListenerInstalled=false;
|
|
646
|
+
let pendingSpeechAvailability=null;
|
|
647
|
+
let speechAvailabilityListenerInstalled=false;
|
|
535
648
|
const hostSubmissionBarrier=new ConversationSubmissionBarrier();
|
|
649
|
+
const aiRuntimeStateAbortController=new AbortController();
|
|
536
650
|
let programmaticSubmissionQueue=Promise.resolve(true);
|
|
537
651
|
let conversationTimeboxController=null;
|
|
538
652
|
let conversationTimeboxUnsubscribe=null;
|
|
@@ -556,8 +670,17 @@
|
|
|
556
670
|
host.setConversationComplete=setConversationComplete;
|
|
557
671
|
host.bindConversationTimebox=bindConversationTimebox;
|
|
558
672
|
host.submitMessage=submitMessage;
|
|
559
|
-
host.
|
|
673
|
+
host.destroy=destroy;
|
|
674
|
+
host.aiAvailability={llm:false,stt:false,tts:false};
|
|
560
675
|
host.conversationComplete=false;
|
|
676
|
+
applyAIAvailability(host.aiAvailability);
|
|
677
|
+
subscribeAIRuntimeState(
|
|
678
|
+
synchronizeAIRuntimeState,
|
|
679
|
+
{
|
|
680
|
+
signal:aiRuntimeStateAbortController.signal
|
|
681
|
+
}
|
|
682
|
+
);
|
|
683
|
+
window.addEventListener('pagehide',destroy,{once:true});
|
|
561
684
|
|
|
562
685
|
function setConversationComplete(complete=true){
|
|
563
686
|
if(typeof complete!=='boolean'){
|
|
@@ -699,6 +822,7 @@
|
|
|
699
822
|
}
|
|
700
823
|
|
|
701
824
|
function setAIAvailability(input={}){
|
|
825
|
+
const explicitSpeechAvailability={};
|
|
702
826
|
const availability={
|
|
703
827
|
llm:Object.prototype.hasOwnProperty.call(input,'llm')
|
|
704
828
|
?Boolean(input.llm)
|
|
@@ -711,25 +835,69 @@
|
|
|
711
835
|
:host.aiAvailability.tts
|
|
712
836
|
};
|
|
713
837
|
|
|
714
|
-
|
|
838
|
+
if(Object.prototype.hasOwnProperty.call(input,'stt')){
|
|
839
|
+
explicitSpeechAvailability.stt=availability.stt;
|
|
840
|
+
}
|
|
841
|
+
if(Object.prototype.hasOwnProperty.call(input,'tts')){
|
|
842
|
+
explicitSpeechAvailability.tts=availability.tts;
|
|
843
|
+
}
|
|
844
|
+
applyAIAvailability(availability);
|
|
845
|
+
if(Object.keys(explicitSpeechAvailability).length){
|
|
846
|
+
pendingSpeechAvailability={
|
|
847
|
+
...(pendingSpeechAvailability||{}),
|
|
848
|
+
...explicitSpeechAvailability
|
|
849
|
+
};
|
|
850
|
+
synchronizeSpeechCompatibilityAvailability();
|
|
851
|
+
}
|
|
852
|
+
return {...availability};
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function applyAIAvailability(availability){
|
|
856
|
+
host.aiAvailability={...availability};
|
|
715
857
|
send.disabled=host.conversationComplete||!availability.llm;
|
|
716
858
|
send.title=availability.llm
|
|
717
859
|
?'Send message'
|
|
718
860
|
:'The selected language model service is unavailable.';
|
|
719
861
|
send.setAttribute('aria-label',send.title);
|
|
862
|
+
}
|
|
720
863
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
864
|
+
function synchronizeAIRuntimeState(snapshot){
|
|
865
|
+
applyAIAvailability({
|
|
866
|
+
llm:snapshot.roles.llm.state==='ready',
|
|
867
|
+
stt:snapshot.roles.stt.state==='ready',
|
|
868
|
+
tts:snapshot.roles.tts.state==='ready'
|
|
724
869
|
});
|
|
870
|
+
}
|
|
725
871
|
|
|
872
|
+
function synchronizeSpeechCompatibilityAvailability(){
|
|
873
|
+
if(!pendingSpeechAvailability){
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
726
876
|
if(speech.componentReady){
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
speech.
|
|
877
|
+
const availability=pendingSpeechAvailability;
|
|
878
|
+
pendingSpeechAvailability=null;
|
|
879
|
+
speech.setAvailability?.({...availability});
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
if(speechAvailabilityListenerInstalled){
|
|
883
|
+
return;
|
|
730
884
|
}
|
|
731
885
|
|
|
732
|
-
|
|
886
|
+
speechAvailabilityListenerInstalled=true;
|
|
887
|
+
speech.addEventListener(
|
|
888
|
+
'speech-ready',
|
|
889
|
+
applyPendingSpeechAvailability,
|
|
890
|
+
{once:true}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function applyPendingSpeechAvailability(){
|
|
895
|
+
speechAvailabilityListenerInstalled=false;
|
|
896
|
+
if(pendingSpeechAvailability){
|
|
897
|
+
const availability=pendingSpeechAvailability;
|
|
898
|
+
pendingSpeechAvailability=null;
|
|
899
|
+
speech.setAvailability?.({...availability});
|
|
900
|
+
}
|
|
733
901
|
}
|
|
734
902
|
|
|
735
903
|
function formatConversationTime(milliseconds=0){
|
|
@@ -928,7 +1096,7 @@
|
|
|
928
1096
|
}
|
|
929
1097
|
)
|
|
930
1098
|
);
|
|
931
|
-
console.error('The conversation timebox message could not be sent.'
|
|
1099
|
+
console.error('The conversation timebox message could not be sent.');
|
|
932
1100
|
return false;
|
|
933
1101
|
}
|
|
934
1102
|
);
|
|
@@ -997,10 +1165,22 @@
|
|
|
997
1165
|
const clearThinking=thinkingHTML;
|
|
998
1166
|
|
|
999
1167
|
//console.table(arguments);
|
|
1000
|
-
if(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1168
|
+
if(
|
|
1169
|
+
!isThinking
|
|
1170
|
+
&&text
|
|
1171
|
+
&&host.aiAvailability.tts
|
|
1172
|
+
&&speech.muted===false
|
|
1173
|
+
){
|
|
1174
|
+
const aiRuntime=globalThis.ai;
|
|
1175
|
+
if(typeof aiRuntime?.streamTTS==='function'){
|
|
1176
|
+
void Promise.resolve(aiRuntime.streamTTS(text)).catch(
|
|
1177
|
+
reportTTSError
|
|
1178
|
+
);
|
|
1179
|
+
}else{
|
|
1180
|
+
reportTTSError(
|
|
1181
|
+
new Error('The ready speech provider has no synthesis adapter.')
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1004
1184
|
}
|
|
1005
1185
|
|
|
1006
1186
|
if(clearThinking){
|
|
@@ -1126,7 +1306,7 @@
|
|
|
1126
1306
|
}
|
|
1127
1307
|
)
|
|
1128
1308
|
);
|
|
1129
|
-
console.error('The chat message could not be submitted.'
|
|
1309
|
+
console.error('The chat message could not be submitted.');
|
|
1130
1310
|
return false;
|
|
1131
1311
|
}
|
|
1132
1312
|
);
|
|
@@ -1143,6 +1323,9 @@
|
|
|
1143
1323
|
if(host.conversationComplete){
|
|
1144
1324
|
return false;
|
|
1145
1325
|
}
|
|
1326
|
+
if(!host.aiAvailability.llm){
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1146
1329
|
const submissionContext=Object.freeze({
|
|
1147
1330
|
source:'user',
|
|
1148
1331
|
preserveDraft:false,
|
|
@@ -1154,7 +1337,6 @@
|
|
|
1154
1337
|
return false;
|
|
1155
1338
|
}
|
|
1156
1339
|
const messageTime = getMilTime();
|
|
1157
|
-
console.info("Dispatching message:", text);
|
|
1158
1340
|
if(!submissionContext.preserveDraft){
|
|
1159
1341
|
textArea.value='';
|
|
1160
1342
|
}
|
|
@@ -1189,8 +1371,15 @@
|
|
|
1189
1371
|
}
|
|
1190
1372
|
);
|
|
1191
1373
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1374
|
+
if(host.aiAvailability.tts&&speech.muted===false){
|
|
1375
|
+
const aiRuntime=globalThis.ai;
|
|
1376
|
+
aiRuntime?.stopAudio?.();
|
|
1377
|
+
if(typeof aiRuntime?.resumeAudio==='function'){
|
|
1378
|
+
void Promise.resolve(aiRuntime.resumeAudio()).catch(
|
|
1379
|
+
reportTTSError
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1194
1383
|
|
|
1195
1384
|
host.dispatchEvent(chatEvent);
|
|
1196
1385
|
|
|
@@ -1219,6 +1408,35 @@
|
|
|
1219
1408
|
chatOutput.scrollTop = chatOutput.scrollHeight;
|
|
1220
1409
|
}
|
|
1221
1410
|
|
|
1411
|
+
function reportTTSError(error){
|
|
1412
|
+
if(typeof speech.reportTTSError==='function'){
|
|
1413
|
+
speech.reportTTSError(error);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
const message=typeof error?.message==='string'&&error.message.trim()
|
|
1418
|
+
?error.message.trim().slice(0,240)
|
|
1419
|
+
:'Speech synthesis failed.';
|
|
1420
|
+
host.dispatchEvent(
|
|
1421
|
+
new CustomEvent(
|
|
1422
|
+
'chat-speech-synthesis-error',
|
|
1423
|
+
{
|
|
1424
|
+
bubbles:true,
|
|
1425
|
+
composed:true,
|
|
1426
|
+
detail:{error,message}
|
|
1427
|
+
}
|
|
1428
|
+
)
|
|
1429
|
+
);
|
|
1430
|
+
console.error('Speech synthesis failed.');
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function destroy(){
|
|
1434
|
+
aiRuntimeStateAbortController.abort();
|
|
1435
|
+
window.removeEventListener('pagehide',destroy);
|
|
1436
|
+
speech.destroy?.();
|
|
1437
|
+
host.ready=false;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1222
1440
|
// Initialize and adjust on input
|
|
1223
1441
|
resizeTextArea();
|
|
1224
1442
|
|