snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-16 00:31:07 +05:00
commit 50f788e799
7907 changed files with 988785 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
{
"name": "@blocksuite/affine-block-latex",
"description": "Latex block for BlockSuite.",
"type": "module",
"scripts": {
"build": "yarn run -T tsc"
},
"sideEffects": false,
"keywords": [],
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@blocksuite/affine-block-note": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-inline-latex": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/icons": "^2.2.17",
"@blocksuite/std": "workspace:*",
"@blocksuite/store": "workspace:*",
"@floating-ui/dom": "^1.6.13",
"@preact/signals-core": "^1.8.0",
"katex": "^0.16.27",
"lit": "^3.2.0",
"remark-math": "^6.0.0"
},
"exports": {
".": "./src/index.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},
"files": [
"src",
"dist",
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.27.5"
}

View File

@@ -0,0 +1,11 @@
import type { ExtensionType } from '@blocksuite/store';
import { LatexMarkdownAdapterExtensions } from './markdown/index.js';
import { LatexBlockNotionHtmlAdapterExtension } from './notion-html.js';
import { LatexBlockPlainTextAdapterExtension } from './plain-text.js';
export const LatexBlockAdapterExtensions: ExtensionType[] = [
LatexMarkdownAdapterExtensions,
LatexBlockNotionHtmlAdapterExtension,
LatexBlockPlainTextAdapterExtension,
].flat();

View File

@@ -0,0 +1,3 @@
export * from './markdown/index.js';
export * from './notion-html.js';
export * from './plain-text.js';

View File

@@ -0,0 +1,12 @@
import type { ExtensionType } from '@blocksuite/store';
import { LatexBlockMarkdownAdapterExtension } from './markdown.js';
import { LatexMarkdownPreprocessorExtension } from './preprocessor.js';
export * from './markdown.js';
export * from './preprocessor.js';
export const LatexMarkdownAdapterExtensions: ExtensionType[] = [
LatexMarkdownPreprocessorExtension,
LatexBlockMarkdownAdapterExtension,
];

View File

@@ -0,0 +1,55 @@
import { LatexBlockSchema } from '@blocksuite/affine-model';
import {
BlockMarkdownAdapterExtension,
type BlockMarkdownAdapterMatcher,
type MarkdownAST,
} from '@blocksuite/affine-shared/adapters';
import { nanoid } from '@blocksuite/store';
const isLatexNode = (node: MarkdownAST) => node.type === 'math';
export const latexBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
flavour: LatexBlockSchema.model.flavour,
toMatch: o => isLatexNode(o.node),
fromMatch: o => o.node.flavour === LatexBlockSchema.model.flavour,
toBlockSnapshot: {
enter: (o, context) => {
const latex = 'value' in o.node ? o.node.value : '';
const { walkerContext } = context;
walkerContext
.openNode(
{
type: 'block',
id: nanoid(),
flavour: 'affine:latex',
props: {
latex,
},
children: [],
},
'children'
)
.closeNode();
},
},
fromBlockSnapshot: {
enter: (o, context) => {
const latex =
'latex' in o.node.props ? (o.node.props.latex as string) : '';
const { walkerContext } = context;
walkerContext
.openNode(
{
type: 'math',
value: latex,
},
'children'
)
.closeNode();
},
},
};
export const LatexBlockMarkdownAdapterExtension = BlockMarkdownAdapterExtension(
latexBlockMarkdownAdapterMatcher
);

View File

