添加快捷菜单功能
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2024-present AI-Labs
|
||||
|
||||
// @ ts-nocheck
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
import Suggestion, { SuggestionOptions, SuggestionProps } from '@tiptap/suggestion';
|
||||
import tippy, { Instance } from 'tippy.js';
|
||||
import { InnerEditor } from '../core/UAIEditor.ts';
|
||||
import { AICommand } from '../ai/config/AIConfig.ts';
|
||||
import OpenAI from "openai";
|
||||
import { markdownToHtml } from '../utils/MarkdownUtil.ts';
|
||||
import { uuid } from '../utils/UUID.ts';
|
||||
|
||||
export type QuickCommandOptions = {
|
||||
HTMLAttributes?: Record<string, any>;
|
||||
suggestion: Omit<SuggestionOptions, 'editor'>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义快捷命令
|
||||
*/
|
||||
export default Extension.create<QuickCommandOptions>({
|
||||
name: 'quickCommand',
|
||||
addOptions() {
|
||||
return {
|
||||
suggestion: {
|
||||
char: '/',
|
||||
command: ({ editor, range, props }) => {
|
||||
editor.chain().focus().deleteRange(range).run();
|
||||
const from = editor.state.selection.to;
|
||||
const type = props.type;
|
||||
const command = props.command as AICommand;
|
||||
const options = (editor as InnerEditor).uaiEditor.options;
|
||||
const actionKey = "uai_waiting_action";
|
||||
|
||||
const selectedText = editor.state.selection.$head.parent.textContent;
|
||||
if (type === "chat") {
|
||||
// 模型对话命令
|
||||
var models = Object.keys(options.ai?.chat?.models ?? {});
|
||||
if (models.length > 0) {
|
||||
var model = command.model ?? "auto";
|
||||
if (model === "auto") {
|
||||
model = models[0];
|
||||
}
|
||||
const modelConfig = options.ai!.chat!.models![model];
|
||||
const client = new OpenAI({
|
||||
baseURL: modelConfig.baseUrl,
|
||||
apiKey: modelConfig.apiKey,
|
||||
dangerouslyAllowBrowser: true,
|
||||
});
|
||||
|
||||
client.chat.completions.create({
|
||||
model: modelConfig.model ?? "o1",
|
||||
stream: true,
|
||||
max_tokens: modelConfig.max_tokens,
|
||||
temperature: modelConfig.temperature,
|
||||
top_p: modelConfig.top_p,
|
||||
frequency_penalty: modelConfig.frequency_penalty,
|
||||
messages: [
|
||||
{ "role": "system", "content": command.prompt ?? "你是一个很有帮助的人工智能助手。" },
|
||||
{ "role": "user", "content": selectedText }
|
||||
],
|
||||
}).then(async response => {
|
||||
for await (var chunk of response) {
|
||||
var content = chunk.choices[0]?.delta?.content || '';
|
||||
editor.view.dispatch(editor.state.tr.insertText(content));
|
||||
}
|
||||
const end = editor.state.selection.to;
|
||||
const insertedText = editor.state.doc.textBetween(from, end);
|
||||
editor.view.dispatch(editor.state.tr.replaceWith(from, end, (editor as InnerEditor).parseHtml(markdownToHtml(insertedText))).scrollIntoView());
|
||||
});
|
||||
}
|
||||
} else if (type === "text2image") {
|
||||
// 文生图命令
|
||||
var models = Object.keys(options.ai?.image?.models?.text2image ?? {});
|
||||
const id = uuid()
|
||||
if (models.length > 0) {
|
||||
var model = command.model ?? "auto";
|
||||
if (model === "auto") {
|
||||
model = models[0];
|
||||
}
|
||||
const modelConfig = options.ai!.image!.models!.text2image![model];
|
||||
const client = new OpenAI({
|
||||
baseURL: modelConfig.baseUrl,
|
||||
apiKey: modelConfig.apiKey,
|
||||
dangerouslyAllowBrowser: true,
|
||||
});
|
||||
editor.view.dispatch(editor.state.tr.setMeta(actionKey, {
|
||||
type: "add",
|
||||
id,
|
||||
pos: editor.state.tr.selection.from,
|
||||
}));
|
||||
client.images.generate({
|
||||
"model": modelConfig.model ?? "dall-e-3",
|
||||
"prompt": `${command.prompt ?? ""}${selectedText}`,
|
||||
"size": "1024x1024",
|
||||
}).then(response => {
|
||||
editor.view.dispatch(editor.state.tr.setMeta(actionKey, { type: "remove", id }));
|
||||
const previewType = "image";
|
||||
const type = "image";
|
||||
|
||||
editor.commands.insertContentAt(editor.state.tr.selection.from, {
|
||||
type: type,
|
||||
attrs: {
|
||||
['src']: response.data[0].url,
|
||||
type,
|
||||
previewType,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
render: () => {
|
||||
let container: HTMLElement;
|
||||
let popup: Instance;
|
||||
|
||||
return {
|
||||
onStart: (props: SuggestionProps) => {
|
||||
container = document.createElement("div");
|
||||
container.classList.add("uai-popup-action-list");
|
||||
(props.editor as InnerEditor).uaiEditor.options.ai?.chat?.commands?.forEach(command => {
|
||||
const item = document.createElement("div");
|
||||
item.classList.add("uai-popup-action-item");
|
||||
item.innerHTML = `${command.icon} ${command.name}`;
|
||||
item.addEventListener('click', () => {
|
||||
props.command({
|
||||
type: "chat",
|
||||
command: command
|
||||
})
|
||||
});
|
||||
container.appendChild(item);
|
||||
})
|
||||
|
||||
container.appendChild(document.createElement("hr"));
|
||||
|
||||
(props.editor as InnerEditor).uaiEditor.options.ai?.image?.commands?.forEach(command => {
|
||||
const item = document.createElement("div");
|
||||
item.classList.add("uai-popup-action-item");
|
||||
item.innerHTML = `${command.icon} ${command.name}`;
|
||||
item.addEventListener('click', () => {
|
||||
props.command({
|
||||
type: "text2image",
|
||||
command: command
|
||||
})
|
||||
});
|
||||
container.appendChild(item);
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
popup = tippy('body', {
|
||||
appendTo: props.editor.options.element,
|
||||
getReferenceClientRect: props.clientRect,
|
||||
content: container,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
trigger: 'manual',
|
||||
placement: 'right',
|
||||
arrow: false,
|
||||
})[0]
|
||||
},
|
||||
onUpdate(props) {
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
popup.setProps({
|
||||
getReferenceClientRect: props.clientRect as any,
|
||||
})
|
||||
},
|
||||
onKeyDown(props) {
|
||||
if (props.event.key === 'Escape') {
|
||||
popup.hide();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
onExit() {
|
||||
popup.hide();
|
||||
container.remove();
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
Suggestion({
|
||||
editor: this.editor,
|
||||
...this.options.suggestion,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user