arcane-os 0.1.0-dev.5

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.
Files changed (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,476 @@
1
+ <link rel="stylesheet" href="./arcane/css/layout.css?v=3">
2
+ <style>
3
+ :host{
4
+ display:block;
5
+ height:100%;
6
+ min-height:0;
7
+ }
8
+
9
+ .voice-transcription{
10
+ display:grid;
11
+ gap:1em;
12
+ grid-template-rows:auto minmax(12em,1fr) auto auto;
13
+ height:100%;
14
+ min-height:0;
15
+ }
16
+
17
+ .transcript{
18
+ background:var(--background);
19
+ border:1px solid var(--border-color);
20
+ border-radius:.5em;
21
+ overflow:auto;
22
+ padding:1em;
23
+ }
24
+
25
+ .transcript:empty::before{
26
+ content:attr(data-empty-label);
27
+ opacity:.55;
28
+ }
29
+
30
+ .controls{
31
+ display:flex;
32
+ flex-wrap:wrap;
33
+ gap:.5em;
34
+ }
35
+
36
+ .controls button{
37
+ margin:0;
38
+ }
39
+
40
+ .recording{
41
+ color:var(--danger-color);
42
+ }
43
+
44
+ .description,
45
+ .status{
46
+ margin:0;
47
+ }
48
+ </style>
49
+
50
+ <section class="voice-transcription">
51
+ <p id="description" class="description"></p>
52
+ <section id="transcript" class="transcript"></section>
53
+ <p id="status" class="status" aria-live="polite"></p>
54
+ <section class="controls">
55
+ <button id="start" type="button" disabled></button>
56
+ <button id="stop" type="button" disabled></button>
57
+ <button id="complete" type="button" disabled></button>
58
+ </section>
59
+ </section>
60
+
61
+ <script type="module">
62
+ const host=this;
63
+ const componentURL=new URL(
64
+ host.getAttribute('href')||'./arcane/components/voice-transcription.html',
65
+ document.baseURI
66
+ );
67
+ const moduleURL=name=>new URL(`../modules/${name}`,componentURL).href;
68
+ const {default:MD}=await import(moduleURL('MD.js?v=2'));
69
+ const {
70
+ appendTranscription,
71
+ normalizeVoiceOptions
72
+ }=await import(moduleURL('ComponentContracts.js'));
73
+ const description=host.shadowRoot.querySelector('#description');
74
+ const transcriptElement=host.shadowRoot.querySelector('#transcript');
75
+ const status=host.shadowRoot.querySelector('#status');
76
+ const startButton=host.shadowRoot.querySelector('#start');
77
+ const stopButton=host.shadowRoot.querySelector('#stop');
78
+ const completeButton=host.shadowRoot.querySelector('#complete');
79
+
80
+ let chunks=[];
81
+ let mediaStream=null;
82
+ let recorder=null;
83
+ let state='idle';
84
+ let transcript='';
85
+ let sessionGeneration=0;
86
+ let options=normalizeVoiceOptions(optionsFromDataset());
87
+
88
+ host.clear=clear;
89
+ host.completeTranscription=completeStream;
90
+ host.configure=configure;
91
+ host.destroy=destroy;
92
+ host.reset=clear;
93
+ host.startRecording=startRecording;
94
+ host.stopRecording=stopRecording;
95
+
96
+ Object.defineProperties(
97
+ host,
98
+ {
99
+ options:{get:getOptions,set:configure},
100
+ state:{get:()=>state},
101
+ value:{get:()=>transcript,set:setValue}
102
+ }
103
+ );
104
+
105
+ startButton.addEventListener('click',startRecording);
106
+ stopButton.addEventListener('click',stopRecording);
107
+ completeButton.addEventListener('click',completeStream);
108
+
109
+ function optionsFromDataset(){
110
+ const dataset=host.dataset||{};
111
+ const mapped={};
112
+ const fields=[
113
+ 'completeLabel',
114
+ 'description',
115
+ 'emptyLabel',
116
+ 'startLabel',
117
+ 'stopLabel',
118
+ 'transcriptionLabel'
119
+ ];
120
+ for(const field of fields){
121
+ if(dataset[field]!==undefined){
122
+ mapped[field]=dataset[field];
123
+ }
124
+ }
125
+ if(dataset.persist!==undefined){
126
+ mapped.persist=dataset.persist!=='false';
127
+ }
128
+ return mapped;
129
+ }
130
+
131
+ function configure(input={}){
132
+ const setInitial=Object.prototype.hasOwnProperty.call(input||{},'initialValue');
133
+ options=normalizeVoiceOptions(input,options);
134
+ renderOptions();
135
+ if(setInitial){
136
+ setValue(options.initialValue);
137
+ }else{
138
+ setState(state,status.innerText);
139
+ }
140
+ return getOptions();
141
+ }
142
+
143
+ async function startRecording(){
144
+ if(!['idle','error'].includes(state)){
145
+ return false;
146
+ }
147
+ if(
148
+ typeof MediaRecorder==='undefined'
149
+ ||!navigator.mediaDevices?.getUserMedia
150
+ ){
151
+ setState('error',options.messages.unsupported);
152
+ return false;
153
+ }
154
+
155
+ setState('starting',options.messages.requesting);
156
+ chunks=[];
157
+ const generation=++sessionGeneration;
158
+ try{
159
+ const stream=await navigator.mediaDevices.getUserMedia(
160
+ options.mediaConstraints
161
+ );
162
+ if(generation!==sessionGeneration){
163
+ for(const track of stream.getTracks()){
164
+ track.stop();
165
+ }
166
+ return false;
167
+ }
168
+ mediaStream=stream;
169
+ recorder=createRecorder(mediaStream);
170
+ recorder.addEventListener('dataavailable',collectAudio);
171
+ recorder.addEventListener('stop',finishRecording,{once:true});
172
+ recorder.addEventListener('error',recordingError,{once:true});
173
+ for(const track of mediaStream.getAudioTracks()){
174
+ track.addEventListener('ended',recordingInterrupted,{once:true});
175
+ }
176
+ recorder.start();
177
+ setState('recording',options.messages.recording);
178
+ return true;
179
+ }catch(error){
180
+ if(generation!==sessionGeneration){
181
+ return false;
182
+ }
183
+ releaseMicrophone();
184
+ setState('error',options.messages.startError);
185
+ console.error('Unable to start recording:',error);
186
+ return false;
187
+ }
188
+ }
189
+
190
+ function createRecorder(stream){
191
+ for(const mimeType of options.mimeTypes){
192
+ if(MediaRecorder.isTypeSupported?.(mimeType)){
193
+ return new MediaRecorder(stream,{mimeType});
194
+ }
195
+ }
196
+ return new MediaRecorder(stream);
197
+ }
198
+
199
+ function collectAudio(event){
200
+ if(event.data?.size){
201
+ chunks.push(event.data);
202
+ }
203
+ }
204
+
205
+ function stopRecording(){
206
+ if(state!=='recording'||recorder?.state!=='recording'){
207
+ return false;
208
+ }
209
+ setState('transcribing',options.messages.transcribing);
210
+ recorder.stop();
211
+ return true;
212
+ }
213
+
214
+ async function finishRecording(){
215
+ const mimeType=recorder?.mimeType||chunks[0]?.type||'audio/webm';
216
+ const audio=new Blob(chunks,{type:mimeType});
217
+ releaseMicrophone();
218
+ if(!audio.size){
219
+ setState('error',options.messages.emptyAudio);
220
+ return false;
221
+ }
222
+
223
+ try{
224
+ const file=new File(
225
+ [audio],
226
+ `audio.${fileExtension(mimeType)}`,
227
+ {type:mimeType}
228
+ );
229
+ const transcribe=await getTranscriber();
230
+ const result=await transcribe(
231
+ file,
232
+ {
233
+ audio,
234
+ mimeType,
235
+ transcript
236
+ }
237
+ );
238
+ const segment=typeof result==='string'?result.trim():'';
239
+ if(!segment){
240
+ setState('idle',options.messages.noSpeech);
241
+ return false;
242
+ }
243
+
244
+ transcript=appendTranscription(
245
+ transcript,
246
+ segment,
247
+ options.separator
248
+ );
249
+ renderTranscript();
250
+
251
+ if(options.persist){
252
+ const save=options.onSave||(
253
+ typeof host.save==='function'?host.save.bind(host):null
254
+ );
255
+ if(!save){
256
+ setState('error',options.messages.saveError);
257
+ return false;
258
+ }
259
+ setState('saving',options.messages.saving);
260
+ try{
261
+ await save({transcript,segment});
262
+ }catch(error){
263
+ setState('error',options.messages.saveError);
264
+ console.error('Unable to save transcription:',error);
265
+ return false;
266
+ }
267
+ }
268
+
269
+ const detail={text:segment,transcript};
270
+ host.dispatchEvent(
271
+ new CustomEvent(
272
+ 'speech-transcription-complete',
273
+ {bubbles:true,composed:true,detail}
274
+ )
275
+ );
276
+ host.dispatchEvent(
277
+ new CustomEvent(
278
+ 'voice-transcription-segment',
279
+ {bubbles:true,composed:true,detail}
280
+ )
281
+ );
282
+ setState(
283
+ 'idle',
284
+ options.persist?options.messages.saved:options.messages.transcribed
285
+ );
286
+ return true;
287
+ }catch(error){
288
+ setState('error',options.messages.transcribeError);
289
+ console.error('Unable to transcribe recording:',error);
290
+ return false;
291
+ }
292
+ }
293
+
294
+ async function getTranscriber(){
295
+ if(options.transcribe){
296
+ return options.transcribe;
297
+ }
298
+ if(typeof globalThis.ai?.fetchSTT!=='function'){
299
+ await import(moduleURL('AI.js?v=7'));
300
+ }
301
+ if(typeof globalThis.ai?.fetchSTT!=='function'){
302
+ throw new Error('Speech transcription is not configured.');
303
+ }
304
+ return globalThis.ai.fetchSTT.bind(globalThis.ai);
305
+ }
306
+
307
+ function fileExtension(mimeType=''){
308
+ if(mimeType.includes('mp4')){
309
+ return 'm4a';
310
+ }
311
+ if(mimeType.includes('ogg')){
312
+ return 'ogg';
313
+ }
314
+ return 'webm';
315
+ }
316
+
317
+ function recordingInterrupted(){
318
+ if(state==='recording'){
319
+ setState('transcribing',options.messages.recordingEnded);
320
+ if(recorder?.state==='recording'){
321
+ recorder.stop();
322
+ }
323
+ }
324
+ }
325
+
326
+ function recordingError(event){
327
+ setState('error',options.messages.interrupted);
328
+ releaseMicrophone();
329
+ console.error('Audio recorder error:',event.error||event);
330
+ }
331
+
332
+ async function completeStream(){
333
+ if(!transcript.trim()||!['idle','error'].includes(state)){
334
+ return false;
335
+ }
336
+ setState('completing',options.messages.completing);
337
+ try{
338
+ const complete=options.onComplete||(
339
+ typeof host.complete==='function'?host.complete.bind(host):null
340
+ );
341
+ if(complete){
342
+ await complete({transcript});
343
+ }
344
+ host.dispatchEvent(
345
+ new CustomEvent(
346
+ 'voice-transcription-complete',
347
+ {
348
+ bubbles:true,
349
+ composed:true,
350
+ detail:{transcript}
351
+ }
352
+ )
353
+ );
354
+ setState('complete',options.messages.complete);
355
+ return true;
356
+ }catch(error){
357
+ setState('error',options.messages.completeError);
358
+ console.error('Unable to complete transcription:',error);
359
+ return false;
360
+ }
361
+ }
362
+
363
+ function setValue(value=''){
364
+ transcript=`${value??''}`;
365
+ renderTranscript();
366
+ if(state!=='recording'){
367
+ setState('idle',options.messages.ready);
368
+ }
369
+ host.dispatchEvent(
370
+ new CustomEvent(
371
+ 'voice-transcription-change',
372
+ {
373
+ bubbles:true,
374
+ composed:true,
375
+ detail:{transcript}
376
+ }
377
+ )
378
+ );
379
+ return transcript;
380
+ }
381
+
382
+ function clear(){
383
+ if(!['complete','error','idle'].includes(state)){
384
+ return false;
385
+ }
386
+ transcript='';
387
+ renderTranscript();
388
+ setState('idle',options.messages.ready);
389
+ return true;
390
+ }
391
+
392
+ function renderTranscript(){
393
+ transcriptElement.innerHTML=new MD(transcript).safeRendered;
394
+ transcriptElement.querySelectorAll('a').forEach(
395
+ link=>{
396
+ link.target='_blank';
397
+ link.rel='noopener noreferrer';
398
+ }
399
+ );
400
+ }
401
+
402
+ function renderOptions(){
403
+ description.innerText=options.labels.description;
404
+ transcriptElement.setAttribute(
405
+ 'aria-label',
406
+ options.labels.transcription
407
+ );
408
+ transcriptElement.dataset.emptyLabel=options.labels.empty;
409
+ startButton.innerText=options.labels.start;
410
+ stopButton.innerText=options.labels.stop;
411
+ completeButton.innerText=options.labels.complete;
412
+ }
413
+
414
+ function releaseMicrophone(){
415
+ const tracks=mediaStream?.getTracks?.()||[];
416
+ recorder?.removeEventListener('dataavailable',collectAudio);
417
+ recorder?.removeEventListener('stop',finishRecording);
418
+ recorder?.removeEventListener('error',recordingError);
419
+ mediaStream=null;
420
+ recorder=null;
421
+ chunks=[];
422
+ for(const track of tracks){
423
+ track.removeEventListener('ended',recordingInterrupted);
424
+ track.stop();
425
+ }
426
+ }
427
+
428
+ function setState(nextState,message=''){
429
+ state=nextState;
430
+ status.innerText=message||nextState;
431
+ status.classList.toggle('recording',state==='recording');
432
+ startButton.disabled=!['idle','error'].includes(state);
433
+ stopButton.disabled=state!=='recording';
434
+ completeButton.disabled=!transcript.trim()
435
+ ||!['idle','error'].includes(state);
436
+ host.dispatchEvent(
437
+ new CustomEvent(
438
+ 'voice-transcription-state',
439
+ {
440
+ bubbles:true,
441
+ composed:true,
442
+ detail:{message:status.innerText,state}
443
+ }
444
+ )
445
+ );
446
+ }
447
+
448
+ function getOptions(){
449
+ return {
450
+ ...options,
451
+ labels:{...options.labels},
452
+ messages:{...options.messages},
453
+ mediaConstraints:{...options.mediaConstraints},
454
+ mimeTypes:options.mimeTypes.slice()
455
+ };
456
+ }
457
+
458
+ function destroy(){
459
+ sessionGeneration++;
460
+ releaseMicrophone();
461
+ setState('idle',options.messages.ready);
462
+ return true;
463
+ }
464
+
465
+ renderOptions();
466
+ transcript=options.initialValue;
467
+ renderTranscript();
468
+ setState('idle',options.messages.ready);
469
+ host.ready=true;
470
+ host.dispatchEvent(
471
+ new CustomEvent(
472
+ 'voice-transcription-ready',
473
+ {bubbles:true,composed:true}
474
+ )
475
+ );
476
+ </script>
@@ -0,0 +1,8 @@
1
+ <link rel="stylesheet" href="./arcane/css/theme.css?v=1"><link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
2
+ <style>:host{display:block}.widget{display:grid;gap:var(--arcane-space-4)}.current{align-items:center;display:grid;gap:var(--arcane-space-4);grid-template-columns:auto 1fr auto}.symbol{font-size:clamp(2.5rem,8vw,5rem);line-height:1}.temperature{font-size:clamp(2.2rem,7vw,4.5rem);font-weight:760;line-height:1}.location h2,.location p{margin:0}.location p,.details{color:var(--arcane-muted)}.details{display:grid;gap:var(--arcane-space-1);text-align:right}.forecast{display:grid;gap:var(--arcane-space-2);grid-template-columns:repeat(7,minmax(5.5rem,1fr));overflow:auto}.day{display:grid;gap:var(--arcane-space-2);padding:var(--arcane-space-3);text-align:center;background:var(--arcane-surface-muted);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-medium)}.day strong{white-space:nowrap}.day-symbol{font-size:1.8rem}.empty{padding:var(--arcane-space-6);text-align:center;color:var(--arcane-muted)}.footer{display:flex;align-items:center;justify-content:space-between;gap:var(--arcane-space-3)}@media(max-width:36rem){.current{grid-template-columns:auto 1fr}.details{grid-column:1/-1;text-align:left;grid-template-columns:repeat(3,1fr)}}</style>
3
+ <section class="widget arcane-card"><div id="empty" class="empty"><strong>Weather is ready for a location</strong><p>Search for a city or postal code to load current conditions and the forecast.</p></div><div id="content" hidden><div class="arcane-card__body current"><span id="symbol" class="symbol" aria-hidden="true"></span><div class="location"><h2 id="location" class="arcane-section-heading"></h2><p id="condition"></p><div><span id="temperature" class="temperature"></span></div></div><div class="details"><span id="feels"></span><span id="humidity"></span><span id="wind"></span></div></div><div id="forecast" class="arcane-card__body forecast"></div><footer class="arcane-card__footer footer"><span id="source" class="arcane-help"></span><button id="refresh" class="arcane-button arcane-button--secondary" type="button">Refresh</button></footer></div></section>
4
+ <script type="module">const host=this,root=host.shadowRoot,empty=root.querySelector('#empty'),content=root.querySelector('#content'),forecast=root.querySelector('#forecast');let weather=null;const conditions=new Map([[0,['Clear','☀️']],[1,['Mostly clear','🌤️']],[2,['Partly cloudy','⛅']],[3,['Overcast','☁️']],[45,['Fog','🌫️']],[48,['Freezing fog','🌫️']],[51,['Light drizzle','🌦️']],[53,['Drizzle','🌦️']],[55,['Heavy drizzle','🌧️']],[61,['Light rain','🌦️']],[63,['Rain','🌧️']],[65,['Heavy rain','🌧️']],[71,['Light snow','🌨️']],[73,['Snow','🌨️']],[75,['Heavy snow','❄️']],[80,['Rain showers','🌦️']],[81,['Rain showers','🌧️']],[82,['Heavy showers','⛈️']],[95,['Thunderstorm','⛈️']],[96,['Thunderstorm with hail','⛈️']],[99,['Severe thunderstorm','⛈️']]]);
5
+ host.setWeather=setWeather;host.clear=()=>{weather=null;empty.hidden=false;content.hidden=true};root.querySelector('#refresh').addEventListener('click',()=>host.dispatchEvent(new CustomEvent('weather-refresh',{bubbles:true,composed:true,detail:{weather}})));
6
+ function info(code,isDay=true){const value=conditions.get(Number(code))||['Conditions unavailable','🌡️'];if(!isDay&&Number(code)<=1)return ['Clear night','🌙'];return value}
7
+ function setWeather(value){weather=value;if(!value)return host.clear();empty.hidden=true;content.hidden=false;const current=value.current,[label,symbol]=info(current.weatherCode,current.isDay);root.querySelector('#symbol').textContent=symbol;root.querySelector('#location').textContent=[value.location.name,value.location.region,value.location.country].filter(Boolean).join(', ');root.querySelector('#condition').textContent=label;root.querySelector('#temperature').textContent=`${Math.round(current.temperature)}${current.temperatureUnit}`;root.querySelector('#feels').textContent=`Feels ${Math.round(current.apparentTemperature)}${current.temperatureUnit}`;root.querySelector('#humidity').textContent=`Humidity ${Math.round(current.humidity)}%`;root.querySelector('#wind').textContent=`Wind ${Math.round(current.windSpeed)} ${current.windUnit}`;root.querySelector('#source').textContent=`${value.source||'Weather source'} · updated ${new Intl.DateTimeFormat(undefined,{timeStyle:'short'}).format(new Date(value.fetchedAt))}`;forecast.replaceChildren(...Array.from(value.daily||[]).map(day=>{const section=document.createElement('section'),name=document.createElement('strong'),icon=document.createElement('span'),temperatures=document.createElement('span'),rain=document.createElement('span');section.className='day';name.textContent=new Intl.DateTimeFormat(undefined,{weekday:'short'}).format(new Date(day.date));icon.className='day-symbol';icon.textContent=info(day.weatherCode)[1];temperatures.textContent=`${Math.round(day.temperatureMax)}° / ${Math.round(day.temperatureMin)}°`;rain.textContent=`${Math.round(day.precipitationProbability)}% rain`;section.append(name,icon,temperatures,rain);return section}));return weather}
8
+ host.ready=true;host.dispatchEvent(new CustomEvent('weather-widget-ready',{bubbles:true,composed:true}));</script>