feat(notebook): add i18n, Inspector and Streaming components (#11212)

*  feat(notebook): add i18n, Inspector and Streaming components

- Add i18n entries for notebook tool in plugin.ts
- Add zh-CN and en-US translations
- Add CreateDocument Inspector component for streaming status display
- Add CreateDocument Streaming component for real-time markdown preview
- Add AnimatedNumber helper component
- Export NotebookInspectors and NotebookStreamings from client

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* 🐛 fix(notebook): simplify Inspector to show title directly

Follow WebSearch Inspector pattern - use direct string concatenation
instead of Trans component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* 🐛 fix(notebook): add isLoading state for shiny animation

Match WebSearch Inspector pattern - show shiny animation during
both streaming and loading states

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor

* improve document

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Arvin Xu
2026-01-08 17:06:38 +08:00
committed by GitHub
parent de1504cf7b
commit f7dc54fb37
22 changed files with 547 additions and 61 deletions

View File

@@ -92,6 +92,15 @@
"builtins.lobe-local-system.inspector.noResults": "No results",
"builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
"builtins.lobe-local-system.title": "Local System",
"builtins.lobe-notebook.actions.copy": "Copy",
"builtins.lobe-notebook.actions.creating": "Creating document...",
"builtins.lobe-notebook.actions.edit": "Edit",
"builtins.lobe-notebook.actions.expand": "Expand",
"builtins.lobe-notebook.apiName.createDocument": "Create document",
"builtins.lobe-notebook.apiName.deleteDocument": "Delete document",
"builtins.lobe-notebook.apiName.getDocument": "Get document",
"builtins.lobe-notebook.apiName.updateDocument": "Update document",
"builtins.lobe-notebook.title": "Notebook",
"builtins.lobe-page-agent.apiName.batchUpdate": "Batch update nodes",
"builtins.lobe-page-agent.apiName.compareSnapshots": "Compare snapshots",
"builtins.lobe-page-agent.apiName.convertToList": "Convert to list",

View File

@@ -92,6 +92,15 @@
"builtins.lobe-local-system.inspector.noResults": "无结果",
"builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
"builtins.lobe-local-system.title": "本地系统",
"builtins.lobe-notebook.actions.copy": "复制",
"builtins.lobe-notebook.actions.creating": "文档创建中...",
"builtins.lobe-notebook.actions.edit": "编辑",
"builtins.lobe-notebook.actions.expand": "展开",
"builtins.lobe-notebook.apiName.createDocument": "创建文档",
"builtins.lobe-notebook.apiName.deleteDocument": "删除文档",
"builtins.lobe-notebook.apiName.getDocument": "获取文档",
"builtins.lobe-notebook.apiName.updateDocument": "更新文档",
"builtins.lobe-notebook.title": "笔记本",
"builtins.lobe-page-agent.apiName.batchUpdate": "批量更新节点",
"builtins.lobe-page-agent.apiName.compareSnapshots": "比较快照",
"builtins.lobe-page-agent.apiName.convertToList": "转换为列表",

View File

@@ -1,14 +1,14 @@
'use client';
import type { BuiltinStreamingProps } from '@lobechat/types';
import { Flexbox, Icon, Markdown, Text } from '@lobehub/ui';
import { Flexbox, Icon, Text } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { ListChecksIcon } from 'lucide-react';
import { memo } from 'react';
import type { CreatePlanParams } from '../../../types';
import StreamingMarkdown from '@/components/StreamingMarkdown';
const MAX_CONTENT_HEIGHT = 100;
import type { CreatePlanParams } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
@@ -17,15 +17,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
border: 1px solid ${cssVar.colorBorder};
border-radius: 8px;
`,
content: css`
overflow: hidden auto;
max-height: ${MAX_CONTENT_HEIGHT}px;
padding: 12px;
border-radius: 8px;
background: ${cssVar.colorFillQuaternary};
`,
description: css`
font-size: 14px;
color: ${cssVar.colorTextSecondary};
@@ -70,13 +61,7 @@ export const CreatePlanStreaming = memo<BuiltinStreamingProps<CreatePlanParams>>
)}
{/* Context content - streaming with animation */}
{context && (
<div className={styles.content}>
<Markdown animated fontSize={13} variant={'chat'}>
{context}
</Markdown>
</div>
)}
<StreamingMarkdown maxHeight={100}>{context}</StreamingMarkdown>
</Flexbox>
);
});

View File

