├── .gitignore ├── .prettierrc ├── CHANGELOG ├── README.md ├── build.bat ├── doc └── migration-guide.md ├── jsconfig.json ├── make_xpi.bat ├── ns ├── MailNewsTypes2.idl ├── README.md ├── imgIContainer.idl ├── imgITools.idl ├── nsIClipboard.idl ├── nsIClipboardOwner.idl ├── nsIConverterInputStream.idl ├── nsIConverterOutputStream.idl ├── nsIDOMWindow.idl ├── nsIDocShell.idl ├── nsIDocShellTreeItem.idl ├── nsIDocShellTreeOwner.idl ├── nsIFile.idl ├── nsIFilePicker.idl ├── nsIFileStreams.idl ├── nsIFocusManager.idl ├── nsIIOService.idl ├── nsIInputStream.idl ├── nsIInputStreamChannel.idl ├── nsIInputStreamLength.idl ├── nsIInputStreamPriority.idl ├── nsIInputStreamPump.idl ├── nsIInputStreamTee.idl ├── nsIInterfaceRequestor.idl ├── nsIMsgCopyServiceListener.idl ├── nsIMsgFilterCustomAction.idl ├── nsIMsgFilterService.idl ├── nsIMsgFolder.idl ├── nsIMsgHdr.idl ├── nsIMsgSearchCustomTerm.idl ├── nsIMsgWindow.idl ├── nsINetUtil.idl ├── nsIOutputStream.idl ├── nsIPromptService.idl ├── nsIProperties.idl ├── nsIScriptableInputStream.idl ├── nsISpeculativeConnect.idl ├── nsIStyleSheetService.idl ├── nsISupports.idl ├── nsITransferable.idl ├── nsIURI.idl ├── nsIUnicharInputStream.idl ├── nsIUnicharOutputStream.idl ├── nsIVersionComparator.idl ├── nsIWebNavigation.idl ├── nsIWindowWatcher.idl ├── nsMsgSearchCore.idl └── nsPIDOMWindow.h ├── package-lock.json ├── package.json ├── src ├── _locales │ ├── de │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── fr │ │ └── messages.json │ ├── it │ │ └── messages.json │ ├── lv │ │ └── messages.json │ └── ro │ │ └── messages.json ├── api │ ├── LegacyCSS.js │ ├── ResourceUrl.js │ ├── legacy.ts │ ├── qapp.ts │ ├── qnote.ts │ ├── qpopup.ts │ └── xnote.ts ├── background.html ├── background.ts ├── html │ ├── background.css │ ├── installed.css │ ├── installed.html │ ├── options.css │ ├── options.html │ ├── qpopup.css │ ├── qpopup.html │ ├── updated.html │ ├── wepopup.css │ └── wepopup.html ├── images │ ├── 0.14.4-options.png │ ├── 1x1.gif │ ├── empty-win.gif │ ├── icons │ │ ├── EUR.svg │ │ ├── USD.svg │ │ ├── close.svg │ │ ├── copy.svg │ │ ├── edit.svg │ │ ├── gear.svg │ │ ├── gear2.svg │ │ ├── important.svg │ │ ├── new.svg │ │ ├── ok.svg │ │ ├── paste.svg │ │ ├── qnote-disabled.svg │ │ ├── qnote.svg │ │ ├── reset.svg │ │ ├── save.svg │ │ ├── screenshot.svg │ │ └── trash.svg │ ├── mini-note-disabled.gif │ ├── mini-note.gif │ ├── redim-dark.png │ ├── redim.png │ ├── storage.png │ ├── title-background-dark.png │ ├── title-background.png │ ├── txt-background-dark.png │ └── txt-background.png ├── manifest.json ├── modules-exp │ ├── QNoteFile.mts │ ├── QNoteFilters.mts │ ├── QPopups.sys.mjs │ ├── XNoteFile.mts │ └── api.mts ├── modules │ ├── DOMLocalizator.mts │ ├── Menu.mts │ ├── Messages.mts │ ├── Note.mts │ ├── NotePopups.mts │ ├── QEventDispatcher.mts │ ├── common-background.mts │ ├── common.mts │ └── luxon.mjs ├── schemas │ ├── LegacyCSS.json │ ├── ResourceUrl.json │ ├── legacy.json │ ├── qapp.json │ ├── qnote.json │ ├── qpopup.json │ └── xnote.json ├── scripts │ ├── installed.ts │ ├── messageDisplay.ts │ ├── options.ts │ ├── qpopup.ts │ └── wepopup.ts └── types │ ├── browser.d.ts │ ├── thunderbird-webext-browser.d.ts │ └── thunderbirdClassic.d.ts ├── thunderbird.net ├── about.html └── screenshots │ ├── attach_message.jpg │ ├── attach_print.jpg │ ├── column.jpg │ ├── filters.jpg │ ├── note.jpg │ └── search.jpg ├── tsconfig.json └── update_vers.bat /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | _misc 4 | ns 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": true, 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | CHANGELOG QNote 2 | =========================== 3 | 4 | RELEASE 0.14.8 - ... 5 | - Fix compatibility with TB139 6 | 7 | RELEASE 0.14.7 - Mar 24, 2025 8 | - Fix compatibility with TB136 9 | - Fix issues with multinote (#78) 10 | - Fix autosave when closing / switching tab / window 11 | 12 | RELEASE 0.14.4 - Dec 16, 2024 13 | ----------------- 14 | - Remove extra title bar for popup window (#75) 15 | - Revert default save and close mechanism as it was before 0.14.3 (#67, #68, #69) 16 | - Show context menu in message page context (#71) 17 | - Fix large icons when opening message pane in a new tab (#72) 18 | 19 | RELEASE 0.14.3 - Oct 7, 2024 20 | ----------------- 21 | - Complete rewrite in TypeScript. Fixes most of the issues with TB128 22 | - Feature take note screenshot to clipboard 23 | - Fix note scrolling with large texts (#48) 24 | - Fix MacOS blank window (#58) 25 | - Fix QNote missing text column (#57) 26 | - Deprecated: storage option inside extension 27 | 28 | RELEASE 0.13.0 - Aug 21, 2024 29 | ----------------- 30 | - Add templating support for attaching to print and message (#34, #47, #52) 31 | - Add treat note text as HTML option for attaching to print and message 32 | - Add 'aAghe' date formatting rules (#50) 33 | - Fix tagging (#56) 34 | 35 | RELEASE 0.12.7 - Aug 15, 2024 36 | ----------------- 37 | - Fix compatibility with TB128 (#54) 38 | - Fix ColumnHandler with TB115 39 | 40 | RELEASE 0.12.5 - Aug 1, 2023 41 | ----------------- 42 | - Fix some TB 115 issues 43 | - Column handler still won't work with TB 115 44 | - QuickFilter still won't work with TB 115 45 | 46 | RELEASE 0.12.3 - Jul 4, 2023 47 | ----------------- 48 | - Double click on embedded note opens note popup (#35) 49 | - Fix embedded note incorrect text wrapping (#39) 50 | - Fix QuickFilter on focus triggers unnecessary search (#43) 51 | 52 | RELEASE 0.12.* - Nov 9, 2022 53 | ----------------- 54 | - Add multi message selection context menu (create, update, delete, reset, copy, paste) 55 | - Add icon to multi message selection list 56 | - Add filters (Edit / Find / Search Messages) 57 | - Add filter actions (Tools / Message Filters) 58 | - Add clipboard Copy / Paste 59 | - Lots of fixes and code cleanup 60 | 61 | RELEASE 0.11.* - May 12, 2022 62 | ----------------- 63 | - Add context menu to message 64 | - Add QuickFilter support for filtering notes (only for folder storage) 65 | - Add ability to export notes to folder by choosing QNote or XNote++ formats (#20) 66 | - Minor bugfixes 67 | 68 | RELEASE 0.10.4 - Oct 15, 2021 69 | ----------------- 70 | - Options: enable / disable spell check 71 | - Add Romanian locale 72 | - Add dark theme 73 | - Fix note resize issues 74 | - Minor bugfixes 75 | 76 | RELEASE 0.10.3 - Oct 8, 2021 77 | ----------------- 78 | - Use Webextension headerMessageId property (starting with TB85) 79 | - Fix unnecessary trimming when message-id is w/o brackets (#6) 80 | - Fix note movement for TB < 91 (#9) 81 | 82 | RELEASE 0.10.2 - Aug 28, 2021 83 | ----------------- 84 | - Fix compatibility with TB91 (#8) 85 | 86 | RELEASE 0.10.1 - Dec 1, 2020 87 | ----------------- 88 | - Options: auto save 89 | - Options: reset to defaults 90 | - Options: more default positions 91 | - Options: opt always use default size and position 92 | - Add date localization 93 | - Styling tweaks 94 | - Minor bug fixes 95 | 96 | RELEASE 0.10.0 - Nov 23, 2020 97 | ----------------- 98 | - Add localized date format option with custom or predefined formats 99 | - Add default note placement option (only floating panel) 100 | - Add confirm delete option 101 | - Save options w/o extension reload 102 | - Style options page according to selected theme 103 | - Minor bug fixes 104 | 105 | 106 | RELEASE 0.9.1 - Nov 18, 2020 107 | ----------------- 108 | - Attach note to print 109 | - Attach note to message 110 | - Removed search from QuickFilter until we find a good solution 111 | - Lots of bugfixes 112 | 113 | RELEASE 0.7.0 - Nov 5, 2020 114 | ----------------- 115 | - Add QuickFilter support for filtering notes by contents 116 | - Various bug fixes 117 | 118 | RELEASE 0.6.3 - Oct 26, 2020 119 | ----------------- 120 | - Added main toolbar button 121 | - Attach note to window that opened it instead to main window 122 | - Updated icons 123 | - Force note text color to black 124 | - Minor bug fixes 125 | 126 | RELEASE 0.6.2 - Oct 15, 2020 127 | ----------------- 128 | - New locales - IT / LV / FR / DE 129 | - Saving notes using UTF-8 encoded files 130 | - Fix QNote extension interfere with some other extensions 131 | 132 | RELEASE 0.6.1 - Oct 8, 2020 133 | ----------------- 134 | - Added Alt+Q keyboard shortcut 135 | - Better overall keyboard handling 136 | - Note to grab focus when explicitly click on QNote button or context menu 137 | - Minor bug fixes 138 | 139 | RELEASE 0.6.0 - Oct 3, 2020 140 | ----------------- 141 | - Notes will now be saved on disk using more versatile JSON format, not XNote++ format anymore 142 | - Auto close note after folder or tab change 143 | - Preserve column width 144 | - Fix XNote filename handling containing slashes 145 | 146 | RELEASE 0.5.4 - Oct 2, 2020 147 | ----------------- 148 | - Change note timestamp only if text changed 149 | - Option to overwrite existing notes when importing xnotes 150 | - Properly encode/decode .xnote filenames. Some notes did not shown up because of .xnote file names 151 | - Fix popup losing controls (again) 152 | - Minor bugfixes 153 | 154 | RELEASE 0.5.3 - Sep 30, 2020 155 | ----------------- 156 | - Fix popup losing controls 157 | - Add option for note to gain auto focus 158 | 159 | RELEASE 0.5.2 - Sep 29, 2020 160 | ----------------- 161 | - Added column with icon and text option 162 | - Added window option - popup or floating panel 163 | 164 | RELEASE 0.4 - Sep 19, 2020 165 | ----------------- 166 | - Added storage option - folder or extension storage 167 | - Closing with empty text will result in note deletion 168 | 169 | RELEASE 0.2 - Sep 16, 2020 170 | ----------------- 171 | - Fix compatibility with TB78 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 1. [About](#about) 3 | 1. [Features](#features) 4 | 1. [Usage](#usage) 5 | 1. [Storage](#storage) 6 | 1. [Popup windows](#popup-windows) 7 | 1. [Screenshots](#screenshots) 8 | 1. [Building](#building) 9 | 1. [Releases](#releases) 10 | 1. [Known issues](#known-issues) 11 | 1. [Support](#support) 12 | 1. [Credits](#credits) 13 | 14 | 15 | # About 16 | This is the source code repository for the Thunderbird [QNote](https://addons.thunderbird.net/en-US/thunderbird/addon/qnote/) extension. 17 | 18 |

19 | 20 | 21 |

22 | 23 | # Features 24 | - Add "sticky" notes to email messages 25 | - Save note position and size; multiple default note positions 26 | - Search notes using Thunderbird's built-in search (Edit / Find / Search Messages) 27 | - Filter and apply actions based on different conditions (Tools / Message Filters) 28 | - Clipboard copy/paste 29 | - Column with note icon and preview 30 | - Bulk operations on multiple message selections: create, update, delete, reset, copy, paste 31 | - Light and dark themes 32 | - Multiple locales and localized date formats 33 | - Tag messages when creating notes 34 | - Fully compatible with [XNote++](https://addons.thunderbird.net/en-US/thunderbird/addon/xnotepp/) (3.0.0) 35 | - Import / export between XNote++ (.xnote) and QNote (.qnote) file formats 36 | - Supports Thunderbird versions, starting from 68.2.0 (check [archive](https://addons.thunderbird.net/en-US/thunderbird/addon/qnote/versions/) for latest supported version for your Thunderbird installation) 37 | - Simple templating support 38 | 39 | # Usage 40 | 41 | - Press Alt+Q to open / save & close the note 42 | - Press ESC to close the note without saving 43 | - Right-click on message(s) to access more commands in the context menu 44 | - Use the built-in search to search within notes 45 | - Use the built-in Filter Manager to create custom filters and actions 46 | 47 | # Storage 48 | There are two options for storing notes: 49 | 50 | - Storing inside the extension (deprecated) 51 | - Storing in a folder 52 | 53 | Currently, there is no built-in mechanism for sharing notes across multiple computers. For now, in order to share notes across multiple computers, you could use shared folder solutions like Dropbox, NFS, or Windows/Samba shares. 54 | 55 | If you are migrating from the XNote++ extension, you have two options: using the XNote++ folder directly or importing notes into the QNote folder. For more information, refer to the [migration guide](doc/migration-guide.md). 56 | 57 | __If you are using internal storage, don't forget to export data before removing the extension.__ 58 | 59 | # Popup windows 60 | There are two options for note windows: 61 | 62 | - floating panel 63 | - popup window 64 | 65 | _Floating panel_ looks better but might not work well on all platforms. Fall back to _popup window_ if you experience difficulties with the _floating panel_. The _popup window_ uses Thunderbird's standard API. 66 | 67 | # Screenshots 68 |

69 | Note popup 70 | Attach to message 71 |

72 |

73 | Attach to print 74 | Column header 75 |

76 |

77 | Message filters 78 | Message search 79 |

