Feat/end-user-guide-and-help#380
Conversation
…end-user guides and command metadata
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive built-in Help extension for the Asyar launcher, featuring a keyboard shortcut catalog, reactive state management, and extensive user documentation covering features like AI agents, portals, snippets, and window management. The review feedback focuses on enhancing the Help view's usability by adding mouse interaction support (such as click handlers, pointer cursors, and hover states on topic rows) and improving the extension's lifecycle robustness by implementing cleanup in onUnload and adding an activation guard in viewDeactivated to prevent memory leaks.
| <script lang="ts"> | ||
| import { helpViewState } from './helpState.svelte'; | ||
| import { LAUNCHER_SHORTCUTS } from '../../lib/keyboard/shortcutCatalog'; | ||
| import Icon from '../../components/base/Icon.svelte'; | ||
| import { getBuiltInIconName, isBuiltInIcon } from '../../lib/iconUtils'; | ||
| </script> |
There was a problem hiding this comment.
To support mouse interactions, import openUrl and guideUrl so they can be used in a click handler on the topic rows.
<script lang="ts">
import { helpViewState } from './helpState.svelte';
import { LAUNCHER_SHORTCUTS } from '../../lib/keyboard/shortcutCatalog';
import Icon from '../../components/base/Icon.svelte';
import { getBuiltInIconName, isBuiltInIcon } from '../../lib/iconUtils';
import { openUrl } from '@tauri-apps/plugin-opener';
import { guideUrl } from './topics';
</script>
| <li class="topic-row" class:selected={i === helpViewState.selectedIndex}> | ||
| {#if isBuiltInIcon(topic.icon)} | ||
| <Icon name={getBuiltInIconName(topic.icon)} /> | ||
| {/if} | ||
| <span class="topic-text"> | ||
| <span class="topic-title">{topic.title}</span> | ||
| <span class="topic-subtitle">{topic.subtitle}</span> | ||
| </span> | ||
| </li> |
There was a problem hiding this comment.
Add an onclick handler to allow users to select and open a topic by clicking on it, improving usability for mouse users.
{#each helpViewState.filtered as topic, i}
<li
class="topic-row"
class:selected={i === helpViewState.selectedIndex}
onclick={async () => {
helpViewState.selectedIndex = i;
await openUrl(guideUrl(topic.slug));
}}
>
{#if isBuiltInIcon(topic.icon)}
<Icon name={getBuiltInIconName(topic.icon)} />
{/if}
<span class="topic-text">
<span class="topic-title">{topic.title}</span>
<span class="topic-subtitle">{topic.subtitle}</span>
</span>
</li>
{/each}
| .topic-row { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: var(--space-3, 8px); | ||
| padding: var(--space-2, 6px) var(--space-3, 8px); | ||
| border-radius: var(--radius-md, 8px); | ||
| cursor: default; | ||
| } |
There was a problem hiding this comment.
Change the cursor to pointer and add a hover state to visually indicate that the topic rows are interactive and clickable.
.topic-row {
display: flex;
align-items: center;
gap: var(--space-3, 8px);
padding: var(--space-2, 6px) var(--space-3, 8px);
border-radius: var(--radius-md, 8px);
cursor: pointer;
}
.topic-row:hover:not(.selected) {
background: var(--surface-hover, rgba(255, 255, 255, 0.04));
}
| class HelpExtension implements Extension { | ||
| onUnload = () => {}; | ||
| private logService?: ILogService; | ||
| private extensionManager?: IExtensionManager; | ||
| private isViewActive = false; | ||
| private handleKeydownBound = (e: KeyboardEvent) => this.handleKeydown(e); |
There was a problem hiding this comment.
Implement cleanup in onUnload to remove the global keydown event listener and unregister the action when the extension is unloaded, preventing memory leaks.
class HelpExtension implements Extension {
onUnload = () => {
window.removeEventListener('keydown', this.handleKeydownBound);
actionService.unregisterAction(OPEN_GUIDE_ACTION_ID);
};
private logService?: ILogService;
private extensionManager?: IExtensionManager;
private isViewActive = false;
private handleKeydownBound = (e: KeyboardEvent) => this.handleKeydown(e);| async viewDeactivated(_viewPath: string): Promise<void> { | ||
| window.removeEventListener('keydown', this.handleKeydownBound); | ||
| this.extensionManager?.setActiveViewActionLabel(null); | ||
| actionService.unregisterAction(OPEN_GUIDE_ACTION_ID); | ||
| this.isViewActive = false; | ||
| } |
There was a problem hiding this comment.
Add a guard to viewDeactivated to ensure cleanup only runs if the view is currently active, preventing redundant calls and potential side effects.
| async viewDeactivated(_viewPath: string): Promise<void> { | |
| window.removeEventListener('keydown', this.handleKeydownBound); | |
| this.extensionManager?.setActiveViewActionLabel(null); | |
| actionService.unregisterAction(OPEN_GUIDE_ACTION_ID); | |
| this.isViewActive = false; | |
| } | |
| async viewDeactivated(_viewPath: string): Promise<void> { | |
| if (!this.isViewActive) return; | |
| window.removeEventListener('keydown', this.handleKeydownBound); | |
| this.extensionManager?.setActiveViewActionLabel(null); | |
| actionService.unregisterAction(OPEN_GUIDE_ACTION_ID); | |
| this.isViewActive = false; | |
| } |
No description provided.