@@ -0,0 +1,51 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { highlightTextStyles, shinyTextStyles } from '@/styles';
import type { CreateDocumentArgs, CreateDocumentState } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
color: ${cssVar.colorTextSecondary};
`,
}));
export const CreateDocumentInspector = memo<
BuiltinInspectorProps<CreateDocumentArgs, CreateDocumentState>
>(({ args, partialArgs, isArgumentsStreaming, isLoading }) => {
const { t } = useTranslation('plugin');
const title = args?.title || partialArgs?.title;
// During streaming without title, show init
if (isArgumentsStreaming && !title) {
return (
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-notebook.apiName.createDocument')}</span>
</div>
);
}
return (
<div
className={cx(styles.root, (isArgumentsStreaming || isLoading) && shinyTextStyles.shinyText)}
>
<span>{t('builtins.lobe-notebook.apiName.createDocument')}: </span>
{title && <span className={highlightTextStyles.primary}>{title}</span>}
</div>
);
});
CreateDocumentInspector.displayName = 'CreateDocumentInspector';
export default CreateDocumentInspector;

View File

@@ -0,0 +1,14 @@
import { type BuiltinInspector } from '@lobechat/types';
import { NotebookApiName } from '../../types';
import { CreateDocumentInspector } from './CreateDocument';
/**
* Notebook Inspector Components Registry
*
* Inspector components customize the title/header area
* of tool calls in the conversation UI.
*/
export const NotebookInspectors: Record<string, BuiltinInspector> = {
[NotebookApiName.createDocument]: CreateDocumentInspector as BuiltinInspector,
};

View File

@@ -0,0 +1,101 @@
'use client';
import type { BuiltinPlaceholderProps } from '@lobechat/types';
import { Flexbox, Markdown, ScrollShadow } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { NotebookText } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
import type { CreateDocumentArgs } from '../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
position: relative;
overflow: hidden;
width: 100%;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 16px;
background: ${cssVar.colorBgElevated};
`,
content: css`
padding-block: 16px;
padding-inline: 16px;
`,
header: css`
padding-block: 10px;
padding-inline: 12px;
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
icon: css`
color: ${cssVar.colorPrimary};
`,
statusTag: css`
position: absolute;
inset-block-end: 16px;
inset-inline-start: 50%;
transform: translateX(-50%);
display: inline-flex;
gap: 6px;
align-items: center;
padding-block: 4px;
padding-inline: 12px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 16px;
font-size: 14px;
color: ${cssVar.colorText};
background: ${cssVar.colorBgContainer};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-weight: 500;
color: ${cssVar.colorText};
`,
}));
export const CreateDocumentPlaceholder = memo<BuiltinPlaceholderProps<CreateDocumentArgs>>(
({ args }) => {
const { t } = useTranslation('plugin');
const { title, content } = args || {};
return (
<Flexbox className={styles.container}>
{/* Header */}
<Flexbox align={'center'} className={styles.header} gap={8} horizontal>
<NotebookText className={styles.icon} size={16} />
<Flexbox flex={1}>
<div className={styles.title}>{title}</div>
</Flexbox>
<NeuralNetworkLoading size={20} />
</Flexbox>
{/* Content skeleton */}
<ScrollShadow className={styles.content} offset={12} size={12} style={{ maxHeight: 400 }}>
{content && (
<Markdown style={{ overflow: 'unset', paddingBottom: 40 }} variant={'chat'}>
{content}
</Markdown>
)}
</ScrollShadow>
<div className={styles.statusTag}>
<NeuralNetworkLoading size={14} />
<span style={{ fontSize: 12 }}>{t('builtins.lobe-notebook.actions.creating')}</span>
</div>
</Flexbox>
);
},
);
export default CreateDocumentPlaceholder;

View File

@@ -0,0 +1,10 @@
import { type BuiltinPlaceholder } from '@lobechat/types';
import { NotebookApiName } from '../../types';
import { CreateDocumentPlaceholder } from './CreateDocument';
export { CreateDocumentPlaceholder } from './CreateDocument';
export const NotebookPlaceholders: Record<string, BuiltinPlaceholder> = {
[NotebookApiName.createDocument]: CreateDocumentPlaceholder as BuiltinPlaceholder,
};

View File

@@ -1,36 +1,46 @@
'use client';
import { Flexbox, Tag, Text } from '@lobehub/ui';
import { ActionIcon, CopyButton, Flexbox, Markdown, ScrollShadow } from '@lobehub/ui';
import { Button } from 'antd';
import { createStaticStyles } from 'antd-style';
import { FileText, NotebookText } from 'lucide-react';
import { Maximize2, NotebookText, PencilLine } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useChatStore } from '@/store/chat';
import { NotebookDocument } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
cursor: pointer;
position: relative;
overflow: hidden;
width: 100%;
padding-block: 12px;
padding-inline: 12px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 8px;
border-radius: 16px;
background: ${cssVar.colorBgElevated};
&:hover {
background: ${cssVar.colorFillSecondary};
}
`,
description: css`
font-size: 12px;
line-height: 1.5;
color: ${cssVar.colorTextSecondary};
content: css`
padding-inline: 16px;
font-size: 14px;
`,
expandButton: css`
position: absolute;
inset-block-end: 16px;
inset-inline-start: 50%;
transform: translateX(-50%);
box-shadow: ${cssVar.boxShadow};
`,
header: css`
padding-block: 10px;
padding-inline: 12px;
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
icon: css`
color: ${cssVar.colorPrimary};
@@ -44,9 +54,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
font-weight: 500;
color: ${cssVar.colorText};
`,
typeTag: css`
font-size: 11px;
`,
}));
interface DocumentCardProps {
@@ -54,30 +61,53 @@ interface DocumentCardProps {
}
const DocumentCard = memo<DocumentCardProps>(({ document }) => {
const { t } = useTranslation('plugin');
const openDocument = useChatStore((s) => s.openDocument);
const handleClick = () => {
const handleExpand = () => {
openDocument(document.id);
};
return (
<Flexbox className={styles.container} gap={8} onClick={handleClick}>
<Flexbox align={'center'} gap={8} horizontal>
{document.type === 'note' ? (
<NotebookText className={styles.icon} size={16} />
) : (
<FileText className={styles.icon} size={16} />
)}
<div className={styles.title}>{document.title}</div>
<Tag className={styles.typeTag} size={'small'}>
{document.type}
</Tag>
<Flexbox className={styles.container}>
{/* Header */}
<Flexbox align={'center'} className={styles.header} gap={8} horizontal>
<NotebookText className={styles.icon} size={16} />
<Flexbox flex={1}>
<div className={styles.title}>{document.title}</div>
</Flexbox>
<Flexbox gap={4} horizontal>
<CopyButton
content={document.content}
size={'small'}
title={t('builtins.lobe-notebook.actions.copy')}
/>
<ActionIcon
icon={PencilLine}
onClick={handleExpand}
size={'small'}
title={t('builtins.lobe-notebook.actions.edit')}
/>
</Flexbox>
</Flexbox>
{document.description && (
<Text className={styles.description} ellipsis={{ rows: 2 }}>
{document.description}
</Text>
)}
{/* Content */}
<ScrollShadow className={styles.content} offset={12} size={12} style={{ maxHeight: 400 }}>
<Markdown style={{ overflow: 'unset', paddingBottom: 40 }} variant={'chat'}>
{document.content}
</Markdown>
</ScrollShadow>
{/* Floating expand button */}
<Button
className={styles.expandButton}
color={'default'}
icon={<Maximize2 size={14} />}
onClick={handleExpand}
shape={'round'}
variant={'outlined'}
>
{t('builtins.lobe-notebook.actions.expand')}
</Button>
</Flexbox>
);
});

View File

@@ -0,0 +1,75 @@
'use client';
import type { BuiltinStreamingProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { NotebookText } from 'lucide-react';
import { memo } from 'react';
import BubblesLoading from '@/components/BubblesLoading';
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
import StreamingMarkdown from '@/components/StreamingMarkdown';
import type { CreateDocumentArgs } from '../../../types';
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
overflow: hidden;
width: 100%;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 16px;
background: ${cssVar.colorBgElevated};
`,
header: css`
padding-block: 10px;
padding-inline: 12px;
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
icon: css`
color: ${cssVar.colorPrimary};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-weight: 500;
color: ${cssVar.colorText};
`,
}));
export const CreateDocumentStreaming = memo<BuiltinStreamingProps<CreateDocumentArgs>>(
({ args }) => {
const { content, title } = args || {};
if (!content && !title) return null;
return (
<Flexbox className={styles.container}>
{/* Header */}
<Flexbox align={'center'} className={styles.header} gap={8} horizontal>
<NotebookText className={styles.icon} size={16} />
<Flexbox flex={1}>
<div className={styles.title}>{title}</div>
</Flexbox>
<NeuralNetworkLoading size={20} />
</Flexbox>
{/* Content */}
{!content ? (
<Flexbox paddingBlock={16} paddingInline={12}>
<BubblesLoading />
</Flexbox>
) : (
<StreamingMarkdown>{content}</StreamingMarkdown>
)}
</Flexbox>
);
},
);
CreateDocumentStreaming.displayName = 'CreateDocumentStreaming';
export default CreateDocumentStreaming;

