feat: open session help from tray

This commit is contained in:
2026-04-25 21:32:10 -07:00
parent e51bb74e1b
commit a75c83e25e
10 changed files with 85 additions and 24 deletions

View File

@@ -0,0 +1,57 @@
---
id: TASK-303
title: Update tray menu help action
status: Done
assignee:
- Codex
created_date: '2026-04-26 03:54'
updated_date: '2026-04-26 04:12'
labels:
- tray
- overlay
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the tray menu's direct visible-overlay open action with an action that opens the existing in-session help modal. The tray should no longer expose an "Open Overlay" menu item; users should be able to open help from the tray instead.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Tray menu no longer includes an "Open Overlay" option.
- [x] #2 Tray menu includes an option to open the session help modal.
- [x] #3 Selecting the new tray help option initializes overlay runtime if needed and invokes the existing session help modal path.
- [x] #4 Focused regression tests cover the menu label and action wiring.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add focused regression coverage for tray menu template labels and main-process action wiring: assert Open Overlay is absent, Open Help is present, and clicking help initializes overlay runtime if needed before opening the existing session help modal path.
2. Update tray runtime action types/template to replace openOverlay with openSessionHelp.
3. Update tray main action builder dependencies to call the existing openSessionHelpModal function after overlay runtime initialization.
4. Run targeted tray tests, then broader relevant fast tests if needed.
5. Check acceptance criteria and finalize backlog notes.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented tray menu replacement via existing session help overlay path. Verification passed: targeted tray tests (`bun test src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts`), SubMiner verifier lanes `runtime-compat` and `docs`, and `bun run changelog:lint`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Replaced the tray menu's `Open Overlay` item with `Open Help`, wired it to initialize overlay runtime when needed, and then open the existing session help modal path. Updated tray runtime/main-deps/action tests to assert the old label is absent, the new label is present, and the new action calls the help modal. Added changelog fragment `changes/303-tray-help-menu.md`.
Verification:
- `bun test src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts`
- `bash plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane runtime-compat --lane docs src/main/runtime/tray-runtime.ts src/main/runtime/tray-main-actions.ts src/main/runtime/tray-main-deps.ts src/main.ts changes/303-tray-help-menu.md`
- `bun run changelog:lint`
Verifier artifacts: `.tmp/skill-verification/subminer-verify-20260425-211156-9fkdDf/`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,4 @@
type: changed
area: tray
- Tray: Replaced the Open Overlay tray menu item with Open Help, which opens the session help modal.

View File

@@ -5139,7 +5139,7 @@ const { ensureTray: ensureTrayHandler, destroyTray: destroyTrayHandler } =
buildTrayMenuTemplateRuntime,
initializeOverlayRuntime: () => initializeOverlayRuntime(),
isOverlayRuntimeInitialized: () => appState.overlayRuntimeInitialized,
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
openSessionHelpModal: () => openSessionHelpOverlay(),
showFirstRunSetup: () => !firstRunSetupService.isSetupCompleted(),
openFirstRunSetupWindow: () => openFirstRunSetupWindow(),
showWindowsMpvLauncherSetup: () => process.platform === 'win32',

View File

