arcane-os 0.66.0 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +1 -1
- package/browser-runtime/ai/core-image.mjs +84 -34
- package/browser-runtime/ai/core-local.mjs +195 -43
- package/browser-runtime/ai/core-onnx.mjs +71 -12
- package/docs/reference/README.md +6 -0
- package/docs/reference/availability-and-normalization.md +1 -0
- package/docs/reference/core-native-packaging.md +11 -4
- package/docs/reference/core-repositories.md +93 -9
- package/docs/reference/core-runtime.md +46 -5
- package/docs/reference/inventory/package-api.json +62 -20
- package/docs/reference/inventory/runtime-components.json +2 -1
- package/docs/reference/inventory/runtime-modules.json +3 -3
- package/docs/reference/local-ai.md +112 -2
- package/docs/reference/local-image-generation.md +15 -4
- package/docs/reference/model-assets.md +50 -3
- package/docs/reference/native-decisions.md +181 -0
- package/docs/reference/runtime-components.md +62 -5
- package/docs/reference/runtime-modules.md +44 -7
- package/docs/reference/sdk-api.md +229 -19
- package/package.json +5 -2
- package/runtime/arcane/components/theme-switcher.html +112 -13
- package/runtime/arcane/modules/PreferenceStore.js +18 -13
- package/runtime/arcane/modules/SystemAppearance.js +24 -6
- package/src/core/repositories.mjs +27 -8
- package/src/core/runtime.mjs +26 -1
- package/src/core/services/decisions.mjs +144 -0
- package/src/core/services/local-ai.mjs +31 -9
- package/src/core/services/model-assets.mjs +98 -13
- package/src/git-identity.mjs +22 -58
- package/src/local-ai/decision-tokenizer-worker.mjs +31 -0
- package/src/local-ai/decision-tokenizer.mjs +109 -0
- package/src/local-ai/decisions.mjs +273 -0
- package/src/local-ai/dev.mjs +8 -2
- package/src/local-ai/onnx-worker.mjs +67 -7
- package/src/local-ai/onnx.mjs +23 -12
- package/src/native/portable-layout.mjs +8 -3
- package/src/repository.mjs +144 -4
|
@@ -69,6 +69,8 @@ runtime layouts.
|
|
|
69
69
|
| `arcane-os/core/local-ai` | Native llama.cpp, Ollama and ONNX Core service, model readiness, complete responses and owned lifecycle. See [local AI through Core](local-ai.md). |
|
|
70
70
|
| `arcane-os/local-ai` | Official runtime installation and native artifact bundling for selected llama.cpp/Ollama/ONNX/image requirements and NeMo libraries. |
|
|
71
71
|
| `arcane-os/local-ai/onnx` | Retained native ONNX worker sessions with explicit graph loading and complete tensor inference. |
|
|
72
|
+
| `arcane-os/local-ai/decisions` | Explicit native Laya FP32 decisions using the existing ONNX owner and a tokenizer Worker. |
|
|
73
|
+
| `arcane-os/core/decisions` | Renderer-independent typed-decision service composed with existing local-AI and model-assets owners. |
|
|
72
74
|
| `arcane-os/ai/core-onnx` | Optional browser access to system ONNX through an existing Core connection. |
|
|
73
75
|
| `arcane-os/local-ai/image` | Retained native stable-diffusion.cpp context, complete PNG generation and owned worker lifetime. |
|
|
74
76
|
| `arcane-os/ai/core-image` | Browser image lifecycle and complete PNG Blobs through an available Core service. |
|
|
@@ -108,7 +110,7 @@ runtime layouts.
|
|
|
108
110
|
| `arcane-os/pwa` | Nonblocking PWA registration, worker updates, native installation state and a dismissible installation component. |
|
|
109
111
|
| `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
|
|
110
112
|
| `arcane-os/ai/core-local` | Browser llama.cpp provider through an available Core connection; browser-local Wllama and ONNX remain independently available. |
|
|
111
|
-
| `arcane-os/ai/browser-decisions` | Explicit-load Laya
|
|
113
|
+
| `arcane-os/ai/browser-decisions` | Explicit-load Laya and Julia-1 typed decisions with caller-selected precision through the shared Transformers.js Worker owner. |
|
|
112
114
|
| `arcane-os/ai/tool-text-stream` | Shared selected tool-argument text observer for provider integration. |
|
|
113
115
|
| `arcane-os/ai/twin-cloud` | Stateless chat, System One state/questions and image generation with explicit key/model, complete results and operation-specific retry/cancellation. |
|
|
114
116
|
| `arcane-os/ai/browser-speech` | Caller-selected Whisper/Kokoro, native browser recognition/synthesis, cloud TTS, upstream assets, role lifecycle and cancellation. |
|
|
@@ -169,6 +171,9 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
169
171
|
| `acceptWebSocket()` | function | `arcane-os`, `arcane-os/websocket` | WebSocket protocol | Node; browser-native client interoperability |
|
|
170
172
|
| `WS` | class | `arcane-os`, `arcane-os/websocket-client` | Shared WebSocket clients | Node and native-WebSocket browser hosts |
|
|
171
173
|
| `createBrowserDecisionModel()` | function | `arcane-os/ai/browser-decisions` | Browser typed decisions | Browser module Workers and the caller-selected inference backend |
|
|
174
|
+
| `createNativeDecisionModel()` | function | `arcane-os/local-ai/decisions` | Native typed decisions | Node with an existing native ONNX owner and selected Laya FP32 files |
|
|
175
|
+
| `createNativeDecisionService()` | function | `arcane-os/core/decisions` | Native typed decisions | Node Core with existing local-AI and model-assets services |
|
|
176
|
+
| `createNativeDecisionService default export` | function | `arcane-os/core/decisions` | Native typed decisions | Same native decision service factory |
|
|
172
177
|
| `fetchRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud requests | Node and Browser; remote HTTPS provider |
|
|
173
178
|
| `fetchSystemOneRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud state-and-questions requests | Node and Browser; remote HTTPS provider |
|
|
174
179
|
| `generateImages()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud image generation | Node and Browser with Fetch and Blob; remote HTTPS provider |
|
|
@@ -3742,11 +3747,19 @@ Runs one fast-forward-only pull after proving the selected repository worktree i
|
|
|
3742
3747
|
### Signature and result
|
|
3743
3748
|
|
|
3744
3749
|
```text
|
|
3745
|
-
async repositoryPull({ workspaceRoot=process.cwd(), signal, onEvent, run=runProcess }={})
|
|
3750
|
+
async repositoryPull({ workspaceRoot=process.cwd(), target, signal, onEvent, run=runProcess }={})
|
|
3746
3751
|
```
|
|
3747
3752
|
|
|
3748
3753
|
Import it from `arcane-os`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and lifecycle.
|
|
3749
3754
|
|
|
3755
|
+
Optional `target:{remote,ref}` captures exact nonempty native-representable
|
|
3756
|
+
strings before any wait and selects `git pull --ff-only -- <remote> <ref>:`.
|
|
3757
|
+
The current clean branch receives a fast-forward-only integration; the SDK
|
|
3758
|
+
never switches branches, resets or changes configuration. Without a target,
|
|
3759
|
+
the existing configured pull and result remain unchanged. Targeted results
|
|
3760
|
+
add `target`, complete `stdout` and `stderr` to the existing pull result.
|
|
3761
|
+
See [checkout configuration and selected targets](core-repositories.md#existing-checkout-configuration-and-selected-targets).
|
|
3762
|
+
|
|
3750
3763
|
### Availability and normalization
|
|
3751
3764
|
|
|
3752
3765
|
**Node; network-assisted Git.** SDK-normalized inputs, errors, events, and documented result. Deep protocol: [Node ESM](protocols.md).
|
|
@@ -3770,7 +3783,7 @@ Pushes the selected attached branch through the repository's configured remote a
|
|
|
3770
3783
|
### Signature, parameters, and result
|
|
3771
3784
|
|
|
3772
3785
|
```text
|
|
3773
|
-
async repositoryPush({ workspaceRoot=process.cwd(), signal, onEvent, run=runProcess }={})
|
|
3786
|
+
async repositoryPush({ workspaceRoot=process.cwd(), target, signal, onEvent, run=runProcess }={})
|
|
3774
3787
|
```
|
|
3775
3788
|
|
|
3776
3789
|
Import it from `arcane-os`. `workspaceRoot` selects the Git worktree. `signal`
|
|
@@ -3781,10 +3794,16 @@ should keep the default.
|
|
|
3781
3794
|
|
|
3782
3795
|
Before pushing, the SDK runs `git rev-parse --show-toplevel`,
|
|
3783
3796
|
`git branch --show-current`, and `git status --short --branch` in order. It rejects a
|
|
3784
|
-
detached HEAD, then runs plain `git push`; the repository's current branch,
|
|
3797
|
+
detached HEAD, then, without `target`, runs plain `git push`; the repository's current branch,
|
|
3785
3798
|
configured upstream/remote, Git credentials, hooks, and server policy remain
|
|
3786
3799
|
authoritative. Unlike `repositoryPull()`, this function does **not** require a
|
|
3787
|
-
clean worktree. It does not create a commit or
|
|
3800
|
+
clean worktree. It does not create a commit or choose an application destination.
|
|
3801
|
+
Optional `target:{remote,ref}` captures the original nonempty
|
|
3802
|
+
native-representable strings before any wait and runs
|
|
3803
|
+
`git push --no-follow-tags -- <remote> HEAD:<ref>`. This publishes current HEAD
|
|
3804
|
+
to the caller-selected ref without force or implicit configured tag following.
|
|
3805
|
+
It changes no branch or Git configuration; credentials, URL interpretation,
|
|
3806
|
+
hooks and remote rejection remain Git-owned.
|
|
3788
3807
|
|
|
3789
3808
|
The promise resolves to
|
|
3790
3809
|
`{action:'push', repositoryRoot, branch, output}`. `output` is trimmed stdout,
|
|
@@ -3792,6 +3811,9 @@ or trimmed stderr when stdout is empty. A missing Git executable, failed status
|
|
|
3792
3811
|
probe, rejected/nonzero push, cancellation, or event-callback failure rejects
|
|
3793
3812
|
with the normalized process error. Cancellation stops the local process tree;
|
|
3794
3813
|
it cannot prove that a remote accepted no objects before the interruption.
|
|
3814
|
+
Targeted results also contain the captured `target` and complete
|
|
3815
|
+
`stdout`/`stderr`; omitting the target preserves the existing result shape.
|
|
3816
|
+
See [checkout configuration and selected targets](core-repositories.md#existing-checkout-configuration-and-selected-targets).
|
|
3795
3817
|
|
|
3796
3818
|
### Availability and normalization
|
|
3797
3819
|
|
|
@@ -7280,9 +7302,18 @@ createCoreRuntime({application, version, services=[]}={})
|
|
|
7280
7302
|
```
|
|
7281
7303
|
|
|
7282
7304
|
Import it from `arcane-os/core/runtime`. The runtime exposes `current()`,
|
|
7283
|
-
`subscribe()`, `onFrame()`, `emit()`, `registerService()`, `start()`, `handle()`
|
|
7305
|
+
`subscribe()`, `onFrame()`, `emit()`, `registerService()`, `getService()`, `start()`, `handle()`
|
|
7284
7306
|
and idempotent `close()`.
|
|
7285
7307
|
|
|
7308
|
+
`await runtime.getService(name)` and service `context.getService(name)` return
|
|
7309
|
+
the actual registered service after its shared startup. They wait for that
|
|
7310
|
+
dependency alone, may start it before `runtime.start()`, and add no renderer or
|
|
7311
|
+
all-service barrier. Unknown services reject with `CORE_SERVICE_UNAVAILABLE`;
|
|
7312
|
+
new lookups during shutdown reject with `CORE_CLOSING`. Startup failures retain
|
|
7313
|
+
their original error. This is readiness, not a lifetime lease: retain acquired
|
|
7314
|
+
native handles for their actual cleanup rather than looking them up during
|
|
7315
|
+
disposal. See [native service composition](core-runtime.md#native-service-composition).
|
|
7316
|
+
|
|
7286
7317
|
Services start independently. Request-lifetime work receives cooperative
|
|
7287
7318
|
cancellation; accepted `lifetime:'service'` work survives renderer cancellation.
|
|
7288
7319
|
Shutdown stops admission, waits accepted responses, then runs each service's
|
|
@@ -7894,8 +7925,8 @@ explicit existing path. The application owns the remote, branch and Core API.
|
|
|
7894
7925
|
createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})
|
|
7895
7926
|
```
|
|
7896
7927
|
|
|
7897
|
-
Returns `{directory,open,status,pull,push,write,close,drain,dispose}`. Construction does
|
|
7898
|
-
no I/O.
|
|
7928
|
+
Returns `{directory,open,status,configuration,pull,push,write,close,drain,dispose}`. Construction does
|
|
7929
|
+
no I/O. Preparing operations accept `{signal}`; a missing or empty destination clones once,
|
|
7899
7930
|
while existing checkout files, branches, remotes and local changes remain intact.
|
|
7900
7931
|
Same-directory calls are ordered within the process. Shutdown drains accepted
|
|
7901
7932
|
work and preserves the checkout. No CLI cwd, launch state, preference, snapshot
|
|
@@ -7908,7 +7939,25 @@ Selected author fields override inherited child author/committer fields;
|
|
|
7908
7939
|
omitted fields remain inherited. Username remains a credential hint, with
|
|
7909
7940
|
authentication owned by ordinary Git and its configured helper.
|
|
7910
7941
|
|
|
7911
|
-
`
|
|
7942
|
+
`configuration({signal})` observes an existing working root without clone,
|
|
7943
|
+
initialization or fetch. It returns `{repositoryRoot,headRef,origin,upstream}`:
|
|
7944
|
+
full symbolic HEAD (including unborn, null when detached), origin URL/pushURL
|
|
7945
|
+
arrays and current branch remote/merge/URL/pushURL arrays. Unset, empty and
|
|
7946
|
+
repeated configuration values remain distinct. These are ordinary configured
|
|
7947
|
+
values, not authentication or endpoint proof, and are not atomic with later
|
|
7948
|
+
operations. Raw stdout is decoded without replacement; unrepresentable text
|
|
7949
|
+
reports `ARCANE_GIT_CONFIGURATION_NOT_TEXT` with full diagnostics.
|
|
7950
|
+
|
|
7951
|
+
`pull({target,signal})`, `push({target,signal})` and
|
|
7952
|
+
`write({files:[{path,content}],message,target,signal})` accept optional
|
|
7953
|
+
`target:{remote,ref}`, captured as original strings at acceptance. The target
|
|
7954
|
+
selects a fast-forward-only pull into the current branch or a non-force push
|
|
7955
|
+
of current HEAD to the selected ref, without switching branches or changing
|
|
7956
|
+
configuration. It is independent of initial-clone `remote`/`branch`; omission
|
|
7957
|
+
preserves ordinary configured behavior. See the [complete configuration and
|
|
7958
|
+
target contract](core-repositories.md#existing-checkout-configuration-and-selected-targets).
|
|
7959
|
+
|
|
7960
|
+
`write` snapshots the exact selected
|
|
7912
7961
|
strings, writes those repository-relative files, commits only their literal
|
|
7913
7962
|
paths and performs an ordinary non-force push. It retains unrelated staged
|
|
7914
7963
|
paths and existing Git filters/hooks. Results and causal failures distinguish
|
|
@@ -7976,7 +8025,7 @@ Construction selects no model and installs no runtime or transport.
|
|
|
7976
8025
|
### Signature and result
|
|
7977
8026
|
|
|
7978
8027
|
```text
|
|
7979
|
-
createCoreLocalAIProvider({client
|
|
8028
|
+
createCoreLocalAIProvider({client,id='llama.cpp'}={})
|
|
7980
8029
|
```
|
|
7981
8030
|
|
|
7982
8031
|
Import it from `arcane-os/ai/core-local`. The provider implements
|
|
@@ -7988,6 +8037,16 @@ payloads and parsed streaming chunks, and cancel on observed readiness loss.
|
|
|
7988
8037
|
Missing Core reports unavailability; it does not choose a browser or cloud route.
|
|
7989
8038
|
Dispose/unregister at the caller's owning lifecycle boundary.
|
|
7990
8039
|
|
|
8040
|
+
Omitted `client` follows late installation, replacement and retirement through
|
|
8041
|
+
the shared Core installation subscription. An explicit client, including
|
|
8042
|
+
`null`, remains fixed. Replacement revokes readiness and cancels owned work;
|
|
8043
|
+
late results cannot restore it. `inspect()` distinguishes explicit load
|
|
8044
|
+
capability from loaded readiness, while `status()` remains observational.
|
|
8045
|
+
Disposal releases only an actually retained selection through its captured
|
|
8046
|
+
client, so never-loaded and already-unloaded providers send no native unload.
|
|
8047
|
+
The provider/2 runtime reconciles status at its operation boundaries; this
|
|
8048
|
+
does not add an idle role-state subscription.
|
|
8049
|
+
|
|
7991
8050
|
```javascript
|
|
7992
8051
|
import {createCoreLocalAIProvider} from 'arcane-os/ai/core-local';
|
|
7993
8052
|
import {getAIProviderRuntime} from 'arcane-os/ai-provider-runtime';
|
|
@@ -8020,6 +8079,13 @@ resolved installed runtime records. It returns a Core service definition with
|
|
|
8020
8079
|
existing Core lifecycle. Shutdown cancels owned work and joins owned processes;
|
|
8021
8080
|
an already running external service remains under its external owner.
|
|
8022
8081
|
|
|
8082
|
+
After `await context.getService('local-ai')`, `service.getONNXRuntime()` returns
|
|
8083
|
+
that service's actual prepared ONNX owner. It creates no engine and rejects
|
|
8084
|
+
unavailable or closing access. Reacquire on each explicit load after recovery;
|
|
8085
|
+
retain the returned handle for cleanup. Core shutdown cancels request-lifetime
|
|
8086
|
+
work, and independent services dispose concurrently. See
|
|
8087
|
+
[native service owners](local-ai.md#native-service-owners).
|
|
8088
|
+
|
|
8023
8089
|
Optional `prepare({signal,onEvent})` supplies resolved runtime records during
|
|
8024
8090
|
this service's startup. Only operations depending on that service wait for its
|
|
8025
8091
|
preparation; the development listener and independently composed image/model
|
|
@@ -8029,7 +8095,10 @@ requirements. The managed development and native entrypoints separate them.
|
|
|
8029
8095
|
|
|
8030
8096
|
The service provides `localai.status`, explicit `localai.services.recover`,
|
|
8031
8097
|
`llama.status/models/load/unload/chat`, `onnx.status/load/run/unload`, and the documented native Ollama methods.
|
|
8032
|
-
`localai.state` reports runtime/model changes. `llama.
|
|
8098
|
+
`localai.state` reports runtime/model changes. `llama.status` and `llama.models`
|
|
8099
|
+
observe current state without starting, restarting or waiting for an engine.
|
|
8100
|
+
Explicit load retains startup and exact catalog-membership checks.
|
|
8101
|
+
`llama.chunk` and `ollama.chunk`
|
|
8033
8102
|
carry original parsed chunks with `streamId`; terminal responses remain complete.
|
|
8034
8103
|
There is no background health polling or implicit model choice.
|
|
8035
8104
|
|
|
@@ -8046,7 +8115,7 @@ and [native bundling](local-ai.md#native-bundling).
|
|
|
8046
8115
|
|
|
8047
8116
|
`createONNXRuntime({modulePath,signal?,onEvent?})` from `arcane-os/local-ai/onnx`
|
|
8048
8117
|
owns retained native ONNX sessions in workers. It exposes `load({id,model,
|
|
8049
|
-
sessionOptions?,signal?})`, `run({id,feeds,fetches?,runOptions?,signal?})`,
|
|
8118
|
+
sessionOptions?,executionPreference?,signal?})`, `run({id,feeds,fetches?,runOptions?,signal?})`,
|
|
8050
8119
|
`unload({id,signal?})`, `current()`, replaying `subscribe(listener)` and `close()`.
|
|
8051
8120
|
The direct model path is absolute. Complete typed tensor records use
|
|
8052
8121
|
`{type,dims,data}`; callers own the actual graph's inputs and preprocessing.
|
|
@@ -8056,11 +8125,22 @@ before replacement. Independent sessions run concurrently; same-session work
|
|
|
8056
8125
|
waits at that session's loading/execution boundary. Queued cancellation removes
|
|
8057
8126
|
that request; active cancellation retires the affected worker and suppresses
|
|
8058
8127
|
its response. Native code can delay termination; cleanup waits for actual exit.
|
|
8128
|
+
`current().sessions[].stopping` revokes new-run availability at terminal
|
|
8129
|
+
shutdown; `exited` records actual Worker exit. A recoverable run error leaves
|
|
8130
|
+
the loaded session usable. Unload joins exit and output delivery even when a
|
|
8131
|
+
termination request fails, preserving the complete cleanup error.
|
|
8132
|
+
|
|
8133
|
+
The generic load defaults to CPU. `executionPreference:'gpu'` tries advertised
|
|
8134
|
+
CUDA, TensorRT, DirectML, CoreML and WebGPU session creation before CPU.
|
|
8135
|
+
Explicit `sessionOptions.executionProviders` takes precedence unchanged.
|
|
8136
|
+
The complete `execution` record reports discovery, provider attempts and
|
|
8137
|
+
fallback; successful creation establishes configuration, not physical GPU
|
|
8138
|
+
execution. Inference is never replayed on a different provider after failure.
|
|
8059
8139
|
See [complete signatures, readiness, cancellation and usage](local-ai.md#native-onnx-sessions).
|
|
8060
8140
|
|
|
8061
8141
|
## createCoreONNXRuntime()
|
|
8062
8142
|
|
|
8063
|
-
`createCoreONNXRuntime({client
|
|
8143
|
+
`createCoreONNXRuntime({client}={})` from
|
|
8064
8144
|
`arcane-os/ai/core-onnx` accesses an available Core ONNX service. Its `load`,
|
|
8065
8145
|
`run`, and `unload` mirror the native session selection and accept `signal` and
|
|
8066
8146
|
`timeoutMs` (default `0`). Relative model paths resolve at Core's `appRoot`.
|
|
@@ -8068,7 +8148,11 @@ The accessor owns complete tensor transport encoding/decoding, `inspect()`,
|
|
|
8068
8148
|
`current()`, replaying `subscribe(listener)` and `close()`. Closing cancels this
|
|
8069
8149
|
accessor's requests and subscriptions; unloading shared models is explicit.
|
|
8070
8150
|
Construction installs no runtime and opens no transport. Existing browser
|
|
8071
|
-
ONNX/Wllama remain independent.
|
|
8151
|
+
ONNX/Wllama remain independent. Omitted `client` follows late installation and
|
|
8152
|
+
reads each installed connection's current status; explicit clients, including
|
|
8153
|
+
`null`, remain fixed. Retirement or replacement clears readiness and cancels
|
|
8154
|
+
owned work, with late old-client results suppressed. Close also removes the
|
|
8155
|
+
installation subscription. See [Core and browser usage](local-ai.md#onnx-through-core-and-the-browser).
|
|
8072
8156
|
|
|
8073
8157
|
## createImageRuntime()
|
|
8074
8158
|
|
|
@@ -8108,12 +8192,18 @@ browser accessor's detach-only ownership below. See the
|
|
|
8108
8192
|
|
|
8109
8193
|
## createCoreImageRuntime()
|
|
8110
8194
|
|
|
8111
|
-
`createCoreImageRuntime({client
|
|
8195
|
+
`createCoreImageRuntime({client,signal?,onEvent?})`
|
|
8112
8196
|
from `arcane-os/ai/core-image` returns immediately with `load`, `generate`, `edit`,
|
|
8113
8197
|
`unload`, `inspect`, `current`, `subscribe` and `close`. It subscribes before
|
|
8114
8198
|
requesting an initial status snapshot so newer lifecycle events win. It opens
|
|
8115
8199
|
no connection and installs or selects no model.
|
|
8116
8200
|
|
|
8201
|
+
Omitted `client` follows late Core installation, replacement and retirement;
|
|
8202
|
+
an explicit client, including `null`, remains fixed. Each installed client is
|
|
8203
|
+
observed before its status request. Retirement cancels owned work and removes
|
|
8204
|
+
old readiness; late results cannot restore it. Close removes both service and
|
|
8205
|
+
installation subscriptions without closing the shared client or model.
|
|
8206
|
+
|
|
8117
8207
|
`load({model,context?,assetProjectionId?,resourcePaths?,signal?})` and
|
|
8118
8208
|
`generate({model,prompt,parameters?,assetProjectionId?,resourcePaths?,signal?,
|
|
8119
8209
|
onProgress?})` use the selected Core service. Generation acknowledges with
|
|
@@ -8410,6 +8500,14 @@ directory and complete member files.
|
|
|
8410
8500
|
|
|
8411
8501
|
`current()` returns `{closing,projections}`. `retain(id)` accepts a completed
|
|
8412
8502
|
projection and returns `{id,directory,members,release}` for a native owner.
|
|
8503
|
+
Native `prepare({id,workingDirectory,members,signal,onProgress})` accepts
|
|
8504
|
+
ordered `{path,url}` members and returns their ready projection. Explicit calls
|
|
8505
|
+
stream complete upstream responses through the same file owner; startup
|
|
8506
|
+
downloads nothing. Independent members transfer concurrently and progress
|
|
8507
|
+
reports phases and completed files. `release(id)` relinquishes preparation
|
|
8508
|
+
ownership; cancellation, release and disposal join fetches and writes before
|
|
8509
|
+
cleanup. Native retain handles keep completed files alive until actual engine
|
|
8510
|
+
release. See [native preparation](model-assets.md#native-ownership).
|
|
8413
8511
|
`dispose()` releases preparation ownership and joins file writes, closes and
|
|
8414
8512
|
all native retain lifetimes before cleanup. The native owner releases its
|
|
8415
8513
|
handle only after its actual context/worker use ends. Original stored model
|
|
@@ -9071,18 +9169,127 @@ See the [mounting contract](pwa.md#mountpwainstallprompt) and
|
|
|
9071
9169
|
[component reference](runtime-components.md#pwa-installhtml). The browser owns
|
|
9072
9170
|
native installation eligibility and URL-bar promotion.
|
|
9073
9171
|
|
|
9172
|
+
## createNativeDecisionModel()
|
|
9173
|
+
|
|
9174
|
+
### Overview
|
|
9175
|
+
|
|
9176
|
+
Runs explicitly selected Laya FP32 option scoring through an existing native
|
|
9177
|
+
ONNX owner, with tokenization in a Node Worker. It creates no second inference
|
|
9178
|
+
engine and starts no model download at construction.
|
|
9179
|
+
|
|
9180
|
+
### Signature and result
|
|
9181
|
+
|
|
9182
|
+
```text
|
|
9183
|
+
createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',signal}={})
|
|
9184
|
+
```
|
|
9185
|
+
|
|
9186
|
+
Import from `arcane-os/local-ai/decisions`. Supply the existing `onnx` owner and
|
|
9187
|
+
either `modelAssets` plus `workingDirectory`, or native `paths` for the model,
|
|
9188
|
+
tokenizer and tokenizer configuration. The selected graph requires FP32. The
|
|
9189
|
+
handle exposes `load`, `evaluate`, `classify`, `current`, `status`, `subscribe`,
|
|
9190
|
+
`unload` and terminal `dispose`; `classify` aliases `evaluate` and `status`
|
|
9191
|
+
aliases `current`.
|
|
9192
|
+
|
|
9193
|
+
`evaluate(rows,{signal,runOptions})` preserves each complete `state`, `question`,
|
|
9194
|
+
`options` and optional `type` separately. It returns `{decisions,outputs}` with
|
|
9195
|
+
original rows, typed values, raw logits, unrounded probabilities and every
|
|
9196
|
+
named tensor output. It requires a loading or loaded activation. Cancellation
|
|
9197
|
+
retires the shared activation and joins its actual ONNX/tokenizer lifetime;
|
|
9198
|
+
working files remain retained until both Workers exit. Ordinary invalid rows
|
|
9199
|
+
leave a healthy session and sibling evaluations intact.
|
|
9200
|
+
|
|
9201
|
+
### Availability and normalization
|
|
9202
|
+
|
|
9203
|
+
**Node on Windows, Linux and macOS with the selected ONNX Runtime; Android needs
|
|
9204
|
+
host adaptation.** GPU preference attempts actual advertised providers before
|
|
9205
|
+
CPU fallback; accepted configuration does not prove GPU node execution. Model
|
|
9206
|
+
selection, unchanged payloads, scoring interpretation and persistence remain
|
|
9207
|
+
application-owned. Complete lifecycle, errors and paths are documented in
|
|
9208
|
+
[native typed decisions](native-decisions.md).
|
|
9209
|
+
|
|
9210
|
+
### Example
|
|
9211
|
+
|
|
9212
|
+
```javascript
|
|
9213
|
+
import {createNativeDecisionModel} from 'arcane-os/local-ai/decisions';
|
|
9214
|
+
|
|
9215
|
+
function createMoonChefDecisions({onnx, paths}) {
|
|
9216
|
+
return createNativeDecisionModel({onnx, paths});
|
|
9217
|
+
}
|
|
9218
|
+
```
|
|
9219
|
+
|
|
9220
|
+
The caller explicitly loads the returned model before evaluation and unloads
|
|
9221
|
+
or disposes it at its owning boundary; defining this function downloads nothing.
|
|
9222
|
+
|
|
9223
|
+
## createNativeDecisionService()
|
|
9224
|
+
|
|
9225
|
+
### Overview
|
|
9226
|
+
|
|
9227
|
+
Composes the same native decision model into an existing Core runtime without
|
|
9228
|
+
a renderer. The factory starts no inference, tokenizer or model preparation.
|
|
9229
|
+
|
|
9230
|
+
### Signature and result
|
|
9231
|
+
|
|
9232
|
+
```text
|
|
9233
|
+
createNativeDecisionService(configuration={}, {appRoot=process.cwd()}={})
|
|
9234
|
+
```
|
|
9235
|
+
|
|
9236
|
+
Import from `arcane-os/core/decisions`. Configuration selects the model options
|
|
9237
|
+
above and an optional service `name` (default `decisions`); relative supplied
|
|
9238
|
+
`paths` resolve from `appRoot`. Explicit load obtains the registered local-AI
|
|
9239
|
+
and, when needed, model-assets owners through `context.getService`, then
|
|
9240
|
+
reacquires `getONNXRuntime()` after any recovery. Native members are `load`,
|
|
9241
|
+
`evaluate`, `classify`, `current`, `subscribe`, `unload`, `dispose` and Core
|
|
9242
|
+
`start(context)`.
|
|
9243
|
+
|
|
9244
|
+
RPC exposes `decisions.status`, `decisions.load`, `decisions.evaluate` and
|
|
9245
|
+
`decisions.unload`. Evaluation accepts `{rows,runOptions?}` and returns complete
|
|
9246
|
+
`{decisions,outputs}` using the existing tensor codec at the JSON boundary.
|
|
9247
|
+
`decisions.state` and replaying `subscribe(listener,{emitCurrent:true,signal})`
|
|
9248
|
+
report lifecycle. Unload is accepted service-lifetime cleanup; host shutdown
|
|
9249
|
+
still cancels request-lifetime load/evaluation.
|
|
9250
|
+
|
|
9251
|
+
### Availability and normalization
|
|
9252
|
+
|
|
9253
|
+
**Node Core with an existing local-AI ONNX service and selected model files.**
|
|
9254
|
+
Registration does not wait for all services or open another host. Original
|
|
9255
|
+
inputs, complete outputs, failures and cleanup semantics match the native
|
|
9256
|
+
model. See [composition and example](native-decisions.md#compose-with-existing-core-services).
|
|
9257
|
+
|
|
9258
|
+
### Example
|
|
9259
|
+
|
|
9260
|
+
```javascript
|
|
9261
|
+
import {createNativeDecisionService} from 'arcane-os/core/decisions';
|
|
9262
|
+
|
|
9263
|
+
const decisions = createNativeDecisionService({
|
|
9264
|
+
model: 'onnx-community/laya-typed-decisions-ONNX',
|
|
9265
|
+
dtype: 'fp32',
|
|
9266
|
+
workingDirectory: '.models/laya'
|
|
9267
|
+
});
|
|
9268
|
+
// Add decisions to the application's existing Core services; load explicitly.
|
|
9269
|
+
```
|
|
9270
|
+
|
|
9271
|
+
## createNativeDecisionService default export
|
|
9272
|
+
|
|
9273
|
+
The default export from `arcane-os/core/decisions` is exactly the named
|
|
9274
|
+
`createNativeDecisionService` factory, with the same arguments and lifecycle.
|
|
9275
|
+
|
|
9074
9276
|
## createBrowserDecisionModel()
|
|
9075
9277
|
|
|
9076
9278
|
### Overview
|
|
9077
9279
|
|
|
9078
|
-
Creates an explicitly loaded browser model for Laya typed decisions
|
|
9079
|
-
Julia-1
|
|
9280
|
+
Creates an explicitly loaded browser model for Laya typed decisions or
|
|
9281
|
+
Julia-1. Laya defaults to FP16 and Julia to FP32; callers may select `dtype`
|
|
9282
|
+
explicitly. The shared Worker owns Transformers.js loading, full-input
|
|
9080
9283
|
encoding, batched inference, complete results, cancellation and resource lifetime.
|
|
9081
9284
|
Applications own model selection, questions, option meanings and decision policy.
|
|
9082
9285
|
Existing chat and speech providers are unchanged.
|
|
9083
9286
|
|
|
9084
9287
|
### Signature and result
|
|
9085
9288
|
|
|
9289
|
+
```text
|
|
9290
|
+
createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})
|
|
9291
|
+
```
|
|
9292
|
+
|
|
9086
9293
|
```js
|
|
9087
9294
|
import {createBrowserDecisionModel} from 'arcane-os/ai/browser-decisions';
|
|
9088
9295
|
|
|
@@ -9107,8 +9314,11 @@ precision, tokenization, output meanings, lifecycle and model limitations.
|
|
|
9107
9314
|
### Availability and normalization
|
|
9108
9315
|
|
|
9109
9316
|
Browser module Workers plus the selected Transformers.js backend are required
|
|
9110
|
-
on explicit use. Import and construction do not activate a model.
|
|
9111
|
-
`dtype
|
|
9317
|
+
on explicit use. Import and construction do not activate a model. The optional
|
|
9318
|
+
nonempty `dtype` string defaults to `fp16` for Laya and `fp32` for Julia. An
|
|
9319
|
+
explicit value is forwarded unchanged to the upstream loader; unsupported
|
|
9320
|
+
selections report its failure without precision fallback. For Laya FP32, pass
|
|
9321
|
+
`dtype: 'fp32'`.
|
|
9112
9322
|
Cancellation terminates that client's Worker and rejects its in-flight work.
|
|
9113
9323
|
No decisions are inserted into chat history or durable storage.
|
|
9114
9324
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.0",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"./core/preferences": "./src/core/services/preferences.mjs",
|
|
70
70
|
"./core/diarization": "./src/diarization/service.mjs",
|
|
71
71
|
"./core/local-ai": "./src/core/services/local-ai.mjs",
|
|
72
|
+
"./core/decisions": "./src/core/services/decisions.mjs",
|
|
72
73
|
"./core/image": "./src/core/services/image.mjs",
|
|
73
74
|
"./core/speech": "./src/core/services/speech.mjs",
|
|
74
75
|
"./core/model-assets": "./src/core/services/model-assets.mjs",
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"./diarization/build": "./src/diarization/build.mjs",
|
|
77
78
|
"./local-ai": "./src/local-ai/install.mjs",
|
|
78
79
|
"./local-ai/onnx": "./src/local-ai/onnx.mjs",
|
|
80
|
+
"./local-ai/decisions": "./src/local-ai/decisions.mjs",
|
|
79
81
|
"./local-ai/image": "./src/local-ai/image-runtime.mjs",
|
|
80
82
|
"./local-ai/whisper": "./src/local-ai/whisper/index.mjs",
|
|
81
83
|
"./local-ai/whisper/build": "./src/local-ai/whisper/build.mjs",
|
|
@@ -121,7 +123,7 @@
|
|
|
121
123
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
122
124
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
123
125
|
"test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/conversation-closing-report.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/native-windows-icon.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
124
|
-
"test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
126
|
+
"test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
125
127
|
"test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-macos-provider.test.mjs test/native-plan.test.mjs test/native-portable-provider.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
|
|
126
128
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/html-import-packaged-script.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
|
|
127
129
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -158,6 +160,7 @@
|
|
|
158
160
|
"tag": "latest"
|
|
159
161
|
},
|
|
160
162
|
"dependencies": {
|
|
163
|
+
"@huggingface/tokenizers": "0.2.0",
|
|
161
164
|
"event-pubsub": "6.1.1",
|
|
162
165
|
"js-queue": "3.1.1",
|
|
163
166
|
"node-http-server": "10.0.0",
|
|
@@ -1,11 +1,62 @@
|
|
|
1
1
|
<style>
|
|
2
|
-
:host{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
:host {
|
|
3
|
+
display:inline-block;
|
|
4
|
+
min-inline-size:0;
|
|
5
|
+
max-inline-size:100%;
|
|
6
|
+
color:inherit;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.switcher {
|
|
10
|
+
display:flex;
|
|
11
|
+
flex-wrap:wrap;
|
|
12
|
+
align-items:center;
|
|
13
|
+
gap:.15rem;
|
|
14
|
+
box-sizing:border-box;
|
|
15
|
+
max-inline-size:100%;
|
|
16
|
+
padding:.2rem;
|
|
17
|
+
border:1px solid var(--app-bar-border,var(--border-color,rgba(255,255,255,.25)));
|
|
18
|
+
border-radius:1.6rem;
|
|
19
|
+
background:color-mix(in srgb,currentColor 8%,transparent);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
button {
|
|
23
|
+
appearance:none;
|
|
24
|
+
flex:1 0 auto;
|
|
25
|
+
box-sizing:border-box;
|
|
26
|
+
min-inline-size:2.75rem;
|
|
27
|
+
max-inline-size:100%;
|
|
28
|
+
min-block-size:2.75rem;
|
|
29
|
+
padding:.5rem .75rem;
|
|
30
|
+
border:0;
|
|
31
|
+
border-radius:1.4rem;
|
|
32
|
+
background:transparent;
|
|
33
|
+
color:inherit;
|
|
34
|
+
cursor:pointer;
|
|
35
|
+
font:700 .875rem/1.25 system-ui,sans-serif;
|
|
36
|
+
text-align:center;
|
|
37
|
+
white-space:normal;
|
|
38
|
+
overflow-wrap:anywhere;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
button:hover:not(:disabled) {
|
|
42
|
+
background:color-mix(in srgb,currentColor 14%,transparent);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
button[aria-pressed="true"] {
|
|
46
|
+
background:var(--app-bar-accent,var(--secondary-color,rgb(196,154,82)));
|
|
47
|
+
color:var(--app-bar-active-text,var(--primary-color,rgb(23,34,56)));
|
|
48
|
+
box-shadow:inset 0 0 0 .1rem currentColor;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
button:disabled {
|
|
52
|
+
cursor:not-allowed;
|
|
53
|
+
opacity:.55;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
button:focus-visible {
|
|
57
|
+
outline:.18rem solid var(--focus-color,currentColor);
|
|
58
|
+
outline-offset:.12rem;
|
|
59
|
+
}
|
|
9
60
|
</style>
|
|
10
61
|
<div class="switcher" role="group" aria-label="Color scheme">
|
|
11
62
|
<button type="button" data-scheme="system" aria-pressed="false" title="Use device theme">Auto</button>
|
|
@@ -16,31 +67,79 @@
|
|
|
16
67
|
<script type="module">
|
|
17
68
|
const host=this;
|
|
18
69
|
const root=host.shadowRoot;
|
|
70
|
+
const group=root.querySelector('.switcher');
|
|
19
71
|
const buttons=Array.from(root.querySelectorAll('[data-scheme]'));
|
|
72
|
+
let modes=buttons.map(function scheme(button){return button.dataset.scheme;});
|
|
73
|
+
let labels=Object.fromEntries(buttons.map(function label(button){
|
|
74
|
+
return [button.dataset.scheme,button.textContent];
|
|
75
|
+
}));
|
|
76
|
+
let busy=false;
|
|
77
|
+
|
|
78
|
+
host.configure=configure;
|
|
79
|
+
configure({
|
|
80
|
+
modes:host.dataset.modes===undefined?modes:host.dataset.modes.split(/\s+/u).filter(Boolean),
|
|
81
|
+
labels:host.dataset.systemLabel===undefined?{}:{system:host.dataset.systemLabel}
|
|
82
|
+
});
|
|
83
|
+
|
|
20
84
|
const {default:ThemeManager}=await import('../modules/ThemeManager.js');
|
|
21
85
|
const manager=new ThemeManager();
|
|
22
86
|
|
|
23
87
|
host.setMode=setMode;
|
|
24
88
|
host.refresh=refresh;
|
|
25
|
-
|
|
89
|
+
group.addEventListener('click',async function selectScheme(event){
|
|
26
90
|
const button=event.target.closest('[data-scheme]');
|
|
27
|
-
if(!button) return;
|
|
91
|
+
if(!button||button.disabled) return;
|
|
28
92
|
setBusy(true);
|
|
29
93
|
try{
|
|
30
94
|
const state=button.dataset.scheme==='custom'?await manager.activateCustom():await manager.setScheme(button.dataset.scheme);
|
|
31
95
|
setMode(state.mode);setCustomAvailable(Boolean(state.theme));
|
|
32
96
|
}finally{setBusy(false);setCustomAvailable(Boolean(manager.customTheme));}
|
|
33
97
|
});
|
|
34
|
-
globalThis.addEventListener('arcane-theme-change',()
|
|
98
|
+
globalThis.addEventListener('arcane-theme-change',function themeChanged(){
|
|
99
|
+
refresh().catch(function refreshRejected(){});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
function configure(options={}){
|
|
103
|
+
const nextModes=options.modes===undefined?modes:[...options.modes];
|
|
104
|
+
const nextLabels={...labels,...options.labels};
|
|
105
|
+
const visible=[];
|
|
106
|
+
for(const mode of nextModes){
|
|
107
|
+
const button=buttons.find(function matchingScheme(candidate){return candidate.dataset.scheme===mode;});
|
|
108
|
+
if(!button) throw new TypeError(`Unknown theme-switcher mode: ${mode}`);
|
|
109
|
+
if(visible.includes(button)) throw new TypeError(`Repeated theme-switcher mode: ${mode}`);
|
|
110
|
+
visible.push(button);
|
|
111
|
+
}
|
|
112
|
+
for(const button of buttons){
|
|
113
|
+
const label=nextLabels[button.dataset.scheme];
|
|
114
|
+
if(typeof label!=='string') throw new TypeError('Theme-switcher labels must be strings.');
|
|
115
|
+
}
|
|
116
|
+
modes=nextModes;
|
|
117
|
+
labels=nextLabels;
|
|
118
|
+
for(const button of buttons) button.textContent=labels[button.dataset.scheme];
|
|
119
|
+
const reordered=group.children.length!==visible.length||visible.some(function moved(button,index){
|
|
120
|
+
return group.children[index]!==button;
|
|
121
|
+
});
|
|
122
|
+
if(reordered){
|
|
123
|
+
const focused=root.activeElement;
|
|
124
|
+
group.replaceChildren(...visible);
|
|
125
|
+
if(visible.includes(focused)&&!focused.disabled) focused.focus({preventScroll:true});
|
|
126
|
+
}
|
|
127
|
+
return {modes:[...modes],labels:{...labels}};
|
|
128
|
+
}
|
|
35
129
|
|
|
36
130
|
function setMode(mode='system'){
|
|
37
131
|
for(const button of buttons) button.setAttribute('aria-pressed',String(button.dataset.scheme===mode));
|
|
38
132
|
host.dataset.mode=mode;
|
|
39
133
|
return mode;
|
|
40
134
|
}
|
|
41
|
-
function setBusy(value){
|
|
42
|
-
|
|
135
|
+
function setBusy(value){
|
|
136
|
+
busy=Boolean(value);
|
|
137
|
+
for(const button of buttons) button.disabled=busy;
|
|
138
|
+
}
|
|
139
|
+
function setCustomAvailable(value){
|
|
140
|
+
buttons.find(function customScheme(button){return button.dataset.scheme==='custom';}).disabled=busy||!value;
|
|
141
|
+
}
|
|
43
142
|
async function refresh(){const state=await manager.load();setMode(state.mode);setCustomAvailable(Boolean(state.theme));return state;}
|
|
44
143
|
|
|
45
|
-
refresh().catch(()
|
|
144
|
+
refresh().catch(function refreshRejected(){setMode('system');});
|
|
46
145
|
</script>
|