memi-board 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,30 @@
1
- import { A as e, C as t, D as n, E as r, M as i, N as a, O as o, S as s, T as c, _ as l, a as u, b as d, c as f, d as p, f as m, g as h, h as g, i as _, j as v, k as y, l as b, m as x, n as S, o as C, p as w, r as T, s as E, u as D, v as O, w as k, x as A, y as j } from "./slugify-Cyq-Gg8X.js";
2
- import { computed as M, onScopeDispose as N, reactive as ee, ref as P, toValue as F, watch as I } from "vue";
3
- import { deleteDoc as te, deleteField as ne, doc as re, documentId as L, endBefore as ie, getDoc as R, getDocs as z, increment as B, limit as V, limitToLast as ae, onSnapshot as H, orderBy as U, query as W, runTransaction as oe, 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 se, useCurrentUser as Z, useDocument as ce, useFirebaseApp as le, useFirebaseAuth as ue, useFirestore as Q } from "vuefire";
5
- import { GoogleAuthProvider as de, OAuthProvider as fe, createUserWithEmailAndPassword as pe, signInWithEmailAndPassword as me, signInWithPopup as he, signOut as ge, updateProfile as _e } from "firebase/auth";
6
- import { deleteObject as ve, getStorage as ye, listAll as be, ref as xe } from "firebase/storage";
7
- import { GoogleAIBackend as Se, Schema as Ce, getAI as we, getGenerativeModel as Te } from "firebase/ai";
1
+ import { A as e, C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, j as g, k as _, l as v, m as y, n as b, o as x, p as S, r as C, s as w, u as T, v as E, w as D, x as O, y as k } from "./slugify-Cm_pi2al.js";
2
+ import { computed as A, onScopeDispose as j, reactive as ee, ref as M, toValue as N, watch as P } from "vue";
3
+ import { deleteDoc as F, deleteField as I, doc as L, documentId as R, endBefore as te, getDoc as z, getDocs as B, increment as V, limit as H, limitToLast as ne, onSnapshot as U, orderBy as W, query as G, runTransaction as re, serverTimestamp as K, setDoc as q, startAfter as J, updateDoc as Y, where as X, writeBatch as Z } from "firebase/firestore";
4
+ import { useCollection as ie, useCurrentUser as ae, useDocument as oe, useFirebaseApp as se, useFirebaseAuth as ce, useFirestore as Q } from "vuefire";
5
+ import { GoogleAuthProvider as le, OAuthProvider as ue, createUserWithEmailAndPassword as de, signInWithEmailAndPassword as fe, signInWithPopup as pe, signOut as me, updateProfile as he } from "firebase/auth";
6
+ import { deleteObject as ge, getStorage as _e, listAll as ve, ref as ye } from "firebase/storage";
7
+ import { GoogleAIBackend as be, Schema as xe, getAI as Se, getGenerativeModel as Ce } from "firebase/ai";
8
+ //#region \0rolldown/runtime.js
9
+ var we = Object.create, Te = Object.defineProperty, Ee = Object.getOwnPropertyDescriptor, De = Object.getOwnPropertyNames, Oe = Object.getPrototypeOf, ke = Object.prototype.hasOwnProperty, Ae = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), je = (e, t, n, r) => {
10
+ if (t && typeof t == "object" || typeof t == "function") for (var i = De(t), a = 0, o = i.length, s; a < o; a++) s = i[a], !ke.call(e, s) && s !== n && Te(e, s, {
11
+ get: ((e) => t[e]).bind(null, s),
12
+ enumerable: !(r = Ee(t, s)) || r.enumerable
13
+ });
14
+ return e;
15
+ }, Me = (e, t, n) => (n = e == null ? {} : we(Oe(e)), je(t || !e || !e.__esModule || !ke.call(e, "default") ? Te(n, "default", {
16
+ value: e,
17
+ enumerable: !0
18
+ }) : n, e));
19
+ //#endregion
8
20
  //#region src/utils/boardSeo.ts
