Files
voidraft/frontend/src/views/editor/extensions/translator/index.ts
2025-11-23 18:45:49 +08:00

84 lines
2.4 KiB
TypeScript

import { Extension, StateField } from '@codemirror/state';
import { EditorView, showTooltip, Tooltip } from '@codemirror/view';
import { translatorManager } from './manager';
import { TRANSLATION_ICON_SVG } from '@/common/constant/translation';
function TranslationTooltips(state: any): readonly Tooltip[] {
const selection = state.selection.main;
if (selection.empty) return [];
const selectedText = state.sliceDoc(selection.from, selection.to);
if (!selectedText.trim()) return [];
return [{
pos: selection.to,
above: false,
strictSide: true,
arrow: false,
create: (view) => {
const dom = document.createElement('div');
dom.className = 'cm-translator-button';
dom.innerHTML = TRANSLATION_ICON_SVG;
dom.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
showTranslatorDialog(view);
});
return { dom };
}
}];
}
function showTranslatorDialog(view: EditorView) {
const selection = view.state.selection.main;
if (selection.empty) return;
const selectedText = view.state.sliceDoc(selection.from, selection.to);
if (!selectedText.trim()) return;
const coords = view.coordsAtPos(selection.to);
if (!coords) return;
translatorManager.show(view, coords.left, coords.bottom + 5, selectedText);
}
const translationButtonField = StateField.define<readonly Tooltip[]>({
create: (state) => TranslationTooltips(state),
update: (tooltips, tr) => {
if (tr.docChanged || tr.selection) {
return TranslationTooltips(tr.state);
}
return tooltips;
},
provide: field => showTooltip.computeN([field], state => state.field(field))
});
export function createTranslatorExtension(): Extension {
return [
translationButtonField,
EditorView.baseTheme({
".cm-translator-button": {
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
background: "var(--bg-secondary, transparent)",
color: "var(--text-muted, #4285f4)",
border: "1px solid var(--border-color, #dadce0)",
borderRadius: "3px",
padding: "2px",
width: "24px",
height: "24px",
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.08)",
userSelect: "none",
"&:hover": {
background: "var(--bg-hover, rgba(66, 133, 244, 0.1))"
}
}
})
];
}
export default createTranslatorExtension;