@fresh-editor/fresh-editor 0.2.12 → 0.2.13

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 (51) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +10 -0
  3. package/package.json +1 -1
  4. package/plugins/audit_mode.ts +79 -58
  5. package/plugins/check-types.sh +1 -0
  6. package/plugins/clangd-lsp.ts +9 -6
  7. package/plugins/clangd_support.ts +12 -8
  8. package/plugins/code-tour.ts +15 -10
  9. package/plugins/config-schema.json +40 -3
  10. package/plugins/csharp_support.ts +15 -10
  11. package/plugins/css-lsp.ts +9 -6
  12. package/plugins/diagnostics_panel.ts +25 -18
  13. package/plugins/examples/README.md +1 -2
  14. package/plugins/examples/async_demo.ts +28 -28
  15. package/plugins/examples/bookmarks.ts +34 -32
  16. package/plugins/examples/buffer_query_demo.ts +20 -20
  17. package/plugins/examples/hello_world.ts +46 -10
  18. package/plugins/examples/virtual_buffer_demo.ts +16 -12
  19. package/plugins/find_references.ts +7 -5
  20. package/plugins/git_blame.ts +13 -9
  21. package/plugins/git_explorer.ts +9 -6
  22. package/plugins/git_find_file.ts +7 -5
  23. package/plugins/git_grep.ts +3 -2
  24. package/plugins/git_gutter.ts +15 -10
  25. package/plugins/git_log.ts +27 -18
  26. package/plugins/go-lsp.ts +9 -6
  27. package/plugins/html-lsp.ts +9 -6
  28. package/plugins/java-lsp.ts +9 -6
  29. package/plugins/json-lsp.ts +9 -6
  30. package/plugins/latex-lsp.ts +9 -6
  31. package/plugins/lib/finder.ts +1 -0
  32. package/plugins/lib/fresh.d.ts +139 -14
  33. package/plugins/live_grep.ts +3 -2
  34. package/plugins/markdown_compose.ts +33 -23
  35. package/plugins/markdown_source.ts +15 -10
  36. package/plugins/marksman-lsp.ts +9 -6
  37. package/plugins/merge_conflict.ts +33 -22
  38. package/plugins/odin-lsp.ts +9 -6
  39. package/plugins/path_complete.ts +3 -2
  40. package/plugins/pkg.ts +70 -48
  41. package/plugins/python-lsp.ts +9 -6
  42. package/plugins/rust-lsp.ts +102 -6
  43. package/plugins/search_replace.ts +32 -21
  44. package/plugins/templ-lsp.ts +9 -6
  45. package/plugins/test_i18n.ts +3 -2
  46. package/plugins/theme_editor.i18n.json +28 -14
  47. package/plugins/theme_editor.ts +1230 -495
  48. package/plugins/typescript-lsp.ts +9 -6
  49. package/plugins/vi_mode.ts +487 -297
  50. package/plugins/welcome.ts +9 -6
  51. package/plugins/zig-lsp.ts +9 -6