View File

@@ -0,0 +1,14 @@
import { type BuiltinStreaming } from '@lobechat/types';
import { NotebookApiName } from '../../types';
import { CreateDocumentStreaming } from './CreateDocument';
/**
* Notebook Streaming Components Registry
*
* Streaming components are used to render tool calls while arguments
* are still being generated, allowing real-time feedback to users.
*/
export const NotebookStreamings: Record<string, BuiltinStreaming> = {
[NotebookApiName.createDocument]: CreateDocumentStreaming as BuiltinStreaming,
};

View File

@@ -0,0 +1,57 @@
'use client';
import { memo, useEffect, useRef, useState } from 'react';
interface AnimatedNumberProps {
duration?: number;
formatter?: (value: number) => string;
value: number;
}
export const AnimatedNumber = memo<AnimatedNumberProps>(({ value, duration = 500, formatter }) => {
const [displayValue, setDisplayValue] = useState(value);
const frameRef = useRef<number>(undefined);
const startTimeRef = useRef<number>(undefined);
const startValueRef = useRef(value);
useEffect(() => {
const startValue = startValueRef.current;
const diff = value - startValue;
if (diff === 0) return;
const animate = (currentTime: number) => {
if (!startTimeRef.current) {
startTimeRef.current = currentTime;
}
const elapsed = currentTime - startTimeRef.current;
const progress = Math.min(elapsed / duration, 1);
// easeOutCubic
const easeProgress = 1 - (1 - progress) ** 3;
const current = startValue + diff * easeProgress;
setDisplayValue(current);
if (progress < 1) {
frameRef.current = requestAnimationFrame(animate);
} else {
startValueRef.current = value;
startTimeRef.current = undefined;
}
};
frameRef.current = requestAnimationFrame(animate);
return () => {
if (frameRef.current) {
cancelAnimationFrame(frameRef.current);
}
};
}, [value, duration]);
return formatter ? formatter(displayValue) : Math.round(displayValue).toLocaleString();
});
AnimatedNumber.displayName = 'AnimatedNumber';

