@mariozechner/pi-coding-agent 0.32.3 → 0.34.0

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 (102) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +76 -3
  3. package/dist/cli/args.d.ts +5 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +18 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +24 -1
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +65 -9
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/bash-executor.d.ts.map +1 -1
  12. package/dist/core/bash-executor.js +2 -1
  13. package/dist/core/bash-executor.js.map +1 -1
  14. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  15. package/dist/core/custom-tools/loader.js +1 -0
  16. package/dist/core/custom-tools/loader.js.map +1 -1
  17. package/dist/core/export-html/template.css +34 -4
  18. package/dist/core/export-html/template.js +17 -4
  19. package/dist/core/hooks/index.d.ts +1 -1
  20. package/dist/core/hooks/index.d.ts.map +1 -1
  21. package/dist/core/hooks/index.js.map +1 -1
  22. package/dist/core/hooks/loader.d.ts +56 -1
  23. package/dist/core/hooks/loader.d.ts.map +1 -1
  24. package/dist/core/hooks/loader.js +54 -2
  25. package/dist/core/hooks/loader.js.map +1 -1
  26. package/dist/core/hooks/runner.d.ts +33 -5
  27. package/dist/core/hooks/runner.d.ts.map +1 -1
  28. package/dist/core/hooks/runner.js +100 -9
  29. package/dist/core/hooks/runner.js.map +1 -1
  30. package/dist/core/hooks/types.d.ts +135 -3
  31. package/dist/core/hooks/types.d.ts.map +1 -1
  32. package/dist/core/hooks/types.js.map +1 -1
  33. package/dist/core/keybindings.d.ts +59 -0
  34. package/dist/core/keybindings.d.ts.map +1 -0
  35. package/dist/core/keybindings.js +149 -0
  36. package/dist/core/keybindings.js.map +1 -0
  37. package/dist/core/sdk.d.ts +3 -0
  38. package/dist/core/sdk.d.ts.map +1 -1
  39. package/dist/core/sdk.js +102 -27
  40. package/dist/core/sdk.js.map +1 -1
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/main.d.ts.map +1 -1
  46. package/dist/main.js +32 -7
  47. package/dist/main.js.map +1 -1
  48. package/dist/modes/interactive/components/custom-editor.d.ts +13 -12
  49. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  50. package/dist/modes/interactive/components/custom-editor.js +50 -68
  51. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  52. package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/hook-editor.js +5 -4
  54. package/dist/modes/interactive/components/hook-editor.js.map +1 -1
  55. package/dist/modes/interactive/components/hook-input.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/hook-input.js +4 -3
  57. package/dist/modes/interactive/components/hook-input.js.map +1 -1
  58. package/dist/modes/interactive/components/hook-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/hook-selector.js +6 -5
  60. package/dist/modes/interactive/components/hook-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/model-selector.js +6 -5
  63. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  64. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/oauth-selector.js +6 -5
  66. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  67. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/session-selector.js +6 -9
  69. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  70. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  71. package/dist/modes/interactive/components/tree-selector.js +14 -15
  72. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  73. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  74. package/dist/modes/interactive/components/user-message-selector.js +6 -11
  75. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts +34 -1
  77. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  78. package/dist/modes/interactive/interactive-mode.js +300 -64
  79. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  80. package/dist/modes/interactive/theme/theme.d.ts +1 -0
  81. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme.js +3 -0
  83. package/dist/modes/interactive/theme/theme.js.map +1 -1
  84. package/dist/modes/print-mode.d.ts.map +1 -1
  85. package/dist/modes/print-mode.js +3 -0
  86. package/dist/modes/print-mode.js.map +1 -1
  87. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  88. package/dist/modes/rpc/rpc-mode.js +16 -0
  89. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  90. package/dist/modes/rpc/rpc-types.d.ts +6 -0
  91. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  92. package/dist/modes/rpc/rpc-types.js.map +1 -1
  93. package/docs/hooks.md +114 -4
  94. package/docs/tui.md +18 -15
  95. package/examples/custom-tools/subagent/README.md +2 -2
  96. package/examples/hooks/README.md +3 -0
  97. package/examples/hooks/pirate.ts +44 -0
  98. package/examples/hooks/plan-mode.ts +548 -0
  99. package/examples/hooks/snake.ts +7 -7
  100. package/examples/hooks/todo/index.ts +2 -2
  101. package/examples/hooks/tools.ts +145 -0
  102. package/package.json +5 -4
