Commit graph

2909 commits

Author SHA1 Message Date
Johnny
da81b6b48f
feat: persist and display client media metadata (#6180) 2026-08-11 23:47:21 +08:00
Chen, Ting-An
38bc02c3cc
chore(i18n): complete Traditional Chinese translations (#6177) 2026-08-11 18:33:15 +08:00
Johnny
9a928c2868
feat: insert attachment images into memo content (#6172) 2026-08-10 22:53:07 +08:00
Johnny
4e8b262d6d
refactor(memo-views): replace shortcuts with saved views (#6167) 2026-08-09 20:27:27 +08:00
kpab
6c22f9562b
i18n(ja): add missing Japanese translations (#6165) 2026-08-09 16:24:47 +08:00
Johnny
f75d49f9b6
fix(web): restore sidebar and outline behavior (#6161) 2026-08-08 08:58:07 +08:00
s4kura
e5ed6e7ec6
fix: align memo outline links with rendered headings (#6159) 2026-08-07 09:51:48 +08:00
Wally
3f567fda45
fix(web): keep share token on shared-memo attachment thumbnails (#6155) 2026-08-07 00:21:53 +08:00
johnnyjoygh
ec689c3a03 chore(sidebar): replace static mode with a library-content fallback
The rail had two navigation grammars for the same destinations: a
compact icon strip on recognized routes, and StaticNavigation — full
labeled rows — on everything else (/about, /403, /404, wildcard).
Crossing onto one of those pages rearranged the entire rail, and any
route nobody remembered to classify silently inherited the alternate
layout.

Make the frame invariant instead: header, global strip, contextual
content, footer — on every route. Pages without a specific sidebar
tenant now fall back to the default library content (the home
collection for signed-in users, explore for guests), following the
convention that error and utility pages render inside the persistent
shell rather than degrading it. The memo-detail route gets the same
fallback while the page has not yet published its descriptor, instead
of a blank scroll area.

Off the collection routes the library is display-plus-navigation, so
StatisticsView and TagsSection accept a navigationTarget: calendar and
tag clicks land on / or /explore with the filter in the query string,
which RootLayout's route-change effect would otherwise wipe.

StaticNavigation is deleted, About returns to living only in the user
menu (the showAbout prop is gone), and the guest strip now marks About
active on /about.
2026-08-07 00:04:04 +08:00
boojack
d722608947 fix(sidebar): stop the month calendar growing with the rail
Calendar cells were `aspect-square w-full`, so cell height was a pure
function of rail width. Dragging the rail from 224px to its 400px max
took the calendar block from 231px to 382px — a 65% jump that pushed
Views and Tags out of view, left a 12px numeral floating in a 50px box,
and turned the selected day into a 50x50 slab against a rail whose every
other row is 30px.

Split the cell in two: the button spans its whole column and takes the
pointer, while a chip inside it carries the fill, the ring and the today
dot, and stops growing at `maxSize`. A chip is therefore
`min(column, 30px)` — narrow rails are untouched (224px still yields
25px cells, 170px of grid) and wider ones spend the surplus on hit area
instead of height, holding the grid at 200px from 258px up.

The today dot stops detaching as a side effect: it is positioned against
a chip that no longer grows, rather than against a cell floor that
drifted 19px below the numeral at full width.
2026-08-06 22:59:21 +08:00
Johnny
abe87d9440
feat(sidebar): make the desktop rail resizable (#6148)
Signed-off-by: Johnny <johnnyjoygg@gmail.com>
2026-08-06 19:18:38 +08:00
boojack
5192732cee fix(web): keep task checkboxes controlled so check-all updates render
remark-gfm passes checked as undefined for unchecked task items, so the
Base UI Checkbox mounted uncontrolled and permanently ignored the
checked=true arriving after "Check all tasks" rewrote the memo content.
Coerce the prop to a boolean so the checkbox is always controlled.

Also drop the `${updateTime}` suffixes from memo React keys and the
comment editor cache key: a protobuf-es Timestamp stringifies to
"[object Object]", so the suffix was a constant — the keys were
effectively name-only already, and now say so honestly.

Fixes usememos/memos#6143
2026-08-06 09:09:37 +08:00
johnnyjoygh
4eee9a5fb4 chore(web): prune frontend dependencies
- Upgrade Base UI and i18n packages for TypeScript 7 support
- Remove unused runtime, type, and build dependencies
- Delete the orphaned UUID helper
2026-08-06 00:13:09 +08:00
johnnyjoygh
b930b1090a refactor(sidebar): reduce tooltip noise and refine tags
- Centralize tooltip timing and limit hints to useful content.\n- Simplify tag hierarchy presentation and align compact tag rows.
2026-08-05 23:52:13 +08:00
johnnyjoygh
6fbc81337f chore(sidebar): improve view and tag controls
- expose display settings from the Views header and refine layout controls
- add explicit tag list/tree navigation with accessible expansion behavior
- cover view persistence, menu behavior, and tag tree construction
2026-08-05 23:12:49 +08:00
johnnyjoygh
d14a542002 chore(web): add static navigation for default routes
- Show auth-aware common destinations when a route has no contextual sidebar.
- Restore About as a standalone page and route all About entries to it.
2026-08-05 21:46:34 +08:00
johnnyjoygh
83eab44695 fix(web): preserve memo scope across global navigation 2026-08-05 09:32:41 +08:00
Johnny
4d2667983f
feat(web): unify navigation in a route-aware sidebar (#6144) 2026-08-05 09:05:57 +08:00
Johnny
d617f652b0
fix(markdown): support word-internal apostrophes in tags (#6135) 2026-08-02 21:08:57 +08:00
Johnny
8648b97b9e
feat(user): define username format and mention syntax (#6134) 2026-08-02 19:57:16 +08:00
Johnny
c68e3d5bf9
feat(markdown): unify tag syntax and recognition (#6132) 2026-08-02 14:46:04 +08:00
boojack
571e0a3ff6 chore: scope memo providers to authenticated routes 2026-07-30 23:42:37 +08:00
boojack
b895a462da chore: update demo banner 2026-07-30 23:18:13 +08:00
boojack
dd18002b12 perf(sse): reduce redundant connections and fanout overhead
- Share one visible-tab connection across a browser and harden retries.\n- Preframe hub events, disconnect slow clients, and reset idle heartbeats.\n- Add cross-tab, concurrency, race, and fanout benchmark coverage.
2026-07-29 21:47:54 +08:00
boojack
c4221c6dfe fix(attachments): link media to source memos
Close https://github.com/usememos/memos/issues/6120
2026-07-29 09:06:42 +08:00
boojack
6f1ff135fb chore: add global quick-create entry
Use the authenticated navigation logo to open the existing focus-mode editor and preserve the draft cursor across reopenings.
2026-07-29 08:57:04 +08:00
johnnyjoygh
9328413797 fix(ui): keep select menus above parent popovers 2026-07-27 22:05:20 +08:00
johnnyjoygh
b4fefa94d7 fix(editor): restore Windows emoji input 2026-07-27 22:05:16 +08:00
johnnyjoygh
c536434b81 test(release): cover 0.30.0 changelog promises 2026-07-26 22:21:19 +08:00
johnnyjoygh
415a3ec73d fix(auth): enforce private instance access boundaries
- Resolve Gateway procedures from matched HTTP bindings before authorization.\n- Disable anonymous RSS on private instances.\n- Limit share-token access to the shared memo and its attachments.
2026-07-26 21:13:21 +08:00
johnnyjoygh
0d2cbd4f5a refactor: expose shared memo as memo resource
Rename GetMemoByShare to GetSharedMemo and move REST resolution to /api/v1/shares/{share_token}/memo.\n\nBREAKING CHANGE: remove GET /api/v1/shares/{share_id} and the GetMemoByShare RPC.
2026-07-26 21:13:18 +08:00
boojack
cc20327b8a chore(access-token): default expiration to never 2026-07-20 20:03:05 +08:00
boojack
b7d5d09f8a fix(api): update UID compatibility
Use the original UID format consistently across API, username, and store validation so UUID-based callers continue to work. Regenerate API documentation and add regression coverage for UUID memo IDs.
2026-07-20 19:41:55 +08:00
johnnyjoygh
249b331596 perf(web): reduce memo feed startup and rendering work 2026-07-20 00:15:26 +08:00
johnnyjoygh
06ecef33bc chore(web): update about page and access token settings 2026-07-19 22:23:51 +08:00
johnnyjoygh
6c1055f483 perf(web): deduplicate explore data requests
Share canonical user and memo query caches across creator, reaction, comment, mention, and relation lookups. Reuse list data for relation snippets and cover overlapping queries with regression tests.
2026-07-18 23:25:59 +08:00
johnnyjoygh
88c6ee8ebc perf(web): reduce demo bandwidth usage
- Cache fingerprinted frontend assets for one month.
- Load media, rich renderers, dialogs, and editors only when needed.
- Simplify seeded demo content and cover deferred-loading behavior.
2026-07-18 22:06:14 +08:00
johnnyjoygh
0bfeb91d50 fix(api): show clean RPC error messages 2026-07-18 11:20:15 +08:00
johnnyjoygh
84776cc106 fix(api): align resource IDs with AIP conventions
Validate new user-provided IDs using the AIP-122 format while retaining legacy UID compatibility. Correct resource annotations and canonical names returned by user stats.
2026-07-18 11:12:28 +08:00
johnnyjoygh
715306ea66 chore: enrich access tokens setting page
Settings drops the all-in-one bordered card for a de-carded layout in
the property-rail design language: a sticky table-of-contents rail at
md+ (Settings wordmark, uppercase group labels, quiet anchor rows) and
a horizontally swipeable chip strip below md, replacing the mobile
section dropdown. Nav items are real anchors with aria-current, and
switching sections scrolls back to the top.

Access Tokens becomes a first-class section with an explainer panel:
what a PAT is and a copyable curl example (real instance origin,
memos_pat_ prefix) beside token-safety guidelines in a two-column band,
with a Learn more docs link and the tokens table beneath. Successful
PAT authentication now records the token's lastUsedAt asynchronously
inside resolveBearer, with a clone-before-mutate cache guard and
monotonic writes in the store, surfaced in a Last used column.

Also localizes the create dialog's 90 Days label, lets the My Account
row wrap instead of clipping on narrow screens, and drops the dead
select-section key from all locales.
2026-07-18 10:41:53 +08:00
Dedy F. Setyawan
2d01420c23
fix(web): align radio button group indicator (#6097)
Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com>
2026-07-18 01:47:45 +08:00
boojack
469c995cc0 chore: dump demo data
Login on the demo instance is SSO-only, so visitors always arrive as a
fresh user and consume seed content through Explore. Rebuild the seed
around that funnel: four personas (steven the maintainer, alice, ben,
zoe), a public feed mixing short captures with long-form anchors, and
protected memos that visibly appear after sign-in to teach the
visibility model.

Every headline feature is now demonstrated: nested tags, memo
references, comments with mentions, reactions, locations, a database-
stored image attachment, and a rendering test memo covering math,
mermaid, code, tables, and task lists.
2026-07-17 00:14:01 +08:00
boojack
d4b5a1695b feat(web): redesign memo detail sidebar as property rail
Rework MemoDetailSidebar into a Linear-style rail: a quiet icon action
cluster (pin, copy link, share image, share links) followed by
label/value property rows for visibility, created/edited times, author,
and location, then hairline-separated sections for tags, attachments,
relations, and the outline. Attachments and reference relations were
previously not surfaced in the sidebar at all.

- Visibility is editable in place, reusing VisibilitySelector with a
  new compact size variant that matches the 13px row grammar
- Pin toggle and visibility changes go through useUpdateMemo
- MemoOutline gains active-section tracking (scroll-spy), depth
  indentation, and tick-rail styling; drops the animated underline
  and native title tooltips
- Widen the detail rail from w-56 to w-60
2026-07-16 23:59:31 +08:00
boojack
e497895bf3 feat(web): restore scroll position across navigation 2026-07-16 22:31:00 +08:00
boojack
773e56e537 fix(web): position tooltip arrows outside content
Closes #6095
2026-07-15 22:29:38 +08:00
boojack
df1967df91 fix(editor): restore CodeMirror default spacing
Remove the zero-padding line override that replaced CodeMirror's built-in horizontal spacing and caused the editor content regression reported in #6093.

Disable completion icons through CodeMirror configuration, preserve placeholder and dark-theme contrast, and remove redundant editor style overrides.

Fixes #6093
2026-07-14 21:35:55 +08:00
boojack
8d9849e13f chore: redesign about page as identity hero
Rebuild /about around an identity hero: instance branding with custom
profile fallbacks, a demo badge, and version/commit chips linked to the
GitHub release and commit. Dev builds show a plain version chip and hide
the unknown-commit chip.

Project links become an annotated directory (homepage, docs, API docs,
GitHub repo), followed by an MIT license note. The bird sprite gallery
moves out of the page, and the version/commit block leaves the settings
sidebar now that /about owns build info.
2026-07-14 20:54:29 +08:00
boojack
1e91cfba9d fix(auth): stabilize SSO option loading 2026-07-14 20:06:54 +08:00
boojack
9c3bd44a1b chore(web): migrate UI primitives to Base UI 2026-07-14 09:06:40 +08:00
boojack
564da949cb chore(auth): redesign sign-in and sign-up pages with panel layout
Rebuild the auth pages around a shared AuthPageLayout card with a
three-tier guidance hierarchy: the form (or provider buttons) on top,
a centered switch sentence below it, and a quiet explore band anchored
to the card foot on public instances.

- add AuthPageLayout (brand row, chip, empty state, link prompt,
  explore band gated by instance URL with hideExplore opt-out)
- extract CredentialFields shared by sign-in and sign-up forms
- extract IdentityProviderButtons; fetch the provider list via a
  cached TanStack Query hook with an enabled flag
- cover every instance config: SSO-only, password-only, registrations
  closed, private instance, nothing enabled, first-run setup, admin
- promote /auth/signup, /auth/admin, /auth/callback to ROUTES and add
  appendSearchParams for param-preserving auth links
- move the explore link out of AuthFooter (selectors only now)
- refresh en and zh-Hans auth strings; drop dead keys
2026-07-13 19:40:31 +08:00
boojack
a9fcd459f6 chore(demo): harden SSO-only demo experience 2026-07-13 00:31:15 +08:00
boojack
4bc3928029 fix(user): implement ListUsers pagination
Rework ListUsers to match the ListMemos pagination contract: opaque
PageToken, normalizePageSize, DB-level limit+1 look-ahead, and a
next_page_token. Adds Offset to store.FindUser with an OFFSET clause in
all three dialects, and an `id DESC` ORDER BY tiebreaker so offset pages
stay stable when created_ts ties.

Also align pagination across list endpoints:
- Bump DefaultPageSize 10 -> 50 to match the documented default; use
  normalizePageSize in ListAttachments.
- Remove the never-implemented total_size field from all six list
  responses (ListUsers, ListAttachments, ListMemoComments,
  ListMemoReactions, ListUserSettings, ListPersonalAccessTokens) and
  regenerate.
- useListUsers now pages through next_page_token so the admin members
  view still loads every user past the default page size.
2026-07-12 20:51:04 +08:00
Yuhang Wang
375e2bc60e
fix(i18n): complete missing Simplified Chinese translations (#6084) 2026-07-12 19:55:37 +08:00
boojack
3fe145083f chore: reorganize backend and frontend modules
- remove the unused internal cron package
- split API service implementations by responsibility
- clarify frontend shared-module ownership
2026-07-12 17:59:12 +08:00
boojack
9203a22ed1 chore(editor): update focus and save workflows
- preserve grid layout while the editor is in focus mode\n- extract save orchestration into a dedicated lifecycle hook\n- simplify code formatting controls in the toolbar
2026-07-12 17:23:36 +08:00
boojack
42ad4105c1 fix(editor): align CodeMirror markdown event handling
- intercept file paste and drop before CodeMirror inserts file text
- reconfigure localized placeholders without remounting the editor
- add regression coverage and document the CodeMirror review
2026-07-10 18:59:33 +08:00
boojack
a9ac008a68 fix(editor): let CodeMirror own caret layout
Keep caret and placeholder behavior on CodeMirror's standard extensions, and move height and scrolling to the editor elements so empty space stays interactive across browsers.\n\n- disable unused multi-cursor selection\n- upgrade CodeMirror state and view patches
2026-07-10 08:51:16 +08:00
boojack
0150040056 chore(web): upgrade typescript to 7.0
TS 7.0 is the native compiler and stays CLI-compatible, so the tsc-based lint is unchanged. Verified pnpm lint and pnpm build pass.
2026-07-09 21:38:32 +08:00
boojack
730c24592f fix(web): stabilize multi-column memo layout 2026-07-09 20:56:35 +08:00
boojack
5329e60407 fix(web): fill the editor host so the caret shows in empty space
The CodeMirror editor only grew to its content height, so in focus mode
(where the host is tall) the empty area below the text was bare host
markup outside the editor. Clicking there could not focus the editor or
place the caret, so it appeared to vanish in "empty" space. Give
.cm-editor height:100% — it collapses to auto in normal (auto-height)
mode and only fills when the host has a definite height.

Fixes #6076
2026-07-08 23:37:47 +08:00
boojack
a72ae804c0 fix(web): keep the multi-column feed balanced
Newly-created memos were pinned to column one and never released, and cards
that grew after placement (late images/comments) never rebalanced, so the
columns drifted badly out of balance.

- Leave the priority (just-created) memo's column assignment transient, so a
  superseded memo rebalances instead of piling up in column one forever.
- Self-heal: re-pack from a clean slate when a late height change leaves the
  columns lopsided, adopting it only when it meaningfully shrinks the spread.
- Animate only the adopted rebalance; resizes, column-count changes, growth
  reflows and first paint stay instant so widths and positions stay in lockstep.
2026-07-08 21:51:02 +08:00
boojack
dc41480ece chore(web): replace memo list skeleton with a simple delayed spinner
The card-shaped skeleton never matched the loaded memos (heights vary
with markdown, images, and tags), making the swap feel jarring. Use the
same centered spinner the grid layout already used, still gated behind
the 250ms delay so fast loads render no indicator at all.

Ref https://github.com/orgs/usememos/discussions/6066
2026-07-07 23:49:43 +08:00
johnnyjoygh
e3c231fcac feat(web): add code block and strikethrough to formatting toolbar
Reorganize the toolbar into inline marks (bold, italic, strikethrough,
inline code) and blocks (lists, fenced code block), with the Type glyph
for the paragraph/heading picker. Marks derive from the shared MARKS
table; code block toggling reuses the same selection-probe lookup so
toggle-off always agrees with the button's active state. A fresh empty
strikethrough pair (~~~~) parses as a tilde code fence, so empty-pair
removal and code-block detection recognize doubled mark tokens.
2026-07-07 22:08:33 +08:00
johnnyjoygh
475b04f765 chore(proto): regenerate with updated protoc plugins 2026-07-07 21:18:06 +08:00
johnnyjoygh
620db61f7c chore(web): unify compact truncation into a single ClampedSection
Compact cards previously had two truncation systems taped together: a
text-only clamp inside MemoContent (144px, no re-measure on image load,
fade tinted to the wrong surface) and a whole-tile cap in ColumnGrid
(hard clip mid-image, no affordance), later joined by a content-shape
rule choosing an owner per card — which could still stack two Show-more
buttons and silently broke footnote navigation in clamped cards.

Now there is exactly one mechanism: ClampedSection measures its content
and folds anything taller than 420px to a 360px preview with a fade and
a Show more/less toggle. MemoBody wraps the whole body in it (reactions
stay outside, never hidden); ColumnGrid is pure packing again and never
clips; MemoContent is a stateless renderer whose `compact` prop only
informs footnote-link behavior; MemoPreview uses a static CSS bound in
place of its previously inert toggle.

Deltas: text-only compact previews grow from ~6 rows to the same 360px
image cards get, and pinned memos clamp like any card (pinned means
ordering and a badge, not size).
2026-07-07 21:01:19 +08:00
boojack
14a1c9a53b chore(web): icon layout switcher with radio semantics
Replace the layout Select with a lean 28px segmented control: Rows3 /
Columns2 / Columns3 / Infinity icons on a quiet muted track, only the
active option filled. Each option carries a tooltip with a label and an
honest description of the ceiling semantics ("up to N columns when the
width allows").

The control is a proper radiogroup — roving tabindex, arrow-key
movement, aria-checked — restoring the single-choice semantics the
Select used to provide. Options derive from ViewContext's canonical
value list, and one exhaustive record maps each value to its icon and
wording, so a future column option fails to compile until both exist.

The Compact mode row now stays visible in multi-column layouts, shown
as on and locked (multi-column always renders compact tiles); the
stored preference is untouched and resurfaces at a single column.
2026-07-07 09:24:48 +08:00
boojack
177d65a90e feat(web): add max-columns memo feed layout
Replace the single-column-only feed with a max-columns model: one
Columns setting (1 / 2 / 3 / ∞) where 1 is the reading list and
anything wider packs into a Google-Keep-style column grid. There is
deliberately no separate list/grid mode — the setting is a ceiling,
and widths that only fit one column fall back to the flow list.

- ColumnGrid: absolute-positioned packing that only translates cards,
  so appends and reorders never remount them; sticky column assignment
  keeps existing cards in place when new memos arrive; tiles are capped
  at 360px with a fade; columns clamp to 420px and center.
- Column one is the action column: the composer and active filters
  stack as its first tile, and a just-created memo is pinned directly
  beneath them.
- Multi-column always renders compact cards (policy centralized in
  PagedMemoList and threaded through renderer(memo, { compact })).
- Setting persists in ViewContext localStorage; the settings menu
  derives its options from the context's canonical value list.
2026-07-06 23:55:36 +08:00
boojack
d1cef7a9ab feat(auth): add private instance mode derived from instance_url
Run the instance in private mode when instance_url is not configured: the API rejects anonymous requests except the auth-bootstrap set (sign-in, token refresh, instance profile/settings, SSO providers, share-link access) plus first-run user creation, and the web UI redirects anonymous visitors to /auth instead of /explore. Setting instance_url keeps the current public behavior. Access tokens and personal access tokens are never gated.

Enforcement lives in a shared Authorizer used by both the Connect interceptor and the gRPC-gateway middleware; the file server applies the same rule to public-memo attachments and avatars. Also merges the duplicated Authenticate/AuthenticateToUser token dispatch behind resolveBearer, dedups the AuthContext unauthenticated state, extracts the redirect decision into a pure shouldGatePrivateInstance helper, and prints the access mode at startup.
2026-07-05 22:48:00 +08:00
boojack
1794d0dc51 chore(editor): flatten insert menu
- Insert menu: replace the nested "More" submenu with a flat dropdown —
  insert actions plus inline Focus Mode and Formatting-toolbar toggles.
- Formatting toolbar: remove the link button and its window.prompt flow;
  the link command stays in the shared catalog for future surfaces.
2026-07-05 20:43:02 +08:00
Archit Goyal
b787bfa75f
feat(web): add untagged shortcut filter example (#6065) 2026-07-05 19:31:58 +08:00
boojack
c349c1549e fix(editor): make formatting commands toggle and convert correctly
Bold (and italic/code) now remove an empty just-inserted delimiter pair instead of nesting more asterisks, and strip real marks whichever way the selection was made; clicking Link inside a link unwraps it instead of inserting [](); the three list modes convert between each other (bullet on a task line no longer leaves '[ ] ...' behind), toggle off any ordered number, respect indentation, and apply across multi-line selections; list/heading edits keep the cursor in place (task insert on an empty line lands after the marker).

Structurally, highlight and toggle now share one line-mode detector so they cannot disagree; the heading regex is shared with headingDecorations, so indented headings render styled; one MARKS table drives all inline-mark behavior; run("link") is total (URL doubles as label on empty selection, now inserted inline rather than as its own block); the unused getSelectedText contract method and the dead markdown-list-detection.ts util are removed.

Known divergence, not addressed here: markdown-task-actions.ts TASK_LINE_REGEXP accepts ordered task items ('1. [ ]') which the editor's TASK_LINE does not; the viewer toggles such checkboxes but the editor toolbar won't detect them.
2026-07-02 20:41:46 +08:00
boojack
ba609c5cb8 chore(editor): toggleable formatting toolbar
Add a localStorage-backed insert-menu toggle (default off) that surfaces the formatting toolbar outside focus mode, and redesign it as a lean inline row: ghost icon buttons grouped by thin dividers, a level-reflecting heading glyph (pilcrow / H1-H3), and the active command as the only filled control.

Also keep the editor focused when using the toolbar: command buttons preventDefault on mousedown, and the dropdown menus return focus to the editor on close.
2026-07-02 09:19:34 +08:00
boojack
10200606db feat(markdown): render and navigate GFM footnotes
Footnote definitions previously vanished on display. Two underlying issues:

- rehype-sanitize re-clobbered `id` attributes with a second `user-content-`
  prefix while leaving hrefs untouched, so footnote refs/backrefs pointed at
  ids that didn't exist. Disable clobbering (ids are already namespaced by
  remark-rehype) so anchors match their targets.
- Footnote anchors went through the external Link (target="_blank"), opening a
  blank tab instead of navigating. Route in-page `#` anchors through a new
  AnchorLink: scroll within the memo when shown in full, else navigate to the
  memo detail page with the hash, where MemoDetail scrolls it into view.

Also style the footnotes section GitHub-style: thin separator, smaller muted
text, and un-underlined ref/backref links.
2026-06-29 22:46:41 +08:00
boojack
3b601b8416 fix(location): truncate long address in memo location chip
The location chip used the kit Button (shrink-0 + whitespace-nowrap),
which grew unbounded so its inner truncate span never fired and long
addresses overflowed the memo card. Replace with a raw button that
caps width (max-w-full min-w-0) and truncates the address text,
matching the existing LocationDisplayEditor pattern.
2026-06-29 20:27:52 +08:00
johnnyjoygh
5a73d7d3e5 refactor: rebuild the editor on CodeMirror as decorated source
Rebuild the memo editor as a single CodeMirror 6 "decorated source"
editor. The document is the raw markdown, stored verbatim and styled in
place (markers stay visible), so the editor never serializes a tree back
to markdown — removing the round-trip fidelity bug class (inline images,
setext headings, ordered-list indentation, HTML entities) that the old
editor needed per-case patches for.

- MemoEditor/Editor: CodeMirror 6 + lang-markdown (GFM). Tokens, heading
  lines, #tag/@mention, and the autocomplete popover are styled in plain
  CSS (Editor/editor.css) with theme tokens, not a CSS-in-JS theme.
- Tab/Shift-Tab nest/outdent list items (marker-aware, ordered items
  renumbered so nesting is CommonMark-valid); Escape blurs; #tag
  autocomplete sourced from useTagCounts.
- Focus-mode toolbar reimplemented as markdown-text ops; active state read
  from the Lezer tree via the backend-agnostic formatting/commands catalog.
- Remove the old serialize-back-to-markdown editor (its Editor dir,
  PlainEditor, the editor-mode system) and its now-unused dependencies
  (marked, textarea-caret, and the rich-text editor packages).
- Consolidate toolbar components under Toolbar/. Read-only MemoContent
  rendering is unchanged.
2026-06-28 22:59:14 +08:00
Santhosh Thottingal
281e0dc17c
fix(tags): include combining marks in tag character class (#6051) 2026-06-28 10:41:41 +08:00
boojack
047175dbed chore(mcp): improve tool discoverability, add orientation tools and evals
Make the OpenAPI-driven MCP surface more usable by agents, following the
mcp-builder guidance.

- Enrich proto descriptions (single source of truth, flows to OpenAPI + MCP
  tool descriptions): document the memo `filter` CEL grammar with fields and
  examples (replacing the dangling "Refer to Shortcut.filter"), clarify the
  created_ts/updated_ts vs create_time/update_time naming, the visibility
  enum, the declarative replace semantics of Set* ops, and steer tag filters
  to `"x" in tags` (not the unsupported `tag == "x"`).
- Mark SetMemoAttachments / SetMemoRelations idempotent via a per-operation
  override the HTTP-method heuristic can't express.
- Curate two read-only orientation tools: shortcut_list_shortcuts (surfaces
  reusable CEL filters) and auth_get_current_user (the single allowed
  auth/identity op, for resolving the current user); guard test updated to
  keep the rest of the auth/user surface excluded.
- Add a task-level evaluation suite (server/router/mcp/evals) with 10
  verified questions, pinned to the deterministic demo seed.
2026-06-27 10:32:42 +08:00
boojack
daa71d0456 feat(editor): recognize and style @mentions in the rich editor
Mirror the #tag mark so the TipTap editor styles @username while typing
and on load, matching the read-only view. A shared mention grammar
(utils/mention-grammar.ts) feeds both the editor tokenizer and the
read-only remark renderer so they can't drift.

Styling only -- no autocomplete dropdown (ListUsers is admin-only and
there is no user-search RPC). Bare emails still autolink to mailto: and
are never treated as mentions: the tokenizer's start() skips email-glued
@s so marked's GFM autolinker keeps seeing the whole address.
2026-06-26 09:28:41 +08:00
boojack
eb826455b6 chore(webhook): reveal-later signing secret flow
Generate webhook signing secrets server-side and let users reveal them on
demand, replacing the create-dialog secret controls that surfaced internal
mask state (Status / Generate & Copy / Clear / Pending) to users.

- Add owner-gated GetUserWebhookSigningSecret RPC — the only path that
  returns the secret; list/create/update responses still omit it.
- Generate the secret server-side on create (webhook.GenerateSigningSecret),
  so validity no longer depends on the client.
- Rename UserWebhook.has_signing_secret -> signing_secret_set for parity
  with the existing api_key_set field.
- Create dialog drops the secret section to a one-line note; the generated
  secret is shown once right after create and revealable from Edit later.
2026-06-26 09:03:24 +08:00
Woookash
c6d65bcf3f
chore: i18n polish translations (#6050) 2026-06-26 00:36:37 +08:00
Yiges.M.x.
c703b05dab
feat: add webhook edit UI and signing secret status indicator (#6027) 2026-06-25 22:26:20 +08:00
boojack
9e84f61029 fix(web): delay loading skeleton to avoid flash on fast loads
Render the memo list skeleton only after loading exceeds a short delay
(SKELETON_LOADING_DELAY_MS). Fast/self-hosted loads now finish before the
threshold and never flash the skeleton, while slow loads still show it.

Close #6047
2026-06-25 22:02:39 +08:00
boojack
e3e4ae1051 feat(web): add link preview view setting
Add a persisted "Link preview" toggle (default on) to the memo
display-settings popover, mirroring the compact-mode setting. When turned
off, link-preview cards are hidden across all memo views and the metadata
request is skipped (the gate sits at the paragraph renderer and reads the
preference from ViewContext). Resolves the request in #6045.
2026-06-25 20:45:51 +08:00
greymoth
a5c10a7221
i18n(ja): complete Japanese translations — fill 24 missing keys (#6048) 2026-06-25 18:11:23 +08:00
boojack
5f7e038aa7 fix(editor): support backslash-escaping literal #tags
Memos layers #tag syntax on top of CommonMark, but there was no way to
write a literal "#NAS" — it always became a clickable tag (issue #6035).

Handle escapes lexically, the way TipTap/prosemirror-markdown do, with no
"escaped tag" node in the document model:

- Parse: marked's built-in backslash escape already turns "\#NAS" into
  plain text; the read-only renderer (remark-tag) now honors the same
  escape by lexing from the original source slice and falling back to the
  prior value-based parse when it can't faithfully reconstruct the text.
- Serialize: a tag-aware Markdown extension (tagMarkdown.ts) backslash-
  escapes a "#" that would re-parse into a tag, skipping links and code
  where tags never form.
- The editor's #tag lexing moves to the canonical @tiptap/markdown
  markdownTokenizer; tag-in-link skipping moves to a tree pass so the
  editor and renderer agree.

The capped tag-run grammar is consolidated into utils/tag-grammar.ts
(TAG_RUN), shared by the tokenizer, the serialize-escape, and the
renderer so they can't drift. README documents why the markdown manager
is worked around in three places.
2026-06-23 23:00:54 +08:00
boojack
20c19ef82d feat(storage): add insecure_skip_tls_verify option for S3
Adds an opt-in toggle to skip TLS certificate verification when connecting
to the S3 endpoint, for self-hosted S3-compatible backends (e.g. rustfs,
MinIO) that use self-signed certificates. Exposed in both the store/API
protos and the storage settings UI, mirroring the existing use_path_style
toggle. When enabled, the AWS client uses an HTTP transport with
InsecureSkipVerify; default behavior is unchanged.

This governs backend-initiated S3 calls (uploads, deletes, thumbnails, and
image/document streaming). Video/audio playback redirects the browser to a
presigned URL, so that path still requires the browser to trust the cert.

Closes #6039
2026-06-23 00:04:54 +08:00
boojack
1e3ec38fa1 refactor(editor): modularize formatting and simplify editor state
Behavior-preserving restructure of the MemoEditor subsystem for simplicity
and extensibility:

- Add a data-driven command catalog (editorCommands.ts) as the single source
  for formatting verbs; the toolbar, active-state hook, and WYSIWYG handle all
  derive from it, so adding a verb is a one-file change.
- Add a createSuggestionExtension factory; TagSuggestion consumes it, so new
  `/` or `@` triggers reuse the shared popup in ~10 lines.
- Collapse FormattingController into a single EditorController with an optional
  `formatting` capability; PlainEditor is an honest textarea fallback (no faked
  formatting); replace stringly-typed isActive with typed getActiveFormats.
- Move audio-recorder state out of the reducer into useAudioRecorder, keeping a
  single recorderBusy flag in the store.
- Convert the editor context to an external store (useSyncExternalStore) with
  per-slice subscriptions, so typing no longer re-renders the toolbar, insert
  menu, or metadata.
- Extract the shared #tag lexing grammar (tag-grammar.ts) used by both the
  editor tokenizer and the remark renderer.
- Remove dead reducer actions/cases and fix a preview blob-URL leak on the
  upload path.

Add tests for the command catalog, suggestion factory, and autosave.
2026-06-22 23:51:59 +08:00
boojack
26f4b73cb9 feat(filter): standard CEL now variable, time accessors, set ops
Replace the custom now() function with an idiomatic `now` timestamp variable (host-injected, frozen once per compile) and retype created_ts/updated_ts/create_time to CEL timestamp. Filters now use standard timestamp/duration arithmetic, e.g. `created_ts >= now - duration("24h")` and `timestamp("2025-01-01T00:00:00Z")`.

Add standard CEL surface that compiles to SQL across SQLite/MySQL/Postgres: timestamp accessors (getFullYear/getMonth/getDate/getDayOfWeek/..., with 0-based month and weekday normalized), ext.Sets() (sets.contains/intersects/equivalent over tags), tags.exists_one(), size() on string fields, and division/modulo folding. A frozen clock is injectable for deterministic tests.

BREAKING CHANGE: now() is removed (use the `now` variable) and time fields are timestamps, so bare-epoch comparisons need timestamp(<epoch>). Existing saved shortcuts using the old syntax must be updated.
2026-06-22 22:42:44 +08:00
johnnyjoygh
cafa56f1a8 chore: make compact mode an opt-in view setting
Memo list views hard-coded compact rendering, truncating long memos by
default. Make full content the default and add a persisted 'Compact mode'
toggle (default off) in the display-settings popover, backed by the
localStorage ViewContext. Wire Home/Explore/Archived/UserProfile to read
it; add the compact-mode label across all locales.
2026-06-21 22:55:56 +08:00
johnnyjoygh
96cb65320b fix(instance): add needs_setup so admin-less instances aren't treated as fresh
The frontend keyed first-run setup off a null InstanceProfile.admin, but a
null admin only means "no admin-role user exists" — which also happens on a
populated instance that has lost all its admins. Such an instance was wrongly
redirected to signup, where the new account is created as a normal user (the
first-user promotion only triggers when there are zero users), leaving the
instance permanently admin-less.

Add an explicit InstanceProfile.needs_setup derived from user count == 0, and
switch the signup redirect and host tip to use it. admin stays for display only.
2026-06-21 22:14:15 +08:00
johnnyjoygh
6eb17864df feat: surface newly created memo above pinned list 2026-06-21 14:11:46 +08:00
johnnyjoygh
deddf71d7b feat(editor): add focus-mode formatting toolbar
Add a rich-text formatting toolbar as the focus-mode header when the
WYSIWYG editor is active: heading dropdown, bold/italic/code, lists, and
link, with a priority+overflow responsive layout and live active-state
highlighting. The toolbar is a self-contained component driven through a
new FormattingController surface routed via EditorContent.

Remove the now-redundant slash-command feature (/todo, /code, /link,
/table) and its "Type / for commands" hint, since the toolbar covers
those actions; the shared suggestion renderer stays for #tag.
2026-06-21 11:57:45 +08:00
johnnyjoygh
f727ad217c fix(comments): list all memo comments via pagination
Memo detail only showed the first 10 comments: the frontend requested
pageSize=0, which the backend normalizes to DefaultPageSize (10), and the
returned nextPageToken was never followed.

Add useInfiniteMemoComments (mirrors useInfiniteMemos) so the detail page
paginates through every comment, with a "Load more" control in
MemoCommentSection. Page size defaults to DEFAULT_LIST_MEMOS_PAGE_SIZE to
match the memo list convention.
2026-06-21 09:55:37 +08:00
johnnyjoygh
f5a60263b2 chore(ui): align frontend on shadcn kit
Design system:
- add semantic --success/--warning OKLCH tokens across themes; replace
  hardcoded green/amber feedback colors and their manual dark: overrides
- drop the unused @emotion dependency

Kit usage:
- migrate raw <button>/<input> to ui-kit components (actions) or
  <div>/<span> (non-action surfaces); keep genuinely custom looks as
  raw HTML with their own styles
- make kit usage prop-only (no className overrides): add Button
  size="icon-sm", Badge "warning" variant + "pill" shape
- extract a shared Tabs primitive (segmented/underline) and migrate
  Inboxes + UserProfile onto it
- tokenize z-index tiers as z-overlay/z-dropdown/z-tooltip
- export variant types (ButtonVariant/Size, BadgeVariant/Shape, TabsVariant)
- document the kit and its policy in components/ui/README.md
2026-06-21 09:17:04 +08:00
boojack
f0e4a5624f feat(filter): expand CEL filter surface with startsWith/endsWith, matches(), and all()
Let users write three more CEL constructs in the filter field, each compiled to
SQL across SQLite/MySQL/Postgres:

- Scalar startsWith()/endsWith() on content/filename/mime_type (case-insensitive)
- matches() regex: PG ~, MySQL/SQLite REGEXP (Go-backed SQLite fn), validated at
  compile time via cel.ValidateRegexLiterals()
- all() comprehension over tags via per-element subqueries, non-empty required

Also: contains() now escapes LIKE metacharacters (%, _, \); cross-dialect render
tests plus behavioral tests; cel-go bumped to v0.28.1; new operators surfaced in
the frontend shortcut guide.
2026-06-15 23:22:28 +08:00
boojack
817561df8f fix(editor): collapse lingering select-all selection after delete
Ctrl+A creates a whole-document AllSelection; deleting it (Backspace, Delete, or Cut) maps the AllSelection onto the now-empty paragraph instead of collapsing to a caret, so the view paints a "selected" empty block. A ProseMirror appendTransaction now collapses a leftover AllSelection to a caret after any doc-changing edit.
2026-06-15 09:33:16 +08:00
ManJieqi
46685b1906
chore: update Chinese translations in zh-Hans.json (#6033)
Signed-off-by: ManJieqi <40858189+manjieqi@users.noreply.github.com>
2026-06-14 23:23:18 +08:00
boojack
00225db922 refactor(web): share markdown element styles between viewer and editor
Extract the Tailwind classes for common markdown elements (paragraph,
blockquote, lists, inline code, link, hr, headings) into a single
markdownStyles.ts consumed by both the read-only MemoContent components
and the WYSIWYG editor, replacing the duplicated per-element strings and
the .memo-wysiwyg CSS block. Heading classes are precomputed per level so
the hot renderHTML path is a lookup, not a cn() merge.

Also require at least one character after `#` before opening the tag
suggestion menu so a bare `#` (or `# ` heading) no longer conflicts with
markdown headings.
2026-06-13 22:17:00 +08:00
boojack
2c0efeba7c refactor(web): rename editor dirs and simplify raw mode to a plain textarea 2026-06-13 01:08:38 +08:00
boojack
797f1ff15d
feat(web): markdown WYSIWYG editor with raw-mode toggle (#6030)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:34:51 +08:00