memi-board 0.37.2 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -174,19 +174,25 @@ function Be(e, t) {
174
174
  function Ve(e, t, n, r) {
175
175
  return e ? t ? !0 : n ? Be(e, r) : !1 : !1;
176
176
  }
177
+ function He(e, t, n, r) {
178
+ if (!e) return !1;
179
+ if (r?.visibility === "hidden") return Ve(e, t, n, r);
180
+ let i = r?.commentWriteRole ?? "user";
181
+ return i === "admin" ? t : i !== "staff" || Ve(e, t, n, r);
182
+ }
177
183
  //#endregion
178
184
  //#region src/composables/boardLookup.ts
179
- var He;
180
- function Ue(e) {
181
- He = e;
182
- }
185
+ var Ue;
183
186
  function We(e) {
184
- return He?.(e);
187
+ Ue = e;
188
+ }
189
+ function Ge(e) {
190
+ return Ue?.(e);
185
191
  }
186
192
  //#endregion
187
193
  //#region src/utils/moderation-strike.ts
188
- var Ge = 10, Ke = 864e5;
189
- function qe(e, t, n = Date.now(), r = Ke) {
194
+ var Ke = 10, qe = 864e5;
195
+ function Je(e, t, n = Date.now(), r = qe) {
190
196
  let i = Math.max(0, Math.floor(Number(e) || 0));
191
197
  if (i <= 0) return 0;
192
198
  let a = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -194,7 +200,7 @@ function qe(e, t, n = Date.now(), r = Ke) {
194
200
  let o = Math.floor((n - a) / r);
195
201
  return Math.max(0, i - o);
196
202
  }
197
- function Je(e, t, n = Date.now(), r = 10, i = Ke) {
203
+ function Ye(e, t, n = Date.now(), r = 10, i = qe) {
198
204
  let a = Math.max(0, Math.floor(Number(e) || 0));
199
205
  if (a < r) return null;
200
206
  let o = typeof t == "number" && Number.isFinite(t) ? t : null;
@@ -202,17 +208,17 @@ function Je(e, t, n = Date.now(), r = 10, i = Ke) {
202
208
  let s = o + (a - (r - 1)) * i;
203
209
  return s > n ? s : null;
204
210
  }
205
- function Ye(e, t, n = Date.now(), r = 10, i = Ke) {
206
- return Je(e, t, n, r, i) != null;
211
+ function Xe(e, t, n = Date.now(), r = 10, i = qe) {
212
+ return Ye(e, t, n, r, i) != null;
207
213
  }
208
- function Xe(e, t = Date.now()) {
214
+ function Ze(e, t = Date.now()) {
209
215
  let n = Math.max(0, e - t), r = Math.ceil(n / 36e5);
210
216
  if (r <= 0) return "곧 해제";
211
217
  if (r < 24) return `약 ${r}시간 후 해제`;
212
218
  let i = Math.floor(r / 24), a = r % 24;
213
219
  return a === 0 ? `약 ${i}일 후 해제` : `약 ${i}일 ${a}시간 후 해제`;
214
220
  }
215
- function Ze(e) {
221
+ function Qe(e) {
216
222
  if (e == null) return null;
217
223
  if (typeof e == "number" && Number.isFinite(e)) return e;
218
224
  if (typeof e == "object" && e && "toMillis" in e && typeof e.toMillis == "function") return e.toMillis();
@@ -224,26 +230,26 @@ function Ze(e) {
224
230
  }
225
231
  //#endregion
226
232
  //#region src/composables/useMemiBoardAuth.ts
227
- var Qe = "__MEMI_BOARD_AUTH_STATE__";
228
- function $e() {
233
+ var $e = "__MEMI_BOARD_AUTH_STATE__";
234
+ function et() {
229
235
  let e = globalThis;
230
- return e[Qe] || (e[Qe] = {
236
+ return e[$e] || (e[$e] = {
231
237
  role: P("user"),
232
238
  rolePending: P(!0),
233
239
  rawBlockCount: P(0),
234
240
  blockAtMs: P(null),
235
241
  watchStarted: !1
236
- }), e[Qe];
242
+ }), e[$e];
237
243
  }
238
- function et() {
239
- let e = u(), t = $e(), n = Q(), r = ue(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: c } = t;
244
+ function tt() {
245
+ let e = u(), t = et(), n = Q(), r = ue(), i = Z(), { role: a, rolePending: o, rawBlockCount: s, blockAtMs: c } = t;
240
246
  function l() {
241
247
  let t = e.moderation?.blockBanThreshold;
242
248
  return t === 0 ? 0 : typeof t == "number" && t > 0 ? Math.floor(t) : 10;
243
249
  }
244
250
  function d() {
245
251
  let t = e.moderation?.blockBanDecayMs;
246
- return typeof t == "number" && t > 0 ? t : Ke;
252
+ return typeof t == "number" && t > 0 ? t : qe;
247
253
  }
248
254
  function f(e) {
249
255
  return x(n, _(), e);
@@ -253,7 +259,7 @@ function et() {
253
259
  s.value = 0, c.value = null;
254
260
  return;
255
261
  }
256
- s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), c.value = Ze(e.moderationBlockAt);
262
+ s.value = Math.max(0, Math.floor(Number(e.moderationBlockCount) || 0)), c.value = Qe(e.moderationBlockAt);
257
263
  }
258
264
  async function m(e) {
259
265
  let t = f(e.uid), r = g(n, _(), e.uid), i = await R(t);
@@ -299,14 +305,14 @@ function et() {
299
305
  o.value = !1;
300
306
  }
301
307
  }, { immediate: !0 }));
302
- let v = M(() => !!i.value), y = M(() => a.value === "admin"), b = M(() => a.value === "staff"), S = M(() => y.value || b.value), C = M(() => qe(s.value, c.value, Date.now(), d())), w = M(() => {
308
+ let v = M(() => !!i.value), y = M(() => a.value === "admin"), b = M(() => a.value === "staff"), S = M(() => y.value || b.value), C = M(() => Je(s.value, c.value, Date.now(), d())), w = M(() => {
303
309
  let e = l();
304
- return e <= 0 ? !1 : Ye(s.value, c.value, Date.now(), e, d());
310
+ return e <= 0 ? !1 : Xe(s.value, c.value, Date.now(), e, d());
305
311
  }), T = M(() => {
306
312
  let e = l();
307
313
  if (e <= 0) return null;
308
- let t = Je(s.value, c.value, Date.now(), e, d());
309
- return t == null ? null : Xe(t);
314
+ let t = Ye(s.value, c.value, Date.now(), e, d());
315
+ return t == null ? null : Ze(t);
310
316
  }), E = M(() => {
311
317
  if (!w.value) return null;
312
318
  let e = T.value;
@@ -333,9 +339,9 @@ function et() {
333
339
  });
334
340
  let a = (r.exists() ? Math.max(0, Math.floor(Number(r.data().moderationBlockCount) || 0)) : 0) + 1, o = Date.now();
335
341
  s.value = a, c.value = o;
336
- let u = qe(a, o, o, d()), p = Ye(a, o, o, t, d()), m = ` (경고 ${u}/${t})`;
342
+ let u = Je(a, o, o, d()), p = Xe(a, o, o, t, d()), m = ` (경고 ${u}/${t})`;
337
343
  if (p) {
338
- let e = Je(a, o, o, t, d()), n = e == null ? null : Xe(e, o);
344
+ let e = Ye(a, o, o, t, d()), n = e == null ? null : Ze(e, o);
339
345
  m = n ? ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다 (${n}).` : ` 경고 ${u}회가 되어 글·댓글 작성이 제한됩니다.`;
340
346
  }
341
347
  return {
@@ -366,7 +372,7 @@ function et() {
366
372
  await ge(O());
367
373
  }
368
374
  function P(e) {
369
- return Ve(i.value?.uid, y.value, b.value, e ? We(e) : void 0);
375
+ return Ve(i.value?.uid, y.value, b.value, e ? Ge(e) : void 0);
370
376
  }
371
377
  function F(e) {
372
378
  return v.value && (i.value?.uid === e.authorUid || P(e.boardId));
@@ -404,7 +410,7 @@ function et() {
404
410
  }
405
411
  //#endregion
406
412
  //#region src/utils/extractEditorImageUrls.ts
407
- function tt(e) {
413
+ function nt(e) {
408
414
  if (!e) return [];
409
415
  let t = /* @__PURE__ */ new Set();
410
416
  for (let n of e.matchAll(/!\[[^\]]*]\((https?:\/\/[^)\s]+)\)/g)) n[1] && t.add(n[1].replaceAll("&amp;", "&"));
@@ -413,7 +419,7 @@ function tt(e) {
413
419
  }
414
420
  //#endregion
415
421
  //#region src/utils/storagePath.ts
416
- function nt(e) {
422
+ function rt(e) {
417
423
  try {
418
424
  let t = new URL(e), n = t.hostname.toLowerCase();
419
425
  if (!(n === "firebasestorage.googleapis.com" || n.endsWith(".firebasestorage.app"))) return null;
@@ -423,104 +429,109 @@ function nt(e) {
423
429
  return null;
424
430
  }
425
431
  }
426
- function rt(e) {
432
+ function it(e) {
427
433
  return e.match(/(?:^|\/)posts\/([^/]+)\//)?.[1] ?? null;
428
434
  }
429
435
  //#endregion
430
436
  //#region src/utils/deletePostCascade.ts
431
- async function it(e) {
432
- let t = await be(e);
433
- await Promise.all([...t.items.map((e) => ve(e)), ...t.prefixes.map((e) => it(e))]);
437
+ async function at(e) {
438
+ try {
439
+ let t = await be(e);
440
+ await Promise.all([...t.items.map((e) => ve(e).catch(() => {})), ...t.prefixes.map((e) => at(e))]);
441
+ } catch {}
434
442
  }
435
- async function at(e, t) {
443
+ async function ot(e, t) {
444
+ await at(xe(e, `${t}/images`)), await at(xe(e, `${t}/attachments`));
445
+ }
446
+ async function st(e, t) {
436
447
  let n = [...new Set([...t].filter(Boolean))];
437
448
  await Promise.all(n.map((t) => ve(xe(e, t)).catch(() => {})));
438
449
  }
439
- var ot = 450;
440
- async function st(e, t) {
450
+ var ct = 450;
451
+ async function lt(e, t) {
441
452
  let n = await z(t);
442
- for (let t = 0; t < n.docs.length; t += ot) {
453
+ for (let t = 0; t < n.docs.length; t += ct) {
443
454
  let r = X(e);
444
- n.docs.slice(t, t + ot).forEach((e) => r.delete(e.ref)), await r.commit();
455
+ n.docs.slice(t, t + ct).forEach((e) => r.delete(e.ref)), await r.commit();
445
456
  }
446
457
  }
447
- async function ct(e, t, n) {
448
- await st(e, W(A(e, t), Y("postId", "==", n)));
458
+ async function ut(e, t, n) {
459
+ await lt(e, W(A(e, t), Y("postId", "==", n)));
449
460
  }
450
- async function lt(e, t, n) {
451
- await st(e, W(o(e, t), Y("postId", "==", n)));
461
+ async function dt(e, t, n) {
462
+ await lt(e, W(o(e, t), Y("postId", "==", n)));
452
463
  }
453
- async function ut(e, t, n) {
454
- await st(e, W(A(e, t), Y("commentId", "==", n)));
464
+ async function ft(e, t, n) {
465
+ await lt(e, W(A(e, t), Y("commentId", "==", n)));
455
466
  }
456
- async function dt(e, n, r, i) {
467
+ async function pt(e, n, r, i) {
457
468
  let a = t(e, r, i), o = s(e, r, i), [c, l] = await Promise.all([R(a), R(o)]);
458
469
  if (!c.exists()) return;
459
470
  let u = c.data(), d = l.exists() ? String(l.data().content ?? "") : "";
460
- await st(e, W(O(e, r), Y("postId", "==", i))), await ct(e, r, i), await lt(e, r, i);
471
+ await lt(e, W(O(e, r), Y("postId", "==", i))), await ut(e, r, i), await dt(e, r, i);
461
472
  let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
462
473
  for (let e of u.attachments ?? []) {
463
474
  if (!e?.path) continue;
464
475
  f.add(e.path);
465
- let t = rt(e.path);
476
+ let t = it(e.path);
466
477
  t && t !== i && p.add(t);
467
478
  }
468
- for (let e of tt(d)) {
469
- let t = nt(e);
479
+ for (let e of nt(d)) {
480
+ let t = rt(e);
470
481
  if (!t) continue;
471
482
  f.add(t);
472
483
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
473
484
  n !== t && n.includes("/images/thumbnails/") && f.add(n);
474
485
  let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
475
486
  r && !t.includes("/thumbnails/") && f.add(`${r[1]}thumbnails/${r[2]}.jpg`);
476
- let a = rt(t);
487
+ let a = it(t);
477
488
  a && a !== i && p.add(a);
478
489
  }
479
- await te(o), await it(xe(n, k(r, i)));
480
- for (let e of p) await it(xe(n, k(r, e)));
481
- await at(n, f), await te(a);
490
+ await te(o), await ot(n, k(r, i));
491
+ for (let e of p) await ot(n, k(r, e));
492
+ await st(n, f), await te(a);
482
493
  }
483
494
  //#endregion
484
495
  //#region src/utils/postBody.ts
485
- function ft(e) {
496
+ function mt(e) {
486
497
  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();
487
498
  }
488
- function pt(e, t = 40) {
489
- let n = ft(e);
499
+ function ht(e, t = 40) {
500
+ let n = mt(e);
490
501
  if (!n) return "";
491
502
  if (n.length <= t) return n;
492
503
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
493
504
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
494
505
  }
495
- function mt(e) {
496
- return ft(e).length > 0;
506
+ function gt(e) {
507
+ return mt(e).length > 0;
497
508
  }
498
- function ht(e, t) {
509
+ function _t(e, t) {
499
510
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
500
511
  }
501
- function gt(e) {
502
- return !mt(e);
512
+ function vt(e) {
513
+ return !gt(e);
503
514
  }
504
515
  //#endregion
505
516
  //#region src/utils/postPreview.ts
506
- function _t(e) {
517
+ function yt(e) {
507
518
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
508
519
  }
509
- function vt(e) {
520
+ function bt(e) {
510
521
  if (e) try {
511
- let t = new URL(_t(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
522
+ let t = new URL(yt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
512
523
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
513
524
  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;
514
525
  } catch {
515
526
  return;
516
527
  }
517
528
  }
518
- function yt(e) {
519
- let t = vt(e.videoUrl);
529
+ function xt(e) {
530
+ let t = bt(e.videoUrl);
520
531
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
521
532
  }
522
- function bt(e, t = []) {
523
- 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 ? _t(r[1]) : a, s = o ? vt(o) : void 0, c = n ? _t(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
533
+ function St(e, t = []) {
534
+ let n = e.match(/<img\b[^>]*\bsrc=["']([^"']+)["']/i), r = e.match(/<iframe\b[^>]*\bsrc=["']([^"']+)["']/i), i = t.find((e) => e.type.startsWith("image/"))?.url, a = t.find((e) => e.type.startsWith("video/"))?.url, o = r ? yt(r[1]) : a, s = o ? bt(o) : void 0, c = n ? yt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
524
535
  return {
525
536
  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),
526
537
  ...c ? { previewImage: c } : {},
@@ -529,7 +540,7 @@ function bt(e, t = []) {
529
540
  }
530
541
  //#endregion
531
542
  //#region src/composables/useMemiBoardSettings.ts
532
- var xt = [
543
+ var Ct = [
533
544
  {
534
545
  id: "free",
535
546
  label: "자유",
@@ -557,25 +568,25 @@ var xt = [
557
568
  visibility: "public",
558
569
  order: 2
559
570
  }
560
- ], St = xt;
561
- function Ct(e) {
571
+ ], wt = Ct;
572
+ function Tt(e) {
562
573
  return e === "hidden" ? "hidden" : "public";
563
574
  }
564
- function wt(e, t, n) {
575
+ function Et(e, t, n) {
565
576
  return {
566
577
  id: e,
567
578
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
568
579
  order: typeof t.order == "number" ? t.order : n,
569
580
  description: (typeof t.description == "string" ? t.description : "") || "",
570
- visibility: Ct(t.visibility),
581
+ visibility: Tt(t.visibility),
571
582
  listView: t.listView ?? "default",
572
583
  writeRole: t.writeRole ?? "user",
573
584
  commentWriteRole: t.commentWriteRole ?? "user",
574
585
  allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
575
586
  };
576
587
  }
577
- function Tt() {
578
- let t = () => _(), n = Q(), r = le(), { isSignedIn: i, isAdmin: a, isStaff: o, user: s } = et(), l = M(() => W(e(n, t()), U("order", "asc"))), { data: u, pending: d } = se(l, { ssrKey: w(t(), "boards") }), f = M(() => u.value.map((e, t) => wt(e.id, e, t))), p = f, m = M(() => f.value.filter((e) => e.visibility !== "hidden")), h = m;
588
+ function Dt() {
589
+ let t = () => _(), n = Q(), r = le(), { isSignedIn: i, isAdmin: a, isStaff: o, user: s } = tt(), l = M(() => W(e(n, t()), U("order", "asc"))), { data: u, pending: d } = se(l, { ssrKey: w(t(), "boards") }), f = M(() => u.value.map((e, t) => Et(e.id, e, t))), p = f, m = M(() => f.value.filter((e) => e.visibility !== "hidden")), h = m;
579
590
  function g(e) {
580
591
  if (e) return f.value.find((t) => t.id === e)?.label ?? e;
581
592
  }
@@ -592,29 +603,32 @@ function Tt() {
592
603
  return C(e) === "hidden";
593
604
  }
594
605
  let D = E;
595
- function O(e) {
606
+ function k(e) {
596
607
  if (e) return f.value.find((t) => t.id === e);
597
608
  }
598
- Ue((e) => O(e));
599
- function k(e) {
600
- return Ve(s.value?.uid, a.value, o.value, e ? O(e) : void 0);
609
+ We((e) => k(e));
610
+ function A(e) {
611
+ return Ve(s.value?.uid, a.value, o.value, e ? k(e) : void 0);
601
612
  }
602
- let A = M(() => f.value.filter((e) => k(e.id)));
603
- async function j(e, r) {
613
+ let j = M(() => f.value.filter((e) => A(e.id)));
614
+ async function N(e, t, r) {
604
615
  let i;
605
616
  for (;;) {
606
- let a = await z(W(c(n, t()), Y("boardId", "==", e), U("__name__"), ...i ? [q(i)] : [], V(400)));
617
+ let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
607
618
  if (a.empty) break;
608
- let o = X(n);
609
- for (let e of a.docs) o.update(e.ref, { listed: r });
610
- if (await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
619
+ let o = X(n), s = 0;
620
+ for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
621
+ if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
611
622
  }
612
623
  }
613
- function N(e, t) {
624
+ async function ee(e, r) {
625
+ await N(c(n, t()), e, r), await N(O(n, t()), e, r);
626
+ }
627
+ function P(e, t) {
614
628
  return {
615
629
  label: e.label.trim(),
616
630
  description: (e.description ?? "").trim(),
617
- visibility: Ct(e.visibility),
631
+ visibility: Tt(e.visibility),
618
632
  listView: e.listView ?? "default",
619
633
  writeRole: e.writeRole ?? "user",
620
634
  commentWriteRole: e.commentWriteRole ?? "user",
@@ -623,53 +637,53 @@ function Tt() {
623
637
  updatedAt: G()
624
638
  };
625
639
  }
626
- async function ee(e, r = e.order ?? 0) {
640
+ async function F(e, r = e.order ?? 0) {
627
641
  if (!i.value) throw Error("로그인이 필요합니다.");
628
642
  let a = e.id.trim(), o = e.label.trim();
629
643
  if (!a || !o) throw Error("보드 ID와 이름을 입력해 주세요.");
630
- let s = Ct(e.visibility), c = f.value.find((e) => e.id === a)?.visibility ?? "public", l = N({
644
+ let s = Tt(e.visibility), c = P({
631
645
  ...e,
632
646
  label: o,
633
647
  visibility: s
634
648
  }, r);
635
- await K(v(n, t(), a), l, { merge: !0 }), (c !== s || s === "public") && await j(a, s !== "hidden");
649
+ await K(v(n, t(), a), c, { merge: !0 }), await ee(a, s !== "hidden");
636
650
  }
637
- let P = ee;
638
- async function F(e) {
651
+ let I = F;
652
+ async function ne(e) {
639
653
  if (!i.value) throw Error("로그인이 필요합니다.");
640
654
  let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
641
655
  e.forEach((e, i) => {
642
656
  let s = e.id.trim(), c = e.label.trim();
643
657
  if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
644
- let l = Ct(e.visibility), u = N({
658
+ let l = Tt(e.visibility), u = P({
645
659
  ...e,
646
660
  label: c,
647
661
  visibility: l
648
662
  }, i);
649
- a.set(v(n, t(), s), u, { merge: !0 }), (r.get(s) ?? "public") !== l && o.push({
663
+ a.set(v(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
650
664
  id: s,
651
665
  listed: l !== "hidden"
652
666
  });
653
667
  }), await a.commit();
654
- for (let e of o) await j(e.id, e.listed);
668
+ for (let e of o) await ee(e.id, e.listed);
655
669
  }
656
- let I = F;
657
- async function ne(e) {
670
+ let re = ne;
671
+ async function L(e) {
658
672
  if (!i.value) throw Error("로그인이 필요합니다.");
659
673
  let a = e.trim();
660
674
  if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
661
675
  for (;;) {
662
676
  let e = await z(W(c(n, t()), Y("boardId", "==", a), V(25)));
663
677
  if (e.empty) break;
664
- for (let i of e.docs) await dt(n, ye(r), t(), i.id);
678
+ for (let i of e.docs) await pt(n, ye(r), t(), i.id);
665
679
  }
666
680
  await te(v(n, t(), e));
667
681
  }
668
- let re = ne;
669
- async function L() {
682
+ let ie = L;
683
+ async function R() {
670
684
  return Promise.resolve();
671
685
  }
672
- async function ie(e) {
686
+ async function B(e) {
673
687
  let t = e.trim();
674
688
  if (!t) throw Error("보드 이름을 입력해 주세요.");
675
689
  if (!i.value) throw Error("로그인이 필요합니다.");
@@ -677,7 +691,7 @@ function Tt() {
677
691
  if (n) return n.id;
678
692
  let r = S(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
679
693
  for (; s.has(a);) a = `${r}-${o++}`;
680
- return await ee({
694
+ return await F({
681
695
  id: a,
682
696
  label: t,
683
697
  visibility: "public",
@@ -694,9 +708,9 @@ function Tt() {
694
708
  boardDescription: b,
695
709
  boardVisibility: C,
696
710
  isBoardHidden: E,
697
- getBoard: O,
698
- canManageBoard: k,
699
- managedBoards: A,
711
+ getBoard: k,
712
+ canManageBoard: A,
713
+ managedBoards: j,
700
714
  categories: p,
701
715
  publicCategories: h,
702
716
  categoryLabel: y,
@@ -704,33 +718,33 @@ function Tt() {
704
718
  categoryVisibility: T,
705
719
  isCategoryHidden: D,
706
720
  settingsPending: d,
707
- ensureSettings: L,
708
- addBoard: ie,
709
- addCategory: ie,
710
- saveBoard: ee,
711
- saveCategory: P,
712
- saveBoards: F,
713
- saveCategories: I,
714
- deleteBoard: ne,
715
- deleteCategory: re,
716
- DEFAULT_BOARDS: xt,
717
- DEFAULT_CATEGORIES: St
721
+ ensureSettings: R,
722
+ addBoard: B,
723
+ addCategory: B,
724
+ saveBoard: F,
725
+ saveCategory: I,
726
+ saveBoards: ne,
727
+ saveCategories: re,
728
+ deleteBoard: L,
729
+ deleteCategory: ie,
730
+ DEFAULT_BOARDS: Ct,
731
+ DEFAULT_CATEGORIES: wt
718
732
  };
719
733
  }
720
734
  //#endregion
721
735
  //#region src/composables/useMemiBoardPosts.ts
722
- function Et(e) {
736
+ function Ot(e) {
723
737
  if (!e || typeof e != "object") return !1;
724
738
  let t = "code" in e ? String(e.code) : "";
725
739
  return t === "permission-denied" || t === "firestore/permission-denied";
726
740
  }
727
- function Dt(e) {
741
+ function kt(e) {
728
742
  let t = F(e)?.trim();
729
743
  if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
730
744
  return t;
731
745
  }
732
- function Ot(e) {
733
- let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } = Tt(), o = () => _(), l = () => Dt(e), u = () => c(n, o()), d = (e) => t(n, o(), e), f = (e) => s(n, o(), e);
746
+ function At(e) {
747
+ let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } = Dt(), o = () => _(), l = () => kt(e), u = () => c(n, o()), d = (e) => t(n, o(), e), f = (e) => s(n, o(), e);
734
748
  function p() {
735
749
  return !i(l());
736
750
  }
@@ -738,7 +752,7 @@ function Ot(e) {
738
752
  return a(l())?.listView === "image";
739
753
  }
740
754
  function h(e) {
741
- let t = m(), n = mt(e.content), r = ht(e.content, e.attachments);
755
+ let t = m(), n = gt(e.content), r = _t(e.content, e.attachments);
742
756
  if (t) {
743
757
  if (!r) throw Error("사진을 올려 주세요.");
744
758
  return;
@@ -781,13 +795,13 @@ function Ot(e) {
781
795
  post: e
782
796
  } : { status: "not-found" };
783
797
  } catch (e) {
784
- return Et(e) ? { status: "permission-denied" } : {
798
+ return Ot(e) ? { status: "permission-denied" } : {
785
799
  status: "error",
786
800
  message: e instanceof Error ? e.message : String(e)
787
801
  };
788
802
  }
789
803
  } catch (e) {
790
- return Et(e) ? { status: "permission-denied" } : {
804
+ return Ot(e) ? { status: "permission-denied" } : {
791
805
  status: "error",
792
806
  message: e instanceof Error ? e.message : String(e)
793
807
  };
@@ -825,7 +839,7 @@ function Ot(e) {
825
839
  async function C(e) {
826
840
  h(e);
827
841
  let t = e.postId || g(), n = m(), r, i;
828
- if (n) r = t, i = pt(e.content);
842
+ if (n) r = t, i = ht(e.content);
829
843
  else {
830
844
  i = e.title.trim();
831
845
  let t = S(i) || "post";
@@ -833,7 +847,7 @@ function Ot(e) {
833
847
  let n = 1;
834
848
  for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
835
849
  }
836
- let a = bt(e.content, e.attachments), o = p();
850
+ let a = St(e.content, e.attachments), o = p();
837
851
  return await K(d(t), {
838
852
  boardId: l(),
839
853
  slug: r,
@@ -857,7 +871,7 @@ function Ot(e) {
857
871
  }
858
872
  async function w(e, t) {
859
873
  h(t);
860
- let n = m() ? pt(t.content) : t.title.trim(), r = bt(t.content, t.attachments), i = p();
874
+ let n = m() ? ht(t.content) : t.title.trim(), r = St(t.content, t.attachments), i = p();
861
875
  await Promise.all([J(d(e), {
862
876
  title: n,
863
877
  ...r,
@@ -877,7 +891,7 @@ function Ot(e) {
877
891
  });
878
892
  }
879
893
  async function E(e) {
880
- await dt(n, ye(r), o(), e);
894
+ await pt(n, ye(r), o(), e);
881
895
  }
882
896
  return {
883
897
  createPostId: g,
@@ -891,9 +905,9 @@ function Ot(e) {
891
905
  deletePost: E
892
906
  };
893
907
  }
894
- var kt = 10;
895
- function At(e, t = {}) {
896
- let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = et(), o = () => _(), s = t.pageSize ?? kt, l = M(() => i.value?.uid ?? ""), u = M(() => Dt(e)), d = () => c(n, o()), f = M(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = P([]), m = P([]), h = P([]), g = P(!0), v = P(!0), y = P(!1), b = P(""), x = null, S = null, C, w;
908
+ var jt = 10;
909
+ function Mt(e, t = {}) {
910
+ let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = tt(), o = () => _(), s = t.pageSize ?? jt, l = M(() => i.value?.uid ?? ""), u = M(() => kt(e)), d = () => c(n, o()), f = M(() => t.publicOnly === void 0 ? !(r.value || a(u.value)) : typeof t.publicOnly == "boolean" ? t.publicOnly : t.publicOnly.value), p = P([]), m = P([]), h = P([]), g = P(!0), v = P(!0), y = P(!1), b = P(""), x = null, S = null, C, w;
897
911
  function T(e) {
898
912
  return e.createdAt?.toMillis?.() ?? 0;
899
913
  }
@@ -976,8 +990,8 @@ function At(e, t = {}) {
976
990
  loadMore: A
977
991
  };
978
992
  }
979
- function jt(e, n) {
980
- let r = Q(), i = () => _(), a = M(() => Dt(e)), o = M(() => typeof n == "string" ? n : n.value), c = M(() => t(r, i(), o.value)), l = M(() => s(r, i(), o.value)), { data: u, pending: d } = ce(c), { data: f, pending: p } = ce(l);
993
+ function Nt(e, n) {
994
+ let r = Q(), i = () => _(), a = M(() => kt(e)), o = M(() => typeof n == "string" ? n : n.value), c = M(() => t(r, i(), o.value)), l = M(() => s(r, i(), o.value)), { data: u, pending: d } = ce(c), { data: f, pending: p } = ce(l);
981
995
  return {
982
996
  post: M(() => !u.value || u.value.boardId !== a.value ? null : {
983
997
  ...u.value,
@@ -989,9 +1003,9 @@ function jt(e, n) {
989
1003
  }
990
1004
  //#endregion
991
1005
  //#region src/composables/useMemiBoardUserPosts.ts
992
- var Mt = 10;
993
- function Nt(e, t = {}) {
994
- let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ?? Mt, o = M(() => F(e)?.trim() || ""), s = P([]), l = P([]), u = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
1006
+ var Pt = 10;
1007
+ function Ft(e, t = {}) {
1008
+ let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ?? Pt, o = M(() => F(e)?.trim() || ""), s = P([]), l = P([]), u = P(!0), d = P(!0), f = P(!1), p = P(""), m = null, h = null, g;
995
1009
  function v(e) {
996
1010
  return {
997
1011
  id: e.id,
@@ -1057,9 +1071,9 @@ function Nt(e, t = {}) {
1057
1071
  }
1058
1072
  //#endregion
1059
1073
  //#region src/composables/useMemiBoardUserComments.ts
1060
- var Pt = 10;
1061
- function Ft(e, t = {}) {
1062
- let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ?? Pt, 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;
1074
+ var It = 10;
1075
+ function Lt(e, t = {}) {
1076
+ let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ?? It, o = M(() => F(e)?.trim() || ""), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
1063
1077
  function g(e) {
1064
1078
  return {
1065
1079
  id: e.id,
@@ -1075,7 +1089,7 @@ function Ft(e, t = {}) {
1075
1089
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
1076
1090
  });
1077
1091
  function b() {
1078
- return [Y("authorUid", "==", o.value)];
1092
+ return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
1079
1093
  }
1080
1094
  function x() {
1081
1095
  if (h?.(), !o.value) {
@@ -1121,11 +1135,11 @@ function Ft(e, t = {}) {
1121
1135
  }
1122
1136
  //#endregion
1123
1137
  //#region src/composables/useMemiBoardCommentFeed.ts
1124
- var It = 10;
1125
- function Lt(e = "latest", t = {}) {
1138
+ var Rt = 10;
1139
+ function zt(e = "latest", t = {}) {
1126
1140
  let n = Q(), r = () => _(), i = () => O(n, r()), a = M(() => {
1127
1141
  let e = Math.floor(Number(F(t.pageSize)));
1128
- return Number.isFinite(e) && e > 0 ? e : It;
1142
+ return Number.isFinite(e) && e > 0 ? e : Rt;
1129
1143
  }), o = M(() => F(e) === "likes" ? "likes" : "latest"), s = P([]), c = P([]), l = P(!0), u = P(!0), d = P(!1), f = P(""), p = null, m = null, h;
1130
1144
  function g(e) {
1131
1145
  return {
@@ -1150,10 +1164,15 @@ function Lt(e = "latest", t = {}) {
1150
1164
  });
1151
1165
  function x() {
1152
1166
  return o.value === "likes" ? [
1167
+ Y("listed", "==", !0),
1153
1168
  U("likeCount", "desc"),
1154
1169
  U("createdAt", "desc"),
1155
1170
  U(L(), "desc")
1156
- ] : [U("createdAt", "desc"), U(L(), "desc")];
1171
+ ] : [
1172
+ Y("listed", "==", !0),
1173
+ U("createdAt", "desc"),
1174
+ U(L(), "desc")
1175
+ ];
1157
1176
  }
1158
1177
  function S() {
1159
1178
  h?.(), h = H(W(i(), ...x(), V(a.value)), (e) => {
@@ -1190,9 +1209,9 @@ function Lt(e = "latest", t = {}) {
1190
1209
  }
1191
1210
  //#endregion
1192
1211
  //#region src/composables/useMemiBoardAuthorMemo.ts
1193
- var Rt = 100, zt = /* @__PURE__ */ new Map();
1212
+ var Bt = 100, Vt = /* @__PURE__ */ new Map();
1194
1213
  function $(e) {
1195
- let t = zt.get(e);
1214
+ let t = Vt.get(e);
1196
1215
  return t || (t = ee({
1197
1216
  text: "",
1198
1217
  sentiment: null,
@@ -1200,10 +1219,10 @@ function $(e) {
1200
1219
  pending: !1,
1201
1220
  saving: !1,
1202
1221
  error: ""
1203
- }), zt.set(e, t)), t;
1222
+ }), Vt.set(e, t)), t;
1204
1223
  }
1205
- function Bt(e) {
1206
- let t = Q(), n = () => _(), { user: r } = et(), i = M(() => F(e)), a = M(() => `${r.value?.uid ?? ""}:${i.value}`), o = M(() => {
1224
+ function Ht(e) {
1225
+ let t = Q(), n = () => _(), { user: r } = tt(), i = M(() => F(e)), a = M(() => `${r.value?.uid ?? ""}:${i.value}`), o = M(() => {
1207
1226
  let e = r.value?.uid, a = i.value;
1208
1227
  return !e || !a ? null : m(t, n(), e, a);
1209
1228
  }), s = M({
@@ -1272,95 +1291,96 @@ function Bt(e) {
1272
1291
  }
1273
1292
  //#endregion
1274
1293
  //#region src/composables/useMemiBoardComments.ts
1275
- var Vt = 10, Ht = 5, Ut = 1e3;
1276
- function Wt(e) {
1294
+ var Ut = 10, Wt = 5, Gt = 1e3;
1295
+ function Kt(e) {
1277
1296
  let t = e.trim();
1278
1297
  if (!t) throw Error("댓글 내용을 입력해 주세요.");
1279
- if (t.length > 1e3) throw Error(`댓글은 ${Ut.toLocaleString()}자까지 작성할 수 있습니다.`);
1298
+ if (t.length > 1e3) throw Error(`댓글은 ${Gt.toLocaleString()}자까지 작성할 수 있습니다.`);
1280
1299
  return t;
1281
1300
  }
1282
- function Gt(e, n, r = {}) {
1283
- let i = Q(), a = () => _(), o = () => O(i, a()), s = M(() => F(e)), c = M(() => F(n)), u = P([]), d = P([]), f = P(r.subscribe !== !1), p = P([]), m = P(!1), h = P(r.subscribe !== !1), g = P(r.subscribe !== !1), v = null, y, b;
1284
- function x(e) {
1301
+ function qt(e, n, r = {}) {
1302
+ let i = Q(), a = () => _(), { isBoardHidden: o } = Dt(), s = () => O(i, a()), c = M(() => F(e)), u = M(() => F(n)), d = () => !o(c.value), f = P([]), p = P([]), m = P(r.subscribe !== !1), h = P([]), g = P(!1), v = P(r.subscribe !== !1), y = P(r.subscribe !== !1), b = null, x, S;
1303
+ function C(e) {
1285
1304
  return e.createdAt?.toMillis?.() ?? 0;
1286
1305
  }
1287
- let S = M(() => {
1306
+ let w = M(() => {
1288
1307
  let e = /* @__PURE__ */ new Map();
1289
1308
  for (let t of [
1290
- ...u.value,
1291
- ...d.value,
1292
- ...p.value
1309
+ ...f.value,
1310
+ ...p.value,
1311
+ ...h.value
1293
1312
  ]) t.id && e.set(t.id, t);
1294
- return [...e.values()].sort((e, t) => x(e) - x(t) || String(e.id).localeCompare(String(t.id)));
1313
+ return [...e.values()].sort((e, t) => C(e) - C(t) || String(e.id).localeCompare(String(t.id)));
1295
1314
  });
1296
- I([c, s], async ([e]) => {
1297
- if (p.value = [], u.value = [], d.value = [], v = null, clearTimeout(y), y = void 0, b?.(), b = void 0, h.value = r.subscribe !== !1, r.subscribe !== !1) {
1298
- f.value = !0, g.value = !0;
1315
+ I([u, c], async ([e]) => {
1316
+ if (h.value = [], f.value = [], p.value = [], b = null, clearTimeout(x), x = void 0, S?.(), S = void 0, v.value = r.subscribe !== !1, r.subscribe !== !1) {
1317
+ m.value = !0, y.value = !0;
1299
1318
  try {
1300
- let t = z(W(o(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1319
+ let t = z(W(s(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1301
1320
  if (await Promise.all([t.then((t) => {
1302
- if (c.value !== e) return;
1321
+ if (u.value !== e) return;
1303
1322
  let n = t.docs.map((e) => ({
1304
1323
  id: e.id,
1305
1324
  ...e.data()
1306
1325
  }));
1307
- d.value = n.filter((e) => e.parentId === void 0);
1308
- }), w()]), c.value !== e) return;
1326
+ p.value = n.filter((e) => e.parentId === void 0);
1327
+ }), E()]), u.value !== e) return;
1309
1328
  } finally {
1310
- c.value === e && (f.value = !1, g.value = !1);
1329
+ u.value === e && (m.value = !1, y.value = !1);
1311
1330
  }
1312
1331
  }
1313
1332
  }, { immediate: !0 });
1314
- function C() {
1315
- b?.(), b = H(W(o(), Y("postId", "==", c.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1316
- let t = new Map(u.value.map((e) => [e.id, e]));
1333
+ function T() {
1334
+ S?.(), S = H(W(s(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1335
+ let t = new Map(f.value.map((e) => [e.id, e]));
1317
1336
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1318
1337
  id: n.doc.id,
1319
1338
  ...n.doc.data()
1320
1339
  });
1321
- u.value = [...t.values()];
1340
+ f.value = [...t.values()];
1322
1341
  }, (e) => {
1323
1342
  console.error("[memi-board:comments] onSnapshot failed", e);
1324
1343
  });
1325
1344
  }
1326
- async function w() {
1327
- if (!(m.value || !h.value)) {
1328
- m.value = !0;
1345
+ async function E() {
1346
+ if (!(g.value || !v.value)) {
1347
+ g.value = !0;
1329
1348
  try {
1330
1349
  let e = [
1331
- Y("postId", "==", c.value),
1350
+ Y("postId", "==", u.value),
1332
1351
  Y("parentId", "==", null),
1333
1352
  U("createdAt", "asc"),
1334
1353
  U(L(), "asc"),
1335
- ...v ? [q(v)] : [],
1354
+ ...b ? [q(b)] : [],
1336
1355
  V(11)
1337
- ], t = await z(W(o(), ...e)), n = t.docs.slice(0, Vt), r = n.map((e) => ({
1356
+ ], t = await z(W(s(), ...e)), n = t.docs.slice(0, Ut), r = n.map((e) => ({
1338
1357
  id: e.id,
1339
1358
  ...e.data()
1340
- })), i = new Set(p.value.map((e) => e.id));
1341
- p.value.push(...r.filter((e) => !i.has(e.id))), v = n.at(-1) ?? v, t.size <= Vt && (h.value = !1, y = setTimeout(() => {
1342
- y = void 0, C();
1359
+ })), i = new Set(h.value.map((e) => e.id));
1360
+ h.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Ut && (v.value = !1, x = setTimeout(() => {
1361
+ x = void 0, T();
1343
1362
  }, 1e3));
1344
1363
  } finally {
1345
- m.value = !1;
1364
+ g.value = !1;
1346
1365
  }
1347
1366
  }
1348
1367
  }
1349
1368
  N(() => {
1350
- clearTimeout(y), b?.();
1369
+ clearTimeout(x), S?.();
1351
1370
  });
1352
- async function T(e) {
1353
- let n = Wt(e.body), r = X(i), l = re(o());
1354
- r.set(l, {
1355
- postId: c.value,
1356
- boardId: s.value,
1371
+ async function D(e) {
1372
+ let n = Kt(e.body), r = X(i), o = re(s());
1373
+ r.set(o, {
1374
+ postId: u.value,
1375
+ boardId: c.value,
1376
+ listed: d(),
1357
1377
  body: n,
1358
1378
  authorUid: e.authorUid,
1359
1379
  authorName: e.authorName,
1360
1380
  authorPhoto: e.authorPhoto,
1361
1381
  moderationStatus: "approved",
1362
1382
  parentId: null,
1363
- rootId: l.id,
1383
+ rootId: o.id,
1364
1384
  depth: 0,
1365
1385
  replyToUid: null,
1366
1386
  replyToName: null,
@@ -1369,25 +1389,26 @@ function Gt(e, n, r = {}) {
1369
1389
  isReply: !1,
1370
1390
  createdAt: G(),
1371
1391
  updatedAt: G()
1372
- }), r.update(t(i, a(), c.value), { commentCount: B(1) }), await r.commit();
1392
+ }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1373
1393
  }
1374
- async function E(e) {
1375
- let n = Wt(e.body);
1394
+ async function k(e) {
1395
+ let n = Kt(e.body);
1376
1396
  if (!e.parent.id) throw Error("답글 대상 댓글을 찾을 수 없습니다.");
1377
- let r = X(i), u = re(o()), d = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1378
- if (!d) throw Error("댓글 스레드를 찾을 수 없습니다.");
1379
- let f = Math.min((e.parent.depth ?? 0) + 1, 2);
1380
- r.set(u, {
1381
- postId: c.value,
1382
- boardId: s.value,
1397
+ let r = X(i), o = re(s()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1398
+ if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
1399
+ let p = Math.min((e.parent.depth ?? 0) + 1, 2);
1400
+ r.set(o, {
1401
+ postId: u.value,
1402
+ boardId: c.value,
1403
+ listed: d(),
1383
1404
  body: n,
1384
1405
  authorUid: e.authorUid,
1385
1406
  authorName: e.authorName,
1386
1407
  authorPhoto: e.authorPhoto,
1387
1408
  moderationStatus: "approved",
1388
1409
  parentId: e.parent.id,
1389
- rootId: d,
1390
- depth: f,
1410
+ rootId: f,
1411
+ depth: p,
1391
1412
  replyToUid: e.parent.authorUid,
1392
1413
  replyToName: e.parent.authorName,
1393
1414
  replyCount: 0,
@@ -1395,23 +1416,23 @@ function Gt(e, n, r = {}) {
1395
1416
  isReply: !0,
1396
1417
  createdAt: G(),
1397
1418
  updatedAt: G()
1398
- }), r.update(l(i, a(), d), { replyCount: B(1) }), r.update(t(i, a(), c.value), { commentCount: B(1) }), await r.commit();
1419
+ }), r.update(l(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1399
1420
  }
1400
- async function D(e) {
1421
+ async function A(e) {
1401
1422
  if (!e.id) return;
1402
1423
  if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
1403
- await ut(i, a(), e.id);
1424
+ await ft(i, a(), e.id);
1404
1425
  let n = X(i);
1405
- n.delete(l(i, a(), e.id)), e.parentId && e.rootId && n.update(l(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(), c.value), { commentCount: B(-1) }), await n.commit(), p.value = p.value.filter((t) => t.id !== e.id), d.value = d.value.filter((t) => t.id !== e.id), u.value = u.value.filter((t) => t.id !== e.id);
1426
+ n.delete(l(i, a(), e.id)), e.parentId && e.rootId && n.update(l(i, a(), e.rootId), { replyCount: B(-1) }), n.update(t(i, a(), u.value), { commentCount: B(-1) }), await n.commit(), h.value = h.value.filter((t) => t.id !== e.id), p.value = p.value.filter((t) => t.id !== e.id), f.value = f.value.filter((t) => t.id !== e.id);
1406
1427
  }
1407
- async function k(e, t) {
1408
- let n = Wt(t);
1428
+ async function j(e, t) {
1429
+ let n = Kt(t);
1409
1430
  await J(l(i, a(), e), {
1410
1431
  body: n,
1411
1432
  updatedAt: G()
1412
1433
  });
1413
1434
  }
1414
- async function A(e, t, n) {
1435
+ async function ee(e, t, n) {
1415
1436
  await J(l(i, a(), e), {
1416
1437
  isBlinded: t,
1417
1438
  moderatedAt: G(),
@@ -1419,74 +1440,75 @@ function Gt(e, n, r = {}) {
1419
1440
  });
1420
1441
  }
1421
1442
  return {
1422
- comments: S,
1423
- commentsPending: M(() => g.value || f.value),
1424
- hasMore: h,
1425
- loadingMore: m,
1426
- loadMore: w,
1427
- addComment: T,
1428
- addReply: E,
1429
- updateComment: k,
1430
- setCommentBlinded: A,
1431
- deleteComment: D
1443
+ comments: w,
1444
+ commentsPending: M(() => y.value || m.value),
1445
+ hasMore: v,
1446
+ loadingMore: g,
1447
+ loadMore: E,
1448
+ addComment: D,
1449
+ addReply: k,
1450
+ updateComment: j,
1451
+ setCommentBlinded: ee,
1452
+ deleteComment: A
1432
1453
  };
1433
1454
  }
1434
- function Kt(e, t, n) {
1435
- let r = Q(), i = () => _(), a = () => O(r, i()), o = P([]), s = P(!1), c = P(!1), l = P(!0), u = null, d;
1436
- function f(e) {
1455
+ function Jt(e, t, n) {
1456
+ let r = Q(), i = () => _(), a = () => O(r, i()), o = t.trim(), s = P([]), c = P(!1), l = P(!1), u = P(!0), d = null, f;
1457
+ function p(e) {
1437
1458
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
1438
1459
  }
1439
- function p() {
1440
- d ||= H(W(a(), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1441
- let t = new Map(o.value.map((e) => [e.id, e]));
1460
+ function m() {
1461
+ f ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1462
+ let t = new Map(s.value.map((e) => [e.id, e]));
1442
1463
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1443
1464
  id: n.doc.id,
1444
1465
  ...n.doc.data()
1445
1466
  });
1446
- o.value = f([...t.values()]);
1467
+ s.value = p([...t.values()]);
1447
1468
  }, (e) => {
1448
1469
  console.error("[memi-board:replies] onSnapshot failed", e);
1449
1470
  });
1450
1471
  }
1451
- async function m() {
1452
- if (!(s.value || !l.value)) {
1453
- s.value = !0;
1472
+ async function h() {
1473
+ if (!(c.value || !u.value)) {
1474
+ c.value = !0;
1454
1475
  try {
1455
1476
  let e = [
1477
+ Y("postId", "==", o),
1456
1478
  Y("rootId", "==", n),
1457
1479
  Y("isReply", "==", !0),
1458
1480
  U("createdAt", "asc"),
1459
1481
  U(L(), "asc"),
1460
- ...u ? [q(u)] : [],
1482
+ ...d ? [q(d)] : [],
1461
1483
  V(6)
1462
- ], t = await z(W(a(), ...e)), r = new Set(o.value.map((e) => e.id)), i = t.docs.slice(0, Ht), s = i.map((e) => ({
1484
+ ], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Wt), c = i.map((e) => ({
1463
1485
  id: e.id,
1464
1486
  ...e.data()
1465
1487
  })).filter((e) => !r.has(e.id));
1466
- o.value = f([...o.value, ...s]), u = i.at(-1) ?? u, c.value = !0, t.size <= Ht && (l.value = !1, p());
1488
+ s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Wt && (u.value = !1, m());
1467
1489
  } finally {
1468
- s.value = !1;
1490
+ c.value = !1;
1469
1491
  }
1470
1492
  }
1471
1493
  }
1472
- function h() {
1473
- d?.(), d = void 0, o.value = [], u = null, c.value = !1, l.value = !0;
1494
+ function g() {
1495
+ f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
1474
1496
  }
1475
- async function g() {
1476
- h(), await m();
1477
- }
1478
- return N(() => d?.()), {
1479
- replies: o,
1480
- loading: s,
1481
- loaded: c,
1482
- hasMore: l,
1483
- loadMore: m,
1484
- refresh: g
1497
+ async function v() {
1498
+ g(), await h();
1499
+ }
1500
+ return N(() => f?.()), {
1501
+ replies: s,
1502
+ loading: c,
1503
+ loaded: l,
1504
+ hasMore: u,
1505
+ loadMore: h,
1506
+ refresh: v
1485
1507
  };
1486
1508
  }
1487
1509
  //#endregion
1488
1510
  //#region src/composables/useMemiBoardLikes.ts
1489
- function qt(e, n) {
1511
+ function Yt(e, n) {
1490
1512
  let r = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1491
1513
  function l(e) {
1492
1514
  return j(i, r(), n, e);
@@ -1525,8 +1547,8 @@ function qt(e, n) {
1525
1547
  toggleLike: u
1526
1548
  };
1527
1549
  }
1528
- var Jt = Symbol.for("memi-board:comment-likes");
1529
- function Yt(e, t) {
1550
+ var Xt = Symbol.for("memi-board:comment-likes");
1551
+ function Zt(e, t) {
1530
1552
  let n = () => _(), r = Q(), i = Z(), a = M(() => F(e)), o = M(() => F(t)), s = P(/* @__PURE__ */ new Set()), c = P(null), u;
1531
1553
  I([i, o], ([e, t]) => {
1532
1554
  u?.(), u = void 0, s.value = /* @__PURE__ */ new Set(), !(!e || !t) && (u = H(W(A(r, n()), Y("uid", "==", e.uid), Y("postId", "==", t), Y("target", "==", "comment")), (e) => {
@@ -1579,7 +1601,7 @@ function Yt(e, t) {
1579
1601
  }
1580
1602
  //#endregion
1581
1603
  //#region src/composables/useMemiBoardReports.ts
1582
- var Xt = 200, Zt = [
1604
+ var Qt = 200, $t = [
1583
1605
  {
1584
1606
  value: "spam",
1585
1607
  label: "스팸·광고"
@@ -1601,10 +1623,10 @@ var Xt = 200, Zt = [
1601
1623
  label: "기타"
1602
1624
  }
1603
1625
  ];
1604
- function Qt(e) {
1605
- return Zt.find((t) => t.value === e)?.label || e || "신고";
1626
+ function en(e) {
1627
+ return $t.find((t) => t.value === e)?.label || e || "신고";
1606
1628
  }
1607
- function $t(e, t) {
1629
+ function tn(e, t) {
1608
1630
  let n = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1609
1631
  I(a, (e) => {
1610
1632
  if (c?.(), c = void 0, !e) {
@@ -1622,7 +1644,7 @@ function $t(e, t) {
1622
1644
  if (!l) throw Error("로그인이 필요합니다.");
1623
1645
  if (s.value) return;
1624
1646
  if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
1625
- let u = Zt.some((e) => e.value === c.reason) ? c.reason : null;
1647
+ let u = $t.some((e) => e.value === c.reason) ? c.reason : null;
1626
1648
  if (!u) throw Error("신고 사유를 선택해 주세요.");
1627
1649
  let d = (c.detail || "").trim();
1628
1650
  if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
@@ -1651,9 +1673,9 @@ function $t(e, t) {
1651
1673
  submitReport: l
1652
1674
  };
1653
1675
  }
1654
- var en = 20;
1655
- function tn(e = {}) {
1656
- let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } = et(), s = e.pageSize ?? en, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
1676
+ var nn = 20;
1677
+ function rn(e = {}) {
1678
+ let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } = tt(), s = e.pageSize ?? nn, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
1657
1679
  function g(e) {
1658
1680
  return {
1659
1681
  id: e.id,
@@ -1707,7 +1729,7 @@ function tn(e = {}) {
1707
1729
  }
1708
1730
  //#endregion
1709
1731
  //#region src/composables/useMemiBoardViews.ts
1710
- function nn() {
1732
+ function an() {
1711
1733
  let e = () => _(), n = Q();
1712
1734
  function r(t, n) {
1713
1735
  let { postsCollection: r } = e();
@@ -1747,7 +1769,7 @@ function nn() {
1747
1769
  }
1748
1770
  //#endregion
1749
1771
  //#region src/utils/moderation-prompt.ts
1750
- var rn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\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은 \"\"", an = {
1772
+ var on = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이터입니다.\n아래 텍스트에 욕설·비속어·성희롱·혐오·위협·스팸·도배성 광고가 있으면 반드시 거부(flagged=true)합니다.\n\nflagged=true 예:\n- 욕설/비속어: 씨발, 시발, 병신, 지랄, 좆, 개새끼, 존나, fuck, shit, bitch 등\n- 성적 모욕, 혐오 발언, 협박\n- 게시판 주제와 무관한 도배성 광고/스팸\n\nflagged=false:\n- 게시판 주제에 맞는 정상적인 글/댓글 (강한 의견이나 정당한 비판은 허용)\n\nJSON만 출력:\n{\"flagged\":boolean,\"category\":\"none\"|\"abuse\"|\"spam\"|\"adult\"|\"violence\"|\"other\",\"reason\":string}\n- flagged=true면 category를 고르고, reason은 한국어로 **무엇이 문제인지** 구체적으로 1문장\n (예: \"욕설(비속어)이 포함되어 게시할 수 없습니다.\", \"광고·스팸성 문구가 있어 게시할 수 없습니다.\")\n 욕설 단어 전체를 그대로 길게 인용하지 말고, 유형(욕설/혐오/스팸 등)을 분명히 밝힌다.\n- flagged=false면 category는 \"none\", reason은 \"\"", sn = {
1751
1773
  abuse: "욕설·비속어·모욕",
1752
1774
  spam: "스팸·광고",
1753
1775
  adult: "선정적·성인 내용",
@@ -1755,17 +1777,17 @@ var rn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
1755
1777
  other: "부적절한 내용",
1756
1778
  none: ""
1757
1779
  };
1758
- function on(e) {
1780
+ function cn(e) {
1759
1781
  if (e.via === "local" && e.localHit) {
1760
1782
  let t = e.localHit.trim();
1761
1783
  return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
1762
1784
  }
1763
1785
  let t = (e.reason || "").trim();
1764
1786
  if (t) return t;
1765
- let n = e.category && e.category !== "none" ? an[e.category] || e.category : "";
1787
+ let n = e.category && e.category !== "none" ? sn[e.category] || e.category : "";
1766
1788
  return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
1767
1789
  }
1768
- var sn = [
1790
+ var ln = [
1769
1791
  "씨발",
1770
1792
  "시발",
1771
1793
  "시이발",
@@ -1791,10 +1813,10 @@ var sn = [
1791
1813
  "bitch",
1792
1814
  "asshole"
1793
1815
  ];
1794
- function cn(e) {
1816
+ function un(e) {
1795
1817
  return new RegExp(e.join("|"), "i");
1796
1818
  }
1797
- var ln = /* @__PURE__ */ new Set([
1819
+ var dn = /* @__PURE__ */ new Set([
1798
1820
  "none",
1799
1821
  "abuse",
1800
1822
  "spam",
@@ -1802,7 +1824,7 @@ var ln = /* @__PURE__ */ new Set([
1802
1824
  "violence",
1803
1825
  "other"
1804
1826
  ]);
1805
- function un(e) {
1827
+ function fn(e) {
1806
1828
  let t = e.trim();
1807
1829
  if (!t) return null;
1808
1830
  let n;
@@ -1820,7 +1842,7 @@ function un(e) {
1820
1842
  if (typeof n != "object" || !n) return null;
1821
1843
  let r = n;
1822
1844
  if (typeof r.flagged != "boolean") return null;
1823
- let i = typeof r.category == "string" && ln.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1845
+ let i = typeof r.category == "string" && dn.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1824
1846
  return {
1825
1847
  flagged: r.flagged,
1826
1848
  category: i,
@@ -1829,20 +1851,20 @@ function un(e) {
1829
1851
  }
1830
1852
  //#endregion
1831
1853
  //#region src/composables/useMemiBoardModeration.ts
1832
- var dn = {
1854
+ var pn = {
1833
1855
  flagged: !1,
1834
1856
  category: "none",
1835
1857
  reason: "",
1836
1858
  via: "empty"
1837
1859
  };
1838
- function fn() {
1839
- let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = et(), a = cn([...sn, ...e.localBlocklist ?? []]);
1860
+ function mn() {
1861
+ let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = tt(), a = un([...ln, ...e.localBlocklist ?? []]);
1840
1862
  function o(e) {
1841
1863
  let t = e.match(a);
1842
1864
  return t ? {
1843
1865
  flagged: !0,
1844
1866
  category: "abuse",
1845
- reason: on({
1867
+ reason: cn({
1846
1868
  via: "local",
1847
1869
  localHit: t[0] || "",
1848
1870
  category: "abuse"
@@ -1857,7 +1879,7 @@ function fn() {
1857
1879
  });
1858
1880
  return Te(i, {
1859
1881
  model: n,
1860
- systemInstruction: rn,
1882
+ systemInstruction: on,
1861
1883
  generationConfig: {
1862
1884
  temperature: 0,
1863
1885
  maxOutputTokens: 256,
@@ -1886,7 +1908,7 @@ function fn() {
1886
1908
  };
1887
1909
  }
1888
1910
  function l(e, t = "") {
1889
- let n = on({
1911
+ let n = cn({
1890
1912
  reason: e.reason,
1891
1913
  category: e.category,
1892
1914
  via: e.via
@@ -1914,22 +1936,22 @@ function fn() {
1914
1936
  };
1915
1937
  let i = t.trim();
1916
1938
  if (!i) return {
1917
- ...dn,
1939
+ ...pn,
1918
1940
  via: "empty"
1919
1941
  };
1920
1942
  let a = o(i);
1921
1943
  if (a) return d(a);
1922
1944
  if (e.enabled === !1) return {
1923
- ...dn,
1945
+ ...pn,
1924
1946
  via: "disabled"
1925
1947
  };
1926
1948
  try {
1927
- let e = un((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
1949
+ let e = fn((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
1928
1950
  if (!e) return c();
1929
1951
  let t = {
1930
1952
  flagged: e.flagged,
1931
1953
  category: e.category,
1932
- reason: on({
1954
+ reason: cn({
1933
1955
  reason: e.reason,
1934
1956
  category: e.category,
1935
1957
  via: "ai"
@@ -1949,14 +1971,14 @@ function fn() {
1949
1971
  via: "restricted"
1950
1972
  };
1951
1973
  if (e.enabled === !1 || !e.moderateImages) return {
1952
- ...dn,
1974
+ ...pn,
1953
1975
  via: "disabled"
1954
1976
  };
1955
1977
  try {
1956
1978
  let e = await new Promise((e, n) => {
1957
1979
  let r = new FileReader();
1958
1980
  r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
1959
- }), n = un((await s().generateContent([{ inlineData: {
1981
+ }), n = fn((await s().generateContent([{ inlineData: {
1960
1982
  mimeType: t.type || "image/jpeg",
1961
1983
  data: e
1962
1984
  } }, "이 이미지를 심사해 주세요."])).response.text());
@@ -1964,7 +1986,7 @@ function fn() {
1964
1986
  let r = {
1965
1987
  flagged: n.flagged,
1966
1988
  category: n.category,
1967
- reason: on({
1989
+ reason: cn({
1968
1990
  reason: n.reason,
1969
1991
  category: n.category,
1970
1992
  via: "ai"
@@ -1983,7 +2005,7 @@ function fn() {
1983
2005
  }
1984
2006
  //#endregion
1985
2007
  //#region src/composables/useMemiBoardUsers.ts
1986
- var pn = [
2008
+ var hn = [
1987
2009
  {
1988
2010
  value: "admin",
1989
2011
  label: "관리자",
@@ -2000,10 +2022,10 @@ var pn = [
2000
2022
  description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
2001
2023
  }
2002
2024
  ];
2003
- function mn(e = {}) {
2025
+ function gn(e = {}) {
2004
2026
  let t = () => _(), n = Q(), r = M(() => F(e.enabled) ?? !0 ? h(n, t()) : null), i = se(r, { ssrKey: w(t(), "users-management") });
2005
2027
  async function a(e, r) {
2006
- if (!pn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2028
+ if (!hn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2007
2029
  await J(x(n, t(), e), {
2008
2030
  role: r,
2009
2031
  updatedAt: G()
@@ -2017,7 +2039,7 @@ function mn(e = {}) {
2017
2039
  }
2018
2040
  //#endregion
2019
2041
  //#region src/composables/useMemiBoardUserProfile.ts
2020
- function hn(e) {
2042
+ function _n(e) {
2021
2043
  let t = Q(), n = () => _(), r = M(() => F(e)), i = M(() => {
2022
2044
  let e = r.value;
2023
2045
  return e ? x(t, n(), e) : null;
@@ -2029,7 +2051,7 @@ function hn(e) {
2029
2051
  }
2030
2052
  //#endregion
2031
2053
  //#region src/utils/section.ts
2032
- var gn = [
2054
+ var vn = [
2033
2055
  3,
2034
2056
  4,
2035
2057
  5,
@@ -2038,7 +2060,7 @@ var gn = [
2038
2060
  8,
2039
2061
  9,
2040
2062
  12
2041
- ], _n = [
2063
+ ], yn = [
2042
2064
  {
2043
2065
  value: "list",
2044
2066
  label: "게시목록"
@@ -2051,24 +2073,24 @@ var gn = [
2051
2073
  value: "comments",
2052
2074
  label: "댓글모음"
2053
2075
  }
2054
- ], vn = [{
2076
+ ], bn = [{
2055
2077
  value: "latest",
2056
2078
  label: "최신순"
2057
2079
  }, {
2058
2080
  value: "likes",
2059
2081
  label: "좋아요순"
2060
2082
  }];
2061
- function yn(e) {
2083
+ function xn(e) {
2062
2084
  return e === "list" ? 5 : 4;
2063
2085
  }
2064
- function bn(e, t) {
2086
+ function Sn(e, t) {
2065
2087
  let n = Math.floor(Number(t));
2066
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : yn(e);
2088
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : xn(e);
2067
2089
  }
2068
- function xn(e) {
2090
+ function Cn(e) {
2069
2091
  return e === 12 ? null : 12 - e;
2070
2092
  }
2071
- function Sn(e) {
2093
+ function wn(e) {
2072
2094
  return {
2073
2095
  3: "col-span-12 md:col-span-3",
2074
2096
  4: "col-span-12 md:col-span-4",
@@ -2080,36 +2102,36 @@ function Sn(e) {
2080
2102
  12: "col-span-12"
2081
2103
  }[e];
2082
2104
  }
2083
- function Cn(e) {
2105
+ function Tn(e) {
2084
2106
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2085
2107
  }
2086
- function wn(e) {
2108
+ function En(e) {
2087
2109
  let t = e.boardId || e.category || "free";
2088
2110
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2089
2111
  }
2090
- function Tn(e) {
2112
+ function Dn(e) {
2091
2113
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2092
2114
  }
2093
- function En(e) {
2115
+ function On(e) {
2094
2116
  if (!(!e.boardId || !e.postId)) return `/board/${e.boardId}/post/${e.postId}`;
2095
2117
  }
2096
- function Dn(e) {
2118
+ function kn(e) {
2097
2119
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2098
2120
  }
2099
- function On(e) {
2121
+ function An(e) {
2100
2122
  return e.title?.trim() || e.summary || "게시물";
2101
2123
  }
2102
- function kn(e) {
2124
+ function jn(e) {
2103
2125
  return e === "image" || e === "video" ? "media" : "list";
2104
2126
  }
2105
- function An(e) {
2106
- return kn(e) === "media" ? "미디어" : "목록";
2127
+ function Mn(e) {
2128
+ return jn(e) === "media" ? "미디어" : "목록";
2107
2129
  }
2108
- function jn(e, t) {
2130
+ function Nn(e, t) {
2109
2131
  return e ? t(e)?.listView ?? "default" : "default";
2110
2132
  }
2111
- function Mn(e, t) {
2112
- let n = e.map((e) => jn(e.boardId, t));
2133
+ function Pn(e, t) {
2134
+ let n = e.map((e) => Nn(e.boardId, t));
2113
2135
  if (!n.length) return "default";
2114
2136
  if (n.every((e) => e === n[0])) return n[0];
2115
2137
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2117,27 +2139,27 @@ function Mn(e, t) {
2117
2139
  }
2118
2140
  //#endregion
2119
2141
  //#region src/composables/useMemiBoardSections.ts
2120
- var Nn = [];
2121
- function Pn(e) {
2142
+ var Fn = [];
2143
+ function In(e) {
2122
2144
  let t = Number(e.cols);
2123
- if (gn.includes(t)) return t;
2145
+ if (vn.includes(t)) return t;
2124
2146
  let n = e.width;
2125
2147
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2126
2148
  }
2127
- function Fn(e) {
2149
+ function Ln(e) {
2128
2150
  let t = e.height ?? e.size;
2129
2151
  return t === "sm" || t === "lg" ? t : "md";
2130
2152
  }
2131
- function In(e) {
2153
+ function Rn(e) {
2132
2154
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2133
2155
  }
2134
- function Ln(e) {
2156
+ function zn(e) {
2135
2157
  return e.sort === "likes" ? "likes" : "latest";
2136
2158
  }
2137
- function Rn(e) {
2159
+ function Bn(e) {
2138
2160
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2139
2161
  }
2140
- function zn(e, t) {
2162
+ function Vn(e, t) {
2141
2163
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2142
2164
  for (let e of n) {
2143
2165
  let n = String(e || "").trim();
@@ -2145,40 +2167,40 @@ function zn(e, t) {
2145
2167
  }
2146
2168
  return r;
2147
2169
  }
2148
- function Bn(e, t) {
2149
- let n = In(e), r = bn(n, e.count);
2170
+ function Hn(e, t) {
2171
+ let n = Rn(e), r = Sn(n, e.count);
2150
2172
  return {
2151
2173
  id: String(e.id || `box-${t + 1}`),
2152
2174
  title: String(e.title || `섹션 ${t + 1}`),
2153
2175
  showTitle: e.showTitle !== !1,
2154
2176
  kind: n,
2155
- boardId: Rn(e),
2177
+ boardId: Bn(e),
2156
2178
  count: r,
2157
- postIds: zn(e, r),
2158
- sort: Ln(e),
2159
- cols: Pn(e),
2160
- height: Fn(e),
2179
+ postIds: Vn(e, r),
2180
+ sort: zn(e),
2181
+ cols: In(e),
2182
+ height: Ln(e),
2161
2183
  order: t
2162
2184
  };
2163
2185
  }
2164
- function Vn() {
2165
- let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Nn).map((e, t) => Bn(e, t)));
2186
+ function Un() {
2187
+ let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Fn).map((e, t) => Hn(e, t)));
2166
2188
  async function a(e) {
2167
2189
  await K(t, {
2168
2190
  columns: 12,
2169
2191
  blocks: e.map((e, t) => {
2170
- let n = e, r = In(n), i = bn(r, e.count);
2192
+ let n = e, r = Rn(n), i = Sn(r, e.count);
2171
2193
  return {
2172
2194
  id: e.id,
2173
2195
  title: e.title.trim() || `섹션 ${t + 1}`,
2174
2196
  showTitle: e.showTitle !== !1,
2175
2197
  kind: r,
2176
- boardId: Rn(n),
2198
+ boardId: Bn(n),
2177
2199
  count: i,
2178
- postIds: zn(n, i),
2179
- sort: Ln(n),
2180
- cols: Pn(n),
2181
- height: Fn(n),
2200
+ postIds: Vn(n, i),
2201
+ sort: zn(n),
2202
+ cols: In(n),
2203
+ height: Ln(n),
2182
2204
  order: t
2183
2205
  };
2184
2206
  }),
@@ -2197,7 +2219,7 @@ function Vn() {
2197
2219
  }
2198
2220
  //#endregion
2199
2221
  //#region src/composables/useMemiBoardMiniPosts.ts
2200
- function Hn(e, t) {
2222
+ function Wn(e, t) {
2201
2223
  let n = Q(), r = () => _(), i = M(() => {
2202
2224
  if (!import.meta.client) return null;
2203
2225
  let i = Math.max(1, Math.floor(Number(F(t)) || 5)), a = F(e)?.trim() || "", o = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
@@ -2209,7 +2231,7 @@ function Hn(e, t) {
2209
2231
  error: s
2210
2232
  };
2211
2233
  }
2212
- function Un(e) {
2234
+ function Gn(e) {
2213
2235
  let t = Q(), n = () => _(), r = M(() => [...new Set(F(e).map((e) => e.trim()).filter(Boolean))].slice(0, 10)), i = M(() => !import.meta.client || !r.value.length ? null : W(c(t, n()), Y(L(), "in", r.value))), { data: a, pending: o, error: s } = se(i);
2214
2236
  return {
2215
2237
  posts: M(() => {
@@ -2226,7 +2248,19 @@ function Un(e) {
2226
2248
  }
2227
2249
  //#endregion
2228
2250
  //#region src/data/versionHistory.ts
2229
- var Wn = [
2251
+ var Kn = [
2252
+ {
2253
+ version: "0.38.1",
2254
+ date: "2026-08-21",
2255
+ highlights: ["내가 쓴 글을 지울 때 첨부 사진 때문에 막히던 문제를 고쳤어요"],
2256
+ note: "호스트는 Storage 예시 규칙의 memiBoardPosts/{postId}/{allPaths=**} 목록 블록을 넣고 배포해야 해요."
2257
+ },
2258
+ {
2259
+ version: "0.38.0",
2260
+ date: "2026-08-21",
2261
+ highlights: ["숨김 게시판에는 댓글을 거의 쓰지 않아요. 필요하면 담당 스태프만 남길 수 있어요", "숨김 게시판 댓글은 공개 댓글 모음에 나오지 않아요"],
2262
+ note: "호스트는 Firestore 규칙·댓글 listed 인덱스를 다시 배포해야 해요. 공개 보드는 한 번 저장하면 예전 댓글이 목록에 다시 채워져요."
2263
+ },
2230
2264
  {
2231
2265
  version: "0.36.0",
2232
2266
  date: "2026-08-19",
@@ -2459,7 +2493,7 @@ var Wn = [
2459
2493
  highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
2460
2494
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
2461
2495
  }
2462
- ], Gn = /* @__PURE__ */ i(((e, t) => {
2496
+ ], qn = /* @__PURE__ */ i(((e, t) => {
2463
2497
  (function(n, r) {
2464
2498
  typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs = r();
2465
2499
  })(e, (function() {
@@ -2753,7 +2787,7 @@ var Wn = [
2753
2787
  return w(1e3 * e);
2754
2788
  }, w.en = b[y], w.Ls = b, w.p = {}, w;
2755
2789
  }));
2756
- })), Kn = /* @__PURE__ */ i(((e, t) => {
2790
+ })), Jn = /* @__PURE__ */ i(((e, t) => {
2757
2791
  (function(n, r) {
2758
2792
  typeof e == "object" && t !== void 0 ? t.exports = r() : typeof define == "function" && define.amd ? define(r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_plugin_relativeTime = r();
2759
2793
  })(e, (function() {
@@ -2857,9 +2891,9 @@ var Wn = [
2857
2891
  };
2858
2892
  };
2859
2893
  }));
2860
- })), qn = /* @__PURE__ */ i(((e, t) => {
2894
+ })), Yn = /* @__PURE__ */ i(((e, t) => {
2861
2895
  (function(n, r) {
2862
- typeof e == "object" && t !== void 0 ? t.exports = r(Gn()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
2896
+ typeof e == "object" && t !== void 0 ? t.exports = r(qn()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
2863
2897
  })(e, (function(e) {
2864
2898
  function t(e) {
2865
2899
  return e && typeof e == "object" && "default" in e ? e : { default: e };
@@ -2907,24 +2941,24 @@ var Wn = [
2907
2941
  };
2908
2942
  return n.default.locale(r, null, !0), r;
2909
2943
  }));
2910
- })), Jn = /* @__PURE__ */ a(Gn(), 1), Yn = /* @__PURE__ */ a(Kn(), 1);
2911
- qn(), Jn.default.extend(Yn.default), Jn.default.locale("ko");
2912
- function Xn(e) {
2944
+ })), Xn = /* @__PURE__ */ a(qn(), 1), Zn = /* @__PURE__ */ a(Jn(), 1);
2945
+ Yn(), Xn.default.extend(Zn.default), Xn.default.locale("ko");
2946
+ function Qn(e) {
2913
2947
  return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
2914
2948
  }
2915
- function Zn(e, t = Date.now()) {
2916
- return e?.toDate ? (0, Jn.default)(e.toDate()).from((0, Jn.default)(t)) : "방금 전";
2949
+ function $n(e, t = Date.now()) {
2950
+ return e?.toDate ? (0, Xn.default)(e.toDate()).from((0, Xn.default)(t)) : "방금 전";
2917
2951
  }
2918
- function Qn(e) {
2919
- return e?.toDate ? (0, Jn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2952
+ function er(e) {
2953
+ return e?.toDate ? (0, Xn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2920
2954
  }
2921
- function $n(e, t) {
2922
- let n = Qn(e), r = e?.toMillis?.(), i = t?.toMillis?.();
2923
- return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${Qn(t)}`];
2955
+ function tr(e, t) {
2956
+ let n = er(e), r = e?.toMillis?.(), i = t?.toMillis?.();
2957
+ return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${er(t)}`];
2924
2958
  }
2925
2959
  //#endregion
2926
2960
  //#region src/composables/useMemiBoardWritingAssistant.ts
2927
- var er = 10, tr = 864e5, nr = {
2961
+ var nr = 10, rr = 864e5, ir = {
2928
2962
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
2929
2963
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
2930
2964
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -2933,13 +2967,13 @@ var er = 10, tr = 864e5, nr = {
2933
2967
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
2934
2968
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
2935
2969
  };
2936
- function rr(e) {
2970
+ function ar(e) {
2937
2971
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
2938
2972
  }
2939
- function ir(e) {
2973
+ function or(e) {
2940
2974
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
2941
2975
  }
2942
- function ar() {
2976
+ function sr() {
2943
2977
  let e = le(), t = Q(), n = Z(), r = u();
2944
2978
  async function i() {
2945
2979
  let e = n.value?.uid;
@@ -2947,9 +2981,9 @@ function ar() {
2947
2981
  await oe(t, async (n) => {
2948
2982
  let r = x(t, _(), e), i = await n.get(r);
2949
2983
  if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
2950
- let a = i.data(), o = ir(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < tr;
2984
+ let a = i.data(), o = or(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < rr;
2951
2985
  if (l && s >= 10) {
2952
- let e = o + tr, t = Math.max(1, Math.ceil((e - c) / 36e5));
2986
+ let e = o + rr, t = Math.max(1, Math.ceil((e - c) / 36e5));
2953
2987
  throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
2954
2988
  }
2955
2989
  n.update(r, {
@@ -2978,10 +3012,10 @@ function ar() {
2978
3012
  }), s = t.content.trim();
2979
3013
  if (!s) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
2980
3014
  let c = [
2981
- `작업: ${nr[t.action]}`,
3015
+ `작업: ${ir[t.action]}`,
2982
3016
  t.title ? `현재 제목: ${t.title}` : "",
2983
3017
  `본문:\n${s.slice(0, 12e3)}`
2984
- ].filter(Boolean).join("\n\n"), l = rr((await o.generateContent(c)).response.text());
3018
+ ].filter(Boolean).join("\n\n"), l = ar((await o.generateContent(c)).response.text());
2985
3019
  if (!l) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
2986
3020
  return l;
2987
3021
  }
@@ -2989,14 +3023,14 @@ function ar() {
2989
3023
  }
2990
3024
  //#endregion
2991
3025
  //#region src/utils/renderMarkdown.ts
2992
- function or(e) {
3026
+ function cr(e) {
2993
3027
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2994
3028
  }
2995
- function sr(e) {
3029
+ function lr(e) {
2996
3030
  let t = e;
2997
3031
  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;
2998
3032
  }
2999
- function cr(e) {
3033
+ function ur(e) {
3000
3034
  if (!e?.trim()) return "";
3001
3035
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3002
3036
  function c() {
@@ -3005,7 +3039,7 @@ function cr(e) {
3005
3039
  return;
3006
3040
  }
3007
3041
  let e = o;
3008
- n.push(`<${e}>${s.map((e) => `<li>${sr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3042
+ n.push(`<${e}>${s.map((e) => `<li>${lr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3009
3043
  }
3010
3044
  for (; r < t.length;) {
3011
3045
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3014,7 +3048,7 @@ function cr(e) {
3014
3048
  continue;
3015
3049
  }
3016
3050
  if (i) {
3017
- a.push(or(e)), r++;
3051
+ a.push(cr(e)), r++;
3018
3052
  continue;
3019
3053
  }
3020
3054
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3025,24 +3059,24 @@ function cr(e) {
3025
3059
  if (u) {
3026
3060
  c();
3027
3061
  let e = u[1].length;
3028
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${sr(or(u[2]))}</h${e}>`), r++;
3062
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${lr(cr(u[2]))}</h${e}>`), r++;
3029
3063
  continue;
3030
3064
  }
3031
3065
  if (e.startsWith("> ")) {
3032
3066
  c();
3033
3067
  let e = [];
3034
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(or((t[r] ?? "").slice(2))), r++;
3035
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${sr(e.join("<br />"))}</blockquote>`);
3068
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(cr((t[r] ?? "").slice(2))), r++;
3069
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${lr(e.join("<br />"))}</blockquote>`);
3036
3070
  continue;
3037
3071
  }
3038
3072
  let d = e.match(/^[-*+]\s+(.+)$/);
3039
3073
  if (d) {
3040
- o && o !== "ul" && c(), o = "ul", s.push(or(d[1])), r++;
3074
+ o && o !== "ul" && c(), o = "ul", s.push(cr(d[1])), r++;
3041
3075
  continue;
3042
3076
  }
3043
3077
  let f = e.match(/^\d+\.\s+(.+)$/);
3044
3078
  if (f) {
3045
- o && o !== "ol" && c(), o = "ol", s.push(or(f[1])), r++;
3079
+ o && o !== "ol" && c(), o = "ol", s.push(cr(f[1])), r++;
3046
3080
  continue;
3047
3081
  }
3048
3082
  if (!e.trim()) {
@@ -3054,13 +3088,13 @@ function cr(e) {
3054
3088
  for (; r < t.length;) {
3055
3089
  let e = t[r] ?? "";
3056
3090
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3057
- p.push(or(e)), r++;
3091
+ p.push(cr(e)), r++;
3058
3092
  }
3059
- n.push(`<p class="my-2 leading-relaxed">${sr(p.join("<br />"))}</p>`);
3093
+ n.push(`<p class="my-2 leading-relaxed">${lr(p.join("<br />"))}</p>`);
3060
3094
  }
3061
3095
  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");
3062
3096
  }
3063
3097
  //#endregion
3064
- export { Rt as AUTHOR_MEMO_MAX_LENGTH, gn as BOARD_SECTION_COLS, _n as BOARD_SECTION_KINDS, vn as BOARD_SECTION_SORTS, pn as BOARD_USER_ROLES, Ut as COMMENT_BODY_MAX_LENGTH, Ke as DEFAULT_BLOCK_BAN_DECAY_MS, Ge as DEFAULT_BLOCK_BAN_THRESHOLD, xt as DEFAULT_BOARDS, St as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Xt as REPORT_DETAIL_MAX_LENGTH, Zt as REPORT_REASONS, er as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, bt as buildPostPreview, Ve as canManageBoardByRole, bn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, yn as defaultSectionCount, qe as effectiveModerationBlockCount, tt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Xn as formatDate, Qn as formatFullDate, Zn as formatRelativeDate, Xe as formatRestrictedUntilLabel, $n as formatTimestampDetails, ht as hasBodyImage, mt as hasBodyText, Be as isAssignedStaff, gt as isContentEmpty, Et as isFirestorePermissionDenied, Ye as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Jt as memiBoardCommentLikesKey, jn as miniBoardView, En as miniCommentLink, Tn as miniMoreLink, Dn as miniPostImage, wn as miniPostLink, On as miniPostTitle, Mn as miniPostsView, kn as miniViewGroup, An as miniViewLabel, Je as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Bn as normalizeBoardSection, xn as pairSectionCols, ft as plainTextFromHtml, s as postBodyDoc, t as postDoc, rt as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, cr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, Qt as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, Sn as sectionColClass, Cn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, nt as storagePathFromDownloadUrl, pt as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, et as useMemiBoardAuth, Bt as useMemiBoardAuthorMemo, Lt as useMemiBoardCommentFeed, Yt as useMemiBoardCommentLikes, Gt as useMemiBoardComments, u as useMemiBoardConfig, qt as useMemiBoardLikes, Hn as useMemiBoardMiniLatest, Un as useMemiBoardMiniPicked, fn as useMemiBoardModeration, jt as useMemiBoardPost, At as useMemiBoardPostList, Ot as useMemiBoardPosts, Kt as useMemiBoardReplies, tn as useMemiBoardReportQueue, $t as useMemiBoardReports, Vn as useMemiBoardSections, Tt as useMemiBoardSettings, Ft as useMemiBoardUserComments, Nt as useMemiBoardUserPosts, hn as useMemiBoardUserProfile, mn as useMemiBoardUsers, nn as useMemiBoardViews, ar as useMemiBoardWritingAssistant, Wn as versionHistory, yt as videoListCoverUrl, vt as youtubeId };
3098
+ export { Bt as AUTHOR_MEMO_MAX_LENGTH, vn as BOARD_SECTION_COLS, yn as BOARD_SECTION_KINDS, bn as BOARD_SECTION_SORTS, hn as BOARD_USER_ROLES, Gt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, Ct as DEFAULT_BOARDS, wt as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Qt as REPORT_DETAIL_MAX_LENGTH, $t as REPORT_REASONS, nr as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, St as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, Sn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, xn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Qn as formatDate, er as formatFullDate, $n as formatRelativeDate, Ze as formatRestrictedUntilLabel, tr as formatTimestampDetails, _t as hasBodyImage, gt as hasBodyText, Be as isAssignedStaff, vt as isContentEmpty, Ot as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Xt as memiBoardCommentLikesKey, Nn as miniBoardView, On as miniCommentLink, Dn as miniMoreLink, kn as miniPostImage, En as miniPostLink, An as miniPostTitle, Pn as miniPostsView, jn as miniViewGroup, Mn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Hn as normalizeBoardSection, Cn as pairSectionCols, mt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, ur as renderMarkdownToHtml, r as reportDoc, n as reportDocId, en as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, wn as sectionColClass, Tn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, ht as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Ht as useMemiBoardAuthorMemo, zt as useMemiBoardCommentFeed, Zt as useMemiBoardCommentLikes, qt as useMemiBoardComments, u as useMemiBoardConfig, Yt as useMemiBoardLikes, Wn as useMemiBoardMiniLatest, Gn as useMemiBoardMiniPicked, mn as useMemiBoardModeration, Nt as useMemiBoardPost, Mt as useMemiBoardPostList, At as useMemiBoardPosts, Jt as useMemiBoardReplies, rn as useMemiBoardReportQueue, tn as useMemiBoardReports, Un as useMemiBoardSections, Dt as useMemiBoardSettings, Lt as useMemiBoardUserComments, Ft as useMemiBoardUserPosts, _n as useMemiBoardUserProfile, gn as useMemiBoardUsers, an as useMemiBoardViews, sr as useMemiBoardWritingAssistant, Kn as versionHistory, xt as videoListCoverUrl, bt as youtubeId };
3065
3099
 
3066
3100
  //# sourceMappingURL=index.js.map