mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 19:49:01 +01:00 
			
		
		
		
	Merge branch 'develop' into quick_search_in_autocomplete
This commit is contained in:
		
						commit
						0d9e13c1e0
					
				
							
								
								
									
										12
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							| @ -3,6 +3,12 @@ description: Report a bug | ||||
| title: "(Bug report) " | ||||
| labels: "Type: Bug" | ||||
| body: | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Description | ||||
|     description: A clear and concise description of the bug and any additional information. | ||||
|   validations: | ||||
|     required: true | ||||
| - type: input | ||||
|   attributes: | ||||
|     label: TriliumNext Version | ||||
| @ -38,12 +44,6 @@ body: | ||||
|     placeholder: "e.g. Windows 10 version 1909, macOS Catalina 10.15.7, or Ubuntu 20.04" | ||||
|   validations: | ||||
|     required: true | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Description | ||||
|     description: A clear and concise description of the bug and any additional information. | ||||
|   validations: | ||||
|     required: true | ||||
| - type: textarea | ||||
|   attributes: | ||||
|     label: Error logs | ||||
|  | ||||
| @ -17,17 +17,45 @@ | ||||
| # | ||||
| # -------------------------------------------------------------------------------------------------- | ||||
| 
 | ||||
| number_of_keys() { | ||||
| 	[ -f "$1" ] && jq 'path(..) | select(length == 2) | .[1]' "$1" | wc -l || echo "0" | ||||
| } | ||||
| 
 | ||||