@@ -0,0 +1,88 @@
import {
type MarkdownAdapterPreprocessor,
MarkdownPreprocessorExtension,
} from '@blocksuite/affine-shared/adapters';
function escapeBrackets(text: string) {
const pattern =
/(```[\S\s]*?```|`.*?`)|\\\[([\S\s]*?[^\\])\\]|\\\((.*?)\\\)/g;
return text.replaceAll(
pattern,
(match, codeBlock, squareBracket, roundBracket) => {
if (codeBlock) {
return codeBlock;
} else if (squareBracket) {
return `$$${squareBracket}$$`;
} else if (roundBracket) {
return `$${roundBracket}$`;
}
return match;
}
);
}
function escapeMhchem(text: string) {
return text.replaceAll('$\\ce{', '$\\\\ce{').replaceAll('$\\pu{', '$\\\\pu{');
}
/**
* Preprocess the content to protect code blocks and LaTeX expressions
* reference issue: https://github.com/remarkjs/react-markdown/issues/785
* reference comment: https://github.com/remarkjs/react-markdown/issues/785#issuecomment-2307567823
* @param content - The content to preprocess
* @returns The preprocessed content
*/
function preprocessLatex(content: string) {
// Protect code blocks
const codeBlocks: string[] = [];
let preprocessedContent = content;
preprocessedContent = preprocessedContent.replace(
/(```[\s\S]*?```|`[^`\n]+`)/g,
(_, code) => {
codeBlocks.push(code);
return `<<CODE_BLOCK_${codeBlocks.length - 1}>>`;
}
);
// Protect existing LaTeX expressions
const latexExpressions: string[] = [];
preprocessedContent = preprocessedContent.replace(
/(\$\$[\s\S]*?\$\$|\\\[[\s\S]*?\\\]|\\\(.*?\\\))/g,
match => {
latexExpressions.push(match);
return `<<LATEX_${latexExpressions.length - 1}>>`;
}
);
// Escape dollar signs that are likely currency indicators
preprocessedContent = preprocessedContent.replace(/\$(?=\d)/g, '\\$');
// Restore LaTeX expressions
preprocessedContent = preprocessedContent.replace(
/<<LATEX_(\d+)>>/g,
(_, index) => latexExpressions[parseInt(index)]
);
// Restore code blocks
preprocessedContent = preprocessedContent.replace(
/<<CODE_BLOCK_(\d+)>>/g,
(_, index) => codeBlocks[parseInt(index)]
);
// Apply additional escaping functions
preprocessedContent = escapeBrackets(preprocessedContent);
preprocessedContent = escapeMhchem(preprocessedContent);
return preprocessedContent;
}
const latexPreprocessor: MarkdownAdapterPreprocessor = {
name: 'latex',
levels: ['block', 'slice', 'doc'],
preprocess: content => {
return preprocessLatex(content);
},
};
export const LatexMarkdownPreprocessorExtension =
MarkdownPreprocessorExtension(latexPreprocessor);

View File

@@ -0,0 +1,50 @@
import { LatexBlockSchema } from '@blocksuite/affine-model';
import {
BlockNotionHtmlAdapterExtension,
type BlockNotionHtmlAdapterMatcher,
HastUtils,
} from '@blocksuite/affine-shared/adapters';
import { nanoid } from '@blocksuite/store';
export const latexBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatcher =
{
flavour: LatexBlockSchema.model.flavour,
toMatch: o => {
return (
HastUtils.isElement(o.node) &&
o.node.tagName === 'figure' &&
!!HastUtils.querySelector(o.node, '.equation-container')
);
},
fromMatch: () => false,
toBlockSnapshot: {
enter: (o, context) => {
if (!HastUtils.isElement(o.node)) {
return;
}
const { walkerContext } = context;
const latex = HastUtils.getTextContent(
HastUtils.querySelector(o.node, 'annotation')
);
walkerContext
.openNode(
{
type: 'block',
id: nanoid(),
flavour: LatexBlockSchema.model.flavour,
props: {
latex,
},
children: [],
},
'children'
)
.closeNode();
walkerContext.skipAllChildren();
},
},
fromBlockSnapshot: {},
};
export const LatexBlockNotionHtmlAdapterExtension =
BlockNotionHtmlAdapterExtension(latexBlockNotionHtmlAdapterMatcher);

View File

@@ -0,0 +1,29 @@
import { LatexBlockSchema } from '@blocksuite/affine-model';
import {
BlockPlainTextAdapterExtension,
type BlockPlainTextAdapterMatcher,
} from '@blocksuite/affine-shared/adapters';
const latexPrefix = 'LaTex, with value: ';
export const latexBlockPlainTextAdapterMatcher: BlockPlainTextAdapterMatcher = {
flavour: LatexBlockSchema.model.flavour,
toMatch: () => false,
fromMatch: o => o.node.flavour === LatexBlockSchema.model.flavour,
toBlockSnapshot: {},
fromBlockSnapshot: {
enter: (o, context) => {
const latex =
'latex' in o.node.props ? (o.node.props.latex as string) : '';
const { textBuffer } = context;
if (latex) {
textBuffer.content += `${latexPrefix}${latex}`;
textBuffer.content += '\n';
}
},
},
};
export const LatexBlockPlainTextAdapterExtension =
BlockPlainTextAdapterExtension(latexBlockPlainTextAdapterMatcher);

View File

@@ -0,0 +1,75 @@
import type { LatexProps } from '@blocksuite/affine-model';
import {
DocModeProvider,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import type { Command } from '@blocksuite/std';
import type { BlockModel } from '@blocksuite/store';
import { LatexBlockComponent } from './latex-block.js';
export const insertLatexBlockCommand: Command<
{
latex?: string;
place?: 'after' | 'before';
removeEmptyLine?: boolean;
selectedModels?: BlockModel[];
},
{
insertedLatexBlockId: Promise<string>;
}
> = (ctx, next) => {
const { selectedModels, latex, place, removeEmptyLine, std } = ctx;
if (!selectedModels?.length) return;
const targetModel =
place === 'before'
? selectedModels[0]
: selectedModels[selectedModels.length - 1];
const latexBlockProps: Partial<LatexProps> & {
flavour: 'affine:latex';
} = {
flavour: 'affine:latex',
latex: latex ?? '',
};
const result = std.store.addSiblingBlocks(
targetModel,
[latexBlockProps],
place
);
if (result.length === 0) return;
if (removeEmptyLine && targetModel.text?.length === 0) {
std.store.deleteBlock(targetModel);
}
next({
insertedLatexBlockId: std.host.updateComplete.then(async () => {
if (!latex) {
const blockComponent = std.view.getBlock(result[0]);
if (blockComponent instanceof LatexBlockComponent) {
await blockComponent.updateComplete;
blockComponent.toggleEditor();
const mode = std.get(DocModeProvider).getEditorMode() ?? 'page';
const ifEdgelessText = blockComponent.closest('affine-edgeless-text');
std.getOptional(TelemetryProvider)?.track('Latex', {
from:
mode === 'page'
? 'doc'
: ifEdgelessText
? 'edgeless text'
: 'edgeless note',
page: mode === 'page' ? 'doc' : 'edgeless',
segment: mode === 'page' ? 'doc' : 'whiteboard',
module: 'equation',
control: 'create equation',
});
}
}
return result[0];
}),
});
};

View File

@@ -0,0 +1,62 @@
import { insertInlineLatex } from '@blocksuite/affine-inline-latex';
import {
getSelectedModelsCommand,
getTextSelectionCommand,
} from '@blocksuite/affine-shared/commands';
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { TeXIcon } from '@blocksuite/icons/lit';
import { insertLatexBlockCommand } from '../commands';
import { LatexTooltip } from './tooltips';
export const latexSlashMenuConfig: SlashMenuConfig = {
items: [
{
name: 'Inline equation',
group: '0_Basic@8',
description: 'Create a inline equation.',
icon: TeXIcon(),
tooltip: {
figure: LatexTooltip(
'Energy. Mass. Light. In a single equation,',
'E=mc^2',
false
),
caption: 'Inline equation',
},
searchAlias: ['inlineMath, inlineEquation', 'inlineLatex'],
action: ({ std }) => {
std.command
.chain()
.pipe(getTextSelectionCommand)
.pipe(insertInlineLatex)
.run();
},
},
{
name: 'Equation',
description: 'Create a equation block.',
icon: TeXIcon(),
tooltip: {
figure: LatexTooltip(
'Create a equation via LaTeX.',
String.raw`\frac{a}{b} \pm \frac{c}{d} = \frac{ad \pm bc}{bd}`,
true
),
caption: 'Equation',
},
searchAlias: ['mathBlock, equationBlock', 'latexBlock'],
group: '4_Content & Media@10',
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertLatexBlockCommand, {
place: 'after',
removeEmptyLine: true,
})
.run();
},
},
],
};

View File

@@ -0,0 +1,42 @@
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { unsafeHTML } from '@blocksuite/affine-shared/utils';
import katex from 'katex';
import { html } from 'lit';
export const LatexTooltip = (
str: string,
latex: string,
displayMode: boolean = false
) =>
html` <style>
.latex-tooltip {
background: ${unsafeCSSVarV2('layer/pureWhite')};
border-radius: 2px;
width: 170px;
padding: 5px 5px 5px 6px;
box-sizing: border-box;
}
.latex-tooltip-content {
width: 159px;
color: #121212;
font-family: var(--affine-font-family);
font-size: 10px;
font-style: normal;
.katex > math[display='block'] {
margin-top: 1em;
}
}
</style>
<div class="latex-tooltip">
<div class="latex-tooltip-content">
<span>${str}</span>
${unsafeHTML(
katex.renderToString(latex, {
displayMode,
output: 'mathml',
}),
{ USE_PROFILES: { html: true, mathMl: true } }
)}
</div>
</div>`;

View File

@@ -0,0 +1,5 @@
import { LatexBlockComponent } from './latex-block';
export function effects() {
customElements.define('affine-latex', LatexBlockComponent);
}

View File

@@ -0,0 +1,8 @@
import type * as RemarkMathType from 'remark-math';
export * from './adapters';
export * from './commands';
export * from './latex-block';
// Global types
declare type _GLOBAl = typeof RemarkMathType;

View File

@@ -0,0 +1,158 @@
import { selectBlock } from '@blocksuite/affine-block-note';
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import { createLitPortal } from '@blocksuite/affine-components/portal';
import type { LatexBlockModel } from '@blocksuite/affine-model';
import { BlockSelection } from '@blocksuite/std';
import type { Placement } from '@floating-ui/dom';
import { effect } from '@preact/signals-core';
import katex from 'katex';
import { html, render } from 'lit';
import { query } from 'lit/decorators.js';
import { latexBlockStyles } from './styles.js';
export class LatexBlockComponent extends CaptionedBlockComponent<LatexBlockModel> {
static override styles = latexBlockStyles;
private _editorAbortController: AbortController | null = null;
get editorPlacement(): Placement {
return 'bottom';
}
get isBlockSelected() {
const blockSelection = this.selection.filter(BlockSelection);
return blockSelection.some(
selection => selection.blockId === this.model.id
);
}
override firstUpdated(props: Map<string, unknown>) {
super.firstUpdated(props);
const { disposables } = this;
this._editorAbortController?.abort();
this._editorAbortController = new AbortController();
disposables.add(() => {
this._editorAbortController?.abort();
});
const katexContainer = this._katexContainer;
if (!katexContainer) return;
disposables.add(
effect(() => {
const latex = this.model.props.latex$.value;
katexContainer.replaceChildren();
// @ts-expect-error lit hack won't fix
delete katexContainer['_$litPart$'];
if (latex.length === 0) {
render(
html`<span class="latex-block-empty-placeholder">Equation</span>`,
katexContainer
);
} else {
try {
katex.render(latex, katexContainer, {
displayMode: true,
});
} catch {
katexContainer.replaceChildren();
// @ts-expect-error lit hack won't fix
delete katexContainer['_$litPart$'];
render(
html`<span class="latex-block-error-placeholder"
>Error equation</span
>`,
katexContainer
);
}
}
})
);
}
private _handleClick() {
if (this.store.readonly) return;
if (this.isBlockSelected) {
this.toggleEditor();
} else {
this.selectBlock();
}
}
removeEditor(portal: HTMLDivElement) {
portal.remove();
}
override renderBlock() {
return html`
<div
contenteditable="false"
class="latex-block-container"
@click=${this._handleClick}
>
<div class="katex"></div>
</div>
`;
}
selectBlock() {
this.host.command.exec(selectBlock, {
focusBlock: this,
});
}
toggleEditor() {
const katexContainer = this._katexContainer;
if (!katexContainer) return;
this._editorAbortController?.abort();
this._editorAbortController = new AbortController();
this.selection.setGroup('note', []);
const { portal } = createLitPortal({
template: html`<latex-editor-menu
.std=${this.std}
.latexSignal=${this.model.props.latex$}
.abortController=${this._editorAbortController}
></latex-editor-menu>`,
container: this.host,
computePosition: {
referenceElement: this,
placement: this.editorPlacement,
autoUpdate: {
animationFrame: true,
},
},
closeOnClickAway: true,
abortController: this._editorAbortController,
shadowDom: false,
portalStyles: {
zIndex: 'var(--affine-z-index-popover)',
},
});
this._editorAbortController.signal.addEventListener(
'abort',
() => {
this.removeEditor(portal);
},
{ once: true }
);
}
@query('.latex-block-container')
private accessor _katexContainer!: HTMLDivElement;
}
declare global {
interface HTMLElementTagNameMap {
'affine-latex': LatexBlockComponent;
}
}

View File

@@ -0,0 +1,19 @@
import {
type StoreExtensionContext,
StoreExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { LatexBlockSchemaExtension } from '@blocksuite/affine-model';
import { LatexBlockAdapterExtensions } from './adapters/extension';
import { LatexMarkdownPreprocessorExtension } from './adapters/markdown/preprocessor';
export class LatexStoreExtension extends StoreExtensionProvider {
override name = 'affine-latex-block';
override setup(context: StoreExtensionContext) {
super.setup(context);
context.register([LatexBlockSchemaExtension]);
context.register(LatexBlockAdapterExtensions);
context.register(LatexMarkdownPreprocessorExtension);
}
}

View File

@@ -0,0 +1,40 @@
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { css } from 'lit';
export const latexBlockStyles = css`
.latex-block-container {
display: flex;
position: relative;
width: 100%;
height: 100%;
padding: 10px 24px;
flex-direction: column;
align-items: stretch;
justify-content: center;
border-radius: 4px;
overflow-x: auto;
user-select: none;
}
.latex-block-container:hover {
background: ${unsafeCSSVar('hoverColor')};
}
.latex-block-error-placeholder {
color: ${unsafeCSSVarV2('text/highlight/fg/red')};
font-family: Inter;
font-size: 12px;
font-weight: 500;
line-height: normal;
user-select: none;
}
.latex-block-empty-placeholder {
color: ${unsafeCSSVarV2('text/secondary')};
font-family: Inter;
font-size: 12px;
font-weight: 500;
line-height: normal;
user-select: none;
}
`;

View File

@@ -0,0 +1,27 @@
import {
type ViewExtensionContext,
ViewExtensionProvider,
} from '@blocksuite/affine-ext-loader';
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
import { BlockViewExtension } from '@blocksuite/std';
import { literal } from 'lit/static-html.js';
import { latexSlashMenuConfig } from './configs/slash-menu';
import { effects } from './effects';
export class LatexViewExtension extends ViewExtensionProvider {
override name = 'affine-latex-block';
override effect() {
super.effect();
effects();
}
override setup(context: ViewExtensionContext) {
super.setup(context);
context.register([
BlockViewExtension('affine:latex', literal`affine-latex`),
SlashMenuConfigExtension('affine:latex', latexSlashMenuConfig),
]);
}
}

View File

@@ -0,0 +1,20 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src"],
"references": [
{ "path": "../note" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
{ "path": "../../inlines/latex" },
{ "path": "../../model" },
{ "path": "../../shared" },
{ "path": "../../widgets/slash-menu" },
{ "path": "../../../framework/std" },
{ "path": "../../../framework/store" }
]
}