File Sync

File Sync is part of Agent πŸ“‚ Shareβ„’. While a folder is shared, it turns every exported file of your page, that is every element with an o: attribute, and every worker script into a separate file in the work folder, and keeps those files and πŸ“„.html in sync both ways. An agent can change a stylesheet, a template or a worker by editing one small file instead of rewriting the whole page. File Sync is on by default.

How File Sync works

Edits on either side map to the exact source range in πŸ“„.html. A change to .css on disk therefore touches only the inner text of the <style> that it came from, and the padding around that text survives. The ranges come from the source map of IOSync.

DirectionFile Sync on (the default)File Sync off
Editor β†’ πŸ“„.htmlEvery editor change rewrites the whole file, coalesced per sync tick.Only at share start and on πŸ’Ύ Save: πŸ“„.html.
πŸ“„.html β†’ editorExternal changes are imported. Changes on both sides at once are a conflict.Live: a change to the file replaces the differing region of the editor as one undoable edit. There is no conflict check, even if the editor holds newer typing in that region.
Exports and βš™οΈ.jsTwo-way files.No sync. Files that already exist stay on disk but stop syncing.

Turning File Sync on and off

✨ Agent: File Sync: Toggle in the command palette switches File Sync. A toast says File Sync enabled or File Sync disabled, and the change applies at once to a running share:

  • Off stops the session and watches the folder without looking into subfolders.
  • On prepares a session, honouring Share ← Folder. That may show the Files differ dialog. Then it starts and watches the folder recursively. Cancel in the dialog unshares. Any other error leaves the share running without File Sync and shows a warning; the stored preference stays on.

The File Sync module is loaded when File Sync starts: at share start, or when you toggle it on. If the module cannot load at share start, the share fails; turn File Sync off to share without it.

Which elements become files

File Sync looks at every element with an o: attribute (see o: and Exporting files) and at every <script type=worker> (see <script type=worker>).

Example: exports and a worker

html
<style o:=.css>
  main { padding: 1rem }
</style>
<template o:=about.html>
  <h1>About</h1>
</template>
<img o:=dot.png src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==">
<main>Hi</main>
<script type=worker edge>
  export default { fetch: () => new Response('hi') }
</script>

With the route new, the work folder <root>/new/ gets these managed files, besides the agent files. A ⏎ marks the single final newline.

FileHolds
πŸ“„.htmlThe whole source, without a final newline.
.css main { padding: 1rem }⏎. A hidden dotfile on macOS and Linux.
about.html <h1>About</h1>⏎
dot.pngThe 70 decoded bytes of the PNG.
βš™οΈ.js export default { fetch: () => new Response('hi') }⏎

Editing .css on disk changes only the inner text of the <style>. Writing </style> into .css is refused, with textContent cannot be stored without changing the surrounding HTML. Writing </template> into about.html is refused in the same way, with innerHTML cannot be stored …. The src of the image must be quoted, because the base64 text contains =.

File names

Rules

A file's name is the value of o:, relative to the work folder. A / in it creates subfolders: o:=css/site.css becomes css/site.css. The editor's hints, however, flag names that contain /, and names without a ., as invalid. Prefer flat names with a dot, such as site.css.

File Sync refuses a name, and shows the warning <name>: unsafe or reserved Share path ((unnamed export): … for an empty name), when it:

  • is empty;
  • contains \, a control character, :, *, ?, ", <, > or |;
  • has an empty, . or .. path segment, so a leading /, a trailing / and // are refused;
  • has a reserved name as its first path segment: πŸ“„.html, πŸ“„.sel, πŸ“œ.json, πŸ“Έ.png, πŸ“Έ.mp4, io.eval.js, io.eval.json, o.eval.js, o.eval.json, AGENTS.md or CLAUDE.md;
  • is .agents or .claude, or lies inside them;
  • has any path segment that begins with πŸ“.

If one declared name is a path prefix of another, such as lib and lib/x.js, both are skipped with <name>: a declared file conflicts with this directory path. Each diagnostic appears once per session, as a warning toast.

Name checks are case-sensitive: agents.md and Claude.md are accepted, and a.js and A.js are separate entries. Windows and default macOS volumes ignore case, so on them such names can collide with AGENTS.md or CLAUDE.md, or with each other. Choose names that differ by more than case.

Duplicate names

