@lobehub/lobehub 2.0.0-next.239 → 2.0.0-next.240

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.cursor/rules/typescript.mdc +1 -0
  2. package/CHANGELOG.md +33 -0
  3. package/changelog/v1.json +5 -0
  4. package/locales/en-US/plugin.json +9 -0
  5. package/locales/zh-CN/plugin.json +9 -0
  6. package/package.json +1 -1
  7. package/packages/builtin-tool-gtd/src/client/Streaming/CreatePlan/index.tsx +4 -19
  8. package/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx +51 -0
  9. package/packages/builtin-tool-notebook/src/client/Inspector/index.ts +14 -0
  10. package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +101 -0
  11. package/packages/builtin-tool-notebook/src/client/Placeholder/index.ts +10 -0
  12. package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +63 -33
  13. package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +75 -0
  14. package/packages/builtin-tool-notebook/src/client/Streaming/index.ts +14 -0
  15. package/packages/builtin-tool-notebook/src/client/components/AnimatedNumber.tsx +57 -0
  16. package/packages/builtin-tool-notebook/src/client/index.ts +12 -0
  17. package/packages/builtin-tool-notebook/src/systemRole.ts +2 -1
  18. package/packages/memory-user-memory/src/extractors/base.ts +8 -13
  19. package/packages/memory-user-memory/src/extractors/context.test.ts +2 -7
  20. package/packages/memory-user-memory/src/extractors/context.ts +7 -2
  21. package/packages/memory-user-memory/src/extractors/experience.test.ts +2 -10
  22. package/packages/memory-user-memory/src/extractors/experience.ts +7 -2
  23. package/packages/memory-user-memory/src/extractors/gatekeeper.test.ts +2 -7
  24. package/packages/memory-user-memory/src/extractors/gatekeeper.ts +3 -2
  25. package/packages/memory-user-memory/src/extractors/identity.test.ts +2 -7
  26. package/packages/memory-user-memory/src/extractors/identity.ts +7 -2
  27. package/packages/memory-user-memory/src/extractors/preference.test.ts +2 -10
  28. package/packages/memory-user-memory/src/extractors/preference.ts +7 -2
  29. package/packages/memory-user-memory/src/prompts/gatekeeper.ts +127 -0
  30. package/packages/memory-user-memory/src/prompts/index.ts +2 -0
  31. package/packages/memory-user-memory/src/prompts/layers/context.ts +155 -0
  32. package/packages/memory-user-memory/src/prompts/layers/experience.ts +162 -0
  33. package/packages/memory-user-memory/src/prompts/layers/identity.ts +219 -0
  34. package/packages/memory-user-memory/src/prompts/layers/index.ts +4 -0
  35. package/packages/memory-user-memory/src/prompts/layers/preference.ts +164 -0
  36. package/packages/memory-user-memory/src/services/extractExecutor.ts +0 -7
  37. package/packages/memory-user-memory/src/types.ts +0 -1
  38. package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +2 -2
  39. package/src/app/[variants]/(main)/image/features/ImageWorkspace/Content.tsx +1 -11
  40. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +1 -7
  41. package/src/app/[variants]/(main)/image/index.tsx +2 -5
  42. package/src/components/Loading/BrandTextLoading/index.module.css +0 -1
  43. package/src/components/StreamingMarkdown/index.tsx +88 -0
  44. package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +3 -5
  45. package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +14 -0
  46. package/src/features/PluginDevModal/PluginPreview/EmptyState.tsx +1 -1
  47. package/src/locales/default/plugin.ts +9 -0
  48. package/src/server/routers/async/image.ts +1 -1
  49. package/src/server/routers/lambda/image/index.test.ts +491 -0
  50. package/src/server/routers/lambda/{image.ts → image/index.ts} +57 -41
  51. package/src/server/routers/lambda/{__tests__/image.test.ts → image/utils.test.ts} +1 -21
  52. package/src/server/routers/lambda/image/utils.ts +24 -0
  53. package/src/server/services/file/__tests__/index.test.ts +3 -3
  54. package/src/server/services/file/impls/index.ts +4 -4
  55. package/src/server/services/file/impls/s3.test.ts +57 -39
  56. package/src/server/services/file/impls/s3.ts +29 -21
  57. package/src/server/services/file/impls/type.ts +1 -2
  58. package/src/server/services/file/index.ts +5 -3
  59. package/src/tools/inspectors.ts +2 -0
  60. package/src/tools/placeholders.ts +5 -0
  61. package/src/tools/streamings.ts +2 -0
  62. package/packages/memory-user-memory/src/prompts/gatekeeper.md +0 -125
  63. package/packages/memory-user-memory/src/prompts/layers/context.md +0 -153
  64. package/packages/memory-user-memory/src/prompts/layers/experience.md +0 -160
  65. package/packages/memory-user-memory/src/prompts/layers/identity.md +0 -217
  66. package/packages/memory-user-memory/src/prompts/layers/preference.md +0 -162
  67. package/packages/memory-user-memory/src/utils/path.ts +0 -5
  68. package/src/server/services/file/impls/utils.test.ts +0 -154
  69. package/src/server/services/file/impls/utils.ts +0 -17
