diff --git a/src/assets/icons/arxiv.svg b/src/assets/icons/arxiv.svg
new file mode 100644
index 0000000..4b8a00e
--- /dev/null
+++ b/src/assets/icons/arxiv.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/containers/ArxivContainer.ts b/src/components/containers/ArxivContainer.ts
new file mode 100644
index 0000000..eab4ade
--- /dev/null
+++ b/src/components/containers/ArxivContainer.ts
@@ -0,0 +1,441 @@
+// Copyright (c) 2024-present AI-Labs
+
+// @ ts-nocheck
+import { EditorEvents } from "@tiptap/core";
+import { t } from "i18next";
+
+import { UAIEditorEventListener, UAIEditorOptions } from "../../core/UAIEditor.ts";
+import { createAvatar } from "../../utils/CreateAvatar.ts";
+import { Icons } from "../Icons.ts";
+import { markdownToHtml } from "../../utils/MarkdownUtil.ts";
+import { AIRequestAction } from "../ai/AIAction.ts";
+
+import closeIcon from "../../assets/icons/close.svg";
+import titleIcon from "../../assets/icons/arxiv.svg";
+import sendIcon from "../../assets/icons/send-message.svg";
+import { FullScreenModal } from "../modals/FullScreenModal.ts";
+
+export class ArxivContainer extends HTMLElement implements UAIEditorEventListener {
+ // 容器元素
+ container!: HTMLElement;
+
+ // 标题元素
+ arxivTitle!: HTMLElement;
+
+ // 内容元素
+ content!: HTMLElement;
+
+ // 文本输入框
+ messageTextarea!: HTMLTextAreaElement;
+ imageButton!: HTMLButtonElement;
+ categorySelect!: HTMLSelectElement;
+ subCategorySelect!: HTMLSelectElement;
+ querySelect!: HTMLSelectElement;
+
+ event!: EditorEvents["create"];
+ options!: UAIEditorOptions;
+
+ currentPage = 1;
+ pageSize = 10;
+
+ categories = [
+ {
+ value: 'all', label: 'All Categories', children: [
+ { value: 'all', label: 'All Categories' }
+ ]
+ },
+ {
+ value: 'cs', label: 'Computer Science', children: [
+ { value: 'cs', label: 'All Computer Science' },
+ { value: 'cs.AI', label: 'Artificial Intelligence' },
+ { value: 'cs.AR', label: 'Hardware Architecture' },
+ { value: 'cs.CC', label: 'Computational Complexity' },
+ ]
+ },
+ {
+ value: 'econ', label: 'Economics', children: [
+ { value: 'econ', label: 'All Economics' },
+ { value: 'econ.EM', label: 'Econometrics' },
+ { value: 'econ.GN', label: 'General Economics' },
+ { value: 'econ.TH', label: 'Theoretical Economics' },
+ ]
+ },
+ {
+ value: 'eess', label: 'Electrical Engineering and Systems Science', children: [
+ { value: 'eess', label: 'Electrical Engineering and Systems Science' }
+ ]
+ },
+ {
+ value: 'math', label: 'Mathematics', children: [
+ { value: 'math', label: 'Mathematics' }
+ ]
+ },
+ {
+ value: 'physics', label: 'Physics', children: [
+ { value: 'physics', label: 'Physics' }
+ ]
+ },
+ {
+ value: 'q-bio', label: 'Quantitative Biology', children: [
+ { value: 'q-bio', label: 'Quantitative Biology' }
+ ]
+ },
+ {
+ value: 'q-fin', label: 'Quantitative Finance', children: [
+ { value: 'q-fin', label: 'Quantitative Finance' }
+ ]
+ },
+ {
+ value: 'stat', label: 'Statistics', children: [
+ { value: 'stat', label: 'Statistics' }
+ ]
+ },
+ ];
+ queries = [
+ { value: 'all', label: 'All Papers' },
+ { value: 'ti', label: 'Titles' },
+ { value: 'au', label: 'Authors' },
+ { value: 'abs', label: 'Abstracts' },
+ { value: 'co', label: 'Comments' },
+ ]
+ constructor() {
+ super();
+
+ // 初始化容器
+ this.container = document.createElement("div");
+ this.container.classList.add("uai-pannel-container");
+ this.appendChild(this.container);
+
+ // 初始化标题
+ this.arxivTitle = document.createElement("div");
+ this.arxivTitle.classList.add("uai-pannel-title");
+ this.arxivTitle.innerHTML = `
${t('tools.arxiv.title')}`
+ this.container.appendChild(this.arxivTitle);
+ // 初始化关闭按钮
+ const dialogClose = document.createElement("div");
+ dialogClose.classList.add("uai-pannel-close");
+ dialogClose.innerHTML = `
`;
+ // 点击关闭按钮时关闭容器
+ dialogClose.addEventListener("click", () => {
+ this.style.display = "none";
+ })
+ this.arxivTitle.appendChild(dialogClose);
+
+ const categorySelectDiv = document.createElement('div');
+ categorySelectDiv.classList.add('uai-container-app-models');
+
+ const categorySelectText = document.createElement('div');
+ categorySelectText.innerText = "类别:";
+ categorySelectDiv.appendChild(categorySelectText);
+
+ this.categorySelect = document.createElement('select');
+ this.categorySelect.classList.add('uai-model-select');
+ categorySelectDiv.appendChild(this.categorySelect);
+ this.container.appendChild(categorySelectDiv);
+
+ const subCategorySelectDiv = document.createElement('div');
+ subCategorySelectDiv.classList.add('uai-container-app-models');
+
+ const subCategorySelectText = document.createElement('div');
+ subCategorySelectText.innerText = "子类:";
+ subCategorySelectDiv.appendChild(subCategorySelectText);
+
+ this.subCategorySelect = document.createElement('select');
+ this.subCategorySelect.classList.add('uai-model-select');
+ subCategorySelectDiv.appendChild(this.subCategorySelect);
+ this.container.appendChild(subCategorySelectDiv);
+
+ const querySelectDiv = document.createElement('div');
+ querySelectDiv.classList.add('uai-container-app-models');
+
+ const querySelectText = document.createElement('div');
+ querySelectText.innerText = "查询:";
+ querySelectDiv.appendChild(querySelectText);
+
+ this.querySelect = document.createElement('select');
+ this.querySelect.classList.add('uai-model-select');
+ querySelectDiv.appendChild(this.querySelect);
+ this.container.appendChild(querySelectDiv);
+
+ // 初始化对话内容容器
+ this.content = document.createElement('div');
+ this.content.classList.add('uai-ai-content');
+ this.container.appendChild(this.content);
+
+ // 定义一个监听,当聊天内容有变动时,聊天内容容器滚动到最底部
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.type === 'childList' || mutation.type === 'characterData') {
+ this.content.scrollTop = this.content.scrollHeight;
+ }
+ })
+ })
+ // 配置MutationObserver
+ const config = { childList: true, characterData: true };
+
+ // 开始监听div的变化
+ observer.observe(this.content, config);
+
+ // 定义对话输入框组件
+ const divAppInput = document.createElement('div');
+ divAppInput.classList.add('uai-container-app-input');
+
+ const divMessageContainer = document.createElement('div');
+ divMessageContainer.classList.add('uai-message-container');
+
+ this.messageTextarea = document.createElement('textarea');
+ this.messageTextarea.classList.add('uai-message-textarea');
+ this.messageTextarea.rows = 1;
+ this.messageTextarea.placeholder = '请输入内容... Shift+Enter换行;Enter发送';
+
+ // 定义键盘事件,Enter发送数据
+ this.messageTextarea.addEventListener('keydown', (event) => {
+ this.handleKeyDown(event);
+ })
+ // 定义输入事件,输入框自动根据输入内容调整大小
+ this.messageTextarea.addEventListener('input', () => {
+ this.messageTextarea.style.height = 'auto';
+ this.messageTextarea.style.height = this.messageTextarea.scrollHeight + 'px';
+ })
+ divMessageContainer.appendChild(this.messageTextarea);
+
+ // 初始化发送按钮
+ this.imageButton = document.createElement('button');
+ this.imageButton.classList.add('uai-image-button');
+ this.imageButton.innerHTML = `
`;
+ this.imageButton.addEventListener('click', () => {
+ this.handleSearch(this.messageTextarea.value)
+ })
+ divMessageContainer.appendChild(this.imageButton);
+
+ divAppInput.appendChild(divMessageContainer);
+ this.container.appendChild(divAppInput);
+ }
+
+ onCreate(event: EditorEvents["create"], options: UAIEditorOptions) {
+ this.event = event;
+ this.options = options;
+
+ this.categories.forEach(cat => {
+ const option = document.createElement('option');
+ option.value = cat.value;
+ option.textContent = cat.label;
+ this.categorySelect.appendChild(option);
+ });
+
+ this.categorySelect.addEventListener('change', () => {
+ const subCategories = this.categories.filter(cat => cat.value === this.categorySelect.value).flatMap(cat => cat.children);
+ this.subCategorySelect.innerHTML = '';
+ subCategories.forEach(cat => {
+ const option = document.createElement('option');
+ option.value = cat.value;
+ option.textContent = cat.label;
+ this.subCategorySelect.appendChild(option);
+ });
+ });
+
+ // 触发 categorySelect 的 change 事件,以初始化子类选项
+ this.categorySelect.dispatchEvent(new Event('change'));
+
+ this.queries.forEach(query => {
+ const option = document.createElement('option');
+ option.value = query.value;
+ option.textContent = query.label;
+ this.querySelect.appendChild(option);
+ });
+ }
+
+ onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) {
+
+ }
+
+ onEditableChange(editable: boolean) {
+
+ }
+
+ handleKeyDown(event: KeyboardEvent) {
+ // 检查是否按下了 Shift 和 Enter 键
+ if (event.shiftKey && event.key === 'Enter') {
+ // 如果是 Shift + Enter,允许默认行为(通常是换行)
+ return;
+ }
+ // 检查是否按下了 Enter 键
+ if (event.key === 'Enter') {
+ // 阻止默认的换行行为
+ event.preventDefault();
+ // 处理发送消息的逻辑
+ this.handleSearch(this.messageTextarea.value);
+ }
+ }
+
+ handleSearch(message: string) {
+ try {
+ if (message.trim() !== '') {
+ // 创建用户消息容器
+ const userMessageDiv = document.createElement('div');
+ userMessageDiv.classList.add('message');
+ userMessageDiv.classList.add('message-sent');
+
+ // 创建用户发送消息容器
+ const userBubbleDiv = document.createElement('div');
+ userBubbleDiv.classList.add('markdown-body');
+ userBubbleDiv.classList.add('bubble');
+ userBubbleDiv.classList.add('bubble-sent');
+ userBubbleDiv.innerHTML = markdownToHtml(message);
+ userBubbleDiv.setAttribute("InnerContent", message);
+
+ // 创建用户输入的功能按钮
+ const userActionDown = document.createElement(
+ 'uai-editor-ai-action-request-action'
+ ) as AIRequestAction;
+ userActionDown.classList.add('hidden-content');
+ userActionDown.onCreate(this.event, this.options);
+ userActionDown.bindSourceElement(userBubbleDiv, this.messageTextarea);
+
+ // 创建用户头像容器
+ const userAvatarDiv = createAvatar(Icons.UserAvatar);
+ userAvatarDiv.classList.add('user-avatar');
+
+ // 用户消息容器中,添加功能按钮
+ userMessageDiv.appendChild(userActionDown);
+ // 用户消息容器中,添加用户发送内容
+ userMessageDiv.appendChild(userBubbleDiv);
+ // 用户消息容器中,添加用户头像
+ userMessageDiv.appendChild(userAvatarDiv);
+
+ // 展示用户发送的消息
+ this.content.appendChild(userMessageDiv);
+
+ // 清空消息输入框
+ this.messageTextarea.value = '';
+ this.messageTextarea.style.height = 'auto';
+ this.messageTextarea.style.height = this.messageTextarea.scrollHeight + 'px';
+ this.messageTextarea.focus();
+
+ // 发送消息并等待响应
+ this.fetchArxivData(message);
+ }
+ } catch (error) {
+ console.error('arXiv API error:', error);
+ }
+ }
+
+ fetchArxivData(message: string) {
+ // 创建模型响应消息容器
+ const botResponseDiv = document.createElement('div');
+ botResponseDiv.classList.add('message');
+ botResponseDiv.classList.add('message-received');
+
+ // 定义模型头像容器
+ const botAvatarDiv = createAvatar(Icons.BotAvatar);
+ botAvatarDiv.classList.add('bot-avatar');
+
+ // 定义模型输出内容容器
+ const botBubbleDiv = document.createElement('div');
+ botBubbleDiv.classList.add('markdown-body');
+ botBubbleDiv.classList.add('bubble');
+ botBubbleDiv.classList.add('bubble-received');
+ botBubbleDiv.innerHTML = Icons.Loading;
+
+ // 在模型响应消息容器中,添加头像
+ botResponseDiv.appendChild(botAvatarDiv);
+ // 在模型响应消息容器中,添加模型响应消息内容
+ botResponseDiv.appendChild(botBubbleDiv);
+
+ this.content.appendChild(botResponseDiv);
+
+ const searchQuery = this.subCategorySelect.value === "all" ? `${this.querySelect.value}:${message}` : `cat:${this.subCategorySelect.value} AND ${this.querySelect.value}:${message}`;
+ const start = (this.currentPage - 1) * this.pageSize;
+ fetch(
+ `https://export.arxiv.org/api/query?search_query=${encodeURIComponent(searchQuery)}&start=${start}&max_results=${this.pageSize}&sortBy=submittedDate&sortOrder=descending`
+ )
+ .then(response => response.text())
+ .then(xmlData => {
+ const arxivs = this.parseXmlResponse(xmlData);
+ botBubbleDiv.innerHTML = "";
+ arxivs.items.forEach(arxiv => {
+ const arxivItem = document.createElement('div');
+ const title = document.createElement('h3');
+ title.textContent = arxiv.title;
+ arxivItem.appendChild(title);
+ const authors = document.createElement('p');
+ authors.textContent = `作者:${arxiv.authors}`;
+ arxivItem.appendChild(authors);
+ const published = document.createElement('p');
+ published.textContent = `日期:${arxiv.published}`;
+ arxivItem.appendChild(published);
+ const summary = document.createElement('p');
+ summary.textContent = `摘要:${arxiv.summary}`;
+ arxivItem.appendChild(summary);
+ const link = document.createElement('a');
+ link.style.margin = '5px';
+ link.textContent = '查看原文';
+ link.addEventListener('click', () => {
+ window.open(arxiv.link, '_blank');
+ });
+ arxivItem.appendChild(link);
+ const pdf = document.createElement('a');
+ pdf.style.margin = '5px';
+ pdf.textContent = '查看论文';
+ pdf.addEventListener('click', () => {
+ const modal = new FullScreenModal(arxiv.title);
+ const body = document.createElement('div');
+ body.classList.add("uai-file-preview-modal-body");
+ const iframe = document.createElement('iframe');
+ iframe.src = arxiv.pdf;
+ body.appendChild(iframe);
+ modal.modalBody.appendChild(body);
+ modal.show();
+ });
+ arxivItem.appendChild(pdf);
+ const ref = document.createElement('a');
+ ref.style.margin = '5px';
+ ref.textContent = '添加引用';
+ ref.addEventListener('click', () => {
+ const {
+ state: { selection }
+ } = this.event.editor;
+
+ this.event.editor.commands.insertContentAt(selection.from, {
+ type: 'file',
+ attrs: {
+ ['url']: arxiv.pdf,
+ name: `${arxiv.title}.pdf`,
+ size: 0,
+ type: "application/pdf",
+ previewType: "application/pdf",
+ },
+ });
+ });
+ arxivItem.appendChild(ref);
+ botBubbleDiv.appendChild(arxivItem);
+ botBubbleDiv.appendChild(document.createElement('hr'));
+ });
+ })
+ .catch(error => {
+ console.error('arXiv API error:', error);
+ });
+ }
+
+ parseXmlResponse(xml: string) {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(xml, 'application/xml');
+
+ const entries = Array.from(doc.querySelectorAll('entry'));
+ return {
+ total: parseInt(doc.querySelector('opensearch>totalResults')?.textContent || '0'),
+ items: entries.map(entry => ({
+ title: entry.querySelector('title')?.textContent?.trim() || '',
+ authors: Array.from(entry.querySelectorAll('author')).map(a =>
+ a.querySelector('name')?.textContent?.trim() || ''
+ ).join(', '),
+ published: new Date(
+ entry.querySelector('published')?.textContent || ''
+ ).toLocaleDateString(),
+ summary: entry.querySelector('summary')?.textContent?.trim() || '',
+ link: entry.querySelector('link[type="text/html"]')?.getAttribute('href') || '',
+ pdf: entry.querySelector('link[type="application/pdf"]')?.getAttribute('href') || '',
+ }))
+ };
+ }
+}
diff --git a/src/components/containers/ChatContainer.ts b/src/components/containers/ChatContainer.ts
index 5ec3064..2954f5c 100644
--- a/src/components/containers/ChatContainer.ts
+++ b/src/components/containers/ChatContainer.ts
@@ -50,7 +50,7 @@ export class ChatContainer extends HTMLElement implements UAIEditorEventListener
// 初始化标题
this.chatTitle = document.createElement("div");
this.chatTitle.classList.add("uai-pannel-title");
- this.chatTitle.innerHTML = `
${t('ai.chat.title')}`
+ this.chatTitle.innerHTML = `
${t('ai.chat.title')}`
this.container.appendChild(this.chatTitle);
// 初始化关闭按钮
diff --git a/src/components/containers/ImageContainer.ts b/src/components/containers/ImageContainer.ts
index c1684a9..1c4d910 100644
--- a/src/components/containers/ImageContainer.ts
+++ b/src/components/containers/ImageContainer.ts
@@ -51,7 +51,7 @@ export class ImageContainer extends HTMLElement implements UAIEditorEventListene
// 初始化标题
this.imageTitle = document.createElement("div");
this.imageTitle.classList.add("uai-pannel-title");
- this.imageTitle.innerHTML = `
${t('ai.image.title')}`;
+ this.imageTitle.innerHTML = `
${t('ai.image.title')}`;
this.container.appendChild(this.imageTitle);
// 初始化关闭容器按钮
diff --git a/src/components/containers/TocContainer.ts b/src/components/containers/TocContainer.ts
index 483e85a..b44e5a6 100644
--- a/src/components/containers/TocContainer.ts
+++ b/src/components/containers/TocContainer.ts
@@ -34,7 +34,7 @@ export class TocContainer extends HTMLElement implements UAIEditorEventListener
// 初始化标题
this.tocTitle = document.createElement("div");
this.tocTitle.classList.add("uai-pannel-title");
- this.tocTitle.innerHTML = `
${t('toc.title')}`;
+ this.tocTitle.innerHTML = `
${t('toc.title')}`;
this.container.appendChild(this.tocTitle);
// 初始化关闭容器按钮
diff --git a/src/components/index.ts b/src/components/index.ts
index ca177df..e2349be 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -7,6 +7,7 @@ import { Header } from "../components/Header.ts";
import { Editor } from "../components/Editor.ts";
import { Footer } from "../components/Footer.ts";
+import { ArxivContainer } from "./containers/ArxivContainer.ts";
import { ChatContainer } from "../components/containers/ChatContainer.ts";
import { ImageContainer } from "../components/containers/ImageContainer.ts";
import { TocContainer } from "../components/containers/TocContainer.ts";
@@ -80,6 +81,7 @@ import { DeleteRow } from "./menus/toolbar/table/DeleteRow.ts";
import { DeleteTable } from "./menus/toolbar/table/DeleteTable.ts";
import { Diagrams } from "./menus/toolbar/tools/Diagrams.ts";
+import { ToggleArxiv } from "./menus/toolbar/tools/ToggleArxiv.ts";
import { ToggleToc } from "./menus/toolbar/page/ToggleToc.ts";
import { BackgroundColor } from "./menus/toolbar/page/BackgroundColor.ts";
@@ -119,6 +121,7 @@ defineCustomElement('uai-editor-header', Header);
defineCustomElement('uai-editor-editor', Editor);
defineCustomElement('uai-editor-footer', Footer);
+defineCustomElement('uai-editor-arxiv-container', ArxivContainer);
defineCustomElement('uai-editor-chat-container', ChatContainer);
defineCustomElement('uai-editor-image-container', ImageContainer);
defineCustomElement('uai-editor-toc-container', TocContainer);
@@ -192,6 +195,7 @@ defineCustomElement('uai-editor-table-menu-delete-row', DeleteRow);
defineCustomElement('uai-editor-table-menu-delete-table', DeleteTable);
defineCustomElement('uai-editor-tools-menu-diagrams', Diagrams);
+defineCustomElement('uai-editor-tools-menu-toggle-arxiv', ToggleArxiv);
defineCustomElement('uai-editor-page-menu-toggle-toc', ToggleToc);
defineCustomElement('uai-editor-page-menu-background-color', BackgroundColor);
diff --git a/src/components/menus/toolbar/Classic.ts b/src/components/menus/toolbar/Classic.ts
index 47c1121..b3ee15d 100644
--- a/src/components/menus/toolbar/Classic.ts
+++ b/src/components/menus/toolbar/Classic.ts
@@ -67,6 +67,7 @@ import { DeleteRow } from "./table/DeleteRow.ts";
import { DeleteTable } from "./table/DeleteTable.ts";
import { Diagrams } from "./tools/Diagrams.ts";
+import { ToggleArxiv } from "./tools/ToggleArxiv.ts";
import { ToggleToc } from "./page/ToggleToc.ts";
import { BackgroundColor } from "./page/BackgroundColor.ts";
@@ -183,6 +184,7 @@ export class Classic extends HTMLElement implements UAIEditorEventListener {
// 工具菜单
toolsMenuDiagrams!: Diagrams;
+ toolsMenuToggleArxiv!: ToggleArxiv;
// 页面菜单
pageMenuToggleToc!: ToggleToc;
@@ -266,7 +268,7 @@ export class Classic extends HTMLElement implements UAIEditorEventListener {
this.classicScrollableContainer.appendChild(this.classicMenuAIScrollable);
}
})
-
+
this.classicScrollableContainer = document.createElement("div");
this.classicScrollableContainer.classList.add("uai-classic-scrollable-container");
this.classicMenu.appendChild(this.classicScrollableContainer);
@@ -450,6 +452,9 @@ export class Classic extends HTMLElement implements UAIEditorEventListener {
this.toolsMenuDiagrams = new Diagrams({ menuType: "button", enable: true, header: "classic", hideText: false });
this.eventComponents.push(this.toolsMenuDiagrams);
+ this.toolsMenuToggleArxiv = new ToggleArxiv({ menuType: "button", enable: true, header: "classic", hideText: false });
+ this.eventComponents.push(this.toolsMenuToggleArxiv);
+
this.pageMenuToggleToc = new ToggleToc({ menuType: "button", enable: true, header: "classic", hideText: false });
this.eventComponents.push(this.pageMenuToggleToc);
@@ -627,6 +632,11 @@ export class Classic extends HTMLElement implements UAIEditorEventListener {
group1.classList.add("uai-classic-virtual-group");
this.classicMenuToolGroup.appendChild(group1);
group1.appendChild(this.toolsMenuDiagrams);
+
+ const group2 = document.createElement("div");
+ group2.classList.add("uai-classic-virtual-group");
+ this.classicMenuToolGroup.appendChild(group2);
+ group2.appendChild(this.toolsMenuToggleArxiv);
}
/**
diff --git a/src/components/menus/toolbar/Ribbon.ts b/src/components/menus/toolbar/Ribbon.ts
index df52fc2..03a6871 100644
--- a/src/components/menus/toolbar/Ribbon.ts
+++ b/src/components/menus/toolbar/Ribbon.ts
@@ -70,6 +70,7 @@ import { DeleteRow } from "./table/DeleteRow.ts";
import { DeleteTable } from "./table/DeleteTable.ts";
import { Diagrams } from "./tools/Diagrams.ts";
+import { ToggleArxiv } from "./tools/ToggleArxiv.ts";
import { ToggleToc } from "./page/ToggleToc.ts";
import { BackgroundColor } from "./page/BackgroundColor.ts";
@@ -185,6 +186,7 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener {
// 工具菜单
toolsMenuDiagrams!: Diagrams;
+ toolsMenuToggleArxiv!: ToggleArxiv;
// 页面菜单
pageMenuToggleToc!: ToggleToc;
@@ -471,6 +473,9 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener {
this.toolsMenuDiagrams = new Diagrams({ menuType: "button", enable: true, huge: true, hideText: false });
this.eventComponents.push(this.toolsMenuDiagrams);
+ this.toolsMenuToggleArxiv = new ToggleArxiv({ menuType: "button", enable: true, huge: true, hideText: false });
+ this.eventComponents.push(this.toolsMenuToggleArxiv);
+
this.pageMenuToggleToc = new ToggleToc({ menuType: "button", enable: true, huge: true, hideText: false });
this.eventComponents.push(this.pageMenuToggleToc);
@@ -734,6 +739,11 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener {
group1.classList.add("uai-ribbon-virtual-group");
this.ribbonMenuToolGroup.appendChild(group1);
group1.appendChild(this.toolsMenuDiagrams);
+
+ const group2 = document.createElement("div");
+ group2.classList.add("uai-ribbon-virtual-group");
+ this.ribbonMenuToolGroup.appendChild(group2);
+ group2.appendChild(this.toolsMenuToggleArxiv);
}
/**
diff --git a/src/components/menus/toolbar/tools/ToggleArxiv.ts b/src/components/menus/toolbar/tools/ToggleArxiv.ts
new file mode 100644
index 0000000..9f9c3ba
--- /dev/null
+++ b/src/components/menus/toolbar/tools/ToggleArxiv.ts
@@ -0,0 +1,82 @@
+// Copyright (c) 2024-present AI-Labs
+
+// @ ts-nocheck
+import { EditorEvents } from "@tiptap/core";
+
+import { t } from "i18next";
+
+import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts";
+import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts";
+
+import icon from "../../../../assets/icons/arxiv.svg";
+
+/**
+ * 人工智能菜单:聊天
+ */
+export class ToggleArxiv extends HTMLElement implements UAIEditorEventListener {
+ // 按钮选项
+ menuButtonOptions: MenuButtonOptions = {
+ menuType: "button",
+ enable: true,
+ icon: icon,
+ hideText: false,
+ text: t('tools.arxiv.title'),
+ tooltip: t('tools.arxiv.title'),
+ }
+
+ // 功能按钮
+ menuButton: MenuButton;
+
+ constructor(options: MenuButtonOptions) {
+ super();
+
+ // 初始化功能按钮选项
+ this.menuButtonOptions = { ...this.menuButtonOptions, ...options };
+
+ // 创建功能按钮
+ this.menuButton = new MenuButton(this.menuButtonOptions);
+ }
+
+ /**
+ * 定义创建方法
+ * @param event
+ * @param options
+ */
+ onCreate(event: EditorEvents["create"], options: UAIEditorOptions) {
+ this.menuButton.onCreate(event, options);
+ this.appendChild(this.menuButton);
+
+ // 定义按钮点击事件,打开人工智能聊天窗口
+ this.addEventListener("click", () => {
+ if (this.menuButtonOptions.enable) {
+ // 判断聊天窗口是否已打开
+ const display = (event.editor as InnerEditor).uaiEditor.arxivContainer.style.display;
+ if (display != "none") {
+ // 如果窗口已打开,则关闭窗口
+ (event.editor as InnerEditor).uaiEditor.arxivContainer.style.display = "none";
+ } else {
+ // 如果窗口没打开,则先关闭其他窗口,再打开聊天窗口
+ (event.editor as InnerEditor).uaiEditor.toggleContainers.forEach(toggle => {
+ toggle.style.display = "none";
+ });
+ (event.editor as InnerEditor).uaiEditor.arxivContainer.style.display = "block";
+ }
+ }
+ })
+ }
+
+ /**
+ * 定义Transaction监听方法
+ * @param event
+ * @param options
+ */
+ onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) {
+ this.menuButton.onTransaction(event, options);
+ }
+
+ onEditableChange(editable: boolean) {
+ this.menuButtonOptions.enable = editable;
+ this.menuButton.onEditableChange(editable);
+ }
+}
+
diff --git a/src/core/UAIEditor.ts b/src/core/UAIEditor.ts
index c8dbfe4..fb60d5e 100644
--- a/src/core/UAIEditor.ts
+++ b/src/core/UAIEditor.ts
@@ -16,6 +16,7 @@ import { Resource } from "i18next";
import "../components"
import { Header } from "../components/Header.ts";
+import { ArxivContainer } from "../components/containers/ArxivContainer.ts";
import { ChatContainer } from "../components/containers/ChatContainer.ts";
import { ImageContainer } from "../components/containers/ImageContainer.ts";
import { TocContainer } from "../components/containers/TocContainer.ts";
@@ -213,6 +214,7 @@ export class UAIEditor {
header!: Header;
chatContainer!: ChatContainer;
imageContainer!: ImageContainer;
+ arxivContainer!: ArxivContainer;
tocContainer!: TocContainer;
editor!: Editor;
footer!: Footer;
@@ -358,6 +360,11 @@ export class UAIEditor {
this.center.appendChild(this.tocContainer);
this.toggleContainers.push(this.tocContainer);
+ this.arxivContainer = new ArxivContainer();
+ this.arxivContainer.style.display = "none";
+ this.center.appendChild(this.arxivContainer);
+ this.toggleContainers.push(this.arxivContainer);
+
this.chatContainer = new ChatContainer();
this.chatContainer.style.display = "none";
this.center.appendChild(this.chatContainer);
@@ -430,6 +437,7 @@ export class UAIEditor {
}
this.header.onCreate(event, this.options);
this.tocContainer.onCreate(event, this.options);
+ this.arxivContainer.onCreate(event, this.options);
this.chatContainer.onCreate(event, this.options);
this.imageContainer.onCreate(event, this.options);
this.editor.onCreate(event, this.options);
@@ -442,6 +450,7 @@ export class UAIEditor {
protected onTransaction(event: EditorEvents['transaction']) {
this.header.onTransaction(event, this.options);
this.tocContainer.onTransaction(event, this.options);
+ this.arxivContainer.onTransaction(event, this.options);
this.chatContainer.onTransaction(event, this.options);
this.imageContainer.onTransaction(event, this.options);
this.editor.onTransaction(event, this.options);
diff --git a/src/extensions/File.ts b/src/extensions/File.ts
index 360bc6b..277290a 100644
--- a/src/extensions/File.ts
+++ b/src/extensions/File.ts
@@ -138,7 +138,7 @@ export default Node.create({
const body = document.createElement('div');
body.classList.add("uai-file-preview-modal-body");
const iframe = document.createElement('iframe');
- iframe.src = URL.createObjectURL(base64ToBlob(attrs.url, attrs.previewType));
+ iframe.src = attrs.url.startsWith("data") ? URL.createObjectURL(base64ToBlob(attrs.url, attrs.previewType)) : attrs.url;
body.appendChild(iframe);
modal.modalBody.appendChild(body);
modal.show();
diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts
index eab0232..cec649d 100644
--- a/src/i18n/zh.ts
+++ b/src/i18n/zh.ts
@@ -271,6 +271,10 @@ export const zh = {
"edit": "编辑流程图",
"loading": "加载中..."
},
+ "arxiv": {
+ "text": "论文",
+ "title": "论文搜索",
+ },
"mermaid": {
"text": "Mermaid",
"edit": "编辑 Mermaid",
diff --git a/src/styles/uaieditor.less b/src/styles/uaieditor.less
index e4c776f..acd8846 100644
--- a/src/styles/uaieditor.less
+++ b/src/styles/uaieditor.less
@@ -1054,7 +1054,7 @@
border-bottom: solid 1px var(--uai-border-color);
.uai-model-select {
- height: 24px;
+ height: 32px;
font-size: large;
}