Skip to content

Commit

Permalink
feat: 优化图片上传
Browse files Browse the repository at this point in the history
  • Loading branch information
moshangqi committed Mar 21, 2024
1 parent 0fbcec9 commit 8ce6eb1
Show file tree
Hide file tree
Showing 23 changed files with 300 additions and 54 deletions.
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"name": "yuque-chrome-extension",
"version": "0.5.5",
"version": "0.5.9",
"description": "语雀浏览器插件",
"private": true,
"releaseNotes": [
"[特性] 支持全文剪藏",
"[优化] 优化插件性能",
"[修复] 修复上传图片失败的异常处理"
"[特性] 支持行内代码块",
"[优化] 优化图片剪藏逻辑"
],
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions scripts/preset-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const {

const { distFolder, sleep } = require('./common');

const LAKE_EDITOR_VERSION = '1.11.0';
const LAKE_EDITOR_VERSION = '1.17.0';

const lakeIconURL = 'https://mdn.alipayobjects.com/design_kitchencore/afts/file/HWy0Q7LuuV0AAAAAAAAAAAAADhulAQBr';
const lakeIconURL = 'https://mdn.alipayobjects.com/design_kitchencore/afts/file/GxqwR5_S3xIAAAAAAAAAAAAADhulAQBr';

// 必须包含 name 名称
const remoteAssetsUrls = {
Expand Down
5 changes: 5 additions & 0 deletions src/background/core/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getMsgId, transformUrlToFile } from '@/isomorphic/util';
import { PageEventTypes } from '@/isomorphic/event/pageEvent';
import ExtensionMessage from '@/isomorphic/extensionMessage/extensionMessage';
import { ExtensionMessageListener } from '@/isomorphic/extensionMessage/interface';
import Env from '@/isomorphic/env';
import chromeExtension from './chromeExtension';
import { getCurrentAccount } from './util';

Expand Down Expand Up @@ -59,6 +60,10 @@ export default class HttpClient {
private decoder = new TextDecoder();

constructor() {
// 只在后台监听,其余系统页都不监听
if (!Env.isBackground) {
return;
}
// 监听关闭的请求
ExtensionMessage.addListener(ExtensionMessageListener.callRequestService, async (data, requestId, { tab }) => {
const { type, methodParams = {}, callbackFnId } = data;
Expand Down
4 changes: 3 additions & 1 deletion src/components/AccountLayout/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import styles from './Login.module.less';

interface ILoginProps {
forceUpgradeHtml?: string;
setUser: (user: any) => void;
}

function Login(props: ILoginProps) {
const { forceUpgradeHtml } = props;
const { forceUpgradeHtml, setUser } = props;

const onLogin = async () => {
const user = await backgroundBridge.user.login();
setUser(user);
if (!user) {
message.error(__i18n('登录失败'));
return;
Expand Down
2 changes: 1 addition & 1 deletion src/components/AccountLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function AccountLayout(props: IAccountLayoutProps) {
{isLogined && !forceUpgradeHtml ? (
props.children
) : (
<Login forceUpgradeHtml={forceUpgradeHtml} />
<Login forceUpgradeHtml={forceUpgradeHtml} setUser={setUser} />
)}
</AccountContext.Provider>
);
Expand Down
4 changes: 4 additions & 0 deletions src/components/AntdLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { ConfigProvider } from 'antd';
import { ConfigProviderProps } from 'antd/es/config-provider';
import { SidePanelZIndex } from '@/config';

interface IAntdLayoutProps {
children: React.ReactNode;
Expand All @@ -25,6 +26,9 @@ function AntdLayout(props: IAntdLayoutProps) {
colorBgSpotlight: 'rgba(0, 0, 0, 0.75)',
fontSize: 12,
},
Message: {
zIndexPopup: SidePanelZIndex,
},
},
}}
{...props.config}
Expand Down
34 changes: 28 additions & 6 deletions src/components/SuperSideBar/impl/ClipAssistant/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,16 @@ import AddTagButton from './component/AddTagButton';
import TagList from './component/TagList';
import styles from './index.module.less';

const LoadingMessage = {
ocr: __i18n('正在识别中'),
parse: __i18n('正在解析中'),
};

function ClipContent() {
const editorRef = useRef<IEditorRef>(null);
const shortcutMap = useClipShortCut();
const [loading, setLoading] = useState<{
type?: 'ocr';
type?: keyof typeof LoadingMessage;
loading: boolean;
}>({
loading: false,
Expand Down Expand Up @@ -127,12 +132,13 @@ function ClipContent() {
setLoading({ loading: false });
};

const onUploadImage = useCallback(async (params: { data: string }) => {
const onUploadImage = useCallback(async (params: { data: any }) => {
const file = await transformUrlToFile(params.data);
const res = await Promise.all([webProxy.upload.attach(params.data), ocrManager.startOCR('file', file)]);
const res = await webProxy.upload.attach(params.data);
const ocrTask = ocrManager.startOCR('file', file);
return {
...(res[0] || {}),
ocrLocations: res[1],
...(res || {}),
ocrTask,
};
}, []);

Expand All @@ -148,12 +154,18 @@ function ClipContent() {
};

const onClipPage = async () => {
setLoading({ loading: true, type: 'parse' });
const html = await backgroundBridge.clip.clipPage();
if (!html) {
setLoading({ loading: false });
return;
}
const isAddLink = await addLinkWhenEmpty();
if (!isAddLink) {
editorRef.current?.insertBreakLine();
}
editorRef.current?.appendContent(html);
setLoading({ loading: false });
};

const addLinkWhenEmpty = async () => {
Expand All @@ -167,12 +179,18 @@ function ClipContent() {
};

const onSelectArea = async () => {
setLoading({ loading: true, type: 'parse' });
const html = await backgroundBridge.clip.selectArea();
if (!html) {
setLoading({ loading: false });
return;
}
const isAddLink = await addLinkWhenEmpty();
if (!isAddLink) {
editorRef.current?.insertBreakLine();
}
editorRef.current?.appendContent(html);
setLoading({ loading: false });
};

const onScreenOcr = async () => {
Expand All @@ -199,6 +217,10 @@ function ClipContent() {
};
});
const text = textArray?.map(item => item.text)?.join('') || '';
if (!text) {
setLoading({ loading: false });
return;
}
const isAddLink = await addLinkWhenEmpty();
if (!isAddLink) {
editorRef.current?.insertBreakLine();
Expand All @@ -224,7 +246,7 @@ function ClipContent() {
if (!loading.loading) {
return null;
}
const text = loading.type === 'ocr' ? '正在识别中' : '';
const text = LoadingMessage[loading.type as keyof typeof LoadingMessage] || '';
return (
<div className={styles.loadingWrapper}>
<Spin />
Expand Down
16 changes: 16 additions & 0 deletions src/components/lake-editor/editor-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ export function InjectEditorPlugin({ EditorPlugin, KernelPlugin, PositionUtil, O
},
},
);
htmlService.registerHTMLNodeReader(
['code'],
{
readNode(context: any, node: any) {
context.setNode({
id: node.attrs.id || '',
type: 'element',
name: 'code',
attrs: {},
});
},
leaveNode() {
// ignore empty
},
},
);
}
}
}
Expand Down
36 changes: 10 additions & 26 deletions src/components/lake-editor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,38 +136,22 @@ export default forwardRef<IEditorRef, EditorProps>((props, ref) => {
return !url?.startsWith('https://cdn.nlark.com/yuque');
},
createUploadPromise: props.uploadImage,
editUI: class extends win.Doc.EditCardUI.extend(win.Doc.Plugins.Image.editImageUIAddon) {
init(...args: any[]) {
super.init(...args);
this.on('uploadSuccess', (data: { ocrTask: Promise<any>}) => {
data.ocrTask.then(res => {
this.cardData.setImageInfo({ ...data, ocrLocations: res });
});
});
}
},
innerButtonWidgets: [
{
name: 'ocr',
title: 'OCR',
icon: <OcrIconSvg fill="#fff" />,
enable: (cardUI: any) => {
cardUI.on('uploadSuccess', () => {
setTimeout(() => {
if (!cardUI.cardData._cardValue?.ocr?.length) {
return;
}
cardUI.uiViewProxy.rerender({
innerButtonWidgets:
cardUI.pluginOption.innerButtonWidgets.map(
(widget: any) => ({
...widget,
execute: () => {
widget.execute(cardUI);
},
enable:
typeof widget.enable === 'function'
? () => {
return widget.enable(cardUI);
}
: () => {
return !!widget.enable;
},
}),
),
});
}, 500);
});
return cardUI.cardData.getOcrLocations()?.length > 0;
},
execute: (cardUI: any) => {
Expand Down
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ export const TRACERT_CONFIG = {
spmAPos: 'a385',
spmBPos: 'b65721',
};

// sidePanel 的 zIndex 层级
export const SidePanelZIndex = 2147483646;
30 changes: 29 additions & 1 deletion src/core/ocr-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ class OCRManager {
private ocrIframeId = 'yq-ocr-iframe-id';
private sendMessageRef: ((requestData: { action: string; data?: any }) => Promise<any>) | undefined;
private initSidePanelPromise: Promise<boolean> | undefined;
private ocrQueue: Array<{
task: () => Promise<Array<IOcrResultItem>>;
resolve: (res: Array<IOcrResultItem>) => void;
}> = [];
// 当前运行数
private runningTasks = 0;
// 最大的任务运行数
private concurrentTasks = 5;

async init() {
if (this.initSidePanelPromise) {
Expand Down Expand Up @@ -78,7 +86,7 @@ class OCRManager {
return this.initSidePanelPromise;
}

async startOCR(type: 'file' | 'blob' | 'url', content: File | Blob | string) {
private async executeOcr(type: 'file' | 'blob' | 'url', content: File | Blob | string) {
// 调用 ocr 时,开始 ocr 等预热
await this.init();
const enableOcrStatus = await ocrManager.isWebOcrReady();
Expand Down Expand Up @@ -109,6 +117,26 @@ class OCRManager {
return [];
}

async startOCR(type: 'file' | 'blob' | 'url', content: File | Blob | string) {
return await new Promise(resolve => {
this.ocrQueue.push({ resolve, task: () => this.executeOcr(type, content) });
this.dequeue();
});
}

private dequeue() {
if (this.runningTasks >= this.concurrentTasks || !this.ocrQueue.length) {
return;
}
const { task, resolve } = this.ocrQueue.shift()!;
this.runningTasks++;
task().then((res: any) => {
this.runningTasks--;
resolve(res);
this.dequeue();
});
}

async isWebOcrReady() {
const result = await this.sendMessage('isWebOcrReady');
if (!result) {
Expand Down
8 changes: 8 additions & 0 deletions src/core/parseDom/plugin/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export class CodeParsePlugin extends BasePlugin {
preElements.forEach(pre => {
// 查询所有的代码块
const codeElementArray = pre.querySelectorAll('code');
/**
* 有些页面的代码块不是
* <pre>xxxx</pre>
* 对于这类不处理
*/
if (!codeElementArray.length) {
return;
}
Array.from(pre.childNodes).forEach(item => {
pre.removeChild(item);
});
Expand Down
7 changes: 3 additions & 4 deletions src/core/parseDom/plugin/hexoCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@ export class HexoCodeParsePlugin extends BasePlugin {
}
const codeElement = code.querySelector('pre');
if (codeElement) {
node.parentNode?.appendChild(codeElement);
node.parentNode?.replaceChild(codeElement, node);
}
node.parentNode?.removeChild(node);
};
figures.forEach(figure => {
Array.from(figures).forEach(figure => {
processingCodeBlock(figure);
});
if (figures.length === 0) {
const tables = cloneDom.querySelectorAll('table');
tables.forEach(table => {
Array.from(tables).forEach(table => {
processingCodeBlock(table);
});
}
Expand Down
36 changes: 28 additions & 8 deletions src/core/parseDom/plugin/image.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
import { BasePlugin } from './base';

export class ImageParsePlugin extends BasePlugin {
public parse(cloneDom: HTMLElement): Promise<void> | void {
public async parse(cloneDom: HTMLElement): Promise<void> {
const images = cloneDom.querySelectorAll('img');
images.forEach(image => {
/**
* data-src 占位图
* currentSrc 真实渲染的图片
* src
*/
image.setAttribute('src', image.getAttribute('data-src') || image.currentSrc || image.src);
const requestArray = Array.from(images).map(image => {
return new Promise(async resolve => {
image.setAttribute('src', image.getAttribute('data-src') || image.currentSrc || image.src);
const isOriginImage = /^(http|https):\/\//.test(image.src);
if (!isOriginImage) {
resolve(true);
return;
}
try {
const response = await fetch(image.src);
if (response.status !== 200) {
throw new Error('Error fetching image');
}
const blob = await response.blob(); // 将响应体转换为 Blob
const reader = new FileReader();
reader.readAsDataURL(blob); // 读取 Blob 数据并编码为 Base64
reader.onloadend = () => {
// 获取 Base64 编码的数据
const base64data = reader.result;
image.src = base64data as string;
resolve(true);
};
} catch (e: any) {
resolve(true);
}
});
});
await Promise.all(requestArray);
}
}
Loading

0 comments on commit 8ce6eb1

Please sign in to comment.