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/README.md +56 -122
- package/dist/config.d.ts +14 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +967 -951
- package/dist/index.js.map +1 -1
- package/dist/module.mjs +7 -0
- package/dist/module.mjs.map +1 -1
- package/dist/runtime/components/CategorySettings.vue +8 -7
- package/dist/runtime/components/Editor.vue +5 -5
- package/dist/runtime/components/Settings.vue +8 -7
- package/dist/{slugify-B05Qykf_.js → slugify-DzXnx16m.js} +109 -92
- package/dist/slugify-DzXnx16m.js.map +1 -0
- package/dist/storage.js +1 -1
- package/docs/firebase-setup.md +10 -5
- package/docs/host-integration.md +355 -0
- package/docs/internationalization.md +180 -0
- package/docs/roles-migration.md +4 -0
- package/package.json +3 -2
- package/dist/slugify-B05Qykf_.js.map +0 -1
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
|
package/dist/module.mjs.map
CHANGED
|
@@ -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":";;;AA4BA,IAAM,IAAsD,EAAyC;CACnG,MAAM;EACJ,MAAM;EACN,WAAW;CACb;CACA,MAAM,GAAS,GAAM;EACnB,IAAM,EAAE,eAAY,EAAe,YAAY,GAAG,GAW5C,IAAgB;GAJpB,EAAQ,mBAAmB;GAC3B,EAAQ,sBAAsB;GAC9B,EAAQ,cAAc;EAEF,CAAA,CAAW,MAAK,MAAK,EAAW,CAAC,CAAC;EACxD,IAAI,CAAC,GACH,MAAU,MAAM,6CAA6C;EAI/D,AAFA,EAAK,QAAQ,MAAM,UAAU,KAAK,YAAY,GAE9C,EAAK,QAAQ,SAAS,CAAC;EAMvB,IAAM,IAAc,EAAQ,IAAI;EAEhC,AADA,EAAK,QAAQ,KAAK,WAAW,CAAC,GAC9B,EAAK,QAAQ,KAAK,OAAO,OAAO,CAAC;EACjC,IAAM,oBAAU,IAAI,IAAI,CACtB,GAAI,EAAK,QAAQ,KAAK,OAAO,GAAG,SAAS,CAAC,GAC1C,CACF,CAAC;EAGD,AAFA,EAAK,QAAQ,KAAK,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAO,GAE/C,EAAK,QAAQ,KAAK,YAAY,CAAC;EAG/B,IAAM,oBAAS,IAAI,IAAI;GACrB,GAAI,EAAK,QAAQ,KAAK,QAAQ,UAAU,CAAC;GACzC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAGD,AAFA,EAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC,GAAG,CAAM,GAE7C,EAAK,QAAQ,KAAK,iBAAiB,CAAC;EACpC,IAAM,IAAU,IAAI,IAAI,EAAK,QAAQ,KAAK,aAAa,WAAW,CAAC,CAAC;EAcpE,AAbA,EAAQ,IAAI,YAAY,GACxB,EAAK,QAAQ,KAAK,aAAa,UAAU,CAAC,GAAG,CAAO,GAWpD,EAAK,QAAQ,YAAY,CAAC,GAC1B,EAAK,QAAQ,QAAQ,cAAc,CAAC;EACpC,IAAM,oBAAmB,IAAI,IAAI,CAC/B,GAAI,EAAK,QAAQ,QAAQ,UAAU,WAAW,CAAC,GAC/C,+BACF,CAAC;EAGD,AAFA,EAAK,QAAQ,QAAQ,UAAU,UAAU,CAAC,GAAG,CAAgB,GAE7D,EAAiB;GACf,MAAM;GACN,YAAY;GACZ,QAAQ;EAEV,CAAC;EASD,IAAM,IAAU;GAJd,EAAQ,uCAAuC;GAC/C,EAAQ,0CAA0C;GAClD,EAAQ,kCAAkC;EAE5B,CAAA,CAAc,MAAK,MAAK,EAAW,CAAC,CAAC;EACrD,IAAI,CAAC,GACH,MAAU,MAAM,gDAAgD;EAGlE,IAAM,IAAO;EACb,EAAW;GACT;IAAE,MAAM;IAAsB;GAAK;GACnC;IAAE,MAAM;IAAsB;GAAK;GACnC;IAAE,MAAM;IAAoB;GAAK;GACjC;IAAE,MAAM;IAAqB;GAAK;GAClC;IAAE,MAAM;IAAwB;GAAK;GACrC;IAAE,MAAM;IAAqB;GAAK;GAClC;IAAE,MAAM;IAA0B;GAAK;GAEvC;IAAE,MAAM;IAAuB,MAAM;GAAqB;GAC1D;IAAE,MAAM;IAAwB;GAAK;GACrC;IAAE,MAAM;IAAqB;GAAK;GAClC;IAAE,MAAM;IAAuB,MAAM;GAAQ;GAC7C;IAAE,MAAM;IAAuB,MAAM;GAAQ;GAC7C;IAAE,MAAM;IAAyB;GAAK;GACtC;IAAE,MAAM;IAAyB;GAAK;GACtC;IAAE,MAAM;IAAsB;GAAK;GACnC;IAAE,MAAM;IAAkB;GAAK;GAC/B;IAAE,MAAM;IAAoB;GAAK;GACjC;IAAE,MAAM;IAAsB;GAAK;GACnC;IAAE,MAAM;IAA6B;GAAK;EAC5C,CAAC;EAED,IAAM,IAAgB,KAAK,UAAU,CAAO,CAAC,CAAC,QAAQ,MAAM,SAAS,GAC/D,IAAe,EAAY;GAC/B,UAAU;GACV,mBAAmB
|
|
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
|
|
2
|
-
import { collection as
|
|
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
|
|
5
|
-
function
|
|
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
|
|
16
|
-
return
|
|
15
|
+
function v(e, t) {
|
|
16
|
+
return l(e, t.settingsCollection);
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
return
|
|
18
|
+
function y(e, t, n) {
|
|
19
|
+
return u(e, t.settingsCollection, n);
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
return
|
|
21
|
+
function b(e, t) {
|
|
22
|
+
return l(e, t.postsCollection);
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
return
|
|
24
|
+
function te(e, t, n) {
|
|
25
|
+
return u(e, t.postsCollection, n);
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
return
|
|
27
|
+
function ne(e, t, n) {
|
|
28
|
+
return u(e, t.postsCollection, n, "body", "main");
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
return
|
|
30
|
+
function re(e, t) {
|
|
31
|
+
return l(e, t.commentsCollection);
|
|
32
32
|
}
|
|
33
|
-
function
|
|
34
|
-
return
|
|
33
|
+
function ie(e, t, n) {
|
|
34
|
+
return u(e, t.commentsCollection, n);
|
|
35
35
|
}
|
|
36
|
-
function
|
|
37
|
-
return
|
|
36
|
+
function ae(e, t) {
|
|
37
|
+
return l(e, t.likesCollection);
|
|
38
38
|
}
|
|
39
|
-
function
|
|
39
|
+
function x(e, t) {
|
|
40
40
|
return `${e}_${t}`;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
return
|
|
42
|
+
function oe(e, t, n, r) {
|
|
43
|
+
return u(e, t.likesCollection, x(n, r));
|
|
44
44
|
}
|
|
45
|
-
function
|
|
46
|
-
return
|
|
45
|
+
function se(e, t) {
|
|
46
|
+
return l(e, t.reportsCollection);
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function S(e, t) {
|
|
49
49
|
return `${e}_${t}`;
|
|
50
50
|
}
|
|
51
|
-
function
|
|
52
|
-
return
|
|
51
|
+
function ce(e, t, n, r) {
|
|
52
|
+
return u(e, t.reportsCollection, S(n, r));
|
|
53
53
|
}
|
|
54
|
-
function
|
|
55
|
-
return
|
|
54
|
+
function le(e, t) {
|
|
55
|
+
return l(e, t.usersCollection);
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
return
|
|
57
|
+
function ue(e, t, n) {
|
|
58
|
+
return u(e, t.usersCollection, n);
|
|
59
59
|
}
|
|
60
|
-
function
|
|
61
|
-
return
|
|
60
|
+
function de(e, t, n) {
|
|
61
|
+
return u(e, t.usersCollection, n, "private", "info");
|
|
62
62
|
}
|
|
63
|
-
function
|
|
64
|
-
return
|
|
63
|
+
function C(e, t, n, r) {
|
|
64
|
+
return u(e, t.usersCollection, n, "memos", r);
|
|
65
65
|
}
|
|
66
|
-
function
|
|
66
|
+
function fe(e, t) {
|
|
67
67
|
return `${e.postsCollection}/${t}`;
|
|
68
68
|
}
|
|
69
|
-
function
|
|
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
|
|
75
|
-
function
|
|
74
|
+
var w = "__MEMI_BOARD_CONFIG__";
|
|
75
|
+
function T() {
|
|
76
76
|
let e = globalThis;
|
|
77
|
-
return e[
|
|
78
|
-
postsCollection:
|
|
79
|
-
commentsCollection:
|
|
80
|
-
likesCollection:
|
|
81
|
-
reportsCollection:
|
|
82
|
-
settingsCollection:
|
|
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[
|
|
101
|
+
})), e[w];
|
|
102
102
|
}
|
|
103
|
-
function
|
|
104
|
-
let 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.
|
|
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
|
-
|
|
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
|
|
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
|
|
897
|
+
function U(e) {
|
|
883
898
|
return !!B().messages[e];
|
|
884
899
|
}
|
|
885
|
-
function
|
|
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
|
|
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
|
|
914
|
+
function G(e, t, n) {
|
|
900
915
|
let r = B();
|
|
901
916
|
r.version.value;
|
|
902
|
-
let i = e ?? "ko", a =
|
|
903
|
-
return a == null ? t :
|
|
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
|
|
922
|
+
function K(e) {
|
|
908
923
|
return I(e ?? "ko").intl;
|
|
909
924
|
}
|
|
910
|
-
function
|
|
911
|
-
return e?.toDate ? e.toDate().toLocaleDateString(
|
|
925
|
+
function q(e, t) {
|
|
926
|
+
return e?.toDate ? e.toDate().toLocaleDateString(K(t)) : "";
|
|
912
927
|
}
|
|
913
|
-
var
|
|
914
|
-
function
|
|
915
|
-
if (!e?.toDate) return
|
|
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
|
|
918
|
-
let i = new Intl.RelativeTimeFormat(
|
|
919
|
-
return r < 90 ? i.format(-1, "minute") : r < 2700 ? i.format(-Math.round(r /
|
|
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
|
|
922
|
-
return e?.toDate ? new Intl.DateTimeFormat(
|
|
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()) :
|
|
945
|
+
}).format(e.toDate()) : G(t, "common.date.checking");
|
|
931
946
|
}
|
|
932
|
-
function
|
|
933
|
-
let r =
|
|
934
|
-
return a == null || i == null || i === a ? [r] : [
|
|
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
|
|
939
|
-
function
|
|
940
|
-
let s = n(),
|
|
941
|
-
o.boardLocale !== void 0 && s && a(
|
|
942
|
-
let
|
|
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:
|
|
945
|
-
|
|
946
|
-
|
|
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
|
|
966
|
+
return d.roleLabels?.[n] ?? G(m.value, e, t);
|
|
950
967
|
},
|
|
951
|
-
formatDate: (e) =>
|
|
952
|
-
formatFullDate: (e) =>
|
|
953
|
-
formatRelativeDate: (e, t) =>
|
|
954
|
-
formatTimestampDetails: (e, t) =>
|
|
955
|
-
formatNumber: (e) => e.toLocaleString(
|
|
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
|
|
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
|
|
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 {
|
|
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-
|
|
986
|
+
//# sourceMappingURL=slugify-DzXnx16m.js.map
|