Files
document-server/frontend/static/js/viewer/features/bookmark-manager.js
Hyungi Ahn 5d4465b15c 하이라이트 색상 문제 해결 및 다중 하이라이트 렌더링 개선
주요 수정사항:
- 하이라이트 생성 시 color → highlight_color 필드명 수정으로 색상 전달 문제 해결
- 분홍색을 더 연하게 변경하여 글씨 가독성 향상
- 다중 하이라이트 렌더링을 위아래 균등 분할로 개선
- CSS highlight-span 클래스 추가 및 색상 적용 강화
- 하이라이트 생성/렌더링 과정에 상세한 디버깅 로그 추가

UI 개선:
- 단일 하이라이트: 선택한 색상으로 정확히 표시
- 다중 하이라이트: 위아래로 균등하게 색상 분할 표시
- 메모 입력 모달에서 선택된 텍스트 표시 개선

버그 수정:
- 프론트엔드-백엔드 API 스키마 불일치 해결
- CSS 스타일 우선순위 문제 해결
- 하이라이트 색상이 노랑색으로만 표시되던 문제 해결
2025-08-28 07:13:00 +09:00

269 lines
9.2 KiB
JavaScript

/**
* BookmarkManager 모듈
* 북마크 관리
*/
class BookmarkManager {
constructor(api) {
this.api = api;
// 캐싱된 API 사용 (사용 가능한 경우)
this.cachedApi = window.cachedApi || api;
this.bookmarks = [];
this.bookmarkForm = {
title: '',
description: ''
};
this.editingBookmark = null;
this.currentScrollPosition = null;
}
/**
* 북마크 데이터 로드
*/
async loadBookmarks(documentId) {
try {
this.bookmarks = await this.cachedApi.get('/bookmarks', { document_id: documentId }, { category: 'bookmarks' }).catch(() => []);
return this.bookmarks || [];
} catch (error) {
console.error('북마크 로드 실패:', error);
return [];
}
}
/**
* 북마크 추가
*/
async addBookmark(document) {
const scrollPosition = window.scrollY;
this.bookmarkForm = {
title: `${document.title} - ${new Date().toLocaleString()}`,
description: ''
};
this.currentScrollPosition = scrollPosition;
// ViewerCore의 모달 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.showBookmarkModal = true;
}
}
/**
* 북마크 편집
*/
editBookmark(bookmark) {
this.editingBookmark = bookmark;
this.bookmarkForm = {
title: bookmark.title,
description: bookmark.description || ''
};
// ViewerCore의 모달 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.showBookmarkModal = true;
}
}
/**
* 북마크 저장
*/
async saveBookmark(documentId) {
try {
// ViewerCore의 로딩 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.bookmarkLoading = true;
}
const bookmarkData = {
title: this.bookmarkForm.title,
description: this.bookmarkForm.description,
scroll_position: this.currentScrollPosition || 0
};
if (this.editingBookmark) {
// 북마크 수정
const updatedBookmark = await this.api.updateBookmark(this.editingBookmark.id, bookmarkData);
const index = this.bookmarks.findIndex(b => b.id === this.editingBookmark.id);
if (index !== -1) {
this.bookmarks[index] = updatedBookmark;
}
} else {
// 새 북마크 생성
bookmarkData.document_id = documentId;
const newBookmark = await this.api.createBookmark(bookmarkData);
this.bookmarks.push(newBookmark);
}
this.closeBookmarkModal();
console.log('북마크 저장 완료');
} catch (error) {
console.error('Failed to save bookmark:', error);
alert('북마크 저장에 실패했습니다');
} finally {
// ViewerCore의 로딩 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.bookmarkLoading = false;
}
}
}
/**
* 북마크 삭제
*/
async deleteBookmark(bookmarkId) {
if (!confirm('이 북마크를 삭제하시겠습니까?')) {
return;
}
try {
await this.api.deleteBookmark(bookmarkId);
this.bookmarks = this.bookmarks.filter(b => b.id !== bookmarkId);
console.log('북마크 삭제 완료:', bookmarkId);
} catch (error) {
console.error('Failed to delete bookmark:', error);
alert('북마크 삭제에 실패했습니다');
}
}
/**
* 북마크로 스크롤
*/
scrollToBookmark(bookmark) {
window.scrollTo({
top: bookmark.scroll_position,
behavior: 'smooth'
});
}
/**
* 북마크 모달 닫기
*/
closeBookmarkModal() {
this.editingBookmark = null;
this.bookmarkForm = { title: '', description: '' };
this.currentScrollPosition = null;
// ViewerCore의 모달 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.showBookmarkModal = false;
}
}
/**
* 선택된 텍스트로 북마크 생성
*/
async createBookmarkFromSelection(documentId, selectedText, selectedRange) {
if (!selectedText || !selectedRange) return;
try {
// 하이라이트 생성 (북마크는 주황색)
const highlightData = await this.createHighlight(selectedText, selectedRange, '#FFA500');
// 북마크 생성
const bookmarkData = {
highlight_id: highlightData.id,
title: selectedText.substring(0, 50) + (selectedText.length > 50 ? '...' : ''),
description: `선택된 텍스트: "${selectedText}"`
};
const bookmark = await this.api.createBookmark(documentId, bookmarkData);
this.bookmarks.push(bookmark);
console.log('선택 텍스트 북마크 생성 완료:', bookmark);
alert('북마크가 생성되었습니다.');
} catch (error) {
console.error('북마크 생성 실패:', error);
alert('북마크 생성에 실패했습니다: ' + error.message);
}
}
/**
* 하이라이트 생성 (북마크용)
* HighlightManager와 연동
*/
async createHighlight(selectedText, selectedRange, color) {
try {
const viewerInstance = window.documentViewerInstance;
if (viewerInstance && viewerInstance.highlightManager) {
// HighlightManager의 상태 설정
viewerInstance.highlightManager.selectedText = selectedText;
viewerInstance.highlightManager.selectedRange = selectedRange;
viewerInstance.highlightManager.selectedHighlightColor = color;
// ViewerCore의 상태도 동기화
viewerInstance.selectedText = selectedText;
viewerInstance.selectedRange = selectedRange;
viewerInstance.selectedHighlightColor = color;
// HighlightManager의 createHighlight 호출
await viewerInstance.highlightManager.createHighlight();
// 생성된 하이라이트 찾기 (가장 최근 생성된 것)
const highlights = viewerInstance.highlightManager.highlights;
if (highlights && highlights.length > 0) {
return highlights[highlights.length - 1];
}
}
// 폴백: 간단한 하이라이트 데이터 반환
console.warn('HighlightManager 연동 실패, 폴백 데이터 사용');
return {
id: Date.now().toString(),
selected_text: selectedText,
color: color,
start_offset: 0,
end_offset: selectedText.length
};
} catch (error) {
console.error('하이라이트 생성 실패:', error);
// 폴백: 간단한 하이라이트 데이터 반환
return {
id: Date.now().toString(),
selected_text: selectedText,
color: color,
start_offset: 0,
end_offset: selectedText.length
};
}
}
/**
* 북마크 모드 활성화
*/
activateBookmarkMode() {
console.log('🔖 북마크 모드 활성화');
// 현재 선택된 텍스트가 있는지 확인
const selection = window.getSelection();
if (selection.rangeCount > 0 && !selection.isCollapsed) {
const selectedText = selection.toString().trim();
if (selectedText.length > 0) {
// ViewerCore의 선택된 텍스트 상태 업데이트
if (window.documentViewerInstance) {
window.documentViewerInstance.selectedText = selectedText;
window.documentViewerInstance.selectedRange = selection.getRangeAt(0);
}
this.createBookmarkFromSelection(
window.documentViewerInstance?.documentId,
selectedText,
selection.getRangeAt(0)
);
return;
}
}
// 텍스트 선택 모드 활성화
console.log('📝 텍스트 선택 모드 활성화');
if (window.documentViewerInstance) {
window.documentViewerInstance.activeMode = 'bookmark';
window.documentViewerInstance.showSelectionMessage('텍스트를 선택하세요.');
window.documentViewerInstance.setupTextSelectionListener();
}
}
}
// 전역으로 내보내기
window.BookmarkManager = BookmarkManager;