/**
* dsh-ui-tweaks — task notifications (browser half).
*
* Watches the sessions list feed together with the unified session-status
* source (the same facts the sidebar's running flag, amber interaction dot and
* green "done" reminder project from) and raises a browser-side heads-up when a
* session finishes its turn or starts blocking on the user — so a backgrounded
* tab can call you back:
*
* - "Finished" = a session's `running` flag drops (true → false) without an
* interaction taking over, or its host-tracked `completionUnread` reminder
* rises (finished while not selected). The host `dshTurnOutcome` session
* projection (`src/turn-outcome.ts`, folded from `turn/end` reasons) says
* WHY it finished, so the copy distinguishes a clean completion from a
* user abort and from a failed request (with the error text); when the
* projection is missing or stale, falls back to a plain-finish announce.
* - "Interaction" = a session gains `pendingInteraction`
* ('approval' | 'plan-review' | 'question') — exactly the sidebar's
* amber-dot classification.
*
* Three independent channels, each silently degrading when unavailable:
* - Title flash: alternates `(N) 🔔
` ↔ `(N) ` until the page
* is visible and focused again, then restores the captured title. DSH may
* rewrite `document.title` independently; while flashing our ticks win,
* and on stop we restore the pre-flash capture (worst case a stale base).
* - System notification: the Web Notifications API; permission is requested
* from the settings toggle's user gesture (`requestNotifyPermission`).
* Clicking one focuses the window and opens that session through the
* workspace navigation service. `silent` tracks the chime channel so the
* two never double-beep.
* - Chime: a tiny WebAudio two-note motif (rising = done, falling = needs
* you), synthesized in-process — no audio assets. Autoplay policy allows
* this once the user has interacted with the origin (sticky activation),
* which the settings-toggle clicks provide.
*
* Anti-spam: the first snapshot after install only arms the baseline (a page
* load never fires a burst); events fire on transitions only; a short
* per-session+kind cooldown absorbs reconnect re-pull flicker. Subagent child
* rows are skipped — their parent carries the turn, and mid-turn subagent
* completions would be noise.
* @module dsh-ui-tweaks/client/notifier
*/
import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
import type { SessionStatusSnapshot } from '@deepseek-ai/dsh-client-ui-session/client';
import type { SessionId } from '@deepseek-ai/dsh-session/types';
/** Client-side view of the host `dshTurnOutcome` fold (see src/turn-outcome.ts). */
interface TurnOutcomeSnapshot {
/** Literal `TurnEndReason` kind; unknown merged variants stay strings. */
kind: string;
/** Unix epoch ms of the `turn/end` event. */
time: number;
/** Truncated failure text; present only on error outcomes. */
errorMessage?: string;
}
declare module '@deepseek-ai/dsh-session-projection/types' {
interface SessionProjectionMap {
dshTurnOutcome: TurnOutcomeSnapshot | null;
}
}
/** Interaction kinds the host reports while a session blocks on the user. */
export type NotifyPendingKind = 'approval' | 'plan-review' | 'question';
/** Localized copy snapshot the notifier needs (resolved once at install). */
export interface NotifierText {
/** System-notification heading for completion events. */
notifyTitleDone: string;
/** System-notification heading for user-interrupted turns. */
notifyTitleAborted: string;
/** System-notification heading for failed requests. */
notifyTitleFailed: string;
/** System-notification heading for interaction events. */
notifyTitlePending: string;
/** Completion body template; `{title}` becomes the session display title. */
bodyComplete: string;
/** Interruption body template. */
bodyAborted: string;
/** Failure body template; `{error}` becomes the truncated failure message. */
bodyFailed: string;
/** Approval body template. */
bodyApproval: string;
/** Plan-review body template. */
bodyPlan: string;
/** Question body template. */
bodyQuestion: string;
}
/**
* Live-read behavior switches — read through {@link TaskNotifierInput.readState}
* on every feed tick so settings-panel flips apply without reinstalling.
*/
export interface NotifierOptions {
/** Raise alerts only while the page is hidden or unfocused. */
onlyWhenHidden: boolean;
/** Alert when a session finishes its turn. */
onComplete: boolean;
/** Alert when a session starts waiting on the user. */
onInteraction: boolean;
}
/** Output channels, each independently toggleable. */
export interface NotifierChannels {
titleFlash: boolean;
systemNotification: boolean;
sound: boolean;
}
/** Install-time wiring for {@link installTaskNotifier}. */
export interface TaskNotifierInput {
/** The client sessions service whose list feed carries the rows' identity, title and turn-outcome projection. */
sessionsService: ISessions;
/** Localized copy snapshot used across the notifier's lifetime. */
text: NotifierText;
/** Fresh behavior switches + channel toggles, read on every tick. */
readState(): {
options: NotifierOptions;
channels: NotifierChannels;
};
/**
* Unified per-session UI status (DSH 0.1.6-alpha.2): running, the
* highest-precedence pending interaction, and the unread completion
* reminder. Replaces the alpha.1 `uiSession.pendingInteractions` map and
* the list row's removed `completed` flag.
*/
sessionStatus: {
getSnapshot(): SessionStatusSnapshot;
subscribe(listener: () => void): () => void;
};
/** Select a session and show its conversation — the notification click target. */
openSession(target: SessionId): void;
}
/**
* Ask the browser for desktop-notification permission. Must run inside a user
* gesture (the settings toggle's click) to avoid a denied or ignored prompt;
* repeated calls are cheap no-ops once decided. Inside the DSH desktop shell
* the app's session auto-grants non-media permissions, so this resolves
* 'granted' without any visible prompt — harmless there, needed in a browser.
*/
export declare function requestNotifyPermission(): void;
export declare function previewTitleFlash(durationMs?: number): void;
/**
* One-shot self-test for the settings panel's 测试 button: previews every
* enabled channel — a forced ~6s title-flash blink (the real one would clear
* instantly on a focused page), the falling (needs-you) chime, and, when
* permission is already granted, one sample system notification.
*/
export declare function previewAlerts(channels: NotifierChannels, text: NotifierText): void;
/**
* Install the task notifier against the sessions list feed.
* @returns disposer — unsubscribes, restores the tab title and tears the channels down.
*/
export declare function installTaskNotifier(input: TaskNotifierInput): () => void;
export {};
//# sourceMappingURL=notifier.d.ts.map