memi-board 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -467,6 +467,12 @@ const router = useRouter()
467
467
  콘텐츠 차단이 누적되면 `memiBoardUsers/{uid}` 에 `moderationBlockCount` / `moderationBlockAt` 이 기록된다.
468
468
  10회면 약 하루 동안 작성 불가(24h마다 1 차감). Rules 예시에서 본인은 **+1만** 가능하고 감소·리셋은 admin.
469
469
 
470
+ ### 관리자 검열 우회
471
+
472
+ 글쓰기 폼(`Editor.vue`) 하단, 제출 버튼 위에 `isAdmin`인 사용자에게만 "비속어 필터 무시" 스위치가 보인다. 켜고 제출하면 로컬 금칙어·AI 검열을 모두 건너뛰고 저장한다(`via: 'admin-override'`, 경고 카운트도 쌓이지 않음). 댓글 폼(`CommentForm.vue`)에는 없다.
473
+
474
+ 직접 호출할 때는 `useMemiBoardModeration().checkText(text, { skipFilter: true })`로 우회한다. `skipFilter`는 호스트가 넘긴 값 그대로 신뢰하므로, admin 여부 판정은 호출부(호스트)의 책임이다.
475
+
470
476
  ### AI 글쓰기 도우미 사용 제한
471
477
 
472
478
  에디터의 AI 글쓰기 도우미는 로그인 사용자별 24시간 구간에 최대 **10회** 사용할 수 있다.
@@ -5,6 +5,8 @@ import { ModerationResult } from '../types';
5
5
  * 콘텐츠 차단(local/ai) 시 board users 경고 누적.
6
6
  */
7
7
  export declare function useMemiBoardModeration(): {
8
- checkText: (text: string) => Promise<ModerationResult>;
8
+ checkText: (text: string, opts?: {
9
+ skipFilter?: boolean;
10
+ }) => Promise<ModerationResult>;
9
11
  checkImage: (file: File) => Promise<ModerationResult>;
10
12
  };
@@ -1,10 +1,12 @@
1
1
  export declare const WRITING_ASSISTANT_DAILY_LIMIT = 10;
2
- export type WritingAssistantAction = 'proofread' | 'polish' | 'continue' | 'summarize' | 'translate-en' | 'translate-ko' | 'title';
2
+ export type WritingAssistantAction = 'proofread' | 'polish' | 'continue' | 'summarize' | 'translate-en' | 'translate-ko' | 'title' | 'custom';
3
3
  interface WritingAssistantInput {
4
4
  action: WritingAssistantAction;
5
5
  content: string;
6
6
  title?: string;
7
7
  selection?: boolean;
8
+ /** action이 'custom'일 때 사용자가 직접 입력한 지시문 (예: "경상도 사투리 스타일로") */
9
+ customInstruction?: string;
8
10
  }
9
11
  /** 검열과 동일한 Firebase AI Logic 연결을 사용하는 글쓰기 도우미. */
10
12
  export declare function useMemiBoardWritingAssistant(): {
package/dist/index.js CHANGED
@@ -1930,26 +1930,30 @@ function mn() {
1930
1930
  return console.warn("[memi-board] record moderation strike failed", t instanceof Error ? t.message : t), l(e);
1931
1931
  }
1932
1932
  }