80 | 81 | # Building 82 | ``` 83 | npm install 84 | ./node_modules/.bin/tsc 85 | build.bat 86 | ``` 87 | 88 | All assets should now be placed in the `dist/release` folder if there are no errors. 89 | 90 | Use `make_xpi.bat` to generate a `.xpi` ZIP file or point to `dist/release` as the source folder for `Tools / Developer Tools / Debug Add-ons`. 91 | 92 | If you are using Linux, adjust `.bat` files to the appropriate Linux commands. 93 | 94 | # Releases 95 | 96 | Official releases are published on [addons.thunderbird.net](https://addons.thunderbird.net/en-US/thunderbird/addon/qnote/versions/) website. 97 | 98 | # Known issues 99 | - It does not work well together with the [Conversations](https://addons.thunderbird.net/en-US/thunderbird/addon/gmail-conversation-view/) extension. 100 | 101 | # Support 102 | Maintaining this extension requires a significant amount of time. If you find it useful, and you'd like to support its development, contributions in [EUR](https://www.paypal.com/donate/?hosted_button_id=CCFL84AMQKV4S) or [USD](https://www.paypal.com/donate/?hosted_button_id=NKF22QJS87LWN) via PayPal would be greatly appreciated. 103 | 104 | # Credits 105 | - Beautiful icons sourced from the [Gartoon Redux Action Icons Pack](https://www.iconarchive.com/show/gartoon-action-icons-by-gartoon-team.html) 106 | - Date formatting is provided by the [Luxon](https://github.com/moment/luxon/) library 107 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | xcopy /S /D /Y src\_locales\ dist\release\_locales\ 4 | xcopy /S /D /Y src\html\ dist\release\html\ 5 | xcopy /S /D /Y src\images\ dist\release\images\ 6 | xcopy /S /D /Y src\schemas\ dist\release\schemas\ 7 | xcopy /D /Y src\manifest.json dist\release\ 8 | xcopy /D /Y src\background.html dist\release\ 9 | -------------------------------------------------------------------------------- /doc/migration-guide.md: -------------------------------------------------------------------------------- 1 | # How to Migrate from XNote++ (and Back) 2 | 1. [Using the Existing XNote++ Folder](#same-folder) 3 | 2. [Import XNote++ Files](#import-from-folder) 4 | 3. [Export as .xnote or .qnote Files](#export) 5 | 6 | ## Using the Existing XNote++ Folder 7 | You can use the same folder where you saved XNote++ files. In this case, .xnote files will be automatically converted to .qnote files when you edit or delete them. Unchanged files will remain intact. 8 | 9 | ## Import XNote++ Files 10 | Go to Preferences, navigate to the Storage section, and click the "Import Notes from Folder" button. Next, select the folder containing your XNote++ or QNote files. Both .xnote and .qnote files will be imported into the currently selected storage option. 11 | 12 | Storage Section 13 | 14 | ## Export as .xnote or .qnote Files 15 | If you wish to migrate back to XNote++ extensions, you can do so. Go to Preferences, navigate to the Storage section, and click the "Export QNotes" or "Export XNotes" buttons. Then, select the target folder. Notes will be exported from your current storage option (either "Inside extension" or "Local folder") to the selected target folder. 16 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /make_xpi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rd /s /q dist 4 | 5 | call ./node_modules/.bin/tsc 6 | call build.bat 7 | call update_vers.bat 8 | 9 | for /f %%i in ('jq -r .version dist/release/manifest.json') do set VERS=%%i 10 | set E=qnote-%VERS%.zip 11 | 12 | if exist %E% ( 13 | echo %E% exists, removing 14 | del %E% 15 | rem file exists 16 | ) 17 | 18 | "C:\Program Files\7-Zip\7z.exe" a %E% .\dist\release\* > nul 19 | 20 | if errorlevel 1 ( 21 | echo 7z failure %errorlevel% 22 | exit /b %errorlevel% 23 | ) else ( 24 | echo %E% created 25 | ) 26 | -------------------------------------------------------------------------------- /ns/MailNewsTypes2.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | typedef unsigned long nsMsgKey; 9 | typedef unsigned long nsMsgViewIndex; 10 | 11 | typedef long nsMsgSearchScopeValue; 12 | 13 | typedef long nsMsgPriorityValue; 14 | typedef long nsMsgSocketTypeValue; 15 | typedef long nsMsgAuthMethodValue; 16 | 17 | typedef unsigned long nsMsgJunkStatus; 18 | 19 | typedef unsigned long nsMsgJunkScore; 20 | 21 | [scriptable, uuid(94C0D8D8-2045-11d3-8A8F-0060B0FC04D2)] 22 | interface nsMsgPriority : nsISupports { 23 | const nsMsgPriorityValue notSet = 0; 24 | const nsMsgPriorityValue none = 1; 25 | const nsMsgPriorityValue lowest = 2; 26 | const nsMsgPriorityValue low = 3; 27 | const nsMsgPriorityValue normal = 4; 28 | const nsMsgPriorityValue high = 5; 29 | const nsMsgPriorityValue highest = 6; 30 | //the default priority (if none) is set in the message 31 | const nsMsgPriorityValue Default = 4; 32 | }; 33 | 34 | /** 35 | * Defines whether to use SSL or STARTTLS or not. 36 | * Used by @see nsIMsgIncomingServer.socketType 37 | * and @see nsIMsgOutgoingServer.socketType 38 | */ 39 | [scriptable, uuid(bc78bc74-1b34-48e8-ac2b-968e8dff1aeb)] 40 | interface nsMsgSocketType : nsISupports { 41 | /// No SSL or STARTTLS 42 | const nsMsgSocketTypeValue plain = 0; 43 | /// Use TLS via STARTTLS, but only if server offers it. 44 | /// @deprecated This is vulnerable to MITM attacks 45 | const nsMsgSocketTypeValue trySTARTTLS = 1; 46 | /// Insist on TLS via STARTTLS. 47 | /// Uses normal port. 48 | const nsMsgSocketTypeValue alwaysSTARTTLS = 2; 49 | /// Connect via SSL. 50 | /// Needs special SSL port. 51 | const nsMsgSocketTypeValue SSL = 3; 52 | }; 53 | 54 | /** 55 | * Defines which authentication schemes we should try. 56 | * Used by @see nsIMsgIncomingServer.authMethod 57 | * and @see nsIMsgOutgoingServer.authMethod 58 | */ 59 | [scriptable, uuid(4a10e647-d179-4a53-b7ef-df575ff5f405)] 60 | interface nsMsgAuthMethod : nsISupports { 61 | // 0 is intentionally undefined and invalid 62 | /// No login needed. E.g. IP-address-based. 63 | const nsMsgAuthMethodValue none = 1; 64 | /// Do not use AUTH commands (e.g. AUTH=PLAIN), 65 | /// but the original login commands that the protocol specified 66 | /// (POP: "USER"/"PASS", IMAP: "login", not valid for SMTP) 67 | const nsMsgAuthMethodValue old = 2; 68 | /// password in the clear. AUTH=PLAIN/LOGIN or old-style login. 69 | const nsMsgAuthMethodValue passwordCleartext = 3; 70 | /// hashed password. CRAM-MD5, DIGEST-MD5 71 | const nsMsgAuthMethodValue passwordEncrypted = 4; 72 | /// Kerberos / GSSAPI (Unix single-signon) 73 | const nsMsgAuthMethodValue GSSAPI = 5; 74 | /// NTLM is a Windows single-singon scheme. 75 | /// Includes MSN / Passport.net, which is the same with a different name. 76 | const nsMsgAuthMethodValue NTLM = 6; 77 | /// Auth External is cert-based authentication 78 | const nsMsgAuthMethodValue External = 7; 79 | /// Encrypted password or Kerberos / GSSAPI or NTLM. 80 | /// @deprecated - for migration only. 81 | const nsMsgAuthMethodValue secure = 8; 82 | /// Let us pick any of the auth types supported by the server. 83 | /// Discouraged, because vulnerable to MITM attacks, even if server offers secure auth. 84 | const nsMsgAuthMethodValue anything = 9; 85 | 86 | /// Use OAuth2 to authenticate. 87 | const nsMsgAuthMethodValue OAuth2 = 10; 88 | }; 89 | 90 | typedef long nsMsgViewSortOrderValue; 91 | typedef long nsMsgViewSortTypeValue; 92 | typedef long nsMsgViewTypeValue; 93 | typedef long nsMsgViewFlagsTypeValue; 94 | -------------------------------------------------------------------------------- /ns/README.md: -------------------------------------------------------------------------------- 1 | I added these *.idl files to the repo as a reference and to keep track of any changes in the type definitions later. -------------------------------------------------------------------------------- /ns/nsIClipboardOwner.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | 8 | #include "nsISupports.idl" 9 | #include "nsITransferable.idl" 10 | 11 | 12 | [scriptable, uuid(5A31C7A1-E122-11d2-9A57-000064657374)] 13 | interface nsIClipboardOwner : nsISupports 14 | { 15 | /** 16 | * Notifies the owner of the clipboard transferable that the 17 | * transferable is being removed from the clipboard 18 | * 19 | * @param aTransferable The transferable 20 | * @result NS_Ok if no errors 21 | */ 22 | 23 | void LosingOwnership ( in nsITransferable aTransferable ) ; 24 | }; 25 | 26 | 27 | %{ C++ 28 | 29 | %} 30 | -------------------------------------------------------------------------------- /ns/nsIConverterInputStream.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsIUnicharInputStream.idl" 7 | 8 | interface nsIInputStream; 9 | 10 | /** 11 | * A unichar input stream that wraps an input stream. 12 | * This allows reading unicode strings from a stream, automatically converting 13 | * the bytes from a selected character encoding. 14 | */ 15 | [scriptable, builtinclass, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)] 16 | interface nsIConverterInputStream : nsIUnicharInputStream { 17 | /** 18 | * Default replacement char value, U+FFFD REPLACEMENT CHARACTER. 19 | */ 20 | const char16_t DEFAULT_REPLACEMENT_CHARACTER = 0xFFFD; 21 | 22 | /** 23 | * Special replacement character value that requests errors to 24 | * be treated as fatal. 25 | */ 26 | const char16_t ERRORS_ARE_FATAL = 0; 27 | 28 | /** 29 | * Initialize this stream. 30 | * @param aStream 31 | * The underlying stream to read from. 32 | * @param aCharset 33 | * The character encoding to use for converting the bytes of the 34 | * stream. A null charset will be interpreted as UTF-8. 35 | * @param aBufferSize 36 | * How many bytes to buffer. 37 | * @param aReplacementChar 38 | * The character to replace unknown byte sequences in the stream 39 | * with. The standard replacement character is U+FFFD. 40 | * A value of 0x0000 will cause an exception to be thrown if unknown 41 | * byte sequences are encountered in the stream. 42 | */ 43 | void init (in nsIInputStream aStream, in string aCharset, 44 | in long aBufferSize, in char16_t aReplacementChar); 45 | }; 46 | -------------------------------------------------------------------------------- /ns/nsIConverterOutputStream.idl: -------------------------------------------------------------------------------- 1 | /* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsIUnicharOutputStream.idl" 7 | 8 | interface nsIOutputStream; 9 | 10 | /** 11 | * This interface allows writing strings to a stream, doing automatic 12 | * character encoding conversion. 13 | */ 14 | [scriptable, builtinclass, uuid(4b71113a-cb0d-479f-8ed5-01daeba2e8d4)] 15 | interface nsIConverterOutputStream : nsIUnicharOutputStream 16 | { 17 | /** 18 | * Initialize this stream. Must be called before any other method on this 19 | * interface, or you will crash. The output stream passed to this method 20 | * must not be null, or you will crash. 21 | * 22 | * @param aOutStream 23 | * The underlying output stream to which the converted strings will 24 | * be written. 25 | * @param aCharset 26 | * The character set to use for encoding the characters. A null 27 | * charset will be interpreted as UTF-8. 28 | */ 29 | void init(in nsIOutputStream aOutStream, in string aCharset); 30 | }; 31 | -------------------------------------------------------------------------------- /ns/nsIDOMWindow.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "domstubs.idl" 7 | 8 | interface nsIControllers; 9 | interface nsIPrompt; 10 | interface nsIVariant; 11 | 12 | /** 13 | * Empty interface for compatibility with older versions. 14 | * @deprecated Use WebIDL for script visible features, 15 | * nsPIDOMWindow for C++ callers. 16 | */ 17 | 18 | [scriptable, builtinclass, uuid(b8343993-0383-4add-9930-ad176b189240)] 19 | interface nsIDOMWindow : nsISupports {}; 20 | -------------------------------------------------------------------------------- /ns/nsIDocShellTreeOwner.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | #include "nsISupports.idl" 8 | 9 | /** 10 | * The nsIDocShellTreeOwner 11 | */ 12 | 13 | interface nsIDocShellTreeItem; 14 | interface nsIRemoteTab; 15 | webidl BrowsingContext; 16 | 17 | [scriptable, uuid(0e3dc4b1-4cea-4a37-af71-79f0afd07574)] 18 | interface nsIDocShellTreeOwner : nsISupports 19 | { 20 | /** 21 | * Called when a content shell is added to the docshell tree. This is 22 | * _only_ called for "root" content shells (that is, ones whose parent is a 23 | * chrome shell). 24 | * 25 | * @param aContentShell the shell being added. 26 | * @param aPrimary whether the shell is primary. 27 | */ 28 | void contentShellAdded(in nsIDocShellTreeItem aContentShell, 29 | in boolean aPrimary); 30 | 31 | /** 32 | * Called when a content shell is removed from the docshell tree. This is 33 | * _only_ called for "root" content shells (that is, ones whose parent is a 34 | * chrome shell). Note that if aContentShell was never added, 35 | * contentShellRemoved should just do nothing. 36 | * 37 | * @param aContentShell the shell being removed. 38 | */ 39 | void contentShellRemoved(in nsIDocShellTreeItem aContentShell); 40 | 41 | /* 42 | Returns the Primary Content Shell 43 | */ 44 | readonly attribute nsIDocShellTreeItem primaryContentShell; 45 | 46 | void remoteTabAdded(in nsIRemoteTab aTab, in boolean aPrimary); 47 | void remoteTabRemoved(in nsIRemoteTab aTab); 48 | 49 | /* 50 | In multiprocess case we may not have primaryContentShell but 51 | primaryRemoteTab. 52 | */ 53 | readonly attribute nsIRemoteTab primaryRemoteTab; 54 | 55 | /* 56 | Get the BrowsingContext associated with either the primary content shell or 57 | primary remote tab, depending on which is available. 58 | */ 59 | readonly attribute BrowsingContext primaryContentBrowsingContext; 60 | 61 | /* 62 | Tells the tree owner to size its window or parent window in such a way 63 | that the shell passed along will be the size specified. 64 | */ 65 | [can_run_script] 66 | void sizeShellTo(in nsIDocShellTreeItem shell, in long cx, in long cy); 67 | 68 | /* 69 | Gets the size of the primary content area in device pixels. This should work 70 | for both in-process and out-of-process content areas. 71 | */ 72 | void getPrimaryContentSize(out long width, out long height); 73 | /* 74 | Sets the size of the primary content area in device pixels. This should work 75 | for both in-process and out-of-process content areas. 76 | */ 77 | void setPrimaryContentSize(in long width, in long height); 78 | 79 | /* 80 | Gets the size of the root docshell in device pixels. 81 | */ 82 | void getRootShellSize(out long width, out long height); 83 | /* 84 | Sets the size of the root docshell in device pixels. 85 | */ 86 | void setRootShellSize(in long width, in long height); 87 | 88 | /* 89 | Sets the persistence of different attributes of the window. 90 | */ 91 | void setPersistence(in boolean aPersistPosition, 92 | in boolean aPersistSize, 93 | in boolean aPersistSizeMode); 94 | 95 | /* 96 | Gets the current persistence states of the window. 97 | */ 98 | void getPersistence(out boolean aPersistPosition, 99 | out boolean aPersistSize, 100 | out boolean aPersistSizeMode); 101 | 102 | /* 103 | Gets the number of tabs currently open in our window, assuming 104 | this tree owner has such a concept. 105 | */ 106 | readonly attribute unsigned long tabCount; 107 | 108 | /* 109 | Returns true if there is a primary content shell or a primary 110 | remote tab. 111 | */ 112 | readonly attribute boolean hasPrimaryContent; 113 | }; 114 | -------------------------------------------------------------------------------- /ns/nsIInputStreamChannel.idl: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "nsISupports.idl" 6 | 7 | interface nsIInputStream; 8 | interface nsIURI; 9 | 10 | /** 11 | * nsIInputStreamChannel 12 | * 13 | * This interface provides methods to initialize an input stream channel. 14 | * The input stream channel serves as a data pump for an input stream. 15 | */ 16 | [scriptable, uuid(ea730238-4bfd-4015-8489-8f264d05b343)] 17 | interface nsIInputStreamChannel : nsISupports 18 | { 19 | /** 20 | * Sets the URI for this channel. This must be called before the 21 | * channel is opened, and it may only be called once. 22 | */ 23 | void setURI(in nsIURI aURI); 24 | 25 | /** 26 | * Get/set the content stream 27 | * 28 | * This stream contains the data that will be pushed to the channel's 29 | * stream listener. If the stream is non-blocking and supports the 30 | * nsIAsyncInputStream interface, then the stream will be read directly. 31 | * Otherwise, the stream will be read on a background thread. 32 | * 33 | * This attribute must be set before the channel is opened, and it may 34 | * only be set once. 35 | * 36 | * @throws NS_ERROR_IN_PROGRESS if the setter is called after the channel 37 | * has been opened. 38 | */ 39 | attribute nsIInputStream contentStream; 40 | 41 | /** 42 | * Get/set the srcdoc data string. When the input stream channel is 43 | * created to load a srcdoc iframe, this is set to hold the value of the 44 | * srcdoc attribute. 45 | * 46 | * This should be the same value used to create contentStream, but this is 47 | * not checked. 48 | * 49 | * Changing the value of this attribute will not otherwise affect the 50 | * functionality of the channel or input stream. 51 | */ 52 | attribute AString srcdocData; 53 | 54 | /** 55 | * Returns true if srcdocData has been set within the channel. 56 | */ 57 | readonly attribute boolean isSrcdocChannel; 58 | 59 | /** 60 | * The base URI to be used for the channel. Used when the base URI cannot 61 | * be inferred by other means, for example when this is a srcdoc channel. 62 | */ 63 | attribute nsIURI baseURI; 64 | }; 65 | -------------------------------------------------------------------------------- /ns/nsIInputStreamLength.idl: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "nsISupports.idl" 6 | 7 | interface nsIEventTarget; 8 | interface nsIInputStreamLengthCallback; 9 | 10 | /** 11 | * Note: Instead of using these interfaces directly, consider to use 12 | * InputStreamLengthHelper class. 13 | */ 14 | 15 | [uuid(452d059f-9a9c-4434-8839-e10d1405647c)] 16 | interface nsIInputStreamLength : nsISupports 17 | { 18 | /** 19 | * Returns the total length of the stream if known. Otherwise it returns -1. 20 | * This is different than calling available() which returns the number of 21 | * bytes ready to be read from the stream. 22 | * -1 is a valid value for a stream that doesn't know its length. For 23 | * instance, a pipe stream could return such value. 24 | * 25 | * It could throw NS_BASE_STREAM_WOULD_BLOCK if the inputStream is 26 | * non-blocking. If this happens, you should use 27 | * nsIAsyncInputStreamLength::asyncLengthWait(). 28 | * 29 | * If the stream has already been read (read()/readSegments()/close()/seek() 30 | * methods has been called), length() returns NS_ERROR_NOT_AVAILABLE. 31 | * 32 | * This is not an attribute because a stream can change its length. For 33 | * instance, if the stream is a file inputStream and the underlying OS file 34 | * changes, its length will change as well. 35 | */ 36 | long long length(); 37 | }; 38 | 39 | [uuid(b63f9ecf-4668-44a3-93bd-72dbc65a6125)] 40 | interface nsIAsyncInputStreamLength : nsISupports 41 | { 42 | /** 43 | * If the stream is non-blocking, nsIInputStreamLength::length() can return 44 | * NS_BASE_STREAM_WOULD_BLOCK. The caller must then wait for the stream to 45 | * know its length. 46 | * 47 | * If the stream implements nsIAsyncInputStreamLength, then the caller can 48 | * use this interface to request an asynchronous notification when the 49 | * stream's length becomes known (via the AsyncLengthWait method). 50 | * If the length is already known, the aCallback will be still called 51 | * asynchronously. 52 | * 53 | * If the stream has already been read (read()/readSegments()/close()/seek() 54 | * methods has been called), length() returns NS_ERROR_NOT_AVAILABLE. 55 | * 56 | * @param aCallback 57 | * This object is notified when the length becomes known. This 58 | * parameter may be null to clear an existing callback. 59 | * @param aEventTarget 60 | * Specify that the notification must be delivered to a specific event 61 | * target. 62 | */ 63 | void asyncLengthWait(in nsIInputStreamLengthCallback aCallback, 64 | in nsIEventTarget aEventTarget); 65 | }; 66 | 67 | /** 68 | * This is a companion interface for 69 | * nsIAsyncInputStreamLength::asyncLengthWait. 70 | */ 71 | [function, uuid(9c0c13b9-1b33-445d-8adb-a8a7866a6c06)] 72 | interface nsIInputStreamLengthCallback : nsISupports 73 | { 74 | /** 75 | * Called to inform what the total length of the stream is. 76 | * 77 | * @param aStream 78 | * The stream whose asyncLengthWait method was called. 79 | * @param aLength 80 | * The stream's length. It can be -1 if the stream doesn't know its 81 | * length. For instance, this can happen for a pipe inputStream. 82 | */ 83 | void onInputStreamLengthReady(in nsIAsyncInputStreamLength aStream, 84 | in long long aLength); 85 | }; 86 | -------------------------------------------------------------------------------- /ns/nsIInputStreamPriority.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | #include "nsIRunnable.idl" 8 | 9 | [scriptable, uuid(daa45b24-98ee-4eb2-9cec-aad0bc023e9d)] 10 | interface nsIInputStreamPriority : nsISupports 11 | { 12 | /** 13 | * An input stream implementing this interface will dispatch runnable 14 | * events with this priority. See nsIRunnablePriority. 15 | */ 16 | attribute unsigned long priority; 17 | }; 18 | -------------------------------------------------------------------------------- /ns/nsIInputStreamPump.idl: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "nsIRequest.idl" 6 | 7 | interface nsIInputStream; 8 | interface nsISerialEventTarget; 9 | interface nsIStreamListener; 10 | 11 | /** 12 | * nsIInputStreamPump 13 | * 14 | * This interface provides a means to configure and use a input stream pump 15 | * instance. The input stream pump will asynchronously read from an input 16 | * stream, and push data to an nsIStreamListener instance. It utilizes the 17 | * current thread's nsIEventTarget in order to make reading from the stream 18 | * asynchronous. A different thread can be used if the pump also implements 19 | * nsIThreadRetargetableRequest. 20 | * 21 | * If the given stream supports nsIAsyncInputStream, then the stream pump will 22 | * call the stream's AsyncWait method to drive the stream listener. Otherwise, 23 | * the stream will be read on a background thread utilizing the stream 24 | * transport service. More details are provided below. 25 | */ 26 | [scriptable, uuid(400F5468-97E7-4d2b-9C65-A82AECC7AE82)] 27 | interface nsIInputStreamPump : nsIRequest 28 | { 29 | /** 30 | * Initialize the input stream pump. 31 | * 32 | * @param aStream 33 | * contains the data to be read. if the input stream is non-blocking, 34 | * then it will be QI'd to nsIAsyncInputStream. if the QI succeeds 35 | * then the stream will be read directly. otherwise, it will be read 36 | * on a background thread using the stream transport service. 37 | * @param aSegmentSize 38 | * if the stream transport service is used, then this parameter 39 | * specifies the segment size for the stream transport's buffer. 40 | * pass 0 to specify the default value. 41 | * @param aSegmentCount 42 | * if the stream transport service is used, then this parameter 43 | * specifies the segment count for the stream transport's buffer. 44 | * pass 0 to specify the default value. 45 | * @param aCloseWhenDone 46 | * if true, the input stream will be closed after it has been read. 47 | * @param aMainThreadTarget 48 | * a labeled main therad event target. 49 | */ 50 | void init(in nsIInputStream aStream, 51 | in unsigned long aSegmentSize, 52 | in unsigned long aSegmentCount, 53 | in boolean aCloseWhenDone, 54 | [optional] in nsISerialEventTarget aMainThreadTarget); 55 | 56 | /** 57 | * asyncRead causes the input stream to be read in chunks and delivered 58 | * asynchronously to the listener via OnDataAvailable. 59 | * 60 | * @param aListener 61 | * receives notifications. 62 | * @param aListenerContext 63 | * passed to listener methods. 64 | */ 65 | void asyncRead(in nsIStreamListener aListener); 66 | }; 67 | -------------------------------------------------------------------------------- /ns/nsIInputStreamTee.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsIInputStream.idl" 7 | 8 | interface nsIOutputStream; 9 | interface nsIEventTarget; 10 | 11 | /** 12 | * A nsIInputStreamTee is a wrapper for an input stream, that when read 13 | * reads the specified amount of data from its |source| and copies that 14 | * data to its |sink|. |sink| must be a blocking output stream. 15 | */ 16 | [scriptable, builtinclass, uuid(90a9d790-3bca-421e-a73b-98f68e13c917)] 17 | interface nsIInputStreamTee : nsIInputStream 18 | { 19 | attribute nsIInputStream source; 20 | attribute nsIOutputStream sink; 21 | 22 | /** 23 | * If |eventTarget| is set, copying to sink is done asynchronously using 24 | * the event-target (e.g. a thread). If |eventTarget| is not set, copying 25 | * to sink happens synchronously while reading from the source. 26 | */ 27 | attribute nsIEventTarget eventTarget; 28 | }; 29 | 30 | %{C++ 31 | // factory methods 32 | extern nsresult 33 | NS_NewInputStreamTee(nsIInputStream **tee, // read from this input stream 34 | nsIInputStream *source, 35 | nsIOutputStream *sink); 36 | 37 | extern nsresult 38 | NS_NewInputStreamTeeAsync(nsIInputStream **tee, // read from this input stream 39 | nsIInputStream *source, 40 | nsIOutputStream *sink, 41 | nsIEventTarget *eventTarget); 42 | %} 43 | -------------------------------------------------------------------------------- /ns/nsIInterfaceRequestor.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | #include "nsISupports.idl" 8 | 9 | /** 10 | * The nsIInterfaceRequestor interface defines a generic interface for 11 | * requesting interfaces that a given object might provide access to. 12 | * This is very similar to QueryInterface found in nsISupports. 13 | * The main difference is that interfaces returned from GetInterface() 14 | * are not required to provide a way back to the object implementing this 15 | * interface. The semantics of QI() dictate that given an interface A that 16 | * you QI() on to get to interface B, you must be able to QI on B to get back 17 | * to A. This interface however allows you to obtain an interface C from A 18 | * that may or most likely will not have the ability to get back to A. 19 | */ 20 | 21 | [scriptable, uuid(033A1470-8B2A-11d3-AF88-00A024FFC08C)] 22 | interface nsIInterfaceRequestor : nsISupports 23 | { 24 | /** 25 | * Retrieves the specified interface pointer. 26 | * 27 | * @param uuid The IID of the interface being requested. 28 | * @param result [out] The interface pointer to be filled in if 29 | * the interface is accessible. 30 | * @throws NS_NOINTERFACE - interface not accessible. 31 | * @throws NS_ERROR* - method failure. 32 | */ 33 | void getInterface(in nsIIDRef uuid, 34 | [iid_is(uuid),retval] out nsQIResult result); 35 | }; 36 | -------------------------------------------------------------------------------- /ns/nsIMsgCopyServiceListener.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | #include "MailNewsTypes2.idl" 8 | 9 | [scriptable, uuid(da6b9843-5464-4630-b121-c5970aa3d6ed)] 10 | interface nsIMsgCopyServiceListener : nsISupports { 11 | 12 | /** 13 | * Notify the observer that the message has started to be copied. This 14 | * method is called only once, at the beginning of a message 15 | * copyoperation. 16 | */ 17 | void onStartCopy(); 18 | 19 | /** 20 | * Notify the observer that progress as occurred for the message copy 21 | * @param aProgress - State of progress. 22 | * @param aProgressMax - Max progress. 23 | */ 24 | void onProgress(in uint32_t aProgress, 25 | in uint32_t aProgressMax); 26 | 27 | /** 28 | * Setting newly created message key. This method is tailored specifically 29 | * for nsIMsgCopyService::copyFileMessage() when saving Drafts/Templates. 30 | * We need to have a way to inform the client what's the key of the newly 31 | * created message. 32 | * @param aKey - Message key. 33 | */ 34 | void setMessageKey(in nsMsgKey aKey); 35 | 36 | /** 37 | * Getting the file message message ID. This method is tailored 38 | * specifically for nsIMsgCopyService::copyFileMessage() when saving 39 | * Drafts/Templates. In order to work with imap server which doesn't 40 | * support uidplus we have to use search command to retrieve the key of 41 | * newly created message. Message ID generated by the compose guarantee its 42 | * uniqueness. 43 | * NOTE: The only real implementor is nsImapMailFolder::GetMessageId 44 | */ 45 | AUTF8String getMessageId(); 46 | 47 | /** 48 | * Notify the observer that the message copied operation has completed. 49 | * This method is called regardless of whether the the operation was 50 | * successful. 51 | * @param aStatus - indicate whether the operation was succeeded 52 | */ 53 | void onStopCopy(in nsresult aStatus); 54 | }; 55 | -------------------------------------------------------------------------------- /ns/nsIMsgFilterCustomAction.idl: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "nsMsgFilterCore.idl" 6 | 7 | interface nsIMsgCopyServiceListener; 8 | interface nsIMsgWindow; 9 | interface nsIMsgDBHdr; 10 | 11 | /** 12 | * describes a custom action added to a message filter 13 | */ 14 | [scriptable,uuid(4699C41E-3671-436e-B6AE-4FD8106747E4)] 15 | interface nsIMsgFilterCustomAction : nsISupports 16 | { 17 | /* globally unique string to identify this filter action. 18 | * recommended form: ExtensionName@example.com#ActionName 19 | */ 20 | readonly attribute ACString id; 21 | 22 | /* action name to display in action list. This should be localized. */ 23 | readonly attribute AString name; 24 | 25 | /** 26 | * Is this custom action valid for a particular filter type? 27 | * 28 | * @param type the filter type 29 | * @param scope the search scope 30 | * 31 | * @return true if valid 32 | */ 33 | boolean isValidForType(in nsMsgFilterTypeType type, in nsMsgSearchScopeValue scope); 34 | 35 | /** 36 | * After the user inputs a particular action value for the action, determine 37 | * if that value is valid. 38 | * 39 | * @param actionValue The value entered. 40 | * @param actionFolder Folder in the filter list 41 | * @param filterType Filter Type (Manual, OfflineMail, etc.) 42 | * 43 | * @return errorMessage A localized message to display if invalid 44 | * Set to null if the actionValue is valid 45 | */ 46 | AUTF8String validateActionValue(in AUTF8String actionValue, 47 | in nsIMsgFolder actionFolder, 48 | in nsMsgFilterTypeType filterType); 49 | 50 | /* allow duplicate actions in the same filter list? Default No. */ 51 | attribute boolean allowDuplicates; 52 | 53 | /* 54 | * The custom action itself 55 | * 56 | * Generally for the applyAction method, folder-based methods give correct 57 | * results and are preferred if available. Otherwise, be careful 58 | * that the action does correct notifications to maintain counts, and correct 59 | * manipulations of both IMAP and local non-database storage of message 60 | * metadata. 61 | */ 62 | 63 | /** 64 | * Apply the custom action to an array of messages 65 | * 66 | * @param msgHdrs array of nsIMsgDBHdr objects of messages 67 | * @param actionValue user-set value to use in the action 68 | * @param copyListener calling method (filterType Manual only) 69 | * @param filterType type of filter being applied 70 | * @param msgWindow message window 71 | */ 72 | 73 | void applyAction(in Array msgHdrs, 74 | in AUTF8String actionValue, 75 | in nsIMsgCopyServiceListener copyListener, 76 | in nsMsgFilterTypeType filterType, 77 | in nsIMsgWindow msgWindow); 78 | 79 | /* does this action start an async action? If so, a copy listener must 80 | * be used to continue filter processing after the action. This only 81 | * applies to after-the-fact (manual) filters. Call OnStopCopy when done 82 | * using the copyListener to continue. 83 | */ 84 | readonly attribute boolean isAsync; 85 | 86 | /// Does this action need the message body? 87 | readonly attribute boolean needsBody; 88 | }; 89 | -------------------------------------------------------------------------------- /ns/nsIMsgFilterService.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | #include "nsMsgFilterCore.idl" 8 | 9 | interface nsIMsgFilterList; 10 | interface nsIMsgWindow; 11 | interface nsIMsgFilterCustomAction; 12 | interface nsIFile; 13 | interface nsIMsgFolder; 14 | interface nsIMsgSearchCustomTerm; 15 | interface nsIMsgOperationListener; 16 | 17 | [scriptable, uuid(78a74023-1692-4567-8d72-9ca58fbbd427)] 18 | interface nsIMsgFilterService : nsISupports { 19 | 20 | nsIMsgFilterList OpenFilterList(in nsIFile filterFile, in nsIMsgFolder rootFolder, in nsIMsgWindow msgWindow); 21 | void CloseFilterList(in nsIMsgFilterList filterList); 22 | 23 | void SaveFilterList(in nsIMsgFilterList filterList, 24 | in nsIFile filterFile); 25 | 26 | void CancelFilterList(in nsIMsgFilterList filterList); 27 | nsIMsgFilterList getTempFilterList(in nsIMsgFolder aFolder); 28 | void applyFiltersToFolders(in nsIMsgFilterList aFilterList, 29 | in Array aFolders, 30 | in nsIMsgWindow aMsgWindow, 31 | [optional] in nsIMsgOperationListener aCallback); 32 | 33 | /** 34 | * Apply filters to a specific list of messages in a folder. 35 | * @param aFilterType The type of filter to match against 36 | * @param aMsgHdrList The list of message headers (nsIMsgDBHdr objects) 37 | * @param aFolder The folder the messages belong to 38 | * @param aMsgWindow A UI window for attaching progress/dialogs 39 | * @param aCallback A listener that gets notified of any filtering error 40 | */ 41 | void applyFilters(in nsMsgFilterTypeType aFilterType, 42 | in Array aMsgHdrList, 43 | in nsIMsgFolder aFolder, 44 | in nsIMsgWindow aMsgWindow, 45 | [optional] in nsIMsgOperationListener aCallback); 46 | 47 | /** 48 | * Add a custom filter action. 49 | * 50 | * @param aAction the custom action to add 51 | */ 52 | void addCustomAction(in nsIMsgFilterCustomAction aAction); 53 | 54 | /** 55 | * get the list of custom actions 56 | * 57 | * @return an array of nsIMsgFilterCustomAction objects 58 | */ 59 | Array getCustomActions(); 60 | 61 | /** 62 | * Lookup a custom action given its id. 63 | * 64 | * @param id unique identifier for a particular custom action 65 | * 66 | * @return the custom action, or null if not found 67 | */ 68 | nsIMsgFilterCustomAction getCustomAction(in ACString id); 69 | 70 | /** 71 | * Add a custom search term. 72 | * 73 | * @param aTerm the custom term to add 74 | */ 75 | void addCustomTerm(in nsIMsgSearchCustomTerm aTerm); 76 | 77 | /** 78 | * get the list of custom search terms 79 | * 80 | * @return an array of nsIMsgSearchCustomTerm objects 81 | */ 82 | Array getCustomTerms(); 83 | 84 | /** 85 | * Lookup a custom search term given its id. 86 | * 87 | * @param id unique identifier for a particular custom search term 88 | * 89 | * @return the custom search term, or null if not found 90 | */ 91 | nsIMsgSearchCustomTerm getCustomTerm(in ACString id); 92 | 93 | /** 94 | * Translate the filter type flag into human readable type names. 95 | * In case of multiple flag they are delimited by '&'. 96 | * 97 | * @param filterType nsMsgFilterType flags of filter type 98 | * 99 | * @return A string describing the filter type. 100 | */ 101 | ACString filterTypeName(in nsMsgFilterTypeType filterType); 102 | }; 103 | -------------------------------------------------------------------------------- /ns/nsIMsgHdr.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | #include "MailNewsTypes2.idl" 9 | 10 | interface nsIMsgFolder; 11 | interface nsIUTF8StringEnumerator; 12 | 13 | [scriptable, uuid(3c11ddbe-c805-40c5-b9c9-d065fad5d0be)] 14 | interface nsIMsgDBHdr : nsISupports 15 | { 16 | void setStringProperty(in string propertyName, in AUTF8String propertyValue); 17 | AUTF8String getStringProperty(in string propertyName); 18 | unsigned long getUint32Property(in string propertyName); 19 | void setUint32Property(in string propertyName, 20 | in unsigned long propertyVal); 21 | 22 | // accessors, to make our JS cleaner 23 | readonly attribute boolean isRead; 24 | readonly attribute boolean isFlagged; 25 | 26 | // Special accessor that checks if a message is part of an ignored subthread 27 | readonly attribute boolean isKilled; 28 | 29 | // Mark message routines 30 | void markRead(in boolean read); 31 | void markFlagged(in boolean flagged); 32 | void markHasAttachments(in boolean hasAttachments); 33 | 34 | attribute nsMsgPriorityValue priority; 35 | 36 | /* flag handling routines */ 37 | attribute unsigned long flags; 38 | unsigned long orFlags(in unsigned long flags); 39 | unsigned long andFlags(in unsigned long flags); 40 | 41 | /* various threading stuff */ 42 | attribute nsMsgKey threadId; 43 | attribute nsMsgKey messageKey; 44 | attribute nsMsgKey threadParent; 45 | 46 | /* meta information about the message, learned from reading the message */ 47 | 48 | /** 49 | * For "Offline" supporting folders (IMAP, NNTP), .messageSize is 50 | * the size of the original message on the server. 51 | * For Local folders, this is the exact size of the message as written to 52 | * the msgStore. 53 | * See also Bug 1764857. 54 | */ 55 | attribute unsigned long messageSize; 56 | attribute unsigned long lineCount; 57 | /** 58 | * The offset into the local folder/offline store of the message. This 59 | * will be pluggable store-dependent, e.g., for mail dir it should 60 | * always be 0. 61 | */ 62 | attribute unsigned long long messageOffset; 63 | /** 64 | * For "Offline" supporting folders (IMAP, NNTP): .offlineMessageSize is 65 | * the exact size of the local copy of the message in the msgStore. 66 | * If the message is not flagged Offline, this will be zero or unset. 67 | * For Local folders, this is unset or zero. 68 | * See also Bug 1764857. 69 | */ 70 | attribute unsigned long offlineMessageSize; 71 | /* common headers */ 72 | attribute PRTime date; 73 | readonly attribute unsigned long dateInSeconds; 74 | attribute AUTF8String messageId; 75 | attribute AUTF8String ccList; 76 | attribute AUTF8String bccList; 77 | attribute AUTF8String author; 78 | attribute AUTF8String subject; 79 | attribute AUTF8String recipients; 80 | 81 | /* anything below here still has to be fixed */ 82 | void setReferences(in AUTF8String references); 83 | readonly attribute unsigned short numReferences; 84 | AUTF8String getStringReference(in long refNum); 85 | 86 | readonly attribute AString mime2DecodedAuthor; 87 | readonly attribute AString mime2DecodedSubject; 88 | readonly attribute AString mime2DecodedRecipients; 89 | 90 | Array getAuthorCollationKey(); 91 | Array getSubjectCollationKey(); 92 | Array getRecipientsCollationKey(); 93 | 94 | attribute string charset; 95 | 96 | /** 97 | * Returns the effective character set for the message (@ref charset). 98 | * For NNTP, if there is no specific set defined for the message, 99 | * the character set of the server instead. 100 | */ 101 | readonly attribute ACString effectiveCharset; 102 | 103 | attribute string accountKey; 104 | readonly attribute nsIMsgFolder folder; 105 | 106 | /// Array of names of all database properties in the header. 107 | readonly attribute Array properties; 108 | }; 109 | /* *******************************************************************************/ 110 | -------------------------------------------------------------------------------- /ns/nsIMsgSearchCustomTerm.idl: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "nsMsgSearchCore.idl" 6 | 7 | /** 8 | * describes a custom term added to a message search or filter 9 | */ 10 | [scriptable,uuid(925DB5AA-21AF-494c-8652-984BC7BAD13A)] 11 | interface nsIMsgSearchCustomTerm : nsISupports 12 | { 13 | /** 14 | * globally unique string to identify this search term. 15 | * recommended form: ExtensionName@example.com#TermName 16 | * Commas and quotes are not allowed, the id must not 17 | * parse to an integer, and names of standard search 18 | * attributes in SearchAttribEntryTable in nsMsgSearchTerm.cpp 19 | * are not allowed. 20 | */ 21 | readonly attribute ACString id; 22 | 23 | /// name to display in term list. This should be localized. */ 24 | readonly attribute AString name; 25 | 26 | /// Does this term need the message body? 27 | readonly attribute boolean needsBody; 28 | 29 | /** 30 | * Is this custom term enabled? 31 | * 32 | * @param scope search scope (nsMsgSearchScope) 33 | * @param op search operator (nsMsgSearchOp). If null, determine 34 | * if term is available for any operator. 35 | * 36 | * @return true if enabled 37 | */ 38 | boolean getEnabled(in nsMsgSearchScopeValue scope, 39 | in nsMsgSearchOpValue op); 40 | 41 | /** 42 | * Is this custom term available? 43 | * 44 | * @param scope search scope (nsMsgSearchScope) 45 | * @param op search operator (nsMsgSearchOp). If null, determine 46 | * if term is available for any operator. 47 | * 48 | * @return true if available 49 | */ 50 | boolean getAvailable(in nsMsgSearchScopeValue scope, 51 | in nsMsgSearchOpValue op); 52 | 53 | /** 54 | * List the valid operators for this term. 55 | * 56 | * @param scope search scope (nsMsgSearchScope) 57 | * 58 | * @return array of operators 59 | */ 60 | Array getAvailableOperators(in nsMsgSearchScopeValue scope); 61 | 62 | /** 63 | * Apply the custom search term to a message 64 | * 65 | * @param msgHdr header database reference representing the message 66 | * @param searchValue user-set value to use in the search 67 | * @param searchOp search operator (Contains, IsHigherThan, etc.) 68 | * 69 | * @return true if the term matches the message, else false 70 | */ 71 | 72 | boolean match(in nsIMsgDBHdr msgHdr, 73 | in AUTF8String searchValue, 74 | in nsMsgSearchOpValue searchOp); 75 | }; 76 | -------------------------------------------------------------------------------- /ns/nsIMsgWindow.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | interface nsIMsgStatusFeedback; 9 | interface nsIMsgFolder; 10 | interface nsITransactionManager; 11 | interface nsIDocShell; 12 | interface mozIDOMWindowProxy; 13 | interface nsIPrompt; 14 | interface nsIInterfaceRequestor; 15 | interface nsIAuthPrompt; 16 | interface nsIPrincipal; 17 | 18 | [scriptable, uuid(a846fe48-4022-4296-a1c4-1dcd7eaecfe5)] 19 | interface nsIMsgWindow : nsISupports { 20 | attribute nsIMsgStatusFeedback statusFeedback; 21 | attribute nsITransactionManager transactionManager; 22 | attribute nsIMsgFolder openFolder; 23 | 24 | /** 25 | * @note Setting this attribute has various side effects, including 26 | * wiring up this object as the parent nsIURIContentListener for the 27 | * passed-in docshell as well as setting the message content policy service 28 | * to listen for OnLocationChange notifications. 29 | */ 30 | attribute nsIDocShell rootDocShell; 31 | 32 | /** 33 | * @note Small helper function used to optimize our use of a weak reference 34 | * on the message window docshell. Under no circumstances should you be 35 | * holding on to the docshell returned here outside the scope of your routine. 36 | */ 37 | readonly attribute nsIDocShell messageWindowDocShell; 38 | 39 | /** 40 | Has a running url been stopped? If you care about checking 41 | this flag, you need to clear it before you start your operation since 42 | there's no convenient place to clear it. 43 | */ 44 | attribute boolean stopped; 45 | 46 | attribute mozIDOMWindowProxy domWindow; 47 | 48 | void StopUrls(); 49 | 50 | /** 51 | when the msg window is being unloaded from the content window, 52 | we can use this notification to force a flush on anything the 53 | msg window hangs on too. For some reason xpconnect is still hanging 54 | onto the msg window even though all of our objects have let go of it 55 | this forces a release... 56 | */ 57 | void closeWindow(); 58 | }; 59 | -------------------------------------------------------------------------------- /ns/nsIProperties.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | /* 9 | * Simple mapping service interface. 10 | */ 11 | 12 | [scriptable, uuid(78650582-4e93-4b60-8e85-26ebd3eb14ca)] 13 | interface nsIProperties : nsISupports 14 | { 15 | /** 16 | * Gets a property with a given name. 17 | * 18 | * @throws NS_ERROR_FAILURE if a property with that name doesn't exist. 19 | * @throws NS_ERROR_NO_INTERFACE if the found property fails to QI to the 20 | * given iid. 21 | */ 22 | void get(in string prop, in nsIIDRef iid, 23 | [iid_is(iid),retval] out nsQIResult result); 24 | 25 | /** 26 | * Sets a property with a given name to a given value. 27 | */ 28 | void set(in string prop, in nsISupports value); 29 | 30 | /** 31 | * Returns true if the property with the given name exists. 32 | */ 33 | boolean has(in string prop); 34 | 35 | /** 36 | * Undefines a property. 37 | * @throws NS_ERROR_FAILURE if a property with that name doesn't 38 | * already exist. 39 | */ 40 | void undefine(in string prop); 41 | 42 | /** 43 | * Returns an array of the keys. 44 | */ 45 | Array getKeys(); 46 | }; 47 | -------------------------------------------------------------------------------- /ns/nsIScriptableInputStream.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | interface nsIInputStream; 9 | 10 | /** 11 | * nsIScriptableInputStream provides scriptable access to an nsIInputStream 12 | * instance. 13 | */ 14 | [scriptable, uuid(3fce9015-472a-4080-ac3e-cd875dbe361e)] 15 | interface nsIScriptableInputStream : nsISupports 16 | { 17 | /** 18 | * Closes the stream. 19 | */ 20 | void close(); 21 | 22 | /** 23 | * Wrap the given nsIInputStream with this nsIScriptableInputStream. 24 | * 25 | * @param aInputStream parameter providing the stream to wrap 26 | */ 27 | void init(in nsIInputStream aInputStream); 28 | 29 | /** 30 | * Return the number of bytes currently available in the stream 31 | * 32 | * @return the number of bytes 33 | * 34 | * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed 35 | */ 36 | unsigned long long available(); 37 | 38 | /** 39 | * Read data from the stream. 40 | * 41 | * WARNING: If the data contains a null byte, then this method will return 42 | * a truncated string. 43 | * 44 | * @param aCount the maximum number of bytes to read 45 | * 46 | * @return the data, which will be an empty string if the stream is at EOF. 47 | * 48 | * @throws NS_BASE_STREAM_CLOSED if called after the stream has been closed 49 | * @throws NS_ERROR_NOT_INITIALIZED if init was not called 50 | */ 51 | string read(in unsigned long aCount); 52 | 53 | /** 54 | * Read data from the stream, including NULL bytes. 55 | * 56 | * @param aCount the maximum number of bytes to read. 57 | * 58 | * @return the data from the stream, which will be an empty string if EOF 59 | * has been reached. 60 | * 61 | * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream 62 | * would block the calling thread (non-blocking mode only). 63 | * @throws NS_ERROR_FAILURE if there are not enough bytes available to read 64 | * aCount amount of data. 65 | */ 66 | ACString readBytes(in unsigned long aCount); 67 | }; 68 | -------------------------------------------------------------------------------- /ns/nsISpeculativeConnect.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | interface nsIPrincipal; 9 | interface nsIURI; 10 | interface nsIInterfaceRequestor; 11 | 12 | %{C++ 13 | namespace mozilla { 14 | 15 | class OriginAttributes; 16 | 17 | } 18 | %} 19 | 20 | native OriginAttributes(mozilla::OriginAttributes&&); 21 | 22 | [scriptable, builtinclass, uuid(d74a17ac-5b8a-4824-a309-b1f04a3c4aed)] 23 | interface nsISpeculativeConnect : nsISupports 24 | { 25 | /** 26 | * Called as a hint to indicate a new transaction for the URI is likely coming 27 | * soon. The implementer may use this information to start a TCP 28 | * and/or SSL level handshake for that resource immediately so that it is 29 | * ready and/or progressed when the transaction is actually submitted. 30 | * 31 | * No obligation is taken on by the implementer, nor is the submitter obligated 32 | * to actually open the new channel. 33 | * 34 | * @param aURI the URI of the hinted transaction 35 | * @param aPrincipal the principal that will be used for opening the 36 | * channel of the hinted transaction. 37 | * @param aCallbacks any security callbacks for use with SSL for interfaces. 38 | * May be null. 39 | * @param aAnonymous indicates if this is an anonymous connection. 40 | * 41 | */ 42 | void speculativeConnect(in nsIURI aURI, 43 | in nsIPrincipal aPrincipal, 44 | in nsIInterfaceRequestor aCallbacks, 45 | in boolean aAnonymous); 46 | 47 | /** 48 | * This method is similar to speculativeConnect, but it use 49 | * the partition key of the originAttributes directly to create the 50 | * connection. 51 | */ 52 | [implicit_jscontext] 53 | void speculativeConnectWithOriginAttributes( 54 | in nsIURI aURI, 55 | in jsval originAttributes, 56 | in nsIInterfaceRequestor aCallbacks, 57 | in boolean aAnonymous); 58 | 59 | [notxpcom] 60 | void speculativeConnectWithOriginAttributesNative( 61 | in nsIURI aURI, 62 | in OriginAttributes originAttributes, 63 | in nsIInterfaceRequestor aCallbacks, 64 | in boolean aAnonymous); 65 | }; 66 | 67 | /** 68 | * This is used to override the default values for various values (documented 69 | * inline) to determine whether or not to actually make a speculative 70 | * connection. 71 | */ 72 | [uuid(1040ebe3-6ed1-45a6-8587-995e082518d7)] 73 | interface nsISpeculativeConnectionOverrider : nsISupports 74 | { 75 | /** 76 | * Used to determine the maximum number of unused speculative connections 77 | * we will have open for a host at any one time 78 | */ 79 | [infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit; 80 | 81 | /** 82 | * Used to determine if we will ignore the existence of any currently idle 83 | * connections when we decide whether or not to make a speculative 84 | * connection. 85 | */ 86 | [infallible] readonly attribute boolean ignoreIdle; 87 | 88 | /* 89 | * Used by the Predictor to gather telemetry data on speculative connection 90 | * usage. 91 | */ 92 | [infallible] readonly attribute boolean isFromPredictor; 93 | 94 | /** 95 | * by default speculative connections are not made to RFC 1918 addresses 96 | */ 97 | [infallible] readonly attribute boolean allow1918; 98 | }; 99 | -------------------------------------------------------------------------------- /ns/nsIStyleSheetService.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | /* interface for managing user and user-agent style sheets */ 7 | 8 | #include "nsISupports.idl" 9 | 10 | interface nsIPreloadedStyleSheet; 11 | interface nsIURI; 12 | 13 | /* 14 | * nsIStyleSheetService allows extensions or embeddors to add to the 15 | * built-in list of user or agent style sheets. 16 | */ 17 | 18 | [scriptable, uuid(4de68896-e8eb-41de-8237-a797b570ac4a)] 19 | interface nsIStyleSheetService : nsISupports 20 | { 21 | const unsigned long AGENT_SHEET = 0; 22 | const unsigned long USER_SHEET = 1; 23 | const unsigned long AUTHOR_SHEET = 2; 24 | 25 | /** 26 | * Synchronously loads a style sheet from |sheetURI| and adds it to the list 27 | * of user or agent style sheets. 28 | * 29 | * A user sheet loaded via this API will come before userContent.css and 30 | * userChrome.css in the cascade (so the rules in it will have lower 31 | * precedence than rules in those sheets). 32 | * 33 | * An agent sheet loaded via this API will come after ua.css in the cascade 34 | * (so the rules in it will have higher precedence than rules in ua.css). 35 | * 36 | * The relative ordering of two user or two agent sheets loaded via 37 | * this API is undefined. 38 | * 39 | * Sheets added via this API take effect on all documents, including 40 | * already-loaded ones, immediately. 41 | */ 42 | void loadAndRegisterSheet(in nsIURI sheetURI, in unsigned long type); 43 | 44 | /** 45 | * Returns true if a style sheet at |sheetURI| has previously been 46 | * added to the list of style sheets specified by |type|. 47 | */ 48 | boolean sheetRegistered(in nsIURI sheetURI, in unsigned long type); 49 | 50 | /** 51 | * Synchronously loads a style sheet from |sheetURI| and returns the 52 | * new style sheet object. Can be used with nsIDOMWindowUtils.addSheet. 53 | */ 54 | nsIPreloadedStyleSheet preloadSheet(in nsIURI sheetURI, 55 | in unsigned long type); 56 | 57 | /** 58 | * Asynchronously loads a style sheet from |sheetURI| and returns a Promise 59 | * which resolves to the new style sheet object, which can be used with 60 | * nsIDOMWindowUtils.addSheet, when it has completed loading. 61 | */ 62 | [implicit_jscontext] 63 | jsval preloadSheetAsync(in nsIURI sheetURI, in unsigned long type); 64 | 65 | /** 66 | * Remove the style sheet at |sheetURI| from the list of style sheets 67 | * specified by |type|. The removal takes effect immediately, even for 68 | * already-loaded documents. 69 | */ 70 | void unregisterSheet(in nsIURI sheetURI, in unsigned long type); 71 | }; 72 | -------------------------------------------------------------------------------- /ns/nsISupports.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | /** 7 | * The mother of all xpcom interfaces. 8 | */ 9 | 10 | #include "nsrootidl.idl" 11 | 12 | /** 13 | * Basic component object model interface. Objects which implement 14 | * this interface support runtime interface discovery (QueryInterface) 15 | * and a reference counted memory model (AddRef/Release). This is 16 | * modelled after the win32 IUnknown API. 17 | * 18 | * Historically, nsISupports needed to be binary compatible with COM's 19 | * IUnknown, so the IID of nsISupports is the same as it. That is no 20 | * longer a goal, and hopefully nobody depends on it. We may break 21 | * this compatibility at any time. 22 | */ 23 | [scriptable, uuid(00000000-0000-0000-c000-000000000046)] 24 | interface nsISupports { 25 | 26 | /** 27 | * A run time mechanism for interface discovery. 28 | * @param aIID [in] A requested interface IID 29 | * @param aInstancePtr [out] A pointer to an interface pointer to 30 | * receive the result. 31 | * @return NS_OK if the interface is supported by the associated 32 | * instance, NS_NOINTERFACE if it is not. 33 | * 34 | * aInstancePtr must not be null. 35 | */ 36 | void QueryInterface(in nsIIDRef aIID, 37 | [iid_is(aIID), retval] out nsQIResult aInstancePtr); 38 | 39 | /** 40 | * Increases the reference count for this interface. 41 | * The associated instance will not be deleted unless 42 | * the reference count is returned to zero. 43 | * 44 | * @return The resulting reference count. 45 | */ 46 | [noscript, notxpcom] MozExternalRefCountType AddRef(); 47 | 48 | /** 49 | * Decreases the reference count for this interface. 50 | * Generally, if the reference count returns to zero, 51 | * the associated instance is deleted. 52 | * 53 | * @return The resulting reference count. 54 | */ 55 | [noscript, notxpcom] MozExternalRefCountType Release(); 56 | }; 57 | 58 | %{C++ 59 | #include "nsISupportsUtils.h" 60 | %} 61 | -------------------------------------------------------------------------------- /ns/nsIUnicharInputStream.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | interface nsIUnicharInputStream; 9 | interface nsIInputStream; 10 | 11 | %{C++ 12 | /** 13 | * The signature of the writer function passed to ReadSegments. This 14 | * is the "consumer" of data that gets read from the stream's buffer. 15 | * 16 | * @param aInStream stream being read 17 | * @param aClosure opaque parameter passed to ReadSegments 18 | * @param aFromSegment pointer to memory owned by the input stream 19 | * @param aToOffset number of UTF-16 code units already read 20 | * (since ReadSegments was called) 21 | * @param aCount length of fromSegment 22 | * @param aWriteCount number of UTF-16 code units read 23 | * 24 | * Implementers should return the following: 25 | * 26 | * @throws if not interested in consuming any data 27 | * 28 | * Errors are never passed to the caller of ReadSegments. 29 | * 30 | * NOTE: returning NS_OK and (*aWriteCount = 0) has undefined behavior. 31 | */ 32 | typedef nsresult (*nsWriteUnicharSegmentFun)(nsIUnicharInputStream *aInStream, 33 | void *aClosure, 34 | const char16_t *aFromSegment, 35 | uint32_t aToOffset, 36 | uint32_t aCount, 37 | uint32_t *aWriteCount); 38 | %} 39 | native nsWriteUnicharSegmentFun(nsWriteUnicharSegmentFun); 40 | 41 | /** 42 | * Abstract UTF-16 input stream 43 | * @see nsIInputStream 44 | */ 45 | [scriptable, builtinclass, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)] 46 | interface nsIUnicharInputStream : nsISupports { 47 | /** 48 | * Reads into a caller-provided array. 49 | * 50 | * @return The number of utf-16 code units that were successfully read. 51 | * May be less than aCount, even if there is more data in the input 52 | * stream. A return value of 0 means EOF. 53 | * 54 | * @note To read more than 2^32 code units, call this method multiple times. 55 | */ 56 | [noscript] unsigned long read([array, size_is(aCount)] in char16_t aBuf, 57 | in unsigned long aCount); 58 | 59 | /** 60 | * Low-level read method that has access to the stream's underlying buffer. 61 | * The writer function may be called multiple times for segmented buffers. 62 | * ReadSegments is expected to keep calling the writer until either there is 63 | * nothing left to read or the writer returns an error. ReadSegments should 64 | * not call the writer with zero UTF-16 code units to consume. 65 | * 66 | * @param aWriter the "consumer" of the data to be read 67 | * @param aClosure opaque parameter passed to writer 68 | * @param aCount the maximum number of UTF-16 code units to be read 69 | * 70 | * @return number of UTF-16 code units read (may be less than aCount) 71 | * @return 0 if reached end of file (or if aWriter refused to consume data) 72 | * 73 | * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream would 74 | * block the calling thread (non-blocking mode only) 75 | * @throws on failure 76 | * 77 | * NOTE: this function may be unimplemented if a stream has no underlying 78 | * buffer 79 | */ 80 | [noscript] unsigned long readSegments(in nsWriteUnicharSegmentFun aWriter, 81 | in voidPtr aClosure, 82 | in unsigned long aCount); 83 | 84 | /** 85 | * Read into a string object. 86 | * 87 | * @param aCount The number of UTF-16 code units that should be read 88 | * @return The number of UTF-16 code units that were read. 89 | */ 90 | unsigned long readString(in unsigned long aCount, out AString aString); 91 | 92 | /** 93 | * Close the stream and free associated resources. This also closes the 94 | * underlying stream, if any. 95 | */ 96 | void close(); 97 | }; 98 | -------------------------------------------------------------------------------- /ns/nsIUnicharOutputStream.idl: -------------------------------------------------------------------------------- 1 | /* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | /** 9 | * An interface that allows writing unicode data. 10 | */ 11 | [scriptable, uuid(2d00b1bb-8b21-4a63-bcc6-7213f513ac2e)] 12 | interface nsIUnicharOutputStream : nsISupports 13 | { 14 | /** 15 | * Write a single character to the stream. When writing many characters, 16 | * prefer the string-taking write method. 17 | * 18 | * @retval true The character was written successfully 19 | * @retval false Not all bytes of the character could be written. 20 | */ 21 | boolean write(in unsigned long aCount, 22 | [const, array, size_is(aCount)] in char16_t c); 23 | 24 | /** 25 | * Write a string to the stream. 26 | * 27 | * @retval true The string was written successfully 28 | * @retval false Not all bytes of the string could be written. 29 | */ 30 | boolean writeString(in AString str); 31 | 32 | /** 33 | * Flush the stream. This finishes the conversion and writes any bytes that 34 | * finish the current byte sequence. 35 | * 36 | * It does NOT flush the underlying stream. 37 | */ 38 | void flush(); 39 | 40 | /** 41 | * Close the stream and free associated resources. This also closes the 42 | * underlying stream. 43 | */ 44 | void close(); 45 | }; 46 | -------------------------------------------------------------------------------- /ns/nsIVersionComparator.idl: -------------------------------------------------------------------------------- 1 | /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #include "nsISupports.idl" 7 | 8 | /** 9 | * Version strings are dot-separated sequences of version-parts. 10 | * 11 | * A version-part consists of up to four parts, all of which are optional: 12 | * 13 | * 14 | * 15 | * A version-part may also consist of a single asterisk "*" which indicates 16 | * "infinity". 17 | * 18 | * Numbers are base-10, and are zero if left out. 19 | * Strings are compared bytewise. 20 | * 21 | * For additional backwards compatibility, if "string-b" is "+" then 22 | * "number-a" is incremented by 1 and "string-b" becomes "pre". 23 | * 24 | * 1.0pre1 25 | * < 1.0pre2 26 | * < 1.0 == 1.0.0 == 1.0.0.0 27 | * < 1.1pre == 1.1pre0 == 1.0+ 28 | * < 1.1pre1a 29 | * < 1.1pre1 30 | * < 1.1pre10a 31 | * < 1.1pre10 32 | * 33 | * Although not required by this interface, it is recommended that 34 | * numbers remain within the limits of a signed char, i.e. -127 to 128. 35 | */ 36 | [builtinclass, scriptable, uuid(e6cd620a-edbb-41d2-9e42-9a2ffc8107f3)] 37 | interface nsIVersionComparator : nsISupports 38 | { 39 | /** 40 | * Compare two version strings 41 | * @param A The first version 42 | * @param B The second version 43 | * @returns < 0 if A < B 44 | * = 0 if A == B 45 | * > 0 if A > B 46 | */ 47 | long compare(in ACString A, in ACString B); 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qnote", 3 | "version": "0.14.0", 4 | "description": "QNote addon from Thunderbird", 5 | "main": "index.js", 6 | "dependencies": { 7 | "typescript": "^5.5.4" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "", 14 | "devDependencies": { 15 | "eslint-config-prettier": "^9.1.0", 16 | "ts-loader": "^9.5.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/ResourceUrl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided by the addon-developer-support repository at 3 | * https://github.com/thundernest/addon-developer-support 4 | * 5 | * Version 1.0 6 | * - initial release 7 | * 8 | * Authors: 9 | * - John Bieling (john@thunderbird.net) 10 | * - Arnd Issler (email@arndissler.net) 11 | * 12 | * This Source Code Form is subject to the terms of the Mozilla Public 13 | * License, v. 2.0. If a copy of the MPL was not distributed with this 14 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 15 | */ 16 | 17 | "use strict"; 18 | 19 | (function (exports) { 20 | 21 | // Import some things we need. 22 | var { ExtensionUtils } = ChromeUtils.importESModule("resource://gre/modules/ExtensionUtils.sys.mjs"); 23 | // var { Services } = ChromeUtils.importESModule("resource://gre/modules/Services.sys.mjs"); 24 | var { ExtensionError } = ExtensionUtils; 25 | 26 | let resourceUrls = new Set(); 27 | 28 | class ResourceUrl extends ExtensionCommon.ExtensionAPI { 29 | // The API implementation. 30 | getAPI(context) { 31 | return { 32 | ResourceUrl: { 33 | register(namespace, folder) { 34 | const resProto = Cc[ 35 | "@mozilla.org/network/protocol;1?name=resource" 36 | ].getService(Ci.nsISubstitutingProtocolHandler); 37 | 38 | if (resProto.hasSubstitution(namespace)) { 39 | throw new ExtensionError(`There is already a resource:// url for the namespace "${namespace}"`); 40 | } 41 | let uri = Services.io.newURI( 42 | folder || ".", 43 | null, 44 | context.extension.rootURI 45 | ); 46 | resProto.setSubstitutionWithFlags( 47 | namespace, 48 | uri, 49 | resProto.ALLOW_CONTENT_ACCESS 50 | ); 51 | resourceUrls.add(namespace); 52 | } 53 | } 54 | }; 55 | } 56 | 57 | onShutdown(isAppShutdown) { 58 | if (isAppShutdown) { 59 | return; // the application gets unloaded anyway 60 | } 61 | 62 | // Unload JSMs of this add-on 63 | for (let module of Cu.loadedModules) { 64 | let [schema, , namespace] = module.split("/"); 65 | if (schema == "resource:" && resourceUrls.has(namespace)) { 66 | console.log("Unloading module", module); 67 | Cu.unload(module); 68 | } 69 | } 70 | 71 | // Flush all caches 72 | Services.obs.notifyObservers(null, "startupcache-invalidate"); 73 | 74 | const resProto = Cc[ 75 | "@mozilla.org/network/protocol;1?name=resource" 76 | ].getService(Ci.nsISubstitutingProtocolHandler); 77 | 78 | resourceUrls.forEach(namespace => { 79 | console.log("Unloading namespace", namespace); 80 | resProto.setSubstitution(namespace, null); 81 | }); 82 | } 83 | } 84 | 85 | exports.ResourceUrl = ResourceUrl; 86 | })(this); 87 | -------------------------------------------------------------------------------- /src/api/legacy.ts: -------------------------------------------------------------------------------- 1 | var api = ChromeUtils.importESModule("resource://qnote/modules-exp/api.mjs?version=version"); 2 | 3 | var legacy = class extends ExtensionCommon.ExtensionAPI { 4 | getAPI() { 5 | return { 6 | legacy: api.LegacyAPI 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/qnote.ts: -------------------------------------------------------------------------------- 1 | var api = ChromeUtils.importESModule("resource://qnote/modules-exp/api.mjs?version=version"); 2 | 3 | var qnote = class extends ExtensionCommon.ExtensionAPI { 4 | getAPI() { 5 | return { 6 | qnote: api.QNoteFileAPI 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/xnote.ts: -------------------------------------------------------------------------------- 1 | var api = ChromeUtils.importESModule("resource://qnote/modules-exp/api.mjs?version=version"); 2 | 3 | var xnote = class extends ExtensionCommon.ExtensionAPI { 4 | getAPI() { 5 | return { 6 | xnote: api.XNoteFileAPI 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | QNote background 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/html/background.css: -------------------------------------------------------------------------------- 1 | /* .qnote-insidenote pre { 2 | all: initial !important; 3 | white-space: pre !important; 4 | } */ 5 | 6 | /* Reduce svg image size because it occupies too much space in column */ 7 | #qnoteButton > img, .qnote-column > img { 8 | width: 16px; 9 | height: 16px; 10 | } 11 | 12 | .qnote-insidenote { 13 | cursor: context-menu; 14 | box-shadow: 2px 2px 2px 1px hsla(210,4%,10%,.15); 15 | } 16 | 17 | .qnote-title { 18 | color: black; 19 | background: url('../images/title-background.png'); 20 | background-repeat: repeat-x; 21 | background-position: bottom; 22 | background-color: #fff08d; 23 | align-items: baseline; 24 | font-size: small; 25 | font-weight: bolder; 26 | border: solid 1px #d19231; 27 | margin: 0; 28 | padding: 6px; 29 | } 30 | 31 | .qnote-text { 32 | color: black; 33 | box-sizing: border-box; 34 | margin: 0; 35 | padding: 6px; 36 | background: url('../images/txt-background.png'); 37 | background-repeat: no-repeat; 38 | background-position:right bottom; 39 | background-color: #FBFEBF; 40 | border: solid 1px #FAF098; 41 | } 42 | 43 | .qnote-insidenote-top { 44 | margin-bottom: 0.5em; 45 | } 46 | 47 | .qnote-insidenote-bottom { 48 | margin-top: 0.5em; 49 | } 50 | 51 | .qnote-mm { 52 | background-image: url('../images/icons/qnote.svg'); 53 | background-size: contain; 54 | background-repeat: no-repeat; 55 | width: 16px; 56 | height: 16px; 57 | } 58 | 59 | @media (prefers-color-scheme: dark) { 60 | .qnote-title { 61 | color: white; 62 | background-image: url(../images/title-background-dark.png); 63 | background-color: #4d4d4d; 64 | border: solid 1px black; 65 | } 66 | .qnote-text { 67 | color: white; 68 | background-color: #2f2f33; 69 | background-image: url(../images/txt-background-dark.png); 70 | border: solid 1px black; 71 | } 72 | .qnote-text pre { 73 | color: white; 74 | } 75 | .qnote-insidenote pre { 76 | color: white; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/html/installed.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | margin: 1rem !important; 9 | } 10 | 11 | img { 12 | border: none; 13 | } 14 | 15 | .contents { 16 | background: url(../images/txt-background.png); 17 | background-repeat: no-repeat; 18 | background-position: right bottom; 19 | background-color: #FBFEBF; 20 | border: solid 1px #FAF098; 21 | padding: 1rem; 22 | padding-bottom: 0; 23 | border-radius: 1rem; 24 | margin-top: 0.5rem; 25 | } 26 | 27 | .head { 28 | background-image: url(../images/icons/qnote.svg); 29 | background-size: 4rem; 30 | background-repeat: no-repeat; 31 | background-position: 0 center; 32 | padding: 0.5rem 0 0.5rem 4.5rem; 33 | margin: 1rem; 34 | } 35 | 36 | .subhead { 37 | margin: 0 2rem; 38 | } 39 | 40 | .stdpad p, h2, ul { 41 | padding: 0 1rem 1rem 1rem; 42 | } 43 | .stdpad li { 44 | margin: 0 0 0 2rem; 45 | } 46 | 47 | .support { 48 | background-image: url(../images/icons/important.svg); 49 | background-size: 3.4rem; 50 | background-repeat: no-repeat; 51 | background-position: 0 center; 52 | padding: 0.5rem 4rem; 53 | } 54 | 55 | .eur, .usd { 56 | background-size: 1rem; 57 | background-repeat: no-repeat; 58 | background-position: left; 59 | padding: 0 0.5rem 0 1.1rem; 60 | font-weight: bold; 61 | } 62 | .eur { 63 | background-image: url(../images/icons/EUR.svg); 64 | } 65 | .usd { 66 | background-image: url(../images/icons/USD.svg); 67 | } 68 | 69 | .thick-border { 70 | border: 0.3rem dashed darkred; 71 | } 72 | 73 | @media (prefers-color-scheme: dark) { 74 | .contents { 75 | color: white; 76 | background-color: #2f2f33; 77 | background-image: url(../images/txt-background-dark.png); 78 | border: solid 1px black; 79 | } 80 | 81 | .thick-border { 82 | border: 0.3rem dashed silver; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/html/installed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | QNote 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |

Welcome to QNote Thunderbird extension!

16 |

Extension to attach notes to emails

17 |
18 |
19 | 20 |
21 |

Quick usage:

22 |
    23 |
  • Set up storage folder and other preferences
  • 24 |
  • Press Alt+Q to open / save & close the note
  • 25 |
  • Press ESC to close note without saving
  • 26 |
27 |

Complete list of features and more information available on GitHub

28 |
29 | 30 |
31 |

Migrating from the XNote++ extension?

32 |

Read the migration guide

33 |
34 | 35 |
36 |

Found a bug or missing a feature?

37 |
    38 |
  • Check for open or closed issues, or open a new one
  • 39 |
40 |
41 | 42 |
43 |
44 |

Support the development

45 |

46 | It was fun experimenting with Thunderbird (TB) internals when I first decided to rewrite XNote++ to support the latest TB version about four years ago. 47 | This was mainly because I was an XNote++ user myself and was disappointed when it stopped working due to changes in TB internals. 48 |

49 | 50 |

51 | And now, again, in version 126 some breaking changes have been made to TB internals, so much so that another rewrite was required to support the latest TB versions. 52 |

53 | 54 |

55 | I really hate to ask for donations, but it has come to this. 56 | In all honesty, I'm not sure if I still want to (or can) spend so much time developing this extension. 57 | If this extension brings you any value, please consider sending a donation. 58 |

