Host Integration
Textagram has a reusable core library.
That core is the main editing engine: it owns the document model, modes, clipboard policy, semantic rendering, and the rules that make Textagram feel like Textagram.
The long-term goal is for that same engine to power many hosts without rewriting the editing logic each time:
- the web app
- the terminal app
- IDE/editor embeds
- any future hosts we have not yet imagined
This section is a guide to building a new Textagram host.
To do that well, the first thing to understand is the boundary between the reusable core and a concrete host.
The main host-facing surface is textagram::Session. The core owns:
- document state
- translated input semantics: modes, key handling, and resulting actions
- clipboard semantics: what copy, cut, and paste mean in each mode
- semantic rendering and structured presentation: glyphs, text, highlights, and screen rows
- timer requests for transient UI animation
The host owns everything outside that boundary:
- the event loop
- raw platform events and translation into
AppKeyEvent, plus clipboard-intent routing when needed - real clipboard integration: reading, writing, and bridging OS/browser clipboard APIs
- viewport measurement
- drawing to the terminal, DOM, or editor surface
- file I/O and save prompts
There are currently two implemented integrations, and both are useful reference points when building a new host:
| Host layer | Responsibility | Main code |
|---|---|---|
tg |
terminal event loop, ratatui painting, OS clipboard, file-backed save/quit flow | tg/src/main.rs |
textagram-wasm + browser host |
wasm surface, DOM keyboard/clipboard bridge, timer scheduling, screen-row HTML rendering | textagram-wasm/src/web_session.rs, textagram-wasm/src/web_host.rs, web/textagram-host.js |
The actual interface
A host creates a Session, feeds it translated input, and consumes semantic output.
At the center of that contract are a few types:
SessionAppKeyEventActionClipboardIntentAreaScreenFrameRenderLine/RenderSegment/SemanticStyle
In practice, the host loop looks like this:
native event
-> translate to AppKeyEvent
-> optionally ask Session::clipboard_intent(event)
-> if clipboard: call clipboard_copy/_cut/_paste(...)
-> else: call Session::handle_key(...)
-> inspect Action if handle_key was used
-> ask Session for ScreenFrame
-> paint it using host-native rendering
-> schedule next tick if Session asks for oneClipboard goes through a separate path because the host still owns the actual clipboard transport. The core classifies the intent, but the host is the one that reads clipboard text for paste and writes clipboard text after copy or cut.
Important current design points
- The Session-based editing core does not perform host I/O directly. Hosts own files, terminal/DOM APIs, and real clipboard transport. The crate still contains optional debug helpers such as trace-file setup, but they are outside the normal host/session editing contract.
- Clipboard is split into policy vs transport. The core decides what
CopyDocument,PasteSelection, orCutSelectionmean. The host decides how to talk to the real clipboard. - Rendering is surface-agnostic. The core returns structured presentation
data —
ScreenFrame,RenderLine,RenderSegment, andSemanticStyle— not ratatui widgets or DOM nodes. - Hosts may add their own chrome.
tgusesChromeOverridesto add file identity and save prompts without forking the shared compositor. - Dirty file state is host-owned. The core knows whether the document changed since the last export. The terminal host decides what “save” means for a real file path.
- The browser and terminal implementations should stay behaviorally aligned. Translation and
clipboard bridges differ, but the same
Sessionshould reach the same document state from equivalent logical input.
Reading order
- Core/Host Interface — the
Sessionsurface and what a host actually calls - Input — how native events become
AppKeyEventand clipboard intents - Render — how hosts consume
ScreenFrameand semantic styles - Adding a New Host — checklist for a third integration
The deeper pages in this section are being refreshed toward this Session-first
view. Start here, then read them as the lower-level details behind this
boundary.