@@ -251,5 +251,12 @@
|
||||
"Small": "صغير",
|
||||
"Large": "كبير",
|
||||
"Auto": "تلقائي",
|
||||
"Language": "اللغة"
|
||||
"Language": "اللغة",
|
||||
"No annotations to export": "لا توجد تعليقات للتصدير",
|
||||
"Author": "المؤلف",
|
||||
"Exported from Readest": "تم التصدير من ريديست",
|
||||
"Highlights & Annotations": "التمييزات والتعليقات",
|
||||
"Note": "ملاحظة",
|
||||
"Copied to clipboard": "تم النسخ إلى الحافظة",
|
||||
"Export Annotations": "تصدير التعليقات"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Klein",
|
||||
"Large": "Groß",
|
||||
"Auto": "Automatisch",
|
||||
"Language": "Sprache"
|
||||
"Language": "Sprache",
|
||||
"No annotations to export": "Keine Anmerkungen zum Exportieren",
|
||||
"Author": "Autor",
|
||||
"Exported from Readest": "Exportiert von Readest",
|
||||
"Highlights & Annotations": "Hervorhebungen & Anmerkungen",
|
||||
"Note": "Notiz",
|
||||
"Copied to clipboard": "In die Zwischenablage kopiert",
|
||||
"Export Annotations": "Anmerkungen exportieren"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Μικρό",
|
||||
"Large": "Μεγάλο",
|
||||
"Auto": "Αυτόματο",
|
||||
"Language": "Γλώσσα"
|
||||
"Language": "Γλώσσα",
|
||||
"No annotations to export": "Δεν υπάρχουν σχολιασμοί για εξαγωγή",
|
||||
"Author": "Συγγραφέας",
|
||||
"Exported from Readest": "Εξήχθη από το Readest",
|
||||
"Highlights & Annotations": "Επισημάνσεις & Σχολιασμοί",
|
||||
"Note": "Σημείωση",
|
||||
"Copied to clipboard": "Αντιγράφηκε στο πρόχειρο",
|
||||
"Export Annotations": "Εξαγωγή σχολιασμών"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Pequeño",
|
||||
"Large": "Grande",
|
||||
"Auto": "Automático",
|
||||
"Language": "Idioma"
|
||||
"Language": "Idioma",
|
||||
"No annotations to export": "No hay anotaciones para exportar",
|
||||
"Author": "Autor",
|
||||
"Exported from Readest": "Exportado desde Readest",
|
||||
"Highlights & Annotations": "Resaltados y anotaciones",
|
||||
"Note": "Nota",
|
||||
"Copied to clipboard": "Copiado al portapapeles",
|
||||
"Export Annotations": "Exportar anotaciones"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Petit",
|
||||
"Large": "Grand",
|
||||
"Auto": "Automatique",
|
||||
"Language": "Langue"
|
||||
"Language": "Langue",
|
||||
"No annotations to export": "Aucune annotation à exporter",
|
||||
"Author": "Auteur",
|
||||
"Exported from Readest": "Exporté depuis Readest",
|
||||
"Highlights & Annotations": "Surlignages et annotations",
|
||||
"Note": "Note",
|
||||
"Copied to clipboard": "Copié dans le presse-papiers",
|
||||
"Export Annotations": "Exporter les annotations"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "छोटा",
|
||||
"Large": "बड़ा",
|
||||
"Auto": "स्वचालित",
|
||||
"Language": "भाषा"
|
||||
"Language": "भाषा",
|
||||
"No annotations to export": "निर्यात करने के लिए कोई टिप्पणियाँ नहीं हैं",
|
||||
"Author": "लेखक",
|
||||
"Exported from Readest": "Readest से निर्यात किया गया",
|
||||
"Highlights & Annotations": "हाइलाइट और टिप्पणियाँ",
|
||||
"Note": "टिप्पणी",
|
||||
"Copied to clipboard": "क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"Export Annotations": "टिप्पणियाँ निर्यात करें"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Kecil",
|
||||
"Large": "Besar",
|
||||
"Auto": "Otomatis",
|
||||
"Language": "Bahasa"
|
||||
"Language": "Bahasa",
|
||||
"No annotations to export": "Tidak ada anotasi yang diekspor",
|
||||
"Author": "Penulis",
|
||||
"Exported from Readest": "Diekspor dari Readest",
|
||||
"Highlights & Annotations": "Sorotan & Anotasi",
|
||||
"Note": "Catatan",
|
||||
"Copied to clipboard": "Disalin ke papan klip",
|
||||
"Export Annotations": "Ekspor Anotasi"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Piccolo",
|
||||
"Large": "Grande",
|
||||
"Auto": "Automatico",
|
||||
"Language": "Lingua"
|
||||
"Language": "Lingua",
|
||||
"No annotations to export": "Nessuna annotazione da esportare",
|
||||
"Author": "Autore",
|
||||
"Exported from Readest": "Esportato da Readest",
|
||||
"Highlights & Annotations": "Evidenziazioni e annotazioni",
|
||||
"Note": "Nota",
|
||||
"Copied to clipboard": "Copiato negli appunti",
|
||||
"Export Annotations": "Esporta annotazioni"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "小",
|
||||
"Large": "大",
|
||||
"Auto": "自動",
|
||||
"Language": "言語"
|
||||
"Language": "言語",
|
||||
"No annotations to export": "エクスポートする注釈はありません",
|
||||
"Author": "著者",
|
||||
"Exported from Readest": "Readestからエクスポート",
|
||||
"Highlights & Annotations": "ハイライトと注釈",
|
||||
"Note": "メモ",
|
||||
"Copied to clipboard": "クリップボードにコピー",
|
||||
"Export Annotations": "注釈をエクスポート"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "작음",
|
||||
"Large": "큼",
|
||||
"Auto": "자동",
|
||||
"Language": "언어"
|
||||
"Language": "언어",
|
||||
"No annotations to export": "내보낼 주석이 없습니다",
|
||||
"Author": "저자",
|
||||
"Exported from Readest": "Readest에서 내보냄",
|
||||
"Highlights & Annotations": "하이라이트 및 주석",
|
||||
"Note": "노트",
|
||||
"Copied to clipboard": "클립보드에 복사",
|
||||
"Export Annotations": "주석 내보내기"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Mały",
|
||||
"Large": "Duży",
|
||||
"Auto": "Automatyczny",
|
||||
"Language": "Język"
|
||||
"Language": "Język",
|
||||
"No annotations to export": "Brak adnotacji do eksportu",
|
||||
"Author": "Autor",
|
||||
"Exported from Readest": "Eksportowane z Readest",
|
||||
"Highlights & Annotations": "Zaznaczenia i adnotacje",
|
||||
"Note": "Notatka",
|
||||
"Copied to clipboard": "Skopiowano do schowka",
|
||||
"Export Annotations": "Eksportuj adnotacje"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Pequeno",
|
||||
"Large": "Grande",
|
||||
"Auto": "Automático",
|
||||
"Language": "Idioma"
|
||||
"Language": "Idioma",
|
||||
"No annotations to export": "Nenhuma anotação para exportar",
|
||||
"Author": "Autor",
|
||||
"Exported from Readest": "Exportado do Readest",
|
||||
"Highlights & Annotations": "Destaques e Anotações",
|
||||
"Note": "Nota",
|
||||
"Copied to clipboard": "Copiado para a área de transferência",
|
||||
"Export Annotations": "Exportar Anotações"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Маленький",
|
||||
"Large": "Большой",
|
||||
"Auto": "Авто",
|
||||
"Language": "Язык"
|
||||
"Language": "Язык",
|
||||
"No annotations to export": "Нет аннотаций для экспорта",
|
||||
"Author": "Автор",
|
||||
"Exported from Readest": "Экспортировано из Readest",
|
||||
"Highlights & Annotations": "Выделения и аннотации",
|
||||
"Note": "Заметка",
|
||||
"Copied to clipboard": "Скопировано в буфер обмена",
|
||||
"Export Annotations": "Экспортировать аннотации"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Küçük",
|
||||
"Large": "Büyük",
|
||||
"Auto": "Otomatik",
|
||||
"Language": "Dil"
|
||||
"Language": "Dil",
|
||||
"No annotations to export": "Dışa aktarılacak not yok",
|
||||
"Author": "Yazar",
|
||||
"Exported from Readest": "Readest'ten dışa aktarıldı",
|
||||
"Highlights & Annotations": "Vurgular ve Notlar",
|
||||
"Note": "Not",
|
||||
"Copied to clipboard": "Panoya kopyalandı",
|
||||
"Export Annotations": "Notları Dışa Aktar"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Малий",
|
||||
"Large": "Великий",
|
||||
"Auto": "Автоматичний",
|
||||
"Language": "Мова"
|
||||
"Language": "Мова",
|
||||
"No annotations to export": "Немає анотацій для експорту",
|
||||
"Author": "Автор",
|
||||
"Exported from Readest": "Експортовано з Readest",
|
||||
"Highlights & Annotations": "Виділення та анотації",
|
||||
"Note": "Примітка",
|
||||
"Copied to clipboard": "Скопійовано в буфер обміну",
|
||||
"Export Annotations": "Експортувати анотації"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "Nhỏ",
|
||||
"Large": "Lớn",
|
||||
"Auto": "Tự động",
|
||||
"Language": "Ngôn ngữ"
|
||||
"Language": "Ngôn ngữ",
|
||||
"No annotations to export": "Không có chú thích để xuất",
|
||||
"Author": "Tác giả",
|
||||
"Exported from Readest": "Đã xuất từ Readest",
|
||||
"Highlights & Annotations": "Điểm nổi bật & Chú thích",
|
||||
"Note": "Ghi chú",
|
||||
"Copied to clipboard": "Đã sao chép vào bảng ghi",
|
||||
"Export Annotations": "Xuất chú thích"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "小",
|
||||
"Large": "大",
|
||||
"Auto": "自动",
|
||||
"Language": "语言"
|
||||
"Language": "语言",
|
||||
"No annotations to export": "没有要导出的笔记",
|
||||
"Author": "作者",
|
||||
"Exported from Readest": "从 Readest 导出",
|
||||
"Highlights & Annotations": "划线和笔记",
|
||||
"Note": "笔记",
|
||||
"Copied to clipboard": "已复制到剪贴板",
|
||||
"Export Annotations": "导出笔记"
|
||||
}
|
||||
|
||||
@@ -251,5 +251,12 @@
|
||||
"Small": "小",
|
||||
"Large": "大",
|
||||
"Auto": "自動",
|
||||
"Language": "語言"
|
||||
"Language": "語言",
|
||||
"No annotations to export": "沒有要匯出的筆記",
|
||||
"Author": "作者",
|
||||
"Exported from Readest": "從 Readest 匯出",
|
||||
"Highlights & Annotations": "高亮和筆記",
|
||||
"Note": "筆記",
|
||||
"Copied to clipboard": "已複製到剪貼板",
|
||||
"Export Annotations": "匯出筆記"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FaHeadphones } from 'react-icons/fa6';
|
||||
import * as CFI from 'foliate-js/epubcfi.js';
|
||||
import { Overlayer } from 'foliate-js/overlayer.js';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { BookNote, HighlightColor, HighlightStyle } from '@/types/book';
|
||||
import { BookNote, BooknoteGroup, HighlightColor, HighlightStyle } from '@/types/book';
|
||||
import { getOSPlatform, uniqueId } from '@/utils/misc';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
@@ -24,6 +24,7 @@ import { useFoliateEvents } from '../../hooks/useFoliateEvents';
|
||||
import { useNotesSync } from '../../hooks/useNotesSync';
|
||||
import { getPopupPosition, getPosition, Position, TextSelection } from '@/utils/sel';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { findTocItemBS } from '@/utils/toc';
|
||||
import { HIGHLIGHT_COLOR_HEX } from '@/services/constants';
|
||||
import AnnotationPopup from './AnnotationPopup';
|
||||
import WiktionaryPopup from './WiktionaryPopup';
|
||||
@@ -240,8 +241,10 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
};
|
||||
|
||||
eventDispatcher.onSync('iframe-single-click', handleSingleClick);
|
||||
eventDispatcher.on('export-annotations', handleExportMarkdown);
|
||||
return () => {
|
||||
eventDispatcher.offSync('iframe-single-click', handleSingleClick);
|
||||
eventDispatcher.off('export-annotations', handleExportMarkdown);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -438,6 +441,90 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
eventDispatcher.dispatch('tts-speak', { bookKey, range: selection.range });
|
||||
};
|
||||
|
||||
const handleExportMarkdown = (event: CustomEvent) => {
|
||||
const { bookKey: exportBookKey } = event.detail;
|
||||
if (bookKey !== exportBookKey) return;
|
||||
|
||||
const { bookDoc, book } = bookData;
|
||||
if (!bookDoc || !book || !bookDoc.toc) return;
|
||||
|
||||
const { booknotes: allNotes = [] } = config;
|
||||
const booknotes = allNotes.filter((note) => !note.deletedAt);
|
||||
if (booknotes.length === 0) {
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'info',
|
||||
message: _('No annotations to export'),
|
||||
className: 'whitespace-nowrap',
|
||||
timeout: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const booknoteGroups: { [href: string]: BooknoteGroup } = {};
|
||||
for (const booknote of booknotes) {
|
||||
const tocItem = findTocItemBS(bookDoc.toc ?? [], booknote.cfi);
|
||||
const href = tocItem?.href || '';
|
||||
const label = tocItem?.label || '';
|
||||
const id = tocItem?.id || 0;
|
||||
if (!booknoteGroups[href]) {
|
||||
booknoteGroups[href] = { id, href, label, booknotes: [] };
|
||||
}
|
||||
booknoteGroups[href].booknotes.push(booknote);
|
||||
}
|
||||
|
||||
Object.values(booknoteGroups).forEach((group) => {
|
||||
group.booknotes.sort((a, b) => {
|
||||
return CFI.compare(a.cfi, b.cfi);
|
||||
});
|
||||
});
|
||||
|
||||
const sortedGroups = Object.values(booknoteGroups).sort((a, b) => {
|
||||
return a.id - b.id;
|
||||
});
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(`# ${book.title}`);
|
||||
lines.push(`**${_('Author')}**: ${book.author || ''}`);
|
||||
lines.push('');
|
||||
lines.push(`**${_('Exported from Readest')}**: ${new Date().toISOString().slice(0, 10)}`);
|
||||
lines.push('');
|
||||
lines.push('---');
|
||||
lines.push('');
|
||||
lines.push(`## ${_('Highlights & Annotations')}`);
|
||||
lines.push('');
|
||||
|
||||
for (const group of sortedGroups) {
|
||||
const chapterTitle = group.label || _('Untitled');
|
||||
lines.push(`### ${chapterTitle}`);
|
||||
for (const note of group.booknotes) {
|
||||
lines.push(`> "${note.text}"`);
|
||||
if (note.note) {
|
||||
lines.push(`**${_('Note')}**:: ${note.note}`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
lines.push('---');
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
const markdownContent = lines.join('\n');
|
||||
|
||||
navigator.clipboard?.writeText(markdownContent);
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'info',
|
||||
message: _('Copied to clipboard'),
|
||||
className: 'whitespace-nowrap',
|
||||
timeout: 2000,
|
||||
});
|
||||
if (appService?.isMobile) return;
|
||||
const blob = new Blob([markdownContent], { type: 'text/markdown;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${book.title.replace(/\s+/g, '_')}.md`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const selectionAnnotated = selection?.annotated;
|
||||
const buttons = [
|
||||
{ tooltipText: _('Copy'), Icon: FiCopy, onClick: handleCopy },
|
||||
|
||||
@@ -5,8 +5,10 @@ import Image from 'next/image';
|
||||
import MenuItem from '@/components/MenuItem';
|
||||
import { setAboutDialogVisible } from '@/components/AboutWindow';
|
||||
import { useLibraryStore } from '@/store/libraryStore';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { isWebAppPlatform } from '@/services/environment';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { DOWNLOAD_READEST_URL } from '@/services/constants';
|
||||
import useBooksManager from '../../hooks/useBooksManager';
|
||||
|
||||
@@ -19,6 +21,8 @@ const BookMenu: React.FC<BookMenuProps> = ({ menuClassName, setIsDropdownOpen })
|
||||
const _ = useTranslation();
|
||||
const { getVisibleLibrary } = useLibraryStore();
|
||||
const { openParallelView } = useBooksManager();
|
||||
const { sideBarBookKey } = useSidebarStore();
|
||||
|
||||
const handleParallelView = (id: string) => {
|
||||
openParallelView(id);
|
||||
setIsDropdownOpen?.(false);
|
||||
@@ -35,6 +39,10 @@ const BookMenu: React.FC<BookMenuProps> = ({ menuClassName, setIsDropdownOpen })
|
||||
window.open(DOWNLOAD_READEST_URL, '_blank');
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
const handleExportAnnotations = () => {
|
||||
eventDispatcher.dispatch('export-annotations', { bookKey: sideBarBookKey });
|
||||
setIsDropdownOpen?.(false);
|
||||
};
|
||||
|
||||
const isWebApp = isWebAppPlatform();
|
||||
|
||||
@@ -49,6 +57,7 @@ const BookMenu: React.FC<BookMenuProps> = ({ menuClassName, setIsDropdownOpen })
|
||||
<MenuItem label={_('Parallel Read')} noIcon>
|
||||
<ul className='max-h-60 overflow-y-auto'>
|
||||
{getVisibleLibrary()
|
||||
.filter((book) => book.format !== 'PDF' && book.format !== 'CBZ')
|
||||
.slice(0, 20)
|
||||
.map((book) => (
|
||||
<MenuItem
|
||||
@@ -72,6 +81,7 @@ const BookMenu: React.FC<BookMenuProps> = ({ menuClassName, setIsDropdownOpen })
|
||||
))}
|
||||
</ul>
|
||||
</MenuItem>
|
||||
<MenuItem label={_('Export Annotations')} noIcon onClick={handleExportAnnotations} />
|
||||
<MenuItem label={_('Reload Page')} noIcon shortcut='Shift+R' onClick={handleReloadPage} />
|
||||
<hr className='border-base-200 my-1' />
|
||||
{isWebApp && <MenuItem label={_('Download Readest')} noIcon onClick={downloadReadest} />}
|
||||
|
||||
@@ -4,16 +4,9 @@ import * as CFI from 'foliate-js/epubcfi.js';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { findTocItemBS } from '@/utils/toc';
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { BookNote, BookNoteType } from '@/types/book';
|
||||
import { BooknoteGroup, BookNoteType } from '@/types/book';
|
||||
import BooknoteItem from './BooknoteItem';
|
||||
|
||||
interface BooknoteGroup {
|
||||
id: number;
|
||||
href: string;
|
||||
label: string;
|
||||
booknotes: BookNote[];
|
||||
}
|
||||
|
||||
const BooknoteView: React.FC<{
|
||||
type: BookNoteType;
|
||||
bookKey: string;
|
||||
|
||||
@@ -53,6 +53,13 @@ export interface BookNote {
|
||||
deletedAt?: number | null;
|
||||
}
|
||||
|
||||
export interface BooknoteGroup {
|
||||
id: number;
|
||||
href: string;
|
||||
label: string;
|
||||
booknotes: BookNote[];
|
||||
}
|
||||
|
||||
export type WritingMode = 'auto' | 'horizontal-tb' | 'horizontal-rl' | 'vertical-rl';
|
||||
|
||||
export interface BookLayout {
|
||||
|
||||
Reference in New Issue
Block a user