@@ -1164,7 +1164,7 @@ function computeResultConflictOffset(conflictIndex: number): number {
1164
1164
  /**
1165
1165
  * Start merge conflict resolution for current buffer
1166
1166
  */
1167
- globalThis.start_merge_conflict = async function(): Promise<void> {
1167
+ async function start_merge_conflict() : Promise<void> {
1168
1168
  if (mergeState.isActive) {
1169
1169
  editor.setStatus(editor.t("status.already_active"));
1170
1170
  return;
@@ -1304,7 +1304,8 @@ globalThis.start_merge_conflict = async function(): Promise<void> {
1304
1304
  } else {
1305
1305
  editor.setStatus(editor.t("status.all_auto_resolved", { total: String(mergeState.conflicts.length) }));
1306
1306
  }
1307
- };
1307
+ }
1308
+ registerHandler("start_merge_conflict", start_merge_conflict);
1308
1309
 
1309
1310
  /**
1310
1311
  * Create the multi-panel merge UI (JetBrains-style: OURS | RESULT | THEIRS)
@@ -1393,7 +1394,7 @@ async function createMergePanels(): Promise<void> {
1393
1394
  // Public Commands - Navigation
1394
1395
  // =============================================================================
1395
1396
 
1396
- globalThis.merge_next_conflict = function(): void {
1397
+ function merge_next_conflict() : void {
1397
1398
  editor.debug(`merge_next_conflict called, isActive=${mergeState.isActive}, conflicts=${mergeState.conflicts.length}`);
1398
1399
 
1399
1400
  if (!mergeState.isActive) {
@@ -1432,9 +1433,10 @@ globalThis.merge_next_conflict = function(): void {
1432
1433
  editor.setStatus(editor.t("status.conflict_all_resolved", { current: String(mergeState.selectedIndex + 1), total: String(mergeState.conflicts.length) }));
1433
1434
  updateViews();
1434
1435
  scrollToSelectedConflict();
1435
- };
1436
+ }
1437
+ registerHandler("merge_next_conflict", merge_next_conflict);
1436
1438
 
1437
- globalThis.merge_prev_conflict = function(): void {
1439
+ function merge_prev_conflict() : void {
1438
1440
  editor.debug(`merge_prev_conflict called, isActive=${mergeState.isActive}, conflicts=${mergeState.conflicts.length}`);
1439
1441
 
1440
1442
  if (!mergeState.isActive) {
@@ -1473,13 +1475,14 @@ globalThis.merge_prev_conflict = function(): void {
1473
1475
  editor.setStatus(editor.t("status.conflict_all_resolved", { current: String(mergeState.selectedIndex + 1), total: String(mergeState.conflicts.length) }));
1474
1476
  updateViews();
1475
1477
  scrollToSelectedConflict();
1476
- };
1478
+ }
1479
+ registerHandler("merge_prev_conflict", merge_prev_conflict);
1477
1480
 
1478
1481
  // =============================================================================
1479
1482
  // Public Commands - Resolution
1480
1483
  // =============================================================================
1481
1484
 
1482
- globalThis.merge_use_ours = function(): void {
1485
+ function merge_use_ours() : void {
1483
1486
  if (!mergeState.isActive) {
1484
1487
  editor.setStatus(editor.t("status.no_active_merge"));
1485
1488
  return;
@@ -1497,9 +1500,10 @@ globalThis.merge_use_ours = function(): void {
1497
1500
  // Move to next unresolved conflict
1498
1501
  moveToNextUnresolved();
1499
1502
  updateViews();
1500
- };
1503
+ }
1504
+ registerHandler("merge_use_ours", merge_use_ours);
1501
1505
 
1502
- globalThis.merge_take_theirs = function(): void {
1506
+ function merge_take_theirs() : void {
1503
1507
  if (!mergeState.isActive) {
1504
1508
  editor.setStatus(editor.t("status.no_active_merge"));
1505
1509
  return;
@@ -1517,9 +1521,10 @@ globalThis.merge_take_theirs = function(): void {
1517
1521
  // Move to next unresolved conflict
1518
1522
  moveToNextUnresolved();
1519
1523
  updateViews();
1520
- };
1524
+ }
1525
+ registerHandler("merge_take_theirs", merge_take_theirs);
1521
1526
 
1522
- globalThis.merge_use_both = function(): void {
1527
+ function merge_use_both() : void {
1523
1528
  if (!mergeState.isActive) {
1524
1529
  editor.setStatus(editor.t("status.no_active_merge"));
1525
1530
  return;
@@ -1537,7 +1542,8 @@ globalThis.merge_use_both = function(): void {
1537
1542
  // Move to next unresolved conflict
1538
1543
  moveToNextUnresolved();
1539
1544
  updateViews();
1540
- };
1545
+ }
1546
+ registerHandler("merge_use_both", merge_use_both);
1541
1547
 
1542
1548
  /**
1543
1549
  * Move selection to the next unresolved conflict
@@ -1561,7 +1567,7 @@ function moveToNextUnresolved(): void {
1561
1567
  // Public Commands - Completion
1562
1568
  // =============================================================================
1563
1569
 
1564
- globalThis.merge_save_and_exit = async function(): Promise<void> {
1570
+ async function merge_save_and_exit() : Promise<void> {
1565
1571
  if (!mergeState.isActive) {
1566
1572
  editor.setStatus(editor.t("status.no_active_merge"));
1567
1573
  return;
@@ -1609,9 +1615,10 @@ globalThis.merge_save_and_exit = async function(): Promise<void> {
1609
1615
  closeMergePanels();
1610
1616
 
1611
1617
  editor.setStatus(editor.t("status.complete"));
1612
- };
1618
+ }
1619
+ registerHandler("merge_save_and_exit", merge_save_and_exit);
1613
1620
 
1614
- globalThis.merge_abort = function(): void {
1621
+ function merge_abort() : void {
1615
1622
  if (!mergeState.isActive) {
1616
1623
  editor.setStatus(editor.t("status.nothing_to_abort"));
1617
1624
  return;
@@ -1623,7 +1630,8 @@ globalThis.merge_abort = function(): void {
1623
1630
  closeMergePanels();
1624
1631
 
1625
1632
  editor.setStatus(editor.t("status.aborted"));
1626
- };
1633
+ }
1634
+ registerHandler("merge_abort", merge_abort);
1627
1635
 
1628
1636
  /**
1629
1637
  * Close all merge panels and reset state
@@ -1682,9 +1690,10 @@ function closeMergePanels(): void {
1682
1690
  // Public Commands - Help
1683
1691
  // =============================================================================
1684
1692
 
1685
- globalThis.merge_show_help = function(): void {
1693
+ function merge_show_help() : void {
1686
1694
  editor.setStatus(editor.t("status.help"));
1687
- };
1695
+ }
1696
+ registerHandler("merge_show_help", merge_show_help);
1688
1697
 
1689
1698
  // =============================================================================
1690
1699
  // Hook Handlers - Auto-Detection
@@ -1693,7 +1702,7 @@ globalThis.merge_show_help = function(): void {
1693
1702
  /**
1694
1703
  * Handle buffer activation - check for conflict markers
1695
1704
  */
1696
- globalThis.onMergeBufferActivated = async function(data: { buffer_id: number }): Promise<void> {
1705
+ async function onMergeBufferActivated(data: { buffer_id: number }) : Promise<void> {
1697
1706
  // Don't trigger if already in merge mode
1698
1707
  if (mergeState.isActive) return;
1699
1708
 
@@ -1717,12 +1726,13 @@ globalThis.onMergeBufferActivated = async function(data: { buffer_id: number }):
1717
1726
  } catch (e) {
1718
1727
  // Not in git repo or other error, ignore
1719
1728
  }
1720
- };
1729
+ }
1730
+ registerHandler("onMergeBufferActivated", onMergeBufferActivated);
1721
1731
 
1722
1732
  /**
1723
1733
  * Handle file open - check for conflict markers
1724
1734
  */
1725
- globalThis.onMergeAfterFileOpen = async function(data: { buffer_id: number; path: string }): Promise<void> {
1735
+ async function onMergeAfterFileOpen(data: { buffer_id: number; path: string }) : Promise<void> {
1726
1736
  // Don't trigger if already in merge mode
1727
1737
  if (mergeState.isActive) return;
1728
1738
 
@@ -1742,7 +1752,8 @@ globalThis.onMergeAfterFileOpen = async function(data: { buffer_id: number; path
1742
1752
  } catch (e) {
1743
1753
  // Not in git repo or other error, ignore
1744
1754
  }
1745
- };
1755
+ }
1756
+ registerHandler("onMergeAfterFileOpen", onMergeAfterFileOpen);
1746
1757
 
1747
1758
  // =============================================================================
1748
1759
  // Hook Registration
@@ -44,7 +44,7 @@ let odinLspError: { serverCommand: string; message: string } | null = null;
44
44
  /**
45
45
  * Handle LSP server errors for Odin
46
46
  */
47
- globalThis.on_odin_lsp_server_error = function (data: LspServerErrorData): void {
47
+ function on_odin_lsp_server_error(data: LspServerErrorData) : void {
48
48
  // Only handle Odin language errors
49
49
  if (data.language !== "odin") {
50
50
  return;
@@ -66,7 +66,8 @@ globalThis.on_odin_lsp_server_error = function (data: LspServerErrorData): void
66
66
  } else {
67
67
  editor.setStatus(`Odin LSP error: ${data.message}`);
68
68
  }
69
- };
69
+ }
70
+ registerHandler("on_odin_lsp_server_error", on_odin_lsp_server_error);
70
71
 
71
72
  // Register hook for LSP server errors
72
73
  editor.on("lsp_server_error", "on_odin_lsp_server_error");
@@ -74,7 +75,7 @@ editor.on("lsp_server_error", "on_odin_lsp_server_error");
74
75
  /**
75
76
  * Handle status bar click when there's an Odin LSP error
76
77
  */
77
- globalThis.on_odin_lsp_status_clicked = function (
78
+ function on_odin_lsp_status_clicked(
78
79
  data: LspStatusClickedData
79
80
  ): void {
80
81
  // Only handle Odin language clicks when there's an error
@@ -94,7 +95,8 @@ globalThis.on_odin_lsp_status_clicked = function (
94
95
  { id: "dismiss", label: "Dismiss (ESC)" },
95
96
  ],
96
97
  });
97
- };
98
+ }
99
+ registerHandler("on_odin_lsp_status_clicked", on_odin_lsp_status_clicked);
98
100
 
