no message

This commit is contained in:
KuroSago 2025-07-10 16:49:59 +08:00
parent bb1b91b6a6
commit 78823734da
6 changed files with 47 additions and 379 deletions

View File

@ -7,5 +7,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { TiptapEditor, ThemeToggle, useTiptapContext } from "@tiptap/component"; import { TiptapEditor, ThemeToggle, useTiptapContext } from "@tiptap/component";
useTiptapContext(); import { onMounted } from "vue";
onMounted(() => {
// useTiptapContext();
});
</script> </script>

View File

@ -1,58 +1,47 @@
<template> <template>
<div class="mb-2"> <div class="flex items-center justify-between">
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="locale.setLang('zhHans')" @click="locale.setLang('zhHans')"
> >
中文 中文
</button> </button>
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="locale.setLang('en')" @click="locale.setLang('en')"
> >
English English
</button> </button>
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="emit('toggle-minimal')" @click="state.minimal = !state.minimal"
> >
{{ minimal ? "Full" : "Minimal" }} {{ state.minimal ? "Full" : "Minimal" }}
</button> </button>
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="emit('toggle-toolbar')" @click="state.hideToolbar = !state.hideToolbar"
> >
{{ !hideToolbar ? "Hide Toolbar" : "Show Toolbar" }} {{ !state.hideToolbar ? "Hide Toolbar" : "Show Toolbar" }}
</button> </button>
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="emit('toggle-menubar')" @click="state.hideMenubar = !state.hideMenubar"
> >
{{ !hideMenubar ? "Hide Menubar" : "Show Menubar" }} {{ !state.hideMenubar ? "Hide Menubar" : "Show Menubar" }}
</button> </button>
<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" class="inline-flex items-center justify-center px-3 py-2 text-sm transition-colors border rounded-md hover:bg-accent hover:text-accent-foreground"
@click="emit('toggle-editable')" @click="state.disabled = !state.disabled"
> >
{{ disabled ? "Editable" : "Readonly" }} {{ state.disabled ? "Editable" : "Readonly" }}
</button> </button>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { locale } from "echo-editor"; import { locale } from "echo-editor";
import { useTiptapContext } from "../context/useTiptapStore";
defineProps<{ const { state } = useTiptapContext();
minimal: boolean;
hideToolbar: boolean;
hideMenubar: boolean;
disabled: boolean;
}>();
const emit = defineEmits([
"toggle-minimal",
"toggle-toolbar",
"toggle-menubar",
"toggle-editable",
]);
</script> </script>

View File

@ -1,4 +1,4 @@
import { provide, inject, reactive, readonly, type InjectionKey } from "vue"; import { provide, inject, reactive, type InjectionKey } from "vue";
const TiptapStoreKey: InjectionKey<TiptapStore> = Symbol("TiptapStore"); const TiptapStoreKey: InjectionKey<TiptapStore> = Symbol("TiptapStore");
@ -11,18 +11,8 @@ export interface TiptapState {
minimal: boolean; minimal: boolean;
} }
export interface TiptapActions {
toggleMinimal: () => void;
updateContent: (newContent: string) => void;
setTheme: (theme: string) => void;
toggleToolbar: () => void;
toggleMenubar: () => void;
toggleEditable: () => void;
}
export type TiptapStore = { export type TiptapStore = {
state: Readonly<TiptapState>; state: TiptapState;
actions: TiptapActions;
}; };
export function useTiptapStore(initialState?: Partial<TiptapState>) { export function useTiptapStore(initialState?: Partial<TiptapState>) {
@ -36,42 +26,8 @@ export function useTiptapStore(initialState?: Partial<TiptapState>) {
...initialState, ...initialState,
}); });
const toggleMinimal = () => {
state.minimal = !state.minimal;
};
const updateContent = (newContent: string) => {
state.content = newContent;
};
const setTheme = (theme: string) => {
state.theme = theme;
};
const toggleToolbar = () => {
state.hideToolbar = !state.hideToolbar;
};
const toggleMenubar = () => {
state.hideMenubar = !state.hideMenubar;
};
const toggleEditable = () => {
state.disabled = !state.disabled;
};
const actions: TiptapActions = {
toggleMinimal,
updateContent,
setTheme,
toggleToolbar,
toggleMenubar,
toggleEditable,
};
const store: TiptapStore = { const store: TiptapStore = {
state: readonly(state), state: state,
actions,
}; };
provide(TiptapStoreKey, store); provide(TiptapStoreKey, store);

View File

@ -1,5 +1,12 @@
import TiptapEditor from "./views/index.vue"; import TiptapEditor from "./views/index.vue";
import { useTiptapStore, useTiptapContext } from "./context/useTiptapStore"; import { useTiptapStore, useTiptapContext } from "./context/useTiptapStore";
import { ThemeToggle } from "echo-editor"; import { ThemeToggle } from "echo-editor";
import EditorActions from "./components/EditorActions.vue";
export { TiptapEditor, ThemeToggle, useTiptapStore, useTiptapContext }; export {
TiptapEditor,
ThemeToggle,
useTiptapStore,
useTiptapContext,
EditorActions,
};

View File

