NAVIGATION
ELOWEN DOCUMENTATION

Last updated: 10 October 2026

Browse documentation · editor implementation
Developer reference

editor implementation

Browser draft lifecycle

web-src/editor/useEditorDrafts.ts owns the selected file, open tabs, draft text, dirty paths, content versions, per-path save queues and discard confirmation for ProjectEditor. Callers key the editor by Project identity. A root change confirms unsaved work, then clears tabs, drafts, expanded folders and the selection and returns the view to Edit. The chosen root (remembered per device) and the embedded height remain.

Each save captures Project, root, path and text and publishes through the existing root-aware fileData.ts query keys and host QueryClient. Writes to one path serialize even without a content version; different paths can proceed together. A queued write uses the previous response's version. Save completion retires only a draft that still matches the sent text, so typing during a pending save remains visible and dirty. Every operation reports its own result. Closing a dirty tab asks for confirmation; rename remaps tabs and drafts, while deletion forgets the affected path and descendants. Fullscreen and resizing preserve the mounted draft state.

web-src/editor/menu.ts builds tree context menus and File, View and Settings descriptors. Desktop passes them to the host ContextMenu; phones flatten those same descriptors for ActionMenu and run the selected action after the menu closes. Actions receive the current root, selection, preferences and existing callbacks. Empty selection keeps selection-dependent actions disabled; host Projects omit the managed root submenu, and System roots omit Git views.

Managed request ownership

The managed server dispatcher in src/managed.ts keeps the existing mount and method checks, linked-account and provider admission, unsupported-operation HTTP 501 and final error response. The API's authorized Project and root target enters src/managed/context.ts once per request. That context derives the guest root and binds confined paths, live provider resolution, chunk reads, follow-stat, request input, upload baseline versions and refusal mapping. File and command operations resolve the live provider again rather than retaining the admission provider for execution.

Named handlers in src/managed/files.ts own listings, text saves, entry mutations, streamed downloads and Office previews, including the shared two-conversion limit. src/managed/upload.ts owns HTTP upload chunks through the existing upload sessions and core guest upload handle. src/managed/git.ts owns the read-only history routes and rejects them outside the Project root. Every handler receives the same authorized context; these modules add no backend registration or public core API. The dispatcher awaits handlers inside its error boundary, so validation, upload and access refusals retain their existing status and wording while unknown failures remain sanitized HTTP 503.

Managed command and browser contracts

Editor and OneDrive execute one-shot project commands with sandbox.runCommand(input, actor, options). The host authorizes the prepared target and owns byte capture, heartbeat, timeout, cancellation and lease release; the actor preserves the caller's account and allowed roots. Editor uses leaseKind: 'editor', a 30-second timeout and an 8 MiB combined output bound, returns sanitized stdout, and rejects nonzero results. OneDrive uses leaseKind: 'files', its Git timeout of 120 seconds by default, and a 64 MiB combined bound. It retains real Git exit codes and maps interrupted execution to code: null with sanitized partial output; cleanup and authorization errors remain exceptions. Missing Sandbox support makes the operation unavailable. Long-lived services retain raw prepareExecution sessions. No consumer timer or second capture loop is required.

The binary preview and status bar read the viewer locale from the existing host translation hook and pass it to the published host byte formatter; switching languages changes numeric captions without a local formatter.

Editor JSON writes call runtime.api(path, { method, json: payload }) through ElowenUiRuntime['api']; the host owns serialization, headers, authentication and request lifecycle. File saves omit an undefined version and preserve an explicit null version. Omitting json retains raw request behavior, including downloads and multipart uploads. Consumers still validate their domain responses.

File writes

Managed uploads use core's beginGuestUpload from elowen/dist/plugins/guestUpload.js; text saves above one guest chunk use uploadGuestFile(files, { path, size, expectedVersion, subject }, [bytes]). Both receive the editor's existing authorized projectFiles binding. Core owns guest chunk boundaries, acknowledgements, resolved destinations and atomic version-checked commits, including missing parent directories. Small text saves keep the single write contract.

Host browser uploads use openHostUpload({ path: part, fd }, { size, subject }) from elowen/dist/plugins/hostUpload.js. The editor applies the authorized Project guard or its own administrator-only System guard, creates missing parents and claims <destination>.elowen-upload with wx. Core closes the claimed descriptor, writes positionally, checks inode and stored size on each append, and verifies the declared total before publication. Create-only publication refuses an existing destination; explicit overwrite uses replacement. The editor closes the read descriptor returned by publish. No descriptor stays open between requests, and staging names remain hidden from listings.

The browser still sends sequential 2 MiB HTTP requests with offset, size, final and overwrite. It advances by bytes sent and does not read written. A guest entry keeps only the unfinished guest-chunk tail between requests, below one guest chunk, and includes it in written and the next expected offset. Both transports share one map with at most 64 entries keyed by acting account, Project, selected root and destination. Size and overwrite mode cannot change during a session. Host parent aliases and Project/System views reserve the same physical destination; a different live owner receives HTTP 409. Guest paths are scoped to their Project environment, and a provider change drops its old guest entries without touching host uploads.

Admission evicts only the least-recent idle entry, awaiting its own core abort() while keeping its destination busy; concurrent admission rechecks capacity afterwards. If every entry is busy, admission returns HTTP 409. Cleanup failure is a server error and retains the reservation. A broken host handle is dropped without aborting, so a replaced staging file is not deleted. Its next offset-zero request follows the same explicit stale recovery: with no live owner, lstat permits removal only of a regular file at the editor's staging name before a fresh wx claim; symlinks and directories are refused. A restart loses sessions, so a leftover part cannot resume at a nonzero offset.

Editor validation, conflicts, access refusals and the upload-refusal allowlist still determine HTTP responses. Raw core and filesystem diagnostics never reach the browser. Without the provider, managed requests remain unavailable; host transfers still use their separately authorized filesystem guard. Core 0.29.42 supplies the host upload seam and integrity check.

File downloads

The existing authorized /projects/:id/raw route returns a streamed PluginHttpResponse.body; download=1 adds the attachment header. Project selection and administrator-only host System authorization are unchanged. Binary files, and image, PDF, Office and media files above their preview limits, show an enabled Download button instead of reading their content into the browser. An oversized text file shows a notice and no download button.

Host Project and System downloads use their existing path guard, re-authorize the resolved leaf and open it with O_NOFOLLOW, and take size metadata from that same descriptor. A Node read stream owns the descriptor and reads in 64 KiB blocks; EOF, errors and core-managed client cancellation close it. Invalid ranges close the descriptor before returning HTTP 416. A pathname replacement after opening cannot switch the download to another file.

Managed Project and System downloads use a pull-based web byte stream over the existing authorized guest read operation. No content is read before the consumer pulls. Each pull reads at most 256 KiB and validates the initial file version and expected byte count. A failed, changed or incomplete read errors the stream with a sanitized message; it cannot change an HTTP status whose headers were already sent. Cancellation stops subsequent reads and lets at most one bounded in-flight read settle without enqueueing or exposing an unhandled rejection. Empty files need no guest read.

Both paths preserve byte ranges, HTTP 206 and content-range, with the existing 8 MiB maximum range response. The response-body streaming contract first shipped in core 0.28.34, and its cancellation-safe adaptation shipped in 0.29.14. The plugin still requires 0.29.42 because its existing host uploads depend on that newer core seam.

The plugin has no configuration fields. Disabling it removes the editor and its file API; Project registration and task Git history remain available.

Next: GitHub