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