diff --git a/app/javascript/dashboard/helper/snoozeDateParser/localization.js b/app/javascript/dashboard/helper/snoozeDateParser/localization.js index bc0b2bcae..08b5697fe 100644 --- a/app/javascript/dashboard/helper/snoozeDateParser/localization.js +++ b/app/javascript/dashboard/helper/snoozeDateParser/localization.js @@ -198,8 +198,9 @@ const buildReplacementPairsUncached = (translations, locale) => { const addPair = (local, en) => { const l = sanitize(safeString(local)); const e = safeString(en).toLowerCase(); - if (l && e && l !== e && !seen.has(l)) { - seen.add(l); + const key = `${l}\0${e}`; + if (l && e && l !== e && !seen.has(key)) { + seen.add(key); pairs.push([l, e]); } }; diff --git a/app/javascript/dashboard/helper/snoozeDateParser/parser.js b/app/javascript/dashboard/helper/snoozeDateParser/parser.js index 1e03afb93..4a778064d 100644 --- a/app/javascript/dashboard/helper/snoozeDateParser/parser.js +++ b/app/javascript/dashboard/helper/snoozeDateParser/parser.js @@ -266,10 +266,14 @@ const matchRelativeDay = (text, now) => { const dayAtTimeMatch = text.match(RELATIVE_DAY_AT_TIME_RE); if (dayAtTimeMatch) { - const time = parseTimeString(dayAtTimeMatch[2]); + const [, dayKey, timeRaw] = dayAtTimeMatch; + const bare = /^(tonight|tonite)$/.test(dayKey) && !/[ap]m/i.test(timeRaw); + const time = bare + ? inferHoursFromTOD('tonight', ...timeRaw.split(':')) + : parseTimeString(timeRaw); if (!time) return null; return applyTimeWithRollover( - RELATIVE_DAY_MAP[dayAtTimeMatch[1]], + RELATIVE_DAY_MAP[dayKey], time.hours, time.minutes, now diff --git a/app/javascript/dashboard/helper/specs/snoozeDateParser.spec.js b/app/javascript/dashboard/helper/specs/snoozeDateParser.spec.js index 388967a8e..708e62315 100644 --- a/app/javascript/dashboard/helper/specs/snoozeDateParser.spec.js +++ b/app/javascript/dashboard/helper/specs/snoozeDateParser.spec.js @@ -1011,6 +1011,12 @@ describe('golden tests: pinned phrase → exact date/time', () => { ['2025-01-15', 2025, 0, 15, 9, 0], ['01/15/2025', 2025, 0, 15, 9, 0], + // ── Tonight bare-hour (must infer PM, not AM) ── + ['tonight 8', 2023, 5, 16, 20, 0], + ['tonite 7', 2023, 5, 16, 19, 0], + ['tonight 11', 2023, 5, 16, 23, 0], + ['today 8', 2023, 5, 17, 8, 0], // 8am is past → rolls to next day + // ── Special ── ['this weekend', 2023, 5, 17, 9, 0], ['end of month', 2023, 5, 30, 17, 0], diff --git a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue index 65d98e65c..ccd175321 100644 --- a/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue +++ b/app/javascript/dashboard/routes/dashboard/commands/commandbar.vue @@ -13,6 +13,7 @@ import { useConversationHotKeys } from 'dashboard/composables/commands/useConver import wootConstants from 'dashboard/constants/globals'; import { GENERAL_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; import { generateSnoozeSuggestions } from 'dashboard/helper/snoozeHelpers'; +import { parseDateFromText } from 'dashboard/helper/snoozeDateParser'; import { ICON_SNOOZE_CONVERSATION } from 'dashboard/helper/commandbar/icons'; import { CMD_SNOOZE_CONVERSATION, @@ -107,7 +108,10 @@ const buildDynamicSnoozeActions = (search, parentId) => { section, icon: ICON_SNOOZE_CONVERSATION, keywords: search, - handler: () => emitter.emit(busEvent, parsed.unixTime), + handler: () => { + const fresh = parseDateFromText(parsed.label.toLowerCase()); + emitter.emit(busEvent, fresh?.unix ?? parsed.unixTime); + }, })); };