gloomberb 0.8.2 → 0.9.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 (85) hide show
  1. package/README.md +9 -7
  2. package/package.json +14 -13
  3. package/patches/{@opentui%2Fcore@0.1.90.patch → @opentui%2Fcore@0.3.2.patch} +175 -90
  4. package/src/app/global-shortcuts.test.tsx +54 -4
  5. package/src/app/global-shortcuts.ts +11 -5
  6. package/src/components/command-bar/action-runtime.ts +1 -0
  7. package/src/components/command-bar/layout-items/current-layout.test.ts +115 -0
  8. package/src/components/command-bar/layout-items/current-layout.ts +33 -0
  9. package/src/components/command-bar/list/view.tsx +3 -1
  10. package/src/components/command-bar/pane-templates/items.ts +4 -1
  11. package/src/components/command-bar/panel/body.tsx +1 -0
  12. package/src/components/command-bar/routes/root/results.ts +0 -30
  13. package/src/components/command-bar/routes/root/runtime.ts +2 -20
  14. package/src/components/command-bar/routes/ticker-search/actions.ts +42 -4
  15. package/src/components/command-bar/routes/ticker-search/route.ts +4 -1
  16. package/src/components/command-bar/routing/launch-request.ts +14 -7
  17. package/src/components/command-bar/routing/list-state.ts +1 -3
  18. package/src/components/command-bar/surface/index.test.tsx +76 -4
  19. package/src/components/command-bar/surface/index.tsx +0 -2
  20. package/src/components/command-bar/surface/pane.test.tsx +1 -0
  21. package/src/components/command-bar/surface/test-harness.tsx +1 -0
  22. package/src/components/command-bar/view-model.ts +3 -3
  23. package/src/components/layout/shell/index.test.tsx +120 -2
  24. package/src/components/layout/shell/index.tsx +2 -0
  25. package/src/components/layout/shell/menu.ts +1 -7
  26. package/src/components/layout/shell/pane/actions.ts +8 -0
  27. package/src/components/layout/shell/pane/management-shortcuts.ts +12 -2
  28. package/src/components/layout/shell/shortcuts.ts +14 -6
  29. package/src/components/layout/shell/window-mode/index.ts +8 -3
  30. package/src/components/layout/window-edit/presentation.ts +4 -4
  31. package/src/components/onboarding/onboarding-steps.tsx +3 -2
  32. package/src/components/ticker/list-table-view.tsx +6 -0
  33. package/src/components/ui/remote-image.tsx +1 -14
  34. package/src/components/ui/tabs.tsx +95 -80
  35. package/src/components/ui/ui.test.tsx +54 -0
  36. package/src/core/state/app/types.ts +12 -4
  37. package/src/plugins/builtin/chat/content/channel-navigation.ts +44 -11
  38. package/src/plugins/builtin/chat/content/index.tsx +5 -4
  39. package/src/plugins/builtin/chat/content/shortcuts.ts +7 -0
  40. package/src/plugins/builtin/chat/sidebar.test.tsx +71 -0
  41. package/src/plugins/builtin/help/components.tsx +1 -1
  42. package/src/plugins/builtin/help/index.test.tsx +105 -30
  43. package/src/plugins/builtin/help/index.tsx +324 -168
  44. package/src/plugins/builtin/help/shortcut-model.ts +5 -6
  45. package/src/plugins/builtin/portfolio-list/column-values.ts +304 -1
  46. package/src/plugins/builtin/portfolio-list/metrics.test.ts +93 -0
  47. package/src/plugins/builtin/portfolio-list/pane/data.ts +7 -0
  48. package/src/plugins/builtin/portfolio-list/pane/index.tsx +25 -2
  49. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +281 -0
  50. package/src/plugins/builtin/portfolio-list/settings.ts +27 -0
  51. package/src/plugins/builtin/portfolio-list/table.tsx +4 -2
  52. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +10 -68
  53. package/src/plugins/builtin/substack/api/auth.ts +185 -0
  54. package/src/plugins/builtin/substack/api/cache.ts +78 -0
  55. package/src/plugins/builtin/substack/api/loaders.ts +146 -0
  56. package/src/plugins/builtin/substack/api/store.ts +186 -0
  57. package/src/plugins/builtin/substack/api/types.ts +40 -0
  58. package/src/plugins/builtin/substack/api.test.ts +298 -0
  59. package/src/plugins/builtin/substack/article-detail.tsx +359 -0
  60. package/src/plugins/builtin/substack/article-stack.tsx +129 -0
  61. package/src/plugins/builtin/substack/content.ts +478 -0
  62. package/src/plugins/builtin/substack/feed-tabs.tsx +41 -0
  63. package/src/plugins/builtin/substack/index.tsx +49 -0
  64. package/src/plugins/builtin/substack/login-view.tsx +126 -0
  65. package/src/plugins/builtin/substack/model.test.ts +155 -0
  66. package/src/plugins/builtin/substack/normalize.ts +343 -0
  67. package/src/plugins/builtin/substack/pane-footer.ts +71 -0
  68. package/src/plugins/builtin/substack/pane-state.ts +170 -0
  69. package/src/plugins/builtin/substack/pane.tsx +500 -0
  70. package/src/plugins/builtin/substack/table.ts +111 -0
  71. package/src/plugins/builtin/substack/types.ts +71 -0
  72. package/src/plugins/builtin/substack/utils.ts +131 -0
  73. package/src/plugins/builtin/ticker-detail/index.test.tsx +16 -2
  74. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  75. package/src/plugins/builtin/ticker-detail/pane.tsx +13 -1
  76. package/src/plugins/catalog-ui.ts +2 -0
  77. package/src/plugins/pane-manager/layout-state.ts +11 -0
  78. package/src/plugins/pane-manager.ts +1 -1
  79. package/src/react/use-throttled-commit-value.ts +116 -0
  80. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +42 -0
  81. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -0
  82. package/src/renderers/electrobun/view/http-fetch.ts +10 -3
  83. package/src/state/app/context/index.test.tsx +16 -0
  84. package/src/tickers/search/index.ts +1 -0
  85. package/src/version.ts +1 -1