99
101
  // Register hook for status bar clicks
100
102
  editor.on("lsp_status_clicked", "on_odin_lsp_status_clicked");
@@ -102,7 +104,7 @@ editor.on("lsp_status_clicked", "on_odin_lsp_status_clicked");
102
104
  /**
103
105
  * Handle action popup results for Odin LSP help
104
106
  */
105
- globalThis.on_odin_lsp_action_result = function (
107
+ function on_odin_lsp_action_result(
106
108
  data: ActionPopupResultData
107
109
  ): void {
108
110
  // Only handle our popup
@@ -127,7 +129,8 @@ globalThis.on_odin_lsp_action_result = function (
127
129
  default:
128
130
  editor.debug(`odin-lsp: Unknown action: ${data.action_id}`);
129
131
  }
130
- };
132
+ }
133
+ registerHandler("on_odin_lsp_action_result", on_odin_lsp_action_result);
131
134
 
132
135
  // Register hook for action popup results
133
136
  editor.on("action_popup_result", "on_odin_lsp_action_result");
@@ -148,7 +148,7 @@ function generateCompletions(input: string): PromptSuggestion[] {
148
148
  }
149
149
 
150
150
  // Handle prompt changes for file prompts
151
- globalThis.onPathCompletePromptChanged = function (args: { prompt_type: string; input: string }): boolean {
151
+ function onPathCompletePromptChanged(args: { prompt_type: string; input: string }) : boolean {
152
152
  if (args.prompt_type !== "open-file" && args.prompt_type !== "save-file-as") {
153
153
  return true; // Not our prompt
154
154
  }
@@ -157,7 +157,8 @@ globalThis.onPathCompletePromptChanged = function (args: { prompt_type: string;
157
157
  editor.setPromptSuggestions(suggestions);
158
158
 
159
159
  return true;
160
- };
160
+ }
161
+ registerHandler("onPathCompletePromptChanged", onPathCompletePromptChanged);
161
162
 
162
163
  // Register event handler
163
164
  editor.on("prompt_changed", "onPathCompletePromptChanged");
package/plugins/pkg.ts CHANGED
@@ -584,7 +584,7 @@ function getInstalledPackages(type: "plugin" | "theme" | "language" | "bundle"):
584
584
  type,
585
585
  source,
586
586
  version: manifest?.version || "unknown",
587
- manifest,
587
+ manifest: manifest ?? undefined,
588
588
  localSource,
589
589
  });
