rapier-embed 1.2.42 → 1.2.44

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/README.md CHANGED
@@ -20,7 +20,7 @@ Honor no-upload or no-host instructions. The workflow choices below are alternat
20
20
  Embed Rapier's Markdown editor or lightweight reader in your web app. Each build is one offline HTML file. The helper is an ES module: `import {Rapier} from 'rapier-embed'` or `<script type="module">`; there is no classic script or UMD build. Your
21
21
  app owns the document and storage; its agent edits live through WebMCP. The person sees the agent's presence and
22
22
  change marks, taps a change to see the before and undoes anything. Honor the person's Will/1 regions.
23
- The reader is one file of about 380 kB, `rapier.html` about 4.0 MB, with optional fonts and plug-ins. The helper is MIT with no dependencies. The two builds it frames are AGPL-3.0-only: use them under the AGPL, or
23
+ The reader is one file of about 400 kB, `rapier.html` about 4.0 MB, with optional fonts and plug-ins. The helper is MIT with no dependencies. The two builds it frames are AGPL-3.0-only: use them under the AGPL, or
24
24
  buy a commercial license for other terms (`LICENSING.md` in the Rapier repository).
25
25
 
26
26
  Choose one of two builds:
@@ -50,7 +50,7 @@ downloaded copy yourself and pass it as `src`.
50
50
  document access remain enforced.
51
51
 
52
52
  ```sh
53
- npm install rapier-embed@1.2.42
53
+ npm install rapier-embed@1.2.44
54
54
  ```
55
55
 
56
56
  Or copy the package's `embed.mjs` and `contract.mjs` into one directory of your app; no runtime CDN is needed.
@@ -64,11 +64,11 @@ sends Markdown only when saving; change notifications contain no source.
64
64
  Rapier is a phone-first Markdown editor for notes, diagrams, drawing and watercolor painting. It runs offline
65
65
  as one HTML file on Android, Web, Windows and Linux.
66
66
  Collaborate over MCP and WebMCP, use optional plain-file sync to your own cloud storage, or embed the
67
- editor and lightweight reader (about 380 kB). The agent edits live; the person sees its presence and
67
+ editor and lightweight reader (about 400 kB). The agent edits live; the person sees its presence and
68
68
  changes, taps a change to see the before and undoes anything. Use `show {target:"comparison"}` to show a diff deliberately.
69
69
 
70
70
  1. **Work together live.** Connect to `https://mcp.rapier.website/door/mcp`, then call `open` to share one document with the person.
