Files
inkling/src/main/services/NotificationService.ts
altair823 2d90a48621 feat(copy): replace '기억 구출' framing with 표면별 자연 동사 + Zeigarnik priming
F3: '구출' (rescue) is unnatural everyday Korean. Replace per-surface:
  - 트레이 '구출한 메모 보기' → '보관한 메모 보기'
  - 트레이 '기억 구출하기' → '한 줄 적기'
  - 토스트 #2 → '머릿속에서 꺼내 두었습니다.'
  - 토스트 #3 → '방금 한 줄 잡아뒀습니다.'
  - QC 힌트 'Ctrl+Enter 구출' → 'Ctrl+Enter 저장'
  - package.json description → 'local-first 한 줄 보관 도구'

F4-E (Zeigarnik priming): empty state copy reframed to evoke the
"unfinished thought tugging at memory" → "외재화로 해소" loop:
  - '첫 기억을 구출해보세요.' → '머릿속에 떠다니는 한 줄을 적어보세요.'

E2E smoke assertion updated to match. Slice §1.1 invariant 5
('실패/끊김/연속 실패' 금지) preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 11:29:31 +09:00

34 lines
912 B
TypeScript

import { createHash } from 'node:crypto';
export const REWARD_COPIES = [
'이 생각은 이제 Inkling이 들고 있습니다.',
'머릿속에서 꺼내 두었습니다.',
'방금 한 줄 잡아뒀습니다.',
'기록 완료. 이제 잊어도 됩니다.'
] as const;
export interface NotificationDeps {
isSupported: () => boolean;
send: (body: string) => void;
}
export class NotificationService {
constructor(private deps: NotificationDeps) {}
celebrate(noteId: string): void {
if (!this.deps.isSupported()) return;
const idx = this.pick(noteId);
const body = REWARD_COPIES[idx]!;
try {
this.deps.send(body);
} catch {
// Swallow notification errors — capture must not fail because of toast.
}
}
private pick(noteId: string): number {
const hash = createHash('sha256').update(noteId).digest();
return hash[0]! % REWARD_COPIES.length;
}
}