View File

@@ -1,6 +1,18 @@
// Inspector components (customized tool call headers)
export { NotebookInspectors } from './Inspector';
// Intervention components (approval dialogs)
export { NotebookInterventions } from './Intervention';
// Placeholder components (loading states)
export { CreateDocumentPlaceholder, NotebookPlaceholders } from './Placeholder';
// Render components (read-only snapshots)
export { CreateDocument, NotebookRenders } from './Render';
// Streaming components
export { NotebookStreamings } from './Streaming';
// Re-export types and manifest for convenience
export { NotebookManifest } from '../manifest';
export * from '../types';

View File

@@ -34,11 +34,12 @@ Note: The list of existing documents is automatically provided in the context, s
</workflow>
<best_practices>
- Use descriptive titles that summarize the content
- Use clean, concise titles without decorations or suffixes (e.g., use "The Last Letter" instead of "《The Last Letter》 - Short Story")
- Choose appropriate document types based on content nature
- For long content, consider breaking into multiple documents
- Use append mode when adding to existing documents
- Always confirm before deleting documents
- Do NOT include h1 headings in document content (the title field already serves as the document title)
</best_practices>
<response_format>

View File

@@ -65,7 +65,6 @@
align-items: center;
padding: 2px 8px;
border: 1px solid var(--brand-border-color);
border-radius: 6px;
background: var(--brand-tag-bg);

View File

