Commit Graph

2606 Commits

Author SHA1 Message Date
Steven 6b37fcc01b fix: improve KaTeX and Mermaid error handling and overflow 2026-03-02 21:11:07 +08:00
Steven 7f753bf6d7 refactor(theme): consolidate theme system and polish dark mode colors
- Reduce themes from 6 to 4: system, light, dark, paper
  - Remove midnight (too similar to dark) and whitewall (too similar to light)
  - Drop all unused tokens: chart-*, sidebar-primary, sidebar-border,
    sidebar-ring, shadow-2xs, tracking-normal, spacing
  - Remove redundant @theme inline blocks from dynamic theme files
    (Tailwind only processes them at compile time, not at runtime)
  - Move shared tokens (fonts, radius) to default.css only

- Polish dark mode color palette
  - Consistent cool-slate hue (h265) across all surfaces
  - Proper surface layering: sidebar(0.07) → bg(0.09) → card(0.13) →
    popover(0.17) → secondary(0.19) → muted(0.21) → accent(0.22)
  - Foreground softened to 0.82 (from 0.9) — comfortable off-white
  - Accent more chromatic than muted (0.012 vs 0.008) — hover feels interactive
  - Popover elevated above card (0.17 vs 0.13) — floating elements visible
  - Destructive more vivid (0.62/c0.20) — clearly red on dark surfaces
  - Input border (0.25) more distinct than layout border (0.21)
  - Sidebar foreground (0.68) dimmer than content foreground (0.82)