@@ -41,7 +41,7 @@ test('build tray template handler wires actions and init guards', () => {
let initialized = false;
const buildTemplate = createBuildTrayMenuTemplateHandler({
buildTrayMenuTemplateRuntime: (handlers) => {
handlers.openOverlay();
handlers.openSessionHelp();
handlers.openFirstRunSetup();
handlers.openWindowsMpvLauncherSetup();
handlers.openYomitanSettings();
@@ -56,7 +56,7 @@ test('build tray template handler wires actions and init guards', () => {
calls.push('init');
},
isOverlayRuntimeInitialized: () => initialized,
setVisibleOverlayVisible: (visible) => calls.push(`visible:${visible}`),
openSessionHelpModal: () => calls.push('help'),
showFirstRunSetup: () => true,
openFirstRunSetupWindow: () => calls.push('setup'),
showWindowsMpvLauncherSetup: () => true,
@@ -71,7 +71,7 @@ test('build tray template handler wires actions and init guards', () => {
assert.deepEqual(template, [{ label: 'ok' }]);
assert.deepEqual(calls, [
'init',
'visible:true',
'help',
'setup',
'setup',
'yomitan',

View File

@@ -28,7 +28,7 @@ export function createResolveTrayIconPathHandler(deps: {
export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
buildTrayMenuTemplateRuntime: (handlers: {
openOverlay: () => void;
openSessionHelp: () => void;
openFirstRunSetup: () => void;
showFirstRunSetup: boolean;
openWindowsMpvLauncherSetup: () => void;
@@ -41,7 +41,7 @@ export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
}) => TMenuItem[];
initializeOverlayRuntime: () => void;
isOverlayRuntimeInitialized: () => boolean;
setVisibleOverlayVisible: (visible: boolean) => void;
openSessionHelpModal: () => void;
showFirstRunSetup: () => boolean;
openFirstRunSetupWindow: () => void;
showWindowsMpvLauncherSetup: () => boolean;
@@ -53,11 +53,11 @@ export function createBuildTrayMenuTemplateHandler<TMenuItem>(deps: {
}) {
return (): TMenuItem[] => {
return deps.buildTrayMenuTemplateRuntime({
openOverlay: () => {
openSessionHelp: () => {
if (!deps.isOverlayRuntimeInitialized()) {
deps.initializeOverlayRuntime();
}
deps.setVisibleOverlayVisible(true);
deps.openSessionHelpModal();
},
openFirstRunSetup: () => {
deps.openFirstRunSetupWindow();

View File

@@ -24,7 +24,7 @@ test('tray main deps builders return mapped handlers', () => {
buildTrayMenuTemplateRuntime: () => [{ label: 'tray' }] as never,
initializeOverlayRuntime: () => calls.push('init'),
isOverlayRuntimeInitialized: () => false,
setVisibleOverlayVisible: (visible) => calls.push(`visible:${visible}`),
openSessionHelpModal: () => calls.push('help'),
showFirstRunSetup: () => true,
openFirstRunSetupWindow: () => calls.push('setup'),
showWindowsMpvLauncherSetup: () => true,
@@ -36,7 +36,7 @@ test('tray main deps builders return mapped handlers', () => {
})();
const template = menuDeps.buildTrayMenuTemplateRuntime({
openOverlay: () => calls.push('open-overlay'),
openSessionHelp: () => calls.push('open-help'),
openFirstRunSetup: () => calls.push('open-setup'),
showFirstRunSetup: true,
openWindowsMpvLauncherSetup: () => calls.push('open-windows-mpv'),

View File

@@ -27,7 +27,7 @@ export function createBuildResolveTrayIconPathMainDepsHandler(deps: {
export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
buildTrayMenuTemplateRuntime: (handlers: {
openOverlay: () => void;
openSessionHelp: () => void;
openFirstRunSetup: () => void;
showFirstRunSetup: boolean;
openWindowsMpvLauncherSetup: () => void;
@@ -40,7 +40,7 @@ export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
}) => TMenuItem[];
initializeOverlayRuntime: () => void;
isOverlayRuntimeInitialized: () => boolean;
setVisibleOverlayVisible: (visible: boolean) => void;
openSessionHelpModal: () => void;
showFirstRunSetup: () => boolean;
openFirstRunSetupWindow: () => void;
showWindowsMpvLauncherSetup: () => boolean;
@@ -54,7 +54,7 @@ export function createBuildTrayMenuTemplateMainDepsHandler<TMenuItem>(deps: {
buildTrayMenuTemplateRuntime: deps.buildTrayMenuTemplateRuntime,
initializeOverlayRuntime: deps.initializeOverlayRuntime,
isOverlayRuntimeInitialized: deps.isOverlayRuntimeInitialized,
setVisibleOverlayVisible: deps.setVisibleOverlayVisible,
openSessionHelpModal: deps.openSessionHelpModal,
showFirstRunSetup: deps.showFirstRunSetup,
openFirstRunSetupWindow: deps.openFirstRunSetupWindow,
showWindowsMpvLauncherSetup: deps.showWindowsMpvLauncherSetup,

View File

@@ -19,14 +19,12 @@ test('tray runtime handlers compose resolve/menu/ensure/destroy handlers', () =>
fileExists: () => true,
},
buildTrayMenuTemplateDeps: {
buildTrayMenuTemplateRuntime: () => [{ label: 'Open Overlay' }],
buildTrayMenuTemplateRuntime: () => [{ label: 'Open Help' }],
initializeOverlayRuntime: () => {
overlayInitialized = true;
},
isOverlayRuntimeInitialized: () => overlayInitialized,
setVisibleOverlayVisible: (visible) => {
visibleOverlay = visible;
},
openSessionHelpModal: () => {},
showFirstRunSetup: () => true,
openFirstRunSetupWindow: () => {},
showWindowsMpvLauncherSetup: () => true,
@@ -88,7 +86,7 @@ test('tray runtime handlers compose resolve/menu/ensure/destroy handlers', () =>
});
assert.equal(runtime.resolveTrayIconPath(), '/tmp/SubMiner.png');
assert.deepEqual(runtime.buildTrayMenu(), { template: [{ label: 'Open Overlay' }] });
assert.deepEqual(runtime.buildTrayMenu(), { template: [{ label: 'Open Help' }] });
runtime.ensureTray();
assert.equal(overlayInitialized, true);
assert.equal(visibleOverlay, true);

View File

@@ -29,7 +29,7 @@ test('resolve tray icon returns null when no asset exists', () => {
test('tray menu template contains expected entries and handlers', () => {
const calls: string[] = [];
const template = buildTrayMenuTemplateRuntime({
openOverlay: () => calls.push('overlay'),
openSessionHelp: () => calls.push('help'),
openFirstRunSetup: () => calls.push('setup'),
showFirstRunSetup: true,
openWindowsMpvLauncherSetup: () => calls.push('windows-mpv'),
@@ -42,15 +42,17 @@ test('tray menu template contains expected entries and handlers', () => {
});
assert.equal(template.length, 9);
assert.equal(template.some((entry) => entry.label === 'Open Overlay'), false);
assert.equal(template[0]!.label, 'Open Help');
template[0]!.click?.();
template[7]!.type === 'separator' ? calls.push('separator') : calls.push('bad');
template[8]!.click?.();
assert.deepEqual(calls, ['overlay', 'separator', 'quit']);
assert.deepEqual(calls, ['help', 'separator', 'quit']);
});
test('tray menu template omits first-run setup entry when setup is complete', () => {
const labels = buildTrayMenuTemplateRuntime({
openOverlay: () => undefined,
openSessionHelp: () => undefined,
openFirstRunSetup: () => undefined,
showFirstRunSetup: false,
openWindowsMpvLauncherSetup: () => undefined,

View File

@@ -30,7 +30,7 @@ export function resolveTrayIconPathRuntime(deps: {
}
export type TrayMenuActionHandlers = {
openOverlay: () => void;
openSessionHelp: () => void;
openFirstRunSetup: () => void;
showFirstRunSetup: boolean;
openWindowsMpvLauncherSetup: () => void;
@@ -49,8 +49,8 @@ export function buildTrayMenuTemplateRuntime(handlers: TrayMenuActionHandlers):
}> {
return [
{
label: 'Open Overlay',
click: handlers.openOverlay,
label: 'Open Help',
click: handlers.openSessionHelp,
},
...(handlers.showFirstRunSetup
? [