Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
609184a
feat: refreshing UX/UI
gnugomez Jul 1, 2026
9b33fa9
chore: cleaning up redesign implementation
gnugomez Jul 1, 2026
053f2be
feat: adding some more shortcuts
gnugomez Jul 2, 2026
466a2ac
fix: removing some unnecessary hooks/contexts
gnugomez Jul 2, 2026
1b58875
refactor: improving search implementation following react good practices
gnugomez Jul 2, 2026
10800fe
fix: wrong negative margin
gnugomez Jul 2, 2026
a4885ae
feat: some UI consistency improvements
gnugomez Jul 2, 2026
13a68ee
fix: cancel pending debounced search on unmount and route change
gnugomez Jul 2, 2026
1661c30
fix: redirect legacy search URLs to the new search route
gnugomez Jul 2, 2026
6c761c7
fix: restore ExtensionCategory and getCategories in the public API
gnugomez Jul 2, 2026
c40011c
fix: fetch home page rows with TanStack Query
gnugomez Jul 2, 2026
7694b7d
fix: use router location in useSearch and push history when entering …
gnugomez Jul 2, 2026
0fa5b60
fix: ignore global shortcuts while a popup or dialog has focus
gnugomez Jul 2, 2026
420fd28
fix: remove redundant debounce from extension list fetches
gnugomez Jul 2, 2026
d8e6773
refactor: consolidate floating surface styling in the theme
gnugomez Jul 2, 2026
cffddb0
fix: keep footer below the fold on short pages
gnugomez Jul 2, 2026
389bf28
refactor: convert px values to rem
gnugomez Jul 2, 2026
9d2ec88
fix: enforce viewport min-height on main content
gnugomez Jul 2, 2026
825b3bc
refactor: merge category data into a single module
gnugomez Jul 2, 2026
83ba1d6
refactor: move query sync into SearchProvider and slim useSearch
gnugomez Jul 2, 2026
29aa0ca
refactor: move hooks and context files to their conventional directories
gnugomez Jul 2, 2026
d5760a7
chore: remove dead props and simplify card stagger
gnugomez Jul 2, 2026
47085d1
feat: improve accessibility of search and category controls
gnugomez Jul 2, 2026
791208f
refactor: share focus ring and hover primitives, dedup menu item styles
gnugomez Jul 2, 2026
8fde429
fix: align namespace detail and search page with new card and icon types
gnugomez Jul 2, 2026
a7143d7
feat: scroll selected category pill into view
gnugomez Jul 2, 2026
7847693
feat: add keyboard focus outline to pills, cards and footer controls
gnugomez Jul 2, 2026
83b50c2
fix: stop sidebar scroll container clipping focus outlines
gnugomez Jul 2, 2026
63c1efd
fix: match compact navbar mark size to the full logo
gnugomez Jul 2, 2026
130f595
fix: center toolbar logo vertically on all pages
gnugomez Jul 2, 2026
404d5f9
fix: balance spacing around mobile navbar mark
gnugomez Jul 2, 2026
626ba7b
feat: open first result on Enter from the search input
gnugomez Jul 2, 2026
9d2b4ca
feat: share a results cursor between the search input and the grid
gnugomez Jul 2, 2026
9deca40
refactor: consume signals as objects with typed payloads
gnugomez Jul 2, 2026
381d357
refactor: renaming seach focus signals
gnugomez Jul 2, 2026
231690d
refactor: rebuild the results grid navigation as a declarative cursor
gnugomez Jul 3, 2026
3d7e809
feat: make the home page and footer fully replaceable
gnugomez Jul 3, 2026
d3c6628
fix: only push the footer under the fold on mobile
gnugomez Jul 3, 2026
7d40bdf
fix: ease the navbar tint fade to remove its visible edge
gnugomez Jul 3, 2026
fa0e128
feat: make the search field background translucent
gnugomez Jul 3, 2026
ea4927a
fix: adding scroll-to-top component
gnugomez Jul 3, 2026
ca89a61
chore: updating changelog
gnugomez Jul 3, 2026
9947d33
chore: deleting unnecesary package-lock
gnugomez Jul 3, 2026
76693ad
chore: use full license headers
gnugomez Jul 3, 2026
bf947f5
feat: using curated sections sortby as on see all filter
gnugomez Jul 3, 2026
4e79592
feat: expose keyboard shortcuts and co-locate them with their controls
gnugomez Jul 3, 2026
2ac832e
chore: updating yarn.lock
gnugomez Jul 3, 2026
282a985
feat: using Section primitive in search
gnugomez Jul 3, 2026
81f0832
fix: theme has wrong xs value
gnugomez Jul 3, 2026
18c3368
fix: improving layered navbar blur
gnugomez Jul 5, 2026
3b8241b
feat: improving search-bar focust behaviour
gnugomez Jul 5, 2026
622ad0e
refactor: split search coordination into focus and page-bar contexts
gnugomez Jul 5, 2026
81795c8
fix: harden search field behavior across entry points
gnugomez Jul 5, 2026
c3f44de
fix: removing unnecessary footer links + adjusting bottom padding
gnugomez Jul 6, 2026
8176db4
Merge origin/main into gnugomez/main/redesing
gnugomez Jul 6, 2026
36cb623
fix: inconsistencies with the theme
gnugomez Jul 6, 2026
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
9 changes: 9 additions & 0 deletions webui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ This change log covers only the frontend library (webui) of Open VSX.