- Fix inline code background invisible in dark mode (#5674)
  - Muted was same lightness as card — increased to 0.21 for clear contrast
2026-03-02 09:30:57 +08:00
Johnny 16576be111 chore: polish MemoDetailSidebar for consistent structure and styles 2026-03-01 21:22:11 +08:00
Johnny 53a8a436ba chore: fix frontend linter 2026-03-01 19:51:52 +08:00
Johnny 026ea92f7b fix(editor): show newly linked memos when editing a memo with attachments 2026-03-01 19:16:57 +08:00
mostapko 104d2ec0a6
feat(ui): allow navigating between images with arrows in preview dialog (#5669) 2026-03-01 11:30:47 +08:00
Steven 664b8c5629 fix(ui): unify metadata component styles across MemoView and MemoEditor
Align header padding, text color, background tokens, and item row styles
across CodeBlock, AttachmentList, RelationList, and LocationDisplay so all
metadata panels follow the same visual pattern.
2026-02-26 23:18:11 +08:00
milvasic 6b0736b293
fix(ui): show comment editor above the comment list (#5662) 2026-02-26 08:50:27 +08:00
Steven f7ac6a0191 refactor: auth token refresh flow and simplify user hooks 2026-02-25 22:08:18 +08:00
Steven 333c9df233 fix(web): refresh memo detail cache after editor save 2026-02-25 20:03:09 +08:00
Steven bbdc998646 fix(web): use BroadcastChannel to sync token refreshes across tabs
When multiple tabs are open and a token expires, each tab independently
attempts a refresh. With server-side token rotation this causes all but
the first tab to fail, logging the user out.

Add a BroadcastChannel (memos_token_sync) so that when any tab
successfully refreshes, it broadcasts the new token to all other tabs.
Receiving tabs adopt the token in-memory immediately, skipping their own
refresh request and avoiding conflicts with token rotation.

Falls back gracefully when BroadcastChannel is unavailable (e.g. some
privacy modes).
2026-02-24 23:31:59 +08:00
Steven 9ecd7b876b fix(web): fix spurious logout on page reload with expired access token
Two bugs caused users to be redirected to /auth too frequently:

1. Race condition in Promise.all([initInstance(), initAuth()]):
   initInstance() makes a gRPC request whose auth interceptor calls
   getAccessToken() synchronously. When the access token was expired,
   getAccessToken() eagerly deleted it from localStorage as a "cleanup"
   side-effect. By the time initAuth() ran and checked hasStoredToken(),
   localStorage was already empty, so it skipped the getCurrentUser()
   call and the token refresh cycle entirely — logging the user out even
   when the refresh-token cookie was still valid. Fix: remove the
   localStorage deletion from getAccessToken(); clearAccessToken()
   (called on confirmed auth failure and logout) handles proper cleanup.

2. React Query retry: 1 caused a second refresh+redirect attempt after
   auth failures. The auth interceptor already handles token refresh and
   request retry internally. If it still throws Unauthenticated, the
   redirect is already in flight — a React Query retry only fires another
   failed refresh and a redundant redirectOnAuthFailure() call. Fix: use
   a shouldRetry function that skips retries for Unauthenticated errors
   while keeping the existing once-retry behaviour for other errors.
2026-02-23 14:08:59 +08:00
Steven 03c30b8ccb fix(web): fix explore page showing private tags and improve stats hook
The explore page sidebar was showing tags from the current user's private
memos because the default ListMemos query applies a server-side OR filter
(creator_id == X || visibility in [...]), mixing private content in.

Fix by using a visibility-scoped ListMemos request in the explore context
so private memos are always excluded via the AND'd server auth filter.

Also consolidate two always-firing useMemos calls into one context-aware
query, unify activity stats computation with countBy across all branches,
and extract a toDateString helper to remove duplicated formatting logic.
2026-02-23 13:15:01 +08:00
Steven 1cea9b0a78 fix(web): make MonthNavigator month label reactive to language changes
Use useTranslation() hook instead of the static i18n import so that
the month label re-computes when the language changes.
2026-02-23 11:04:04 +08:00
Steven 17fc8383df fix(web): improve MemoEditor layout and timestamp popover styling 2026-02-23 10:20:00 +08:00
Steven c3061002f3 fix(web): persist auth token in localStorage for cross-tab sessions
Switch from sessionStorage to localStorage so the auth token survives
across tabs and browser restarts, matching standard platform behavior.
Also guard the signup redirect in App.tsx behind profileLoaded to avoid
a false redirect when the instance profile fetch fails.
2026-02-22 13:55:46 +08:00
Steven 4aaebc507e fix(web): skip GetCurrentUser on init when no token is stored
When no token exists in sessionStorage, AuthContext.initialize() was
still calling GetCurrentUser, triggering the auth interceptor to attempt
RefreshToken and retry — producing a burst of 5+ auth API calls in under
a second that reverse proxies with rate limiting (e.g. CrowdSec) flag as
brute force.

Add hasStoredToken() to auth-state and bail out of initialize() early
when there is definitively no session to restore. The refresh flow for
expired tokens is preserved since hasStoredToken() checks for presence
regardless of expiry.

Fixes #5647
2026-02-22 13:40:34 +08:00
Steven b8bca6bacf fix(web): scope task list item index to memo content container
The closest() selector was targeting a CSS class that never existed on
the container, causing fallback to document.body and collecting task
items across all visible memos. This caused index collisions when
multiple memos with todo lists were on the page.

Adds data-memo-content attribute to the container and updates the
selector accordingly.

Fixes #5635
2026-02-22 13:20:29 +08:00
Steven 2ab476ef2e fix(web): memoize useNavigateTo to prevent infinite view transition loop
Wrapping the returned function in useCallback prevents a new reference
on every render, which was causing an infinite startViewTransition loop
on the initial signup page (fresh install with no admin).

Fixes #5626
2026-02-22 13:16:59 +08:00
Steven 6eaab52e48 fix(web): handle delete memo errors and only run post-delete actions on success 2026-02-22 13:00:36 +08:00
milvasic 1d9d4cd797
fix: invalidate comments query on comment reaction and delete (#5641) 2026-02-22 12:58:56 +08:00
Kaki021 122ac94273
fix(web): ensure default memo visibility is correctly applied (#5623) 2026-02-14 16:58:58 +08:00
Steven 861b58cbf3 refactor(web): extract NsfwOverlay into standalone component with flexbox centering 2026-02-12 23:19:54 +08:00
Steven aeb1e5fe40 perf(web): eliminate redundant fetch when opening inline memo editor 2026-02-12 23:02:25 +08:00
Johnny 6402618c26 feat(web): replace EditableTimestamp with inline editor timestamp popover 2026-02-11 23:45:53 +08:00
Johnny 566fdccae6 fix(web): allow calendar year picker to go back to 1970 2026-02-11 22:58:25 +08:00
Johnny 177a74e82b fix: allow ampersand in tags to support compound tags 2026-02-11 22:55:45 +08:00
Steven 47e2ace690 fix(web): redirect to auth page instead of explore on session expiry
Fixes #5617
2026-02-10 21:15:44 +08:00
MuLingQwQ b5108b4f97
fix(web): calendar navigation should use current page path (#5605) 2026-02-09 20:40:12 +08:00
Johnny f827296d6b chore: fix broken links 2026-02-08 19:46:03 +08:00
Johnny c4176b4ef1 fix: videos attachment 2026-02-07 16:03:52 +08:00
Steven 81ef53b398 fix: prevent 401 errors on window focus when token expires
Fixes #5589

When the page returns from background to foreground after the JWT
token expires (~15 min), React Query's automatic refetch-on-focus
triggers multiple API calls simultaneously. These all fail with 401
Unauthorized, leaving the user with empty content.

Solution:
- Add useTokenRefreshOnFocus hook that listens to visibilitychange
- Proactively refresh token BEFORE React Query refetches
- Uses 2-minute buffer to catch expiring tokens early
- Graceful error handling - logs error but doesn't block

Changes:
- Created web/src/hooks/useTokenRefreshOnFocus.ts
- Updated isTokenExpired() to accept optional buffer parameter
- Exported refreshAccessToken() for use by the hook
- Integrated hook into AppInitializer (only when user authenticated)
2026-02-05 22:14:48 +08:00
Steven cf65f0867b refactor: remove hide-scrollbar utility
Removed the hide-scrollbar CSS class and all its usages throughout the
codebase. Hiding scrollbars can hurt UX by making it unclear when
content is scrollable.

Changes:
- Removed hide-scrollbar CSS definition from index.css
- Removed hide-scrollbar class from Navigation component (2 instances)
- Removed hide-scrollbar class from MemoDetailSidebar (2 instances)
- Removed hide-scrollbar class from TagsSection
- Removed hide-scrollbar class from ShortcutsSection

Components now use standard browser scrollbar behavior, which provides
better visual feedback to users about scrollable content. Modern
browsers already handle scrollbar appearance elegantly.
2026-02-04 20:24:54 +08:00
Steven 74b63b278c perf: disable tooltips in year calendar to fix lag
Fixed issue #5579 where the calendar selection dialog was very laggy.

The root cause was rendering ~365 individual Tooltip components when
opening the year calendar view (one per day with activity). This created
a huge number of DOM nodes and event listeners that caused significant
performance issues.

Changes:
- Added disableTooltips prop to MonthCalendar and CalendarCell components
- Disabled tooltips in YearCalendar's small month views
- Removed unnecessary TooltipProvider wrapper in YearCalendar
- Tooltips remain enabled in the default month calendar view

Performance improvements:
- Eliminates ~365 tooltip instances when dialog opens
- Reduces initial render time significantly
- Makes dialog interactions smooth and responsive

Users can still click on dates to drill down for details if needed.
2026-02-04 20:14:55 +08:00
Steven e7605d90da fix: shortcut edit button opens create dialog instead of edit dialog
Fixed issue #5576 where clicking the edit button on a shortcut would
incorrectly open a create dialog instead of an edit dialog.

The root cause was an incorrect useEffect that was watching the shortcut
state itself instead of the initialShortcut prop. When the dialog was
opened for editing, the state wasn't properly reinitializing with the
existing shortcut data.

Changed the useEffect to:
- Watch initialShortcut as the dependency
- Reinitialize the shortcut state when initialShortcut changes
- Properly distinguishes between create (initialShortcut undefined) and
  edit (initialShortcut has data) modes
2026-02-04 20:03:53 +08:00
memory_clear f9bf903eea
chore: update access token deletion message description for zh-Hans (#5573)
Signed-off-by: memory_clear <83893503+MemoryClear@users.noreply.github.com>
2026-02-04 19:54:51 +08:00
Steven 6bb383a4a5 fix: prevent CTRL+Enter save while editor is loading content (#5581)
- Add validation check for loading state before allowing save
- Prevents false "Content, attachment, or file required" error
- Occurs when user presses CTRL+Enter immediately after opening edit mode
- Editor state may still be loading when keyboard shortcut fires

Closes #5581
2026-02-03 23:55:07 +08:00
Steven b4fea8c647 fix: nested task list display and checkbox interaction (#5575)
- Fix nested task lists not showing proper indentation
- Use simple CSS cascade with [&_ul.contains-task-list]:ml-6
- Fix checkbox clicks toggling wrong tasks in nested lists
- Search from memo root container for global task indexing
- Remove complex selectors in favor of standard approach
- Match behavior of GitHub, Notion, and other platforms

Closes #5575
2026-02-03 23:45:45 +08:00
Steven cf0a285ef1 fix(auth): make PKCE optional for OAuth sign-in (#5570)
Fixes issue where OAuth sign-in fails with 'Cannot read properties of
undefined (reading 'digest')' when accessing Memos over HTTP.

The crypto.subtle API is only available in secure contexts (HTTPS or
localhost), but PKCE (RFC 7636) is optional per OAuth 2.0 standards.

Changes:
- Make PKCE generation optional with graceful fallback
- Use PKCE when crypto.subtle available (HTTPS/localhost)
- Fall back to standard OAuth flow when unavailable (HTTP)
- Log warning to console when PKCE unavailable
- Only include code_challenge in auth URL when PKCE enabled

The backend already supports optional PKCE (empty codeVerifier), so no
backend changes needed. This fix aligns frontend behavior with backend.

Benefits:
- OAuth sign-in works on HTTP deployments (reverse proxy scenarios)
- Enhanced security (PKCE) still used when HTTPS available
- Backward compatible with OAuth providers that don't support PKCE

Fixes #5570
2026-02-02 23:28:21 +08:00
Steven 29412f4690 refactor: simplify NSFW implementation by inlining logic
- Remove unused showNsfwContent prop (was only used in MemoDetail to pre-reveal NSFW, which defeats the purpose)
- Inline useNsfwContent hook logic directly into MemoView.tsx (only 3 lines, no reusability benefit)
- Delete web/src/components/MemoView/hooks/useNsfwContent.ts
- NSFW content now consistently starts hidden across all pages
- Maintains same user experience: click to reveal, no toggle back

This removes unnecessary indirection and prop threading while preserving functionality.
2026-02-02 22:06:04 +08:00
Steven 6db58fae11 fix: prevent private memos from disappearing during token refresh (#5565)
Root cause: enabled={isInitialized && !!user} prevented displaying cached
data when user auth state transitioned during token refresh.

Changes:
- Remove !!user check from Home page enabled condition
- Add clearAccessToken() in redirectOnAuthFailure for clean logout

Fixes #5565
2026-02-02 21:45:24 +08:00
Damian Jankowski c14843fa62
chore: view memo URL updated (#5569) 2026-02-02 20:39:33 +08:00
Steven 2db57b139a fix: handle pasted files in memo editor
Fixes #5568
2026-02-02 20:36:08 +08:00
Steven 0dbc35a2ba fix: restore access token creation flow
Fixes #5563
2026-02-02 20:31:22 +08:00
Steven 34c341c88c chore: fix update user request name in member dialog
Fixed #5564
2026-02-02 19:52:46 +08:00
Steven b8029c70ef chore: fix OAuth callback double-run state error 2026-02-02 09:07:55 +08:00
Ganesh M 27de96d440
fix(ui): math render (#5549) 2026-01-31 21:46:19 +08:00
Johnny 8cd9c591d4 chore: deprecate remove completed tasks action
- Remove menu item and dialog from MemoActionMenu
- Remove removeCompletedTasks() and hasCompletedTasks() utilities
- Remove translation keys from all 34 locale files
- Feature was not aligned with standard note-taking UX patterns
2026-01-31 21:03:05 +08:00
Johnny 5396c126b8 chore: extract task list class names to constants
- Add TASK_LIST_CLASS and TASK_LIST_ITEM_CLASS constants
- Replace hardcoded 'contains-task-list' and 'task-list-item' strings
- Improve maintainability and prevent typos
2026-01-31 20:53:55 +08:00
Johnny 97ba15450f chore: prevent unnecessary API calls when timestamp unchanged in MemoDetailSidebar
- Add same value check before updating createTime/updateTime
- Skip request if new timestamp equals current timestamp
- Simplify callback handlers and improve code readability
- Use .some() instead of .filter().length for cleaner code
2026-01-31 15:12:27 +08:00