9
- function Ee(e) {
21
+ function Ne(e) {
10
22
  if (typeof e != "string") return null;
11
23
  let t = e.trim();
12
24
  return /^https?:\/\//i.test(t) ? t : null;
13
25
  }
14
- function De(e) {
15
- let t = Ee(e);
26
+ function Pe(e) {
27
+ let t = Ne(e);
16
28
  if (!t) return null;
17
29
  if (!/firebasestorage\.googleapis\.com/i.test(t)) return t;
18
30
  try {
@@ -29,42 +41,42 @@ function De(e) {
29
41
  return t;
30
42
  }
31
43
  }
32
- function Oe(e, t, n = "") {
44
+ function Fe(e, t, n = "") {
33
45
  let r = t.replace(/\/$/, ""), i = e.trim();
34
- return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? Oe(n, t) : r;
46
+ return i ? /^https?:\/\//i.test(i) ? i : `${r}${i.startsWith("/") ? i : `/${i}`}` : n ? Fe(n, t) : r;
35
47
  }
36
- function ke(e, t, n, r) {
48
+ function Ie(e, t, n, r) {
37
49
  let i = r?.trim().slice(0, 40), a = e.trim() || i || "사진", o = t?.trim(), s = n.trim() || "Board";
38
50
  return o ? `${a} · ${o} | ${s}` : `${a} | ${s}`;
39
51
  }
40
- function Ae(e) {
52
+ function Le(e) {
41
53
  let t = [];
42
54
  return e.summary?.trim() ? t.push(e.summary.trim().slice(0, 140)) : t.push("게시판 글"), e.categoryLabel?.trim() && t.push(e.categoryLabel.trim()), e.authorName?.trim() && t.push(e.authorName.trim()), t.join(" · ").slice(0, 200);
43
55
  }
44
- function je(e, t) {
56
+ function Re(e, t) {
45
57
  let n = t.trim() || "Board", r = e?.trim();
46
58
  return r && r !== "전체" ? `${r} 게시판 | ${n}` : `게시판 | ${n}`;
47
59
  }
48
- function Me(e) {
60
+ function ze(e) {
49
61
  if (e.description?.trim()) return e.description.trim().slice(0, 200);
50
62
  let t = e.categoryLabel?.trim(), n = t && t !== "전체" ? `${t} 게시판 — 소식을 확인하세요.` : "게시판 — 공지와 이야기를 확인하세요.", r = (e.recentTitles || []).filter(Boolean).slice(0, 3);
51
63
  return r.length ? `${n} ${r.map((e) => `「${e}」`).join(" ")}`.slice(0, 200) : n.slice(0, 200);
52
64
  }
53
- function Ne(e) {
65
+ function Be(e) {
54
66
  let t = (e || "/board").trim() || "/board";
55
67
  return (t.startsWith("/") ? t : `/${t}`).replace(/\/$/, "") || "/board";
56
68
  }
57
69
  //#endregion
58
70
  //#region src/composables/fetchPublicSeo.ts
59
- function Pe() {
71
+ function Ve() {
60
72
  return {
61
73
  db: Q(),
62
- paths: _()
74
+ paths: h()
63
75
  };
64
76
  }
65
- async function Fe(e, t) {
77
+ async function He(e, t) {
66
78
  try {
67
- let n = await R(v(e.db, e.paths, t));
79
+ let n = await z(g(e.db, e.paths, t));
68
80
  if (!n.exists()) return {
69
81
  label: t,
70
82
  description: "",
@@ -84,7 +96,7 @@ async function Fe(e, t) {
84
96
  };
85
97
  }
86
98
  }
87
- function Ie(e) {
99
+ function Ue(e) {
88
100
  if (e && typeof e == "object" && "toDate" in e) try {
89
101
  let t = e.toDate();
90
102
  return t instanceof Date ? t.toISOString() : null;
@@ -93,36 +105,36 @@ function Ie(e) {
93
105
  }
94
106
  return null;
95
107
  }
96
- async function Le(e, t, n) {
108
+ async function We(e, t, n) {
97
109
  let r = e.trim(), i = t.trim();
98
110
  if (!r || !i) return null;
99
- let a = n ?? Pe();
100
- a.paths = y(a.paths);
101
- let o = await Fe(a, r);
102
- if (o.hidden) return null;
111
+ let a = n ?? Ve();
112
+ a.paths = _(a.paths);
113
+ let s = await He(a, r);
114
+ if (s.hidden) return null;
103
115
  try {
104
- let e = (await z(W(c(a.db, a.paths), Y("boardId", "==", r), Y("slug", "==", i), Y("listed", "==", !0), Y("isPublished", "==", !0), V(1)))).docs[0];
116
+ let e = (await B(G(o(a.db, a.paths), X("boardId", "==", r), X("slug", "==", i), X("listed", "==", !0), X("isPublished", "==", !0), H(1)))).docs[0];
105
117
  if (!e) return null;
106
118
  let t = e.data() ?? {}, n = typeof t.title == "string" ? t.title.trim() : "";
107
119
  return n ? {
108
120
  id: e.id,
109
121
  slug: typeof t.slug == "string" ? t.slug : i,
110
122
  category: r,
111
- categoryLabel: o.label,
123
+ categoryLabel: s.label,
112
124
  title: n,
113
125
  summary: typeof t.summary == "string" ? t.summary.trim().slice(0, 200) : "",
114
- previewImage: De(t.previewImage),
126
+ previewImage: Pe(t.previewImage),
115
127
  authorName: typeof t.authorName == "string" && t.authorName.trim() ? t.authorName.trim() : null,
116
- createdAt: Ie(t.createdAt)
128
+ createdAt: Ue(t.createdAt)
117
129
  } : null;
118
130
  } catch (e) {
119
131
  return import.meta.dev && console.warn("[memi-board] fetchPublicPostForSeo failed", e), null;
120
132
  }
121
133
  }
122
- async function Re(e, t) {
123
- let n = e?.trim() || null, r = t ?? Pe();
124
- if (r.paths = y(r.paths), n) {
125
- let e = await Fe(r, n);
134
+ async function Ge(e, t) {
135
+ let n = e?.trim() || null, r = t ?? Ve();
136
+ if (r.paths = _(r.paths), n) {
137
+ let e = await He(r, n);
126
138
  if (e.hidden) return {
127
139
  kind: "category",
128
140
  category: n,
@@ -131,7 +143,7 @@ async function Re(e, t) {
131
143
  ogImage: null,
132
144
  recentTitles: []
133
145
  };
134
- let { recentTitles: t, ogImage: i } = await ze(r, n);
146
+ let { recentTitles: t, ogImage: i } = await Ke(r, n);
135
147
  return {
136
148
  kind: "category",
137
149
  category: n,
@@ -150,13 +162,13 @@ async function Re(e, t) {
150
162
  recentTitles: []
151
163
  };
152
164
  }
153
- async function ze(e, t) {
165
+ async function Ke(e, t) {
154
166
  let n = [], r = null;
155
167
  try {
156
- let i = await z(W(c(e.db, e.paths), Y("boardId", "==", t), Y("listed", "==", !0), Y("isPublished", "==", !0), U("createdAt", "desc"), V(12)));
168
+ let i = await B(G(o(e.db, e.paths), X("boardId", "==", t), X("listed", "==", !0), X("isPublished", "==", !0), W("createdAt", "desc"), H(12)));
157
169
  for (let e of i.docs) {
158
170
  let t = e.data() ?? {}, i = typeof t.title == "string" ? t.title.trim() : "";
159
- i && n.length < 5 && n.push(i), r ||= De(t.previewImage);
171
+ i && n.length < 5 && n.push(i), r ||= Pe(t.previewImage);
160
172
  }
161
173
  } catch (e) {
162
174
  import.meta.dev && console.warn("[memi-board] loadRecentListed failed", e);
@@ -168,31 +180,31 @@ async function ze(e, t) {
168
180
  }
169
181
  //#endregion
170
182
  //#region src/utils/boardAccess.ts
171
- function Be(e, t) {
183
+ function qe(e, t) {
172
184
  return !e || !t ? !1 : (t.allowedStaffUids ?? []).includes(e);
173
185
  }
174
- function Ve(e, t, n, r) {
175
- return e ? t ? !0 : n ? Be(e, r) : !1 : !1;
186
+ function Je(e, t, n, r) {
187
+ return e ? t ? !0 : n ? qe(e, r) : !1 : !1;
176
188
  }
177
- function He(e, t, n, r) {
189
+ function Ye(e, t, n, r) {
178
190
  if (!e) return !1;
179
- if (r?.visibility === "hidden") return Ve(e, t, n, r);
191
+ if (r?.visibility === "hidden") return Je(e, t, n, r);
180
192
  let i = r?.commentWriteRole ?? "user";
181
- return i === "admin" ? t : i !== "staff" || Ve(e, t, n, r);
193
+ return i === "admin" ? t : i !== "staff" || Je(e, t, n, r);
182
194
  }
183
195
  //#endregion
184
196
  //#region src/composables/boardLookup.ts
185
- var Ue;
186
- function We(e) {
187
- Ue = e;
197
+ var Xe;
198
+ function Ze(e) {
199
+ Xe = e;
188
200
  }
189
- function Ge(e) {
190
- return Ue?.(e);
201
+ function Qe(e) {
202
+ return Xe?.(e);
191
203
  }
192
204
  //#endregion
193
205
  //#region src/utils/moderation-strike.ts
194
- var Ke = 10, qe = 864e5;
195
- function Je(e, t, n = Date.now(), r = qe) {
206
+ var $e = 10, et = 864e5;
207
+ function tt(e, t, n = Date.now(), r = et) {
196
208
  let i = Math.max(0, Math.floor(Number(e) || 0));
197
209
  if (i <= 0) return 0;
198
210
  let a = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -200,7 +212,7 @@ function Je(e, t, n = Date.now(), r = qe) {
200
212
  let o = Math.floor((n - a) / r);
201
213
  return Math.max(0, i - o);
202
214
  }
203
- function Ye(e, t, n = Date.now(), r = 10, i = qe) {
215
+ function nt(e, t, n = Date.now(), r = 10, i = et) {
204
216
  let a = Math.max(0, Math.floor(Number(e) || 0));
205
217
  if (a < r) return null;
206
218
  let o = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -208,17 +220,17 @@ function Ye(e, t, n = Date.now(), r = 10, i = qe) {
208
220
  let s = o + (a - (r - 1)) * i;
209
221
  return s > n ? s : null;
210
222
  }
211
- function Xe(e, t, n = Date.now(), r = 10, i = qe) {
212
- return Ye(e, t, n, r, i) != null;
223
+ function rt(e, t, n = Date.now(), r = 10, i = et) {
224
+ return nt(e, t, n, r, i) != null;
213
225
  }
214
- function Ze(e, t = Date.now()) {
226
+ function it(e, t = Date.now()) {
215
227
  let n = Math.max(0, e - t), r = Math.ceil(n / 36e5);
216
228
  if (r <= 0) return "곧 해제";
217
229
  if (r < 24) return `약 ${r}시간 후 해제`;
218
230
  let i = Math.floor(r / 24), a = r % 24;
219
231
  return a === 0 ? `약 ${i}일 후 해제` : `약 ${i}일 ${a}시간 후 해제`;
220
232
  }
221
- function Qe(e) {
233
+ function at(e) {
222
234
  if (e == null) return null;
223
235
  if (typeof e == "number" && Number.isFinite(e)) return e;
224
236
  if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
@@ -230,122 +242,122 @@ function Qe(e) {
230
242
  }
231
243
  //#endregion
232
244
  //#region src/composables/useMemiBoardAuth.ts
233
- var $e = "__MEMI_BOARD_AUTH_STATE__";
234
- function et() {
245
+ var ot = "__MEMI_BOARD_AUTH_STATE__";
246
+ function st() {
235
247
  let e = globalThis;
236
- return e[$e] || (e[$e] = {
237
- role: P("user"),
238
- rolePending: P(!0),
239
- rawBlockCount: P(0),
240
- blockAtMs: P(null),
248
+ return e[ot] || (e[ot] = {
249
+ role: M("user"),
250
+ rolePending: M(!0),
251
+ rawBlockCount: M(0),
252
+ blockAtMs: M(null),
241
253
  watchStarted: !1
242
- }), e[$e];
254
+ }), e[ot];
243
255
  }
244
- function tt() {
245
- let e = u(), t = et(), n = Q(), r = ue(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: c } = t;
246
- function l() {
256
+ function ct() {
257
+ let e = c(), t = st(), n = Q(), r = ce(), i = ae(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: l } = t;
258
+ function u() {
247
259
  let t = e.moderation?.blockBanThreshold;
248
260
  return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
249
261
  }
250
262
  function d() {
251
263
  let t = e.moderation?.blockBanDecayMs;
252
- return typeof t == "number" && t > 0 ? t : qe;
264
+ return typeof t == "number" && t > 0 ? t : et;
253
265
  }
254
266
  function f(e) {
255
- return x(n, _(), e);
267
+ return y(n, h(), e);
256
268
  }
257
269
  function p(e) {
258
270
  if (!e) {
259
- s.value = 0, c.value = null;
271
+ s.value = 0, l.value = null;
260
272
  return;
261
273
  }
262
- s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), c.value = Qe(e.moderationBlockAt);
274
+ s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), l.value = at(e.moderationBlockAt);
263
275
  }
264
- async function m(e) {
265
- let t = f(e.uid), r = g(n, _(), e.uid), i = await R(t);
266
- if (!i.exists()) return await Promise.all([K(t, {
276
+ async function g(e) {
277
+ let t = f(e.uid), r = m(n, h(), e.uid), i = await z(t);
278
+ if (!i.exists()) return await Promise.all([q(t, {
267
279
  role: "user",
268
280
  displayName: e.displayName,
269
281
  photoURL: e.photoURL,
270
- joinedAt: G(),
271
- lastVisitAt: G(),
272
- updatedAt: G()
273
- }), K(r, { email: e.email })]), p({ moderationBlockCount: 0 }), "user";
282
+ joinedAt: K(),
283
+ lastVisitAt: K(),
284
+ updatedAt: K()
285
+ }), q(r, { email: e.email })]), p({ moderationBlockCount: 0 }), "user";
274
286
  let a = i.data();
275
287
  p(a);
276
288
  let o = typeof a.displayName == "string" && a.displayName.trim(), s = typeof a.photoURL == "string" && a.photoURL.trim(), c = {
277
- lastVisitAt: G(),
278
- updatedAt: G()
289
+ lastVisitAt: K(),
290
+ updatedAt: K()
279
291
  };
280
- return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([J(t, c), K(r, { email: e.email }, { merge: !0 })]), [
292
+ return !o && e.displayName && (c.displayName = e.displayName), !s && e.photoURL && (c.photoURL = e.photoURL), await Promise.all([Y(t, c), q(r, { email: e.email }, { merge: !0 })]), [
281
293
  "admin",
282
294
  "staff",
283
295
  "user"
284
296
  ].includes(String(a.role)) ? a.role : "user";
285
297
  }
286
- async function h() {
298
+ async function _() {
287
299
  let e = i.value?.uid;
288
300
  if (!e) {
289
301
  p(void 0);
290
302
  return;
291
303
  }
292
- let t = await R(f(e));
304
+ let t = await z(f(e));
293
305
  p(t.exists() ? t.data() : void 0);
294
306
  }
295
- t.watchStarted || (t.watchStarted = !0, I(i, async (e) => {
307
+ t.watchStarted || (t.watchStarted = !0, P(i, async (e) => {
296
308
  if (o.value = !0, !e) {
297
309
  a.value = "user", p(void 0), o.value = !1;
298
310
  return;
299
311
  }
300
312
  try {
301
- a.value = await m(e);
313
+ a.value = await g(e);
302
314
  } catch (e) {
303
315
  console.error("[memi-board] ensureUserDoc failed", e), a.value = "user", p(void 0);
304
316
  } finally {
305
317
  o.value = !1;
306
318
  }
307
319
  }, { immediate: !0 }));
308
- let v = M(() => !!i.value), y = M(() => a.value === "admin"), b = M(() => a.value === "staff"), S = M(() => y.value || b.value), C = M(() => Je(s.value, c.value, Date.now(), d())), w = M(() => {
309
- let e = l();
310
- return e <= 0 ? !1 : Xe(s.value, c.value, Date.now(), e, d());
311
- }), T = M(() => {
312
- let e = l();
320
+ let v = A(() => !!i.value), b = A(() => a.value === "admin"), x = A(() => a.value === "staff"), S = A(() => b.value || x.value), C = A(() => tt(s.value, l.value, Date.now(), d())), w = A(() => {
321
+ let e = u();
322
+ return e <= 0 ? !1 : rt(s.value, l.value, Date.now(), e, d());
323
+ }), T = A(() => {
324
+ let e = u();
313
325
  if (e <= 0) return null;
314
- let t = Ye(s.value, c.value, Date.now(), e, d());
315
- return t == null ? null : Ze(t);
316
- }), E = M(() => {
326
+ let t = nt(s.value, l.value, Date.now(), e, d());
327
+ return t == null ? null : it(t);
328
+ }), E = A(() => {
317
329
  if (!w.value) return null;
318
330
  let e = T.value;
319
331
  return e ? `콘텐츠 경고가 누적되어 글·댓글 작성이 잠시 제한됐어요. (${e})` : "콘텐츠 경고가 누적되어 글·댓글 작성이 잠시 제한됐어요.";
320
332
  });
321
333
  async function D() {
322
- let e = i.value?.uid, t = l();
334
+ let e = i.value?.uid, t = u();
323
335
  if (!e || t <= 0) return {
324
336
  effectiveCount: 0,
325
337
  restricted: !1,
326
338
  messageSuffix: ""
327
339
  };
328
- let n = f(e), r = await R(n);
329
- r.exists() ? await J(n, {
330
- moderationBlockCount: B(1),
331
- moderationBlockAt: G(),
332
- updatedAt: G()
333
- }) : await K(n, {
340
+ let n = f(e), r = await z(n);
341
+ r.exists() ? await Y(n, {
342
+ moderationBlockCount: V(1),
343
+ moderationBlockAt: K(),
344
+ updatedAt: K()
345
+ }) : await q(n, {
334
346
  role: "user",
335
347
  displayName: i.value?.displayName ?? null,
336
348
  moderationBlockCount: 1,
337
- moderationBlockAt: G(),
338
- updatedAt: G()
349
+ moderationBlockAt: K(),
350
+ updatedAt: K()
339
351
  });
340
352
  let a = (r.exists() ? Math.max(0, Math.floor(Number(r.data().moderationBlockCount) || 0)) : 0) + 1, o = Date.now();
341
- s.value = a, c.value = o;
342
- let u = Je(a, o, o, d()), p = Xe(a, o, o, t, d()), m = ` (경고 ${u}/${t})`;
353
+ s.value = a, l.value = o;
354
+ let c = tt(a, o, o, d()), p = rt(a, o, o, t, d()), m = ` (경고 ${c}/${t})`;
343
355
  if (p) {
344
- let e = Ye(a, o, o, t, d()), n = e == null ? null : Ze(e, o);
345
- m = n ? ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다 (${n}).` : ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다.`;
356
+ let e = nt(a, o, o, t, d()), n = e == null ? null : it(e, o);
357
+ m = n ? ` 경고 ${c}회가 되어 글·댓글 작성이 제한됩니다 (${n}).` : ` 경고 ${c}회가 되어 글·댓글 작성이 제한됩니다.`;
346
358
  }
347
359
  return {
348
- effectiveCount: u,
360
+ effectiveCount: c,
349
361
  restricted: p,
350
362
  messageSuffix: m
351
363
  };
@@ -355,62 +367,62 @@ function tt() {
355
367
  return r;
356
368
  }
357
369
  async function k() {
358
- return he(O(), new de());
370
+ return pe(O(), new le());
359
371
  }
360
- async function A() {
361
- let e = new fe("apple.com");
362
- return e.addScope("email"), e.addScope("name"), he(O(), e);
372
+ async function j() {
373
+ let e = new ue("apple.com");
374
+ return e.addScope("email"), e.addScope("name"), pe(O(), e);
363
375
  }
364
- async function j(e, t) {
365
- return me(O(), e, t);
376
+ async function ee(e, t) {
377
+ return fe(O(), e, t);
366
378
  }
367
- async function N(e, t, n) {
368
- let r = await pe(O(), e, t);
369
- return n && await _e(r.user, { displayName: n }), r;
370
- }
371
- async function ee() {
372
- await ge(O());
379
+ async function M(e, t, n) {
380
+ let r = await de(O(), e, t);
381
+ return n && await he(r.user, { displayName: n }), r;
373
382
  }
374
- function P(e) {
375
- return Ve(i.value?.uid, y.value, b.value, e ? Ge(e) : void 0);
383
+ async function N() {
384
+ await me(O());
376
385
  }
377
386
  function F(e) {
378
- return v.value && (i.value?.uid === e.authorUid || P(e.boardId));
387
+ return Je(i.value?.uid, b.value, x.value, e ? Qe(e) : void 0);
379
388
  }
380
- function te(e) {
389
+ function I(e) {
390
+ return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
391
+ }
392
+ function L(e) {
381
393
  return v.value && i.value?.uid === e.authorUid;
382
394
  }
383
- function ne(e) {
384
- return v.value && (i.value?.uid === e.authorUid || P(e.boardId));
395
+ function R(e) {
396
+ return v.value && (i.value?.uid === e.authorUid || F(e.boardId));
385
397
  }
386
398
  return {
387
399
  user: i,
388
400
  isSignedIn: v,
389
- isAdmin: y,
390
- isStaff: b,
401
+ isAdmin: b,
402
+ isStaff: x,
391
403
  canManageContent: S,
392
- canManageBoard: P,
404
+ canManageBoard: F,
393
405
  rolePending: o,
394
406
  moderationBlockCount: C,
395
407
  isWriteRestricted: w,
396
408
  restrictedUntilLabel: T,
397
409
  restrictedMessage: E,
398
410
  signInWithGoogle: k,
399
- signInWithApple: A,
400
- signInWithEmail: j,
401
- signUpWithEmail: N,
402
- signOut: ee,
403
- canEdit: F,
404
- canDelete: F,
405
- canEditComment: te,
406
- canDeleteComment: ne,
411
+ signInWithApple: j,
412
+ signInWithEmail: ee,
413
+ signUpWithEmail: M,
414
+ signOut: N,
415
+ canEdit: I,
416
+ canDelete: I,
417
+ canEditComment: L,
418
+ canDeleteComment: R,
407
419
  recordContentModerationBlock: D,
408
- refreshBoardUser: h
420
+ refreshBoardUser: _
409
421
  };
410
422
  }
411
423
  //#endregion
412
424
  //#region src/utils/extractEditorImageUrls.ts
413
- function nt(e) {
425
+ function lt(e) {
414
426
  if (!e) return [];
415
427
  let t = /* @__PURE__ */ new Set();
416
428
  for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&amp;", "&"));
@@ -419,7 +431,7 @@ function nt(e) {
419
431
  }
420
432
  //#endregion
421
433
  //#region src/utils/storagePath.ts
422
- function rt(e) {
434
+ function ut(e) {
423
435
  try {
424
436
  let t = new URL(e), n = t.hostname.toLowerCase();
425
437
  if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
@@ -429,109 +441,109 @@ function rt(e) {
429
441
  return null;
430
442
  }
431
443
  }
432
- function it(e) {
444
+ function dt(e) {
433
445
  return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
434
446
  }
435
447
  //#endregion
436
448
  //#region src/utils/deletePostCascade.ts
437
- async function at(e) {
449
+ async function ft(e) {
438
450
  try {
439
- let t = await be(e);
440
- await Promise.all([...t.items.map((e) => ve(e).catch(() => {})), ...t.prefixes.map((e) => at(e))]);
451
+ let t = await ve(e);
452
+ await Promise.all([...t.items.map((e) => ge(e).catch(() => {})), ...t.prefixes.map((e) => ft(e))]);
441
453
  } catch {}
442
454
  }
443
- async function ot(e, t) {
444
- await at(xe(e, `${t}/images`)), await at(xe(e, `${t}/attachments`));
455
+ async function pt(e, t) {
456
+ await ft(ye(e, `${t}/images`)), await ft(ye(e, `${t}/attachments`));
445
457
  }
446
- async function st(e, t) {
458
+ async function mt(e, t) {
447
459
  let n = [...new Set([...t].filter(Boolean))];
448
- await Promise.all(n.map((t) => ve(xe(e, t)).catch(() => {})));
460
+ await Promise.all(n.map((t) => ge(ye(e, t)).catch(() => {})));
449
461
  }
450
- var ct = 450;
451
- async function lt(e, t) {
452
- let n = await z(t);
453
- for (let t = 0; t < n.docs.length; t += ct) {
454
- let r = X(e);
455
- n.docs.slice(t, t + ct).forEach((e) => r.delete(e.ref)), await r.commit();
462
+ var ht = 450;
463
+ async function gt(e, t) {
464
+ let n = await B(t);
465
+ for (let t = 0; t < n.docs.length; t += ht) {
466
+ let r = Z(e);
467
+ n.docs.slice(t, t + ht).forEach((e) => r.delete(e.ref)), await r.commit();
456
468
  }
457
469
  }
458
- async function ut(e, t, n) {
459
- await lt(e, W(A(e, t), Y("postId", "==", n)));
470
+ async function _t(e, t, n) {
471
+ await gt(e, G(O(e, t), X("postId", "==", n)));
460
472
  }
461
- async function dt(e, t, n) {
462
- await lt(e, W(o(e, t), Y("postId", "==", n)));
473
+ async function vt(e, t, n) {
474
+ await gt(e, G(i(e, t), X("postId", "==", n)));
463
475
  }
464
- async function ft(e, t, n) {
465
- await lt(e, W(A(e, t), Y("commentId", "==", n)));
476
+ async function yt(e, t, n) {
477
+ await gt(e, G(O(e, t), X("commentId", "==", n)));
466
478
  }
467
- async function pt(e, n, r, i) {
468
- let a = t(e, r, i), o = s(e, r, i), [c, l] = await Promise.all([R(a), R(o)]);
479
+ async function bt(e, n, r, i) {
480
+ let o = t(e, r, i), s = a(e, r, i), [c, l] = await Promise.all([z(o), z(s)]);
469
481
  if (!c.exists()) return;
470
482
  let u = c.data(), d = l.exists() ? String(l.data().content ?? "") : "";
471
- await lt(e, W(O(e, r), Y("postId", "==", i))), await ut(e, r, i), await dt(e, r, i);
483
+ await gt(e, G(E(e, r), X("postId", "==", i))), await _t(e, r, i), await vt(e, r, i);
472
484
  let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
473
485
  for (let e of u.attachments ?? []) {
474
486
  if (!e?.path) continue;
475
487
  f.add(e.path);
476
- let t = it(e.path);
488
+ let t = dt(e.path);
477
489
  t && t !== i && p.add(t);
478
490
  }
479
- for (let e of nt(d)) {
480
- let t = rt(e);
491
+ for (let e of lt(d)) {
492
+ let t = ut(e);
481
493
  if (!t) continue;
482
494
  f.add(t);
483
495
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
484
496
  n !== t && n.includes("/images/thumbnails/") && f.add(n);
485
497
  let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
486
498
  r && !t.includes("/thumbnails/") && f.add(`${r[1]}thumbnails/${r[2]}.jpg`);
487
- let a = it(t);
499
+ let a = dt(t);
488
500
  a && a !== i && p.add(a);
489
501
  }
490
- await te(o), await ot(n, k(r, i));
491
- for (let e of p) await ot(n, k(r, e));
492
- await st(n, f), await te(a);
502
+ await F(s), await pt(n, D(r, i));
503
+ for (let e of p) await pt(n, D(r, e));
504
+ await mt(n, f), await F(o);
493
505
  }
494
506
  //#endregion
495
507
  //#region src/utils/postBody.ts
496
- function mt(e) {
508
+ function xt(e) {
497
509
  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();
498
510
  }
499
- function ht(e, t = 40) {
500
- let n = mt(e);
511
+ function St(e, t = 40) {
512
+ let n = xt(e);
501
513
  if (!n) return "";
502
514
  if (n.length <= t) return n;
503
515
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
504
516
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
505
517
  }
506
- function gt(e) {
507
- return mt(e).length > 0;
518
+ function Ct(e) {
519
+ return xt(e).length > 0;
508
520
  }
509
- function _t(e, t) {
521
+ function wt(e, t) {
510
522
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
511
523
  }
512
- function vt(e) {
513
- return !gt(e);
524
+ function Tt(e) {
525
+ return !Ct(e);
514
526
  }
515
527
  //#endregion
516
528
  //#region src/utils/postPreview.ts
517
- function yt(e) {
529
+ function Et(e) {
518
530
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
519
531
  }
520
- function bt(e) {
532
+ function Dt(e) {
521
533
  if (e) try {
522
- let t = new URL(yt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
534
+ let t = new URL(Et(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
523
535
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
524
536
  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;
525
537
  } catch {
526
538
  return;
527
539
  }
528
540
  }
529
- function xt(e) {
530
- let t = bt(e.videoUrl);
541
+ function Ot(e) {
542
+ let t = Dt(e.videoUrl);
531
543
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
532
544
  }
533
- function St(e, t = []) {
534
- 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 ? yt(r[1]) : a, s = o ? bt(o) : void 0, c = n ? yt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
545
+ function kt(e, t = []) {
546
+ 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 ? Et(r[1]) : a, s = o ? Dt(o) : void 0, c = n ? Et(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
535
547
  return {
536
548
  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),
537
549
  ...c ? { previewImage: c } : {},
@@ -540,7 +552,7 @@ function St(e, t = []) {
540
552
  }
541
553
  //#endregion
542
554
  //#region src/composables/useMemiBoardSettings.ts
543
- var Ct = [
555
+ var At = [
544
556
  {
545
557
  id: "free",
546
558
  label: "자유",
@@ -568,17 +580,17 @@ var Ct = [
568
580
  visibility: "public",
569
581
  order: 2
570
582
  }
571
- ], wt = Ct;
572
- function Tt(e) {
583
+ ], jt = At;
584
+ function Mt(e) {
573
585
  return e === "hidden" ? "hidden" : "public";
574
586
  }
575
- function Et(e, t, n) {
587
+ function Nt(e, t, n) {
576
588
  return {
577
589
  id: e,
578
590
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
579
591
  order: typeof t.order == "number" ? t.order : n,
580
592
  description: (typeof t.description == "string" ? t.description : "") || "",
581
- visibility: Tt(t.visibility),
593
+ visibility: Mt(t.visibility),
582
594
  listView: t.listView ?? "default",
583
595
  editorType: t.editorType ?? (t.listView === "image" ? "image" : "default"),
584
596
  writeRole: t.writeRole ?? "user",
@@ -586,114 +598,114 @@ function Et(e, t, n) {
586
598
  allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
587
599
  };
588
600
  }
589
- function Dt() {
590
- let t = () => _(), n = Q(), r = le(), { isSignedIn: i, isAdmin: a, isStaff: o, user: s } = tt(), l = M(() => W(e(n, t()), U("order", "asc"))), { data: u, pending: d } = se(l, { ssrKey: w(t(), "boards") }), f = M(() => u.value.map((e, t) => Et(e.id, e, t))), p = f, m = M(() => f.value.filter((e) => e.visibility !== "hidden")), h = m;
591
- function g(e) {
601
+ function Pt() {
602
+ let t = () => h(), n = Q(), r = se(), { isSignedIn: i, isAdmin: a, isStaff: s, user: c } = ct(), l = A(() => G(e(n, t()), W("order", "asc"))), { data: u, pending: d } = ie(l, { ssrKey: S(t(), "boards") }), f = A(() => u.value.map((e, t) => Nt(e.id, e, t))), p = f, m = A(() => f.value.filter((e) => e.visibility !== "hidden")), _ = m;
603
+ function v(e) {
592
604
  if (e) return f.value.find((t) => t.id === e)?.label ?? e;
593
605
  }
594
- let y = g;
595
- function b(e) {
606
+ let y = v;
607
+ function x(e) {
596
608
  if (e) return f.value.find((t) => t.id === e)?.description?.trim() || void 0;
597
609
  }
598
- let x = b;
599
- function C(e) {
610
+ let C = x;
611
+ function w(e) {
600
612
  return e ? f.value.find((t) => t.id === e)?.visibility ?? "public" : "public";
601
613
  }
602
- let T = C;
603
- function E(e) {
604
- return C(e) === "hidden";
614
+ let T = w;
615
+ function D(e) {
616
+ return w(e) === "hidden";
605
617
  }
606
- let D = E;
618
+ let O = D;
607
619
  function k(e) {
608
620
  if (e) return f.value.find((t) => t.id === e);
609
621
  }
610
- We((e) => k(e));
611
- function A(e) {
612
- return Ve(s.value?.uid, a.value, o.value, e ? k(e) : void 0);
622
+ Ze((e) => k(e));
623
+ function j(e) {
624
+ return Je(c.value?.uid, a.value, s.value, e ? k(e) : void 0);
613
625
  }
614
- let j = M(() => f.value.filter((e) => A(e.id)));
615
- async function N(e, t, r) {
626
+ let ee = A(() => f.value.filter((e) => j(e.id)));
627
+ async function M(e, t, r) {
616
628
  let i;
617
629
  for (;;) {
618
- let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
630
+ let a = await B(G(e, X("boardId", "==", t), W("__name__"), ...i ? [J(i)] : [], H(400)));
619
631
  if (a.empty) break;
620
- let o = X(n), s = 0;
632
+ let o = Z(n), s = 0;
621
633
  for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
622
634
  if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
623
635
  }
624
636
  }
625
- async function ee(e, r) {
626
- await N(c(n, t()), e, r), await N(O(n, t()), e, r);
637
+ async function N(e, r) {
638
+ await M(o(n, t()), e, r), await M(E(n, t()), e, r);
627
639
  }
628
640
  function P(e, t) {
629
641
  return {
630
642
  label: e.label.trim(),
631
643
  description: (e.description ?? "").trim(),
632
- visibility: Tt(e.visibility),
644
+ visibility: Mt(e.visibility),
633
645
  listView: e.listView ?? "default",
634
646
  editorType: e.editorType ?? (e.listView === "image" ? "image" : "default"),
635
647
  writeRole: e.writeRole ?? "user",
636
648
  commentWriteRole: e.commentWriteRole ?? "user",
637
649
  allowedStaffUids: e.allowedStaffUids ?? [],
638
650
  order: t,
639
- updatedAt: G()
651
+ updatedAt: K()
640
652
  };
641
653
  }
642
- async function F(e, r = e.order ?? 0) {
654
+ async function I(e, r = e.order ?? 0) {
643
655
  if (!i.value) throw Error("로그인이 필요합니다.");
644
656
  let a = e.id.trim(), o = e.label.trim();
645
657
  if (!a || !o) throw Error("보드 ID와 이름을 입력해 주세요.");
646
- let s = Tt(e.visibility), c = P({
658
+ let s = Mt(e.visibility), c = P({
647
659
  ...e,
648
660
  label: o,
649
661
  visibility: s
650
662
  }, r);
651
- await K(v(n, t(), a), c, { merge: !0 }), await ee(a, s !== "hidden");
663
+ await q(g(n, t(), a), c, { merge: !0 }), await N(a, s !== "hidden");
652
664
  }
653
- let I = F;
654
- async function ne(e) {
665
+ let L = I;
666
+ async function R(e) {
655
667
  if (!i.value) throw Error("로그인이 필요합니다.");
656
- let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
668
+ let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = Z(n), o = [];
657
669
  e.forEach((e, i) => {
658
670
  let s = e.id.trim(), c = e.label.trim();
659
671
  if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
660
- let l = Tt(e.visibility), u = P({
672
+ let l = Mt(e.visibility), u = P({
661
673
  ...e,
662
674
  label: c,
663
675
  visibility: l
664
676
  }, i);
665
- a.set(v(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
677
+ a.set(g(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
666
678
  id: s,
667
679
  listed: l !== "hidden"
668
680
  });
669
681
  }), await a.commit();
670
- for (let e of o) await ee(e.id, e.listed);
682
+ for (let e of o) await N(e.id, e.listed);
671
683
  }
672
- let re = ne;
673
- async function L(e) {
684
+ let te = R;
685
+ async function z(e) {
674
686
  if (!i.value) throw Error("로그인이 필요합니다.");
675
687
  let a = e.trim();
676
688
  if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
677
689
  for (;;) {
678
- let e = await z(W(c(n, t()), Y("boardId", "==", a), V(25)));
690
+ let e = await B(G(o(n, t()), X("boardId", "==", a), H(25)));
679
691
  if (e.empty) break;
680
- for (let i of e.docs) await pt(n, ye(r), t(), i.id);
692
+ for (let i of e.docs) await bt(n, _e(r), t(), i.id);
681
693
  }
682
- await te(v(n, t(), e));
694
+ await F(g(n, t(), e));
683
695
  }
684
- let ie = L;
685
- async function R() {
696
+ let V = z;
697
+ async function ne() {
686
698
  return Promise.resolve();
687
699
  }
688
- async function B(e) {
700
+ async function U(e) {
689
701
  let t = e.trim();
690
702
  if (!t) throw Error("보드 이름을 입력해 주세요.");
691
703
  if (!i.value) throw Error("로그인이 필요합니다.");
692
704
  let n = f.value.find((e) => e.label === t);
693
705
  if (n) return n.id;
694
- let r = S(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
706
+ let r = b(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
695
707
  for (; s.has(a);) a = `${r}-${o++}`;
696
- return await F({
708
+ return await I({
697
709
  id: a,
698
710
  label: t,
699
711
  visibility: "public",
@@ -707,55 +719,55 @@ function Dt() {
707
719
  return {
708
720
  boards: f,
709
721
  publicBoards: m,
710
- boardLabel: g,
711
- boardDescription: b,
712
- boardVisibility: C,
713
- isBoardHidden: E,
722
+ boardLabel: v,
723
+ boardDescription: x,
724
+ boardVisibility: w,
725
+ isBoardHidden: D,
714
726
  getBoard: k,
715
- canManageBoard: A,
716
- managedBoards: j,
727
+ canManageBoard: j,
728
+ managedBoards: ee,
717
729
  categories: p,
718
- publicCategories: h,
730
+ publicCategories: _,
719
731
  categoryLabel: y,
720
- categoryDescription: x,
732
+ categoryDescription: C,
721
733
  categoryVisibility: T,
722
- isCategoryHidden: D,
734
+ isCategoryHidden: O,
723
735
  settingsPending: d,
724
- ensureSettings: R,
725
- addBoard: B,
726
- addCategory: B,
727
- saveBoard: F,
728
- saveCategory: I,
729
- saveBoards: ne,
730
- saveCategories: re,
731
- deleteBoard: L,
732
- deleteCategory: ie,
733
- DEFAULT_BOARDS: Ct,
734
- DEFAULT_CATEGORIES: wt
736
+ ensureSettings: ne,
737
+ addBoard: U,
738
+ addCategory: U,
739
+ saveBoard: I,
740
+ saveCategory: L,
741
+ saveBoards: R,
742
+ saveCategories: te,
743
+ deleteBoard: z,
744
+ deleteCategory: V,
745
+ DEFAULT_BOARDS: At,
746
+ DEFAULT_CATEGORIES: jt
735
747
  };
736
748
  }
737
749
  //#endregion
738
750
  //#region src/composables/useMemiBoardPosts.ts
739
- function Ot(e) {
751
+ function Ft(e) {
740
752
  if (!e || typeof e != "object") return !1;
741
753
  let t = "code" in e ? String(e.code) : "";
742
754
  return t === "permission-denied" || t === "firestore/permission-denied";
743
755
  }
744
- function kt(e) {
745
- let t = F(e)?.trim();
756
+ function It(e) {
757
+ let t = N(e)?.trim();
746
758
  if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
747
759
  return t;
748
760
  }
749
- function At(e) {
750
- let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } = Dt(), o = () => _(), l = () => kt(e), u = () => c(n, o()), d = (e) => t(n, o(), e), f = (e) => s(n, o(), e);
761
+ function Lt(e) {
762
+ let n = Q(), r = se(), { isBoardHidden: i, getBoard: s } = Pt(), c = () => h(), l = () => It(e), u = () => o(n, c()), d = (e) => t(n, c(), e), f = (e) => a(n, c(), e);
751
763
  function p() {
752
764
  return !i(l());
753
765
  }
754
766
  function m() {
755
- return a(l())?.editorType === "image";
767
+ return s(l())?.editorType === "image";
756
768
  }
757
- function h(e) {
758
- let t = m(), n = gt(e.content), r = _t(e.content, e.attachments);
769
+ function g(e) {
770
+ let t = m(), n = Ct(e.content), r = wt(e.content, e.attachments);
759
771
  if (t) {
760
772
  if (!r) throw Error("사진을 올려 주세요.");
761
773
  return;
@@ -763,13 +775,13 @@ function At(e) {
763
775
  if (!n && !r) throw Error("본문에 글자를 입력하거나 이미지를 첨부해 주세요.");
764
776
  if (!e.title?.trim()) throw Error("제목을 입력해 주세요.");
765
777
  }
766
- function g() {
767
- return re(u()).id;
778
+ function _() {
779
+ return L(u()).id;
768
780
  }
769
781
  async function v(e = {}) {
770
- let t = e.pageSize ?? 20, n = [Y("boardId", "==", l()), Y("isPublished", "==", !0)];
771
- 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));
772
- let r = await z(W(u(), ...n)), i = r.docs.slice(0, t);
782
+ let t = e.pageSize ?? 20, n = [X("boardId", "==", l()), X("isPublished", "==", !0)];
783
+ 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));
784
+ let r = await B(G(u(), ...n)), i = r.docs.slice(0, t);
773
785
  return {
774
786
  posts: i.map((e) => ({
775
787
  id: e.id,
@@ -780,16 +792,16 @@ function At(e) {
780
792
  };
781
793
  }
782
794
  async function y(e) {
783
- let [t, n] = await Promise.all([R(d(e)), R(f(e))]);
795
+ let [t, n] = await Promise.all([z(d(e)), z(f(e))]);
784
796
  return t.exists() ? {
785
797
  id: t.id,
786
798
  ...t.data(),
787
799
  content: n.exists() ? n.data().content ?? "" : ""
788
800
  } : null;
789
801
  }
790
- async function b(e) {
802
+ async function x(e) {
791
803
  try {
792
- let t = (await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", e.trim()), V(1)))).docs[0];
804
+ let t = (await B(G(u(), X("boardId", "==", l()), X("slug", "==", e.trim()), H(1)))).docs[0];
793
805
  if (!t) return { status: "not-found" };
794
806
  try {
795
807
  let e = await y(t.id);
@@ -798,30 +810,30 @@ function At(e) {
798
810
  post: e
799
811
  } : { status: "not-found" };
800
812
  } catch (e) {
801
- return Ot(e) ? { status: "permission-denied" } : {
813
+ return Ft(e) ? { status: "permission-denied" } : {
802
814
  status: "error",
803
815
  message: e instanceof Error ? e.message : String(e)
804
816
  };
805
817
  }
806
818
  } catch (e) {
807
- return Ot(e) ? { status: "permission-denied" } : {
819
+ return Ft(e) ? { status: "permission-denied" } : {
808
820
  status: "error",
809
821
  message: e instanceof Error ? e.message : String(e)
810
822
  };
811
823
  }
812
824
  }
813
- async function x(e) {
825
+ async function S(e) {
814
826
  if (!e.createdAt) return {
815
827
  previous: null,
816
828
  next: null
817
829
  };
818
830
  try {
819
831
  let t = [
820
- Y("boardId", "==", l()),
821
- Y("isPublished", "==", !0),
822
- ...e.listed === !1 ? [] : [Y("listed", "==", !0)],
823
- U("createdAt", "desc")
824
- ], [n, r] = await Promise.all([z(W(u(), ...t, q(e.createdAt), V(1))), z(W(u(), ...t, ie(e.createdAt), ae(1)))]), i = (e) => {
832
+ X("boardId", "==", l()),
833
+ X("isPublished", "==", !0),
834
+ ...e.listed === !1 ? [] : [X("listed", "==", !0)],
835
+ W("createdAt", "desc")
836
+ ], [n, r] = await Promise.all([B(G(u(), ...t, J(e.createdAt), H(1))), B(G(u(), ...t, te(e.createdAt), ne(1)))]), i = (e) => {
825
837
  let t = e.docs[0];
826
838
  return t ? {
827
839
  id: t.id,
@@ -840,18 +852,18 @@ function At(e) {
840
852
  }
841
853
  }
842
854
  async function C(e) {
843
- h(e);
844
- let t = e.postId || g(), n = m(), r, i;
845
- if (n) r = t, i = ht(e.content);
855
+ g(e);
856
+ let t = e.postId || _(), n = m(), r, i;
857
+ if (n) r = t, i = St(e.content);
846
858
  else {
847
859
  i = e.title.trim();
848
- let t = S(i) || "post";
860
+ let t = b(i) || "post";
849
861
  r = t;
850
862
  let n = 1;
851
- for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
863
+ for (; !(await B(G(u(), X("boardId", "==", l()), X("slug", "==", r), H(1)))).empty;) n++, r = `${t}-${n}`;
852
864
  }
853
- let a = St(e.content, e.attachments), o = p();
854
- return await K(d(t), {
865
+ let a = kt(e.content, e.attachments), o = p();
866
+ return await q(d(t), {
855
867
  boardId: l(),
856
868
  slug: r,
857
869
  title: i,
@@ -868,84 +880,84 @@ function At(e) {
868
880
  authorPhoto: e.authorPhoto,
869
881
  moderationStatus: "approved",
870
882
  moderationModel: e.moderationModel ?? null,
871
- createdAt: G(),
872
- updatedAt: G()
873
- }), await K(f(t), { content: e.content }), r;
883
+ createdAt: K(),
884
+ updatedAt: K()
885
+ }), await q(f(t), { content: e.content }), r;
874
886
  }
875
887
  async function w(e, t) {
876
- h(t);
877
- let n = m() ? ht(t.content) : t.title.trim(), r = St(t.content, t.attachments), i = p();
878
- await Promise.all([J(d(e), {
888
+ g(t);
889
+ let n = m() ? St(t.content) : t.title.trim(), r = kt(t.content, t.attachments), i = p();
890
+ await Promise.all([Y(d(e), {
879
891
  title: n,
880
892
  ...r,
881
- previewImage: r.previewImage ? r.previewImage : ne(),
882
- videoUrl: r.videoUrl ? r.videoUrl : ne(),
893
+ previewImage: r.previewImage ? r.previewImage : I(),
894
+ videoUrl: r.videoUrl ? r.videoUrl : I(),
883
895
  tags: t.tags ?? [],
884
896
  listed: i,
885
897
  attachments: t.attachments ?? [],
886
- updatedAt: G()
887
- }), K(f(e), { content: t.content }, { merge: !0 })]);
898
+ updatedAt: K()
899
+ }), q(f(e), { content: t.content }, { merge: !0 })]);
888
900
  }
889
901
  async function T(e) {
890
- await J(d(e), {
902
+ await Y(d(e), {
891
903
  isPublished: !0,
892
- publishedAt: G(),
893
- updatedAt: G()
904
+ publishedAt: K(),
905
+ updatedAt: K()
894
906
  });
895
907
  }
896
908
  async function E(e) {
897
- await pt(n, ye(r), o(), e);
909
+ await bt(n, _e(r), c(), e);
898
910
  }
899
911
  return {
900
- createPostId: g,
912
+ createPostId: _,
901
913
  getPosts: v,
902
914
  getPost: y,
903
- getPostBySlug: b,
904
- getAdjacentPosts: x,
915
+ getPostBySlug: x,
916
+ getAdjacentPosts: S,
905
917
  createPost: C,
906
918
  updatePost: w,
907
919
  publishPost: T,
908
920
  deletePost: E
909
921
  };
910
922
  }
911
- var jt = 10;
912
- function Mt(e, t = {}) {
913
- let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = tt(), o = () => _(), s = t.pageSize ?? jt, l = M(() => i.value?.uid ?? ""), u = M(() => kt(e)), d = () => c(n, o()), f = M(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = P([]), m = P([]), h = P([]), g = P(!0), v = P(!0), y = P(!1), b = P(""), x = null, S = null, C, w;
923
+ var Rt = 10;
924
+ function zt(e, t = {}) {
925
+ let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = ct(), s = () => h(), c = t.pageSize ?? Rt, l = A(() => i.value?.uid ?? ""), u = A(() => It(e)), d = () => o(n, s()), f = A(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = M([]), m = M([]), g = M([]), _ = M(!0), v = M(!0), y = M(!1), b = M(""), x = null, S = null, C, w;
914
926
  function T(e) {
915
927
  return e.createdAt?.toMillis?.() ?? 0;
916
928
  }
917
- let E = M(() => {
929
+ let E = A(() => {
918
930
  let e = /* @__PURE__ */ new Map();
919
931
  for (let t of [
920
932
  ...m.value,
921
933
  ...p.value,
922
- ...h.value
934
+ ...g.value
923
935
  ]) t.id && e.set(t.id, t);
924
936
  return [...e.values()].sort((e, t) => T(t) - T(e) || String(t.id).localeCompare(String(e.id)));
925
937
  });
926
938
  function D() {
927
- let e = [Y("boardId", "==", u.value), Y("isPublished", "==", !0)];
928
- return f.value && e.push(Y("listed", "==", !0)), e;
939
+ let e = [X("boardId", "==", u.value), X("isPublished", "==", !0)];
940
+ return f.value && e.push(X("listed", "==", !0)), e;
929
941
  }
930
942
  function O() {
931
- C?.(), C = H(W(d(), ...D(), U("createdAt", "desc"), U(L(), "desc"), V(s)), (e) => {
943
+ C?.(), C = U(G(d(), ...D(), W("createdAt", "desc"), W(R(), "desc"), H(c)), (e) => {
932
944
  p.value = e.docs.map((e) => ({
933
945
  id: e.id,
934
946
  ...e.data()
935
- })), x = e.docs.at(-1) ?? null, S || (v.value = e.docs.length >= s), g.value = !1;
947
+ })), x = e.docs.at(-1) ?? null, S || (v.value = e.docs.length >= c), _.value = !1;
936
948
  }, (e) => {
937
- console.error("[memi-board:postList] onSnapshot failed", e), b.value = e.message || String(e), g.value = !1;
949
+ console.error("[memi-board:postList] onSnapshot failed", e), b.value = e.message || String(e), _.value = !1;
938
950
  });
939
951
  }
940
952
  function k() {
941
953
  w?.();
942
954
  let e = l.value;
943
955
  if (!e) {
944
- h.value = [];
956
+ g.value = [];
945
957
  return;
946
958
  }
947
- w = H(W(d(), Y("boardId", "==", u.value), Y("authorUid", "==", e), Y("isPublished", "==", !1), U("createdAt", "desc"), V(20)), (e) => {
948
- h.value = e.docs.map((e) => ({
959
+ w = U(G(d(), X("boardId", "==", u.value), X("authorUid", "==", e), X("isPublished", "==", !1), W("createdAt", "desc"), H(20)), (e) => {
960
+ g.value = e.docs.map((e) => ({
949
961
  id: e.id,
950
962
  ...e.data()
951
963
  }));
@@ -953,21 +965,21 @@ function Mt(e, t = {}) {
953
965
  console.error("[memi-board:postList] own draft onSnapshot failed", e);
954
966
  });
955
967
  }
956
- async function A() {
957
- if (!(y.value || !v.value || g.value)) {
968
+ async function ee() {
969
+ if (!(y.value || !v.value || _.value)) {
958
970
  y.value = !0, b.value = "";
959
971
  try {
960
972
  let e = S ?? x, t = [
961
973
  ...D(),
962
- U("createdAt", "desc"),
963
- U(L(), "desc"),
964
- ...e ? [q(e)] : [],
965
- V(s + 1)
966
- ], n = await z(W(d(), ...t)), r = n.docs.slice(0, s), i = r.map((e) => ({
974
+ W("createdAt", "desc"),
975
+ W(R(), "desc"),
976
+ ...e ? [J(e)] : [],
977
+ H(c + 1)
978
+ ], n = await B(G(d(), ...t)), r = n.docs.slice(0, c), i = r.map((e) => ({
967
979
  id: e.id,
968
980
  ...e.data()
969
981
  })), a = new Set([...m.value, ...p.value].map((e) => e.id));
970
- m.value.push(...i.filter((e) => !a.has(e.id))), S = r.at(-1) ?? S, v.value = n.docs.length > s;
982
+ m.value.push(...i.filter((e) => !a.has(e.id))), S = r.at(-1) ?? S, v.value = n.docs.length > c;
971
983
  } catch (e) {
972
984
  b.value = e.message || String(e), console.error("[memi-board:postList] loadMore failed", e);
973
985
  } finally {
@@ -975,40 +987,40 @@ function Mt(e, t = {}) {
975
987
  }
976
988
  }
977
989
  }
978
- function j() {
979
- C?.(), p.value = [], m.value = [], x = null, S = null, v.value = !0, b.value = "", g.value = !0, O(), k();
990
+ function N() {
991
+ C?.(), p.value = [], m.value = [], x = null, S = null, v.value = !0, b.value = "", _.value = !0, O(), k();
980
992
  }
981
- return I([
993
+ return P([
982
994
  u,
983
995
  f,
984
996
  l
985
- ], j, { immediate: !0 }), N(() => {
997
+ ], N, { immediate: !0 }), j(() => {
986
998
  C?.(), w?.();
987
999
  }), {
988
1000
  posts: E,
989
- postsPending: g,
1001
+ postsPending: _,
990
1002
  hasMore: v,
991
1003
  loadingMore: y,
992
1004
  loadError: b,
993
- loadMore: A
1005
+ loadMore: ee
994
1006
  };
995
1007
  }
996
- function Nt(e, n) {
997
- let r = Q(), i = () => _(), a = M(() => kt(e)), o = M(() => typeof n == "string" ? n : n.value), c = M(() => t(r, i(), o.value)), l = M(() => s(r, i(), o.value)), { data: u, pending: d } = ce(c), { data: f, pending: p } = ce(l);
1008
+ function Bt(e, n) {
1009
+ let r = Q(), i = () => h(), o = A(() => It(e)), s = A(() => typeof n == "string" ? n : n.value), c = A(() => t(r, i(), s.value)), l = A(() => a(r, i(), s.value)), { data: u, pending: d } = oe(c), { data: f, pending: p } = oe(l);
998
1010
  return {
999
- post: M(() => !u.value || u.value.boardId !== a.value ? null : {
1011
+ post: A(() => !u.value || u.value.boardId !== o.value ? null : {
1000
1012
  ...u.value,
1001
- id: o.value,
1013
+ id: s.value,
1002
1014
  content: f.value?.content ?? ""
1003
1015
  }),
1004
- pending: M(() => d.value || p.value)
1016
+ pending: A(() => d.value || p.value)
1005
1017
  };
1006
1018
  }
1007
1019
  //#endregion
1008
1020
  //#region src/composables/useMemiBoardUserPosts.ts
1009
- var Pt = 10;
1010
- function Ft(e, t = {}) {
1011
- let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ?? Pt, o = M(() => F(e)?.trim() || ""), s = P([]), l = P([]), u = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
1021
+ var Vt = 10;
1022
+ function Ht(e, t = {}) {
1023
+ let n = Q(), r = () => h(), i = () => o(n, r()), a = t.pageSize ?? Vt, s = A(() => N(e)?.trim() || ""), c = M([]), l = M([]), u = M(!0), d = M(!0), f = M(!1), p = M(""), m = null, g = null, _;
1012
1024
  function v(e) {
1013
1025
  return {
1014
1026
  id: e.id,
@@ -1018,41 +1030,41 @@ function Ft(e, t = {}) {
1018
1030
  function y(e) {
1019
1031
  return e.createdAt?.toMillis?.() ?? 0;
1020
1032
  }
1021
- let b = M(() => {
1033
+ let b = A(() => {
1022
1034
  let e = /* @__PURE__ */ new Map();
1023
- for (let t of [...l.value, ...s.value]) t.id && e.set(t.id, t);
1035
+ for (let t of [...l.value, ...c.value]) t.id && e.set(t.id, t);
1024
1036
  return [...e.values()].sort((e, t) => y(t) - y(e) || String(t.id).localeCompare(String(e.id)));
1025
1037
  });
1026
1038
  function x() {
1027
1039
  return [
1028
- Y("authorUid", "==", o.value),
1029
- Y("isPublished", "==", !0),
1030
- Y("listed", "==", !0)
1040
+ X("authorUid", "==", s.value),
1041
+ X("isPublished", "==", !0),
1042
+ X("listed", "==", !0)
1031
1043
  ];
1032
1044
  }
1033
1045
  function S() {
1034
- if (g?.(), !o.value) {
1046
+ if (_?.(), !s.value) {
1035
1047
  u.value = !1;
1036
1048
  return;
1037
1049
  }
1038
- g = H(W(i(), ...x(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
1039
- s.value = e.docs.map(v), m = e.docs.at(-1) ?? null, h || (d.value = e.docs.length >= a), u.value = !1;
1050
+ _ = U(G(i(), ...x(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1051
+ c.value = e.docs.map(v), m = e.docs.at(-1) ?? null, g || (d.value = e.docs.length >= a), u.value = !1;
1040
1052
  }, (e) => {
1041
1053
  console.error("[memi-board:userPosts] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
1042
1054
  });
1043
1055
  }
1044
1056
  async function C() {
1045
- if (!(f.value || !d.value || u.value || !o.value)) {
1057
+ if (!f.value && d.value && !u.value && s.value) {
1046
1058
  f.value = !0, p.value = "";
1047
1059
  try {
1048
- let e = h ?? m, t = [
1060
+ let e = g ?? m, t = [
1049
1061
  ...x(),
1050
- U("createdAt", "desc"),
1051
- U(L(), "desc"),
1052
- ...e ? [q(e)] : [],
1053
- V(a + 1)
1054
- ], n = await z(W(i(), ...t)), r = n.docs.slice(0, a), o = r.map(v), c = new Set([...l.value, ...s.value].map((e) => e.id));
1055
- l.value.push(...o.filter((e) => !c.has(e.id))), h = r.at(-1) ?? h, d.value = n.docs.length > a;
1062
+ W("createdAt", "desc"),
1063
+ W(R(), "desc"),
1064
+ ...e ? [J(e)] : [],
1065
+ H(a + 1)
1066
+ ], n = await B(G(i(), ...t)), r = n.docs.slice(0, a), o = r.map(v), s = new Set([...l.value, ...c.value].map((e) => e.id));
1067
+ l.value.push(...o.filter((e) => !s.has(e.id))), g = r.at(-1) ?? g, d.value = n.docs.length > a;
1056
1068
  } catch (e) {
1057
1069
  p.value = e.message || String(e), console.error("[memi-board:userPosts] loadMore failed", e);
1058
1070
  } finally {
@@ -1061,9 +1073,9 @@ function Ft(e, t = {}) {
1061
1073
  }
1062
1074
  }
1063
1075
  function w() {
1064
- g?.(), s.value = [], l.value = [], m = null, h = null, d.value = !0, p.value = "", u.value = !0, S();
1076
+ _?.(), c.value = [], l.value = [], m = null, g = null, d.value = !0, p.value = "", u.value = !0, S();
1065
1077
  }
1066
- return I(o, w, { immediate: !0 }), N(() => g?.()), {
1078
+ return P(s, w, { immediate: !0 }), j(() => _?.()), {
1067
1079
  posts: b,
1068
1080
  postsPending: u,
1069
1081
  hasMore: d,
@@ -1074,10 +1086,10 @@ function Ft(e, t = {}) {
1074
1086
  }
1075
1087
  //#endregion
1076
1088
  //#region src/composables/useMemiBoardUserComments.ts
1077
- var It = 10;
1078
- function Lt(e, t = {}) {
1079
- let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ?? It, 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;
1080
- function g(e) {
1089
+ var Ut = 10;
1090
+ function Wt(e, t = {}) {
1091
+ let n = Q(), r = () => h(), i = () => E(n, r()), a = t.pageSize ?? Ut, o = A(() => N(e)?.trim() || ""), s = M([]), c = M([]), l = M(!0), u = M(!0), d = M(!1), f = M(""), p = null, m = null, g;
1092
+ function _(e) {
1081
1093
  return {
1082
1094
  id: e.id,
1083
1095
  ...e.data()
@@ -1086,36 +1098,36 @@ function Lt(e, t = {}) {
1086
1098
  function v(e) {
1087
1099
  return e.createdAt?.toMillis?.() ?? 0;
1088
1100
  }
1089
- let y = M(() => {
1101
+ let y = A(() => {
1090
1102
  let e = /* @__PURE__ */ new Map();
1091
1103
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1092
1104
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
1093
1105
  });
1094
1106
  function b() {
1095
- return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
1107
+ return [X("authorUid", "==", o.value), X("listed", "==", !0)];
1096
1108
  }
1097
1109
  function x() {
1098
- if (h?.(), !o.value) {
1110
+ if (g?.(), !o.value) {
1099
1111
  l.value = !1;
1100
1112
  return;
1101
1113
  }
1102
- h = H(W(i(), ...b(), U("createdAt", "desc"), U(L(), "desc"), V(a)), (e) => {
1103
- s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
1114
+ g = U(G(i(), ...b(), W("createdAt", "desc"), W(R(), "desc"), H(a)), (e) => {
1115
+ s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a), l.value = !1;
1104
1116
  }, (e) => {
1105
1117
  console.error("[memi-board:userComments] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
1106
1118
  });
1107
1119
  }
1108
1120
  async function S() {
1109
- if (!(d.value || !u.value || l.value || !o.value)) {
1121
+ if (!d.value && u.value && !l.value && o.value) {
1110
1122
  d.value = !0, f.value = "";
1111
1123
  try {
1112
1124
  let e = m ?? p, t = [
1113
1125
  ...b(),
1114
- U("createdAt", "desc"),
1115
- U(L(), "desc"),
1116
- ...e ? [q(e)] : [],
1117
- V(a + 1)
1118
- ], 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(_), l = new Set([...c.value, ...s.value].map((e) => e.id));
1119
1131
  c.value.push(...o.filter((e) => !l.has(e.id))), m = r.at(-1) ?? m, u.value = n.docs.length > a;
1120
1132
  } catch (e) {
1121
1133
  f.value = e.message || String(e), console.error("[memi-board:userComments] loadMore failed", e);
@@ -1125,9 +1137,9 @@ function Lt(e, t = {}) {
1125
1137
  }
1126
1138
  }
1127
1139
  function C() {
1128
- h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, x();
1140
+ g?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, x();
1129
1141
  }
1130
- return I(o, C, { immediate: !0 }), N(() => h?.()), {
1142
+ return P(o, C, { immediate: !0 }), j(() => g?.()), {
1131
1143
  comments: y,
1132
1144
  commentsPending: l,
1133
1145
  hasMore: u,
@@ -1138,13 +1150,13 @@ function Lt(e, t = {}) {
1138
1150
  }
1139
1151
  //#endregion
1140
1152
  //#region src/composables/useMemiBoardCommentFeed.ts
1141
- var Rt = 10;
1142
- function zt(e = "latest", t = {}) {
1143
- let n = Q(), r = () => _(), i = () => O(n, r()), a = M(() => {
1144
- let e = Math.floor(Number(F(t.pageSize)));
1145
- return Number.isFinite(e) && e > 0 ? e : Rt;
1146
- }), o = M(() => F(e) === "likes" ? "likes" : "latest"), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
1147
- function g(e) {
1153
+ var Gt = 10;
1154
+ function Kt(e = "latest", t = {}) {
1155
+ let n = Q(), r = () => h(), i = () => E(n, r()), a = A(() => {
1156
+ let e = Math.floor(Number(N(t.pageSize)));
1157
+ return Number.isFinite(e) && e > 0 ? e : Gt;
1158
+ }), o = A(() => N(e) === "likes" ? "likes" : "latest"), s = M([]), c = M([]), l = M(!0), u = M(!0), d = M(!1), f = M(""), p = null, m = null, g;
1159
+ function _(e) {
1148
1160
  return {
1149
1161
  id: e.id,
1150
1162
  ...e.data()
@@ -1160,26 +1172,26 @@ function zt(e = "latest", t = {}) {
1160
1172
  }
1161
1173
  return v(t) - v(e) || String(t.id).localeCompare(String(e.id));
1162
1174
  }
1163
- let b = M(() => {
1175
+ let b = A(() => {
1164
1176
  let e = /* @__PURE__ */ new Map();
1165
1177
  for (let t of [...c.value, ...s.value]) t.id && e.set(t.id, t);
1166
1178
  return [...e.values()].sort(y);
1167
1179
  });
1168
1180
  function x() {
1169
1181
  return o.value === "likes" ? [
1170
- Y("listed", "==", !0),
1171
- U("likeCount", "desc"),
1172
- U("createdAt", "desc"),
1173
- U(L(), "desc")
1182
+ X("listed", "==", !0),
1183
+ W("likeCount", "desc"),
1184
+ W("createdAt", "desc"),
1185
+ W(R(), "desc")
1174
1186
  ] : [
1175
- Y("listed", "==", !0),
1176
- U("createdAt", "desc"),
1177
- U(L(), "desc")
1187
+ X("listed", "==", !0),
1188
+ W("createdAt", "desc"),
1189
+ W(R(), "desc")
1178
1190
  ];
1179
1191
  }
1180
1192
  function S() {
1181
- h?.(), h = H(W(i(), ...x(), V(a.value)), (e) => {
1182
- s.value = e.docs.map(g), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
1193
+ g?.(), g = U(G(i(), ...x(), H(a.value)), (e) => {
1194
+ s.value = e.docs.map(_), p = e.docs.at(-1) ?? null, m || (u.value = e.docs.length >= a.value), l.value = !1;
1183
1195
  }, (e) => {
1184
1196
  console.error("[memi-board:commentFeed] onSnapshot failed", e), f.value = e.message || String(e), l.value = !1;
1185
1197
  });
@@ -1188,7 +1200,7 @@ function zt(e = "latest", t = {}) {
1188
1200
  if (!(d.value || !u.value || l.value)) {
1189
1201
  d.value = !0, f.value = "";
1190
1202
  try {
1191
- let e = m ?? p, t = await z(W(i(), ...x(), ...e ? [q(e)] : [], V(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(g), o = new Set([...c.value, ...s.value].map((e) => e.id));
1203
+ let e = m ?? p, t = await B(G(i(), ...x(), ...e ? [J(e)] : [], H(a.value + 1))), n = t.docs.slice(0, a.value), r = n.map(_), o = new Set([...c.value, ...s.value].map((e) => e.id));
1192
1204
  c.value.push(...r.filter((e) => !o.has(e.id))), m = n.at(-1) ?? m, u.value = t.docs.length > a.value;
1193
1205
  } catch (e) {
1194
1206
  f.value = e.message || String(e), console.error("[memi-board:commentFeed] loadMore failed", e);
@@ -1198,9 +1210,9 @@ function zt(e = "latest", t = {}) {
1198
1210
  }
1199
1211
  }
1200
1212
  function w() {
1201
- h?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, S();
1213
+ g?.(), s.value = [], c.value = [], p = null, m = null, u.value = !0, f.value = "", l.value = !0, S();
1202
1214
  }
1203
- return I([o, a], w, { immediate: !0 }), N(() => h?.()), {
1215
+ return P([o, a], w, { immediate: !0 }), j(() => g?.()), {
1204
1216
  comments: b,
1205
1217
  commentsPending: l,
1206
1218
  hasMore: u,
@@ -1212,9 +1224,9 @@ function zt(e = "latest", t = {}) {
1212
1224
  }
1213
1225
  //#endregion
1214
1226
  //#region src/composables/useMemiBoardAuthorMemo.ts
1215
- var Bt = 100, Vt = /* @__PURE__ */ new Map();
1227
+ var qt = 100, Jt = /* @__PURE__ */ new Map();
1216
1228
  function $(e) {
1217
- let t = Vt.get(e);
1229
+ let t = Jt.get(e);
1218
1230
  return t || (t = ee({
1219
1231
  text: "",
1220
1232
  sentiment: null,
@@ -1222,25 +1234,25 @@ function $(e) {
1222
1234
  pending: !1,
1223
1235
  saving: !1,
1224
1236
  error: ""
1225
- }), Vt.set(e, t)), t;
1237
+ }), Jt.set(e, t)), t;
1226
1238
  }
1227
- function Ht(e) {
1228
- let t = Q(), n = () => _(), { user: r } = tt(), i = M(() => F(e)), a = M(() => `${r.value?.uid ?? ""}:${i.value}`), o = M(() => {
1239
+ function Yt(e) {
1240
+ let t = Q(), n = () => h(), { user: r } = ct(), i = A(() => N(e)), a = A(() => `${r.value?.uid ?? ""}:${i.value}`), o = A(() => {
1229
1241
  let e = r.value?.uid, a = i.value;
1230
- return !e || !a ? null : m(t, n(), e, a);
1231
- }), s = M({
1242
+ return !e || !a ? null : f(t, n(), e, a);
1243
+ }), s = A({
1232
1244
  get: () => $(a.value).text,
1233
1245
  set: (e) => {
1234
1246
  $(a.value).text = e;
1235
1247
  }
1236
- }), c = M(() => $(a.value).sentiment), l = M(() => $(a.value).loaded), u = M(() => $(a.value).pending), d = M(() => $(a.value).saving), f = M(() => $(a.value).error);
1237
- async function p() {
1248
+ }), c = A(() => $(a.value).sentiment), l = A(() => $(a.value).loaded), u = A(() => $(a.value).pending), d = A(() => $(a.value).saving), p = A(() => $(a.value).error);
1249
+ async function m() {
1238
1250
  let e = o.value;
1239
1251
  if (!e) return;
1240
1252
  let t = $(a.value);
1241
1253
  t.pending = !0, t.error = "";
1242
1254
  try {
1243
- let n = (await R(e)).data();
1255
+ let n = (await z(e)).data();
1244
1256
  t.text = n ? n.text ?? "" : "", t.sentiment = n ? n.sentiment ?? null : null, t.loaded = !0;
1245
1257
  } catch (e) {
1246
1258
  t.error = e.message || String(e), console.error("[memi-board:authorMemo] load failed", e);
@@ -1248,7 +1260,7 @@ function Ht(e) {
1248
1260
  t.pending = !1;
1249
1261
  }
1250
1262
  }
1251
- async function h(e, t) {
1263
+ async function g(e, t) {
1252
1264
  let n = o.value;
1253
1265
  if (!n) return;
1254
1266
  let r = e.trim();
@@ -1256,10 +1268,10 @@ function Ht(e) {
1256
1268
  let i = $(a.value);
1257
1269
  i.saving = !0, i.error = "";
1258
1270
  try {
1259
- await K(n, {
1271
+ await q(n, {
1260
1272
  text: r,
1261
1273
  sentiment: t,
1262
- updatedAt: G()
1274
+ updatedAt: K()
1263
1275
  }), i.text = r, i.sentiment = t, i.loaded = !0;
1264
1276
  } catch (e) {
1265
1277
  throw i.error = e.message || String(e), console.error("[memi-board:authorMemo] save failed", e), e;
@@ -1267,13 +1279,13 @@ function Ht(e) {
1267
1279
  i.saving = !1;
1268
1280
  }
1269
1281
  }
1270
- async function g() {
1282
+ async function _() {
1271
1283
  let e = o.value;
1272
1284
  if (!e) return;
1273
1285
  let t = $(a.value);
1274
1286
  t.saving = !0, t.error = "";
1275
1287
  try {
1276
- await te(e), t.text = "", t.sentiment = null, t.loaded = !0;
1288
+ await F(e), t.text = "", t.sentiment = null, t.loaded = !0;
1277
1289
  } catch (e) {
1278
1290
  throw t.error = e.message || String(e), console.error("[memi-board:authorMemo] remove failed", e), e;
1279
1291
  } finally {
@@ -1286,40 +1298,40 @@ function Ht(e) {
1286
1298
  loaded: l,
1287
1299
  pending: u,
1288
1300
  saving: d,
1289
- error: f,
1290
- load: p,
1291
- save: h,
1292
- remove: g
1301
+ error: p,
1302
+ load: m,
1303
+ save: g,
1304
+ remove: _
1293
1305
  };
1294
1306
  }
1295
1307
  //#endregion
1296
1308
  //#region src/composables/useMemiBoardComments.ts
1297
- var Ut = 10, Wt = 5, Gt = 1e3;
1298
- function Kt(e) {
1309
+ var Xt = 10, Zt = 5, Qt = 1e3;
1310
+ function $t(e) {
1299
1311
  let t = e.trim();
1300
1312
  if (!t) throw Error("댓글 내용을 입력해 주세요.");
1301
- if (t.length > 1e3) throw Error(`댓글은 ${Gt.toLocaleString()}자까지 작성할 수 있습니다.`);
1313
+ if (t.length > 1e3) throw Error(`댓글은 ${Qt.toLocaleString()}자까지 작성할 수 있습니다.`);
1302
1314
  return t;
1303
1315
  }
1304
- function qt(e, n, r = {}) {
1305
- let i = Q(), a = () => _(), { isBoardHidden: o } = Dt(), s = () => O(i, a()), c = M(() => F(e)), u = M(() => F(n)), d = () => !o(c.value), f = P([]), p = P([]), m = P(r.subscribe !== !1), h = P([]), g = P(!1), v = P(r.subscribe !== !1), y = P(r.subscribe !== !1), b = null, x, S;
1316
+ function en(e, n, r = {}) {
1317
+ let i = Q(), a = () => h(), { isBoardHidden: o } = Pt(), c = () => E(i, a()), l = A(() => N(e)), u = A(() => N(n)), d = () => !o(l.value), f = M([]), p = M([]), m = M(r.subscribe !== !1), g = M([]), _ = M(!1), v = M(r.subscribe !== !1), y = M(r.subscribe !== !1), b = null, x, S;
1306
1318
  function C(e) {
1307
1319
  return e.createdAt?.toMillis?.() ?? 0;
1308
1320
  }
1309
- let w = M(() => {
1321
+ let w = A(() => {
1310
1322
  let e = /* @__PURE__ */ new Map();
1311
1323
  for (let t of [
1312
1324
  ...f.value,
1313
1325
  ...p.value,
1314
- ...h.value
1326
+ ...g.value
1315
1327
  ]) t.id && e.set(t.id, t);
1316
1328
  return [...e.values()].sort((e, t) => C(e) - C(t) || String(e.id).localeCompare(String(t.id)));
1317
1329
  });
1318
- I([u, c], async ([e]) => {
1319
- if (h.value = [], f.value = [], p.value = [], b = null, clearTimeout(x), x = void 0, S?.(), S = void 0, v.value = r.subscribe !== !1, r.subscribe !== !1) {
1330
+ P([u, l], async ([e]) => {
1331
+ if (g.value = [], f.value = [], p.value = [], b = null, clearTimeout(x), x = void 0, S?.(), S = void 0, v.value = r.subscribe !== !1, r.subscribe !== !1) {
1320
1332
  m.value = !0, y.value = !0;
1321
1333
  try {
1322
- let t = z(W(s(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1334
+ let t = B(G(c(), X("postId", "==", e), W("createdAt", "desc"), W(R(), "desc")));
1323
1335
  if (await Promise.all([t.then((t) => {
1324
1336
  if (u.value !== e) return;
1325
1337
  let n = t.docs.map((e) => ({
@@ -1327,14 +1339,14 @@ function qt(e, n, r = {}) {
1327
1339
  ...e.data()
1328
1340
  }));
1329
1341
  p.value = n.filter((e) => e.parentId === void 0);
1330
- }), E()]), u.value !== e) return;
1342
+ }), D()]), u.value !== e) return;
1331
1343
  } finally {
1332
1344
  u.value === e && (m.value = !1, y.value = !1);
1333
1345
  }
1334
1346
  }
1335
1347
  }, { immediate: !0 });
1336
1348
  function T() {
1337
- S?.(), S = H(W(s(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1349
+ S?.(), S = U(G(c(), X("postId", "==", u.value), X("parentId", "==", null), W("createdAt", "desc"), W(R(), "desc"), H(1)), (e) => {
1338
1350
  let t = new Map(f.value.map((e) => [e.id, e]));
1339
1351
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1340
1352
  id: n.doc.id,
@@ -1345,37 +1357,37 @@ function qt(e, n, r = {}) {
1345
1357
  console.error("[memi-board:comments] onSnapshot failed", e);
1346
1358
  });
1347
1359
  }
1348
- async function E() {
1349
- if (!(g.value || !v.value)) {
1350
- g.value = !0;
1360
+ async function D() {
1361
+ if (!_.value && v.value) {
1362
+ _.value = !0;
1351
1363
  try {
1352
1364
  let e = [
1353
- Y("postId", "==", u.value),
1354
- Y("parentId", "==", null),
1355
- U("createdAt", "asc"),
1356
- U(L(), "asc"),
1357
- ...b ? [q(b)] : [],
1358
- V(11)
1359
- ], t = await z(W(s(), ...e)), n = t.docs.slice(0, Ut), r = n.map((e) => ({
1365
+ X("postId", "==", u.value),
1366
+ X("parentId", "==", null),
1367
+ W("createdAt", "asc"),
1368
+ W(R(), "asc"),
1369
+ ...b ? [J(b)] : [],
1370
+ H(11)
1371
+ ], t = await B(G(c(), ...e)), n = t.docs.slice(0, Xt), r = n.map((e) => ({
1360
1372
  id: e.id,
1361
1373
  ...e.data()
1362
- })), i = new Set(h.value.map((e) => e.id));
1363
- h.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Ut && (v.value = !1, x = setTimeout(() => {
1374
+ })), i = new Set(g.value.map((e) => e.id));
1375
+ g.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Xt && (v.value = !1, x = setTimeout(() => {
1364
1376
  x = void 0, T();
1365
1377
  }, 1e3));
1366
1378
  } finally {
1367
- g.value = !1;
1379
+ _.value = !1;
1368
1380
  }
1369
1381
  }
1370
1382
  }
1371
- N(() => {
1383
+ j(() => {
1372
1384
  clearTimeout(x), S?.();
1373
1385
  });
1374
- async function D(e) {
1375
- let n = Kt(e.body), r = X(i), o = re(s());
1386
+ async function O(e) {
1387
+ let n = $t(e.body), r = Z(i), o = L(c());
1376
1388
  r.set(o, {
1377
1389
  postId: u.value,
1378
- boardId: c.value,
1390
+ boardId: l.value,
1379
1391
  listed: d(),
1380
1392
  body: n,
1381
1393
  authorUid: e.authorUid,
@@ -1390,19 +1402,19 @@ function qt(e, n, r = {}) {
1390
1402
  replyCount: 0,
1391
1403
  likeCount: 0,
1392
1404
  isReply: !1,
1393
- createdAt: G(),
1394
- updatedAt: G()
1395
- }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1405
+ createdAt: K(),
1406
+ updatedAt: K()
1407
+ }), r.update(t(i, a(), u.value), { commentCount: V(1) }), await r.commit();
1396
1408
  }
1397
1409
  async function k(e) {
1398
- let n = Kt(e.body);
1410
+ let n = $t(e.body);
1399
1411
  if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
1400
- let r = X(i), o = re(s()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1412
+ let r = Z(i), o = L(c()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1401
1413
  if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
1402
1414
  let p = Math.min((e.parent.depth ?? 0) + 1, 2);
1403
1415
  r.set(o, {
1404
1416
  postId: u.value,
1405
- boardId: c.value,
1417
+ boardId: l.value,
1406
1418
  listed: d(),
1407
1419
  body: n,
1408
1420
  authorUid: e.authorUid,
@@ -1417,51 +1429,51 @@ function qt(e, n, r = {}) {
1417
1429
  replyCount: 0,
1418
1430
  likeCount: 0,
1419
1431
  isReply: !0,
1420
- createdAt: G(),
1421
- updatedAt: G()
1422
- }), r.update(l(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1432
+ createdAt: K(),
1433
+ updatedAt: K()
1434
+ }), r.update(s(i, a(), f), { replyCount: V(1) }), r.update(t(i, a(), u.value), { commentCount: V(1) }), await r.commit();
1423
1435
  }
1424
- async function A(e) {
1436
+ async function ee(e) {
1425
1437
  if (!e.id) return;
1426
1438
  if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
1427
- await ft(i, a(), e.id);
1428
- let n = X(i);
1429
- n.delete(l(i, a(), e.id)), e.parentId && e.rootId && n.update(l(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(), u.value), { commentCount: B(-1) }), await n.commit(), h.value = h.value.filter((t) => t.id !== e.id), p.value = p.value.filter((t) => t.id !== e.id), f.value = f.value.filter((t) => t.id !== e.id);
1439
+ await yt(i, a(), e.id);
1440
+ let n = Z(i);
1441
+ n.delete(s(i, a(), e.id)), e.parentId && e.rootId && n.update(s(i, a(), e.rootId), { replyCount: V(-1) }), n.update(t(i, a(), u.value), { commentCount: V(-1) }), await n.commit(), g.value = g.value.filter((t) => t.id !== e.id), p.value = p.value.filter((t) => t.id !== e.id), f.value = f.value.filter((t) => t.id !== e.id);
1430
1442
  }
1431
- async function j(e, t) {
1432
- let n = Kt(t);
1433
- await J(l(i, a(), e), {
1443
+ async function F(e, t) {
1444
+ let n = $t(t);
1445
+ await Y(s(i, a(), e), {
1434
1446
  body: n,
1435
- updatedAt: G()
1447
+ updatedAt: K()
1436
1448
  });
1437
1449
  }
1438
- async function ee(e, t, n) {
1439
- await J(l(i, a(), e), {
1450
+ async function I(e, t, n) {
1451
+ await Y(s(i, a(), e), {
1440
1452
  isBlinded: t,
1441
- moderatedAt: G(),
1453
+ moderatedAt: K(),
1442
1454
  moderatedBy: n
1443
1455
  });
1444
1456
  }
1445
1457
  return {
1446
1458
  comments: w,
1447
- commentsPending: M(() => y.value || m.value),
1459
+ commentsPending: A(() => y.value || m.value),
1448
1460
  hasMore: v,
1449
- loadingMore: g,
1450
- loadMore: E,
1451
- addComment: D,
1461
+ loadingMore: _,
1462
+ loadMore: D,
1463
+ addComment: O,
1452
1464
  addReply: k,
1453
- updateComment: j,
1454
- setCommentBlinded: ee,
1455
- deleteComment: A
1465
+ updateComment: F,
1466
+ setCommentBlinded: I,
1467
+ deleteComment: ee
1456
1468
  };
1457
1469
  }
1458
- function Jt(e, t, n) {
1459
- let r = Q(), i = () => _(), a = () => O(r, i()), o = t.trim(), s = P([]), c = P(!1), l = P(!1), u = P(!0), d = null, f;
1470
+ function tn(e, t, n) {
1471
+ let r = Q(), i = () => h(), a = () => E(r, i()), o = t.trim(), s = M([]), c = M(!1), l = M(!1), u = M(!0), d = null, f;
1460
1472
  function p(e) {
1461
1473
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
1462
1474
  }
1463
1475
  function m() {
1464
- f ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1476
+ f ||= U(G(a(), X("postId", "==", o), X("rootId", "==", n), X("isReply", "==", !0), W("createdAt", "desc"), W(R(), "desc"), H(1)), (e) => {
1465
1477
  let t = new Map(s.value.map((e) => [e.id, e]));
1466
1478
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1467
1479
  id: n.doc.id,
@@ -1472,61 +1484,61 @@ function Jt(e, t, n) {
1472
1484
  console.error("[memi-board:replies] onSnapshot failed", e);
1473
1485
  });
1474
1486
  }
1475
- async function h() {
1476
- if (!(c.value || !u.value)) {
1487
+ async function g() {
1488
+ if (!c.value && u.value) {
1477
1489
  c.value = !0;
1478
1490
  try {
1479
1491
  let e = [
1480
- Y("postId", "==", o),
1481
- Y("rootId", "==", n),
1482
- Y("isReply", "==", !0),
1483
- U("createdAt", "asc"),
1484
- U(L(), "asc"),
1485
- ...d ? [q(d)] : [],
1486
- V(6)
1487
- ], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Wt), c = i.map((e) => ({
1492
+ X("postId", "==", o),
1493
+ X("rootId", "==", n),
1494
+ X("isReply", "==", !0),
1495
+ W("createdAt", "asc"),
1496
+ W(R(), "asc"),
1497
+ ...d ? [J(d)] : [],
1498
+ H(6)
1499
+ ], t = await B(G(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Zt), c = i.map((e) => ({
1488
1500
  id: e.id,
1489
1501
  ...e.data()
1490
1502
  })).filter((e) => !r.has(e.id));
1491
- s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Wt && (u.value = !1, m());
1503
+ s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Zt && (u.value = !1, m());
1492
1504
  } finally {
1493
1505
  c.value = !1;
1494
1506
  }
1495
1507
  }
1496
1508
  }
1497
- function g() {
1509
+ function _() {
1498
1510
  f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
1499
1511
  }
1500
1512
  async function v() {
1501
- g(), await h();
1513
+ _(), await g();
1502
1514
  }
1503
- return N(() => f?.()), {
1515
+ return j(() => f?.()), {
1504
1516
  replies: s,
1505
1517
  loading: c,
1506
1518
  loaded: l,
1507
1519
  hasMore: u,
1508
- loadMore: h,
1520
+ loadMore: g,
1509
1521
  refresh: v
1510
1522
  };
1511
1523
  }
1512
1524
  //#endregion
1513
1525
  //#region src/composables/useMemiBoardLikes.ts
1514
- function Yt(e, n) {
1515
- let r = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1526
+ function nn(e, n) {
1527
+ let r = () => h(), i = Q(), a = ae(), o = M(!1), s = M(!1), c;
1516
1528
  function l(e) {
1517
- return j(i, r(), n, e);
1529
+ return k(i, r(), n, e);
1518
1530
  }
1519
- I(a, (e) => {
1531
+ P(a, (e) => {
1520
1532
  if (c?.(), c = void 0, !e) {
1521
1533
  o.value = !1;
1522
1534
  return;
1523
1535
  }
1524
- c = H(l(e.uid), (e) => {
1536
+ c = U(l(e.uid), (e) => {
1525
1537
  o.value = e.exists();
1526
1538
  }, (e) => {
1527
1539
  console.error("[memi-board:likes] onSnapshot failed", e);
1528
1540
  });
1529
- }, { immediate: !0 }), N(() => c?.());
1541
+ }, { immediate: !0 }), j(() => c?.());
1530
1542
  async function u() {
1531
1543
  let c = a.value?.uid;
1532
1544
  if (!c) throw Error("로그인이 필요합니다.");
@@ -1534,12 +1546,12 @@ function Yt(e, n) {
1534
1546
  s.value = !0;
1535
1547
  try {
1536
1548
  let a = l(c), o = t(i, r(), n);
1537
- return await oe(i, async (t) => (await t.get(a)).exists() ? (t.delete(a), t.update(o, { likeCount: B(-1) }), !1) : (t.set(a, {
1549
+ return await re(i, async (t) => (await t.get(a)).exists() ? (t.delete(a), t.update(o, { likeCount: V(-1) }), !1) : (t.set(a, {
1538
1550
  uid: c,
1539
1551
  postId: n,
1540
1552
  boardId: e,
1541
- createdAt: G()
1542
- }), t.update(o, { likeCount: B(1) }), !0));
1553
+ createdAt: K()
1554
+ }), t.update(o, { likeCount: V(1) }), !0));
1543
1555
  } finally {
1544
1556
  s.value = !1;
1545
1557
  }
@@ -1550,50 +1562,50 @@ function Yt(e, n) {
1550
1562
  toggleLike: u
1551
1563
  };
1552
1564
  }
1553
- var Xt = Symbol.for("memi-board:comment-likes");
1554
- function Zt(e, t) {
1555
- let n = () => _(), r = Q(), i = Z(), a = M(() => F(e)), o = M(() => F(t)), s = P(/* @__PURE__ */ new Set()), c = P(null), u;
1556
- I([i, o], ([e, t]) => {
1557
- u?.(), u = void 0, s.value = /* @__PURE__ */ new Set(), !(!e || !t) && (u = H(W(A(r, n()), Y("uid", "==", e.uid), Y("postId", "==", t), Y("target", "==", "comment")), (e) => {
1565
+ var rn = Symbol.for("memi-board:comment-likes");
1566
+ function an(e, t) {
1567
+ let n = () => h(), r = Q(), i = ae(), a = A(() => N(e)), o = A(() => N(t)), c = M(/* @__PURE__ */ new Set()), l = M(null), u;
1568
+ P([i, o], ([e, t]) => {
1569
+ u?.(), u = void 0, c.value = /* @__PURE__ */ new Set(), e && t && (u = U(G(O(r, n()), X("uid", "==", e.uid), X("postId", "==", t), X("target", "==", "comment")), (e) => {
1558
1570
  let t = /* @__PURE__ */ new Set();
1559
1571
  for (let n of e.docs) {
1560
1572
  let e = n.data().commentId;
1561
1573
  typeof e == "string" && e && t.add(e);
1562
1574
  }
1563
- s.value = t;
1575
+ c.value = t;
1564
1576
  }, (e) => {
1565
1577
  console.error("[memi-board:comment-likes] onSnapshot failed", e);
1566
1578
  }));
1567
- }, { immediate: !0 }), N(() => u?.());
1579
+ }, { immediate: !0 }), j(() => u?.());
1568
1580
  function d(e) {
1569
- return !!e && s.value.has(e);
1581
+ return !!e && c.value.has(e);
1570
1582
  }
1571
1583
  function f(e) {
1572
- return !!e && c.value === e;
1584
+ return !!e && l.value === e;
1573
1585
  }
1574
1586
  async function p(e) {
1575
1587
  let t = i.value?.uid;
1576
1588
  if (!t) throw Error("로그인이 필요합니다.");
1577
1589
  if (!e) return !1;
1578
- if (c.value) return d(e);
1579
- c.value = e;
1590
+ if (l.value) return d(e);
1591
+ l.value = e;
1580
1592
  try {
1581
- let i = j(r, n(), e, t), c = l(r, n(), e), u = await oe(r, async (n) => {
1582
- let r = await n.get(i), s = await n.get(c);
1593
+ let i = k(r, n(), e, t), l = s(r, n(), e), u = await re(r, async (n) => {
1594
+ let r = await n.get(i), s = await n.get(l);
1583
1595
  if (!s.exists()) throw Error("댓글을 찾을 수 없습니다.");
1584
- let l = Number(s.data()?.likeCount ?? 0);
1585
- return r.exists() ? (n.delete(i), n.update(c, { likeCount: Math.max(0, l - 1) }), !1) : (n.set(i, {
1596
+ let c = Number(s.data()?.likeCount ?? 0);
1597
+ return r.exists() ? (n.delete(i), n.update(l, { likeCount: Math.max(0, c - 1) }), !1) : (n.set(i, {
1586
1598
  uid: t,
1587
1599
  postId: o.value,
1588
1600
  boardId: a.value,
1589
1601
  commentId: e,
1590
1602
  target: "comment",
1591
- createdAt: G()
1592
- }), n.update(c, { likeCount: l + 1 }), !0);
1593
- }), d = new Set(s.value);
1594
- return u ? d.add(e) : d.delete(e), s.value = d, u;
1603
+ createdAt: K()
1604
+ }), n.update(l, { likeCount: c + 1 }), !0);
1605
+ }), d = new Set(c.value);
1606
+ return u ? d.add(e) : d.delete(e), c.value = d, u;
1595
1607
  } finally {
1596
- c.value = null;
1608
+ l.value = null;
1597
1609
  }
1598
1610
  }
1599
1611
  return {
@@ -1604,7 +1616,7 @@ function Zt(e, t) {
1604
1616
  }
1605
1617
  //#endregion
1606
1618
  //#region src/composables/useMemiBoardReports.ts
1607
- var Qt = 200, $t = [
1619
+ var on = 200, sn = [
1608
1620
  {
1609
1621
  value: "spam",
1610
1622
  label: "스팸·광고"
@@ -1626,34 +1638,34 @@ var Qt = 200, $t = [
1626
1638
  label: "기타"
1627
1639
  }
1628
1640
  ];
1629
- function en(e) {
1630
- return $t.find((t) => t.value === e)?.label || e || "신고";
1641
+ function cn(e) {
1642
+ return sn.find((t) => t.value === e)?.label || e || "신고";
1631
1643
  }
1632
- function tn(e, t) {
1633
- let n = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1634
- I(a, (e) => {
1644
+ function ln(e, t) {
1645
+ let n = () => h(), i = Q(), a = ae(), o = M(!1), s = M(!1), c;
1646
+ P(a, (e) => {
1635
1647
  if (c?.(), c = void 0, !e) {
1636
1648
  o.value = !1;
1637
1649
  return;
1638
1650
  }
1639
- c = H(r(i, n(), t, e.uid), (e) => {
1651
+ c = U(r(i, n(), t, e.uid), (e) => {
1640
1652
  o.value = e.exists();
1641
1653
  }, (e) => {
1642
1654
  console.error("[memi-board:reports] onSnapshot failed", e);
1643
1655
  });
1644
- }, { immediate: !0 }), N(() => c?.());
1656
+ }, { immediate: !0 }), j(() => c?.());
1645
1657
  async function l(c) {
1646
1658
  let l = a.value?.uid;
1647
1659
  if (!l) throw Error("로그인이 필요합니다.");
1648
1660
  if (s.value) return;
1649
1661
  if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
1650
- let u = $t.some((e) => e.value === c.reason) ? c.reason : null;
1662
+ let u = sn.some((e) => e.value === c.reason) ? c.reason : null;
1651
1663
  if (!u) throw Error("신고 사유를 선택해 주세요.");
1652
1664
  let d = (c.detail || "").trim();
1653
1665
  if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
1654
1666
  s.value = !0;
1655
1667
  try {
1656
- await K(r(i, n(), t, l), {
1668
+ await q(r(i, n(), t, l), {
1657
1669
  uid: l,
1658
1670
  target: "post",
1659
1671
  postId: t,
@@ -1664,7 +1676,7 @@ function tn(e, t) {
1664
1676
  postTitle: (c.postTitle || "").trim().slice(0, 200),
1665
1677
  authorUid: c.authorUid || "",
1666
1678
  authorName: c.authorName ?? null,
1667
- createdAt: G()
1679
+ createdAt: K()
1668
1680
  }), o.value = !0;
1669
1681
  } finally {
1670
1682
  s.value = !1;
@@ -1676,35 +1688,35 @@ function tn(e, t) {
1676
1688
  submitReport: l
1677
1689
  };
1678
1690
  }
1679
- var nn = 20;
1680
- function rn(e = {}) {
1681
- let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } = tt(), s = e.pageSize ?? nn, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
1682
- function g(e) {
1691
+ var un = 20;
1692
+ function dn(e = {}) {
1693
+ let t = () => h(), n = Q(), { canManageContent: a, canManageBoard: o } = ct(), s = e.pageSize ?? un, c = A(() => N(e.boardId)?.trim() || ""), l = M([]), u = M(!0), d = M(!1), f = M(!1), p = M(""), m = null, g;
1694
+ function _(e) {
1683
1695
  return {
1684
1696
  id: e.id,
1685
1697
  ...e.data()
1686
1698
  };
1687
1699
  }
1688
1700
  function v() {
1689
- if (h?.(), !i.value) {
1701
+ if (g?.(), !a.value) {
1690
1702
  l.value = [], u.value = !1;
1691
1703
  return;
1692
1704
  }
1693
1705
  u.value = !0;
1694
- let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")];
1695
- h = H(W(o(n, t()), ...e, U("createdAt", "desc"), U(L(), "desc"), V(s)), (e) => {
1696
- l.value = e.docs.map(g).filter((e) => a(e.boardId)), m = e.docs.at(-1) ?? null, f.value = e.docs.length >= s, u.value = !1;
1706
+ let e = c.value ? [X("status", "==", "open"), X("boardId", "==", c.value)] : [X("status", "==", "open"), X("target", "==", "post")];
1707
+ g = U(G(i(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), H(s)), (e) => {
1708
+ l.value = e.docs.map(_).filter((e) => o(e.boardId)), m = e.docs.at(-1) ?? null, f.value = e.docs.length >= s, u.value = !1;
1697
1709
  }, (e) => {
1698
1710
  console.error("[memi-board:report-queue] onSnapshot failed", e), p.value = e.message || String(e), u.value = !1;
1699
1711
  });
1700
1712
  }
1701
- I([i, c], v, { immediate: !0 }), N(() => h?.());
1713
+ P([a, c], v, { immediate: !0 }), j(() => g?.());
1702
1714
  async function y() {
1703
- if (!(d.value || !f.value || !m || !i.value)) {
1715
+ if (!d.value && f.value && m && a.value) {
1704
1716
  d.value = !0, p.value = "";
1705
1717
  try {
1706
- let e = c.value ? [Y("status", "==", "open"), Y("boardId", "==", c.value)] : [Y("status", "==", "open"), Y("target", "==", "post")], r = await z(W(o(n, t()), ...e, U("createdAt", "desc"), U(L(), "desc"), q(m), V(s + 1))), i = r.docs.slice(0, s), u = new Set(l.value.map((e) => e.id));
1707
- l.value.push(...i.map(g).filter((e) => !u.has(e.id) && a(e.boardId))), m = i.at(-1) ?? m, f.value = r.docs.length > s;
1718
+ let e = c.value ? [X("status", "==", "open"), X("boardId", "==", c.value)] : [X("status", "==", "open"), X("target", "==", "post")], r = await B(G(i(n, t()), ...e, W("createdAt", "desc"), W(R(), "desc"), J(m), H(s + 1))), a = r.docs.slice(0, s), u = new Set(l.value.map((e) => e.id));
1719
+ l.value.push(...a.map(_).filter((e) => !u.has(e.id) && o(e.boardId))), m = a.at(-1) ?? m, f.value = r.docs.length > s;
1708
1720
  } catch (e) {
1709
1721
  p.value = e.message || String(e);
1710
1722
  } finally {
@@ -1713,10 +1725,10 @@ function rn(e = {}) {
1713
1725
  }
1714
1726
  }
1715
1727
  async function b(e, i) {
1716
- !e.id || !a(e.boardId) || await J(r(n, t(), e.postId, e.uid), {
1728
+ e.id && o(e.boardId) && await Y(r(n, t(), e.postId, e.uid), {
1717
1729
  status: i,
1718
- reviewedAt: G(),
1719
- reviewedBy: Z().value?.uid || ""
1730
+ reviewedAt: K(),
1731
+ reviewedBy: ae().value?.uid || ""
1720
1732
  });
1721
1733
  }
1722
1734
  return {
@@ -1732,8 +1744,8 @@ function rn(e = {}) {
1732
1744
  }
1733
1745
  //#endregion
1734
1746
  //#region src/composables/useMemiBoardViews.ts
1735
- function an() {
1736
- let e = () => _(), n = Q();
1747
+ function fn() {
1748
+ let e = () => h(), n = Q();
1737
1749
  function r(t, n) {
1738
1750
  let { postsCollection: r } = e();
1739
1751
  return `memi-board:viewed:${r}:${t}:${n}`;
@@ -1747,16 +1759,16 @@ function an() {
1747
1759
  }
1748
1760
  }
1749
1761
  function a(e, t) {
1750
- if (!(typeof sessionStorage > "u")) try {
1762
+ if (typeof sessionStorage < "u") try {
1751
1763
  sessionStorage.setItem(r(e, t), "1");
1752
1764
  } catch {}
1753
1765
  }
1754
1766
  async function o(o, s) {
1755
1767
  let c = o?.trim(), l = s?.trim();
1756
- if (!(!c || !l) && !i(c, l)) {
1768
+ if (c && l && !i(c, l)) {
1757
1769
  a(c, l);
1758
1770
  try {
1759
- await J(t(n, e(), l), { viewCount: B(1) });
1771
+ await Y(t(n, e(), l), { viewCount: V(1) });
1760
1772
  } catch (e) {
1761
1773
  try {
1762
1774
  sessionStorage.removeItem(r(c, l));
@@ -1772,7 +1784,7 @@ function an() {
1772
1784
  }
1773
1785
  //#endregion
1774
1786
  //#region src/utils/moderation-prompt.ts
1775
- var on = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", sn = {
1787
+ var pn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", mn = {
1776
1788
  abuse: "욕설·비속어·모욕",
1777
1789
  spam: "스팸·광고",
1778
1790
  adult: "선정적·성인 내용",
@@ -1780,17 +1792,17 @@ var on = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
1780
1792
  other: "부적절한 내용",
1781
1793
  none: ""
1782
1794
  };
1783
- function cn(e) {
1795
+ function hn(e) {
1784
1796
  if (e.via === "local" && e.localHit) {
1785
1797
  let t = e.localHit.trim();
1786
1798
  return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
1787
1799
  }
1788
1800
  let t = (e.reason || "").trim();
1789
1801
  if (t) return t;
1790
- let n = e.category && e.category !== "none" ? sn[e.category] || e.category : "";
1802
+ let n = e.category && e.category !== "none" ? mn[e.category] || e.category : "";
1791
1803
  return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
1792
1804
  }
1793
- var ln = [
1805
+ var gn = [
1794
1806
  "씨발",
1795
1807
  "시발",
1796
1808
  "시이발",
@@ -1816,10 +1828,10 @@ var ln = [
1816
1828
  "bitch",
1817
1829
  "asshole"
1818
1830
  ];
1819
- function un(e) {
1831
+ function _n(e) {
1820
1832
  return new RegExp(e.join("|"), "i");
1821
1833
  }
1822
- var dn = /* @__PURE__ */ new Set([
1834
+ var vn = /* @__PURE__ */ new Set([
1823
1835
  "none",
1824
1836
  "abuse",
1825
1837
  "spam",
@@ -1827,7 +1839,7 @@ var dn = /* @__PURE__ */ new Set([
1827
1839
  "violence",
1828
1840
  "other"
1829
1841
  ]);
1830
- function fn(e) {
1842
+ function yn(e) {
1831
1843
  let t = e.trim();
1832
1844
  if (!t) return null;
1833
1845
  let n;
@@ -1845,7 +1857,7 @@ function fn(e) {
1845
1857
  if (typeof n != "object" || !n) return null;
1846
1858
  let r = n;
1847
1859
  if (typeof r.flagged != "boolean") return null;
1848
- let i = typeof r.category == "string" && dn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1860
+ let i = typeof r.category == "string" && vn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1849
1861
  return {
1850
1862
  flagged: r.flagged,
1851
1863
  category: i,
@@ -1854,20 +1866,20 @@ function fn(e) {
1854
1866
  }
1855
1867
  //#endregion
1856
1868
  //#region src/composables/useMemiBoardModeration.ts
1857
- var pn = {
1869
+ var bn = {
1858
1870
  flagged: !1,
1859
1871
  category: "none",
1860
1872
  reason: "",
1861
1873
  via: "empty"
1862
1874
  };
1863
- function mn() {
1864
- let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = tt(), a = un([...ln, ...e.localBlocklist ?? []]);
1875
+ function xn() {
1876
+ let e = c().moderation ?? {}, t = se(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = ct(), a = _n([...gn, ...e.localBlocklist ?? []]);
1865
1877
  function o(e) {
1866
1878
  let t = e.match(a);
1867
1879
  return t ? {
1868
1880
  flagged: !0,
1869
1881
  category: "abuse",
1870
- reason: cn({
1882
+ reason: hn({
1871
1883
  via: "local",
1872
1884
  localHit: t[0] || "",
1873
1885
  category: "abuse"
@@ -1876,26 +1888,26 @@ function mn() {
1876
1888
  } : null;
1877
1889
  }
1878
1890
  function s() {
1879
- let n = e.model ?? "gemini-3.5-flash-lite", r = e.useLimitedUseAppCheckTokens !== !1, i = we(t, {
1880
- backend: new Se(),
1891
+ let n = e.model ?? "gemini-3.5-flash-lite", r = e.useLimitedUseAppCheckTokens !== !1, i = Se(t, {
1892
+ backend: new be(),
1881
1893
  useLimitedUseAppCheckTokens: r
1882
1894
  });
1883
- return Te(i, {
1895
+ return Ce(i, {
1884
1896
  model: n,
1885
- systemInstruction: on,
1897
+ systemInstruction: pn,
1886
1898
  generationConfig: {
1887
1899
  temperature: 0,
1888
1900
  maxOutputTokens: 256,
1889
1901
  responseMimeType: "application/json",
1890
- responseSchema: Ce.object({ properties: {
1891
- flagged: Ce.boolean(),
1892
- category: Ce.string(),
1893
- reason: Ce.string()
1902
+ responseSchema: xe.object({ properties: {
1903
+ flagged: xe.boolean(),
1904
+ category: xe.string(),
1905
+ reason: xe.string()
1894
1906
  } })
1895
1907
  }
1896
1908
  });
1897
1909
  }
1898
- function c() {
1910
+ function l() {
1899
1911
  return e.onError === "block" ? {
1900
1912
  flagged: !0,
1901
1913
  category: "other",
@@ -1910,8 +1922,8 @@ function mn() {
1910
1922
  via: "ai-error-allow"
1911
1923
  };
1912
1924
  }
1913
- function l(e, t = "") {
1914
- let n = cn({
1925
+ function u(e, t = "") {
1926
+ let n = hn({
1915
1927
  reason: e.reason,
1916
1928
  category: e.category,
1917
1929
  via: e.via
@@ -1925,9 +1937,9 @@ function mn() {
1925
1937
  if (!e.flagged || e.error || e.via !== "local" && e.via !== "ai") return e;
1926
1938
  try {
1927
1939
  let { messageSuffix: t } = await i();
1928
- return l(e, t);
1940
+ return u(e, t);
1929
1941
  } catch (t) {
1930
- return console.warn("[memi-board] record moderation strike failed", t instanceof Error ? t.message : t), l(e);
1942
+ return console.warn("[memi-board] record moderation strike failed", t instanceof Error ? t.message : t), u(e);
1931
1943
  }
1932
1944
  }
1933
1945
  async function f(t, i) {
@@ -1939,26 +1951,26 @@ function mn() {
1939
1951
  };
1940
1952
  let a = t.trim();
1941
1953
  if (!a) return {
1942
- ...pn,
1954
+ ...bn,
1943
1955
  via: "empty"
1944
1956
  };
1945
1957
  if (i?.skipFilter) return {
1946
- ...pn,
1958
+ ...bn,
1947
1959
  via: "admin-override"
1948
1960
  };
1949
- let l = o(a);
1950
- if (l) return d(l);
1961
+ let c = o(a);
1962
+ if (c) return d(c);
1951
1963
  if (e.enabled === !1) return {
1952
- ...pn,
1964
+ ...bn,
1953
1965
  via: "disabled"
1954
1966
  };
1955
1967
  try {
1956
- let e = fn((await s().generateContent(`심사할 텍스트:\n"""${a.slice(0, 4e3)}"""`)).response.text());
1957
- if (!e) return c();
1968
+ let e = yn((await s().generateContent(`심사할 텍스트:\n"""${a.slice(0, 4e3)}"""`)).response.text());
1969
+ if (!e) return l();
1958
1970
  let t = {
1959
1971
  flagged: e.flagged,
1960
1972
  category: e.category,
1961
- reason: cn({
1973
+ reason: hn({
1962
1974
  reason: e.reason,
1963
1975
  category: e.category,
1964
1976
  via: "ai"
@@ -1967,7 +1979,7 @@ function mn() {
1967
1979
  };
1968
1980
  return t.flagged ? d(t) : t;
1969
1981
  } catch (e) {
1970
- return console.warn("[memi-board] moderation AI failed", e instanceof Error ? e.message : e), c();
1982
+ return console.warn("[memi-board] moderation AI failed", e instanceof Error ? e.message : e), l();
1971
1983
  }
1972
1984
  }
1973
1985
  async function p(t) {
@@ -1978,22 +1990,22 @@ function mn() {
1978
1990
  via: "restricted"
1979
1991
  };
1980
1992
  if (e.enabled === !1 || !e.moderateImages) return {
1981
- ...pn,
1993
+ ...bn,
1982
1994
  via: "disabled"
1983
1995
  };
1984
1996
  try {
1985
1997
  let e = await new Promise((e, n) => {
1986
1998
  let r = new FileReader();
1987
1999
  r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
1988
- }), n = fn((await s().generateContent([{ inlineData: {
2000
+ }), n = yn((await s().generateContent([{ inlineData: {
1989
2001
  mimeType: t.type || "image/jpeg",
1990
2002
  data: e
1991
2003
  } }, "이 이미지를 심사해 주세요."])).response.text());
1992
- if (!n) return c();
2004
+ if (!n) return l();
1993
2005
  let r = {
1994
2006
  flagged: n.flagged,
1995
2007
  category: n.category,
1996
- reason: cn({
2008
+ reason: hn({
1997
2009
  reason: n.reason,
1998
2010
  category: n.category,
1999
2011
  via: "ai"
@@ -2002,7 +2014,7 @@ function mn() {
2002
2014
  };
2003
2015
  return r.flagged ? d(r) : r;
2004
2016
  } catch (e) {
2005
- return console.warn("[memi-board] moderation AI image failed", e instanceof Error ? e.message : e), c();
2017
+ return console.warn("[memi-board] moderation AI image failed", e instanceof Error ? e.message : e), l();
2006
2018
  }
2007
2019
  }
2008
2020
  return {
@@ -2012,7 +2024,7 @@ function mn() {
2012
2024
  }
2013
2025
  //#endregion
2014
2026
  //#region src/composables/useMemiBoardUsers.ts
2015
- var hn = [
2027
+ var Sn = [
2016
2028
  {
2017
2029
  value: "admin",
2018
2030
  label: "관리자",
@@ -2029,13 +2041,13 @@ var hn = [
2029
2041
  description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
2030
2042
  }
2031
2043
  ];
2032
- function gn(e = {}) {
2033
- let t = () => _(), n = Q(), r = M(() => F(e.enabled) ?? !0 ? h(n, t()) : null), i = se(r, { ssrKey: w(t(), "users-management") });
2044
+ function Cn(e = {}) {
2045
+ let t = () => h(), n = Q(), r = A(() => N(e.enabled) ?? !0 ? p(n, t()) : null), i = ie(r, { ssrKey: S(t(), "users-management") });
2034
2046
  async function a(e, r) {
2035
- if (!hn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2036
- await J(x(n, t(), e), {
2047
+ if (!Sn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2048
+ await Y(y(n, t(), e), {
2037
2049
  role: r,
2038
- updatedAt: G()
2050
+ updatedAt: K()
2039
2051
  });
2040
2052
  }
2041
2053
  return {
@@ -2046,11 +2058,11 @@ function gn(e = {}) {
2046
2058
  }
2047
2059
  //#endregion
2048
2060
  //#region src/composables/useMemiBoardUserProfile.ts
2049
- function _n(e) {
2050
- let t = Q(), n = () => _(), r = M(() => F(e)), i = M(() => {
2061
+ function wn(e) {
2062
+ let t = Q(), n = () => h(), r = A(() => N(e)), i = A(() => {
2051
2063
  let e = r.value;
2052
- return e ? x(t, n(), e) : null;
2053
- }), { data: a, pending: o } = ce(i);
2064
+ return e ? y(t, n(), e) : null;
2065
+ }), { data: a, pending: o } = oe(i);
2054
2066
  return {
2055
2067
  profile: a,
2056
2068
  profilePending: o
@@ -2058,7 +2070,7 @@ function _n(e) {
2058
2070
  }
2059
2071
  //#endregion
2060
2072
  //#region src/utils/section.ts
2061
- var vn = [
2073
+ var Tn = [
2062
2074
  3,
2063
2075
  4,
2064
2076
  5,
@@ -2067,7 +2079,7 @@ var vn = [
2067
2079
  8,
2068
2080
  9,
2069
2081
  12
2070
- ], yn = [
2082
+ ], En = [
2071
2083
  {
2072
2084
  value: "list",
2073
2085
  label: "게시목록"
@@ -2080,24 +2092,24 @@ var vn = [
2080
2092
  value: "comments",
2081
2093
  label: "댓글모음"
2082
2094
  }
2083
- ], bn = [{
2095
+ ], Dn = [{
2084
2096
  value: "latest",
2085
2097
  label: "최신순"
2086
2098
  }, {
2087
2099
  value: "likes",
2088
2100
  label: "좋아요순"
2089
2101
  }];
2090
- function xn(e) {
2102
+ function On(e) {
2091
2103
  return e === "list" ? 5 : 4;
2092
2104
  }
2093
- function Sn(e, t) {
2105
+ function kn(e, t) {
2094
2106
  let n = Math.floor(Number(t));
2095
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : xn(e);
2107
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : On(e);
2096
2108
  }
2097
- function Cn(e) {
2109
+ function An(e) {
2098
2110
  return e === 12 ? null : 12 - e;
2099
2111
  }
2100
- function wn(e) {
2112
+ function jn(e) {
2101
2113
  return {
2102
2114
  3: "col-span-12 md:col-span-3",
2103
2115
  4: "col-span-12 md:col-span-4",
@@ -2109,36 +2121,36 @@ function wn(e) {
2109
2121
  12: "col-span-12"
2110
2122
  }[e];
2111
2123
  }
2112
- function Tn(e) {
2124
+ function Mn(e) {
2113
2125
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2114
2126
  }
2115
- function En(e) {
2127
+ function Nn(e) {
2116
2128
  let t = e.boardId || e.category || "free";
2117
2129
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2118
2130
  }
2119
- function Dn(e) {
2131
+ function Pn(e) {
2120
2132
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2121
2133
  }
2122
- function On(e) {
2123
- if (!(!e.boardId || !e.postId)) return `/board/${e.boardId}/post/${e.postId}`;
2134
+ function Fn(e) {
2135
+ if (e.boardId && e.postId) return `/board/${e.boardId}/post/${e.postId}`;
2124
2136
  }
2125
- function kn(e) {
2137
+ function In(e) {
2126
2138
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2127
2139
  }
2128
- function An(e) {
2140
+ function Ln(e) {
2129
2141
  return e.title?.trim() || e.summary || "게시물";
2130
2142
  }
2131
- function jn(e) {
2143
+ function Rn(e) {
2132
2144
  return e === "image" || e === "video" ? "media" : "list";
2133
2145
  }
2134
- function Mn(e) {
2135
- return jn(e) === "media" ? "미디어" : "목록";
2146
+ function zn(e) {
2147
+ return Rn(e) === "media" ? "미디어" : "목록";
2136
2148
  }
2137
- function Nn(e, t) {
2149
+ function Bn(e, t) {
2138
2150
  return e ? t(e)?.listView ?? "default" : "default";
2139
2151
  }
2140
- function Pn(e, t) {
2141
- let n = e.map((e) => Nn(e.boardId, t));
2152
+ function Vn(e, t) {
2153
+ let n = e.map((e) => Bn(e.boardId, t));
2142
2154
  if (!n.length) return "default";
2143
2155
  if (n.every((e) => e === n[0])) return n[0];
2144
2156
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2146,72 +2158,72 @@ function Pn(e, t) {
2146
2158
  }
2147
2159
  //#endregion
2148
2160
  //#region src/composables/useMemiBoardSections.ts
2149
- var Fn = [];
2150
- function In(e) {
2161
+ var Hn = [];
2162
+ function Un(e) {
2151
2163
  let t = Number(e.cols);
2152
- if (vn.includes(t)) return t;
2164
+ if (Tn.includes(t)) return t;
2153
2165
  let n = e.width;
2154
2166
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2155
2167
  }
2156
- function Ln(e) {
2168
+ function Wn(e) {
2157
2169
  let t = e.height ?? e.size;
2158
2170
  return t === "sm" || t === "lg" ? t : "md";
2159
2171
  }
2160
- function Rn(e) {
2172
+ function Gn(e) {
2161
2173
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2162
2174
  }
2163
- function zn(e) {
2175
+ function Kn(e) {
2164
2176
  return e.sort === "likes" ? "likes" : "latest";
2165
2177
  }
2166
- function Bn(e) {
2178
+ function qn(e) {
2167
2179
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2168
2180
  }
2169
- function Vn(e, t) {
2181
+ function Jn(e, t) {
2170
2182
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2171
2183
  for (let e of n) {
2172
2184
  let n = String(e || "").trim();
2173
- if (!(!n || r.includes(n)) && (r.push(n), r.length >= t)) break;
2185
+ if (n && !r.includes(n) && (r.push(n), r.length >= t)) break;
2174
2186
  }
2175
2187
  return r;
2176
2188
  }
2177
- function Hn(e, t) {
2178
- let n = Rn(e), r = Sn(n, e.count);
2189
+ function Yn(e, t) {
2190
+ let n = Gn(e), r = kn(n, e.count);
2179
2191
  return {
2180
2192
  id: String(e.id || `box-${t + 1}`),
2181
2193
  title: String(e.title || `섹션 ${t + 1}`),
2182
2194
  showTitle: e.showTitle !== !1,
2183
2195
  kind: n,
2184
- boardId: Bn(e),
2196
+ boardId: qn(e),
2185
2197
  count: r,
2186
- postIds: Vn(e, r),
2187
- sort: zn(e),
2188
- cols: In(e),
2189
- height: Ln(e),
2198
+ postIds: Jn(e, r),
2199
+ sort: Kn(e),
2200
+ cols: Un(e),
2201
+ height: Wn(e),
2190
2202
  order: t
2191
2203
  };
2192
2204
  }
2193
- function Un() {
2194
- let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Fn).map((e, t) => Hn(e, t)));
2205
+ function Xn() {
2206
+ let e = Q(), t = L(e, "settings", "homeLayout"), { data: n, pending: r } = oe(t), i = A(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Hn).map((e, t) => Yn(e, t)));
2195
2207
  async function a(e) {
2196
- await K(t, {
2208
+ await q(t, {
2197
2209
  columns: 12,
2198
2210
  blocks: e.map((e, t) => {
2199
- let n = e, r = Rn(n), i = Sn(r, e.count);
2211
+ let n = e, r = Gn(n), i = kn(r, e.count);
2200
2212
  return {
2201
2213
  id: e.id,
2202
2214
  title: e.title.trim() || `섹션 ${t + 1}`,
2203
2215
  showTitle: e.showTitle !== !1,
2204
2216
  kind: r,
2205
- boardId: Bn(n),
2217
+ boardId: qn(n),
2206
2218
  count: i,
2207
- postIds: Vn(n, i),
2208
- sort: zn(n),
2209
- cols: In(n),
2210
- height: Ln(n),
2219
+ postIds: Jn(n, i),
2220
+ sort: Kn(n),
2221
+ cols: Un(n),
2222
+ height: Wn(n),
2211
2223
  order: t
2212
2224
  };
2213
2225
  }),
2214
- updatedAt: G()
2226
+ updatedAt: K()
2215
2227
  });
2216
2228
  }
2217
2229
  return {
@@ -2219,43 +2231,42 @@ function Un() {
2219
2231
  pending: r,
2220
2232
  sections: i,
2221
2233
  blocks: i,
2222
- hasSavedLayout: M(() => Array.isArray(n.value?.blocks)),
2234
+ hasSavedLayout: A(() => Array.isArray(n.value?.blocks)),
2223
2235
  saveSections: a,
2224
2236
  saveBlocks: a
2225
2237
  };
2226
2238
  }
2227
2239
  //#endregion
2228
2240
  //#region src/composables/useMemiBoardMiniPosts.ts
2229
- function Wn(e, t) {
2230
- let n = Q(), r = () => _(), i = M(() => {
2231
- if (!import.meta.client) return null;
2232
- let i = Math.max(1, Math.floor(Number(F(t)) || 5)), a = F(e)?.trim() || "", o = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
2233
- return a && o.unshift(Y("boardId", "==", a)), W(c(n, r()), ...o, U("createdAt", "desc"), V(i));
2234
- }), { data: a, pending: o, error: s } = se(i);
2241
+ function Zn(e, t) {
2242
+ let n = Q(), r = () => h(), i = N(e)?.trim() || "all", a = A(() => {
2243
+ let i = Math.max(1, Math.floor(Number(N(t)) || 5)), a = N(e)?.trim() || "", s = [X("isPublished", "==", !0), X("listed", "==", !0)];
2244
+ return a && s.unshift(X("boardId", "==", a)), G(o(n, r()), ...s, W("createdAt", "desc"), H(i));
2245
+ }), { data: s, pending: c, error: l } = ie(a, { ssrKey: S(r(), `mini-list/${i}`) });
2235
2246
  return {
2236
- posts: M(() => a.value ?? []),
2237
- pending: o,
2238
- error: s
2247
+ posts: A(() => s.value ?? []),
2248
+ pending: c,
2249
+ error: l
2239
2250
  };
2240
2251
  }
2241
- function Gn(e) {
2242
- let t = Q(), n = () => _(), r = M(() => [...new Set(F(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = M(() => !import.meta.client || !r.value.length ? null : W(c(t, n()), Y(L(), "in", r.value))), { data: a, pending: o, error: s } = se(i);
2252
+ function Qn(e) {
2253
+ let t = Q(), n = () => h(), r = A(() => [...new Set(N(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = r.value.join(",") || "none", a = A(() => r.value.length ? G(o(t, n()), X(R(), "in", r.value)) : null), { data: s, pending: c, error: l } = ie(a, { ssrKey: S(n(), `mini-picked/${i}`) });
2243
2254
  return {
2244
- posts: M(() => {
2245
- let e = new Map((a.value ?? []).map((e) => [e.id, e])), t = [];
2255
+ posts: A(() => {
2256
+ let e = new Map((s.value ?? []).map((e) => [e.id, e])), t = [];
2246
2257
  for (let n of r.value) {
2247
2258
  let r = e.get(n);
2248
2259
  r && r.isPublished !== !1 && t.push(r);
2249
2260
  }
2250
2261
  return t;
2251
2262
  }),
2252
- pending: o,
2253
- error: s
2263
+ pending: c,
2264
+ error: l
2254
2265
  };
2255
2266
  }
2256
2267
  //#endregion
2257
2268
  //#region src/data/versionHistory.ts
2258
- var Kn = [
2269
+ var $n = [
2259
2270
  {
2260
2271
  version: "0.42.0",
2261
2272
  date: "2026-08-27",
@@ -2525,7 +2536,7 @@ var Kn = [
2525
2536
  highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
2526
2537
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
2527
2538
  }
2528
- ], qn = /* @__PURE__ */ i(((e, t) => {
2539
+ ], er = /* @__PURE__ */ Ae(((e, t) => {
2529
2540
  (function(n, r) {
2530
2541
  typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs = r();
2531
2542
  })(e, (function() {
@@ -2819,7 +2830,7 @@ var Kn = [
2819
2830
  return w(1e3 * e);
2820
2831
  }, w.en = b[y], w.Ls = b, w.p = {}, w;
2821
2832
  }));
2822
- })), Jn = /* @__PURE__ */ i(((e, t) => {
2833
+ })), tr = /* @__PURE__ */ Ae(((e, t) => {
2823
2834
  (function(n, r) {
2824
2835
  typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_plugin_relativeTime = r();
2825
2836
  })(e, (function() {
@@ -2923,9 +2934,9 @@ var Kn = [
2923
2934
  };
2924
2935
  };
2925
2936
  }));
2926
- })), Yn = /* @__PURE__ */ i(((e, t) => {
2937
+ })), nr = /* @__PURE__ */ Ae(((e, t) => {
2927
2938
  (function(n, r) {
2928
- typeof e == "object" && t !== void 0 ? t.exports = r(qn()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
2939
+ typeof e == "object" && t !== void 0 ? t.exports = r(er()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
2929
2940
  })(e, (function(e) {
2930
2941
  function t(e) {
2931
2942
  return e && typeof e == "object" && "default" in e ? e : { default: e };
@@ -2973,24 +2984,24 @@ var Kn = [
2973
2984
  };
2974
2985
  return n.default.locale(r, null, !0), r;
2975
2986
  }));
2976
- })), Xn = /* @__PURE__ */ a(qn(), 1), Zn = /* @__PURE__ */ a(Jn(), 1);
2977
- Yn(), Xn.default.extend(Zn.default), Xn.default.locale("ko");
2978
- function Qn(e) {
2987
+ })), rr = /* @__PURE__ */ Me(er(), 1), ir = /* @__PURE__ */ Me(tr(), 1);
2988
+ nr(), rr.default.extend(ir.default), rr.default.locale("ko");
2989
+ function ar(e) {
2979
2990
  return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
2980
2991
  }
2981
- function $n(e, t = Date.now()) {
2982
- return e?.toDate ? (0, Xn.default)(e.toDate()).from((0, Xn.default)(t)) : "방금 전";
2992
+ function or(e, t = Date.now()) {
2993
+ return e?.toDate ? (0, rr.default)(e.toDate()).from((0, rr.default)(t)) : "방금 전";
2983
2994
  }
2984
- function er(e) {
2985
- return e?.toDate ? (0, Xn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2995
+ function sr(e) {
2996
+ return e?.toDate ? (0, rr.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2986
2997
  }
2987
- function tr(e, t) {
2988
- let n = er(e), r = e?.toMillis?.(), i = t?.toMillis?.();
2989
- return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${er(t)}`];
2998
+ function cr(e, t) {
2999
+ let n = sr(e), r = e?.toMillis?.(), i = t?.toMillis?.();
3000
+ return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${sr(t)}`];
2990
3001
  }
2991
3002
  //#endregion
2992
3003
  //#region src/composables/useMemiBoardWritingAssistant.ts
2993
- var nr = 10, rr = 864e5, ir = 200, ar = {
3004
+ var lr = 10, ur = 864e5, dr = 200, fr = {
2994
3005
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
2995
3006
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
2996
3007
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -2999,46 +3010,46 @@ var nr = 10, rr = 864e5, ir = 200, ar = {
2999
3010
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
3000
3011
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
3001
3012
  };
3002
- function or(e) {
3003
- if (e.action !== "custom") return ar[e.action];
3004
- let t = e.customInstruction?.trim().slice(0, ir);
3013
+ function pr(e) {
3014
+ if (e.action !== "custom") return fr[e.action];
3015
+ let t = e.customInstruction?.trim().slice(0, dr);
3005
3016
  if (!t) throw Error("어떻게 바꿀지 지시문을 입력해 주세요.");
3006
3017
  return `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${t}`;
3007
3018
  }
3008
- function sr(e) {
3019
+ function mr(e) {
3009
3020
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
3010
3021
  }
3011
- function cr(e) {
3022
+ function hr(e) {
3012
3023
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
3013
3024
  }
3014
- function lr() {
3015
- let e = le(), t = Q(), n = Z(), r = u();
3025
+ function gr() {
3026
+ let e = se(), t = Q(), n = ae(), r = c();
3016
3027
  async function i() {
3017
3028
  let e = n.value?.uid;
3018
3029
  if (!e) throw Error("AI 글쓰기 도우미는 로그인 후 사용할 수 있습니다.");
3019
- await oe(t, async (n) => {
3020
- let r = x(t, _(), e), i = await n.get(r);
3030
+ await re(t, async (n) => {
3031
+ let r = y(t, h(), e), i = await n.get(r);
3021
3032
  if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
3022
- let a = i.data(), o = cr(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < rr;
3033
+ let a = i.data(), o = hr(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < ur;
3023
3034
  if (l && s >= 10) {
3024
- let e = o + rr, t = Math.max(1, Math.ceil((e - c) / 36e5));
3035
+ let e = o + ur, t = Math.max(1, Math.ceil((e - c) / 36e5));
3025
3036
  throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
3026
3037
  }
3027
3038
  n.update(r, {
3028
3039
  aiWritingCount: l ? s + 1 : 1,
3029
- aiWritingWindowAt: l ? a.aiWritingWindowAt : G(),
3030
- updatedAt: G()
3040
+ aiWritingWindowAt: l ? a.aiWritingWindowAt : K(),
3041
+ updatedAt: K()
3031
3042
  });
3032
3043
  });
3033
3044
  }
3034
3045
  async function a(t) {
3035
- let n = or(t);
3046
+ let n = pr(t);
3036
3047
  if (!t.content.trim()) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
3037
3048
  await i();
3038
- let a = r.moderation ?? {}, o = we(e, {
3039
- backend: new Se(),
3049
+ let a = r.moderation ?? {}, o = Se(e, {
3050
+ backend: new be(),
3040
3051
  useLimitedUseAppCheckTokens: a.useLimitedUseAppCheckTokens !== !1
3041
- }), s = Te(o, {
3052
+ }), s = Ce(o, {
3042
3053
  model: a.model ?? "gemini-3.5-flash-lite",
3043
3054
  systemInstruction: [
3044
3055
  "너는 게시판 글쓰기 편집 도우미다.",
@@ -3053,7 +3064,7 @@ function lr() {
3053
3064
  `작업: ${n}`,
3054
3065
  t.title ? `현재 제목: ${t.title}` : "",
3055
3066
  `본문:\n${c.slice(0, 12e3)}`
3056
- ].filter(Boolean).join("\n\n"), u = sr((await s.generateContent(l)).response.text());
3067
+ ].filter(Boolean).join("\n\n"), u = mr((await s.generateContent(l)).response.text());
3057
3068
  if (!u) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
3058
3069
  return u;
3059
3070
  }
@@ -3061,14 +3072,14 @@ function lr() {
3061
3072
  }
3062
3073
  //#endregion
3063
3074
  //#region src/utils/renderMarkdown.ts
3064
- function ur(e) {
3075
+ function _r(e) {
3065
3076
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3066
3077
  }
3067
- function dr(e) {
3078
+ function vr(e) {
3068
3079
  let t = e;
3069
3080
  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;
3070
3081
  }
3071
- function fr(e) {
3082
+ function yr(e) {
3072
3083
  if (!e?.trim()) return "";
3073
3084
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3074
3085
  function c() {
@@ -3077,7 +3088,7 @@ function fr(e) {
3077
3088
  return;
3078
3089
  }
3079
3090
  let e = o;
3080
- n.push(`<${e}>${s.map((e) => `<li>${dr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3091
+ n.push(`<${e}>${s.map((e) => `<li>${vr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3081
3092
  }
3082
3093
  for (; r < t.length;) {
3083
3094
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3086,7 +3097,7 @@ function fr(e) {
3086
3097
  continue;
3087
3098
  }
3088
3099
  if (i) {
3089
- a.push(ur(e)), r++;
3100
+ a.push(_r(e)), r++;
3090
3101
  continue;
3091
3102
  }
3092
3103
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3097,24 +3108,24 @@ function fr(e) {
3097
3108
  if (u) {
3098
3109
  c();
3099
3110
  let e = u[1].length;
3100
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${dr(ur(u[2]))}</h${e}>`), r++;
3111
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${vr(_r(u[2]))}</h${e}>`), r++;
3101
3112
  continue;
3102
3113
  }
3103
3114
  if (e.startsWith("> ")) {
3104
3115
  c();
3105
3116
  let e = [];
3106
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(ur((t[r] ?? "").slice(2))), r++;
3107
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${dr(e.join("<br />"))}</blockquote>`);
3117
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(_r((t[r] ?? "").slice(2))), r++;
3118
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${vr(e.join("<br />"))}</blockquote>`);
3108
3119
  continue;
3109
3120
  }
3110
3121
  let d = e.match(/^[-*+]\s+(.+)$/);
3111
3122
  if (d) {
3112
- o && o !== "ul" && c(), o = "ul", s.push(ur(d[1])), r++;
3123
+ o && o !== "ul" && c(), o = "ul", s.push(_r(d[1])), r++;
3113
3124
  continue;
3114
3125
  }
3115
3126
  let f = e.match(/^\d+\.\s+(.+)$/);
3116
3127
  if (f) {
3117
- o && o !== "ol" && c(), o = "ol", s.push(ur(f[1])), r++;
3128
+ o && o !== "ol" && c(), o = "ol", s.push(_r(f[1])), r++;
3118
3129
  continue;
3119
3130
  }
3120
3131
  if (!e.trim()) {
@@ -3126,13 +3137,13 @@ function fr(e) {
3126
3137
  for (; r < t.length;) {
3127
3138
  let e = t[r] ?? "";
3128
3139
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3129
- p.push(ur(e)), r++;
3140
+ p.push(_r(e)), r++;
3130
3141
  }
3131
- n.push(`<p class="my-2 leading-relaxed">${dr(p.join("<br />"))}</p>`);
3142
+ n.push(`<p class="my-2 leading-relaxed">${vr(p.join("<br />"))}</p>`);
3132
3143
  }
3133
3144
  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");
3134
3145
  }
3135
3146
  //#endregion
3136
- export { Bt as AUTHOR_MEMO_MAX_LENGTH, vn as BOARD_SECTION_COLS, yn as BOARD_SECTION_KINDS, bn as BOARD_SECTION_SORTS, hn as BOARD_USER_ROLES, Gt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, Ct as DEFAULT_BOARDS, wt as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Qt as REPORT_DETAIL_MAX_LENGTH, $t as REPORT_REASONS, nr as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, St as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, Sn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, xn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Qn as formatDate, er as formatFullDate, $n as formatRelativeDate, Ze as formatRestrictedUntilLabel, tr as formatTimestampDetails, _t as hasBodyImage, gt as hasBodyText, Be as isAssignedStaff, vt as isContentEmpty, Ot as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Xt as memiBoardCommentLikesKey, Nn as miniBoardView, On as miniCommentLink, Dn as miniMoreLink, kn as miniPostImage, En as miniPostLink, An as miniPostTitle, Pn as miniPostsView, jn as miniViewGroup, Mn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Hn as normalizeBoardSection, Cn as pairSectionCols, mt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, fr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, en as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, wn as sectionColClass, Tn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, ht as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Ht as useMemiBoardAuthorMemo, zt as useMemiBoardCommentFeed, Zt as useMemiBoardCommentLikes, qt as useMemiBoardComments, u as useMemiBoardConfig, Yt as useMemiBoardLikes, Wn as useMemiBoardMiniLatest, Gn as useMemiBoardMiniPicked, mn as useMemiBoardModeration, Nt as useMemiBoardPost, Mt as useMemiBoardPostList, At as useMemiBoardPosts, Jt as useMemiBoardReplies, rn as useMemiBoardReportQueue, tn as useMemiBoardReports, Un as useMemiBoardSections, Dt as useMemiBoardSettings, Lt as useMemiBoardUserComments, Ft as useMemiBoardUserPosts, _n as useMemiBoardUserProfile, gn as useMemiBoardUsers, an as useMemiBoardViews, lr as useMemiBoardWritingAssistant, Kn as versionHistory, xt as videoListCoverUrl, bt as youtubeId };
3147
+ export { qt as AUTHOR_MEMO_MAX_LENGTH, Tn as BOARD_SECTION_COLS, En as BOARD_SECTION_KINDS, Dn as BOARD_SECTION_SORTS, Sn as BOARD_USER_ROLES, Qt as COMMENT_BODY_MAX_LENGTH, et as DEFAULT_BLOCK_BAN_DECAY_MS, $e as DEFAULT_BLOCK_BAN_THRESHOLD, At as DEFAULT_BOARDS, jt as DEFAULT_CATEGORIES, x as DEFAULT_COMMENTS_COLLECTION, w as DEFAULT_LIKES_COLLECTION, u as DEFAULT_POSTS_COLLECTION, v as DEFAULT_REPORTS_COLLECTION, T as DEFAULT_SETTINGS_COLLECTION, d as DEFAULT_USERS_COLLECTION, on as REPORT_DETAIL_MAX_LENGTH, sn as REPORT_REASONS, lr as WRITING_ASSISTANT_DAILY_LIMIT, Ne as asHttpUrl, ze as boardListOgDescription, Re as boardListOgTitle, Le as boardPostOgDescription, Ie as boardPostOgTitle, S as boardSsrKey, y as boardUserDoc, p as boardUsersCol, kt as buildPostPreview, Je as canManageBoardByRole, Ye as canWriteCommentByRole, kn as clampSectionCount, s as commentDoc, E as commentsCol, C as configureMemiBoard, On as defaultSectionCount, tt as effectiveModerationBlockCount, lt as extractEditorImageUrls, Ge as fetchPublicListForSeo, We as fetchPublicPostForSeo, ar as formatDate, sr as formatFullDate, or as formatRelativeDate, it as formatRestrictedUntilLabel, cr as formatTimestampDetails, wt as hasBodyImage, Ct as hasBodyText, qe as isAssignedStaff, Tt as isContentEmpty, Ft as isFirestorePermissionDenied, rt as isModerationWriteRestricted, k as likeDoc, l as likeDocId, O as likesCol, rn as memiBoardCommentLikesKey, Bn as miniBoardView, Fn as miniCommentLink, Pn as miniMoreLink, In as miniPostImage, Nn as miniPostLink, Ln as miniPostTitle, Vn as miniPostsView, Rn as miniViewGroup, zn as miniViewLabel, nt as moderationWriteRestrictedUntilMs, Be as normalizeBasePath, Yn as normalizeBoardSection, An as pairSectionCols, xt as plainTextFromHtml, a as postBodyDoc, t as postDoc, dt as postNamespaceFromStoragePath, D as postStorageFolder, o as postsCol, yr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, cn as reportReasonLabel, i as reportsCol, _ as resolveBoardPathConfig, Ve as resolvePublicSeoDb, jn as sectionColClass, Mn as sectionMinHeight, e as settingsCol, g as settingsDoc, b as slugify, ut as storagePathFromDownloadUrl, St as titleFromBody, Fe as toAbsoluteUrl, Pe as toBoardOgImageUrl, h as useBoardPathConfig, ct as useMemiBoardAuth, Yt as useMemiBoardAuthorMemo, Kt as useMemiBoardCommentFeed, an as useMemiBoardCommentLikes, en as useMemiBoardComments, c as useMemiBoardConfig, nn as useMemiBoardLikes, Zn as useMemiBoardMiniLatest, Qn as useMemiBoardMiniPicked, xn as useMemiBoardModeration, Bt as useMemiBoardPost, zt as useMemiBoardPostList, Lt as useMemiBoardPosts, tn as useMemiBoardReplies, dn as useMemiBoardReportQueue, ln as useMemiBoardReports, Xn as useMemiBoardSections, Pt as useMemiBoardSettings, Wt as useMemiBoardUserComments, Ht as useMemiBoardUserPosts, wn as useMemiBoardUserProfile, Cn as useMemiBoardUsers, fn as useMemiBoardViews, gr as useMemiBoardWritingAssistant, $n as versionHistory, Ot as videoListCoverUrl, Dt as youtubeId };
3137
3148
 
3138
3149
  //# sourceMappingURL=index.js.map