Commit Graph
1134 Commits
Author SHA1 Message Date
Timothy Jaeryang Baek 57bd08304e refac 2026-08-13 22:01:17 -06:00
Timothy Jaeryang Baek 14e4d72d9a refac 2026-08-13 22:01:15 -06:00
Timothy Jaeryang Baek 083e351441 refac 2026-08-13 21:02:17 -06:00
Timothy Jaeryang Baek 7d99b2716a refac 2026-08-13 19:59:11 -06:00
Timothy Jaeryang Baek 9c21d4ed3b refac 2026-08-11 17:42:25 -06:00
Timothy Jaeryang BaekandClassic298 c8f8fa451a a11y
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-08-11 01:25:20 -06:00
Classic298 4eb0394511 fix: merged response receiving empty model responses after reload (#27673)
Clicking "Merged Response" in a multi-model chat often made the merging model answer with "It appears that the responses provided from the other models were empty".

The merge handler collected each model's answer via `history.messages[id].content`. Assistant messages are persisted by the backend with `output` only (`upsert_message_to_chat_by_id_and_message_id` writes `done`/`role`/`output`, never `content`), so `content` is only populated in the browser session that generated the responses, where the streaming handler mirrors it. Once the chat is reloaded from the database, every assistant message has `content: ''` and the merge request is sent with a list of empty strings, which is exactly what the merging model then reports. That is why the failure looks random: merging works right after generating, and fails after a refresh or when reopening the chat.

Read the responses through `getOutputText(message.output) || message.content`, the same fallback already used by every other read site (`ResponseMessage`, `Overview/Node`, `SearchModal`, `ChatItem`, `ChatMenu`, `Navbar/Menu`).

Fixes #26962
2026-08-10 23:31:21 -06:00
Solaris-star 2387ce63cb fix(chat): keep regenerate button visible in High Contrast mode (#27644)
Every action button on a response message gates its visibility on
'isLastMessage || ($settings?.highContrastMode ?? false)' so that buttons
stay visible (not hover-only) when High Contrast mode is on. The two
Regenerate buttons — the one inside RegenerateMenu and the fallback in the
{:else} branch — were missed and still gated on isLastMessage alone, so on
any non-last message the Regenerate icon was invisible until mouse-over even
with High Contrast enabled (#27638).

Add the same highContrastMode clause to both buttons, matching the other
action buttons in this file.
2026-08-10 23:27:42 -06:00
G30andNameless-Monster-Nerd b91bb67b55 fix: hide chat fork actions when chat import permission is disabled (#27711)
Co-authored-by: Nameless-Monster-Nerd <Nameless-Monster-Nerd@users.noreply.github.com>
2026-08-10 23:24:59 -06:00
G30 38a03830c2 fix: show a placeholder when an image cannot be loaded (#27730)
A message referencing a file that no longer exists rendered a broken
image, because nothing anywhere noticed the failed load. What the user saw
depended on the caller's alt text: in a response the alt is the message
content, so the entire reply was rendered inside the image frame, and the
preview still opened full-screen on a dead image.

Image.svelte now tracks a failed load and renders an 'Image unavailable'
placeholder instead, suppressing the preview for a source that cannot be
shown. The failed state resets when the source changes, so a replaced or
corrected URL is retried. An onError callback lets a caller react without
changing behaviour for the eight existing call sites, which are untouched.

Responses now pass the file name (or 'Generated Image') as alt rather than
the message content, which was never a description of the image.
2026-08-10 22:27:13 -06:00
Classic298 eff5c4a2d9 feat: parse :::writing block metadata and use the subject as the block title (#28280)
Newer OpenAI chat models put metadata on the opening line of a colon fence block, like :::writing{variant="email" id="48173" subject="Short question" recipient="mail@example.com"}. The tokenizer matched that line and discarded it, so every block rendered under the same generic "Writing" heading no matter what it contained.

The opening line is now parsed into an attributes map on the token and the header uses it: the subject becomes the title, the recipient follows it and the full string is reachable on hover when the row is too narrow for it. Blocks without metadata render exactly as before, and the other fence types get the parsed attributes for free.

Attributes are read only from inside the {...} braces, not from the whole opening line. Scanning the whole line turned ordinary prose containing key="value" into metadata, and it backtracked quadratically: a 40k character opening line took 586ms to parse, and that runs again on every re-lex while the message streams. Anchored to the braces it is 0.0ms.

Nothing here turns the recipient into a link or a send action. That metadata is model output and can be steered by whatever is in the context, so a prefilled mail action is a separate decision rather than a side effect of parsing.
2026-08-10 19:44:06 -06:00
G30 8836dcb59f fix(ui): fall back to the default avatar when a profile image fails to load (#28270) 2026-08-10 19:18:32 -06:00
Timothy Jaeryang Baek d4461bd6f3 refac 2026-08-10 18:52:18 -06:00
Timothy Jaeryang Baek cbb3aade2b refac 2026-08-05 06:41:30 -05:00
G30 b67804f2b6 fix: do not auto-open artifacts from the sidebar chat hover preview (#27773) 2026-07-31 17:58:02 -04:00
Timothy Jaeryang Baek 810378c0b8 refac 2026-07-27 19:39:36 -04:00
Timothy Jaeryang Baek faeba7c17a refac 2026-07-27 05:28:33 -04:00
Classic298andmaxntv bc600d3f08 fix: escape KaTeX render-error fallback to prevent XSS via {@html} (#26718)
KatexRenderer rendered the raw math source through {@html} whenever renderToString threw. throwOnError only suppresses KaTeX ParseError, so a RangeError (maximum call stack size exceeded, reachable with deeply-nested brace input) escaped into the catch and re-exposed the unescaped source. Because the math tokenizer captures everything between the delimiters verbatim, that source can carry an HTML/JS payload which then executed in the viewer's browser on the application origin, a stored, cross-user XSS reachable through normal chat/channel/shared-chat rendering. Escape the fallback so the source is shown as text and is never injected as HTML. Valid math is unaffected, it still renders through the success path.

Co-authored-by: maxntv <maxntv@users.noreply.github.com>
2026-07-27 01:36:41 -04:00
Timothy Jaeryang Baek 75e54bf46b refac 2026-07-27 01:21:00 -04:00
G30 085d11eef2 chore: drop redundant background repaints so surfaces inherit their parent (#27576)
* chore: drop redundant background repaints so surfaces inherit their parent

Four spots repaint the exact color their parent surface already provides
(bg-white / dark:bg-gray-900 rows inside same-colored pages and modals,
and the selectClass dark repaint inside the connection modals — the
sibling input const is already fully transparent). Visually identical in
stock light and dark; removing them lets instance theming show through
instead of leaving opaque boxes:

- .tiptap tr (app.css) — table rows in notes/editors
- Edit User Group Users tab body rows (common Modal surface)
- AddToolServerModal + AddTerminalServerModal selectClass dark repaint

The matching repaints inside the ModelUsage/UserUsage components are
not part of this change — those files were dead code and were removed
entirely in #27574.

* chore: catch remaining redundant surface repaints missed in the first pass

Same rule as the previous commit — every one of these repaints the exact
color its parent surface already provides, so removal is stock-identical
in light and dark while letting instance theming show through:

- Analytics Dashboard's inline Model Usage / User Activity row markup
  (the Analytics tab renders these tables from Dashboard.svelte itself;
  the unreferenced ModelUsage/UserUsage component files were removed
  in #27574)
- Evaluations Feedbacks + Leaderboard body rows (settings modal surface)
- admin UserList body rows (app page surface)
- chat markdown tables (MarkdownTokens): thead and body rows — unlike
  the tiptap header (gray-850 contrast, untouched), this thead painted
  the page's own color
- CitationsModal source rows (common Modal surface)
- AddConnectionModal selectClass dark repaint — third copy of the same
  const already fixed in AddToolServerModal / AddTerminalServerModal
2026-07-27 00:44:51 -04:00
Timothy Jaeryang Baek 4856afcef8 refac 2026-07-26 18:30:58 -04:00
Timothy Jaeryang Baek b4d13793a3 refac 2026-07-24 02:14:56 -04:00
Timothy Jaeryang Baek 5f6a9d16b2 refac 2026-07-24 00:56:43 -04:00
Timothy Jaeryang Baek d637c2128c refac 2026-07-24 00:56:29 -04:00
Classic298 c895490aa8 fix: blank chat messages on Safari caused by content-visibility virtualization (#26805)
Since v0.10.0 chat messages are virtualized with content-visibility: auto to skip rendering of off-screen messages. Safari's implementation of content-visibility has known paint bugs (WebKit bugs 277573, 281570 and 283846) that can leave the contents of a message unpainted even when it is on screen. On iOS this makes assistant responses render as empty, both in Safari and as a PWA, while the same chats render fine in Chromium and Firefox. This matches the regression window reported in #26712, which appeared when upgrading from 0.9.6 to 0.10.2.

Detect Safari (including all iOS browsers, which use WebKit) with the same user agent check already used in MessageInput and ShareChatModal, and skip the virtualization class there. Safari falls back to rendering all messages like before v0.10.0, while other engines keep the optimization. Verified with a spoofed Safari user agent that messages render without the virtualization class and with content-visibility resolving to visible, while Chromium keeps content-visibility: auto.

Fixes #26712
2026-07-23 23:38:18 -04:00
Timothy Jaeryang Baek e769f9ff4f refac 2026-07-23 17:53:13 -04:00
Timothy Jaeryang Baek 93bd05271c refac 2026-07-23 13:40:30 -04:00
Timothy Jaeryang Baek d0f7da4f45 refac 2026-07-23 04:45:27 -04:00
Timothy Jaeryang Baek 73421c5b42 refac 2026-07-23 02:55:06 -04:00
Timothy Jaeryang Baek b2f7ecd83a refac 2026-07-17 06:45:00 -04:00
Timothy Jaeryang Baek 890bfd0d97 refac 2026-07-17 06:28:07 -04:00
Timothy Jaeryang Baek 346b99c383 refac 2026-07-16 23:39:16 -04:00
Timothy Jaeryang Baek b08f1e8847 refac 2026-07-16 23:38:27 -04:00
Timothy Jaeryang Baek 53b8e6560b refac 2026-07-16 23:35:13 -04:00
Timothy Jaeryang Baek 9be11883b8 refac 2026-07-16 02:05:08 -04:00
Timothy Jaeryang Baek 4d27bfff92 refac 2026-07-16 01:42:24 -04:00
Timothy Jaeryang Baek 92fbc77877 refac 2026-07-16 00:32:27 -04:00
Timothy Jaeryang Baek b23ddeb280 refac 2026-07-16 00:30:44 -04:00
Timothy Jaeryang Baek 1fe862b965 refac 2026-07-16 00:07:41 -04:00
Timothy Jaeryang Baek 07fafe081b refac 2026-07-15 23:34:45 -04:00
Timothy Jaeryang Baek f7af03ff26 refac 2026-07-15 22:48:41 -04:00
Timothy Jaeryang Baek 588f129695 refac 2026-07-15 22:45:00 -04:00
Timothy Jaeryang Baek 9e2536aa57 refac 2026-07-15 22:40:30 -04:00
Timothy Jaeryang Baek e65fdf1ca5 refac 2026-07-15 22:36:47 -04:00
Timothy Jaeryang Baek 185bca8552 refac 2026-07-15 22:34:52 -04:00
Timothy Jaeryang Baek 423cafd4e7 refac 2026-07-15 21:43:47 -04:00
Timothy Jaeryang Baek 9499587c33 refac 2026-07-15 04:25:51 -04:00
Timothy Jaeryang Baek 36cff229a9 refac 2026-07-15 03:17:32 -04:00
Timothy Jaeryang Baek 04311d559d refac 2026-07-15 02:53:39 -04:00
Timothy Jaeryang Baek 622767d724 refac 2026-07-15 02:49:46 -04:00