memi-board 0.46.2 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.mjs CHANGED
@@ -129,6 +129,8 @@ var s = a({
129
129
  import { computed } from 'vue'
130
130
  import {
131
131
  configureMemiBoard,
132
+ useMemiBoardConfig,
133
+ isMemiBoardI18nEnabled,
132
134
  loadMemiBoardLocale,
133
135
  memiBoardHostLocaleKey,
134
136
  normalizeMemiBoardLocale,
@@ -140,6 +142,11 @@ export default defineNuxtPlugin({
140
142
  dependsOn: ['i18n:plugin'],
141
143
  async setup(nuxtApp) {
142
144
  configureMemiBoard(${_})
145
+ if (!isMemiBoardI18nEnabled()) {
146
+ const fixed = normalizeMemiBoardLocale(useMemiBoardConfig().locale) ?? 'ko'
147
+ await loadMemiBoardLocale(fixed)
148
+ return
149
+ }
143
150
  // 호스트가 @nuxtjs/i18n 을 쓰면 그 언어를 게시판 기본 언어로 쓴다.
144
151
  // 요청(SSR)마다 앱 인스턴스가 따로라 provide 로 넘긴다.
145
152
  const i18n = nuxtApp.$i18n
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":[],"sources":["../src/module.ts"],"sourcesContent":["// @ts-nocheck — NuxtModule 반환 타입이 @nuxt/schema 경로에 묶여 d.ts 생성 시 불안정\nimport {\n defineNuxtModule,\n createResolver,\n addComponentsDir,\n addImports,\n addPlugin,\n addTemplate,\n} from '@nuxt/kit'\nimport { existsSync } from 'node:fs'\nimport type { NuxtModule } from '@nuxt/schema'\nimport type { MemiBoardConfig } from './config'\n\ntype MemiBoardModuleOptions = Partial<MemiBoardConfig>\n\n/**\n * Nuxt 전용 모듈 — 호스트 Vite 파이프라인에 게시판 SFC와 필수 설정을 올린다.\n *\n * 하지 않는 일:\n * - nuxt-vuefire / Firebase 설정\n * - 라우트·미들웨어 등록\n *\n * 하는 일:\n * - components 디렉터리 등록 → 호스트가 SFC 컴파일 → Nuxt UI auto-import 정상 동작\n * - composable / configure 를 auto-import\n * - memiBoard 옵션을 런타임 플러그인으로 전달해 configureMemiBoard 자동 호출\n * - transpile + optimizeDeps 설정으로 linked package와 CommonJS 의존성 정규화\n */\nconst memiBoardModule: NuxtModule<MemiBoardModuleOptions> = defineNuxtModule<MemiBoardModuleOptions>({\n meta: {\n name: 'memi-board',\n configKey: 'memiBoard',\n },\n setup(options, nuxt) {\n const { resolve } = createResolver(import.meta.url)\n\n // 우선순위:\n // 1) monorepo link 시 dist/module.mjs → ../src/components (소스 직접, 재빌드 없이 UI 수정)\n // 2) publish 패키지 dist/runtime/components\n // 3) src/module.ts 로 직접 로드할 때 ./components\n const candidates = [\n resolve('../src/components'),\n resolve('./runtime/components'),\n resolve('./components'),\n ]\n const componentsDir = candidates.find(p => existsSync(p))\n if (!componentsDir) {\n throw new Error('[memi-board] components directory not found')\n }\n nuxt.options.build.transpile.push('memi-board')\n\n nuxt.options.vite ||= {}\n\n // link:/file: 설치 시 memi-board 패키지 루트(dist/ 등)가 호스트의 node_modules\n // 밖에 있어서 Vite dev 서버의 fs.allow 기본값(root + node_modules)에 안 걸린다 —\n // /@fs/ 요청이 403(\"outside of Vite serving allow list\")으로 막히고, 그 403 HTML을\n // JS로 파싱하려다 알 수 없는 SyntaxError가 난다. 패키지 루트를 명시적으로 허용한다.\n const packageRoot = resolve('..')\n nuxt.options.vite.server ||= {}\n nuxt.options.vite.server.fs ||= {}\n const fsAllow = new Set([\n ...(nuxt.options.vite.server.fs.allow || []),\n packageRoot,\n ])\n nuxt.options.vite.server.fs.allow = [...fsAllow]\n\n nuxt.options.vite.resolve ||= {}\n // link:/file: 로 설치 시 패키지 쪽 node_modules 의 vuefire·firebase 가\n // 호스트와 이중으로 잡히면 useFirestore 가 깨져 permission-denied 가 난다.\n const dedupe = new Set([\n ...(nuxt.options.vite.resolve.dedupe || []),\n 'vue',\n 'vue-router',\n 'vuefire',\n 'firebase',\n 'nuxt',\n '@tiptap/core',\n '@tiptap/pm',\n '@tiptap/vue-3',\n ])\n nuxt.options.vite.resolve.dedupe = [...dedupe]\n\n nuxt.options.vite.optimizeDeps ||= {}\n const exclude = new Set(nuxt.options.vite.optimizeDeps.exclude || [])\n exclude.add('memi-board')\n nuxt.options.vite.optimizeDeps.exclude = [...exclude]\n\n // link:/file: 개발 중엔 memi-board의 실제 경로가 host의 node_modules 밖에 있어서\n // Nuxt auto-import(unimport)의 \"node_modules는 건드리지 않는다\" 체크(경로에\n // 'node_modules' 문자열이 있는지로 판단)를 그냥 통과해버린다 — 그러면 memi-board의\n // dist 청크를 \"프로젝트 소스\"로 스캔하다가, rollup이 공유 청크의 export를 붙일 때\n // 우연히 짧은 별칭(a, b, … h …)을 골랐는데 그게 Vue의 전역 auto-import `h`\n // (hyperscript)와 겹치면 `import { h } from 'vue'`를 그 청크에 주입해버려서\n // 이미 있는 `h` export 별칭과 충돌한다(\"Identifier 'h' has already been declared\").\n // 명시적으로 제외해 이 청크는 절대 auto-import 스캔 대상이 되지 않게 한다\n // (published npm 패키지 설치 시엔 이미 node_modules 안이라 원래도 제외 대상).\n nuxt.options.imports ||= {}\n nuxt.options.imports.transform ||= {}\n const transformExclude = new Set([\n ...(nuxt.options.imports.transform.exclude || []),\n /[\\\\/]memi-board[\\\\/]dist[\\\\/]/,\n ])\n nuxt.options.imports.transform.exclude = [...transformExclude]\n\n addComponentsDir({\n path: componentsDir,\n pathPrefix: false,\n prefix: 'MemiBoard',\n // List.vue → MemiBoardList\n })\n\n // SEO: 호스트 Nuxt 가 소스를 컴파일해야 #imports / useSeoMeta 가 앱 인스턴스를 탄다.\n // dist/index.js 에 넣으면 link 시 패키지 node_modules/nuxt 로 해석되어 깨진다.\n const seoCandidates = [\n resolve('../src/composables/useMemiBoardSeo.ts'),\n resolve('./runtime/composables/useMemiBoardSeo.ts'),\n resolve('./composables/useMemiBoardSeo.ts'),\n ]\n const seoFrom = seoCandidates.find(p => existsSync(p))\n if (!seoFrom) {\n throw new Error('[memi-board] useMemiBoardSeo runtime not found')\n }\n\n const from = 'memi-board/runtime'\n addImports([\n { name: 'configureMemiBoard', from },\n { name: 'useMemiBoardConfig', from },\n { name: 'useMemiBoardAuth', from },\n { name: 'useMemiBoardPosts', from },\n { name: 'useMemiBoardComments', from },\n { name: 'useMemiBoardViews', from },\n { name: 'useMemiBoardModeration', from },\n // 이미지 압축 등 브라우저 전용 — SSR 서버 번들에 안 섞이도록 별도 서브패스에서 해석\n { name: 'useMemiBoardStorage', from: 'memi-board/storage' },\n { name: 'useMemiBoardSettings', from },\n { name: 'useMemiBoardUsers', from },\n { name: 'useMemiBoardPostSeo', from: seoFrom },\n { name: 'useMemiBoardListSeo', from: seoFrom },\n { name: 'fetchPublicPostForSeo', from },\n { name: 'fetchPublicListForSeo', from },\n { name: 'resolvePublicSeoDb', from },\n { name: 'versionHistory', from },\n { name: 'useMemiBoardI18n', from },\n { name: 'MEMI_BOARD_LOCALES', from },\n { name: 'useMemiBoardAutoTranslate', from },\n ])\n\n const runtimeConfig = JSON.stringify(options).replace(/</g, '\\\\u003c')\n const configPlugin = addTemplate({\n filename: 'memi-board.config.mjs',\n getContents: () => `\nimport { computed } from 'vue'\nimport {\n configureMemiBoard,\n loadMemiBoardLocale,\n memiBoardHostLocaleKey,\n normalizeMemiBoardLocale,\n} from 'memi-board/runtime'\n\nexport default defineNuxtPlugin({\n name: 'memi-board:config',\n // @nuxtjs/i18n 플러그인이 $i18n 을 만든 뒤 실행되어야 한다.\n dependsOn: ['i18n:plugin'],\n async setup(nuxtApp) {\n configureMemiBoard(${runtimeConfig})\n // 호스트가 @nuxtjs/i18n 을 쓰면 그 언어를 게시판 기본 언어로 쓴다.\n // 요청(SSR)마다 앱 인스턴스가 따로라 provide 로 넘긴다.\n const i18n = nuxtApp.$i18n\n if (!i18n?.locale) return\n const hostLocale = computed(() => i18n.locale.value)\n nuxtApp.vueApp.provide(memiBoardHostLocaleKey, hostLocale)\n const initial = normalizeMemiBoardLocale(hostLocale.value)\n if (initial) await loadMemiBoardLocale(initial)\n },\n})\n`,\n })\n addPlugin(configPlugin.dst)\n },\n})\n\nexport default memiBoardModule\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.mjs","names":[],"sources":["../src/module.ts"],"sourcesContent":["// @ts-nocheck — NuxtModule 반환 타입이 @nuxt/schema 경로에 묶여 d.ts 생성 시 불안정\nimport {\n defineNuxtModule,\n createResolver,\n addComponentsDir,\n addImports,\n addPlugin,\n addTemplate,\n} from '@nuxt/kit'\nimport { existsSync } from 'node:fs'\nimport type { NuxtModule } from '@nuxt/schema'\nimport type { MemiBoardConfig } from './config'\n\ntype MemiBoardModuleOptions = Partial<MemiBoardConfig>\n\n/**\n * Nuxt 전용 모듈 — 호스트 Vite 파이프라인에 게시판 SFC와 필수 설정을 올린다.\n *\n * 하지 않는 일:\n * - nuxt-vuefire / Firebase 설정\n * - 라우트·미들웨어 등록\n *\n * 하는 일:\n * - components 디렉터리 등록 → 호스트가 SFC 컴파일 → Nuxt UI auto-import 정상 동작\n * - composable / configure 를 auto-import\n * - memiBoard 옵션을 런타임 플러그인으로 전달해 configureMemiBoard 자동 호출\n * - transpile + optimizeDeps 설정으로 linked package와 CommonJS 의존성 정규화\n */\nconst memiBoardModule: NuxtModule<MemiBoardModuleOptions> = defineNuxtModule<MemiBoardModuleOptions>({\n meta: {\n name: 'memi-board',\n configKey: 'memiBoard',\n },\n setup(options, nuxt) {\n const { resolve } = createResolver(import.meta.url)\n\n // 우선순위:\n // 1) monorepo link 시 dist/module.mjs → ../src/components (소스 직접, 재빌드 없이 UI 수정)\n // 2) publish 패키지 dist/runtime/components\n // 3) src/module.ts 로 직접 로드할 때 ./components\n const candidates = [\n resolve('../src/components'),\n resolve('./runtime/components'),\n resolve('./components'),\n ]\n const componentsDir = candidates.find(p => existsSync(p))\n if (!componentsDir) {\n throw new Error('[memi-board] components directory not found')\n }\n nuxt.options.build.transpile.push('memi-board')\n\n nuxt.options.vite ||= {}\n\n // link:/file: 설치 시 memi-board 패키지 루트(dist/ 등)가 호스트의 node_modules\n // 밖에 있어서 Vite dev 서버의 fs.allow 기본값(root + node_modules)에 안 걸린다 —\n // /@fs/ 요청이 403(\"outside of Vite serving allow list\")으로 막히고, 그 403 HTML을\n // JS로 파싱하려다 알 수 없는 SyntaxError가 난다. 패키지 루트를 명시적으로 허용한다.\n const packageRoot = resolve('..')\n nuxt.options.vite.server ||= {}\n nuxt.options.vite.server.fs ||= {}\n const fsAllow = new Set([\n ...(nuxt.options.vite.server.fs.allow || []),\n packageRoot,\n ])\n nuxt.options.vite.server.fs.allow = [...fsAllow]\n\n nuxt.options.vite.resolve ||= {}\n // link:/file: 로 설치 시 패키지 쪽 node_modules 의 vuefire·firebase 가\n // 호스트와 이중으로 잡히면 useFirestore 가 깨져 permission-denied 가 난다.\n const dedupe = new Set([\n ...(nuxt.options.vite.resolve.dedupe || []),\n 'vue',\n 'vue-router',\n 'vuefire',\n 'firebase',\n 'nuxt',\n '@tiptap/core',\n '@tiptap/pm',\n '@tiptap/vue-3',\n ])\n nuxt.options.vite.resolve.dedupe = [...dedupe]\n\n nuxt.options.vite.optimizeDeps ||= {}\n const exclude = new Set(nuxt.options.vite.optimizeDeps.exclude || [])\n exclude.add('memi-board')\n nuxt.options.vite.optimizeDeps.exclude = [...exclude]\n\n // link:/file: 개발 중엔 memi-board의 실제 경로가 host의 node_modules 밖에 있어서\n // Nuxt auto-import(unimport)의 \"node_modules는 건드리지 않는다\" 체크(경로에\n // 'node_modules' 문자열이 있는지로 판단)를 그냥 통과해버린다 — 그러면 memi-board의\n // dist 청크를 \"프로젝트 소스\"로 스캔하다가, rollup이 공유 청크의 export를 붙일 때\n // 우연히 짧은 별칭(a, b, … h …)을 골랐는데 그게 Vue의 전역 auto-import `h`\n // (hyperscript)와 겹치면 `import { h } from 'vue'`를 그 청크에 주입해버려서\n // 이미 있는 `h` export 별칭과 충돌한다(\"Identifier 'h' has already been declared\").\n // 명시적으로 제외해 이 청크는 절대 auto-import 스캔 대상이 되지 않게 한다\n // (published npm 패키지 설치 시엔 이미 node_modules 안이라 원래도 제외 대상).\n nuxt.options.imports ||= {}\n nuxt.options.imports.transform ||= {}\n const transformExclude = new Set([\n ...(nuxt.options.imports.transform.exclude || []),\n /[\\\\/]memi-board[\\\\/]dist[\\\\/]/,\n ])\n nuxt.options.imports.transform.exclude = [...transformExclude]\n\n addComponentsDir({\n path: componentsDir,\n pathPrefix: false,\n prefix: 'MemiBoard',\n // List.vue → MemiBoardList\n })\n\n // SEO: 호스트 Nuxt 가 소스를 컴파일해야 #imports / useSeoMeta 가 앱 인스턴스를 탄다.\n // dist/index.js 에 넣으면 link 시 패키지 node_modules/nuxt 로 해석되어 깨진다.\n const seoCandidates = [\n resolve('../src/composables/useMemiBoardSeo.ts'),\n resolve('./runtime/composables/useMemiBoardSeo.ts'),\n resolve('./composables/useMemiBoardSeo.ts'),\n ]\n const seoFrom = seoCandidates.find(p => existsSync(p))\n if (!seoFrom) {\n throw new Error('[memi-board] useMemiBoardSeo runtime not found')\n }\n\n const from = 'memi-board/runtime'\n addImports([\n { name: 'configureMemiBoard', from },\n { name: 'useMemiBoardConfig', from },\n { name: 'useMemiBoardAuth', from },\n { name: 'useMemiBoardPosts', from },\n { name: 'useMemiBoardComments', from },\n { name: 'useMemiBoardViews', from },\n { name: 'useMemiBoardModeration', from },\n // 이미지 압축 등 브라우저 전용 — SSR 서버 번들에 안 섞이도록 별도 서브패스에서 해석\n { name: 'useMemiBoardStorage', from: 'memi-board/storage' },\n { name: 'useMemiBoardSettings', from },\n { name: 'useMemiBoardUsers', from },\n { name: 'useMemiBoardPostSeo', from: seoFrom },\n { name: 'useMemiBoardListSeo', from: seoFrom },\n { name: 'fetchPublicPostForSeo', from },\n { name: 'fetchPublicListForSeo', from },\n { name: 'resolvePublicSeoDb', from },\n { name: 'versionHistory', from },\n { name: 'useMemiBoardI18n', from },\n { name: 'MEMI_BOARD_LOCALES', from },\n { name: 'useMemiBoardAutoTranslate', from },\n ])\n\n const runtimeConfig = JSON.stringify(options).replace(/</g, '\\\\u003c')\n const configPlugin = addTemplate({\n filename: 'memi-board.config.mjs',\n getContents: () => `\nimport { computed } from 'vue'\nimport {\n configureMemiBoard,\n useMemiBoardConfig,\n isMemiBoardI18nEnabled,\n loadMemiBoardLocale,\n memiBoardHostLocaleKey,\n normalizeMemiBoardLocale,\n} from 'memi-board/runtime'\n\nexport default defineNuxtPlugin({\n name: 'memi-board:config',\n // @nuxtjs/i18n 플러그인이 $i18n 을 만든 뒤 실행되어야 한다.\n dependsOn: ['i18n:plugin'],\n async setup(nuxtApp) {\n configureMemiBoard(${runtimeConfig})\n if (!isMemiBoardI18nEnabled()) {\n const fixed = normalizeMemiBoardLocale(useMemiBoardConfig().locale) ?? 'ko'\n await loadMemiBoardLocale(fixed)\n return\n }\n // 호스트가 @nuxtjs/i18n 을 쓰면 그 언어를 게시판 기본 언어로 쓴다.\n // 요청(SSR)마다 앱 인스턴스가 따로라 provide 로 넘긴다.\n const i18n = nuxtApp.$i18n\n if (!i18n?.locale) return\n const hostLocale = computed(() => i18n.locale.value)\n nuxtApp.vueApp.provide(memiBoardHostLocaleKey, hostLocale)\n const initial = normalizeMemiBoardLocale(hostLocale.value)\n if (initial) await loadMemiBoardLocale(initial)\n },\n})\n`,\n })\n addPlugin(configPlugin.dst)\n },\n})\n\nexport default memiBoardModule\n"],"mappings":"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"}
@@ -23,7 +23,7 @@ const props = withDefaults(defineProps<{
23
23
 
24
24
  const emit = defineEmits<{ saved: [category: BoardCategory] }>()
25
25
 
26
- const { t } = useMemiBoardI18n()
26
+ const { t, i18nEnabled, translationEnabled } = useMemiBoardI18n()
27
27
  const listViewOptions = computed<Array<{ label: string, value: BoardListView }>>(() => [
28
28
  { label: t('settings.option.default'), value: 'default' },
29
29
  { label: t('settings.listView.options.dense'), value: 'dense' },
@@ -149,7 +149,7 @@ watch(source, (category) => {
149
149
  }, { immediate: true })
150
150
 
151
151
  async function translateCopy() {
152
- if (!draft.value?.label.trim() || translating.value || saving.value) return
152
+ if (!translationEnabled.value || !draft.value?.label.trim() || translating.value || saving.value) return
153
153
  const current = draft.value
154
154
  const snapshot = { ...current }
155
155
  translating.value = true
@@ -177,7 +177,7 @@ async function save() {
177
177
  error.value = ''
178
178
  const snapshot = { ...draft.value, autoTranslate: draft.value.autoTranslate === true }
179
179
  let translateFailed = false
180
- if (snapshot.autoTranslate) {
180
+ if (translationEnabled.value && snapshot.autoTranslate) {
181
181
  try {
182
182
  const translated = await translateBoard(snapshot)
183
183
  snapshot.i18n = translated.i18n
@@ -275,6 +275,7 @@ async function save() {
275
275
  <div class="flex flex-wrap items-center gap-2">
276
276
  <UInput v-model="draft.label" class="min-w-0 flex-1" :disabled="translating" @update:model-value="saved = false" />
277
277
  <UButton
278
+ v-if="translationEnabled"
278
279
  :label="t('settings.autoTranslate.action')"
279
280
  icon="i-lucide-languages"
280
281
  color="neutral"
@@ -297,15 +298,15 @@ async function save() {
297
298
  :placeholder="t('settings.description.placeholder')"
298
299
  @update:model-value="saved = false"
299
300
  />
300
- <div class="flex items-center justify-between gap-3">
301
+ <div v-if="translationEnabled" class="flex items-center justify-between gap-3">
301
302
  <div>
302
303
  <p class="text-sm font-medium">{{ t('settings.autoTranslate.label') }}</p>
303
304
  <p class="text-xs text-muted">{{ t('settings.autoTranslate.help') }}</p>
304
305
  </div>
305
306
  <USwitch v-model="draft.autoTranslate" size="sm" @update:model-value="saved = false" />
306
307
  </div>
307
- <p class="text-xs text-muted">{{ t('settings.autoTranslate.manualHelp') }}</p>
308
- <p v-if="boardCopyIsTranslated(draft)" class="text-xs text-primary">{{ t(translatedDraft ? 'settings.autoTranslate.pending' : 'settings.autoTranslate.ready') }}</p>
308
+ <p v-if="translationEnabled" class="text-xs text-muted">{{ t('settings.autoTranslate.manualHelp') }}</p>
309
+ <p v-if="translationEnabled && boardCopyIsTranslated(draft)" class="text-xs text-primary">{{ t(translatedDraft ? 'settings.autoTranslate.pending' : 'settings.autoTranslate.ready') }}</p>
309
310
  </div>
310
311
  </div>
311
312
  <div v-if="isAdmin" class="flex flex-col gap-2">
@@ -324,7 +325,7 @@ async function save() {
324
325
  <div><p class="text-sm font-medium">{{ t('settings.editorType.label') }}</p><p class="text-xs text-muted">{{ t('settings.editorType.help') }}</p></div>
325
326
  <MemiBoardOptionCards v-model="draft.editorType" :options="editorTypeOptions" @update:model-value="saved = false" />
326
327
  </div>
327
- <div class="grid gap-2 sm:grid-cols-[9rem_1fr] sm:items-center">
328
+ <div v-if="i18nEnabled" class="grid gap-2 sm:grid-cols-[9rem_1fr] sm:items-center">
328
329
  <div><p class="text-sm font-medium">{{ t('settings.locale.label') }}</p><p class="text-xs text-muted">{{ t('settings.locale.help') }}</p></div>
329
330
  <USelect
330
331
  :model-value="draft.locale || LOCALE_AUTO"
@@ -45,7 +45,7 @@ const resolvedBoardId = computed(() => props.boardId || props.fixedCategory || '
45
45
  const { createPostId, getPost, createPost, updatePost, savePostTranslations, resolveUniqueSlug } = useMemiBoardPosts(resolvedBoardId)
46
46
  const { getBoard, ensureSettings } = useMemiBoardSettings()
47
47
  // 보드 설정 언어 → 호스트 언어 순.
48
- const { t, dir, locale } = useMemiBoardI18n({ boardLocale: () => getBoard(resolvedBoardId.value)?.locale })
48
+ const { t, dir, locale, translationEnabled } = useMemiBoardI18n({ boardLocale: () => getBoard(resolvedBoardId.value)?.locale })
49
49
  // provide 한 컴포넌트 자신은 inject 를 못 받으므로 AI 프롬프트 언어를 직접 넘긴다.
50
50
  const { checkText } = useMemiBoardModeration({ locale })
51
51
  const { uploadEditorImage } = useMemiBoardStorage()
@@ -657,9 +657,9 @@ const aiMenuItems = computed(() => [
657
657
  { label: t('editor.ai.continue'), icon: 'i-lucide-text-cursor-input', onSelect: () => runWritingAssistant('continue') },
658
658
  { label: t('editor.ai.summarize'), icon: 'i-lucide-scan-text', onSelect: () => runWritingAssistant('summarize') },
659
659
  ],
660
- [
660
+ ...(translationEnabled.value ? [[
661
661
  { label: t('editor.ai.translateAll'), icon: 'i-lucide-languages', onSelect: () => translateAllLocales() },
662
- ],
662
+ ]] : []),
663
663
  [
664
664
  { label: t('editor.ai.title'), icon: 'i-lucide-heading-1', onSelect: () => runWritingAssistant('title') },
665
665
  ],
@@ -738,7 +738,7 @@ function translationKey(body: string, storedTitle: string): string {
738
738
  }
739
739
 
740
740
  async function translateAllLocales() {
741
- if (aiRunning.value) return
741
+ if (!translationEnabled.value || aiRunning.value) return
742
742
  aiError.value = ''
743
743
  aiNotice.value = ''
744
744
  const body = currentBodyContent()
@@ -1044,7 +1044,7 @@ async function handleSubmit() {
1044
1044
  submitHint.value = t('editor.hint.saving')
1045
1045
  const bodyContent = currentBodyContent()
1046
1046
  const storedTitle = currentStoredTitle(bodyContent)
1047
- const translations = pendingTranslations.value
1047
+ const translations = translationEnabled.value && pendingTranslations.value
1048
1048
  && pendingTranslationKey.value === translationKey(bodyContent, storedTitle)
1049
1049
  ? pendingTranslations.value
1050
1050
  : undefined
@@ -27,7 +27,7 @@ const emit = defineEmits<{
27
27
  saved: [categories: BoardCategory[]]
28
28
  }>()
29
29
 
30
- const { t } = useMemiBoardI18n()
30
+ const { t, i18nEnabled, translationEnabled } = useMemiBoardI18n()
31
31
  const listViewOptions = computed<Array<{ label: string, value: BoardListView }>>(() => [
32
32
  { label: t('settings.option.default'), value: 'default' },
33
33
  { label: t('settings.listView.options.dense'), value: 'dense' },
@@ -178,7 +178,7 @@ function categoryTo(id: string) {
178
178
  }
179
179
 
180
180
  async function translateCopy(category: BoardCategory) {
181
- if (!category.label.trim() || translatingId.value || savingId.value) return
181
+ if (!translationEnabled.value || !category.label.trim() || translatingId.value || savingId.value) return
182
182
  const snapshot = { ...category }
183
183
  translatingId.value = category.id
184
184
  savedId.value = null
@@ -205,7 +205,7 @@ async function save(category: BoardCategory, index: number) {
205
205
  savingId.value = category.id
206
206
  category.autoTranslate = category.autoTranslate === true
207
207
  let translateFailed = false
208
- if (category.autoTranslate) {
208
+ if (translationEnabled.value && category.autoTranslate) {
209
209
  try {
210
210
  const translated = await translateBoard(category)
211
211
  category.i18n = translated.i18n
@@ -343,6 +343,7 @@ async function runDeleteAll() {
343
343
  <div class="flex flex-wrap items-center gap-2">
344
344
  <UInput v-model="category.label" class="min-w-0 flex-1" :disabled="translatingId === category.id" @update:model-value="savedId = null" />
345
345
  <UButton
346
+ v-if="translationEnabled"
346
347
  :label="t('settings.autoTranslate.action')"
347
348
  icon="i-lucide-languages"
348
349
  color="neutral"
@@ -365,15 +366,15 @@ async function runDeleteAll() {
365
366
  :placeholder="t('settings.description.placeholder')"
366
367
  @update:model-value="savedId = null"
367
368
  />
368
- <div class="flex items-center justify-between gap-3">
369
+ <div v-if="translationEnabled" class="flex items-center justify-between gap-3">
369
370
  <div>
370
371
  <p class="text-sm font-medium">{{ t('settings.autoTranslate.label') }}</p>
371
372
  <p class="text-xs text-muted">{{ t('settings.autoTranslate.help') }}</p>
372
373
  </div>
373
374
  <USwitch v-model="category.autoTranslate" size="sm" @update:model-value="savedId = null" />
374
375
  </div>
375
- <p class="text-xs text-muted">{{ t('settings.autoTranslate.manualHelp') }}</p>
376
- <p v-if="boardCopyIsTranslated(category)" class="text-xs text-primary">{{ t(translatedDraftIds.has(category.id) ? 'settings.autoTranslate.pending' : 'settings.autoTranslate.ready') }}</p>
376
+ <p v-if="translationEnabled" class="text-xs text-muted">{{ t('settings.autoTranslate.manualHelp') }}</p>
377
+ <p v-if="translationEnabled && boardCopyIsTranslated(category)" class="text-xs text-primary">{{ t(translatedDraftIds.has(category.id) ? 'settings.autoTranslate.pending' : 'settings.autoTranslate.ready') }}</p>
377
378
  </div>
378
379
  </div>
379
380
  <div v-if="canManageStaffAssignment" class="flex flex-col gap-2">
@@ -403,7 +404,7 @@ async function runDeleteAll() {
403
404
  @update:model-value="savedId = null"
404
405
  />
405
406
  </div>
406
- <div class="grid gap-2 sm:grid-cols-[9rem_1fr] sm:items-center">
407
+ <div v-if="i18nEnabled" class="grid gap-2 sm:grid-cols-[9rem_1fr] sm:items-center">
407
408
  <div><p class="text-sm font-medium">{{ t('settings.locale.label') }}</p><p class="text-xs text-muted">{{ t('settings.locale.help') }}</p></div>
408
409
  <USelect
409
410
  :model-value="category.locale || LOCALE_AUTO"
@@ -1,8 +1,8 @@
1
- import { computed as e, getCurrentInstance as t, hasInjectionContext as n, inject as r, onServerPrefetch as i, provide as a, reactive as o, shallowRef as s, toValue as c, watch as l } from "vue";
2
- import { collection as u, doc as d } from "firebase/firestore";
1
+ import { computed as e, getCurrentInstance as t, hasInjectionContext as n, inject as r, onServerPrefetch as i, provide as a, reactive as o, shallowRef as s, toValue as c, watch as ee } from "vue";
2
+ import { collection as l, doc as u } from "firebase/firestore";
3
3
  //#region src/utils/boardPaths.ts
4
- var f = "memiBoardPosts", p = "memiBoardComments", m = "memiBoardLikes", h = "memiBoardReports", g = "memiBoardSettings", _ = "memiBoardUsers";
5
- function v(e) {
4
+ var d = "memiBoardPosts", f = "memiBoardComments", p = "memiBoardLikes", m = "memiBoardReports", h = "memiBoardSettings", g = "memiBoardUsers";
5
+ function _(e) {
6
6
  return {
7
7
  postsCollection: e.postsCollection?.trim() || "memiBoardPosts",
8
8
  commentsCollection: e.commentsCollection?.trim() || "memiBoardComments",
@@ -12,75 +12,75 @@ function v(e) {
12
12
  usersCollection: e.usersCollection?.trim() || "memiBoardUsers"
13
13
  };
14
14
  }
15
- function ee(e, t) {
16
- return u(e, t.settingsCollection);
15
+ function v(e, t) {
16
+ return l(e, t.settingsCollection);
17
17
  }
18
- function te(e, t, n) {
19
- return d(e, t.settingsCollection, n);
18
+ function y(e, t, n) {
19
+ return u(e, t.settingsCollection, n);
20
20
  }
21
- function ne(e, t) {
22
- return u(e, t.postsCollection);
21
+ function b(e, t) {
22
+ return l(e, t.postsCollection);
23
23
  }
24
- function y(e, t, n) {
25
- return d(e, t.postsCollection, n);
24
+ function te(e, t, n) {
25
+ return u(e, t.postsCollection, n);
26
26
  }
27
- function b(e, t, n) {
28
- return d(e, t.postsCollection, n, "body", "main");
27
+ function ne(e, t, n) {
28
+ return u(e, t.postsCollection, n, "body", "main");
29
29
  }
30
- function x(e, t) {
31
- return u(e, t.commentsCollection);
30
+ function re(e, t) {
31
+ return l(e, t.commentsCollection);
32
32
  }
33
- function S(e, t, n) {
34
- return d(e, t.commentsCollection, n);
33
+ function ie(e, t, n) {
34
+ return u(e, t.commentsCollection, n);
35
35
  }
36
- function C(e, t) {
37
- return u(e, t.likesCollection);
36
+ function ae(e, t) {
37
+ return l(e, t.likesCollection);
38
38
  }
39
- function w(e, t) {
39
+ function x(e, t) {
40
40
  return `${e}_${t}`;
41
41
  }
42
- function T(e, t, n, r) {
43
- return d(e, t.likesCollection, w(n, r));
42
+ function oe(e, t, n, r) {
43
+ return u(e, t.likesCollection, x(n, r));
44
44
  }
45
- function re(e, t) {
46
- return u(e, t.reportsCollection);
45
+ function se(e, t) {
46
+ return l(e, t.reportsCollection);
47
47
  }
48
- function E(e, t) {
48
+ function S(e, t) {
49
49
  return `${e}_${t}`;
50
50
  }
51
- function ie(e, t, n, r) {
52
- return d(e, t.reportsCollection, E(n, r));
51
+ function ce(e, t, n, r) {
52
+ return u(e, t.reportsCollection, S(n, r));
53
53
  }
54
- function ae(e, t) {
55
- return u(e, t.usersCollection);
54
+ function le(e, t) {
55
+ return l(e, t.usersCollection);
56
56
  }
57
- function oe(e, t, n) {
58
- return d(e, t.usersCollection, n);
57
+ function ue(e, t, n) {
58
+ return u(e, t.usersCollection, n);
59
59
  }
60
- function se(e, t, n) {
61
- return d(e, t.usersCollection, n, "private", "info");
60
+ function de(e, t, n) {
61
+ return u(e, t.usersCollection, n, "private", "info");
62
62
  }
63
- function ce(e, t, n, r) {
64
- return d(e, t.usersCollection, n, "memos", r);
63
+ function C(e, t, n, r) {
64
+ return u(e, t.usersCollection, n, "memos", r);
65
65
  }
66
- function le(e, t) {
66
+ function fe(e, t) {
67
67
  return `${e.postsCollection}/${t}`;
68
68
  }
69
- function ue(e, t) {
69
+ function pe(e, t) {
70
70
  return `${e.postsCollection}/${e.usersCollection}/${t}`;
71
71
  }
72
72
  //#endregion
73
73
  //#region src/config.ts
74
- var D = "__MEMI_BOARD_CONFIG__";
75
- function O() {
74
+ var w = "__MEMI_BOARD_CONFIG__";
75
+ function T() {
76
76
  let e = globalThis;
77
- return e[D] || (e[D] = o({
78
- postsCollection: f,
79
- commentsCollection: p,
80
- likesCollection: m,
81
- reportsCollection: h,
82
- settingsCollection: g,
83
- usersCollection: _,
77
+ return e[w] || (e[w] = o({
78
+ postsCollection: d,
79
+ commentsCollection: f,
80
+ likesCollection: p,
81
+ reportsCollection: m,
82
+ settingsCollection: h,
83
+ usersCollection: g,
84
84
  auth: { providers: ["google", "apple"] },
85
85
  moderation: {
86
86
  enabled: !0,
@@ -98,17 +98,32 @@ function O() {
98
98
  defaultOgImage: "",
99
99
  basePath: "/board"
100
100
  }
101
- })), e[D];
101
+ })), e[w];
102
102
  }
103
- function de(e) {
104
- let t = O();
105
- e.postsCollection != null && e.postsCollection !== "" && (t.postsCollection = e.postsCollection.trim()), e.commentsCollection != null && e.commentsCollection !== "" && (t.commentsCollection = e.commentsCollection.trim()), e.likesCollection != null && e.likesCollection !== "" && (t.likesCollection = e.likesCollection.trim()), e.reportsCollection != null && e.reportsCollection !== "" && (t.reportsCollection = e.reportsCollection.trim()), e.settingsCollection != null && e.settingsCollection !== "" && (t.settingsCollection = e.settingsCollection.trim()), e.usersCollection != null && e.usersCollection !== "" && (t.usersCollection = e.usersCollection.trim()), e.auth && Object.assign(t.auth, e.auth), e.moderation && Object.assign(t.moderation, e.moderation), e.seo && Object.assign(t.seo, e.seo), e.roleLabels && (t.roleLabels = { ...e.roleLabels }), e.locale && (t.locale = e.locale);
103
+ function me(e) {
104
+ let t = T();
105
+ e.postsCollection != null && e.postsCollection !== "" && (t.postsCollection = e.postsCollection.trim()), e.commentsCollection != null && e.commentsCollection !== "" && (t.commentsCollection = e.commentsCollection.trim()), e.likesCollection != null && e.likesCollection !== "" && (t.likesCollection = e.likesCollection.trim()), e.reportsCollection != null && e.reportsCollection !== "" && (t.reportsCollection = e.reportsCollection.trim()), e.settingsCollection != null && e.settingsCollection !== "" && (t.settingsCollection = e.settingsCollection.trim()), e.usersCollection != null && e.usersCollection !== "" && (t.usersCollection = e.usersCollection.trim()), e.auth && Object.assign(t.auth, e.auth), e.moderation && Object.assign(t.moderation, e.moderation), e.seo && Object.assign(t.seo, e.seo), e.i18n && (t.i18n = {
106
+ ...t.i18n,
107
+ ...e.i18n
108
+ }), e.translation && (t.translation = {
109
+ ...t.translation,
110
+ ...e.translation
111
+ }), e.roleLabels && (t.roleLabels = { ...e.roleLabels }), e.locale && (t.locale = e.locale);
112
+ }
113
+ function E() {
114
+ return T();
115
+ }
116
+ function D() {
117
+ return E().i18n?.enabled !== !1;
118
+ }
119
+ function O() {
120
+ return D() && E().translation?.enabled !== !1;
106
121
  }
107
122
  function k() {
108
- return O();
123
+ if (!O()) throw Object.assign(/* @__PURE__ */ Error("Content translation is disabled for this board service."), { code: "memi-board/translation-disabled" });
109
124
  }
110
125
  function A() {
111
- return v(k());
126
+ return _(E());
112
127
  }
113
128
  //#endregion
114
129
  //#region src/i18n/locales.ts
@@ -879,10 +894,10 @@ function V(e) {
879
894
  function H(e) {
880
895
  return e === "ko" ? Promise.resolve() : Promise.all([V(e), V("en")]).then(() => void 0);
881
896
  }
882
- function fe(e) {
897
+ function U(e) {
883
898
  return !!B().messages[e];
884
899
  }
885
- function U(e, t) {
900
+ function W(e, t) {
886
901
  let n = e;
887
902
  for (let e of t.split(".")) {
888
903
  if (!n || typeof n == "string") return;
@@ -890,36 +905,36 @@ function U(e, t) {
890
905
  }
891
906
  return typeof n == "string" ? n : void 0;
892
907
  }
893
- function pe(e, t) {
908
+ function he(e, t) {
894
909
  return t ? e.replace(/\{(\w+)\}/g, (e, n) => {
895
910
  let r = t[n];
896
911
  return r == null ? e : String(r);
897
912
  }) : e;
898
913
  }
899
- function W(e, t, n) {
914
+ function G(e, t, n) {
900
915
  let r = B();
901
916
  r.version.value;
902
- let i = e ?? "ko", a = U(r.messages[i], t) ?? (i === "ko" ? void 0 : U(r.messages.en, t)) ?? U(r.messages.ko, t);
903
- return a == null ? t : pe(a, n);
917
+ let i = e ?? "ko", a = W(r.messages[i], t) ?? (i === "ko" ? void 0 : W(r.messages.en, t)) ?? W(r.messages.ko, t);
918
+ return a == null ? t : he(a, n);
904
919
  }
905
920
  //#endregion
906
921
  //#region src/utils/formatDate.ts
907
- function G(e) {
922
+ function K(e) {
908
923
  return I(e ?? "ko").intl;
909
924
  }
910
- function K(e, t) {
911
- return e?.toDate ? e.toDate().toLocaleDateString(G(t)) : "";
925
+ function q(e, t) {
926
+ return e?.toDate ? e.toDate().toLocaleDateString(K(t)) : "";
912
927
  }
913
- var me = 60, q = 3600, he = 24 * q;
914
- function J(e, t = Date.now(), n) {
915
- if (!e?.toDate) return W(n, "common.date.justNow");
928
+ var ge = 60, J = 3600, _e = 24 * J;
929
+ function Y(e, t = Date.now(), n) {
930
+ if (!e?.toDate) return G(n, "common.date.justNow");
916
931
  let r = Math.max(0, Math.round((t - e.toDate().getTime()) / 1e3));
917
- if (r < 45) return W(n, "common.date.fewSecondsAgo");
918
- let i = new Intl.RelativeTimeFormat(G(n), { numeric: "always" }), a = r / he;
919
- return r < 90 ? i.format(-1, "minute") : r < 2700 ? i.format(-Math.round(r / me), "minute") : r < 5400 ? i.format(-1, "hour") : r < 22 * q ? i.format(-Math.round(r / q), "hour") : r < 36 * q ? i.format(-1, "day") : a < 26 ? i.format(-Math.round(a), "day") : a < 46 ? i.format(-1, "month") : a < 320 ? i.format(-Math.round(a / 30.4), "month") : a < 548 ? i.format(-1, "year") : i.format(-Math.round(a / 365), "year");
932
+ if (r < 45) return G(n, "common.date.fewSecondsAgo");
933
+ let i = new Intl.RelativeTimeFormat(K(n), { numeric: "always" }), a = r / _e;
934
+ return r < 90 ? i.format(-1, "minute") : r < 2700 ? i.format(-Math.round(r / ge), "minute") : r < 5400 ? i.format(-1, "hour") : r < 22 * J ? i.format(-Math.round(r / J), "hour") : r < 36 * J ? i.format(-1, "day") : a < 26 ? i.format(-Math.round(a), "day") : a < 46 ? i.format(-1, "month") : a < 320 ? i.format(-Math.round(a / 30.4), "month") : a < 548 ? i.format(-1, "year") : i.format(-Math.round(a / 365), "year");
920
935
  }
921
- function Y(e, t) {
922
- return e?.toDate ? new Intl.DateTimeFormat(G(t), {
936
+ function X(e, t) {
937
+ return e?.toDate ? new Intl.DateTimeFormat(K(t), {
923
938
  year: "numeric",
924
939
  month: "long",
925
940
  day: "numeric",
@@ -927,43 +942,45 @@ function Y(e, t) {
927
942
  minute: "2-digit",
928
943
  second: "2-digit",
929
944
  hourCycle: "h23"
930
- }).format(e.toDate()) : W(t, "common.date.checking");
945
+ }).format(e.toDate()) : G(t, "common.date.checking");
931
946
  }
932
- function X(e, t, n) {
933
- let r = Y(e, n), i = e?.toMillis?.(), a = t?.toMillis?.();
934
- return a == null || i == null || i === a ? [r] : [W(n, "common.date.createdAt", { time: r }), W(n, "common.date.updatedAt", { time: Y(t, n) })];
947
+ function Z(e, t, n) {
948
+ let r = X(e, n), i = e?.toMillis?.(), a = t?.toMillis?.();
949
+ return a == null || i == null || i === a ? [r] : [G(n, "common.date.createdAt", { time: r }), G(n, "common.date.updatedAt", { time: X(t, n) })];
935
950
  }
936
951
  //#endregion
937
952
  //#region src/i18n/useMemiBoardI18n.ts
938
- var Z = "memiBoard:hostLocale", Q = Symbol("memiBoard:boardLocale");
939
- function ge(o = {}) {
940
- let s = n(), u = s ? r(Q, null) : null, d = s ? r(Z, null) : null, f = k(), p = e(() => F(c(o.boardLocale)) ?? u?.value ?? F(c(d)) ?? F(f.locale) ?? "ko");
941
- o.boardLocale !== void 0 && s && a(Q, p), l(p, (e) => void H(e), { immediate: !0 }), t() && i(() => H(p.value));
942
- let m = e(() => I(p.value));
953
+ var Q = "memiBoard:hostLocale", $ = Symbol("memiBoard:boardLocale");
954
+ function ve(o = {}) {
955
+ let s = n(), l = s ? r($, null) : null, u = s ? r(Q, null) : null, d = E(), f = e(D), p = e(O), m = e(() => f.value ? F(c(o.boardLocale)) ?? l?.value ?? F(c(u)) ?? F(d.locale) ?? "ko" : F(d.locale) ?? "ko");
956
+ o.boardLocale !== void 0 && s && a($, m), ee(m, (e) => void H(e), { immediate: !0 }), t() && i(() => H(m.value));
957
+ let h = e(() => I(m.value));
943
958
  return {
944
- locale: p,
945
- dir: e(() => m.value.dir),
946
- intlLocale: e(() => m.value.intl),
959
+ locale: m,
960
+ i18nEnabled: f,
961
+ translationEnabled: p,
962
+ dir: e(() => h.value.dir),
963
+ intlLocale: e(() => h.value.intl),
947
964
  t: (e, t) => {
948
965
  let n = e.startsWith("common.role.") ? e.slice(12) : "";
949
- return f.roleLabels?.[n] ?? W(p.value, e, t);
966
+ return d.roleLabels?.[n] ?? G(m.value, e, t);
950
967
  },
951
- formatDate: (e) => K(e, p.value),
952
- formatFullDate: (e) => Y(e, p.value),
953
- formatRelativeDate: (e, t) => J(e, t, p.value),
954
- formatTimestampDetails: (e, t) => X(e, t, p.value),
955
- formatNumber: (e) => e.toLocaleString(m.value.intl)
968
+ formatDate: (e) => q(e, m.value),
969
+ formatFullDate: (e) => X(e, m.value),
970
+ formatRelativeDate: (e, t) => Y(e, t, m.value),
971
+ formatTimestampDetails: (e, t) => Z(e, t, m.value),
972
+ formatNumber: (e) => e.toLocaleString(h.value.intl)
956
973
  };
957
974
  }
958
975
  //#endregion
959
976
  //#region src/utils/slugify.ts
960
- function $(e) {
977
+ function ye(e) {
961
978
  return e.trim().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9가-힣ㄱ-ㅎㅏ-ㅣ-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
962
979
  }
963
- function _e(e, t = 60) {
980
+ function be(e, t = 60) {
964
981
  return e.replace(/[^a-zA-Z0-9가-힣ㄱ-ㅎ._-]/g, "-").slice(0, t);
965
982
  }
966
983
  //#endregion
967
- export { ae as A, ie as B, h as C, ue as D, ce as E, C as F, te as G, re as H, b as I, y as L, x as M, T as N, oe as O, w as P, le as R, f as S, _ as T, v as U, E as V, ee as W, de as _, K as a, p as b, X as c, W as d, M as f, F as g, I as h, ge as i, S as j, se as k, fe as l, j as m, $ as n, Y as o, N as p, Z as r, J as s, _e as t, H as u, A as v, g as w, m as x, k as y, ne as z };
984
+ export { pe as A, te as B, f as C, h as D, m as E, re as F, se as G, b as H, oe as I, y as J, _ as K, x as L, de as M, le as N, g as O, ie as P, ae as R, E as S, d as T, ce as U, fe as V, S as W, k as _, q as a, O as b, Z as c, G as d, M as f, F as g, I as h, ve as i, ue as j, C as k, U as l, j as m, ye as n, X as o, N as p, v as q, Q as r, Y as s, be as t, H as u, me as v, p as w, A as x, D as y, ne as z };
968
985
 
969
- //# sourceMappingURL=slugify-B05Qykf_.js.map
986
+ //# sourceMappingURL=slugify-DzXnx16m.js.map