diff --git a/data/com.github.johnfactotum.Foliate.gschema.xml b/data/com.github.johnfactotum.Foliate.gschema.xml index 78407cd7..96c4c936 100644 --- a/data/com.github.johnfactotum.Foliate.gschema.xml +++ b/data/com.github.johnfactotum.Foliate.gschema.xml @@ -48,6 +48,9 @@ 'yellow' + + '' + foliate-js/vendor/pdfjs/pdf.worker.js opds/main.js opds/main.html - opds/widgets.js + selection-tools/common.css + selection-tools/translate.html + selection-tools/wikipedia.html + selection-tools/wiktionary.html + common/widgets.js reader/reader.html reader/reader.js reader/markup.js diff --git a/src/opds/main.js b/src/opds/main.js index cd1a7ea3..20ed4fef 100644 --- a/src/opds/main.js +++ b/src/opds/main.js @@ -1,4 +1,4 @@ -import './widgets.js' +import '../common/widgets.js' import { REL, SYMBOL, isOPDSCatalog, getPublication, getFeed, getSearch, getOpenSearch } from '../foliate-js/opds.js' const emit = x => globalThis.webkit.messageHandlers.opds diff --git a/src/selection-tools.js b/src/selection-tools.js index c20bbec7..dfea1f52 100644 --- a/src/selection-tools.js +++ b/src/selection-tools.js @@ -25,284 +25,60 @@ const getGoogleTranslateLanguages = utils.memoize(() => { const displayName = new Intl.DisplayNames(locales, { type: 'language' }) const langs = ['af', 'sq', 'am', 'ar', 'hy', 'as', 'ay', 'az', 'bm', 'eu', 'be', 'bn', 'bho', 'bs', 'bg', 'ca', 'ceb', 'zh-CN', 'zh-TW', 'co', 'hr', 'cs', 'da', 'dv', 'doi', 'nl', 'en', 'eo', 'et', 'ee', 'fil', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el', 'gn', 'gu', 'ht', 'ha', 'haw', 'he', 'hi', 'hmn', 'hu', 'is', 'ig', 'ilo', 'id', 'ga', 'it', 'ja', 'jv', 'kn', 'kk', 'km', 'rw', 'gom', 'ko', 'kri', 'ku', 'ckb', 'ky', 'lo', 'la', 'lv', 'ln', 'lt', 'lg', 'lb', 'mk', 'mai', 'mg', 'ms', 'ml', 'mt', 'mi', 'mr', 'mni-Mtei', 'lus', 'mn', 'my', 'ne', 'no', 'ny', 'or', 'om', 'ps', 'fa', 'pl', 'pt', 'pa', 'qu', 'ro', 'ru', 'sm', 'sa', 'gd', 'nso', 'sr', 'st', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', 'su', 'sw', 'sv', 'tl', 'tg', 'ta', 'tt', 'te', 'th', 'ti', 'ts', 'tr', 'tk', 'ak', 'uk', 'ur', 'ug', 'uz', 'vi', 'cy', 'xh', 'yi', 'yo', 'zu'] const defaultLang = matchLocales(langs)[0] ?? 'en' - return JSON.stringify([langs.map(lang => [lang, displayName.of(lang)]), defaultLang]) + return [langs.map(lang => [lang, displayName.of(lang)]), defaultLang] }) -const commonStyle = ` -html, body { - color-scheme: light dark; - font: menu; -} -h1 { - font-size: larger; -} -h2 { - font-size: smaller; -} -a:any-link { - color: highlight; -} -ul, ol { - padding-inline-start: 2em; -} -footer { - font-size: smaller; - opacity: .6; -} -:is([data-state="loading"], [data-state="error"]) footer { - display: none; -} -[data-state="loaded"] footer { - display: block; -} -[data-state="error"] main { - display: flex; - position: absolute; - inset: 0; - width: 100%; - height: 100%; - text-align: center; - justify-content: center; - align-items: center; -} -` - const tools = { 'dictionary': { label: _('Dictionary'), - run: ({ text, lang }) => { - const language = getLanguage(lang) - return ` - - -
- -` - }, + uri: 'foliate-selection-tool:///selection-tools/wiktionary.html', + run: (__, { text, lang }) => ({ + msg: { + footer: _('From Wiktionary, released under the CC BY-SA License.'), + error: _('No Definitions Found'), + errorAction: _('Search on Wiktionary'), + }, + text, + lang: getLanguage(lang), + }), }, 'wikipedia': { label: _('Wikipedia'), - run: ({ text, lang }) => { - const language = getLanguage(lang) - return ` -
- -` - }, + uri: 'foliate-selection-tool:///selection-tools/wikipedia.html', + run: (__, { text, lang }) => ({ + msg: { + footer: _('From Wikipedia, released under the CC BY-SA License.'), + error: _('No Definitions Found'), + errorAction: _('Search on Wikipedia'), + }, + text, + lang: getLanguage(lang), + }), }, 'translate': { label: _('Translate'), - run: ({ text }) => ` -
-
- - -`, + uri: 'foliate-selection-tool:///selection-tools/translate.html', + run: (popover, { text }) => { + const [langs, defaultLang] = getGoogleTranslateLanguages() + return { + msg: { + footer: _('Translation by Google Translate'), + error: _('Cannot retrieve translation'), + search: _('Search…'), + langs, + }, + text, + lang: popover.translate_target_language || defaultLang, + } + }, }, } const SelectionToolPopover = GObject.registerClass({ GTypeName: 'FoliateSelectionToolPopover', + Properties: utils.makeParams({ + 'translate-target-language': 'string', + }), }, class extends Gtk.Popover { #webView = utils.connect(new WebView({ settings: new WebKit.Settings({ @@ -318,7 +94,7 @@ const SelectionToolPopover = GObject.registerClass({ case WebKit.PolicyDecisionType.NAVIGATION_ACTION: case WebKit.PolicyDecisionType.NEW_WINDOW_ACTION: { const { uri } = decision.navigation_action.get_request() - if (!uri.startsWith('foliate:')) { + if (!uri.startsWith('foliate-selection-tool:')) { decision.ignore() new Gtk.UriLauncher({ uri }).launch(this.root, null, null) return true @@ -329,16 +105,22 @@ const SelectionToolPopover = GObject.registerClass({ }) constructor(params) { super(params) + utils.bindSettings('viewer', this, ['translate-target-language']) Object.assign(this, { width_request: 300, height_request: 300, }) this.child = this.#webView this.#webView.set_background_color(new Gdk.RGBA()) + this.#webView.registerHandler('settings', payload => { + if (payload.key === 'translate-target-language') + this.translate_target_language = payload.value + }) } - load(html) { - this.#webView.loadHTML(html, 'foliate:selection-tool') + loadTool(tool, init) { + this.#webView.loadURI(tool.uri) .then(() => this.#webView.opacity = 1) + .then(() => this.#webView.exec('init', init)) .catch(e => console.error(e)) } }) @@ -366,8 +148,8 @@ export const SelectionPopover = GObject.registerClass({ const action = new Gio.SimpleAction({ name }) action.connect('activate', () => { const popover = getSelectionToolPopover() - Promise.resolve(tool.run(this.emit('run-tool'))) - .then(x => popover.load(x)) + Promise.resolve(tool.run(popover, this.emit('run-tool'))) + .then(x => popover.loadTool(tool, x)) .catch(e => console.error(e)) this.emit('show-popover', popover) }) diff --git a/src/selection-tools/common.css b/src/selection-tools/common.css new file mode 100644 index 00000000..032e7c03 --- /dev/null +++ b/src/selection-tools/common.css @@ -0,0 +1,39 @@ +* { + box-sizing: border-box; +} +html, body { + color-scheme: light dark; + font: menu; +} +h1 { + font-size: larger; +} +h2 { + font-size: smaller; +} +a:any-link { + color: highlight; +} +ul, ol { + padding-inline-start: 2em; +} +footer { + font-size: smaller; + opacity: .6; +} +:is([data-state="loading"], [data-state="error"]) footer { + display: none; +} +[data-state="loaded"] footer { + display: block; +} +[data-state="error"] main { + display: flex; + position: absolute; + inset: 0; + width: 100%; + height: 100%; + text-align: center; + justify-content: center; + align-items: center; +} diff --git a/src/selection-tools/translate.html b/src/selection-tools/translate.html new file mode 100644 index 00000000..d253610b --- /dev/null +++ b/src/selection-tools/translate.html @@ -0,0 +1,217 @@ + + + + + + +
+ +
+
+ + + +
+
+ + diff --git a/src/selection-tools/wikipedia.html b/src/selection-tools/wikipedia.html new file mode 100644 index 00000000..53f35e98 --- /dev/null +++ b/src/selection-tools/wikipedia.html @@ -0,0 +1,61 @@ + + + + + + +
+ + diff --git a/src/selection-tools/wiktionary.html b/src/selection-tools/wiktionary.html new file mode 100644 index 00000000..e417afd0 --- /dev/null +++ b/src/selection-tools/wiktionary.html @@ -0,0 +1,114 @@ + + + + + + + +
+ + diff --git a/src/webview.js b/src/webview.js index 7f1d2ab2..2d21c510 100644 --- a/src/webview.js +++ b/src/webview.js @@ -26,7 +26,8 @@ const registerPaths = (name, dirs) => registerScheme(name, req => { }) registerPaths('foliate', ['/reader/', '/foliate-js/']) -registerPaths('foliate-opds', ['/opds/', '/foliate-js/', '/icons/']) +registerPaths('foliate-opds', ['/opds/', '/foliate-js/', '/icons/', '/common/']) +registerPaths('foliate-selection-tool', ['/selection-tools/', '/icons/', '/common/']) /* `.run_javascript()` is hard to use if you're running an async function. You have