feat: publication IA (magazine/brief/essay) + fixes critique
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m22s
CI / Deploy production (on server) (push) Has been skipped

Publication IA:
- 4 templates (magazine, brief, essay, simple) avec CSS riche
- Rewrite IA (article/exercises/tutorial/reference/mixed)
- Modération avec timeout 12s + fallback safe
- Quotas publish_enhance par tier (basic=2, pro=15, business=100)
- Détection contenu stale (hash)
- Migration DB publishedContent/publishedTemplate/publishedSourceHash

Fixes:
- cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast
- _isShared ajouté au type Note (champ virtuel serveur)
- callout colors PDF export: extraction fonction pure testable
- admin/published: guard note.userId null
- Cmd+S fonctionne en mode dialog (pas seulement fullPage)

i18n:
- 23 clés publish* traduites dans les 15 locales
- Extension Web Clipper: 13 locales mise à jour

Tests:
- callout-colors.test.ts (6 tests)
- note-visible-in-view.test.ts (5 tests)
- entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs
- 199/199 tests passent

Tracker: user-stories.md sync avec sprint-status.yaml
This commit is contained in:
Antigravity
2026-06-28 07:32:57 +00:00
parent 902fe95a69
commit 96e7902f01
169 changed files with 5382 additions and 1527 deletions

View File

