diff --git a/src/background/index.ts b/src/background/index.ts index d15eba4..dfac677 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -73,20 +73,52 @@ let cachedScreenshot: Blob | null = null; // ─── Plan 01-09 badge palette + notification constants ─────────────── // Project naming standard: SCREAMING_SNAKE for true constants. These // drive the operator-facing badge state machine + notification flow. -const BADGE_REC_COLOR = '#00C853'; // material green -const BADGE_OFF_COLOR = '#D32F2F'; // material red -const BADGE_ERROR_COLOR = '#F9A825'; // material amber-700 +// +// Plan 01-12 Wave 4: BADGE_REC_COLOR updated from #00C853 (material +// green) to #b2543d (= --mks-madder-600 per D-04 loom palette per +// RESEARCH §10 Open Question A7). OFF + ERROR colors retained as +// engineering choices (no loom-palette token for material-red / +// material-amber-700 equivalents); document choice inline. +// +// Plan 01-12 Wave 4: BADGE_*_TITLE constants kept as FALLBACKS for the +// chrome.i18n.getMessage reads at the setBadgeState call sites. Unit +// tests that don't stub chrome.i18n degrade to these literals rather +// than empty strings. +const BADGE_REC_COLOR = '#b2543d'; // --mks-madder-600 per D-04 loom palette (Plan 01-12) +const BADGE_OFF_COLOR = '#D32F2F'; // material red (no loom-palette token for OFF) +const BADGE_ERROR_COLOR = '#F9A825'; // material amber-700 (no loom-palette token for ERR) const BADGE_REC_TEXT = 'REC'; const BADGE_OFF_TEXT = ''; const BADGE_ERROR_TEXT = 'ERR'; -const BADGE_REC_TITLE = 'Recording — last 30 s buffered. Click to save.'; -const BADGE_OFF_TITLE = 'Not recording. Click to start.'; -const BADGE_ERROR_TITLE = 'Recording error. Click to try again.'; +const BADGE_REC_TITLE_FALLBACK = 'Recording — last 30 s buffered. Click to save.'; +const BADGE_OFF_TITLE_FALLBACK = 'Not recording. Click to start.'; +const BADGE_ERROR_TITLE_FALLBACK = 'Recording error. Click to try again.'; const NOTIFICATION_ICON_PATH = 'icons/icon128.png'; const NOTIFICATION_STARTUP_PREFIX = 'mokosh-startup-'; const NOTIFICATION_RECOVERY_PREFIX = 'mokosh-recovery-'; const POPUP_HTML_PATH = 'src/popup/index.html'; +// Plan 01-12 Wave 4: operator-facing copy fallbacks for the notification +// title (extName) + the two notification messages. Same `|| fallback` +// pattern as the popup — unit-test contexts without chrome.i18n stub +// degrade to these literals. +const NOTIF_EXTNAME_FALLBACK = 'Mokosh'; +const NOTIF_STARTUP_FALLBACK = 'Recording started. Click here to start a recording.'; +const NOTIF_RECOVERY_FALLBACK = 'Recording stopped. Click here to start a new session.'; + +/** + * Safe wrapper around chrome.i18n.getMessage with explicit fallback. + * Returns the fallback when chrome.i18n is undefined (unit-test contexts + * without a stub) OR when the key is missing in the resolved locale. + * + * chrome.i18n.getMessage is SYNCHRONOUS per Chrome docs — no Promise. + * Returns '' for missing keys, hence the explicit length check. + */ +function i18nMessage(key: string, fallback: string): string { + const text = chrome?.i18n?.getMessage?.(key) ?? ''; + return text.length > 0 ? text : fallback; +} + // ─── Plan 01-09 badge state machine + mode helpers ─────────────────── // 3-state machine: REC (during recording), OFF (idle), ERROR (after // RECORDING_ERROR). Each setBadgeState call is best-effort: chrome.action @@ -101,15 +133,15 @@ function setBadgeState(state: BadgeState): void { if (state === 'REC') { text = BADGE_REC_TEXT; color = BADGE_REC_COLOR; - title = BADGE_REC_TITLE; + title = i18nMessage('tooltipRecPrefix', BADGE_REC_TITLE_FALLBACK); } else if (state === 'OFF') { text = BADGE_OFF_TEXT; color = BADGE_OFF_COLOR; - title = BADGE_OFF_TITLE; + title = i18nMessage('tooltipOff', BADGE_OFF_TITLE_FALLBACK); } else { text = BADGE_ERROR_TEXT; color = BADGE_ERROR_COLOR; - title = BADGE_ERROR_TITLE; + title = i18nMessage('tooltipErr', BADGE_ERROR_TITLE_FALLBACK); } try { chrome.action.setBadgeText({ text }); } catch (e) { logger.warn('setBadgeText failed:', e); } try { chrome.action.setBadgeBackgroundColor({ color }); } catch (e) { logger.warn('setBadgeBackgroundColor failed:', e); } @@ -822,8 +854,8 @@ chrome.runtime.onMessage.addListener((message: Message, sender, sendResponse) => chrome.notifications.create(recoveryId, { type: 'basic', iconUrl: chrome.runtime.getURL(NOTIFICATION_ICON_PATH), - title: 'Mokosh stopped', - message: 'Recording stopped. Click here to start a new session.', + title: i18nMessage('extName', NOTIF_EXTNAME_FALLBACK), + message: i18nMessage('notifRecovery', NOTIF_RECOVERY_FALLBACK), priority: 1, }); } catch (e) { @@ -924,8 +956,8 @@ try { chrome.notifications.create(notificationId, { type: 'basic', iconUrl: chrome.runtime.getURL(NOTIFICATION_ICON_PATH), - title: 'Mokosh ready', - message: 'Click here to start recording your session.', + title: i18nMessage('extName', NOTIF_EXTNAME_FALLBACK), + message: i18nMessage('notifStartup', NOTIF_STARTUP_FALLBACK), priority: 1, }); } catch (e) { diff --git a/src/popup/index.html b/src/popup/index.html index 99fe8de..4b5d520 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -3,19 +3,25 @@
-- Последние 30 сек видео + 10 мин лога -
+