mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Content server viewer: Allow exporting all highlights
Fixes #1909529 [Browser viewer: Highlight management](https://bugs.launchpad.net/calibre/+bug/1909529)
This commit is contained in:
parent
d457997830
commit
fa85e012dd
@ -3,14 +3,18 @@
|
|||||||
from __python__ import bound_methods, hash_literals
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
from elementmaker import E
|
from elementmaker import E
|
||||||
from gettext import gettext as _, ngettext
|
|
||||||
|
|
||||||
|
from ajax import encode_query
|
||||||
from book_list.globals import get_session_data
|
from book_list.globals import get_session_data
|
||||||
from read_book.globals import is_dark_theme
|
|
||||||
from book_list.theme import get_color
|
from book_list.theme import get_color
|
||||||
from complete import create_search_bar
|
from complete import create_search_bar
|
||||||
from dom import add_extra_css, build_rule, clear, svgicon, unique_id
|
from dom import add_extra_css, build_rule, clear, svgicon, unique_id
|
||||||
from modals import error_dialog, get_text_dialog, question_dialog, warning_dialog
|
from gettext import gettext as _, ngettext
|
||||||
|
from modals import (
|
||||||
|
create_custom_dialog, error_dialog, get_text_dialog, question_dialog,
|
||||||
|
warning_dialog
|
||||||
|
)
|
||||||
|
from read_book.globals import is_dark_theme
|
||||||
from widgets import create_button
|
from widgets import create_button
|
||||||
|
|
||||||
ICON_SIZE_VAL = 3
|
ICON_SIZE_VAL = 3
|
||||||
@ -521,6 +525,84 @@ def get_container():
|
|||||||
return document.getElementById(get_container_id())
|
return document.getElementById(get_container_id())
|
||||||
|
|
||||||
|
|
||||||
|
def render_highlight_as_text(hl, lines, as_markdown=False, link_prefix=None):
|
||||||
|
lines.push(hl.highlighted_text)
|
||||||
|
date = Date(hl.timestamp).toLocaleString()
|
||||||
|
if as_markdown and link_prefix:
|
||||||
|
cfi = hl.start_cfi
|
||||||
|
spine_index = (1 + hl.spine_index) * 2
|
||||||
|
link = link_prefix + encode_query({'open_at': f'epubcfi(/{spine_index}{cfi})'})
|
||||||
|
date = f'[{date}]({link})'
|
||||||
|
lines.push(date)
|
||||||
|
notes = hl.notes
|
||||||
|
if notes:
|
||||||
|
lines.push('')
|
||||||
|
lines.push(notes)
|
||||||
|
lines.push('')
|
||||||
|
if as_markdown:
|
||||||
|
lines.push('-' * 20)
|
||||||
|
else:
|
||||||
|
lines.push('───')
|
||||||
|
lines.push('')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def show_export_dialog(annotations_manager):
|
||||||
|
sd = get_session_data()
|
||||||
|
fmt = sd.get('highlights_export_format')
|
||||||
|
if v"['text', 'markdown', 'calibre_annotations_collection']".indexOf(fmt) < 0:
|
||||||
|
fmt = 'text'
|
||||||
|
all_highlights = annotations_manager.all_highlights()
|
||||||
|
ta_id = unique_id()
|
||||||
|
|
||||||
|
def update_text():
|
||||||
|
if fmt is 'calibre_annotations_collection':
|
||||||
|
data = {
|
||||||
|
'version': 1,
|
||||||
|
'type': 'calibre_annotation_collection',
|
||||||
|
'annotations': all_highlights,
|
||||||
|
}
|
||||||
|
document.getElementById(ta_id).textContent = JSON.stringify(data, None, 2)
|
||||||
|
return
|
||||||
|
as_markdown = fmt is 'markdown'
|
||||||
|
lines = v'[]'
|
||||||
|
for hl in all_highlights:
|
||||||
|
render_highlight_as_text(hl, lines, as_markdown=as_markdown)
|
||||||
|
document.getElementById(ta_id).textContent = lines.join('\n')
|
||||||
|
|
||||||
|
def fmt_item(text, val):
|
||||||
|
ans = E.label(E.input(type='radio', name='format', value=val, checked=val is fmt), '\xa0', text)
|
||||||
|
ans.style.marginRight = '1rem'
|
||||||
|
ans.firstChild.addEventListener('change', def(ev):
|
||||||
|
nonlocal fmt
|
||||||
|
fmt = this.value
|
||||||
|
sd.set('highlights_export_format', this.value)
|
||||||
|
update_text()
|
||||||
|
)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
create_custom_dialog(_('Export highlights'), def (modal_container, close_modal):
|
||||||
|
modal_container.appendChild(E.div(
|
||||||
|
E.div(_('Format for exported highlights:')),
|
||||||
|
E.div(
|
||||||
|
fmt_item(_('Plain text'), 'text'),
|
||||||
|
fmt_item(_('Markdown'), 'markdown'),
|
||||||
|
fmt_item('calibre', 'calibre_annotations_collection'),
|
||||||
|
),
|
||||||
|
E.textarea(style='margin-top: 1ex; resize: none; max-height: 25vh', readonly=True, rows='20', cols='80', id=ta_id),
|
||||||
|
E.div(
|
||||||
|
class_='button-box',
|
||||||
|
create_button(_('Copy'), 'copy', def (ev):
|
||||||
|
x = document.getElementById(ta_id)
|
||||||
|
x.focus()
|
||||||
|
x.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
))
|
||||||
|
))
|
||||||
|
window.setTimeout(update_text, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def focus_search():
|
def focus_search():
|
||||||
c = get_container()
|
c = get_container()
|
||||||
c.querySelector('input').focus()
|
c.querySelector('input').focus()
|
||||||
@ -679,12 +761,14 @@ def create_highlights_panel(annotations_manager, book, container, onclick):
|
|||||||
prev_button.addEventListener('click', def(ev): find_previous();)
|
prev_button.addEventListener('click', def(ev): find_previous();)
|
||||||
sb = create_search_bar(find_next, 'search-in-highlights', placeholder=_('Search') + '…', button=next_button, associated_widgets=[prev_button])
|
sb = create_search_bar(find_next, 'search-in-highlights', placeholder=_('Search') + '…', button=next_button, associated_widgets=[prev_button])
|
||||||
sb.style.flexGrow = '10'
|
sb.style.flexGrow = '10'
|
||||||
|
export_button = create_button(_('Export'), 'cloud-download', show_export_dialog.bind(None, annotations_manager))
|
||||||
|
export_button.style.marginLeft = '1rem'
|
||||||
c = E.div(
|
c = E.div(
|
||||||
style='margin: 1rem',
|
style='margin: 1rem',
|
||||||
id=get_container_id(),
|
id=get_container_id(),
|
||||||
E.div(
|
E.div(
|
||||||
style='display: flex',
|
style='display: flex',
|
||||||
sb, next_button, prev_button
|
sb, next_button, prev_button, export_button
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container.appendChild(c)
|
container.appendChild(c)
|
||||||
|
@ -63,6 +63,7 @@ defaults = {
|
|||||||
'user_stylesheet': '',
|
'user_stylesheet': '',
|
||||||
'word_actions': v'[]',
|
'word_actions': v'[]',
|
||||||
'highlight_style': None,
|
'highlight_style': None,
|
||||||
|
'highlights_export_format': 'text',
|
||||||
'custom_highlight_styles': v'[]',
|
'custom_highlight_styles': v'[]',
|
||||||
'show_selection_bar': True,
|
'show_selection_bar': True,
|
||||||
'net_search_url': 'https://google.com/search?q={q}',
|
'net_search_url': 'https://google.com/search?q={q}',
|
||||||
@ -104,6 +105,7 @@ is_local_setting = {
|
|||||||
'tts': True,
|
'tts': True,
|
||||||
'tts_backend': True,
|
'tts_backend': True,
|
||||||
'fullscreen_when_opening': True,
|
'fullscreen_when_opening': True,
|
||||||
|
'highlights_export_format': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -170,7 +172,12 @@ class SessionData:
|
|||||||
if defval is undefined:
|
if defval is undefined:
|
||||||
defval = None
|
defval = None
|
||||||
return defval
|
return defval
|
||||||
return JSON.parse(ans)
|
try:
|
||||||
|
return JSON.parse(ans)
|
||||||
|
except:
|
||||||
|
if defval is undefined:
|
||||||
|
defval = None
|
||||||
|
return defval
|
||||||
|
|
||||||
def set(self, key, value):
|
def set(self, key, value):
|
||||||
key = self.global_prefix + key
|
key = self.global_prefix + key
|
||||||
|
Loading…
x
Reference in New Issue
Block a user