memi-board 0.48.1 → 0.50.0

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.
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
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 te, f as ne, g as O, h as re, j as ie, k as ae, l as oe, m as k, o as se, p as ce, q as le, r as ue, s as A, t as de, u as fe, v as pe, w as j, x as me, y as M, z as he } from "./commentImages-D1PEZIU9.js";
2
- import { computed as N, onScopeDispose as P, reactive as ge, ref as F, toValue as I, watch as L } from "vue";
3
- import { deleteDoc as _e, deleteField as ve, doc as ye, documentId as R, endAt as be, endBefore as xe, getDoc as z, getDocs as B, increment as V, limit as H, limitToLast as Se, onSnapshot as U, orderBy as W, query as G, runTransaction as Ce, serverTimestamp as K, setDoc as q, startAfter as J, updateDoc as Y, where as X, writeBatch as we } from "firebase/firestore";
4
- import { useCollection as Te, useCurrentUser as Ee, useDocument as De, useFirebaseApp as Oe, useFirebaseAuth as ke, useFirestore as Z } from "vuefire";
5
- import { GoogleAuthProvider as Ae, OAuthProvider as je, createUserWithEmailAndPassword as Me, signInWithEmailAndPassword as Ne, signInWithPopup as Pe, signOut as Fe, updateProfile as Ie } from "firebase/auth";
6
- import { deleteObject as Le, getStorage as Re, listAll as ze, ref as Be } from "firebase/storage";
7
- import { GoogleAIBackend as Ve, Schema as He, getAI as Ue, getGenerativeModel as We } from "firebase/ai";
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 te, f as ne, g as O, h as re, j as ie, k as ae, l as oe, m as k, o as se, p as ce, q as le, r as ue, s as A, t as de, u as fe, v as pe, w as j, x as me, y as M, z as he } from "./commentImages-DyqzlNcX.js";
2
+ import { computed as N, onScopeDispose as P, reactive as ge, readonly as _e, ref as F, toValue as I, watch as L } from "vue";
3
+ import { collection as ve, deleteDoc as ye, deleteField as be, doc as R, documentId as z, endAt as xe, endBefore as Se, getDoc as B, getDocFromServer as Ce, getDocs as V, getDocsFromServer as we, increment as H, 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
+ import { useCollection as ke, useCurrentUser as Ae, useDocument as je, useFirebaseApp as Me, useFirebaseAuth as Ne, useFirestore as Z } from "vuefire";
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
+ import { deleteObject as He, getMetadata as Ue, getStorage as We, list as Ge, listAll as Ke, ref as qe } from "firebase/storage";
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
- function Ge(e, t) {
9
+ function Qe(e, t) {
10
10
  return !e || e === "전체" || e === k(t, "seo.allCategory");
11
11
  }
12
- function Ke(e) {
12
+ function $e(e) {
13
13
  if (typeof e != "string") return null;
14
14
  let t = e.trim();
15
15
  return /^https?:\/\//i.test(t) ? t : null;
16
16
  }
17
- function qe(e) {
18
- let t = Ke(e);
17
+ function et(e) {
18
+ let t = $e(e);
19
19
  if (!t) return null;
20
20
  if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
21
21
  try {
@@ -32,49 +32,49 @@ function qe(e) {
32
32
  return t;
33
33
  }
34
34
  }
35
- function Je(e, t, n = "") {
35
+ function tt(e, t, n = "") {
36
36
  let r = t.replace(/\/$/, ""), i = e.trim();
37
- return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? Je(n, t) : r;
37
+ return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? tt(n, t) : r;
38
38
  }
39
- function Ye(e, t, n, r, i) {
39
+ function nt(e, t, n, r, i) {
40
40
  let a = r?.trim().slice(0, 40), o = e.trim() || a || k(i, "seo.photo"), s = t?.trim(), c = n.trim() || "Board";
41
41
  return s ? `${o} · ${s} | ${c}` : `${o} | ${c}`;
42
42
  }
43
- function Xe(e, t) {
43
+ function rt(e, t) {
44
44
  let n = [];
45
45
  return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(k(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
- function Ze(e, t, n) {
47
+ function it(e, t, n) {
48
48
  let r = t.trim() || "Board", i = e?.trim();
49
- return Ge(i, n) ? k(n, "seo.listTitleAll", { site: r }) : k(n, "seo.listTitle", {
49
+ return Qe(i, n) ? k(n, "seo.listTitleAll", { site: r }) : k(n, "seo.listTitle", {
50
50
  name: i,
51
51
  site: r
52
52
  });
53
53
  }
54
- function Qe(e, t) {
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 = Ge(n, t) ? k(t, "seo.listDescriptionAll") : k(t, "seo.listDescription", { name: n }), i = (e.recentTitles || []).filter(Boolean).slice(0, 3);
56
+ let n = e.categoryLabel?.trim(), r = Qe(n, t) ? k(t, "seo.listDescriptionAll") : k(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
- function $e(e) {
59
+ function ot(e) {
60
60
  let t = (e || "/board").trim() || "/board";
61
61
  return (t.startsWith("/") ? t : `/${t}`).replace(/\/$/, "") || "/board";
62
62
  }
63
63
  //#endregion
64
64
  //#region src/utils/localizedFields.ts
65
- var et = [
65
+ var st = [
66
66
  "label",
67
67
  "description",
68
68
  "title",
69
69
  "summary",
70
70
  "contentHash"
71
71
  ];
72
- function tt(e) {
72
+ function ct(e) {
73
73
  let t = 5381;
74
74
  for (let n = 0; n < e.length; n++) t = (t << 5) + t ^ e.charCodeAt(n);
75
75
  return (t >>> 0).toString(36);
76
76
  }
77
- function nt(e, t) {
77
+ function lt(e, t) {
78
78
  let n = e?.[t];
79
79
  return typeof n == "string" ? n.trim() : "";
80
80
  }
@@ -90,19 +90,19 @@ function Q(e) {
90
90
  }
91
91
  return Object.keys(t).length ? t : void 0;
92
92
  }
93
- function rt(e) {
93
+ function ut(e) {
94
94
  if (!e || typeof e != "object") return;
95
95
  let t = e, n = {}, r = M(t.locale);
96
96
  r && (n.locale = r);
97
- for (let e of et) typeof t[e] == "string" && (n[e] = t[e]);
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
- function it(e) {
100
+ function dt(e) {
101
101
  if (!e || typeof e != "object") return;
102
102
  let t = e, n = {}, r = M(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
- function at(e, t, n) {
105
+ function ft(e, t, n) {
106
106
  let r = {};
107
107
  if (n) for (let e of T) {
108
108
  let t = n[e.code];
@@ -121,58 +121,58 @@ function at(e, t, n) {
121
121
  ...i
122
122
  }), r;
123
123
  }
124
- function ot(e, t) {
125
- return !e || !t.length ? !1 : T.every((n) => t.every((t) => !!nt(e[n.code], t)));
124
+ function pt(e, t) {
125
+ return !e || !t.length ? !1 : T.every((n) => t.every((t) => !!lt(e[n.code], t)));
126
126
  }
127
- function st(e, t, r, i, a) {
127
+ function mt(e, t, r, i, a) {
128
128
  if (!n()) return i;
129
129
  let o = M(t), s = M(a);
130
130
  if (!o || s && o === s) return i;
131
- let c = nt(e?.[o], r);
131
+ let c = lt(e?.[o], r);
132
132
  if (c) return c;
133
133
  if (o !== "en" && s !== "en") {
134
- let t = nt(e?.en, r);
134
+ let t = lt(e?.en, r);
135
135
  if (t) return t;
136
136
  }
137
137
  return i;
138
138
  }
139
- function ct(e, t, n, r, i) {
140
- return r ? !t || t[n] !== r ? r : st(e, i, n, r, t.locale) : "";
139
+ function ht(e, t, n, r, i) {
140
+ return r ? !t || t[n] !== r ? r : mt(e, i, n, r, t.locale) : "";
141
141
  }
142
- function lt(e, t) {
143
- return ct(e.i18n, e.i18nSource, "title", e.title ?? "", t);
142
+ function gt(e, t) {
143
+ return ht(e.i18n, e.i18nSource, "title", e.title ?? "", t);
144
144
  }
145
- function ut(e, t) {
146
- return ct(e.i18n, e.i18nSource, "summary", e.summary ?? "", t);
145
+ function _t(e, t) {
146
+ return ht(e.i18n, e.i18nSource, "summary", e.summary ?? "", t);
147
147
  }
148
- function dt(e, t) {
148
+ function vt(e, t) {
149
149
  let n = e.content || "", r = e.contentI18nSource?.hash;
150
- return !n || !r || r !== tt(n) ? n : st(e.contentI18n, t, "content", n, e.contentI18nSource?.locale) || n;
150
+ return !n || !r || r !== ct(n) ? n : mt(e.contentI18n, t, "content", n, e.contentI18nSource?.locale) || n;
151
151
  }
152
- function ft(e) {
152
+ function yt(e) {
153
153
  let t = e.label.trim(), n = (e.description ?? "").trim();
154
- return !t || !e.i18nSource || e.i18nSource.label !== t || (e.i18nSource.description ?? "") !== n ? !1 : ot(e.i18n, n ? ["label", "description"] : ["label"]);
154
+ return !t || !e.i18nSource || e.i18nSource.label !== t || (e.i18nSource.description ?? "") !== n ? !1 : pt(e.i18n, n ? ["label", "description"] : ["label"]);
155
155
  }
156
156
  //#endregion
157
157
  //#region src/composables/fetchPublicSeo.ts
158
- function pt() {
158
+ function bt() {
159
159
  return {
160
160
  db: Z(),
161
161
  paths: j()
162
162
  };
163
163
  }
164
- async function mt(e, t, n) {
164
+ async function xt(e, t, n) {
165
165
  try {
166
- let r = await z(w(e.db, e.paths, t));
166
+ let r = await B(w(e.db, e.paths, t));
167
167
  if (!r.exists()) return {
168
168
  label: t,
169
169
  description: "",
170
170
  hidden: !1
171
171
  };
172
- let i = r.data() ?? {}, a = typeof i.label == "string" && i.label.trim() ? i.label.trim() : t, o = typeof i.description == "string" ? i.description.trim() : "", s = Q(i.i18n), c = rt(i.i18nSource);
172
+ let i = r.data() ?? {}, a = typeof i.label == "string" && i.label.trim() ? i.label.trim() : t, o = typeof i.description == "string" ? i.description.trim() : "", s = Q(i.i18n), c = ut(i.i18nSource);
173
173
  return {
174
- label: ct(s, c, "label", a, n) || a,
175
- description: ct(s, c, "description", o, n),
174
+ label: ht(s, c, "label", a, n) || a,
175
+ description: ht(s, c, "description", o, n),
176
176
  hidden: i.visibility === "hidden"
177
177
  };
178
178
  } catch {
@@ -183,7 +183,7 @@ async function mt(e, t, n) {
183
183
  };
184
184
  }
185
185
  }
186
- function ht(e) {
186
+ function St(e) {
187
187
  if (e && typeof e == "object" && "toDate" in e) try {
188
188
  let t = e.toDate();
189
189
  return t instanceof Date ? t.toISOString() : null;
@@ -192,17 +192,17 @@ function ht(e) {
192
192
  }
193
193
  return null;
194
194
  }
195
- async function gt(e, t, n, r) {
195
+ async function Ct(e, t, n, r) {
196
196
  let i = e.trim(), a = t.trim();
197
197
  if (!i || !a) return null;
198
- let s = n ?? pt();
198
+ let s = n ?? bt();
199
199
  s.paths = C(s.paths), r && await ce(r);
200
- let c = await mt(s, i, r);
200
+ let c = await xt(s, i, r);
201
201
  if (c.hidden) return null;
202
202
  try {
203
- let e = (await B(G(o(s.db, s.paths), X("boardId", "==", i), X("slug", "==", a), X("listed", "==", !0), X("isPublished", "==", !0), H(1)))).docs[0];
203
+ let e = (await V(K(o(s.db, s.paths), X("boardId", "==", i), X("slug", "==", a), X("listed", "==", !0), X("isPublished", "==", !0), U(1)))).docs[0];
204
204
  if (!e) return null;
205
- let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title : "", l = typeof t.summary == "string" ? t.summary : "", u = Q(t.i18n), d = rt(t.i18nSource), f = lt({
205
+ let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title : "", l = typeof t.summary == "string" ? t.summary : "", u = Q(t.i18n), d = ut(t.i18nSource), f = gt({
206
206
  title: n,
207
207
  i18n: u,
208
208
  i18nSource: d
@@ -213,24 +213,24 @@ async function gt(e, t, n, r) {
213
213
  category: i,
214
214
  categoryLabel: c.label,
215
215
  title: f,
216
- summary: ut({
216
+ summary: _t({
217
217
  summary: l,
218
218
  i18n: u,
219
219
  i18nSource: d
220
220
  }, r).trim().slice(0, 200),
221
- previewImage: qe(t.previewImage),
221
+ previewImage: et(t.previewImage),
222
222
  authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
223
- createdAt: ht(t.createdAt)
223
+ createdAt: St(t.createdAt)
224
224
  } : null;
225
225
  } catch (e) {
226
226
  return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
227
227
  }
228
228
  }
229
- async function _t(e, t, n) {
229
+ async function wt(e, t, n) {
230
230
  n && await ce(n);
231
- let r = e?.trim() || null, i = t ?? pt();
231
+ let r = e?.trim() || null, i = t ?? bt();
232
232
  if (i.paths = C(i.paths), r) {
233
- let e = await mt(i, r, n);
233
+ let e = await xt(i, r, n);
234
234
  if (e.hidden) return {
235
235
  kind: "category",
236
236
  category: r,
@@ -239,7 +239,7 @@ async function _t(e, t, n) {
239
239
  ogImage: null,
240
240
  recentTitles: []
241
241
  };
242
- let { recentTitles: t, ogImage: a } = await vt(i, r, n);
242
+ let { recentTitles: t, ogImage: a } = await Tt(i, r, n);
243
243
  return {
244
244
  kind: "category",
245
245
  category: r,
@@ -258,17 +258,17 @@ async function _t(e, t, n) {
258
258
  recentTitles: []
259
259
  };
260
260
  }
261
- async function vt(e, t, n) {
261
+ async function Tt(e, t, n) {
262
262
  let r = [], i = null;
263
263
  try {
264
- let a = await B(G(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), W("createdAt", "desc"), H(12)));
264
+ let a = await V(K(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), G("createdAt", "desc"), U(12)));
265
265
  for (let e of a.docs) {
266
- let t = e.data() ?? {}, a = lt({
266
+ let t = e.data() ?? {}, a = gt({
267
267
  title: typeof t.title == "string" ? t.title : "",
268
268
  i18n: Q(t.i18n),
269
- i18nSource: rt(t.i18nSource)
269
+ i18nSource: ut(t.i18nSource)
270
270
  }, n).trim();
271
- a && r.length < 5 && r.push(a), i ||= qe(t.previewImage);
271
+ a && r.length < 5 && r.push(a), i ||= et(t.previewImage);
272
272
  }
273
273
  } catch (e) {
274
274
  import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
@@ -280,31 +280,31 @@ async function vt(e, t, n) {
280
280
  }
281
281
  //#endregion
282
282
  //#region src/utils/boardAccess.ts
283
- function yt(e, t) {
283
+ function Et(e, t) {
284
284
  return !e || !t ? !1 : [...t.allowedStaffUids ?? [], ...t.allowedManagerUids ?? []].includes(e);
285
285
  }
286
- function bt(e, t, n, r) {
287
- return e ? t ? !0 : n ? yt(e, r) : !1 : !1;
286
+ function Dt(e, t, n, r) {
287
+ return e ? t ? !0 : n ? Et(e, r) : !1 : !1;
288
288
  }
289
- function xt(e, t, n, r) {
289
+ function Ot(e, t, n, r) {
290
290
  if (!e) return !1;
291
- if (r?.visibility === "hidden") return bt(e, t, n, r);
291
+ if (r?.visibility === "hidden") return Dt(e, t, n, r);
292
292
  let i = r?.commentWriteRole ?? "user";
293
- return i === "admin" ? t : i !== "staff" || bt(e, t, n, r);
293
+ return i === "admin" ? t : i !== "staff" || Dt(e, t, n, r);
294
294
  }
295
295
  //#endregion
296
296
  //#region src/composables/boardLookup.ts
297
- var St;
298
- function Ct(e) {
299
- St = e;
297
+ var kt;
298
+ function At(e) {
299
+ kt = e;
300
300
  }
301
- function wt(e) {
302
- return St?.(e);
301
+ function jt(e) {
302
+ return kt?.(e);
303
303
  }
304
304
  //#endregion
305
305
  //#region src/utils/moderation-strike.ts
306
- var Tt = 10, Et = 864e5;
307
- function Dt(e, t, n = Date.now(), r = Et) {
306
+ var Mt = 10, Nt = 864e5;
307
+ function Pt(e, t, n = Date.now(), r = Nt) {
308
308
  let i = Math.max(0, Math.floor(Number(e) || 0));
309
309
  if (i <= 0) return 0;
310
310
  let a = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -312,7 +312,7 @@ function Dt(e, t, n = Date.now(), r = Et) {
312
312
  let o = Math.floor((n - a) / r);
313
313
  return Math.max(0, i - o);
314
314
  }
315
- function Ot(e, t, n = Date.now(), r = 10, i = Et) {
315
+ function Ft(e, t, n = Date.now(), r = 10, i = Nt) {
316
316
  let a = Math.max(0, Math.floor(Number(e) || 0));
317
317
  if (a < r) return null;
318
318
  let o = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -320,10 +320,10 @@ function Ot(e, t, n = Date.now(), r = 10, i = Et) {
320
320
  let s = o + (a - (r - 1)) * i;
321
321
  return s > n ? s : null;
322
322
  }
323
- function kt(e, t, n = Date.now(), r = 10, i = Et) {
324
- return Ot(e, t, n, r, i) != null;
323
+ function It(e, t, n = Date.now(), r = 10, i = Nt) {
324
+ return Ft(e, t, n, r, i) != null;
325
325
  }
326
- function At(e, t = Date.now(), n) {
326
+ function Lt(e, t = Date.now(), n) {
327
327
  let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
328
328
  if (i <= 0) return k(n, "moderationStrike.liftSoon");
329
329
  if (i < 24) return k(n, "moderationStrike.liftInHours", { hours: i });
@@ -333,7 +333,7 @@ function At(e, t = Date.now(), n) {
333
333
  hours: o
334
334
  });
335
335
  }
336
- function jt(e) {
336
+ function Rt(e) {
337
337
  if (e == null) return null;
338
338
  if (typeof e == "number" && Number.isFinite(e)) return e;
339
339
  if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
@@ -345,26 +345,26 @@ function jt(e) {
345
345
  }
346
346
  //#endregion
347
347
  //#region src/composables/useMemiBoardAuth.ts
348
- var Mt = "__MEMI_BOARD_AUTH_STATE__";
349
- function Nt() {
348
+ var zt = "__MEMI_BOARD_AUTH_STATE__";
349
+ function Bt() {
350
350
  let e = globalThis;
351
- return e[Mt] || (e[Mt] = {
351
+ return e[zt] || (e[zt] = {
352
352
  role: F("user"),
353
353
  rolePending: F(!0),
354
354
  rawBlockCount: F(0),
355
355
  blockAtMs: F(null),
356
356
  watchStarted: !1
357
- }), e[Mt];
357
+ }), e[zt];
358
358
  }
359
- function Pt() {
360
- let e = v(), { t, locale: n } = A(), r = Nt(), i = Z(), o = ke(), s = Ee(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
359
+ function Vt() {
360
+ let e = v(), { t, locale: n } = A(), r = Bt(), i = Z(), o = Ne(), s = Ae(), { role: c, rolePending: l, rawBlockCount: u, blockAtMs: d } = r;
361
361
  function f() {
362
362
  let t = e.moderation?.blockBanThreshold;
363
363
  return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
364
364
  }
365
365
  function p() {
366
366
  let t = e.moderation?.blockBanDecayMs;
367
- return typeof t == "number" && t > 0 ? t : Et;
367
+ return typeof t == "number" && t > 0 ? t : Nt;
368
368
  }
369
369
  function m(e) {
370
370
  return h(i, j(), e);
@@ -380,25 +380,25 @@ function Pt() {
380
380
  u.value = 0, d.value = null;
381
381
  return;
382
382
  }
383
- u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = jt(e.moderationBlockAt);
383
+ u.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), d.value = Rt(e.moderationBlockAt);
384
384
  }
385
385
  async function _(e) {
386
- let t = m(e.uid), n = a(i, j(), e.uid), r = await z(t);
387
- if (!r.exists()) return await Promise.all([q(t, {
386
+ let t = m(e.uid), n = a(i, j(), e.uid), r = await B(t);
387
+ if (!r.exists()) return await Promise.all([De(t, {
388
388
  role: "user",
389
389
  displayName: e.displayName,
390
390
  photoURL: e.photoURL,
391
- joinedAt: K(),
392
- lastVisitAt: K(),
393
- updatedAt: K()
394
- }), q(n, { email: e.email })]), g({ moderationBlockCount: 0 }), "user";
391
+ joinedAt: q(),
392
+ lastVisitAt: q(),
393
+ updatedAt: q()
394
+ }), De(n, { email: e.email })]), g({ moderationBlockCount: 0 }), "user";
395
395
  let o = r.data();
396
396
  g(o);
397
397
  let s = typeof o.displayName == "string" && o.displayName.trim(), c = typeof o.photoURL == "string" && o.photoURL.trim(), l = {
398
- lastVisitAt: K(),
399
- updatedAt: K()
398
+ lastVisitAt: q(),
399
+ updatedAt: q()
400
400
  };
401
- return !s && e.displayName && (l.displayName = e.displayName), !c && e.photoURL && (l.photoURL = e.photoURL), await Promise.all([Y(t, l), q(n, { email: e.email }, { merge: !0 })]), [
401
+ return !s && e.displayName && (l.displayName = e.displayName), !c && e.photoURL && (l.photoURL = e.photoURL), await Promise.all([Y(t, l), De(n, { email: e.email }, { merge: !0 })]), [
402
402
  "owner",
403
403
  "admin",
404
404
  "manager",
@@ -412,7 +412,7 @@ function Pt() {
412
412
  g(void 0);
413
413
  return;
414
414
  }
415
- let t = await z(m(e));
415
+ let t = await B(m(e));
416
416
  g(t.exists() ? t.data() : void 0);
417
417
  }
418
418
  r.watchStarted || (r.watchStarted = !0, L(s, async (e) => {
@@ -428,14 +428,14 @@ function Pt() {
428
428
  l.value = !1;
429
429
  }
430
430
  }, { immediate: !0 }));
431
- let b = N(() => !!s.value), x = N(() => c.value === "owner"), S = N(() => c.value === "manager"), C = N(() => x.value || c.value === "admin"), w = N(() => S.value || c.value === "staff"), T = N(() => C.value || w.value), E = N(() => Dt(u.value, d.value, Date.now(), p())), D = N(() => {
431
+ let b = N(() => !!s.value), x = N(() => c.value === "owner"), S = N(() => c.value === "manager"), C = N(() => x.value || c.value === "admin"), w = N(() => S.value || c.value === "staff"), T = N(() => C.value || w.value), E = N(() => Pt(u.value, d.value, Date.now(), p())), D = N(() => {
432
432
  let e = f();
433
- return e <= 0 ? !1 : kt(u.value, d.value, Date.now(), e, p());
433
+ return e <= 0 ? !1 : It(u.value, d.value, Date.now(), e, p());
434
434
  }), ee = N(() => {
435
435
  let e = f();
436
436
  if (e <= 0) return null;
437
- let t = Ot(u.value, d.value, Date.now(), e, p());
438
- return t == null ? null : At(t, Date.now(), n.value);
437
+ let t = Ft(u.value, d.value, Date.now(), e, p());
438
+ return t == null ? null : Lt(t, Date.now(), n.value);
439
439
  }), te = N(() => {
440
440
  if (!D.value) return null;
441
441
  let e = ee.value;
@@ -448,26 +448,26 @@ function Pt() {
448
448
  restricted: !1,
449
449
  messageSuffix: ""
450
450
  };
451
- let i = m(e), a = await z(i);
451
+ let i = m(e), a = await B(i);
452
452
  a.exists() ? await Y(i, {
453
- moderationBlockCount: V(1),
454
- moderationBlockAt: K(),
455
- updatedAt: K()
456
- }) : await q(i, {
453
+ moderationBlockCount: H(1),
454
+ moderationBlockAt: q(),
455
+ updatedAt: q()
456
+ }) : await De(i, {
457
457
  role: "user",
458
458
  displayName: s.value?.displayName ?? null,
459
459
  moderationBlockCount: 1,
460
- moderationBlockAt: K(),
461
- updatedAt: K()
460
+ moderationBlockAt: q(),
461
+ updatedAt: q()
462
462
  });
463
463
  let o = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
464
464
  u.value = o, d.value = c;
465
- let l = Dt(o, c, c, p()), h = kt(o, c, c, r, p()), g = t("auth.strikeSuffix", {
465
+ let l = Pt(o, c, c, p()), h = It(o, c, c, r, p()), g = t("auth.strikeSuffix", {
466
466
  count: l,
467
467
  threshold: r
468
468
  });
469
469
  if (h) {
470
- let e = Ot(o, c, c, r, p()), i = e == null ? null : At(e, c, n.value);
470
+ let e = Ft(o, c, c, r, p()), i = e == null ? null : Lt(e, c, n.value);
471
471
  g = i ? t("auth.strikeRestrictedUntil", {
472
472
  count: l,
473
473
  until: i
@@ -484,27 +484,27 @@ function Pt() {
484
484
  return o;
485
485
  }
486
486
  async function re() {
487
- return Pe(O(), new Ae());
487
+ return ze(O(), new Pe());
488
488
  }
489
489
  async function ie() {
490
- let e = new je("apple.com");
491
- return e.addScope("email"), e.addScope("name"), Pe(O(), e);
490
+ let e = new Fe("apple.com");
491
+ return e.addScope("email"), e.addScope("name"), ze(O(), e);
492
492
  }
493
493
  async function ae(e, t) {
494
- return Ne(O(), e, t);
494
+ return Re(O(), e, t);
495
495
  }
496
496
  async function oe(e, t, n) {
497
- let r = await Me(O(), e, t);
498
- return n && await Ie(r.user, { displayName: n }), r;
497
+ let r = await Ie(O(), e, t);
498
+ return n && await Ve(r.user, { displayName: n }), r;
499
499
  }
500
500
  async function k() {
501
- await Fe(O());
501
+ await Be(O());
502
502
  }
503
503
  function se(e) {
504
- return bt(s.value?.uid, C.value, w.value, e ? wt(e) : void 0);
504
+ return Dt(s.value?.uid, C.value, w.value, e ? jt(e) : void 0);
505
505
  }
506
506
  function ce(e) {
507
- return s.value ? C.value ? !0 : S.value && !!e && (wt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
507
+ return s.value ? C.value ? !0 : S.value && !!e && (jt(e)?.allowedManagerUids ?? []).includes(s.value.uid) : !1;
508
508
  }
509
509
  function le(e) {
510
510
  return b.value && (s.value?.uid === e.authorUid || se(e.boardId));
@@ -546,34 +546,34 @@ function Pt() {
546
546
  }
547
547
  //#endregion
548
548
  //#region src/utils/imeSafeSubmit.ts
549
- var Ft = /* @__PURE__ */ new WeakMap();
550
- function It(e) {
549
+ var Ht = /* @__PURE__ */ new WeakMap();
550
+ function Ut(e) {
551
551
  let t = e.currentTarget;
552
552
  return t instanceof HTMLButtonElement ? t : t instanceof Element ? t.closest("button") : null;
553
553
  }
554
- function Lt(e) {
554
+ function Wt(e) {
555
555
  if (e.pointerType !== "mouse" || e.button !== 0) return;
556
- let t = It(e), n = t?.form;
556
+ let t = Ut(e), n = t?.form;
557
557
  if (!t || !n || t.disabled) return;
558
- Ft.set(t, Date.now() + 1e3);
558
+ Ht.set(t, Date.now() + 1e3);
559
559
  let r = document.activeElement;
560
560
  r instanceof HTMLElement && r.blur(), setTimeout(() => n.requestSubmit(t), 0);
561
561
  }
562
- function Rt(e) {
563
- let t = It(e);
562
+ function Gt(e) {
563
+ let t = Ut(e);
564
564
  if (!t) return;
565
- let n = Ft.get(t);
566
- n && Date.now() < n && (e.preventDefault(), Ft.delete(t));
565
+ let n = Ht.get(t);
566
+ n && Date.now() < n && (e.preventDefault(), Ht.delete(t));
567
567
  }
568
568
  //#endregion
569
569
  //#region src/utils/findReadablePost.ts
570
- async function zt(e, t, n, r, i, a) {
570
+ async function Kt(e, t, n, r, i, a) {
571
571
  let s = [X("boardId", "==", n), X("slug", "==", r)];
572
- return a ? (await B(G(o(e, t), ...s, H(1)))).docs[0] : (await Promise.all([B(G(o(e, t), ...s, X("listed", "==", !0), X("isPublished", "==", !0), H(1))), ...i ? [B(G(o(e, t), ...s, X("authorUid", "==", i), H(1)))] : []])).flatMap((e) => e.docs)[0];
572
+ return a ? (await V(K(o(e, t), ...s, U(1)))).docs[0] : (await Promise.all([V(K(o(e, t), ...s, X("listed", "==", !0), X("isPublished", "==", !0), U(1))), ...i ? [V(K(o(e, t), ...s, X("authorUid", "==", i), U(1)))] : []])).flatMap((e) => e.docs)[0];
573
573
  }
574
574
  //#endregion
575
575
  //#region src/utils/extractEditorImageUrls.ts
576
- function Bt(e) {
576
+ function qt(e) {
577
577
  if (!e) return [];
578
578
  let t = /* @__PURE__ */ new Set();
579
579
  for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&amp;", "&"));
@@ -582,7 +582,7 @@ function Bt(e) {
582
582
  }
583
583
  //#endregion
584
584
  //#region src/utils/storagePath.ts
585
- function Vt(e) {
585
+ function Jt(e) {
586
586
  try {
587
587
  let t = new URL(e), n = t.hostname.toLowerCase();
588
588
  if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
@@ -592,107 +592,107 @@ function Vt(e) {
592
592
  return null;
593
593
  }
594
594
  }
595
- function Ht(e) {
595
+ function Yt(e) {
596
596
  return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
597
597
  }
598
598
  //#endregion
599
599
  //#region src/utils/deletePostCascade.ts
600
- async function Ut(e) {
600
+ async function Xt(e) {
601
601
  try {
602
- let t = await ze(e);
603
- await Promise.all([...t.items.map((e) => Le(e).catch(() => {})), ...t.prefixes.map((e) => Ut(e))]);
602
+ let t = await Ke(e);
603
+ await Promise.all([...t.items.map((e) => He(e).catch(() => {})), ...t.prefixes.map((e) => Xt(e))]);
604
604
  } catch {}
605
605
  }
606
- async function Wt(e, t) {
607
- await Ut(Be(e, `${t}/images`)), await Ut(Be(e, `${t}/attachments`)), await Ut(Be(e, `${t}/comment-images`));
606
+ async function Zt(e, t) {
607
+ await Xt(qe(e, `${t}/images`)), await Xt(qe(e, `${t}/attachments`)), await Xt(qe(e, `${t}/comment-images`));
608
608
  }
609
- var Gt = 450;
610
- async function Kt(e, t, n) {
611
- return (await B(G(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => de(e.data().images, `${x(t, n)}/comment-images/`));
609
+ var Qt = 450;
610
+ async function $t(e, t, n) {
611
+ return (await V(K(g(e, t), X("postId", "==", n)))).docs.flatMap((e) => de(e.data().images, `${x(t, n)}/comment-images/`));
612
612
  }
613
- async function qt(e, t) {
613
+ async function en(e, t) {
614
614
  let n = [...new Set([...t].filter(Boolean))];
615
- await Promise.all(n.map((t) => Le(Be(e, t)).catch(() => {})));
615
+ await Promise.all(n.map((t) => He(qe(e, t)).catch(() => {})));
616
616
  }
617
- async function Jt(e, t) {
618
- let n = await B(t);
619
- for (let t = 0; t < n.docs.length; t += Gt) {
620
- let r = we(e);
621
- n.docs.slice(t, t + Gt).forEach((e) => r.delete(e.ref)), await r.commit();
617
+ async function tn(e, t) {
618
+ let n = await V(t);
619
+ for (let t = 0; t < n.docs.length; t += Qt) {
620
+ let r = Oe(e);
621
+ n.docs.slice(t, t + Qt).forEach((e) => r.delete(e.ref)), await r.commit();
622
622
  }
623
623
  }
624
- async function Yt(e, t, n) {
625
- await Jt(e, G(b(e, t), X("postId", "==", n)));
624
+ async function nn(e, t, n) {
625
+ await tn(e, K(b(e, t), X("postId", "==", n)));
626
626
  }
627
- async function Xt(e, t, n) {
628
- await Jt(e, G(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
627
+ async function rn(e, t, n) {
628
+ await tn(e, K(b(e, t), X("commentId", "==", n), X("target", "==", "comment")));
629
629
  }
630
- async function Zt(e, t, n, r) {
631
- let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([z(i), z(a)]);
630
+ async function an(e, t, n, r) {
631
+ let i = y(e, n, r), a = s(e, n, r), [o, c] = await Promise.all([B(i), B(a)]);
632
632
  if (!o.exists()) return;
633
- let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await Kt(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
633
+ let l = o.data(), u = c.exists() ? String(c.data().content ?? "") : "", d = await $t(e, n, r), f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
634
634
  for (let e of l.attachments ?? []) {
635
635
  if (!e?.path) continue;
636
636
  f.add(e.path);
637
- let t = Ht(e.path);
637
+ let t = Yt(e.path);
638
638
  t && t !== r && p.add(t);
639
639
  }
640
- for (let e of Bt(u)) {
641
- let t = Vt(e);
640
+ for (let e of qt(u)) {
641
+ let t = Jt(e);
642
642
  if (!t) continue;
643
643
  f.add(t);
644
644
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
645
645
  n !== t && n.includes("/images/thumbnails/") && f.add(n);
646
646
  let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
647
647
  i && !t.includes("/thumbnails/") && f.add(`${i[1]}thumbnails/${i[2]}.jpg`);
648
- let a = Ht(t);
648
+ let a = Yt(t);
649
649
  a && a !== r && p.add(a);
650
650
  }
651
- await Wt(t, x(n, r));
652
- for (let e of p) await Wt(t, x(n, e));
653
- await qt(t, d), await qt(t, f), await Jt(e, G(g(e, n), X("postId", "==", r))), await Yt(e, n, r), await _e(a), await _e(i);
651
+ await Zt(t, x(n, r));
652
+ for (let e of p) await Zt(t, x(n, e));
653
+ await en(t, d), await en(t, f), await tn(e, K(g(e, n), X("postId", "==", r))), await nn(e, n, r), await ye(a), await ye(i);
654
654
  }
655
655
  //#endregion
656
656
  //#region src/utils/postBody.ts
657
- function Qt(e) {
657
+ function on(e) {
658
658
  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();
659
659
  }
660
- function $t(e, t = 40) {
661
- let n = Qt(e);
660
+ function sn(e, t = 40) {
661
+ let n = on(e);
662
662
  if (!n) return "";
663
663
  if (n.length <= t) return n;
664
664
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
665
665
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
666
666
  }
667
- function en(e) {
668
- return Qt(e).length > 0;
667
+ function cn(e) {
668
+ return on(e).length > 0;
669
669
  }
670
- function tn(e, t) {
670
+ function ln(e, t) {
671
671
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
672
672
  }
673
- function nn(e) {
674
- return !en(e);
673
+ function un(e) {
674
+ return !cn(e);
675
675
  }
676
676
  //#endregion
677
677
  //#region src/utils/postPreview.ts
678
- function rn(e) {
678
+ function dn(e) {
679
679
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
680
680
  }
681
- function an(e) {
681
+ function fn(e) {
682
682
  if (e) try {
683
- let t = new URL(rn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
683
+ let t = new URL(dn(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
684
684
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
685
685
  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;
686
686
  } catch {
687
687
  return;
688
688
  }
689
689
  }
690
- function on(e) {
691
- let t = an(e.videoUrl);
690
+ function pn(e) {
691
+ let t = fn(e.videoUrl);
692
692
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
693
693
  }
694
- function sn(e, t = []) {
695
- 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 ? rn(r[1]) : a, s = o ? an(o) : void 0, c = n ? rn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
694
+ function mn(e, t = []) {
695
+ 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 ? dn(r[1]) : a, s = o ? fn(o) : void 0, c = n ? dn(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
696
696
  return {
697
697
  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),
698
698
  ...c ? { previewImage: c } : {},
@@ -701,44 +701,44 @@ function sn(e, t = []) {
701
701
  }
702
702
  //#endregion
703
703
  //#region src/utils/saveBoardVisibility.ts
704
- function cn(e) {
704
+ function hn(e) {
705
705
  return e === "hidden" ? "hidden" : "public";
706
706
  }
707
- async function ln(e, t, n, r) {
707
+ async function gn(e, t, n, r) {
708
708
  let i;
709
709
  for (;;) {
710
- let a = await B(G(t, X("boardId", "==", n), W("__name__"), ...i ? [J(i)] : [], H(400)));
710
+ let a = await V(K(t, X("boardId", "==", n), G("__name__"), ...i ? [J(i)] : [], U(400)));
711
711
  if (a.empty) break;
712
- let o = we(e), s = 0;
712
+ let o = Oe(e), s = 0;
713
713
  for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
714
714
  if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
715
715
  }
716
716
  }
717
- async function un(e, t, n, r) {
718
- await ln(e, o(e, t), n, r), await ln(e, g(e, t), n, r);
717
+ async function _n(e, t, n, r) {
718
+ await gn(e, o(e, t), n, r), await gn(e, g(e, t), n, r);
719
719
  }
720
- async function dn(e, t, n, r) {
721
- let i = w(e, t, n), a = cn(r.visibility);
722
- await Ce(e, async (e) => {
720
+ async function vn(e, t, n, r) {
721
+ let i = w(e, t, n), a = hn(r.visibility);
722
+ await Ee(e, async (e) => {
723
723
  let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
724
724
  if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
725
- return !t.exists() || !o && cn(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
725
+ return !t.exists() || !o && hn(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
726
726
  ...r,
727
- visibility: a === "public" ? "public" : cn(n?.visibility),
727
+ visibility: a === "public" ? "public" : hn(n?.visibility),
728
728
  visibilityTransition: a
729
729
  }, { merge: !0 }), !0);
730
- }) && (await un(e, t, n, a !== "hidden"), await Ce(e, async (e) => {
730
+ }) && (await _n(e, t, n, a !== "hidden"), await Ee(e, async (e) => {
731
731
  if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
732
732
  e.update(i, {
733
733
  visibility: a,
734
734
  visibilityTransition: "",
735
- updatedAt: K()
735
+ updatedAt: q()
736
736
  });
737
737
  }));
738
738
  }
739
739
  //#endregion
740
740
  //#region src/utils/moderation-prompt.ts
741
- var fn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", pn = {
741
+ var yn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", bn = {
742
742
  ko: "Korean",
743
743
  en: "English",
744
744
  ja: "Japanese",
@@ -750,12 +750,12 @@ var fn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
750
750
  ar: "Arabic",
751
751
  id: "Indonesian"
752
752
  };
753
- function mn(e) {
754
- let t = pn[e] ?? "English", n = pe(e).name;
753
+ function xn(e) {
754
+ let t = bn[e] ?? "English", n = pe(e).name;
755
755
  return t === n ? t : `${t} (${n})`;
756
756
  }
757
- function hn(e) {
758
- let t = mn(e);
757
+ function Sn(e) {
758
+ let t = xn(e);
759
759
  return `You are a strict moderator reviewing content for a message board.
760
760
  If the text below contains profanity, vulgar slang, sexual harassment, hate speech, threats, spam, or repetitive/flooding advertisements, you MUST reject it (flagged=true).
761
761
 
@@ -782,17 +782,17 @@ Output JSON only:
782
782
  Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
783
783
  - If flagged=false, category is "none" and reason is ""`;
784
784
  }
785
- function gn(e = "ko") {
786
- return e === "ko" ? fn : hn(e);
785
+ function Cn(e = "ko") {
786
+ return e === "ko" ? yn : Sn(e);
787
787
  }
788
- function _n(e, t = "ko") {
788
+ function wn(e, t = "ko") {
789
789
  let n = `"""${e.slice(0, 4e3)}"""`;
790
790
  return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
791
791
  }
792
- function vn(e = "ko") {
792
+ function Tn(e = "ko") {
793
793
  return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
794
794
  }
795
- var yn = {
795
+ var En = {
796
796
  abuse: "욕설·비속어·모욕",
797
797
  spam: "스팸·광고",
798
798
  adult: "선정적·성인 내용",
@@ -800,10 +800,10 @@ var yn = {
800
800
  other: "부적절한 내용",
801
801
  none: ""
802
802
  };
803
- function bn(e, t = "ko") {
804
- return !e || e === "none" ? "" : e in yn ? k(t, `moderationPrompt.categories.${e}`) : e;
803
+ function Dn(e, t = "ko") {
804
+ return !e || e === "none" ? "" : e in En ? k(t, `moderationPrompt.categories.${e}`) : e;
805
805
  }
806
- function xn(e) {
806
+ function On(e) {
807
807
  let t = e.locale ?? "ko";
808
808
  if (e.via === "local" && e.localHit) {
809
809
  let n = e.localHit.trim();
@@ -811,10 +811,10 @@ function xn(e) {
811
811
  }
812
812
  let n = (e.reason || "").trim();
813
813
  if (n) return n;
814
- let r = e.category ? bn(e.category, t) : "";
814
+ let r = e.category ? Dn(e.category, t) : "";
815
815
  return r ? k(t, "moderationPrompt.categoryBlocked", { category: r }) : k(t, "moderationPrompt.genericBlocked");
816
816
  }
817
- var Sn = [
817
+ var kn = [
818
818
  "씨발",
819
819
  "시발",
820
820
  "시이발",
@@ -840,10 +840,10 @@ var Sn = [
840
840
  "bitch",
841
841
  "asshole"
842
842
  ];
843
- function Cn(e) {
843
+ function An(e) {
844
844
  return new RegExp(e.join("|"), "i");
845
845
  }
846
- var wn = /* @__PURE__ */ new Set([
846
+ var jn = /* @__PURE__ */ new Set([
847
847
  "none",
848
848
  "abuse",
849
849
  "spam",
@@ -851,7 +851,7 @@ var wn = /* @__PURE__ */ new Set([
851
851
  "violence",
852
852
  "other"
853
853
  ]);
854
- function Tn(e) {
854
+ function Mn(e) {
855
855
  let t = e.trim();
856
856
  if (!t) return null;
857
857
  let n;
@@ -869,7 +869,7 @@ function Tn(e) {
869
869
  if (typeof n != "object" || !n) return null;
870
870
  let r = n;
871
871
  if (typeof r.flagged != "boolean") return null;
872
- let i = typeof r.category == "string" && wn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
872
+ let i = typeof r.category == "string" && jn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
873
873
  return {
874
874
  flagged: r.flagged,
875
875
  category: i,
@@ -878,11 +878,11 @@ function Tn(e) {
878
878
  }
879
879
  //#endregion
880
880
  //#region src/composables/useMemiBoardAutoTranslate.ts
881
- var En = 3e4, Dn = 45e3, On = 12e3, kn = 3;
882
- function An(e) {
881
+ var Nn = 3e4, Pn = 45e3, Fn = 12e3, In = 3;
882
+ function Ln(e) {
883
883
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
884
884
  }
885
- async function jn(e, t, n) {
885
+ async function Rn(e, t, n) {
886
886
  let r = Array(e.length), i = 0;
887
887
  async function a() {
888
888
  for (; i < e.length;) {
@@ -892,7 +892,7 @@ async function jn(e, t, n) {
892
892
  }
893
893
  return await Promise.all(Array.from({ length: Math.min(t, e.length) }, () => a())), r;
894
894
  }
895
- function Mn(e, t) {
895
+ function zn(e, t) {
896
896
  return new Promise((n, r) => {
897
897
  let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
898
898
  e.then((e) => {
@@ -902,14 +902,14 @@ function Mn(e, t) {
902
902
  });
903
903
  });
904
904
  }
905
- function Nn() {
906
- let e = Oe(), { moderation: t } = v();
905
+ function Bn() {
906
+ let e = Me(), { moderation: t } = v();
907
907
  function n(n, r) {
908
- let i = Ue(e, {
909
- backend: new Ve(),
908
+ let i = Xe(e, {
909
+ backend: new Je(),
910
910
  useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
911
- }), a = He.object({ properties: Object.fromEntries(r.map((e) => [e, He.string()])) });
912
- return We(i, {
911
+ }), a = Ye.object({ properties: Object.fromEntries(r.map((e) => [e, Ye.string()])) });
912
+ return Ze(i, {
913
913
  model: t.model ?? "gemini-3.5-flash-lite",
914
914
  systemInstruction: [
915
915
  "You translate short user-interface strings for a website.",
@@ -921,19 +921,19 @@ function Nn() {
921
921
  temperature: .2,
922
922
  maxOutputTokens: 2048,
923
923
  responseMimeType: "application/json",
924
- responseSchema: He.object({ properties: Object.fromEntries(n.map((e) => [e, a])) })
924
+ responseSchema: Ye.object({ properties: Object.fromEntries(n.map((e) => [e, a])) })
925
925
  }
926
926
  });
927
927
  }
928
928
  async function r(e, t, r, i) {
929
929
  let a = Object.keys(e.fields), o = [
930
930
  `Purpose: ${i}`,
931
- `Source language: ${mn(t)} (${t})`,
932
- `Translate into: ${r.map((e) => `${e} = ${mn(e)}`).join(", ")}`,
931
+ `Source language: ${xn(t)} (${t})`,
932
+ `Translate into: ${r.map((e) => `${e} = ${xn(e)}`).join(", ")}`,
933
933
  e.context ? `Context (do not translate): ${e.context}` : "",
934
934
  "Text (JSON):",
935
935
  JSON.stringify(e.fields)
936
- ].filter(Boolean).join("\n"), s = await Mn(n(r, a).generateContent(o), En), c = JSON.parse(s.response.text()), l = {};
936
+ ].filter(Boolean).join("\n"), s = await zn(n(r, a).generateContent(o), Nn), c = JSON.parse(s.response.text()), l = {};
937
937
  for (let e of r) {
938
938
  let t = c?.[e];
939
939
  if (!t) continue;
@@ -957,11 +957,11 @@ function Nn() {
957
957
  return Object.fromEntries(o.map((e, t) => [e.id, s[t]]));
958
958
  }
959
959
  function a() {
960
- let n = Ue(e, {
961
- backend: new Ve(),
960
+ let n = Xe(e, {
961
+ backend: new Je(),
962
962
  useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
963
963
  });
964
- return We(n, {
964
+ return Ze(n, {
965
965
  model: t.model ?? "gemini-3.5-flash-lite",
966
966
  systemInstruction: [
967
967
  "You translate an HTML fragment of a message-board post.",
@@ -979,14 +979,14 @@ function Nn() {
979
979
  D();
980
980
  let n = t.source ?? "ko", r = (t.targets ?? T.map((e) => e.code)).filter((e) => e !== n), i = e.trim();
981
981
  if (!i || !r.length) return {};
982
- if (i.length > 12e3) throw Error(`translation is limited to ${On} characters`);
983
- let o = await jn(r, kn, async (e) => {
982
+ if (i.length > 12e3) throw Error(`translation is limited to ${Fn} characters`);
983
+ let o = await Rn(r, In, async (e) => {
984
984
  let t = [
985
- `Source language: ${mn(n)} (${n})`,
986
- `Translate into: ${e} = ${mn(e)}`,
985
+ `Source language: ${xn(n)} (${n})`,
986
+ `Translate into: ${e} = ${xn(e)}`,
987
987
  "HTML:",
988
988
  i
989
- ].join("\n"), r = An((await Mn(a().generateContent(t), Dn)).response.text());
989
+ ].join("\n"), r = Ln((await zn(a().generateContent(t), Pn)).response.text());
990
990
  if (!r) throw Error(`empty translation for ${e}`);
991
991
  return [e, r];
992
992
  });
@@ -999,7 +999,7 @@ function Nn() {
999
999
  }
1000
1000
  //#endregion
1001
1001
  //#region src/composables/useMemiBoardSettings.ts
1002
- var Pn = [
1002
+ var Vn = [
1003
1003
  {
1004
1004
  id: "free",
1005
1005
  label: "자유",
@@ -1027,17 +1027,17 @@ var Pn = [
1027
1027
  visibility: "public",
1028
1028
  order: 2
1029
1029
  }
1030
- ], Fn = Pn;
1031
- function In(e) {
1030
+ ], Hn = Vn;
1031
+ function Un(e) {
1032
1032
  return e === "hidden" ? "hidden" : "public";
1033
1033
  }
1034
- function Ln(e, t, n) {
1034
+ function Wn(e, t, n) {
1035
1035
  return {
1036
1036
  id: e,
1037
1037
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
1038
1038
  order: typeof t.order == "number" ? t.order : n,
1039
1039
  description: (typeof t.description == "string" ? t.description : "") || "",
1040
- visibility: In(t.visibility),
1040
+ visibility: Un(t.visibility),
1041
1041
  visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
1042
1042
  listView: t.listView ?? "default",
1043
1043
  editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
@@ -1047,22 +1047,23 @@ function Ln(e, t, n) {
1047
1047
  allowedManagerUids: Array.isArray(t.allowedManagerUids) ? t.allowedManagerUids : [],
1048
1048
  locale: M(t.locale) ?? "",
1049
1049
  autoTranslate: t.autoTranslate === !0,
1050
+ autoTranslatePosts: t.autoTranslatePosts === !0,
1050
1051
  i18n: Q(t.i18n),
1051
- i18nSource: rt(t.i18nSource)
1052
+ i18nSource: ut(t.i18nSource)
1052
1053
  };
1053
1054
  }
1054
- function Rn() {
1055
- let e = () => j(), t = Z(), r = Oe(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Pt(), { t: l, locale: u } = A(), { translateEntries: d } = Nn(), f = N(() => G(S(t, e()), W("order", "asc"))), { data: m, pending: h } = Te(f, { ssrKey: p(e(), "boards") }), g = N(() => m.value.map((e, t) => Ln(e.id, e, t))), _ = g, v = N(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
1055
+ function Gn() {
1056
+ let e = () => j(), t = Z(), r = Me(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = Vt(), { t: l, locale: u } = A(), { translateEntries: d } = Bn(), f = N(() => K(S(t, e()), G("order", "asc"))), { data: m, pending: h } = ke(f, { ssrKey: p(e(), "boards") }), g = N(() => m.value.map((e, t) => Wn(e.id, e, t))), _ = g, v = N(() => g.value.filter((e) => e.visibility !== "hidden")), y = v;
1056
1057
  function b(e, t) {
1057
1058
  if (!e) return;
1058
1059
  let n = g.value.find((t) => t.id === e);
1059
- return n ? ct(n.i18n, n.i18nSource, "label", n.label, t ?? u.value) || n.label : e;
1060
+ return n ? ht(n.i18n, n.i18nSource, "label", n.label, t ?? u.value) || n.label : e;
1060
1061
  }
1061
1062
  let x = b;
1062
1063
  function C(e, t) {
1063
1064
  if (!e) return;
1064
1065
  let n = g.value.find((t) => t.id === e), r = n?.description?.trim();
1065
- if (n && r) return ct(n.i18n, n.i18nSource, "description", r, t ?? u.value) || void 0;
1066
+ if (n && r) return ht(n.i18n, n.i18nSource, "description", r, t ?? u.value) || void 0;
1066
1067
  }
1067
1068
  let T = C;
1068
1069
  function D(e) {
@@ -1076,16 +1077,16 @@ function Rn() {
1076
1077
  function O(e) {
1077
1078
  if (e) return g.value.find((t) => t.id === e);
1078
1079
  }
1079
- Ct((e) => O(e));
1080
+ At((e) => O(e));
1080
1081
  function re(e) {
1081
- return bt(c.value?.uid, a.value, s.value, e ? O(e) : void 0);
1082
+ return Dt(c.value?.uid, a.value, s.value, e ? O(e) : void 0);
1082
1083
  }
1083
1084
  let ie = N(() => g.value.filter((e) => re(e.id)));
1084
1085
  function ae(e, t) {
1085
1086
  return {
1086
1087
  label: e.label.trim(),
1087
1088
  description: (e.description ?? "").trim(),
1088
- visibility: In(e.visibility),
1089
+ visibility: Un(e.visibility),
1089
1090
  listView: e.listView ?? "default",
1090
1091
  editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
1091
1092
  writeRole: e.writeRole ?? "user",
@@ -1094,17 +1095,18 @@ function Rn() {
1094
1095
  allowedManagerUids: e.allowedManagerUids ?? [],
1095
1096
  locale: M(e.locale) ?? "",
1096
1097
  autoTranslate: e.autoTranslate === !0,
1098
+ autoTranslatePosts: e.autoTranslatePosts === !0,
1097
1099
  ...e.i18n ? { i18n: e.i18n } : {},
1098
1100
  ...e.i18nSource ? { i18nSource: e.i18nSource } : {},
1099
1101
  order: t,
1100
- updatedAt: K()
1102
+ updatedAt: q()
1101
1103
  };
1102
1104
  }
1103
1105
  async function oe(e) {
1104
1106
  if (!n()) return e;
1105
1107
  let t = e.label.trim(), r = (e.description ?? "").trim();
1106
1108
  if (!t) return e;
1107
- if (ft({
1109
+ if (yt({
1108
1110
  ...e,
1109
1111
  label: t,
1110
1112
  description: r
@@ -1115,7 +1117,7 @@ function Rn() {
1115
1117
  };
1116
1118
  let i = u.value, a = { label: t };
1117
1119
  r && (a.description = r);
1118
- let o = at(i, a, (await d([{
1120
+ let o = ft(i, a, (await d([{
1119
1121
  id: e.id || "board",
1120
1122
  fields: a,
1121
1123
  context: e.id
@@ -1123,7 +1125,7 @@ function Rn() {
1123
1125
  source: i,
1124
1126
  purpose: "board name and short description shown in navigation and page headers"
1125
1127
  }))[e.id || "board"]);
1126
- if (!ot(o, Object.keys(a))) throw Error(l("settings.autoTranslate.failed"));
1128
+ if (!pt(o, Object.keys(a))) throw Error(l("settings.autoTranslate.failed"));
1127
1129
  return {
1128
1130
  ...e,
1129
1131
  label: t,
@@ -1140,12 +1142,12 @@ function Rn() {
1140
1142
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
1141
1143
  let a = n.id.trim(), o = n.label.trim();
1142
1144
  if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
1143
- let s = In(n.visibility), c = ae({
1145
+ let s = Un(n.visibility), c = ae({
1144
1146
  ...n,
1145
1147
  label: o,
1146
1148
  visibility: s
1147
1149
  }, r);
1148
- await dn(t, e(), a, c);
1150
+ await vn(t, e(), a, c);
1149
1151
  }
1150
1152
  let se = k;
1151
1153
  async function ce(e) {
@@ -1159,11 +1161,11 @@ function Rn() {
1159
1161
  let a = n.trim();
1160
1162
  if (!a) throw Error(l("boardSettings.deleteIdRequired"));
1161
1163
  for (;;) {
1162
- let n = await B(G(o(t, e()), X("boardId", "==", a), H(25)));
1164
+ let n = await V(K(o(t, e()), X("boardId", "==", a), U(25)));
1163
1165
  if (n.empty) break;
1164
- for (let i of n.docs) await Zt(t, Re(r), e(), i.id);
1166
+ for (let i of n.docs) await an(t, We(r), e(), i.id);
1165
1167
  }
1166
- await _e(w(t, e(), n));
1168
+ await ye(w(t, e(), n));
1167
1169
  }
1168
1170
  let de = ue;
1169
1171
  async function fe() {
@@ -1215,34 +1217,34 @@ function Rn() {
1215
1217
  saveCategories: le,
1216
1218
  deleteBoard: ue,
1217
1219
  deleteCategory: de,
1218
- DEFAULT_BOARDS: Pn,
1219
- DEFAULT_CATEGORIES: Fn
1220
+ DEFAULT_BOARDS: Vn,
1221
+ DEFAULT_CATEGORIES: Hn
1220
1222
  };
1221
1223
  }
1222
1224
  //#endregion
1223
1225
  //#region src/composables/useMemiBoardPosts.ts
1224
- var zn = /* @__PURE__ */ new Map();
1225
- function Bn(e) {
1226
+ var Kn = /* @__PURE__ */ new Map();
1227
+ function qn(e) {
1226
1228
  if (!e || typeof e != "object") return !1;
1227
1229
  let t = "code" in e ? String(e.code) : "";
1228
1230
  return t === "permission-denied" || t === "firestore/permission-denied";
1229
1231
  }
1230
- function Vn(e, t) {
1232
+ function Jn(e, t) {
1231
1233
  let n = I(e)?.trim();
1232
1234
  if (!n) throw Error(k(t, "posts.boardIdRequired"));
1233
1235
  return n;
1234
1236
  }
1235
- function Hn(e) {
1236
- let t = Z(), n = Oe(), { isBoardHidden: r, getBoard: i } = Rn(), { user: a, canManageBoard: c } = Pt(), { t: l, locale: u } = A(), d = () => j(), f = () => Vn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
1237
+ function Yn(e) {
1238
+ let t = Z(), n = Me(), { isBoardHidden: r, getBoard: i } = Gn(), { user: a, canManageBoard: c } = Vt(), { t: l, locale: u } = A(), d = () => j(), f = () => Jn(e, u.value), p = () => o(t, d()), m = (e) => y(t, d(), e), h = (e) => s(t, d(), e);
1237
1239
  function g(e, t, n) {
1238
1240
  return {
1239
1241
  id: e,
1240
1242
  ...t,
1241
1243
  i18n: Q(t.i18n),
1242
- i18nSource: rt(t.i18nSource),
1244
+ i18nSource: ut(t.i18nSource),
1243
1245
  content: typeof n?.content == "string" ? n.content : "",
1244
1246
  contentI18n: Q(n?.i18n),
1245
- contentI18nSource: it(n?.i18nSource)
1247
+ contentI18nSource: dt(n?.i18nSource)
1246
1248
  };
1247
1249
  }
1248
1250
  function _() {
@@ -1252,7 +1254,7 @@ function Hn(e) {
1252
1254
  return i(f())?.editorType === "image";
1253
1255
  }
1254
1256
  function b(e) {
1255
- let t = v(), n = en(e.content), r = tn(e.content, e.attachments);
1257
+ let t = v(), n = cn(e.content), r = ln(e.content, e.attachments);
1256
1258
  if (t) {
1257
1259
  if (!r) throw Error(l("posts.photoRequired"));
1258
1260
  return;
@@ -1261,15 +1263,15 @@ function Hn(e) {
1261
1263
  if (!e.title?.trim()) throw Error(l("posts.titleRequired"));
1262
1264
  }
1263
1265
  function x() {
1264
- return ye(p()).id;
1266
+ return R(p()).id;
1265
1267
  }
1266
1268
  function S(e) {
1267
- return zt(t, d(), f(), e, a.value?.uid, c(f()));
1269
+ return Kt(t, d(), f(), e, a.value?.uid, c(f()));
1268
1270
  }
1269
1271
  async function C(e = {}) {
1270
1272
  let t = e.pageSize ?? 20, n = [X("boardId", "==", f()), X("isPublished", "==", !0)];
1271
- e.publicOnly !== !1 && n.push(X("listed", "==", !0)), n.push(W("createdAt", "desc")), e.cursor && n.push(J(e.cursor)), n.push(H(t + 1));
1272
- let r = await B(G(p(), ...n)), i = r.docs.slice(0, t);
1273
+ e.publicOnly !== !1 && n.push(X("listed", "==", !0)), n.push(G("createdAt", "desc")), e.cursor && n.push(J(e.cursor)), n.push(U(t + 1));
1274
+ let r = await V(K(p(), ...n)), i = r.docs.slice(0, t);
1273
1275
  return {
1274
1276
  posts: i.map((e) => ({
1275
1277
  id: e.id,
@@ -1280,7 +1282,7 @@ function Hn(e) {
1280
1282
  };
1281
1283
  }
1282
1284
  async function w(e) {
1283
- let [t, n] = await Promise.all([z(m(e)), z(h(e))]);
1285
+ let [t, n] = await Promise.all([B(m(e)), B(h(e))]);
1284
1286
  return t.exists() ? g(t.id, t.data(), n.exists() ? n.data() : void 0) : null;
1285
1287
  }
1286
1288
  async function T(e) {
@@ -1294,20 +1296,20 @@ function Hn(e) {
1294
1296
  post: e
1295
1297
  } : { status: "not-found" };
1296
1298
  } catch (e) {
1297
- return Bn(e) ? { status: "permission-denied" } : {
1299
+ return qn(e) ? { status: "permission-denied" } : {
1298
1300
  status: "error",
1299
1301
  message: e instanceof Error ? e.message : String(e)
1300
1302
  };
1301
1303
  }
1302
1304
  } catch (e) {
1303
- return Bn(e) ? { status: "permission-denied" } : {
1305
+ return qn(e) ? { status: "permission-denied" } : {
1304
1306
  status: "error",
1305
1307
  message: e instanceof Error ? e.message : String(e)
1306
1308
  };
1307
1309
  }
1308
1310
  }
1309
1311
  async function ee(e) {
1310
- let t = e.trim(), n = zn.get(`${f()}/${t}`);
1312
+ let t = e.trim(), n = Kn.get(`${f()}/${t}`);
1311
1313
  if (n) return {
1312
1314
  status: "ok",
1313
1315
  id: n
@@ -1319,7 +1321,7 @@ function Hn(e) {
1319
1321
  id: e.id
1320
1322
  } : { status: r(f()) ? "permission-denied" : "not-found" };
1321
1323
  } catch (e) {
1322
- return Bn(e) ? { status: "permission-denied" } : {
1324
+ return qn(e) ? { status: "permission-denied" } : {
1323
1325
  status: "error",
1324
1326
  message: e instanceof Error ? e.message : String(e)
1325
1327
  };
@@ -1338,8 +1340,8 @@ function Hn(e) {
1338
1340
  X("boardId", "==", f()),
1339
1341
  X("isPublished", "==", !0),
1340
1342
  ...e.listed === !1 ? [] : [X("listed", "==", !0)],
1341
- W("createdAt", "desc")
1342
- ], [n, r] = await Promise.all([B(G(p(), ...t, J(e.createdAt), H(1))), B(G(p(), ...t, xe(e.createdAt), Se(1)))]), i = (e) => {
1343
+ G("createdAt", "desc")
1344
+ ], [n, r] = await Promise.all([V(K(p(), ...t, J(e.createdAt), U(1))), V(K(p(), ...t, Se(e.createdAt), Te(1)))]), i = (e) => {
1343
1345
  let t = e.docs[0];
1344
1346
  return t ? {
1345
1347
  id: t.id,
@@ -1360,8 +1362,8 @@ function Hn(e) {
1360
1362
  async function O(e) {
1361
1363
  b(e);
1362
1364
  let n = e.postId || x(), r = v(), i, a;
1363
- r ? (i = n, a = $t(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await te(a));
1364
- let o = sn(e.content, e.attachments), s = _(), c = we(t);
1365
+ r ? (i = n, a = sn(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await te(a));
1366
+ let o = mn(e.content, e.attachments), s = _(), c = Oe(t);
1365
1367
  return c.set(m(n), {
1366
1368
  boardId: f(),
1367
1369
  slug: i,
@@ -1379,8 +1381,8 @@ function Hn(e) {
1379
1381
  authorPhoto: e.authorPhoto,
1380
1382
  moderationStatus: "approved",
1381
1383
  moderationModel: e.moderationModel ?? null,
1382
- createdAt: K(),
1383
- updatedAt: K(),
1384
+ createdAt: q(),
1385
+ updatedAt: q(),
1384
1386
  ...e.translations ? {
1385
1387
  i18n: e.translations.i18n,
1386
1388
  i18nSource: e.translations.i18nSource
@@ -1391,32 +1393,32 @@ function Hn(e) {
1391
1393
  i18n: e.translations.contentI18n,
1392
1394
  i18nSource: e.translations.contentI18nSource
1393
1395
  } : {}
1394
- }), await c.commit(), zn.set(`${f()}/${i}`, n), i;
1396
+ }), await c.commit(), Kn.set(`${f()}/${i}`, n), i;
1395
1397
  }
1396
1398
  async function re(e, t) {
1397
1399
  b(t);
1398
- let n = v() ? $t(t.content) : t.title.trim(), r = sn(t.content, t.attachments), i = _();
1400
+ let n = v() ? sn(t.content) : t.title.trim(), r = mn(t.content, t.attachments), i = _();
1399
1401
  await Promise.all([Y(m(e), {
1400
1402
  title: n,
1401
1403
  ...r,
1402
- previewImage: r.previewImage ? r.previewImage : ve(),
1403
- videoUrl: r.videoUrl ? r.videoUrl : ve(),
1404
+ previewImage: r.previewImage ? r.previewImage : be(),
1405
+ videoUrl: r.videoUrl ? r.videoUrl : be(),
1404
1406
  tags: t.tags ?? [],
1405
1407
  listed: i,
1406
1408
  attachments: t.attachments ?? [],
1407
- updatedAt: K(),
1409
+ updatedAt: q(),
1408
1410
  ...t.translations ? {
1409
1411
  i18n: t.translations.i18n,
1410
1412
  i18nSource: t.translations.i18nSource
1411
1413
  } : {}
1412
- }), q(h(e), {
1414
+ }), De(h(e), {
1413
1415
  content: t.content,
1414
1416
  ...t.translations ? t.translations.contentI18n ? {
1415
1417
  i18n: t.translations.contentI18n,
1416
1418
  i18nSource: t.translations.contentI18nSource
1417
1419
  } : {
1418
- i18n: ve(),
1419
- i18nSource: ve()
1420
+ i18n: be(),
1421
+ i18nSource: be()
1420
1422
  } : {}
1421
1423
  }, { merge: !0 })]);
1422
1424
  }
@@ -1424,25 +1426,25 @@ function Hn(e) {
1424
1426
  D(), await Promise.all([Y(m(e), {
1425
1427
  i18n: t.i18n,
1426
1428
  i18nSource: t.i18nSource,
1427
- updatedAt: K()
1428
- }), q(h(e), t.contentI18n ? {
1429
+ updatedAt: q()
1430
+ }), De(h(e), t.contentI18n ? {
1429
1431
  i18n: t.contentI18n,
1430
1432
  i18nSource: t.contentI18nSource
1431
1433
  } : {
1432
- i18n: ve(),
1433
- i18nSource: ve()
1434
+ i18n: be(),
1435
+ i18nSource: be()
1434
1436
  }, { merge: !0 })]);
1435
1437
  }
1436
1438
  async function ae(e) {
1437
1439
  await Y(m(e), {
1438
1440
  isPublished: !0,
1439
- publishedAt: K(),
1440
- updatedAt: K()
1441
+ publishedAt: q(),
1442
+ updatedAt: q()
1441
1443
  });
1442
1444
  }
1443
1445
  async function oe(e) {
1444
- await Zt(t, Re(n), d(), e);
1445
- for (let [t, n] of zn) n === e && zn.delete(t);
1446
+ await an(t, We(n), d(), e);
1447
+ for (let [t, n] of Kn) n === e && Kn.delete(t);
1446
1448
  }
1447
1449
  return {
1448
1450
  createPostId: x,
@@ -1459,9 +1461,9 @@ function Hn(e) {
1459
1461
  deletePost: oe
1460
1462
  };
1461
1463
  }
1462
- var Un = 10;
1463
- function Wn(e, t = {}) {
1464
- let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Pt(), s = () => j(), c = t.pageSize ?? Un, l = N(() => i.value?.uid ?? ""), u = N(() => Vn(e)), d = () => o(n, s()), f = N(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = F([]), m = F([]), h = F([]), g = F(!0), _ = F(!0), v = F(!1), y = F(""), b = null, x = null, S, C;
1464
+ var Xn = 10;
1465
+ function Zn(e, t = {}) {
1466
+ let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = Vt(), s = () => j(), c = t.pageSize ?? Xn, l = N(() => i.value?.uid ?? ""), u = N(() => Jn(e)), d = () => o(n, s()), f = N(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = F([]), m = F([]), h = F([]), g = F(!0), _ = F(!0), v = F(!1), y = F(""), b = null, x = null, S, C;
1465
1467
  function w(e) {
1466
1468
  return e.createdAt?.toMillis?.() ?? 0;
1467
1469
  }
@@ -1479,7 +1481,7 @@ function Wn(e, t = {}) {
1479
1481
  return f.value && e.push(X("listed", "==", !0)), e;
1480
1482
  }
1481
1483
  function D() {
1482
- S?.(), S = U(G(d(), ...E(), W("createdAt", "desc"), W(R(), "desc"), H(c)), (e) => {
1484
+ S?.(), S = W(K(d(), ...E(), G("createdAt", "desc"), G(z(), "desc"), U(c)), (e) => {
1483
1485
  p.value = e.docs.map((e) => ({
1484
1486
  id: e.id,
1485
1487
  ...e.data()
@@ -1495,7 +1497,7 @@ function Wn(e, t = {}) {
1495
1497
  h.value = [];
1496
1498
  return;
1497
1499
  }
1498
- C = U(G(d(), X("boardId", "==", u.value), X("authorUid", "==", e), X("isPublished", "==", !1), W("createdAt", "desc"), H(20)), (e) => {
1500
+ C = W(K(d(), X("boardId", "==", u.value), X("authorUid", "==", e), X("isPublished", "==", !1), G("createdAt", "desc"), U(20)), (e) => {
1499
1501
  h.value = e.docs.map((e) => ({
1500
1502
  id: e.id,
1501
1503
  ...e.data()
@@ -1510,11 +1512,11 @@ function Wn(e, t = {}) {
1510
1512
  try {
1511
1513
  let e = x ?? b, t = [
1512
1514
  ...E(),
1513
- W("createdAt", "desc"),
1514
- W(R(), "desc"),
1515
+ G("createdAt", "desc"),
1516
+ G(z(), "desc"),
1515
1517
  ...e ? [J(e)] : [],
1516
- H(c + 1)
1517
- ], n = await B(G(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
1518
+ U(c + 1)
1519
+ ], n = await V(K(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
1518
1520
  id: e.id,
1519
1521
  ...e.data()
1520
1522
  })), a = new Set([...m.value, ...p.value].map((e) => e.id));
@@ -1544,8 +1546,8 @@ function Wn(e, t = {}) {
1544
1546
  loadMore: te
1545
1547
  };
1546
1548
  }
1547
- function Gn(e, t) {
1548
- let n = Z(), r = () => j(), i = N(() => Vn(e)), a = N(() => typeof t == "string" ? t : t.value), o = N(() => y(n, r(), a.value)), c = N(() => s(n, r(), a.value)), { data: l, pending: u } = De(o), { data: d, pending: f } = De(c);
1549
+ function Qn(e, t) {
1550
+ let n = Z(), r = () => j(), i = N(() => Jn(e)), a = N(() => typeof t == "string" ? t : t.value), o = N(() => y(n, r(), a.value)), c = N(() => s(n, r(), a.value)), { data: l, pending: u } = je(o), { data: d, pending: f } = je(c);
1549
1551
  return {
1550
1552
  post: N(() => {
1551
1553
  if (!l.value || l.value.boardId !== i.value) return null;
@@ -1554,10 +1556,10 @@ function Gn(e, t) {
1554
1556
  ...l.value,
1555
1557
  id: a.value,
1556
1558
  i18n: Q(l.value.i18n),
1557
- i18nSource: rt(l.value.i18nSource),
1559
+ i18nSource: ut(l.value.i18nSource),
1558
1560
  content: d.value?.content ?? "",
1559
1561
  contentI18n: Q(e?.i18n),
1560
- contentI18nSource: it(e?.i18nSource)
1562
+ contentI18nSource: dt(e?.i18nSource)
1561
1563
  };
1562
1564
  }),
1563
1565
  pending: N(() => u.value || f.value)
@@ -1565,9 +1567,9 @@ function Gn(e, t) {
1565
1567
  }
1566
1568
  //#endregion
1567
1569
  //#region src/composables/useMemiBoardUserPosts.ts
1568
- var Kn = 10;
1569
- function qn(e, t = {}) {
1570
- let n = Z(), r = () => j(), i = () => o(n, r()), a = t.pageSize ?? Kn, s = N(() => I(e)?.trim() || ""), c = F([]), l = F([]), u = F(!0), d = F(!0), f = F(!1), p = F(""), m = null, h = null, g;
1570
+ var $n = 10;
1571
+ function er(e, t = {}) {
1572
+ let n = Z(), r = () => j(), i = () => o(n, r()), a = t.pageSize ?? $n, s = N(() => I(e)?.trim() || ""), c = F([]), l = F([]), u = F(!0), d = F(!0), f = F(!1), p = F(""), m = null, h = null, g;
1571
1573
  function _(e) {
1572
1574
  return {
1573
1575
  id: e.id,
@@ -1594,7 +1596,7 @@ function qn(e, t = {}) {
1594
1596
  u.value = !1;
1595
1597
  return;
1596
1598
  }
1597
- g = U(G(i(), ...b(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1599
+ g = W(K(i(), ...b(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
1598
1600
  c.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), u.value = !1;
1599
1601
  }, (e) => {
1600
1602
  console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
@@ -1606,11 +1608,11 @@ function qn(e, t = {}) {
1606
1608
  try {
1607
1609
  let e = h ?? m, t = [
1608
1610
  ...b(),
1609
- W("createdAt", "desc"),
1610
- W(R(), "desc"),
1611
+ G("createdAt", "desc"),
1612
+ G(z(), "desc"),
1611
1613
  ...e ? [J(e)] : [],
1612
- H(a + 1)
1613
- ], n = await B(G(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), s = new Set([...l.value, ...c.value].map((e) => e.id));
1614
+ U(a + 1)
1615
+ ], n = await V(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), s = new Set([...l.value, ...c.value].map((e) => e.id));
1614
1616
  l.value.push(...o.filter((e) => !s.has(e.id))), h = r.at(-1) ?? h, d.value = n.docs.length > a;
1615
1617
  } catch (e) {
1616
1618
  p.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
@@ -1633,9 +1635,9 @@ function qn(e, t = {}) {
1633
1635
  }
1634
1636
  //#endregion
1635
1637
  //#region src/composables/useMemiBoardUserComments.ts
1636
- var Jn = 10;
1637
- function Yn(e, t = {}) {
1638
- let n = Z(), r = () => j(), i = () => g(n, r()), a = t.pageSize ?? Jn, o = N(() => I(e)?.trim() || ""), s = F([]), c = F([]), l = F(!0), u = F(!0), d = F(!1), f = F(""), p = null, m = null, h;
1638
+ var tr = 10;
1639
+ function nr(e, t = {}) {
1640
+ let n = Z(), r = () => j(), i = () => g(n, r()), a = t.pageSize ?? tr, o = N(() => I(e)?.trim() || ""), s = F([]), c = F([]), l = F(!0), u = F(!0), d = F(!1), f = F(""), p = null, m = null, h;
1639
1641
  function _(e) {
1640
1642
  return {
1641
1643
  id: e.id,
@@ -1658,7 +1660,7 @@ function Yn(e, t = {}) {
1658
1660
  l.value = !1;
1659
1661
  return;
1660
1662
  }
1661
- h = U(G(i(), ...b(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1663
+ h = W(K(i(), ...b(), G("createdAt", "desc"), G(z(), "desc"), U(a)), (e) => {
1662
1664
  s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
1663
1665
  }, (e) => {
1664
1666
  console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
@@ -1670,11 +1672,11 @@ function Yn(e, t = {}) {
1670
1672
  try {
1671
1673
  let e = m ?? p, t = [
1672
1674
  ...b(),
1673
- W("createdAt", "desc"),
1674
- W(R(), "desc"),
1675
+ G("createdAt", "desc"),
1676
+ G(z(), "desc"),
1675
1677
  ...e ? [J(e)] : [],
1676
- H(a + 1)
1677
- ], n = await B(G(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
1678
+ U(a + 1)
1679
+ ], n = await V(K(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
1678
1680
  c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
1679
1681
  } catch (e) {
1680
1682
  f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
@@ -1697,11 +1699,11 @@ function Yn(e, t = {}) {
1697
1699
  }
1698
1700
  //#endregion
1699
1701
  //#region src/composables/useMemiBoardCommentFeed.ts
1700
- var Xn = 10;
1701
- function Zn(e = "latest", t = {}) {
1702
+ var rr = 10;
1703
+ function ir(e = "latest", t = {}) {
1702
1704
  let n = Z(), r = () => j(), i = () => g(n, r()), a = N(() => {
1703
1705
  let e = Math.floor(Number(I(t.pageSize)));
1704
- return Number.isFinite(e) && e > 0 ? e : Xn;
1706
+ return Number.isFinite(e) && e > 0 ? e : rr;
1705
1707
  }), o = N(() => I(e) === "likes" ? "likes" : "latest"), s = F([]), c = F([]), l = F(!0), u = F(!0), d = F(!1), f = F(""), p = null, m = null, h;
1706
1708
  function _(e) {
1707
1709
  return {
@@ -1727,17 +1729,17 @@ function Zn(e = "latest", t = {}) {
1727
1729
  function x() {
1728
1730
  return o.value === "likes" ? [
1729
1731
  X("listed", "==", !0),
1730
- W("likeCount", "desc"),
1731
- W("createdAt", "desc"),
1732
- W(R(), "desc")
1732
+ G("likeCount", "desc"),
1733
+ G("createdAt", "desc"),
1734
+ G(z(), "desc")
1733
1735
  ] : [
1734
1736
  X("listed", "==", !0),
1735
- W("createdAt", "desc"),
1736
- W(R(), "desc")
1737
+ G("createdAt", "desc"),
1738
+ G(z(), "desc")
1737
1739
  ];
1738
1740
  }
1739
1741
  function S() {
1740
- h?.(), h = U(G(i(), ...x(), H(a.value)), (e) => {
1742
+ h?.(), h = W(K(i(), ...x(), U(a.value)), (e) => {
1741
1743
  s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
1742
1744
  }, (e) => {
1743
1745
  console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
@@ -1747,7 +1749,7 @@ function Zn(e = "latest", t = {}) {
1747
1749
  if (!(d.value || !u.value || l.value)) {
1748
1750
  d.value = !0, f.value = "";
1749
1751
  try {
1750
- let e = m ?? p, t = await B(G(i(), ...x(), ...e ? [J(e)] : [], H(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
1752
+ let e = m ?? p, t = await V(K(i(), ...x(), ...e ? [J(e)] : [], U(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
1751
1753
  c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
1752
1754
  } catch (e) {
1753
1755
  f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
@@ -1771,9 +1773,9 @@ function Zn(e = "latest", t = {}) {
1771
1773
  }
1772
1774
  //#endregion
1773
1775
  //#region src/composables/useMemiBoardAuthorMemo.ts
1774
- var Qn = 100, $n = /* @__PURE__ */ new Map();
1776
+ var ar = 100, or = /* @__PURE__ */ new Map();
1775
1777
  function $(e) {
1776
- let t = $n.get(e);
1778
+ let t = or.get(e);
1777
1779
  return t || (t = ge({
1778
1780
  text: "",
1779
1781
  sentiment: null,
@@ -1781,10 +1783,10 @@ function $(e) {
1781
1783
  pending: !1,
1782
1784
  saving: !1,
1783
1785
  error: ""
1784
- }), $n.set(e, t)), t;
1786
+ }), or.set(e, t)), t;
1785
1787
  }
1786
- function er(e) {
1787
- let t = Z(), n = () => j(), { user: r } = Pt(), { t: i, formatNumber: a } = A(), o = N(() => I(e)), s = N(() => `${r.value?.uid ?? ""}:${o.value}`), c = N(() => {
1788
+ function sr(e) {
1789
+ let t = Z(), n = () => j(), { user: r } = Vt(), { t: i, formatNumber: a } = A(), o = N(() => I(e)), s = N(() => `${r.value?.uid ?? ""}:${o.value}`), c = N(() => {
1788
1790
  let e = r.value?.uid, i = o.value;
1789
1791
  return !e || !i ? null : f(t, n(), e, i);
1790
1792
  }), l = N({
@@ -1799,7 +1801,7 @@ function er(e) {
1799
1801
  let t = $(s.value);
1800
1802
  t.pending = !0, t.error = "";
1801
1803
  try {
1802
- let n = (await z(e)).data();
1804
+ let n = (await B(e)).data();
1803
1805
  t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
1804
1806
  } catch (e) {
1805
1807
  t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
@@ -1815,10 +1817,10 @@ function er(e) {
1815
1817
  let o = $(s.value);
1816
1818
  o.saving = !0, o.error = "";
1817
1819
  try {
1818
- await q(n, {
1820
+ await De(n, {
1819
1821
  text: r,
1820
1822
  sentiment: t,
1821
- updatedAt: K()
1823
+ updatedAt: q()
1822
1824
  }), o.text = r, o.sentiment = t, o.loaded = !0;
1823
1825
  } catch (e) {
1824
1826
  throw o.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
@@ -1832,7 +1834,7 @@ function er(e) {
1832
1834
  let t = $(s.value);
1833
1835
  t.saving = !0, t.error = "";
1834
1836
  try {
1835
- await _e(e), t.text = "", t.sentiment = null, t.loaded = !0;
1837
+ await ye(e), t.text = "", t.sentiment = null, t.loaded = !0;
1836
1838
  } catch (e) {
1837
1839
  throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
1838
1840
  } finally {
@@ -1853,23 +1855,23 @@ function er(e) {
1853
1855
  }
1854
1856
  //#endregion
1855
1857
  //#region src/utils/subscribeCommentRange.ts
1856
- function tr(e, t, n, r, i, a) {
1857
- return U(G(g(e, t), X("postId", "==", n), ...r.rootId ? [X("rootId", "==", r.rootId), X("isReply", "==", !0)] : [X("parentId", "==", null)], W("createdAt", "asc"), W(R(), "asc"), ...r.hasMore && r.cursor ? [be(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
1858
+ function cr(e, t, n, r, i, a) {
1859
+ 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(z(), "asc"), ...r.hasMore && r.cursor ? [xe(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
1858
1860
  ...e.data(),
1859
1861
  id: e.id
1860
1862
  }))), a);
1861
1863
  }
1862
1864
  //#endregion
1863
1865
  //#region src/composables/useMemiBoardComments.ts
1864
- var nr = 10, rr = 5, ir = 1e3;
1865
- function ar(e, { t, formatNumber: n }) {
1866
+ var lr = 10, ur = 5, dr = 1e3;
1867
+ function fr(e, { t, formatNumber: n }) {
1866
1868
  let r = e.trim();
1867
1869
  if (!r) throw Error(t("comments.bodyRequired"));
1868
- if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(ir) }));
1870
+ if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(dr) }));
1869
1871
  return r;
1870
1872
  }
1871
- function or(e, t, n = {}) {
1872
- let r = Z(), i = Oe(), a = () => j(), { isBoardHidden: o } = Rn(), s = A(), { t: c } = s, l = () => g(r, a()), u = N(() => I(e)), f = N(() => I(t)), p = () => !o(u.value), m = F([]), h = F(n.subscribe !== !1), _ = F([]), v = F(!1), b = F(n.subscribe !== !1), S = F(n.subscribe !== !1), C = null, w = 0, T = 0, E;
1873
+ function pr(e, t, n = {}) {
1874
+ let r = Z(), i = Me(), a = () => j(), { isBoardHidden: o } = Gn(), s = A(), { t: c } = s, l = () => g(r, a()), u = N(() => I(e)), f = N(() => I(t)), p = () => !o(u.value), m = F([]), h = F(n.subscribe !== !1), _ = F([]), v = F(!1), b = F(n.subscribe !== !1), S = F(n.subscribe !== !1), C = null, w = 0, T = 0, E;
1873
1875
  function D(e) {
1874
1876
  return e.createdAt?.toMillis?.() ?? 0;
1875
1877
  }
@@ -1883,7 +1885,7 @@ function or(e, t, n = {}) {
1883
1885
  if (T++, v.value = !1, _.value = [], m.value = [], C = null, E?.(), E = void 0, b.value = n.subscribe !== !1, n.subscribe !== !1) {
1884
1886
  h.value = !0, S.value = !0;
1885
1887
  try {
1886
- let n = B(G(l(), X("postId", "==", e), W("createdAt", "desc"), W(R(), "desc")));
1888
+ let n = V(K(l(), X("postId", "==", e), G("createdAt", "desc"), G(z(), "desc")));
1887
1889
  if (await Promise.all([n.then((e) => {
1888
1890
  if (t !== w) return;
1889
1891
  let n = e.docs.map((e) => ({
@@ -1900,7 +1902,7 @@ function or(e, t, n = {}) {
1900
1902
  function te() {
1901
1903
  E?.();
1902
1904
  let e = ++T;
1903
- E = tr(r, a(), f.value, {
1905
+ E = cr(r, a(), f.value, {
1904
1906
  cursor: C,
1905
1907
  hasMore: b.value
1906
1908
  }, (t) => {
@@ -1917,17 +1919,17 @@ function or(e, t, n = {}) {
1917
1919
  let t = [
1918
1920
  X("postId", "==", f.value),
1919
1921
  X("parentId", "==", null),
1920
- W("createdAt", "asc"),
1921
- W(R(), "asc"),
1922
+ G("createdAt", "asc"),
1923
+ G(z(), "asc"),
1922
1924
  ...C ? [J(C)] : [],
1923
- H(11)
1924
- ], n = await B(G(l(), ...t));
1925
+ U(11)
1926
+ ], n = await V(K(l(), ...t));
1925
1927
  if (e !== w) return;
1926
- let r = n.docs.slice(0, nr), i = r.map((e) => ({
1928
+ let r = n.docs.slice(0, lr), i = r.map((e) => ({
1927
1929
  id: e.id,
1928
1930
  ...e.data()
1929
1931
  })), a = new Set(_.value.map((e) => e.id));
1930
- _.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size > nr, te();
1932
+ _.value.push(...i.filter((e) => !a.has(e.id))), C = r.at(-1) ?? C, b.value = n.size > lr, te();
1931
1933
  } finally {
1932
1934
  e === w && (v.value = !1);
1933
1935
  }
@@ -1936,10 +1938,10 @@ function or(e, t, n = {}) {
1936
1938
  w++, T++, E?.();
1937
1939
  });
1938
1940
  function O() {
1939
- return ye(l()).id;
1941
+ return R(l()).id;
1940
1942
  }
1941
1943
  function re(e) {
1942
- if (!e) return ye(l());
1944
+ if (!e) return R(l());
1943
1945
  if (!/^[A-Za-z0-9]{10,128}$/.test(e)) throw Error(c("commentForm.uploadFailed"));
1944
1946
  return d(r, a(), e);
1945
1947
  }
@@ -1953,25 +1955,25 @@ function or(e, t, n = {}) {
1953
1955
  postId: f.value,
1954
1956
  boardId: u.value,
1955
1957
  listed: p(),
1956
- body: ar(e.body, s),
1958
+ body: fr(e.body, s),
1957
1959
  authorUid: e.authorUid,
1958
1960
  authorName: e.authorName,
1959
1961
  authorPhoto: e.authorPhoto,
1960
1962
  moderationStatus: "approved",
1961
1963
  replyCount: 0,
1962
1964
  likeCount: 0,
1963
- createdAt: K(),
1964
- updatedAt: K(),
1965
+ createdAt: q(),
1966
+ updatedAt: q(),
1965
1967
  ...n.length ? { images: n } : {}
1966
1968
  };
1967
1969
  }
1968
1970
  async function oe(e) {
1969
1971
  if (!e.length) return;
1970
- let t = Re(i);
1971
- await Promise.all(e.map((e) => Le(Be(t, e)).catch(() => {})));
1972
+ let t = We(i);
1973
+ await Promise.all(e.map((e) => He(qe(t, e)).catch(() => {})));
1972
1974
  }
1973
1975
  async function k(e) {
1974
- let t = we(r), n = re(e.id);
1976
+ let t = Oe(r), n = re(e.id);
1975
1977
  t.set(n, {
1976
1978
  ...ae(e, n.id),
1977
1979
  parentId: null,
@@ -1980,11 +1982,11 @@ function or(e, t, n = {}) {
1980
1982
  replyToUid: null,
1981
1983
  replyToName: null,
1982
1984
  isReply: !1
1983
- }), t.update(y(r, a(), f.value), { commentCount: V(1) }), await t.commit();
1985
+ }), t.update(y(r, a(), f.value), { commentCount: H(1) }), await t.commit();
1984
1986
  }
1985
1987
  async function se(e) {
1986
1988
  if (!e.parent.id) throw Error(c("comments.replyTargetNotFound"));
1987
- let t = we(r), n = re(e.id), i = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1989
+ let t = Oe(r), n = re(e.id), i = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1988
1990
  if (!i) throw Error(c("comments.threadNotFound"));
1989
1991
  let o = Math.min((e.parent.depth ?? 0) + 1, 2);
1990
1992
  t.set(n, {
@@ -1995,31 +1997,31 @@ function or(e, t, n = {}) {
1995
1997
  replyToUid: e.parent.authorUid,
1996
1998
  replyToName: e.parent.authorName,
1997
1999
  isReply: !0
1998
- }), t.update(d(r, a(), i), { replyCount: V(1) }), t.update(y(r, a(), f.value), { commentCount: V(1) }), await t.commit();
2000
+ }), t.update(d(r, a(), i), { replyCount: H(1) }), t.update(y(r, a(), f.value), { commentCount: H(1) }), await t.commit();
1999
2001
  }
2000
2002
  async function ce(e) {
2001
2003
  if (!e.id) return;
2002
2004
  if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(c("comments.hasRepliesCannotDelete"));
2003
2005
  let t = de(e.images, `${x(a(), f.value)}/comment-images/${e.id}/`);
2004
- await Xt(r, a(), e.id);
2005
- let n = we(r);
2006
- n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount: V(-1) }), n.update(y(r, a(), f.value), { commentCount: V(-1) }), await n.commit(), await oe(t), _.value = _.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
2006
+ await rn(r, a(), e.id);
2007
+ let n = Oe(r);
2008
+ n.delete(d(r, a(), e.id)), e.parentId && e.rootId && n.update(d(r, a(), e.rootId), { replyCount: H(-1) }), n.update(y(r, a(), f.value), { commentCount: H(-1) }), await n.commit(), await oe(t), _.value = _.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
2007
2009
  }
2008
2010
  async function le(e, t, n) {
2009
2011
  let i = {
2010
- body: ar(t, s),
2011
- updatedAt: K()
2012
+ body: fr(t, s),
2013
+ updatedAt: q()
2012
2014
  };
2013
2015
  if (n !== void 0) {
2014
2016
  let t = ie(e, n);
2015
- i.images = t.length ? t : ve();
2017
+ i.images = t.length ? t : be();
2016
2018
  }
2017
2019
  await Y(d(r, a(), e), i);
2018
2020
  }
2019
2021
  async function fe(e, t, n) {
2020
2022
  await Y(d(r, a(), e), {
2021
2023
  isBlinded: t,
2022
- moderatedAt: K(),
2024
+ moderatedAt: q(),
2023
2025
  moderatedBy: n
2024
2026
  });
2025
2027
  }
@@ -2037,7 +2039,7 @@ function or(e, t, n = {}) {
2037
2039
  deleteComment: ce
2038
2040
  };
2039
2041
  }
2040
- function sr(e, t, n) {
2042
+ function mr(e, t, n) {
2041
2043
  let r = Z(), i = () => j(), a = () => g(r, i()), o = t.trim(), s = F([]), c = F(!1), l = F(!1), u = F(!0), d = null, f, p = 0, m = 0;
2042
2044
  function h(e) {
2043
2045
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
@@ -2045,7 +2047,7 @@ function sr(e, t, n) {
2045
2047
  function _() {
2046
2048
  f?.();
2047
2049
  let e = ++m;
2048
- f = tr(r, i(), o, {
2050
+ f = cr(r, i(), o, {
2049
2051
  rootId: n,
2050
2052
  cursor: d,
2051
2053
  hasMore: u.value
@@ -2064,17 +2066,17 @@ function sr(e, t, n) {
2064
2066
  X("postId", "==", o),
2065
2067
  X("rootId", "==", n),
2066
2068
  X("isReply", "==", !0),
2067
- W("createdAt", "asc"),
2068
- W(R(), "asc"),
2069
+ G("createdAt", "asc"),
2070
+ G(z(), "asc"),
2069
2071
  ...d ? [J(d)] : [],
2070
- H(6)
2071
- ], r = await B(G(a(), ...t));
2072
+ U(6)
2073
+ ], r = await V(K(a(), ...t));
2072
2074
  if (e !== p) return;
2073
- let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, rr), f = c.map((e) => ({
2075
+ let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, ur), f = c.map((e) => ({
2074
2076
  id: e.id,
2075
2077
  ...e.data()
2076
2078
  })).filter((e) => !i.has(e.id));
2077
- s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > rr, _();
2079
+ s.value = h([...s.value, ...f]), d = c.at(-1) ?? d, l.value = !0, u.value = r.size > ur, _();
2078
2080
  } finally {
2079
2081
  e === p && (c.value = !1);
2080
2082
  }
@@ -2098,8 +2100,8 @@ function sr(e, t, n) {
2098
2100
  }
2099
2101
  //#endregion
2100
2102
  //#region src/composables/useMemiBoardLikes.ts
2101
- function cr(e, t) {
2102
- let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = F(!1), s = F(!1), c;
2103
+ function hr(e, t) {
2104
+ let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = F(!1), s = F(!1), c;
2103
2105
  function l(e) {
2104
2106
  return he(r, n(), t, e);
2105
2107
  }
@@ -2108,7 +2110,7 @@ function cr(e, t) {
2108
2110
  o.value = !1;
2109
2111
  return;
2110
2112
  }
2111
- c = U(l(e.uid), (e) => {
2113
+ c = W(l(e.uid), (e) => {
2112
2114
  o.value = e.exists();
2113
2115
  }, (e) => {
2114
2116
  console.error("[memi-board:likes] onSnapshot failed", e);
@@ -2121,12 +2123,12 @@ function cr(e, t) {
2121
2123
  s.value = !0;
2122
2124
  try {
2123
2125
  let i = l(c), a = y(r, n(), t);
2124
- return await Ce(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: V(-1) }), !1) : (n.set(i, {
2126
+ return await Ee(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: H(-1) }), !1) : (n.set(i, {
2125
2127
  uid: c,
2126
2128
  postId: t,
2127
2129
  boardId: e,
2128
- createdAt: K()
2129
- }), n.update(a, { likeCount: V(1) }), !0));
2130
+ createdAt: q()
2131
+ }), n.update(a, { likeCount: H(1) }), !0));
2130
2132
  } finally {
2131
2133
  s.value = !1;
2132
2134
  }
@@ -2137,11 +2139,11 @@ function cr(e, t) {
2137
2139
  toggleLike: u
2138
2140
  };
2139
2141
  }
2140
- var lr = Symbol.for("memi-board:comment-likes");
2141
- function ur(e, t) {
2142
- let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = N(() => I(e)), s = N(() => I(t)), c = F(/* @__PURE__ */ new Set()), l = F(null), u;
2142
+ var gr = Symbol.for("memi-board:comment-likes");
2143
+ function _r(e, t) {
2144
+ let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = N(() => I(e)), s = N(() => I(t)), c = F(/* @__PURE__ */ new Set()), l = F(null), u;
2143
2145
  L([i, s], ([e, t]) => {
2144
- u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = U(G(b(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
2146
+ 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) => {
2145
2147
  let t = /* @__PURE__ */ new Set();
2146
2148
  for (let n of e.docs) {
2147
2149
  let e = n.data().commentId;
@@ -2165,7 +2167,7 @@ function ur(e, t) {
2165
2167
  if (l.value) return f(e);
2166
2168
  l.value = e;
2167
2169
  try {
2168
- let i = he(r, n(), e, t), l = d(r, n(), e), u = await Ce(r, async (n) => {
2170
+ let i = he(r, n(), e, t), l = d(r, n(), e), u = await Ee(r, async (n) => {
2169
2171
  let r = await n.get(i), c = await n.get(l);
2170
2172
  if (!c.exists()) throw Error(a("likes.commentNotFound"));
2171
2173
  let u = Number(c.data()?.likeCount ?? 0);
@@ -2175,7 +2177,7 @@ function ur(e, t) {
2175
2177
  boardId: o.value,
2176
2178
  commentId: e,
2177
2179
  target: "comment",
2178
- createdAt: K()
2180
+ createdAt: q()
2179
2181
  }), n.update(l, { likeCount: u + 1 }), !0);
2180
2182
  }), f = new Set(c.value);
2181
2183
  return u ? f.add(e) : f.delete(e), c.value = f, u;
@@ -2191,7 +2193,7 @@ function ur(e, t) {
2191
2193
  }
2192
2194
  //#endregion
2193
2195
  //#region src/composables/useMemiBoardReports.ts
2194
- var dr = 200, fr = [
2196
+ var vr = 200, yr = [
2195
2197
  {
2196
2198
  value: "spam",
2197
2199
  label: "스팸·광고"
@@ -2213,17 +2215,17 @@ var dr = 200, fr = [
2213
2215
  label: "기타"
2214
2216
  }
2215
2217
  ];
2216
- function pr(e, t) {
2217
- return fr.some((t) => t.value === e) ? k(t, `reports.reason.${e}`) : e || k(t, "reports.fallbackLabel");
2218
+ function br(e, t) {
2219
+ return yr.some((t) => t.value === e) ? k(t, `reports.reason.${e}`) : e || k(t, "reports.fallbackLabel");
2218
2220
  }
2219
- function mr(e, t) {
2220
- let n = () => j(), r = Z(), i = Ee(), { t: a } = A(), o = F(!1), s = F(!1), c;
2221
+ function xr(e, t) {
2222
+ let n = () => j(), r = Z(), i = Ae(), { t: a } = A(), o = F(!1), s = F(!1), c;
2221
2223
  L(i, (e) => {
2222
2224
  if (c?.(), c = void 0, !e) {
2223
2225
  o.value = !1;
2224
2226
  return;
2225
2227
  }
2226
- c = U(u(r, n(), t, e.uid), (e) => {
2228
+ c = W(u(r, n(), t, e.uid), (e) => {
2227
2229
  o.value = e.exists();
2228
2230
  }, (e) => {
2229
2231
  console.error("[memi-board:reports] onSnapshot failed", e);
@@ -2234,13 +2236,13 @@ function mr(e, t) {
2234
2236
  if (!l) throw Error(a("reports.signInRequired"));
2235
2237
  if (s.value) return;
2236
2238
  if (c.authorUid && c.authorUid === l) throw Error(a("reports.cannotReportOwn"));
2237
- let d = fr.some((e) => e.value === c.reason) ? c.reason : null;
2239
+ let d = yr.some((e) => e.value === c.reason) ? c.reason : null;
2238
2240
  if (!d) throw Error(a("reports.reasonRequired"));
2239
2241
  let f = (c.detail || "").trim();
2240
2242
  if (f.length > 200) throw Error(a("reports.detailTooLong", { max: 200 }));
2241
2243
  s.value = !0;
2242
2244
  try {
2243
- await q(u(r, n(), t, l), {
2245
+ await De(u(r, n(), t, l), {
2244
2246
  uid: l,
2245
2247
  target: "post",
2246
2248
  postId: t,
@@ -2251,7 +2253,7 @@ function mr(e, t) {
2251
2253
  postTitle: (c.postTitle || "").trim().slice(0, 200),
2252
2254
  authorUid: c.authorUid || "",
2253
2255
  authorName: c.authorName ?? null,
2254
- createdAt: K()
2256
+ createdAt: q()
2255
2257
  }), o.value = !0;
2256
2258
  } finally {
2257
2259
  s.value = !1;
@@ -2263,9 +2265,9 @@ function mr(e, t) {
2263
2265
  submitReport: l
2264
2266
  };
2265
2267
  }
2266
- var hr = 20;
2267
- function gr(e = {}) {
2268
- let t = () => j(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Pt(), o = e.pageSize ?? hr, s = N(() => I(e.boardId)?.trim() || ""), c = F([]), d = F(!0), f = F(!1), p = F(!1), m = F(""), h = null, g;
2268
+ var Sr = 20;
2269
+ function Cr(e = {}) {
2270
+ let t = () => j(), n = Z(), { isAdmin: r, canManageContent: i, canManageBoard: a } = Vt(), o = e.pageSize ?? Sr, s = N(() => I(e.boardId)?.trim() || ""), c = F([]), d = F(!0), f = F(!1), p = F(!1), m = F(""), h = null, g;
2269
2271
  function _(e) {
2270
2272
  return {
2271
2273
  id: e.id,
@@ -2279,7 +2281,7 @@ function gr(e = {}) {
2279
2281
  }
2280
2282
  d.value = !0;
2281
2283
  let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")];
2282
- g = U(G(l(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), H(o)), (e) => {
2284
+ g = W(K(l(n, t()), ...e, G("createdAt", "desc"), G(z(), "desc"), U(o)), (e) => {
2283
2285
  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;
2284
2286
  }, (e) => {
2285
2287
  console.error("[memi-board:report-queue] onSnapshot failed", e), m.value = e.message || String(e), d.value = !1;
@@ -2290,7 +2292,7 @@ function gr(e = {}) {
2290
2292
  if (!f.value && p.value && h && i.value) {
2291
2293
  f.value = !0, m.value = "";
2292
2294
  try {
2293
- let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await B(G(l(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), J(h), H(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
2295
+ let e = s.value ? [X("status", "==", "open"), X("boardId", "==", s.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await V(K(l(n, t()), ...e, G("createdAt", "desc"), G(z(), "desc"), J(h), U(o + 1))), i = r.docs.slice(0, o), u = new Set(c.value.map((e) => e.id));
2294
2296
  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;
2295
2297
  } catch (e) {
2296
2298
  m.value = e.message || String(e);
@@ -2302,8 +2304,8 @@ function gr(e = {}) {
2302
2304
  async function b(e, r) {
2303
2305
  e.id && a(e.boardId) && await Y(u(n, t(), e.postId, e.uid), {
2304
2306
  status: r,
2305
- reviewedAt: K(),
2306
- reviewedBy: Ee().value?.uid || ""
2307
+ reviewedAt: q(),
2308
+ reviewedBy: Ae().value?.uid || ""
2307
2309
  });
2308
2310
  }
2309
2311
  return {
@@ -2319,7 +2321,7 @@ function gr(e = {}) {
2319
2321
  }
2320
2322
  //#endregion
2321
2323
  //#region src/composables/useMemiBoardViews.ts
2322
- function _r() {
2324
+ function wr() {
2323
2325
  let e = () => j(), t = Z();
2324
2326
  function n(t, n) {
2325
2327
  let { postsCollection: r } = e();
@@ -2343,7 +2345,7 @@ function _r() {
2343
2345
  if (s && c && !r(s, c)) {
2344
2346
  i(s, c);
2345
2347
  try {
2346
- await Y(y(t, e(), c), { viewCount: V(1) });
2348
+ await Y(y(t, e(), c), { viewCount: H(1) });
2347
2349
  } catch (e) {
2348
2350
  try {
2349
2351
  sessionStorage.removeItem(n(s, c));
@@ -2359,20 +2361,20 @@ function _r() {
2359
2361
  }
2360
2362
  //#endregion
2361
2363
  //#region src/composables/useMemiBoardModeration.ts
2362
- var vr = {
2364
+ var Tr = {
2363
2365
  flagged: !1,
2364
2366
  category: "none",
2365
2367
  reason: "",
2366
2368
  via: "empty"
2367
2369
  };
2368
- function yr(e = {}) {
2369
- let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Oe(), a = (e) => k(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Pt(), l = Cn([...Sn, ...r.localBlocklist ?? []]);
2370
+ function Er(e = {}) {
2371
+ let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = v().moderation ?? {}, i = Me(), a = (e) => k(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = Vt(), l = An([...kn, ...r.localBlocklist ?? []]);
2370
2372
  function u(e) {
2371
2373
  let t = e.match(l);
2372
2374
  return t ? {
2373
2375
  flagged: !0,
2374
2376
  category: "abuse",
2375
- reason: xn({
2377
+ reason: On({
2376
2378
  via: "local",
2377
2379
  localHit: t[0] || "",
2378
2380
  category: "abuse"
@@ -2381,21 +2383,21 @@ function yr(e = {}) {
2381
2383
  } : null;
2382
2384
  }
2383
2385
  function d() {
2384
- let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Ue(i, {
2385
- backend: new Ve(),
2386
+ let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Xe(i, {
2387
+ backend: new Je(),
2386
2388
  useLimitedUseAppCheckTokens: t
2387
2389
  });
2388
- return We(a, {
2390
+ return Ze(a, {
2389
2391
  model: e,
2390
- systemInstruction: gn(n.value),
2392
+ systemInstruction: Cn(n.value),
2391
2393
  generationConfig: {
2392
2394
  temperature: 0,
2393
2395
  maxOutputTokens: 256,
2394
2396
  responseMimeType: "application/json",
2395
- responseSchema: He.object({ properties: {
2396
- flagged: He.boolean(),
2397
- category: He.string(),
2398
- reason: He.string()
2397
+ responseSchema: Ye.object({ properties: {
2398
+ flagged: Ye.boolean(),
2399
+ category: Ye.string(),
2400
+ reason: Ye.string()
2399
2401
  } })
2400
2402
  }
2401
2403
  });
@@ -2416,7 +2418,7 @@ function yr(e = {}) {
2416
2418
  };
2417
2419
  }
2418
2420
  function p(e, t = "") {
2419
- let n = xn({
2421
+ let n = On({
2420
2422
  reason: e.reason,
2421
2423
  category: e.category,
2422
2424
  via: e.via
@@ -2444,26 +2446,26 @@ function yr(e = {}) {
2444
2446
  };
2445
2447
  let i = e.trim();
2446
2448
  if (!i) return {
2447
- ...vr,
2449
+ ...Tr,
2448
2450
  via: "empty"
2449
2451
  };
2450
2452
  if (t?.skipFilter) return {
2451
- ...vr,
2453
+ ...Tr,
2452
2454
  via: "admin-override"
2453
2455
  };
2454
2456
  let c = u(i);
2455
2457
  if (c) return m(c);
2456
2458
  if (r.enabled === !1) return {
2457
- ...vr,
2459
+ ...Tr,
2458
2460
  via: "disabled"
2459
2461
  };
2460
2462
  try {
2461
- let e = Tn((await d().generateContent(_n(i, n.value))).response.text());
2463
+ let e = Mn((await d().generateContent(wn(i, n.value))).response.text());
2462
2464
  if (!e) return f();
2463
2465
  let t = {
2464
2466
  flagged: e.flagged,
2465
2467
  category: e.category,
2466
- reason: xn({
2468
+ reason: On({
2467
2469
  reason: e.reason,
2468
2470
  category: e.category,
2469
2471
  via: "ai"
@@ -2483,22 +2485,22 @@ function yr(e = {}) {
2483
2485
  via: "restricted"
2484
2486
  };
2485
2487
  if (r.enabled === !1 || !r.moderateImages) return {
2486
- ...vr,
2488
+ ...Tr,
2487
2489
  via: "disabled"
2488
2490
  };
2489
2491
  try {
2490
2492
  let t = await new Promise((t, n) => {
2491
2493
  let r = new FileReader();
2492
2494
  r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
2493
- }), r = Tn((await d().generateContent([{ inlineData: {
2495
+ }), r = Mn((await d().generateContent([{ inlineData: {
2494
2496
  mimeType: e.type || "image/jpeg",
2495
2497
  data: t
2496
- } }, vn(n.value)])).response.text());
2498
+ } }, Tn(n.value)])).response.text());
2497
2499
  if (!r) return f();
2498
2500
  let i = {
2499
2501
  flagged: r.flagged,
2500
2502
  category: r.category,
2501
- reason: xn({
2503
+ reason: On({
2502
2504
  reason: r.reason,
2503
2505
  category: r.category,
2504
2506
  via: "ai"
@@ -2517,7 +2519,7 @@ function yr(e = {}) {
2517
2519
  }
2518
2520
  //#endregion
2519
2521
  //#region src/composables/useMemiBoardUsers.ts
2520
- var br = [
2522
+ var Dr = [
2521
2523
  "owner",
2522
2524
  "admin",
2523
2525
  "manager",
@@ -2528,13 +2530,13 @@ var br = [
2528
2530
  label: k("ko", `common.role.${e}`),
2529
2531
  description: k("ko", `boardUsers.roleDescription.${e}`)
2530
2532
  }));
2531
- function xr(e = {}) {
2532
- let t = () => j(), n = Z(), { t: r } = A(), i = N(() => I(e.enabled) ?? !0 ? c(n, t()) : null), a = Te(i, { ssrKey: p(t(), "users-management") });
2533
+ function Or(e = {}) {
2534
+ let t = () => j(), n = Z(), { t: r } = A(), i = N(() => I(e.enabled) ?? !0 ? c(n, t()) : null), a = ke(i, { ssrKey: p(t(), "users-management") });
2533
2535
  async function o(e, i) {
2534
- if (!br.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
2536
+ if (!Dr.some((e) => e.value === i)) throw Error(r("boardUsers.unsupportedRole"));
2535
2537
  await Y(h(n, t(), e), {
2536
2538
  role: i,
2537
- updatedAt: K()
2539
+ updatedAt: q()
2538
2540
  });
2539
2541
  }
2540
2542
  return {
@@ -2545,11 +2547,11 @@ function xr(e = {}) {
2545
2547
  }
2546
2548
  //#endregion
2547
2549
  //#region src/composables/useMemiBoardUserProfile.ts
2548
- function Sr(e) {
2550
+ function kr(e) {
2549
2551
  let t = Z(), n = () => j(), r = N(() => I(e)), i = N(() => {
2550
2552
  let e = r.value;
2551
2553
  return e ? h(t, n(), e) : null;
2552
- }), { data: a, pending: o } = De(i);
2554
+ }), { data: a, pending: o } = je(i);
2553
2555
  return {
2554
2556
  profile: a,
2555
2557
  profilePending: o
@@ -2557,7 +2559,7 @@ function Sr(e) {
2557
2559
  }
2558
2560
  //#endregion
2559
2561
  //#region src/utils/section.ts
2560
- var Cr = [
2562
+ var Ar = [
2561
2563
  3,
2562
2564
  4,
2563
2565
  5,
@@ -2567,7 +2569,7 @@ var Cr = [
2567
2569
  9,
2568
2570
  12
2569
2571
  ];
2570
- function wr(e) {
2572
+ function jr(e) {
2571
2573
  return [
2572
2574
  {
2573
2575
  value: "list",
@@ -2583,7 +2585,7 @@ function wr(e) {
2583
2585
  }
2584
2586
  ];
2585
2587
  }
2586
- function Tr(e) {
2588
+ function Mr(e) {
2587
2589
  return [{
2588
2590
  value: "latest",
2589
2591
  label: k(e, "sectionUtil.sort.latest")
@@ -2592,18 +2594,18 @@ function Tr(e) {
2592
2594
  label: k(e, "sectionUtil.sort.likes")
2593
2595
  }];
2594
2596
  }
2595
- var Er = wr("ko"), Dr = Tr("ko");
2596
- function Or(e) {
2597
+ var Nr = jr("ko"), Pr = Mr("ko");
2598
+ function Fr(e) {
2597
2599
  return e === "list" ? 5 : 4;
2598
2600
  }
2599
- function kr(e, t) {
2601
+ function Ir(e, t) {
2600
2602
  let n = Math.floor(Number(t));
2601
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Or(e);
2603
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Fr(e);
2602
2604
  }
2603
- function Ar(e) {
2605
+ function Lr(e) {
2604
2606
  return e === 12 ? null : 12 - e;
2605
2607
  }
2606
- function jr(e) {
2608
+ function Rr(e) {
2607
2609
  return {
2608
2610
  3: "col-span-12 md:col-span-3",
2609
2611
  4: "col-span-12 md:col-span-4",
@@ -2615,36 +2617,36 @@ function jr(e) {
2615
2617
  12: "col-span-12"
2616
2618
  }[e];
2617
2619
  }
2618
- function Mr(e) {
2620
+ function zr(e) {
2619
2621
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2620
2622
  }
2621
- function Nr(e) {
2623
+ function Br(e) {
2622
2624
  let t = e.boardId || e.category || "free";
2623
2625
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2624
2626
  }
2625
- function Pr(e) {
2627
+ function Vr(e) {
2626
2628
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2627
2629
  }
2628
- function Fr(e) {
2630
+ function Hr(e) {
2629
2631
  if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
2630
2632
  }
2631
- function Ir(e) {
2633
+ function Ur(e) {
2632
2634
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2633
2635
  }
2634
- function Lr(e, t) {
2635
- return lt(e, t).trim() || ut(e, t).trim() || k(t, "sectionUtil.untitledPost");
2636
+ function Wr(e, t) {
2637
+ return gt(e, t).trim() || _t(e, t).trim() || k(t, "sectionUtil.untitledPost");
2636
2638
  }
2637
- function Rr(e) {
2639
+ function Gr(e) {
2638
2640
  return e === "image" || e === "video" ? "media" : "list";
2639
2641
  }
2640
- function zr(e, t) {
2641
- return k(t, Rr(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2642
+ function Kr(e, t) {
2643
+ return k(t, Gr(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2642
2644
  }
2643
- function Br(e, t) {
2645
+ function qr(e, t) {
2644
2646
  return e ? t(e)?.listView ?? "default" : "default";
2645
2647
  }
2646
- function Vr(e, t) {
2647
- let n = e.map((e) => Br(e.boardId, t));
2648
+ function Jr(e, t) {
2649
+ let n = e.map((e) => qr(e.boardId, t));
2648
2650
  if (!n.length) return "default";
2649
2651
  if (n.every((e) => e === n[0])) return n[0];
2650
2652
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2652,27 +2654,27 @@ function Vr(e, t) {
2652
2654
  }
2653
2655
  //#endregion
2654
2656
  //#region src/composables/useMemiBoardSections.ts
2655
- var Hr = [];
2656
- function Ur(e) {
2657
+ var Yr = [];
2658
+ function Xr(e) {
2657
2659
  let t = Number(e.cols);
2658
- if (Cr.includes(t)) return t;
2660
+ if (Ar.includes(t)) return t;
2659
2661
  let n = e.width;
2660
2662
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2661
2663
  }
2662
- function Wr(e) {
2664
+ function Zr(e) {
2663
2665
  let t = e.height ?? e.size;
2664
2666
  return t === "sm" || t === "lg" ? t : "md";
2665
2667
  }
2666
- function Gr(e) {
2668
+ function Qr(e) {
2667
2669
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2668
2670
  }
2669
- function Kr(e) {
2671
+ function $r(e) {
2670
2672
  return e.sort === "likes" ? "likes" : "latest";
2671
2673
  }
2672
- function qr(e) {
2674
+ function ei(e) {
2673
2675
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2674
2676
  }
2675
- function Jr(e, t) {
2677
+ function ti(e, t) {
2676
2678
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2677
2679
  for (let e of n) {
2678
2680
  let n = String(e || "").trim();
@@ -2680,34 +2682,34 @@ function Jr(e, t) {
2680
2682
  }
2681
2683
  return r;
2682
2684
  }
2683
- function Yr(e, t) {
2685
+ function ni(e, t) {
2684
2686
  return k(t, "sections.defaultTitle", { n: e + 1 });
2685
2687
  }
2686
- function Xr(e, t, n) {
2687
- let r = Gr(e), i = kr(r, e.count), a = Q(e.i18n), o = rt(e.i18nSource);
2688
+ function ri(e, t, n) {
2689
+ let r = Qr(e), i = Ir(r, e.count), a = Q(e.i18n), o = ut(e.i18nSource);
2688
2690
  return {
2689
2691
  id: String(e.id || `box-${t + 1}`),
2690
- title: String(e.title || Yr(t, n)),
2692
+ title: String(e.title || ni(t, n)),
2691
2693
  showTitle: e.showTitle !== !1,
2692
2694
  kind: r,
2693
- boardId: qr(e),
2695
+ boardId: ei(e),
2694
2696
  count: i,
2695
- postIds: Jr(e, i),
2696
- sort: Kr(e),
2697
- cols: Ur(e),
2698
- height: Wr(e),
2697
+ postIds: ti(e, i),
2698
+ sort: $r(e),
2699
+ cols: Xr(e),
2700
+ height: Zr(e),
2699
2701
  order: t,
2700
2702
  ...a ? { i18n: a } : {},
2701
2703
  ...o ? { i18nSource: o } : {}
2702
2704
  };
2703
2705
  }
2704
- function Zr() {
2705
- let { locale: e } = A(), { translateEntries: t } = Nn(), r = Z(), i = ye(r, "settings", "homeLayout"), { data: a, pending: o } = De(i), s = N(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : Hr).map((t, n) => Xr(t, n, e.value)));
2706
+ function ii() {
2707
+ let { locale: e } = A(), { translateEntries: t } = Bn(), r = Z(), i = R(r, "settings", "homeLayout"), { data: a, pending: o } = je(i), s = N(() => (Array.isArray(a.value?.blocks) ? a.value.blocks : Yr).map((t, n) => ri(t, n, e.value)));
2706
2708
  async function c(r) {
2707
2709
  if (!n()) return r;
2708
2710
  let i = e.value, a = r.filter((e) => {
2709
2711
  let t = e.title.trim();
2710
- return t ? !(e.i18nSource?.title === t && ot(e.i18n, ["title"])) : !1;
2712
+ return t ? !(e.i18nSource?.title === t && pt(e.i18n, ["title"])) : !1;
2711
2713
  });
2712
2714
  if (!a.length) return r;
2713
2715
  let o = await t(a.map((e) => ({
@@ -2721,8 +2723,8 @@ function Zr() {
2721
2723
  return r.map((e) => {
2722
2724
  let t = o[e.id];
2723
2725
  if (!t) return e;
2724
- let n = e.title.trim(), r = at(i, { title: n }, t);
2725
- return ot(r, ["title"]) ? {
2726
+ let n = e.title.trim(), r = ft(i, { title: n }, t);
2727
+ return pt(r, ["title"]) ? {
2726
2728
  ...e,
2727
2729
  title: n,
2728
2730
  i18n: r,
@@ -2734,18 +2736,18 @@ function Zr() {
2734
2736
  });
2735
2737
  }
2736
2738
  function l(t, n) {
2737
- let r = t, i = Gr(r), a = kr(i, t.count), o = t.title.trim() || Yr(n, e.value);
2739
+ let r = t, i = Qr(r), a = Ir(i, t.count), o = t.title.trim() || ni(n, e.value);
2738
2740
  return {
2739
2741
  id: t.id,
2740
2742
  title: o,
2741
2743
  showTitle: t.showTitle !== !1,
2742
2744
  kind: i,
2743
- boardId: qr(r),
2745
+ boardId: ei(r),
2744
2746
  count: a,
2745
- postIds: Jr(r, a),
2746
- sort: Kr(r),
2747
- cols: Ur(r),
2748
- height: Wr(r),
2747
+ postIds: ti(r, a),
2748
+ sort: $r(r),
2749
+ cols: Xr(r),
2750
+ height: Zr(r),
2749
2751
  order: n,
2750
2752
  ...t.i18n ? { i18n: t.i18n } : {},
2751
2753
  ...t.i18nSource?.title === o ? { i18nSource: t.i18nSource } : {}
@@ -2758,10 +2760,10 @@ function Zr() {
2758
2760
  } catch (e) {
2759
2761
  console.warn("[memi-board] section translation skipped", e);
2760
2762
  }
2761
- await q(i, {
2763
+ await De(i, {
2762
2764
  columns: 12,
2763
2765
  blocks: t.map((e, t) => l(e, t)),
2764
- updatedAt: K()
2766
+ updatedAt: q()
2765
2767
  });
2766
2768
  }
2767
2769
  return {
@@ -2776,19 +2778,19 @@ function Zr() {
2776
2778
  }
2777
2779
  //#endregion
2778
2780
  //#region src/composables/useMemiBoardMiniPosts.ts
2779
- function Qr(e, t) {
2781
+ function ai(e, t) {
2780
2782
  let n = Z(), r = () => j(), i = I(e)?.trim() || "all", a = N(() => {
2781
2783
  let i = Math.max(1, Math.floor(Number(I(t)) || 5)), a = I(e)?.trim() || "", s = [X("isPublished", "==", !0), X("listed", "==", !0)];
2782
- return a && s.unshift(X("boardId", "==", a)), G(o(n, r()), ...s, W("createdAt", "desc"), H(i));
2783
- }), { data: s, pending: c, error: l } = Te(a, { ssrKey: p(r(), `mini-list/${i}`) });
2784
+ return a && s.unshift(X("boardId", "==", a)), K(o(n, r()), ...s, G("createdAt", "desc"), U(i));
2785
+ }), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(r(), `mini-list/${i}`) });
2784
2786
  return {
2785
2787
  posts: N(() => s.value ?? []),
2786
2788
  pending: c,
2787
2789
  error: l
2788
2790
  };
2789
2791
  }
2790
- function $r(e) {
2791
- let t = Z(), n = () => j(), r = N(() => [...new Set(I(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = N(() => r.value.length ? G(o(t, n()), X(R(), "in", r.value)) : null), { data: s, pending: c, error: l } = Te(a, { ssrKey: p(n(), `mini-picked/${i}`) });
2792
+ function oi(e) {
2793
+ let t = Z(), n = () => j(), r = N(() => [...new Set(I(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = N(() => r.value.length ? K(o(t, n()), X(z(), "in", r.value)) : null), { data: s, pending: c, error: l } = ke(a, { ssrKey: p(n(), `mini-picked/${i}`) });
2792
2794
  return {
2793
2795
  posts: N(() => {
2794
2796
  let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
@@ -2804,7 +2806,22 @@ function $r(e) {
2804
2806
  }
2805
2807
  //#endregion
2806
2808
  //#region src/data/versionHistory.ts
2807
- var ei = [
2809
+ var si = [
2810
+ {
2811
+ version: "0.50.0",
2812
+ date: "2026-10-29",
2813
+ highlights: [
2814
+ "게시판 설정에서 고아 파일·댓글·좋아요를 조회하고 선택 삭제할 수 있어요",
2815
+ "최근 업로드를 보호하고, 삭제 전에 연결과 관리자 권한을 다시 확인해요",
2816
+ "파일 목록과 비공개 댓글 조회의 보안 규칙을 강화했어요"
2817
+ ],
2818
+ note: "호스트 프로젝트의 Firestore·Storage 규칙도 함께 갱신해야 합니다."
2819
+ },
2820
+ {
2821
+ version: "0.49.0",
2822
+ date: "2026-10-29",
2823
+ highlights: ["보드 설정에서 글 작성 시 자동 번역을 켤 수 있어요. 기본값은 꺼짐이에요", "새 글의 제목·요약·본문을 10개 언어로 번역해 함께 저장해요"]
2824
+ },
2808
2825
  {
2809
2826
  version: "0.48.1",
2810
2827
  date: "2026-10-29",
@@ -3149,7 +3166,7 @@ var ei = [
3149
3166
  highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
3150
3167
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
3151
3168
  }
3152
- ], ti = 10, ni = 864e5, ri = 200, ii = {
3169
+ ], ci = 10, li = 864e5, ui = 200, di = {
3153
3170
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
3154
3171
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
3155
3172
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -3158,7 +3175,7 @@ var ei = [
3158
3175
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
3159
3176
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
3160
3177
  };
3161
- function ai(e) {
3178
+ function fi(e) {
3162
3179
  return {
3163
3180
  proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
3164
3181
  polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
@@ -3169,27 +3186,27 @@ function ai(e) {
3169
3186
  title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
3170
3187
  };
3171
3188
  }
3172
- function oi(e, t, n) {
3173
- let r = t === "ko", i = mn(t);
3189
+ function pi(e, t, n) {
3190
+ let r = t === "ko", i = xn(t);
3174
3191
  if (e.action !== "custom") {
3175
- if (r) return ii[e.action];
3176
- let t = ai(i)[e.action];
3192
+ if (r) return di[e.action];
3193
+ let t = fi(i)[e.action];
3177
3194
  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}.`;
3178
3195
  }
3179
- let a = e.customInstruction?.trim().slice(0, ri);
3196
+ let a = e.customInstruction?.trim().slice(0, ui);
3180
3197
  if (!a) throw Error(n("writingAssistant.customRequired"));
3181
3198
  return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
3182
3199
  }
3183
- function si(e) {
3200
+ function mi(e) {
3184
3201
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
3185
3202
  }
3186
- function ci(e) {
3203
+ function hi(e) {
3187
3204
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
3188
3205
  }
3189
- function li(e = {}) {
3190
- let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = (e, t) => k(n.value, e, t), i = Oe(), a = Z(), o = Ee(), s = v(), { translateEntries: c, translateHtml: l } = Nn();
3206
+ function gi(e = {}) {
3207
+ let t = A(), n = N(() => M(I(e.locale)) ?? t.locale.value), r = (e, t) => k(n.value, e, t), i = Me(), a = Z(), o = Ae(), s = v(), { translateEntries: c, translateHtml: l } = Bn();
3191
3208
  function u(e, t) {
3192
- let n = ci(e.aiWritingWindowAt), r = n != null && t - n < ni, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
3209
+ let n = hi(e.aiWritingWindowAt), r = n != null && t - n < li, i = Math.max(0, Math.floor(Number(e.aiWritingCount) || 0)), a = Math.max(0, Math.floor(Number(e.aiWritingPending) || 0));
3193
3210
  return {
3194
3211
  active: r,
3195
3212
  windowAt: n,
@@ -3198,7 +3215,7 @@ function li(e = {}) {
3198
3215
  };
3199
3216
  }
3200
3217
  function d(e, t) {
3201
- let n = e + ni, i = Math.max(1, Math.ceil((n - t) / 36e5));
3218
+ let n = e + li, i = Math.max(1, Math.ceil((n - t) / 36e5));
3202
3219
  return Error(r("writingAssistant.dailyLimit", {
3203
3220
  limit: 10,
3204
3221
  hours: i
@@ -3207,13 +3224,13 @@ function li(e = {}) {
3207
3224
  async function f(e) {
3208
3225
  let t = o.value?.uid;
3209
3226
  if (!t) throw Error(r("writingAssistant.loginRequired"));
3210
- await Ce(a, async (n) => {
3227
+ await Ee(a, async (n) => {
3211
3228
  let i = h(a, j(), t), o = await n.get(i);
3212
3229
  if (!o.exists()) throw Error(r("writingAssistant.userMissing"));
3213
3230
  let s = o.data(), c = e(u(s, Date.now()), s);
3214
3231
  c && n.update(i, {
3215
3232
  ...c,
3216
- updatedAt: K()
3233
+ updatedAt: q()
3217
3234
  });
3218
3235
  });
3219
3236
  }
@@ -3223,7 +3240,7 @@ function li(e = {}) {
3223
3240
  return e.active ? { aiWritingPending: e.pending + 1 } : {
3224
3241
  aiWritingCount: 0,
3225
3242
  aiWritingPending: 1,
3226
- aiWritingWindowAt: K()
3243
+ aiWritingWindowAt: q()
3227
3244
  };
3228
3245
  });
3229
3246
  }
@@ -3250,13 +3267,13 @@ function li(e = {}) {
3250
3267
  }
3251
3268
  async function y(e) {
3252
3269
  (e.action === "translate-en" || e.action === "translate-ko") && D();
3253
- let t = n.value, a = t === "ko", o = oi(e, t, r);
3270
+ let t = n.value, a = t === "ko", o = pi(e, t, r);
3254
3271
  if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
3255
3272
  return _(async () => {
3256
- let t = s.moderation ?? {}, n = Ue(i, {
3257
- backend: new Ve(),
3273
+ let t = s.moderation ?? {}, n = Xe(i, {
3274
+ backend: new Je(),
3258
3275
  useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
3259
- }), c = We(n, {
3276
+ }), c = Ze(n, {
3260
3277
  model: t.model ?? "gemini-3.5-flash-lite",
3261
3278
  systemInstruction: (a ? [
3262
3279
  "너는 게시판 글쓰기 편집 도우미다.",
@@ -3279,7 +3296,7 @@ function li(e = {}) {
3279
3296
  `Task: ${o}`,
3280
3297
  e.title ? `Current title: ${e.title}` : "",
3281
3298
  `Body:\n${l.slice(0, 12e3)}`
3282
- ]).filter(Boolean).join("\n\n"), d = si((await c.generateContent(u)).response.text());
3299
+ ]).filter(Boolean).join("\n\n"), d = mi((await c.generateContent(u)).response.text());
3283
3300
  if (!d) throw Error(r("writingAssistant.emptyResult"));
3284
3301
  return d;
3285
3302
  });
@@ -3288,9 +3305,9 @@ function li(e = {}) {
3288
3305
  D();
3289
3306
  let t = e.source ?? n.value, i = e.title?.trim() ?? "", a = e.summary?.trim() ?? "", o = e.content ?? "", s = {};
3290
3307
  i && (s.title = i), a && (s.summary = a);
3291
- let u = !!Qt(o);
3308
+ let u = !!on(o);
3292
3309
  if (!Object.keys(s).length && !u) throw Error(r("writingAssistant.translateEmpty"));
3293
- if (u && o.length > 12e3) throw Error(r("writingAssistant.translateTooLong", { limit: On }));
3310
+ if (u && o.length > 12e3) throw Error(r("writingAssistant.translateTooLong", { limit: Fn }));
3294
3311
  return _(async () => {
3295
3312
  let [e, n] = await Promise.all([Object.keys(s).length ? c([{
3296
3313
  id: "post",
@@ -3299,8 +3316,8 @@ function li(e = {}) {
3299
3316
  }], {
3300
3317
  source: t,
3301
3318
  purpose: "message board post title and short summary"
3302
- }) : Promise.resolve({}), u ? l(o, { source: t }) : Promise.resolve({})]), d = at(t, s, e.post);
3303
- if (Object.keys(s).length && !ot(d, Object.keys(s))) throw Error(r("writingAssistant.translateIncomplete"));
3319
+ }) : Promise.resolve({}), u ? l(o, { source: t }) : Promise.resolve({})]), d = ft(t, s, e.post);
3320
+ if (Object.keys(s).length && !pt(d, Object.keys(s))) throw Error(r("writingAssistant.translateIncomplete"));
3304
3321
  let f = {
3305
3322
  i18n: d,
3306
3323
  i18nSource: {
@@ -3315,11 +3332,11 @@ function li(e = {}) {
3315
3332
  let n = M(t);
3316
3333
  n && typeof r == "string" && r.trim() && (e[n] = { content: r.trim() });
3317
3334
  }
3318
- let i = at(t, { content: o }, e);
3319
- if (!ot(i, ["content"])) throw Error(r("writingAssistant.translateIncomplete"));
3335
+ let i = ft(t, { content: o }, e);
3336
+ if (!pt(i, ["content"])) throw Error(r("writingAssistant.translateIncomplete"));
3320
3337
  f.contentI18n = i, f.contentI18nSource = {
3321
3338
  locale: t,
3322
- hash: tt(o)
3339
+ hash: ct(o)
3323
3340
  };
3324
3341
  }
3325
3342
  return f;
@@ -3332,14 +3349,14 @@ function li(e = {}) {
3332
3349
  }
3333
3350
  //#endregion
3334
3351
  //#region src/utils/renderMarkdown.ts
3335
- function ui(e) {
3352
+ function _i(e) {
3336
3353
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3337
3354
  }
3338
- function di(e) {
3355
+ function vi(e) {
3339
3356
  let t = e;
3340
3357
  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;
3341
3358
  }
3342
- function fi(e) {
3359
+ function yi(e) {
3343
3360
  if (!e?.trim()) return "";
3344
3361
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3345
3362
  function c() {
@@ -3348,7 +3365,7 @@ function fi(e) {
3348
3365
  return;
3349
3366
  }
3350
3367
  let e = o;
3351
- n.push(`<${e}>${s.map((e) => `<li>${di(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3368
+ n.push(`<${e}>${s.map((e) => `<li>${vi(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3352
3369
  }
3353
3370
  for (; r < t.length;) {
3354
3371
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3357,7 +3374,7 @@ function fi(e) {
3357
3374
  continue;
3358
3375
  }
3359
3376
  if (i) {
3360
- a.push(ui(e)), r++;
3377
+ a.push(_i(e)), r++;
3361
3378
  continue;
3362
3379
  }
3363
3380
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3368,24 +3385,24 @@ function fi(e) {
3368
3385
  if (u) {
3369
3386
  c();
3370
3387
  let e = u[1].length;
3371
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${di(ui(u[2]))}</h${e}>`), r++;
3388
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${vi(_i(u[2]))}</h${e}>`), r++;
3372
3389
  continue;
3373
3390
  }
3374
3391
  if (e.startsWith("> ")) {
3375
3392
  c();
3376
3393
  let e = [];
3377
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(ui((t[r] ?? "").slice(2))), r++;
3378
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${di(e.join("<br />"))}</blockquote>`);
3394
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(_i((t[r] ?? "").slice(2))), r++;
3395
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${vi(e.join("<br />"))}</blockquote>`);
3379
3396
  continue;
3380
3397
  }
3381
3398
  let d = e.match(/^[-*+]\s+(.+)$/);
3382
3399
  if (d) {
3383
- o && o !== "ul" && c(), o = "ul", s.push(ui(d[1])), r++;
3400
+ o && o !== "ul" && c(), o = "ul", s.push(_i(d[1])), r++;
3384
3401
  continue;
3385
3402
  }
3386
3403
  let f = e.match(/^\d+\.\s+(.+)$/);
3387
3404
  if (f) {
3388
- o && o !== "ol" && c(), o = "ol", s.push(ui(f[1])), r++;
3405
+ o && o !== "ol" && c(), o = "ol", s.push(_i(f[1])), r++;
3389
3406
  continue;
3390
3407
  }
3391
3408
  if (!e.trim()) {
@@ -3397,13 +3414,194 @@ function fi(e) {
3397
3414
  for (; r < t.length;) {
3398
3415
  let e = t[r] ?? "";
3399
3416
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3400
- p.push(ui(e)), r++;
3417
+ p.push(_i(e)), r++;
3401
3418
  }
3402
- n.push(`<p class="my-2 leading-relaxed">${di(p.join("<br />"))}</p>`);
3419
+ n.push(`<p class="my-2 leading-relaxed">${vi(p.join("<br />"))}</p>`);
3403
3420
  }
3404
3421
  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");
3405
3422
  }
3406
3423
  //#endregion
3407
- export { Qn as AUTHOR_MEMO_MAX_LENGTH, Cr as BOARD_SECTION_COLS, Er as BOARD_SECTION_KINDS, Dr as BOARD_SECTION_SORTS, br as BOARD_USER_ROLES, ir as COMMENT_BODY_MAX_LENGTH, Et as DEFAULT_BLOCK_BAN_DECAY_MS, Tt as DEFAULT_BLOCK_BAN_THRESHOLD, Pn as DEFAULT_BOARDS, Fn as DEFAULT_CATEGORIES, i as DEFAULT_COMMENTS_COLLECTION, r as DEFAULT_LIKES_COLLECTION, re as DEFAULT_MEMI_BOARD_LOCALE, m as DEFAULT_POSTS_COLLECTION, ae as DEFAULT_REPORTS_COLLECTION, e as DEFAULT_SETTINGS_COLLECTION, ie as DEFAULT_USERS_COLLECTION, O as FALLBACK_MEMI_BOARD_LOCALE, On as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, dr as REPORT_DETAIL_MAX_LENGTH, fr as REPORT_REASONS, ti as WRITING_ASSISTANT_DAILY_LIMIT, Ke as asHttpUrl, ft as boardCopyIsTranslated, Qe as boardListOgDescription, Ze as boardListOgTitle, Xe as boardPostOgDescription, Ye as boardPostOgTitle, wr as boardSectionKinds, Tr as boardSectionSorts, p as boardSsrKey, h as boardUserDoc, c as boardUsersCol, sn as buildPostPreview, bt as canManageBoardByRole, xt as canWriteCommentByRole, kr as clampSectionCount, d as commentDoc, g as commentsCol, me as configureMemiBoard, ot as coversLocales, Or as defaultSectionCount, dt as displayPostContent, ut as displayPostSummary, lt as displayPostTitle, ct as displayStoredField, Dt as effectiveModerationBlockCount, Bt as extractEditorImageUrls, _t as fetchPublicListForSeo, gt as fetchPublicPostForSeo, ee as formatDate, oe as formatFullDate, fe as formatRelativeDate, At as formatRestrictedUntilLabel, te as formatTimestampDetails, tn as hasBodyImage, en as hasBodyText, tt as hashText, Rt as imeSafeSubmitClick, Lt as imeSafeSubmitPointerDown, yt as isAssignedStaff, nn as isContentEmpty, Bn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, ne as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, kt as isModerationWriteRestricted, he as likeDoc, t as likeDocId, b as likesCol, ce as loadMemiBoardLocale, st as localizedText, lr as memiBoardCommentLikesKey, se as memiBoardHostLocaleKey, pe as memiBoardLocaleInfo, at as mergeSourceFields, Br as miniBoardView, Fr as miniCommentLink, Pr as miniMoreLink, Ir as miniPostImage, Nr as miniPostLink, Lr as miniPostTitle, Vr as miniPostsView, Rr as miniViewGroup, zr as miniViewLabel, Ot as moderationWriteRestrictedUntilMs, $e as normalizeBasePath, Xr as normalizeBoardSection, M as normalizeMemiBoardLocale, Ar as pairSectionCols, Qt as plainTextFromHtml, s as postBodyDoc, y as postDoc, Ht as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, rt as readI18nSource, Q as readLocalizedFields, fi as renderMarkdownToHtml, u as reportDoc, le as reportDocId, pr as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, pt as resolvePublicSeoDb, jr as sectionColClass, Mr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Vt as storagePathFromDownloadUrl, $t as titleFromBody, Je as toAbsoluteUrl, qe as toBoardOgImageUrl, k as translate, j as useBoardPathConfig, Pt as useMemiBoardAuth, er as useMemiBoardAuthorMemo, Nn as useMemiBoardAutoTranslate, Zn as useMemiBoardCommentFeed, ur as useMemiBoardCommentLikes, or as useMemiBoardComments, v as useMemiBoardConfig, A as useMemiBoardI18n, cr as useMemiBoardLikes, Qr as useMemiBoardMiniLatest, $r as useMemiBoardMiniPicked, yr as useMemiBoardModeration, Gn as useMemiBoardPost, Wn as useMemiBoardPostList, Hn as useMemiBoardPosts, sr as useMemiBoardReplies, gr as useMemiBoardReportQueue, mr as useMemiBoardReports, Zr as useMemiBoardSections, Rn as useMemiBoardSettings, Yn as useMemiBoardUserComments, qn as useMemiBoardUserPosts, Sr as useMemiBoardUserProfile, xr as useMemiBoardUsers, _r as useMemiBoardViews, li as useMemiBoardWritingAssistant, ei as versionHistory, on as videoListCoverUrl, an as youtubeId };
3424
+ //#region src/utils/orphanCleanup.ts
3425
+ function bi(e, t, n, r) {
3426
+ 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);
3427
+ }
3428
+ function xi(e, t) {
3429
+ let n = Date.parse(e);
3430
+ return Number.isFinite(n) && t - n >= 864e5;
3431
+ }
3432
+ function Si(e, t) {
3433
+ if (!e.startsWith(`${t}/`)) return null;
3434
+ let n = e.slice(t.length + 1).split("/");
3435
+ if (n.some((e) => !e || e === "." || e === "..")) return null;
3436
+ let [r, i, a] = n;
3437
+ return i === "attachments" && n.length === 3 || i === "images" && (n.length === 3 || n.length === 4 && a === "thumbnails") ? { postId: r } : i === "comment-images" && n.length === 4 ? {
3438
+ postId: r,
3439
+ commentId: a
3440
+ } : null;
3441
+ }
3442
+ function Ci(e, t, n) {
3443
+ let r = `${t === "comment" ? n.commentsCollection : n.likesCollection}/`, i = e.startsWith(r) ? e.slice(r.length) : "";
3444
+ return !!i && !i.includes("/") && i !== "." && i !== "..";
3445
+ }
3446
+ //#endregion
3447
+ //#region src/composables/useMemiBoardCleanup.ts
3448
+ function wi() {
3449
+ let e = Z(), t = Me(), { isAdmin: n, rolePending: r } = Vt(), { t: i } = A(), a = F([]), o = F(!1), s = F(!1), c = F(""), l = F(""), u = [], d = "", f = /* @__PURE__ */ new Map();
3450
+ function p() {
3451
+ let e = Le(t).currentUser?.uid;
3452
+ if (!e) throw Error(i("settings.noAccess"));
3453
+ return JSON.stringify([e, j()]);
3454
+ }
3455
+ function m(e) {
3456
+ if (h(), p() !== e) throw Error(i("settings.cleanup.contextChanged"));
3457
+ }
3458
+ function h() {
3459
+ if (!n.value || r.value || !Le(t).currentUser) throw Error(i("settings.noAccess"));
3460
+ }
3461
+ async function g() {
3462
+ let t = j(), [n, r, i] = await Promise.all([
3463
+ we(ve(e, t.postsCollection)),
3464
+ we(ve(e, t.commentsCollection)),
3465
+ we(ve(e, t.likesCollection))
3466
+ ]), a = /* @__PURE__ */ new Set();
3467
+ function o(e) {
3468
+ if (typeof e == "string") {
3469
+ e.startsWith(`${t.postsCollection}/`) && a.add(e);
3470
+ let n = Jt(e);
3471
+ n && a.add(n);
3472
+ for (let t of e.matchAll(/https?:\/\/[^\s"'<>]+/g)) {
3473
+ let e = Jt(t[0].replace(/&amp;/g, "&"));
3474
+ e && a.add(e);
3475
+ }
3476
+ } else Array.isArray(e) ? e.forEach(o) : e && typeof e == "object" && Object.getPrototypeOf(e) === Object.prototype && Object.values(e).forEach(o);
3477
+ }
3478
+ n.docs.forEach((e) => o(e.data())), r.docs.forEach((e) => o(e.data()));
3479
+ for (let e of n.docs) (await we(ve(e.ref, "body"))).docs.forEach((e) => o(e.data()));
3480
+ for (let e of [...a]) {
3481
+ let t = e.match(/^(.*\/images\/)([^/]+)\.[^/.]+$/);
3482
+ t && a.add(`${t[1]}thumbnails/${t[2]}.jpg`);
3483
+ }
3484
+ return {
3485
+ cfg: t,
3486
+ posts: new Set(n.docs.map((e) => e.id)),
3487
+ comments: new Set(r.docs.map((e) => e.id)),
3488
+ commentDocs: r.docs,
3489
+ likes: i.docs,
3490
+ referenced: a
3491
+ };
3492
+ }
3493
+ async function _() {
3494
+ if (!o.value) {
3495
+ o.value = !0, s.value = !1, a.value = [], u = [], d = "", f.clear(), c.value = "", l.value = "";
3496
+ try {
3497
+ h();
3498
+ let e = p(), n = await g();
3499
+ m(e);
3500
+ let r = [];
3501
+ for (let [e, t] of [["comment", n.commentDocs], ["like", n.likes]]) for (let i of t) {
3502
+ let t = i.data();
3503
+ bi(e, t, n.posts, n.comments) && r.push({
3504
+ kind: e,
3505
+ path: i.ref.path,
3506
+ postId: t.postId,
3507
+ commentId: t.commentId
3508
+ });
3509
+ }
3510
+ let i = We(t), o = [qe(i, n.cfg.postsCollection)];
3511
+ for (; o.length;) {
3512
+ m(e);
3513
+ let t = o.pop(), i;
3514
+ do {
3515
+ let e = await Ge(t, {
3516
+ maxResults: 1e3,
3517
+ pageToken: i
3518
+ });
3519
+ o.push(...e.prefixes);
3520
+ for (let t of e.items) {
3521
+ let e = Si(t.fullPath, n.cfg.postsCollection);
3522
+ if (!e) continue;
3523
+ let { postId: i, commentId: a } = e;
3524
+ if (n.referenced.has(t.fullPath) || n.posts.has(i) && (!a || n.comments.has(a))) continue;
3525
+ let o = await Ue(t);
3526
+ xi(o.updated, Date.now()) && (f.set(t.fullPath, o.generation), r.push({
3527
+ kind: "file",
3528
+ path: t.fullPath,
3529
+ postId: i,
3530
+ commentId: a,
3531
+ size: o.size
3532
+ }));
3533
+ }
3534
+ i = e.nextPageToken;
3535
+ } while (i);
3536
+ }
3537
+ m(e), u = r.map((e) => Object.freeze({ ...e })), d = e, a.value = r, s.value = !0;
3538
+ } catch (e) {
3539
+ c.value = `${i("settings.cleanup.failed")} ${e instanceof Error ? e.message : String(e)}`;
3540
+ } finally {
3541
+ o.value = !1;
3542
+ }
3543
+ }
3544
+ }
3545
+ async function v(n) {
3546
+ if (o.value || !s.value) return;
3547
+ o.value = !0, c.value = "", l.value = "";
3548
+ let r = 0, p = 0, h = 0;
3549
+ try {
3550
+ m(d);
3551
+ let o = await g();
3552
+ m(d);
3553
+ let s = new Set(n), c = new Set(a.value.map((e) => e.path));
3554
+ for (let n of u) if (s.has(n.path) && c.has(n.path)) {
3555
+ m(d);
3556
+ try {
3557
+ let i = !1;
3558
+ if (n.kind === "file") {
3559
+ if (!Si(n.path, o.cfg.postsCollection)) throw Error("Invalid cleanup path");
3560
+ let r = We(t), a = qe(r, n.path), [s, c, l] = await Promise.all([
3561
+ Ce(R(e, o.cfg.postsCollection, n.postId)),
3562
+ n.commentId ? Ce(R(e, o.cfg.commentsCollection, n.commentId)) : null,
3563
+ Ue(a)
3564
+ ]);
3565
+ m(d), l.generation === f.get(n.path) && !o.referenced.has(n.path) && xi(l.updated, Date.now()) && (!s.exists() || n.commentId && !c?.exists()) && (await He(a), i = !0);
3566
+ } else {
3567
+ let t = n.kind;
3568
+ if (!Ci(n.path, t, o.cfg)) throw Error("Invalid cleanup path");
3569
+ i = await Ee(e, async (r) => {
3570
+ let i = await r.get(R(e, n.path));
3571
+ if (!i.exists()) return !0;
3572
+ let a = i.data();
3573
+ if (typeof a.postId != "string" || !a.postId || a.postId.includes("/")) return !1;
3574
+ let s = await r.get(R(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(R(e, o.cfg.commentsCollection, a.commentId)) : null;
3575
+ return bi(t, a, new Set(s.exists() ? [a.postId] : []), new Set(c?.exists() ? [a.commentId] : [])) ? (m(d), r.delete(i.ref), !0) : !1;
3576
+ });
3577
+ }
3578
+ i ? r++ : p++, a.value = a.value.filter((e) => e.path !== n.path);
3579
+ } catch {
3580
+ h++;
3581
+ }
3582
+ }
3583
+ l.value = i("settings.cleanup.result", {
3584
+ deleted: r,
3585
+ skipped: p,
3586
+ failed: h
3587
+ });
3588
+ } catch (e) {
3589
+ c.value = `${i("settings.cleanup.failed")} ${e instanceof Error ? e.message : String(e)}`;
3590
+ } finally {
3591
+ o.value = !1;
3592
+ }
3593
+ }
3594
+ return {
3595
+ items: _e(a),
3596
+ busy: _e(o),
3597
+ scanned: _e(s),
3598
+ error: _e(c),
3599
+ result: _e(l),
3600
+ scan: _,
3601
+ remove: v
3602
+ };
3603
+ }
3604
+ //#endregion
3605
+ export { ar as AUTHOR_MEMO_MAX_LENGTH, Ar as BOARD_SECTION_COLS, Nr as BOARD_SECTION_KINDS, Pr as BOARD_SECTION_SORTS, Dr as BOARD_USER_ROLES, dr as COMMENT_BODY_MAX_LENGTH, Nt as DEFAULT_BLOCK_BAN_DECAY_MS, Mt as DEFAULT_BLOCK_BAN_THRESHOLD, Vn as DEFAULT_BOARDS, Hn as DEFAULT_CATEGORIES, i as DEFAULT_COMMENTS_COLLECTION, r as DEFAULT_LIKES_COLLECTION, re as DEFAULT_MEMI_BOARD_LOCALE, m as DEFAULT_POSTS_COLLECTION, ae as DEFAULT_REPORTS_COLLECTION, e as DEFAULT_SETTINGS_COLLECTION, ie as DEFAULT_USERS_COLLECTION, O as FALLBACK_MEMI_BOARD_LOCALE, Fn as HTML_MAX_CHARS, T as MEMI_BOARD_LOCALES, vr as REPORT_DETAIL_MAX_LENGTH, yr as REPORT_REASONS, ci as WRITING_ASSISTANT_DAILY_LIMIT, $e as asHttpUrl, yt as boardCopyIsTranslated, at as boardListOgDescription, it as boardListOgTitle, rt as boardPostOgDescription, nt as boardPostOgTitle, jr as boardSectionKinds, Mr as boardSectionSorts, p as boardSsrKey, h as boardUserDoc, c as boardUsersCol, mn as buildPostPreview, Dt as canManageBoardByRole, Ot as canWriteCommentByRole, Ir as clampSectionCount, d as commentDoc, g as commentsCol, me as configureMemiBoard, pt as coversLocales, Fr as defaultSectionCount, vt as displayPostContent, _t as displayPostSummary, gt as displayPostTitle, ht as displayStoredField, Pt as effectiveModerationBlockCount, qt as extractEditorImageUrls, wt as fetchPublicListForSeo, Ct as fetchPublicPostForSeo, ee as formatDate, oe as formatFullDate, fe as formatRelativeDate, Lt as formatRestrictedUntilLabel, te as formatTimestampDetails, ln as hasBodyImage, cn as hasBodyText, ct as hashText, Gt as imeSafeSubmitClick, Wt as imeSafeSubmitPointerDown, Et as isAssignedStaff, un as isContentEmpty, qn as isFirestorePermissionDenied, _ as isMemiBoardI18nEnabled, ne as isMemiBoardLocaleLoaded, n as isMemiBoardTranslationEnabled, It as isModerationWriteRestricted, he as likeDoc, t as likeDocId, b as likesCol, ce as loadMemiBoardLocale, mt as localizedText, gr as memiBoardCommentLikesKey, se as memiBoardHostLocaleKey, pe as memiBoardLocaleInfo, ft as mergeSourceFields, qr as miniBoardView, Hr as miniCommentLink, Vr as miniMoreLink, Ur as miniPostImage, Br as miniPostLink, Wr as miniPostTitle, Jr as miniPostsView, Gr as miniViewGroup, Kr as miniViewLabel, Ft as moderationWriteRestrictedUntilMs, ot as normalizeBasePath, ri as normalizeBoardSection, M as normalizeMemiBoardLocale, Lr as pairSectionCols, on as plainTextFromHtml, s as postBodyDoc, y as postDoc, Yt as postNamespaceFromStoragePath, x as postStorageFolder, o as postsCol, ut as readI18nSource, Q as readLocalizedFields, yi as renderMarkdownToHtml, u as reportDoc, le as reportDocId, br as reportReasonLabel, l as reportsCol, C as resolveBoardPathConfig, bt as resolvePublicSeoDb, Rr as sectionColClass, zr as sectionMinHeight, S as settingsCol, w as settingsDoc, E as slugify, Jt as storagePathFromDownloadUrl, sn as titleFromBody, tt as toAbsoluteUrl, et as toBoardOgImageUrl, k as translate, j as useBoardPathConfig, Vt as useMemiBoardAuth, sr as useMemiBoardAuthorMemo, Bn as useMemiBoardAutoTranslate, wi as useMemiBoardCleanup, ir as useMemiBoardCommentFeed, _r as useMemiBoardCommentLikes, pr as useMemiBoardComments, v as useMemiBoardConfig, A as useMemiBoardI18n, hr as useMemiBoardLikes, ai as useMemiBoardMiniLatest, oi as useMemiBoardMiniPicked, Er as useMemiBoardModeration, Qn as useMemiBoardPost, Zn as useMemiBoardPostList, Yn as useMemiBoardPosts, mr as useMemiBoardReplies, Cr as useMemiBoardReportQueue, xr as useMemiBoardReports, ii as useMemiBoardSections, Gn as useMemiBoardSettings, nr as useMemiBoardUserComments, er as useMemiBoardUserPosts, kr as useMemiBoardUserProfile, Or as useMemiBoardUsers, wr as useMemiBoardViews, gi as useMemiBoardWritingAssistant, si as versionHistory, pn as videoListCoverUrl, fn as youtubeId };
3408
3606
 
3409
3607
  //# sourceMappingURL=index.js.map