Gradio Agents & MCP 黑客马拉松

获奖者
Gradio logo
  1. 自定义组件
  2. 前端

前端 🌐⭐️

本指南将涵盖实现自定义组件前端所需的一切知识。

提示: Gradio 组件使用 Svelte。编写 Svelte 很有趣!如果你不熟悉它,我们建议查看其交互式[指南](https://learn.svelte.dev/tutorial/welcome-to-svelte)。

目录结构

前端代码至少应包含以下三个文件:

  • Index.svelte: 这是主要导出文件,组件的布局和逻辑应在此处。
  • Example.svelte: 这里定义了组件的示例视图。

你可以随意添加额外的文件和子目录。如果你想导出任何额外的模块,请记住修改 package.json 文件

"exports": {
    ".": "./Index.svelte",
    "./example": "./Example.svelte",
    "./package.json": "./package.json"
},

Index.svelte 文件

你的组件应公开以下将从父 Gradio 应用程序传递下来的 props。

import type { LoadingStatus } from "@gradio/statustracker";
import type { Gradio } from "@gradio/utils";

export let gradio: Gradio<{
    event_1: never;
    event_2: never;
}>;

export let elem_id = "";
export let elem_classes: string[] = [];
export let scale: number | null = null;
export let min_width: number | undefined = undefined;
export let loading_status: LoadingStatus | undefined = undefined;
export let mode: "static" | "interactive";
  • elem_idelem_classes 允许 Gradio 应用开发者使用 Python Blocks 类针对你的组件进行自定义 CSS 和 JavaScript。

  • scalemin_width 允许 Gradio 应用开发者控制你的组件在 UI 中占据的空间大小。

  • 当组件是事件的输出时,loading_status 用于在组件上方显示加载状态。

  • mode 是父 Gradio 应用告诉你的组件应该显示交互版本还是静态版本的方式。

  • gradio: gradio 对象由父 Gradio 应用创建。它存储了一些对你的组件有用的应用级配置,例如国际化。你必须使用它从组件分派事件。

一个最小的 Index.svelte 文件如下所示:

<script lang="ts">
	import type { LoadingStatus } from "@gradio/statustracker";
    import { Block } from "@gradio/atoms";
	import { StatusTracker } from "@gradio/statustracker";
	import type { Gradio } from "@gradio/utils";

	export let gradio: Gradio<{
		event_1: never;
		event_2: never;
	}>;

    export let value = "";
	export let elem_id = "";
	export let elem_classes: string[] = [];
	export let scale: number | null = null;
	export let min_width: number | undefined = undefined;
	export let loading_status: LoadingStatus | undefined = undefined;
    export let mode: "static" | "interactive";
</script>

<Block
	visible={true}
	{elem_id}
	{elem_classes}
	{scale}
	{min_width}
	allow_overflow={false}
	padding={true}
>
	{#if loading_status}
		<StatusTracker
			autoscroll={gradio.autoscroll}
			i18n={gradio.i18n}
			{...loading_status}
		/>
	{/if}
    <p>{value}</p>
</Block>

Example.svelte 文件

Example.svelte 文件应公开以下 props

    export let value: string;
    export let type: "gallery" | "table";
    export let selected = false;
    export let index: number;
  • value: 应该显示的示例值。

  • type: 这是一个变量,可以是 "gallery""table",具体取决于示例的显示方式。当示例对应于单个输入组件时,使用 "gallery" 形式;当用户有多个输入组件且需要填充所有组件时,使用 "table" 形式。

  • selected: 你还可以通过使用 selected 变量来调整示例的显示方式(如果用户“选择”了特定示例)。

  • index: 当前选中值的索引。

  • 你的“非示例”组件接受的任何额外 props!

这是代码 Radio 组件的 Example.svelte 文件

<script lang="ts">
	export let value: string;
	export let type: "gallery" | "table";
	export let selected = false;
</script>

<div
	class:table={type === "table"}
	class:gallery={type === "gallery"}
	class:selected
>
	{value}
</div>

<style>
	.gallery {
		padding: var(--size-1) var(--size-2);
	}
</style>

处理文件

如果你的组件处理文件,这些文件应该上传到后端服务器。@gradio/client npm 包提供了 uploadprepare_files 实用函数来帮助你完成此操作。

prepare_files 函数会将浏览器的 File 数据类型转换为 gradio 的内部 FileData 类型。你应在组件中使用 FileData 数据来跟踪上传的文件。

upload 函数会将 FileData 值的数组上传到服务器。

这是一个从 <input> 元素加载文件并在其值更改时处理的示例。

<script lang="ts">
    import { upload, prepare_files, type FileData } from "@gradio/client";
    export let root;
    export let value;
    let uploaded_files;

    async function handle_upload(file_data: FileData[]): Promise<void> {
        await tick();
        uploaded_files = await upload(file_data, root);
    }

    async function loadFiles(files: FileList): Promise<void> {
        let _files: File[] = Array.from(files);
        if (!files.length) {
            return;
        }
        if (file_count === "single") {
            _files = [files[0]];
        }
        let file_data = await prepare_files(_files);
        await handle_upload(file_data);
    }

    async function loadFilesFromUpload(e: Event): Promise<void> {
		const target = e.target;

		if (!target.files) return;
		await loadFiles(target.files);
	}
</script>

<input
    type="file"
    on:change={loadFilesFromUpload}
    multiple={true}
/>

该组件公开了一个名为 root 的 prop。这是由父 Gradio 应用程序传递下来的,它代表文件将上传和获取的基础 URL。

对于 WASM 支持,你应该从 Context 获取上传函数,并将其作为 upload 函数的第三个参数传递。

<script lang="ts">
    import { getContext } from "svelte";
    const upload_fn = getContext<typeof upload_files>("upload_files");

    async function handle_upload(file_data: FileData[]): Promise<void> {
        await tick();
        await upload(file_data, root, upload_fn);
    }
</script>

利用现有 Gradio 组件

大多数 Gradio 前端组件都发布在 npm,这是一个 JavaScript 包仓库。这意味着你可以使用它们来节省时间,同时在组件中融入常见模式,例如文件上传。例如,@gradio/upload 包包含 UploadModifyUpload 组件,用于正确将文件上传到 Gradio 服务器。以下是如何使用它们来创建用户界面以上传和显示 PDF 文件。

<script>
	import { type FileData, Upload, ModifyUpload } from "@gradio/upload";
	import { Empty, UploadText, BlockLabel } from "@gradio/atoms";
</script>

<BlockLabel Icon={File} label={label || "PDF"} />
{#if value === null && interactive}
    <Upload
        filetype="application/pdf"
        on:load={handle_load}
        {root}
        >
        <UploadText type="file" i18n={gradio.i18n} />
    </Upload>
{:else if value !== null}
    {#if interactive}
        <ModifyUpload i18n={gradio.i18n} on:clear={handle_clear}/>
    {/if}
    <iframe title={value.orig_name || "PDF"} src={value.data} height="{height}px" width="100%"></iframe>
{:else}
    <Empty size="large"> <File/> </Empty>	
{/if}

你还可以组合现有 Gradio 组件来创建完全独特的体验。例如,渲染聊天机器人对话的图库。可能性是无限的,请在此处阅读我们的 JavaScript 包文档。我们将在未来几周内添加更多包和文档!

匹配 Gradio 核心设计系统

你可以通过 Storybook 浏览我们的组件库。你将能够与我们的组件进行交互,并查看它们在各种状态下的表现。

对于那些对设计定制感兴趣的人,我们提供了包含我们调色板、半径、间距和所用图标的 CSS 变量——这样你就可以轻松地将自定义组件与我们核心组件的样式匹配起来。此 Storybook 将定期更新,包含任何新增内容或更改。

Storybook 链接

自定义配置

如果你想利用庞大的 Vite 生态系统,可以使用 gradio.config.js 文件来配置组件的构建过程。这允许你使用 tailwindcss、mdsvex 等工具。

目前,可以配置以下内容:

Vite 选项

  • plugins: 要使用的 Vite 插件列表。

Svelte 选项

  • preprocess: 要使用的 Svelte 预处理器列表。
  • extensions: 要编译为 .svelte 文件的文件扩展名列表。
  • build.target: 构建目标,这可能对于支持较新的 JavaScript 功能是必要的。有关更多信息,请参阅 esbuild 文档

gradio.config.js 文件应放在组件的 frontend 目录的根目录中。当你创建新组件时,会为你创建一个默认配置文件。但你也可以创建自己的配置文件(如果不存在),并用它来自定义组件的构建过程。

Vite 插件示例

自定义组件可以使用 Vite 插件来定制构建过程。有关更多信息,请查看 Vite 文档

这里我们配置 TailwindCSS,这是一个实用优先的 CSS 框架。使用版本 4 预发布版设置最简单。

npm install tailwindcss@next @tailwindcss/vite@next

gradio.config.js

import tailwindcss from "@tailwindcss/vite";
export default {
    plugins: [tailwindcss()]
};

然后创建一个 style.css 文件,内容如下

@import "tailwindcss";

将此文件导入 Index.svelte。请注意,你需要导入包含 @import 的 CSS 文件,而不能只使用 <style> 标签并在其中使用 @import

<script lang="ts">
[...]
import "./style.css";
[...]
</script>

Svelte 选项示例

gradio.config.js 中,你还可以指定一些 Svelte 选项来应用于 Svelte 编译。在此示例中,我们将添加对 mdsvex 的支持,它是一个用于 Svelte 的 Markdown 预处理器。

为了实现这一点,我们需要在 gradio.config.jssvelte 对象中添加一个 Svelte 预处理器,并配置 extensions 字段。其他选项目前不支持。

首先,安装 mdsvex 插件

npm install mdsvex

然后将以下内容添加到 gradio.config.js

import { mdsvex } from "mdsvex";

export default {
    svelte: {
        preprocess: [
            mdsvex()
        ],
        extensions: [".svelte", ".svx"]
    }
};

现在我们可以在组件的 frontend 目录中创建 mdsvex 文档,它们将被编译成 .svelte 文件。

<!-- HelloWorld.svx -->

<script lang="ts">
    import { Block } from "@gradio/atoms";

    export let title = "Hello World";
</script>

<Block label="Hello World">

# {title}

This is a markdown file.

</Block>

然后我们可以在组件中使用 HelloWorld.svx 文件

<script lang="ts">
    import HelloWorld from "./HelloWorld.svx";
</script>

<HelloWorld />

总结

你现在知道如何为组件创建令人愉悦的前端了!