mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-10 13:18:25 +00:00
Compare commits
15
Commits
1.71.0
...
algolia-search
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bc57dbee8 | ||
|
|
cc69c9f0e5 | ||
|
|
6c17ba30f8 | ||
|
|
b78eb72f35 | ||
|
|
9c6e275c84 | ||
|
|
c48136c183 | ||
|
|
b2254fa52f | ||
|
|
8e324e5c27 | ||
|
|
9d13a32b8a | ||
|
|
e0efef7cd9 | ||
|
|
dce1e76788 | ||
|
|
fb2377de17 | ||
|
|
88e585400b | ||
|
|
df8f7d85fc | ||
|
|
420e22b5f1 |
@@ -0,0 +1,13 @@
|
||||
# Copy to `docs/.env`.
|
||||
|
||||
# Icon popularity from Plausible. Defaults to 0 for every icon when unset.
|
||||
LUCIDE_ANALYTICS_TOKEN=
|
||||
|
||||
# Set to `true` to also show icons that are not released yet.
|
||||
VITE_SHOW_AWAITING_RELEASE=
|
||||
|
||||
# Client-side Algolia icon search (public search-only key). Required for the icon search.
|
||||
# The index is filled by the Algolia crawler.
|
||||
VITE_ALGOLIA_APP_ID=R7KKMCPXOB
|
||||
VITE_ALGOLIA_SEARCH_API_KEY=dd3101054267d35fb28f49c42715f1aa
|
||||
VITE_ALGOLIA_INDEX_NAME=icons
|
||||
@@ -10,6 +10,7 @@ import { resourcesSidebar } from './sidebar/resources';
|
||||
import llmstxt from 'vitepress-plugin-llms';
|
||||
import { transformPageData } from './transformPageData';
|
||||
import getHeadConfig from './getHeadConfig';
|
||||
import { loadEnv } from 'vitepress';
|
||||
|
||||
const defaultSandpackCSS = await readFile(
|
||||
fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)),
|
||||
@@ -24,6 +25,9 @@ const title = 'Lucide';
|
||||
const socialTitle = 'Lucide Icons';
|
||||
const description = 'Beautiful & consistent icon toolkit made by the community.';
|
||||
|
||||
|
||||
const env = loadEnv(process.env.NODE_ENV, process.cwd());
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title,
|
||||
@@ -134,8 +138,8 @@ export default defineConfig({
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: 'R7KKMCPXOB',
|
||||
apiKey: 'dd3101054267d35fb28f49c42715f1aa',
|
||||
appId: env.VITE_ALGOLIA_APP_ID,
|
||||
apiKey: env.VITE_ALGOLIA_SEARCH_API_KEY,
|
||||
indexName: 'lucide',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -15,6 +15,11 @@ export default async function getStructuredData(iconName: string, pageData: Page
|
||||
|
||||
const awaitingRelease = Boolean(params.awaitingRelease);
|
||||
|
||||
// Aliases come through either as plain names or as `{ name, deprecated, ... }` objects.
|
||||
const aliases: string[] = (params.aliases ?? [])
|
||||
.map((alias: string | { name?: string }) => (typeof alias === 'string' ? alias : alias?.name))
|
||||
.filter((alias?: string): alias is string => Boolean(alias));
|
||||
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebPage',
|
||||
@@ -39,6 +44,7 @@ export default async function getStructuredData(iconName: string, pageData: Page
|
||||
'@type': 'ImageObject',
|
||||
'@id': `${url}#icon`,
|
||||
name: iconName,
|
||||
...(aliases.length ? { alternateName: aliases } : {}),
|
||||
// TODO: Add support for description extraction from icon metadata
|
||||
// description: 'An ...SVG icon from the Lucide icon set.',
|
||||
// `/api/icons/<name>` only renders core icons, a lab name would 404 there.
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, defineAsyncComponent, onMounted } from 'vue';
|
||||
import type { IconEntity, Category } from '../../types';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import useIconSearch, { entityId } from '../../composables/useIconSearch';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
import useSearchInput from '../../composables/useSearchInput';
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
import StickyBar from './StickyBar.vue';
|
||||
import IconsCategory, { CategoryRow } from './IconsCategory.vue';
|
||||
import useFetchTags from '../../composables/useFetchTags';
|
||||
import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import useScrollToCategory from '../../composables/useScrollToCategory';
|
||||
import { useCategoryView } from '../../composables/useCategoryView';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
import { prefetchAlgoliaClient } from '../../utils/algolia';
|
||||
|
||||
const ICON_SIZE = 56;
|
||||
const ICON_GRID_GAP = 8;
|
||||
@@ -28,7 +27,7 @@ const props = defineProps<{
|
||||
const activeIconName = ref(null);
|
||||
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||
const { selectedCategory } = useCategoryView();
|
||||
const isSearching = computed(() => !!searchQuery.value);
|
||||
const isSearching = computed(() => !!searchQueryDebounced.value);
|
||||
|
||||
watch(searchQueryDebounced, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
@@ -44,13 +43,6 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categoriesMap,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
|
||||
@@ -58,27 +50,14 @@ const columnSize = computed(() => {
|
||||
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
|
||||
});
|
||||
|
||||
const mappedIcons = computed(() => {
|
||||
if (tags.value == null) {
|
||||
return props.icons;
|
||||
}
|
||||
return props.icons.map((icon) => {
|
||||
const iconTags = tags.value[icon.name];
|
||||
const iconCategories = categoriesMap.value?.[icon.name] ?? [];
|
||||
const icons = computed(() => props.icons);
|
||||
|
||||
return {
|
||||
...icon,
|
||||
tags: iconTags,
|
||||
categories: iconCategories,
|
||||
};
|
||||
});
|
||||
});
|
||||
const { results: searchResults, isPending: isSearchPending } = useIconSearch(
|
||||
searchQueryDebounced,
|
||||
icons,
|
||||
);
|
||||
|
||||
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'aliases', weight: 3 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
]);
|
||||
const resultIds = computed(() => new Set(searchResults.value.map(entityId)));
|
||||
|
||||
const categories = computed(() => {
|
||||
if (!props.categories?.length || !props.icons?.length) return [];
|
||||
@@ -92,10 +71,8 @@ const categories = computed(() => {
|
||||
return iconCategories?.includes(name);
|
||||
});
|
||||
|
||||
const searchedCategoryIcons = isSearching
|
||||
? categoryIcons.filter((icon) =>
|
||||
searchResults.value.some((item) => item?.name === icon?.name),
|
||||
)
|
||||
const searchedCategoryIcons = isSearching.value
|
||||
? categoryIcons.filter((icon) => resultIds.value.has(entityId(icon)))
|
||||
: categoryIcons;
|
||||
|
||||
return {
|
||||
@@ -121,8 +98,8 @@ const categoriesList = computed(() => {
|
||||
}, []);
|
||||
});
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && !isFetchingTags && !isFetchingCategories,
|
||||
const isSearchSettling = computed(
|
||||
() => searchQuery.value !== searchQueryDebounced.value || isSearchPending.value,
|
||||
);
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
|
||||
@@ -142,21 +119,6 @@ onMounted(() => {
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
});
|
||||
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categoriesMap.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
|
||||
@@ -191,11 +153,11 @@ function handleCloseDrawer() {
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
@focus="prefetchAlgoliaClient"
|
||||
/>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchSettling"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
|
||||
import type { IconEntity } from '../../types';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import { useRoute } from 'vitepress';
|
||||
import { DefaultTheme, useData, useRoute } from 'vitepress';
|
||||
import IconGrid from './IconGrid.vue';
|
||||
import Select from '../base/Select.vue';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import useIconSearch from '../../composables/useIconSearch';
|
||||
import useSearchInput from '../../composables/useSearchInput';
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
import StickyBar from './StickyBar.vue';
|
||||
import useFetchTags from '../../composables/useFetchTags';
|
||||
import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
import { sortIcons, type SortKey } from '../../utils/sortIcons';
|
||||
import { prefetchAlgoliaClient } from '../../utils/algolia';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { listSortDescending } from '~/.vitepress/data/iconNodes';
|
||||
|
||||
const ICON_SIZE = 56;
|
||||
const ICON_GRID_GAP = 8;
|
||||
const SORTING = [
|
||||
const SORTING: { name: string; value: SortKey }[] = [
|
||||
{
|
||||
name: 'Popularity',
|
||||
value: 'popularity',
|
||||
@@ -33,7 +33,7 @@ const SORTING = [
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
const initialGridItems = computed(() => {
|
||||
if (containerWidth.value === 0) return 120;
|
||||
@@ -48,15 +48,11 @@ const props = defineProps<{
|
||||
icons: IconEntity[];
|
||||
}>();
|
||||
|
||||
const activeIconName = ref(null);
|
||||
const selectedSort = ref(SORTING[0])
|
||||
const { theme } = useData<DefaultTheme.Config>()
|
||||
const algoliaSearchOptions = theme.value.search?.options as DefaultTheme.AlgoliaSearchOptions;
|
||||
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categories,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
const activeIconName = ref(null);
|
||||
const selectedSort = ref(SORTING[0]);
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
@@ -65,45 +61,7 @@ const columnSize = computed(() => {
|
||||
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
|
||||
});
|
||||
|
||||
const sortedIcons = computed(() => {
|
||||
switch (selectedSort.value.value) {
|
||||
case 'popularity':
|
||||
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;
|
||||
});
|
||||
case 'name':
|
||||
return [...props.icons].sort((a, b) => a.name.localeCompare(b.name));
|
||||
default:
|
||||
return props.icons;
|
||||
}
|
||||
});
|
||||
|
||||
const mappedIcons = computed(() => {
|
||||
if (tags.value == null) {
|
||||
return sortedIcons.value;
|
||||
}
|
||||
|
||||
if (categories.value == null) {
|
||||
return sortedIcons.value;
|
||||
}
|
||||
|
||||
return sortedIcons.value.map((icon) => {
|
||||
const iconTags = tags.value[icon.name];
|
||||
const iconCategories = categories.value[icon.name] ?? [];
|
||||
|
||||
return {
|
||||
...icon,
|
||||
tags: iconTags,
|
||||
categories: iconCategories,
|
||||
};
|
||||
});
|
||||
});
|
||||
const sortedIcons = computed(() => sortIcons(props.icons, selectedSort.value.value));
|
||||
|
||||
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||
|
||||
@@ -111,20 +69,29 @@ const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
searchInput.value?.focus();
|
||||
});
|
||||
|
||||
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'aliases', weight: 8 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
{ name: 'categories', weight: 1 },
|
||||
]);
|
||||
const { results: searchResults, isPending: isSearchPending } = useIconSearch(
|
||||
searchQueryDebounced,
|
||||
sortedIcons,
|
||||
algoliaSearchOptions,
|
||||
);
|
||||
|
||||
// While searching, "Popularity" keeps Algolia's relevance order.
|
||||
// The other sort options re-sort the matching icons locally.
|
||||
const displayedIcons = computed(() => {
|
||||
if (searchQueryDebounced.value && selectedSort.value.value !== 'popularity') {
|
||||
return sortIcons(searchResults.value, selectedSort.value.value);
|
||||
}
|
||||
|
||||
return searchResults.value;
|
||||
});
|
||||
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && !isFetchingTags && !isFetchingCategories,
|
||||
const isSearchSettling = computed(
|
||||
() => searchQuery.value !== searchQueryDebounced.value || isSearchPending.value,
|
||||
);
|
||||
|
||||
const chunkedIcons = computed(() => {
|
||||
return chunkArray(searchResults.value, columnSize.value);
|
||||
return chunkArray(displayedIcons.value, columnSize.value);
|
||||
});
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedIcons, {
|
||||
@@ -147,21 +114,6 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categories.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
@@ -197,7 +149,7 @@ function handleCloseDrawer() {
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="loadSearchMetadata"
|
||||
@focus="() =>prefetchAlgoliaClient(algoliaSearchOptions)"
|
||||
/>
|
||||
|
||||
<Select
|
||||
@@ -216,7 +168,7 @@ function handleCloseDrawer() {
|
||||
</Select>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchSettling"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
@@ -224,7 +176,7 @@ function handleCloseDrawer() {
|
||||
<IconGrid
|
||||
v-else-if="list.length === 0"
|
||||
overlayMode
|
||||
:icons="searchResults.slice(0, initialGridItems)"
|
||||
:icons="displayedIcons.slice(0, initialGridItems)"
|
||||
:activeIcon="activeIconName"
|
||||
@setActiveIcon="setActiveIconName"
|
||||
/>
|
||||
|
||||
@@ -22,13 +22,15 @@ import FakeInput from '../base/FakeInput.vue'
|
||||
|
||||
const VPAlgoliaSearchBox = defineAsyncComponent(() => import('vitepress/dist/client/theme-default/components/VPAlgoliaSearchBox.vue'));
|
||||
|
||||
const { theme } = useData<DefaultTheme.Config>()
|
||||
const { theme, page } = useData<DefaultTheme.Config>()
|
||||
|
||||
const algolia = computed(
|
||||
() =>
|
||||
(theme.value.search?.options ?? theme.value.algolia) as DefaultTheme.AlgoliaSearchOptions,
|
||||
)
|
||||
|
||||
const isGuide = computed(() => page.value?.relativePath?.startsWith?.('guide'));
|
||||
|
||||
// whether this instance is the one rendering the (hidden) search box
|
||||
const isOwner = ref(false)
|
||||
const shortcutText = ref('')
|
||||
@@ -117,7 +119,7 @@ function isEditingContent(event: KeyboardEvent): boolean {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="VPNavBarSearch">
|
||||
<div class="VPNavBarSearch" v-if="isGuide">
|
||||
<FakeInput
|
||||
class="nav-search-input"
|
||||
:shortcut="shortcutText"
|
||||
@@ -136,27 +138,29 @@ function isEditingContent(event: KeyboardEvent): boolean {
|
||||
|
||||
<style>
|
||||
|
||||
.VPNavBarSearch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.VPNavBarSearch {
|
||||
flex-grow: 1;
|
||||
padding-left: 0px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPNavBarSearch {
|
||||
padding-left: 0px;
|
||||
padding-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarSearch {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.VPNavBarSearch .nav-search-input {
|
||||
padding: 8px 12px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
.VPNavBarSearch .docsearch-host {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { useFetch } from '@vueuse/core';
|
||||
|
||||
const useFetchCategories = () =>
|
||||
useFetch<Record<string, string[]>>(
|
||||
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/categories`,
|
||||
{
|
||||
immediate:
|
||||
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('search'),
|
||||
},
|
||||
).json();
|
||||
|
||||
export default useFetchCategories;
|
||||
@@ -1,12 +0,0 @@
|
||||
import { useFetch } from '@vueuse/core';
|
||||
|
||||
const useFetchTags = () =>
|
||||
useFetch<Record<string, string[]>>(
|
||||
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/tags`,
|
||||
{
|
||||
immediate:
|
||||
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('search'),
|
||||
},
|
||||
).json();
|
||||
|
||||
export default useFetchTags;
|
||||
@@ -0,0 +1,83 @@
|
||||
import { computed, ref, shallowRef, watch, type ComputedRef, type Ref } from 'vue';
|
||||
import { searchIconIds } from '../utils/algolia';
|
||||
import { DefaultTheme, useData } from 'vitepress';
|
||||
|
||||
export interface SearchableIcon {
|
||||
name: string;
|
||||
externalLibrary?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the `objectID` scheme of the Algolia `icons` index:
|
||||
* `name` for core icons, `lab/<name>` for lab icons.
|
||||
*/
|
||||
export const entityId = (icon: SearchableIcon) =>
|
||||
icon.externalLibrary ? `${icon.externalLibrary}/${icon.name}` : icon.name;
|
||||
|
||||
export interface UseIconSearchResult<T> {
|
||||
/** Matching icons in relevance order, or the full collection when the query is empty. */
|
||||
results: ComputedRef<T[]>;
|
||||
/** `true` while a search request is in flight. */
|
||||
isPending: Ref<boolean>;
|
||||
}
|
||||
|
||||
const useIconSearch = <T extends SearchableIcon>(
|
||||
query: Ref<string>,
|
||||
collection: Ref<T[]>,
|
||||
algoliaSearchOptions: DefaultTheme.AlgoliaSearchOptions,
|
||||
): UseIconSearchResult<T> => {
|
||||
const byId = computed(() => new Map(collection.value.map((icon) => [entityId(icon), icon])));
|
||||
|
||||
const hitIds = shallowRef<string[]>([]);
|
||||
const isPending = ref(false);
|
||||
let requestSeq = 0;
|
||||
|
||||
watch(
|
||||
query,
|
||||
async (searchQuery) => {
|
||||
const seq = ++requestSeq;
|
||||
|
||||
if (!searchQuery) {
|
||||
isPending.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isPending.value = true;
|
||||
|
||||
try {
|
||||
const ids = await searchIconIds(searchQuery, algoliaSearchOptions);
|
||||
|
||||
if (seq === requestSeq) {
|
||||
hitIds.value = ids;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Icon search failed', error);
|
||||
|
||||
if (seq === requestSeq) {
|
||||
hitIds.value = [];
|
||||
}
|
||||
} finally {
|
||||
if (seq === requestSeq) {
|
||||
isPending.value = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const results = computed(() => {
|
||||
if (!query.value) {
|
||||
return collection.value;
|
||||
}
|
||||
|
||||
// Hits without a local entity are dropped: lab icons while the Lab library is not
|
||||
// loaded, or icons the crawler indexed after this build was deployed.
|
||||
return hitIds.value
|
||||
.map((id) => byId.value.get(id))
|
||||
.filter((icon): icon is T => icon !== undefined);
|
||||
});
|
||||
|
||||
return { results, isPending };
|
||||
};
|
||||
|
||||
export default useIconSearch;
|
||||
@@ -1,32 +0,0 @@
|
||||
import Fuse, { type FuseOptionKeyObject } from 'fuse.js';
|
||||
import { shallowRef, computed, Ref } from 'vue';
|
||||
|
||||
const useSearch = <T>(
|
||||
query: Ref<string>,
|
||||
collection: Ref<T[]>,
|
||||
keys: FuseOptionKeyObject<T>[] = [],
|
||||
) => {
|
||||
const index = shallowRef(
|
||||
new Fuse(collection.value, {
|
||||
threshold: 0.2,
|
||||
useExtendedSearch: true,
|
||||
keys,
|
||||
}),
|
||||
);
|
||||
|
||||
const results = computed(() => {
|
||||
index.value.setCollection(collection.value);
|
||||
|
||||
if (query.value) {
|
||||
return index.value
|
||||
.search({ $and: query.value.split(' ').filter((t) => !!t) })
|
||||
.map((result) => result.item);
|
||||
}
|
||||
|
||||
return collection.value;
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export default useSearch;
|
||||
@@ -2,7 +2,6 @@
|
||||
import { computed } from 'vue';
|
||||
import { useData } from 'vitepress';
|
||||
import VPSidebarGroup from 'vitepress/dist/client/theme-default/components/VPSidebarGroup.vue';
|
||||
import VPNavBarSearch from '../components/overrides/VPNavBarSearch.vue';
|
||||
import FrameworkSelect from '../components/guide/FrameworkSelect.vue';
|
||||
import { guideSidebarTop } from '../../sidebar';
|
||||
|
||||
@@ -13,7 +12,6 @@ const isGuide = computed(() => page.value?.relativePath?.startsWith?.('guide'));
|
||||
|
||||
<template>
|
||||
<template v-if="isGuide">
|
||||
<VPNavBarSearch />
|
||||
<VPSidebarGroup :items="guideSidebarTop" />
|
||||
<FrameworkSelect />
|
||||
</template>
|
||||
|
||||
@@ -129,10 +129,6 @@ html {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.content-body .search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero .container .image {
|
||||
order: 1;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { LiteClient } from 'algoliasearch/lite';
|
||||
import { DefaultTheme, useData } from 'vitepress';
|
||||
|
||||
/**
|
||||
* The index caps retrievable hits at 1000 per query (`paginationLimitedTo`),
|
||||
* so a single page of this size is the most we can ever get back.
|
||||
*/
|
||||
const HITS_PER_PAGE = 1000;
|
||||
|
||||
let clientPromise: Promise<LiteClient> | undefined;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Lazily loads the Algolia lite client so it stays out of the initial page bundle.
|
||||
* Call it on search input focus to prefetch before the first keystroke.
|
||||
*/
|
||||
export function getAlgoliaClient(algoliaSearchOptions: DefaultTheme.AlgoliaSearchOptions): Promise<LiteClient> {
|
||||
clientPromise ??= import('algoliasearch/lite')
|
||||
.then(({ liteClient }) =>
|
||||
liteClient(algoliaSearchOptions.appId, algoliaSearchOptions.apiKey),
|
||||
)
|
||||
.catch((error) => {
|
||||
// Allow a retry on the next call instead of caching the failure.
|
||||
clientPromise = undefined;
|
||||
throw error;
|
||||
});
|
||||
|
||||
return clientPromise;
|
||||
}
|
||||
|
||||
export function prefetchAlgoliaClient(algoliaSearchOptions: DefaultTheme.AlgoliaSearchOptions) {
|
||||
getAlgoliaClient(algoliaSearchOptions).catch(() => {
|
||||
// Ignored here, the actual search reports errors.
|
||||
});
|
||||
}
|
||||
|
||||
const cache = new Map<string, string[]>();
|
||||
|
||||
/**
|
||||
* Searches the icon index and returns the matching `objectID`s in relevance order.
|
||||
* Core icons use their name as `objectID`, lab icons use `lab/<name>`.
|
||||
*/
|
||||
export async function searchIconIds(query: string, algoliaSearchOptions: DefaultTheme.AlgoliaSearchOptions): Promise<string[]> {
|
||||
const cached = cache.get(query);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const client = await getAlgoliaClient(algoliaSearchOptions);
|
||||
const {
|
||||
results: [result],
|
||||
} = await client.searchForHits<{ objectID: string }>({
|
||||
requests: [
|
||||
{
|
||||
indexName: 'icons',
|
||||
query,
|
||||
hitsPerPage: HITS_PER_PAGE,
|
||||
attributesToRetrieve: ['objectID'],
|
||||
attributesToHighlight: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const ids = result.hits.map((hit) => hit.objectID);
|
||||
cache.set(query, ids);
|
||||
|
||||
return ids;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { IconEntity } from '../types';
|
||||
|
||||
export type SortKey = 'popularity' | 'release-date' | 'name';
|
||||
|
||||
type SortableIcon = Pick<IconEntity, 'name' | 'popularity' | 'createdRelease' | 'awaitingRelease'>;
|
||||
|
||||
export function sortIcons<T extends SortableIcon>(icons: T[], sort: SortKey): T[] {
|
||||
switch (sort) {
|
||||
case 'popularity':
|
||||
return [...icons].sort((a, b) => (b.popularity || 0) - (a.popularity || 0));
|
||||
case 'release-date':
|
||||
return [...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;
|
||||
});
|
||||
case 'name':
|
||||
return [...icons].sort((a, b) => a.name.localeCompare(b.name));
|
||||
default:
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
Vendored
+7
@@ -35,6 +35,13 @@ declare global {
|
||||
// Defined by VitePress at build time, used by its VPDocAsideCarbonAds component
|
||||
const __CARBON__: boolean;
|
||||
|
||||
// Algolia icon search, see docs/.env.example
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_ALGOLIA_APP_ID: string;
|
||||
readonly VITE_ALGOLIA_SEARCH_API_KEY: string;
|
||||
readonly VITE_ALGOLIA_INDEX_NAME: string;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
_carbonads?: unknown;
|
||||
ExpoSnack?: {
|
||||
|
||||
@@ -18,6 +18,14 @@ pnpm docs:dev
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
Copy `.env.example` to `.env` if you need any of them. All variables are optional; see the comments in `.env.example`.
|
||||
|
||||
## Icon search
|
||||
|
||||
Search on `/icons` and `/icons/categories` queries the Algolia `icons` index, which is filled by the Algolia crawler. The site only reads from it (`.vitepress/theme/utils/algolia.ts`), it never writes to it.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
|
||||
+1
-1
@@ -65,8 +65,8 @@
|
||||
"@vueuse/components": "^14.4.0",
|
||||
"@vueuse/core": "^14.4.0",
|
||||
"@vueuse/integrations": "^14.4.0",
|
||||
"algoliasearch": "^5.59.0",
|
||||
"element-to-path": "^1.2.1",
|
||||
"fuse.js": "^7.5.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"lucide": "workspace:*",
|
||||
|
||||
Generated
+127
-127
@@ -155,12 +155,12 @@ importers:
|
||||
'@vueuse/integrations':
|
||||
specifier: ^14.4.0
|
||||
version: 14.4.0(focus-trap@7.8.0)(fuse.js@7.5.0)(vue@3.5.44(typescript@6.0.3))
|
||||
algoliasearch:
|
||||
specifier: ^5.59.0
|
||||
version: 5.59.0
|
||||
element-to-path:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
fuse.js:
|
||||
specifier: ^7.5.0
|
||||
version: 7.5.0
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
@@ -191,7 +191,7 @@ importers:
|
||||
devDependencies:
|
||||
'@docsearch/js':
|
||||
specifier: 3.8.2
|
||||
version: 3.8.2(@algolia/client-search@5.57.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
version: 3.8.2(@algolia/client-search@5.59.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
'@lucide/build-icons':
|
||||
specifier: workspace:*
|
||||
version: link:../tools/build-icons
|
||||
@@ -257,7 +257,7 @@ importers:
|
||||
version: 6.0.3
|
||||
vitepress:
|
||||
specifier: ^1.6.4
|
||||
version: 1.6.4(@algolia/client-search@5.57.0)(@types/node@26.4.1)(@types/react@19.3.0)(esbuild@0.28.2)(fuse.js@7.5.0)(jiti@2.7.0)(less@4.9.1(supports-color@10.2.2))(postcss@8.5.30)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(sass-embedded@1.105.1)(sass@1.105.1)(search-insights@2.8.2)(stylus@0.56.0(supports-color@10.2.2))(terser@5.51.2)(typescript@6.0.3)(yaml@2.9.0)
|
||||
version: 1.6.4(@algolia/client-search@5.59.0)(@types/node@26.4.1)(@types/react@19.3.0)(esbuild@0.28.2)(fuse.js@7.5.0)(jiti@2.7.0)(less@4.9.1(supports-color@10.2.2))(postcss@8.5.30)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(sass-embedded@1.105.1)(sass@1.105.1)(search-insights@2.8.2)(stylus@0.56.0(supports-color@10.2.2))(terser@5.51.2)(typescript@6.0.3)(yaml@2.9.0)
|
||||
vitepress-plugin-group-icons:
|
||||
specifier: ^1.7.6
|
||||
version: 1.7.6(vite@8.3.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@10.2.2))(sass-embedded@1.105.1)(sass@1.105.1)(stylus@0.56.0(supports-color@10.2.2))(terser@5.51.2)(yaml@2.9.0))
|
||||
@@ -1514,8 +1514,8 @@ packages:
|
||||
'@adobe/css-tools@4.5.0':
|
||||
resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==}
|
||||
|
||||
'@algolia/abtesting@1.23.0':
|
||||
resolution: {integrity: sha512-j45MBISstltys9QyQ4xf6quRiN1g7vMuwQL9VM4dx8YuRZvCQ173b9royZAx6iAbRX3IB1VnG1z//NuwyQ8jpQ==}
|
||||
'@algolia/abtesting@1.25.0':
|
||||
resolution: {integrity: sha512-rSTin9Uta23uaewYVQEp8XI9T3iA/zrg0/1G2vhf8oFFDxFL5vybnZ5IQwsVAg4JpKxPX4/WYNKdcfWrZymk7w==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/autocomplete-core@1.17.7':
|
||||
@@ -1538,56 +1538,56 @@ packages:
|
||||
'@algolia/client-search': '>= 4.9.1 < 6'
|
||||
algoliasearch: '>= 4.9.1 < 6'
|
||||
|
||||
'@algolia/client-abtesting@5.57.0':
|
||||
resolution: {integrity: sha512-JVFFujiZUCguk5tz3LZr4fTQxqpIrj4/Jw3SI7kMljSqtfLxYn/s/TWH0J2s4iNfsDpxPhgFGMotCpmDI4kZ8w==}
|
||||
'@algolia/client-abtesting@5.59.0':
|
||||
resolution: {integrity: sha512-bm2XN0hCSMYwStSsCBT0/PUB2BDxoyR1Lnub3c392HMEy9bi8PUSW8vR6zltVEKWG2t4PQFWMD5C07bmJxGgPg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-analytics@5.57.0':
|
||||
resolution: {integrity: sha512-6KqECK4ED3JJQEoDrQWnGPQzElA828xAD4qK5ceawNNyP/LcSvzAoLHjFkoTPksZ/kxj6VUtCRH+IHZesLltng==}
|
||||
'@algolia/client-analytics@5.59.0':
|
||||
resolution: {integrity: sha512-XOFPOTa69WuqHR6c5tMgnUUwwqQgNSzMpxmhrgA9KmxRf8WIqEa0cokHJvohk5CYb7CZx0xeSL6Bk2IUJ7Lv7Q==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-common@5.57.0':
|
||||
resolution: {integrity: sha512-uqpGF3oXYsoCbQq5d7BzNrNTfIfuvJyGP1CKvSW27T9boUg7KOwyxsAw1AX0a3jSW2HrYEJ/NN+Z4MiGivbpeQ==}
|
||||
'@algolia/client-common@5.59.0':
|
||||
resolution: {integrity: sha512-PC8ipLOYFKRTfIUY1J3FJxS6ryzWziaXmIX9/sNMoUR8L+XhF7hX2QAeUI87YVl5zujvlYTSOb+HAIqKXDjyHQ==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-insights@5.57.0':
|
||||
resolution: {integrity: sha512-u5NboJVJXDEFplvNnqqX4CxkXPYysjJRj47hOSh9329H8kG5gFLKJBIiS5utMQ+GZm8xQl3Te7NInDk6elEADQ==}
|
||||
'@algolia/client-insights@5.59.0':
|
||||
resolution: {integrity: sha512-yFNcCMM5fHiyoR0HuxMrzy+VjDcmhFUZTm2IJ2DHwGsVH3B5SEob4zTmeEZ3j/AZqNnmrJOCKl/tJnBg7+84bA==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-personalization@5.57.0':
|
||||
resolution: {integrity: sha512-uzc0b2LmHAK9/QID4xeo35OG84AkZl4YewkCqawqAOGLjT2eZpM/OZx45ESygMHG30Ws+ZTSdluPtMJcUnrbWQ==}
|
||||
'@algolia/client-personalization@5.59.0':
|
||||
resolution: {integrity: sha512-GYja6HkDt2VrQhWmB2cLx3Z5fDwI9no7q+xwCWcFrTPm5CLH9QZvK0XLO9co1FcNIDxxkMaP3AXM39/XcecEOg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-query-suggestions@5.57.0':
|
||||
resolution: {integrity: sha512-dIAhnM6ue/ssa5PjgNfu4g8A4yTojl9ZOUzZU3wIaIKRerL2R/3Emuf9n/D6ICXXP167KC6XCeC7nliSw7cuSw==}
|
||||
'@algolia/client-query-suggestions@5.59.0':
|
||||
resolution: {integrity: sha512-Wofg7bMpWh8N5qDDZs0wy6whc+KMmdNsxgrIGp9Ug2s1Bka0uq7AjyckdxReKPHLA0Q1qH8X/SNh0wn2t2X2Lw==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/client-search@5.57.0':
|
||||
resolution: {integrity: sha512-2TTPTTKSJmCptvhCm4Xf3bBYMqZni+Pgc2hVdqc4l9wsBpSJNVTVIKpnd10OubUgkGcmppVDj1XQqYaf6EnPSQ==}
|
||||
'@algolia/client-search@5.59.0':
|
||||
resolution: {integrity: sha512-fHnALZfbEnODczGk14Y/1YBRApp6UEpZUTexGcMUPzY7RDc7q4HN2Y6jh0KUG8Jo9B+q+wOoQEc3ziR0ErbuFg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/ingestion@1.57.0':
|
||||
resolution: {integrity: sha512-W4JseHKt+pzOxlFV+T3MWEG0h4Z2Se5zjoXUD0ewlw8aOWMG/yjRdopUdLQsXULepB/My2tDuZjkwk2sMfsUrQ==}
|
||||
'@algolia/ingestion@1.59.0':
|
||||
resolution: {integrity: sha512-Fa38s1mHgoaLCT117sfJ6P78rtxUt93CYxBYpq1VOIcslaF+cH+1h5uikAPsKfxLnsuEDZHiBiaI6eNPTsQMRA==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/monitoring@1.57.0':
|
||||
resolution: {integrity: sha512-BrxJVE0/eLinEPICCD7BKN/2xnt0nkjge70u8zzE2ISP3fuB3tjLgcwpanUycvlHBFLI4gK0l5ol54p6IYuR/Q==}
|
||||
'@algolia/monitoring@1.59.0':
|
||||
resolution: {integrity: sha512-NyNsRSqM2tF1MX7ZGw/j4rduoEJiQ5wfvbSo/CFVdEzYCjyxbGFRPOZyS/GETJG9rk1TdHOq8NZqKqk/u1fm4g==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/recommend@5.57.0':
|
||||
resolution: {integrity: sha512-Gc29jkeiLKlVfHvyrIgyUHHE+aYTdXEeLfK42rjr5/1TTVsYwUJz0XkvoIBIqfMjcDg6gXeHb1jTUZ0H+SYIlQ==}
|
||||
'@algolia/recommend@5.59.0':
|
||||
resolution: {integrity: sha512-nXBK2uygWtvbCOffMWqqfjjcEtp9enDKY5/2Pw/Hhw8VVaOyK2ThbGK04bNX/Le+qoK1VHYkodWGVTKfw0Otkw==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/requester-browser-xhr@5.57.0':
|
||||
resolution: {integrity: sha512-PIPnPN7MP3fp2VAi01BVXhCWmD366ZB2Hkq5TlYKtThd4KxUtMmaaNDpFgVCTXtSIqWVZLJntOHRvxg/sIPd8Q==}
|
||||
'@algolia/requester-browser-xhr@5.59.0':
|
||||
resolution: {integrity: sha512-yb+4afX/zja8QwX0KmV4/ae2kyYkRknsE79CRusYS2U5D8qwn2wq0cn1x12f3tm3F3+5FgmDdbuGdQTuCLSKMQ==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/requester-fetch@5.57.0':
|
||||
resolution: {integrity: sha512-AX3RlOudXMdTwtwUqdAf5hAVLvXfOZZH1FZh6ALDdrhVLT0TtAIe48N6nYcWcTnwoTxK/wDIQqZ19IMjK8zJAA==}
|
||||
'@algolia/requester-fetch@5.59.0':
|
||||
resolution: {integrity: sha512-Lp52TmpA1QtNmdHzs505Xwf4tgII7RAapkDSF9AAtWIBETcGaTIaXci4Rd9nS6SdaWK1BWTSqAPp5wzh6UN3Ag==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@algolia/requester-node-http@5.57.0':
|
||||
resolution: {integrity: sha512-cWZc1dKb7wy9/wPpwMtL1y89gK2G7y2A47Coa7zwf1ydtIeJm4+S+XxoQ2b/ZRiQnrC1YHavLjYUPDCdnT7Khg==}
|
||||
'@algolia/requester-node-http@5.59.0':
|
||||
resolution: {integrity: sha512-YYHLEs5rC6oRTFwT7bJaKBR9NSFzikeVHGAT1ffATmUQzR8XqyhHXjoieAUoR7fBU6I5cfy1FVeh5GoRgriuzA==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
@@ -7179,8 +7179,8 @@ packages:
|
||||
ajv@8.20.0:
|
||||
resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
|
||||
|
||||
algoliasearch@5.57.0:
|
||||
resolution: {integrity: sha512-HpND7MBGctOAkd1GoQoDZCGoCpqNTS5NG1LuhElFet3RdLJkwnyTYZXZhXwtpAQPrI36fqQ3eT6KQrdKDTKu3A==}
|
||||
algoliasearch@5.59.0:
|
||||
resolution: {integrity: sha512-wUXzaeI7B526W4y1gFg3lcxgDZ67XSgRJIiellYWOas/pLpO7rOtxm9Gr2E/C8aiSDXIgx1q5TdSsvK67Uakqw==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
alien-signals@3.2.1:
|
||||
@@ -13853,117 +13853,117 @@ snapshots:
|
||||
|
||||
'@adobe/css-tools@4.5.0': {}
|
||||
|
||||
'@algolia/abtesting@1.23.0':
|
||||
'@algolia/abtesting@1.25.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)(search-insights@2.8.2)':
|
||||
'@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.8.2)':
|
||||
dependencies:
|
||||
'@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)(search-insights@2.8.2)
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)
|
||||
'@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.8.2)
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)
|
||||
transitivePeerDependencies:
|
||||
- '@algolia/client-search'
|
||||
- algoliasearch
|
||||
- search-insights
|
||||
|
||||
'@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)(search-insights@2.8.2)':
|
||||
'@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.8.2)':
|
||||
dependencies:
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)
|
||||
search-insights: 2.8.2
|
||||
transitivePeerDependencies:
|
||||
- '@algolia/client-search'
|
||||
- algoliasearch
|
||||
|
||||
'@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)':
|
||||
'@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)':
|
||||
dependencies:
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)
|
||||
'@algolia/client-search': 5.57.0
|
||||
algoliasearch: 5.57.0
|
||||
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)
|
||||
'@algolia/client-search': 5.59.0
|
||||
algoliasearch: 5.59.0
|
||||
|
||||
'@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)':
|
||||
'@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)':
|
||||
dependencies:
|
||||
'@algolia/client-search': 5.57.0
|
||||
algoliasearch: 5.57.0
|
||||
'@algolia/client-search': 5.59.0
|
||||
algoliasearch: 5.59.0
|
||||
|
||||
'@algolia/client-abtesting@5.57.0':
|
||||
'@algolia/client-abtesting@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/client-analytics@5.57.0':
|
||||
'@algolia/client-analytics@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/client-common@5.57.0': {}
|
||||
'@algolia/client-common@5.59.0': {}
|
||||
|
||||
'@algolia/client-insights@5.57.0':
|
||||
'@algolia/client-insights@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/client-personalization@5.57.0':
|
||||
'@algolia/client-personalization@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/client-query-suggestions@5.57.0':
|
||||
'@algolia/client-query-suggestions@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/client-search@5.57.0':
|
||||
'@algolia/client-search@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/ingestion@1.57.0':
|
||||
'@algolia/ingestion@1.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/monitoring@1.57.0':
|
||||
'@algolia/monitoring@1.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/recommend@5.57.0':
|
||||
'@algolia/recommend@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
'@algolia/requester-browser-xhr@5.57.0':
|
||||
'@algolia/requester-browser-xhr@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
|
||||
'@algolia/requester-fetch@5.57.0':
|
||||
'@algolia/requester-fetch@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
|
||||
'@algolia/requester-node-http@5.57.0':
|
||||
'@algolia/requester-node-http@5.59.0':
|
||||
dependencies:
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
@@ -15630,9 +15630,9 @@ snapshots:
|
||||
|
||||
'@docsearch/css@3.8.2': {}
|
||||
|
||||
'@docsearch/js@3.8.2(@algolia/client-search@5.57.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)':
|
||||
'@docsearch/js@3.8.2(@algolia/client-search@5.59.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)':
|
||||
dependencies:
|
||||
'@docsearch/react': 3.8.2(@algolia/client-search@5.57.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
'@docsearch/react': 3.8.2(@algolia/client-search@5.59.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
preact: 10.29.8
|
||||
transitivePeerDependencies:
|
||||
- '@algolia/client-search'
|
||||
@@ -15642,12 +15642,12 @@ snapshots:
|
||||
- react-dom
|
||||
- search-insights
|
||||
|
||||
'@docsearch/react@3.8.2(@algolia/client-search@5.57.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)':
|
||||
'@docsearch/react@3.8.2(@algolia/client-search@5.59.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)':
|
||||
dependencies:
|
||||
'@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)(search-insights@2.8.2)
|
||||
'@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.57.0)(algoliasearch@5.57.0)
|
||||
'@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)(search-insights@2.8.2)
|
||||
'@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.59.0)(algoliasearch@5.59.0)
|
||||
'@docsearch/css': 3.8.2
|
||||
algoliasearch: 5.57.0
|
||||
algoliasearch: 5.59.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.3.0
|
||||
react: 19.3.1
|
||||
@@ -19833,22 +19833,22 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
algoliasearch@5.57.0:
|
||||
algoliasearch@5.59.0:
|
||||
dependencies:
|
||||
'@algolia/abtesting': 1.23.0
|
||||
'@algolia/client-abtesting': 5.57.0
|
||||
'@algolia/client-analytics': 5.57.0
|
||||
'@algolia/client-common': 5.57.0
|
||||
'@algolia/client-insights': 5.57.0
|
||||
'@algolia/client-personalization': 5.57.0
|
||||
'@algolia/client-query-suggestions': 5.57.0
|
||||
'@algolia/client-search': 5.57.0
|
||||
'@algolia/ingestion': 1.57.0
|
||||
'@algolia/monitoring': 1.57.0
|
||||
'@algolia/recommend': 5.57.0
|
||||
'@algolia/requester-browser-xhr': 5.57.0
|
||||
'@algolia/requester-fetch': 5.57.0
|
||||
'@algolia/requester-node-http': 5.57.0
|
||||
'@algolia/abtesting': 1.25.0
|
||||
'@algolia/client-abtesting': 5.59.0
|
||||
'@algolia/client-analytics': 5.59.0
|
||||
'@algolia/client-common': 5.59.0
|
||||
'@algolia/client-insights': 5.59.0
|
||||
'@algolia/client-personalization': 5.59.0
|
||||
'@algolia/client-query-suggestions': 5.59.0
|
||||
'@algolia/client-search': 5.59.0
|
||||
'@algolia/ingestion': 1.59.0
|
||||
'@algolia/monitoring': 1.59.0
|
||||
'@algolia/recommend': 5.59.0
|
||||
'@algolia/requester-browser-xhr': 5.59.0
|
||||
'@algolia/requester-fetch': 5.59.0
|
||||
'@algolia/requester-node-http': 5.59.0
|
||||
|
||||
alien-signals@3.2.1: {}
|
||||
|
||||
@@ -27162,10 +27162,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vitepress@1.6.4(@algolia/client-search@5.57.0)(@types/node@26.4.1)(@types/react@19.3.0)(esbuild@0.28.2)(fuse.js@7.5.0)(jiti@2.7.0)(less@4.9.1(supports-color@10.2.2))(postcss@8.5.30)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(sass-embedded@1.105.1)(sass@1.105.1)(search-insights@2.8.2)(stylus@0.56.0(supports-color@10.2.2))(terser@5.51.2)(typescript@6.0.3)(yaml@2.9.0):
|
||||
vitepress@1.6.4(@algolia/client-search@5.59.0)(@types/node@26.4.1)(@types/react@19.3.0)(esbuild@0.28.2)(fuse.js@7.5.0)(jiti@2.7.0)(less@4.9.1(supports-color@10.2.2))(postcss@8.5.30)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(sass-embedded@1.105.1)(sass@1.105.1)(search-insights@2.8.2)(stylus@0.56.0(supports-color@10.2.2))(terser@5.51.2)(typescript@6.0.3)(yaml@2.9.0):
|
||||
dependencies:
|
||||
'@docsearch/css': 3.8.2
|
||||
'@docsearch/js': 3.8.2(@algolia/client-search@5.57.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
'@docsearch/js': 3.8.2(@algolia/client-search@5.59.0)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)(search-insights@2.8.2)
|
||||
'@iconify-json/simple-icons': 1.2.94
|
||||
'@shikijs/core': 2.5.0
|
||||
'@shikijs/transformers': 2.5.0
|
||||
|
||||
Reference in New Issue
Block a user