no message
This commit is contained in:
parent
c1138766c8
commit
63442cb50e
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": true,
|
"source.fixAll": "explicit",
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mind-map/component": "workspace:*",
|
"@mind-map/component": "workspace:*",
|
||||||
|
"@tiptap/component": "workspace:*",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.27",
|
||||||
|
|||||||
@ -1,19 +1,29 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
|
|
||||||
// 路由配置
|
// 路由配置
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: "/",
|
||||||
name: 'Home',
|
name: "Home",
|
||||||
component: () => import('../views/Home.vue')
|
component: () => import("../views/Home.vue"),
|
||||||
},
|
},
|
||||||
]
|
{
|
||||||
|
path: "/mind-map",
|
||||||
|
name: "mindMap",
|
||||||
|
component: () => import("../views/mindMap/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tiptap",
|
||||||
|
name: "tiptap",
|
||||||
|
component: () => import("../views/tiptap/index.vue"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// 创建路由实例
|
// 创建路由实例
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes
|
routes,
|
||||||
})
|
});
|
||||||
|
|
||||||
export default router
|
export default router;
|
||||||
|
|||||||
@ -1,43 +1,110 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-screen p-6 bg-gradient-to-br from-gray-50 to-gray-100">
|
<div class="home-container">
|
||||||
<MindMap @onDel="onDel" @onAddChild="onAddChild" @onEdit="onEdit" />
|
<div class="title">欢迎使用思维导图工具</div>
|
||||||
|
|
||||||
|
<div class="nav-buttons">
|
||||||
|
<button @click="goToMindMap" class="nav-btn mind-map-btn">
|
||||||
|
思维导图
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button @click="goToTiptap" class="nav-btn tiptap-btn">
|
||||||
|
富文本编辑器 (Tiptap)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from "vue";
|
import { useRouter } from "vue-router";
|
||||||
import { MindMap, useMindMapStore } from "@mind-map/component";
|
|
||||||
import type { MindMapNode } from "@mind-map/component";
|
|
||||||
|
|
||||||
onMounted(() => {
|
const router = useRouter();
|
||||||
console.log("Home view mounted");
|
|
||||||
});
|
|
||||||
|
|
||||||
const { removeNode, insertChildNode } = useMindMapStore();
|
// 跳转到思维导图页面
|
||||||
|
function goToMindMap() {
|
||||||
function onDel(node: MindMapNode) {
|
router.push({ name: "mindMap" });
|
||||||
removeNode({
|
|
||||||
beforeRemoveCallback: async () => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
nodeId: node.uid,
|
|
||||||
allowRemoveWithChildren: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAddChild(node: MindMapNode) {
|
// 跳转到 Tiptap 页面
|
||||||
insertChildNode({
|
function goToTiptap() {
|
||||||
parentNodeId: node.uid,
|
router.push({ name: "tiptap" });
|
||||||
beforeInsertCallback: async () => {
|
|
||||||
return {
|
|
||||||
uid: "node.uid",
|
|
||||||
text: "node.text",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEdit(node: MindMapNode) {
|
|
||||||
console.log("Node edited:", node);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-width: 200px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mind-map-btn {
|
||||||
|
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mind-map-btn:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 8px 25px rgba(255, 154, 158, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiptap-btn {
|
||||||
|
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiptap-btn:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 8px 25px rgba(168, 237, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn:active {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.nav-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn {
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
43
web3/apps/web/src/views/mindMap/index.vue
Normal file
43
web3/apps/web/src/views/mindMap/index.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-screen p-6 bg-gradient-to-br from-gray-50 to-gray-100">
|
||||||
|
<MindMap @onDel="onDel" @onAddChild="onAddChild" @onEdit="onEdit" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import { MindMap, useMindMapStore } from "@mind-map/component";
|
||||||
|
import type { MindMapNode } from "@mind-map/component";
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log("Home view mounted");
|
||||||
|
});
|
||||||
|
|
||||||
|
const { removeNode, insertChildNode } = useMindMapStore();
|
||||||
|
|
||||||
|
function onDel(node: MindMapNode) {
|
||||||
|
removeNode({
|
||||||
|
beforeRemoveCallback: async () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
nodeId: node.uid,
|
||||||
|
allowRemoveWithChildren: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAddChild(node: MindMapNode) {
|
||||||
|
insertChildNode({
|
||||||
|
parentNodeId: node.uid,
|
||||||
|
beforeInsertCallback: async () => {
|
||||||
|
return {
|
||||||
|
uid: "node.uid",
|
||||||
|
text: "node.text",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEdit(node: MindMapNode) {
|
||||||
|
console.log("Node edited:", node);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
9
web3/apps/web/src/views/tiptap/index.vue
Normal file
9
web3/apps/web/src/views/tiptap/index.vue
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<TiptapEditor />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { TiptapEditor } from "@tiptap/component";
|
||||||
|
</script>
|
||||||
@ -1,18 +1,16 @@
|
|||||||
// import { App } from 'vue';
|
// import { App } from 'vue';
|
||||||
// import pinia from './store';
|
// import pinia from './store';
|
||||||
|
|
||||||
import MindMap from './views/index.vue';
|
import MindMap from "./views/index.vue";
|
||||||
|
|
||||||
import type MindMapNode from "simple-mind-map/types/src/core/render/node/MindMapNode";
|
import type MindMapNode from "simple-mind-map/types/src/core/render/node/MindMapNode";
|
||||||
|
|
||||||
export { MindMap };
|
export { MindMap };
|
||||||
|
|
||||||
export * from './store';
|
export * from "./store";
|
||||||
|
|
||||||
export type { MindMapNode };
|
export type { MindMapNode };
|
||||||
|
|
||||||
// export function installMindMap(app: App) {
|
// export function installMindMap(app: App) {
|
||||||
// app.use(pinia);
|
// app.use(pinia);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
40
web3/packages/tiptap/package.json
Normal file
40
web3/packages/tiptap/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@tiptap/component",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc && vite build",
|
||||||
|
"build:no-check": "vite build",
|
||||||
|
"type-check": "vue-tsc",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tiptap/core": "^2.25.0",
|
||||||
|
"@types/file-saver": "^2.0.7",
|
||||||
|
"docx": "^9.5.1",
|
||||||
|
"echo-editor": "^0.4.8",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"openai": "^5.8.3",
|
||||||
|
"prosemirror-docx": "latest",
|
||||||
|
"vite-svg-loader": "latest",
|
||||||
|
"vue": "^3.3.4"
|
||||||
|
},
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"default": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"vite": "^4.4.5",
|
||||||
|
"vue-tsc": "^2.2.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ActionButton } from "echo-editor";
|
||||||
|
import type { Editor } from "echo-editor";
|
||||||
|
import { ButtonViewReturnComponentProps } from "echo-editor";
|
||||||
|
import iconUrl from "../../../icons/exportWord.svg";
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
disabled: false,
|
||||||
|
isActive: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
disabled?: boolean;
|
||||||
|
isActive?: ButtonViewReturnComponentProps["isActive"];
|
||||||
|
editor?: Editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleExport() {
|
||||||
|
props.editor?.commands.exportToWord();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<action-button
|
||||||
|
tooltip="ExportToWord"
|
||||||
|
:is-active="isActive"
|
||||||
|
:disabled="disabled"
|
||||||
|
:action="handleExport"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<iconUrl style="width: 16px; height: 16px" />
|
||||||
|
</template>
|
||||||
|
</action-button>
|
||||||
|
</template>
|
||||||
64
web3/packages/tiptap/src/components/ExportWord/index.ts
Normal file
64
web3/packages/tiptap/src/components/ExportWord/index.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { Extension } from "@tiptap/core";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import ActionButton from "./components/ActionButton.vue";
|
||||||
|
import { DocxSerializer, defaultNodes, defaultMarks } from "prosemirror-docx";
|
||||||
|
import { Packer } from "docx";
|
||||||
|
import type { GeneralOptions } from "echo-editor";
|
||||||
|
|
||||||
|
declare module "@tiptap/core" {
|
||||||
|
interface Commands<ReturnType> {
|
||||||
|
exportWord: {
|
||||||
|
exportToWord: () => ReturnType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface ExportWordOptions extends GeneralOptions<ExportWordOptions> {}
|
||||||
|
|
||||||
|
const nodeSerializer = {
|
||||||
|
...defaultNodes,
|
||||||
|
hardBreak: defaultNodes.hard_break,
|
||||||
|
codeBlock: defaultNodes.code_block,
|
||||||
|
orderedList: defaultNodes.ordered_list,
|
||||||
|
listItem: defaultNodes.list_item,
|
||||||
|
bulletList: defaultNodes.bullet_list,
|
||||||
|
horizontalRule: defaultNodes.horizontal_rule,
|
||||||
|
image(state: any, node: any) {
|
||||||
|
// No image
|
||||||
|
state.renderInline(node);
|
||||||
|
state.closeBlock(node);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const docxSerializer = new DocxSerializer(nodeSerializer, defaultMarks);
|
||||||
|
|
||||||
|
export const ExportWord = Extension.create<ExportWordOptions>({
|
||||||
|
name: "exportWord",
|
||||||
|
addOptions() {
|
||||||
|
return {
|
||||||
|
...this.parent?.(),
|
||||||
|
button: ({}) => ({
|
||||||
|
component: ActionButton,
|
||||||
|
componentProps: {},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
addCommands() {
|
||||||
|
return {
|
||||||
|
exportToWord:
|
||||||
|
() =>
|
||||||
|
({ editor }) => {
|
||||||
|
const opts: any = {
|
||||||
|
getImageBuffer: async (src: string) => {
|
||||||
|
const response = await fetch(src);
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
return new Uint8Array(arrayBuffer);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const wordDocument = docxSerializer.serialize(editor.state.doc, opts);
|
||||||
|
Packer.toBlob(wordDocument).then((blob) =>
|
||||||
|
saveAs(new Blob([blob]), "example.docx")
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
1
web3/packages/tiptap/src/icons/exportWord.svg
Normal file
1
web3/packages/tiptap/src/icons/exportWord.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1721031224139" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7445" width="200" height="200"><path d="M679.253333 402.363733l-60.484266 158.651734-60.347734-158.651734a30.037333 30.037333 0 0 0-30.446933-18.6368 29.764267 29.764267 0 0 0-30.446933 18.6368l-60.416 158.651734-60.416-158.651734a30.5152 30.5152 0 0 0-38.843734-17.271466 28.945067 28.945067 0 0 0-17.954133 37.546666l88.814933 233.2672c4.369067 11.4688 15.701333 19.114667 28.398934 19.114667a30.3104 30.3104 0 0 0 28.4672-19.114667l62.395733-163.908266 62.395733 163.84c4.437333 11.605333 15.701333 19.182933 28.398934 19.182933a30.3104 30.3104 0 0 0 28.4672-19.114667l88.746666-233.2672a28.945067 28.945067 0 0 0-17.885866-37.546666 30.446933 30.446933 0 0 0-38.912 17.271466zM898.730667 797.969067l-51.882667-29.218134c-28.672-16.1792-52.224-3.072-52.224 29.0816v0.273067H643.208533a29.832533 29.832533 0 0 0-30.3104 29.354667c0 16.1792 13.585067 29.218133 30.3104 29.218133h151.825067c1.092267 30.5152 24.029867 43.076267 52.224 27.648l51.063467-27.989333c29.013333-15.906133 29.149867-42.1888 0.4096-58.368z" fill="#333333" p-id="7446"></path><path d="M810.666667 913.134933l-0.477867 0.068267H201.796267c-19.8656 0-36.727467-11.6736-36.727467-25.6V269.243733h154.965333c51.268267 0 92.910933-39.389867 92.910934-87.8592V93.525333h397.243733c19.797333 0 36.522667 11.741867 36.522667 25.668267v620.066133h61.986133V119.261867c0-46.421333-44.168533-84.241067-98.5088-84.241067H328.362667l-225.28 194.56v658.090667c0 46.2848 44.2368 84.104533 98.7136 84.104533h608.392533c43.758933 0 80.554667-24.712533 93.320533-58.5728h-92.842666zM350.890667 94.890667v86.562133c0 16.110933-13.858133 29.2864-30.9248 29.2864H216.814933l134.144-115.848533z" fill="#333333" p-id="7447"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
3
web3/packages/tiptap/src/index.ts
Normal file
3
web3/packages/tiptap/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import TiptapEditor from "./views/index.vue";
|
||||||
|
|
||||||
|
export { TiptapEditor };
|
||||||
394
web3/packages/tiptap/src/views/index.vue
Normal file
394
web3/packages/tiptap/src/views/index.vue
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-screen bg-background">
|
||||||
|
<header
|
||||||
|
class="border-grid w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"
|
||||||
|
>
|
||||||
|
<div class="container sticky flex items-center h-14">
|
||||||
|
<div class="hidden mr-4 md:mr-1 md:flex">
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="flex items-center mr-4 md:mr-2 lg:mr-6 lg:space-x1 xl:space-x-2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
class="mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12 7v10" />
|
||||||
|
<path d="M8 9v6" opacity="0.7" />
|
||||||
|
<path d="M4 11v2" opacity="0.4" />
|
||||||
|
<path d="M16 9v6" opacity="0.7" />
|
||||||
|
<path d="M20 11v2" opacity="0.4" />
|
||||||
|
<rect
|
||||||
|
x="10"
|
||||||
|
y="5"
|
||||||
|
width="4"
|
||||||
|
height="14"
|
||||||
|
fill="currentColor"
|
||||||
|
opacity="0.1"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="font-bold"> Echo Editor </span>
|
||||||
|
</a>
|
||||||
|
<nav class="flex items-center gap-4 text-sm xl:gap-6"></nav>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between flex-1 space-x-2 md:justify-end"
|
||||||
|
>
|
||||||
|
<div class="flex-1 w-full md:w-auto md:flex-none"></div>
|
||||||
|
<nav class="flex items-center gap-0.5">
|
||||||
|
<a
|
||||||
|
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground w-8 h-8"
|
||||||
|
href="https://github.com/Seedsa/echo-editor"
|
||||||
|
target="_blank"
|
||||||
|
><svg
|
||||||
|
viewBox="0 0 15 15"
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
class="w-4 h-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M7.5.25a7.25 7.25 0 0 0-2.292 14.13c.363.066.495-.158.495-.35c0-.172-.006-.628-.01-1.233c-2.016.438-2.442-.972-2.442-.972c-.33-.838-.805-1.06-.805-1.06c-.658-.45.05-.441.05-.441c.728.051 1.11.747 1.11.747c.647 1.108 1.697.788 2.11.602c.066-.468.254-.788.46-.969c-1.61-.183-3.302-.805-3.302-3.583a2.8 2.8 0 0 1 .747-1.945c-.075-.184-.324-.92.07-1.92c0 0 .61-.194 1.994.744A7 7 0 0 1 7.5 3.756A7 7 0 0 1 9.315 4c1.384-.938 1.992-.743 1.992-.743c.396.998.147 1.735.072 1.919c.465.507.745 1.153.745 1.945c0 2.785-1.695 3.398-3.31 3.577c.26.224.492.667.492 1.343c0 .97-.009 1.751-.009 1.989c0 .194.131.42.499.349A7.25 7.25 0 0 0 7.499.25"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path></svg></a
|
||||||
|
><ThemeToggle />
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="my-0 mx-auto max-w-[1024px] p-6">
|
||||||
|
<div class="mb-2">
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="locale.setLang('zhHans')"
|
||||||
|
>
|
||||||
|
中文
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="locale.setLang('en')"
|
||||||
|
>
|
||||||
|
English
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="toggleMinimal"
|
||||||
|
>
|
||||||
|
{{ minimal ? "Full" : "Minimal" }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="hideToolbar = !hideToolbar"
|
||||||
|
>
|
||||||
|
{{ !hideToolbar ? "Hide Toolbar" : "Show Toolbar" }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="hideMenubar = !hideMenubar"
|
||||||
|
>
|
||||||
|
{{ !hideMenubar ? "Hide Menubar" : "Show Menubar" }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors rounded-md hover:bg-accent hover:text-accent-foreground"
|
||||||
|
@click="disabled = !disabled"
|
||||||
|
>
|
||||||
|
{{ disabled ? "Editable" : "Readonly" }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="border rounded-lg shadow-sm bg-card text-card-foreground">
|
||||||
|
<echo-editor
|
||||||
|
v-model="content"
|
||||||
|
:extensions="extensions"
|
||||||
|
:hideToolbar="hideToolbar"
|
||||||
|
:hideMenubar="hideMenubar || minimal"
|
||||||
|
:key="minimal ? 'minimal' : 'full'"
|
||||||
|
:disabled="disabled"
|
||||||
|
:maxHeight="512"
|
||||||
|
output="html"
|
||||||
|
:dark="theme === 'dark'"
|
||||||
|
>
|
||||||
|
</echo-editor>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 mt-6 border rounded-lg bg-muted">
|
||||||
|
<h3 class="mb-2 text-sm font-medium">HTML Output</h3>
|
||||||
|
<div class="rounded bg-muted-foreground/5 max-h-[500px] overflow-auto">
|
||||||
|
<span>{{ content }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, 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,
|
||||||
|
locale,
|
||||||
|
ImportWord,
|
||||||
|
Columns,
|
||||||
|
TextAlign,
|
||||||
|
ImageUpload,
|
||||||
|
VideoUpload,
|
||||||
|
FontFamily,
|
||||||
|
FindAndReplace,
|
||||||
|
Code,
|
||||||
|
AI,
|
||||||
|
Preview,
|
||||||
|
Printer,
|
||||||
|
Iframe,
|
||||||
|
EchoEditor,
|
||||||
|
ThemeToggle,
|
||||||
|
SpecialCharacter,
|
||||||
|
SourceCode,
|
||||||
|
} from "echo-editor";
|
||||||
|
import { ExportWord } from "../components/ExportWord";
|
||||||
|
import OpenAI from "openai";
|
||||||
|
import { DEMO_CONTENT } from "./initContent";
|
||||||
|
|
||||||
|
import "./style.css";
|
||||||
|
import "echo-editor/style.css";
|
||||||
|
|
||||||
|
const content = ref(DEMO_CONTENT);
|
||||||
|
const theme = ref<string | null>(null);
|
||||||
|
const hideToolbar = ref<boolean>(false);
|
||||||
|
const hideMenubar = ref<boolean>(false);
|
||||||
|
const disabled = ref<boolean>(false);
|
||||||
|
const minimal = ref(false);
|
||||||
|
|
||||||
|
const extensions = computed(() =>
|
||||||
|
minimal.value ? 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);
|
||||||
|
}
|
||||||
|
function toggleMinimal() {
|
||||||
|
minimal.value = !minimal.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI Completions handler function
|
||||||
|
* WARNING: This is just a demo implementation. In production:
|
||||||
|
* - DO NOT expose API keys in the frontend
|
||||||
|
* - DO implement this through your backend API
|
||||||
|
* - DO add proper error handling and rate limiting
|
||||||
|
*
|
||||||
|
* @param history - Chat history array containing messages with role and content
|
||||||
|
* @param signal - AbortSignal for cancelling requests
|
||||||
|
* @returns OpenAI chat completion stream
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
258
web3/packages/tiptap/src/views/initContent.ts
Normal file
258
web3/packages/tiptap/src/views/initContent.ts
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
export const DEMO_CONTENT = {
|
||||||
|
type: 'doc',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { textAlign: 'center', indent: 0, lineHeight: null, level: 1 },
|
||||||
|
content: [{ type: 'text', text: 'Echo Editor' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'A modern WYSIWYG AI rich text editor based on ' },
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
attrs: {
|
||||||
|
href: 'https://github.com/scrumpy/tiptap',
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer nofollow',
|
||||||
|
class: 'link',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
text: 'tiptap',
|
||||||
|
},
|
||||||
|
{ type: 'text', text: ' and ' },
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
attrs: {
|
||||||
|
href: 'https://www.shadcn-vue.com/',
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer nofollow',
|
||||||
|
class: 'link',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
text: 'shadcn ui',
|
||||||
|
},
|
||||||
|
{ type: 'text', text: ' for Vue.js' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'paragraph', attrs: { textAlign: 'left', indent: 0, lineHeight: null } },
|
||||||
|
{ type: 'paragraph', attrs: { textAlign: 'left', indent: 0, lineHeight: null } },
|
||||||
|
{ type: 'paragraph', attrs: { textAlign: 'left', indent: 0, lineHeight: null } },
|
||||||
|
{
|
||||||
|
type: 'image',
|
||||||
|
attrs: {
|
||||||
|
textAlign: 'center',
|
||||||
|
src: 'https://picsum.photos/1920/1080.webp?t=1',
|
||||||
|
alt: null,
|
||||||
|
title: null,
|
||||||
|
width: 500,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ type: 'paragraph', attrs: { textAlign: 'left', indent: 0, lineHeight: null } },
|
||||||
|
{ type: 'horizontalRule' },
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null, level: 2 },
|
||||||
|
content: [{ type: 'text', text: 'Demo' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: '👉' },
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
attrs: {
|
||||||
|
href: 'https://echo-editor.jzcloud.site/',
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer nofollow',
|
||||||
|
class: 'link',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
text: 'Demo',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null, level: 2 },
|
||||||
|
content: [{ type: 'text', text: 'Features' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bulletList',
|
||||||
|
attrs: { listStyleType: 'disc' },
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'Use ' },
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
marks: [
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
attrs: {
|
||||||
|
href: 'https://www.shadcn-vue.com/',
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopener noreferrer nofollow',
|
||||||
|
class: 'link',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
text: 'shadcn ui',
|
||||||
|
},
|
||||||
|
{ type: 'text', text: ' components' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Markdown support' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'TypeScript support' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'I18n support' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Vue 3.x support' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Slash Command' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Dark Mode' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Multi Column' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'AI Power' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'Embed' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'listItem',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null },
|
||||||
|
content: [{ type: 'text', text: 'TailwindCss' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null, level: 2 },
|
||||||
|
content: [{ type: 'text', text: 'Installation' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'codeBlock',
|
||||||
|
attrs: { language: 'bash' },
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'pnpm add echo-editor\n// or\nor yarn add echo-editor\n// or\nor npm i echo-editor -S' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
attrs: { textAlign: 'left', indent: 0, lineHeight: null, level: 3 },
|
||||||
|
content: [{ type: 'text', text: 'install plugin' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'codeBlock',
|
||||||
|
attrs: { language: "typescript", },
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: "import { createApp } from 'vue'\nimport App from './App.vue'\nimport EchoEditor from 'echo-editor'\nimport 'echo-editor/style.css'\nconst app = createApp(App)\napp.use(EchoEditor)\napp.mount('#app')",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ type: 'paragraph', attrs: { textAlign: 'left', indent: 0, lineHeight: null } },
|
||||||
|
],
|
||||||
|
}
|
||||||
89
web3/packages/tiptap/src/views/style.css
Normal file
89
web3/packages/tiptap/src/views/style.css
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
|
||||||
|
--primary: 240 5.9% 10%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
--chart-1: 12 76% 61%;
|
||||||
|
|
||||||
|
--chart-2: 173 58% 39%;
|
||||||
|
|
||||||
|
--chart-3: 197 37% 24%;
|
||||||
|
|
||||||
|
--chart-4: 43 74% 66%;
|
||||||
|
|
||||||
|
--chart-5: 27 87% 67%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 4.9% 83.9%;
|
||||||
|
|
||||||
|
--chart-1: 220 70% 50%;
|
||||||
|
|
||||||
|
--chart-2: 160 60% 45%;
|
||||||
|
|
||||||
|
--chart-3: 30 80% 55%;
|
||||||
|
|
||||||
|
--chart-4: 280 65% 60%;
|
||||||
|
|
||||||
|
--chart-5: 340 75% 55%;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
web3/packages/tiptap/tsconfig.json
Normal file
37
web3/packages/tiptap/tsconfig.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"],
|
||||||
|
"@views/*": ["src/views/*"],
|
||||||
|
"@components/*": ["src/components/*"]
|
||||||
|
// "@store/*": ["src/store/*"],
|
||||||
|
// "@assets/*": ["src/assets/*"],
|
||||||
|
// "@utils/*": ["src/utils/*"],
|
||||||
|
// "@styles/*": ["src/styles/*"],
|
||||||
|
// "@helpers/*": ["src/store/helpers/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
||||||
1310
web3/pnpm-lock.yaml
generated
1310
web3/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
|||||||
packages:
|
packages:
|
||||||
- 'packages/*'
|
- 'packages/*'
|
||||||
- 'packages/mind-map/*'
|
- 'packages/mind-map/*'
|
||||||
|
- 'packages/tiptap/*'
|
||||||
- 'apps/*'
|
- 'apps/*'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user