AI Component · Sidebar

Sidebar

Display configuration and detail information in AI scenarios

Usage Scenarios

The Sidebar is mainly used in AI scenarios for information display and feature configuration, including MCP configuration, reference sources, code preview, rich text preview and editing, etc.

Code Examples

How to import

<script lang="ts">
    import { Annotation, MCPConfigure, Sidebar } from '@fibrae/semi-ai-svelte';

    const { Container, CodeContent, CodeItem, FileContent, FileItem } = Sidebar;
    const { AnnotationContent } = Annotation;
</script>

Basic Container

The basic container of the Sidebar is the base container for MCP configuration, References and Sidebar.

  • bind:visible controls show/hide. Clicking the close button, or pressing Esc while closeOnEsc is true, sets it to false first and then calls onCancel
  • title sets the title, plain text or a snippet; use the header snippet to customize the whole header
  • motion sets whether there is an expand animation
  • resizable sets whether the width can be resized; when true, use minWidth and maxWidth to set the minimum and maximum width
是否有动画
是否可伸缩
基础容器示例
Code
<script lang="ts">
    import { Sidebar } from '@fibrae/semi-ai-svelte';
    import { Button, Switch } from '@fibrae/semi-ui-svelte';

    const { Container } = Sidebar;

    const containerStyle = 'display: flex; height: 500px; border: 1px solid var(--semi-color-border); border-radius: 8px; overflow: hidden; box-sizing: border-box';

    let visible = $state(true);
    let motion = $state(true);
    let resizable = $state(true);
</script>

<div style={containerStyle}>
    <div style="flex-grow: 1; flex-shrink: 1; padding: 20px">
        <div>
            <Button aria-pressed={visible} onclick={() => (visible = !visible)}>显示容器</Button>
            <div style="display: flex; align-items: center; margin-top: 10px">
                <span id="sidebar-container-motion">是否有动画</span>
                <Switch aria-labelledby="sidebar-container-motion" bind:checked={motion} />
            </div>
            <div style="display: flex; align-items: center; margin-top: 10px">
                <span id="sidebar-container-resizable">是否可伸缩</span>
                <Switch aria-labelledby="sidebar-container-resizable" bind:checked={resizable} />
            </div>
        </div>
    </div>
    <Container bind:visible maxWidth="60%" minWidth={250} {motion} {resizable} style={resizable ? undefined : 'width: 200px'} title="基础容器示例" />
</div>

MCP Configuration

Use MCPConfigure to display, enable/disable, configure and search MCP tools.

  • Use bind:options and bind:customOptions to bind the built-in and custom MCP tools; switching a tool updates the binding first, then calls onStatusChange
  • Use bind:visible to control show and hide, and onCancel to listen to user close behavior
  • Use onStatusChange to customize how enabling/disabling an MCP tool is handled
  • Use onAddClick to handle the add button of the custom MCP page
  • Use onConfigureClick to listen to configuring a built-in MCP tool, and onEditClick to listen to editing a custom one
  • Use the icon and desc snippets for the icon and description of an item, and item to replace the whole row; the snippet parameter is { item, index, custom }

For other APIs of MCPConfigure, see MCPConfigure.

The type of MCP tool item is as follows:

interface MCPConfigureOption {
    icon?: string; // image URL; use the icon snippet for other content
    label?: string;
    value: string; // required, identifies the tool and must be unique
    desc?: string; // plain text; use the desc snippet for other content
    active?: boolean;
    disabled?: boolean;
    configure?: boolean; // whether to show configure button
}

Usage example:

