File editing system with a WYSIWYG markdown editor, visual workflow editor, HTML preview, and binary file viewer.
- File-Type-Specific Editors: Automatically selects the best editor based on file extension
- Markdown 3-Mode Editing: Switch between Preview / WYSIWYG / Raw
- Visual Workflow Editing: Display and edit YAML as a Mermaid flowchart
- Auto-Save: Debounced (1 second) auto-save to IndexedDB cache
- Edit History: Maintain local change history with restore to any version
- Diff View: Compare with any file and view unified diffs
- Temp File Sharing: Generate shareable temporary edit URLs via Temp Upload
- Encrypted File Support: Decrypt, edit, and re-encrypt
.encryptedfiles - Plugin Extensions: Add custom editor views via plugins
- Image Insertion: Insert images from Drive file picker into markdown
The file tree toolbar provides a New File button that opens a creation dialog. In addition to specifying a file name and extension, you can optionally include date/time and geolocation metadata in the initial file content.
| Option | Description |
|---|---|
| Add date/time | Inserts the current date and time (YYYY-MM-DD HH:MM:SS) at the top of the file |
| Add location | Requests browser geolocation and inserts latitude/longitude coordinates |
- Checkbox preferences are saved to
localStorageand restored on the next file creation - For
.mdfiles, labels use bold formatting (**Date:**,**Location:**); other file types use plain text - Geolocation uses the Web Geolocation API with a 10-second timeout; silently skipped on failure
- Default file name follows the format
YYYY/MM/DD_HH_MM_SS
When a memo contains latitude and longitude, you can ask the AI in the chat panel to identify the place name. The AI reads the file content via the read_drive_file function call and resolves the coordinates to a human-readable location.
MainViewer switches the display component based on file extension and MIME type.
| Extension | Editor | Modes |
|---|---|---|
.md |
MarkdownFileEditor | Preview / WYSIWYG / Raw |
.yaml, .yml |
WorkflowEditor | Visual / YAML |
.html, .htm |
HtmlFileEditor | Preview / Raw |
Other (.txt, .js, .json, etc.) |
TextFileEditor | Raw only |
Binary files are detected by file extension (case-insensitive), with MIME type as a fallback.
| Type | Extensions | Display |
|---|---|---|
| Image | .png, .jpg, .jpeg, .gif, .webp, .svg, .bmp, .ico |
<img> image display |
| Video | .mp4, .webm, .ogg, .mov, .avi, .mkv |
<video> player |
| Audio | .mp3, .wav, .flac, .aac, .m4a, .opus |
<audio> player |
.pdf |
iframe preview |
Binary files show Temp Download / Temp Upload buttons for local editing and download.
Files with the .encrypted extension are handled by EncryptedFileViewer. Additionally, files whose content starts with encrypted YAML frontmatter (encrypted: true) are also treated as encrypted, regardless of extension. After entering a password for decryption, the content can be edited as plain text. The password and derived private key are cached for the session (private key takes priority), enabling auto-decryption.
Decrypted binary content (images, videos, audio, PDF) is displayed in a media viewer instead of a text editor.
Auto-save is supported for encrypted text files with a 5-second debounce (re-encrypt then save to cache). Encrypted binary files do not auto-save.
Diff is not available for encrypted files.
A Permanent Decrypt button is available after decryption. This sends the plaintext to the server, removes the .encrypted extension from the file name, and stores the file unencrypted on Drive. A confirmation dialog is shown before proceeding.
Plugins can register extensions and components in mainViews to add custom editors for new file types. Plugin views are checked before the built-in editors, so a plugin can override the default editor for any extension (e.g., .md, .yaml).
Markdown files (.md) have three editing modes.
Read-only rendering via GfmMarkdownPreview.
- GitHub Flavored Markdown (tables, checklists, strikethrough)
- Code block syntax highlighting (
rehype-highlight) - Inline Mermaid diagram rendering
Rich text editing powered by the wysimark-lite library.
- Rich text operations: bold, italic, lists, code blocks, etc.
- Editing while preserving markdown syntax
- Image insertion (via Drive file picker)
- Lazy-loaded (dynamic import via
useEffect)
Direct markdown source editing in a plain <textarea> with monospace font.
Workflow files (.yaml, .yml) have two editing modes.
- Parses YAML via
engine/parser.tsand converts to a Mermaid flowchart viaworkflow-to-mermaid.ts - Displayed as an interactive SVG diagram
- Click a node to open the properties panel (right sidebar)
- Direct YAML editing in a raw textarea
- Auto-save with 1-second debounce
All editors use a common auto-save pattern.
- Save to IndexedDB cache after a 1-second debounce from content change. Encrypted files use a 5-second debounce due to re-encryption overhead.
- Flush unsaved content when the editor loses focus (blur)
- Emit a
file-modifiedevent on save to update the file tree badge - Record changes in the
editHistorystore (for Sync)
Changes are reflected on Drive via a manual Push operation.
A mode switcher and action buttons are displayed at the top of the editor.
Mode toggle buttons depending on file type (e.g., Preview / WYSIWYG / Raw for markdown).
| Button | Description |
|---|---|
| Edit History | Open the edit history modal to view and restore past versions |
| Diff | Select a file to compare and display the difference in unified format |
| Temp Upload | Generate a temporary edit URL and copy it to clipboard |
| Temp Download | Fetch temporary changes and merge them into the editor |
Manages local edit history.
- Records a snapshot boundary when a file is opened
- Open the history modal via the Edit History button
- Select any version to restore
- Emits a
file-restoredevent on restore to update the editor
Related service: app/services/edit-history-local.ts
Compare with any file to view differences.
- Click the Diff button
- Select the target file with
QuickOpenDialog - Top: editable textarea (current file), Bottom: unified diff display
- Uses
createTwoFilesPatch()from thediffpackage
Enables temporary editing from external tools.
- File content is stored in the Google Drive
__TEMP__folder (no local filesystem storage) - Edit URLs contain an encrypted token (AES-256-GCM,
SESSION_SECRET-derived key) embedding{ accessToken, rootFolderId, fileId, fileName, createdAt } - A fresh access token is issued at URL generation time, giving the URL a 1-hour validity window for both GET and PUT
- Multi-server compatible: no server-local state is required
- Click the Temp Upload button in the editor
- A confirm dialog asks whether to generate a shareable URL
- If confirmed, the
generateEditUrlaction uploads and generates the URL in one request; the server refreshes the access token (full 1 hour), encrypts credentials into the URL, and copies it to clipboard - If declined, the
saveaction uploads the file content to/api/drive/temp(saves to Drive__TEMP__folder); only an "Uploaded" message is shown (no URL generated)
- GET
/api/temp-edit/:token/:fileName— reads from Drive__TEMP__and returns the content with appropriate Content-Type - PUT
/api/temp-edit/:token/:fileName— writes the request body back to the Drive__TEMP__file - Both methods validate the encrypted token and check the 1-hour expiry (returns 410 Gone if expired)
- Click the Temp Download button
- Check if the temporary file has changes
- If changes exist, merge them into the editor
Image insertion flow in WYSIWYG mode:
- Click the image button in the wysimark editor
- Select an image file with
QuickOpenDialog(supported extensions:.png,.jpg,.jpeg,.gif,.webp,.svg,.bmp,.ico) - Upload to Drive (
/api/drive/files, action:create-image) - Insert the returned Drive file URL as a markdown image link
Context for managing shared editor state (app/contexts/EditorContext.tsx).
| Field | Type | Description |
|---|---|---|
activeFileId |
string | null |
ID of the currently open file |
activeFileContent |
string | null |
Current file content |
activeFileName |
string | null |
Current file name |
fileList |
FileListItem[] |
All files from the file tree |
getActiveSelection |
() => SelectionInfo | null |
Get the current text selection |
hasActiveSelection |
boolean |
Whether a file with content is open (not whether text is selected) |
Tracks editor text selection for use with chat panel slash commands ({selection}).
- Raw textarea: Records
{text, start, end}ononSelectevent - WYSIWYG: Monitors DOM selection via
selectionchangeevent (WysiwygSelectionTracker) - Chat access: Retrieved via
editorCtx.getActiveSelection()
| Shortcut | Function |
|---|---|
Ctrl/Cmd+Shift+F |
Open search panel |
Ctrl/Cmd+P |
Open quick file picker |
- Swipe navigation to switch between file/editor/chat panels
visualViewportsupport for iOS Safari (layout adjustment when soft keyboard appears)- Responsive toolbar (dropdown menu on mobile)
- Touch swipe in HTML preview forwarded to parent via
postMessage
| File | Description |
|---|---|
app/components/editor/MarkdownEditor.tsx |
WYSIWYG markdown editor (wysimark-lite) |
app/components/ide/MainViewer.tsx |
Editor routing by file type |
app/components/ide/WorkflowEditor.tsx |
Visual workflow + YAML editor |
app/components/ide/EncryptedFileViewer.tsx |
Encrypted file decryption and editing |
app/components/ide/GfmMarkdownPreview.tsx |
GFM markdown preview (Mermaid support) |
app/components/ide/EditorToolbarActions.tsx |
Toolbar actions (Diff, History, Temp) |
app/contexts/EditorContext.tsx |
Editor shared state context |
app/hooks/useFileWithCache.ts |
Cache-first loading + auto-save |
app/services/edit-history-local.ts |
Local edit history management |

