@mybricks/plugin-ai 0.0.1 → 0.0.2

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 (93) hide show
  1. package/package.json +7 -2
  2. package/src/agents/app.ts +188 -60
  3. package/src/agents/common.ts +428 -68
  4. package/src/agents/custom.ts +14 -0
  5. package/src/agents/index.ts +31 -1
  6. package/src/agents/knowledges/README.md +614 -0
  7. package/src/agents/knowledges/SUMMARY.md +527 -0
  8. package/src/agents/knowledges/index.ts +8 -0
  9. package/src/agents/knowledges/knowledge-base.ts +565 -0
  10. package/src/agents/knowledges/knowledge-node.ts +266 -0
  11. package/src/agents/knowledges/types.ts +208 -0
  12. package/src/agents/utils/config.ts +427 -0
  13. package/src/agents/workspace/coding-manager.ts +31 -0
  14. package/src/agents/workspace/components-manager.ts +124 -0
  15. package/src/agents/workspace/outline-focus.ts +188 -0
  16. package/src/agents/workspace/outline-info.ts +520 -0
  17. package/src/agents/workspace/page-tree-generator.ts +83 -0
  18. package/src/agents/workspace/workspace.ts +319 -0
  19. package/src/agents/workspace-by-knowledges/MIGRATION.md +568 -0
  20. package/src/agents/workspace-by-knowledges/README.md +521 -0
  21. package/src/agents/workspace-by-knowledges/index.ts +11 -0
  22. package/src/agents/workspace-by-knowledges/providers/component-docs-provider.ts +92 -0
  23. package/src/agents/workspace-by-knowledges/providers/focus-info-provider.ts +131 -0
  24. package/src/agents/workspace-by-knowledges/providers/index.ts +8 -0
  25. package/src/agents/workspace-by-knowledges/providers/project-info-provider.ts +151 -0
  26. package/src/agents/workspace-by-knowledges/test.ts +240 -0
  27. package/src/agents/workspace-by-knowledges/types.ts +56 -0
  28. package/src/agents/workspace-by-knowledges/utils/components-manager.ts +145 -0
  29. package/src/agents/workspace-by-knowledges/utils/index.ts +8 -0
  30. package/src/agents/workspace-by-knowledges/utils/outline-focus.ts +178 -0
  31. package/src/agents/workspace-by-knowledges/utils/outline-info.ts +521 -0
  32. package/src/agents/workspace-by-knowledges/workspace.ts +166 -0
  33. package/src/api/cloud-components.ts +129 -0
  34. package/src/api-record-replay/README.md +187 -0
  35. package/src/api-record-replay/index.ts +11 -0
  36. package/src/api-record-replay/manager.ts +168 -0
  37. package/src/api-record-replay/recorder.ts +117 -0
  38. package/src/api-record-replay/replayer.ts +148 -0
  39. package/src/components/attachments/index.less +117 -0
  40. package/src/components/attachments/index.tsx +136 -0
  41. package/src/components/icons/index.tsx +21 -1
  42. package/src/components/index.less +34 -0
  43. package/src/components/mention/index.less +23 -0
  44. package/src/components/mention/index.tsx +19 -0
  45. package/src/components/messages/index.less +444 -237
  46. package/src/components/messages/index.tsx +371 -88
  47. package/src/components/sender/index.less +203 -0
  48. package/src/components/sender/index.tsx +298 -0
  49. package/src/components/types.ts +31 -0
  50. package/src/constants/index.ts +8 -0
  51. package/src/context/RequestStatusTracker.ts +50 -0
  52. package/src/context/index.ts +68 -6
  53. package/src/{types.d.ts → global.d.ts} +40 -5
  54. package/src/index.tsx +212 -32
  55. package/src/preset/agents.ts +380 -0
  56. package/src/preset/createTemplates.ts +25 -0
  57. package/src/preset/index.ts +12 -0
  58. package/src/preset/prompts.ts +235 -0
  59. package/src/preset/requestAsStream.ts +246 -0
  60. package/src/preset/user.ts +6 -0
  61. package/src/startView/components/header/header.less +17 -0
  62. package/src/startView/components/header/header.tsx +15 -0
  63. package/src/startView/components/index.ts +1 -0
  64. package/src/startView/index.less +22 -204
  65. package/src/startView/index.tsx +35 -203
  66. package/src/tools/analyze-and-expand-prd.ts +192 -86
  67. package/src/tools/analyze-requirement-and-components.ts +589 -0
  68. package/src/tools/answer.ts +59 -0
  69. package/src/tools/build-process.ts +1174 -0
  70. package/src/tools/coding-subagent-as-tool.ts +119 -0
  71. package/src/tools/generate-ui-content.ts +1083 -0
  72. package/src/tools/index.ts +22 -19
  73. package/src/tools/open-dsl.ts +69 -0
  74. package/src/tools/refactor-ui-content.ts +801 -0
  75. package/src/tools/utils.ts +880 -28
  76. package/src/types/index.ts +4 -0
  77. package/src/view/components/header/header.less +36 -2
  78. package/src/view/components/header/header.tsx +47 -2
  79. package/src/view/components/index.ts +0 -2
  80. package/src/view/index.tsx +158 -8
  81. package/src/tools/answer-user.ts +0 -35
  82. package/src/tools/focus-element.ts +0 -47
  83. package/src/tools/generate-page.ts +0 -750
  84. package/src/tools/get-component-info-by-ids.ts +0 -166
  85. package/src/tools/get-component-info.ts +0 -53
  86. package/src/tools/get-components-doc-and-prd.ts +0 -137
  87. package/src/tools/get-focus-mybricks-dsl.ts +0 -26
  88. package/src/tools/get-mybricks-dsl.ts +0 -73
  89. package/src/tools/modify-component.ts +0 -385
  90. package/src/view/components/messages/messages.less +0 -228
  91. package/src/view/components/messages/messages.tsx +0 -172
  92. package/src/view/components/sender/sender.less +0 -44
  93. package/src/view/components/sender/sender.tsx +0 -62