590
590
  }
@@ -1188,7 +1188,7 @@ async function loadLanguagePack(packageDir: string, manifest: PackageManifest):
1188
1188
  }
1189
1189
 
1190
1190
  // Apply changes
1191
- editor.reloadGrammars();
1191
+ await editor.reloadGrammars();
1192
1192
  }
1193
1193
 
1194
1194
  /**
@@ -1271,7 +1271,7 @@ async function loadBundle(packageDir: string, manifest: PackageManifest): Promis
1271
1271
  }
1272
1272
 
1273
1273
  // Apply grammar changes
1274
- editor.reloadGrammars();
1274
+ await editor.reloadGrammars();
1275
1275
  editor.debug(`[pkg] Bundle loaded: ${bundleName}`);
1276
1276
  }
1277
1277
 
@@ -2559,7 +2559,7 @@ function getCurrentFocusIndex(): number {
2559
2559
  }
2560
2560
 
2561
2561
  // Navigation commands
2562
- globalThis.pkg_nav_up = function(): void {
2562
+ function pkg_nav_up() : void {
2563
2563
  if (!pkgState.isOpen) return;
2564
2564
 
2565
2565
  const items = getFilteredItems();
@@ -2569,9 +2569,10 @@ globalThis.pkg_nav_up = function(): void {
2569
2569
  pkgState.selectedIndex = Math.max(0, pkgState.selectedIndex - 1);
2570
2570
  pkgState.focus = { type: "list" };
2571
2571
  updatePkgManagerView();
2572
- };
2572
+ }
2573
+ registerHandler("pkg_nav_up", pkg_nav_up);
2573
2574
 
2574
- globalThis.pkg_nav_down = function(): void {
2575
+ function pkg_nav_down() : void {
2575
2576
  if (!pkgState.isOpen) return;
2576
2577
 
2577
2578
  const items = getFilteredItems();
@@ -2581,9 +2582,10 @@ globalThis.pkg_nav_down = function(): void {
2581
2582
  pkgState.selectedIndex = Math.min(items.length - 1, pkgState.selectedIndex + 1);
2582
2583
  pkgState.focus = { type: "list" };
2583
2584
  updatePkgManagerView();
2584
- };
2585
+ }
2586
+ registerHandler("pkg_nav_down", pkg_nav_down);
2585
2587
 
2586
- globalThis.pkg_next_button = function(): void {
2588
+ function pkg_next_button() : void {
2587
2589
  if (!pkgState.isOpen) return;
2588
2590
 
2589
2591
  const order = getFocusOrder();
@@ -2591,9 +2593,10 @@ globalThis.pkg_next_button = function(): void {
2591
2593
  const nextIdx = (currentIdx + 1) % order.length;
2592
2594
  pkgState.focus = order[nextIdx];
2593
2595
  updatePkgManagerView();
2594
- };
2596
+ }
2597
+ registerHandler("pkg_next_button", pkg_next_button);
2595
2598
 
2596
- globalThis.pkg_prev_button = function(): void {
2599
+ function pkg_prev_button() : void {
2597
2600
  if (!pkgState.isOpen) return;
2598
2601
 
2599
2602
  const order = getFocusOrder();
@@ -2601,9 +2604,10 @@ globalThis.pkg_prev_button = function(): void {
2601
2604
  const prevIdx = (currentIdx - 1 + order.length) % order.length;
2602
2605
  pkgState.focus = order[prevIdx];
2603
2606
  updatePkgManagerView();
2604
- };
2607
+ }
2608
+ registerHandler("pkg_prev_button", pkg_prev_button);
2605
2609
 
2606
- globalThis.pkg_activate = async function(): Promise<void> {
2610
+ async function pkg_activate() : Promise<void> {
2607
2611
  if (!pkgState.isOpen) return;
2608
2612
 
2609
2613
  const focus = pkgState.focus;
@@ -2628,7 +2632,7 @@ globalThis.pkg_activate = async function(): Promise<void> {
2628
2632
 
2629
2633
  // Handle search button - open search prompt with current query
2630
2634
  if (focus.type === "search") {
2631
- globalThis.pkg_search();
2635
+ pkg_search();
2632
2636
  return;
2633
2637
  }
2634
2638
 
@@ -2679,9 +2683,10 @@ globalThis.pkg_activate = async function(): Promise<void> {
2679
2683
  updatePkgManagerView();
2680
2684
  }
2681
2685
  }
2682
- };
2686
+ }
2687
+ registerHandler("pkg_activate", pkg_activate);
2683
2688
 
2684
- globalThis.pkg_back_or_close = function(): void {
2689
+ function pkg_back_or_close() : void {
2685
2690
  if (!pkgState.isOpen) return;
2686
2691
 
2687
2692
  // If focus is on action buttons, go back to list
@@ -2693,19 +2698,22 @@ globalThis.pkg_back_or_close = function(): void {
2693
2698
 
2694
2699
  // Otherwise close
2695
2700
  closePackageManager();
2696
- };
2701
+ }
2702
+ registerHandler("pkg_back_or_close", pkg_back_or_close);
2697
2703
 
2698
- globalThis.pkg_scroll_up = function(): void {
2704
+ function pkg_scroll_up() : void {
2699
2705
  // Just move cursor up in detail view
2700
2706
  editor.executeAction("move_up");
2701
- };
2707
+ }
2708
+ registerHandler("pkg_scroll_up", pkg_scroll_up);
2702
2709
 
2703
- globalThis.pkg_scroll_down = function(): void {
2710
+ function pkg_scroll_down() : void {
2704
2711
  // Just move cursor down in detail view
2705
2712
  editor.executeAction("move_down");
2706
- };
2713
+ }
2714
+ registerHandler("pkg_scroll_down", pkg_scroll_down);
2707
2715
 
2708
- globalThis.pkg_search = function(): void {
2716
+ function pkg_search() : void {
2709
2717
  if (!pkgState.isOpen) return;
2710
2718
 
2711
2719
  // Pre-fill with current search query so typing replaces it
@@ -2714,9 +2722,10 @@ globalThis.pkg_search = function(): void {
2714
2722
  } else {
2715
2723
  editor.startPrompt("Search packages: ", "pkg-search");
2716
2724
  }
2717
- };
2725
+ }
2726
+ registerHandler("pkg_search", pkg_search);
2718
2727
 
2719
- globalThis.onPkgSearchConfirmed = function(args: {
2728
+ function onPkgSearchConfirmed(args: {
2720
2729
  prompt_type: string;
2721
2730
  selected_index: number | null;
2722
2731
  input: string;
@@ -2729,7 +2738,8 @@ globalThis.onPkgSearchConfirmed = function(args: {
2729
2738
  updatePkgManagerView();
2730
2739
 
2731
2740
  return true;
2732
- };
2741
+ }
2742
+ registerHandler("onPkgSearchConfirmed", onPkgSearchConfirmed);
2733
2743
 
2734
2744
  editor.on("prompt_confirmed", "onPkgSearchConfirmed");
2735
2745
 
@@ -2755,7 +2765,7 @@ const registryFinder = new Finder<[string, RegistryEntry]>(editor, {
2755
2765
  /**
2756
2766
  * Browse and install plugins from registry
2757
2767
  */
