fix: Enhance Kol Variable Rendering and Management
- Updated the regex for placeholder matching to support more flexible key formats. - Refactored variable storage to allow both ID and key lookups in rendering. - Improved schema validation to check for duplicate keys and enforce non-empty keys. - Added new utility functions for variable value lookup and display name generation. - Enhanced tests to cover new key-based variable scenarios. - Refactored KolPrompt repository to simplify prompt storage and management, including the removal of obsolete revision handling. - Introduced new API endpoints for saving, activating, and archiving prompts. - Added new utility functions for handling Kol placeholders and platform options in the admin web. - Implemented database migrations to simplify prompt storage structure and ensure data integrity.
This commit is contained in:
@@ -52,8 +52,16 @@ import { callCommand, replaceAll } from "@milkdown/kit/utils";
|
||||
import { Crepe, CrepeFeature } from "@milkdown/crepe";
|
||||
import "@milkdown/crepe/theme/common/style.css";
|
||||
import "@milkdown/crepe/theme/frame.css";
|
||||
import { Milkdown, useEditor, useInstance } from "@milkdown/vue";
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
type ComponentPublicInstance,
|
||||
} from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import EditorActionMenu from "@/components/editor-common/EditorActionMenu.vue";
|
||||
@@ -88,8 +96,11 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const crepeRef = ref<Crepe | null>(null);
|
||||
const crepeRef = shallowRef<Crepe | null>(null);
|
||||
const syncingExternalMarkdown = ref(false);
|
||||
const editorRootRef = ref<HTMLElement | null>(null);
|
||||
const editorRef = shallowRef<MilkdownEditorInstance | null>(null);
|
||||
const loading = ref(true);
|
||||
const surfaceRef = ref<HTMLElement | null>(null);
|
||||
const tablePickerRef = ref<HTMLElement | null>(null);
|
||||
|
||||
@@ -197,6 +208,8 @@ type SelectedImageSnapshot = {
|
||||
align: RichImageAlign;
|
||||
};
|
||||
|
||||
type MilkdownEditorInstance = Awaited<ReturnType<Crepe["create"]>>;
|
||||
|
||||
function createDefaultTableContextMenuState(): TableContextMenuState {
|
||||
return {
|
||||
open: false,
|
||||
@@ -248,7 +261,7 @@ function createDefaultAiOptimizePanelState(): AiOptimizePanelState {
|
||||
};
|
||||
}
|
||||
|
||||
const { loading } = useEditor((root) => {
|
||||
function createCrepe(root: HTMLElement): Crepe {
|
||||
const crepe = new Crepe({
|
||||
root,
|
||||
defaultValue: props.modelValue || "",
|
||||
@@ -298,10 +311,13 @@ const { loading } = useEditor((root) => {
|
||||
|
||||
crepeRef.value = crepe;
|
||||
return crepe;
|
||||
});
|
||||
}
|
||||
|
||||
const [instanceLoading, getEditor] = useInstance();
|
||||
const editorDisabled = computed(() => props.disabled || instanceLoading.value || loading.value);
|
||||
function getEditor(): MilkdownEditorInstance | null {
|
||||
return editorRef.value;
|
||||
}
|
||||
|
||||
const editorDisabled = computed(() => props.disabled || loading.value);
|
||||
const tablePickerOpen = ref(false);
|
||||
const imagePickerOpen = ref(false);
|
||||
const imagePickerAction = ref<ImagePickerAction>("insert");
|
||||
@@ -528,9 +544,9 @@ watch(
|
||||
);
|
||||
|
||||
watch(
|
||||
[() => props.modelValue, loading, instanceLoading],
|
||||
([value, editorLoading, currentInstanceLoading]) => {
|
||||
if (editorLoading || currentInstanceLoading) {
|
||||
[() => props.modelValue, loading],
|
||||
([value, editorLoading]) => {
|
||||
if (editorLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -539,6 +555,26 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
async function mountEditor(): Promise<void> {
|
||||
const root = editorRootRef.value;
|
||||
if (!root || crepeRef.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const crepe = createCrepe(root);
|
||||
|
||||
try {
|
||||
editorRef.value = await crepe.create();
|
||||
} catch (error) {
|
||||
crepeRef.value = null;
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopAiOptimizeGeneration();
|
||||
if (selectedImageSyncFrame) {
|
||||
@@ -549,6 +585,8 @@ onBeforeUnmount(() => {
|
||||
window.removeEventListener("pointerdown", handleWindowPointerDown);
|
||||
window.removeEventListener("resize", handleWindowResize);
|
||||
window.removeEventListener("keydown", handleWindowKeydown);
|
||||
editorRef.value = null;
|
||||
void crepeRef.value?.destroy().catch(console.error);
|
||||
crepeRef.value = null;
|
||||
});
|
||||
|
||||
@@ -556,6 +594,7 @@ onMounted(() => {
|
||||
window.addEventListener("pointerdown", handleWindowPointerDown);
|
||||
window.addEventListener("resize", handleWindowResize);
|
||||
window.addEventListener("keydown", handleWindowKeydown);
|
||||
void mountEditor();
|
||||
});
|
||||
|
||||
function runCommand(command: { key: unknown }, payload?: unknown): void {
|
||||
@@ -680,6 +719,18 @@ function closeTablePicker(): void {
|
||||
tablePickerOpen.value = false;
|
||||
}
|
||||
|
||||
function assignTablePickerRef(element: Element | ComponentPublicInstance | null): void {
|
||||
tablePickerRef.value = element instanceof HTMLElement ? element : null;
|
||||
}
|
||||
|
||||
function assignSurfaceRef(element: Element | ComponentPublicInstance | null): void {
|
||||
surfaceRef.value = element instanceof HTMLElement ? element : null;
|
||||
}
|
||||
|
||||
function assignEditorRootRef(element: Element | ComponentPublicInstance | null): void {
|
||||
editorRootRef.value = element instanceof HTMLElement ? element : null;
|
||||
}
|
||||
|
||||
function closeTableContextMenu(): void {
|
||||
tableContextMenu.value = createDefaultTableContextMenuState();
|
||||
}
|
||||
@@ -1830,7 +1881,7 @@ function runTableContextAction(action: TableContextMenuAction): void {
|
||||
<template #icon><OrderedListOutlined /></template>
|
||||
</a-button>
|
||||
|
||||
<div ref="tablePickerRef" class="article-editor-canvas__table-trigger">
|
||||
<div :ref="assignTablePickerRef" class="article-editor-canvas__table-trigger">
|
||||
<a-button
|
||||
size="small"
|
||||
type="text"
|
||||
@@ -1861,7 +1912,7 @@ function runTableContextAction(action: TableContextMenuAction): void {
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref="surfaceRef"
|
||||
:ref="assignSurfaceRef"
|
||||
class="article-editor-canvas__surface"
|
||||
@contextmenu.capture="handleEditorContextMenu"
|
||||
@dragstart.capture="handleEditorDragStart"
|
||||
@@ -1881,7 +1932,7 @@ function runTableContextAction(action: TableContextMenuAction): void {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Milkdown />
|
||||
<div :ref="assignEditorRootRef" data-milkdown-root></div>
|
||||
|
||||
<EditorSelectionFrame
|
||||
v-if="selectedImage.open"
|
||||
|
||||
Reference in New Issue
Block a user