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应用程序传递下来。

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 应用开发者控制你的组件在用户界面中占据的空间。

  • loading_status 用于在组件作为事件输出时显示加载状态。

  • mode 是父级 Gradio 应用程序告诉你的组件应该显示 interactive 还是 static 版本的方式。

  • 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 文件应暴露以下属性:

    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: 所选值的当前索引。

  • 您的“非示例”组件接受的任何额外属性!

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

<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 值的数组到服务器。

这里有一个示例,展示了当元素的值发生变化时如何加载文件。

<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的属性。 这是由父级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文件,而不能仅仅使用

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

Svelte 选项示例

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

为了做到这一点,我们需要在gradio.config.js中的svelte对象中添加一个Svelte Preprocessor并配置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 />

结论

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