59 | 60 |

61 | To put things into perspective: it took around one month of full-time development to bring this extension up to date yet again. 62 | If all current QNote users donated 1€ (or $1), this could cover the latest rewrite and then some. 63 |

64 | 65 |

66 | Thank you to all who have already donated! 67 |

68 | 69 |

70 | Donate via PayPal: 71 | EUR 72 | USD 73 |

74 |
75 |
76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /src/html/options.css: -------------------------------------------------------------------------------- 1 | @import url("chrome://global/skin/in-content/common.css"); 2 | @import url("chrome://messenger/skin/preferences/preferences.css"); 3 | 4 | html, body { 5 | /* font: message-box; */ 6 | /* background-color: var(--in-content-page-background); */ 7 | color: var(--in-content-page-color); 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | form { 13 | margin: 0 !important; 14 | padding: 0 !important; 15 | } 16 | 17 | button { 18 | white-space: nowrap; 19 | text-align: center; 20 | } 21 | 22 | legend { 23 | padding: 0.25rem 0.5rem; 24 | /* background-color: var(--in-content-categories-background); 25 | border: 1px solid var(--in-content-box-border-color); */ 26 | /* border:1px solid #96c9ef; */ 27 | /* background-color: #ffda44; */ 28 | } 29 | 30 | fieldset { 31 | /* border: 1px solid #96c9ef; */ 32 | /* border: 1px solid var(--in-content-categories-background) !important; */ 33 | background-color: var(--in-content-categories-background); 34 | border: 1px solid var(--in-content-box-border-color) !important; 35 | /* margin: 0 0 0 1em; */ 36 | margin: 0; 37 | padding: 0 1rem; 38 | } 39 | 40 | select, input[type=text], input[type=number] { 41 | margin-left: 8px; /* Hack to match select */ 42 | /* border: 1px solid var(--in-content-box-background-active); */ 43 | } 44 | 45 | input[type=number] { 46 | border-right: none; 47 | } 48 | 49 | input[type=checkbox], input[type=radio] { 50 | margin: 0; 51 | /* margin-top: 3px; */ 52 | margin-inline-end: unset; 53 | vertical-align: text-top; 54 | } 55 | 56 | .radioBox { 57 | vertical-align: baseline; 58 | float: left; 59 | margin-right: 1rem; 60 | } 61 | 62 | .radioBox label { 63 | vertical-align: baseline; 64 | } 65 | 66 | label { 67 | white-space: nowrap; 68 | } 69 | 70 | select { 71 | min-height: auto; 72 | } 73 | 74 | option, optgroup { 75 | padding: 0.25rem; 76 | } 77 | 78 | td { 79 | vertical-align: middle; 80 | } 81 | 82 | #options-error-box { 83 | position: relative; 84 | padding: 0; 85 | min-width: 320px; 86 | margin: auto; 87 | outline: none; 88 | box-sizing: border-box; 89 | } 90 | 91 | :root { 92 | --aspect-ratio: 1.6; 93 | --cell-w: 64px; 94 | --cell-h: calc(var(--cell-w) / var(--aspect-ratio)); 95 | } 96 | 97 | .anchorSelector { 98 | vertical-align: top; 99 | width: 50%; 100 | /* padding-top: calc(var(--cell-h) / 2); */ 101 | } 102 | 103 | #posGrid { 104 | margin: 0 auto; 105 | width: calc(5 * var(--cell-w)); 106 | height: calc(5 * var(--cell-h)); 107 | display: grid; 108 | grid-template-columns: repeat(5, 1fr); 109 | 110 | background-repeat: no-repeat; 111 | background-image: url(../images/empty-win.gif); 112 | background-size: calc(100% - 2 * var(--cell-w)) calc(100% - 2 * var(--cell-h)); 113 | background-position: var(--cell-w) var(--cell-h); 114 | } 115 | 116 | #posGrid .cell { 117 | width: var(--cell-w); 118 | height: var(--cell-h); 119 | cursor: pointer; 120 | background-repeat: no-repeat; 121 | background-image: url(../images/mini-note-disabled.gif); 122 | background-position: center; 123 | background-size: 50%; 124 | opacity: 0.5; 125 | /* border:1px solid #96c9ef; */ 126 | } 127 | 128 | #posGrid .cell:hover, 129 | #posGrid .active { 130 | background-image: url(../images/mini-note.gif); 131 | opacity: 1; 132 | } 133 | 134 | /* Align mini-note backgrounds */ 135 | /* Row 1 */ 136 | #posGrid .col .cell:nth-child(1) { 137 | background-position-y: bottom; 138 | } 139 | 140 | /* Row 2 */ 141 | #posGrid .col .cell:nth-child(2) { 142 | background-position-y: top; 143 | } 144 | 145 | /* Row 4 */ 146 | #posGrid .col .cell:nth-child(4) { 147 | background-position-y: bottom; 148 | } 149 | 150 | /* Row 5 */ 151 | #posGrid .col .cell:nth-child(5) { 152 | background-position-y: top; 153 | } 154 | 155 | /* Col 1 */ 156 | #posGrid .col:nth-child(1) .cell { 157 | background-position-x: right; 158 | } 159 | 160 | /* Col 2 */ 161 | #posGrid .col:nth-child(2) .cell { 162 | background-position-x: left; 163 | } 164 | 165 | /* Col 4 */ 166 | #posGrid .col:nth-child(4) .cell { 167 | background-position-x: right; 168 | } 169 | 170 | /* Col 5 */ 171 | #posGrid .col:nth-child(5) .cell { 172 | background-position-x: left; 173 | } 174 | 175 | #dateBlock { 176 | display: grid; 177 | grid-template-columns: min-content min-content; 178 | } 179 | 180 | #dateBlock > div { 181 | white-space: nowrap; 182 | margin: auto 0; 183 | } 184 | 185 | ::backdrop { 186 | backdrop-filter: blur(4px); 187 | } 188 | 189 | .gear-rotate { 190 | animation: rotate 3s infinite linear; 191 | } 192 | 193 | @keyframes rotate { 194 | 0% { 195 | transform: rotate(0deg); 196 | } 197 | 50% { 198 | transform: rotate(180deg); 199 | } 200 | 100% { 201 | transform: rotate(360deg); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/html/qpopup.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --title-height: 2rem; 3 | --controls-height: 2rem; 4 | --controls-icons-size: 1.5rem; 5 | } 6 | 7 | .unselectable { 8 | -webkit-touch-callout: none; 9 | -webkit-user-select: none; 10 | -khtml-user-select: none; 11 | -moz-user-select: none; 12 | -ms-user-select: none; 13 | -o-user-select: none; 14 | user-select: none; 15 | } 16 | 17 | /* Main wrapper */ 18 | .qpopup { 19 | width: 100%; 20 | height: 100%; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | 25 | /* Title */ 26 | .qpopup-title { 27 | color: black; 28 | background: url(../images/title-background.png); 29 | background-repeat: repeat-x; 30 | background-position: bottom; 31 | height: var(--title-height); 32 | background-color: #fff08d; 33 | display: flex; 34 | line-height: var(--title-height); 35 | font-size: smaller; 36 | font-weight: bolder; 37 | border-bottom: solid 1px #d19231; 38 | align-items: center; 39 | } 40 | .qpopup-title-text { 41 | margin-left: calc(var(--title-height) / 4); 42 | flex-grow: 1; 43 | } 44 | 45 | /* Mid section */ 46 | .qpopup-contents-wrap { 47 | width: 100%; 48 | height: 100%; 49 | height: calc(100% - var(--controls-height) - var(--title-height)); 50 | flex-grow: 1; 51 | } 52 | 53 | .qpopup-textinput { 54 | color: black; 55 | resize: none; 56 | width: 100%; 57 | height: 100%; 58 | display: flex; 59 | outline: none; 60 | 61 | padding: 4px; 62 | background: url(../images/txt-background.png); 63 | background-repeat: no-repeat; 64 | background-position:right bottom; 65 | background-color: #FBFEBF; 66 | border: solid 1px #FAF098; 67 | } 68 | 69 | /* Controls at the bottom */ 70 | .qpopup-controls { 71 | height: var(--controls-height); 72 | display: flex; 73 | /* justify-content: space-between; */ 74 | /* align-items: flex-end; */ 75 | background-color: #FBFEBF; 76 | } 77 | 78 | .qpopup-custom-controls { 79 | height: var(--controls-height); 80 | display: flex; 81 | flex-grow: 1; 82 | align-items: center; 83 | } 84 | .qpopup-spacer { 85 | flex-grow: 1; 86 | } 87 | .qpopup-controls-resize { 88 | cursor: nwse-resize; 89 | width: var(--controls-icons-size); 90 | height: 14px; 91 | background: url(../images/redim.png); 92 | background-repeat: no-repeat; 93 | background-position: bottom right; 94 | align-self: flex-end; 95 | } 96 | 97 | /* Buttons */ 98 | .qpopup-button { 99 | width: var(--controls-icons-size); 100 | height: var(--controls-icons-size); 101 | margin-left: calc(var(--controls-icons-size) / 4); 102 | cursor: pointer; 103 | background-size: calc(var(--controls-icons-size)*0.8) calc(var(--controls-icons-size)*0.8); 104 | background-position: center; 105 | background-repeat: no-repeat; 106 | } 107 | .qpopup-button:hover { 108 | background-size: var(--controls-icons-size) var(--controls-icons-size); 109 | } 110 | 111 | /* Close button */ 112 | .qpopup-button-close { 113 | margin: 0 calc(var(--title-height) / 4); 114 | background-image: url(../images/icons/close.svg); 115 | } 116 | 117 | /* Delete button */ 118 | .qpopup-button-delete { 119 | background-image: url(../images/icons/trash.svg); 120 | } 121 | 122 | /* Screenshot button */ 123 | .qpopup-button-screenshot { 124 | background-image: url(../images/icons/screenshot.svg); 125 | } 126 | .qpopup-button-screenshot-taken { 127 | background-image: url(../images/icons/ok.svg); 128 | background-size: var(--controls-icons-size) var(--controls-icons-size); 129 | } 130 | 131 | /* Reset button */ 132 | .qpopup-button-reset { 133 | background-image: url(../images/icons/reset.svg); 134 | } 135 | 136 | /* Save button */ 137 | .qpopup-button-save { 138 | background-image: url(../images/icons/save.svg); 139 | } 140 | 141 | @media (prefers-color-scheme: dark) { 142 | .qpopup-textinput { 143 | color: white; 144 | background-color: #2f2f33; 145 | background-image: url(../images/txt-background-dark.png); 146 | border: solid 1px black; 147 | } 148 | 149 | .qpopup-controls { 150 | background-color: #2f2f33; 151 | } 152 | 153 | .qpopup-title { 154 | color: white; 155 | background-image: url(../images/title-background-dark.png); 156 | background-color: #4d4d4d; 157 | border-bottom: solid 1px black; 158 | } 159 | .qpopup-controls-resize { 160 | background-image: url(../images/redim-dark.png); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/html/qpopup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
QNote
30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /src/html/updated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | QNote 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |

Welcome to QNote Thunderbird extension release 0.14.8!

16 |

Extension to attach notes to emails

17 |
18 |
19 | 20 |
21 |

Changelog:

22 |

Updated to support TB 139

23 |

Full CHANGELOG

24 |
25 | 26 |
27 |
28 |

Support the development

29 | 30 |

31 | Thank you to all who have already donated! 32 |

33 | 34 |

35 | Donate via PayPal: 36 | EUR 37 | USD 38 |

39 |
40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /src/html/wepopup.css: -------------------------------------------------------------------------------- 1 | html{ 2 | min-height:100%; 3 | position:relative; 4 | } 5 | 6 | body{ 7 | height:100%; 8 | } 9 | 10 | #popup-content { 11 | position:absolute; 12 | top:0; 13 | bottom:0; 14 | left:0; 15 | right:0; 16 | overflow:hidden; 17 | } 18 | 19 | textarea { 20 | resize: none; 21 | color: black; 22 | width: 100%; 23 | height:100%; 24 | margin: 0 auto; 25 | background: url(txt-background.png); 26 | background-repeat: no-repeat; 27 | background-position:right bottom; 28 | background-color: #FBFEBF; 29 | border: solid 2px #FAF098; 30 | } 31 | -------------------------------------------------------------------------------- /src/html/wepopup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /src/images/0.14.4-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/0.14.4-options.png -------------------------------------------------------------------------------- /src/images/1x1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/1x1.gif -------------------------------------------------------------------------------- /src/images/empty-win.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/empty-win.gif -------------------------------------------------------------------------------- /src/images/icons/EUR.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/images/icons/USD.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/images/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/images/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/images/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/images/icons/gear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/images/icons/gear2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/images/icons/important.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/images/icons/new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/images/icons/ok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/images/icons/paste.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/images/icons/qnote-disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/images/icons/qnote.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/images/icons/reset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/images/icons/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/images/icons/screenshot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/images/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/images/mini-note-disabled.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/mini-note-disabled.gif -------------------------------------------------------------------------------- /src/images/mini-note.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/mini-note.gif -------------------------------------------------------------------------------- /src/images/redim-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/redim-dark.png -------------------------------------------------------------------------------- /src/images/redim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/redim.png -------------------------------------------------------------------------------- /src/images/storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/storage.png -------------------------------------------------------------------------------- /src/images/title-background-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/title-background-dark.png -------------------------------------------------------------------------------- /src/images/title-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/title-background.png -------------------------------------------------------------------------------- /src/images/txt-background-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/txt-background-dark.png -------------------------------------------------------------------------------- /src/images/txt-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/src/images/txt-background.png -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "QNote", 4 | "author": "Martins Lazdans", 5 | "default_locale": "en", 6 | "description": "__MSG_description__", 7 | "version": "0.14.8", 8 | "applications": { 9 | "gecko": { 10 | "id": "qnote@dqdp.net", 11 | "strict_min_version": "128.0", 12 | "strict_max_version": "139.*" 13 | } 14 | }, 15 | "icons": { 16 | "32": "images/icons/qnote.svg", 17 | "48": "images/icons/qnote.svg", 18 | "64": "images/icons/qnote.svg" 19 | }, 20 | "permissions": [ 21 | "messagesTagsList", 22 | "messagesRead", 23 | "messagesUpdate", 24 | "storage", 25 | "unlimitedStorage", 26 | "downloads", 27 | "menus", 28 | "accountsRead", 29 | "scripting", 30 | "messagesModify" 31 | ], 32 | "background": { 33 | "page": "background.html" 34 | }, 35 | "options_ui": { 36 | "page": "html/options.html" 37 | }, 38 | "message_display_action": { 39 | "default_title": "QNote", 40 | "default_icon": "images/icons/qnote-disabled.svg" 41 | }, 42 | "browser_action": { 43 | "default_title": "QNote", 44 | "default_icon": "images/icons/qnote-disabled.svg" 45 | }, 46 | "commands": { 47 | "qnote": { 48 | "suggested_key": { 49 | "default": "Alt+Q" 50 | }, 51 | "description": "__MSG_toggleqnote__" 52 | } 53 | }, 54 | "experiment_apis": { 55 | "qapp": { 56 | "schema": "schemas/qapp.json", 57 | "parent": { 58 | "scopes": ["addon_parent"], 59 | "paths": [["qapp"]], 60 | "script": "api/qapp.js" 61 | } 62 | }, 63 | "legacy": { 64 | "schema": "schemas/legacy.json", 65 | "parent": { 66 | "scopes": ["addon_parent"], 67 | "paths": [["legacy"]], 68 | "script": "api/legacy.js" 69 | } 70 | }, 71 | "xnote": { 72 | "schema": "schemas/xnote.json", 73 | "parent": { 74 | "scopes": ["addon_parent"], 75 | "paths": [["xnote"]], 76 | "script": "api/xnote.js" 77 | } 78 | }, 79 | "qnote": { 80 | "schema": "schemas/qnote.json", 81 | "parent": { 82 | "scopes": ["addon_parent"], 83 | "paths": [["qnote"]], 84 | "script": "api/qnote.js" 85 | } 86 | }, 87 | "qpopup": { 88 | "schema": "schemas/qpopup.json", 89 | "parent": { 90 | "scopes": ["addon_parent"], 91 | "paths": [["qpopup"]], 92 | "script": "api/qpopup.js" 93 | } 94 | }, 95 | "ResourceUrl": { 96 | "schema": "schemas/ResourceUrl.json", 97 | "parent": { 98 | "scopes": ["addon_parent"], 99 | "paths": [["ResourceUrl"]], 100 | "script": "api/ResourceUrl.js" 101 | } 102 | }, 103 | "LegacyCSS": { 104 | "schema": "schemas/LegacyCSS.json", 105 | "parent": { 106 | "scopes": ["addon_parent"], 107 | "paths": [["LegacyCSS"]], 108 | "script": "api/LegacyCSS.js" 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/modules-exp/QNoteFile.mts: -------------------------------------------------------------------------------- 1 | import { INoteData } from "../modules/Note.mjs"; 2 | import { INoteFileAPI, INoteFileProvider } from "./api.mjs"; 3 | 4 | var { FileUtils } = ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs"); 5 | 6 | export interface IQNoteFileAPI extends INoteFileAPI { 7 | copyToClipboard(note: INoteData): Promise 8 | getFromClipboard(): Promise 9 | } 10 | 11 | export class QNoteFile implements INoteFileProvider { 12 | exists(file: any){ 13 | return file && file.exists(); 14 | } 15 | 16 | encodeFileName(str: string){ 17 | return encodeURIComponent(str) 18 | .replace(/\*/g, "%2A") 19 | .replace(/\~/g, "%7E") 20 | ; 21 | } 22 | 23 | decodeFileName(str: string){ 24 | return decodeURIComponent(str) 25 | .replace(/%2A/g, "*") 26 | .replace(/%7E/g, "~") 27 | ; 28 | } 29 | 30 | getFile(root:string, keyId: string){ 31 | var file = new FileUtils.File(root); 32 | 33 | file.appendRelativePath(this.encodeFileName(keyId + '.qnote')); 34 | 35 | return file; 36 | } 37 | 38 | getExistingFile(root: string, keyId: string) { 39 | var file = this.getFile(root, keyId); 40 | 41 | if(this.exists(file)){ 42 | return file; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | load(root: string, keyId: string): INoteData | null { 49 | var file = this.getExistingFile(root, keyId); 50 | 51 | if(!file){ 52 | return null; 53 | } 54 | 55 | var fileInStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); 56 | fileInStream.init(file, 0x01, parseInt("0444", 8), null); 57 | 58 | var con = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream); 59 | con.init(fileInStream, "utf-8", 0, 0xFFFD); // U+FFFD = replacement character 60 | 61 | var data = ''; 62 | var str: AString = {}; 63 | while (con.readString(4096, str) != 0) { 64 | data += str.value; 65 | } 66 | 67 | con.close(); 68 | fileInStream.close(); 69 | 70 | return JSON.parse(data); 71 | } 72 | 73 | delete(root: string, keyId: string){ 74 | const file = this.getExistingFile(root, keyId); 75 | if(file){ 76 | file.remove(false); 77 | } 78 | } 79 | 80 | save(root: string, keyId: string, note: INoteData) { 81 | var file = this.getFile(root, keyId); 82 | let data = JSON.stringify(note); 83 | 84 | let tempFile = file.parent.clone(); 85 | tempFile.append("~" + file.leafName + ".tmp"); 86 | tempFile.createUnique(tempFile.NORMAL_FILE_TYPE, parseInt("0660",8)); 87 | 88 | let fileOutStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); 89 | fileOutStream.init(tempFile, 2, 0x200, 0); // Opens for writing only 90 | 91 | var con = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream); 92 | // con.init(fileOutStream, "utf-8", 0, 0xFFFD); // U+FFFD = replacement character 93 | con.init(fileOutStream, "utf-8"); 94 | con.writeString(data); 95 | con.close(); 96 | 97 | fileOutStream.close(); 98 | 99 | tempFile.moveTo(null, file.leafName); 100 | } 101 | 102 | getAllKeys(root: string) { 103 | var file = new FileUtils.File(root); 104 | var eFiles = file.directoryEntries; 105 | var notes = []; 106 | 107 | while (eFiles.hasMoreElements()) { 108 | var o = eFiles.getNext().QueryInterface(Ci.nsIFile); 109 | 110 | var fileName = this.decodeFileName(o.leafName); 111 | if(fileName.substring(fileName.length - 6) === '.qnote'){ 112 | notes.push(fileName.substring(0, fileName.length - 6)); 113 | } 114 | } 115 | 116 | return notes; 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /src/modules/DOMLocalizator.mts: -------------------------------------------------------------------------------- 1 | import { 2 | isButtonElement, 3 | isHTMLElement, 4 | isInputElement, 5 | isSelectElement, 6 | isTextAreaElement, 7 | isTypeCheckbox, 8 | isTypeRadio, 9 | } from "./common.mjs"; 10 | 11 | export class DOMLocalizator { 12 | _: typeof browser.i18n.getMessage; 13 | constructor(localizator: typeof browser.i18n.getMessage) { 14 | this._ = localizator; 15 | } 16 | 17 | setTexts(document: Document) { 18 | for (const node of document.querySelectorAll("[data-i18n]")) { 19 | if (!(isHTMLElement(node) && node.dataset.i18n)) { 20 | continue; 21 | } 22 | 23 | // Optional parameters that can be injected into localization string, for example: 24 | // "implemented.formatting.rules": { 25 | // "message": "(implemented: $1)" 26 | // } 27 | // 28 | const params = new Map; 29 | 30 | for(const k in node.dataset){ 31 | k.match(/^i18n\.param(\d+)$/)?.slice(1).map(v => params.set(v, node.dataset[k] || "")); 32 | } 33 | 34 | const substitutions: Array = []; 35 | 36 | [...params.keys()].sort().map(v => substitutions.push(params.get(v) || "")); 37 | 38 | const text = this._(node.dataset.i18n, substitutions); 39 | 40 | if (isButtonElement(node)) { 41 | node.textContent = text; 42 | } else { 43 | node.prepend(document.createTextNode(text)); 44 | } 45 | } 46 | 47 | for (const node of document.querySelectorAll("[data-i18ntitle]")) { 48 | if (isHTMLElement(node) && node.dataset.i18ntitle) { 49 | node.title = this._(node.dataset.i18ntitle); 50 | } 51 | } 52 | } 53 | 54 | setValues(document: Document, values: any) { 55 | for (const node of document.querySelectorAll("[data-preference]")) { 56 | if (isHTMLElement(node) && node.dataset.preference){ 57 | node.setAttribute("name", node.dataset.preference); 58 | this.setNodeValue(node, values[node.dataset.preference]); 59 | } 60 | } 61 | } 62 | 63 | setNodeValue(node: Element, value: any): void { 64 | if (isSelectElement(node)) { 65 | for (let option of node.querySelectorAll("option")) { 66 | if (option.value == value) { 67 | option.selected = true; 68 | return; 69 | } 70 | } 71 | } else if (isInputElement(node)) { 72 | if (isTypeCheckbox(node)) { 73 | node.checked = value; 74 | } else if (isTypeRadio(node)) { 75 | node.checked = value === node.value; 76 | } else { 77 | node.value = value; 78 | } 79 | } else if (isTextAreaElement(node) || isButtonElement(node)) { 80 | node.value = value; 81 | } else { 82 | console.error(`[qnote:DOMLocalizator] unsupported input element type: ${node.nodeName}`); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/modules/Menu.mts: -------------------------------------------------------------------------------- 1 | import { isClipboardSet } from "./common-background.mjs"; 2 | 3 | var _ = browser.i18n.getMessage; 4 | 5 | export var Menu = { 6 | optionsMenu: { 7 | id: "options", 8 | title: _("options"), 9 | contexts: ["message_list", "page", "frame"], 10 | icons: "../images/icons/gear.svg", 11 | }, 12 | modify: async () => { 13 | // Modify 14 | browser.menus.create({ 15 | id: "modify", 16 | title: _("modify.note"), 17 | contexts: ["message_list", "page", "frame"], 18 | icons: "../images/icons/edit.svg", 19 | }); 20 | 21 | // Copy 22 | browser.menus.create({ 23 | id: "copy", 24 | title: _("copy"), 25 | contexts: ["message_list", "page", "frame"], 26 | icons: "../images/icons/copy.svg", 27 | }); 28 | 29 | // Existing paste 30 | browser.menus.create({ 31 | id: "paste", 32 | title: _("paste"), 33 | enabled: isClipboardSet(await browser.qnote.getFromClipboard()), 34 | contexts: ["message_list", "page", "frame"], 35 | icons: "../images/icons/paste.svg", 36 | }); 37 | 38 | // Delete 39 | browser.menus.create({ 40 | id: "delete", 41 | title: _("delete.note"), 42 | contexts: ["message_list", "page", "frame"], 43 | icons: "../images/icons/trash.svg" 44 | }); 45 | 46 | // Reset 47 | browser.menus.create({ 48 | id: "reset", 49 | title: _("reset.note.window"), 50 | contexts: ["message_list", "page", "frame"], 51 | icons: "../images/icons/reset.svg" 52 | }); 53 | 54 | browser.menus.create(Menu.optionsMenu as browser.menus._CreateCreateProperties); 55 | }, 56 | new: async () => { 57 | browser.menus.create({ 58 | id: "create", 59 | title: _("create.new.note"), 60 | contexts: ["message_list", "page", "frame"], 61 | icons: "../images/icons/new.svg", 62 | }); 63 | 64 | if(isClipboardSet(await browser.qnote.getFromClipboard())){ 65 | browser.menus.create({ 66 | id: "paste", 67 | title: _("paste"), 68 | contexts: ["message_list", "page", "frame"], 69 | icons: "../images/icons/paste.svg", 70 | }); 71 | browser.menus.create(Menu.optionsMenu as browser.menus._CreateCreateProperties); 72 | } 73 | }, 74 | multi: async () => { 75 | // Create multi 76 | browser.menus.create({ 77 | id: "create_multi", 78 | title: _("create.new.note"), 79 | contexts: ["message_list"], 80 | icons: "../images/icons/new.svg", 81 | }); 82 | 83 | // Paste multi 84 | browser.menus.create({ 85 | id: "paste_multi", 86 | title: _("paste"), 87 | contexts: ["message_list"], 88 | enabled: isClipboardSet(await browser.qnote.getFromClipboard()), 89 | icons: "../images/icons/paste.svg", 90 | }); 91 | 92 | // Delete multi 93 | browser.menus.create({ 94 | id: "delete_multi", 95 | title: _("delete.note"), 96 | contexts: ["message_list"], 97 | icons: "../images/icons/trash.svg", 98 | }); 99 | 100 | // Reset multi 101 | browser.menus.create({ 102 | id: "reset_multi", 103 | title: _("reset.note.window"), 104 | contexts: ["message_list"], 105 | icons: "../images/icons/reset.svg", 106 | }); 107 | 108 | browser.menus.create(Menu.optionsMenu as browser.menus._CreateCreateProperties); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/modules/Messages.mts: -------------------------------------------------------------------------------- 1 | import { IPopupCloseReason } from "../modules-exp/api.mjs"; 2 | import { IPreferences } from "./common.mjs"; 3 | import { INoteData } from "./Note.mjs"; 4 | import { IPopupState } from "./NotePopups.mjs"; 5 | 6 | abstract class DefaultMessage { 7 | abstract command: string; 8 | 9 | parse(data: any): M | undefined { 10 | if (!data || !("command" in data) || data.command !== this.command) { 11 | return; 12 | } 13 | 14 | for (const k of Object.getOwnPropertyNames(this)) { 15 | if (!(k in data)) { 16 | return; 17 | } 18 | } 19 | 20 | return data; 21 | } 22 | 23 | from(data: M): M { 24 | return Object.assign({}, { command: this.command, ...data}); 25 | } 26 | 27 | // Send to tab and optionally receive reply 28 | async send(tabId: number, data?: M) { 29 | return browser.tabs.sendMessage(tabId, { ...data, command: this.command }); 30 | } 31 | 32 | // Send to connected port 33 | post(port: browser.runtime.Port, data?: M): void { 34 | port.postMessage({ ...data, command: this.command }); 35 | } 36 | 37 | // Just send and optionally receive reply 38 | async sendMessage(data?: M) { 39 | return browser.runtime.sendMessage({ ...data, command: this.command }); 40 | } 41 | } 42 | 43 | // PrefsUpdated 44 | export class PrefsUpdated extends DefaultMessage { 45 | command = "PrefsUpdated"; 46 | } 47 | 48 | // AttachToMessage 49 | export class AttachToMessage extends DefaultMessage { 50 | command = "AttachToMessage"; 51 | } 52 | 53 | // AttachToMessageReply 54 | export abstract class AttachToMessageReplyData { 55 | abstract prefs: IPreferences; 56 | abstract note: INoteData; 57 | abstract html: string; 58 | abstract keyId: string; 59 | } 60 | export class AttachToMessageReply extends DefaultMessage { 61 | command = "AttachToMessageReply"; 62 | } 63 | 64 | // RestoreFocus 65 | export class RestoreFocus extends DefaultMessage { 66 | command = "RestoreFocus"; 67 | } 68 | 69 | // NoteDataRequest 70 | export abstract class KeyIdData { 71 | abstract keyId: string 72 | } 73 | export class PopupDataRequest extends DefaultMessage { 74 | command = "PopupDataRequest" 75 | } 76 | 77 | // NoteDataReply 78 | abstract class PopupDataReplyData { 79 | abstract keyId: string 80 | abstract state: IPopupState 81 | } 82 | export class PopupDataReply extends DefaultMessage { 83 | command = "PopupDataReply" 84 | } 85 | 86 | // SyncNote 87 | abstract class SyncNoteData { 88 | abstract keyId: string 89 | abstract reason: IPopupCloseReason 90 | abstract noteData: INoteData 91 | } 92 | export class SyncNote extends DefaultMessage { 93 | command = "SyncNote" 94 | } 95 | 96 | // PopNote 97 | export class PopNote extends DefaultMessage { 98 | command = "PopNote" 99 | } 100 | -------------------------------------------------------------------------------- /src/modules/Note.mts: -------------------------------------------------------------------------------- 1 | export type NoteType = QNoteFolder | XNoteFolder | QNoteLocalStorage; 2 | export type NoteClassType = 3 | | typeof QNoteFolder 4 | | typeof XNoteFolder 5 | | typeof QNoteLocalStorage; 6 | 7 | export interface INoteData { 8 | text? : string; 9 | left? : number; 10 | top? : number; 11 | width? : number; 12 | height?: number; 13 | ts? : number; 14 | } 15 | 16 | export interface INote { 17 | readonly keyId: string; // message-id header or another unique id 18 | load(): Promise; 19 | save(): Promise; 20 | delete(): Promise; 21 | assignData(data: INoteData): void; 22 | getData(): INoteData | null; 23 | exists(): boolean; 24 | } 25 | 26 | export abstract class DefaultNote implements INote { 27 | readonly keyId: string; 28 | protected data: INoteData | null = null; 29 | 30 | constructor(keyId: string) { 31 | this.keyId = keyId; 32 | } 33 | 34 | abstract save(): Promise; 35 | abstract delete(): Promise; 36 | protected abstract subload(): Promise; 37 | 38 | async load(): Promise { 39 | try { 40 | this.data = await this.subload(); 41 | return this.getData(); 42 | } catch(e) { 43 | if(e instanceof Error){ 44 | console.error("[qnote] error loading note:", e.message); 45 | } else { 46 | console.error("[qnote] error loading note:", e); 47 | } 48 | return null; 49 | } 50 | } 51 | 52 | assignData(data: INoteData) { 53 | this.data = Object.assign(this.data || {}, data); 54 | } 55 | 56 | getData(): INoteData | null { 57 | return this.data ? Object.assign({}, this.data) : null; // Don't want to pass a reference 58 | } 59 | 60 | exists(): boolean { 61 | return this.data !== null; 62 | } 63 | } 64 | 65 | export class QNoteLocalStorage extends DefaultNote { 66 | constructor(keyId: string) { 67 | super(keyId); 68 | } 69 | 70 | async subload(): Promise { 71 | return browser.storage.local 72 | .get(this.keyId) 73 | .then((store) => (store[this.keyId] ? store[this.keyId] : null)); 74 | } 75 | 76 | async save() { 77 | return this.data 78 | ? browser.storage.local 79 | .set({ 80 | [this.keyId]: this.data, 81 | }) 82 | .then(() => true) 83 | .catch(() => false) 84 | : false; 85 | } 86 | 87 | async delete() { 88 | return browser.storage.local 89 | .remove(this.keyId) 90 | .then(() => true) 91 | .catch(() => false); 92 | } 93 | } 94 | 95 | export class QNoteFolder extends DefaultNote { 96 | root: string; 97 | 98 | constructor(keyId: string, root: string) { 99 | super(keyId); 100 | this.root = root; 101 | } 102 | 103 | async subload(): Promise { 104 | return browser.qnote.load(this.root, this.keyId).then((data) => { 105 | return data ?? browser.xnote.load(this.root, this.keyId); 106 | }); 107 | } 108 | 109 | async save() { 110 | return this.data 111 | ? browser.qnote 112 | .save(this.root, this.keyId, this.data) 113 | .then(() => true) 114 | .catch(() => false) 115 | : false; 116 | } 117 | 118 | async delete() { 119 | const d1 = await browser.xnote.delete(this.root, this.keyId); 120 | const d2 = await browser.qnote.delete(this.root, this.keyId); 121 | return d1 || d2; 122 | } 123 | } 124 | 125 | export class XNoteFolder extends DefaultNote { 126 | root: string; 127 | 128 | constructor(keyId: string, root: string) { 129 | super(keyId); 130 | this.root = root; 131 | } 132 | 133 | async subload(): Promise { 134 | return browser.xnote.load(this.root, this.keyId); 135 | } 136 | 137 | async save() { 138 | return this.data 139 | ? browser.xnote 140 | .save(this.root, this.keyId, this.data) 141 | .then(() => true) 142 | .catch(() => false) 143 | : false; 144 | } 145 | 146 | async delete() { 147 | return browser.xnote 148 | .delete(this.root, this.keyId) 149 | .then(() => true) 150 | .catch(() => false); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/modules/QEventDispatcher.mts: -------------------------------------------------------------------------------- 1 | type FunctionWithArgs = (...args: any) => any; 2 | 3 | export class QEventDispatcher { 4 | private listenerMap: Map>; 5 | 6 | constructor() { 7 | this.listenerMap = new Map(); 8 | } 9 | 10 | addListener(key: K, listener: L) { 11 | if (!this.listenerMap.has(key)) { 12 | this.listenerMap.set(key, new Set()); 13 | } 14 | this.listenerMap.get(key)?.add(listener); 15 | } 16 | 17 | removeListener(key: K, listener: L) { 18 | this.listenerMap.get(key)?.delete(listener); 19 | } 20 | 21 | removeAllListenersUnder(key: K) { 22 | const listeners = this.listenerMap.get(key); 23 | listeners?.forEach((listener) => listeners.delete(listener)); 24 | } 25 | 26 | removeAllListeners(key?: K) { 27 | if (key !== undefined) { 28 | this.removeAllListenersUnder(key); 29 | } else { 30 | for (const key of this.listenerMap.keys()) { 31 | this.removeAllListenersUnder(key); 32 | } 33 | } 34 | } 35 | 36 | hasListener(key: K, listener: L): boolean { 37 | return this.listenerMap.get(key)?.has(listener) ? true : false; 38 | } 39 | 40 | fireListeners(key: K, ...args: Parameters): void { 41 | const listeners = this.listenerMap.get(key); 42 | if (listeners) { 43 | for (const listener of listeners) { 44 | listener(...args); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/schemas/LegacyCSS.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "namespace": "LegacyCSS", 4 | "events": [ 5 | { 6 | "name": "onWindowOpened", 7 | "type": "function", 8 | "description": "", 9 | "parameters": [ 10 | { 11 | "name": "url", 12 | "type": "string" 13 | } 14 | ] 15 | } 16 | ], 17 | "functions": [ 18 | { 19 | "name": "inject", 20 | "type": "function", 21 | "async": true, 22 | "description": "Injects the given CSS file into the window with the given url. Returns false if window was not open.", 23 | "parameters": [ 24 | { 25 | "name": "url", 26 | "type": "string" 27 | }, 28 | { 29 | "name": "cssFile", 30 | "type": "string" 31 | } 32 | ] 33 | }, 34 | { 35 | "name": "registerChromeUrl", 36 | "type": "function", 37 | "async": true, 38 | "description": "Register folders which should be available as chrome:// urls (as defined in the legacy chrome.manifest). You probably only need this to load files which should manipulate the folder tree view.", 39 | "parameters": [ 40 | { 41 | "name": "data", 42 | "type": "array", 43 | "items": { 44 | "type": "array", 45 | "items": { 46 | "type": "string" 47 | } 48 | }, 49 | "description": "Array of manifest url definitions (content, locale, resource)" 50 | } 51 | ] 52 | } 53 | ] 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /src/schemas/ResourceUrl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "namespace": "ResourceUrl", 4 | "functions": [ 5 | { 6 | "name": "register", 7 | "type": "function", 8 | "description": "Registers a folder which should be available as a resource:// url.", 9 | "parameters": [ 10 | { 11 | "name": "namespace", 12 | "type": "string", 13 | "pattern": "^[a-z0-9_]+$", 14 | "description": "The namespace to be used for the new resource:// url. JSM files in the specified folder will be accessible at 'resource://namespace/file.jsm'" 15 | }, 16 | { 17 | "name": "folder", 18 | "type": "string", 19 | "description": "The folder relative to the root of the extension, which should be made available via the resource url. The root folder is used, if no folder is specified.", 20 | "optional": true 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /src/schemas/legacy.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "namespace": "legacy", 3 | "functions": [{ 4 | "name": "alert", 5 | "type": "function", 6 | "description": "Display alert dialog", 7 | "async": true, 8 | "parameters": [{ 9 | "name": "title", 10 | "type": "string", 11 | "description": "Dialog title or message" 12 | },{ 13 | "name": "msg", 14 | "optional": true, 15 | "type": "string", 16 | "description": "Dialog message" 17 | }] 18 | },{ 19 | "name": "confirm", 20 | "type": "function", 21 | "description": "Display confirm dialog", 22 | "async": true, 23 | "parameters": [{ 24 | "name": "title", 25 | "type": "string", 26 | "description": "Dialog title or message" 27 | },{ 28 | "name": "msg", 29 | "optional": true, 30 | "type": "string", 31 | "description": "Dialog message" 32 | }] 33 | },{ 34 | "name": "compareVersions", 35 | "type": "function", 36 | "description": "Version compare", 37 | "async": true, 38 | "parameters": [{ 39 | "name": "v1", 40 | "type": "string", 41 | "description": "Version 1" 42 | }, 43 | { 44 | "name": "v2", 45 | "type": "string", 46 | "description": "Version 2" 47 | }] 48 | },{ 49 | "name": "folderPicker", 50 | "type": "function", 51 | "description": "Pick a folder", 52 | "async": true, 53 | "parameters": [{ 54 | "name": "initialPath", 55 | "type": "string", 56 | "optional": true 57 | }] 58 | },{ 59 | "name": "isReadable", 60 | "type": "function", 61 | "description": "Returns true if path is readable", 62 | "async": true, 63 | "parameters": [{ 64 | "name": "path", 65 | "type": "string", 66 | "description": "Path to check" 67 | }] 68 | },{ 69 | "name": "isFolderReadable", 70 | "type": "function", 71 | "description": "Returns true if folder is readable", 72 | "async": true, 73 | "parameters": [{ 74 | "name": "path", 75 | "type": "string", 76 | "description": "Path to check" 77 | }] 78 | },{ 79 | "name": "isFolderWritable", 80 | "type": "function", 81 | "description": "Returns true if folder is writable", 82 | "async": true, 83 | "parameters": [{ 84 | "name": "path", 85 | "type": "string", 86 | "description": "Path to check" 87 | }] 88 | }] 89 | }] 90 | -------------------------------------------------------------------------------- /src/schemas/qapp.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "namespace": "qnote", 3 | "types": [{ 4 | "id": "NoteData", 5 | "name": "data", 6 | "type": "object", 7 | "description": "Note data (keep in sync with qnote.json)", 8 | "properties": { 9 | "text": { 10 | "type": "string", 11 | "optional": true, 12 | "description": "Note text" 13 | }, 14 | "left": { 15 | "type": "integer", 16 | "optional": true, 17 | "description": "Windows left position" 18 | }, 19 | "top": { 20 | "type": "integer", 21 | "optional": true, 22 | "description": "Windows top position" 23 | }, 24 | "width": { 25 | "type": "integer", 26 | "optional": true, 27 | "description": "Windows width" 28 | }, 29 | "height": { 30 | "type": "integer", 31 | "optional": true, 32 | "description": "Windows height" 33 | }, 34 | "ts": { 35 | "type": "integer", 36 | "optional": true, 37 | "description": "Modification timestamp" 38 | } 39 | } 40 | }] 41 | },{ 42 | "namespace": "qapp", 43 | "types": [{ 44 | "id": "Prefs", 45 | "name": "prefs", 46 | "type": "object", 47 | "description": "Preferences", 48 | "properties": { 49 | "storageOption": { 50 | "type": "string", 51 | "description": "" 52 | }, 53 | "storageFolder": { 54 | "type": "string", 55 | "optional": true, 56 | "default": "", 57 | "description": "" 58 | }, 59 | "showFirstChars": { 60 | "type": "integer", 61 | "optional": true, 62 | "default": 0, 63 | "minimum": 0, 64 | "description": "" 65 | }, 66 | "messageAttachTop": { 67 | "type": "boolean", 68 | "optional": true, 69 | "default": false, 70 | "description": "" 71 | }, 72 | "messageAttachBottom": { 73 | "type": "boolean", 74 | "optional": true, 75 | "default": false, 76 | "description": "" 77 | }, 78 | "attachTemplate": { 79 | "type": "string", 80 | "optional": true, 81 | "default": "", 82 | "description": "" 83 | }, 84 | "treatTextAsHtml": { 85 | "type": "boolean", 86 | "optional": true, 87 | "default": false, 88 | "description": "" 89 | }, 90 | "enableDebug": { 91 | "type": "boolean", 92 | "optional": true, 93 | "description": "" 94 | } 95 | } 96 | }], 97 | "functions": [{ 98 | "name": "getProfilePath", 99 | "type": "function", 100 | "description": "Get profile path", 101 | "async": true, 102 | "parameters": [] 103 | },{ 104 | "name": "createStoragePath", 105 | "type": "function", 106 | "description": "Create storage folder under profile", 107 | "async": true, 108 | "parameters": [] 109 | },{ 110 | "name": "updateColumsView", 111 | "type": "function", 112 | "description": "", 113 | "async": true, 114 | "parameters": [] 115 | },{ 116 | "name": "attachNotesToMultiMessage", 117 | "type": "function", 118 | "description": "", 119 | "async": true, 120 | "parameters": [{ 121 | "type": "array", 122 | "items": { 123 | "name": "keyId", 124 | "type": "string" 125 | } 126 | }] 127 | },{ 128 | "name": "setPrefs", 129 | "type": "function", 130 | "description": "", 131 | "async": true, 132 | "parameters": [{ 133 | "$ref": "Prefs" 134 | }] 135 | },{ 136 | "name": "init", 137 | "type": "function", 138 | "description": "Initialize extension", 139 | "async": true, 140 | "parameters": [{ 141 | "$ref": "Prefs" 142 | }] 143 | },{ 144 | "name": "saveFocus", 145 | "type": "function", 146 | "description": "", 147 | "async": true, 148 | "parameters": [] 149 | },{ 150 | "name": "restoreFocus", 151 | "type": "function", 152 | "description": "", 153 | "async": true, 154 | "parameters": [] 155 | }] 156 | }] 157 | -------------------------------------------------------------------------------- /src/schemas/qnote.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "namespace": "qnote", 3 | "types": [{ 4 | "id": "NoteData", 5 | "name": "data", 6 | "type": "object", 7 | "description": "Note data (keep in sync with qapp.json)", 8 | "properties": { 9 | "text": { 10 | "type": "string", 11 | "optional": true, 12 | "description": "Note text" 13 | }, 14 | "left": { 15 | "type": "integer", 16 | "optional": true, 17 | "description": "Windows left position" 18 | }, 19 | "top": { 20 | "type": "integer", 21 | "optional": true, 22 | "description": "Windows top position" 23 | }, 24 | "width": { 25 | "type": "integer", 26 | "optional": true, 27 | "description": "Windows width" 28 | }, 29 | "height": { 30 | "type": "integer", 31 | "optional": true, 32 | "description": "Windows height" 33 | }, 34 | "ts": { 35 | "type": "integer", 36 | "optional": true, 37 | "description": "Modification timestamp" 38 | } 39 | } 40 | }], 41 | "functions": [{ 42 | "name": "getAllKeys", 43 | "type": "function", 44 | "description": "Returns list of QNote keys in a folder", 45 | "async": true, 46 | "parameters": [{ 47 | "name": "path", 48 | "type": "string", 49 | "description": "Path to QNote storage root" 50 | }] 51 | },{ 52 | "name": "load", 53 | "type": "function", 54 | "async": true, 55 | "parameters": [{ 56 | "name": "root", 57 | "type": "string", 58 | "description": "Root folder for QNotes" 59 | },{ 60 | "name": "keyId", 61 | "type": "string", 62 | "description": "QNote key" 63 | }] 64 | },{ 65 | "name": "delete", 66 | "type": "function", 67 | "async": true, 68 | "parameters": [{ 69 | "name": "root", 70 | "type": "string", 71 | "description": "Root folder for QNotes" 72 | },{ 73 | "name": "keyId", 74 | "type": "string", 75 | "description": "QNote key" 76 | }] 77 | },{ 78 | "name": "save", 79 | "type": "function", 80 | "description": "Saves QNote into file", 81 | "async": true, 82 | "parameters": [{ 83 | "name": "root", 84 | "type": "string", 85 | "description": "Root folder for QNotes" 86 | },{ 87 | "name": "keyId", 88 | "type": "string", 89 | "description": "QNote key" 90 | },{ 91 | "$ref": "NoteData" 92 | }] 93 | },{ 94 | "name": "copyToClipboard", 95 | "type": "function", 96 | "description": "", 97 | "async": true, 98 | "parameters": [{ 99 | "$ref": "NoteData" 100 | }] 101 | },{ 102 | "name": "getFromClipboard", 103 | "type": "function", 104 | "description": "", 105 | "async": true, 106 | "parameters": [] 107 | }] 108 | }] -------------------------------------------------------------------------------- /src/schemas/qpopup.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "namespace": "qpopup", 3 | "types": [{ 4 | "id": "IPopupState", 5 | "type": "object", 6 | "properties": { 7 | "focused": { 8 | "type": "boolean", 9 | "optional": true 10 | }, 11 | "top": { 12 | "type": "integer", 13 | "optional": true 14 | }, 15 | "left": { 16 | "type": "integer", 17 | "optional": true 18 | }, 19 | "offsetTop": { 20 | "type": "integer", 21 | "optional": true 22 | }, 23 | "offsetLeft": { 24 | "type": "integer", 25 | "optional": true 26 | }, 27 | "width": { 28 | "type": "integer", 29 | "optional": true 30 | }, 31 | "height": { 32 | "type": "integer", 33 | "optional": true 34 | }, 35 | "anchor": { 36 | "type": "string", 37 | "optional": true 38 | }, 39 | "anchorPlacement": { 40 | "type": "string", 41 | "optional": true 42 | }, 43 | "title": { 44 | "type": "string", 45 | "optional": true 46 | }, 47 | "text": { 48 | "type": "string", 49 | "optional": true 50 | }, 51 | "placeholder": { 52 | "type": "string", 53 | "optional": true 54 | }, 55 | "focusOnDisplay": { 56 | "type": "boolean", 57 | "optional": true 58 | }, 59 | "enableSpellChecker": { 60 | "type": "boolean", 61 | "optional": true 62 | }, 63 | "confirmDelete": { 64 | "type": "boolean", 65 | "optional": true 66 | }, 67 | "enableDebug": { 68 | "type": "boolean", 69 | "optional": true, 70 | "description": "" 71 | } 72 | } 73 | }], 74 | "functions": [{ 75 | "name": "get", 76 | "type": "function", 77 | "async": "callback", 78 | "parameters": [{ 79 | "type": "integer", 80 | "name": "id", 81 | "minimum": 1 82 | }] 83 | },{ 84 | "name": "update", 85 | "type": "function", 86 | "async": "callback", 87 | "parameters": [{ 88 | "name": "id", 89 | "type": "integer", 90 | "minimum": 1 91 | },{ 92 | "name": "state", 93 | "$ref": "IPopupState" 94 | }] 95 | },{ 96 | "name": "close", 97 | "type": "function", 98 | "async": "callback", 99 | "parameters": [{ 100 | "name": "id", 101 | "type": "integer", 102 | "minimum": 1 103 | },{ 104 | "name": "reason", 105 | "type": "string" 106 | }] 107 | },{ 108 | "name": "create", 109 | "type": "function", 110 | "async": "callback", 111 | "parameters": [{ 112 | "type": "integer", 113 | "name": "windowId", 114 | "minimum": 1 115 | },{ 116 | "name": "state", 117 | "$ref": "IPopupState" 118 | }] 119 | },{ 120 | "name": "pop", 121 | "type": "function", 122 | "async": "callback", 123 | "parameters": [{ 124 | "type": "integer", 125 | "name": "id", 126 | "minimum": 1 127 | }] 128 | },{ 129 | "name": "takeScreenshot", 130 | "type": "function", 131 | "async": "callback", 132 | "parameters": [{ 133 | "type": "integer", 134 | "name": "id", 135 | "minimum": 1 136 | }] 137 | },{ 138 | "name": "resetPosition", 139 | "type": "function", 140 | "async": "callback", 141 | "parameters": [{ 142 | "name": "id", 143 | "type": "integer", 144 | "minimum": 1 145 | }] 146 | },{ 147 | "name": "setPanelStyle", 148 | "type": "function", 149 | "async": "callback", 150 | "parameters": [{ 151 | "name": "id", 152 | "type": "integer", 153 | "minimum": 1 154 | },{ 155 | "name": "state", 156 | "type": "object", 157 | "properties": { 158 | "opacity": { 159 | "type": "string", 160 | "optional": true 161 | } 162 | } 163 | }] 164 | }], 165 | "events": [{ 166 | "name": "onClose", 167 | "type": "function", 168 | "parameters": [{ 169 | "name": "id", 170 | "type": "integer" 171 | },{ 172 | "name": "reason", 173 | "type": "string" 174 | },{ 175 | "name": "state", 176 | "$ref": "IPopupState" 177 | }] 178 | },{ 179 | "name": "onFocus", 180 | "type": "function", 181 | "parameters": [{ 182 | "name": "id", 183 | "type": "integer" 184 | }] 185 | },{ 186 | "name": "onBlur", 187 | "type": "function", 188 | "parameters": [{ 189 | "name": "id", 190 | "type": "integer" 191 | }] 192 | }] 193 | }] 194 | -------------------------------------------------------------------------------- /src/schemas/xnote.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "namespace": "xnote", 3 | "functions": [{ 4 | "name": "getPrefs", 5 | "type": "function", 6 | "description": "Get preferences", 7 | "async": true, 8 | "parameters": [] 9 | },{ 10 | "name": "getStoragePath", 11 | "type": "function", 12 | "description": "Get XNote storage folder", 13 | "async": true, 14 | "parameters": [{ 15 | "name": "path", 16 | "optional": true, 17 | "type": "string", 18 | "description": "Path from prefs (may contain [ProfD]) or null if XNote folder exists under profile dir" 19 | }] 20 | },{ 21 | "name": "getAllKeys", 22 | "type": "function", 23 | "description": "Returns list of XNote keys in a folder", 24 | "async": true, 25 | "parameters": [{ 26 | "name": "root", 27 | "type": "string", 28 | "description": "Path to XNote folder" 29 | }] 30 | },{ 31 | "name": "load", 32 | "type": "function", 33 | "async": true, 34 | "parameters": [{ 35 | "name": "root", 36 | "type": "string", 37 | "description": "Path to XNote folder" 38 | },{ 39 | "name": "keyId", 40 | "type": "string" 41 | }] 42 | },{ 43 | "name": "delete", 44 | "type": "function", 45 | "async": true, 46 | "parameters": [{ 47 | "name": "root", 48 | "type": "string", 49 | "description": "Path to XNote folder" 50 | },{ 51 | "name": "keyId", 52 | "type": "string", 53 | "description": "XNote key" 54 | }] 55 | },{ 56 | "name": "save", 57 | "type": "function", 58 | "async": true, 59 | "parameters": [{ 60 | "name": "root", 61 | "type": "string", 62 | "description": "Path to XNote folder" 63 | },{ 64 | "name": "keyId", 65 | "type": "string", 66 | "description": "XNote key" 67 | },{ 68 | "name": "data", 69 | "type": "object", 70 | "properties": { 71 | "left": { 72 | "type": "integer", 73 | "optional": true, 74 | "default": 0, 75 | "description": "Windows left position" 76 | }, 77 | "top": { 78 | "type": "integer", 79 | "optional": true, 80 | "default": 0, 81 | "description": "Windows top position" 82 | }, 83 | "width": { 84 | "type": "integer", 85 | "optional": true, 86 | "default": 0, 87 | "description": "Windows width" 88 | }, 89 | "height": { 90 | "type": "integer", 91 | "optional": true, 92 | "default": 0, 93 | "description": "Windows height" 94 | }, 95 | "text": { 96 | "type": "string", 97 | "optional": true, 98 | "default": "", 99 | "description": "Note text" 100 | }, 101 | "ts": { 102 | "type": "integer", 103 | "optional": true, 104 | "default": 0, 105 | "description": "Modification timestamp" 106 | } 107 | }, 108 | "default": {}, 109 | "description": "Data object" 110 | }] 111 | }] 112 | }] -------------------------------------------------------------------------------- /src/scripts/installed.ts: -------------------------------------------------------------------------------- 1 | window.addEventListener("DOMContentLoaded", async () => { 2 | const preferencesA = document.getElementById("preferences") as HTMLLinkElement | null; 3 | if(preferencesA) { 4 | preferencesA.addEventListener("click", () =>{ 5 | browser.runtime.openOptionsPage(); 6 | }); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /src/scripts/messageDisplay.ts: -------------------------------------------------------------------------------- 1 | // TODO: why this file is called 2x? 2 | import { AttachToMessageReplyData } from "../modules/Messages.mjs"; 3 | 4 | function attachToMessage(reply: AttachToMessageReplyData | undefined) { 5 | // Cleanup already attached note 6 | document.querySelectorAll(".qnote-insidenote").forEach(e => e.remove()); 7 | 8 | if(!reply){ 9 | return; 10 | } 11 | 12 | if (reply.prefs.messageAttachTop) { 13 | document.body.insertAdjacentHTML("afterbegin", '
' + reply.html + "
"); 14 | } 15 | 16 | if (reply.prefs.messageAttachBottom) { 17 | document.body.insertAdjacentHTML("beforeend", '
' + reply.html + "
"); 18 | } 19 | 20 | document.querySelectorAll(".qnote-text-span").forEach(el => { 21 | if (reply.prefs.treatTextAsHtml) { 22 | el.innerHTML = reply.note.text || ""; 23 | } else { 24 | el.textContent = reply.note.text || ""; 25 | } 26 | }); 27 | 28 | document.querySelectorAll(".qnote-insidenote").forEach(el => { 29 | if(el instanceof HTMLDivElement){ 30 | el.addEventListener("mousedown", e => { 31 | if (e.detail > 1) { 32 | e.preventDefault(); 33 | } 34 | }); 35 | 36 | el.addEventListener("dblclick", e => { 37 | browser.runtime.sendMessage({ command: "PopNote", keyId: reply.keyId }); // Can't use classes from Message because of imports >:/ 38 | }); 39 | } 40 | }); 41 | } 42 | 43 | browser.runtime.sendMessage({ command: "AttachToMessage" }).then(attachToMessage); 44 | -------------------------------------------------------------------------------- /src/scripts/wepopup.ts: -------------------------------------------------------------------------------- 1 | import { IPopupCloseReason } from "../modules-exp/api.mjs"; 2 | import { querySelectorOrDie } from "../modules/common.mjs"; 3 | import { DOMLocalizator } from "../modules/DOMLocalizator.mjs"; 4 | import { PopupDataReply, PopupDataRequest, RestoreFocus, SyncNote } from "../modules/Messages.mjs"; 5 | import { IPopupState, state2note } from "../modules/NotePopups.mjs"; 6 | 7 | const urlParams = new URLSearchParams(window.location.search); 8 | const keyId = urlParams.get("keyId")!; 9 | 10 | if(!keyId){ 11 | throw new Error("Missing query parameter: keyId"); 12 | } 13 | 14 | const State: IPopupState = {}; 15 | const i18n = new DOMLocalizator(browser.i18n.getMessage); 16 | 17 | // const titleTextEl = querySelectorOrDie(".qpopup-title-text") as HTMLElement; 18 | // const closeEl = querySelectorOrDie(".qpopup-button-close") as HTMLElement; 19 | const YTextE = querySelectorOrDie('.qpopup-textinput') as HTMLTextAreaElement; 20 | const delEl = querySelectorOrDie(".qpopup-button-delete") as HTMLElement; 21 | // TODO: resetEl 22 | // const resetEl = querySelectorOrDie(".qpopup-button-reset") as HTMLElement; 23 | const saveEl = querySelectorOrDie(".qpopup-button-save") as HTMLElement; 24 | 25 | function updateElements(state: IPopupState){ 26 | YTextE.setAttribute("spellcheck", state.enableSpellChecker ? "true" : "false"); 27 | if(state.text)YTextE.value = state.text; 28 | // if(state.title)titleTextEl.textContent = state.title; 29 | // if(state.width && state.height)resizeNote(state.width, state.height); 30 | if(state.placeholder)YTextE.setAttribute("placeholder", state.placeholder); 31 | } 32 | 33 | function updateNoteData(){ 34 | State.text = YTextE.value; 35 | State.left = window.screenX; 36 | State.top = window.screenY; 37 | State.width = window.outerWidth; 38 | State.height = window.outerHeight; 39 | sendNoteData("sync"); 40 | } 41 | 42 | function popup(){ 43 | i18n.setTexts(document); 44 | 45 | // closeEl.addEventListener ("click", () => customClose("close")); 46 | saveEl.addEventListener ("click", () => customClose("saveclose")); 47 | delEl.addEventListener ("click", () => { 48 | if(!State.confirmDelete || confirm(i18n._("delete.note"))){ 49 | customClose("delete"); 50 | } 51 | }); 52 | // resetEl.addEventListener ("click", () => { 53 | // if(this.id)browser.windows.update(this.id, { 54 | // left: undefined, 55 | // top: undefined, 56 | // }); 57 | // }); 58 | 59 | window.addEventListener("resize", updateNoteData); 60 | window.addEventListener("focus", () => YTextE.focus()); 61 | YTextE.addEventListener("keyup", updateNoteData); 62 | document.addEventListener("keyup", e => { 63 | if(e.code == "Escape"){ 64 | customClose("escape"); 65 | } 66 | 67 | if(e.repeat){ 68 | return; 69 | } 70 | 71 | // TODO: respect user changed default key bindings 72 | if(e.altKey && e.code == "KeyQ"){ 73 | customClose("saveclose"); 74 | } 75 | }); 76 | 77 | if(!State.focusOnDisplay){ 78 | setTimeout(() => (new RestoreFocus).sendMessage(), 100); // NOTE: arbitrary 100ms. Probably should attach to some event or smth 79 | } else { 80 | YTextE.focus(); 81 | } 82 | } 83 | 84 | function sendNoteData(reason: IPopupCloseReason){ 85 | return (new SyncNote).sendMessage({ 86 | keyId: keyId, 87 | reason: reason, 88 | noteData: state2note(State) 89 | }); 90 | } 91 | 92 | function customClose(reason: IPopupCloseReason){ 93 | sendNoteData(reason).then(() => window.close()); 94 | } 95 | 96 | window.addEventListener("DOMContentLoaded", async () => { 97 | const reply = await (new PopupDataRequest()).sendMessage({ keyId }); 98 | const data = (new PopupDataReply).parse(reply); 99 | 100 | if(data){ 101 | Object.assign(State, data.state); 102 | updateElements(State); 103 | popup(); 104 | } 105 | }); 106 | 107 | // document.addEventListener("visibilitychange", () => { 108 | // sendNoteData("sync"); // Sync data and later will save if needed, e.g., window closed by command or forcibly 109 | // }); 110 | -------------------------------------------------------------------------------- /src/types/browser.d.ts: -------------------------------------------------------------------------------- 1 | import 'thunderbird-webext-browser'; 2 | import { IQNoteFileAPI } from '../modules-exp/QNoteFile.mts'; 3 | import { IXNoteFileAPI } from '../modules-exp/XNoteFile.mts'; 4 | import { ILegacyAPI, IQAppAPI, IQPopupAPI } from '../modules-exp/api.mts'; 5 | 6 | export {} 7 | 8 | declare global { 9 | // Numeric typedefs, useful as a quick reference in method signatures. 10 | type double = number; 11 | type float = number; 12 | type i16 = number; 13 | type i32 = number; 14 | type i64 = number; 15 | type u16 = number; 16 | type u32 = number; 17 | type u64 = number; 18 | type u8 = number; 19 | 20 | class AString { 21 | length?: number | null 22 | value?: string | null 23 | } 24 | 25 | class XULDocument extends Document{ 26 | createXULElement(name: string): XULElement 27 | } 28 | 29 | class MozXULElement extends XULElement{ 30 | } 31 | 32 | class MozWindow extends Window { 33 | gFilter: any 34 | gTabmail: any 35 | gFolderDisplay: any 36 | document: XULDocument 37 | MozXULElement: typeof MozXULElement 38 | MutationObserver: typeof MutationObserver 39 | } 40 | 41 | namespace browser { 42 | export const qnote: IQNoteFileAPI; 43 | export const xnote: IXNoteFileAPI; 44 | export const qpopup: IQPopupAPI; 45 | export const qapp: IQAppAPI; 46 | export const legacy: ILegacyAPI; 47 | export const LegacyCSS: any; 48 | 49 | export namespace ResourceUrl { 50 | function register(name: string): Promise; 51 | } 52 | 53 | export namespace NotifyTools { 54 | export const onNotifyBackground: WebExtEvent<(e: any) => void>; 55 | } 56 | 57 | export namespace scripting { 58 | const messageDisplay: any; 59 | function getRegisteredContentScripts(): any; 60 | } 61 | } 62 | 63 | var FileUtils: typeof globalThis; 64 | var ExtensionUtils: typeof globalThis; 65 | var ExtensionParent: typeof globalThis; 66 | var MailServices: typeof globalThis; 67 | } 68 | -------------------------------------------------------------------------------- /thunderbird.net/about.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | QNote extension development was started after XNote++ support was dropped. It aims to be XNote++ drop-in replacement. 6 | 7 | Read about: 8 | 13 | 14 | Visit QNote GitHub page for latest information or to report a bug or submit a new feature. 15 | 16 | *DO NOT* use reviews section for reporting issues! 17 | 18 | Features: 19 |
    20 |
  • Add "sticky" notes to email messages
  • 21 |
  • Save note position and size; multiple default note positions
  • 22 |
  • Search notes using Thunderbird's built-in search (Edit / Find / Search Messages)
  • 23 |
  • Filter and apply actions based on different conditions (Tools / Message Filters)
  • 24 |
  • Clipboard copy/paste
  • 25 |
  • Column with note icon and preview
  • 26 |
  • Bulk operations on multiple message selections: create, update, delete, reset, copy, paste
  • 27 |
  • Light and dark themes
  • 28 |
  • Multiple locales and localized date formats
  • 29 |
  • Tag messages when creating notes
  • 30 |
  • Fully compatible with XNote++ (3.0.0)
  • 31 |
  • Import / export between XNote++ (.xnote) and QNote (.qnote) file formats
  • 32 |
  • Supports Thunderbird versions, starting from 68.2.0 (check archive for latest supported version for your Thunderbird installation)
  • 33 |
  • Simple templating support
  • 34 |
35 | 36 | Usage: 37 |
    38 |
  • Press Alt+Q to open / save & close the note
  • 39 |
  • Press ESC to close the note without saving
  • 40 |
  • Right-click on message(s) to access more commands in the context menu
  • 41 |
  • Use the built-in search to search within notes
  • 42 |
  • Use the built-in Filter Manager to create custom filters and actions
  • 43 |
44 | 45 | If you are using internal storage then don't forget to export data before removing extension! 46 | 47 | History: 48 |
    49 |
  • Version 0.14: complete rewrite in TypeScript, fix most of TB128 incompatibilities
  • 50 |
  • Version 0.13: add templating support for attaching to print and message
  • 51 |
  • Version 0.12: create custom filters and actions, clipboard copy / paste, multi message selection context menu (create, update, delete, reset, copy, paste)
  • 52 |
  • Version 0.11: search notes using QuickFilter, import / export notes in various formats and between storage options
  • 53 |
  • Version 0.10: configurable localized date format, set default note placement, optional confirm delete, save options w/o extension reload, options page styling according to selected theme
  • 54 |
  • Version 0.9: attaching note to message and printer, major rewrite
  • 55 |
  • Version 0.7: note search feature using QuickFilter
  • 56 |
  • Version 0.6: save notes using JSON file format instead of dated XNote++ format
  • 57 |
  • Version 0.5: add QNote column and two options for popup window style - floating panel (preferred) or windows using TB new API
  • 58 |
  • Version 0.4: two storage options - internal extension or XNote++ local folder (compatible with original extension)
  • 59 |
60 | 61 | Known issues: 62 |
    63 |
  • It does not work well together with the Conversations extension.
  • 64 |
65 | -------------------------------------------------------------------------------- /thunderbird.net/screenshots/attach_message.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/attach_message.jpg -------------------------------------------------------------------------------- /thunderbird.net/screenshots/attach_print.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/attach_print.jpg -------------------------------------------------------------------------------- /thunderbird.net/screenshots/column.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/column.jpg -------------------------------------------------------------------------------- /thunderbird.net/screenshots/filters.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/filters.jpg -------------------------------------------------------------------------------- /thunderbird.net/screenshots/note.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/note.jpg -------------------------------------------------------------------------------- /thunderbird.net/screenshots/search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlazdans/qnote/0d8588c8f1282293464dbfd33ffdc2f3cfd4357a/thunderbird.net/screenshots/search.jpg -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "./src/" 4 | // "./src/types/browser.d.ts", 5 | // "./node_modules/@types/luxon/src/*.d.ts", 6 | ], 7 | "compilerOptions": { 8 | "module": "Preserve", 9 | "target": "es2018", 10 | // "baseUrl": "./src", 11 | // "skipLibCheck": true, 12 | // "module": "Preserve", 13 | // "target": "es2016", 14 | 15 | "esModuleInterop": true, 16 | "strict": true, 17 | 18 | "sourceMap": false, 19 | "allowJs": true, 20 | "rootDir": "src", 21 | "outDir": "./dist/release", 22 | "typeRoots": [ 23 | // "./src/types/", 24 | // "./node_modules/@types/luxon/src/" 25 | ], 26 | "noImplicitReturns": true, 27 | "noUnusedLocals": false, 28 | "removeComments": true, 29 | // "declaration": true, 30 | 31 | "forceConsistentCasingInFileNames": true, 32 | "paths": { 33 | // "resource://gre/modules/ExtensionCommon.sys.mjs": ["./ExtensionCommon.sys.mjs"], 34 | // "gecko": [ 35 | // "../s/thunderbird-128.1.0/toolkit/components/extensions/types/gecko.ts" 36 | // ], 37 | // "resource://gre/modules/*": [ 38 | // "D:/thunderbird/s/thunderbird-128.1.0/toolkit/modules/", 39 | // "D:/thunderbird/s/thunderbird-128.1.0/toolkit/components/extensions/" 40 | // // "../s/thunderbird-128.1.0/toolkit/modules/", 41 | // // "../s/thunderbird-128.1.0/toolkit/components/extensions/", 42 | // ], 43 | }, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /update_vers.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | for /f %%i in ('jq -r .version dist/release/manifest.json') do set VERS=%%i 4 | C:\cygwin64\bin\grep.exe -iEHlr "\?version=version" dist/release | xargs C:\cygwin64\bin\sed.exe -i "s/?version=version/?version=%VERS%/g" 5 | --------------------------------------------------------------------------------