@@ -1,4 +1,4 @@
1
- import { Container, isArrowDown, isArrowUp, isCtrlC, isEnter, isEscape, Spacer, Text, truncateToWidth, } from "@mariozechner/pi-tui";
1
+ import { Container, getEditorKeybindings, Spacer, Text, truncateToWidth } from "@mariozechner/pi-tui";
2
2
  import { theme } from "../theme/theme.js";
3
3
  import { DynamicBorder } from "./dynamic-border.js";
4
4
  /**
@@ -55,29 +55,24 @@ class UserMessageList {
55
55
  return lines;
56
56
  }
57
57
  handleInput(keyData) {
58
+ const kb = getEditorKeybindings();
58
59
  // Up arrow - go to previous (older) message, wrap to bottom when at top
59
- if (isArrowUp(keyData)) {
60
+ if (kb.matches(keyData, "selectUp")) {
60
61
  this.selectedIndex = this.selectedIndex === 0 ? this.messages.length - 1 : this.selectedIndex - 1;
61
62
  }
62
63
  // Down arrow - go to next (newer) message, wrap to top when at bottom
63
- else if (isArrowDown(keyData)) {
64
+ else if (kb.matches(keyData, "selectDown")) {
64
65
  this.selectedIndex = this.selectedIndex === this.messages.length - 1 ? 0 : this.selectedIndex + 1;
65
66
  }
66
67
  // Enter - select message and branch
67
- else if (isEnter(keyData)) {
68
+ else if (kb.matches(keyData, "selectConfirm")) {
68
69
  const selected = this.messages[this.selectedIndex];
69
70
  if (selected && this.onSelect) {
70
71
  this.onSelect(selected.id);
71
72
  }
72
73
  }
73
74
  // Escape - cancel
74
- else if (isEscape(keyData)) {
75
- if (this.onCancel) {
76
- this.onCancel();
77
- }
78
- }
79
- // Ctrl+C - cancel
80
- else if (isCtrlC(keyData)) {
75
+ else if (kb.matches(keyData, "selectCancel")) {
81
76
  if (this.onCancel) {
82
77
  this.onCancel();
83
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"user-message-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/user-message-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\tisArrowDown,\n\tisArrowUp,\n\tisCtrlC,\n\tisEnter,\n\tisEscape,\n\tSpacer,\n\tText,\n\ttruncateToWidth,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\ninterface UserMessageItem {\n\tid: string; // Entry ID in the session\n\ttext: string; // The message text\n\ttimestamp?: string; // Optional timestamp if available\n}\n\n/**\n * Custom user message list component with selection\n */\nclass UserMessageList implements Component {\n\tprivate messages: UserMessageItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tprivate maxVisible: number = 10; // Max messages visible\n\n\tconstructor(messages: UserMessageItem[]) {\n\t\t// Store messages in chronological order (oldest to newest)\n\t\tthis.messages = messages;\n\t\t// Start with the last (most recent) message selected\n\t\tthis.selectedIndex = Math.max(0, messages.length - 1);\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.messages.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No user messages found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.messages.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.messages.length);\n\n\t\t// Render visible messages (2 lines per message + blank line)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst message = this.messages[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Normalize message to single line\n\t\t\tconst normalizedMessage = message.text.replace(/\\n/g, \" \").trim();\n\n\t\t\t// First line: cursor + message\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst maxMsgWidth = width - 2; // Account for cursor (2 chars)\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, maxMsgWidth);\n\t\t\tconst messageLine = cursor + (isSelected ? theme.bold(truncatedMsg) : truncatedMsg);\n\n\t\t\tlines.push(messageLine);\n\n\t\t\t// Second line: metadata (position in history)\n\t\t\tconst position = i + 1;\n\t\t\tconst metadata = ` Message ${position} of ${this.messages.length}`;\n\t\t\tconst metadataLine = theme.fg(\"muted\", metadata);\n\t\t\tlines.push(metadataLine);\n\t\t\tlines.push(\"\"); // Blank line between messages\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.messages.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.messages.length})`);\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\t// Up arrow - go to previous (older) message, wrap to bottom when at top\n\t\tif (isArrowUp(keyData)) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.messages.length - 1 : this.selectedIndex - 1;\n\t\t}\n\t\t// Down arrow - go to next (newer) message, wrap to top when at bottom\n\t\telse if (isArrowDown(keyData)) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.messages.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t}\n\t\t// Enter - select message and branch\n\t\telse if (isEnter(keyData)) {\n\t\t\tconst selected = this.messages[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.id);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (isEscape(keyData)) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t\t// Ctrl+C - cancel\n\t\telse if (isCtrlC(keyData)) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a user message selector for branching\n */\nexport class UserMessageSelectorComponent extends Container {\n\tprivate messageList: UserMessageList;\n\n\tconstructor(messages: UserMessageItem[], onSelect: (entryId: string) => void, onCancel: () => void) {\n\t\tsuper();\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(\"Branch from Message\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Select a message to create a new branch from that point\"), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create message list\n\t\tthis.messageList = new UserMessageList(messages);\n\t\tthis.messageList.onSelect = onSelect;\n\t\tthis.messageList.onCancel = onCancel;\n\n\t\tthis.addChild(this.messageList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Auto-cancel if no messages\n\t\tif (messages.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tgetMessageList(): UserMessageList {\n\t\treturn this.messageList;\n\t}\n}\n"]}
1
+ {"version":3,"file":"user-message-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/user-message-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Container, getEditorKeybindings, Spacer, Text, truncateToWidth } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\ninterface UserMessageItem {\n\tid: string; // Entry ID in the session\n\ttext: string; // The message text\n\ttimestamp?: string; // Optional timestamp if available\n}\n\n/**\n * Custom user message list component with selection\n */\nclass UserMessageList implements Component {\n\tprivate messages: UserMessageItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tprivate maxVisible: number = 10; // Max messages visible\n\n\tconstructor(messages: UserMessageItem[]) {\n\t\t// Store messages in chronological order (oldest to newest)\n\t\tthis.messages = messages;\n\t\t// Start with the last (most recent) message selected\n\t\tthis.selectedIndex = Math.max(0, messages.length - 1);\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.messages.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No user messages found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.messages.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.messages.length);\n\n\t\t// Render visible messages (2 lines per message + blank line)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst message = this.messages[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Normalize message to single line\n\t\t\tconst normalizedMessage = message.text.replace(/\\n/g, \" \").trim();\n\n\t\t\t// First line: cursor + message\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst maxMsgWidth = width - 2; // Account for cursor (2 chars)\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, maxMsgWidth);\n\t\t\tconst messageLine = cursor + (isSelected ? theme.bold(truncatedMsg) : truncatedMsg);\n\n\t\t\tlines.push(messageLine);\n\n\t\t\t// Second line: metadata (position in history)\n\t\t\tconst position = i + 1;\n\t\t\tconst metadata = ` Message ${position} of ${this.messages.length}`;\n\t\t\tconst metadataLine = theme.fg(\"muted\", metadata);\n\t\t\tlines.push(metadataLine);\n\t\t\tlines.push(\"\"); // Blank line between messages\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.messages.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.messages.length})`);\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\t// Up arrow - go to previous (older) message, wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.messages.length - 1 : this.selectedIndex - 1;\n\t\t}\n\t\t// Down arrow - go to next (newer) message, wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.messages.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t}\n\t\t// Enter - select message and branch\n\t\telse if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.messages[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.id);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a user message selector for branching\n */\nexport class UserMessageSelectorComponent extends Container {\n\tprivate messageList: UserMessageList;\n\n\tconstructor(messages: UserMessageItem[], onSelect: (entryId: string) => void, onCancel: () => void) {\n\t\tsuper();\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(\"Branch from Message\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Select a message to create a new branch from that point\"), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create message list\n\t\tthis.messageList = new UserMessageList(messages);\n\t\tthis.messageList.onSelect = onSelect;\n\t\tthis.messageList.onCancel = onCancel;\n\n\t\tthis.addChild(this.messageList);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Auto-cancel if no messages\n\t\tif (messages.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tgetMessageList(): UserMessageList {\n\t\treturn this.messageList;\n\t}\n}\n"]}
@@ -15,6 +15,7 @@ export declare class InteractiveMode {
15
15
  private editor;
16
16
  private editorContainer;
17
17
  private footer;
18
+ private keybindings;
18
19
  private version;
19
20
  private isInitialized;
20
21
  private onInputCallback?;
@@ -40,6 +41,8 @@ export declare class InteractiveMode {
40
41
  private hookSelector;
41
42
  private hookInput;
42
43
  private hookEditor;
44
+ private hookWidgets;
45
+ private widgetContainer;
43
46
  private customTools;
44
47
  private get agent();
45
48
  private get sessionManager();
@@ -52,10 +55,27 @@ export declare class InteractiveMode {
52
55
  * Show a tool error in the chat.
53
56
  */
54
57
  private showToolError;
58
+ /**
59
+ * Set up keyboard shortcuts registered by hooks.
60
+ */
61
+ private setupHookShortcuts;
55
62
  /**
56
63
  * Set hook status text in the footer.
57
64
  */
58
65
  private setHookStatus;
66
+ /**
67
+ * Set a hook widget (string array or custom component).
68
+ */
69
+ private setHookWidget;
70
+ private static readonly MAX_WIDGET_LINES;
71
+ /**
72
+ * Render all hook widgets to the widget container.
73
+ */
74
+ private renderWidgets;
75
+ /**
76
+ * Create the HookUIContext for hooks and tools.
77
+ */
78
+ private createHookUIContext;
59
79
  /**
60
80
  * Show a selector for hooks.
61
81
  */
@@ -95,6 +115,7 @@ export declare class InteractiveMode {
95
115
  * If streaming, queue the message. Otherwise, start a new agent loop.
96
116
  */
97
117
  private setupKeyHandlers;
118
+ private handleClipboardImagePaste;
98
119
  private setupEditorSubmitHandler;
99
120
  private subscribeToAgent;
100
121
  private handleEvent;
@@ -122,7 +143,7 @@ export declare class InteractiveMode {
122
143
  private handleCtrlD;
123
144
  private shutdown;
124
145
  private handleCtrlZ;
125
- private handleAltEnter;
146
+ private handleFollowUp;
126
147
  private updateEditorBorderColor;
127
148
  private cycleThinkingLevel;
128
149
  private cycleModel;
@@ -153,6 +174,18 @@ export declare class InteractiveMode {
153
174
  private handleCopyCommand;
154
175
  private handleSessionCommand;
155
176
  private handleChangelogCommand;
177
+ /**
178
+ * Format keybindings for display (e.g., "ctrl+c" -> "Ctrl+C").
179
+ */
180
+ private formatKeyDisplay;
181
+ /**
182
+ * Get display string for an app keybinding action.
183
+ */
184
+ private getAppKeyDisplay;
185
+ /**
186
+ * Get display string for an editor keybinding action.
187
+ */
188
+ private getEditorKeyDisplay;
156
189
  private handleHotkeysCommand;
157
190
  private handleClearCommand;
158
191
  private handleDebugCommand;