Commit Graph

29 Commits

Author SHA1 Message Date
ZhenYi
74f38d0d42 feat(i18n): initialize i18n infrastructure 2026-04-27 18:19:10 +08:00
ZhenYi
1deea4c671 fix(frontend): resolve TypeScript type errors in repository pages
- commits.tsx: fix Unix timestamp (time_secs) not multiplied by 1000
- RoomMessageSearch.tsx: add explicit generic type for resp.data
- RoomPinPanel.tsx: make sender_id optional (string | null)
- message-list.tsx: remove unused index variable in map
- repository-context.tsx: use correct RepoInfo field names
- use-audio-recording.ts: use audioStream state instead of undefined var
- universal-ws.ts: rename unused id to _id
2026-04-27 16:39:43 +08:00
ZhenYi
09645d8641 fix: resolve multiple bugs across backend and frontend
Security fixes:
- Remove WS token from plaintext log output (ws_universal.rs)
- Replace weak LCG PRNG with rand::thread_rng() for access key generation
- Add project membership check to issue triage endpoint (prevent unauthorized AI usage)
- Validate deepLinkUrl to prevent javascript: navigation (XSS defense-in-depth)

Data integrity fixes:
- Fix UUID truncation in AI model sync (as_u128() as i64 -> timestamp_millis)
- Wrap PR cascade delete in database transaction
- Add missing cascade deletes for room_message_reaction, room_message_edit_history, room_notifications
- Fix N+1 query for last_commit_times (single grouped query instead of per-repo)

Panic prevention:
- Replace unwrap() with safe fallbacks in health/metrics endpoints (email, git-hook apps)
- Replace unwrap() in access key scopes serialization
- Replace expect() in tool executor result map with synthetic error
- Replace expect() in log level parsing with default fallback

Logic bugs:
- Fix users_online metric double-decrement (decrement only when count reaches 0)
- Fix Map iteration + deletion bug in universal-ws.ts onclose handler
- Fix stale audioStream reference in catch block (use local stream variable)
- Add missing reInit event cleanup in carousel.tsx
- Fix email retry backoff integer overflow ((1 << i) as u64 -> 1u64 << i)

React fixes:
- Use message.id instead of index as key in message-list
- Add audio stream cleanup on unmount in use-audio-recording
2026-04-27 13:54:21 +08:00
ZhenYi
cce9d216b8 fix: resolve 4 remaining "design decision" bugs
- SSH rate limiter: wire SshRateLimiter into SSHServer with IP-based
  rate limiting on new_client connections
- Room startup: cap initial room load at 1000 via limit() to prevent
  resource exhaustion on large instances
- WS token exposure: only include token in URL for cross-origin
  connections; same-origin web clients authenticate via secure cookies
- CSRF: confirmed SameSite::Lax + Secure + HttpOnly are all set
  (session config defaults)
2026-04-27 11:20:38 +08:00
ZhenYi
bdb5393835 fix: resolve 30+ bugs from security audit
Critical:
- CORS: replace allow_any_origin + credentials with env-configured origins
- XSS: escape HTML before dangerouslySetInnerHTML in search results
- Path traversal: sanitize storage keys to reject ".." components
- Auth missing: add Session requirement to git init/open/is-repo endpoints
- Transaction: wrap issue cascade delete in DB transaction

High:
- Mutex poisoning: replace unwrap() with poison-recovering guards
- Drop tokio::spawn: use runtime handle or fallback thread for lock release
- Redis KEYS: replace with non-blocking SCAN for typing events
- SSH panic: handle missing stdin/stdout/stderr gracefully
- LFS auth: remove x-user-uid header injection vector, generate per-request tokens

Medium:
- Memory leak: remove Box::leak in provider normalization
- Race conditions: query closed count directly instead of subtraction
- Silent failures: add tracing::warn for AI tasks, room events, activity logs
- Frontend nav: sync activeRoomId when initialRoomId prop changes
- Duplicate nav: remove redundant setActiveRoom in delete handler
- Callback conflict: skip undefined values in updateCallbacks merge
- Stale closure: use wsClient state instead of wsClientRef.current in useMemo

