diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index baff2d14dc..0ffd81bf02 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -1680,7 +1680,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { display: inline !important; } - body.mobile .options-section table { + body.mobile .options-section-card table { word-break: break-all; } @@ -1860,12 +1860,12 @@ button.close:hover { margin-bottom: 15px; } -.options-section h5 { +.options-section-card h5 { margin-top: 10px; margin-bottom: 10px; } -.options-section input[type="number"] { +.options-section-card input[type="number"] { /* overriding settings from .form-control */ width: 10em !important; flex-grow: 0 !important; diff --git a/apps/client/src/stylesheets/theme-next/pages.css b/apps/client/src/stylesheets/theme-next/pages.css index 42a831bae7..e6b8c287df 100644 --- a/apps/client/src/stylesheets/theme-next/pages.css +++ b/apps/client/src/stylesheets/theme-next/pages.css @@ -186,12 +186,7 @@ body.experimental-feature-new-layout .note-detail-content-widget-content.options } .options-section:not(.tn-no-card) { - margin-bottom: calc(var(--options-title-offset) + 26px) !important; - box-shadow: var(--card-box-shadow); - border: 1px solid var(--card-border-color) !important; - border-radius: 8px; - background: var(--card-background-color); - padding: var(--options-card-padding); + margin-bottom: 26px !important; } body.desktop .options-section:not(.tn-no-card) { @@ -199,40 +194,70 @@ body.desktop .options-section:not(.tn-no-card) { max-width: var(--options-card-max-width); } +.options-section-card { + box-shadow: var(--card-box-shadow); + border: 1px solid var(--card-border-color); + border-radius: 8px; + background: var(--card-background-color); + padding: var(--options-card-padding); +} + .note-detail-content-widget-content.options { --default-padding: 15px; - padding-top: calc(var(--default-padding) + var(--options-title-offset) + var(--options-title-font-size)); + padding-top: var(--default-padding); padding-bottom: var(--default-padding); } -.options-section:not(.tn-no-card) h4, -.options-section:not(.tn-no-card) h5 { +.options-section:not(.tn-no-card) h4 { text-transform: uppercase; letter-spacing: .4pt; } - .options-section:not(.tn-no-card) h4 { font-size: var(--options-title-font-size); font-weight: 600; color: var(--launcher-pane-text-color); - margin-top: calc(-1 * var(--options-card-padding) - var(--options-title-font-size) - var(--options-title-offset)) !important; - margin-bottom: calc(var(--options-title-offset) + var(--options-card-padding)) !important; - margin-inline-start: calc(-1 * var(--options-card-padding)); + margin-top: 0; + margin-bottom: var(--options-title-offset); } -.options-section:not(.tn-no-card) h5 { +.options-section:not(.tn-no-card) .options-section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.options-section:not(.tn-no-card) .options-section-header h4 { + margin: 0; + margin-bottom: 0; +} + +.options-section:not(.tn-no-card) .options-section-header .icon-action { + margin-inline-start: auto; +} + +.options-section-description { + color: var(--muted-text-color); + font-size: 13px; + margin-top: 0; + margin-bottom: 16px; +} + +.options-section-card h5 { + text-transform: uppercase; + letter-spacing: .4pt; font-size: var(--options-title-font-size); font-weight: bold; margin-top: 1em !important; margin-bottom: unset !important; } -.options-section:not(.tn-no-card) h5:first-of-type { +.options-section-card h5:first-of-type { margin-top: unset !important; } -.options-section hr { +.options-section-card hr { --bs-border-width: 2px; margin-inline-start: calc(var(--options-card-padding) * -1); @@ -241,27 +266,26 @@ body.desktop .options-section:not(.tn-no-card) { color: var(--root-background); } -.options-section p:last-of-type:not(:first-of-type), -.options-section h4 + p:last-child, -.options-section .existing-anonymized-databases { +.options-section-card p:last-of-type:not(:first-of-type), +.options-section-card .existing-anonymized-databases { margin-bottom: 0; } -.options-section .form-group { +.options-section-card .form-group { margin-bottom: 1em; } -.options-section .form-group:last-child { +.options-section-card .form-group:last-child { margin-bottom: 0; } -.options-section ul { +.options-section-card ul { margin: 0; padding: 0; margin-bottom: 1em; } -.options-section label:not(.tn-checkbox):not(.tn-radio) { +.options-section-card label:not(.tn-checkbox):not(.tn-radio) { margin-bottom: 6px; } @@ -275,7 +299,7 @@ nav.options-section-tabs .nav-tabs { border-bottom: 0; } -nav.options-section-tabs + .options-section { +nav.options-section-tabs + .options-section .options-section-card { border-top-left-radius: 0; border-top-right-radius: 0; } @@ -329,3 +353,9 @@ nav.options-section-tabs + .options-section { .etapi-options-section div { height: auto !important; } + +/* BACKUP */ + +.options-section-card table a { + color: inherit; +} diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 25e6940e9a..4f5473af38 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1121,17 +1121,20 @@ }, "consistency_checks": { "title": "Consistency Checks", + "find_and_fix_label": "Find and fix consistency issues", + "find_and_fix_description": "Scan for and automatically repair any data consistency issues in the database.", "find_and_fix_button": "Find and fix consistency issues", "finding_and_fixing_message": "Finding and fixing consistency issues...", "issues_fixed_message": "Any consistency issue which may have been found is now fixed." }, "database_anonymization": { "title": "Database Anonymization", - "full_anonymization": "Full Anonymization", - "full_anonymization_description": "This action will create a new copy of the database and anonymize it (remove all note content and leave only structure and some non-sensitive metadata) for sharing online for debugging purposes without fear of leaking your personal data.", + "description": "Create an anonymized copy of your database for sharing with developers when debugging issues, without exposing personal data.", + "full_anonymization": "Full anonymization", + "full_anonymization_description": "Creates a copy of the database with all note content removed, leaving only structure and non-sensitive metadata. Safe for sharing online when debugging issues.", "save_fully_anonymized_database": "Save fully anonymized database", - "light_anonymization": "Light Anonymization", - "light_anonymization_description": "This action will create a new copy of the database and do a light anonymization on it — specifically only content of all notes will be removed, but titles and attributes will remain. Additionally, custom JS frontend/backend script notes and custom widgets will remain. This provides more context to debug the issues.", + "light_anonymization": "Light anonymization", + "light_anonymization_description": "Creates a copy with note content removed, but titles, attributes, and custom scripts/widgets remain. Provides more context for debugging.", "choose_anonymization": "You can decide yourself if you want to provide a fully or lightly anonymized database. Even fully anonymized DB is very useful, however in some cases lightly anonymized database can speed up the process of bug identification and fixing.", "save_lightly_anonymized_database": "Save lightly anonymized database", "existing_anonymized_databases": "Existing anonymized databases", @@ -1144,7 +1147,8 @@ }, "database_integrity_check": { "title": "Database Integrity Check", - "description": "This will check that the database is not corrupted on the SQLite level. It might take some time, depending on the DB size.", + "check_integrity_label": "Check database integrity", + "check_integrity_description": "Verify that the database is not corrupted on the SQLite level.", "check_button": "Check database integrity", "checking_integrity": "Checking database integrity...", "integrity_check_succeeded": "Integrity check succeeded - no problems found.", @@ -1152,7 +1156,11 @@ }, "sync": { "title": "Sync", + "force_full_sync_label": "Force full sync", + "force_full_sync_description": "Trigger a complete synchronization with the sync server, re-uploading all changes.", "force_full_sync_button": "Force full sync", + "fill_entity_changes_label": "Fill entity changes", + "fill_entity_changes_description": "Rebuild entity change records. Use this if sync is missing some changes.", "fill_entity_changes_button": "Fill entity changes records", "full_sync_triggered": "Full sync triggered", "filling_entity_changes": "Filling entity changes rows...", @@ -1160,8 +1168,13 @@ "finished-successfully": "Sync finished successfully.", "failed": "Sync failed: {{message}}" }, + "database": { + "title": "Database" + }, "vacuum_database": { "title": "Vacuum Database", + "vacuum_label": "Vacuum database", + "vacuum_description": "Rebuild the database to reduce file size. No data will be changed.", "description": "This will rebuild the database which will typically result in a smaller database file. No data will be actually changed.", "button_text": "Vacuum database", "vacuuming_database": "Vacuuming database...", @@ -1178,16 +1191,18 @@ "fonts": { "theme_defined": "Theme defined", "fonts": "Fonts", - "main_font": "Main Font", + "custom_fonts": "Use custom fonts", + "main_font": "Interface text", "font_family": "Font family", "size": "Size", - "note_tree_font": "Note Tree Font", - "note_detail_font": "Note Detail Font", - "monospace_font": "Monospace (code) Font", - "note_tree_and_detail_font_sizing": "Note that tree and detail font sizing is relative to the main font size setting.", - "not_all_fonts_available": "Not all listed fonts may be available on your system.", - "apply_font_changes": "To apply font changes, click on", - "reload_frontend": "reload frontend", + "preview": "Preview", + "note_tree_font": "Note tree text", + "note_detail_font": "Document text", + "monospace_font": "Monospace text", + "monospace_font_description": "Used for code notes and code blocks", + "size_relative_to_general": "Size is relative to the general font size", + "not_all_fonts_available": "Not all listed fonts may be available on your system", + "apply_changes": "Reload to apply changes", "generic-fonts": "Generic fonts", "sans-serif-system-fonts": "Sans-serif system fonts", "serif-system-fonts": "Serif system fonts", @@ -1216,15 +1231,18 @@ "edited_notes_message": "Edited Notes ribbon tab will automatically open on day notes" }, "theme": { - "title": "Application Theme", - "theme_label": "Theme", + "title": "User Interface", + "theme_label": "Application theme", "override_theme_fonts_label": "Override theme fonts", - "auto_theme": "Legacy (Follow system color scheme)", - "light_theme": "Legacy (Light)", - "dark_theme": "Legacy (Dark)", - "triliumnext": "Trilium (Follow system color scheme)", - "triliumnext-light": "Trilium (Light)", - "triliumnext-dark": "Trilium (Dark)", + "auto_theme": "Follow system color scheme", + "light_theme": "Light", + "dark_theme": "Dark", + "triliumnext": "Follow system color scheme", + "triliumnext-light": "Light", + "triliumnext-dark": "Dark", + "modern_themes": "Modern", + "legacy_themes": "Legacy", + "custom_themes": "Custom", "layout": "Layout", "layout-vertical-title": "Vertical", "layout-horizontal-title": "Horizontal", @@ -1233,11 +1251,11 @@ }, "ui-performance": { "title": "Performance", - "enable-motion": "Enable transitions and animations", - "enable-shadows": "Enable shadows", - "enable-backdrop-effects": "Enable background effects for menus, popups and panels", - "enable-smooth-scroll": "Enable smooth scrolling", - "app-restart-required": "(a restart of the application is required for the change to take effect)" + "enable-motion": "Transitions and animations", + "enable-shadows": "Shadows", + "enable-backdrop-effects": "Background effects for menus, popups and panels", + "enable-smooth-scroll": "Smooth scrolling", + "app-restart-required": "Requires app restart" }, "zoom_factor": { "title": "Zoom Factor (desktop build only)", @@ -1246,7 +1264,7 @@ "code_auto_read_only_size": { "title": "Automatic Read-Only Size", "description": "Automatic read-only note size is the size after which notes will be displayed in a read-only mode (for performance reasons).", - "label": "Automatic read-only size (code notes)", + "label": "Automatic read-only size", "unit": "characters" }, "code-editor-options": { @@ -1292,36 +1310,45 @@ "batch_ocr_error": "Error during batch processing: {{error}}" }, "attachment_erasure_timeout": { - "attachment_erasure_timeout": "Attachment Erasure Timeout", - "attachment_auto_deletion_description": "Attachments get automatically deleted (and erased) if they are not referenced by their note anymore after a defined time out.", - "erase_attachments_after": "Erase unused attachments after:", - "manual_erasing_description": "You can also trigger erasing manually (without considering the timeout defined above):", - "erase_unused_attachments_now": "Erase unused attachment notes now", + "attachment_erasure_timeout": "Unused Attachments", + "description": "Attachments that are no longer referenced by any note are considered unused and can be automatically erased after a period of time.", + "erase_attachments_after": "Erase unused attachments after", + "erase_attachments_after_description": "Time before unused attachments are permanently erased.", + "manual_erasing_description": "Trigger erasing manually, ignoring the timeout above.", + "erase_unused_attachments_now": "Erase unused attachments now", "unused_attachments_erased": "Unused attachments have been erased." }, "network_connections": { - "network_connections_title": "Network Connections", - "check_for_updates": "Check for updates automatically" + "network_connections_title": "Network", + "check_for_updates": "Check for updates automatically", + "check_for_updates_description": "Checks for new versions on GitHub and shows a notification in the global menu when available." }, "note_erasure_timeout": { - "note_erasure_timeout_title": "Note Erasure Timeout", - "note_erasure_description": "Deleted notes (and attributes, revisions...) are at first only marked as deleted and it is possible to recover them from Recent Notes dialog. After a period of time, deleted notes are \"erased\" which means their content is not recoverable anymore. This setting allows you to configure the length of the period between deleting and erasing the note.", - "erase_notes_after": "Erase notes after:", - "manual_erasing_description": "You can also trigger erasing manually (without considering the timeout defined above):", + "note_erasure_timeout_title": "Deleted Notes", + "description": "Deleted notes are first only marked as deleted and can be recovered from Recent Notes. After a period of time, they are permanently erased.", + "erase_notes_after": "Erase notes after", + "erase_notes_after_description": "Time before deleted notes are permanently erased.", + "manual_erasing_description": "Trigger erasing manually, ignoring the timeout above.", "erase_deleted_notes_now": "Erase deleted notes now", "deleted_notes_erased": "Deleted notes have been erased." }, + "revisions": { + "title": "Note Revisions" + }, "revisions_snapshot_interval": { "note_revisions_snapshot_interval_title": "Note Revision Snapshot Interval", "note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See wiki for more info.", - "snapshot_time_interval_label": "Note revision snapshot time interval:" + "note_revisions_snapshot_description_short": "Time after which a new note revision will be created.", + "snapshot_time_interval_label": "Snapshot interval" }, "revisions_snapshot_limit": { "note_revisions_snapshot_limit_title": "Note Revision Snapshot Limit", "note_revisions_snapshot_limit_description": "The note revision snapshot number limit refers to the maximum number of revisions that can be saved for each note. Where -1 means no limit, 0 means delete all revisions. You can set the maximum revisions for a single note through the #versioningLimit label.", - "snapshot_number_limit_label": "Note revision snapshot number limit:", + "note_revisions_snapshot_limit_description_short": "Max revisions per note. Use -1 for unlimited, 0 to disable.", + "snapshot_number_limit_label": "Maximum revisions", "snapshot_number_limit_unit": "snapshots", "erase_excess_revision_snapshots": "Erase excess revision snapshots now", + "erase_excess_revision_snapshots_description": "Delete revisions exceeding the limit for all notes.", "erase_excess_revision_snapshots_prompt": "Excess revision snapshots have been erased." }, "search": { @@ -1333,24 +1360,30 @@ }, "search_engine": { "title": "Search Engine", - "custom_search_engine_info": "Custom search engine requires both a name and a URL to be set. If either of these is not set, DuckDuckGo will be used as the default search engine.", - "predefined_templates_label": "Predefined search engine templates", + "custom_search_engine_info": "Used when searching the web for selected text. If not configured, DuckDuckGo will be used.", + "predefined_templates_label": "Presets", "bing": "Bing", "baidu": "Baidu", "duckduckgo": "DuckDuckGo", "google": "Google", - "custom_name_label": "Custom search engine name", - "custom_name_placeholder": "Customize search engine name", - "custom_url_label": "Custom search engine URL should include {keyword} as a placeholder for the search term.", - "custom_url_placeholder": "Customize search engine URL", + "custom_name_label": "Name", + "custom_name_placeholder": "Search engine name", + "custom_url_label": "URL", + "custom_url_description": "Use {keyword} as a placeholder for the search term.", + "custom_url_placeholder": "Search engine URL", "save_button": "Save" }, "tray": { "title": "System Tray", - "enable_tray": "Enable tray (Trilium needs to be restarted for this change to take effect)" + "enable_tray": "Tray icon", + "enable_tray_description": "Trilium needs to be restarted for this change to take effect." + }, + "text_editor": { + "title": "Editor" }, "heading_style": { - "title": "Heading Style", + "title": "Heading style", + "description": "Visual style for headings in text notes.", "plain": "Plain", "underline": "Underline", "markdown": "Markdown-style" @@ -1377,14 +1410,16 @@ "text_auto_read_only_size": { "title": "Automatic Read-Only Size", "description": "Automatic read-only note size is the size after which notes will be displayed in a read-only mode (for performance reasons).", - "label": "Automatic read-only size (text notes)", + "label": "Automatic read-only size", "unit": "characters" }, "custom_date_time_format": { - "title": "Custom Date/Time Format", + "title": "Date/time format", "description": "Customize the format of the date and time inserted via or the toolbar. See Day.js docs for available format tokens.", - "format_string": "Format string:", - "formatted_time": "Formatted date/time:" + "description_short": "Customize the format of the date and time inserted via the toolbar.", + "preview": "Preview: {{preview}}", + "format_string": "Format string", + "formatted_time": "Formatted date/time" }, "i18n": { "title": "Localization", @@ -1399,20 +1434,20 @@ "sunday": "Sunday", "first-week-of-the-year": "First week of the year", "first-week-contains-first-day": "First week contains first day of the year", - "first-week-contains-first-thursday": "First week contains first Thursday of the year", + "first-week-contains-first-thursday": "First week contains first Thursday (ISO 8601)", "first-week-has-minimum-days": "First week has minimum days", "min-days-in-first-week": "Minimum days in first week", - "first-week-info": "First week contains first Thursday of the year is based on ISO 8601 standard.", - "first-week-warning": "Changing first week options may cause duplicate with existing Week Notes and the existing Week Notes will not be updated accordingly.", + "first-week-warning": "Changing this may cause duplicates with existing Week Notes.", "formatting-locale": "Date & number format", "formatting-locale-auto": "Based on the application's language" }, "backup": { + "title": "Backup", "automatic_backup": "Automatic backup", "automatic_backup_description": "Trilium can back up the database automatically:", - "enable_daily_backup": "Enable daily backup", - "enable_weekly_backup": "Enable weekly backup", - "enable_monthly_backup": "Enable monthly backup", + "enable_daily_backup": "Backup daily", + "enable_weekly_backup": "Backup weekly", + "enable_monthly_backup": "Backup monthly", "backup_recommendation": "It's recommended to keep the backup turned on, but this can make application startup slow with large databases and/or slow storage devices.", "backup_now": "Backup now", "backup_database_now": "Backup database now", @@ -1456,11 +1491,15 @@ "new_password": "New password", "new_password_confirmation": "New password confirmation", "change_password": "Change password", - "protected_session_timeout": "Protected Session Timeout", - "protected_session_timeout_description": "Protected session timeout is a time period after which the protected session is wiped from the browser's memory. This is measured from the last interaction with protected notes. See", + "change_password_description": "Update your current password", + "reset_password": "Reset password", + "reset_password_description": "Permanently lose access to protected notes", + "cancel": "Cancel", + "protected_session_timeout": "Protected Session", + "protected_session_timeout_description": "Time of inactivity before the session is cleared from browser memory. See", "wiki": "wiki", "for_more_info": "for more info.", - "protected_session_timeout_label": "Protected session timeout:", + "protected_session_timeout_label": "Auto-close session after", "reset_confirmation": "By resetting the password you will forever lose access to all your existing protected notes. Do you really want to reset the password?", "reset_success_message": "Password has been reset. Please set new password", "change_password_heading": "Change Password", @@ -1530,18 +1569,17 @@ "related_description": "Configure spell check languages and custom dictionary." }, "sync_2": { - "config_title": "Sync Configuration", - "server_address": "Server instance address", - "timeout": "Sync timeout", - "timeout_description": "How long to wait before giving up on a slow sync connection. Increase if you have an unstable network.", - "proxy_label": "Sync proxy server (optional)", - "note": "Note", - "note_description": "If you leave the proxy setting blank, the system proxy will be used (applies to desktop/electron build only).", - "special_value_description": "Another special value is noproxy which forces ignoring even the system proxy and respects NODE_TLS_REJECT_UNAUTHORIZED.", + "config_title": "Sync Server", + "server_address": "Server address", + "server_address_description": "URL of the Trilium server to sync with.", + "timeout": "Connection timeout", + "timeout_description": "Time to wait before giving up on a slow connection.", + "proxy_label": "Proxy server", + "proxy_description": "Leave blank to use system proxy (desktop only). Use \"noproxy\" to bypass all proxies.", "save": "Save", "help": "Help", - "test_title": "Sync Test", - "test_description": "This will test the connection and handshake to the sync server. If the sync server isn't initialized, this will set it up to sync with the local document.", + "test_title": "Test Connection", + "test_description": "Test the connection to the sync server. If not initialized, this will set up syncing.", "test_button": "Test sync", "handshake_failed": "Sync server handshake failed, error: {{message}}" }, @@ -1903,6 +1941,7 @@ "editing": { "editor_type": { "label": "Formatting toolbar", + "toolbar_style": "Toolbar style", "floating": { "title": "Floating", "description": "editing tools appear near the cursor;" @@ -1911,7 +1950,7 @@ "title": "Fixed", "description": "editing tools appear in the \"Formatting\" ribbon tab." }, - "multiline-toolbar": "Display the toolbar on multiple lines if it doesn't fit." + "multiline-toolbar": "Display the toolbar on multiple lines if it doesn't fit" } }, "electron_context_menu": { @@ -1978,7 +2017,7 @@ "days": "Days" }, "share": { - "title": "Share Settings", + "title": "Share", "redirect_bare_domain": "Redirect bare domain to Share page", "redirect_bare_domain_description": "Redirect anonymous users to the Share page instead of showing Login", "show_login_link": "Show Login link in Share theme", @@ -2042,12 +2081,12 @@ }, "editorfeatures": { "title": "Features", - "emoji_completion_enabled": "Enable Emoji auto-completion", - "emoji_completion_description": "If enabled, emojis can be easily inserted into text by typing `:`, followed by the name of an emoji.", - "note_completion_enabled": "Enable note auto-completion", - "note_completion_description": "If enabled, links to notes can be created by typing `@` followed by the title of a note.", - "slash_commands_enabled": "Enable slash commands", - "slash_commands_description": "If enabled, editing commands such as inserting line breaks or headings can be toggled by typing `/`." + "emoji_completion_enabled": "Emoji auto-completion", + "emoji_completion_description": "Emojis can be easily inserted into text by typing `:`, followed by the name of an emoji.", + "note_completion_enabled": "Note auto-completion", + "note_completion_description": "Links to notes can be created by typing `@` followed by the title of a note.", + "slash_commands_enabled": "Slash commands", + "slash_commands_description": "Editing commands such as inserting line breaks or headings can be toggled by typing `/`." }, "table_view": { "new-row": "New row", @@ -2166,6 +2205,8 @@ "related_code_blocks": "Color scheme for code blocks in text notes", "related_code_notes": "Color scheme for code notes", "ui": "User interface", + "ui_layout_style": "Layout style", + "ui_layout_orientation": "Launcher bar orientation", "ui_old_layout": "Old layout", "ui_new_layout": "New layout" }, diff --git a/apps/client/src/widgets/react/FormSelect.tsx b/apps/client/src/widgets/react/FormSelect.tsx index 6c23405434..b8f10c1536 100644 --- a/apps/client/src/widgets/react/FormSelect.tsx +++ b/apps/client/src/widgets/react/FormSelect.tsx @@ -24,14 +24,15 @@ interface FormSelectProps extends ValueConfig { onChange: OnChangeListener; style?: CSSProperties; className?: string; + disabled?: boolean; } /** * Combobox component that takes in any object array as data. Each item of the array is rendered as an item, and the key and values are obtained by looking into the object by a specified key. */ -export default function FormSelect({ name, id, onChange, style, className, ...restProps }: FormSelectProps) { +export default function FormSelect({ name, id, onChange, style, className, disabled, ...restProps }: FormSelectProps) { return ( - + ); @@ -40,9 +41,9 @@ export default function FormSelect({ name, id, onChange, style, className, .. /** * Similar to {@link FormSelect}, but the top-level elements are actually groups. */ -export function FormSelectWithGroups({ name, id, values, keyProperty, titleProperty, currentValue, onChange, ...restProps }: FormSelectProps | T>) { +export function FormSelectWithGroups({ name, id, values, keyProperty, titleProperty, currentValue, onChange, disabled, ...restProps }: FormSelectProps | T>) { return ( - + {values.map((item) => { if (!item) return <>; if (typeof item === "object" && "items" in item) { @@ -61,7 +62,7 @@ export function FormSelectWithGroups({ name, id, values, keyProperty, titlePr ) } -function FormSelectBody({ id, name, children, onChange, style, className }: { id?: string, name?: string, children: ComponentChildren, onChange: OnChangeListener, style?: CSSProperties, className?: string }) { +function FormSelectBody({ id, name, children, onChange, style, className, disabled }: { id?: string, name?: string, children: ComponentChildren, onChange: OnChangeListener, style?: CSSProperties, className?: string, disabled?: boolean }) { return ( diff --git a/apps/client/src/widgets/type_widgets/options/advanced.tsx b/apps/client/src/widgets/type_widgets/options/advanced.tsx index 619a1fb514..5858609572 100644 --- a/apps/client/src/widgets/type_widgets/options/advanced.tsx +++ b/apps/client/src/widgets/type_widgets/options/advanced.tsx @@ -1,41 +1,39 @@ import { AnonymizedDbResponse, DatabaseAnonymizeResponse, DatabaseCheckIntegrityResponse } from "@triliumnext/commons"; import { useCallback, useEffect, useMemo, useState } from "preact/hooks"; -import { experimentalFeatures, type ExperimentalFeatureId } from "../../../services/experimental_features"; +import { type ExperimentalFeatureId,experimentalFeatures } from "../../../services/experimental_features"; import { t } from "../../../services/i18n"; import server from "../../../services/server"; import toast from "../../../services/toast"; -import Button from "../../react/Button"; -import Column from "../../react/Column"; import FormText from "../../react/FormText"; -import FormToggle from "../../react/FormToggle"; import { useTriliumOptionJson } from "../../react/hooks"; -import OptionsRow from "./components/OptionsRow"; +import { OptionsRowWithButton, OptionsRowWithToggle } from "./components/OptionsRow"; import OptionsSection from "./components/OptionsSection"; export default function AdvancedSettings() { return <> - - + - + ; } function AdvancedSyncOptions() { return ( - - - - - + setShowModal(false)} + title={label} + fontFamily={fontFamily ?? ""} + fontSize={parseInt(fontSize ?? "100", 10)} + onFontFamilyChange={setFontFamily} + onFontSizeChange={(size) => setFontSize(String(size))} + getFontFamily={getFontFamily} + sizeDescription={sizeDescription} + /> ); } +const PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog. 0123456789"; + +interface FontPickerModalProps { + show: boolean; + onHidden: () => void; + title: string; + fontFamily: string; + fontSize: number; + onFontFamilyChange: (value: string) => void; + onFontSizeChange: (value: number) => void; + getFontFamily: (value: string) => string | undefined; + sizeDescription?: string; +} + +function FontPickerModal({ show, onHidden, title, fontFamily, fontSize, onFontFamilyChange, onFontSizeChange, getFontFamily, sizeDescription }: FontPickerModalProps) { + return createPortal( + +
+
+ + {FONT_FAMILIES.map(group => ( + + + {group.items.map(item => ( + onFontFamilyChange(item.value)} + checked={fontFamily === item.value} + selected={fontFamily === item.value} + > + + {item.label ?? item.value} + + + ))} + + ))} + +
+ +
+
+ +
+ + {fontSize}% +
+ {sizeDescription && {sizeDescription}} +
+ +
+ +
+ {PREVIEW_TEXT} +
+
+
+
+
, + document.body + ); +} + function ElectronIntegration() { const [ zoomFactor ] = useTriliumOption("zoomFactor"); const [ nativeTitleBarVisible, setNativeTitleBarVisible ] = useTriliumOptionBool("nativeTitleBarVisible"); @@ -353,26 +551,28 @@ function ElectronIntegration() { /> - - - + - - - {t("electron_integration.background-effects")} - {" "} - - } - currentValue={backgroundEffects} onChange={setBackgroundEffects} - disabled={nativeTitleBarVisible} - /> - + {t("electron_integration.background-effects")} } + description={t("electron_integration.background-effects-description")} + currentValue={backgroundEffects} + onChange={setBackgroundEffects} + disabled={nativeTitleBarVisible} + /> - + ); +} diff --git a/apps/client/src/widgets/type_widgets/options/components/OptionsSection.tsx b/apps/client/src/widgets/type_widgets/options/components/OptionsSection.tsx index ce42b66e36..883801731c 100644 --- a/apps/client/src/widgets/type_widgets/options/components/OptionsSection.tsx +++ b/apps/client/src/widgets/type_widgets/options/components/OptionsSection.tsx @@ -1,19 +1,47 @@ import type { ComponentChildren } from "preact"; import { CSSProperties } from "preact/compat"; +import HelpButton from "../../../react/HelpButton"; interface OptionsSectionProps { title?: ComponentChildren; + description?: ComponentChildren; children: ComponentChildren; noCard?: boolean; style?: CSSProperties; className?: string; + helpUrl?: string; } -export default function OptionsSection({ title, children, noCard, className, ...rest }: OptionsSectionProps) { - return ( -
+export default function OptionsSection({ title, description, children, noCard, className, helpUrl, ...rest }: OptionsSectionProps) { + const header = (title || helpUrl) && ( +
{title &&

{title}

} + {helpUrl && } +
+ ); + + const content = ( + <> + {description &&

{description}

} {children} + + ); + + if (noCard) { + return ( +
+ {header} + {content} +
+ ); + } + + return ( +
+ {header} +
+ {content} +
); } diff --git a/apps/client/src/widgets/type_widgets/options/components/RadioWithIllustration.css b/apps/client/src/widgets/type_widgets/options/components/RadioWithIllustration.css index a8286805e9..b48a15ac2d 100644 --- a/apps/client/src/widgets/type_widgets/options/components/RadioWithIllustration.css +++ b/apps/client/src/widgets/type_widgets/options/components/RadioWithIllustration.css @@ -1,4 +1,4 @@ -.options-section .radio-with-illustration { +.options-section-card .radio-with-illustration { list-style-type: none; margin-bottom: 0; padding: 0; diff --git a/apps/client/src/widgets/type_widgets/options/i18n.tsx b/apps/client/src/widgets/type_widgets/options/i18n.tsx index 5f65993021..4809adc3e0 100644 --- a/apps/client/src/widgets/type_widgets/options/i18n.tsx +++ b/apps/client/src/widgets/type_widgets/options/i18n.tsx @@ -6,10 +6,7 @@ import OptionsSection from "./components/OptionsSection"; import { useTriliumOption, useTriliumOptionJson } from "../../react/hooks"; import type { Locale } from "@triliumnext/commons"; import { isElectron, restartDesktopApp } from "../../../services/utils"; -import FormRadioGroup from "../../react/FormRadioGroup"; import FormText from "../../react/FormText"; -import RawHtml from "../../react/RawHtml"; -import Admonition from "../../react/Admonition"; import Button from "../../react/Button"; import CheckboxList from "./components/CheckboxList"; import RelatedSettings from "./components/RelatedSettings"; @@ -96,10 +93,13 @@ function DateSettings() { /> - - + ({ days: String(i + 1) }))} /> } - - - - - - {t("i18n.first-week-warning")} - -
- - -
+ + + -
- ); -} -export function SyncTest() { - return ( - - {t("sync_2.test_description")} -