arcane-os 0.47.0 → 0.49.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/browser-speech-artifacts.mjs +4 -0
- package/browser-runtime/ai/browser-speech-providers.mjs +1 -1
- package/browser-runtime/ai/twin-cloud-speech.mjs +148 -55
- package/docs/reference/ai/browser-speech.md +24 -11
- package/docs/reference/runtime-components.md +72 -20
- package/docs/reference/runtime-modules.md +25 -10
- package/package.json +1 -1
- package/runtime/arcane/components/chat.html +223 -2
- package/runtime/arcane/components/speech.html +123 -4
- package/runtime/arcane/components/voice-transcription.html +21 -23
- package/runtime/arcane/css/document-site.css +2 -35
- package/runtime/arcane/css/print.css +235 -0
- package/runtime/arcane/css/theme.css +2 -40
- package/runtime/arcane/modules/PrintView.js +19 -55
|
@@ -56,13 +56,14 @@ appropriate.
|
|
|
56
56
|
|
|
57
57
|
## Shared print presentation
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
scoped
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
The shared `theme.css` and `document-site.css` import `print.css`, the single
|
|
60
|
+
print-style owner. It provides a light paper palette and `1in` (`25.4mm`) page
|
|
61
|
+
margins. During print, root/body/scoped palette tokens override screen colors;
|
|
62
|
+
the shared header/sidebar shell is hidden and its main scroller expands into
|
|
63
|
+
document flow. Screen layout, preferences, fonts, and media remain unchanged.
|
|
64
|
+
[`PrintView.js`](runtime-modules.md#printviewjs) loads the same stylesheet for
|
|
65
|
+
rendered snapshots of Chat, the editor, and file previews. Its temporary
|
|
66
|
+
snapshot remains owned until the browser's `afterprint` event.
|
|
66
67
|
|
|
67
68
|
## Canonical inventory
|
|
68
69
|
|
|
@@ -73,7 +74,7 @@ owner described with the editor and file preview below.
|
|
|
73
74
|
| [`browser-ai-setup.html`](#browser-ai-setuphtml) | Browser API availability, reported GPU class, and copyable NPU/GPU settings addresses. | `refresh()`<br>`checkGpu()`<br>`open()`<br>`destroy()`<br>`ready` | `browser-ai-setup-ready` | Browser-reported adapter class; browser flag state and model execution remain browser-owned |
|
|
74
75
|
| [`calculator.html`](#calculatorhtml) | Calculator keypad and result/error event surface backed by CalculatorEngine. | `calculate()`<br>`destroy()` | `calculator-ready`<br>`calculation-complete`<br>`calculation-error` | Normalized Calculation/error events |
|
|
75
76
|
| [`chart.html`](#charthtml) | Accessible uPlot line, area, or point chart with normalized options and rows. | `configure()`<br>`populate()`<br>`setData()`<br>`addData()`<br>`update()`<br>`destroy()` | `chart-ready`<br>`chart-remove` | Options/rows normalized; uPlot rendering is vendor-native |
|
|
76
|
-
| [`chat.html`](#chathtml) | Shared chat, visible selected-model activation request, file upload, streaming, structural tool settlement, speech, language, availability, and conversation-timebox surface. | `streamMessage()`<br>`setMessageProgress()`<br>`setAIAvailability()`<br>`setInitialSpeechMuted()`<br>`setConversationComplete()`<br>`bindConversationTimebox()`<br>`bindSession()`<br>`submitMessage()`<br>`submitToolResult()`<br>`submitToolResults()`<br>`sendMessage()`<br>`languageChanged()`<br>`requestAIActivation()`<br>`destroy()` | `chat-ready`<br>`chat-session-bound`<br>`chat-session-message`<br>`chat-session-error`<br>`chat-send-message`<br>`chat-send-error`<br>`chat-file-uploaded`<br>`chat-file-upload-error`<br>`chat-language-changed`<br>`chat-language-change-error`<br>`chat-ai-activation-request`<br>`chat-ai-activation-error`<br>`chat-speech-synthesis-error`<br>`conversation-timebox-error` | UI/runtime state, explicit user activation intent, and honest structural-call settlement normalized; AI/storage/media behavior mixed |
|
|
77
|
+
| [`chat.html`](#chathtml) | Shared chat, visible selected-model activation request, file upload, streaming, structural tool settlement, speech, language, availability, and conversation-timebox surface. | `streamMessage()`<br>`setMessageProgress()`<br>`setAIAvailability()`<br>`setInitialSpeechMuted()`<br>`setConversationComplete()`<br>`bindConversationTimebox()`<br>`bindSession()`<br>`submitMessage()`<br>`submitToolResult()`<br>`submitToolResults()`<br>`sendMessage()`<br>`appendDraft()`<br>`print()`<br>`languageChanged()`<br>`requestAIActivation()`<br>`destroy()` | `chat-ready`<br>`chat-session-bound`<br>`chat-session-message`<br>`chat-session-error`<br>`chat-send-message`<br>`chat-send-error`<br>`chat-file-uploaded`<br>`chat-file-upload-error`<br>`chat-language-changed`<br>`chat-language-change-error`<br>`chat-ai-activation-request`<br>`chat-ai-activation-error`<br>`chat-speech-synthesis-error`<br>`conversation-timebox-error` | UI/runtime state, explicit user activation intent, and honest structural-call settlement normalized; AI/storage/media behavior mixed |
|
|
77
78
|
| [`conversation-view.html`](#conversation-viewhtml) | Provider-neutral conversation display, advisory actions, composer, busy state, and status. | `setConversation()`<br>`setBusy()`<br>`setStatus()`<br>`clearComposer()`<br>`destroy()` | `conversation-view-ready`<br>`communication-send`<br>`communication-advisory-action` | DOM-normalized |
|
|
78
79
|
| [`dashboard-config.html`](#dashboard-confightml) | Selects which normalized chart definitions are visible on a dashboard. | `configure()`<br>`setDefinitions()`<br>`setVisibility()`<br>`getChartOptions()`<br>`getEffectiveVisibility()`<br>`open()`<br>`close()`<br>`destroy()` | `dashboard-config-ready`<br>`dashboard-config-opened`<br>`dashboard-config-closed`<br>`dashboard-config-change` | Fully normalized definitions and visibility |
|
|
79
80
|
| [`data-maintenance.html`](#data-maintenancehtml) | Runs destructive cleanup of empty chats and memories inside the current app data scope. | `open()`<br>`destroy()` | `data-maintenance-ready`<br>`data-maintenance-complete` | Normalized counts; DBOPFS failures mixed |
|
|
@@ -98,7 +99,7 @@ owner described with the editor and file preview below.
|
|
|
98
99
|
| [`screen-capture.html`](#screen-capturehtml) | Presents image, video, or GIF display-capture workflow. | `capture` (`ScreenCapture` instance)<br>`destroy()` | `screen-capture-ready`<br>`screen-capture-result` | State/result normalized; media permission/codec failures mixed |
|
|
99
100
|
| [`source-code-viewer.html`](#source-code-viewerhtml) | Renders complete line-addressable source code with load, error, focus, and state behavior. | `configure()`<br>`load()`<br>`render()`<br>`clear()`<br>`fail()`<br>`focus()`<br>`focusLine()`<br>`destroy()` | `source-code-viewer-ready`<br>`source-code-viewer-state`<br>`source-code-viewer-state-loading`<br>`source-code-viewer-state-ready`<br>`source-code-viewer-state-empty`<br>`source-code-viewer-state-error` | Complete mutable source/state |
|
|
100
101
|
| [`source-explanation.html`](#source-explanationhtml) | Presents an evidence finding, source selection, explanation, and save state. | `showFinding()`<br>`populate()`<br>`selectSource()`<br>`markSaved()`<br>`destroy()` | `source-explanation-ready`<br>`source-explanation-save`<br>`source-explanation-source-selected` | DOM-normalized |
|
|
101
|
-
| [`speech.html`](#speechhtml) | Coordinates explicit STT activation, speech controls, transcription completion, mute state, and microphone availability. | `configure()`<br>`setMuted()`<br>`reportTTSError()`<br>`requestSTTActivation()`<br>`destroy()`<br>`availability`<br>`muted`<br>`initialMuted`<br>`componentReady` | `speech-ready`<br>`speech-transcription-complete`<br>`speech-transcription-error`<br>`speech-transcription-cancelled`<br>`speech-microphone-unavailable`<br>`speech-stt-activation-request`<br>`speech-stt-activation-error`<br>`speech-tts-lifecycle-error`<br>`speech-synthesis-error` | Sticky runtime speech readiness, explicit STT activation, request cancellation, TTS mute lifecycle intent, and exact TTS operation failures normalized; provider/model authority remains external |
|
|
102
|
+
| [`speech.html`](#speechhtml) | Coordinates explicit STT activation, speech controls, transcription completion, mute state, and microphone availability. | `configure()`<br>`setMuted()`<br>`reportTTSError()`<br>`requestSTTActivation()`<br>`destroy()`<br>`availability`<br>`muted`<br>`initialMuted`<br>`recognitionLanguage`<br>`displayTranscription`<br>`componentReady` | `speech-ready`<br>`speech-transcription-progress`<br>`speech-transcription-complete`<br>`speech-transcription-error`<br>`speech-transcription-cancelled`<br>`speech-microphone-unavailable`<br>`speech-stt-activation-request`<br>`speech-stt-activation-error`<br>`speech-tts-lifecycle-error`<br>`speech-synthesis-error` | Sticky runtime speech readiness, explicit STT activation, request cancellation, TTS mute lifecycle intent, and exact TTS operation failures normalized; provider/model authority remains external |
|
|
102
103
|
| [`summary-strip.html`](#summary-striphtml) | Displays compact selectable KPI or summary items. | `configure()`<br>`setItems()`<br>`updateItem()`<br>`clear()`<br>`destroy()` | `summary-strip-ready`<br>`summary-strip-change`<br>`summary-strip-select` | DOM-normalized |
|
|
103
104
|
| [`table.html`](#tablehtml) | Builds and updates a simple header/body table. | `buildHeader()`<br>`buildTable()`<br>`destroy()` | `table-ready`<br>`header-update`<br>`body-update` | DOM-normalized |
|
|
104
105
|
| [`task-progress.html`](#task-progresshtml) | Runs and displays a task list with started/change/complete/error state. | `configure()`<br>`setTasks()`<br>`updateTask()`<br>`runTasks()`<br>`clear()`<br>`destroy()` | `task-progress-ready`<br>`task-progress-started`<br>`task-progress-change`<br>`task-progress-complete`<br>`task-progress-error` | Task state normalized; injected task results mixed |
|
|
@@ -317,7 +318,8 @@ Methods/properties: `streamMessage()`, `setMessageProgress()`,
|
|
|
317
318
|
`submitMessage()`, `submitToolResult()`, `submitToolResults()`, `sendMessage()`,
|
|
318
319
|
`languageChanged()`, `requestAIActivation()`, `session`, `sessionStatus`,
|
|
319
320
|
`pendingTool`, `pendingTools`, `pendingToolCall`, `pendingToolCalls`, `modelName`,
|
|
320
|
-
|
|
321
|
+
`transcriptionMode`, `recognitionLanguage`, `appendDraft()`, `print()`,
|
|
322
|
+
`printTitle`, and `destroy()`.
|
|
321
323
|
|
|
322
324
|
`sendMessage(text)` and `languageChanged(text)` are host-overridable async
|
|
323
325
|
extension callbacks. The component installs warning-only defaults when the host
|
|
@@ -331,6 +333,38 @@ editing. The visible Send button remains available on every device. Input
|
|
|
331
333
|
changes resize the composer without submitting; held desktop Enter does not
|
|
332
334
|
submit repeatedly. All input listeners follow the component's existing cleanup.
|
|
333
335
|
|
|
336
|
+
`transcriptionMode` defaults to `'submit'`, preserving completed-transcription
|
|
337
|
+
submission. Set it to `'draft'` for editable voice input with manual Send.
|
|
338
|
+
Native final words and interim hypotheses appear immediately in the existing
|
|
339
|
+
composer; later hypotheses replace only the recognition-owned interim range.
|
|
340
|
+
Typing elsewhere is preserved, and editing that range makes its text user-owned.
|
|
341
|
+
Completion does not append the same final text twice. Cancellation removes only
|
|
342
|
+
an unedited interim hypothesis; final words and user edits remain in the draft.
|
|
343
|
+
The shared Speech status stops repeating transcript text in draft mode.
|
|
344
|
+
`appendDraft(text)` appends complete text without submitting, preserves existing
|
|
345
|
+
draft text and selection, and emits the normal input event. It adds a newline
|
|
346
|
+
between entries only when neither boundary already has whitespace. It returns
|
|
347
|
+
`false` for empty input or a destroyed component.
|
|
348
|
+
|
|
349
|
+
`recognitionLanguage` forwards the Speech component's native BCP47 language
|
|
350
|
+
selection. If omitted, Speech uses the browser language. It is independent of
|
|
351
|
+
the existing conversation-language label and is latched when capture starts.
|
|
352
|
+
|
|
353
|
+
`await print()` delegates the current rendered transcript to shared PrintView;
|
|
354
|
+
`printTitle` optionally supplies the title (default `Conversation`). Controls,
|
|
355
|
+
status, transient thinking, and tool-inspection details are outside the printed
|
|
356
|
+
conversation; complete human-facing tool messages remain. Print uses
|
|
357
|
+
a document-flow transcript layout instead of left/right screen bubbles;
|
|
358
|
+
timestamps keep their own alignment and table headings can repeat across pages.
|
|
359
|
+
Native Print uses
|
|
360
|
+
the same registered view while it is visible and has transcript content. The
|
|
361
|
+
method resolves `true` when the browser print dialog is requested, not when a
|
|
362
|
+
PDF or paper output is produced; it resolves `false` when unavailable or
|
|
363
|
+
cancelled during preparation and rejects preparation failures. Complete errors
|
|
364
|
+
reach developer diagnostics and a concise recovery status stays outside the
|
|
365
|
+
transcript. Destruction cancels pending preparation, while an already requested
|
|
366
|
+
print retains its snapshot through `afterprint`.
|
|
367
|
+
|
|
334
368
|
Before any session binding begins, callback-mode `sessionStatus` follows the
|
|
335
369
|
selected sticky LLM role and the existing application-owned `modelName` label
|
|
336
370
|
(or the selected model ID). Assigning `modelName` refreshes that status even
|
|
@@ -950,7 +984,7 @@ preview ahead of the editor. Printing uses the shared
|
|
|
950
984
|
[`PrintView.js`](runtime-modules.md#printviewjs) owner; accessible HTML frames
|
|
951
985
|
are copied from their current rendered document, never substituted with source
|
|
952
986
|
text. Explicit printing waits for images/fonts and HTML frame readiness.
|
|
953
|
-
The shared print surface uses a light paper palette and `
|
|
987
|
+
The shared print surface uses a light paper palette and `1in` (`25.4mm`) page margins
|
|
954
988
|
while preserving image/SVG colors and leaving the screen theme unchanged.
|
|
955
989
|
Native PDF and inaccessible embedded viewers require their own print command.
|
|
956
990
|
`true` means the browser print dialog was requested, not that paper or a PDF
|
|
@@ -1153,7 +1187,7 @@ Markdown with `entryTitle`, independently of saving. Native browser Print uses
|
|
|
1153
1187
|
that same registered view unless an open file preview takes precedence.
|
|
1154
1188
|
The SDK copies current rendered DOM/styles into a temporary print-only view,
|
|
1155
1189
|
leaving the editor's source, controls, and screen layout in place. Both explicit
|
|
1156
|
-
and native print use the shared light paper palette and `
|
|
1190
|
+
and native print use the shared light paper palette and `1in` (`25.4mm`) page margins;
|
|
1157
1191
|
image pixels and SVG artwork keep their colors. Explicit printing waits for
|
|
1158
1192
|
resource readiness; native `beforeprint` is synchronous
|
|
1159
1193
|
and uses the currently rendered content. `print()` returns `false` when the
|
|
@@ -1632,13 +1666,27 @@ Events: `source-explanation-ready`, `source-explanation-save`, `source-explanati
|
|
|
1632
1666
|
An explicitly selected native Web Speech provider uses gesture-started live
|
|
1633
1667
|
capture instead of MediaRecorder. The shared control retains every final
|
|
1634
1668
|
result and emits its existing completion event once with the exact combined
|
|
1635
|
-
text when native capture ends. The
|
|
1636
|
-
|
|
1669
|
+
text when native capture ends. The `speech-transcription-progress` event carries
|
|
1670
|
+
cumulative complete final `text` and the current `interim` hypothesis, with the
|
|
1671
|
+
capture's `operationId`. Final segments replace their interim hypothesis before
|
|
1672
|
+
publishing; capture completion clears remaining interim text. The live display
|
|
1673
|
+
retains both while `displayTranscription` is true (the default). Set it false
|
|
1674
|
+
when a containing Chat owns the editable draft; status still reports listening,
|
|
1675
|
+
loading, and errors. Interim hypotheses stay transient. Release/Stop
|
|
1637
1676
|
finishes native recognition; cancellation, replacement and destruction abort
|
|
1638
1677
|
it and suppress late submission. A persisted page hide stops live native capture
|
|
1639
1678
|
without restarting it when the page returns. Existing Whisper and TTS paths
|
|
1640
1679
|
remain unchanged.
|
|
1641
1680
|
|
|
1681
|
+
On desktop native capture, an accessible editable BCP47 language control occupies
|
|
1682
|
+
the feedback area instead of idle transcription/voice readiness boilerplate.
|
|
1683
|
+
`recognitionLanguage` defaults to `navigator.language`, accepts an explicit
|
|
1684
|
+
language tag without restricting it to the suggested choices, and is passed to
|
|
1685
|
+
the capture owner at start. The control is disabled during active capture so
|
|
1686
|
+
changes affect the next capture. Native voice loading, errors, and speaking
|
|
1687
|
+
remain visible. Model-based transcription retains its existing status, and
|
|
1688
|
+
mobile speech retains its existing voice-output-only presentation.
|
|
1689
|
+
|
|
1642
1690
|
### Overview
|
|
1643
1691
|
|
|
1644
1692
|
Coordinates explicit speech-to-text activation, speech controls, transcription
|
|
@@ -1648,9 +1696,11 @@ completion, mute state, and microphone availability.
|
|
|
1648
1696
|
|
|
1649
1697
|
Methods/properties: `configure()`, `setMuted()`,
|
|
1650
1698
|
`reportTTSError(error,boundary='synthesis')`, `requestSTTActivation()`,
|
|
1651
|
-
`destroy()`, `availability`, `muted`, `initialMuted`,
|
|
1699
|
+
`destroy()`, `availability`, `muted`, `initialMuted`, `recognitionLanguage`,
|
|
1700
|
+
`displayTranscription`, and `componentReady`. The two new properties may also
|
|
1701
|
+
be supplied through `configure()`.
|
|
1652
1702
|
|
|
1653
|
-
Events: `speech-ready`, `speech-transcription-complete`,
|
|
1703
|
+
Events: `speech-ready`, `speech-transcription-progress`, `speech-transcription-complete`,
|
|
1654
1704
|
`speech-transcription-error`, `speech-transcription-cancelled`,
|
|
1655
1705
|
`speech-microphone-unavailable`, `speech-stt-activation-request`,
|
|
1656
1706
|
`speech-stt-activation-error`, `speech-tts-lifecycle-error`, and
|
|
@@ -2095,10 +2145,12 @@ Native methods remain subject to the bound app's capabilities. [Deep protocol de
|
|
|
2095
2145
|
|
|
2096
2146
|
An explicitly selected native Web Speech provider uses the same ordered
|
|
2097
2147
|
transcript/save queue in either capture mode. Each complete final text segment
|
|
2098
|
-
skips file transcription and enters the existing append/save stages
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2148
|
+
skips file transcription and enters the existing append/save stages. The existing
|
|
2149
|
+
transcript output immediately displays canonical text, queued final segments,
|
|
2150
|
+
and the current interim hypothesis in order. There is no separate interim pane.
|
|
2151
|
+
Interim text never reaches `value`, a save, or a success event. Moving an accepted
|
|
2152
|
+
final segment into canonical text does not display it twice; saved text and
|
|
2153
|
+
retry payloads remain owned by the ordered queue. Native `stopRecording()` returns the drain promise, including final
|
|
2102
2154
|
results emitted during native stop. Browser `end` stops capture without an
|
|
2103
2155
|
automatic restart. Provider replacement stops native capture while already
|
|
2104
2156
|
accepted final segments retain their ordered saves. Explicit Cancel, transcript
|
|
@@ -3394,12 +3394,14 @@ console.log(typeof prepareSpeech); // function
|
|
|
3394
3394
|
|
|
3395
3395
|
### Overview
|
|
3396
3396
|
|
|
3397
|
-
Shared rendered-print owner used by Markdown Editor and File Manager. It
|
|
3397
|
+
Shared rendered-print owner used by Chat, Markdown Editor, and File Manager. It
|
|
3398
3398
|
captures the current DOM and computed styles, including accessible rendered
|
|
3399
3399
|
HTML frames, complete text, selected images, and the current title. A temporary
|
|
3400
3400
|
print-only surface expands scrollable content without editing the live screen.
|
|
3401
|
-
|
|
3402
|
-
|
|
3401
|
+
The shared `/arcane/css/print.css` owns print presentation for this snapshot,
|
|
3402
|
+
direct themed pages, and documentation sites. It supplies the existing Arcane
|
|
3403
|
+
light palette and `1in` (`25.4mm`) page margins. The snapshot retains rendered
|
|
3404
|
+
fonts, font sizing, line height, emphasis, and complete
|
|
3403
3405
|
content while removing screen-theme text/shadow paint from ordinary HTML and
|
|
3404
3406
|
pseudo-elements. Links are underlined and table cells receive visible borders.
|
|
3405
3407
|
Image pixels, canvas snapshots, and SVG artwork keep their own colors. These
|
|
@@ -3434,6 +3436,13 @@ the developer console receives complete preparation failures.
|
|
|
3434
3436
|
Native printing never invokes the asynchronous `prepare` callback; prepared
|
|
3435
3437
|
views still receive the synchronous content/title read at capture.
|
|
3436
3438
|
|
|
3439
|
+
Import fetches the complete shared stylesheet through a module-relative URL
|
|
3440
|
+
and resolves after that text is available. The editor, file preview, and chat
|
|
3441
|
+
load this module independently of rendering; stylesheet readiness delays only
|
|
3442
|
+
print availability. Every registered view can therefore capture synchronously
|
|
3443
|
+
during native `beforeprint`, with no new stylesheet request at print time.
|
|
3444
|
+
Loading failures reject the module import and remain observable to its caller.
|
|
3445
|
+
|
|
3437
3446
|
`destroy()` unregisters the view and cancels pending preparation. Once the
|
|
3438
3447
|
dialog has been requested, its snapshot and retained media remain until
|
|
3439
3448
|
`afterprint`. Import alone creates no listener, provider, storage, or print job.
|
|
@@ -3443,7 +3452,8 @@ Exact exports: `createPrintView`.
|
|
|
3443
3452
|
### Availability and normalization
|
|
3444
3453
|
|
|
3445
3454
|
**Browser and native WebViews with a print implementation.** Requires DOM,
|
|
3446
|
-
CSSOM, FontFace/Image readiness, and
|
|
3455
|
+
CSSOM, Fetch for initial stylesheet loading, FontFace/Image readiness, and
|
|
3456
|
+
browser print events. No Core capability.
|
|
3447
3457
|
Cross-origin/inaccessible frames, native PDF frames, and object/embed viewers
|
|
3448
3458
|
must use their owning viewer's print command; the helper does not replace their
|
|
3449
3459
|
content with raw source or claim an unrendered document was printed. Font
|
|
@@ -4088,13 +4098,18 @@ The bare root retains the generic neutral light/dark baseline; the prior
|
|
|
4088
4098
|
layout default palette is selected explicitly through `data-user-skin="default"`,
|
|
4089
4099
|
`body.default` compatibility, or the default swatch.
|
|
4090
4100
|
|
|
4091
|
-
|
|
4092
|
-
|
|
4101
|
+
`theme.css` and `document-site.css` import the shared `/arcane/css/print.css`.
|
|
4102
|
+
During printing, that owner applies the existing base light palette, with a
|
|
4103
|
+
white paper surface and `1in` (`25.4mm`) page margins, to the root, body, and scoped
|
|
4093
4104
|
`data-arcane-palette` elements. These print-only tokens override screen/custom
|
|
4094
|
-
palette tokens.
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4105
|
+
palette tokens. During print, it resets root/body margins, hides the shared
|
|
4106
|
+
header/sidebar shell, and expands main-content scrolling into document flow.
|
|
4107
|
+
Saved preferences, screen presentation, fonts, and media remain unchanged.
|
|
4108
|
+
[`PrintView.js`](#printviewjs) loads that same stylesheet before registering
|
|
4109
|
+
views and applies it to its expanded rendered-content snapshot. Snapshot rules
|
|
4110
|
+
are scoped to owned print nodes; documentation-site print rules remain scoped
|
|
4111
|
+
to that site's surface. Screen styles and saved theme preferences remain with
|
|
4112
|
+
their existing owners.
|
|
4098
4113
|
|
|
4099
4114
|
A scoped `data-arcane-palette="warm"` element exposes the same palette variables
|
|
4100
4115
|
for a chooser preview without changing the page's selection. Use any of the
|
package/package.json
CHANGED
|
@@ -902,6 +902,30 @@
|
|
|
902
902
|
const browserDeviceClass = getBrowserDeviceClass();
|
|
903
903
|
|
|
904
904
|
host.language=host.language||'English';
|
|
905
|
+
let transcriptionMode=host.transcriptionMode??'submit';
|
|
906
|
+
speech.displayTranscription=transcriptionMode!=='draft';
|
|
907
|
+
Object.defineProperty(host,'transcriptionMode',{
|
|
908
|
+
configurable:true,
|
|
909
|
+
enumerable:true,
|
|
910
|
+
get:function getTranscriptionMode(){return transcriptionMode;},
|
|
911
|
+
set:function setTranscriptionMode(mode){
|
|
912
|
+
transcriptionMode=mode;
|
|
913
|
+
speech.displayTranscription=mode!=='draft';
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
if(host.recognitionLanguage!==undefined){
|
|
917
|
+
speech.recognitionLanguage=host.recognitionLanguage;
|
|
918
|
+
}
|
|
919
|
+
Object.defineProperty(host,'recognitionLanguage',{
|
|
920
|
+
configurable:true,
|
|
921
|
+
enumerable:true,
|
|
922
|
+
get:function getRecognitionLanguage(){
|
|
923
|
+
return speech.recognitionLanguage;
|
|
924
|
+
},
|
|
925
|
+
set:function setRecognitionLanguage(language){
|
|
926
|
+
speech.recognitionLanguage=language;
|
|
927
|
+
}
|
|
928
|
+
});
|
|
905
929
|
host.name = host.name||'User';
|
|
906
930
|
host.aiName='AI';
|
|
907
931
|
let requestedInitialSpeechMuted=is.boolean(speech.initialMuted)
|
|
@@ -920,6 +944,22 @@
|
|
|
920
944
|
let hostSubmissionGeneration=0;
|
|
921
945
|
let eventOperationSequence=0;
|
|
922
946
|
let destroyed=false;
|
|
947
|
+
let recognitionDraft=null;
|
|
948
|
+
let writingRecognitionDraft=false;
|
|
949
|
+
const printViewPromise=import('../modules/PrintView.js').then(
|
|
950
|
+
function registerChatPrint({createPrintView}){
|
|
951
|
+
if(destroyed) return null;
|
|
952
|
+
return createPrintView({
|
|
953
|
+
host,
|
|
954
|
+
content:function chatPrintContent(){return chatOutput;},
|
|
955
|
+
title:function chatPrintTitle(){return host.printTitle||'Conversation';},
|
|
956
|
+
active:function chatPrintActive(){return !destroyed&&chatOutput.children.length>0;},
|
|
957
|
+
signal:aiRuntimeStateAbortController.signal,
|
|
958
|
+
onError:reportChatPrintFailure
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
);
|
|
962
|
+
void printViewPromise.catch(reportChatPrintFailure);
|
|
923
963
|
let boundChatSession=null;
|
|
924
964
|
let boundChatAI=null;
|
|
925
965
|
let sessionBindingPending=false;
|
|
@@ -1852,6 +1892,8 @@
|
|
|
1852
1892
|
host.bindConversationTimebox=bindConversationTimebox;
|
|
1853
1893
|
host.bindSession=bindSession;
|
|
1854
1894
|
host.submitMessage=submitMessage;
|
|
1895
|
+
host.appendDraft=appendDraft;
|
|
1896
|
+
host.print=print;
|
|
1855
1897
|
host.submitToolResult=submitToolResult;
|
|
1856
1898
|
host.submitToolResults=submitToolResults;
|
|
1857
1899
|
Object.defineProperty(host,'pendingTool',{
|
|
@@ -3005,6 +3047,12 @@
|
|
|
3005
3047
|
{signal:aiRuntimeStateAbortController.signal}
|
|
3006
3048
|
);
|
|
3007
3049
|
|
|
3050
|
+
textArea.addEventListener(
|
|
3051
|
+
'input',
|
|
3052
|
+
trackRecognitionDraftEdit,
|
|
3053
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
3054
|
+
);
|
|
3055
|
+
|
|
3008
3056
|
textArea.addEventListener(
|
|
3009
3057
|
'input',
|
|
3010
3058
|
resizeTextArea,
|
|
@@ -3084,9 +3132,29 @@
|
|
|
3084
3132
|
{signal:aiRuntimeStateAbortController.signal}
|
|
3085
3133
|
);
|
|
3086
3134
|
|
|
3135
|
+
speech.addEventListener(
|
|
3136
|
+
'speech-transcription-progress',
|
|
3137
|
+
receiveRecognitionDraft,
|
|
3138
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
3139
|
+
);
|
|
3140
|
+
speech.addEventListener(
|
|
3141
|
+
'speech-transcription-cancelled',
|
|
3142
|
+
finishRecognitionDraft,
|
|
3143
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
3144
|
+
);
|
|
3087
3145
|
speech.addEventListener(
|
|
3088
3146
|
'speech-transcription-complete',
|
|
3089
|
-
function
|
|
3147
|
+
function receiveTranscribedChatMessage(event){
|
|
3148
|
+
if(recognitionDraft&&recognitionDraft.operationId===event.detail?.operationId){
|
|
3149
|
+
const completedDraft=recognitionDraft;
|
|
3150
|
+
receiveRecognitionDraft(event);
|
|
3151
|
+
if(recognitionDraft===completedDraft) recognitionDraft=null;
|
|
3152
|
+
return;
|
|
3153
|
+
}
|
|
3154
|
+
if(host.transcriptionMode==='draft'){
|
|
3155
|
+
appendDraft(event.detail?.text??'');
|
|
3156
|
+
return;
|
|
3157
|
+
}
|
|
3090
3158
|
void submitMessage(
|
|
3091
3159
|
event.detail?.text||'',
|
|
3092
3160
|
{source:'speech'}
|
|
@@ -3170,6 +3238,140 @@
|
|
|
3170
3238
|
resizeTextArea();
|
|
3171
3239
|
}
|
|
3172
3240
|
|
|
3241
|
+
function trackRecognitionDraftEdit(){
|
|
3242
|
+
if(!recognitionDraft||writingRecognitionDraft){
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
const state=recognitionDraft;
|
|
3246
|
+
const before=state.value;
|
|
3247
|
+
const after=textArea.value;
|
|
3248
|
+
let start=0;
|
|
3249
|
+
while(start<before.length&&start<after.length&&before[start]===after[start]) start++;
|
|
3250
|
+
let oldEnd=before.length;
|
|
3251
|
+
let newEnd=after.length;
|
|
3252
|
+
while(oldEnd>start&&newEnd>start&&before[oldEnd-1]===after[newEnd-1]){
|
|
3253
|
+
oldEnd--;
|
|
3254
|
+
newEnd--;
|
|
3255
|
+
}
|
|
3256
|
+
if(state.range&&before!==after){
|
|
3257
|
+
const range=state.range;
|
|
3258
|
+
if(oldEnd<=range.start){
|
|
3259
|
+
const shift=newEnd-oldEnd;
|
|
3260
|
+
range.start+=shift;
|
|
3261
|
+
range.end+=shift;
|
|
3262
|
+
}else if(start<range.end){
|
|
3263
|
+
// An intentional edit promotes the hypothesis to user text.
|
|
3264
|
+
// Later recognition must not overwrite that edit.
|
|
3265
|
+
state.range=null;
|
|
3266
|
+
state.editedInterim=true;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
state.value=after;
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
function replaceRecognitionDraft(text,finalLength,state=recognitionDraft){
|
|
3273
|
+
const existing=textArea.value;
|
|
3274
|
+
const range=state.range??{start:existing.length,end:existing.length};
|
|
3275
|
+
const selectionStart=textArea.selectionStart;
|
|
3276
|
+
const selectionEnd=textArea.selectionEnd;
|
|
3277
|
+
const direction=textArea.selectionDirection;
|
|
3278
|
+
const editing=shadowRoot.activeElement===textArea;
|
|
3279
|
+
const wasWriting=writingRecognitionDraft;
|
|
3280
|
+
writingRecognitionDraft=true;
|
|
3281
|
+
try{
|
|
3282
|
+
textArea.value=existing.slice(0,range.start)+text+existing.slice(range.end);
|
|
3283
|
+
if(editing){
|
|
3284
|
+
function moveSelection(position){
|
|
3285
|
+
if(position<=range.start) return position;
|
|
3286
|
+
if(position>=range.end) return position+text.length-(range.end-range.start);
|
|
3287
|
+
return range.start+text.length;
|
|
3288
|
+
}
|
|
3289
|
+
textArea.setSelectionRange(moveSelection(selectionStart),moveSelection(selectionEnd),direction);
|
|
3290
|
+
}
|
|
3291
|
+
state.range={start:range.start+finalLength,end:range.start+text.length};
|
|
3292
|
+
state.value=textArea.value;
|
|
3293
|
+
textArea.dispatchEvent(new Event('input',{bubbles:true,composed:true}));
|
|
3294
|
+
}finally{
|
|
3295
|
+
writingRecognitionDraft=wasWriting;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
function receiveRecognitionDraft(event){
|
|
3300
|
+
if(destroyed||(!recognitionDraft&&host.transcriptionMode!=='draft')) return;
|
|
3301
|
+
const {text='',interim='',operationId=null}=event.detail??{};
|
|
3302
|
+
if(recognitionDraft&&recognitionDraft.operationId!==operationId){
|
|
3303
|
+
finishRecognitionDraft();
|
|
3304
|
+
// A synchronous input observer may already own a newer capture.
|
|
3305
|
+
if(recognitionDraft) return;
|
|
3306
|
+
}
|
|
3307
|
+
if(!recognitionDraft){
|
|
3308
|
+
recognitionDraft={
|
|
3309
|
+
operationId,
|
|
3310
|
+
finalText:'',
|
|
3311
|
+
value:textArea.value,
|
|
3312
|
+
range:null,
|
|
3313
|
+
separator:'',
|
|
3314
|
+
started:false,
|
|
3315
|
+
editedInterim:false
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
trackRecognitionDraftEdit();
|
|
3319
|
+
const state=recognitionDraft;
|
|
3320
|
+
const finalText=text.startsWith(state.finalText)?text.slice(state.finalText.length):text;
|
|
3321
|
+
state.finalText=text;
|
|
3322
|
+
if(state.editedInterim){
|
|
3323
|
+
if(!finalText) return;
|
|
3324
|
+
// The edited words stand in place of this finalized hypothesis.
|
|
3325
|
+
state.editedInterim=false;
|
|
3326
|
+
state.started=false;
|
|
3327
|
+
state.separator='';
|
|
3328
|
+
if(!interim) return;
|
|
3329
|
+
receiveRecognitionDraft({detail:{text,interim,operationId}});
|
|
3330
|
+
return;
|
|
3331
|
+
}
|
|
3332
|
+
if(!state.started&&(finalText||interim)){
|
|
3333
|
+
const incoming=finalText||interim;
|
|
3334
|
+
state.separator=textArea.value&&!/\s$/.test(textArea.value)&&!/^\s/.test(incoming)?'\n':'';
|
|
3335
|
+
state.started=true;
|
|
3336
|
+
}
|
|
3337
|
+
const separator=state.separator;
|
|
3338
|
+
const replacement=finalText||interim?separator+finalText+interim:'';
|
|
3339
|
+
if(finalText) state.separator='';
|
|
3340
|
+
replaceRecognitionDraft(replacement,finalText?separator.length+finalText.length:0,state);
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
function finishRecognitionDraft(event){
|
|
3344
|
+
if(!recognitionDraft||(event&&recognitionDraft.operationId!==event.detail?.operationId)) return;
|
|
3345
|
+
trackRecognitionDraftEdit();
|
|
3346
|
+
const completedDraft=recognitionDraft;
|
|
3347
|
+
recognitionDraft=null;
|
|
3348
|
+
if(completedDraft.range){
|
|
3349
|
+
replaceRecognitionDraft('',0,completedDraft);
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
function appendDraft(text){
|
|
3354
|
+
if(!is.string(text)){
|
|
3355
|
+
throw new TypeError('Chat draft text must be a string.');
|
|
3356
|
+
}
|
|
3357
|
+
if(destroyed||text===''){
|
|
3358
|
+
return false;
|
|
3359
|
+
}
|
|
3360
|
+
const existing=textArea.value;
|
|
3361
|
+
const selectionStart=textArea.selectionStart;
|
|
3362
|
+
const selectionEnd=textArea.selectionEnd;
|
|
3363
|
+
const selectionDirection=textArea.selectionDirection;
|
|
3364
|
+
const editing=shadowRoot.activeElement===textArea;
|
|
3365
|
+
// Join distinct entries without changing either entry's authored text.
|
|
3366
|
+
const separator=existing&&!/\s$/.test(existing)&&!/^\s/.test(text)?'\n':'';
|
|
3367
|
+
textArea.value=existing+separator+text;
|
|
3368
|
+
if(editing){
|
|
3369
|
+
textArea.setSelectionRange(selectionStart,selectionEnd,selectionDirection);
|
|
3370
|
+
}
|
|
3371
|
+
textArea.dispatchEvent(new Event('input',{bubbles:true,composed:true}));
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3173
3375
|
async function resizeTextArea() {
|
|
3174
3376
|
// Reset height to shrink to content size
|
|
3175
3377
|
trackTranscriptScrollPosition();
|
|
@@ -3934,6 +4136,24 @@
|
|
|
3934
4136
|
return message;
|
|
3935
4137
|
}
|
|
3936
4138
|
|
|
4139
|
+
async function print(){
|
|
4140
|
+
if(destroyed) return false;
|
|
4141
|
+
try{
|
|
4142
|
+
const view=await printViewPromise;
|
|
4143
|
+
return destroyed||!view?false:await view.print();
|
|
4144
|
+
}catch(error){
|
|
4145
|
+
reportChatPrintFailure(error);
|
|
4146
|
+
throw error;
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
|
|
4150
|
+
function reportChatPrintFailure(error){
|
|
4151
|
+
arcaneLogging.error('Unable to print the conversation.',error);
|
|
4152
|
+
if(!destroyed){
|
|
4153
|
+
setSessionStatus('error','Unable to open print preview. Please try again.');
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
|
|
3937
4157
|
function reportTTSError(error,boundary='synthesis'){
|
|
3938
4158
|
if(destroyed){
|
|
3939
4159
|
return false;
|
|
@@ -4001,9 +4221,10 @@
|
|
|
4001
4221
|
if(destroyed){
|
|
4002
4222
|
return false;
|
|
4003
4223
|
}
|
|
4224
|
+
destroyed=true;
|
|
4225
|
+
finishRecognitionDraft();
|
|
4004
4226
|
sessionBindingGeneration+=1;
|
|
4005
4227
|
setSessionStatus('destroyed','Chat closed.');
|
|
4006
|
-
destroyed=true;
|
|
4007
4228
|
transcriptResizeObserver?.disconnect();
|
|
4008
4229
|
transcriptMutationObserver?.disconnect();
|
|
4009
4230
|
const destroyReason=createChatSubmissionAbort(
|