Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Writer Studio (standalone writing editor) (part 4)

Part 4 of the Writer Studio page: publishing a document to Shares and sending it by email, SMS or Slack, PDF and Word export, Google Docs import and export, local file import, and the Session, Mission Control, KMS vault, Team Chat and PM-tool bridges.

What it is

This is part 4 of the Writer Studio (standalone writing editor) page. It covers everything a document can leave through — publishing to Shares and sending by email, SMS or Slack, PDF and Word export, Google Docs round-trips, local file import, and the Session, Mission Control, KMS vault, Team Chat and PM-tool bridges.

Where to find it

Writer Studio opens from the Writer virtual project in the sidebar, and the parent page covers gating, surfaces and first-run setup. Inside the writer view these actions live in the document header — the Publish, Export to Docs, Send to MC and Send to... buttons — and in the File and Share menus; local file import is reached from the Welcome screen, the empty-state button and the sidebar + menu.

How it behaves

Publish to Shares

A Publish button in the document header publishes the current document's markdown to Omniscio Shares, generating a shareable URL anyone can open — no Omniscio account needed to view it. The document sidebar shows a small share icon on published documents.

  • Publish — calls writer:publish, which pushes the markdown content to Shares and stores the resulting share URL and token on the document row. The link is copied to the clipboard automatically (best-effort — a clipboard failure does not affect the publish itself).
  • Republish — appears when a document is already published. Updates the existing share in place (same URL, new content) by passing the stored republishToken to publishArtifact. The old share is revoked only if the token changed.
  • Unpublish — revokes the published link so it stops working. This is a destructive action (anyone with the old link loses access), so it opens a confirmation dialog first.
  • Published indicator — when a document has a live share link, the header shows an accent-colored "Published" button that copies the link on click, plus Republish and Unpublish options.

Deleting a document automatically revokes its share link if one exists. Empty documents cannot be published.

Send via channels

Published documents can be sent directly to recipients via email, SMS, or Slack from the Share menu. When a document is published, the menu shows "Send via Email", "Send via SMS", and "Send via Slack" items (hidden when not published).

Clicking any channel opens WriterSendViaDialog — a modal with a recipient field (channel-appropriate placeholder from the CHANNELS catalog in shares-detail-helpers.ts) and an optional message textarea. The dialog resolves the document's shareToken to a shareId via the share store, then calls IPC.SHARE_SEND with { shareId, channelKind, recipient, message }. Success shows a toast; errors are humanized via humanizeError.

This reuses the existing share-send infrastructure (src/main/ipc/share/send.ts) — no new IPC channels or backend changes. The SHARE_SEND handler validates recipients per channel (email format, phone E.164, Slack channel name) and applies a 10/minute rate limit.

File export (PDF / Word)

Writer documents can be exported as PDF or Word (DOCX) files from both the File menu and the Share menu. Both entry points call the shared useExportMarkdownToFile hook (src/renderer/src/hooks/useExportMarkdownToFile.ts) with the document's markdown content, format ('pdf' or 'docx'), title, and surface 'standalone'.

  • PDF export is always available.
  • Word export is gated by the wordExportEnabled setting (matching the ExportMenu pattern used elsewhere in the app).
  • Both items show a disabled state while an export is in progress.
  • The hook handles empty-document warnings, file save dialogs, and error toasts internally.

Import & export with Google Docs

Writer Studio can pull a Google Doc in and push a document out, reusing Omniscio's existing Google plumbing — no Writer Studio-specific Google services.

  • Import from Drive — the sidebar + menu "Import from Google Docs" item (and empty-state "Import" button) opens WriterDriveImportDialog, a DialogShell picker that supports two import methods: pasting a direct Google Doc/Sheet/Slides URL, or searching by keyword. The URL input sits above a divider; the keyword search lists the user's Google Docs, Sheets, and Slides (three parallel drive:list-files IPC calls, one per MIME type, merged and sorted by modifiedTime). Docs import via GOOGLE_DOC_FETCH_MARKDOWN (markdown export). Sheets import via writer:import-google-sheet (XLSX export → extractOfficeText → markdown table). Slides import via writer:import-google-slides (PPTX export → extractOfficeText → text outline). All three create a new Writer Studio document via writer:create + writer:save-content. The original file is untouched; import never overwrites the open document. Picker state lives in the dedicated writer-drive-import-store.ts slice (separate from the standalone Drive browser's drive-store.ts). File type labels ("Sheet", "Slides") appear next to each file in the picker via google-apps-mime-labels.ts. Not-connected / expired Google sessions show a "Connect Google" prompt routing to Settings → Google Workspace.
  • Export to Docs — the header "Export to Docs" button publishes the open document's markdown to a fresh Google Doc via the shared usePublishMarkdownToGoogleDoc hook (surface 'writer'). Re-exporting creates another new Doc — no linked/two-way sync. Export is gated by the existing googleDocsExportEnabled setting; when off, the hook shows a "turn it on in Settings" prompt.

Both entry points live inside the gated ai-writer surface. For offline E2E, AMC_GOOGLE_FAKE=1 returns deterministic Drive/Doc data without real OAuth.

