edit button with validator and YAML

This commit is contained in:
polv committed 2026-07-17 18:03:49 +07:00
1 parent ee87eeb616
commit 72cd3bcd09
8 files changed
+68 -35

No files matched your search

+2 -1
View File
@@ -29,5 +29,6 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.maxTokenizationLineLength": 2500 "editor.maxTokenizationLineLength": 2500
} },
"vue3snippets.enable-compile-vue-file-on-did-save-code": false
} }
@@ -15,15 +15,7 @@
import { useTemplateRef } from "vue"; import { useTemplateRef } from "vue";
import TTS from "./TTS.vue"; import TTS from "./TTS.vue";
import AudioButton from "./AudioButton.vue"; import AudioButton from "./AudioButton.vue";
import { IVoc } from "../types.ts";
export interface IVoc {
ko: string;
audio?: string;
en: string;
usages?: IVoc[];
notes?: string;
examples?: IVoc[];
}
defineProps<{ defineProps<{
v: IVoc; v: IVoc;
@@ -57,7 +57,7 @@
<nav> <nav>
<label v-for="c in vCats" :key="c"> <label v-for="c in vCats" :key="c">
<input type="checkbox" :name="c" checked @change="updateCats(c)" /> <input type="checkbox" :name="c" checked @change="updateCats(c)" />
{{ c.replace("_", " ") }} {{ c.replace(/_/g, " ") }}
</label> </label>
</nav> </nav>
<nav> <nav>
@@ -99,8 +99,10 @@ import {
useTemplateRef, useTemplateRef,
watch, watch,
} from "vue"; } from "vue";
import SentenceAudio, { IVoc } from "./SentenceAudio.vue"; import yaml from "yaml";
import SentenceAudio from "./SentenceAudio.vue";
import { supabase } from "../supabase.ts"; import { supabase } from "../supabase.ts";
import { IVoc, Voc } from "../types.ts";
const { fmKey = "vocabulary" } = defineProps<{ const { fmKey = "vocabulary" } = defineProps<{
fmKey?: string; fmKey?: string;
@@ -157,7 +159,7 @@ watch(isKoFirst, () => {
watch(isEdit, () => { watch(isEdit, () => {
if (isEdit.value) { if (isEdit.value) {
const { ko, ...vContent } = v.value; const { ko, ...vContent } = v.value;
vEditContent.value = JSON.stringify(vContent, null, 2); vEditContent.value = yaml.stringify(vContent);
} else { } else {
vEditContent.value = ""; vEditContent.value = "";
} }
@@ -240,7 +242,7 @@ function updateCats(cat: string) {
async function onVContentSave() { async function onVContentSave() {
const oldV = v.value; const oldV = v.value;
const newV = { ko: oldV.ko, ...JSON.parse(vEditContent.value) }; const newV = Voc.parse({ ko: oldV.ko, ...yaml.parse(vEditContent.value) });
let key = ""; let key = "";
let vKey = oldV.ko; let vKey = oldV.ko;
@@ -257,7 +259,8 @@ async function onVContentSave() {
vCount.set(v.ko, i + 1); vCount.set(v.ko, i + 1);
if (oldV.ko === v.ko && oldV.en === v.en) { if (oldV.ko === v.ko && oldV.en === v.en) {
key = k; // TODO: handling multiple fmKey comma-separated
key = JSON.stringify([fmKey, k]);
vKey = vK; vKey = vK;
vocabulary[k][i_vs] = newV; vocabulary[k][i_vs] = newV;
+16
View File
@@ -0,0 +1,16 @@
import * as z from "zod";
export const Voc = z.object({
ko: z.string(),
audio: z.string().optional(),
en: z.string(),
get usage() {
return z.array(Voc).optional();
},
notes: z.string().optional(),
get examples() {
return z.array(Voc).optional();
},
});
export type IVoc = z.infer<typeof Voc>;
+4 -4
View File
@@ -420,7 +420,7 @@ vocabulary:
examples: examples:
- ko: 우리 집은 너무 **멀어요** - ko: 우리 집은 너무 **멀어요**
en: Our home is very **far** en: Our home is very **far**
- ko: 저는 먼 병원에 갔어요 - ko: 저는 먼 **병원에** 갔어요
en: I went to a **far away** hospital (a hospital that is **far away**) en: I went to a **far away** hospital (a hospital that is **far away**)
- ko: 마르다 - ko: 마르다
@@ -440,7 +440,7 @@ vocabulary:
examples: examples:
- ko: 저와 저의 아버지는 너무 **비슷해요** - ko: 저와 저의 아버지는 너무 **비슷해요**
en: My father and I are very **similar** en: My father and I are very **similar**
- ko: 저는 친구와 비슷해요 - ko: 저는 친구와 **비슷해요**
en: I am **similar** to my friend en: I am **similar** to my friend
- ko: 싫다 - ko: 싫다
@@ -1245,7 +1245,7 @@ Example:
Example: Example:
- &NoBreak;<TTS class="md">우리 집은 너무 **멀어요**</TTS> = <span class="md spoiler">Our home is very **far**</span> - &NoBreak;<TTS class="md">우리 집은 너무 **멀어요**</TTS> = <span class="md spoiler">Our home is very **far**</span>
- &NoBreak;<TTS class="md">저는 먼 병원에 갔어요</TTS> = <span class="md spoiler">I went to a **far away** hospital (a hospital that is **far away**)</span> - &NoBreak;<TTS class="md">저는 먼 **병원에** 갔어요</TTS> = <span class="md spoiler">I went to a **far away** hospital (a hospital that is **far away**)</span>
</Details> </Details>
@@ -1274,7 +1274,7 @@ The pronunciation of this word is closer to “비스타다”
Examples: Examples:
- &NoBreak;<TTS class="md">저와 저의 아버지는 너무 **비슷해요**</TTS> = <span class="md spoiler">My father and I are very **similar**</span> - &NoBreak;<TTS class="md">저와 저의 아버지는 너무 **비슷해요**</TTS> = <span class="md spoiler">My father and I are very **similar**</span>
- &NoBreak;<TTS class="md">저는 친구와 비슷해요</TTS> = <span class="md spoiler">I am **similar** to my friend</span> - &NoBreak;<TTS class="md">저는 친구와 **비슷해요**</TTS> = <span class="md spoiler">I am **similar** to my friend</span>
</Details> </Details>
+8 -8
View File
@@ -67,7 +67,7 @@ vocabulary:
audio: https://www.howtostudykorean.com/wp-content/uploads/2015/11/Lesson13v6.mp3 audio: https://www.howtostudykorean.com/wp-content/uploads/2015/11/Lesson13v6.mp3
en: underground en: underground
examples: examples:
- ko: 차는 지하 **주차장**에 있어요 - ko: 차는 **지하** 주차장에 있어요
en: The car is in the **underground** parking lot en: The car is in the **underground** parking lot
- ko: 빵 - ko: 빵
@@ -80,7 +80,7 @@ vocabulary:
en: I made **bread** for my friend en: I made **bread** for my friend
- ko: 우리는 **빵**을 같이 먹었어요 - ko: 우리는 **빵**을 같이 먹었어요
en: We ate **bread** together en: We ate **bread** together
- ko: 애기는 **밥**하고 빵을 먹었어요 - ko: 애기는 밥하고 **빵**을 먹었어요
en: The baby ate rice and **bread** en: The baby ate rice and **bread**
- ko: 쓰레기 - ko: 쓰레기
@@ -291,7 +291,7 @@ vocabulary:
examples: examples:
- ko: 우리가 **늦**어서 죄송해요 - ko: 우리가 **늦**어서 죄송해요
en: Sorry we arrived **late** en: Sorry we arrived **late**
- ko: 우리는 늦은 저녁을 먹었어요 - ko: 우리는 **늦**은 저녁을 먹었어요
en: We ate a **late** dinner en: We ate a **late** dinner
- ko: 그녀는 언제나 **늦**게 와요 - ko: 그녀는 언제나 **늦**게 와요
en: She comes **late** every time en: She comes **late** every time
@@ -434,7 +434,7 @@ vocabulary:
en: I want to eat what we ate **(together)** last time en: I want to eat what we ate **(together)** last time
- ko: 이 일을 **같이** 하고 싶으면 우리는 협조해야 돼요 - ko: 이 일을 **같이** 하고 싶으면 우리는 협조해야 돼요
en: If we want to do this job **together**, we have to cooperate en: If we want to do this job **together**, we have to cooperate
- ko: 우리 학교에서 학생들은 역사와 지리를 같이 배워요 - ko: 우리 학교에서 학생들은 역사와 지리를 **같이** 배워요
en: At our school, students learn history and geography **together** en: At our school, students learn history and geography **together**
--- ---
@@ -571,7 +571,7 @@ Common Usages:
Examples: Examples:
- 차는 지하 **주차장**에 있어요 = <span class="md spoiler">The car is in the **underground** parking lot</span> - 차는 **지하** 주차장에 있어요 = <span class="md spoiler">The car is in the **underground** parking lot</span>
</Details> </Details>
@@ -589,7 +589,7 @@ Example:
- 저는 **빵**을 먹고 싶어요 = <span class="md spoiler">I want to eat **bread**</span> - 저는 **빵**을 먹고 싶어요 = <span class="md spoiler">I want to eat **bread**</span>
- 저는 친구를 위해 **빵**을 만들었어요 = <span class="md spoiler">I made **bread** for my friend</span> - 저는 친구를 위해 **빵**을 만들었어요 = <span class="md spoiler">I made **bread** for my friend</span>
- 우리는 **빵**을 같이 먹었어요 = <span class="md spoiler">We ate **bread** together</span> - 우리는 **빵**을 같이 먹었어요 = <span class="md spoiler">We ate **bread** together</span>
- 애기는 **밥**하고 빵을 먹었어요 = <span class="md spoiler">The baby ate rice and **bread**</span> - 애기는 밥하고 **빵**을 먹었어요 = <span class="md spoiler">The baby ate rice and **bread**</span>
</Details> </Details>
@@ -927,7 +927,7 @@ This is often irrelevant, but 늦다 is both <span class="md spoiler">a verb and
It is often used as an adjective to describe a time that is late. For example: It is often used as an adjective to describe a time that is late. For example:
- 우리는 늦은 저녁을 먹었어요 = <span class="md spoiler">We ate a **late** dinner</span> - 우리는 **늦**은 저녁을 먹었어요 = <span class="md spoiler">We ate a **late** dinner</span>
As an adjective, ~게 is often added to it in order to create <span class="md spoiler">an adverb</span>. For example: As an adjective, ~게 is often added to it in order to create <span class="md spoiler">an adverb</span>. For example:
@@ -1136,7 +1136,7 @@ Examples:
- 엄마가 우리랑 **같이** 못 먹어서 안타까워요 = <span class="md spoiler">It’s too bad that your mom can’t eat **together** (with us)</span> - 엄마가 우리랑 **같이** 못 먹어서 안타까워요 = <span class="md spoiler">It’s too bad that your mom can’t eat **together** (with us)</span>
- 나는 우리가 지난 번에 **같이** 먹은 것을 먹고 싶어 = <span class="md spoiler">I want to eat what we ate **(together)** last time</span> - 나는 우리가 지난 번에 **같이** 먹은 것을 먹고 싶어 = <span class="md spoiler">I want to eat what we ate **(together)** last time</span>
- 이 일을 **같이** 하고 싶으면 우리는 협조해야 돼요 = <span class="md spoiler">If we want to do this job **together**, we have to cooperate</span> - 이 일을 **같이** 하고 싶으면 우리는 협조해야 돼요 = <span class="md spoiler">If we want to do this job **together**, we have to cooperate</span>
- 우리 학교에서 학생들은 역사와 지리를 같이 배워요 = <span class="md spoiler">At our school, students learn history and geography **together**</span> - 우리 학교에서 학생들은 역사와 지리를 **같이** 배워요 = <span class="md spoiler">At our school, students learn history and geography **together**</span>
</Details> </Details>
+3 -1
View File
@@ -27,6 +27,8 @@
"dependencies": { "dependencies": {
"@supabase/supabase-js": "^2.110.5", "@supabase/supabase-js": "^2.110.5",
"pdfjs-dist": "^5.7.284", "pdfjs-dist": "^5.7.284",
"tauri-plugin-tts-api": "^0.1.11" "tauri-plugin-tts-api": "^0.1.11",
"yaml": "^2.9.0",
"zod": "^4.4.3"
} }
} }
+26 -7
View File
@@ -17,13 +17,19 @@ importers:
tauri-plugin-tts-api: tauri-plugin-tts-api:
specifier: ^0.1.11 specifier: ^0.1.11
version: 0.1.11 version: 0.1.11
yaml:
specifier: ^2.9.0
version: 2.9.0
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies: devDependencies:
'@tauri-apps/cli': '@tauri-apps/cli':
specifier: ^2.11.4 specifier: ^2.11.4
version: 2.11.4 version: 2.11.4
vitepress: vitepress:
specifier: 2.0.0-alpha.17 specifier: 2.0.0-alpha.17
version: 2.0.0-alpha.17(postcss@8.5.16) version: 2.0.0-alpha.17(postcss@8.5.16)(yaml@2.9.0)
vue: vue:
specifier: ^3.5.39 specifier: ^3.5.39
version: 3.5.39 version: 3.5.39
@@ -950,6 +956,14 @@ packages:
typescript: typescript:
optional: true optional: true
yaml@2.9.0:
resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
engines: {node: '>= 14.6'}
hasBin: true
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
zwitch@2.0.4: zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -1329,10 +1343,10 @@ snapshots:
'@ungap/structured-clone@1.3.2': {} '@ungap/structured-clone@1.3.2': {}
'@vitejs/plugin-vue@6.0.7(vite@7.3.6)(vue@3.5.39)': '@vitejs/plugin-vue@6.0.7(vite@7.3.6(yaml@2.9.0))(vue@3.5.39)':
dependencies: dependencies:
'@rolldown/pluginutils': 1.0.1 '@rolldown/pluginutils': 1.0.1
vite: 7.3.6 vite: 7.3.6(yaml@2.9.0)
vue: 3.5.39 vue: 3.5.39
'@vue/compiler-core@3.5.39': '@vue/compiler-core@3.5.39':
@@ -1683,7 +1697,7 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite@7.3.6: vite@7.3.6(yaml@2.9.0):
dependencies: dependencies:
esbuild: 0.28.1 esbuild: 0.28.1
fdir: 6.5.0(picomatch@4.0.5) fdir: 6.5.0(picomatch@4.0.5)
@@ -1693,8 +1707,9 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
yaml: 2.9.0
vitepress@2.0.0-alpha.17(postcss@8.5.16): vitepress@2.0.0-alpha.17(postcss@8.5.16)(yaml@2.9.0):
dependencies: dependencies:
'@docsearch/css': 4.6.3 '@docsearch/css': 4.6.3
'@docsearch/js': 4.6.3 '@docsearch/js': 4.6.3
@@ -1704,7 +1719,7 @@ snapshots:
'@shikijs/transformers': 3.23.0 '@shikijs/transformers': 3.23.0
'@shikijs/types': 3.23.0 '@shikijs/types': 3.23.0
'@types/markdown-it': 14.1.2 '@types/markdown-it': 14.1.2
'@vitejs/plugin-vue': 6.0.7(vite@7.3.6)(vue@3.5.39) '@vitejs/plugin-vue': 6.0.7(vite@7.3.6(yaml@2.9.0))(vue@3.5.39)
'@vue/devtools-api': 8.1.5 '@vue/devtools-api': 8.1.5
'@vue/shared': 3.5.39 '@vue/shared': 3.5.39
'@vueuse/core': 14.3.0(vue@3.5.39) '@vueuse/core': 14.3.0(vue@3.5.39)
@@ -1713,7 +1728,7 @@ snapshots:
mark.js: 8.11.1 mark.js: 8.11.1
minisearch: 7.2.0 minisearch: 7.2.0
shiki: 3.23.0 shiki: 3.23.0
vite: 7.3.6 vite: 7.3.6(yaml@2.9.0)
vue: 3.5.39 vue: 3.5.39
optionalDependencies: optionalDependencies:
postcss: 8.5.16 postcss: 8.5.16
@@ -1750,4 +1765,8 @@ snapshots:
'@vue/server-renderer': 3.5.39(vue@3.5.39) '@vue/server-renderer': 3.5.39(vue@3.5.39)
'@vue/shared': 3.5.39 '@vue/shared': 3.5.39
yaml@2.9.0: {}
zod@4.4.3: {}
zwitch@2.0.4: {} zwitch@2.0.4: {}