Commit Graph

9692 Commits

Author SHA1 Message Date
Timothy Jaeryang Baek 009999f363 refac 2026-08-08 15:47:10 -06:00
Timothy Jaeryang Baek c1c07cbe0f refac 2026-08-05 07:57:55 -05:00
Timothy Jaeryang Baek 6c4d0ace16 refac 2026-08-05 07:44:06 -05:00
Timothy Jaeryang Baek 29eeda9f9a refac 2026-08-05 07:12:28 -05:00
Timothy Jaeryang Baek 9c7ce154e7 refac 2026-08-05 07:04:30 -05: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 3becec6ccf refac 2026-07-31 17:35:34 -04:00
Timothy Jaeryang Baek 5b333d75c6 refac 2026-07-31 17:34:39 -04:00
joaoback 8d333335b9 i18n: add pt-BR translations for newly added UI items and consistency pass (#27814)
New **pt-BR** translations for items introduced in the latest releases, plus a consistency/quality pass across existing strings (grammar, tone, capitalization, pluralization). Placeholders and hotkeys preserved. No logic changes.
2026-07-31 17:25:10 -04:00
Timothy Jaeryang Baek 810378c0b8 refac 2026-07-27 19:39:36 -04:00
Timothy Jaeryang Baek b6b16d5871 refac 2026-07-27 19:24:03 -04:00
Timothy Jaeryang Baek faeba7c17a refac 2026-07-27 05:28:33 -04:00
Timothy Jaeryang Baek c004b4ecb5 chore: format 2026-07-27 04:38:46 -04:00
Timothy Jaeryang Baek e8fda1c7a0 refac 2026-07-27 03:59:12 -04:00
G30 867006acce fix: keep admin access to connections without access grants when admin bypass is disabled (#27581) 2026-07-27 03:39:40 -04:00
Timothy Jaeryang Baek db2d24896b refac 2026-07-27 03:32:21 -04:00
Classic298 147c3b6ac8 fix: mark the open chat with aria-current in the sidebar (WCAG 1.4.1, 4.1.2) (#27502)
On latest `dev`, the chat that is currently open is indicated **only** by a background tint: `bg-black/[0.035]` in light mode and `dark:bg-white/[0.045]` in dark.

Against the page background that is **1.07:1** in light and **1.05:1** in dark. It is close to imperceptible for sighted users, and it carries no programmatic state at all, so assistive technology has no way to tell which entry in the list is the one being viewed.

Breaks WCAG 1.4.1 Use of Color (Level A), since the state is conveyed by colour alone, and 4.1.2 Name, Role, Value (Level A), since the state is not exposed.

Fix: set `aria-current="page"` on the chat link when it is the open chat, using the same `id === $chatId` condition that already drives the visual highlight, so the two cannot drift apart. `'page'` is the correct token because the trigger is a real navigation to `/c/{id}`.

This matches the existing pattern in `routes/(app)/workspace/+layout.svelte` and `chat/Placeholder/ChatList.svelte`, which already set `aria-current` for their active entries.

Verified that the bits-ui `LinkPreview.Trigger` forwards unknown attributes to the rendered anchor and does not set `aria-current` itself, so the attribute reaches the DOM.

This does not change the visual contrast of the highlight, which is worth addressing separately.

Severity: Serious. In a long chat list there is no reliable way to tell which chat is open.

### Contributor License Agreement

<!--
🚨 DO NOT DELETE THE TEXT BELOW 🚨
Keep the "Contributor License Agreement" confirmation text intact.
Deleting it will trigger the CLA-Bot to INVALIDATE your PR.

Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA.
-->

- [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.

> [!NOTE]
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.
2026-07-27 03:20:32 -04:00
Timothy Jaeryang Baek 2f2bf38e34 refac 2026-07-27 03:19:10 -04:00
Timothy Jaeryang Baek f59d86a10c refac 2026-07-27 03:18:12 -04:00
Classic298 71511ccd5a fix: make sidebar folder rows keyboard operable (WCAG 2.1.1, 4.1.2) (#27509)
On latest `dev`, the sidebar folder row is a bare `<div>` carrying `on:click` (navigate into the folder) and `on:dblclick` (rename). It has **no `role`, no `tabindex` and no key handler**, so opening a folder is impossible from the keyboard.

The nested chevron `<button>` is focusable, but it only expands the folder in place, it does not navigate to it, so there is no keyboard route to the folder page at all.

Breaks WCAG 2.1.1 Keyboard (Level A) and 4.1.2 Name, Role, Value (Level A). The Svelte compiler already flags this file with `a11y_click_events_have_key_events`; after this change the component compiles with zero a11y warnings.

Fix: apply the row pattern already used elsewhere in this codebase (`workspace/Prompts.svelte`, `workspace/Knowledge.svelte`, `admin/Functions.svelte`), namely `role="button"`, `tabindex="0"` and a keydown handler for Enter and Space, with the same `e.currentTarget !== e.target` guard and the same `shouldIgnoreRowClick` helper those files use.

That guard matters more here than in the files it was copied from: the rename `<input>` is rendered **inside** this row, so without it typing a space in the rename field would be swallowed and navigate away, and Enter would both save the rename and navigate.

The navigation body is extracted to `openFolderHandler` because it now has two callers. The keyboard path calls it directly rather than through the 100ms `clickTimer`, which exists only to disambiguate single from double click and has no keyboard equivalent.

A dead `(e) => e.stopPropagation();` expression statement in the click handler is removed. It allocated an arrow function and discarded it without ever calling it.

The `…` folder menu is still `invisible group-hover:visible` and therefore unreachable, so rename, share, delete, export and new subfolder remain keyboard-inaccessible until that is addressed. That is fixed repo wide in a separate PR that replaces the `invisible group-hover:visible` pattern, so it is deliberately not touched here to avoid conflicting on the same line.

Folder reparenting by drag still has no keyboard alternative, which is a separate WCAG 2.5.7 issue needing a "Move" menu action.

Severity: Critical. Folders cannot be opened without a pointing device.

### Contributor License Agreement

<!--
🚨 DO NOT DELETE THE TEXT BELOW 🚨
Keep the "Contributor License Agreement" confirmation text intact.
Deleting it will trigger the CLA-Bot to INVALIDATE your PR.

Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA.
-->

- [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.

> [!NOTE]
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.

Co-authored-by: Tim Baek <tim@openwebui.com>
2026-07-27 03:16:25 -04:00
Timothy Jaeryang Baek 9707d3a5c2 refac 2026-07-27 03:09:25 -04:00
Timothy Jaeryang Baek 69e449e318 refac 2026-07-27 03:05:26 -04:00
Classic298 5278eb906e fix: block external resource loading in Vega chart rendering to prevent client-side SSRF (#26806)
* fix: block external resource loading in Vega chart rendering to prevent client-side SSRF

renderVegaVisualization renders vega/vega-lite chart specs that appear in untrusted chat content (shared chats, channel messages, assistant/RAG/tool output) by constructing a Vega View with no restricted loader, so a crafted spec could make a viewer's browser issue arbitrary outbound requests. There are two paths: data.url (and topojson/geo data) is fetched via loader.load at view construction, and image-mark urls are resolved via loader.sanitize and emitted as <image href> into the output SVG, fetched by the browser when the SVG is displayed. Both are client-side SSRF, and against same-origin or CORS-permissive targets allow reading the response back into the page. Pass a loader that rejects external resource loads on both paths, load throws and sanitize rejects http(s)/protocol-relative URIs, so rendered charts can only use inline data. Inline data.values charts are unaffected.

Co-authored-by: Zureno <Zureno@users.noreply.github.com>

* fix: resolve Vega image urls with the URL parser before blocking external loads

The previous scheme regex could be bypassed with encodings the browser URL parser
normalizes away, such as a leading tab or newline before the scheme and backslash
variants of protocol-relative urls like /\evil.com, which would still be emitted
into the rendered SVG and fetched externally on display. Resolve the uri against
document.baseURI with the browser's own URL parser and only allow data: uris and
same-origin results, so the check cannot diverge from what the browser would
actually fetch. Also shortens the explanatory comments.

---------

Co-authored-by: Zureno <Zureno@users.noreply.github.com>
2026-07-27 03:03:30 -04:00
Timothy Jaeryang Baek 7d77efe0f1 refac 2026-07-27 02:47:09 -04:00
Timothy Jaeryang Baek e28b391e51 refac 2026-07-27 02:29:56 -04:00
Timothy Jaeryang Baek 498cdab9a5 refac 2026-07-27 02:23:33 -04:00
Timothy Jaeryang Baek f91ac068d0 refac 2026-07-27 02:12:40 -04:00
Timothy Jaeryang Baek 3492021361 refac 2026-07-27 02:04:44 -04:00
Timothy Jaeryang Baek 7801909d27 a11y
Co-Authored-By: Vince Castillo, PhD <154394560+professorcastillo@users.noreply.github.com>
2026-07-27 01:43:58 -04:00
Classic298 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 067cf31f40 refac 2026-07-27 01:32:41 -04:00
Timothy Jaeryang Baek 8a90bf6256 chore: format 2026-07-27 01:22:08 -04:00
G30 cce3b68265 fix: enforce a single open user profile preview across ProfilePreview instances (#27578) 2026-07-27 01:21:57 -04:00
Timothy Jaeryang Baek 75e54bf46b refac 2026-07-27 01:21:00 -04:00
Timothy Jaeryang Baek 89caa7c849 refac 2026-07-27 01:19:34 -04:00
Vince Castillo, PhD 6379d37863 fix: expose ConfirmDialog with dialog role and label its input (WCAG 4.1.2, 3.3.2) (#26769)
ConfirmDialog trapped focus and closed on Escape but its container was a plain
  div, so screen readers did not announce it as a modal dialog. Its text input
  also had only a placeholder, giving no persistent accessible name. Add
  role=dialog / aria-modal / aria-label / tabindex to the dialog surface and an
  aria-label to the textarea.

  Relates to #2790

Co-authored-by: Tim Baek <tim@openwebui.com>
2026-07-27 01:15:12 -04:00
Classic298 7e96c53a20 feat: multiselect valve input type with static or dynamic options (#26884)
Adds a multiselect input type for Valves and UserValves so plugin authors can let users pick multiple values from static or runtime-resolved options instead of maintaining comma-separated text fields with hardcoded allowed-value lists in the description.

ENABLED_ITEMS: list[str] = Field(
    default=["foo"],
    json_schema_extra={"input": {"type": "multiselect", "options": "get_item_options"}},
)

@classmethod
def get_item_options(cls):
    return [{"value": "foo", "label": "Foo"}, {"value": "bar", "label": "Bar"}]

Options accept the same shapes as the existing select input: either a static list (strings or {value, label} dicts) or a classmethod name resolved at request time (including __user__ context for UserValves). No backend changes are needed because resolve_valves_schema_options already resolves options independently of the input type.

The new MultiSelect component follows the existing Select portal dropdown pattern and renders checkbox rows that stay open while toggling, with the selected labels shown in the trigger. Values bind as a real string array end to end: the array-to-comma-string conversions in the chat controls valves panel and the valves modal are skipped for multiselect fields, so the stored valve is a native list[str] validated by Pydantic.

Requested in #26848.
2026-07-27 01:11:38 -04:00
Timothy Jaeryang Baek 6732852ce6 refac 2026-07-27 01:05:52 -04:00
Timothy Jaeryang Baek de681aa543 refac 2026-07-27 01:03:10 -04:00
Timothy Jaeryang Baek 5c505c1119 refac 2026-07-27 00:48:30 -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
G30 8295f2dacc chore: remove dead admin Analytics ModelUsage and UserUsage components (#27574)
Nothing in the tree imports either component; the admin Analytics tab
renders its own inline copies of both tables directly from
Dashboard.svelte. Both files landed with the dashboard in a4ad34841
(feat: analytics frontend dashboard) but were never wired into it.

The remaining name matches elsewhere (the getUserUsage API and
UserUsage* types in src/lib/apis/users/index.ts, consumed by
chat/Settings/Usage.svelte, plus the backend usage endpoints) belong to
the unrelated per-user usage feature and are untouched.
2026-07-27 00:35:08 -04:00
Timothy Jaeryang Baek 57e60423b9 refac 2026-07-27 00:27:38 -04:00
Timothy Jaeryang Baek 20647bd2d5 chore: format 2026-07-27 00:12:47 -04:00
Timothy Jaeryang Baek e53ff57fb5 refac 2026-07-27 00:12:16 -04:00
Timothy Jaeryang Baek 8ddf119570 refac 2026-07-26 23:55:37 -04:00
Timothy Jaeryang Baek e5a08d5220 refac 2026-07-26 23:54:16 -04:00
Timothy Jaeryang Baek ba7c95f7ef refac 2026-07-26 23:50:09 -04:00
G30 cb64068893 feat: add default file upload mode user setting (#20900)
* feat: add default upload mode setting

Add user setting to configure the default upload mode for files, allowing users to choose between "Using Entire Document" (full context) and "Using Focused Retrieval" (RAG processing) as the default behavior.

* i18n: sync locale catalogs for the new upload mode strings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: re-trigger CI (previous run hit the pre-existing Node heap OOM, see #27254)

* fix: apply the default upload mode at upload time so the payload carries it

The previous approach only pre-set the modal toggle's visual state on
mount; item.context is written solely by the Switch's on:change, so the
sent files kept context: undefined and the backend never saw 'full'. It
also showed a misleading ON state for legacy context-less files, since
FileItemModal mounts with every FileItem chip render.

Stamp context on the fileItem in uploadFileHandler instead (before
...itemData, so callers passing an explicit context still win) and
revert the FileItemModal hunk — the modal already renders from
item.context alone.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 22:50:03 -05:00