File import (local files)

Writer Studio can import local document files, converting them to markdown and creating a new Writer document. Supported formats:

Format Extension(s) Conversion method
DOCX .docx mammoth → turndown (HTML → markdown)
Markdown .md, .markdown Direct read (with UTF-8 BOM stripping)
Text .txt Direct read (with UTF-8 BOM stripping)
RTF .rtf Pure-JS control-word parser (extracts text + \u Unicode)
ODT .odt adm-zip → content.xml XML tag walking
EPUB .epub adm-zip → container.xml → OPF → spine → turndown

The import service (src/main/services/writer/writer-file-import-service.ts) is self-contained — it does NOT extend the generic conversion engine. No new npm dependencies; mammoth, turndown, and adm-zip were already in the tree.

  • Entry points — the Welcome screen (2×2 grid "Upload a file" card), the empty-state "Upload a file" button, and the sidebar + menu "Upload" item all open a native file picker (DIALOG_OPEN_FILE) filtered to the supported extensions, then call writer:import-file → handleImported({ title, markdown }) (the same callback Google Docs import uses, which creates a new document + navigates to it).
  • Safety — ZIP-based formats (DOCX, ODT, EPUB) run through assertArchiveNotBomb() (total uncompressed XML capped at MAX_DOCX_XML_BYTES). File size capped at 200 MB (MAX_FILE_BYTES); converted markdown capped at 10 MB (MAX_MARKDOWN_BYTES). Image references are stripped (Writer is text-only). DRM-protected EPUBs are detected and rejected with a humanized error.
  • Desktop-only — writer:import-file reads a local file path, so it is in BLOCKED_CHANNELS for web-access WS and exempt from CLI parity (feature-scoped-surface).
  • Tests — tests/integration/writer-file-import-service.test.ts (63 tests: all formats + error cases + image stripping + DRM detection + BOM + size limits).

Session bridge (Phase B2)

Writer Studio connects bi-directionally with Claude Code sessions:

  • Session → Writer Studio: right-click a session in the dashboard → "Send to Writer Studio". This assembles the session's visible-only transcript as markdown, creates a new Writer document with that content, and navigates to it in Writer Studio. The action appears only when ai-writer is enabled and exactly one session is selected.
  • Writer Studio → Session: click "Send to Session" in the Writer toolbar. This opens a new Claude Code session with the document's content pre-filled in the composer (not auto-sent), so you can continue working on it with Claude.
  • CLI: POST /writer/create-from-session accepts { sessionId, title? } and returns the new document metadata. Returns 404 for unknown sessions, 403 when the feature is disabled. Agents can use this to programmatically create Writer documents from session transcripts.

Both UI actions are gated by the ai-writer unreleased feature. The IPC channel (writer:create-from-session) is blocked for mobile/web access (desktop-only — it reads the local session database).

Mission Control bridge

Writer documents can be linked to Mission Control (PM) tasks. The bridge requires BOTH ai-writer AND settings.missionControlEnabled to be active (dual gate).

  • "Send to MC" button in the Writer header opens a dialog (SendToMCDialog) where the user creates a new MC task (with board picker) or links to an existing one (via PmItemPicker).
  • Linked task chip replaces the button once linked, showing the task name. Clicking navigates to Mission Control; the unlink button removes the association after a confirm dialog.
  • Dual-write pattern — linking stores both a forward FK (writer_documents.mc_item_id) and a reverse row in pm_item_links (entity_type writer_document), transactionally.
  • Content sync — when a linked Writer document is saved (non-conflict), syncWriterContentToMcItem in writer-mc-bridge.ts copies the document content to the MC item's description column (truncated at 5,000 graphemes with a …(truncated) suffix). The sync is fire-and-forget (try/catch, non-blocking) so a sync failure never blocks the document save. Initial sync also runs at link-creation time via createMcItemAndLink.
  • MC LinksTab shows linked Writer documents with click-through navigation back to the Writer surface (via navigateToEntity on the session bridge).
  • CLI routes — GET/POST/DELETE /writer/documents/:id/mc-link for headless linking; POST /writer/documents/:id/mc-create-and-link to atomically create a new MC item and link it.
  • Hook: useWriterMcLink(docId) manages link state with auto-refresh.

Vault (KMS) bridge

Writer Studio documents can be exported to and imported from the KMS knowledge base (vault), creating a bi-directional markdown bridge between the two surfaces.

  • Export to Vault — the Writer header's "Export to Vault" button saves the current document's markdown content as a KMS note. On first export, a new note is created in the user's default vault (path derived from the document title via sanitizeTitleForPath

    • pickAvailableNoteRelativePath), and the Writer document stores the link (linked_kms_note_id + linked_kms_vault_id). Subsequent exports update the existing linked note's body rather than creating a duplicate. If the link is lost (e.g. after a DB restore), a path-based reconciliation re-links an existing note at the expected path before creating a new one. The handler dual-gates on both ai-writer and KMS being enabled.
  • Open in Writer Studio — the KMS file tree's context menu offers "Open in Writer Studio" on any note. If a Writer document already links to that KMS note, it is opened directly (no duplicate). Otherwise a new Writer document is created with the note's body as content, and the link is stored. Navigation dispatches open-ai-writer to switch to the Writer surface.

  • Link tracking — the linked_kms_note_id column has a unique partial index (WHERE NOT NULL) preventing two Writer documents from linking to the same KMS note. Soft-deleted documents do not block the index. Both directions emit KMS_NOTE_CHANGED via emitPush so the KMS file tree refreshes without polling.

  • CLI: POST /writer/documents/:id/export-to-vault mirrors the IPC handler but additionally tags the note with writer-studio and exported (merged with any content-derived tags). Idempotent via X-Client-Request-Id. Returns 403 when KMS is disabled, 409 when no vault is configured.