1933
- async function f(t) {
1933
+ async function f(t, i) {
1934
1934
  if (n.value) return {
1935
1935
  flagged: !0,
1936
1936
  category: "other",
1937
1937
  reason: r.value || "콘텐츠 경고가 누적되어 글·댓글 작성이 잠시 제한됐어요.",
1938
1938
  via: "restricted"
1939
1939
  };
1940
- let i = t.trim();
1941
- if (!i) return {
1940
+ let a = t.trim();
1941
+ if (!a) return {
1942
1942
  ...pn,
1943
1943
  via: "empty"
1944
1944
  };
1945
- let a = o(i);
1946
- if (a) return d(a);
1945
+ if (i?.skipFilter) return {
1946
+ ...pn,
1947
+ via: "admin-override"
1948
+ };
1949
+ let l = o(a);
1950
+ if (l) return d(l);
1947
1951
  if (e.enabled === !1) return {
1948
1952
  ...pn,
1949
1953
  via: "disabled"
1950
1954
  };
1951
1955
  try {
1952
- let e = fn((await s().generateContent(`심사할 텍스트:\n"""${i.slice(0, 4e3)}"""`)).response.text());
1956
+ let e = fn((await s().generateContent(`심사할 텍스트:\n"""${a.slice(0, 4e3)}"""`)).response.text());
1953
1957
  if (!e) return c();
1954
1958
  let t = {
1955
1959
  flagged: e.flagged,
@@ -2252,6 +2256,16 @@ function Gn(e) {
2252
2256
  //#endregion
2253
2257
  //#region src/data/versionHistory.ts
2254
2258
  var Kn = [
2259
+ {
2260
+ version: "0.40.0",
2261
+ date: "2026-08-27",
2262
+ highlights: ["AI 글쓰기 도우미에서 원하는 문체를 직접 입력해 바꿀 수 있어요 (예: 경상도 사투리 스타일로)"]
2263
+ },
2264
+ {
2265
+ version: "0.39.1",
2266
+ date: "2026-08-27",
2267
+ highlights: ["관리자는 글쓰기 하단 스위치로 비속어 필터를 끄고 글을 올릴 수 있어요"]
2268
+ },
2255
2269
  {
2256
2270
  version: "0.39.0",
2257
2271
  date: "2026-08-21",
@@ -2966,7 +2980,7 @@ function tr(e, t) {
2966
2980
  }
2967
2981
  //#endregion
2968
2982
  //#region src/composables/useMemiBoardWritingAssistant.ts
2969
- var nr = 10, rr = 864e5, ir = {
2983
+ var nr = 10, rr = 864e5, ir = 200, ar = {
2970
2984
  proofread: "맞춤법, 띄어쓰기, 문법 오류만 고쳐라. 의미와 말투는 바꾸지 마라.",
2971
2985
  polish: "뜻과 작성자의 말투를 유지하면서 더 자연스럽고 읽기 좋은 문장으로 다듬어라.",
2972
2986
  continue: "앞의 맥락과 말투에 맞는 내용을 과장 없이 한두 문단 이어서 작성하라.",
@@ -2975,13 +2989,19 @@ var nr = 10, rr = 864e5, ir = {
2975
2989
  "translate-ko": "자연스러운 한국어로 번역하라. 고유명사와 기술 용어는 정확히 유지하라.",
2976
2990
  title: "본문을 대표하는 간결한 한국어 제목 하나를 만들어라. 따옴표나 설명 없이 제목만 출력하라."
2977
2991
  };
2978
- function ar(e) {
2992
+ function or(e) {
2993
+ if (e.action !== "custom") return ar[e.action];
2994
+ let t = e.customInstruction?.trim().slice(0, ir);
2995
+ if (!t) throw Error("어떻게 바꿀지 지시문을 입력해 주세요.");
2996
+ return `뜻은 유지하면서 다음 스타일 지시를 반영해 다시 써라: ${t}`;
2997
+ }
2998
+ function sr(e) {
2979
2999
  return e.trim().replace(/^```(?:html|text)?\s*/i, "").replace(/\s*```$/, "").trim();
2980
3000
  }
2981
- function or(e) {
3001
+ function cr(e) {
2982
3002
  return !e || typeof e != "object" ? null : "toMillis" in e && typeof e.toMillis == "function" ? e.toMillis() : null;
2983
3003
  }
2984
- function sr() {
3004
+ function lr() {
2985
3005
  let e = le(), t = Q(), n = Z(), r = u();
2986
3006
  async function i() {
2987
3007
  let e = n.value?.uid;
@@ -2989,7 +3009,7 @@ function sr() {
2989
3009
  await oe(t, async (n) => {
2990
3010
  let r = x(t, _(), e), i = await n.get(r);
2991
3011
  if (!i.exists()) throw Error("사용자 정보를 확인한 뒤 다시 시도해 주세요.");
2992
- let a = i.data(), o = or(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < rr;
3012
+ let a = i.data(), o = cr(a.aiWritingWindowAt), s = Math.max(0, Math.floor(Number(a.aiWritingCount) || 0)), c = Date.now(), l = o != null && c - o < rr;
2993
3013
  if (l && s >= 10) {
2994
3014
  let e = o + rr, t = Math.max(1, Math.ceil((e - c) / 36e5));
2995
3015
  throw Error(`AI 글쓰기 도우미는 하루 10회까지 사용할 수 있습니다. 약 ${t}시간 후 다시 이용해 주세요.`);
@@ -3002,43 +3022,43 @@ function sr() {
3002
3022
  });
3003
3023
  }
3004
3024
  async function a(t) {
3025
+ let n = or(t);
3026
+ if (!t.content.trim()) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
3005
3027
  await i();
3006
- let n = r.moderation ?? {}, a = we(e, {
3028
+ let a = r.moderation ?? {}, o = we(e, {
3007
3029
  backend: new Se(),
3008
- useLimitedUseAppCheckTokens: n.useLimitedUseAppCheckTokens !== !1
3009
- }), o = Te(a, {
3010
- model: n.model ?? "gemini-3.5-flash-lite",
3030
+ useLimitedUseAppCheckTokens: a.useLimitedUseAppCheckTokens !== !1
3031
+ }), s = Te(o, {
3032
+ model: a.model ?? "gemini-3.5-flash-lite",
3011
3033
  systemInstruction: [
3012
3034
  "너는 게시판 글쓰기 편집 도우미다.",
3013
3035
  "사용자가 요청한 편집 결과만 출력하고 설명, 인사, 마크다운 코드 펜스를 붙이지 마라.",
3014
3036
  t.action === "title" || t.selection ? "출력은 일반 텍스트로 작성하라." : "입력의 HTML 구조와 링크, 이미지 태그를 보존하고 결과도 HTML 조각으로 출력하라."
3015
3037
  ].join("\n"),
3016
3038
  generationConfig: {
3017
- temperature: t.action === "continue" || t.action === "title" ? .6 : .2,
3039
+ temperature: t.action === "continue" || t.action === "title" || t.action === "custom" ? .6 : .2,
3018
3040
  maxOutputTokens: 2048
3019
3041
  }
3020
- }), s = t.content.trim();
3021
- if (!s) throw Error("AI로 다듬을 내용을 먼저 입력해 주세요.");
3022
- let c = [
3023
- `작업: ${ir[t.action]}`,
3042
+ }), c = t.content.trim(), l = [
3043
+ `작업: ${n}`,
3024
3044
  t.title ? `현재 제목: ${t.title}` : "",
3025
- `본문:\n${s.slice(0, 12e3)}`
3026
- ].filter(Boolean).join("\n\n"), l = ar((await o.generateContent(c)).response.text());
3027
- if (!l) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
3028
- return l;
3045
+ `본문:\n${c.slice(0, 12e3)}`
3046
+ ].filter(Boolean).join("\n\n"), u = sr((await s.generateContent(l)).response.text());
3047
+ if (!u) throw Error("AI가 결과를 만들지 못했습니다. 다시 시도해 주세요.");
3048
+ return u;
3029
3049
  }
3030
3050
  return { assist: a };
3031
3051
  }
3032
3052
  //#endregion
3033
3053
  //#region src/utils/renderMarkdown.ts
3034
- function cr(e) {
3054
+ function ur(e) {
3035
3055
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3036
3056
  }
3037
- function lr(e) {
3057
+ function dr(e) {
3038
3058
  let t = e;
3039
3059
  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;
3040
3060
  }
3041
- function ur(e) {
3061
+ function fr(e) {
3042
3062
  if (!e?.trim()) return "";
3043
3063
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = 0, i = !1, a = [], o = null, s = [];
3044
3064
  function c() {
@@ -3047,7 +3067,7 @@ function ur(e) {
3047
3067
  return;
3048
3068
  }
3049
3069
  let e = o;
3050
- n.push(`<${e}>${s.map((e) => `<li>${lr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3070
+ n.push(`<${e}>${s.map((e) => `<li>${dr(e)}</li>`).join("")}</${e}>`), o = null, s = [];
3051
3071
  }
3052
3072
  for (; r < t.length;) {
3053
3073
  let e = t[r] ?? "", l = e.match(/^```(\w*)\s*$/);
@@ -3056,7 +3076,7 @@ function ur(e) {
3056
3076
  continue;
3057
3077
  }
3058
3078
  if (i) {
3059
- a.push(cr(e)), r++;
3079
+ a.push(ur(e)), r++;
3060
3080
  continue;
3061
3081
  }
3062
3082
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(e)) {
@@ -3067,24 +3087,24 @@ function ur(e) {
3067
3087
  if (u) {
3068
3088
  c();
3069
3089
  let e = u[1].length;
3070
- n.push(`<h${e} class="font-semibold mt-4 mb-2">${lr(cr(u[2]))}</h${e}>`), r++;
3090
+ n.push(`<h${e} class="font-semibold mt-4 mb-2">${dr(ur(u[2]))}</h${e}>`), r++;
3071
3091
  continue;
3072
3092
  }
3073
3093
  if (e.startsWith("> ")) {
3074
3094
  c();
3075
3095
  let e = [];
3076
- for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(cr((t[r] ?? "").slice(2))), r++;
3077
- n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${lr(e.join("<br />"))}</blockquote>`);
3096
+ for (; r < t.length && (t[r] ?? "").startsWith("> ");) e.push(ur((t[r] ?? "").slice(2))), r++;
3097
+ n.push(`<blockquote class="border-l-4 border-default pl-3 text-muted my-3">${dr(e.join("<br />"))}</blockquote>`);
3078
3098
  continue;
3079
3099
  }
3080
3100
  let d = e.match(/^[-*+]\s+(.+)$/);
3081
3101
  if (d) {
3082
- o && o !== "ul" && c(), o = "ul", s.push(cr(d[1])), r++;
3102
+ o && o !== "ul" && c(), o = "ul", s.push(ur(d[1])), r++;
3083
3103
  continue;
3084
3104
  }
3085
3105
  let f = e.match(/^\d+\.\s+(.+)$/);
3086
3106
  if (f) {
3087
- o && o !== "ol" && c(), o = "ol", s.push(cr(f[1])), r++;
3107
+ o && o !== "ol" && c(), o = "ol", s.push(ur(f[1])), r++;
3088
3108
  continue;
3089
3109
  }
3090
3110
  if (!e.trim()) {
@@ -3096,13 +3116,13 @@ function ur(e) {
3096
3116
  for (; r < t.length;) {
3097
3117
  let e = t[r] ?? "";
3098
3118
  if (!e.trim() || /^(#{1,3}\s|```|[-*+]\s|\d+\.\s|> |(-{3,}|\*{3,})\s*$)/.test(e)) break;
3099
- p.push(cr(e)), r++;
3119
+ p.push(ur(e)), r++;
3100
3120
  }
3101
- n.push(`<p class="my-2 leading-relaxed">${lr(p.join("<br />"))}</p>`);
3121
+ n.push(`<p class="my-2 leading-relaxed">${dr(p.join("<br />"))}</p>`);
3102
3122
  }
3103
3123
  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");
3104
3124
  }
3105
3125
  //#endregion
3106
- export { Bt as AUTHOR_MEMO_MAX_LENGTH, vn as BOARD_SECTION_COLS, yn as BOARD_SECTION_KINDS, bn as BOARD_SECTION_SORTS, hn as BOARD_USER_ROLES, Gt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, Ct as DEFAULT_BOARDS, wt as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Qt as REPORT_DETAIL_MAX_LENGTH, $t as REPORT_REASONS, nr as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, St as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, Sn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, xn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Qn as formatDate, er as formatFullDate, $n as formatRelativeDate, Ze as formatRestrictedUntilLabel, tr as formatTimestampDetails, _t as hasBodyImage, gt as hasBodyText, Be as isAssignedStaff, vt as isContentEmpty, Ot as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Xt as memiBoardCommentLikesKey, Nn as miniBoardView, On as miniCommentLink, Dn as miniMoreLink, kn as miniPostImage, En as miniPostLink, An as miniPostTitle, Pn as miniPostsView, jn as miniViewGroup, Mn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Hn as normalizeBoardSection, Cn as pairSectionCols, mt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, ur as renderMarkdownToHtml, r as reportDoc, n as reportDocId, en as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, wn as sectionColClass, Tn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, ht as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Ht as useMemiBoardAuthorMemo, zt as useMemiBoardCommentFeed, Zt as useMemiBoardCommentLikes, qt as useMemiBoardComments, u as useMemiBoardConfig, Yt as useMemiBoardLikes, Wn as useMemiBoardMiniLatest, Gn as useMemiBoardMiniPicked, mn as useMemiBoardModeration, Nt as useMemiBoardPost, Mt as useMemiBoardPostList, At as useMemiBoardPosts, Jt as useMemiBoardReplies, rn as useMemiBoardReportQueue, tn as useMemiBoardReports, Un as useMemiBoardSections, Dt as useMemiBoardSettings, Lt as useMemiBoardUserComments, Ft as useMemiBoardUserPosts, _n as useMemiBoardUserProfile, gn as useMemiBoardUsers, an as useMemiBoardViews, sr as useMemiBoardWritingAssistant, Kn as versionHistory, xt as videoListCoverUrl, bt as youtubeId };
3126
+ export { Bt as AUTHOR_MEMO_MAX_LENGTH, vn as BOARD_SECTION_COLS, yn as BOARD_SECTION_KINDS, bn as BOARD_SECTION_SORTS, hn as BOARD_USER_ROLES, Gt as COMMENT_BODY_MAX_LENGTH, qe as DEFAULT_BLOCK_BAN_DECAY_MS, Ke as DEFAULT_BLOCK_BAN_THRESHOLD, Ct as DEFAULT_BOARDS, wt as DEFAULT_CATEGORIES, C as DEFAULT_COMMENTS_COLLECTION, E as DEFAULT_LIKES_COLLECTION, f as DEFAULT_POSTS_COLLECTION, b as DEFAULT_REPORTS_COLLECTION, D as DEFAULT_SETTINGS_COLLECTION, p as DEFAULT_USERS_COLLECTION, Qt as REPORT_DETAIL_MAX_LENGTH, $t as REPORT_REASONS, nr as WRITING_ASSISTANT_DAILY_LIMIT, Ee as asHttpUrl, Me as boardListOgDescription, je as boardListOgTitle, Ae as boardPostOgDescription, ke as boardPostOgTitle, w as boardSsrKey, x as boardUserDoc, h as boardUsersCol, St as buildPostPreview, Ve as canManageBoardByRole, He as canWriteCommentByRole, Sn as clampSectionCount, l as commentDoc, O as commentsCol, T as configureMemiBoard, xn as defaultSectionCount, Je as effectiveModerationBlockCount, nt as extractEditorImageUrls, Re as fetchPublicListForSeo, Le as fetchPublicPostForSeo, Qn as formatDate, er as formatFullDate, $n as formatRelativeDate, Ze as formatRestrictedUntilLabel, tr as formatTimestampDetails, _t as hasBodyImage, gt as hasBodyText, Be as isAssignedStaff, vt as isContentEmpty, Ot as isFirestorePermissionDenied, Xe as isModerationWriteRestricted, j as likeDoc, d as likeDocId, A as likesCol, Xt as memiBoardCommentLikesKey, Nn as miniBoardView, On as miniCommentLink, Dn as miniMoreLink, kn as miniPostImage, En as miniPostLink, An as miniPostTitle, Pn as miniPostsView, jn as miniViewGroup, Mn as miniViewLabel, Ye as moderationWriteRestrictedUntilMs, Ne as normalizeBasePath, Hn as normalizeBoardSection, Cn as pairSectionCols, mt as plainTextFromHtml, s as postBodyDoc, t as postDoc, it as postNamespaceFromStoragePath, k as postStorageFolder, c as postsCol, fr as renderMarkdownToHtml, r as reportDoc, n as reportDocId, en as reportReasonLabel, o as reportsCol, y as resolveBoardPathConfig, Pe as resolvePublicSeoDb, wn as sectionColClass, Tn as sectionMinHeight, e as settingsCol, v as settingsDoc, S as slugify, rt as storagePathFromDownloadUrl, ht as titleFromBody, Oe as toAbsoluteUrl, De as toBoardOgImageUrl, _ as useBoardPathConfig, tt as useMemiBoardAuth, Ht as useMemiBoardAuthorMemo, zt as useMemiBoardCommentFeed, Zt as useMemiBoardCommentLikes, qt as useMemiBoardComments, u as useMemiBoardConfig, Yt as useMemiBoardLikes, Wn as useMemiBoardMiniLatest, Gn as useMemiBoardMiniPicked, mn as useMemiBoardModeration, Nt as useMemiBoardPost, Mt as useMemiBoardPostList, At as useMemiBoardPosts, Jt as useMemiBoardReplies, rn as useMemiBoardReportQueue, tn as useMemiBoardReports, Un as useMemiBoardSections, Dt as useMemiBoardSettings, Lt as useMemiBoardUserComments, Ft as useMemiBoardUserPosts, _n as useMemiBoardUserProfile, gn as useMemiBoardUsers, an as useMemiBoardViews, lr as useMemiBoardWritingAssistant, Kn as versionHistory, xt as videoListCoverUrl, bt as youtubeId };
3107
3127
 
3108
3128
  //# sourceMappingURL=index.js.map