mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 19:02:23 +08:00
feat: support bookmark (#2458)
Co-authored-by: himself65 <himself65@outlook.com>
This commit is contained in:
1
.github/workflows/nightly-build.yml
vendored
1
.github/workflows/nightly-build.yml
vendored
@@ -67,6 +67,7 @@ jobs:
|
|||||||
ENABLE_TEST_PROPERTIES: false
|
ENABLE_TEST_PROPERTIES: false
|
||||||
ENABLE_IMAGE_PREVIEW_MODAL: false
|
ENABLE_IMAGE_PREVIEW_MODAL: false
|
||||||
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
|
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
|
||||||
|
ENABLE_BOOKMARK_OPERATION: true
|
||||||
|
|
||||||
- name: Upload Artifact (web-static)
|
- name: Upload Artifact (web-static)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
1
.github/workflows/release-desktop-app.yml
vendored
1
.github/workflows/release-desktop-app.yml
vendored
@@ -68,6 +68,7 @@ jobs:
|
|||||||
ENABLE_TEST_PROPERTIES: false
|
ENABLE_TEST_PROPERTIES: false
|
||||||
ENABLE_IMAGE_PREVIEW_MODAL: false
|
ENABLE_IMAGE_PREVIEW_MODAL: false
|
||||||
RELEASE_VERSION: ${{ github.event.inputs.version }}
|
RELEASE_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
ENABLE_BOOKMARK_OPERATION: false
|
||||||
|
|
||||||
- name: Upload Artifact (web-static)
|
- name: Upload Artifact (web-static)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
125
apps/electron/layers/main/src/handlers/ui/getMetaData/index.ts
Normal file
125
apps/electron/layers/main/src/handlers/ui/getMetaData/index.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import type { CheerioAPI, Element } from 'cheerio';
|
||||||
|
import { load } from 'cheerio';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
|
import type { Context, MetaData, Options, RuleSet } from './types';
|
||||||
|
|
||||||
|
export * from './types';
|
||||||
|
|
||||||
|
import { metaDataRules } from './rules';
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
maxRedirects: 5,
|
||||||
|
ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',
|
||||||
|
lang: '*',
|
||||||
|
timeout: 10000,
|
||||||
|
forceImageHttps: true,
|
||||||
|
customRules: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const runRule = function (ruleSet: RuleSet, $: CheerioAPI, context: Context) {
|
||||||
|
let maxScore = 0;
|
||||||
|
let value;
|
||||||
|
|
||||||
|
for (let currRule = 0; currRule < ruleSet.rules.length; currRule++) {
|
||||||
|
const [query, handler] = ruleSet.rules[currRule];
|
||||||
|
const elements = Array.from($(query));
|
||||||
|
|
||||||
|
if (elements.length) {
|
||||||
|
for (const element of elements) {
|
||||||
|
let score = ruleSet.rules.length - currRule;
|
||||||
|
|
||||||
|
if (ruleSet.scorer) {
|
||||||
|
const newScore = ruleSet.scorer(element as Element, score);
|
||||||
|
|
||||||
|
if (newScore) {
|
||||||
|
score = newScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score > maxScore) {
|
||||||
|
maxScore = score;
|
||||||
|
value = handler(element as Element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
if (ruleSet.processor) {
|
||||||
|
value = ruleSet.processor(value, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleSet.defaultValue) {
|
||||||
|
return ruleSet.defaultValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMetaData = async function (
|
||||||
|
input: string | Partial<Options>,
|
||||||
|
inputOptions: Partial<Options> = {}
|
||||||
|
) {
|
||||||
|
let url;
|
||||||
|
if (typeof input === 'object') {
|
||||||
|
inputOptions = input;
|
||||||
|
url = input.url || '';
|
||||||
|
} else {
|
||||||
|
url = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = Object.assign({}, defaultOptions, inputOptions);
|
||||||
|
|
||||||
|
const rules: Record<string, RuleSet> = { ...metaDataRules };
|
||||||
|
Object.keys(options.customRules).forEach((key: string) => {
|
||||||
|
rules[key] = {
|
||||||
|
rules: [...metaDataRules[key].rules, ...options.customRules[key].rules],
|
||||||
|
defaultValue:
|
||||||
|
options.customRules[key].defaultValue ||
|
||||||
|
metaDataRules[key].defaultValue,
|
||||||
|
processor:
|
||||||
|
options.customRules[key].processor || metaDataRules[key].processor,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let html;
|
||||||
|
if (!options.html) {
|
||||||
|
const response = await got(url, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': options.ua,
|
||||||
|
'Accept-Language': options.lang,
|
||||||
|
},
|
||||||
|
timeout: options.timeout,
|
||||||
|
...(options.maxRedirects === 0
|
||||||
|
? { followRedirect: false }
|
||||||
|
: { maxRedirects: options.maxRedirects }),
|
||||||
|
});
|
||||||
|
html = response.body;
|
||||||
|
} else {
|
||||||
|
html = options.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata: MetaData = {};
|
||||||
|
const context: Context = {
|
||||||
|
url,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
|
||||||
|
const $ = load(html);
|
||||||
|
// console.log('===============================');
|
||||||
|
// console.log('html');
|
||||||
|
// console.log(doc);
|
||||||
|
|
||||||
|
Object.keys(rules).forEach((key: string) => {
|
||||||
|
const ruleSet = rules[key];
|
||||||
|
metadata[key] = runRule(ruleSet, $, context) || undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getMetaData;
|
||||||
690
apps/electron/layers/main/src/handlers/ui/getMetaData/rules.ts
Normal file
690
apps/electron/layers/main/src/handlers/ui/getMetaData/rules.ts
Normal file
@@ -0,0 +1,690 @@
|
|||||||
|
import type { RuleSet } from './types';
|
||||||
|
import { getProvider, makeUrlAbsolute, makeUrlSecure, parseUrl } from './utils';
|
||||||
|
|
||||||
|
export const metaDataRules: Record<string, RuleSet> = {
|
||||||
|
title: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:title"][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.title"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['title', (element: any) => element.text],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="description" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="description" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:description"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="summary" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="summary" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
rules: [
|
||||||
|
['html[lang]', element => element.attribs['lang']],
|
||||||
|
[
|
||||||
|
'meta[property="language" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="language" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:locale"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:locale"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
processor: (language: any) => language.split('-')[0],
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:type"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:type"][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-type"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-type"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="medium"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="medium"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:url"][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="al:web:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="al:web:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-link"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-link"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['a.amp-canurl', element => element.attribs['href']],
|
||||||
|
['link[rel="canonical"][href]', element => element.attribs['href']],
|
||||||
|
],
|
||||||
|
defaultValue: context => context.url,
|
||||||
|
processor: (url: any, context) => makeUrlAbsolute(context.url, url),
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:site_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:site_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="publisher" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="publisher" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="application-name" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="application-name" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="al:android:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="al:android:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="al:iphone:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="al:iphone:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="al:ipad:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="al:ipad:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="al:ios:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="al:ios:app_name"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:app:name:iphone"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:app:name:iphone"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:app:name:ipad"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:app:name:ipad"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:app:name:googleplay"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:app:name:googleplay"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
defaultValue: context => getProvider(parseUrl(context.url)),
|
||||||
|
},
|
||||||
|
keywords: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="keywords" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="keywords" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-tags"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-tags"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.tags"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.tags"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="article:tag" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="article:tag" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="book:tag" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="book:tag" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="topic" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="topic" i][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
processor: (keywords: any) =>
|
||||||
|
keywords.split(',').map((keyword: string) => keyword.trim()),
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="article:section"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="article:section"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="category"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="category"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="author" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="author" i][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="article:author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="article:author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="book:author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="book:author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.author"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['a[class*="author" i]', (element: any) => element.text],
|
||||||
|
['[rel="author"]', (element: any) => element.text],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:creator"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:creator"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="profile:username"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="profile:username"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
published: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="article:published_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="article:published_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="published_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="published_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-pub-date"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-pub-date"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.date"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.date"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="date" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="date" i][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="release_date" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="release_date" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['time[datetime]', element => element.attribs['datetime']],
|
||||||
|
['time[datetime][pubdate]', element => element.attribs['datetime']],
|
||||||
|
],
|
||||||
|
processor: (value: any) =>
|
||||||
|
Date.parse(value.toString())
|
||||||
|
? new Date(value.toString()).toISOString()
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
modified: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:updated_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:updated_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="article:modified_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="article:modified_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="updated_time" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="updated_time" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="modified_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="modified_time"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="revised"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="revised"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
processor: (value: any) =>
|
||||||
|
Date.parse(value.toString())
|
||||||
|
? new Date(value.toString()).toISOString()
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="robots" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="robots" i][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
processor: (keywords: any) =>
|
||||||
|
keywords.split(',').map((keyword: string) => keyword.trim()),
|
||||||
|
},
|
||||||
|
copyright: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="copyright" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="copyright" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="email" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="email" i][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="reply-to" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="reply-to" i][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="twitter:site"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:site"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="fb:pages"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="fb:pages"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:image:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:image:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:image:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:image:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:image"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:image"][content]', element => element.attribs['content']],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:image"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:image"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="twitter:image:src"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="twitter:image:src"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="thumbnail"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="thumbnail"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="parsely-image-url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="parsely-image-url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="sailthru.image.full"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="sailthru.image.full"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
processor: (imageUrl: any, context) =>
|
||||||
|
context.options.forceImageHttps === true
|
||||||
|
? makeUrlSecure(makeUrlAbsolute(context.url, imageUrl))
|
||||||
|
: makeUrlAbsolute(context.url, imageUrl),
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'link[rel="apple-touch-icon"][href]',
|
||||||
|
element => element.attribs['href'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'link[rel="apple-touch-icon-precomposed"][href]',
|
||||||
|
element => element.attribs['href'],
|
||||||
|
],
|
||||||
|
['link[rel="icon" i][href]', element => element.attribs['href']],
|
||||||
|
['link[rel="fluid-icon"][href]', element => element.attribs['href']],
|
||||||
|
['link[rel="shortcut icon"][href]', element => element.attribs['href']],
|
||||||
|
['link[rel="Shortcut Icon"][href]', element => element.attribs['href']],
|
||||||
|
['link[rel="mask-icon"][href]', element => element.attribs['href']],
|
||||||
|
],
|
||||||
|
scorer: element => {
|
||||||
|
const sizes = element.attribs['sizes'];
|
||||||
|
if (sizes) {
|
||||||
|
const sizeMatches = sizes.match(/\d+/g);
|
||||||
|
if (sizeMatches) {
|
||||||
|
const parsed = parseInt(sizeMatches[0]);
|
||||||
|
if (!isNaN(parsed)) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultValue: context => makeUrlAbsolute(context.url, '/favicon.ico'),
|
||||||
|
processor: (iconUrl, context) =>
|
||||||
|
context.options.forceImageHttps === true
|
||||||
|
? makeUrlSecure(makeUrlAbsolute(context.url, iconUrl))
|
||||||
|
: makeUrlAbsolute(context.url, iconUrl),
|
||||||
|
},
|
||||||
|
video: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:video:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:video:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:video:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:video:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:video"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:video"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
processor: (imageUrl: any, context) =>
|
||||||
|
context.options.forceImageHttps === true
|
||||||
|
? makeUrlSecure(makeUrlAbsolute(context.url, imageUrl))
|
||||||
|
: makeUrlAbsolute(context.url, imageUrl),
|
||||||
|
},
|
||||||
|
audio: {
|
||||||
|
rules: [
|
||||||
|
[
|
||||||
|
'meta[property="og:audio:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:audio:secure_url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:audio:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[name="og:audio:url"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta[property="og:audio"][content]',
|
||||||
|
element => element.attribs['content'],
|
||||||
|
],
|
||||||
|
['meta[name="og:audio"][content]', element => element.attribs['content']],
|
||||||
|
],
|
||||||
|
processor: (imageUrl: any, context) =>
|
||||||
|
context.options.forceImageHttps === true
|
||||||
|
? makeUrlSecure(makeUrlAbsolute(context.url, imageUrl))
|
||||||
|
: makeUrlAbsolute(context.url, imageUrl),
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import type { Element } from 'cheerio';
|
||||||
|
|
||||||
|
export interface MetaData {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
icon?: string;
|
||||||
|
image?: string;
|
||||||
|
keywords?: string[];
|
||||||
|
language?: string;
|
||||||
|
type?: string;
|
||||||
|
url?: string;
|
||||||
|
provider?: string;
|
||||||
|
|
||||||
|
[x: string]: string | string[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MetadataRule = [string, (el: Element) => string | null];
|
||||||
|
|
||||||
|
export interface Context {
|
||||||
|
url: string;
|
||||||
|
options: Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RuleSet {
|
||||||
|
rules: MetadataRule[];
|
||||||
|
defaultValue?: (context: Context) => string | string[];
|
||||||
|
scorer?: (el: Element, score: any) => any;
|
||||||
|
processor?: (input: any, context: Context) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
maxRedirects?: number;
|
||||||
|
ua?: string;
|
||||||
|
lang?: string;
|
||||||
|
timeout?: number;
|
||||||
|
forceImageHttps?: boolean;
|
||||||
|
html?: string;
|
||||||
|
url?: string;
|
||||||
|
customRules?: Record<string, RuleSet>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import urlparse from 'url';
|
||||||
|
|
||||||
|
export function makeUrlAbsolute(base: string, relative: string): string {
|
||||||
|
const relativeParsed = urlparse.parse(relative);
|
||||||
|
|
||||||
|
if (relativeParsed.host === null) {
|
||||||
|
return urlparse.resolve(base, relative);
|
||||||
|
}
|
||||||
|
|
||||||
|
return relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeUrlSecure(url: string): string {
|
||||||
|
return url.replace(/^http:/, 'https:');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseUrl(url: string): string {
|
||||||
|
return urlparse.parse(url).hostname || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProvider(host: string): string {
|
||||||
|
return host
|
||||||
|
.replace(/www[a-zA-Z0-9]*\./, '')
|
||||||
|
.replace('.co.', '.')
|
||||||
|
.split('.')
|
||||||
|
.slice(0, -1)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { app, BrowserWindow, nativeTheme } from 'electron';
|
import { app, BrowserWindow, nativeTheme, session } from 'electron';
|
||||||
|
|
||||||
import { isMacOS } from '../../../../utils';
|
import { isMacOS } from '../../../../utils';
|
||||||
import type { NamespaceHandlers } from '../type';
|
import type { NamespaceHandlers } from '../type';
|
||||||
|
import getMetaData from './getMetaData';
|
||||||
import { getGoogleOauthCode } from './google-auth';
|
import { getGoogleOauthCode } from './google-auth';
|
||||||
|
|
||||||
export const uiHandlers = {
|
export const uiHandlers = {
|
||||||
@@ -39,4 +40,9 @@ export const uiHandlers = {
|
|||||||
getGoogleOauthCode: async () => {
|
getGoogleOauthCode: async () => {
|
||||||
return getGoogleOauthCode();
|
return getGoogleOauthCode();
|
||||||
},
|
},
|
||||||
|
getBookmarkDataByLink: async (_, url: string) => {
|
||||||
|
return getMetaData(url, {
|
||||||
|
ua: session.defaultSession.getUserAgent(),
|
||||||
|
});
|
||||||
|
},
|
||||||
} satisfies NamespaceHandlers;
|
} satisfies NamespaceHandlers;
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^8.3.0",
|
"better-sqlite3": "^8.3.0",
|
||||||
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"electron-updater": "^5.3.0",
|
"electron-updater": "^5.3.0",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
"@affine/jotai": "workspace:*",
|
"@affine/jotai": "workspace:*",
|
||||||
"@affine/templates": "workspace:*",
|
"@affine/templates": "workspace:*",
|
||||||
"@affine/workspace": "workspace:*",
|
"@affine/workspace": "workspace:*",
|
||||||
"@blocksuite/blocks": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/blocks": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/editor": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/editor": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/global": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/global": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/icons": "^2.1.19",
|
"@blocksuite/icons": "^2.1.19",
|
||||||
"@blocksuite/lit": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/lit": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/store": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/store": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@dnd-kit/core": "^6.0.8",
|
"@dnd-kit/core": "^6.0.8",
|
||||||
"@dnd-kit/sortable": "^7.0.2",
|
"@dnd-kit/sortable": "^7.0.2",
|
||||||
"@emotion/cache": "^11.11.0",
|
"@emotion/cache": "^11.11.0",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import('@affine/env').BlockSuiteFeatureFlags}
|
* @type {import('@affine/env').BlockSuiteFeatureFlags}
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +11,7 @@ export const blockSuiteFeatureFlags = {
|
|||||||
enable_drag_handle: true,
|
enable_drag_handle: true,
|
||||||
enable_surface: true,
|
enable_surface: true,
|
||||||
enable_linked_page: true,
|
enable_linked_page: true,
|
||||||
|
enable_bookmark_operation: process.env.ENABLE_BOOKMARK_OPERATION === 'true',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -52,12 +52,12 @@
|
|||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blocksuite/blocks": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/blocks": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/editor": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/editor": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/global": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/global": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/icons": "^2.1.19",
|
"@blocksuite/icons": "^2.1.19",
|
||||||
"@blocksuite/lit": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/lit": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/store": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/store": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@storybook/addon-actions": "^7.0.12",
|
"@storybook/addon-actions": "^7.0.12",
|
||||||
"@storybook/addon-coverage": "^0.0.8",
|
"@storybook/addon-coverage": "^0.0.8",
|
||||||
"@storybook/addon-essentials": "^7.0.12",
|
"@storybook/addon-essentials": "^7.0.12",
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ import {
|
|||||||
blockSuiteEditorHeaderStyle,
|
blockSuiteEditorHeaderStyle,
|
||||||
blockSuiteEditorStyle,
|
blockSuiteEditorStyle,
|
||||||
} from './index.css';
|
} from './index.css';
|
||||||
|
import { bookmarkPlugin } from './plugins/bookmark';
|
||||||
|
|
||||||
|
export type EditorPlugin = {
|
||||||
|
flavour: string;
|
||||||
|
onInit?: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||||
|
onLoad?: (page: Page, editor: EditorContainer) => () => void;
|
||||||
|
render?: (props: { page: Page }) => ReactElement | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type EditorProps = {
|
export type EditorProps = {
|
||||||
page: Page;
|
page: Page;
|
||||||
@@ -42,6 +50,9 @@ const ImagePreviewModal = lazy(() =>
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// todo(himself65): plugin-infra should support this
|
||||||
|
const plugins = [bookmarkPlugin];
|
||||||
|
|
||||||
const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
|
const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
|
||||||
const { onLoad, page, mode, style, onInit } = props;
|
const { onLoad, page, mode, style, onInit } = props;
|
||||||
const JotaiEditorContainer = useAtomValue(
|
const JotaiEditorContainer = useAtomValue(
|
||||||
@@ -66,13 +77,23 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
|
|||||||
editor.page = page;
|
editor.page = page;
|
||||||
if (page.root === null) {
|
if (page.root === null) {
|
||||||
onInit(page, editor);
|
onInit(page, editor);
|
||||||
|
plugins.forEach(plugin => {
|
||||||
|
plugin.onInit?.(page, editor);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [onInit, editor, props, page]);
|
}, [editor, page, onInit]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editor.page && onLoad) {
|
if (editor.page && onLoad) {
|
||||||
return onLoad(page, editor);
|
const disposes = [] as ((() => void) | undefined)[];
|
||||||
|
disposes.push(onLoad?.(page, editor));
|
||||||
|
disposes.push(...plugins.map(plugin => plugin.onLoad?.(page, editor)));
|
||||||
|
return () => {
|
||||||
|
disposes
|
||||||
|
.filter((dispose): dispose is () => void => !!dispose)
|
||||||
|
.forEach(dispose => dispose());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}, [editor, editor.page, page, onLoad]);
|
}, [editor, editor.page, page, onLoad]);
|
||||||
|
|
||||||
@@ -180,6 +201,12 @@ export const BlockSuiteEditor = memo(function BlockSuiteEditor(
|
|||||||
)}
|
)}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
|
{plugins.map(plugin => {
|
||||||
|
const Renderer = plugin.render;
|
||||||
|
return Renderer ? (
|
||||||
|
<Renderer page={props.page} key={plugin.flavour} />
|
||||||
|
) : null;
|
||||||
|
})}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
import { MenuItem, MuiClickAwayListener, PureMenu } from '@affine/component';
|
||||||
|
import type { EditorPlugin } from '@affine/component/block-suite-editor';
|
||||||
|
import {
|
||||||
|
getCurrentBlockRange,
|
||||||
|
getCurrentNativeRange,
|
||||||
|
getVirgoByModel,
|
||||||
|
hasNativeSelection,
|
||||||
|
} from '@blocksuite/blocks/std';
|
||||||
|
import type { Page } from '@blocksuite/store';
|
||||||
|
import { assertExists } from '@blocksuite/store';
|
||||||
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
const isCursorInLink = (page: Page) => {
|
||||||
|
if (!hasNativeSelection()) return false;
|
||||||
|
const blockRange = getCurrentBlockRange(page);
|
||||||
|
if (
|
||||||
|
!blockRange ||
|
||||||
|
blockRange.type !== 'Native' ||
|
||||||
|
blockRange.startOffset !== blockRange.endOffset
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
models: [model],
|
||||||
|
} = blockRange;
|
||||||
|
const vEditor = getVirgoByModel(model);
|
||||||
|
const delta = vEditor?.getDeltaByRangeIndex(blockRange.startOffset);
|
||||||
|
|
||||||
|
return delta?.attributes?.link;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShortcutMap = {
|
||||||
|
[key: string]: (e: KeyboardEvent, page: Page) => void;
|
||||||
|
};
|
||||||
|
const menuOptions = [
|
||||||
|
{
|
||||||
|
id: 'dismiss',
|
||||||
|
label: 'Dismiss',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bookmark',
|
||||||
|
label: 'Create bookmark',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleEnter = ({
|
||||||
|
page,
|
||||||
|
selectedOption,
|
||||||
|
callback,
|
||||||
|
}: {
|
||||||
|
page: Page;
|
||||||
|
selectedOption: keyof ShortcutMap;
|
||||||
|
callback: () => void;
|
||||||
|
}) => {
|
||||||
|
if (selectedOption === 'dismiss') {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
const blockRange = getCurrentBlockRange(page) as Exclude<
|
||||||
|
ReturnType<typeof getCurrentBlockRange>,
|
||||||
|
null
|
||||||
|
>;
|
||||||
|
const vEditor = getVirgoByModel(blockRange.models[0]);
|
||||||
|
const linkInfo = vEditor!
|
||||||
|
.getDeltasByVRange({
|
||||||
|
index: blockRange.startOffset,
|
||||||
|
length: 0,
|
||||||
|
})
|
||||||
|
.find(delta => delta[0]?.attributes?.link);
|
||||||
|
if (!linkInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [, { index, length }] = linkInfo;
|
||||||
|
const link = linkInfo[0]?.attributes?.link as string;
|
||||||
|
|
||||||
|
const model = blockRange.models[0];
|
||||||
|
const parent = page.getParent(model);
|
||||||
|
assertExists(parent);
|
||||||
|
const currentBlockIndex = parent.children.indexOf(model);
|
||||||
|
page.addBlock(
|
||||||
|
'affine:bookmark',
|
||||||
|
{ url: link },
|
||||||
|
parent,
|
||||||
|
currentBlockIndex + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
vEditor!.deleteText({
|
||||||
|
index,
|
||||||
|
length,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (model.isEmpty()) {
|
||||||
|
page.deleteBlock(model);
|
||||||
|
}
|
||||||
|
return callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const BookMarkMenu: EditorPlugin['render'] = ({ page }) => {
|
||||||
|
const [anchor, setAnchor] = useState<Range | null>(null);
|
||||||
|
const [selectedOption, setSelectedOption] = useState<string>('');
|
||||||
|
const shouldHijack = useRef(false);
|
||||||
|
const shortcutMap = useMemo<ShortcutMap>(
|
||||||
|
() => ({
|
||||||
|
ArrowUp: () => {
|
||||||
|
const curIndex = menuOptions.findIndex(
|
||||||
|
({ id }) => id === selectedOption
|
||||||
|
);
|
||||||
|
if (menuOptions[curIndex - 1]) {
|
||||||
|
setSelectedOption(menuOptions[curIndex - 1].id);
|
||||||
|
} else if (curIndex === -1) {
|
||||||
|
setSelectedOption(menuOptions[0].id);
|
||||||
|
} else {
|
||||||
|
setSelectedOption(menuOptions[menuOptions.length - 1].id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ArrowDown: () => {
|
||||||
|
const curIndex = menuOptions.findIndex(
|
||||||
|
({ id }) => id === selectedOption
|
||||||
|
);
|
||||||
|
if (curIndex !== -1 && menuOptions[curIndex + 1]) {
|
||||||
|
setSelectedOption(menuOptions[curIndex + 1].id);
|
||||||
|
} else {
|
||||||
|
setSelectedOption(menuOptions[0].id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Enter: () =>
|
||||||
|
handleEnter({
|
||||||
|
page,
|
||||||
|
selectedOption,
|
||||||
|
callback: () => {
|
||||||
|
shouldHijack.current = false;
|
||||||
|
setAnchor(null);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[page, selectedOption]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// TODO: textUpdated slot is not working
|
||||||
|
// const disposer = page.slots.textUpdated.on(() => {
|
||||||
|
// console.log('text Updated', page);
|
||||||
|
// });
|
||||||
|
const disposer = page.slots.historyUpdated.on(() => {
|
||||||
|
if (!isCursorInLink(page)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAnchor(getCurrentNativeRange());
|
||||||
|
shouldHijack.current = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// disposer.dispose();
|
||||||
|
disposer.dispose();
|
||||||
|
};
|
||||||
|
}, [page, shortcutMap]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const keydown = (e: KeyboardEvent) => {
|
||||||
|
if (!shouldHijack.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const shortcut = shortcutMap[e.key];
|
||||||
|
if (shortcut) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
shortcut(e, page);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', keydown, { capture: true });
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', keydown, { capture: true });
|
||||||
|
};
|
||||||
|
}, [page, shortcutMap]);
|
||||||
|
|
||||||
|
return anchor ? (
|
||||||
|
<MuiClickAwayListener
|
||||||
|
onClickAway={() => {
|
||||||
|
setAnchor(null);
|
||||||
|
setSelectedOption('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<PureMenu open={!!anchor} anchorEl={anchor} placement="bottom-start">
|
||||||
|
{menuOptions.map(({ id, label }) => {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={id}
|
||||||
|
active={selectedOption === id}
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</PureMenu>
|
||||||
|
</div>
|
||||||
|
</MuiClickAwayListener>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Defender: EditorPlugin['render'] = props => {
|
||||||
|
const flag = props.page.awarenessStore.getFlag('enable_bookmark_operation');
|
||||||
|
return flag ? <BookMarkMenu {...props} /> : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bookmarkPlugin: EditorPlugin = {
|
||||||
|
flavour: 'bookmark',
|
||||||
|
render: Defender,
|
||||||
|
};
|
||||||
@@ -13,6 +13,7 @@ export type IconMenuProps = PropsWithChildren<{
|
|||||||
endIcon?: ReactElement;
|
endIcon?: ReactElement;
|
||||||
iconSize?: [number, number];
|
iconSize?: [number, number];
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
active?: boolean;
|
||||||
}> &
|
}> &
|
||||||
HTMLAttributes<HTMLButtonElement>;
|
HTMLAttributes<HTMLButtonElement>;
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ export const StyledContent = styled('div')(() => {
|
|||||||
export const StyledMenuItem = styled('button')<{
|
export const StyledMenuItem = styled('button')<{
|
||||||
isDir?: boolean;
|
isDir?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}>(({ isDir = false, disabled = false }) => {
|
active?: boolean;
|
||||||
|
}>(({ isDir = false, disabled = false, active = false }) => {
|
||||||
return {
|
return {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
@@ -82,5 +83,11 @@ export const StyledMenuItem = styled('button')<{
|
|||||||
: {
|
: {
|
||||||
backgroundColor: 'var(--affine-hover-color)',
|
backgroundColor: 'var(--affine-hover-color)',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
...(active && !disabled
|
||||||
|
? {
|
||||||
|
backgroundColor: 'var(--affine-hover-color)',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
2
packages/env/package.json
vendored
2
packages/env/package.json
vendored
@@ -4,7 +4,7 @@
|
|||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"module": "./src/index.ts",
|
"module": "./src/index.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blocksuite/global": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/global": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"next": "^13.4.2",
|
"next": "^13.4.2",
|
||||||
"react": "18.3.0-canary-16d053d59-20230506",
|
"react": "18.3.0-canary-16d053d59-20230506",
|
||||||
"react-dom": "18.3.0-canary-16d053d59-20230506",
|
"react-dom": "18.3.0-canary-16d053d59-20230506",
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
"jotai": "^2.1.0"
|
"jotai": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blocksuite/blocks": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/blocks": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/editor": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/editor": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/global": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/global": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/lit": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/lit": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/store": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/store": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"lottie-web": "^5.11.0"
|
"lottie-web": "^5.11.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
"idb": "^7.1.1"
|
"idb": "^7.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@blocksuite/blocks": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/blocks": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"@blocksuite/store": "0.0.0-20230525011821-20259c76-nightly",
|
"@blocksuite/store": "0.0.0-20230526010315-e1d4bf97-nightly",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.8",
|
||||||
"vite-plugin-dts": "^2.3.0",
|
"vite-plugin-dts": "^2.3.0",
|
||||||
"y-indexeddb": "^9.0.11"
|
"y-indexeddb": "^9.0.11"
|
||||||
|
|||||||
217
yarn.lock
217
yarn.lock
@@ -50,12 +50,12 @@ __metadata:
|
|||||||
"@affine/i18n": "workspace:*"
|
"@affine/i18n": "workspace:*"
|
||||||
"@affine/jotai": "workspace:*"
|
"@affine/jotai": "workspace:*"
|
||||||
"@affine/workspace": "workspace:*"
|
"@affine/workspace": "workspace:*"
|
||||||
"@blocksuite/blocks": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/blocks": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/editor": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/editor": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/icons": ^2.1.19
|
"@blocksuite/icons": ^2.1.19
|
||||||
"@blocksuite/lit": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/lit": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@dnd-kit/core": ^6.0.8
|
"@dnd-kit/core": ^6.0.8
|
||||||
"@dnd-kit/sortable": ^7.0.2
|
"@dnd-kit/sortable": ^7.0.2
|
||||||
"@emotion/cache": ^11.11.0
|
"@emotion/cache": ^11.11.0
|
||||||
@@ -148,6 +148,7 @@ __metadata:
|
|||||||
"@types/fs-extra": ^11.0.1
|
"@types/fs-extra": ^11.0.1
|
||||||
"@types/uuid": ^9.0.1
|
"@types/uuid": ^9.0.1
|
||||||
better-sqlite3: ^8.3.0
|
better-sqlite3: ^8.3.0
|
||||||
|
cheerio: ^1.0.0-rc.12
|
||||||
chokidar: ^3.5.3
|
chokidar: ^3.5.3
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
electron: 24.4.0
|
electron: 24.4.0
|
||||||
@@ -175,7 +176,7 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/env@workspace:packages/env"
|
resolution: "@affine/env@workspace:packages/env"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
lit: ^2.7.4
|
lit: ^2.7.4
|
||||||
next: ^13.4.2
|
next: ^13.4.2
|
||||||
react: 18.3.0-canary-16d053d59-20230506
|
react: 18.3.0-canary-16d053d59-20230506
|
||||||
@@ -221,11 +222,11 @@ __metadata:
|
|||||||
resolution: "@affine/jotai@workspace:packages/jotai"
|
resolution: "@affine/jotai@workspace:packages/jotai"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@affine/env": "workspace:*"
|
"@affine/env": "workspace:*"
|
||||||
"@blocksuite/blocks": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/blocks": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/editor": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/editor": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/lit": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/lit": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
jotai: ^2.1.0
|
jotai: ^2.1.0
|
||||||
lottie-web: ^5.11.0
|
lottie-web: ^5.11.0
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -310,12 +311,12 @@ __metadata:
|
|||||||
"@affine/jotai": "workspace:*"
|
"@affine/jotai": "workspace:*"
|
||||||
"@affine/templates": "workspace:*"
|
"@affine/templates": "workspace:*"
|
||||||
"@affine/workspace": "workspace:*"
|
"@affine/workspace": "workspace:*"
|
||||||
"@blocksuite/blocks": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/blocks": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/editor": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/editor": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/icons": ^2.1.19
|
"@blocksuite/icons": ^2.1.19
|
||||||
"@blocksuite/lit": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/lit": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@dnd-kit/core": ^6.0.8
|
"@dnd-kit/core": ^6.0.8
|
||||||
"@dnd-kit/sortable": ^7.0.2
|
"@dnd-kit/sortable": ^7.0.2
|
||||||
"@emotion/cache": ^11.11.0
|
"@emotion/cache": ^11.11.0
|
||||||
@@ -2144,14 +2145,14 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/blocks@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/blocks@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/blocks@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/blocks@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/connector": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/connector": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/phasor": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/phasor": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/virgo": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/virgo": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@popperjs/core": ^2.11.6
|
"@popperjs/core": ^2.11.6
|
||||||
hotkeys-js: ^3.10.1
|
hotkeys-js: ^3.10.1
|
||||||
lit: ^2.7.3
|
lit: ^2.7.3
|
||||||
@@ -2160,40 +2161,40 @@ __metadata:
|
|||||||
turndown: ^7.1.1
|
turndown: ^7.1.1
|
||||||
zod: ^3.21.4
|
zod: ^3.21.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@blocksuite/lit": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/lit": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
yjs: ^13
|
yjs: ^13
|
||||||
checksum: d17ffb4194ca0ea3431bc7b3abef34f0d265c143a9a6ca446b64761d993b1f7c7348afd83bb9a75be5b09f74773ddc94c3c9f1e81ba6d3e110ed2f9b73a727cb
|
checksum: 68fca65f12d7109c362b8c29b076149463ad11c892e7db243ea0c2dd33bfb65233a14339d716897562bdbb6da98e1d52f33a403a6af11685f8dd4f9182e185d2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/connector@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/connector@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/connector@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/connector@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
checksum: e7e0a1bcc8d02816e8885858dc7a7a3b81bfd5dd24d55e0c5a0f75f399fddc4f2491d25d3afa0f95e9c6cd23db464577bc884f6acd7c4a06f102f370ad95b2f2
|
checksum: dbee999f2ffd9784e9c57df397bf1ecc3d9e88a9a640585c6e63ede3a3e66f44bf9907db800f0a64ce79c9ba6a697bee28e74a0c8b4383b073f3be61209a6816
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/editor@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/editor@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/editor@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/editor@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@toeverything/theme": 0.5.9
|
"@toeverything/theme": 0.5.9
|
||||||
lit: ^2.7.3
|
lit: ^2.7.3
|
||||||
marked: ^4.2.12
|
marked: ^4.2.12
|
||||||
turndown: ^7.1.1
|
turndown: ^7.1.1
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@blocksuite/blocks": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/blocks": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/lit": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/lit": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
checksum: db7dbf81b428b1eeac50cdce4212890beb8fef7511eae4d38670012435f9d9d11b8cd268a54f977fe6659256d2df70d60f56c266ac5335cc8aa8a0012db440cf
|
checksum: 1de0b76a10eaa5fe8b8a7803cbe0b4817c39b8e9351bda2dec2c7859042ab0124c4f5649097c085080c574cf2b9cfd3560cc9f966297ad775b4179bf95775000
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/global@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/global@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/global@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/global@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-colors: ^4.1.3
|
ansi-colors: ^4.1.3
|
||||||
zod: ^3.21.4
|
zod: ^3.21.4
|
||||||
@@ -2202,7 +2203,7 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
lit:
|
lit:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 0370200a36cec7c12346e274d3c06485b529dff36d1622e1de9d8833c0c516f802abcfdc81aafa42e75eecdb6edd7bda6ee24dbad2e1c0531e6e6e4027d9862e
|
checksum: 76f321e5e7f76011628784bc084cf724c67e357bbb286c3f843388d9a35903e963bb89fea8a7ed39c29990b6ccde8ab9fde1532da9eb47d97ff481a2950d34ce
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -2216,38 +2217,38 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/lit@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/lit@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/lit@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/lit@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
lit: ^2.7.3
|
lit: ^2.7.3
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
checksum: 029a462a7cdfda2d370a2df57bba690f72d9033516af221a449bedcd59b29d8dd59a1a4e024340756498f9f45c51ee1ca977176df6e46be347ce002199f2a5ae
|
checksum: 7d1ce7a49758dab759af36fa5b234ac1eeb557cdae72d517b392c49ece7e15a79044951648363fce03f9b54150c39eca8b130349dfcd1e3d4a3e1b0f17c42b21
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/phasor@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/phasor@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/phasor@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/phasor@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
fractional-indexing: ^3.2.0
|
fractional-indexing: ^3.2.0
|
||||||
roughjs: ^4.5.2
|
roughjs: ^4.5.2
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
nanoid: ^4
|
nanoid: ^4
|
||||||
yjs: ^13
|
yjs: ^13
|
||||||
checksum: 52979810f3594486e78c109829ffd6ca5a41c7ac6981ebef617c4947c8d7084337b042e0c40fda119a148204c9e0c09e66ee39a0da43f7b1fab1a1f9e561a795
|
checksum: 2708d5d0cb41ba5279871f5f9b57b5fcbf72ebf29d034b6aa36465b82a34aa029dc6ed16b3f1cb92baee0b8574594bfcdd8cdd895c5f174d13cddf357f3ef46a
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/store@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/store@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/store@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/store@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/virgo": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/virgo": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@types/flexsearch": ^0.7.3
|
"@types/flexsearch": ^0.7.3
|
||||||
buffer: ^6.0.3
|
buffer: ^6.0.3
|
||||||
flexsearch: 0.7.21
|
flexsearch: 0.7.21
|
||||||
@@ -2262,20 +2263,20 @@ __metadata:
|
|||||||
zod: ^3.21.4
|
zod: ^3.21.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
yjs: ^13
|
yjs: ^13
|
||||||
checksum: 9e6df21af3dc654fee45d150e1a139f9d9349711eaa3751e82154b0f4de74ae7a4b2b0741535ce34e675974cff9da6a72cefa57aa9c11fce96aa60aa5a12f112
|
checksum: 92b14fcbafefba25dc95a3820df73715fd211464a2742ac7124420a4a393075fe1dd699e4f218a257291622f1f076c974d37b4ca00d5d0b40073a0b68490264f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@blocksuite/virgo@npm:0.0.0-20230525011821-20259c76-nightly":
|
"@blocksuite/virgo@npm:0.0.0-20230526010315-e1d4bf97-nightly":
|
||||||
version: 0.0.0-20230525011821-20259c76-nightly
|
version: 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
resolution: "@blocksuite/virgo@npm:0.0.0-20230525011821-20259c76-nightly"
|
resolution: "@blocksuite/virgo@npm:0.0.0-20230526010315-e1d4bf97-nightly"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/global": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/global": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
zod: ^3.21.4
|
zod: ^3.21.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
lit: ^2.7
|
lit: ^2.7
|
||||||
yjs: ^13
|
yjs: ^13
|
||||||
checksum: aa14f5d16682689feab13e2723314d86257fa47a4ce6e55c7367289bed22b10df5158f49d77ccd04bedae4d8009a80b3c7143a6c379492cba18c96025b92891f
|
checksum: 3eea9e391e005ff63b527cfb3950a212acd762dcce27c59ca397d07d302b7dfb5c1b36371d8127204a2e04cfb7b88f00aa4ddb24d8495419665f4fb86d1c14f9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -8677,8 +8678,8 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@toeverything/y-indexeddb@workspace:packages/y-indexeddb"
|
resolution: "@toeverything/y-indexeddb@workspace:packages/y-indexeddb"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/blocks": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/blocks": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
"@blocksuite/store": 0.0.0-20230525011821-20259c76-nightly
|
"@blocksuite/store": 0.0.0-20230526010315-e1d4bf97-nightly
|
||||||
idb: ^7.1.1
|
idb: ^7.1.1
|
||||||
vite: ^4.3.8
|
vite: ^4.3.8
|
||||||
vite-plugin-dts: ^2.3.0
|
vite-plugin-dts: ^2.3.0
|
||||||
@@ -11323,6 +11324,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"boolbase@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "boolbase@npm:1.0.0"
|
||||||
|
checksum: 3e25c80ef626c3a3487c73dbfc70ac322ec830666c9ad915d11b701142fab25ec1e63eff2c450c74347acfd2de854ccde865cd79ef4db1683f7c7b046ea43bb0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"boolean@npm:^3.0.1":
|
"boolean@npm:^3.0.1":
|
||||||
version: 3.2.0
|
version: 3.2.0
|
||||||
resolution: "boolean@npm:3.2.0"
|
resolution: "boolean@npm:3.2.0"
|
||||||
@@ -11914,6 +11922,35 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"cheerio-select@npm:^2.1.0":
|
||||||
|
version: 2.1.0
|
||||||
|
resolution: "cheerio-select@npm:2.1.0"
|
||||||
|
dependencies:
|
||||||
|
boolbase: ^1.0.0
|
||||||
|
css-select: ^5.1.0
|
||||||
|
css-what: ^6.1.0
|
||||||
|
domelementtype: ^2.3.0
|
||||||
|
domhandler: ^5.0.3
|
||||||
|
domutils: ^3.0.1
|
||||||
|
checksum: 843d6d479922f28a6c5342c935aff1347491156814de63c585a6eb73baf7bb4185c1b4383a1195dca0f12e3946d737c7763bcef0b9544c515d905c5c44c5308b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"cheerio@npm:^1.0.0-rc.12":
|
||||||
|
version: 1.0.0-rc.12
|
||||||
|
resolution: "cheerio@npm:1.0.0-rc.12"
|
||||||
|
dependencies:
|
||||||
|
cheerio-select: ^2.1.0
|
||||||
|
dom-serializer: ^2.0.0
|
||||||
|
domhandler: ^5.0.3
|
||||||
|
domutils: ^3.0.1
|
||||||
|
htmlparser2: ^8.0.1
|
||||||
|
parse5: ^7.0.0
|
||||||
|
parse5-htmlparser2-tree-adapter: ^7.0.0
|
||||||
|
checksum: 5d4c1b7a53cf22d3a2eddc0aff70cf23cbb30d01a4c79013e703a012475c02461aa1fcd99127e8d83a02216386ed6942b2c8103845fd0812300dd199e6e7e054
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"chokidar@npm:3.5.3, chokidar@npm:^3.4.2, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3":
|
"chokidar@npm:3.5.3, chokidar@npm:^3.4.2, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3":
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
resolution: "chokidar@npm:3.5.3"
|
resolution: "chokidar@npm:3.5.3"
|
||||||
@@ -12764,6 +12801,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"css-select@npm:^5.1.0":
|
||||||
|
version: 5.1.0
|
||||||
|
resolution: "css-select@npm:5.1.0"
|
||||||
|
dependencies:
|
||||||
|
boolbase: ^1.0.0
|
||||||
|
css-what: ^6.1.0
|
||||||
|
domhandler: ^5.0.2
|
||||||
|
domutils: ^3.0.1
|
||||||
|
nth-check: ^2.0.1
|
||||||
|
checksum: 2772c049b188d3b8a8159907192e926e11824aea525b8282981f72ba3f349cf9ecd523fdf7734875ee2cb772246c22117fc062da105b6d59afe8dcd5c99c9bda
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"css-spring@npm:^4.1.0":
|
"css-spring@npm:^4.1.0":
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
resolution: "css-spring@npm:4.1.0"
|
resolution: "css-spring@npm:4.1.0"
|
||||||
@@ -12780,6 +12830,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"css-what@npm:^6.1.0":
|
||||||
|
version: 6.1.0
|
||||||
|
resolution: "css-what@npm:6.1.0"
|
||||||
|
checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"css.escape@npm:^1.5.1":
|
"css.escape@npm:^1.5.1":
|
||||||
version: 1.5.1
|
version: 1.5.1
|
||||||
resolution: "css.escape@npm:1.5.1"
|
resolution: "css.escape@npm:1.5.1"
|
||||||
@@ -20958,6 +21015,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"nth-check@npm:^2.0.1":
|
||||||
|
version: 2.1.1
|
||||||
|
resolution: "nth-check@npm:2.1.1"
|
||||||
|
dependencies:
|
||||||
|
boolbase: ^1.0.0
|
||||||
|
checksum: 5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"nullthrows@npm:^1.1.1":
|
"nullthrows@npm:^1.1.1":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "nullthrows@npm:1.1.1"
|
resolution: "nullthrows@npm:1.1.1"
|
||||||
@@ -21499,6 +21565,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"parse5-htmlparser2-tree-adapter@npm:^7.0.0":
|
||||||
|
version: 7.0.0
|
||||||
|
resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0"
|
||||||
|
dependencies:
|
||||||
|
domhandler: ^5.0.2
|
||||||
|
parse5: ^7.0.0
|
||||||
|
checksum: fc5d01e07733142a1baf81de5c2a9c41426c04b7ab29dd218acb80cd34a63177c90aff4a4aee66cf9f1d0aeecff1389adb7452ad6f8af0a5888e3e9ad6ef733d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"parse5@npm:^7.0.0":
|
||||||
|
version: 7.1.2
|
||||||
|
resolution: "parse5@npm:7.1.2"
|
||||||
|
dependencies:
|
||||||
|
entities: ^4.4.0
|
||||||
|
checksum: 59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"parseurl@npm:~1.3.2, parseurl@npm:~1.3.3":
|
"parseurl@npm:~1.3.2, parseurl@npm:~1.3.3":
|
||||||
version: 1.3.3
|
version: 1.3.3
|
||||||
resolution: "parseurl@npm:1.3.3"
|
resolution: "parseurl@npm:1.3.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user