memi-board 0.45.0 → 0.45.1

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, L as l, M as u, N as d, O as f, P as p, R as m, S as h, T as g, U as _, V as v, W as y, _ as b, a as x, b as S, c as C, d as w, f as T, g as E, h as D, i as O, j as k, k as ee, l as te, m as ne, n as re, o as ie, p as ae, r as oe, s as se, u as ce, v as A, w as le, x as ue, y as de, z as j } from "./slugify-6bG6GSO_.js";
2
- import { computed as M, onScopeDispose as N, reactive as fe, ref as P, toValue as F, watch as I } from "vue";
3
- import { deleteDoc as pe, deleteField as me, doc as he, documentId as L, endBefore as ge, getDoc as R, getDocs as z, increment as B, limit as V, limitToLast as _e, onSnapshot as H, orderBy as U, query as W, runTransaction as ve, serverTimestamp as G, setDoc as K, startAfter as q, updateDoc as J, where as Y, writeBatch as X } from "firebase/firestore";
4
- import { useCollection as ye, useCurrentUser as be, useDocument as xe, useFirebaseApp as Se, useFirebaseAuth as Ce, useFirestore as Z } from "vuefire";
5
- import { GoogleAuthProvider as we, OAuthProvider as Te, createUserWithEmailAndPassword as Ee, signInWithEmailAndPassword as De, signInWithPopup as Oe, signOut as ke, updateProfile as Ae } from "firebase/auth";
6
- import { deleteObject as je, getStorage as Me, listAll as Ne, ref as Pe } from "firebase/storage";
7
- import { GoogleAIBackend as Fe, Schema as Q, getAI as Ie, getGenerativeModel as Le } 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, L as l, M as u, N as d, O as f, P as p, R as m, S as h, T as g, U as _, V as v, W as y, _ as b, a as x, b as S, c as C, d as w, f as T, g as E, h as D, i as O, j as k, k as ee, l as te, m as A, n as ne, o as re, p as ie, r as ae, s as oe, u as se, v as j, w as ce, x as le, y as ue, z as M } from "./slugify-BuOMFLxN.js";
2
+ import { computed as N, onScopeDispose as P, reactive as de, ref as F, toValue as I, watch as L } from "vue";
3
+ import { deleteDoc as fe, deleteField as pe, doc as me, documentId as R, endAt as he, endBefore as ge, getDoc as z, getDocs as B, increment as V, limit as H, limitToLast as _e, onSnapshot as U, orderBy as W, query as G, runTransaction as ve, serverTimestamp as K, setDoc as q, startAfter as J, updateDoc as Y, where as X, writeBatch as ye } from "firebase/firestore";
4
+ import { useCollection as be, useCurrentUser as xe, useDocument as Se, useFirebaseApp as Ce, useFirebaseAuth as we, useFirestore as Z } from "vuefire";
5
+ import { GoogleAuthProvider as Te, OAuthProvider as Ee, createUserWithEmailAndPassword as De, signInWithEmailAndPassword as Oe, signInWithPopup as ke, signOut as Ae, updateProfile as je } from "firebase/auth";
6
+ import { deleteObject as Me, getStorage as Ne, listAll as Pe, ref as Fe } from "firebase/storage";
7
+ import { GoogleAIBackend as Ie, Schema as Q, getAI as Le, getGenerativeModel as Re } from "firebase/ai";
8
8
  //#region src/utils/boardSeo.ts
