주요 수정사항: - 하이라이트 생성 시 color → highlight_color 필드명 수정으로 색상 전달 문제 해결 - 분홍색을 더 연하게 변경하여 글씨 가독성 향상 - 다중 하이라이트 렌더링을 위아래 균등 분할로 개선 - CSS highlight-span 클래스 추가 및 색상 적용 강화 - 하이라이트 생성/렌더링 과정에 상세한 디버깅 로그 추가 UI 개선: - 단일 하이라이트: 선택한 색상으로 정확히 표시 - 다중 하이라이트: 위아래로 균등하게 색상 분할 표시 - 메모 입력 모달에서 선택된 텍스트 표시 개선 버그 수정: - 프론트엔드-백엔드 API 스키마 불일치 해결 - CSS 스타일 우선순위 문제 해결 - 하이라이트 색상이 노랑색으로만 표시되던 문제 해결
269 lines
9.2 KiB
JavaScript
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;
|