Skip to content

fix: Inbox 纯文本自动转换为 TipTap JSON (closes #83)#86

Merged
ximing merged 40 commits into
masterfrom
ralph/inbox-rich-text
Mar 19, 2026
Merged

fix: Inbox 纯文本自动转换为 TipTap JSON (closes #83)#86
ximing merged 40 commits into
masterfrom
ralph/inbox-rich-text

Conversation

@ximing
Copy link
Copy Markdown
Owner

@ximing ximing commented Mar 18, 2026

Summary

Changes

  • apps/server/src/services/inbox.service.ts: 修改 create() 和 update() 方法,添加 convertPlainTextToTipTapJson 调用
  • apps/server/src/__tests__/inbox.service.test.ts: 新增 5 个测试用例验证纯文本转换

Test plan

  • pnpm lint 通过
  • pnpm typecheck 通过
  • 40 个 InboxService 单元测试全部通过

🤖 Generated with Claude Code

ximing and others added 30 commits March 17, 2026 21:12
Added storageKey column to echoe_media table for dynamic URL generation.

Changes:
- Added storageKey varchar(500) column to echoe_media schema
- Generated migration 0001_tired_forge.sql
- Column will store storage key format: echoe-media/{uid}/{filename}

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Modified uploadMedia method to save storageKey to database
- storageKey format: echoe-media/{uid}/{filename}
- Existing URL generation logic preserved (returns URL on upload)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Added url field to EchoeMediaDto (optional, expires in 6 hours)
- Modified listMedia to call generateAccessUrl for each record with storageKey
- Handles missing storageKey gracefully (backwards compatibility)
- Logs warning if URL generation fails

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Added getMediaMetadata method to EchoeMediaService that returns metadata with dynamic URL
- Added GET /api/v1/media/:filename/metadata endpoint to controller
- Returns EchoeMediaDto with temporary access URL (6 hour expiry)
- Validates user ownership and handles missing storageKey gracefully
- Preserves existing getMedia file-serving functionality

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Audited all media-related endpoints and methods
- Verified all metadata endpoints use dynamic URL generation:
  * GET / (listMedia) - returns URLs
  * GET /:filename/metadata (getMediaMetadata) - returns URL
  * POST /upload (uploadMedia) - returns URL
- File-serving endpoint (GET /:filename) serves buffers as designed
- No additional endpoints require updates
- All media URLs now use storageKey + generateAccessUrl pattern

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Add storageKey field to EchoeMediaDto interface in packages/dto to
include the storage key used for dynamic URL generation. This field
was already stored in the database but was missing from the DTO.

Fixes #82

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- POST /api/v1/import/apkg endpoint already implemented
- Converts Anki notes to Echoe format with field normalization
- Parses Anki templates (qfmt, afmt) and stores HTML content
- Parses CSS from models and applies to notes
- Creates notes in database with batch inserts
- Creates deck/folder structure from Anki decks
- Fixed missing storageKey field in EchoeMediaDto

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Verified that review history import is fully implemented in the Echoe import service:
- Revlog parsing integrated into importApkg() pipeline
- Converts Anki revlog entries to Echoe FSRS review records
- Preserves original timestamps from Anki revlog IDs
- Maintains interval and ease factor data with proper FSRS conversion
- All tests passing (8/8 in echoe-import.service.test.ts)
- Typecheck passes for both server and web

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Upload media files BEFORE importing notes to get filename mapping
- Build mapping from original filenames to stored filenames (timestamp-hash format)
- Update replaceMediaReferences() to handle both [sound:...] and <img src="..."> patterns
- Replace media references with stored filenames in note content
- Support common media types: images (jpg, png, gif, svg), audio (mp3, wav, ogg), video (mp4, webm)
- Typecheck passes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add .apkg file format validation (ZIP structure check)
- Add corrupted SQLite database error handling with integrity checks
- Add graceful handling for missing/corrupted media files
- Improve error messages with user-friendly suggestions
- Add detailed error categorization (general, media, etc.)
- Update frontend to display error details with collapsible list
- Use shared ImportResultDto and ImportErrorDetailDto types

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- 在 CreateInboxDto 和 UpdateInboxDto 中添加 frontJson 和 backJson 可选字段
- 在 InboxService 中添加 convertPlainTextToTipTapJson() 方法用于纯文本转换
- 更新 InboxService 的 create 和 update 方法,当收到 JSON 时直接序列化为 HTML,当收到纯文本时保持原样
- JSON 格式优先于纯文本(当两者都提供时使用 JSON)
- 添加完整的单元测试覆盖转换逻辑
- 所有测试通过,typecheck 通过

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- 将 CreateInboxDialog 中的 textarea 替换为 RichTextEditor
- 添加 frontJson 和 backJson 状态来跟踪 TipTap JSON 格式
- RichTextEditor 的 onChange 回调同时返回 HTML 和 JSON
- 提交时将 JSON 格式内容传递到 API(通过已更新的 CreateInboxDto)
- 支持从剪贴板粘贴富文本(RichTextEditor 内置支持)
- Typecheck passes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- 将 EditInboxDialog 中的 textarea 替换为 RichTextEditor
- 添加 frontJson 和 backJson 状态来跟踪 TipTap JSON 格式
- RichTextEditor 加载现有内容(支持纯文本和 HTML)
- RichTextEditor 自动处理纯文本内容(RichTextRenderer 内部转换)
- 提交时将 JSON 格式内容传递到 API
- 支持从剪贴板粘贴富文本(RichTextEditor 内置支持)
- Typecheck passes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
ximing and others added 10 commits March 18, 2026 09:05
- 添加 truncateContent 辅助函数用于截断 HTML 内容为纯文本预览
- 在 Inbox 列表中使用 truncateContent 显示内容摘要(前 100 字符)
- 自动去除 HTML 标签,显示纯文本预览
- 对于纯文本历史数据,truncateContent 能正确处理
- 添加 line-clamp-2 CSS 类限制显示为最多 2 行
- Typecheck passes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Added RichTextRenderer import to inbox-page.tsx
- Updated ConvertToCardDialog to display rich text preview of front and back content
- Expanded dialog width to max-w-2xl and added scrolling for long content
- Rich text content is already preserved during conversion (backend uses HTML format)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Modify inbox schema to store front/back as JSON columns
- Update InboxService to store JSON directly instead of converting to HTML
- Add convertToPlainText method for card conversion (serializes JSON to plain text)
- Update InboxToCardController to use convertToPlainText when building note fields
- Update DTOs to accept JSON or string types
- Add tests for JSON storage behavior
- Generate migration for schema change

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- InboxService.create() 方法现在会将纯文本 front/back 转换为 TipTap JSON 格式
- InboxService.update() 方法同样支持纯文本自动转换
- 如果提供了 frontJson/backJson,则优先使用 JSON(保持原有行为)
- 新增单元测试验证纯文本转换路径

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove redundant front/back HTML string state from CreateInboxDialog
- Remove redundant front/back HTML string state from EditInboxDialog
- Update RichTextEditor onChange to only set frontJson/backJson
- Add helper functions to handle content (string | JSON) conversion
- Make front/back optional in DTOs when JSON is provided

Closes #85

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 注入 InboxService 并使用 convertToPlainText 方法转换 front/back 字段
- 修复 getL1Context 中的相似度计算,正确处理 JSON 格式内容
- 修复 buildPrompt 中的 AI 提示构建,正确传递纯文本内容
- 修复 formatInboxContent 中的内容格式化
- 修复 generateDailyReport 中的日常报告生成
- 修复 organizeInbox 中的原始内容提取(包括 fallback 路径)
- 更新测试 mock 数据使用 TipTap JSON 格式

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 将 CreateInboxParams 的 front 和 back 改为可选字段
- 添加业务校验:创建时必须提供 frontJson 或 front 之一
- 修复 TypeScript 编译错误

Refs #88

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ximing ximing merged commit 0d07165 into master Mar 19, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant