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,135 @@
1
+ <link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
2
+ <style>
3
+ :host{display:block;min-width:0}
4
+ form{display:grid;gap:var(--arcane-space-4)}
5
+ .heading{display:grid;gap:var(--arcane-space-1)}
6
+ .heading p{margin:0}
7
+ .fields{display:grid;gap:var(--arcane-space-3)}
8
+ .preference{align-items:center;display:grid;gap:var(--arcane-space-3);grid-template-columns:minmax(0,1fr) minmax(10rem,16rem)}
9
+ .copy{display:grid;gap:var(--arcane-space-1)}
10
+ .copy label{font-weight:700}
11
+ .copy p{margin:0}
12
+ .control input[type="checkbox"]{height:1.25rem;width:1.25rem}
13
+ .control :where(input:not([type="checkbox"]),select){width:100%}
14
+ .status{min-height:1.4em;margin:0}
15
+ @media(max-width:36rem){.preference{grid-template-columns:1fr}}
16
+ </style>
17
+
18
+ <form class="arcane-card">
19
+ <header class="arcane-card__header heading">
20
+ <h2 id="title" class="arcane-section-heading">Preferences</h2>
21
+ <p id="description" class="arcane-help"></p>
22
+ </header>
23
+ <div class="arcane-card__body fields" id="fields"></div>
24
+ <footer class="arcane-card__footer">
25
+ <p class="status arcane-help" id="status" role="status" aria-live="polite"></p>
26
+ <button class="arcane-button arcane-button--tertiary" id="reset" type="button">Restore defaults</button>
27
+ <button class="arcane-button" id="save" type="submit">Save changes</button>
28
+ </footer>
29
+ </form>
30
+
31
+ <script type="module">
32
+ const host=this;
33
+ const root=host.shadowRoot;
34
+ const form=root.querySelector('form');
35
+ const fields=root.querySelector('#fields');
36
+ const title=root.querySelector('#title');
37
+ const description=root.querySelector('#description');
38
+ const status=root.querySelector('#status');
39
+ const reset=root.querySelector('#reset');
40
+ const save=root.querySelector('#save');
41
+ let schema=[];
42
+
43
+ host.configure=configure;
44
+ host.getValues=getValues;
45
+ host.setValues=setValues;
46
+ host.setBusy=setBusy;
47
+ host.setStatus=setStatus;
48
+
49
+ form.addEventListener('submit',event=>{
50
+ event.preventDefault();
51
+ host.dispatchEvent(new CustomEvent('preferences-submit',{bubbles:true,composed:true,detail:{values:getValues()}}));
52
+ });
53
+ form.addEventListener('input',event=>{
54
+ const field=schema.find(item=>item.key===event.target.name);
55
+ if(!field) return;
56
+ host.dispatchEvent(new CustomEvent('preferences-change',{bubbles:true,composed:true,detail:{key:field.key,value:readControl(event.target,field),values:getValues()}}));
57
+ });
58
+ reset.addEventListener('click',()=>host.dispatchEvent(new CustomEvent('preferences-reset',{bubbles:true,composed:true})));
59
+
60
+ function configure(options={}){
61
+ schema=Array.from(options.schema||[]).map(item=>typeof item.toJSON==='function'?item.toJSON():{...item});
62
+ title.innerText=String(options.title||'Preferences');
63
+ description.innerText=String(options.description||'');
64
+ description.hidden=!description.innerText;
65
+ renderFields();
66
+ setValues(options.values||{});
67
+ return host;
68
+ }
69
+
70
+ function renderFields(){
71
+ const fragment=document.createDocumentFragment();
72
+ for(const item of schema){
73
+ const row=document.createElement('div');
74
+ const copy=document.createElement('div');
75
+ const label=document.createElement('label');
76
+ const help=document.createElement('p');
77
+ const controlWrap=document.createElement('div');
78
+ const control=createControl(item);
79
+ const id=`preference-${item.key.replace(/[^A-Za-z0-9_-]/g,'-')}`;
80
+ row.className='preference';copy.className='copy';help.className='arcane-help';controlWrap.className='control arcane-field';
81
+ label.htmlFor=id;label.innerText=String(item.label||item.key);help.innerText=String(item.description||'');help.hidden=!help.innerText;
82
+ control.id=id;control.name=item.key;
83
+ copy.append(label,help);controlWrap.append(control);row.append(copy,controlWrap);fragment.append(row);
84
+ }
85
+ fields.replaceChildren(fragment);
86
+ }
87
+
88
+ function createControl(item){
89
+ if(item.type==='select'){
90
+ const select=document.createElement('select');
91
+ for(const option of item.options||[]){
92
+ const element=document.createElement('option');element.value=String(option.value);element.innerText=String(option.label);select.append(element);
93
+ }
94
+ return select;
95
+ }
96
+ const input=document.createElement('input');
97
+ input.type=item.type==='boolean'?'checkbox':item.type==='number'?'number':'text';
98
+ if(item.minimum!==undefined) input.min=String(item.minimum);
99
+ if(item.maximum!==undefined) input.max=String(item.maximum);
100
+ if(item.step!==undefined) input.step=String(item.step);
101
+ return input;
102
+ }
103
+
104
+ function readControl(control,item){
105
+ if(item.type==='boolean') return control.checked;
106
+ if(item.type==='number') return control.valueAsNumber;
107
+ return control.value;
108
+ }
109
+
110
+ function getValues(){
111
+ return Object.fromEntries(schema.map(item=>{
112
+ const control=form.elements.namedItem(item.key);
113
+ return [item.key,readControl(control,item)];
114
+ }));
115
+ }
116
+
117
+ function setValues(values={}){
118
+ for(const item of schema){
119
+ const control=form.elements.namedItem(item.key);
120
+ const value=Object.prototype.hasOwnProperty.call(values,item.key)?values[item.key]:item.defaultValue;
121
+ if(item.type==='boolean') control.checked=Boolean(value); else control.value=String(value??'');
122
+ }
123
+ return getValues();
124
+ }
125
+
126
+ function setBusy(value=true){
127
+ const busy=Boolean(value);form.setAttribute('aria-busy',String(busy));
128
+ for(const control of form.elements) control.disabled=busy;
129
+ return busy;
130
+ }
131
+ function setStatus(message=''){status.innerText=String(message||'');return status.innerText;}
132
+
133
+ host.ready=true;
134
+ host.dispatchEvent(new CustomEvent('preferences-form-ready',{bubbles:true,composed:true}));
135
+ </script>
@@ -0,0 +1,105 @@
1
+ <style>
2
+ :host{display:block;color:var(--text-color);font-family:Inter,Segoe UI,sans-serif}
3
+ *{box-sizing:border-box}
4
+ .timeline{display:grid;gap:.7rem;margin:0;padding:0;list-style:none}
5
+ .event{display:grid;grid-template-columns:minmax(8.5rem,10rem) minmax(0,1fr);gap:1rem;position:relative;padding:0 0 1rem}
6
+ .event::before{content:'';position:absolute;left:9.45rem;top:2rem;bottom:-.15rem;width:.12rem;background:var(--border-color)}
7
+ .event:last-child::before{display:none}
8
+ .date{display:grid;align-content:start;gap:.2rem;padding:.65rem .7rem;border:1px solid var(--border-color);border-radius:.55rem;background:var(--modal-background);font-weight:800;text-align:right}
9
+ .date small{color:var(--platinum);font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}
10
+ .open{display:grid;gap:.45rem;width:100%;margin:0;padding:.8rem 1rem;border:1px solid var(--border-color);border-left:.35rem solid var(--accent-color);border-radius:.55rem;background:var(--modal-background);box-shadow:none;color:var(--text-color);text-align:left}
11
+ .open:hover,.open:focus-visible{background:var(--secondary-color);color:var(--text-color);box-shadow:none}
12
+ .open:focus-visible{outline:.18rem solid var(--focus-color);outline-offset:.12rem}
13
+ .heading{display:flex;align-items:start;justify-content:space-between;gap:.7rem}
14
+ .heading strong{font-size:.94rem;line-height:1.35}
15
+ .category{flex:0 0 auto;padding:.2rem .45rem;border:1px solid var(--border-color);border-radius:99rem;font-size:.65rem;font-weight:800;text-transform:uppercase;letter-spacing:.04em}
16
+ .summary{color:var(--platinum);font-size:.78rem;line-height:1.45}
17
+ .meta{display:flex;gap:.45rem;flex-wrap:wrap;color:var(--platinum);font-size:.68rem}
18
+ .meta span+span::before{content:'·';margin-right:.45rem}
19
+ .empty{padding:1.2rem;border:1px dashed var(--border-color);border-radius:.55rem;color:var(--platinum);text-align:center}
20
+ @media(max-width:42rem){.event{grid-template-columns:1fr;gap:.45rem}.event::before{display:none}.date{text-align:left}.heading{display:grid}.category{justify-self:start}}
21
+ @media(forced-colors:active){.open{border-color:CanvasText}.category{border-color:CanvasText}}
22
+ </style>
23
+
24
+ <ol class="timeline" aria-label="Chronology" data-list></ol>
25
+ <p class="empty" data-empty>No dated events match this view.</p>
26
+
27
+ <script type="module">
28
+ const host=this;
29
+ const list=host.shadowRoot.querySelector('[data-list]');
30
+ const empty=host.shadowRoot.querySelector('[data-empty]');
31
+ let items=[];
32
+ let options={emptyLabel:'No dated events match this view.'};
33
+
34
+ host.setItems=setItems;
35
+ host.populate=(value,input={})=>setItems(value,input);
36
+
37
+ list.addEventListener('click',event=>{
38
+ const trigger=event.target.closest('[data-event-id]');
39
+ if(!trigger) return;
40
+ const item=items.find(candidate=>candidate.id===trigger.dataset.eventId);
41
+ if(!item) return;
42
+ host.dispatchEvent(new CustomEvent('record-timeline-open',{bubbles:true,composed:true,detail:{id:item.id,item:{...item}}}));
43
+ });
44
+
45
+ function normalizeItem(item,index){
46
+ const source=item&&typeof item==='object'?item:{};
47
+ const date=String(source.date||source.isoDate||'').slice(0,32);
48
+ if(!source.id||!date) return null;
49
+ return {
50
+ ...source,
51
+ id:String(source.id).slice(0,180),
52
+ date,
53
+ title:String(source.title||'Dated event').slice(0,500),
54
+ summary:String(source.summary||'').slice(0,2400),
55
+ category:String(source.category||source.kind||'Event').slice(0,100),
56
+ actor:String(source.actor||'Actor not assigned').slice(0,180),
57
+ sourceLabel:String(source.sourceLabel||'').slice(0,240),
58
+ status:String(source.status||'').slice(0,120),
59
+ factualPosture:String(source.factualPosture||'').slice(0,160),
60
+ sourceCount:Math.max(0,Math.min(9999,Number(source.sourceCount)||0)),
61
+ order:index
62
+ };
63
+ }
64
+
65
+ function setItems(value=[],input={}){
66
+ options={...options,...(input&&typeof input==='object'?input:{})};
67
+ items=(Array.isArray(value)?value:[]).slice(0,5000).map(normalizeItem).filter(Boolean)
68
+ .sort((left,right)=>left.date.localeCompare(right.date)||left.order-right.order);
69
+ render();
70
+ return items.map(item=>({...item}));
71
+ }
72
+
73
+ function displayDate(value=''){
74
+ const parsed=new Date(`${value.slice(0,10)}T00:00:00Z`);
75
+ return Number.isFinite(parsed.getTime())
76
+ ?new Intl.DateTimeFormat(undefined,{year:'numeric',month:'short',day:'numeric',timeZone:'UTC'}).format(parsed)
77
+ :value;
78
+ }
79
+
80
+ function render(){
81
+ list.replaceChildren();
82
+ empty.textContent=String(options.emptyLabel||'No dated events match this view.');
83
+ empty.hidden=Boolean(items.length);
84
+ for(const item of items){
85
+ const row=document.createElement('li'); row.className='event';
86
+ const time=document.createElement('time'); time.className='date'; time.dateTime=item.date; time.textContent=displayDate(item.date);
87
+ if(item.datePrecision&&item.datePrecision!=='day'){
88
+ const precision=document.createElement('small'); precision.textContent=String(item.datePrecision); time.append(precision);
89
+ }
90
+ const button=document.createElement('button'); button.type='button'; button.className='open'; button.dataset.eventId=item.id;
91
+ const heading=document.createElement('span'); heading.className='heading';
92
+ const title=document.createElement('strong'); title.textContent=item.title;
93
+ const category=document.createElement('span'); category.className='category'; category.textContent=item.category;
94
+ heading.append(title,category); button.append(heading);
95
+ if(item.summary){const summary=document.createElement('span'); summary.className='summary'; summary.textContent=item.summary; button.append(summary);}
96
+ const meta=document.createElement('span'); meta.className='meta';
97
+ for(const value of [item.actor,item.status,item.factualPosture,item.sourceLabel,item.sourceCount?`${item.sourceCount} source${item.sourceCount===1?'':'s'}`:'']){
98
+ if(!value) continue; const part=document.createElement('span'); part.textContent=value; meta.append(part);
99
+ }
100
+ button.append(meta); row.append(time,button); list.append(row);
101
+ }
102
+ }
103
+
104
+ host.dispatchEvent(new CustomEvent('record-timeline-ready',{bubbles:true,composed:true}));
105
+ </script>
@@ -0,0 +1,116 @@
1
+ <style>
2
+ :host{display:block;color:var(--text-color);font-family:Inter,Segoe UI,sans-serif}
3
+ *{box-sizing:border-box}
4
+ .layout{display:grid;grid-template-columns:minmax(0,2.2fr) minmax(17rem,.8fr);gap:1rem;align-items:start}
5
+ .board{display:grid;grid-template-columns:repeat(var(--lane-count,4),minmax(12rem,1fr));gap:.7rem;overflow:auto;padding:.25rem}
6
+ .lane{display:grid;align-content:start;gap:.55rem;min-height:18rem;padding:.7rem;border:1px solid var(--border-color);border-radius:.65rem;background:var(--background)}
7
+ .lane h3{margin:0;padding:0 0 .5rem;border-bottom:1px solid var(--border-color);font-size:.75rem;text-transform:uppercase;letter-spacing:.08em}
8
+ .node{display:grid;gap:.28rem;width:100%;margin:0;padding:.65rem;border:1px solid var(--border-color);border-left:.32rem solid var(--accent-color);border-radius:.5rem;background:var(--modal-background);box-shadow:none;color:var(--text-color);text-align:left}
9
+ .node:hover,.node:focus-visible,.node[aria-pressed="true"]{background:var(--secondary-color);color:var(--text-color);box-shadow:none}
10
+ .node:focus-visible{outline:.18rem solid var(--focus-color);outline-offset:.12rem}
11
+ .node strong{font-size:.82rem;line-height:1.3}.node small{color:var(--platinum);line-height:1.35}
12
+ .connections{position:sticky;top:.7rem;display:grid;gap:.7rem;padding:.85rem;border:1px solid var(--border-color);border-radius:.65rem;background:var(--modal-background)}
13
+ .connections h3,.connections p{margin:0}.connections p{color:var(--platinum);font-size:.78rem;line-height:1.45}
14
+ .edge-list{display:grid;gap:.45rem;margin:0;padding:0;list-style:none}
15
+ .edge{width:100%;margin:0;padding:.55rem;border:1px solid var(--border-color);border-radius:.4rem;background:var(--background);box-shadow:none;color:var(--text-color);font-size:.75rem;text-align:left}
16
+ .edge:hover,.edge:focus-visible{background:var(--secondary-color);color:var(--text-color);box-shadow:none}.edge:focus-visible{outline:.18rem solid var(--focus-color);outline-offset:.12rem}
17
+ .empty{padding:.7rem;color:var(--platinum);font-size:.78rem}
18
+ @media(max-width:62rem){.layout{grid-template-columns:1fr}.connections{position:static}.board{grid-template-columns:repeat(var(--lane-count,4),minmax(15rem,1fr))}}
19
+ @media(forced-colors:active){.node,.edge{border-color:CanvasText}}
20
+ </style>
21
+
22
+ <section class="layout">
23
+ <div class="board" data-board aria-label="Relationship board"></div>
24
+ <aside class="connections" aria-live="polite" aria-atomic="true">
25
+ <h3 data-selected-title>Choose a card</h3>
26
+ <p data-selected-summary>Select a person, event, source, order, or hypothesis to see its record-based connections.</p>
27
+ <ul class="edge-list" data-edges></ul>
28
+ </aside>
29
+ </section>
30
+
31
+ <script type="module">
32
+ const host=this;
33
+ const board=host.shadowRoot.querySelector('[data-board]');
34
+ const selectedTitle=host.shadowRoot.querySelector('[data-selected-title]');
35
+ const selectedSummary=host.shadowRoot.querySelector('[data-selected-summary]');
36
+ const edgeList=host.shadowRoot.querySelector('[data-edges]');
37
+ let nodes=[]; let edges=[]; let lanes=[]; let selectedId='';
38
+
39
+ host.setGraph=setGraph;
40
+ host.populate=(graph={},options={})=>setGraph(graph,options);
41
+
42
+ board.addEventListener('click',event=>{
43
+ const trigger=event.target.closest('[data-node-id]');
44
+ if(!trigger) return;
45
+ selectedId=trigger.dataset.nodeId; renderSelection();
46
+ const node=nodes.find(item=>item.id===selectedId);
47
+ host.dispatchEvent(new CustomEvent('relationship-node-open',{bubbles:true,composed:true,detail:{id:selectedId,node:{...node}}}));
48
+ });
49
+ edgeList.addEventListener('click',event=>{
50
+ const trigger=event.target.closest('[data-edge-id]');
51
+ if(!trigger) return;
52
+ const edge=edges.find(item=>item.id===trigger.dataset.edgeId);
53
+ if(edge) host.dispatchEvent(new CustomEvent('relationship-edge-open',{bubbles:true,composed:true,detail:{id:edge.id,edge:{...edge}}}));
54
+ });
55
+
56
+ function normalizeNode(value,index){
57
+ const item=value&&typeof value==='object'?value:{};
58
+ if(!item.id) return null;
59
+ return {...item,id:String(item.id).slice(0,180),label:String(item.label||item.title||item.id).slice(0,500),summary:String(item.summary||'').slice(0,1800),lane:String(item.lane||item.kind||'Other').slice(0,100),order:Number.isFinite(Number(item.order))?Number(item.order):index};
60
+ }
61
+ function normalizeEdge(value,index,known){
62
+ const item=value&&typeof value==='object'?value:{};
63
+ const from=String(item.from||'').slice(0,180); const to=String(item.to||'').slice(0,180);
64
+ if(!from||!to||!known.has(from)||!known.has(to)) return null;
65
+ return {...item,id:String(item.id||`edge-${index+1}`).slice(0,180),from,to,label:String(item.label||'related to').slice(0,300),summary:String(item.summary||'').slice(0,1200)};
66
+ }
67
+
68
+ function setGraph(graph={},options={}){
69
+ nodes=(Array.isArray(graph.nodes)?graph.nodes:[]).slice(0,1500).map(normalizeNode).filter(Boolean);
70
+ const known=new Set(nodes.map(item=>item.id));
71
+ edges=(Array.isArray(graph.edges)?graph.edges:[]).slice(0,6000).map((item,index)=>normalizeEdge(item,index,known)).filter(Boolean);
72
+ const supplied=Array.isArray(options.lanes)?options.lanes:Array.isArray(graph.lanes)?graph.lanes:[];
73
+ lanes=[...new Set([...supplied.map(String),...nodes.map(item=>item.lane)])].slice(0,12);
74
+ board.style.setProperty('--lane-count',String(Math.max(1,Math.min(6,lanes.length))));
75
+ if(selectedId&&!known.has(selectedId)) selectedId='';
76
+ render();
77
+ return {nodes:nodes.map(item=>({...item})),edges:edges.map(item=>({...item}))};
78
+ }
79
+
80
+ function render(){
81
+ board.replaceChildren();
82
+ for(const laneName of lanes){
83
+ const lane=document.createElement('section'); lane.className='lane'; lane.setAttribute('aria-label',laneName);
84
+ const heading=document.createElement('h3'); heading.textContent=laneName; lane.append(heading);
85
+ const laneNodes=nodes.filter(node=>node.lane===laneName).sort((left,right)=>left.order-right.order||left.label.localeCompare(right.label));
86
+ for(const node of laneNodes){
87
+ const button=document.createElement('button'); button.type='button'; button.className='node'; button.dataset.nodeId=node.id; button.setAttribute('aria-pressed',String(node.id===selectedId));
88
+ const label=document.createElement('strong'); label.textContent=node.label; button.append(label);
89
+ if(node.summary){const summary=document.createElement('small'); summary.textContent=node.summary; button.append(summary);}
90
+ lane.append(button);
91
+ }
92
+ if(!laneNodes.length){const empty=document.createElement('p'); empty.className='empty'; empty.textContent='No cards in this lane.'; lane.append(empty);}
93
+ board.append(lane);
94
+ }
95
+ renderSelection();
96
+ }
97
+
98
+ function renderSelection(){
99
+ board.querySelectorAll('[data-node-id]').forEach(button=>button.setAttribute('aria-pressed',String(button.dataset.nodeId===selectedId)));
100
+ const selected=nodes.find(item=>item.id===selectedId);
101
+ selectedTitle.textContent=selected?.label||'Choose a card';
102
+ selectedSummary.textContent=selected?.summary||'Select a person, event, source, order, or hypothesis to see its record-based connections.';
103
+ edgeList.replaceChildren();
104
+ if(!selected) return;
105
+ const related=edges.filter(edge=>edge.from===selectedId||edge.to===selectedId);
106
+ for(const edge of related){
107
+ const otherId=edge.from===selectedId?edge.to:edge.from; const other=nodes.find(node=>node.id===otherId);
108
+ const row=document.createElement('li'); const button=document.createElement('button');
109
+ button.type='button'; button.className='edge'; button.dataset.edgeId=edge.id; button.textContent=`${edge.label}: ${other?.label||otherId}`;
110
+ row.append(button); edgeList.append(row);
111
+ }
112
+ if(!related.length){const row=document.createElement('li'); row.className='empty'; row.textContent='No displayed connections.'; edgeList.append(row);}
113
+ }
114
+
115
+ host.dispatchEvent(new CustomEvent('relationship-board-ready',{bubbles:true,composed:true}));
116
+ </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}.capture{display:grid;gap:var(--arcane-space-4)}.actions{display:grid;gap:var(--arcane-space-3);grid-template-columns:repeat(3,minmax(0,1fr))}.action{display:grid;gap:var(--arcane-space-2);padding:var(--arcane-space-4);text-align:left}.action strong{font-size:1.05rem}.action span{color:var(--arcane-muted);font-size:.85rem}.recording{align-items:center;display:flex;gap:var(--arcane-space-3);justify-content:space-between;padding:var(--arcane-space-3);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-medium)}.preview{display:grid;place-items:center;min-height:16rem;overflow:hidden;background:var(--arcane-surface-muted);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-large)}.preview :where(img,video){display:block;max-width:100%;max-height:34rem}.empty{color:var(--arcane-muted);text-align:center}.status{margin:0;color:var(--arcane-muted)}@media(max-width:44rem){.actions{grid-template-columns:1fr}}</style>
3
+ <section class="capture"><div class="actions"><button class="action arcane-card" type="button" data-capture="image"><strong>Screenshot</strong><span>Capture the selected display as PNG.</span></button><button class="action arcane-card" type="button" data-capture="video"><strong>Record video</strong><span>Record the selected display in a browser-supported video format.</span></button><button class="action arcane-card" type="button" data-capture="gif"><strong>Record GIF</strong><span>Record a compact, lower-frame-rate animated GIF.</span></button></div><div id="recording" class="recording" hidden><span id="recordingLabel">Recording…</span><button id="stop" class="arcane-button" type="button">Stop recording</button></div><div id="preview" class="preview"><div class="empty"><strong>No capture yet</strong><p>Your capture remains on this device unless you choose to save it.</p></div></div><p id="status" class="status" role="status" aria-live="polite">Choose a capture type. Your browser will ask which display to share.</p></section>
4
+ <script type="module">const host=this,root=host.shadowRoot,preview=root.querySelector('#preview'),recording=root.querySelector('#recording'),status=root.querySelector('#status');const componentURL=new URL(host.getAttribute('href')||'./arcane/components/screen-capture.html',document.baseURI);const {default:ScreenCapture}=await import(new URL('../modules/ScreenCapture.js',componentURL));const capture=new ScreenCapture();let previewUrl='';
5
+ root.querySelector('.actions').addEventListener('click',async event=>{const mode=event.target.closest('[data-capture]')?.dataset.capture;if(!mode)return;setButtons(true);try{if(mode==='image'){status.textContent='Choose a display to capture…';showResult(await capture.captureImage())}else{status.textContent='Choose a display to record…';if(mode==='video')await capture.startVideo();else await capture.startGif();recording.hidden=false;root.querySelector('#recordingLabel').textContent=mode==='gif'?'Recording GIF frames…':'Recording video…';status.textContent='Recording. Return here and press Stop when finished.'}}catch(error){status.textContent=error.message;setButtons(false)}});root.querySelector('#stop').addEventListener('click',async()=>{root.querySelector('#stop').disabled=true;status.textContent='Finishing capture…';try{showResult(await capture.stop())}catch(error){status.textContent=error.message}finally{recording.hidden=true;root.querySelector('#stop').disabled=false;setButtons(false)}});
6
+ function setButtons(value){for(const button of root.querySelectorAll('[data-capture]'))button.disabled=value}
7
+ function showResult(result){if(previewUrl)URL.revokeObjectURL(previewUrl);previewUrl=URL.createObjectURL(result.blob);const media=document.createElement(result.mimeType.startsWith('video/')?'video':'img');media.src=previewUrl;if(media.tagName==='VIDEO')media.controls=true;media.alt='Captured display preview';preview.replaceChildren(media);status.textContent=`Capture ready · ${(result.blob.size/1024/1024).toFixed(2)} MB`;setButtons(false);host.dispatchEvent(new CustomEvent('screen-capture-result',{bubbles:true,composed:true,detail:{result}}))}
8
+ host.capture=capture;host.ready=true;host.dispatchEvent(new CustomEvent('screen-capture-ready',{bubbles:true,composed:true}));</script>