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,142 @@
1
+ <style>
2
+ /* Styles when 'no-logo' class is added to the host */
3
+ :host(.no-logo) .logo{
4
+ visibility: hidden;
5
+ -webkit-app-region: drag;
6
+ }
7
+
8
+ header{
9
+ -webkit-app-region: drag;
10
+ padding: 0 0.5em;
11
+ display: grid;
12
+ grid-template-columns:2em 2em 2em minmax(0,1fr) 2em 8em;
13
+ align-items: center;
14
+ text-align:center;
15
+ color: var(--platinum);
16
+ }
17
+
18
+ header .logo {
19
+ height:2em;
20
+ text-align:center;
21
+ -webkit-app-region: no-drag;
22
+ cursor:pointer;
23
+ }
24
+
25
+ h1{
26
+ font-weight: 300;
27
+ font-size:1em;
28
+ }
29
+
30
+ button{
31
+ -webkit-app-region: no-drag;
32
+ opacity:55%;
33
+ transition:opacity 250ms;
34
+ border: none;
35
+ background:transparent;
36
+ cursor:pointer;
37
+ color:var(--platinum);
38
+ font-weight:600
39
+ }
40
+
41
+ button:hover{
42
+ opacity:100%;
43
+ cursor:pointer;
44
+ }
45
+
46
+ button>img{
47
+ filter:var(--icon-filter,none);
48
+ height:1.2em;
49
+ }
50
+
51
+ .crisis-call{
52
+ background:var(--primary-color);
53
+ color:var(--button-text-color,var(--text-color));
54
+ padding:.7em 0 .4em;
55
+ border-radius: .2em;
56
+ opacity:70%;
57
+ width:100%;
58
+ white-space:nowrap;
59
+ }
60
+
61
+ .internet-status{
62
+ color:var(--danger-color);
63
+ }
64
+
65
+ .internet-status.online{
66
+ color:var(--success-color);
67
+ }
68
+ </style>
69
+ <header>
70
+ <button id="back"><img src="./arcane/img/arrow-left.png"/></button>
71
+ <button id="forward"><img src="./arcane/img/arrow-right.png"/></button>
72
+ <button id="refresh"><img src="./arcane/img/refresh.png"/></button>
73
+ <h1 id="title"></h1>
74
+ <span class="internet-status">●</span>
75
+ <a part='crisis-call-988' href='tel:988'>
76
+ <button class="crisis-call">988</button>
77
+ </a>
78
+ </header>
79
+
80
+ <script type="module">
81
+ import('../entities/User.js');
82
+
83
+ const host=this;
84
+
85
+ window.addEventListener('user-entity-loaded', setSkin);
86
+ window.addEventListener('online', checkInternet);
87
+ window.addEventListener('offline', checkInternet);
88
+
89
+ if (window.user?.ready) {
90
+ setSkin();
91
+ }
92
+
93
+ checkInternet();
94
+
95
+ let color = {};
96
+
97
+ this.shadowRoot.querySelector('#back').addEventListener(
98
+ 'click',
99
+ (e)=>{
100
+ window.history.back();
101
+ }
102
+ );
103
+
104
+ this.shadowRoot.querySelector('#forward').addEventListener(
105
+ 'click',
106
+ (e)=>{
107
+ window.history.forward();
108
+ }
109
+ );
110
+
111
+ this.shadowRoot.querySelector('#refresh').addEventListener(
112
+ 'click',
113
+ (e)=>{
114
+ window.location.reload(true);
115
+ }
116
+ );
117
+
118
+ this.shadowRoot.querySelector('#title').innerText = document.title;
119
+
120
+ async function setSkin() {
121
+ try {
122
+ const skin = window.user?.skin;
123
+ if (skin) {
124
+ document.body.className = skin;
125
+ } else {
126
+ console.log('No saved skin found. Using default.');
127
+ }
128
+ } catch (err) {
129
+ console.error('Failed to set global saved skin', err);
130
+ }
131
+ }
132
+
133
+ // Check if user can connect to the internet (make sure the cloud can be reached)
134
+ async function checkInternet() {
135
+ const online = navigator.onLine;
136
+ console.log(`Internet status: ${online ? 'Online' : 'Offline'}`);
137
+ host.shadowRoot.querySelector('.internet-status')
138
+ .classList
139
+ .toggle('online', online);
140
+
141
+ }
142
+ </script>
@@ -0,0 +1,14 @@
1
+ <link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
2
+ <style>
3
+ :host{display:block}.heading{align-items:flex-start;display:flex;gap:1rem;justify-content:space-between}.heading h2,.heading p{margin:0}.services{display:grid;gap:.75rem}.service{display:grid;gap:.65rem;padding:1rem;border:1px solid var(--border-color,#ccd4df);border-radius:.7rem}.service-head{align-items:flex-start;display:flex;gap:.75rem}.service-head input{height:1.25rem;width:1.25rem}.service-copy{flex:1}.service-copy strong{display:block}.service-copy p{color:var(--muted-text-color,#607080);margin:.2rem 0 0}.fields{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr))}.status{border-radius:999px;padding:.2rem .55rem;background:var(--secondary-color,#e8edf4);font-size:.72rem}.actions{display:flex;gap:.5rem;justify-content:flex-end}.notice{padding:.75rem;border-left:.25rem solid var(--focus-color,#7657d5);background:var(--secondary-color,#e8edf4)}
4
+ </style>
5
+ <section class="arcane-card"><header class="arcane-card__header heading"><div><h2 id="title" class="arcane-section-heading">Connected services</h2><p id="description" class="arcane-help">Choose the services that appear in this application.</p></div><button id="close" class="arcane-button arcane-button--tertiary" type="button">Close</button></header><div class="arcane-card__body"><p class="notice">Passwords and API secrets are never stored here. Arcane delegates credentials to the local communications bridge or the provider's authorization flow.</p><div id="services" class="services"></div></div><footer class="arcane-card__footer"><p id="message" class="arcane-help" role="status" aria-live="polite"></p><button id="save" class="arcane-button" type="button">Save services</button></footer></section>
6
+ <script type="module">
7
+ const host=this,root=host.shadowRoot,services=root.querySelector('#services'),message=root.querySelector('#message');let descriptors=[],values={};
8
+ host.configure=options=>{descriptors=Array.from(options?.services||[]);values={...(options?.values||{})};root.querySelector('#title').textContent=options?.title||'Connected services';root.querySelector('#description').textContent=options?.description||'Choose the services that appear in this application.';render();return host};host.getValues=()=>read();host.setStatus=value=>message.textContent=String(value||'');
9
+ root.querySelector('#close').addEventListener('click',()=>host.dispatchEvent(new CustomEvent('integration-settings-close',{bubbles:true,composed:true})));root.querySelector('#save').addEventListener('click',()=>host.dispatchEvent(new CustomEvent('integration-settings-save',{bubbles:true,composed:true,detail:{values:read()}})));
10
+ function render(){services.replaceChildren(...descriptors.map(item=>{const section=document.createElement('section');section.className='service';section.dataset.id=item.id;const head=document.createElement('div');head.className='service-head';const enabled=document.createElement('input');enabled.type='checkbox';enabled.name='enabled';enabled.checked=Boolean(values[item.id]?.enabled);enabled.setAttribute('aria-label',`Enable ${item.label}`);const copy=document.createElement('div');copy.className='service-copy';const label=document.createElement('strong');label.textContent=item.label;const description=document.createElement('p');description.textContent=item.description||'';const state=document.createElement('span');state.className='status';state.textContent=values[item.id]?.status||item.defaultStatus||'Disconnected';copy.append(label,description);head.append(enabled,copy,state);section.append(head);const fields=document.createElement('div');fields.className='fields';if(item.acceptsEndpoint){fields.append(field('Bridge URL','endpoint',values[item.id]?.endpoint||item.defaultEndpoint||'http://127.0.0.1:8020'),item.id)}if(item.acceptsAccountLabel!==false)fields.append(field('Account label','accountLabel',values[item.id]?.accountLabel||'',item.id));if(fields.childElementCount)section.append(fields);if(item.actionLabel){const actions=document.createElement('div');actions.className='actions';const action=document.createElement('button');action.type='button';action.className='arcane-button arcane-button--secondary';action.textContent=item.actionLabel;action.addEventListener('click',()=>host.dispatchEvent(new CustomEvent('integration-action',{bubbles:true,composed:true,detail:{service:item,values:read()[item.id]}})));actions.append(action);section.append(actions)}return section}))}
11
+ function field(labelText,name,value,id){const label=document.createElement('label');label.className='arcane-field';const span=document.createElement('span');span.textContent=labelText;const input=document.createElement('input');input.name=name;input.value=value;input.dataset.service=id;input.autocomplete='off';label.append(span,input);return label}
12
+ function read(){return Object.fromEntries(descriptors.map(item=>{const section=services.querySelector(`[data-id="${CSS.escape(item.id)}"]`);return [item.id,{enabled:section.querySelector('[name="enabled"]').checked,endpoint:section.querySelector('[name="endpoint"]')?.value.trim()||'',accountLabel:section.querySelector('[name="accountLabel"]')?.value.trim()||'',status:values[item.id]?.status||item.defaultStatus||'Disconnected'}]}))}
13
+ host.ready=true;host.dispatchEvent(new CustomEvent('integration-settings-ready',{bubbles:true,composed:true}));
14
+ </script>
@@ -0,0 +1,360 @@
1
+ <link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
2
+ <style>
3
+ :host{
4
+ display:block;
5
+ inset-block-start:4.5rem;
6
+ inset-inline-end:1rem;
7
+ max-width:min(34rem,calc(100vw - 2rem));
8
+ position:fixed;
9
+ width:max-content;
10
+ z-index:120;
11
+ }
12
+ :host([hidden]){display:none}
13
+ .panel{
14
+ background:var(--arcane-surface-raised,var(--modal-background));
15
+ border:1px solid var(--arcane-border,var(--border-color));
16
+ border-radius:var(--arcane-radius-3,.7rem);
17
+ box-shadow:0 .7rem 2rem var(--shadow-color);
18
+ color:var(--arcane-text,var(--text-color));
19
+ display:grid;
20
+ gap:var(--arcane-space-3,.75rem);
21
+ max-height:calc(100vh - 6rem);
22
+ overflow:auto;
23
+ padding:var(--arcane-space-4,1rem);
24
+ }
25
+ .heading{align-items:start;display:flex;gap:var(--arcane-space-3,.75rem);justify-content:space-between}
26
+ h2,p{margin:0}
27
+ h2{font-size:1.05rem;line-height:1.3}
28
+ .dismiss{
29
+ background:transparent;
30
+ border:0;
31
+ color:inherit;
32
+ cursor:pointer;
33
+ font:inherit;
34
+ min-height:2.5rem;
35
+ min-width:2.5rem;
36
+ padding:.35rem;
37
+ }
38
+ .dismiss:focus-visible,.action:focus-visible,summary:focus-visible{
39
+ outline:3px solid var(--arcane-focus,var(--focus-color));
40
+ outline-offset:2px;
41
+ }
42
+ .services{display:grid;gap:.45rem;list-style:none;margin:0;padding:0}
43
+ .service{align-items:start;display:grid;gap:.25rem;grid-template-columns:minmax(7.5rem,auto) 1fr}
44
+ .service-name{font-weight:700}
45
+ .service-state{color:var(--arcane-muted,var(--muted-text-color));overflow-wrap:anywhere}
46
+ .service[data-state="ready"] .service-state{color:var(--success-color)}
47
+ .service[data-state="unavailable"] .service-state{color:var(--danger-color)}
48
+ .actions{display:flex;flex-wrap:wrap;gap:.55rem}
49
+ .action{
50
+ align-items:center;
51
+ background:var(--arcane-action,var(--button-background));
52
+ border:1px solid var(--arcane-border,var(--border-color));
53
+ border-radius:.5rem;
54
+ color:var(--arcane-action-text,var(--button-text-color));
55
+ cursor:pointer;
56
+ display:inline-flex;
57
+ font:inherit;
58
+ font-weight:700;
59
+ justify-content:center;
60
+ min-height:2.75rem;
61
+ padding:.65rem .9rem;
62
+ text-decoration:none;
63
+ }
64
+ .action.secondary{background:transparent;color:inherit}
65
+ details{border-block-start:1px solid var(--arcane-border,var(--border-color));padding-block-start:.7rem}
66
+ summary{cursor:pointer;font-weight:700;min-height:2.5rem;padding:.45rem 0}
67
+ details p{color:var(--arcane-muted,var(--muted-text-color));line-height:1.45}
68
+ @media(max-width:36rem){
69
+ :host{inset-block-start:auto;inset-block-end:1rem;inset-inline:1rem;max-width:none;width:auto}
70
+ .service{grid-template-columns:1fr}
71
+ .actions{display:grid}
72
+ }
73
+ @media(forced-colors:active){.panel,.action{border:1px solid CanvasText}}
74
+ </style>
75
+
76
+ <section id="panel" class="panel" aria-labelledby="localAIStatusTitle" aria-busy="false">
77
+ <header class="heading">
78
+ <div>
79
+ <h2 id="localAIStatusTitle">Checking local AI</h2>
80
+ <p id="summary" role="status" aria-live="polite">Checking the selected local services.</p>
81
+ </div>
82
+ <button id="dismiss" class="dismiss" type="button" aria-label="Dismiss local AI status">&#x2715;</button>
83
+ </header>
84
+ <ul id="services" class="services" aria-label="Selected local AI services"></ul>
85
+ <div id="actions" class="actions" hidden>
86
+ <button id="retry" class="action" type="button">Retry</button>
87
+ <button id="settings" class="action secondary" type="button">Open Arcane Settings</button>
88
+ <a id="profile" class="action secondary">Open Profile</a>
89
+ </div>
90
+ <p id="nativeHelp" role="status" tabindex="-1" hidden>From the Arcane OS Home screen, open Settings, choose Local AI, review the signed service diagnostics, and then return here to Retry.</p>
91
+ <details id="manual" hidden>
92
+ <summary>Start local services manually</summary>
93
+ <p id="manualText">Open the Ollama application or run <code>ollama serve</code>. If local transcription or speech is selected, also start your installed Arcane speech runtime. Then return here and choose Retry.</p>
94
+ </details>
95
+ </section>
96
+
97
+ <script type="module">
98
+ const host=this;
99
+ const root=host.shadowRoot;
100
+ const panel=root.querySelector('#panel');
101
+ const title=root.querySelector('#localAIStatusTitle');
102
+ const summary=root.querySelector('#summary');
103
+ const services=root.querySelector('#services');
104
+ const actions=root.querySelector('#actions');
105
+ const retry=root.querySelector('#retry');
106
+ const settings=root.querySelector('#settings');
107
+ const profile=root.querySelector('#profile');
108
+ const nativeHelp=root.querySelector('#nativeHelp');
109
+ const manual=root.querySelector('#manual');
110
+ const manualText=root.querySelector('#manualText');
111
+ const dismiss=root.querySelector('#dismiss');
112
+ const labels=Object.freeze({
113
+ llm:'Language model',
114
+ stt:'Transcription',
115
+ tts:'Spoken responses'
116
+ });
117
+ const readyDismissDelay=5000;
118
+ let options={};
119
+ let readyDismissTimer=null;
120
+ let readyForDismiss=false;
121
+
122
+ host.begin=begin;
123
+ host.configure=configure;
124
+ host.present=present;
125
+
126
+ retry.addEventListener('click',function dispatchRetryRequest(){
127
+ host.dispatchEvent(new CustomEvent('local-ai-retry',{
128
+ bubbles:true,
129
+ composed:true
130
+ }));
131
+ });
132
+ settings.addEventListener('click',function showNativeRecoveryHelp(){
133
+ nativeHelp.hidden=false;
134
+ nativeHelp.focus?.();
135
+ });
136
+ dismiss.addEventListener('click',function dismissStatusManually(){
137
+ hideStatus('manual');
138
+ });
139
+ host.addEventListener('pointerenter',cancelReadyDismiss);
140
+ host.addEventListener('pointerleave',scheduleReadyDismiss);
141
+ host.addEventListener('focusin',cancelReadyDismiss);
142
+ host.addEventListener('focusout',scheduleReadyDismiss);
143
+
144
+ function cancelReadyDismiss(){
145
+ if(readyDismissTimer!==null){
146
+ clearTimeout(readyDismissTimer);
147
+ readyDismissTimer=null;
148
+ }
149
+ }
150
+
151
+ function scheduleReadyDismiss(){
152
+ cancelReadyDismiss();
153
+ if(!readyForDismiss){
154
+ return;
155
+ }
156
+ readyDismissTimer=setTimeout(autoDismissReadyStatus,readyDismissDelay);
157
+ }
158
+
159
+ function autoDismissReadyStatus(){
160
+ readyDismissTimer=null;
161
+ if(!readyForDismiss){
162
+ return;
163
+ }
164
+ if(host.matches(':hover')||root.activeElement){
165
+ return;
166
+ }
167
+ hideStatus('ready-timeout');
168
+ }
169
+
170
+ function hideStatus(reason){
171
+ cancelReadyDismiss();
172
+ readyForDismiss=false;
173
+ host.hidden=true;
174
+ host.dispatchEvent(new CustomEvent('local-ai-status-dismissed',{
175
+ bubbles:true,
176
+ composed:true,
177
+ detail:{reason}
178
+ }));
179
+ }
180
+
181
+ function configure(input={}){
182
+ options={
183
+ profileHref:host.dataset.profileHref||null,
184
+ ...options,
185
+ ...input
186
+ };
187
+ if(options.profileHref){
188
+ profile.href=new URL(options.profileHref,document.baseURI).href;
189
+ }else{
190
+ profile.removeAttribute('href');
191
+ }
192
+ return {...options};
193
+ }
194
+
195
+ function begin(requirements={}){
196
+ readyForDismiss=false;
197
+ cancelReadyDismiss();
198
+ host.hidden=false;
199
+ panel.setAttribute('aria-busy','true');
200
+ title.textContent='Checking local AI';
201
+ summary.setAttribute('role','status');
202
+ summary.textContent='Checking the selected local services.';
203
+ actions.hidden=true;
204
+ manual.hidden=true;
205
+ nativeHelp.hidden=true;
206
+ renderServices(requirements,'checking');
207
+ }
208
+
209
+ function present(report={}){
210
+ const runtime=report.runtime||{};
211
+ const results=report.slots||report.results||report;
212
+ const requiredEntries=Object.keys(labels).map(function createRequiredEntry(key){
213
+ return [
214
+ key,
215
+ normalizeResult(results?.[key])
216
+ ];
217
+ }).filter(function entryIsRequired(entry){
218
+ return entry[1].required;
219
+ });
220
+ const unavailable=requiredEntries.filter(function entryIsUnavailable(entry){
221
+ return !entry[1].ready;
222
+ });
223
+ const userManaged=report.mode==='user-managed-loopback';
224
+ const guidanceMessage=String(report?.guidance?.message||'').trim();
225
+ const browser=runtime.managedLocalAI===false||runtime.native===false||report.mode==='browser';
226
+ const manualRecovery=browser||userManaged;
227
+
228
+ if(requiredEntries.length===0){
229
+ readyForDismiss=false;
230
+ cancelReadyDismiss();
231
+ host.hidden=true;
232
+ return report;
233
+ }
234
+
235
+ host.hidden=false;
236
+ panel.setAttribute('aria-busy','false');
237
+ renderEntries(requiredEntries);
238
+ actions.hidden=unavailable.length===0;
239
+ manual.hidden=!manualRecovery||unavailable.length===0;
240
+ settings.hidden=manualRecovery;
241
+ profile.hidden=!manualRecovery;
242
+ nativeHelp.hidden=true;
243
+ manualText.textContent=userManaged
244
+ ?guidanceMessage
245
+ ||'Review the unavailable user-managed Android service, then return here and choose Retry. Arcane does not install, start, repair, pull, or otherwise manage local AI services or models.'
246
+ :'Open the Ollama application or run ollama serve. If local transcription or speech is selected, also start your installed Arcane speech runtime. Then return here and choose Retry.';
247
+
248
+ if(unavailable.length===0){
249
+ title.textContent='Local AI is ready';
250
+ summary.setAttribute('role','status');
251
+ summary.textContent=userManaged
252
+ ?'The Android host admitted the selected model through your reachable Android Linux Terminal/Ollama service. Arcane does not manage its lifecycle.'
253
+ :'The selected local model and speech services are available.';
254
+ readyForDismiss=true;
255
+ scheduleReadyDismiss();
256
+ return report;
257
+ }
258
+
259
+ readyForDismiss=false;
260
+ cancelReadyDismiss();
261
+ title.textContent='Local AI needs attention';
262
+ summary.setAttribute('role','alert');
263
+ summary.textContent=userManaged
264
+ ?guidanceMessage
265
+ ||'Review the unavailable user-managed Android service, then Retry or open Profile and switch the affected option to OpenAI.'
266
+ :browser
267
+ ?'Start the unavailable local service yourself and Retry, or open Profile, switch each affected option to OpenAI, and enter your OpenAI API key.'
268
+ :'Arcane could not recover every selected managed local service. Retry or open Arcane Settings > Local AI.';
269
+ return report;
270
+ }
271
+
272
+ function positiveSafeInteger(value){
273
+ return Number.isSafeInteger(value)&&value>0
274
+ ?value
275
+ :null;
276
+ }
277
+
278
+ function normalizeResult(value={}){
279
+ if(typeof value==='boolean'){
280
+ return {
281
+ required:true,
282
+ ready:value,
283
+ activeParallelRequests:null,
284
+ message:''
285
+ };
286
+ }
287
+ return {
288
+ required:Boolean(value?.required),
289
+ ready:Boolean(value?.ready),
290
+ activeParallelRequests:positiveSafeInteger(
291
+ value?.activeParallelRequests
292
+ ),
293
+ message:String(value?.message||value?.reason||value?.errorCode||'')
294
+ };
295
+ }
296
+
297
+ function renderServices(requirements,state){
298
+ const entries=Object.keys(labels).map(function createCheckingEntry(key){
299
+ return [
300
+ key,
301
+ {
302
+ required:Boolean(requirements?.[key]?.required??requirements?.[key]),
303
+ ready:false,
304
+ activeParallelRequests:null,
305
+ message:''
306
+ }
307
+ ];
308
+ }).filter(function checkingEntryIsRequired(entry){
309
+ return entry[1].required;
310
+ });
311
+ renderEntries(entries,state);
312
+ }
313
+
314
+ function serviceStatusText(result={},base=''){
315
+ const parallelRequests=positiveSafeInteger(
316
+ result.activeParallelRequests
317
+ );
318
+
319
+ if(parallelRequests===null){
320
+ return base;
321
+ }
322
+
323
+ return `${base} — numParallel ${parallelRequests}`;
324
+ }
325
+
326
+ function renderEntries(entries,state=''){
327
+ const fragment=document.createDocumentFragment();
328
+ for(const [key,result] of entries){
329
+ const item=document.createElement('li');
330
+ const name=document.createElement('span');
331
+ const status=document.createElement('span');
332
+ const itemState=state==='checking'
333
+ ?'checking'
334
+ :result.ready
335
+ ?'ready'
336
+ :'unavailable';
337
+ item.className='service';
338
+ item.dataset.state=itemState;
339
+ name.className='service-name';
340
+ name.textContent=labels[key];
341
+ status.className='service-state';
342
+ status.textContent=state==='checking'
343
+ ?'Checking...'
344
+ :result.ready
345
+ ?serviceStatusText(result,'Ready')
346
+ :serviceStatusText(
347
+ result,
348
+ result.message||'Unavailable'
349
+ );
350
+ item.append(name,status);
351
+ fragment.append(item);
352
+ }
353
+ services.replaceChildren(fragment);
354
+ }
355
+
356
+ configure();
357
+ host.hidden=true;
358
+ host.ready=true;
359
+ host.dispatchEvent(new CustomEvent('local-ai-status-ready',{bubbles:true,composed:true}));
360
+ </script>