@fresh-editor/fresh-editor 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +66 -2
  2. package/package.json +1 -1
  3. package/plugins/astro-lsp.ts +6 -12
  4. package/plugins/audit_mode.ts +106 -113
  5. package/plugins/bash-lsp.ts +15 -22
  6. package/plugins/clangd-lsp.ts +15 -24
  7. package/plugins/clojure-lsp.ts +9 -12
  8. package/plugins/cmake-lsp.ts +9 -12
  9. package/plugins/code-tour.ts +15 -16
  10. package/plugins/config-schema.json +10 -0
  11. package/plugins/csharp_support.ts +25 -30
  12. package/plugins/css-lsp.ts +15 -22
  13. package/plugins/dart-lsp.ts +9 -12
  14. package/plugins/dashboard.ts +118 -0
  15. package/plugins/devcontainer.i18n.json +84 -28
  16. package/plugins/devcontainer.ts +897 -170
  17. package/plugins/diagnostics_panel.ts +10 -17
  18. package/plugins/elixir-lsp.ts +9 -12
  19. package/plugins/erlang-lsp.ts +9 -12
  20. package/plugins/examples/bookmarks.ts +10 -16
  21. package/plugins/find_references.ts +5 -9
  22. package/plugins/flash.ts +577 -0
  23. package/plugins/fsharp-lsp.ts +9 -12
  24. package/plugins/git_explorer.ts +16 -20
  25. package/plugins/git_gutter.ts +65 -79
  26. package/plugins/git_log.ts +8 -8
  27. package/plugins/gleam-lsp.ts +9 -12
  28. package/plugins/go-lsp.ts +15 -22
  29. package/plugins/graphql-lsp.ts +9 -12
  30. package/plugins/haskell-lsp.ts +9 -12
  31. package/plugins/html-lsp.ts +15 -24
  32. package/plugins/java-lsp.ts +9 -12
  33. package/plugins/json-lsp.ts +15 -24
  34. package/plugins/julia-lsp.ts +9 -12
  35. package/plugins/kotlin-lsp.ts +15 -22
  36. package/plugins/latex-lsp.ts +9 -12
  37. package/plugins/lib/fresh.d.ts +378 -0
  38. package/plugins/lua-lsp.ts +15 -22
  39. package/plugins/markdown_compose.ts +78 -122
  40. package/plugins/markdown_source.ts +8 -10
  41. package/plugins/marksman-lsp.ts +9 -12
  42. package/plugins/merge_conflict.ts +15 -17
  43. package/plugins/nim-lsp.ts +9 -12
  44. package/plugins/nix-lsp.ts +9 -12
  45. package/plugins/nushell-lsp.ts +9 -12
  46. package/plugins/ocaml-lsp.ts +9 -12
  47. package/plugins/odin-lsp.ts +15 -22
  48. package/plugins/path_complete.ts +5 -6
  49. package/plugins/perl-lsp.ts +9 -12
  50. package/plugins/php-lsp.ts +15 -22
  51. package/plugins/pkg.ts +10 -21
  52. package/plugins/protobuf-lsp.ts +9 -12
  53. package/plugins/python-lsp.ts +15 -24
  54. package/plugins/r-lsp.ts +9 -12
  55. package/plugins/ruby-lsp.ts +15 -22
  56. package/plugins/rust-lsp.ts +18 -28
  57. package/plugins/scala-lsp.ts +9 -12
  58. package/plugins/schemas/theme.schema.json +18 -0
  59. package/plugins/search_replace.ts +10 -13
  60. package/plugins/solidity-lsp.ts +9 -12
  61. package/plugins/sql-lsp.ts +9 -12
  62. package/plugins/svelte-lsp.ts +9 -12
  63. package/plugins/swift-lsp.ts +9 -12
  64. package/plugins/tailwindcss-lsp.ts +9 -12
  65. package/plugins/templ-lsp.ts +9 -12
  66. package/plugins/terraform-lsp.ts +9 -12
  67. package/plugins/theme_editor.i18n.json +70 -14
  68. package/plugins/theme_editor.ts +152 -208
  69. package/plugins/toml-lsp.ts +15 -22
  70. package/plugins/tsconfig.json +100 -0
  71. package/plugins/typescript-lsp.ts +15 -24
  72. package/plugins/typst-lsp.ts +15 -22
  73. package/plugins/vi_mode.ts +77 -290
  74. package/plugins/vue-lsp.ts +9 -12
  75. package/plugins/yaml-lsp.ts +15 -22
  76. package/plugins/zig-lsp.ts +9 -12