MCP 配置
已激活 MCP 数: 0/2
支持 Semi 的文档、源码搜索,辅助开发
Figma MCP Server 连接Figma与AI开发工具的功能。它通过标准化的模型上下文协MCP),将组件、变量等设计数据和上下文暴露给AI,从而实现从设计稿到代码的智能生成,显著提升开发效率。
Code
<script lang="ts">
    import { MCPConfigure, type MCPConfigureOption } from '@fibrae/semi-ai-svelte';
    import { IconFigma, IconSemiLogo } from '@fibrae/semi-icons-svelte';
    import { Button, Form, FormInput, FormTextArea, Modal, type FormApi } from '@fibrae/semi-ui-svelte';

    interface McpFormValues {
        name: string;
        src: string;
        desc: string;
    }

    const containerStyle = 'display: flex; height: 500px; border: 1px solid var(--semi-color-border); border-radius: 8px; overflow: hidden; box-sizing: border-box';

    // icon only takes an image URL, so the built-in options get their icons from the icon snippet
    const builtinIcons: Record<string, typeof IconFigma> = {
        'Semi mcp': IconSemiLogo,
        figma: IconFigma,
    };

    const srcRules = [
        { required: true, message: '请输入 MCP 图标 URL' },
        {
            validator: (_rule: unknown, value: string) => /^(?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+(?::\d+)?(?:\/[^\s]*)?$/i.test(value),
            message: '请输入有效的 MCP 图标 URL',
        },
    ];

    let index = 1;

    let visible = $state(true);
    let options = $state.raw<MCPConfigureOption[]>([
        {
            value: 'Semi mcp',
            label: 'Semi',
            configure: true,
            desc: '支持 Semi 的文档、源码搜索,辅助开发',
        },
        {
            value: 'figma',
            label: 'Figma',
            desc: 'Figma MCP Server 连接Figma与AI开发工具的功能。它通过标准化的模型上下文协MCP),将组件、变量等设计数据和上下文暴露给AI,从而实现从设计稿到代码的智能生成,显著提升开发效率。',
        },
    ]);
    let customOptions = $state.raw<MCPConfigureOption[]>([]);
    let modalVisible = $state(false);
    let formApi: FormApi<McpFormValues> | undefined;

    const handleOk = () => {
        formApi
            ?.validate()
            .then((values) => {
                customOptions = [
                    ...customOptions,
                    {
                        label: values.name,
                        icon: values.src,
                        value: `mcp-${index++}`,
                        desc: values.desc,
                    },
                ];
                modalVisible = false;
            })
            .catch((errors) => {
                console.log('errors', errors);
            });
    };

    const onConfigureClick = (_event: MouseEvent, option: MCPConfigureOption) => {
        console.log('configure click', option);
    };

    const onEditClick = (_event: MouseEvent, option: MCPConfigureOption) => {
        console.log('edit click', option);
    };
</script>

<div style={containerStyle}>
    <div style="flex-grow: 1; flex-shrink: 1; padding: 20px">
        <Button aria-pressed={visible} onclick={() => (visible = !visible)}>MCP 配置</Button>
    </div>
    <MCPConfigure bind:customOptions bind:options bind:visible onAddClick={() => (modalVisible = true)} {onConfigureClick} {onEditClick} resizable={false} style="width: 500px">
        {#snippet icon({ item })}
            {#if item.icon}
                <img alt="" src={item.icon} style="width: 100%; height: 100%" />
            {:else}
                {@const Icon = builtinIcons[item.value]}
                <Icon />
            {/if}
        {/snippet}
    </MCPConfigure>
    <Modal bind:visible={modalVisible} closeOnEsc onOk={handleOk} title="自定义 MCP">
        <Form getFormApi={(api) => (formApi = api)} layout="vertical" onValueChange={(values) => console.log(values)}>
            <FormInput field="name" label="MCP 名称" rules={[{ required: true, message: '请输入 MCP 名称' }]} style="width: 100%" />
            <FormInput field="src" initValue="/assets/placeholder/avatar-a.svg" label="MCP 图标 URL" rules={srcRules} style="width: 100%" />
            <FormTextArea field="desc" label="MCP 介绍" rules={[{ required: true, message: '请输入 MCP 介绍' }]} style="width: 100%" />
        </Form>
    </Modal>
</div>

References

Use the Annotation component to manage the display of reference sources.

  • bind:activeKey manages the currently expanded item
  • info sets the reference content list
  • onClick is called when a reference is clicked, with the event and its item
  • Use the infoHeader snippet for a group header and item to replace an item; header is the whole container header
Code
<script lang="ts">
    import { Annotation, type AnnotationInfo, type AnnotationItem } from '@fibrae/semi-ai-svelte';
    import { Button } from '@fibrae/semi-ui-svelte';

    const containerStyle = 'display: flex; height: 500px; border: 1px solid var(--semi-color-border); border-radius: 8px; overflow: hidden; box-sizing: border-box';

    const logo = '/assets/placeholder/avatar-a.svg';
    const img = '/assets/root-web-sites/abstract.jpg';

    const defaultInfoList: AnnotationInfo[] = [
        {
            header: 'Semi design introduction',
            key: '1',
            annotations: [
                {
                    order: 1,
                    type: 'video',
                    duration: 4432,
                    title: 'Semi Design is a design system designed',
                    url: 'https://semi.design/',
                    logo,
                    siteName: 'Semi Design',
                    detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
                    img,
                },
                {
                    order: 2,
                    title: 'Quick start',
                    type: 'video',
                    duration: 56,
                    url: 'https://semi.design/',
                    detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
                    logo,
                    siteName: 'Semi Design',
                    img,
                },
                {
                    order: 3,
                    title: 'Use components in a modular way',
                    url: 'https://semi.design/',
                    detail: `Semi provides esm format dist, and the css of the component is only imported by the corresponding js.
When used in Webpack, Rspack, create-react-app or Vite projects, there is no need to configure any compilation items.`,
                    logo,
                    siteName: 'Semi Design',
                    img,
                },
            ],
        },
        {
            header: 'Design resource',
            key: '2',
            annotations: [
                {
                    order: 2,
                    title: 'Semi Design resource',
                    url: 'https://semi.design/',
                    detail: 'Semi Design provides a wealth of design resources to help designers and developers collaborate efficiently. Whether you are a community user or a ByteDance internal designer, you can find UI Kit resource and Figma plug-ins that suit you here.',
                    logo,
                    siteName: 'Semi Design resource',
                    img,
                },
            ],
        },
    ];

    const defaultSize = { width: 420 };
    let visible = $state(true);
    let activeKey = $state<string | string[]>('1');

    const onChange = (key: string | string[]) => {
        console.log('change', key);
    };

    const onClick = (event: MouseEvent, item: AnnotationItem) => {
        console.log('click', event, item);
    };
</script>

<div style={containerStyle}>
    <div style="flex-grow: 1; flex-shrink: 1; padding: 20px">
        <Button aria-pressed={visible} onclick={() => (visible = !visible)}>参考来源</Button>
    </div>
    <Annotation bind:activeKey bind:visible {defaultSize} info={defaultInfoList} {onChange} {onClick} />
</div>

Code Display

You can use CodeItem in Sidebar to display code. CodeItem is built based on JsonViewer and CodeHighlight.

  • content: content string
  • isJson: whether the content is json
  • language: programming language name, same as language in CodeHighlight; register it with CodeHighlight.register first
  • jsonViewerProps: configure other JsonViewer props
  • codeHighlightProps: configure other CodeHighlight props

The value of JsonViewer in jsonViewerProps defaults to content, and both jsonViewerProps and codeHighlightProps accept class and style. The custom rendering and search button of JsonViewer are available through the customRender and searchButton snippets of CodeItem.

Code
<script lang="ts">
    import { Sidebar } from '@fibrae/semi-ai-svelte';
    import { Radio, RadioGroup, type RadioValue } from '@fibrae/semi-ui-svelte';

    const { CodeItem } = Sidebar;

    const code = `.semi-animation-react-demo-auto {
    button {
        height: 50px;
        border: 0;
        cursor: pointer !important;
        background: #777;
        color: white;
        outline: none;
        -webkit-appearance: none;
    }

    button:hover {
        background: #878787;
    }

    .auto-main {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: auto 1fr;
        background: #575757;
    }

    .content {
        grid-column: span 3;
    }

    .item {
        background: indianred;
        width: 100%;
        overflow: hidden;
        color: white;
    }

    .item p {
        margin: 0;
        padding: 10px;
    }
}
`;

    const json = `{
    "axisX": {
        "title": {
            "visible": false,
            "position": "center"
        },
        "label": {
            "visible": true,
            "style": {
                "fontSize": 12,
                "fontWeight": 400,
                "lineHeight": 16,
                "fontFamily": [
                    "Inter"
                ],
                "fill": "rgba(0, 0, 0, 0.47843137254901963)"
            },
            "space": 12
        }
    }
}`;

    const codeProps = {
        content: code,
        language: 'css',
    };

    const jsonProps = {
        isJson: true,
        content: json,
    };

    let type = $state<RadioValue | undefined>('json');
    const itemProps = $derived(type === 'json' ? jsonProps : codeProps);
</script>

<RadioGroup bind:value={type} buttonSize="middle" style="margin-bottom: 10px" type="button">
    <Radio value="json">JSON</Radio>
    <Radio value="css">CSS</Radio>
</RadioGroup>
<div style="height: 200px; overflow: auto">
    <CodeItem {...itemProps} />
</div>

Code List

You can use CodeContent in Sidebar to display a list of code blocks.

  • Use bind:activeKey to manage the expanded panels
  • Use onExpand to listen to the expand button in a panel header; the button is shown only when it is set
  • The customRender and searchButton snippets are forwarded to the JsonViewer of every JSON panel
import React from 'react';
import { AutoComplete } from '@fibrae/semi-ui-react';
import { IconSearch } from '@fibrae/semi-icons-react';

() => {
    const [stringData, setStringData] = useState([]);
    const [value, setValue] = useState('');
    const handleStringSearch = (value) => {
        let result;
        if (value) {
            result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
        } else {
            result = [];
        }
        setStringData(result);
    };

    const handleChange = (value) => {
        console.log('onChange', value);
        setValue(value);
    };
    return (
        <AutoComplete
            data={stringData}
            value={value}
            showClear
            prefix={<IconSearch />}
            placeholder="搜索... "
            onSearch={handleStringSearch}
            onChange={handleChange}
            style={{ width: 200 }}
        />
    );
};
Code
<script lang="ts">
    import { Sidebar } from '@fibrae/semi-ai-svelte';

    const { CodeContent } = Sidebar;

    const defaultCodes = [
        {
            name: 'Component.js',
            key: 'code1',
            language: 'javascript',
            content: `import React from 'react';
import { AutoComplete } from '@fibrae/semi-ui-react';
import { IconSearch } from '@fibrae/semi-icons-react';

() => {
    const [stringData, setStringData] = useState([]);
    const [value, setValue] = useState('');
    const handleStringSearch = (value) => {
        let result;
        if (value) {
            result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
        } else {
            result = [];
        }
        setStringData(result);
    };

    const handleChange = (value) => {
        console.log('onChange', value);
        setValue(value);
    };
    return (
        <AutoComplete
            data={stringData}
            value={value}
            showClear
            prefix={<IconSearch />}
            placeholder="搜索... "
            onSearch={handleStringSearch}
            onChange={handleChange}
            style={{ width: 200 }}
        />
    );
};
`,
        },
        {
            name: 'Style.css',
            key: 'code2',
            language: 'css',
            content: `.semi-animation-react-demo-auto {
    button {
        height: 50px;
        border: 0;
        cursor: pointer !important;
        background: #777;
        color: white;
        outline: none;
        -webkit-appearance: none;
    }

    button:hover {
        background: #878787;
    }

    .auto-main {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: auto 1fr;
        background: #575757;
    }

    .content {
        grid-column: span 3;
    }

    .item {
        background: indianred;
        width: 100%;
        overflow: hidden;
        color: white;
    }

    .item p {
        margin: 0;
        padding: 10px;
    }
}
`,
        },
        {
            name: 'Chart.json',
            key: 'code3',
            isJson: true,
            language: 'html',
            content: `{
    "axisX": {
        "title": {
            "visible": false,
            "position": "center"
        },
        "label": {
            "visible": true,
            "style": {
                "fontSize": 12,
                "fontWeight": 400,
                "lineHeight": 16,
                "fontFamily": [
                    "Inter"
                ],
                "fill": "rgba(0, 0, 0, 0.47843137254901963)"
            },
            "space": 12
        },
        "domainLine": {
            "visible": true,
            "style": {
                "lineWidth": 1,
                "stroke": "rgba(0, 0, 0, 0.12156862745098039)",
                "lineDash": []
            }
        },
        "tick": {
            "visible": false,
            "style": {
                "lineWidth": 1,
                "stroke": "rgba(255, 255, 255, 0)"
            }
        },
        "subTick": {
            "visible": false
        },
        "grid": {
            "visible": false
        },
        "subGrid": {
            "visible": false
        }
    }
}`,
        },
    ];

    let activeKey = $state<string | string[]>('code1');
</script>

<div class="semi-sidebar-main">
    <CodeContent bind:activeKey codes={defaultCodes} style="width: 600px" />
</div>

Rich Text Editor

You can use FileItem in Sidebar to view and edit rich text content. FileItem is implemented based on tiptap.

  • content: rich text as HTML. A change after mount is synced to the editor; it is not applied when it equals what the editor already holds, so typing and the cursor are not interrupted. The sync does not call onContentChange
  • editable: whether the content is editable; a change after mount is synced to the editor
  • imgUploadProps: configure image upload endpoint and how to set the src after upload. Use action to set upload url and getUploadImageSrc(response) to return the uploaded image url from the upload response (when omitted, a string response is used as the url, otherwise response.src), which will be used as src of img node in rich text; the on callbacks of Upload (such as onSuccess) can be passed here too. The toolbar shows the image button only when action or customRequest is set
  • extensions and imgUploadProps are read only when the editor is created; use {#key} to remount it with new values
  • onContentChange is called after an edit, with the content as HTML


Code
<script lang="ts">
    import { Sidebar, type ImageUploadNodeOptions } from '@fibrae/semi-ai-svelte';
    import { Button } from '@fibrae/semi-ui-svelte';

    const { FileItem } = Sidebar;

    const content = `<h2>
  Semi Design 介绍
</h2>
<p>
  Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
  <li>
    设计简洁、现代化。
  </li>
  <li>
    提供主题方案,可深度样式定制。
  </li>
</ul>
<p>
  国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
  display: none;
}</code></pre>
<p>
  采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
  Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
  <br />
  — Semi Design
</blockquote>`;

    const imgUploadProps: ImageUploadNodeOptions = {
        action: 'https://api.semi.design/upload',
        // response is the action's result; real code reads the uploaded image URL from it
        getUploadImageSrc: () => '/assets/placeholder/avatar-a.svg',
    };

    let editable = $state(true);
</script>

<Button aria-pressed={editable} onclick={() => (editable = !editable)}>可编辑</Button>
<br />
<br />
<FileItem {content} {editable} {imgUploadProps} name="Semi Design 介绍" style="border: 1px solid var(--semi-color-border); padding: 12px" />

Rich Text List

You can use FileContent in Sidebar to display a list of rich text documents.

  • Use bind:activeKey to manage the expanded panels
  • Use onExpand to listen to the expand button in a panel header; the button is shown only when it is set
Code
<script lang="ts">
    import { Sidebar } from '@fibrae/semi-ai-svelte';

    const { FileContent } = Sidebar;

    const intro = `<h2>
  Semi Design 介绍
</h2>
<p>
  Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
  <li>
    设计简洁、现代化。
  </li>
  <li>
    提供主题方案,可深度样式定制。
  </li>
</ul>
<p>
  国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
  display: none;
}</code></pre>
<p>
  采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
  Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
  <br />
  — Semi Design
</blockquote>
`;

    const defaultFiles = [
        {
            key: 'file1',
            name: 'Semi Design 介绍',
            content: `\n${intro}${intro}${intro}`,
        },
        {
            key: 'file2',
            name: 'Semi Design DSM',
            content: `<h2>
  Semi Design DSM
</h2>
<p>Semi DSM is a design system management tool provided by Semi Design. It supports global and component-level style customization and keeps synchronization between Figma and front-end code. Suitable for teams of all sizes. Whether you need to simplify workflow, improve team collaboration, or increase productivity, we have features suitable for you.</p>
`,
        },
        {
            key: 'file3',
            name: 'React v19 Adaptation',
            content: `<h2>
  React v19 Adaptation
</h2>
<p>Since the release of React v19, React has introduced numerous underlying mechanism and API changes, including upgrades and adjustments to the render mechanism, ref, context, TypeScript types, and related deprecated APIs. To ensure that the Semi Design component library is smoothly compatible with both React v19 and lower versions, we provide the original component package @fibrae/semi-ui-react for React versions below v19, as well as a new package @fibrae/semi-ui-19 specifically adapted for React v19, so users can choose as needed. This guide will help you understand how to install, use, and the precautions to take.
Installation & Usage
If your project is using React v19, please use @fibrae/semi-ui-19. For React versions below v19, continue using @fibrae/semi-ui-react as before.</p>
`,
        },
    ];

    let activeKey = $state<string | string[]>('file1');
</script>

<div class="semi-sidebar-main">
    <FileContent bind:activeKey files={defaultFiles} style="width: 600px" />
</div>

The Sidebar has a main view (mode is main) and detail views (mode is code, file and other custom values).

In the main view you can configure the title through title, decide whether to show close button through showClose, and configure the top button group through options (each item is { name, key }); bind:activeKey binds the selected item. Use the optionIcon snippet (parameter { item, index }) for the button icon, or optionItem to replace the whole button, whose onClick parameter selects the item.

For content rendering in the main view, use the mainContent snippet (parameter { activeKey }) and render by the selected item. You can use AnnotationContent in Annotation to render references, FileContent in Sidebar to render rich text, and CodeContent to render code.

For detail views, if the content is rich text or code, set mode to code or file and provide detailContent to display the content with built-in code display component / rich text editor; a new key in detailContent remounts it. If you want to customize how the detail view is rendered, use the detailBody snippet (parameter { mode }), and customize the detail header through the detailHeader snippet (parameter { mode, detailContent }); with empty content, both fall back to the built-in ones. Clicking the back button of the detail header calls onBackWard.

工作区占位
Agent 的工作空间
import React from 'react';
import { AutoComplete } from '@fibrae/semi-ui-react';
import { IconSearch } from '@fibrae/semi-icons-react';

() => {
    const [stringData, setStringData] = useState([]);
    const [value, setValue] = useState('');
    const handleStringSearch = (value) => {
        let result;
        if (value) {
            result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
        } else {
            result = [];
        }
        setStringData(result);
    };

    const handleChange = (value) => {
        console.log('onChange', value);
        setValue(value);
    };
    return (
        <AutoComplete
            data={stringData}
            value={value}
            showClear
            prefix={<IconSearch />}
            placeholder="搜索... "
            onSearch={handleStringSearch}
            onChange={handleChange}
            style={{ width: 200 }}
        />
    );
};
Code
<script lang="ts">
    import { Annotation, Sidebar, type AnnotationInfo, type ImageUploadNodeOptions, type SideBarDetailContent } from '@fibrae/semi-ai-svelte';
    import { IconBriefStroked, IconCodeStroked, IconModalStroked, IconSearch } from '@fibrae/semi-icons-svelte';

    const { FileContent, CodeContent } = Sidebar;
    const { AnnotationContent } = Annotation;

    const containerStyle = 'display: flex; height: 800px; border: 1px solid var(--semi-color-border); border-radius: 8px; overflow: hidden; box-sizing: border-box';

    const optionList = [
        {
            name: '查看搜索',
            key: 'searchResult',
        },
        {
            name: '文件预览',
            key: 'filePreview',
        },
        {
            name: '代码预览',
            key: 'codePreview',
        },
        {
            name: '浏览器',
            key: 'network',
        },
    ];

    const optionIcons: Record<string, typeof IconSearch> = {
        searchResult: IconSearch,
        filePreview: IconBriefStroked,
        codePreview: IconCodeStroked,
        network: IconModalStroked,
    };

    const defaultCodes = [
        {
            name: 'Component.js',
            key: 'code1',
            language: 'javascript',
            content: `import React from 'react';
import { AutoComplete } from '@fibrae/semi-ui-react';
import { IconSearch } from '@fibrae/semi-icons-react';

() => {
    const [stringData, setStringData] = useState([]);
    const [value, setValue] = useState('');
    const handleStringSearch = (value) => {
        let result;
        if (value) {
            result = ['gmail.com', '163.com', 'qq.com'].map(domain => value+domain);
        } else {
            result = [];
        }
        setStringData(result);
    };

    const handleChange = (value) => {
        console.log('onChange', value);
        setValue(value);
    };
    return (
        <AutoComplete
            data={stringData}
            value={value}
            showClear
            prefix={<IconSearch />}
            placeholder="搜索... "
            onSearch={handleStringSearch}
            onChange={handleChange}
            style={{ width: 200 }}
        />
    );
};
`,
        },
        {
            name: 'Style.css',
            key: 'code2',
            language: 'css',
            content: `.semi-animation-react-demo-auto {
    button {
        height: 50px;
        border: 0;
        cursor: pointer !important;
        background: #777;
        color: white;
        outline: none;
        -webkit-appearance: none;
    }

    button:hover {
        background: #878787;
    }

    .auto-main {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        grid-template-rows: auto 1fr;
        background: #575757;
    }

    .content {
        grid-column: span 3;
    }

    .item {
        background: indianred;
        width: 100%;
        overflow: hidden;
        color: white;
    }

    .item p {
        margin: 0;
        padding: 10px;
    }
}
`,
        },
        {
            name: 'Chart.json',
            key: 'code3',
            isJson: true,
            language: 'html',
            content: `{
    "axisX": {
        "title": {
            "visible": false,
            "position": "center"
        },
        "label": {
            "visible": true,
            "style": {
                "fontSize": 12,
                "fontWeight": 400,
                "lineHeight": 16,
                "fontFamily": [
                    "Inter"
                ],
                "fill": "rgba(0, 0, 0, 0.47843137254901963)"
            },
            "space": 12
        },
        "domainLine": {
            "visible": true,
            "style": {
                "lineWidth": 1,
                "stroke": "rgba(0, 0, 0, 0.12156862745098039)",
                "lineDash": []
            }
        },
        "tick": {
            "visible": false,
            "style": {
                "lineWidth": 1,
                "stroke": "rgba(255, 255, 255, 0)"
            }
        },
        "subTick": {
            "visible": false
        },
        "grid": {
            "visible": false
        },
        "subGrid": {
            "visible": false
        }
    }
}`,
        },
    ];

    const intro = `<h2>
  Semi Design 介绍
</h2>
<p>
  Semi Design 是由 <strong>抖音前端团队</strong>和MED产品设计团队设计、开发并维护的<em>设计系统</em>。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户,详情见https://semi.design/zh-CN/start/introduction。Semi Design的特点包括:
</p>
<ul>
  <li>
    设计简洁、现代化。
  </li>
  <li>
    提供主题方案,可深度样式定制。
  </li>
</ul>
<p>
  国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
</p>
<pre><code class="language-css">body {
  display: none;
}</code></pre>
<p>
  采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
</p>
<blockquote>
  Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
  <br />
  — Semi Design
</blockquote>
`;

    const defaultFiles = [
        {
            key: 'file1',
            name: 'Semi Design 介绍',
            content: `\n${intro}${intro}${intro}`,
        },
        {
            key: 'file2',
            name: 'Semi Design DSM',
            content: `<h2>
  Semi Design DSM
</h2>
<p>Semi DSM is a design system management tool provided by Semi Design. It supports global and component-level style customization and keeps synchronization between Figma and front-end code. Suitable for teams of all sizes. Whether you need to simplify workflow, improve team collaboration, or increase productivity, we have features suitable for you.</p>
`,
        },
        {
            key: 'file3',
            name: 'React v19 Adaptation',
            content: `<h2>
  React v19 Adaptation
</h2>
<p>Since the release of React v19, React has introduced numerous underlying mechanism and API changes, including upgrades and adjustments to the render mechanism, ref, context, TypeScript types, and related deprecated APIs. To ensure that the Semi Design component library is smoothly compatible with both React v19 and lower versions, we provide the original component package @fibrae/semi-ui-react for React versions below v19, as well as a new package @fibrae/semi-ui-19 specifically adapted for React v19, so users can choose as needed. This guide will help you understand how to install, use, and the precautions to take.
Installation & Usage
If your project is using React v19, please use @fibrae/semi-ui-19. For React versions below v19, continue using @fibrae/semi-ui-react as before.</p>
`,
        },
    ];

    const logo = '/assets/placeholder/avatar-a.svg';
    const img = '/assets/root-web-sites/abstract.jpg';

    const defaultInfoList: AnnotationInfo[] = [
        {
            header: 'Semi design introduction',
            key: '1',
            annotations: [
                {
                    order: 1,
                    type: 'video',
                    duration: 4432,
                    title: 'Semi Design is a design system designed',
                    url: 'https://semi.design/',
                    logo,
                    siteName: 'Semi Design',
                    detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
                    img,
                },
                {
                    order: 2,
                    title: 'Quick start',
                    type: 'video',
                    duration: 56,
                    url: 'https://semi.design/',
                    detail: ' As a comprehensive, easy-to-use, and high-quality modern enterprise-level application UI solution',
                    logo,
                    siteName: 'Semi Design',
                    img,
                },
                {
                    order: 3,
                    title: 'Use components in a modular way',
                    url: 'https://semi.design/',
                    detail: `Semi provides esm format dist, and the css of the component is only imported by the corresponding js.
When used in Webpack, Rspack, create-react-app or Vite projects, there is no need to configure any compilation items.`,
                    logo,
                    siteName: 'Semi Design',
                    img,
                },
            ],
        },
        {
            header: 'Design resource',
            key: '2',
            annotations: [
                {
                    order: 2,
                    title: 'Semi Design resource',
                    url: 'https://semi.design/',
                    detail: 'Semi Design provides a wealth of design resources to help designers and developers collaborate efficiently. Whether you are a community user or a ByteDance internal designer, you can find UI Kit resource and Figma plug-ins that suit you here.',
                    logo,
                    siteName: 'Semi Design resource',
                    img,
                },
            ],
        },
    ];

    const imgUploadProps: ImageUploadNodeOptions = {
        action: 'https://api.semi.design/upload',
        // response is the action's result; real code reads the uploaded image URL from it
        getUploadImageSrc: () => logo,
    };

    const defaultSize = { width: '60%' };
    let mode = $state('main');
    let activeKey = $state('codePreview');
    let activeCodeKey = $state<string | string[]>('code1');
    let activeFileKey = $state<string | string[]>('file1');
    let activeReferKey = $state<string | string[]>('1');
    let currentDetail = $state.raw<SideBarDetailContent>();

    const onExpand = (_event: MouseEvent, content: SideBarDetailContent, expandMode: string) => {
        mode = expandMode;
        currentDetail = content;
    };
</script>

<div style={containerStyle}>
    <div style="flex-grow: 1; flex-shrink: 1; padding: 20px">工作区占位</div>
    <Sidebar bind:activeKey {defaultSize} detailContent={currentDetail} {imgUploadProps} {mode} motion={false} onBackWard={(_event, backMode) => (mode = backMode)} options={optionList} showClose={false} title="Agent 的工作空间" visible>
        {#snippet optionIcon({ item })}
            {@const Icon = optionIcons[item.key]}
            <Icon />
        {/snippet}
        {#snippet mainContent({ activeKey: currentKey })}
            {#if currentKey === 'searchResult'}
                <AnnotationContent bind:activeKey={activeReferKey} info={defaultInfoList} />
            {:else if currentKey === 'filePreview'}
                <FileContent bind:activeKey={activeFileKey} files={defaultFiles} {onExpand} />
            {:else if currentKey === 'codePreview'}
                <CodeContent bind:activeKey={activeCodeKey} codes={defaultCodes} {onExpand} />
            {:else if currentKey === 'network'}
                <img alt="network" src="/assets/root-web-sites/test.jpg" style="width: 100%" />
            {/if}
        {/snippet}
    </Sidebar>
</div>

API Reference

The class, style and other undeclared attributes (such as aria-label) of Sidebar, Annotation, MCPConfigure and Container land on the .semi-sidebar-container element; when the root is named (aria-label or aria-labelledby) and no role is given, its role is group. Undeclared attributes of CodeContent, CodeItem, FileContent, FileItem and AnnotationContent land on their own root elements.

Sidebar, Annotation, MCPConfigure and Sidebar.Container expose getRoot() through export function; get the instance with bind:this and call it. It returns HTMLDivElement | null, and null while the container is hidden. The type of Container is SidebarContainerHandle (exported from @fibrae/semi-ai-svelte).

A snippet written inside a component tag becomes a prop of that component by its name, so only names the component declares, such as optionIcon and mainContent, can go inside the tag; declare your own helper snippets outside the component tag.

Container

Props

PropertyDescriptionTypeDefault
afterVisibleChangeCalled after the show or hide animation ends, with whether the container is visible.Type(visible: boolean) => void-
childrenThe content area of the container.Snippet-
closeOnEscWhether pressing Esc calls onCancel; off by default.booleanfalse
defaultSizeInitial width and height; applies only when resizable is true.{ width?: number | string; height?: number | string }-
headerReplaces the whole header, the title and close button included.Snippet-
maxWidthMaximum width when resizing; applies only when resizable is true.number | string-
minWidthMinimum width when resizing; applies only when resizable is true.number | string150
motionWhether showing and hiding use the expand animation.booleantrue
onCancelCalled when the close button is clicked, or when Esc is pressed while closeOnEsc is true.Type(event: MouseEvent | KeyboardEvent) => void-
onintroendNative introend listener, run alongside the component's own handling of the animation end.TypeEventHandler<CustomEvent<null>, T> | null-
onoutroendNative outroend listener, run alongside the component's own handling of the animation end.TypeEventHandler<CustomEvent<null>, T> | null-
resizableWhether the width can be resized by dragging; minWidth, maxWidth and defaultSize apply only when true.booleantrue
showCloseWhether the header shows the close button.booleantrue
titleTitle, either plain text or a snippet.Snippet | string-
visibleWhether the container is shown; bind it with bind:visible. When the close button or Esc cancels, false is written first, then onCancel is called.boolean-

Methods

MethodDescriptionType
getRootReturns the .semi-sidebar-container root element; null while hidden.Type() => HTMLDivElement | null

MCPConfigure

Props

PropertyDescriptionTypeDefault
afterVisibleChangeCalled after the show or hide animation ends, with whether it is visible.Type(visible: boolean) => void-
closeOnEscWhether pressing Esc calls onCancel.boolean-
customOptionsCustom MCP tools; bind it with bind:customOptions. Switching a tool writes the new list back before onStatusChange is called.MCPConfigureOption[]-
defaultSizeInitial width and height; applies only when resizable is true.{ width?: number | string; height?: number | string }-
descDescription of an option, with item, index and custom; the desc field is shown when absent.TypeSnippet<[MCPConfigureItemSnippetProps]>-
filterCustom search filter, called with the lower-cased input and an option; the label and desc are matched by default.Type(inputValue: string, option: MCPConfigureOption) => boolean-
headerReplaces the whole header, the title and close button included.Snippet-
iconIcon of an option, with item, index and custom. When provided it is used for every option; the icon field is an image URL only when it is absent.TypeSnippet<[MCPConfigureItemSnippetProps]>-
itemReplaces the whole built-in row, with item, index and custom.TypeSnippet<[MCPConfigureItemSnippetProps]>-
maxWidthMaximum width when resizing; applies only when resizable is true.number | string-
minWidthMinimum width when resizing; applies only when resizable is true.number | string-
motionWhether showing and hiding use the expand animation.boolean-
onAddClickCalled when the add button of the custom page is clicked.Type(event: MouseEvent) => void-
onCancelCalled when the close button is clicked, or when Esc is pressed while closeOnEsc is true.Type(event: MouseEvent | KeyboardEvent) => void-
onConfigureClickCalled when the configure button is clicked, with the event and its option.Type(event: MouseEvent, option: MCPConfigureOption) => void-
onEditClickCalled when the edit button of a custom tool is clicked, with the event and its option.Type(event: MouseEvent, option: MCPConfigureOption) => void-
onSearchCalled when the search input changes, with the input value and whether the custom list is shown.Type(inputValue: string, custom: boolean) => void-
onStatusChangeCalled after a tool is switched, with the list whose active flag changed and whether it is the custom list.Type(options: MCPConfigureOption[], custom: boolean) => void-
optionsBuilt-in MCP tools; bind it with bind:options. Each item needs a unique value. Switching a tool writes the new list back before onStatusChange is called.MCPConfigureOption[]-
placeholderPlaceholder of the search input, the locale text by default.string-
resizableWhether the width can be resized by dragging; minWidth, maxWidth and defaultSize apply only when true.boolean-
showCloseWhether the header shows the close button.boolean-
titleTitle, plain text or a snippet; the locale's MCP configure text by default.Snippet | string-
visibleWhether it is shown; bind it with bind:visible.boolean-

Methods

MethodDescriptionType
getRootReturns the .semi-sidebar-container root element; null while hidden.Type() => any

MCPConfigureOption

PropertyDescriptionTypeDefault
activeWhether this MCP is activebooleanfalse
configureWhether to show the configure buttonbooleanfalse
descDescription text, matched by the built-in filter; use the desc snippet for other contentstring-
disabledWhether this MCP is disabled. When true, users cannot toggle its active statebooleanfalse
iconImage URL of the icon; use the icon snippet for other contentstring-
labelLabel textstring-
valueValue, required. Identifies the option in onStatusChange and is used as the item key, so it must be unique in a liststring-

Annotation

Props

PropertyDescriptionTypeDefault
activeKeyKeys of the expanded groups; bind it with bind:activeKey.string | string[]-
afterVisibleChangeCalled after the show or hide animation ends, with whether it is visible.Type(visible: boolean) => void-
closeOnEscWhether pressing Esc calls onCancel.boolean-
defaultSizeInitial width and height; applies only when resizable is true.{ width?: number | string; height?: number | string }-
headerReplaces the whole header, the title and close button included.Snippet-
infoGroups of references; each has a header, a key and its annotations.AnnotationInfo[]-
infoHeaderContent of a group header after its icon, with item and index; the header field is shown when absent. The container's header replaces the whole header, so the group header is named infoHeader.TypeSnippet<[AnnotationHeaderSnippetProps]>-
itemReplaces the built-in reference item, with item and index (its position in the group).TypeSnippet<[AnnotationItemSnippetProps]>-
maxWidthMaximum width when resizing; applies only when resizable is true.number | string-
minWidthMinimum width when resizing; applies only when resizable is true.number | string-
motionWhether showing and hiding use the expand animation.boolean-
onCancelCalled when the close button is clicked, or when Esc is pressed while closeOnEsc is true.Type(event: MouseEvent | KeyboardEvent) => void-
onChangeCalled when a group opens or closes, with the expanded keys.Type(activeKey: string | string[]) => void-
onClickCalled when a reference is clicked, with the event and its item; the native click of the root is not reported apart from it.Type(event: MouseEvent, item: AnnotationItem) => void-
resizableWhether the width can be resized by dragging; minWidth, maxWidth and defaultSize apply only when true.boolean-
showCloseWhether the header shows the close button.boolean-
titleTitle, plain text or a snippet; the locale text by default.Snippet | string-
visibleWhether it is shown; bind it with bind:visible.boolean-

Methods

MethodDescriptionType
getRootReturns the .semi-sidebar-container root element; null while hidden.Type() => any

AnnotationContent

Props

PropertyDescriptionTypeDefault
activeKeyKeys of the expanded groups; bind it with bind:activeKey.string | string[]-
headerContent of a group header after its icon, with item and index; the header field is shown when absent.TypeSnippet<[AnnotationHeaderSnippetProps]>-
infoGroups of references; each has a header, a key and its annotations.AnnotationInfo[][]
itemReplaces the built-in reference item, with item and index (its position in the group).TypeSnippet<[AnnotationItemSnippetProps]>-
onChangeCalled when a group opens or closes, with the expanded keys.Type(activeKey: string | string[]) => void-
onClickCalled when a reference is clicked, with the event and its item; the native click of the root is not reported apart from it.Type(event: MouseEvent, item: AnnotationItem) => void-

AnnotationInfo

PropertyDescriptionTypeDefault
annotationsReference items of the groupAnnotationItem[]-
headerPlain-text group title; customize it with the infoHeader (Annotation) or header (AnnotationContent) snippetstring-
keyUnique key of the groupstring-

AnnotationItem

PropertyDescriptionTypeDefault
detailExtra detail text such as video description or text notestring-
durationDuration, usually used for videos, in secondsnumber-
imgImage url, such as video cover or illustrationstring-
logoLogo url of the site/platform to which the content belongsstring-
orderOrder/index numbernumber-
siteNameName of the site/platform where the content belongsstring-
titleTitlestring-
typeContent type'video' | 'text'-
urlResource url, opened in a new tab. An item without it renders a link without href that only the pointer can activatestring-

Props

PropertyDescriptionTypeDefault
activeKeyKey of the selected option in the main view; bind it with bind:activeKey.string-
afterVisibleChangeCalled after the show or hide animation ends, with whether it is visible.Type(visible: boolean) => void-
closeOnEscWhether pressing Esc calls onCancel.boolean-
customRenderForwarded to the customRender snippet of the JsonViewer in the built-in code detail, for the content matched by customRenderRule.TypeSnippet<[JsonViewerCustomRenderArgs]>-
defaultSizeInitial width and height; applies only when resizable is true.{ width?: number | string; height?: number | string }-
detailBodyContent in detail modes, with mode; empty content falls back to the built-in code or file detail. detailContent is already the data prop, so this is named detailBody.TypeSnippet<[SideBarDetailBodySnippetProps]>-
detailContentThe item shown in code and file modes: the CodeItem or FileItem props plus a key; a new key remounts it. In file mode its editable and imgUploadProps have no effect, as fileEditable and imgUploadProps decide them.SideBarDetailContent-
detailHeaderHeader in detail modes, with mode and detailContent; empty content falls back to the built-in header.TypeSnippet<[SideBarDetailHeaderSnippetProps]>-
fileEditableWhether the rich text in file mode is editable.booleantrue
imgUploadPropsImage upload options of file mode, read when the editor is created; later changes have no effect.ImageUploadNodeOptions-
mainContentContent below the options in the main view, with activeKey.TypeSnippet<[SideBarMainContentSnippetProps]>-
maxWidthMaximum width when resizing; applies only when resizable is true.number | string-
minWidthMinimum width when resizing; applies only when resizable is true.number | string-
modeDisplay mode: main is the main view, code and file use the built-in details, and any other string is rendered by the detailBody snippet.string"main"
motionWhether showing and hiding use the expand animation.boolean-
onActiveOptionChangeCalled after activeKey took the clicked option, with the event and the option's key.Type(event: MouseEvent, activeKey: string) => void-
onBackWardCalled when the back button of the detail header is clicked, with the event and main.Type(event: MouseEvent, mode: string) => void-
onCancelCalled when the close button is clicked, or when Esc is pressed while closeOnEsc is true.Type(event: MouseEvent | KeyboardEvent) => void-
onDetailContentCopyCalled after the copy button wrote to the clipboard, with the event, the content and whether it succeeded.Type(event: MouseEvent, content: string, success: boolean) => void-
onFileContentChangeCalled after an edit in file mode, with the content as HTML.Type(content: string) => void-
optionIconIcon of the built-in option button, with item and index.TypeSnippet<[SideBarOptionSnippetProps]>-
optionItemReplaces the whole option button, with item, index, active and onClick (which selects the option).TypeSnippet<[SideBarOptionItemSnippetProps]>-
optionsOption buttons at the top of the main view, each with a name and a key; icons use the optionIcon snippet.SideBarOption[]-
resizableWhether the width can be resized by dragging; minWidth, maxWidth and defaultSize apply only when true.boolean-
searchButtonForwarded to the searchButton snippet of the JsonViewer in the built-in code detail, replacing its search button.TypeSnippet<[JsonViewerSearchButtonArgs]>-
showCloseWhether the header shows the close button.boolean-
titleTitle, plain text or a snippet.Snippet | string-
visibleWhether it is shown; bind it with bind:visible.boolean-

Methods

MethodDescriptionType
getRootReturns the .semi-sidebar-container root element; null while hidden.Type() => any

SideBarOption

PropertyDescriptionTypeDefault
keyUnique key of the optionstring-
nameOption name; use the optionIcon snippet for the iconstring-

CodeContent

Each item of codes is the props of CodeItem plus a key that identifies its panel.

Props

PropertyDescriptionTypeDefault
activeKeyKeys of the expanded code panels; bind it with bind:activeKey.string | string[]-
codesThe code entries, each with the CodeItem props and a key identifying its panel.CodeItemData[][]
customRenderForwarded to the customRender snippet of the JsonViewer in every JSON panel.TypeSnippet<[JsonViewerCustomRenderArgs]>-
onChangeCalled when a panel opens or closes through its arrow, with the expanded keys.Type(activeKey: string | string[]) => void-
onExpandCalled when the expand button beside a panel header is clicked, with the event, its entry and code. Without it the button is not rendered.Type(event: MouseEvent, item: T, mode: string) => void-
searchButtonForwarded to the searchButton snippet of the JsonViewer in every JSON panel, replacing its search button.TypeSnippet<[JsonViewerSearchButtonArgs]>-

CodeItem

Props

PropertyDescriptionTypeDefault
codeHighlightPropsOther props for CodeHighlight, plus an extra class and style.TypePartial<CodeHighlightProps>-
contentThe code or JSON string.string-
customRenderThe customRender snippet of JsonViewer, for the content matched by jsonViewerProps.options.customRenderRule.TypeSnippet<[JsonViewerCustomRenderArgs]>-
isJsonWhen true the content is shown by JsonViewer, otherwise by CodeHighlight.booleanfalse
jsonViewerPropsOther props for JsonViewer, plus an extra class and style. value (the content), width and height (100%), options (read-only, auto wrap) and showSearch (false) have defaults; a prop given here replaces its default as a whole.TypePartial<JsonViewerProps>-
languageLanguage name as in the CodeHighlight language prop; register it with CodeHighlight.register first, otherwise plain text is shown.string-
nameName of the code item, shown by the CodeContent panel header and the Sidebar detail header; CodeItem itself does not render it.string-
searchButtonThe searchButton snippet of JsonViewer, replacing its search button.TypeSnippet<[JsonViewerSearchButtonArgs]>-

FileContent

Each item of files is the props of FileItem plus a key that identifies its panel; they are rendered read-only, and extensions and imgUploadProps reach their preview.

Props

PropertyDescriptionTypeDefault
activeKeyKeys of the expanded file panels; bind it with bind:activeKey.string | string[]-
filesThe file entries, rendered read-only: each entry's extensions and imgUploadProps reach its preview.FileItemData[][]
onChangeCalled when a panel opens or closes through its arrow, with the expanded keys.Type(activeKey: string | string[]) => void-
onExpandCalled when the expand button beside a panel header is clicked, with the event, its entry and file. Without it the button is not rendered.Type(event: MouseEvent, item: T, mode: string) => void-

FileItem

Props

PropertyDescriptionTypeDefault
contentRich text as HTML. Later changes are synced to the editor; content equal to the editor's is not reset, and syncing does not call onContentChange.string-
editableWhether it is editable; later changes are synced to the editor.booleantrue
extensionstiptap extensions appended to the built-in ones, read when the editor is created; later changes have no effect.Extensions[]
imgUploadPropsImage upload options, read when the editor is created; later changes have no effect.ImageUploadNodeOptions-
nameFile name, used as the accessible name of the editing area; later changes are synced to the editor.string-
onContentChangeCalled after an edit, with the content as HTML.Type(content: string) => void-