@@ -0,0 +1,89 @@
'use client';
import { Markdown, ScrollShadow } from '@lobehub/ui';
import { createStaticStyles } from 'antd-style';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
const styles = createStaticStyles(({ css }) => ({
container: css`
padding-block: 12px;
padding-inline: 16px;
border-radius: 8px;
font-size: 14px;
`,
}));
interface StreamingMarkdownProps {
children?: string;
maxHeight?: number;
}
const StreamingMarkdown = memo<StreamingMarkdownProps>(({ children, maxHeight = 400 }) => {
const containerRef = useRef<HTMLDivElement>(null);
const [userHasScrolled, setUserHasScrolled] = useState(false);
const isAutoScrollingRef = useRef(false);
// Handle user scroll detection
const handleScroll = useCallback(() => {
// Ignore scroll events triggered by auto-scroll
if (isAutoScrollingRef.current) return;
const container = containerRef.current;
if (!container) return;
// Check if user scrolled away from bottom
const distanceToBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
const isAtBottom = distanceToBottom < 20;
// If user scrolled up, stop auto-scrolling
if (!isAtBottom) {
setUserHasScrolled(true);
}
}, []);
// Auto scroll to bottom when content changes (unless user has scrolled)
useEffect(() => {
if (userHasScrolled) return;
const container = containerRef.current;
if (!container) return;
isAutoScrollingRef.current = true;
requestAnimationFrame(() => {
container.scrollTop = container.scrollHeight;
// Reset the flag after scroll completes
requestAnimationFrame(() => {
isAutoScrollingRef.current = false;
});
});
}, [children, userHasScrolled]);
// Reset userHasScrolled when content is cleared (new stream starts)
useEffect(() => {
if (!children) {
setUserHasScrolled(false);
}
}, [children]);
if (!children) return null;
return (
<ScrollShadow
className={styles.container}
offset={12}
onScroll={handleScroll}
ref={containerRef}
size={12}
style={{ maxHeight }}
>
<Markdown animated style={{ overflow: 'unset' }} variant={'chat'}>
{children}
</Markdown>
</ScrollShadow>
);
});
StreamingMarkdown.displayName = 'StreamingMarkdown';
export default StreamingMarkdown;

View File