2758
- globalThis.pkg_install_plugin = async function(): Promise<void> {
2768
+ async function pkg_install_plugin() : Promise<void> {
2759
2769
  editor.debug("[pkg] pkg_install_plugin called");
2760
2770
  try {
2761
2771
  // Always sync registry to ensure latest plugins are available
@@ -2785,12 +2795,13 @@ globalThis.pkg_install_plugin = async function(): Promise<void> {
2785
2795
  editor.debug(`[pkg] Error in pkg_install_plugin: ${e}`);
2786
2796
  editor.setStatus(`Error: ${e}`);
2787
2797
  }
2788
- };
2798
+ }
2799
+ registerHandler("pkg_install_plugin", pkg_install_plugin);
2789
2800
 
2790
2801
  /**
2791
2802
  * Browse and install themes from registry
2792
2803
  */
2793
- globalThis.pkg_install_theme = async function(): Promise<void> {
2804
+ async function pkg_install_theme() : Promise<void> {
2794
2805
  editor.debug("[pkg] pkg_install_theme called");
2795
2806
  try {
2796
2807
  // Always sync registry to ensure latest themes are available
@@ -2816,16 +2827,18 @@ globalThis.pkg_install_theme = async function(): Promise<void> {
2816
2827
  editor.debug(`[pkg] Error in pkg_install_theme: ${e}`);
2817
2828
  editor.setStatus(`Error: ${e}`);
2818
2829
  }
2819
- };
2830
+ }
2831
+ registerHandler("pkg_install_theme", pkg_install_theme);
2820
2832
 
2821
2833
  /**
2822
2834
  * Install from git URL or local path
2823
2835
  */
2824
- globalThis.pkg_install_url = function(): void {
2836
+ function pkg_install_url() : void {
2825
2837
  editor.startPrompt("Git URL or local path:", "pkg-install-url");
2826
- };
2838
+ }
2839
+ registerHandler("pkg_install_url", pkg_install_url);
2827
2840
 
2828
- globalThis.onPkgInstallUrlConfirmed = async function(args: {
2841
+ async function onPkgInstallUrlConfirmed(args: {
2829
2842
  prompt_type: string;
2830
2843
  selected_index: number | null;
2831
2844
  input: string;
@@ -2840,28 +2853,31 @@ globalThis.onPkgInstallUrlConfirmed = async function(args: {
2840
2853
  }
2841
2854
 
2842
2855
  return true;
2843
- };
2856
+ }
2857
+ registerHandler("onPkgInstallUrlConfirmed", onPkgInstallUrlConfirmed);
2844
2858
 
2845
2859
  editor.on("prompt_confirmed", "onPkgInstallUrlConfirmed");
2846
2860
 
2847
2861
  /**
2848
2862
  * Open the package manager UI
2849
2863
  */
2850
- globalThis.pkg_list = async function(): Promise<void> {
2864
+ async function pkg_list() : Promise<void> {
2851
2865
  await openPackageManager();
2852
- };
2866
+ }
2867
+ registerHandler("pkg_list", pkg_list);
2853
2868
 
2854
2869
  /**
2855
2870
  * Update all packages
2856
2871
  */
2857
- globalThis.pkg_update_all = async function(): Promise<void> {
2872
+ async function pkg_update_all() : Promise<void> {
2858
2873
  await updateAllPackages();
2859
- };
2874
+ }
2875
+ registerHandler("pkg_update_all", pkg_update_all);
2860
2876
 
2861
2877
  /**
2862
2878
  * Update a specific package
2863
2879
  */
2864
- globalThis.pkg_update = function(): void {
2880
+ function pkg_update() : void {
2865
2881
  const plugins = getInstalledPackages("plugin");
2866
2882
  const themes = getInstalledPackages("theme");
2867
2883
  const all = [...plugins, ...themes];
@@ -2895,12 +2911,13 @@ globalThis.pkg_update = function(): void {
2895
2911
  load: async () => all
2896
2912
  }
2897
2913
  });
2898
- };
2914
+ }
2915
+ registerHandler("pkg_update", pkg_update);
2899
2916
 
2900
2917
  /**
2901
2918
  * Remove a package
2902
2919
  */
2903
- globalThis.pkg_remove = function(): void {
2920
+ function pkg_remove() : void {
2904
2921
  const plugins = getInstalledPackages("plugin");
2905
2922
  const themes = getInstalledPackages("theme");
2906
2923
  const all = [...plugins, ...themes];
@@ -2930,19 +2947,21 @@ globalThis.pkg_remove = function(): void {
2930
2947
  load: async () => all
2931
2948
  }
2932
2949
  });
2933
- };
2950
+ }
2951
+ registerHandler("pkg_remove", pkg_remove);
2934
2952
 
2935
2953
  /**
2936
2954
  * Sync registry
2937
2955
  */
2938
- globalThis.pkg_sync = async function(): Promise<void> {
2956
+ async function pkg_sync() : Promise<void> {
2939
2957
  await syncRegistry();
2940
- };
2958
+ }
2959
+ registerHandler("pkg_sync", pkg_sync);
2941
2960
 
2942
2961
  /**
2943
2962
  * Show outdated packages
2944
2963
  */
2945
- globalThis.pkg_outdated = async function(): Promise<void> {
2964
+ async function pkg_outdated() : Promise<void> {
2946
2965
  const plugins = getInstalledPackages("plugin");
2947
2966
  const themes = getInstalledPackages("theme");
2948
2967
  const all = [...plugins, ...themes];
@@ -2996,21 +3015,24 @@ globalThis.pkg_outdated = async function(): Promise<void> {
2996
3015
  load: async () => outdated
2997
3016
  }
2998
3017
  });
2999
- };
3018
+ }
3019
+ registerHandler("pkg_outdated", pkg_outdated);
3000
3020
 
