memi-board 0.52.1 → 0.52.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{ar-BVwpedHS.js → ar-3hd6xh6U.js} +3 -1
  2. package/dist/ar-3hd6xh6U.js.map +1 -0
  3. package/dist/{commentImages-CzXO42gE.js → commentImages-4IOk2u0O.js} +14 -12
  4. package/dist/commentImages-4IOk2u0O.js.map +1 -0
  5. package/dist/composables/useMemiBoardComments.d.ts +36 -2
  6. package/dist/composables/useMemiBoardWritingAssistant.d.ts +6 -2
  7. package/dist/{de-K2gYwR8U.js → de-DXtmk-4H.js} +3 -1
  8. package/dist/de-DXtmk-4H.js.map +1 -0
  9. package/dist/{en-CmPLbFcN.js → en-CyZH4Ct1.js} +5 -3
  10. package/dist/en-CyZH4Ct1.js.map +1 -0
  11. package/dist/{es-1P1miLJH.js → es-BtRbHicT.js} +3 -1
  12. package/dist/es-BtRbHicT.js.map +1 -0
  13. package/dist/{fr-cDQCmNSc.js → fr-vnizG3rD.js} +3 -1
  14. package/dist/fr-vnizG3rD.js.map +1 -0
  15. package/dist/{id-BzDIqeZ4.js → id-Cf8yw8n_.js} +3 -1
  16. package/dist/id-Cf8yw8n_.js.map +1 -0
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.js +882 -808
  19. package/dist/index.js.map +1 -1
  20. package/dist/{ja-B2wxHGCr.js → ja-D-Nrtjk-.js} +3 -1
  21. package/dist/ja-D-Nrtjk-.js.map +1 -0
  22. package/dist/{pt-BCY3OODD.js → pt-CSsshr7u.js} +3 -1
  23. package/dist/pt-CSsshr7u.js.map +1 -0
  24. package/dist/runtime/components/CommentAi.vue +93 -0
  25. package/dist/runtime/components/CommentFeed.vue +3 -3
  26. package/dist/runtime/components/CommentForm.vue +21 -5
  27. package/dist/runtime/components/CommentItem.vue +20 -8
  28. package/dist/runtime/components/Editor.vue +15 -9
  29. package/dist/runtime/components/MiniComments.vue +3 -3
  30. package/dist/runtime/components/UserCommentList.vue +3 -3
  31. package/dist/storage.js +1 -1
  32. package/dist/types.d.ts +10 -1
  33. package/dist/utils/localizedFields.d.ts +3 -1
  34. package/dist/{zh-Y824rwR6.js → zh-BzrsqVN2.js} +3 -1
  35. package/dist/zh-BzrsqVN2.js.map +1 -0
  36. package/docs/firestore.rules.example +3 -3
  37. package/docs/host-integration.md +13 -0
  38. package/package.json +1 -1
  39. package/dist/ar-BVwpedHS.js.map +0 -1
  40. package/dist/commentImages-CzXO42gE.js.map +0 -1
  41. package/dist/de-K2gYwR8U.js.map +0 -1
  42. package/dist/en-CmPLbFcN.js.map +0 -1
  43. package/dist/es-1P1miLJH.js.map +0 -1
  44. package/dist/fr-cDQCmNSc.js.map +0 -1
  45. package/dist/id-BzDIqeZ4.js.map +0 -1
  46. package/dist/ja-B2wxHGCr.js.map +0 -1
  47. package/dist/pt-BCY3OODD.js.map +0 -1
  48. package/dist/zh-Y824rwR6.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- import { A as e, B as t, C as n, D as r, E as i, F as a, G as o, H as s, I as c, J as l, K as u, L as d, M as f, N as p, O as m, P as h, R as g, S as _, T as v, U as y, V as b, W as x, X as S, Y as C, Z as w, _ as T, a as E, b as D, c as ee, d as O, f as te, g as k, h as ne, j as re, k as ie, l as ae, m as A, o as oe, p as se, q as ce, r as le, s as j, t as ue, u as de, v as fe, w as M, x as pe, y as N, z as me } from "./commentImages-CzXO42gE.js";
2
- import { computed as P, onScopeDispose as F, reactive as he, readonly as ge, ref as I, toValue as L, watch as R } from "vue";
3
- import { collection as _e, deleteDoc as ve, deleteField as ye, doc as z, documentId as B, endAt as be, endBefore as xe, getDoc as V, getDocFromServer as Se, getDocs as H, getDocsFromServer as Ce, increment as we, limit as U, limitToLast as Te, onSnapshot as W, orderBy as G, query as K, runTransaction as Ee, serverTimestamp as q, setDoc as De, startAfter as J, updateDoc as Y, where as X, writeBatch as Oe } from "firebase/firestore";
1
+ import { A as e, B as t, C as n, D as r, E as i, F as a, G as o, H as s, I as c, J as l, K as u, L as d, M as f, N as p, O as m, P as h, R as g, S as _, T as v, U as y, V as b, W as x, X as S, Y as C, Z as w, _ as T, a as E, b as D, c as ee, d as O, f as k, g as A, h as j, j as te, k as ne, l as re, m as M, o as ie, p as ae, q as oe, r as se, s as N, t as ce, u as le, v as ue, w as P, x as de, y as F, z as fe } from "./commentImages-4IOk2u0O.js";
2
+ import { computed as I, onScopeDispose as L, reactive as pe, readonly as me, ref as R, toValue as z, watch as B } from "vue";
3
+ import { collection as he, deleteDoc as ge, deleteField as _e, doc as ve, documentId as V, endAt as ye, endBefore as be, getDoc as xe, getDocFromServer as Se, getDocs as H, getDocsFromServer as Ce, increment as we, limit as U, limitToLast as Te, onSnapshot as W, orderBy as G, query as K, runTransaction as Ee, serverTimestamp as q, setDoc as De, startAfter as J, updateDoc as Y, where as X, writeBatch as Oe } from "firebase/firestore";
4
4
  import { useCollection as ke, useCurrentUser as Ae, useDocument as je, useFirebaseApp as Me, useFirebaseAuth as Ne, useFirestore as Z } from "vuefire";
5
5
  import { GoogleAuthProvider as Pe, OAuthProvider as Fe, createUserWithEmailAndPassword as Ie, getAuth as Le, signInWithEmailAndPassword as Re, signInWithPopup as ze, signOut as Be, updateProfile as Ve } from "firebase/auth";
6
6
  import { deleteObject as He, getMetadata as Ue, getStorage as We, list as Ge, listAll as Ke, ref as qe } from "firebase/storage";
7
7
  import { GoogleAIBackend as Je, Schema as Ye, getAI as Xe, getGenerativeModel as Ze } from "firebase/ai";
8
8
  //#region src/utils/boardSeo.ts
9
9
  function Qe(e, t) {
10
- return !e || e === "전체" || e === A(t, "seo.allCategory");
10
+ return !e || e === "전체" || e === M(t, "seo.allCategory");
11
11
  }