@@ -50,7 +50,10 @@ let kotlinLspError: { serverCommand: string; message: string } | null = null;
50
50
  /**
51
51
  * Handle LSP server errors for Kotlin
52
52
  */
53
- function on_kotlin_lsp_server_error(data: LspServerErrorData): void {
53
+
54
+
55
+ // Register hook for LSP server errors
56
+ editor.on("lsp_server_error", (data) => {
54
57
  // Only handle Kotlin language errors
55
58
  if (data.language !== "kotlin") {
56
59
  return;
@@ -72,18 +75,15 @@ function on_kotlin_lsp_server_error(data: LspServerErrorData): void {
72
75
  } else {
73
76
  editor.setStatus(`Kotlin LSP error: ${data.message}`);
74
77
  }
75
- }
76
- registerHandler("on_kotlin_lsp_server_error", on_kotlin_lsp_server_error);
77
-
78
- // Register hook for LSP server errors
79
- editor.on("lsp_server_error", "on_kotlin_lsp_server_error");
78
+ });
80
79
 
81
80
  /**
82
81
  * Handle status bar click when there's a Kotlin LSP error
83
82
  */
84
- function on_kotlin_lsp_status_clicked(
85
- data: LspStatusClickedData
86
- ): void {
83
+
84
+
85
+ // Register hook for status bar clicks
86
+ editor.on("lsp_status_clicked", (data) => {
87
87
  // Only handle Kotlin language clicks when there's an error
88
88
  if (data.language !== "kotlin" || !kotlinLspError) {
89
89
  return;
@@ -104,18 +104,15 @@ function on_kotlin_lsp_status_clicked(
104
104
  { id: "dismiss", label: "Dismiss (ESC)" },
105
105
  ],
106
106
  });
107
- }
108
- registerHandler("on_kotlin_lsp_status_clicked", on_kotlin_lsp_status_clicked);
109
-
110
- // Register hook for status bar clicks
111
- editor.on("lsp_status_clicked", "on_kotlin_lsp_status_clicked");
107
+ });
112
108
 
113
109
  /**
114
110
  * Handle action popup results for Kotlin LSP help
115
111
  */
