no message
This commit is contained in:
parent
9febf0777e
commit
093ebcb8b5
@ -1,15 +1,23 @@
|
||||
<template>
|
||||
<div>
|
||||
<ThemeToggle />
|
||||
<TiptapEditor />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TiptapEditor, ThemeToggle, useTiptapContext } from "@tiptap/component";
|
||||
import { TiptapEditor, DEMO_CONTENT, useTiptapStore } from "@tiptap/component";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
useTiptapStore({
|
||||
content: DEMO_CONTENT,
|
||||
minimal: false,
|
||||
hideToolbar: false,
|
||||
hideMenubar: false,
|
||||
disabled: false,
|
||||
theme: "light",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// useTiptapContext();
|
||||
// useTiptapStore();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -1,241 +0,0 @@
|
||||
import { computed } from "vue";
|
||||
import {
|
||||
Bold,
|
||||
BulletList,
|
||||
Italic,
|
||||
BaseKit,
|
||||
Underline,
|
||||
Strike,
|
||||
LineHeight,
|
||||
Image,
|
||||
History,
|
||||
Heading,
|
||||
CodeBlock,
|
||||
FontSize,
|
||||
Highlight,
|
||||
Table,
|
||||
Clear,
|
||||
Blockquote,
|
||||
Link,
|
||||
Color,
|
||||
Video,
|
||||
OrderedList,
|
||||
HorizontalRule,
|
||||
Fullscreen,
|
||||
TaskList,
|
||||
MoreMark,
|
||||
FormatPainter,
|
||||
SlashCommand,
|
||||
Indent,
|
||||
ImportWord,
|
||||
Columns,
|
||||
TextAlign,
|
||||
ImageUpload,
|
||||
VideoUpload,
|
||||
FontFamily,
|
||||
FindAndReplace,
|
||||
Code,
|
||||
AI,
|
||||
Preview,
|
||||
Printer,
|
||||
Iframe,
|
||||
SpecialCharacter,
|
||||
SourceCode,
|
||||
} from "echo-editor";
|
||||
import { ExportWord } from "./ExportWord";
|
||||
import OpenAI from "openai";
|
||||
|
||||
export function useEditorConfig(minimal: boolean) {
|
||||
const extensions = computed(() =>
|
||||
minimal ? minimalExtensions : fullExtensions
|
||||
);
|
||||
|
||||
const minimalExtensions = [
|
||||
BaseKit.configure({
|
||||
characterCount: {
|
||||
limit: 50000,
|
||||
},
|
||||
}),
|
||||
Heading,
|
||||
Bold.configure({ spacer: true }),
|
||||
Italic,
|
||||
Underline,
|
||||
HorizontalRule,
|
||||
TextAlign.configure({
|
||||
types: ["heading", "paragraph", "image"],
|
||||
spacer: true,
|
||||
}),
|
||||
Image,
|
||||
Blockquote.configure({ spacer: true }),
|
||||
Code,
|
||||
Link,
|
||||
Color,
|
||||
TaskList.configure({ spacer: true }),
|
||||
OrderedList,
|
||||
BulletList,
|
||||
];
|
||||
const fullExtensions = [
|
||||
BaseKit.configure({
|
||||
placeholder: {
|
||||
showOnlyCurrent: true,
|
||||
},
|
||||
characterCount: {
|
||||
limit: 50000,
|
||||
},
|
||||
}),
|
||||
History,
|
||||
Columns,
|
||||
FormatPainter,
|
||||
Clear,
|
||||
Heading.configure({ spacer: true }),
|
||||
FontSize,
|
||||
FontFamily,
|
||||
Bold,
|
||||
Italic,
|
||||
Underline,
|
||||
Strike,
|
||||
MoreMark,
|
||||
Color.configure({ spacer: true }),
|
||||
Highlight,
|
||||
BulletList,
|
||||
OrderedList,
|
||||
TextAlign.configure({
|
||||
types: ["heading", "paragraph", "image"],
|
||||
spacer: true,
|
||||
}),
|
||||
Indent,
|
||||
LineHeight,
|
||||
TaskList.configure({
|
||||
spacer: true,
|
||||
taskItem: {
|
||||
nested: true,
|
||||
},
|
||||
}),
|
||||
Link,
|
||||
Image,
|
||||
ImageUpload.configure({
|
||||
upload: (files: File) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(URL.createObjectURL(files));
|
||||
}, 3000);
|
||||
});
|
||||
},
|
||||
}),
|
||||
Video,
|
||||
VideoUpload.configure({
|
||||
upload: handleFileUpload,
|
||||
}),
|
||||
Blockquote,
|
||||
SlashCommand,
|
||||
HorizontalRule,
|
||||
CodeBlock,
|
||||
Table.configure({ spacer: true }),
|
||||
Code,
|
||||
ExportWord,
|
||||
AI.configure({
|
||||
completions: AICompletions,
|
||||
shortcuts: [
|
||||
// 这里可以传入额外的自定义shortcuts
|
||||
{
|
||||
label: "Custom Actions",
|
||||
children: [
|
||||
{
|
||||
label: "This is Custom Action",
|
||||
prompt:
|
||||
"Rewrite this content with no spelling mistakes, proper grammar, and with more descriptive language, using best writing practices without losing the original meaning.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
ImportWord.configure({
|
||||
upload: handleFileUpload,
|
||||
}),
|
||||
SpecialCharacter,
|
||||
Fullscreen.configure({ spacer: true }),
|
||||
SourceCode,
|
||||
Preview,
|
||||
FindAndReplace.configure({ spacer: true }),
|
||||
Printer,
|
||||
Iframe,
|
||||
];
|
||||
async function handleFileUpload(files: File[]) {
|
||||
const f = files.map((file) => ({
|
||||
src: URL.createObjectURL(file),
|
||||
alt: file.name,
|
||||
}));
|
||||
return Promise.resolve(f);
|
||||
}
|
||||
|
||||
async function AICompletions(
|
||||
history: Array<{ role: string; content: string }> = [],
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
// groq.com For free llm api recommend deepseek r1 70b
|
||||
// SECURITY WARNING: API keys should never be exposed in the frontend
|
||||
// This is just for demo purposes
|
||||
// In production, implement this through your backend API
|
||||
const apiKey = "import.meta.env.VITE_OPENAI_API_KEY";
|
||||
const baseURL = "import.meta.env.VITE_OPENAI_BASE_URL";
|
||||
const model = "import.meta.env.VITE_OPENAI_MODEL";
|
||||
|
||||
if (!apiKey || !baseURL || !model) {
|
||||
throw new Error(
|
||||
"OpenAI configuration is missing. Please check your environment variables."
|
||||
);
|
||||
}
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: apiKey,
|
||||
dangerouslyAllowBrowser: true,
|
||||
baseURL: baseURL,
|
||||
});
|
||||
|
||||
const systemMsg = `You are a professional writing assistant. Please respond based on the user's context:
|
||||
|
||||
1. Maintain a professional, accurate, and objective tone
|
||||
2. Ensure responses are clear, coherent, and well-structured
|
||||
3. Responses must be in HTML format, preserving all HTML tags, links, and styles
|
||||
4. Support the following writing enhancements:
|
||||
- Grammar and spelling corrections
|
||||
- Improved sentence structure and expression
|
||||
- Optimized article formatting and layout
|
||||
- Maintain the core meaning of the original text
|
||||
5. If context includes code, maintain code formatting and provide optimization suggestions
|
||||
6. Add appropriate HTML elements like headings, lists, quotes etc. to enhance readability as needed
|
||||
|
||||
Please respond only based on the provided context, do not add irrelevant information.`;
|
||||
|
||||
const systemPrompt = [{ role: "system", content: systemMsg }];
|
||||
const finalMessages = [...systemPrompt];
|
||||
|
||||
if (history.length > 0) {
|
||||
finalMessages.push(...history);
|
||||
}
|
||||
|
||||
try {
|
||||
const stream = await openai.chat.completions.create(
|
||||
{
|
||||
model,
|
||||
messages: finalMessages,
|
||||
temperature: 0.7,
|
||||
stream: true,
|
||||
reasoning_format: "parsed", // groq deepseek r1 need this
|
||||
} as any,
|
||||
{ signal }
|
||||
);
|
||||
|
||||
return stream;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error in AI Completions:", error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
extensions,
|
||||
};
|
||||
}
|
||||
232
web3/packages/tiptap/src/context/consts.ts
Normal file
232
web3/packages/tiptap/src/context/consts.ts
Normal file
@ -0,0 +1,232 @@
|
||||
import { ExportWord } from "../components/ExportWord";
|
||||
import OpenAI from "openai";
|
||||
|
||||
import {
|
||||
Bold,
|
||||
BulletList,
|
||||
Italic,
|
||||
BaseKit,
|
||||
Underline,
|
||||
Strike,
|
||||
LineHeight,
|
||||
Image,
|
||||
History,
|
||||
Heading,
|
||||
CodeBlock,
|
||||
FontSize,
|
||||
Highlight,
|
||||
Table,
|
||||
Clear,
|
||||
Blockquote,
|
||||
Link,
|
||||
Color,
|
||||
Video,
|
||||
OrderedList,
|
||||
HorizontalRule,
|
||||
Fullscreen,
|
||||
TaskList,
|
||||
MoreMark,
|
||||
FormatPainter,
|
||||
SlashCommand,
|
||||
Indent,
|
||||
ImportWord,
|
||||
Columns,
|
||||
TextAlign,
|
||||
ImageUpload,
|
||||
VideoUpload,
|
||||
FontFamily,
|
||||
FindAndReplace,
|
||||
Code,
|
||||
AI,
|
||||
Preview,
|
||||
Printer,
|
||||
Iframe,
|
||||
SpecialCharacter,
|
||||
SourceCode,
|
||||
} from "echo-editor";
|
||||
|
||||
async function handleFileUpload(files: File[]) {
|
||||
const f = files.map((file) => ({
|
||||
src: URL.createObjectURL(file),
|
||||
alt: file.name,
|
||||
}));
|
||||
return Promise.resolve(f);
|
||||
}
|
||||
|
||||
async function AICompletions(
|
||||
history: Array<{ role: string; content: string }> = [],
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
// groq.com For free llm api recommend deepseek r1 70b
|
||||
// SECURITY WARNING: API keys should never be exposed in the frontend
|
||||
// This is just for demo purposes
|
||||
// In production, implement this through your backend API
|
||||
const apiKey = "import.meta.env.VITE_OPENAI_API_KEY";
|
||||
const baseURL = "import.meta.env.VITE_OPENAI_BASE_URL";
|
||||
const model = "import.meta.env.VITE_OPENAI_MODEL";
|
||||
|
||||
if (!apiKey || !baseURL || !model) {
|
||||
throw new Error(
|
||||
"OpenAI configuration is missing. Please check your environment variables."
|
||||
);
|
||||
}
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: apiKey,
|
||||
dangerouslyAllowBrowser: true,
|
||||
baseURL: baseURL,
|
||||
});
|
||||
|
||||
const systemMsg = `You are a professional writing assistant. Please respond based on the user's context:
|
||||
|
||||
1. Maintain a professional, accurate, and objective tone
|
||||
2. Ensure responses are clear, coherent, and well-structured
|
||||
3. Responses must be in HTML format, preserving all HTML tags, links, and styles
|
||||
4. Support the following writing enhancements:
|
||||
- Grammar and spelling corrections
|
||||
- Improved sentence structure and expression
|
||||
- Optimized article formatting and layout
|
||||
- Maintain the core meaning of the original text
|
||||
5. If context includes code, maintain code formatting and provide optimization suggestions
|
||||
6. Add appropriate HTML elements like headings, lists, quotes etc. to enhance readability as needed
|
||||
|
||||
Please respond only based on the provided context, do not add irrelevant information.`;
|
||||
|
||||
const systemPrompt = [{ role: "system", content: systemMsg }];
|
||||
const finalMessages = [...systemPrompt];
|
||||
|
||||
if (history.length > 0) {
|
||||
finalMessages.push(...history);
|
||||
}
|
||||
|
||||
try {
|
||||
const stream = await openai.chat.completions.create(
|
||||
{
|
||||
model,
|
||||
messages: finalMessages,
|
||||
temperature: 0.7,
|
||||
stream: true,
|
||||
reasoning_format: "parsed", // groq deepseek r1 need this
|
||||
} as any,
|
||||
{ signal }
|
||||
);
|
||||
|
||||
return stream;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error in AI Completions:", error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export const minimalExtensions = [
|
||||
BaseKit.configure({
|
||||
characterCount: {
|
||||
limit: 50000,
|
||||
},
|
||||
}),
|
||||
Heading,
|
||||
Bold.configure({ spacer: true }),
|
||||
Italic,
|
||||
Underline,
|
||||
HorizontalRule,
|
||||
TextAlign.configure({
|
||||
types: ["heading", "paragraph", "image"],
|
||||
spacer: true,
|
||||
}),
|
||||
Image,
|
||||
Blockquote.configure({ spacer: true }),
|
||||
Code,
|
||||
Link,
|
||||
Color,
|
||||
TaskList.configure({ spacer: true }),
|
||||
OrderedList,
|
||||
BulletList,
|
||||
];
|
||||
export const fullExtensions = [
|
||||
BaseKit.configure({
|
||||
placeholder: {
|
||||
showOnlyCurrent: true,
|
||||
},
|
||||
characterCount: {
|
||||
limit: 50000,
|
||||
},
|
||||
}),
|
||||
History,
|
||||
Columns,
|
||||
FormatPainter,
|
||||
Clear,
|
||||
Heading.configure({ spacer: true }),
|
||||
FontSize,
|
||||
FontFamily,
|
||||
Bold,
|
||||
Italic,
|
||||
Underline,
|
||||
Strike,
|
||||
MoreMark,
|
||||
Color.configure({ spacer: true }),
|
||||
Highlight,
|
||||
BulletList,
|
||||
OrderedList,
|
||||
TextAlign.configure({
|
||||
types: ["heading", "paragraph", "image"],
|
||||
spacer: true,
|
||||
}),
|
||||
Indent,
|
||||
LineHeight,
|
||||
TaskList.configure({
|
||||
spacer: true,
|
||||
taskItem: {
|
||||
nested: true,
|
||||
},
|
||||
}),
|
||||
Link,
|
||||
Image,
|
||||
ImageUpload.configure({
|
||||
upload: (files: File) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(URL.createObjectURL(files));
|
||||
}, 3000);
|
||||
});
|
||||
},
|
||||
}),
|
||||
Video,
|
||||
VideoUpload.configure({
|
||||
upload: handleFileUpload,
|
||||
}),
|
||||
Blockquote,
|
||||
SlashCommand,
|
||||
HorizontalRule,
|
||||
CodeBlock,
|
||||
Table.configure({ spacer: true }),
|
||||
Code,
|
||||
ExportWord,
|
||||
AI.configure({
|
||||
completions: AICompletions,
|
||||
shortcuts: [
|
||||
// 这里可以传入额外的自定义shortcuts
|
||||
{
|
||||
label: "Custom Actions",
|
||||
children: [
|
||||
{
|
||||
label: "This is Custom Action",
|
||||
prompt:
|
||||
"Rewrite this content with no spelling mistakes, proper grammar, and with more descriptive language, using best writing practices without losing the original meaning.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
// ImportWord.configure({
|
||||
// upload: handleFileUpload,
|
||||
// }),
|
||||
SpecialCharacter,
|
||||
Fullscreen.configure({ spacer: true }),
|
||||
SourceCode,
|
||||
Preview,
|
||||
FindAndReplace.configure({ spacer: true }),
|
||||
Printer,
|
||||
Iframe,
|
||||
];
|
||||
@ -1,4 +1,13 @@
|
||||
import { provide, inject, reactive, type InjectionKey } from "vue";
|
||||
import {
|
||||
provide,
|
||||
inject,
|
||||
reactive,
|
||||
type InjectionKey,
|
||||
computed,
|
||||
type ComputedRef,
|
||||
} from "vue";
|
||||
|
||||
import { minimalExtensions, fullExtensions } from "./consts";
|
||||
|
||||
const TiptapStoreKey: InjectionKey<TiptapStore> = Symbol("TiptapStore");
|
||||
|
||||
@ -13,6 +22,7 @@ export interface TiptapState {
|
||||
|
||||
export type TiptapStore = {
|
||||
state: TiptapState;
|
||||
extensions: ComputedRef<any[]>;
|
||||
};
|
||||
|
||||
export function useTiptapStore(initialState?: Partial<TiptapState>) {
|
||||
@ -26,8 +36,13 @@ export function useTiptapStore(initialState?: Partial<TiptapState>) {
|
||||
...initialState,
|
||||
});
|
||||
|
||||
const extensions = computed(() => {
|
||||
return state.minimal ? minimalExtensions : fullExtensions;
|
||||
});
|
||||
|
||||
const store: TiptapStore = {
|
||||
state: state,
|
||||
extensions,
|
||||
};
|
||||
|
||||
provide(TiptapStoreKey, store);
|
||||
|
||||
@ -2,6 +2,7 @@ import TiptapEditor from "./views/index.vue";
|
||||
import { useTiptapStore, useTiptapContext } from "./context/useTiptapStore";
|
||||
import { ThemeToggle } from "echo-editor";
|
||||
import EditorActions from "./components/EditorActions.vue";
|
||||
import { DEMO_CONTENT } from "./views/initContent";
|
||||
|
||||
export {
|
||||
TiptapEditor,
|
||||
@ -9,4 +10,5 @@ export {
|
||||
useTiptapStore,
|
||||
useTiptapContext,
|
||||
EditorActions,
|
||||
DEMO_CONTENT,
|
||||
};
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<EditorActions />
|
||||
<echo-editor
|
||||
:model-value="state.content"
|
||||
:extensions="editorExtensions"
|
||||
:extensions="extensions"
|
||||
:hideToolbar="state.hideToolbar"
|
||||
:hideMenubar="state.hideMenubar || state.minimal"
|
||||
:key="state.minimal ? 'minimal' : 'full'"
|
||||
@ -18,28 +17,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { EchoEditor } from "echo-editor";
|
||||
import { DEMO_CONTENT } from "./initContent";
|
||||
import EditorActions from "../components/EditorActions.vue";
|
||||
import { useEditorConfig } from "../components/useEditorConfig";
|
||||
import { useTiptapStore, type TiptapState } from "../context/useTiptapStore";
|
||||
import { type TiptapState, useTiptapContext } from "../context/useTiptapStore";
|
||||
|
||||
import "./style.css";
|
||||
import "echo-editor/style.css";
|
||||
|
||||
const props = defineProps<{
|
||||
initialState?: Partial<TiptapState>;
|
||||
}>();
|
||||
|
||||
const { state } = useTiptapStore({
|
||||
...props.initialState,
|
||||
content: props.initialState?.content || DEMO_CONTENT,
|
||||
});
|
||||
const { state, extensions } = useTiptapContext();
|
||||
|
||||
console.log("Tiptap state:", state);
|
||||
|
||||
const editorExtensions = computed(() => {
|
||||
return useEditorConfig(state.minimal).extensions.value;
|
||||
});
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user