diff --git a/components/admin/AdminMediaDetailModal.vue b/components/admin/AdminMediaDetailModal.vue
new file mode 100644
index 0000000..08e09ac
--- /dev/null
+++ b/components/admin/AdminMediaDetailModal.vue
@@ -0,0 +1,424 @@
+
+
+
+
+
diff --git a/docs/map.md b/docs/map.md
index e7e9367..a0f2508 100644
--- a/docs/map.md
+++ b/docs/map.md
@@ -192,6 +192,7 @@
| components/admin/AdminPostExportFileRow.vue | 관리자 사이트 설정 내보내기 작업의 분할 파일 선택 행 |
| components/admin/AdminMediaFolderPanel.vue | 관리자 미디어 화면의 폴더 사이드바(라이브러리 폴더 트리·폴더별 개수·드래그 드롭 이동·폴더 삭제 메뉴·폴더 추가 버튼·시스템 이미지 탭 안내 패널) |
| components/admin/AdminMediaGrid.vue | 관리자 미디어 화면의 그리드 카드 목록(미디어 선택 체크·이미지/영상/파일 썸네일·사용처 개수·상태 배지·드래그 시작·상세 열기 이벤트) |
+| components/admin/AdminMediaDetailModal.vue | 관리자 미디어 화면의 상세 모달(미리보기·파일 정보·썸네일 상태·폴더·사용 현황·파일명·삭제 액션 표시) |
| components/admin/AdminMediaVideoThumbnail.vue | 관리자 미디어 목록 비디오 항목의 초반 프레임 캔버스 썸네일 |
| components/admin/AdminPostForm.vue | 관리자 글 작성/수정 폼, Ghost형 툴바(왼쪽 상태 텍스트·Publish/Update/Unpublish/Unschedule, 서버 반영 상태 기준 분기), 초안만 서버 디바운스 자동 저장·자동 저장 실패 payload 반복 재시도 차단·자동 저장 실패/진행 중 이탈 차단·신규 임시 슬러그·중복 슬러그 필드 하단 오류 표시·중복 슬러그 중 Publish/Update/최종 저장 비활성화·저장 중 추가 변경 재자동저장·발행 저장 중 이탈 확인·발행·예약·멤버십·비공개 상태 저장, 미저장 비교에서 태그 표시명 보조값 제외, 공개 상태 전환 시 빈 발행 시각 현재 시각 보정, 발행 모달(중앙 배치), 설정 패널 상태와 이벤트 연결, 미리보기 emit·미저장 이탈 가드, 자동 생성 썸네일을 숨기는 원본 전용 대표 이미지 선택(서버 페이지네이션·파일명/사용처 검색·번호형 페이지 이동·작은 파생 이미지 우선 미리보기·고정 높이 모달), 대표 이미지 본문 상단 표시 토글, 추천 글 토글, 사이트 설정 기준 태그 최대 개수 제한, 태그 색상 배지 다중 입력·최근 사용 태그 5개 우선 추천·메인 태그 드롭다운·부분 검색 추천, 새 태그 입력명 유지와 `/`→`-` 슬러그 변환 |
| components/admin/AdminPostSettingsSidebar.vue | 관리자 게시물 작성/수정 우측 설정 사이드바(Post URL·상태·발행 시각·요약·태그·대표 이미지 표시·추천 글·검색엔진 노출·본문 통계·삭제 버튼·블록 설정 패널 오버레이) |
diff --git a/docs/todo.md b/docs/todo.md
index 31e7d22..b414179 100644
--- a/docs/todo.md
+++ b/docs/todo.md
@@ -5,7 +5,7 @@
- 거대 컴포넌트 책임 분리 진행.
- `components/admin/AdminMarkdownEditor.vue`: 미디어 모달, 슬래시 명령, 히스토리·선택 복원, 라이브/소스 전환 로직을 단계적으로 composable 또는 하위 컴포넌트로 분리.
- `components/content/ContentMarkdownRenderer.vue`: 파싱·블록 렌더링·라이브 편집 중개·라이트박스·광고 삽입 책임을 분리.
- - `pages/admin/media/index.vue`: 상세 모달, 재생성 액션을 작은 단위로 분리.
+ - `pages/admin/media/index.vue`: 재생성 액션을 작은 단위로 분리.
- 미디어 카탈로그 전환 코드 수명 정리.
- 운영 `MEDIA_CATALOG_MODE=database` 안정화 후 `shadow`/`filesystem` 런타임 분기 축소 가능 여부 검토.
- fallback은 운영 안정성에 필요하므로 제거 전 reconcile·로그·테스트 기준을 먼저 정리.
diff --git a/docs/update.md b/docs/update.md
index 7c2715f..e66b4a8 100644
--- a/docs/update.md
+++ b/docs/update.md
@@ -1,5 +1,11 @@
# 업데이트 이력
+## v1.11.46
+
+- 관리자 미디어 화면의 상세 모달 렌더링을 `AdminMediaDetailModal` 컴포넌트로 분리.
+- 미디어 미리보기·파일 정보·사용 현황·폴더 입력·파일명 입력·썸네일 생성 버튼 표시 책임을 하위 컴포넌트로 이동.
+- 미디어 화면은 선택 항목 상태와 다운로드·저장·삭제·재생성 이벤트 연결만 담당하도록 정리.
+
## v1.11.45
- 관리자 미디어 화면의 그리드 카드 목록 렌더링을 `AdminMediaGrid` 컴포넌트로 분리.
diff --git a/package-lock.json b/package-lock.json
index b02f5c0..dda2adc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "sori.studio",
- "version": "1.11.45",
+ "version": "1.11.46",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sori.studio",
- "version": "1.11.45",
+ "version": "1.11.46",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",
diff --git a/package.json b/package.json
index 27525cd..e7cf050 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sori.studio",
- "version": "1.11.45",
+ "version": "1.11.46",
"private": true,
"type": "module",
"imports": {
diff --git a/pages/admin/media/index.vue b/pages/admin/media/index.vue
index a468584..afeadc9 100644
--- a/pages/admin/media/index.vue
+++ b/pages/admin/media/index.vue
@@ -547,26 +547,6 @@ const setMediaUsageFilter = (unusedOnly) => {
clearMediaSelection()
}
-/**
- * ISO 시각을 짧은 로캘 문자열로 표시한다.
- * @param {string | null} iso - ISO 시각
- * @returns {string} 표시 문자열
- */
-const formatDateTime = (iso) => {
- if (!iso) {
- return '—'
- }
-
- try {
- return new Date(iso).toLocaleString('ko-KR', {
- dateStyle: 'medium',
- timeStyle: 'short'
- })
- } catch {
- return '—'
- }
-}
-
/**
* 상세 모달에서 선택된 미디어를 브라우저로 내려받는다.
* @returns {void}
@@ -681,23 +661,6 @@ const selectedDeletableMediaItems = computed(() => {
return filteredMediaItems.value.filter((item) => selectedUrlSet.has(item.url) && !isMediaItemLocked(item))
})
-/**
- * 파일 크기 표시 문자열 생성
- * @param {number} size - 파일 크기
- * @returns {string} 표시 문자열
- */
-const formatFileSize = (size) => {
- if (size < 1024) {
- return `${size} B`
- }
-
- if (size < 1024 * 1024) {
- return `${(size / 1024).toFixed(1)} KB`
- }
-
- return `${(size / 1024 / 1024).toFixed(1)} MB`
-}
-
/**
* 미디어 선택 여부 확인
* @param {Object} item - 미디어 항목
@@ -1538,288 +1501,38 @@ watch(() => mediaPagination.value.page, (page) => {
-
+ v-model:editing-category="editingCategory"
+ v-model:editing-name="editingName"
+ :can-edit-media-category="canEditMediaCategory"
+ :can-regenerate-page-cover-thumbnail="canRegeneratePageCoverThumbnail"
+ :can-regenerate-post-body-thumbnail="canRegeneratePostBodyThumbnail"
+ :can-regenerate-post-thumbnail="canRegeneratePostThumbnail"
+ :can-rename-media-item="canRenameMediaItem"
+ :deleting-url="deletingUrl"
+ :folders="normalizedFolders"
+ :get-media-badge-label="getMediaBadgeLabel"
+ :get-media-item-kind="getMediaItemKind"
+ :get-media-preview-url="getMediaPreviewUrl"
+ :get-post-body-thumbnail-resolution-label="getPostBodyThumbnailResolutionLabel"
+ :is-media-item-locked="isMediaItemLocked"
+ :is-page-cover-thumbnail-disk-item="isPageCoverThumbnailDiskItem"
+ :is-post-body-thumbnail-disk-item="isPostBodyThumbnailDiskItem"
+ :is-post-card-thumbnail-disk-item="isPostCardThumbnailDiskItem"
+ :item="selectedMedia"
+ :media-thumbnail-root="MEDIA_THUMBNAIL_ROOT"
+ :regenerating-body-thumbnail-url="regeneratingBodyThumbnailUrl"
+ :regenerating-thumbnail-url="regeneratingThumbnailUrl"
+ @close="closeMediaDetail"
+ @delete="deleteMedia"
+ @download="downloadSelectedMedia"
+ @regenerate-body-thumbnail="regeneratePostBodyThumbnail"
+ @regenerate-page-cover-thumbnail="regeneratePageCoverThumbnail"
+ @regenerate-post-thumbnail="regeneratePostThumbnail"
+ @rename="renameMedia"
+ @save-category="saveMediaCategory"
+ />