@@ -0,0 +1,521 @@
1
+ import { ComponentsManager } from "./components-manager";
2
+
3
+ export interface SlotInfo {
4
+ id: string;
5
+ title?: string;
6
+ layout?: any;
7
+ components?: OutlineNode[];
8
+ }
9
+
10
+ export interface OutlineNode {
11
+ id: string;
12
+ title: string;
13
+ def?: {
14
+ namespace?: string;
15
+ };
16
+ asRoot?: boolean;
17
+ data?: any;
18
+ style?: any;
19
+ slots?: SlotInfo[];
20
+ _hasCollapsedChildren?: boolean;
21
+ }
22
+
23
+ // 设计器给出的 OutlineNode
24
+ export interface OriginOutlineNode extends OutlineNode {
25
+ components: OutlineNode[]
26
+ layout: {
27
+ width: string | number
28
+ height: string | number
29
+ }
30
+ }
31
+
32
+
33
+ export interface ComponentsResult {
34
+ id: string;
35
+ jsx: string;
36
+ namespaces: string[];
37
+ }
38
+
39
+ const ROOT_NAMESPACE = 'root';
40
+ const ROOT_ID = '_root_';
41
+ const ROOT_SLOT_ID = '_rootSlot_';
42
+
43
+ export class OutlineInfoManager {
44
+
45
+ api: any;
46
+
47
+ constructor({ api }: { api: any }) {
48
+ this.api = api;
49
+ }
50
+
51
+ private getOutlineInfo(id: string, type: string): OriginOutlineNode {
52
+ if (type === "logicCom") {
53
+ return this.api?.logicCom?.api?.getOutlineInfo(id)
54
+ } else if (type === "uiCom") {
55
+ return this.api?.uiCom?.api?.getOutlineInfo(id)
56
+ } else {
57
+ return this.api?.page?.api?.getOutlineInfo(id)
58
+ }
59
+ }
60
+
61
+ getPageOutline(pageId: string) {
62
+ return this.normalizePageOutline(this.getOutlineInfo(pageId, 'page'), pageId);
63
+ }
64
+
65
+ getUiComOutline(componentId: string) {
66
+ return this.getOutlineInfo(componentId, 'uiCom');
67
+ }
68
+
69
+ getLogicComOutline(componentId: string) {
70
+ return this.getOutlineInfo(componentId, 'logicCom');
71
+ }
72
+
73
+ private getOriginOutlineRootCom = (originOutline: OriginOutlineNode) => {
74
+ return originOutline?.components?.[0]?.asRoot ? originOutline?.components?.[0] : null
75
+ }
76
+
77
+ getPageMetaInfo(pageId: string) {
78
+ const originOutlineJson = this.getOutlineInfo(pageId, 'page')
79
+
80
+ const rootId = this.getOriginOutlineRootCom(originOutlineJson) ? this.getOriginOutlineRootCom(originOutlineJson)?.id : undefined;
81
+
82
+ return {
83
+ pageId,
84
+ rootId
85
+ }
86
+ }
87
+
88
+
89
+ private normalizePageOutline(outline: OriginOutlineNode, pageId: string): OutlineNode {
90
+ if (outline?.id === pageId) {
91
+
92
+ let rootNode: OutlineNode = outline
93
+ let rootSlots: SlotInfo[] | undefined = [{
94
+ id: ROOT_SLOT_ID,
95
+ components: outline?.components,
96
+ layout: outline?.layout
97
+ }];
98
+ const whInfo: any = {
99
+ width: outline?.layout?.width,
100
+ height: outline?.layout?.height,
101
+ }
102
+
103
+ // 说明有asRoot组件,把asRoot的信息往上提取一层
104
+ const rootCom = this.getOriginOutlineRootCom(outline)
105
+ if (!!rootCom) {
106
+ rootNode = rootCom;
107
+ rootSlots = rootNode.slots
108
+ }
109
+
110
+ const normalized = {
111
+ ...rootNode,
112
+ style: {
113
+ ...(rootNode.style ?? {}),
114
+ ...whInfo, // 注意用的是页面的宽高
115
+ },
116
+ slots: rootSlots,
117
+ def: {
118
+ version: '1.0.0',
119
+ namespace: ROOT_NAMESPACE
120
+ },
121
+ asRoot: true
122
+ };
123
+
124
+ return {
125
+ id: pageId,
126
+ title: outline.title, // 注意用的是页面的title
127
+ slots: [{ id: ROOT_ID, components: [normalized] }]
128
+ };
129
+ }
130
+
131
+ return {
132
+ id: pageId,
133
+ title: outline.title,
134
+ slots: [{ id: ROOT_ID, components: [outline] }]
135
+ };
136
+ }
137
+
138
+ getComponentIdToTitleMap(pageId: string) {
139
+ const outline = this.getPageOutline(pageId);
140
+ const componentMap = new Map<string, string>();
141
+
142
+ componentMap.set(ROOT_ID, outline.title ?? '页面根节点');
143
+
144
+ function traverse(data: any) {
145
+ if (!data) return;
146
+
147
+ if (Array.isArray(data)) {
148
+ data.forEach(item => traverse(item));
149
+ return;
150
+ }
151
+
152
+ if (data.id && data.title) {
153
+ componentMap.set(data.id, data.title);
154
+ }
155
+
156
+ if (data.slots && Array.isArray(data.slots)) {
157
+ data.slots.forEach((slot: SlotInfo) => {
158
+ if (slot.components && Array.isArray(slot.components)) {
159
+ slot.components.forEach(component => traverse(component));
160
+ }
161
+ });
162
+ }
163
+ }
164
+
165
+ traverse(outline);
166
+ return componentMap;
167
+ }
168
+
169
+ generateJSXByPageId(pageId: string, targetComponentIds: string[] = []): ComponentsResult {
170
+ const outline = this.getPageOutline(pageId);
171
+ return this.generateJSXByOutline(outline, targetComponentIds);
172
+ }
173
+
174
+ generateJSXByOutline(outlineInfo: OutlineNode, targetComponentIds: string[] = []): ComponentsResult {
175
+ return OutlineJSXGenerator.generate(outlineInfo, targetComponentIds);
176
+ }
177
+
178
+ findParentNodeByComId(pageOutlineInfo: OutlineNode, comId: string): OutlineNode | null {
179
+ function helper(node: OutlineNode): OutlineNode | null {
180
+ if (!node || !node.slots) return null;
181
+ for (const slot of node.slots || []) {
182
+ if (slot.components && Array.isArray(slot.components)) {
183
+ for (const component of slot.components) {
184
+ if (component.id === comId) {
185
+ return node;
186
+ }
187
+ // 向下递归
188
+ const found = helper(component);
189
+ if (found) return found;
190
+ }
191
+ }
192
+ }
193
+ return null;
194
+ }
195
+ return helper(pageOutlineInfo);
196
+ }
197
+ }
198
+
199
+ class OutlineJSXGenerator {
200
+ private static namespacesSet = new Set<string>();
201
+
202
+ /**
203
+ * 遮蔽base64和svg代码,避免DSL过大
204
+ */
205
+ private static maskLargeContent(obj: any): any {
206
+ if (obj === null || obj === undefined) {
207
+ return obj;
208
+ }
209
+
210
+ // 如果是字符串,检查是否为base64或svg
211
+ if (typeof obj === 'string') {
212
+ // 检测base64图片(data:image开头或长base64字符串)
213
+ if (obj.startsWith('data:image/') || /^[A-Za-z0-9+/=]{100,}$/.test(obj)) {
214
+ const prefix = obj.substring(0, 22);
215
+ return `[BASE64_MASKED:${prefix}...length:${obj.length}]`;
216
+ }
217
+ // 检测SVG代码
218
+ if (obj.includes('<svg') || obj.includes('<?xml') && obj.includes('svg')) {
219
+ const length = obj.length;
220
+ if (length > 200) {
221
+ return `[SVG_MASKED:length:${length}]`;
222
+ }
223
+ }
224
+ return obj;
225
+ }
226
+
227
+ // 如果是数组,递归处理每个元素
228
+ if (Array.isArray(obj)) {
229
+ return obj.map(item => this.maskLargeContent(item));
230
+ }
231
+
232
+ // 如果是对象,递归处理每个属性
233
+ if (typeof obj === 'object') {
234
+ const result: any = {};
235
+ for (const key in obj) {
236
+ if (obj.hasOwnProperty(key)) {
237
+ result[key] = this.maskLargeContent(obj[key]);
238
+ }
239
+ }
240
+ return result;
241
+ }
242
+
243
+ return obj;
244
+ }
245
+
246
+ static generate(outlineInfo: OutlineNode, targetComponentIds: string[] = []): ComponentsResult {
247
+ this.namespacesSet.clear();
248
+
249
+ if (targetComponentIds.length === 0) {
250
+ const jsx = this.processData(outlineInfo);
251
+ return {
252
+ id: outlineInfo.id,
253
+ jsx,
254
+ namespaces: Array.from(this.namespacesSet)
255
+ };
256
+ }
257
+
258
+ if (targetComponentIds.length === 1) {
259
+ const targetNode = this.findNodeById(outlineInfo, targetComponentIds[0]);
260
+ if (targetNode) {
261
+ const jsx = this.processData(targetNode);
262
+ return {
263
+ id: targetNode.id,
264
+ jsx,
265
+ namespaces: Array.from(this.namespacesSet)
266
+ };
267
+ }
268
+ }
269
+
270
+ const ancestorNodes = this.findMinimalCommonAncestors(outlineInfo, targetComponentIds);
271
+ const jsx = ancestorNodes.map(node => this.processData(node)).join('\n');
272
+
273
+ return {
274
+ id: ancestorNodes[0]?.id,
275
+ jsx,
276
+ namespaces: Array.from(this.namespacesSet)
277
+ };
278
+ }
279
+
280
+ private static findMinimalCommonAncestors(root: OutlineNode, targetIds: string[]): OutlineNode[] {
281
+ if (targetIds.length === 0) return [root];
282
+ if (targetIds.length === 1) {
283
+ const targetNode = this.findNodeById(root, targetIds[0]);
284
+ return targetNode ? [targetNode] : [];
285
+ }
286
+
287
+ const paths: OutlineNode[][] = [];
288
+ for (const targetId of targetIds) {
289
+ const path = this.findPathToNode(root, targetId);
290
+ if (path) {
291
+ paths.push(path);
292
+ }
293
+ }
294
+
295
+ if (paths.length === 0) {
296
+ return [];
297
+ }
298
+ if (paths.length === 1) {
299
+ return [paths[0][paths[0].length - 1]];
300
+ }
301
+
302
+ let commonAncestor: OutlineNode | null = null;
303
+ const minLength = Math.min(...paths.map(path => path.length));
304
+
305
+ for (let i = 0; i < minLength; i++) {
306
+ const currentNodes = paths.map(path => path[i]);
307
+ const firstNode = currentNodes[0];
308
+
309
+ if (currentNodes.every(node => node.id === firstNode.id)) {
310
+ commonAncestor = firstNode;
311
+ } else {
312
+ break;
313
+ }
314
+ }
315
+
316
+ if (commonAncestor) {
317
+ return [commonAncestor];
318
+ }
319
+
320
+ return [root];
321
+ }
322
+
323
+ private static findPathToNode(root: OutlineNode, targetId: string): OutlineNode[] | null {
324
+ if (root.id === targetId) {
325
+ return [root];
326
+ }
327
+
328
+ if (root.slots && Array.isArray(root.slots)) {
329
+ for (const slot of root.slots) {
330
+ if (slot.components && Array.isArray(slot.components)) {
331
+ for (const component of slot.components) {
332
+ const path = this.findPathToNode(component, targetId);
333
+ if (path) {
334
+ return [root, ...path];
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+
341
+ return null;
342
+ }
343
+
344
+ private static findNodeById(root: OutlineNode, targetId: string): OutlineNode | null {
345
+ if (root.id === targetId) {
346
+ return root;
347
+ }
348
+
349
+ if (root.slots && Array.isArray(root.slots)) {
350
+ for (const slot of root.slots) {
351
+ if (slot.components && Array.isArray(slot.components)) {
352
+ for (const component of slot.components) {
353
+ const found = this.findNodeById(component, targetId);
354
+ if (found) return found;
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ return null;
361
+ }
362
+
363
+ private static extractLayout(style: any): Record<string, any> {
364
+ if (!style) return {};
365
+
366
+ const layout: Record<string, any> = {};
367
+
368
+ ['width', 'height', 'margin', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom']
369
+ .forEach(prop => {
370
+ if (style[prop] !== undefined) {
371
+ layout[prop] = style[prop];
372
+ }
373
+ });
374
+
375
+ if (style.layout !== undefined) {
376
+ if (style.layout === 'flex-column' || style.layout === 'flex') {
377
+ layout.display = 'flex';
378
+ layout.flexDirection = 'column';
379
+ }
380
+ if (style.layout === 'flex-row') {
381
+ layout.display = 'flex';
382
+ layout.flexDirection = 'row';
383
+ }
384
+ if (style.alignItems) layout.alignItems = style.alignItems;
385
+ if (style.justifyContent) layout.justifyContent = style.justifyContent;
386
+ if (style.layout === 'absolute') {
387
+ layout.position = 'relative';
388
+ }
389
+ }
390
+
391
+ if (style.position === 'absolute') {
392
+ ['left', 'right', 'top', 'bottom', 'widthFact', 'heightFact', 'position']
393
+ .forEach(prop => {
394
+ if (style[prop] !== undefined) {
395
+ layout[prop] = style[prop];
396
+ }
397
+ })
398
+ }
399
+
400
+ return layout;
401
+ }
402
+
403
+ private static extractStyleArray(style: any): string[] {
404
+ if (!style?.css || !Array.isArray(style.css)) return [];
405
+
406
+ return style.css.map((cssItem: any) => {
407
+ const selector = cssItem.selector || '';
408
+ const cssProps = cssItem.css || {};
409
+
410
+ const cssString = Object.entries(cssProps)
411
+ .map(([key, value]) => `${key}: '${value}'`)
412
+ .join(', ');
413
+
414
+ return `${selector} : { ${cssString} }`;
415
+ });
416
+ }
417
+
418
+ private static processData(node: OutlineNode | OutlineNode[]): string {
419
+ if (!node) return '';
420
+
421
+ if (Array.isArray(node)) {
422
+ return node.map(item => this.processData(item)).filter(Boolean).join('\n');
423
+ }
424
+
425
+ if (node.id && node.def?.namespace) {
426
+ return this.generateComponentJSX(node);
427
+ }
428
+
429
+ if (node.slots && Array.isArray(node.slots)) {
430
+ return node.slots.map(slot => {
431
+ if (slot.components && Array.isArray(slot.components)) {
432
+ return this.processData(slot.components);
433
+ }
434
+ return '';
435
+ }).filter(Boolean).join('');
436
+ }
437
+
438
+ return '';
439
+ }
440
+
441
+ private static generateComponentJSX(node: OutlineNode, indent = ''): string {
442
+ if (!node?.id) return '';
443
+
444
+ const namespace = node.def?.namespace;
445
+ if (namespace !== ROOT_NAMESPACE) {
446
+ this.namespacesSet.add(namespace);
447
+ }
448
+ const layout = this.extractLayout(node.style);
449
+ const styleArray = this.extractStyleArray(node.style);
450
+
451
+ let jsx;
452
+
453
+ const namespaceTag = ComponentsManager.getAbbreviation(namespace)
454
+
455
+ // 遮蔽data中的base64和svg内容
456
+ const maskedData = node.data ? this.maskLargeContent(node.data) : null;
457
+
458
+ if (node.asRoot) {
459
+ jsx = `<${ROOT_NAMESPACE} id="${ROOT_ID}"` + (maskedData ? ` data={${JSON.stringify(maskedData)}}` : '');
460
+ } else {
461
+ jsx = `<${namespaceTag} id="${node.id}" title="${node.title}"` + (maskedData ? ` data={${JSON.stringify(maskedData)}}` : '');
462
+ }
463
+
464
+ if (Object.keys(layout).length > 0) {
465
+ jsx += ` layout={${JSON.stringify(layout)}}`;
466
+ }
467
+
468
+ if (styleArray.length > 0) {
469
+ // 遮蔽styleArray中的base64和svg内容
470
+ const maskedStyleArray = styleArray.map(style => this.maskLargeContent(style));
471
+ jsx += ` styleAry={[${maskedStyleArray.map(style => `"${style}"`).join(', ')}]}`;
472
+ }
473
+
474
+ jsx += ' >';
475
+
476
+ const slotsJSX = this.generateSlotsJSX(node.slots || [], indent + ' ');
477
+ if (slotsJSX) {
478
+ jsx += slotsJSX;
479
+ jsx += `\n${indent}</${namespaceTag}>`;
480
+ } else {
481
+ jsx += ' />';
482
+ }
483
+
484
+ return jsx;
485
+ }
486
+
487
+ private static generateSlotsJSX(slots: SlotInfo[], indent = ' '): string {
488
+ if (!slots || slots.length === 0) return '';
489
+
490
+ let slotsJSX = '';
491
+ slots.forEach(slot => {
492
+ if (slot.id) {
493
+ slotsJSX += `\n${indent}<slots.${slot.id}`;
494
+
495
+ if (slot.title) {
496
+ slotsJSX += ` title="${slot.title}"`;
497
+ }
498
+
499
+ if (slot.layout) {
500
+ slotsJSX += ` layout={${JSON.stringify(this.extractLayout(slot.layout))}}`;
501
+ }
502
+
503
+ slotsJSX += '>';
504
+
505
+ if (slot.components && Array.isArray(slot.components)) {
506
+ slot.components.forEach(component => {
507
+ const childJSX = this.generateComponentJSX(component, indent + ' ');
508
+ if (childJSX) {
509
+ slotsJSX += `\n${indent} ${childJSX}`;
510
+ }
511
+ });
512
+ }
513
+
514
+ slotsJSX += `\n${indent}</slots.${slot.id}>`;
515
+ }
516
+ });
517
+
518
+ return slotsJSX;
519
+ }
520
+ }
521
+
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Workspace 类(基于知识库架构重构 + Context)
3
+ *
4
+ * 从 context 获取所有依赖,无需手动传入 api 和 focusInfo
5
+ */
6
+
7
+ import { KnowledgeBase } from '../knowledges/knowledge-base';
8
+ import { WorkspaceConfig } from './types';
9
+ import { DynamicDocument } from '../knowledges/types';
10
+ import {
11
+ createProjectInfoProvider,
12
+ createFocusInfoProvider,
13
+ createComponentDocsProvider
14
+ } from './providers';
15
+ import { ComponentsManager } from './utils';
16
+
17
+ /**
18
+ * Workspace 类
19
+ * 继承自 KnowledgeBase,提供工作空间的知识库视图
20
+ */
21
+ export class Workspace extends KnowledgeBase {
22
+ private defaultFilesOpened: Promise<void>;
23
+
24
+ constructor(config?: WorkspaceConfig) {
25
+ super({
26
+ name: config?.name || 'Workspace',
27
+ description: config?.description || '工作空间包含项目信息、聚焦信息和组件配置文档'
28
+ });
29
+
30
+ // 初始化 providers
31
+ this.initializeProviders();
32
+
33
+ // 默认打开项目信息和聚焦信息(保存 Promise 以便外部可以等待)
34
+ this.defaultFilesOpened = this.openDefaultFiles();
35
+ }
36
+
37
+ /**
38
+ * 初始化所有 providers
39
+ */
40
+ private initializeProviders(): void {
41
+ // 1. 项目信息 Provider(包含画布索引)
42
+ this.registerProvider(createProjectInfoProvider());
43
+
44
+ // 2. 聚焦信息 Provider(包含聚焦树结构和描述)
45
+ this.registerProvider(createFocusInfoProvider());
46
+
47
+ // 3. 组件配置文档 Provider(隐藏目录)
48
+ this.registerProvider(createComponentDocsProvider());
49
+ }
50
+
51
+ /**
52
+ * 默认打开项目信息和聚焦信息
53
+ */
54
+ private async openDefaultFiles(): Promise<void> {
55
+ try {
56
+ // 打开项目信息
57
+ await this.openFile('项目信息.md');
58
+
59
+ // 打开聚焦信息
60
+ await this.openFile('聚焦信息.md');
61
+ } catch (error) {
62
+ console.warn('Failed to open default files:', error);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 等待默认文件打开完成
68
+ */
69
+ async waitForReady(): Promise<void> {
70
+ await this.defaultFilesOpened;
71
+ }
72
+
73
+ /**
74
+ * 打开组件文档(通过路径)
75
+ * @example await workspace.openComponentDoc('组件配置文档/pc.button.md')
76
+ */
77
+ async openComponentDoc(path: string): Promise<void> {
78
+ await this.defaultFilesOpened; // 确保默认文件已打开
79
+ await this.openFile(path);
80
+
81
+ // 自动打开依赖组件
82
+ const match = path.match(/组件配置文档\/(.+)\.md$/);
83
+ if (match) {
84
+ const abbreviation = match[1];
85
+ const namespace = ComponentsManager.getFullNamespace(abbreviation);
86
+ const requires = ComponentsManager.getRequireComponents(namespace);
87
+
88
+ if (requires && requires.length > 0) {
89
+ for (const requiredNs of requires) {
90
+ const requiredAbbr = ComponentsManager.getAbbreviation(requiredNs);
91
+ const requiredPath = `组件配置文档/${requiredAbbr}.md`;
92
+ try {
93
+ await this.openFile(requiredPath);
94
+ } catch (error) {
95
+ console.warn(`Failed to auto-open required component ${requiredPath}:`, error);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * 根据组件 namespace 打开组件文档
104
+ */
105
+ async openComponentDocByNamespace(namespace: string): Promise<void> {
106
+ const abbreviation = ComponentsManager.getAbbreviation(namespace);
107
+ const path = `组件配置文档/${abbreviation}.md`;
108
+ await this.openComponentDoc(path);
109
+ }
110
+
111
+ /**
112
+ * 打开文档(类似旧版 workspace 的 openDocument)
113
+ * @param id 文档ID(页面ID或组件ID)
114
+ * @param options 文档选项
115
+ */
116
+ async openDocument(id: string, options: {
117
+ type: '画布' | '组件';
118
+ content: string;
119
+ title?: string;
120
+ description?: string;
121
+ /** 所属目录ID,如果不指定则使用默认动态文档目录 */
122
+ directoryId?: string;
123
+ }): Promise<void> {
124
+ await this.defaultFilesOpened; // 确保默认文件已打开
125
+
126
+ const doc: DynamicDocument = {
127
+ id,
128
+ title: options.title || id,
129
+ description: options.description,
130
+ content: options.content,
131
+ docType: options.type,
132
+ extname: '.md',
133
+ directoryId: options.directoryId
134
+ };
135
+
136
+ // 使用 KnowledgeBase 的底层动态文档功能
137
+ await this.openDynamicDocument(doc);
138
+ }
139
+
140
+ /**
141
+ * 关闭文档
142
+ * @param id 文档ID
143
+ * @param directoryId 目录ID(可选)
144
+ */
145
+ closeDocument(id: string, directoryId?: string): void {
146
+ this.removeDynamicDocument(id, directoryId);
147
+ }
148
+
149
+ /**
150
+ * 刷新工作空间(当 context.currentFocus 变化时调用)
151
+ */
152
+ async refresh(): Promise<void> {
153
+ // 清空所有已打开的文件
154
+ this.clearOpenedFiles();
155
+
156
+ // 清空所有动态文档
157
+ this.clearDynamicDocuments();
158
+
159
+ // 重新初始化 providers
160
+ this.initializeProviders();
161
+
162
+ // 重新打开默认文件
163
+ this.defaultFilesOpened = this.openDefaultFiles();
164
+ await this.defaultFilesOpened;
165
+ }
166
+ }