mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into next60
# Conflicts: # src/routes/api/options.js # src/services/import/zip.js # src/services/options_init.js
This commit is contained in:
		| @@ -8,6 +8,7 @@ import contextMenu from "./menus/context_menu.js"; | ||||
| import DesktopLayout from "./layouts/desktop_layout.js"; | ||||
| import glob from "./services/glob.js"; | ||||
| import zoomService from './components/zoom.js'; | ||||
| import options from "./services/options.js"; | ||||
|  | ||||
| bundleService.getWidgetBundlesByParent().then(widgetBundles => { | ||||
|     appContext.setLayout(new DesktopLayout(widgetBundles)); | ||||
| @@ -115,11 +116,27 @@ if (utils.isElectron()) { | ||||
|                 ? (`${params.selectionText.substr(0, 13)}…`) | ||||
|                 : params.selectionText; | ||||
|  | ||||
|             // Read the search engine from the options and fallback to DuckDuckGo if the option is not set. | ||||
|             const customSearchEngineName = options.get("customSearchEngineName"); | ||||
|             const customSearchEngineUrl = options.get("customSearchEngineUrl"); | ||||
|             let searchEngineName; | ||||
|             let searchEngineUrl; | ||||
|             if (customSearchEngineName && customSearchEngineUrl) { | ||||
|                 searchEngineName = customSearchEngineName; | ||||
|                 searchEngineUrl = customSearchEngineUrl; | ||||
|             } else { | ||||
|                 searchEngineName = "Duckduckgo"; | ||||
|                 searchEngineUrl = "https://duckduckgo.com/?q={keyword}"; | ||||
|             } | ||||
|  | ||||
|             // Replace the placeholder with the real search keyword. | ||||
|             let searchUrl = searchEngineUrl.replace("{keyword}", encodeURIComponent(params.selectionText)); | ||||
|  | ||||
|             items.push({ | ||||
|                 enabled: editFlags.canPaste, | ||||
|                 title: `Search for "${shortenedSelection}" with DuckDuckGo`, | ||||
|                 title: `Search for "${shortenedSelection}" with ${searchEngineName}`, | ||||
|                 uiIcon: "bx bx-search-alt", | ||||
|                 handler: () => electron.shell.openExternal(`https://duckduckgo.com/?q=${encodeURIComponent(params.selectionText)}`) | ||||
|                 handler: () => electron.shell.openExternal(searchUrl) | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import PasswordOptions from "./options/password.js"; | ||||
| import EtapiOptions from "./options/etapi.js"; | ||||
| import BackupOptions from "./options/backup.js"; | ||||
| import SyncOptions from "./options/sync.js"; | ||||
| import SearchEngineOptions from "./options/other/search_engine.js"; | ||||
| import TrayOptions from "./options/other/tray.js"; | ||||
| import NoteErasureTimeoutOptions from "./options/other/note_erasure_timeout.js"; | ||||
| import NoteRevisionsSnapshotIntervalOptions from "./options/other/note_revisions_snapshot_interval.js"; | ||||
| @@ -76,6 +77,7 @@ const CONTENT_WIDGETS = { | ||||
|     _optionsBackup: [ BackupOptions ], | ||||
|     _optionsSync: [ SyncOptions ], | ||||
|     _optionsOther: [ | ||||
|         SearchEngineOptions, | ||||
|         TrayOptions, | ||||
|         NoteErasureTimeoutOptions, | ||||
|         AttachmentErasureTimeoutOptions, | ||||
|   | ||||
| @@ -0,0 +1,78 @@ | ||||
| import OptionsWidget from "../options_widget.js"; | ||||
| import utils from "../../../../services/utils.js"; | ||||
|  | ||||
| const TPL = ` | ||||
| <div class="options-section"> | ||||
|  | ||||
|  | ||||
|     <h4>Search Engine</h4> | ||||
|      | ||||
|     <p>Custom search engine requires both a name and a URL to be set. If either of these is not set, DuckDuckGo will be used as the default search engine.</p> | ||||
|      | ||||
|     <form class="sync-setup-form"> | ||||
|         <div class="form-group"> | ||||
|             <label>Predefined search engine templates</label> | ||||
|             <select class="predefined-search-engine-select form-control"> | ||||
|                 <option value="Bing">Bing</option> | ||||
|                 <option value="Baidu">Baidu</option> | ||||
|                 <option value="Duckduckgo">Duckduckgo</option> | ||||
|                 <option value="Google">Google</option> | ||||
|             </select> | ||||
|         </div> | ||||
|          | ||||
|         <div class="form-group"> | ||||
|             <label>Custom search engine name</label> | ||||
|             <input type="text" class="custom-search-engine-name form-control" placeholder="Customize search engine name"> | ||||
|         </div> | ||||
|          | ||||
|         <div class="form-group"> | ||||
|             <label>Custom search engine URL should include <code>{keyword}</code> as a placeholder for the search term.</label> | ||||
|             <input type="text" class="custom-search-engine-url form-control" placeholder="Customize search engine url"> | ||||
|         </div> | ||||
|          | ||||
|         <div style="display: flex; justify-content: space-between;"> | ||||
|             <button class="btn btn-primary">Save</button> | ||||
|         </div> | ||||
|     </form> | ||||
| </div>`; | ||||
|  | ||||
| const SEARCH_ENGINES = { | ||||
|     "Bing": "https://www.bing.com/search?q={keyword}", | ||||
|     "Baidu": "https://www.baidu.com/s?wd={keyword}", | ||||
|     "Duckduckgo": "https://duckduckgo.com/?q={keyword}", | ||||
|     "Google": "https://www.google.com/search?q={keyword}", | ||||
| } | ||||
|  | ||||
| export default class SearchEngineOptions extends OptionsWidget { | ||||
|     isEnabled() { | ||||
|         return super.isEnabled() && utils.isElectron(); | ||||
|     } | ||||
|  | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|  | ||||
|         this.$form = this.$widget.find(".sync-setup-form"); | ||||
|         this.$predefinedSearchEngineSelect = this.$widget.find(".predefined-search-engine-select"); | ||||
|         this.$customSearchEngineName = this.$widget.find(".custom-search-engine-name"); | ||||
|         this.$customSearchEngineUrl = this.$widget.find(".custom-search-engine-url"); | ||||
|  | ||||
|         this.$predefinedSearchEngineSelect.on('change', () => { | ||||
|             const predefinedSearchEngine = this.$predefinedSearchEngineSelect.val(); | ||||
|             this.$customSearchEngineName[0].value = predefinedSearchEngine; | ||||
|             this.$customSearchEngineUrl[0].value = SEARCH_ENGINES[predefinedSearchEngine]; | ||||
|         }); | ||||
|  | ||||
|         this.$form.on('submit', () => { | ||||
|             this.updateMultipleOptions({ | ||||
|                 'customSearchEngineName': this.$customSearchEngineName.val(), | ||||
|                 'customSearchEngineUrl': this.$customSearchEngineUrl.val() | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     async optionsLoaded(options) { | ||||
|         this.$predefinedSearchEngineSelect.val(""); | ||||
|         this.$customSearchEngineName[0].value = options.customSearchEngineName; | ||||
|         this.$customSearchEngineUrl[0].value = options.customSearchEngineUrl; | ||||
|     } | ||||
| } | ||||
| @@ -62,7 +62,10 @@ const ALLOWED_OPTIONS = new Set([ | ||||
|     'minTocHeadings', | ||||
|     'checkForUpdates', | ||||
|     'disableTray', | ||||
|     'eraseUnusedImageAttachmentsAfterSeconds' | ||||
|     'eraseUnusedImageAttachmentsAfterSeconds', | ||||
|     'disableTray', | ||||
|     'customSearchEngineName', | ||||
|     'customSearchEngineUrl', | ||||
| ]); | ||||
|  | ||||
| function getOptions() { | ||||
|   | ||||
| @@ -318,6 +318,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) | ||||
|         if (noteMeta.format === 'html') { | ||||
|             if (!content.substr(0, 100).toLowerCase().includes("<html")) { | ||||
|                 const cssUrl = `${"../".repeat(noteMeta.notePath.length - 1)}style.css`; | ||||
|                 const htmlTitle = utils.escapeHtml(title); | ||||
|  | ||||
|                 // <base> element will make sure external links are openable - https://github.com/zadam/trilium/issues/1289#issuecomment-704066809 | ||||
|                 content = `<html> | ||||
| @@ -326,10 +327,11 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="${cssUrl}"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>${htmlTitle}</title> | ||||
| </head> | ||||
| <body> | ||||
|   <div class="content"> | ||||
|       <h1>${utils.escapeHtml(title)}</h1> | ||||
|       <h1 data-trilium-h1>${htmlTitle}</h1> | ||||
|        | ||||
|       <div class="ck-content">${content}</div> | ||||
|   </div> | ||||
| @@ -504,7 +506,7 @@ ${markdownContent}`; | ||||
|     const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames); | ||||
|  | ||||
|     const metaFile = { | ||||
|         formatVersion: 1, | ||||
|         formatVersion: 2, | ||||
|         appVersion: packageInfo.version, | ||||
|         files: [ rootMeta ] | ||||
|     }; | ||||
|   | ||||
| @@ -314,6 +314,8 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|             return /^(?:[a-z]+:)?\/\//i.test(url); | ||||
|         } | ||||
|  | ||||
|         content = removeTrilumTags(content); | ||||
|  | ||||
|         content = content.replace(/<h1>([^<]*)<\/h1>/gi, (match, text) => { | ||||
|             if (noteTitle.trim() === text.trim()) { | ||||
|                 return ""; // remove whole H1 tag | ||||
| @@ -403,6 +405,18 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|         return content; | ||||
|     } | ||||
|  | ||||
|     function removeTrilumTags(content) { | ||||
|         const tagsToRemove = [ | ||||
|             '<h1 data-trilium-h1>([^<]*)<\/h1>', | ||||
|             '<title data-trilium-title>([^<]*)<\/title>' | ||||
|         ] | ||||
|         for (const tag of tagsToRemove) { | ||||
|             let re = new RegExp(tag, "gi"); | ||||
|             content = content.replace(re, ''); | ||||
|         } | ||||
|         return content; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {NoteMeta} noteMeta | ||||
|      * @param {string} type | ||||
|   | ||||
| @@ -89,6 +89,8 @@ const defaultOptions = [ | ||||
|     { name: 'checkForUpdates', value: 'true', isSynced: true }, | ||||
|     { name: 'disableTray', value: 'false', isSynced: false }, | ||||
|     { name: 'eraseUnusedImageAttachmentsAfterSeconds', value: '86400', isSynced: false }, | ||||
|     { name: 'customSearchEngineName', value: 'Duckduckgo', isSynced: false }, | ||||
|     { name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: false }, | ||||
| ]; | ||||
|  | ||||
| function initStartupOptions() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user