Commit graph

2908 commits

Author SHA1 Message Date
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
Yiges.M.x.
063a44498d
feat: add optional webhook signing secret (Standard Webhooks HMAC-SHA256) (#6013) 2026-06-09 22:45:01 +08:00
boojack
1052c04d33 fix(web): improve mobile control spacing 2026-06-09 21:04:47 +08:00
boojack
418398587c feat(i18n): add searchable locale picker 2026-06-09 09:33:17 +08:00
boojack
a47d04954e feat(i18n): expand European locale coverage 2026-06-09 09:07:28 +08:00
boojack
ecbe2ab797 fix(memo): preserve expanded todo list state 2026-06-08 19:11:29 +08:00
ay5399
8f1377324f
fix(editor): wire Ctrl+B and Ctrl+I markdown shortcuts to textarea (#6016)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: boojack <stevenlgtm@gmail.com>
2026-06-08 00:24:05 +08:00
boojack
9eabb554d5
feat(settings): move tag metadata to user settings (#6017) 2026-06-07 23:58:00 +08:00
boojack
a50ce09e81 fix(markdown): ignore tags inside links 2026-06-06 00:01:28 +08:00
boojack
0e2a9a9c0c fix(web): render video attachment posters on mobile 2026-06-02 23:08:16 +08:00
boojack
0d31e3c2fb chore(media): add zoom controls to preview dialog 2026-06-02 22:46:07 +08:00
boojack
53abb8020e refactor(frontend): remove react-use dependency 2026-05-31 18:32:17 +08:00
boojack
7c3bff4e98 fix(markdown): keep task item content in one grid column 2026-05-27 23:57:23 +08:00
boojack
e0bb3a2e68 fix(editor): wrap selected text when pasting URL 2026-05-27 20:36:27 +08:00
boojack
648b3bd812
feat(memo): add task list quick actions (#5983) 2026-05-27 09:13:55 +08:00
boojack
e564c1a993 chore: update about page 2026-05-26 21:14:59 +08:00
boojack
439ddaebf5 refactor(placeholder): extract tile sprite strip 2026-05-14 20:38:00 +08:00
boojack
411ba7b34c feat(about): add about page with bird sprites 2026-05-14 20:30:37 +08:00
boojack
638e4f398e feat(placeholder): add woodpecker tilemap 2026-05-14 09:35:11 +08:00
boojack
cf55f11072 feat(frontend): add pixel bird tilemaps 2026-05-14 00:03:21 +08:00
boojack
ca2bc4eb84 chore: render placeholder ascii pieces as components 2026-05-13 09:06:23 +08:00
boojack
aa5cb455e9 refactor(web): use / as the home route instead of /home
Home now lives at `/` directly. Unauthenticated visitors are sent to
`/explore` by the landing gate; old `/home` URLs redirect to `/` for
bookmark compatibility.
2026-05-12 23:17:46 +08:00
boojack
8c16ffa1f1
feat: add <Placeholder> component with ASCII bird states (#5949)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 22:48:37 +08:00
boojack
a6024eebf1
perf: lazy load heavy first-screen dependencies (#5947) 2026-05-12 20:11:39 +08:00
boojack
f3f059b2f7 chore: add batch get settings API 2026-05-09 09:30:28 +08:00
boojack
21303e879d fix(sse): stream initial response and refresh tokens 2026-05-09 09:15:50 +08:00
boojack
88ac3ec31e feat(stats): support filtered all-user stats
- Add state and filter inputs to ListAllUserStats and reuse it for explore/archive sidebar stats.
- Reduce duplicate home initialization requests by sharing stats/settings data paths.
- Include memo paragraph regression coverage from the current working tree.
2026-05-09 09:11:04 +08:00
boojack
1df6479443
feat: add dedicated shortcuts page (#5942) 2026-05-08 23:06:12 +08:00
boojack
b6f42cbe6b
chore: upgrade frontend to React 19 (#5940) 2026-05-08 22:10:48 +08:00
hchengting
bcbcb03894
chore: update zh-Hant translation (#5930) 2026-05-04 20:18:00 +08:00
boojack
5ccba98adc
refactor: split STT and Audio-LLM into separate interfaces (#5928) 2026-05-03 00:21:58 +08:00
boojack
238f27dea1
feat(transcription): explicit STT settings with provider, model, prompt (#5926) 2026-05-02 19:35:18 +08:00
boojack
ef55013418
feat(memo): create memos on the selected calendar date (#5925) 2026-05-02 11:53:50 +08:00
Steven
d349fe4409 chore(theme): rebalance dark palette for readability 2026-05-02 09:11:46 +08:00
Steven
8daef1dc89 feat(activity-calendar): aggregate by ViewContext.timeBasis
Fixes the inconsistency where switching the memo list to update_time
left the activity heatmap aggregating by created_time. The heatmap
now follows the same time basis as the list it sits next to.

Backend
- UserStats gains memo_updated_timestamps (additive proto field, tag 8).
- GetUserStats and ListAllUserStats populate it alongside the existing
  memo_created_timestamps. No DB migration; memo.updated_ts already
  exists on every row.

Frontend
- useFilteredMemoStats reads timeBasis from ViewContext and selects
  the matching timestamp source.
- StatisticsView and MonthNavigator forward timeBasis through to
  MonthCalendar / YearCalendar so tooltip text matches the basis
  ("X memos in DATE" vs "X memos updated on DATE").
- Falls back to memoCreatedTimestamps when an old server returns an
  empty memoUpdatedTimestamps array (detected by length divergence,
  since protobuf-es deserializes missing repeated fields as []).

Tests
- Backend: TestGetUserStats_MemoUpdatedTimestamps verifies the field
  is populated and reflects post-creation updates.
- Frontend: filtered-memo-stats covers create/update source switching
  and the old-server fallback path; activity-calendar-tooltip covers
  basis-aware label selection.

Spec and implementation plan committed under docs/superpowers/.
2026-05-02 00:26:53 +08:00
Steven
ea0625da45 feat(stats): admin instance resource statistics 2026-05-01 23:15:56 +08:00
Steven
cd4f28ae10 feat(notification): add smtp email settings
- Add admin notification email settings UI and test-email RPC
- Dispatch privacy-first comment and mention emails through server notification layer
- Keep SMTP secrets write-only and require passwords when SMTP identity changes
2026-05-01 18:48:21 +08:00
Steven
a0b6417689 chore: bump pnpm to 11.0.1 and align list test assertions 2026-04-30 20:05:39 +08:00
boojack
e008b1a23c fix(markdown): align list items with checkboxes 2026-04-30 08:49:49 +08:00
boojack
0bc56694b0 feat: render link metadata cards 2026-04-29 23:23:52 +08:00
boojack
9c5c604944 feat: add link metadata endpoints 2026-04-29 22:38:08 +08:00
memoclaw
267f90a3ff
chore: refactor Settings UI structure (#5912)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-29 09:30:58 +08:00
boojack
14480bfc46 chore(settings): improve storage tags and memo panels 2026-04-28 23:28:01 +08:00
boojack
ee65e90a39
chore: upgrade pnpm to 11 (#5911) 2026-04-28 22:16:22 +08:00
Chyna
328396a97f
fix(web): sync avatar changes immediately after profile update (#5903)
Signed-off-by: Chyna <68607983+chyna-gvng@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-28 21:57:58 +08:00
Mostafa Attia
9353848603
chore: increased muted text contrast in dark theme (#5902) 2026-04-28 20:03:24 +08:00
boojack
616487fa37
chore: update backend and frontend dependencies (#5900)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-28 08:25:26 +08:00
boojack
18e729fe00 chore(memo): simplify markdown task list rendering 2026-04-27 23:22:29 +08:00
boojack
9e310bf93c chore(memo): clarify displayed update time 2026-04-27 21:50:41 +08:00
boojack
94ce1e5347 chore(settings): show build commit in version info 2026-04-27 08:51:11 +08:00
boojack
e2c60845ea fix(markdown): split mixed task and bullet lists 2026-04-26 23:25:29 +08:00
boojack
c268551a16
feat(memos): choose created or updated time for memos (#5894) 2026-04-26 11:50:26 +08:00
boojack
3949a252db style(web): refine scrollbar styling 2026-04-26 00:24:45 +08:00
boojack
34c90dd5e1 chore: remove duplicate tags from share image preview 2026-04-25 22:22:49 +08:00
boojack
ee1799851e
feat: redesign account and SSO management (#5886) 2026-04-24 09:08:58 +08:00
boojack
30c0611a82
fix: fix legacy username auth flows (#5885) 2026-04-23 22:35:38 +08:00
boojack
d688914b28
feat(auth): add SSO user identity linkage (#5883) 2026-04-23 08:51:45 +08:00
boojack
50638040f6
fix: reduce list memo query overhead (#5880) 2026-04-22 09:31:48 +08:00
boojack
b5863d76be
fix(web): preserve task checkbox state (#5867) 2026-04-20 23:27:00 +08:00
wndnjs0
d98f665919
fix: disable modal prop on DropdownMenu to prevent scroll disappearing (#5861) 2026-04-20 21:38:28 +08:00
boojack
88cb58ab64
refactor(web/routing): guard-based auth flow, migrate tests to Vitest (#5848) 2026-04-18 01:06:54 +08:00
Mattia Borda
5be530dbac
chore: update Italian translation for 'cancel' key (#5841)
Signed-off-by: Mattia Borda <mattiagiovanni.borda@icloud.com>
2026-04-15 21:01:57 +08:00
boojack
411d8335e7
chore: set native color scheme for dark themes (#5840) 2026-04-15 20:55:41 +08:00
boojack
a3a5b52d60 chore: tweak location picker 2026-04-15 20:07:36 +08:00
boojack
ab5332901f fix: hide transcribe button without AI provider 2026-04-14 21:47:59 +08:00
boojack
35504cc8bd
feat(web): add demo mode banner (#5836) 2026-04-14 09:08:17 +08:00
boojack
101704c8ea
feat(ai): add BYOK audio transcription (#5832) 2026-04-13 22:09:24 +08:00
boojack
0ad0fec8d4 feat(ai): add Anthropic provider option 2026-04-12 21:42:17 +08:00
memoclaw
d87539a1e1
feat: add Gemini transcription provider (#5830)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-12 21:12:03 +08:00
memoclaw
83ed32f119
feat(ai): add instance AI providers and transcription (#5829)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-12 19:23:34 +08:00
boojack
efeb28c872 fix: remove duplicate Japanese locale keys 2026-04-12 10:34:20 +08:00
Takuro Onoue
0f7c7eb75e
i18n: refine and normalize Japanese locale strings (#5826)
Signed-off-by: Takuro Onoue <kusanaginoturugi@gmail.com>
2026-04-11 22:15:26 +08:00
boojack
2523d966f4 chore(web): improve navigation accessibility 2026-04-10 22:27:29 +08:00
boojack
8cdcd7b284 refactor(attachments): extract visual gallery layout and tile style tokens 2026-04-10 08:55:20 +08:00
boojack
9ca71229a6 fix: preserve draft content when tab is suspended or editor remounts
Gate auto-save on initialization to prevent empty content from overwriting
cached drafts on remount. Flush drafts synchronously on visibilitychange/
pagehide so Chromium tab suspension cannot drop pending debounced saves.
Restore cached draft over saved memo content when they differ (inline editing).
2026-04-10 08:48:50 +08:00
boojack
938c405b0f refactor: move embedded content styles from global CSS to Tailwind 2026-04-08 23:34:49 +08:00
boojack
124708f164 chore: refactor attachment media layout and insert menu organization 2026-04-08 23:28:49 +08:00
boojack
7e21b728b3 fix: harden memo content iframe and HTML sanitization 2026-04-08 21:10:35 +08:00
boojack
2d682ae176 refactor(attachments): compact audio item layout and playback logic
Made-with: Cursor
2026-04-07 21:47:06 +08:00
boojack
5b78023fd5 Polish share-as-image UI and sidebar sharing actions
Made-with: Cursor
2026-04-07 21:07:40 +08:00
boojack
e51985a29f
feat(memo-editor): add compact live waveform recorder panel (#5817) 2026-04-07 20:40:34 +08:00
boojack
61c963841b chore(web): polish dark theme and calendar UI
- simplify ActivityCalendar state handling and shared max-count utilities
- remove calendar ring styling and darken the default dark theme primary colors
- tighten the audio recorder panel layout and action sizing
2026-04-06 22:44:11 +08:00
memoclaw
24fc8ab8ca
feat(mentions): add memo mention parsing, notifications, and rendering (#5811)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-06 22:16:53 +08:00
boojack
38fc22b754 feat(memo): add image sharing in detail view
Keep the unpublished image-sharing flow scoped to memo detail pages.

- add a dedicated share-image preview and export pipeline
- measure the rendered memo card so preview and exported image stay aligned
- move the entry point into the detail sidebar and drawer only
2026-04-06 18:30:01 +08:00
boojack
2cbc70762b refactor(attachments): simplify the attachment library
- split attachment page states and primitives into focused components
- unify card and list item presentation across media, audio, documents, and unused uploads
- move attachment paging and cleanup flows onto shared query and view-model hooks
2026-04-06 17:39:58 +08:00
boojack
7ac9989d43 fix(map): align dark mode map styling 2026-04-06 17:38:56 +08:00
boojack
15d8ec10d5 chore: update locale translations for new i18n entries 2026-04-06 16:11:21 +08:00
boojack
067d7ff0ce chore: refactor memo editor audio recording flow 2026-04-06 15:46:38 +08:00
boojack
aafcc21ae6 fix: improve image preview dialog and live photo trigger 2026-04-06 13:41:27 +08:00
boojack
6b0487dcd8 fix: unify live photo previews around LIVE badge playback 2026-04-06 11:56:58 +08:00
boojack
4b4e719470
feat(attachments): add Live Photo and Motion Photo support (#5810) 2026-04-06 10:47:01 +08:00
boojack
894b3eb045 fix(map): refine Leaflet controls and memo map styling 2026-04-06 08:45:54 +08:00
memoclaw
f403f8c03c refactor: simplify memo metadata components 2026-04-02 22:30:21 +08:00
memoclaw
0e4d2d25ca refactor: simplify audio attachment playback component 2026-04-02 21:56:01 +08:00
Serhat
9676e72533
i18n: complete Turkish (tr) translations to 100% coverage (#5802)
Signed-off-by: Serhat <49079271+onwp@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-01 19:25:18 +08:00
memoclaw
c0d5854f67
feat(editor): add voice note recording to the memo composer (#5801)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-04-01 09:36:33 +08:00
memoclaw
a0d83e1a9e fix(web): refine attachment media layout 2026-04-01 08:40:36 +08:00
memoclaw
63a17d897d refactor: split audio attachment view into reusable components 2026-03-31 23:03:38 +08:00
memoclaw
1921b57662
fix(tags): allow blur-only tag metadata (#5800)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-03-31 21:38:55 +08:00
memoclaw
e520b637fd fix: prevent stale comment drafts from being restored 2026-03-30 22:37:07 +08:00
memoclaw
c53677fcba fix(api): improve SSE hub design and fix double-broadcast on comments
- Fix duplicate SSE event on comment creation: CreateMemoComment now
  suppresses the redundant memo.created broadcast from the inner
  CreateMemo call, emitting only memo.comment.created
- Extract reaction event-building IIFEs into buildMemoReactionSSEEvent
  helper, removing duplicated inline DB-fetch logic
- Promote resolveSSEAudienceCreatorID from method to free function
  (resolveSSECreatorID) since it never used the receiver
- Add userID to SSE connect/disconnect log lines for traceability
- Change canReceive default from permissive (return true) to
  deny-with-warning for unknown visibility types
- Add comprehensive tests covering all new helpers, visibility edge
  cases, slow-client drop behavior, and the double-broadcast fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 07:33:40 +08:00
memoclaw
acddef1f3d
fix(api): switch user resource names to usernames (#5779)
Co-authored-by: memoclaw <265580040+memoclaw@users.noreply.github.com>
2026-03-25 09:11:17 +08:00