| stats() { | ||||
| 	# Print the number of existing strings on the JSON files for each locale | ||||
| 	s=$(jq 'path(..) | select(length == 2) | .[1]' "${paths[0]}/en/server.json" | wc -l) | ||||
| 	c=$(jq 'path(..) | select(length == 2) | .[1]' "${paths[1]}/en/translation.json" | wc -l) | ||||
| 	echo "|locale |server strings |client strings |" | ||||
| 	echo "|-------|---------------|---------------|" | ||||
| 	echo "|  en   |      ${s}      |     ${c}      |" | ||||
| 	s=$(number_of_keys "${paths[0]}/en/server.json") | ||||
| 	c=$(number_of_keys "${paths[1]}/en/translation.json") | ||||
| 	echo "| locale |server strings |client strings |" | ||||
| 	echo "|--------|---------------|---------------|" | ||||
| 	echo "|   en   |      ${s}      |     ${c}      |" | ||||
| 	for locale in "${locales[@]}"; do | ||||
| 		s=$(jq 'path(..) | select(length == 2) | .[1]' "${paths[0]}/${locale}/server.json" | wc -l) | ||||
| 		c=$(jq 'path(..) | select(length == 2) | .[1]' "${paths[1]}/${locale}/translation.json" | wc -l) | ||||
| 		echo "|  ${locale}   |      ${s}      |     ${c}      |" | ||||
| 		s=$(number_of_keys "${paths[0]}/${locale}/server.json") | ||||
| 		c=$(number_of_keys "${paths[1]}/${locale}/translation.json") | ||||
| 		n1=$(((8 - ${#locale}) / 2)) | ||||
| 		n2=$((n1 == 1 ? n1 + 1 : n1)) | ||||
| 		echo "|$(printf "%${n1}s")${locale}$(printf "%${n2}s")|      ${s}      |     ${c}      |" | ||||
| 	done | ||||
| } | ||||
| 
 | ||||
| update_1() { | ||||
| 	# Update PO files from English and localized JSON files as source | ||||
| 	# NOTE: if you want a new language you need to first create the JSON files | ||||
| 	# on their corresponding place with `{}` as content to avoid error on `json2po` | ||||
| 	local locales=("$@") | ||||
| 	for path in "${paths[@]}"; do | ||||
| 		for locale in "${locales[@]}"; do | ||||
| 			json2po -t "${path}/en" "${path}/${locale}" "${path}/po-${locale}" | ||||
| 		done | ||||
| 	done | ||||
| } | ||||
| 
 | ||||
| update_2() { | ||||
| 	# Recover translation from PO files to localized JSON files | ||||
| 	local locales=("$@") | ||||
| 	for path in "${paths[@]}"; do | ||||
| 		for locale in "${locales[@]}"; do | ||||
| 			po2json -t "${path}/en" "${path}/po-${locale}" "${path}/${locale}" | ||||
| 		done | ||||
| 	done | ||||
| } | ||||
| 
 | ||||
| @ -35,11 +63,11 @@ help() { | ||||
| 	echo -e "\nDescription:" | ||||
| 	echo -e "\tCreate PO files to make easier the labor of translation" | ||||
| 	echo -e "\nUsage:" | ||||
| 	echo -e "\t./translation.sh [--stats] [--update <OPT_LOCALE>] [--update2 <OPT_LOCALE>]" | ||||
| 	echo -e "\t./translation.sh [--stats] [--update1 <OPT_LOCALE>] [--update2 <OPT_LOCALE>]" | ||||
| 	echo -e "\nFlags:" | ||||
| 	echo -e "  --clear\n\tClear all po-* directories" | ||||
| 	echo -e "  --stats\n\tPrint the number of existing strings on the JSON files for each locale" | ||||
| 	echo -e "  --update <LOCALE>\n\tUpdate PO files from English and localized JSON files as source" | ||||
| 	echo -e "  --update1 <LOCALE>\n\tUpdate PO files from English and localized JSON files as source" | ||||
| 	echo -e "  --update2 <LOCALE>\n\tRecover translation from PO files to localized JSON files" | ||||
| } | ||||
| 
 | ||||
| @ -51,7 +79,7 @@ file_path="$( | ||||
| 	pwd -P | ||||
| )" | ||||
| paths=("${file_path}/../translations/" "${file_path}/../src/public/translations/") | ||||
| locales=(cn es fr ro) | ||||
| locales=(cn de es fr pt_br ro tw) | ||||
| 
 | ||||
| if [ $# -eq 1 ]; then | ||||
| 	if [ "$1" == "--clear" ]; then | ||||
| @ -62,34 +90,18 @@ if [ $# -eq 1 ]; then | ||||
| 		done | ||||
| 	elif [ "$1" == "--stats" ]; then | ||||
| 		stats | ||||
| 	elif [ "$1" == "--update" ]; then | ||||
| 		# Update PO files from English and localized JSON files as source | ||||
| 		for path in "${paths[@]}"; do | ||||
| 			for locale in "${locales[@]}"; do | ||||
| 				json2po -t "${path}/en" "${path}/${locale}" "${path}/po-${locale}" | ||||
| 			done | ||||
| 		done | ||||
| 	elif [ "$1" == "--update1" ]; then | ||||
| 		update_1 "${locales[@]}" | ||||
| 	elif [ "$1" == "--update2" ]; then | ||||
| 		# Recover translation from PO files to localized JSON files | ||||
| 		for path in "${paths[@]}"; do | ||||
| 			for locale in "${locales[@]}"; do | ||||
| 				po2json -t "${path}/en" "${path}/po-${locale}" "${path}/${locale}" | ||||
| 			done | ||||
| 		done | ||||
| 		update_2 "${locales[@]}" | ||||
| 	else | ||||
| 		help | ||||
| 	fi | ||||
| elif [ $# -eq 2 ]; then | ||||
| 	if [ "$1" == "--update" ]; then | ||||
| 		locale="$2" | ||||
| 		for path in "${paths[@]}"; do | ||||
| 			json2po -t "${path}/en" "${path}/${locale}" "${path}/po-${locale}" | ||||
| 		done | ||||
| 	if [ "$1" == "--update1" ]; then | ||||
| 		update_1 "$2" | ||||
| 	elif [ "$1" == "--update2" ]; then | ||||
| 		locale="$2" | ||||
| 		for path in "${paths[@]}"; do | ||||
| 			po2json -t "${path}/en" "${path}/po-${locale}" "${path}/${locale}" | ||||
| 		done | ||||
| 		update_2 "$2" | ||||
| 	else | ||||
| 		help | ||||
| 	fi | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								db/demo.zip
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/demo.zip
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								libraries/mermaid-elk/elk.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								libraries/mermaid-elk/elk.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										13
									
								
								libraries/mermaid-elk/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								libraries/mermaid-elk/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| { | ||||
|   "name": "mermaid-elk", | ||||
|   "version": "1.0.0", | ||||
|   "lockfileVersion": 3, | ||||
|   "requires": true, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "mermaid-elk", | ||||
|       "version": "1.0.0", | ||||
|       "license": "ISC" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								libraries/mermaid-elk/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								libraries/mermaid-elk/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| { | ||||
|   "name": "mermaid-elk", | ||||
|   "version": "1.0.0", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|     "build": "cross-env node --import ../../loader-register.js ../../node_modules/webpack/bin/webpack.js -c webpack.config.cjs" | ||||
|   }, | ||||
|   "keywords": [], | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "description": "", | ||||
|   "dependencies": {} | ||||
| } | ||||
							
								
								
									
										19
									
								
								libraries/mermaid-elk/webpack.config.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								libraries/mermaid-elk/webpack.config.cjs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| const path = require("path"); | ||||
| const webpack = require("webpack"); | ||||
| 
 | ||||
| module.exports = { | ||||
|     mode: "production", | ||||
|     entry: "../../node_modules/@mermaid-js/layout-elk/dist/mermaid-layout-elk.esm.min.mjs", | ||||
|     output: { | ||||
|         library: "MERMAID_ELK", | ||||
|         filename: "elk.min.js", | ||||
|         path: path.resolve(__dirname), | ||||
|         libraryTarget: "umd", | ||||
|         libraryExport: "default" | ||||
|     }, | ||||
|     plugins: [ | ||||
|         new webpack.optimize.LimitChunkCountPlugin({ | ||||
|             maxChunks: 1 | ||||
|         }) | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										22
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,18 +1,19 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "version": "0.90.11-beta", | ||||
|   "version": "0.90.12", | ||||
|   "lockfileVersion": 3, | ||||
|   "requires": true, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "name": "trilium", | ||||
|       "version": "0.90.11-beta", | ||||
|       "version": "0.90.12", | ||||
|       "license": "AGPL-3.0-only", | ||||
|       "dependencies": { | ||||
|         "@braintree/sanitize-url": "7.1.0", | ||||
|         "@electron/remote": "2.1.2", | ||||
|         "@excalidraw/excalidraw": "0.17.6", | ||||
|         "@highlightjs/cdn-assets": "11.10.0", | ||||
|         "@mermaid-js/layout-elk": "0.1.5", | ||||
|         "archiver": "7.0.1", | ||||
|         "async-mutex": "0.5.0", | ||||
|         "autocomplete.js": "0.38.1", | ||||
| @ -3072,6 +3073,18 @@ | ||||
|         "url": "https://github.com/malept/cross-spawn-promise?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mermaid-js/layout-elk": { | ||||
|       "version": "0.1.5", | ||||
|       "resolved": "https://registry.npmjs.org/@mermaid-js/layout-elk/-/layout-elk-0.1.5.tgz", | ||||
|       "integrity": "sha512-6ML4iWdVdyIkSW47KiID9runHzaomLxdMfNo9U60LJvfcQkB/FAjg0Vjc4AZEQnnBq7ibAoAknAWlT1XetwXSg==", | ||||
|       "dependencies": { | ||||
|         "d3": "^7.9.0", | ||||
|         "elkjs": "^0.9.3" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "mermaid": "^11.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mermaid-js/parser": { | ||||
|       "version": "0.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", | ||||
| @ -7904,6 +7917,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", | ||||
|       "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" | ||||
|     }, | ||||
|     "node_modules/elkjs": { | ||||
|       "version": "0.9.3", | ||||
|       "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", | ||||
|       "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==" | ||||
|     }, | ||||
|     "node_modules/emitter-listener": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "TriliumNext Notes", | ||||
|   "description": "Build your personal knowledge base with TriliumNext Notes", | ||||
|   "version": "0.90.11-beta", | ||||
|   "version": "0.90.12", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "./dist/electron-main.js", | ||||
|   "author": { | ||||
| @ -54,6 +54,7 @@ | ||||
|     "@electron/remote": "2.1.2", | ||||
|     "@excalidraw/excalidraw": "0.17.6", | ||||
|     "@highlightjs/cdn-assets": "11.10.0", | ||||
|     "@mermaid-js/layout-elk": "0.1.5", | ||||
|     "archiver": "7.0.1", | ||||
|     "async-mutex": "0.5.0", | ||||
|     "autocomplete.js": "0.38.1", | ||||
|  | ||||
| @ -88,8 +88,11 @@ export default class Component { | ||||
| 
 | ||||
|         if (fun) { | ||||
|             return this.callMethod(fun, data); | ||||
|         } | ||||
|         else { | ||||
|         } else { | ||||
|             if (!this.parent) { | ||||
|                 throw new Error(`Component "${this.componentId}" does not have a parent attached to propagate a command.`); | ||||
|             } | ||||
| 
 | ||||
|             return this.parent.triggerCommand(name, data); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -83,6 +83,7 @@ import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js"; | ||||
| import CopyImageReferenceButton from "../widgets/floating_buttons/copy_image_reference_button.js"; | ||||
| import ScrollPaddingWidget from "../widgets/scroll_padding.js"; | ||||
| import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js"; | ||||
| import options from "../services/options.js"; | ||||
| 
 | ||||
| export default class DesktopLayout { | ||||
|     constructor(customWidgets) { | ||||
| @ -92,112 +93,120 @@ export default class DesktopLayout { | ||||
|     getRootWidget(appContext) { | ||||
|         appContext.noteTreeWidget = new NoteTreeWidget(); | ||||
| 
 | ||||
|         return new RootContainer() | ||||
|         const launcherPaneIsHorizontal = (options.get("layoutOrientation") === "horizontal"); | ||||
|         const launcherPane = this.#buildLauncherPane(launcherPaneIsHorizontal); | ||||
| 
 | ||||
|         return new RootContainer(launcherPaneIsHorizontal) | ||||
|             .setParent(appContext) | ||||
|             .child(new FlexContainer("column") | ||||
|                 .id("launcher-pane") | ||||
|                 .css("width", "53px") | ||||
|                 .child(new GlobalMenuWidget()) | ||||
|                 .child(new LauncherContainer()) | ||||
|                 .child(new LeftPaneToggleWidget()) | ||||
|             .optChild(launcherPaneIsHorizontal, new FlexContainer('row')                | ||||
|                 .child(new TabRowWidget().class("full-width")) | ||||
|                 .child(new TitleBarButtonsWidget()) | ||||
|                 .css('height', '40px') | ||||
|                 .css('background-color', 'var(--launcher-pane-background-color)') | ||||
|                 .setParent(appContext) | ||||
|             ) | ||||
|             .child(new LeftPaneContainer() | ||||
|                 .child(new QuickSearchWidget()) | ||||
|                 .child(appContext.noteTreeWidget) | ||||
|                 .child(...this.customWidgets.get('left-pane')) | ||||
|             ) | ||||
|             .child(new FlexContainer('column') | ||||
|                 .id('rest-pane') | ||||
|             .optChild(launcherPaneIsHorizontal, launcherPane) | ||||
|             .child(new FlexContainer('row') | ||||
|                 .css("flex-grow", "1") | ||||
|                 .child(new FlexContainer('row') | ||||
|                     .child(new TabRowWidget()) | ||||
|                     .child(new TitleBarButtonsWidget()) | ||||
|                     .css('height', '40px') | ||||
|                 .optChild(!launcherPaneIsHorizontal, launcherPane) | ||||
|                 .child(new LeftPaneContainer() | ||||
|                     .optChild(!launcherPaneIsHorizontal, new QuickSearchWidget()) | ||||
|                     .child(appContext.noteTreeWidget) | ||||
|                     .child(...this.customWidgets.get('left-pane')) | ||||
|                 ) | ||||
|                 .child(new FlexContainer('row') | ||||
|                     .filling() | ||||
|                     .collapsible() | ||||
|                     .child(new FlexContainer('column') | ||||
|                 .child(new FlexContainer('column') | ||||
|                     .id('rest-pane') | ||||
|                     .css("flex-grow", "1") | ||||
|                     .optChild(!launcherPaneIsHorizontal, new FlexContainer('row') | ||||
|                         .child(new TabRowWidget()) | ||||
|                         .child(new TitleBarButtonsWidget()) | ||||
|                         .css('height', '40px') | ||||
|                     ) | ||||
|                     .child(new FlexContainer('row') | ||||
|                         .filling() | ||||
|                         .collapsible() | ||||
|                         .id('center-pane') | ||||
|                         .child(new SplitNoteContainer(() => | ||||
|                                 new NoteWrapperWidget() | ||||
|                                     .child(new FlexContainer('row').class('title-row') | ||||
|                                         .css("height", "50px") | ||||
|                                         .css("min-height", "50px") | ||||
|                                         .css('align-items', "center") | ||||
|                                         .cssBlock('.title-row > * { margin: 5px; }') | ||||
|                                         .child(new NoteIconWidget()) | ||||
|                                         .child(new NoteTitleWidget()) | ||||
|                                         .child(new SpacerWidget(0, 1)) | ||||
|                                         .child(new MovePaneButton(true)) | ||||
|                                         .child(new MovePaneButton(false)) | ||||
|                                         .child(new ClosePaneButton()) | ||||
|                                         .child(new CreatePaneButton()) | ||||
|                                     ) | ||||
|                                     .child( | ||||
|                                         new RibbonContainer() | ||||
|                                             // the order of the widgets matter. Some of these want to "activate" themselves
 | ||||
|                                             // when visible. When this happens to multiple of them, the first one "wins".
 | ||||
|                                             // promoted attributes should always win.
 | ||||
|                                             .ribbon(new ClassicEditorToolbar()) | ||||
|                                             .ribbon(new PromotedAttributesWidget()) | ||||
|                                             .ribbon(new ScriptExecutorWidget()) | ||||
|                                             .ribbon(new SearchDefinitionWidget()) | ||||
|                                             .ribbon(new EditedNotesWidget()) | ||||
|                                             .ribbon(new BookPropertiesWidget()) | ||||
|                                             .ribbon(new NotePropertiesWidget()) | ||||
|                                             .ribbon(new FilePropertiesWidget()) | ||||
|                                             .ribbon(new ImagePropertiesWidget()) | ||||
|                                             .ribbon(new BasicPropertiesWidget()) | ||||
|                                             .ribbon(new OwnedAttributeListWidget()) | ||||
|                                             .ribbon(new InheritedAttributesWidget()) | ||||
|                                             .ribbon(new NotePathsWidget()) | ||||
|                                             .ribbon(new NoteMapRibbonWidget()) | ||||
|                                             .ribbon(new SimilarNotesWidget()) | ||||
|                                             .ribbon(new NoteInfoWidget()) | ||||
|                                             .button(new RevisionsButton()) | ||||
|                                             .button(new NoteActionsWidget()) | ||||
|                                     ) | ||||
|                                     .child(new SharedInfoWidget()) | ||||
|                                     .child(new WatchedFileUpdateStatusWidget()) | ||||
|                                     .child(new FloatingButtons() | ||||
|                                         .child(new EditButton()) | ||||
|                                         .child(new ShowTocWidgetButton()) | ||||
|                                         .child(new ShowHighlightsListWidgetButton()) | ||||
|                                         .child(new CodeButtonsWidget()) | ||||
|                                         .child(new RelationMapButtons()) | ||||
|                                         .child(new CopyImageReferenceButton()) | ||||
|                                         .child(new SvgExportButton()) | ||||
|                                         .child(new BacklinksWidget()) | ||||
|                                         .child(new HideFloatingButtonsButton()) | ||||
|                                     ) | ||||
|                                     .child(new MermaidWidget()) | ||||
|                                     .child( | ||||
|                                         new ScrollingContainer() | ||||
|                                             .filling() | ||||
|                                             .child(new SqlTableSchemasWidget()) | ||||
|                                             .child(new NoteDetailWidget()) | ||||
|                                             .child(new NoteListWidget()) | ||||
|                                             .child(new SearchResultWidget()) | ||||
|                                             .child(new SqlResultWidget()) | ||||
|                                             .child(new ScrollPaddingWidget()) | ||||
|                                     ) | ||||
|                                     .child(new ApiLogWidget()) | ||||
|                                     .child(new FindWidget()) | ||||
|                                     .child( | ||||
|                                         ...this.customWidgets.get('node-detail-pane'), // typo, let's keep it for a while as BC
 | ||||
|                                         ...this.customWidgets.get('note-detail-pane') | ||||
|                                     ) | ||||
|                         .child(new FlexContainer('column') | ||||
|                             .filling() | ||||
|                             .collapsible() | ||||
|                             .id('center-pane') | ||||
|                             .child(new SplitNoteContainer(() => | ||||
|                                     new NoteWrapperWidget() | ||||
|                                         .child(new FlexContainer('row').class('title-row') | ||||
|                                             .css("height", "50px") | ||||
|                                             .css("min-height", "50px") | ||||
|                                             .css('align-items', "center") | ||||
|                                             .cssBlock('.title-row > * { margin: 5px; }') | ||||
|                                             .child(new NoteIconWidget()) | ||||
|                                             .child(new NoteTitleWidget()) | ||||
|                                             .child(new SpacerWidget(0, 1)) | ||||
|                                             .child(new MovePaneButton(true)) | ||||
|                                             .child(new MovePaneButton(false)) | ||||
|                                             .child(new ClosePaneButton()) | ||||
|                                             .child(new CreatePaneButton()) | ||||
|                                         ) | ||||
|                                         .child( | ||||
|                                             new RibbonContainer() | ||||
|                                                 // the order of the widgets matter. Some of these want to "activate" themselves
 | ||||
|                                                 // when visible. When this happens to multiple of them, the first one "wins".
 | ||||
|                                                 // promoted attributes should always win.
 | ||||
|                                                 .ribbon(new ClassicEditorToolbar()) | ||||
|                                                 .ribbon(new PromotedAttributesWidget()) | ||||
|                                                 .ribbon(new ScriptExecutorWidget()) | ||||
|                                                 .ribbon(new SearchDefinitionWidget()) | ||||
|                                                 .ribbon(new EditedNotesWidget()) | ||||
|                                                 .ribbon(new BookPropertiesWidget()) | ||||
|                                                 .ribbon(new NotePropertiesWidget()) | ||||
|                                                 .ribbon(new FilePropertiesWidget()) | ||||
|                                                 .ribbon(new ImagePropertiesWidget()) | ||||
|                                                 .ribbon(new BasicPropertiesWidget()) | ||||
|                                                 .ribbon(new OwnedAttributeListWidget()) | ||||
|                                                 .ribbon(new InheritedAttributesWidget()) | ||||
|                                                 .ribbon(new NotePathsWidget()) | ||||
|                                                 .ribbon(new NoteMapRibbonWidget()) | ||||
|                                                 .ribbon(new SimilarNotesWidget()) | ||||
|                                                 .ribbon(new NoteInfoWidget()) | ||||
|                                                 .button(new RevisionsButton()) | ||||
|                                                 .button(new NoteActionsWidget()) | ||||
|                                         ) | ||||
|                                         .child(new SharedInfoWidget()) | ||||
|                                         .child(new WatchedFileUpdateStatusWidget()) | ||||
|                                         .child(new FloatingButtons() | ||||
|                                             .child(new EditButton()) | ||||
|                                             .child(new ShowTocWidgetButton()) | ||||
|                                             .child(new ShowHighlightsListWidgetButton()) | ||||
|                                             .child(new CodeButtonsWidget()) | ||||
|                                             .child(new RelationMapButtons()) | ||||
|                                             .child(new CopyImageReferenceButton()) | ||||
|                                             .child(new SvgExportButton()) | ||||
|                                             .child(new BacklinksWidget()) | ||||
|                                             .child(new HideFloatingButtonsButton()) | ||||
|                                         ) | ||||
|                                         .child(new MermaidWidget()) | ||||
|                                         .child( | ||||
|                                             new ScrollingContainer() | ||||
|                                                 .filling() | ||||
|                                                 .child(new SqlTableSchemasWidget()) | ||||
|                                                 .child(new NoteDetailWidget()) | ||||
|                                                 .child(new NoteListWidget()) | ||||
|                                                 .child(new SearchResultWidget()) | ||||
|                                                 .child(new SqlResultWidget()) | ||||
|                                                 .child(new ScrollPaddingWidget()) | ||||
|                                         ) | ||||
|                                         .child(new ApiLogWidget()) | ||||
|                                         .child(new FindWidget()) | ||||
|                                         .child( | ||||
|                                             ...this.customWidgets.get('node-detail-pane'), // typo, let's keep it for a while as BC
 | ||||
|                                             ...this.customWidgets.get('note-detail-pane') | ||||
|                                         ) | ||||
|                                 ) | ||||
|                             ) | ||||
|                             .child(...this.customWidgets.get('center-pane')) | ||||
|                         ) | ||||
|                         .child(new RightPaneContainer() | ||||
|                             .child(new TocWidget()) | ||||
|                             .child(new HighlightsListWidget()) | ||||
|                             .child(...this.customWidgets.get('right-pane')) | ||||
|                         ) | ||||
|                         .child(...this.customWidgets.get('center-pane')) | ||||
|                     ) | ||||
|                     .child(new RightPaneContainer() | ||||
|                         .child(new TocWidget()) | ||||
|                         .child(new HighlightsListWidget()) | ||||
|                         .child(...this.customWidgets.get('right-pane')) | ||||
|                     ) | ||||
|                 ) | ||||
|             ) | ||||
| @ -225,4 +234,27 @@ export default class DesktopLayout { | ||||
|             .child(new ConfirmDialog()) | ||||
|             .child(new PromptDialog()); | ||||
|     } | ||||
| 
 | ||||
|     #buildLauncherPane(isHorizontal) { | ||||
|         let launcherPane;         | ||||
| 
 | ||||
|         if (isHorizontal) { | ||||
|             launcherPane = new FlexContainer("row") | ||||
|                 .css("height", "53px") | ||||
|                 .class("horizontal") | ||||
|                 .child(new LeftPaneToggleWidget(true)) | ||||
|                 .child(new LauncherContainer(true)) | ||||
|                 .child(new GlobalMenuWidget(true)) | ||||
|         } else { | ||||
|             launcherPane = new FlexContainer("column") | ||||
|                 .css("width", "53px") | ||||
|                 .class("vertical") | ||||
|                 .child(new GlobalMenuWidget(false)) | ||||
|                 .child(new LauncherContainer(false)) | ||||
|                 .child(new LeftPaneToggleWidget(false)); | ||||
|         } | ||||
| 
 | ||||
|         launcherPane.id("launcher-pane"); | ||||
|         return launcherPane; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -24,6 +24,7 @@ import RootContainer from "../widgets/containers/root_container.js"; | ||||
| import SharedInfoWidget from "../widgets/shared_info.js"; | ||||
| import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js"; | ||||
| import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js"; | ||||
| import options from "../services/options.js"; | ||||
| 
 | ||||
| const MOBILE_CSS = ` | ||||
| <style> | ||||
| @ -112,15 +113,12 @@ span.fancytree-expander { | ||||
| 
 | ||||
| export default class MobileLayout { | ||||
|     getRootWidget(appContext) { | ||||
|         return new RootContainer() | ||||
|         const launcherPaneIsHorizontal = (options.get("layoutOrientation") === "horizontal"); | ||||
| 
 | ||||
|         return new RootContainer(launcherPaneIsHorizontal) | ||||
|             .setParent(appContext) | ||||
|             .cssBlock(MOBILE_CSS) | ||||
|             .child(new FlexContainer("column") | ||||
|                 .id("launcher-pane") | ||||
|                 .css("width", "53px") | ||||
|                 .child(new GlobalMenuWidget()) | ||||
|                 .child(new LauncherContainer()) | ||||
|             ) | ||||
|             .child(this.#buildLauncherPane(launcherPaneIsHorizontal)) | ||||
|             .child(new FlexContainer("row") | ||||
|                 .filling() | ||||
|                 .child(new ScreenContainer("tree", 'column') | ||||
| @ -140,12 +138,14 @@ export default class MobileLayout { | ||||
|                     .child(new FlexContainer('row').contentSized() | ||||
|                         .css('font-size', 'larger') | ||||
|                         .css('align-items', 'center') | ||||
|                         .child(new MobileDetailMenuWidget().contentSized()) | ||||
|                         .optChild(!launcherPaneIsHorizontal, new MobileDetailMenuWidget(false).contentSized()) | ||||
|                         .child(new NoteTitleWidget() | ||||
|                             .contentSized() | ||||
|                             .css("position: relative;") | ||||
|                             .css("top: 5px;") | ||||
|                             .optCss(launcherPaneIsHorizontal, "padding-left", "0.5em") | ||||
|                         ) | ||||
|                         .optChild(launcherPaneIsHorizontal, new MobileDetailMenuWidget(true).contentSized()) | ||||
|                         .child(new CloseDetailButtonWidget().contentSized())) | ||||
|                     .child(new SharedInfoWidget()) | ||||
|                     .child(new FloatingButtons() | ||||
| @ -174,4 +174,25 @@ export default class MobileLayout { | ||||
|                 .child(new ConfirmDialog()) | ||||
|             ); | ||||
|     } | ||||
| 
 | ||||
|     #buildLauncherPane(isHorizontal) { | ||||
|         let launcherPane; | ||||
| 
 | ||||
|         if (isHorizontal) { | ||||
|             launcherPane = new FlexContainer("row") | ||||
|                 .class("horizontal") | ||||
|                 .css("height", "53px") | ||||
|                 .child(new LauncherContainer(true)) | ||||
|                 .child(new GlobalMenuWidget(true)); | ||||
|         } else { | ||||
|             launcherPane = new FlexContainer("column")                 | ||||
|                 .class("vertical") | ||||
|                 .css("width", "53px") | ||||
|                 .child(new GlobalMenuWidget(false)) | ||||
|                 .child(new LauncherContainer(false)); | ||||
|         } | ||||
| 
 | ||||
|         launcherPane.id("launcher-pane"); | ||||
|         return launcherPane; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { t } from '../services/i18n.js'; | ||||
| import utils from "../services/utils.js"; | ||||
| import contextMenu from "./context_menu.js"; | ||||
| import imageService from "../services/image.js"; | ||||
| @ -18,14 +19,14 @@ function setupContextMenu($image) { | ||||
|             y: e.pageY, | ||||
|             items: [ | ||||
|                 { | ||||
|                     title: "Copy reference to clipboard", | ||||
|                     title: t("image_context_menu.copy_reference_to_clipboard"), | ||||
|                     command: "copyImageReferenceToClipboard", | ||||
|                     uiIcon: "bx bx-directions" | ||||
|                 }, | ||||
|                 {  | ||||
|                     title: "Copy image to clipboard", | ||||
|                 { | ||||
|                     title: t("image_context_menu.copy_image_to_clipboard"), | ||||
|                     command: "copyImageToClipboard", | ||||
|                     uiIcon: "bx bx-copy"  | ||||
|                     uiIcon: "bx bx-copy" | ||||
|                 }, | ||||
|             ], | ||||
|             selectMenuItemHandler: async ({ command }) => { | ||||
| @ -61,4 +62,4 @@ function setupContextMenu($image) { | ||||
| 
 | ||||
| export default { | ||||
|     setupContextMenu | ||||
| }; | ||||
| }; | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { t } from "../services/i18n.js"; | ||||
| import contextMenu from "./context_menu.js"; | ||||
| import appContext from "../components/app_context.js"; | ||||
| 
 | ||||
| @ -6,9 +7,9 @@ function openContextMenu(notePath, e, viewScope = {}, hoistedNoteId = null) { | ||||
|         x: e.pageX, | ||||
|         y: e.pageY, | ||||
|         items: [ | ||||
|             {title: "Open note in a new tab", command: "openNoteInNewTab", uiIcon: "bx bx-link-external"}, | ||||
|             {title: "Open note in a new split", command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right"}, | ||||
|             {title: "Open note in a new window", command: "openNoteInNewWindow", uiIcon: "bx bx-window-open"} | ||||
|             {title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external"}, | ||||
|             {title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right"}, | ||||
|             {title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open"} | ||||
|         ], | ||||
|         selectMenuItemHandler: ({command}) => { | ||||
|             if (!hoistedNoteId) { | ||||
|  | ||||
| @ -12,6 +12,7 @@ import FAttachment from "../entities/fattachment.js"; | ||||
| import imageContextMenuService from "../menus/image_context_menu.js"; | ||||
| import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js"; | ||||
| import mime_types from "./mime_types.js"; | ||||
| import { loadElkIfNeeded } from "./mermaid.js"; | ||||
| 
 | ||||
| let idCounter = 1; | ||||
| 
 | ||||
| @ -237,6 +238,7 @@ async function renderMermaid(note, $renderedContent) { | ||||
|     mermaid.mermaidAPI.initialize({startOnLoad: false, theme: mermaidTheme.trim(), securityLevel: 'antiscript'}); | ||||
| 
 | ||||
|     try { | ||||
|         await loadElkIfNeeded(content); | ||||
|         const {svg} = await mermaid.mermaidAPI.render("in-mermaid-graph-" + idCounter++, content); | ||||
| 
 | ||||
|         $renderedContent.append($(svg)); | ||||
|  | ||||
| @ -58,7 +58,19 @@ const FORCE_GRAPH = { | ||||
| }; | ||||
| 
 | ||||
| const MERMAID = { | ||||
|     js: [ "node_modules/mermaid/dist/mermaid.min.js" ] | ||||
|     js: [ | ||||
|         "node_modules/mermaid/dist/mermaid.min.js"         | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * The ELK extension of Mermaid.js, which supports more advanced layouts. | ||||
|  * See https://www.npmjs.com/package/@mermaid-js/layout-elk for more information.
 | ||||
|  */ | ||||
| const MERMAID_ELK = { | ||||
|     js: [ | ||||
|         "libraries/mermaid-elk/elk.min.js" | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| const EXCALIDRAW = { | ||||
| @ -197,6 +209,7 @@ export default { | ||||
|     WHEEL_ZOOM, | ||||
|     FORCE_GRAPH, | ||||
|     MERMAID, | ||||
|     MERMAID_ELK, | ||||
|     EXCALIDRAW, | ||||
|     MARKJS, | ||||
|     I18NEXT, | ||||
|  | ||||
							
								
								
									
										28
									
								
								src/public/app/services/mermaid.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/public/app/services/mermaid.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| import library_loader from "./library_loader.js"; | ||||
| 
 | ||||
| let elkLoaded = false; | ||||
| 
 | ||||
| /** | ||||
|  * Determines whether the ELK extension of Mermaid.js needs to be loaded (which is a relatively large library), based on the | ||||
|  * front-matter of the diagram and loads the library if needed. | ||||
|  *  | ||||
|  * <p> | ||||
|  * If the library has already been loaded or the diagram does not require it, the method will exit immediately. | ||||
|  *  | ||||
|  * @param mermaidContent the plain text of the mermaid diagram, potentially including a frontmatter. | ||||
|  */ | ||||
| export async function loadElkIfNeeded(mermaidContent) { | ||||
|     if (elkLoaded) { | ||||
|         // Exit immediately since the ELK library is already loaded.
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const parsedContent = await mermaid.parse(mermaidContent, { | ||||
|         suppressErrors: true  | ||||
|     }); | ||||
|     if (parsedContent?.config?.layout === "elk") { | ||||
|         elkLoaded = true; | ||||
|         await library_loader.requireLibrary(library_loader.MERMAID_ELK); | ||||
|         mermaid.registerLayoutLoaders(MERMAID_ELK); | ||||
|     }     | ||||
| } | ||||
| @ -6,7 +6,7 @@ function parse(value) { | ||||
|         if (token === 'promoted') { | ||||
|             defObj.isPromoted = true; | ||||
|         } | ||||
|         else if (['text', 'number', 'boolean', 'date', 'datetime', 'url'].includes(token)) { | ||||
|         else if (['text', 'number', 'boolean', 'date', 'datetime', 'time', 'url'].includes(token)) { | ||||
|             defObj.labelType = token; | ||||
|         } | ||||
|         else if (['single', 'multi'].includes(token)) { | ||||
|  | ||||
| @ -125,6 +125,7 @@ const TPL = ` | ||||
|                   <option value="boolean">${t('attribute_detail.boolean')}</option> | ||||
|                   <option value="date">${t('attribute_detail.date')}</option> | ||||
|                   <option value="datetime">${t('attribute_detail.date_time')}</option> | ||||
|                   <option value="time">${t('attribute_detail.time')}</option> | ||||
|                   <option value="url">${t('attribute_detail.url')}</option> | ||||
|                 </select> | ||||
|             </td> | ||||
|  | ||||
| @ -40,6 +40,21 @@ class BasicWidget extends Component { | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Conditionally adds the given components as children to this component. | ||||
|      *  | ||||
|      * @param {boolean} condition whether to add the components. | ||||
|      * @param  {...any} components the components to be added as children to this component provided the condition is truthy.  | ||||
|      * @returns self for chaining. | ||||
|      */ | ||||
|     optChild(condition, ...components) { | ||||
|         if (condition) { | ||||
|             return this.child(...components); | ||||
|         } else { | ||||
|             return this; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     id(id) { | ||||
|         this.attrs.id = id; | ||||
|         return this; | ||||
| @ -50,11 +65,34 @@ class BasicWidget extends Component { | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the CSS attribute of the given name to the given value. | ||||
|      *  | ||||
|      * @param {string} name the name of the CSS attribute to set (e.g. `padding-left`). | ||||
|      * @param {string} value the value of the CSS attribute to set (e.g. `12px`). | ||||
|      * @returns self for chaining. | ||||
|      */ | ||||
|     css(name, value) { | ||||
|         this.attrs.style += `${name}: ${value};`; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the CSS attribute of the given name to the given value, but only if the condition provided is truthy. | ||||
|      *  | ||||
|      * @param {boolean} condition `true` in order to apply the CSS, `false` to ignore it. | ||||
|      * @param {string} name the name of the CSS attribute to set (e.g. `padding-left`). | ||||
|      * @param {string} value the value of the CSS attribute to set (e.g. `12px`). | ||||
|      * @returns self for chaining. | ||||
|      */ | ||||
|     optCss(condition, name, value) { | ||||
|         if (condition) { | ||||
|             return this.css(name, value); | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     contentSized() { | ||||
|         this.css("contain", "none"); | ||||
| 
 | ||||
|  | ||||
| @ -4,8 +4,8 @@ import BookmarkFolderWidget from "./buttons/bookmark_folder.js"; | ||||
| import froca from "../services/froca.js"; | ||||
| 
 | ||||
| export default class BookmarkButtons extends FlexContainer { | ||||
|     constructor() { | ||||
|         super("column"); | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(isHorizontalLayout ? "row" : "column"); | ||||
| 
 | ||||
|         this.contentSized(); | ||||
|     } | ||||
|  | ||||
| @ -23,7 +23,11 @@ export default class AbstractButtonWidget extends NoteContextAwareWidget { | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.tooltip = new bootstrap.Tooltip(this.$widget, { | ||||
|             html: true, title: () => this.getTitle(), trigger: 'hover' | ||||
|             html: true, | ||||
|             title: () => this.getTitle(), | ||||
|             trigger: 'hover', | ||||
|             placement: this.settings.titlePlacement, | ||||
|             fallbackPlacements: [ this.settings.titlePlacement ] | ||||
|         }) | ||||
| 
 | ||||
|         if (this.settings.onContextMenu) { | ||||
| @ -36,8 +40,6 @@ export default class AbstractButtonWidget extends NoteContextAwareWidget { | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         this.$widget.attr("data-placement", this.settings.titlePlacement); | ||||
| 
 | ||||
|         super.doRender(); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ import UpdateAvailableWidget from "./update_available.js"; | ||||
| import options from "../../services/options.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="dropdown global-menu dropend"> | ||||
| <div class="dropdown global-menu"> | ||||
|     <style> | ||||
|     .global-menu { | ||||
|         width: 53px; | ||||
| @ -107,22 +107,6 @@ const TPL = ` | ||||
| 
 | ||||
|     <button type="button" data-bs-toggle="dropdown" aria-haspopup="true" | ||||
|             aria-expanded="false" class="icon-action global-menu-button"> | ||||
|         <svg viewBox="0 0 256 256" data-bs-toggle="tooltip" title="${t('global_menu.menu')}"> | ||||
|             <g> | ||||
|                 <path class="st0" d="m202.9 112.7c-22.5 16.1-54.5 12.8-74.9 6.3l14.8-11.8 14.1-11.3 49.1-39.3-51.2 35.9-14.3 10-14.9 10.5c0.7-21.2 7-49.9 28.6-65.4 1.8-1.3 3.9-2.6 6.1-3.8 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.4 2.8-4.9 5.4-7.4 7.8-3.4 3.5-6.8 6.4-10.1 8.8z"/> | ||||
|                 <path class="st1" d="m213.1 104c-22.2 12.6-51.4 9.3-70.3 3.2l14.1-11.3 49.1-39.3-51.2 35.9-14.3 10c0.5-18.1 4.9-42.1 19.7-58.6 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.3 2.8-4.8 5.4-7.2 7.8z"/> | ||||
|                 <path class="st2" d="m220.5 96.2c-21.1 8.6-46.6 5.3-63.7-0.2l49.2-39.4-51.2 35.9c0.3-15.8 3.5-36.6 14.3-52.8 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.8 66z"/> | ||||
|              | ||||
|                 <path class="st3" d="m106.7 179c-5.8-21 5.2-43.8 15.5-57.2l4.8 14.2 4.5 13.4 15.9 47-12.8-47.6-3.6-13.2-3.7-13.9c15.5 6.2 35.1 18.6 40.7 38.8 0.5 1.7 0.9 3.6 1.2 5.5 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.1-3.8-7.6-1.6-3.5-2.9-6.8-3.8-10z"/> | ||||
|                 <path class="st4" d="m110.4 188.9c-3.4-19.8 6.9-40.5 16.6-52.9l4.5 13.4 15.9 47-12.8-47.6-3.6-13.2c13.3 5.2 29.9 15 38.1 30.4 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.2-3.8-7.7z"/> | ||||
|                 <path class="st5" d="m114.2 196.5c-0.7-18 8.6-35.9 17.3-47.1l15.9 47-12.8-47.6c11.6 4.4 26.1 12.4 35.2 24.8 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8z"/> | ||||
|      | ||||
|                 <path class="st6" d="m86.3 59.1c21.7 10.9 32.4 36.6 35.8 54.9l-15.2-6.6-14.5-6.3-50.6-22 48.8 24.9 13.6 6.9 14.3 7.3c-16.6 7.9-41.3 14.5-62.1 4.1-1.8-0.9-3.6-1.9-5.4-3.2-2.3-1.5-4.5-3.2-6.8-5.1-19.9-16.4-40.3-46.4-42.7-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.2 0.8 6.2 1.6 9.1 2.5 4 1.3 7.6 2.8 10.9 4.4z"/> | ||||
|                 <path class="st7" d="m75.4 54.8c18.9 12 28.4 35.6 31.6 52.6l-14.5-6.3-50.6-22 48.7 24.9 13.6 6.9c-14.1 6.8-34.5 13-53.3 8.2-2.3-1.5-4.5-3.2-6.8-5.1-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.1 0.8 6.2 1.6 9.1 2.6z"/> | ||||
|                 <path class="st8" d="m66.3 52.2c15.3 12.8 23.3 33.6 26.1 48.9l-50.6-22 48.8 24.9c-12.2 6-29.6 11.8-46.5 10-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3z"/> | ||||
|             </g> | ||||
|         </svg> | ||||
| 
 | ||||
|         <div class="global-menu-button-update-available"></div> | ||||
|     </button> | ||||
| 
 | ||||
| @ -235,7 +219,7 @@ const TPL = ` | ||||
|             ${t('global_menu.options')} | ||||
|         </li> | ||||
| 
 | ||||
|         <div class="dropdown-divider"></div> | ||||
|         <div class="dropdown-divider desktop-only"></div> | ||||
| 
 | ||||
|         <li class="dropdown-item show-help-button" data-trigger-command="showHelp"> | ||||
|             <span class="bx bx-help-circle"></span> | ||||
| @ -265,18 +249,46 @@ const TPL = ` | ||||
| `;
 | ||||
| 
 | ||||
| export default class GlobalMenuWidget extends BasicWidget { | ||||
|     constructor() { | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.updateAvailableWidget = new UpdateAvailableWidget(); | ||||
|         this.isHorizontalLayout = isHorizontalLayout;         | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
| 
 | ||||
|         this.dropdown = bootstrap.Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")); | ||||
|         if (!this.isHorizontalLayout) { | ||||
|             this.$widget.addClass("dropend"); | ||||
|         } | ||||
| 
 | ||||
|         this.tooltip = new bootstrap.Tooltip(this.$widget.find("[data-bs-toggle='tooltip']"), { trigger: "hover" }); | ||||
|         const $globalMenuButton = this.$widget.find(".global-menu-button") | ||||
|         if (!this.isHorizontalLayout) { | ||||
|             $globalMenuButton.prepend($(`\
 | ||||
|                 <svg viewBox="0 0 256 256" data-bs-toggle="tooltip" title="${t('global_menu.menu')}"> | ||||
|                     <g> | ||||
|                         <path class="st0" d="m202.9 112.7c-22.5 16.1-54.5 12.8-74.9 6.3l14.8-11.8 14.1-11.3 49.1-39.3-51.2 35.9-14.3 10-14.9 10.5c0.7-21.2 7-49.9 28.6-65.4 1.8-1.3 3.9-2.6 6.1-3.8 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.4 2.8-4.9 5.4-7.4 7.8-3.4 3.5-6.8 6.4-10.1 8.8z"/> | ||||
|                         <path class="st1" d="m213.1 104c-22.2 12.6-51.4 9.3-70.3 3.2l14.1-11.3 49.1-39.3-51.2 35.9-14.3 10c0.5-18.1 4.9-42.1 19.7-58.6 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.3 2.8-4.8 5.4-7.2 7.8z"/> | ||||
|                         <path class="st2" d="m220.5 96.2c-21.1 8.6-46.6 5.3-63.7-0.2l49.2-39.4-51.2 35.9c0.3-15.8 3.5-36.6 14.3-52.8 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.8 66z"/> | ||||
|                      | ||||
|                         <path class="st3" d="m106.7 179c-5.8-21 5.2-43.8 15.5-57.2l4.8 14.2 4.5 13.4 15.9 47-12.8-47.6-3.6-13.2-3.7-13.9c15.5 6.2 35.1 18.6 40.7 38.8 0.5 1.7 0.9 3.6 1.2 5.5 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.1-3.8-7.6-1.6-3.5-2.9-6.8-3.8-10z"/> | ||||
|                         <path class="st4" d="m110.4 188.9c-3.4-19.8 6.9-40.5 16.6-52.9l4.5 13.4 15.9 47-12.8-47.6-3.6-13.2c13.3 5.2 29.9 15 38.1 30.4 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.2-3.8-7.7z"/> | ||||
|                         <path class="st5" d="m114.2 196.5c-0.7-18 8.6-35.9 17.3-47.1l15.9 47-12.8-47.6c11.6 4.4 26.1 12.4 35.2 24.8 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8z"/> | ||||
|              | ||||
|                         <path class="st6" d="m86.3 59.1c21.7 10.9 32.4 36.6 35.8 54.9l-15.2-6.6-14.5-6.3-50.6-22 48.8 24.9 13.6 6.9 14.3 7.3c-16.6 7.9-41.3 14.5-62.1 4.1-1.8-0.9-3.6-1.9-5.4-3.2-2.3-1.5-4.5-3.2-6.8-5.1-19.9-16.4-40.3-46.4-42.7-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.2 0.8 6.2 1.6 9.1 2.5 4 1.3 7.6 2.8 10.9 4.4z"/> | ||||
|                         <path class="st7" d="m75.4 54.8c18.9 12 28.4 35.6 31.6 52.6l-14.5-6.3-50.6-22 48.7 24.9 13.6 6.9c-14.1 6.8-34.5 13-53.3 8.2-2.3-1.5-4.5-3.2-6.8-5.1-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.1 0.8 6.2 1.6 9.1 2.6z"/> | ||||
|                         <path class="st8" d="m66.3 52.2c15.3 12.8 23.3 33.6 26.1 48.9l-50.6-22 48.8 24.9c-12.2 6-29.6 11.8-46.5 10-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3z"/> | ||||
|                     </g> | ||||
|                 </svg>`)); | ||||
|             this.tooltip = new bootstrap.Tooltip(this.$widget.find("[data-bs-toggle='tooltip']"), { trigger: "hover" }); | ||||
|         } else { | ||||
|             $globalMenuButton.toggleClass("bx bx-menu"); | ||||
|         } | ||||
| 
 | ||||
|         this.dropdown = bootstrap.Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']"), { | ||||
|             alignment: "bottom" | ||||
|         }); | ||||
| 
 | ||||
|         this.$widget.find(".show-about-dialog-button").on('click', () => this.triggerCommand("openAboutDialog")); | ||||
| 
 | ||||
| @ -300,7 +312,7 @@ export default class GlobalMenuWidget extends BasicWidget { | ||||
|             if ($(e.target).children(".dropdown-menu").length === 1 || $(e.target).hasClass('dropdown-toggle')) { | ||||
|                 e.stopPropagation(); | ||||
|             } | ||||
|         }) | ||||
|         })         | ||||
| 
 | ||||
|         this.$widget.find(".global-menu-button-update-available").append( | ||||
|             this.updateAvailableWidget.render() | ||||
| @ -316,10 +328,14 @@ export default class GlobalMenuWidget extends BasicWidget { | ||||
|         this.$zoomState = this.$widget.find(".zoom-state"); | ||||
|         this.$widget.on('show.bs.dropdown', () => { | ||||
|             this.updateZoomState(); | ||||
|             this.tooltip.hide(); | ||||
|             this.tooltip.disable(); | ||||
|             if (this.tooltip) { | ||||
|                 this.tooltip.hide(); | ||||
|                 this.tooltip.disable(); | ||||
|             } | ||||
|         }); | ||||
|         this.$widget.on('hide.bs.dropdown', () => this.tooltip.enable()); | ||||
|         if (this.tooltip) { | ||||
|             this.$widget.on('hide.bs.dropdown', () => this.tooltip.enable()); | ||||
|         } | ||||
| 
 | ||||
|         this.$widget.find(".zoom-buttons").on("click", | ||||
|             // delay to wait for the actual zoom change
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ import CommandButtonWidget from "./command_button.js"; | ||||
| import { t } from "../../services/i18n.js"; | ||||
| 
 | ||||
| export default class LeftPaneToggleWidget extends CommandButtonWidget { | ||||
|     constructor() { | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.class("launcher-button"); | ||||
| @ -20,6 +20,10 @@ export default class LeftPaneToggleWidget extends CommandButtonWidget { | ||||
|         this.settings.command = () => options.is('leftPaneVisible') | ||||
|             ? "hideLeftPane" | ||||
|             : "showLeftPane"; | ||||
| 
 | ||||
|         if (isHorizontalLayout) { | ||||
|             this.settings.titlePlacement = "bottom"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     refreshIcon() { | ||||
|  | ||||
| @ -2,13 +2,7 @@ import BasicWidget from "../basic_widget.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="dropdown right-dropdown-widget dropend"> | ||||
|     <style> | ||||
|     .right-dropdown-widget { | ||||
|         height: 53px; | ||||
|     } | ||||
|     </style> | ||||
| 
 | ||||
|     <button type="button" data-bs-toggle="dropdown" data-placement="right" | ||||
|     <button type="button" data-bs-toggle="dropdown" | ||||
|             aria-haspopup="true" aria-expanded="false"  | ||||
|             class="bx right-dropdown-button launcher-button"></button> | ||||
|      | ||||
| @ -25,6 +19,10 @@ export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|         this.iconClass = iconClass; | ||||
|         this.title = title; | ||||
|         this.dropdownTpl = dropdownTpl; | ||||
| 
 | ||||
|         this.settings = { | ||||
|             titlePlacement: "right"     | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
| @ -33,7 +31,10 @@ export default class RightDropdownButtonWidget extends BasicWidget { | ||||
|         this.dropdown = bootstrap.Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")); | ||||
| 
 | ||||
|         this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title); | ||||
|         this.tooltip = new bootstrap.Tooltip(this.$tooltip); | ||||
|         this.tooltip = new bootstrap.Tooltip(this.$tooltip, { | ||||
|             placement: this.settings.titlePlacement, | ||||
|             fallbackPlacements: [ this.settings.titlePlacement ] | ||||
|         }); | ||||
| 
 | ||||
|         this.$widget.find(".right-dropdown-button") | ||||
|             .addClass(this.iconClass) | ||||
|  | ||||
| @ -10,12 +10,14 @@ import CommandButtonWidget from "../buttons/command_button.js"; | ||||
| import utils from "../../services/utils.js"; | ||||
| import TodayLauncher from "../buttons/launcher/today_launcher.js"; | ||||
| import HistoryNavigationButton from "../buttons/history_navigation.js"; | ||||
| import QuickSearchLauncherWidget from "../quick_search_launcher.js"; | ||||
| 
 | ||||
| export default class LauncherWidget extends BasicWidget { | ||||
|     constructor() { | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.innerWidget = null; | ||||
|         this.isHorizontalLayout = isHorizontalLayout; | ||||
|     } | ||||
| 
 | ||||
|     isEnabled() { | ||||
| @ -63,6 +65,9 @@ export default class LauncherWidget extends BasicWidget { | ||||
|         } | ||||
| 
 | ||||
|         this.child(this.innerWidget); | ||||
|         if (this.isHorizontalLayout && this.innerWidget.settings) { | ||||
|             this.innerWidget.settings.titlePlacement = "bottom"; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| @ -86,29 +91,31 @@ export default class LauncherWidget extends BasicWidget { | ||||
| 
 | ||||
|     initBuiltinWidget(note) { | ||||
|         const builtinWidget = note.getLabelValue("builtinWidget"); | ||||
| 
 | ||||
|         if (builtinWidget === 'calendar') { | ||||
|             return new CalendarWidget(note.title, note.getIcon()); | ||||
|         } else if (builtinWidget === 'spacer') { | ||||
|             // || has to be inside since 0 is a valid value
 | ||||
|             const baseSize = parseInt(note.getLabelValue("baseSize") || "40"); | ||||
|             const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100"); | ||||
| 
 | ||||
|             return new SpacerWidget(baseSize, growthFactor); | ||||
|         } else if (builtinWidget === 'bookmarks') { | ||||
|             return new BookmarkButtons(); | ||||
|         } else if (builtinWidget === 'protectedSession') { | ||||
|             return new ProtectedSessionStatusWidget(); | ||||
|         } else if (builtinWidget === 'syncStatus') { | ||||
|             return new SyncStatusWidget(); | ||||
|         } else if (builtinWidget === 'backInHistoryButton') { | ||||
|             return new HistoryNavigationButton(note, "backInNoteHistory"); | ||||
|         } else if (builtinWidget === 'forwardInHistoryButton') { | ||||
|             return new HistoryNavigationButton(note, "forwardInNoteHistory"); | ||||
|         } else if (builtinWidget === 'todayInJournal') { | ||||
|             return new TodayLauncher(note); | ||||
|         } else { | ||||
|             throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); | ||||
|         switch (builtinWidget) { | ||||
|             case "calendar": | ||||
|                 return new CalendarWidget(note.title, note.getIcon()); | ||||
|             case "spacer": | ||||
|                 // || has to be inside since 0 is a valid value
 | ||||
|                 const baseSize = parseInt(note.getLabelValue("baseSize") || "40"); | ||||
|                 const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100"); | ||||
|          | ||||
|                 return new SpacerWidget(baseSize, growthFactor); | ||||
|             case "bookmarks": | ||||
|                 return new BookmarkButtons(this.isHorizontalLayout); | ||||
|             case "protectedSession": | ||||
|                 return new ProtectedSessionStatusWidget(); | ||||
|             case "syncStatus": | ||||
|                 return new SyncStatusWidget(); | ||||
|             case "backInHistoryButton": | ||||
|                 return new HistoryNavigationButton(note, "backInNoteHistory"); | ||||
|             case "forwardInHistoryButton": | ||||
|                 return new HistoryNavigationButton(note, "forwardInNoteHistory"); | ||||
|             case "todayInJournal": | ||||
|                 return new TodayLauncher(note); | ||||
|             case "quickSearch": | ||||
|                 return new QuickSearchLauncherWidget(this.isHorizontalLayout); | ||||
|             default: | ||||
|                 throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,12 +4,13 @@ import appContext from "../../components/app_context.js"; | ||||
| import LauncherWidget from "./launcher.js"; | ||||
| 
 | ||||
| export default class LauncherContainer extends FlexContainer { | ||||
|     constructor() { | ||||
|         super('column'); | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(isHorizontalLayout ? "row" : "column"); | ||||
| 
 | ||||
|         this.id('launcher-container'); | ||||
|         this.css('height', '100%'); | ||||
|         this.css(isHorizontalLayout ? "width" : 'height', '100%'); | ||||
|         this.filling(); | ||||
|         this.isHorizontalLayout = isHorizontalLayout; | ||||
| 
 | ||||
|         this.load(); | ||||
|     } | ||||
| @ -29,7 +30,7 @@ export default class LauncherContainer extends FlexContainer { | ||||
| 
 | ||||
|         for (const launcherNote of await visibleLaunchersRoot.getChildNotes()) { | ||||
|             try { | ||||
|                 const launcherWidget = new LauncherWidget(); | ||||
|                 const launcherWidget = new LauncherWidget(this.isHorizontalLayout); | ||||
|                 const success = await launcherWidget.initLauncher(launcherNote); | ||||
| 
 | ||||
|                 if (success) { | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| import FlexContainer from "./flex_container.js"; | ||||
| 
 | ||||
| export default class RootContainer extends FlexContainer { | ||||
|     constructor() { | ||||
|         super('row'); | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(isHorizontalLayout ? "column" : "row"); | ||||
| 
 | ||||
|         this.id('root-widget'); | ||||
|         this.css('height', '100%'); | ||||
|  | ||||
| @ -3,6 +3,7 @@ import libraryLoader from "../services/library_loader.js"; | ||||
| import NoteContextAwareWidget from "./note_context_aware_widget.js"; | ||||
| import server from "../services/server.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import { loadElkIfNeeded } from "../services/mermaid.js"; | ||||
| 
 | ||||
| const TPL = `<div class="mermaid-widget">
 | ||||
|     <style> | ||||
| @ -57,10 +58,10 @@ export default class MermaidWidget extends NoteContextAwareWidget { | ||||
|         this.$errorContainer.hide(); | ||||
| 
 | ||||
|         await libraryLoader.requireLibrary(libraryLoader.MERMAID); | ||||
| 
 | ||||
|          | ||||
|         const documentStyle = window.getComputedStyle(document.documentElement); | ||||
|         const mermaidTheme = documentStyle.getPropertyValue('--mermaid-theme'); | ||||
| 
 | ||||
|         const mermaidTheme = documentStyle.getPropertyValue('--mermaid-theme');         | ||||
|          | ||||
|         mermaid.mermaidAPI.initialize({ | ||||
|             startOnLoad: false, | ||||
|             theme: mermaidTheme.trim(), | ||||
| @ -111,6 +112,7 @@ export default class MermaidWidget extends NoteContextAwareWidget { | ||||
|                 zoomOnClick: false | ||||
|             }); | ||||
|         } catch (e) { | ||||
|             console.warn(e); | ||||
|             this.$errorMessage.text(e.message); | ||||
|             this.$errorContainer.show(); | ||||
|         } | ||||
| @ -122,6 +124,7 @@ export default class MermaidWidget extends NoteContextAwareWidget { | ||||
|         const blob = await this.note.getBlob(); | ||||
|         const content = blob.content || ""; | ||||
| 
 | ||||
|         await loadElkIfNeeded(content); | ||||
|         const {svg} = await mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content); | ||||
|         return svg; | ||||
|     } | ||||
|  | ||||
| @ -6,12 +6,20 @@ import branchService from "../../services/branches.js"; | ||||
| import treeService from "../../services/tree.js"; | ||||
| import { t } from "../../services/i18n.js"; | ||||
| 
 | ||||
| const TPL = `<button type="button" class="action-button bx bx-menu" style="padding-top: 10px;"></button>`; | ||||
| const TPL = `<button type="button" class="action-button bx" style="padding-top: 10px;"></button>`; | ||||
| 
 | ||||
| class MobileDetailMenuWidget extends BasicWidget { | ||||
| 
 | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(); | ||||
|         this.isHorizontalLayout = isHorizontalLayout; | ||||
|     } | ||||
| 
 | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
| 
 | ||||
|         this.$widget.addClass(this.isHorizontalLayout ? "bx-dots-vertical-rounded" : "bx-menu"); | ||||
| 
 | ||||
|         this.$widget.on("click", async e => { | ||||
|             const note = appContext.tabManager.getActiveContextNote(); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										33
									
								
								src/public/app/widgets/quick_search_launcher.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/public/app/widgets/quick_search_launcher.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| import utils from "../services/utils.js"; | ||||
| import QuickSearchWidget from "./quick_search.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Similar to the {@link QuickSearchWidget} but meant to be included inside the launcher bar. | ||||
|  *  | ||||
|  * <p> | ||||
|  * Adds specific tweaks such as: | ||||
|  *  | ||||
|  * - Hiding the widget on mobile. | ||||
|  */ | ||||
| export default class QuickSearchLauncherWidget extends QuickSearchWidget { | ||||
| 
 | ||||
|     constructor(isHorizontalLayout) { | ||||
|         super(); | ||||
|         this.isHorizontalLayout = isHorizontalLayout; | ||||
|     } | ||||
| 
 | ||||
|     isEnabled() { | ||||
|         if (!this.isHorizontalLayout) { | ||||
|             // The quick search widget is added somewhere else on the vertical layout.
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (utils.isMobile()) { | ||||
|             // The widget takes too much spaces to be included in the mobile layout.
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return super.isEnabled(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -225,6 +225,9 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { | ||||
|             else if (definition.labelType === 'datetime') { | ||||
|                 $input.prop('type', 'datetime-local') | ||||
|             } | ||||
|             else if (definition.labelType === 'time') { | ||||
|                 $input.prop('type', 'time') | ||||
|             } | ||||
|             else if (definition.labelType === 'url') { | ||||
|                 $input.prop("placeholder", t("promoted_attributes.url_placeholder")); | ||||
| 
 | ||||
|  | ||||
| @ -55,6 +55,10 @@ const TAB_ROW_TPL = ` | ||||
|         background: var(--main-background-color); | ||||
|         overflow: hidden; | ||||
|     } | ||||
| 
 | ||||
|     .tab-row-widget.full-width { | ||||
|         background: var(--launcher-pane-background-color); | ||||
|     } | ||||
|      | ||||
|     .tab-row-widget * { | ||||
|         box-sizing: inherit; | ||||
|  | ||||
| @ -1,45 +1,53 @@ | ||||
| import NoteContextAwareWidget from "../../note_context_aware_widget.js"; | ||||
| import server from "../../../services/server.js"; | ||||
| import { t } from "../../../services/i18n.js"; | ||||
| import AbstractCodeTypeWidget from "../abstract_code_type_widget.js"; | ||||
| 
 | ||||
| const TPL = `<div style="height: 100%; display: flex; flex-direction: column;">
 | ||||
|     <style> | ||||
|         .backend-log-textarea { | ||||
|         .backend-log-editor { | ||||
|             flex-grow: 1;  | ||||
|             width: 100%; | ||||
|             border: none; | ||||
|             resize: none; | ||||
|         }    | ||||
|     </style> | ||||
| 
 | ||||
|     <textarea class="backend-log-textarea" readonly="readonly"></textarea> | ||||
|     <pre class="backend-log-editor"></pre> | ||||
|      | ||||
|     <div style="display: flex; justify-content: space-around; margin-top: 10px;"> | ||||
|         <button class="refresh-backend-log-button btn btn-primary">${t("backend_log.refresh")}</button> | ||||
|     </div> | ||||
| </div>`; | ||||
| 
 | ||||
| export default class BackendLogWidget extends NoteContextAwareWidget { | ||||
| export default class BackendLogWidget extends AbstractCodeTypeWidget { | ||||
|     doRender() { | ||||
|         super.doRender(); | ||||
|         this.$widget = $(TPL); | ||||
|         this.$backendLogTextArea = this.$widget.find(".backend-log-textarea"); | ||||
|         this.$editor = this.$widget.find(".backend-log-editor"); | ||||
| 
 | ||||
|         this.$refreshBackendLog = this.$widget.find(".refresh-backend-log-button"); | ||||
| 
 | ||||
|         this.$refreshBackendLog.on('click', () => this.load()); | ||||
|     } | ||||
| 
 | ||||
|     scrollToBottom() { | ||||
|         this.$backendLogTextArea.scrollTop(this.$backendLogTextArea[0].scrollHeight); | ||||
|     } | ||||
| 
 | ||||
|     async refresh() { | ||||
|         await this.load(); | ||||
|     } | ||||
| 
 | ||||
|     getExtraOpts() { | ||||
|         return { | ||||
|             lineWrapping: false, | ||||
|             readOnly: true | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     async load() { | ||||
|         const backendLog = await server.get('backend-log'); | ||||
|         const content = await server.get('backend-log'); | ||||
|         await this.initialized; | ||||
| 
 | ||||
|         this.$backendLogTextArea.text(backendLog); | ||||
| 
 | ||||
|         this.scrollToBottom(); | ||||
|         this._update({ | ||||
|             mime: "text/plain"             | ||||
|         }, content); | ||||
|         this.show(); | ||||
|         this.scrollToEnd(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,26 @@ import { t } from "../../../../services/i18n.js"; | ||||
| 
 | ||||
| const TPL = ` | ||||
| <div class="options-section"> | ||||
|     <h4>${t("theme.layout")}</h4> | ||||
| 
 | ||||
|     <div class="form-group row"> | ||||
|         <div> | ||||
|             <label> | ||||
|                 <input type="radio" name="layout-orientation" value="vertical" /> | ||||
|                 <strong>${t("theme.layout-vertical-title")}</strong> | ||||
|                 - ${t("theme.layout-vertical-description")} | ||||
|             </label> | ||||
|         </div> | ||||
| 
 | ||||
|         <div> | ||||
|             <label> | ||||
|                 <input type="radio" name="layout-orientation" value="horizontal" /> | ||||
|                 <strong>${t("theme.layout-horizontal-title")}</strong> | ||||
|                 - ${t("theme.layout-horizontal-description")} | ||||
|             </label> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <h4>${t("theme.title")}</h4> | ||||
|      | ||||
|     <div class="form-group row"> | ||||
| @ -19,7 +39,7 @@ const TPL = ` | ||||
|                 ${t("theme.override_theme_fonts_label")} | ||||
|             </label> | ||||
|         </div> | ||||
|     </div> | ||||
|     </div>     | ||||
| </div>`; | ||||
| 
 | ||||
| export default class ThemeOptions extends OptionsWidget { | ||||
| @ -27,6 +47,11 @@ export default class ThemeOptions extends OptionsWidget { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$themeSelect = this.$widget.find(".theme-select"); | ||||
|         this.$overrideThemeFonts = this.$widget.find(".override-theme-fonts"); | ||||
|         this.$layoutOrientation = this.$widget.find(`input[name="layout-orientation"]`).on("change", async () => { | ||||
|             const newLayoutOrientation = this.$widget.find(`input[name="layout-orientation"]:checked`).val(); | ||||
|             await this.updateOption("layoutOrientation", newLayoutOrientation); | ||||
|             utils.reloadFrontendApp("layout orientation change"); | ||||
|         }); | ||||
| 
 | ||||
|         this.$themeSelect.on('change', async () => { | ||||
|             const newTheme = this.$themeSelect.val(); | ||||
| @ -57,5 +82,8 @@ export default class ThemeOptions extends OptionsWidget { | ||||
|         this.$themeSelect.val(options.theme); | ||||
| 
 | ||||
|         this.setCheckboxState(this.$overrideThemeFonts, options.overrideThemeFonts); | ||||
| 
 | ||||
|         this.$widget.find(`input[name="layout-orientation"][value="${options.layoutOrientation}"]`) | ||||
|             .prop("checked", "true"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -40,6 +40,10 @@ body { | ||||
|     font-size: var(--main-font-size); | ||||
| } | ||||
| 
 | ||||
| body.mobile .desktop-only { | ||||
|     display: none !important; | ||||
| } | ||||
| 
 | ||||
| a { | ||||
|     text-decoration: none; | ||||
| } | ||||
| @ -1022,6 +1026,18 @@ li.dropdown-submenu:hover > ul.dropdown-menu { | ||||
|     overflow: auto; | ||||
| } | ||||
| 
 | ||||
| body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { | ||||
|     left: calc(-100% + 10px); | ||||
| } | ||||
| 
 | ||||
| #launcher-pane.horizontal .right-dropdown-widget { | ||||
|     width: 53px; | ||||
| } | ||||
| 
 | ||||
| #launcher-pane.vertical .right-dropdown-widget { | ||||
|     height: 53px; | ||||
| } | ||||
| 
 | ||||
| /* rotate caret on hover */ | ||||
| .dropdown-menu > li > a:hover:after { | ||||
|     text-decoration: underline; | ||||
| @ -1123,9 +1139,21 @@ li.dropdown-submenu:hover > ul.dropdown-menu { | ||||
|     cursor: pointer; | ||||
|     border: none; | ||||
|     color: var(--launcher-pane-text-color); | ||||
|     background-color: var(--launcher-pane-background-color); | ||||
|     height: 53px; | ||||
|     background-color: var(--launcher-pane-background-color);     | ||||
| } | ||||
| 
 | ||||
| #launcher-pane.vertical .launcher-button { | ||||
|     width: 100%; | ||||
|     height: 53px; | ||||
| } | ||||
| 
 | ||||
| #launcher-pane.horizontal .launcher-button { | ||||
|     width: 53px; | ||||
|     height: 100%; | ||||
| } | ||||
| 
 | ||||
| #launcher-pane.horizontal .quick-search { | ||||
|     width: 350px; | ||||
| } | ||||
| 
 | ||||
| #launcher-pane .icon-action:hover { | ||||
|  | ||||
| @ -15,7 +15,13 @@ | ||||
|       "message": "发生了严重错误,导致客户端应用程序无法启动:\n\n{{message}}\n\n这很可能是由于脚本以意外的方式失败引起的。请尝试以安全模式启动应用程序并解决问题。" | ||||
|     }, | ||||
|     "widget-error": { | ||||
|       "title": "小部件初始化失败" | ||||
|       "title": "小部件初始化失败", | ||||
|       "message-custom": "来自 ID 为 \"{{id}}\"、标题为 \"{{title}}\" 的笔记的自定义小部件因以下原因无法初始化:\n\n{{message}}", | ||||
|       "message-unknown": "未知小部件因以下原因无法初始化:\n\n{{message}}" | ||||
|     }, | ||||
|     "bundle-error": { | ||||
|       "title": "加载自定义脚本失败", | ||||
|       "message": "来自 ID 为 \"{{id}}\"、标题为 \"{{title}}\" 的笔记的脚本因以下原因无法执行:\n\n{{message}}" | ||||
|     } | ||||
|   }, | ||||
|   "add_link": { | ||||
| @ -45,7 +51,11 @@ | ||||
|     "chosen_actions": "选择的操作", | ||||
|     "execute_bulk_actions": "执行批量操作", | ||||
|     "bulk_actions_executed": "批量操作已成功执行。", | ||||
|     "none_yet": "暂无操作 ... 通过点击上方的可用操作添加一个操作。" | ||||
|     "none_yet": "暂无操作 ... 通过点击上方的可用操作添加一个操作。", | ||||
|     "labels": "标签", | ||||
|     "relations": "关联关系", | ||||
|     "notes": "笔记", | ||||
|     "other": "其它" | ||||
|   }, | ||||
|   "clone_to": { | ||||
|     "clone_notes_to": "克隆笔记到...", | ||||
| @ -164,7 +174,8 @@ | ||||
|     "textImportedAsText": "如果元数据不明确,将HTML、Markdown和TXT导入为文本笔记", | ||||
|     "codeImportedAsCode": "如果元数据不明确,将识别的代码文件(例如<code>.json</code>)导入为代码笔记", | ||||
|     "replaceUnderscoresWithSpaces": "在导入的笔记名称中将下划线替换为空格", | ||||
|     "import": "导入" | ||||
|     "import": "导入", | ||||
|     "failed": "导入失败: {{message}}." | ||||
|   }, | ||||
|   "include_note": { | ||||
|     "dialog_title": "包含笔记", | ||||
| @ -304,6 +315,7 @@ | ||||
|     "boolean": "布尔值", | ||||
|     "date": "日期", | ||||
|     "date_time": "日期和时间", | ||||
|     "time": "时间", | ||||
|     "url": "网址", | ||||
|     "precision_title": "值设置界面中浮点数后的位数。", | ||||
|     "precision": "精度", | ||||
| @ -630,7 +642,10 @@ | ||||
|     "export_note": "导出笔记", | ||||
|     "delete_note": "删除笔记", | ||||
|     "print_note": "打印笔记", | ||||
|     "save_revision": "保存笔记历史" | ||||
|     "save_revision": "保存笔记历史", | ||||
|     "convert_into_attachment_failed": "笔记 '{{title}}' 转换失败。", | ||||
|     "convert_into_attachment_successful": "笔记 '{{title}}' 已成功转换为附件。", | ||||
|     "convert_into_attachment_prompt": "确定要将笔记 '{{title}}' 转换为父笔记的附件吗?" | ||||
|   }, | ||||
|   "onclick_button": { | ||||
|     "no_click_handler": "按钮组件'{{componentId}}'没有定义点击处理程序" | ||||
| @ -885,7 +900,8 @@ | ||||
|     "label_rock_or_pop": "只需一个标签存在即可", | ||||
|     "label_year_comparison": "数字比较(也包括>,>=,<)。", | ||||
|     "label_date_created": "上个月创建的笔记", | ||||
|     "error": "搜索错误:{{error}}" | ||||
|     "error": "搜索错误:{{error}}", | ||||
|     "search_prefix": "搜索:" | ||||
|   }, | ||||
|   "attachment_detail": { | ||||
|     "open_help_page": "打开附件帮助页面", | ||||
| @ -919,7 +935,15 @@ | ||||
|   }, | ||||
|   "protected_session": { | ||||
|     "enter_password_instruction": "显示受保护的笔记需要输入您的密码:", | ||||
|     "start_session_button": "开始受保护的会话" | ||||
|     "start_session_button": "开始受保护的会话", | ||||
|     "started": "受保护的会话已启动。", | ||||
|     "wrong_password": "密码错误。", | ||||
|     "protecting-finished-successfully": "保护操作已成功完成。", | ||||
|     "unprotecting-finished-successfully": "解除保护操作已成功完成。", | ||||
|     "protecting-in-progress": "保护进行中:{{count}}", | ||||
|     "unprotecting-in-progress-count": "解除保护进行中:{{count}}", | ||||
|     "protecting-title": "保护状态", | ||||
|     "unprotecting-title": "解除保护状态" | ||||
|   }, | ||||
|   "relation_map": { | ||||
|     "open_in_new_tab": "在新标签页中打开", | ||||
| @ -990,7 +1014,9 @@ | ||||
|     "fill_entity_changes_button": "填充实体变更记录", | ||||
|     "full_sync_triggered": "全量同步已触发", | ||||
|     "filling_entity_changes": "正在填充实体变更行...", | ||||
|     "sync_rows_filled_successfully": "同步行填充成功" | ||||
|     "sync_rows_filled_successfully": "同步行填充成功", | ||||
|     "finished-successfully": "同步已完成。", | ||||
|     "failed": "同步失败:{{message}}" | ||||
|   }, | ||||
|   "vacuum_database": { | ||||
|     "title": "数据库清理", | ||||
| @ -1036,8 +1062,13 @@ | ||||
|     "theme_label": "主题", | ||||
|     "override_theme_fonts_label": "覆盖主题字体", | ||||
|     "light_theme": "浅色", | ||||
|     "dark_theme": "深色" | ||||
|   }, | ||||
|     "dark_theme": "深色", | ||||
|     "layout": "布局", | ||||
|     "layout-vertical-title": "垂直", | ||||
|     "layout-horizontal-title": "水平", | ||||
|     "layout-vertical-description": "启动栏位于左侧(默认)", | ||||
|     "layout-horizontal-description": "启动栏位于标签栏下方,标签栏现在是全宽的。" | ||||
|  }, | ||||
|   "zoom_factor": { | ||||
|     "title": "缩放系数(仅桌面客户端有效)", | ||||
|     "description": "缩放也可以通过 CTRL+- 和 CTRL+= 快捷键进行控制。" | ||||
| @ -1162,6 +1193,8 @@ | ||||
|     "backup_now": "立即备份", | ||||
|     "backup_database_now": "立即备份数据库", | ||||
|     "existing_backups": "现有备份", | ||||
|     "date-and-time": "日期和时间", | ||||
|     "path": "路径", | ||||
|     "database_backed_up_to": "数据库已备份到", | ||||
|     "no_backup_yet": "尚无备份" | ||||
|   }, | ||||
| @ -1309,7 +1342,9 @@ | ||||
|     "duplicate-subtree": "复制子树", | ||||
|     "export": "导出", | ||||
|     "import-into-note": "导入到笔记", | ||||
|     "apply-bulk-actions": "应用批量操作" | ||||
|     "apply-bulk-actions": "应用批量操作", | ||||
|     "converted-to-attachments": "{{count}} 个笔记已被转换为附件。", | ||||
|     "convert-to-attachment-confirm": "确定要将选中的笔记转换为其父笔记的附件吗?" | ||||
|   }, | ||||
|   "shared_info": { | ||||
|     "shared_publicly": "此笔记已公开分享在", | ||||
| @ -1332,7 +1367,8 @@ | ||||
|     "image": "图片", | ||||
|     "launcher": "启动器", | ||||
|     "doc": "文档", | ||||
|     "widget": "小部件" | ||||
|     "widget": "小部件", | ||||
|     "confirm-change": "当笔记内容不为空时,不建议更改笔记类型。您仍然要继续吗?" | ||||
|   }, | ||||
|   "protect_note": { | ||||
|     "toggle-on": "保护笔记", | ||||
| @ -1355,7 +1391,11 @@ | ||||
|   "open-help-page": "打开帮助页面", | ||||
|   "find": { | ||||
|     "case_sensitive": "区分大小写", | ||||
|     "match_words": "匹配单词" | ||||
|     "match_words": "匹配单词", | ||||
|     "find_placeholder": "在文本中查找...", | ||||
|     "replace_placeholder": "替换为...", | ||||
|     "replace": "替换", | ||||
|     "replace_all": "全部替换" | ||||
|   }, | ||||
|   "highlights_list_2": { | ||||
|     "title": "高亮列表", | ||||
| @ -1378,7 +1418,9 @@ | ||||
|     "hide-archived-notes": "隐藏已归档笔记", | ||||
|     "automatically-collapse-notes": "自动折叠笔记", | ||||
|     "automatically-collapse-notes-title": "笔记在一段时间内未使用将被折叠,以减少树形结构的杂乱。", | ||||
|     "save-changes": "保存并应用更改" | ||||
|     "save-changes": "保存并应用更改", | ||||
|     "auto-collapsing-notes-after-inactivity": "在不活动后自动折叠笔记...", | ||||
|     "saved-search-note-refreshed": "已保存的搜索笔记已刷新。" | ||||
|   }, | ||||
|   "title_bar_buttons": { | ||||
|     "window-on-top": "保持此窗口置顶" | ||||
| @ -1407,8 +1449,11 @@ | ||||
|     "add_new_tab": "添加新标签页", | ||||
|     "close": "关闭", | ||||
|     "close_other_tabs": "关闭其他标签页", | ||||
|     "close_right_tabs": "关闭右侧标签页", | ||||
|     "close_all_tabs": "关闭所有标签页", | ||||
|     "reopen_last_tab": "重新打开最后一个关闭的标签页", | ||||
|     "move_tab_to_new_window": "将此标签页移动到新窗口", | ||||
|     "copy_tab_to_new_window": "将此标签页复制到新窗口", | ||||
|     "new_tab": "新标签页" | ||||
|   }, | ||||
|   "toc": { | ||||
| @ -1422,5 +1467,101 @@ | ||||
|   }, | ||||
|   "app_context": { | ||||
|     "please_wait_for_save": "请等待几秒钟以完成保存,然后您可以尝试再操作一次。" | ||||
|   }, | ||||
|   "note_create": { | ||||
|     "duplicated": "笔记 \"{{title}}\" 已被复制。" | ||||
|   }, | ||||
|   "image": { | ||||
|     "copied-to-clipboard": "图片的引用已复制到剪贴板,可以粘贴到任何文本笔记中。", | ||||
|     "cannot-copy": "无法将图片引用复制到剪贴板。" | ||||
|   }, | ||||
|   "clipboard": { | ||||
|     "cut": "笔记已剪切到剪贴板。", | ||||
|     "copied": "笔记已复制到剪贴板。" | ||||
|   }, | ||||
|   "entrypoints": { | ||||
|     "note-revision-created": "笔记修订已创建。", | ||||
|     "note-executed": "笔记已执行。", | ||||
|     "sql-error": "执行 SQL 查询时发生错误:{{message}}" | ||||
|   }, | ||||
|   "branches": { | ||||
|     "cannot-move-notes-here": "无法将笔记移动到这里。", | ||||
|     "delete-status": "删除状态", | ||||
|     "delete-notes-in-progress": "正在删除笔记:{{count}}", | ||||
|     "delete-finished-successfully": "删除成功完成。", | ||||
|     "undeleting-notes-in-progress": "正在恢复删除的笔记:{{count}}", | ||||
|     "undeleting-notes-finished-successfully": "恢复删除的笔记已成功完成。" | ||||
|   }, | ||||
|   "frontend_script_api": { | ||||
|     "async_warning": "您正在将一个异步函数传递给 `api.runOnBackend()`,这可能无法按预期工作。\\n要么使该函数同步(通过移除 `async` 关键字),要么使用 `api.runAsyncOnBackendWithManualTransactionHandling()`。", | ||||
|     "sync_warning": "您正在将一个同步函数传递给 `api.runAsyncOnBackendWithManualTransactionHandling()`,\\n而您可能应该使用 `api.runOnBackend()`。" | ||||
|   }, | ||||
|   "ws": { | ||||
|     "sync-check-failed": "同步检查失败!", | ||||
|     "consistency-checks-failed": "一致性检查失败!请查看日志了解详细信息。", | ||||
|     "encountered-error": "遇到错误 \"{{message}}\",请查看控制台。" | ||||
|   }, | ||||
|   "hoisted_note": { | ||||
|     "confirm_unhoisting": "请求的笔记 '{{requestedNote}}' 位于提升的笔记 '{{hoistedNote}}' 的子树之外,您必须取消提升才能访问该笔记。是否继续取消提升?" | ||||
|   }, | ||||
|   "launcher_context_menu": { | ||||
|     "reset_launcher_confirm": "您确定要重置 \"{{title}}\" 吗?此笔记(及其子项)中的所有数据/设置将丢失,且启动器将恢复到其原始位置。", | ||||
|     "add-note-launcher": "添加笔记启动器", | ||||
|     "add-script-launcher": "添加脚本启动器", | ||||
|     "add-custom-widget": "添加自定义小部件", | ||||
|     "add-spacer": "添加间隔", | ||||
|     "delete": "删除", | ||||
|     "reset": "重置", | ||||
|     "move-to-visible-launchers": "移动到可见启动器", | ||||
|     "move-to-available-launchers": "移动到可用启动器", | ||||
|     "duplicate-launcher": "复制启动器" | ||||
|   }, | ||||
|   "editable-text": { | ||||
|     "auto-detect-language": "自动检测" | ||||
|   }, | ||||
|   "highlighting": { | ||||
|     "title": "文本笔记的代码语法高亮", | ||||
|     "description": "控制文本笔记中代码块的语法高亮,代码笔记不会受到影响。", | ||||
|     "color-scheme": "颜色方案" | ||||
|   }, | ||||
|   "code_block": { | ||||
|     "word_wrapping": "自动换行" | ||||
|   }, | ||||
|   "classic_editor_toolbar": { | ||||
|     "title": "格式化" | ||||
|   }, | ||||
|   "editor": { | ||||
|     "title": "编辑器" | ||||
|   }, | ||||
|   "editing": { | ||||
|     "editor_type": { | ||||
|       "label": "格式化工具栏", | ||||
|       "floating": { | ||||
|         "title": "浮动", | ||||
|         "description": "编辑工具出现在光标附近;" | ||||
|       }, | ||||
|       "fixed": { | ||||
|         "title": "固定", | ||||
|         "description": "编辑工具出现在 \"格式化\" 功能区标签中。" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "electron_context_menu": { | ||||
|     "add-term-to-dictionary": "将 \"{{term}}\" 添加到字典", | ||||
|     "cut": "剪切", | ||||
|     "copy": "复制", | ||||
|     "copy-link": "复制链接", | ||||
|     "paste": "粘贴", | ||||
|     "paste-as-plain-text": "以纯文本粘贴", | ||||
|     "search_online": "用 {{searchEngine}} 搜索 \"{{term}}\"" | ||||
|   }, | ||||
|   "image_context_menu": { | ||||
|     "copy_reference_to_clipboard": "复制引用到剪贴板", | ||||
|     "copy_image_to_clipboard": "复制图片到剪贴板" | ||||
|   }, | ||||
|   "link_context_menu": { | ||||
|     "open_note_in_new_tab": "在新标签页中打开笔记", | ||||
|     "open_note_in_new_split": "在新分屏中打开笔记", | ||||
|     "open_note_in_new_window": "在新窗口中打开笔记" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -310,6 +310,7 @@ | ||||
|     "boolean": "Boolescher Wert", | ||||
|     "date": "Datum", | ||||
|     "date_time": "Datum und Uhrzeit", | ||||
|     "time": "Uhrzeit", | ||||
|     "url": "URL", | ||||
|     "precision_title": "Wie viele Nachkommastellen im Wert-Einstellungs-Interface verfügbar sein sollen.", | ||||
|     "precision": "Präzision", | ||||
|  | ||||
| @ -315,6 +315,7 @@ | ||||
|     "boolean": "Boolean", | ||||
|     "date": "Date", | ||||
|     "date_time": "Date & Time", | ||||
|     "time": "Time", | ||||
|     "url": "URL", | ||||
|     "precision_title": "What number of digits after floating point should be available in the value setting interface.", | ||||
|     "precision": "Precision", | ||||
| @ -1061,7 +1062,12 @@ | ||||
|     "theme_label": "Theme", | ||||
|     "override_theme_fonts_label": "Override theme fonts", | ||||
|     "light_theme": "Light", | ||||
|     "dark_theme": "Dark" | ||||
|     "dark_theme": "Dark", | ||||
|     "layout": "Layout", | ||||
|     "layout-vertical-title": "Vertical", | ||||
|     "layout-horizontal-title": "Horizontal", | ||||
|     "layout-vertical-description": "launcher bar is on the left (default)", | ||||
|     "layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width." | ||||
|   }, | ||||
|   "zoom_factor": { | ||||
|     "title": "Zoom Factor (desktop build only)", | ||||
| @ -1548,5 +1554,14 @@ | ||||
|     "paste": "Paste", | ||||
|     "paste-as-plain-text": "Paste as plain text", | ||||
|     "search_online": "Search for \"{{term}}\" with {{searchEngine}}" | ||||
|   }, | ||||
|   "image_context_menu": { | ||||
|     "copy_reference_to_clipboard": "Copy reference to clipboard", | ||||
|     "copy_image_to_clipboard": "Copy image to clipboard" | ||||
|   }, | ||||
|   "link_context_menu": { | ||||
|     "open_note_in_new_tab": "Open note in a new tab", | ||||
|     "open_note_in_new_split": "Open note in a new split", | ||||
|     "open_note_in_new_window": "Open note in a new window" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -51,7 +51,11 @@ | ||||
|     "chosen_actions": "Acciones elegidas", | ||||
|     "execute_bulk_actions": "Ejecutar acciones en bloque", | ||||
|     "bulk_actions_executed": "Las acciones en bloque se han ejecutado con éxito.", | ||||
|     "none_yet": "Ninguna todavía... agrega una acción haciendo clic en una de las disponibles arriba." | ||||
|     "none_yet": "Ninguna todavía... agregue una acción haciendo clic en una de las disponibles arriba.", | ||||
|     "labels": "Etiquetas", | ||||
|     "relations": "Relaciones", | ||||
|     "notes": "Notas", | ||||
|     "other": "Otro" | ||||
|   }, | ||||
|   "clone_to": { | ||||
|     "clone_notes_to": "Clonar notas a...", | ||||
| @ -311,6 +315,7 @@ | ||||
|     "boolean": "Booleano", | ||||
|     "date": "Fecha", | ||||
|     "date_time": "Fecha y hora", | ||||
|     "time": "Hora", | ||||
|     "url": "URL", | ||||
|     "precision_title": "Cantidad de dígitos después del punto flotante que deben estar disponibles en la interfaz de configuración del valor.", | ||||
|     "precision": "Precisión", | ||||
| @ -1057,7 +1062,12 @@ | ||||
|     "theme_label": "Tema", | ||||
|     "override_theme_fonts_label": "Sobreescribir fuentes de tema", | ||||
|     "light_theme": "Claro", | ||||
|     "dark_theme": "Oscuro" | ||||
|     "dark_theme": "Oscuro", | ||||
|     "layout": "Disposición", | ||||
|     "layout-vertical-title": "Vertical", | ||||
|     "layout-horizontal-title": "Horizontal", | ||||
|     "layout-vertical-description": "la barra del lanzador está en la izquierda (por defecto)", | ||||
|     "layout-horizontal-description": "la barra de lanzamiento está debajo de la barra de pestañas, la barra de pestañas ahora tiene ancho completo." | ||||
|   }, | ||||
|   "zoom_factor": { | ||||
|     "title": "Factor de zoom (solo versión de escritorio)", | ||||
| @ -1183,6 +1193,8 @@ | ||||
|     "backup_now": "Realizar copia de seguridad ahora", | ||||
|     "backup_database_now": "Realizar copia de seguridad de la base de datos ahora", | ||||
|     "existing_backups": "Copias de seguridad existentes", | ||||
|     "date-and-time": "Fecha y hora", | ||||
|     "path": "Ruta", | ||||
|     "database_backed_up_to": "Se ha realizado una copia de seguridad de la base de datos en", | ||||
|     "no_backup_yet": "no hay copia de seguridad todavía" | ||||
|   }, | ||||
| @ -1439,7 +1451,9 @@ | ||||
|     "close_other_tabs": "Cerrar otras pestañas", | ||||
|     "close_right_tabs": "Cerrar pestañas a la derecha", | ||||
|     "close_all_tabs": "Cerras todas las pestañas", | ||||
|     "reopen_last_tab": "Reabrir última pestaña cerrada", | ||||
|     "move_tab_to_new_window": "Mover esta pestaña a una nueva ventana", | ||||
|     "copy_tab_to_new_window": "Copiar esta pestaña a una ventana nueva", | ||||
|     "new_tab": "Nueva pestaña" | ||||
|   }, | ||||
|   "toc": { | ||||
| @ -1531,5 +1545,14 @@ | ||||
|         "description": "las herramientas de edición aparecen en la pestaña de la cinta \"Formato\")." | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "electron_context_menu": { | ||||
|     "add-term-to-dictionary": "Agregar \"{{term}}\" al diccionario.", | ||||
|     "cut": "Cortar", | ||||
|     "copy": "Copiar", | ||||
|     "copy-link": "Copiar enlace", | ||||
|     "paste": "Pegar", | ||||
|     "paste-as-plain-text": "Pegar como texto plano", | ||||
|     "search_online": "Buscar \"{{term}}\" con {{searchEngine}}" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -311,6 +311,7 @@ | ||||
|     "boolean": "Booléen", | ||||
|     "date": "Date", | ||||
|     "date_time": "Date et heure", | ||||
|     "time": "Heure", | ||||
|     "url": "URL", | ||||
|     "precision_title": "Nombre de chiffres après la virgule devant être disponible dans l'interface définissant la valeur.", | ||||
|     "precision": "Précision", | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/public/translations/pt_br/translation.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/public/translations/pt_br/translation.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {} | ||||
| @ -127,6 +127,7 @@ | ||||
|     "custom_resource_provider": "a se vedea <a href=\"javascript:\" data-help-page=\"custom-request-handler.html\">Custom request handler</a>", | ||||
|     "date": "Dată", | ||||
|     "date_time": "Dată și timp", | ||||
|     "time": "Timp", | ||||
|     "delete": "Șterge", | ||||
|     "digits": "număr de zecimale", | ||||
|     "disable_inclusion": "script-urile cu această etichetă nu vor fi incluse în execuția scriptului părinte.", | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/public/translations/tw/translation.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/public/translations/tw/translation.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {} | ||||
| @ -66,7 +66,8 @@ const ALLOWED_OPTIONS = new Set([ | ||||
|     'editedNotesOpenInRibbon', | ||||
|     'locale', | ||||
|     'firstDayOfWeek', | ||||
|     'textNoteEditorType' | ||||
|     'textNoteEditorType', | ||||
|     'layoutOrientation' | ||||
| ]); | ||||
| 
 | ||||
| function getOptions() { | ||||
|  | ||||
| @ -34,7 +34,7 @@ interface Item { | ||||
|     baseSize?: string; | ||||
|     growthFactor?: string; | ||||
|     targetNoteId?: "_backendLog" | "_globalNoteMap"; | ||||
|     builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar"; | ||||
|     builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar" | "quickSearch"; | ||||
|     command?: keyof typeof Command; | ||||
| } | ||||
| 
 | ||||
| @ -103,7 +103,8 @@ const HIDDEN_SUBTREE_DEFINITION: Item = { | ||||
|             type: 'contentWidget', | ||||
|             icon: 'bx-terminal', | ||||
|             attributes: [ | ||||
|                 { type: 'label', name: 'keepCurrentHoisting' } | ||||
|                 { type: 'label', name: 'keepCurrentHoisting' }, | ||||
|                 { type: 'label', name: 'fullContentWidth' } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
| @ -240,6 +241,7 @@ const HIDDEN_SUBTREE_DEFINITION: Item = { | ||||
|                         { id: '_lbBookmarks', title: 'Bookmarks', type: 'launcher', builtinWidget: 'bookmarks', icon: 'bx bx-bookmark' }, | ||||
|                         { id: '_lbToday', title: "Open Today's Journal Note", type: 'launcher', builtinWidget: 'todayInJournal', icon: 'bx bx-calendar-star' }, | ||||
|                         { id: '_lbSpacer2', title: 'Spacer', type: 'launcher', builtinWidget: 'spacer', baseSize: "0", growthFactor: "1" }, | ||||
|                         { id: '_lbQuickSearch', title: "Quick Search", type: "launcher", builtinWidget: "quickSearch", icon: "bx bx-rectangle" }, | ||||
|                         { id: '_lbProtectedSession', title: 'Protected Session', type: 'launcher', builtinWidget: 'protectedSession', icon: 'bx bx bx-shield-quarter' }, | ||||
|                         { id: '_lbSyncStatus', title: 'Sync Status', type: 'launcher', builtinWidget: 'syncStatus', icon: 'bx bx-wifi' }, | ||||
|                         { id: '_lbSettings', title: 'Settings', type: 'launcher', command: 'showOptions', icon: 'bx bx-cog' } | ||||
|  | ||||
| @ -134,7 +134,9 @@ const defaultOptions: DefaultOption[] = [ | ||||
|     { name: "codeBlockWordWrap", value: "false", isSynced: true }, | ||||
| 
 | ||||
|     // Text note configuration
 | ||||
|     { name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true } | ||||
|     { name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true }, | ||||
| 
 | ||||
|     { name: "layoutOrientation", value: "vertical", isSynced: false } | ||||
| ]; | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -8,7 +8,7 @@ function parse(value: string): DefinitionObject { | ||||
|         if (token === 'promoted') { | ||||
|             defObj.isPromoted = true; | ||||
|         } | ||||
|         else if (['text', 'number', 'boolean', 'date', 'datetime', 'url'].includes(token)) { | ||||
|         else if (['text', 'number', 'boolean', 'date', 'datetime', 'time', 'url'].includes(token)) { | ||||
|             defObj.labelType = token; | ||||
|         } | ||||
|         else if (['single', 'multi'].includes(token)) { | ||||
|  | ||||
| @ -89,7 +89,8 @@ | ||||
|         "copy-without-formatting": "不带格式复制选定文本", | ||||
|         "force-save-revision": "强制创建/保存当前笔记的历史版本", | ||||
|         "show-help": "显示内置帮助/备忘单", | ||||
|         "toggle-book-properties": "切换书籍属性" | ||||
|         "toggle-book-properties": "切换书籍属性", | ||||
|     	"toggle-classic-editor-toolbar": "切换具有固定工具栏的编辑器格式化选项卡" | ||||
|     }, | ||||
|     "login": { | ||||
|         "title": "登录", | ||||
| @ -157,5 +158,40 @@ | ||||
|         "clipped-from": "此笔记最初剪切自 {{- url}}", | ||||
|         "child-notes": "子笔记:", | ||||
|         "no-content": "此笔记没有内容。" | ||||
|     }, | ||||
|     "weekdays": { | ||||
|         "monday": "周一", | ||||
|         "tuesday": "周二", | ||||
|         "wednesday": "周三", | ||||
|         "thursday": "周四", | ||||
|         "friday": "周五", | ||||
|         "saturday": "周六", | ||||
|         "sunday": "周日" | ||||
|     }, | ||||
|     "months": { | ||||
|         "january": "一月", | ||||
|         "february": "二月", | ||||
|         "march": "三月", | ||||
|         "april": "四月", | ||||
|         "may": "五月", | ||||
|         "june": "六月", | ||||
|         "july": "七月", | ||||
|         "august": "八月", | ||||
|         "september": "九月", | ||||
|         "october": "十月", | ||||
|         "november": "十一月", | ||||
|         "december": "十二月" | ||||
|     }, | ||||
|     "special_notes": { | ||||
|        "search_prefix": "搜索:" | ||||
|     }, | ||||
|     "code_block": { | ||||
|         "theme_none": "无语法高亮", | ||||
|         "theme_group_light": "浅色主题", | ||||
|         "theme_group_dark": "深色主题" | ||||
|     }, | ||||
|     "test_sync": { | ||||
|         "not-configured": "同步服务器主机未配置。请先配置同步。", | ||||
|         "successful": "同步服务器握手成功,同步已开始。" | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SiriusXT
						SiriusXT