添加论文搜索功能

This commit is contained in:
wux_labs
2025-03-11 00:59:43 +08:00
parent 033c0ffdbf
commit 9c42eb590a
13 changed files with 567 additions and 6 deletions
+1
View File
@@ -0,0 +1 @@
<svg fill="#ffffff" viewBox="-1.92 -1.92 27.84 27.84" role="img" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff" stroke-width="0.00024000000000000003" transform="rotate(0)"><g id="SVGRepo_bgCarrier" stroke-width="0" transform="translate(0,0), scale(1)"><rect x="-1.92" y="-1.92" width="27.84" height="27.84" rx="6.96" fill="#000000" strokewidth="0"></rect></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#030303" stroke-width="0.288"><title>Zhihu icon</title><path d="M5.721 0C2.251 0 0 2.25 0 5.719V18.28C0 21.751 2.252 24 5.721 24h12.56C21.751 24 24 21.75 24 18.281V5.72C24 2.249 21.75 0 18.281 0zm1.964 4.078c-.271.73-.5 1.434-.68 2.11h4.587c.545-.006.445 1.168.445 1.171H9.384a58.104 58.104 0 01-.112 3.797h2.712c.388.023.393 1.251.393 1.266H9.183a9.223 9.223 0 01-.408 2.102l.757-.604c.452.456 1.512 1.712 1.906 2.177.473.681.063 2.081.063 2.081l-2.794-3.382c-.653 2.518-1.845 3.607-1.845 3.607-.523.468-1.58.82-2.64.516 2.218-1.73 3.44-3.917 3.667-6.497H4.491c0-.015.197-1.243.806-1.266h2.71c.024-.32.086-3.254.086-3.797H6.598c-.136.406-.158.447-.268.753-.594 1.095-1.603 1.122-1.907 1.155.906-1.821 1.416-3.6 1.591-4.064.425-1.124 1.671-1.125 1.671-1.125zM13.078 6h6.377v11.33h-2.573l-2.184 1.373-.401-1.373h-1.219zm1.313 1.219v8.86h.623l.263.937 1.455-.938h1.456v-8.86z"></path></g><g id="SVGRepo_iconCarrier"><title>Zhihu icon</title><path d="M5.721 0C2.251 0 0 2.25 0 5.719V18.28C0 21.751 2.252 24 5.721 24h12.56C21.751 24 24 21.75 24 18.281V5.72C24 2.249 21.75 0 18.281 0zm1.964 4.078c-.271.73-.5 1.434-.68 2.11h4.587c.545-.006.445 1.168.445 1.171H9.384a58.104 58.104 0 01-.112 3.797h2.712c.388.023.393 1.251.393 1.266H9.183a9.223 9.223 0 01-.408 2.102l.757-.604c.452.456 1.512 1.712 1.906 2.177.473.681.063 2.081.063 2.081l-2.794-3.382c-.653 2.518-1.845 3.607-1.845 3.607-.523.468-1.58.82-2.64.516 2.218-1.73 3.44-3.917 3.667-6.497H4.491c0-.015.197-1.243.806-1.266h2.71c.024-.32.086-3.254.086-3.797H6.598c-.136.406-.158.447-.268.753-.594 1.095-1.603 1.122-1.907 1.155.906-1.821 1.416-3.6 1.591-4.064.425-1.124 1.671-1.125 1.671-1.125zM13.078 6h6.377v11.33h-2.573l-2.184 1.373-.401-1.373h-1.219zm1.313 1.219v8.86h.623l.263.937 1.455-.938h1.456v-8.86z"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+441
View File
@@ -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 = `<img src="${titleIcon}" height="24" width="24"/>&nbsp; ${t('tools.arxiv.title')}`
this.container.appendChild(this.arxivTitle);
// 初始化关闭按钮
const dialogClose = document.createElement("div");
dialogClose.classList.add("uai-pannel-close");
dialogClose.innerHTML = `<img src="${closeIcon}" width="24"/>`;
// 点击关闭按钮时关闭容器
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 = `<img src="${sendIcon}" height="24" />`;
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') || '',
}))
};
}
}
+1 -1
View File
@@ -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 = `<img src="${titleIcon}" width="18" />&nbsp; ${t('ai.chat.title')}`
this.chatTitle.innerHTML = `<img src="${titleIcon}" height="24" width="24"/>&nbsp; ${t('ai.chat.title')}`
this.container.appendChild(this.chatTitle);
// 初始化关闭按钮
+1 -1
View File
@@ -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 = `<img src="${titleIcon}" width="18" />&nbsp; ${t('ai.image.title')}`;
this.imageTitle.innerHTML = `<img src="${titleIcon}" height="24" width="24"/>&nbsp; ${t('ai.image.title')}`;
this.container.appendChild(this.imageTitle);
// 初始化关闭容器按钮
+1 -1
View File
@@ -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 = `<img src="${titleIcon}" width="18" />&nbsp; ${t('toc.title')}`;
this.tocTitle.innerHTML = `<img src="${titleIcon}" height="24" width="24"/>&nbsp; ${t('toc.title')}`;
this.container.appendChild(this.tocTitle);
// 初始化关闭容器按钮
+4
View File
@@ -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);
+11 -1
View File
@@ -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);
}
/**
+10
View File
@@ -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);
}
/**
@@ -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);
}
}
+9
View File
@@ -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);
+1 -1
View File
@@ -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();
+4
View File
@@ -271,6 +271,10 @@ export const zh = {
"edit": "编辑流程图",
"loading": "加载中..."
},
"arxiv": {
"text": "论文",
"title": "论文搜索",
},
"mermaid": {
"text": "Mermaid",
"edit": "编辑 Mermaid",
+1 -1
View File
@@ -1054,7 +1054,7 @@
border-bottom: solid 1px var(--uai-border-color);
.uai-model-select {
height: 24px;
height: 32px;
font-size: large;
}