Both entry points are gated behind ai-writer AND KMS being enabled. The vault bridge invariants are I34-I38 in writer-contract.md.

Send to Team Chat

Writer documents can be shared to Team Chat channels from the Share menu. The dialog (SendToTeamChatDialog) posts the document content as a message with an omniscio://writer/<docId> deep-link appended. Messages longer than 8,000 characters are truncated with a "(truncated; full document in Writer)" suffix.

  • Threading — the first send stores the clientMsgId and channelId on the document (writer_documents.team_chat_msg_id / team_chat_channel_id). Subsequent sends to the same channel post as a threaded reply via sendReply (using the stored msgId as parentId). The button label changes to "Update in Thread" when a reference exists.
  • Deep-links — the writer host is registered in src/main/services/deep-link.ts, which parses an omniscio://writer/<docId> link into an open-writer-doc action. The renderer's deep-link-router.ts handles that action with handleOpenWriterDoc, which navigates to the document in Writer Studio.
  • Deleted-channel recovery — if the reply fails (e.g. the channel was deleted), the reference is cleared via writer:clear-team-chat-link and the message is re-sent as a fresh message with a "thread unavailable" warning toast.
  • IPC channels — writer:set-team-chat-link and writer:clear-team-chat-link store/clear the reference. Both are desktop-only.

Bidirectional sync (F001)

Linked Writer documents support bidirectional sync with their vault notes:

  • Sync on Save — a global toggle (writerKmsSyncOnSave in kmsSettingsSchema, default off) that auto-pushes the document's content to the linked vault note on every save. The sync is fire-and-forget (does not block the save response) and is gated on KMS being enabled, the setting being on, and the document having a link. If the vault note was modified externally since the last sync (determined by comparing note.updatedAt > meta.kmsSyncedAt), the push is skipped and a warning is logged to avoid overwriting external changes. The toggle appears as a "Sync on Save" menu item with a checkbox indicator in the Share menu, visible only when the document is linked.
  • Pull Latest from Vault — a menu item in the Share menu that replaces the Writer document's content with the latest vault note body. Before overwriting, a pre-import version snapshot is created (appendWriterVersion with reason 'pre-import') so the user can restore via the version history panel. The pull updates kms_synced_at on success and refreshes the sync status indicator.
  • Sync status indicator — a colored dot on the "Save to Vault" / "Update in Vault" menu item showing the current sync state: green (in-sync), amber (writer-ahead or vault-ahead), red (conflict — both sides changed since last sync). The status is computed by comparing meta.updatedAt, note.updatedAt, and meta.kmsSyncedAt via the WRITER_KMS_SYNC_STATUS IPC handler.
  • Conflict handling — when both the Writer document and vault note have changed since the last sync (meta.updatedAt > kmsSyncedAt AND note.updatedAt > kmsSyncedAt), the status reports 'conflict' (red dot) and sync-on-save is skipped. The user resolves conflicts manually via Pull Latest (which snapshots first) or Export (which overwrites the vault note).
  • kms_synced_at — a new TEXT column on writer_documents (migration 20260902214334) that records the ISO timestamp of the last successful sync in either direction (export or pull). Both the export handler and pull handler update this column on success. It is exposed in WriterDocumentMeta and the plugin bridge schema.

Send to PM tool (Phase C3)

A "Send to..." dropdown in the document header creates a task/card/issue in a connected PM tool (ClickUp, Trello, Jira, or Arij) with the document content as the description. Each PM tool appears only when its integration is visible (unreleased-feature-gated) and connected (credentials exist). The dialog shows cascading selects for the provider's hierarchy (e.g. ClickUp: Workspace → Space → Folder → List). The same flow is also accessible from the Share menu ("Send to PM Tool" item, gated on pmEnabled).

After creation, the external URL and provider are stored on the writer_documents row (external_url, external_provider), and a linked indicator appears in the document header. Sending again overwrites the previous link. Trello descriptions are truncated at 16,000 chars.

Both new IPC channels (writer:pm-status, writer:send-to-pm) are desktop-only (BLOCKED_CHANNELS in the WS bridge) and CLI-parity-exempt (feature-scoped-surface). Credentials stay in main — the renderer never sees API keys.

Related

The editor and document list the document is written in are in part 2, the AI capabilities that edit it in part 3, and the IPC channels behind these bridges in part 5; the Writer Studio (standalone writing editor) page is the parent.

Last verified 2026-10-06