Untitled
Project Persistence
Section titled “Project Persistence”Projects use a repository with continuous saving and durable, branching history. A repository can live in a selected filesystem folder, browser storage (OPFS), or a folder accessed through the Native Helper. See Project Repository and Durable History for its format and transaction model.
Choose and open a project
Section titled “Choose and open a project”The Choose project dialog offers New project, Open existing, and remembered recent projects. New projects use a named repository directory. Cancelling a folder picker leaves the current project and entered name intact. Invalid names and storage failures remain visible in the dialog.
Browser folder pickers open a selected directory. Where those pickers are unavailable, the chooser lists projects stored on the device. Recent entries reuse their remembered location and may need folder permission again. The chooser and File > Open Recent name each entry’s location (Folder, Native, Browser local or Temporary, the IndexedDB fallback; browser entries remember their store), and a temporary copy is always listed after a permanent project of the same name.
An existing repository is opened from its project.msrepo.json descriptor.
Legacy project.json folders and .msproj packages are converted in place on
first open: the repository is added to the same folder through a separate,
read-only import path, and the old files stay unchanged. Opening old work does
not overwrite its source format.
An incomplete or unreadable source is reported instead of being replaced with
a new blank project.
Project opening prepares the new session before activation. Switching flushes the previous session first; failure leaves its state available. Project switches are outside content undo. Recent, startup, Android and Native Helper entrypoints use the same session handoff.
Storage backends
Section titled “Storage backends”| Backend | Location and access |
|---|---|
| File System Access (FSA) | A user-selected directory; browser permissions and directory handles |
| OPFS | The current browser profile and site origin; no system directory picker |
| Native Helper | A granted local directory; helper protocol and operating-system writer ownership |
All three use the same repository format. The browser’s IndexedDB databases cache handles and derived metadata; they are not the authoritative project history. An unavailable index can be reconstructed from committed records.
A deploy does not change stored project or media links: handles, recent
projects, the workspace id and the repository stay as they are. What lapses is
the browser’s permission. Chrome keeps File System Access grants for the
browsing session only (unless “Allow on every visit” was chosen): after a
browser restart, or once every tab of the site was closed, a stored folder
reports prompt again; a same-tab reload (including the version reload after a
deploy) keeps it. The editor then does not reopen the project silently and does
not treat it as lost. The Choose project dialog shows Resume “
Saved media locations (source folder and relative path) are part of the browser’s project workspace, together with layout and playhead. Every tab of a browser reuses one workspace, so closing the tab and reopening the project later keeps them. A workspace that has never stored a value (another browser, origin or machine) reads it from the most recently written workspace of the project instead of starting without media locations.
The relink dialog shows each known source folder with one of its saved paths (“contains …”), so the right folder is recognizable. A remembered folder that no longer holds its saved media, or a newly chosen folder without them, is not rebound to that source folder: the dialog names the folder that is needed and still searches the chosen one for matching files. If browser storage was cleared, select the existing project folder again. The filesystem directory can still exist even when its cached browser handle has been lost.
OPFS projects are subject to browser quota, eviction and clearing site data. Persistent-storage permission reduces eviction risk but does not prevent a user from deleting site data. OPFS projects do not become independent backups merely because the browser remembers their names. A browser without OPFS or writable file streams (for example an older iOS Safari) cannot open the scratch recovery; the editor logs that as a warning about the environment, not as a code error.
WebKit (every iOS/iPadOS browser) can refuse OPFS with an opaque UnknownError
while its storage process resumes, in Private Browsing, in in-app browsers or on
a nearly full device. Opening the project root and the scratch recovery retries
once; if OPFS stays unavailable, browser projects switch to an IndexedDB
fallback (ms-browser-project-storage) that serves the same directory/file
handle surface, so project creation, saving, scratch recovery and .msproj
export keep working. The choice is made on the first storage access and kept
for the tab (sessionStorage), and the storage worker adopts the editor’s
choice, so one project never spans both stores. While the fallback is active
the save status reads “Temporary storage”, explains that Private Browsing and
in-app browsers may delete the project when the window closes, and a warning
styled Temporary · Save permanently button sits next to it (only “Save” on
phone widths). Both open the This project is only temporary dialog, which
offers only the destinations this device supports, best first:
- Save to a folder (folder picker or Native Helper): the existing Save As copy of the repository with history, journals and workspace views; the editor switches to the folder. Verification reads the new folder’s own session (same project lineage, writable, history holds the last confirmed revision; one re-read on a failed read). When only saving the latest edits into the folder failed after the switch, it saves once more; edits that still wait are named in the result while the verified temporary copy is removed. A copy that opened but does not verify is reported as such (“now opens from the chosen folder … temporary copy was kept”), never as “nothing changed”. The removed temporary copy also leaves the recent projects.
- Keep in this browser (when OPFS opens and accepts a write in this tab
now): the repository is copied into OPFS as a new repository and opened
there and adopted by the project core, so thumbnails, waveforms and caches
follow the copy; the tab then uses OPFS, also across reloads. Locations carry an
explicit
store(opfs/indexeddb) so one tab can address both stores. - Download .msproj archive named after the project (full history); on iPhone/iPad it lands in the Files app. The open project stays temporary.
Moving between the stores never locks a project read-only: the writer registry
(ms-repository-identity-locations) treats opfs:<origin>:<path> and
indexeddb:<origin>:<path> of one repository as the same project changing
store, and a moved temporary project that gets a new folder name takes over the
writer registration from its verified source. Copies at any other location still
open read-only (Copy/Restore). Renaming (also inside Save As and Keep in this
browser) rewrites only the project root, so media carrying repository-internal
source-identity proofs no longer abort these moves.
Duplicates (Save As, Save permanently, imports) contain only project.msrepo.json
and .masterselects/ at the project root; archive-manifest.json is written only
into .msproj export staging. Repository directory lookups never hand a reader’s
pending “does this folder exist” lookup to a concurrent writer: on slow disks a
view-slot or artifact-manifest write into a folder created at that moment
failed with “A requested file or directory could not be found”, which made a
successful Save to a folder report failure.
A temporary copy is removed only after its permanent copy is open, writable and
contains the last confirmed revision; on any failure it stays and the dialog
says so. The scratch browser recovery is never deleted. The dialog also shows
whether the browser keeps its storage (navigator.storage.persisted()) and can
ask for it (persist(), Ask to keep it). When a tab itself uses OPFS while projects remain in the fallback
database (a tab still on the fallback only lists them, because Open Project
would not find them there), the button reads “Move N temporary projects” and moves them with a
byte-verified copy (SHA-256 per file) under their repository writer lock; a
project open in another tab is skipped. Only when IndexedDB refuses as
well do project dialogs and the save status explain the cause instead of the
raw browser text. iOS, iPadOS and Android never try the desktop Native Helper.
To test the fallback on a device whose OPFS works, start the dev server with
VITE_MS_FORCE_BROWSER_STORAGE=indexeddb; production builds ignore it.
iPhone and iPad: Home Screen first
Section titled “iPhone and iPad: Home Screen first”WebKit offers no folder picker, so on iPhone and iPad a real project folder is
impossible and browser storage is the only place a project lives. A Safari tab
(any iOS/iPadOS browser tab) is subject to Safari’s eviction of script-writable
storage after seven days of browsing without a visit; a web app opened from the
Home Screen is exempt and has its own, separate storage.
resolveAppleHomeScreenMode() (src/utils/mobileAppleWebKit.ts) tells the cases
apart: iOS/iPadOS WebKit (including iPadOS’s desktop-mode Mac user agent, via
maxTouchPoints) running as a Home Screen app (navigator.standalone or
display-mode: standalone/fullscreen), in a browser tab, or none
elsewhere. Desktop and Android behave as described above.
- In a Safari tab the dialog leads with numbered Home Screen steps, each
one bold line with a short hint: 1 Download this project (the step’s
button downloads the
.msproj, because the Home Screen app has its own, empty storage; the step turns into a check mark afterwards), 2 Tap Share, 3 Tap Add to Home Screen (under More if it is not listed, Open as Web App on), 4 Open the new icon, 5 Open Project → Import. Keep in this browser (which says that Safari can still delete it), Download a backup and Ask to keep it sit under the collapsed More options. - The same tab also marks browser-local projects (not only temporary
ones, including the browser recovery) with a Not safe yet · Keep safe
button in the place and style of the Temporary button. It opens the dialog as
This project can get lost (Home Screen steps; the archive as a backup
outside Safari under More options) and stays until the page runs from the Home Screen or
navigator.storage.persisted()returns true; it is not dismissible. While persistence is still being checked it stays hidden, so it does not flicker. - The dialog renders above the workspace bar and fits the visible viewport (dynamic viewport height minus the safe areas, e.g. iPad landscape at 1180x734): its content scrolls and the footer with Close/Done stays visible. A new result or error scrolls into view.
- In the Home Screen app there is no Home Screen step and no hint for
browser-local projects: browser storage is the permanent place. A temporary
project still gets Keep in this browser first and the
.msprojdownload as backup, and More options notes that the seven-day cleanup does not apply.
The dialog is deliberately plain (decision 2026-10-21, user: the first
iPad version was far too much text). It shows one large headline that names
the problem (This project is only temporary, This project can get lost,
Some projects are still temporary, and Your project is safe once nothing
is at risk any more), one “why” sentence, and one obvious action: the Home
Screen steps in a Safari tab, otherwise one big button for the best destination
(Save to a folder, Keep in this browser or Download project file).
All other destinations, Ask to keep it, leftover temporary projects and the
storage notes are one short line each under the collapsed, keyboard-operable
More options disclosure. Results are one bold sentence (green with a check
mark on success, amber where the project is still at risk). The body scrolls
and the Close/Done footer stays visible; the dialog is portaled to the page so
the phone/iPad workspace bar cannot cover it, Escape and the Tab trap still
work after a pointer activation blurred the control, and the first focus goes
to the recommended action rather than the name field (no iPad keyboard pops up).
Copy lives in src/components/common/toolbar/savePermanentlyCopy.ts.
The Native Helper checks granted paths and negotiates repository write support. A connected helper with ordinary file commands does not by itself prove that it can safely own a repository writer. Native protocol or ownership failures are reported rather than silently weakening the commit protocol.
Only one writer owns a repository. A second session can browse read-only. Read-only history navigation does not publish project changes or bypass the active writer’s ownership.
A read-only tab shows Take over editing next to the save status; its tooltip names when the writing tab started editing and whether it is in the background. The action asks the writing tab to save, reopen itself read-only (it then says “Editing moved to another tab”) and release the writer lock; this tab then reopens the project with write access. If no tab holds the lock (the writer was closed, or folder access was missing), the action reads Enable editing and reopens directly, requesting folder access first.
A read-only tab does not follow the writer’s edits live. The writing tab
announces every saved revision on the handover channel; a read-only tab that
shows an older revision (including a former writer after a handover) then says
“This project changed in another tab” and offers Reload, which reopens it
read-only at the latest saved revision. While a handover or reload runs, the
toolbar buttons stay focusable (aria-disabled); when the focused button
disappears after a keyboard takeover or reload, focus moves to the save status
instead of falling to the page body.
During a project open or reopen, media entries exist before their runtime files are restored. Composition previews log such clips at debug level as “not loaded yet” (not as “Media file not found”) and rebuild the composition once the file arrives; an entry that stays without a file outside a load is reported as offline or needing relink.
Installed app keeps folder access
Section titled “Installed app keeps folder access”MasterSelects is installable as an app in Chrome and Edge on Windows and Linux
(public/manifest.webmanifest, linked from index.html: start URL /editor,
scope /editor, standalone window, #0f0f0f theme and background from
--bg-primary). The scope covers only the editor, so links to the website and
the documentation keep opening in the browser instead of the app window. For the
same reason the installed app stays in the full editor on a phone instead of
moving to the phone editor at /m. Chrome keeps File System Access grants of an installed web app
across browser restarts, so projects opened in the app window do not ask for
their folders again; a browser tab still loses them when the session ends.
While the browser offers installation (beforeinstallprompt) and the editor is
not already running as an installed app, the Resume “AppInstall. Hiding the hint is
remembered on this device. An app installed earlier is not detected from a
browser tab, so the hint is simply absent there: open MasterSelects from its app
window. Grants belong to the window they were made in; folders allowed in a tab
must be allowed once more in the app.
There is no service worker; the manifest only makes the editor installable and
does not cache the app. The icons in public/icons/ (any and maskable,
192/512 px, plus a 180 px apple-touch-icon) are rendered from the 2048 px
“MS” logo by node scripts/generate-pwa-icons.mjs (needs ffmpeg). On iPad, a
home-screen web app has its own storage, separate from Safari’s, so projects
stored in Safari’s browser storage do not appear there; take them along as
.msproj (see iPhone and iPad: Home Screen first).
Continuous saving
Section titled “Continuous saving”Completed content actions become immutable revisions and enter the asynchronous storage queue. There is no manual-only or interval-save mode after repository cutover. Ctrl/Cmd+S requests a flush and waits for the captured content, history navigation and workspace state to be confirmed.
The visible edit happens before disk confirmation. Hashing, record publication and filesystem work run through the storage worker. Pending and failed writes remain distinguishable from confirmed saves. A failure does not clear pending work or display a successful save acknowledgement.
Small edits encode their affected aggregates. Clips and tracks are separately
versioned, and large data is split into bounded structural blocks. Unchanged
binary artifacts are reused. Saving a keyframe does not rebuild a complete
.msproj package or duplicate every mesh and audio result.
Workspace changes such as playhead, zoom, scroll, the loop toggle and dock layout use coalesced
view slots. They do not append content revisions on every playback tick. Each
view has two slots so an interrupted update can retain its last valid value.
A workspace that lacks a view (a new tab, browser or machine) takes it from the
workspace that wrote it most recently. Each slot stores its write time
(updatedAt), because the Native Helper backend reports no file times; slots
written before that field existed fall back to the file time, and equal times
pick the lowest workspace id, so every reader chooses the same source.
Pending recovery data is bounded. Storage pressure, lost permissions and I/O failures can stop further content edits before the pending budget is exceeded. Retry or protect the pending work before changing projects; a failed flush is not treated as permission to discard it.
Folder access paused while a project is open
Section titled “Folder access paused while a project is open”Chrome can also withdraw a session-only grant while the project is open, for example after the tab sat in the background. The editor treats that as a pause, not as a failure or a missing file:
- Detection. The storage worker reports a refused read or write
(
NotAllowedError/SecurityError) with the repository codeaccess(permissionstays reserved for repository policy refusals). Returning to the tab, window focus and a 30-second check also query the project folder and the media locations this session already opened, so the pause shows before a save fails. - One toolbar state. The toolbar shows “Folder access was paused by the browser” with Re-allow; the save status reads Access paused (not “Save failed”) and re-allows on click, as does Ctrl/Cmd+S.
- No data loss. Edits keep committing in the tab; their writes stay queued in order. Re-allow asks for the project folder first (Chrome’s restore prompt can bring back the other folders with it), then the media folders and files of this session, and republishes every queued write before the pause clears. If the browser refuses, the pause and the queue stay.
- No relink dialog. Media whose saved location only waits for the prompt, and whose access worked earlier in this session, stay “not opened yet” instead of offline; export explains the pause. After Re-allow they open from their saved locations. Media that still cannot be read then go to the Reconnect media dialog as before; locations never allowed in this session (e.g. after a browser restart) keep using the Resume and Reconnect flows above.
- Interrupted writes. A write cut off after its file was created leaves an empty file whose removal the browser also refused. Immutable records are never empty, so a later retry finishes that file instead of failing with a conflict.
Project files deleted while a project is open
Section titled “Project files deleted while a project is open”A project folder that is deleted or moved on disk (or a browser project whose storage was cleared) while it is open cannot be saved again; retrying cannot bring the files back. The editor keeps the work on screen and offers to save it elsewhere:
- Detection. The storage worker reports
NotFoundErrorwith the repository codemissing. The save status then reads Project files missing, its tooltip says what happened, and a click opens Save As instead of a retry. The phone status reads Files missing and points to Duplicate. - Save As keeps the current state. When the open project’s folder or
project.msrepo.jsonis gone, Save As (and the phone’s Duplicate) does not copy the vanished repository with its history. It creates the new project from the editor’s current state, edits after the loss included, and keeps the composition, playhead and panels that were open. The old session ends without the final save that could only fail. Before, Save As tried to copy the vanished project, failed, and left the tab without any project (“Recovery unavailable”). - Limits. The history before the loss is not carried over, and media files that were copied into the deleted folder are gone with it: such media show as offline in the new project after a reload and need Relink. Media linked from elsewhere on disk stay connected.
- If the rescue fails (for example the new location cannot be written), the old session stays mapped with the editor state untouched, so Save As can be tried again with another location.
- Covered by
tests/playwright/persistence/project-folder-loss.spec.ts(case 2: status, Save As, reload and reopen).
Save status and lifecycle
Section titled “Save status and lifecycle”Save status belongs to the active repository session. Its operation receipt tracks content and navigation independently of the currently selected revision, so undo followed by Save confirms the new cursor position as well as the required records.
Closing, switching and development reload handling use the same persistence boundary. Asynchronous jobs retain their original session and source identity. A result that finishes after a switch cannot modify the destination project. Scratch recovery follows the same domain serialization rules as named projects.
A repository with no write ownership remains explicitly read-only. Local navigation in that session must not be reported as a new repository commit.
What gets saved
Section titled “What gets saved”| Category | Examples | Restored by content undo? |
|---|---|---|
| Project content | Composition settings, clips, tracks, keyframes, effects, masks, markers, authored In/Out ranges and per-composition master audio | Yes |
| Other authored domains | Documents, graphs, MIDI mappings, tracking results, storyboard decisions, generated items, export settings and saved job definitions | Yes |
| Workspace | Active tabs, selection, playhead, zoom, scroll, loop toggle, panel layout, inspector state and display preferences | Separately preserved |
| External journals | Conversations, provider requests, job events, execution evidence and Agent Timeline records | No |
| Artifacts | Original media, document attachments, audio bakes, transcripts and analysis results | Through immutable references |
| Runtime | File handles, object URLs, DOM/media elements, decoders, GPU resources and running workers | Recreated, never serialized into content |
Projects saved before batch export carry export settings without a batch section; opening or resuming them fills in an empty batch instead of failing.
Undo does not restart an export or provider request and does not erase chat. Selection is cleaned up when its referenced object no longer exists. A free-standing (detached) effect group keeps that state through save, reload, Undo and Redo instead of re-attaching as a bypassed effect. Each composition remains the owner of its timeline and master audio state.
Temporary camera NO KF live offsets remain preview-session state. Provider
credentials and authentication state are excluded from portable project
records and archives.
Media and relinking
Section titled “Media and relinking”Media Source → Link proxy file… links another file to a video media item, for example an H.264 copy of a codec the browser cannot decode in real time. The original stays the media’s identity; the linked file plays, and Media Source switches back. Exports always render the originals: for the export run, media playing a linked proxy switch to their original and back afterwards (the saved choice is unchanged; an unreachable original keeps the proxy and logs a warning).
Original media can remain linked to its selected source location. A source-root identifier and relative path describe the binding; browser directory handles stay in local handle storage. The relink dialog and Preferences > Import > Connect source folder can restore access to a selected source tree.
Chromium uses directory handles when available. Folder upload remains a session-level fallback on browsers without those handles. Project-local or imported retained bytes can be read from the repository before requesting an external source again.
Linking only survives a reload where the browser can keep the file’s handle in
IndexedDB. The editor measures this with a real handle (an aborted probe write,
never the user agent): WebKit (Safari on iPhone, iPad and Mac) refuses handles
with DataCloneError, so there - and for every file without a reopenable
location, such as <input> picks - imports and relinks are copied into the
project’s Raw/ folder by default. This works in named projects and in the
browser recovery project, in OPFS and in the IndexedDB fallback, and the copies
move along with Save permanently. A notice shows the copied size and the
browser storage left, reports copies that fail (the media stay usable until the
next reload) and offers Link future imports; Preferences > Import > Copy
media that cannot stay linked holds that per-device choice
(ms.media.linkWhenNotDurable). Chromium keeps linking as before.
The old size-plus-file-prefix fingerprint is a lookup hint. Historical source identity requires a full content hash: equal filenames, sizes or the first 2 MiB do not prove that the original bytes are unchanged. Replacing a source creates a new source version; it must not silently reinterpret old revisions.
Artifact binary data and its metadata have separate identities. New metadata produces a new immutable manifest even when the binary bytes are identical. Retained branches, named versions and journals protect the versions they use. Cleanup cannot delete another project’s data or confirmed historical requirements just because the current timeline no longer displays them.
If media is unavailable, use the Media panel’s relink action to restore access or select a replacement. Historical originals that were never retained cannot be reconstructed from metadata alone.
History and named versions
Section titled “History and named versions”Undo and redo navigate existing revisions. Editing an older revision preserves its former future as another branch. Named versions are durable references to chosen revisions and are separate from Save.
The History panel loads bounded pages and renders only its visible rows. Keyboard navigation uses logical positions so it can reach revisions outside the currently rendered page. In-memory cache limits do not truncate on-disk history.
A checkout prepares domains and runtime resources asynchronously, then publishes one shared content generation for rendering and audio. A failed activation restores the previous state instead of exposing a partly restored project.
Archives, duplicates and backups
Section titled “Archives, duplicates and backups”The File menu’s transport actions distinguish current-state, named-version,
selected-branch and full-history archives. .msproj is an explicit archive;
it is not the active continuous-save container.
A self-contained archive includes the dependencies required by its selected scope, including verified original media. Unavailable historical bytes are reported rather than replaced with a same-named current file. Import does not require the source repository’s IndexedDB index.
The current .msproj archive writer uses classic ZIP. It rejects archives at
the 4 GiB size boundary or 65,535-entry boundary instead of emitting an invalid
file; ZIP64 is not supported. Directory-based repository storage and backups do
not use this ZIP container limit. The exported file is named after the open
project (<Project name>.msproj); only an unnamed project falls back to
MasterSelects Project.msproj. The archive manifest records the project name
(projectName), so an import can name the project before restoring it.
Restoring an archive streams it entry by entry: entries are stored strictly in
archive order, so a small entry never waits behind a later entry that still
needs input (this ordering once hung the import at “Reading selected project
10%”). If no entry makes progress for two minutes, the restore fails with an
“Archive extraction stalled” error naming the entry instead of hanging silently.
Work on an entry that does not read archive bytes (digesting an already staged
copy of a large file when a restore resumes) reports progress itself, so it
never trips the watchdog. A repository archive is recognised at its first
repository entry instead of being buffered in memory as an old package first.
The restore stages into repository-archive-staging/<import>; that copy is
removed once the project holds it, after a failed in-place import, and after a
cancel. Only a failed import into a separate folder keeps it, to resume from.
A restored archive is an ordinary repository project afterwards. The import
records .masterselects/imports/<import>/target.json before copying and
complete.json (kind repository-archive, the archive file, the restored
commit) after it, so every later open, reload or browser restart opens the
repository directly instead of treating the archive beside it as an unconverted
package. A restore that stopped before its commit is refused with a clear
message (import the archive again into a new project) instead of opening half
built. Originals embedded in a self-contained archive live in the repository’s
.masterselects/artifacts/; they are opened into runtime files in the
background right after the project activates (two at a time, each verified
against its content hash), without a clip having to use them first. Media
outside the repository still open on demand.
Duplicates receive a new repository identity and retain the content lineage. Incremental backups preserve the source’s immutable descriptor and commit chain; opening a completed backup imports it into a separate editable repository with a new identity. A backup is complete only after its required dependencies are present and confirmed.
Legacy imports preserve raw historical evidence, including multiple roots and ambiguous snapshots. Missing composition context is not invented to make an unreconstructable snapshot appear navigable.
Workspace and recent projects
Section titled “Workspace and recent projects”Dock layout, active composition/document tabs, media-panel presentation, playhead and timeline view state are restored independently of content history. Global named/default layouts remain application preferences. The Output Manager’s browser window identity and position remain browser/runtime state.
Recent entries remember the repository location. Access or read failures remain visible with a recovery action; reconnect the original location rather than create a replacement project. Clearing the recent list does not delete project files. Clearing browser site data is a different operation and can remove OPFS projects as well as cached handles.
Troubleshooting
Section titled “Troubleshooting”- Folder permission is missing: reconnect the existing project directory or its external source folder. To stop the prompt after every browser restart, install MasterSelects as an app (see Installed app keeps folder access) or choose “Allow on every visit” in the browser prompt.
- “Folder access was paused by the browser”: click Re-allow and accept the browser prompt; choose “Allow on every visit” or install the app to avoid it. Queued changes are saved afterwards.
- Project is read-only: use Take over editing in the toolbar. If the writing tab does not answer within a few seconds while it still holds the lock (a frozen or suspended tab), the toolbar says so: switch to that tab or close it and try again. Deleting media in a read-only tab is refused up front with the read-only notice instead of failing midway.
- No project folder picker: browsers without the File System Access API
(Firefox, Safari) open project folders only through a running Native Helper.
Without it, File > Open Project lists the projects stored in this browser and
offers Import .msproj archive: the archive is copied into a new
browser-storage project folder and opened there, which restores a repository
archive or converts an older
.msprojpackage in place. The folder is named after the project recorded in the archive manifest (Name,Name (2), …); archives written before the name was recorded use the file name. Once a repository archive is restored, its copy is removed from the folder (an older package stays, because its converted project still reads it); reopening the project later never imports again. Opening a stored project that no longer exists reports that instead of creating an empty folder, and a folder without any project is reported as such instead of being sent to the old-project converter. The archive is streamed from disk into browser storage; the log lineProject archive copied into browser storagereports setup and copy times. An incomplete copy or an import that does not open is removed again and reported. While the import runs, Cancel import (or Escape, or closing the dialog) aborts the copy, the archive restore or the conversion; the partial project folder and its staging are removed and the open project stays as it was, including its save status. The start-up project chooser offers the same import below the stored projects, where Back turns into Cancel import while it runs. A failed or cancelled open of another project never marks the open project as “Save failed”. The stored-project list shows only folders with a project (project.msrepo.json,project.jsonor a not yet imported.msproj), not model caches or repository staging folders. - Writes are pending or failed: inspect the storage error, restore access or available space, and retry. A pending edit is not yet a confirmed disk save.
- The history index is unavailable: records remain authoritative; allow reconstruction or fallback queries rather than deleting project data.
- Media is missing: restore source access or select a replacement. A backup without historical original bytes cannot recover those bytes.
- Browser storage is unavailable: protect recoverable work before resetting the browser. Clearing site data can delete the project itself.
Implementation and verification
Section titled “Implementation and verification”The public repository format and module boundaries are documented in Project Repository and Durable History. Existing media, audio, document and timeline codecs are shared with the editor’s runtime restore paths. Provider orchestration remains in the separate private kernel.
Repository changes are verified with explicitly named regression tests, the final application build, relevant Native Helper checks and live editor flows. The full Vitest suite is not part of the shared-workspace verification command.
Related features: Media Panel, Timeline, Audio, UI Panels.
