Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions webui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ This change log covers only the frontend library (webui) of Open VSX.
- Migrate admin dashboard to use `@tanstack/react-query` ([#1917](https://github.com/eclipse-openvsx/openvsx/pull/1917)
- Replace formatting from `stylistic` with `prettier` ([#1916](https://github.com/eclipse-openvsx/openvsx/pull/1916))
- Upgrade to vite 8+ and disable manual chunks for bundling ([#1989](https://github.com/eclipse-openvsx/openvsx/pull/1989))
- Upgrade to react-router 7: import everything from `react-router` instead of `react-router-dom`. Consumers passing `additionalRoutes` must upgrade to react-router 7 as well

### Fixed

- Refresh the extension version list in the delete views when a delete fails with a conflict
- Keep the hero-to-navbar search morph working under react-router 7, which wraps location updates in `React.startTransition`

### Dependencies

Expand All @@ -37,6 +39,8 @@ This change log covers only the frontend library (webui) of Open VSX.
- Bump tar from `7.5.16` to `7.5.22` ([#1994](https://github.com/eclipse-openvsx/openvsx/pull/1994))
- Bump js-yaml from `4.2.0` to `4.3.0` ([#1976](https://github.com/eclipse-openvsx/openvsx/pull/1976))
- Bump dompurify from `3.4.11` to `3.4.12` ([#1984](https://github.com/eclipse-openvsx/openvsx/pull/1984))
- Bump react-router from `6.30.4` to `7.18.1`
- Remove react-router-dom and its stale `@types/react-router-dom` v5 typings

## [v1.0.2] (23/06/2026)

Expand Down
4 changes: 1 addition & 3 deletions webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@
"react-dropzone": "^14.2.3",
"react-helmet-async": "^2.0.5",
"react-infinite-scroller": "^1.2.6",
"react-router": "^6.30.4",
"react-router-dom": "^6.30.4"
"react-router": "^7.18.1"
},
"resolutions": {
"@types/react": "^18.2.14",
Expand All @@ -94,7 +93,6 @@
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react-infinite-scroller": "^1.2.0",
"@types/react-router-dom": "^5.3.0",
"@types/react-transition-group": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^8.56.0",
"@typescript-eslint/parser": "^8.56.0",
Expand Down
2 changes: 1 addition & 1 deletion webui/src/components/extension-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
********************************************************************************/

import { forwardRef, FunctionComponent, memo } from 'react';
import { Link as RouteLink } from 'react-router-dom';
import { Link as RouteLink } from 'react-router';
import { Paper, Typography, Box, Fade, Skeleton } from '@mui/material';
import { CSSObject, styled, Theme } from '@mui/material/styles';
import SaveAltIcon from '@mui/icons-material/SaveAlt';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/components/extension/extension-detail-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import { FunctionComponent, ReactNode, useEffect, useState } from 'react';
import { Box, Button, Divider, Stack, Typography } from '@mui/material';
import { Link as RouteLink } from 'react-router-dom';
import { Link as RouteLink } from 'react-router';
import { Extension, VERSION_ALIASES, VersionTargetPlatforms } from '../../extension-registry-types';
import { ExtensionHeader } from './extension-header';
import { ExtensionStatusChips } from './extension-status-chips';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/components/sanitized-markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { alert } from '@mdit/plugin-alert';
import DOMPurify from 'dompurify';
import { Theme, styled } from '@mui/material/styles';
import linkIcon from './link-icon';
import { useLocation } from 'react-router-dom';
import { useLocation } from 'react-router';

const Markdown = styled('div')(({ theme }: { theme: Theme }) => ({
// The container owns the leading gap; the first block shouldn't add its own
Expand Down
19 changes: 17 additions & 2 deletions webui/src/context/search/search-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/

import { createContext, FunctionComponent, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import {
createContext,
FunctionComponent,
ReactNode,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useState
} from 'react';
import { useLocation, useSearchParams } from 'react-router';
import { SearchFocusProvider } from './search-focus-context';
import { PageSearchBarProvider } from './page-search-bar-context';
import { resolveSearchViewTransition } from './search-view-transition';

export interface SearchContextValue {
query: string;
Expand Down Expand Up @@ -45,6 +55,11 @@ export const SearchProvider: FunctionComponent<{ children: ReactNode }> = ({ chi
const [query, setQuery] = useState('');
const [searchParams] = useSearchParams();
const urlQuery = searchParams.get('q') ?? '';
const location = useLocation();

// This provider outlives search navigations, so it releases the hero → nav bar
// morph once the new route has committed.
useLayoutEffect(resolveSearchViewTransition, [location]);

// Keep the fields in sync with URL query changes (back/forward, shared links, category tiles).
useEffect(() => {
Expand Down
54 changes: 54 additions & 0 deletions webui/src/context/search/search-view-transition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/

/** Release a transition whose navigation never landed, rather than suspend rendering forever. */
const COMMIT_TIMEOUT_MS = 250;

let pendingCommit: (() => void) | undefined;

/**
* Runs `update` inside a view transition, holding it open until React has
* committed the resulting navigation. react-router wraps location updates in
* `React.startTransition`, which `flushSync` cannot force, so the browser would
* otherwise snapshot the new state while the old route was still mounted and the
* morph would animate an element to its own position.
*/
export function startSearchViewTransition(update: () => void): ViewTransition | undefined {
if (typeof document.startViewTransition !== 'function') {
update();
return undefined;
}

return document.startViewTransition(
() =>
new Promise<void>(resolve => {
let timeout = 0;
const release = () => {
window.clearTimeout(timeout);
pendingCommit = undefined;
resolve();
};
timeout = window.setTimeout(release, COMMIT_TIMEOUT_MS);
pendingCommit = release;
update();
})
);
}

/**
* Lets the browser snapshot the new state. Call from a component that outlives the
* navigation — the field that starts the morph unmounts as part of it.
*/
export function resolveSearchViewTransition(): void {
pendingCommit?.();
}
2 changes: 1 addition & 1 deletion webui/src/default/default-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import '../main.css';
import { createRoot } from 'react-dom/client';
import { useMemo } from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { BrowserRouter } from 'react-router-dom';
import { BrowserRouter } from 'react-router';
import { ThemeProvider } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import { ExtensionRegistryService } from '../extension-registry-service';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/default/menu-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import { FunctionComponent, PropsWithChildren, useContext, useRef, useState } from 'react';
import { Avatar, Button, IconButton, Link, Menu, MenuItem, Typography } from '@mui/material';
import { useLocation, useNavigate, Link as RouteLink } from 'react-router-dom';
import { useLocation, useNavigate, Link as RouteLink } from 'react-router';
import { UserAvatar } from '../pages/user/avatar';
import { UserSettingsRoutes } from '../pages/user/user-settings-routes';
import { alpha, styled, Theme } from '@mui/material/styles';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/default/page-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { FunctionComponent, ReactNode } from 'react';
import { Helmet } from 'react-helmet-async';
import { Typography, Box, Link } from '@mui/material';
import { Link as RouteLink, Route, useParams } from 'react-router-dom';
import { Link as RouteLink, Route, useParams } from 'react-router';
import GitHubIcon from '@mui/icons-material/GitHub';
import CallSplitIcon from '@mui/icons-material/CallSplit';
import BugReportIcon from '@mui/icons-material/BugReport';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/header-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Menu, IconButton, useTheme } from '@mui/material';
import useMediaQuery from '@mui/material/useMediaQuery';
import MenuIcon from '@mui/icons-material/Menu';
import { MainContext } from './context';
import { useLocation } from 'react-router-dom';
import { useLocation } from 'react-router';

export const HeaderMenu: FunctionComponent = () => {
const theme = useTheme();
Expand Down
2 changes: 1 addition & 1 deletion webui/src/hooks/use-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
********************************************************************************/

import { useCallback, useMemo } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { useLocation, useNavigate, useSearchParams } from 'react-router';
import { ExtensionListRoutes } from '../pages/extension-list/extension-list-routes';
import { ExtensionCategory, SortBy, SortOrder } from '../extension-registry-types';

Expand Down
2 changes: 1 addition & 1 deletion webui/src/layout/app-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { FunctionComponent, useContext, useState } from 'react';
import { Box, Container, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import type { Theme } from '@mui/material/styles';
import { Link as RouteLink } from 'react-router-dom';
import { Link as RouteLink } from 'react-router';
import { KbdKey } from '../components/kbd-key';
import { useShortcut } from '../hooks/use-shortcut';
import { Eyebrow, accentHover, focusOutline } from '../components/page-primitives';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/layout/app-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
********************************************************************************/

import { FunctionComponent, lazy, Suspense, useContext, useEffect, useState } from 'react';
import { Routes, Route, useNavigate } from 'react-router-dom';
import { Routes, Route, useNavigate } from 'react-router';
import { Box } from '@mui/material';
import { styled } from '@mui/material/styles';
import { Banner } from '../components/banner';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/layout/app-navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { FunctionComponent, useContext, useEffect, useRef, useState } from 'react';
import { AppBar, Box, Toolbar } from '@mui/material';
import { alpha, styled, useTheme } from '@mui/material/styles';
import { Link as RouteLink } from 'react-router-dom';
import { Link as RouteLink } from 'react-router';
import { HeaderMenu } from '../header-menu';
import { MainContext } from '../context';
import { usePageSearchBar } from '../context/search/page-search-bar-context';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/layout/nav-search-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
useState
} from 'react';
import { Box } from '@mui/material';
import { useLocation } from 'react-router-dom';
import { useLocation } from 'react-router';
import { ExtensionSearchfield } from '../components/extension-searchfield';
import { ExtensionListRoutes } from '../pages/extension-list/extension-list-routes';
import { useSearch, SEARCH_DEBOUNCE_MS } from '../hooks/use-search';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/layout/scroll-to-top.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
********************************************************************************/

import { FunctionComponent, useLayoutEffect } from 'react';
import { useLocation, useNavigationType } from 'react-router-dom';
import { useLocation, useNavigationType } from 'react-router';

// BrowserRouter leaves window scroll untouched on navigation, so a page opened
// from deep in a long list would start at that old offset. Reset to the top on
Expand Down
2 changes: 1 addition & 1 deletion webui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import { FunctionComponent, ReactNode, useEffect, useState, useRef, lazy, Suspense } from 'react';
import { CssBaseline } from '@mui/material';
import { Route, Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { queryClient } from './query-client';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/admin-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { FunctionComponent, ReactNode, useContext, lazy, Suspense } from 'react';
import { Box, Container, CssBaseline, Typography, IconButton } from '@mui/material';
import { styled } from '@mui/material/styles';
import { Route, Routes, useNavigate } from 'react-router-dom';
import { Route, Routes, useNavigate } from 'react-router';
import AccountBoxIcon from '@mui/icons-material/AccountBox';
import AssignmentIndIcon from '@mui/icons-material/AssignmentInd';
import BarChartIcon from '@mui/icons-material/BarChart';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/admin-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { FunctionComponent } from 'react';
import { AppBar, Breadcrumbs, IconButton, Link, Toolbar, Typography } from '@mui/material';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import { Link as RouterLink, useLocation } from 'react-router-dom';
import { Link as RouterLink, useLocation } from 'react-router';

export interface AdminHeaderProps {
routeNames: Record<string, string>;
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/admin-sidepanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*****************************************************************************/

import { FunctionComponent } from 'react';
import { useLocation } from 'react-router-dom';
import { useLocation } from 'react-router';
import { Sidepanel } from '../../components/sidepanel/sidepanel';
import { NavigationItem } from '../../components/sidepanel/navigation-item';
import { isNavGroup, NavEntry } from './nav-types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { FC, useState } from 'react';
import { Box, Typography, Button, Alert, LinearProgress } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import type { Customer } from '../../../extension-registry-types';
import { handleError } from '../../../utils';
import { useAdminUsageStats } from '../usage-stats/use-usage-stats';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
type PaperProps,
Paper
} from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';
import { Link as RouterLink } from 'react-router';
import { AdminDashboardRoutes } from '../admin-dashboard-routes';
import { MainContext } from '../../../context';
import { Customer, UserData } from '../../../extension-registry-types';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/customers/customers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { DeleteCustomerDialog } from './delete-customer-dialog';
import { createRoute, handleError } from '../../../utils';
import { createMultiSelectFilterOperators, createArrayContainsFilterOperators } from '../components';
import { AdminDashboardRoutes } from '../admin-dashboard-routes';
import { Link } from 'react-router-dom';
import { Link } from 'react-router';
import { useCreateCustomer, useCustomers, useDeleteCustomer, useUpdateCustomer } from './use-customers';

export const Customers: FC = () => {
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/extension-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
********************************************************************************/

import { FunctionComponent, useContext, useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router';
import { Button, Typography } from '@mui/material';

import { MainContext } from '../../context';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/namespace-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import { FunctionComponent, useState, useContext, useEffect, ReactNode } from 'react';
import { Typography, Box } from '@mui/material';
import { useParams, useNavigate } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router';
import { NamespaceDetail, NamespaceDetailConfigContext } from '../user/user-settings-namespace-detail';
import { ButtonWithProgress } from '../../components/button-with-progress';
import { MainContext } from '../../context';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/publisher-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import SearchIcon from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear';
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
import PersonIcon from '@mui/icons-material/Person';
import { useParams, useNavigate } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router';
import { UserRelationships } from '../../extension-registry-types';
import { ErrorResponse } from '../../server-request';
import { MainContext } from '../../context';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/publisher-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
Typography
} from '@mui/material';
import { useIsMutating } from '@tanstack/react-query';
import { Link as RouterLink } from 'react-router-dom';
import { Link as RouterLink } from 'react-router';
import GitHubIcon from '@mui/icons-material/GitHub';
import PersonIcon from '@mui/icons-material/Person';
import FolderSharedIcon from '@mui/icons-material/FolderShared';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import { FC, useContext, useMemo } from 'react';
import { Box, Alert } from '@mui/material';
import { useParams, useNavigate } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router';
import { MainContext } from '../../../context';
import type { Customer } from '../../../extension-registry-types';
import { handleError } from '../../../utils';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/admin-dashboard/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import { FunctionComponent } from 'react';
import { Box, Card, CardActionArea, CardContent, Divider, Grid, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { isNavGroup, NavEntry, RouteEntry } from './nav-types';

interface NavSection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import { FunctionComponent, ReactNode, useContext, useEffect, useState, useRef, useMemo } from 'react';
import { Box, Theme, Typography, Button, Link, NativeSelect, SxProps, styled, Grid, Stack } from '@mui/material';
import { Link as RouteLink, useNavigate, useParams } from 'react-router-dom';
import { Link as RouteLink, useNavigate, useParams } from 'react-router';
import HomeIcon from '@mui/icons-material/Home';
import GitHubIcon from '@mui/icons-material/GitHub';
import BugReportIcon from '@mui/icons-material/BugReport';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/extension-detail/extension-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
decomposeColor
} from '@mui/material';
import { alpha, styled } from '@mui/material/styles';
import { Link as RouteLink, Route, Routes, useNavigate, useParams } from 'react-router-dom';
import { Link as RouteLink, Route, Routes, useNavigate, useParams } from 'react-router';
import { MainContext } from '../../context';
import { createRoute, formatCompactNumber } from '../../utils';
import { DelayedLoadIndicator } from '../../components/delayed-load-indicator';
Expand Down
2 changes: 1 addition & 1 deletion webui/src/pages/home/get-involved.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { FunctionComponent, ReactNode } from 'react';
import { Box, Container, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import type { Theme } from '@mui/material/styles';
import { Link as RouteLink } from 'react-router-dom';
import { Link as RouteLink } from 'react-router';
import { HomeInvolvementCard } from '../../page-settings';
import { Eyebrow, focusOutline } from '../../components/page-primitives';

Expand Down
Loading