diff --git a/package.json b/package.json index 6e9834a..83d61d5 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@ungap/structured-clone": "^1.2.0", "@vicons/fluent": "^0.12.0", "@vue/tsconfig": "^0.4.0", + "codemirror": "^6.0.1", "fflate": "^0.8.0", "jieba-wasm": "^0.0.2", "jss": "^10.10.0", @@ -41,6 +42,7 @@ "sass": "^1.64.1", "save-file": "^2.3.1", "vue": "^3.3.4", + "vue-codemirror": "^6.1.1", "vue-i18n": "9", "vue-virtual-scroller": "next", "vuedraggable": "next" diff --git a/src/App.vue b/src/App.vue index 31001b4..a8cb111 100644 --- a/src/App.vue +++ b/src/App.vue @@ -48,6 +48,7 @@ + @@ -74,6 +75,7 @@ import ContextMenu from "./components/ContextMenu.vue"; import UploadDBDialog from "./components/modals/UploadDBDialog.vue"; import SplitWordModal from "./components/modals/SplitWordModal.vue"; import ServiceWorkerUpdater from "./components/ServiceWorkerUpdater.vue"; +import ImportPlainTextModal from "./components/modals/ImportPlainTextModal.vue"; const LyricEditor = defineAsyncComponent(() => import("./components/LyricEditor.vue")); const LyricSyncEditor = defineAsyncComponent(() => import("./components/LyricSyncEditor.vue")); const AMLLPreviewView = defineAsyncComponent(() => import("./components/AMLLPreviewView.vue")); diff --git a/src/components/LyricLineEditor.vue b/src/components/LyricLineEditor.vue index 7e8d9dd..a65727a 100644 --- a/src/components/LyricLineEditor.vue +++ b/src/components/LyricLineEditor.vue @@ -17,15 +17,18 @@ -
-
-
@@ -64,7 +67,7 @@ const editState = reactive({ romanLine: props.line.romanLyric, }); const settings = useSettings(); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); const inputRef = ref(null); function onSort(e: CustomEvent & { diff --git a/src/components/TopBar.vue b/src/components/TopBar.vue index 6f8f635..7614d9b 100644 --- a/src/components/TopBar.vue +++ b/src/components/TopBar.vue @@ -92,7 +92,7 @@ const settings = useSettings(); const aboutModalOpened = ref(false); const notify = useNotification(); const dialogs = useDialogs(); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); const MENU = ref({ file: [ @@ -104,6 +104,8 @@ const MENU = ref({ // { type: 'divider' }, { label: t('topBar.menu.importLyric'), key: 'import-from', children: [{ + label: t('topBar.menu.importLyricFromText'), key: 'import-from-text', + }, { label: t('topBar.menu.importLyricFromLrc'), key: 'import-from-lrc', }, { label: t('topBar.menu.importLyricFromYrc'), key: 'import-from-yrc', @@ -187,6 +189,10 @@ function onSelectMenu(key: string) { fileDialog.remove(); break; } + case "import-from-text": { + dialogs.importFromText = true; + break; + } case "import-from-lrc": { const fileDialog = document.createElement("input"); fileDialog.type = "file"; diff --git a/src/components/modals/ImportPlainTextModal.vue b/src/components/modals/ImportPlainTextModal.vue new file mode 100644 index 0000000..d5c3851 --- /dev/null +++ b/src/components/modals/ImportPlainTextModal.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/src/components/modals/ProgressOverlay.vue b/src/components/modals/ProgressOverlay.vue index d447aa0..a8d7db1 100644 --- a/src/components/modals/ProgressOverlay.vue +++ b/src/components/modals/ProgressOverlay.vue @@ -20,6 +20,6 @@ import { storeToRefs } from "pinia"; import { useI18n } from "vue-i18n"; const { currentProgresses } = storeToRefs(useProgress()); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); diff --git a/src/components/modals/SplitWordModal.vue b/src/components/modals/SplitWordModal.vue index ac5a575..fc0112a 100644 --- a/src/components/modals/SplitWordModal.vue +++ b/src/components/modals/SplitWordModal.vue @@ -20,7 +20,7 @@ import { useI18n } from "vue-i18n"; const lyric = useEditingLyric(); const notify = useNotification(); const dialogs = useDialogs(); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); const submitData = reactive({ name: "", diff --git a/src/components/modals/TutorialModal.vue b/src/components/modals/TutorialModal.vue index 4082722..f5551dc 100644 --- a/src/components/modals/TutorialModal.vue +++ b/src/components/modals/TutorialModal.vue @@ -167,6 +167,6 @@ import { useSettings } from '../../store'; import { useI18n } from "vue-i18n"; const settings = useSettings(); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); diff --git a/src/components/modals/UploadDBDialog.vue b/src/components/modals/UploadDBDialog.vue index e737f5c..75af3ad 100644 --- a/src/components/modals/UploadDBDialog.vue +++ b/src/components/modals/UploadDBDialog.vue @@ -57,7 +57,7 @@ import { useI18n } from "vue-i18n"; const lyric = useEditingLyric(); const notify = useNotification(); const dialogs = useDialogs(); -const { t } = useI18n(); +const { t } = useI18n({ useScope: "global" }); const submitData = reactive({ name: "", diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 4912571..6f2479d 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,5 +1,4 @@ import { createI18n } from "vue-i18n"; -import type { DefineLocaleMessage } from "vue-i18n"; import { zhCN } from "./zh-cn"; import { enUS } from "./en-us"; @@ -18,6 +17,7 @@ export type LocateMessage = FullPartial; export const i18n = createI18n<[typeof zhCN]>({ legacy: false, globalInjection: true, + silentFallbackWarn: true, locale: navigator.language, fallbackLocale: [...navigator.languages, "zh-CN"], messages: { diff --git a/src/i18n/zh-cn.ts b/src/i18n/zh-cn.ts index f91589d..e5209e4 100644 --- a/src/i18n/zh-cn.ts +++ b/src/i18n/zh-cn.ts @@ -11,6 +11,7 @@ export const zhCN = { openLyric: "打开歌词", saveLyric: "保存歌词", importLyric: "导入歌词...", + importLyricFromText: "从纯文本导入", importLyricFromLrc: "从 LRC 歌词导入", importLyricFromYrc: "从 YRC 歌词导入", importLyricFromQrc: "从 QRC 歌词导入", @@ -106,6 +107,23 @@ export const zhCN = { title: "拆分单词", splitBtn: "拆分", }, + importPlainTextModal: { + title: "从纯文本导入歌词", + textPlaceholder: "纯文本歌词内容", + importMode: "导入模式", + lyricOnly: "仅歌词", + lyricWithTranslation: "歌词和翻译歌词", + lyricWithTranslationAndRoman: "歌词和翻译、音译歌词", + lyricSplitMode: "歌词分行(翻译和音译)模式", + sameLineWithSeparator: "同行分隔", + sameLineSeparator: "歌词行分隔符", + sameLineSeparatorPlaceholder: "留空则不分隔", + interleavedLine: "多行交错分隔", + swapTransAndRoman: "交换翻译行和音译行", + wordSeparator: "单词分隔符", + wordSeparatorPlaceholder: "留空则不分隔", + importBtn: "导入歌词", + }, uploadDBDialog: { title: "提交歌词到 AMLL 歌词数据库(仅网易云)", ncmOnlyWarning: @@ -158,7 +176,7 @@ export const zhCN = { title: "加载歌词", content: [ "您可以通过左上角的菜单 - 文件 - 打开歌词来加载歌词哦!", - "如果您的歌词不是 TTML 格式也没有关系,歌词工具支持从 LRC/YRC/QRC/Lyricify Syllable 格式导入歌词哦!", + "如果您的歌词不是 TTML 格式也没有关系,歌词工具支持从 纯文本/LRC/YRC/QRC/Lyricify Syllable 格式导入歌词哦!", ].join("\n"), }, step2: { diff --git a/src/store/dialogs.ts b/src/store/dialogs.ts index 4d1653d..e765d0c 100644 --- a/src/store/dialogs.ts +++ b/src/store/dialogs.ts @@ -4,5 +4,6 @@ export const useDialogs = defineStore("dialogs", { state: () => ({ submitLyric: false, editLyricInfo: false, + importFromText: false, }), }); diff --git a/yarn.lock b/yarn.lock index f28def8..a514994 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1398,6 +1398,89 @@ __metadata: languageName: node linkType: hard +"@codemirror/autocomplete@npm:^6.0.0": + version: 6.9.0 + resolution: "@codemirror/autocomplete@npm:6.9.0" + dependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.6.0 + "@lezer/common": ^1.0.0 + peerDependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + checksum: a5f661944c75f40b02c90a193c9a459c0fd7e335c0ac5973420c19157dfb46010f573c2b70731591fe477e7a2ad10121ff3ae394a72d450946d7b886c28b0368 + languageName: node + linkType: hard + +"@codemirror/commands@npm:6.x, @codemirror/commands@npm:^6.0.0": + version: 6.2.4 + resolution: "@codemirror/commands@npm:6.2.4" + dependencies: + "@codemirror/language": ^6.0.0 + "@codemirror/state": ^6.2.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + checksum: 468895fa19ff0554181b698c81f850820de5c0289cab92c44392fb127286f09ca72b921d6ea4353b70b616a4fd0c3667d86b6f917202a3ad2e196eb7b581f7b6 + languageName: node + linkType: hard + +"@codemirror/language@npm:6.x, @codemirror/language@npm:^6.0.0": + version: 6.9.0 + resolution: "@codemirror/language@npm:6.9.0" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + "@lezer/common": ^1.0.0 + "@lezer/highlight": ^1.0.0 + "@lezer/lr": ^1.0.0 + style-mod: ^4.0.0 + checksum: 9a897fb0f569159eeafb7dce83061b425af7244bbeae2649e0e677488548b2a02eaf0c13c0c5b4d59da55e8866e6f4dc7abe3dfaa09c13749a2fa2c0dbc0c565 + languageName: node + linkType: hard + +"@codemirror/lint@npm:^6.0.0": + version: 6.4.0 + resolution: "@codemirror/lint@npm:6.4.0" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + crelt: ^1.0.5 + checksum: ba15f7dd87afbceafaa0b68f94b0d53727e4aacca7a81a4ed3278706df5787fdf18cd3f0d807a136f902b2fc2296bf3490462fd543d1d4ced17a0d8c171820fd + languageName: node + linkType: hard + +"@codemirror/search@npm:^6.0.0": + version: 6.5.1 + resolution: "@codemirror/search@npm:6.5.1" + dependencies: + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + crelt: ^1.0.5 + checksum: 672515c20238f69ff5cd8b662128699178ba7e020fc44a8ed2b0dcc25d8d5f5579418865616dd8809317a408fb08b6001a442f0fb706a772250b4284d7437045 + languageName: node + linkType: hard + +"@codemirror/state@npm:6.x, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.4, @codemirror/state@npm:^6.2.0": + version: 6.2.1 + resolution: "@codemirror/state@npm:6.2.1" + checksum: d12a321d0471b264b9d3259042bff913a8b939e8d28d408ff452004538a71ca9d5329df3f8a1d8a9183f5b42a7ef5b200737bcab1065714f5ae8e0a5ba9d59d3 + languageName: node + linkType: hard + +"@codemirror/view@npm:6.x, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.6.0": + version: 6.16.0 + resolution: "@codemirror/view@npm:6.16.0" + dependencies: + "@codemirror/state": ^6.1.4 + style-mod: ^4.0.0 + w3c-keyname: ^2.2.4 + checksum: 54d412b5159716c8a1a9c46fa04ff083e68a663cb887e6e2a4ca86fe9c3930d5255200fe84c65620e0a442f62dc2c13df277bcd1d4eef2e11e3c4e124fcf9d38 + languageName: node + linkType: hard + "@css-render/plugin-bem@npm:^0.15.10": version: 0.15.12 resolution: "@css-render/plugin-bem@npm:0.15.12" @@ -1705,6 +1788,31 @@ __metadata: languageName: node linkType: hard +"@lezer/common@npm:^1.0.0": + version: 1.0.4 + resolution: "@lezer/common@npm:1.0.4" + checksum: 0bea82da76e0b89afad4e5159d3add460022916352c47906ec67b26d6fe5ec9cb8e23df0e2bf0adef765ae78bed1706fc573a11506d01a80112a5b6dd317730c + languageName: node + linkType: hard + +"@lezer/highlight@npm:^1.0.0": + version: 1.1.6 + resolution: "@lezer/highlight@npm:1.1.6" + dependencies: + "@lezer/common": ^1.0.0 + checksum: 411a702394c4c996b7d7f145a38f3a85a8cc698b3918acc7121c629255bb76d4ab383753f69009e011dc415210c6acbbb5b27bde613259ab67e600b29397b03b + languageName: node + linkType: hard + +"@lezer/lr@npm:^1.0.0": + version: 1.3.10 + resolution: "@lezer/lr@npm:1.3.10" + dependencies: + "@lezer/common": ^1.0.0 + checksum: 9d3c22bf692561cf7fe2f3d14e821913f87116ff9d73b8b550e7998b6135baae9f504563846a4257e1bb4eae97ae1b60c06c6066450ddeef5e03e8783526b2ae + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2747,6 +2855,7 @@ __metadata: "@vicons/fluent": ^0.12.0 "@vitejs/plugin-vue": ^4.2.3 "@vue/tsconfig": ^0.4.0 + codemirror: ^6.0.1 fflate: ^0.8.0 jieba-wasm: ^0.0.2 jss: ^10.10.0 @@ -2766,6 +2875,7 @@ __metadata: vite-plugin-wasm: ^3.2.2 vite-svg-loader: ^4.0.0 vue: ^3.3.4 + vue-codemirror: ^6.1.1 vue-i18n: 9 vue-virtual-scroller: next vuedraggable: next @@ -3994,6 +4104,21 @@ __metadata: languageName: node linkType: hard +"codemirror@npm:^6.0.1": + version: 6.0.1 + resolution: "codemirror@npm:6.0.1" + dependencies: + "@codemirror/autocomplete": ^6.0.0 + "@codemirror/commands": ^6.0.0 + "@codemirror/language": ^6.0.0 + "@codemirror/lint": ^6.0.0 + "@codemirror/search": ^6.0.0 + "@codemirror/state": ^6.0.0 + "@codemirror/view": ^6.0.0 + checksum: 1a78f7077ac5801bdbff162aa0c61bf2b974603c7e9a477198c3ce50c789af674a061d7c293c58b73807eda345c2b5228c38ad2aabb9319d552d5486f785cbef + languageName: node + linkType: hard + "collection-visit@npm:^1.0.0": version: 1.0.0 resolution: "collection-visit@npm:1.0.0" @@ -4459,6 +4584,13 @@ __metadata: languageName: node linkType: hard +"crelt@npm:^1.0.5": + version: 1.0.6 + resolution: "crelt@npm:1.0.6" + checksum: dad842093371ad702afbc0531bfca2b0a8dd920b23a42f26e66dabbed9aad9acd5b9030496359545ef3937c3aced0fd4ac39f7a2d280a23ddf9eb7fdcb94a69f + languageName: node + linkType: hard + "cross-spawn@npm:^5.0.1": version: 5.1.0 resolution: "cross-spawn@npm:5.1.0" @@ -12541,6 +12673,13 @@ __metadata: languageName: node linkType: hard +"style-mod@npm:^4.0.0": + version: 4.1.0 + resolution: "style-mod@npm:4.1.0" + checksum: 8402b14ca11113a3640d46b3cf7ba49f05452df7846bc5185a3535d9b6a64a3019e7fb636b59ccbb7816aeb0725b24723e77a85b05612a9360e419958e13b4e6 + languageName: node + linkType: hard + "subarg@npm:^1.0.0": version: 1.0.0 resolution: "subarg@npm:1.0.0" @@ -13598,6 +13737,21 @@ __metadata: languageName: node linkType: hard +"vue-codemirror@npm:^6.1.1": + version: 6.1.1 + resolution: "vue-codemirror@npm:6.1.1" + dependencies: + "@codemirror/commands": 6.x + "@codemirror/language": 6.x + "@codemirror/state": 6.x + "@codemirror/view": 6.x + peerDependencies: + codemirror: 6.x + vue: 3.x + checksum: 90eb7a786040a352e9e5b21c9db582dab60dff1289e26470cc8c51613c4457ca1b85d72b8042d2c059b642b9526e77532751847a6911453eb403762d3eaf0799 + languageName: node + linkType: hard + "vue-demi@npm:>=0.14.5": version: 0.14.5 resolution: "vue-demi@npm:0.14.5" @@ -13700,6 +13854,13 @@ __metadata: languageName: node linkType: hard +"w3c-keyname@npm:^2.2.4": + version: 2.2.8 + resolution: "w3c-keyname@npm:2.2.8" + checksum: 95bafa4c04fa2f685a86ca1000069c1ec43ace1f8776c10f226a73296caeddd83f893db885c2c220ebeb6c52d424e3b54d7c0c1e963bbf204038ff1a944fbb07 + languageName: node + linkType: hard + "walk@npm:2.3.3": version: 2.3.3 resolution: "walk@npm:2.3.3"