9
- function Re(e, t) {
9
+ function ze(e, t) {
10
10
  return !e || e === "전체" || e === w(t, "seo.allCategory");
11
11
  }
12
- function ze(e) {
12
+ function Be(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 Be(e) {
18
- let t = ze(e);
17
+ function Ve(e) {
18
+ let t = Be(e);
19
19
  if (!t) return null;
20
20
  if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
21
21
  try {
@@ -32,45 +32,45 @@ function Be(e) {
32
32
  return t;
33
33
  }
34
34
  }
35
- function Ve(e, t, n = "") {
35
+ function He(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 ? Ve(n, t) : r;
37
+ return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? He(n, t) : r;
38
38
  }
39
- function He(e, t, n, r, i) {
39
+ function Ue(e, t, n, r, i) {
40
40
  let a = r?.trim().slice(0, 40), o = e.trim() || a || w(i, "seo.photo"), s = t?.trim(), c = n.trim() || "Board";
41
41
  return s ? `${o} · ${s} | ${c}` : `${o} | ${c}`;
42
42
  }
43
- function Ue(e, t) {
43
+ function We(e, t) {
44
44
  let n = [];
45
45
  return e.summary?.trim() ? n.push(e.summary.trim().slice(0, 140)) : n.push(w(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 We(e, t, n) {
47
+ function Ge(e, t, n) {
48
48
  let r = t.trim() || "Board", i = e?.trim();
49
- return Re(i, n) ? w(n, "seo.listTitleAll", { site: r }) : w(n, "seo.listTitle", {
49
+ return ze(i, n) ? w(n, "seo.listTitleAll", { site: r }) : w(n, "seo.listTitle", {
50
50
  name: i,
51
51
  site: r
52
52
  });
53
53
  }
54
- function Ge(e, t) {
54
+ function Ke(e, t) {
55
55
  if (e.description?.trim()) return e.description.trim().slice(0, 200);
56
- let n = e.categoryLabel?.trim(), r = Re(n, t) ? w(t, "seo.listDescriptionAll") : w(t, "seo.listDescription", { name: n }), i = (e.recentTitles || []).filter(Boolean).slice(0, 3);
56
+ let n = e.categoryLabel?.trim(), r = ze(n, t) ? w(t, "seo.listDescriptionAll") : w(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 Ke(e) {
59
+ function qe(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/composables/fetchPublicSeo.ts
65
- function qe() {
65
+ function Je() {
66
66
  return {
67
67
  db: Z(),
68
- paths: A()
68
+ paths: j()
69
69
  };
70
70
  }
71
- async function Je(e, t) {
71
+ async function Ye(e, t) {
72
72
  try {
73
- let n = await R(o(e.db, e.paths, t));
73
+ let n = await z(o(e.db, e.paths, t));
74
74
  if (!n.exists()) return {
75
75
  label: t,
76
76
  description: "",
@@ -90,7 +90,7 @@ async function Je(e, t) {
90
90
  };
91
91
  }
92
92
  }
93
- function Ye(e) {
93
+ function Xe(e) {
94
94
  if (e && typeof e == "object" && "toDate" in e) try {
95
95
  let t = e.toDate();
96
96
  return t instanceof Date ? t.toISOString() : null;
@@ -99,15 +99,15 @@ function Ye(e) {
99
99
  }
100
100
  return null;
101
101
  }
102
- async function Xe(e, t, n) {
102
+ async function Ze(e, t, n) {
103
103
  let r = e.trim(), i = t.trim();
104
104
  if (!r || !i) return null;
105
- let a = n ?? qe();
105
+ let a = n ?? Je();
106
106
  a.paths = _(a.paths);
107
- let o = await Je(a, r);
107
+ let o = await Ye(a, r);
108
108
  if (o.hidden) return null;
109
109
  try {
110
- let e = (await z(W(j(a.db, a.paths), Y("boardId", "==", r), Y("slug", "==", i), Y("listed", "==", !0), Y("isPublished", "==", !0), V(1)))).docs[0];
110
+ let e = (await B(G(M(a.db, a.paths), X("boardId", "==", r), X("slug", "==", i), X("listed", "==", !0), X("isPublished", "==", !0), H(1)))).docs[0];
111
111
  if (!e) return null;
112
112
  let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title.trim() : "";
113
113
  return n ? {
@@ -117,19 +117,19 @@ async function Xe(e, t, n) {
117
117
  categoryLabel: o.label,
118
118
  title: n,
119
119
  summary: typeof t.summary == "string" ? t.summary.trim().slice(0, 200) : "",
120
- previewImage: Be(t.previewImage),
120
+ previewImage: Ve(t.previewImage),
121
121
  authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
122
- createdAt: Ye(t.createdAt)
122
+ createdAt: Xe(t.createdAt)
123
123
  } : null;
124
124
  } catch (e) {
125
125
  return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
126
126
  }
127
127
  }
128
- async function Ze(e, t, n) {
129
- n && await ce(n);
130
- let r = e?.trim() || null, i = t ?? qe();
128
+ async function Qe(e, t, n) {
129
+ n && await se(n);
130
+ let r = e?.trim() || null, i = t ?? Je();
131
131
  if (i.paths = _(i.paths), r) {
132
- let e = await Je(i, r);
132
+ let e = await Ye(i, r);
133
133
  if (e.hidden) return {
134
134
  kind: "category",
135
135
  category: r,
@@ -138,7 +138,7 @@ async function Ze(e, t, n) {
138
138
  ogImage: null,
139
139
  recentTitles: []
140
140
  };
141
- let { recentTitles: t, ogImage: a } = await Qe(i, r);
141
+ let { recentTitles: t, ogImage: a } = await $e(i, r);
142
142
  return {
143
143
  kind: "category",
144
144
  category: r,
@@ -157,13 +157,13 @@ async function Ze(e, t, n) {
157
157
  recentTitles: []
158
158
  };
159
159
  }
160
- async function Qe(e, t) {
160
+ async function $e(e, t) {
161
161
  let n = [], r = null;
162
162
  try {
163
- let i = await z(W(j(e.db, e.paths), Y("boardId", "==", t), Y("listed", "==", !0), Y("isPublished", "==", !0), U("createdAt", "desc"), V(12)));
163
+ let i = await B(G(M(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), W("createdAt", "desc"), H(12)));
164
164
  for (let e of i.docs) {
165
165
  let t = e.data() ?? {}, i = typeof t.title == "string" ? t.title.trim() : "";
166
- i && n.length < 5 && n.push(i), r ||= Be(t.previewImage);
166
+ i && n.length < 5 && n.push(i), r ||= Ve(t.previewImage);
167
167
  }
168
168
  } catch (e) {
169
169
  import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
@@ -175,31 +175,31 @@ async function Qe(e, t) {
175
175
  }
176
176
  //#endregion
177
177
  //#region src/utils/boardAccess.ts
178
- function $e(e, t) {
178
+ function et(e, t) {
179
179
  return !e || !t ? !1 : (t.allowedStaffUids ?? []).includes(e);
180
180
  }
181
- function et(e, t, n, r) {
182
- return e ? t ? !0 : n ? $e(e, r) : !1 : !1;
183
- }
184
181
  function tt(e, t, n, r) {
182
+ return e ? t ? !0 : n ? et(e, r) : !1 : !1;
183
+ }
184
+ function nt(e, t, n, r) {
185
185
  if (!e) return !1;
186
- if (r?.visibility === "hidden") return et(e, t, n, r);
186
+ if (r?.visibility === "hidden") return tt(e, t, n, r);
187
187
  let i = r?.commentWriteRole ?? "user";
188
- return i === "admin" ? t : i !== "staff" || et(e, t, n, r);
188
+ return i === "admin" ? t : i !== "staff" || tt(e, t, n, r);
189
189
  }
190
190
  //#endregion
191
191
  //#region src/composables/boardLookup.ts
192
- var nt;
193
- function rt(e) {
194
- nt = e;
195
- }
192
+ var rt;
196
193
  function it(e) {
197
- return nt?.(e);
194
+ rt = e;
195
+ }
196
+ function at(e) {
197
+ return rt?.(e);
198
198
  }
199
199
  //#endregion
200
200
  //#region src/utils/moderation-strike.ts
201
- var at = 10, ot = 864e5;
202
- function st(e, t, n = Date.now(), r = ot) {
201
+ var ot = 10, st = 864e5;
202
+ function ct(e, t, n = Date.now(), r = st) {
203
203
  let i = Math.max(0, Math.floor(Number(e) || 0));
204
204
  if (i <= 0) return 0;
205
205
  let a = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -207,7 +207,7 @@ function st(e, t, n = Date.now(), r = ot) {
207
207
  let o = Math.floor((n - a) / r);
208
208
  return Math.max(0, i - o);
209
209
  }
210
- function ct(e, t, n = Date.now(), r = 10, i = ot) {
210
+ function lt(e, t, n = Date.now(), r = 10, i = st) {
211
211
  let a = Math.max(0, Math.floor(Number(e) || 0));
212
212
  if (a < r) return null;
213
213
  let o = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -215,10 +215,10 @@ function ct(e, t, n = Date.now(), r = 10, i = ot) {
215
215
  let s = o + (a - (r - 1)) * i;
216
216
  return s > n ? s : null;
217
217
  }
218
- function lt(e, t, n = Date.now(), r = 10, i = ot) {
219
- return ct(e, t, n, r, i) != null;
218
+ function ut(e, t, n = Date.now(), r = 10, i = st) {
219
+ return lt(e, t, n, r, i) != null;
220
220
  }
221
- function ut(e, t = Date.now(), n) {
221
+ function dt(e, t = Date.now(), n) {
222
222
  let r = Math.max(0, e - t), i = Math.ceil(r / 36e5);
223
223
  if (i <= 0) return w(n, "moderationStrike.liftSoon");
224
224
  if (i < 24) return w(n, "moderationStrike.liftInHours", { hours: i });
@@ -228,7 +228,7 @@ function ut(e, t = Date.now(), n) {
228
228
  hours: o
229
229
  });
230
230
  }
231
- function dt(e) {
231
+ function ft(e) {
232
232
  if (e == null) return null;
233
233
  if (typeof e == "number" && Number.isFinite(e)) return e;
234
234
  if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
@@ -240,54 +240,54 @@ function dt(e) {
240
240
  }
241
241
  //#endregion
242
242
  //#region src/composables/useMemiBoardAuth.ts
243
- var ft = "__MEMI_BOARD_AUTH_STATE__";
244
- function pt() {
243
+ var pt = "__MEMI_BOARD_AUTH_STATE__";
244
+ function mt() {
245
245
  let e = globalThis;
246
- return e[ft] || (e[ft] = {
247
- role: P("user"),
248
- rolePending: P(!0),
249
- rawBlockCount: P(0),
250
- blockAtMs: P(null),
246
+ return e[pt] || (e[pt] = {
247
+ role: F("user"),
248
+ rolePending: F(!0),
249
+ rawBlockCount: F(0),
250
+ blockAtMs: F(null),
251
251
  watchStarted: !1
252
- }), e[ft];
252
+ }), e[pt];
253
253
  }
254
- function mt() {
255
- let e = de(), { t, locale: n } = O(), r = pt(), i = Z(), a = Ce(), o = be(), { role: s, rolePending: c, rawBlockCount: l, blockAtMs: u } = r;
254
+ function ht() {
255
+ let e = ue(), { t, locale: n } = O(), r = mt(), i = Z(), a = we(), o = xe(), { role: s, rolePending: c, rawBlockCount: l, blockAtMs: u } = r;
256
256
  function d() {
257
257
  let t = e.moderation?.blockBanThreshold;
258
258
  return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
259
259
  }
260
260
  function p() {
261
261
  let t = e.moderation?.blockBanDecayMs;
262
- return typeof t == "number" && t > 0 ? t : ot;
262
+ return typeof t == "number" && t > 0 ? t : st;
263
263
  }
264
264
  function m(e) {
265
- return f(i, A(), e);
265
+ return f(i, j(), e);
266
266
  }
267
267
  function h(e) {
268
268
  if (!e) {
269
269
  l.value = 0, u.value = null;
270
270
  return;
271
271
  }
272
- l.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), u.value = dt(e.moderationBlockAt);
272
+ l.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), u.value = ft(e.moderationBlockAt);
273
273
  }
274
274
  async function g(e) {
275
- let t = m(e.uid), n = ee(i, A(), e.uid), r = await R(t);
276
- if (!r.exists()) return await Promise.all([K(t, {
275
+ let t = m(e.uid), n = ee(i, j(), e.uid), r = await z(t);
276
+ if (!r.exists()) return await Promise.all([q(t, {
277
277
  role: "user",
278
278
  displayName: e.displayName,
279
279
  photoURL: e.photoURL,
280
- joinedAt: G(),
281
- lastVisitAt: G(),
282
- updatedAt: G()
283
- }), K(n, { email: e.email })]), h({ moderationBlockCount: 0 }), "user";
280
+ joinedAt: K(),
281
+ lastVisitAt: K(),
282
+ updatedAt: K()
283
+ }), q(n, { email: e.email })]), h({ moderationBlockCount: 0 }), "user";
284
284
  let a = r.data();
285
285
  h(a);
286
286
  let o = typeof a.displayName == "string" && a.displayName.trim(), s = typeof a.photoURL == "string" && a.photoURL.trim(), c = {
287
- lastVisitAt: G(),
288
- updatedAt: G()
287
+ lastVisitAt: K(),
288
+ updatedAt: K()
289
289
  };
290
- return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([J(t, c), K(n, { email: e.email }, { merge: !0 })]), [
290
+ return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([Y(t, c), q(n, { email: e.email }, { merge: !0 })]), [
291
291
  "admin",
292
292
  "staff",
293
293
  "user"
@@ -299,10 +299,10 @@ function mt() {
299
299
  h(void 0);
300
300
  return;
301
301
  }
302
- let t = await R(m(e));
302
+ let t = await z(m(e));
303
303
  h(t.exists() ? t.data() : void 0);
304
304
  }
305
- r.watchStarted || (r.watchStarted = !0, I(o, async (e) => {
305
+ r.watchStarted || (r.watchStarted = !0, L(o, async (e) => {
306
306
  if (c.value = !0, !e) {
307
307
  s.value = "user", h(void 0), c.value = !1;
308
308
  return;
@@ -315,15 +315,15 @@ function mt() {
315
315
  c.value = !1;
316
316
  }
317
317
  }, { immediate: !0 }));
318
- let v = M(() => !!o.value), y = M(() => s.value === "admin"), b = M(() => s.value === "staff"), x = M(() => y.value || b.value), S = M(() => st(l.value, u.value, Date.now(), p())), C = M(() => {
318
+ let v = N(() => !!o.value), y = N(() => s.value === "admin"), b = N(() => s.value === "staff"), x = N(() => y.value || b.value), S = N(() => ct(l.value, u.value, Date.now(), p())), C = N(() => {
319
319
  let e = d();
320
- return e <= 0 ? !1 : lt(l.value, u.value, Date.now(), e, p());
321
- }), w = M(() => {
320
+ return e <= 0 ? !1 : ut(l.value, u.value, Date.now(), e, p());
321
+ }), w = N(() => {
322
322
  let e = d();
323
323
  if (e <= 0) return null;
324
- let t = ct(l.value, u.value, Date.now(), e, p());
325
- return t == null ? null : ut(t, Date.now(), n.value);
326
- }), T = M(() => {
324
+ let t = lt(l.value, u.value, Date.now(), e, p());
325
+ return t == null ? null : dt(t, Date.now(), n.value);
326
+ }), T = N(() => {
327
327
  if (!C.value) return null;
328
328
  let e = w.value;
329
329
  return e ? t("auth.writeRestrictedUntil", { until: e }) : t("auth.writeRestricted");
@@ -335,26 +335,26 @@ function mt() {
335
335
  restricted: !1,
336
336
  messageSuffix: ""
337
337
  };
338
- let i = m(e), a = await R(i);
339
- a.exists() ? await J(i, {
340
- moderationBlockCount: B(1),
341
- moderationBlockAt: G(),
342
- updatedAt: G()
343
- }) : await K(i, {
338
+ let i = m(e), a = await z(i);
339
+ a.exists() ? await Y(i, {
340
+ moderationBlockCount: V(1),
341
+ moderationBlockAt: K(),
342
+ updatedAt: K()
343
+ }) : await q(i, {
344
344
  role: "user",
345
345
  displayName: o.value?.displayName ?? null,
346
346
  moderationBlockCount: 1,
347
- moderationBlockAt: G(),
348
- updatedAt: G()
347
+ moderationBlockAt: K(),
348
+ updatedAt: K()
349
349
  });
350
350
  let s = (a.exists() ? Math.max(0, Math.floor(Number(a.data().moderationBlockCount) || 0)) : 0) + 1, c = Date.now();
351
351
  l.value = s, u.value = c;
352
- let f = st(s, c, c, p()), h = lt(s, c, c, r, p()), g = t("auth.strikeSuffix", {
352
+ let f = ct(s, c, c, p()), h = ut(s, c, c, r, p()), g = t("auth.strikeSuffix", {
353
353
  count: f,
354
354
  threshold: r
355
355
  });
356
356
  if (h) {
357
- let e = ct(s, c, c, r, p()), i = e == null ? null : ut(e, c, n.value);
357
+ let e = lt(s, c, c, r, p()), i = e == null ? null : dt(e, c, n.value);
358
358
  g = i ? t("auth.strikeRestrictedUntil", {
359
359
  count: f,
360
360
  until: i
@@ -371,33 +371,33 @@ function mt() {
371
371
  return a;
372
372
  }
373
373
  async function k() {
374
- return Oe(D(), new we());
374
+ return ke(D(), new Te());
375
375
  }
376
376
  async function te() {
377
- let e = new Te("apple.com");
378
- return e.addScope("email"), e.addScope("name"), Oe(D(), e);
377
+ let e = new Ee("apple.com");
378
+ return e.addScope("email"), e.addScope("name"), ke(D(), e);
379
379
  }
380
- async function ne(e, t) {
381
- return De(D(), e, t);
380
+ async function A(e, t) {
381
+ return Oe(D(), e, t);
382
382
  }
383
- async function re(e, t, n) {
384
- let r = await Ee(D(), e, t);
385
- return n && await Ae(r.user, { displayName: n }), r;
383
+ async function ne(e, t, n) {
384
+ let r = await De(D(), e, t);
385
+ return n && await je(r.user, { displayName: n }), r;
386
+ }
387
+ async function re() {
388
+ await Ae(D());
386
389
  }
387
- async function ie() {
388
- await ke(D());
390
+ function ie(e) {
391
+ return tt(o.value?.uid, y.value, b.value, e ? at(e) : void 0);
389
392
  }
390
393
  function ae(e) {
391
- return et(o.value?.uid, y.value, b.value, e ? it(e) : void 0);
394
+ return v.value && (o.value?.uid === e.authorUid || ie(e.boardId));
392
395
  }
393
396
  function oe(e) {
394
- return v.value && (o.value?.uid === e.authorUid || ae(e.boardId));
395
- }
396
- function se(e) {
397
397
  return v.value && o.value?.uid === e.authorUid;
398
398
  }
399
- function ce(e) {
400
- return v.value && (o.value?.uid === e.authorUid || ae(e.boardId));
399
+ function se(e) {
400
+ return v.value && (o.value?.uid === e.authorUid || ie(e.boardId));
401
401
  }
402
402
  return {
403
403
  user: o,
@@ -405,7 +405,7 @@ function mt() {
405
405
  isAdmin: y,
406
406
  isStaff: b,
407
407
  canManageContent: x,
408
- canManageBoard: ae,
408
+ canManageBoard: ie,
409
409
  rolePending: c,
410
410
  moderationBlockCount: S,
411
411
  isWriteRestricted: C,
@@ -413,41 +413,47 @@ function mt() {
413
413
  restrictedMessage: T,
414
414
  signInWithGoogle: k,
415
415
  signInWithApple: te,
416
- signInWithEmail: ne,
417
- signUpWithEmail: re,
418
- signOut: ie,
419
- canEdit: oe,
420
- canDelete: oe,
421
- canEditComment: se,
422
- canDeleteComment: ce,
416
+ signInWithEmail: A,
417
+ signUpWithEmail: ne,
418
+ signOut: re,
419
+ canEdit: ae,
420
+ canDelete: ae,
421
+ canEditComment: oe,
422
+ canDeleteComment: se,
423
423
  recordContentModerationBlock: E,
424
424
  refreshBoardUser: _
425
425
  };
426
426
  }
427
427
  //#endregion
428
428
  //#region src/utils/imeSafeSubmit.ts
429
- var ht = /* @__PURE__ */ new WeakMap();
430
- function gt(e) {
429
+ var gt = /* @__PURE__ */ new WeakMap();
430
+ function _t(e) {
431
431
  let t = e.currentTarget;
432
432
  return t instanceof HTMLButtonElement ? t : t instanceof Element ? t.closest("button") : null;
433
433
  }
434
- function _t(e) {
434
+ function vt(e) {
435
435
  if (e.pointerType !== "mouse" || e.button !== 0) return;
436
- let t = gt(e), n = t?.form;
436
+ let t = _t(e), n = t?.form;
437
437
  if (!t || !n || t.disabled) return;
438
- ht.set(t, Date.now() + 1e3);
438
+ gt.set(t, Date.now() + 1e3);
439
439
  let r = document.activeElement;
440
440
  r instanceof HTMLElement && r.blur(), setTimeout(() => n.requestSubmit(t), 0);
441
441
  }
442
- function vt(e) {
443
- let t = gt(e);
442
+ function yt(e) {
443
+ let t = _t(e);
444
444
  if (!t) return;
445
- let n = ht.get(t);
446
- n && Date.now() < n && (e.preventDefault(), ht.delete(t));
445
+ let n = gt.get(t);
446
+ n && Date.now() < n && (e.preventDefault(), gt.delete(t));
447
+ }
448
+ //#endregion
449
+ //#region src/utils/findReadablePost.ts
450
+ async function bt(e, t, n, r, i, a) {
451
+ let o = [X("boardId", "==", n), X("slug", "==", r)];
452
+ return a ? (await B(G(M(e, t), ...o, H(1)))).docs[0] : (await Promise.all([B(G(M(e, t), ...o, X("listed", "==", !0), X("isPublished", "==", !0), H(1))), ...i ? [B(G(M(e, t), ...o, X("authorUid", "==", i), H(1)))] : []])).flatMap((e) => e.docs)[0];
447
453
  }
448
454
  //#endregion
449
455
  //#region src/utils/extractEditorImageUrls.ts
450
- function yt(e) {
456
+ function xt(e) {
451
457
  if (!e) return [];
452
458
  let t = /* @__PURE__ */ new Set();
453
459
  for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&amp;", "&"));
@@ -456,7 +462,7 @@ function yt(e) {
456
462
  }
457
463
  //#endregion
458
464
  //#region src/utils/storagePath.ts
459
- function bt(e) {
465
+ function St(e) {
460
466
  try {
461
467
  let t = new URL(e), n = t.hostname.toLowerCase();
462
468
  if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
@@ -466,109 +472,106 @@ function bt(e) {
466
472
  return null;
467
473
  }
468
474
  }
469
- function xt(e) {
475
+ function Ct(e) {
470
476
  return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
471
477
  }
472
478
  //#endregion
473
479
  //#region src/utils/deletePostCascade.ts
474
- async function St(e) {
480
+ async function wt(e) {
475
481
  try {
476
- let t = await Ne(e);
477
- await Promise.all([...t.items.map((e) => je(e).catch(() => {})), ...t.prefixes.map((e) => St(e))]);
482
+ let t = await Pe(e);
483
+ await Promise.all([...t.items.map((e) => Me(e).catch(() => {})), ...t.prefixes.map((e) => wt(e))]);
478
484
  } catch {}
479
485
  }
480
- async function Ct(e, t) {
481
- await St(Pe(e, `${t}/images`)), await St(Pe(e, `${t}/attachments`));
486
+ async function Tt(e, t) {
487
+ await wt(Fe(e, `${t}/images`)), await wt(Fe(e, `${t}/attachments`));
482
488
  }
483
- async function wt(e, t) {
489
+ async function Et(e, t) {
484
490
  let n = [...new Set([...t].filter(Boolean))];
485
- await Promise.all(n.map((t) => je(Pe(e, t)).catch(() => {})));
491
+ await Promise.all(n.map((t) => Me(Fe(e, t)).catch(() => {})));
486
492
  }
487
- var Tt = 450;
488
- async function Et(e, t) {
489
- let n = await z(t);
490
- for (let t = 0; t < n.docs.length; t += Tt) {
491
- let r = X(e);
492
- n.docs.slice(t, t + Tt).forEach((e) => r.delete(e.ref)), await r.commit();
493
+ var Dt = 450;
494
+ async function Ot(e, t) {
495
+ let n = await B(t);
496
+ for (let t = 0; t < n.docs.length; t += Dt) {
497
+ let r = ye(e);
498
+ n.docs.slice(t, t + Dt).forEach((e) => r.delete(e.ref)), await r.commit();
493
499
  }
494
500
  }
495
- async function Dt(e, t, n) {
496
- await Et(e, W(a(e, t), Y("postId", "==", n)));
497
- }
498
- async function Ot(e, t, n) {
499
- await Et(e, W(s(e, t), Y("postId", "==", n)));
500
- }
501
501
  async function kt(e, t, n) {
502
- await Et(e, W(a(e, t), Y("commentId", "==", n)));
502
+ await Ot(e, G(a(e, t), X("postId", "==", n)));
503
+ }
504
+ async function At(e, t, n) {
505
+ await Ot(e, G(a(e, t), X("commentId", "==", n), X("target", "==", "comment")));
503
506
  }
504
- async function At(e, t, n, r) {
505
- let i = l(e, n, r), a = c(e, n, r), [o, s] = await Promise.all([R(i), R(a)]);
507
+ async function jt(e, t, n, r) {
508
+ let i = l(e, n, r), a = c(e, n, r), [o, s] = await Promise.all([z(i), z(a)]);
506
509
  if (!o.exists()) return;
507
510
  let d = o.data(), f = s.exists() ? String(s.data().content ?? "") : "";
508
- await Et(e, W(u(e, n), Y("postId", "==", r))), await Dt(e, n, r), await Ot(e, n, r);
511
+ await Ot(e, G(u(e, n), X("postId", "==", r))), await kt(e, n, r);
509
512
  let p = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set();
510
513
  for (let e of d.attachments ?? []) {
511
514
  if (!e?.path) continue;
512
515
  p.add(e.path);
513
- let t = xt(e.path);
516
+ let t = Ct(e.path);
514
517
  t && t !== r && h.add(t);
515
518
  }
516
- for (let e of yt(f)) {
517
- let t = bt(e);
519
+ for (let e of xt(f)) {
520
+ let t = St(e);
518
521
  if (!t) continue;
519
522
  p.add(t);
520
523
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
521
524
  n !== t && n.includes("/images/thumbnails/") && p.add(n);
522
525
  let i = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
523
526
  i && !t.includes("/thumbnails/") && p.add(`${i[1]}thumbnails/${i[2]}.jpg`);
524
- let a = xt(t);
527
+ let a = Ct(t);
525
528
  a && a !== r && h.add(a);
526
529
  }
527
- await pe(a), await Ct(t, m(n, r));
528
- for (let e of h) await Ct(t, m(n, e));
529
- await wt(t, p), await pe(i);
530
+ await fe(a), await Tt(t, m(n, r));
531
+ for (let e of h) await Tt(t, m(n, e));
532
+ await Et(t, p), await fe(i);
530
533
  }
531
534
  //#endregion
532
535
  //#region src/utils/postBody.ts
533
- function jt(e) {
536
+ function Mt(e) {
534
537
  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();
535
538
  }
536
- function Mt(e, t = 40) {
537
- let n = jt(e);
539
+ function Nt(e, t = 40) {
540
+ let n = Mt(e);
538
541
  if (!n) return "";
539
542
  if (n.length <= t) return n;
540
543
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
541
544
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
542
545
  }
543
- function Nt(e) {
544
- return jt(e).length > 0;
546
+ function Pt(e) {
547
+ return Mt(e).length > 0;
545
548
  }
546
- function Pt(e, t) {
549
+ function Ft(e, t) {
547
550
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
548
551
  }
549
- function Ft(e) {
550
- return !Nt(e);
552
+ function It(e) {
553
+ return !Pt(e);
551
554
  }
552
555
  //#endregion
553
556
  //#region src/utils/postPreview.ts
554
- function It(e) {
557
+ function Lt(e) {
555
558
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
556
559
  }
557
- function Lt(e) {
560
+ function Rt(e) {
558
561
  if (e) try {
559
- let t = new URL(It(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
562
+ let t = new URL(Lt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
560
563
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
561
564
  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;
562
565
  } catch {
563
566
  return;
564
567
  }
565
568
  }
566
- function Rt(e) {
567
- let t = Lt(e.videoUrl);
569
+ function zt(e) {
570
+ let t = Rt(e.videoUrl);
568
571
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
569
572
  }
570
- function zt(e, t = []) {
571
- 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 ? It(r[1]) : a, s = o ? Lt(o) : void 0, c = n ? It(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
573
+ function Bt(e, t = []) {
574
+ 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 ? Lt(r[1]) : a, s = o ? Rt(o) : void 0, c = n ? Lt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
572
575
  return {
573
576
  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),
574
577
  ...c ? { previewImage: c } : {},
@@ -576,8 +579,45 @@ function zt(e, t = []) {
576
579
  };
577
580
  }
578
581
  //#endregion
582
+ //#region src/utils/saveBoardVisibility.ts
583
+ function Vt(e) {
584
+ return e === "hidden" ? "hidden" : "public";
585
+ }
586
+ async function Ht(e, t, n, r) {
587
+ let i;
588
+ for (;;) {
589
+ let a = await B(G(t, X("boardId", "==", n), W("__name__"), ...i ? [J(i)] : [], H(400)));
590
+ if (a.empty) break;
591
+ let o = ye(e), s = 0;
592
+ for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
593
+ if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
594
+ }
595
+ }
596
+ async function Ut(e, t, n, r) {
597
+ await Ht(e, M(e, t), n, r), await Ht(e, u(e, t), n, r);
598
+ }
599
+ async function Wt(e, t, n, r) {
600
+ let i = o(e, t, n), a = Vt(r.visibility);
601
+ await ve(e, async (e) => {
602
+ let t = await e.get(i), n = t.data(), o = n?.visibilityTransition || "";
603
+ if (o && o !== a) throw Error("Finish the pending visibility change before changing it again.");
604
+ return !t.exists() || !o && Vt(n?.visibility) === a ? (e.set(i, r, { merge: !0 }), !1) : (e.set(i, {
605
+ ...r,
606
+ visibility: a === "public" ? "public" : Vt(n?.visibility),
607
+ visibilityTransition: a
608
+ }, { merge: !0 }), !0);
609
+ }) && (await Ut(e, t, n, a !== "hidden"), await ve(e, async (e) => {
610
+ if ((await e.get(i)).data()?.visibilityTransition !== a) throw Error("The visibility transition changed. Reload and retry.");
611
+ e.update(i, {
612
+ visibility: a,
613
+ visibilityTransition: "",
614
+ updatedAt: K()
615
+ });
616
+ }));
617
+ }
618
+ //#endregion
579
619
  //#region src/composables/useMemiBoardSettings.ts
580
- var Bt = [
620
+ var Gt = [
581
621
  {
582
622
  id: "free",
583
623
  label: "자유",
@@ -605,17 +645,18 @@ var Bt = [
605
645
  visibility: "public",
606
646
  order: 2
607
647
  }
608
- ], Vt = Bt;
609
- function Ht(e) {
648
+ ], Kt = Gt;
649
+ function qt(e) {
610
650
  return e === "hidden" ? "hidden" : "public";
611
651
  }
612
- function Ut(e, t, n) {
652
+ function Jt(e, t, n) {
613
653
  return {
614
654
  id: e,
615
655
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
616
656
  order: typeof t.order == "number" ? t.order : n,
617
657
  description: (typeof t.description == "string" ? t.description : "") || "",
618
- visibility: Ht(t.visibility),
658
+ visibility: qt(t.visibility),
659
+ visibilityTransition: t.visibilityTransition === "hidden" || t.visibilityTransition === "public" ? t.visibilityTransition : void 0,
619
660
  listView: t.listView ?? "default",
620
661
  editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
621
662
  writeRole: t.writeRole ?? "user",
@@ -624,50 +665,37 @@ function Ut(e, t, n) {
624
665
  locale: E(t.locale) ?? ""
625
666
  };
626
667
  }
627
- function Wt() {
628
- let e = () => A(), t = Z(), n = Se(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = mt(), { t: l } = O(), d = M(() => W(y(t, e()), U("order", "asc"))), { data: f, pending: p } = ye(d, { ssrKey: r(e(), "boards") }), m = M(() => f.value.map((e, t) => Ut(e.id, e, t))), h = m, g = M(() => m.value.filter((e) => e.visibility !== "hidden")), _ = g;
629
- function v(e) {
630
- if (e) return m.value.find((t) => t.id === e)?.label ?? e;
631
- }
632
- let b = v;
633
- function x(e) {
634
- if (e) return m.value.find((t) => t.id === e)?.description?.trim() || void 0;
635
- }
636
- let S = x;
637
- function C(e) {
638
- return e ? m.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
668
+ function Yt() {
669
+ let e = () => j(), t = Z(), n = Ce(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = ht(), { t: l } = O(), u = N(() => G(y(t, e()), W("order", "asc"))), { data: d, pending: f } = be(u, { ssrKey: r(e(), "boards") }), p = N(() => d.value.map((e, t) => Jt(e.id, e, t))), m = p, h = N(() => p.value.filter((e) => e.visibility !== "hidden")), g = h;
670
+ function _(e) {
671
+ if (e) return p.value.find((t) => t.id === e)?.label ?? e;
639
672
  }
640
- let w = C;
641
- function T(e) {
642
- return C(e) === "hidden";
673
+ let v = _;
674
+ function b(e) {
675
+ if (e) return p.value.find((t) => t.id === e)?.description?.trim() || void 0;
643
676
  }
644
- let D = T;
645
- function k(e) {
646
- if (e) return m.value.find((t) => t.id === e);
677
+ let x = b;
678
+ function S(e) {
679
+ return e ? p.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
647
680
  }
648
- rt((e) => k(e));
649
- function ee(e) {
650
- return et(c.value?.uid, a.value, s.value, e ? k(e) : void 0);
681
+ let C = S;
682
+ function w(e) {
683
+ return S(e) === "hidden";
651
684
  }
652
- let te = M(() => m.value.filter((e) => ee(e.id)));
653
- async function ne(e, n, r) {
654
- let i;
655
- for (;;) {
656
- let a = await z(W(e, Y("boardId", "==", n), U("__name__"), ...i ? [q(i)] : [], V(400)));
657
- if (a.empty) break;
658
- let o = X(t), s = 0;
659
- for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
660
- if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
661
- }
685
+ let T = w;
686
+ function D(e) {
687
+ if (e) return p.value.find((t) => t.id === e);
662
688
  }
663
- async function ie(n, r) {
664
- await ne(j(t, e()), n, r), await ne(u(t, e()), n, r);
689
+ it((e) => D(e));
690
+ function k(e) {
691
+ return tt(c.value?.uid, a.value, s.value, e ? D(e) : void 0);
665
692
  }
666
- function ae(e, t) {
693
+ let ee = N(() => p.value.filter((e) => k(e.id)));
694
+ function te(e, t) {
667
695
  return {
668
696
  label: e.label.trim(),
669
697
  description: (e.description ?? "").trim(),
670
- visibility: Ht(e.visibility),
698
+ visibility: qt(e.visibility),
671
699
  listView: e.listView ?? "default",
672
700
  editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
673
701
  writeRole: e.writeRole ?? "user",
@@ -675,64 +703,51 @@ function Wt() {
675
703
  allowedStaffUids: e.allowedStaffUids ?? [],
676
704
  locale: E(e.locale) ?? "",
677
705
  order: t,
678
- updatedAt: G()
706
+ updatedAt: K()
679
707
  };
680
708
  }
681
- async function oe(n, r = n.order ?? 0) {
709
+ async function A(n, r = n.order ?? 0) {
682
710
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
683
- let a = n.id.trim(), s = n.label.trim();
684
- if (!a || !s) throw Error(l("boardSettings.idAndNameRequired"));
685
- let c = Ht(n.visibility), u = ae({
711
+ let a = n.id.trim(), o = n.label.trim();
712
+ if (!a || !o) throw Error(l("boardSettings.idAndNameRequired"));
713
+ let s = qt(n.visibility), c = te({
686
714
  ...n,
687
- label: s,
688
- visibility: c
715
+ label: o,
716
+ visibility: s
689
717
  }, r);
690
- await K(o(t, e(), a), u, { merge: !0 }), await ie(a, c !== "hidden");
718
+ await Wt(t, e(), a, c);
691
719
  }
692
- let se = oe;
693
- async function ce(n) {
720
+ let re = A;
721
+ async function ie(e) {
694
722
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
695
- let r = new Map(m.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(t), s = [];
696
- n.forEach((n, i) => {
697
- let c = n.id.trim(), u = n.label.trim();
698
- if (!c || !u) throw Error(l("boardSettings.idAndNameRequired"));
699
- let d = Ht(n.visibility), f = ae({
700
- ...n,
701
- label: u,
702
- visibility: d
703
- }, i);
704
- a.set(o(t, e(), c), f, { merge: !0 }), ((r.get(c) ?? "public") !== d || d === "hidden") && s.push({
705
- id: c,
706
- listed: d !== "hidden"
707
- });
708
- }), await a.commit();
709
- for (let e of s) await ie(e.id, e.listed);
723
+ for (let t of e) if (!t.id.trim() || !t.label.trim()) throw Error(l("boardSettings.idAndNameRequired"));
724
+ for (let [t, n] of e.entries()) await A(n, t);
710
725
  }
711
- let le = ce;
712
- async function ue(r) {
726
+ let ae = ie;
727
+ async function oe(r) {
713
728
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
714
729
  let a = r.trim();
715
730
  if (!a) throw Error(l("boardSettings.deleteIdRequired"));
716
731
  for (;;) {
717
- let r = await z(W(j(t, e()), Y("boardId", "==", a), V(25)));
732
+ let r = await B(G(M(t, e()), X("boardId", "==", a), H(25)));
718
733
  if (r.empty) break;
719
- for (let i of r.docs) await At(t, Me(n), e(), i.id);
734
+ for (let i of r.docs) await jt(t, Ne(n), e(), i.id);
720
735
  }
721
- await pe(o(t, e(), r));
736
+ await fe(o(t, e(), r));
722
737
  }
723
- let de = ue;
724
- async function N() {
738
+ let se = oe;
739
+ async function ce() {
725
740
  return Promise.resolve();
726
741
  }
727
- async function fe(e) {
742
+ async function le(e) {
728
743
  let t = e.trim();
729
744
  if (!t) throw Error(l("boardSettings.nameRequired"));
730
745
  if (!i.value) throw Error(l("boardSettings.signInRequired"));
731
- let n = m.value.find((e) => e.label === t);
746
+ let n = p.value.find((e) => e.label === t);
732
747
  if (n) return n.id;
733
- let r = re(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(m.value.map((e) => e.id));
748
+ let r = ne(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(p.value.map((e) => e.id));
734
749
  for (; s.has(a);) a = `${r}-${o++}`;
735
- return await oe({
750
+ return await A({
736
751
  id: a,
737
752
  label: t,
738
753
  visibility: "public",
@@ -740,76 +755,79 @@ function Wt() {
740
755
  editorType: "default",
741
756
  writeRole: "user",
742
757
  commentWriteRole: "user",
743
- order: m.value.length
758
+ order: p.value.length
744
759
  }), a;
745
760
  }
746
761
  return {
747
- boards: m,
748
- publicBoards: g,
749
- boardLabel: v,
750
- boardDescription: x,
751
- boardVisibility: C,
752
- isBoardHidden: T,
753
- getBoard: k,
754
- canManageBoard: ee,
755
- managedBoards: te,
756
- categories: h,
757
- publicCategories: _,
758
- categoryLabel: b,
759
- categoryDescription: S,
760
- categoryVisibility: w,
761
- isCategoryHidden: D,
762
- settingsPending: p,
763
- ensureSettings: N,
764
- addBoard: fe,
765
- addCategory: fe,
766
- saveBoard: oe,
767
- saveCategory: se,
768
- saveBoards: ce,
769
- saveCategories: le,
770
- deleteBoard: ue,
771
- deleteCategory: de,
772
- DEFAULT_BOARDS: Bt,
773
- DEFAULT_CATEGORIES: Vt
762
+ boards: p,
763
+ publicBoards: h,
764
+ boardLabel: _,
765
+ boardDescription: b,
766
+ boardVisibility: S,
767
+ isBoardHidden: w,
768
+ getBoard: D,
769
+ canManageBoard: k,
770
+ managedBoards: ee,
771
+ categories: m,
772
+ publicCategories: g,
773
+ categoryLabel: v,
774
+ categoryDescription: x,
775
+ categoryVisibility: C,
776
+ isCategoryHidden: T,
777
+ settingsPending: f,
778
+ ensureSettings: ce,
779
+ addBoard: le,
780
+ addCategory: le,
781
+ saveBoard: A,
782
+ saveCategory: re,
783
+ saveBoards: ie,
784
+ saveCategories: ae,
785
+ deleteBoard: oe,
786
+ deleteCategory: se,
787
+ DEFAULT_BOARDS: Gt,
788
+ DEFAULT_CATEGORIES: Kt
774
789
  };
775
790
  }
776
791
  //#endregion
777
792
  //#region src/composables/useMemiBoardPosts.ts
778
- var Gt = /* @__PURE__ */ new Map();
779
- function Kt(e) {
793
+ var Xt = /* @__PURE__ */ new Map();
794
+ function Zt(e) {
780
795
  if (!e || typeof e != "object") return !1;
781
796
  let t = "code" in e ? String(e.code) : "";
782
797
  return t === "permission-denied" || t === "firestore/permission-denied";
783
798
  }
784
- function qt(e, t) {
785
- let n = F(e)?.trim();
799
+ function Qt(e, t) {
800
+ let n = I(e)?.trim();
786
801
  if (!n) throw Error(w(t, "posts.boardIdRequired"));
787
802
  return n;
788
803
  }
789
- function Jt(e) {
790
- let t = Z(), n = Se(), { isBoardHidden: r, getBoard: i } = Wt(), { t: a, locale: o } = O(), s = () => A(), u = () => qt(e, o.value), d = () => j(t, s()), f = (e) => l(t, s(), e), p = (e) => c(t, s(), e);
791
- function m() {
792
- return !r(u());
804
+ function $t(e) {
805
+ let t = Z(), n = Ce(), { isBoardHidden: r, getBoard: i } = Yt(), { user: a, canManageBoard: o } = ht(), { t: s, locale: u } = O(), d = () => j(), f = () => Qt(e, u.value), p = () => M(t, d()), m = (e) => l(t, d(), e), h = (e) => c(t, d(), e);
806
+ function g() {
807
+ return !r(f());
793
808
  }
794
- function h() {
795
- return i(u())?.editorType === "image";
809
+ function _() {
810
+ return i(f())?.editorType === "image";
796
811
  }
797
- function g(e) {
798
- let t = h(), n = Nt(e.content), r = Pt(e.content, e.attachments);
812
+ function v(e) {
813
+ let t = _(), n = Pt(e.content), r = Ft(e.content, e.attachments);
799
814
  if (t) {
800
- if (!r) throw Error(a("posts.photoRequired"));
815
+ if (!r) throw Error(s("posts.photoRequired"));
801
816
  return;
802
817
  }
803
- if (!n && !r) throw Error(a("posts.bodyRequired"));
804
- if (!e.title?.trim()) throw Error(a("posts.titleRequired"));
818
+ if (!n && !r) throw Error(s("posts.bodyRequired"));
819
+ if (!e.title?.trim()) throw Error(s("posts.titleRequired"));
805
820
  }
806
- function _() {
807
- return he(d()).id;
821
+ function y() {
822
+ return me(p()).id;
823
+ }
824
+ function b(e) {
825
+ return bt(t, d(), f(), e, a.value?.uid, o(f()));
808
826
  }
809
- async function v(e = {}) {
810
- let t = e.pageSize ?? 20, n = [Y("boardId", "==", u()), Y("isPublished", "==", !0)];
811
- e.publicOnly !== !1 && n.push(Y("listed", "==", !0)), n.push(U("createdAt", "desc")), e.cursor && n.push(q(e.cursor)), n.push(V(t + 1));
812
- let r = await z(W(d(), ...n)), i = r.docs.slice(0, t);
827
+ async function x(e = {}) {
828
+ let t = e.pageSize ?? 20, n = [X("boardId", "==", f()), X("isPublished", "==", !0)];
829
+ 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));
830
+ let r = await B(G(p(), ...n)), i = r.docs.slice(0, t);
813
831
  return {
814
832
  posts: i.map((e) => ({
815
833
  id: e.id,
@@ -819,73 +837,71 @@ function Jt(e) {
819
837
  hasMore: r.docs.length > t
820
838
  };
821
839
  }
822
- async function y(e) {
823
- let [t, n] = await Promise.all([R(f(e)), R(p(e))]);
840
+ async function S(e) {
841
+ let [t, n] = await Promise.all([z(m(e)), z(h(e))]);
824
842
  return t.exists() ? {
825
843
  id: t.id,
826
844
  ...t.data(),
827
845
  content: n.exists() ? n.data().content ?? "" : ""
828
846
  } : null;
829
847
  }
830
- async function b(e) {
848
+ async function C(e) {
831
849
  try {
832
- let t = (await z(W(d(), Y("boardId", "==", u()), Y("slug", "==", e.trim()), V(1)))).docs[0];
850
+ let t = await b(e.trim());
833
851
  if (!t) return { status: "not-found" };
834
852
  try {
835
- let e = await y(t.id);
853
+ let e = await S(t.id);
836
854
  return e ? {
837
855
  status: "ok",
838
856
  post: e
839
857
  } : { status: "not-found" };
840
858
  } catch (e) {
841
- return Kt(e) ? { status: "permission-denied" } : {
859
+ return Zt(e) ? { status: "permission-denied" } : {
842
860
  status: "error",
843
861
  message: e instanceof Error ? e.message : String(e)
844
862
  };
845
863
  }
846
864
  } catch (e) {
847
- return Kt(e) ? { status: "permission-denied" } : {
865
+ return Zt(e) ? { status: "permission-denied" } : {
848
866
  status: "error",
849
867
  message: e instanceof Error ? e.message : String(e)
850
868
  };
851
869
  }
852
870
  }
853
- async function x(e) {
854
- let t = e.trim(), n = Gt.get(`${u()}/${t}`);
871
+ async function w(e) {
872
+ let t = e.trim(), n = Xt.get(`${f()}/${t}`);
855
873
  if (n) return {
856
874
  status: "ok",
857
875
  id: n
858
876
  };
859
877
  try {
860
- let e = (await z(W(d(), Y("boardId", "==", u()), Y("slug", "==", t), V(1)))).docs[0];
878
+ let e = await b(t);
861
879
  return e ? {
862
880
  status: "ok",
863
881
  id: e.id
864
- } : { status: "not-found" };
882
+ } : { status: r(f()) ? "permission-denied" : "not-found" };
865
883
  } catch (e) {
866
- return Kt(e) ? { status: "permission-denied" } : {
884
+ return Zt(e) ? { status: "permission-denied" } : {
867
885
  status: "error",
868
886
  message: e instanceof Error ? e.message : String(e)
869
887
  };
870
888
  }
871
889
  }
872
- async function S(e) {
873
- let t = re(e.trim()) || "post", n = t, r = 1;
874
- for (; !(await z(W(d(), Y("boardId", "==", u()), Y("slug", "==", n), V(1)))).empty;) r++, n = `${t}-${r}`;
875
- return n;
890
+ async function T(e) {
891
+ return `${ne(e.trim()) || "post"}-${y()}`;
876
892
  }
877
- async function C(e) {
893
+ async function E(e) {
878
894
  if (!e.createdAt) return {
879
895
  previous: null,
880
896
  next: null
881
897
  };
882
898
  try {
883
899
  let t = [
884
- Y("boardId", "==", u()),
885
- Y("isPublished", "==", !0),
886
- ...e.listed === !1 ? [] : [Y("listed", "==", !0)],
887
- U("createdAt", "desc")
888
- ], [n, r] = await Promise.all([z(W(d(), ...t, q(e.createdAt), V(1))), z(W(d(), ...t, ge(e.createdAt), _e(1)))]), i = (e) => {
900
+ X("boardId", "==", f()),
901
+ X("isPublished", "==", !0),
902
+ ...e.listed === !1 ? [] : [X("listed", "==", !0)],
903
+ W("createdAt", "desc")
904
+ ], [n, r] = await Promise.all([B(G(p(), ...t, J(e.createdAt), H(1))), B(G(p(), ...t, ge(e.createdAt), _e(1)))]), i = (e) => {
889
905
  let t = e.docs[0];
890
906
  return t ? {
891
907
  id: t.id,
@@ -903,13 +919,13 @@ function Jt(e) {
903
919
  };
904
920
  }
905
921
  }
906
- async function w(e) {
907
- g(e);
908
- let n = e.postId || _(), r = h(), i, a;
909
- r ? (i = n, a = Mt(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await S(a));
910
- let o = zt(e.content, e.attachments), s = m(), c = X(t);
911
- return c.set(f(n), {
912
- boardId: u(),
922
+ async function D(e) {
923
+ v(e);
924
+ let n = e.postId || y(), r = _(), i, a;
925
+ r ? (i = n, a = Nt(e.content)) : (a = e.title.trim(), i = e.slug?.trim() || await T(a));
926
+ let o = Bt(e.content, e.attachments), s = g(), c = ye(t);
927
+ return c.set(m(n), {
928
+ boardId: f(),
913
929
  slug: i,
914
930
  title: a,
915
931
  ...o,
@@ -925,56 +941,56 @@ function Jt(e) {
925
941
  authorPhoto: e.authorPhoto,
926
942
  moderationStatus: "approved",
927
943
  moderationModel: e.moderationModel ?? null,
928
- createdAt: G(),
929
- updatedAt: G()
930
- }), c.set(p(n), { content: e.content }), await c.commit(), Gt.set(`${u()}/${i}`, n), i;
931
- }
932
- async function T(e, t) {
933
- g(t);
934
- let n = h() ? Mt(t.content) : t.title.trim(), r = zt(t.content, t.attachments), i = m();
935
- await Promise.all([J(f(e), {
944
+ createdAt: K(),
945
+ updatedAt: K()
946
+ }), c.set(h(n), { content: e.content }), await c.commit(), Xt.set(`${f()}/${i}`, n), i;
947
+ }
948
+ async function k(e, t) {
949
+ v(t);
950
+ let n = _() ? Nt(t.content) : t.title.trim(), r = Bt(t.content, t.attachments), i = g();
951
+ await Promise.all([Y(m(e), {
936
952
  title: n,
937
953
  ...r,
938
- previewImage: r.previewImage ? r.previewImage : me(),
939
- videoUrl: r.videoUrl ? r.videoUrl : me(),
954
+ previewImage: r.previewImage ? r.previewImage : pe(),
955
+ videoUrl: r.videoUrl ? r.videoUrl : pe(),
940
956
  tags: t.tags ?? [],
941
957
  listed: i,
942
958
  attachments: t.attachments ?? [],
943
- updatedAt: G()
944
- }), K(p(e), { content: t.content }, { merge: !0 })]);
959
+ updatedAt: K()
960
+ }), q(h(e), { content: t.content }, { merge: !0 })]);
945
961
  }
946
- async function E(e) {
947
- await J(f(e), {
962
+ async function ee(e) {
963
+ await Y(m(e), {
948
964
  isPublished: !0,
949
- publishedAt: G(),
950
- updatedAt: G()
965
+ publishedAt: K(),
966
+ updatedAt: K()
951
967
  });
952
968
  }
953
- async function D(e) {
954
- await At(t, Me(n), s(), e);
955
- for (let [t, n] of Gt) n === e && Gt.delete(t);
969
+ async function te(e) {
970
+ await jt(t, Ne(n), d(), e);
971
+ for (let [t, n] of Xt) n === e && Xt.delete(t);
956
972
  }
957
973
  return {
958
- createPostId: _,
959
- getPosts: v,
960
- getPost: y,
961
- getPostBySlug: b,
962
- resolvePostIdBySlug: x,
963
- resolveUniqueSlug: S,
964
- getAdjacentPosts: C,
965
- createPost: w,
966
- updatePost: T,
967
- publishPost: E,
968
- deletePost: D
974
+ createPostId: y,
975
+ getPosts: x,
976
+ getPost: S,
977
+ getPostBySlug: C,
978
+ resolvePostIdBySlug: w,
979
+ resolveUniqueSlug: T,
980
+ getAdjacentPosts: E,
981
+ createPost: D,
982
+ updatePost: k,
983
+ publishPost: ee,
984
+ deletePost: te
969
985
  };
970
986
  }
971
- var Yt = 10;
972
- function Xt(e, t = {}) {
973
- let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = mt(), o = () => A(), s = t.pageSize ?? Yt, c = M(() => i.value?.uid ?? ""), l = M(() => qt(e)), u = () => j(n, o()), d = M(() => t.publicOnly === void 0 ? !(r.value || a(l.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), f = P([]), p = P([]), m = P([]), h = P(!0), g = P(!0), _ = P(!1), v = P(""), y = null, b = null, x, S;
987
+ var en = 10;
988
+ function tn(e, t = {}) {
989
+ let n = Z(), { isAdmin: r, user: i, canManageBoard: a } = ht(), o = () => j(), s = t.pageSize ?? en, c = N(() => i.value?.uid ?? ""), l = N(() => Qt(e)), u = () => M(n, o()), d = N(() => t.publicOnly === void 0 ? !(r.value || a(l.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), f = F([]), p = F([]), m = F([]), h = F(!0), g = F(!0), _ = F(!1), v = F(""), y = null, b = null, x, S;
974
990
  function C(e) {
975
991
  return e.createdAt?.toMillis?.() ?? 0;
976
992
  }
977
- let w = M(() => {
993
+ let w = N(() => {
978
994
  let e = /* @__PURE__ */ new Map();
979
995
  for (let t of [
980
996
  ...p.value,
@@ -984,11 +1000,11 @@ function Xt(e, t = {}) {
984
1000
  return [...e.values()].sort((e, t) => C(t) - C(e) || String(t.id).localeCompare(String(e.id)));
985
1001
  });
986
1002
  function T() {
987
- let e = [Y("boardId", "==", l.value), Y("isPublished", "==", !0)];
988
- return d.value && e.push(Y("listed", "==", !0)), e;
1003
+ let e = [X("boardId", "==", l.value), X("isPublished", "==", !0)];
1004
+ return d.value && e.push(X("listed", "==", !0)), e;
989
1005
  }
990
1006
  function E() {
991
- x?.(), x = H(W(u(), ...T(), U("createdAt", "desc"), U(L(), "desc"), V(s)), (e) => {
1007
+ x?.(), x = U(G(u(), ...T(), W("createdAt", "desc"), W(R(), "desc"), H(s)), (e) => {
992
1008
  f.value = e.docs.map((e) => ({
993
1009
  id: e.id,
994
1010
  ...e.data()
@@ -1004,7 +1020,7 @@ function Xt(e, t = {}) {
1004
1020
  m.value = [];
1005
1021
  return;
1006
1022
  }
1007
- S = H(W(u(), Y("boardId", "==", l.value), Y("authorUid", "==", e), Y("isPublished", "==", !1), U("createdAt", "desc"), V(20)), (e) => {
1023
+ S = U(G(u(), X("boardId", "==", l.value), X("authorUid", "==", e), X("isPublished", "==", !1), W("createdAt", "desc"), H(20)), (e) => {
1008
1024
  m.value = e.docs.map((e) => ({
1009
1025
  id: e.id,
1010
1026
  ...e.data()
@@ -1019,11 +1035,11 @@ function Xt(e, t = {}) {
1019
1035
  try {
1020
1036
  let e = b ?? y, t = [
1021
1037
  ...T(),
1022
- U("createdAt", "desc"),
1023
- U(L(), "desc"),
1024
- ...e ? [q(e)] : [],
1025
- V(s + 1)
1026
- ], n = await z(W(u(), ...t)), r = n.docs.slice(0, s), i = r.map((e) => ({
1038
+ W("createdAt", "desc"),
1039
+ W(R(), "desc"),
1040
+ ...e ? [J(e)] : [],
1041
+ H(s + 1)
1042
+ ], n = await B(G(u(), ...t)), r = n.docs.slice(0, s), i = r.map((e) => ({
1027
1043
  id: e.id,
1028
1044
  ...e.data()
1029
1045
  })), a = new Set([...p.value, ...f.value].map((e) => e.id));
@@ -1038,11 +1054,11 @@ function Xt(e, t = {}) {
1038
1054
  function k() {
1039
1055
  x?.(), f.value = [], p.value = [], y = null, b = null, g.value = !0, v.value = "", h.value = !0, E(), D();
1040
1056
  }
1041
- return I([
1057
+ return L([
1042
1058
  l,
1043
1059
  d,
1044
1060
  c
1045
- ], k, { immediate: !0 }), N(() => {
1061
+ ], k, { immediate: !0 }), P(() => {
1046
1062
  x?.(), S?.();
1047
1063
  }), {
1048
1064
  posts: w,
@@ -1053,22 +1069,22 @@ function Xt(e, t = {}) {
1053
1069
  loadMore: O
1054
1070
  };
1055
1071
  }
1056
- function Zt(e, t) {
1057
- let n = Z(), r = () => A(), i = M(() => qt(e)), a = M(() => typeof t == "string" ? t : t.value), o = M(() => l(n, r(), a.value)), s = M(() => c(n, r(), a.value)), { data: u, pending: d } = xe(o), { data: f, pending: p } = xe(s);
1072
+ function nn(e, t) {
1073
+ let n = Z(), r = () => j(), i = N(() => Qt(e)), a = N(() => typeof t == "string" ? t : t.value), o = N(() => l(n, r(), a.value)), s = N(() => c(n, r(), a.value)), { data: u, pending: d } = Se(o), { data: f, pending: p } = Se(s);
1058
1074
  return {
1059
- post: M(() => !u.value || u.value.boardId !== i.value ? null : {
1075
+ post: N(() => !u.value || u.value.boardId !== i.value ? null : {
1060
1076
  ...u.value,
1061
1077
  id: a.value,
1062
1078
  content: f.value?.content ?? ""
1063
1079
  }),
1064
- pending: M(() => d.value || p.value)
1080
+ pending: N(() => d.value || p.value)
1065
1081
  };
1066
1082
  }
1067
1083
  //#endregion
1068
1084
  //#region src/composables/useMemiBoardUserPosts.ts
1069
- var Qt = 10;
1070
- function $t(e, t = {}) {
1071
- let n = Z(), r = () => A(), i = () => j(n, r()), a = t.pageSize ?? Qt, o = M(() => F(e)?.trim() || ""), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
1085
+ var rn = 10;
1086
+ function an(e, t = {}) {
1087
+ let n = Z(), r = () => j(), i = () => M(n, r()), a = t.pageSize ?? rn, 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;
1072
1088
  function g(e) {
1073
1089
  return {
1074
1090
  id: e.id,
@@ -1078,16 +1094,16 @@ function $t(e, t = {}) {
1078
1094
  function _(e) {
1079
1095
  return e.createdAt?.toMillis?.() ?? 0;
1080
1096
  }
1081
- let v = M(() => {
1097
+ let v = N(() => {
1082
1098
  let e = /* @__PURE__ */ new Map();
1083
1099
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1084
1100
  return [...e.values()].sort((e, t) => _(t) - _(e) || String(t.id).localeCompare(String(e.id)));
1085
1101
  });
1086
1102
  function y() {
1087
1103
  return [
1088
- Y("authorUid", "==", o.value),
1089
- Y("isPublished", "==", !0),
1090
- Y("listed", "==", !0)
1104
+ X("authorUid", "==", o.value),
1105
+ X("isPublished", "==", !0),
1106
+ X("listed", "==", !0)
1091
1107
  ];
1092
1108
  }
1093
1109
  function b() {
@@ -1095,7 +1111,7 @@ function $t(e, t = {}) {
1095
1111
  l.value = !1;
1096
1112
  return;
1097
1113
  }
1098
- h = H(W(i(), ...y(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
1114
+ h = U(G(i(), ...y(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1099
1115
  s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
1100
1116
  }, (e) => {
1101
1117
  console.error("[memi-board:userPosts] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
@@ -1107,11 +1123,11 @@ function $t(e, t = {}) {
1107
1123
  try {
1108
1124
  let e = m ?? p, t = [
1109
1125
  ...y(),
1110
- U("createdAt", "desc"),
1111
- U(L(), "desc"),
1112
- ...e ? [q(e)] : [],
1113
- V(a + 1)
1114
- ], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(g), l = new Set([...c.value, ...s.value].map((e) => e.id));
1126
+ W("createdAt", "desc"),
1127
+ W(R(), "desc"),
1128
+ ...e ? [J(e)] : [],
1129
+ H(a + 1)
1130
+ ], n = await B(G(i(), ...t)), r = n.docs.slice(0, a), o = r.map(g), l = new Set([...c.value, ...s.value].map((e) => e.id));
1115
1131
  c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
1116
1132
  } catch (e) {
1117
1133
  f.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
@@ -1123,7 +1139,7 @@ function $t(e, t = {}) {
1123
1139
  function S() {
1124
1140
  h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, b();
1125
1141
  }
1126
- return I(o, S, { immediate: !0 }), N(() => h?.()), {
1142
+ return L(o, S, { immediate: !0 }), P(() => h?.()), {
1127
1143
  posts: v,
1128
1144
  postsPending: l,
1129
1145
  hasMore: u,
@@ -1134,9 +1150,9 @@ function $t(e, t = {}) {
1134
1150
  }
1135
1151
  //#endregion
1136
1152
  //#region src/composables/useMemiBoardUserComments.ts
1137
- var en = 10;
1138
- function tn(e, t = {}) {
1139
- let n = Z(), r = () => A(), i = () => u(n, r()), a = t.pageSize ?? en, o = M(() => F(e)?.trim() || ""), s = P([]), c = P([]), l = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
1153
+ var on = 10;
1154
+ function sn(e, t = {}) {
1155
+ let n = Z(), r = () => j(), i = () => u(n, r()), a = t.pageSize ?? on, o = N(() => I(e)?.trim() || ""), s = F([]), c = F([]), l = F(!0), d = F(!0), f = F(!1), p = F(""), m = null, h = null, g;
1140
1156
  function _(e) {
1141
1157
  return {
1142
1158
  id: e.id,
@@ -1146,20 +1162,20 @@ function tn(e, t = {}) {
1146
1162
  function v(e) {
1147
1163
  return e.createdAt?.toMillis?.() ?? 0;
1148
1164
  }
1149
- let y = M(() => {
1165
+ let y = N(() => {
1150
1166
  let e = /* @__PURE__ */ new Map();
1151
1167
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1152
1168
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
1153
1169
  });
1154
1170
  function b() {
1155
- return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
1171
+ return [X("authorUid", "==", o.value), X("listed", "==", !0)];
1156
1172
  }
1157
1173
  function x() {
1158
1174
  if (g?.(), !o.value) {
1159
1175
  l.value = !1;
1160
1176
  return;
1161
1177
  }
1162
- g = H(W(i(), ...b(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
1178
+ g = U(G(i(), ...b(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1163
1179
  s.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), l.value = !1;
1164
1180
  }, (e) => {
1165
1181
  console.error("[memi-board:userComments] onSnapshot failed", e), p.value = e.message || String(e), l.value = !1;
@@ -1171,11 +1187,11 @@ function tn(e, t = {}) {
1171
1187
  try {
1172
1188
  let e = h ?? m, t = [
1173
1189
  ...b(),
1174
- U("createdAt", "desc"),
1175
- U(L(), "desc"),
1176
- ...e ? [q(e)] : [],
1177
- V(a + 1)
1178
- ], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
1190
+ W("createdAt", "desc"),
1191
+ W(R(), "desc"),
1192
+ ...e ? [J(e)] : [],
1193
+ H(a + 1)
1194
+ ], 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));
1179
1195
  c.value.push(...o.filter((e) => !l.has(e.id))), h = r.at(-1) ?? h, d.value = n.docs.length > a;
1180
1196
  } catch (e) {
1181
1197
  p.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
@@ -1187,7 +1203,7 @@ function tn(e, t = {}) {
1187
1203
  function C() {
1188
1204
  g?.(), s.value = [], c.value = [], m = null, h = null, d.value = !0, p.value = "", l.value = !0, x();
1189
1205
  }
1190
- return I(o, C, { immediate: !0 }), N(() => g?.()), {
1206
+ return L(o, C, { immediate: !0 }), P(() => g?.()), {
1191
1207
  comments: y,
1192
1208
  commentsPending: l,
1193
1209
  hasMore: d,
@@ -1198,12 +1214,12 @@ function tn(e, t = {}) {
1198
1214
  }
1199
1215
  //#endregion
1200
1216
  //#region src/composables/useMemiBoardCommentFeed.ts
1201
- var nn = 10;
1202
- function rn(e = "latest", t = {}) {
1203
- let n = Z(), r = () => A(), i = () => u(n, r()), a = M(() => {
1204
- let e = Math.floor(Number(F(t.pageSize)));
1205
- return Number.isFinite(e) && e > 0 ? e : nn;
1206
- }), o = M(() => F(e) === "likes" ? "likes" : "latest"), s = P([]), c = P([]), l = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
1217
+ var cn = 10;
1218
+ function ln(e = "latest", t = {}) {
1219
+ let n = Z(), r = () => j(), i = () => u(n, r()), a = N(() => {
1220
+ let e = Math.floor(Number(I(t.pageSize)));
1221
+ return Number.isFinite(e) && e > 0 ? e : cn;
1222
+ }), o = N(() => I(e) === "likes" ? "likes" : "latest"), s = F([]), c = F([]), l = F(!0), d = F(!0), f = F(!1), p = F(""), m = null, h = null, g;
1207
1223
  function _(e) {
1208
1224
  return {
1209
1225
  id: e.id,
@@ -1220,25 +1236,25 @@ function rn(e = "latest", t = {}) {
1220
1236
  }
1221
1237
  return v(t) - v(e) || String(t.id).localeCompare(String(e.id));
1222
1238
  }
1223
- let b = M(() => {
1239
+ let b = N(() => {
1224
1240
  let e = /* @__PURE__ */ new Map();
1225
1241
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1226
1242
  return [...e.values()].sort(y);
1227
1243
  });
1228
1244
  function x() {
1229
1245
  return o.value === "likes" ? [
1230
- Y("listed", "==", !0),
1231
- U("likeCount", "desc"),
1232
- U("createdAt", "desc"),
1233
- U(L(), "desc")
1246
+ X("listed", "==", !0),
1247
+ W("likeCount", "desc"),
1248
+ W("createdAt", "desc"),
1249
+ W(R(), "desc")
1234
1250
  ] : [
1235
- Y("listed", "==", !0),
1236
- U("createdAt", "desc"),
1237
- U(L(), "desc")
1251
+ X("listed", "==", !0),
1252
+ W("createdAt", "desc"),
1253
+ W(R(), "desc")
1238
1254
  ];
1239
1255
  }
1240
1256
  function S() {
1241
- g?.(), g = H(W(i(), ...x(), V(a.value)), (e) => {
1257
+ g?.(), g = U(G(i(), ...x(), H(a.value)), (e) => {
1242
1258
  s.value = e.docs.map(_), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a.value), l.value = !1;
1243
1259
  }, (e) => {
1244
1260
  console.error("[memi-board:commentFeed] onSnapshot failed", e), p.value = e.message || String(e), l.value = !1;
@@ -1248,7 +1264,7 @@ function rn(e = "latest", t = {}) {
1248
1264
  if (!(f.value || !d.value || l.value)) {
1249
1265
  f.value = !0, p.value = "";
1250
1266
  try {
1251
- let e = h ?? m, t = await z(W(i(), ...x(), ...e ? [q(e)] : [], V(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
1267
+ let e = h ?? m, 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));
1252
1268
  c.value.push(...r.filter((e) => !o.has(e.id))), h = n.at(-1) ?? h, d.value = t.docs.length > a.value;
1253
1269
  } catch (e) {
1254
1270
  p.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
@@ -1260,7 +1276,7 @@ function rn(e = "latest", t = {}) {
1260
1276
  function w() {
1261
1277
  g?.(), s.value = [], c.value = [], m = null, h = null, d.value = !0, p.value = "", l.value = !0, S();
1262
1278
  }
1263
- return I([o, a], w, { immediate: !0 }), N(() => g?.()), {
1279
+ return L([o, a], w, { immediate: !0 }), P(() => g?.()), {
1264
1280
  comments: b,
1265
1281
  commentsPending: l,
1266
1282
  hasMore: d,
@@ -1272,35 +1288,35 @@ function rn(e = "latest", t = {}) {
1272
1288
  }
1273
1289
  //#endregion
1274
1290
  //#region src/composables/useMemiBoardAuthorMemo.ts
1275
- var an = 100, on = /* @__PURE__ */ new Map();
1291
+ var un = 100, dn = /* @__PURE__ */ new Map();
1276
1292
  function $(e) {
1277
- let t = on.get(e);
1278
- return t || (t = fe({
1293
+ let t = dn.get(e);
1294
+ return t || (t = de({
1279
1295
  text: "",
1280
1296
  sentiment: null,
1281
1297
  loaded: !1,
1282
1298
  pending: !1,
1283
1299
  saving: !1,
1284
1300
  error: ""
1285
- }), on.set(e, t)), t;
1301
+ }), dn.set(e, t)), t;
1286
1302
  }
1287
- function sn(e) {
1288
- let t = Z(), n = () => A(), { user: r } = mt(), { t: a, formatNumber: o } = O(), s = M(() => F(e)), c = M(() => `${r.value?.uid ?? ""}:${s.value}`), l = M(() => {
1303
+ function fn(e) {
1304
+ let t = Z(), n = () => j(), { user: r } = ht(), { t: a, formatNumber: o } = O(), s = N(() => I(e)), c = N(() => `${r.value?.uid ?? ""}:${s.value}`), l = N(() => {
1289
1305
  let e = r.value?.uid, a = s.value;
1290
1306
  return !e || !a ? null : i(t, n(), e, a);
1291
- }), u = M({
1307
+ }), u = N({
1292
1308
  get: () => $(c.value).text,
1293
1309
  set: (e) => {
1294
1310
  $(c.value).text = e;
1295
1311
  }
1296
- }), d = M(() => $(c.value).sentiment), f = M(() => $(c.value).loaded), p = M(() => $(c.value).pending), m = M(() => $(c.value).saving), h = M(() => $(c.value).error);
1312
+ }), d = N(() => $(c.value).sentiment), f = N(() => $(c.value).loaded), p = N(() => $(c.value).pending), m = N(() => $(c.value).saving), h = N(() => $(c.value).error);
1297
1313
  async function g() {
1298
1314
  let e = l.value;
1299
1315
  if (!e) return;
1300
1316
  let t = $(c.value);
1301
1317
  t.pending = !0, t.error = "";
1302
1318
  try {
1303
- let n = (await R(e)).data();
1319
+ let n = (await z(e)).data();
1304
1320
  t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
1305
1321
  } catch (e) {
1306
1322
  t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
@@ -1316,10 +1332,10 @@ function sn(e) {
1316
1332
  let i = $(c.value);
1317
1333
  i.saving = !0, i.error = "";
1318
1334
  try {
1319
- await K(n, {
1335
+ await q(n, {
1320
1336
  text: r,
1321
1337
  sentiment: t,
1322
- updatedAt: G()
1338
+ updatedAt: K()
1323
1339
  }), i.text = r, i.sentiment = t, i.loaded = !0;
1324
1340
  } catch (e) {
1325
1341
  throw i.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
@@ -1333,7 +1349,7 @@ function sn(e) {
1333
1349
  let t = $(c.value);
1334
1350
  t.saving = !0, t.error = "";
1335
1351
  try {
1336
- await pe(e), t.text = "", t.sentiment = null, t.loaded = !0;
1352
+ await fe(e), t.text = "", t.sentiment = null, t.loaded = !0;
1337
1353
  } catch (e) {
1338
1354
  throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
1339
1355
  } finally {
@@ -1353,86 +1369,91 @@ function sn(e) {
1353
1369
  };
1354
1370
  }
1355
1371
  //#endregion
1372
+ //#region src/utils/subscribeCommentRange.ts
1373
+ function pn(e, t, n, r, i, a) {
1374
+ return U(G(u(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 ? [he(r.cursor)] : []), (e) => i(e.docs.map((e) => ({
1375
+ ...e.data(),
1376
+ id: e.id
1377
+ }))), a);
1378
+ }
1379
+ //#endregion
1356
1380
  //#region src/composables/useMemiBoardComments.ts
1357
- var cn = 10, ln = 5, un = 1e3;
1358
- function dn(e, { t, formatNumber: n }) {
1381
+ var mn = 10, hn = 5, gn = 1e3;
1382
+ function _n(e, { t, formatNumber: n }) {
1359
1383
  let r = e.trim();
1360
1384
  if (!r) throw Error(t("comments.bodyRequired"));
1361
- if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(un) }));
1385
+ if (r.length > 1e3) throw Error(t("comments.bodyTooLong", { max: n(gn) }));
1362
1386
  return r;
1363
1387
  }
1364
- function fn(e, t, n = {}) {
1365
- let r = Z(), i = () => A(), { isBoardHidden: a } = Wt(), o = O(), { t: s } = o, c = () => u(r, i()), d = M(() => F(e)), f = M(() => F(t)), p = () => !a(d.value), m = P([]), h = P([]), g = P(n.subscribe !== !1), _ = P([]), v = P(!1), y = P(n.subscribe !== !1), b = P(n.subscribe !== !1), x = null, S, C;
1388
+ function vn(e, t, n = {}) {
1389
+ let r = Z(), i = () => j(), { isBoardHidden: a } = Yt(), o = O(), { t: s } = o, c = () => u(r, i()), d = N(() => I(e)), f = N(() => I(t)), p = () => !a(d.value), m = F([]), h = F(n.subscribe !== !1), g = F([]), _ = F(!1), v = F(n.subscribe !== !1), y = F(n.subscribe !== !1), b = null, x = 0, S = 0, C;
1366
1390
  function w(e) {
1367
1391
  return e.createdAt?.toMillis?.() ?? 0;
1368
1392
  }
1369
- let T = M(() => {
1393
+ let T = N(() => {
1370
1394
  let e = /* @__PURE__ */ new Map();
1371
- for (let t of [
1372
- ...m.value,
1373
- ...h.value,
1374
- ..._.value
1375
- ]) t.id && e.set(t.id, t);
1395
+ for (let t of [...m.value, ...g.value]) t.id && e.set(t.id, t);
1376
1396
  return [...e.values()].sort((e, t) => w(e) - w(t) || String(e.id).localeCompare(String(t.id)));
1377
1397
  });
1378
- I([f, d], async ([e]) => {
1379
- if (_.value = [], m.value = [], h.value = [], x = null, clearTimeout(S), S = void 0, C?.(), C = void 0, y.value = n.subscribe !== !1, n.subscribe !== !1) {
1380
- g.value = !0, b.value = !0;
1398
+ L([f, d], async ([e]) => {
1399
+ let t = ++x;
1400
+ if (S++, _.value = !1, g.value = [], m.value = [], b = null, C?.(), C = void 0, v.value = n.subscribe !== !1, n.subscribe !== !1) {
1401
+ h.value = !0, y.value = !0;
1381
1402
  try {
1382
- let t = z(W(c(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1383
- if (await Promise.all([t.then((t) => {
1384
- if (f.value !== e) return;
1385
- let n = t.docs.map((e) => ({
1403
+ let n = B(G(c(), X("postId", "==", e), W("createdAt", "desc"), W(R(), "desc")));
1404
+ if (await Promise.all([n.then((e) => {
1405
+ if (t !== x) return;
1406
+ let n = e.docs.map((e) => ({
1386
1407
  id: e.id,
1387
1408
  ...e.data()
1388
1409
  }));
1389
- h.value = n.filter((e) => e.parentId === void 0);
1390
- }), D()]), f.value !== e) return;
1410
+ m.value = n.filter((e) => e.parentId === void 0);
1411
+ }), D()]), t !== x) return;
1391
1412
  } finally {
1392
- f.value === e && (g.value = !1, b.value = !1);
1413
+ t === x && (h.value = !1, y.value = !1);
1393
1414
  }
1394
1415
  }
1395
1416
  }, { immediate: !0 });
1396
1417
  function E() {
1397
- C?.(), C = H(W(c(), Y("postId", "==", f.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1398
- let t = new Map(m.value.map((e) => [e.id, e]));
1399
- for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1400
- id: n.doc.id,
1401
- ...n.doc.data()
1402
- });
1403
- m.value = [...t.values()];
1418
+ C?.();
1419
+ let e = ++S;
1420
+ C = pn(r, i(), f.value, {
1421
+ cursor: b,
1422
+ hasMore: v.value
1423
+ }, (t) => {
1424
+ e === S && (g.value = t);
1404
1425
  }, (e) => {
1405
1426
  console.error("[memi-board:comments] onSnapshot failed", e);
1406
1427
  });
1407
1428
  }
1408
1429
  async function D() {
1409
- if (!v.value && y.value) {
1410
- v.value = !0;
1411
- try {
1412
- let e = [
1413
- Y("postId", "==", f.value),
1414
- Y("parentId", "==", null),
1415
- U("createdAt", "asc"),
1416
- U(L(), "asc"),
1417
- ...x ? [q(x)] : [],
1418
- V(11)
1419
- ], t = await z(W(c(), ...e)), n = t.docs.slice(0, cn), r = n.map((e) => ({
1420
- id: e.id,
1421
- ...e.data()
1422
- })), i = new Set(_.value.map((e) => e.id));
1423
- _.value.push(...r.filter((e) => !i.has(e.id))), x = n.at(-1) ?? x, t.size <= cn && (y.value = !1, S = setTimeout(() => {
1424
- S = void 0, E();
1425
- }, 1e3));
1426
- } finally {
1427
- v.value = !1;
1428
- }
1430
+ if (_.value || !v.value) return;
1431
+ let e = x;
1432
+ _.value = !0;
1433
+ try {
1434
+ let t = [
1435
+ X("postId", "==", f.value),
1436
+ X("parentId", "==", null),
1437
+ W("createdAt", "asc"),
1438
+ W(R(), "asc"),
1439
+ ...b ? [J(b)] : [],
1440
+ H(11)
1441
+ ], n = await B(G(c(), ...t));
1442
+ if (e !== x) return;
1443
+ let r = n.docs.slice(0, mn), i = r.map((e) => ({
1444
+ id: e.id,
1445
+ ...e.data()
1446
+ })), a = new Set(g.value.map((e) => e.id));
1447
+ g.value.push(...i.filter((e) => !a.has(e.id))), b = r.at(-1) ?? b, v.value = n.size > mn, E();
1448
+ } finally {
1449
+ e === x && (_.value = !1);
1429
1450
  }
1430
1451
  }
1431
- N(() => {
1432
- clearTimeout(S), C?.();
1452
+ P(() => {
1453
+ x++, S++, C?.();
1433
1454
  });
1434
1455
  async function ee(e) {
1435
- let t = dn(e.body, o), n = X(r), a = he(c());
1456
+ let t = _n(e.body, o), n = ye(r), a = me(c());
1436
1457
  n.set(a, {
1437
1458
  postId: f.value,
1438
1459
  boardId: d.value,
@@ -1450,14 +1471,14 @@ function fn(e, t, n = {}) {
1450
1471
  replyCount: 0,
1451
1472
  likeCount: 0,
1452
1473
  isReply: !1,
1453
- createdAt: G(),
1454
- updatedAt: G()
1455
- }), n.update(l(r, i(), f.value), { commentCount: B(1) }), await n.commit();
1474
+ createdAt: K(),
1475
+ updatedAt: K()
1476
+ }), n.update(l(r, i(), f.value), { commentCount: V(1) }), await n.commit();
1456
1477
  }
1457
1478
  async function te(e) {
1458
- let t = dn(e.body, o);
1479
+ let t = _n(e.body, o);
1459
1480
  if (!e.parent.id) throw Error(s("comments.replyTargetNotFound"));
1460
- let n = X(r), a = he(c()), u = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1481
+ let n = ye(r), a = me(c()), u = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1461
1482
  if (!u) throw Error(s("comments.threadNotFound"));
1462
1483
  let m = Math.min((e.parent.depth ?? 0) + 1, 2);
1463
1484
  n.set(a, {
@@ -1477,116 +1498,121 @@ function fn(e, t, n = {}) {
1477
1498
  replyCount: 0,
1478
1499
  likeCount: 0,
1479
1500
  isReply: !0,
1480
- createdAt: G(),
1481
- updatedAt: G()
1482
- }), n.update(k(r, i(), u), { replyCount: B(1) }), n.update(l(r, i(), f.value), { commentCount: B(1) }), await n.commit();
1501
+ createdAt: K(),
1502
+ updatedAt: K()
1503
+ }), n.update(k(r, i(), u), { replyCount: V(1) }), n.update(l(r, i(), f.value), { commentCount: V(1) }), await n.commit();
1483
1504
  }
1484
- async function ne(e) {
1505
+ async function A(e) {
1485
1506
  if (!e.id) return;
1486
1507
  if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error(s("comments.hasRepliesCannotDelete"));
1487
- await kt(r, i(), e.id);
1488
- let t = X(r);
1489
- t.delete(k(r, i(), e.id)), e.parentId && e.rootId && t.update(k(r, i(), e.rootId), { replyCount: B(-1) }), t.update(l(r, i(), f.value), { commentCount: B(-1) }), await t.commit(), _.value = _.value.filter((t) => t.id !== e.id), h.value = h.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
1508
+ await At(r, i(), e.id);
1509
+ let t = ye(r);
1510
+ t.delete(k(r, i(), e.id)), e.parentId && e.rootId && t.update(k(r, i(), e.rootId), { replyCount: V(-1) }), t.update(l(r, i(), f.value), { commentCount: V(-1) }), await t.commit(), g.value = g.value.filter((t) => t.id !== e.id), m.value = m.value.filter((t) => t.id !== e.id);
1490
1511
  }
1491
- async function re(e, t) {
1492
- let n = dn(t, o);
1493
- await J(k(r, i(), e), {
1512
+ async function ne(e, t) {
1513
+ let n = _n(t, o);
1514
+ await Y(k(r, i(), e), {
1494
1515
  body: n,
1495
- updatedAt: G()
1516
+ updatedAt: K()
1496
1517
  });
1497
1518
  }
1498
- async function ie(e, t, n) {
1499
- await J(k(r, i(), e), {
1519
+ async function re(e, t, n) {
1520
+ await Y(k(r, i(), e), {
1500
1521
  isBlinded: t,
1501
- moderatedAt: G(),
1522
+ moderatedAt: K(),
1502
1523
  moderatedBy: n
1503
1524
  });
1504
1525
  }
1505
1526
  return {
1506
1527
  comments: T,
1507
- commentsPending: M(() => b.value || g.value),
1508
- hasMore: y,
1509
- loadingMore: v,
1528
+ commentsPending: N(() => y.value || h.value),
1529
+ hasMore: v,
1530
+ loadingMore: _,
1510
1531
  loadMore: D,
1511
1532
  addComment: ee,
1512
1533
  addReply: te,
1513
- updateComment: re,
1514
- setCommentBlinded: ie,
1515
- deleteComment: ne
1534
+ updateComment: ne,
1535
+ setCommentBlinded: re,
1536
+ deleteComment: A
1516
1537
  };
1517
1538
  }
1518
- function pn(e, t, n) {
1519
- let r = Z(), i = () => A(), a = () => u(r, i()), o = t.trim(), s = P([]), c = P(!1), l = P(!1), d = P(!0), f = null, p;
1520
- function m(e) {
1539
+ function yn(e, t, n) {
1540
+ let r = Z(), i = () => j(), a = () => u(r, i()), o = t.trim(), s = F([]), c = F(!1), l = F(!1), d = F(!0), f = null, p, m = 0, h = 0;
1541
+ function g(e) {
1521
1542
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
1522
1543
  }
1523
- function h() {
1524
- p ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1525
- let t = new Map(s.value.map((e) => [e.id, e]));
1526
- for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1527
- id: n.doc.id,
1528
- ...n.doc.data()
1529
- });
1530
- s.value = m([...t.values()]);
1544
+ function _() {
1545
+ p?.();
1546
+ let e = ++h;
1547
+ p = pn(r, i(), o, {
1548
+ rootId: n,
1549
+ cursor: f,
1550
+ hasMore: d.value
1551
+ }, (t) => {
1552
+ e === h && (s.value = t);
1531
1553
  }, (e) => {
1532
1554
  console.error("[memi-board:replies] onSnapshot failed", e);
1533
1555
  });
1534
1556
  }
1535
- async function g() {
1536
- if (!c.value && d.value) {
1537
- c.value = !0;
1538
- try {
1539
- let e = [
1540
- Y("postId", "==", o),
1541
- Y("rootId", "==", n),
1542
- Y("isReply", "==", !0),
1543
- U("createdAt", "asc"),
1544
- U(L(), "asc"),
1545
- ...f ? [q(f)] : [],
1546
- V(6)
1547
- ], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, ln), c = i.map((e) => ({
1548
- id: e.id,
1549
- ...e.data()
1550
- })).filter((e) => !r.has(e.id));
1551
- s.value = m([...s.value, ...c]), f = i.at(-1) ?? f, l.value = !0, t.size <= ln && (d.value = !1, h());
1552
- } finally {
1553
- c.value = !1;
1554
- }
1557
+ async function v() {
1558
+ if (c.value || !d.value) return;
1559
+ let e = m;
1560
+ c.value = !0;
1561
+ try {
1562
+ let t = [
1563
+ X("postId", "==", o),
1564
+ X("rootId", "==", n),
1565
+ X("isReply", "==", !0),
1566
+ W("createdAt", "asc"),
1567
+ W(R(), "asc"),
1568
+ ...f ? [J(f)] : [],
1569
+ H(6)
1570
+ ], r = await B(G(a(), ...t));
1571
+ if (e !== m) return;
1572
+ let i = new Set(s.value.map((e) => e.id)), c = r.docs.slice(0, hn), u = c.map((e) => ({
1573
+ id: e.id,
1574
+ ...e.data()
1575
+ })).filter((e) => !i.has(e.id));
1576
+ s.value = g([...s.value, ...u]), f = c.at(-1) ?? f, l.value = !0, d.value = r.size > hn, _();
1577
+ } finally {
1578
+ e === m && (c.value = !1);
1555
1579
  }
1556
1580
  }
1557
- function _() {
1558
- p?.(), p = void 0, s.value = [], f = null, l.value = !1, d.value = !0;
1581
+ function y() {
1582
+ m++, h++, c.value = !1, p?.(), p = void 0, s.value = [], f = null, l.value = !1, d.value = !0;
1559
1583
  }
1560
- async function v() {
1561
- _(), await g();
1584
+ async function b() {
1585
+ y(), await v();
1562
1586
  }
1563
- return N(() => p?.()), {
1587
+ return P(() => {
1588
+ m++, h++, p?.();
1589
+ }), {
1564
1590
  replies: s,
1565
1591
  loading: c,
1566
1592
  loaded: l,
1567
1593
  hasMore: d,
1568
- loadMore: g,
1569
- refresh: v
1594
+ loadMore: v,
1595
+ refresh: b
1570
1596
  };
1571
1597
  }
1572
1598
  //#endregion
1573
1599
  //#region src/composables/useMemiBoardLikes.ts
1574
- function mn(e, t) {
1575
- let n = () => A(), r = Z(), i = be(), { t: a } = O(), o = P(!1), s = P(!1), c;
1600
+ function bn(e, t) {
1601
+ let n = () => j(), r = Z(), i = xe(), { t: a } = O(), o = F(!1), s = F(!1), c;
1576
1602
  function u(e) {
1577
1603
  return d(r, n(), t, e);
1578
1604
  }
1579
- I(i, (e) => {
1605
+ L(i, (e) => {
1580
1606
  if (c?.(), c = void 0, !e) {
1581
1607
  o.value = !1;
1582
1608
  return;
1583
1609
  }
1584
- c = H(u(e.uid), (e) => {
1610
+ c = U(u(e.uid), (e) => {
1585
1611
  o.value = e.exists();
1586
1612
  }, (e) => {
1587
1613
  console.error("[memi-board:likes] onSnapshot failed", e);
1588
1614
  });
1589
- }, { immediate: !0 }), N(() => c?.());
1615
+ }, { immediate: !0 }), P(() => c?.());
1590
1616
  async function f() {
1591
1617
  let c = i.value?.uid;
1592
1618
  if (!c) throw Error(a("likes.signInRequired"));
@@ -1594,12 +1620,12 @@ function mn(e, t) {
1594
1620
  s.value = !0;
1595
1621
  try {
1596
1622
  let i = u(c), a = l(r, n(), t);
1597
- return await ve(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: B(-1) }), !1) : (n.set(i, {
1623
+ return await ve(r, async (n) => (await n.get(i)).exists() ? (n.delete(i), n.update(a, { likeCount: V(-1) }), !1) : (n.set(i, {
1598
1624
  uid: c,
1599
1625
  postId: t,
1600
1626
  boardId: e,
1601
- createdAt: G()
1602
- }), n.update(a, { likeCount: B(1) }), !0));
1627
+ createdAt: K()
1628
+ }), n.update(a, { likeCount: V(1) }), !0));
1603
1629
  } finally {
1604
1630
  s.value = !1;
1605
1631
  }
@@ -1610,11 +1636,11 @@ function mn(e, t) {
1610
1636
  toggleLike: f
1611
1637
  };
1612
1638
  }
1613
- var hn = Symbol.for("memi-board:comment-likes");
1614
- function gn(e, t) {
1615
- let n = () => A(), r = Z(), i = be(), { t: o } = O(), s = M(() => F(e)), c = M(() => F(t)), l = P(/* @__PURE__ */ new Set()), u = P(null), f;
1616
- I([i, c], ([e, t]) => {
1617
- f?.(), f = void 0, l.value = /* @__PURE__ */ new Set(), e && t && (f = H(W(a(r, n()), Y("uid", "==", e.uid), Y("postId", "==", t), Y("target", "==", "comment")), (e) => {
1639
+ var xn = Symbol.for("memi-board:comment-likes");
1640
+ function Sn(e, t) {
1641
+ let n = () => j(), r = Z(), i = xe(), { t: o } = O(), s = N(() => I(e)), c = N(() => I(t)), l = F(/* @__PURE__ */ new Set()), u = F(null), f;
1642
+ L([i, c], ([e, t]) => {
1643
+ f?.(), f = void 0, l.value = /* @__PURE__ */ new Set(), e && t && (f = U(G(a(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
1618
1644
  let t = /* @__PURE__ */ new Set();
1619
1645
  for (let n of e.docs) {
1620
1646
  let e = n.data().commentId;
@@ -1624,7 +1650,7 @@ function gn(e, t) {
1624
1650
  }, (e) => {
1625
1651
  console.error("[memi-board:comment-likes] onSnapshot failed", e);
1626
1652
  }));
1627
- }, { immediate: !0 }), N(() => f?.());
1653
+ }, { immediate: !0 }), P(() => f?.());
1628
1654
  function p(e) {
1629
1655
  return !!e && l.value.has(e);
1630
1656
  }
@@ -1648,7 +1674,7 @@ function gn(e, t) {
1648
1674
  boardId: s.value,
1649
1675
  commentId: e,
1650
1676
  target: "comment",
1651
- createdAt: G()
1677
+ createdAt: K()
1652
1678
  }), n.update(a, { likeCount: u + 1 }), !0);
1653
1679
  }), f = new Set(l.value);
1654
1680
  return u ? f.add(e) : f.delete(e), l.value = f, u;
@@ -1664,7 +1690,7 @@ function gn(e, t) {
1664
1690
  }
1665
1691
  //#endregion
1666
1692
  //#region src/composables/useMemiBoardReports.ts
1667
- var _n = 200, vn = [
1693
+ var Cn = 200, wn = [
1668
1694
  {
1669
1695
  value: "spam",
1670
1696
  label: "스팸·광고"
@@ -1686,34 +1712,34 @@ var _n = 200, vn = [
1686
1712
  label: "기타"
1687
1713
  }
1688
1714
  ];
1689
- function yn(e, t) {
1690
- return vn.some((t) => t.value === e) ? w(t, `reports.reason.${e}`) : e || w(t, "reports.fallbackLabel");
1715
+ function Tn(e, t) {
1716
+ return wn.some((t) => t.value === e) ? w(t, `reports.reason.${e}`) : e || w(t, "reports.fallbackLabel");
1691
1717
  }
1692
- function bn(e, n) {
1693
- let r = () => A(), i = Z(), a = be(), { t: o } = O(), s = P(!1), c = P(!1), l;
1694
- I(a, (e) => {
1718
+ function En(e, n) {
1719
+ let r = () => j(), i = Z(), a = xe(), { t: o } = O(), s = F(!1), c = F(!1), l;
1720
+ L(a, (e) => {
1695
1721
  if (l?.(), l = void 0, !e) {
1696
1722
  s.value = !1;
1697
1723
  return;
1698
1724
  }
1699
- l = H(t(i, r(), n, e.uid), (e) => {
1725
+ l = U(t(i, r(), n, e.uid), (e) => {
1700
1726
  s.value = e.exists();
1701
1727
  }, (e) => {
1702
1728
  console.error("[memi-board:reports] onSnapshot failed", e);
1703
1729
  });
1704
- }, { immediate: !0 }), N(() => l?.());
1730
+ }, { immediate: !0 }), P(() => l?.());
1705
1731
  async function u(l) {
1706
1732
  let u = a.value?.uid;
1707
1733
  if (!u) throw Error(o("reports.signInRequired"));
1708
1734
  if (c.value) return;
1709
1735
  if (l.authorUid && l.authorUid === u) throw Error(o("reports.cannotReportOwn"));
1710
- let d = vn.some((e) => e.value === l.reason) ? l.reason : null;
1736
+ let d = wn.some((e) => e.value === l.reason) ? l.reason : null;
1711
1737
  if (!d) throw Error(o("reports.reasonRequired"));
1712
1738
  let f = (l.detail || "").trim();
1713
1739
  if (f.length > 200) throw Error(o("reports.detailTooLong", { max: 200 }));
1714
1740
  c.value = !0;
1715
1741
  try {
1716
- await K(t(i, r(), n, u), {
1742
+ await q(t(i, r(), n, u), {
1717
1743
  uid: u,
1718
1744
  target: "post",
1719
1745
  postId: n,
@@ -1724,7 +1750,7 @@ function bn(e, n) {
1724
1750
  postTitle: (l.postTitle || "").trim().slice(0, 200),
1725
1751
  authorUid: l.authorUid || "",
1726
1752
  authorName: l.authorName ?? null,
1727
- createdAt: G()
1753
+ createdAt: K()
1728
1754
  }), s.value = !0;
1729
1755
  } finally {
1730
1756
  c.value = !1;
@@ -1736,9 +1762,9 @@ function bn(e, n) {
1736
1762
  submitReport: u
1737
1763
  };
1738
1764
  }
1739
- var xn = 20;
1740
- function Sn(e = {}) {
1741
- let n = () => A(), r = Z(), { canManageContent: i, canManageBoard: a } = mt(), o = e.pageSize ?? xn, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
1765
+ var Dn = 20;
1766
+ function On(e = {}) {
1767
+ let n = () => j(), r = Z(), { canManageContent: i, canManageBoard: a } = ht(), o = e.pageSize ?? Dn, c = N(() => I(e.boardId)?.trim() || ""), l = F([]), u = F(!0), d = F(!1), f = F(!1), p = F(""), m = null, h;
1742
1768
  function g(e) {
1743
1769
  return {
1744
1770
  id: e.id,
@@ -1751,19 +1777,19 @@ function Sn(e = {}) {
1751
1777
  return;
1752
1778
  }
1753
1779
  u.value = !0;
1754
- let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")];
1755
- h = H(W(s(r, n()), ...e, U("createdAt", "desc"), U(L(), "desc"), V(o)), (e) => {
1780
+ let e = c.value ? [X("status", "==", "open"), X("boardId", "==", c.value)] : [X("status", "==", "open"), X("target", "==", "post")];
1781
+ h = U(G(s(r, n()), ...e, W("createdAt", "desc"), W(R(), "desc"), H(o)), (e) => {
1756
1782
  l.value = e.docs.map(g).filter((e) => a(e.boardId)), m = e.docs.at(-1) ?? null, f.value = e.docs.length >= o, u.value = !1;
1757
1783
  }, (e) => {
1758
1784
  console.error("[memi-board:report-queue] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
1759
1785
  });
1760
1786
  }
1761
- I([i, c], _, { immediate: !0 }), N(() => h?.());
1787
+ L([i, c], _, { immediate: !0 }), P(() => h?.());
1762
1788
  async function v() {
1763
1789
  if (!d.value && f.value && m && i.value) {
1764
1790
  d.value = !0, p.value = "";
1765
1791
  try {
1766
- let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")], t = await z(W(s(r, n()), ...e, U("createdAt", "desc"), U(L(), "desc"), q(m), V(o + 1))), i = t.docs.slice(0, o), u = new Set(l.value.map((e) => e.id));
1792
+ let e = c.value ? [X("status", "==", "open"), X("boardId", "==", c.value)] : [X("status", "==", "open"), X("target", "==", "post")], t = await B(G(s(r, n()), ...e, W("createdAt", "desc"), W(R(), "desc"), J(m), H(o + 1))), i = t.docs.slice(0, o), u = new Set(l.value.map((e) => e.id));
1767
1793
  l.value.push(...i.map(g).filter((e) => !u.has(e.id) && a(e.boardId))), m = i.at(-1) ?? m, f.value = t.docs.length > o;
1768
1794
  } catch (e) {
1769
1795
  p.value = e.message || String(e);
@@ -1773,10 +1799,10 @@ function Sn(e = {}) {
1773
1799
  }
1774
1800
  }
1775
1801
  async function y(e, i) {
1776
- e.id && a(e.boardId) && await J(t(r, n(), e.postId, e.uid), {
1802
+ e.id && a(e.boardId) && await Y(t(r, n(), e.postId, e.uid), {
1777
1803
  status: i,
1778
- reviewedAt: G(),
1779
- reviewedBy: be().value?.uid || ""
1804
+ reviewedAt: K(),
1805
+ reviewedBy: xe().value?.uid || ""
1780
1806
  });
1781
1807
  }
1782
1808
  return {
@@ -1792,8 +1818,8 @@ function Sn(e = {}) {
1792
1818
  }
1793
1819
  //#endregion
1794
1820
  //#region src/composables/useMemiBoardViews.ts
1795
- function Cn() {
1796
- let e = () => A(), t = Z();
1821
+ function kn() {
1822
+ let e = () => j(), t = Z();
1797
1823
  function n(t, n) {
1798
1824
  let { postsCollection: r } = e();
1799
1825
  return `memi-board:viewed:${r}:${t}:${n}`;
@@ -1816,7 +1842,7 @@ function Cn() {
1816
1842
  if (s && c && !r(s, c)) {
1817
1843
  i(s, c);
1818
1844
  try {
1819
- await J(l(t, e(), c), { viewCount: B(1) });
1845
+ await Y(l(t, e(), c), { viewCount: V(1) });
1820
1846
  } catch (e) {
1821
1847
  try {
1822
1848
  sessionStorage.removeItem(n(s, c));
@@ -1832,7 +1858,7 @@ function Cn() {
1832
1858
  }
1833
1859
  //#endregion
1834
1860
  //#region src/utils/moderation-prompt.ts
1835
- var wn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", Tn = {
1861
+ var An = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", jn = {
1836
1862
  ko: "Korean",
1837
1863
  en: "English",
1838
1864
  ja: "Japanese",
@@ -1844,12 +1870,12 @@ var wn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
1844
1870
  ar: "Arabic",
1845
1871
  id: "Indonesian"
1846
1872
  };
1847
- function En(e) {
1848
- let t = Tn[e] ?? "English", n = D(e).name;
1873
+ function Mn(e) {
1874
+ let t = jn[e] ?? "English", n = D(e).name;
1849
1875
  return t === n ? t : `${t} (${n})`;
1850
1876
  }
1851
- function Dn(e) {
1852
- let t = En(e);
1877
+ function Nn(e) {
1878
+ let t = Mn(e);
1853
1879
  return `You are a strict moderator reviewing content for a message board.
1854
1880
  If the text below contains profanity, vulgar slang, sexual harassment, hate speech, threats, spam, or repetitive/flooding advertisements, you MUST reject it (flagged=true).
1855
1881
 
@@ -1876,17 +1902,17 @@ Output JSON only:
1876
1902
  Do not quote the offensive words at length; clearly state the type (profanity/hate/spam, etc.).
1877
1903
  - If flagged=false, category is "none" and reason is ""`;
1878
1904
  }
1879
- function On(e = "ko") {
1880
- return e === "ko" ? wn : Dn(e);
1905
+ function Pn(e = "ko") {
1906
+ return e === "ko" ? An : Nn(e);
1881
1907
  }
1882
- function kn(e, t = "ko") {
1908
+ function Fn(e, t = "ko") {
1883
1909
  let n = `"""${e.slice(0, 4e3)}"""`;
1884
1910
  return t === "ko" ? `심사할 텍스트:\n${n}` : `Text to review:\n${n}`;
1885
1911
  }
1886
- function An(e = "ko") {
1912
+ function In(e = "ko") {
1887
1913
  return e === "ko" ? "이 이미지를 심사해 주세요." : "Please review this image.";
1888
1914
  }
1889
- var jn = {
1915
+ var Ln = {
1890
1916
  abuse: "욕설·비속어·모욕",
1891
1917
  spam: "스팸·광고",
1892
1918
  adult: "선정적·성인 내용",
@@ -1894,10 +1920,10 @@ var jn = {
1894
1920
  other: "부적절한 내용",
1895
1921
  none: ""
1896
1922
  };
1897
- function Mn(e, t = "ko") {
1898
- return !e || e === "none" ? "" : e in jn ? w(t, `moderationPrompt.categories.${e}`) : e;
1923
+ function Rn(e, t = "ko") {
1924
+ return !e || e === "none" ? "" : e in Ln ? w(t, `moderationPrompt.categories.${e}`) : e;
1899
1925
  }
1900
- function Nn(e) {
1926
+ function zn(e) {
1901
1927
  let t = e.locale ?? "ko";
1902
1928
  if (e.via === "local" && e.localHit) {
1903
1929
  let n = e.localHit.trim();
@@ -1905,10 +1931,10 @@ function Nn(e) {
1905
1931
  }
1906
1932
  let n = (e.reason || "").trim();
1907
1933
  if (n) return n;
1908
- let r = e.category ? Mn(e.category, t) : "";
1934
+ let r = e.category ? Rn(e.category, t) : "";
1909
1935
  return r ? w(t, "moderationPrompt.categoryBlocked", { category: r }) : w(t, "moderationPrompt.genericBlocked");
1910
1936
  }
1911
- var Pn = [
1937
+ var Bn = [
1912
1938
  "씨발",
1913
1939
  "시발",
1914
1940
  "시이발",
@@ -1934,10 +1960,10 @@ var Pn = [
1934
1960
  "bitch",
1935
1961
  "asshole"
1936
1962
  ];
1937
- function Fn(e) {
1963
+ function Vn(e) {
1938
1964
  return new RegExp(e.join("|"), "i");
1939
1965
  }
1940
- var In = /* @__PURE__ */ new Set([
1966
+ var Hn = /* @__PURE__ */ new Set([
1941
1967
  "none",
1942
1968
  "abuse",
1943
1969
  "spam",
@@ -1945,7 +1971,7 @@ var In = /* @__PURE__ */ new Set([
1945
1971
  "violence",
1946
1972
  "other"
1947
1973
  ]);
1948
- function Ln(e) {
1974
+ function Un(e) {
1949
1975
  let t = e.trim();
1950
1976
  if (!t) return null;
1951
1977
  let n;
@@ -1963,7 +1989,7 @@ function Ln(e) {
1963
1989
  if (typeof n != "object" || !n) return null;
1964
1990
  let r = n;
1965
1991
  if (typeof r.flagged != "boolean") return null;
1966
- let i = typeof r.category == "string" && In.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1992
+ let i = typeof r.category == "string" && Hn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1967
1993
  return {
1968
1994
  flagged: r.flagged,
1969
1995
  category: i,
@@ -1972,20 +1998,20 @@ function Ln(e) {
1972
1998
  }
1973
1999
  //#endregion
1974
2000
  //#region src/composables/useMemiBoardModeration.ts
1975
- var Rn = {
2001
+ var Wn = {
1976
2002
  flagged: !1,
1977
2003
  category: "none",
1978
2004
  reason: "",
1979
2005
  via: "empty"
1980
2006
  };
1981
- function zn(e = {}) {
1982
- let t = O(), n = M(() => E(F(e.locale)) ?? t.locale.value), r = de().moderation ?? {}, i = Se(), a = (e) => w(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = mt(), l = Fn([...Pn, ...r.localBlocklist ?? []]);
2007
+ function Gn(e = {}) {
2008
+ let t = O(), n = N(() => E(I(e.locale)) ?? t.locale.value), r = ue().moderation ?? {}, i = Ce(), a = (e) => w(n.value, e), { isWriteRestricted: o, restrictedMessage: s, recordContentModerationBlock: c } = ht(), l = Vn([...Bn, ...r.localBlocklist ?? []]);
1983
2009
  function u(e) {
1984
2010
  let t = e.match(l);
1985
2011
  return t ? {
1986
2012
  flagged: !0,
1987
2013
  category: "abuse",
1988
- reason: Nn({
2014
+ reason: zn({
1989
2015
  via: "local",
1990
2016
  localHit: t[0] || "",
1991
2017
  category: "abuse"
@@ -1994,13 +2020,13 @@ function zn(e = {}) {
1994
2020
  } : null;
1995
2021
  }
1996
2022
  function d() {
1997
- let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Ie(i, {
1998
- backend: new Fe(),
2023
+ let e = r.model ?? "gemini-3.5-flash-lite", t = r.useLimitedUseAppCheckTokens !== !1, a = Le(i, {
2024
+ backend: new Ie(),
1999
2025
  useLimitedUseAppCheckTokens: t
2000
2026
  });
2001
- return Le(a, {
2027
+ return Re(a, {
2002
2028
  model: e,
2003
- systemInstruction: On(n.value),
2029
+ systemInstruction: Pn(n.value),
2004
2030
  generationConfig: {
2005
2031
  temperature: 0,
2006
2032
  maxOutputTokens: 256,
@@ -2029,7 +2055,7 @@ function zn(e = {}) {
2029
2055
  };
2030
2056
  }
2031
2057
  function p(e, t = "") {
2032
- let n = Nn({
2058
+ let n = zn({
2033
2059
  reason: e.reason,
2034
2060
  category: e.category,
2035
2061
  via: e.via
@@ -2057,26 +2083,26 @@ function zn(e = {}) {
2057
2083
  };
2058
2084
  let i = e.trim();
2059
2085
  if (!i) return {
2060
- ...Rn,
2086
+ ...Wn,
2061
2087
  via: "empty"
2062
2088
  };
2063
2089
  if (t?.skipFilter) return {
2064
- ...Rn,
2090
+ ...Wn,
2065
2091
  via: "admin-override"
2066
2092
  };
2067
2093
  let c = u(i);
2068
2094
  if (c) return m(c);
2069
2095
  if (r.enabled === !1) return {
2070
- ...Rn,
2096
+ ...Wn,
2071
2097
  via: "disabled"
2072
2098
  };
2073
2099
  try {
2074
- let e = Ln((await d().generateContent(kn(i, n.value))).response.text());
2100
+ let e = Un((await d().generateContent(Fn(i, n.value))).response.text());
2075
2101
  if (!e) return f();
2076
2102
  let t = {
2077
2103
  flagged: e.flagged,
2078
2104
  category: e.category,
2079
- reason: Nn({
2105
+ reason: zn({
2080
2106
  reason: e.reason,
2081
2107
  category: e.category,
2082
2108
  via: "ai"
@@ -2096,22 +2122,22 @@ function zn(e = {}) {
2096
2122
  via: "restricted"
2097
2123
  };
2098
2124
  if (r.enabled === !1 || !r.moderateImages) return {
2099
- ...Rn,
2125
+ ...Wn,
2100
2126
  via: "disabled"
2101
2127
  };
2102
2128
  try {
2103
2129
  let t = await new Promise((t, n) => {
2104
2130
  let r = new FileReader();
2105
2131
  r.onload = () => t(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(e);
2106
- }), r = Ln((await d().generateContent([{ inlineData: {
2132
+ }), r = Un((await d().generateContent([{ inlineData: {
2107
2133
  mimeType: e.type || "image/jpeg",
2108
2134
  data: t
2109
- } }, An(n.value)])).response.text());
2135
+ } }, In(n.value)])).response.text());
2110
2136
  if (!r) return f();
2111
2137
  let i = {
2112
2138
  flagged: r.flagged,
2113
2139
  category: r.category,
2114
- reason: Nn({
2140
+ reason: zn({
2115
2141
  reason: r.reason,
2116
2142
  category: r.category,
2117
2143
  via: "ai"
@@ -2130,7 +2156,7 @@ function zn(e = {}) {
2130
2156
  }
2131
2157
  //#endregion
2132
2158
  //#region src/composables/useMemiBoardUsers.ts
2133
- var Bn = [
2159
+ var Kn = [
2134
2160
  "admin",
2135
2161
  "staff",
2136
2162
  "user"
@@ -2139,13 +2165,13 @@ var Bn = [
2139
2165
  label: w("ko", `common.role.${e}`),
2140
2166
  description: w("ko", `boardUsers.roleDescription.${e}`)
2141
2167
  }));
2142
- function Vn(t = {}) {
2143
- let n = () => A(), i = Z(), { t: a } = O(), o = M(() => F(t.enabled) ?? !0 ? e(i, n()) : null), s = ye(o, { ssrKey: r(n(), "users-management") });
2168
+ function qn(t = {}) {
2169
+ let n = () => j(), i = Z(), { t: a } = O(), o = N(() => I(t.enabled) ?? !0 ? e(i, n()) : null), s = be(o, { ssrKey: r(n(), "users-management") });
2144
2170
  async function c(e, t) {
2145
- if (!Bn.some((e) => e.value === t)) throw Error(a("boardUsers.unsupportedRole"));
2146
- await J(f(i, n(), e), {
2171
+ if (!Kn.some((e) => e.value === t)) throw Error(a("boardUsers.unsupportedRole"));
2172
+ await Y(f(i, n(), e), {
2147
2173
  role: t,
2148
- updatedAt: G()
2174
+ updatedAt: K()
2149
2175
  });
2150
2176
  }
2151
2177
  return {
@@ -2156,11 +2182,11 @@ function Vn(t = {}) {
2156
2182
  }
2157
2183
  //#endregion
2158
2184
  //#region src/composables/useMemiBoardUserProfile.ts
2159
- function Hn(e) {
2160
- let t = Z(), n = () => A(), r = M(() => F(e)), i = M(() => {
2185
+ function Jn(e) {
2186
+ let t = Z(), n = () => j(), r = N(() => I(e)), i = N(() => {
2161
2187
  let e = r.value;
2162
2188
  return e ? f(t, n(), e) : null;
2163
- }), { data: a, pending: o } = xe(i);
2189
+ }), { data: a, pending: o } = Se(i);
2164
2190
  return {
2165
2191
  profile: a,
2166
2192
  profilePending: o
@@ -2168,7 +2194,7 @@ function Hn(e) {
2168
2194
  }
2169
2195
  //#endregion
2170
2196
  //#region src/utils/section.ts
2171
- var Un = [
2197
+ var Yn = [
2172
2198
  3,
2173
2199
  4,
2174
2200
  5,
@@ -2178,7 +2204,7 @@ var Un = [
2178
2204
  9,
2179
2205
  12
2180
2206
  ];
2181
- function Wn(e) {
2207
+ function Xn(e) {
2182
2208
  return [
2183
2209
  {
2184
2210
  value: "list",
@@ -2194,7 +2220,7 @@ function Wn(e) {
2194
2220
  }
2195
2221
  ];
2196
2222
  }
2197
- function Gn(e) {
2223
+ function Zn(e) {
2198
2224
  return [{
2199
2225
  value: "latest",
2200
2226
  label: w(e, "sectionUtil.sort.latest")
@@ -2203,18 +2229,18 @@ function Gn(e) {
2203
2229
  label: w(e, "sectionUtil.sort.likes")
2204
2230
  }];
2205
2231
  }
2206
- var Kn = Wn("ko"), qn = Gn("ko");
2207
- function Jn(e) {
2232
+ var Qn = Xn("ko"), $n = Zn("ko");
2233
+ function er(e) {
2208
2234
  return e === "list" ? 5 : 4;
2209
2235
  }
2210
- function Yn(e, t) {
2236
+ function tr(e, t) {
2211
2237
  let n = Math.floor(Number(t));
2212
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : Jn(e);
2238
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : er(e);
2213
2239
  }
2214
- function Xn(e) {
2240
+ function nr(e) {
2215
2241
  return e === 12 ? null : 12 - e;
2216
2242
  }
2217
- function Zn(e) {
2243
+ function rr(e) {
2218
2244
  return {
2219
2245
  3: "col-span-12 md:col-span-3",
2220
2246
  4: "col-span-12 md:col-span-4",
@@ -2226,36 +2252,36 @@ function Zn(e) {
2226
2252
  12: "col-span-12"
2227
2253
  }[e];
2228
2254
  }
2229
- function Qn(e) {
2255
+ function ir(e) {
2230
2256
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2231
2257
  }
2232
- function $n(e) {
2258
+ function ar(e) {
2233
2259
  let t = e.boardId || e.category || "free";
2234
2260
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2235
2261
  }
2236
- function er(e) {
2262
+ function or(e) {
2237
2263
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2238
2264
  }
2239
- function tr(e) {
2265
+ function sr(e) {
2240
2266
  if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
2241
2267
  }
2242
- function nr(e) {
2268
+ function cr(e) {
2243
2269
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2244
2270
  }
2245
- function rr(e, t) {
2271
+ function lr(e, t) {
2246
2272
  return e.title?.trim() || e.summary || w(t, "sectionUtil.untitledPost");
2247
2273
  }
2248
- function ir(e) {
2274
+ function ur(e) {
2249
2275
  return e === "image" || e === "video" ? "media" : "list";
2250
2276
  }
2251
- function ar(e, t) {
2252
- return w(t, ir(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2277
+ function dr(e, t) {
2278
+ return w(t, ur(e) === "media" ? "sectionUtil.view.media" : "sectionUtil.view.list");
2253
2279
  }
2254
- function or(e, t) {
2280
+ function fr(e, t) {
2255
2281
  return e ? t(e)?.listView ?? "default" : "default";
2256
2282
  }
2257
- function sr(e, t) {
2258
- let n = e.map((e) => or(e.boardId, t));
2283
+ function pr(e, t) {
2284
+ let n = e.map((e) => fr(e.boardId, t));
2259
2285
  if (!n.length) return "default";
2260
2286
  if (n.every((e) => e === n[0])) return n[0];
2261
2287
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2263,27 +2289,27 @@ function sr(e, t) {
2263
2289
  }
2264
2290
  //#endregion
2265
2291
  //#region src/composables/useMemiBoardSections.ts
2266
- var cr = [];
2267
- function lr(e) {
2292
+ var mr = [];
2293
+ function hr(e) {
2268
2294
  let t = Number(e.cols);
2269
- if (Un.includes(t)) return t;
2295
+ if (Yn.includes(t)) return t;
2270
2296
  let n = e.width;
2271
2297
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2272
2298
  }
2273
- function ur(e) {
2299
+ function gr(e) {
2274
2300
  let t = e.height ?? e.size;
2275
2301
  return t === "sm" || t === "lg" ? t : "md";
2276
2302
  }
2277
- function dr(e) {
2303
+ function _r(e) {
2278
2304
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2279
2305
  }
2280
- function fr(e) {
2306
+ function vr(e) {
2281
2307
  return e.sort === "likes" ? "likes" : "latest";
2282
2308
  }
2283
- function pr(e) {
2309
+ function yr(e) {
2284
2310
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2285
2311
  }
2286
- function mr(e, t) {
2312
+ function br(e, t) {
2287
2313
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2288
2314
  for (let e of n) {
2289
2315
  let n = String(e || "").trim();
@@ -2291,47 +2317,47 @@ function mr(e, t) {
2291
2317
  }
2292
2318
  return r;
2293
2319
  }
2294
- function hr(e, t) {
2320
+ function xr(e, t) {
2295
2321
  return w(t, "sections.defaultTitle", { n: e + 1 });
2296
2322
  }
2297
- function gr(e, t, n) {
2298
- let r = dr(e), i = Yn(r, e.count);
2323
+ function Sr(e, t, n) {
2324
+ let r = _r(e), i = tr(r, e.count);
2299
2325
  return {
2300
2326
  id: String(e.id || `box-${t + 1}`),
2301
- title: String(e.title || hr(t, n)),
2327
+ title: String(e.title || xr(t, n)),
2302
2328
  showTitle: e.showTitle !== !1,
2303
2329
  kind: r,
2304
- boardId: pr(e),
2330
+ boardId: yr(e),
2305
2331
  count: i,
2306
- postIds: mr(e, i),
2307
- sort: fr(e),
2308
- cols: lr(e),
2309
- height: ur(e),
2332
+ postIds: br(e, i),
2333
+ sort: vr(e),
2334
+ cols: hr(e),
2335
+ height: gr(e),
2310
2336
  order: t
2311
2337
  };
2312
2338
  }
2313
- function _r() {
2314
- let { locale: e } = O(), t = Z(), n = he(t, "settings", "homeLayout"), { data: r, pending: i } = xe(n), a = M(() => (Array.isArray(r.value?.blocks) ? r.value.blocks : cr).map((t, n) => gr(t, n, e.value)));
2339
+ function Cr() {
2340
+ let { locale: e } = O(), t = Z(), n = me(t, "settings", "homeLayout"), { data: r, pending: i } = Se(n), a = N(() => (Array.isArray(r.value?.blocks) ? r.value.blocks : mr).map((t, n) => Sr(t, n, e.value)));
2315
2341
  async function o(t) {
2316
- await K(n, {
2342
+ await q(n, {
2317
2343
  columns: 12,
2318
2344
  blocks: t.map((t, n) => {
2319
- let r = t, i = dr(r), a = Yn(i, t.count);
2345
+ let r = t, i = _r(r), a = tr(i, t.count);
2320
2346
  return {
2321
2347
  id: t.id,
2322
- title: t.title.trim() || hr(n, e.value),
2348
+ title: t.title.trim() || xr(n, e.value),
2323
2349
  showTitle: t.showTitle !== !1,
2324
2350
  kind: i,
2325
- boardId: pr(r),
2351
+ boardId: yr(r),
2326
2352
  count: a,
2327
- postIds: mr(r, a),
2328
- sort: fr(r),
2329
- cols: lr(r),
2330
- height: ur(r),
2353
+ postIds: br(r, a),
2354
+ sort: vr(r),
2355
+ cols: hr(r),
2356
+ height: gr(r),
2331
2357
  order: n
2332
2358
  };
2333
2359
  }),
2334
- updatedAt: G()
2360
+ updatedAt: K()
2335
2361
  });
2336
2362
  }
2337
2363
  return {
@@ -2339,28 +2365,28 @@ function _r() {
2339
2365
  pending: i,
2340
2366
  sections: a,
2341
2367
  blocks: a,
2342
- hasSavedLayout: M(() => Array.isArray(r.value?.blocks)),
2368
+ hasSavedLayout: N(() => Array.isArray(r.value?.blocks)),
2343
2369
  saveSections: o,
2344
2370
  saveBlocks: o
2345
2371
  };
2346
2372
  }
2347
2373
  //#endregion
2348
2374
  //#region src/composables/useMemiBoardMiniPosts.ts
2349
- function vr(e, t) {
2350
- let n = Z(), i = () => A(), a = F(e)?.trim() || "all", o = M(() => {
2351
- let r = Math.max(1, Math.floor(Number(F(t)) || 5)), a = F(e)?.trim() || "", o = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
2352
- return a && o.unshift(Y("boardId", "==", a)), W(j(n, i()), ...o, U("createdAt", "desc"), V(r));
2353
- }), { data: s, pending: c, error: l } = ye(o, { ssrKey: r(i(), `mini-list/${a}`) });
2375
+ function wr(e, t) {
2376
+ let n = Z(), i = () => j(), a = I(e)?.trim() || "all", o = N(() => {
2377
+ let r = Math.max(1, Math.floor(Number(I(t)) || 5)), a = I(e)?.trim() || "", o = [X("isPublished", "==", !0), X("listed", "==", !0)];
2378
+ return a && o.unshift(X("boardId", "==", a)), G(M(n, i()), ...o, W("createdAt", "desc"), H(r));
2379
+ }), { data: s, pending: c, error: l } = be(o, { ssrKey: r(i(), `mini-list/${a}`) });
2354
2380
  return {
2355
- posts: M(() => s.value ?? []),
2381
+ posts: N(() => s.value ?? []),
2356
2382
  pending: c,
2357
2383
  error: l
2358
2384
  };
2359
2385
  }
2360
- function yr(e) {
2361
- let t = Z(), n = () => A(), i = M(() => [...new Set(F(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), a = i.value.join(",") || "none", o = M(() => i.value.length ? W(j(t, n()), Y(L(), "in", i.value)) : null), { data: s, pending: c, error: l } = ye(o, { ssrKey: r(n(), `mini-picked/${a}`) });
2386
+ function Tr(e) {
2387
+ let t = Z(), n = () => j(), i = N(() => [...new Set(I(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), a = i.value.join(",") || "none", o = N(() => i.value.length ? G(M(t, n()), X(R(), "in", i.value)) : null), { data: s, pending: c, error: l } = be(o, { ssrKey: r(n(), `mini-picked/${a}`) });
2362
2388
  return {
2363
- posts: M(() => {
2389
+ posts: N(() => {
2364
2390
  let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
2365
2391
  for (let n of i.value) {
2366
2392
  let r = e.get(n);
@@ -2374,7 +2400,16 @@ function yr(e) {
2374
2400
  }
2375
2401
  //#endregion
2376
2402
  //#region src/data/versionHistory.ts
2377
- var br = [
2403
+ var Er = [
2404
+ {
2405
+ version: "0.45.1",
2406
+ date: "2026-10-15",
2407
+ highlights: [
2408
+ "일반 계정 글쓰기·초안 조회와 태그 입력을 고쳤어요",
2409
+ "댓글·답글 수정과 좋아요가 다른 사용자에게 바로 반영돼요",
2410
+ "글 삭제·첨부파일 보호·게시판 공개 범위 변경을 보강했어요"
2411
+ ]
2412
+ },
2378
2413
  {
2379
2414
  version: "0.45.0",
2380
2415
  date: "2026-10-01",
@@ -2659,7 +2694,7 @@ var br = [
2659
2694
  highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
2660
2695
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
2661
2696
  }
2662
- ], xr = 10, Sr = 864e5, Cr = 200, wr = {
2697
+ ], Dr = 10, Or = 864e5, kr = 200, Ar = {
2663
2698
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
2664
2699
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
2665
2700
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -2668,7 +2703,7 @@ var br = [
2668
2703
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
2669
2704
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
2670
2705
  };
2671
- function Tr(e) {
2706
+ function jr(e) {
2672
2707
  return {
2673
2708
  proofread: "Fix only spelling, spacing, and grammar errors. Do not change the meaning or tone.",
2674
2709
  polish: "Refine the text into more natural, readable sentences while keeping the meaning and the author's tone.",
@@ -2679,34 +2714,34 @@ function Tr(e) {
2679
2714
  title: `Create one concise title in ${e} that represents the body. Output only the title, without quotes or explanation.`
2680
2715
  };
2681
2716
  }
2682
- function Er(e, t, n) {
2683
- let r = t === "ko", i = En(t);
2717
+ function Mr(e, t, n) {
2718
+ let r = t === "ko", i = Mn(t);
2684
2719
  if (e.action !== "custom") {
2685
- if (r) return wr[e.action];
2686
- let t = Tr(i)[e.action];
2720
+ if (r) return Ar[e.action];
2721
+ let t = jr(i)[e.action];
2687
2722
  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}.`;
2688
2723
  }
2689
- let a = e.customInstruction?.trim().slice(0, Cr);
2724
+ let a = e.customInstruction?.trim().slice(0, kr);
2690
2725
  if (!a) throw Error(n("writingAssistant.customRequired"));
2691
2726
  return r ? `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${a}` : `Rewrite the text applying the following style instruction while keeping the meaning: ${a}`;
2692
2727
  }
2693
- function Dr(e) {
2728
+ function Nr(e) {
2694
2729
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
2695
2730
  }
2696
- function Or(e) {
2731
+ function Pr(e) {
2697
2732
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
2698
2733
  }
2699
- function kr(e = {}) {
2700
- let t = O(), n = M(() => E(F(e.locale)) ?? t.locale.value), r = (e, t) => w(n.value, e, t), i = Se(), a = Z(), o = be(), s = de();
2734
+ function Fr(e = {}) {
2735
+ let t = O(), n = N(() => E(I(e.locale)) ?? t.locale.value), r = (e, t) => w(n.value, e, t), i = Ce(), a = Z(), o = xe(), s = ue();
2701
2736
  async function c() {
2702
2737
  let e = o.value?.uid;
2703
2738
  if (!e) throw Error(r("writingAssistant.loginRequired"));
2704
2739
  await ve(a, async (t) => {
2705
- let n = f(a, A(), e), i = await t.get(n);
2740
+ let n = f(a, j(), e), i = await t.get(n);
2706
2741
  if (!i.exists()) throw Error(r("writingAssistant.userMissing"));
2707
- let o = i.data(), s = Or(o.aiWritingWindowAt), c = Math.max(0, Math.floor(Number(o.aiWritingCount) || 0)), l = Date.now(), u = s != null && l - s < Sr;
2742
+ let o = i.data(), s = Pr(o.aiWritingWindowAt), c = Math.max(0, Math.floor(Number(o.aiWritingCount) || 0)), l = Date.now(), u = s != null && l - s < Or;
2708
2743
  if (u && c >= 10) {
2709
- let e = s + Sr, t = Math.max(1, Math.ceil((e - l) / 36e5));
2744
+ let e = s + Or, t = Math.max(1, Math.ceil((e - l) / 36e5));
2710
2745
  throw Error(r("writingAssistant.dailyLimit", {
2711
2746
  limit: 10,
2712
2747
  hours: t
@@ -2714,19 +2749,19 @@ function kr(e = {}) {
2714
2749
  }
2715
2750
  t.update(n, {
2716
2751
  aiWritingCount: u ? c + 1 : 1,
2717
- aiWritingWindowAt: u ? o.aiWritingWindowAt : G(),
2718
- updatedAt: G()
2752
+ aiWritingWindowAt: u ? o.aiWritingWindowAt : K(),
2753
+ updatedAt: K()
2719
2754
  });
2720
2755
  });
2721
2756
  }
2722
2757
  async function l(e) {
2723
- let t = n.value, a = t === "ko", o = Er(e, t, r);
2758
+ let t = n.value, a = t === "ko", o = Mr(e, t, r);
2724
2759
  if (!e.content.trim()) throw Error(r("writingAssistant.contentRequired"));
2725
2760
  await c();
2726
- let l = s.moderation ?? {}, u = Ie(i, {
2727
- backend: new Fe(),
2761
+ let l = s.moderation ?? {}, u = Le(i, {
2762
+ backend: new Ie(),
2728
2763
  useLimitedUseAppCheckTokens: l.useLimitedUseAppCheckTokens !== !1
2729
- }), d = Le(u, {
2764
+ }), d = Re(u, {
2730
2765
  model: l.model ?? "gemini-3.5-flash-lite",
2731
2766
  systemInstruction: (a ? [
2732
2767
  "너는 게시판 글쓰기 편집 도우미다.",
@@ -2749,7 +2784,7 @@ function kr(e = {}) {
2749
2784
  `Task: ${o}`,
2750
2785
  e.title ? `Current title: ${e.title}` : "",
2751
2786
  `Body:\n${f.slice(0, 12e3)}`
2752
- ]).filter(Boolean).join("\n\n"), m = Dr((await d.generateContent(p)).response.text());
2787
+ ]).filter(Boolean).join("\n\n"), m = Nr((await d.generateContent(p)).response.text());
2753
2788
  if (!m) throw Error(r("writingAssistant.emptyResult"));
2754
2789
  return m;
2755
2790
  }
@@ -2757,8 +2792,8 @@ function kr(e = {}) {
2757
2792
  }
2758
2793
  //#endregion
2759
2794
  //#region src/composables/useMemiBoardAutoTranslate.ts
2760
- var Ar = 3e4;
2761
- function jr(e, t) {
2795
+ var Ir = 3e4;
2796
+ function Lr(e, t) {
2762
2797
  return new Promise((n, r) => {
2763
2798
  let i = setTimeout(() => r(/* @__PURE__ */ Error(`translation timed out after ${t / 1e3}s`)), t);
2764
2799
  e.then((e) => {
@@ -2768,14 +2803,14 @@ function jr(e, t) {
2768
2803
  });
2769
2804
  });
2770
2805
  }
2771
- function Mr() {
2772
- let e = Se(), { moderation: t } = de();
2806
+ function Rr() {
2807
+ let e = Ce(), { moderation: t } = ue();
2773
2808
  function n(n, r) {
2774
- let i = Ie(e, {
2775
- backend: new Fe(),
2809
+ let i = Le(e, {
2810
+ backend: new Ie(),
2776
2811
  useLimitedUseAppCheckTokens: t.useLimitedUseAppCheckTokens !== !1
2777
2812
  }), a = Q.object({ properties: Object.fromEntries(r.map((e) => [e, Q.string()])) });
2778
- return Le(i, {
2813
+ return Re(i, {
2779
2814
  model: t.model ?? "gemini-3.5-flash-lite",
2780
2815
  systemInstruction: [
2781
2816
  "You translate short user-interface strings for a website.",
@@ -2794,12 +2829,12 @@ function Mr() {
2794
2829
  async function r(e, t, r, i) {
2795
2830
  let a = Object.keys(e.fields), o = [
2796
2831
  `Purpose: ${i}`,
2797
- `Source language: ${En(t)} (${t})`,
2798
- `Translate into: ${r.map((e) => `${e} = ${En(e)}`).join(", ")}`,
2832
+ `Source language: ${Mn(t)} (${t})`,
2833
+ `Translate into: ${r.map((e) => `${e} = ${Mn(e)}`).join(", ")}`,
2799
2834
  e.context ? `Context (do not translate): ${e.context}` : "",
2800
2835
  "Text (JSON):",
2801
2836
  JSON.stringify(e.fields)
2802
- ].filter(Boolean).join("\n"), s = await jr(n(r, a).generateContent(o), Ar), c = JSON.parse(s.response.text()), l = {};
2837
+ ].filter(Boolean).join("\n"), s = await Lr(n(r, a).generateContent(o), Ir), c = JSON.parse(s.response.text()), l = {};
2803
2838
  for (let e of r) {
2804
2839
  let t = c?.[e];
2805
2840
  if (!t) continue;
@@ -2813,7 +2848,7 @@ function Mr() {
2813
2848
  return l;
2814
2849
  }
2815
2850
  async function i(e, t = {}) {
2816
- let n = t.source ?? "ko", i = (t.targets ?? ne.map((e) => e.code)).filter((e) => e !== n), a = t.purpose ?? "website UI labels", o = e.map((e) => ({
2851
+ let n = t.source ?? "ko", i = (t.targets ?? A.map((e) => e.code)).filter((e) => e !== n), a = t.purpose ?? "website UI labels", o = e.map((e) => ({
2817
2852
  ...e,
2818
2853
  fields: Object.fromEntries(Object.entries(e.fields).map(([e, t]) => [e, t.trim()]).filter(([, e]) => e))
2819
2854
  })).filter((e) => Object.keys(e.fields).length);
@@ -2823,20 +2858,20 @@ function Mr() {
2823
2858
  }
2824
2859
  return { translateEntries: i };
2825
2860
  }
2826
- function Nr(e, t, n, r) {
2861
+ function zr(e, t, n, r) {
2827
2862
  let i = E(t);
2828
2863
  return !i || i === "ko" ? r : e?.[i]?.[n] || e?.en?.[n] || r;
2829
2864
  }
2830
2865
  //#endregion
2831
2866
  //#region src/utils/renderMarkdown.ts
2832
- function Pr(e) {
2867
+ function Br(e) {
2833
2868
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2834
2869
  }
2835
- function Fr(e) {
2870
+ function Vr(e) {
2836
2871
  let t = e;
2837
2872
  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;
2838
2873
  }
2839
- function Ir(e) {
2874
+ function Hr(e) {
2840
2875
  if (!e?.trim()) return "";
2841
2876
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
2842
2877
  function c() {
@@ -2845,7 +2880,7 @@ function Ir(e) {
2845
2880
  return;
2846
2881
  }
2847
2882
  let e = o;
2848
- n.push(`<${e}>${s.map((e) => `<li>${Fr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
2883
+ n.push(`<${e}>${s.map((e) => `<li>${Vr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
2849
2884
  }
2850
2885
  for (; r < t.length;) {
2851
2886
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -2854,7 +2889,7 @@ function Ir(e) {
2854
2889
  continue;
2855
2890
  }
2856
2891
  if (i) {
2857
- a.push(Pr(e)), r++;
2892
+ a.push(Br(e)), r++;
2858
2893
  continue;
2859
2894
  }
2860
2895
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -2865,24 +2900,24 @@ function Ir(e) {
2865
2900
  if (u) {
2866
2901
  c();
2867
2902
  let e = u[1].length;
2868
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${Fr(Pr(u[2]))}</h${e}>`), r++;
2903
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${Vr(Br(u[2]))}</h${e}>`), r++;
2869
2904
  continue;
2870
2905
  }
2871
2906
  if (e.startsWith("> ")) {
2872
2907
  c();
2873
2908
  let e = [];
2874
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(Pr((t[r] ?? "").slice(2))), r++;
2875
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${Fr(e.join("<br />"))}</blockquote>`);
2909
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(Br((t[r] ?? "").slice(2))), r++;
2910
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${Vr(e.join("<br />"))}</blockquote>`);
2876
2911
  continue;
2877
2912
  }
2878
2913
  let d = e.match(/^[-*+]\s+(.+)$/);
2879
2914
  if (d) {
2880
- o && o !== "ul" && c(), o = "ul", s.push(Pr(d[1])), r++;
2915
+ o && o !== "ul" && c(), o = "ul", s.push(Br(d[1])), r++;
2881
2916
  continue;
2882
2917
  }
2883
2918
  let f = e.match(/^\d+\.\s+(.+)$/);
2884
2919
  if (f) {
2885
- o && o !== "ol" && c(), o = "ol", s.push(Pr(f[1])), r++;
2920
+ o && o !== "ol" && c(), o = "ol", s.push(Br(f[1])), r++;
2886
2921
  continue;
2887
2922
  }
2888
2923
  if (!e.trim()) {
@@ -2894,13 +2929,13 @@ function Ir(e) {
2894
2929
  for (; r < t.length;) {
2895
2930
  let e = t[r] ?? "";
2896
2931
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
2897
- p.push(Pr(e)), r++;
2932
+ p.push(Br(e)), r++;
2898
2933
  }
2899
- n.push(`<p class="my-2 leading-relaxed">${Fr(p.join("<br />"))}</p>`);
2934
+ n.push(`<p class="my-2 leading-relaxed">${Vr(p.join("<br />"))}</p>`);
2900
2935
  }
2901
2936
  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");
2902
2937
  }
2903
2938
  //#endregion
2904
- export { an as AUTHOR_MEMO_MAX_LENGTH, Un as BOARD_SECTION_COLS, Kn as BOARD_SECTION_KINDS, qn as BOARD_SECTION_SORTS, Bn as BOARD_USER_ROLES, un as COMMENT_BODY_MAX_LENGTH, ot as DEFAULT_BLOCK_BAN_DECAY_MS, at as DEFAULT_BLOCK_BAN_THRESHOLD, Bt as DEFAULT_BOARDS, Vt as DEFAULT_CATEGORIES, S as DEFAULT_COMMENTS_COLLECTION, ue as DEFAULT_LIKES_COLLECTION, T as DEFAULT_MEMI_BOARD_LOCALE, h as DEFAULT_POSTS_COLLECTION, n as DEFAULT_REPORTS_COLLECTION, le as DEFAULT_SETTINGS_COLLECTION, g as DEFAULT_USERS_COLLECTION, ae as FALLBACK_MEMI_BOARD_LOCALE, ne as MEMI_BOARD_LOCALES, _n as REPORT_DETAIL_MAX_LENGTH, vn as REPORT_REASONS, xr as WRITING_ASSISTANT_DAILY_LIMIT, ze as asHttpUrl, Ge as boardListOgDescription, We as boardListOgTitle, Ue as boardPostOgDescription, He as boardPostOgTitle, Wn as boardSectionKinds, Gn as boardSectionSorts, r as boardSsrKey, f as boardUserDoc, e as boardUsersCol, zt as buildPostPreview, et as canManageBoardByRole, tt as canWriteCommentByRole, Yn as clampSectionCount, k as commentDoc, u as commentsCol, b as configureMemiBoard, Jn as defaultSectionCount, st as effectiveModerationBlockCount, yt as extractEditorImageUrls, Ze as fetchPublicListForSeo, Xe as fetchPublicPostForSeo, x as formatDate, ie as formatFullDate, se as formatRelativeDate, ut as formatRestrictedUntilLabel, C as formatTimestampDetails, Pt as hasBodyImage, Nt as hasBodyText, vt as imeSafeSubmitClick, _t as imeSafeSubmitPointerDown, $e as isAssignedStaff, Ft as isContentEmpty, Kt as isFirestorePermissionDenied, te as isMemiBoardLocaleLoaded, lt as isModerationWriteRestricted, d as likeDoc, p as likeDocId, a as likesCol, ce as loadMemiBoardLocale, Nr as localizedText, hn as memiBoardCommentLikesKey, oe as memiBoardHostLocaleKey, D as memiBoardLocaleInfo, or as miniBoardView, tr as miniCommentLink, er as miniMoreLink, nr as miniPostImage, $n as miniPostLink, rr as miniPostTitle, sr as miniPostsView, ir as miniViewGroup, ar as miniViewLabel, ct as moderationWriteRestrictedUntilMs, Ke as normalizeBasePath, gr as normalizeBoardSection, E as normalizeMemiBoardLocale, Xn as pairSectionCols, jt as plainTextFromHtml, c as postBodyDoc, l as postDoc, xt as postNamespaceFromStoragePath, m as postStorageFolder, j as postsCol, Ir as renderMarkdownToHtml, t as reportDoc, v as reportDocId, yn as reportReasonLabel, s as reportsCol, _ as resolveBoardPathConfig, qe as resolvePublicSeoDb, Zn as sectionColClass, Qn as sectionMinHeight, y as settingsCol, o as settingsDoc, re as slugify, bt as storagePathFromDownloadUrl, Mt as titleFromBody, Ve as toAbsoluteUrl, Be as toBoardOgImageUrl, w as translate, A as useBoardPathConfig, mt as useMemiBoardAuth, sn as useMemiBoardAuthorMemo, Mr as useMemiBoardAutoTranslate, rn as useMemiBoardCommentFeed, gn as useMemiBoardCommentLikes, fn as useMemiBoardComments, de as useMemiBoardConfig, O as useMemiBoardI18n, mn as useMemiBoardLikes, vr as useMemiBoardMiniLatest, yr as useMemiBoardMiniPicked, zn as useMemiBoardModeration, Zt as useMemiBoardPost, Xt as useMemiBoardPostList, Jt as useMemiBoardPosts, pn as useMemiBoardReplies, Sn as useMemiBoardReportQueue, bn as useMemiBoardReports, _r as useMemiBoardSections, Wt as useMemiBoardSettings, tn as useMemiBoardUserComments, $t as useMemiBoardUserPosts, Hn as useMemiBoardUserProfile, Vn as useMemiBoardUsers, Cn as useMemiBoardViews, kr as useMemiBoardWritingAssistant, br as versionHistory, Rt as videoListCoverUrl, Lt as youtubeId };
2939
+ export { un as AUTHOR_MEMO_MAX_LENGTH, Yn as BOARD_SECTION_COLS, Qn as BOARD_SECTION_KINDS, $n as BOARD_SECTION_SORTS, Kn as BOARD_USER_ROLES, gn as COMMENT_BODY_MAX_LENGTH, st as DEFAULT_BLOCK_BAN_DECAY_MS, ot as DEFAULT_BLOCK_BAN_THRESHOLD, Gt as DEFAULT_BOARDS, Kt as DEFAULT_CATEGORIES, S as DEFAULT_COMMENTS_COLLECTION, le as DEFAULT_LIKES_COLLECTION, T as DEFAULT_MEMI_BOARD_LOCALE, h as DEFAULT_POSTS_COLLECTION, n as DEFAULT_REPORTS_COLLECTION, ce as DEFAULT_SETTINGS_COLLECTION, g as DEFAULT_USERS_COLLECTION, ie as FALLBACK_MEMI_BOARD_LOCALE, A as MEMI_BOARD_LOCALES, Cn as REPORT_DETAIL_MAX_LENGTH, wn as REPORT_REASONS, Dr as WRITING_ASSISTANT_DAILY_LIMIT, Be as asHttpUrl, Ke as boardListOgDescription, Ge as boardListOgTitle, We as boardPostOgDescription, Ue as boardPostOgTitle, Xn as boardSectionKinds, Zn as boardSectionSorts, r as boardSsrKey, f as boardUserDoc, e as boardUsersCol, Bt as buildPostPreview, tt as canManageBoardByRole, nt as canWriteCommentByRole, tr as clampSectionCount, k as commentDoc, u as commentsCol, b as configureMemiBoard, er as defaultSectionCount, ct as effectiveModerationBlockCount, xt as extractEditorImageUrls, Qe as fetchPublicListForSeo, Ze as fetchPublicPostForSeo, x as formatDate, re as formatFullDate, oe as formatRelativeDate, dt as formatRestrictedUntilLabel, C as formatTimestampDetails, Ft as hasBodyImage, Pt as hasBodyText, yt as imeSafeSubmitClick, vt as imeSafeSubmitPointerDown, et as isAssignedStaff, It as isContentEmpty, Zt as isFirestorePermissionDenied, te as isMemiBoardLocaleLoaded, ut as isModerationWriteRestricted, d as likeDoc, p as likeDocId, a as likesCol, se as loadMemiBoardLocale, zr as localizedText, xn as memiBoardCommentLikesKey, ae as memiBoardHostLocaleKey, D as memiBoardLocaleInfo, fr as miniBoardView, sr as miniCommentLink, or as miniMoreLink, cr as miniPostImage, ar as miniPostLink, lr as miniPostTitle, pr as miniPostsView, ur as miniViewGroup, dr as miniViewLabel, lt as moderationWriteRestrictedUntilMs, qe as normalizeBasePath, Sr as normalizeBoardSection, E as normalizeMemiBoardLocale, nr as pairSectionCols, Mt as plainTextFromHtml, c as postBodyDoc, l as postDoc, Ct as postNamespaceFromStoragePath, m as postStorageFolder, M as postsCol, Hr as renderMarkdownToHtml, t as reportDoc, v as reportDocId, Tn as reportReasonLabel, s as reportsCol, _ as resolveBoardPathConfig, Je as resolvePublicSeoDb, rr as sectionColClass, ir as sectionMinHeight, y as settingsCol, o as settingsDoc, ne as slugify, St as storagePathFromDownloadUrl, Nt as titleFromBody, He as toAbsoluteUrl, Ve as toBoardOgImageUrl, w as translate, j as useBoardPathConfig, ht as useMemiBoardAuth, fn as useMemiBoardAuthorMemo, Rr as useMemiBoardAutoTranslate, ln as useMemiBoardCommentFeed, Sn as useMemiBoardCommentLikes, vn as useMemiBoardComments, ue as useMemiBoardConfig, O as useMemiBoardI18n, bn as useMemiBoardLikes, wr as useMemiBoardMiniLatest, Tr as useMemiBoardMiniPicked, Gn as useMemiBoardModeration, nn as useMemiBoardPost, tn as useMemiBoardPostList, $t as useMemiBoardPosts, yn as useMemiBoardReplies, On as useMemiBoardReportQueue, En as useMemiBoardReports, Cr as useMemiBoardSections, Yt as useMemiBoardSettings, sn as useMemiBoardUserComments, an as useMemiBoardUserPosts, Jn as useMemiBoardUserProfile, qn as useMemiBoardUsers, kn as useMemiBoardViews, Fr as useMemiBoardWritingAssistant, Er as versionHistory, zt as videoListCoverUrl, Rt as youtubeId };
2905
2940
 
2906
2941
  //# sourceMappingURL=index.js.map