Low:
- Captcha: validate captcha not empty before login submission
- Broadcast capacity: reduce from 100K to 1000
- Error handling: add try/catch for removeMember and updateMemberRole
- Loading state: show placeholder instead of null in RepositoryContextProvider
- WebSocket: add heartbeat ping and jitter to reconnect backoff
2026-04-27 10:57:23 +08:00
ZhenYi
a26551343c fix(frontend): refresh WS token after connection failures and handle AI/repo events
Clear wsToken on auth-related close codes (3000-4999), connection
timeout, and after 3 consecutive reconnect failures so the next connect
attempt fetches a fresh token. Add onRoomAiUpdated and onRepoChanged
callbacks that re-fetch AI configs and repo list when pushed via WS.
Fix AI member list to never display raw UUID.
2026-04-26 23:59:07 +08:00
ZhenYi
c8eba28e7a feat(frontend): add repo type to mention autocomplete system
Add 'repo' to MentionType across all editor types, include repos in the
@ trigger pool, add repo badge (green chip), Repos section in the
mention dropdown, and MentionBadge styles. Wire projectRepos from
room context into IMEditor mentionItems.
2026-04-26 23:58:59 +08:00
ZhenYi
07e74c230c feat: thinking_content column + first-project budget logic
Some checks are pending
CI / Rust Lint & Check (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Frontend Lint & Type Check (push) Waiting to run
CI / Frontend Build (push) Blocked by required conditions
- Add thinking_content column to room_message table
- Migration for thinking_content column
- ws-protocol update with streaming chunk types
- Billing: first project gets $10, first workspace gets $30
- Subsequent projects/workspaces get $0 budget
2026-04-26 13:11:06 +08:00
ZhenYi
61210da7a1 feat(frontend): typing indicator with AI/human split, page visibility reconnect
- TypingUsers state split by sender_type: AI vs human typing
- AI typing shows "{Name} is thinking..." with accent color
- Human typing shows "{Name} is typing..." with muted style
- AI typing relies on backend 60s TTL stop (no client-side 4s fallback)
- Add Page Visibility API to reconnect WS on tab become visible
- Add debug logs for typing flow tracing
- Pass sender_type through WS room.typing event routing
2026-04-25 22:45:11 +08:00
ZhenYi
73ba6329ea fix(frontend): prevent typing.stop on editor init, add typing display
- MessageInput: ignore empty text in handleEditorUpdate to avoid
  TipTap's onUpdate("") on init clearing the typing state
- DiscordChatPanel: show typing indicator when other users are typing
- room-context: wire onTypingStart/Stop into ws callbacks
2026-04-25 20:09:03 +08:00
ZhenYi
dfa5f7664a feat: add notification drawer, command registry, keyboard shortcuts, hooks
New components:
- NotificationDrawer: global bell button with unread badge + Sheet drawer
- CommandPalette: Cmd+K / Ctrl+Alt+F command palette with real API data
- KeyboardShortcutsSheet: ? shortcut reference sheet
- GlobalNavigationShortcuts: g+n/i/r/m two-key navigation
- useNotification: real-time notification management hook
- useCommandRegistry: global command registration hook
- useKeyboardShortcut: keyboard shortcut formatting hook
- useTypingIndicator: unified typing indicator hook
- LinkPreview / CodeBlock / CodeReference: code-aware chat rendering
- ContentRenderer: unified content rendering
- MiniChat: compact inline chat component
- MentionBadge: @mention badge renderer

New libs:
- libs/api/agent/issue_triage.rs: AI issue triage API endpoint
- libs/service/agent/issue_triage.rs: AI triage service
- src/lib/mention.ts: mention parsing and rendering
- src/lib/link-unfurl.ts: URL pattern detection
- src/lib/code-lang-detect.ts: code language detection
- src/lib/code-ref-parser.ts: line-level code reference parsing
2026-04-25 09:53:49 +08:00
ZhenYi
616c0c0e88 fix(room): scroll-to-bottom logic and AI sender display name
- Remove duplicate smooth scroll effect from DiscordChatPanel; handle
  all scroll logic in MessageList instead
- MessageList: track isInitialLoadRef to instant-jump to bottom on
  first load (no animation), and only auto-scroll for new messages
  when user is already near the bottom
- sender.ts: getSenderDisplayName rejects UUID values and falls back
  to 'AI' for AI messages; getSenderModelId uses display_name
2026-04-25 09:52:58 +08:00
ZhenYi
59640c6f44 feat(ws-client): add TypingStart/TypingStop protocol types and client handlers
ws-protocol.ts: TypingStartPayload/TypingStopPayload interfaces,
WsEventPayload union types.

room-ws-client.ts: onTypingStart/onTypingStop callbacks, sendTyping()
method, event dispatch for typing.start/typing_start.

editor/types.ts: special_here/special_channel MentionType + description
field on MentionItem.
2026-04-24 00:04:36 +08:00
ZhenYi
4d3afc5e71 fix(frontend): Discord UI polish, room streaming and kanban improvements
- DiscordChatPanel: update threading layout, message list padding
- DiscordMemberList: role colors, online status indicators
- RoomThreadPanel, MessageBubble, MessageInput: UI refinements
- IMEditor: editor state improvements
- KanbanCard, KanbanColumn: card layout, column styling
- room-context.tsx, room-ws-client.ts, ws-protocol.ts: streaming
- pull-request-detail.tsx: PR review improvements
- Add RoomPinPanel component
2026-04-21 22:31:48 +08:00
ZhenYi
e43d9fc8bf feat(frontend): add attachment_ids to message creation flow and types 2026-04-20 19:33:09 +08:00
ZhenYi
6f6f57f062 feat(frontend): add push notification hooks, image compression, and update room/chat components 2026-04-20 15:45:47 +08:00
ZhenYi
63c75ad453 feat(room): add category creation and drag-to-assign for channels
Some checks are pending
CI / Rust Lint & Check (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Frontend Lint & Type Check (push) Waiting to run
CI / Frontend Build (push) Blocked by required conditions
- Rewrite DiscordChannelSidebar with @dnd-kit drag-and-drop:
  rooms are sortable within categories; dragging onto a different
  category header assigns the room to that category
- Add inline 'Add Category' button: Enter/Esc to confirm/cancel
- Wire category create/move handlers in room.tsx via RoomContext
- Fix onAiStreamChunk to accumulate content properly and avoid
  redundant re-renders during AI streaming (dedup guard)
- No backend changes needed: category CRUD and room category update
  endpoints were already wired
2026-04-19 16:44:31 +08:00
ZhenYi
821b0e998d refactor(room): Discord layout and room WebSocket client refactor
- Refactor room-context.tsx with improved WebSocket state management
- Enhance room-ws-client.ts with reconnect logic and message handling
- Update Discord layout components with message editor improvements
- Add WebSocket universal endpoint support in ws_universal.rs
2026-04-18 19:05:21 +08:00
ZhenYi
00a5369fe1 feat(frontend): Discord layout + AI Studio theme + Room Settings
Some checks are pending
CI / Rust Lint & Check (push) Waiting to run
CI / Rust Tests (push) Waiting to run
CI / Frontend Lint & Type Check (push) Waiting to run
CI / Frontend Build (push) Blocked by required conditions
Frontend:
- Add Discord-style 3-column layout (server icons / channel sidebar / chat)
- AI Studio design system: new CSS token palette (--room-* vars)
- Replace all hardcoded Discord colors with CSS variable tokens
- Add RoomSettingsPanel (name, visibility, AI model management)
- Settings + Member list panels mutually exclusive (don't overlap)
- AI models shown at top of member list with green accent
- Fix TS errors: TipTap SuggestionOptions, unused imports, StarterKit options
- Remove MentionInput, MentionPopover, old room components (废弃代码清理)

Backend:
- RoomAiResponse returns model_name from agents.model JOIN
- room_ai_list and room_ai_upsert fetch model name for each config
- AiConfigData ws-protocol interface updated with model_name

Note: RoomSettingsPanel UI still uses shadcn defaults (未完全迁移到AI Studio)
2026-04-18 16:59:36 +08:00
ZhenYi
168fdc4da8 refactor(frontend): rewrite mention system with clean state architecture
Replace module-level refs and circular dependencies with a centralized
mention state hook (useMentionState) and shared types (mention-types).

- Introduce mention-types.ts for shared types (avoid circular deps)
- Introduce mention-state.ts with useMentionState hook (replace global refs)
- Rewrite MentionInput as contenteditable with AST-based mention rendering
- Rewrite MentionPopover to use props-only communication (no module refs)
- Fix MessageMentions import cycle (mention-types instead of MentionPopover)
- Wire ChatInputArea to useMentionState with proper cursor handling
2026-04-18 01:26:35 +08:00
ZhenYi
3cd5b3003c fix(room): fix mention Enter key by reading textarea DOM directly
Problem: showMentionPopover state is stale when handleKeyDown fires
immediately after handleChange (both in same event loop), causing Enter
to be silently swallowed.

Solution:
- Read textarea.value directly in handleKeyDown to detect @ mentions
- Module-level refs (mentionSelectedIdxRef, mentionVisibleRef) share
  selection state between MentionPopover and ChatInputArea
- handleMentionSelect reads DOM instead of relying on props state
2026-04-18 00:25:03 +08:00
ZhenYi
26682973e7 feat(room): redesign mention system with AST-based format
Backend:
- Add MENTION_TAG_RE matching new `<mention type="..." id="...">label</mention>` format
- Extend extract_mentions() and resolve_mentions() to parse new format (legacy backward-compatible)

Frontend:
- New src/lib/mention-ast.ts: AST types (TextNode, MentionNode, AiActionNode),
  parse() and serialize() functions for AST↔HTML conversion
- MentionPopover: load @repository: from project repos, @ai: from room_ai configs
  (not room members); output new HTML format with ID instead of label
- MessageMentions: use AST parse() for rendering (falls back to legacy parser)
- ChatInputArea: insertMention now produces `<mention type="user" id="...">label</mention>`
- RoomParticipantsPanel: onMention passes member UUID to insertMention
- RoomContext: add projectRepos and roomAiConfigs for mention data sources
2026-04-17 23:43:26 +08:00
ZhenYi
047782e585 fix(room): handle reaction updates in onRoomMessage WS handler
Root cause: publish_reaction_event sends a RoomMessageEvent (not
reaction_added) with reactions field set. The onRoomMessage handler
previously returned prev immediately when a duplicate ID was found,
skipping the reaction update entirely.

Fix:
- Add reactions field to RoomMessagePayload so TypeScript knows it's there
- When a duplicate message ID is found AND payload carries reactions,
  update the existing message's reactions field instead of ignoring
- ReactionItem and ReactionGroup have identical shapes, so assignment works
2026-04-17 22:37:20 +08:00
ZhenYi
a1ddb5d5bc fix(room): add HTTP batch reactions endpoint and clean up dead code
Backend:
- Add reaction_batch handler: GET /api/rooms/{room_id}/messages/reactions/batch?message_ids=...
- Register route in libs/api/room/mod.rs
- Backend already had message_reactions_batch service method, just needed HTTP exposure

Frontend:
- Add ReactionListData import to room-context.tsx
- Fix thisLoadReactions client type (was using broken NonNullable<ReturnType<>>)
- Remove unused oldRoomId variable
- Delete unused useRoomWs.ts hook (RoomWsClient has no on/off methods)
- Remove unused EmojiPicker function and old manual overlay picker from RoomMessageBubble
- Remove unused savedToken variable in room-ws-client
2026-04-17 22:12:10 +08:00
ZhenYi
b70d91866c fix(room-ws): try reconnect with existing token before requesting new
On auto-reconnect (scheduleReconnect), attempt connection with the stored
wsToken first. If the WS closes immediately (server rejected the token),
fall back to fetching a fresh token and retrying. Only requests a new
token when the existing one fails or when connect() is called manually
with forceNewToken=true.
2026-04-17 21:40:07 +08:00
ZhenYi
309bc50e86 perf(room): prioritize IndexedDB cache for instant loads
- Remove clearRoomMessages on room switch: IDB cache now persists across
  visits, enabling instant re-entry without API round-trip
- Increase initial API limit from 50 → 200: more messages cached upfront
- Add loadOlderMessagesFromIdb: uses 'by_room_seq' compound index to
  serve scroll-back history from IDB without API call
- loadMore now tries IDB first before falling back to API
2026-04-17 21:36:55 +08:00
ZhenYi
7416f37cec fix(room): prevent double-send, log resubscribe errors, dim pending messages
- sendMessage: guard with sendingRef to prevent concurrent in-flight
  sends (was missing — rapid clicks could create duplicate messages)
- resubscribeAll: log at warn level instead of silently swallowing,
  so operators can observe auth expiry or persistent failure patterns
- RoomMessageBubble: apply opacity-60 when isPending or isFailed,
  and hide action toolbar for pending messages (can't react/act on
  unconfirmed messages)
2026-04-16 19:29:34 +08:00
ZhenYi
c89f01b718 feat(room): improve robustness — optimistic send, atomic seq, jitter reconnect
Backend:
- Atomic seq assignment via Redis Lua script: INCR + GET run atomically
  inside a Lua script, preventing duplicate seqs under concurrent requests.
  DB reconciliation only triggers on cross-server handoff (rare path).
- Broadcast channel capacity: 10,000 → 100,000 to prevent message drops
  under high-throughput rooms.

Frontend:
- Optimistic sendMessage: adds message to UI immediately (marked
  isOptimistic=true) so user sees it instantly. Replaces with
  server-confirmed message on success, marks as isOptimisticError on
  failure. Fire-and-forget to IndexedDB for persistence.
- Seq-based dedup in onRoomMessage: replaces optimistic message by
  matching seq, preventing duplicates when WS arrives before REST confirm.
- Reconnect jitter: replaced deterministic backoff with full jitter
  (random within backoff window), preventing thundering herd on server
  restart.
- Visual WS status dot in room header: green=connected, amber
  (pulsing)=connecting, red=error/disconnected.
- isPending check extended to cover both old 'temp-' prefix and new
  isOptimistic flag, showing 'Sending...' / 'Failed' badges.
2026-04-16 19:23:06 +08:00
ZhenYi
93cfff9738 init 2026-04-15 09:08:09 +08:00