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,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;max-width:34rem}.calculator{display:grid;gap:var(--arcane-space-3);padding:var(--arcane-space-4)}.display{display:grid;gap:var(--arcane-space-2)}.display input{font:600 1.3rem/1.4 ui-monospace,monospace;text-align:right}.result{min-height:2.5rem;margin:0;text-align:right;font-size:1.8rem;font-weight:750;overflow-wrap:anywhere}.keys{display:grid;gap:var(--arcane-space-2);grid-template-columns:repeat(5,1fr)}.keys button{min-width:0;padding:.7rem .35rem}.status{min-height:1.4rem;margin:0;color:var(--arcane-muted)}@media(max-width:28rem){.keys{grid-template-columns:repeat(4,1fr)}}</style>
3
+ <section class="calculator arcane-card"><div class="display arcane-field"><label for="expression">Expression</label><input id="expression" inputmode="decimal" autocomplete="off" spellcheck="false" value=""><p id="result" class="result" aria-live="polite">0</p></div><div id="keys" class="keys" aria-label="Calculator keypad"></div><p id="status" class="status" role="status"></p></section>
4
+ <script type="module">const host=this,root=host.shadowRoot,input=root.querySelector('#expression'),result=root.querySelector('#result'),status=root.querySelector('#status'),keys=root.querySelector('#keys');const componentURL=new URL(host.getAttribute('href')||'./arcane/components/calculator.html',document.baseURI);const {default:CalculatorEngine}=await import(new URL('../modules/CalculatorEngine.js',componentURL));const engine=new CalculatorEngine(),definitions=['7','8','9','/','sqrt(','4','5','6','*','^','1','2','3','-','%','0','.','(',')','+','pi','e','C','⌫','='];
5
+ for(const value of definitions){const button=document.createElement('button');button.type='button';button.className=value==='='?'arcane-button':'arcane-button arcane-button--secondary';button.textContent=value;button.dataset.value=value;keys.append(button)}
6
+ keys.addEventListener('click',event=>{const value=event.target.closest('[data-value]')?.dataset.value;if(!value)return;if(value==='C'){input.value='';result.textContent='0';status.textContent='';return}if(value==='⌫'){input.value=input.value.slice(0,-1);return}if(value==='='){calculate();return}input.value+=value;input.focus()});input.addEventListener('keydown',event=>{if(event.key==='Enter'){event.preventDefault();calculate()}});
7
+ function calculate(){try{const calculation=engine.calculate(input.value);result.textContent=new Intl.NumberFormat(undefined,{maximumSignificantDigits:14}).format(calculation.result);status.textContent='';host.dispatchEvent(new CustomEvent('calculation-complete',{bubbles:true,composed:true,detail:{calculation}}))}catch(error){status.textContent=error.message;host.dispatchEvent(new CustomEvent('calculation-error',{bubbles:true,composed:true,detail:{error,expression:input.value}}))}}
8
+ host.calculate=calculate;host.ready=true;host.dispatchEvent(new CustomEvent('calculator-ready',{bubbles:true,composed:true}));</script>
@@ -0,0 +1,655 @@
1
+ <link rel="stylesheet" href="./arcane/css/layout.css?v=3">
2
+ <link rel="stylesheet" href="./arcane/modules/uPlot.min.css">
3
+ <style>
4
+ :host{
5
+ display:block;
6
+ min-height:18em;
7
+ min-width:0;
8
+ }
9
+
10
+ .chart{
11
+ background:var(--chart-background,var(--background));
12
+ border:.1em solid var(--chart-grid-color,var(--border-color));
13
+ border-radius:.5em;
14
+ box-sizing:border-box;
15
+ color:var(--chart-text-color,var(--text-color));
16
+ display:grid;
17
+ grid-template-rows:auto auto minmax(11em,1fr) auto auto;
18
+ height:100%;
19
+ min-height:18em;
20
+ padding:1em;
21
+ position:relative;
22
+ }
23
+
24
+ h2{
25
+ font-size:1.15em;
26
+ margin:0 2em .4em 0;
27
+ }
28
+
29
+ .chart-info{
30
+ font-size:.88em;
31
+ margin:0 0 .65em;
32
+ opacity:.75;
33
+ }
34
+
35
+ .remove-chart{
36
+ background:transparent;
37
+ border:0;
38
+ box-shadow:none;
39
+ color:inherit;
40
+ font-size:1.5em;
41
+ line-height:1;
42
+ margin:0;
43
+ opacity:.25;
44
+ padding:.2em;
45
+ position:absolute;
46
+ right:.35em;
47
+ top:.25em;
48
+ transition:opacity 150ms ease-out;
49
+ z-index:2;
50
+ }
51
+
52
+ .remove-chart:hover,
53
+ .remove-chart:focus-visible{
54
+ opacity:1;
55
+ }
56
+
57
+ .plot{
58
+ min-height:11em;
59
+ min-width:0;
60
+ outline:none;
61
+ overflow:hidden;
62
+ position:relative;
63
+ }
64
+
65
+ .plot:focus-visible{
66
+ outline:2px solid currentColor;
67
+ outline-offset:.2em;
68
+ }
69
+
70
+ .chart-value{
71
+ background:var(--modal-background);
72
+ border:1px solid var(--chart-grid-color,var(--border-color));
73
+ border-left:.35em solid var(--chart-line-color,var(--accent-color));
74
+ border-radius:.45em;
75
+ display:grid;
76
+ font-size:.85em;
77
+ gap:.25em 1em;
78
+ grid-template-columns:minmax(0,1fr) auto;
79
+ margin:.55em 0 0;
80
+ min-height:3.5em;
81
+ padding:.5em .65em;
82
+ transition:box-shadow 120ms ease-out,border-color 120ms ease-out;
83
+ }
84
+
85
+ .chart-value[data-state="hover"],
86
+ .chart-value[data-state="keyboard"]{
87
+ border-color:var(--chart-line-color,var(--accent-color));
88
+ box-shadow:0 0 0 .15em var(--chart-grid-color,var(--border-color));
89
+ }
90
+
91
+ .value-context{
92
+ font-weight:bold;
93
+ grid-column:1 / -1;
94
+ }
95
+
96
+ .value-context::before{
97
+ color:var(--chart-point-color,var(--accent-color));
98
+ content:'\25cf';
99
+ margin-right:.4em;
100
+ }
101
+
102
+ .value-field{
103
+ display:flex;
104
+ flex-direction:column;
105
+ min-width:0;
106
+ }
107
+
108
+ .value-field:last-child{
109
+ text-align:right;
110
+ }
111
+
112
+ .value-label{
113
+ font-size:.72em;
114
+ font-weight:bold;
115
+ letter-spacing:.06em;
116
+ opacity:.7;
117
+ text-transform:uppercase;
118
+ }
119
+
120
+ .value-date,
121
+ .value-score{
122
+ font-size:1.05em;
123
+ font-weight:bold;
124
+ }
125
+
126
+ .empty{
127
+ align-self:center;
128
+ font-size:.85em;
129
+ justify-self:center;
130
+ margin:.4em 0 0;
131
+ min-height:1.2em;
132
+ opacity:.65;
133
+ }
134
+
135
+ .hidden{
136
+ display:none;
137
+ }
138
+
139
+ .uplot{
140
+ font-family:inherit;
141
+ }
142
+ </style>
143
+
144
+ <section class="chart">
145
+ <button id="removeChart" class="remove-chart hidden" type="button" title="Remove chart" aria-label="Remove chart" disabled>&times;</button>
146
+ <h2 id="chartTitle"></h2>
147
+ <p id="chartInfo" class="chart-info hidden"></p>
148
+ <div id="plot" class="plot" tabindex="0" aria-describedby="chartValue"></div>
149
+ <section id="chartValue" class="chart-value" data-state="latest" aria-live="polite" aria-atomic="true">
150
+ <span id="valueContext" class="value-context"></span>
151
+ <span class="value-field">
152
+ <span id="timeLabel" class="value-label"></span>
153
+ <time id="valueDate" class="value-date"></time>
154
+ </span>
155
+ <span class="value-field">
156
+ <span id="valueLabel" class="value-label"></span>
157
+ <strong id="valueScore" class="value-score"></strong>
158
+ </span>
159
+ </section>
160
+ <p id="empty" class="empty hidden"></p>
161
+ </section>
162
+
163
+ <script type="module">
164
+ const host=this;
165
+ const componentURL=new URL(
166
+ host.getAttribute('href')||'./arcane/components/chart.html',
167
+ document.baseURI
168
+ );
169
+ const moduleURL=name=>new URL(`../modules/${name}`,componentURL).href;
170
+ const {default:loadChartLibrary}=await import(moduleURL('ChartLibrary.js'));
171
+ const {
172
+ normalizeChartOptions,
173
+ normalizeChartRows
174
+ }=await import(moduleURL('ComponentContracts.js'));
175
+ const title=host.shadowRoot.querySelector('#chartTitle');
176
+ const info=host.shadowRoot.querySelector('#chartInfo');
177
+ const plot=host.shadowRoot.querySelector('#plot');
178
+ const chartValue=host.shadowRoot.querySelector('#chartValue');
179
+ const valueContext=host.shadowRoot.querySelector('#valueContext');
180
+ const timeLabel=host.shadowRoot.querySelector('#timeLabel');
181
+ const valueDate=host.shadowRoot.querySelector('#valueDate');
182
+ const valueLabel=host.shadowRoot.querySelector('#valueLabel');
183
+ const valueScore=host.shadowRoot.querySelector('#valueScore');
184
+ const empty=host.shadowRoot.querySelector('#empty');
185
+ const removeButton=host.shadowRoot.querySelector('#removeChart');
186
+
187
+ let rows=[];
188
+ let chart=null;
189
+ let uPlot=null;
190
+ let selectedIndex=-1;
191
+ let resizeFrame=0;
192
+ let renderGeneration=0;
193
+ let options=normalizeChartOptions(optionsFromDataset());
194
+
195
+ host.addData=addData;
196
+ host.configure=configure;
197
+ host.destroy=destroy;
198
+ host.populate=populate;
199
+ host.setData=update;
200
+ host.update=update;
201
+
202
+ Object.defineProperties(
203
+ host,
204
+ {
205
+ chartStyle:{
206
+ get:()=>options.style,
207
+ set:value=>void configure({style:value})
208
+ },
209
+ chartTitle:{
210
+ get:()=>options.title,
211
+ set:value=>void configure({title:value})
212
+ },
213
+ dataSet:{
214
+ get:()=>rows.map(row=>row.slice()),
215
+ set:value=>void update(value)
216
+ },
217
+ options:{
218
+ get:getOptions,
219
+ set:value=>void configure(value)
220
+ }
221
+ }
222
+ );
223
+
224
+ removeButton.addEventListener(
225
+ 'click',
226
+ ()=>host.dispatchEvent(
227
+ new CustomEvent(
228
+ 'chart-remove',
229
+ {
230
+ bubbles:true,
231
+ composed:true,
232
+ detail:{key:options.key}
233
+ }
234
+ )
235
+ )
236
+ );
237
+ plot.addEventListener('keydown',navigatePoints);
238
+ plot.addEventListener(
239
+ 'pointerleave',
240
+ ()=>updateSelectedValue(selectedIndex,'selected')
241
+ );
242
+
243
+ const resizeObserver=typeof ResizeObserver==='function'
244
+ ?new ResizeObserver(scheduleResize)
245
+ :null;
246
+ resizeObserver?.observe(host);
247
+ if(!resizeObserver){
248
+ globalThis.addEventListener?.('resize',scheduleResize);
249
+ }
250
+
251
+ function optionsFromDataset(){
252
+ const dataset=host.dataset||{};
253
+ const mapped={};
254
+ const fields={
255
+ chartKey:'key',
256
+ chartStyle:'style',
257
+ color:'color',
258
+ emptyLabel:'emptyLabel',
259
+ info:'info',
260
+ removeLabel:'removeLabel',
261
+ seriesLabel:'seriesLabel',
262
+ timeLabel:'timeLabel',
263
+ title:'title',
264
+ unit:'unit',
265
+ valueLabel:'valueLabel',
266
+ xKey:'xKey',
267
+ yKey:'yKey'
268
+ };
269
+ for(const [source,target] of Object.entries(fields)){
270
+ if(dataset[source]!==undefined){
271
+ mapped[target]=dataset[source];
272
+ }
273
+ }
274
+ for(const field of ['min','max']){
275
+ if(dataset[field]!==undefined&&dataset[field]!==''){
276
+ mapped[field]=dataset[field];
277
+ }
278
+ }
279
+ if(dataset.removable!==undefined){
280
+ mapped.removable=dataset.removable==='true';
281
+ }
282
+ if(dataset.time!==undefined){
283
+ mapped.time=dataset.time!=='false';
284
+ }
285
+ return mapped;
286
+ }
287
+
288
+ async function configure(input={}){
289
+ const suppliedData=Object.prototype.hasOwnProperty.call(input||{},'data')
290
+ ||Object.prototype.hasOwnProperty.call(input||{},'dataset')
291
+ ||Object.prototype.hasOwnProperty.call(input||{},'datasets');
292
+ options=normalizeChartOptions(input,options);
293
+ renderOptions();
294
+
295
+ if(suppliedData){
296
+ return update(options.data);
297
+ }
298
+ if(rows.length&&uPlot){
299
+ createChart();
300
+ }
301
+ return getOptions();
302
+ }
303
+
304
+ async function populate(data=[],input={}){
305
+ options=normalizeChartOptions(input,options);
306
+ renderOptions();
307
+ return update(data);
308
+ }
309
+
310
+ async function update(data=[]){
311
+ rows=normalizeChartRows(data,options);
312
+ options={...options,data};
313
+ return renderRows();
314
+ }
315
+
316
+ async function renderRows(){
317
+ const generation=++renderGeneration;
318
+ selectedIndex=rows.length-1;
319
+
320
+ if(!rows.length){
321
+ destroyChart();
322
+ plot.classList.add('hidden');
323
+ chartValue.classList.add('hidden');
324
+ empty.classList.remove('hidden');
325
+ updateSelectedValue(-1);
326
+ return [];
327
+ }
328
+
329
+ plot.classList.remove('hidden');
330
+ chartValue.classList.remove('hidden');
331
+ empty.classList.add('hidden');
332
+ uPlot=uPlot||await loadChartLibrary();
333
+
334
+ if(generation!==renderGeneration){
335
+ return host.dataSet;
336
+ }
337
+ if(chart){
338
+ chart.setData(toChartData(rows));
339
+ updateSelectedValue(selectedIndex,'latest');
340
+ }else{
341
+ createChart();
342
+ }
343
+ return host.dataSet;
344
+ }
345
+
346
+ async function addData(data=[]){
347
+ const additions=normalizeChartRows(data,options);
348
+ const merged=new Map(rows.concat(additions).map(row=>[row[0],row]));
349
+ rows=[...merged.values()].sort((left,right)=>left[0]-right[0]);
350
+ options={...options,data:host.dataSet};
351
+ return renderRows();
352
+ }
353
+
354
+ function toChartData(data=[]){
355
+ return [
356
+ data.map(row=>options.time?row[0]/1000:row[0]),
357
+ data.map(row=>row[1])
358
+ ];
359
+ }
360
+
361
+ function createChart(){
362
+ if(!uPlot||!rows.length||plot.clientWidth<1){
363
+ return false;
364
+ }
365
+ const color=getChartColor(
366
+ '--chart-text-color',
367
+ '--text-color',
368
+ 'rgb(60,60,60)'
369
+ );
370
+ const lineColor=options.color||getChartColor(
371
+ '--chart-line-color',
372
+ '--accent-color',
373
+ 'rgb(118,87,213)'
374
+ );
375
+ const pointColor=getChartColor(
376
+ '--chart-point-color',
377
+ '--accent-color',
378
+ lineColor
379
+ );
380
+ const gridColor=getChartColor(
381
+ '--chart-grid-color',
382
+ '--border-color',
383
+ 'rgba(90,90,90,.35)'
384
+ );
385
+ const areaColor=getChartColor(
386
+ '--chart-area-color',
387
+ '--secondary-color',
388
+ lineColor
389
+ );
390
+ const height=Math.max(176,plot.clientHeight||176);
391
+ const width=Math.max(280,plot.clientWidth||280);
392
+ const defaultOptions={
393
+ width,
394
+ height,
395
+ cursor:{
396
+ drag:{x:true,y:false,setScale:true},
397
+ points:{size:8}
398
+ },
399
+ legend:{show:false},
400
+ scales:{
401
+ x:{time:options.time},
402
+ y:createYScale()
403
+ },
404
+ axes:[
405
+ {stroke:color,grid:{stroke:gridColor}},
406
+ {stroke:color,grid:{stroke:gridColor},size:38}
407
+ ],
408
+ series:[
409
+ {},
410
+ {
411
+ label:options.seriesLabel,
412
+ stroke:lineColor,
413
+ width:options.style==='points'?0:2,
414
+ fill:options.style==='area'?areaColor:null,
415
+ points:{show:true,stroke:pointColor,size:7,width:2}
416
+ }
417
+ ],
418
+ hooks:{
419
+ setCursor:[
420
+ instance=>{
421
+ if(Number.isInteger(instance.cursor.idx)){
422
+ selectedIndex=instance.cursor.idx;
423
+ updateSelectedValue(selectedIndex,'hover');
424
+ }
425
+ }
426
+ ]
427
+ }
428
+ };
429
+ const supplied=typeof options.chartOptions==='function'
430
+ ?options.chartOptions({
431
+ defaults:defaultOptions,
432
+ options:getOptions(),
433
+ rows:host.dataSet
434
+ })
435
+ :options.chartOptions;
436
+ if(supplied!==null&&supplied!==undefined&&(
437
+ !supplied||typeof supplied!=='object'||Array.isArray(supplied)
438
+ )){
439
+ throw new TypeError('chartOptions must resolve to an object or null');
440
+ }
441
+
442
+ const plotOptions=mergePlotOptions(defaultOptions,supplied||{});
443
+ destroyChart();
444
+ chart=new uPlot(
445
+ plotOptions,
446
+ toChartData(rows),
447
+ plot
448
+ );
449
+ updateSelectedValue(selectedIndex,'latest');
450
+ return true;
451
+ }
452
+
453
+ function createYScale(){
454
+ if(options.min===null&&options.max===null){
455
+ return {auto:true};
456
+ }
457
+ return {
458
+ auto:false,
459
+ range:(instance,dataMin,dataMax)=>[
460
+ options.min??dataMin,
461
+ options.max??dataMax
462
+ ]
463
+ };
464
+ }
465
+
466
+ function mergePlotOptions(defaults,supplied){
467
+ const merged={...defaults,...supplied};
468
+ merged.cursor={...defaults.cursor,...(supplied.cursor||{})};
469
+ merged.cursor.drag={
470
+ ...defaults.cursor.drag,
471
+ ...(supplied.cursor?.drag||{})
472
+ };
473
+ merged.cursor.points={
474
+ ...defaults.cursor.points,
475
+ ...(supplied.cursor?.points||{})
476
+ };
477
+ merged.scales={...defaults.scales,...(supplied.scales||{})};
478
+ if(Array.isArray(supplied.series)){
479
+ merged.series=defaults.series.map(
480
+ (series,index)=>({...series,...(supplied.series[index]||{})})
481
+ );
482
+ }
483
+ merged.hooks={...defaults.hooks};
484
+ for(const [name,callbacks] of Object.entries(supplied.hooks||{})){
485
+ merged.hooks[name]=[
486
+ ...(defaults.hooks[name]||[]),
487
+ ...(Array.isArray(callbacks)?callbacks:[callbacks])
488
+ ].filter(callback=>typeof callback==='function');
489
+ }
490
+ return merged;
491
+ }
492
+
493
+ function getChartColor(name='',fallbackName='',fallback=''){
494
+ const styles=getComputedStyle(host);
495
+ return styles.getPropertyValue(name).trim()
496
+ ||styles.getPropertyValue(fallbackName).trim()
497
+ ||fallback;
498
+ }
499
+
500
+ function navigatePoints(event){
501
+ if(!rows.length||!['ArrowLeft','ArrowRight','Home','End'].includes(event.key)){
502
+ return;
503
+ }
504
+ event.preventDefault();
505
+ if(event.key==='Home'){
506
+ selectedIndex=0;
507
+ }else if(event.key==='End'){
508
+ selectedIndex=rows.length-1;
509
+ }else if(event.key==='ArrowLeft'){
510
+ selectedIndex=Math.max(0,selectedIndex-1);
511
+ }else{
512
+ selectedIndex=Math.min(rows.length-1,selectedIndex+1);
513
+ }
514
+ updateSelectedValue(selectedIndex,'keyboard');
515
+ }
516
+
517
+ function updateSelectedValue(index=-1,state='selected'){
518
+ const row=rows[index];
519
+ if(!row){
520
+ valueContext.innerText=options.labels.noSelection;
521
+ valueDate.removeAttribute('datetime');
522
+ valueDate.innerText='';
523
+ valueScore.innerText='';
524
+ plot.setAttribute('aria-label',`${options.title}. ${options.labels.empty}`);
525
+ return;
526
+ }
527
+
528
+ const displayedTime=formatTime(row[0]);
529
+ const score=formatValue(row[1]);
530
+ const maximum=options.max===null?'':formatValue(options.max);
531
+ const contexts={
532
+ hover:options.labels.hover,
533
+ keyboard:options.labels.keyboard,
534
+ latest:options.labels.latest,
535
+ selected:options.labels.selected
536
+ };
537
+
538
+ chartValue.dataset.state=state;
539
+ valueContext.innerText=contexts[state]||contexts.selected;
540
+ if(options.time&&Number.isFinite(new Date(row[0]).getTime())){
541
+ valueDate.dateTime=new Date(row[0]).toISOString();
542
+ }else{
543
+ valueDate.removeAttribute('datetime');
544
+ }
545
+ valueDate.innerText=displayedTime;
546
+ valueScore.innerText=maximum?`${score} / ${maximum}`:score;
547
+ const valueDescription=maximum?`${score} / ${maximum}`:score;
548
+ plot.setAttribute(
549
+ 'aria-label',
550
+ `${options.title}. ${options.labels.time}: ${displayedTime}. ${options.labels.value}: ${valueDescription}. ${options.labels.navigation}`
551
+ );
552
+ }
553
+
554
+ function formatTime(value){
555
+ if(options.formatTime){
556
+ return `${options.formatTime(value)}`;
557
+ }
558
+ if(!options.time){
559
+ return `${value}`;
560
+ }
561
+ const date=new Date(value);
562
+ return Number.isFinite(date.getTime())?date.toLocaleString():`${value}`;
563
+ }
564
+
565
+ function formatValue(value=0){
566
+ const formatted=options.formatValue
567
+ ?`${options.formatValue(value)}`
568
+ :Number.isInteger(value)
569
+ ?`${value}`
570
+ :value.toFixed(2).replace(/0+$/,'').replace(/\.$/,'');
571
+ return options.unit?`${formatted} ${options.unit}`:formatted;
572
+ }
573
+
574
+ function renderOptions(){
575
+ title.innerText=options.title;
576
+ info.innerText=options.info;
577
+ info.classList.toggle('hidden',!options.info);
578
+ timeLabel.innerText=options.labels.time;
579
+ valueLabel.innerText=options.labels.value||options.seriesLabel;
580
+ empty.innerText=options.labels.empty;
581
+ removeButton.classList.toggle('hidden',!options.removable);
582
+ removeButton.disabled=!options.removable;
583
+ const removeLabel=options.removeLabel||`Remove ${options.title}`;
584
+ removeButton.setAttribute('aria-label',removeLabel);
585
+ removeButton.title=removeLabel;
586
+ if(options.key){
587
+ host.dataset.chartKey=options.key;
588
+ }else{
589
+ delete host.dataset.chartKey;
590
+ }
591
+ }
592
+
593
+ function getOptions(){
594
+ return {
595
+ ...options,
596
+ labels:{...options.labels},
597
+ chartOptions:typeof options.chartOptions==='object'&&options.chartOptions!==null
598
+ ?{...options.chartOptions}
599
+ :options.chartOptions,
600
+ data:Array.isArray(options.data)?options.data.slice():options.data
601
+ };
602
+ }
603
+
604
+ function scheduleResize(){
605
+ if(resizeFrame){
606
+ cancelAnimationFrame(resizeFrame);
607
+ }
608
+ resizeFrame=requestAnimationFrame(resizeChart);
609
+ }
610
+
611
+ function resizeChart(){
612
+ resizeFrame=0;
613
+ if(plot.clientWidth<1){
614
+ return;
615
+ }
616
+ if(!chart&&rows.length){
617
+ createChart();
618
+ return;
619
+ }
620
+ if(!chart){
621
+ return;
622
+ }
623
+ chart.setSize({
624
+ width:Math.max(280,plot.clientWidth),
625
+ height:Math.max(176,plot.clientHeight||176)
626
+ });
627
+ }
628
+
629
+ function destroyChart(){
630
+ chart?.destroy();
631
+ chart=null;
632
+ plot.replaceChildren();
633
+ }
634
+
635
+ function destroy(){
636
+ renderGeneration++;
637
+ resizeObserver?.disconnect();
638
+ if(!resizeObserver){
639
+ globalThis.removeEventListener?.('resize',scheduleResize);
640
+ }
641
+ if(resizeFrame){
642
+ cancelAnimationFrame(resizeFrame);
643
+ resizeFrame=0;
644
+ }
645
+ destroyChart();
646
+ rows=[];
647
+ }
648
+
649
+ renderOptions();
650
+ updateSelectedValue(-1);
651
+ host.ready=true;
652
+ host.dispatchEvent(
653
+ new CustomEvent('chart-ready',{bubbles:true,composed:true})
654
+ );
655
+ </script>