When several elements declare the same name, every occurrence gets a number after the full name, in document order: <name>.1, <name>.2 and so on. No unsuffixed file is written. Two <style o:=.css> give .css.1 and .css.2, and two workers give βš™οΈ.js.1 and βš™οΈ.js.2. Numbers that literal declarations already use are skipped: a.js, a.js and a.js.1 become a.js.2, a.js.3 and a.js.1.

html
<style o:=.css>h1 { color: red }</style>
<style o:=.css>h2 { color: blue }</style>

File Sync writes .css.1 (h1 { color: red }⏎) and .css.2 (h2 { color: blue }⏎). The output and RTEdge.net, by contrast, serve one /.css that joins both texts with a newline.

What each file contains

The content of a file depends on the element and on its o:content attribute.

Elemento:contentThe file holds
<script type=worker>-Its exact inner source, as βš™οΈ.js.
<script>, <style>none, text or textContentThe exact authored inner source: no entity decoding, no re-serialization.
<template>none or innerHTMLThe exact authored inner source.
Any other elementnoneThe exact authored source of the element itself (outerHTML).
<img> with a data: srcnoneThe raw decoded bytes.
Any element except <script> and <style>textContentThe decoded text.
<textarea>, <input>valueThe value.
Any elementanother propertyThe property's value, if it is a scalar with a setter that persists in HTML. Otherwise the file is read-only.

Final newlines and framing

A text file is the element's content plus exactly one final newline. File Sync normalizes the content to LF and hides the whitespace that frames it:

  • A first line that holds only whitespace moves to a hidden prefix. A last line that holds only whitespace, after a newline, moves to a hidden suffix, together with the content's trailing newlines.
  • Only one leading blank line is hidden. Extra blank lines stay in the file.
  • When File Sync writes back, it puts the prefix and the suffix in place again, so the padding of the element and the indentation of its closing tag survive.
  • A content that is only a newline frame gives a file with a single newline. Writing an empty file restores the original whitespace.

For example, the content "\n a { color: red }\n " gives the file " a { color: red }\n", and a file that holds " a { color: blue }\n\n\n" becomes the content "\n a { color: blue }\n ".

Comparisons ignore final newlines. Disk content is decoded as UTF-8, CRLF and CR become LF, and all trailing \r, \n, U+0085, U+2028 and U+2029 are trimmed before File Sync compares. Newline-only differences never import and never conflict. Trailing spaces and tabs are significant.

  • πŸ“„.html is written without a final newline, but it is not rewritten for newline-only differences, so a final newline that an agent adds may stay on disk. Text exports, including βš™οΈ.js, always get exactly one final newline. Binary image files hold the raw bytes.
  • Each import into the editor is one undo step, and it leaves the input without final newlines. Typing and Undo are not trimmed.
  • You cannot change an element's trailing newlines from an export file, because the end-of-file newline is not part of the content. Edit πŸ“„.html for that.
  • A UTF-8 byte-order mark is not stripped, so a BOM written by another editor would enter the element as a U+FEFF character. Write UTF-8 without a BOM.

Binary images

An <img> that has an o: name, no o:content and a src that starts with data: syncs as the raw decoded bytes, for base64 and for percent-encoded data URLs. Writing the file re-encodes the image as ;base64, and keeps the original media type: writing a JPEG into an image typed image/png keeps image/png. The bytes must read back unchanged, or the write fails with Binary value cannot be stored in this element. The other errors are Expected binary file contents and Invalid data URL, which may add escape or byte.

A pasted image becomes an exported file like this. After you paste a screenshot into the editor, it holds <img src="data:image/webp;base64,…">. Add a name whose extension matches the chosen format:

html
<img o:=logo.webp src="data:image/webp;base64,UklGR…" alt=Logo>

The share then contains logo.webp as raw bytes. Replacing the file writes a new base64 data URL into src, with the original media type and the existing quote style. Keep src quoted: base64 often ends in = padding, and an unquoted value that contains = is quoted on write-back anyway. See Pasted files for how pasting works.

The o:content property

  • textContent on an element other than a script or style: the file holds the decoded text, and writing escapes & and <. On <template>, textContent is read-only (template.textContent has no persistent serialized content).
  • value on a <textarea>: the file is the current value. Writing escapes the text, and keeps or adds the leading newline that HTML drops after <textarea>.
  • value on an <input>: the file is the value. Writing edits the value attribute, and adds value="…" when it is missing. A quoted value keeps its quotes. An unquoted value stays unquoted when that is safe: non-empty, with no whitespace, quotes, backtick, =, < or >.
  • Any other property is writable only when it is a scalar (string, number or boolean) with a setter somewhere on its prototype chain, and when the new value persists in HTML. Otherwise the file is read-only, with <prop> is not a persistent scalar property or <prop> is read-only or has no persistent setter. Writes can fail with <prop> does not persist in HTML, <prop> cannot represent that value or, for a boolean, Expected true or false.
