memi-board 0.37.2 → 0.38.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
@@ -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,104 @@ 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) {
437
+ async function at(e) {
432
438
  let t = await be(e);
433
- await Promise.all([...t.items.map((e) => ve(e)), ...t.prefixes.map((e) => it(e))]);
439
+ await Promise.all([...t.items.map((e) => ve(e)), ...t.prefixes.map((e) => at(e))]);
434
440
  }
435
- async function at(e, t) {
441
+ async function ot(e, t) {
436
442
  let n = [...new Set([...t].filter(Boolean))];
437
443
  await Promise.all(n.map((t) => ve(xe(e, t)).catch(() => {})));
438
444
  }
439
- var ot = 450;
440
- async function st(e, t) {
445
+ var st = 450;
446
+ async function ct(e, t) {
441
447
  let n = await z(t);
442
- for (let t = 0; t < n.docs.length; t += ot) {
448
+ for (let t = 0; t < n.docs.length; t += st) {
443
449
  let r = X(e);
444
- n.docs.slice(t, t + ot).forEach((e) => r.delete(e.ref)), await r.commit();
450
+ n.docs.slice(t, t + st).forEach((e) => r.delete(e.ref)), await r.commit();
445
451
  }
446
452
  }
447
- async function ct(e, t, n) {
448
- await st(e, W(A(e, t), Y("postId", "==", n)));
449
- }
450
453
  async function lt(e, t, n) {
451
- await st(e, W(o(e, t), Y("postId", "==", n)));
454
+ await ct(e, W(A(e, t), Y("postId", "==", n)));
452
455
  }
453
456
  async function ut(e, t, n) {
454
- await st(e, W(A(e, t), Y("commentId", "==", n)));
457
+ await ct(e, W(o(e, t), Y("postId", "==", n)));
458
+ }
459
+ async function dt(e, t, n) {
460
+ await ct(e, W(A(e, t), Y("commentId", "==", n)));
455
461
  }
456
- async function dt(e, n, r, i) {
462
+ async function ft(e, n, r, i) {
457
463
  let a = t(e, r, i), o = s(e, r, i), [c, l] = await Promise.all([R(a), R(o)]);
458
464
  if (!c.exists()) return;
459
465
  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);
466
+ await ct(e, W(O(e, r), Y("postId", "==", i))), await lt(e, r, i), await ut(e, r, i);
461
467
  let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set();
462
468
  for (let e of u.attachments ?? []) {
463
469
  if (!e?.path) continue;
464
470
  f.add(e.path);
465
- let t = rt(e.path);
471
+ let t = it(e.path);
466
472
  t && t !== i && p.add(t);
467
473
  }
468
- for (let e of tt(d)) {
469
- let t = nt(e);
474
+ for (let e of nt(d)) {
475
+ let t = rt(e);
470
476
  if (!t) continue;
471
477
  f.add(t);
472
478
  let n = t.replace(/\/images\/([^/]+)$/, "/images/thumbnails/$1").replace(/\.[^.]+$/, ".jpg");
473
479
  n !== t && n.includes("/images/thumbnails/") && f.add(n);
474
480
  let r = t.match(/^(.*\/images\/)([^/]+)\.[^.]+$/);
475
481
  r && !t.includes("/thumbnails/") && f.add(`${r[1]}thumbnails/${r[2]}.jpg`);
476
- let a = rt(t);
482
+ let a = it(t);
477
483
  a && a !== i && p.add(a);
478
484
  }
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);
485
+ await te(o), await at(xe(n, k(r, i)));
486
+ for (let e of p) await at(xe(n, k(r, e)));
487
+ await ot(n, f), await te(a);
482
488
  }
483
489
  //#endregion
484
490
  //#region src/utils/postBody.ts
485
- function ft(e) {
491
+ function pt(e) {
486
492
  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
493
  }
488
- function pt(e, t = 40) {
489
- let n = ft(e);
494
+ function mt(e, t = 40) {
495
+ let n = pt(e);
490
496
  if (!n) return "";
491
497
  if (n.length <= t) return n;
492
498
  let r = n.slice(0, t), i = r.lastIndexOf(" ");
493
499
  return `${(i >= Math.floor(t * .6) ? r.slice(0, i) : r).trimEnd()}…`;
494
500
  }
495
- function mt(e) {
496
- return ft(e).length > 0;
501
+ function ht(e) {
502
+ return pt(e).length > 0;
497
503
  }
498
- function ht(e, t) {
504
+ function gt(e, t) {
499
505
  return /<img\b/i.test(String(e || "")) ? !0 : (t || []).some((e) => !!e?.type?.startsWith("image/"));
500
506
  }
501
- function gt(e) {
502
- return !mt(e);
507
+ function _t(e) {
508
+ return !ht(e);
503
509
  }
504
510
  //#endregion
505
511
  //#region src/utils/postPreview.ts
506
- function _t(e) {
512
+ function vt(e) {
507
513
  return e.replace(/&amp;/g, "&").replace(/&quot;/g, "\"").replace(/&#39;/g, "'");
508
514
  }
509
- function vt(e) {
515
+ function yt(e) {
510
516
  if (e) try {
511
- let t = new URL(_t(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
517
+ let t = new URL(vt(e)), n = t.hostname.toLowerCase().replace(/^www\./, "");
512
518
  if (n === "youtu.be") return t.pathname.split("/").filter(Boolean)[0];
513
519
  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
520
  } catch {
515
521
  return;
516
522
  }
517
523
  }
518
- function yt(e) {
519
- let t = vt(e.videoUrl);
524
+ function bt(e) {
525
+ let t = yt(e.videoUrl);
520
526
  return t ? `https://i.ytimg.com/vi/${t}/hqdefault.jpg` : e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
521
527
  }
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);
528
+ function xt(e, t = []) {
529
+ 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 ? vt(r[1]) : a, s = o ? yt(o) : void 0, c = n ? vt(n[1]) : i || (s ? `https://i.ytimg.com/vi/${s}/hqdefault.jpg` : void 0);
524
530
  return {
525
531
  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
532
  ...c ? { previewImage: c } : {},
@@ -529,7 +535,7 @@ function bt(e, t = []) {
529
535
  }
530
536
  //#endregion
531
537
  //#region src/composables/useMemiBoardSettings.ts
532
- var xt = [
538
+ var St = [
533
539
  {
534
540
  id: "free",
535
541
  label: "자유",
@@ -557,25 +563,25 @@ var xt = [
557
563
  visibility: "public",
558
564
  order: 2
559
565
  }
560
- ], St = xt;
561
- function Ct(e) {
566
+ ], Ct = St;
567
+ function wt(e) {
562
568
  return e === "hidden" ? "hidden" : "public";
563
569
  }
564
- function wt(e, t, n) {
570
+ function Tt(e, t, n) {
565
571
  return {
566
572
  id: e,
567
573
  label: typeof t.label == "string" && t.label.trim() ? t.label.trim() : e,
568
574
  order: typeof t.order == "number" ? t.order : n,
569
575
  description: (typeof t.description == "string" ? t.description : "") || "",
570
- visibility: Ct(t.visibility),
576
+ visibility: wt(t.visibility),
571
577
  listView: t.listView ?? "default",
572
578
  writeRole: t.writeRole ?? "user",
573
579
  commentWriteRole: t.commentWriteRole ?? "user",
574
580
  allowedStaffUids: Array.isArray(t.allowedStaffUids) ? t.allowedStaffUids : []
575
581
  };
576
582
  }
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;
583
+ function Et() {
584
+ 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) => Tt(e.id, e, t))), p = f, m = M(() => f.value.filter((e) => e.visibility !== "hidden")), h = m;
579
585
  function g(e) {
580
586
  if (e) return f.value.find((t) => t.id === e)?.label ?? e;
581
587
  }
@@ -592,29 +598,32 @@ function Tt() {
592
598
  return C(e) === "hidden";
593
599
  }
594
600
  let D = E;
595
- function O(e) {
601
+ function k(e) {
596
602
  if (e) return f.value.find((t) => t.id === e);
597
603
  }
598
- Ue((e) => O(e));
599
- function k(e) {
600
- return Ve(s.value?.uid, a.value, o.value, e ? O(e) : void 0);
604
+ We((e) => k(e));
605
+ function A(e) {
606
+ return Ve(s.value?.uid, a.value, o.value, e ? k(e) : void 0);
601
607
  }
602
- let A = M(() => f.value.filter((e) => k(e.id)));
603
- async function j(e, r) {
608
+ let j = M(() => f.value.filter((e) => A(e.id)));
609
+ async function N(e, t, r) {
604
610
  let i;
605
611
  for (;;) {
606
- let a = await z(W(c(n, t()), Y("boardId", "==", e), U("__name__"), ...i ? [q(i)] : [], V(400)));
612
+ let a = await z(W(e, Y("boardId", "==", t), U("__name__"), ...i ? [q(i)] : [], V(400)));
607
613
  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;
614
+ let o = X(n), s = 0;
615
+ for (let e of a.docs) e.data().listed !== r && (o.update(e.ref, { listed: r }), s++);
616
+ if (s && await o.commit(), i = a.docs[a.docs.length - 1], a.docs.length < 400) break;
611
617
  }
612
618
  }
613
- function N(e, t) {
619
+ async function ee(e, r) {
620
+ await N(c(n, t()), e, r), await N(O(n, t()), e, r);
621
+ }
622
+ function P(e, t) {
614
623
  return {
615
624
  label: e.label.trim(),
616
625
  description: (e.description ?? "").trim(),
617
- visibility: Ct(e.visibility),
626
+ visibility: wt(e.visibility),
618
627
  listView: e.listView ?? "default",
619
628
  writeRole: e.writeRole ?? "user",
620
629
  commentWriteRole: e.commentWriteRole ?? "user",
@@ -623,53 +632,53 @@ function Tt() {
623
632
  updatedAt: G()
624
633
  };
625
634
  }
626
- async function ee(e, r = e.order ?? 0) {
635
+ async function F(e, r = e.order ?? 0) {
627
636
  if (!i.value) throw Error("로그인이 필요합니다.");
628
637
  let a = e.id.trim(), o = e.label.trim();
629
638
  if (!a || !o) throw Error("보드 ID와 이름을 입력해 주세요.");
630
- let s = Ct(e.visibility), c = f.value.find((e) => e.id === a)?.visibility ?? "public", l = N({
639
+ let s = wt(e.visibility), c = P({
631
640
  ...e,
632
641
  label: o,
633
642
  visibility: s
634
643
  }, r);
635
- await K(v(n, t(), a), l, { merge: !0 }), (c !== s || s === "public") && await j(a, s !== "hidden");
644
+ await K(v(n, t(), a), c, { merge: !0 }), await ee(a, s !== "hidden");
636
645
  }
637
- let P = ee;
638
- async function F(e) {
646
+ let I = F;
647
+ async function ne(e) {
639
648
  if (!i.value) throw Error("로그인이 필요합니다.");
640
649
  let r = new Map(f.value.map((e) => [e.id, e.visibility ?? "public"])), a = X(n), o = [];
641
650
  e.forEach((e, i) => {
642
651
  let s = e.id.trim(), c = e.label.trim();
643
652
  if (!s || !c) throw Error("보드 ID와 이름을 입력해 주세요.");
644
- let l = Ct(e.visibility), u = N({
653
+ let l = wt(e.visibility), u = P({
645
654
  ...e,
646
655
  label: c,
647
656
  visibility: l
648
657
  }, i);
649
- a.set(v(n, t(), s), u, { merge: !0 }), (r.get(s) ?? "public") !== l && o.push({
658
+ a.set(v(n, t(), s), u, { merge: !0 }), ((r.get(s) ?? "public") !== l || l === "hidden") && o.push({
650
659
  id: s,
651
660
  listed: l !== "hidden"
652
661
  });
653
662
  }), await a.commit();
654
- for (let e of o) await j(e.id, e.listed);
663
+ for (let e of o) await ee(e.id, e.listed);
655
664
  }
656
- let I = F;
657
- async function ne(e) {
665
+ let re = ne;
666
+ async function L(e) {
658
667
  if (!i.value) throw Error("로그인이 필요합니다.");
659
668
  let a = e.trim();
660
669
  if (!a) throw Error("삭제할 게시판 ID가 필요합니다.");
661
670
  for (;;) {
662
671
  let e = await z(W(c(n, t()), Y("boardId", "==", a), V(25)));
663
672
  if (e.empty) break;
664
- for (let i of e.docs) await dt(n, ye(r), t(), i.id);
673
+ for (let i of e.docs) await ft(n, ye(r), t(), i.id);
665
674
  }
666
675
  await te(v(n, t(), e));
667
676
  }
668
- let re = ne;
669
- async function L() {
677
+ let ie = L;
678
+ async function R() {
670
679
  return Promise.resolve();
671
680
  }
672
- async function ie(e) {
681
+ async function B(e) {
673
682
  let t = e.trim();
674
683
  if (!t) throw Error("보드 이름을 입력해 주세요.");
675
684
  if (!i.value) throw Error("로그인이 필요합니다.");
@@ -677,7 +686,7 @@ function Tt() {
677
686
  if (n) return n.id;
678
687
  let r = S(t) || `board-${Date.now()}`, a = r, o = 2, s = new Set(f.value.map((e) => e.id));
679
688
  for (; s.has(a);) a = `${r}-${o++}`;
680
- return await ee({
689
+ return await F({
681
690
  id: a,
682
691
  label: t,
683
692
  visibility: "public",
@@ -694,9 +703,9 @@ function Tt() {
694
703
  boardDescription: b,
695
704
  boardVisibility: C,
696
705
  isBoardHidden: E,
697
- getBoard: O,
698
- canManageBoard: k,
699
- managedBoards: A,
706
+ getBoard: k,
707
+ canManageBoard: A,
708
+ managedBoards: j,
700
709
  categories: p,
701
710
  publicCategories: h,
702
711
  categoryLabel: y,
@@ -704,33 +713,33 @@ function Tt() {
704
713
  categoryVisibility: T,
705
714
  isCategoryHidden: D,
706
715
  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
716
+ ensureSettings: R,
717
+ addBoard: B,
718
+ addCategory: B,
719
+ saveBoard: F,
720
+ saveCategory: I,
721
+ saveBoards: ne,
722
+ saveCategories: re,
723
+ deleteBoard: L,
724
+ deleteCategory: ie,
725
+ DEFAULT_BOARDS: St,
726
+ DEFAULT_CATEGORIES: Ct
718
727
  };
719
728
  }
720
729
  //#endregion
721
730
  //#region src/composables/useMemiBoardPosts.ts
722
- function Et(e) {
731
+ function Dt(e) {
723
732
  if (!e || typeof e != "object") return !1;
724
733
  let t = "code" in e ? String(e.code) : "";
725
734
  return t === "permission-denied" || t === "firestore/permission-denied";
726
735
  }
727
- function Dt(e) {
736
+ function Ot(e) {
728
737
  let t = F(e)?.trim();
729
738
  if (!t) throw Error("[memi-board] boardId 가 필요합니다.");
730
739
  return t;
731
740
  }
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);
741
+ function kt(e) {
742
+ let n = Q(), r = le(), { isBoardHidden: i, getBoard: a } = Et(), o = () => _(), l = () => Ot(e), u = () => c(n, o()), d = (e) => t(n, o(), e), f = (e) => s(n, o(), e);
734
743
  function p() {
735
744
  return !i(l());
736
745
  }
@@ -738,7 +747,7 @@ function Ot(e) {
738
747
  return a(l())?.listView === "image";
739
748
  }
740
749
  function h(e) {
741
- let t = m(), n = mt(e.content), r = ht(e.content, e.attachments);
750
+ let t = m(), n = ht(e.content), r = gt(e.content, e.attachments);
742
751
  if (t) {
743
752
  if (!r) throw Error("사진을 올려 주세요.");
744
753
  return;
@@ -781,13 +790,13 @@ function Ot(e) {
781
790
  post: e
782
791
  } : { status: "not-found" };
783
792
  } catch (e) {
784
- return Et(e) ? { status: "permission-denied" } : {
793
+ return Dt(e) ? { status: "permission-denied" } : {
785
794
  status: "error",
786
795
  message: e instanceof Error ? e.message : String(e)
787
796
  };
788
797
  }
789
798
  } catch (e) {
790
- return Et(e) ? { status: "permission-denied" } : {
799
+ return Dt(e) ? { status: "permission-denied" } : {
791
800
  status: "error",
792
801
  message: e instanceof Error ? e.message : String(e)
793
802
  };
@@ -825,7 +834,7 @@ function Ot(e) {
825
834
  async function C(e) {
826
835
  h(e);
827
836
  let t = e.postId || g(), n = m(), r, i;
828
- if (n) r = t, i = pt(e.content);
837
+ if (n) r = t, i = mt(e.content);
829
838
  else {
830
839
  i = e.title.trim();
831
840
  let t = S(i) || "post";
@@ -833,7 +842,7 @@ function Ot(e) {
833
842
  let n = 1;
834
843
  for (; !(await z(W(u(), Y("boardId", "==", l()), Y("slug", "==", r), V(1)))).empty;) n++, r = `${t}-${n}`;
835
844
  }
836
- let a = bt(e.content, e.attachments), o = p();
845
+ let a = xt(e.content, e.attachments), o = p();
837
846
  return await K(d(t), {
838
847
  boardId: l(),
839
848
  slug: r,
@@ -857,7 +866,7 @@ function Ot(e) {
857
866
  }
858
867
  async function w(e, t) {
859
868
  h(t);
860
- let n = m() ? pt(t.content) : t.title.trim(), r = bt(t.content, t.attachments), i = p();
869
+ let n = m() ? mt(t.content) : t.title.trim(), r = xt(t.content, t.attachments), i = p();
861
870
  await Promise.all([J(d(e), {
862
871
  title: n,
863
872
  ...r,
@@ -877,7 +886,7 @@ function Ot(e) {
877
886
  });
878
887
  }
879
888
  async function E(e) {
880
- await dt(n, ye(r), o(), e);
889
+ await ft(n, ye(r), o(), e);
881
890
  }
882
891
  return {
883
892
  createPostId: g,
@@ -891,9 +900,9 @@ function Ot(e) {
891
900
  deletePost: E
892
901
  };
893
902
  }
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;
903
+ var At = 10;
904
+ function jt(e, t = {}) {
905
+ let n = Q(), { isAdmin: r, user: i, canManageBoard: a } = tt(), o = () => _(), s = t.pageSize ?? At, l = M(() => i.value?.uid ?? ""), u = M(() => Ot(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
906
  function T(e) {
898
907
  return e.createdAt?.toMillis?.() ?? 0;
899
908
  }
@@ -976,8 +985,8 @@ function At(e, t = {}) {
976
985
  loadMore: A
977
986
  };
978
987
  }
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);
988
+ function Mt(e, n) {
989
+ let r = Q(), i = () => _(), a = M(() => Ot(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
990
  return {
982
991
  post: M(() => !u.value || u.value.boardId !== a.value ? null : {
983
992
  ...u.value,
@@ -989,9 +998,9 @@ function jt(e, n) {
989
998
  }
990
999
  //#endregion
991
1000
  //#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;
1001
+ var Nt = 10;
1002
+ function Pt(e, t = {}) {
1003
+ let n = Q(), r = () => _(), i = () => c(n, r()), a = t.pageSize ?? Nt, 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
1004
  function v(e) {
996
1005
  return {
997
1006
  id: e.id,
@@ -1057,9 +1066,9 @@ function Nt(e, t = {}) {
1057
1066
  }
1058
1067
  //#endregion
1059
1068
  //#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;
1069
+ var Ft = 10;
1070
+ function It(e, t = {}) {
1071
+ let n = Q(), r = () => _(), i = () => O(n, r()), a = t.pageSize ?? Ft, 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
1072
  function g(e) {
1064
1073
  return {
1065
1074
  id: e.id,
@@ -1075,7 +1084,7 @@ function Ft(e, t = {}) {
1075
1084
  return [...e.values()].sort((e, t) => v(t) - v(e) || String(t.id).localeCompare(String(e.id)));
1076
1085
  });
1077
1086
  function b() {
1078
- return [Y("authorUid", "==", o.value)];
1087
+ return [Y("authorUid", "==", o.value), Y("listed", "==", !0)];
1079
1088
  }
1080
1089
  function x() {
1081
1090
  if (h?.(), !o.value) {
@@ -1121,11 +1130,11 @@ function Ft(e, t = {}) {
1121
1130
  }
1122
1131
  //#endregion
1123
1132
  //#region src/composables/useMemiBoardCommentFeed.ts
1124
- var It = 10;
1125
- function Lt(e = "latest", t = {}) {
1133
+ var Lt = 10;
1134
+ function Rt(e = "latest", t = {}) {
1126
1135
  let n = Q(), r = () => _(), i = () => O(n, r()), a = M(() => {
1127
1136
  let e = Math.floor(Number(F(t.pageSize)));
1128
- return Number.isFinite(e) && e > 0 ? e : It;
1137
+ return Number.isFinite(e) && e > 0 ? e : Lt;
1129
1138
  }), 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
1139
  function g(e) {
1131
1140
  return {
@@ -1150,10 +1159,15 @@ function Lt(e = "latest", t = {}) {
1150
1159
  });
1151
1160
  function x() {
1152
1161
  return o.value === "likes" ? [
1162
+ Y("listed", "==", !0),
1153
1163
  U("likeCount", "desc"),
1154
1164
  U("createdAt", "desc"),
1155
1165
  U(L(), "desc")
1156
- ] : [U("createdAt", "desc"), U(L(), "desc")];
1166
+ ] : [
1167
+ Y("listed", "==", !0),
1168
+ U("createdAt", "desc"),
1169
+ U(L(), "desc")
1170
+ ];
1157
1171
  }
1158
1172
  function S() {
1159
1173
  h?.(), h = H(W(i(), ...x(), V(a.value)), (e) => {
@@ -1190,9 +1204,9 @@ function Lt(e = "latest", t = {}) {
1190
1204
  }
1191
1205
  //#endregion
1192
1206
  //#region src/composables/useMemiBoardAuthorMemo.ts
1193
- var Rt = 100, zt = /* @__PURE__ */ new Map();
1207
+ var zt = 100, Bt = /* @__PURE__ */ new Map();
1194
1208
  function $(e) {
1195
- let t = zt.get(e);
1209
+ let t = Bt.get(e);
1196
1210
  return t || (t = ee({
1197
1211
  text: "",
1198
1212
  sentiment: null,
@@ -1200,10 +1214,10 @@ function $(e) {
1200
1214
  pending: !1,
1201
1215
  saving: !1,
1202
1216
  error: ""
1203
- }), zt.set(e, t)), t;
1217
+ }), Bt.set(e, t)), t;
1204
1218
  }
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(() => {
1219
+ function Vt(e) {
1220
+ let t = Q(), n = () => _(), { user: r } = tt(), i = M(() => F(e)), a = M(() => `${r.value?.uid ?? ""}:${i.value}`), o = M(() => {
1207
1221
  let e = r.value?.uid, a = i.value;
1208
1222
  return !e || !a ? null : m(t, n(), e, a);
1209
1223
  }), s = M({
@@ -1272,95 +1286,96 @@ function Bt(e) {
1272
1286
  }
1273
1287
  //#endregion
1274
1288
  //#region src/composables/useMemiBoardComments.ts
1275
- var Vt = 10, Ht = 5, Ut = 1e3;
1276
- function Wt(e) {
1289
+ var Ht = 10, Ut = 5, Wt = 1e3;
1290
+ function Gt(e) {
1277
1291
  let t = e.trim();
1278
1292
  if (!t) throw Error("댓글 내용을 입력해 주세요.");
1279
- if (t.length > 1e3) throw Error(`댓글은 ${Ut.toLocaleString()}자까지 작성할 수 있습니다.`);
1293
+ if (t.length > 1e3) throw Error(`댓글은 ${Wt.toLocaleString()}자까지 작성할 수 있습니다.`);
1280
1294
  return t;
1281
1295
  }
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) {
1296
+ function Kt(e, n, r = {}) {
1297
+ let i = Q(), a = () => _(), { isBoardHidden: o } = Et(), 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;
1298
+ function C(e) {
1285
1299
  return e.createdAt?.toMillis?.() ?? 0;
1286
1300
  }
1287
- let S = M(() => {
1301
+ let w = M(() => {
1288
1302
  let e = /* @__PURE__ */ new Map();
1289
1303
  for (let t of [
1290
- ...u.value,
1291
- ...d.value,
1292
- ...p.value
1304
+ ...f.value,
1305
+ ...p.value,
1306
+ ...h.value
1293
1307
  ]) 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)));
1308
+ return [...e.values()].sort((e, t) => C(e) - C(t) || String(e.id).localeCompare(String(t.id)));
1295
1309
  });
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;
1310
+ I([u, c], async ([e]) => {
1311
+ 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) {
1312
+ m.value = !0, y.value = !0;
1299
1313
  try {
1300
- let t = z(W(o(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1314
+ let t = z(W(s(), Y("postId", "==", e), U("createdAt", "desc"), U(L(), "desc")));
1301
1315
  if (await Promise.all([t.then((t) => {
1302
- if (c.value !== e) return;
1316
+ if (u.value !== e) return;
1303
1317
  let n = t.docs.map((e) => ({
1304
1318
  id: e.id,
1305
1319
  ...e.data()
1306
1320
  }));
1307
- d.value = n.filter((e) => e.parentId === void 0);
1308
- }), w()]), c.value !== e) return;
1321
+ p.value = n.filter((e) => e.parentId === void 0);
1322
+ }), E()]), u.value !== e) return;
1309
1323
  } finally {
1310
- c.value === e && (f.value = !1, g.value = !1);
1324
+ u.value === e && (m.value = !1, y.value = !1);
1311
1325
  }
1312
1326
  }
1313
1327
  }, { 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]));
1328
+ function T() {
1329
+ S?.(), S = H(W(s(), Y("postId", "==", u.value), Y("parentId", "==", null), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1330
+ let t = new Map(f.value.map((e) => [e.id, e]));
1317
1331
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1318
1332
  id: n.doc.id,
1319
1333
  ...n.doc.data()
1320
1334
  });
1321
- u.value = [...t.values()];
1335
+ f.value = [...t.values()];
1322
1336
  }, (e) => {
1323
1337
  console.error("[memi-board:comments] onSnapshot failed", e);
1324
1338
  });
1325
1339
  }
1326
- async function w() {
1327
- if (!(m.value || !h.value)) {
1328
- m.value = !0;
1340
+ async function E() {
1341
+ if (!(g.value || !v.value)) {
1342
+ g.value = !0;
1329
1343
  try {
1330
1344
  let e = [
1331
- Y("postId", "==", c.value),
1345
+ Y("postId", "==", u.value),
1332
1346
  Y("parentId", "==", null),
1333
1347
  U("createdAt", "asc"),
1334
1348
  U(L(), "asc"),
1335
- ...v ? [q(v)] : [],
1349
+ ...b ? [q(b)] : [],
1336
1350
  V(11)
1337
- ], t = await z(W(o(), ...e)), n = t.docs.slice(0, Vt), r = n.map((e) => ({
1351
+ ], t = await z(W(s(), ...e)), n = t.docs.slice(0, Ht), r = n.map((e) => ({
1338
1352
  id: e.id,
1339
1353
  ...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();
1354
+ })), i = new Set(h.value.map((e) => e.id));
1355
+ h.value.push(...r.filter((e) => !i.has(e.id))), b = n.at(-1) ?? b, t.size <= Ht && (v.value = !1, x = setTimeout(() => {
1356
+ x = void 0, T();
1343
1357
  }, 1e3));
1344
1358
  } finally {
1345
- m.value = !1;
1359
+ g.value = !1;
1346
1360
  }
1347
1361
  }
1348
1362
  }
1349
1363
  N(() => {
1350
- clearTimeout(y), b?.();
1364
+ clearTimeout(x), S?.();
1351
1365
  });
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,
1366
+ async function D(e) {
1367
+ let n = Gt(e.body), r = X(i), o = re(s());
1368
+ r.set(o, {
1369
+ postId: u.value,
1370
+ boardId: c.value,
1371
+ listed: d(),
1357
1372
  body: n,
1358
1373
  authorUid: e.authorUid,
1359
1374
  authorName: e.authorName,
1360
1375
  authorPhoto: e.authorPhoto,
1361
1376
  moderationStatus: "approved",
1362
1377
  parentId: null,
1363
- rootId: l.id,
1378
+ rootId: o.id,
1364
1379
  depth: 0,
1365
1380
  replyToUid: null,
1366
1381
  replyToName: null,
@@ -1369,25 +1384,26 @@ function Gt(e, n, r = {}) {
1369
1384
  isReply: !1,
1370
1385
  createdAt: G(),
1371
1386
  updatedAt: G()
1372
- }), r.update(t(i, a(), c.value), { commentCount: B(1) }), await r.commit();
1387
+ }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1373
1388
  }
1374
- async function E(e) {
1375
- let n = Wt(e.body);
1389
+ async function k(e) {
1390
+ let n = Gt(e.body);
1376
1391
  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,
1392
+ let r = X(i), o = re(s()), f = e.parent.parentId == null ? e.parent.id : e.parent.rootId;
1393
+ if (!f) throw Error("댓글 스레드를 찾을 수 없습니다.");
1394
+ let p = Math.min((e.parent.depth ?? 0) + 1, 2);
1395
+ r.set(o, {
1396
+ postId: u.value,
1397
+ boardId: c.value,
1398
+ listed: d(),
1383
1399
  body: n,
1384
1400
  authorUid: e.authorUid,
1385
1401
  authorName: e.authorName,
1386
1402
  authorPhoto: e.authorPhoto,
1387
1403
  moderationStatus: "approved",
1388
1404
  parentId: e.parent.id,
1389
- rootId: d,
1390
- depth: f,
1405
+ rootId: f,
1406
+ depth: p,
1391
1407
  replyToUid: e.parent.authorUid,
1392
1408
  replyToName: e.parent.authorName,
1393
1409
  replyCount: 0,
@@ -1395,23 +1411,23 @@ function Gt(e, n, r = {}) {
1395
1411
  isReply: !0,
1396
1412
  createdAt: G(),
1397
1413
  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();
1414
+ }), r.update(l(i, a(), f), { replyCount: B(1) }), r.update(t(i, a(), u.value), { commentCount: B(1) }), await r.commit();
1399
1415
  }
1400
- async function D(e) {
1416
+ async function A(e) {
1401
1417
  if (!e.id) return;
1402
1418
  if (e.parentId == null && (e.replyCount ?? 0) > 0) throw Error("답글이 있는 댓글은 현재 삭제할 수 없습니다.");
1403
- await ut(i, a(), e.id);
1419
+ await dt(i, a(), e.id);
1404
1420
  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);
1421
+ 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
1422
  }
1407
- async function k(e, t) {
1408
- let n = Wt(t);
1423
+ async function j(e, t) {
1424
+ let n = Gt(t);
1409
1425
  await J(l(i, a(), e), {
1410
1426
  body: n,
1411
1427
  updatedAt: G()
1412
1428
  });
1413
1429
  }
1414
- async function A(e, t, n) {
1430
+ async function ee(e, t, n) {
1415
1431
  await J(l(i, a(), e), {
1416
1432
  isBlinded: t,
1417
1433
  moderatedAt: G(),
@@ -1419,74 +1435,75 @@ function Gt(e, n, r = {}) {
1419
1435
  });
1420
1436
  }
1421
1437
  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
1438
+ comments: w,
1439
+ commentsPending: M(() => y.value || m.value),
1440
+ hasMore: v,
1441
+ loadingMore: g,
1442
+ loadMore: E,
1443
+ addComment: D,
1444
+ addReply: k,
1445
+ updateComment: j,
1446
+ setCommentBlinded: ee,
1447
+ deleteComment: A
1432
1448
  };
1433
1449
  }
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) {
1450
+ function qt(e, t, n) {
1451
+ 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;
1452
+ function p(e) {
1437
1453
  return e.sort((e, t) => (e.createdAt?.toMillis?.() ?? 0) - (t.createdAt?.toMillis?.() ?? 0) || String(e.id).localeCompare(String(t.id)));
1438
1454
  }
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]));
1455
+ function m() {
1456
+ f ||= H(W(a(), Y("postId", "==", o), Y("rootId", "==", n), Y("isReply", "==", !0), U("createdAt", "desc"), U(L(), "desc"), V(1)), (e) => {
1457
+ let t = new Map(s.value.map((e) => [e.id, e]));
1442
1458
  for (let n of e.docChanges()) n.type !== "removed" && t.set(n.doc.id, {
1443
1459
  id: n.doc.id,
1444
1460
  ...n.doc.data()
1445
1461
  });
1446
- o.value = f([...t.values()]);
1462
+ s.value = p([...t.values()]);
1447
1463
  }, (e) => {
1448
1464
  console.error("[memi-board:replies] onSnapshot failed", e);
1449
1465
  });
1450
1466
  }
1451
- async function m() {
1452
- if (!(s.value || !l.value)) {
1453
- s.value = !0;
1467
+ async function h() {
1468
+ if (!(c.value || !u.value)) {
1469
+ c.value = !0;
1454
1470
  try {
1455
1471
  let e = [
1472
+ Y("postId", "==", o),
1456
1473
  Y("rootId", "==", n),
1457
1474
  Y("isReply", "==", !0),
1458
1475
  U("createdAt", "asc"),
1459
1476
  U(L(), "asc"),
1460
- ...u ? [q(u)] : [],
1477
+ ...d ? [q(d)] : [],
1461
1478
  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) => ({
1479
+ ], t = await z(W(a(), ...e)), r = new Set(s.value.map((e) => e.id)), i = t.docs.slice(0, Ut), c = i.map((e) => ({
1463
1480
  id: e.id,
1464
1481
  ...e.data()
1465
1482
  })).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());
1483
+ s.value = p([...s.value, ...c]), d = i.at(-1) ?? d, l.value = !0, t.size <= Ut && (u.value = !1, m());
1467
1484
  } finally {
1468
- s.value = !1;
1485
+ c.value = !1;
1469
1486
  }
1470
1487
  }
1471
1488
  }
1472
- function h() {
1473
- d?.(), d = void 0, o.value = [], u = null, c.value = !1, l.value = !0;
1489
+ function g() {
1490
+ f?.(), f = void 0, s.value = [], d = null, l.value = !1, u.value = !0;
1491
+ }
1492
+ async function v() {
1493
+ g(), await h();
1474
1494
  }
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
1495
+ return N(() => f?.()), {
1496
+ replies: s,
1497
+ loading: c,
1498
+ loaded: l,
1499
+ hasMore: u,
1500
+ loadMore: h,
1501
+ refresh: v
1485
1502
  };
1486
1503
  }
1487
1504
  //#endregion
1488
1505
  //#region src/composables/useMemiBoardLikes.ts
1489
- function qt(e, n) {
1506
+ function Jt(e, n) {
1490
1507
  let r = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1491
1508
  function l(e) {
1492
1509
  return j(i, r(), n, e);
@@ -1525,8 +1542,8 @@ function qt(e, n) {
1525
1542
  toggleLike: u
1526
1543
  };
1527
1544
  }
1528
- var Jt = Symbol.for("memi-board:comment-likes");
1529
- function Yt(e, t) {
1545
+ var Yt = Symbol.for("memi-board:comment-likes");
1546
+ function Xt(e, t) {
1530
1547
  let n = () => _(), r = Q(), i = Z(), a = M(() => F(e)), o = M(() => F(t)), s = P(/* @__PURE__ */ new Set()), c = P(null), u;
1531
1548
  I([i, o], ([e, t]) => {
1532
1549
  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 +1596,7 @@ function Yt(e, t) {
1579
1596
  }
1580
1597
  //#endregion
1581
1598
  //#region src/composables/useMemiBoardReports.ts
1582
- var Xt = 200, Zt = [
1599
+ var Zt = 200, Qt = [
1583
1600
  {
1584
1601
  value: "spam",
1585
1602
  label: "스팸·광고"
@@ -1601,10 +1618,10 @@ var Xt = 200, Zt = [
1601
1618
  label: "기타"
1602
1619
  }
1603
1620
  ];
1604
- function Qt(e) {
1605
- return Zt.find((t) => t.value === e)?.label || e || "신고";
1621
+ function $t(e) {
1622
+ return Qt.find((t) => t.value === e)?.label || e || "신고";
1606
1623
  }
1607
- function $t(e, t) {
1624
+ function en(e, t) {
1608
1625
  let n = () => _(), i = Q(), a = Z(), o = P(!1), s = P(!1), c;
1609
1626
  I(a, (e) => {
1610
1627
  if (c?.(), c = void 0, !e) {
@@ -1622,7 +1639,7 @@ function $t(e, t) {
1622
1639
  if (!l) throw Error("로그인이 필요합니다.");
1623
1640
  if (s.value) return;
1624
1641
  if (c.authorUid && c.authorUid === l) throw Error("자신의 글은 신고할 수 없습니다.");
1625
- let u = Zt.some((e) => e.value === c.reason) ? c.reason : null;
1642
+ let u = Qt.some((e) => e.value === c.reason) ? c.reason : null;
1626
1643
  if (!u) throw Error("신고 사유를 선택해 주세요.");
1627
1644
  let d = (c.detail || "").trim();
1628
1645
  if (d.length > 200) throw Error("자세한 내용은 200자까지입니다.");
@@ -1651,9 +1668,9 @@ function $t(e, t) {
1651
1668
  submitReport: l
1652
1669
  };
1653
1670
  }
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;
1671
+ var tn = 20;
1672
+ function nn(e = {}) {
1673
+ let t = () => _(), n = Q(), { canManageContent: i, canManageBoard: a } = tt(), s = e.pageSize ?? tn, c = M(() => F(e.boardId)?.trim() || ""), l = P([]), u = P(!0), d = P(!1), f = P(!1), p = P(""), m = null, h;
1657
1674
  function g(e) {
1658
1675
  return {
1659
1676
  id: e.id,
@@ -1707,7 +1724,7 @@ function tn(e = {}) {
1707
1724
  }
1708
1725
  //#endregion
1709
1726
  //#region src/composables/useMemiBoardViews.ts
1710
- function nn() {
1727
+ function rn() {
1711
1728
  let e = () => _(), n = Q();
1712
1729
  function r(t, n) {
1713
1730
  let { postsCollection: r } = e();
@@ -1747,7 +1764,7 @@ function nn() {
1747
1764
  }
1748
1765
  //#endregion
1749
1766
  //#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 = {
1767
+ 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은 \"\"", on = {
1751
1768
  abuse: "욕설·비속어·모욕",
1752
1769
  spam: "스팸·광고",
1753
1770
  adult: "선정적·성인 내용",
@@ -1755,17 +1772,17 @@ var rn = "당신은 게시판 콘텐츠를 심사하는 엄격한 모더레이
1755
1772
  other: "부적절한 내용",
1756
1773
  none: ""
1757
1774
  };
1758
- function on(e) {
1775
+ function sn(e) {
1759
1776
  if (e.via === "local" && e.localHit) {
1760
1777
  let t = e.localHit.trim();
1761
1778
  return t.length > 0 && t.length <= 12 ? `「${t}」 같은 표현은 사용할 수 없습니다. 다른 말로 바꿔 주세요.` : "욕설·비속어가 포함되어 게시할 수 없습니다. 표현을 바꿔 주세요.";
1762
1779
  }
1763
1780
  let t = (e.reason || "").trim();
1764
1781
  if (t) return t;
1765
- let n = e.category && e.category !== "none" ? an[e.category] || e.category : "";
1782
+ let n = e.category && e.category !== "none" ? on[e.category] || e.category : "";
1766
1783
  return n ? `${n}이(가) 포함되어 게시할 수 없습니다. 내용을 수정해 주세요.` : "게시할 수 없는 내용이 포함되어 있습니다. 내용을 수정해 주세요.";
1767
1784
  }
1768
- var sn = [
1785
+ var cn = [
1769
1786
  "씨발",
1770
1787
  "시발",
1771
1788
  "시이발",
@@ -1791,10 +1808,10 @@ var sn = [
1791
1808
  "bitch",
1792
1809
  "asshole"
1793
1810
  ];
1794
- function cn(e) {
1811
+ function ln(e) {
1795
1812
  return new RegExp(e.join("|"), "i");
1796
1813
  }
1797
- var ln = /* @__PURE__ */ new Set([
1814
+ var un = /* @__PURE__ */ new Set([
1798
1815
  "none",
1799
1816
  "abuse",
1800
1817
  "spam",
@@ -1802,7 +1819,7 @@ var ln = /* @__PURE__ */ new Set([
1802
1819
  "violence",
1803
1820
  "other"
1804
1821
  ]);
1805
- function un(e) {
1822
+ function dn(e) {
1806
1823
  let t = e.trim();
1807
1824
  if (!t) return null;
1808
1825
  let n;
@@ -1820,7 +1837,7 @@ function un(e) {
1820
1837
  if (typeof n != "object" || !n) return null;
1821
1838
  let r = n;
1822
1839
  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 : "";
1840
+ let i = typeof r.category == "string" && un.has(r.category) ? r.category : r.flagged ? "other" : "none", a = typeof r.reason == "string" ? r.reason : "";
1824
1841
  return {
1825
1842
  flagged: r.flagged,
1826
1843
  category: i,
@@ -1829,20 +1846,20 @@ function un(e) {
1829
1846
  }
1830
1847
  //#endregion
1831
1848
  //#region src/composables/useMemiBoardModeration.ts
1832
- var dn = {
1849
+ var fn = {
1833
1850
  flagged: !1,
1834
1851
  category: "none",
1835
1852
  reason: "",
1836
1853
  via: "empty"
1837
1854
  };
1838
- function fn() {
1839
- let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = et(), a = cn([...sn, ...e.localBlocklist ?? []]);
1855
+ function pn() {
1856
+ let e = u().moderation ?? {}, t = le(), { isWriteRestricted: n, restrictedMessage: r, recordContentModerationBlock: i } = tt(), a = ln([...cn, ...e.localBlocklist ?? []]);
1840
1857
  function o(e) {
1841
1858
  let t = e.match(a);
1842
1859
  return t ? {
1843
1860
  flagged: !0,
1844
1861
  category: "abuse",
1845
- reason: on({
1862
+ reason: sn({
1846
1863
  via: "local",
1847
1864
  localHit: t[0] || "",
1848
1865
  category: "abuse"
@@ -1857,7 +1874,7 @@ function fn() {
1857
1874
  });
1858
1875
  return Te(i, {
1859
1876
  model: n,
1860
- systemInstruction: rn,
1877
+ systemInstruction: an,
1861
1878
  generationConfig: {
1862
1879
  temperature: 0,
1863
1880
  maxOutputTokens: 256,
@@ -1886,7 +1903,7 @@ function fn() {
1886
1903
  };
1887
1904
  }
1888
1905
  function l(e, t = "") {
1889
- let n = on({
1906
+ let n = sn({
1890
1907
  reason: e.reason,
1891
1908
  category: e.category,
1892
1909
  via: e.via
@@ -1914,22 +1931,22 @@ function fn() {
1914
1931
  };
1915
1932
  let i = t.trim();
1916
1933
  if (!i) return {
1917
- ...dn,
1934
+ ...fn,
1918
1935
  via: "empty"
1919
1936
  };
1920
1937
  let a = o(i);
1921
1938
  if (a) return d(a);
1922
1939
  if (e.enabled === !1) return {
1923
- ...dn,
1940
+ ...fn,
1924
1941
  via: "disabled"
1925
1942
  };
1926
1943
  try {
1927
- let e = un((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
1944
+ let e = dn((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
1928
1945
  if (!e) return c();
1929
1946
  let t = {
1930
1947
  flagged: e.flagged,
1931
1948
  category: e.category,
1932
- reason: on({
1949
+ reason: sn({
1933
1950
  reason: e.reason,
1934
1951
  category: e.category,
1935
1952
  via: "ai"
@@ -1949,14 +1966,14 @@ function fn() {
1949
1966
  via: "restricted"
1950
1967
  };
1951
1968
  if (e.enabled === !1 || !e.moderateImages) return {
1952
- ...dn,
1969
+ ...fn,
1953
1970
  via: "disabled"
1954
1971
  };
1955
1972
  try {
1956
1973
  let e = await new Promise((e, n) => {
1957
1974
  let r = new FileReader();
1958
1975
  r.onload = () => e(r.result.split(",")[1] ?? ""), r.onerror = n, r.readAsDataURL(t);
1959
- }), n = un((await s().generateContent([{ inlineData: {
1976
+ }), n = dn((await s().generateContent([{ inlineData: {
1960
1977
  mimeType: t.type || "image/jpeg",
1961
1978
  data: e
1962
1979
  } }, "이 이미지를 심사해 주세요."])).response.text());
@@ -1964,7 +1981,7 @@ function fn() {
1964
1981
  let r = {
1965
1982
  flagged: n.flagged,
1966
1983
  category: n.category,
1967
- reason: on({
1984
+ reason: sn({
1968
1985
  reason: n.reason,
1969
1986
  category: n.category,
1970
1987
  via: "ai"
@@ -1983,7 +2000,7 @@ function fn() {
1983
2000
  }
1984
2001
  //#endregion
1985
2002
  //#region src/composables/useMemiBoardUsers.ts
1986
- var pn = [
2003
+ var mn = [
1987
2004
  {
1988
2005
  value: "admin",
1989
2006
  label: "관리자",
@@ -2000,10 +2017,10 @@ var pn = [
2000
2017
  description: "글과 댓글을 작성하고 본인이 작성한 콘텐츠만 수정·삭제할 수 있습니다."
2001
2018
  }
2002
2019
  ];
2003
- function mn(e = {}) {
2020
+ function hn(e = {}) {
2004
2021
  let t = () => _(), n = Q(), r = M(() => F(e.enabled) ?? !0 ? h(n, t()) : null), i = se(r, { ssrKey: w(t(), "users-management") });
2005
2022
  async function a(e, r) {
2006
- if (!pn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2023
+ if (!mn.some((e) => e.value === r)) throw Error("지원하지 않는 역할입니다.");
2007
2024
  await J(x(n, t(), e), {
2008
2025
  role: r,
2009
2026
  updatedAt: G()
@@ -2017,7 +2034,7 @@ function mn(e = {}) {
2017
2034
  }
2018
2035
  //#endregion
2019
2036
  //#region src/composables/useMemiBoardUserProfile.ts
2020
- function hn(e) {
2037
+ function gn(e) {
2021
2038
  let t = Q(), n = () => _(), r = M(() => F(e)), i = M(() => {
2022
2039
  let e = r.value;
2023
2040
  return e ? x(t, n(), e) : null;
@@ -2029,7 +2046,7 @@ function hn(e) {
2029
2046
  }
2030
2047
  //#endregion
2031
2048
  //#region src/utils/section.ts
2032
- var gn = [
2049
+ var _n = [
2033
2050
  3,
2034
2051
  4,
2035
2052
  5,
@@ -2038,7 +2055,7 @@ var gn = [
2038
2055
  8,
2039
2056
  9,
2040
2057
  12
2041
- ], _n = [
2058
+ ], vn = [
2042
2059
  {
2043
2060
  value: "list",
2044
2061
  label: "게시목록"
@@ -2051,24 +2068,24 @@ var gn = [
2051
2068
  value: "comments",
2052
2069
  label: "댓글모음"
2053
2070
  }
2054
- ], vn = [{
2071
+ ], yn = [{
2055
2072
  value: "latest",
2056
2073
  label: "최신순"
2057
2074
  }, {
2058
2075
  value: "likes",
2059
2076
  label: "좋아요순"
2060
2077
  }];
2061
- function yn(e) {
2078
+ function bn(e) {
2062
2079
  return e === "list" ? 5 : 4;
2063
2080
  }
2064
- function bn(e, t) {
2081
+ function xn(e, t) {
2065
2082
  let n = Math.floor(Number(t));
2066
- return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : yn(e);
2083
+ return Number.isFinite(n) ? Math.min(e === "list" ? 20 : 10, Math.max(1, n)) : bn(e);
2067
2084
  }
2068
- function xn(e) {
2085
+ function Sn(e) {
2069
2086
  return e === 12 ? null : 12 - e;
2070
2087
  }
2071
- function Sn(e) {
2088
+ function Cn(e) {
2072
2089
  return {
2073
2090
  3: "col-span-12 md:col-span-3",
2074
2091
  4: "col-span-12 md:col-span-4",
@@ -2080,36 +2097,36 @@ function Sn(e) {
2080
2097
  12: "col-span-12"
2081
2098
  }[e];
2082
2099
  }
2083
- function Cn(e) {
2100
+ function wn(e) {
2084
2101
  return e === "sm" ? "min-h-40" : e === "lg" ? "min-h-80" : "min-h-56";
2085
2102
  }
2086
- function wn(e) {
2103
+ function Tn(e) {
2087
2104
  let t = e.boardId || e.category || "free";
2088
2105
  return e.slug ? `/board/${t}/${e.slug}` : `/board/${t}`;
2089
2106
  }
2090
- function Tn(e) {
2107
+ function En(e) {
2091
2108
  return e.kind === "comments" ? e.sort === "likes" ? "/boardComments?sort=likes" : "/boardComments" : e.kind === "list" && e.boardId ? `/board/${e.boardId}` : "/board";
2092
2109
  }
2093
- function En(e) {
2110
+ function Dn(e) {
2094
2111
  if (!(!e.boardId || !e.postId)) return `/board/${e.boardId}/post/${e.postId}`;
2095
2112
  }
2096
- function Dn(e) {
2113
+ function On(e) {
2097
2114
  return e.previewImage || e.attachments?.find((e) => e.type.startsWith("image/"))?.url;
2098
2115
  }
2099
- function On(e) {
2116
+ function kn(e) {
2100
2117
  return e.title?.trim() || e.summary || "게시물";
2101
2118
  }
2102
- function kn(e) {
2119
+ function An(e) {
2103
2120
  return e === "image" || e === "video" ? "media" : "list";
2104
2121
  }
2105
- function An(e) {
2106
- return kn(e) === "media" ? "미디어" : "목록";
2122
+ function jn(e) {
2123
+ return An(e) === "media" ? "미디어" : "목록";
2107
2124
  }
2108
- function jn(e, t) {
2125
+ function Mn(e, t) {
2109
2126
  return e ? t(e)?.listView ?? "default" : "default";
2110
2127
  }
2111
- function Mn(e, t) {
2112
- let n = e.map((e) => jn(e.boardId, t));
2128
+ function Nn(e, t) {
2129
+ let n = e.map((e) => Mn(e.boardId, t));
2113
2130
  if (!n.length) return "default";
2114
2131
  if (n.every((e) => e === n[0])) return n[0];
2115
2132
  let r = n.filter((e) => e === "image").length, i = n.filter((e) => e === "video").length;
@@ -2117,27 +2134,27 @@ function Mn(e, t) {
2117
2134
  }
2118
2135
  //#endregion
2119
2136
  //#region src/composables/useMemiBoardSections.ts
2120
- var Nn = [];
2121
- function Pn(e) {
2137
+ var Pn = [];
2138
+ function Fn(e) {
2122
2139
  let t = Number(e.cols);
2123
- if (gn.includes(t)) return t;
2140
+ if (_n.includes(t)) return t;
2124
2141
  let n = e.width;
2125
2142
  return n === "xs" ? 3 : n === "sm" ? 4 : n === "lg" || Number(e.span) === 2 ? 12 : 6;
2126
2143
  }
2127
- function Fn(e) {
2144
+ function In(e) {
2128
2145
  let t = e.height ?? e.size;
2129
2146
  return t === "sm" || t === "lg" ? t : "md";
2130
2147
  }
2131
- function In(e) {
2148
+ function Ln(e) {
2132
2149
  return e.kind === "post" || e.kind === "comments" ? e.kind : "list";
2133
2150
  }
2134
- function Ln(e) {
2151
+ function Rn(e) {
2135
2152
  return e.sort === "likes" ? "likes" : "latest";
2136
2153
  }
2137
- function Rn(e) {
2154
+ function zn(e) {
2138
2155
  return (typeof e.boardId == "string" ? e.boardId.trim() : "") || null;
2139
2156
  }
2140
- function zn(e, t) {
2157
+ function Bn(e, t) {
2141
2158
  let n = Array.isArray(e.postIds) ? e.postIds : [], r = [];
2142
2159
  for (let e of n) {
2143
2160
  let n = String(e || "").trim();
@@ -2145,40 +2162,40 @@ function zn(e, t) {
2145
2162
  }
2146
2163
  return r;
2147
2164
  }
2148
- function Bn(e, t) {
2149
- let n = In(e), r = bn(n, e.count);
2165
+ function Vn(e, t) {
2166
+ let n = Ln(e), r = xn(n, e.count);
2150
2167
  return {
2151
2168
  id: String(e.id || `box-${t + 1}`),
2152
2169
  title: String(e.title || `섹션 ${t + 1}`),
2153
2170
  showTitle: e.showTitle !== !1,
2154
2171
  kind: n,
2155
- boardId: Rn(e),
2172
+ boardId: zn(e),
2156
2173
  count: r,
2157
- postIds: zn(e, r),
2158
- sort: Ln(e),
2159
- cols: Pn(e),
2160
- height: Fn(e),
2174
+ postIds: Bn(e, r),
2175
+ sort: Rn(e),
2176
+ cols: Fn(e),
2177
+ height: In(e),
2161
2178
  order: t
2162
2179
  };
2163
2180
  }
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)));
2181
+ function Hn() {
2182
+ let e = Q(), t = re(e, "settings", "homeLayout"), { data: n, pending: r } = ce(t), i = M(() => (Array.isArray(n.value?.blocks) ? n.value.blocks : Pn).map((e, t) => Vn(e, t)));
2166
2183
  async function a(e) {
2167
2184
  await K(t, {
2168
2185
  columns: 12,
2169
2186
  blocks: e.map((e, t) => {
2170
- let n = e, r = In(n), i = bn(r, e.count);
2187
+ let n = e, r = Ln(n), i = xn(r, e.count);
2171
2188
  return {
2172
2189
  id: e.id,
2173
2190
  title: e.title.trim() || `섹션 ${t + 1}`,
2174
2191
  showTitle: e.showTitle !== !1,
2175
2192
  kind: r,
2176
- boardId: Rn(n),
2193
+ boardId: zn(n),
2177
2194
  count: i,
2178
- postIds: zn(n, i),
2179
- sort: Ln(n),
2180
- cols: Pn(n),
2181
- height: Fn(n),
2195
+ postIds: Bn(n, i),
2196
+ sort: Rn(n),
2197
+ cols: Fn(n),
2198
+ height: In(n),
2182
2199
  order: t
2183
2200
  };
2184
2201
  }),
@@ -2197,7 +2214,7 @@ function Vn() {
2197
2214
  }
2198
2215
  //#endregion
2199
2216
  //#region src/composables/useMemiBoardMiniPosts.ts
2200
- function Hn(e, t) {
2217
+ function Un(e, t) {
2201
2218
  let n = Q(), r = () => _(), i = M(() => {
2202
2219
  if (!import.meta.client) return null;
2203
2220
  let i = Math.max(1, Math.floor(Number(F(t)) || 5)), a = F(e)?.trim() || "", o = [Y("isPublished", "==", !0), Y("listed", "==", !0)];
@@ -2209,7 +2226,7 @@ function Hn(e, t) {
2209
2226
  error: s
2210
2227
  };
2211
2228
  }
2212
- function Un(e) {
2229
+ function Wn(e) {
2213
2230
  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
2231
  return {
2215
2232
  posts: M(() => {
@@ -2226,7 +2243,13 @@ function Un(e) {
2226
2243
  }
2227
2244
  //#endregion
2228
2245
  //#region src/data/versionHistory.ts
2229
- var Wn = [
2246
+ var Gn = [
2247
+ {
2248
+ version: "0.38.0",
2249
+ date: "2026-08-21",
2250
+ highlights: ["숨김 게시판에는 댓글을 거의 쓰지 않아요. 필요하면 담당 스태프만 남길 수 있어요", "숨김 게시판 댓글은 공개 댓글 모음에 나오지 않아요"],
2251
+ note: "호스트는 Firestore 규칙·댓글 listed 인덱스를 다시 배포해야 해요. 공개 보드는 한 번 저장하면 예전 댓글이 목록에 다시 채워져요."
2252
+ },
2230
2253
  {
2231
2254
  version: "0.36.0",
2232
2255
  date: "2026-08-19",
@@ -2459,7 +2482,7 @@ var Wn = [
2459
2482
  highlights: ["글쓰기·댓글·첨부·로그인 권한으로 게시판을 바로 시작할 수 있어요", "부적절한 글·댓글은 AI 검열로 막을 수 있어요"],
2460
2483
  note: "실제 보안은 호스트 Firebase 규칙 등록이 필요합니다."
2461
2484
  }
2462
- ], Gn = /* @__PURE__ */ i(((e, t) => {
2485
+ ], Kn = /* @__PURE__ */ i(((e, t) => {
2463
2486
  (function(n, r) {
2464
2487
  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
2488
  })(e, (function() {
@@ -2753,7 +2776,7 @@ var Wn = [
2753
2776
  return w(1e3 * e);
2754
2777
  }, w.en = b[y], w.Ls = b, w.p = {}, w;
2755
2778
  }));
2756
- })), Kn = /* @__PURE__ */ i(((e, t) => {
2779
+ })), qn = /* @__PURE__ */ i(((e, t) => {
2757
2780
  (function(n, r) {
2758
2781
  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
2782
  })(e, (function() {
@@ -2857,9 +2880,9 @@ var Wn = [
2857
2880
  };
2858
2881
  };
2859
2882
  }));
2860
- })), qn = /* @__PURE__ */ i(((e, t) => {
2883
+ })), Jn = /* @__PURE__ */ i(((e, t) => {
2861
2884
  (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);
2885
+ typeof e == "object" && t !== void 0 ? t.exports = r(Kn()) : typeof define == "function" && define.amd ? define(["dayjs"], r) : (n = typeof globalThis < "u" ? globalThis : n || self).dayjs_locale_ko = r(n.dayjs);
2863
2886
  })(e, (function(e) {
2864
2887
  function t(e) {
2865
2888
  return e && typeof e == "object" && "default" in e ? e : { default: e };
@@ -2907,24 +2930,24 @@ var Wn = [
2907
2930
  };
2908
2931
  return n.default.locale(r, null, !0), r;
2909
2932
  }));
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) {
2933
+ })), Yn = /* @__PURE__ */ a(Kn(), 1), Xn = /* @__PURE__ */ a(qn(), 1);
2934
+ Jn(), Yn.default.extend(Xn.default), Yn.default.locale("ko");
2935
+ function Zn(e) {
2913
2936
  return e?.toDate ? e.toDate().toLocaleDateString("ko-KR") : "";
2914
2937
  }
2915
- function Zn(e, t = Date.now()) {
2916
- return e?.toDate ? (0, Jn.default)(e.toDate()).from((0, Jn.default)(t)) : "방금 전";
2938
+ function Qn(e, t = Date.now()) {
2939
+ return e?.toDate ? (0, Yn.default)(e.toDate()).from((0, Yn.default)(t)) : "방금 전";
2917
2940
  }
2918
- function Qn(e) {
2919
- return e?.toDate ? (0, Jn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2941
+ function $n(e) {
2942
+ return e?.toDate ? (0, Yn.default)(e.toDate()).format("YYYY년 M월 D일 HH:mm:ss") : "시간 확인 중";
2920
2943
  }
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)}`];
2944
+ function er(e, t) {
2945
+ let n = $n(e), r = e?.toMillis?.(), i = t?.toMillis?.();
2946
+ return i == null || r == null || r === i ? [n] : [`생성시간: ${n}`, `수정시간: ${$n(t)}`];
2924
2947
  }
2925
2948
  //#endregion
2926
2949
  //#region src/composables/useMemiBoardWritingAssistant.ts
2927
- var er = 10, tr = 864e5, nr = {
2950
+ var tr = 10, nr = 864e5, rr = {
2928
2951
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
2929
2952
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
2930
2953
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -2933,13 +2956,13 @@ var er = 10, tr = 864e5, nr = {
2933
2956
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
2934
2957
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
2935
2958
  };
2936
- function rr(e) {
2959
+ function ir(e) {
2937
2960
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
2938
2961
  }
2939
- function ir(e) {
2962
+ function ar(e) {
2940
2963
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
2941
2964
  }
2942
- function ar() {
2965
+ function or() {
2943
2966
  let e = le(), t = Q(), n = Z(), r = u();
2944
2967
  async function i() {
2945
2968
  let e = n.value?.uid;
@@ -2947,9 +2970,9 @@ function ar() {
2947
2970
  await oe(t, async (n) => {
2948
2971
  let r = x(t, _(), e), i = await n.get(r);
2949
2972
  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;
2973
+ let a = i.data(), o = ar(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < nr;
2951
2974
  if (l && s >= 10) {
2952
- let e = o + tr, t = Math.max(1, Math.ceil((e - c) / 36e5));
2975
+ let e = o + nr, t = Math.max(1, Math.ceil((e - c) / 36e5));
2953
2976
  throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
2954
2977
  }
2955
2978
  n.update(r, {
@@ -2978,10 +3001,10 @@ function ar() {
2978
3001
  }), s = t.content.trim();
2979
3002
  if (!s) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
2980
3003
  let c = [
2981
- `작업: ${nr[t.action]}`,
3004
+ `작업: ${rr[t.action]}`,
2982
3005
  t.title ? `현재 제목: ${t.title}` : "",
2983
3006
  `본문:\n${s.slice(0, 12e3)}`
2984
- ].filter(Boolean).join("\n\n"), l = rr((await o.generateContent(c)).response.text());
3007
+ ].filter(Boolean).join("\n\n"), l = ir((await o.generateContent(c)).response.text());
2985
3008
  if (!l) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
2986
3009
  return l;
2987
3010
  }
@@ -2989,14 +3012,14 @@ function ar() {
2989
3012
  }
2990
3013
  //#endregion
2991
3014
  //#region src/utils/renderMarkdown.ts
2992
- function or(e) {
3015
+ function sr(e) {
2993
3016
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2994
3017
  }
2995
- function sr(e) {
3018
+ function cr(e) {
2996
3019
  let t = e;
2997
3020
  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
3021
  }
2999
- function cr(e) {
3022
+ function lr(e) {
3000
3023
  if (!e?.trim()) return "";
3001
3024
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3002
3025
  function c() {
@@ -3005,7 +3028,7 @@ function cr(e) {
3005
3028
  return;
3006
3029
  }
3007
3030
  let e = o;
3008
- n.push(`<${e}>${s.map((e) => `<li>${sr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3031
+ n.push(`<${e}>${s.map((e) => `<li>${cr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3009
3032
  }
3010
3033
  for (; r < t.length;) {
3011
3034
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3014,7 +3037,7 @@ function cr(e) {
3014
3037
  continue;
3015
3038
  }
3016
3039
  if (i) {
3017
- a.push(or(e)), r++;
3040
+ a.push(sr(e)), r++;
3018
3041
  continue;
3019
3042
  }
3020
3043
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3025,24 +3048,24 @@ function cr(e) {
3025
3048
  if (u) {
3026
3049
  c();
3027
3050
  let e = u[1].length;
3028
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${sr(or(u[2]))}</h${e}>`), r++;
3051
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${cr(sr(u[2]))}</h${e}>`), r++;
3029
3052
  continue;
3030
3053
  }
3031
3054
  if (e.startsWith("> ")) {
3032
3055
  c();
3033
3056
  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>`);
3057
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(sr((t[r] ?? "").slice(2))), r++;
3058
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${cr(e.join("<br />"))}</blockquote>`);
3036
3059
  continue;
3037
3060
  }
3038
3061
  let d = e.match(/^[-*+]\s+(.+)$/);
3039
3062
  if (d) {
3040
- o && o !== "ul" && c(), o = "ul", s.push(or(d[1])), r++;
3063
+ o && o !== "ul" && c(), o = "ul", s.push(sr(d[1])), r++;
3041
3064
  continue;
3042
3065
  }
3043
3066
  let f = e.match(/^\d+\.\s+(.+)$/);
3044
3067
  if (f) {
3045
- o && o !== "ol" && c(), o = "ol", s.push(or(f[1])), r++;
3068
+ o && o !== "ol" && c(), o = "ol", s.push(sr(f[1])), r++;
3046
3069
  continue;
3047
3070
  }
3048
3071
  if (!e.trim()) {
@@ -3054,13 +3077,13 @@ function cr(e) {
3054
3077
  for (; r < t.length;) {
3055
3078
  let e = t[r] ?? "";
3056
3079
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3057
- p.push(or(e)), r++;
3080
+ p.push(sr(e)), r++;
3058
3081
  }
3059
- n.push(`<p class="my-2 leading-relaxed">${sr(p.join("<br />"))}</p>`);
3082
+ n.push(`<p class="my-2 leading-relaxed">${cr(p.join("<br />"))}</p>`);
3060
3083
  }
3061
3084
  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
3085
  }
3063
3086
  //#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 };
3087
+ export { zt as AUTHOR_MEMO_MAX_LENGTH, _n as BOARD_SECTION_COLS, vn as BOARD_SECTION_KINDS, yn as BOARD_SECTION_SORTS, mn as BOARD_USER_ROLES, Wt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, St as DEFAULT_BOARDS, Ct 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, Zt as REPORT_DETAIL_MAX_LENGTH, Qt as REPORT_REASONS, tr 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, xt as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, xn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, bn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Zn as formatDate, $n as formatFullDate, Qn as formatRelativeDate, Ze as formatRestrictedUntilLabel, er as formatTimestampDetails, gt as hasBodyImage, ht as hasBodyText, Be as isAssignedStaff, _t as isContentEmpty, Dt as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Yt as memiBoardCommentLikesKey, Mn as miniBoardView, Dn as miniCommentLink, En as miniMoreLink, On as miniPostImage, Tn as miniPostLink, kn as miniPostTitle, Nn as miniPostsView, An as miniViewGroup, jn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Vn as normalizeBoardSection, Sn as pairSectionCols, pt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, lr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, $t as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, Cn as sectionColClass, wn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, mt as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Vt as useMemiBoardAuthorMemo, Rt as useMemiBoardCommentFeed, Xt as useMemiBoardCommentLikes, Kt as useMemiBoardComments, u as useMemiBoardConfig, Jt as useMemiBoardLikes, Un as useMemiBoardMiniLatest, Wn as useMemiBoardMiniPicked, pn as useMemiBoardModeration, Mt as useMemiBoardPost, jt as useMemiBoardPostList, kt as useMemiBoardPosts, qt as useMemiBoardReplies, nn as useMemiBoardReportQueue, en as useMemiBoardReports, Hn as useMemiBoardSections, Et as useMemiBoardSettings, It as useMemiBoardUserComments, Pt as useMemiBoardUserPosts, gn as useMemiBoardUserProfile, hn as useMemiBoardUsers, rn as useMemiBoardViews, or as useMemiBoardWritingAssistant, Gn as versionHistory, bt as videoListCoverUrl, yt as youtubeId };
3065
3088
 
3066
3089
  //# sourceMappingURL=index.js.map