12
12
  function $e(e) {
13
13
  if (typeof e != "string") return null;
@@ -37,23 +37,23 @@ function tt(e, t, n = "") {
37
37
  return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? tt(n, t) : r;
38
38
  }
39
39
  function nt(e, t, n, r, i) {
40
- let a = r?.trim().slice(0, 40), o = e.trim() || a || A(i, "seo.photo"), s = t?.trim(), c = n.trim() || "Board";
40
+ let a = r?.trim().slice(0, 40), o = e.trim() || a || M(i, "seo.photo"), s = t?.trim(), c = n.trim() || "Board";
41
41
  return s ? `${o} · ${s} | ${c}` : `${o} | ${c}`;
42
42
  }
43
43
  function rt(e, t) {
44
44
  let n = [];
45
- return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(A(t, "seo.postDescriptionFallback")), e.categoryLabel?.trim() && n.push(e.categoryLabel.trim()), e.authorName?.trim() && n.push(e.authorName.trim()), n.join(" · ").slice(0, 200);
45
+ return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(M(t, "seo.postDescriptionFallback")), e.categoryLabel?.trim() && n.push(e.categoryLabel.trim()), e.authorName?.trim() && n.push(e.authorName.trim()), n.join(" · ").slice(0, 200);
46
46
  }
47
47
  function it(e, t, n) {
48
48
  let r = t.trim() || "Board", i = e?.trim();
49
- return Qe(i, n) ? A(n, "seo.listTitleAll", { site: r }) : A(n, "seo.listTitle", {
49
+ return Qe(i, n) ? M(n, "seo.listTitleAll", { site: r }) : M(n, "seo.listTitle", {
50
50
  name: i,
51
51
  site: r
52
52
  });
53
53
  }
54
54
  function at(e, t) {
55
55
  if (e.description?.trim()) return e.description.trim().slice(0, 200);
56
- let n = e.categoryLabel?.trim(), r = Qe(n, t) ? A(t, "seo.listDescriptionAll") : A(t, "seo.listDescription", { name: n }), i = (e.recentTitles || []).filter(Boolean).slice(0, 3);
56
+ let n = e.categoryLabel?.trim(), r = Qe(n, t) ? M(t, "seo.listDescriptionAll") : M(t, "seo.listDescription", { name: n }), i = (e.recentTitles || []).filter(Boolean).slice(0, 3);
57
57
  return i.length ? `${r} ${i.map((e) => `「${e}」`).join(" ")}`.slice(0, 200) : r.slice(0, 200);
58
58
  }
59
59
  function ot(e) {
@@ -82,7 +82,7 @@ function Q(e) {
82
82
  if (!e || typeof e != "object") return;
83
83
  let t = {};
84
84
  for (let [n, r] of Object.entries(e)) {
85
- let e = N(n);
85
+ let e = F(n);
86
86
  if (!e || !r || typeof r != "object") continue;
87
87
  let i = {};
88
88
  for (let [e, t] of Object.entries(r)) typeof t == "string" && t.trim() && (i[e] = t.trim());
@@ -92,14 +92,14 @@ function Q(e) {
92
92
  }
93
93
  function ut(e) {
94
94
  if (!e || typeof e != "object") return;
95
- let t = e, n = {}, r = N(t.locale);
95
+ let t = e, n = {}, r = F(t.locale);
96
96
  r && (n.locale = r);
97
97
  for (let e of st) typeof t[e] == "string" && (n[e] = t[e]);
98
98
  return Object.keys(n).length ? n : void 0;
99
99
  }
100
100
  function dt(e) {
101
101
  if (!e || typeof e != "object") return;
102
- let t = e, n = {}, r = N(t.locale);
102
+ let t = e, n = {}, r = F(t.locale);
103
103
  return r && (n.locale = r), typeof t.hash == "string" && t.hash && (n.hash = t.hash), n.locale || n.hash ? n : void 0;
104
104
  }
105
105
  function ft(e, t, n) {
@@ -122,7 +122,7 @@ function ft(e, t, n) {
122
122
  }), r;
123
123
  }
124
124
  function pt(e) {
125
- let t = new Set((e?.translationLocales ?? []).map((e) => N(e)).filter((e) => !!e)), n = T.map((e) => e.code);
125
+ let t = new Set((e?.translationLocales ?? []).map((e) => F(e)).filter((e) => !!e)), n = T.map((e) => e.code);
126
126
  return t.size ? n.filter((e) => t.has(e)) : n;
127
127
  }
128
128
  function mt(e) {
@@ -135,7 +135,7 @@ function ht(e, t, n = T.map((e) => e.code)) {
135
135
  }
136
136
  function gt(e, t, r, i, a) {
137
137
  if (!n()) return i;
138
- let o = N(t), s = N(a);
138
+ let o = F(t), s = F(a);
139
139
  if (!o || s && o === s) return i;
140
140
  let c = lt(e?.[o], r);
141
141
  if (c) return c;
@@ -162,17 +162,20 @@ function xt(e) {
162
162
  let t = e.label.trim(), n = (e.description ?? "").trim();
163
163
  return !t || !e.i18nSource || e.i18nSource.label !== t || (e.i18nSource.description ?? "") !== n ? !1 : ht(e.i18n, n ? ["label", "description"] : ["label"], pt(e));
164
164
  }
165
+ function St(e, t) {
166
+ return e.i18nSource?.body === e.body ? gt(e.i18n, t, "body", e.body, e.i18nSource.locale) : e.body;
167
+ }
165
168
  //#endregion
166
169
  //#region src/composables/fetchPublicSeo.ts
167
- function St() {
170
+ function Ct() {
168
171
  return {
169
172
  db: Z(),
170
- paths: M()
173
+ paths: P()
171
174
  };
172
175
  }
173
- async function Ct(e, t, n) {
176
+ async function wt(e, t, n) {
174
177
  try {
175
- let r = await V(w(e.db, e.paths, t));
178
+ let r = await xe(w(e.db, e.paths, t));
176
179
  if (!r.exists()) return {
177
180
  label: t,
178
181
  description: "",
@@ -192,7 +195,7 @@ async function Ct(e, t, n) {
192
195
  };
193
196
  }
194
197
  }
195
- function wt(e) {
198
+ function Tt(e) {
196
199
  if (e && typeof e == "object" && "toDate" in e) try {
197
200
  let t = e.toDate();
198
201
  return t instanceof Date ? t.toISOString() : null;
@@ -201,12 +204,12 @@ function wt(e) {
201
204
  }
202
205
  return null;
203
206
  }
204
- async function Tt(e, t, n, r) {
207
+ async function Et(e, t, n, r) {
205
208
  let i = e.trim(), a = t.trim();
206
209
  if (!i || !a) return null;
207
- let s = n ?? St();
208
- s.paths = C(s.paths), r && await se(r);
209
- let c = await Ct(s, i, r);
210
+ let s = n ?? Ct();
211
+ s.paths = C(s.paths), r && await ae(r);
212
+ let c = await wt(s, i, r);
210
213
  if (c.hidden) return null;
211
214
  try {
212
215
  let e = (await H(K(o(s.db, s.paths), X("boardId", "==", i), X("slug", "==", a), X("listed", "==", !0), X("isPublished", "==", !0), U(1)))).docs[0];
@@ -229,17 +232,17 @@ async function Tt(e, t, n, r) {
229
232
  }, r).trim().slice(0, 200),
230
233
  previewImage: et(t.previewImage),
231
234
  authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
232
- createdAt: wt(t.createdAt)
235
+ createdAt: Tt(t.createdAt)
233
236
  } : null;
234
237
  } catch (e) {
235
238
  return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
236
239
  }
237
240
  }
238
- async function Et(e, t, n) {
239
- n && await se(n);
240
- let r = e?.trim() || null, i = t ?? St();
241
+ async function Dt(e, t, n) {
242
+ n && await ae(n);
243
+ let r = e?.trim() || null, i = t ?? Ct();
241
244
  if (i.paths = C(i.paths), r) {
242
- let e = await Ct(i, r, n);
245
+ let e = await wt(i, r, n);
243
246
  if (e.hidden) return {
244
247
  kind: "category",
245
248
  category: r,
@@ -248,12 +251,12 @@ async function Et(e, t, n) {
248
251
  ogImage: null,
249
252
  recentTitles: []
250
253
  };
251
- let { recentTitles: t, ogImage: a } = await Dt(i, r, n);
254
+ let { recentTitles: t, ogImage: a } = await Ot(i, r, n);
252
255
  return {
253
256
  kind: "category",
254
257
  category: r,
255
258
  categoryLabel: e.label,
256
- description: e.description || A(n, "seo.boardDescription", { name: e.label }),
259
+ description: e.description || M(n, "seo.boardDescription", { name: e.label }),
257
260
  ogImage: a,
258
261
  recentTitles: t
259
262
  };
@@ -261,13 +264,13 @@ async function Et(e, t, n) {
261
264
  return {
262
265
  kind: "all",
263
266
  category: null,
264
- categoryLabel: A(n, "seo.allCategory"),
265
- description: A(n, "seo.allDescription"),
267
+ categoryLabel: M(n, "seo.allCategory"),
268
+ description: M(n, "seo.allDescription"),
266
269
  ogImage: null,
267
270
  recentTitles: []
268
271
  };
269
272
  }
270
- async function Dt(e, t, n) {
273
+ async function Ot(e, t, n) {
271
274
  let r = [], i = null;
272
275
  try {
273
276
  let a = await H(K(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), G("createdAt", "desc"), U(12)));
@@ -289,31 +292,31 @@ async function Dt(e, t, n) {
289
292
  }
290
293
  //#endregion
291
294
  //#region src/utils/boardAccess.ts
292
- function Ot(e, t) {
295
+ function kt(e, t) {
293
296
  return !e || !t ? !1 : [...t.allowedStaffUids ?? [], ...t.allowedManagerUids ?? []].includes(e);
294
297
  }
295
- function kt(e, t, n, r) {
296
- return e ? t ? !0 : n ? Ot(e, r) : !1 : !1;
297
- }
298
298
  function At(e, t, n, r) {
299
+ return e ? t ? !0 : n ? kt(e, r) : !1 : !1;
300
+ }
301
+ function jt(e, t, n, r) {
299
302
  if (!e) return !1;
300
- if (r?.visibility === "hidden") return kt(e, t, n, r);
303
+ if (r?.visibility === "hidden") return At(e, t, n, r);
301
304
  let i = r?.commentWriteRole ?? "user";
302
- return i === "admin" ? t : i !== "staff" || kt(e, t, n, r);
305
+ return i === "admin" ? t : i !== "staff" || At(e, t, n, r);
303
306
  }
304
307
  //#endregion
305
308
  //#region src/composables/boardLookup.ts
306
- var jt;
307
- function Mt(e) {
308
- jt = e;
309
- }
309
+ var Mt;
310
310
  function Nt(e) {
311
- return jt?.(e);
311
+ Mt = e;
312
+ }
313
+ function Pt(e) {
314
+ return Mt?.(e);
312
315
  }
313
316
  //#endregion
314
317
  //#region src/utils/moderation-strike.ts
315
- var Pt = 10, Ft = 864e5;
316
- function It(e, t, n = Date.now(), r = Ft) {
318
+ var Ft = 10, It = 864e5;
319
+ function Lt(e, t, n = Date.now(), r = It) {
317
320
  let i = Math.max(0, Math.floor(Number(e) || 0));
318
321
  if (i <= 0) return 0;
319
322
  let a = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -321,7 +324,7 @@ function It(e, t, n = Date.now(), r = Ft) {
321
324
  let o = Math.floor((n - a) / r);
322
325
  return Math.max(0, i - o);
323
326
  }
324
- function Lt(e, t, n = Date.now(), r = 10, i = Ft) {
327
+ function Rt(e, t, n = Date.now(), r = 10, i = It) {
325
328
  let a = Math.max(0, Math.floor(Number(e) || 0));
326
329
  if (a < r) return null;
327
330
  let o = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -329,20 +332,20 @@ function Lt(e, t, n = Date.now(), r = 10, i = Ft) {
329
332
  let s = o + (a - (r - 1)) * i;
330
333
  return s > n ? s : null;
331
334
  }
332
- function Rt(e, t, n = Date.now(), r = 10, i = Ft) {
333
- return Lt(e, t, n, r, i) != null;
335
+ function zt(e, t, n = Date.now(), r = 10, i = It) {
336
+ return Rt(e, t, n, r, i) != null;
334
337
  }
335
- function zt(e, t = Date.now(), n) {
338
+ function Bt(e, t = Date.now(), n) {
336
339
  let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
337
- if (i <= 0) return A(n, "moderationStrike.liftSoon");
338
- if (i < 24) return A(n, "moderationStrike.liftInHours", { hours: i });
340
+ if (i <= 0) return M(n, "moderationStrike.liftSoon");
341
+ if (i < 24) return M(n, "moderationStrike.liftInHours", { hours: i });
339
342
  let a = Math.floor(i / 24), o = i % 24;
340
- return o === 0 ? A(n, "moderationStrike.liftInDays", { days: a }) : A(n, "moderationStrike.liftInDaysHours", {
343
+ return o === 0 ? M(n, "moderationStrike.liftInDays", { days: a }) : M(n, "moderationStrike.liftInDaysHours", {
341
344
  days: a,
342
345
  hours: o
343
346
  });
344
347
  }
345
- function Bt(e) {
348
+ function Vt(e) {
346
349
  if (e == null) return null;
347
350
  if (typeof e == "number" && Number.isFinite(e)) return e;
348
351
  if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
@@ -354,29 +357,29 @@ function Bt(e) {
354
357
  }
355
358
  //#endregion
356
359
  //#region src/composables/useMemiBoardAuth.ts
357
- var Vt = "__MEMI_BOARD_AUTH_STATE__";
358
- function Ht() {
360
+ var Ht = "__MEMI_BOARD_AUTH_STATE__";
361
+ function Ut() {
359
362
  let e = globalThis;
360
- return e[Vt] || (e[Vt] = {
361
- role: I("user"),
362
- rolePending: I(!0),
363
- rawBlockCount: I(0),
364
- blockAtMs: I(null),
363
+ return e[Ht] || (e[Ht] = {
364
+ role: R("user"),
365
+ rolePending: R(!0),
366
+ rawBlockCount: R(0),
367
+ blockAtMs: R(null),
365
368
  watchStarted: !1
366
- }), e[Vt];
369
+ }), e[Ht];
367
370
  }
368
- function Ut() {
369
- let e = v(), { t, locale: n } = j(), r = Ht(), i = Z(), o = Ne(), s = Ae(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
371
+ function Wt() {
372
+ let e = v(), { t, locale: n } = N(), r = Ut(), i = Z(), o = Ne(), s = Ae(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
370
373
  function f() {
371
374
  let t = e.moderation?.blockBanThreshold;
372
375
  return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
373
376
  }
374
377
  function p() {
375
378
  let t = e.moderation?.blockBanDecayMs;
376
- return typeof t == "number" && t > 0 ? t : Ft;
379
+ return typeof t == "number" && t > 0 ? t : It;
377
380
  }
378
381
  function m(e) {
379
- return h(i, M(), e);
382
+ return h(i, P(), e);
380
383
  }
381
384
  function g(e) {
382
385
  if (c.value = [
@@ -389,10 +392,10 @@ function Ut() {
389
392
  u.value = 0, d.value = null;
390
393
  return;
391
394
  }
392
- u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = Bt(e.moderationBlockAt);
395
+ u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = Vt(e.moderationBlockAt);
393
396
  }
394
397
  async function _(e) {
395
- let t = m(e.uid), n = a(i, M(), e.uid), r = await V(t);
398
+ let t = m(e.uid), n = a(i, P(), e.uid), r = await xe(t);
396
399
  if (!r.exists()) return await Promise.all([De(t, {
397
400
  role: "user",
398
401
  displayName: e.displayName,
@@ -421,10 +424,10 @@ function Ut() {
421
424
  g(void 0);
422
425
  return;
423
426
  }
424
- let t = await V(m(e));
427
+ let t = await xe(m(e));
425
428
  g(t.exists() ? t.data() : void 0);
426
429
  }
427
- r.watchStarted || (r.watchStarted = !0, R(s, async (e) => {
430
+ r.watchStarted || (r.watchStarted = !0, B(s, async (e) => {
428
431
  if (l.value = !0, !e) {
429
432
  c.value = "user", g(void 0), l.value = !1;
430
433
  return;
@@ -437,27 +440,27 @@ function Ut() {
437
440
  l.value = !1;
438
441
  }
439
442
  }, { immediate: !0 }));
440
- let b = P(() => !!s.value), x = P(() => c.value === "owner"), S = P(() => c.value === "manager"), C = P(() => x.value || c.value === "admin"), w = P(() => S.value || c.value === "staff"), T = P(() => C.value || w.value), E = P(() => It(u.value, d.value, Date.now(), p())), D = P(() => {
443
+ let b = I(() => !!s.value), x = I(() => c.value === "owner"), S = I(() => c.value === "manager"), C = I(() => x.value || c.value === "admin"), w = I(() => S.value || c.value === "staff"), T = I(() => C.value || w.value), E = I(() => Lt(u.value, d.value, Date.now(), p())), D = I(() => {
441
444
  let e = f();
442
- return e <= 0 ? !1 : Rt(u.value, d.value, Date.now(), e, p());
443
- }), ee = P(() => {
445
+ return e <= 0 ? !1 : zt(u.value, d.value, Date.now(), e, p());
446
+ }), ee = I(() => {
444
447
  let e = f();
445
448
  if (e <= 0) return null;
446
- let t = Lt(u.value, d.value, Date.now(), e, p());
447
- return t == null ? null : zt(t, Date.now(), n.value);
448
- }), O = P(() => {
449
+ let t = Rt(u.value, d.value, Date.now(), e, p());
450
+ return t == null ? null : Bt(t, Date.now(), n.value);
451
+ }), O = I(() => {
449
452
  if (!D.value) return null;
450
453
  let e = ee.value;
451
454
  return e ? t("auth.writeRestrictedUntil", { until: e }) : t("auth.writeRestricted");
452
455
  });
453
- async function te() {
456
+ async function k() {
454
457
  let e = s.value?.uid, r = f();
455
458
  if (!e || r <= 0) return {
456
459
  effectiveCount: 0,
457
460
  restricted: !1,
458
461
  messageSuffix: ""
459
462
  };
460
- let i = m(e), a = await V(i);
463
+ let i = m(e), a = await xe(i);
461
464
  a.exists() ? await Y(i, {
462
465
  moderationBlockCount: we(1),
463
466
  moderationBlockAt: q(),
@@ -471,12 +474,12 @@ function Ut() {
471
474
  });
472
475
  let o = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
473
476
  u.value = o, d.value = c;
474
- let l = It(o, c, c, p()), h = Rt(o, c, c, r, p()), g = t("auth.strikeSuffix", {
477
+ let l = Lt(o, c, c, p()), h = zt(o, c, c, r, p()), g = t("auth.strikeSuffix", {
475
478
  count: l,
476
479
  threshold: r
477
480
  });
478
481
  if (h) {
479
- let e = Lt(o, c, c, r, p()), i = e == null ? null : zt(e, c, n.value);
482
+ let e = Rt(o, c, c, r, p()), i = e == null ? null : Bt(e, c, n.value);
480
483
  g = i ? t("auth.strikeRestrictedUntil", {
481
484
  count: l,
482
485
  until: i
@@ -488,101 +491,101 @@ function Ut() {
488
491
  messageSuffix: g
489
492
  };
490
493
  }
491
- function k() {
494
+ function A() {
492
495
  if (!o) throw Error(t("auth.notInitialized"));
493
496
  return o;
494
497
  }
495
- async function ne() {
496
- return ze(k(), new Pe());
498
+ async function j() {
499
+ return ze(A(), new Pe());
497
500
  }
498
- async function re() {
501
+ async function te() {
499
502
  let e = new Fe("apple.com");
500
- return e.addScope("email"), e.addScope("name"), ze(k(), e);
503
+ return e.addScope("email"), e.addScope("name"), ze(A(), e);
501
504
  }
502
- async function ie(e, t) {
503
- return Re(k(), e, t);
505
+ async function ne(e, t) {
506
+ return Re(A(), e, t);
504
507
  }
505
- async function ae(e, t, n) {
506
- let r = await Ie(k(), e, t);
508
+ async function re(e, t, n) {
509
+ let r = await Ie(A(), e, t);
507
510
  return n && await Ve(r.user, { displayName: n }), r;
508
511
  }
509
- async function A() {
510
- await Be(k());
512
+ async function M() {
513
+ await Be(A());
511
514
  }
512
- function oe(e) {
513
- return kt(s.value?.uid, C.value, w.value, e ? Nt(e) : void 0);
515
+ function ie(e) {
516
+ return At(s.value?.uid, C.value, w.value, e ? Pt(e) : void 0);
514
517
  }
515
- function se(e) {
516
- return s.value ? C.value ? !0 : S.value && !!e && (Nt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
518
+ function ae(e) {
519
+ return s.value ? C.value ? !0 : S.value && !!e && (Pt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
517
520
  }
518
- function ce(e) {
519
- return b.value && (s.value?.uid === e.authorUid || oe(e.boardId));
521
+ function oe(e) {
522
+ return b.value && (s.value?.uid === e.authorUid || ie(e.boardId));
520
523
  }
521
- function le(e) {
524
+ function se(e) {
522
525
  return b.value && s.value?.uid === e.authorUid;
523
526
  }
524
- function ue(e) {
525
- return b.value && (s.value?.uid === e.authorUid || oe(e.boardId));
527
+ function ce(e) {
528
+ return b.value && (s.value?.uid === e.authorUid || ie(e.boardId));
526
529
  }
527
530
  return {
528
531
  user: s,
529
532
  role: c,
530
533
  isOwner: x,
531
534
  isManager: S,
532
- canConfigureBoard: se,
535
+ canConfigureBoard: ae,
533
536
  isSignedIn: b,
534
537
  isAdmin: C,
535
538
  isStaff: w,
536
539
  canManageContent: T,
537
- canManageBoard: oe,
540
+ canManageBoard: ie,
538
541
  rolePending: l,
539
542
  moderationBlockCount: E,
540
543
  isWriteRestricted: D,
541
544
  restrictedUntilLabel: ee,
542
545
  restrictedMessage: O,
543
- signInWithGoogle: ne,
544
- signInWithApple: re,
545
- signInWithEmail: ie,
546
- signUpWithEmail: ae,
547
- signOut: A,
548
- canEdit: ce,
549
- canDelete: ce,
550
- canEditComment: le,
551
- canDeleteComment: ue,
552
- recordContentModerationBlock: te,
546
+ signInWithGoogle: j,
547
+ signInWithApple: te,
548
+ signInWithEmail: ne,
549
+ signUpWithEmail: re,
550
+ signOut: M,
551
+ canEdit: oe,
552
+ canDelete: oe,
553
+ canEditComment: se,
554
+ canDeleteComment: ce,
555
+ recordContentModerationBlock: k,
553
556
  refreshBoardUser: y
554
557
  };
555
558
  }
556
559
  //#endregion
557
560
  //#region src/utils/imeSafeSubmit.ts
558
- var Wt = /* @__PURE__ */ new WeakMap();
559
- function Gt(e) {
561
+ var Gt = /* @__PURE__ */ new WeakMap();
562
+ function Kt(e) {
560
563
  let t = e.currentTarget;
561
564
  return t instanceof HTMLButtonElement ? t : t instanceof Element ? t.closest("button") : null;
562
565
  }
563
- function Kt(e) {
566
+ function qt(e) {
564
567
  if (e.pointerType !== "mouse" || e.button !== 0) return;
565
- let t = Gt(e), n = t?.form;
568
+ let t = Kt(e), n = t?.form;
566
569
  if (!t || !n || t.disabled) return;
567
- Wt.set(t, Date.now() + 1e3);
570
+ Gt.set(t, Date.now() + 1e3);
568
571
  let r = document.activeElement;
569
572
  r instanceof HTMLElement && r.blur(), setTimeout(() => n.requestSubmit(t), 0);
570
573
  }
571
- function qt(e) {
572
- let t = Gt(e);
574
+ function Jt(e) {
575
+ let t = Kt(e);
573
576
  if (!t) return;
574
- let n = Wt.get(t);
575
- n && Date.now() < n && (e.preventDefault(), Wt.delete(t));
577
+ let n = Gt.get(t);
578
+ n && Date.now() < n && (e.preventDefault(), Gt.delete(t));
576
579
  }
577
580
  //#endregion
578
581
  //#region src/utils/findReadablePost.ts
579
- async function Jt(e, t, n, r, i, a) {
582
+ async function Yt(e, t, n, r, i, a) {
580
583
  let s = [X("boardId", "==", n), X("slug", "==", r)];
581
584
  return a ? (await H(K(o(e, t), ...s, U(1)))).docs[0] : (await Promise.all([H(K(o(e, t), ...s, X("listed", "==", !0), X("isPublished", "==", !0), U(1))), ...i ? [H(K(o(e, t), ...s, X("authorUid", "==", i), U(1)))] : []])).flatMap((e) => e.docs)[0];
582
585
  }
583
586
  //#endregion
584
587
  //#region src/utils/extractEditorImageUrls.ts
585
- function Yt(e) {
588
+ function Xt(e) {
586
589
  if (!e) return [];
587
590
  let t = /* @__PURE__ */ new Set();
588
591
  for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&amp;", "&"));
@@ -591,7 +594,7 @@ function Yt(e) {
591
594
  }
592
595
  //#endregion
593
596
  //#region src/utils/storagePath.ts
594
- function Xt(e) {
597
+ function Zt(e) {
595
598
  try {
596
599
  let t = new URL(e), n = t.hostname.toLowerCase();
597
600
  if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
@@ -601,107 +604,107 @@ function Xt(e) {
601
604
  return null;
602
605
  }
603
606
  }
604
- function Zt(e) {
607
+ function Qt(e) {
605
608
  return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
606
609
  }
607
610
  //#endregion
608
611
  //#region src/utils/deletePostCascade.ts
609
- async function Qt(e) {
612
+ async function $t(e) {
610
613
  try {
611
614
  let t = await Ke(e);
612
- await Promise.all([...t.items.map((e) => He(e).catch(() => {})), ...t.prefixes.map((e) => Qt(e))]);
615
+ await Promise.all([...t.items.map((e) => He(e).catch(() => {})), ...t.prefixes.map((e) => $t(e))]);
613
616
  } catch {}
614
617
  }
615
- async function $t(e, t) {
616
- await Qt(qe(e, `${t}/images`)), await Qt(qe(e, `${t}/attachments`)), await Qt(qe(e, `${t}/comment-images`));
618
+ async function en(e, t) {
619
+ await $t(qe(e, `${t}/images`)), await $t(qe(e, `${t}/attachments`)), await $t(qe(e, `${t}/comment-images`));
617
620
  }
618
- var en = 450;
619
- async function tn(e, t, n) {
620
- return (await H(K(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => ue(e.data().images, `${x(t, n)}/comment-images/`));
621
+ var tn = 450;
622
+ async function nn(e, t, n) {
623
+ return (await H(K(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => ce(e.data().images, `${x(t, n)}/comment-images/`));
621
624
  }
622
- async function nn(e, t) {
625
+ async function rn(e, t) {
623
626
  let n = [...new Set([...t].filter(Boolean))];
624
627
  await Promise.all(n.map((t) => He(qe(e, t)).catch(() => {})));
625
628
  }
626
- async function rn(e, t) {
629
+ async function an(e, t) {
627
630
  let n = await H(t);
628
- for (let t = 0; t < n.docs.length; t += en) {
631
+ for (let t = 0; t < n.docs.length; t += tn) {
629
632
  let r = Oe(e);
630
- n.docs.slice(t, t + en).forEach((e) => r.delete(e.ref)), await r.commit();
633
+ n.docs.slice(t, t + tn).forEach((e) => r.delete(e.ref)), await r.commit();
631
634
  }
632
635
  }
633
- async function an(e, t, n) {
634
- await rn(e, K(b(e, t), X("postId", "==", n)));
635
- }
636
636
  async function on(e, t, n) {
637
- await rn(e, K(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
637
+ await an(e, K(b(e, t), X("postId", "==", n)));
638
+ }
639
+ async function sn(e, t, n) {
640
+ await an(e, K(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
638
641
  }
639
- async function sn(e, t, n, r) {
640
- let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([V(i), V(a)]);
642
+ async function cn(e, t, n, r) {
643
+ let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([xe(i), xe(a)]);
641
644
  if (!o.exists()) return;
642
- let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await tn(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
645
+ let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await nn(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
643
646
  for (let e of l.attachments ?? []) {
644
647
  if (!e?.path) continue;
645
648
  f.add(e.path);
646
- let t = Zt(e.path);
649
+ let t = Qt(e.path);
647
650
  t && t !== r && p.add(t);
648
651
  }
649
- for (let e of Yt(u)) {
650
- let t = Xt(e);
652
+ for (let e of Xt(u)) {
653
+ let t = Zt(e);
651
654
  if (!t) continue;
652
655
  f.add(t);
653
656
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
654
657
  n !== t && n.includes("/images/thumbnails/") && f.add(n);
655
658
  let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
656
659
  i && !t.includes("/thumbnails/") && f.add(`${i[1]}thumbnails/${i[2]}.jpg`);
657
- let a = Zt(t);
660
+ let a = Qt(t);
658
661
  a && a !== r && p.add(a);
659
662
  }
660
- await $t(t, x(n, r));
661
- for (let e of p) await $t(t, x(n, e));
662
- await nn(t, d), await nn(t, f), await rn(e, K(g(e, n), X("postId", "==", r))), await an(e, n, r), await ve(a), await ve(i);
663
+ await en(t, x(n, r));
664
+ for (let e of p) await en(t, x(n, e));
665
+ await rn(t, d), await rn(t, f), await an(e, K(g(e, n), X("postId", "==", r))), await on(e, n, r), await ge(a), await ge(i);
663
666
  }
664
667
  //#endregion
665
668
  //#region src/utils/postBody.ts
666
- function cn(e) {
669
+ function ln(e) {
667
670
  return String(e || "").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<img\b[^>]*>/gi, " ").replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, " ").replace(/<video\b[\s\S]*?<\/video>/gi, " ").replace(/<audio\b[\s\S]*?<\/audio>/gi, " ").replace(/<[^>]+>/g, " ").replace(/&nbsp;/gi, " ").replace(/&#160;/g, " ").replace(/&[a-z]+;/gi, " ").replace(/&#\d+;/g, " ").replace(/[\u200B-\u200D\uFEFF\u00AD]/g, "").replace(/\s+/g, " ").trim();
668
671
  }
669
- function ln(e, t = 40) {
670
- let n = cn(e);
672
+ function un(e, t = 40) {
673
+ let n = ln(e);
671
674
  if (!n) return "";
672
675
  if (n.length <= t) return n;
673
676
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
674
677
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
675
678
  }
676
- function un(e) {
677
- return cn(e).length > 0;
679
+ function dn(e) {
680
+ return ln(e).length > 0;
678
681
  }
679
- function dn(e, t) {
682
+ function fn(e, t) {
680
683
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
681
684
  }
682
- function fn(e) {
683
- return !un(e);
685
+ function pn(e) {
686
+ return !dn(e);
684
687
  }
685
688
  //#endregion
686
689
  //#region src/utils/postPreview.ts
687
- function pn(e) {
690
+ function mn(e) {
688
691
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
689
692
  }
690
- function mn(e) {
693
+ function hn(e) {
691
694
  if (e) try {
692
- let t = new URL(pn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
695
+ let t = new URL(mn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
693
696
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
694
697
  if (n.endsWith("youtube.com") || n === "youtube-nocookie.com") return t.pathname.startsWith("/embed/") || t.pathname.startsWith("/shorts/") ? t.pathname.split("/")[2] : t.searchParams.get("v") || void 0;
695
698
  } catch {
696
699
  return;
697
700
  }
698
701
  }
699
- function hn(e) {
700
- let t = mn(e.videoUrl);
702
+ function gn(e) {
703
+ let t = hn(e.videoUrl);
701
704
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
702
705
  }
703
- function gn(e, t = []) {
704
- let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? pn(r[1]) : a, s = o ? mn(o) : void 0, c = n ? pn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
706
+ function _n(e, t = []) {
707
+ let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? mn(r[1]) : a, s = o ? hn(o) : void 0, c = n ? mn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
705
708
  return {
706
709
  summary: e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|blockquote)>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/[^\S\n]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{2,}/g, "\n").trim().slice(0, 160),
707
710
  ...c ? { previewImage: c } : {},
@@ -710,10 +713,10 @@ function gn(e, t = []) {
710
713
  }
711
714
  //#endregion
712
715
  //#region src/utils/saveBoardVisibility.ts
713
- function _n(e) {
716
+ function vn(e) {
714
717
  return e === "hidden" ? "hidden" : "public";
715
718
  }
716
- async function vn(e, t, n, r) {
719
+ async function yn(e, t, n, r) {
717
720
  let i;
718
721
  for (;;) {
719
722
  let a = await H(K(t, X("boardId", "==", n), G("__name__"), ...i ? [J(i)] : [], U(400)));
@@ -723,20 +726,20 @@ async function vn(e, t, n, r) {
723
726
  if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
724
727
  }
725
728
  }
726
- async function yn(e, t, n, r) {
727
- await vn(e, o(e, t), n, r), await vn(e, g(e, t), n, r);
728
- }
729
729
  async function bn(e, t, n, r) {
730
- let i = w(e, t, n), a = _n(r.visibility);
730
+ await yn(e, o(e, t), n, r), await yn(e, g(e, t), n, r);
731
+ }
732
+ async function xn(e, t, n, r) {
733
+ let i = w(e, t, n), a = vn(r.visibility);
731
734
  await Ee(e, async (e) => {
732
735
  let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
733
736
  if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
734
- return !t.exists() || !o && _n(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
737
+ return !t.exists() || !o && vn(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
735
738
  ...r,
736
- visibility: a === "public" ? "public" : _n(n?.visibility),
739
+ visibility: a === "public" ? "public" : vn(n?.visibility),
737
740
  visibilityTransition: a
738
741
  }, { merge: !0 }), !0);
739
- }) && (await yn(e, t, n, a !== "hidden"), await Ee(e, async (e) => {
742
+ }) && (await bn(e, t, n, a !== "hidden"), await Ee(e, async (e) => {
740
743
  if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
741
744
  e.update(i, {
742
745
  visibility: a,
@@ -747,7 +750,7 @@ async function bn(e, t, n, r) {
747
750
  }
748
751
  //#endregion
749
752
  //#region src/utils/moderation-prompt.ts
750
- var xn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", Sn = {
753
+ var Sn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", Cn = {
751
754
  ko: "Korean",
752
755
  en: "English",
753
756
  ja: "Japanese",
@@ -759,12 +762,12 @@ var xn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
759
762
  ar: "Arabic",
760
763
  id: "Indonesian"
761
764
  };
762
- function Cn(e) {
763
- let t = Sn[e] ?? "English", n = fe(e).name;
765
+ function wn(e) {
766
+ let t = Cn[e] ?? "English", n = ue(e).name;
764
767
  return t === n ? t : `${t} (${n})`;
765
768
  }
766
- function wn(e) {
767
- let t = Cn(e);
769
+ function Tn(e) {
770
+ let t = wn(e);
768
771
  return `You are a strict moderator reviewing content for a message board.
769
772
  If the text below contains profanity, vulgar slang, sexual harassment, hate speech, threats, spam, or repetitive/flooding advertisements, you MUST reject it (flagged=true).
770
773
 
@@ -791,17 +794,17 @@ Output JSON only:
791
794
  Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
792
795
  - If flagged=false, category is "none" and reason is ""`;
793
796
  }
794
- function Tn(e = "ko") {
795
- return e === "ko" ? xn : wn(e);
797
+ function En(e = "ko") {
798
+ return e === "ko" ? Sn : Tn(e);
796
799
  }
797
- function En(e, t = "ko") {
800
+ function Dn(e, t = "ko") {
798
801
  let n = `"""${e.slice(0, 4e3)}"""`;
799
802
  return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
800
803
  }
801
- function Dn(e = "ko") {
804
+ function On(e = "ko") {
802
805
  return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
803
806
  }
804
- var On = {
807
+ var kn = {
805
808
  abuse: "욕설·비속어·모욕",
806
809
  spam: "스팸·광고",
807
810
  adult: "선정적·성인 내용",
@@ -809,21 +812,21 @@ var On = {
809
812
  other: "부적절한 내용",
810
813
  none: ""
811
814
  };
812
- function kn(e, t = "ko") {
813
- return !e || e === "none" ? "" : e in On ? A(t, `moderationPrompt.categories.${e}`) : e;
815
+ function An(e, t = "ko") {
816
+ return !e || e === "none" ? "" : e in kn ? M(t, `moderationPrompt.categories.${e}`) : e;
814
817
  }
815
- function An(e) {
818
+ function jn(e) {
816
819
  let t = e.locale ?? "ko";
817
820
  if (e.via === "local" && e.localHit) {
818
821
  let n = e.localHit.trim();
819
- return n.length > 0 && n.length <= 12 ? A(t, "moderationPrompt.localHit", { hit: n }) : A(t, "moderationPrompt.localGeneric");
822
+ return n.length > 0 && n.length <= 12 ? M(t, "moderationPrompt.localHit", { hit: n }) : M(t, "moderationPrompt.localGeneric");
820
823
  }
821
824
  let n = (e.reason || "").trim();
822
825
  if (n) return n;
823
- let r = e.category ? kn(e.category, t) : "";
824
- return r ? A(t, "moderationPrompt.categoryBlocked", { category: r }) : A(t, "moderationPrompt.genericBlocked");
826
+ let r = e.category ? An(e.category, t) : "";
827
+ return r ? M(t, "moderationPrompt.categoryBlocked", { category: r }) : M(t, "moderationPrompt.genericBlocked");
825
828
  }
826
- var jn = [
829
+ var Mn = [
827
830
  "씨발",
828
831
  "시발",
829
832
  "시이발",
@@ -849,10 +852,10 @@ var jn = [
849
852
  "bitch",
850
853
  "asshole"
851
854
  ];
852
- function Mn(e) {
855
+ function Nn(e) {
853
856
  return new RegExp(e.join("|"), "i");
854
857
  }
855
- var Nn = /* @__PURE__ */ new Set([
858
+ var Pn = /* @__PURE__ */ new Set([
856
859
  "none",
857
860
  "abuse",
858
861
  "spam",
@@ -860,7 +863,7 @@ var Nn = /* @__PURE__ */ new Set([
860
863
  "violence",
861
864
  "other"
862
865
  ]);
863
- function Pn(e) {
866
+ function Fn(e) {
864
867
  let t = e.trim();
865
868
  if (!t) return null;
866
869
  let n;
@@ -878,7 +881,7 @@ function Pn(e) {
878
881
  if (typeof n != "object" || !n) return null;
879
882
  let r = n;
880
883
  if (typeof r.flagged != "boolean") return null;
881
- let i = typeof r.category == "string" && Nn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
884
+ let i = typeof r.category == "string" && Pn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
882
885
  return {
883
886
  flagged: r.flagged,
884
887
  category: i,
@@ -887,11 +890,11 @@ function Pn(e) {
887
890
  }
888
891
  //#endregion
889
892
  //#region src/composables/useMemiBoardAutoTranslate.ts
890
- var Fn = 3e4, In = 45e3, Ln = 12e3, Rn = 3;
891
- function zn(e) {
893
+ var In = 3e4, Ln = 45e3, Rn = 12e3, zn = 3;
894
+ function Bn(e) {
892
895
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
893
896
  }
894
- async function Bn(e, t, n) {
897
+ async function Vn(e, t, n) {
895
898
  let r = Array(e.length), i = 0;
896
899
  async function a() {
897
900
  for (; i < e.length;) {
@@ -901,7 +904,7 @@ async function Bn(e, t, n) {
901
904
  }
902
905
  return await Promise.all(Array.from({ length: Math.min(t, e.length) }, () => a())), r;
903
906
  }
904
- function Vn(e, t) {
907
+ function Hn(e, t) {
905
908
  return new Promise((n, r) => {
906
909
  let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
907
910
  e.then((e) => {
@@ -911,14 +914,14 @@ function Vn(e, t) {
911
914
  });
912
915
  });
913
916
  }
914
- function Hn() {
917
+ function Un() {
915
918
  let e = Me(), { moderation: t } = v();
916
919
  async function n(n) {
917
920
  D();
918
921
  let r = Xe(e, {
919
922
  backend: new Je(),
920
923
  useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
921
- }), i = await Vn(Ze(r, {
924
+ }), i = await Hn(Ze(r, {
922
925
  model: t.model ?? "gemini-3.5-flash-lite",
923
926
  systemInstruction: [
924
927
  "Identify the predominant language of the supplied message-board text.",
@@ -931,7 +934,7 @@ function Hn() {
931
934
  responseMimeType: "application/json",
932
935
  responseSchema: Ye.object({ properties: { locale: Ye.enumString({ enum: T.map((e) => e.code) }) } })
933
936
  }
934
- }).generateContent(JSON.stringify({ text: n.slice(0, Ln) })), Fn), a = N(JSON.parse(i.response.text())?.locale);
937
+ }).generateContent(JSON.stringify({ text: n.slice(0, Rn) })), In), a = F(JSON.parse(i.response.text())?.locale);
935
938
  if (!a) throw Error("Could not identify the original language. Please try translating again.");
936
939
  return a;
937
940
  }
@@ -959,12 +962,12 @@ function Hn() {
959
962
  async function i(e, t, n, i) {
960
963
  let a = Object.keys(e.fields), o = [
961
964
  `Purpose: ${i}`,
962
- `Source language: ${Cn(t)} (${t})`,
963
- `Translate into: ${n.map((e) => `${e} = ${Cn(e)}`).join(", ")}`,
965
+ `Source language: ${wn(t)} (${t})`,
966
+ `Translate into: ${n.map((e) => `${e} = ${wn(e)}`).join(", ")}`,
964
967
  e.context ? `Context (do not translate): ${e.context}` : "",
965
968
  "Text (JSON):",
966
969
  JSON.stringify(e.fields)
967
- ].filter(Boolean).join("\n"), s = await Vn(r(n, a).generateContent(o), Fn), c = JSON.parse(s.response.text()), l = {};
970
+ ].filter(Boolean).join("\n"), s = await Hn(r(n, a).generateContent(o), In), c = JSON.parse(s.response.text()), l = {};
968
971
  for (let e of n) {
969
972
  let t = c?.[e];
970
973
  if (!t) continue;
@@ -1010,14 +1013,14 @@ function Hn() {
1010
1013
  D();
1011
1014
  let n = t.source ?? "ko", r = (t.targets ?? T.map((e) => e.code)).filter((e) => e !== n), i = e.trim();
1012
1015
  if (!i || !r.length) return {};
1013
- if (i.length > 12e3) throw Error(`translation is limited to ${Ln} characters`);
1014
- let a = await Bn(r, Rn, async (e) => {
1016
+ if (i.length > 12e3) throw Error(`translation is limited to ${Rn} characters`);
1017
+ let a = await Vn(r, zn, async (e) => {
1015
1018
  let t = [
1016
- `Source language: ${Cn(n)} (${n})`,
1017
- `Translate into: ${e} = ${Cn(e)}`,
1019
+ `Source language: ${wn(n)} (${n})`,
1020
+ `Translate into: ${e} = ${wn(e)}`,
1018
1021
  "HTML:",
1019
1022
  i
1020
- ].join("\n"), r = zn((await Vn(o().generateContent(t), In)).response.text());
1023
+ ].join("\n"), r = Bn((await Hn(o().generateContent(t), Ln)).response.text());
1021
1024
  if (!r) throw Error(`empty translation for ${e}`);
1022
1025
  return [e, r];
1023
1026
  });
@@ -1031,7 +1034,7 @@ function Hn() {
1031
1034
  }
1032
1035
  //#endregion
1033
1036
  //#region src/composables/useMemiBoardSettings.ts
1034
- var Un = [
1037
+ var Wn = [
1035
1038
  {
1036
1039
  id: "free",
1037
1040
  label: "자유",
@@ -1059,17 +1062,17 @@ var Un = [
1059
1062
  visibility: "public",
1060
1063
  order: 2
1061
1064
  }
1062
- ], Wn = Un;
1063
- function Gn(e) {
1065
+ ], Gn = Wn;
1066
+ function Kn(e) {
1064
1067
  return e === "hidden" ? "hidden" : "public";
1065
1068
  }
1066
- function Kn(e, t, n) {
1069
+ function qn(e, t, n) {
1067
1070
  return {
1068
1071
  id: e,
1069
1072
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
1070
1073
  order: typeof t.order == "number" ? t.order : n,
1071
1074
  description: (typeof t.description == "string" ? t.description : "") || "",
1072
- visibility: Gn(t.visibility),
1075
+ visibility: Kn(t.visibility),
1073
1076
  visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
1074
1077
  listView: t.listView ?? "default",
1075
1078
  editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
@@ -1077,7 +1080,7 @@ function Kn(e, t, n) {
1077
1080
  commentWriteRole: t.commentWriteRole ?? "user",
1078
1081
  allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : [],
1079
1082
  allowedManagerUids: Array.isArray(t.allowedManagerUids) ? t.allowedManagerUids : [],
1080
- locale: N(t.locale) ?? "",
1083
+ locale: F(t.locale) ?? "",
1081
1084
  autoTranslate: t.autoTranslate === !0,
1082
1085
  autoTranslatePosts: t.autoTranslatePosts === !0,
1083
1086
  translationLocales: mt(t.translationLocales),
@@ -1085,8 +1088,8 @@ function Kn(e, t, n) {
1085
1088
  i18nSource: ut(t.i18nSource)
1086
1089
  };
1087
1090
  }
1088
- function qn() {
1089
- let e = () => M(), t = Z(), r = Me(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Ut(), { t: l, locale: u } = j(), { translateEntries: d } = Hn(), f = P(() => K(S(t, e()), G("order", "asc"))), { data: m, pending: h } = ke(f, { ssrKey: p(e(), "boards") }), g = P(() => m.value.map((e, t) => Kn(e.id, e, t))), _ = g, v = P(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
1091
+ function Jn() {
1092
+ let e = () => P(), t = Z(), r = Me(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Wt(), { t: l, locale: u } = N(), { translateEntries: d } = Un(), f = I(() => K(S(t, e()), G("order", "asc"))), { data: m, pending: h } = ke(f, { ssrKey: p(e(), "boards") }), g = I(() => m.value.map((e, t) => qn(e.id, e, t))), _ = g, v = I(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
1090
1093
  function b(e, t) {
1091
1094
  if (!e) return;
1092
1095
  let n = g.value.find((t) => t.id === e);
@@ -1106,27 +1109,27 @@ function qn() {
1106
1109
  function O(e) {
1107
1110
  return D(e) === "hidden";
1108
1111
  }
1109
- let te = O;
1110
- function k(e) {
1112
+ let k = O;
1113
+ function A(e) {
1111
1114
  if (e) return g.value.find((t) => t.id === e);
1112
1115
  }
1113
- Mt((e) => k(e));
1114
- function ne(e) {
1115
- return kt(c.value?.uid, a.value, s.value, e ? k(e) : void 0);
1116
+ Nt((e) => A(e));
1117
+ function j(e) {
1118
+ return At(c.value?.uid, a.value, s.value, e ? A(e) : void 0);
1116
1119
  }
1117
- let re = P(() => g.value.filter((e) => ne(e.id)));
1118
- function ie(e, t) {
1120
+ let te = I(() => g.value.filter((e) => j(e.id)));
1121
+ function ne(e, t) {
1119
1122
  return {
1120
1123
  label: e.label.trim(),
1121
1124
  description: (e.description ?? "").trim(),
1122
- visibility: Gn(e.visibility),
1125
+ visibility: Kn(e.visibility),
1123
1126
  listView: e.listView ?? "default",
1124
1127
  editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
1125
1128
  writeRole: e.writeRole ?? "user",
1126
1129
  commentWriteRole: e.commentWriteRole ?? "user",
1127
1130
  allowedStaffUids: e.allowedStaffUids ?? [],
1128
1131
  allowedManagerUids: e.allowedManagerUids ?? [],
1129
- locale: N(e.locale) ?? "",
1132
+ locale: F(e.locale) ?? "",
1130
1133
  autoTranslate: e.autoTranslate === !0,
1131
1134
  autoTranslatePosts: e.autoTranslatePosts === !0,
1132
1135
  translationLocales: mt(e.translationLocales),
@@ -1136,7 +1139,7 @@ function qn() {
1136
1139
  updatedAt: q()
1137
1140
  };
1138
1141
  }
1139
- async function ae(e) {
1142
+ async function re(e) {
1140
1143
  if (!n()) return e;
1141
1144
  let t = e.label.trim(), r = (e.description ?? "").trim();
1142
1145
  if (!t) return e;
@@ -1173,40 +1176,40 @@ function qn() {
1173
1176
  }
1174
1177
  };
1175
1178
  }
1176
- async function A(n, r = n.order ?? 0) {
1179
+ async function M(n, r = n.order ?? 0) {
1177
1180
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
1178
1181
  let a = n.id.trim(), o = n.label.trim();
1179
1182
  if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
1180
- let s = Gn(n.visibility), c = ie({
1183
+ let s = Kn(n.visibility), c = ne({
1181
1184
  ...n,
1182
1185
  label: o,
1183
1186
  visibility: s
1184
1187
  }, r);
1185
- await bn(t, e(), a, c);
1188
+ await xn(t, e(), a, c);
1186
1189
  }
1187
- let oe = A;
1188
- async function se(e) {
1190
+ let ie = M;
1191
+ async function ae(e) {
1189
1192
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
1190
1193
  for (let t of e) if (!t.id.trim() || !t.label.trim()) throw Error(l("boardSettings.idAndNameRequired"));
1191
- for (let [t, n] of e.entries()) await A(n, t);
1194
+ for (let [t, n] of e.entries()) await M(n, t);
1192
1195
  }
1193
- let ce = se;
1194
- async function le(n) {
1196
+ let oe = ae;
1197
+ async function se(n) {
1195
1198
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
1196
1199
  let a = n.trim();
1197
1200
  if (!a) throw Error(l("boardSettings.deleteIdRequired"));
1198
1201
  for (;;) {
1199
1202
  let n = await H(K(o(t, e()), X("boardId", "==", a), U(25)));
1200
1203
  if (n.empty) break;
1201
- for (let i of n.docs) await sn(t, We(r), e(), i.id);
1204
+ for (let i of n.docs) await cn(t, We(r), e(), i.id);
1202
1205
  }
1203
- await ve(w(t, e(), n));
1206
+ await ge(w(t, e(), n));
1204
1207
  }
1205
- let ue = le;
1206
- async function de() {
1208
+ let ce = se;
1209
+ async function le() {
1207
1210
  return Promise.resolve();
1208
1211
  }
1209
- async function fe(e) {
1212
+ async function ue(e) {
1210
1213
  let t = e.trim();
1211
1214
  if (!t) throw Error(l("boardSettings.nameRequired"));
1212
1215
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
@@ -1214,7 +1217,7 @@ function qn() {
1214
1217
  if (n) return n.id;
1215
1218
  let r = E(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(g.value.map((e) => e.id));
1216
1219
  for (; s.has(a);) a = `${r}-${o++}`;
1217
- return await A({
1220
+ return await M({
1218
1221
  id: a,
1219
1222
  label: t,
1220
1223
  visibility: "public",
@@ -1232,45 +1235,45 @@ function qn() {
1232
1235
  boardDescription: C,
1233
1236
  boardVisibility: D,
1234
1237
  isBoardHidden: O,
1235
- getBoard: k,
1236
- canManageBoard: ne,
1237
- managedBoards: re,
1238
+ getBoard: A,
1239
+ canManageBoard: j,
1240
+ managedBoards: te,
1238
1241
  categories: _,
1239
1242
  publicCategories: y,
1240
1243
  categoryLabel: x,
1241
1244
  categoryDescription: T,
1242
1245
  categoryVisibility: ee,
1243
- isCategoryHidden: te,
1246
+ isCategoryHidden: k,
1244
1247
  settingsPending: h,
1245
- ensureSettings: de,
1246
- addBoard: fe,
1247
- addCategory: fe,
1248
- saveBoard: A,
1249
- saveCategory: oe,
1250
- translateBoard: ae,
1251
- saveBoards: se,
1252
- saveCategories: ce,
1253
- deleteBoard: le,
1254
- deleteCategory: ue,
1255
- DEFAULT_BOARDS: Un,
1256
- DEFAULT_CATEGORIES: Wn
1248
+ ensureSettings: le,
1249
+ addBoard: ue,
1250
+ addCategory: ue,
1251
+ saveBoard: M,
1252
+ saveCategory: ie,
1253
+ translateBoard: re,
1254
+ saveBoards: ae,
1255
+ saveCategories: oe,
1256
+ deleteBoard: se,
1257
+ deleteCategory: ce,
1258
+ DEFAULT_BOARDS: Wn,
1259
+ DEFAULT_CATEGORIES: Gn
1257
1260
  };
1258
1261
  }
1259
1262
  //#endregion
1260
1263
  //#region src/composables/useMemiBoardPosts.ts
1261
- var Jn = /* @__PURE__ */ new Map();
1262
- function Yn(e) {
1264
+ var Yn = /* @__PURE__ */ new Map();
1265
+ function Xn(e) {
1263
1266
  if (!e || typeof e != "object") return !1;
1264
1267
  let t = "code" in e ? String(e.code) : "";
1265
1268
  return t === "permission-denied" || t === "firestore/permission-denied";
1266
1269
  }
1267
- function Xn(e, t) {
1268
- let n = L(e)?.trim();
1269
- if (!n) throw Error(A(t, "posts.boardIdRequired"));
1270
+ function Zn(e, t) {
1271
+ let n = z(e)?.trim();
1272
+ if (!n) throw Error(M(t, "posts.boardIdRequired"));
1270
1273
  return n;
1271
1274
  }
1272
- function Zn(e) {
1273
- let t = Z(), n = Me(), { isBoardHidden: r, getBoard: i } = qn(), { user: a, canManageBoard: c } = Ut(), { t: l, locale: u } = j(), d = () => M(), f = () => Xn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
1275
+ function Qn(e) {
1276
+ let t = Z(), n = Me(), { isBoardHidden: r, getBoard: i } = Jn(), { user: a, canManageBoard: c } = Wt(), { t: l, locale: u } = N(), d = () => P(), f = () => Zn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
1274
1277
  function g(e, t, n) {
1275
1278
  return {
1276
1279
  id: e,
@@ -1289,7 +1292,7 @@ function Zn(e) {
1289
1292
  return i(f())?.editorType === "image";
1290
1293
  }
1291
1294
  function b(e) {
1292
- let t = v(), n = un(e.content), r = dn(e.content, e.attachments);
1295
+ let t = v(), n = dn(e.content), r = fn(e.content, e.attachments);
1293
1296
  if (t) {
1294
1297
  if (!r) throw Error(l("posts.photoRequired"));
1295
1298
  return;
@@ -1298,10 +1301,10 @@ function Zn(e) {
1298
1301
  if (!e.title?.trim()) throw Error(l("posts.titleRequired"));
1299
1302
  }
1300
1303
  function x() {
1301
- return z(p()).id;
1304
+ return ve(p()).id;
1302
1305
  }
1303
1306
  function S(e) {
1304
- return Jt(t, d(), f(), e, a.value?.uid, c(f()));
1307
+ return Yt(t, d(), f(), e, a.value?.uid, c(f()));
1305
1308
  }
1306
1309
  async function C(e = {}) {
1307
1310
  let t = e.pageSize ?? 20, n = [X("boardId", "==", f()), X("isPublished", "==", !0)];
@@ -1317,7 +1320,7 @@ function Zn(e) {
1317
1320
  };
1318
1321
  }
1319
1322
  async function w(e) {
1320
- let [t, n] = await Promise.all([V(m(e)), V(h(e))]);
1323
+ let [t, n] = await Promise.all([xe(m(e)), xe(h(e))]);
1321
1324
  return t.exists() ? g(t.id, t.data(), n.exists() ? n.data() : void 0) : null;
1322
1325
  }
1323
1326
  async function T(e) {
@@ -1331,20 +1334,20 @@ function Zn(e) {
1331
1334
  post: e
1332
1335
  } : { status: "not-found" };
1333
1336
  } catch (e) {
1334
- return Yn(e) ? { status: "permission-denied" } : {
1337
+ return Xn(e) ? { status: "permission-denied" } : {
1335
1338
  status: "error",
1336
1339
  message: e instanceof Error ? e.message : String(e)
1337
1340
  };
1338
1341
  }
1339
1342
  } catch (e) {
1340
- return Yn(e) ? { status: "permission-denied" } : {
1343
+ return Xn(e) ? { status: "permission-denied" } : {
1341
1344
  status: "error",
1342
1345
  message: e instanceof Error ? e.message : String(e)
1343
1346
  };
1344
1347
  }
1345
1348
  }
1346
1349
  async function ee(e) {
1347
- let t = e.trim(), n = Jn.get(`${f()}/${t}`);
1350
+ let t = e.trim(), n = Yn.get(`${f()}/${t}`);
1348
1351
  if (n) return {
1349
1352
  status: "ok",
1350
1353
  id: n
@@ -1356,7 +1359,7 @@ function Zn(e) {
1356
1359
  id: e.id
1357
1360
  } : { status: r(f()) ? "permission-denied" : "not-found" };
1358
1361
  } catch (e) {
1359
- return Yn(e) ? { status: "permission-denied" } : {
1362
+ return Xn(e) ? { status: "permission-denied" } : {
1360
1363
  status: "error",
1361
1364
  message: e instanceof Error ? e.message : String(e)
1362
1365
  };
@@ -1365,7 +1368,7 @@ function Zn(e) {
1365
1368
  async function O(e) {
1366
1369
  return `${E(e.trim()) || "post"}-${x()}`;
1367
1370
  }
1368
- async function te(e) {
1371
+ async function k(e) {
1369
1372
  if (!e.createdAt) return {
1370
1373
  previous: null,
1371
1374
  next: null
@@ -1376,7 +1379,7 @@ function Zn(e) {
1376
1379
  X("isPublished", "==", !0),
1377
1380
  ...e.listed === !1 ? [] : [X("listed", "==", !0)],
1378
1381
  G("createdAt", "desc")
1379
- ], [n, r] = await Promise.all([H(K(p(), ...t, J(e.createdAt), U(1))), H(K(p(), ...t, xe(e.createdAt), Te(1)))]), i = (e) => {
1382
+ ], [n, r] = await Promise.all([H(K(p(), ...t, J(e.createdAt), U(1))), H(K(p(), ...t, be(e.createdAt), Te(1)))]), i = (e) => {
1380
1383
  let t = e.docs[0];
1381
1384
  return t ? {
1382
1385
  id: t.id,
@@ -1394,11 +1397,11 @@ function Zn(e) {
1394
1397
  };
1395
1398
  }
1396
1399
  }
1397
- async function k(e) {
1400
+ async function A(e) {
1398
1401
  b(e);
1399
1402
  let n = e.postId || x(), r = v(), i, a;
1400
- r ? (i = n, a = ln(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await O(a));
1401
- let o = gn(e.content, e.attachments), s = _(), c = Oe(t);
1403
+ r ? (i = n, a = un(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await O(a));
1404
+ let o = _n(e.content, e.attachments), s = _(), c = Oe(t);
1402
1405
  return c.set(m(n), {
1403
1406
  boardId: f(),
1404
1407
  slug: i,
@@ -1428,16 +1431,16 @@ function Zn(e) {
1428
1431
  i18n: e.translations.contentI18n,
1429
1432
  i18nSource: e.translations.contentI18nSource
1430
1433
  } : {}
1431
- }), await c.commit(), Jn.set(`${f()}/${i}`, n), i;
1434
+ }), await c.commit(), Yn.set(`${f()}/${i}`, n), i;
1432
1435
  }
1433
- async function ne(e, t) {
1436
+ async function j(e, t) {
1434
1437
  b(t);
1435
- let n = v() ? ln(t.content) : t.title.trim(), r = gn(t.content, t.attachments), i = _();
1438
+ let n = v() ? un(t.content) : t.title.trim(), r = _n(t.content, t.attachments), i = _();
1436
1439
  await Promise.all([Y(m(e), {
1437
1440
  title: n,
1438
1441
  ...r,
1439
- previewImage: r.previewImage ? r.previewImage : ye(),
1440
- videoUrl: r.videoUrl ? r.videoUrl : ye(),
1442
+ previewImage: r.previewImage ? r.previewImage : _e(),
1443
+ videoUrl: r.videoUrl ? r.videoUrl : _e(),
1441
1444
  tags: t.tags ?? [],
1442
1445
  listed: i,
1443
1446
  attachments: t.attachments ?? [],
@@ -1452,12 +1455,12 @@ function Zn(e) {
1452
1455
  i18n: t.translations.contentI18n,
1453
1456
  i18nSource: t.translations.contentI18nSource
1454
1457
  } : {
1455
- i18n: ye(),
1456
- i18nSource: ye()
1458
+ i18n: _e(),
1459
+ i18nSource: _e()
1457
1460
  } : {}
1458
1461
  }, { merge: !0 })]);
1459
1462
  }
1460
- async function re(e, t) {
1463
+ async function te(e, t) {
1461
1464
  D(), await Promise.all([Y(m(e), {
1462
1465
  i18n: t.i18n,
1463
1466
  i18nSource: t.i18nSource,
@@ -1466,29 +1469,29 @@ function Zn(e) {
1466
1469
  i18n: t.contentI18n,
1467
1470
  i18nSource: t.contentI18nSource
1468
1471
  } : {
1469
- i18n: ye(),
1470
- i18nSource: ye()
1472
+ i18n: _e(),
1473
+ i18nSource: _e()
1471
1474
  }, { merge: !0 })]);
1472
1475
  }
1473
- async function ie(e) {
1476
+ async function ne(e) {
1474
1477
  await Y(m(e), {
1475
1478
  isPublished: !0,
1476
1479
  publishedAt: q(),
1477
1480
  updatedAt: q()
1478
1481
  });
1479
1482
  }
1480
- async function ae(e, t) {
1483
+ async function re(e, t) {
1481
1484
  await Y(m(e), t ? {
1482
1485
  pinned: !0,
1483
1486
  pinnedAt: q()
1484
1487
  } : {
1485
- pinned: ye(),
1486
- pinnedAt: ye()
1488
+ pinned: _e(),
1489
+ pinnedAt: _e()
1487
1490
  });
1488
1491
  }
1489
- async function A(e) {
1490
- await sn(t, We(n), d(), e);
1491
- for (let [t, n] of Jn) n === e && Jn.delete(t);
1492
+ async function M(e) {
1493
+ await cn(t, We(n), d(), e);
1494
+ for (let [t, n] of Yn) n === e && Yn.delete(t);
1492
1495
  }
1493
1496
  return {
1494
1497
  createPostId: x,
@@ -1497,22 +1500,22 @@ function Zn(e) {
1497
1500
  getPostBySlug: T,
1498
1501
  resolvePostIdBySlug: ee,
1499
1502
  resolveUniqueSlug: O,
1500
- getAdjacentPosts: te,
1501
- createPost: k,
1502
- updatePost: ne,
1503
- savePostTranslations: re,
1504
- publishPost: ie,
1505
- setPostPinned: ae,
1506
- deletePost: A
1503
+ getAdjacentPosts: k,
1504
+ createPost: A,
1505
+ updatePost: j,
1506
+ savePostTranslations: te,
1507
+ publishPost: ne,
1508
+ setPostPinned: re,
1509
+ deletePost: M
1507
1510
  };
1508
1511
  }
1509
- var Qn = 10, $n = 10;
1510
- function er(e, t = {}) {
1511
- let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Ut(), s = () => M(), c = t.pageSize ?? Qn, l = P(() => i.value?.uid ?? ""), u = P(() => Xn(e)), d = () => o(n, s()), f = P(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = I([]), m = I([]), h = I([]), g = I([]), _ = I(!0), v = I(!0), y = I(!0), b = I(!1), x = I(""), S = null, C = null, w, T, E;
1512
+ var $n = 10, er = 10;
1513
+ function tr(e, t = {}) {
1514
+ let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Wt(), s = () => P(), c = t.pageSize ?? $n, l = I(() => i.value?.uid ?? ""), u = I(() => Zn(e)), d = () => o(n, s()), f = I(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = R([]), m = R([]), h = R([]), g = R([]), _ = R(!0), v = R(!0), y = R(!0), b = R(!1), x = R(""), S = null, C = null, w, T, E;
1512
1515
  function D(e) {
1513
1516
  return e.createdAt?.toMillis?.() ?? 0;
1514
1517
  }
1515
- let ee = P(() => {
1518
+ let ee = I(() => {
1516
1519
  let e = new Set(g.value.map((e) => e.id)), t = /* @__PURE__ */ new Map();
1517
1520
  for (let n of [
1518
1521
  ...m.value,
@@ -1525,8 +1528,8 @@ function er(e, t = {}) {
1525
1528
  let e = [X("boardId", "==", u.value), X("isPublished", "==", !0)];
1526
1529
  return f.value && e.push(X("listed", "==", !0)), e;
1527
1530
  }
1528
- function te() {
1529
- w?.(), w = W(K(d(), ...O(), G("createdAt", "desc"), G(B(), "desc"), U(c)), (e) => {
1531
+ function k() {
1532
+ w?.(), w = W(K(d(), ...O(), G("createdAt", "desc"), G(V(), "desc"), U(c)), (e) => {
1530
1533
  p.value = e.docs.map((e) => ({
1531
1534
  id: e.id,
1532
1535
  ...e.data()
@@ -1535,8 +1538,8 @@ function er(e, t = {}) {
1535
1538
  console.error("[memi-board:postList] onSnapshot failed", e), x.value = e.message || String(e), v.value = !1;
1536
1539
  });
1537
1540
  }
1538
- function k() {
1539
- E?.(), E = W(K(d(), ...O(), X("pinned", "==", !0), G("pinnedAt", "desc"), U($n)), (e) => {
1541
+ function A() {
1542
+ E?.(), E = W(K(d(), ...O(), X("pinned", "==", !0), G("pinnedAt", "desc"), U(er)), (e) => {
1540
1543
  g.value = e.docs.map((e) => ({
1541
1544
  id: e.id,
1542
1545
  ...e.data()
@@ -1545,7 +1548,7 @@ function er(e, t = {}) {
1545
1548
  console.error("[memi-board:postList] notice onSnapshot failed", e), g.value = [], _.value = !1;
1546
1549
  });
1547
1550
  }
1548
- function ne() {
1551
+ function j() {
1549
1552
  T?.();
1550
1553
  let e = l.value;
1551
1554
  if (!e) {
@@ -1561,14 +1564,14 @@ function er(e, t = {}) {
1561
1564
  console.error("[memi-board:postList] own draft onSnapshot failed", e);
1562
1565
  });
1563
1566
  }
1564
- async function re() {
1567
+ async function te() {
1565
1568
  if (!(b.value || !y.value || v.value)) {
1566
1569
  b.value = !0, x.value = "";
1567
1570
  try {
1568
1571
  let e = C ?? S, t = [
1569
1572
  ...O(),
1570
1573
  G("createdAt", "desc"),
1571
- G(B(), "desc"),
1574
+ G(V(), "desc"),
1572
1575
  ...e ? [J(e)] : [],
1573
1576
  U(c + 1)
1574
1577
  ], n = await H(K(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
@@ -1583,29 +1586,29 @@ function er(e, t = {}) {
1583
1586
  }
1584
1587
  }
1585
1588
  }
1586
- function ie() {
1587
- w?.(), p.value = [], m.value = [], S = null, C = null, y.value = !0, x.value = "", v.value = !0, g.value = [], _.value = !0, te(), k(), ne();
1589
+ function ne() {
1590
+ w?.(), p.value = [], m.value = [], S = null, C = null, y.value = !0, x.value = "", v.value = !0, g.value = [], _.value = !0, k(), A(), j();
1588
1591
  }
1589
- return R([
1592
+ return B([
1590
1593
  u,
1591
1594
  f,
1592
1595
  l
1593
- ], ie, { immediate: !0 }), F(() => {
1596
+ ], ne, { immediate: !0 }), L(() => {
1594
1597
  w?.(), E?.(), T?.();
1595
1598
  }), {
1596
1599
  posts: ee,
1597
1600
  notices: g,
1598
- postsPending: P(() => v.value || _.value),
1601
+ postsPending: I(() => v.value || _.value),
1599
1602
  hasMore: y,
1600
1603
  loadingMore: b,
1601
1604
  loadError: x,
1602
- loadMore: re
1605
+ loadMore: te
1603
1606
  };
1604
1607
  }
1605
- function tr(e, t) {
1606
- let n = Z(), r = () => M(), i = P(() => Xn(e)), a = P(() => typeof t == "string" ? t : t.value), o = P(() => y(n, r(), a.value)), c = P(() => s(n, r(), a.value)), { data: l, pending: u } = je(o), { data: d, pending: f } = je(c);
1608
+ function nr(e, t) {
1609
+ let n = Z(), r = () => P(), i = I(() => Zn(e)), a = I(() => typeof t == "string" ? t : t.value), o = I(() => y(n, r(), a.value)), c = I(() => s(n, r(), a.value)), { data: l, pending: u } = je(o), { data: d, pending: f } = je(c);
1607
1610
  return {
1608
- post: P(() => {
1611
+ post: I(() => {
1609
1612
  if (!l.value || l.value.boardId !== i.value) return null;
1610
1613
  let e = d.value;
1611
1614
  return {
@@ -1618,14 +1621,14 @@ function tr(e, t) {
1618
1621
  contentI18nSource: dt(e?.i18nSource)
1619
1622
  };
1620
1623
  }),
1621
- pending: P(() => u.value || f.value)
1624
+ pending: I(() => u.value || f.value)
1622
1625
  };
1623
1626
  }
1624
1627
  //#endregion
1625
1628
  //#region src/composables/useMemiBoardUserPosts.ts
1626
- var nr = 10;
1627
- function rr(e, t = {}) {
1628
- let n = Z(), r = () => M(), i = () => o(n, r()), a = t.pageSize ?? nr, s = P(() => L(e)?.trim() || ""), c = I([]), l = I([]), u = I(!0), d = I(!0), f = I(!1), p = I(""), m = null, h = null, g;
1629
+ var rr = 10;
1630
+ function ir(e, t = {}) {
1631
+ let n = Z(), r = () => P(), i = () => o(n, r()), a = t.pageSize ?? rr, s = I(() => z(e)?.trim() || ""), c = R([]), l = R([]), u = R(!0), d = R(!0), f = R(!1), p = R(""), m = null, h = null, g;
1629
1632
  function _(e) {
1630
1633
  return {
1631
1634
  id: e.id,
@@ -1635,7 +1638,7 @@ function rr(e, t = {}) {
1635
1638
  function v(e) {
1636
1639
  return e.createdAt?.toMillis?.() ?? 0;
1637
1640
  }
1638
- let y = P(() => {
1641
+ let y = I(() => {
1639
1642
  let e = /* @__PURE__ */ new Map();
1640
1643
  for (let t of [...l.value, ...c.value]) t.id && e.set(t.id, t);
1641
1644
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
@@ -1652,7 +1655,7 @@ function rr(e, t = {}) {
1652
1655
  u.value = !1;
1653
1656
  return;
1654
1657
  }
1655
- g = W(K(i(), ...b(), G("createdAt", "desc"), G(B(), "desc"), U(a)), (e) => {
1658
+ g = W(K(i(), ...b(), G("createdAt", "desc"), G(V(), "desc"), U(a)), (e) => {
1656
1659
  c.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), u.value = !1;
1657
1660
  }, (e) => {
1658
1661
  console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
@@ -1665,7 +1668,7 @@ function rr(e, t = {}) {
1665
1668
  let e = h ?? m, t = [
1666
1669
  ...b(),
1667
1670
  G("createdAt", "desc"),
1668
- G(B(), "desc"),
1671
+ G(V(), "desc"),
1669
1672
  ...e ? [J(e)] : [],
1670
1673
  U(a + 1)
1671
1674
  ], n = await H(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), s = new Set([...l.value, ...c.value].map((e) => e.id));
@@ -1680,7 +1683,7 @@ function rr(e, t = {}) {
1680
1683
  function C() {
1681
1684
  g?.(), c.value = [], l.value = [], m = null, h = null, d.value = !0, p.value = "", u.value = !0, x();
1682
1685
  }
1683
- return R(s, C, { immediate: !0 }), F(() => g?.()), {
1686
+ return B(s, C, { immediate: !0 }), L(() => g?.()), {
1684
1687
  posts: y,
1685
1688
  postsPending: u,
1686
1689
  hasMore: d,
@@ -1691,9 +1694,9 @@ function rr(e, t = {}) {
1691
1694
  }
1692
1695
  //#endregion
1693
1696
  //#region src/composables/useMemiBoardUserComments.ts
1694
- var ir = 10;
1695
- function ar(e, t = {}) {
1696
- let n = Z(), r = () => M(), i = () => g(n, r()), a = t.pageSize ?? ir, o = P(() => L(e)?.trim() || ""), s = I([]), c = I([]), l = I(!0), u = I(!0), d = I(!1), f = I(""), p = null, m = null, h;
1697
+ var ar = 10;
1698
+ function or(e, t = {}) {
1699
+ let n = Z(), r = () => P(), i = () => g(n, r()), a = t.pageSize ?? ar, o = I(() => z(e)?.trim() || ""), s = R([]), c = R([]), l = R(!0), u = R(!0), d = R(!1), f = R(""), p = null, m = null, h;
1697
1700
  function _(e) {
1698
1701
  return {
1699
1702
  id: e.id,
@@ -1703,7 +1706,7 @@ function ar(e, t = {}) {
1703
1706
  function v(e) {
1704
1707
  return e.createdAt?.toMillis?.() ?? 0;
1705
1708
  }
1706
- let y = P(() => {
1709
+ let y = I(() => {
1707
1710
  let e = /* @__PURE__ */ new Map();
1708
1711
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1709
1712
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
@@ -1716,7 +1719,7 @@ function ar(e, t = {}) {
1716
1719
  l.value = !1;
1717
1720
  return;
1718
1721
  }
1719
- h = W(K(i(), ...b(), G("createdAt", "desc"), G(B(), "desc"), U(a)), (e) => {
1722
+ h = W(K(i(), ...b(), G("createdAt", "desc"), G(V(), "desc"), U(a)), (e) => {
1720
1723
  s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
1721
1724
  }, (e) => {
1722
1725
  console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
@@ -1729,7 +1732,7 @@ function ar(e, t = {}) {
1729
1732
  let e = m ?? p, t = [
1730
1733
  ...b(),
1731
1734
  G("createdAt", "desc"),
1732
- G(B(), "desc"),
1735
+ G(V(), "desc"),
1733
1736
  ...e ? [J(e)] : [],
1734
1737
  U(a + 1)
1735
1738
  ], n = await H(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
@@ -1744,7 +1747,7 @@ function ar(e, t = {}) {
1744
1747
  function C() {
1745
1748
  h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, x();
1746
1749
  }
1747
- return R(o, C, { immediate: !0 }), F(() => h?.()), {
1750
+ return B(o, C, { immediate: !0 }), L(() => h?.()), {
1748
1751
  comments: y,
1749
1752
  commentsPending: l,
1750
1753
  hasMore: u,
@@ -1755,12 +1758,12 @@ function ar(e, t = {}) {
1755
1758
  }
1756
1759
  //#endregion
1757
1760
  //#region src/composables/useMemiBoardCommentFeed.ts
1758
- var or = 10;
1759
- function sr(e = "latest", t = {}) {
1760
- let n = Z(), r = () => M(), i = () => g(n, r()), a = P(() => {
1761
- let e = Math.floor(Number(L(t.pageSize)));
1762
- return Number.isFinite(e) && e > 0 ? e : or;
1763
- }), o = P(() => L(e) === "likes" ? "likes" : "latest"), s = I([]), c = I([]), l = I(!0), u = I(!0), d = I(!1), f = I(""), p = null, m = null, h;
1761
+ var sr = 10;
1762
+ function cr(e = "latest", t = {}) {
1763
+ let n = Z(), r = () => P(), i = () => g(n, r()), a = I(() => {
1764
+ let e = Math.floor(Number(z(t.pageSize)));
1765
+ return Number.isFinite(e) && e > 0 ? e : sr;
1766
+ }), o = I(() => z(e) === "likes" ? "likes" : "latest"), s = R([]), c = R([]), l = R(!0), u = R(!0), d = R(!1), f = R(""), p = null, m = null, h;
1764
1767
  function _(e) {
1765
1768
  return {
1766
1769
  id: e.id,
@@ -1777,7 +1780,7 @@ function sr(e = "latest", t = {}) {
1777
1780
  }
1778
1781
  return v(t) - v(e) || String(t.id).localeCompare(String(e.id));
1779
1782
  }
1780
- let b = P(() => {
1783
+ let b = I(() => {
1781
1784
  let e = /* @__PURE__ */ new Map();
1782
1785
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1783
1786
  return [...e.values()].sort(y);
@@ -1787,11 +1790,11 @@ function sr(e = "latest", t = {}) {
1787
1790
  X("listed", "==", !0),
1788
1791
  G("likeCount", "desc"),
1789
1792
  G("createdAt", "desc"),
1790
- G(B(), "desc")
1793
+ G(V(), "desc")
1791
1794
  ] : [
1792
1795
  X("listed", "==", !0),
1793
1796
  G("createdAt", "desc"),
1794
- G(B(), "desc")
1797
+ G(V(), "desc")
1795
1798
  ];
1796
1799
  }
1797
1800
  function S() {
@@ -1817,7 +1820,7 @@ function sr(e = "latest", t = {}) {
1817
1820
  function w() {
1818
1821
  h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, S();
1819
1822
  }
1820
- return R([o, a], w, { immediate: !0 }), F(() => h?.()), {
1823
+ return B([o, a], w, { immediate: !0 }), L(() => h?.()), {
1821
1824
  comments: b,
1822
1825
  commentsPending: l,
1823
1826
  hasMore: u,
@@ -1829,35 +1832,35 @@ function sr(e = "latest", t = {}) {
1829
1832
  }
1830
1833
  //#endregion
1831
1834
  //#region src/composables/useMemiBoardAuthorMemo.ts
1832
- var cr = 100, lr = /* @__PURE__ */ new Map();
1835
+ var lr = 100, ur = /* @__PURE__ */ new Map();
1833
1836
  function $(e) {
1834
- let t = lr.get(e);
1835
- return t || (t = he({
1837
+ let t = ur.get(e);
1838
+ return t || (t = pe({
1836
1839
  text: "",
1837
1840
  sentiment: null,
1838
1841
  loaded: !1,
1839
1842
  pending: !1,
1840
1843
  saving: !1,
1841
1844
  error: ""
1842
- }), lr.set(e, t)), t;
1845
+ }), ur.set(e, t)), t;
1843
1846
  }
1844
- function ur(e) {
1845
- let t = Z(), n = () => M(), { user: r } = Ut(), { t: i, formatNumber: a } = j(), o = P(() => L(e)), s = P(() => `${r.value?.uid ?? ""}:${o.value}`), c = P(() => {
1847
+ function dr(e) {
1848
+ let t = Z(), n = () => P(), { user: r } = Wt(), { t: i, formatNumber: a } = N(), o = I(() => z(e)), s = I(() => `${r.value?.uid ?? ""}:${o.value}`), c = I(() => {
1846
1849
  let e = r.value?.uid, i = o.value;
1847
1850
  return !e || !i ? null : f(t, n(), e, i);
1848
- }), l = P({
1851
+ }), l = I({
1849
1852
  get: () => $(s.value).text,
1850
1853
  set: (e) => {
1851
1854
  $(s.value).text = e;
1852
1855
  }
1853
- }), u = P(() => $(s.value).sentiment), d = P(() => $(s.value).loaded), p = P(() => $(s.value).pending), m = P(() => $(s.value).saving), h = P(() => $(s.value).error);
1856
+ }), u = I(() => $(s.value).sentiment), d = I(() => $(s.value).loaded), p = I(() => $(s.value).pending), m = I(() => $(s.value).saving), h = I(() => $(s.value).error);
1854
1857
  async function g() {
1855
1858
  let e = c.value;
1856
1859
  if (!e) return;
1857
1860
  let t = $(s.value);
1858
1861
  t.pending = !0, t.error = "";
1859
1862
  try {
1860
- let n = (await V(e)).data();
1863
+ let n = (await xe(e)).data();
1861
1864
  t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
1862
1865
  } catch (e) {
1863
1866
  t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
@@ -1890,7 +1893,7 @@ function ur(e) {
1890
1893
  let t = $(s.value);
1891
1894
  t.saving = !0, t.error = "";
1892
1895
  try {
1893
- await ve(e), t.text = "", t.sentiment = null, t.loaded = !0;
1896
+ await ge(e), t.text = "", t.sentiment = null, t.loaded = !0;
1894
1897
  } catch (e) {
1895
1898
  throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
1896
1899
  } finally {
@@ -1910,108 +1913,344 @@ function ur(e) {
1910
1913
  };
1911
1914
  }
1912
1915
  //#endregion
1916
+ //#region src/composables/useMemiBoardWritingAssistant.ts
1917
+ var fr = 20, pr = 864e5, mr = 200, hr = {
1918
+ proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
1919
+ polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
1920
+ continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
1921
+ summarize: "핵심 정보는 보존하면서 원문의 약 절반 길이로 간결하게 줄여라.",
1922
+ "translate-en": "자연스러운 영어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
1923
+ "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
1924
+ title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
1925
+ };
1926
+ function gr(e) {
1927
+ return {
1928
+ proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
1929
+ polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
1930
+ continue: "Continue writing one or two paragraphs that match the preceding context and tone, without exaggeration.",
1931
+ summarize: "Shorten the text concisely to about half of its original length while preserving the key information.",
1932
+ "translate-en": "Translate into natural English. Keep proper nouns and technical terms accurate.",
1933
+ "translate-ko": "Translate into natural Korean. Keep proper nouns and technical terms accurate.",
1934
+ title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
1935
+ };
1936
+ }
1937
+ function _r(e, t, n) {
1938
+ let r = t === "ko", i = wn(t);
1939
+ if (e.action !== "custom") {
1940
+ if (r) return hr[e.action];
1941
+ let t = gr(i)[e.action];
1942
+ return e.action === "translate-en" || e.action === "translate-ko" || e.action === "title" ? t : `${t} Write the result in the same language as the original text; if unclear, write in ${i}.`;
1943
+ }
1944
+ let a = e.customInstruction?.trim().slice(0, mr);
1945
+ if (!a) throw Error(n("writingAssistant.customRequired"));
1946
+ return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
1947
+ }
1948
+ function vr(e) {
1949
+ return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
1950
+ }
1951
+ function yr(e) {
1952
+ return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
1953
+ }
1954
+ function br(e = {}) {
1955
+ let t = N(), n = I(() => F(z(e.locale)) ?? t.locale.value), r = (e, t) => M(n.value, e, t), i = Me(), a = Z(), o = Ae(), s = v(), { translateEntries: c, translateHtml: l, detectSourceLocale: u } = Un();
1956
+ function d(e, t) {
1957
+ let n = yr(e.aiWritingWindowAt), r = n != null && t - n < pr, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
1958
+ return {
1959
+ active: r,
1960
+ windowAt: n,
1961
+ count: r ? i : 0,
1962
+ pending: r ? a : 0
1963
+ };
1964
+ }
1965
+ function f(e, t) {
1966
+ let n = e + pr, i = Math.max(1, Math.ceil((n - t) / 36e5));
1967
+ return Error(r("writingAssistant.dailyLimit", {
1968
+ limit: 20,
1969
+ hours: i
1970
+ }));
1971
+ }
1972
+ async function p(e) {
1973
+ let t = o.value?.uid;
1974
+ if (!t) throw Error(r("writingAssistant.loginRequired"));
1975
+ await Ee(a, async (n) => {
1976
+ let i = h(a, P(), t), o = await n.get(i);
1977
+ if (!o.exists()) throw Error(r("writingAssistant.userMissing"));
1978
+ let s = o.data(), c = e(d(s, Date.now()), s);
1979
+ c && n.update(i, {
1980
+ ...c,
1981
+ updatedAt: q()
1982
+ });
1983
+ });
1984
+ }
1985
+ async function m() {
1986
+ await p((e) => {
1987
+ if (e.active && e.windowAt != null && e.count + e.pending >= 20) throw f(e.windowAt, Date.now());
1988
+ return e.active ? { aiWritingPending: e.pending + 1 } : {
1989
+ aiWritingCount: 0,
1990
+ aiWritingPending: 1,
1991
+ aiWritingWindowAt: q()
1992
+ };
1993
+ });
1994
+ }
1995
+ async function g() {
1996
+ await p((e) => !e.active || e.pending < 1 || e.count >= 20 ? null : {
1997
+ aiWritingCount: e.count + 1,
1998
+ aiWritingPending: e.pending - 1
1999
+ });
2000
+ }
2001
+ async function _() {
2002
+ await p((e) => !e.active || e.pending < 1 ? null : { aiWritingPending: e.pending - 1 });
2003
+ }
2004
+ async function y(e) {
2005
+ await m();
2006
+ try {
2007
+ let t = await e();
2008
+ try {
2009
+ await g();
2010
+ } catch {}
2011
+ return t;
2012
+ } catch (e) {
2013
+ throw await _().catch(() => {}), e;
2014
+ }
2015
+ }
2016
+ async function b(e) {
2017
+ (e.action === "translate-en" || e.action === "translate-ko") && D();
2018
+ let t = n.value, a = t === "ko", o = _r(e, t, r);
2019
+ if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
2020
+ return y(async () => {
2021
+ let t = s.moderation ?? {}, n = Xe(i, {
2022
+ backend: new Je(),
2023
+ useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
2024
+ }), c = Ze(n, {
2025
+ model: t.model ?? "gemini-3.5-flash-lite",
2026
+ systemInstruction: (a ? [
2027
+ "너는 게시판 글쓰기 편집 도우미다.",
2028
+ "사용자가 요청한 편집 결과만 출력하고 설명, 인사, 마크다운 코드 펜스를 붙이지 마라.",
2029
+ e.action === "title" || e.selection ? "출력은 일반 텍스트로 작성하라." : "입력의 HTML 구조와 링크, 이미지 태그를 보존하고 결과도 HTML 조각으로 출력하라."
2030
+ ] : [
2031
+ "You are an editing assistant for writing message board posts.",
2032
+ "Output only the requested edit result. Do not add explanations, greetings, or markdown code fences.",
2033
+ e.action === "title" || e.selection ? "Write the output as plain text." : "Preserve the HTML structure, links, and image tags of the input, and output the result as an HTML fragment as well."
2034
+ ]).join("\n"),
2035
+ generationConfig: {
2036
+ temperature: e.action === "continue" || e.action === "title" || e.action === "custom" ? .6 : .2,
2037
+ maxOutputTokens: 2048
2038
+ }
2039
+ }), l = e.content.trim(), u = (a ? [
2040
+ `작업: ${o}`,
2041
+ e.title ? `현재 제목: ${e.title}` : "",
2042
+ `본문:\n${l.slice(0, 12e3)}`
2043
+ ] : [
2044
+ `Task: ${o}`,
2045
+ e.title ? `Current title: ${e.title}` : "",
2046
+ `Body:\n${l.slice(0, 12e3)}`
2047
+ ]).filter(Boolean).join("\n\n"), d = vr((await c.generateContent(u)).response.text());
2048
+ if (!d) throw Error(r("writingAssistant.emptyResult"));
2049
+ return d;
2050
+ });
2051
+ }
2052
+ async function x(e) {
2053
+ D();
2054
+ let t = e.targets?.length ? e.targets : pt(), n = e.title?.trim() ?? "", i = e.summary?.trim() ?? "", a = e.content ?? "", o = {};
2055
+ n && (o.title = n), i && (o.summary = i);
2056
+ let s = !!ln(a);
2057
+ if (!Object.keys(o).length && !s) throw Error(r("writingAssistant.translateEmpty"));
2058
+ if (s && a.length > 12e3) throw Error(r("writingAssistant.translateTooLong", {
2059
+ limit: Rn,
2060
+ count: t.length
2061
+ }));
2062
+ return y(async () => {
2063
+ let d = e.source ?? await u([
2064
+ ln(a),
2065
+ n,
2066
+ i
2067
+ ].filter(Boolean).join("\n")), f = [...t, d], [p, m] = await Promise.all([Object.keys(o).length ? c([{
2068
+ id: "post",
2069
+ fields: o,
2070
+ context: "message board post title and short summary"
2071
+ }], {
2072
+ source: d,
2073
+ targets: t,
2074
+ purpose: "message board post title and short summary"
2075
+ }) : Promise.resolve({}), s ? l(a, {
2076
+ source: d,
2077
+ targets: t
2078
+ }) : Promise.resolve({})]), h = ft(d, o, p.post);
2079
+ if (Object.keys(o).length && !ht(h, Object.keys(o), f)) throw Error(r("writingAssistant.translateIncomplete"));
2080
+ let g = {
2081
+ i18n: h,
2082
+ i18nSource: {
2083
+ locale: d,
2084
+ title: n,
2085
+ summary: i
2086
+ }
2087
+ };
2088
+ if (s) {
2089
+ let e = {};
2090
+ for (let [t, n] of Object.entries(m)) {
2091
+ let r = F(t);
2092
+ r && typeof n == "string" && n.trim() && (e[r] = { content: n.trim() });
2093
+ }
2094
+ let t = ft(d, { content: a }, e);
2095
+ if (!ht(t, ["content"], f)) throw Error(r("writingAssistant.translateIncomplete"));
2096
+ g.contentI18n = t, g.contentI18nSource = {
2097
+ locale: d,
2098
+ hash: ct(a)
2099
+ };
2100
+ }
2101
+ return g;
2102
+ });
2103
+ }
2104
+ async function S(e) {
2105
+ D();
2106
+ let t = e.body.trim();
2107
+ if (!t) throw Error(r("writingAssistant.translateEmpty"));
2108
+ let n = e.targets?.length ? e.targets : pt();
2109
+ return y(async () => {
2110
+ let e = await u(t), i = await c([{
2111
+ id: "comment",
2112
+ fields: { body: t },
2113
+ context: "message board comment or reply, plain text"
2114
+ }], {
2115
+ source: e,
2116
+ targets: n,
2117
+ purpose: "message board comments and replies"
2118
+ }), a = ft(e, { body: t }, i.comment);
2119
+ if (!ht(a, ["body"], [...n, e])) throw Error(r("writingAssistant.translateIncomplete"));
2120
+ return {
2121
+ i18n: a,
2122
+ i18nSource: {
2123
+ locale: e,
2124
+ body: t
2125
+ }
2126
+ };
2127
+ });
2128
+ }
2129
+ return {
2130
+ assist: b,
2131
+ translatePost: x,
2132
+ translateComment: S
2133
+ };
2134
+ }
2135
+ //#endregion
1913
2136
  //#region src/utils/subscribeCommentRange.ts
1914
- function dr(e, t, n, r, i, a) {
1915
- return W(K(g(e, t), X("postId", "==", n), ...r.rootId ? [X("rootId", "==", r.rootId), X("isReply", "==", !0)] : [X("parentId", "==", null)], G("createdAt", "asc"), G(B(), "asc"), ...r.hasMore && r.cursor ? [be(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
2137
+ function xr(e, t, n, r, i, a) {
2138
+ return W(K(g(e, t), X("postId", "==", n), ...r.rootId ? [X("rootId", "==", r.rootId), X("isReply", "==", !0)] : [X("parentId", "==", null)], G("createdAt", "asc"), G(V(), "asc"), ...r.hasMore && r.cursor ? [ye(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
1916
2139
  ...e.data(),
1917
2140
  id: e.id
1918
2141
  }))), a);
1919
2142
  }
1920
2143
  //#endregion
1921
2144
  //#region src/composables/useMemiBoardComments.ts
1922
- var fr = 10, pr = 5, mr = 1e3;
1923
- function hr(e, { t, formatNumber: n }) {
2145
+ var Sr = 10, Cr = 5, wr = 1e3;
2146
+ function Tr(e, { t, formatNumber: n }) {
1924
2147
  let r = e.trim();
1925
2148
  if (!r) throw Error(t("comments.bodyRequired"));
1926
- if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(mr) }));
2149
+ if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(wr) }));
1927
2150
  return r;
1928
2151
  }
1929
- function gr(e, t, n = {}) {
1930
- let r = Z(), i = Me(), a = () => M(), { isBoardHidden: o } = qn(), s = j(), { t: c } = s, l = () => g(r, a()), u = P(() => L(e)), f = P(() => L(t)), p = () => !o(u.value), m = I([]), h = I(n.subscribe !== !1), _ = I([]), v = I(!1), b = I(n.subscribe !== !1), S = I(n.subscribe !== !1), C = null, w = 0, T = 0, E;
1931
- function D(e) {
2152
+ function Er(e, t, r = {}) {
2153
+ let i = Z(), a = Me(), o = () => P(), { isBoardHidden: s, getBoard: c } = Jn(), { translateComment: l } = br(), u, f = R(!1);
2154
+ async function p(e, t) {
2155
+ if (f.value = !1, !n()) return {};
2156
+ if (t?.i18nSource.body === e) return t;
2157
+ if (c(v.value)?.autoTranslatePosts !== !0) return {};
2158
+ if (u?.i18nSource.body !== e) try {
2159
+ u = await l({
2160
+ body: e,
2161
+ targets: pt(c(v.value))
2162
+ });
2163
+ } catch (e) {
2164
+ return import.meta.dev && console.warn("[memi-board] comment auto-translation skipped", e), f.value = !0, {};
2165
+ }
2166
+ return u;
2167
+ }
2168
+ let m = N(), { t: h } = m, _ = () => g(i, o()), v = I(() => z(e)), b = I(() => z(t)), S = () => !s(v.value), C = R([]), w = R(r.subscribe !== !1), T = R([]), E = R(!1), D = R(r.subscribe !== !1), ee = R(r.subscribe !== !1), O = null, k = 0, A = 0, j;
2169
+ function te(e) {
1932
2170
  return e.createdAt?.toMillis?.() ?? 0;
1933
2171
  }
1934
- let ee = P(() => {
2172
+ let ne = I(() => {
1935
2173
  let e = /* @__PURE__ */ new Map();
1936
- for (let t of [...m.value, ..._.value]) t.id && e.set(t.id, t);
1937
- return [...e.values()].sort((e, t) => D(e) - D(t) || String(e.id).localeCompare(String(t.id)));
2174
+ for (let t of [...C.value, ...T.value]) t.id && e.set(t.id, t);
2175
+ return [...e.values()].sort((e, t) => te(e) - te(t) || String(e.id).localeCompare(String(t.id)));
1938
2176
  });
1939
- R([f, u], async ([e]) => {
1940
- let t = ++w;
1941
- if (T++, v.value = !1, _.value = [], m.value = [], C = null, E?.(), E = void 0, b.value = n.subscribe !== !1, n.subscribe !== !1) {
1942
- h.value = !0, S.value = !0;
2177
+ B([b, v], async ([e]) => {
2178
+ let t = ++k;
2179
+ if (A++, E.value = !1, T.value = [], C.value = [], O = null, j?.(), j = void 0, D.value = r.subscribe !== !1, r.subscribe !== !1) {
2180
+ w.value = !0, ee.value = !0;
1943
2181
  try {
1944
- let n = H(K(l(), X("postId", "==", e), G("createdAt", "desc"), G(B(), "desc")));
2182
+ let n = H(K(_(), X("postId", "==", e), G("createdAt", "desc"), G(V(), "desc")));
1945
2183
  if (await Promise.all([n.then((e) => {
1946
- if (t !== w) return;
2184
+ if (t !== k) return;
1947
2185
  let n = e.docs.map((e) => ({
1948
2186
  id: e.id,
1949
2187
  ...e.data()
1950
2188
  }));
1951
- m.value = n.filter((e) => e.parentId === void 0);
1952
- }), te()]), t !== w) return;
2189
+ C.value = n.filter((e) => e.parentId === void 0);
2190
+ }), M()]), t !== k) return;
1953
2191
  } finally {
1954
- t === w && (h.value = !1, S.value = !1);
2192
+ t === k && (w.value = !1, ee.value = !1);
1955
2193
  }
1956
2194
  }
1957
2195
  }, { immediate: !0 });
1958
- function O() {
1959
- E?.();
1960
- let e = ++T;
1961
- E = dr(r, a(), f.value, {
1962
- cursor: C,
1963
- hasMore: b.value
2196
+ function re() {
2197
+ j?.();
2198
+ let e = ++A;
2199
+ j = xr(i, o(), b.value, {
2200
+ cursor: O,
2201
+ hasMore: D.value
1964
2202
  }, (t) => {
1965
- e === T && (_.value = t);
2203
+ e === A && (T.value = t);
1966
2204
  }, (e) => {
1967
2205
  console.error("[memi-board:comments] onSnapshot failed", e);
1968
2206
  });
1969
2207
  }
1970
- async function te() {
1971
- if (v.value || !b.value) return;
1972
- let e = w;
1973
- v.value = !0;
2208
+ async function M() {
2209
+ if (E.value || !D.value) return;
2210
+ let e = k;
2211
+ E.value = !0;
1974
2212
  try {
1975
2213
  let t = [
1976
- X("postId", "==", f.value),
2214
+ X("postId", "==", b.value),
1977
2215
  X("parentId", "==", null),
1978
2216
  G("createdAt", "asc"),
1979
- G(B(), "asc"),
1980
- ...C ? [J(C)] : [],
2217
+ G(V(), "asc"),
2218
+ ...O ? [J(O)] : [],
1981
2219
  U(11)
1982
- ], n = await H(K(l(), ...t));
1983
- if (e !== w) return;
1984
- let r = n.docs.slice(0, fr), i = r.map((e) => ({
2220
+ ], n = await H(K(_(), ...t));
2221
+ if (e !== k) return;
2222
+ let r = n.docs.slice(0, Sr), i = r.map((e) => ({
1985
2223
  id: e.id,
1986
2224
  ...e.data()
1987
- })), a = new Set(_.value.map((e) => e.id));
1988
- _.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size > fr, O();
2225
+ })), a = new Set(T.value.map((e) => e.id));
2226
+ T.value.push(...i.filter((e) => !a.has(e.id))), O = r.at(-1) ?? O, D.value = n.size > Sr, re();
1989
2227
  } finally {
1990
- e === w && (v.value = !1);
2228
+ e === k && (E.value = !1);
1991
2229
  }
1992
2230
  }
1993
- F(() => {
1994
- w++, T++, E?.();
2231
+ L(() => {
2232
+ k++, A++, j?.();
1995
2233
  });
1996
- function k() {
1997
- return z(l()).id;
2234
+ function ie() {
2235
+ return ve(_()).id;
1998
2236
  }
1999
- function ne(e) {
2000
- if (!e) return z(l());
2001
- if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(c("commentForm.uploadFailed"));
2002
- return d(r, a(), e);
2237
+ function ae(e) {
2238
+ if (!e) return ve(_());
2239
+ if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(h("commentForm.uploadFailed"));
2240
+ return d(i, o(), e);
2003
2241
  }
2004
- function re(e, t) {
2005
- let n = `${x(a(), f.value)}/comment-images/${e}/`;
2006
- return le(t).filter((e) => e.path.startsWith(n));
2242
+ function oe(e, t) {
2243
+ let n = `${x(o(), b.value)}/comment-images/${e}/`;
2244
+ return se(t).filter((e) => e.path.startsWith(n));
2007
2245
  }
2008
- function ie(e, t) {
2009
- let n = re(t, e.images);
2246
+ async function le(e, t) {
2247
+ let n = oe(t, e.images), r = Tr(e.body, m);
2010
2248
  return {
2011
- postId: f.value,
2012
- boardId: u.value,
2013
- listed: p(),
2014
- body: hr(e.body, s),
2249
+ postId: b.value,
2250
+ boardId: v.value,
2251
+ listed: S(),
2252
+ body: r,
2253
+ ...await p(r, e.translations),
2015
2254
  authorUid: e.authorUid,
2016
2255
  authorName: e.authorName,
2017
2256
  authorPhoto: e.authorPhoto,
@@ -2023,87 +2262,91 @@ function gr(e, t, n = {}) {
2023
2262
  ...n.length ? { images: n } : {}
2024
2263
  };
2025
2264
  }
2026
- async function ae(e) {
2265
+ async function ue(e) {
2027
2266
  if (!e.length) return;
2028
- let t = We(i);
2267
+ let t = We(a);
2029
2268
  await Promise.all(e.map((e) => He(qe(t, e)).catch(() => {})));
2030
2269
  }
2031
- async function A(e) {
2032
- let t = Oe(r), n = ne(e.id);
2270
+ async function de(e) {
2271
+ let t = Oe(i), n = ae(e.id);
2033
2272
  t.set(n, {
2034
- ...ie(e, n.id),
2273
+ ...await le(e, n.id),
2035
2274
  parentId: null,
2036
2275
  rootId: n.id,
2037
2276
  depth: 0,
2038
2277
  replyToUid: null,
2039
2278
  replyToName: null,
2040
2279
  isReply: !1
2041
- }), t.update(y(r, a(), f.value), { commentCount: we(1) }), await t.commit();
2280
+ }), t.update(y(i, o(), b.value), { commentCount: we(1) }), await t.commit();
2042
2281
  }
2043
- async function oe(e) {
2044
- if (!e.parent.id) throw Error(c("comments.replyTargetNotFound"));
2045
- let t = Oe(r), n = ne(e.id), i = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
2046
- if (!i) throw Error(c("comments.threadNotFound"));
2047
- let o = Math.min((e.parent.depth ?? 0) + 1, 2);
2282
+ async function F(e) {
2283
+ if (!e.parent.id) throw Error(h("comments.replyTargetNotFound"));
2284
+ let t = Oe(i), n = ae(e.id), r = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
2285
+ if (!r) throw Error(h("comments.threadNotFound"));
2286
+ let a = Math.min((e.parent.depth ?? 0) + 1, 2);
2048
2287
  t.set(n, {
2049
- ...ie(e, n.id),
2288
+ ...await le(e, n.id),
2050
2289
  parentId: e.parent.id,
2051
- rootId: i,
2052
- depth: o,
2290
+ rootId: r,
2291
+ depth: a,
2053
2292
  replyToUid: e.parent.authorUid,
2054
2293
  replyToName: e.parent.authorName,
2055
2294
  isReply: !0
2056
- }), t.update(d(r, a(), i), { replyCount: we(1) }), t.update(y(r, a(), f.value), { commentCount: we(1) }), await t.commit();
2295
+ }), t.update(d(i, o(), r), { replyCount: we(1) }), t.update(y(i, o(), b.value), { commentCount: we(1) }), await t.commit();
2057
2296
  }
2058
- async function se(e) {
2297
+ async function fe(e) {
2059
2298
  if (!e.id) return;
2060
- if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(c("comments.hasRepliesCannotDelete"));
2061
- let t = ue(e.images, `${x(a(), f.value)}/comment-images/${e.id}/`);
2062
- await on(r, a(), e.id);
2063
- let n = Oe(r);
2064
- n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount: we(-1) }), n.update(y(r, a(), f.value), { commentCount: we(-1) }), await n.commit(), await ae(t), _.value = _.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
2065
- }
2066
- async function ce(e, t, n) {
2067
- let i = {
2068
- body: hr(t, s),
2299
+ if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(h("comments.hasRepliesCannotDelete"));
2300
+ let t = ce(e.images, `${x(o(), b.value)}/comment-images/${e.id}/`);
2301
+ await sn(i, o(), e.id);
2302
+ let n = Oe(i);
2303
+ n.delete(d(i, o(), e.id)), e.parentId && e.rootId && n.update(d(i, o(), e.rootId), { replyCount: we(-1) }), n.update(y(i, o(), b.value), { commentCount: we(-1) }), await n.commit(), await ue(t), T.value = T.value.filter((t) => t.id !== e.id), C.value = C.value.filter((t) => t.id !== e.id);
2304
+ }
2305
+ async function pe(e, t, n, r) {
2306
+ let a = Tr(t, m), s = {
2307
+ body: a,
2069
2308
  updatedAt: q()
2070
2309
  };
2071
2310
  if (n !== void 0) {
2072
- let t = re(e, n);
2073
- i.images = t.length ? t : ye();
2311
+ let t = oe(e, n);
2312
+ s.images = t.length ? t : _e();
2074
2313
  }
2075
- await Y(d(r, a(), e), i);
2314
+ await Y(d(i, o(), e), {
2315
+ ...s,
2316
+ ...await p(a, r)
2317
+ });
2076
2318
  }
2077
- async function de(e, t, n) {
2078
- await Y(d(r, a(), e), {
2319
+ async function me(e, t, n) {
2320
+ await Y(d(i, o(), e), {
2079
2321
  isBlinded: t,
2080
2322
  moderatedAt: q(),
2081
2323
  moderatedBy: n
2082
2324
  });
2083
2325
  }
2084
2326
  return {
2085
- comments: ee,
2086
- commentsPending: P(() => S.value || h.value),
2087
- hasMore: b,
2088
- loadingMore: v,
2089
- loadMore: te,
2090
- newCommentId: k,
2091
- addComment: A,
2092
- addReply: oe,
2093
- updateComment: ce,
2094
- setCommentBlinded: de,
2095
- deleteComment: se
2327
+ comments: ne,
2328
+ commentsPending: I(() => ee.value || w.value),
2329
+ hasMore: D,
2330
+ loadingMore: E,
2331
+ loadMore: M,
2332
+ newCommentId: ie,
2333
+ translationSkipped: f,
2334
+ addComment: de,
2335
+ addReply: F,
2336
+ updateComment: pe,
2337
+ setCommentBlinded: me,
2338
+ deleteComment: fe
2096
2339
  };
2097
2340
  }
2098
- function _r(e, t, n) {
2099
- let r = Z(), i = () => M(), a = () => g(r, i()), o = t.trim(), s = I([]), c = I(!1), l = I(!1), u = I(!0), d = null, f, p = 0, m = 0;
2341
+ function Dr(e, t, n) {
2342
+ let r = Z(), i = () => P(), a = () => g(r, i()), o = t.trim(), s = R([]), c = R(!1), l = R(!1), u = R(!0), d = null, f, p = 0, m = 0;
2100
2343
  function h(e) {
2101
2344
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
2102
2345
  }
2103
2346
  function _() {
2104
2347
  f?.();
2105
2348
  let e = ++m;
2106
- f = dr(r, i(), o, {
2349
+ f = xr(r, i(), o, {
2107
2350
  rootId: n,
2108
2351
  cursor: d,
2109
2352
  hasMore: u.value
@@ -2123,16 +2366,16 @@ function _r(e, t, n) {
2123
2366
  X("rootId", "==", n),
2124
2367
  X("isReply", "==", !0),
2125
2368
  G("createdAt", "asc"),
2126
- G(B(), "asc"),
2369
+ G(V(), "asc"),
2127
2370
  ...d ? [J(d)] : [],
2128
2371
  U(6)
2129
2372
  ], r = await H(K(a(), ...t));
2130
2373
  if (e !== p) return;
2131
- let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, pr), f = c.map((e) => ({
2374
+ let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, Cr), f = c.map((e) => ({
2132
2375
  id: e.id,
2133
2376
  ...e.data()
2134
2377
  })).filter((e) => !i.has(e.id));
2135
- s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > pr, _();
2378
+ s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > Cr, _();
2136
2379
  } finally {
2137
2380
  e === p && (c.value = !1);
2138
2381
  }
@@ -2143,7 +2386,7 @@ function _r(e, t, n) {
2143
2386
  async function b() {
2144
2387
  y(), await v();
2145
2388
  }
2146
- return F(() => {
2389
+ return L(() => {
2147
2390
  p++, m++, f?.();
2148
2391
  }), {
2149
2392
  replies: s,
@@ -2156,12 +2399,12 @@ function _r(e, t, n) {
2156
2399
  }
2157
2400
  //#endregion
2158
2401
  //#region src/composables/useMemiBoardLikes.ts
2159
- function vr(e, t) {
2160
- let n = () => M(), r = Z(), i = Ae(), { t: a } = j(), o = I(!1), s = I(!1), c;
2402
+ function Or(e, t) {
2403
+ let n = () => P(), r = Z(), i = Ae(), { t: a } = N(), o = R(!1), s = R(!1), c;
2161
2404
  function l(e) {
2162
- return me(r, n(), t, e);
2405
+ return fe(r, n(), t, e);
2163
2406
  }
2164
- R(i, (e) => {
2407
+ B(i, (e) => {
2165
2408
  if (c?.(), c = void 0, !e) {
2166
2409
  o.value = !1;
2167
2410
  return;
@@ -2171,7 +2414,7 @@ function vr(e, t) {
2171
2414
  }, (e) => {
2172
2415
  console.error("[memi-board:likes] onSnapshot failed", e);
2173
2416
  });
2174
- }, { immediate: !0 }), F(() => c?.());
2417
+ }, { immediate: !0 }), L(() => c?.());
2175
2418
  async function u() {
2176
2419
  let c = i.value?.uid;
2177
2420
  if (!c) throw Error(a("likes.signInRequired"));
@@ -2195,10 +2438,10 @@ function vr(e, t) {
2195
2438
  toggleLike: u
2196
2439
  };
2197
2440
  }
2198
- var yr = Symbol.for("memi-board:comment-likes");
2199
- function br(e, t) {
2200
- let n = () => M(), r = Z(), i = Ae(), { t: a } = j(), o = P(() => L(e)), s = P(() => L(t)), c = I(/* @__PURE__ */ new Set()), l = I(null), u;
2201
- R([i, s], ([e, t]) => {
2441
+ var kr = Symbol.for("memi-board:comment-likes");
2442
+ function Ar(e, t) {
2443
+ let n = () => P(), r = Z(), i = Ae(), { t: a } = N(), o = I(() => z(e)), s = I(() => z(t)), c = R(/* @__PURE__ */ new Set()), l = R(null), u;
2444
+ B([i, s], ([e, t]) => {
2202
2445
  u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = W(K(b(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
2203
2446
  let t = /* @__PURE__ */ new Set();
2204
2447
  for (let n of e.docs) {
@@ -2209,7 +2452,7 @@ function br(e, t) {
2209
2452
  }, (e) => {
2210
2453
  console.error("[memi-board:comment-likes] onSnapshot failed", e);
2211
2454
  }));
2212
- }, { immediate: !0 }), F(() => u?.());
2455
+ }, { immediate: !0 }), L(() => u?.());
2213
2456
  function f(e) {
2214
2457
  return !!e && c.value.has(e);
2215
2458
  }
@@ -2223,7 +2466,7 @@ function br(e, t) {
2223
2466
  if (l.value) return f(e);
2224
2467
  l.value = e;
2225
2468
  try {
2226
- let i = me(r, n(), e, t), l = d(r, n(), e), u = await Ee(r, async (n) => {
2469
+ let i = fe(r, n(), e, t), l = d(r, n(), e), u = await Ee(r, async (n) => {
2227
2470
  let r = await n.get(i), c = await n.get(l);
2228
2471
  if (!c.exists()) throw Error(a("likes.commentNotFound"));
2229
2472
  let u = Number(c.data()?.likeCount ?? 0);
@@ -2249,7 +2492,7 @@ function br(e, t) {
2249
2492
  }
2250
2493
  //#endregion
2251
2494
  //#region src/composables/useMemiBoardReports.ts
2252
- var xr = 200, Sr = [
2495
+ var jr = 200, Mr = [
2253
2496
  {
2254
2497
  value: "spam",
2255
2498
  label: "스팸·광고"
@@ -2271,12 +2514,12 @@ var xr = 200, Sr = [
2271
2514
  label: "기타"
2272
2515
  }
2273
2516
  ];
2274
- function Cr(e, t) {
2275
- return Sr.some((t) => t.value === e) ? A(t, `reports.reason.${e}`) : e || A(t, "reports.fallbackLabel");
2517
+ function Nr(e, t) {
2518
+ return Mr.some((t) => t.value === e) ? M(t, `reports.reason.${e}`) : e || M(t, "reports.fallbackLabel");
2276
2519
  }
2277
- function wr(e, t) {
2278
- let n = () => M(), r = Z(), i = Ae(), { t: a } = j(), o = I(!1), s = I(!1), c;
2279
- R(i, (e) => {
2520
+ function Pr(e, t) {
2521
+ let n = () => P(), r = Z(), i = Ae(), { t: a } = N(), o = R(!1), s = R(!1), c;
2522
+ B(i, (e) => {
2280
2523
  if (c?.(), c = void 0, !e) {
2281
2524
  o.value = !1;
2282
2525
  return;
@@ -2286,13 +2529,13 @@ function wr(e, t) {
2286
2529
  }, (e) => {
2287
2530
  console.error("[memi-board:reports] onSnapshot failed", e);
2288
2531
  });
2289
- }, { immediate: !0 }), F(() => c?.());
2532
+ }, { immediate: !0 }), L(() => c?.());
2290
2533
  async function l(c) {
2291
2534
  let l = i.value?.uid;
2292
2535
  if (!l) throw Error(a("reports.signInRequired"));
2293
2536
  if (s.value) return;
2294
2537
  if (c.authorUid && c.authorUid === l) throw Error(a("reports.cannotReportOwn"));
2295
- let d = Sr.some((e) => e.value === c.reason) ? c.reason : null;
2538
+ let d = Mr.some((e) => e.value === c.reason) ? c.reason : null;
2296
2539
  if (!d) throw Error(a("reports.reasonRequired"));
2297
2540
  let f = (c.detail || "").trim();
2298
2541
  if (f.length > 200) throw Error(a("reports.detailTooLong", { max: 200 }));
@@ -2321,9 +2564,9 @@ function wr(e, t) {
2321
2564
  submitReport: l
2322
2565
  };
2323
2566
  }
2324
- var Tr = 20;
2325
- function Er(e = {}) {
2326
- let t = () => M(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Ut(), o = e.pageSize ?? Tr, s = P(() => L(e.boardId)?.trim() || ""), c = I([]), d = I(!0), f = I(!1), p = I(!1), m = I(""), h = null, g;
2567
+ var Fr = 20;
2568
+ function Ir(e = {}) {
2569
+ let t = () => P(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Wt(), o = e.pageSize ?? Fr, s = I(() => z(e.boardId)?.trim() || ""), c = R([]), d = R(!0), f = R(!1), p = R(!1), m = R(""), h = null, g;
2327
2570
  function _(e) {
2328
2571
  return {
2329
2572
  id: e.id,
@@ -2337,18 +2580,18 @@ function Er(e = {}) {
2337
2580
  }
2338
2581
  d.value = !0;
2339
2582
  let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")];
2340
- g = W(K(l(n, t()), ...e, G("createdAt", "desc"), G(B(), "desc"), U(o)), (e) => {
2583
+ g = W(K(l(n, t()), ...e, G("createdAt", "desc"), G(V(), "desc"), U(o)), (e) => {
2341
2584
  c.value = e.docs.map(_).filter((e) => a(e.boardId)), h = e.docs.at(-1) ?? null, p.value = e.docs.length >= o, d.value = !1;
2342
2585
  }, (e) => {
2343
2586
  console.error("[memi-board:report-queue] onSnapshot failed", e), m.value = e.message || String(e), d.value = !1;
2344
2587
  });
2345
2588
  }
2346
- R([i, s], v, { immediate: !0 }), F(() => g?.());
2589
+ B([i, s], v, { immediate: !0 }), L(() => g?.());
2347
2590
  async function y() {
2348
2591
  if (!f.value && p.value && h && i.value) {
2349
2592
  f.value = !0, m.value = "";
2350
2593
  try {
2351
- let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await H(K(l(n, t()), ...e, G("createdAt", "desc"), G(B(), "desc"), J(h), U(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
2594
+ let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await H(K(l(n, t()), ...e, G("createdAt", "desc"), G(V(), "desc"), J(h), U(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
2352
2595
  c.value.push(...i.map(_).filter((e) => !u.has(e.id) && a(e.boardId))), h = i.at(-1) ?? h, p.value = r.docs.length > o;
2353
2596
  } catch (e) {
2354
2597
  m.value = e.message || String(e);
@@ -2377,8 +2620,8 @@ function Er(e = {}) {
2377
2620
  }
2378
2621
  //#endregion
2379
2622
  //#region src/composables/useMemiBoardViews.ts
2380
- function Dr() {
2381
- let e = () => M(), t = Z();
2623
+ function Lr() {
2624
+ let e = () => P(), t = Z();
2382
2625
  function n(t, n) {
2383
2626
  let { postsCollection: r } = e();
2384
2627
  return `memi-board:viewed:${r}:${t}:${n}`;
@@ -2417,20 +2660,20 @@ function Dr() {
2417
2660
  }
2418
2661
  //#endregion
2419
2662
  //#region src/composables/useMemiBoardModeration.ts
2420
- var Or = {
2663
+ var Rr = {
2421
2664
  flagged: !1,
2422
2665
  category: "none",
2423
2666
  reason: "",
2424
2667
  via: "empty"
2425
2668
  };
2426
- function kr(e = {}) {
2427
- let t = j(), n = P(() => N(L(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Me(), a = (e) => A(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Ut(), l = Mn([...jn, ...r.localBlocklist ?? []]);
2669
+ function zr(e = {}) {
2670
+ let t = N(), n = I(() => F(z(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Me(), a = (e) => M(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Wt(), l = Nn([...Mn, ...r.localBlocklist ?? []]);
2428
2671
  function u(e) {
2429
2672
  let t = e.match(l);
2430
2673
  return t ? {
2431
2674
  flagged: !0,
2432
2675
  category: "abuse",
2433
- reason: An({
2676
+ reason: jn({
2434
2677
  via: "local",
2435
2678
  localHit: t[0] || "",
2436
2679
  category: "abuse"
@@ -2445,7 +2688,7 @@ function kr(e = {}) {
2445
2688
  });
2446
2689
  return Ze(a, {
2447
2690
  model: e,
2448
- systemInstruction: Tn(n.value),
2691
+ systemInstruction: En(n.value),
2449
2692
  generationConfig: {
2450
2693
  temperature: 0,
2451
2694
  maxOutputTokens: 256,
@@ -2474,7 +2717,7 @@ function kr(e = {}) {
2474
2717
  };
2475
2718
  }
2476
2719
  function p(e, t = "") {
2477
- let n = An({
2720
+ let n = jn({
2478
2721
  reason: e.reason,
2479
2722
  category: e.category,
2480
2723
  via: e.via
@@ -2502,26 +2745,26 @@ function kr(e = {}) {
2502
2745
  };
2503
2746
  let i = e.trim();
2504
2747
  if (!i) return {
2505
- ...Or,
2748
+ ...Rr,
2506
2749
  via: "empty"
2507
2750
  };
2508
2751
  if (t?.skipFilter) return {
2509
- ...Or,
2752
+ ...Rr,
2510
2753
  via: "admin-override"
2511
2754
  };
2512
2755
  let c = u(i);
2513
2756
  if (c) return m(c);
2514
2757
  if (r.enabled === !1) return {
2515
- ...Or,
2758
+ ...Rr,
2516
2759
  via: "disabled"
2517
2760
  };
2518
2761
  try {
2519
- let e = Pn((await d().generateContent(En(i, n.value))).response.text());
2762
+ let e = Fn((await d().generateContent(Dn(i, n.value))).response.text());
2520
2763
  if (!e) return f();
2521
2764
  let t = {
2522
2765
  flagged: e.flagged,
2523
2766
  category: e.category,
2524
- reason: An({
2767
+ reason: jn({
2525
2768
  reason: e.reason,
2526
2769
  category: e.category,
2527
2770
  via: "ai"
@@ -2541,22 +2784,22 @@ function kr(e = {}) {
2541
2784
  via: "restricted"
2542
2785
  };
2543
2786
  if (r.enabled === !1 || !r.moderateImages) return {
2544
- ...Or,
2787
+ ...Rr,
2545
2788
  via: "disabled"
2546
2789
  };
2547
2790
  try {
2548
2791
  let t = await new Promise((t, n) => {
2549
2792
  let r = new FileReader();
2550
2793
  r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
2551
- }), r = Pn((await d().generateContent([{ inlineData: {
2794
+ }), r = Fn((await d().generateContent([{ inlineData: {
2552
2795
  mimeType: e.type || "image/jpeg",
2553
2796
  data: t
2554
- } }, Dn(n.value)])).response.text());
2797
+ } }, On(n.value)])).response.text());
2555
2798
  if (!r) return f();
2556
2799
  let i = {
2557
2800
  flagged: r.flagged,
2558
2801
  category: r.category,
2559
- reason: An({
2802
+ reason: jn({
2560
2803
  reason: r.reason,
2561
2804
  category: r.category,
2562
2805
  via: "ai"
@@ -2575,7 +2818,7 @@ function kr(e = {}) {
2575
2818
  }
2576
2819
  //#endregion
2577
2820
  //#region src/composables/useMemiBoardUsers.ts
2578
- var Ar = [
2821
+ var Br = [
2579
2822
  "owner",
2580
2823
  "admin",
2581
2824
  "manager",
@@ -2583,13 +2826,13 @@ var Ar = [
2583
2826
  "user"
2584
2827
  ].map((e) => ({
2585
2828
  value: e,
2586
- label: A("ko", `common.role.${e}`),
2587
- description: A("ko", `boardUsers.roleDescription.${e}`)
2829
+ label: M("ko", `common.role.${e}`),
2830
+ description: M("ko", `boardUsers.roleDescription.${e}`)
2588
2831
  }));
2589
- function jr(e = {}) {
2590
- let t = () => M(), n = Z(), { t: r } = j(), i = P(() => L(e.enabled) ?? !0 ? c(n, t()) : null), a = ke(i, { ssrKey: p(t(), "users-management") });
2832
+ function Vr(e = {}) {
2833
+ let t = () => P(), n = Z(), { t: r } = N(), i = I(() => z(e.enabled) ?? !0 ? c(n, t()) : null), a = ke(i, { ssrKey: p(t(), "users-management") });
2591
2834
  async function o(e, i) {
2592
- if (!Ar.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
2835
+ if (!Br.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
2593
2836
  await Y(h(n, t(), e), {
2594
2837
  role: i,
2595
2838
  updatedAt: q()
@@ -2603,8 +2846,8 @@ function jr(e = {}) {
2603
2846
  }
2604
2847
  //#endregion
2605
2848
  //#region src/composables/useMemiBoardUserProfile.ts
2606
- function Mr(e) {
2607
- let t = Z(), n = () => M(), r = P(() => L(e)), i = P(() => {
2849
+ function Hr(e) {
2850
+ let t = Z(), n = () => P(), r = I(() => z(e)), i = I(() => {
2608
2851
  let e = r.value;
2609
2852
  return e ? h(t, n(), e) : null;
2610
2853
  }), { data: a, pending: o } = je(i);
@@ -2615,7 +2858,7 @@ function Mr(e) {
2615
2858
  }
2616
2859
  //#endregion
2617
2860
  //#region src/utils/section.ts
2618
- var Nr = [
2861
+ var Ur = [
2619
2862
  3,
2620
2863
  4,
2621
2864
  5,
@@ -2625,43 +2868,43 @@ var Nr = [
2625
2868
  9,
2626
2869
  12
2627
2870
  ];
2628
- function Pr(e) {
2871
+ function Wr(e) {
2629
2872
  return [
2630
2873
  {
2631
2874
  value: "list",
2632
- label: A(e, "sectionUtil.kind.list")
2875
+ label: M(e, "sectionUtil.kind.list")
2633
2876
  },
2634
2877
  {
2635
2878
  value: "post",
2636
- label: A(e, "sectionUtil.kind.post")
2879
+ label: M(e, "sectionUtil.kind.post")
2637
2880
  },
2638
2881
  {
2639
2882
  value: "comments",
2640
- label: A(e, "sectionUtil.kind.comments")
2883
+ label: M(e, "sectionUtil.kind.comments")
2641
2884
  }
2642
2885
  ];
2643
2886
  }
2644
- function Fr(e) {
2887
+ function Gr(e) {
2645
2888
  return [{
2646
2889
  value: "latest",
2647
- label: A(e, "sectionUtil.sort.latest")
2890
+ label: M(e, "sectionUtil.sort.latest")
2648
2891
  }, {
2649
2892
  value: "likes",
2650
- label: A(e, "sectionUtil.sort.likes")
2893
+ label: M(e, "sectionUtil.sort.likes")
2651
2894
  }];
2652
2895
  }
2653
- var Ir = Pr("ko"), Lr = Fr("ko");
2654
- function Rr(e) {
2896
+ var Kr = Wr("ko"), qr = Gr("ko");
2897
+ function Jr(e) {
2655
2898
  return e === "list" ? 5 : 4;
2656
2899
  }
2657
- function zr(e, t) {
2900
+ function Yr(e, t) {
2658
2901
  let n = Math.floor(Number(t));
2659
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Rr(e);
2902
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Jr(e);
2660
2903
  }
2661
- function Br(e) {
2904
+ function Xr(e) {
2662
2905
  return e === 12 ? null : 12 - e;
2663
2906
  }
2664
- function Vr(e) {
2907
+ function Zr(e) {
2665
2908
  return {
2666
2909
  3: "col-span-12 md:col-span-3",
2667
2910
  4: "col-span-12 md:col-span-4",
@@ -2673,36 +2916,36 @@ function Vr(e) {
2673
2916
  12: "col-span-12"
2674
2917
  }[e];
2675
2918
  }
2676
- function Hr(e) {
2919
+ function Qr(e) {
2677
2920
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2678
2921
  }
2679
- function Ur(e) {
2922
+ function $r(e) {
2680
2923
  let t = e.boardId || e.category || "free";
2681
2924
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2682
2925
  }
2683
- function Wr(e) {
2926
+ function ei(e) {
2684
2927
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2685
2928
  }
2686
- function Gr(e) {
2929
+ function ti(e) {
2687
2930
  if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
2688
2931
  }
2689
- function Kr(e) {
2932
+ function ni(e) {
2690
2933
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2691
2934
  }
2692
- function qr(e, t) {
2693
- return vt(e, t).trim() || yt(e, t).trim() || A(t, "sectionUtil.untitledPost");
2935
+ function ri(e, t) {
2936
+ return vt(e, t).trim() || yt(e, t).trim() || M(t, "sectionUtil.untitledPost");
2694
2937
  }
2695
- function Jr(e) {
2938
+ function ii(e) {
2696
2939
  return e === "image" || e === "video" ? "media" : "list";
2697
2940
  }
2698
- function Yr(e, t) {
2699
- return A(t, Jr(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2941
+ function ai(e, t) {
2942
+ return M(t, ii(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2700
2943
  }
2701
- function Xr(e, t) {
2944
+ function oi(e, t) {
2702
2945
  return e ? t(e)?.listView ?? "default" : "default";
2703
2946
  }
2704
- function Zr(e, t) {
2705
- let n = e.map((e) => Xr(e.boardId, t));
2947
+ function si(e, t) {
2948
+ let n = e.map((e) => oi(e.boardId, t));
2706
2949
  if (!n.length) return "default";
2707
2950
  if (n.every((e) => e === n[0])) return n[0];
2708
2951
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2710,27 +2953,27 @@ function Zr(e, t) {
2710
2953
  }
2711
2954
  //#endregion
2712
2955
  //#region src/composables/useMemiBoardSections.ts
2713
- var Qr = [];
2714
- function $r(e) {
2956
+ var ci = [];
2957
+ function li(e) {
2715
2958
  let t = Number(e.cols);
2716
- if (Nr.includes(t)) return t;
2959
+ if (Ur.includes(t)) return t;
2717
2960
  let n = e.width;
2718
2961
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2719
2962
  }
2720
- function ei(e) {
2963
+ function ui(e) {
2721
2964
  let t = e.height ?? e.size;
2722
2965
  return t === "sm" || t === "lg" ? t : "md";
2723
2966
  }
2724
- function ti(e) {
2967
+ function di(e) {
2725
2968
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2726
2969
  }
2727
- function ni(e) {
2970
+ function fi(e) {
2728
2971
  return e.sort === "likes" ? "likes" : "latest";
2729
2972
  }
2730
- function ri(e) {
2973
+ function pi(e) {
2731
2974
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2732
2975
  }
2733
- function ii(e, t) {
2976
+ function mi(e, t) {
2734
2977
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2735
2978
  for (let e of n) {
2736
2979
  let n = String(e || "").trim();
@@ -2738,29 +2981,29 @@ function ii(e, t) {
2738
2981
  }
2739
2982
  return r;
2740
2983
  }
2741
- function ai(e, t) {
2742
- return A(t, "sections.defaultTitle", { n: e + 1 });
2984
+ function hi(e, t) {
2985
+ return M(t, "sections.defaultTitle", { n: e + 1 });
2743
2986
  }
2744
- function oi(e, t, n) {
2745
- let r = ti(e), i = zr(r, e.count), a = Q(e.i18n), o = ut(e.i18nSource);
2987
+ function gi(e, t, n) {
2988
+ let r = di(e), i = Yr(r, e.count), a = Q(e.i18n), o = ut(e.i18nSource);
2746
2989
  return {
2747
2990
  id: String(e.id || `box-${t + 1}`),
2748
- title: String(e.title || ai(t, n)),
2991
+ title: String(e.title || hi(t, n)),
2749
2992
  showTitle: e.showTitle !== !1,
2750
2993
  kind: r,
2751
- boardId: ri(e),
2994
+ boardId: pi(e),
2752
2995
  count: i,
2753
- postIds: ii(e, i),
2754
- sort: ni(e),
2755
- cols: $r(e),
2756
- height: ei(e),
2996
+ postIds: mi(e, i),
2997
+ sort: fi(e),
2998
+ cols: li(e),
2999
+ height: ui(e),
2757
3000
  order: t,
2758
3001
  ...a ? { i18n: a } : {},
2759
3002
  ...o ? { i18nSource: o } : {}
2760
3003
  };
2761
3004
  }
2762
- function si() {
2763
- let { locale: e } = j(), { translateEntries: t } = Hn(), r = Z(), i = z(r, "settings", "homeLayout"), { data: a, pending: o } = je(i), s = P(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : Qr).map((t, n) => oi(t, n, e.value)));
3005
+ function _i() {
3006
+ let { locale: e } = N(), { translateEntries: t } = Un(), r = Z(), i = ve(r, "settings", "homeLayout"), { data: a, pending: o } = je(i), s = I(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : ci).map((t, n) => gi(t, n, e.value)));
2764
3007
  async function c(r) {
2765
3008
  if (!n()) return r;
2766
3009
  let i = e.value, a = r.filter((e) => {
@@ -2792,18 +3035,18 @@ function si() {
2792
3035
  });
2793
3036
  }
2794
3037
  function l(t, n) {
2795
- let r = t, i = ti(r), a = zr(i, t.count), o = t.title.trim() || ai(n, e.value);
3038
+ let r = t, i = di(r), a = Yr(i, t.count), o = t.title.trim() || hi(n, e.value);
2796
3039
  return {
2797
3040
  id: t.id,
2798
3041
  title: o,
2799
3042
  showTitle: t.showTitle !== !1,
2800
3043
  kind: i,
2801
- boardId: ri(r),
3044
+ boardId: pi(r),
2802
3045
  count: a,
2803
- postIds: ii(r, a),
2804
- sort: ni(r),
2805
- cols: $r(r),
2806
- height: ei(r),
3046
+ postIds: mi(r, a),
3047
+ sort: fi(r),
3048
+ cols: li(r),
3049
+ height: ui(r),
2807
3050
  order: n,
2808
3051
  ...t.i18n ? { i18n: t.i18n } : {},
2809
3052
  ...t.i18nSource?.title === o ? { i18nSource: t.i18nSource } : {}
@@ -2827,28 +3070,28 @@ function si() {
2827
3070
  pending: o,
2828
3071
  sections: s,
2829
3072
  blocks: s,
2830
- hasSavedLayout: P(() => Array.isArray(a.value?.blocks)),
3073
+ hasSavedLayout: I(() => Array.isArray(a.value?.blocks)),
2831
3074
  saveSections: u,
2832
3075
  saveBlocks: u
2833
3076
  };
2834
3077
  }
2835
3078
  //#endregion
2836
3079
  //#region src/composables/useMemiBoardMiniPosts.ts
2837
- function ci(e, t) {
2838
- let n = Z(), r = () => M(), i = L(e)?.trim() || "all", a = P(() => {
2839
- let i = Math.max(1, Math.floor(Number(L(t)) || 5)), a = L(e)?.trim() || "", s = [X("isPublished", "==", !0), X("listed", "==", !0)];
3080
+ function vi(e, t) {
3081
+ let n = Z(), r = () => P(), i = z(e)?.trim() || "all", a = I(() => {
3082
+ let i = Math.max(1, Math.floor(Number(z(t)) || 5)), a = z(e)?.trim() || "", s = [X("isPublished", "==", !0), X("listed", "==", !0)];
2840
3083
  return a && s.unshift(X("boardId", "==", a)), K(o(n, r()), ...s, G("createdAt", "desc"), U(i));
2841
3084
  }), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(r(), `mini-list/${i}`) });
2842
3085
  return {
2843
- posts: P(() => s.value ?? []),
3086
+ posts: I(() => s.value ?? []),
2844
3087
  pending: c,
2845
3088
  error: l
2846
3089
  };
2847
3090
  }
2848
- function li(e) {
2849
- let t = Z(), n = () => M(), r = P(() => [...new Set(L(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = P(() => r.value.length ? K(o(t, n()), X(B(), "in", r.value)) : null), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(n(), `mini-picked/${i}`) });
3091
+ function yi(e) {
3092
+ let t = Z(), n = () => P(), r = I(() => [...new Set(z(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = I(() => r.value.length ? K(o(t, n()), X(V(), "in", r.value)) : null), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(n(), `mini-picked/${i}`) });
2850
3093
  return {
2851
- posts: P(() => {
3094
+ posts: I(() => {
2852
3095
  let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
2853
3096
  for (let n of r.value) {
2854
3097
  let r = e.get(n);
@@ -2862,7 +3105,29 @@ function li(e) {
2862
3105
  }
2863
3106
  //#endregion
2864
3107
  //#region src/data/versionHistory.ts
2865
- var ui = [
3108
+ var bi = [
3109
+ {
3110
+ version: "0.52.3",
3111
+ date: "2026-10-30",
3112
+ highlights: [
3113
+ "AI 글쓰기 도우미를 하루 20회까지 쓸 수 있어요",
3114
+ "자동 번역이 실패하거나 한도를 넘어도 글과 댓글은 원문으로 저장돼요",
3115
+ "한도 안내가 10개 언어로 보여요"
3116
+ ],
3117
+ highlightsEn: [
3118
+ "The AI writing assistant can now be used up to 20 times a day",
3119
+ "Posts and comments are saved with the original text even when automatic translation fails or the limit is reached",
3120
+ "The daily limit message now appears in all 10 languages"
3121
+ ],
3122
+ note: "호스트 Firestore 규칙의 AI 사용 한도도 20으로 바꿔 배포해야 해요.",
3123
+ noteEn: "Update the AI usage limit in the host Firestore rules to 20 and deploy them."
3124
+ },
3125
+ {
3126
+ version: "0.52.2",
3127
+ date: "2026-10-30",
3128
+ highlights: ["댓글·답글 작성 및 수정창에서도 AI로 문장을 다듬고 번역할 수 있어요", "글 자동 번역을 켜면 댓글·답글도 번역해 보는 언어에 맞춰 보여줘요"],
3129
+ highlightsEn: ["Use AI writing assistance and translation when writing or editing comments and replies", "Automatic post translation now also translates comments and replies for each viewer’s language"]
3130
+ },
2866
3131
  {
2867
3132
  version: "0.52.1",
2868
3133
  date: "2026-10-30",
@@ -3397,208 +3662,17 @@ var ui = [
3397
3662
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다.",
3398
3663
  noteEn: "Real security requires registering the host's Firebase rules."
3399
3664
  }
3400
- ], di = 10, fi = 864e5, pi = 200, mi = {
3401
- proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
3402
- polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
3403
- continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
3404
- summarize: "핵심 정보는 보존하면서 원문의 약 절반 길이로 간결하게 줄여라.",
3405
- "translate-en": "자연스러운 영어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
3406
- "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
3407
- title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
3408
- };
3409
- function hi(e) {
3410
- return {
3411
- proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
3412
- polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
3413
- continue: "Continue writing one or two paragraphs that match the preceding context and tone, without exaggeration.",
3414
- summarize: "Shorten the text concisely to about half of its original length while preserving the key information.",
3415
- "translate-en": "Translate into natural English. Keep proper nouns and technical terms accurate.",
3416
- "translate-ko": "Translate into natural Korean. Keep proper nouns and technical terms accurate.",
3417
- title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
3418
- };
3419
- }
3420
- function gi(e, t, n) {
3421
- let r = t === "ko", i = Cn(t);
3422
- if (e.action !== "custom") {
3423
- if (r) return mi[e.action];
3424
- let t = hi(i)[e.action];
3425
- return e.action === "translate-en" || e.action === "translate-ko" || e.action === "title" ? t : `${t} Write the result in the same language as the original text; if unclear, write in ${i}.`;
3426
- }
3427
- let a = e.customInstruction?.trim().slice(0, pi);
3428
- if (!a) throw Error(n("writingAssistant.customRequired"));
3429
- return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
3430
- }
3431
- function _i(e) {
3432
- return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
3433
- }
3434
- function vi(e) {
3435
- return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
3436
- }
3437
- function yi(e = {}) {
3438
- let t = j(), n = P(() => N(L(e.locale)) ?? t.locale.value), r = (e, t) => A(n.value, e, t), i = Me(), a = Z(), o = Ae(), s = v(), { translateEntries: c, translateHtml: l, detectSourceLocale: u } = Hn();
3439
- function d(e, t) {
3440
- let n = vi(e.aiWritingWindowAt), r = n != null && t - n < fi, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
3441
- return {
3442
- active: r,
3443
- windowAt: n,
3444
- count: r ? i : 0,
3445
- pending: r ? a : 0
3446
- };
3447
- }
3448
- function f(e, t) {
3449
- let n = e + fi, i = Math.max(1, Math.ceil((n - t) / 36e5));
3450
- return Error(r("writingAssistant.dailyLimit", {
3451
- limit: 10,
3452
- hours: i
3453
- }));
3454
- }
3455
- async function p(e) {
3456
- let t = o.value?.uid;
3457
- if (!t) throw Error(r("writingAssistant.loginRequired"));
3458
- await Ee(a, async (n) => {
3459
- let i = h(a, M(), t), o = await n.get(i);
3460
- if (!o.exists()) throw Error(r("writingAssistant.userMissing"));
3461
- let s = o.data(), c = e(d(s, Date.now()), s);
3462
- c && n.update(i, {
3463
- ...c,
3464
- updatedAt: q()
3465
- });
3466
- });
3467
- }
3468
- async function m() {
3469
- await p((e) => {
3470
- if (e.active && e.windowAt != null && e.count + e.pending >= 10) throw f(e.windowAt, Date.now());
3471
- return e.active ? { aiWritingPending: e.pending + 1 } : {
3472
- aiWritingCount: 0,
3473
- aiWritingPending: 1,
3474
- aiWritingWindowAt: q()
3475
- };
3476
- });
3477
- }
3478
- async function g() {
3479
- await p((e) => !e.active || e.pending < 1 || e.count >= 10 ? null : {
3480
- aiWritingCount: e.count + 1,
3481
- aiWritingPending: e.pending - 1
3482
- });
3483
- }
3484
- async function _() {
3485
- await p((e) => !e.active || e.pending < 1 ? null : { aiWritingPending: e.pending - 1 });
3486
- }
3487
- async function y(e) {
3488
- await m();
3489
- try {
3490
- let t = await e();
3491
- try {
3492
- await g();
3493
- } catch {}
3494
- return t;
3495
- } catch (e) {
3496
- throw await _().catch(() => {}), e;
3497
- }
3498
- }
3499
- async function b(e) {
3500
- (e.action === "translate-en" || e.action === "translate-ko") && D();
3501
- let t = n.value, a = t === "ko", o = gi(e, t, r);
3502
- if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
3503
- return y(async () => {
3504
- let t = s.moderation ?? {}, n = Xe(i, {
3505
- backend: new Je(),
3506
- useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
3507
- }), c = Ze(n, {
3508
- model: t.model ?? "gemini-3.5-flash-lite",
3509
- systemInstruction: (a ? [
3510
- "너는 게시판 글쓰기 편집 도우미다.",
3511
- "사용자가 요청한 편집 결과만 출력하고 설명, 인사, 마크다운 코드 펜스를 붙이지 마라.",
3512
- e.action === "title" || e.selection ? "출력은 일반 텍스트로 작성하라." : "입력의 HTML 구조와 링크, 이미지 태그를 보존하고 결과도 HTML 조각으로 출력하라."
3513
- ] : [
3514
- "You are an editing assistant for writing message board posts.",
3515
- "Output only the requested edit result. Do not add explanations, greetings, or markdown code fences.",
3516
- e.action === "title" || e.selection ? "Write the output as plain text." : "Preserve the HTML structure, links, and image tags of the input, and output the result as an HTML fragment as well."
3517
- ]).join("\n"),
3518
- generationConfig: {
3519
- temperature: e.action === "continue" || e.action === "title" || e.action === "custom" ? .6 : .2,
3520
- maxOutputTokens: 2048
3521
- }
3522
- }), l = e.content.trim(), u = (a ? [
3523
- `작업: ${o}`,
3524
- e.title ? `현재 제목: ${e.title}` : "",
3525
- `본문:\n${l.slice(0, 12e3)}`
3526
- ] : [
3527
- `Task: ${o}`,
3528
- e.title ? `Current title: ${e.title}` : "",
3529
- `Body:\n${l.slice(0, 12e3)}`
3530
- ]).filter(Boolean).join("\n\n"), d = _i((await c.generateContent(u)).response.text());
3531
- if (!d) throw Error(r("writingAssistant.emptyResult"));
3532
- return d;
3533
- });
3534
- }
3535
- async function x(e) {
3536
- D();
3537
- let t = e.targets?.length ? e.targets : pt(), n = e.title?.trim() ?? "", i = e.summary?.trim() ?? "", a = e.content ?? "", o = {};
3538
- n && (o.title = n), i && (o.summary = i);
3539
- let s = !!cn(a);
3540
- if (!Object.keys(o).length && !s) throw Error(r("writingAssistant.translateEmpty"));
3541
- if (s && a.length > 12e3) throw Error(r("writingAssistant.translateTooLong", {
3542
- limit: Ln,
3543
- count: t.length
3544
- }));
3545
- return y(async () => {
3546
- let d = e.source ?? await u([
3547
- cn(a),
3548
- n,
3549
- i
3550
- ].filter(Boolean).join("\n")), f = [...t, d], [p, m] = await Promise.all([Object.keys(o).length ? c([{
3551
- id: "post",
3552
- fields: o,
3553
- context: "message board post title and short summary"
3554
- }], {
3555
- source: d,
3556
- targets: t,
3557
- purpose: "message board post title and short summary"
3558
- }) : Promise.resolve({}), s ? l(a, {
3559
- source: d,
3560
- targets: t
3561
- }) : Promise.resolve({})]), h = ft(d, o, p.post);
3562
- if (Object.keys(o).length && !ht(h, Object.keys(o), f)) throw Error(r("writingAssistant.translateIncomplete"));
3563
- let g = {
3564
- i18n: h,
3565
- i18nSource: {
3566
- locale: d,
3567
- title: n,
3568
- summary: i
3569
- }
3570
- };
3571
- if (s) {
3572
- let e = {};
3573
- for (let [t, n] of Object.entries(m)) {
3574
- let r = N(t);
3575
- r && typeof n == "string" && n.trim() && (e[r] = { content: n.trim() });
3576
- }
3577
- let t = ft(d, { content: a }, e);
3578
- if (!ht(t, ["content"], f)) throw Error(r("writingAssistant.translateIncomplete"));
3579
- g.contentI18n = t, g.contentI18nSource = {
3580
- locale: d,
3581
- hash: ct(a)
3582
- };
3583
- }
3584
- return g;
3585
- });
3586
- }
3587
- return {
3588
- assist: b,
3589
- translatePost: x
3590
- };
3591
- }
3665
+ ];
3592
3666
  //#endregion
3593
3667
  //#region src/utils/renderMarkdown.ts
3594
- function bi(e) {
3668
+ function xi(e) {
3595
3669
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3596
3670
  }
3597
- function xi(e) {
3671
+ function Si(e) {
3598
3672
  let t = e;
3599
3673
  return t = t.replace(/`([^`]+)`/g, "<code>$1</code>"), t = t.replace(/!\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g, "<img src=\"$2\" alt=\"$1\" class=\"max-w-full h-auto rounded-lg my-3\" loading=\"lazy\" />"), t = t.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, "<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary underline\">$1</a>"), t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"), t = t.replace(/\*([^*]+)\*/g, "<em>$1</em>"), t = t.replace(/~~([^~]+)~~/g, "<del>$1</del>"), t;
3600
3674
  }
3601
- function Si(e) {
3675
+ function Ci(e) {
3602
3676
  if (!e?.trim()) return "";
3603
3677
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3604
3678
  function c() {
@@ -3607,7 +3681,7 @@ function Si(e) {
3607
3681
  return;
3608
3682
  }
3609
3683
  let e = o;
3610
- n.push(`<${e}>${s.map((e) => `<li>${xi(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3684
+ n.push(`<${e}>${s.map((e) => `<li>${Si(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3611
3685
  }
3612
3686
  for (; r < t.length;) {
3613
3687
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3616,7 +3690,7 @@ function Si(e) {
3616
3690
  continue;
3617
3691
  }
3618
3692
  if (i) {
3619
- a.push(bi(e)), r++;
3693
+ a.push(xi(e)), r++;
3620
3694
  continue;
3621
3695
  }
3622
3696
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3627,24 +3701,24 @@ function Si(e) {
3627
3701
  if (u) {
3628
3702
  c();
3629
3703
  let e = u[1].length;
3630
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${xi(bi(u[2]))}</h${e}>`), r++;
3704
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${Si(xi(u[2]))}</h${e}>`), r++;
3631
3705
  continue;
3632
3706
  }
3633
3707
  if (e.startsWith("> ")) {
3634
3708
  c();
3635
3709
  let e = [];
3636
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(bi((t[r] ?? "").slice(2))), r++;
3637
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${xi(e.join("<br />"))}</blockquote>`);
3710
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(xi((t[r] ?? "").slice(2))), r++;
3711
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${Si(e.join("<br />"))}</blockquote>`);
3638
3712
  continue;
3639
3713
  }
3640
3714
  let d = e.match(/^[-*+]\s+(.+)$/);
3641
3715
  if (d) {
3642
- o && o !== "ul" && c(), o = "ul", s.push(bi(d[1])), r++;
3716
+ o && o !== "ul" && c(), o = "ul", s.push(xi(d[1])), r++;
3643
3717
  continue;
3644
3718
  }
3645
3719
  let f = e.match(/^\d+\.\s+(.+)$/);
3646
3720
  if (f) {
3647
- o && o !== "ol" && c(), o = "ol", s.push(bi(f[1])), r++;
3721
+ o && o !== "ol" && c(), o = "ol", s.push(xi(f[1])), r++;
3648
3722
  continue;
3649
3723
  }
3650
3724
  if (!e.trim()) {
@@ -3656,22 +3730,22 @@ function Si(e) {
3656
3730
  for (; r < t.length;) {
3657
3731
  let e = t[r] ?? "";
3658
3732
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3659
- p.push(bi(e)), r++;
3733
+ p.push(xi(e)), r++;
3660
3734
  }
3661
- n.push(`<p class="my-2 leading-relaxed">${xi(p.join("<br />"))}</p>`);
3735
+ n.push(`<p class="my-2 leading-relaxed">${Si(p.join("<br />"))}</p>`);
3662
3736
  }
3663
3737
  return c(), i && a.length && n.push(`<pre class="overflow-x-auto rounded-lg bg-muted p-3 text-sm"><code>${a.join("\n")}</code></pre>`), n.join("\n");
3664
3738
  }
3665
3739
  //#endregion
3666
3740
  //#region src/utils/orphanCleanup.ts
3667
- function Ci(e, t, n, r) {
3741
+ function wi(e, t, n, r) {
3668
3742
  return typeof t.postId != "string" || !t.postId || t.postId.includes("/") ? !1 : !n.has(t.postId) || e === "like" && t.target === "comment" && typeof t.commentId == "string" && !!t.commentId && !t.commentId.includes("/") && !r.has(t.commentId);
3669
3743
  }
3670
- function wi(e, t) {
3744
+ function Ti(e, t) {
3671
3745
  let n = Date.parse(e);
3672
3746
  return Number.isFinite(n) && t - n >= 864e5;
3673
3747
  }
3674
- function Ti(e, t) {
3748
+ function Ei(e, t) {
3675
3749
  if (!e.startsWith(`${t}/`)) return null;
3676
3750
  let n = e.slice(t.length + 1).split("/");
3677
3751
  if (n.some((e) => !e || e === "." || e === "..")) return null;
@@ -3681,18 +3755,18 @@ function Ti(e, t) {
3681
3755
  commentId: a
3682
3756
  } : null;
3683
3757
  }
3684
- function Ei(e, t, n) {
3758
+ function Di(e, t, n) {
3685
3759
  let r = `${t === "comment" ? n.commentsCollection : n.likesCollection}/`, i = e.startsWith(r) ? e.slice(r.length) : "";
3686
3760
  return !!i && !i.includes("/") && i !== "." && i !== "..";
3687
3761
  }
3688
3762
  //#endregion
3689
3763
  //#region src/composables/useMemiBoardCleanup.ts
3690
- function Di() {
3691
- let e = Z(), t = Me(), { isAdmin: n, rolePending: r } = Ut(), { t: i } = j(), a = I([]), o = I(!1), s = I(!1), c = I(""), l = I(""), u = [], d = "", f = /* @__PURE__ */ new Map();
3764
+ function Oi() {
3765
+ let e = Z(), t = Me(), { isAdmin: n, rolePending: r } = Wt(), { t: i } = N(), a = R([]), o = R(!1), s = R(!1), c = R(""), l = R(""), u = [], d = "", f = /* @__PURE__ */ new Map();
3692
3766
  function p() {
3693
3767
  let e = Le(t).currentUser?.uid;
3694
3768
  if (!e) throw Error(i("settings.noAccess"));
3695
- return JSON.stringify([e, M()]);
3769
+ return JSON.stringify([e, P()]);
3696
3770
  }
3697
3771
  function m(e) {
3698
3772
  if (h(), p() !== e) throw Error(i("settings.cleanup.contextChanged"));
@@ -3701,24 +3775,24 @@ function Di() {
3701
3775
  if (!n.value || r.value || !Le(t).currentUser) throw Error(i("settings.noAccess"));
3702
3776
  }
3703
3777
  async function g() {
3704
- let t = M(), [n, r, i] = await Promise.all([
3705
- Ce(_e(e, t.postsCollection)),
3706
- Ce(_e(e, t.commentsCollection)),
3707
- Ce(_e(e, t.likesCollection))
3778
+ let t = P(), [n, r, i] = await Promise.all([
3779
+ Ce(he(e, t.postsCollection)),
3780
+ Ce(he(e, t.commentsCollection)),
3781
+ Ce(he(e, t.likesCollection))
3708
3782
  ]), a = /* @__PURE__ */ new Set();
3709
3783
  function o(e) {
3710
3784
  if (typeof e == "string") {
3711
3785
  e.startsWith(`${t.postsCollection}/`) && a.add(e);
3712
- let n = Xt(e);
3786
+ let n = Zt(e);
3713
3787
  n && a.add(n);
3714
3788
  for (let t of e.matchAll(/https?:\/\/[^\s"'<>]+/g)) {
3715
- let e = Xt(t[0].replace(/&amp;/g, "&"));
3789
+ let e = Zt(t[0].replace(/&amp;/g, "&"));
3716
3790
  e && a.add(e);
3717
3791
  }
3718
3792
  } else Array.isArray(e) ? e.forEach(o) : e && typeof e == "object" && Object.getPrototypeOf(e) === Object.prototype && Object.values(e).forEach(o);
3719
3793
  }
3720
3794
  n.docs.forEach((e) => o(e.data())), r.docs.forEach((e) => o(e.data()));
3721
- for (let e of n.docs) (await Ce(_e(e.ref, "body"))).docs.forEach((e) => o(e.data()));
3795
+ for (let e of n.docs) (await Ce(he(e.ref, "body"))).docs.forEach((e) => o(e.data()));
3722
3796
  for (let e of [...a]) {
3723
3797
  let t = e.match(/^(.*\/images\/)([^/]+)\.[^/.]+$/);
3724
3798
  t && a.add(`${t[1]}thumbnails/${t[2]}.jpg`);
@@ -3742,7 +3816,7 @@ function Di() {
3742
3816
  let r = [];
3743
3817
  for (let [e, t] of [["comment", n.commentDocs], ["like", n.likes]]) for (let i of t) {
3744
3818
  let t = i.data();
3745
- Ci(e, t, n.posts, n.comments) && r.push({
3819
+ wi(e, t, n.posts, n.comments) && r.push({
3746
3820
  kind: e,
3747
3821
  path: i.ref.path,
3748
3822
  postId: t.postId,
@@ -3760,12 +3834,12 @@ function Di() {
3760
3834
  });
3761
3835
  o.push(...e.prefixes);
3762
3836
  for (let t of e.items) {
3763
- let e = Ti(t.fullPath, n.cfg.postsCollection);
3837
+ let e = Ei(t.fullPath, n.cfg.postsCollection);
3764
3838
  if (!e) continue;
3765
3839
  let { postId: i, commentId: a } = e;
3766
3840
  if (n.referenced.has(t.fullPath) || n.posts.has(i) && (!a || n.comments.has(a))) continue;
3767
3841
  let o = await Ue(t);
3768
- wi(o.updated, Date.now()) && (f.set(t.fullPath, o.generation), r.push({
3842
+ Ti(o.updated, Date.now()) && (f.set(t.fullPath, o.generation), r.push({
3769
3843
  kind: "file",
3770
3844
  path: t.fullPath,
3771
3845
  postId: i,
@@ -3798,23 +3872,23 @@ function Di() {
3798
3872
  try {
3799
3873
  let i = !1;
3800
3874
  if (n.kind === "file") {
3801
- if (!Ti(n.path, o.cfg.postsCollection)) throw Error("Invalid cleanup path");
3875
+ if (!Ei(n.path, o.cfg.postsCollection)) throw Error("Invalid cleanup path");
3802
3876
  let r = We(t), a = qe(r, n.path), [s, c, l] = await Promise.all([
3803
- Se(z(e, o.cfg.postsCollection, n.postId)),
3804
- n.commentId ? Se(z(e, o.cfg.commentsCollection, n.commentId)) : null,
3877
+ Se(ve(e, o.cfg.postsCollection, n.postId)),
3878
+ n.commentId ? Se(ve(e, o.cfg.commentsCollection, n.commentId)) : null,
3805
3879
  Ue(a)
3806
3880
  ]);
3807
- m(d), l.generation === f.get(n.path) && !o.referenced.has(n.path) && wi(l.updated, Date.now()) && (!s.exists() || n.commentId && !c?.exists()) && (await He(a), i = !0);
3881
+ m(d), l.generation === f.get(n.path) && !o.referenced.has(n.path) && Ti(l.updated, Date.now()) && (!s.exists() || n.commentId && !c?.exists()) && (await He(a), i = !0);
3808
3882
  } else {
3809
3883
  let t = n.kind;
3810
- if (!Ei(n.path, t, o.cfg)) throw Error("Invalid cleanup path");
3884
+ if (!Di(n.path, t, o.cfg)) throw Error("Invalid cleanup path");
3811
3885
  i = await Ee(e, async (r) => {
3812
- let i = await r.get(z(e, n.path));
3886
+ let i = await r.get(ve(e, n.path));
3813
3887
  if (!i.exists()) return !0;
3814
3888
  let a = i.data();
3815
3889
  if (typeof a.postId != "string" || !a.postId || a.postId.includes("/")) return !1;
3816
- let s = await r.get(z(e, o.cfg.postsCollection, a.postId)), c = n.kind === "like" && a.target === "comment" && typeof a.commentId == "string" && a.commentId && !a.commentId.includes("/") ? await r.get(z(e, o.cfg.commentsCollection, a.commentId)) : null;
3817
- return Ci(t, a, new Set(s.exists() ? [a.postId] : []), new Set(c?.exists() ? [a.commentId] : [])) ? (m(d), r.delete(i.ref), !0) : !1;
3890
+ let s = await r.get(ve(e, o.cfg.postsCollection, a.postId)), c = n.kind === "like" && a.target === "comment" && typeof a.commentId == "string" && a.commentId && !a.commentId.includes("/") ? await r.get(ve(e, o.cfg.commentsCollection, a.commentId)) : null;
3891
+ return wi(t, a, new Set(s.exists() ? [a.postId] : []), new Set(c?.exists() ? [a.commentId] : [])) ? (m(d), r.delete(i.ref), !0) : !1;
3818
3892
  });
3819
3893
  }
3820
3894
  i ? r++ : p++, a.value = a.value.filter((e) => e.path !== n.path);
@@ -3834,16 +3908,16 @@ function Di() {
3834
3908
  }
3835
3909
  }
3836
3910
  return {
3837
- items: ge(a),
3838
- busy: ge(o),
3839
- scanned: ge(s),
3840
- error: ge(c),
3841
- result: ge(l),
3911
+ items: me(a),
3912
+ busy: me(o),
3913
+ scanned: me(s),
3914
+ error: me(c),
3915
+ result: me(l),
3842
3916
  scan: _,
3843
3917
  remove: v
3844
3918
  };
3845
3919
  }
3846
3920
  //#endregion
3847
- export { cr as AUTHOR_MEMO_MAX_LENGTH, Nr as BOARD_SECTION_COLS, Ir as BOARD_SECTION_KINDS, Lr as BOARD_SECTION_SORTS, Ar as BOARD_USER_ROLES, mr as COMMENT_BODY_MAX_LENGTH, Ft as DEFAULT_BLOCK_BAN_DECAY_MS, Pt as DEFAULT_BLOCK_BAN_THRESHOLD, Un as DEFAULT_BOARDS, Wn as DEFAULT_CATEGORIES, i as DEFAULT_COMMENTS_COLLECTION, r as DEFAULT_LIKES_COLLECTION, ne as DEFAULT_MEMI_BOARD_LOCALE, m as DEFAULT_POSTS_COLLECTION, ie as DEFAULT_REPORTS_COLLECTION, e as DEFAULT_SETTINGS_COLLECTION, re as DEFAULT_USERS_COLLECTION, k as FALLBACK_MEMI_BOARD_LOCALE, Ln as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, xr as REPORT_DETAIL_MAX_LENGTH, Sr as REPORT_REASONS, di as WRITING_ASSISTANT_DAILY_LIMIT, $e as asHttpUrl, xt as boardCopyIsTranslated, at as boardListOgDescription, it as boardListOgTitle, rt as boardPostOgDescription, nt as boardPostOgTitle, Pr as boardSectionKinds, Fr as boardSectionSorts, p as boardSsrKey, pt as boardTranslationLocales, h as boardUserDoc, c as boardUsersCol, gn as buildPostPreview, kt as canManageBoardByRole, At as canWriteCommentByRole, zr as clampSectionCount, d as commentDoc, g as commentsCol, pe as configureMemiBoard, ht as coversLocales, Rr as defaultSectionCount, bt as displayPostContent, yt as displayPostSummary, vt as displayPostTitle, _t as displayStoredField, It as effectiveModerationBlockCount, Yt as extractEditorImageUrls, Et as fetchPublicListForSeo, Tt as fetchPublicPostForSeo, ee as formatDate, ae as formatFullDate, de as formatRelativeDate, zt as formatRestrictedUntilLabel, O as formatTimestampDetails, dn as hasBodyImage, un as hasBodyText, ct as hashText, qt as imeSafeSubmitClick, Kt as imeSafeSubmitPointerDown, Ot as isAssignedStaff, fn as isContentEmpty, Yn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, te as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, Rt as isModerationWriteRestricted, me as likeDoc, t as likeDocId, b as likesCol, se as loadMemiBoardLocale, gt as localizedText, yr as memiBoardCommentLikesKey, oe as memiBoardHostLocaleKey, fe as memiBoardLocaleInfo, ft as mergeSourceFields, Xr as miniBoardView, Gr as miniCommentLink, Wr as miniMoreLink, Kr as miniPostImage, Ur as miniPostLink, qr as miniPostTitle, Zr as miniPostsView, Jr as miniViewGroup, Yr as miniViewLabel, Lt as moderationWriteRestrictedUntilMs, ot as normalizeBasePath, oi as normalizeBoardSection, N as normalizeMemiBoardLocale, Br as pairSectionCols, cn as plainTextFromHtml, s as postBodyDoc, y as postDoc, Zt as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, ut as readI18nSource, Q as readLocalizedFields, Si as renderMarkdownToHtml, u as reportDoc, ce as reportDocId, Cr as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, St as resolvePublicSeoDb, Vr as sectionColClass, Hr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Xt as storagePathFromDownloadUrl, ln as titleFromBody, tt as toAbsoluteUrl, et as toBoardOgImageUrl, A as translate, M as useBoardPathConfig, Ut as useMemiBoardAuth, ur as useMemiBoardAuthorMemo, Hn as useMemiBoardAutoTranslate, Di as useMemiBoardCleanup, sr as useMemiBoardCommentFeed, br as useMemiBoardCommentLikes, gr as useMemiBoardComments, v as useMemiBoardConfig, j as useMemiBoardI18n, vr as useMemiBoardLikes, ci as useMemiBoardMiniLatest, li as useMemiBoardMiniPicked, kr as useMemiBoardModeration, tr as useMemiBoardPost, er as useMemiBoardPostList, Zn as useMemiBoardPosts, _r as useMemiBoardReplies, Er as useMemiBoardReportQueue, wr as useMemiBoardReports, si as useMemiBoardSections, qn as useMemiBoardSettings, ar as useMemiBoardUserComments, rr as useMemiBoardUserPosts, Mr as useMemiBoardUserProfile, jr as useMemiBoardUsers, Dr as useMemiBoardViews, yi as useMemiBoardWritingAssistant, ui as versionHistory, hn as videoListCoverUrl, mn as youtubeId };
3921
+ export { lr as AUTHOR_MEMO_MAX_LENGTH, Ur as BOARD_SECTION_COLS, Kr as BOARD_SECTION_KINDS, qr as BOARD_SECTION_SORTS, Br as BOARD_USER_ROLES, wr as COMMENT_BODY_MAX_LENGTH, It as DEFAULT_BLOCK_BAN_DECAY_MS, Ft as DEFAULT_BLOCK_BAN_THRESHOLD, Wn as DEFAULT_BOARDS, Gn as DEFAULT_CATEGORIES, i as DEFAULT_COMMENTS_COLLECTION, r as DEFAULT_LIKES_COLLECTION, j as DEFAULT_MEMI_BOARD_LOCALE, m as DEFAULT_POSTS_COLLECTION, ne as DEFAULT_REPORTS_COLLECTION, e as DEFAULT_SETTINGS_COLLECTION, te as DEFAULT_USERS_COLLECTION, A as FALLBACK_MEMI_BOARD_LOCALE, Rn as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, jr as REPORT_DETAIL_MAX_LENGTH, Mr as REPORT_REASONS, fr as WRITING_ASSISTANT_DAILY_LIMIT, $e as asHttpUrl, xt as boardCopyIsTranslated, at as boardListOgDescription, it as boardListOgTitle, rt as boardPostOgDescription, nt as boardPostOgTitle, Wr as boardSectionKinds, Gr as boardSectionSorts, p as boardSsrKey, pt as boardTranslationLocales, h as boardUserDoc, c as boardUsersCol, _n as buildPostPreview, At as canManageBoardByRole, jt as canWriteCommentByRole, Yr as clampSectionCount, d as commentDoc, g as commentsCol, de as configureMemiBoard, ht as coversLocales, Jr as defaultSectionCount, St as displayCommentBody, bt as displayPostContent, yt as displayPostSummary, vt as displayPostTitle, _t as displayStoredField, Lt as effectiveModerationBlockCount, Xt as extractEditorImageUrls, Dt as fetchPublicListForSeo, Et as fetchPublicPostForSeo, ee as formatDate, re as formatFullDate, le as formatRelativeDate, Bt as formatRestrictedUntilLabel, O as formatTimestampDetails, fn as hasBodyImage, dn as hasBodyText, ct as hashText, Jt as imeSafeSubmitClick, qt as imeSafeSubmitPointerDown, kt as isAssignedStaff, pn as isContentEmpty, Xn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, k as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, zt as isModerationWriteRestricted, fe as likeDoc, t as likeDocId, b as likesCol, ae as loadMemiBoardLocale, gt as localizedText, kr as memiBoardCommentLikesKey, ie as memiBoardHostLocaleKey, ue as memiBoardLocaleInfo, ft as mergeSourceFields, oi as miniBoardView, ti as miniCommentLink, ei as miniMoreLink, ni as miniPostImage, $r as miniPostLink, ri as miniPostTitle, si as miniPostsView, ii as miniViewGroup, ai as miniViewLabel, Rt as moderationWriteRestrictedUntilMs, ot as normalizeBasePath, gi as normalizeBoardSection, F as normalizeMemiBoardLocale, Xr as pairSectionCols, ln as plainTextFromHtml, s as postBodyDoc, y as postDoc, Qt as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, ut as readI18nSource, Q as readLocalizedFields, Ci as renderMarkdownToHtml, u as reportDoc, oe as reportDocId, Nr as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, Ct as resolvePublicSeoDb, Zr as sectionColClass, Qr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Zt as storagePathFromDownloadUrl, un as titleFromBody, tt as toAbsoluteUrl, et as toBoardOgImageUrl, M as translate, P as useBoardPathConfig, Wt as useMemiBoardAuth, dr as useMemiBoardAuthorMemo, Un as useMemiBoardAutoTranslate, Oi as useMemiBoardCleanup, cr as useMemiBoardCommentFeed, Ar as useMemiBoardCommentLikes, Er as useMemiBoardComments, v as useMemiBoardConfig, N as useMemiBoardI18n, Or as useMemiBoardLikes, vi as useMemiBoardMiniLatest, yi as useMemiBoardMiniPicked, zr as useMemiBoardModeration, nr as useMemiBoardPost, tr as useMemiBoardPostList, Qn as useMemiBoardPosts, Dr as useMemiBoardReplies, Ir as useMemiBoardReportQueue, Pr as useMemiBoardReports, _i as useMemiBoardSections, Jn as useMemiBoardSettings, or as useMemiBoardUserComments, ir as useMemiBoardUserPosts, Hr as useMemiBoardUserProfile, Vr as useMemiBoardUsers, Lr as useMemiBoardViews, br as useMemiBoardWritingAssistant, bi as versionHistory, gn as videoListCoverUrl, hn as youtubeId };
3848
3922
 
3849
3923
  //# sourceMappingURL=index.js.map