memi-board 0.45.0 → 0.46.0

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