주요 수정사항: - 하이라이트 생성 시 color → highlight_color 필드명 수정으로 색상 전달 문제 해결 - 분홍색을 더 연하게 변경하여 글씨 가독성 향상 - 다중 하이라이트 렌더링을 위아래 균등 분할로 개선 - CSS highlight-span 클래스 추가 및 색상 적용 강화 - 하이라이트 생성/렌더링 과정에 상세한 디버깅 로그 추가 UI 개선: - 단일 하이라이트: 선택한 색상으로 정확히 표시 - 다중 하이라이트: 위아래로 균등하게 색상 분할 표시 - 메모 입력 모달에서 선택된 텍스트 표시 개선 버그 수정: - 프론트엔드-백엔드 API 스키마 불일치 해결 - CSS 스타일 우선순위 문제 해결 - 하이라이트 색상이 노랑색으로만 표시되던 문제 해결
953 lines
58 KiB
HTML
953 lines
58 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>문서 뷰어 - Document Server</title>
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📄</text></svg>">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/css/viewer.css">
|
|
</head>
|
|
<body class="bg-gray-50 min-h-screen">
|
|
<!-- 인증 확인 및 리다이렉트 -->
|
|
<script>
|
|
// 페이지 로드 시 인증 상태 확인
|
|
(function() {
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
console.log('🔐 인증 토큰이 없습니다. 메인 페이지로 리다이렉트합니다.');
|
|
window.location.href = '/';
|
|
return;
|
|
}
|
|
console.log('✅ 인증 토큰 확인됨:', token.substring(0, 20) + '...');
|
|
})();
|
|
</script>
|
|
|
|
<div x-data="documentViewer" x-init="init()">
|
|
<!-- 헤더 - 투명하고 세련된 3줄 디자인 -->
|
|
<header class="bg-white/80 backdrop-blur-md shadow-lg border-b border-white/20 sticky top-0 z-50 w-full">
|
|
<div class="w-full px-6 py-4">
|
|
<!-- 첫 번째 줄: 네비게이션 + 제목 -->
|
|
<div class="flex items-center justify-between mb-2">
|
|
<!-- 왼쪽: 네비게이션 -->
|
|
<div class="flex items-center space-x-3">
|
|
<!-- 뒤로가기 -->
|
|
<button @click="goBack" class="w-9 h-9 bg-white/60 hover:bg-white/80 rounded-xl flex items-center justify-center transition-all duration-200 text-gray-700 hover:text-gray-900 shadow-sm">
|
|
<i class="fas fa-arrow-left text-sm"></i>
|
|
</button>
|
|
|
|
<!-- 서적 네비게이션 -->
|
|
<div x-show="navigation" class="flex items-center space-x-2">
|
|
<button @click="navigateToDocument(navigation?.previous?.id)"
|
|
x-show="navigation?.previous"
|
|
:title="navigation?.previous ? `이전: ${navigation.previous.title}` : ''"
|
|
class="w-8 h-8 bg-blue-500/20 hover:bg-blue-500/30 rounded-lg flex items-center justify-center transition-all duration-200 text-blue-700">
|
|
<i class="fas fa-chevron-left text-xs"></i>
|
|
</button>
|
|
<button @click="goToBookContents()"
|
|
x-show="navigation?.book_info"
|
|
:title="navigation?.book_info ? `목차: ${navigation.book_info.title}` : ''"
|
|
class="w-8 h-8 bg-green-500/20 hover:bg-green-500/30 rounded-lg flex items-center justify-center transition-all duration-200 text-green-700">
|
|
<i class="fas fa-list text-xs"></i>
|
|
</button>
|
|
<button @click="navigateToDocument(navigation?.next?.id)"
|
|
x-show="navigation?.next"
|
|
:title="navigation?.next ? `다음: ${navigation.next.title}` : ''"
|
|
class="w-8 h-8 bg-blue-500/20 hover:bg-blue-500/30 rounded-lg flex items-center justify-center transition-all duration-200 text-blue-700">
|
|
<i class="fas fa-chevron-right text-xs"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 중앙: 문서 제목 -->
|
|
<div class="flex-1 text-center">
|
|
<h1 class="text-xl font-bold text-gray-800" x-text="document?.title || '로딩 중...'"></h1>
|
|
</div>
|
|
|
|
<!-- 오른쪽: 검색 -->
|
|
<div class="flex items-center">
|
|
<div class="relative">
|
|
<input type="text" x-model="searchQuery" @input="searchInDocument"
|
|
placeholder="검색..."
|
|
class="w-60 pl-9 pr-3 py-2 bg-white/50 border border-white/30 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-400/50 focus:bg-white/70 shadow-sm transition-all duration-200 text-sm">
|
|
<i class="fas fa-search absolute left-3 top-2.5 text-gray-500 text-xs"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 두 번째 줄: 문서 정보 -->
|
|
<div x-show="document" class="flex items-center justify-between text-sm text-gray-600 mb-3 px-2">
|
|
<!-- 왼쪽: 업로더 정보 -->
|
|
<div class="flex items-center space-x-4">
|
|
<span class="flex items-center space-x-1">
|
|
<i class="fas fa-user text-xs"></i>
|
|
<span x-text="document?.uploader_name"></span>
|
|
</span>
|
|
<span x-show="navigation?.book_info" class="flex items-center space-x-1">
|
|
<i class="fas fa-book text-xs"></i>
|
|
<span x-text="navigation?.book_info?.title"></span>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- 오른쪽: 문서 통계 -->
|
|
<div class="flex items-center space-x-4">
|
|
<span x-show="notes.length > 0" class="flex items-center space-x-1">
|
|
<i class="fas fa-sticky-note text-xs text-blue-500"></i>
|
|
<span x-text="`메모 ${notes.length}개`"></span>
|
|
</span>
|
|
<span x-show="bookmarks.length > 0" class="flex items-center space-x-1">
|
|
<i class="fas fa-bookmark text-xs text-amber-500"></i>
|
|
<span x-text="`책갈피 ${bookmarks.length}개`"></span>
|
|
</span>
|
|
<span x-show="documentLinks.length > 0" class="flex items-center space-x-1">
|
|
<i class="fas fa-link text-xs text-purple-500"></i>
|
|
<span x-text="`링크 ${documentLinks.length}개`"></span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 세 번째 줄: 도구 모음 -->
|
|
<div class="flex items-center justify-between">
|
|
<!-- 왼쪽: 하이라이트 색상 -->
|
|
<div class="flex items-center space-x-1 bg-white/50 rounded-xl shadow-sm border border-white/30 px-3 py-2">
|
|
<span class="text-xs font-medium text-gray-600 mr-2">하이라이트</span>
|
|
<button @click="createHighlightWithColor('#FFFF00')"
|
|
:class="selectedHighlightColor === '#FFFF00' ? 'ring-2 ring-yellow-400 scale-110' : 'hover:scale-110'"
|
|
class="w-6 h-6 bg-gradient-to-br from-yellow-300 to-yellow-400 rounded-full border border-white shadow-sm transition-all duration-200"
|
|
title="노란색"></button>
|
|
<button @click="createHighlightWithColor('#90EE90')"
|
|
:class="selectedHighlightColor === '#90EE90' ? 'ring-2 ring-green-400 scale-110' : 'hover:scale-110'"
|
|
class="w-6 h-6 bg-gradient-to-br from-green-300 to-green-400 rounded-full border border-white shadow-sm transition-all duration-200"
|
|
title="초록색"></button>
|
|
<button @click="createHighlightWithColor('#FFCCCB')"
|
|
:class="selectedHighlightColor === '#FFCCCB' ? 'ring-2 ring-pink-400 scale-110' : 'hover:scale-110'"
|
|
class="w-6 h-6 bg-gradient-to-br from-pink-200 to-pink-300 rounded-full border border-white shadow-sm transition-all duration-200"
|
|
title="분홍색"></button>
|
|
<button @click="createHighlightWithColor('#87CEEB')"
|
|
:class="selectedHighlightColor === '#87CEEB' ? 'ring-2 ring-blue-400 scale-110' : 'hover:scale-110'"
|
|
class="w-6 h-6 bg-gradient-to-br from-blue-300 to-blue-400 rounded-full border border-white shadow-sm transition-all duration-200"
|
|
title="파란색"></button>
|
|
</div>
|
|
|
|
<!-- 중앙: 기능 버튼들 -->
|
|
<div class="flex items-center space-x-3">
|
|
<!-- 링크 버튼 그룹 -->
|
|
<div class="relative">
|
|
<button @click="toggleFeatureMenu('link')"
|
|
:class="activeFeatureMenu === 'link' ? 'bg-purple-600' : 'bg-purple-500/80 hover:bg-purple-500'"
|
|
class="px-4 py-2 text-white rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm">
|
|
<i class="fas fa-link text-sm"></i>
|
|
<span class="text-sm font-medium">링크</span>
|
|
<span x-show="documentLinks.length > 0"
|
|
class="bg-white text-purple-600 text-xs rounded-full w-5 h-5 flex items-center justify-center font-bold"
|
|
x-text="documentLinks.length"></span>
|
|
<i class="fas fa-chevron-down text-xs ml-1" :class="activeFeatureMenu === 'link' ? 'rotate-180' : ''"></i>
|
|
</button>
|
|
<!-- 링크 서브메뉴 -->
|
|
<div x-show="activeFeatureMenu === 'link'"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="absolute top-full left-0 mt-2 bg-white/90 backdrop-blur-md rounded-xl shadow-lg border border-white/30 p-2 z-10 min-w-max">
|
|
<button @click="showLinksModal = true; activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-purple-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-eye text-purple-500"></i>
|
|
<span>링크 보기</span>
|
|
</button>
|
|
<button @click="console.log('링크 만들기 클릭됨'); activateLinkMode(); activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-purple-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-plus text-purple-500"></i>
|
|
<span>링크 만들기</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 백링크 버튼 그룹 -->
|
|
<div class="relative">
|
|
<button @click="toggleFeatureMenu('backlink')"
|
|
:class="activeFeatureMenu === 'backlink' ? 'bg-orange-600' : 'bg-orange-500/80 hover:bg-orange-500'"
|
|
class="px-4 py-2 text-white rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm">
|
|
<i class="fas fa-arrow-left text-sm"></i>
|
|
<span class="text-sm font-medium">백링크</span>
|
|
<span x-show="backlinks.length > 0"
|
|
class="bg-white text-orange-600 text-xs rounded-full w-5 h-5 flex items-center justify-center font-bold"
|
|
x-text="backlinks.length"></span>
|
|
<i class="fas fa-chevron-down text-xs ml-1" :class="activeFeatureMenu === 'backlink' ? 'rotate-180' : ''"></i>
|
|
</button>
|
|
<!-- 백링크 서브메뉴 -->
|
|
<div x-show="activeFeatureMenu === 'backlink'"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="absolute top-full left-0 mt-2 bg-white/90 backdrop-blur-md rounded-xl shadow-lg border border-white/30 p-2 z-10 min-w-max">
|
|
<button @click="showBacklinksModal = true; loadBacklinks(); activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-orange-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-eye text-orange-500"></i>
|
|
<span>백링크 보기</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 메모 버튼 그룹 -->
|
|
<div class="relative">
|
|
<button @click="toggleFeatureMenu('memo')"
|
|
:class="activeFeatureMenu === 'memo' ? 'bg-blue-600' : 'bg-blue-500/80 hover:bg-blue-500'"
|
|
class="px-4 py-2 text-white rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm">
|
|
<i class="fas fa-sticky-note text-sm"></i>
|
|
<span class="text-sm font-medium">메모</span>
|
|
<span x-show="notes.length > 0"
|
|
class="bg-white text-blue-600 text-xs rounded-full w-5 h-5 flex items-center justify-center font-bold"
|
|
x-text="notes.length"></span>
|
|
<i class="fas fa-chevron-down text-xs ml-1" :class="activeFeatureMenu === 'memo' ? 'rotate-180' : ''"></i>
|
|
</button>
|
|
<!-- 메모 서브메뉴 -->
|
|
<div x-show="activeFeatureMenu === 'memo'"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="absolute top-full left-0 mt-2 bg-white/90 backdrop-blur-md rounded-xl shadow-lg border border-white/30 p-2 z-10 min-w-max">
|
|
<button @click="showNotesModal = true; activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-blue-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-eye text-blue-500"></i>
|
|
<span>메모 보기</span>
|
|
</button>
|
|
<button @click="activateNoteMode(); activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-green-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-plus text-green-500"></i>
|
|
<span>메모 만들기</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 책갈피 버튼 그룹 -->
|
|
<div class="relative">
|
|
<button @click="toggleFeatureMenu('bookmark')"
|
|
:class="activeFeatureMenu === 'bookmark' ? 'bg-amber-600' : 'bg-amber-500/80 hover:bg-amber-500'"
|
|
class="px-4 py-2 text-white rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm">
|
|
<i class="fas fa-bookmark text-sm"></i>
|
|
<span class="text-sm font-medium">책갈피</span>
|
|
<span x-show="bookmarks.length > 0"
|
|
class="bg-white text-amber-600 text-xs rounded-full w-5 h-5 flex items-center justify-center font-bold"
|
|
x-text="bookmarks.length"></span>
|
|
<i class="fas fa-chevron-down text-xs ml-1" :class="activeFeatureMenu === 'bookmark' ? 'rotate-180' : ''"></i>
|
|
</button>
|
|
<!-- 책갈피 서브메뉴 -->
|
|
<div x-show="activeFeatureMenu === 'bookmark'"
|
|
x-transition:enter="transition ease-out duration-200"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-150"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="absolute top-full left-0 mt-2 bg-white/90 backdrop-blur-md rounded-xl shadow-lg border border-white/30 p-2 z-10 min-w-max">
|
|
<button @click="showBookmarksModal = true; activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-amber-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-eye text-amber-500"></i>
|
|
<span>책갈피 보기</span>
|
|
</button>
|
|
<button @click="console.log('책갈피 만들기 클릭됨'); activateBookmarkMode(); activeFeatureMenu = null"
|
|
class="w-full px-3 py-2 text-sm text-gray-700 hover:bg-amber-100 rounded-lg flex items-center space-x-2 transition-colors">
|
|
<i class="fas fa-plus text-amber-500"></i>
|
|
<span>책갈피 만들기</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 오른쪽: 액션 버튼들 -->
|
|
<div class="flex items-center space-x-3">
|
|
<button @click="downloadOriginalFile()"
|
|
class="bg-red-500/80 hover:bg-red-500 text-white px-4 py-2 rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm"
|
|
title="원본 PDF 다운로드">
|
|
<i class="fas fa-file-pdf text-sm"></i>
|
|
<span class="text-sm font-medium">PDF</span>
|
|
</button>
|
|
|
|
<button @click="toggleLanguage()"
|
|
class="bg-blue-500/80 hover:bg-blue-500 text-white px-4 py-2 rounded-xl transition-all duration-200 flex items-center space-x-2 shadow-sm"
|
|
id="language-toggle-btn">
|
|
<i class="fas fa-globe text-sm"></i>
|
|
<span class="text-sm font-medium">언어전환</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- 메인 컨텐츠 -->
|
|
<div class="flex w-full min-h-screen">
|
|
<!-- 문서 뷰어 -->
|
|
<main class="flex-1 bg-white">
|
|
<div class="px-12 py-8 max-w-5xl mx-auto">
|
|
<!-- 로딩 상태 -->
|
|
<div x-show="loading" class="text-center py-16">
|
|
<i class="fas fa-spinner fa-spin text-4xl text-gray-400 mb-4"></i>
|
|
<p class="text-gray-600">문서를 불러오는 중...</p>
|
|
</div>
|
|
|
|
<!-- 에러 상태 -->
|
|
<div x-show="error" class="text-center py-16">
|
|
<i class="fas fa-exclamation-triangle text-4xl text-red-400 mb-4"></i>
|
|
<p class="text-red-600" x-text="error"></p>
|
|
<button @click="goBack" class="mt-4 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700">
|
|
돌아가기
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 문서 내용 -->
|
|
<div x-show="!loading && !error" id="document-content" class="prose max-w-none">
|
|
<!-- 문서 HTML이 여기에 로드됩니다 -->
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- 링크 모달 -->
|
|
<div x-show="showLinksModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click.self="showLinksModal = false">
|
|
<div class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
|
|
<!-- 헤더 -->
|
|
<div class="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h3 class="text-xl font-bold text-gray-900 flex items-center space-x-2">
|
|
<i class="fas fa-link text-purple-500"></i>
|
|
<span>링크</span>
|
|
</h3>
|
|
<button @click="showLinksModal = false"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 내용 -->
|
|
<div class="p-6 overflow-y-auto max-h-96">
|
|
<!-- 빈 상태 -->
|
|
<template x-if="documentLinks.length === 0">
|
|
<div class="text-center py-8">
|
|
<i class="fas fa-link text-4xl text-gray-300 mb-4"></i>
|
|
<p class="text-gray-500">아직 링크가 없습니다.</p>
|
|
<p class="text-sm text-gray-400 mt-2">텍스트를 선택하고 링크를 만들어보세요.</p>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- 링크 목록 -->
|
|
<template x-for="link in documentLinks" :key="link.id">
|
|
<div class="border rounded-lg p-4 mb-3 hover:bg-purple-50 cursor-pointer transition-colors"
|
|
@click="navigateToLink(link)">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex-1">
|
|
<div class="font-medium text-purple-700 mb-1" x-text="link.target_document_title"></div>
|
|
|
|
<!-- 선택된 텍스트 또는 문서 전체 링크 -->
|
|
<div x-show="link.selected_text" class="mb-2">
|
|
<div class="text-sm text-gray-600 bg-gray-100 px-3 py-2 rounded border-l-4 border-purple-500" x-text="link.selected_text"></div>
|
|
</div>
|
|
<div x-show="!link.selected_text" class="mb-2">
|
|
<div class="text-sm text-gray-600 italic">📄 문서 전체 링크</div>
|
|
</div>
|
|
|
|
<!-- 설명 -->
|
|
<div x-show="link.description" class="text-sm text-gray-600 mb-2" x-text="link.description"></div>
|
|
|
|
<!-- 링크 타입과 날짜 -->
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-xs text-gray-500"
|
|
x-text="link.link_type === 'text_fragment' ? '텍스트 조각 링크' : '문서 링크'"></span>
|
|
<span class="text-xs text-gray-500" x-text="formatDate(link.created_at)"></span>
|
|
</div>
|
|
</div>
|
|
<div class="ml-3">
|
|
<svg class="w-5 h-5 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 링크 생성 모달 -->
|
|
<div x-show="showLinkModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click.self="closeLinkModal()">
|
|
<div class="bg-white rounded-2xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
|
<!-- 헤더 -->
|
|
<div class="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h3 class="text-xl font-bold text-gray-900 flex items-center space-x-2">
|
|
<i class="fas fa-link text-purple-500"></i>
|
|
<span>링크 생성</span>
|
|
</h3>
|
|
<button @click="closeLinkModal()"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 내용 -->
|
|
<div class="flex-1 overflow-y-auto p-6">
|
|
<!-- 선택된 텍스트 표시 -->
|
|
<div class="bg-purple-50 rounded-lg p-4 mb-6">
|
|
<h4 class="font-semibold text-purple-800 mb-2">선택된 텍스트</h4>
|
|
<p class="text-purple-700" x-text="linkForm?.selected_text || ''"></p>
|
|
</div>
|
|
|
|
<!-- 서적 범위 선택 -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-gray-700 mb-3">링크 대상 범위</label>
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<button @click="linkForm.book_scope = 'same'; resetTargetSelection()"
|
|
:class="linkForm.book_scope === 'same' ? 'bg-green-100 border-green-500 text-green-700' : 'bg-gray-50 border-gray-300 text-gray-600'"
|
|
class="p-4 border-2 rounded-lg transition-all duration-200 text-left">
|
|
<div class="flex items-center space-x-2 mb-2">
|
|
<i class="fas fa-book"></i>
|
|
<span class="font-semibold">같은 서적</span>
|
|
</div>
|
|
<p class="text-xs">현재 서적 내 문서</p>
|
|
</button>
|
|
<button @click="linkForm.book_scope = 'other'; resetTargetSelection()"
|
|
:class="linkForm.book_scope === 'other' ? 'bg-blue-100 border-blue-500 text-blue-700' : 'bg-gray-50 border-gray-300 text-gray-600'"
|
|
class="p-4 border-2 rounded-lg transition-all duration-200 text-left">
|
|
<div class="flex items-center space-x-2 mb-2">
|
|
<i class="fas fa-books"></i>
|
|
<span class="font-semibold">다른 서적</span>
|
|
</div>
|
|
<p class="text-xs">다른 서적의 문서</p>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 대상 서적 선택 (다른 서적인 경우만) -->
|
|
<div x-show="linkForm.book_scope === 'other'" class="mb-6">
|
|
<label class="block text-sm font-semibold text-gray-700 mb-2">대상 서적</label>
|
|
<select x-model="linkForm.target_book_id"
|
|
@change="loadDocumentsFromBook()"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
<option value="">서적을 선택하세요</option>
|
|
<template x-for="book in availableBooks" :key="book.id">
|
|
<option :value="book.id" x-text="book.title"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- 대상 문서 선택 -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
|
대상 문서
|
|
<span x-show="linkForm.book_scope === 'same'" class="text-green-600 text-xs">(현재 서적)</span>
|
|
<span x-show="linkForm.book_scope === 'other' && linkForm.target_book_id" class="text-blue-600 text-xs" x-text="`(${getSelectedBookTitle()})`"></span>
|
|
</label>
|
|
<select x-model="linkForm.target_document_id"
|
|
@change="onTargetDocumentChange()"
|
|
:disabled="linkForm.book_scope === 'other' && !linkForm.target_book_id"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 disabled:bg-gray-100 disabled:cursor-not-allowed">
|
|
<option value="">
|
|
<span x-show="linkForm.book_scope === 'same'">문서를 선택하세요</span>
|
|
<span x-show="linkForm.book_scope === 'other' && !linkForm.target_book_id">먼저 서적을 선택하세요</span>
|
|
<span x-show="linkForm.book_scope === 'other' && linkForm.target_book_id">문서를 선택하세요</span>
|
|
</option>
|
|
<template x-for="doc in filteredDocuments" :key="doc.id">
|
|
<option :value="doc.id" x-text="doc.title"></option>
|
|
</template>
|
|
</select>
|
|
<p x-show="filteredDocuments.length > 0" class="text-xs text-gray-500 mt-1" x-text="`${filteredDocuments.length}개 문서`"></p>
|
|
</div>
|
|
|
|
<!-- 대상 텍스트 선택 (무조건 텍스트 선택만 지원) -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-gray-700 mb-2">대상 텍스트</label>
|
|
<div class="space-y-3">
|
|
<button @click="openTargetDocumentSelector()"
|
|
:disabled="!linkForm.target_document_id"
|
|
:class="linkForm.target_document_id ? 'bg-blue-500 hover:bg-blue-600 text-white' : 'bg-gray-300 text-gray-500 cursor-not-allowed'"
|
|
class="w-full px-4 py-2 rounded-lg transition-colors flex items-center justify-center space-x-2">
|
|
<i class="fas fa-crosshairs"></i>
|
|
<span>대상 문서에서 텍스트 선택</span>
|
|
</button>
|
|
<div x-show="linkForm.target_text" class="bg-blue-50 rounded-lg p-3">
|
|
<p class="text-sm text-blue-800 font-medium">선택된 대상 텍스트:</p>
|
|
<p class="text-blue-700 mt-1" x-text="linkForm.target_text"></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 링크 설명 -->
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-gray-700 mb-2">설명 (선택사항)</label>
|
|
<textarea x-model="linkForm.description"
|
|
placeholder="링크에 대한 설명을 입력하세요..."
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500 resize-none"
|
|
rows="3"></textarea>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- 버튼 영역 (고정) -->
|
|
<div class="border-t border-gray-200 p-6">
|
|
<div class="flex justify-end space-x-3">
|
|
<button @click="closeLinkModal()"
|
|
class="px-4 py-2 text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors">
|
|
취소
|
|
</button>
|
|
<button @click="saveDocumentLink()"
|
|
:disabled="!linkForm?.target_document_id"
|
|
:class="linkForm?.target_document_id ? 'bg-purple-500 hover:bg-purple-600' : 'bg-gray-300 cursor-not-allowed'"
|
|
class="px-4 py-2 text-white rounded-lg transition-colors">
|
|
링크 생성
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 메모 모달 -->
|
|
<div x-show="showNotesModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click="showNotesModal = false">
|
|
|
|
<div @click.stop class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
|
|
<div class="flex justify-between items-center p-6 border-b">
|
|
<h3 class="text-xl font-bold text-gray-800 flex items-center space-x-2">
|
|
<i class="fas fa-sticky-note text-blue-600"></i>
|
|
<span>메모</span>
|
|
</h3>
|
|
<button @click="showNotesModal = false"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-full flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-6 overflow-y-auto max-h-96">
|
|
<!-- 메모가 없을 때 -->
|
|
<template x-if="notes.length === 0">
|
|
<div class="text-center text-gray-500">
|
|
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i class="fas fa-sticky-note text-2xl text-blue-500"></i>
|
|
</div>
|
|
<h4 class="font-semibold text-gray-700 mb-2">메모가 없습니다</h4>
|
|
<p class="text-sm text-gray-500">텍스트를 선택하고 메모를 추가해보세요</p>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- 메모 목록 -->
|
|
<div class="space-y-3">
|
|
<template x-for="note in notes" :key="note.id">
|
|
<div class="bg-gray-50 rounded-lg p-4 hover:bg-gray-100 transition-colors cursor-pointer"
|
|
@click="scrollToHighlight(note.highlight.id)">
|
|
<!-- 선택된 텍스트 -->
|
|
<div class="bg-blue-50 rounded-md p-2 mb-3">
|
|
<p class="text-sm text-blue-800 font-medium" x-text="note.highlight.selected_text"></p>
|
|
</div>
|
|
|
|
<!-- 메모 내용 -->
|
|
<p class="text-gray-800 mb-2 leading-relaxed" x-text="note.content"></p>
|
|
|
|
<!-- 날짜 -->
|
|
<div class="flex justify-between items-center">
|
|
<span class="text-xs text-gray-500" x-text="formatDate(note.created_at)"></span>
|
|
<div class="flex space-x-1">
|
|
<button @click.stop="editNote(note)"
|
|
class="w-6 h-6 bg-blue-100 text-blue-600 rounded-md hover:bg-blue-200 flex items-center justify-center transition-colors">
|
|
<i class="fas fa-edit text-xs"></i>
|
|
</button>
|
|
<button @click.stop="deleteNote(note.id)"
|
|
class="w-6 h-6 bg-red-100 text-red-600 rounded-md hover:bg-red-200 flex items-center justify-center transition-colors">
|
|
<i class="fas fa-trash text-xs"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 메모 입력 모달 (하이라이트 생성 후) -->
|
|
<div x-show="showNoteInputModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click="showNoteInputModal = false">
|
|
|
|
<div @click.stop class="bg-white rounded-2xl shadow-2xl max-w-md w-full">
|
|
<div class="flex justify-between items-center p-6 border-b">
|
|
<h3 class="text-xl font-bold text-gray-800 flex items-center space-x-2">
|
|
<i class="fas fa-sticky-note text-blue-600"></i>
|
|
<span>메모 추가</span>
|
|
</h3>
|
|
<button @click="showNoteInputModal = false"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-full flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="p-6">
|
|
<!-- 선택된 텍스트 표시 -->
|
|
<div class="bg-blue-50 rounded-lg p-3 mb-4">
|
|
<p class="text-sm text-gray-600 mb-1">선택된 텍스트:</p>
|
|
<p class="text-blue-800 font-medium" x-text="selectedText"></p>
|
|
</div>
|
|
|
|
<!-- 메모 입력 질문 -->
|
|
<p class="text-gray-700 mb-4">이 하이라이트에 메모를 추가하시겠습니까?</p>
|
|
|
|
<!-- 메모 입력 칸 -->
|
|
<textarea x-model="noteForm.content"
|
|
placeholder="메모 내용을 입력하세요..."
|
|
class="w-full h-32 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
|
|
@keydown.ctrl.enter="createNoteForHighlight()"
|
|
@keydown.meta.enter="createNoteForHighlight()"></textarea>
|
|
|
|
<!-- 태그 입력 (선택사항) -->
|
|
<input x-model="noteForm.tags"
|
|
type="text"
|
|
placeholder="태그 (선택사항, 쉼표로 구분)"
|
|
class="w-full mt-3 p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
|
|
<!-- 버튼들 -->
|
|
<div class="flex justify-end space-x-3 mt-6">
|
|
<button @click="skipNoteForHighlight()"
|
|
class="px-4 py-2 text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors">
|
|
나중에 입력
|
|
</button>
|
|
<button @click="createNoteForHighlight()"
|
|
:disabled="!noteForm.content.trim()"
|
|
:class="noteForm.content.trim() ? 'bg-blue-600 hover:bg-blue-700' : 'bg-gray-300 cursor-not-allowed'"
|
|
class="px-6 py-2 text-white rounded-lg transition-colors">
|
|
확인
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 책갈피 모달 -->
|
|
<div x-show="showBookmarksModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click="showBookmarksModal = false">
|
|
|
|
<div @click.stop class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
|
|
<div class="flex justify-between items-center p-6 border-b">
|
|
<h3 class="text-xl font-bold text-gray-800 flex items-center space-x-2">
|
|
<i class="fas fa-bookmark text-amber-600"></i>
|
|
<span>책갈피</span>
|
|
</h3>
|
|
<button @click="showBookmarksModal = false"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-full flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-6 overflow-y-auto max-h-96">
|
|
<!-- 책갈피가 없을 때 -->
|
|
<template x-if="bookmarks.length === 0">
|
|
<div class="text-center text-gray-500">
|
|
<div class="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i class="fas fa-bookmark text-2xl text-amber-500"></i>
|
|
</div>
|
|
<h4 class="font-semibold text-gray-700 mb-2">책갈피가 없습니다</h4>
|
|
<p class="text-sm text-gray-500">텍스트를 선택하고 책갈피를 추가해보세요</p>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- 책갈피 목록 -->
|
|
<div class="space-y-3">
|
|
<template x-for="bookmark in bookmarks" :key="bookmark.id">
|
|
<div class="bg-gray-50 rounded-lg p-4 hover:bg-gray-100 transition-colors cursor-pointer"
|
|
@click="scrollToHighlight(bookmark.highlight.id)">
|
|
<!-- 선택된 텍스트 -->
|
|
<div class="bg-amber-50 rounded-md p-2 mb-3">
|
|
<p class="text-sm text-amber-800 font-medium" x-text="bookmark.highlight.selected_text"></p>
|
|
</div>
|
|
|
|
<!-- 책갈피 제목 -->
|
|
<p class="text-gray-800 mb-2 leading-relaxed font-semibold" x-text="bookmark.title || '제목 없음'"></p>
|
|
|
|
<!-- 날짜 -->
|
|
<div class="flex justify-between items-center">
|
|
<span class="text-xs text-gray-500" x-text="formatDate(bookmark.created_at)"></span>
|
|
<div class="flex space-x-1">
|
|
<button @click.stop="editBookmark(bookmark)"
|
|
class="w-6 h-6 bg-amber-100 text-amber-600 rounded-md hover:bg-amber-200 flex items-center justify-center transition-colors">
|
|
<i class="fas fa-edit text-xs"></i>
|
|
</button>
|
|
<button @click.stop="deleteBookmark(bookmark.id)"
|
|
class="w-6 h-6 bg-red-100 text-red-600 rounded-md hover:bg-red-200 flex items-center justify-center transition-colors">
|
|
<i class="fas fa-trash text-xs"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 백링크 모달 -->
|
|
<div x-show="showBacklinksModal"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
|
|
@click="showBacklinksModal = false">
|
|
|
|
<div @click.stop class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
|
|
<div class="flex justify-between items-center p-6 border-b">
|
|
<h3 class="text-xl font-bold text-gray-800 flex items-center space-x-2">
|
|
<i class="fas fa-arrow-left text-orange-600"></i>
|
|
<span>백링크</span>
|
|
</h3>
|
|
<button @click="showBacklinksModal = false"
|
|
class="w-8 h-8 bg-gray-100 hover:bg-gray-200 rounded-full flex items-center justify-center transition-colors">
|
|
<i class="fas fa-times text-gray-600"></i>
|
|
</button>
|
|
</div>
|
|
<div class="p-6 overflow-y-auto max-h-96">
|
|
<!-- 백링크가 없을 때 -->
|
|
<template x-if="backlinks.length === 0">
|
|
<div class="text-center text-gray-500">
|
|
<div class="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i class="fas fa-arrow-left text-2xl text-orange-500"></i>
|
|
</div>
|
|
<h4 class="font-semibold text-gray-700 mb-2">백링크가 없습니다</h4>
|
|
<p class="text-sm text-gray-500">다른 문서에서 이 문서를 참조하는 링크가 없습니다</p>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- 백링크 목록 -->
|
|
<div class="space-y-3">
|
|
<template x-for="backlink in backlinks" :key="backlink.id">
|
|
<div class="bg-gray-50 rounded-lg p-4 hover:bg-gray-100 transition-colors cursor-pointer"
|
|
@click="navigateToBacklink(backlink)">
|
|
<!-- 참조하는 문서 정보 -->
|
|
<div class="bg-orange-50 rounded-md p-2 mb-3">
|
|
<p class="text-sm text-orange-800 font-medium" x-text="backlink.source_document_title"></p>
|
|
</div>
|
|
|
|
<!-- 선택된 텍스트 또는 문서 전체 링크 -->
|
|
<div x-show="backlink.selected_text" class="mb-2">
|
|
<p class="text-gray-800 leading-relaxed" x-text="backlink.selected_text"></p>
|
|
</div>
|
|
<div x-show="!backlink.selected_text" class="mb-2">
|
|
<p class="text-gray-600 italic">📄 문서 전체 링크</p>
|
|
</div>
|
|
|
|
<!-- 설명 -->
|
|
<p class="text-sm text-gray-600 mb-2" x-text="backlink.description || '설명 없음'"></p>
|
|
|
|
<!-- 날짜 -->
|
|
<div class="flex justify-between items-center">
|
|
<span class="text-xs text-gray-500" x-text="formatDate(backlink.created_at)"></span>
|
|
<button class="text-xs text-orange-600 hover:text-orange-800 font-medium">
|
|
문서로 이동 →
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 스크립트 -->
|
|
<script src="/static/js/api.js?v=2025012614"></script>
|
|
|
|
<!-- 캐시 및 성능 최적화 시스템 -->
|
|
<script src="/static/js/viewer/utils/cache-manager.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/utils/cached-api.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/utils/module-loader.js?v=2025012607"></script>
|
|
|
|
<!-- 모든 모듈들 직접 로드 -->
|
|
<script src="/static/js/viewer/core/document-loader.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/features/ui-manager.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/features/highlight-manager.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/features/link-manager.js?v=2025012607"></script>
|
|
<script src="/static/js/viewer/features/bookmark-manager.js?v=2025012607"></script>
|
|
|
|
<!-- ViewerCore (Alpine.js 컴포넌트) -->
|
|
<script src="/static/js/viewer/viewer-core.js?v=2025012607"></script>
|
|
|
|
<!-- Alpine.js 프레임워크 -->
|
|
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
|
|
<style>
|
|
[x-cloak] { display: none !important; }
|
|
|
|
/* 링크된 텍스트 하이라이트 (URL에서 온 것) - 레이아웃 안전 */
|
|
.linked-text-highlight {
|
|
background-color: #FEF3C7 !important;
|
|
border: 1px solid #F59E0B !important;
|
|
border-radius: 2px !important;
|
|
padding: 0 1px !important;
|
|
display: inline !important;
|
|
box-decoration-break: clone !important;
|
|
-webkit-box-decoration-break: clone !important;
|
|
line-height: inherit !important;
|
|
vertical-align: baseline !important;
|
|
margin: 0 !important;
|
|
box-sizing: border-box !important;
|
|
}
|
|
|
|
/* 백링크 강제 스타일 - 레이아웃 안전 */
|
|
.backlink-highlight {
|
|
color: #EA580C !important;
|
|
background-color: rgba(234, 88, 12, 0.2) !important;
|
|
border: 1px solid #EA580C !important;
|
|
border-radius: 3px !important;
|
|
padding: 0 2px !important;
|
|
font-weight: bold !important;
|
|
display: inline !important;
|
|
box-decoration-break: clone !important;
|
|
-webkit-box-decoration-break: clone !important;
|
|
line-height: inherit !important;
|
|
vertical-align: baseline !important;
|
|
margin: 0 !important;
|
|
box-sizing: border-box !important;
|
|
text-decoration: underline !important;
|
|
cursor: pointer !important;
|
|
}
|
|
|
|
/* 링크 스타일 */
|
|
.document-link {
|
|
color: #7C3AED !important;
|
|
background-color: rgba(124, 58, 237, 0.1) !important;
|
|
border-radius: 2px !important;
|
|
padding: 0 1px !important;
|
|
display: inline !important;
|
|
line-height: inherit !important;
|
|
vertical-align: baseline !important;
|
|
margin: 0 !important;
|
|
box-sizing: border-box !important;
|
|
text-decoration: underline !important;
|
|
cursor: pointer !important;
|
|
}
|
|
|
|
/* 겹치는 영역 처리 - 백링크 안에 링크가 있는 경우 */
|
|
.backlink-highlight .document-link {
|
|
background: linear-gradient(to bottom,
|
|
rgba(234, 88, 12, 0.3) 0%,
|
|
rgba(234, 88, 12, 0.3) 50%,
|
|
rgba(124, 58, 237, 0.2) 50%,
|
|
rgba(124, 58, 237, 0.2) 100%) !important;
|
|
border-top: 1px solid #EA580C !important;
|
|
border-bottom: 1px solid #7C3AED !important;
|
|
}
|
|
|
|
/* 겹치는 영역 처리 - 링크 안에 백링크가 있는 경우 */
|
|
.document-link .backlink-highlight {
|
|
background: linear-gradient(to bottom,
|
|
rgba(124, 58, 237, 0.2) 0%,
|
|
rgba(124, 58, 237, 0.2) 50%,
|
|
rgba(234, 88, 12, 0.3) 50%,
|
|
rgba(234, 88, 12, 0.3) 100%) !important;
|
|
border-top: 1px solid #7C3AED !important;
|
|
border-bottom: 1px solid #EA580C !important;
|
|
}
|
|
|
|
/* 하이라이트 스타일 개선 */
|
|
.highlight, .highlight-span {
|
|
padding: 1px 2px;
|
|
border-radius: 2px;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.highlight:hover, .highlight-span:hover {
|
|
box-shadow: 0 0 4px rgba(0,0,0,0.3);
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.multi-highlight {
|
|
padding: 1px 2px;
|
|
border-radius: 2px;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
position: relative;
|
|
}
|
|
|
|
.multi-highlight:hover {
|
|
box-shadow: 0 0 6px rgba(0,0,0,0.4);
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.multi-highlight::after {
|
|
content: "🎨";
|
|
position: absolute;
|
|
top: -8px;
|
|
right: -8px;
|
|
font-size: 10px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
width: 16px;
|
|
height: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
/* 기존 언어 전환 버튼 숨기기 */
|
|
.language-toggle-old,
|
|
button[onclick*="toggleLanguage"],
|
|
button[onclick*="language"],
|
|
.lang-toggle,
|
|
#old-language-toggle {
|
|
display: none !important;
|
|
}
|
|
|
|
/* 문서 내 기존 언어 버튼들 숨기기 */
|
|
#document-content button[onclick*="toggleLanguage"],
|
|
#document-content button[onclick*="language"],
|
|
#document-content .language-toggle,
|
|
#document-content .lang-btn {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
</body>
|
|
</html>
|