From ba6751ac45d379f6359d75ef2228cff6ba96c122 Mon Sep 17 00:00:00 2001 From: Eric Fennis Date: Fri, 18 Sep 2026 14:22:47 +0200 Subject: [PATCH] feat(site): Better icon detail page and add unreleased flag (#4877) * setup icon popularity call * Adjust sort button * Adjust sorting * Adds aliases * Format code * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Add check for env variable * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Improve styling on mobile * Better icon detail page * Update detail page * feat: make edit button more visible * Fix types * Add unreleased version tag * Adjust icon detail * Added edit button * format code * Replace pencil sparkles * Revert changes * Fix types * Add docs checks * Fix types * Fix awaiting release * revert icons changes * Format code * Fix type issues * Update CarbonAdOverlay to not display Disable CarbonAdOverlay by setting drawerOpen to false. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix types * Fix condition for showing awaiting release icons Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Format code --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jakob Guddas --- .github/workflows/docs.yml | 55 +++++ docs/.vitepress/api/figma/data.ts | 3 +- docs/.vitepress/api/icon-nodes/index.get.ts | 3 +- docs/.vitepress/config.ts | 2 +- docs/.vitepress/lib/SvgPreview/types.ts | 5 +- docs/.vitepress/lib/SvgPreview/utils.ts | 5 +- docs/.vitepress/lib/categories.ts | 6 +- docs/.vitepress/lib/getFallbackZip.tsx | 3 +- docs/.vitepress/lib/icons.ts | 2 + docs/.vitepress/markdown/example.ts | 3 +- docs/.vitepress/markdown/sandpack.ts | 4 +- docs/.vitepress/markdown/snackPlayer.ts | 2 +- docs/.vitepress/markdown/types.ts | 7 + .../theme/components/SurveyAskOverlay.vue | 7 +- .../base/{IconButton.vue => Button.vue} | 46 ++++- .../theme/components/base/Input.vue | 9 +- .../theme/components/base/InputSearch.vue | 1 + .../theme/components/base/LayoutTop.vue | 3 +- .../theme/components/base/PageTabs.vue | 74 +++++++ .../theme/components/base/RangeSlider.vue | 2 +- .../theme/components/base/ResetButton.vue | 7 +- .../theme/components/base/Select.vue | 2 + .../theme/components/editors/Sandpack.vue | 2 +- .../components/guide/FrameworkSelect.vue | 11 +- .../home/HomeHeroIconsAnimation.vue | 9 +- .../components/home/HomeHeroIconsCard.data.ts | 7 +- .../home/HomeHeroInfoBefore.data.ts | 7 +- .../components/home/HomeHeroInfoBefore.vue | 3 +- .../components/home/HomeIconCustomizer.vue | 4 +- .../home/HomePackagesSection.data.ts | 7 +- .../components/home/HomeSectionTitle.vue | 2 +- .../components/home/RocketDuotoneIcon.ts | 6 +- .../components/icons/CarbonAdOverlay.vue | 7 +- .../components/icons/CategoryList.data.ts | 11 +- .../theme/components/icons/CategoryList.vue | 2 +- .../components/icons/EditInStudioButton.vue | 29 +++ .../components/icons/IconContributors.vue | 9 +- .../theme/components/icons/IconDetail.vue | 24 +-- .../theme/components/icons/IconDetailName.vue | 7 +- .../components/icons/IconDetailOverlay.vue | 29 ++- .../theme/components/icons/IconInfo.vue | 143 +++++-------- .../theme/components/icons/IconPreview.vue | 133 ++++++------ .../theme/components/icons/IconShowcase.vue | 12 +- .../theme/components/icons/IconTags.vue | 110 ++++++++++ .../icons/IconsCategoryOverview.vue | 2 +- .../theme/components/icons/IconsOverview.vue | 6 +- .../theme/components/icons/NoResults.vue | 6 +- .../theme/components/icons/RelatedIcons.vue | 1 - .../icons/SidebarIconCustomizer.vue | 4 +- .../theme/components/icons/StickyBar.vue | 47 +++++ .../theme/components/overrides/Hero.vue | 2 +- .../theme/components/overrides/HeroMatrix.vue | 2 +- .../theme/components/overrides/VPFooter.vue | 2 +- .../components/packages/PackageList.data.ts | 7 +- .../theme/composables/useActiveAnchor.ts | 2 +- .../.vitepress/theme/composables/useSearch.ts | 4 +- docs/.vitepress/theme/style.css | 4 + docs/.vitepress/theme/types.ts | 13 +- docs/.vitepress/theme/utils/chunkArray.ts | 4 +- docs/.vitepress/theme/utils/getStudioLink.ts | 25 +++ docs/.vitepress/types.d.ts | 5 + docs/icons/[name].md | 194 +++++++++--------- docs/icons/icons.data.ts | 33 ++- docs/icons/lab/[name].paths.ts | 3 +- docs/package.json | 9 +- docs/scripts/writeIconDetails.mts | 5 +- docs/scripts/writeIconNodes.mts | 13 +- docs/scripts/writeLabIconNodes.mts | 13 +- docs/scripts/writeReleaseMetadata.mts | 31 ++- docs/tsconfig.json | 1 + pnpm-lock.yaml | 41 ++++ 71 files changed, 917 insertions(+), 387 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/.vitepress/markdown/types.ts rename docs/.vitepress/theme/components/base/{IconButton.vue => Button.vue} (56%) create mode 100644 docs/.vitepress/theme/components/base/PageTabs.vue create mode 100644 docs/.vitepress/theme/components/icons/EditInStudioButton.vue create mode 100644 docs/.vitepress/theme/components/icons/IconTags.vue create mode 100644 docs/.vitepress/theme/utils/getStudioLink.ts diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..551624b82 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,55 @@ +name: Docs Checks + +on: + pull_request: + paths: + - docs/** + - .github/workflows/docs.yml + - package.json + - pnpm-workspace.yaml + - pnpm-lock.yaml + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Release metadata is generated by diffing every release tag + fetch-depth: 0 + - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + cache: 'pnpm' + node-version-file: 'package.json' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build docs + run: pnpm --filter @lucide/docs build + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # Release metadata is generated by diffing every release tag + fetch-depth: 0 + - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + cache: 'pnpm' + node-version-file: 'package.json' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate docs data + run: pnpm --filter @lucide/docs run "/^prebuild:.*/" + + - name: Typecheck + run: pnpm --filter @lucide/docs typecheck diff --git a/docs/.vitepress/api/figma/data.ts b/docs/.vitepress/api/figma/data.ts index cde37f140..fad04b166 100644 --- a/docs/.vitepress/api/figma/data.ts +++ b/docs/.vitepress/api/figma/data.ts @@ -1,13 +1,12 @@ import { eventHandler, setResponseHeader } from 'h3'; import iconNodes from '../../data/iconNodes/index.ts'; -import { IconNodeWithKeys } from '../../theme/types'; import iconMetaData from '../../data/iconMetaData'; import releaseMeta from '../../data/releaseMetaData.json'; import categories from '../../data/categoriesData.json'; const dataResponse = { icons: Object.entries(iconNodes).reduce((acc, [name, iconNode]) => { - const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => { + const newIconNode = iconNode.map(([name, { key, ...attrs }]) => { return [name, attrs]; }); diff --git a/docs/.vitepress/api/icon-nodes/index.get.ts b/docs/.vitepress/api/icon-nodes/index.get.ts index 8ab634c78..dcdfe98fd 100644 --- a/docs/.vitepress/api/icon-nodes/index.get.ts +++ b/docs/.vitepress/api/icon-nodes/index.get.ts @@ -1,6 +1,5 @@ import { eventHandler, getQuery, setResponseHeader } from 'h3'; import iconNodes from '../../data/iconNodes'; -import { IconNodeWithKeys } from '../../theme/types'; export default eventHandler((event) => { const query = getQuery(event); @@ -19,7 +18,7 @@ export default eventHandler((event) => { return [name, iconNode]; } - const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => { + const newIconNode = iconNode.map(([name, { key, ...attrs }]) => { return [name, attrs]; }); diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 175fa5066..21024d431 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -97,7 +97,7 @@ export default defineConfig({ { text: 'Resources', items: [ - ...resourcesSidebar[0].items, + ...resourcesSidebar[0].items.map(({ text, link }) => ({ text, link })), { text: 'How to use icons', link: '/how-to/' }, { text: 'Contributing icons', link: '/contribute/icons/' }, ], diff --git a/docs/.vitepress/lib/SvgPreview/types.ts b/docs/.vitepress/lib/SvgPreview/types.ts index ecfe201db..f58296b2b 100644 --- a/docs/.vitepress/lib/SvgPreview/types.ts +++ b/docs/.vitepress/lib/SvgPreview/types.ts @@ -1,4 +1,3 @@ -import { SVGProps } from 'react'; import { getCommands } from './utils'; export type Point = { x: number; y: number }; @@ -15,8 +14,8 @@ export type Path = { }; export type PathProps< - RequiredProps extends keyof SVGProps, - NeverProps extends keyof SVGProps, + RequiredProps extends keyof React.SVGProps, + NeverProps extends keyof React.SVGProps, > = Required, RequiredProps>> & Omit< React.SVGProps, diff --git a/docs/.vitepress/lib/SvgPreview/utils.ts b/docs/.vitepress/lib/SvgPreview/utils.ts index 04ba47f5c..993cbfe6f 100644 --- a/docs/.vitepress/lib/SvgPreview/utils.ts +++ b/docs/.vitepress/lib/SvgPreview/utils.ts @@ -53,10 +53,7 @@ export const getCommands = (src: string) => getNodes(src) .map(convertToPathNode) .flatMap(({ d, name }, idx) => - new SVGPathData(d) - .toAbs() - // @ts-expect-error `commands` is typed as a union of command types svgson does not narrow - .commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })), + new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })), ); const getPaths = (src: string) => { diff --git a/docs/.vitepress/lib/categories.ts b/docs/.vitepress/lib/categories.ts index dfc0268b6..71d551b5a 100644 --- a/docs/.vitepress/lib/categories.ts +++ b/docs/.vitepress/lib/categories.ts @@ -4,7 +4,7 @@ import { Category, IconEntity } from '../theme/types'; const directory = path.join(process.cwd(), '../categories'); -export function getAllCategoryFiles(): Category[] { +export function getAllCategoryFiles(): Omit[] { const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json'); return fileNames.map((fileName) => { @@ -21,9 +21,9 @@ export function getAllCategoryFiles(): Category[] { } export function mapCategoryIconCount( - categories: Category[], + categories: Omit[], icons: { categories: IconEntity['categories'] }[], -) { +): Category[] { return categories.map((category) => ({ ...category, iconCount: icons.reduce( diff --git a/docs/.vitepress/lib/getFallbackZip.tsx b/docs/.vitepress/lib/getFallbackZip.tsx index 6f776c47b..1c176430b 100644 --- a/docs/.vitepress/lib/getFallbackZip.tsx +++ b/docs/.vitepress/lib/getFallbackZip.tsx @@ -1,4 +1,5 @@ -import { createLucideIcon } from 'lucide-react/src/lucide-react'; +import React from 'react'; +import createLucideIcon from 'lucide-react/src/createLucideIcon'; import { type LucideProps, type IconNode } from 'lucide-react/src/types'; import { IconEntity } from '../theme/types'; import { renderToStaticMarkup } from 'react-dom/server'; diff --git a/docs/.vitepress/lib/icons.ts b/docs/.vitepress/lib/icons.ts index ece3f9bfd..05a78bbb0 100644 --- a/docs/.vitepress/lib/icons.ts +++ b/docs/.vitepress/lib/icons.ts @@ -20,6 +20,8 @@ export async function getData(name: string) { const iconNode = iconNodes[name]; const releaseData = releaseMeta?.[name] ?? { + fromFork: false, + awaitingRelease: false, createdRelease: { version: '0.0.0', date: DATE_OF_FORK, diff --git a/docs/.vitepress/markdown/example.ts b/docs/.vitepress/markdown/example.ts index 8f1527a25..f38b949c1 100644 --- a/docs/.vitepress/markdown/example.ts +++ b/docs/.vitepress/markdown/example.ts @@ -1,7 +1,6 @@ import { readFileSync } from 'node:fs'; import { dirname, isAbsolute, resolve } from 'node:path'; -import type MarkdownIt from 'markdown-it'; -import type { RenderRule } from 'markdown-it/lib/renderer.mjs'; +import type { MarkdownIt, RenderRule } from './types'; import container from 'markdown-it-container'; type ContainerArgs = [typeof container, string, { render: RenderRule }]; diff --git a/docs/.vitepress/markdown/sandpack.ts b/docs/.vitepress/markdown/sandpack.ts index 94a137306..983a2ef90 100644 --- a/docs/.vitepress/markdown/sandpack.ts +++ b/docs/.vitepress/markdown/sandpack.ts @@ -1,6 +1,4 @@ -import type MarkdownIt from 'markdown-it'; -import type { RenderRule } from 'markdown-it/lib/renderer.mjs'; -import type Token from 'markdown-it/lib/token.mjs'; +import type { MarkdownIt, RenderRule, Token } from './types'; import container from 'markdown-it-container'; import sandpackTheme from '../theme/sandpackTheme.json'; diff --git a/docs/.vitepress/markdown/snackPlayer.ts b/docs/.vitepress/markdown/snackPlayer.ts index cb848d4ef..3005ed208 100644 --- a/docs/.vitepress/markdown/snackPlayer.ts +++ b/docs/.vitepress/markdown/snackPlayer.ts @@ -16,7 +16,7 @@ * /> */ -import type MarkdownIt from 'markdown-it'; +import type { MarkdownIt } from './types'; type SnackParams = Record; diff --git a/docs/.vitepress/markdown/types.ts b/docs/.vitepress/markdown/types.ts new file mode 100644 index 000000000..a71f15fa2 --- /dev/null +++ b/docs/.vitepress/markdown/types.ts @@ -0,0 +1,7 @@ +import type { MarkdownRenderer } from 'vitepress'; + +// VitePress 1.x renders with markdown-it 14, so derive the plugin types from its renderer +// instead of the markdown-it 15 package installed in docs. +export type MarkdownIt = MarkdownRenderer; +export type RenderRule = NonNullable; +export type Token = Parameters[0][number]; diff --git a/docs/.vitepress/theme/components/SurveyAskOverlay.vue b/docs/.vitepress/theme/components/SurveyAskOverlay.vue index a8e42e81f..fd693c90d 100644 --- a/docs/.vitepress/theme/components/SurveyAskOverlay.vue +++ b/docs/.vitepress/theme/components/SurveyAskOverlay.vue @@ -1,6 +1,6 @@ + diff --git a/docs/.vitepress/theme/components/base/RangeSlider.vue b/docs/.vitepress/theme/components/base/RangeSlider.vue index bfef6d091..871452650 100644 --- a/docs/.vitepress/theme/components/base/RangeSlider.vue +++ b/docs/.vitepress/theme/components/base/RangeSlider.vue @@ -34,7 +34,7 @@ const percentage = computed(() => `${((Number(props.modelValue) - props. type="range" v-bind="$attrs" v-bind:value="modelValue" - v-on:input="$emit('update:modelValue', Number($event.target.value))" + v-on:input="$emit('update:modelValue', Number(($event.target as HTMLInputElement).value))" :min="min" :max="max" :step="step" diff --git a/docs/.vitepress/theme/components/base/ResetButton.vue b/docs/.vitepress/theme/components/base/ResetButton.vue index ff205f796..17c666fb2 100644 --- a/docs/.vitepress/theme/components/base/ResetButton.vue +++ b/docs/.vitepress/theme/components/base/ResetButton.vue @@ -1,16 +1,17 @@ diff --git a/docs/.vitepress/theme/components/icons/IconContributors.vue b/docs/.vitepress/theme/components/icons/IconContributors.vue index 5cbfb364d..35aabf5da 100644 --- a/docs/.vitepress/theme/components/icons/IconContributors.vue +++ b/docs/.vitepress/theme/components/icons/IconContributors.vue @@ -30,10 +30,15 @@ const props = defineProps<{ .contributors { display: flex; flex-direction: row; - align-items: center; + align-items: start; /* justify-content: flex-end; */ - gap: 16px; + gap: 2px; } + +.contributors .label { + margin-top: 12px; +} + .avatar-group { display: flex; } diff --git a/docs/.vitepress/theme/components/icons/IconDetail.vue b/docs/.vitepress/theme/components/icons/IconDetail.vue index b99f5921a..c42e665c4 100644 --- a/docs/.vitepress/theme/components/icons/IconDetail.vue +++ b/docs/.vitepress/theme/components/icons/IconDetail.vue @@ -16,19 +16,17 @@ const tags = computed(() => { diff --git a/docs/.vitepress/theme/components/icons/IconInfo.vue b/docs/.vitepress/theme/components/icons/IconInfo.vue index a5b66240b..1aa8cb76d 100644 --- a/docs/.vitepress/theme/components/icons/IconInfo.vue +++ b/docs/.vitepress/theme/components/icons/IconInfo.vue @@ -10,30 +10,36 @@ import { computed } from 'vue'; import createLucideIcon from '@lucide/vue/src/createLucideIcon'; import { diamond } from '../../../data/iconNodes'; import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts'; +import type { IconDeprecationReason } from '../../../../../tools/build-icons/types.ts'; +import IconTags from "./IconTags.vue" +import getStudioLink from '../../utils/getStudioLink'; const props = defineProps<{ icon: IconEntity; popoverPosition?: 'top' | 'bottom'; + showEditButton?: boolean; }>(); const { go } = useRouter(); const { page } = useData(); -const tags = computed(() => { - if (!props.icon || !props?.icon?.tags) return []; - return props.icon.tags.join(' • '); -}); - const DiamondIcon = createLucideIcon('Diamond', diamond); const deprecatedTitle = computed(() => { if (!props.icon.deprecationReason) return ''; - return deprecationReasonTemplate(props.icon.deprecationReason, { + return deprecationReasonTemplate(props.icon.deprecationReason as IconDeprecationReason, { componentName: props.icon.name, iconName: props.icon.name, - toBeRemovedInVersion: props.icon.toBeRemovedInVersion, }); }); + +const iconPath = computed(() => { + if (props.icon.externalLibrary) { + return `icons/${props.icon.externalLibrary}/${props.icon.name}`; + } + + return `icons/${props.icon.name}`; +}); diff --git a/docs/.vitepress/theme/components/icons/IconShowcase.vue b/docs/.vitepress/theme/components/icons/IconShowcase.vue index 4d5250fc5..b41d2e9c5 100644 --- a/docs/.vitepress/theme/components/icons/IconShowcase.vue +++ b/docs/.vitepress/theme/components/icons/IconShowcase.vue @@ -70,7 +70,6 @@ const prettyName = props.name class="showcase" v-if="iconComponent" > -

See this icon in action

@@ -236,18 +235,17 @@ const prettyName = props.name .showcase-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap: 48px; - margin-inline-start: 24px; - margin-block-start: 48px; + gap: 32px; + background: var(--vp-code-block-bg); + padding: 32px; + border-radius: 8px; } .showcase-item { padding: 24px; border-radius: 8px; background-color: var(--vp-c-bg); - box-shadow: - var(--vp-shadow-4), - -24px -24px 0 var(--vp-c-bg-soft); + box-shadow: var(--vp-shadow-2); display: flex; align-items: center; justify-content: center; diff --git a/docs/.vitepress/theme/components/icons/IconTags.vue b/docs/.vitepress/theme/components/icons/IconTags.vue new file mode 100644 index 000000000..5e4204a84 --- /dev/null +++ b/docs/.vitepress/theme/components/icons/IconTags.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/docs/.vitepress/theme/components/icons/IconsCategoryOverview.vue b/docs/.vitepress/theme/components/icons/IconsCategoryOverview.vue index a5066017d..e3aab9189 100644 --- a/docs/.vitepress/theme/components/icons/IconsCategoryOverview.vue +++ b/docs/.vitepress/theme/components/icons/IconsCategoryOverview.vue @@ -163,7 +163,7 @@ const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay function handleCloseDrawer() { setActiveIconName(''); - const url = new URL(window.location); + const url = new URL(window.location.href); url.pathname = '/icons/categories'; if (searchQueryDebounced.value) { diff --git a/docs/.vitepress/theme/components/icons/IconsOverview.vue b/docs/.vitepress/theme/components/icons/IconsOverview.vue index d9bc7dd82..a5dde2d5b 100644 --- a/docs/.vitepress/theme/components/icons/IconsOverview.vue +++ b/docs/.vitepress/theme/components/icons/IconsOverview.vue @@ -35,7 +35,6 @@ const SORTING = [ }, ] - const initialGridItems = computed(() => { if (containerWidth.value === 0) return 120; @@ -72,6 +71,8 @@ const sortedIcons = computed(() => { return [...props.icons].sort((a, b) => (b.popularity || 0) - (a.popularity || 0)); case 'release-date': return [...props.icons].sort((a, b) => { + if (a.awaitingRelease !== b.awaitingRelease) return a.awaitingRelease ? -1 : 1; + const aDate = a.createdRelease?.date ? new Date(a.createdRelease.date).getTime() : 0; const bDate = b.createdRelease?.date ? new Date(b.createdRelease.date).getTime() : 0; return bDate - aDate; @@ -172,7 +173,7 @@ watch(searchQueryDebounced, () => { function handleCloseDrawer() { setActiveIconName(''); - const url = new URL(window.location); + const url = new URL(window.location.href); url.pathname = '/icons/'; if (searchQueryDebounced.value) { @@ -207,6 +208,7 @@ function handleCloseDrawer() {