@@ -1,4 +1,3 @@
import { LOADING_FLAT } from '@lobechat/const';
import { type ChatToolResult, type ToolIntervention } from '@lobechat/types';
import { safeParsePartialJSON } from '@lobechat/utils';
import { Flexbox } from '@lobehub/ui';
@@ -20,6 +19,7 @@ interface RenderProps {
identifier: string;
intervention?: ToolIntervention;
isArgumentsStreaming?: boolean;
isToolCalling?: boolean;
/**
* ContentBlock ID (not the group message ID)
*/
@@ -52,6 +52,7 @@ const Render = memo<RenderProps>(
intervention,
toolMessageId,
isArgumentsStreaming,
isToolCalling,
}) => {
if (toolMessageId && intervention?.status === 'pending') {
return (
@@ -125,10 +126,7 @@ const Render = memo<RenderProps>(
/>
);
// Standalone plugins always have LOADING_FLAT as content
const inPlaceholder = result.content === LOADING_FLAT && type !== 'standalone';
if (inPlaceholder) return placeholder;
if (isToolCalling) return placeholder;
return (
<Suspense fallback={placeholder}>

View File

@@ -1,9 +1,12 @@
import { LOADING_FLAT } from '@lobechat/const';
import { type ChatToolResult, type ToolIntervention } from '@lobechat/types';
import { AccordionItem, Flexbox, Skeleton } from '@lobehub/ui';
import { Divider } from 'antd';
import dynamic from 'next/dynamic';
import { memo, useEffect, useState } from 'react';
import { useChatStore } from '@/store/chat';
import { operationSelectors } from '@/store/chat/slices/operation/selectors';
import { useToolStore } from '@/store/tool';
import { toolSelectors } from '@/store/tool/selectors';
import { getBuiltinRender } from '@/tools/renders';
@@ -71,6 +74,16 @@ const Tool = memo<GroupToolProps>(
const hasStreamingRenderer = !!getBuiltinStreaming(identifier, apiName);
const forceShowStreamingRender = isArgumentsStreaming && hasStreamingRenderer;
// Get precise tool calling state from operation
const isToolCallingFromOperation = useChatStore(
operationSelectors.isMessageInToolCalling(assistantMessageId),
);
// Fallback: arguments completed but no final result yet
const isToolCallingFallback =
!isArgumentsStreaming && (!result || result.content === LOADING_FLAT || !result.content);
const isToolCalling = isToolCallingFromOperation || isToolCallingFallback;
const hasCustomRender = !!getBuiltinRender(identifier, apiName);
// Handle expand state changes with showPluginRender
@@ -138,6 +151,7 @@ const Tool = memo<GroupToolProps>(
identifier={identifier}
intervention={intervention}
isArgumentsStreaming={isArgumentsStreaming}
isToolCalling={isToolCalling}
messageId={assistantMessageId}
result={result}
setShowPluginRender={setShowPluginRender}

View File

@@ -64,7 +64,7 @@ export default function PluginEmptyState() {
{t('dev.preview.empty.title')}
</Text>
<Text className={styles.description}>{t('dev.preview.empty.desc')}</Text>
<Space align="center" direction="vertical">
<Space align="center" orientation="vertical">
<div className={styles.line} style={{ width: 128 }} />
<div className={styles.line} style={{ width: 96 }} />
<div className={styles.line} style={{ width: 48 }} />

View File

@@ -93,6 +93,15 @@ export default {
'builtins.lobe-local-system.inspector.rename.result':
'<old>{{oldName}}</old> → <new>{{newName}}</new>',
'builtins.lobe-local-system.title': 'Local System',
'builtins.lobe-notebook.actions.copy': 'Copy',
'builtins.lobe-notebook.actions.creating': 'Creating document...',
'builtins.lobe-notebook.actions.edit': 'Edit',
'builtins.lobe-notebook.actions.expand': 'Expand',
'builtins.lobe-notebook.apiName.createDocument': 'Create document',
'builtins.lobe-notebook.apiName.deleteDocument': 'Delete document',
'builtins.lobe-notebook.apiName.getDocument': 'Get document',
'builtins.lobe-notebook.apiName.updateDocument': 'Update document',
'builtins.lobe-notebook.title': 'Notebook',
'builtins.lobe-page-agent.apiName.batchUpdate': 'Batch update nodes',
'builtins.lobe-page-agent.apiName.compareSnapshots': 'Compare snapshots',
'builtins.lobe-page-agent.apiName.convertToList': 'Convert to list',

View File

@@ -15,6 +15,7 @@ import {
LocalSystemInspectors,
LocalSystemManifest,
} from '@lobechat/builtin-tool-local-system/client';
import { NotebookInspectors, NotebookManifest } from '@lobechat/builtin-tool-notebook/client';
import { PageAgentInspectors, PageAgentManifest } from '@lobechat/builtin-tool-page-agent/client';
import {
WebBrowsingInspectors,
@@ -38,6 +39,7 @@ const BuiltinToolInspectors: Record<string, Record<string, BuiltinInspector>> =
[GTDManifest.identifier]: GTDInspectors as Record<string, BuiltinInspector>,
[KnowledgeBaseManifest.identifier]: KnowledgeBaseInspectors as Record<string, BuiltinInspector>,
[LocalSystemManifest.identifier]: LocalSystemInspectors as Record<string, BuiltinInspector>,
[NotebookManifest.identifier]: NotebookInspectors as Record<string, BuiltinInspector>,
[PageAgentManifest.identifier]: PageAgentInspectors as Record<string, BuiltinInspector>,
[WebBrowsingManifest.identifier]: WebBrowsingInspectors as Record<string, BuiltinInspector>,
};

View File

@@ -4,6 +4,10 @@ import {
LocalSystemListFilesPlaceholder,
LocalSystemSearchFilesPlaceholder,
} from '@lobechat/builtin-tool-local-system/client';
import {
NotebookIdentifier,
NotebookPlaceholders,
} from '@lobechat/builtin-tool-notebook/client';
import {
WebBrowsingManifest,
WebBrowsingPlaceholders,
@@ -19,6 +23,7 @@ export const BuiltinToolPlaceholders: Record<string, Record<string, any>> = {
[LocalSystemApiName.searchLocalFiles]: LocalSystemSearchFilesPlaceholder,
[LocalSystemApiName.listLocalFiles]: LocalSystemListFilesPlaceholder,
},
[NotebookIdentifier]: NotebookPlaceholders as Record<string, any>,
[WebBrowsingManifest.identifier]: WebBrowsingPlaceholders as Record<string, any>,
};

View File

@@ -11,6 +11,7 @@ import {
LocalSystemManifest,
LocalSystemStreamings,
} from '@lobechat/builtin-tool-local-system/client';
import { NotebookManifest, NotebookStreamings } from '@lobechat/builtin-tool-notebook/client';
import { type BuiltinStreaming } from '@lobechat/types';
/**
@@ -29,6 +30,7 @@ const BuiltinToolStreamings: Record<string, Record<string, BuiltinStreaming>> =
>,
[GTDManifest.identifier]: GTDStreamings as Record<string, BuiltinStreaming>,
[LocalSystemManifest.identifier]: LocalSystemStreamings as Record<string, BuiltinStreaming>,
[NotebookManifest.identifier]: NotebookStreamings as Record<string, BuiltinStreaming>,
};
/**