Commit Graph
3907 Commits
Author SHA1 Message Date
Timothy Jaeryang Baek 55c202e841 refac 2026-08-13 21:48:01 -06:00
Timothy Jaeryang Baek b4738d1a2e refac 2026-08-13 21:31:49 -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 b018feb741 refac 2026-08-13 18:33:51 -06:00
Timothy Jaeryang Baek 133549a87e refac 2026-08-13 18:19:25 -06:00
Timothy Jaeryang Baek 4465f52a3e refac 2026-08-13 18:13:43 -06:00
Timothy Jaeryang Baek f96b717566 refac 2026-08-13 16:04:53 -06:00
Timothy Jaeryang Baek 52c5e3b20d refac 2026-08-13 15:43:27 -06:00
Timothy Jaeryang Baek c93c6d6fc4 refac 2026-08-13 15:42:54 -06:00
Timothy Jaeryang Baek c1f914a626 refac 2026-08-13 15:38:25 -06:00
Timothy Jaeryang Baek bfb68feea7 refac 2026-08-13 15:33:42 -06:00
Timothy Jaeryang Baek 2befa8f796 refac 2026-08-13 15:26:19 -06:00
Timothy Jaeryang Baek 7dfbdd221a refac 2026-08-13 15:20:59 -06:00
Timothy Jaeryang Baek 8d25ad00e2 refac 2026-08-13 14:52:30 -06:00
Timothy Jaeryang Baek 60feca71a6 refac 2026-08-13 14:50:43 -06:00
Timothy Jaeryang Baek a17cb174ad refac 2026-08-13 14:46:08 -06:00
Classic298 5cb87e6630 fix: announce toggle state of integrations menu rows to screen readers (#27667)
Every toggle row in the chat integrations menu (filters, Web Search, Image, Code Interpreter, Tools, Skills) is a button whose on/off state was carried only by the decorative Switch inside it. Screen readers announced the row name and nothing else, so there was no way to tell whether a tool or feature was active without looking at it.

Each row button now carries aria-pressed, and the Switch wrapper is marked inert so the nested role=switch stops competing with the row for the announcement and stops adding a nameless tab stop. Hit testing skips inert content, so clicking the switch still toggles the row.

Tool rows that are not yet authenticated omit aria-pressed: activating those starts an OAuth redirect rather than toggling, so announcing them as an unpressed toggle would be wrong.

The Web Search, Image and Code Interpreter rows also had a state-flipping aria-label ("Disable Web Search") on top of aria-pressed, which announces as "Disable Web Search, pressed" and reads as the opposite of the truth. Removed: the visible row text already names each control.

Fixes #17150
2026-08-13 13:04:13 -06:00
Timothy Jaeryang Baek 31c1ffd55a refac 2026-08-13 00:45:02 -06:00
Timothy Jaeryang Baek e1acd7e7ca refac 2026-08-13 00:38:32 -06:00
Timothy Jaeryang Baek 25802c048e refac 2026-08-13 00:34:10 -06:00
Timothy Jaeryang Baek 3813fd4cdc refac 2026-08-12 23:20:19 -06:00
Timothy Jaeryang Baek 4e03d89414 refac 2026-08-12 23:17:37 -06:00
Timothy Jaeryang Baek c086b80313 refac 2026-08-12 00:51:12 -06:00
Timothy Jaeryang Baek 9c21d4ed3b refac 2026-08-11 17:42:25 -06:00
G30 e17dfae72e fix: enforce the image generation flag on the legacy chat feature path (#27759)
* fix: enforce the image generation flag on the legacy chat feature path

* fix: refresh the config store when image generation is disabled in admin settings

* fix: hide active feature pills when the feature is no longer available
2026-08-11 17:38:10 -06:00
Timothy Jaeryang Baek 1674e5a9ef refac 2026-08-11 12:50:50 -06:00
Timothy Jaeryang Baek f79b443c22 refac 2026-08-11 11:40:24 -06:00
G30 25455e943d fix(ui): resolve owner avatars against the API base url and add a fallback (#28272) 2026-08-11 11:05:35 -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
Timothy Jaeryang Baek e963d36e39 refac 2026-08-11 01:08:58 -06:00
Timothy Jaeryang Baek 9122c24ea2 refac 2026-08-11 00:06:21 -06:00
G30 d959e3e312 fix: scale the settings modal height with tall viewports instead of capping at 54rem (#27615) 2026-08-10 23:34:04 -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
Classic298 fcc130c9bb fix: send stream_options.include_usage for backend-initiated chats (#27661)
Only the frontend added `stream_options: {include_usage: true}` to the completion payload, gated on the model's `usage` capability. Every backend-initiated run builds its own payload (automations, timers, subagents, channels) and omitted it, so those responses came back without token counts and never rendered the usage block, even with the capability enabled on the model.

Set it in `chat_completion` instead, the single handler all of those callers go through, and drop the two duplicate copies (the Anthropic-compat handler and the frontend). Capabilities are read from the resolved model before the custom-model fallback can rebind it, and the flag is applied after the model's `stream_response` override so a non-streaming model is unaffected.

Fixes #27653
2026-08-10 23:30:46 -06:00
G30 8e83d83324 fix: left-edge clipping of the terminal cloud icon and account profile avatar (#27691) 2026-08-10 23:28:17 -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
Classic298 be9af1653e fix: anchor chat input expand button to the input row (#27676)
The "expand input" button was positioned with `fixed top-0 right-0`. That only kept it near the composer by accident: `#message-input-container` sets `backdrop-blur-sm`, and a backdrop-filter makes an element the containing block for fixed descendants, so the button resolved to the top-right corner of the entire composer instead of the text area it belongs to.

That corner is already taken. The `@`-tagged model chip renders as the first row of the same container with its dismiss button at the right end, so with a multi-line prompt and a tagged model the two controls are drawn on top of each other. The attached-files row has the same problem: the button paints over the first thumbnail and its remove button.

Anchor the button to the wrapper that holds the text area instead, using `relative`/`absolute`, so it always sits at the top-right of the input row and below whatever rows precede it. With no chip and no files the position is unchanged. As a side effect the button is no longer a child of the `overflow-auto` scroller, so it can no longer be clipped or scrolled out of view on long prompts.

Fixes #26736
2026-08-10 23:26:53 -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 a3d33b4cf3 fix: hide chat delete actions when the chat delete permission is disabled (#27714)
The chat delete endpoints gate on chat.delete for non-admins, but none of
the UI affordances that reach them were conditioned on it, so a user
without the permission is offered controls that always fail with
"Access prohibited".

Gates every entry point on the same condition the backend enforces,
matching the existing chat.share / chat.export / chat.import gates in the
same components:

- Sidebar chat menu (ChatMenu) Delete item — also covers the Search
  modal's menu, which reuses ChatMenu
- Sidebar Shift+hover inline Delete button (ChatItem)
- Sidebar hidden #delete-chat-button keyboard-shortcut target (ChatItem)
- Chat navbar menu Delete item (Navbar/Menu)
- Search modal Shift-held inline Delete button (SearchModal)
- Settings -> Data Controls -> Delete All Chats
- Settings -> Archived Chats per-row Delete
2026-08-10 23:24:02 -06:00
G30 fe62934be7 fix: derive overview node spacing from rem so branches never overlap (#27995) 2026-08-10 23:23:31 -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
G30 a3e5d0b362 fix(chat): attribute shared chat messages to their author, not the viewer (#28274) 2026-08-10 21:40:54 -06:00
G30 148283f974 fix: release a queued message once an attached URL finishes (#28381)
A message sent while an attachment is still uploading is held in the chat
queue until the file settles. For files that release works, because the
message input calls onUpdate once the upload completes, which refreshes the
queued entry and asks the queue to run.

Attaching a URL takes a different path. uploadWeb sets the item to uploaded
but calls neither onUpdate nor processNextInQueue, and the queue is
otherwise only revisited when a generation finishes or when the chat is
mounted while idle. In a new chat with nothing generating, none of those
happen, so a message queued behind a URL waits with no event able to
release it until the chat is reloaded.

uploadWeb now asks the queue to run when it is done, the same way the file
path already does.
2026-08-10 20:24:35 -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 72a909fd2f refac 2026-08-10 19:16:21 -06:00
Timothy Jaeryang Baek 30d08a42f8 refac 2026-08-10 18:52:58 -06:00
Timothy Jaeryang Baek 407c40f72c refac 2026-08-10 18:52:34 -06:00
Timothy Jaeryang Baek b4d3b27caf refac 2026-08-10 18:52:27 -06:00