3001
3021
  /**
3002
3022
  * Generate lockfile
3003
3023
  */
3004
- globalThis.pkg_lock = async function(): Promise<void> {
3024
+ async function pkg_lock() : Promise<void> {
3005
3025
  await generateLockfile();
3006
- };
3026
+ }
3027
+ registerHandler("pkg_lock", pkg_lock);
3007
3028
 
3008
3029
  /**
3009
3030
  * Install from lockfile
3010
3031
  */
3011
- globalThis.pkg_install_lock = async function(): Promise<void> {
3032
+ async function pkg_install_lock() : Promise<void> {
3012
3033
  await installFromLockfile();
3013
- };
3034
+ }
3035
+ registerHandler("pkg_install_lock", pkg_install_lock);
3014
3036
 
3015
3037
  // =============================================================================
3016
3038
  // Command Registration
@@ -49,7 +49,7 @@ let pythonLspError: { serverCommand: string; message: string } | null = null;
49
49
  /**
50
50
  * Handle LSP server errors for Python
51
51
  */
52
- globalThis.on_python_lsp_server_error = function (
52
+ function on_python_lsp_server_error(
53
53
  data: LspServerErrorData
54
54
  ): void {
55
55
  // Only handle Python language errors
@@ -75,7 +75,8 @@ globalThis.on_python_lsp_server_error = function (
75
75
  } else {
76
76
  editor.setStatus(`Python LSP error: ${data.message}`);
77
77
  }
78
- };
78
+ }
79
+ registerHandler("on_python_lsp_server_error", on_python_lsp_server_error);
79
80
 
