mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-22 16:57:00 +08:00
feat(core): add markdown preview for backlinks (#8883)
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/package.json b/package.json
|
||||||
|
index 5fef2811aa86f3f1f8228daef7d867863e71db72..b795fbd2a0e1cba0b6389ff051220f4e3c52fc13 100644
|
||||||
|
--- a/package.json
|
||||||
|
+++ b/package.json
|
||||||
|
@@ -34,7 +34,7 @@
|
||||||
|
"deno": "./index.js",
|
||||||
|
"react-native": "./index.js",
|
||||||
|
"worker": "./index.js",
|
||||||
|
- "browser": "./index.dom.js",
|
||||||
|
+ "browser": "./index.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
@@ -156,6 +156,7 @@
|
|||||||
"which-typed-array": "npm:@nolyfill/which-typed-array@latest",
|
"which-typed-array": "npm:@nolyfill/which-typed-array@latest",
|
||||||
"macos-alias": "npm:@napi-rs/macos-alias@0.0.4",
|
"macos-alias": "npm:@napi-rs/macos-alias@0.0.4",
|
||||||
"fs-xattr": "npm:@napi-rs/xattr@latest",
|
"fs-xattr": "npm:@napi-rs/xattr@latest",
|
||||||
"vite": "6.0.1"
|
"vite": "6.0.1",
|
||||||
|
"decode-named-character-reference@npm:^1.0.0": "patch:decode-named-character-reference@npm%3A1.0.2#~/.yarn/patches/decode-named-character-reference-npm-1.0.2-db17a755fd.patch"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class Document<S extends Schema = any> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const key in map) {
|
for (const key in map) {
|
||||||
if (map[key] === undefined) {
|
if (map[key] === undefined || map[key] === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
doc.insert(key, map[key]);
|
doc.insert(key, map[key]);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const PropertyCollapsibleSection = forwardRef<
|
|||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
onCollapseChange?: (collapsed: boolean) => void;
|
onCollapseChange?: (collapsed: boolean) => void;
|
||||||
}> &
|
}> &
|
||||||
HTMLProps<HTMLDivElement>
|
Omit<HTMLProps<HTMLDivElement>, 'title'>
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
} from '@affine/core/modules/doc-link';
|
} from '@affine/core/modules/doc-link';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
|
import { LiveData, useLiveData, useServices } from '@toeverything/infra';
|
||||||
import { useCallback, useState } from 'react';
|
import { Fragment, useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { AffinePageReference } from '../../affine/reference-link';
|
import { AffinePageReference } from '../../affine/reference-link';
|
||||||
import * as styles from './bi-directional-link-panel.css';
|
import * as styles from './bi-directional-link-panel.css';
|
||||||
@@ -52,9 +52,14 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
{t['com.affine.page-properties.backlinks']()} · {backlinks.length}
|
{t['com.affine.page-properties.backlinks']()} · {backlinks.length}
|
||||||
</div>
|
</div>
|
||||||
{backlinks.map(link => (
|
{backlinks.map(link => (
|
||||||
<div key={link.docId} className={styles.link}>
|
<Fragment key={link.docId}>
|
||||||
<AffinePageReference key={link.docId} pageId={link.docId} />
|
<div className={styles.link}>
|
||||||
</div>
|
<AffinePageReference key={link.docId} pageId={link.docId} />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<pre style={{ opacity: 0.5 }}>{link.markdownPreview}</pre>
|
||||||
|
<br />
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.linksContainer}>
|
<div className={styles.linksContainer}>
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ const DatabaseBacklinkRow = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PropertyCollapsibleSection
|
<PropertyCollapsibleSection
|
||||||
// @ts-expect-error fix type
|
|
||||||
title={
|
title={
|
||||||
<span className={styles.databaseNameWrapper}>
|
<span className={styles.databaseNameWrapper}>
|
||||||
<span className={styles.databaseName}>
|
<span className={styles.databaseName}>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface Backlink {
|
|||||||
docId: string;
|
docId: string;
|
||||||
blockId: string;
|
blockId: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
markdownPreview?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocBacklinks extends Entity {
|
export class DocBacklinks extends Entity {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class DocsIndexer extends Entity {
|
|||||||
/**
|
/**
|
||||||
* increase this number to re-index all docs
|
* increase this number to re-index all docs
|
||||||
*/
|
*/
|
||||||
static INDEXER_VERSION = 6;
|
static INDEXER_VERSION = 10;
|
||||||
|
|
||||||
private readonly jobQueue: JobQueue<IndexerJobPayload> =
|
private readonly jobQueue: JobQueue<IndexerJobPayload> =
|
||||||
new IndexedDBJobQueue<IndexerJobPayload>(
|
new IndexedDBJobQueue<IndexerJobPayload>(
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const blockIndexSchema = defineSchema({
|
|||||||
// additional info
|
// additional info
|
||||||
// { "databaseName": "xxx" }
|
// { "databaseName": "xxx" }
|
||||||
additional: { type: 'String', index: false },
|
additional: { type: 'String', index: false },
|
||||||
|
markdownPreview: { type: 'String', index: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
export type BlockIndexSchema = typeof blockIndexSchema;
|
export type BlockIndexSchema = typeof blockIndexSchema;
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ export class DocsSearchService extends Service {
|
|||||||
'docId',
|
'docId',
|
||||||
{
|
{
|
||||||
hits: {
|
hits: {
|
||||||
fields: ['docId', 'blockId'],
|
fields: ['docId', 'blockId', 'markdownPreview'],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 1,
|
limit: 1,
|
||||||
},
|
},
|
||||||
@@ -499,10 +499,16 @@ export class DocsSearchService extends Service {
|
|||||||
const title =
|
const title =
|
||||||
docData.find(doc => doc.id === bucket.key)?.get('title') ?? '';
|
docData.find(doc => doc.id === bucket.key)?.get('title') ?? '';
|
||||||
const blockId = bucket.hits.nodes[0]?.fields.blockId ?? '';
|
const blockId = bucket.hits.nodes[0]?.fields.blockId ?? '';
|
||||||
|
const markdownPreview =
|
||||||
|
bucket.hits.nodes[0]?.fields.markdownPreview ?? '';
|
||||||
return {
|
return {
|
||||||
docId: bucket.key,
|
docId: bucket.key,
|
||||||
blockId: typeof blockId === 'string' ? blockId : blockId[0],
|
blockId: typeof blockId === 'string' ? blockId : blockId[0],
|
||||||
title: typeof title === 'string' ? title : title[0],
|
title: typeof title === 'string' ? title : title[0],
|
||||||
|
markdownPreview:
|
||||||
|
typeof markdownPreview === 'string'
|
||||||
|
? markdownPreview
|
||||||
|
: markdownPreview[0],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import type { AffineTextAttributes } from '@blocksuite/affine/blocks';
|
import {
|
||||||
|
type AffineTextAttributes,
|
||||||
|
MarkdownAdapter,
|
||||||
|
} from '@blocksuite/affine/blocks';
|
||||||
|
import {
|
||||||
|
createYProxy,
|
||||||
|
DocCollection,
|
||||||
|
type DraftModel,
|
||||||
|
Job,
|
||||||
|
type YBlock,
|
||||||
|
} from '@blocksuite/affine/store';
|
||||||
import type { DeltaInsert } from '@blocksuite/inline';
|
import type { DeltaInsert } from '@blocksuite/inline';
|
||||||
import { Document } from '@toeverything/infra';
|
import { Document, getAFFiNEWorkspaceSchema } from '@toeverything/infra';
|
||||||
import { toHexString } from 'lib0/buffer.js';
|
import { toHexString } from 'lib0/buffer.js';
|
||||||
import { digest as lib0Digest } from 'lib0/hash/sha256';
|
import { digest as lib0Digest } from 'lib0/hash/sha256';
|
||||||
import { difference, uniq } from 'lodash-es';
|
import { difference, uniq } from 'lodash-es';
|
||||||
@@ -20,6 +30,8 @@ import type {
|
|||||||
WorkerOutput,
|
WorkerOutput,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
|
const blocksuiteSchema = getAFFiNEWorkspaceSchema();
|
||||||
|
|
||||||
const LRU_CACHE_SIZE = 5;
|
const LRU_CACHE_SIZE = 5;
|
||||||
|
|
||||||
// lru cache for ydoc instances, last used at the end of the array
|
// lru cache for ydoc instances, last used at the end of the array
|
||||||
@@ -61,6 +73,92 @@ async function getOrCreateCachedYDoc(data: Uint8Array) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function yblockToDraftModal(yblock: YBlock): DraftModel | null {
|
||||||
|
const flavour = yblock.get('sys:flavour');
|
||||||
|
const blockSchema = blocksuiteSchema.flavourSchemaMap.get(flavour);
|
||||||
|
if (!blockSchema) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const keys = Array.from(yblock.keys())
|
||||||
|
.filter(key => key.startsWith('prop:'))
|
||||||
|
.map(key => key.substring(5));
|
||||||
|
|
||||||
|
const props = Object.fromEntries(
|
||||||
|
keys.map(key => [key, createYProxy(yblock.get(`prop:${key}`))])
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
id: yblock.get('sys:id'),
|
||||||
|
flavour,
|
||||||
|
children: [],
|
||||||
|
role: blockSchema.model.role,
|
||||||
|
version: (yblock.get('sys:version') as number) ?? blockSchema.version,
|
||||||
|
keys: Array.from(yblock.keys())
|
||||||
|
.filter(key => key.startsWith('prop:'))
|
||||||
|
.map(key => key.substring(5)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const markdownAdapter = new MarkdownAdapter(
|
||||||
|
new Job({
|
||||||
|
collection: new DocCollection({
|
||||||
|
id: 'indexer',
|
||||||
|
schema: blocksuiteSchema,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
interface BlockDocumentInfo {
|
||||||
|
docId: string;
|
||||||
|
blockId: string;
|
||||||
|
content?: string | string[];
|
||||||
|
flavour: string;
|
||||||
|
blob?: string[];
|
||||||
|
refDocId?: string[];
|
||||||
|
ref?: string[];
|
||||||
|
parentFlavour?: string;
|
||||||
|
parentBlockId?: string;
|
||||||
|
additional?: { databaseName?: string };
|
||||||
|
yblock: YMap<any>;
|
||||||
|
markdownPreview?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const markdownPreviewCache = new WeakMap<BlockDocumentInfo, string | null>();
|
||||||
|
const generateMarkdownPreview = async (block: BlockDocumentInfo) => {
|
||||||
|
if (markdownPreviewCache.has(block)) {
|
||||||
|
return markdownPreviewCache.get(block);
|
||||||
|
}
|
||||||
|
const flavour = block.flavour;
|
||||||
|
let markdown: string | null = null;
|
||||||
|
if (
|
||||||
|
flavour === 'affine:paragraph' ||
|
||||||
|
flavour === 'affine:list' ||
|
||||||
|
flavour === 'affine:code'
|
||||||
|
) {
|
||||||
|
const draftModel = yblockToDraftModal(block.yblock);
|
||||||
|
markdown =
|
||||||
|
block.parentFlavour === 'affine:database'
|
||||||
|
? `database · ${block.additional?.databaseName}\n`
|
||||||
|
: ((draftModel ? await markdownAdapter.fromBlock(draftModel) : null)
|
||||||
|
?.file ?? null);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
flavour === 'affine:embed-linked-doc' ||
|
||||||
|
flavour === 'affine:embed-synced-doc'
|
||||||
|
) {
|
||||||
|
markdown = '🔗\n';
|
||||||
|
}
|
||||||
|
if (flavour === 'affine:attachment') {
|
||||||
|
markdown = '📃\n';
|
||||||
|
}
|
||||||
|
if (flavour === 'affine:image') {
|
||||||
|
markdown = '🖼️\n';
|
||||||
|
}
|
||||||
|
markdownPreviewCache.set(block, markdown);
|
||||||
|
return markdown;
|
||||||
|
};
|
||||||
|
|
||||||
async function crawlingDocData({
|
async function crawlingDocData({
|
||||||
docBuffer,
|
docBuffer,
|
||||||
storageDocId,
|
storageDocId,
|
||||||
@@ -110,7 +208,7 @@ async function crawlingDocData({
|
|||||||
let docTitle = '';
|
let docTitle = '';
|
||||||
let summaryLenNeeded = 1000;
|
let summaryLenNeeded = 1000;
|
||||||
let summary = '';
|
let summary = '';
|
||||||
const blockDocuments: Document<BlockIndexSchema>[] = [];
|
const blockDocuments: BlockDocumentInfo[] = [];
|
||||||
|
|
||||||
const blocks = ydoc.getMap<any>('blocks');
|
const blocks = ydoc.getMap<any>('blocks');
|
||||||
|
|
||||||
@@ -147,6 +245,7 @@ async function crawlingDocData({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #region first loop - generate block base info
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const next = queue.pop();
|
const next = queue.pop();
|
||||||
if (!next) {
|
if (!next) {
|
||||||
@@ -167,14 +266,13 @@ async function crawlingDocData({
|
|||||||
|
|
||||||
if (flavour === 'affine:page') {
|
if (flavour === 'affine:page') {
|
||||||
docTitle = block.get('prop:title').toString();
|
docTitle = block.get('prop:title').toString();
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
content: docTitle,
|
||||||
content: docTitle,
|
yblock: block,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -183,6 +281,7 @@ async function crawlingDocData({
|
|||||||
flavour === 'affine:code'
|
flavour === 'affine:code'
|
||||||
) {
|
) {
|
||||||
const text = block.get('prop:text') as YText;
|
const text = block.get('prop:text') as YText;
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -213,27 +312,24 @@ async function crawlingDocData({
|
|||||||
? parentBlock?.get('prop:title')?.toString()
|
? parentBlock?.get('prop:title')?.toString()
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
content: text.toString(),
|
||||||
content: text.toString(),
|
...refs.reduce<{ refDocId: string[]; ref: string[] }>(
|
||||||
...refs.reduce<{ refDocId: string[]; ref: string[] }>(
|
(prev, curr) => {
|
||||||
(prev, curr) => {
|
prev.refDocId.push(curr.refDocId);
|
||||||
prev.refDocId.push(curr.refDocId);
|
prev.ref.push(curr.ref);
|
||||||
prev.ref.push(curr.ref);
|
return prev;
|
||||||
return prev;
|
},
|
||||||
},
|
{ refDocId: [], ref: [] }
|
||||||
{ refDocId: [], ref: [] }
|
),
|
||||||
),
|
parentFlavour,
|
||||||
parentFlavour,
|
parentBlockId,
|
||||||
parentBlockId,
|
additional: { databaseName },
|
||||||
additional: databaseName
|
yblock: block,
|
||||||
? JSON.stringify({ databaseName })
|
});
|
||||||
: undefined,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (summaryLenNeeded > 0) {
|
if (summaryLenNeeded > 0) {
|
||||||
summary += text.toString();
|
summary += text.toString();
|
||||||
@@ -249,33 +345,31 @@ async function crawlingDocData({
|
|||||||
if (typeof pageId === 'string') {
|
if (typeof pageId === 'string') {
|
||||||
// reference info
|
// reference info
|
||||||
const params = block.get('prop:params') ?? {};
|
const params = block.get('prop:params') ?? {};
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
refDocId: [pageId],
|
||||||
refDocId: [pageId],
|
ref: [JSON.stringify({ docId: pageId, ...params })],
|
||||||
ref: [JSON.stringify({ docId: pageId, ...params })],
|
parentFlavour,
|
||||||
parentFlavour,
|
parentBlockId,
|
||||||
parentBlockId,
|
yblock: block,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flavour === 'affine:attachment' || flavour === 'affine:image') {
|
if (flavour === 'affine:attachment' || flavour === 'affine:image') {
|
||||||
const blobId = block.get('prop:sourceId');
|
const blobId = block.get('prop:sourceId');
|
||||||
if (typeof blobId === 'string') {
|
if (typeof blobId === 'string') {
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
blob: [blobId],
|
||||||
blob: [blobId],
|
parentFlavour,
|
||||||
parentFlavour,
|
parentBlockId,
|
||||||
parentBlockId,
|
yblock: block,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,16 +402,15 @@ async function crawlingDocData({
|
|||||||
texts.push(text.toString());
|
texts.push(text.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
content: texts,
|
||||||
content: texts,
|
parentFlavour,
|
||||||
parentFlavour,
|
parentBlockId,
|
||||||
parentBlockId,
|
yblock: block,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flavour === 'affine:database') {
|
if (flavour === 'affine:database') {
|
||||||
@@ -356,16 +449,81 @@ async function crawlingDocData({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockDocuments.push(
|
blockDocuments.push({
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
docId,
|
||||||
docId,
|
flavour,
|
||||||
flavour,
|
blockId,
|
||||||
blockId,
|
content: texts,
|
||||||
content: texts,
|
yblock: block,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region second loop - generate markdown preview
|
||||||
|
const TARGET_PREVIEW_CHARACTER = 500;
|
||||||
|
const TARGET_PREVIOUS_BLOCK = 1;
|
||||||
|
const TARGET_FOLLOW_BLOCK = 4;
|
||||||
|
for (let i = 0; i < blockDocuments.length; i++) {
|
||||||
|
const block = blockDocuments[i];
|
||||||
|
if (block.ref) {
|
||||||
|
// only generate markdown preview for reference blocks
|
||||||
|
let previewText = (await generateMarkdownPreview(block)) ?? '';
|
||||||
|
let previousBlock = 0;
|
||||||
|
let followBlock = 0;
|
||||||
|
let previousIndex = i;
|
||||||
|
let followIndex = i;
|
||||||
|
|
||||||
|
while (
|
||||||
|
!(
|
||||||
|
(
|
||||||
|
previewText.length > TARGET_PREVIEW_CHARACTER || // stop if preview text reaches the limit
|
||||||
|
((previousBlock >= TARGET_PREVIOUS_BLOCK || previousIndex < 0) &&
|
||||||
|
(followBlock >= TARGET_FOLLOW_BLOCK ||
|
||||||
|
followIndex >= blockDocuments.length))
|
||||||
|
) // stop if no more blocks, or preview block reaches the limit
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (previousBlock < TARGET_PREVIOUS_BLOCK) {
|
||||||
|
previousIndex--;
|
||||||
|
const block =
|
||||||
|
previousIndex >= 0 ? blockDocuments.at(previousIndex) : null;
|
||||||
|
const markdown = block
|
||||||
|
? await generateMarkdownPreview(block)
|
||||||
|
: null;
|
||||||
|
if (
|
||||||
|
markdown &&
|
||||||
|
!previewText.startsWith(
|
||||||
|
markdown
|
||||||
|
) /* A small hack to skip blocks with the same content */
|
||||||
|
) {
|
||||||
|
previewText = markdown + previewText;
|
||||||
|
previousBlock++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (followBlock < TARGET_FOLLOW_BLOCK) {
|
||||||
|
followIndex++;
|
||||||
|
const block = blockDocuments.at(followIndex);
|
||||||
|
const markdown = block
|
||||||
|
? await generateMarkdownPreview(block)
|
||||||
|
: null;
|
||||||
|
if (
|
||||||
|
markdown &&
|
||||||
|
!previewText.endsWith(
|
||||||
|
markdown
|
||||||
|
) /* A small hack to skip blocks with the same content */
|
||||||
|
) {
|
||||||
|
previewText = previewText + markdown;
|
||||||
|
followBlock++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.markdownPreview = previewText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addedDoc: [
|
addedDoc: [
|
||||||
@@ -375,7 +533,23 @@ async function crawlingDocData({
|
|||||||
title: docTitle,
|
title: docTitle,
|
||||||
summary,
|
summary,
|
||||||
}),
|
}),
|
||||||
blocks: blockDocuments,
|
blocks: blockDocuments.map(block =>
|
||||||
|
Document.from<BlockIndexSchema>(`${docId}:${block.blockId}`, {
|
||||||
|
docId: block.docId,
|
||||||
|
blockId: block.blockId,
|
||||||
|
content: block.content,
|
||||||
|
flavour: block.flavour,
|
||||||
|
blob: block.blob,
|
||||||
|
refDocId: block.refDocId,
|
||||||
|
ref: block.ref,
|
||||||
|
parentFlavour: block.parentFlavour,
|
||||||
|
parentBlockId: block.parentBlockId,
|
||||||
|
additional: block.additional
|
||||||
|
? JSON.stringify(block.additional)
|
||||||
|
: undefined,
|
||||||
|
markdownPreview: block.markdownPreview,
|
||||||
|
})
|
||||||
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
11
yarn.lock
11
yarn.lock
@@ -18060,7 +18060,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"decode-named-character-reference@npm:^1.0.0":
|
"decode-named-character-reference@npm:1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "decode-named-character-reference@npm:1.0.2"
|
resolution: "decode-named-character-reference@npm:1.0.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -18069,6 +18069,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"decode-named-character-reference@patch:decode-named-character-reference@npm%3A1.0.2#~/.yarn/patches/decode-named-character-reference-npm-1.0.2-db17a755fd.patch":
|
||||||
|
version: 1.0.2
|
||||||
|
resolution: "decode-named-character-reference@patch:decode-named-character-reference@npm%3A1.0.2#~/.yarn/patches/decode-named-character-reference-npm-1.0.2-db17a755fd.patch::version=1.0.2&hash=2c2160"
|
||||||
|
dependencies:
|
||||||
|
character-entities: "npm:^2.0.0"
|
||||||
|
checksum: 10/bd6e42b2cc162f55351a34fa5123cee0bfdebd983aa3690e5347c9ec23ce8b7f701fce0f77099b3a51eb1451b6a17e66f41d69e7cfc482ea3a0a1e38fe2442bf
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"decode-uri-component@npm:^0.2.2":
|
"decode-uri-component@npm:^0.2.2":
|
||||||
version: 0.2.2
|
version: 0.2.2
|
||||||
resolution: "decode-uri-component@npm:0.2.2"
|
resolution: "decode-uri-component@npm:0.2.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user