mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 22:09:08 +08:00
feat(server): adapt gemini3.1 preview (#14583)
#### PR Dependency Tree * **PR #14583** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Gemini 3.1 Pro Preview support (text, image, audio) and new GPT‑5 variants as defaults; centralized persistent telemetry state for more reliable client identity. * **UX** * Improved model submenu placement in chat preferences. * More robust mindmap parsing, preview, regeneration and replace behavior. * **Chores** * Bumped AI SDK and related dependencies. * **Tests** * Expanded/updated tests and increased timeouts for flaky flows. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -26,10 +26,17 @@ import {
|
||||
ThinkingIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { ShadowlessElement } from '@blocksuite/std';
|
||||
import { autoPlacement, offset, shift } from '@floating-ui/dom';
|
||||
import { computed } from '@preact/signals-core';
|
||||
import { css, html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
const modelSubMenuMiddleware = [
|
||||
autoPlacement({ allowedPlacements: ['right-start', 'left-start'] }),
|
||||
offset({ mainAxis: 4, crossAxis: 0 }),
|
||||
shift({ crossAxis: true, padding: 8 }),
|
||||
];
|
||||
|
||||
export class ChatInputPreference extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
@@ -140,6 +147,7 @@ export class ChatInputPreference extends SignalWatcher(
|
||||
menu.subMenu({
|
||||
name: 'Model',
|
||||
prefix: AiOutlineIcon(),
|
||||
middleware: modelSubMenuMiddleware,
|
||||
postfix: html`
|
||||
<span class="ai-active-model-name"> ${this.model.value?.name} </span>
|
||||
`,
|
||||
|
||||
+65
@@ -99,4 +99,69 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
|
||||
|
||||
expect(nodes).toEqual(null);
|
||||
});
|
||||
|
||||
test('accepts leading plain text before the markdown list', () => {
|
||||
const markdown = `Here is the regenerated mind map:
|
||||
|
||||
- Text A
|
||||
- Text B`;
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc().getStore();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
|
||||
expect(nodes).toEqual({
|
||||
text: 'Text A',
|
||||
children: [
|
||||
{
|
||||
text: 'Text B',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('accepts markdown lists wrapped in a code block', () => {
|
||||
const markdown = `\`\`\`markdown
|
||||
- Text A
|
||||
- Text B
|
||||
\`\`\``;
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc().getStore();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
|
||||
expect(nodes).toEqual({
|
||||
text: 'Text A',
|
||||
children: [
|
||||
{
|
||||
text: 'Text B',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('keeps inline markdown content inside node labels', () => {
|
||||
const markdown = `
|
||||
- Root with [link](https://example.com) and [^1]
|
||||
- Child with \`code\`
|
||||
|
||||
[^1]: footnote
|
||||
`;
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc().getStore();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
|
||||
expect(nodes).toEqual({
|
||||
text: 'Root with link and',
|
||||
children: [
|
||||
{
|
||||
text: 'Child with code',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ import { css, html, LitElement, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import type { Root } from 'mdast';
|
||||
import type { Root, RootContent } from 'mdast';
|
||||
import { Doc as YDoc } from 'yjs';
|
||||
|
||||
import { MiniMindmapSchema, MiniMindmapSpecs } from './spec.js';
|
||||
@@ -234,19 +234,68 @@ type Node = {
|
||||
children: Node[];
|
||||
};
|
||||
|
||||
type MarkdownNode =
|
||||
| RootContent
|
||||
| { alt?: string | null; children?: MarkdownNode[]; value?: string };
|
||||
|
||||
export const markdownToMindmap = (
|
||||
answer: string,
|
||||
doc: Store,
|
||||
provider: ServiceProvider
|
||||
) => {
|
||||
let result: Node | null = null;
|
||||
const transformer = doc.getTransformer();
|
||||
const markdown = new MarkdownAdapter(transformer, provider);
|
||||
const ast: Root = markdown['_markdownToAst'](answer);
|
||||
const astToMindmap = (ast: Root): Node | null => {
|
||||
const findList = (
|
||||
nodes: Root['children']
|
||||
): Unpacked<Root['children']> | null => {
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'list') {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.type === 'code' && node.value) {
|
||||
const nestedAst: Root = markdown['_markdownToAst'](node.value);
|
||||
const nestedList = findList(nestedAst.children);
|
||||
if (nestedList) {
|
||||
return nestedList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const list = findList(ast.children);
|
||||
if (!list) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return traverse(list, true);
|
||||
};
|
||||
|
||||
const traverse = (
|
||||
markdownNode: Unpacked<(typeof ast)['children']>,
|
||||
markdownNode: Unpacked<Root['children']>,
|
||||
firstLevel = false
|
||||
): Node | null => {
|
||||
const toPlainText = (node: MarkdownNode): string => {
|
||||
if ('value' in node && typeof node.value === 'string') {
|
||||
return node.value;
|
||||
}
|
||||
|
||||
if ('alt' in node && typeof node.alt === 'string') {
|
||||
return node.alt;
|
||||
}
|
||||
|
||||
if ('children' in node && Array.isArray(node.children)) {
|
||||
return node.children
|
||||
.map((child: MarkdownNode) => toPlainText(child))
|
||||
.join('');
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
switch (markdownNode.type) {
|
||||
case 'list':
|
||||
{
|
||||
@@ -267,11 +316,11 @@ export const markdownToMindmap = (
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (
|
||||
paragraph?.type === 'paragraph' &&
|
||||
paragraph.children[0]?.type === 'text'
|
||||
) {
|
||||
node.text = paragraph.children[0].value;
|
||||
if (paragraph?.type === 'paragraph') {
|
||||
node.text = paragraph.children
|
||||
.map((child: MarkdownNode) => toPlainText(child))
|
||||
.join('')
|
||||
.trim();
|
||||
}
|
||||
|
||||
if (list?.type === 'list') {
|
||||
@@ -287,9 +336,5 @@ export const markdownToMindmap = (
|
||||
return null;
|
||||
};
|
||||
|
||||
if (ast?.children?.[0]?.type === 'list') {
|
||||
result = traverse(ast.children[0], true);
|
||||
}
|
||||
|
||||
return result;
|
||||
return astToMindmap(markdown['_markdownToAst'](answer));
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ThemeService,
|
||||
} from '@blocksuite/affine/shared/services';
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/affine/std';
|
||||
import { ToolController } from '@blocksuite/affine/std/gfx';
|
||||
import type { BlockSchema, ExtensionType } from '@blocksuite/affine/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
import type { z } from 'zod';
|
||||
@@ -24,6 +25,7 @@ export const MiniMindmapSpecs: ExtensionType[] = [
|
||||
ThemeService,
|
||||
FlavourExtension('affine:page'),
|
||||
MindmapService,
|
||||
ToolController,
|
||||
BlockViewExtension('affine:page', literal`mini-mindmap-root-block`),
|
||||
FlavourExtension('affine:surface'),
|
||||
MindMapView,
|
||||
|
||||
Reference in New Issue
Block a user