@@ -52,3 +52,4 @@ alwaysApply: false
52
52
 
53
53
  - Never log user private information like api key, etc
54
54
  - Don't use `import { log } from 'debug'` to log messages, because it will directly log the message to the console.
55
+ - Use console.error instead of debug package to log error message in catch block.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.240](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.239...v2.0.0-next.240)
6
+
7
+ <sup>Released on **2026-01-08**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **memory-user-memory**: Migrated to use typescript module for prompts.
12
+
13
+ #### ✨ Features
14
+
15
+ - **notebook**: Add i18n, Inspector and Streaming components.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### Code refactoring
23
+
24
+ - **memory-user-memory**: Migrated to use typescript module for prompts, closes [#11344](https://github.com/lobehub/lobe-chat/issues/11344) ([902cfe5](https://github.com/lobehub/lobe-chat/commit/902cfe5))
25
+
26
+ #### What's improved
27
+
28
+ - **notebook**: Add i18n, Inspector and Streaming components, closes [#11212](https://github.com/lobehub/lobe-chat/issues/11212) ([f7dc54f](https://github.com/lobehub/lobe-chat/commit/f7dc54f))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
5
38
  ## [Version 2.0.0-next.239](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.238...v2.0.0-next.239)
6
39
 
7
40
  <sup>Released on **2026-01-08**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,9 @@
1
1
  [
2
+ {
3
+ "children": {},
4
+ "date": "2026-01-08",
5
+ "version": "2.0.0-next.240"
6
+ },
2
7
  {
3
8
  "children": {
4
9
  "features": [
@@ -92,6 +92,15 @@
92
92
  "builtins.lobe-local-system.inspector.noResults": "No results",
93
93
  "builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
94
94
  "builtins.lobe-local-system.title": "Local System",
95
+ "builtins.lobe-notebook.actions.copy": "Copy",
96
+ "builtins.lobe-notebook.actions.creating": "Creating document...",
97
+ "builtins.lobe-notebook.actions.edit": "Edit",
98
+ "builtins.lobe-notebook.actions.expand": "Expand",
99
+ "builtins.lobe-notebook.apiName.createDocument": "Create document",
100
+ "builtins.lobe-notebook.apiName.deleteDocument": "Delete document",
101
+ "builtins.lobe-notebook.apiName.getDocument": "Get document",
102
+ "builtins.lobe-notebook.apiName.updateDocument": "Update document",
103
+ "builtins.lobe-notebook.title": "Notebook",
95
104
  "builtins.lobe-page-agent.apiName.batchUpdate": "Batch update nodes",
96
105
  "builtins.lobe-page-agent.apiName.compareSnapshots": "Compare snapshots",
97
106
  "builtins.lobe-page-agent.apiName.convertToList": "Convert to list",
@@ -92,6 +92,15 @@
92
92
  "builtins.lobe-local-system.inspector.noResults": "无结果",
93
93
  "builtins.lobe-local-system.inspector.rename.result": "<old>{{oldName}}</old> → <new>{{newName}}</new>",
94
94
  "builtins.lobe-local-system.title": "本地系统",
95
+ "builtins.lobe-notebook.actions.copy": "复制",
96
+ "builtins.lobe-notebook.actions.creating": "文档创建中...",
97
+ "builtins.lobe-notebook.actions.edit": "编辑",
98
+ "builtins.lobe-notebook.actions.expand": "展开",
99
+ "builtins.lobe-notebook.apiName.createDocument": "创建文档",
100
+ "builtins.lobe-notebook.apiName.deleteDocument": "删除文档",
101
+ "builtins.lobe-notebook.apiName.getDocument": "获取文档",
102
+ "builtins.lobe-notebook.apiName.updateDocument": "更新文档",
103
+ "builtins.lobe-notebook.title": "笔记本",
95
104
  "builtins.lobe-page-agent.apiName.batchUpdate": "批量更新节点",
96
105
  "builtins.lobe-page-agent.apiName.compareSnapshots": "比较快照",
97
106
  "builtins.lobe-page-agent.apiName.convertToList": "转换为列表",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.239",
3
+ "version": "2.0.0-next.240",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,14 +1,14 @@
1
1
  'use client';
2
2
 
3
3
  import type { BuiltinStreamingProps } from '@lobechat/types';
4
- import { Flexbox, Icon, Markdown, Text } from '@lobehub/ui';
4
+ import { Flexbox, Icon, Text } from '@lobehub/ui';
5
5
  import { createStaticStyles } from 'antd-style';
6
6
  import { ListChecksIcon } from 'lucide-react';
7
7
  import { memo } from 'react';
8
8
 
9
- import type { CreatePlanParams } from '../../../types';
9
+ import StreamingMarkdown from '@/components/StreamingMarkdown';
10
10
 
11
- const MAX_CONTENT_HEIGHT = 100;
11
+ import type { CreatePlanParams } from '../../../types';
12
12
 
13
13
  const styles = createStaticStyles(({ css, cssVar }) => ({
14
14
  container: css`
@@ -17,15 +17,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
17
17
  border: 1px solid ${cssVar.colorBorder};
18
18
  border-radius: 8px;
19
19
  `,
20
- content: css`
21
- overflow: hidden auto;
22
-
23
- max-height: ${MAX_CONTENT_HEIGHT}px;
24
- padding: 12px;
25
- border-radius: 8px;
26
-
27
- background: ${cssVar.colorFillQuaternary};
28
- `,
29
20
  description: css`
30
21
  font-size: 14px;
31
22
  color: ${cssVar.colorTextSecondary};
@@ -70,13 +61,7 @@ export const CreatePlanStreaming = memo<BuiltinStreamingProps<CreatePlanParams>>
70
61
  )}
71
62
 
72
63
  {/* Context content - streaming with animation */}
73
- {context && (
74
- <div className={styles.content}>
75
- <Markdown animated fontSize={13} variant={'chat'}>
76
- {context}
77
- </Markdown>
78
- </div>
79
- )}
64
+ <StreamingMarkdown maxHeight={100}>{context}</StreamingMarkdown>
80
65
  </Flexbox>
81
66
  );
82
67
  });
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+
3
+ import type { BuiltinInspectorProps } from '@lobechat/types';
4
+ import { createStaticStyles, cx } from 'antd-style';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { highlightTextStyles, shinyTextStyles } from '@/styles';
9
+
10
+ import type { CreateDocumentArgs, CreateDocumentState } from '../../../types';
11
+
12
+ const styles = createStaticStyles(({ css, cssVar }) => ({
13
+ root: css`
14
+ overflow: hidden;
15
+ display: -webkit-box;
16
+ -webkit-box-orient: vertical;
17
+ -webkit-line-clamp: 1;
18
+
19
+ color: ${cssVar.colorTextSecondary};
20
+ `,
21
+ }));
22
+
23
+ export const CreateDocumentInspector = memo<
24
+ BuiltinInspectorProps<CreateDocumentArgs, CreateDocumentState>
25
+ >(({ args, partialArgs, isArgumentsStreaming, isLoading }) => {
26
+ const { t } = useTranslation('plugin');
27
+
28
+ const title = args?.title || partialArgs?.title;
29
+
30
+ // During streaming without title, show init
31
+ if (isArgumentsStreaming && !title) {
32
+ return (
33
+ <div className={cx(styles.root, shinyTextStyles.shinyText)}>
34
+ <span>{t('builtins.lobe-notebook.apiName.createDocument')}</span>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ return (
40
+ <div
41
+ className={cx(styles.root, (isArgumentsStreaming || isLoading) && shinyTextStyles.shinyText)}
42
+ >
43
+ <span>{t('builtins.lobe-notebook.apiName.createDocument')}: </span>
44
+ {title && <span className={highlightTextStyles.primary}>{title}</span>}
45
+ </div>
46
+ );
47
+ });
48
+
49
+ CreateDocumentInspector.displayName = 'CreateDocumentInspector';
50
+
51
+ export default CreateDocumentInspector;
@@ -0,0 +1,14 @@
1
+ import { type BuiltinInspector } from '@lobechat/types';
2
+
3
+ import { NotebookApiName } from '../../types';
4
+ import { CreateDocumentInspector } from './CreateDocument';
5
+
6
+ /**
7
+ * Notebook Inspector Components Registry
8
+ *
9
+ * Inspector components customize the title/header area
10
+ * of tool calls in the conversation UI.
11
+ */
12
+ export const NotebookInspectors: Record<string, BuiltinInspector> = {
13
+ [NotebookApiName.createDocument]: CreateDocumentInspector as BuiltinInspector,
14
+ };
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+
3
+ import type { BuiltinPlaceholderProps } from '@lobechat/types';
4
+ import { Flexbox, Markdown, ScrollShadow } from '@lobehub/ui';
5
+ import { createStaticStyles } from 'antd-style';
6
+ import { NotebookText } from 'lucide-react';
7
+ import { memo } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
11
+
12
+ import type { CreateDocumentArgs } from '../../types';
13
+
14
+ const styles = createStaticStyles(({ css, cssVar }) => ({
15
+ container: css`
16
+ position: relative;
17
+
18
+ overflow: hidden;
19
+
20
+ width: 100%;
21
+ border: 1px solid ${cssVar.colorBorderSecondary};
22
+ border-radius: 16px;
23
+
24
+ background: ${cssVar.colorBgElevated};
25
+ `,
26
+ content: css`
27
+ padding-block: 16px;
28
+ padding-inline: 16px;
29
+ `,
30
+ header: css`
31
+ padding-block: 10px;
32
+ padding-inline: 12px;
33
+ border-block-end: 1px solid ${cssVar.colorBorderSecondary};
34
+ `,
35
+ icon: css`
36
+ color: ${cssVar.colorPrimary};
37
+ `,
38
+ statusTag: css`
39
+ position: absolute;
40
+ inset-block-end: 16px;
41
+ inset-inline-start: 50%;
42
+ transform: translateX(-50%);
43
+
44
+ display: inline-flex;
45
+ gap: 6px;
46
+ align-items: center;
47
+
48
+ padding-block: 4px;
49
+ padding-inline: 12px;
50
+ border: 1px solid ${cssVar.colorBorderSecondary};
51
+ border-radius: 16px;
52
+
53
+ font-size: 14px;
54
+ color: ${cssVar.colorText};
55
+
56
+ background: ${cssVar.colorBgContainer};
57
+ `,
58
+ title: css`
59
+ overflow: hidden;
60
+ display: -webkit-box;
61
+ -webkit-box-orient: vertical;
62
+ -webkit-line-clamp: 1;
63
+
64
+ font-weight: 500;
65
+ color: ${cssVar.colorText};
66
+ `,
67
+ }));
68
+
69
+ export const CreateDocumentPlaceholder = memo<BuiltinPlaceholderProps<CreateDocumentArgs>>(
70
+ ({ args }) => {
71
+ const { t } = useTranslation('plugin');
72
+ const { title, content } = args || {};
73
+
74
+ return (
75
+ <Flexbox className={styles.container}>
76
+ {/* Header */}
77
+ <Flexbox align={'center'} className={styles.header} gap={8} horizontal>
78
+ <NotebookText className={styles.icon} size={16} />
79
+ <Flexbox flex={1}>
80
+ <div className={styles.title}>{title}</div>
81
+ </Flexbox>
82
+ <NeuralNetworkLoading size={20} />
83
+ </Flexbox>
84
+ {/* Content skeleton */}
85
+ <ScrollShadow className={styles.content} offset={12} size={12} style={{ maxHeight: 400 }}>
86
+ {content && (
87
+ <Markdown style={{ overflow: 'unset', paddingBottom: 40 }} variant={'chat'}>
88
+ {content}
89
+ </Markdown>
90
+ )}
91
+ </ScrollShadow>
92
+ <div className={styles.statusTag}>
93
+ <NeuralNetworkLoading size={14} />
94
+ <span style={{ fontSize: 12 }}>{t('builtins.lobe-notebook.actions.creating')}</span>
95
+ </div>
96
+ </Flexbox>
97
+ );
98
+ },
99
+ );
100
+
101
+ export default CreateDocumentPlaceholder;
@@ -0,0 +1,10 @@
1
+ import { type BuiltinPlaceholder } from '@lobechat/types';
2
+
3
+ import { NotebookApiName } from '../../types';
4
+ import { CreateDocumentPlaceholder } from './CreateDocument';
5
+
6
+ export { CreateDocumentPlaceholder } from './CreateDocument';
7
+
8
+ export const NotebookPlaceholders: Record<string, BuiltinPlaceholder> = {
9
+ [NotebookApiName.createDocument]: CreateDocumentPlaceholder as BuiltinPlaceholder,
10
+ };
@@ -1,36 +1,46 @@
1
1
  'use client';
2
2
 
3
- import { Flexbox, Tag, Text } from '@lobehub/ui';
3
+ import { ActionIcon, CopyButton, Flexbox, Markdown, ScrollShadow } from '@lobehub/ui';
4
+ import { Button } from 'antd';
4
5
  import { createStaticStyles } from 'antd-style';
5
- import { FileText, NotebookText } from 'lucide-react';
6
+ import { Maximize2, NotebookText, PencilLine } from 'lucide-react';
6
7
  import { memo } from 'react';
8
+ import { useTranslation } from 'react-i18next';
7
9
 
8
10
  import { useChatStore } from '@/store/chat';
9
11
 
10
12
  import { NotebookDocument } from '../../../types';
11
13
 
14
+
12
15
  const styles = createStaticStyles(({ css, cssVar }) => ({
13
16
  container: css`
14
- cursor: pointer;
17
+ position: relative;
15
18
 
16
19
  overflow: hidden;
17
20
 
18
21
  width: 100%;
19
- padding-block: 12px;
20
- padding-inline: 12px;
21
22
  border: 1px solid ${cssVar.colorBorderSecondary};
22
- border-radius: 8px;
23
+ border-radius: 16px;
23
24
 
24
25
  background: ${cssVar.colorBgElevated};
26
+ `,
27
+ content: css`
28
+ padding-inline: 16px;
29
+
30
+ font-size: 14px;
31
+ `,
32
+ expandButton: css`
33
+ position: absolute;
34
+ inset-block-end: 16px;
35
+ inset-inline-start: 50%;
36
+ transform: translateX(-50%);
25
37
 
26
- &:hover {
27
- background: ${cssVar.colorFillSecondary};
28
- }
38
+ box-shadow: ${cssVar.boxShadow};
29
39
  `,
30
- description: css`
31
- font-size: 12px;
32
- line-height: 1.5;
33
- color: ${cssVar.colorTextSecondary};
40
+ header: css`
41
+ padding-block: 10px;
42
+ padding-inline: 12px;
43
+ border-block-end: 1px solid ${cssVar.colorBorderSecondary};
34
44
  `,
35
45
  icon: css`
36
46
  color: ${cssVar.colorPrimary};
@@ -44,9 +54,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
44
54
  font-weight: 500;
45
55
  color: ${cssVar.colorText};
46
56
  `,
47
- typeTag: css`
48
- font-size: 11px;
49
- `,
50
57
  }));
51
58
 
52
59
  interface DocumentCardProps {
@@ -54,30 +61,53 @@ interface DocumentCardProps {
54
61
  }
55
62
 
56
63
  const DocumentCard = memo<DocumentCardProps>(({ document }) => {
64
+ const { t } = useTranslation('plugin');
57
65
  const openDocument = useChatStore((s) => s.openDocument);
58
66
 
59
- const handleClick = () => {
67
+ const handleExpand = () => {
60
68
  openDocument(document.id);
61
69
  };
62
70
 
63
71
  return (
64
- <Flexbox className={styles.container} gap={8} onClick={handleClick}>
65
- <Flexbox align={'center'} gap={8} horizontal>
66
- {document.type === 'note' ? (
67
- <NotebookText className={styles.icon} size={16} />
68
- ) : (
69
- <FileText className={styles.icon} size={16} />
70
- )}
71
- <div className={styles.title}>{document.title}</div>
72
- <Tag className={styles.typeTag} size={'small'}>
73
- {document.type}
74
- </Tag>
72
+ <Flexbox className={styles.container}>
73
+ {/* Header */}
74
+ <Flexbox align={'center'} className={styles.header} gap={8} horizontal>
75
+ <NotebookText className={styles.icon} size={16} />
76
+ <Flexbox flex={1}>
77
+ <div className={styles.title}>{document.title}</div>
78
+ </Flexbox>
79
+ <Flexbox gap={4} horizontal>
80
+ <CopyButton
81
+ content={document.content}
82
+ size={'small'}
83
+ title={t('builtins.lobe-notebook.actions.copy')}
84
+ />
85
+ <ActionIcon
86
+ icon={PencilLine}
87
+ onClick={handleExpand}
88
+ size={'small'}
89
+ title={t('builtins.lobe-notebook.actions.edit')}
90
+ />
91
+ </Flexbox>
75
92
  </Flexbox>
76
- {document.description && (
77
- <Text className={styles.description} ellipsis={{ rows: 2 }}>
78
- {document.description}
79
- </Text>
80
- )}
93
+ {/* Content */}
94
+ <ScrollShadow className={styles.content} offset={12} size={12} style={{ maxHeight: 400 }}>
95
+ <Markdown style={{ overflow: 'unset', paddingBottom: 40 }} variant={'chat'}>
96
+ {document.content}
97
+ </Markdown>
98
+ </ScrollShadow>
99
+
100
+ {/* Floating expand button */}
101
+ <Button
102
+ className={styles.expandButton}
103
+ color={'default'}
104
+ icon={<Maximize2 size={14} />}
105
+ onClick={handleExpand}
106
+ shape={'round'}
107
+ variant={'outlined'}
108
+ >
109
+ {t('builtins.lobe-notebook.actions.expand')}
110
+ </Button>
81
111
  </Flexbox>
82
112
  );
83
113
  });
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+
3
+ import type { BuiltinStreamingProps } from '@lobechat/types';
4
+ import { Flexbox } from '@lobehub/ui';
5
+ import { createStaticStyles } from 'antd-style';
6
+ import { NotebookText } from 'lucide-react';
7
+ import { memo } from 'react';
8
+
9
+ import BubblesLoading from '@/components/BubblesLoading';
10
+ import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
11
+ import StreamingMarkdown from '@/components/StreamingMarkdown';
12
+
13
+ import type { CreateDocumentArgs } from '../../../types';
14
+
15
+ const styles = createStaticStyles(({ css, cssVar }) => ({
16
+ container: css`
17
+ overflow: hidden;
18
+
19
+ width: 100%;
20
+ border: 1px solid ${cssVar.colorBorderSecondary};
21
+ border-radius: 16px;
22
+
23
+ background: ${cssVar.colorBgElevated};
24
+ `,
25
+ header: css`
26
+ padding-block: 10px;
27
+ padding-inline: 12px;
28
+ border-block-end: 1px solid ${cssVar.colorBorderSecondary};
29
+ `,
30
+ icon: css`
31
+ color: ${cssVar.colorPrimary};
32
+ `,
33
+ title: css`
34
+ overflow: hidden;
35
+ display: -webkit-box;
36
+ -webkit-box-orient: vertical;
37
+ -webkit-line-clamp: 1;
38
+
39
+ font-weight: 500;
40
+ color: ${cssVar.colorText};
41
+ `,
42
+ }));
43
+
44
+ export const CreateDocumentStreaming = memo<BuiltinStreamingProps<CreateDocumentArgs>>(
45
+ ({ args }) => {
46
+ const { content, title } = args || {};
47
+
48
+ if (!content && !title) return null;
49
+
50
+ return (
51
+ <Flexbox className={styles.container}>
52
+ {/* Header */}
53
+ <Flexbox align={'center'} className={styles.header} gap={8} horizontal>
54
+ <NotebookText className={styles.icon} size={16} />
55
+ <Flexbox flex={1}>
56
+ <div className={styles.title}>{title}</div>
57
+ </Flexbox>
58
+ <NeuralNetworkLoading size={20} />
59
+ </Flexbox>
60
+ {/* Content */}
61
+ {!content ? (
62
+ <Flexbox paddingBlock={16} paddingInline={12}>
63
+ <BubblesLoading />
64
+ </Flexbox>
65
+ ) : (
66
+ <StreamingMarkdown>{content}</StreamingMarkdown>
67
+ )}
68
+ </Flexbox>
69
+ );
70
+ },
71
+ );
72
+
73
+ CreateDocumentStreaming.displayName = 'CreateDocumentStreaming';
74
+
75
+ export default CreateDocumentStreaming;
@@ -0,0 +1,14 @@
1
+ import { type BuiltinStreaming } from '@lobechat/types';
2
+
3
+ import { NotebookApiName } from '../../types';
4
+ import { CreateDocumentStreaming } from './CreateDocument';
5
+
6
+ /**
7
+ * Notebook Streaming Components Registry
8
+ *
9
+ * Streaming components are used to render tool calls while arguments
10
+ * are still being generated, allowing real-time feedback to users.
11
+ */
12
+ export const NotebookStreamings: Record<string, BuiltinStreaming> = {
13
+ [NotebookApiName.createDocument]: CreateDocumentStreaming as BuiltinStreaming,
14
+ };
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import { memo, useEffect, useRef, useState } from 'react';
4
+
5
+ interface AnimatedNumberProps {
6
+ duration?: number;
7
+ formatter?: (value: number) => string;
8
+ value: number;
9
+ }
10
+
11
+ export const AnimatedNumber = memo<AnimatedNumberProps>(({ value, duration = 500, formatter }) => {
12
+ const [displayValue, setDisplayValue] = useState(value);
13
+ const frameRef = useRef<number>(undefined);
14
+ const startTimeRef = useRef<number>(undefined);
15
+ const startValueRef = useRef(value);
16
+
17
+ useEffect(() => {
18
+ const startValue = startValueRef.current;
19
+ const diff = value - startValue;
20
+
21
+ if (diff === 0) return;
22
+
23
+ const animate = (currentTime: number) => {
24
+ if (!startTimeRef.current) {
25
+ startTimeRef.current = currentTime;
26
+ }
27
+
28
+ const elapsed = currentTime - startTimeRef.current;
29
+ const progress = Math.min(elapsed / duration, 1);
30
+
31
+ // easeOutCubic
32
+ const easeProgress = 1 - (1 - progress) ** 3;
33
+ const current = startValue + diff * easeProgress;
34
+
35
+ setDisplayValue(current);
36
+
37
+ if (progress < 1) {
38
+ frameRef.current = requestAnimationFrame(animate);
39
+ } else {
40
+ startValueRef.current = value;
41
+ startTimeRef.current = undefined;
42
+ }
43
+ };
44
+
45
+ frameRef.current = requestAnimationFrame(animate);
46
+
47
+ return () => {
48
+ if (frameRef.current) {
49
+ cancelAnimationFrame(frameRef.current);
50
+ }
51
+ };
52
+ }, [value, duration]);
53
+
54
+ return formatter ? formatter(displayValue) : Math.round(displayValue).toLocaleString();
55
+ });
56
+
57
+ AnimatedNumber.displayName = 'AnimatedNumber';
@@ -1,6 +1,18 @@
1
+ // Inspector components (customized tool call headers)
2
+ export { NotebookInspectors } from './Inspector';
3
+
4
+ // Intervention components (approval dialogs)
1
5
  export { NotebookInterventions } from './Intervention';
6
+
7
+ // Placeholder components (loading states)
8
+ export { CreateDocumentPlaceholder, NotebookPlaceholders } from './Placeholder';
9
+
10
+ // Render components (read-only snapshots)
2
11
  export { CreateDocument, NotebookRenders } from './Render';
3
12
 
13
+ // Streaming components
14
+ export { NotebookStreamings } from './Streaming';
15
+
4
16
  // Re-export types and manifest for convenience
5
17
  export { NotebookManifest } from '../manifest';
6
18
  export * from '../types';
@@ -34,11 +34,12 @@ Note: The list of existing documents is automatically provided in the context, s
34
34
  </workflow>
35
35
 
36
36
  <best_practices>
37
- - Use descriptive titles that summarize the content
37
+ - Use clean, concise titles without decorations or suffixes (e.g., use "The Last Letter" instead of "《The Last Letter》 - Short Story")
38
38
  - Choose appropriate document types based on content nature
39
39
  - For long content, consider breaking into multiple documents
40
40
  - Use append mode when adding to existing documents
41
41
  - Always confirm before deleting documents
42
+ - Do NOT include h1 headings in document content (the title field already serves as the document title)
42
43
  </best_practices>
43
44
 
44
45
  <response_format>