@difizen/libro-ai-native 0.2.41-next.0

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/es/ai-native-code-block.d.ts +4 -0
  4. package/es/ai-native-code-block.d.ts.map +1 -0
  5. package/es/ai-native-code-block.js +176 -0
  6. package/es/ai-native-command-contribution.d.ts +16 -0
  7. package/es/ai-native-command-contribution.d.ts.map +1 -0
  8. package/es/ai-native-command-contribution.js +297 -0
  9. package/es/ai-native-command.d.ts +5 -0
  10. package/es/ai-native-command.d.ts.map +1 -0
  11. package/es/ai-native-command.js +17 -0
  12. package/es/ai-native-for-cell-view.d.ts +20 -0
  13. package/es/ai-native-for-cell-view.d.ts.map +1 -0
  14. package/es/ai-native-for-cell-view.js +190 -0
  15. package/es/ai-native-output-top.d.ts +6 -0
  16. package/es/ai-native-output-top.d.ts.map +1 -0
  17. package/es/ai-native-output-top.js +37 -0
  18. package/es/ai-native-service.d.ts +13 -0
  19. package/es/ai-native-service.d.ts.map +1 -0
  20. package/es/ai-native-service.js +75 -0
  21. package/es/ai-side-toolbar-selector.d.ts +3 -0
  22. package/es/ai-side-toolbar-selector.d.ts.map +1 -0
  23. package/es/ai-side-toolbar-selector.js +47 -0
  24. package/es/chat-slot-contribution.d.ts +15 -0
  25. package/es/chat-slot-contribution.d.ts.map +1 -0
  26. package/es/chat-slot-contribution.js +71 -0
  27. package/es/chat-view.d.ts +15 -0
  28. package/es/chat-view.d.ts.map +1 -0
  29. package/es/chat-view.js +114 -0
  30. package/es/error-output-model.d.ts +8 -0
  31. package/es/error-output-model.d.ts.map +1 -0
  32. package/es/error-output-model.js +140 -0
  33. package/es/icon.d.ts +4 -0
  34. package/es/icon.d.ts.map +1 -0
  35. package/es/icon.js +92 -0
  36. package/es/index.d.ts +6 -0
  37. package/es/index.d.ts.map +1 -0
  38. package/es/index.js +5 -0
  39. package/es/index.less +297 -0
  40. package/es/libro-ai-msg-item-model.d.ts +18 -0
  41. package/es/libro-ai-msg-item-model.d.ts.map +1 -0
  42. package/es/libro-ai-msg-item-model.js +85 -0
  43. package/es/libro-ai-native-chat-service.d.ts +8 -0
  44. package/es/libro-ai-native-chat-service.d.ts.map +1 -0
  45. package/es/libro-ai-native-chat-service.js +157 -0
  46. package/es/libro-ai-native-chat-view.d.ts +42 -0
  47. package/es/libro-ai-native-chat-view.d.ts.map +1 -0
  48. package/es/libro-ai-native-chat-view.js +88 -0
  49. package/es/libro-ai-native-color-registry.d.ts +6 -0
  50. package/es/libro-ai-native-color-registry.d.ts.map +1 -0
  51. package/es/libro-ai-native-color-registry.js +46 -0
  52. package/es/module.d.ts +3 -0
  53. package/es/module.d.ts.map +1 -0
  54. package/es/module.js +28 -0
  55. package/es/protocol.d.ts +10 -0
  56. package/es/protocol.d.ts.map +1 -0
  57. package/es/protocol.js +1 -0
  58. package/es/utils.js +114 -0
  59. package/package.json +74 -0
  60. package/src/ai-native-code-block.tsx +138 -0
  61. package/src/ai-native-command-contribution.tsx +248 -0
  62. package/src/ai-native-command.ts +23 -0
  63. package/src/ai-native-for-cell-view.tsx +156 -0
  64. package/src/ai-native-output-top.tsx +43 -0
  65. package/src/ai-native-service.ts +39 -0
  66. package/src/ai-side-toolbar-selector.tsx +51 -0
  67. package/src/chat-slot-contribution.ts +36 -0
  68. package/src/chat-view.tsx +141 -0
  69. package/src/error-output-model.tsx +103 -0
  70. package/src/icon.tsx +80 -0
  71. package/src/index.less +330 -0
  72. package/src/index.spec.ts +8 -0
  73. package/src/index.ts +5 -0
  74. package/src/libro-ai-msg-item-model.ts +69 -0
  75. package/src/libro-ai-native-chat-service.ts +95 -0
  76. package/src/libro-ai-native-chat-view.tsx +54 -0
  77. package/src/libro-ai-native-color-registry.ts +33 -0
  78. package/src/module.ts +44 -0
  79. package/src/protocol.ts +18 -0
  80. package/src/utils.tsx +115 -0