### Added

- Add a home page with hero search, popular searches, a category browser, curated extension rows and get-involved cards
- Add a dedicated search page under `/search` with query, category, sort field and sort order synced to the URL
- Add global keyboard shortcuts
- Add keyboard navigation of search results: `↑`/`↓`
- Add a structured footer
- Add scroll-to-top on forward navigation
- Support searching users and managing their roles in the admin dashboard ([#1847](https://github.com/eclipse-openvsx/openvsx/pull/1847))
- Added an extension details page to admin dashboard and user settings ([#1939](https://github.com/eclipse-openvsx/openvsx/pull/1939))

### Changed

- Redesign the web UI: new navbar with integrated search field, new theme, extension cards, category pills and page layout
- Improve accessibility: visible focus outlines on interactive controls
- Morph the hero search into the navbar search field using the View Transitions API
- 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))

Expand Down
3 changes: 0 additions & 3 deletions webui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
</head>

<body>
Expand Down
3 changes: 3 additions & 0 deletions webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource-variable/geist": "^5.2.9",
"@fontsource-variable/geist-mono": "^5.2.8",
"@fontsource/roboto": "^5.2.10",
"@mdit/plugin-alert": "^0.22.3",
"@mui/base": "^5.0.0-beta.9",
"@mui/icons-material": "^5.15.14",
Expand Down
65 changes: 65 additions & 0 deletions webui/src/components/categories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/********************************************************************************
* 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
********************************************************************************/

import { FunctionComponent } from 'react';
import { SvgIconProps } from '@mui/material';
import DataObjectIcon from '@mui/icons-material/DataObject';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import SpellcheckIcon from '@mui/icons-material/Spellcheck';
import FormatAlignLeftIcon from '@mui/icons-material/FormatAlignLeft';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import PaletteIcon from '@mui/icons-material/Palette';
import BugReportIcon from '@mui/icons-material/BugReport';
import AccountTreeIcon from '@mui/icons-material/AccountTree';
import KeyboardIcon from '@mui/icons-material/Keyboard';
import ExtensionIcon from '@mui/icons-material/Extension';
import BarChartIcon from '@mui/icons-material/BarChart';
import TranslateIcon from '@mui/icons-material/Translate';
import InsightsIcon from '@mui/icons-material/Insights';
import ModelTrainingIcon from '@mui/icons-material/ModelTraining';
import MenuBookIcon from '@mui/icons-material/MenuBook';
import GridViewIcon from '@mui/icons-material/GridView';
import { CATEGORIES, ExtensionCategory } from '../extension-registry-types';

export const DefaultCategoryIcon: FunctionComponent<SvgIconProps> = GridViewIcon;

// The category list itself lives in extension-registry-types; this record is
// compile-checked to stay exhaustive when categories are added or removed.
export const CATEGORY_ICONS: Record<ExtensionCategory, FunctionComponent<SvgIconProps>> = {
AI: AutoAwesomeIcon,
'Programming Languages': DataObjectIcon,
Snippets: ContentCopyIcon,
Linters: SpellcheckIcon,
Themes: PaletteIcon,
Debuggers: BugReportIcon,
Formatters: FormatAlignLeftIcon,
Keymaps: KeyboardIcon,
'SCM Providers': AccountTreeIcon,
Other: GridViewIcon,
'Extension Packs': ExtensionIcon,
'Language Packs': TranslateIcon,
'Data Science': InsightsIcon,
'Machine Learning': ModelTrainingIcon,
Visualization: BarChartIcon,
Notebooks: MenuBookIcon
};

const SORTED_CATEGORIES: ExtensionCategory[] = [...CATEGORIES].sort((a, b) => {
if (a === 'Other') return 1;
if (b === 'Other') return -1;
return a.localeCompare(b);
});

export function useCategories(): ExtensionCategory[] {
return SORTED_CATEGORIES;
}
61 changes: 61 additions & 0 deletions webui/src/components/category-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/********************************************************************************
* 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
********************************************************************************/

import { FunctionComponent } from 'react';
import { Box, ButtonBase, SvgIconProps, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import { cardHoverLift, cardSurface, focusOutline } from './page-primitives';

const Root = styled(ButtonBase)(({ theme }) => ({
...cardSurface(theme),
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
justifyContent: 'flex-start',
textAlign: 'left',
overflow: 'hidden',
padding: '1.125rem',
color: theme.palette.text.primary,
width: '100%',
transition: 'border-color 0.15s, box-shadow 0.15s, transform 0.15s',
...cardHoverLift(theme),
...focusOutline(theme),
'& .MuiTouchRipple-root': { color: theme.palette.secondary.main }
}));

export interface CategoryCardProps {
label: string;
icon: FunctionComponent<SvgIconProps>;
onClick: () => void;
}

export const CategoryCard: FunctionComponent<CategoryCardProps> = ({ label, icon: Icon, onClick }) => (
<Root onClick={onClick}>
<Box
sx={{
width: '2.375rem',
height: '2.375rem',
borderRadius: '10px',
bgcolor: 'accentSoft',
color: 'secondary.main',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mb: '0.875rem',
flexShrink: 0
}}>
<Icon sx={{ fontSize: '1.25rem' }} />
</Box>
<Typography sx={{ fontSize: '0.9375rem', fontWeight: 600, mb: '0.1875rem' }}>{label}</Typography>
</Root>
);
64 changes: 64 additions & 0 deletions webui/src/components/category-list-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/********************************************************************************
* 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
********************************************************************************/

import { FunctionComponent } from 'react';
import { ButtonBase, SvgIconProps } from '@mui/material';
import { styled } from '@mui/material/styles';
import { focusOutline } from './page-primitives';

const Root = styled(ButtonBase, {
shouldForwardProp: prop => prop !== 'isSelected'
})<{ isSelected: boolean }>(({ theme, isSelected }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
gap: '0.5rem',
width: '100%',
textAlign: 'left',
padding: '0.4375rem 0.625rem',
borderRadius: theme.shape.borderRadius,
overflow: 'hidden',
fontSize: '0.8125rem',
fontWeight: isSelected ? 600 : 400,
color: isSelected ? theme.palette.secondary.light : theme.palette.text.secondary,
backgroundColor: isSelected ? theme.palette.accentSoft : 'transparent',
marginBottom: '1px',
fontFamily: 'inherit',
transition: 'background 0.14s, color 0.14s',
'&:hover': isSelected
? {}
: {
backgroundColor: theme.palette.surface3,
color: theme.palette.text.primary
},
...focusOutline(theme)
}));

export interface CategoryListItemProps {
label: string;
icon: FunctionComponent<SvgIconProps>;
isSelected: boolean;
onClick: () => void;
}

export const CategoryListItem: FunctionComponent<CategoryListItemProps> = ({
label,
icon: Icon,
isSelected,
onClick
}) => (
<Root isSelected={isSelected} aria-current={isSelected ? 'true' : undefined} onClick={onClick}>
<Icon sx={{ fontSize: '0.9375rem', opacity: 0.75, flexShrink: 0 }} />
{label}
</Root>
);
64 changes: 64 additions & 0 deletions webui/src/components/category-pill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/********************************************************************************
* 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
********************************************************************************/

import { FunctionComponent, useEffect, useRef } from 'react';
import { ButtonBase, SvgIconProps } from '@mui/material';
import { styled } from '@mui/material/styles';
import { accentHover, focusOutline } from './page-primitives';

const Root = styled(ButtonBase, {
shouldForwardProp: prop => prop !== 'isSelected'
})<{ isSelected?: boolean }>(({ theme, isSelected }) => ({
display: 'inline-flex',
alignItems: 'center',
gap: '0.4375rem',
flexShrink: 0,
overflow: 'hidden',
backgroundColor: isSelected ? theme.palette.accentSoft : theme.palette.surface2,
border: `1px solid ${isSelected ? theme.palette.secondary.main : theme.palette.divider}`,
color: isSelected ? theme.palette.secondary.light : theme.palette.text.secondary,
fontSize: '0.8125rem',
fontWeight: isSelected ? 600 : 500,
padding: '0.4375rem 0.8125rem',
borderRadius: '999px',
whiteSpace: 'nowrap',
fontFamily: 'inherit',
transition: 'border-color 0.14s, color 0.14s',
...(isSelected ? {} : accentHover(theme)),
...focusOutline(theme)
}));

export interface CategoryPillProps {
label: string;
icon: FunctionComponent<SvgIconProps>;
isSelected?: boolean;
onClick: () => void;
}

export const CategoryPill: FunctionComponent<CategoryPillProps> = ({ label, icon: Icon, isSelected, onClick }) => {
const ref = useRef<HTMLButtonElement>(null);

// Keep the selected pill visible when the row overflows (deep links, home tiles).
useEffect(() => {
if (isSelected) {
ref.current?.scrollIntoView({ block: 'nearest', inline: 'center', behavior: 'smooth' });
}
}, [isSelected]);

return (
<Root ref={ref} isSelected={isSelected} aria-pressed={!!isSelected} onClick={onClick}>
<Icon sx={{ fontSize: '1rem', flexShrink: 0, color: 'secondary.main' }} />
{label}
</Root>
);
};
Loading