Commit graph

13 commits

Author SHA1 Message Date
yushen
4036d64996 fix(attachment): use UUIDv7 as S3 key and link attachments on issue/comment creation
- Use google/uuid NewV7() for attachment ID and S3 file key instead of
  random hex, so the S3 object name matches the attachment record ID
- Add LinkAttachmentsToIssue query to associate orphaned attachments
  with a newly created issue
- Pass attachment_ids in CreateIssue request so uploads during issue
  creation (before the issue exists) get linked after commit
- Collect and pass attachment IDs in comment-input and reply-input
  so comment creation properly links uploaded files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 07:55:17 +08:00
Naiyuan Qing
27e58d91af refactor(editor): unify editor into features/editor with single markdown pipeline
Replace three divergent data paths (Marked HTML loading, regex post-processing
saving, separate paste parsing) with one symmetric path through @tiptap/markdown.

Key changes:
- Create features/editor/ module with ContentEditor (unified edit+readonly)
  and TitleEditor, replacing components/common/ editor files
- Load content via contentType: 'markdown' instead of markdownToHtml() hack
- Save content via editor.getMarkdown() directly, no post-processing
- Merge RichTextEditor + ReadonlyEditor into single ContentEditor with
  editable prop
- Extract extensions into separate modules (mention, file-upload,
  markdown-paste, submit-shortcut, code-block-view)
- Extract shared preprocessMentionShortcodes to components/markdown/mentions.ts
- Add copyMarkdown utility for clipboard operations
- Upgrade all @tiptap packages from 3.20.5 to 3.22.1 (lexer isolation fix,
  HTML entity roundtrip fix, table alignment support)
- Delete markdownToHtml.ts, readonly-editor.tsx, and 10 old component files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:28:29 +08:00
Naiyuan Qing
7cc4e63e0e fix(editor): unify image upload flow for paste and button
Paste/drop and attachment button previously used separate upload paths.
The button uploaded first then called insertFile (which replaced the
current selection), while paste inserted a blob preview first. This
caused the second image to overwrite the first when both were used.

Now both paths share the same flow via uploadAndInsertFile():
blob preview with uploading animation → background upload → replace URL.

- Extract shared uploadAndInsertFile() function
- Replace insertFile ref method with uploadFile (inserts at doc end)
- Simplify FileUploadButton to onSelect(file) — no more onUpload/onInsert
- Wire onUploadFile in comment edit mode (was missing, upload was no-op)
- Unify image border-radius CSS for both editing and readonly modes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:02:28 +08:00
Naiyuan Qing
9a37af4ca1 feat(ui): editor UX improvements — lowlight, upload, emoji, comment editing
- New QuickEmojiPicker: shared SmilePlus + 8 quick emojis + full picker
- New FileUploadButton: reusable Paperclip upload trigger
- New CodeBlockView: React NodeView with language label + copy button
- CodeBlockLowlight: syntax highlighting in editor (replaces plain codeBlock)
- ReactionBar: brand-tinted pill styles, hideAddButton prop
- Comment header: emoji picker + three-dot menu in top-right
- Comment edit: inline editing with brand border, blur-to-save, Escape-to-cancel
- RichTextEditor: add onBlur prop, markdown paste extension
- Create issue: upload button in footer
- Issue detail: upload button next to reaction bar
- Comment/reply: use FileUploadButton, loading spinners, no optimistic updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:37:53 +08:00
yushen
79cd2a3a5d fix(upload): link attachments to comments via client-side ID tracking
Instead of regex-parsing markdown content to find attachment URLs
(fragile), the frontend now tracks uploaded attachment IDs and sends
them with the comment creation request. The backend links them by ID.

Frontend: upload returns attachment ID, comment/reply inputs collect
IDs during editing session, pass as attachment_ids on submit.
Backend: CreateComment accepts attachment_ids, links by ID+issue scope.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 16:47:27 +08:00
Naiyuan Qing
8fae493f01 merge: resolve conflicts with main (file upload support)
- Merge main's file upload (Image extension, Paperclip, useFileUpload)
- Keep our mention/markdown/TitleEditor changes
- Apply RichTextEditor edit/display to main's Collapsible CommentCard layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 16:07:45 +08:00
Naiyuan Qing
98829fad29 fix(comments): replace optimistic updates with loading state
- Remove temp-xxx optimistic inserts from submitComment/submitReply
- Wait for API response, then insert real comment into timeline
- Add Loader2 spinner to comment/reply submit buttons during loading
- Remove hover card from Markdown.tsx (will be handled via NodeView later)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 16:03:13 +08:00
yushen
9e23fb76fc fix(upload): harden upload flow — sanitize filenames, refresh CF cookies, deduplicate handlers
- Sanitize Content-Disposition filenames to prevent header injection (strip control chars, quotes, semicolons)
- Add CloudFront cookie refresh middleware so cookies are re-issued when expired
- Log errors in groupAttachments instead of silently swallowing them
- Move useFileUpload hook to shared/hooks/ per project architecture conventions
- Add uploadWithToast helper to deduplicate try/catch/toast pattern across 3 components
- Refactor ApiClient.uploadFile to reuse auth headers, 401 handling, and error parsing
- Allow empty MIME types client-side (let server sniff and decide)
- Constrain Image extension max-width in rich-text-editor to prevent layout overflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:52:40 +08:00
yushen
15f96468be feat(upload): add attachment table for tracking uploaded files
- Add attachment table with workspace/issue/comment associations
- Upload handler creates attachment record when workspace context exists
- Add GET /api/issues/{id}/attachments and DELETE /api/attachments/{id}
- Frontend passes issueId context during uploads for tracking
- Add Attachment type, listAttachments, deleteAttachment to API client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:29:41 +08:00
yushen
423aa38888 feat(upload): add file upload UI — avatar, editor paste/drop, attachments
- Add uploadFile method to ApiClient (FormData + 401 handling)
- Add useFileUpload hook with client-side validation
- ActorAvatar renders actual avatar images with fallback to initials
- Account settings: replace URL input with clickable avatar upload
- RichTextEditor: add Image extension, paste/drop/insertFile support
- Markdown renderer: add img component for uploaded images
- CommentInput & ReplyInput: add paperclip button for file attachments
- Issue description: paste/drop file upload support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:17:54 +08:00
Naiyuan Qing
cd34b82454 fix(issues): UI polish batch — comment input, card gap, board title, activity coalescing
- CommentInput: remove border-t divider, position submit button inside
  editor area (bottom-right) for cleaner look
- CommentCard: add !gap-0 to override Card's default gap-4
- CommentInput/ReplyInput: strip trailing empty lines from markdown
  before submit to prevent extra blank lines in rendered comments
- BoardCard: use normal text color for title instead of muted+hover
- Timeline: coalesce same actor + same action within 2 min window,
  keeping only the final result (e.g. 5 status changes → 1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:22:43 +08:00
Naiyuan Qing
b0a6489ac6 fix(ui): polish issue detail — wider content, thinner comment border, smaller props text
Increase content max-width to 4xl, use ring-only border on comment
input, and reduce property sidebar value text to text-xs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 17:51:02 +08:00
Naiyuan Qing
5d2e62ccde fix(comments): fix padding, separate CommentInput from ReplyInput
- Remove Card double padding (Card py-4 + inner px-4 was too much, override with !py-0)
- Use lighter border-border/50 for reply separators inside Card
- Create CommentInput component for bottom "Leave a comment" — no avatar, full
  width editor, submit button in footer row below editor
- ReplyInput stays for in-card "Leave a reply" — has avatar, compact inline layout
- Two different components for two different use cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 23:13:03 +08:00