html
<textarea o:=notes.txt o:content=value>
Remember the milk
</textarea>
<input o:=status.txt o:content=value value=draft>
<p o:=quote.txt o:content=textContent>a &lt; b</p>

The files are notes.txt = Remember the milk⏎, status.txt = draft⏎ and quote.txt = a < b⏎. Writing final to status.txt turns the attribute into value=final. Writing x & y to quote.txt stores x &amp; y.

Element files and protected attributes

A file that holds an element's own source (outerHTML, the default for most elements) contains the exact authored source. The version that is served is a DOM serialization with the platform attributes (o:, io:, edge:, h:, worker:) removed, so file and served body can differ in formatting and content. When File Sync writes such a file back:

  • The file must contain exactly one element. Whitespace-only text around it is allowed, comments are not. Otherwise the error is outerHTML requires one element.
  • Protected attributes are forced back to their original values: added ones are removed, and changed or removed ones are restored. Only then is the element serialized by the DOM. Without such changes, the text you wrote is used verbatim.

Protected attributes are all attributes whose names start with o:, h:, worker: or io:. Worker scripts also protect type, edge and fetch. Other attributes, such as edge:redact, edge:remove, copy:redact, i: and class, are not protected.

Declarations are frozen while you edit files. After every import, the set of [o:] elements and workers, their o: values and their protected attributes must be unchanged. Otherwise the import fails with Content edits must preserve export and worker declarations. Writing a nested <span o:=x.txt> into an outerHTML export is refused for this reason. To add, remove or rename an export or a worker, edit πŸ“„.html.

Editing the files

Prefer focused edits in the exported files, or in βš™οΈ.js for worker code. Use πŸ“„.html for structural changes. Avoid editing the same content in both representations at the same time.

Messages

MessageMeaning
outerHTML requires one elementThe file must hold exactly one element.
Content edits must preserve export and worker declarationsThe edit would add, remove or rename an export or worker, or change a protected attribute.
Export edits overlap; edit one owning file at a timeTwo files that map to overlapping source were edited together, for example <section o:=card.html> around <style o:=card.css>, or the two files of an o: worker.
New exported files conflict with existing disk content: <names>. Reconcile their declarations in πŸ“„.html.A newly declared export already exists on disk with other content, and the clash was found late in a sync tick.
<prop> cannot be stored without changing the surrounding HTMLAfter the edit, the element would not read back exactly the value you wrote, for example a script file that contains </script>.
Element has no editable content, The opening tag has no editable attribute boundary, Invalid source editOther import errors.
<name>: unsafe or reserved Share path, <name>: a declared file conflicts with this directory path, <name>: no exact source rangeThe declaration gets no file; see File names.

Conflicts

At share start

With File Sync on, Share ← Folder off and the folder not emptied, a share compares the folder with the editor. These are conflicts:

  • πŸ“„.html, listed first, if it exists on disk and differs from the editor, ignoring final newlines;
  • every declared file that exists on disk and differs.

Files that are missing on disk are not conflicts: File Sync generates them. How an existing πŸ“„.html is treated depends on two settings:

File SyncShare ← FolderResult
onoffA differing disk πŸ“„.html, or differing exports, open the Files differ dialog.
ononThe disk πŸ“„.html and the declared exports win, without a dialog.
offonAn existing πŸ“„.html is loaded into the editor.
offoffπŸ“„.html is overwritten with the editor source.

With Shift (Empty) and Share ← Folder off, the folder is emptied first and File Sync skips the comparison, so no dialog appears. Share ← Folder lasts only until the page reloads.

The Files differ dialog

The dialog lists each conflicting file with a Compare button. Its footer has three buttons:

Write (primary)

Keeps the playground and overwrites the folder.

Read

Takes the folder: the disk πŸ“„.html, if it exists, and the disk exports.

Cancel (focused by default)

Esc and the close button do the same. Cancel during startup cancels the share. Cancel during a running share unshares.

If the editor or the disk changes while the dialog is open, the check runs again.

Compare opens a read-only diff in a popup at diff.rt.ht, with the disk text labelled Disk and the playground text labelled Playground. Binary files, and files that are not valid UTF-8, appear as hex lines (an 8-digit offset and 16 bytes). Nothing goes into a URL and nothing is written back. If the popup is blocked, the message is The comparison popup was blocked. Allow popups and try Compare again. If it has not loaded after 30 seconds, it is The comparison did not finish loading. Try Compare again.

During a running share

  • Editor-only changes are written to disk. Disk-only changes are imported.
  • A file that changed on disk whose element also changed in the editor since the last sync is a conflict, and so is a πŸ“„.html that changed on both sides. Both open Files differ. Read imports only the files that changed externally, and the disk πŸ“„.html if it changed. Write keeps the editor and overwrites those files.
  • If πŸ“„.html and export files change on disk together while the editor is unchanged, the disk πŸ“„.html is applied first and projected again. An export file whose element that change did not touch is then imported on top. An export file whose element it also changed becomes a conflict.
  • Two files that map to overlapping source and are edited at the same time fail with Export edits overlap; edit one owning file at a time. During a share they are conflicts. Under Share ← Folder they stop startup.
  • Adding an o: export in the editor whose file already exists on disk with different content opens Files differ.

Saving and loading πŸ“„.html

Two commands need an active share. Both work with File Sync on or off:

πŸ’Ύ Save: πŸ“„.html (Alt+B / βŒ₯+B, while the editor has focus)

With File Sync off, writes the editor's text to πŸ“„.html. With File Sync on, flushes File Sync, an immediate two-way reconciliation: it is not a blind overwrite, because pending disk edits are imported, or produce a conflict. Both then refresh πŸ“„.sel and request a new πŸ“Έ.png.

πŸ’Ύ Load: πŸ“„.html (Alt+L / βŒ₯+L, while the editor has focus)

With File Sync off, re-reads πŸ“„.html and applies it. With File Sync on, marks πŸ“„.html as changed and reconciles.

Note: Deploying does not wait for File Sync. edge.save.limit() posts the editor's current state, so a change in the folder that the playground has not imported yet may not be part of the deployment. Wait until the edit has appeared in the editor first.

Performance

File Sync does its work on the whole document: every tick parses and projects the entire source again, rewrites the whole πŸ“„.html for every editor change, and reads its own writes again when the folder observer reports them. Large single-document projects pay the most. Splitting large assets into their own o:= exports keeps an agent's edits small, but it does not shrink the πŸ“„.html mirror.

Measured on one Mac with Chrome 153, on a frozen app source of 2.77 MB (20 September 2026):

ScenarioWith File SyncWith πŸ“„.html only
Median time from save to the output's acknowledgement, with a forced reload (20 edits per case, measured with an earlier editor build)523 ms (small JS), 535 ms (CSS), 683 ms (exported HTML), 752 ms (worker), 966 ms (large JS)1018 to 1074 ms (whole-file read-back)
Median time for a small edit, without reload (5 edits per case)280.4 ms (CSS), 300.7 ms (an existing HTML attribute value)162.8 ms (CSS), 229.4 ms (an existing HTML attribute value)
Warm share setupabout 21.9 to 22.0 sabout 3.0 s
Burst of 8 saves, 10 ms apart: time to converge after the last save0.73 to 1.12 s2.42 to 2.90 s

With File Sync, an agent writes only the focused file for an edit (1.44 kB to 1.43 MB) instead of the whole 2.77 MB source. In the browser, however, File Sync still wrote the full πŸ“„.html mirror, about 2.77 MB, and read about 11.1 to 16.5 MB per edit, against about 5.55 MB for the whole-file read-back. These are instrumented payload bytes, not disk traffic. Editor profiles showed 27 to 68 percent less scripting and 22 to 61 percent less sampled allocation with File Sync, and larger heap growth after garbage collection (15.1 to 29.2 MB per batch against 0.35 to 7.26 MB).

The measurements make no claim of statistical significance and no universal ranking: File Sync was faster in some cases, slower in others, and much slower to start on this large source.

Availability

FeaturePlaygroundRTEdge.netNotes
File SyncYesNoPart of Agent Folder Share: Chrome or Edge on a desktop. On by default.
o: exports and workers themselvesYesYesFile Sync only mirrors them as files. See Exporting files.

See also