arcane-os 0.45.0 → 0.46.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 +21 -0
- package/README.md +1 -1
- package/docs/reference/runtime-components.md +36 -4
- package/docs/reference/runtime-modules.md +48 -8
- package/package.json +2 -2
- package/runtime/arcane/components/file-manager.html +35 -3
- package/runtime/arcane/css/theme.css +41 -0
- package/runtime/arcane/modules/MD.js +14 -0
- package/runtime/arcane/modules/MarkdownMedia.js +41 -13
- package/runtime/arcane/modules/PrintView.js +23 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.46.0
|
|
4
|
+
|
|
5
|
+
- Add public `decodeMarkdownMediaRecord` for complete stored image records or
|
|
6
|
+
JSON text without storage or network access. Preserve singleton record layers,
|
|
7
|
+
original content and explicit decoding errors; never select from multiple
|
|
8
|
+
records. File previews recognize declared image records in JSON-family files
|
|
9
|
+
while ordinary untyped/non-image JSON and application preview hooks keep their
|
|
10
|
+
existing presentation and precedence.
|
|
11
|
+
- Preserve complete standalone IMG tags with multiline quoted descriptions
|
|
12
|
+
through the shared Markdown renderer and its original-source mapping. Native
|
|
13
|
+
HTML handling and literal Markdown examples keep their existing parsing;
|
|
14
|
+
stored Markdown is unchanged and requires no migration.
|
|
15
|
+
- Use the existing light paper palette and 15mm page margins for rendered print
|
|
16
|
+
snapshots and direct shared-theme printing. Preserve full content, rendered
|
|
17
|
+
fonts, image pixels, SVG artwork, live screen preferences, cancellation and
|
|
18
|
+
resource ownership through afterprint. A dialog request is not proof of
|
|
19
|
+
physical printing or PDF completion.
|
|
20
|
+
- Keep image destinations, export selection, model and credentials application
|
|
21
|
+
owned. No saved-data migration, model/provider/default change or app layout
|
|
22
|
+
change is introduced.
|
|
23
|
+
|
|
3
24
|
## 0.45.0
|
|
4
25
|
|
|
5
26
|
- Add public MarkdownMedia save, read, reference parsing and hydration helpers.
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
|
|
|
19
19
|
`arcane/` runtime. Both profiles share the theme, packaging, event, cancellation,
|
|
20
20
|
and browser run contracts while retaining their selected application layout.
|
|
21
21
|
|
|
22
|
-
This checkout defines the `0.
|
|
22
|
+
This checkout defines the `0.46.0` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
|
@@ -54,6 +54,16 @@ imported component's `destroy()` through `HTMLImport`; callers that invoke
|
|
|
54
54
|
`destroy()` directly retain responsibility for removing the element when
|
|
55
55
|
appropriate.
|
|
56
56
|
|
|
57
|
+
## Shared print presentation
|
|
58
|
+
|
|
59
|
+
Applications loading the shared `theme.css` receive a print-only base light
|
|
60
|
+
palette with a white paper surface and `15mm` page margins. Root, body, and
|
|
61
|
+
scoped `data-arcane-palette` tokens override screen/custom colors during print;
|
|
62
|
+
root/body margins are reset. Saved preferences, screen presentation, scrolling,
|
|
63
|
+
media, and other layout rules remain unchanged. Registered rendered-print
|
|
64
|
+
views use the separate [`PrintView.js`](runtime-modules.md#printviewjs) snapshot
|
|
65
|
+
owner described with the editor and file preview below.
|
|
66
|
+
|
|
57
67
|
## Canonical inventory
|
|
58
68
|
|
|
59
69
|
| Component | Capability | Principal methods | Events | Normalization |
|
|
@@ -919,7 +929,16 @@ extension. Routing compares the lowercase MIME essence without its parameters;
|
|
|
919
929
|
the callback's `mimeType` and the file content remain unchanged.
|
|
920
930
|
HTML uses its complete original string in `srcdoc` without an
|
|
921
931
|
added sandbox policy; Markdown reads the complete original text before
|
|
922
|
-
rendering.
|
|
932
|
+
rendering. With no descriptor or `previewTransform`, JSON, JSONL, and NDJSON
|
|
933
|
+
files are read in full and recognized through
|
|
934
|
+
[`decodeMarkdownMediaRecord()`](runtime-modules.md#markdownmediajs). A complete
|
|
935
|
+
stored image record, including nested single-record arrays, opens as an image
|
|
936
|
+
when its decoded Blob has an `image/` media type. Untyped/non-image records,
|
|
937
|
+
multiple records, and unrecognized content keep their ordinary JSON-family
|
|
938
|
+
presentation; malformed recognized image encoding follows the existing
|
|
939
|
+
file-open failure path.
|
|
940
|
+
This recognition leaves saved content unchanged and performs no migration.
|
|
941
|
+
Untransformed JSONL/NDJSON shows its complete original text,
|
|
923
942
|
including lines that are not JSON records. Other binary files offer a download
|
|
924
943
|
of the original `File`.
|
|
925
944
|
The larger file-preview dialog reuses `--modal-width`, `--modal-max-height`,
|
|
@@ -931,6 +950,8 @@ preview ahead of the editor. Printing uses the shared
|
|
|
931
950
|
[`PrintView.js`](runtime-modules.md#printviewjs) owner; accessible HTML frames
|
|
932
951
|
are copied from their current rendered document, never substituted with source
|
|
933
952
|
text. Explicit printing waits for images/fonts and HTML frame readiness.
|
|
953
|
+
The shared print surface uses a light paper palette and `15mm` page margins
|
|
954
|
+
while preserving image/SVG colors and leaving the screen theme unchanged.
|
|
934
955
|
Native PDF and inaccessible embedded viewers require their own print command.
|
|
935
956
|
`true` means the browser print dialog was requested, not that paper or a PDF
|
|
936
957
|
was produced. Closed, destroyed, or unavailable previews return `false`;
|
|
@@ -945,6 +966,9 @@ pending media because it cannot await them. Closing or replacing a preview
|
|
|
945
966
|
cancels display readiness without waiting for storage; URLs already retained
|
|
946
967
|
by a requested print remain until `afterprint`. Original Markdown and ordinary
|
|
947
968
|
external image URLs remain unchanged.
|
|
969
|
+
Standalone IMG blocks with multiline quoted descriptions use the shared
|
|
970
|
+
[`MD.js`](runtime-modules.md#mdjs) tokenizer in both ordinary and source-mapped
|
|
971
|
+
rendering; the complete description and stored Markdown remain intact.
|
|
948
972
|
|
|
949
973
|
`await close()` cancels pending presentation work and closes delete, file, and
|
|
950
974
|
directory dialogs in that order. A settled preview keeps its rendered content,
|
|
@@ -957,7 +981,7 @@ writes complete through the storage owner without reopening closed dialogs.
|
|
|
957
981
|
|
|
958
982
|
Events: `file-manager-ready`, `file-manager-select`, `file-manager-open`, `file-manager-action`.
|
|
959
983
|
|
|
960
|
-
Shared dependencies: [`DBOPFS.js`](runtime-modules.md#dbopfsjs), [`File.js`](runtime-entities.md#filejs), [`MD.js`](runtime-modules.md#mdjs), [`WaitForComponent.js`](runtime-modules.md#waitforcomponentjs).
|
|
984
|
+
Shared dependencies: [`DBOPFS.js`](runtime-modules.md#dbopfsjs), [`File.js`](runtime-entities.md#filejs), [`MD.js`](runtime-modules.md#mdjs), [`MarkdownMedia.js`](runtime-modules.md#markdownmediajs), [`PrintView.js`](runtime-modules.md#printviewjs), [`WaitForComponent.js`](runtime-modules.md#waitforcomponentjs).
|
|
961
985
|
|
|
962
986
|
### Availability and normalization
|
|
963
987
|
|
|
@@ -1082,6 +1106,9 @@ diagnostics remain in the developer console; successful images and document
|
|
|
1082
1106
|
text remain visible. Replacing, clearing, failing, or destroying the document
|
|
1083
1107
|
releases its display resources without deleting stored media. Markdown source
|
|
1084
1108
|
and ordinary external image URLs remain unchanged.
|
|
1109
|
+
Complete standalone IMG blocks with quoted multiline descriptions render
|
|
1110
|
+
through the shared [`MD.js`](runtime-modules.md#mdjs) tokenizer without
|
|
1111
|
+
rewriting saved Markdown.
|
|
1085
1112
|
|
|
1086
1113
|
Shared dependencies: [`MD.js`](runtime-modules.md#mdjs), [`MarkdownMedia.js`](runtime-modules.md#markdownmediajs).
|
|
1087
1114
|
|
|
@@ -1125,8 +1152,10 @@ The visible **Print** control and `await print()` print the complete rendered
|
|
|
1125
1152
|
Markdown with `entryTitle`, independently of saving. Native browser Print uses
|
|
1126
1153
|
that same registered view unless an open file preview takes precedence.
|
|
1127
1154
|
The SDK copies current rendered DOM/styles into a temporary print-only view,
|
|
1128
|
-
leaving the editor's source, controls, and screen layout in place.
|
|
1129
|
-
|
|
1155
|
+
leaving the editor's source, controls, and screen layout in place. Both explicit
|
|
1156
|
+
and native print use the shared light paper palette and `15mm` page margins;
|
|
1157
|
+
image pixels and SVG artwork keep their colors. Explicit printing waits for
|
|
1158
|
+
resource readiness; native `beforeprint` is synchronous
|
|
1130
1159
|
and uses the currently rendered content. `print()` returns `false` when the
|
|
1131
1160
|
view is unavailable, rejects preparation failures, and returns `true` only
|
|
1132
1161
|
when the browser dialog was requested. Completion of physical printing or PDF
|
|
@@ -1142,6 +1171,9 @@ waits for the current image reads/decoding, follows a replacement preview, and
|
|
|
1142
1171
|
rechecks readiness before capture. Native Print reports still-pending media
|
|
1143
1172
|
synchronously; it cannot wait or cancel the native dialog. Print-owned URLs
|
|
1144
1173
|
outlive a changed or destroyed editor until `afterprint`.
|
|
1174
|
+
The shared [`MD.js`](runtime-modules.md#mdjs) tokenizer preserves complete
|
|
1175
|
+
standalone IMG blocks with quoted multiline descriptions in ordinary and
|
|
1176
|
+
source-mapped rendering. Existing saved markup needs no rewriting.
|
|
1145
1177
|
|
|
1146
1178
|
`configure({fit:true,followPreview:true})` opts into the two independent
|
|
1147
1179
|
behaviors. Both options default to `false`; omitted options retain their
|
|
@@ -96,7 +96,7 @@ own asynchronous work, cancellation, and backpressure.
|
|
|
96
96
|
| [`Mail.js`](#mailjs) | esm | Builds complete reports and prefers the native mail capability with an explicit HTTP transport fallback. | Browser/native hybrid + cloud | Mail inputs/results normalized; transport failures mixed. |
|
|
97
97
|
| [`MailOutbox.mjs`](#mailoutboxmjs) | esm | Persists complete mail reports before delivery and normalizes idempotent enqueue, retry, reconciliation, and invalid-record maintenance. | Browser/native WebView or compatible injected host | Complete records, full work, cancellation, and lifecycle states normalized; storage, lock, and delivery failures coded. |
|
|
98
98
|
| [`MailTransport.mjs`](#mailtransportmjs) | esm | Sends one complete mail report to a normalized HTTP(S) endpoint. | Browser/server with fetch + cloud | Normalized endpoint and transport errors; remote detail preserved. |
|
|
99
|
-
| [`MarkdownMedia.js`](#markdownmediajs) | esm | Saves local Markdown images separately and resolves
|
|
99
|
+
| [`MarkdownMedia.js`](#markdownmediajs) | esm | Saves local Markdown images separately, decodes complete stored records, and resolves stable references in rendered views. | Browser / supported native WebView | Complete image records, concurrent display reads, cancellation, and print-owned URL lifetime. |
|
|
100
100
|
| [`MarkdownSpeech.js`](#markdownspeechjs) | esm | Removes repeated Markdown formatting marks from streamed narration. | Cross-host | Speech-only filtering; single marks and ordinary punctuation remain literal. |
|
|
101
101
|
| [`Marked.min.js`](#markedminjs) | esm | Vendored Marked 18.0.5 Markdown lexer, parser, renderer, extension, and walk-token API. | Cross-host vendor module | Vendor-native Marked contract. |
|
|
102
102
|
| [`MD.js`](#mdjs) | esm | Renders complete Markdown with Marked and optionally maps source blocks to rendered comment anchors. | Browser / native WebView | Complete raw and rendered Marked values; opt-in original-source offsets; parse errors vendor-native. |
|
|
@@ -2816,9 +2816,18 @@ Public package import: `arcane-os/modules/MarkdownMedia.js`.
|
|
|
2816
2816
|
- `parseMarkdownMediaReference(reference)` returns `{tableName,fileName}` for
|
|
2817
2817
|
a local reference, or `null` for an ordinary URL. Malformed local addresses
|
|
2818
2818
|
report their actual parsing error.
|
|
2819
|
+
- `decodeMarkdownMediaRecord(value)` synchronously decodes a complete
|
|
2820
|
+
`{dataUrl,mediaType?}` record or its JSON text into a `Blob`, without storage
|
|
2821
|
+
or network access. Nested single-record arrays are unwrapped; arrays with
|
|
2822
|
+
multiple records are never reduced to one image. Unrecognized content,
|
|
2823
|
+
including text that is not complete JSON, returns `null`. A recognized
|
|
2824
|
+
record with unreadable base64 data-URL content throws its decoding error.
|
|
2825
|
+
Omitted `mediaType` preserves the Blob's empty type. Input remains unchanged.
|
|
2819
2826
|
- `await readMarkdownMedia(reference)` returns the complete `Blob`, using
|
|
2820
2827
|
only local storage and data-URL decoding; stored strings are never fetched
|
|
2821
|
-
as network addresses.
|
|
2828
|
+
as network addresses. It uses the same record decoder after loading, while
|
|
2829
|
+
preserving JSON parse errors for unreadable stored JSON strings and reporting
|
|
2830
|
+
an unreadable record as an error rather than `null`.
|
|
2822
2831
|
- `hydrateMarkdownMedia(root,{signal}={})` returns `{ready,destroy,retain}`
|
|
2823
2832
|
immediately. It includes an IMG root and descendant IMG elements, removes
|
|
2824
2833
|
local-reference `src` values synchronously, starts independent reads
|
|
@@ -2841,6 +2850,9 @@ records their existing JSON backup/export includes. Reusing the same filename
|
|
|
2841
2850
|
supports an application-selected save-only retry without another generation
|
|
2842
2851
|
request. The helper performs no existing-data migration.
|
|
2843
2852
|
|
|
2853
|
+
Exact exports: `saveMarkdownMedia`, `parseMarkdownMediaReference`,
|
|
2854
|
+
`decodeMarkdownMediaRecord`, `readMarkdownMedia`, `hydrateMarkdownMedia`.
|
|
2855
|
+
|
|
2844
2856
|
### Availability and normalization
|
|
2845
2857
|
|
|
2846
2858
|
Browser or native WebView with the existing app-scoped DBOPFS owner, Blob,
|
|
@@ -2896,16 +2908,29 @@ mapping, not exact rendered glyph or caret geometry.
|
|
|
2896
2908
|
|
|
2897
2909
|
Assigning `raw` or calling `append(string)` rebuilds both the complete rendered
|
|
2898
2910
|
result and its current mapping. Without `sourceMap:true`, `sourceMap` is empty
|
|
2899
|
-
and the
|
|
2911
|
+
and the same parsing runs without comment anchors. Existing raw content, link
|
|
2900
2912
|
behavior, no-op `rendered` setter, and complete `append()` result remain.
|
|
2901
2913
|
|
|
2914
|
+
Both rendering paths first use Marked's native HTML block tokenizer. When it
|
|
2915
|
+
does not recognize a complete standalone `<img>` tag with a newline inside a
|
|
2916
|
+
quoted attribute value, MD keeps that complete tag together as one HTML block.
|
|
2917
|
+
This includes blank lines in an image's full `alt` description. The tag must
|
|
2918
|
+
start at an existing Markdown block boundary with at most three leading spaces
|
|
2919
|
+
and end on its own line or at the end of the source. Separate inserted image
|
|
2920
|
+
blocks from surrounding Markdown with blank lines. Native HTML handling,
|
|
2921
|
+
inline prose, fenced/indented/inline code, escaped examples, and incomplete
|
|
2922
|
+
tags retain their existing parsing behavior. No source is rewritten or saved
|
|
2923
|
+
data migrated. Marked's existing rendered line-ending normalization remains;
|
|
2924
|
+
mapped offsets still address the exact original source, including CRLF or CR.
|
|
2925
|
+
|
|
2902
2926
|
Exact exports: `default`.
|
|
2903
2927
|
|
|
2904
2928
|
### Availability and normalization
|
|
2905
2929
|
|
|
2906
|
-
**Browser / native WebView.**
|
|
2907
|
-
|
|
2908
|
-
scrolling belong to the
|
|
2930
|
+
**Browser / native WebView.** Marked parsing, including the shared standalone
|
|
2931
|
+
image-token correction above; parse errors remain vendor-native. Rendering and
|
|
2932
|
+
the getters are DOM-independent. DOM lookup and scrolling belong to the
|
|
2933
|
+
consuming component;
|
|
2909
2934
|
mapping performs no persistence or viewport movement. Transport: Marked.
|
|
2910
2935
|
[Deep protocol details](protocols.md).
|
|
2911
2936
|
|
|
@@ -3345,6 +3370,13 @@ Shared rendered-print owner used by Markdown Editor and File Manager. It
|
|
|
3345
3370
|
captures the current DOM and computed styles, including accessible rendered
|
|
3346
3371
|
HTML frames, complete text, selected images, and the current title. A temporary
|
|
3347
3372
|
print-only surface expands scrollable content without editing the live screen.
|
|
3373
|
+
That surface uses the shared Arcane light-theme text, paper, and border colors,
|
|
3374
|
+
with `15mm` page margins. It retains rendered fonts, emphasis, and complete
|
|
3375
|
+
content while removing screen-theme text/shadow paint from ordinary HTML and
|
|
3376
|
+
pseudo-elements. Links are underlined and table cells receive visible borders.
|
|
3377
|
+
Image pixels, canvas snapshots, and SVG artwork keep their own colors. These
|
|
3378
|
+
print rules leave the live screen theme unchanged; browser print settings still
|
|
3379
|
+
control the eventual page output.
|
|
3348
3380
|
|
|
3349
3381
|
### Public surface
|
|
3350
3382
|
|
|
@@ -4022,12 +4054,20 @@ is applied once the body is available.
|
|
|
4022
4054
|
|
|
4023
4055
|
The shared `theme.css` owns the default, warm, curious, hopeful, harmony, and
|
|
4024
4056
|
warrior palettes. Each works with explicit light/dark or system appearance.
|
|
4025
|
-
`layout.css` imports that same owner.
|
|
4026
|
-
through `data-arcane-skin="custom"` and their inline tokens.
|
|
4057
|
+
`layout.css` imports that same owner. On screen, explicit custom themes retain
|
|
4058
|
+
precedence through `data-arcane-skin="custom"` and their inline tokens.
|
|
4027
4059
|
The bare root retains the generic neutral light/dark baseline; the prior
|
|
4028
4060
|
layout default palette is selected explicitly through `data-user-skin="default"`,
|
|
4029
4061
|
`body.default` compatibility, or the default swatch.
|
|
4030
4062
|
|
|
4063
|
+
During printing, `theme.css` applies the existing base light palette, with a
|
|
4064
|
+
white paper surface and `15mm` page margins, to the root, body, and scoped
|
|
4065
|
+
`data-arcane-palette` elements. These print-only tokens override screen/custom
|
|
4066
|
+
palette tokens. It resets the root/body margin without changing saved
|
|
4067
|
+
preferences, screen presentation, scrolling, media, or other layout rules.
|
|
4068
|
+
The separate [`PrintView.js`](#printviewjs) owner uses the same light palette
|
|
4069
|
+
and margins for its expanded rendered-content snapshot.
|
|
4070
|
+
|
|
4031
4071
|
A scoped `data-arcane-palette="warm"` element exposes the same palette variables
|
|
4032
4072
|
for a chooser preview without changing the page's selection. Use any of the
|
|
4033
4073
|
six named palettes and paint the preview with ordinary shared variables such
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
87
87
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
88
88
|
"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/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
89
|
-
"test:functional": "node ./bin/arcane-test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.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/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/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/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/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
89
|
+
"test:functional": "node ./bin/arcane-test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.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/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/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/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/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
90
90
|
"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-plan.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",
|
|
91
91
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.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",
|
|
92
92
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -2142,8 +2142,9 @@
|
|
|
2142
2142
|
const rendered=document.createElement('div');
|
|
2143
2143
|
rendered.className='file-preview';
|
|
2144
2144
|
let value=fileRef.parsed;
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2145
|
+
const jsonFile=['json','jsonl','ndjson'].includes(fileRef.ext);
|
|
2146
|
+
const transformed=jsonFile&&is.function(host.previewTransform);
|
|
2147
|
+
let completeJSON;
|
|
2147
2148
|
|
|
2148
2149
|
if(transformed){
|
|
2149
2150
|
value=await host.previewTransform(
|
|
@@ -2155,6 +2156,37 @@
|
|
|
2155
2156
|
}
|
|
2156
2157
|
);
|
|
2157
2158
|
preview.controller.signal.throwIfAborted();
|
|
2159
|
+
}else if(jsonFile){
|
|
2160
|
+
// Recognition needs the complete file: FileEntity's parsed JSONL
|
|
2161
|
+
// rows do not include every unparseable line in the original.
|
|
2162
|
+
const [{decodeMarkdownMediaRecord},text]=await Promise.all([
|
|
2163
|
+
import('../modules/MarkdownMedia.js'),
|
|
2164
|
+
fileRef.text()
|
|
2165
|
+
]);
|
|
2166
|
+
preview.controller.signal.throwIfAborted();
|
|
2167
|
+
completeJSON=text;
|
|
2168
|
+
let imageRecord;
|
|
2169
|
+
try {
|
|
2170
|
+
imageRecord = JSON.parse(completeJSON);
|
|
2171
|
+
} catch {
|
|
2172
|
+
// Mixed JSONL and ordinary text keep their complete presentation.
|
|
2173
|
+
imageRecord = null;
|
|
2174
|
+
}
|
|
2175
|
+
while (is.array(imageRecord) && imageRecord.length === 1) {
|
|
2176
|
+
imageRecord = imageRecord[0];
|
|
2177
|
+
}
|
|
2178
|
+
// A dataUrl property alone does not identify an image record.
|
|
2179
|
+
// Recognize its declared media type before attempting any decoding.
|
|
2180
|
+
if (is.string(imageRecord?.mediaType)
|
|
2181
|
+
&& imageRecord.mediaType.toLowerCase().startsWith('image/')) {
|
|
2182
|
+
const image = decodeMarkdownMediaRecord(imageRecord);
|
|
2183
|
+
if (image?.type.startsWith('image/')) {
|
|
2184
|
+
return renderDescriptor(
|
|
2185
|
+
{kind: 'image', content: image, alt: fileRef.name},
|
|
2186
|
+
preview
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2158
2190
|
}
|
|
2159
2191
|
|
|
2160
2192
|
switch(fileRef.ext){
|
|
@@ -2168,7 +2200,7 @@
|
|
|
2168
2200
|
case 'jsonl':
|
|
2169
2201
|
case 'ndjson':{
|
|
2170
2202
|
if(!transformed){
|
|
2171
|
-
rendered.append(createPreformatted(
|
|
2203
|
+
rendered.append(createPreformatted(completeJSON));
|
|
2172
2204
|
break;
|
|
2173
2205
|
}
|
|
2174
2206
|
const records=is.array(value)?value:[];
|
|
@@ -327,3 +327,44 @@ body{
|
|
|
327
327
|
scroll-behavior:auto !important;
|
|
328
328
|
transition-duration:.01ms !important;
|
|
329
329
|
}
|
|
330
|
+
|
|
331
|
+
/* Paper uses the existing base light palette regardless of the screen skin.
|
|
332
|
+
* Print-only importance also overrides custom-theme inline tokens; no saved
|
|
333
|
+
* preference, screen layout, media paint, or scrolling state changes here.
|
|
334
|
+
*/
|
|
335
|
+
@media print {
|
|
336
|
+
@page {
|
|
337
|
+
margin: 15mm;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
:root,
|
|
341
|
+
body,
|
|
342
|
+
[data-arcane-palette] {
|
|
343
|
+
color-scheme: light !important;
|
|
344
|
+
--background: rgb(255, 255, 255) !important;
|
|
345
|
+
--modal-background: rgb(255, 255, 255) !important;
|
|
346
|
+
--text-color: rgb(23, 34, 56) !important;
|
|
347
|
+
--primary-color: rgb(23, 34, 56) !important;
|
|
348
|
+
--secondary-color: rgb(229, 233, 243) !important;
|
|
349
|
+
--accent-color: var(--focus-color) !important;
|
|
350
|
+
--button-text-color: rgb(255, 255, 255) !important;
|
|
351
|
+
--border-color: rgb(203, 211, 226) !important;
|
|
352
|
+
--focus-color: rgb(118, 87, 213) !important;
|
|
353
|
+
--muted-text-color: rgb(95, 107, 130) !important;
|
|
354
|
+
--success-color: rgb(39, 122, 75) !important;
|
|
355
|
+
--warning-color: rgb(154, 101, 0) !important;
|
|
356
|
+
--danger-color: rgb(165, 56, 56) !important;
|
|
357
|
+
--review-color: rgb(133, 85, 165) !important;
|
|
358
|
+
--shadow-color: rgba(23, 34, 56, .16) !important;
|
|
359
|
+
--platinum: rgb(102, 112, 133) !important;
|
|
360
|
+
--icon-filter: none !important;
|
|
361
|
+
--file-icon-filter: hue-rotate(8deg) saturate(.62) brightness(.86) !important;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
html,
|
|
365
|
+
body {
|
|
366
|
+
margin: 0;
|
|
367
|
+
background-color: var(--modal-background);
|
|
368
|
+
color: var(--text-color);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
@@ -6,6 +6,20 @@ const markdownOptions = {
|
|
|
6
6
|
async: false,
|
|
7
7
|
pedantic: false,
|
|
8
8
|
gfm: true,
|
|
9
|
+
tokenizer: {
|
|
10
|
+
html(src) {
|
|
11
|
+
const native = marked.Tokenizer.prototype.html.call(this, src);
|
|
12
|
+
if (native) return native;
|
|
13
|
+
// Quoted image values can contain blank lines that would otherwise
|
|
14
|
+
// split the tag into Markdown paragraphs before inline HTML parsing.
|
|
15
|
+
const image = /^ {0,3}<img(?:\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*\/?>(?=[ \t]*(?:\n|$))/i;
|
|
16
|
+
const match = image.exec(src);
|
|
17
|
+
if (!match) return;
|
|
18
|
+
const quotedValues = match[0].match(/"[^"]*"|'[^']*'/g) || [];
|
|
19
|
+
if (!quotedValues.some(function containsNewline(value) {return value.includes('\n');})) return;
|
|
20
|
+
return {type:'html', block:true, raw:match[0], text:match[0]};
|
|
21
|
+
}
|
|
22
|
+
},
|
|
9
23
|
renderer: {
|
|
10
24
|
link(href, title, text) {
|
|
11
25
|
const link = marked.Renderer.prototype.link.call(this, href, title, text);
|
|
@@ -59,24 +59,28 @@ export async function saveMarkdownMedia({
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Decode a complete local media record or its JSON text without storage or
|
|
64
|
+
* network access. Unrecognized content returns null; recognized records with
|
|
65
|
+
* unreadable encoding throw. Records carry dataUrl and an optional mediaType;
|
|
66
|
+
* omitting mediaType preserves Blob's empty type. No source content is changed.
|
|
67
|
+
*/
|
|
68
|
+
export function decodeMarkdownMediaRecord(value) {
|
|
69
|
+
let record = value;
|
|
70
|
+
if (is.string(record)) {
|
|
71
|
+
try {
|
|
72
|
+
record = JSON.parse(record);
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
72
76
|
}
|
|
73
77
|
// DBOPFS parses JSONL/NDJSON as record arrays; restoring a parsed backup can
|
|
74
78
|
// wrap its one record again. Never choose one image from multiple records.
|
|
75
|
-
let record = is.string(stored) ? JSON.parse(stored) : stored;
|
|
76
79
|
while (is.array(record) && record.length === 1) record = record[0];
|
|
77
|
-
|
|
80
|
+
if (!record || !is.object(record) || is.array(record) || !('dataUrl' in record)) return null;
|
|
81
|
+
const encoded = /^data:[^,]*;base64,([\s\S]*)$/u.exec(record.dataUrl);
|
|
78
82
|
if (!encoded) {
|
|
79
|
-
throw new TypeError(
|
|
83
|
+
throw new TypeError('Markdown image has an unreadable data URL.');
|
|
80
84
|
}
|
|
81
85
|
// Decode locally; a stored string is never passed to Fetch or a network API.
|
|
82
86
|
const decoded = atob(encoded[1]);
|
|
@@ -86,6 +90,30 @@ export async function readMarkdownMedia(reference) {
|
|
|
86
90
|
return new Blob([content], {type: record.mediaType});
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/** Read the complete local image for display or application-owned export. */
|
|
94
|
+
export async function readMarkdownMedia(reference) {
|
|
95
|
+
const address = parseMarkdownMediaReference(reference);
|
|
96
|
+
if (!address) {
|
|
97
|
+
throw new TypeError('Expected an arcane-media: reference.');
|
|
98
|
+
}
|
|
99
|
+
const {default: DBOPFS} = await import('./DBOPFS.js');
|
|
100
|
+
const stored = await new DBOPFS().get(address.tableName, address.fileName);
|
|
101
|
+
if (stored === null || stored === undefined) {
|
|
102
|
+
throw new DOMException(`Markdown image was not found: ${reference}`, 'NotFoundError');
|
|
103
|
+
}
|
|
104
|
+
// Preserve the strict stored-JSON parser boundary for explicit reads.
|
|
105
|
+
const record = is.string(stored) ? JSON.parse(stored) : stored;
|
|
106
|
+
const unreadable = `Markdown image has an unreadable data URL: ${reference}`;
|
|
107
|
+
try {
|
|
108
|
+
const blob = decodeMarkdownMediaRecord(record);
|
|
109
|
+
if (blob) return blob;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (!(error instanceof TypeError)) throw error;
|
|
112
|
+
throw new TypeError(unreadable, {cause: error});
|
|
113
|
+
}
|
|
114
|
+
throw new TypeError(unreadable);
|
|
115
|
+
}
|
|
116
|
+
|
|
89
117
|
/**
|
|
90
118
|
* Resolve only local Markdown IMG references beneath a rendered root (including
|
|
91
119
|
* an IMG root). Markdown source and ordinary URLs are never changed. Independent
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Is from 'strong-type';
|
|
2
|
+
import {arcaneLightThemeTokens} from '../entities/Theme.js';
|
|
2
3
|
|
|
3
4
|
const is=new Is(false);
|
|
4
5
|
const registries=new WeakMap();
|
|
@@ -400,17 +401,36 @@ function capture(registry,session){
|
|
|
400
401
|
const heading=document.createElement('h1');
|
|
401
402
|
heading.textContent=session.title;
|
|
402
403
|
const rendered=copyRendered(session.content,state,true);
|
|
403
|
-
|
|
404
|
+
// Screen layout and fonts describe the rendered content; screen-theme paint
|
|
405
|
+
// does not describe a readable paper surface. Keep this palette in the
|
|
406
|
+
// snapshot, including its pseudo-elements, without recoloring SVG artwork.
|
|
407
|
+
styles.textContent=`
|
|
408
|
+
:host{color-scheme:light!important;color:${arcaneLightThemeTokens.text}!important;background:${arcaneLightThemeTokens.surface}!important;font:12pt/1.5 serif}
|
|
409
|
+
:host>h1{margin:0 0 .75em;font-size:1.5em;line-height:1.2}
|
|
410
|
+
[data-arcane-print-node]:not(svg,svg *,img,video,audio),
|
|
411
|
+
[data-arcane-print-node]:not(svg,svg *,img,video,audio)::before,
|
|
412
|
+
[data-arcane-print-node]:not(svg,svg *,img,video,audio)::after{
|
|
413
|
+
color:${arcaneLightThemeTokens.text}!important;background-color:transparent!important;
|
|
414
|
+
border-color:${arcaneLightThemeTokens.border}!important;color-scheme:light!important;
|
|
415
|
+
-webkit-text-fill-color:currentColor!important;-webkit-text-stroke:0!important;
|
|
416
|
+
text-decoration-color:currentColor!important;text-shadow:none!important;box-shadow:none!important
|
|
417
|
+
}
|
|
418
|
+
a[data-arcane-print-node]:not(svg *){text-decoration:underline!important}
|
|
419
|
+
table[data-arcane-print-node]:not(svg *){border-collapse:collapse!important}
|
|
420
|
+
th[data-arcane-print-node]:not(svg *),td[data-arcane-print-node]:not(svg *){border:1px solid ${arcaneLightThemeTokens.border}!important;padding:.35em .5em!important}
|
|
421
|
+
${state.rules.join('\n')}
|
|
422
|
+
`;
|
|
404
423
|
root.append(styles,heading,rendered);
|
|
405
424
|
const sheet=document.createElement('style');
|
|
406
425
|
session.sheet=sheet;
|
|
407
426
|
sheet.textContent=`
|
|
408
427
|
[data-arcane-print-stage]{display:none!important}
|
|
409
428
|
@media print{
|
|
410
|
-
|
|
429
|
+
@page{margin:15mm}
|
|
430
|
+
html:root,html:root>body{display:block!important;height:auto!important;min-height:0!important;max-height:none!important;overflow:visible!important;contain:none!important;margin:0!important;padding:0!important;border:0!important;background:${arcaneLightThemeTokens.surface}!important;color:${arcaneLightThemeTokens.text}!important;color-scheme:light!important}
|
|
411
431
|
html:root>body>*:not([data-arcane-print-stage]){display:none!important}
|
|
412
432
|
html:root>body::before,html:root>body::after{display:none!important}
|
|
413
|
-
html:root>body>[data-arcane-print-stage]{display:block!important;position:static!important;width:auto!important;height:auto!important;overflow:visible!important}
|
|
433
|
+
html:root>body>[data-arcane-print-stage]{display:block!important;position:static!important;width:auto!important;height:auto!important;overflow:visible!important;margin:0!important;padding:0!important;border:0!important}
|
|
414
434
|
}
|
|
415
435
|
`;
|
|
416
436
|
document.head.append(sheet);
|