@@ -575,7 +575,7 @@
"noNoteId": "No note ID"
},
"openAssistant": "AI 어시스턴트 열기",
"poweredByMomento": "Momento AI 제공",
"poweredByMomento": "Memento AI 제공",
"welcomeMsg": "안녕하세요! AI 어시스턴트입니다. 오늘 노트에 대해 어떻게 도와드릴까요? 어조 조정, 메시지 확장, 콘텐츠 요약 등이 가능합니다.",
"summaryLast5": "최근 5개 노트 요약",
"analyzingProgress": "분석 중...",
@@ -2184,7 +2184,30 @@
"importMarkdown": "Markdown 가져오기",
"markdownExportSuccess": "노트가 Markdown으로 내보내졌습니다",
"markdownExportError": "노트 내보내기 실패",
"markdownImportSuccess": "Markdown 가져오기 성공"
"markdownImportSuccess": "Markdown 가져오기 성공",
"publishTitle": "공개 게시",
"publishDesc": "이 노트를 공개 URL에 게시합니다. 링크가 있는 누구나 읽을 수 있습니다.",
"publish": "게시",
"publishSuccess": "노트가 게시되었습니다!",
"publishLive": "게시 중",
"unpublish": "게시 취소",
"unpublishSuccess": "노트가 게시 취소되었습니다",
"publishBlocked": "게시가 거부되었습니다",
"publishSimple": "있는 그대로 게시",
"publishSimpleHint": "무료 — 노트 내용을 그대로 사용",
"publishWithAi": "AI로 게시",
"publishWithAiHint": "강화된 레이아웃 · {count}크레딧 남음",
"publishWithAiLocked": "Pro 요금제에서 이용 가능",
"publishWithAiGenerating": "레이아웃 생성 중…",
"publishTemplateMagazine": "매거진 기사",
"publishTemplateBrief": "전문가 브리핑",
"publishTemplateEssay": "에세이",
"publishAiSuccess": "AI 강화 페이지가 게시되었습니다!",
"publishRewriteLabel": "웹용으로 다시 쓰기",
"publishRewriteOnHint": "편집기 블록(연습문제, 접기 섹션, 안내 상자)을 웹용으로 구성 — AI는 서론만 작성",
"publishRewriteOffHint": "원본 텍스트가 유지되며 AI가 편집 프레임만 추가",
"copyPublicLink": "링크 복사",
"openPublicPage": "공개 페이지 열기"
},
"brainstorm": {
"title": "Waves of Thought",
@@ -2492,7 +2515,7 @@
"badge": "인공지능으로 구동",
"title1": "당신의 제2의 뇌,",
"title2": "드디어 증폭됩니다.",
"subtitle": "Momento는 단순한 메모 앱이 아닙니다. 6종류의 AI 에이전트와 최첨단 시맨틱 검색으로 아이디어를 실시간으로 연결·분석·발전시키는 지능형 생태계입니다.",
"subtitle": "Memento는 단순한 메모 앱이 아닙니다. 6종류의 AI 에이전트와 최첨단 시맨틱 검색으로 아이디어를 실시간으로 연결·분석·발전시키는 지능형 생태계입니다.",
"cta": "지금 가입하기",
"secondary": "기능 보기",
"memoryEcho": "Memory Echo",
@@ -2504,7 +2527,7 @@
"label": "AI 역량",
"title": "유연한 지능,",
"title2": "모든 단어에 스며듭니다.",
"desc": "Momento는 멀티 프로바이더 아키텍처로 아이디어를 조율합니다.",
"desc": "Memento는 멀티 프로바이더 아키텍처로 아이디어를 조율합니다.",
"f1Title": "시맨틱 검색",
"f1Desc": "키워드 검색은 그만. 개념으로 찾으세요. 하이브리드 Vector + FTS 엔진이 노트 뒤의 의도를 이해합니다.",
"f2Title": "컨텍스트 RAG 채팅",
@@ -2590,7 +2613,7 @@
"popular": "가장 인기",
"basic": {
"name": "Basic",
"desc": "Momento의 마법을 경험하세요.",
"desc": "Memento의 마법을 경험하세요.",
"cta": "시작하기",
"feature0": "최대 100개 노트",
"feature1": "3개 노트북",
@@ -2636,7 +2659,7 @@
"byok": {
"label": "오픈 클라우드 기술",
"title": "BYOK 전략",
"desc": "OpenAI, Anthropic, Google API 키가 있으신가요? Momento에 직접 연결하세요. 강제 크레딧 한도 없이, 선호하는 프로바이더의 실제 사용량만 지불합니다.",
"desc": "OpenAI, Anthropic, Google API 키가 있으신가요? Memento에 직접 연결하세요. 강제 크레딧 한도 없이, 선호하는 프로바이더의 실제 사용량만 지불합니다.",
"noLockin": "락인 없음",
"noLockinDesc": "클릭 한 번으로 프로바이더 변경.",
"cost": "최적화된 비용",
@@ -2646,8 +2669,8 @@
"cta": {
"title1": "당신의",
"title2": "잠재력을 깨울 준비가 되셨나요?",
"desc": "Momento로 미래를 만들고 있는 수천 명의 연구자, 디자이너, 사상가와 함께하세요.",
"button": "Momento 실행"
"desc": "Memento로 미래를 만들고 있는 수천 명의 연구자, 디자이너, 사상가와 함께하세요.",
"button": "Memento 실행"
},
"footer": {
"desc": "AI로 증폭된 제2의 뇌. 창의적인 분들을 위해.",
@@ -2704,14 +2727,14 @@
"necessaryDesc": "These cookies are required to authenticate your session, save your language and theme preferences. They cannot be turned off.",
"alwaysOn": "Always On",
"analyticsTitle": "Anonymous Analytics",
"analyticsDesc": "Help us improve Momento by sending completely anonymous usage statistics. No personal data is ever tracked.",
"analyticsDesc": "Help us improve Memento by sending completely anonymous usage statistics. No personal data is ever tracked.",
"cancel": "Cancel",
"save": "Save Preferences",
"saved": "Preferences updated successfully."
},
"ai": {
"modalTitle": "AI 처리 동의 필요",
"modalDescription": "노트, PDF 또는 브레인스토밍 세션을 분석하기 위해 Momento는 해당 데이터를 제3자 AI API(OpenAI, Gemini, DeepSeek)로 안전하게 전송합니다. 엄격한 데이터 무보관 정책을 적용합니다. 동의하면 이 처리를 허용한 것입니다.",
"modalDescription": "노트, PDF 또는 브레인스토밍 세션을 분석하기 위해 Memento는 해당 데이터를 제3자 AI API(OpenAI, Gemini, DeepSeek)로 안전하게 전송합니다. 엄격한 데이터 무보관 정책을 적용합니다. 동의하면 이 처리를 허용한 것입니다.",
"zeroRetentionTitle": "데이터 무보관",
"zeroRetentionDesc": "모든 아웃바운드 요청에 모델 학습 방지 플래그를 포함하여 지적 재산을 보호합니다.",
"rememberMe": "내 선택 기억하기(다시 묻지 않음)",
@@ -2725,10 +2748,10 @@
"statusActive": "활성",
"statusInactive": "미부여",
"helpAriaLabel": "AI 동의란?",
"helpTooltip": "Momento는 데이터를 판매하지 않습니다. 이 동의는 AI 작업을 실행할 때만 적용됩니다(제목, 태그, 채팅, 재작성). 동의 없이도 앱의 나머지 기능은 정상 작동합니다.",
"helpTooltip": "Memento는 데이터를 판매하지 않습니다. 이 동의는 AI 작업을 실행할 때만 적용됩니다(제목, 태그, 채팅, 재작성). 동의 없이도 앱의 나머지 기능은 정상 작동합니다.",
"whatItMeansTitle": "무슨 뜻인가요?",
"inactiveHint": "동의하지 않으면 AI 기능만 비활성화됩니다. 노트 작성·편집·정리는 평소처럼 가능합니다.",
"noCommercialUse": "Momento는 개인 데이터를 판매하거나 수익화하지 않습니다.",
"noCommercialUse": "Memento는 개인 데이터를 판매하거나 수익화하지 않습니다.",
"affectedFeatures": "영향 기능: 제목 제안, 태그, AI 채팅, 재작성, Memory Echo 등.",
"dataPortabilityHint": "언제든 모든 데이터를 내보내거나 삭제할 수 있습니다(설정 → 데이터).",
"dataPortabilityLink": "내 데이터 내보내기",
@@ -2782,7 +2805,7 @@
},
"onboarding": {
"welcome_title": "AI로 강화된 당신의 기억",
"welcome_subtitle": "Momento는 당신이 잊은 것을 기억합니다.",
"welcome_subtitle": "Memento는 당신이 잊은 것을 기억합니다.",
"welcome_cta": "시작하기",
"skip": "건너뛰기",
"step_notes_title": "당신의 노트",
@@ -2794,7 +2817,7 @@
"step_aha_title": "잊은 것을 찾아보세요",
"step_aha_subtitle": "질문을 입력하세요. 잊었던 노트를 찾아보세요.",
"step_aha_placeholder": "생산성에 관한 노트...",
"step_aha_cta": "Momento 탐색",
"step_aha_cta": "Memento 탐색",
"progress": "{total} 중 {current}",
"creating_demo_notes": "예제 노트 생성 중...",
"demo_notes_ready": "예제 노트 5개 생성 완료!",
@@ -2898,4 +2921,4 @@
"copyRefNoNote": "Save the note before copying a block reference",
"copyRefUnsupported": "This block type cannot be referenced yet"
}
}
}