├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── pull_request_template.md
├── .gitignore
├── .travis.yml
├── build
├── background.png
├── background@2x.png
├── entitlements.mas.plist
├── icon.icns
├── icon.ico
└── icon.png
├── code-of-conduct.md
├── contributing.md
├── docs
├── _config.yml
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon-96x96.png
├── highres-icon.png
├── index.html
├── media
│ ├── black-theme.png
│ ├── compact-mode.png
│ ├── custom-shortcut-keys.gif
│ ├── dark-theme.png
│ ├── export-notes.gif
│ ├── focus-mode.png
│ ├── header.png
│ ├── logo.png
│ ├── note-navigation.gif
│ ├── print-note.gif
│ ├── scalable-interface.gif
│ ├── sepia-theme.png
│ └── yinxiang-support.png
└── update.json
├── index.js
├── license.md
├── package.json
├── readme.md
├── src
├── browser.js
├── config.js
├── configs
│ ├── darwin.js
│ ├── index.js
│ ├── linux.js
│ └── win32.js
├── dialog.js
├── file.js
├── keymap.js
├── menu
│ ├── app.js
│ ├── edit.js
│ ├── file.js
│ ├── format.js
│ ├── help.js
│ ├── index.js
│ ├── tray.js
│ ├── view.js
│ └── window.js
├── mode.js
├── nav.js
├── pdf.js
├── save.js
├── settings.js
├── startup.js
├── style
│ ├── black-mode.css
│ ├── browser.css
│ ├── dark-mode.css
│ └── sepia-mode.css
├── time.js
├── tray.js
├── update.js
├── url.js
├── util.js
└── win.js
├── static
├── Icon.icns
├── Icon.ico
├── Icon.png
├── IconTray.png
└── IconTray@2x.png
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [{*.json, *.yml}]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 | *.psd binary
4 | *.pfd binary
5 | *.ai binary
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior.
12 |
13 | **Expected behavior**
14 | A clear and concise description of what you expected to happen.
15 |
16 | **Screenshots**
17 | If applicable, add screenshots to help explain your problem.
18 |
19 | **Technical Info (please complete the following information)**
20 | - OS:
21 | - Tusk Version:
22 |
23 | **Additional context**
24 | Add any other context about the problem here.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is.
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Additional context**
14 | Add any other context or screenshots about the feature request here.
15 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 | package-json.lock
4 |
5 | # logs
6 | *.log
7 |
8 | # OS
9 | .DS_Store
10 |
11 | # IDE
12 | .vscode
13 | .idea
14 | *.swp
15 | *.swo
16 |
17 | # compiled
18 | dist
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | include:
3 | - os: osx
4 | osx_image: xcode10.2
5 | language: node_js
6 | node_js: "12"
7 | env:
8 | - ELECTRON_CACHE=$HOME/.cache/electron
9 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
10 |
11 | - os: linux
12 | services: docker
13 | language: generic
14 |
15 | cache:
16 | directories:
17 | - node_modules
18 | - $HOME/.cache/electron
19 | - $HOME/.cache/electron-builder
20 |
21 | script:
22 | - |
23 | if [ "$TRAVIS_OS_NAME" == "linux" ]; then
24 | docker run --rm \
25 | --env-file <(env | grep -v '\r' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') \
26 | -v ${PWD}:/project \
27 | -v ~/.cache/electron:/root/.cache/electron \
28 | -v ~/.cache/electron-builder:/root/.cache/electron-builder \
29 | electronuserland/builder:wine \
30 | /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn release --linux --win"
31 | else
32 | yarn release
33 | fi
34 |
35 | before_cache:
36 | - rm -rf $HOME/.cache/electron-builder/wine
37 |
38 | branches:
39 | except:
40 | - "/^v\\d+\\.\\d+\\.\\d+$/"
41 |
--------------------------------------------------------------------------------
/build/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/build/background.png
--------------------------------------------------------------------------------
/build/background@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/build/background@2x.png
--------------------------------------------------------------------------------
/build/entitlements.mas.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 | com.apple.security.files.user-selected.read-only
10 |
11 | com.apple.security.files.user-selected.read-write
12 |
13 | com.apple.security.files.downloads.read-write
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/build/icon.ico
--------------------------------------------------------------------------------
/build/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/build/icon.png
--------------------------------------------------------------------------------
/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at klaussinani@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to Tusk ❤
2 |
3 | Thank you for taking the time to contribute to Tusk! ✨🎉
4 |
5 | Please note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.
6 |
7 | ## How to contribute!
8 |
9 | ### Improve documentation
10 |
11 | Typo corrections, error fixes, better explanations, more examples etc. Open an issue regarding anything that you think it could be improved! You can use the [`docs` label](https://github.com/klaussinani/tusk/labels/docs) to find out what others have suggested!
12 |
13 | ### Improve issues
14 |
15 | Sometimes reported issues lack information, are not reproducible, or are even plain invalid. Help us out to make them easier to resolve. Handling issues takes a lot of time that we could rather spend on fixing bugs and adding features.
16 |
17 | ### Give feedback on issues
18 |
19 | We're always looking for more opinions on discussions in the issue tracker. It's a good opportunity to influence the future direction of the project.
20 |
21 | The [`question` label](https://github.com/klaussinani/tusk/labels/question) is a good place to find ongoing discussions.
22 |
23 | ### Write code
24 |
25 | You can use issue labels to discover issues you could help us out with!
26 |
27 | - [`enhancement` issues](https://github.com/klaussinani/tusk/labels/enhancement) are features we are open to including
28 | - [`bug` issues](https://github.com/klaussinani/tusk/labels/bug) are known bugs we would like to fix
29 | - [`needs testing` issues](https://github.com/klaussinani/tusk/labels/needs%20testing) are features that we are still working on improving
30 | - [`future` issues](https://github.com/klaussinani/tusk/labels/future) are those that we'd like to get to, but not anytime soon. Please check before working on these since we may not yet want to take on the burden of supporting those features
31 | - on the [`help wanted`](https://github.com/klaussinani/tusk/labels/future) label you can always find something exciting going on
32 |
33 | You may find an issue is assigned, or has the [`assigned` label](https://github.com/klaussinani/tusk/labels/assigned). Please double-check before starting on this issue because somebody else is likely already working on it
34 |
35 | ### Say hi!
36 |
37 | Come over and say hi anytime you feel like on [Gitter](https://gitter.im/klaussinani/tusk).
38 |
39 | ### Translating Documentation
40 |
41 | - Make sure that the document is not already translated in that language.
42 | - Add the name of the language to the document as an extension, e.g: `readme.JP.md`
43 | - Place the translated document inside the [`docs`](https://github.com/klaussinani/tusk/tree/master/docs) directory.
44 | - Create a Pull Request including the language in the title, e.g: `Readme: Japanese Translation`
45 |
46 | ### Submitting an issue
47 |
48 | - Search the issue tracker before opening an issue
49 | - Ensure you're using the latest version of Tusk: 
50 | - Use a descriptive title
51 | - Include as much information as possible;
52 | - Steps to reproduce the issue
53 | - App settings
54 | - Error message
55 | - Tusk version
56 | - Operating system **etc**
57 |
58 | ### Submitting a pull request
59 |
60 | - Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work
61 | - Try making the pull request from a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) if it is of crucial importance
62 | - Use a descriptive title for the pull request and commits
63 | - You might be asked to do changes to your pull request, you can do that by just [updating the existing one](https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md)
64 |
65 | > Inspired by project [AVA](https://github.com/avajs/ava/blob/master/contributing.md)'s contributing.md
66 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | include: [".well-known", "update.json"]
2 | auto: true
3 |
--------------------------------------------------------------------------------
/docs/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/favicon-96x96.png
--------------------------------------------------------------------------------
/docs/highres-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/highres-icon.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Tusk - Refined Evernote desktop app
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 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
207 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
Refined Evernote desktop app
226 |
227 |
228 |
229 |
230 |
231 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
Toggle anything in a flash.
249 |
View all.
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
Perfect for glossy screens.
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
Silky & relaxing.
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
Deep focus materialized.
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
Immerse yourself in a distraction-free note-taking mode.
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
Keep your productivity to the maximum no matter the screen size.
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
Transfering your notes from screen to paper is only a keystroke away.
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
Navigate seamlessly without taking your hands away from the keyboard.
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
Adjust Tusk to your workflow by modifying any shortcut key to your own preference.
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
Export and save your notes effortlessly on your machine as `pdf` files.
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
One-click login to your Yinxiang account.
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
Adjust the zooming factor to your own preference.
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
View all the features in detail on Github.
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
370 |
Tusk is an unofficial, open source, third-party, community driven, free app and is not affiliated in any way with Evernote.
371 |
372 |
373 |
374 |
395 |
396 |
397 |
398 |
--------------------------------------------------------------------------------
/docs/media/black-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/black-theme.png
--------------------------------------------------------------------------------
/docs/media/compact-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/compact-mode.png
--------------------------------------------------------------------------------
/docs/media/custom-shortcut-keys.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/custom-shortcut-keys.gif
--------------------------------------------------------------------------------
/docs/media/dark-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/dark-theme.png
--------------------------------------------------------------------------------
/docs/media/export-notes.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/export-notes.gif
--------------------------------------------------------------------------------
/docs/media/focus-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/focus-mode.png
--------------------------------------------------------------------------------
/docs/media/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/header.png
--------------------------------------------------------------------------------
/docs/media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/logo.png
--------------------------------------------------------------------------------
/docs/media/note-navigation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/note-navigation.gif
--------------------------------------------------------------------------------
/docs/media/print-note.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/print-note.gif
--------------------------------------------------------------------------------
/docs/media/scalable-interface.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/scalable-interface.gif
--------------------------------------------------------------------------------
/docs/media/sepia-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/sepia-theme.png
--------------------------------------------------------------------------------
/docs/media/yinxiang-support.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/docs/media/yinxiang-support.png
--------------------------------------------------------------------------------
/docs/update.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Tusk v0.23.0",
3 | "url": "https://github.com/klaussinani/tusk/releases/latest",
4 | "version": "0.23.0"
5 | }
6 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app, BrowserWindow, ipcMain, Menu, shell} = require('electron');
3 | const fs = require('fs');
4 | const {is, formatTitle, formatURL, formatYinxiangURL, readSheet} = require('./src/util');
5 | const file = require('./src/file');
6 | const menu = require('./src/menu');
7 | const pdf = require('./src/pdf');
8 | const settings = require('./src/settings');
9 | const shortcut = require('./src/keymap');
10 | const time = require('./src/time');
11 | const tray = require('./src/tray');
12 | const update = require('./src/update');
13 | const url = require('./src/url');
14 | const win = require('./src/win');
15 |
16 | const {log} = console;
17 |
18 | require('electron-debug')({enabled: true});
19 | require('electron-dl')();
20 | require('electron-context-menu')();
21 |
22 | let exiting = false;
23 | let mainWindow;
24 |
25 | if (!app.requestSingleInstanceLock()) {
26 | app.quit();
27 | }
28 |
29 | app.on('second-instance', () => {
30 | if (mainWindow) {
31 | if (mainWindow.isMinimized()) {
32 | mainWindow.restore();
33 | }
34 |
35 | mainWindow.show();
36 | }
37 | });
38 |
39 | function createMainWindow() {
40 | const lastURL = settings.get('useYinxiang') ? url.yinxiang : url.evernote;
41 |
42 | const tuskWindow = new BrowserWindow(win.defaultOpts);
43 |
44 | tuskWindow.loadURL(lastURL);
45 |
46 | tuskWindow.on('close', e => {
47 | if (!exiting) {
48 | e.preventDefault();
49 |
50 | if (is.darwin) {
51 | app.hide();
52 | } else {
53 | tuskWindow.hide();
54 | }
55 | }
56 | });
57 |
58 | tuskWindow.on('page-title-updated', e => {
59 | e.preventDefault();
60 | });
61 |
62 | tuskWindow.on('unresponsive', log);
63 |
64 | tuskWindow.webContents.on('did-navigate-in-page', (_, url) => {
65 | settings.set('lastURL', url);
66 | });
67 |
68 | return tuskWindow;
69 | }
70 |
71 | app.on('ready', () => {
72 | Menu.setApplicationMenu(menu);
73 | mainWindow = createMainWindow();
74 |
75 | if (settings.get('useGlobalShortcuts')) {
76 | shortcut.registerGlobal();
77 | }
78 |
79 | if (!settings.get('hideTray')) {
80 | tray.create();
81 | }
82 |
83 | const {webContents} = mainWindow;
84 |
85 | webContents.on('dom-ready', () => {
86 | const stylesheets = fs.readdirSync(file.style);
87 | stylesheets.forEach(x => webContents.insertCSS(readSheet(x)));
88 |
89 | if (settings.get('launchMinimized')) {
90 | mainWindow.minimize();
91 | } else {
92 | mainWindow.show();
93 | }
94 | });
95 |
96 | webContents.on('new-window', (e, url) => {
97 | e.preventDefault();
98 | url = settings.get('useYinxiang') ? formatYinxiangURL(url) : formatURL(url);
99 |
100 | if (is.downloadURL(url)) {
101 | webContents.downloadURL(url);
102 | } else {
103 | shell.openExternal(url);
104 | }
105 | });
106 |
107 | webContents.on('crashed', log);
108 |
109 | if (!settings.get('disableAutoUpdateCheck')) {
110 | setInterval(() => update.auto(), time.ms(settings.get('updateCheckPeriod')));
111 | }
112 | });
113 |
114 | ipcMain.on('print-to-pdf', pdf.print);
115 |
116 | ipcMain.on('export-as-pdf', e => {
117 | const {webContents} = mainWindow;
118 | pdf.save(e, formatTitle(webContents.getTitle()));
119 | });
120 |
121 | process.on('uncaughtException', log);
122 |
123 | app.on('activate', () => mainWindow.show());
124 |
125 | app.on('before-quit', () => {
126 | exiting = true;
127 | if (!mainWindow.isFullScreen()) {
128 | settings.set('lastWindowState', mainWindow.getBounds());
129 | }
130 | });
131 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 - present Klaus Sinani (klaussinani.github.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tusk",
3 | "productName": "Tusk",
4 | "version": "0.23.0",
5 | "description": "Refined Evernote desktop app",
6 | "license": "MIT",
7 | "repository": "klaussinani/tusk",
8 | "author": {
9 | "name": "Klaus Sinani",
10 | "email": "klaussinani@gmail.com",
11 | "url": "https://klaussinani.github.io"
12 | },
13 | "maintainers": [
14 | {
15 | "name": "Mario Sinani",
16 | "email": "mariosinani@protonmail.ch",
17 | "url": "https://github.com/mariosinani"
18 | }
19 | ],
20 | "scripts": {
21 | "postinstall": "electron-builder install-app-deps",
22 | "test": "xo && stylelint 'src/style/*.css'",
23 | "release": "build --publish always",
24 | "start": "electron ."
25 | },
26 | "dependencies": {
27 | "auto-launch": "^5.0.1",
28 | "decode-uri-component": "^0.2.0",
29 | "electron-context-menu": "^0.9.0",
30 | "electron-debug": "^1.3.0",
31 | "electron-dl": "^1.11.0",
32 | "electron-settings": "^3.1.4",
33 | "turndown": "^5.0.1"
34 | },
35 | "devDependencies": {
36 | "electron": "6.0.11",
37 | "electron-builder": "22.9.1",
38 | "stylelint": "^9.9.0",
39 | "xo": "*"
40 | },
41 | "xo": {
42 | "envs": [
43 | "browser",
44 | "node"
45 | ],
46 | "rules": {
47 | "quote-props": 0
48 | },
49 | "space": 2
50 | },
51 | "stylelint": {
52 | "rules": {
53 | "block-closing-brace-empty-line-before": "never",
54 | "block-closing-brace-newline-after": "always",
55 | "block-no-empty": true,
56 | "block-opening-brace-space-before": "always",
57 | "color-hex-case": "upper",
58 | "color-hex-length": "long",
59 | "color-no-invalid-hex": true,
60 | "comment-no-empty": true,
61 | "declaration-block-semicolon-space-before": "never",
62 | "indentation": 2,
63 | "max-empty-lines": 0,
64 | "no-duplicate-selectors": true
65 | }
66 | },
67 | "build": {
68 | "appId": "com.klaussinani.tusk",
69 | "files": [
70 | "**/*",
71 | "!media${/*}",
72 | "!docs${/*}"
73 | ],
74 | "linux": {
75 | "category": "Office",
76 | "description": "Tusk is an unofficial, featureful, open source, community-driven, free Evernote app used by people in more than 130 countries.",
77 | "synopsis": "Refined Evernote desktop app",
78 | "target": [
79 | {
80 | "target": "AppImage",
81 | "arch": [
82 | "ia32",
83 | "x64"
84 | ]
85 | },
86 | {
87 | "target": "deb",
88 | "arch": [
89 | "ia32",
90 | "x64"
91 | ]
92 | },
93 | {
94 | "target": "pacman",
95 | "arch": [
96 | "ia32",
97 | "x64"
98 | ]
99 | },
100 | {
101 | "target": "rpm",
102 | "arch": [
103 | "ia32",
104 | "x64"
105 | ]
106 | },
107 | {
108 | "target": "snap",
109 | "arch": [
110 | "x64"
111 | ]
112 | }
113 | ]
114 | },
115 | "nsis": {
116 | "license": "license.md"
117 | },
118 | "snap": {
119 | "grade": "stable",
120 | "confinement": "strict"
121 | },
122 | "win": {
123 | "target": [
124 | {
125 | "target": "nsis",
126 | "arch": [
127 | "ia32",
128 | "x64"
129 | ]
130 | },
131 | {
132 | "target": "portable",
133 | "arch": [
134 | "ia32",
135 | "x64"
136 | ]
137 | }
138 | ]
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 | Tusk
3 |
4 |
5 |
6 | Refined Evernote desktop app
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
34 |
35 | ## Description
36 |
37 | Tusk is an unofficial, featureful, open source, community-driven, free Evernote app used by people in more than [140 countries](https://snapcraft.io/tusk).
38 |
39 | Tusk is [indicated by Evernote](https://help.evernote.com/hc/en-us/articles/208313748-Evernote-on-Linux) as an alternative client for Linux environments trusted by the open source community.
40 |
41 | You can now support the development process through [GitHub Sponsors](https://github.com/sponsors/klaussinani).
42 |
43 | Come over to [Gitter](https://gitter.im/klaussinani/tusk) or [Twitter](https://twitter.com/klaussinani) to share your thoughts on the project.
44 |
45 | Visit the [contributing guidelines](https://github.com/klaussinani/tusk/blob/master/contributing.md#translating-documentation) to learn more on how to translate this document into more languages.
46 |
47 | You can find more apps [here](#related-apps).
48 |
49 | ## Highlights
50 |
51 | - Black, Dark & Sepia Themes
52 | - Focus, Compact & Auto-Night Modes
53 | - Local & Global Customizable Keyboard Shortcuts
54 | - Export Notes as PDF, HTML & Markdown Files
55 | - Note Navigation
56 | - Yinxiang Support
57 | - Cross Platform
58 | - Scalable Interface
59 | - Update Notifications
60 | - Drag and Drop Files
61 |
62 | ## Contents
63 |
64 | - [Description](#description)
65 | - [Highlights](#highlights)
66 | - [Install](#install)
67 | - [Features](#features)
68 | - [Keyboard Shortcuts](#keyboard-shortcuts)
69 | - [Development](#development)
70 | - [Related Apps](#related-apps)
71 | - [Team](#team)
72 | - [Sponsors](#sponsors)
73 | - [Disclaimer](#disclaimer)
74 | - [License](#license)
75 |
76 | ## Install
77 |
78 | #### Github Releases
79 |
80 | Head to the [releases](https://github.com/klaussinani/tusk/releases/latest) page and download the appropriate installer for your system.
81 |
82 | #### Snapcraft
83 |
84 | Ubuntu Linux users can directly install through [Snapcraft](https://snapcraft.io/tusk) `snap install tusk`
85 |
86 | #### Homebrew
87 |
88 | Macos users can directly install through [Homebrew Cask](https://caskroom.github.io/) `brew cask install tusk`
89 |
90 | #### Note
91 |
92 | The version available on `Homebrew Cask` may not be the latest, since unlike `Snapcraft`, it is not offically maintained. If that is the case, please consider downloading directly from the [Github releases](https://github.com/klaussinani/tusk/releases/latest) page.
93 |
94 | ## Features
95 |
96 | Visit the project [homepage](https://klaussinani.github.io/tusk) to view all features in detail.
97 |
98 | - Auto Night Mode - Press Cmd/Ctrl Alt N to allow Tusk to adjust to your environment.
99 | - Black Theme - Activate it by pressing Cmd/Ctrl Alt E .
100 | - Compact Mode - Downsize the window to enter the mode.
101 | - Custom Shortcut Keys - Navigate to `~/.tusk.json` or press Cmd/Ctrl . to modify any shortcut key. To reset delete `~/.tusk.json` & restart the app.
102 | - Dark Theme - Activate it by pressing Cmd/Ctrl D .
103 | - Drag & Drop Files - Attach files by dragging them to the app window.
104 | - Export Notes as Markdown - Press Cmd/Ctrl O to save your notes as `Markdown` files.
105 | - Export Notes as HTML - Press Cmd/Ctrl Shift H to save your notes as `HTML` files.
106 | - Export Notes as PDF - Press Cmd/Ctrl Shift E to save your notes as `PDF` files.
107 | - Focus Mode - Activate it by pressing Cmd/Ctrl K .
108 | - Global Shortcut Keys - Enable them by using the `File` > `Enable Global Shortcut Keys` option.
109 | - Note Navigation - Navigate your notes by pressing Cmd/Ctrl Tab / Cmd/Ctrl Shift Tab or jump directly to one by using Cmd/Ctrl 1 - 9 .
110 | - Note Printing - Press Cmd/Ctrl Alt P to print your notes.
111 | - Scalable Interface - Adjust the zooming factor by pressing Cmd/Ctrl Shift = or Cmd/Ctrl - .
112 | - Sepia Theme - Activate it by pressing Cmd/Ctrl G .
113 | - Update Notifications - Customize the apps update checking frequency.
114 | - Yinxiang Support - Login to Yinxiang by using the `File` > `Switch to Yinxiang` option.
115 |
116 | ## Keyboard Shortcuts
117 |
118 | ### Local Shortcut Keys
119 |
120 | 70+ local keyboard shortcuts. Toggle anything in a flash.
121 |
122 |
123 | View all the available local keyboard shortcuts.
124 |
125 |
126 |
127 | Description | Keys
128 | -------------------------- | --------------------------
129 | Activate Auto Night Mode | Cmd/Ctrl Alt N
130 | Add Link | Cmd/Ctrl Shift K
131 | Add Shortcut | Cmd/Ctrl Alt S
132 | Align Center | Cmd/Ctrl Alt M
133 | Align Left | Cmd/Ctrl Alt L
134 | Align Right | Cmd/Ctrl Alt R
135 | Attach File | Cmd/Ctrl Shift F
136 | Bold Text | Cmd/Ctrl B
137 | Bulleted List | Cmd/Ctrl Shift .
138 | Change Font Size | Cmd/Ctrl Alt 1 - 6
139 | Code Block | Cmd/Ctrl Shift L
140 | Decrease Indentation | Cmd/Ctrl Shift M
141 | Delete Note | Delete
142 | Edit Shortcut Keys | Cmd/Ctrl .
143 | Export Note as HTML | Cmd/Ctrl Shift H
144 | Export Note as Markdown | Cmd/Ctrl O
145 | Export Note as PDF | Cmd/Ctrl Shift E
146 | Increase Indentation | Cmd/Ctrl Alt K
147 | Insert Date Stamp | Cmd/Ctrl Shift ;
148 | Insert Date-Time Stamp | Cmd/Ctrl ;
149 | Insert from Drive | Cmd/Ctrl Shift D
150 | Insert Horizontal Rule | Cmd/Ctrl Shift -
151 | Italic Text | Cmd/Ctrl I
152 | Jump to Note | Cmd/Ctrl 1 - 9
153 | Make Text Larger | Cmd/Ctrl Shift =
154 | Make Text Smaller | Cmd/Ctrl -
155 | Navigate to Next Note | Cmd/Ctrl Tab
156 | Navigate to Previews Note | Cmd/Ctrl Shift Tab
157 | New Note | Cmd/Ctrl N
158 | New Notebook | Cmd/Ctrl Shift N
159 | New Tag | Cmd/Ctrl Shift T
160 | Numbered List | Cmd/Ctrl Shift O
161 | Print Note | Cmd/Ctrl Alt P
162 | Remove Formatting | Cmd/Ctrl Shift Space
163 | Reset Zoom Level | Cmd/Ctrl 0
164 | Return to Notes | Esc
165 | Save Note | Cmd/Ctrl S
166 | Search Notes | Cmd/Ctrl F
167 | Set Always on Top | Cmd/Ctrl Shift P
168 | Set Reminder | Cmd/Ctrl E
169 | Strikethrough Text | Cmd/Ctrl T
170 | Subscript Text | Cmd/Ctrl Shift ]
171 | Superscript Text | Cmd/Ctrl Shift [
172 | Toggle Black Theme | Cmd/Ctrl Alt E
173 | Toggle Checkbox | Cmd/Ctrl Shift B
174 | Toggle Dark Theme | Cmd/Ctrl D
175 | Toggle Focus Mode | Cmd/Ctrl K
176 | Toggle Notebooks | Alt Shift N
177 | Toggle Sepia Theme | Cmd/Ctrl G
178 | Toggle Settings | Cmd/Ctrl ,
179 | Toggle Shortcuts | Cmd/Ctrl Shift S
180 | Toggle Sidebar | Cmd/Ctrl \\
181 | Toggle Tags | Alt Shift T
182 | Toggle Window Menu | Alt
183 | Underline Text | Cmd/Ctrl U
184 |
185 |
186 |
187 |
188 |
189 | ### Global Shortcut Keys
190 |
191 | Access Tusk at any moment from anywhere within your operating system. All global shortcuts can be customized to match your own preference through the configuration file `~/.tusk.json`.
192 |
193 |
194 | View all the available global keyboard shortcuts.
195 |
196 |
197 |
198 | Description | Global Shortcut
199 | -------------------------- | --------------------------
200 | Toggle Tusk Window | Cmd/Ctrl Alt A
201 | Create New Note | Cmd/Ctrl Alt C
202 | Search Notes | Cmd/Ctrl Alt F
203 |
204 |
205 |
206 |
207 |
208 | ## Development
209 |
210 | For more info on how to contribute to the project, please read the [contributing guidelines](https://github.com/klaussinani/tusk/blob/master/contributing.md).
211 |
212 | - Fork the repository and clone it to your machine
213 | - Navigate to your local fork: `cd tusk`
214 | - Install the project dependencies: `npm install` or `yarn install`
215 | - Run Tusk on dev mode: `npm start` or `yarn start`
216 | - Lint code for errors: `npm test` or `yarn test`
217 | - Build binaries and installers: `npm run release` or `yarn release`
218 |
219 | ## Related Apps
220 |
221 | - [Ao](https://github.com/klaudiosinani/ao) - Elegant Microsoft To-Do desktop app.
222 | - [Taskbook](https://github.com/klaudiosinani/taskbook) - Tasks, boards & notes for the command-line habitat.
223 |
224 | ## Team
225 |
226 | - Klaudio Sinani [(@klaussinani)](https://github.com/klaudiosinani)
227 | - Mario Sinani [(@mariosinani)](https://github.com/mariosinani)
228 | - Athan Gkanos [(@athangkanos)](https://github.com/athangkanos)
229 |
230 | ## Sponsors
231 |
232 | A big thank you to all the people and companies supporting our Open Source work:
233 |
234 | - [Better Stack: Spot, Resolve, and Prevent Downtime.](https://betterstack.com/)
235 |
236 | ## Disclaimer
237 |
238 | Tusk is an unofficial, open source, third-party, community-driven, free app and is not affiliated in any way with Evernote.
239 |
240 | ## License
241 |
242 | [MIT](https://github.com/klaussinani/tusk/blob/master/license.md)
243 |
--------------------------------------------------------------------------------
/src/browser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {ipcRenderer: ipc} = require('electron');
3 | const mode = require('./mode');
4 | const nav = require('./nav');
5 | const save = require('./save');
6 | const settings = require('./settings');
7 | const startup = require('./startup');
8 |
9 | ipc.on('print', () => ipc.send('print-to-pdf'));
10 |
11 | ipc.on('export', () => ipc.send('export-as-pdf'));
12 |
13 | ipc.on('export-as-markdown', () => save.md());
14 |
15 | ipc.on('export-as-html', () => save.html());
16 |
17 | ipc.on('auto-launch', () => startup.autoLaunch());
18 |
19 | ipc.on('next-note', () => nav.nextNote());
20 |
21 | ipc.on('previous-note', () => nav.previousNote());
22 |
23 | ipc.on('focus-mode', () => mode.toggleFocus());
24 |
25 | ipc.on('toggle-dark-mode', () => mode.dark());
26 |
27 | ipc.on('toggle-black-mode', () => mode.black());
28 |
29 | ipc.on('toggle-sepia-mode', () => mode.sepia());
30 |
31 | ipc.on('auto-night-mode', () => mode.autoNight());
32 |
33 | ipc.on('toggle-side-bar', () => {
34 | settings.set('sideBarHidden', !settings.get('sideBarHidden'));
35 | nav.sideBar();
36 | });
37 |
38 | ipc.on('zoom-in', () => nav.zoomIn());
39 |
40 | ipc.on('zoom-out', () => nav.zoomOut());
41 |
42 | ipc.on('zoom-reset', () => nav.zoomReset());
43 |
44 | ipc.on('log-out', () => {
45 | nav.click('#gwt-debug-AccountMenu-avatar');
46 | nav.click('#gwt-debug-AccountMenu-logout');
47 | });
48 |
49 | ipc.on('new-notebook', () => {
50 | const notebooks = nav.select('#gwt-debug-Sidebar-notebooksButton');
51 | notebooks.click();
52 | nav.click('#gwt-debug-NotebooksDrawer-createNotebookButton');
53 | notebooks.click();
54 | });
55 |
56 | ipc.on('new-tag', () => {
57 | const tags = nav.select('#gwt-debug-Sidebar-tagsButton');
58 | tags.click();
59 | nav.click('.focus-drawer-TagsDrawer-TagsDrawer-create-tag-icon');
60 | tags.click();
61 | });
62 |
63 | ipc.on('subscript', () => {
64 | const formatMenu = nav.select('#gwt-debug-FormattingBar-overflowButton');
65 | formatMenu.click();
66 | nav.click('#gwt-debug-FormattingBar-subscriptButton');
67 | });
68 |
69 | ipc.on('superscript', () => {
70 | const formatMenu = nav.select('#gwt-debug-FormattingBar-overflowButton');
71 | formatMenu.click();
72 | nav.click('#gwt-debug-FormattingBar-superscriptButton');
73 | });
74 |
75 | ipc.on('add-link', () => {
76 | nav.click('#gwt-debug-FormattingBar-linkButton');
77 | });
78 |
79 | ipc.on('add-shortcut', () => {
80 | nav.click('#gwt-debug-NoteAttributes-shortcutButton');
81 | });
82 |
83 | ipc.on('align-center', () => {
84 | nav.click('#gwt-debug-EditorAlignDropdown-center');
85 | });
86 |
87 | ipc.on('align-left', () => {
88 | nav.click('#gwt-debug-EditorAlignDropdown-left');
89 | });
90 |
91 | ipc.on('align-right', () => {
92 | nav.click('#gwt-debug-EditorAlignDropdown-right');
93 | });
94 |
95 | ipc.on('attach-file', () => {
96 | nav.click('#gwt-debug-FormattingBar-linkButton');
97 | });
98 |
99 | ipc.on('bold', () => {
100 | nav.click('#gwt-debug-FormattingBar-boldButton');
101 | });
102 |
103 | ipc.on('bulleted', () => {
104 | nav.click('#gwt-debug-FormattingBar-bulletButton');
105 | });
106 |
107 | ipc.on('checkbox', () => {
108 | nav.click('#gwt-debug-FormattingBar-checkboxButton');
109 | });
110 |
111 | ipc.on('code-block', () => {
112 | nav.click('#gwt-debug-FormattingBar-codeBlockButton');
113 | });
114 |
115 | ipc.on('delete-note', () => {
116 | nav.click('#gwt-debug-NoteAttributes-trashButton');
117 | });
118 |
119 | ipc.on('header-five', () => {
120 | nav.click('#gwt-debug-FontSizeDropdown-root-TWELVE');
121 | });
122 |
123 | ipc.on('header-four', () => {
124 | nav.click('#gwt-debug-FontSizeDropdown-root-FOURTEEN');
125 | });
126 |
127 | ipc.on('header-one', () => {
128 | nav.click('#gwt-debug-FontSizeDropdown-root-THIRTY_SIX');
129 | });
130 |
131 | ipc.on('header-six', () => {
132 | nav.click('#gwt-debug-FontSizeDropdown-root-TEN');
133 | });
134 |
135 | ipc.on('header-three', () => {
136 | nav.click('#gwt-debug-FontSizeDropdown-root-EIGHTEEN');
137 | });
138 |
139 | ipc.on('header-two', () => {
140 | nav.click('#gwt-debug-FontSizeDropdown-root-TWENTY_FOUR');
141 | });
142 |
143 | ipc.on('horizontal-rule', () => {
144 | nav.click('#gwt-debug-FormattingBar-horizontalRuleButton');
145 | });
146 |
147 | ipc.on('indent', () => {
148 | nav.click('#gwt-debug-FormattingBar-indentButton');
149 | });
150 |
151 | ipc.on('insert-drive', () => {
152 | nav.click('#gwt-debug-FormattingBar-attachmentButton');
153 | });
154 |
155 | ipc.on('italic', () => {
156 | nav.click('#gwt-debug-FormattingBar-italicButton');
157 | });
158 |
159 | ipc.on('new-note', () => {
160 | nav.click('#gwt-debug-Sidebar-newNoteButton');
161 | });
162 |
163 | ipc.on('numbered', () => {
164 | nav.click('#gwt-debug-FormattingBar-listButton');
165 | });
166 |
167 | ipc.on('outdent', () => {
168 | nav.click('#gwt-debug-FormattingBar-outdentButton');
169 | });
170 |
171 | ipc.on('remove-formatting', () => {
172 | nav.click('#gwt-debug-FormattingBar-noFormatButton');
173 | });
174 |
175 | ipc.on('return', () => {
176 | nav.click('#gwt-debug-Sidebar-notesButton');
177 | });
178 |
179 | ipc.on('search', () => {
180 | nav.click('#gwt-debug-Sidebar-searchButton');
181 | });
182 |
183 | ipc.on('set-reminder', () => {
184 | nav.click('#gwt-debug-NoteAttributes-reminderButton');
185 | });
186 |
187 | ipc.on('shortcuts', () => {
188 | nav.click('#gwt-debug-Sidebar-shortcutsButton');
189 | });
190 |
191 | ipc.on('strikethrough', () => {
192 | nav.click('#gwt-debug-FormattingBar-strikeButton');
193 | });
194 |
195 | ipc.on('toggle-notebooks', () => {
196 | nav.click('#gwt-debug-Sidebar-notebooksButton');
197 | });
198 |
199 | ipc.on('toggle-tags', () => {
200 | nav.click('#gwt-debug-Sidebar-tagsButton');
201 | });
202 |
203 | ipc.on('underline', () => {
204 | nav.click('#gwt-debug-FormattingBar-underlineButton');
205 | });
206 |
207 | document.addEventListener('keydown', e => nav.jumpToNote(e));
208 |
209 | document.addEventListener('DOMContentLoaded', () => {
210 | nav.zoomRestore();
211 |
212 | if (settings.get('autoNightMode')) {
213 | mode.autoNight();
214 | }
215 |
216 | nav.sideBar();
217 | mode.restore();
218 | mode.autoWritingDirection();
219 | });
220 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 | const defaultConfig = require('./configs');
4 | const file = require('./file');
5 |
6 | const {log} = console;
7 |
8 | class Config {
9 | constructor() {
10 | this._default = Object.assign({}, defaultConfig);
11 | }
12 |
13 | get _local() {
14 | try {
15 | return JSON.parse(fs.readFileSync(file.localConfig, 'utf8'));
16 | } catch (error) {
17 | return log(error);
18 | }
19 | }
20 |
21 | get configuration() {
22 | this._ensureLocalConfig(file.localConfig);
23 | return this._local;
24 | }
25 |
26 | get shortcutKeys() {
27 | return this.configuration.shortcutKeys;
28 | }
29 |
30 | _updateConfig(data) {
31 | const result = Object.assign({}, this._default);
32 |
33 | Object.keys(data).forEach(type => {
34 | result[type] = Object.assign({}, result[type], data[type]);
35 | });
36 |
37 | Object.keys(result).forEach(type => {
38 | const [opts, defaultOpts] = [data[type], this._default[type]].map(Object.keys);
39 | const deprecated = opts.filter(x => !defaultOpts.includes(x));
40 | deprecated.forEach(x => delete result[type][x]);
41 | });
42 |
43 | return result;
44 | }
45 |
46 | _ensureLocalConfig(path) {
47 | const data = fs.existsSync(path) ? this._updateConfig(this._local) : this._default;
48 | try {
49 | fs.writeFileSync(path, JSON.stringify(data, null, 4));
50 | } catch (error) {
51 | log(error);
52 | }
53 | }
54 | }
55 |
56 | module.exports = new Config();
57 |
--------------------------------------------------------------------------------
/src/configs/darwin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | shortcutKeys: {
5 | 'add-link': 'Cmd+Shift+K',
6 | 'add-shortcut': 'Cmd+Alt+S',
7 | 'align-center': 'Cmd+Alt+M',
8 | 'align-left': 'Cmd+Alt+L',
9 | 'align-right': 'Cmd+Alt+R',
10 | 'attach-file': 'Cmd+Shift+F',
11 | 'bold': 'Cmd+B',
12 | 'bulleted': 'Cmd+Shift+.',
13 | 'checkbox': 'Cmd+Shift+B',
14 | 'code-block': 'Cmd+Shift+L',
15 | 'date-stamp': 'Cmd+Shift+;',
16 | 'date-time-stamp': 'Cmd+;',
17 | 'decrease-indentation': 'Cmd+Shift+M',
18 | 'delete-note': 'Delete',
19 | 'export-html': 'Cmd+Shift+H',
20 | 'export-markdown': 'Cmd+O',
21 | 'export-pdf': 'Cmd+Shift+E',
22 | 'global-create-note': 'Cmd+Alt+C',
23 | 'global-search-note': 'Cmd+Alt+F',
24 | 'global-toggle-window': 'Cmd+Alt+A',
25 | 'horizontal-rule': 'Cmd+Shift+-',
26 | 'increase-indentation': 'Cmd+Alt+K',
27 | 'insert-drive': 'Cmd+Shift+D',
28 | 'italic': 'Cmd+I',
29 | 'new-note': 'Cmd+N',
30 | 'new-notebook': 'Cmd+Shift+N',
31 | 'new-tag': 'Cmd+Shift+T',
32 | 'numbered': 'Cmd+Shift+O',
33 | 'print': 'Cmd+Alt+P',
34 | 'remove-formatting': 'Cmd+Shift+Space',
35 | 'return': 'Esc',
36 | 'search': 'Cmd+F',
37 | 'set-reminder': 'Cmd+E',
38 | 'settings': 'Cmd+,',
39 | 'shortcuts': 'Cmd+Shift+S',
40 | 'strikethrough': 'Cmd+T',
41 | 'subscript': 'Cmd+Shift+]',
42 | 'superscript': 'Cmd+Shift+[',
43 | 'toggle-black-mode': 'Cmd+Alt+E',
44 | 'toggle-dark-mode': 'Cmd+D',
45 | 'toggle-focus-mode': 'Cmd+K',
46 | 'toggle-notebooks': 'Shift+Alt+N',
47 | 'toggle-sepia-mode': 'Cmd+G',
48 | 'toggle-sidebar': 'Cmd+\\',
49 | 'toggle-tags': 'Shift+Alt+T',
50 | 'underline': 'Cmd+U'
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/configs/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const config = require(`./${process.platform}`);
3 |
4 | module.exports = config;
5 |
--------------------------------------------------------------------------------
/src/configs/linux.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | shortcutKeys: {
5 | 'add-link': 'Ctrl+Shift+K',
6 | 'add-shortcut': 'Ctrl+Alt+S',
7 | 'align-center': 'Ctrl+Alt+M',
8 | 'align-left': 'Ctrl+Alt+L',
9 | 'align-right': 'Ctrl+Alt+R',
10 | 'attach-file': 'Ctrl+Shift+F',
11 | 'bold': 'Ctrl+B',
12 | 'bulleted': 'Ctrl+Shift+.',
13 | 'checkbox': 'Ctrl+Shift+B',
14 | 'code-block': 'Ctrl+Shift+L',
15 | 'date-stamp': 'Ctrl+Shift+;',
16 | 'date-time-stamp': 'Ctrl+;',
17 | 'decrease-indentation': 'Ctrl+Shift+M',
18 | 'delete-note': 'Delete',
19 | 'export-html': 'Ctrl+Shift+H',
20 | 'export-markdown': 'Ctrl+O',
21 | 'export-pdf': 'Ctrl+Shift+E',
22 | 'global-create-note': 'Ctrl+Alt+C',
23 | 'global-search-note': 'Ctrl+Alt+F',
24 | 'global-toggle-window': 'Ctrl+Alt+A',
25 | 'horizontal-rule': 'Ctrl+Shift+-',
26 | 'increase-indentation': 'Ctrl+Alt+K',
27 | 'insert-drive': 'Ctrl+Shift+D',
28 | 'italic': 'Ctrl+I',
29 | 'new-note': 'Ctrl+N',
30 | 'new-notebook': 'Ctrl+Shift+N',
31 | 'new-tag': 'Ctrl+Shift+T',
32 | 'numbered': 'Ctrl+Shift+O',
33 | 'print': 'Ctrl+Alt+P',
34 | 'remove-formatting': 'Ctrl+Shift+Space',
35 | 'return': 'Esc',
36 | 'search': 'Ctrl+F',
37 | 'set-reminder': 'Ctrl+E',
38 | 'settings': 'Ctrl+,',
39 | 'shortcuts': 'Ctrl+Shift+S',
40 | 'strikethrough': 'Ctrl+T',
41 | 'subscript': 'Ctrl+Shift+]',
42 | 'superscript': 'Ctrl+Shift+[',
43 | 'toggle-black-mode': 'Ctrl+Alt+E',
44 | 'toggle-dark-mode': 'Ctrl+D',
45 | 'toggle-focus-mode': 'Ctrl+K',
46 | 'toggle-notebooks': 'Shift+Alt+N',
47 | 'toggle-sepia-mode': 'Ctrl+G',
48 | 'toggle-sidebar': 'Ctrl+\\',
49 | 'toggle-tags': 'Shift+Alt+T',
50 | 'underline': 'Ctrl+U'
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/configs/win32.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | shortcutKeys: {
5 | 'add-link': 'Ctrl+Shift+K',
6 | 'add-shortcut': 'Ctrl+Alt+S',
7 | 'align-center': 'Ctrl+Alt+M',
8 | 'align-left': 'Ctrl+Alt+L',
9 | 'align-right': 'Ctrl+Alt+R',
10 | 'attach-file': 'Ctrl+Shift+F',
11 | 'bold': 'Ctrl+B',
12 | 'bulleted': 'Ctrl+Shift+.',
13 | 'checkbox': 'Ctrl+Shift+B',
14 | 'code-block': 'Ctrl+Shift+L',
15 | 'date-stamp': 'Ctrl+Shift+;',
16 | 'date-time-stamp': 'Ctrl+;',
17 | 'decrease-indentation': 'Ctrl+Shift+M',
18 | 'delete-note': 'Delete',
19 | 'export-html': 'Ctrl+Shift+H',
20 | 'export-markdown': 'Ctrl+O',
21 | 'export-pdf': 'Ctrl+Shift+E',
22 | 'global-create-note': 'Ctrl+Alt+C',
23 | 'global-search-note': 'Ctrl+Alt+F',
24 | 'global-toggle-window': 'Ctrl+Alt+A',
25 | 'horizontal-rule': 'Ctrl+Shift+-',
26 | 'increase-indentation': 'Ctrl+Alt+K',
27 | 'insert-drive': 'Ctrl+Shift+D',
28 | 'italic': 'Ctrl+I',
29 | 'new-note': 'Ctrl+N',
30 | 'new-notebook': 'Ctrl+Shift+N',
31 | 'new-tag': 'Ctrl+Shift+T',
32 | 'numbered': 'Ctrl+Shift+O',
33 | 'print': 'Ctrl+Alt+P',
34 | 'remove-formatting': 'Ctrl+Shift+Space',
35 | 'return': 'Esc',
36 | 'search': 'Ctrl+F',
37 | 'set-reminder': 'Ctrl+E',
38 | 'settings': 'Ctrl+,',
39 | 'shortcuts': 'Ctrl+Shift+S',
40 | 'strikethrough': 'Ctrl+T',
41 | 'subscript': 'Ctrl+Shift+]',
42 | 'superscript': 'Ctrl+Shift+[',
43 | 'toggle-black-mode': 'Ctrl+Alt+E',
44 | 'toggle-dark-mode': 'Ctrl+D',
45 | 'toggle-focus-mode': 'Ctrl+K',
46 | 'toggle-notebooks': 'Shift+Alt+N',
47 | 'toggle-sepia-mode': 'Ctrl+G',
48 | 'toggle-sidebar': 'Ctrl+\\',
49 | 'toggle-tags': 'Shift+Alt+T',
50 | 'underline': 'Ctrl+U'
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/dialog.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app, clipboard, dialog, shell} = require('electron');
3 | const os = require('os');
4 | const {activate} = require('./win');
5 | const {release} = require('./url');
6 | const file = require('./file');
7 | const settings = require('./settings');
8 |
9 | class Dialog {
10 | get _systemInfo() {
11 | return [
12 | `Version: ${app.getVersion()}`,
13 | `Electron: ${process.versions.electron}`,
14 | `Chrome: ${process.versions.chrome}`,
15 | `Node: ${process.versions.node}`,
16 | `V8: ${process.versions.v8}`,
17 | `OS: ${os.type()} ${os.arch()} ${os.release()}`
18 | ].join('\n');
19 | }
20 |
21 | _about() {
22 | return this._create({
23 | buttons: ['Done', 'Copy'],
24 | detail: `Created by Klaus Sinani.\n\n${this._systemInfo}`,
25 | message: `Tusk ${app.getVersion()} (${os.arch()})`,
26 | title: 'About Tusk'
27 | });
28 | }
29 |
30 | _create(options) {
31 | return dialog.showMessageBox(
32 | Object.assign({
33 | cancelId: 1,
34 | defaultId: 0,
35 | icon: file.icon
36 | }, options)
37 | );
38 | }
39 |
40 | _exit() {
41 | return this._create({
42 | buttons: ['Exit', 'Dismiss'],
43 | detail: 'Are you sure you want to exit?',
44 | message: 'Exit Tusk',
45 | title: 'Tusk - Exit Confirmation'
46 | });
47 | }
48 |
49 | _signOut() {
50 | return this._create({
51 | buttons: ['Sign Out', 'Dismiss'],
52 | detail: 'Are you sure you want to sign out?',
53 | message: 'Sign out of Tusk',
54 | title: 'Tusk - Sign Out Confirmation'
55 | });
56 | }
57 |
58 | _restart() {
59 | return this._create({
60 | buttons: ['Restart', 'Dismiss'],
61 | detail: 'Would you like to restart now?',
62 | message: 'Restart Tusk to activate your new settings',
63 | title: 'Tusk - Restart Required'
64 | });
65 | }
66 |
67 | _update(version) {
68 | return this._create({
69 | buttons: ['Download', 'Dismiss'],
70 | detail: 'Click Download to get it now',
71 | message: `Version ${version} is now available`,
72 | title: 'Update Tusk'
73 | });
74 | }
75 |
76 | confirmAbout() {
77 | if (this._about() === 1) {
78 | clipboard.writeText(this._systemInfo);
79 | }
80 | }
81 |
82 | confirmExit() {
83 | if (settings.get('requestExitConfirmation')) {
84 | if (this._exit() === 0) {
85 | app.quit();
86 | }
87 | } else {
88 | app.quit();
89 | }
90 | }
91 |
92 | confirmActivationRestart(option, state) {
93 | if (this._restart() === 0) {
94 | settings.set(option, state);
95 | app.quit();
96 | app.relaunch();
97 | }
98 | }
99 |
100 | confirmSignOut() {
101 | if (this._signOut() === 0) {
102 | activate('log-out');
103 | }
104 | }
105 |
106 | updateError(content) {
107 | return dialog.showErrorBox('Request to get update failed', content);
108 | }
109 |
110 | noUpdate() {
111 | return this._create({
112 | detail: `Tusk is running on the latest ${app.getVersion()} version`,
113 | message: 'There are currently no updates available',
114 | title: 'Tusk - No Update Available',
115 | buttons: ['Done']
116 | });
117 | }
118 |
119 | getUpdate(version) {
120 | if (this._update(version) === 0) {
121 | shell.openExternal(release);
122 | }
123 | }
124 | }
125 |
126 | module.exports = new Dialog();
127 |
--------------------------------------------------------------------------------
/src/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {join} = require('path');
3 | const {homedir} = require('os');
4 |
5 | module.exports = {
6 | icon: join(__dirname, '../static/Icon.png'),
7 | localConfig: join(homedir(), '.tusk.json'),
8 | style: join(__dirname, './style'),
9 | trayIcon: join(__dirname, '../static/IconTray.png')
10 | };
11 |
--------------------------------------------------------------------------------
/src/keymap.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {globalShortcut} = require('electron');
3 | const {shortcutKeys} = require('./config');
4 | const win = require('./win');
5 |
6 | const {log} = console;
7 |
8 | class Keymap {
9 | setAcc(custom, predefined) {
10 | if (Object.prototype.hasOwnProperty.call(shortcutKeys, custom)) {
11 | return shortcutKeys[custom];
12 | }
13 |
14 | return predefined;
15 | }
16 |
17 | registerGlobal() {
18 | const toggleTusk = globalShortcut.register(
19 | this.setAcc('global-toggle-window', 'CmdorCtrl+Alt+A'), () => {
20 | win.toggle();
21 | });
22 |
23 | const searchNote = globalShortcut.register(
24 | this.setAcc('global-search-note', 'CmdorCtrl+Alt+F'), () => {
25 | win.appear();
26 | win.activate('search');
27 | });
28 |
29 | const createNote = globalShortcut.register(
30 | this.setAcc('global-create-note', 'CmdorCtrl+Alt+C'), () => {
31 | win.appear();
32 | win.activate('new-note');
33 | });
34 |
35 | if (toggleTusk && searchNote && createNote) {
36 | log('Successfully registered global shortcut keys');
37 | } else {
38 | log('Global shortcut keys registration failed');
39 | }
40 | }
41 | }
42 |
43 | module.exports = new Keymap();
44 |
--------------------------------------------------------------------------------
/src/menu/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app} = require('electron');
3 | const dialog = require('./../dialog');
4 |
5 | module.exports = {
6 | label: app.getName(),
7 | submenu: [
8 | {
9 | role: 'services',
10 | submenu: []
11 | }, {
12 | type: 'separator'
13 | }, {
14 | role: 'hide'
15 | }, {
16 | role: 'hideothers'
17 | }, {
18 | role: 'unhide'
19 | }, {
20 | type: 'separator'
21 | }, {
22 | label: 'Exit',
23 | click() {
24 | dialog.confirmExit();
25 | }
26 | }
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/src/menu/edit.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | label: 'Edit',
5 | submenu: [
6 | {
7 | type: 'separator'
8 | }, {
9 | role: 'undo'
10 | }, {
11 | role: 'redo'
12 | }, {
13 | type: 'separator'
14 | }, {
15 | role: 'cut'
16 | }, {
17 | role: 'copy'
18 | }, {
19 | role: 'paste'
20 | }, {
21 | role: 'pasteandmatchstyle'
22 | }, {
23 | role: 'delete'
24 | }, {
25 | role: 'selectall'
26 | }
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/src/menu/file.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app, shell} = require('electron');
3 | const {activate} = require('./../win');
4 | const {is} = require('./../util');
5 | const {setAcc} = require('./../keymap');
6 | const dialog = require('./../dialog');
7 | const file = require('./../file');
8 | const settings = require('./../settings');
9 | const url = require('./../url');
10 |
11 | module.exports = {
12 | label: 'File',
13 | submenu: [
14 | {
15 | label: 'Search',
16 | accelerator: setAcc('search', 'CmdorCtrl+F'),
17 | click() {
18 | activate('search');
19 | }
20 | }, {
21 | type: 'separator'
22 | }, {
23 | label: 'Create',
24 | submenu: [
25 | {
26 | label: 'New Note',
27 | accelerator: setAcc('new-note', 'CmdorCtrl+N'),
28 | click() {
29 | activate('new-note');
30 | }
31 | }, {
32 | label: 'Delete Note',
33 | accelerator: setAcc('delete-note', 'Delete'),
34 | click() {
35 | activate('delete-note');
36 | }
37 | }, {
38 | type: 'separator'
39 | }, {
40 | label: 'New Tag',
41 | accelerator: setAcc('new-tag', 'CmdorCtrl+Shift+T'),
42 | click() {
43 | activate('new-tag');
44 | }
45 | }, {
46 | label: 'New Notebook',
47 | accelerator: setAcc('new-notebook', 'CmdorCtrl+Shift+N'),
48 | click() {
49 | activate('new-notebook');
50 | }
51 | }, {
52 | type: 'separator'
53 | }, {
54 | label: 'Set Reminder',
55 | accelerator: setAcc('set-reminder', 'CmdorCtrl+E'),
56 | click() {
57 | activate('set-reminder');
58 | }
59 | }, {
60 | label: 'Add Shortcut',
61 | accelerator: setAcc('add-shortcut', 'CmdorCtrl+Alt+S'),
62 | click() {
63 | activate('add-shortcut');
64 | }
65 | }
66 | ]
67 | }, {
68 | type: 'separator'
69 | }, {
70 | label: 'Navigate',
71 | submenu: [
72 | {
73 | label: 'Tags',
74 | accelerator: setAcc('toggle-tags', 'Shift+Alt+T'),
75 | click() {
76 | activate('toggle-tags');
77 | }
78 | }, {
79 | label: 'Shortcuts',
80 | accelerator: setAcc('shortcuts', 'CmdorCtrl+Shift+S'),
81 | click() {
82 | activate('shortcuts');
83 | }
84 | }, {
85 | label: 'Notebooks',
86 | accelerator: setAcc('toggle-notebooks', 'Shift+Alt+N'),
87 | click() {
88 | activate('toggle-notebooks');
89 | }
90 | }, {
91 | type: 'separator'
92 | }, {
93 | label: 'Return to Notes',
94 | accelerator: setAcc('return', 'Esc'),
95 | click() {
96 | activate('return');
97 | }
98 | }
99 | ]
100 | }, {
101 | type: 'separator'
102 | }, {
103 | label: 'Print Note',
104 | accelerator: setAcc('print', 'CmdorCtrl+Alt+P'),
105 | click() {
106 | activate('print');
107 | }
108 | }, {
109 | label: 'Export Note as',
110 | submenu: [
111 | {
112 | label: 'PDF File',
113 | accelerator: setAcc('export-pdf', 'CmdorCtrl+Shift+E'),
114 | click() {
115 | activate('export');
116 | }
117 | }, {
118 | label: 'HTML File',
119 | accelerator: setAcc('export-html', 'CmdorCtrl+Shift+H'),
120 | click() {
121 | activate('export-as-html');
122 | }
123 | }, {
124 | label: 'Markdown File',
125 | accelerator: setAcc('export-markdown', 'CmdorCtrl+O'),
126 | click() {
127 | activate('export-as-markdown');
128 | }
129 | }
130 | ]
131 | }, {
132 | type: 'separator'
133 | }, {
134 | label: 'Evernote Settings',
135 | accelerator: setAcc('settings', 'CmdorCtrl+,'),
136 | click() {
137 | shell.openExternal(url.settings);
138 | }
139 | }, {
140 | label: 'Launch on Start',
141 | type: 'checkbox',
142 | checked: settings.get('autoLaunch'),
143 | click(item) {
144 | settings.set('autoLaunch', item.checked);
145 | activate('auto-launch');
146 | }
147 | }, {
148 | label: 'Launch Minimized',
149 | type: 'checkbox',
150 | checked: settings.get('launchMinimized'),
151 | click(item) {
152 | settings.set('launchMinimized', item.checked);
153 | }
154 | }, {
155 | type: 'separator'
156 | }, {
157 | label: 'Edit Shortcut Keys',
158 | accelerator: 'CmdorCtrl+.',
159 | click() {
160 | shell.openExternal(file.localConfig);
161 | }
162 | }, {
163 | label: 'Enable Global Shortcut Keys',
164 | type: 'checkbox',
165 | checked: settings.get('useGlobalShortcuts'),
166 | click(item) {
167 | dialog.confirmActivationRestart('useGlobalShortcuts', item.checked);
168 | item.checked = settings.get('useGlobalShortcuts');
169 | }
170 | }, {
171 | label: 'Request Exit Confirmation',
172 | type: 'checkbox',
173 | checked: settings.get('requestExitConfirmation'),
174 | click(item) {
175 | settings.set('requestExitConfirmation', item.checked);
176 | }
177 | }, {
178 | type: 'separator'
179 | }, {
180 | label: 'Switch to Yinxiang',
181 | visible: !settings.get('useYinxiang'),
182 | click() {
183 | settings.set('useYinxiang', true);
184 | app.relaunch();
185 | app.quit();
186 | }
187 | }, {
188 | label: 'Switch to Evernote',
189 | visible: settings.get('useYinxiang'),
190 | click() {
191 | settings.set('useYinxiang', false);
192 | app.relaunch();
193 | app.quit();
194 | }
195 | }, {
196 | type: 'separator'
197 | }, {
198 | label: 'Log Out',
199 | click() {
200 | dialog.confirmSignOut();
201 | }
202 | }, {
203 | label: 'Exit',
204 | visible: !is.darwin,
205 | click() {
206 | dialog.confirmExit();
207 | }
208 | }
209 | ]
210 | };
211 |
--------------------------------------------------------------------------------
/src/menu/format.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {BrowserWindow} = require('electron');
3 | const {activate} = require('./../win');
4 | const {setAcc} = require('./../keymap');
5 | const time = require('./../time');
6 |
7 | module.exports = {
8 | label: 'Format',
9 | submenu: [
10 | {
11 | label: 'Style',
12 | submenu: [
13 | {
14 | label: 'Bold text',
15 | accelerator: setAcc('bold', 'CmdorCtrl+B'),
16 | click() {
17 | activate('bold');
18 | }
19 | }, {
20 | label: 'Italic text',
21 | accelerator: setAcc('italic', 'CmdorCtrl+I'),
22 | click() {
23 | activate('italic');
24 | }
25 | }, {
26 | label: 'Underline text',
27 | accelerator: setAcc('underline', 'CmdorCtrl+U'),
28 | click() {
29 | activate('underline');
30 | }
31 | }, {
32 | label: 'Strikethrough text',
33 | accelerator: setAcc('strikethrough', 'CmdorCtrl+T'),
34 | click() {
35 | activate('strikethrough');
36 | }
37 | }
38 | ]
39 | }, {
40 | label: 'Font Size',
41 | submenu: [
42 | {
43 | label: 'Header 1',
44 | accelerator: 'Alt+CmdorCtrl+1',
45 | click() {
46 | activate('header-one');
47 | }
48 | }, {
49 | label: 'Header 2',
50 | accelerator: 'Alt+CmdorCtrl+2',
51 | click() {
52 | activate('header-two');
53 | }
54 | }, {
55 | label: 'Header 3',
56 | accelerator: 'Alt+CmdorCtrl+3',
57 | click() {
58 | activate('header-three');
59 | }
60 | }, {
61 | label: 'Header 4',
62 | accelerator: 'Alt+CmdorCtrl+4',
63 | click() {
64 | activate('header-four');
65 | }
66 | }, {
67 | label: 'Header 5',
68 | accelerator: 'Alt+CmdorCtrl+5',
69 | click() {
70 | activate('header-five');
71 | }
72 | }, {
73 | label: 'Header 6',
74 | accelerator: 'Alt+CmdorCtrl+6',
75 | click() {
76 | activate('header-six');
77 | }
78 | }
79 | ]
80 | }, {
81 | label: 'Add link',
82 | accelerator: setAcc('add-link', 'CmdorCtrl+Shift+K'),
83 | click() {
84 | activate('add-link');
85 | }
86 | }, {
87 | label: 'Attach file',
88 | accelerator: setAcc('attach-file', 'CmdorCtrl+Shift+F'),
89 | click() {
90 | activate('attach-file');
91 | }
92 | }, {
93 | label: 'Insert from Drive',
94 | accelerator: setAcc('insert-drive', 'CmdorCtrl+Shift+D'),
95 | click() {
96 | activate('insert-drive');
97 | }
98 | }, {
99 | label: 'Paragraph',
100 | submenu: [
101 | {
102 | label: 'Align left',
103 | accelerator: setAcc('align-left', 'CmdorCtrl+Alt+L'),
104 | click() {
105 | activate('align-left');
106 | }
107 | }, {
108 | label: 'Align center',
109 | accelerator: setAcc('align-center', 'CmdorCtrl+Alt+M'),
110 | click() {
111 | activate('align-center');
112 | }
113 | }, {
114 | label: 'Align right',
115 | accelerator: setAcc('align-right', 'CmdorCtrl+Alt+R'),
116 | click() {
117 | activate('align-right');
118 | }
119 | }, {
120 | type: 'separator'
121 | }, {
122 | label: 'Increase indentation',
123 | accelerator: setAcc('increase-indentation', 'CmdorCtrl+Alt+K'),
124 | click() {
125 | activate('increase-indentation');
126 | }
127 | }, {
128 | label: 'Decrease indentation',
129 | accelerator: setAcc('decrease-indentation', 'CmdorCtrl+Shift+M'),
130 | click() {
131 | activate('decrease-indentation');
132 | }
133 | }, {
134 | type: 'separator'
135 | }, {
136 | label: 'Numbered list',
137 | accelerator: setAcc('numbered', 'CmdorCtrl+Shift+O'),
138 | click() {
139 | activate('numbered');
140 | }
141 | }, {
142 | label: 'Bulleted list',
143 | accelerator: setAcc('bulleted', 'CmdorCtrl+Shift+.'),
144 | click() {
145 | activate('bulleted');
146 | }
147 | }
148 | ]
149 | }, {
150 | type: 'separator'
151 | }, {
152 | label: 'Insert Date Stamp',
153 | accelerator: setAcc('date-stamp', 'CmdOrCtrl+Shift+;'),
154 | click() {
155 | const [appWindow] = BrowserWindow.getAllWindows();
156 | appWindow.webContents.insertText(time.date());
157 | }
158 | }, {
159 | label: 'Insert Date-Time Stamp',
160 | accelerator: setAcc('date-time-stamp', 'CmdOrCtrl+;'),
161 | click() {
162 | const [appWindow] = BrowserWindow.getAllWindows();
163 | appWindow.webContents.insertText(time.dateTime());
164 | }
165 | }, {
166 | type: 'separator'
167 | }, {
168 | label: 'Checkbox',
169 | accelerator: setAcc('checkbox', 'CmdorCtrl+Shift+B'),
170 | click() {
171 | activate('checkbox');
172 | }
173 | }, {
174 | label: 'Code block',
175 | accelerator: setAcc('code-block', 'CmdorCtrl+Shift+L'),
176 | click() {
177 | activate('code-block');
178 | }
179 | }, {
180 | type: 'separator'
181 | }, {
182 | label: 'Subscript text',
183 | accelerator: setAcc('subscript', 'CmdorCtrl+Shift+]'),
184 | click() {
185 | activate('subscript');
186 | }
187 | }, {
188 | label: 'Superscript text',
189 | accelerator: setAcc('superscript', 'CmdorCtrl+Shift+['),
190 | click() {
191 | activate('superscript');
192 | }
193 | }, {
194 | type: 'separator'
195 | }, {
196 | label: 'Remove Formatting',
197 | accelerator: setAcc('remove-formatting', 'CmdorCtrl+Shift+Space'),
198 | click() {
199 | activate('remove-formatting');
200 | }
201 | }, {
202 | label: 'Insert Horizontal Rule',
203 | accelerator: setAcc('horizontal-rule', 'CmdorCtrl+Shift+-'),
204 | click() {
205 | activate('horizontal-rule');
206 | }
207 | }
208 | ]
209 | };
210 |
--------------------------------------------------------------------------------
/src/menu/help.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app, shell} = require('electron');
3 | const dialog = require('./../dialog');
4 | const settings = require('./../settings');
5 | const update = require('./../update');
6 | const url = require('./../url');
7 |
8 | module.exports = {
9 | label: 'Help',
10 | submenu: [
11 | {
12 | label: 'View License',
13 | click() {
14 | shell.openExternal(url.license);
15 | }
16 | }, {
17 | label: `Version ${app.getVersion()}`,
18 | enabled: false
19 | }, {
20 | label: 'Tusk Homepage',
21 | click() {
22 | shell.openExternal(url.homepage);
23 | }
24 | }, {
25 | label: 'Check for Update',
26 | click() {
27 | update.check();
28 | }
29 | }, {
30 | label: 'Update Check Frequency',
31 | enabled: !settings.get('disableAutoUpdateCheck'),
32 | submenu: [
33 | {
34 | label: 'Once Every 4 Hours',
35 | type: 'checkbox',
36 | checked: settings.get('updateCheckPeriod') === '4',
37 | click(item) {
38 | dialog.confirmActivationRestart('updateCheckPeriod', '4');
39 | item.checked = settings.get('updateCheckPeriod') === '4';
40 | }
41 | }, {
42 | label: 'Once Every 8 Hours',
43 | type: 'checkbox',
44 | checked: settings.get('updateCheckPeriod') === '8',
45 | click(item) {
46 | dialog.confirmActivationRestart('updateCheckPeriod', '8');
47 | item.checked = settings.get('updateCheckPeriod') === '8';
48 | }
49 | }, {
50 | label: 'Once Every 12 Hours',
51 | type: 'checkbox',
52 | checked: settings.get('updateCheckPeriod') === '12',
53 | click(item) {
54 | dialog.confirmActivationRestart('updateCheckPeriod', '12');
55 | item.checked = settings.get('updateCheckPeriod') === '12';
56 | }
57 | }, {
58 | label: 'Once a Day',
59 | type: 'checkbox',
60 | checked: settings.get('updateCheckPeriod') === '24',
61 | click(item) {
62 | dialog.confirmActivationRestart('updateCheckPeriod', '24');
63 | item.checked = settings.get('updateCheckPeriod') === '24';
64 | }
65 | }
66 | ]
67 | }, {
68 | label: 'Disable Automatic Update Check',
69 | type: 'checkbox',
70 | checked: settings.get('disableAutoUpdateCheck'),
71 | click(item) {
72 | dialog.confirmActivationRestart('disableAutoUpdateCheck', item.checked);
73 | item.checked = settings.get('disableAutoUpdateCheck');
74 | }
75 | }, {
76 | type: 'separator'
77 | }, {
78 | label: 'Keyboard Shortcuts Reference',
79 | click() {
80 | shell.openExternal(url.keyboardShortcutsRef);
81 | }
82 | }, {
83 | type: 'separator'
84 | }, {
85 | label: 'Fork Source',
86 | click() {
87 | shell.openExternal(url.source);
88 | }
89 | }, {
90 | label: 'Report Issue',
91 | click() {
92 | shell.openExternal(url.issue);
93 | }
94 | }, {
95 | label: 'Search Issues',
96 | click() {
97 | shell.openExternal(url.search);
98 | }
99 | }, {
100 | label: 'Search Feature Requests',
101 | click() {
102 | shell.openExternal(url.searchFeatureRequests);
103 | }
104 | }, {
105 | label: 'Community Discussion',
106 | click() {
107 | shell.openExternal(url.community);
108 | }
109 | }, {
110 | type: 'separator'
111 | }, {
112 | role: 'about',
113 | click() {
114 | dialog.confirmAbout();
115 | }
116 | }
117 | ]
118 | };
119 |
--------------------------------------------------------------------------------
/src/menu/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {Menu} = require('electron');
3 | const {is} = require('./../util');
4 | const app = require('./app');
5 | const edit = require('./edit');
6 | const file = require('./file');
7 | const format = require('./format');
8 | const help = require('./help');
9 | const view = require('./view');
10 | const window = require('./window');
11 |
12 | const darwin = [app, file, edit, format, view, window, help];
13 | const rest = [file, edit, format, view, help];
14 |
15 | module.exports = Menu.buildFromTemplate(is.darwin ? darwin : rest);
16 |
--------------------------------------------------------------------------------
/src/menu/tray.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {shell} = require('electron');
3 | const dialog = require('./../dialog');
4 | const settings = require('./../settings');
5 | const url = require('./../url');
6 | const win = require('./../win');
7 |
8 | module.exports = [
9 | {
10 | label: 'Open Tusk',
11 | click() {
12 | win.toggle();
13 | }
14 | }, {
15 | type: 'separator'
16 | }, {
17 | label: 'Search',
18 | click() {
19 | win.appear();
20 | win.activate('search');
21 | }
22 | }, {
23 | type: 'separator'
24 | }, {
25 | label: 'Create',
26 | submenu: [
27 | {
28 | label: 'New Tag',
29 | click() {
30 | win.appear();
31 | win.activate('new-tag');
32 | }
33 | }, {
34 | label: 'New Note',
35 | click() {
36 | win.appear();
37 | win.activate('new-note');
38 | }
39 | }, {
40 | label: 'New Notebook',
41 | click() {
42 | win.appear();
43 | win.activate('new-notebook');
44 | }
45 | }
46 | ]
47 | }, {
48 | type: 'separator'
49 | }, {
50 | label: 'Toggle Theme',
51 | submenu: [
52 | {
53 | label: 'Sepia Theme',
54 | click() {
55 | win.appear();
56 | win.activate('toggle-sepia-mode');
57 | }
58 | }, {
59 | label: 'Dark Theme',
60 | click() {
61 | win.appear();
62 | win.activate('toggle-dark-mode');
63 | }
64 | }, {
65 | label: 'Black Theme',
66 | click() {
67 | win.appear();
68 | win.activate('toggle-black-mode');
69 | }
70 | }
71 | ]
72 | }, {
73 | label: 'Auto Night Mode',
74 | type: 'checkbox',
75 | checked: settings.get('autoNightMode'),
76 | click(item) {
77 | win.appear();
78 | settings.set('autoNightMode', item.checked);
79 | win.activate('auto-night-mode');
80 | }
81 | }, {
82 | type: 'separator'
83 | }, {
84 | label: 'Evernote Settings',
85 | click() {
86 | shell.openExternal(url.settings);
87 | }
88 | }, {
89 | label: 'Report Issue',
90 | click() {
91 | shell.openExternal(url.issue);
92 | }
93 | }, {
94 | type: 'separator'
95 | }, {
96 | label: 'Exit',
97 | click() {
98 | dialog.confirmExit();
99 | }
100 | }
101 | ];
102 |
--------------------------------------------------------------------------------
/src/menu/view.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {activate} = require('./../win');
3 | const {is} = require('./../util');
4 | const {setAcc} = require('./../keymap');
5 | const dialog = require('./../dialog');
6 | const settings = require('./../settings');
7 |
8 | module.exports = {
9 | label: 'View',
10 | submenu: [
11 | {
12 | label: 'Reload',
13 | accelerator: 'CmdOrCtrl+Shift+R',
14 | click: (_, focusedWindow) => {
15 | if (focusedWindow) {
16 | focusedWindow.reload();
17 | }
18 | }
19 | }, {
20 | type: 'separator'
21 | }, {
22 | label: 'Font Size Options',
23 | submenu: [
24 | {
25 | label: 'Make Text Larger',
26 | accelerator: 'CmdOrCtrl+Plus',
27 | click() {
28 | activate('zoom-in');
29 | }
30 | }, {
31 | label: 'Make Text Smaller',
32 | accelerator: 'CmdOrCtrl+-',
33 | click() {
34 | activate('zoom-out');
35 | }
36 | }, {
37 | label: 'Reset Zoom Level',
38 | accelerator: 'CmdOrCtrl+0',
39 | click() {
40 | activate('zoom-reset');
41 | }
42 | }
43 | ]
44 | }, {
45 | type: 'separator'
46 | }, {
47 | label: 'Toggle Theme',
48 | submenu: [
49 | {
50 | label: 'Sepia Theme',
51 | accelerator: setAcc('toggle-sepia-mode', 'CmdOrCtrl+G'),
52 | click() {
53 | activate('toggle-sepia-mode');
54 | }
55 | }, {
56 | label: 'Dark Theme',
57 | accelerator: setAcc('toggle-dark-mode', 'CmdOrCtrl+D'),
58 | click() {
59 | activate('toggle-dark-mode');
60 | }
61 | }, {
62 | label: 'Black Theme',
63 | accelerator: setAcc('toggle-black-mode', 'CmdOrCtrl+Alt+E'),
64 | click() {
65 | activate('toggle-black-mode');
66 | }
67 | }
68 | ]
69 | }, {
70 | label: 'Auto Night Mode',
71 | type: 'checkbox',
72 | checked: settings.get('autoNightMode'),
73 | accelerator: 'CmdorCtrl+Alt+N',
74 | click(item) {
75 | settings.set('autoNightMode', item.checked);
76 | activate('auto-night-mode');
77 | }
78 | }, {
79 | type: 'separator'
80 | }, {
81 | label: 'Navigate to Next Note',
82 | accelerator: 'CmdorCtrl+Tab',
83 | click() {
84 | activate('next-note');
85 | }
86 | }, {
87 | label: 'Navigate to Previous Note',
88 | accelerator: 'CmdorCtrl+Shift+Tab',
89 | click() {
90 | activate('previous-note');
91 | }
92 | }, {
93 | type: 'separator'
94 | }, {
95 | label: 'Always on Top',
96 | type: 'checkbox',
97 | checked: settings.get('alwaysOnTop'),
98 | accelerator: 'CmdorCtrl+Shift+P',
99 | click(item, focusedWindow) {
100 | settings.set('alwaysOnTop', item.checked);
101 | focusedWindow.setAlwaysOnTop(item.checked);
102 | }
103 | }, {
104 | label: 'Hide Tray Icon',
105 | type: 'checkbox',
106 | visible: !is.darwin,
107 | checked: settings.get('hideTray'),
108 | click(item) {
109 | dialog.confirmActivationRestart('hideTray', item.checked);
110 | item.checked = settings.get('hideTray');
111 | }
112 | }, {
113 | type: 'separator'
114 | }, {
115 | label: 'Toggle Side Bar',
116 | type: 'checkbox',
117 | checked: !settings.get('sideBarHidden'),
118 | accelerator: setAcc('toggle-sidebar', 'CmdorCtrl+\\'),
119 | click() {
120 | activate('toggle-side-bar');
121 | }
122 | }, {
123 | label: 'Toggle Menu Bar',
124 | type: 'checkbox',
125 | checked: !settings.get('menuBarHidden'),
126 | visible: !is.darwin,
127 | click(item, focusedWindow) {
128 | settings.set('menuBarHidden', !item.checked);
129 | focusedWindow.setMenuBarVisibility(item.checked);
130 | focusedWindow.setAutoHideMenuBar(!item.checked);
131 | }
132 | }, {
133 | label: 'Toggle Focus Mode',
134 | accelerator: setAcc('toggle-focus-mode', 'CmdOrCtrl+K'),
135 | click() {
136 | activate('focus-mode');
137 | }
138 | }, {
139 | label: 'Toggle Full Screen',
140 | accelerator: is.darwin ? 'Ctrl+Command+F' : 'F11',
141 | click: (_, focusedWindow) => {
142 | if (focusedWindow) {
143 | focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
144 | focusedWindow.send('window:fullscreen', {state: focusedWindow.isFullScreen()});
145 | }
146 | }
147 | }, {
148 | label: 'Toggle Developer Tools',
149 | accelerator: is.darwin ? 'Alt+Command+I' : 'Ctrl+Shift+I',
150 | click: (_, focusedWindow) => {
151 | focusedWindow.toggleDevTools();
152 | }
153 | }
154 | ]
155 | };
156 |
--------------------------------------------------------------------------------
/src/menu/window.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | role: 'window',
5 | submenu: [
6 | {
7 | role: 'minimize'
8 | }, {
9 | role: 'close'
10 | }, {
11 | role: 'front'
12 | }
13 | ]
14 | };
15 |
--------------------------------------------------------------------------------
/src/mode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const nav = require('./nav');
3 | const settings = require('./settings');
4 | const time = require('./time');
5 |
6 | class Mode {
7 | get _colors() {
8 | return {
9 | black: 'lightgrey',
10 | dark: 'lightgrey',
11 | default: 'black',
12 | sepia: 'black'
13 | };
14 | }
15 |
16 | async _setFontColor(mode) {
17 | const frame = await this.getNoteFrame();
18 | const style = document.createElement('style');
19 | const fontColor = settings.get(`mode.${mode}`) ? this._colors[mode] : this._colors.default;
20 | style.textContent = `body {color: ${fontColor};}`;
21 | return frame.contentDocument.head.append(style);
22 | }
23 |
24 | _toggle(mode) {
25 | const modes = settings.get('mode');
26 |
27 | Object.keys(modes).forEach(x => {
28 | settings.set(`mode.${x}`, (x === mode) ? !modes[x] : false);
29 | document.documentElement.classList.toggle(`${x}-mode`, settings.get(`mode.${x}`));
30 | });
31 |
32 | this._setFontColor(mode);
33 | }
34 |
35 | _enableAutoNight() {
36 | if (time.isDaytime()) {
37 | this._toggle(null);
38 | } else if (!settings.get('mode.dark')) {
39 | this._toggle('dark');
40 | }
41 |
42 | setTimeout(() => {
43 | if (settings.get('autoNightMode')) {
44 | return this._enableAutoNight();
45 | }
46 | }, time.ms(time.transitionSpan()));
47 | }
48 |
49 | _disableAutoNight() {
50 | this._toggle(null);
51 | }
52 |
53 | autoNight() {
54 | return settings.get('autoNightMode') ? this._enableAutoNight() : this._disableAutoNight();
55 | }
56 |
57 | black() {
58 | this._toggle('black');
59 | }
60 |
61 | dark() {
62 | this._toggle('dark');
63 | }
64 |
65 | getNoteFrame() {
66 | return new Promise(resolve => {
67 | const checkNoteFrame = () => {
68 | const frame = nav.select('.RichTextArea-entinymce');
69 | if (frame) {
70 | resolve(frame);
71 | }
72 |
73 | setTimeout(checkNoteFrame, 50);
74 | };
75 |
76 | checkNoteFrame();
77 | });
78 | }
79 |
80 | restore() {
81 | const modes = settings.get('mode');
82 | Object.keys(modes).forEach(x => {
83 | if (modes[x]) {
84 | this._setFontColor(x);
85 | document.documentElement.classList.toggle(`${x}-mode`, modes[x]);
86 | }
87 | });
88 | }
89 |
90 | async autoWritingDirection() {
91 | const frame = await this.getNoteFrame();
92 | frame.contentDocument.body.setAttribute('dir', 'auto');
93 | document.querySelector('#gwt-debug-NoteTitleView-container').setAttribute('dir', 'auto');
94 | }
95 |
96 | sepia() {
97 | this._toggle('sepia');
98 | }
99 |
100 | toggleFocus() {
101 | const isFocused = document.querySelector('#gwt-debug-NoteAttributes-focusButton').style.length;
102 |
103 | if (isFocused) {
104 | return nav.click('#gwt-debug-NoteAttributes-doneButton');
105 | }
106 |
107 | nav.click('#gwt-debug-NoteAttributes-focusButton');
108 | }
109 | }
110 |
111 | module.exports = new Mode();
112 |
--------------------------------------------------------------------------------
/src/nav.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {webFrame} = require('electron');
3 | const {is} = require('./util');
4 | const settings = require('./settings');
5 |
6 | class Nav {
7 | constructor() {
8 | this._defaultZoomFactor = 1.0;
9 | this._lowerZoomLimit = 0.7;
10 | this._noteSelector = '.focus-NotesView-Note';
11 | this._notesList = '.NotesView-ScrollWindow > div';
12 | this._notesListSelector = '.NotesView-ScrollWindow';
13 | this._scrollStep = 110;
14 | this._selectedNoteSelector = '.focus-NotesView-Note-selected';
15 | this._upperZoomLimit = 1.3;
16 | this._zoomStep = 0.05;
17 | }
18 |
19 | _currentIdx() {
20 | let currentIdx = 0;
21 |
22 | const selectedNote = document.querySelector(this._selectedNoteSelector);
23 | const notesArray = document.querySelector(this._notesListSelector).querySelectorAll(this._noteSelector);
24 |
25 | for (let i = 0; i < notesArray.length; i++) {
26 | if (notesArray[i] === selectedNote) {
27 | currentIdx = i + 1;
28 | }
29 | }
30 |
31 | return currentIdx;
32 | }
33 |
34 | _scrollUp() {
35 | const notesScrollbox = document.querySelector(this._notesListSelector);
36 | notesScrollbox.scrollTop -= this._scrollStep;
37 | }
38 |
39 | _scrollDown() {
40 | const notesScrollbox = document.querySelector(this._notesListSelector);
41 | notesScrollbox.scrollTop += this._scrollStep;
42 | }
43 |
44 | click(x) {
45 | document.querySelector(x).click();
46 | }
47 |
48 | jumpToNote(event) {
49 | const comboKey = is.darwin ? event.metaKey : event.ctrlKey;
50 |
51 | if (!comboKey) {
52 | return null;
53 | }
54 |
55 | const n = parseInt(event.key, 10);
56 |
57 | if (n < 10 && n > 0) {
58 | this.selectNote(n);
59 | }
60 | }
61 |
62 | nextNote() {
63 | const idx = this._currentIdx();
64 | this.selectNote(idx + 1);
65 | this._scrollDown();
66 | }
67 |
68 | previousNote() {
69 | const idx = this._currentIdx();
70 | this.selectNote(idx - 1);
71 | this._scrollUp();
72 | }
73 |
74 | select(x) {
75 | return document.querySelector(x);
76 | }
77 |
78 | selectNote(index) {
79 | document.querySelector(this._notesList).children[index].firstChild.firstChild.click();
80 | }
81 |
82 | sideBar() {
83 | document.documentElement.classList.toggle('side-bar-hidden', settings.get('sideBarHidden'));
84 |
85 | if (is.darwin) {
86 | // Macos visual tweak
87 | document.documentElement.classList.toggle('side-bar-hidden-macos', settings.get('sideBarHidden'));
88 | }
89 | }
90 |
91 | zoomIn() {
92 | const zoomFactor = webFrame.getZoomFactor() + this._zoomStep;
93 |
94 | if (zoomFactor < this._upperZoomLimit) {
95 | webFrame.setZoomFactor(zoomFactor);
96 | settings.set('zoomFactor', zoomFactor);
97 | }
98 | }
99 |
100 | zoomReset() {
101 | webFrame.setZoomFactor(this._defaultZoomFactor);
102 | settings.set('zoomFactor', this._defaultZoomFactor);
103 | }
104 |
105 | zoomRestore() {
106 | webFrame.setZoomFactor(settings.get('zoomFactor'));
107 | }
108 |
109 | zoomOut() {
110 | const zoomFactor = webFrame.getZoomFactor() - this._zoomStep;
111 |
112 | if (zoomFactor > this._lowerZoomLimit) {
113 | webFrame.setZoomFactor(zoomFactor);
114 | settings.set('zoomFactor', zoomFactor);
115 | }
116 | }
117 | }
118 |
119 | module.exports = new Nav();
120 |
--------------------------------------------------------------------------------
/src/pdf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {BrowserWindow, dialog, shell} = require('electron');
3 | const {join} = require('path');
4 | const {tmpdir} = require('os');
5 | const {writeFile} = require('fs');
6 | const {formatExternal, is} = require('./util');
7 | const {stamp} = require('./time');
8 |
9 | const {log} = console;
10 |
11 | class Pdf {
12 | get _opts() {
13 | return {
14 | filters: [
15 | {
16 | name: 'PDF File',
17 | extensions: ['pdf']
18 | }, {
19 | name: 'All Files',
20 | extensions: ['*']
21 | }
22 | ]
23 | };
24 | }
25 |
26 | print(event) {
27 | const path = join(tmpdir(), `Tusk_Note_${stamp()}.pdf`);
28 | const {webContents} = BrowserWindow.fromWebContents(event.sender);
29 |
30 | webContents.printToPDF({}, (error, data) => {
31 | if (error) {
32 | return log(error);
33 | }
34 |
35 | writeFile(path, data, error => {
36 | if (error) {
37 | return log(error);
38 | }
39 |
40 | shell.openExternal(formatExternal(path));
41 | });
42 | });
43 | }
44 |
45 | save(event, title) {
46 | const opts = Object.assign(this._opts, {defaultPath: title});
47 | const {webContents} = BrowserWindow.fromWebContents(event.sender);
48 |
49 | webContents.printToPDF({}, (error, data) => {
50 | if (error) {
51 | return log(error);
52 | }
53 |
54 | dialog.showSaveDialog(opts, path => {
55 | if (is.undef(path)) {
56 | return;
57 | }
58 |
59 | writeFile(path, data, error => {
60 | if (error) {
61 | return dialog.showErrorBox('Failed to export note as PDF', error.message);
62 | }
63 | });
64 | });
65 | });
66 | }
67 | }
68 |
69 | module.exports = new Pdf();
70 |
--------------------------------------------------------------------------------
/src/save.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {writeFile} = require('fs');
3 | const electron = require('electron');
4 | const Turndown = require('turndown');
5 | const {is} = require('./util');
6 | const mode = require('./mode');
7 |
8 | const {dialog} = electron.remote;
9 | const {log} = console;
10 |
11 | class Save {
12 | get _opts() {
13 | return {
14 | md: {
15 | filters: [
16 | {
17 | name: 'Markdown File',
18 | extensions: ['md']
19 | }, {
20 | name: 'All Files',
21 | extensions: ['*']
22 | }
23 | ]
24 | },
25 | html: {
26 | filters: [
27 | {
28 | name: 'HTML File',
29 | extensions: ['html']
30 | }, {
31 | name: 'All Files',
32 | extensions: ['*']
33 | }
34 | ]
35 | }
36 | };
37 | }
38 |
39 | _getTitle() {
40 | const title = document.querySelector('#gwt-debug-NoteTitleView-label').innerHTML;
41 | return title.length > 0 ? title.trim().replace(/ /g, ' ') : 'note';
42 | }
43 |
44 | _toHTML(noteFrame) {
45 | return noteFrame.contentDocument.body.innerHTML;
46 | }
47 |
48 | _toMarkdown(noteFrame) {
49 | const turndownUtil = new Turndown();
50 | return turndownUtil.turndown(noteFrame.contentDocument.body);
51 | }
52 |
53 | _save(opts, data) {
54 | const options = Object.assign(opts, {defaultPath: this._getTitle()});
55 |
56 | dialog.showSaveDialog(options, path => {
57 | if (is.undef(path)) {
58 | return;
59 | }
60 |
61 | writeFile(path, data, error => {
62 | if (error) {
63 | dialog.showErrorBox('Failed to export note.', error.message);
64 | return log(error);
65 | }
66 | });
67 | });
68 | }
69 |
70 | async md() {
71 | const noteFrame = await mode.getNoteFrame();
72 | return this._save(this._opts.md, this._toMarkdown(noteFrame));
73 | }
74 |
75 | async html() {
76 | const noteFrame = await mode.getNoteFrame();
77 | return this._save(this._opts.html, this._toHTML(noteFrame));
78 | }
79 | }
80 |
81 | module.exports = new Save();
82 |
--------------------------------------------------------------------------------
/src/settings.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const settings = require('electron-settings');
3 | const {touchFileSync} = require('./util');
4 |
5 | touchFileSync(settings.file());
6 |
7 | settings.setAll({
8 | alwaysOnTop: settings.get('alwaysOnTop', false),
9 | autoLaunch: settings.get('autoLaunch', false),
10 | autoNightMode: settings.get('autoNightMode', false),
11 | disableAutoUpdateCheck: settings.get('disableAutoUpdateCheck', false),
12 | hideTray: settings.get('hideTray', false),
13 | lastWindowState: {
14 | x: settings.get('lastWindowState.x'),
15 | y: settings.get('lastWindowState.y'),
16 | width: settings.get('lastWindowState.width'),
17 | height: settings.get('lastWindowState.height')
18 | },
19 | launchMinimized: settings.get('launchMinimized', false),
20 | menuBarHidden: settings.get('menuBarHidden', false),
21 | mode: {
22 | black: settings.get('mode.black', false),
23 | dark: settings.get('mode.dark', false),
24 | sepia: settings.get('mode.sepia', false)
25 | },
26 | requestExitConfirmation: settings.get('requestExitConfirmation', true),
27 | sideBarHidden: settings.get('sideBarHidden', false),
28 | updateCheckPeriod: settings.get('updateCheckPeriod', '4'),
29 | useGlobalShortcuts: settings.get('useGlobalShortcuts', false),
30 | useYinxiang: settings.get('useYinxiang', false),
31 | zoomFactor: settings.get('zoomFactor', 1)
32 | });
33 |
34 | module.exports = settings;
35 |
--------------------------------------------------------------------------------
/src/startup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app} = require('electron');
3 | const AutoLaunch = require('auto-launch');
4 | const {is} = require('./util');
5 | const settings = require('./settings');
6 |
7 | const _settings = {
8 | name: 'Tusk',
9 | path: is.darwin ? app.getPath('exe').replace(/\.app\/Content.*/, '.app') : undefined,
10 | isHidden: true
11 | };
12 |
13 | class Startup {
14 | constructor(settings) {
15 | this._launcher = new AutoLaunch(settings);
16 | }
17 |
18 | async _activate() {
19 | const enabled = await this._launcher.isEnabled();
20 | if (!enabled) {
21 | return this._launcher.enable();
22 | }
23 | }
24 |
25 | async _deactivate() {
26 | const enabled = await this._launcher.isEnabled();
27 | if (enabled) {
28 | return this._launcher.disable();
29 | }
30 | }
31 |
32 | autoLaunch() {
33 | if (settings.get('autoLaunch')) {
34 | this._activate();
35 | } else {
36 | this._deactivate();
37 | }
38 | }
39 | }
40 |
41 | module.exports = new Startup(_settings);
42 |
--------------------------------------------------------------------------------
/src/style/black-mode.css:
--------------------------------------------------------------------------------
1 | html.black-mode ::-webkit-scrollbar-track { /* Notes list scrollbar track */
2 | background: var(--black) !important;
3 | }
4 | html.black-mode div#gwt-debug-NotesHeader-title, html.black-mode div#gwt-debug-NoteAttributesView-root, html.black-mode div#gwt-debug-NotebooksDrawerView-root, html.black-mode div#gwt-debug-AccountMenuPopup-root, html.black-mode div#gwt-debug-arrowChatView, html.black-mode div#gwt-debug-ReminderSetDialog-root {
5 | background-color: var(--black) !important;
6 | }
7 | html.black-mode .GJDCG5CJEB { /* Metting note background */
8 | background-color: var(--black) !important;
9 | }
10 | html.black-mode .GJDCG5COYB { /* Note list header */
11 | background-color: var(--black) !important;
12 | }
13 | html.black-mode .GJDCG5CAR.GJDCG5CBR, html.black-mode .GJDCG5CCR {
14 | opacity: 1 !important;
15 | }
16 | html.black-mode .Dropdown-menu {
17 | background: var(--black) !important;
18 | }
19 | html.black-mode .GJDCG5CMGB, html.black-mode .GDAMOPFCAHB, html.black-mode .GDAMOPFCPGB, html.black-mode .GDAMOPFCHYC.GDAMOPFCLD, html.black-mode .GJDCG5CFKB { /* Chat windows */
20 | background: var(--black) !important;
21 | }
22 | html.black-mode .GJDCG5CKKC, html.black-mode .GJDCG5CK-B { /* Reminder modal */
23 | background: var(--black) !important;
24 | }
25 | html.black-mode .GDAMOPFCHEB {
26 | background: var(--black) !important;
27 | }
28 | html.black-mode .GJDCG5CJ-B, html.black-mode .GDAMOPFCEQB, html.black-mode .GJDCG5CGQB, html.black-mode .GDAMOPFCDAC.GDAMOPFCM5B, html.black-mode .GJDCG5CO-B .GJDCG5CD1B, html.black-mode .GJDCG5CL-B .GJDCG5CP0B:hover { /* Search modal */
29 | background-color: var(--black) !important;
30 | }
31 | html.black-mode .GJDCG5CK-B, html.black-mode .GJDCG5CP-B, html.black-mode .GJDCG5CDXB {
32 | border-bottom: 1px solid var(--white-five) !important;
33 | }
34 | html.black-mode .GJDCG5CEXB {
35 | border-bottom: 1px solid var(--black) !important;
36 | }
37 | html.black-mode .GJDCG5CGWB, html.black-mode .GDAMOPFCIXB, html.black-mode .GJDCG5CKXB.GJDCG5CHXB, html.black-mode .GDAMOPFCFTB { /* Notebooks modal */
38 | background-color: var(--black) !important;
39 | }
40 | html.black-mode .GJDCG5CDXB.GJDCG5CPXB { /* Selected notebook in notebooks modal */
41 | background-color: #2DBE60 !important;
42 | }
43 | html.black-mode .GJDCG5CITB { /* Select notebook at toolbar option */
44 | background-color: var(--black) !important;
45 | }
46 | html.black-mode .GJDCG5CJJ, html.black-mode .focus-drawer-Filter-input { /* Search notebook/tags input text */
47 | color: #E1E1E1 !important;
48 | }
49 | html.black-mode div#gwt-debug-GlassModalDialog-container { /* Glass modal background */
50 | background-color: var(--black) !important;
51 | }
52 | html.black-mode div#gwt-debug-GlassModalDialog-glass { /* Glass modal bottom line */
53 | background-color: var(--black) !important;
54 | }
55 | html.black-mode .GDAMOPFCPE { /* Notebook info */
56 | color: #E1E1E1 !important;
57 | }
58 | html.black-mode .GDAMOPFCN-B .GDAMOPFCC1B, html.black-mode .GDAMOPFCDQB { /* Saved search */
59 | background-color: var(--black) !important;
60 | }
61 | html.black-mode .GJDCG5CJ3B, html.black-mode .GJDCG5CI3B, html.black-mode .GJDCG5CH3B, html.black-mode .GJDCG5CN5B, html.black-mode .GJDCG5CGHB, html.black-mode .GJDCG5CFHB, html.black-mode .GJDCG5CM5B { /* Shortcuts modal */
62 | background-color: var(--black) !important;
63 | border-right: 3px solid var(--white-five) !important;
64 | }
65 | html.black-mode .GJDCG5CP2 { /* Settings menu */
66 | background-color: var(--black) !important;
67 | }
68 | html.black-mode .GJDCG5CB5B {
69 | background: var(--black) !important;
70 | background-color: var(--black) !important;
71 | border-right: 1px solid var(--white-five) !important;
72 | }
73 | html.black-mode .GDAMOPFCJYB { /* Notes list head */
74 | background-color: var(--black) !important;
75 | }
76 | html.black-mode .GJDCG5CBQB { /* Notes list left border */
77 | background-color: var(--black) !important;
78 | border-left: 1px solid var(--white-five) !important;
79 | }
80 | html.black-mode .GJDCG5CB-B {
81 | background-color: var(--black) !important;
82 | }
83 | html.black-mode .GDAMOPFCA5B { /* Notes list left border */
84 | border-left: 1px solid var(--white-five) !important;
85 | border-right: 1px solid var(--white-five) !important;
86 | background: var(--black) !important;
87 | background-color: var(--black) !important;
88 | }
89 | html.black-mode .focus-NotesView-Note.focus-NotesView-Note-selected .focus-NotesView-Note-selectOverlay { /* Selected note bordered box */
90 | border: 3px solid var(--white-five) !important;
91 | }
92 | html.black-mode .focus-NotesView-RemindersList { /* Reminders list borders */
93 | border-top: 2px solid var(--white-five) !important;
94 | background-color: var(--black) !important;
95 | border-bottom: 3px solid var(--white-five) !important;
96 | }
97 | html.black-mode .focus-NotesView-NotebookReminders-DropAreaContainer { /* Notebook reminder background */
98 | background: var(--black) !important;
99 | }
100 | html.black-mode .focus-NotesView-Subheader { /* Subheader border */
101 | border-bottom: 1px solid var(--white-five) !important;
102 | }
103 | html.black-mode .GJDCG5CLLB {
104 | background-color: var(--black) !important;
105 | }
106 | html.black-mode .GJDCG5CJKB, html.black-mode .GJDCG5CA0B { /* Toolbar bottom border */
107 | border-bottom: 1px solid var(--white-five) !important;
108 | }
109 | html.black-mode .GDAMOPFCILB, html.black-mode .GJDCG5CAOB { /* Note attributes background */
110 | background: var(--black) !important;
111 | }
112 | html.black-mode .GJDCG5CP0, html.black-mode .GJDCG5CLKB, html.black-mode .GJDCG5CCQB { /* Toolbar separators */
113 | background: var(--black) !important;
114 | border-left: 1px solid var(--white-five) !important;
115 | }
116 | html.black-mode .focus-NotesView-Note .focus-NotesView-Note-snippetDivider { /* Notes divider color */
117 | border-top: 1px solid var(--white-five) !important;
118 | }
119 | html.black-mode .GJDCG5CO3B { /* Profile background */
120 | background-color: var(--black) !important;
121 | border-top: 1px solid var(--white-five) !important;
122 | }
123 | html.black-mode .GDAMOPFCPYB { /* Note list transition background */
124 | background-color: var(--black) !important;
125 | border-top: 1px solid var(--white-five) !important;
126 | }
127 | html.black-mode .GDAMOPFCMYB { /* Note list header */
128 | background-color: var(--black) !important;
129 | margin-bottom: -6px !important;
130 | }
131 | html.black-mode div#gwt-debug-FocusView-root { /* Sidebar transition background */
132 | background-color: var(--black) !important;
133 | }
134 | html.black-mode .focus-NotesView-NotesView { /* Notes list body */
135 | background-color: var(--black) !important;
136 | }
137 | html.black-mode .GJDCG5CNNB, html.black-mode .GDAMOPFCOPB { /* Note editor body */
138 | background-color: var(--black) !important;
139 | }
140 | html.black-mode .GJDCG5CKKB, html.black-mode .GJDCG5CI1 { /* Note editor toolbar */
141 | background-color: var(--black) !important;
142 | border-bottom: 1px solid var(--black) !important;
143 | }
144 | html.black-mode .GJDCG5CMKB, html.black-mode .GJDCG5CO0 {
145 | border-left: 1px solid var(--black) !important;
146 | }
147 | html.black-mode .GJDCG5CJ1 { /* Note editor toolbar options */
148 | background-color: var(--black) !important;
149 | }
150 | html.black-mode .GJDCG5CONB { /* Note editor background */
151 | background-color: var(--black) !important;
152 | }
153 | html.black-mode .GJDCG5COF { /* Colors background */
154 | border: 1px solid var(--black) !important;
155 | background-color: var(--black) !important;
156 | }
157 | html.black-mode .GJDCG5CHK, html.black-mode .GJDCG5CGK, html.black-mode .GJDCG5CGK.GJDCG5CAK, html.black-mode .GJDCG5CNQ.GJDCG5CPQ , html.black-mode .GJDCG5CIEB.GJDCG5CBFB, html.black-mode .GJDCG5CM1 { /* Submenus background */
158 | background-color: var(--black) !important;
159 | }
160 | html.black-mode .GDAMOPFCNHC { /* Dialogs background */
161 | background-color: var(--black) !important;
162 | }
163 | html.black-mode .GJDCG5CFLB { /* Note editor head */
164 | background-color: var(--black) !important;
165 | }
166 | html.black-mode div#imggal-glass { /* Image gallery background*/
167 | background-color: var(--black) !important;
168 | }
169 | html.black-mode #imggal-top .close-icon { /* Image gallery exit button */
170 | border-radius: 0% !important; /* Makes the button square */
171 | background-color: var(--black) !important;
172 | }
173 |
--------------------------------------------------------------------------------
/src/style/browser.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --black: rgba(0, 0, 0, 1);
3 | --sepia: rgba(251, 240, 217, 1);
4 | --dark-grey: rgba(33, 33, 33, 1);
5 | --black-five: rgba(0, 0, 0, 0.05);
6 | --white-five: rgba(255, 255, 255, 0.05);
7 | }
8 | html {
9 | overflow: hidden;
10 | }
11 | body {
12 | -webkit-app-region: drag;
13 | }
14 | ::-webkit-scrollbar { /* Notes list scrollbar */
15 | width: 6px !important;
16 | }
17 | ::-webkit-scrollbar-track { /* Notes list scrollbar track */
18 | border-radius: 0px !important;
19 | }
20 | ::-webkit-scrollbar-thumb { /* Notes list scrollbar thumb */
21 | border-radius: 0px !important;
22 | background: #2DBE60 !important;
23 | }
24 | #switch-link { /* Allow text selection on Macos */
25 | -webkit-app-region: no-drag;
26 | }
27 | div#gwt-debug-NoteView-root {
28 | -webkit-app-region: no-drag;
29 | }
30 | div#gwt-debug-notesListView {
31 | -webkit-app-region: no-drag;
32 | }
33 | div#gwt-debug-FocusView-root { /* Main app window background */
34 | background-color: white !important;
35 | }
36 | .GJDCG5CO4B { /* Elephant logo */
37 | left: 0;
38 | right: 0;
39 | bottom: 0;
40 | top: 25px;
41 | height: 66px;
42 | position: relative;
43 | }
44 | .GJDCG5CF5B { /* Sidebar buttons */
45 | margin: 20px 0px !important;
46 | }
47 | .FocusClipperButton-button {
48 | visibility: hidden !important;
49 | }
50 | .focus-NotesView-NotesView {
51 | background-color: #FFFFFF !important;
52 | }
53 | .focus-NotesView-RemindersList {
54 | background-color: #FFFFFF;
55 | }
56 | .GJDCG5COYB { /* Note list header */
57 | background-color: #FFFFFF;
58 | margin-bottom: -6px !important;
59 | }
60 | .GJDCG5CO3B { /* Profile background */
61 | background-color: #F8F8F8 !important;
62 | }
63 | div#gwt-debug-NotesHeaderView-root {
64 | background-color: #FFFFFF !important;
65 | }
66 | div#gwt-debug-NotesHeader-title { /* Notes list title */
67 | padding-bottom: 10px !important;
68 | }
69 | html.side-bar-hidden div#gwt-debug-sidebar { /* Make side bar visible */
70 | display: none !important;
71 | }
72 | html.side-bar-hidden div#gwt-debug-stage {
73 | margin-left: 0px !important;
74 | }
75 | html.side-bar-hidden-macos div#gwt-debug-NotesHeader-title {
76 | padding-top: 13px !important;
77 | }
78 | @media all and (max-width: 800px) {
79 | div#gwt-debug-NoteView-root {
80 | margin-left: 0px !important;
81 | }
82 | div#gwt-debug-notesListView { /* Auto-hide note list */
83 | display: none !important;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/style/dark-mode.css:
--------------------------------------------------------------------------------
1 | html.dark-mode ::-webkit-scrollbar-track { /* Notes list scrollbar track */
2 | background: var(--dark-grey) !important;
3 | }
4 | html.dark-mode div#gwt-debug-NotesHeader-title, html.dark-mode div#gwt-debug-NoteAttributesView-root, html.dark-mode div#gwt-debug-NotebooksDrawerView-root, html.dark-mode div#gwt-debug-AccountMenuPopup-root, html.dark-mode div#gwt-debug-arrowChatView, html.dark-mode div#gwt-debug-ReminderSetDialog-root {
5 | background-color: var(--dark-grey) !important;
6 | }
7 | html.dark-mode .GJDCG5CJEB { /* Metting note background */
8 | background-color: var(--dark-grey) !important;
9 | }
10 | html.dark-mode .GJDCG5COYB { /* Note list header */
11 | background-color: var(--dark-grey) !important;
12 | }
13 | html.dark-mode .GJDCG5CAR.GJDCG5CBR, html.dark-mode .GJDCG5CCR {
14 | opacity: 1 !important;
15 | }
16 | html.dark-mode .Dropdown-menu {
17 | background: var(--dark-grey) !important;
18 | }
19 | html.dark-mode .GJDCG5CMGB, html.dark-mode .GDAMOPFCAHB, html.dark-mode .GDAMOPFCPGB, html.dark-mode .GDAMOPFCHYC.GDAMOPFCLD, html.dark-mode .GJDCG5CFKB { /* Chat windows */
20 | background: var(--dark-grey) !important;
21 | }
22 | html.dark-mode .GJDCG5CKKC, html.dark-mode .GJDCG5CK-B { /* Reminder modal */
23 | background: var(--dark-grey) !important;
24 | }
25 | html.dark-mode .GDAMOPFCHEB {
26 | background: var(--dark-grey) !important;
27 | }
28 | html.dark-mode .GJDCG5CJ-B, html.dark-mode .GDAMOPFCEQB, html.dark-mode .GJDCG5CGQB, html.dark-mode .GDAMOPFCDAC.GDAMOPFCM5B, html.dark-mode .GJDCG5CO-B .GJDCG5CD1B, html.dark-mode .GJDCG5CL-B .GJDCG5CP0B:hover { /* Search modal */
29 | background-color: var(--dark-grey) !important;
30 | }
31 | html.dark-mode .GJDCG5CK-B, html.dark-mode .GJDCG5CP-B, html.dark-mode .GJDCG5CDXB {
32 | border-bottom: 1px solid var(--white-five) !important;
33 | }
34 | html.dark-mode .GJDCG5CEXB {
35 | border-bottom: 1px solid var(--dark-grey) !important;
36 | }
37 | html.dark-mode .GJDCG5CGWB, html.dark-mode .GDAMOPFCIXB, html.dark-mode .GJDCG5CKXB.GJDCG5CHXB, html.dark-mode .GDAMOPFCFTB { /* Notebooks modal */
38 | background-color: var(--dark-grey) !important;
39 | }
40 | html.dark-mode .GJDCG5CDXB.GJDCG5CPXB { /* Selected notebook in notebooks modal */
41 | background-color: #2DBE60 !important;
42 | }
43 | html.dark-mode .GJDCG5CITB { /* Select notebook at toolbar option */
44 | background-color: var(--dark-grey) !important;
45 | }
46 | html.dark-mode .GJDCG5CJJ, html.dark-mode .focus-drawer-Filter-input { /* Search notebook/tags input text */
47 | color: #E1E1E1 !important;
48 | }
49 | html.dark-mode div#gwt-debug-GlassModalDialog-container { /* Glass modal background */
50 | background-color: var(--dark-grey) !important;
51 | }
52 | html.dark-mode div#gwt-debug-GlassModalDialog-glass { /* Glass modal bottom line */
53 | background-color: var(--dark-grey) !important;
54 | }
55 | html.dark-mode .GDAMOPFCPE { /* Notebook info */
56 | color: #E1E1E1 !important;
57 | }
58 | html.dark-mode .GDAMOPFCN-B .GDAMOPFCC1B, html.dark-mode .GDAMOPFCDQB { /* Saved search */
59 | background-color: var(--dark-grey) !important;
60 | }
61 | html.dark-mode .GJDCG5CJ3B, html.dark-mode .GJDCG5CI3B, html.dark-mode .GJDCG5CH3B, html.dark-mode .GJDCG5CN5B, html.dark-mode .GJDCG5CGHB, html.dark-mode .GJDCG5CFHB, html.dark-mode .GJDCG5CM5B { /* Shortcuts modal */
62 | background-color: var(--dark-grey) !important;
63 | border-right: 3px solid var(--white-five) !important;
64 | }
65 | html.dark-mode .GJDCG5CP2 { /* Settings menu */
66 | background-color: var(--dark-grey) !important;
67 | }
68 | html.dark-mode .GJDCG5CB5B {
69 | background: var(--dark-grey) !important;
70 | background-color: var(--dark-grey) !important;
71 | border-right: 1px solid var(--white-five) !important;
72 | }
73 | html.dark-mode .GDAMOPFCJYB { /* Notes list head */
74 | background-color: var(--dark-grey) !important;
75 | }
76 | html.dark-mode .GJDCG5CBQB { /* Notes list left border */
77 | background-color: var(--dark-grey) !important;
78 | border-left: 1px solid var(--white-five) !important;
79 | }
80 | html.dark-mode .GJDCG5CB-B {
81 | background-color: var(--dark-grey) !important;
82 | }
83 | html.dark-mode .GDAMOPFCA5B { /* Notes list left border */
84 | border-left: 1px solid var(--white-five) !important;
85 | border-right: 1px solid var(--white-five) !important;
86 | background: var(--dark-grey) !important;
87 | background-color: var(--dark-grey) !important;
88 | }
89 | html.dark-mode .focus-NotesView-Note.focus-NotesView-Note-selected .focus-NotesView-Note-selectOverlay { /* Selected note bordered box */
90 | border: 3px solid var(--white-five) !important;
91 | }
92 | html.dark-mode .focus-NotesView-RemindersList { /* Reminders list borders */
93 | border-top: 2px solid var(--white-five) !important;
94 | border-bottom: 3px solid var(--white-five) !important;
95 | background-color: var(--dark-grey) !important;
96 | }
97 | html.dark-mode .focus-NotesView-NotebookReminders-DropAreaContainer { /* Notebook reminder background */
98 | background: var(--dark-grey) !important;
99 | }
100 | html.dark-mode .focus-NotesView-Subheader { /* Subheader border */
101 | border-bottom: 1px solid var(--white-five) !important;
102 | }
103 | html.dark-mode .GJDCG5CLLB {
104 | background-color: var(--dark-grey) !important;
105 | }
106 | html.dark-mode .GJDCG5CJKB, html.dark-mode .GJDCG5CA0B { /* Toolbar bottom border */
107 | border-bottom: 1px solid var(--white-five) !important;
108 | }
109 | html.dark-mode .GDAMOPFCILB, html.dark-mode .GJDCG5CAOB { /* Note attributes background */
110 | background: var(--dark-grey) !important;
111 | }
112 | html.dark-mode .GJDCG5CP0, html.dark-mode .GJDCG5CLKB, html.dark-mode .GJDCG5CCQB { /* Toolbar separators */
113 | background: var(--dark-grey) !important;
114 | border-left: 1px solid var(--white-five) !important;
115 | }
116 | html.dark-mode .focus-NotesView-Note .focus-NotesView-Note-snippetDivider { /* Notes divider color */
117 | border-top: 1px solid var(--white-five) !important;
118 | }
119 | html.dark-mode .GJDCG5CO3B { /* Profile background */
120 | background-color: var(--dark-grey) !important;
121 | border-top: 1px solid var(--white-five) !important;
122 | }
123 | html.dark-mode .GDAMOPFCPYB { /* Note list transition background */
124 | background-color: var(--dark-grey) !important;
125 | border-top: 1px solid var(--white-five) !important;
126 | }
127 | html.dark-mode .GDAMOPFCMYB { /* Note list header */
128 | background-color: var(--dark-grey) !important;
129 | margin-bottom: -6px !important;
130 | }
131 | html.dark-mode div#gwt-debug-FocusView-root { /* Sidebar transition background */
132 | background-color: var(--dark-grey) !important;
133 | }
134 | html.dark-mode .focus-NotesView-NotesView { /* Notes list body */
135 | background-color: var(--dark-grey) !important;
136 | }
137 | html.dark-mode .GJDCG5CNNB, html.dark-mode .GDAMOPFCOPB { /* Note editor body */
138 | background-color: var(--dark-grey) !important;
139 | }
140 | html.dark-mode .GJDCG5CKKB, html.dark-mode .GJDCG5CI1 {
141 | /* Note editor toolbar */
142 | background-color: var(--dark-grey) !important;
143 | border-bottom: 1px solid var(--dark-grey) !important;
144 | }
145 | html.dark-mode .GJDCG5CMKB, html.dark-mode .GJDCG5CO0 {
146 | border-left: 1px solid var(--dark-grey) !important;
147 | }
148 | html.dark-mode .GJDCG5CJ1 { /* Note editor toolbar options */
149 | background-color: var(--dark-grey) !important;
150 | }
151 | html.dark-mode .GJDCG5CONB { /* Note editor background */
152 | background-color: var(--dark-grey) !important;
153 | }
154 | html.dark-mode .GJDCG5COF { /* Colors background */
155 | border: 1px solid var(--dark-grey) !important;
156 | background-color: var(--dark-grey) !important;
157 | }
158 | html.dark-mode .GJDCG5CHK, html.dark-mode .GJDCG5CGK, html.dark-mode .GJDCG5CGK.GJDCG5CAK, html.dark-mode .GJDCG5CNQ.GJDCG5CPQ , html.dark-mode .GJDCG5CIEB.GJDCG5CBFB, html.dark-mode .GJDCG5CM1 { /* Submenus background */
159 | background-color: var(--dark-grey) !important;
160 | }
161 | html.dark-mode .GDAMOPFCNHC { /* Dialogs background */
162 | background-color: var(--dark-grey) !important;
163 | }
164 | html.dark-mode .GJDCG5CFLB { /* Note editor head */
165 | background-color: var(--dark-grey) !important;
166 | }
167 | html.dark-mode div#imggal-glass { /* Image gallery background*/
168 | background-color: var(--dark-grey) !important;
169 | }
170 | html.dark-mode #imggal-top .close-icon { /* Image gallery exit button */
171 | border-radius: 0% !important; /* Makes the button square */
172 | background-color: var(--dark-grey) !important;
173 | }
174 |
--------------------------------------------------------------------------------
/src/style/sepia-mode.css:
--------------------------------------------------------------------------------
1 | html.sepia-mode ::-webkit-scrollbar-track { /* Notes list scrollbar track */
2 | background: var(--sepia) !important;
3 | }
4 | html.sepia-mode div#gwt-debug-NotesHeader-title, html.sepia-mode div#gwt-debug-NoteAttributesView-root, html.sepia-mode div#gwt-debug-NotebooksDrawerView-root, html.sepia-mode div#gwt-debug-AccountMenuPopup-root, html.sepia-mode div#gwt-debug-arrowChatView, html.sepia-mode div#gwt-debug-ReminderSetDialog-root {
5 | background-color: var(--sepia) !important;
6 | }
7 | html.sepia-mode .GJDCG5CJEB { /* Metting note background */
8 | background-color: var(--sepia) !important;
9 | }
10 | html.sepia-mode .GJDCG5COYB { /* Note list header */
11 | background-color: var(--sepia) !important;
12 | }
13 | html.sepia-mode .GJDCG5CAR.GJDCG5CBR, html.sepia-mode .GJDCG5CCR {
14 | opacity: 1 !important;
15 | }
16 | html.sepia-mode .Dropdown-menu {
17 | background: var(--sepia) !important;
18 | }
19 | html.sepia-mode .GJDCG5CMGB, html.sepia-mode .GDAMOPFCAHB, html.sepia-mode .GDAMOPFCPGB, html.sepia-mode .GDAMOPFCHYC.GDAMOPFCLD, html.sepia-mode .GJDCG5CFKB { /* Chat windows */
20 | background: var(--sepia) !important;
21 | }
22 | html.sepia-mode .GJDCG5CKKC, html.sepia-mode .GJDCG5CK-B { /* Reminder modal */
23 | background: var(--sepia) !important;
24 | }
25 | html.sepia-mode .GDAMOPFCHEB {
26 | background: var(--sepia) !important;
27 | }
28 | html.sepia-mode .GJDCG5CJ-B, html.sepia-mode .GDAMOPFCEQB, html.sepia-mode .GJDCG5CGQB, html.sepia-mode .GDAMOPFCDAC.GDAMOPFCM5B, html.sepia-mode .GJDCG5CO-B .GJDCG5CD1B, html.sepia-mode .GJDCG5CL-B .GJDCG5CP0B:hover { /* Search modal */
29 | background-color: var(--sepia) !important;
30 | }
31 | html.sepia-mode .GJDCG5CK-B, html.sepia-mode .GJDCG5CP-B, html.sepia-mode .GJDCG5CDXB {
32 | border-bottom: 1px solid var(--black-five) !important;
33 | }
34 | html.sepia-mode .GJDCG5CEXB {
35 | border-bottom: 1px solid var(--sepia) !important;
36 | }
37 | html.sepia-mode .GJDCG5CGWB, html.sepia-mode .GDAMOPFCIXB, html.sepia-mode .GJDCG5CKXB.GJDCG5CHXB, html.sepia-mode .GDAMOPFCFTB { /* Notebooks modal */
38 | background-color: var(--sepia) !important;
39 | }
40 | html.sepia-mode .GJDCG5CDXB.GJDCG5CPXB { /* Selected notebook in notebooks modal */
41 | background-color: #2DBE60 !important;
42 | }
43 | html.sepia-mode .GJDCG5CITB { /* Select notebook at toolbar option */
44 | background-color: var(--sepia) !important;
45 | }
46 | html.sepia-mode .GJDCG5CJJ, html.sepia-mode .focus-drawer-Filter-input { /* Search notebook/tags input text */
47 | color: #E1E1E1 !important;
48 | }
49 | html.sepia-mode div#gwt-debug-GlassModalDialog-container { /* Glass modal background */
50 | background-color: var(--sepia) !important;
51 | }
52 | html.sepia-mode div#gwt-debug-GlassModalDialog-glass { /* Glass modal bottom line */
53 | background-color: var(--sepia) !important;
54 | }
55 | html.sepia-mode .GDAMOPFCPE { /* Notebook info */
56 | color: #E1E1E1 !important;
57 | }
58 | html.sepia-mode .GDAMOPFCN-B .GDAMOPFCC1B, html.sepia-mode .GDAMOPFCDQB { /* Saved search */
59 | background-color: var(--sepia) !important;
60 | }
61 | html.sepia-mode .GJDCG5CJ3B, html.sepia-mode .GJDCG5CI3B, html.sepia-mode .GJDCG5CH3B, html.sepia-mode .GJDCG5CN5B, html.sepia-mode .GJDCG5CGHB, html.sepia-mode .GJDCG5CFHB, html.sepia-mode .GJDCG5CM5B { /* Shortcuts modal */
62 | background-color: var(--sepia) !important;
63 | border-right: 3px solid var(--black-five) !important;
64 | }
65 | html.sepia-mode .GJDCG5CP2 { /* Settings menu */
66 | background-color: var(--sepia) !important;
67 | }
68 | html.sepia-mode .GJDCG5CB5B {
69 | background: var(--sepia) !important;
70 | background-color: var(--sepia) !important;
71 | border-right: 1px solid var(--black-five) !important;
72 | }
73 | html.sepia-mode .GDAMOPFCJYB { /* Notes list head */
74 | background-color: var(--sepia) !important;
75 | }
76 | html.sepia-mode .GJDCG5CBQB { /* Notes list left border */
77 | background-color: var(--sepia) !important;
78 | border-left: 1px solid var(--black-five) !important;
79 | }
80 | html.sepia-mode .GJDCG5CB-B {
81 | background-color: var(--sepia) !important;
82 | }
83 | html.sepia-mode .GDAMOPFCA5B { /* Notes list left border */
84 | border-left: 1px solid var(--black-five) !important;
85 | border-right: 1px solid var(--black-five) !important;
86 | background: var(--sepia) !important;
87 | background-color: var(--sepia) !important;
88 | }
89 | html.sepia-mode .focus-NotesView-Note.focus-NotesView-Note-selected .focus-NotesView-Note-selectOverlay { /* Selected note bordered box */
90 | border: 3px solid var(--black-five) !important;
91 | }
92 | html.sepia-mode .focus-NotesView-RemindersList { /* Reminders list borders */
93 | background-color: var(--sepia) !important;
94 | border-top: 2px solid var(--black-five) !important;
95 | border-bottom: 3px solid var(--black-five) !important;
96 | }
97 | html.sepia-mode .focus-NotesView-NotebookReminders-DropAreaContainer { /* Notebook reminder background */
98 | background: var(--sepia) !important;
99 | }
100 | html.sepia-mode .focus-NotesView-Subheader { /* Subheader border */
101 | border-bottom: 1px solid var(--black-five) !important;
102 | }
103 | html.sepia-mode .GJDCG5CLLB {
104 | background-color: var(--sepia) !important;
105 | }
106 | html.sepia-mode .GJDCG5CJKB, html.sepia-mode .GJDCG5CA0B { /* Toolbar bottom border */
107 | border-bottom: 1px solid var(--black-five) !important;
108 | }
109 | html.sepia-mode .GDAMOPFCILB, html.sepia-mode .GJDCG5CAOB { /* Note attributes background */
110 | background: var(--sepia) !important;
111 | }
112 | html.sepia-mode .GJDCG5CP0, html.sepia-mode .GJDCG5CLKB, html.sepia-mode .GJDCG5CCQB { /* Toolbar separators */
113 | background: var(--sepia) !important;
114 | border-left: 1px solid var(--black-five) !important;
115 | }
116 | html.sepia-mode .focus-NotesView-Note .focus-NotesView-Note-snippetDivider { /* Notes divider color */
117 | border-top: 1px solid var(--black-five) !important;
118 | }
119 | html.sepia-mode .GJDCG5CO3B { /* Profile background */
120 | background-color: var(--sepia) !important;
121 | border-top: 1px solid var(--black-five) !important;
122 | }
123 | html.sepia-mode .GDAMOPFCPYB { /* Note list transition background */
124 | background-color: var(--sepia) !important;
125 | border-top: 1px solid var(--black-five) !important;
126 | }
127 | html.sepia-mode .GDAMOPFCMYB { /* Note list header */
128 | background-color: var(--sepia) !important;
129 | margin-bottom: -6px !important;
130 | }
131 | html.sepia-mode div#gwt-debug-FocusView-root { /* Sidebar transition background */
132 | background-color: var(--sepia) !important;
133 | }
134 | html.sepia-mode .focus-NotesView-NotesView { /* Notes list body */
135 | background-color: var(--sepia) !important;
136 | }
137 | html.sepia-mode .GJDCG5CNNB, html.sepia-mode .GDAMOPFCOPB { /* Note editor body */
138 | background-color: var(--sepia) !important;
139 | }
140 | html.sepia-mode .GJDCG5CKKB, html.sepia-mode .GJDCG5CI1 { /* Note editor toolbar */
141 | background-color: var(--sepia) !important;
142 | border-bottom: 1px solid var(--sepia) !important;
143 | }
144 | html.sepia-mode .GJDCG5CMKB, html.sepia-mode .GJDCG5CO0 {
145 | border-left: 1px solid var(--sepia) !important;
146 | }
147 | html.sepia-mode .GJDCG5CJ1 { /* Note editor toolbar options */
148 | background-color: var(--sepia) !important;
149 | }
150 | html.sepia-mode .GJDCG5CONB { /* Note editor background */
151 | background-color: var(--sepia) !important;
152 | }
153 | html.sepia-mode .GJDCG5COF { /* Colors background */
154 | border: 1px solid var(--sepia) !important;
155 | background-color: var(--sepia) !important;
156 | }
157 | html.sepia-mode .GJDCG5CHK, html.sepia-mode .GJDCG5CGK, html.sepia-mode .GJDCG5CGK.GJDCG5CAK, html.sepia-mode .GJDCG5CNQ.GJDCG5CPQ , html.sepia-mode .GJDCG5CIEB.GJDCG5CBFB, html.sepia-mode .GJDCG5CM1 { /* Submenus background */
158 | background-color: var(--sepia) !important;
159 | }
160 | html.sepia-mode .GDAMOPFCNHC { /* Dialogs background */
161 | background-color: var(--sepia) !important;
162 | }
163 | html.sepia-mode .GJDCG5CFLB { /* Note editor head */
164 | background-color: var(--sepia) !important;
165 | }
166 | html.sepia-mode div#imggal-glass { /* Image gallery background*/
167 | background-color: var(--sepia) !important;
168 | }
169 | html.sepia-mode #imggal-top .close-icon { /* Image gallery exit button */
170 | border-radius: 0% !important; /* Makes the button square */
171 | background-color: var(--sepia) !important;
172 | }
173 |
--------------------------------------------------------------------------------
/src/time.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Time {
4 | date() {
5 | const _ = new Date();
6 | return [_.getMonth() + 1, _.getDate(), _.getFullYear()].join('/');
7 | }
8 |
9 | dateTime() {
10 | return `${this.date()} ${this.time()}`;
11 | }
12 |
13 | hours() {
14 | return new Date().getHours();
15 | }
16 |
17 | isDaytime() {
18 | const hs = this.hours();
19 | return hs < 18 && hs > 6;
20 | }
21 |
22 | ms(hours) {
23 | return 1000 * 60 * 60 * parseInt(hours, 10);
24 | }
25 |
26 | stamp() {
27 | const _ = new Date();
28 | return [
29 | _.getMonth(),
30 | _.getDay(),
31 | _.getFullYear(),
32 | _.getHours(),
33 | _.getMinutes(),
34 | _.getSeconds()
35 | ].join('-');
36 | }
37 |
38 | time() {
39 | const _ = new Date();
40 | return [_.getHours(), _.getMinutes(), _.getSeconds()].join(':');
41 | }
42 |
43 | transitionSpan() {
44 | const hs = this.hours();
45 | return this.isDaytime() ? 18 - hs : (hs < 6 ? 6 - hs : 30 - hs);
46 | }
47 | }
48 |
49 | module.exports = new Time();
50 |
--------------------------------------------------------------------------------
/src/tray.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const electron = require('electron');
3 | const {is} = require('./util');
4 | const file = require('./file');
5 | const tpl = require('./menu/tray');
6 | const win = require('./win');
7 |
8 | const {app, Menu} = electron;
9 |
10 | class Tray {
11 | constructor() {
12 | this._tray = null;
13 | }
14 |
15 | create() {
16 | if (is.darwin) {
17 | return;
18 | }
19 |
20 | this._tray = new electron.Tray(file.trayIcon);
21 | this._tray.setToolTip(app.getName());
22 | this._tray.setContextMenu(Menu.buildFromTemplate(tpl));
23 | this._tray.on('click', win.toggle);
24 | }
25 | }
26 |
27 | module.exports = new Tray();
28 |
--------------------------------------------------------------------------------
/src/update.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {app} = require('electron');
3 | const {get} = require('https');
4 | const dialog = require('./dialog');
5 | const url = require('./url');
6 |
7 | const {log} = console;
8 |
9 | class Update {
10 | _compareToLocal(version) {
11 | const [x, y] = [version, app.getVersion()].map(x => x.split('.').map(Number));
12 |
13 | for (let i = 0; i < 3; i++) {
14 | const dif = x[i] - y[i];
15 | if (dif !== 0) {
16 | return dif;
17 | }
18 | }
19 |
20 | return 0;
21 | }
22 |
23 | _fetchUpdateData() {
24 | return new Promise((resolve, reject) => {
25 | const request = get(url.update, res => {
26 | const {statusCode: sc} = res;
27 |
28 | if (sc < 200 || sc > 299) {
29 | reject(new Error(`Request to get update data failed with HTTP status code: ${sc}`));
30 | }
31 |
32 | const data = [];
33 | res.on('data', d => data.push(d));
34 | res.on('end', () => resolve(JSON.parse(data.join(''))));
35 | });
36 |
37 | request.on('error', err => reject(err));
38 | });
39 | }
40 |
41 | _hasUpdate(version) {
42 | return this._compareToLocal(version) > 0;
43 | }
44 |
45 | async auto() {
46 | let latestVer;
47 |
48 | try {
49 | const data = await this._fetchUpdateData();
50 | latestVer = data.version;
51 | } catch (error) {
52 | return log(error);
53 | }
54 |
55 | if (this._hasUpdate(latestVer)) {
56 | return dialog.getUpdate(latestVer);
57 | }
58 | }
59 |
60 | async check() {
61 | let latestVer;
62 |
63 | try {
64 | const data = await this._fetchUpdateData();
65 | latestVer = data.version;
66 | } catch (error) {
67 | return dialog.updateError(error.message);
68 | }
69 |
70 | if (this._hasUpdate(latestVer)) {
71 | return dialog.getUpdate(latestVer);
72 | }
73 |
74 | return dialog.noUpdate();
75 | }
76 | }
77 |
78 | module.exports = new Update();
79 |
--------------------------------------------------------------------------------
/src/url.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | community: 'https://gitter.im/klaussinani/tusk',
5 | evernote: 'https://www.evernote.com/Login.action',
6 | homepage: 'https://klaussinani.github.io/tusk',
7 | issue: 'https://github.com/klaussinani/tusk/issues/new',
8 | keyboardShortcutsRef: 'https://github.com/klaussinani/tusk#keyboard-shortcuts',
9 | license: 'https://github.com/klaussinani/tusk/blob/master/license.md',
10 | redirect: 'https://www.evernote.com/OutboundRedirect.action?dest=',
11 | release: 'https://github.com/klaussinani/tusk/releases/latest',
12 | search: 'https://github.com/search?q=+is:issue+repo:klaussinani/tusk',
13 | searchFeatureRequests: 'https://github.com/klaussinani/tusk/labels/feature-request',
14 | settings: 'https://www.evernote.com/Settings.action',
15 | source: 'https://github.com/klaussinani/tusk',
16 | update: 'https://raw.githubusercontent.com/klaussinani/tusk/master/docs/update.json',
17 | yinxiang: 'https://app.yinxiang.com/Login.action',
18 | yinxiangRedirect: 'https://app.yinxiang.com/OutboundRedirect.action?dest='
19 | };
20 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {dirname, join} = require('path');
3 | const fs = require('fs');
4 | const decodeUri = require('decode-uri-component');
5 | const url = require('./url');
6 |
7 | const {log} = console;
8 | const {platform} = process;
9 |
10 | class Util {
11 | get is() {
12 | return {
13 | darwin: platform === 'darwin',
14 | downloadURL: x => x.split('/', 4).includes('shard'),
15 | linux: platform === 'linux',
16 | undef: x => x === undefined,
17 | win32: platform === 'win32'
18 | };
19 | }
20 |
21 | ensureFileSync(path, data) {
22 | if (!fs.existsSync(path)) {
23 | try {
24 | fs.writeFileSync(path, data);
25 | } catch (error) {
26 | log(error);
27 | }
28 | }
29 | }
30 |
31 | formatExternal(x) {
32 | return `file://${x}`;
33 | }
34 |
35 | formatTitle(x) {
36 | return x.replace(' | Evernote Web', '');
37 | }
38 |
39 | formatURL(x) {
40 | return decodeUri(x.replace(url.redirect, ''));
41 | }
42 |
43 | formatYinxiangURL(x) {
44 | return decodeUri(x.replace(url.yinxiangRedirect, ''));
45 | }
46 |
47 | readSheet(x) {
48 | return fs.readFileSync(join(__dirname, './style', x), 'utf8');
49 | }
50 |
51 | touchFileSync(x) {
52 | const dir = dirname(x);
53 |
54 | if (!fs.existsSync(dir)) {
55 | fs.mkdirSync(dir);
56 | }
57 |
58 | return fs.closeSync(fs.openSync(x, 'a'));
59 | }
60 | }
61 | module.exports = new Util();
62 |
--------------------------------------------------------------------------------
/src/win.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {join} = require('path');
3 | const electron = require('electron');
4 | const {is} = require('./util');
5 | const file = require('./file');
6 | const settings = require('./settings');
7 |
8 | const {app, BrowserWindow} = electron;
9 |
10 | class Win {
11 | get _screenDimensions() {
12 | const {width, height} = electron.screen.getPrimaryDisplay().workAreaSize;
13 | return [width, height];
14 | }
15 |
16 | get _defaultDimensions() {
17 | return this._screenDimensions.map(x => Math.round(x * 0.9));
18 | }
19 |
20 | get _lastState() {
21 | const {x, y, width, height} = settings.get('lastWindowState');
22 | const [defaultWidth, defaultHeight] = this._defaultDimensions;
23 |
24 | return {
25 | x,
26 | y,
27 | width: width || defaultWidth,
28 | height: height || defaultHeight
29 | };
30 | }
31 |
32 | get _minDimensions() {
33 | const [minWidth, minHeight] = this._screenDimensions.map(x => Math.round(x * 0.3));
34 | return {minWidth, minHeight};
35 | }
36 |
37 | get defaultOpts() {
38 | return Object.assign({}, this._minDimensions, this._lastState, {
39 | alwaysOnTop: settings.get('alwaysOnTop'),
40 | autoHideMenuBar: settings.get('menuBarHidden'),
41 | darkTheme: settings.get('mode.dark') || settings.get('mode.black'),
42 | icon: is.linux && file.icon,
43 | show: false,
44 | title: app.getName(),
45 | titleBarStyle: 'hiddenInset',
46 | webPreferences: {
47 | nodeIntegration: false,
48 | plugins: true,
49 | preload: join(__dirname, './browser.js')
50 | }
51 | });
52 | }
53 |
54 | activate(command) {
55 | const [win] = BrowserWindow.getAllWindows();
56 |
57 | if (is.darwin) {
58 | win.restore();
59 | }
60 |
61 | win.webContents.send(command);
62 | }
63 |
64 | appear() {
65 | const [win] = BrowserWindow.getAllWindows();
66 | if (!win.isVisible() || !win.isFocused()) {
67 | win.show();
68 | win.focus();
69 | }
70 | }
71 |
72 | toggle() {
73 | const [win] = BrowserWindow.getAllWindows();
74 | if (win.isVisible()) {
75 | win.hide();
76 | } else {
77 | win.show();
78 | win.focus();
79 | }
80 | }
81 | }
82 |
83 | module.exports = new Win();
84 |
--------------------------------------------------------------------------------
/static/Icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/static/Icon.icns
--------------------------------------------------------------------------------
/static/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/static/Icon.ico
--------------------------------------------------------------------------------
/static/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/static/Icon.png
--------------------------------------------------------------------------------
/static/IconTray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/static/IconTray.png
--------------------------------------------------------------------------------
/static/IconTray@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/klaudiosinani/tusk/5d06732b7186e4e2cf3e7686d75be2f1e9be8e34/static/IconTray@2x.png
--------------------------------------------------------------------------------