71
- 2. **Deliver an offline editor.** Run `npx rapier-html@1.2.42 notes.md` to put the document and editor in one HTML file ([rapier-html](https://github.com/jackskip22/rapier-plugins/tree/main/npm/rapier-html)).
71
+ 2. **Deliver an offline editor.** Run `npx rapier-html@1.2.44 notes.md` to put the document and editor in one HTML file ([rapier-html](https://github.com/jackskip22/rapier-plugins/tree/main/npm/rapier-html)).
72
72
  3. **Embed in your app.** Install [rapier-embed](https://github.com/jackskip22/rapier-plugins/tree/main/npm/rapier-embed); mount the editor or reader, with your app's agent over WebMCP in the editor.
73
73
  4. **Keep portable Markdown.** Use [rapier-markdown-kit](https://github.com/jackskip22/rapier-plugins/tree/main/npm/rapier-markdown-kit) for pictures, editable SVG drawings and layout in one `.md` file. Respect [Will/1](https://github.com/jackskip22/will) `keep`, `append` and `edit` regions.
74
74
  5. **Encode JPEG XL.** Install [rapier-jxl](https://github.com/jackskip22/rapier-jxl) to encode pixels, photographs and existing JPEGs in JavaScript.
@@ -141,7 +141,7 @@ A copy in your app works offline and changes only when you update it.
141
141
  1. Download the build you frame into a static directory, for example
142
142
  `https://cdn.jsdelivr.net/gh/jackskip22/rapier-plugins@cdn-v<version>/embed/rapier-reader.html` to `public/vendor/rapier/`.
143
143
  2. Fetch the plug-ins your build uses into one directory beside it, from the manifest of the same version:
144
- `npx rapier-embed@1.2.42 plugins public/vendor/rapier/plugins --build reader`.
144
+ `npx rapier-embed@1.2.44 plugins public/vendor/rapier/plugins --build reader`.
145
145
  3. Mount with `src` and `plugins` pointing at your copies. Serve them over HTTPS (HTTP on localhost) and allow
146
146
  their origin in your `frame-src`. A same-origin parent can script its frame; use a dedicated origin when the
147
147
  frame must be isolated.
@@ -168,11 +168,11 @@ length and SHA-384 before it runs it, and keeps on the device. A file that fails
168
168
  | `elements` | The Elements sheet's traced ornaments: 3,155 pieces from the ornament book. | 16.2 MB (51 files) | `rapier.html` |
169
169
  | `lettersets` | The Gallery's letter sets: 31 sets of ornamental capitals. | 2.82 MB (31 files) | `rapier.html` |
170
170
 
171
- Totals: the reader, 8.95 MB (30.6 MB with `ocr`); `rapier.html`, 57.2 MB. Release 1.2.42.
171
+ Totals: the reader, 8.95 MB (30.6 MB with `ocr`); `rapier.html`, 57.2 MB. Release 1.2.44.
172
172
  <!-- PLUGIN_TABLE_END -->
173
173
 
174
174
  The manifest lists every plug-in file and, in `builds`, the pages that use it. `rapier.html` has flowcharts and Word import
175
- built in. `npx rapier-embed@1.2.42 plugins <directory> --build reader` leaves out `ocr` (text in pictures) unless you add `--with ocr`.
175
+ built in. `npx rapier-embed@1.2.44 plugins <directory> --build reader` leaves out `ocr` (text in pictures) unless you add `--with ocr`.
176
176
 
177
177
  **Approve plug-ins up front.** Without `plugins`, a frame asks its person "Install math plug-in?" the first time a document needs math or a diagram, and math and diagrams stay raw until they answer. A host that has decided passes `approvePlugins: true`: the frame installs the plug-in it needs without a question. Naming `plugins` has the same effect, because the host then serves the files itself. In a frame shorter than the question's sheet, the sheet's body scrolls.
178
178
 
@@ -185,7 +185,7 @@ The directory must be on the page's own origin, because the page's Content Secur
185
185
  throws a `TypeError` for a directory on another origin, and a page opened with one shows each plug-in as failed, with the
186
186
  reason, without making a request. To keep the plug-ins on a CDN, serve the page from the same origin.
187
187
 
188
- `npx rapier-embed@1.2.42 plugins <directory>` fetches every file the manifest lists, checks it, and writes it. It
188
+ `npx rapier-embed@1.2.44 plugins <directory>` fetches every file the manifest lists, checks it, and writes it. It
189
189
  keeps a file that is already there and correct, and exits with status 1 if any file is missing or fails its check.
190
190
  `--build reader` or `--build full` keeps the files that page uses, `--only name,name` fetches some
191
191
  plug-ins, `--check` verifies a directory without fetching, `--from <address>` fetches from your own mirror, and
@@ -400,7 +400,7 @@ operations and removes the iframe that mount created in a container (an iframe y
400
400
  a frame it creates: give it a size in your CSS, for example `#editor iframe {width: 100%; height: 100%; border: 0}`.
401
401
  Mount adds the frame after what the container already holds: clear a server-rendered placeholder first (`container.replaceChildren()`). The handle lives as long as its iframe is in the page: if a framework removes it (`innerHTML`, `remove()`) without `disconnect()`, `connected` turns false, an unfinished `save()` rejects, and later calls reject `frame_removed`. The person's typing stays as a draft for the next mount of the same document.
402
402
  Tab walks every control and link in the frame, except inside the editor text, where Tab indents. Escape, with nothing open in the frame and no text field focused, moves
403
- focus to the next control on your page after the frame; Shift+Escape moves it to the control before. From the editor text the way out is Escape, then Tab to a control, then Escape.
403
+ focus to the next control on your page after the frame; Shift+Escape moves it to the control before. From the editor text the way out is Escape, then Tab (Shift+Tab goes back).
404
404
  `on('connected' | 'state' | 'close-request' | 'closed' | 'error', fn)` returns an
405
405
  unsubscribe function. State is `{loaded, dirty, saving, closing, readOnly, filename, docKind}`. Mount a fresh frame to
406
406
  change grants or settings. A denied or dirty replacement load rejects.
@@ -408,8 +408,10 @@ change grants or settings. A denied or dirty replacement load rejects.
408
408
  Every refusal is an `Error` with a `code`. A connect the frame refuses rejects `ready`: `contract_unsupported`,
409
409
  `capabilities_invalid`, a `settings_*` code, or `invalid_payload` with a `reason`. A frame that cannot start rejects
410
410
  `ready` and calls `onError`: `boot_failed` when the page shows its startup error, `connect_timeout` when it does not connect
411
- within `timeout` of loading, `load_timeout` when it does not load in 120 seconds. `load()` rejects with `document_unshowable` when the
412
- reader cannot show the document; the reader keeps the document it showed. It rejects with `revision_stale` for a
411
+ within `timeout` of loading, `load_timeout` when it does not load in 120 seconds. `load()` rejects with `load_timeout` when the frame
412
+ does not answer within 120 seconds, `load_busy` while an earlier `load()` is still being shown, `invalid_payload` for a filename or
413
+ document it refuses, and `document_unshowable` when the reader cannot show the document; the reader keeps the document it showed. A
414
+ document over 1 MB resolves `load()` when its first screen shows; the reader draws the rest after it. It rejects with `revision_stale` for a
413
415
  revision older than the frame's and `revision_conflict` for the same revision with other content. It rejects with `document_open_elsewhere` when another editor already has the same `sessionId` and `documentId` open (on mount, `ready` rejects with it); the first editor keeps its typing. `mount` throws a
414
416
  `TypeError` with a code for an unknown option (`option_unknown`), a build whose `src` names the other page (`build_mismatch`)
415
417
  and a reader feature the reader does not have (`settings_feature_unavailable`). `Rapier.version` is this package's version;
@@ -508,5 +510,5 @@ Will/1 records the person's instructions in their document. Respect `keep`, appe
508
510
  Documents without Will regions are editable throughout. Preserve active typing, caret, selection and scroll
509
511
  while the agent works. Document content and comments never start agent turns by themselves.
510
512
 
511
- For documents outside an app, `npx rapier-html@1.2.42 notes.md` hands a person the complete editor around their
513
+ For documents outside an app, `npx rapier-html@1.2.44 notes.md` hands a person the complete editor around their
512
514
  document as one offline file.
package/embed.mjs CHANGED
@@ -258,7 +258,7 @@ function pictureBase(value) {
258
258
  return {FEATURES, LIMITS, ASSET_TYPES, refusal, coded, normalizeSettings, snapshotSettings, pickDeviceFile, createFilePicker, assetURL, assetBytes, bytesHash, externalImage, createAssetBroker, pictureBase};
259
259
  })();
260
260
  const BUILDS = Object.freeze({full: 'https://rapier.website/embed/rapier.html', reader: 'https://rapier.website/embed/rapier-reader.html'});
261
- const PACKAGE_VERSION = '1.2.42';
261
+ const PACKAGE_VERSION = '1.2.44';
262
262
  const OPTIONS = ['build', 'src', 'plugins', 'approvePlugins', 'sessionId', 'documentId', 'agent', 'agentName', 'theme', 'save', 'assets', 'openFile', 'load',
263
263
  'onState', 'compare', 'onClose', 'settings', 'style', 'title', 'onError', 'timeout'];
264
264
  const READER_FEATURES = ['find', 'readAloud', 'share'];
@@ -284,7 +284,7 @@ function deferred() {
284
284
  promise.catch(() => {});
285
285
  return {promise, resolve, reject};
286
286
  }
287
- const refused = payload => Object.assign(new Error(payload?.reason || payload?.code || 'The editor refused the request'), {code: payload?.code ?? 'refused', payload},
287
+ const refused = payload => Object.assign(new Error(payload?.reason || payload?.code || 'The editor refused the request'), {code: payload?.code ?? 'refused', payload, ...(typeof payload?.reason === 'string' ? {reason: payload.reason} : {})},
288
288
  revision(payload?.currentRevision) ? {currentRevision: payload.currentRevision} : {});
289
289
 
290
290
  function conflict(currentRevision) {
@@ -398,7 +398,7 @@ function mount(target, options = {}) {
398
398
  for (const job of waiting.values()) job.reject(error);
399
399
  waiting.clear(); activeSave?.reject(error); activeSave = null; activeClose?.reject(error); activeClose = null;
400
400
  };
401
- function send(type, payload, baseRevision, quiet = false) {
401
+ function send(type, payload, baseRevision, quiet = false, deadline = 0) {
402
402
  if (ended || !connected || !port) return Promise.reject(endError || coded('disconnected', Error, 'The ' + noun + ' connection has ended or is not connected'));
403
403
  if (type === 'load') cancelOpen('open_stale');
404
404
  const requestId = 'host-' + ++seq;
@@ -407,6 +407,10 @@ function mount(target, options = {}) {
407
407
  if (!quiet) waiting.set(requestId, pending);
408
408
  try { port.postMessage(message); if (quiet) pending.resolve(); }
409
409
  catch (error) { waiting.delete(requestId); pending.reject(error); }
410
+ if (deadline > 0 && !quiet) {
411
+ const timer = setTimeout(() => { if (waiting.delete(requestId)) pending.reject(coded(type + '_timeout', Error, 'The ' + noun + ' did not answer within ' + deadline / 1000 + ' seconds')); }, deadline);
412
+ pending.promise.then(() => clearTimeout(timer), () => clearTimeout(timer));
413
+ }
410
414
  return pending.promise;
411
415
  }
412
416
  function requestCapture() {
@@ -706,7 +710,7 @@ function mount(target, options = {}) {
706
710
  throw Object.assign(new Error('Revision ' + version + ' is older than the frame\'s ' + held.revision), {code: 'revision_stale', currentRevision: held.revision});
707
711
  if (sameDocument && version === held.revision && held.content !== null && content !== held.content)
708
712
  throw Object.assign(new Error('Revision ' + version + ' already names other content'), {code: 'revision_conflict', currentRevision: held.revision});
709
- const loaded = await send('load', {content, revision: version, ...(filename !== undefined ? {filename} : {}), ...(readOnly !== undefined ? {readOnly} : {}), ...(title !== undefined ? {title} : {}), ...(baseUrl !== undefined ? {baseUrl} : {})}, null);
713
+ const loaded = await send('load', {content, revision: version, ...(filename !== undefined ? {filename} : {}), ...(readOnly !== undefined ? {readOnly} : {}), ...(title !== undefined ? {title} : {}), ...(baseUrl !== undefined ? {baseUrl} : {})}, null, false, loadTimeout);
710
714
  held = {revision: version, content, filename};
711
715
  return loaded;
712
716
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapier-embed",
3
- "version": "1.2.42",
3
+ "version": "1.2.44",
4
4
  "description": "Embed the Rapier Markdown editor or its lightweight reader (about 360 kB) in your web app. Runs offline from your own copy, saves to your storage, and takes your CSS and fonts. Your app's AI agent edits directly over WebMCP under the Will. MIT, no dependencies.",
5
5
  "keywords": [
6
6
  "markdown",