@@ -0,0 +1,248 @@
1
+ import { CommentOutlined } from '@ant-design/icons';
2
+ import {
3
+ LibroCommandRegister,
4
+ LibroSlotManager,
5
+ LibroSlotView,
6
+ LibroToolbarArea,
7
+ LibroCellView,
8
+ LibroView,
9
+ } from '@difizen/libro-jupyter';
10
+ import type { CommandRegistry, ToolbarRegistry } from '@difizen/mana-app';
11
+ import { inject } from '@difizen/mana-app';
12
+ import { CommandContribution } from '@difizen/mana-app';
13
+ import { singleton, ToolbarContribution, ViewManager } from '@difizen/mana-app';
14
+ import { l10n } from '@difizen/mana-l10n';
15
+
16
+ import { AINativeCommands } from './ai-native-command.js';
17
+ import { LibroAINativeService } from './ai-native-service.js';
18
+ import { AIToolbarSelector } from './ai-side-toolbar-selector.js';
19
+ import { LibroAIChatSlotContribution } from './chat-slot-contribution.js';
20
+ import { AIIcon } from './icon.js';
21
+ import { addCellAIClassname } from './utils.js';
22
+
23
+ @singleton({ contrib: [CommandContribution, ToolbarContribution] })
24
+ export class LibroAINativeCommandContribution
25
+ implements ToolbarContribution, CommandContribution
26
+ {
27
+ @inject(LibroCommandRegister) protected readonly libroCommand: LibroCommandRegister;
28
+ @inject(LibroAIChatSlotContribution)
29
+ libroAIChatSlotContribution: LibroAIChatSlotContribution;
30
+ @inject(LibroSlotManager) libroSlotManager: LibroSlotManager;
31
+ @inject(LibroAINativeService) libroAINativeService: LibroAINativeService;
32
+ @inject(ViewManager) viewManager: ViewManager;
33
+
34
+ registerToolbarItems(registry: ToolbarRegistry): void {
35
+ registry.registerItem({
36
+ id: AINativeCommands['AISideToolbarSelect'].id,
37
+ command: AINativeCommands['AISideToolbarSelect'].id,
38
+ icon: AIToolbarSelector,
39
+ showLabelInline: true,
40
+ order: 'b',
41
+ });
42
+ registry.registerItem({
43
+ id: AINativeCommands['Chat'].id,
44
+ command: AINativeCommands['Chat'].id,
45
+ icon: AIIcon,
46
+ order: 'a',
47
+ });
48
+
49
+ registry.registerItem({
50
+ id: AINativeCommands['CellChat'].id,
51
+ command: AINativeCommands['CellChat'].id,
52
+ icon: <CommentOutlined className="libro-ai-native-cell-chat-icon" />,
53
+ order: 'a',
54
+ });
55
+ }
56
+
57
+ registerCommands(command: CommandRegistry): void {
58
+ this.libroCommand.registerLibroCommand(
59
+ command,
60
+ AINativeCommands['AISideToolbarSelect'],
61
+ {
62
+ execute: async () => {
63
+ // this.libroService.active?.enterEditMode();
64
+ },
65
+ isVisible: (cell, libro, path) => {
66
+ if (!cell || !libro || !(libro instanceof LibroView)) {
67
+ return false;
68
+ }
69
+ return path === LibroToolbarArea.CellRight;
70
+ },
71
+ isEnabled: (cell, libro) => {
72
+ if (!libro || !(libro instanceof LibroView)) {
73
+ return false;
74
+ }
75
+ return true;
76
+ },
77
+ },
78
+ );
79
+
80
+ this.libroCommand.registerLibroCommand(command, AINativeCommands['CellChat'], {
81
+ execute: async (cell, libro) => {
82
+ if (!libro || !cell) {
83
+ return;
84
+ }
85
+ addCellAIClassname(cell);
86
+ const chatView = this.libroAINativeService.chatViewMap.get(libro.id);
87
+ const showChat = this.libroAINativeService.showChatMap.get(libro.id);
88
+ if (chatView) {
89
+ chatView.setAINativeChatView({
90
+ id: cell.id,
91
+ isCellChat: true,
92
+ cell: cell,
93
+ });
94
+ if (showChat) {
95
+ return;
96
+ }
97
+ this.libroAINativeService.showChatMap.set(libro.id, !showChat);
98
+ this.libroAINativeService.cellAIChatMap.set(cell.id, !showChat);
99
+ this.libroSlotManager.slotViewManager.addView(
100
+ chatView,
101
+ this.libroSlotManager.getSlotName(
102
+ libro,
103
+ this.libroAIChatSlotContribution.slot,
104
+ ),
105
+ {
106
+ reveal: true,
107
+ order: 'a',
108
+ },
109
+ );
110
+ }
111
+ //优化交互,用于控制点击按钮后菜单消失
112
+ this.libroAINativeService.showSideToolbar = false;
113
+ },
114
+ isEnabled: (cell, libro) => {
115
+ if (!libro || !(libro instanceof LibroView)) {
116
+ return false;
117
+ }
118
+ return true;
119
+ },
120
+ isVisible: (cell, libro, path) => {
121
+ if (!cell || !libro || !(libro instanceof LibroView)) {
122
+ return false;
123
+ }
124
+ return path === LibroToolbarArea.CellRight;
125
+ },
126
+ });
127
+ this.libroCommand.registerLibroCommand(command, AINativeCommands['Chat'], {
128
+ execute: async (cell, libro) => {
129
+ if (!libro || !(libro instanceof LibroView)) {
130
+ return;
131
+ }
132
+ const chatView = this.libroAINativeService.chatViewMap.get(libro.id);
133
+ const showChat = this.libroAINativeService.showChatMap.get(libro.id);
134
+ this.libroAINativeService.showChatMap;
135
+ if (chatView) {
136
+ if (showChat && chatView.chatView.isCellChat) {
137
+ chatView.setAINativeChatView({
138
+ id: libro.id,
139
+ isCellChat: false,
140
+ });
141
+ return;
142
+ }
143
+ chatView.setAINativeChatView({
144
+ id: libro.id,
145
+ isCellChat: false,
146
+ });
147
+ this.libroAINativeService.showChatMap.set(libro.id, !showChat);
148
+ if (showChat) {
149
+ const slotview = this.libroSlotManager.slotViewManager.getSlotView(
150
+ this.libroSlotManager.getSlotName(libro, 'right'),
151
+ );
152
+ if (slotview instanceof LibroSlotView) {
153
+ slotview.revertActive();
154
+ }
155
+ } else {
156
+ this.libroSlotManager.slotViewManager.addView(
157
+ chatView,
158
+ this.libroSlotManager.getSlotName(
159
+ libro,
160
+ this.libroAIChatSlotContribution.slot,
161
+ ),
162
+ {
163
+ reveal: true,
164
+ order: 'a',
165
+ },
166
+ );
167
+ }
168
+ }
169
+ this.libroAINativeService.showSideToolbar = false;
170
+ },
171
+ isVisible: (cell, libro, path) => {
172
+ if (!cell || !libro || !(libro instanceof LibroView)) {
173
+ return false;
174
+ }
175
+ return path === LibroToolbarArea.HeaderRight;
176
+ },
177
+ isEnabled: (cell, libro) => {
178
+ if (!libro || !(libro instanceof LibroView)) {
179
+ return false;
180
+ }
181
+ return true;
182
+ },
183
+ });
184
+ this.libroCommand.registerLibroCommand(command, AINativeCommands['Explain'], {
185
+ execute: async (cell, libro) => {
186
+ if (
187
+ !cell ||
188
+ !(cell instanceof LibroCellView) ||
189
+ !libro ||
190
+ !(libro instanceof LibroView)
191
+ ) {
192
+ return;
193
+ }
194
+ const libroAINativeForCellView =
195
+ await this.libroAINativeService.getOrCreateLibroAINativeForCellView(
196
+ cell.id,
197
+ cell,
198
+ );
199
+ libroAINativeForCellView.showAI = true;
200
+ addCellAIClassname(cell);
201
+ libroAINativeForCellView.chatStream({
202
+ content: l10n.getLang()
203
+ ? `Could you please explain this piece of code?:${cell.model.value},Provide the reasons and the results of the optimization.`
204
+ : `帮忙优化一下这段代码:${cell.model.value},给出原因以及优化结果`,
205
+ });
206
+ // this.libroService.active?.enterEditMode();
207
+ },
208
+ isEnabled: (cell, libro) => {
209
+ if (!libro || !(libro instanceof LibroView)) {
210
+ return false;
211
+ }
212
+ return true;
213
+ },
214
+ });
215
+
216
+ this.libroCommand.registerLibroCommand(command, AINativeCommands['Optimize'], {
217
+ execute: async (cell, libro) => {
218
+ if (
219
+ !cell ||
220
+ !(cell instanceof LibroCellView) ||
221
+ !libro ||
222
+ !(libro instanceof LibroView)
223
+ ) {
224
+ return;
225
+ }
226
+ const libroAINativeForCellView =
227
+ await this.libroAINativeService.getOrCreateLibroAINativeForCellView(
228
+ cell.id,
229
+ cell,
230
+ );
231
+ libroAINativeForCellView.showAI = true;
232
+ addCellAIClassname(cell);
233
+ libroAINativeForCellView.chatStream({
234
+ content: l10n.getLang()
235
+ ? `Please help optimize this piece of code: ${cell.model.value},provide reasons and the optimized result.`
236
+ : `帮忙优化一下这段代码:${cell.model.value},给出原因以及优化结果`,
237
+ });
238
+ // this.libroService.active?.enterEditMode();
239
+ },
240
+ isEnabled: (cell, libro) => {
241
+ if (!libro || !(libro instanceof LibroView)) {
242
+ return false;
243
+ }
244
+ return true;
245
+ },
246
+ });
247
+ }
248
+ }
@@ -0,0 +1,23 @@
1
+ import type { Command } from '@difizen/mana-app';
2
+
3
+ export const AINativeCommands: Record<string, Command & { keybind?: string }> = {
4
+ Explain: {
5
+ id: 'ai-native:explain',
6
+ label: 'EXPLAIN',
7
+ },
8
+ Chat: {
9
+ id: 'ai-native:chat',
10
+ label: 'Chat',
11
+ },
12
+ CellChat: {
13
+ id: 'ai-native:cell-chat',
14
+ label: 'Cell Chat',
15
+ },
16
+ AISideToolbarSelect: {
17
+ id: 'ai-native:side-toolbat-select',
18
+ },
19
+ Optimize: {
20
+ id: 'ai-native:optimize',
21
+ label: 'Optimize',
22
+ },
23
+ };
@@ -0,0 +1,156 @@
1
+ import { CloseOutlined } from '@ant-design/icons';
2
+ import type { CellView } from '@difizen/libro-jupyter';
3
+ import type { IChatMessage } from '@difizen/magent-chat';
4
+ import { AnswerState } from '@difizen/magent-chat';
5
+ import { ChatComponents } from '@difizen/magent-chat';
6
+ import { ChatEvent } from '@difizen/magent-chat';
7
+ import type { ToAutoFactory } from '@difizen/magent-core';
8
+ import { Fetcher } from '@difizen/magent-core';
9
+ import { toAutoFactory } from '@difizen/magent-core';
10
+ import type { ViewComponent } from '@difizen/mana-app';
11
+ import { ViewOption } from '@difizen/mana-app';
12
+ import { prop } from '@difizen/mana-app';
13
+ import { useObserve } from '@difizen/mana-app';
14
+ import { useInject, ViewInstance } from '@difizen/mana-app';
15
+ import { inject } from '@difizen/mana-app';
16
+ import { BaseView, transient, view } from '@difizen/mana-app';
17
+ import { l10n } from '@difizen/mana-l10n';
18
+ import { Button } from 'antd';
19
+ import { EventSourceParserStream } from 'eventsource-parser/stream';
20
+ import breaks from 'remark-breaks';
21
+ import remarkGfm from 'remark-gfm';
22
+
23
+ import { CodeBlockInCell } from './ai-native-code-block.js';
24
+ import { LibroAINativeService } from './ai-native-service.js';
25
+ import { AILoadding } from './icon.js';
26
+ import { LibroAIChatMessageItemModel } from './libro-ai-msg-item-model.js';
27
+ import type { IAINativeForCellViewOption } from './protocol.js';
28
+ import { cancelCellAIClassname, stringToReadableStream } from './utils.js';
29
+
30
+ export function LibroAINativeForCellRender() {
31
+ const LLMRender = ChatComponents.Markdown;
32
+
33
+ const instance = useInject<LibroAINativeForCellView>(ViewInstance);
34
+ const libroAINativeService = useInject<LibroAINativeService>(LibroAINativeService);
35
+
36
+ const msgItem = useObserve(instance.libroAIChatMessageItemModel);
37
+ if (!instance.showAI) {
38
+ return null;
39
+ }
40
+
41
+ return (
42
+ <div className="libro-ai-native-for-cell-container">
43
+ {msgItem?.state === AnswerState.FAIL ? (
44
+ <div className="libro-ai-native-for-cell-error">{l10n.t('请求报错~')}</div>
45
+ ) : (
46
+ <LLMRender
47
+ type="message"
48
+ components={{ code: CodeBlockInCell }}
49
+ remarkPlugins={[remarkGfm, breaks]}
50
+ >
51
+ {msgItem?.content || ''}
52
+ </LLMRender>
53
+ )}
54
+ <Button
55
+ color="default"
56
+ variant="outlined"
57
+ className={`libro-ai-native-for-cell-cancel-btn ${msgItem?.state === AnswerState.FAIL ? 'error' : ''}`}
58
+ onClick={() => {
59
+ instance.showAI = false;
60
+ if (!libroAINativeService.cellAIChatMap.get(instance.cell.id)) {
61
+ cancelCellAIClassname(instance.cell);
62
+ }
63
+ }}
64
+ icon={
65
+ msgItem?.state === AnswerState.SUCCESS ||
66
+ msgItem?.state === AnswerState.FAIL ? (
67
+ <CloseOutlined />
68
+ ) : (
69
+ <AILoadding />
70
+ )
71
+ }
72
+ >
73
+ {l10n.t('取消')}
74
+ </Button>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ @transient()
80
+ @view('libro-ai-native-for-cell-view')
81
+ export class LibroAINativeForCellView extends BaseView {
82
+ override view: ViewComponent = LibroAINativeForCellRender;
83
+ @inject(Fetcher) fetcher: Fetcher;
84
+ cell: CellView;
85
+ @inject(toAutoFactory(LibroAIChatMessageItemModel))
86
+ libroAiChatMessageItemFactory: ToAutoFactory<typeof LibroAIChatMessageItemModel>;
87
+ @prop()
88
+ libroAIChatMessageItemModel?: LibroAIChatMessageItemModel;
89
+
90
+ @prop()
91
+ showAI = false;
92
+ constructor(@inject(ViewOption) options: IAINativeForCellViewOption) {
93
+ super();
94
+ this.cell = options.cell;
95
+ }
96
+
97
+ chatStream = async (option: IChatMessage) => {
98
+ const { chat_key, content, language } = option;
99
+
100
+ const url = `/libro/api/chatstream`;
101
+ const msg = {
102
+ chat_key: chat_key,
103
+ prompt: content,
104
+ language: language,
105
+ };
106
+ const res = await this.fetcher.post<ReadableStream<Uint8Array>>(url, msg, {
107
+ headers: {
108
+ Accept: 'text/event-stream',
109
+ },
110
+ responseType: 'stream',
111
+ adapter: 'fetch',
112
+ });
113
+ if (res.status === 200) {
114
+ let reader;
115
+ if (typeof res.data === 'string') {
116
+ reader = stringToReadableStream(res.data)
117
+ .pipeThrough(new TextDecoderStream())
118
+ .pipeThrough(new EventSourceParserStream())
119
+ .getReader();
120
+ } else {
121
+ const stream = res.data;
122
+ reader = stream
123
+ .pipeThrough(new TextDecoderStream())
124
+ .pipeThrough(new EventSourceParserStream())
125
+ .getReader();
126
+ }
127
+ const msgItem = this.libroAiChatMessageItemFactory({
128
+ sender: { type: 'AI', id: 'libro' },
129
+ content: '',
130
+ });
131
+ this.libroAIChatMessageItemModel = msgItem;
132
+ let alreayDone = false;
133
+ try {
134
+ while (!alreayDone) {
135
+ const { value, done } = await reader.read();
136
+ if (done) {
137
+ alreayDone = true;
138
+ msgItem.handleEventData({
139
+ type: 'done',
140
+ });
141
+
142
+ break;
143
+ }
144
+ const data = JSON.parse(value.data);
145
+ const event = ChatEvent.format(value.event || 'chunk', data);
146
+ msgItem.handleEventData(event);
147
+ }
148
+ } catch {
149
+ msgItem.handleEventData({
150
+ type: 'error',
151
+ });
152
+ }
153
+ return;
154
+ }
155
+ };
156
+ }
@@ -0,0 +1,43 @@
1
+ import type { CellView } from '@difizen/libro-jupyter';
2
+ import { useInject, ViewManager, ViewRender } from '@difizen/mana-app';
3
+ import { useEffect, useState } from 'react';
4
+ import './index.less';
5
+
6
+ import type { LibroAINativeForCellView } from './ai-native-for-cell-view.js';
7
+ import { LibroAINativeService } from './ai-native-service.js';
8
+
9
+ export function LibroAINativeCellTopBlank({ cell }: { cell: CellView }) {
10
+ const viewManager = useInject<ViewManager>(ViewManager);
11
+
12
+ const [aiNativeForCellView, setAiNativeForCellView] =
13
+ useState<LibroAINativeForCellView>();
14
+ const libroAINativeService = useInject<LibroAINativeService>(LibroAINativeService);
15
+
16
+ useEffect(() => {
17
+ libroAINativeService
18
+ .getOrCreateLibroAINativeForCellView(cell.id, cell)
19
+ .then((view) => {
20
+ setAiNativeForCellView(view);
21
+ return;
22
+ })
23
+ .catch(() => {
24
+ //
25
+ });
26
+ }, [
27
+ cell,
28
+ cell.id,
29
+ libroAINativeService,
30
+ libroAINativeService.libroAINativeForCellViewMap,
31
+ viewManager,
32
+ ]);
33
+
34
+ if (!aiNativeForCellView) {
35
+ return null;
36
+ }
37
+
38
+ return (
39
+ <div className="libro-ai-native-output-top">
40
+ <ViewRender view={aiNativeForCellView}></ViewRender>
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,39 @@
1
+ import type { CellView } from '@difizen/libro-jupyter';
2
+ import { getOrigin, inject, prop, singleton, ViewManager } from '@difizen/mana-app';
3
+
4
+ import { LibroAINativeForCellView } from './ai-native-for-cell-view.js';
5
+ import type { LibroChatView } from './chat-view.js';
6
+ import type { LibroAiNativeChatView } from './libro-ai-native-chat-view.js';
7
+
8
+ @singleton()
9
+ export class LibroAINativeService {
10
+ @prop()
11
+ showSideToolbar = false;
12
+
13
+ chatViewMap: Map<string, LibroChatView> = new Map();
14
+
15
+ //用于控制是否显示当前 libro 层级的 chat 面板
16
+ showChatMap: Map<string, boolean> = new Map();
17
+
18
+ cellAIChatMap: Map<string, boolean> = new Map();
19
+ @inject(ViewManager)
20
+ viewManager: ViewManager;
21
+
22
+ libroAINativeForCellViewMap: Map<string, LibroAINativeForCellView> = new Map();
23
+
24
+ libroAINativeChatViewMap: Map<string, LibroAiNativeChatView> = new Map();
25
+
26
+ async getOrCreateLibroAINativeForCellView(id: string, cell: CellView) {
27
+ let libroAINativeForCellView = this.libroAINativeForCellViewMap.get(id);
28
+ if (libroAINativeForCellView) {
29
+ return libroAINativeForCellView;
30
+ } else {
31
+ libroAINativeForCellView = await this.viewManager.getOrCreateView(
32
+ LibroAINativeForCellView,
33
+ { id: id, cell: getOrigin(cell) },
34
+ );
35
+ this.libroAINativeForCellViewMap.set(cell.id, libroAINativeForCellView);
36
+ }
37
+ return libroAINativeForCellView;
38
+ }
39
+ }
@@ -0,0 +1,51 @@
1
+ import type { LibroSideToolbarMenuItemType } from '@difizen/libro-jupyter';
2
+ import { LibroSideToolbarMenu } from '@difizen/libro-jupyter';
3
+ import { useInject } from '@difizen/mana-app';
4
+ import { l10n } from '@difizen/mana-l10n';
5
+ import { Popover } from 'antd';
6
+
7
+ import { AINativeCommands } from './ai-native-command.js';
8
+ import { LibroAINativeService } from './ai-native-service.js';
9
+ import { AIIcon } from './icon.js';
10
+
11
+ export const AIToolbarSelector: React.FC = () => {
12
+ const items: LibroSideToolbarMenuItemType[] = [
13
+ {
14
+ id: AINativeCommands['Explain'].id,
15
+ label: (
16
+ <>
17
+ <span className="libro-menu-item-label">{l10n.t('代码解释')}</span>
18
+ </>
19
+ ),
20
+ group: 'ai',
21
+ },
22
+ {
23
+ id: AINativeCommands['Optimize'].id,
24
+ label: (
25
+ <>
26
+ <span className="libro-menu-item-label">{l10n.t('代码优化')}</span>
27
+ </>
28
+ ),
29
+ group: 'ai',
30
+ },
31
+ ];
32
+ const libroAINativeService = useInject<LibroAINativeService>(LibroAINativeService);
33
+ const handleOpenChange = (newOpen: boolean) => {
34
+ libroAINativeService.showSideToolbar = newOpen;
35
+ };
36
+
37
+ return (
38
+ <Popover
39
+ placement="leftTop"
40
+ content={<LibroSideToolbarMenu items={items} />}
41
+ trigger="hover"
42
+ open={libroAINativeService.showSideToolbar}
43
+ onOpenChange={handleOpenChange}
44
+ overlayClassName="libro-popover-side-toolbar-menu libro-side-toolbar-ai-select-menu"
45
+ >
46
+ <div>
47
+ <AIIcon />
48
+ </div>
49
+ </Popover>
50
+ );
51
+ };
@@ -0,0 +1,36 @@
1
+ import type {
2
+ LibroView,
3
+ LibroExtensionSlotFactory,
4
+ LibroSlot,
5
+ } from '@difizen/libro-jupyter';
6
+ import { LibroExtensionSlotContribution } from '@difizen/libro-jupyter';
7
+ import { ViewManager } from '@difizen/mana-app';
8
+ import { inject, singleton } from '@difizen/mana-app';
9
+
10
+ import { LibroAINativeService } from './ai-native-service.js';
11
+ import { LibroChatView } from './chat-view.js';
12
+
13
+ @singleton({ contrib: [LibroExtensionSlotContribution] })
14
+ export class LibroAIChatSlotContribution implements LibroExtensionSlotContribution {
15
+ @inject(ViewManager) viewManager: ViewManager;
16
+ @inject(LibroAINativeService) libroAINativeService: LibroAINativeService;
17
+
18
+ public readonly slot: LibroSlot = 'right';
19
+
20
+ factory: LibroExtensionSlotFactory = async (libro: LibroView) => {
21
+ const view = await this.viewManager.getOrCreateView(LibroChatView, {
22
+ parentId: libro.id,
23
+ });
24
+ view.parent = libro;
25
+ this.libroAINativeService.chatViewMap.set(libro.id, view);
26
+ this.libroAINativeService.showChatMap.set(libro.id, false);
27
+ view.onDisposed(() => {
28
+ this.libroAINativeService.chatViewMap.delete(libro.id);
29
+ });
30
+ return view;
31
+ };
32
+ viewOpenOption = {
33
+ reveal: false,
34
+ order: 'a',
35
+ };
36
+ }