export { AccountMailPreferences, SignatureChoice } from "components/account-mail-preferences.slint"; import { OpenPgpReader } from "components/account-mail-preferences.slint"; import { MailAttachments } from "models.slint"; import { AttachmentPreviewDialog } from "dialogs/attachment-preview-dialog.slint"; export { MailAttachments, MailAttachment } from "models.slint"; export { ZoomApi } from "components/zoom-controls.slint"; export { RendererSettings } from "renderer-settings.slint"; export { WindowChrome } from "window-chrome.slint"; export { AppIcon, AppIconVariant } from "app-icon.slint"; export { DefaultEmailApp } from "default-email-app.slint"; export { SystemNotifications } from "system-notifications.slint"; import { AppIcon, AppIconVariant } from "app-icon.slint"; import { WindowChrome, WindowControls, WindowDragArea } from "window-chrome.slint"; // Bundled once at compile time and used throughout the application chrome. // Rust registers the separate Noto Emoji face before constructing this window. import "../resources/fonts/google-sans-flex/GoogleSansFlex-VariableFont_GRAD,ROND,opsz,slnt,wdth,wght.ttf"; import { Palette } from "std-widgets.slint"; import { TypeScale } from "typography.slint"; export { AppTheme, MotionSettings, ThemeUtilities } from "design-system.slint"; import { AppTheme, LayoutMetrics } from "design-system.slint"; import { Button } from "components/button.slint"; import { LoadingSkeletonBlock } from "components/loading-skeleton.slint"; import { AccountRow, CalendarAgendaRow, CalendarAgendaFilterRow, CalendarDay, CalendarEventRow, CalendarSourceRow, CalendarWeekDay, ComposeAttachment, ComposeContact, ComposeEditorTile, ContactSidebarRow, ContactRow, ContactSyncState, ContactValueRow, EmailLink, EmailRow, EmailTemplateRow, EmailTile, LocalizedMessage, MailListEntry, MailProfileRow, ProfileMemberRow, SidebarRow, MailLabelRow, StorageUsageRow, ThreadMessageRow } from "models.slint"; export { EmailReader, ReaderCaret, ReaderImage, ReaderItem, MailDragApi } from "models.slint"; export { MailListNavigation, MailNavigationTarget } from "models.slint"; export { ThreadMessageRow, StorageUsageRow } from "models.slint"; export { CalendarAgendaNavigation } from "components/calendar-agenda.slint"; export { KeyboardKeys, KeyChord } from "components/kbd.slint"; import { FunctionalCalendarView } from "components/calendar.slint"; import { CompactProductNav, ProductRail, WindowTopBar } from "components/workspace-navigation.slint"; export { TabsUi, TabRow } from "components/workspace-navigation.slint"; import { TabsUi } from "components/workspace-navigation.slint"; import { TabOverview } from "components/tab-overview.slint"; import { WorkspaceFrame } from "components/workspace-layout.slint"; import { CalendarEventDialog } from "dialogs/calendar-event-dialog.slint"; import { ComposeDialog } from "dialogs/compose-dialog.slint"; import { BoundComposer, ComposerUi } from "components/composer-ui.slint"; import { ComposerText } from "components/composer-fields.slint"; export { ComposerUi } from "components/composer-ui.slint"; import { FolderDialog } from "dialogs/folder-dialog.slint"; export { MovePickerUi, MoveTargetRow } from "dialogs/move-dialog.slint"; import { MoveDialog, MovePickerUi } from "dialogs/move-dialog.slint"; import { ConfirmationDialog } from "components/confirmation-dialog.slint"; import { SettingsDialog } from "dialogs/settings-dialog.slint"; import { AccountImport, ImportDialog } from "dialogs/import-dialog.slint"; export { LocalMailUi } from "local-mail.slint"; export { Numbers } from "numbers.slint"; import { LocalMailUi } from "local-mail.slint"; export { AccountImport, ImportAccountRow, ImportFailureRow, ImportFindingRow, ImportMailRow, ImportProfileRow } from "dialogs/import-dialog.slint"; export { ThemeDraft } from "components/theme-editor.slint"; import { ThemeDraft } from "components/theme-editor.slint"; import { MailView } from "views/mail-view.slint"; export { MessageColors } from "message-colors.slint"; export { FilesUi, FileRow, OperationRow } from "views/files-view.slint"; import { FilesView, FilesUi } from "views/files-view.slint"; import { ContactsView } from "views/contacts-view.slint"; import { OnboardingView } from "views/onboarding-view.slint"; // Rust view-models call this global for the date vocabulary and status text // that must be materialized before it enters a Slint model. Keeping the // translations here makes those strings part of the same bundled catalog as // the declarative UI. export global I18n { // Date vocabulary and patterns for src/date_format.rs. Patterns use CLDR // date field symbols: y year, M month (MMM abbreviated, MMMM full), // LLLL standalone month, d day, E weekday (EEEE full), a AM/PM, h and H // 12- and 24-hour hours, m minutes. Text in single quotes is literal. pure callback date-month(int, string) -> string; pure callback date-weekday(int, bool) -> string; pure callback date-day-period(bool) -> string; pure callback date-yesterday() -> string; pure callback date-pattern(string) -> string; pure callback date-range(string, string) -> string; pure callback date-at-time(string, string) -> string; pure callback calendar-all-day() -> string; pure callback untitled-event() -> string; pure callback no-contact-history() -> string; pure callback contact-history-available() -> string; pure callback new-contact-entry() -> string; pure callback no-connected-account() -> string; pure callback interaction-count(int) -> string; // Words of new-mail notifications (src/notifications.rs). pure callback notification-new-message() -> string; pure callback notification-no-subject() -> string; pure callback notification-new-messages(int) -> string; pure callback notification-more-senders(string, int) -> string; pure callback notification-open() -> string; pure callback notification-mark-read() -> string; pure callback notification-archive() -> string; pure callback notification-delete() -> string; // What a screen reader says for the count on the app icon. pure callback notification-unread-count(int) -> string; // The line a desktop shows for the app while it runs with its window // closed (GNOME's Background Apps). pure callback background-status() -> string; pure callback last-contacted(string) -> string; pure callback loading-local-data() -> string; pure callback local-data-ready() -> string; pure callback local-data-unavailable(string) -> string; pure callback could-not-display-local-mail(string) -> string; pure callback ui-message(string, string, string, string) -> string; pure callback ui-message-settings(string, string, string, string) -> string; pure callback ui-message-system(string, string, string, string) -> string; pure callback ui-message-account(string, string, string, string) -> string; pure callback ui-message-data(string, string, string, string) -> string; pure callback ui-message-list(string, string, string, string) -> string; pure callback ui-message-contacts(string, string, string, string) -> string; pure callback ui-message-calendar(string, string, string, string) -> string; pure callback ui-message-startup(string, string, string, string) -> string; pure callback ui-message-sync(string, string, string, string) -> string; pure callback ui-message-actions(string, string, string, string) -> string; pure callback ui-message-compose(string, string, string, string) -> string; pure callback ui-message-oauth(string, string, string, string) -> string; date-month(month, form) => { if (form == "abbreviated") { if (month == 1) { return @tr("abbreviated month in a date" => "Jan"); } if (month == 2) { return @tr("abbreviated month in a date" => "Feb"); } if (month == 3) { return @tr("abbreviated month in a date" => "Mar"); } if (month == 4) { return @tr("abbreviated month in a date" => "Apr"); } if (month == 5) { return @tr("abbreviated month in a date" => "May"); } if (month == 6) { return @tr("abbreviated month in a date" => "Jun"); } if (month == 7) { return @tr("abbreviated month in a date" => "Jul"); } if (month == 8) { return @tr("abbreviated month in a date" => "Aug"); } if (month == 9) { return @tr("abbreviated month in a date" => "Sep"); } if (month == 10) { return @tr("abbreviated month in a date" => "Oct"); } if (month == 11) { return @tr("abbreviated month in a date" => "Nov"); } if (month == 12) { return @tr("abbreviated month in a date" => "Dec"); } } if (form == "wide") { if (month == 1) { return @tr("month in a date" => "January"); } if (month == 2) { return @tr("month in a date" => "February"); } if (month == 3) { return @tr("month in a date" => "March"); } if (month == 4) { return @tr("month in a date" => "April"); } if (month == 5) { return @tr("month in a date" => "May"); } if (month == 6) { return @tr("month in a date" => "June"); } if (month == 7) { return @tr("month in a date" => "July"); } if (month == 8) { return @tr("month in a date" => "August"); } if (month == 9) { return @tr("month in a date" => "September"); } if (month == 10) { return @tr("month in a date" => "October"); } if (month == 11) { return @tr("month in a date" => "November"); } if (month == 12) { return @tr("month in a date" => "December"); } } if (month == 1) { return @tr("month on its own" => "January"); } if (month == 2) { return @tr("month on its own" => "February"); } if (month == 3) { return @tr("month on its own" => "March"); } if (month == 4) { return @tr("month on its own" => "April"); } if (month == 5) { return @tr("month on its own" => "May"); } if (month == 6) { return @tr("month on its own" => "June"); } if (month == 7) { return @tr("month on its own" => "July"); } if (month == 8) { return @tr("month on its own" => "August"); } if (month == 9) { return @tr("month on its own" => "September"); } if (month == 10) { return @tr("month on its own" => "October"); } if (month == 11) { return @tr("month on its own" => "November"); } if (month == 12) { return @tr("month on its own" => "December"); } return ""; } date-weekday(day, abbreviated) => { if (day == 0) { return abbreviated ? @tr("Mon") : @tr("Monday"); } if (day == 1) { return abbreviated ? @tr("Tue") : @tr("Tuesday"); } if (day == 2) { return abbreviated ? @tr("Wed") : @tr("Wednesday"); } if (day == 3) { return abbreviated ? @tr("Thu") : @tr("Thursday"); } if (day == 4) { return abbreviated ? @tr("Fri") : @tr("Friday"); } if (day == 5) { return abbreviated ? @tr("Sat") : @tr("Saturday"); } return abbreviated ? @tr("Sun") : @tr("Sunday"); } date-day-period(pm) => { return pm ? @tr("time of day" => "PM") : @tr("time of day" => "AM"); } date-yesterday() => { return @tr("Yesterday"); } date-pattern(key) => { if (key == "short-day-month") { return @tr("date pattern (CLDR): day and month of recent mail in lists" => "MMM d"); } if (key == "short-date") { return @tr("date pattern (CLDR): date of older mail in lists, keep it short" => "MMM d, y"); } if (key == "date") { return @tr("date pattern (CLDR): a date on its own" => "MMM d, y"); } if (key == "weekday-date") { return @tr("date pattern (CLDR): a date with its weekday" => "EEE, MMM d, y"); } if (key == "weekday-day-month") { return @tr("date pattern (CLDR): day heading in the agenda" => "EEEE, MMMM d"); } if (key == "month-year") { return @tr("date pattern (CLDR): month heading, LLLL is the month name on its own" => "LLLL y"); } if (key == "same-month-start") { return @tr("date pattern (CLDR): first date of a range within one month" => "MMMM d"); } if (key == "same-month-end") { return @tr("date pattern (CLDR): last date of a range within one month" => "d, y"); } if (key == "same-year-start") { return @tr("date pattern (CLDR): first date of a range within one year" => "MMM d"); } if (key == "same-year-end") { return @tr("date pattern (CLDR): last date of a range within one year" => "MMM d, y"); } if (key == "time-24") { return @tr("time pattern (CLDR): 24-hour clock" => "HH:mm"); } if (key == "time-12") { return @tr("time pattern (CLDR): 12-hour clock, a is AM or PM" => "h:mm a"); } return @tr("time pattern (CLDR): whole hour on a 12-hour clock" => "h a"); } date-range(start, end) => { return @tr("date range" => "{0} – {1}", start, end); } date-at-time(date, time) => { return @tr("date and time" => "{0} at {1}", date, time); } calendar-all-day() => { return @tr("All day"); } untitled-event() => { return @tr("(untitled event)"); } no-contact-history() => { return @tr("No conversation history"); } contact-history-available() => { return @tr("Conversation history available"); } new-contact-entry() => { return @tr("New address-book entry"); } no-connected-account() => { return @tr("No connected account"); } interaction-count(count) => { return @tr("{n} interaction" | "{n} interactions" % count); } notification-new-message() => { return @tr("New message"); } notification-no-subject() => { return @tr("(No subject)"); } notification-new-messages(count) => { return @tr("{n} new message" | "{n} new messages" % count); } notification-more-senders(senders, count) => { return @tr("the first senders of new mail, then how many others" => "{0} and {n} more" | "{0} and {n} more" % count, senders); } notification-open() => { return @tr("Open"); } notification-mark-read() => { return @tr("Mark as read"); } notification-unread-count(count) => { return @tr("{n} unread message" | "{n} unread messages" % count); } notification-archive() => { return @tr("Archive"); } notification-delete() => { return @tr("Delete"); } background-status() => { return @tr("Checking for new mail"); } last-contacted(date) => { return @tr("Last contacted {}", date); } loading-local-data() => { return @tr("Loading local data…"); } local-data-ready() => { return @tr("Local data ready."); } local-data-unavailable(error) => { return @tr("Local data is unavailable: {}", error); } could-not-display-local-mail(error) => { return @tr("Could not display local mail: {}", error); } ui-message(key, argument, second-argument, third-argument) => { if (key == "") { return ""; } // Every Rust-originated UI message is selected here from a stable key. // Never translate `key` dynamically: explicit @tr expressions keep the // complete source catalog available to extraction and translators. The // bounded groups avoid a deeply recursive expression in the compiler. let settings = root.ui-message-settings(key, argument, second-argument, third-argument); if (settings != "") { return settings; } let system = root.ui-message-system(key, argument, second-argument, third-argument); if (system != "") { return system; } let account = root.ui-message-account(key, argument, second-argument, third-argument); if (account != "") { return account; } let data = root.ui-message-data(key, argument, second-argument, third-argument); if (data != "") { return data; } let list = root.ui-message-list(key, argument, second-argument, third-argument); if (list != "") { return list; } let contacts = root.ui-message-contacts(key, argument, second-argument, third-argument); if (contacts != "") { return contacts; } let calendar = root.ui-message-calendar(key, argument, second-argument, third-argument); if (calendar != "") { return calendar; } let startup = root.ui-message-startup(key, argument, second-argument, third-argument); if (startup != "") { return startup; } let sync = root.ui-message-sync(key, argument, second-argument, third-argument); if (sync != "") { return sync; } let actions = root.ui-message-actions(key, argument, second-argument, third-argument); if (actions != "") { return actions; } let compose = root.ui-message-compose(key, argument, second-argument, third-argument); if (compose != "") { return compose; } let oauth = root.ui-message-oauth(key, argument, second-argument, third-argument); if (oauth != "") { return oauth; } return @tr("An unexpected interface message occurred."); } ui-message-settings(key, argument, second-argument, third-argument) => { if (key == "Notification preferences are active for this session.") { return @tr("Notification preferences are active for this session."); } if (key == "Notification preferences saved.") { return @tr("Notification preferences saved."); } if (key == "Could not save notifications: {}") { return @tr("Could not save notifications: {}", argument); } if (key == "Sync interval is active for this session.") { return @tr("Sync interval is active for this session."); } if (key == "Sync interval set to {} minutes.") { return @tr("Sync interval set to {} minutes.", argument); } if (key == "Could not save sync interval: {}") { return @tr("Could not save sync interval: {}", argument); } if (key == "Message-opening preference updated for this session.") { return @tr("Message-opening preference updated for this session."); } if (key == "Message-opening preference saved.") { return @tr("Message-opening preference saved."); } if (key == "Could not save message-opening preference: {}") { return @tr("Could not save message-opening preference: {}", argument); } if (key == "Contact suggestion preferences are active for this session.") { return @tr("Contact suggestion preferences are active for this session."); } if (key == "Contact suggestion preferences saved.") { return @tr("Contact suggestion preferences saved."); } if (key == "Could not save contact suggestion preferences: {}") { return @tr("Could not save contact suggestion preferences: {}", argument); } if (key == "Contact storage is still starting. Try again shortly.") { return @tr("Contact storage is still starting. Try again shortly."); } if (key == "Cleared {} suggested people.") { return @tr("Cleared {} suggested people.", argument); } if (key == "Could not clear suggested people: {}") { return @tr("Could not clear suggested people: {}", argument); } if (key == "Theme updated for this session.") { return @tr("Theme updated for this session."); } if (key == "Theme preference saved.") { return @tr("Theme preference saved."); } if (key == "Message appearance updated for this session.") { return @tr("Message appearance updated for this session."); } if (key == "Message appearance preference saved.") { return @tr("Message appearance preference saved."); } if (key == "Could not save message appearance: {}") { return @tr("Could not save message appearance: {}", argument); } if (key == "Could not save theme: {}") { return @tr("Could not save theme: {}", argument); } if (key == "Theme palette updated for this session.") { return @tr("Theme palette updated for this session."); } if (key == "Theme palette saved.") { return @tr("Theme palette saved."); } if (key == "Could not save theme palette: {}") { return @tr("Could not save theme palette: {}", argument); } if (key == "Custom theme updated for this session.") { return @tr("Custom theme updated for this session."); } if (key == "Custom theme saved.") { return @tr("Custom theme saved."); } if (key == "Download location saved.") { return @tr("Download location saved."); } if (key == "Download location is saved once an account is set up.") { return @tr("Download location is saved once an account is set up."); } if (key == "Could not save the download location: {}") { return @tr("Could not save the download location: {}", argument); } if (key == "Choose download location") { return @tr("Choose download location"); } if (key == "Could not save custom theme: {}") { return @tr("Could not save custom theme: {}", argument); } if (key == "Language updated for this session.") { return @tr("Language updated for this session."); } if (key == "Language preference saved.") { return @tr("Language preference saved."); } if (key == "Could not save language: {}") { return @tr("Could not save language: {}", argument); } if (key == "Time format updated for this session.") { return @tr("Time format updated for this session."); } if (key == "Time format preference saved.") { return @tr("Time format preference saved."); } if (key == "Could not save time format: {}") { return @tr("Could not save time format: {}", argument); } if (key == "Sidebar icon style updated for this session.") { return @tr("Sidebar icon style updated for this session."); } if (key == "Sidebar icon preference saved.") { return @tr("Sidebar icon preference saved."); } if (key == "Could not save sidebar icon preference: {}") { return @tr("Could not save sidebar icon preference: {}", argument); } if (key == "App icon updated for this session.") { return @tr("App icon updated for this session."); } if (key == "App icon saved.") { return @tr("App icon saved."); } if (key == "Could not save app icon: {}") { return @tr("Could not save app icon: {}", argument); } if (key == "Avatar preference updated for this session.") { return @tr("Avatar preference updated for this session."); } if (key == "Avatar preference saved.") { return @tr("Avatar preference saved."); } if (key == "Could not save avatar preference: {}") { return @tr("Could not save avatar preference: {}", argument); } if (key == "Window frame updated for this session.") { return @tr("Window frame updated for this session."); } if (key == "Window frame preference saved.") { return @tr("Window frame preference saved."); } if (key == "Could not save window frame preference: {}") { return @tr("Could not save window frame preference: {}", argument); } if (key == "Tabs preference updated for this session.") { return @tr("Tabs preference updated for this session."); } if (key == "Tabs preference saved.") { return @tr("Tabs preference saved."); } if (key == "Could not save tabs preference: {}") { return @tr("Could not save tabs preference: {}", argument); } if (key == "At most {} tabs can be open. Close a tab to open another.") { return @tr("At most {} tabs can be open. Close a tab to open another.", argument); } if (key == "Mail grouping is active for this session.") { return @tr("Mail grouping is active for this session."); } if (key == "Mail grouping preference saved.") { return @tr("Mail grouping preference saved."); } if (key == "Could not save mail grouping preference: {}") { return @tr("Could not save mail grouping preference: {}", argument); } if (key == "Conversation view updated for this session.") { return @tr("Conversation view updated for this session."); } if (key == "Conversation view preference saved.") { return @tr("Conversation view preference saved."); } if (key == "Could not save conversation view preference: {}") { return @tr("Could not save conversation view preference: {}", argument); } if (key == "Profile changes require local mail data.") { return @tr("Profile changes require local mail data."); } if (key == "Profile saved.") { return @tr("Profile saved."); } if (key == "Could not save profile: {}") { return @tr("Could not save profile: {}", argument); } if (key == "Account profile updated.") { return @tr("Account profile updated."); } if (key == "Account color changes require local mail data.") { return @tr("Account color changes require local mail data."); } if (key == "Account color updated.") { return @tr("Account color updated."); } if (key == "Could not update account color: {}") { return @tr("Could not update account color: {}", argument); } if (key == "Could not update account profile: {}") { return @tr("Could not update account profile: {}", argument); } if (key == "Profile deleted.") { return @tr("Profile deleted."); } if (key == "Could not delete profile: {}") { return @tr("Could not delete profile: {}", argument); } if (key == "Could not switch profile: {}") { return @tr("Could not switch profile: {}", argument); } if (key == "Marker preference is active for this session.") { return @tr("Marker preference is active for this session."); } if (key == "Mail-list marker preference saved.") { return @tr("Mail-list marker preference saved."); } if (key == "Could not save mail-list marker preference: {}") { return @tr("Could not save mail-list marker preference: {}", argument); } if (key == "Workspace layout updated for this session.") { return @tr("Workspace layout updated for this session."); } if (key == "Workspace layout preference saved.") { return @tr("Workspace layout preference saved."); } if (key == "Could not save workspace layout: {}") { return @tr("Could not save workspace layout: {}", argument); } if (key == "Could not save list pane width: {}") { return @tr("Could not save list pane width: {}", argument); } return ""; } ui-message-system(key, argument, second-argument, third-argument) => { if (key == "Opened {}") { return @tr("Opened {}", argument); } if (key == "Could not open link: {}") { return @tr("Could not open link: {}", argument); } if (key == "This email link is too long to open.") { return @tr("This email link is too long to open."); } if (key == "The email link will open when you close this message.") { return @tr("The email link will open when you close this message."); } if (key == "The message you were writing was saved to Drafts.") { return @tr("The message you were writing was saved to Drafts."); } if (key == "Files from links are never attached for you. Attach them yourself if you trust them: {}") { return @tr("Files from links are never attached for you. Attach them yourself if you trust them: {}", argument); } if (key == "Copied HTML source") { return @tr("Copied HTML source"); } if (key == "Copied HTML source. Paste it into the issue.") { return @tr("Copied HTML source. Paste it into the issue."); } if (key == "Saved HTML source. Attach the file to the issue.") { return @tr("Saved HTML source. Attach the file to the issue."); } if (key == "Could not save HTML source: {}") { return @tr("Could not save HTML source: {}", argument); } if (key == "Copied selected text") { return @tr("Copied selected text"); } if (key == "Copied email text") { return @tr("Copied email text"); } if (key == "Copied link") { return @tr("Copied link"); } if (key == "Blitz software selection render failed: {}") { return @tr("Blitz software selection render failed: {}", argument); } if (key == "Blitz software tile render failed: {}") { return @tr("Blitz software tile render failed: {}", argument); } if (key == "Message ready.") { return @tr("Message ready."); } if (key == "System tray behavior is unavailable in this package.") { return @tr("System tray behavior is unavailable in this package."); } if (key == "Close-to-tray is active for this session.") { return @tr("Close-to-tray is active for this session."); } if (key == "Saving close behavior…") { return @tr("Saving close behavior…"); } if (key == "Closing the window will now keep Flectar Mail in the tray.") { return @tr("Closing the window will now keep Flectar Mail in the tray."); } if (key == "Closing the window will now quit Flectar Mail.") { return @tr("Closing the window will now quit Flectar Mail."); } if (key == "Closing the window will now keep Flectar Mail running in the background.") { return @tr("Closing the window will now keep Flectar Mail running in the background."); } if (key == "Your desktop did not allow Flectar Mail to run in the background.") { return @tr("Your desktop did not allow Flectar Mail to run in the background."); } if (key == "Could not save close behavior: {}") { return @tr("Could not save close behavior: {}", argument); } if (key == "Flectar Mail will start when you sign in.") { return @tr("Flectar Mail will start when you sign in."); } if (key == "Flectar Mail is now your default email app.") { return @tr("Flectar Mail is now your default email app."); } if (key == "Choose Flectar Mail for MAILTO in Windows Settings to finish.") { return @tr("Choose Flectar Mail for MAILTO in Windows Settings to finish."); } if (key == "Windows no longer offers this copy for email links.") { return @tr("Windows no longer offers this copy for email links."); } if (key == "Could not change the default email app: {}") { return @tr("Could not change the default email app: {}", argument); } if (key == "Flectar Mail will no longer start when you sign in.") { return @tr("Flectar Mail will no longer start when you sign in."); } if (key == "Could not change startup behavior: {}") { return @tr("Could not change startup behavior: {}", argument); } if (key == "Allow Flectar Mail under Login Items in System Settings to finish.") { return @tr("Allow Flectar Mail under Login Items in System Settings to finish."); } if (key == "Turn on Flectar Mail under Startup apps in Windows Settings to finish.") { return @tr("Turn on Flectar Mail under Startup apps in Windows Settings to finish."); } return ""; } ui-message-account(key, argument, second-argument, third-argument) => { if (key == "Database snapshot exported to {}.") { return @tr("Database snapshot exported to {}.", argument); } if (key == "Choose where to export the mail") { return @tr("Choose where to export the mail"); } if (key == "Exporting mail…") { return @tr("Exporting mail…"); } if (key == "Mail exported to {}.") { return @tr("Mail exported to {}.", argument); } if (key == "Mail exported to {}. Some messages could not be found on this device or the mail server.") { return @tr("Mail exported to {}. Some messages could not be found on this device or the mail server.", argument); } if (key == "Could not export mail: {}") { return @tr("Could not export mail: {}", argument); } if (key == "Could not export database snapshot: {}") { return @tr("Could not export database snapshot: {}", argument); } if (key == "Account order saved.") { return @tr("Account order saved."); } if (key == "Could not save account order: {}") { return @tr("Could not save account order: {}", argument); } if (key == "Mail history updated. Background sync is adjusting now.") { return @tr("Mail history updated. Background sync is adjusting now."); } if (key == "Account removed from this device.") { return @tr("Account removed from this device."); } if (key == "Account removed. Mail refresh will retry.") { return @tr("Account removed. Mail refresh will retry."); } if (key == "Account removed; local cleanup needs a retry: {}") { return @tr("Account removed; local cleanup needs a retry: {}", argument); } if (key == "Could not remove account: {}") { return @tr("Could not remove account: {}", argument); } if (key == "Removing account…") { return @tr("Removing account…"); } if (key == "Deleting mail on this device…") { return @tr("Deleting mail on this device…"); } if (key == "Mail on this device deleted.") { return @tr("Mail on this device deleted."); } if (key == "Mail on this device deleted. Mail refresh will retry.") { return @tr("Mail on this device deleted. Mail refresh will retry."); } if (key == "Mail on this device deleted; local cleanup needs a retry: {}") { return @tr("Mail on this device deleted; local cleanup needs a retry: {}", argument); } if (key == "Could not delete mail on this device: {}") { return @tr("Could not delete mail on this device: {}", argument); } if (key == "Synchronizing account…") { return @tr("Synchronizing account…"); } if (key == "Could not update mail history: {}") { return @tr("Could not update mail history: {}", argument); } if (key == "Connected {}. Initial sync is running in the background.") { return @tr("Connected {}. Initial sync is running in the background.", argument); } if (key == "Account setup failed: {}") { return @tr("Account setup failed: {}", argument); } if (key == "OAuth failed: {}") { return @tr("OAuth failed: {}", argument); } if (key == "Reconnected {}. Synchronization is resuming.") { return @tr("Reconnected {}. Synchronization is resuming.", argument); } if (key == "Reconnection failed: {}") { return @tr("Reconnection failed: {}", argument); } if (key == "Calendar connected and initial sync started.") { return @tr("Calendar connected and initial sync started."); } if (key == "Could not connect calendar: {}") { return @tr("Could not connect calendar: {}", argument); } if (key == "Calendar sync enabled.") { return @tr("Calendar sync enabled."); } if (key == "Calendar sync paused.") { return @tr("Calendar sync paused."); } if (key == "Could not update calendar sync: {}") { return @tr("Could not update calendar sync: {}", argument); } if (key == "CalDAV connected and initial sync started.") { return @tr("CalDAV connected and initial sync started."); } if (key == "Could not connect CalDAV: {}") { return @tr("Could not connect CalDAV: {}", argument); } if (key == "Calendar removed.") { return @tr("Calendar removed."); } if (key == "Could not remove calendar: {}") { return @tr("Could not remove calendar: {}", argument); } if (key == "CardDAV connected and initial sync started.") { return @tr("CardDAV connected and initial sync started."); } if (key == "Could not connect CardDAV: {}") { return @tr("Could not connect CardDAV: {}", argument); } if (key == "CardDAV sync enabled.") { return @tr("CardDAV sync enabled."); } if (key == "CardDAV sync paused.") { return @tr("CardDAV sync paused."); } if (key == "Could not update CardDAV sync: {}") { return @tr("Could not update CardDAV sync: {}", argument); } if (key == "CardDAV disconnected.") { return @tr("CardDAV disconnected."); } if (key == "Could not disconnect CardDAV: {}") { return @tr("Could not disconnect CardDAV: {}", argument); } return ""; } ui-message-data(key, argument, second-argument, third-argument) => { if (key == "Connect an account before creating a backup.") { return @tr("Connect an account before creating a backup."); } if (key == "Export Flectar Mail account backup") { return @tr("Export Flectar Mail account backup"); } if (key == "Flectar Mail backup") { return @tr("Flectar Mail backup"); } if (key == "Import accounts and mail") { return @tr("Import accounts and mail"); } if (key == "Choose a Thunderbird profile or a folder of mail") { return @tr("Choose a Thunderbird profile or a folder of mail"); } if (key == "Imported messages") { return @tr("Imported messages"); } if (key == "Choose where to export the database snapshot") { return @tr("Choose where to export the database snapshot"); } if (key == "Backup exported. Passwords and OAuth tokens were not included.") { return @tr("Backup exported. Passwords and OAuth tokens were not included."); } if (key == "Could not export backup: {}") { return @tr("Could not export backup: {}", argument); } if (key == "Local data storage is unavailable. Retry startup.") { return @tr("Local data storage is unavailable. Retry startup."); } if (key == "Creating verified database snapshot…") { return @tr("Creating verified database snapshot…"); } if (key == "All local Flectar Mail data was deleted.") { return @tr("All local Flectar Mail data was deleted."); } if (key == "Could not delete all local data: {}") { return @tr("Could not delete all local data: {}", argument); } if (key == "Deleting local accounts and cached data…") { return @tr("Deleting local accounts and cached data…"); } return ""; } ui-message-list(key, argument, second-argument, third-argument) => { if (key == "Background mail refresh failed: {}") { return @tr("Background mail refresh failed: {}", argument); } if (key == "No conversations in this folder") { return @tr("No conversations in this folder"); } if (key == "No conversations match this search") { return @tr("No conversations match this search"); } if (key == "Mail core is ready for account synchronization and message actions.") { return @tr("Mail core is ready for account synchronization and message actions."); } if (key == "Large message rendered in compatibility mode.") { return @tr("Large message rendered in compatibility mode."); } if (key == "Software email render failed: {}") { return @tr("Software email render failed: {}", argument); } if (key == "Rendered with software fallback after GPU error: {}") { return @tr("Rendered with software fallback after GPU error: {}", argument); } if (key == "Blitz render failed (GPU: {}; software: {})") { return @tr("Blitz render failed (GPU: {}; software: {})", argument, second-argument); } return ""; } ui-message-contacts(key, argument, second-argument, third-argument) => { if (key == "Enter a name, email address, or phone number, then save.") { return @tr("Enter a name, email address, or phone number, then save."); } if (key == "Could not save: contact storage is unavailable.") { return @tr("Could not save: contact storage is unavailable."); } if (key == "Could not save contact: {}") { return @tr("Could not save contact: {}", argument); } if (key == "Contact saved.") { return @tr("Contact saved."); } if (key == "Saved to contacts.") { return @tr("Saved to contacts."); } if (key == "Could not delete: contact storage is unavailable.") { return @tr("Could not delete: contact storage is unavailable."); } if (key == "Could not delete contact: {}") { return @tr("Could not delete contact: {}", argument); } if (key == "Contact deleted.") { return @tr("Contact deleted."); } if (key == "Could not load contacts: {}") { return @tr("Could not load contacts: {}", argument); } if (key == "Local contact storage is still starting…") { return @tr("Local contact storage is still starting…"); } if (key == "Loading contacts…") { return @tr("Loading contacts…"); } return ""; } ui-message-calendar(key, argument, second-argument, third-argument) => { if (key == "Local calendar") { return @tr("Local calendar"); } if (key == "Calendar") { return @tr("Calendar"); } if (key == "Could not load calendar: {}") { return @tr("Could not load calendar: {}", argument); } if (key == "Calendar shown and syncing.") { return @tr("Calendar shown and syncing."); } if (key == "Calendar hidden and sync paused.") { return @tr("Calendar hidden and sync paused."); } if (key == "Calendar update failed: {}") { return @tr("Calendar update failed: {}", argument); } if (key == "Only events created in Flectar Mail can be edited.") { return @tr("Only events created in Flectar Mail can be edited."); } if (key == "Only events created in Flectar Mail can be deleted.") { return @tr("Only events created in Flectar Mail can be deleted."); } if (key == "Calendar storage is unavailable.") { return @tr("Calendar storage is unavailable."); } if (key == "Could not delete event: {}") { return @tr("Could not delete event: {}", argument); } if (key == "Event deleted, but refresh failed: {}") { return @tr("Event deleted, but refresh failed: {}", argument); } if (key == "Event deleted.") { return @tr("Event deleted."); } if (key == "Use YYYY-MM-DD for the date and a time such as {}.") { return @tr("Use YYYY-MM-DD for the date and a time such as {}.", argument); } if (key == "Event duration must be a number of minutes.") { return @tr("Event duration must be a number of minutes."); } if (key == "Add a title and choose a duration from 15 to 1440 minutes.") { return @tr("Add a title and choose a duration from 15 to 1440 minutes."); } if (key == "That local time does not exist in the current time zone.") { return @tr("That local time does not exist in the current time zone."); } if (key == "Could not save event: {}") { return @tr("Could not save event: {}", argument); } if (key == "Event saved, but refresh failed: {}") { return @tr("Event saved, but refresh failed: {}", argument); } if (key == "Event saved. Provider sync will run when enabled.") { return @tr("Event saved. Provider sync will run when enabled."); } return ""; } ui-message-startup(key, argument, second-argument, third-argument) => { if (key == "Email resource render failed: {}") { return @tr("Email resource render failed: {}", argument); } if (key == "Message refresh failed: {}") { return @tr("Message refresh failed: {}", argument); } if (key == "Message load failed: {}") { return @tr("Message load failed: {}", argument); } if (key == "Your local mailbox could not be opened. No account operation was started. Retry, or copy the diagnostics for support.") { return @tr("Your local mailbox could not be opened. No account operation was started. Retry, or copy the diagnostics for support."); } if (key == "Local storage startup failed.") { return @tr("Local storage startup failed."); } if (key == "Loading local data…") { return @tr("Loading local data…"); } if (key == "Local data ready.") { return @tr("Local data ready."); } return ""; } ui-message-sync(key, argument, second-argument, third-argument) => { if (key == "Could not identify the dragged account.") { return @tr("Could not identify the dragged account."); } if (key == "Account ordering requires local mail data.") { return @tr("Account ordering requires local mail data."); } if (key == "Could not find the account to reorder.") { return @tr("Could not find the account to reorder."); } if (key == "Saving account order…") { return @tr("Saving account order…"); } if (key == "Sent copy settings require local mail data.") { return @tr("Sent copy settings require local mail data."); } if (key == "Updating Sent copy settings…") { return @tr("Updating Sent copy settings…"); } if (key == "Sent copy settings updated.") { return @tr("Sent copy settings updated."); } if (key == "Could not update Sent copy settings: {}") { return @tr("Could not update Sent copy settings: {}", argument); } if (key == "Server mail settings require local mail data.") { return @tr("Server mail settings require local mail data."); } if (key == "Updating server mail settings…") { return @tr("Updating server mail settings…"); } if (key == "Server mail settings updated.") { return @tr("Server mail settings updated."); } if (key == "Could not update server mail settings: {}") { return @tr("Could not update server mail settings: {}", argument); } if (key == "Mail history requires local mail data.") { return @tr("Mail history requires local mail data."); } if (key == "Updating mail history…") { return @tr("Updating mail history…"); } if (key == "Remote images are unavailable in this minimal build.") { return @tr("Remote images are unavailable in this minimal build."); } if (key == "Could not save remote-image setting: {}") { return @tr("Could not save remote-image setting: {}", argument); } if (key == "Remote sender icons unavailable: {}") { return @tr("Remote sender icons unavailable: {}", argument); } if (key == "Remote images unavailable: {}") { return @tr("Remote images unavailable: {}", argument); } if (key == "Remote body images and sender favicons enabled.") { return @tr("Remote body images and sender favicons enabled."); } if (key == "Remote images blocked.") { return @tr("Remote images blocked."); } if (key == "Could not refresh message images: {}") { return @tr("Could not refresh message images: {}", argument); } if (key == "Remote-image loading is disabled.") { return @tr("Remote-image loading is disabled."); } if (key == "Could not load message images: {}") { return @tr("Could not load message images: {}", argument); } if (key == "Sync finished, refresh failed: {}") { return @tr("Sync finished, refresh failed: {}", argument); } if (key == "Sync complete.") { return @tr("Sync complete."); } if (key == "Sync failed: {}") { return @tr("Sync failed: {}", argument); } if (key == "Local mail data is unavailable; preview data is read-only.") { return @tr("Local mail data is unavailable; preview data is read-only."); } if (key == "Synchronizing accounts…") { return @tr("Synchronizing accounts…"); } return ""; } ui-message-actions(key, argument, second-argument, third-argument) => { if (key == "Message action completed.") { return @tr("Message action completed."); } if (key == "Permanent deletion queued. Mail will disappear after the server confirms.") { return @tr("Permanent deletion queued. Mail will disappear after the server confirms."); } if (key == "Message action failed: {}") { return @tr("Message action failed: {}", argument); } if (key == "Message action failed: message is no longer available") { return @tr("Message action failed: message is no longer available"); } if (key == "Label added.") { return @tr("Label added."); } if (key == "Label removed.") { return @tr("Label removed."); } if (key == "Label created and added.") { return @tr("Label created and added."); } if (key == "Label created.") { return @tr("Label created."); } if (key == "Label updated.") { return @tr("Label updated."); } if (key == "Label deleted.") { return @tr("Label deleted."); } if (key == "No other folder to move this mail to.") { return @tr("No other folder to move this mail to."); } if (key == "Could not move mail: {}") { return @tr("Could not move mail: {}", argument); } if (key == "No folder to copy this mail to.") { return @tr("No folder to copy this mail to."); } if (key == "Attaching the original's files…") { return @tr("Attaching the original's files…"); } if (key == "Could not attach the original's files: {}") { return @tr("Could not attach the original's files: {}", argument); } if (key == "Some of the original's files are over the 25 MB limit for attachments.") { return @tr("Some of the original's files are over the 25 MB limit for attachments."); } if (key == "Could not copy mail: {}") { return @tr("Could not copy mail: {}", argument); } if (key == "Moving mail to the other account…") { return @tr("Moving mail to the other account…"); } if (key == "Copying mail to the other account…") { return @tr("Copying mail to the other account…"); } if (key == "Moved {} messages to the other account.") { return @tr("Moved one message to the other account." | "Moved {0} messages to the other account." % round(argument.to-float()), argument); } if (key == "Copied {} messages to the other account.") { return @tr("Copied one message to the other account." | "Copied {0} messages to the other account." % round(argument.to-float()), argument); } if (key == "Moved or copied {} messages to the other account.") { return @tr("Moved or copied one message to the other account." | "Moved or copied {0} messages to the other account." % round(argument.to-float()), argument); } if (key == "The other account had {} messages already.") { return @tr("The other account had that message already." | "The other account had those {0} messages already." % round(argument.to-float()), argument); } if (key == "Could not move or copy {} messages: {}") { return @tr("Could not move or copy one message: {1}" | "Could not move or copy {0} messages: {1}" % round(argument.to-float()), argument, second-argument); } if (key == "Could not update label: {}") { return @tr("Could not update label: {}", argument); } if (key == "Could not save label: {}") { return @tr("Could not save label: {}", argument); } if (key == "Could not delete label: {}") { return @tr("Could not delete label: {}", argument); } if (key == "Folder created.") { return @tr("Folder created."); } if (key == "Folder renamed.") { return @tr("Folder renamed."); } if (key == "Folder deleted.") { return @tr("Folder deleted."); } if (key == "Could not create folder: {}") { return @tr("Could not create folder: {}", argument); } if (key == "Could not rename folder: {}") { return @tr("Could not rename folder: {}", argument); } if (key == "Could not delete folder: {}") { return @tr("Could not delete folder: {}", argument); } if (key == "Could not close preview: {}") { return @tr("Could not close preview: {}", argument); } if (key == "Opened the system print view.") { return @tr("Opened the system print view."); } if (key == "Could not print message: {}") { return @tr("Could not print message: {}", argument); } if (key == "Opened the message in your browser.") { return @tr("Opened the message in your browser."); } if (key == "Could not open message in browser: {}") { return @tr("Could not open message in browser: {}", argument); } if (key == "Connect an account before replying or forwarding.") { return @tr("Connect an account before replying or forwarding."); } if (key == "Choose a message first.") { return @tr("Choose a message first."); } if (key == "Only drafts can be reopened for editing.") { return @tr("Only drafts can be reopened for editing."); } if (key == "Could not open draft: {}") { return @tr("Could not open draft: {}", argument); } if (key == "Could not open draft: invalid account identifier") { return @tr("Could not open draft: invalid account identifier"); } if (key == "Connected account") { return @tr("Connected account"); } if (key == "Could not open composer: {}") { return @tr("Could not open composer: {}", argument); } if (key == "Add an account to send mail.") { return @tr("Add an account to send mail."); } if (key == "On this device") { return @tr("On this device"); } if (key == "Could not open composer: invalid account identifier") { return @tr("Could not open composer: invalid account identifier"); } return ""; } ui-message-compose(key, argument, second-argument, third-argument) => { // Reply and forward quotation headers, written into the draft. if (key == "On {}, {} wrote:") { return @tr("On {}, {} wrote:", argument, second-argument); } if (key == "{} wrote:") { return @tr("{} wrote:", argument); } if (key == "Forwarded message") { return @tr("Forwarded message"); } if (key == "From: {}") { return @tr("forwarded message header" => "From: {}", argument); } if (key == "Date: {}") { return @tr("forwarded message header" => "Date: {}", argument); } if (key == "Subject: {}") { return @tr("forwarded message header" => "Subject: {}", argument); } if (key == "To: {}") { return @tr("forwarded message header" => "To: {}", argument); } if (key == "Attach files to this email") { return @tr("Attach files to this email"); } if (key == "Could not add attachments: {}") { return @tr("Could not add attachments: {}", argument); } if (key == "Could not show the quoted message: {}") { return @tr("Could not show the quoted message: {}", argument); } if (key == "Added 1 attachment.") { return @tr("Added 1 attachment."); } if (key == "Added {} attachments.") { return @tr("Added {} attachments.", argument); } if (key == "Attachments added: {}. Skipped: {} (duplicate, unreadable, or over the 25 MB total limit).") { return @tr("Attachments added: {}. Skipped: {} (duplicate, unreadable, or over the 25 MB total limit).", argument, second-argument); } if (key == "Attachment removed.") { return @tr("Attachment removed."); } if (key == "Choose the account this message should be sent from.") { return @tr("Choose the account this message should be sent from."); } if (key == "Choose a verified sender identity for this message.") { return @tr("Choose a verified sender identity for this message."); } if (key == "Connect an account before saving or sending mail.") { return @tr("Connect an account before saving or sending mail."); } if (key == "Message queued for delivery (action {}).") { return @tr("Message queued for delivery (action {}).", argument); } if (key == "Sending message…") { return @tr("Sending message…"); } if (key == "Message sent.") { return @tr("Message sent."); } if (key == "Message was not sent: {} Your draft is preserved.") { return @tr("Message was not sent: {} Your draft is preserved.", argument); } if (key == "Draft saved locally (#{}).") { return @tr("Draft saved locally (#{}).", argument); } if (key == "Compose failed: {}") { return @tr("Compose failed: {}", argument); } if (key == "Connect an account before managing email templates.") { return @tr("Connect an account before managing email templates."); } if (key == "Could not load email templates: {}") { return @tr("Could not load email templates: {}", argument); } if (key == "Enter a template name.") { return @tr("Enter a template name."); } if (key == "Add a subject or message to the template.") { return @tr("Add a subject or message to the template."); } if (key == "Email template saved.") { return @tr("Email template saved."); } if (key == "Could not save email template: {}") { return @tr("Could not save email template: {}", argument); } if (key == "Email template deleted.") { return @tr("Email template deleted."); } if (key == "Could not delete email template: {}") { return @tr("Could not delete email template: {}", argument); } if (key == "Email template is no longer available.") { return @tr("Email template is no longer available."); } if (key == "Email template inserted.") { return @tr("Email template inserted."); } if (key == "Email template inserted, but its usage could not be saved: {}") { return @tr("Email template inserted, but its usage could not be saved: {}", argument); } if (key == "Choose a supported account type and connection security.") { return @tr("Choose a supported account type and connection security."); } if (key == "IMAP port must be between 1 and 65535.") { return @tr("IMAP port must be between 1 and 65535."); } if (key == "POP3 port must be between 1 and 65535.") { return @tr("POP3 port must be between 1 and 65535."); } if (key == "SMTP port must be between 1 and 65535.") { return @tr("SMTP port must be between 1 and 65535."); } if (key == "Local mail data is unavailable. Retry startup.") { return @tr("Local mail data is unavailable. Retry startup."); } if (key == "Discovering JMAP and checking capabilities…") { return @tr("Discovering JMAP and checking capabilities…"); } if (key == "Certificate imported. Connect to verify the server.") { return @tr("Certificate imported. Connect to verify the server."); } if (key == "Could not load account preferences: {}") { return @tr("Could not load account preferences: {}", argument); } if (key == "Your edited signature was preserved. Choose No signature to keep it as message text before inserting another.") { return @tr("Your edited signature was preserved. Choose No signature to keep it as message text before inserting another."); } if (key == "Certificate import failed: {}") { return @tr("Certificate import failed: {}", argument); } if (key == "Testing IMAP and SMTP connections…") { return @tr("Testing IMAP and SMTP connections…"); } if (key == "Testing POP3 and SMTP connections…") { return @tr("Testing POP3 and SMTP connections…"); } return ""; } ui-message-oauth(key, argument, second-argument, third-argument) => { if (key == "Another browser authorization is already in progress; finish it or choose Cancel.") { return @tr("Another browser authorization is already in progress; finish it or choose Cancel."); } if (key == "Add app keys in Sign-in settings to enable this provider.") { return @tr("Add app keys in Sign-in settings to enable this provider."); } if (key == "Choose Gmail or Outlook OAuth.") { return @tr("Choose Gmail or Outlook OAuth."); } if (key == "Complete sign-in in your browser, or choose Cancel here.") { return @tr("Complete sign-in in your browser, or choose Cancel here."); } if (key == "Cancelling browser sign-in…") { return @tr("Cancelling browser sign-in…"); } if (key == "Finishing browser sign-in…") { return @tr("Finishing browser sign-in…"); } if (key == "Could not use that browser address: {}") { return @tr("Could not use that browser address: {}", argument); } if (key == "Calendar core is unavailable.") { return @tr("Calendar core is unavailable."); } if (key == "Account no longer exists.") { return @tr("Account no longer exists."); } if (key == "Authorize calendar access in your browser.") { return @tr("Authorize calendar access in your browser."); } if (key == "OAuth app keys saved.") { return @tr("OAuth app keys saved."); } if (key == "{} OAuth settings saved.") { return @tr("{} OAuth settings saved.", argument); } if (key == "OAuth settings failed: {}") { return @tr("OAuth settings failed: {}", argument); } if (key == "Mail refresh failed: {}") { return @tr("Mail refresh failed: {}", argument); } if (key == "Mail filter failed: {}") { return @tr("Mail filter failed: {}", argument); } if (key == "Mail pagination failed: {}") { return @tr("Mail pagination failed: {}", argument); } if (key == "Could not mark message as read: {}") { return @tr("Could not mark message as read: {}", argument); } if (key == "Loading message…") { return @tr("Loading message…"); } return ""; } } // Everything a composer window copies from the main window's look. struct Appearance { mode: string, preset: string, icon: AppIconVariant, light_primary: color, light_page: color, light_surface: color, light_text: color, light_border: color, dark_primary: color, dark_page: color, dark_surface: color, dark_text: color, dark_border: color, } // A message moved into its own window on the desktop. Each window has its // own ComposerUi, so several messages can be open at once while the main // window stays free to read mail or start another. export component ComposerWindow inherits Window { // "light", "dark" or "system": the main window's theme-mode. in property theme: "system"; in property attachment-picker-supported: true; // The window's close button asks the composer, which protects a // message with content behind its discard confirmation. callback request-close(); // Minimized: the composer keeps no pixels until it is shown again. in-out property render-suspended: false; title: ComposerUi.subject != "" ? ComposerUi.subject : ComposerText.mode-title(ComposerUi.mode); icon: AppIcon.window-image(AppIcon.variant); default-font-family: "Google Sans Flex"; preferred-width: 760px; preferred-height: 720px; min-width: 420px; min-height: 480px; background: AppTheme.surface; init => { root.apply-theme(); } changed theme => { root.apply-theme(); } request-close => { composer.back-requested(); } function apply-theme() { Palette.color-scheme = root.theme == "dark" ? ColorScheme.dark : root.theme == "light" ? ColorScheme.light : ColorScheme.unknown; } composer := BoundComposer { width: parent.width; height: parent.height; windowed: true; attachment-picker-supported: root.attachment-picker-supported; notice: I18n.ui-message(ComposerUi.message.key, ComposerUi.message.argument, ComposerUi.message.second_argument, ComposerUi.message.third_argument); } } export component AppWindow inherits Window { title: @tr("Flectar Mail"); icon: AppIcon.window-image(AppIcon.variant); // Windows keeps its native frame and macOS its traffic lights in the // integrated frame; only Linux removes the window manager's decorations. no-frame: WindowChrome.integrated && WindowChrome.platform == "linux"; resize-border-width: root.client-frame ? 6px : 0px; default-font-family: "Google Sans Flex"; preferred-width: 1320px; preferred-height: 800px; min-width: 320px; min-height: 320px; background: root.page-bg; callback select_email(int); callback select_thread_message(int); callback search_changed(string); callback set_search_filter(string); callback select_scope(string); callback toggle_folder(int, bool); callback toggle_sidebar_section(string, bool); callback filter_folders(string); callback create_folder(int, int, string); callback rename_folder(int, string); callback delete_folder(int); callback load_more(); callback toggle_settings(); callback sync_mail(); callback sync_account(int); // F5: the accounts whose mail the view shows. callback check_mail_in_view(); callback delete_account(int); callback message_action(string); callback message_action_for_email(int, string); callback bulk_message_action(string); callback toggle_mail_group(string); callback empty_trash(int); callback set_email_checked(int, bool); callback set_all_emails_checked(bool); callback toggle_mail_label(int, bool); callback save_mail_label(int, int, string, color, bool, int); callback delete_mail_label(int); callback search_mail_labels(string); callback compose_message_action(string); callback close_preview(); callback print_message(); callback open_message_in_browser(); callback save_theme(string); callback save_theme_preset(string); callback save_custom_theme(color, color, color, color, color, color, color, color, color, color) -> bool; callback save_avatar_visibility(bool); callback save_show_tabs(bool); callback save_conversation_view(bool); callback save_group_mail_by_date(bool); callback choose_download_directory(); callback clear_download_directory(); // Save a profile's name and color, with the checked `profile_members` // as its accounts. callback save_mail_profile(string, string, color) -> string; callback prepare_profile_members(string, int); callback assign_account_profile(int, string); callback save_account_color(int, color, bool); callback delete_mail_profile(string); // Show only this profile's accounts in the mail view; empty shows all. callback select_mail_profile(string); // Rust: the active profile or its accounts changed. callback mail_profile_changed(); callback save_account_markers_visibility(bool); callback save_workspace_layout(string); callback save_workspace_list_pane_width(length); callback save_language(string); callback save_time_format(string); callback save_sidebar_icon_style(bool); callback save_notification_settings(bool, bool, string); callback save_unread_badge(bool); callback save_sync_interval(int); callback save_mark_read_on_open(bool); callback save_contact_discovery_settings(bool, bool, bool, bool); callback clear_contact_suggestions() -> bool; callback set_close_to_tray(bool); callback set_launch_at_login(bool); // A new message, to the recipients given, if any. callback open_compose(string); callback add_password_account(string, string, string, string, string, string, string, string, string); callback suggest_account_settings(string); callback start_oauth(string); callback reauth_account(int); callback cancel_oauth(); callback save_oauth_app(string, string, string); callback save_oauth_apps(string, string, string); pure callback make_account_transfer(string) -> data-transfer; pure callback read_account_transfer(data-transfer) -> string; pure callback settings-search-matches(string, string) -> bool; callback reorder_account(string, int, bool); callback save_account_mail_history(int, string); callback save_account_sent_copy_policy(int, string); callback save_account_keep_on_server(int, string); callback connect_account_calendar(int); // (account, connection or -1 for a new one, url, username, password) callback connect_caldav(int, int, string, string, string); callback set_calendar_connection_enabled(int, bool); callback remove_calendar_connection(int); callback connect_carddav(int, string, string, string); callback set_account_carddav_enabled(int, bool); callback disconnect_carddav(int); callback export_account_backup(); callback import_accounts(); callback export_database_snapshot(); callback export_device_mail(int); callback delete_all_data(); callback load_storage_usage(); callback cancel_storage_usage(); callback open_email_link(string); callback copy_email_status(string); callback report_rendering_issue(); callback email-pointer-event(float, float, string, bool, bool, bool, bool); callback email-scroll(float, float); callback email-key-event(string, bool, bool, bool, bool, bool, bool); callback select-email-text(); callback set-remote-images(bool); callback load-remote-images-for-message(); callback calendar-navigate(string, int); callback calendar-select-date(string); callback calendar-set-view(string); callback calendar-set-source-enabled(int, bool); callback calendar-toggle-agenda-filter(int, bool); callback calendar-clear-agenda-filters(); callback calendar-today(); callback calendar-open-editor(); callback calendar-edit-event(int); callback calendar-delete-event(int); callback calendar-save-event(string, string, string, string, int); callback select-contact(int); callback search-contacts(string); callback select-contact-scope(string); callback toggle-contact-section(string, bool); callback new-contact(); callback resolve-contact-conflict(bool); pure callback usable-contact-email(string) -> bool; usable-contact-email(email) => { false } callback add-contact-value(string); callback prefer-contact-value(string,int); callback remove-contact-value(string,int); callback save-contact(int, string, string, string, string, string, string, string, string, bool); callback delete-contact(int); callback load-contacts(); callback load-more-contacts(); callback system-back-requested() -> bool; callback retry-startup(); // Host-internal wakeups. Background work only schedules these when data is // ready; Rust drains the corresponding bounded channel on Slint's thread. callback drain-startup-updates(); callback drain-core-updates(); callback drain-mail-list-updates(); callback drain-mail-metadata-updates(); callback drain-folder-updates(); callback drain-message-load-updates(); callback drain-read-updates(); callback drain-navigation-updates(); callback drain-action-updates(); callback drain-favicon-updates(); callback drain-profile-avatar-updates(); callback drain-ui-task-updates(); callback drain-sync-updates(); callback drain-data-reset-updates(); callback drain-contact-load-updates(); callback drain-compose-send-updates(); // A quoted message in a composer finished loading an image. callback refresh-composer-quotes(); callback refresh-email-resources(); // Calendar connections changed state while Settings shows them. callback reload-calendar-connections(); // The language or the time format changed how dates are written. callback date-formats-changed(); in property <[EmailRow]> emails: []; in property <[MailListEntry]> mail_list_entries: []; in property <[ThreadMessageRow]> thread_messages: []; in property selected_thread_index: 0; in property <[SidebarRow]> sidebar_rows: []; in-out property folder_filter_query: ""; in property <[MailLabelRow]> mail_labels: []; in property <[MailLabelRow]> mail_label_results: []; in property selected_mail_label_count: 0; in property selected_account_uses_labels: false; in property <[AccountRow]> connected_accounts: []; in property has_device_mail: false; in property mail_export_running: false; in property mail-export-message; in property <[MailProfileRow]> mail_profiles: []; in property <[ProfileMemberRow]> profile_members: []; // The profile the mail view shows; an empty id means every account. in property active_mail_profile; // Set to open Settings' profile editor for a new profile. in-out property new_profile_requested: false; // The saved visible page can paint before the core is safe to use. Keep // that presentation milestone separate from startup-ready, which guards // every callback that can touch local storage. in property startup_hydrated: false; private property compose-back-request: 0; in property render-suspended: false; callback suspend-rendering(bool); in property startup_ready: false; in property startup_failed: false; in property startup-message; property startup_error: I18n.ui-message(root.startup-message.key, root.startup-message.argument, root.startup-message.second_argument, root.startup-message.third_argument); in property startup_diagnostic_id: ""; in property diagnostics_text: ""; in property <[CalendarDay]> calendar_month_days: []; in property <[CalendarWeekDay]> calendar_week_days: []; in property <[CalendarEventRow]> calendar_events: []; in property <[CalendarAgendaRow]> calendar_agenda_rows: []; in property <[CalendarAgendaFilterRow]> calendar_agenda_filters: []; in property calendar_agenda_filter_count: 0; in property calendar_agenda_event_count: 0; in property calendar_agenda_anchor: 0; in property calendar_agenda_scroll_request: 0; in property calendar_timezone: ""; in property <[CalendarSourceRow]> calendar_sources: []; in property <[ContactRow]> contacts: []; in-out property <[ContactValueRow]> contact-emails; in-out property <[ContactValueRow]> contact-phones; in property contact-read-only; in property contact-source; in property contact-sync; in property contact-sync-detail; in property contact-photo; in property contact-has-photo; in property <[ContactSidebarRow]> contact_sidebar_rows: []; in-out property contact_scope: "All contacts"; in-out property contact_search_query: ""; in property contact_can_load_more: false; in property contact_loading_more: false; in property contact_list_revision: 0; in property contact-save-message; property contact_save_status: I18n.ui-message(root.contact-save-message.key, root.contact-save-message.argument, root.contact-save-message.second_argument, root.contact-save-message.third_argument); in property contact_has_selection: false; in-out property contact_id: -1; in-out property contact_name: ""; in-out property contact_company: ""; in-out property contact_job_title: ""; in-out property contact_website: ""; in-out property contact_birthday: ""; in-out property contact_address: ""; in-out property contact_notes: ""; in-out property contact_tags: ""; in-out property contact_favorite: false; in property contact_is_suggested: false; in property contact_initials: "?"; in property contact_interactions: ""; in property contact_last_interacted: ""; in property contact_total_count: 0; in property contact_favorite_count: 0; in property contact_suggestion_count: 0; in property calendar_month_title: ""; in property calendar_period_title: ""; in property calendar_week_label: ""; in property calendar_selected_date: ""; in property calendar_selected_date_label: ""; in property <[string]> calendar_hour_labels: ["08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00"]; in property calendar_selected_date_has_events: false; in-out property calendar_sidebar_collapsed: false; in-out property calendar_view_mode: "week"; in-out property calendar_editor_open: false; in-out property calendar_event_editing: false; in-out property calendar_event_title: ""; in-out property calendar_event_date: ""; in-out property calendar_event_time: "09:00"; in-out property calendar_event_duration: "60"; in-out property calendar_event_color: 0; in property selected_scope: "Unified Inbox"; in property selected_scope_title: "Unified Inbox"; in property trash_view: false; in-out property mail_sidebar_collapsed: false; in-out property contacts_sidebar_collapsed: false; in property unified_count: ""; in-out property search_query: ""; in property search_filter: "All mail"; in property selected_sender: ""; in property selected_address: ""; in property selected_subject: ""; in property selected_time: ""; in property selected_to: ""; in property selected_label: ""; in property selected_is_draft: false; in property selected_initials: "?"; in property selected_favicon; in property selected_has_favicon: false; in property render-message; property render_status: I18n.ui-message(root.render-message.key, root.render-message.argument, root.render-message.second_argument, root.render-message.third_argument); in property selected_plain_text: ""; in property selected_source: ""; in property selected_text: ""; in property has_selection: false; in property clipboard_request: ""; in property clipboard_request_id: 0; in property sync-message; property sync_status: I18n.ui-message(root.sync-message.key, root.sync-message.argument, root.sync-message.second_argument, root.sync-message.third_argument); in property sync_in_progress: false; in property <[EmailTile]> email_tiles: []; in property <[EmailLink]> email_links: []; // The renderer reports the total laid-out content height while retaining // only viewport-adjacent raster tiles. in-out property email-content-aspect: 900 / 520; in-out property source_mode: false; in-out property rendering_info_open: false; in property can_load_more: false; in property mail_loading_more: false; in property mail_page_loading: false; in property mail_list_revision: 0; in property mail_selection_count: 0; in property has_selected: true; in property previous_email_id: -1; in property next_email_id: -1; in property selected_starred: false; in property selected_unread: false; in property selected_sender_verification: ""; in-out property text_mode: false; // The Rust renderer reads these bindings during Slint's WGPU rendering // callback and reflows Blitz at the current logical viewport size. in-out property email-viewport-width: 0px; in-out property email-viewport-height: 0px; in-out property email-scroll-document-height: 0px; in-out property email-scroll-y: 0px; in-out property settings_open: false; changed settings_open => { root.product-changed(root.active_view); // Hiding Settings destroys the dialog before its own change handlers // run, so an unsaved palette and open forms are discarded here. The // dialog's private state starts over when it is created again. if root.settings_open { root.settings-shown(); } else { ThemeDraft.revert(); root.account_form_open = false; root.account_password = ""; root.caldav_account_id = -1; root.caldav_connection_id = -1; root.caldav_password = ""; root.carddav_account_id = -1; root.carddav_password = ""; root.carddav_manage_existing = false; } TabsUi.navigated(); } // Settings appeared, whichever way it was opened. callback settings-shown(); in-out property active_view: "mail"; callback product-changed(string); private property mail-compact-page: "list"; private property mail-folders-open: false; private property contacts-detail-visible: false; // A tab shown in a single-pane layout opens on its list or its reader. private property tab-shown: TabsUi.shown; changed tab-shown => { root.mail-compact-page = TabsUi.shown-conversation ? "message" : "list"; root.mail-folders-open = false; root.contacts-detail-visible = false; } changed active_view => { if root.active_view != "mail" { MailAttachments.command("close", ""); } root.product-changed(root.active_view); TabsUi.navigated(); } in-out property settings_tab: "General"; changed settings_tab => { TabsUi.navigated(); } // The active tab saves where the mail view is once it settles. changed selected_scope => { TabsUi.navigated(); } changed search_query => { TabsUi.navigated(); } changed search_filter => { TabsUi.navigated(); } changed has_selected => { TabsUi.navigated(); } changed selected_subject => { TabsUi.navigated(); } changed calendar_selected_date => { TabsUi.navigated(); } changed calendar_view_mode => { TabsUi.navigated(); } // Own scan lifetime here: the conditional SettingsDialog is destroyed on // close, so its own change handlers cannot reliably cancel a running scan. private property storage-page-open: root.settings_open && root.settings_tab == "Data"; changed storage-page-open => { if root.storage-page-open { root.load_storage_usage(); } else { root.cancel_storage_usage(); } } in property storage_loading: false; in property storage_loaded: false; in property storage_total: ""; in property storage_error: ""; in property <[StorageUsageRow]> storage_usage: []; in-out property theme_mode: "system"; in-out property show_avatars: true; // Show tabs: the desktop tab strip, or the compact tab switcher. in-out property show_tabs: true; in-out property group_mail_by_date: true; // Group replies into conversations; off lists each message on its own. in-out property conversation_view: true; in-out property show_account_markers: false; in-out property workspace_layout: "default"; in-out property workspace_list_pane_width: LayoutMetrics.list-pane-width; in-out property language_mode: "system"; // "automatic" follows the regional clock; "12h" and "24h" fix it. in-out property time_format: "automatic"; in property time_format_sample: "13:30"; in-out property monochrome_sidebar_icons: false; in-out property notifications_enabled: true; in-out property notification_sound_enabled: true; in-out property unread_badge_enabled: true; in-out property notification_scope: "important"; in-out property sync_interval_minutes: 5; in-out property mark_read_on_open: true; in-out property collect_outgoing_contacts: true; in-out property collect_incoming_contacts: false; in-out property contact_suggest_all_accounts: false; in-out property suggest_learned_contacts: true; in-out property close_to_tray: false; in-out property launch_at_login: false; in property launch_at_login_available: false; // False where closing to the background shows no tray icon (the Flatpak). in property tray_supported: true; in-out property account_setup_in_progress: false; in-out property account_form_open: false; in-out property oauth_in_progress: false; in property remote-images-available: true; in-out property remote-images-enabled: false; in property remote-images-blocked: false; // The account a new message is written from: one the mail view shows. in-out property compose_account_id: -1; in-out property compose_from_label: @tr("No connected account"); in-out property account_email: ""; in-out property account_username: ""; in-out property account_password: ""; in-out property account_protocol: "imap"; in-out property jmap_url: ""; in-out property connect_calendar_on_add: true; in-out property caldav_account_id: -1; in-out property caldav_connection_id: -1; in-out property caldav_url: ""; in-out property caldav_username: ""; in-out property caldav_password: ""; in property caldav_connecting: false; in-out property carddav_account_id: -1; in-out property carddav_url: ""; in-out property carddav_username: ""; in-out property carddav_password: ""; in-out property carddav_manage_existing: false; in-out property incoming_security: "tls"; in-out property smtp_security: "tls"; in-out property trusted_certificate_pem; in property oauth_authorization_url; in property oauth_browser_error; callback retry_oauth_browser(); callback copy_oauth_link(); callback submit_oauth_redirect(string); callback copy_oauth_authorization_url(); copy_oauth_authorization_url => { if root.oauth_in_progress && root.oauth_authorization_url != "" { oauth-clipboard.text = root.oauth_authorization_url; oauth-clipboard.select-all(); oauth-clipboard.copy(); } } oauth-clipboard := TextInput { x: -10000px; y: -10000px; width: 1px; height: 1px; visible: false; enabled: false; read-only: true; accessible-role: none; } callback import_mail_certificate(); in-out property incoming_host: ""; in-out property incoming_port: "993"; in-out property smtp_host: ""; in-out property smtp_port: "465"; in-out property google_client_id: ""; in-out property google_client_secret: ""; in-out property ms_client_id: ""; // The download folder as shown: its host path inside Flatpak. Empty is the default. in-out property download_directory: ""; // Only desktop hosts pick a filesystem directory; mobile hosts export // through their own document provider and hide the setting. in-out property download_directory_available: false; in-out property custom_oauth_open: false; in property custom_oauth_configured: false; in property google_oauth_available: false; in property microsoft_oauth_available: false; in-out property oauth_setup_open: false; in property oauth_settings_saving: false; in-out property oauth_settings_error: ""; in property google_oauth_bundled: false; in property microsoft_oauth_bundled: false; // Android/iOS must use their native print frameworks; until those hosts // provide one, do not expose the desktop file:// printing fallback. in property print_supported: true; in property document_apis_supported: true; in property app-version: ""; // The host switches this to the UI language's translation of the policy. in property privacy-policy-url: "https://flectar.com/mail/privacy/"; // Neutral, low-chrome surfaces keep the message content visually primary. // Interactive controls come from Slint's desktop widget set. private property dark-mode: root.theme_mode == "dark" ? true : root.theme_mode == "light" ? false : Palette.color-scheme == ColorScheme.dark; private property page-bg: AppTheme.page-bg; private property surface: AppTheme.surface; private property surface-raised: AppTheme.surface-raised; private property sidebar-bg: AppTheme.sidebar-bg; private property control-bg: AppTheme.control-bg; private property muted-control-bg: AppTheme.muted-control-bg; private property accent: AppTheme.accent; private property accent-soft: AppTheme.accent-soft; private property accent-pale: AppTheme.accent-pale; private property selected-row: AppTheme.selected-row; private property avatar-bg: AppTheme.avatar-bg; private property avatar-selected-bg: AppTheme.avatar-selected-bg; private property border: AppTheme.border; private property border-subtle: AppTheme.border-subtle; private property title-color: AppTheme.title; private property heading: AppTheme.heading; private property body-text: AppTheme.body; private property secondary-text: AppTheme.secondary; private property muted-text: AppTheme.muted; private property faint-text: AppTheme.faint; // Mail to show: an account's, or mail imported to this device alone. private property has-mail: root.connected_accounts.length > 0 || LocalMailUi.account-id >= 0; // This content-driven breakpoint is the smallest width that can hold the // rail, folders, message list, and a useful reading pane simultaneously. private property compact-layout: root.width - root.safe-area-insets.left - root.safe-area-insets.right < 980px; // In the integrated frame the window controls sit over the top corners // and toolbars reserve room for them, so content keeps the full window. // Desktop windows with accounts show a top bar above the product rail, // holding the profile pill and the tabs; in the integrated frame it also // holds the window controls. Compact layouts navigate from the bottom bar. // With tabs turned off, only the integrated frame keeps the bar, for its // window controls; the system frame drops it and the mail sidebar takes // the profile switcher. private property tabs-on: root.show_tabs && root.has-mail; private property top-bar: !root.compact-layout && root.has-mail && (root.tabs-on || WindowChrome.controls-visible); changed tabs-on => { root.sync-window-chrome(); } // A tone apart from the workspace, so the active tab can open into it. private property tab-strip-bg: AppTheme.sidebar-tint.mix(AppTheme.title, 0.94); changed top-bar => { root.sync-window-chrome(); } private property content-x: root.safe-area-insets.left; private property content-y: root.safe-area-insets.top + (root.top-bar ? WindowChrome.band-height : 0px); private property content-width: root.width - root.safe-area-insets.left - root.safe-area-insets.right; private property content-height: root.height - root.content-y - root.safe-area-insets.bottom; // Linux draws the whole frame, so it also provides the resize border and // window outline that the window manager would otherwise draw. private property client-frame: root.no-frame && !root.maximized && !root.full-screen; private property compact-primary-nav-visible: root.compact-layout && !ComposerUi.open && !root.calendar_editor_open && !(root.active_view == "mail" && root.mail-compact-page == "message") && !(root.active_view == "mail" && root.mail-folders-open) && !(root.active_view == "contacts" && root.contacts-detail-visible) && !(root.active_view == "files" && FilesUi.preview-open); private property compact-primary-nav-height: LayoutMetrics.compact-navigation-height; private property folder-dialog-open: false; private property trash-delete-open: false; private property trash-delete-kind: ""; private property trash-delete-id: -1; function confirm-trash-deletion(kind: string, id: int) { root.trash-delete-kind = kind; root.trash-delete-id = id; root.trash-delete-open = true; } private property folder-dialog-item-kind: "folder"; private property folder-dialog-mode: "create"; private property folder-dialog-account-id: -1; private property folder-dialog-folder-id: -1; private property folder-dialog-parent-id: -1; private property folder-dialog-parent-name: ""; private property folder-dialog-parent-label-id: -1; private property folder-dialog-parent-label-path: ""; private property folder-dialog-has-descendants: false; private property folder-dialog-name: ""; private property folder-dialog-label-color: transparent; function open-folder-create(account-id: int, parent-id: int, parent-name: string) { root.folder-dialog-item-kind = "folder"; root.folder-dialog-mode = "create"; root.folder-dialog-account-id = account-id; root.folder-dialog-parent-id = parent-id; root.folder-dialog-parent-label-id = -1; root.folder-dialog-parent-label-path = ""; root.folder-dialog-has-descendants = false; root.folder-dialog-parent-name = parent-name; root.folder-dialog-name = ""; root.folder-dialog-open = true; } function open-label-create(account-id: int, account-name: string, parent-label-id: int, parent-path: string) { root.folder-dialog-item-kind = "label"; root.folder-dialog-mode = "create"; root.folder-dialog-account-id = account-id; root.folder-dialog-folder-id = -1; root.folder-dialog-parent-label-id = parent-label-id; root.folder-dialog-parent-label-path = parent-path; root.folder-dialog-has-descendants = false; root.folder-dialog-parent-name = parent-path == "" ? account-name : parent-path; root.folder-dialog-name = ""; // Gmail accepts a fixed provider palette; this blue is supported // remotely and remains a sensible local default for other accounts. root.folder-dialog-label-color = #4a86e8; root.folder-dialog-open = true; } function open-folder-rename(folder-id: int, name: string) { root.folder-dialog-item-kind = "folder"; root.folder-dialog-mode = "rename"; root.folder-dialog-folder-id = folder-id; root.folder-dialog-parent-label-id = -1; root.folder-dialog-parent-label-path = ""; root.folder-dialog-has-descendants = false; root.folder-dialog-parent-name = ""; root.folder-dialog-name = name; root.folder-dialog-open = true; } function open-folder-delete(folder-id: int, name: string) { root.folder-dialog-item-kind = "folder"; root.folder-dialog-mode = "delete"; root.folder-dialog-folder-id = folder-id; root.folder-dialog-parent-label-id = -1; root.folder-dialog-parent-label-path = ""; root.folder-dialog-has-descendants = false; root.folder-dialog-parent-name = ""; root.folder-dialog-name = name; root.folder-dialog-open = true; } function open-label-rename(label-id: int, name: string, color: color, account: string, global: bool) { root.folder-dialog-item-kind = "label"; root.folder-dialog-mode = "rename"; root.folder-dialog-folder-id = label-id; root.folder-dialog-parent-label-id = -1; root.folder-dialog-parent-label-path = ""; root.folder-dialog-has-descendants = false; root.folder-dialog-parent-name = global ? @tr("Global Labels (local only)") : account; root.folder-dialog-name = name; root.folder-dialog-label-color = color; root.folder-dialog-open = true; } function open-label-delete(label-id: int, name: string, account: string, global: bool, has-descendants: bool) { root.folder-dialog-item-kind = "label"; root.folder-dialog-mode = "delete"; root.folder-dialog-folder-id = label-id; root.folder-dialog-parent-label-id = -1; root.folder-dialog-parent-label-path = ""; root.folder-dialog-has-descendants = has-descendants; root.folder-dialog-parent-name = global ? @tr("Global Labels (local only)") : account; root.folder-dialog-name = name; root.folder-dialog-open = true; } system-back-requested => { // The import dialog covers everything; accounts being written cannot // be abandoned, so back waits for that step to finish. if AccountImport.open { if AccountImport.stage != "importing" { AccountImport.close(); } return true; } if MailAttachments.open { MailAttachments.command("close", ""); return true; } if root.active_view == "files" && FilesUi.transfers-open {FilesUi.transfers-open=false;return true;} if root.active_view == "files" && FilesUi.dialog != "" { FilesUi.dialog = ""; return true; } if root.active_view == "files" && FilesUi.setup-open { FilesUi.setup-open = false; return true; } if root.active_view == "files" && FilesUi.navigation-open { FilesUi.navigation-open = false; return true; } if root.active_view == "files" && FilesUi.preview-open { FilesUi.preview-open = false; if FilesUi.busy { FilesUi.command("cancel", "", ""); } FilesUi.command("release-preview", "", ""); return true; } if root.active_view == "files" && FilesUi.can-up && !FilesUi.busy { FilesUi.command("up", "", ""); return true; } if (root.folder-dialog-open) { root.folder-dialog-open = false; return true; } if (root.trash-delete-open) { root.trash-delete-open = false; return true; } if (!root.compact-layout) { return false; } if TabsUi.overview-open { TabsUi.overview-open = false; return true; } if (ComposerUi.open) { root.compose-back-request += 1; return true; } if (root.calendar_editor_open) { root.calendar_editor_open = false; return true; } if (root.settings_open) { root.settings_open = false; return true; } if (root.active_view == "mail" && root.mail-folders-open) { root.mail-folders-open = false; return true; } if (root.active_view == "mail" && root.mail-compact-page == "message") { root.mail-compact-page = "list"; root.close_preview(); return true; } if (root.active_view == "contacts" && root.contacts-detail-visible) { root.contacts-detail-visible = false; return true; } return false; } private property composer-open: ComposerUi.open; changed composer-open => { if (!root.composer-open) { root.compose-back-request = 0; ComposerUi.editor-scroll-y = 0px; ComposerUi.editor-preedit-text = ""; } } changed theme-mode => { Palette.color-scheme = root.theme-mode == "dark" ? ColorScheme.dark : root.theme-mode == "light" ? ColorScheme.light : ColorScheme.unknown; } // Composer windows have their own globals and follow this window's // appearance through Rust. callback appearance-changed(); private property appearance: { mode: root.theme-mode, preset: AppTheme.preset, icon: AppIcon.variant, light_primary: AppTheme.custom-light-primary, light_page: AppTheme.custom-light-page-bg, light_surface: AppTheme.custom-light-surface, light_text: AppTheme.custom-light-text, light_border: AppTheme.custom-light-border, dark_primary: AppTheme.custom-dark-primary, dark_page: AppTheme.custom-dark-page-bg, dark_surface: AppTheme.custom-dark-surface, dark_text: AppTheme.custom-dark-text, dark_border: AppTheme.custom-dark-border, }; changed appearance => { root.appearance-changed(); } changed width => { root.sync-window-chrome(); } changed full-screen => { root.sync-window-chrome(); } changed compact-layout => { root.sync-window-chrome(); } private property chrome-band: WindowChrome.band-height; private property chrome-trailing: WindowChrome.trailing-width; changed chrome-band => { WindowChrome.geometry-changed(); } changed chrome-trailing => { WindowChrome.geometry-changed(); } function sync-window-chrome() { WindowChrome.window-width = root.width; WindowChrome.full-screen = root.full-screen; WindowChrome.compact = root.compact-layout; WindowChrome.top-bar = root.top-bar; AppTheme.top-bar = root.top-bar; TabsUi.enabled = root.tabs-on; TabsUi.compact = root.compact-layout; if !root.compact-layout || !root.tabs-on { TabsUi.overview-open = false; } } // Seeds WindowChrome once; the handlers above keep it current. A child // element runs this so AppWindow's own init stays free for tooling. Rectangle { width: 0px; height: 0px; init => { root.sync-window-chrome(); } } // Toolbar rows sit above this band; wherever they have no interactive // content, input falls through to it and moves the window. if WindowChrome.controls-visible : WindowDragArea { x: root.content-x; y: root.safe-area-insets.top; width: root.content-width; height: WindowChrome.band-height; } // Account discovery is asynchronous. Paint a small initial view while // neither the cached mailbox nor the onboarding decision is available. if !root.startup_ready && !root.startup_failed && !root.has-mail : Rectangle { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; background: root.page-bg; accessible-role: groupbox; accessible-label: I18n.loading-local-data(); VerticalLayout { width: max(240px, self.preferred-width).min(parent.width - 32px); height: 92px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; spacing: 12px; Text { text: "Flectar Mail"; color: root.title-color; font-size: TypeScale.heading; font-weight: 600; horizontal-alignment: center; } Text { text: I18n.loading-local-data(); color: root.muted-text; font-size: TypeScale.body; horizontal-alignment: center; } LoadingSkeletonBlock { height: 4px; radius: 2px; base-color: root.border; highlight-color: root.accent; } } } if root.has-mail : Rectangle { // Do not construct the mailbox on a fresh profile. `visible: false` // retains the entire tree, and constructing it before the account // query touches code and assets that onboarding never needs. x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; background: root.page-bg; product-rail := ProductRail { visible: !root.compact-layout; x: 0px; y: 0px; width: LayoutMetrics.product-rail-width; height: parent.height; active-view <=> root.active_view; settings-active: root.settings_open; background-color: root.sidebar-bg; divider-color: root.border; accent-color: root.accent; accent-background: root.accent-soft; title-color: root.title-color; muted-color: root.muted-text; open-settings => { root.toggle_settings(); } select-view(view) => { root.active_view = view; root.settings_open = false; } } if root.active_view == "mail" : WorkspaceFrame { compact: root.compact-layout; reserve-compact-navigation: root.compact-primary-nav-visible; available-width: parent.width; available-height: parent.height; MailView { changed email-viewport-width => { root.email-viewport-width = self.email-viewport-width; } changed email-viewport-height => { root.email-viewport-height = self.email-viewport-height; } changed email-scroll-document-height => { root.email-scroll-document-height = self.email-scroll-document-height; } compact-page <=> root.mail-compact-page; folders-open <=> root.mail-folders-open; loading: !root.startup_hydrated || root.mail_page_loading; x: 0px; y: 0px; width: parent.width; height: parent.height; available-width: parent.width; compact: root.compact-layout; layout-mode: root.workspace_layout; list-pane-width <=> root.workspace_list_pane_width; list-pane-width-committed(width) => { root.save_workspace_list_pane_width(width); } show-avatars: root.show_avatars; emails: root.emails; list_entries: root.mail_list_entries; group_mail_by_date: root.group_mail_by_date; thread_messages: root.thread_messages; selected_thread_index: root.selected_thread_index; sidebar_rows: root.sidebar_rows; mail_profiles: root.mail_profiles; profiles-in-sidebar: !root.top-bar && !root.compact-layout; active_mail_profile: root.active_mail_profile; account_count: root.connected_accounts.length; select_mail_profile(id) => { root.select_mail_profile(id); } create_mail_profile => { root.settings_tab = "Accounts"; root.settings_open = true; root.new_profile_requested = true; } folder_filter_query <=> root.folder_filter_query; mail_labels: root.mail_labels; mail_label_results: root.mail_label_results; selected_mail_label_count: root.selected_mail_label_count; selected_account_uses_labels: root.selected_account_uses_labels; email_tiles: root.email_tiles; email_links: root.email_links; active_view: root.active_view; selected_scope: root.selected_scope; selected_scope_title: root.selected_scope_title; trash_view: root.trash_view; unified_count: root.unified_count; search_query <=> root.search_query; search_filter: root.search_filter; selected_sender: root.selected_sender; selected_address: root.selected_address; selected_subject: root.selected_subject; selected_time: root.selected_time; selected_to: root.selected_to; selected_label: root.selected_label; selected_is_draft: root.selected_is_draft; selected_initials: root.selected_initials; selected_favicon: root.selected_favicon; selected_has_favicon: root.selected_has_favicon; selected_plain_text: root.selected_plain_text; selected_source: root.selected_source; selected_text: root.selected_text; has_selection: root.has_selection; clipboard_request: root.clipboard_request; clipboard_request_id: root.clipboard_request_id; sync_in_progress: root.sync_in_progress; can_load_more: root.can_load_more; loading_more: root.mail_loading_more; list_content_revision: root.mail_list_revision; selection-count: root.mail_selection_count; has_selected: root.startup_ready && root.has_selected; previous-email-id: root.previous_email_id; next-email-id: root.next_email_id; selected_starred: root.selected_starred; selected_unread: root.selected_unread; selected_sender_verification: root.selected_sender_verification; remote-images-blocked: root.remote-images-blocked; monochrome_sidebar_icons: root.monochrome_sidebar_icons; print_supported: root.print_supported; mail_sidebar_collapsed <=> root.mail_sidebar_collapsed; text_mode <=> root.text_mode; source_mode <=> root.source_mode; rendering_info_open <=> root.rendering_info_open; email-content-aspect <=> root.email-content-aspect; email-scroll-y <=> root.email-scroll-y; settings_open <=> root.settings_open; settings_tab <=> root.settings_tab; accent: root.accent; accent-soft: root.accent-soft; avatar-bg: root.avatar-bg; avatar-selected-bg: root.avatar-selected-bg; body-text: root.body-text; border: root.border; border-subtle: root.border-subtle; control-bg: root.control-bg; faint-text: root.faint-text; heading: root.heading; muted-control-bg: root.muted-control-bg; muted-text: root.muted-text; secondary-text: root.secondary-text; selected-row: root.selected-row; sidebar-bg: root.sidebar-bg; surface: root.surface; surface-raised: root.surface-raised; title-color: root.title-color; select_email(id) => { root.select_email(id); } select_thread_message(index) => { root.select_thread_message(index); } search_changed(query) => { root.search_changed(query); } set_search_filter(filter) => { root.set_search_filter(filter); } select_scope(scope) => { root.select_scope(scope); } toggle_sidebar_section(key, open) => { root.toggle_sidebar_section(key, open); } toggle_folder(id, expanded) => { root.toggle_folder(id, expanded); } filter_folders(query) => { root.filter_folders(query); } request_folder_create(account-id, parent-id, parent-name) => { root.open-folder-create(account-id, parent-id, parent-name); } request_label_create(account-id, account-name, parent-id, parent-path) => { root.open-label-create(account-id, account-name, parent-id, parent-path); } request_folder_rename(id, name) => { root.open-folder-rename(id, name); } request_folder_delete(id, name) => { root.open-folder-delete(id, name); } request_label_rename(id, name, color, account, global) => { root.open-label-rename(id, name, color, account, global); } request_label_delete(id, name, account, global, has-descendants) => { root.open-label-delete(id, name, account, global, has-descendants); } load_more => { root.load_more(); } sync_mail => { root.sync_mail(); } sync_account(account-id) => { root.sync_account(account-id); } check_mail_in_view => { root.check_mail_in_view(); } message_action(action) => { if action == "delete_permanently" { root.confirm-trash-deletion("selected", -1); } else { root.message_action(action); } } message_action_for_email(id, action) => { if action == "delete_permanently" { root.confirm-trash-deletion("row", id); } else { root.message_action_for_email(id, action); } } bulk_message_action(action) => { if action == "delete_permanently" { root.confirm-trash-deletion("bulk", -1); } else { root.bulk_message_action(action); } } toggle_mail_group(key) => { root.toggle_mail_group(key); } request_empty_trash(account-id) => { root.confirm-trash-deletion("empty", account-id); } set_email_checked(id, checked) => { root.set_email_checked(id, checked); } set_all_emails_checked(checked) => { root.set_all_emails_checked(checked); } toggle_mail_label(id, applied) => { root.toggle_mail_label(id, applied); } save_mail_label(id, account-id, name, color, global, parent-id) => { root.save_mail_label(id, account-id, name, color, global, parent-id); } search_mail_labels(query) => { root.search_mail_labels(query); } compose_message_action(action) => { root.compose_message_action(action); } close_preview => { root.close_preview(); } print_message => { root.print_message(); } open_message_in_browser => { root.open_message_in_browser(); } open_compose => { root.open_compose(""); } open_email_link(url) => { root.open_email_link(url); } copy_email_status(status) => { root.copy_email_status(status); } report_rendering_issue => { root.report_rendering_issue(); } email-pointer-event(x, y, kind, control, shift, alt, meta) => { root.email-pointer-event(x, y, kind, control, shift, alt, meta); } email-scroll(offset, viewport-height) => { root.email-scroll(offset, viewport-height); } email-key-event(text, pressed, repeat, control, shift, alt, meta) => { root.email-key-event(text, pressed, repeat, control, shift, alt, meta); } select-email-text => { root.select-email-text(); } load-remote-images-for-message => { root.load-remote-images-for-message(); } } } if root.active_view == "calendar" : WorkspaceFrame { compact: root.compact-layout; reserve-compact-navigation: root.compact-primary-nav-visible; available-width: parent.width; available-height: parent.height; FunctionalCalendarView { active: root.active_view == "calendar"; x: 0px; y: 0px; width: parent.width; height: parent.height; compact: root.compact-layout; sidebar-collapsed <=> root.calendar_sidebar_collapsed; dark-mode: root.dark-mode; month-days: root.calendar_month_days; week-days: root.calendar_week_days; events: root.calendar_events; agenda-rows: root.calendar_agenda_rows; agenda-filters: root.calendar_agenda_filters; agenda-filter-count: root.calendar_agenda_filter_count; agenda-event-count: root.calendar_agenda_event_count; agenda-anchor: root.calendar_agenda_anchor; agenda-scroll-request: root.calendar_agenda_scroll_request; timezone: root.calendar_timezone; sources: root.calendar_sources; month-title: root.calendar_month_title; period-title: root.calendar_period_title; week-label: root.calendar_week_label; selected-date: root.calendar_selected_date; selected-date-label: root.calendar_selected_date_label; hour-labels: root.calendar_hour_labels; selected-date-has-events: root.calendar_selected_date_has_events; view-mode: root.calendar_view_mode; background-color: root.page-bg; sidebar-color: root.sidebar-bg; surface-color: root.surface; ui-border-color: root.border; subtle-border-color: root.border-subtle; title-color: root.title-color; body-color: root.body-text; secondary-color: root.secondary-text; muted-color: root.muted-text; control-color: root.control-bg; control-hover-color: root.muted-control-bg; accent-color: root.accent; accent-background: root.accent-soft; navigate(scope, direction) => { root.calendar-navigate(scope, direction); } select-date(date) => { root.calendar-select-date(date); } set-view(mode) => { root.calendar-set-view(mode); } set-source-enabled(calendar-id, enabled) => { root.calendar-set-source-enabled(calendar-id, enabled); } toggle-agenda-filter(id, checked) => { root.calendar-toggle-agenda-filter(id, checked); } clear-agenda-filters => { root.calendar-clear-agenda-filters(); } today => { root.calendar-today(); } new-event => { root.calendar-open-editor(); } edit-event(event-id) => { root.calendar-edit-event(event-id); } delete-event(event-id) => { root.calendar-delete-event(event-id); } open-link(url) => { root.open_email_link(url); } } } if root.active_view == "contacts" : WorkspaceFrame { compact: root.compact-layout; reserve-compact-navigation: root.compact-primary-nav-visible; available-width: parent.width; available-height: parent.height; ContactsView { detail-page-visible <=> root.contacts-detail-visible; active: root.active_view == "contacts"; x: 0px; y: 0px; width: parent.width; height: parent.height; available-width: parent.width; compact: root.compact-layout; layout-mode: root.workspace_layout; list-pane-width <=> root.workspace_list_pane_width; list-pane-width-committed(width) => { root.save_workspace_list_pane_width(width); } dark-mode: root.dark-mode; sidebar-collapsed <=> root.contacts_sidebar_collapsed; contacts: root.contacts; sidebar-rows: root.contact_sidebar_rows; toggle-section(key, open) => { root.toggle-contact-section(key, open); } query <=> root.contact_search_query; filter: root.contact_scope == "Favorites" ? "favorites" : root.contact_scope == "Suggestions" ? "suggestions" : "all"; selected-scope: root.contact_scope; status: root.contact_save_status; can-load-more: root.contact_can_load_more; loading-more: root.contact_loading_more; content-revision: root.contact_list_revision; total-count: root.contact_total_count; favorite-count: root.contact_favorite_count; suggestion-count: root.contact_suggestion_count; has-selected: root.contact_has_selection; selected-id <=> root.contact_id; edit-name <=> root.contact_name; edit-company <=> root.contact_company; edit-job-title <=> root.contact_job_title; edit-website <=> root.contact_website; edit-birthday <=> root.contact_birthday; edit-postal-address <=> root.contact_address; edit-notes <=> root.contact_notes; edit-tags <=> root.contact_tags; edit-favorite <=> root.contact_favorite; is-suggested: root.contact_is_suggested; edit-initials: root.contact_initials; email-values <=> root.contact-emails; phone-values <=> root.contact-phones; add-value(kind) => { root.add-contact-value(kind); } prefer-value(kind,index) => { root.prefer-contact-value(kind,index); } remove-value(kind,index) => { root.remove-contact-value(kind,index); } read-only: root.contact-read-only; source-name: root.contact-source; sync: root.contact-sync; sync-detail: root.contact-sync-detail; photo: root.contact-photo; has-photo: root.contact-has-photo; resolve-conflict(keep-local) => { root.resolve-contact-conflict(keep-local); } interaction-label: root.contact_interactions + (root.contact_last_interacted != "" ? " · " + root.contact_last_interacted : ""); accent: root.accent; accent-soft: root.accent-soft; avatar-bg: root.avatar-bg; body-text: root.body-text; border: root.border; border-subtle: root.border-subtle; control-bg: root.control-bg; heading: root.heading; muted-control-bg: root.muted-control-bg; muted-text: root.muted-text; secondary-text: root.secondary-text; selected-row: root.selected-row; sidebar-bg: root.sidebar-bg; surface: root.surface; surface-raised: root.surface-raised; title-color: root.title-color; select-contact(id) => { root.select-contact(id); } search(query) => { root.search-contacts(query); } refresh => { root.load-contacts(); } load-more => { root.load-more-contacts(); } select-filter(filter) => { root.select-contact-scope(filter == "favorites" ? "Favorites" : filter == "suggestions" ? "Suggestions" : filter == "all" ? "All contacts" : filter); } new-contact => { root.new-contact(); } save-contact(id, name, company, job-title, website, birthday, address, notes, tags, favorite) => { root.save-contact( id, name, company, job-title, website, birthday, address, notes, tags, favorite ); } delete-contact(id) => { root.delete-contact(id); } usable-email(email) => { root.usable-contact-email(email) } compose-to-contact(email) => { if root.usable-contact-email(email) { root.open_compose(email); } } } } if root.active_view == "files" : WorkspaceFrame { compact: root.compact-layout; reserve-compact-navigation: root.compact-primary-nav-visible; available-width: parent.width; available-height: parent.height; files-view := FilesView { active: root.active_view == "files"; width: parent.width; height: parent.height; available-width: parent.width; compact: root.compact-layout; layout-mode: root.workspace_layout; list-pane-width <=> root.workspace_list_pane_width; account-model: root.connected_accounts; list-pane-width-committed(width) => { root.save_workspace_list_pane_width(width); } } } CompactProductNav { // Keep compact navigation mounted so entering/leaving a detail // screen can slide it instead of popping it in and out. visible: root.compact-layout && (root.compact-primary-nav-visible || self.opacity > 0); x: 0px; y: root.compact-primary-nav-visible ? parent.height - root.compact-primary-nav-height : parent.height; width: parent.width; height: root.compact-primary-nav-height; opacity: root.compact-primary-nav-visible ? 1 : 0; interactive: root.compact-primary-nav-visible; animate y { duration: 240ms; easing: spring(0); } animate opacity { duration: 160ms; easing: ease-out; } active-view <=> root.active_view; settings-active: root.settings_open; background-color: root.sidebar-bg; divider-color: root.border; accent-color: root.accent; accent-background: root.accent-soft; muted-color: root.muted-text; open-settings => { root.toggle_settings(); } select-view(view) => { root.active_view = view; root.settings_open = false; } } // Over the compact layout and its navigation, below the dialogs. if TabsUi.enabled && TabsUi.compact && TabsUi.overview-open : TabOverview { x: 0px; y: 0px; width: parent.width; height: parent.height; live-workspace: root.settings_open ? "settings" : root.active_view; live-title: root.settings_open || root.active_view != "mail" ? "" : root.has_selected && root.selected_subject != "" ? root.selected_subject : root.selected_scope_title; } if root.calendar_editor_open : CalendarEventDialog { compact: root.compact-layout; x: 0px; y: 0px; width: parent.width; height: parent.height; dark-mode: root.dark-mode; accent: root.accent; border: root.border; border-subtle: root.border-subtle; control-bg: root.control-bg; muted-text: root.muted-text; surface: root.surface; title-color: root.title-color; calendar_editor_open <=> root.calendar_editor_open; calendar_event_editing: root.calendar_event_editing; calendar_event_title <=> root.calendar_event_title; calendar_event_date <=> root.calendar_event_date; calendar_event_time <=> root.calendar_event_time; time-placeholder: root.time_format_sample; calendar_event_duration <=> root.calendar_event_duration; calendar_event_color <=> root.calendar_event_color; calendar-save-event(title, date, time, duration, color) => { root.calendar-save-event(title, date, time, duration, color); } } if root.settings_open : WorkspaceFrame { compact: root.compact-layout; reserve-compact-navigation: root.compact-primary-nav-visible; available-width: parent.width; available-height: parent.height; SettingsDialog { compact: root.compact-layout; x: 0px; y: 0px; width: parent.width; height: parent.height; connected_accounts: root.connected_accounts; has_device_mail: root.has_device_mail; mail_export_running: root.mail_export_running; mail_export_status: I18n.ui-message(root.mail-export-message.key, root.mail-export-message.argument, root.mail-export-message.second_argument, root.mail-export-message.third_argument); mail_profiles: root.mail_profiles; profile_members: root.profile_members; new-profile-requested <=> root.new_profile_requested; custom_oauth_configured: root.custom_oauth_configured; google_oauth_available: root.google_oauth_available; microsoft_oauth_available: root.microsoft_oauth_available; google_oauth_bundled: root.google_oauth_bundled; microsoft_oauth_bundled: root.microsoft_oauth_bundled; remote-images-available: root.remote-images-available; account-transfer-supported: root.document_apis_supported; sync_in_progress: root.sync_in_progress; account_setup_in_progress: root.account_setup_in_progress; account_form_open <=> root.account_form_open; oauth_in_progress: root.oauth_in_progress; oauth_settings_saving: root.oauth_settings_saving; sync_status: root.sync_status; app-version: root.app-version; privacy-policy-url: root.privacy-policy-url; storage_loading: root.storage_loading; storage_loaded: root.storage_loaded; storage_total: root.storage_total; storage_error: root.storage_error; storage_usage: root.storage_usage; accent: root.accent; accent-soft: root.accent-soft; body-text: root.body-text; border-subtle: root.border-subtle; faint-text: root.faint-text; heading: root.heading; muted-text: root.muted-text; page-bg: root.page-bg; secondary-text: root.secondary-text; // Under the tab strip, the sidebar shares the pages' raised // surface, so the Settings tab opens into one color. sidebar-bg: root.top-bar ? root.surface-raised : root.sidebar-bg; surface: root.surface; surface-raised: root.surface-raised; title-color: root.title-color; settings_open <=> root.settings_open; settings_tab <=> root.settings_tab; theme_mode <=> root.theme_mode; show_avatars <=> root.show_avatars; show_tabs <=> root.show_tabs; group_mail_by_date <=> root.group_mail_by_date; conversation_view <=> root.conversation_view; show_account_markers <=> root.show_account_markers; workspace_layout <=> root.workspace_layout; language_mode <=> root.language_mode; time_format <=> root.time_format; time_format_sample: root.time_format_sample; monochrome_sidebar_icons <=> root.monochrome_sidebar_icons; notifications_enabled <=> root.notifications_enabled; notification_sound_enabled <=> root.notification_sound_enabled; unread_badge_enabled <=> root.unread_badge_enabled; notification_scope <=> root.notification_scope; sync_interval_minutes <=> root.sync_interval_minutes; mark_read_on_open <=> root.mark_read_on_open; collect_outgoing_contacts <=> root.collect_outgoing_contacts; collect_incoming_contacts <=> root.collect_incoming_contacts; contact_suggest_all_accounts <=> root.contact_suggest_all_accounts; suggest_learned_contacts <=> root.suggest_learned_contacts; close_to_tray <=> root.close_to_tray; launch_at_login <=> root.launch_at_login; launch_at_login_available: root.launch_at_login_available; tray_supported: root.tray_supported; remote-images-enabled <=> root.remote-images-enabled; account_email <=> root.account_email; account_username <=> root.account_username; account_password <=> root.account_password; account_protocol <=> root.account_protocol; jmap_url <=> root.jmap_url; connect_calendar_on_add <=> root.connect_calendar_on_add; caldav_account_id <=> root.caldav_account_id; caldav_url <=> root.caldav_url; caldav_username <=> root.caldav_username; caldav_password <=> root.caldav_password; caldav_connection_id <=> root.caldav_connection_id; caldav_connecting: root.caldav_connecting; carddav_account_id <=> root.carddav_account_id; carddav_url <=> root.carddav_url; carddav_username <=> root.carddav_username; carddav_password <=> root.carddav_password; carddav_manage_existing <=> root.carddav_manage_existing; incoming_security <=> root.incoming_security; smtp_security <=> root.smtp_security; trusted_certificate_pem <=> root.trusted_certificate_pem; oauth_authorization_url: root.oauth_authorization_url; oauth_browser_error: root.oauth_browser_error; retry_oauth_browser => { root.retry_oauth_browser(); } copy_oauth_link => { root.copy_oauth_link(); } submit_oauth_redirect(address) => { root.submit_oauth_redirect(address); } import_mail_certificate => { root.import_mail_certificate(); } incoming_host <=> root.incoming_host; incoming_port <=> root.incoming_port; smtp_host <=> root.smtp_host; smtp_port <=> root.smtp_port; google_client_id <=> root.google_client_id; google_client_secret <=> root.google_client_secret; ms_client_id <=> root.ms_client_id; custom_oauth_open <=> root.custom_oauth_open; save_theme(mode) => { root.save_theme(mode); } save_theme_preset(preset) => { root.save_theme_preset(preset); } download_directory <=> root.download_directory; download_directory_available <=> root.download_directory_available; choose_download_directory => { root.choose_download_directory(); } clear_download_directory => { root.clear_download_directory(); } save_custom_theme( light-primary, light-page, light-surface, light-text, light-border, dark-primary, dark-page, dark-surface, dark-text, dark-border, ) => { return root.save_custom_theme( light-primary, light-page, light-surface, light-text, light-border, dark-primary, dark-page, dark-surface, dark-text, dark-border, ); } save_avatar_visibility(enabled) => { root.save_avatar_visibility(enabled); } save_show_tabs(enabled) => { root.save_show_tabs(enabled); } prepare_profile_members(profile-id, account-id) => { root.prepare_profile_members(profile-id, account-id); } save_mail_profile(profile-id, name, color) => { return root.save_mail_profile(profile-id, name, color); } assign_account_profile(account-id, profile-id) => { root.assign_account_profile(account-id, profile-id); } save_account_color(account-id, color, generated) => { root.save_account_color(account-id, color, generated); } delete_mail_profile(profile-id) => { root.delete_mail_profile(profile-id); } save_account_markers_visibility(enabled) => { root.save_account_markers_visibility(enabled); } save_workspace_layout(mode) => { root.save_workspace_layout(mode); } save_language(language) => { root.save_language(language); } save_time_format(format) => { root.save_time_format(format); } save_sidebar_icon_style(monochrome) => { root.save_sidebar_icon_style(monochrome); } save_notification_settings(enabled, sound-enabled, scope) => { root.save_notification_settings(enabled, sound-enabled, scope); } save_unread_badge(enabled) => { root.save_unread_badge(enabled); } save_sync_interval(minutes) => { root.save_sync_interval(minutes); } save_mark_read_on_open(enabled) => { root.save_mark_read_on_open(enabled); } save_group_mail_by_date(enabled) => { root.save_group_mail_by_date(enabled); } save_conversation_view(enabled) => { root.save_conversation_view(enabled); } save_contact_discovery_settings(outgoing, incoming, all-accounts, suggest) => { root.save_contact_discovery_settings(outgoing, incoming, all-accounts, suggest); } clear_contact_suggestions => { return root.clear_contact_suggestions(); } set_close_to_tray(enabled) => { root.set_close_to_tray(enabled); } set_launch_at_login(enabled) => { root.set_launch_at_login(enabled); } sync_mail => { root.sync_mail(); } sync_account(account-id) => { root.sync_account(account-id); } delete_account(account-id) => { root.delete_account(account-id); } add_password_account(protocol, email, username, password, jmap-url, incoming-host, incoming-port, smtp-host, smtp-port) => { root.add_password_account(protocol, email, username, password, jmap-url, incoming-host, incoming-port, smtp-host, smtp-port); } suggest_account_settings(email) => { root.suggest_account_settings(email); } start_oauth(provider) => { root.start_oauth(provider); } reauth_account(account-id) => { root.reauth_account(account-id); } cancel_oauth => { root.cancel_oauth(); } save_oauth_app(google-id, google-secret, microsoft-id) => { root.save_oauth_app(google-id, google-secret, microsoft-id); } set-remote-images(enabled) => { root.set-remote-images(enabled); } make_account_transfer(value) => root.make_account_transfer(value); read_account_transfer(value) => root.read_account_transfer(value); settings-search-matches(haystack, query) => root.settings-search-matches(haystack, query); reorder_account(source, target, after) => { root.reorder_account(source, target, after); } save_account_mail_history(account-id, value) => { root.save_account_mail_history(account-id, value); } save_account_sent_copy_policy(account-id, value) => { root.save_account_sent_copy_policy(account-id, value); } save_account_keep_on_server(account-id, value) => { root.save_account_keep_on_server(account-id, value); } connect_account_calendar(account-id) => { root.connect_account_calendar(account-id); } connect_caldav(account-id, connection-id, url, username, password) => { root.connect_caldav(account-id, connection-id, url, username, password); } set_calendar_connection_enabled(connection-id, enabled) => { root.set_calendar_connection_enabled(connection-id, enabled); } remove_calendar_connection(connection-id) => { root.remove_calendar_connection(connection-id); } connect_carddav(account-id, url, username, password) => { root.connect_carddav(account-id, url, username, password); } set_account_carddav_enabled(account-id, enabled) => { root.set_account_carddav_enabled(account-id, enabled); } disconnect_carddav(account-id) => { root.disconnect_carddav(account-id); } export_account_backup => { root.export_account_backup(); } import_accounts => { root.import_accounts(); } export_database_snapshot => { root.export_database_snapshot(); } export_device_mail(account-id) => { root.export_device_mail(account-id); } delete_all_data => { root.delete_all_data(); } load_storage_usage => { root.load_storage_usage(); } open-link(url) => { root.open_email_link(url); } } } if ComposerUi.open : ComposeDialog { in property back-request: root.compose-back-request; changed back-request => { if (self.back-request > 0) { self.back-requested(); } } compact: root.compact-layout; attachment-picker-supported: root.document_apis_supported; notice: I18n.ui-message(ComposerUi.message.key, ComposerUi.message.argument, ComposerUi.message.second_argument, ComposerUi.message.third_argument); x: 0px; y: 0px; width: parent.width; height: parent.height; } // The rail width comes from the shared workspace geometry. Its hover label is // rendered here, above the adjacent workspace, so it can extend right // of the icon without being clipped by the rail component. Rectangle { visible: !root.compact-layout && product-rail.tooltip-text != ""; x: LayoutMetrics.product-rail-width + 6px; y: product-rail.tooltip-y; width: rail-tooltip-label.preferred-width + 16px; height: 28px; border-radius: 5px; background: #292929; z: 10000; rail-tooltip-label := Text { text: product-rail.tooltip-text; color: white; font-size: TypeScale.secondary; horizontal-alignment: center; vertical-alignment: center; } } } if root.trash-delete-open : ConfirmationDialog { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; z: 16000; open <=> root.trash-delete-open; title: root.trash-delete-kind == "empty" ? @tr("Empty Trash?") : @tr("Delete permanently?"); subject: ""; description: root.trash-delete-kind == "empty" ? root.trash-delete-id == 0 || (root.trash-delete-id < 0 && root.selected_scope == "Unified Trash") ? @tr("Permanently delete all messages in Trash across every connected account?") : @tr("Permanently delete all messages in this account’s Trash?") : root.trash-delete-kind == "bulk" ? @tr("Permanently delete the selected conversations from Trash?") : @tr("Permanently delete this conversation from Trash?"); detail: @tr("This action cannot be undone."); confirm-label: root.trash-delete-kind == "empty" ? @tr("Empty Trash") : @tr("Delete permanently"); icon: @image-url("icons/lucide/outline/trash.svg"); surface: root.surface; dialog-border-color: root.border; title-color: root.title-color; body-color: root.body-text; muted-color: root.muted-text; confirmed => { if root.trash-delete-kind == "empty" { root.empty_trash(root.trash-delete-id); } else if root.trash-delete-kind == "bulk" { root.bulk_message_action("delete_permanently"); } else if root.trash-delete-kind == "row" { root.message_action_for_email(root.trash-delete-id, "delete_permanently"); } else { root.message_action("delete_permanently"); } } } if AccountImport.open : ImportDialog { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; z: 15500; compact: root.compact-layout; open-accounts => { root.settings_tab = "Accounts"; root.settings_open = true; } show-mail => { root.active_view = "mail"; root.settings_open = false; } } if MovePickerUi.open : MoveDialog { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; z: 15000; compact: root.compact-layout; } if root.folder-dialog-open : FolderDialog { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; z: 15000; mode: root.folder-dialog-mode; item-kind: root.folder-dialog-item-kind; name <=> root.folder-dialog-name; parent-name: root.folder-dialog-parent-name; has-descendants: root.folder-dialog-has-descendants; surface-color: root.surface; raised-color: root.control-bg; ui-border-color: root.border; accent-color: root.accent; title-color: root.title-color; body-color: root.body-text; muted-color: root.muted-text; cancel => { root.folder-dialog-open = false; } submit(name) => { root.folder-dialog-open = false; if (root.folder-dialog-item-kind == "label") { root.save_mail_label( root.folder-dialog-folder-id, root.folder-dialog-account-id, name, root.folder-dialog-label-color, false, root.folder-dialog-parent-label-id, ); if (root.folder-dialog-parent-label-id >= 0) { root.toggle_sidebar_section( "gmail-label:" + root.folder-dialog-account-id + ":" + root.folder-dialog-parent-label-id, true, ); } } else if (root.folder-dialog-mode == "create") { root.create-folder(root.folder-dialog-account-id, root.folder-dialog-parent-id, name); } else { root.rename-folder(root.folder-dialog-folder-id, name); } } confirm-delete => { root.folder-dialog-open = false; if (root.folder-dialog-item-kind == "label") { root.delete_mail_label(root.folder-dialog-folder-id); } else { root.delete-folder(root.folder-dialog-folder-id); } } } // Slint conditional elements are instantiated lazily. The comparatively // large onboarding form is unnecessary on normal launches with an account. if root.startup_ready && !root.has-mail : OnboardingView { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; startup-ready: root.startup_ready; account-setup-in-progress: root.account_setup_in_progress; oauth-in-progress: root.oauth_in_progress; oauth-settings-saving: root.oauth_settings_saving; oauth-settings-error <=> root.oauth_settings_error; google-available: root.google_oauth_available; microsoft-available: root.microsoft_oauth_available; google-bundled: root.google_oauth_bundled; microsoft-bundled: root.microsoft_oauth_bundled; google-client-id: root.google_client_id; google-client-secret: root.google_client_secret; ms-client-id: root.ms_client_id; oauth-setup-open <=> root.oauth_setup_open; save-oauth-apps(google-id, google-secret, microsoft-id) => { root.save_oauth_apps(google-id, google-secret, microsoft-id); } status: root.sync_status; page-bg: root.page-bg; surface: root.surface; hover-surface: root.muted-control-bg; title-color: root.title-color; body-text: root.body-text; muted-text: root.muted-text; ui-border: root.border; account-email <=> root.account_email; account-password <=> root.account_password; account-protocol <=> root.account_protocol; jmap-url <=> root.jmap_url; incoming-security <=> root.incoming_security; smtp-security <=> root.smtp_security; trusted-certificate-pem <=> root.trusted_certificate_pem; account-username <=> root.account_username; oauth-authorization-url: root.oauth_authorization_url; oauth-browser-error: root.oauth_browser_error; retry-oauth-browser => { root.retry_oauth_browser(); } copy-oauth-link => { root.copy_oauth_link(); } submit-oauth-redirect(address) => { root.submit_oauth_redirect(address); } import-mail-certificate => { root.import_mail_certificate(); } incoming-host <=> root.incoming_host; incoming-port <=> root.incoming_port; smtp-host <=> root.smtp_host; smtp-port <=> root.smtp_port; add-password-account(protocol, email, username, password, jmap-url, incoming-host, incoming-port, smtp-host, smtp-port) => { root.add_password_account(protocol, email, username, password, jmap-url, incoming-host, incoming-port, smtp-host, smtp-port); } suggest-account-settings(email) => { root.suggest_account_settings(email); } start-oauth(provider) => { root.connect_calendar_on_add = false; root.start_oauth(provider); } cancel-oauth => { root.cancel_oauth(); } import-accounts => { root.import_accounts(); } privacy-policy-url: root.privacy-policy-url; open-link(url) => { root.open_email_link(url); } } // Keep the progressively painted shell inert until its callbacks have a // core behind them. This disappears as soon as the first page lands. if !root.startup_ready : TouchArea { x: 0px; y: 0px; width: parent.width; height: parent.height; z: 20000; } if root.startup_failed : Rectangle { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; background: root.page-bg; z: 30000; startup-card := Rectangle { width: 460px.min(parent.width - 32px); height: 330px.min(parent.height - 32px); private property cramped: parent.height < 420px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; border-radius: 12px; border-width: 1px; border-color: root.border; background: root.surface; accessible-role: groupbox; accessible-label: @tr("Mailbox startup failed"); VerticalLayout { padding: parent.cramped ? 12px : 24px; spacing: parent.cramped ? 8px : 12px; Text { text: @tr("Flectar Mail could not open local storage"); color: root.title-color; font-size: TypeScale.heading; font-weight: 700; wrap: word-wrap; } Text { text: root.startup_error; color: root.body-text; font-size: TypeScale.body; wrap: word-wrap; } Text { text: @tr("Diagnostic ID: {}", root.startup_diagnostic_id); color: root.muted-text; font-size: TypeScale.secondary; accessible-label: @tr("Diagnostic ID {}", root.startup_diagnostic_id); } Rectangle { vertical-stretch: 1; } Button { height: root.content-height < 420px ? 44px : 48px; text: @tr("Retry"); primary: true; clicked => { root.retry-startup(); } } Button { height: root.content-height < 420px ? 44px : 48px; text: @tr("Copy diagnostics"); clicked => { diagnostic-copy.text = root.diagnostics_text; diagnostic-copy.select-all(); diagnostic-copy.copy(); } } } // Slint's native text input is the cross-platform clipboard bridge. diagnostic-copy := TextInput { width: 1px; height: 1px; opacity: 0; } } } AttachmentPreviewDialog { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; compact: root.compact-layout; } // Below the tab strip and inside the safe area, like the dialogs. OpenPgpReader { x: root.content-x; y: root.content-y; width: root.content-width; height: root.content-height; } // Keep the startup shell movable while its input is blocked. if WindowChrome.controls-visible && !root.startup_ready : WindowDragArea { x: root.content-x; y: root.safe-area-insets.top; width: root.content-width; height: WindowChrome.band-height; z: 20001; } // Above every layer, including startup and modal overlays, so the window // can always be minimized, maximized and closed. if root.top-bar : WindowTopBar { x: root.content-x; y: root.safe-area-insets.top; width: root.content-width; height: WindowChrome.band-height; z: 40000; // The active tab follows Settings, the workspace, and in Mail the // open conversation or mailbox. live-workspace: root.settings_open ? "settings" : root.active_view; live-title: root.settings_open || root.active_view != "mail" ? "" : root.has_selected && root.selected_subject != "" ? root.selected_subject : root.selected_scope_title; background-color: root.tab-strip-bg; // The active tab opens into what lies under the strip: the raised // surface across Settings, the plain one across every workspace. active-tab-background: root.settings_open ? root.surface-raised : root.surface; hover-background: AppTheme.sidebar-tint.mix(AppTheme.title, 0.90); divider-color: root.border; title-color: root.title-color; text-color: root.secondary-text; accent-color: root.accent; profiles: root.mail_profiles; active-profile: root.active_mail_profile; account-count: root.connected_accounts.length; choose-profile(id) => { root.select_mail_profile(id); } create-profile => { root.settings_tab = "Accounts"; root.settings_open = true; root.new_profile_requested = true; } manage-profiles => { root.settings_tab = "Accounts"; root.settings_open = true; } } if WindowChrome.controls-visible : WindowControls { x: root.content-x; y: root.safe-area-insets.top; width: root.content-width; height: WindowChrome.band-height; z: 50000; maximized: root.maximized; foreground: root.title-color; background-color: root.top-bar ? root.tab-strip-bg : AppTheme.surface; } if root.client-frame : Rectangle { z: 50001; border-width: 1px; border-color: AppTheme.control-border; } }