116
- function on_kotlin_lsp_action_result(
117
- data: ActionPopupResultData
118
- ): void {
112
+
113
+
114
+ // Register hook for action popup results
115
+ editor.on("action_popup_result", (data) => {
119
116
  // Only handle our popup
120
117
  if (data.popup_id !== "kotlin-lsp-help") {
121
118
  return;
@@ -153,10 +150,6 @@ function on_kotlin_lsp_action_result(
153
150
  default:
154
151
  editor.debug(`kotlin-lsp: Unknown action: ${data.action_id}`);
155
152
  }
156
- }
157
- registerHandler("on_kotlin_lsp_action_result", on_kotlin_lsp_action_result);
158
-
159
- // Register hook for action popup results
160
- editor.on("action_popup_result", "on_kotlin_lsp_action_result");
153
+ });
161
154
 
162
155
  editor.debug("kotlin-lsp: Plugin loaded");
@@ -22,7 +22,8 @@ interface ActionPopupResultData {
22
22
  const INSTALL_URL = "https://github.com/latex-lsp/texlab#installation";
23
23
  let latexLspError: { serverCommand: string; message: string } | null = null;
24
24
 
25
- function on_latex_lsp_server_error(data: LspServerErrorData) : void {
25
+
26
+ editor.on("lsp_server_error", (data) => {
26
27
  if (data.language !== "latex") return;
27
28
  latexLspError = { serverCommand: data.server_command, message: data.message };
28
29
  if (data.error_type === "not_found") {
@@ -30,11 +31,10 @@ function on_latex_lsp_server_error(data: LspServerErrorData) : void {
30
31
  } else {
31
32
  editor.setStatus(`LaTeX LSP error: ${data.message}`);
32
33
  }
33
- }
34
- registerHandler("on_latex_lsp_server_error", on_latex_lsp_server_error);
35
- editor.on("lsp_server_error", "on_latex_lsp_server_error");
34
+ });
35
+
36
36
 
37
- function on_latex_lsp_status_clicked(data: LspStatusClickedData) : void {
37
+ editor.on("lsp_status_clicked", (data) => {
38
38
  if (data.language !== "latex" || !latexLspError) return;
39
39
  editor.showActionPopup({
40
40
  id: "latex-lsp-help",
@@ -46,11 +46,10 @@ function on_latex_lsp_status_clicked(data: LspStatusClickedData) : void {
46
46
  { id: "dismiss", label: "Dismiss (ESC)" },
47
47
  ],
48
48
  });
49
- }
50
- registerHandler("on_latex_lsp_status_clicked", on_latex_lsp_status_clicked);
51
- editor.on("lsp_status_clicked", "on_latex_lsp_status_clicked");
49
+ });
50
+
52
51
 
53
- function on_latex_lsp_action_result(data: ActionPopupResultData) : void {
52
+ editor.on("action_popup_result", (data) => {
54
53
  if (data.popup_id !== "latex-lsp-help") return;
55
54
  switch (data.action_id) {
56
55
  case "copy_url":
@@ -63,6 +62,4 @@ function on_latex_lsp_action_result(data: ActionPopupResultData) : void {
63
62
  latexLspError = null;
64
63
  break;
65
64
  }
66
- }
67
- registerHandler("on_latex_lsp_action_result", on_latex_lsp_action_result);
68
- editor.on("action_popup_result", "on_latex_lsp_action_result");
65
+ });
@@ -240,6 +240,44 @@ type ViewportInfo = {
240
240
  */
241
241
  height: number;
242
242
  };
243
+ type KeyEventPayload = {
244
+ /**
245
+ * Key name (e.g. `"a"`, `"escape"`, `"f1"`).
246
+ */
247
+ key: string;
248
+ /**
249
+ * Ctrl held.
250
+ */
251
+ ctrl: boolean;
252
+ /**
253
+ * Alt held.
254
+ */
255
+ alt: boolean;
256
+ /**
257
+ * Shift held (only meaningful for non-character keys; for
258
+ * printable characters the case is already encoded in `key`).
259
+ */
260
+ shift: boolean;
261
+ /**
262
+ * Super / Cmd / Meta held.
263
+ */
264
+ meta: boolean;
265
+ };
266
+ type SplitSnapshot = {
267
+ /**
268
+ * Stable split identifier; matches the values used by
269
+ * `setSplitBuffer`, `focusSplit`, `getSplitByLabel`, etc.
270
+ */
271
+ splitId: number;
272
+ /**
273
+ * Buffer currently shown in this split.
274
+ */
275
+ bufferId: BufferId;
276
+ /**
277
+ * Viewport (top byte / dimensions) for this split's active buffer.
278
+ */
279
+ viewport: ViewportInfo;
280
+ };
243
281
  type LayoutHints = {
244
282
  /**
245
283
  * Optional compose width for centering/wrapping
@@ -631,6 +669,19 @@ type GrammarInfoSnapshot = {
631
669
  */
632
670
  short_name: string | null;
633
671
  };
672
+ type AnimationRect = {
673
+ x: number;
674
+ y: number;
675
+ width: number;
676
+ height: number;
677
+ };
678
+ type PluginAnimationEdge = "top" | "bottom" | "left" | "right";
679
+ type PluginAnimationKind = {
680
+ "kind": "slideIn";
681
+ from: PluginAnimationEdge;
682
+ durationMs: number;
683
+ delayMs: number;
684
+ };
634
685
  type AuthorityFilesystem = {
635
686
  kind: "local";
636
687
  };
@@ -641,6 +692,7 @@ type AuthoritySpawner = {
641
692
  container_id: string;
642
693
  user?: string | null;
643
694
  workspace?: string | null;
695
+ env?: [string, string][];
644
696
  };
645
697
  type AuthorityTerminalWrapper = {
646
698
  kind: "host-shell";
@@ -655,6 +707,17 @@ type AuthorityPayload = {
655
707
  spawner: AuthoritySpawner;
656
708
  terminal_wrapper: AuthorityTerminalWrapper;
657
709
  display_label?: string;
710
+ /**
711
+ * Optional host↔remote workspace path mapping. The dev-container
712
+ * authority sets both roots (editor.getCwd() on host;
713
+ * remoteWorkspaceFolder on container) so LSP URIs translate at the
714
+ * host/container boundary. Local and SSH authorities omit it.
715
+ */
716
+ path_translation?: PathTranslationSpec;
717
+ };
718
+ type PathTranslationSpec = {
719
+ host_root: string;
720
+ remote_root: string;
658
721
  };
659
722
  type BackgroundProcessResult = {
660
723
  /**
@@ -1068,6 +1131,15 @@ interface EditorAPI {
1068
1131
  */
1069
1132
  getViewport(): ViewportInfo | null;
1070
1133
  /**
1134
+ * List every split with its active buffer and viewport.
1135
+ *
1136
+ * Plugins that need to operate on every visible buffer
1137
+ * simultaneously (multi-split flash labels, syncing decorations
1138
+ * across panes, …) iterate this list rather than only seeing
1139
+ * `getViewport()`'s active-split data. Order is unspecified.
1140
+ */
1141
+ listSplits(): SplitSnapshot[];
1142
+ /**
1071
1143
  * Get the line number (0-indexed) of the primary cursor
1072
1144
  */
1073
1145
  getCursorLine(): number;
@@ -1140,6 +1212,21 @@ interface EditorAPI {
1140
1212
  */
1141
1213
  closeBuffer(bufferId: number): boolean;
1142
1214
  /**
1215
+ * Start a frame-buffer animation over an arbitrary screen region.
1216
+ * Returns an animation id usable with `cancelAnimation`.
1217
+ */
1218
+ animateArea(rect: AnimationRect, kind: PluginAnimationKind): number;
1219
+ /**
1220
+ * Start an animation over the on-screen Rect currently occupied by a
1221
+ * virtual buffer. No-op if the buffer is not visible.
1222
+ */
1223
+ animateVirtualBuffer(bufferId: number, kind: PluginAnimationKind): number;
1224
+ /**
1225
+ * Cancel an animation previously started via `animateArea` or
1226
+ * `animateVirtualBuffer`. No-op if the ID is unknown or already done.
1227
+ */
1228
+ cancelAnimation(id: number): boolean;
1229
+ /**
1143
1230
  * Subscribe to an editor event
1144
1231
  */
1145
1232
  on(eventName: string, handlerName: string): void;
@@ -1546,6 +1633,14 @@ interface EditorAPI {
1546
1633
  */
1547
1634
  removeVirtualText(bufferId: number, virtualTextId: string): boolean;
1548
1635
  /**
1636
+ * Add styled virtual text — richer form of `addVirtualText` whose
1637
+ * `options` accepts an `addOverlay`-style record: `fg`/`bg` may
1638
+ * be RGB arrays or theme-key strings, plus `bold`/`italic`. Theme
1639
+ * keys are resolved at render time so the label follows theme
1640
+ * changes live.
1641
+ */
1642
+ addVirtualTextStyled(bufferId: number, virtualTextId: string, position: number, text: string, options: Record<string, unknown>, before: boolean): boolean;
1643
+ /**
1549
1644
  * Remove virtual texts whose ID starts with the given prefix
1550
1645
  */
1551
1646
  removeVirtualTextsByPrefix(bufferId: number, prefix: string): boolean;
@@ -1577,6 +1672,38 @@ interface EditorAPI {
1577
1672
  */
1578
1673
  startPrompt(label: string, promptType: string): boolean;
1579
1674
  /**
1675
+ * Begin a key-capture window for the calling plugin.
1676
+ *
1677
+ * Pair with `endKeyCapture()` around any `getNextKey()` loop.
1678
+ * While capture is active, keys arriving between two
1679
+ * `getNextKey()` calls are buffered in-order rather than
1680
+ * falling through to the buffer / mode bindings, so fast typing,
1681
+ * pastes, or held-key auto-repeat are delivered losslessly.
1682
+ * Without this, a plugin's input loop has a race where keys
1683
+ * typed while the plugin is mid-redraw can leak into the editor.
1684
+ */
1685
+ beginKeyCapture(): boolean;
1686
+ /**
1687
+ * End the key-capture window and discard any unconsumed buffered
1688
+ * keys. Call from a `finally` block so capture is released even
1689
+ * if the plugin's loop throws.
1690
+ */
1691
+ endKeyCapture(): boolean;
1692
+ /**
1693
+ * Wait for the next keypress and resolve with a `KeyEventPayload`.
1694
+ *
1695
+ * While the returned promise is pending the editor consumes the
1696
+ * next key and resolves it; the key does not propagate to mode
1697
+ * bindings or other dispatch. Multiple in-flight requests across
1698
+ * plugins are FIFO. Designed for short input loops (flash labels,
1699
+ * vi find-char, replace-char) that would otherwise need to bind
1700
+ * every printable key in `defineMode`.
1701
+ *
1702
+ * For lossless capture against fast typing or paste, wrap the
1703
+ * loop with `beginKeyCapture()` / `endKeyCapture()`.
1704
+ */
1705
+ getNextKey(): Promise<KeyEventPayload>;
1706
+ /**
1580
1707
  * Start a prompt with initial value
1581
1708
  */
1582
1709
  startPromptWithInitial(label: string, promptType: string, initialValue: string): boolean;
@@ -1925,3 +2052,254 @@ interface EditorAPI {
1925
2052
  interface EditorAPI {
1926
2053
  getPluginApi<K extends keyof FreshPluginRegistry>(name: K): FreshPluginRegistry[K] | null;
1927
2054
  }
2055
+ /**
2056
+ * Maps every hook event name to its payload type.
2057
+ *
2058
+ * Payloads match the flat JSON produced by `hook_args_to_json` on the Rust
2059
+ * side (`HookArgs` is `#[serde(untagged)]`, so each variant serializes as its
2060
+ * fields only). The TypeScript types here are derived directly from the Rust
2061
+ * field definitions and must be kept in sync with `fresh-core/src/hooks.rs`.
2062
+ *
2063
+ * `action` in `pre_command`/`post_command` is the serde JSON of the `Action`
2064
+ * enum: unit variants serialize as a plain string (e.g. `"MoveLeft"`),
2065
+ * tuple variants as a single-key object (e.g. `{"InsertChar": "a"}`).
2066
+ */
2067
+ interface HookEventMap {
2068
+ // ── lifecycle ────────────────────────────────────────────────────────────
2069
+ editor_initialized: Record<string, never>;
2070
+ plugins_loaded: Record<string, never>;
2071
+ ready: Record<string, never>;
2072
+ focus_gained: Record<string, never>;
2073
+ authority_changed: {
2074
+ label: string;
2075
+ };
2076
+ // ── buffer lifecycle ─────────────────────────────────────────────────────
2077
+ buffer_activated: {
2078
+ buffer_id: number;
2079
+ };
2080
+ buffer_deactivated: {
2081
+ buffer_id: number;
2082
+ };
2083
+ buffer_closed: {
2084
+ buffer_id: number;
2085
+ };
2086
+ // ── file I/O ─────────────────────────────────────────────────────────────
2087
+ before_file_open: {
2088
+ path: string;
2089
+ };
2090
+ after_file_open: {
2091
+ path: string;
2092
+ buffer_id: number;
2093
+ };
2094
+ before_file_save: {
2095
+ path: string;
2096
+ buffer_id: number;
2097
+ };
2098
+ after_file_save: {
2099
+ path: string;
2100
+ buffer_id: number;
2101
+ };
2102
+ // ── text edits ───────────────────────────────────────────────────────────
2103
+ before_insert: {
2104
+ buffer_id: number;
2105
+ position: number;
2106
+ text: string;
2107
+ };
2108
+ after_insert: {
2109
+ buffer_id: number;
2110
+ position: number;
2111
+ text: string;
2112
+ affected_start: number;
2113
+ affected_end: number;
2114
+ start_line: number;
2115
+ end_line: number;
2116
+ lines_added: number;
2117
+ };
2118
+ before_delete: {
2119
+ buffer_id: number;
2120
+ start: number;
2121
+ end: number;
2122
+ };
2123
+ after_delete: {
2124
+ buffer_id: number;
2125
+ start: number;
2126
+ end: number;
2127
+ deleted_text: string;
2128
+ affected_start: number;
2129
+ deleted_len: number;
2130
+ start_line: number;
2131
+ end_line: number;
2132
+ lines_removed: number;
2133
+ };
2134
+ // ── cursor & viewport ────────────────────────────────────────────────────
2135
+ cursor_moved: {
2136
+ buffer_id: number;
2137
+ cursor_id: number;
2138
+ old_position: number;
2139
+ new_position: number;
2140
+ line: number;
2141
+ text_properties: Record<string, unknown>[];
2142
+ };
2143
+ viewport_changed: {
2144
+ split_id: number;
2145
+ buffer_id: number;
2146
+ top_byte: number;
2147
+ top_line: number | null;
2148
+ width: number;
2149
+ height: number;
2150
+ };
2151
+ // ── rendering ────────────────────────────────────────────────────────────
2152
+ render_start: {
2153
+ buffer_id: number;
2154
+ };
2155
+ render_line: {
2156
+ buffer_id: number;
2157
+ line_number: number;
2158
+ byte_start: number;
2159
+ byte_end: number;
2160
+ content: string;
2161
+ };
2162
+ lines_changed: {
2163
+ buffer_id: number;
2164
+ lines: {
2165
+ line_number: number;
2166
+ byte_start: number;
2167
+ byte_end: number;
2168
+ content: string;
2169
+ }[];
2170
+ };
2171
+ view_transform_request: {
2172
+ buffer_id: number;
2173
+ split_id: number;
2174
+ viewport_start: number;
2175
+ viewport_end: number;
2176
+ tokens: ViewTokenWire[];
2177
+ cursor_positions: number[];
2178
+ };
2179
+ // ── commands ─────────────────────────────────────────────────────────────
2180
+ pre_command: {
2181
+ action: string | Record<string, unknown>;
2182
+ };
2183
+ post_command: {
2184
+ action: string | Record<string, unknown>;
2185
+ };
2186
+ idle: {
2187
+ milliseconds: number;
2188
+ };
2189
+ resize: {
2190
+ width: number;
2191
+ height: number;
2192
+ };
2193
+ // ── prompts ──────────────────────────────────────────────────────────────
2194
+ prompt_changed: {
2195
+ prompt_type: string;
2196
+ input: string;
2197
+ };
2198
+ prompt_confirmed: {
2199
+ prompt_type: string;
2200
+ input: string;
2201
+ selected_index: number | null;
2202
+ };
2203
+ prompt_cancelled: {
2204
+ prompt_type: string;
2205
+ input: string;
2206
+ };
2207
+ prompt_selection_changed: {
2208
+ prompt_type: string;
2209
+ selected_index: number;
2210
+ };
2211
+ // ── mouse ────────────────────────────────────────────────────────────────
2212
+ mouse_click: MouseClickHookArgs;
2213
+ mouse_move: {
2214
+ column: number;
2215
+ row: number;
2216
+ content_x: number;
2217
+ content_y: number;
2218
+ };
2219
+ mouse_scroll: {
2220
+ buffer_id: number;
2221
+ delta: number;
2222
+ col: number;
2223
+ row: number;
2224
+ };
2225
+ // ── LSP ──────────────────────────────────────────────────────────────────
2226
+ diagnostics_updated: {
2227
+ uri: string;
2228
+ count: number;
2229
+ };
2230
+ lsp_references: {
2231
+ symbol: string;
2232
+ locations: {
2233
+ file: string;
2234
+ line: number;
2235
+ column: number;
2236
+ }[];
2237
+ };
2238
+ lsp_server_request: {
2239
+ language: string;
2240
+ method: string;
2241
+ server_command: string;
2242
+ params: string | null;
2243
+ };
2244
+ lsp_server_error: {
2245
+ language: string;
2246
+ server_command: string;
2247
+ error_type: string;
2248
+ message: string;
2249
+ };
2250
+ lsp_status_clicked: {
2251
+ language: string;
2252
+ has_error: boolean;
2253
+ missing_servers: string[];
2254
+ user_dismissed: boolean;
2255
+ };
2256
+ // ── UI events ────────────────────────────────────────────────────────────
2257
+ action_popup_result: {
2258
+ popup_id: string;
2259
+ action_id: string;
2260
+ };
2261
+ process_output: {
2262
+ process_id: number;
2263
+ data: string;
2264
+ };
2265
+ language_changed: {
2266
+ buffer_id: number;
2267
+ language: string;
2268
+ };
2269
+ theme_inspect_key: {
2270
+ theme_name: string;
2271
+ key: string;
2272
+ };
2273
+ keyboard_shortcuts: {
2274
+ bindings: {
2275
+ key: string;
2276
+ action: string;
2277
+ }[];
2278
+ };
2279
+ }
2280
+ /**
2281
+ * Typed overloads of `editor.on` / `editor.off`.
2282
+ *
2283
+ * When the event name is a key of `HookEventMap` the handler receives a
2284
+ * fully-typed payload — TypeScript will flag misspelled field accesses at
2285
+ * compile time. Unknown event names fall through to the untyped base
2286
+ * signatures in the EditorAPI interface.
2287
+ *
2288
+ * Both function-value and handler-name forms are supported:
2289
+ *
2290
+ * ```ts
2291
+ * editor.on("buffer_activated", (args) => { /* args.buffer_id is number *\/ });
2292
+ * editor.on("buffer_activated", "myHandler"); // registerHandler("myHandler", fn)
2293
+ * ```
2294
+ */
2295
+ interface EditorAPI {
2296
+ on<K extends keyof HookEventMap>(eventName: K, handler: (args: HookEventMap[K]) => boolean | void | Promise<boolean | void>): void;
2297
+ on<K extends keyof HookEventMap>(eventName: K, handlerName: string): void;
2298
+ off<K extends keyof HookEventMap>(eventName: K, handler: (args: HookEventMap[K]) => boolean | void | Promise<boolean | void>): void;
2299
+ off<K extends keyof HookEventMap>(eventName: K, handlerName: string): void;
2300
+ /**
2301
+ * Create a buffer group: multiple panels appearing as one tab.
2302
+ * This is an async runtime binding (not a direct #[qjs] method).
2303
+ */
2304
+ createBufferGroup(name: string, mode: string, layout: unknown): Promise<BufferGroupResult>;
2305
+ }
@@ -49,7 +49,10 @@ let luaLspError: { serverCommand: string; message: string } | null = null;
49
49
  /**
50
50
  * Handle LSP server errors for Lua
51
51
  */
52
- function on_lua_lsp_server_error(data: LspServerErrorData): void {
52
+
53
+
54
+ // Register hook for LSP server errors
55
+ editor.on("lsp_server_error", (data) => {
53
56
  // Only handle Lua language errors
54
57
  if (data.language !== "lua") {
55
58
  return;
@@ -71,18 +74,15 @@ function on_lua_lsp_server_error(data: LspServerErrorData): void {
71
74
  } else {
72
75
  editor.setStatus(`Lua LSP error: ${data.message}`);
73
76
  }
74
- }
75
- registerHandler("on_lua_lsp_server_error", on_lua_lsp_server_error);
76
-
77
- // Register hook for LSP server errors
78
- editor.on("lsp_server_error", "on_lua_lsp_server_error");
77
+ });
79
78
 
80
79
  /**
81
80
  * Handle status bar click when there's a Lua LSP error
82
81
  */
83
- function on_lua_lsp_status_clicked(
84
- data: LspStatusClickedData
85
- ): void {
82
+
83
+
84
+ // Register hook for status bar clicks
85
+ editor.on("lsp_status_clicked", (data) => {
86
86
  // Only handle Lua language clicks when there's an error
87
87
  if (data.language !== "lua" || !luaLspError) {
88
88
  return;
@@ -103,18 +103,15 @@ function on_lua_lsp_status_clicked(
103
103
  { id: "dismiss", label: "Dismiss (ESC)" },
104
104
  ],
105
105
  });
106
- }
107
- registerHandler("on_lua_lsp_status_clicked", on_lua_lsp_status_clicked);
108
-
109
- // Register hook for status bar clicks
110
- editor.on("lsp_status_clicked", "on_lua_lsp_status_clicked");
106
+ });
111
107
 
112
108
  /**
113
109
  * Handle action popup results for Lua LSP help
114
110
  */
115
- function on_lua_lsp_action_result(
116
- data: ActionPopupResultData
117
- ): void {
111
+
112
+
113
+ // Register hook for action popup results
114
+ editor.on("action_popup_result", (data) => {
118
115
  // Only handle our popup
119
116
  if (data.popup_id !== "lua-lsp-help") {
120
117
  return;
@@ -152,10 +149,6 @@ function on_lua_lsp_action_result(
152
149
  default:
153
150
  editor.debug(`lua-lsp: Unknown action: ${data.action_id}`);
154
151
  }
155
- }
156
- registerHandler("on_lua_lsp_action_result", on_lua_lsp_action_result);
157
-
158
- // Register hook for action popup results
159
- editor.on("action_popup_result", "on_lua_lsp_action_result");
152
+ });
160
153
 
161
154
  editor.debug("lua-lsp: Plugin loaded");