@ -1,19 +1,8 @@
<template> <template>
<div class="min-h-screen bg-background"> <div class="">
<EditorActions <EditorActions />
:minimal="state.minimal"
:hide-toolbar="state.hideToolbar"
:hide-menubar="state.hideMenubar"
:disabled="state.disabled"
@toggle-minimal="actions.toggleMinimal"
@toggle-toolbar="actions.toggleToolbar"
@toggle-menubar="actions.toggleMenubar"
@toggle-editable="actions.toggleEditable"
/>
<echo-editor <echo-editor
:model-value="state.content" :model-value="state.content"
@update:model-value="actions.updateContent"
:extensions="editorExtensions" :extensions="editorExtensions"
:hideToolbar="state.hideToolbar" :hideToolbar="state.hideToolbar"
:hideMenubar="state.hideMenubar || state.minimal" :hideMenubar="state.hideMenubar || state.minimal"
@ -21,13 +10,11 @@
:disabled="state.disabled" :disabled="state.disabled"
:maxHeight="512" :maxHeight="512"
output="html" output="html"
:theme="state.theme" @update:theme="state.theme"
@update:theme="actions.setTheme" @update:model-value="state.content"
:dark="state.theme === 'dark'" :dark="state.theme === 'dark'"
:theme="state.theme"
/> />
<HtmlOutput :content="state.content" />
minimal : {{ state.minimal }}
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -35,7 +22,6 @@ import { computed } from "vue";
import { EchoEditor } from "echo-editor"; import { EchoEditor } from "echo-editor";
import { DEMO_CONTENT } from "./initContent"; import { DEMO_CONTENT } from "./initContent";
import EditorActions from "../components/EditorActions.vue"; import EditorActions from "../components/EditorActions.vue";
import HtmlOutput from "../components/HtmlOutput.vue";
import { useEditorConfig } from "../components/useEditorConfig"; import { useEditorConfig } from "../components/useEditorConfig";
import { useTiptapStore, type TiptapState } from "../context/useTiptapStore"; import { useTiptapStore, type TiptapState } from "../context/useTiptapStore";
@ -46,12 +32,14 @@ const props = defineProps<{
initialState?: Partial<TiptapState>; initialState?: Partial<TiptapState>;
}>(); }>();
const { state, actions } = useTiptapStore({ const { state } = useTiptapStore({
...props.initialState, ...props.initialState,
content: props.initialState?.content || DEMO_CONTENT, content: props.initialState?.content || DEMO_CONTENT,
}); });
const editorExtensions = computed( console.log("Tiptap state:", state);
() => useEditorConfig(state.minimal).extensions
); const editorExtensions = computed(() => {
return useEditorConfig(state.minimal).extensions.value;
});
</script> </script>

View File

@ -1,279 +1,3 @@
export const DEMO_CONTENT = { export const DEMO_CONTENT = `
type: "doc", <h1 style="text-align: center">Echo Editor</h1><p style="text-align: left">A modern WYSIWYG AI rich text editor based on <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://github.com/scrumpy/tiptap" stricturl="true">tiptap</a> and <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://www.shadcn-vue.com/" stricturl="true">shadcn ui</a> for Vue.js</p><p style="text-align: left"></p><p style="text-align: left"></p><p style="text-align: left"></p><img height="auto" style="margin-left: auto; margin-right: auto; text-align: center" src="https://picsum.photos/1920/1080.webp?t=1" flipx="false" flipy="false" originwidth="500" originheight="281.25" width="500"><p style="text-align: left"></p><div data-type="horizontalRule"><hr></div><h2 style="text-align: left">Demo</h2><p style="text-align: left">👉<a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://echo-editor.jzcloud.site/" stricturl="true">Demo</a></p><h2 style="text-align: left">Features</h2><ul style="list-style-type: disc"><li><p style="text-align: left">Use <a target="_blank" rel="noopener noreferrer nofollow" class="link" href="https://www.shadcn-vue.com/" stricturl="true">shadcn ui</a> components</p></li><li><p style="text-align: left">Markdown support</p></li><li><p style="text-align: left">TypeScript support</p></li><li><p style="text-align: left">I18n support</p></li><li><p style="text-align: left">Vue 3.x support</p></li><li><p style="text-align: left">Slash Command</p></li><li><p style="text-align: left">Dark Mode</p></li><li><p style="text-align: left">Multi Column</p></li><li><p style="text-align: left">AI Power</p></li><li><p style="text-align: left">Embed</p></li><li><p style="text-align: left">TailwindCss</p></li></ul><h2 style="text-align: left">Installation</h2><pre vnode="true" code="pnpm add echo-editor // or or yarn add echo-editor // or or npm i echo-editor -S" language="bash" linenumbers="true" wordwrap="false" tabsize="2" shouldfocus="false"><code>pnpm add echo-editor // or or yarn add echo-editor // or or npm i echo-editor -S</code></pre><h3 style="text-align: left">install plugin</h3><pre vnode="true" code="import { createApp } from 'vue' import App from './App.vue' import EchoEditor from 'echo-editor' import 'echo-editor/style.css' const app = createApp(App) app.use(EchoEditor) app.mount('#app')" language="typescript" linenumbers="true" wordwrap="false" tabsize="2" shouldfocus="false"><code>import { createApp } from 'vue' import App from './App.vue' import EchoEditor from 'echo-editor' import 'echo-editor/style.css' const app = createApp(App) app.use(EchoEditor) app.mount('#app')</code></pre><p style="text-align: left"></p>
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 },
},
],
};