at main 153 lines 6.1 kB view raw
1//! EditorAction conversion for JavaScript. 2 3use serde::{Deserialize, Serialize}; 4use tsify_next::Tsify; 5use wasm_bindgen::prelude::*; 6use weaver_editor_core::{EditorAction, FormatAction, Range}; 7 8/// JavaScript-friendly editor action. 9/// 10/// Mirrors EditorAction from core but with JS-compatible types. 11/// Also includes FormatAction variants for extended formatting. 12#[derive(Debug, Clone, Serialize, Deserialize, Tsify)] 13#[tsify(into_wasm_abi, from_wasm_abi)] 14#[serde(tag = "type", rename_all = "camelCase")] 15pub enum JsEditorAction { 16 // Text insertion 17 Insert { text: String, start: usize, end: usize }, 18 InsertLineBreak { start: usize, end: usize }, 19 InsertParagraph { start: usize, end: usize }, 20 21 // Deletion 22 DeleteBackward { start: usize, end: usize }, 23 DeleteForward { start: usize, end: usize }, 24 DeleteWordBackward { start: usize, end: usize }, 25 DeleteWordForward { start: usize, end: usize }, 26 DeleteToLineStart { start: usize, end: usize }, 27 DeleteToLineEnd { start: usize, end: usize }, 28 DeleteSoftLineBackward { start: usize, end: usize }, 29 DeleteSoftLineForward { start: usize, end: usize }, 30 31 // History 32 Undo, 33 Redo, 34 35 // Inline formatting (EditorAction variants) 36 ToggleBold, 37 ToggleItalic, 38 ToggleCode, 39 ToggleStrikethrough, 40 InsertLink, 41 42 // Extended formatting (FormatAction variants) 43 InsertImage, 44 InsertHeading { level: u8 }, 45 ToggleBulletList, 46 ToggleNumberedList, 47 ToggleQuote, 48 49 // Clipboard 50 Cut, 51 Copy, 52 Paste { start: usize, end: usize }, 53 CopyAsHtml, 54 55 // Selection 56 SelectAll, 57 58 // Cursor 59 MoveCursor { offset: usize }, 60 ExtendSelection { offset: usize }, 61} 62 63/// Result of converting JsEditorAction. 64pub enum ActionKind { 65 /// Standard EditorAction. 66 Editor(EditorAction), 67 /// FormatAction (needs apply_formatting). 68 Format(FormatAction), 69} 70 71impl JsEditorAction { 72 /// Convert to ActionKind (either EditorAction or FormatAction). 73 pub fn to_action_kind(&self) -> ActionKind { 74 match self { 75 // Text insertion 76 Self::Insert { text, start, end } => ActionKind::Editor(EditorAction::Insert { 77 text: text.clone(), 78 range: Range::new(*start, *end), 79 }), 80 Self::InsertLineBreak { start, end } => ActionKind::Editor(EditorAction::InsertLineBreak { 81 range: Range::new(*start, *end), 82 }), 83 Self::InsertParagraph { start, end } => ActionKind::Editor(EditorAction::InsertParagraph { 84 range: Range::new(*start, *end), 85 }), 86 87 // Deletion 88 Self::DeleteBackward { start, end } => ActionKind::Editor(EditorAction::DeleteBackward { 89 range: Range::new(*start, *end), 90 }), 91 Self::DeleteForward { start, end } => ActionKind::Editor(EditorAction::DeleteForward { 92 range: Range::new(*start, *end), 93 }), 94 Self::DeleteWordBackward { start, end } => ActionKind::Editor(EditorAction::DeleteWordBackward { 95 range: Range::new(*start, *end), 96 }), 97 Self::DeleteWordForward { start, end } => ActionKind::Editor(EditorAction::DeleteWordForward { 98 range: Range::new(*start, *end), 99 }), 100 Self::DeleteToLineStart { start, end } => ActionKind::Editor(EditorAction::DeleteToLineStart { 101 range: Range::new(*start, *end), 102 }), 103 Self::DeleteToLineEnd { start, end } => ActionKind::Editor(EditorAction::DeleteToLineEnd { 104 range: Range::new(*start, *end), 105 }), 106 Self::DeleteSoftLineBackward { start, end } => ActionKind::Editor(EditorAction::DeleteSoftLineBackward { 107 range: Range::new(*start, *end), 108 }), 109 Self::DeleteSoftLineForward { start, end } => ActionKind::Editor(EditorAction::DeleteSoftLineForward { 110 range: Range::new(*start, *end), 111 }), 112 113 // History 114 Self::Undo => ActionKind::Editor(EditorAction::Undo), 115 Self::Redo => ActionKind::Editor(EditorAction::Redo), 116 117 // Inline formatting (EditorAction) 118 Self::ToggleBold => ActionKind::Editor(EditorAction::ToggleBold), 119 Self::ToggleItalic => ActionKind::Editor(EditorAction::ToggleItalic), 120 Self::ToggleCode => ActionKind::Editor(EditorAction::ToggleCode), 121 Self::ToggleStrikethrough => ActionKind::Editor(EditorAction::ToggleStrikethrough), 122 Self::InsertLink => ActionKind::Editor(EditorAction::InsertLink), 123 124 // Extended formatting (FormatAction) 125 Self::InsertImage => ActionKind::Format(FormatAction::Image), 126 Self::InsertHeading { level } => ActionKind::Format(FormatAction::Heading(*level)), 127 Self::ToggleBulletList => ActionKind::Format(FormatAction::BulletList), 128 Self::ToggleNumberedList => ActionKind::Format(FormatAction::NumberedList), 129 Self::ToggleQuote => ActionKind::Format(FormatAction::Quote), 130 131 // Clipboard 132 Self::Cut => ActionKind::Editor(EditorAction::Cut), 133 Self::Copy => ActionKind::Editor(EditorAction::Copy), 134 Self::Paste { start, end } => ActionKind::Editor(EditorAction::Paste { 135 range: Range::new(*start, *end), 136 }), 137 Self::CopyAsHtml => ActionKind::Editor(EditorAction::CopyAsHtml), 138 139 // Selection 140 Self::SelectAll => ActionKind::Editor(EditorAction::SelectAll), 141 142 // Cursor 143 Self::MoveCursor { offset } => ActionKind::Editor(EditorAction::MoveCursor { offset: *offset }), 144 Self::ExtendSelection { offset } => ActionKind::Editor(EditorAction::ExtendSelection { offset: *offset }), 145 } 146 } 147} 148 149/// Parse a JsValue into JsEditorAction. 150pub fn parse_action(value: JsValue) -> Result<JsEditorAction, JsError> { 151 serde_wasm_bindgen::from_value(value) 152 .map_err(|e| JsError::new(&format!("Invalid action: {}", e))) 153}