80
81
  // Register hook for LSP server errors
81
82
  editor.on("lsp_server_error", "on_python_lsp_server_error");
@@ -83,7 +84,7 @@ editor.on("lsp_server_error", "on_python_lsp_server_error");
83
84
  /**
84
85
  * Handle status bar click when there's a Python LSP error
85
86
  */
86
- globalThis.on_python_lsp_status_clicked = function (
87
+ function on_python_lsp_status_clicked(
87
88
  data: LspStatusClickedData
88
89
  ): void {
89
90
  // Only handle Python language clicks when there's an error
@@ -106,7 +107,8 @@ globalThis.on_python_lsp_status_clicked = function (
106
107
  { id: "dismiss", label: "Dismiss (ESC)" },
107
108
  ],
108
109
  });
109
- };
110
+ }
111
+ registerHandler("on_python_lsp_status_clicked", on_python_lsp_status_clicked);
110
112
 
111
113
  // Register hook for status bar clicks
112
114
  editor.on("lsp_status_clicked", "on_python_lsp_status_clicked");
@@ -114,7 +116,7 @@ editor.on("lsp_status_clicked", "on_python_lsp_status_clicked");
114
116
  /**
115
117
  * Handle action popup results for Python LSP help
116
118
  */
117
- globalThis.on_python_lsp_action_result = function (
119
+ function on_python_lsp_action_result(
118
120
  data: ActionPopupResultData
119
121
  ): void {
120
122
  // Only handle our popup
@@ -154,7 +156,8 @@ globalThis.on_python_lsp_action_result = function (
154
156
  default:
155
157
  editor.debug(`python-lsp: Unknown action: ${data.action_id}`);
156
158
  }
157
- };
159
+ }
160
+ registerHandler("on_python_lsp_action_result", on_python_lsp_action_result);
158
161
 
159
162
  // Register hook for action popup results
160
163
  editor.on("action_popup_result", "on_python_lsp_action_result");