mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
📚 Add embedded text editor MVP documentation
This commit is contained in:
217
render-wasm/docs/text_editor.md
Normal file
217
render-wasm/docs/text_editor.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Text Editor Architecture
|
||||
|
||||
## Overview (Simplified)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Browser["Browser / DOM"]
|
||||
CE[contenteditable]
|
||||
Events[DOM Events]
|
||||
end
|
||||
|
||||
subgraph CLJS["ClojureScript"]
|
||||
InputHandler[text_editor_input.cljs]
|
||||
Bindings[text_editor.cljs]
|
||||
ContentCache[(content cache)]
|
||||
end
|
||||
|
||||
subgraph WASM["WASM Boundary"]
|
||||
FFI["_text_editor_* functions"]
|
||||
end
|
||||
|
||||
subgraph Rust["Rust"]
|
||||
subgraph StateModule["state/text_editor.rs"]
|
||||
TES[TextEditorState]
|
||||
Selection[TextSelection]
|
||||
Cursor[TextCursor]
|
||||
end
|
||||
|
||||
subgraph WASMImpl["wasm/text_editor.rs"]
|
||||
StateOps[start / stop]
|
||||
CursorOps[cursor / selection]
|
||||
EditOps[insert / delete]
|
||||
ExportOps[export content]
|
||||
end
|
||||
|
||||
subgraph RenderMod["render/text_editor.rs"]
|
||||
RenderOverlay[render_overlay]
|
||||
end
|
||||
|
||||
Shapes[(ShapesPool)]
|
||||
end
|
||||
|
||||
subgraph Skia["Skia"]
|
||||
Canvas[Canvas]
|
||||
Paragraph[Paragraph layout]
|
||||
end
|
||||
|
||||
%% Flow
|
||||
CE --> Events
|
||||
Events --> InputHandler
|
||||
InputHandler --> Bindings
|
||||
Bindings --> FFI
|
||||
FFI --> StateOps & CursorOps & EditOps & ExportOps
|
||||
|
||||
StateOps --> TES
|
||||
CursorOps --> TES
|
||||
EditOps --> TES
|
||||
EditOps --> Shapes
|
||||
ExportOps --> Shapes
|
||||
TES --> Selection --> Cursor
|
||||
|
||||
RenderOverlay --> TES
|
||||
RenderOverlay --> Shapes
|
||||
Shapes --> Paragraph
|
||||
RenderOverlay --> Canvas
|
||||
Paragraph --> Canvas
|
||||
|
||||
ExportOps --> ContentCache
|
||||
ContentCache --> InputHandler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Detailed Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Browser["Browser / DOM"]
|
||||
CE[contenteditable element]
|
||||
KeyEvents[keydown / keyup]
|
||||
MouseEvents[mousedown / mousemove]
|
||||
IME[compositionstart / end]
|
||||
end
|
||||
|
||||
subgraph CLJS["ClojureScript Layer"]
|
||||
subgraph InputMod["text_editor_input.cljs"]
|
||||
EventHandler[Event Handler]
|
||||
BlinkLoop[RAF Blink Loop]
|
||||
SyncFn[sync-content!]
|
||||
end
|
||||
|
||||
subgraph BindingsMod["text_editor.cljs"]
|
||||
direction TB
|
||||
StartStop[start / stop]
|
||||
CursorFns[set-cursor / move]
|
||||
SelectFns[select-all / extend]
|
||||
EditFns[insert / delete]
|
||||
ExportFns[export-content]
|
||||
StyleFns[apply-style]
|
||||
end
|
||||
|
||||
ContentCache[(shape-text-contents<br/>atom)]
|
||||
end
|
||||
|
||||
subgraph WASM["WASM Boundary"]
|
||||
direction TB
|
||||
FFI_State["_text_editor_start<br/>_text_editor_stop<br/>_text_editor_is_active"]
|
||||
FFI_Cursor["_text_editor_set_cursor_from_point<br/>_text_editor_move_cursor<br/>_text_editor_select_all"]
|
||||
FFI_Edit["_text_editor_insert_text<br/>_text_editor_delete_backward<br/>_text_editor_insert_paragraph"]
|
||||
FFI_Query["_text_editor_export_content<br/>_text_editor_get_selection<br/>_text_editor_poll_event"]
|
||||
FFI_Render["_text_editor_render_overlay<br/>_text_editor_update_blink"]
|
||||
end
|
||||
|
||||
subgraph Rust["Rust Layer"]
|
||||
subgraph StateMod["state/text_editor.rs"]
|
||||
TES[TextEditorState]
|
||||
Selection[TextSelection]
|
||||
Cursor[TextCursor]
|
||||
Events[EditorEvent queue]
|
||||
end
|
||||
|
||||
subgraph WASMMod["wasm/text_editor.rs"]
|
||||
direction TB
|
||||
WStateOps[State ops]
|
||||
WCursorOps[Cursor ops]
|
||||
WEditOps[Edit ops]
|
||||
WQueryOps[Query ops]
|
||||
end
|
||||
|
||||
subgraph RenderMod["render/text_editor.rs"]
|
||||
RenderOverlay[render_overlay]
|
||||
RenderCursor[render_cursor]
|
||||
RenderSelection[render_selection]
|
||||
end
|
||||
|
||||
Shapes[(ShapesPool<br/>TextContent)]
|
||||
end
|
||||
|
||||
subgraph Skia["Skia"]
|
||||
Canvas[Canvas]
|
||||
SkParagraph[textlayout::Paragraph]
|
||||
TextBoxes[get_rects_for_range]
|
||||
end
|
||||
|
||||
%% Browser to CLJS
|
||||
CE --> KeyEvents & MouseEvents & IME
|
||||
KeyEvents --> EventHandler
|
||||
MouseEvents --> EventHandler
|
||||
IME --> EventHandler
|
||||
|
||||
%% CLJS internal
|
||||
EventHandler --> StartStop & CursorFns & EditFns & SelectFns
|
||||
BlinkLoop --> FFI_Render
|
||||
SyncFn --> ExportFns
|
||||
ExportFns --> ContentCache
|
||||
ContentCache --> SyncFn
|
||||
StyleFns --> ContentCache
|
||||
|
||||
%% CLJS to WASM
|
||||
StartStop --> FFI_State
|
||||
CursorFns --> FFI_Cursor
|
||||
SelectFns --> FFI_Cursor
|
||||
EditFns --> FFI_Edit
|
||||
ExportFns --> FFI_Query
|
||||
|
||||
%% WASM to Rust impl
|
||||
FFI_State --> WStateOps
|
||||
FFI_Cursor --> WCursorOps
|
||||
FFI_Edit --> WEditOps
|
||||
FFI_Query --> WQueryOps
|
||||
FFI_Render --> RenderOverlay
|
||||
|
||||
%% Rust internal
|
||||
WStateOps --> TES
|
||||
WCursorOps --> TES
|
||||
WEditOps --> TES
|
||||
WEditOps --> Shapes
|
||||
WQueryOps --> TES
|
||||
WQueryOps --> Shapes
|
||||
|
||||
TES --> Selection
|
||||
Selection --> Cursor
|
||||
TES --> Events
|
||||
|
||||
%% Render flow
|
||||
RenderOverlay --> RenderCursor & RenderSelection
|
||||
RenderCursor --> TES
|
||||
RenderSelection --> TES
|
||||
RenderCursor --> Shapes
|
||||
RenderSelection --> Shapes
|
||||
|
||||
%% Skia
|
||||
Shapes --> SkParagraph
|
||||
SkParagraph --> TextBoxes
|
||||
RenderCursor --> Canvas
|
||||
RenderSelection --> Canvas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
| Layer | File | Purpose |
|
||||
|-------|------|---------|
|
||||
| DOM | - | contenteditable captures keyboard/IME input |
|
||||
| CLJS | `text_editor_input.cljs` | Event handling, blink loop, content sync |
|
||||
| CLJS | `text_editor.cljs` | WASM bindings, content cache, style application |
|
||||
| Rust | `state/text_editor.rs` | TextEditorState, TextSelection, TextCursor |
|
||||
| Rust | `wasm/text_editor.rs` | WASM exported functions |
|
||||
| Rust | `render/text_editor.rs` | Cursor & selection overlay rendering |
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. **Input**: DOM events → ClojureScript handler → WASM function → Rust state
|
||||
2. **Edit**: Rust modifies TextContent in ShapesPool → triggers layout
|
||||
3. **Sync**: Export content → merge with cached styles → update shape
|
||||
4. **Render**: RAF loop → render_overlay → Skia draws cursor/selection
|
||||
Reference in New Issue
Block a user