mirror of
https://github.com/gradio-app/gradio.git
synced 2025-04-06 12:30:29 +08:00
Add search to website (#8624)
This commit is contained in:
parent
64ac05b111
commit
ba59bb824f
5
.changeset/wide-wasps-wait.md
Normal file
5
.changeset/wide-wasps-wait.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"website": patch
|
||||
---
|
||||
|
||||
feat:Add search to website
|
@ -210,6 +210,17 @@ def organize_docs(d):
|
||||
|
||||
pages = organize_pages()
|
||||
|
||||
# content_json = {}
|
||||
# def generate_content_json(pages):
|
||||
# for library in pages:
|
||||
# for category in pages[library]:
|
||||
# for page in category["pages"]:
|
||||
# page_path = os.path.join(TEMPLATES_DIR, page["path"] + ".svx")
|
||||
# with open(page_path) as f:
|
||||
# content = f.read()
|
||||
# content_json["content"] = content
|
||||
|
||||
|
||||
organized["gradio"]["events_matrix"] = component_events
|
||||
organized["gradio"]["events"] = events
|
||||
|
||||
|
@ -18,7 +18,8 @@
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"prismjs": "1.29.0",
|
||||
"tailwindcss": "^3.1.6"
|
||||
"tailwindcss": "^3.1.6",
|
||||
"flexsearch": "^0.7.43"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
@ -196,10 +196,7 @@ code.language-bash {
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button {
|
||||
@apply appearance-none h-5 w-5;
|
||||
-webkit-appearance: none;
|
||||
background-image: url("/src/lib/assets/img/esc.svg");
|
||||
background-size: 20px 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-code {
|
||||
|
@ -6,40 +6,9 @@
|
||||
export let current_nav_link = "";
|
||||
|
||||
let show_nav = false;
|
||||
let searchTerm = "";
|
||||
let searchBar: HTMLInputElement;
|
||||
|
||||
const search = () => {
|
||||
let links = document.querySelectorAll(
|
||||
".navigation a"
|
||||
) as NodeListOf<HTMLAnchorElement>;
|
||||
links.forEach((link) => {
|
||||
let linkText = link.innerText.toLowerCase();
|
||||
if (linkText.includes(searchTerm.toLowerCase())) {
|
||||
link.style.display = "block";
|
||||
} else {
|
||||
link.style.display = "none";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
searchBar.focus();
|
||||
}
|
||||
if (e.key == "Escape") {
|
||||
searchTerm = "";
|
||||
searchBar.blur();
|
||||
search();
|
||||
}
|
||||
}
|
||||
|
||||
import DropDown from "$lib/components/VersionDropdown.svelte";
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<section
|
||||
class="top-0 left-0 fixed flex items-center p-4 rounded-br-lg backdrop-blur-lg z-50 bg-gray-200/50 lg:hidden"
|
||||
id="menu-bar"
|
||||
@ -88,16 +57,6 @@
|
||||
<div
|
||||
class="w-full sticky top-0 bg-gradient-to-r from-white to-gray-50 z-10 hidden lg:block my-4 ml-4"
|
||||
>
|
||||
<input
|
||||
bind:value={searchTerm}
|
||||
on:input={search}
|
||||
bind:this={searchBar}
|
||||
id="search"
|
||||
type="search"
|
||||
class="w-4/5 rounded-md border-gray-200 focus:placeholder-transparent focus:shadow-none focus:border-orange-500 focus:ring-0"
|
||||
placeholder="Search ⌘-k / ctrl-k"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<DropDown></DropDown>
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { store } from "../../routes/+layout.svelte";
|
||||
|
||||
import { gradio_logo, github_black } from "../assets";
|
||||
import { gradio_logo } from "../assets";
|
||||
import Search from "./search";
|
||||
|
||||
let click_nav = false;
|
||||
let show_help_menu = false;
|
||||
@ -103,16 +104,17 @@
|
||||
class="thin-link inline-block px-4 py-2 hover:bg-gray-100"
|
||||
href="/brand">Brand</a
|
||||
>
|
||||
<a
|
||||
class="thin-link inline-block px-4 py-2 hover:bg-gray-100"
|
||||
href="https://github.com/gradio-app/gradio"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<a
|
||||
class="thin-link flex items-center gap-3"
|
||||
href="https://github.com/gradio-app/gradio"
|
||||
>
|
||||
<img src={github_black} class="w-6" alt="Github logo" />
|
||||
</a>
|
||||
<Search />
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,40 +9,10 @@
|
||||
let docs_type = "js";
|
||||
|
||||
let show_nav = false;
|
||||
let searchTerm = "";
|
||||
let searchBar: HTMLInputElement;
|
||||
|
||||
const search = () => {
|
||||
let links = document.querySelectorAll(
|
||||
".navigation a"
|
||||
) as NodeListOf<HTMLAnchorElement>;
|
||||
links.forEach((link) => {
|
||||
let linkText = link.innerText.toLowerCase();
|
||||
if (linkText.includes(searchTerm.toLowerCase())) {
|
||||
link.style.display = "block";
|
||||
} else {
|
||||
link.style.display = "none";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
searchBar.focus();
|
||||
}
|
||||
if (e.key == "Escape") {
|
||||
searchTerm = "";
|
||||
searchBar.blur();
|
||||
search();
|
||||
}
|
||||
}
|
||||
|
||||
import DropDown from "$lib/components/VersionDropdown.svelte";
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<section
|
||||
class="top-0 fixed -ml-4 flex items-center p-4 rounded-br-lg backdrop-blur-lg z-50 bg-gray-200/50 lg:hidden"
|
||||
id="menu-bar"
|
||||
@ -91,16 +61,6 @@
|
||||
<div
|
||||
class="w-full sticky top-0 bg-gradient-to-r from-white to-gray-50 z-10 hidden lg:block my-4 ml-4"
|
||||
>
|
||||
<input
|
||||
bind:value={searchTerm}
|
||||
on:input={search}
|
||||
bind:this={searchBar}
|
||||
id="search"
|
||||
type="search"
|
||||
class="w-4/5 rounded-md border-gray-200 focus:placeholder-transparent focus:shadow-none focus:border-orange-500 focus:ring-0"
|
||||
placeholder="Search ⌘-k / ctrl-k"
|
||||
autocomplete="off"
|
||||
/>
|
||||
{#if version_dropdown}
|
||||
<DropDown docs_type="js"></DropDown>
|
||||
{/if}
|
||||
|
15
js/_website/src/lib/components/search/SearchIcon.svelte
Normal file
15
js/_website/src/lib/components/search/SearchIcon.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<title>Search</title>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.3-4.3" />
|
||||
</svg>
|
After Width: | Height: | Size: 285 B |
1
js/_website/src/lib/components/search/index.ts
Normal file
1
js/_website/src/lib/components/search/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from "./search.svelte";
|
17
js/_website/src/lib/components/search/search-worker.ts
Normal file
17
js/_website/src/lib/components/search/search-worker.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { create_pages_index, search_pages_index } from "./search";
|
||||
|
||||
addEventListener("message", async (e) => {
|
||||
const { type, payload } = e.data;
|
||||
|
||||
if (type === "load") {
|
||||
const posts = await fetch("/search-api").then((res) => res.json());
|
||||
create_pages_index(posts);
|
||||
postMessage({ type: "ready" });
|
||||
}
|
||||
|
||||
if (type === "search") {
|
||||
const search_term = payload.search_term;
|
||||
const results = search_pages_index(search_term);
|
||||
postMessage({ type: "results", payload: { results, search_term } });
|
||||
}
|
||||
});
|
344
js/_website/src/lib/components/search/search.svelte
Normal file
344
js/_website/src/lib/components/search/search.svelte
Normal file
@ -0,0 +1,344 @@
|
||||
<script lang="ts">
|
||||
import Search_Worker from "./search-worker?worker";
|
||||
import SearchIcon from "./SearchIcon.svelte";
|
||||
import { onNavigate } from "$app/navigation";
|
||||
import type { Result } from "./search";
|
||||
import { browser } from "$app/environment";
|
||||
|
||||
let search: "idle" | "load" | "ready" = "idle";
|
||||
let search_term = "";
|
||||
let results: Result[] = [];
|
||||
let search_worker: Worker;
|
||||
|
||||
function initialize() {
|
||||
open = true;
|
||||
if (search === "ready") return;
|
||||
search = "load";
|
||||
search_worker = new Search_Worker();
|
||||
search_worker.addEventListener("message", (e) => {
|
||||
const { type, payload } = e.data;
|
||||
type === "ready" && (search = "ready");
|
||||
type === "results" && (results = payload.results);
|
||||
});
|
||||
search_worker.postMessage({ type: "load" });
|
||||
}
|
||||
|
||||
let open: boolean = false;
|
||||
|
||||
onNavigate(() => {
|
||||
open = false;
|
||||
});
|
||||
|
||||
$: if (search === "ready") {
|
||||
search_worker.postMessage({ type: "search", payload: { search_term } });
|
||||
}
|
||||
|
||||
$: if (search_term && !open) {
|
||||
search_term = "";
|
||||
}
|
||||
|
||||
let content_elem: HTMLElement;
|
||||
let search_button_elem: HTMLElement;
|
||||
|
||||
function focus_input(el: HTMLInputElement) {
|
||||
el.focus();
|
||||
}
|
||||
|
||||
function get_os() {
|
||||
// @ts-ignore - userAgentData is not yet in the TS types as it is currently experimental
|
||||
return navigator.userAgentData.platform ?? navigator.userAgent;
|
||||
}
|
||||
|
||||
let meta_key = "⌘";
|
||||
|
||||
$: if (browser && navigator) {
|
||||
let os = get_os();
|
||||
meta_key = os.includes("Mac") || os.includes("mac") ? "⌘" : "CTRL+";
|
||||
}
|
||||
|
||||
function handle_key_down(e: KeyboardEvent): void {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === "k" || e.key === "K") {
|
||||
e.preventDefault();
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
open = false;
|
||||
}
|
||||
if ((e.key === "ArrowUp" || e.key === "ArrowDown") && open) {
|
||||
e.preventDefault();
|
||||
const current = document.activeElement;
|
||||
const items = [...document.getElementsByClassName("res-block")];
|
||||
|
||||
const current_index = current ? items.indexOf(current) : -1;
|
||||
let new_index;
|
||||
if (current_index === -1) {
|
||||
new_index = 1;
|
||||
} else {
|
||||
if (e.key === "ArrowUp") {
|
||||
new_index = (current_index + items.length - 1) % items.length;
|
||||
} else {
|
||||
new_index = (current_index + 1) % items.length;
|
||||
}
|
||||
}
|
||||
|
||||
(current as HTMLElement).blur();
|
||||
|
||||
const newElement = items[new_index] as HTMLElement;
|
||||
if (newElement) {
|
||||
newElement.focus();
|
||||
document
|
||||
.querySelectorAll(".res-block")
|
||||
.forEach((el) => el.classList.remove("first-res"));
|
||||
}
|
||||
}
|
||||
const search_input = document.getElementById(
|
||||
"search-input"
|
||||
) as HTMLInputElement;
|
||||
const first_result = document.querySelector(".res-block") as HTMLElement;
|
||||
if (e.key === "Enter" && document.activeElement === search_input) {
|
||||
first_result.click();
|
||||
}
|
||||
}
|
||||
|
||||
function on_click(e: MouseEvent) {
|
||||
if (content_elem) {
|
||||
if (!content_elem.contains(e.target as Node) && open) {
|
||||
open = false;
|
||||
}
|
||||
} else {
|
||||
if (search_button_elem.contains(e.target as Node)) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: if (browser && document.querySelector(".res-block")) {
|
||||
document.querySelector(".res-block")?.classList.add("first-res");
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handle_key_down} on:click={on_click} />
|
||||
|
||||
<button class="search-button" bind:this={search_button_elem}>
|
||||
<SearchIcon />
|
||||
<span class="pl-1 pr-5">Search</span>
|
||||
<div class="shortcut">
|
||||
<div class="text-sm">
|
||||
{meta_key}K
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if open}
|
||||
<div class="overlay" />
|
||||
<div class="content" bind:this={content_elem}>
|
||||
<div class="search-bar">
|
||||
{#if search === "load"}
|
||||
<div class="loader"></div>
|
||||
{:else}
|
||||
<SearchIcon />
|
||||
{/if}
|
||||
<input
|
||||
bind:value={search_term}
|
||||
placeholder="What are you searching for?"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
enterkeyhint="go"
|
||||
maxlength="64"
|
||||
spellcheck="false"
|
||||
type="search"
|
||||
use:focus_input
|
||||
id="search-input"
|
||||
/>
|
||||
<button
|
||||
on:click={() => {
|
||||
open = false;
|
||||
}}
|
||||
class="text-xs font-semibold rounded-md p-1 border-gray-300 border"
|
||||
>
|
||||
ESC
|
||||
</button>
|
||||
</div>
|
||||
<div class="results">
|
||||
{#if results.length}
|
||||
<ul>
|
||||
{#each results as result, i}
|
||||
{#if result.content.length > 0}
|
||||
<li>
|
||||
<a
|
||||
class="res-block"
|
||||
class:first-res={i === 0}
|
||||
href={result.slug}
|
||||
>
|
||||
<p
|
||||
class:text-green-700={result.type == "DOCS"}
|
||||
class:bg-green-100={result.type == "DOCS"}
|
||||
class:text-orange-700={result.type == "GUIDE"}
|
||||
class:bg-orange-100={result.type == "GUIDE"}
|
||||
class="float-left text-xs font-semibold rounded-md p-1 px-2 mx-1 mt-[3px]"
|
||||
>
|
||||
{result.type}
|
||||
</p>
|
||||
<div class="float-right">
|
||||
<div class="enter">↵</div>
|
||||
</div>
|
||||
<p>{@html result.title}</p>
|
||||
<ol>
|
||||
{#each result.content as content}
|
||||
<li class="res-content">{@html content}</li>
|
||||
{/each}
|
||||
</ol>
|
||||
</a>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
{#if search_term}
|
||||
{#if search === "load"}
|
||||
<p class="mx-auto w-fit text-gray-500">Searching for results...</p>
|
||||
{:else}
|
||||
<p class="mx-auto w-fit text-gray-500">
|
||||
No results found. Try using a different term.
|
||||
</p>
|
||||
{/if}
|
||||
{/if}
|
||||
<ul>
|
||||
<p class="">Suggestions</p>
|
||||
<li>
|
||||
<a class="res-block first-res" href="/quickstart">
|
||||
<p
|
||||
class="float-left text-xs mx-1 font-semibold text-orange-700 bg-orange-100 rounded-md p-1 px-2 mt-[3px]"
|
||||
>
|
||||
GUIDE
|
||||
</p>
|
||||
<div class="float-right">
|
||||
<div class="enter">↵</div>
|
||||
</div>
|
||||
<p>Quickstart</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="res-block" href="/docs/gradio/interface">
|
||||
<p
|
||||
class="float-left text-xs font-semibold text-green-700 bg-green-100 rounded-md p-1 px-2 mx-1 mt-[3px]"
|
||||
>
|
||||
DOCS
|
||||
</p>
|
||||
<div class="float-right">
|
||||
<div class="enter">↵</div>
|
||||
</div>
|
||||
<p>Interface</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="res-block" href="/docs/gradio/blocks">
|
||||
<p
|
||||
class="float-left text-xs font-semibold text-green-700 bg-green-100 rounded-md p-1 px-2 mx-1 mt-[3px]"
|
||||
>
|
||||
DOCS
|
||||
</p>
|
||||
<div class="float-right">
|
||||
<div class="enter">↵</div>
|
||||
</div>
|
||||
<p>Blocks</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.overlay {
|
||||
@apply fixed inset-0 z-30 backdrop-blur-sm bg-black/20;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
@apply font-sans text-lg z-10 px-4 relative flex flex-none items-center border-b border-gray-100 text-gray-500;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
@apply text-lg appearance-none h-14 text-black mx-1 flex-auto min-w-0 border-none cursor-text;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply fixed left-1/2 top-1/4 -translate-x-1/2 mx-auto w-screen max-w-3xl flex flex-col min-h-0 rounded-lg shadow-2xl bg-white z-40;
|
||||
}
|
||||
|
||||
.results {
|
||||
@apply p-5 overflow-y-auto;
|
||||
max-height: 60vh;
|
||||
scrollbar-width: thin;
|
||||
|
||||
& ol {
|
||||
margin-block-start: 2px;
|
||||
}
|
||||
|
||||
& li:not(:last-child) {
|
||||
margin-block-end: 4px;
|
||||
padding-block-end: 4px;
|
||||
}
|
||||
|
||||
& a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.search-button {
|
||||
@apply flex flex-row rounded-full items-center cursor-pointer px-2 text-gray-400 border-gray-300 border text-lg outline-none font-sans;
|
||||
}
|
||||
|
||||
:global(.res-content .mark) {
|
||||
color: #ff7c00;
|
||||
text-decoration: underline;
|
||||
}
|
||||
:global(.res-content) {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
:global(.res-block) {
|
||||
@apply m-2 p-2 border border-gray-100 rounded-md bg-gray-50 hover:bg-gray-100 hover:scale-[1.01] focus:bg-gray-100 focus:scale-[1.01] focus:outline-none;
|
||||
}
|
||||
:global(.first-res) {
|
||||
@apply bg-gray-100 scale-[1.01];
|
||||
}
|
||||
|
||||
:global(.res-block:focus .enter) {
|
||||
display: block !important;
|
||||
}
|
||||
:global(.first-res .enter) {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.enter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.enter {
|
||||
@apply text-xs font-semibold rounded-md p-1 border-gray-300 border text-gray-500 font-sans bg-white;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 1px solid #d0cfcf;
|
||||
border-top: 2px solid #475469;
|
||||
border-radius: 50%;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
animation: spin 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
71
js/_website/src/lib/components/search/search.ts
Normal file
71
js/_website/src/lib/components/search/search.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import FlexSearch from "flexsearch";
|
||||
|
||||
export type Page = {
|
||||
content: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type Result = {
|
||||
content: string[];
|
||||
slug: string;
|
||||
title: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
let pages_index: FlexSearch.Index;
|
||||
let pages: Page[];
|
||||
|
||||
export function create_pages_index(data: Page[]) {
|
||||
pages_index = new FlexSearch.Index({ tokenize: "forward" });
|
||||
|
||||
data.forEach((page, i) => {
|
||||
const item = `${page.title} ${page.content}`;
|
||||
pages_index.add(i, item);
|
||||
});
|
||||
|
||||
pages = data;
|
||||
}
|
||||
|
||||
export function search_pages_index(search_term: string) {
|
||||
const match = search_term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const results = pages_index.search(match);
|
||||
return results
|
||||
.map((index) => pages[index as number])
|
||||
.map(({ slug, title, content, type }) => {
|
||||
return {
|
||||
slug,
|
||||
title: replace_text_with_marker(title, match),
|
||||
content: get_matches(content, match),
|
||||
type
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function replace_text_with_marker(text: string, match: string) {
|
||||
const regex = new RegExp(match, "gi");
|
||||
return text.replaceAll(
|
||||
regex,
|
||||
(match) => `<span class='mark'>${match}</span>`
|
||||
);
|
||||
}
|
||||
|
||||
function get_matches(text: string, search_term: string, limit = 1) {
|
||||
const regex = new RegExp(search_term, "gi");
|
||||
const indexes = [];
|
||||
let matches = 0;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(text)) !== null && matches < limit) {
|
||||
indexes.push(match.index);
|
||||
matches++;
|
||||
}
|
||||
|
||||
return indexes.map((index) => {
|
||||
const start = index - 20;
|
||||
const end = index + 80;
|
||||
const excerpt = text.substring(start, end).trim();
|
||||
return `...${replace_text_with_marker(excerpt, search_term)}...`;
|
||||
});
|
||||
}
|
118
js/_website/src/routes/search-api/+server.ts
Normal file
118
js/_website/src/routes/search-api/+server.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import { json } from "@sveltejs/kit";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
function removeMarkdown(markdown) {
|
||||
return markdown
|
||||
.replace(/^#{1,6}\s+/gm, "")
|
||||
.replace(/(\*\*|__)(.*?)\1/g, "$2")
|
||||
.replace(/(\*|_)(.*?)\1/g, "$2")
|
||||
.replace(/~~(.*?)~~/g, "$1")
|
||||
.replace(/`([^`]+)`/g, "$1")
|
||||
.replace(/```[\s\S]*?```/g, "")
|
||||
.replace(/!\[.*?\]\(.*?\)/g, "")
|
||||
.replace(/\[(.*?)\]\(.*?\)/g, "$1")
|
||||
.replace(/^>\s+/gm, "")
|
||||
.replace(/^---$/gm, "")
|
||||
.replace(/^\s*[-+*]\s+/gm, "")
|
||||
.replace(/^\s*\d+\.\s+/gm, "")
|
||||
.replace(/\n{2,}/g, "\n")
|
||||
.trim();
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const gradio_doc_paths = import.meta.glob(
|
||||
"/src/lib/templates/gradio/**/*.svx"
|
||||
);
|
||||
const gradio_doc_pages = await Promise.all(
|
||||
Object.entries(gradio_doc_paths).map(async ([path, content]) => {
|
||||
content = await content();
|
||||
content = content.default.render().html;
|
||||
let match = content.match(/<h1[^>]*>(.*?)<\/h1>/i);
|
||||
let title = "";
|
||||
if (match && match[1]) {
|
||||
title = match[1];
|
||||
}
|
||||
path = path.split("/").slice(-1)[0];
|
||||
path = path.match(/(?:\d{2}_)?(.+)/i)[1];
|
||||
path = "/main/docs/gradio/" + path.split(".svx")[0];
|
||||
|
||||
return {
|
||||
title: title,
|
||||
slug: path,
|
||||
content: content.replaceAll(/<[^>]*>?/gm, ""),
|
||||
type: "DOCS"
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const client_doc_paths = import.meta.glob(
|
||||
"/src/lib/templates/python-client/**/*.svx"
|
||||
);
|
||||
const client_doc_pages = await Promise.all(
|
||||
Object.entries(client_doc_paths).map(async ([path, content]) => {
|
||||
content = await content();
|
||||
content = content.default.render().html;
|
||||
let match = content.match(/<h1[^>]*>(.*?)<\/h1>/i);
|
||||
let title = "";
|
||||
if (match && match[1]) {
|
||||
title = match[1];
|
||||
}
|
||||
path = path.split("/").slice(-1)[0];
|
||||
path = path.match(/(?:\d{2}_)?(.+)/i)[1];
|
||||
path = "/main/docs/python-client/" + path.split(".svx")[0];
|
||||
|
||||
return {
|
||||
title: title,
|
||||
slug: path,
|
||||
content: content.replaceAll(/<[^>]*>?/gm, ""),
|
||||
type: "DOCS"
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const guide_paths = import.meta.glob("/src/lib/json/guides/*.json");
|
||||
delete guide_paths["/src/lib/json/guides/guides_by_category.json"];
|
||||
delete guide_paths["/src/lib/json/guides/guide_names.json"];
|
||||
const guide_pages = await Promise.all(
|
||||
Object.entries(guide_paths).map(async ([path, content]) => {
|
||||
content = await content();
|
||||
content = content.default.guide;
|
||||
return {
|
||||
title: content.pretty_name,
|
||||
slug: content.url,
|
||||
content: removeMarkdown(content.content.replaceAll(/<[^>]*>?/gm, "")),
|
||||
type: "GUIDE"
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const jsons_path = import.meta.glob("/src/lib/json/docs.json");
|
||||
const jsons_content = await jsons_path["/src/lib/json/docs.json"]();
|
||||
|
||||
const js_client_page = {
|
||||
title: "JavaScript Client Library",
|
||||
slug: "/docs/js-client",
|
||||
content: removeMarkdown(jsons_content.default.js_client),
|
||||
type: "DOCS"
|
||||
};
|
||||
|
||||
const js_components = jsons_content.default.js;
|
||||
const js_pages = await Promise.all(
|
||||
Object.entries(js_components).map(async ([name, content]) => {
|
||||
return {
|
||||
title: name,
|
||||
slug: "/docs/js/" + name,
|
||||
content: removeMarkdown(content.replaceAll(/<[^>]*>?/gm, "")),
|
||||
type: "DOCS"
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
let all_pages = gradio_doc_pages
|
||||
.concat(client_doc_pages)
|
||||
.concat(guide_pages)
|
||||
.concat([js_client_page])
|
||||
.concat(js_pages);
|
||||
return json(all_pages);
|
||||
}
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -458,6 +458,9 @@ importers:
|
||||
'@types/prismjs':
|
||||
specifier: ^1.26.0
|
||||
version: 1.26.1
|
||||
flexsearch:
|
||||
specifier: ^0.7.43
|
||||
version: 0.7.43
|
||||
prismjs:
|
||||
specifier: 1.29.0
|
||||
version: 1.29.0
|
||||
@ -6261,6 +6264,9 @@ packages:
|
||||
flatted@3.3.1:
|
||||
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
|
||||
|
||||
flexsearch@0.7.43:
|
||||
resolution: {integrity: sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==}
|
||||
|
||||
flow-parser@0.235.1:
|
||||
resolution: {integrity: sha512-s04193L4JE+ntEcQXbD6jxRRlyj9QXcgEl2W6xSjH4l9x4b0eHoCHfbYHjqf9LdZFUiM5LhgpiqsvLj/AyOyYQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@ -14816,6 +14822,8 @@ snapshots:
|
||||
|
||||
flatted@3.3.1: {}
|
||||
|
||||
flexsearch@0.7.43: {}
|
||||
|
||||
flow-parser@0.235.1: {}
|
||||
|
||||
for-each@0.3.3:
|
||||
|
Loading…
x
Reference in New Issue
Block a user