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:
DarkSky
2026-03-08 00:53:16 +08:00
committed by GitHub
parent 9742e9735e
commit 9c55edeb62
36 changed files with 980 additions and 375 deletions
@@ -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>
`,
@@ -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,