@@ -1,6 +1,8 @@
1
1
  import { Box, ScrollBox, Text, useUiHost } from "../../../ui";
2
2
  import { TextAttributes } from "../../../ui";
3
3
  import { useState } from "react";
4
+ import { Tabs } from "../../../components";
5
+ import { ExternalLinkText } from "../../../components/ui";
4
6
  import type { GloomPlugin, PaneProps } from "../../../types/plugin";
5
7
  import { colors } from "../../../theme/colors";
6
8
  import { getSharedRegistry } from "../../registry";
@@ -19,9 +21,20 @@ import {
19
21
  resolveWindowTemplates,
20
22
  } from "./shortcut-model";
21
23
 
22
- export function HelpPane({ width, height }: PaneProps) {
24
+ const HELP_TABS = [
25
+ { label: "Basics", value: "basics" },
26
+ { label: "Functions", value: "functions" },
27
+ { label: "Shortcuts", value: "shortcuts" },
28
+ { label: "Issues", value: "issues" },
29
+ ] as const;
30
+
31
+ type HelpTabId = typeof HELP_TABS[number]["value"];
32
+ const GLOOMBERB_ISSUES_URL = "https://github.com/vincelwt/gloomberb/issues";
33
+
34
+ export function HelpPane({ focused, width, height }: PaneProps) {
23
35
  const registry = getSharedRegistry();
24
36
  const { openCommandBar, showPane } = usePluginAppActions();
37
+ const [activeTabId, setActiveTabId] = useState<HelpTabId>("basics");
25
38
  const [hoveredAction, setHoveredAction] = useState<string | null>(null);
26
39
  const commandShortcuts = resolveCommandShortcuts(registry);
27
40
  const pluginShortcuts = resolvePluginShortcuts(registry);
@@ -32,14 +45,15 @@ export function HelpPane({ width, height }: PaneProps) {
32
45
  const shortcutDisplayMode = getShortcutDisplayMode(uiHost.kind);
33
46
  const platformShortcut = (keys: string | readonly string[]) => formatPrimaryShortcut(keys, shortcutPlatform, shortcutDisplayMode);
34
47
  const commandBarBadges = shortcutDisplayMode === "terminal"
35
- ? ["Ctrl+P", "`"]
36
- : ["Ctrl+P", platformShortcut("K"), "`"];
48
+ ? ["Ctrl+P"]
49
+ : ["Ctrl+P", platformShortcut("K")];
37
50
  const copyBadges = shortcutDisplayMode === "terminal"
38
51
  ? ["Ctrl+Shift+C"]
39
52
  : [platformShortcut("C")];
40
53
  const pasteBadges = shortcutDisplayMode === "terminal"
41
54
  ? ["Ctrl+Shift+V"]
42
55
  : [platformShortcut("V")];
56
+ const contentHeight = Math.max(0, height - 1);
43
57
 
44
58
  const openDebugLog = () => {
45
59
  showPane("debug");
@@ -53,94 +67,23 @@ export function HelpPane({ width, height }: PaneProps) {
53
67
  openCommandBar("PL ");
54
68
  };
55
69
 
56
- return (
57
- <Box flexDirection="column" width={width} height={height}>
58
- <ScrollBox flexGrow={1} scrollY>
59
- <Box flexDirection="column" padding={1}>
60
- <Box flexDirection="column" gap={1}>
61
- <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>How To Use Gloomberb</Text>
62
- <Box flexDirection="column">
63
- <Text fg={colors.textDim}>Gloomberb is command-bar first.</Text>
64
- <Text fg={colors.textDim}>Use the keyboard for speed, and the mouse for windows.</Text>
70
+ const renderContent = () => {
71
+ switch (activeTabId) {
72
+ case "functions":
73
+ return (
74
+ <>
75
+ <Box flexDirection="row" gap={1}>
76
+ <ActionButton
77
+ id="plugins"
78
+ label="Manage Plugins"
79
+ hovered={hoveredAction === "plugins"}
80
+ onHover={setHoveredAction}
81
+ onPress={openPluginManager}
82
+ />
65
83
  </Box>
66
- </Box>
67
-
68
- <Box flexDirection="row" gap={1}>
69
- <ActionButton
70
- id="debug"
71
- label="Open Debug Log"
72
- hovered={hoveredAction === "debug"}
73
- onHover={setHoveredAction}
74
- onPress={openDebugLog}
75
- />
76
- <ActionButton
77
- id="layout"
78
- label="Layout Actions"
79
- hovered={hoveredAction === "layout"}
80
- onHover={setHoveredAction}
81
- onPress={openLayoutActions}
82
- />
83
- <ActionButton
84
- id="plugins"
85
- label="Manage Plugins"
86
- hovered={hoveredAction === "plugins"}
87
- onHover={setHoveredAction}
88
- onPress={openPluginManager}
89
- />
90
- </Box>
91
-
92
- <HelpSection title="Command Bar">
93
- <ShortcutRow
94
- badges={commandBarBadges}
95
- description="Open or toggle the command bar from anywhere."
96
- />
97
- <ShortcutRow
98
- badges={["<ticker>"]}
99
- description="Search for a ticker or open the best matching security."
100
- />
101
- <ShortcutRow
102
- badges={["Up/Down", "Ctrl+P/N"]}
103
- description="Move through command bar results."
104
- />
105
- <ShortcutRow
106
- badges={["Enter", "Shift+Enter"]}
107
- description="Run the selected result or its secondary action."
108
- />
109
- <ShortcutRow
110
- badges={["Tab"]}
111
- description="Accept an inferred shortcut argument from the focused ticker."
112
- />
113
- <ShortcutRow
114
- badges={["Esc", "`"]}
115
- description="Close the command bar."
116
- />
117
- </HelpSection>
118
-
119
- <HelpSection title="Command Prefixes">
120
- {groupShortcutEntries(commandShortcuts).map((group) => (
121
- <ShortcutGroup
122
- key={group.title}
123
- title={group.title}
124
- entries={group.entries}
125
- />
126
- ))}
127
- </HelpSection>
128
84
 
129
- <HelpSection title="Window Templates">
130
- {windowTemplates.length > 0 ? groupShortcutEntries(windowTemplates).map((group) => (
131
- <ShortcutGroup
132
- key={group.title}
133
- title={group.title}
134
- entries={group.entries}
135
- />
136
- )) : (
137
- <Text fg={colors.textDim}>No shortcut window templates are currently registered.</Text>
138
- )}
139
- </HelpSection>
140
-
141
- {pluginShortcuts.length > 0 && (
142
- <HelpSection title="Plugin Shortcuts">
143
- {groupShortcutEntries(pluginShortcuts).map((group) => (
85
+ <HelpSection title="Command Prefixes">
86
+ {groupShortcutEntries(commandShortcuts).map((group) => (
144
87
  <ShortcutGroup
145
88
  key={group.title}
146
89
  title={group.title}
@@ -148,94 +91,307 @@ export function HelpPane({ width, height }: PaneProps) {
148
91
  />
149
92
  ))}
150
93
  </HelpSection>
151
- )}
152
94
 
153
- <HelpSection title="Global Keys">
154
- <ShortcutRow
155
- badges={["Tab", "Shift+Tab"]}
156
- description="Move focus between panes and floating windows."
157
- />
158
- <ShortcutRow
159
- badges={["Ctrl+1-9"]}
160
- description="Switch saved layouts by number."
161
- />
162
- <ShortcutRow
163
- badges={["r", "Shift+R"]}
164
- description="Refresh the focused ticker or refresh everything."
165
- />
166
- <ShortcutRow
167
- badges={["q"]}
168
- description="Quit the terminal app."
169
- />
170
- <ShortcutRow
171
- badges={copyBadges}
172
- description="Copy the active terminal selection."
173
- />
174
- <ShortcutRow
175
- badges={pasteBadges}
176
- description="Paste clipboard text into the active input."
177
- />
178
- <ShortcutRow
179
- badges={["u"]}
180
- description="Install an available app update when one is shown."
181
- />
182
- </HelpSection>
95
+ <HelpSection title="Window Templates">
96
+ {windowTemplates.length > 0 ? groupShortcutEntries(windowTemplates).map((group) => (
97
+ <ShortcutGroup
98
+ key={group.title}
99
+ title={group.title}
100
+ entries={group.entries}
101
+ />
102
+ )) : (
103
+ <Text fg={colors.textDim}>No shortcut window templates are currently registered.</Text>
104
+ )}
105
+ </HelpSection>
183
106
 
184
- <HelpSection title="Pane Management">
185
- <ShortcutRow
186
- badges={[platformShortcut("W")]}
187
- description="Close the focused pane, docked or floating."
188
- />
189
- <ShortcutRow
190
- badges={[platformShortcut(",")]}
191
- description="Edit settings for the focused pane."
192
- />
193
- <ShortcutRow
194
- badges={[platformShortcut(["Shift", "D"])]}
195
- description="Dock or float the focused pane."
196
- />
197
- {isDesktopWeb && (
198
- <ShortcutRow
199
- badges={[platformShortcut(["Shift", "O"])]}
200
- description="Pop the focused pane out to a desktop window."
201
- />
107
+ {pluginShortcuts.length > 0 && (
108
+ <HelpSection title="Plugin Shortcuts">
109
+ {groupShortcutEntries(pluginShortcuts).map((group) => (
110
+ <ShortcutGroup
111
+ key={group.title}
112
+ title={group.title}
113
+ entries={group.entries}
114
+ />
115
+ ))}
116
+ </HelpSection>
202
117
  )}
203
- {isDesktopWeb && (
118
+ </>
119
+ );
120
+
121
+ case "shortcuts":
122
+ return (
123
+ <>
124
+ <HelpSection title="Navigation">
204
125
  <ShortcutRow
205
- badges={[platformShortcut(["Shift", "C"])]}
206
- description="Copy a screenshot of the focused pane."
126
+ badges={["Up/Down", "j/k"]}
127
+ description="Move through focused table and list rows."
207
128
  />
208
- )}
209
- <ShortcutRow
210
- badges={[platformShortcut(["Shift", "L"])]}
211
- description="Open layout actions."
212
- />
213
- <ShortcutRow
214
- badges={[platformShortcut(["Shift", "G"])]}
215
- description="Gridlock all windows."
216
- />
217
- <ShortcutRow
218
- badges={["Esc"]}
219
- description="Cancel an active pane drag."
220
- />
221
- </HelpSection>
129
+ <ShortcutRow
130
+ badges={["Enter"]}
131
+ description="Open or activate the selected row."
132
+ />
133
+ <ShortcutRow
134
+ badges={["Left/Right", "h/l"]}
135
+ description="Switch tabs when a tab bar is focused."
136
+ />
137
+ <ShortcutRow
138
+ badges={["Esc", "Backspace"]}
139
+ description="Go back from detail views that support back navigation."
140
+ />
141
+ </HelpSection>
142
+
143
+ <HelpSection title="Scrolling">
144
+ <ShortcutRow
145
+ badges={["PageUp/PageDown"]}
146
+ description="Scroll focused pane content by page."
147
+ />
148
+ <ShortcutRow
149
+ badges={["Home/End"]}
150
+ description="Scroll focused pane content to the start or end."
151
+ />
152
+ </HelpSection>
222
153
 
223
- <HelpSection title="Layout System">
224
- <Text fg={colors.text}>Docked panes stay in the saved layout.</Text>
225
- <Box flexDirection="column">
226
- <Text fg={colors.text}>Floating panes can be dragged by the title bar</Text>
227
- <Text fg={colors.text}>and resized from the lower-right corner.</Text>
154
+ <HelpSection title="Global Keys">
155
+ <ShortcutRow
156
+ badges={["Tab", "Shift+Tab"]}
157
+ description="Move focus between panes and floating windows."
158
+ />
159
+ <ShortcutRow
160
+ badges={["Ctrl+1-9"]}
161
+ description="Switch saved layouts by number."
162
+ />
163
+ <ShortcutRow
164
+ badges={["r", "Shift+R"]}
165
+ description="Refresh the focused ticker or refresh everything."
166
+ />
167
+ <ShortcutRow
168
+ badges={["q"]}
169
+ description="Quit the terminal app."
170
+ />
171
+ <ShortcutRow
172
+ badges={copyBadges}
173
+ description="Copy the active terminal selection."
174
+ />
175
+ <ShortcutRow
176
+ badges={pasteBadges}
177
+ description="Paste clipboard text into the active input."
178
+ />
179
+ <ShortcutRow
180
+ badges={["u"]}
181
+ description="Install an available app update when one is shown."
182
+ />
183
+ </HelpSection>
184
+
185
+ <HelpSection title="Pane Management">
186
+ <ShortcutRow
187
+ badges={[platformShortcut("W")]}
188
+ description="Close the focused pane, docked or floating."
189
+ />
190
+ <ShortcutRow
191
+ badges={[platformShortcut(["Alt", "W"])]}
192
+ description="Close all floating panes."
193
+ />
194
+ <ShortcutRow
195
+ badges={[platformShortcut(",")]}
196
+ description="Edit settings for the focused pane."
197
+ />
198
+ <ShortcutRow
199
+ badges={[platformShortcut(["Shift", "D"])]}
200
+ description="Dock or float the focused pane."
201
+ />
202
+ {isDesktopWeb && (
203
+ <ShortcutRow
204
+ badges={[platformShortcut(["Shift", "O"])]}
205
+ description="Pop the focused pane out to a desktop window."
206
+ />
207
+ )}
208
+ {isDesktopWeb && (
209
+ <ShortcutRow
210
+ badges={[platformShortcut(["Shift", "C"])]}
211
+ description="Copy a screenshot of the focused pane."
212
+ />
213
+ )}
214
+ <ShortcutRow
215
+ badges={[platformShortcut(["Shift", "L"])]}
216
+ description="Open layout actions."
217
+ />
218
+ <ShortcutRow
219
+ badges={[platformShortcut(["Shift", "G"])]}
220
+ description="Gridlock all windows."
221
+ />
222
+ <ShortcutRow
223
+ badges={[platformShortcut(["Shift", "M"])]}
224
+ description="Enter window move mode."
225
+ />
226
+ <ShortcutRow
227
+ badges={[platformShortcut(["Shift", "R"])]}
228
+ description="Enter window resize mode."
229
+ />
230
+ <ShortcutRow
231
+ badges={["Esc"]}
232
+ description="Cancel an active pane drag."
233
+ />
234
+ <ShortcutRow
235
+ badges={["Esc", "Esc"]}
236
+ description="Close the focused pane when nothing is being dragged."
237
+ />
238
+ </HelpSection>
239
+
240
+ <HelpSection title="Window Mode">
241
+ <ShortcutRow
242
+ badges={["m", "r"]}
243
+ description="Switch between move and resize."
244
+ />
245
+ <ShortcutRow
246
+ badges={["d"]}
247
+ description="Dock or float the selected window."
248
+ />
249
+ <ShortcutRow
250
+ badges={["Arrows", "h/j/k/l"]}
251
+ description="Move, resize, or choose a dock target."
252
+ />
253
+ <ShortcutRow
254
+ badges={["Shift"]}
255
+ description="Use larger move and resize steps with direction keys."
256
+ />
257
+ <ShortcutRow
258
+ badges={["Tab", "w"]}
259
+ description="Cycle windows or resize handles."
260
+ />
261
+ <ShortcutRow
262
+ badges={["Enter", "Esc"]}
263
+ description="Commit pending changes or exit window mode."
264
+ />
265
+ </HelpSection>
266
+ </>
267
+ );
268
+
269
+ case "issues":
270
+ return (
271
+ <>
272
+ <Box flexDirection="row" gap={1}>
273
+ <ActionButton
274
+ id="debug"
275
+ label="Open Debug Log"
276
+ hovered={hoveredAction === "debug"}
277
+ onHover={setHoveredAction}
278
+ onPress={openDebugLog}
279
+ />
280
+ <ExternalLinkText
281
+ url={GLOOMBERB_ISSUES_URL}
282
+ label="GitHub Issues"
283
+ />
228
284
  </Box>
229
- <Text fg={colors.text}>Use Layout Actions for split, move, duplicate, undo, redo, and layout presets.</Text>
230
- </HelpSection>
231
285
 
232
- <HelpSection title="If There Is A Bug">
233
- <Text fg={colors.text}>Open Debug Log, then run Export Debug Log from the command bar.</Text>
234
- <Box flexDirection="column">
235
- <Text fg={colors.text}>The file lands in ~/Downloads. Include steps, ticker or layout, plugin,</Text>
236
- <Text fg={colors.text}>and a screenshot if it is visual.</Text>
286
+ <HelpSection title="If There Is A Bug">
287
+ <Text fg={colors.text} wrapText>Open Debug Log, then run Export Debug Log from the command bar.</Text>
288
+ <Text fg={colors.text} wrapText>The file lands in ~/Downloads. Include steps, ticker or layout, plugin, and a screenshot if it is visual.</Text>
289
+ </HelpSection>
290
+ </>
291
+ );
292
+
293
+ case "basics":
294
+ default:
295
+ return (
296
+ <>
297
+ <Box flexDirection="column" gap={1}>
298
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>How To Use Gloomberb</Text>
299
+ <Box flexDirection="column">
300
+ <Text fg={colors.textDim}>Gloomberb is command-bar first.</Text>
301
+ <Text fg={colors.textDim}>Use the keyboard for speed, and the mouse for windows.</Text>
302
+ </Box>
303
+ </Box>
304
+
305
+ <Box flexDirection="row" gap={1}>
306
+ <ActionButton
307
+ id="layout"
308
+ label="Layout Actions"
309
+ hovered={hoveredAction === "layout"}
310
+ onHover={setHoveredAction}
311
+ onPress={openLayoutActions}
312
+ />
237
313
  </Box>
238
- </HelpSection>
314
+
315
+ <HelpSection title="Command Bar">
316
+ <ShortcutRow
317
+ badges={commandBarBadges}
318
+ description="Open command mode for actions, pane commands, and typed prefixes."
319
+ />
320
+ <ShortcutRow
321
+ badges={["`"]}
322
+ description="Open ticker search directly."
323
+ />
324
+ <ShortcutRow
325
+ badges={["DES", "<ticker>"]}
326
+ description="Open security details for a specific ticker."
327
+ />
328
+ <ShortcutRow
329
+ badges={["Up/Down", "Ctrl+P/N"]}
330
+ description="Move through command bar results."
331
+ />
332
+ <ShortcutRow
333
+ badges={["Enter", "Shift+Enter"]}
334
+ description="Run the selected result or its secondary action."
335
+ />
336
+ <ShortcutRow
337
+ badges={["Tab"]}
338
+ description="Accept a suggested command argument when one is available."
339
+ />
340
+ <ShortcutRow
341
+ badges={["Esc", "`"]}
342
+ description="Close the command bar."
343
+ />
344
+ <ShortcutRow
345
+ badges={["Ctrl+U"]}
346
+ description="Clear command text."
347
+ />
348
+ <ShortcutRow
349
+ badges={["Ctrl+W"]}
350
+ description="Delete the previous word in command text."
351
+ />
352
+ <ShortcutRow
353
+ badges={["Backspace"]}
354
+ description="Go back from a nested command screen when the query is empty."
355
+ />
356
+ <ShortcutRow
357
+ badges={["Space"]}
358
+ description="Toggle command-bar plugin rows, toggles, and multi-select choices."
359
+ />
360
+ <ShortcutRow
361
+ badges={["[", "]"]}
362
+ description="Reorder ordered multi-select choices."
363
+ />
364
+ <ShortcutRow
365
+ badges={["Ctrl+S"]}
366
+ description="Submit multiline command forms."
367
+ />
368
+ </HelpSection>
369
+
370
+ <HelpSection title="Layout Basics">
371
+ <Text fg={colors.text}>Docked panes stay in the saved layout.</Text>
372
+ <Text fg={colors.text} wrapText>Floating panes can be dragged by the title bar and resized from the lower-right corner.</Text>
373
+ <Text fg={colors.text} wrapText>Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.</Text>
374
+ </HelpSection>
375
+ </>
376
+ );
377
+ }
378
+ };
379
+
380
+ return (
381
+ <Box flexDirection="column" width={width} height={height}>
382
+ <Box width={width} height={1} flexShrink={0}>
383
+ <Tabs
384
+ tabs={HELP_TABS}
385
+ activeValue={activeTabId}
386
+ onSelect={(value) => setActiveTabId(value as HelpTabId)}
387
+ focused={focused}
388
+ compact
389
+ scrollable={false}
390
+ />
391
+ </Box>
392
+ <ScrollBox key={activeTabId} width={width} height={contentHeight} scrollY>
393
+ <Box flexDirection="column" padding={1}>
394
+ {renderContent()}
239
395
  </Box>
240
396
  </ScrollBox>
241
397
  </Box>
@@ -27,7 +27,7 @@ export function resolveWindowTemplates(registry: SharedRegistry): HelpShortcutEn
27
27
  shortcut.prefix,
28
28
  shortcut.argPlaceholder ? `<${shortcut.argPlaceholder}>` : null,
29
29
  ].filter((value): value is string => !!value),
30
- description: pluginName ? `${template.label} (${pluginName})` : template.label,
30
+ description: template.label,
31
31
  category: pluginName ?? "Core Panes",
32
32
  };
33
33
  })
@@ -46,9 +46,8 @@ function formatPlaceholder(value: string | undefined): string | null {
46
46
  return value ? `<${value}>` : null;
47
47
  }
48
48
 
49
- function withPluginName(description: string | undefined, pluginName: string | null | undefined): string {
50
- const base = description?.trim() || "Run command";
51
- return pluginName ? `${base} (${pluginName})` : base;
49
+ function formatShortcutDescription(description: string | undefined): string {
50
+ return description?.trim() || "Run command";
52
51
  }
53
52
 
54
53
  function sortShortcutEntries(left: HelpShortcutEntry, right: HelpShortcutEntry): number {
@@ -90,7 +89,7 @@ export function resolveCommandShortcuts(registry: SharedRegistry): HelpShortcutE
90
89
  command.shortcut!.toUpperCase(),
91
90
  formatPlaceholder(command.shortcutArg?.placeholder),
92
91
  ].filter((value): value is string => !!value),
93
- description: withPluginName(command.label, pluginName),
92
+ description: formatShortcutDescription(command.label),
94
93
  category: pluginName ?? command.category,
95
94
  };
96
95
  })
@@ -126,7 +125,7 @@ export function resolvePluginShortcuts(registry: SharedRegistry): HelpShortcutEn
126
125
  return {
127
126
  id: `plugin-shortcut:${shortcut.id}`,
128
127
  badges: [formatShortcutKey(shortcut)],
129
- description: withPluginName(shortcut.description, pluginName),
128
+ description: formatShortcutDescription(shortcut.description),
130
129
  category: pluginName ?? "Plugin Shortcuts",
131
130
  };
132
131
  })