1import { r as a, j as e, i as Xt, u as Dt, f as dt } from "./vendor-query-CnLZf_EB.js"; 2import { u as Pn } from "./useMobileUpdateSave-anqCDGbY.js"; 3import { r as fr, f as On } from "./vendor-react-CjjLCydY.js"; 4import { u as Bn } from "./usePortalDropdown-C-mf-EoE.js"; 5import { Q as Ke, $ as _t, bJ as Je, f_ as Oe, cp as on, M as Me, c9 as sr, aN as Vn, br as $n, X as Sr, ay as zn, bP as ln, d as Ln, d0 as xr, ex as gr, aH as Fn, cm as hr, b3 as Hn, aL as Ht, cw as de, bd as Ze, aM as _r, aF as $t, cg as pr, az as br, H as jt, bf as yr, f as yt, G as cn, P as dn, j as or, t as We, e as ir, aO as un, ax as Kn, l as qn, Z as Cr, bL as Un, bN as Wn, c7 as Yn, aP as mn } from "./App-BzxudLOB.js"; 6import { u as fn } from "./useShiftSelect-CUvKmjGR.js"; 7import { a as xn, b as gn, H as hn, i as Qn, p as Xn, u as Gn, c as Zn } from "./HandwritingSaveBanner-dObu8Eb3.js"; 8import { u as pn } from "./useDisableBodyTextSelection-BmhABaHn.js"; 9import { O as vr } from "./OfflineUnavailableNotice-CodyJ7Vu.js"; 10import { u as bn } from "./useHandwritingPersistenceFeedback-DN76OoUv.js"; 11import { S as Pt } from "./shuffle-o6vsobsx.js"; 12import { C as Jn } from "./circle-check-eyHu01cN.js"; 13import { a as ea, A as Er } from "./arrow-up-Ds4NPYNn.js"; 14import { V as Ot } from "./volume-x-x6WChpUm.js"; 15import { V as Nt } from "./volume-2-HNomr_3p.js"; 16import { A as wr } from "./arrow-left-D-FUCoHx.js"; 17import { R as ta } from "./rotate-ccw-DDCLHkiw.js"; 18import { B as ra } from "./brain-B7RZAYl3.js"; 19import { E as Kt } from "./eye-BiqUxfUl.js"; 20import { E as qt } from "./eye-off-BaehqoVT.js"; 21import { C as yn, a as Ut, M as Wt } from "./minimize-2-Anr46dAf.js"; 22import { R as Ir } from "./refresh-cw-BT63URYv.js"; 23import { T as Mr } from "./trash-2-BsEMyM_9.js"; 24import { P as na } from "./plus-kvLLfmk2.js"; 25import { S as aa } from "./star-CzdypP67.js"; 26import { C as sa } from "./chevron-down-CCtAlXwd.js"; 27import "./floatingPosition-CEeNxCs-.js"; 28import "./index-W-g0kpIR.js"; 29import "./vendor-socket-CzTGKfeH.js"; 30import "./mobileOta-CYuBwxYO.js"; 31import "./grip-vertical-J9BXZkrt.js"; 32import "./pen-C3H1vK2L.js"; 33import "./highlighter-BdBX-8K9.js"; 34import "./undo-2-ovcv97Js.js"; 35import "./wifi-off-DcEnYTf9.js"; 36const oa = ` 37`, lr = "\\t\\n\\v\\f\\r \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff", ia = new RegExp(`^[${lr}]+|[${lr}]+$`, "g"), la = new RegExp(`[${lr}]+`, "g"); 38function De(n) { 39 return n.trim().toLowerCase(); 40} 41function vn(n) { 42 return n == null ? "" : n.normalize("NFC").replace(ia, "").replace(la, " ").toLowerCase(); 43} 44function cr(n) { 45 if (n == null) return []; 46 const s = []; 47 for (const l of n.split(oa)) { 48 const d = vn(l); 49 d !== "" && s.push(d); 50 } 51 return s; 52} 53function ca(n, s) { 54 const l = vn(s); 55 return l === "" ? false : cr(n).includes(l); 56} 57function Yt(n, s) { 58 const l = De(n.word), d = s.filter((u) => u.is_active !== false && De(u.word) === l); 59 if (d.length === 0) return { verdict: "new", matchedRow: null, wordMatches: [] }; 60 const f = new Set(cr(n.meaning)); 61 if (f.size > 0) { 62 for (const u of d) if (cr(u.meaning).some((i) => f.has(i))) return { verdict: "duplicate", matchedRow: u, wordMatches: d }; 63 } 64 return { verdict: "conflict", matchedRow: null, wordMatches: d }; 65} 66const Rr = "\uC608\uBB38\uC774 \uAC19\uC544 \uBCC4\uB3C4 \uD56D\uBAA9\uC73C\uB85C \uC800\uC7A5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uAE30\uC874 \uB2E8\uC5B4\uC5D0 \uD1B5\uD569\uD574 \uC8FC\uC138\uC694."; 67function Ar({ value: n }) { 68 return e.jsx("p", { className: "text-sm text-gray-800 whitespace-pre-line break-words", children: n }); 69} 70function wn({ prompt: n, remainingCount: s = 0, onMerge: l, onSeparate: d, onDismiss: f, onDiscard: u }) { 71 var h; 72 return a.useEffect(() => { 73 const i = (x) => { 74 x.key === "Escape" && f(); 75 }; 76 return window.addEventListener("keydown", i), () => window.removeEventListener("keydown", i); 77 }, [f]), e.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 px-4", onClick: f, children: e.jsxs("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": "duplicate-word-modal-title", className: "bg-white rounded-2xl shadow-xl w-full max-w-md max-h-[85vh] overflow-y-auto", onClick: (i) => i.stopPropagation(), children: [e.jsxs("div", { className: "flex items-start gap-3 px-5 pt-5 pb-3", children: [e.jsx("span", { className: "w-9 h-9 flex-none rounded-full bg-amber-50 flex items-center justify-center", children: e.jsx(Ke, { className: "w-[18px] h-[18px] text-amber-500" }) }), e.jsxs("div", { className: "min-w-0 flex-1", children: [e.jsx("h3", { id: "duplicate-word-modal-title", className: "font-semibold text-gray-900", children: "\uC774\uBBF8 \uC800\uC7A5\uB41C \uB2E8\uC5B4\uC785\uB2C8\uB2E4" }), e.jsxs("p", { className: "mt-0.5 text-sm text-gray-500 break-words", children: [e.jsx("span", { className: "font-semibold text-gray-700", children: n.word }), "\u2005", "\u2014 \uB73B\uC774 \uB2EC\uB77C \uC5B4\uB5BB\uAC8C \uC800\uC7A5\uD560\uC9C0 \uC120\uD0DD\uD574 \uC8FC\uC138\uC694."] })] }), e.jsx("button", { type: "button", "aria-label": "\uB2EB\uAE30", onClick: f, className: "p-1 -mr-1 rounded-lg hover:bg-gray-100 transition-colors flex-none", children: e.jsx(_t, { className: "w-4 h-4 text-gray-400" }) })] }), e.jsxs("div", { className: "px-5 pb-4 space-y-3", children: [e.jsxs("section", { children: [e.jsx("h4", { className: "text-xs font-semibold text-gray-400 mb-1.5", children: "\uB098\uC758 \uB2E8\uC5B4\uC7A5\uC5D0 \uC800\uC7A5\uB41C \uB73B" }), e.jsx("div", { className: "rounded-xl border border-gray-200 bg-gray-50/60 divide-y divide-gray-100", children: n.existingMeanings.map((i, x) => e.jsx("div", { className: "px-3 py-2", children: e.jsx(Ar, { value: i }) }, x)) })] }), e.jsxs("section", { children: [e.jsx("h4", { className: "text-xs font-semibold text-gray-400 mb-1.5", children: "\uCD94\uAC00\uD558\uB824\uB294 \uB73B" }), e.jsx("div", { className: "rounded-xl border border-primary/30 bg-primary/5 px-3 py-2", children: e.jsx(Ar, { value: (h = n.incomingMeaning) != null ? h : "-" }) })] }), !n.canAddSeparately && e.jsx("p", { className: "text-xs text-amber-600 bg-amber-50 rounded-lg px-3 py-2", children: Rr })] }), e.jsxs("div", { className: "px-5 pb-5 flex flex-col gap-2", children: [e.jsx("button", { type: "button", onClick: l, className: "w-full px-4 py-2.5 rounded-xl bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors", children: "\uAE30\uC874\uC5D0 \uD1B5\uD569" }), e.jsx("button", { type: "button", onClick: d, disabled: !n.canAddSeparately, title: n.canAddSeparately ? void 0 : Rr, className: "w-full px-4 py-2.5 rounded-xl border border-gray-200 text-gray-700 text-sm font-semibold hover:bg-gray-50 transition-colors disabled:opacity-40 disabled:hover:bg-white", children: "\uBCC4\uB3C4\uB85C \uCD94\uAC00" }), e.jsxs("div", { className: "flex items-center justify-between pt-0.5", children: [s > 0 ? e.jsxs("span", { className: "text-xs text-gray-400", children: [s, "\uAC1C \uB354 \uC120\uD0DD\uD574\uC57C \uD569\uB2C8\uB2E4"] }) : e.jsx("span", {}), u && e.jsx("button", { type: "button", onClick: u, className: "text-xs text-gray-400 hover:text-red-500 transition-colors", children: "\uC774 \uCD94\uAC00 \uCDE8\uC18C" })] })] })] }) }); 78} 79function Ft() { 80 try { 81 return typeof window < "u" && typeof window.speechSynthesis < "u" && typeof window.SpeechSynthesisUtterance < "u"; 82 } catch { 83 return false;
84 } 85} 86function Tr() { 87 return typeof window < "u" && typeof window.Audio < "u"; 88} 89const jn = ["Google US English", "Google UK English", "Samantha", "Daniel", "Karen"], Nn = ["Google \uD55C\uAD6D\uC758", "Yuna", "Sora"]; 90function dr(n, s) { 91 for (const l of s) { 92 const d = n.find((f) => f.name.includes(l)); 93 if (d) return d; 94 } 95 return n[0]; 96} 97function da(n, s) { 98 var l; 99 if (Je() || s != null && s.localService) return s; 100 const d = n.split("-")[0], f = window.speechSynthesis.getVoices().filter((h) => h.localService && h.lang.startsWith(d)); 101 return (l = dr(f, d === "ko" ? Nn : jn)) != null ? l : s; 102} 103function ua(n, s, l, d) { 104 return new Promise((f) => { 105 if (!Ft()) { 106 f(); 107 return; 108 } 109 try { 110 const u = new SpeechSynthesisUtterance(n); 111 u.lang = s, u.rate = l; 112 const h = da(s, d); 113 h && (u.voice = h), u.onend = () => f(), u.onerror = () => f(), window.speechSynthesis.speak(u); 114 } catch { 115 f(); 116 } 117 }); 118} 119const Dr = "vocabTTS_enVoice", Pr = "vocabTTS_koVoice"; 120function jr(n) { 121 const [s, l] = a.useState(false), [d, f] = a.useState(-1), u = a.useRef(false), h = a.useRef(0), i = a.useRef(null), x = a.useRef(null), m = a.useRef(null), b = a.useRef(null), g = a.useRef(n); 122 g.current = n; 123 const v = n.some((R) => R.source === "book" && R.id != null), j = Ft() || v && Tr(), [C, M] = a.useState([]), [o, p] = a.useState([]), [y, S] = a.useState(""), [_, E] = a.useState(""); 124 a.useEffect(() => { 125 if (!j) return; 126 const R = () => { 127 var D, K, ee, he; 128 try { 129 const te = window.speechSynthesis.getVoices(); 130 if (te.length === 0) return; 131 const z = te.filter((L) => L.lang.startsWith("en")), H = te.filter((L) => L.lang.startsWith("ko")); 132 M(z.map((L) => ({ name: L.name, voice: L }))), p(H.map((L) => ({ name: L.name, voice: L }))); 133 const U = localStorage.getItem(Dr), oe = localStorage.getItem(Pr), we = U && z.find((L) => L.name === U), le = oe && H.find((L) => L.name === oe); 134 S(we ? we.name : (K = (D = dr(z, jn)) == null ? void 0 : D.name) != null ? K : ""), E(le ? le.name : (he = (ee = dr(H, Nn)) == null ? void 0 : ee.name) != null ? he : ""); 135 } catch { 136 } 137 }; 138 R(); 139 try { 140 window.speechSynthesis.addEventListener("voiceschanged", R); 141 } catch { 142 return; 143 } 144 return () => { 145 try { 146 window.speechSynthesis.removeEventListener("voiceschanged", R); 147 } catch { 148 } 149 }; 150 }, [j]); 151 const T = a.useCallback((R) => { 152 S(R), localStorage.setItem(Dr, R); 153 }, []), P = a.useCallback((R) => { 154 E(R), localStorage.setItem(Pr, R); 155 }, []), V = a.useRef(void 0), xe = a.useRef(void 0); 156 a.useEffect(() => { 157 var R; 158 V.current = (R = C.find((D) => D.name === y)) == null ? void 0 : R.voice; 159 }, [C, y]), a.useEffect(() => { 160 var R; 161 xe.current = (R = o.find((D) => D.name === _)) == null ? void 0 : R.voice; 162 }, [o, _]); 163 const O = a.useCallback((R) => new Promise((D) => { 164 i.current = setTimeout(() => { 165 i.current = null, D(u.current); 166 }, R); 167 }), []), Y = a.useCallback(() => { 168 var R; 169 const D = x.current; 170 D && (D.onended = null, D.onerror = null, D.pause(), D.removeAttribute("src"), D.load(), x.current = null), m.current && (URL.revokeObjectURL(m.current), m.current = null), (R = b.current) == null || R.call(b, false), b.current = null; 171 }, []), A = a.useCallback(() => { 172 if (h.current += 1, u.current = false, Y(), Ft()) try { 173 window.speechSynthesis.cancel(); 174 } catch { 175 } 176 i.current && (clearTimeout(i.current), i.current = null), l(false), f(-1); 177 }, [Y]), ge = a.useCallback((R) => new Promise((D) => { 178 Y(); 179 const K = URL.createObjectURL(R), ee = new Audio(K); 180 x.current = ee, m.current = K, b.current = D; 181 const he = (te) => { 182 x.current === ee && (x.current = null), m.current === K && (URL.revokeObjectURL(K), m.current = null), b.current === D && (b.current = null), D(te); 183 }; 184 ee.onended = () => he(true), ee.onerror = () => he(false), ee.play().catch(() => he(false)); 185 }), [Y]), G = a.useCallback(async (R, D, K) => { 186 const ee = R[D], he = D === "meaning" ? "ko-KR" : "en-US", te = D === "meaning" ? 1 : 0.9, z = D === "meaning" ? xe.current : V.current; 187 if (ee && R.source === "book" && R.id != null && Je() && Tr()) try { 188 const H = await Oe.getBookEntryTtsAudio(R.id, D); 189 if (!u.current || h.current !== K || H.data.size > 0 && await ge(H.data)) return; 190 } catch { 191 } 192 u.current && h.current === K && ee && await ua(ee, he, te, z); 193 }, [ge]), ue = a.useCallback(async () => { 194 if (g.current.length === 0 || !j) return; 195 A(); 196 const R = h.current; 197 u.current = true, l(true); 198 for (let D = 0; D < g.current.length && !(!u.current || h.current !== R); D++) { 199 const K = g.current[D]; 200 if (K.word && (f(D), await G(K, "word", R), !u.current || h.current !== R || !await O(500) || K.meaning && (await G(K, "meaning", R), !u.current || h.current !== R) || D < g.current.length - 1 && !await O(800))) break; 201 } 202 h.current === R && (u.current = false, l(false), f(-1)); 203 }, [O, j, G, A]), F = a.useCallback(() => { 204 j && (u.current ? A() : ue()); 205 }, [j, ue, A]), Ne = a.useCallback((R) => { 206 const D = g.current[R]; 207 if (!(D != null && D.word) || !j) return; 208 if (u.current && d === R) { 209 A(); 210 return; 211 } 212 A(); 213 const K = h.current; 214 u.current = true, l(true), f(R), G(D, "word", K).finally(() => { 215 !u.current || h.current !== K || (u.current = false, l(false), f(-1)); 216 }); 217 }, [d, j, G, A]); 218 return a.useEffect(() => () => { 219 if (u.current = false, Y(), Ft()) try { 220 window.speechSynthesis.cancel(); 221 } catch { 222 } 223 i.current && clearTimeout(i.current); 224 }, [Y]), a.useEffect(() => { 225 s && A(); 226 }, [n]), { isSupported: j, isPlaying: s, currentIndex: d, toggleTTS: F, stopTTS: A, playItem: Ne, enVoices: C, koVoices: o, selectedEnVoice: y, selectedKoVoice: _, setSelectedEnVoice: T, setSelectedKoVoice: P }; 227} 228function kt(n) { 229 var s; 230 return (s = n.memorization_status) != null ? s : "default"; 231} 232function Or(n, s, l) { 233 n.setQueryData(Me, (d) => { 234 if (!d) return d; 235 let f = false;
236 const u = d.items.map((h) => { 237 const i = s.get(h.id); 238 return !i || l && kt(h) !== l || kt(h) === i ? h : (f = true, { ...h, memorization_status: i }); 239 }); 240 return f ? { ...d, items: u } : d; 241 }); 242} 243function Br(n, s, l, d) { 244 n.setQueryData(["vocabulary", "book-entries", s], (f) => { 245 var u; 246 if (!((u = f == null ? void 0 : f.data) != null && u.entries)) return f; 247 let h = false; 248 const i = f.data.entries.map((x) => { 249 const m = l.get(x.id); 250 return !m || d && kt(x) !== d || kt(x) === m ? x : (h = true, { ...x, memorization_status: m }); 251 }); 252 return h ? { ...f, data: { ...f.data, entries: i } } : f; 253 }); 254} 255class Vr extends Error { 256 persistedEntryIds; 257 constructor(s, l) { 258 super(s instanceof Error && s.message ? s.message : "\uB2E8\uC5B4\uB97C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."), this.name = "MemorizationStatusBatchError", this.persistedEntryIds = l; 259 } 260} 261function kn(n) { 262 const s = Xt(), l = a.useRef(/* @__PURE__ */ new Map()), d = a.useRef(/* @__PURE__ */ new Map()), f = a.useRef(() => { 263 }), u = a.useRef(async () => { 264 }), h = a.useRef(0), i = a.useRef(/* @__PURE__ */ new Map()); 265 a.useEffect(() => { 266 let g = false, v = 0, j = false; 267 l.current = /* @__PURE__ */ new Map(), d.current = /* @__PURE__ */ new Map(); 268 const C = () => { 269 if (g || j) return; 270 const y = new Map(l.current); 271 d.current.forEach(({ status: S }, _) => { 272 y.set(_, S); 273 }), y.size !== 0 && (j = true, n.targetType === "personal" ? Or(s, y) : n.bookId != null && Br(s, n.bookId, y), j = false); 274 }, M = async () => { 275 const y = ++v; 276 try { 277 const S = await ln(n.academyId, n.userId); 278 if (g || y !== v) return; 279 const _ = /* @__PURE__ */ new Map(); 280 for (const E of S) { 281 if (E.kind !== "vocab_memorization_status") continue; 282 const T = E.payload; 283 T.targetType === n.targetType && _.set(T.entryId, T.status); 284 } 285 l.current = _, C(); 286 } catch { 287 } 288 }; 289 f.current = C, u.current = M; 290 const o = on(() => { 291 M(); 292 }), p = s.getQueryCache().subscribe((y) => { 293 if (j || l.current.size === 0 && d.current.size === 0) return; 294 const S = y.query.queryKey; 295 (n.targetType === "personal" ? S.length === Me.length && S.every((E, T) => E === Me[T]) : S[0] === "vocabulary" && S[1] === "book-entries" && S[2] === n.bookId) && C(); 296 }); 297 return M(), () => { 298 g = true, f.current = () => { 299 }, u.current = async () => { 300 }, o(), p(); 301 }; 302 }, [n.academyId, n.bookId, n.targetType, n.userId, s]); 303 const x = (g) => { 304 var v, j, C; 305 const M = new Set(g), o = /* @__PURE__ */ new Map(); 306 if (n.targetType === "personal") { 307 const y = s.getQueryData(Me); 308 for (const S of (v = y == null ? void 0 : y.items) != null ? v : []) M.has(S.id) && o.set(S.id, kt(S)); 309 return o; 310 } 311 const p = s.getQueryData(["vocabulary", "book-entries", n.bookId]); 312 for (const y of (C = (j = p == null ? void 0 : p.data) == null ? void 0 : j.entries) != null ? C : []) M.has(y.id) && o.set(y.id, kt(y)); 313 return o; 314 }, m = (g, v) => { 315 if (n.targetType === "personal") { 316 Or(s, g, v); 317 return; 318 } 319 n.bookId != null && Br(s, n.bookId, g, v); 320 }, b = Dt({ networkMode: "offlineFirst", mutationFn: async ({ entryIds: g, status: v, intentId: j }) => { 321 const C = Array.from(new Set(g)); 322 if (C.length === 0) throw new Error("memorization_entry_ids_required"); 323 const M = []; 324 try { 325 return await sr(async () => { 326 var o; 327 const p = C.filter((P) => i.current.get(P) === j); 328 if (p.length === 0) return { queued: false }; 329 if (n.targetType === "personal" && p.some((P) => P <= 0)) throw new Error("memorization_entry_not_synced"); 330 let y = [], S = false; 331 try { 332 y = (o = await Vn({ academyId: n.academyId, userId: n.userId, deviceId: n.deviceId, targetType: n.targetType, entryIds: p, status: v })) != null ? o : [], S = true; 333 } catch (P) { 334 if (!Je()) throw P; 335 } 336 if (!Je()) return { queued: S }; 337 let _ = y; 338 if (_.length > 0) try { 339 _ = await $n(_); 340 } catch { 341 } 342 const E = new Map(_.map((P) => [P.payload.entryId, P])), T = y.length > 0 ? p.filter((P) => E.has(P)) : p; 343 try { 344 for (let P = 0; P < T.length; P += Sr) { 345 const V = T.slice(P, P + Sr); 346 await Oe.updateMemorizationStatus(n.targetType, V, v), M.push(...V); 347 const xe = V.map((O) => E.get(O)).filter((O) => O != null); 348 await zn(xe); 349 } 350 } catch (P) { 351 if (S) return { queued: true }; 352 throw P; 353 } 354 return { queued: false }; 355 }); 356 } catch (o) { 357 throw new Vr(o, M); 358 } 359 }, onMutate: async ({ entryIds: g, status: v, intentId: j }) => { 360 const C = Array.from(new Set(g)), M = n.targetType === "personal" ? Me : ["vocabulary", "book-entries", n.bookId]; 361 await s.cancelQueries({ queryKey: M, exact: true }); 362 const o = x(C);
363 C.forEach((y) => { 364 var S; 365 const _ = (S = i.current.get(y)) != null ? S : 0; 366 j > _ && i.current.set(y, j); 367 }); 368 const p = C.filter((y) => i.current.get(y) === j); 369 return p.forEach((y) => { 370 d.current.set(y, { intentId: j, status: v }); 371 }), m(new Map(p.map((y) => [y, v]))), { previousStatuses: o }; 372 }, onSuccess: async (g, v) => { 373 const j = n.targetType === "personal" ? Me : ["vocabulary", "book-entries", n.bookId]; 374 await s.cancelQueries({ queryKey: j, exact: true }), await u.current(), v.entryIds.forEach((M) => { 375 var o; 376 ((o = d.current.get(M)) == null ? void 0 : o.intentId) === v.intentId && d.current.delete(M); 377 }); 378 const C = v.entryIds.filter((M) => i.current.get(M) === v.intentId); 379 m(new Map(C.map((M) => [M, v.status]))), f.current(); 380 }, onError: async (g, v, j) => { 381 if (!j) return; 382 const C = n.targetType === "personal" ? Me : ["vocabulary", "book-entries", n.bookId]; 383 await s.cancelQueries({ queryKey: C, exact: true }), await u.current(), v.entryIds.forEach((y) => { 384 var S; 385 ((S = d.current.get(y)) == null ? void 0 : S.intentId) === v.intentId && d.current.delete(y); 386 }); 387 const M = new Set(g instanceof Vr ? g.persistedEntryIds : []), o = new Map([...j.previousStatuses].filter(([y]) => !M.has(y) && i.current.get(y) === v.intentId)); 388 m(o, v.status); 389 const p = [...M].filter((y) => i.current.get(y) === v.intentId); 390 p.length > 0 && m(new Map(p.map((y) => [y, v.status]))), f.current(); 391 } }); 392 return { moveEntries: (g, v) => (h.current += 1, b.mutateAsync({ entryIds: g, status: v, intentId: h.current }).then(() => { 393 })), isPending: b.isPending, error: b.error }; 394} 395const ut = { default: "\uAE30\uBCF8", memorized: "\uC554\uAE30\uB428", known: "\uC544\uB294 \uB2E8\uC5B4" }; 396function ma(n) { 397 return n.reduce((s, l) => (s[l.memorization_status] += 1, s), { default: 0, memorized: 0, known: 0 }); 398} 399function ur(n, s) { 400 return n.filter((l) => l.memorization_status === s); 401} 402function Sn(n, s = Math.random) { 403 const l = [...n]; 404 for (let d = l.length - 1; d > 0; d -= 1) { 405 const f = Math.floor(s() * (d + 1)); 406 [l[d], l[f]] = [l[f], l[d]]; 407 } 408 return l; 409} 410function fa(n, s, l = Math.random) { 411 const d = Math.min(Math.max(Math.trunc(s), 0), n.length); 412 return Sn(n, l).slice(0, d); 413} 414function $r(n, s) { 415 return n instanceof Error && n.message.trim() ? n.message : s; 416} 417const xa = ["button:not([disabled])", "input:not([disabled])", "select:not([disabled])", "textarea:not([disabled])", "a[href]", '[tabindex]:not([tabindex="-1"])'].join(","); 418function zr(n) { 419 return [...n.querySelectorAll(xa)].filter((s) => !s.hidden && s.getAttribute("aria-hidden") !== "true"); 420} 421function Lr(n) { 422 return !!(n != null && n.isConnected && !n.matches(':disabled, [aria-disabled="true"]') && !n.closest('[inert], [aria-hidden="true"]')); 423} 424function _n(n, s, l, d) { 425 a.useEffect(() => { 426 var f; 427 if (!n) return; 428 const u = document.activeElement instanceof HTMLElement ? document.activeElement : null, h = (f = d == null ? void 0 : d.current) != null ? f : null, i = document.body.style.overflow; 429 document.body.style.overflow = "hidden"; 430 const x = requestAnimationFrame(() => { 431 var b; 432 const g = s.current; 433 if (!g) return; 434 const v = l == null ? void 0 : l.current; 435 ((b = v != null ? v : zr(g)[0]) != null ? b : g).focus(); 436 }), m = (b) => { 437 if (b.key !== "Tab") return; 438 const g = s.current; 439 if (!g) return; 440 const v = zr(g); 441 if (v.length === 0) { 442 b.preventDefault(), g.focus(); 443 return; 444 } 445 const j = v[0], C = v[v.length - 1], M = document.activeElement; 446 g.contains(M) ? b.shiftKey && M === j ? (b.preventDefault(), C.focus()) : !b.shiftKey && M === C && (b.preventDefault(), j.focus()) : (b.preventDefault(), (b.shiftKey ? C : j).focus()); 447 }; 448 return document.addEventListener("keydown", m, true), () => { 449 cancelAnimationFrame(x), document.removeEventListener("keydown", m, true), document.body.style.overflow = i; 450 const b = Lr(u) ? u : Lr(h) ? h : null;
451 b == null || b.focus({ preventScroll: true }); 452 }; 453 }, [s, l, n, d]); 454} 455const ga = 10, ha = 72, pa = 88, Fr = 180, Hr = 220, Kr = 80, qr = 180; 456function ba(n) { 457 return n === "default" ? "memorized" : "known"; 458} 459function zt() { 460 return new Promise((n) => { 461 requestAnimationFrame(() => requestAnimationFrame(() => n())); 462 }); 463} 464function ya({ open: n, sourceStatus: s, entries: l, onMoveEntries: d, onClose: f, random: u = Math.random, returnFocusRef: h, pronunciationSource: i }) { 465 var x, m; 466 const b = ur(l, s), [g, v] = a.useState(b), [j, C] = a.useState(0), [M, o] = a.useState(false), [p, y] = a.useState(false), [S, _] = a.useState({ x: 0, y: 0 }), [E, T] = a.useState(null), [P, V] = a.useState(false), [xe, O] = a.useState(null), [Y, A] = a.useState(false), [ge, G] = a.useState(false), [ue, F] = a.useState([]), [Ne, R] = a.useState(null), [D, K] = a.useState(""), ee = a.useRef(null), he = a.useRef(false), te = a.useRef(false), z = a.useRef(false), H = a.useRef(null), U = a.useRef(null), oe = a.useRef(false), we = a.useRef(s), le = a.useRef(null); 467 _n(n, le, void 0, h), a.useEffect(() => { 468 n && (!oe.current || we.current !== s) && (v(ur(l, s)), C(0), o(false), _({ x: 0, y: 0 }), T(null), V(false), O(null), G(false), F([]), R(null), K(""), z.current = false, U.current = null, we.current = s), n || (ee.current = null, y(false), T(null), V(false), z.current = false), oe.current = n; 469 }, [l, n, s]); 470 const L = g[j], Ee = a.useMemo(() => g.map((k) => { 471 var I; 472 return { id: k.id, source: i, word: k.word, meaning: (I = k.meaning) != null ? I : "" }; 473 }), [g, i]), { isSupported: Be, isPlaying: pe, currentIndex: Ie, playItem: me, stopTTS: Le } = jr(Ee), Ve = ba(s), qe = s === "default" ? "\uC554\uAE30\uC644\uB8CC" : "\uC544\uB294 \uB2E8\uC5B4\uB85C \uBCF4\uB0B4\uAE30", se = Y || ge || E !== null || P, _e = ue[ue.length - 1], re = !!(U.current && U.current.record.entry.id === (_e == null ? void 0 : _e.entry.id) && U.current.record.targetStatus === (_e == null ? void 0 : _e.targetStatus)), et = !!(_e && !ge && E === null && !P && (!te.current || re)), Ye = () => { 474 o(false), _({ x: 0, y: 0 }), T(null), V(false), O(null), z.current = false; 475 }, tt = (k) => { 476 if (Y || z.current || E) return; 477 pe && Le(); 478 const I = k === "next" ? Math.min(g.length - 1, j + 1) : Math.max(0, j - 1); 479 if (I === j) { 480 _({ x: 0, y: 0 }); 481 return; 482 } 483 z.current = true, _({ x: 0, y: 0 }), O(null), R(null), T({ direction: k, phase: "exit", targetIndex: I }); 484 }, be = () => tt("previous"), fe = () => { 485 tt("next"); 486 }, Fe = a.useCallback(() => { 487 if (E) { 488 if (E.phase === "exit") { 489 C(E.targetIndex), o(false), _({ x: 0, y: 0 }), T({ ...E, phase: "prepare-enter" }); 490 return; 491 } 492 E.phase === "enter" && (T(null), z.current = false); 493 } 494 }, [E]); 495 a.useEffect(() => { 496 if (!E) return; 497 if (E.phase === "prepare-enter") { 498 let W = 0; 499 const Z = requestAnimationFrame(() => { 500 W = requestAnimationFrame(() => { 501 T((ye) => (ye == null ? void 0 : ye.phase) === "prepare-enter" ? { ...ye, phase: "enter" } : ye); 502 }); 503 }); 504 return () => { 505 cancelAnimationFrame(Z), W && cancelAnimationFrame(W); 506 }; 507 } 508 const k = E.phase === "exit" ? Fr : Hr, I = window.setTimeout(Fe, k + Kr); 509 return () => window.clearTimeout(I); 510 }, [Fe, E]); 511 const ae = a.useCallback(() => new Promise((k) => { 512 let I = 0; 513 const W = () => { 514 H.current === W && (H.current = null, window.clearTimeout(I), k()); 515 }; 516 H.current = W, I = window.setTimeout(W, qr + Kr); 517 }), []), He = a.useCallback(() => { 518 var k; 519 (k = H.current) == null || k.call(H); 520 }, []), Re = async (k, I) => { 521 if (te.current || z.current || !L) return; 522 pe && Le(), te.current = true; 523 const W = L.id, Z = g.length, ye = { entry: L, sourceStatus: s, index: j, targetStatus: k }; 524 A(true), R(null), O(I), _({ x: 0, y: 0 }); 525 const ce = ae(), $e = d([W], k).then(() => ({ ok: true }), (ke) => ({ ok: false, error: ke })), X = { record: ye, persistence: $e, undoRequested: false }; 526 U.current = X; 527 let q = false;
528 try { 529 await ce, q = true, V(true), F((ne) => [...ne, ye]), v((ne) => ne.filter((ve) => ve.id !== W)), C((ne) => { 530 const ve = Math.max(0, Z - 1); 531 return ve === 0 ? 0 : Math.min(ne, ve - 1); 532 }), o(false), O(null), await zt(), V(false); 533 const ke = await $e; 534 if (!ke.ok) { 535 if (X.undoRequested) return; 536 throw ke.error; 537 } 538 X.undoRequested || K(`${L.word}\uC744 ${ut[k]} \uD3F4\uB354\uB85C \uC62E\uACBC\uC2B5\uB2C8\uB2E4.`); 539 } catch (ke) { 540 if (X.undoRequested) return; 541 He(), V(true), q && (F((ne) => { 542 var ve; 543 return ((ve = ne[ne.length - 1]) == null ? void 0 : ve.entry.id) === W ? ne.slice(0, -1) : ne; 544 }), v((ne) => { 545 if (ne.some((Xe) => Xe.id === W)) return ne; 546 const ve = [...ne]; 547 return ve.splice(Math.min(j, ve.length), 0, L), ve; 548 }), C(Math.min(j, Z - 1)), o(false), _({ x: 0, y: 0 })), O(null), R($r(ke, "\uB2E8\uC5B4\uB97C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.")), await zt(), V(false); 549 } finally { 550 X.undoRequested || (U.current === X && (U.current = null), te.current = false), A(false); 551 } 552 }, rt = async () => { 553 const k = ue[ue.length - 1], I = U.current, W = !!(I && I.record.entry.id === (k == null ? void 0 : k.entry.id) && I.record.targetStatus === (k == null ? void 0 : k.targetStatus) && !I.undoRequested); 554 if (!k || z.current || E !== null || P || ge || te.current && !W) return; 555 I && W ? I.undoRequested = true : te.current = true; 556 const Z = g, ye = j, ce = M, $e = Math.min(k.index, g.length); 557 G(true), R(null); 558 try { 559 if (V(true), v((X) => { 560 if (X.some((ke) => ke.id === k.entry.id)) return X; 561 const q = [...X]; 562 return q.splice(Math.min(k.index, q.length), 0, { ...k.entry, memorization_status: k.sourceStatus }), q; 563 }), C($e), o(false), _({ x: 0, y: 0 }), O(null), F((X) => { 564 var q; 565 return ((q = X[X.length - 1]) == null ? void 0 : q.entry.id) === k.entry.id ? X.slice(0, -1) : X; 566 }), await zt(), V(false), I && W) { 567 if ((await I.persistence).ok) { 568 const q = await d([k.entry.id], k.sourceStatus).then(() => ({ ok: true }), (ke) => ({ ok: false, error: ke })); 569 if (!q.ok) throw q.error; 570 } 571 } else { 572 const X = await d([k.entry.id], k.sourceStatus).then(() => ({ ok: true }), (q) => ({ ok: false, error: q })); 573 if (!X.ok) throw X.error; 574 } 575 K(`${k.entry.word}\uC758 ${ut[k.targetStatus]} \uD3F4\uB354 \uC774\uB3D9\uC744 \uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4.`); 576 } catch (X) { 577 V(true), v(Z), C(ye), o(ce), _({ x: 0, y: 0 }), O(null), F(ue), R($r(X, "\uD3F4\uB354 \uC774\uB3D9\uC744 \uCDE8\uC18C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.")), await zt(), V(false); 578 } finally { 579 U.current === I && (U.current = null), te.current = false, A(false), G(false); 580 } 581 }; 582 if (a.useEffect(() => { 583 if (!n) return; 584 const k = (I) => { 585 if (I.key === "Escape") { 586 se || (I.preventDefault(), f()); 587 return; 588 } 589 !L || se || (I.key === "ArrowLeft" ? (I.preventDefault(), be()) : I.key === "ArrowRight" ? (I.preventDefault(), fe()) : I.key === "ArrowUp" ? (I.preventDefault(), Re(Ve, "up")) : I.key === "ArrowDown" && s === "memorized" && (I.preventDefault(), Re("default", "down"))); 590 }; 591 return window.addEventListener("keydown", k), () => window.removeEventListener("keydown", k); 592 }), !n) return null; 593 const Ae = (k) => { 594 if (!(se || z.current || k.isPrimary === false || k.pointerType === "mouse" && k.button !== 0)) { 595 ee.current = { pointerId: k.pointerId, startX: k.clientX, startY: k.clientY, axis: "pending" }, y(true);
596 try { 597 k.currentTarget.setPointerCapture(k.pointerId); 598 } catch { 599 } 600 } 601 }, mt = (k) => { 602 const I = ee.current; 603 if (!I || I.pointerId !== k.pointerId) return; 604 const W = k.clientX - I.startX, Z = k.clientY - I.startY; 605 if (I.axis === "pending") { 606 if (Math.max(Math.abs(W), Math.abs(Z)) < ga) return; 607 I.axis = Math.abs(W) >= Math.abs(Z) ? "horizontal" : "vertical"; 608 } 609 if (k.preventDefault(), I.axis === "horizontal") _({ x: W, y: 0 }); 610 else { 611 const ye = s === "default" && Z > 0 ? Math.min(Z * 0.18, 28) : Z; 612 _({ x: 0, y: ye }); 613 } 614 }, nt = (k, I) => { 615 const W = ee.current; 616 if (!W || W.pointerId !== k.pointerId) return; 617 const Z = k.clientX - W.startX, ye = k.clientY - W.startY, ce = W.axis; 618 try { 619 k.currentTarget.hasPointerCapture(k.pointerId) && k.currentTarget.releasePointerCapture(k.pointerId); 620 } catch { 621 } 622 if (ee.current = null, y(false), I) { 623 _({ x: 0, y: 0 }); 624 return; 625 } 626 if (ce !== "pending" && (he.current = true, window.setTimeout(() => { 627 he.current = false; 628 }, 0)), ce === "horizontal" && Math.abs(Z) >= ha) { 629 Z < 0 ? fe() : be(); 630 return; 631 } 632 if (ce === "vertical" && Math.abs(ye) >= pa) { 633 if (ye < 0) { 634 Re(Ve, "up"); 635 return; 636 } 637 if (s === "memorized") { 638 Re("default", "down"); 639 return; 640 } 641 } 642 _({ x: 0, y: 0 }); 643 }, at = () => { 644 se || g.length < 2 || (pe && Le(), v((k) => Sn(k, u)), C(0), Ye(), R(null), K("\uB0A8\uC740 \uCE74\uB4DC \uC21C\uC11C\uB97C \uC11E\uC5C8\uC2B5\uB2C8\uB2E4.")); 645 }, Qe = E ? E.phase === "exit" ? E.direction === "next" ? "translate3d(-120vw, 0, 0) rotate(-8deg)" : "translate3d(120vw, 0, 0) rotate(8deg)" : E.phase === "prepare-enter" ? E.direction === "next" ? "translate3d(120vw, 0, 0) rotate(6deg)" : "translate3d(-120vw, 0, 0) rotate(-6deg)" : "translate3d(0px, 0px, 0) rotate(0deg)" : null, ft = Qe != null ? Qe : xe === "up" ? "translate3d(0, -130vh, 0) rotate(-4deg)" : xe === "down" ? "translate3d(0, 130vh, 0) rotate(4deg)" : `translate3d(${S.x}px, ${S.y}px, 0) rotate(${S.x / 28}deg)`, st = p || P || (E == null ? void 0 : E.phase) === "prepare-enter" ? "none" : (E == null ? void 0 : E.phase) === "exit" ? `transform ${Fr}ms cubic-bezier(0.4, 0, 1, 1)` : (E == null ? void 0 : E.phase) === "enter" ? `transform ${Hr}ms cubic-bezier(0.22, 1, 0.36, 1)` : xe ? `transform ${qr}ms cubic-bezier(0.4, 0, 1, 1)` : "transform 180ms ease-out", xt = { transform: ft, transition: st, touchAction: "none" }; 646 return fr.createPortal(e.jsxs("div", { ref: le, tabIndex: -1, className: "fixed inset-0 z-[100] flex flex-col overflow-hidden bg-slate-950 text-white", role: "dialog", "aria-modal": "true", "aria-labelledby": "flashcard-deck-title", "aria-busy": se, children: [e.jsxs("header", { className: "flex items-center gap-3 pb-3 pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] pt-[max(0.75rem,env(safe-area-inset-top))]", children: [e.jsxs("div", { className: "min-w-0 flex-1", children: [e.jsxs("h2", { id: "flashcard-deck-title", className: "truncate text-base font-bold", children: [ut[s], " \uC554\uAE30\uCE74\uB4DC"] }), e.jsx("p", { className: "mt-0.5 text-xs text-white/55", children: g.length > 0 ? `${j + 1} / ${g.length}` : "\uC138\uC158 \uC644\uB8CC" })] }), e.jsxs("button", { type: "button", onClick: at, disabled: se || g.length < 2, className: "flex min-h-10 items-center gap-1.5 rounded-xl border border-white/15 bg-white/10 px-3 text-sm font-semibold text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-35", children: [e.jsx(Pt, { className: "h-4 w-4", "aria-hidden": "true" }), "\uC11E\uAE30"] }), e.jsx("button", { type: "button", "aria-label": "\uC554\uAE30\uCE74\uB4DC \uB2EB\uAE30", disabled: se, onClick: f, className: "rounded-xl p-2.5 text-white/70 transition-colors hover:bg-white/10 hover:text-white focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-35", children: e.jsx(_t, { className: "h-5 w-5", "aria-hidden": "true" }) })] }), e.jsx("p", { className: "sr-only", "aria-live": "polite", children: D }), g.length === 0 ? e.jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center px-6 text-center", children: [e.jsx(Jn, { className: "h-16 w-16 text-emerald-400", "aria-hidden": "true" }), e.jsx("h3", { className: "mt-5 text-2xl font-bold", children: "\uC774\uBC88 \uC554\uAE30\uB97C \uC644\uB8CC\uD588\uC5B4\uC694" }), e.jsx("p", { className: "mt-2 text-sm text-white/60", children: "\uC774\uB3D9\uD55C \uB2E8\uC5B4\uB294 \uAC01 \uD3F4\uB354\uC5D0\uC11C \uB2E4\uC2DC \uD655\uC778\uD560 \uC218 \uC788\uC5B4\uC694." }), Ne && e.jsx("p", { role: "alert", className: "mt-3 text-sm text-red-300", children: Ne }), e.jsxs("div", { className: "mt-7 flex items-center gap-2", children: [e.jsx("button", { type: "button", onClick: () => { 647 rt(); 648 }, disabled: !et, className: "flex h-12 shrink-0 items-center justify-center gap-1.5 rounded-xl border border-white/20 bg-white/10 px-4 text-xs font-semibold text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-35", children: "\uCDE8\uC18C" }), e.jsx("button", { type: "button", disabled: se, onClick: f, className: "h-12 rounded-xl bg-white px-7 text-sm font-bold text-slate-950 transition-colors hover:bg-white/90 focus:outline-none focus:ring-2 focus:ring-white/60 disabled:opacity-40", children: "\uD3F4\uB354\uB85C \uB3CC\uC544\uAC00\uAE30" })] })] }) : e.jsxs(e.Fragment, { children: [e.jsxs("main", { className: "flex min-h-0 flex-1 flex-col pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))]", children: [e.jsxs("div", { className: "flex h-10 shrink-0 items-center justify-center text-sm font-semibold text-emerald-300", children: [e.jsx(ea, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }), qe] }), e.jsx("div", { className: "relative mx-auto flex min-h-0 w-full max-w-lg flex-1 items-center justify-center py-2 [perspective:1200px]", children: e.jsxs("div", { "data-testid": "flashcard-gesture-surface", onPointerDown: Ae, onPointerMove: mt, onPointerUp: (k) => nt(k, false), onPointerCancel: (k) => nt(k, true), onTransitionEnd: (k) => { 649 k.target !== k.currentTarget || k.propertyName !== "transform" || (E ? Fe() : xe && He()); 650 }, className: "relative aspect-[4/5] max-h-full w-full max-w-[25rem] select-none rounded-[2rem]", style: xt, children: [e.jsx("button", { type: "button", "aria-label": M ? `${L == null ? void 0 : L.word} \uB73B \uCE74\uB4DC. \uB73B: ${((x = L == null ? void 0 : L.meaning) == null ? void 0 : x.trim()) || "\uB73B \uC815\uBCF4 \uC5C6\uC74C"}. \uC55E\uBA74\uC744 \uBCF4\uB824\uBA74 \uB204\uB974\uC138\uC694.` : `${L == null ? void 0 : L.word} \uB2E8\uC5B4 \uCE74\uB4DC. \uB73B\uC744 \uBCF4\uB824\uBA74 \uB204\uB974\uC138\uC694.`, disabled: se, onClick: (k) => { 651 if (he.current) { 652 k.preventDefault(); 653 return; 654 } 655 o((I) => !I), R(null); 656 }, className: "absolute inset-0 rounded-[2rem] text-left outline-none focus-visible:ring-4 focus-visible:ring-primary/60 disabled:cursor-wait", children: e.jsxs("span", { className: "absolute inset-0 block transition-transform duration-500 [transform-style:preserve-3d]", style: { transform: M ? "rotateY(180deg)" : "rotateY(0deg)" }, children: [e.jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center rounded-[2rem] border border-white/20 bg-gradient-to-br from-white to-slate-100 px-6 text-center text-slate-950 shadow-2xl [backface-visibility:hidden]", style: { WebkitBackfaceVisibility: "hidden" }, children: [e.jsx("span", { className: "break-words text-4xl font-extrabold tracking-tight sm:text-5xl", children: L == null ? void 0 : L.word }), e.jsx("span", { className: "absolute bottom-6 text-xs font-medium text-slate-400", children: "\uD0ED\uD558\uBA74 \uB73B\uC774 \uBCF4\uC5EC\uC694" })] }), e.jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center rounded-[2rem] border border-primary/30 bg-gradient-to-br from-primary to-indigo-700 px-6 text-center text-white shadow-2xl [backface-visibility:hidden]", style: { transform: "rotateY(180deg)", WebkitBackfaceVisibility: "hidden" }, children: [e.jsx("span", { className: "whitespace-pre-line break-words text-2xl font-bold leading-relaxed sm:text-3xl", children: ((m = L == null ? void 0 : L.meaning) == null ? void 0 : m.trim()) || "\uB73B \uC815\uBCF4 \uC5C6\uC74C" }), e.jsx("span", { className: "absolute bottom-6 text-xs font-medium text-white/60", children: "\uD0ED\uD558\uBA74 \uB2E8\uC5B4\uB85C \uB3CC\uC544\uAC00\uC694" })] })] }) }), e.jsx("button", { type: "button", "aria-label": pe && Ie === j ? `${L == null ? void 0 : L.word} \uBC1C\uC74C \uC911\uC9C0` : `${L == null ? void 0 : L.word} \uBC1C\uC74C \uC7AC\uC0DD`, title: Be ? "\uBC1C\uC74C \uB4E3\uAE30" : "\uC774 \uAE30\uAE30\uC5D0\uC11C\uB294 \uBC1C\uC74C\uC744 \uC7AC\uC0DD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", disabled: !Be || se, onPointerDown: (k) => k.stopPropagation(), onClick: (k) => { 657 k.stopPropagation(), me(j); 658 }, className: "absolute right-5 top-5 z-10 inline-flex h-11 w-11 items-center justify-center rounded-full border border-slate-200/80 bg-white/90 text-slate-600 shadow-md backdrop-blur-sm transition-colors hover:bg-white hover:text-primary focus:outline-none focus:ring-4 focus:ring-primary/30 disabled:opacity-40", children: pe && Ie === j ? e.jsx(Ot, { className: "h-5 w-5", "aria-hidden": "true" }) : e.jsx(Nt, { className: "h-5 w-5", "aria-hidden": "true" }) })] }) }), e.jsx("div", { className: "flex h-10 shrink-0 items-center justify-center text-sm font-semibold text-amber-300", children: s === "memorized" && e.jsxs(e.Fragment, { children: [e.jsx(Er, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }), "\uAE30\uBCF8 \uD3F4\uB354\uB85C \uBCF4\uB0B4\uAE30"] }) })] }), e.jsxs("footer", { className: "shrink-0 border-t border-white/10 bg-slate-950/95 pb-[max(0.75rem,env(safe-area-inset-bottom))] pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] pt-3", children: [Ne && e.jsx("p", { role: "alert", className: "mx-auto mb-2 max-w-lg text-center text-sm text-red-300", children: Ne }), e.jsxs("div", { className: "mx-auto grid max-w-lg grid-cols-3 gap-2", children: [e.jsx("button", { type: "button", "aria-label": "\uC774\uC804 \uCE74\uB4DC", disabled: se || j === 0, onClick: be, className: "flex h-11 items-center justify-center rounded-xl border border-white/15 bg-white/10 text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-30", children: e.jsx(wr, { className: "h-5 w-5", "aria-hidden": "true" }) }), e.jsxs("button", { type: "button", disabled: se, onClick: () => { 659 o((k) => !k), R(null); 660 }, className: "flex h-11 items-center justify-center gap-1.5 rounded-xl border border-white/15 bg-white/10 px-2 text-xs font-semibold text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-30", children: [e.jsx(ta, { className: "h-4 w-4", "aria-hidden": "true" }), "\uCE74\uB4DC \uB4A4\uC9D1\uAE30"] }), e.jsx("button", { type: "button", "aria-label": "\uB2E4\uC74C \uCE74\uB4DC", disabled: se || j >= g.length - 1, onClick: fe, className: "flex h-11 items-center justify-center rounded-xl border border-white/15 bg-white/10 text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-30", children: e.jsx(Ln, { className: "h-5 w-5", "aria-hidden": "true" }) })] }), e.jsxs("div", { className: "mx-auto mt-2 flex max-w-lg gap-2", children: [s === "memorized" && e.jsxs("button", { type: "button", disabled: se, onClick: () => { 661 Re("default", "down"); 662 }, className: "flex h-11 min-w-0 flex-1 items-center justify-center gap-1.5 rounded-xl bg-amber-400 px-3 text-sm font-bold text-slate-950 transition-colors hover:bg-amber-300 focus:outline-none focus:ring-2 focus:ring-amber-300 disabled:opacity-40", children: [e.jsx(Er, { className: "h-4 w-4", "aria-hidden": "true" }), "\uAE30\uBCF8 \uD3F4\uB354\uB85C"] }), e.jsx("button", { type: "button", onClick: () => { 663 rt(); 664 }, disabled: !et, className: "flex h-11 shrink-0 items-center justify-center gap-1.5 rounded-xl border border-white/20 bg-white/10 px-3 text-xs font-semibold text-white transition-colors hover:bg-white/15 focus:outline-none focus:ring-2 focus:ring-white/40 disabled:opacity-35", children: "\uCDE8\uC18C" }), e.jsx("button", { type: "button", disabled: se, onClick: () => { 665 Re(Ve, "up"); 666 }, className: "flex h-11 min-w-0 flex-1 items-center justify-center rounded-xl bg-emerald-400 px-3 text-sm font-bold text-slate-950 transition-colors hover:bg-emerald-300 focus:outline-none focus:ring-2 focus:ring-emerald-300 disabled:opacity-40", children: qe })] })] })] })] }), document.body); 667} 668function va({ status: n, entryCount: s, onOpenCards: l, disabled: d = false, className: f = "" }) { 669 if (n === "known") return null; 670 const u = d || s === 0; 671 return e.jsx("div", { className: f, children: e.jsxs("button", { type: "button", onClick: l, disabled: u, className: "inline-flex h-8 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-lg bg-primary px-2.5 text-xs font-semibold text-white transition-colors hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary/40 focu
671s:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-45", children: [e.jsx(ra, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), "\uC554\uAE30\uCE74\uB4DC"] }) }); 672} 673const bt = ["default", "memorized", "known"]; 674function wa({ activeStatus: n, counts: s, onChange: l, disabled: d = false, className: f = "", activeTabRef: u }) { 675 const h = a.useRef([]), i = (x, m) => { 676 var b; 677 if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(x.key)) return; 678 x.preventDefault(); 679 let g = m; 680 x.key === "ArrowLeft" ? g = (m - 1 + bt.length) % bt.length : x.key === "ArrowRight" ? g = (m + 1) % bt.length : x.key === "Home" ? g = 0 : x.key === "End" && (g = bt.length - 1); 681 const v = bt[g]; 682 l(v), (b = h.current[g]) == null || b.focus(); 683 }; 684 return e.jsx("div", { role: "tablist", "aria-label": "\uC554\uAE30 \uD3F4\uB354", className: `grid grid-cols-3 gap-1 rounded-2xl bg-gray-100 p-1 ${f}`, children: bt.map((x, m) => { 685 const b = x === n, g = ut[x]; 686 return e.jsxs("button", { ref: (v) => { 687 h.current[m] = v, b && u && (u.current = v); 688 }, id: `memorization-tab-${x}`, type: "button", role: "tab", "aria-label": `${g} ${s[x]}\uAC1C`, "aria-selected": b, "aria-controls": `memorization-panel-${x}`, tabIndex: b ? 0 : -1, disabled: d, onClick: () => l(x), onKeyDown: (v) => i(v, m), className: `flex min-h-11 min-w-0 items-center justify-center gap-1 rounded-xl px-2 py-1.5 text-sm font-semibold transition-colors md:block focus:outline-none focus:ring-2 focus:ring-primary/40 disabled:cursor-not-allowed disabled:opacity-50 ${b ? "bg-white text-primary shadow-sm" : "text-gray-500 hover:bg-white/60 hover:text-gray-700"}`, children: [e.jsx("span", { className: "min-w-0 truncate md:block", children: g }), e.jsxs("span", { className: `shrink-0 text-xs font-medium md:mt-0.5 md:block ${b ? "text-primary/70" : "text-gray-400"}`, children: [s[x], "\uAC1C"] })] }, x); 689 }) }); 690} 691const Ur = 20; 692function ja({ open: n, status: s, entries: l, onStart: d, onClose: f, random: u = Math.random, returnFocusRef: h }) { 693 const i = a.useMemo(() => ur(l, s), [l, s]), x = Math.min(Ur, i.length), [m, b] = a.useState(String(x)), [g, v] = a.useState(null), j = a.useRef(null), C = a.useRef(null); 694 if (_n(n, C, j, h), a.useEffect(() => { 695 n && (b(String(x)), v(null), requestAnimationFrame(() => { 696 var o; 697 return (o = j.current) == null ? void 0 : o.focus(); 698 })); 699 }, [x, n, s]), a.useEffect(() => { 700 if (!n) return; 701 const o = (p) => { 702 p.key === "Escape" && (p.preventDefault(), f()); 703 }; 704 return window.addEventListener("keydown", o), () => window.removeEventListener("keydown", o); 705 }, [f, n]), !n) return null; 706 const M = (o) => { 707 o.preventDefault(); 708 const p = Number(m); 709 if (!Number.isFinite(p) || p < 1 || i.length === 0) { 710 v("\uC554\uAE30\uD560 \uB2E8\uC5B4 \uAC1C\uC218\uB97C 1\uAC1C \uC774\uC0C1 \uC785\uB825\uD574 \uC8FC\uC138\uC694."); 711 return; 712 } 713 const y = Math.min(Math.trunc(p), i.length); 714 b(String(y)), v(null), d(fa(i, y, u)); 715 }; 716 return fr.createPortal(e.jsx("div", { className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/50 px-4 py-[max(1rem,env(safe-area-inset-top))]", onClick: (o) => { 717 o.target === o.currentTarget && f(); 718 }, children: e.jsxs("form", { ref: C, tabIndex: -1, role: "dialog", "aria-modal": "true", "aria-labelledby": "memorization-setup-title", "aria-describedby": "memorization-setup-description", onSubmit: M, className: "w-full max-w-sm rounded-2xl border border-gray-200 bg-white p-5 shadow-2xl", children: [e.jsxs("div", { className: "flex items-start gap-3", children: [e.jsxs("div", { className: "min-w-0 flex-1", children: [e.jsx("h2", { id: "memorization-setup-title", className: "text-lg font-bold text-gray-900", children: "\uC774\uBC88\uC5D0 \uC678\uC6B8 \uB2E8\uC5B4" }), e.jsxs("p", { id: "memorization-setup-description", className: "mt-1 text-sm text-gray-500", children: [ut[s], " \uD3F4\uB354\uC758 ", i.length, "\uAC1C \uC911 \uB79C\uB364\uC73C\uB85C \uACE8\uB77C \uCE74\uB4DC\uB97C \uB9CC\uB4ED\uB2C8\uB2E4."] })] }), e.jsx("button", { type: "button", "aria-label": "\uB2EB\uAE30", onClick: f, className: "-mr-1 rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-primary/40", children: e.jsx(_t, { className: "h-5 w-5", "aria-hidden": "true" }) })] }), e.jsxs("div", { className: "mt-5", children: [e.jsx("label", { htmlFor: "memorization-study-count", className: "text-sm font-semibold text-gray-700", children: "\uB2E8\uC5B4 \uAC1C\uC218" }), e.jsxs("div", { className: "mt-2 flex gap-2", children: [e.jsxs("div", { className: "relative min-w-0 flex-1", children: [e.jsx("input", { ref: j, id: "memorization-study-count", type: "number", inputMode: "numeric", min: 1, step: 1, value: m, disabled: i.length === 0, "aria-invalid": g != null, "aria-describedby": g ? "memorization-count-error" : void 0, onChange: (o) => { 719 b(o.target.value), v(null); 720 }, className: "h-11 w-full rounded-xl border border-gray-300 px-3 pr-10 text-base text-gray-900 outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/20 disabled:bg-gray-100" }), e.jsx("span", { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-sm text-gray-400", children: "\uAC1C" })] }), e.jsx("button", { type: "button", disabled: i.length === 0, onClick: () => { 721 b(String(i.length)), v(null); 722 }, className: "h-11 shrink-0 rounded-xl border border-gray-300 bg-white px-4 text-sm font-semibold text-gray-700 transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:opacity-45", children: "\uC804\uCCB4 \uC120\uD0DD" })] }), g && e.jsx("p", { id: "memorization-count-error", role: "alert", className: "mt-2 text-sm text-red-600", children: g }), i.length > 0 && e.jsxs("p", { className: "mt-2 text-xs text-gray-400", children: ["\uAE30\uBCF8 ", Math.min(Ur, i.length), "\uAC1C \xB7 \uCD5C\uB300 ", i.length, "\uAC1C"] })] }), e.jsxs("div", { className: "mt-6 flex gap-2", children: [e.jsx("button", { type: "button", onClick: f, className: "h-11 flex-1 rounded-xl border border-gray-300 text-sm font-semibold text-gray-600 transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-300", children: "\uCDE8\uC18C" }), e.jsx("button", { type: "submit", disabled: i.length === 0, className: "h-11 flex-[1.35] rounded-xl bg-primary text-sm font-semibold text-white transition-colors hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary/40 focu
722s:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-45", children: "\uC554\uAE30 \uC2DC\uC791" })] })] }) }), document.body); 723} 724const Wr = 120, Na = 60, Yr = 8, ka = "900px 0px 900px 0px", Sa = 6, ct = 56, Qr = 18; 725function Xr(n) { 726 const s = n == null ? void 0 : n.closest("[data-vocab-select-index]"); 727 if (!s) return null; 728 const l = Number(s.dataset.vocabSelectIndex); 729 return Number.isInteger(l) ? l : null; 730} 731function ar(n, s, l) { 732 const d = Xr(n instanceof Element ? n : null); 733 if (d != null) return d; 734 if (typeof document.elementsFromPoint != "function") return null; 735 for (const f of document.elementsFromPoint(s, l)) { 736 const u = Xr(f); 737 if (u != null) return u; 738 } 739 return null; 740} 741function _a(n) { 742 let s = n.parentElement; 743 for (; s; ) { 744 const l = window.getComputedStyle(s).overflowY; 745 if ((l === "auto" || l === "scroll") && s.scrollHeight > s.clientHeight) return s; 746 s = s.parentElement; 747 } 748 return null; 749} 750function Cn(n, s, l) { 751 const d = a.useRef(s), f = a.useRef(null), u = a.useRef(false), h = a.useRef(null), i = a.useRef(() => { 752 }); 753 a.useEffect(() => { 754 d.current = s; 755 }, [s]); 756 const x = a.useCallback((o, p, y) => { 757 const S = Math.max(0, Math.min(o, p)), _ = Math.min(n.length - 1, Math.max(o, p)); 758 S > _ || l((E) => { 759 const T = new Set(E); 760 for (let P = S; P <= _; P += 1) { 761 const V = n[P]; 762 y ? T.add(V) : T.delete(V); 763 } 764 return T; 765 }); 766 }, [n, l]), m = a.useCallback((o) => { 767 const p = f.current; 768 !p || o == null || o < 0 || o >= n.length || (x(p.lastIndex, o, p.select), p.lastIndex = o); 769 }, [x, n.length]), b = a.useCallback(() => { 770 h.current != null && (cancelAnimationFrame(h.current), h.current = null); 771 }, []); 772 a.useEffect(() => { 773 i.current = () => { 774 const o = f.current; 775 if (!(o != null && o.dragging) || !o.scrollContainer || o.scrollStep === 0) { 776 h.current = null; 777 return; 778 } 779 const p = o.scrollContainer.scrollTop; 780 o.scrollContainer.scrollTop += o.scrollStep, o.scrollContainer.scrollTop !== p && m(ar(null, o.clientX, o.clientY)), h.current = requestAnimationFrame(() => i.current()); 781 }; 782 }, [m]); 783 const g = a.useCallback((o, p) => { 784 const y = f.current; 785 if (!(y != null && y.scrollContainer)) return; 786 y.clientX = o, y.clientY = p; 787 const S = y.scrollContainer.getBoundingClientRect(); 788 if (S.height <= ct * 2) { 789 y.scrollStep = 0, b(); 790 return; 791 } 792 if (p < S.top + ct) { 793 const _ = Math.min(1, (S.top + ct - p) / ct); 794 y.scrollStep = -Math.max(2, Math.round(Qr * _)); 795 } else if (p > S.bottom - ct) { 796 const _ = Math.min(1, (p - (S.bottom - ct)) / ct); 797 y.scrollStep = Math.max(2, Math.round(Qr * _)); 798 } else { 799 y.scrollStep = 0, b(); 800 return; 801 } 802 h.current == null && (h.current = requestAnimationFrame(() => i.current())); 803 }, [b]); 804 a.useEffect(() => () => b(), [b]); 805 const v = a.useCallback((o) => { 806 if (d.current.size === 0 || o.isPrimary === false || o.pointerType === "mouse" && o.button !== 0) return; 807 const p = o.target instanceof Element ? o.target : null, y = p == null ? void 0 : p.closest("[data-vocab-drag-handle]"); 808 if (o.pointerType !== "mouse" && !y || !y && (p != null && p.closest("button, input, a, [data-vocab-selection-ignore]"))) return; 809 const S = ar(o.target, o.clientX, o.clientY); 810 if (!(S == null || S < 0 || S >= n.length)) { 811 f.current = { pointerId: o.pointerId, startX: o.clientX, startY: o.clientY, startIndex: S, lastIndex: S, select: !d.current.has(n[S]), dragging: false, clientX: o.clientX, clientY: o.clientY, scrollContainer: _a(o.currentTarget), scrollStep: 0 };
812 try { 813 o.currentTarget.setPointerCapture(o.pointerId); 814 } catch { 815 } 816 } 817 }, [n]), j = a.useCallback((o) => { 818 const p = f.current; 819 if (!p || p.pointerId !== o.pointerId) return; 820 const y = ar(o.target, o.clientX, o.clientY); 821 if (p.dragging) m(y); 822 else { 823 if (Math.hypot(o.clientX - p.startX, o.clientY - p.startY) < Sa) return; 824 p.dragging = true, x(p.startIndex, y != null ? y : p.startIndex, p.select), p.lastIndex = y != null ? y : p.startIndex; 825 } 826 o.preventDefault(), g(o.clientX, o.clientY); 827 }, [m, x, g]), C = a.useCallback((o) => { 828 const p = f.current; 829 if (!(!p || p.pointerId !== o.pointerId)) { 830 p.dragging && (u.current = true, window.setTimeout(() => { 831 u.current = false; 832 }, 0)), b(); 833 try { 834 o.currentTarget.hasPointerCapture(o.pointerId) && o.currentTarget.releasePointerCapture(o.pointerId); 835 } catch { 836 } 837 f.current = null; 838 } 839 }, [b]), M = a.useCallback((o) => { 840 o.preventDefault(); 841 }, []); 842 return { "data-vocab-selection-mode": s.size > 0 ? "active" : "inactive", onPointerDown: v, onPointerMove: j, onPointerUp: C, onPointerCancel: C, onClickCapture: (o) => { 843 u.current && (o.preventDefault(), o.stopPropagation()); 844 }, onContextMenu: M, onDragStart: M, style: { touchAction: "pan-y", userSelect: "none", WebkitUserSelect: "none", WebkitTouchCallout: "none" } }; 845} 846function Qt(n) { 847 return n === "memorized" || n === "known" ? n : "default"; 848} 849function vt(n) { 850 requestAnimationFrame(() => { 851 var s; 852 (s = document.getElementById(`memorization-tab-${n}`)) == null || s.focus({ preventScroll: true }); 853 }); 854} 855function En({ entries: n, actionableEntries: s = n, activeStatus: l, onStatusChange: d, onMoveEntries: f, onBeforeOpen: u, disabled: h = false, pronunciationSource: i }) { 856 const [x, m] = a.useState(false), [b, g] = a.useState(null), [v, j] = a.useState("default"), C = a.useRef(null), M = a.useMemo(() => ma(n), [n]), o = a.useMemo(() => s.filter((T) => T.memorization_status === l).length, [s, l]), p = l === "known" ? null : l, y = () => { 857 p && (u(), j(p), m(true)); 858 }, S = e.jsx("section", { id: `memorization-panel-${l}`, role: "tabpanel", "aria-labelledby": `memorization-tab-${l}`, className: "mb-3 space-y-2 md:mb-4 md:space-y-2.5", children: e.jsx(wa, { activeStatus: l, counts: M, onChange: d, disabled: h, activeTabRef: C }) }), _ = p ? e.jsx(va, { status: l, entryCount: o, onOpenCards: y, disabled: h, className: "ml-auto -my-1.5" }) : null, E = e.jsxs(e.Fragment, { children: [p && e.jsx(ja, { open: x, status: p, entries: s, returnFocusRef: C, onClose: () => m(false), onStart: (T) => { 859 m(false), j(p), g(T); 860 } }), e.jsx(ya, { open: b != null, sourceStatus: v, entries: b != null ? b : [], onMoveEntries: f, onClose: () => g(null), returnFocusRef: C, pronunciationSource: i })] }); 861 return { folderTabs: S, folderActions: _, overlays: E }; 862} 863const Ca = "[data-native-vocab-draw-zone]", Ea = 'button[data-handwriting-tap-through], input[type="checkbox"]'; 864function In(n, s, l, d) { 865 return `classro:vocab-native-drawing:v1:${n}:${s}:${l}:${d}`; 866} 867function Ia(n) { 868 try { 869 const s = window.localStorage.getItem(n); 870 if (!s) return {}; 871 const l = JSON.parse(s); 872 return l && typeof l == "object" ? l : {}; 873 } catch { 874 return {}; 875 } 876} 877function Gr(n, s, l = false) { 878 try { 879 window.localStorage.setItem(n, JSON.stringify(s)); 880 } catch (d) { 881 if (l) throw d; 882 } 883} 884function Ma(n) { 885 return -n; 886} 887function wt(n) { 888 return n < 0; 889} 890function Zr(n) { 891 return n.filter((s) => !wt(s)); 892} 893function Ra(n, s) { 894 var l; 895 if (s) { 896 const { verdict: d } = Yt({ word: n.word, meaning: n.meaning }, s); 897 return d === "duplicate" ? "saved" : d === "conflict" ? "conflict" : "none"; 898 } 899 return (l = n.membership) != null ? l : n.in_my_vocab ? "saved" : "none"; 900} 901function Aa(n) { 902 var s; 903 const l = n.filter((d) => !wt(d.id)); 904 return (s = hr(l, "newest")[0]) == null ? void 0 : s.id; 905} 906function Ta(n, s) { 907 var l; 908 const d = (l = n.example_sentence) != null ? l : ""; 909 return s.some((f) => { 910 var u; 911 return f.word === n.word && ((u = f.sentence) != null ? u : "") === d; 912 }); 913} 914function mr(n, s, l, d) { 915 return { bookEntryId: n.id, word: n.word, incomingMeaning: n.meaning, existingMeanings: s.map((f) => { 916 var u; 917 return (u = f.meaning) != null ? u : "-"; 918 }), mergeTargetEntryId: Aa(s), canAddSeparately: d ? d !== "separate_unavailable" : !Ta(n, l) }; 919} 920function Da(n) { 921 const s = Object.entries(n); 922 if (s.length === 0) return null; 923 const l = {}; 924 for (const [d, f] of s) l[Number(d)] = f.action === "merge" ? { action: "merge", ...f.targetEntryId != null ? { target_entry_id: f.targetEntryId } : {} } : { action: "separate" }; 925 return l; 926} 927function Jr(n, s) { 928 const l = (s != null ? s : "").trim(); 929 return l === "" || ca(n, l) ? n : n == null || n === "" ? l : `${n} 930${l}`; 931} 932function Pa(n, s) { 933 var l, d; 934 return { id: Ma(n.id), word: n.word, base_form: n.word, meaning: (l = n.meaning) != null ? l : null, sentence: (d = n.example_sentence) != null ? d : "", source_type: "book", homework_title: null, source_label: s, ai_status: "completed", created_at: (/* @__PURE__ */ new Date()).toISOString() }; 935} 936function Oa(n, s, l) { 937 if (s != null) { 938 const u = n.findIndex((h) => h.id === s); 939 if (u >= 0) return u; 940 } 941 const d = De(l), f = hr(n.filter((u) => De(u.word) === d), "newest")[0]; 942 return f ? n.findIndex((u) => u.id === f.id) : -1; 943} 944function Ba(n, s, l, d, f) { 945 var u; 946 if (!l) { 947 const x = new Set(s.map((m) => De(m.word))); 948 return n.filter((m) => !(x.has(De(m.word)) && m.source_type === "book")); 949 } 950 const h = [...n], i = []; 951 for (const x of s) { 952 const m = f == null ? void 0 : f[x.id]; 953 if ((m == null ? void 0 : m.action) === "merge") { 954 const v = Oa(h, m.targetEntryId, x.word); 955 if (v >= 0) { 956 const j = h[v]; 957 h[v] = { ...j, meaning: Jr(j.meaning, x.meaning), sentence: (u = Jr(j.sentence, x.example_sentence)) != null ? u : "", source_type: "book", source_label: d, ai_status: "completed" }; 958 continue; 959 } 960 } 961 const b = Pa(x, d), g = h.findIndex((v) => { 962 var j; 963 return De(v.word) === De(b.word) && ((j = v.sentence) != null ? j : "") === b.sentence; 964 }); 965 if (g >= 0) { 966 h[g] = { ...h[g], base_form: b.base_form, meaning: b.meaning, source_type: "book", source_label: b.source_label, ai_status: "completed" }; 967 continue; 968 } 969 i.push(b); 970 } 971 return i.length === 0 ? h : [...i, ...h]; 972} 973function Va(n, s, l, d, f) { 974 if (s.length === 0) return null; 975 const u = n.getQueryData(Me); 976 if (!(u != null && u.items)) return null; 977 const h = Ba(u.items, s, l, d, f), i = h.length - u.items.length; 978 return n.setQueryData(Me, { ...u, items: h, total: Math.max(0, u.total + i), per_page: h.length }), i; 979} 980function en(n, s) { 981 if (s.length === 0) return 0; 982 const l = n.getQueryData(Me);
983 if (!(l != null && l.items)) return 0; 984 const d = new Set(s), f = new Set(l.items.filter((m) => d.has(m.id) && wt(m.id)).map((m) => De(m.word))), u = [], h = l.items.filter((m) => { 985 const b = d.has(m.id) || m.source_type === "book" && f.has(De(m.word)); 986 return b && u.push(m), !b; 987 }); 988 if (u.length === 0) return 0; 989 n.setQueryData(Me, { ...l, items: h, total: Math.max(0, l.total - u.length), per_page: h.length }), n.setQueryData(["vocabulary", "count"], (m) => { 990 var b; 991 return typeof ((b = m == null ? void 0 : m.data) == null ? void 0 : b.total) != "number" ? m : { ...m, data: { ...m.data, total: Math.max(0, m.data.total - u.length) } }; 992 }); 993 const i = new Set(h.filter((m) => m.source_type === "book").map((m) => De(m.word))), x = new Set(u.map((m) => De(m.word)).filter((m) => !i.has(m))); 994 return x.size > 0 && n.setQueriesData({ queryKey: ["vocabulary", "book-entries"] }, (m) => { 995 var b; 996 return (b = m == null ? void 0 : m.data) != null && b.entries ? { ...m, data: { ...m.data, entries: m.data.entries.map((g) => x.has(De(g.word)) ? { ...g, in_my_vocab: false } : g) } } : m; 997 }), u.length; 998} 999function $a(n, s = 0) { 1000 var l, d, f, u, h, i, x, m, b; 1001 const g = (l = n.rejected) != null ? l : []; 1002 for (const S of g) de.error(S.message); 1003 const v = (d = n.added_count) != null ? d : 0, j = (f = n.merged_count) != null ? f : 0, C = Math.max(0, ((u = n.updated_count) != null ? u : 0) - j), M = (h = n.blocked_count) != null ? h : 0, o = (b = (m = (i = n.needs_decision) == null ? void 0 : i.length) != null ? m : (x = n.needs_decision_book_entry_ids) == null ? void 0 : x.length) != null ? b : 0, p = Math.max(0, o - s), y = []; 1004 v > 0 && y.push(`${v}\uAC1C \uCD94\uAC00`), j > 0 && y.push(`${j}\uAC1C \uD1B5\uD569`), C > 0 && y.push(`${C}\uAC1C \uC5C5\uB370\uC774\uD2B8`), M > 0 && y.push(`${M}\uAC1C\uB294 \uC774\uBBF8 \uC800\uC7A5\uB428`), y.length > 0 ? de.success(`${y.join(", ")}\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`) : o === 0 && g.length === 0 && de.info("\uCD94\uAC00\uD560 \uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."), p > 0 && de.info(`${p}\uAC1C \uB2E8\uC5B4\uB294 \uC800\uC7A5 \uBC29\uC2DD\uC744 \uB2E4\uC2DC \uC120\uD0DD\uD574 \uC8FC\uC138\uC694.`); 1005} 1006const tn = "\uC628\uB77C\uC778 \uC5F0\uACB0 \uD6C4 \uAC00\uB2A5"; 1007function rn() { 1008 return Je() ? "\uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB3D9\uAE30\uD654\uB420 \uB54C \uC11C\uBC84\uC5D0\uB3C4 \uBC18\uC601\uB429\uB2C8\uB2E4." : "\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uB85C \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC5F0\uACB0\uB418\uBA74 \uC790\uB3D9\uC73C\uB85C \uBC18\uC601\uB429\uB2C8\uB2E4."; 1009} 1010function St(n) { 1011 if (!Qn()) return; 1012 const s = /* @__PURE__ */ new Date(), l = `${String(s.getMinutes()).padStart(2, "0")}:${String(s.getSeconds()).padStart(2, "0")}.${String(s.getMilliseconds()).padStart(3, "0")}`; 1013 Xn(`${l} VOCAB ${n}`); 1014} 1015function J({ disabled: n = false, className: s, title: l, ariaLabel: d, children: f, onClick: u }) { 1016 return e.jsx("button", { type: "button", disabled: n, "aria-label": d, title: l, className: s, onClick: u, children: f }); 1017} 1018function Lt({ word: n, active: s, onPlay: l, handwriting: d = false }) { 1019 return e.jsx("button", { type: "button", ...d ? { "data-handwriting-tap-through": true } : {}, "aria-label": `${n} \uBC1C\uC74C \uC7AC\uC0DD`, title: `${n} \uBC1C\uC74C \uC7AC\uC0DD`, onClick: (f) => { 1020 f.stopPropagation(), l(); 1021 }, className: `inline-flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full transition-colors ${s ? "bg-primary/10 text-primary" : "text-gray-400 hover:bg-gray-100 hover:text-primary"} ${d ? "pointer-events-auto" : ""}`, children: e.jsx(Nt, { className: "h-4 w-4" }) }); 1022} 1023function Mn({ enVoices: n, koVoices: s, selectedEnVoice: l, selectedKoVoice: d, onEnVoiceChange: f, onKoVoiceChange: u, onClose: h, avoidNativeButtons: i = false }) { 1024 const x = (m, b) => { 1025 if (!(typeof window > "u" || typeof window.speechSynthesis > "u")) try { 1026 window.speechSynthesis.cancel(); 1027 const g = new SpeechSynthesisUtterance(b); 1028 g.voice = m, g.rate = 0.9, window.speechSynthesis.speak(g); 1029 } catch { 1030 } 1031 }; 1032 return e.jsxs("div", { className: "bg-white border border-gray-200 rounded-xl shadow-lg p-4 mb-3 space-y-3", children: [e.jsxs("div", { className: "flex items-center justify-between", children: [e.jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "\uC74
1032C\uC131 \uC124\uC815" }), e.jsx(J, { avoidNativeButton: i, onClick: h, className: "p-1 hover:bg-gray-100 rounded-lg transition-colors", children: e.jsx(_t, { className: "w-4 h-4 text-gray-400" }) })] }), e.jsxs("div", { children: [e.jsx("label", { className: "text-xs font-medium text-gray-500 mb-1 block", children: "\uC601\uC5B4 \uC74C\uC131" }), e.jsx("div", { className: "flex items-center gap-2", children: e.jsx("select", { value: l, onChange: (m) => { 1033 f(m.target.value); 1034 const b = n.find((g) => g.name === m.target.value); 1035 b && x(b.voice, "Hello"); 1036 }, className: "flex-1 text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary/30", children: n.map((m) => e.jsx("option", { value: m.name, children: m.name }, m.name)) }) })] }), e.jsxs("div", { children: [e.jsx("label", { className: "text-xs font-medium text-gray-500 mb-1 block", children: "\uD55C\uAD6D\uC5B4 \uC74C\uC131" }), e.jsx("div", { className: "flex items-center gap-2", children: e.jsx("select", { value: d, onChange: (m) => { 1037 u(m.target.value); 1038 const b = s.find((g) => g.name === m.target.value); 1039 b && x(b.voice, "\uC548\uB155\uD558\uC138\uC694"); 1040 }, className: "flex-1 text-sm border border-gray-200 rounded-lg px-2.5 py-1.5 text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary/30", children: s.map((m) => e.jsx("option", { value: m.name, children: m.name }, m.name)) }) })] }), e.jsx("p", { className: "text-xs text-gray-400", children: "\uC74C\uC131\uC744 \uBCC0\uACBD\uD558\uBA74 \uBBF8\uB9AC\uB4E3\uAE30\uAC00 \uC7AC\uC0DD\uB429\uB2C8\uB2E4. \uC120\uD0DD\uD55C \uC74C\uC131\uC740 \uC800\uC7A5\uB429\uB2C8\uB2E4." })] }); 1041} 1042function Rn() { 1043 const n = a.useMemo(() => un(), []), [s, l] = a.useState(false), [d, f] = a.useState("pen"), [u, h] = a.useState("#ef4444"), i = a.useRef(/* @__PURE__ */ new Map()), [x, m] = a.useState(null), [b, g] = a.useState(false), v = a.useRef(false), j = a.useRef(null), C = a.useRef(null), M = a.useCallback(() => { 1044 var S, _; 1045 if (v.current) { 1046 (S = j.current) == null || S.call(j); 1047 return; 1048 } 1049 x != null && ((_ = i.current.get(x)) == null || _.undo()); 1050 }, [x]), o = a.useCallback(() => { 1051 var S; 1052 if (v.current) { 1053 (S = C.current) == null || S.call(C); 1054 return; 1055 } 1056 i.current.forEach((_) => _.clear()); 1057 }, []), p = a.useCallback((S) => { 1058 const _ = S instanceof Element ? S : null; 1059 return !(_ != null && _.closest('svg[data-handwriting-surface="true"]')); 1060 }, []), y = a.useCallback((S) => { 1061 const _ = S instanceof Element ? S : null; 1062 return !!(_ != null && _.closest(["button", '[role="button"]', "input", "select", "textarea", "a[href]", "label", '[contenteditable="true"]'].join(","))); 1063 }, []); 1064 return a.useEffect(() => { 1065 if (window.dispatchEvent(new CustomEvent(Cr, { detail: { enabled: s } })), s || m(null), s) { 1066 const S = document.activeElement; 1067 S instanceof HTMLElement && S.blur(); 1068 } 1069 return () => { 1070 s && window.dispatchEvent(new CustomEvent(Cr, { detail: { enabled: false } })); 1071 }; 1072 }, [s]), a.useEffect(() => { 1073 if (!s) return; 1074 const S = (E) => { 1075 !E.cancelable || !p(E.target) || y(E.target) || !("pointerType" in E && E.pointerType === "pen") && !Zn(E) || (E.preventDefault(), St(`PEN_SCROLL_BLOCK ${E.type} target=${E.target instanceof Element ? E.target.tagName.toLowerCase() : "non-element"}`)); 1076 }, _ = { capture: true, passive: false }; 1077 return document.addEventListener("pointermove", S, _), document.addEventListener("touchmove", S, _), () => { 1078 document.removeEventListener("pointermove", S, _), document.removeEventListener("touchmove", S, _); 1079 }; 1080 }, [s, p, y]), { deviceId: n, drawingEnabled: s, setDrawingEnabled: l, handwritingTool: d, setHandwritingTool: f, handwritingColor: u, setHandwritingColor: h, handwritingHandlesRef: i, activeHandwritingEntryId: x, setActiveHandwritingEntryId: m, handleHandwritingUndo: M, handleHandwritingClear: o, largeCards: b, setLargeCards: g, nativeActiveRef: v, nativeUndoRef: j, nativeClearRef: C }; 1081} 1082function An(n) { 1083 const { entityId: s, enabled: l, containerRef: d, tool: f, color: u, scopeName: h, nativeDrawingStorageKey: i, buildStorageScope: x, recordDraft: m, setActiveEntryId: b, setPersistenceStatus: g } = n, [v, j] = a.useState(() => Ia(i)), C = a.useRef(v), M = a.useCallback((T, P) => { 1084 const V = String(T), xe = x(T); 1085 g(V, { target: "local", phase: "saving", retryable: false, updatedAt: P == null ? void 0 : P.updatedAt }), (P ? pr(xe, P) : br(xe)).then(() => { 1086 g(V, { target: "local", phase: "saved", retryable: false, updatedAt: P == null ? void 0 : P.updatedAt }); 1087 }).catch((Y) => { 1088 const A = Y instanceof jt ? Y : new jt("unknown_local_error"); 1089 g(V, { target: "local", phase: "error", reasonCode: A.reasonCode, message: yr(A.reasonCode, h), retryable: true, updatedAt: P == null ? void 0 : P.updatedAt }); 1090 }); 1091 }, [x, h, g]), o = a.useCallback((T) => { 1092 const P = Number(T); 1093 Number.isFinite(P) && b(P); 1094 }, [b]), p = a.useCallback((T, P) => { 1095 const V = Number(T); 1096 Number.isFinite(V) && (m(V, P), M(V, P)); 1097 }, [M, m]), y = a.useCallback((T, P) => { 1098 const V = { ...C.current, [T]: P }; 1099 C.current = V, Gr(i, V), j(V); 1100 }, [i]), { isNativeActive: S, undo: _, clear: E }
1100 = Gn({ entityType: "vocabulary", entityId: s, enabled: l, zoneCanvasMode: true, coordinateSpace: "document", tool: f, color: u, containerRef: d, drawZoneSelector: Ca, tapZoneSelector: Ea, nativeDrawingDataByZone: v, onActiveZoneChange: o, onDraftChangeByZone: p, onNativeDrawingDataByZone: y }); 1101 return Pn({ label: `vocabulary-sidecar:${i}`, prepare: () => { 1102 S && Gr(i, C.current, true); 1103 } }), { isNativeActive: S, undo: _, clear: E }; 1104} 1105function Tn({ entryId: n, index: s, active: l, largeCards: d, isNativeActive: f, onActivate: u, renderOverlay: h, children: i }) { 1106 const x = a.useRef(null), [m, b] = a.useState(s < Yr); 1107 a.useEffect(() => { 1108 if (s < Yr) { 1109 b(true); 1110 return; 1111 } 1112 if (typeof IntersectionObserver > "u") { 1113 b(true); 1114 return; 1115 } 1116 const C = x.current; 1117 if (!C) return; 1118 const M = new IntersectionObserver(([o]) => { 1119 var p; 1120 const y = (p = o == null ? void 0 : o.isIntersecting) != null ? p : false; 1121 b(y), St(`MOUNT_GATE entry=${n} visible=${y ? "Y" : "N"}`); 1122 }, { rootMargin: ka, threshold: 0 }); 1123 return M.observe(C), () => M.disconnect(); 1124 }, [n, s]); 1125 const g = m || l, j = { height: d ? Wr : Na }; 1126 return e.jsxs("div", { ref: x, "data-native-vocab-draw-zone": true, "data-zone-id": String(n), className: "relative bg-white rounded-xl border border-gray-200 overflow-hidden", style: j, onPointerDown: u, children: [!f && g && e.jsx("div", { className: "absolute inset-x-0 top-1/2 -translate-y-1/2", style: { height: Wr }, children: h() }), i] }); 1127} 1128const nn = [{ value: "newest", label: "\uCD5C\uC2E0\uC21C" }, { value: "oldest", label: "\uC624\uB798\uB41C\uC21C" }]; 1129function ks() { 1130 const [n, s] = On(), l = n.get("view"), d = n.get("bookId"), f = d ? Number(d) : null, u = f ? "book-detail" : l === "my-vocab" ? "my-vocab" : "categories", h = () => { 1131 s({ view: "my-vocab" }); 1132 }, i = (m) => { 1133 s({ bookId: String(m) }); 1134 }, x = () => { 1135 s({}); 1136 }; 1137 return u === "my-vocab" ? e.jsx(La, { onBack: x }) : u === "book-detail" && f ? e.jsx(Fa, { bookId: f, onBack: x }) : e.jsx(za, { onSelectMyVocab: h, onSelectBook: i }); 1138} 1139function za({ onSelectMyVocab: n, onSelectBook: s }) { 1140 var l, d, f; 1141 const u = gr(), h = Xt(), i = xr((z) => z.user), { data: x, isLoading: m, isError: b, refetch: g } = dt({ queryKey: ["vocabulary", "count"], queryFn: () => Oe.getList({ page: 1, per_page: 1 }), select: (z) => z.data }), { data: v, isLoading: j, isError: C, refetch: M } = dt({ queryKey: ["vocabulary", "books"], queryFn: () => Oe.listBooks(), select: (z) => z.data.books, staleTime: 6e4 }), o = m || j, p = (l = x == null ? void 0 : x.total) != null ? l : null, y = a.useMemo(() => v != null ? v : [], [v]), S = b && x === void 0, _ = C && v === void 0, E = !u && x === void 0, T = !u && v === void 0, P = E && T, V = E !== T, xe = u && S && _, O = u ? S !== _ : V, Y = () => { 1142 Promise.all([g(), M()]); 1143 }, A = a.useMemo(() => un(), []), ge = (d = i == null ? void 0 : i.academy_id) != null ? d : null, G = (f = i == null ? void 0 : i.id) != null ? f : null, [ue, F] = a.useState([]), [Ne, R] = a.useState([]); 1144 a.useEffect(() => { 1145 if (ge == null || G == null) { 1146 F([]); 1147 return; 1148 } 1149 let z = false; 1150 const H = () => { 1151 Wn(ge, G).then((oe) => { 1152 z || F(oe); 1153 }).catch(() => { 1154 z || F([]); 1155 }); 1156 }; 1157 H(); 1158 const U = on(H); 1159 return () => { 1160 z = true, U(); 1161 }; 1162 }, [ge, G]); 1163 const D = a.useMemo(() => { 1164 var z, H, U, oe; 1165 const we = new Set(Ne); 1166 for (const le of ue) { 1167 if (we.has(le.key)) continue; 1168 const { bookId: L, bookEntryId: Ee } = le.payload, Be = (H = (z = h.getQueryData(["vocabulary", "book-entries", L])) == null ? void 0 : z.data) == null ? void 0 : H.entries, pe = Be == null ? void 0 : Be.find((Le) => Le.id === Ee); 1169 if (!pe) continue; 1170 const Ie = (oe = (U = h.getQueryData(Me)) == null ? void 0 : U.items) != null ? oe : [], me = Yt({ word: pe.word, meaning: pe.meaning }, Ie); 1171 return { job: le, prompt: mr(pe, me.wordMatches, Ie, le.payload.needsDecisionReason) }; 1172 } 1173 return null; 1174 }, [ue, h, Ne]), K = (z) => R((H) => [...H, z]), ee = (z) => { 1175 if (!D) return; 1176 const { bookId: H, bookEntryId: U } = D.job.payload; 1177 K(D.job.key), Ht({ action: "add", academyId: ge != null ? ge : 0, userId: G != null ? G : 0, deviceId: A, bookId: H, bookEntryIds: [U], decisions: { [U]: z } }).then(() => Yn()); 1178 }, he = a.useMemo(() => { 1179 var z; 1180 const H = [], U = /* @__PURE__ */ new Map(); 1181 for (const oe of y) { 1182 const we = oe.classroom_id; 1183 let le = U.get(we); 1184 le || (le = { classroomId: we, classroomName: (z = oe.classroom_name) != null ? z : "\uAE30\uD0C0", books: [] }, U.set(we, le), H.push(le)), le.books.push(oe); 1185 } 1186 return H; 1187 }, [y]); 1188 let te = 0; 1189 return e.jsxs("div", { className: "max-w-[1120px] mx-auto px-2 sm:px-5 pt-6 pb-24 flex flex-col gap-4", children: [D && e.jsx(wn, { prompt: D.prompt, onMerge: () => ee({ action: "merge", targetEntryId: D.prompt.mergeTargetEntryId }), onSeparate: () => ee({ action: "separate" }), onDismiss: () => K(D.job.key), onDiscard: () => { 1190 K(D.job.key), Kn(D.job.key); 1191 } }), e.jsxs("div", { children: [e.jsx("h1", { className: "text-[21px] font-extrabold tracking-[-0.3px] text-ink", children: "\uB2E8\uC5B4\uC7A5" }), e.jsx("p", { className: "mt-[5px] text-[13.5px] text-muted", children: "\uC800\uC7A5\uD55C \uB2E8\uC5B4\uC640 \uD074\uB798\uC2A4 \uB2E8\uC5B4\uC7A5\uC73C\uB85C \uC554\uAE30 \uD559\uC2B5\uC744 \uC2DC\uC791\uD558\uC138\uC694" })] }), P ? e.jsx(vr, { message: "\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uC774\uBA70 \uB2E8\uC5B4\uC7A5 \uC815\uBCF4\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." }) : o && !V ? e.jsx("div", { className: "flex items-center justify-center h-48", children: e.jsx(We, { className: "w-8 h-8 animate-spin text-primary" }) }) : xe ? e.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [e.jsx(Ke, { className: "mb-4 h-12 w-12 text-amber-500" }), e.jsx("h2", { className: "mb-2 text-lg font-semibold text-gray-700", children: "\uB2E8\uC5B4\uC7A5 \uC815\uBCF4\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "mb-5 text-sm text-gray-500", children: "\uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694." }), e.jsx("button", { type: "button", onClick: Y, className: "rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm text-gray-700 hover:bg-gray-50", children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] }) : e.jsxs("div", { className: "flex flex-col gap-4", children: [O && e.jsxs("div", { role: "alert", className: "flex flex-wrap items-center gap-2 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800", children: [e.jsx(Ke, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }), e.jsx("span", { className: "min-w-0 flex-1", children: u ? "\uC77C\uBD80 \uB2E8\uC5B4\uC7A5 \uC815\uBCF4\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4." : "\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uC774\uBA70 \uC77C\uBD80 \uB2E8\uC5B4\uC7A5 \uC815\uBCF4\uB9CC \uD45C\uC2DC\uB429\uB2C8\uB2E4." }), u && e.jsx("button", { type: "button", onClick: Y, className: "rounded-lg border border-amber-300 bg-white px-3 py-1.5 text-xs font-semibold hover:bg-amber-100", children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] }), e.jsxs("button", { onClick: n, className: "w-full flex flex-wrap items-center gap-4 px-5 py-[18px] rounded-2xl text-white text-left hover:brightness-[1.04] transition-all duration-150", style: { background: "linear-gradient(135deg, #667EEA, #764BA2)" }, children: [e.jsx("span", { className: "w-12 h-12 flex-none rounded-[14px] bg-white/[.18] flex items-center justify-center", children: e.jsx(yt, { className: "w-[22px] h-[22px]", strokeWidth: 1.8 }) }), e.jsxs("div", { className: "flex-1 min-w-[160px]", children: [e.jsx("p", { className: "m-0 text-base font-extrabold", children: "\uB098\uC758 \uB2E8\uC5B4\uC7A5" }), e.jsxs("p", { className: "mt-[3px] mb-0 text-[12.5px] opacity-85 tabular-nums", children: ["\uC219\uC81C\xB7\uC624\uB2F5\uC5D0\uC11C \uC800\uC7A5\uD55C \uB2E8\uC5B4 ", e.jsx("b", { children: p != null ? p : "\u2014" }), p == null ? "" : "\uAC1C"] })] }), e.jsx("span", { className: "flex-none px-[18px] py-[9px] rounded-[10px] bg-white text-[#5A47A8] text-[13px] font-extrabold", children: "\uC554\uAE30 \uC2DC\uC791" })] }), he.map((z) => e.jsxs("div", { children: [e.jsx("div", { className: "text-xs font-extrabold tracking-[.06em] text-faint mb-2", children: z.classroomName }), e.jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-3", children: z.books.map((H) => { 1192 const U = te++; 1193 return e.jsxs("button", { onClick: () =>
1193 s(H.id), className: `flex items-center gap-[13px] px-4 py-[15px] rounded-[14px] border text-left transition-shadow duration-150 ${H.is_highlighted ? "bg-[#FFFBEB] border-[#F3D9A4] hover:shadow-[0_4px_14px_rgba(217,119,6,.12)]" : "bg-white border-line hover:shadow-[0_4px_14px_rgba(16,24,40,.07)]"}`, children: [e.jsx("span", { className: "w-11 h-11 flex-none rounded-xl text-white flex items-center justify-center", style: { background: H.is_highlighted ? "linear-gradient(135deg, #F59E0B, #F97316)" : an[U % an.length] }, children: e.jsx(ir, { className: "w-[19px] h-[19px]", strokeWidth: 1.8 }) }), e.jsxs("div", { className: "min-w-0 flex-1", children: [e.jsxs("p", { className: "m-0 text-sm font-bold text-ink-2 flex items-center gap-[5px]", children: [e.jsx("span", { className: "truncate", children: H.title }), H.is_highlighted && e.jsx(aa, { className: "w-[13px] h-[13px] text-[#F59E0B] fill-[#F59E0B] flex-shrink-0" })] }), e.jsxs("p", { className: "mt-0.5 mb-0 text-xs text-faint tabular-nums", children: [H.entry_count, "\uAC1C \uB2E8\uC5B4"] })] }), e.jsx(qn, { className: "w-[15px] h-[15px] text-[#C9CBD6] flex-shrink-0", strokeWidth: 2 })] }, H.id); 1194 }) })] }, z.classroomId)), y.length === 0 && p === 0 && !E && !T && e.jsxs("div", { className: "text-center py-12", children: [e.jsx(yt, { className: "w-16 h-16 mx-auto text-ghost mb-4" }), e.jsx("h2", { className: "text-lg font-bold text-ink-3 mb-2", children: "\uB2E8\uC5B4\uC7A5\uC774 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "text-muted text-sm", children: "\uC219\uC81C \uBB38\uC81C\uB97C \uD480\uBA74\uC11C \uB2E8\uC5B4\uB97C \uD0ED\uD574\uC11C \uC800\uC7A5\uD574\uBCF4\uC138\uC694" })] })] })] }); 1195} 1196const an = ["linear-gradient(135deg, #8B5CF6, #7C3AED)", "linear-gradient(135deg, #10B981, #0D9488)", "linear-gradient(135deg, #3B82F6, #4F46E5)"]; 1197function sn({ sources: n, selected: s, onChange: l, compact: d = false, avoidNativeButtons: f = false }) { 1198 const u = Math.min(typeof window < "u" ? window.innerHeight * 0.7 : 500, n.length * 40 + 110), { isOpen: h, toggle: i, close: x, buttonRef: m, dropdownRef: b, dropdownStyle: g } = Bn({ dropdownWidth: 256, dropdownHeight: u }), v = (o) => { 1199 l(s.includes(o) ? s.filter((p) => p !== o) : [...s, o]); 1200 }, j = () => l([]), C = s.length > 0, M = C ? s.length === 1 ? s[0] : `${s.length}\uAC1C \uC120\uD0DD` : d ? "\uC804\uCCB4" : "\uC804\uCCB4 \uCD9C\uCC98"; 1201 return e.jsxs(e.Fragment, { children: [e.jsxs("div", { ref: m, role: "button", tabIndex: 0, onClick: (o) => { 1202 o.stopPropagation(), i(); 1203 }, onKeyDown: (o) => { 1204 o.key !== "Enter" && o.key !== " " || (o.preventDefault(), i()); 1205 }, className: `flex items-center gap-1 text-sm border rounded-lg ${d ? "px-2 py-1.5 max-w-[120px]" : "px-3 py-1.5 max-w-[200px]"} focus:outline-none focus:ring-2 focus:ring-primary/30 ${C ? "border-primary/30 bg-primary/5 text-primary" : "border-gray-200 text-gray-700 bg-white hover:bg-gray-50"}`, title: C ? s.join(", ") : "\uCD9C\uCC98 \uD544\uD130", children: [e.jsx("span", { className: "truncate flex-1 text-left", children: M }), e.jsx(sa, { className: `w-3.5 h-3.5 flex-shrink-0 transition-transform ${h ? "rotate-180" : ""}` })] }), h && fr.createPortal(e.jsxs("div", { ref: b, style: g, className: "w-64 max-w-[calc(100vw-1.5rem)] max-h-[70vh] flex flex-col bg-white border border-gray-200 rounded-lg shadow-lg", children: [e.jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 bg-white border-b border-gray-100", children: [e.jsxs("span", { className: "text-xs font-medium text-gray-500", children: ["\uCD9C\uCC98 \uC120\uD0DD (", s.length, "/", n.length, ")"] }), C && e.jsx(J, { avoidNativeButton: f, onClick: j, className: "text-xs text-primary hover:underline", children: "\uC804\uCCB4 \uD574\uC81C" })] }), e.jsx("ul", { className: "flex-1 overflow-y-auto py-1", children: n.map((o) => { 1206 const p = s.includes(o); 1207 return e.jsx("li", { children: e.jsxs("label", { className: "flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer", children: [e.jsx("input", { type: "checkbox", checked: p, onChange: () => v(o), className: "w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary/30" }), e.jsx("span", { className: "flex-1 truncate", title: o, children: o })] }) }, o); 1208 }) }), e.jsx("div", { className: "px-3 py-2 border-t border-gray-100 bg-gray-50 rounded-b-lg", children: e.jsx(J, { avoidNativeButton: f, onClick: x, className: "w-full py-2 text-sm font-medium text-white bg-primary hover:brightness-110 rounded-lg transition-all", children: "\uC644\uB8CC" }) })] }), document.body)] }); 1209} 1210function Dn(n, s) { 1211 var l, d; 1212 const f = (d = (l = n == null ? void 0 : n.current_device) == null ? void 0 : l.drafts_by_entry) != null ? d : {}, u = {}; 1213 for (const [h, i] of Object.entries(f)) if (i) { 1214 if (!Un(i)) { 1215 console.warn("[buildServerDraftsByEntry] dropping non-v2 draft for entry", h); 1216 continue; 1217 } 1218 u[h] = i; 1219 } 1220 return u; 1221} 1222function La({ onBack: n }) { 1223 var s, l, d, f, u; 1224 const h = Xt(), i = xr((t) => t.user), [x, m] = a.useState("newest"), [b, g] = a.useState("default"), [v, j] = a.useState([]), [C, M] = a.useState(false), [o, p] = a.useState(false), [y, S] = a.useState(false), [_, E] = a.useState(0), [T, P] = a.useState(false), [V, xe] = a.useState(/* @__PURE__ */ new Set()), O = a.useRef(/* @__PURE__ */ new Map()), { deviceId: Y, drawingEnabled: A, setDrawingEnabled: ge, handwritingTool: G, setHandwritingTool: ue, handwritingColor: F, setHandwritingColor: Ne, handwritingHandlesRef: R, activeHandwritingEntryId: D, setActiveHandwritingEntryId: K, handleHandwritingUndo: ee, handleHandwritingClear: he, largeCards: te, setLargeCards: z, nativeActiveRef: H, nativeUndoRef: U, nativeClearRef: oe } = Rn(), we = a.useRef({}), le = a.useRef(null), { currentError: L, setPersistenceStatus: Ee, statusByKey: Be } = bn("local"); 1225 pn(A); 1226 const pe = kn({ targetType: "personal", academyId: (s = i == null ? void 0 : i.academy_id) != null ? s : 0, userId: (l = i == null ? void 0 : i.id) != null ? l : 0, deviceId: Y }), Ie = a.useCallback((t) => { 1227 const r = O.current.get(t); 1228 r && (clearTimeout(r), O.current.delete(t)), xe((c) => { 1229 const N = new Set(c); 1230 if (N.has(t)) return N.delete(t), N; 1231 N.add(t); 1232 const w = setTimeout(() => { 1233 xe((B) => { 1234 const $ = new Set(B); 1235 return $.delete(t), $; 1236 }), O.current.delete(t); 1237 }, 3e3); 1238 return O.current.set(t, w), N; 1239 }); 1240 }, []); 1241 a.useEffect(() => () => { 1242 O.current.forEach((t) => clearTimeout(t)); 1243 }, []), a.useEffect(() => { 1244 o || (O.current.forEach((t) => clearTimeout(t)), O.current.clear(), xe(/* @__PURE__ */ new Set())); 1245 }, [o]); 1246 const { data: me, isLoading: Le, isError: Ve, refetch: qe } = dt({ queryKey: [...Me], queryFn: mn, placeholderData: (t) => t }), se = gr(), _e = a.useMemo(() => { 1247 var t; 1248 return (t = me == null ? void 0 : me.items) != null ? t : []; 1249 }, [me == null ? void 0 : me.items]), re = a.useMemo(() => _e.map((t) => { 1250 var r; 1251 return { id: t.id, word: (r = t.base_form) != null ? r : t.word, meaning: t.meaning, memorization_status: Qt(t.memorization_status) }; 1252 }), [_e]), et = a.useMemo(() => re.filter((t) => !wt(t.id)), [re]), Ye = a.useMemo(() => Fn(me), [me]), tt = !se && me === void 0, be = se && me === void 0 && Ve, fe = a.useMemo(() => hr(Hn(_e, v), x), [_e, v, x]), Fe = a.useMemo(() => fe.filter((t) => Qt(t.memorization_status) === b), [b, fe]), ae = a.useMemo(() => { 1253 if (!y) return Fe; 1254 const t = [...Fe]; 1255 let r = _; 1256 for (let c = t.length - 1; c > 0; c--) { 1257 r = (r * 16807 + 11) % 2147483647; 1258 const N = r % (c + 1); 1259 [t[c], t[N]] = [t[N], t[c]]; 1260 } 1261 return t; 1262 }, [Fe, y, _]), He = () => { 1263 y ? S(false) : (E(Date.now()), S(true)); 1264 }, Re = a.useMemo(() => ae.filter((t) => t.ai_status !== "pending" && t.ai_status !== "failed").map((t) => { 1265 var r, c; 1266 return { word: (r = t.base_form) != null ? r : t.word, meaning: (c = t.meaning) != null ? c : "" }; 1267 }), [ae]), { isSupported: rt, isPlaying: Ae, currentIndex: mt, toggleTTS: nt, stopTTS: at, enVoices: Qe, koVoices: ft, selectedEnVoice: st, selectedKoVoice: xt, setSelectedEnVoice: k, setSelectedKoVoice: I } = jr(Re), [W, Z] = a.useState(false), ye = a.useMemo(() => { 1268 const t = []; 1269 return ae.forEach((r, c) => { 1270 r.ai_status !== "pending" && r.ai_status !== "failed" && t.push(c); 1271 }), t; 1272 }, [ae]), ce = mt >= 0 && (d = ye[mt]) != null ? d : -1, $e = a.useRef(null); 1273 a.useEffect(() => { 1274 var t; 1275 ce >= 0 && ((t = $e.current) == null || t.scrollIntoView({ behavior: "smooth", block: "center" })); 1276 }, [ce]); 1277 const X = a.useCallback((t) => t.id, []), { selectedIds: q, setSelectedIds: ke, handleSelect: ne, handleSelectAll: ve, clearSelection: Xe } = fn(ae, X), Bt = a.useMemo(() => ae.map((t) => t.id), [ae]), Ce = Cn(Bt, q, ke), Ct = a.useCallback(() => { 1278 Ae && at(), A && ge(false); 1279 }, [A, Ae, ge, at]), Pe = async (t, r) => { 1280 try { 1281 await pe.moveEntries(t, r); 1282 } finally { 1283 Xe(); 1284 } 1285 }, Gt = (t) => { 1286 Ct(), Xe(), g(t); 1287 }, Se = async (t) => { 1288 try { 1289 await Pe([...q], t), de.success(`${t === "default" ? "\uAE30\uBCF8" : "\uC554\uAE30\uB428"} \uD3F4\uB354\uB85C \uC774\uB3D9\uD588\uC2B5\uB2C8\uB2E4.`); 1290 } catch (r) { 1291 de.error(Ze(r, "\uB2E8\uC5B4\uB97C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.")); 1292 } finally { 1293 vt("known"); 1294 } 1295 }, gt = async () => { 1296 try { 1297 await Pe([...q], "known"), de.success("\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9\uD588\uC2B5\uB2C8\uB2E4."); 1298 } catch (t) { 1299 de.error(Ze(t, "\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.")); 1300 } finally { 1301 vt("known"); 1302 } 1303 }, Vt = a.useCallback(async (t) => { 1304 var r, c; 1305 t.length !== 0 && await Ht({ action: "remove", academyId: (r = i == null ? void 0 : i.academy_id) != null ? r : 0, userId: (c = i == null ? void 0 : i.id) != null ? c : 0, deviceId: Y, bookId: null, bookEntryIds: t.map((N) => -N) }); 1306 }, [Y, i == null ? void 0 : i.academy_id, i == null ? void 0 : i.id]), ot = a.useCallback(async (t) => { 1307 var r, c; 1308 if (t.length === 0) return; 1309 const N = (r = i == null ? void 0 : i.academy_id) != null ? r : 0, w = (c = i == null ? void 0 : i.id) != null ? c : 0; 1310 let B; 1311 try { 1312 B = await ln(N, w); 1313 } catch { 1314 return; 1315 } 1316 const $ = new Set(t), ie = B.filter((Q) => Q.kind === "vocab_book_add").map((Q) => Q.payload).filter((Q) => Q.decision === "merge" && Q.mergeTargetEntryId != null && $.has(Q.mergeTargetEntryId)).map((Q) => Q.bookEntryId); 1317 ie.length !== 0 && await Ht({ action: "remove", academyId: N, userId: w, deviceId: Y, bookId: null, bookEntryIds: ie }); 1318 }, [Y, i == null ? void 0 : i.academy_id, i == null ? void 0 : i.id]), ht = Dt({ networkMode: "offlineFirst", mutationFn: async (t) => wt(t) ? (await Vt([t]), { mode: "superseded", ids: [t] }) : sr(async () => { 1319 var r, c, N, w, B, $; 1320 return await ot([t]), Je() ? (await Oe.deleteEntry(t), await $t({ academyId: (B = i == null ? void 0 : i.academy_id) != null ? B : 0, userId: ($ = i == null ? void 0 : i.id) != null ? $ : 0, targetType: "personal", entryIds: [t] }).catch(() => { 1321 }), { mode: "sent", ids: [t] }) : (await _r({ academyId: (r = i == null ? void 0 : i.academy_id) != null ? r : 0, userId: (c = i == null ? void 0 : i.id) != null ? c : 0, deviceId: Y, entryIds: [t] }), await $t({ academyId: (N = i == null ? void 0 : i.academy_id) != null ? N : 0, userId: (w = i == null ? void 0 : i.id) != null ? w : 0, targetType: "personal", entryIds: [t] }).catch(() => { 1322 }), { mode: "queued", ids: [t] }); 1323 }), onSuccess: (t) => { 1324 if (en(h, t.ids), t.mode === "sent") { 1325 h.invalidateQueries({ queryKey: ["vocabulary"] }), de.success("\uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."); 1326 return; 1327 } 1328 de.success(rn()); 1329 }, onError: (t) => { 1330 de.error(Ze(t, "\uC0AD\uC81C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.")); 1331 } }), Et = Dt({ networkMode: "offlineFirst", mutationFn: async (t) => { 1332 const r = t.filter(wt), c = Zr(t); 1333 return await Vt(r), c.length === 0 ? { calledServer: false, ids: t } : sr(async () => { 1334 var N, w, B, $, ie, Q; 1335 return await ot(c), Je() ? (await Oe.batchRemove(c), await $t({ academyId: (ie = i == null ? void 0 : i.academy_id) != null ? ie : 0, userId: (Q = i == null ? void 0 : i.id) != null ? Q : 0, targetType: "personal", entryIds: c }).catch(() => { 1336 }), { calledServer: true, ids: t }) : (await _r({ academyId: (N = i == null ? void 0 : i.academy_id) != null ? N : 0, userId: (w = i == null ? void 0 : i.id) != null ? w : 0, deviceId: Y, entryIds: c }), await $t({ academyId: (B = i == null ? void 0 : i.academy_id) != null ? B : 0, userId: ($ = i == null ? void 0 : i.id) != null ? $ : 0, targetType: "personal", entryIds: c }).catch(() => { 1337 }), { calledServer: false, ids: t }); 1338 }); 1339 }, onSuccess: (t) => { 1340 if (en(h, t.ids), Xe(), M(false), t.calledServer) { 1341 h.invalidateQueries({ queryKey: ["vocabulary"] }), de.success("\uC120\uD0DD \uD56D\uBAA9\uC744 \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4."); 1342 return; 1343 } 1344 de.success(rn()); 1345 }, onError: (t) => { 1346 de.error(Ze(t, "\uC0AD\uC81C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.")); 1347 } }), Ge = Dt({ mutationFn: async (t) => { 1348 const r = Zr(t); 1349 r.length !== 0 && await Oe.retryAi(r); 1350 }, onSuccess: () => { 1351 h.invalidateQueries({ queryKey: ["vocabulary"] }), de.success("\uBD84\uC11D\uC744 \uB2E4\uC2DC \uC2DC\uC791\uD588\uC2B5\uB2C8\uB2E4."); 1352 }, onError: (t) => { 1353 de.error(Ze(t, "\uC7AC\uC2DC\uB3C4\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.")); 1354 } }), { data: It } = dt({ queryKey: ["vocabulary", "handwriting", "personal", Y], queryFn: () => Oe.fetchHandwriting("personal", Y), select: (t) => t.data, enabled: A, staleTime: 6e4 }), Zt = a.useMemo(() => Dn(It), [It, Y]), ze = a.useCallback((t) => { 1355 var r, c; 1356 return { academyId: (r = i == null ? void 0 : i.academy_id) != null ? r : 0, userId: (c = i == null ? void 0 : i.id) != null ? c : 0, scope: "vocab-personal", containerId: "personal", itemId: t }; 1357 }, [i == null ? void 0 : i.academy_id, i == null ? void 0 : i.id]), Jt = a.useCallback((t, r) => { 1358 Ee(String(t), r); 1359 }, [Ee]), Mt = a.useCallback((t, r) => { 1360 we.current[t] = r, St(r ? `SERVER_SAVE_DISABLED key=personal:${t} strokes=${r.strokes.length} updatedAt=${r.updatedAt}` : `SERVER_SAVE_DISABLED key=personal:${t} cleared`); 1361 }, []), er = a.useMemo(() => { 1362 var t, r; 1363 return In("vocab-personal", (t = i == null ? void 0 : i.academy_id) != null ? t : 0, (r = i == null ? void 0 : i.id) != null ? r : 0, "personal"); 1364 }, [i == null ? void 0 : i.academy_id, i == null ? void 0 : i.id]), { isNativeActive: it, undo: Rt, clear: At }
1364 = An({ entityId: String((f = i == null ? void 0 : i.id) != null ? f : 0), enabled: A, containerRef: le, tool: G, color: F, scopeName: "vocab-personal", nativeDrawingStorageKey: er, buildStorageScope: ze, recordDraft: Mt, setActiveEntryId: K, setPersistenceStatus: Ee }); 1365 a.useEffect(() => { 1366 H.current = it, U.current = Rt, oe.current = At; 1367 }, [it, Rt, At, H, U, oe]); 1368 const tr = a.useCallback(() => { 1369 Object.entries(Be).forEach(([t, r]) => { 1370 var c, N; 1371 const w = Number(t), B = (c = we.current[w]) != null ? c : null; 1372 if (((N = r.local) == null ? void 0 : N.phase) !== "error") return; 1373 Ee(t, { target: "local", phase: "saving", retryable: false, updatedAt: B == null ? void 0 : B.updatedAt }), (B ? pr(ze(w), B) : br(ze(w))).then(() => { 1374 Ee(t, { target: "local", phase: "saved", retryable: false, updatedAt: B == null ? void 0 : B.updatedAt }); 1375 }).catch((ie) => { 1376 const Q = ie instanceof jt ? ie : new jt("unknown_local_error"); 1377 Ee(t, { target: "local", phase: "error", reasonCode: Q.reasonCode, message: yr(Q.reasonCode, "vocab-personal"), retryable: true, updatedAt: B == null ? void 0 : B.updatedAt }); 1378 }); 1379 }); 1380 }, [ze, Be, Ee]), Ue = q.size, pt = ae.length > 0 && ae.every((t) => q.has(t.id)), rr = ae.filter((t) => q.has(t.id) && t.ai_status === "failed").map((t) => t.id), Te = En({ entries: re, actionableEntries: et, activeStatus: b, onStatusChange: Gt, onMoveEntries: Pe, onBeforeOpen: Ct, disabled: pe.isPending }); 1381 return e.jsxs("div", { className: `px-2 pt-4 pb-20 md:p-6 md:pb-6 max-w-5xl mx-auto ${Ue > 0 ? "pb-56 sm:pb-36 md:pb-28" : ""}`, children: [e.jsx(xn, { message: (u = L == null ? void 0 : L.message) != null ? u : null, onRetry: tr, className: "mb-4" }), e.jsxs("div", { className: "mb-4", children: [e.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2", children: [e.jsx(J, { avoidNativeButton: A, onClick: n, className: "p-2 hover:bg-gray-100 rounded-lg transition-colors flex-shrink-0", children: e.jsx(wr, { className: "w-5 h-5 text-gray-600" }) }), e.jsxs("h1", { className: "text-xl md:text-2xl font-bold text-gray-900 flex items-center gap-2 flex-none whitespace-nowrap", children: [e.jsx(yt, { className: "w-6 h-6 text-primary flex-shrink-0" }), "\uB098\uC758 \uB2E8\uC5B4\uC7A5"] }), me && e.jsxs("span", { className: "text-sm text-gray-500 flex-shrink-0 whitespace-nowrap", children: [fe.length, "\uAC1C \uB2E8\uC5B4"] }), e.jsxs("div", { className: "hidden md:flex md:flex-wrap items-center justify-end gap-1.5 min-w-0 ml-auto", children: [e.jsx("select", { value: x, onChange: (t) => m(t.target.value), className: "text-sm border border-gray-200 rounded-lg px-3 py-1.5 text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary/30", children: nn.map((t) => e.jsx("option", { value: t.value, children: t.label }, t.value)) }), Ye.length > 0 && e.jsx(sn, { sources: Ye, selected: v, onChange: j, avoidNativeButtons: A }), rt && e.jsxs(e.Fragment, { children: [e.jsxs(J, { avoidNativeButton: A, onClick: nt, disabled: Re.length === 0, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${Ae ? "bg-red-500 text-white border-red-500" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"} disabled:opacity-40`, children: [Ae ? e.jsx(Ot, { className: "w-3.5 h-3.5" }) : e.jsx(Nt, { className: "w-3.5 h-3.5" }), Ae ? "\uC911\uC9C0" : "\uC7AC\uC0DD"] }), e.jsx(J, { avoidNativeButton: A, onClick: () => Z((t) => !t), className: `flex items-center justify-center w-7 h-7 rounded-lg border transition-colors ${W ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-400 hover:text-gray-600 hover:bg-gray-50"}`, title: "\uC74C\uC131 \uC124\uC815", children: e.jsx(cn, { className: "w-3.5 h-3.5" }) })] }), e.jsxs(J, { avoidNativeButton: A, onClick: He, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${y ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(Pt, { className: "w-3.5 h-3.5" }), y ? "\uC6D0\uB798\uC21C\uC11C" : "\uB79C\uB364"] }), e.jsxs(J, { avoidNativeButton: A, onClick: () => p((t) => !t), className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${o ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [o ? e.jsx(Kt, { className: "w-3.5 h-3.5" }) : e.jsx(qt, { className: "w-3.5 h-3.5" }), o ? "\uB73B \uBCF4\uAE30" : "\uB73B \uAC00\uB9AC\uAE30"] }), e.jsxs(J, { avoidNativeButton: A, onClick: () => P((t) => !t), className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${T ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(yn, { className: "w-3.5 h-3.5" }), T ? "1\uC5F4" : "2\uC5F4"] }), A && e.jsxs(J, { avoidNativeButton: A, onClick: () => z((t) => !t), className: "flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-all bg-white border-gray-200 text-gray-600 hover:bg-gray-50", children: [te ? e.jsx(Ut, { className: "w-3.5 h-3.5" }) : e.jsx(Wt, { className: "w-3.5 h-3.5" }), te ? "\uCE74\uB4DC \uC791\uAC8C" : "\uCE74\uB4DC \uD06C\uAC8C"] }), e.jsxs(J, { avoidNativeButton: A, onClick: () => { 1382 Ae && at(), ge((t) => !t); 1383 }, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-all ${A ? "bg-primary text-white border-primary shadow-sm" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(dn, { className: "w-3.5 h-3.5" }), A ? "\uD544\uAE30 ON" : "\uD544\uAE30"] })] })] }), e.jsxs("div", { className: "flex md:hidden items-center gap-1.5 mt-2", children: [e.jsx("select", { value: x, onChange: (t) => m(t.target.value), className: "text-sm border border-gray-200 rounded-lg px-2 py-1.5 text-gray-700 focus:outline-none focus:ring-primary/30", children: nn.map((t) => e.jsx("option", { value: t.value, children: t.label }, t.value)) }), Ye.length > 0 && e.jsx(sn, { sources: Ye, selected: v, onChange: j, compact: true, avoidNativeButtons: A }), rt && e.jsx(J, { avoidNativeButton: A, onClick: nt, disabled: Re.length === 0, className: `flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${Ae ? "bg-red-500 text-white border-red-500" : "bg-white border-gray-200 text-gray-600"} disabled:opacity-40`, title: Ae ? "\uC7AC\uC0DD \uC911\uC9C0" : "\uB2E8\uC5B4 \uC7AC\uC0DD", children: Ae ? e.jsx(Ot, { className: "w-3.5 h-3.5" }) : e.jsx(Nt, { className: "w-3.5 h-3.5" }) }), e.jsx(J, { avoidNativeButton: A, onClick: He, className: `flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${y ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600"}`, title: y ? "\uC6D0\uB798\uC21C\uC11C" : "\uB79C\uB364", children: e.jsx(Pt, { className: "w-3.5 h-3.5" }) }), e.jsx(J, { avoidNativeButton: A, onClick: () => p((t) => !t), className: `flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${o ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600"}`, title: o ? "\uB73B \uBCF4\uAE30" : "\uB73B \uAC00\uB9AC\uAE30", children: o ? e.jsx(Kt, { className: "w-3.5 h-3.5" }) : e.jsx(qt, { className: "w-3.5 h-3.5" }) }), A && e.jsx(J, { avoidNativeButton: A, onClick: () => z((t) => !t), className: "flex items-center justify-center w-8 h-8 rounded-lg border transition-all bg-white border-gray-200 text-gray-600", title: te ? "\uCE74\uB4DC \uC791\uAC8C" : "\uCE74\uB4DC \uD06C\uAC8C", children: te ? e.jsx(Ut, { className: "w-3.5 h-3.5" }) : e.jsx(Wt, { className: "w-3.5 h-3.5" }) })] })] }), Te.folderTabs, Te.overlays, W && Qe.length > 0 && e.jsx(Mn, { enVoices: Qe, koVoices: ft, selectedEnVoice: st, selectedKoVoice: xt, onEnVoiceChange: k, onKoVoiceChange: I, onClose: () => Z(false), avoidNativeButtons: A }), A && e.jsx(gn, { tool: G, onToolChange: ue, penColor: F, onPenColorChange: Ne, onUndo: ee, onClear: he, canUndo: D != null, canClear: true }), Ue > 0 && e.jsxs("div", { className: "fixed bottom-0 left-0 right-0 z-50 flex flex-wrap items-center justify-center gap-2 border-t border-gray-200 bg-white pb-[max(0.75rem,env(safe-area-inset-bottom))] pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] pt-3 shadow-[0_-4px_12px_rgba(0,0,0,0.08)]", children: [e.jsxs("span", { className: "text-sm font-semibold text-primary", children: [Ue, "\uAC1C \uC120\uD0DD\uB428"] }), b === "known" && e.jsxs(e.Fragment, { children: [e.jsx("button", { onClick: () => { 1384 Se("default"); 1385 }, disabled: pe.isPending, className: "flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-50", children: "\uAE30\uBCF8\uC73C\uB85C" }), e.jsx("button", { onClick: () => { 1386 Se("memorized"); 1387 }, disabled: pe.isPending, className: "flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-primary/30 bg-primary/5 text-primary hover:bg-primary/10 transition-colors disabled:opacity-50", children: "\uC554\uAE30\uB428\uC73C\uB85C" })] }), b !== "known" && e.jsxs("button", { onClick: () => { 1388 gt(); 1389 }, disabled: pe.isPending, className: "flex items-center gap-1.5 rounded-lg border border-primary/30 bg-primary/5 px-3 py-1.5 text-sm text-primary transition-colors hover:bg-primary/10 disabled:opacity-50", children: [e.jsx(or, { className: "h-3.5 w-3.5" }), "\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9"] }), rr.length > 0 && e.jsxs("button", { onClick: () => Ge.mutate(rr), disabled: Ge.isPending || !se, title: se ? void 0 : tn, className: "flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-amber-300 text-amber-700 hover:bg-amber-50 transition-colors disabled:opacity-50", children: [e.jsx(Ir, { className: "w-3.5 h-3.5" }), "\uC7AC\uBD84\uC11D"] }), e.jsxs("button", { onClick: () => M(true), className: "flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg text-red-600 bg-red-50 border border-red-200 hover:bg-red-100 transition-colors", children: [e.jsx(Mr, { className: "w-3.5 h-3.5" }), "\uC0AD\uC81C"] }), e.jsxs("button", { onClick: () => { 1390 Xe(), vt(b); 1391 }, className: "flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700 transition-colors", children: [e.jsx(_t, { className: "w-4 h-4" }), "\uD574\uC81C"] })] }), C && e.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40", children: e.jsxs("div", { className: "bg-white rounded-2xl p-6 shadow-xl max-w-sm w-full mx-4", children: [e.jsx("h3", { className: "font-semibold text-gray-900 mb-2", children: "\uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?" }), e.jsxs("p", { className: "text-sm text-gray-500 mb-5", children: [Ue, "\uAC1C\uC758 \uB2E8\uC5B4\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."] }), e.jsxs("div", { className: "flex gap-2 justify-end", children: [e.jsx("button", { onClick: () => M(false), className: "px-4 py-2 rounded-lg border border-gray-200 text-sm text-gray-600 hover:bg-gray-50", children: "\uCDE8\uC18C" }), e.jsxs("button", { onClick: () => Et.mutate(Array.from(q)), disabled: Et.isPending, className: "px-4 py-2 rounded-lg bg-red-500 text-white text-sm hover:bg-red-600 disabled:opacity-50 flex items-center gap-1.5", children: [Et.isPending && e.jsx(We, { className: "w-3.5 h-3.5 animate-spin" }), "\uC0AD\uC81C"] })] })] }) }), tt ? e.jsx(vr, { message: "\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uC774\uBA70 \uC800\uC7A5\uD55C \uB2E8\uC5B4\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." }) : Le ? e.jsx("div", { className: "flex items-center justify-center h-48", children: e.jsx(We, { className: "w-8 h-8 animate-spin text-primary" }) }) : be ? e.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [e.jsx(Ke, { className: "mb-4 h-12 w-12 text-amber-500" }), e.jsx("h2", { className: "mb-2 text-lg font-semibold text-gray-700", children: "\uB2E8\uC5B4\uC7A5\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "mb-5 text-sm text-gray-500", children: "\uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694." }), e.jsx("button", { type: "button", onClick: () => {
1392 qe(); 1393 }, className: "rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm text-gray-700 hover:bg-gray-50", children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] }) : _e.length === 0 ? e.jsxs("div", { className: "text-center py-16", children: [e.jsx(yt, { className: "w-16 h-16 mx-auto text-gray-300 mb-4" }), e.jsx("h2", { className: "text-xl font-semibold text-gray-700 mb-2", children: "\uB2E8\uC5B4\uC7A5\uC774 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "text-gray-500", children: "\uC219\uC81C \uBB38\uC81C\uB97C \uD480\uBA74\uC11C \uB2E8\uC5B4\uB97C \uD0ED\uD574\uC11C \uC800\uC7A5\uD574\uBCF4\uC138\uC694" })] }) : fe.length === 0 ? e.jsxs("div", { className: "text-center py-16", children: [e.jsx(yt, { className: "w-16 h-16 mx-auto text-gray-300 mb-4" }), e.jsx("h2", { className: "text-xl font-semibold text-gray-700 mb-2", children: "\uD544\uD130\uC640 \uC77C\uCE58\uD558\uB294 \uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "text-gray-500 mb-4", children: "\uC120\uD0DD\uD55C \uCD9C\uCC98\uC5D0 \uC800\uC7A5\uB41C \uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }), e.jsx("button", { onClick: () => j([]), className: "px-4 py-2 rounded-lg border border-gray-200 text-sm text-gray-700 hover:bg-gray-50", children: "\uD544\uD130 \uD574\uC81C" })] }) : ae.length === 0 ? e.jsxs("div", { className: "text-center py-14 rounded-xl border border-dashed border-gray-200 bg-gray-50/60", children: [e.jsx(yt, { className: "w-12 h-12 mx-auto text-gray-300 mb-3" }), e.jsxs("h2", { className: "text-base font-semibold text-gray-700", children: [ut[b], " \uD3F4\uB354\uC5D0 \uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4"] }), e.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "\uB2E4\uB978 \uD3F4\uB354\uC5D0\uC11C \uB2E8\uC5B4\uB97C \uC774\uB3D9\uD574 \uBCF4\uC138\uC694." })] }) : A ? e.jsxs(e.Fragment, { children: [Te.folderActions && e.jsx("div", { className: "py-1.5", children: Te.folderActions }), e.jsx("div", { ref: le, className: T ? "flex flex-col gap-0.5 md:grid md:grid-cols-2 md:grid-flow-col md:gap-x-2" : "space-y-0.5", style: T ? { gridTemplateRows: `repeat(${Math.ceil(ae.length / 2)}, auto)` } : void 0, children: ae.map((t, r) => { 1394 var c, N; 1395 const w = D === t.id; 1396 return e.jsx(Tn, { entryId: t.id, index: r, active: w, largeCards: te, isNativeActive: it, onActivate: () => K(t.id), renderOverlay: () => { 1397 var B; 1398 return e.jsx(hn, { ref: ($) => { 1399 $ ? R.current.set(t.id, $) : R.current.delete(t.id); 1400 }, questionId: t.id, sheetId: 0, enabled: A, disableNative: true, deviceId: Y, serverDraft: Zt[String(t.id)], onDraftChange: Mt, syncHydratedDraft: false, touchActionMode: "pan-y", debugPanelActive: w, storageKeyPrefix: "vocab-p-", storageScope: ze(t.id), onPersistenceStatusChange: ($) => Jt(t.id, $), tool: G, onToolChange: ue, penColor: F, onPenColorChange: Ne, hideToolbar: true, onActivate: () =>
1400 K(t.id), entityType: "vocabulary", entityId: String((B = i == null ? void 0 : i.id) != null ? B : 0), subEntityId: String(t.id), tapZoneSelector: "button[data-handwriting-tap-through]" }); 1401 }, children: e.jsxs("div", { className: "absolute inset-0 z-[11] pointer-events-none flex items-center gap-3 px-4", children: [e.jsx("input", { type: "checkbox", checked: q.has(t.id), onChange: (B) => ne(r, B.nativeEvent.shiftKey), className: "w-5 h-5 md:w-4 md:h-4 rounded border-gray-300 text-primary focus:ring-primary/30 pointer-events-auto" }), e.jsx("span", { className: "font-semibold text-gray-900 text-base", children: (c = t.base_form) != null ? c : t.word }), t.base_form && t.base_form !== t.word && e.jsxs("span", { className: "text-sm text-gray-400", children: ["(", t.word, ")"] }), e.jsx("button", { type: "button", "data-handwriting-tap-through": true, onClick: o ? (B) => { 1402 B.stopPropagation(), Ie(t.id); 1403 } : void 0, className: `text-base ml-auto bg-transparent border-0 p-0 whitespace-pre-line text-right ${o ? "pointer-events-auto cursor-pointer select-none active:bg-gray-100 rounded px-1" : "pointer-events-none"} ${o && !V.has(t.id) ? "text-gray-400" : "text-gray-600"}`, children: !o || V.has(t.id) ? (N = t.meaning) != null ? N : "-" : "\u2022\u2022\u2022\u2022" })] }) }, t.id); 1404 }) })] }) : T ? e.jsxs(e.Fragment, { children: [e.jsxs("div", { className: "hidden md:block bg-white rounded-xl border border-gray-200 overflow-hidden", children: [Te.folderActions && e.jsx("div", { className: "flex items-center border-b border-gray-100 bg-gray-50/60 px-2 py-2.5", children: Te.folderActions }), e.jsx("div", { ...Ce, className: "grid grid-cols-2 grid-flow-col py-2", style: { ...Ce.style, gridTemplateRows: `repeat(${Math.ceil(ae.length / 2)}, auto)` }, children: ae.map((t, r) => { 1405 var c, N; 1406 return e.jsxs("div", { "data-vocab-select-index": r, ref: ce === r ? (w) => { 1407 $e.current = w; 1408 } : void 0, onClick: () => ne(r, false), className: `flex items-center gap-2 px-4 py-1.5 cursor-pointer transition-colors ${r < Math.ceil(ae.length / 2) ? "border-r border-gray-100" : ""} ${ce === r ? "bg-yellow-50" : q.has(t.id) ? "bg-primary/5" : "hover:bg-gray-50/50"}`, children: [e.jsx("span", { "data-vocab-drag-handle": true, onClick: (w) => { 1409 w.stopPropagation(), ne(r, false); 1410 }, className: "-m-3 flex flex-shrink-0 items-center justify-center p-3", style: { touchAction: "none" }, children: e.jsx("input", { type: "checkbox", checked: q.has(t.id), onChange: (w) => { 1411 w.stopPropagation(), ne(r, w.nativeEvent.shiftKey); 1412 }, onClick: (w) => w.stopPropagation(), className: "rounded border-gray-300 text-primary focus:ring-primary/30" }) }), e.jsxs("span", { className: "text-xs text-gray-400 w-6 text-right flex-shrink-0", children: [r + 1, "."] }), e.jsx("span", { className: "font-medium text-gray-900 truncate", children: (c = t.base_form) != null ? c : t.word }), e.jsx("span", { onClick: o && t.ai_status !== "pending" && t.ai_status !== "failed" ? (w) => { 1413 w.stopPropagation(), Ie(t.id); 1414 } : void 0, className: `text-sm ml-auto whitespace-pre-line text-right ${o ? "cursor-pointer select-none" : ""} ${o && !V.has(t.id) && t.ai_status !== "pending" && t.ai_status !== "failed" ? "text-gray-400" : "text-gray-600"}`, children: t.ai_status === "pending" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-amber-600", children: [e.jsx(We, { className: "w-3 h-3 animate-spin" }), "\uBD84\uC11D\uC911"] }) : t.ai_status === "failed" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-red-600", children: [e.jsx(Ke, { className: "w-3 h-3" }), "\uC2E4\uD328"] }) : !o || V.has(t.id) ? (N = t.meaning) != null ? N : "-" : "\u2022\u2022\u2022\u2022" })] }, t.id); 1415 }) })] }), e.jsxs("div", { ...Ce, "data-testid": "personal-vocabulary-selection-surface-mobile", className: "md:hidden bg-white rounded-xl border border-gray-200 overflow-hidden", children: [e.jsxs("div", { className: "flex items-center gap-1 px-2 py-2.5 border-b border-gray-100 bg-gray-50/60", children: [e.jsx("input", { type: "checkbox", checked: pt, onChange: (t) => ve(t.target.checked), className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30" }), e.jsx("span", { className: "text-sm text-gray-500", children: "\uC804\uCCB4 \uC120\uD0DD" }), Te.folderActions] }), ae.map((t, r) => { 1416 var c, N; 1417 return e.jsxs("div", { "data-vocab-select-index": r, ref: ce === r ? (w) => { 1418 $e.current = w; 1419 } : void 0, onClick: () => ne(r, false), className: `flex items-center gap-0.5 px-2 py-2.5 border-b border-gray-50 last:border-b-0 transition-colors cursor-pointer active:bg-gray-100 ${ce === r ? "bg-yellow-50 ring-1 ring-inset ring-yellow-300" : q.has(t.id) ? "bg-primary/5" : ""}`, children: [e.jsx("span", { "data-vocab-drag-handle": true, className: "-m-2.5 flex flex-shrink-0 items-center justify-center p-2.5", style: { touchAction: "
1419none" }, children: e.jsx("input", { type: "checkbox", checked: q.has(t.id), readOnly: true, className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30 pointer-events-none" }) }), e.jsx("span", { className: "text-xs text-gray-400 w-4 text-center flex-shrink-0", children: r + 1 }), e.jsx("span", { className: "font-medium text-gray-900 flex-1 min-w-0 line-clamp-2 ml-0.5", children: (c = t.base_form) != null ? c : t.word }), e.jsx("span", { onClick: o && t.ai_status !== "pending" && t.ai_status !== "failed" ? (w) => { 1420 w.stopPropagation(), Ie(t.id); 1421 } : void 0, className: `text-sm flex-shrink-0 whitespace-pre-line text-right ${o && t.ai_status !== "pending" && t.ai_status !== "failed" ? "w-[42%] py-3 -my-2.5 pl-2 -mr-1 pr-1 cursor-pointer select-none active:bg-gray-100 rounded-md" : "max-w-[40%]"} ${o && !V.has(t.id) && t.ai_status !== "pending" && t.ai_status !== "failed" ? "text-gray-400" : "text-gray-600"}`, children: t.ai_status === "pending" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-amber-600", children: [e.jsx(We, { className: "w-3 h-3 animate-spin" }), "\uBD84\uC11D\uC911"] }) : t.ai_status === "failed" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-red-600", children: [e.jsx(Ke, { className: "w-3 h-3" }), "\uC2E4\uD328"] }) : !o || V.has(t.id) ? (N = t.meaning) != null ? N : "-" : "\u2022\u2022\u2022\u2022" })] }, t.id); 1422 })] })] }) : e.jsxs(e.Fragment, { children: [e.jsxs("div", { className: "hidden md:block bg-white rounded-xl border border-gray-200 overflow-hidden", children: [Te.folderActions && e.jsx("div", { className: "flex items-center border-b border-gray-100 bg-gray-50/60 px-2 py-2.5", children: Te.folderActions }), e.jsxs("table", { className: "w-full", children: [e.jsx("thead", { children: e.jsxs("tr", { className: "border-b border-gray-100 bg-gray-50/60", children: [e.jsx("th", { className: "w-10 px-4 py-3", children: e.jsx("input", { type: "checkbox", checked: pt, onChange: (t) => ve(t.target.checked), className: "rounded border-gray-300 text-primary focus:ring-primary/30" }) }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-32", children: "\uC6D0\uD615" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-40", children: "\uD55C\uAE00 \uB73B" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "\uC608\uBB38" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-36", children: "\uCD9C\uCC98" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-28", children: "\uB0A0\uC9DC" }), e.jsx("th", { className: "w-10 px-4 py-3" })] }) }), e.jsx("tbody", { ...Ce, children: ae.map((t, r) => { 1423 var c, N, w, B, $, ie; 1424 return e.jsxs("tr", { "data-vocab-select-index": r, className: `border-b border-gray-50 last:border-b-0 hover:bg-gray-50/50 transition-colors ${ce === r ? "bg-yellow-50 ring-1 ring-inset ring-yellow-300" : q.has(t.id) ? "bg-primary/5" : ""}`, children: [e.jsx("td", { className: "px-4 py-3", children: e.jsx("span", { "data-vocab-drag-handle": true, onClick: (Q) => { 1425 Q.stopPropagation(), ne(r, false); 1426 }, className: "-m-3 inline-flex items-center justify-center p-3", style: { touchAction: "none" }, children: e.jsx("input", { type: "checkbox", checked: q.has(t.id), onChange: (Q) => ne(r, Q.nativeEvent.shiftKey), onClick: (Q) => Q.stopPropagation(), className: "rounded border-gray-300 text-primary focus:ring-primary/30" }) }) }), e.jsxs("td", { onClick: () => ne(r, false), className: "px-4 py-3 cursor-pointer", children: [e.jsx("div", { className: "font-medium text-gray-900", children: (c = t.base_form) != null ? c : t.word }), t.base_form && t.base_form !== t.word && e.jsx("div", { className: "text-xs text-gray-400 mt-0.5", children: t.word })] }), e.jsx("td", { onClick: o && t.ai_status !== "pending" && t.ai_status !== "failed" ? () => Ie(t.id) : void 0, className: `px-4 py-3 ${o && t.ai_status !== "pending" && t.ai_status !== "failed" ? "curs
1426or-pointer select-none" : ""}`, children: t.ai_status === "pending" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded-full", children: [e.jsx(We, { className: "w-3 h-3 animate-spin" }), "\uBD84\uC11D\uC911"] }) : t.ai_status === "failed" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-red-600 bg-red-50 px-2 py-0.5 rounded-full", children: [e.jsx(Ke, { className: "w-3 h-3" }), "\uBD84\uC11D \uC2E4\uD328"] }) : e.jsx("span", { className: `text-sm whitespace-pre-line ${o && !V.has(t.id) ? "text-gray-400" : "text-gray-700"}`, children: !o || V.has(t.id) ? (N = t.meaning) != null ? N : "-" : "\u2022\u2022\u2022\u2022" }) }), e.jsx("td", { className: "px-4 py-3", children: e.jsx("p", { className: "text-sm text-gray-600 whitespace-pre-line", children: t.sentence || "-" }) }), e.jsx("td", { className: "px-4 py-3", children: e.jsx("span", { className: "text-xs text-gray-400 truncate block max-w-[130px]", title: (B = (w = t.source_label) != null ? w : t.homework_title) != null ? B : void 0, children: (ie = ($ = t.source_label) != null ? $ : t.homework_title) != null ? ie : "-" }) }), e.jsx("td", { className: "px-4 py-3", children: e.jsx("span", { className: "text-xs text-gray-400", children: new Date(t.created_at).toLocaleDateString("ko-KR", { timeZone: "Asia/Seoul", month: "2-digit", day: "2-digit" }) }) }), e.jsx("td", { className: "px-4 py-3", children: e.jsxs("div", { className: "flex items-center gap-1", children: [t.ai_status === "failed" && e.jsx("button", { onClick: () => Ge.mutate([t.id]), disabled: Ge.isPending || !se, title: se ? "\uB2E4\uC2DC \uBD84\uC11D" : tn, className: "w-7 h-7 flex items-center justify-center rounded-lg text-amber-500 hover:bg-amber-50 transition-colors disabled:opacity-50", children: e.jsx(Ir, { className: "w-3.5 h-3.5" }) }), e.jsx("button", { onClick: () => ht.mutate(t.id), disabled: ht.isPending, title: "\uC0AD\uC81C", className: "w-7 h-7 flex items-center justify-center rounded-lg text-gray-400 hover:text-red-500 hover:bg-red-50 transition-colors disabled:opacity-50", children: e.jsx(Mr, { className: "w-3.5 h-3.5" }) })] }) })] }, t.id); 1427 }) })] })] }), e.jsxs("div", { ...Ce, "data-testid": "personal-vocabulary-selection-surface-mobile", className: "md:hidden bg-white rounded-xl border border-gray-200 overflow-hidden", children: [e.jsxs("div", { className: "flex items-center gap-1 px-2 py-2.5 border-b border-gray-100 bg-gray-50/60", children: [e.jsx("input", { type: "checkbox", checked: pt, onChange: (t) => ve(t.target.checked), className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30" }), e.jsx("span", { className: "text-sm text-gray-500", children: "\uC804\uCCB4 \uC120\uD0DD" }), Te.folderActions] }), ae.map((t, r) => { 1428 var c, N; 1429 return e.jsxs("div", { "data-vocab-select-index": r, ref: ce === r ? (w) => { 1430 $e.current = w; 1431 } : void 0, onClick: () => ne(r, false), className: `flex items-center gap-0.5 px-2 py-2.5 border-b border-gray-50 last:border-b-0 transition-colors cursor-pointer active:bg-gray-100 ${ce === r ? "bg-yellow-50 ring-1 ring-inset ring-yellow-300" : q.has(t.id) ? "bg-primary/5" : ""}`, children: [e.jsx("span", { "data-vocab-drag-handle": true, className: "-m-2.5 flex flex-shrink-0 items-center justify-center p-2.5", style: { touchAction: "none" }, children: e.jsx("input", { type: "checkbox", checked: q.has(t.id), readOnly: true, className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30 pointer-events-none" }) }), e.jsx("span", { className: "text-xs text-gray-400 w-4 text-center flex-shrink-0", children: r + 1 }), e.jsx("span", { className: "font-medium text-gray-900 flex-1 min-w-0 line-clamp-2 ml-0.5", children: (c = t.base_form) != null ? c : t.word }), e.jsx("span", { onClick: o && t.ai_status !== "pending" && t.ai_status !== "failed" ? (w) => { 1432 w.stopPropagation(), Ie(t.id); 1433 } : void 0, className: `text-sm flex-shrink-0 whitespace-pre-line text-right ${o && t.ai_status !== "pending" && t.ai_status !== "failed" ? "w-[42%] py-3 -my-2.5 pl-2 -mr-1 pr-1 cursor-pointer select-none active:bg-gray-100 rounded-md" : "max-w-[40%]"} ${o && !V.has(t.id) && t.ai_status !== "pending" && t.ai_status !== "failed" ? "text-gray-400" : "text-gray-600"}`, children: t.ai_status === "pending" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-amber-600", children: [e.jsx(We, { className: "w-3 h-3 animate-spin" }), "\uBD84\uC11D\uC911"] }) : t.ai_status === "failed" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-red-600", children: [e.jsx(Ke, { className: "w-3 h-3" }), "\uC2E4\uD328"] }) : !o || V.has(t.id) ? (N = t.meaning) != null ? N : "-" : "\u2022\u2022\u2022\u2022" })] }, t.id); 1434 })] })] })] }); 1435} 1436function Fa({ bookId: n, onBack: s }) { 1437 var l, d, f, u, h; 1438 const i = Xt(), x = xr((r) => r.user), [m, b] = a.useState("default"), { data: g, isLoading: v, isError: j, refetch: C } = dt({ queryKey: ["vocabulary", "book-entries", n], queryFn: () => Oe.getBookEntries(n), select: (r) => r.data, staleTime: 6e4 }), M = gr(), o = g == null ? void 0 : g.book, p = a.useMemo(() => { 1439 var r; 1440 return (r = g == null ? void 0 : g.entries) != null ? r : []; 1441 }, [g == null ? void 0 : g.entries]), y = !M && g === void 0, S = M && g === void 0 && j, [_, E] = a.useState(false), [T, P] = a.useState(false), [V, xe] = a.useState(0), [O, Y] = a.useState(false), [A, ge] = a.useState(/* @__PURE__ */ new Set()), G = a.useRef(/* @__PURE__ */ new Map()), { deviceId: ue, drawingEnabled: F, setDrawingEnabled: Ne, handwritingTool: R, setHandwritingTool: D, handwritingColor: K, setHandwritingColor: ee, handwritingHandlesRef: he, activeHandwritingEntryId: te, setActiveHandwritingEntryId: z, handleHandwritingUndo: H, handleHandwritingClear: U, largeC
1441ards: oe, setLargeCards: we, nativeActiveRef: le, nativeUndoRef: L, nativeClearRef: Ee } = Rn(), Be = a.useRef({}), pe = a.useRef(null), { currentError: Ie, setPersistenceStatus: me, statusByKey: Le } = bn("local"); 1442 pn(F); 1443 const Ve = kn({ targetType: "book", bookId: n, academyId: (l = x == null ? void 0 : x.academy_id) != null ? l : 0, userId: (d = x == null ? void 0 : x.id) != null ? d : 0, deviceId: ue }), qe = a.useCallback((r) => { 1444 const c = G.current.get(r); 1445 c && (clearTimeout(c), G.current.delete(r)), ge((N) => { 1446 const w = new Set(N); 1447 if (w.has(r)) return w.delete(r), w; 1448 w.add(r); 1449 const B = setTimeout(() => { 1450 ge(($) => { 1451 const ie = new Set($); 1452 return ie.delete(r), ie; 1453 }), G.current.delete(r); 1454 }, 3e3); 1455 return G.current.set(r, B), w; 1456 }); 1457 }, []); 1458 a.useEffect(() => () => { 1459 G.current.forEach((r) => clearTimeout(r)); 1460 }, []), a.useEffect(() => { 1461 O || (G.current.forEach((r) => clearTimeout(r)), G.current.clear(), ge(/* @__PURE__ */ new Set())); 1462 }, [O]); 1463 const se = a.useMemo(() => p.map((r) => ({ id: r.id, word: r.word, meaning: r.meaning, memorization_status: Qt(r.memorization_status) })), [p]), _e = a.useMemo(() => p.filter((r) => Qt(r.memorization_status) === m), [m, p]), re = a.useMemo(() => { 1464 if (!T) return _e; 1465 const r = [..._e]; 1466 let c = V; 1467 for (let N = r.length - 1; N > 0; N--) { 1468 c = (c * 16807 + 11) % 2147483647; 1469 const w = c % (N + 1); 1470 [r[N], r[w]] = [r[w], r[N]]; 1471 } 1472 return r; 1473 }, [_e, T, V]), et = () => { 1474 T ? P(false) : (xe(Date.now()), P(true)); 1475 }, Ye = a.useMemo(() => re.map((r) => ({ id: r.id, source: "book", word: r.word, meaning: r.meaning })), [re]), { isSupported: tt, isPlaying: be, currentIndex: fe, toggleTTS: Fe, stopTTS: ae, playItem: He, enVoices: Re, koVoices: rt, selectedEnVoice: Ae, selectedKoVoice: mt, setSelectedEnVoice: nt, setSelectedKoVoice: at } = jr(Ye), [Qe, ft] = a.useState(false), st = a.useRef(null), xt = a.useRef(null); 1476 a.useEffect(() => { 1477 var r, c; 1478 fe >= 0 && ((r = st.current) == null || r.scrollIntoView({ behavior: "smooth", block: "center" }), (c = xt.current) == null || c.scrollIntoView({ behavior: "smooth", block: "center" })); 1479 }, [fe]); 1480 const k = a.useCallback((r) => r.id, []), { selectedIds: I, setSelectedIds: W, handleSelect: Z, handleSelectAll: ye, clearSelection: ce } = fn(re, k), $e = a.useMemo(() => re.map((r) => r.id), [re]), X = Cn($e, I, W), q = a.useCallback(() => { 1481 be && ae(), F && Ne(false); 1482 }, [F, be, Ne, ae]), ke = async (r, c) => { 1483 try { 1484 await Ve.moveEntries(r, c); 1485 } finally { 1486 ce(); 1487 } 1488 }, ne = (r) => { 1489 q(), ce(), b(r); 1490 }, ve = async (r) => { 1491 try { 1492 await ke([...I], r), de.success(`${r === "default" ? "\uAE30\uBCF8" : "\uC554\uAE30\uB428"} \uD3F4\uB354\uB85C \uC774\uB3D9\uD588\uC2B5\uB2C8\uB2E4.`); 1493 } catch (c) { 1494 de.error(Ze(c, "\uB2E8\uC5B4\uB97C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.")); 1495 } finally { 1496 vt("known"); 1497 } 1498 }, Xe = async () => { 1499 try { 1500 await ke([...I], "known"), de.success("\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9\uD588\uC2B5\uB2C8\uB2E4."); 1501 } catch (r) { 1502 de.error(Ze(r, "\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.")); 1503 } finally { 1504 vt("known"); 1505 } 1506 }, { data: Bt } = dt({ queryKey: Me, queryFn: mn, enabled: false }), Ce = (f = Bt == null ? void 0 : Bt.items) != null ? f : null, Ct = a.useMemo(() => { 1507 const r = /* @__PURE__ */ new Map(); 1508 for (const c of p) r.set(c.id, Ra(c, Ce)); 1509 return r; 1510 }, [p, Ce]), Pe = a.useCallback((r) => { 1511 var c; 1512 return (c = Ct.get(r.id)) != null ? c : "none"; 1513 }, [Ct]), Gt = a.useCallback((r, c, N) => { 1514 var w, B, $; 1515 const ie = new Set(r), Q = (B = (w = i.getQueryData(["vocabulary", "book-entries", n])) == null ? void 0 : w.data) == null ? void 0 : B.entries, lt = Q ? Q.filter((je) => ie.has(je.id) && (c ? Pe(je) !== "saved" : Pe(je) !== "none")) : null, Nr = lt ? Va(i, lt, c, ($ = o == null ? void 0 : o.title) != null ? $ : null, N) : null, kr = Nr != null ? Nr : (lt ? lt.length : r.length) * (c ? 1 : -1); 1516 kr !== 0 && i.setQueryData(["vocabulary", "count"], (je) => { 1517 var Tt; 1518 return typeof ((Tt = je == null ? void 0 : je.data) == null ? void 0 : Tt.total) != "number" ? je : { ...je, data: { ...je.data, total: Math.max(0, je.data.total + kr) } }; 1519 }), i.setQueryData(["vocabulary", "book-entries", n], (je) => { 1520 var Tt; 1521 return (Tt = je == null ? void 0 : je.data) != null && Tt.entries ? { ...je, data: { ...je.data, entries: je.data.entries.map((nr) => ie.has(nr.id) ? { ...nr, in_my_vocab: c, membership: c ? "saved" : "none" } : nr) } } : je; 1522 }); 1523 }, [o == null ? void 0 : o.title, n, Pe, i]), [Se, gt] = a.useState(null), Vt = (r) => { 1524 var c; 1525 const N = (c = r.needs_decision) != null ? c : []; 1526 if (N.length === 0 || !Ce) return 0; 1527 const w = []; 1528 for (const B of N) { 1529 const $ = p.find((Q) => Q.id === B.book_entry_id); 1530 if (!$) continue; 1531 const ie = Yt({ word: $.word, meaning: $.meaning }, Ce); 1532 w.push(mr($, ie.wordMatches, Ce, B.reason)); 1533 } 1534 return w.length === 0 ? 0 : (gt({ readyIds: [], queue: w, decisions: {}, decidedIds: [] }), w.length); 1535 }, ot = Dt({ networkMode: "offlineFirst", mutationFn: async ({ ids: r, decisions: c }) => { 1536 var N, w; 1537 if (!Je()) return await Ht({ action: "add", academyId: (N = x == null ? void 0 : x.academy_id) != null ? N : 0, userId: (w = x == null ? void 0 : x.id) != null ? w : 0, deviceId: ue, bookId: n, bookEntryIds: r, decisions: c }), { queued: true, ids: r, decisions: c }; 1538 const B = Da(c), $ = B ? await Oe.addFromBook(r, B) : await Oe.addFromBook(r); 1539 return { queued: false, ids: r, decisions: c, res: $ }; 1540 }, onSuccess: (r) => { 1541 if (r.queued) { 1542 Gt(r.ids, true, r.decisions), ce(), de.success("\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uB85C \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC5F0\uACB0\uB418\uBA74 \uC790\uB3D9\uC73C\uB85C \uBC18\uC601\uB429\uB2C8\uB2E4."); 1543 return; 1544 } 1545 i.invalidateQueries({ queryKey: ["vocabulary"] }), i.invalidateQueries({ queryKey: ["vocabulary", "book-entries", n] }), $a(r.res.data, Vt(r.res.data)); 1546 }, onError: (r) => { 1547 de.error(Ze(r, "\uB2E8\uC5B4 \uCD94\uAC00\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.")); 1548 } }), ht = a.useCallback((r, c) => { 1549 r.length !== 0 && ot.mutate({ ids: r, decisions: c }); 1550 }, [ot]), Et = a.useCallback((r) => { 1551 const c = new Set(r), N = p.filter(($) => c.has($.id)), w = [], B = []; 1552 for (const $ of N) { 1553 if (!Ce) { 1554 w.push($.id); 1555 continue; 1556 } 1557 const ie = Yt({ word: $.word, meaning: $.meaning }, Ce); 1558 ie.verdict === "conflict" ? B.push(mr($, ie.wordMatches, Ce)) : w.push($.id); 1559 } 1560 if (B.length === 0) { 1561 ht(w, {}); 1562 return; 1563 } 1564 gt({ readyIds: w, queue: B, decisions: {}, decidedIds: [] }); 1565 }, [p, Ce, ht]), Ge = (r) => { 1566 if (!Se) return; 1567 const [c, ...N] = Se.queue, w = r ? { ...Se.decisions, [c.bookEntryId]: r } : Se.decisions, B = r ? [...Se.decidedIds, c.bookEntryId] : Se.decidedIds; 1568 if (N.length > 0) { 1569 gt({ ...Se, queue: N, decisions: w, decidedIds: B }); 1570 return; 1571 } 1572 gt(null), ht([...Se.readyIds, ...B], w); 1573 }, { data: It } = dt({ queryKey: ["vocabulary", "handwriting", "book", n, ue], queryFn: () => Oe.fetchHandwriting("book", ue, n), select: (r) => r.data, enabled: F, staleTime: 6e4 }), Zt = a.useMemo(() => Dn(It), [It, ue]), ze = a.useCallback((r) => { 1574 var c, N; 1575 return { academyId: (c = x == null ? void 0 : x.academy_id) != null ? c : 0, userId: (N = x == null ? void 0 : x.id) != null ? N : 0, scope: "vocab-book", containerId: String(n), itemId: r }; 1576 }, [n, x == null ? void 0 : x.academy_id, x == null ? void 0 : x.id]), Jt = a.useCallback((r, c) => { 1577 me(String(r), c); 1578 }, [me]), Mt = a.useCallback((r, c) => { 1579 Be.current[r] = c, St(c ? `SERVER_SAVE_DISABLED key=book:${r} strokes=${c.strokes.length} updatedAt=${c.updatedAt}` : `SERVER_SAVE_DISABLED key=book:${r} cleared`); 1580 }, []), er = a.useMemo(() => { 1581 var r, c; 1582 return In("vocab-book", (r = x == null ? void 0 : x.academy_id) != null ? r : 0, (c = x == null ? void 0 : x.id) != null ? c : 0, String(n)); 1583 }, [n, x == null ? void 0 : x.academy_id, x == null ? void 0 : x.id]), { isNativeActive: it, undo: Rt, clear: At }
1583 = An({ entityId: String(n), enabled: F, containerRef: pe, tool: R, color: K, scopeName: "vocab-book", nativeDrawingStorageKey: er, buildStorageScope: ze, recordDraft: Mt, setActiveEntryId: z, setPersistenceStatus: me }); 1584 a.useEffect(() => { 1585 le.current = it, L.current = Rt, Ee.current = At; 1586 }, [it, Rt, At, le, L, Ee]); 1587 const tr = a.useCallback(() => { 1588 Object.entries(Le).forEach(([r, c]) => { 1589 var N, w; 1590 const B = Number(r), $ = (N = Be.current[B]) != null ? N : null; 1591 ((w = c.local) == null ? void 0 : w.phase) === "error" && (me(r, { target: "local", phase: "saving", retryable: false, updatedAt: $ == null ? void 0 : $.updatedAt }), ($ ? pr(ze(B), $) : br(ze(B))).then(() => { 1592 me(r, { target: "local", phase: "saved", retryable: false, updatedAt: $ == null ? void 0 : $.updatedAt }); 1593 }).catch((Q) => { 1594 const lt = Q instanceof jt ? Q : new jt("unknown_local_error"); 1595 me(r, { target: "local", phase: "error", reasonCode: lt.reasonCode, message: yr(lt.reasonCode, "vocab-book"), retryable: true, updatedAt: $ == null ? void 0 : $.updatedAt }); 1596 })); 1597 }); 1598 }, [ze, me, Le]), Ue = I.size, pt = re.length > 0 && re.every((r) => I.has(r.id)), Te = re.filter((r) => I.has(r.id)).filter((r) => Pe(r) !== "saved").map((r) => r.id), t = En({ entries: se, activeStatus: m, onStatusChange: ne, onMoveEntries: ke, onBeforeOpen: q, disabled: Ve.isPending, pronunciationSource: "book" }); 1599 return e.jsxs("div", { className: `px-2 pt-2 pb-20 md:p-6 md:pb-6 max-w-5xl mx-auto ${Ue > 0 ? "pb-56 sm:pb-36 md:pb-28" : ""}`, children: [e.jsx(xn, { message: (u = Ie == null ? void 0 : Ie.message) != null ? u : null, onRetry: tr, className: "mb-4" }), e.jsxs("div", { className: "mb-2 md:mb-4", children: [e.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2", children: [e.jsx(J, { avoidNativeButton: F, onClick: s, className: "p-2 hover:bg-gray-100 rounded-lg transition-colors flex-shrink-0", children: e.jsx(wr, { className: "w-5 h-5 text-gray-600" }) }), e.jsx("h1", { className: "text-lg md:text-xl font-bold text-gray-900 flex flex-1 min-w-[12rem]", children: e.jsx("span", { className: "truncate", children: (h = o == null ? void 0 : o.title) != null ? h : "..." }) }), e.jsxs("div", { className: "hidden md:flex md:flex-wrap items-center justify-end gap-1.5 min-w-0 ml-auto", children: [tt && e.jsxs(e.Fragment, { children: [e.jsxs(J, { avoidNativeButton: F, onClick: Fe, disabled: re.length === 0, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${be ? "bg-red-500 text-white border-red-500" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"} disabled:opacity-40`, children: [be ? e.jsx(Ot, { className: "w-3.5 h-3.5" }) : e.jsx(Nt, { className: "w-3.5 h-3.5" }), be ? "\uC911\uC9C0" : "\uC7AC\uC0DD"] }), e.jsx(J, { avoidNativeButton: F, onClick: () => ft((r) => !r), className: `flex items-center justify-center w-7 h-7 rounded-lg border transition-colors ${Qe ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-400 hover:text-gray-600 hover:bg-gray-50"}`, title: "\uC74C\uC131 \uC124\uC815", children: e.jsx(cn, { className: "w-3.5 h-3.5" }) })] }), e.jsxs(J, { avoidNativeButton: F, onClick: et, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${T ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(Pt, { className: "w-3.5 h-3.5" }), T ? "\uC6D0\uB798\uC21C\uC11C" : "\uB79C\uB364"] }), e.jsxs(J, { avoidNativeButton: F, onClick: () => Y((r) => !r), className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${O ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [O ? e.jsx(Kt, { className: "w-3.5 h-3.5" }) : e.jsx(qt, { className: "w-3.5 h-3.5" }), O ? "\uB73B \uBCF4\uAE30" : "\uB73B \uAC00\uB9AC\uAE30"] }), e.jsxs(J, { avoidNativeButton: F, onClick: () => E((r) => !r), className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${_ ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(yn, { className: "w-3.5 h-3.5" }), _ ? "1\uC5F4" : "2\uC5F4"] }), F && e.jsxs(J, { avoidNativeButton: F, onClick: () => we((r) => !r), className: "flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-all bg-white border-gray-200 text-gray-600 hover:bg-gray-50", children: [oe ? e.jsx(Ut, { className: "w-3.5 h-3.5" }) : e.jsx(Wt, { className: "w-3.5 h-3.5" }), oe ? "\uCE74\uB4DC \uC791\uAC8C" : "\uCE74\uB4DC \uD06C\uAC8C"] }), e.jsxs(J, { avoidNativeButton: F, onClick: () => { 1600 be && ae(), Ne((r) => !r); 1601 }, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-all ${F ? "bg-primary text-white border-primary shadow-sm" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(dn, { className: "w-3.5 h-3.5" }), F ? "\uD544\uAE30 ON" : "\uD544\uAE30"] })] })] }), e.jsx("div", { className: "flex items-center justify-end gap-2 mt-1", children: e.jsxs("div", { className: "flex md:hidden items-center gap-1.5 ml-auto flex-shrink-0", children: [tt && e.jsx(J, { avoidNativeButton: F, onClick: Fe, disabled: re.length === 0, className: `flex items-center justify-center w-8 h-8 rounded-lg border transition-colors ${be ? "bg-red-500 text-white border-red-500" : "bg-white border-gray-200 text-gray-600"} disabled:opacity-40`, title: be ? "\uC7AC\uC0DD \uC911\uC9C0" : "\uB2E8\uC5B4 \uC7AC\uC0DD", children: be ? e.jsx(Ot, { className: "w-3.5 h-3.5" }) : e.jsx(Nt, { className: "w-3.5 h-3.5" }) }), e.jsxs(J, { avoidNativeButton: F, onClick: et, className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${T ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [e.jsx(Pt, { className: "w-3.5 h-3.5" }), T ? "\uC6D0\uB798\uC21C\uC11C" : "\uB79C\uB364"] }), e.jsxs(J, { avoidNativeButton: F, onClick: () => Y((r) => !r), className: `flex items-center gap-1 px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors ${O ? "bg-primary/10 border-primary/30 text-primary" : "bg-white border-gray-200 text-gray-600 hover:bg-gray-50"}`, children: [O ? e.jsx(Kt, { className: "w-3.5 h-3.5" }) : e.jsx(qt, { className: "w-3.5 h-3.5" }), O ? "\uB73B \uBCF4\uAE30" : "\uB73B \uAC00\uB9AC\uAE30"] }), F && e.jsx(J, { avoidNativeButton: F, onClick: () => we((r) => !r), className: "flex items-center justify-center w-8 h-8 rounded-lg border transition-all bg-white border-gray-200 text-gray-600", title: oe ? "\uCE74\uB4DC \uC791\uAC8C" : "\uCE74\uB4DC \uD06C\uAC8C", children: oe ? e.jsx(Ut, { className: "w-3.5 h-3.5" }) : e.jsx(Wt, { className: "w-3.5 h-3.5" }) })] }) })] }), t.folderTabs, t.overlays, Qe && Re.length > 0 && e.jsx(Mn, { enVoices: Re, koVoices: rt, selectedEnVoice: Ae, selectedKoVoice: mt, onEnVoiceChange: nt, onKoVoiceChange: at, onClose: () => ft(false), avoidNativeButtons: F }), F && e.jsx(gn, { tool: R, onToolChange: D, penColor: K, onPenColorChange: ee, onUndo: H, onClear: U, canUndo: te != null, canClear: true }), Se && Se.queue.length > 0 && e.jsx(wn, { prompt: Se.queue[0], remainingCount: Se.queue.length - 1, onMerge: () => Ge({ action: "merge", targetEntryId: Se.queue[0].mergeTargetEntryId }), onSeparate: () => Ge({ action: "separate" }), onDismiss: () => Ge(null) }), Ue > 0 && e.jsxs("div", { className: "fixed bottom-0 left-0 right-0 z-50 flex flex-nowrap items-center justify-center gap-1 border-t border-gray-200 bg-white pb-[max(0.75rem,env(safe-area-inset-bottom))] pl-[max(0.5rem,env(safe-area-inset-left))] pr-[max(0.5rem,env(safe-area-inset-right))] pt-3 shadow-[0_-4px_12px_rgba(0,0,0,0.08)] sm:gap-2 sm:pl-[max(1rem,env(safe-area-inset-left))] sm:pr-[max(1rem,env(safe-area-inset-right))]", children: [e.jsxs("span", { className: "shrink-0 whitespace-nowrap text-sm font-semibold text-primary", children: [e.jsxs("span", { className: "sm:hidden", children: [Ue, "\uAC1C"] }), e.jsxs("span", { className: "hidden sm:inline", children: [Ue, "\uAC1C \uC120\uD0DD\uB428"] })] }), m === "known" && e.jsxs(e.Fragment, { children: [e.jsxs("button", { onClick: () => { 1602 ve("default"); 1603 }, disabled: Ve.isPending, "aria-label": "\uAE30\uBCF8\uC73C\uB85C", className: "flex shrink-0 items-center gap-1 whitespace-nowrap rounded-lg border border-gray-300 px-2 py-1.5 text-sm text-gray-700 transition-colors hover:bg-gray-50 disabled:opacity-50 sm:gap-1.5 sm:px-3", children: [e.jsx("span", { className: "sm:hidden", children: "\uAE30\uBCF8" }), e.jsx("span", { className: "hidden sm:inline", children: "\uAE30\uBCF8\uC73C\uB85C" })] }), e.jsxs("button", { onClick: () => { 1604 ve("memorized"); 1605 }, disabled: Ve.isPending, "aria-label": "\uC554\uAE30\uB428\uC73C\uB85C", className: "flex shrink-0 items-center gap-1 whitespace-nowrap rounded-lg border border-primary/30 bg-primary/5 px-2 py-1.5 text-sm text-primary transition-colors hover:bg-primary/10 disabled:opacity-50 sm:gap-1.5 sm:px-3", children: [e.jsx("span", { className: "sm:hidden", children: "\uC554\uAE30\uB428" }), e.jsx("span", { className: "hidden sm:inline", children: "\uC554\uAE30\uB428\uC73C\uB85C" })] })] }), e.jsxs("button", { onClick: () => Et(Te), disabled: ot.isPending || Te.length === 0, "aria-label": "\uB098\uC758 \uB2E8\uC5B4\uC7A5\uC5D0 \uCD94\uAC00", className: "flex shrink-0 items-center gap-1 whitespace-nowrap rounded-lg bg-primary px-2 py-1.5 text-sm text-white transition-colors hover:bg-primary/90 disabled:opacity-50 sm:gap-1.5 sm:px-3", children: [ot.isPending ? e.jsx(We, { className: "w-3.5 h-3.5 animate-spin" }) : e.jsx(na, { className: "w-3.5 h-3.5" }), e.jsx("span", { className: "sm:hidden", children: "\uB2E8\uC5B4\uC7A5 \uCD94\uAC00" }), e.jsx("span", { className: "hidden sm:inline", children: "\uB098\uC758 \uB2E8\uC5B4\uC7A5\uC5D0 \uCD94\uAC00" })] }), m !== "known" && e.jsxs("button", { onClick: () => { 1606 Xe(); 1607 }, disabled: Ve.isPending, "aria-label": "\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9", className: "flex shrink-0 items-center gap-1 whitespace-nowrap rounded-lg border border-primary/30 bg-primary/5 px-2 py-1.5 text-sm text-primary transition-colors hover:bg-primary/10 disabled:opacity-50 sm:gap-1.5 sm:px-3", children: [e.jsx(or, { className: "h-3.5 w-3.5" }), e.jsx("span", { className: "sm:hidden", children: "\uC544\uB294 \uB2E8\uC5B4" }), e.jsx("span", { className: "hidden sm:inline", children: "\uC544\uB294 \uB2E8\uC5B4\uB85C \uC774\uB3D9" })] }), e.jsxs("button", { onClick: () => { 1608 ce(), vt(m); 1609 }, className: "flex shrink-0 items-center gap-1 whitespace-nowrap text-sm text-gray-500 transition-colors hover:text-gray-700", children: [e.jsx(_t, { className: "w-4 h-4" }), "\uC120\uD0DD \uD574\uC81C"] })] }), y ? e.jsx(vr, { message: "\uC624\uD504\uB77C\uC778 \uC0C1\uD0DC\uC774\uBA70 \uC774 \uB2E8\uC5B4\uC7A5\uC744 \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." }) : v ? e.jsx("div", { className: "flex items-center justify-center h-48", children: e.jsx(We, { className: "w-8 h-8 animate-spin text-primary" }) }) : S ? e.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [e.jsx(Ke, { className: "mb-4 h-12 w-12 text-amber-500" }), e.jsx("h2", { className: "mb-2 text-lg font-semibold text-gray-700", children: "\uB2E8\uC5B4\uC7A5\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4" }), e.jsx("p", { className: "mb-5 text-sm text-gray-500", children: "\uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694." }), e.jsx("button", { type: "button", onClick: () => { 1610 C(); 1611 }, className: "rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm text-gray-700 hover:bg-gray-50", children: "\uB2E4\uC2DC \uC2DC\uB3C4" })] }) : p.length === 0 ? e.jsxs("div", { className: "text-center py-16", children: [e.jsx(ir, { className: "w-16 h-16 mx-auto text-gray-300 mb-4" }), e.jsx("h2", { className: "text-lg font-semibold text-gray-700 mb-2", children: "\uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" })] }) : re.length === 0 ? e.jsxs("div", { className: "text-center py-14 roun
1611ded-xl border border-dashed border-gray-200 bg-gray-50/60", children: [e.jsx(ir, { className: "w-12 h-12 mx-auto text-gray-300 mb-3" }), e.jsxs("h2", { className: "text-base font-semibold text-gray-700", children: [ut[m], " \uD3F4\uB354\uC5D0 \uB2E8\uC5B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4"] }), e.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "\uB2E4\uB978 \uD3F4\uB354\uC5D0\uC11C \uB2E8\uC5B4\uB97C \uC774\uB3D9\uD574 \uBCF4\uC138\uC694." })] }) : F ? e.jsxs(e.Fragment, { children: [t.folderActions && e.jsx("div", { className: "py-1.5", children: t.folderActions }), e.jsx("div", { ref: pe, className: _ ? "flex flex-col gap-0.5 md:grid md:grid-cols-2 md:grid-flow-col" : "space-y-0.5", style: _ ? { gridTemplateRows: `repeat(${Math.ceil(re.length / 2)}, auto)` } : void 0, children: re.map((r, c) => { 1612 const N = te === r.id; 1613 return e.jsx(Tn, { entryId: r.id, index: c, active: N, largeCards: oe, isNativeActive: it, onActivate: () => z(r.id), renderOverlay: () => e.jsx(hn, { ref: (w) => { 1614 w ? he.current.set(r.id, w) : he.current.delete(r.id); 1615 }, questionId: r.id, sheetId: n, enabled: F, disableNative: true, deviceId: ue, serverDraft: Zt[String(r.id)], onDraftChange: Mt, syncHydratedDraft: false, touchActionMode: "pan-y", debugPanelActive: N, storageKeyPrefix: "vocab-b-", storageScope: ze(r.id), onPersistenceStatusChange: (w) => Jt(r.id, w), tool: R, onToolChange: D, penColor: K, onPenColorChange: ee, hideToolbar: true, onActivate: () =>
1615 z(r.id), entityType: "vocabulary", entityId: String(n), subEntityId: String(r.id), tapZoneSelector: "button[data-handwriting-tap-through]" }), children: e.jsxs("div", { className: "absolute inset-0 z-[11] pointer-events-none flex items-center gap-3 px-4", children: [e.jsx("input", { type: "checkbox", checked: I.has(r.id), onChange: (w) => Z(c, w.nativeEvent.shiftKey), className: "w-5 h-5 md:w-4 md:h-4 rounded border-gray-300 text-primary focus:ring-primary/30 pointer-events-auto" }), e.jsx("span", { className: "text-sm text-gray-400 w-6 text-right", children: c + 1 }), e.jsx(Lt, { word: r.word, active: be && fe === c, onPlay: () => He(c), handwriting: true }), e.jsx("span", { className: "font-semibold text-gray-900 text-base", children: r.word }), e.jsx("button", { type: "button", "data-handwriting-tap-through": true, onClick: O ? (w) => { 1616 w.stopPropagation(), qe(r.id); 1617 } : void 0, className: `text-base ml-auto bg-transparent border-0 p-0 ${O ? "pointer-events-auto cursor-pointer select-none active:bg-gray-100 rounded px-1" : "pointer-events-none"} ${O && !A.has(r.id) ? "text-gray-400" : "text-gray-600"}`, children: !O || A.has(r.id) ? r.meaning : "\u2022\u2022\u2022\u2022" })] }) }, r.id); 1618 }) })] }) : e.jsxs(e.Fragment, { children: [_ ? e.jsxs("div", { className: "hidden md:block bg-white rounded-xl border border-gray-200 overflow-hidden", children: [t.folderActions && e.jsx("div", { className: "flex items-center border-b border-gray-100 bg-gray-50/60 px-2 py-2.5", children: t.folderActions }), e.jsx("div", { ...X, className: "grid grid-cols-2 grid-flow-col py-2", style: { ...X.style, gridTemplateRows: `repeat(${Math.ceil(re.length / 2)}, auto)` }, children: re.map((r, c) => e.jsxs("div", { "data-vocab-select-index": c, ref: fe === c ? (N) => { 1619 st.current = N; 1620 } : void 0, onClick: () => Z(c, false), className: `flex items-center gap-2 px-4 py-1.5 cursor-pointer transition-colors ${c < Math.ceil(re.length / 2) ? "border-r border-gray-100" : ""} ${fe === c ? "bg-yellow-50" : I.has(r.id) ? "bg-primary/5" : "hover:bg-gray-50/50"}`, children: [e.jsx("span", { "data-vocab-drag-handle": true, onClick: (N) => { 1621 N.stopPropagation(), Z(c, false); 1622 }, className: "-m-3 flex flex-shrink-0 items-center justify-center p-3", style: { touchAction: "none" }, children: e.jsx("input", { type: "checkbox", checked: I.has(r.id), onChange: (N) => { 1623 N.stopPropagation(), Z(c, N.nativeEvent.shiftKey); 1624 }, onClick: (N) => N.stopPropagation(), className: "rounded border-gray-300 text-primary focus:ring-primary/30" }) }), e.jsxs("span", { className: "text-xs text-gray-400 w-6 text-right flex-shrink-0", children: [c + 1, "."] }), e.jsx(Lt, { word: r.word, active: be && fe === c, onPlay: () => He(c) }), e.jsx("span", { className: "font-medium text-gray-900 truncate", children: r.word }), e.jsx("span", { onClick: O ? (N) => { 1625 N.stopPropagation(), qe(r.id); 1626 } : void 0, className: `text-sm ml-auto truncate ${O ? "cursor-pointer select-none" : ""} ${O && !A.has(r.id) ? "text-gray-400" : "text-gray-600"}`, children: !O || A.has(r.id) ? r.meaning : "\u2022\u2022\u2022\u2022" })] }, r.id)) })] }) : e.jsxs("div", { className: "hidden md:block bg-white rounded-xl border border-gray-200 overflow-hidden", children: [t.folderActions && e.jsx("div", { className: "flex items-center border-b border-gray-100 bg-gray-50/60 px-2 py-2.5", children: t.folderActions }), e.jsxs("table", { className: "w-full", children: [e.jsx("thead", { children: e.jsxs("tr", { className: "border-b border-gray-100 bg-gray-50/60", children: [e.jsx("th", { className: "w-10 px-4 py-3", children: e.jsx("input", { type: "checkbox", checked: pt, onChange: (r) => ye(r.target.checked), disabled: re.length === 0, className: "rounded border-gray-300 text-primary focus:ring-primary/30 disabled:opacity-30" }) }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-16", children: "\uBC88\uD638" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-48", children: "\uB2E8\uC5B4" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-48", children: "\uB73B" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "\uC608\uBB38" }), e.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-24", children: "\uC0C1\uD0DC" })] }) }), e.jsx("tbody", { ...X, children: re.map((r, c) => { 1627 var N; 1628 return e.jsxs("tr", { "data-vocab-select-index": c, ref: fe === c ? (w) => { 1629 st.current = w; 1630 } : void 0, className: `border-b border-gray-50 last:border-b-0 hover:bg-gray-50/50 transition-colors ${fe === c ? "bg-yellow-50 ring-1 ring-inset ring-yellow-300" : I.has(r.id) ? "bg-primary/5" : ""}`, children: [e.jsx("td", { className: "px-4 py-3", children: e.jsx("span", { "data-vocab-drag-handle": true, onClick: (w) => { 1631 w.stopPropagation(), Z(c, false); 1632 }, className: "-m-3 inline-flex items-center justify-center p-3", style: { touchAction: "
1632none" }, children: e.jsx("input", { type: "checkbox", checked: I.has(r.id), onChange: (w) => Z(c, w.nativeEvent.shiftKey), onClick: (w) => w.stopPropagation(), className: "rounded border-gray-300 text-primary focus:ring-primary/30" }) }) }), e.jsx("td", { className: "px-4 py-3 text-sm text-gray-400", children: c + 1 }), e.jsx("td", { onClick: () => Z(c, false), className: "px-4 py-3 font-medium text-gray-900 whitespace-pre-line cursor-pointer", children: e.jsxs("div", { className: "flex items-center gap-2", children: [e.jsx(Lt, { word: r.word, active: be && fe === c, onPlay: () => He(c) }), e.jsx("span", { children: r.word })] }) }), e.jsx("td", { onClick: O ? () => qe(r.id) : void 0, className: `px-4 py-3 text-sm whitespace-pre-line ${O ? "cursor-pointer select-none" : ""} ${O && !A.has(r.id) ? "text-gray-400" : "text-gray-700"}`, children: !O || A.has(r.id) ? r.meaning : "\u2022\u2022\u2022\u2022" }), e.jsx("td", { className: "px-4 py-3 text-sm text-gray-500 whitespace-pre-line", children: (N = r.example_sentence) != null ? N : "-" }), e.jsx("td", { className: "px-4 py-3", children: Pe(r) === "saved" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-green-600 bg-green-50 px-2 py-0.5 rounded-full", children: [e.jsx(or, { className: "w-3 h-3" }), "\uCD94\uAC00\uB428"] }) : Pe(r) === "conflict" ? e.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-amber-600 bg-amber-50 px-2 py-0.5 rounded-full", children: [e.jsx(Ke, { className: "w-3 h-3" }), "\uB2E4\uB978 \uB73B \uC800\uC7A5\uB428"] }) : e.jsx("span", { className: "text-xs text-gray-400", children: "\uBBF8\uCD94\uAC00" }) })] }, r.id); 1633 }) })] })] }), e.jsxs("div", { ...X, "data-testid": "book-vocabulary-selection-surface-mobile", className: "md:hidden bg-white rounded-xl border border-gray-200 overflow-hidden", children: [e.jsxs("div", { className: "flex items-center gap-1 px-2 py-2.5 border-b border-gray-100 bg-gray-50/60", children: [e.jsx("input", { type: "checkbox", checked: pt, onChange: (r) => ye(r.target.checked), disabled: re.length === 0, className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30 disabled:opacity-30" }), e.jsx("span", { className: "text-sm text-gray-500", children: "\uC804\uCCB4 \uC120\uD0DD" }), t.folderActions] }), re.map((r, c) => e.jsxs("div", { "data-vocab-select-index": c, ref: fe === c ? (N) => { 1634 xt.current = N; 1635 } : void 0, onClick: () => Z(c, false), className: `flex items-center gap-0.5 px-2 py-2.5 border-b border-gray-50 last:border-b-0 transition-colors cursor-pointer active:bg-gray-100 ${fe === c ? "bg-yellow-50 ring-1 ring-inset ring-yellow-300" : I.has(r.id) ? "bg-primary/5" : Pe(r) === "saved" ? "bg-green-50/40" : Pe(r) === "conflict" ? "bg-amber-50/40" : ""}`, children: [e.jsx("span", { "data-vocab-drag-handle": true, className: "-m-2.5 flex flex-shrink-0 items-center justify-center p-2.5", style: { touchAction: "none" }, children: e.jsx("input", { type: "checkbox", checked: I.has(r.id), readOnly: true, className: "w-5 h-5 rounded border-gray-300 text-primary focus:ring-primary/30 pointer-events-none" }) }), e.jsx("span", { className: "text-xs text-gray-400 w-4 text-center flex-shrink-0", children: c + 1 }), e.jsx(Lt, { word: r.word, active: be && fe === c, onPlay: () => He(c) }), e.jsx("span", { className: "font-medium text-gray-900 flex-1 min-w-0 line-clamp-2 ml-0.5", children: r.word }), e.jsx("span", { onClick: O ? (N) => { 1636 N.stopPropagation(), qe(r.id); 1637 } : void 0, className: `text-sm flex-shrink-0 line-clamp-2 text-right ${O ? "w-[42%] py-3 -my-2.5 pl-2 -mr-1 pr-1 cursor-pointer select-none active:bg-gray-100 rounded-md" : "max-w-[40%]"} ${O && !A.has(r.id) ? "text-gray-400" : "text-gray-600"}`, children: !O || A.has(r.id) ? r.meaning : "\u2022\u2022\u2022\u2022" })] }, r.id))] })] })] }); 1638} 1639export { 1640 ks as VocabularyPage 1641};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.