Fix three bugs found during code review:
1. useTauriEvent/useTauriEvents: If the component unmounts before the
async listen() promise resolves, the unlisten function was lost,
leaking the event subscription. Added a cancelled flag to call
unlisten immediately when the promise resolves after cleanup.
2. useTauriEvents: The handlers object was used directly as a useEffect
dependency, causing re-subscription on every render when callers
pass an inline object literal. Replaced with a useRef for handler
stability and a derived eventNames string as the dependency.
3. isMcError type guard: Only checked property existence via 'in'
operator, not property types. An object with wrong-typed properties
(e.g. code: 42) would pass the guard. Now validates that code and
message are strings and recoverable is boolean.
4. AppShell global shortcut listener: Same race condition as (1), plus
missing .catch() on the listen promise could produce unhandled
rejections.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>