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