1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 10:16:26 +00:00

Compare commits

...

638 Commits

Author SHA1 Message Date
dependabot[bot]
8645b2feda Bump ua-parser-js from 0.7.18 to 0.7.28
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.18 to 0.7.28.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.18...0.7.28)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-06 22:52:58 +00:00
Baptiste Augrain
58c4a78be1 avoids conflicting styles between inline codes and code blocks 2020-12-27 11:16:55 +09:00
Gonçalo Santos
2603dfc1ed Fix Analytics save bug 2020-12-12 00:15:37 +09:00
Gonçalo Santos
2df590600b AutoUpdate is auto saved 2020-12-12 00:15:37 +09:00
Gonçalo Santos
ef20a8f3e5 Remove debug statements 2020-12-12 00:15:37 +09:00
Gonçalo Santos
3e405e1abf Fix Cancel update 2020-12-12 00:15:37 +09:00
Gonçalo Santos
553832bdfa Create confirm download dialog 2020-12-12 00:15:37 +09:00
Gonçalo Santos
18d65d999a Menu item calls update-check 2020-12-12 00:15:37 +09:00
Gonçalo Santos
3b5eff582a Update not found message 2020-12-12 00:15:37 +09:00
Gonçalo Santos
85d09b3b3d Add update menu item 2020-12-12 00:15:37 +09:00
Baptiste Augrain
8958e67fcf fix unwanted deletion of attachments 2020-09-15 12:33:12 +09:00
Junyoung Choi
47b796909a v0.16.1 2020-09-04 23:47:01 +09:00
Junyoung Choi
67d76abdfa Merge branch 'master' of github.com:BoostIO/Boostnote 2020-09-04 23:45:41 +09:00
Junyoung Choi
d75d68ba72 Add email subscription form 2020-09-04 23:44:16 +09:00
Junyoung Choi
323be6b72d Merge pull request #2612 from daiyam/export-yfm
improve export
2020-07-22 18:24:27 +09:00
Junyoung Choi
031a113338 Merge branch 'master' of github.com:BoostIO/Boostnote 2020-07-20 21:07:26 +09:00
Junyoung Choi
b50c5386a6 Add BoostHub link to menu 2020-07-20 21:06:55 +09:00
Baptiste Augrain
65777b1d56 Merge branch 'master' into export-yfm 2020-07-20 14:05:21 +02:00
Junyoung Choi
fe728874ac Fix code sign script 2020-07-20 21:02:29 +09:00
Junyoung Choi
c5b4c327fa v0.16.0 2020-07-20 20:42:18 +09:00
Junyoung Choi
4c39922ead Merge pull request #2405 from daiyam/fix-scroll
Better scroll sync between the editor and the preview in the SplitEditor
2020-07-20 19:48:29 +09:00
Junyoung Choi
1cdc74a2f0 Merge pull request #2594 from daiyam/fix-autocomplete-codeblock
improve autocomplete within code blocks
2020-07-20 19:47:48 +09:00
Junyoung Choi
6213a820e6 Merge pull request #3600 from ZeroX-DG/date-iso8601
Add Date shortcut ISO 8601 format as an option in preference
2020-07-20 19:46:53 +09:00
Junyoung Choi
ce81b26d1d Merge branch 'master' into date-iso8601 2020-07-20 19:42:08 +09:00
dependabot[bot]
fae91255f9 Bump lodash from 4.17.13 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.13 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.13...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-20 19:28:17 +09:00
dependabot[bot]
a82a3efb14 Bump websocket-extensions from 0.1.3 to 0.1.4
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-20 19:27:17 +09:00
Junyoung Choi
9556417447 Merge pull request #2923 from LuisReinoso/master
Add Wakatime integration
2020-07-20 19:26:53 +09:00
Baptiste Augrain
60fbb7db5d fix unclickable links 2020-07-20 19:25:53 +09:00
Baptiste Augrain
e504f8e63e fix broken nord and vulcan themes 2020-07-20 19:25:27 +09:00
Baptiste Augrain
071ce12a7e add updated yarn.lock 2020-07-20 19:23:25 +09:00
Baptiste Augrain
0decaf187c update mermaid due to missing arrowheads 2020-07-20 19:23:25 +09:00
Junyoung Choi
961644747e Update readme.md 2020-07-09 13:28:02 +09:00
ZeroX-DG
d1a81984fb fixed eslint 2020-07-02 11:46:13 +12:00
Baptiste Augrain
bd9b1306b1 fix missing isStacking flag 2020-06-26 17:55:16 +02:00
Baptiste Augrain
0ca18d8ca5 re-add missing isStacking flag 2020-06-26 03:07:25 +02:00
Baptiste Augrain
540d72696c Merge branch 'master' into fix-autocomplete-codeblock 2020-06-26 02:40:37 +02:00
Baptiste Augrain
87a530612f - use newest test
- remove useless binding
- regroup function
2020-06-25 22:50:08 +02:00
ehhc
7f3fdedb5d Update readme.md 2020-06-25 10:03:02 +09:00
ehhc
3a80706938 Update readme.md
Added link to a project developing a mobile version of boostnote
2020-06-25 10:03:02 +09:00
Baptiste Augrain
f4259bb4d0 fix exporting storage's notes into their own folders 2020-06-12 17:36:46 +02:00
Baptiste Augrain
aa8b589569 don't show export menu to snippet notes 2020-06-12 16:56:07 +02:00
Baptiste Augrain
febc98c101 fix exporting storage's notes as PDFs 2020-06-12 16:51:35 +02:00
Baptiste Augrain
b678c3bd89 export html is escaping html characters as the preview 2020-06-12 16:40:16 +02:00
Baptiste Augrain
80b8948433 - export untitled notes as 'Untitled' based on the language
- export notes with duplicate title as '<title> (<index>)'
2020-06-12 16:18:27 +02:00
Baptiste Augrain
5414fe3384 export tag 2020-06-12 15:53:23 +02:00
Baptiste Augrain
db4016385d Merge branch 'master' into export-yfm 2020-06-12 15:17:02 +02:00
ZeroX-DG
2cf5f8e966 updated slack invite link 2020-06-08 18:48:55 +09:00
Luis Reinoso
8ede1a4989 refactor: Change UI according requested changes.
NOTE:
Just a simple section with title Wakatime and bellow is a check box saying enable wakatime? and below it is the text box for wakatime key.
2020-06-07 11:17:02 -05:00
Luis Reinoso
76da76ae76 fix: Wakatime command name. 2020-06-07 11:01:44 -05:00
Luis Reinoso
c02ab033f4 fix: Remove extra calling function. Now call directly to check wakatime plugin. 2020-05-23 12:25:37 -05:00
Luis Reinoso
1aaba74e24 refactor: Improve sendWakatimeHeartBeat. 2020-05-23 12:21:43 -05:00
Luis Reinoso
6fe6794796 feat: Add checkbox validation to active or deactive plugin. 2020-05-16 11:13:19 -05:00
Luis Reinoso
fd3e243855 feat: Check for missing wakatime cli. And display modal and alert message. 2020-05-16 10:08:51 -05:00
Luis Reinoso
938b075bf6 fix: Lint errors. 2020-05-16 08:37:35 -05:00
Baptiste Augrain
81ac3d1748 fix scrolling with only one big paragraph 2020-05-16 14:19:22 +02:00
Baptiste Augrain
40d10eae04 Merge branch 'master' into fix-scroll 2020-05-15 02:59:00 +02:00
Baptiste Augrain
9b6a61a91c fix cursor sync 2020-05-15 02:57:00 +02:00
Baptiste Augrain
7116c305ca Merge branch 'master' into fix-scroll 2020-05-15 02:02:38 +02:00
ZeroX-DG
4fbbb4651d updated Boostnote name to Boostnote Legacy 2020-05-11 03:44:21 +09:00
Luis Reinoso
2ac38e9644 fix: Update prettier config with master. 2020-05-08 17:11:16 -05:00
Luis Reinoso
98d4fa0603 fix: Lint issues. 2020-05-08 10:01:30 -05:00
Luis Reinoso
2ea0514bbe Merge remote-tracking branch 'upstream/master' 2020-05-08 06:55:48 -05:00
Junyoung Choi
137aa692bc Merge pull request #3547 from ZeroX-DG/migrate-to-jest
[WIP] Migrate to jest
2020-05-08 18:49:27 +09:00
ZeroX-DG
634fec39c0 migrate more tests to jest 2020-05-08 20:13:39 +12:00
ZeroX-DG
d269f1e8fd migrate more tests to jest 2020-05-06 14:57:35 +12:00
Junyoung Choi
4b67026bbf Merge pull request #2936 from callumbooth/fix-2903
fixes #2903 - Rearrange layout of columns
2020-04-24 01:04:23 +09:00
Alexander Wolf
8b13ec4f0e Issue 1706 tag rename - finishing #2989 (#3469)
* allow a tag to be renamed and update all notes that use that tag

• repurpose RenameFolderModal.styl to RenameModal so it is more generic

* call handleConfirmButtonClick directly instead of sending through a confirm method

* better name for method to confirm the rename

* use close prop instead of a new method

* use callback ref instead of legacy string refs

* bind the handleChange in the constructor to allow for direct function assignment

* update the tag in the URL upon change

* use the eventEmitter to update the tags in the SnippetNoteDetail header via the TagSelect component

* respect themes when modal is opened

* show error message when trying to rename to an existing tag

* lint fix, const over let

* add missing letter

* fix routing and add merge warning dialog

* fix space-before-parens lint error

* change theming

* add check if tag changed

Co-authored-by: Khaliq Gant <khaliqgant@gmail.com>
2020-04-21 20:55:56 +09:00
Junyoung Choi
7fef7660e4 Add roadmap 2020-04-21 20:45:05 +09:00
Junyoung Choi
01d021cc4c Add boosthub intro 2020-04-21 18:33:57 +09:00
Callum Booth
c355f81525 remove redundant icons 2020-04-20 08:43:01 +01:00
Callum Booth
d138a54dfd fix accidental deletion of rtl state 2020-04-20 08:42:45 +01:00
Junyoung Choi
514d4b9059 v0.15.3 2020-04-20 11:43:38 +09:00
Callum Booth
a7ead67c2d moves orientation button to view menu 2020-04-18 14:17:08 +01:00
Callum Booth
2f16784a20 Merge branch 'master' of https://github.com/BoostIO/Boostnote into fix-2903 2020-04-18 13:54:27 +01:00
ZeroX-DG
8ca3ba21ee Merge branch 'master' of https://github.com/BoostIO/Boostnote into migrate-to-jest 2020-04-17 21:49:38 +12:00
ZeroX-DG
58ae6419f0 fixed markdown test error 2020-04-17 21:43:05 +12:00
Junyoung Choi
b56e0b98e3 Use netral color 2020-04-16 00:55:18 +09:00
Arcturus
4def32ab13 add style rule for table 2020-04-16 00:55:18 +09:00
Arcturus
0de78d12ef add hard coded styling 2020-04-16 00:55:18 +09:00
Arcturus
e756534db4 refactoring 2020-04-16 00:55:18 +09:00
Arcturus
2194965dc4 remove hard coded styling 2020-04-16 00:55:18 +09:00
Arcturus
f9e54bcbfc add styling for code 2020-04-16 00:55:18 +09:00
Junyoung Choi
667fd3a601 Fix test 2020-04-16 00:25:11 +09:00
Junyoung Choi
461e24bf39 Fix regex 2020-04-16 00:25:11 +09:00
Junyoung Choi
ac2cfe5169 Fix themeManager 2020-04-15 23:59:12 +09:00
ZeroX-DG
3f320f4337 resolved conflict 2020-04-12 18:45:49 +12:00
xatier
433ee9ed45 Update zh-TW.json (#3537)
- Update zh-TW translation.
- Sync with [locales/en.json](e44381f295/locales/en.json).
2020-04-09 11:58:35 +09:00
hikerpig
6ee92588b1 When storage or folder is removed, Detail components should render without error (#3168)
* optimize: when storage or folder is removed, Detail components should render without error, fix #2876

* optimize: Handle some scenarios where storage is not found, should not break the renderer

* optimize: NoteList should work without error when storage is not found
2020-04-06 18:02:52 +09:00
Junyoung Choi
0d797ce8a8 Merge pull request #2658 from gregueiras/fixIssue2534
Fix issue2534
2020-04-06 17:51:45 +09:00
Gonçalo Santos
4915c545d9 Merge branch 'master' into fixIssue2534 2020-03-27 01:47:02 +00:00
Gonçalo Santos
e1c95fb1f2 Fix Saving Configuration Bug 2020-03-27 01:42:18 +00:00
Junyoung Choi
5f56d3e0de v0.15.2 2020-03-26 18:32:15 +09:00
Junyoung Choi
d6b86b902c Fix scroll sync (#3531)
* Discard empty file

* Fix scroll sync
2020-03-26 18:02:53 +09:00
dependabot[bot]
3abc0fec38 Bump lodash.mergewith from 4.6.1 to 4.6.2
Bumps [lodash.mergewith](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 14:56:36 +09:00
dependabot[bot]
c0619eb746 Bump lodash-es from 4.17.10 to 4.17.15
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.10 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.10...4.17.15)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-26 14:55:57 +09:00
AWolf81
791ababe1e add gfm with modified regex & improve link text handling 2020-03-26 03:55:23 +09:00
AWolf81
d829216c8d Remove duplicated if 2020-03-26 03:55:23 +09:00
AWolf81
1cf6f3b1e2 WIP: Change space before parens. Tag link handling issue present. 2020-03-26 03:55:23 +09:00
AWolf81
4d5939aaf4 address requested changes - tag link & redundant line 2020-03-26 03:55:23 +09:00
AWolf81
2695f62f3e add special link handling 2020-03-26 03:55:23 +09:00
KZ
ccd0355d0b Merge pull request #3521 from BoostIO/update-readme
Update readme
2020-03-18 12:48:04 +09:00
KZ
6d6e3a51c0 Update readme 2020-03-18 12:45:43 +09:00
Gonçalo Santos
48c8164689 Fix scheduled theme change timing 2020-03-10 15:41:34 +00:00
Gonçalo Santos
38ed5b8541 Remove handleSlider 2020-03-05 16:34:04 +00:00
Gonçalo Santos
8a6df8bf95 Remove comment 2020-03-05 16:07:45 +00:00
Junyoung Choi
050b1563df v0.15.1 2020-03-04 05:51:13 +09:00
Junyoung Choi
dbbcf385b1 Discard unused ref 2020-03-04 05:50:23 +09:00
Junyoung Choi
d95a3af667 Fix propTypes warning 2020-03-04 05:50:04 +09:00
Junyoung Choi
87a737babc Make rtl optional 2020-03-04 05:43:34 +09:00
Junyoung Choi
a27ddd7490 Fix ref 2020-03-04 05:43:34 +09:00
dependabot[bot]
5693b6d0f5 Bump url-parse from 1.4.0 to 1.4.7
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.0 to 1.4.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.0...1.4.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-04 03:56:54 +09:00
dependabot[bot]
9debe8218d Bump lodash.template from 4.4.0 to 4.5.0
Bumps [lodash.template](https://github.com/lodash/lodash) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.4.0...4.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-04 03:56:42 +09:00
Aaron Bird
9549355ab7 Tooltip misplaced (#3499)
* fix bug: tooltip misplaced

* Transition only opacity attributes
2020-03-04 03:56:31 +09:00
Nguyen Viet Hung
88e8d2e009 updated PR template (#3498)
* updated PR template

* fixed grammar
2020-02-28 11:53:27 +09:00
Flexo013
a2ea5dd12e Update issue template (#3489)
* Update issue template

* Capitals

* Replace ! with .
2020-02-28 11:52:56 +09:00
Junyoung Choi
9f932a0911 Merge pull request #3002 from AWolf81/feature-tag-links
Add tag link handling with :tag:#tag syntax
2020-02-26 17:32:05 +09:00
Aaron-Bird
71f05b9886 fix: Folder sidebar cannot scroll 2020-02-26 09:57:07 +09:00
AWolf81
2b4e2638dc change tag link format to :tag:tag 2020-02-25 08:16:52 +01:00
Gonçalo Santos
9c3f34fe04 Fix Lint Errors 2020-02-25 03:34:34 +00:00
Gonçalo Santos
d4123eeccd Merge branch 'master' of https://github.com/BoostIO/Boostnote into fixIssue2534 2020-02-25 03:31:21 +00:00
Junyoung Choi
d727a6110a 0.15.0 2020-02-24 18:11:36 +09:00
Junyoung Choi
c42635579c Bump optional deps 2020-02-24 18:10:54 +09:00
Gonçalo Santos
fd54a7b85c Scroll Bars can now be hidden (#2713)
* Scroll Bars can now be hidden

* Fix lint problems

* fix lint errors

* Fix lint

* Fix lint

* Change scrollBar to showScrollBar
2020-02-24 17:14:35 +09:00
xatier
76de78a72e Update build.md
Sync with [build.md](docs/build.md).

- 997ffa620d
- d475146d80
2020-02-24 16:57:19 +09:00
Hahn
5edce1fe6a [WIP]h1 padding bottom value changed (#3215)
* h1 padding bottom value changed

* padding top added to <p>

* padding top to h1

* h2 padding top updated

* heading changed

* typo

* Updated margin for h1 ~ h6
2020-02-24 16:56:50 +09:00
Alex Garrity
afbe43965e Reverse notes after filtering 2020-02-24 16:56:08 +09:00
Alex Garrity
d706a5375c Added sorting direction buttons 2020-02-24 16:56:08 +09:00
Alexander Wolf
feb2a878a9 Dropdown colour (theme aware) (#3472)
* catchup (#7)

* added rtl toggle button

* added rtl toggle button

* keep code styling aligned to the left and ltr at all times

* added hotkey setting for direction toggle

* fixed requested changes

* fix undefined variable

* Copyright info update

updated Copyright (C) 2017 - 2019 BoostIO to Copyright (C) 2017 - 2020 BoostIO

* Refine Chinese translation

- Should not translate "space", it means space key in most context
- Should translate "keymap", "spellcheck disabled", "auto detect"
- Should translate "On Right Click" to "右键点击"
- Refine misc translation.

* Added Wiki Link

* Add Traditional Chinese option to build.md

* Bug fix (sets tabWith to 2 on prettier configuration and ConfigManager so checkboxes can be clikable)

Co-authored-by: ibraude <48394109+ibraude@users.noreply.github.com>
Co-authored-by: Junyoung Choi <rokt33r.choi@gmail.com>
Co-authored-by: Satyendra <33686367+developersatyendra@users.noreply.github.com>
Co-authored-by: Andrew <andysim3d@gmail.com>

* Menu Backgroun Colour

* add white to the apply-theme loop

Co-authored-by: Milo Todt <milo@milotodt.com>
Co-authored-by: ibraude <48394109+ibraude@users.noreply.github.com>
Co-authored-by: Junyoung Choi <rokt33r.choi@gmail.com>
Co-authored-by: Satyendra <33686367+developersatyendra@users.noreply.github.com>
Co-authored-by: Andrew <andysim3d@gmail.com>
2020-02-24 16:51:05 +09:00
AWolf81
e55f1e0308 fix linting 2020-02-19 00:29:54 +01:00
ZeroX-DG
8b2ed8585f fixed wrong handler for setting button 2020-02-18 22:52:21 +09:00
Alexander Wolf
1d570df129 Merge branch 'master' into feature-tag-links 2020-02-12 20:51:59 +01:00
AWolf81
d125bd07f7 Merge branch 'master' into feature-tag-links 2020-02-12 20:42:09 +01:00
jhdcruz
997ffa620d Required both package for builds | Yarn 2020-02-12 18:20:37 +09:00
jhdcruz
d475146d80 Add build dependency packages cmds 2020-02-12 18:20:37 +09:00
dependabot[bot]
1fe59caa19 Bump merge from 1.2.0 to 1.2.1
Bumps [merge](https://github.com/yeikos/js.merge) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/yeikos/js.merge/releases)
- [Commits](https://github.com/yeikos/js.merge/compare/v1.2.0...v1.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-10 21:09:59 +09:00
Baptiste Augrain
a496a84cb8 fix broken headers' color in ConfigTab 2020-02-10 21:09:37 +09:00
Baptiste Augrain
eae964f7e7 add Vulcan UI theme 2020-02-10 21:09:37 +09:00
Junyoung Choi
e44381f295 Merge pull request #2964 from amedora/filter-tags-and-folders
Filter tags and folders
2020-02-06 16:21:15 +09:00
amedora
2c0e0a6e39 Merge branch 'master' into filter-tags-and-folders
# Conflicts:
#	browser/main/SideNav/index.js
2020-02-05 11:13:28 +09:00
Arcturus
216f588aa4 extended language support for multiple languages 2020-02-05 10:00:17 +09:00
Nguyen Viet Hung
592aca1539 fixed eslint error & integrated with prettier as well as formatted the whole codebase (#3450) 2020-02-05 09:28:27 +09:00
amedora
725bf2a691 Merge branch 'master' into filter-tags-and-folders
# Conflicts:
#	browser/main/SideNav/PreferenceButton.styl
#	browser/main/SideNav/SideNav.styl
2020-02-05 09:07:13 +09:00
hiiwave
051ce9e208 Fix #3397 (#3398)
* WIP: Fix #3397

* fixup! WIP: Fix #3397

* fix: catch potential URIError threw from decodeURI
2020-02-03 20:33:38 +09:00
dependabot[bot]
f367e9f08c Bump extend from 3.0.1 to 3.0.2
Bumps [extend](https://github.com/justmoon/node-extend) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/justmoon/node-extend/releases)
- [Changelog](https://github.com/justmoon/node-extend/blob/master/CHANGELOG.md)
- [Commits](https://github.com/justmoon/node-extend/compare/v3.0.1...v3.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-03 20:05:22 +09:00
dependabot[bot]
f72fdfe33f Bump mixin-deep from 1.3.1 to 1.3.2
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-03 20:05:10 +09:00
dependabot[bot]
000a54f5ed Bump handlebars from 4.0.11 to 4.5.3
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.11 to 4.5.3.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.11...v4.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-03 20:04:48 +09:00
xatier
57a5de97f8 Update build.md
text change per @rayou's suggestion.
2020-02-03 20:04:31 +09:00
xatier
262d173c65 Update zh_TW translation of build.md
Complete translation.
Sync with English version.

[1] https://github.com/BoostIO/Boostnote/blob/master/docs/build.md.
2020-02-03 20:04:31 +09:00
Ray Ou
e558fae4b0 Update build.md with correct description 2020-02-03 20:04:02 +09:00
Bumhan Yu
a90d801d08 update korean docs in docs/ko/ (#3454)
* update korean docs in docs/ko/

* Fix typo

Co-authored-by: Junyoung Choi <rokt33r.choi@gmail.com>
2020-02-03 20:03:21 +09:00
Bumhan Yu
636996356f update Korean section of contributing.md 2020-02-03 20:01:44 +09:00
Junyoung Choi
8a87c06b97 Merge pull request #2879 from daiyam/theme-nord
add Nord UI theme
2020-02-03 19:49:09 +09:00
Baptiste Augrain
69831571a5 fix broken title's color in ConfigTab by moving its default color to corresponding themes 2020-01-31 23:46:27 +01:00
Baptiste Augrain
24a5c839a7 fix text color of FolderSelect component 2020-01-31 12:10:19 +01:00
Baptiste Augrain
93e09f11dd fix UI theme for SplitEditor and CreateFromURL modal 2020-01-30 19:31:31 +01:00
Baptiste Augrain
c570fc9873 Merge branch 'master' into theme-nord 2020-01-30 19:14:58 +01:00
amedora
f1d03acbad Merge branch 'master' into filter-tags-and-folders
# Conflicts:
#	locales/zh-CN.json
2020-01-30 10:13:16 +09:00
Jeny Mazo
31ffbd98b6 Bug fix (sets tabWith to 2 on prettier configuration and ConfigManager so checkboxes can be clikable) 2020-01-29 16:37:54 +09:00
Ray Ou
87b9766bc0 Add Traditional Chinese option to build.md 2020-01-29 16:37:24 +09:00
Milo Todt
49c9bcac9a Added Wiki Link 2020-01-29 16:33:57 +09:00
Andrew
5105babd14 Refine Chinese translation
- Should not translate "space", it means space key in most context
- Should translate "keymap", "spellcheck disabled", "auto detect"
- Should translate "On Right Click" to "右键点击"
- Refine misc translation.
2020-01-29 16:31:38 +09:00
Satyendra
0a361f5f41 Copyright info update
updated Copyright (C) 2017 - 2019 BoostIO to Copyright (C) 2017 - 2020 BoostIO
2020-01-29 16:30:54 +09:00
Junyoung Choi
8218d5eb5a Merge pull request #3282 from ibraude/master
Added rtl toggle button
2020-01-29 16:28:31 +09:00
Itai Braude
7fe6925615 fix undefined variable 2020-01-16 23:52:19 +02:00
cephonodes
1dd71fc923 Updated Japanese document about contribution 2020-01-16 07:44:53 +09:00
ibraude
301f03dadd Merge branch 'master' into master 2020-01-07 12:11:27 +02:00
KZ
53c48d86b7 Merge pull request #3415 from BoostIO/update-readme
Update readme
2020-01-06 13:15:05 +09:00
KZ
d760259ce6 Update readme 2020-01-06 13:12:49 +09:00
Junyoung Choi
1195c77f7a v0.14.0 2020-01-03 11:28:19 -05:00
Flexo013
c373c207c0 Added link to downloads 2019-12-29 07:21:13 +09:00
Junyoung Choi
65e83e7017 Merge pull request #2585 from daiyam/fix-mermaid-height
fix height of mermaid diagrams
2019-12-25 05:07:07 -05:00
Itai Braude
0722c2505a fixed requested changes 2019-12-25 10:04:31 +02:00
Abner Soares Alves Junior
3c12e0d119 Fix emoji render on notes list 2019-12-24 06:54:20 +09:00
Abner Soares Alves Junior
60d6c68e48 Add some translations 2019-12-24 06:53:26 +09:00
Abner Soares Alves Junior
d8605965a8 Add Ok button to export confirmation box 2019-12-24 06:53:26 +09:00
Yuki Furukawa
6d455fc286 remove redundant conditions of macOS in main-menu 2019-12-24 06:39:18 +09:00
Mayke
2882667e94 rebuilding test snapshots 2019-12-24 06:36:53 +09:00
Mayke
7fa578880e refactoring to use the new .markdownIt-TOC-wrapper div 2019-12-24 06:36:53 +09:00
Mayke
3f465df1cd configuring a div wrapper for TOC plugin to use overflow-y in <ul> and still use &:before on parent element 2019-12-24 06:36:53 +09:00
Mayke
c423784cc5 adding TOC UI 2019-12-24 06:36:53 +09:00
Nicholas Browning
ce853a7e3a Export: CSS: adjusted relative path 2019-12-24 06:23:51 +09:00
Nicholas Browning
099ebad06b Export: uses markdown preview dom. Supports diagrams 2019-12-24 06:23:51 +09:00
Junyoung Choi
75a1347ae1 Update readme and contributing 2019-12-23 10:02:41 +09:00
amedora
f5779558bb Merge branch 'master' into filter-tags-and-folders
# Conflicts:
#	browser/main/SideNav/index.js
#	locales/da.json
#	locales/de.json
#	locales/en.json
#	locales/es-ES.json
#	locales/fa.json
#	locales/fr.json
#	locales/hu.json
#	locales/it.json
#	locales/ja.json
#	locales/ko.json
#	locales/no.json
#	locales/pl.json
#	locales/pt-BR.json
#	locales/pt-PT.json
#	locales/ru.json
#	locales/sq.json
#	locales/th.json
#	locales/tr.json
#	locales/zh-CN.json
#	locales/zh-TW.json
2019-12-23 09:41:40 +09:00
Baptiste Augrain
b83d3e5c33 Merge branch 'master' into fix-mermaid-height 2019-12-22 23:46:47 +01:00
Junyoung Choi
1a7c719a4e Update new BoostNote info 2019-11-26 14:03:57 +09:00
Gonçalo Santos
b91a76b3b1 Merge branch 'master' into fixIssue2534 2019-11-22 00:19:56 +00:00
Jack Hsieh
d78f6b7aba Ability to stop auto update 2019-11-22 05:42:55 +09:00
dependabot[bot]
7d4d176bf4 Bump js-yaml from 3.12.0 to 3.13.1
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.12.0 to 3.13.1.
- [Release notes](https://github.com/nodeca/js-yaml/releases)
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.12.0...3.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-22 03:57:10 +09:00
hikerpig
52ea44ceaa Remove overcomplicated solution for scrollbar styling. 2019-11-22 03:55:39 +09:00
Nicholas Browning
132d04326b TOC: Scrolling: Fixed TOC links and scrolling 2019-11-22 03:55:39 +09:00
nam
9996b5d686 bump mermaid version to support state diagrams 2019-11-22 03:47:42 +09:00
Rafael Gonzaga
e9d9f49ff3 minor changes
just to improve reading file
2019-11-22 03:47:03 +09:00
Nicholas Browning
95300546dc Delete Dialog: typo for the "type" property 2019-11-22 03:46:45 +09:00
Itai Braude
489fc6578b changed webpack config back to original 2019-11-22 03:46:22 +09:00
Itai Braude
edebba6680 added more admonition styles 2019-11-22 03:46:22 +09:00
dependabot[bot]
72c2a20a74 Bump lodash from 4.17.10 to 4.17.13
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.10 to 4.17.13.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.10...4.17.13)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-22 03:30:33 +09:00
PetrTodorov
abef6c5fee Added Czech translation (#3264)
* Added Czech translation

* Update locales/de.json

Fixed german translation as suggested.

Co-Authored-By: Simon <simon81186@aol.com>
2019-11-04 14:44:09 +09:00
Junyoung Choi
662ae73637 Upgrade electron to v4 2019-10-29 13:28:51 +09:00
roottool
3ef485548a Removed a single quotation 2019-10-28 18:16:59 +09:00
roottool
a90c10ef3e removed single quotation 2019-10-28 18:16:59 +09:00
roottool
d43fe8db75 #3147 fix: added script tag and stylesheet 2019-10-28 18:16:59 +09:00
AWolf81
1d84cac922 Rephrase error messages 2019-10-28 18:10:21 +09:00
AWolf81
5280b6ed63 Add error handling 2019-10-28 18:10:21 +09:00
hikerpig
77833ff980 Fix preview-window's scroll behavior, #3289 2019-10-28 18:09:29 +09:00
Itai Braude
45e75cdfe9 added hotkey setting for direction toggle 2019-10-18 00:36:51 +03:00
Itai Braude
2cb4cbe1b6 keep code styling aligned to the left and ltr at all times 2019-10-17 23:57:04 +03:00
Itai Braude
b22b09a93d added rtl toggle button 2019-10-17 21:04:00 +03:00
Itai Braude
e34485eb83 added rtl toggle button 2019-10-17 20:48:40 +03:00
Junyoung Choi
d010c5532d v0.13.0 2019-10-16 20:18:47 +09:00
hikerpig
f2dc8b8020 optimize: npm 'test' script should contain jest tests 2019-10-15 13:24:44 +09:00
Junyoung Choi
1798353eac Merge pull request #3173 from hikerpig/feature/toc
Suppport auto generating toc content for the [TOC] tag
2019-10-15 13:24:30 +09:00
Kazumasa Yokomizo
772a8b2383 Updated the readme 2019-10-14 13:34:27 +09:00
amedora
fc08d2f8c3 Merge branch 'master' into migrate-to-jest
# Conflicts:
#	tests/lib/snapshots/markdown-test.js.md
#	tests/lib/snapshots/markdown-test.js.snap
2019-10-14 07:41:19 +09:00
hikerpig
5690c8361a 🔥 remove obsolete snap file 2019-10-11 10:41:38 +08:00
hikerpig
6d09cf227c Merge remote-tracking branch 'origin/master' into feature/toc
Fix conflicts
2019-10-10 18:23:39 +08:00
Junyoung Choi
8736666e91 Fix default prettier hotkey 2019-10-10 18:01:27 +09:00
Junyoung Choi
d1fd5cfb45 Set prettier external deps 2019-10-10 18:01:09 +09:00
hikerpig
3eabf95fb3 optimize implementation for a0c15182 2019-10-10 16:34:28 +09:00
hikerpig
8ea920ef91 fix: Can't open external browser in Markdown Preview with external link containing '#', close #3213 2019-10-10 16:34:28 +09:00
jhdcruz
3c0f20f364 Single Instance fix #3241
Fixes single instance depreciation
2019-10-10 16:33:21 +09:00
alwxkxk
59f8425c97 fix #2935 2019-10-10 16:29:55 +09:00
hikerpig
f181a7e459 more strict regex pattern in pathname matching, fix #3183 2019-10-10 16:29:27 +09:00
MSSandroid
6b1c595f87 add test for PlantUml Ditaa 2019-10-10 16:26:57 +09:00
MSSandroid
0003de8f08 remove code redundancy in parsing of PlantUml 2019-10-10 16:26:57 +09:00
MSSandroid
5357d8dc04 remove code redundancy in parsing of PlantUml 2019-10-10 16:26:57 +09:00
MSSandroid
d069722bf9 require(\'markdown-it-plantuml\') only once 2019-10-10 16:26:57 +09:00
MSSandroid
3f4dd49a8f added missing newline at end of document 2019-10-10 16:26:57 +09:00
Michael Schuldes
be06b3f7e8 Added plantUML Gantt support 2019-10-10 16:26:57 +09:00
Michael Schuldes
5044bdda00 Added plantUML wbs support 2019-10-10 16:26:57 +09:00
Michael Schuldes
fbeffb0b5d Added plantUML mindmap support 2019-10-10 16:26:57 +09:00
Olcod
ef0af39aa7 Added package-lock file to the gitignore 2019-10-10 16:13:45 +09:00
Olcod
0697bc0a74 Add ability to sort lines with a hot key combination 2019-10-10 16:13:45 +09:00
Callum Booth
59e361cb37 move config value into ui 2019-10-09 12:59:53 +01:00
Callum Booth
1993a6588d Cleans up toggle button component 2019-10-09 12:45:52 +01:00
Callum Booth
218fba1aa1 fix formatting 2019-10-09 12:41:26 +01:00
Callum Booth
4de6c69f5d merge from upstream/master 2019-10-08 13:21:56 +01:00
Aleksei Seletskiy
43d8ebb3c4 Dracula theme buttons in storage settings fix 2019-09-14 13:20:50 +09:00
Robert Weber
68175cd71b Add sidebar collapse button to sidebar while viewing the tags list
Fixes #2097
2019-09-14 13:20:37 +09:00
lockee14
2b3538d3b1 add indentation and closing brace
missing indentation and brace between line 229 - 234:
before:
'Shift-Cmd-/': function (cm) {
        if (global.process.platform !== 'darwin') { return }
      [translateHotkey(hotkey.insertDateTime)]: function (cm) {
        const dateNow = new Date()
        cm.replaceSelection(dateNow.toLocaleString())
},

after:
'Shift-Cmd-/': function (cm) {
        if (global.process.platform !== 'darwin') { return }
        [translateHotkey(hotkey.insertDateTime)]: function (cm) {
          const dateNow = new Date()
          cm.replaceSelection(dateNow.toLocaleString())
        }
},
2019-09-12 08:30:53 +09:00
lockee14
b84f1173b7 add a comma at the end of line 114 2019-09-12 08:15:42 +09:00
lockee14
bdfe8c0445 add a comma at the end of line 73 2019-09-12 08:14:39 +09:00
amedora
f4d87f64ae Fix #3190 - App blanks out after setting HotKey (#3193)
* fix lack of hotkey properties

* Update HotkeyTab.js
2019-09-03 02:24:09 +09:00
Junyoung Choi
68b3077651 Merge pull request #3099 from AWolf81/html-to-md
Html to md feature
2019-09-03 02:03:51 +09:00
Junyoung Choi
1332b337f3 Merge pull request #3136 from hikerpig/feature/scrollbarAppearance
tweak MarkdownPreview style to optimize overflow scrollbar display
2019-09-03 02:03:23 +09:00
hikerpig
e9975d1ea5 fix: HotkeyTab accidentally set incomplete hotkey, related #3190 2019-09-03 01:59:59 +09:00
Thamara Andrade
2c103aca3d Fix #888 - Wrong word count due splitting 2019-09-03 01:54:53 +09:00
Thamara Andrade
c0a5eb0d2b Fix #888 - Wrong word count due splitting 2019-09-03 01:54:53 +09:00
霸气千秋
ff7c4495f0 format locale files 2019-09-03 01:53:45 +09:00
minbaby.zhang
35fe639cd2 optimize translate 2019-09-03 01:53:45 +09:00
minbaby.zhang
59add8982e update lang 2019-09-03 01:53:45 +09:00
minbaby.zhang
8d9c514097 update lang 2019-09-03 01:53:45 +09:00
minbaby.zhang
6f880d0f02 更新翻译 2019-09-03 01:53:45 +09:00
AWolf81
ec47ee8110 Remove manual script tag filter and use turndown remove filter 2019-08-31 21:35:09 +02:00
lockee14
f64d0b35e1 Merge branch 'master' into Insert_Date_ISO_8601_Style 2019-08-30 21:54:38 +09:00
Nguyễn Việt Hưng
28b8141c6b fixed test 2019-08-30 12:46:40 +09:00
Nguyễn Việt Hưng
5b0b309c49 added test for getAttachmentsPathAndStatus 2019-08-30 12:46:40 +09:00
Nguyễn Việt Hưng
0b84a372f6 re-organize attachment functions and updated comments doc 2019-08-30 12:46:40 +09:00
Nguyễn Việt Hưng
8355e1e006 updated function name and return type 2019-08-30 12:46:40 +09:00
Nguyễn Việt Hưng
c7d33fbd83 Allow user to view attachments and clear unused attachments 2019-08-30 12:46:40 +09:00
ehhc
cf324d93fe Add option to disable the automiatic deletion of un-referenced attachments -> might fix #3203 2019-08-30 12:46:40 +09:00
hikerpig
9a704a2bcb Merge branch 'master' into feature/scrollbarAppearance 2019-08-26 10:38:26 +08:00
hikerpig
1e00651541 feature/toc: upgrade "@hikerpig/markdown-it-toc-and-anchor" package to avoid default anchor lowercase casting 2019-08-25 18:25:17 +08:00
AWolf81
857e75594d Disable Javascript for printout window 2019-08-24 13:43:36 +09:00
AWolf81
2f1dadfc3e Change drag disable styles to be more specific 2019-08-24 13:43:14 +09:00
AWolf81
7d0404657e Fix routing for tag filtering 2019-08-24 13:42:09 +09:00
Junyoung Choi
b9dd651fc1 Merge pull request #3093 from nathan-castlehow/feat-run-prettier-on-markdown
Feat run prettier on markdown
2019-08-24 13:41:41 +09:00
hikerpig
25ef456af2 feat: should scroll to top after selecting another note, also fix #3023 2019-08-24 13:39:28 +09:00
hikerpig
084decaa85 improvement: MarkdownPreview, rewriteIframe attempt can be combined to one call 2019-08-24 13:39:28 +09:00
hikerpig
330a444fc5 optimize: should highlight any non-empty search query, fix #3164 2019-08-24 13:38:28 +09:00
alwxkxk
a47dac2854 fix #3159 2019-08-24 13:38:09 +09:00
Ronald Walker
08070f3e2d fix #3144 2019-08-24 13:37:43 +09:00
hikerpig
2352c78cb6 Add CodeEditor::setLineContent method to manipulate line contents, related #3163
Avoid changing all CodeMirror instance's contents
2019-08-24 13:36:56 +09:00
Antonio Cangiano
6ef9c3865f Fix JavaScript hello world example
The current snippet note example references a non-existent element with id `enjoy`. I updated it to reference the correct id (i.e., `hello`).
2019-08-24 13:36:29 +09:00
sirrah23
ff9789b5a7 Fix 3060
Right now there are only two export types that are using a special
output formatter, pdf and html. Both of these formatters currently populate the
`/html/head/base` portion of the associated html document with the name
of the target directory for the file that the user is exporting.

In order for internal links within the exported document to work
correctly, the value of base must also include the filename. This fix
removes the call to `path.dirname`, which gets rid of the necessary
filename.
2019-08-24 13:36:05 +09:00
Jack Hsieh
f09297f406 Fix 2636 (#3206)
* Fix 2636 Can't scroll to bottom of editor pane

* Fix minor lint issues
2019-08-11 23:22:53 +09:00
amedora
3921655157 replace the snapshot file 2019-08-07 15:22:27 +09:00
amedora
e4e10d523f fix how handle the storage as context data 2019-08-07 14:52:17 +09:00
amedora
404dddcb86 rename files to suit Jest 2019-08-07 14:28:38 +09:00
amedora
ffb2603485 jest-codemods tests/lib 2019-08-07 14:17:48 +09:00
nathan-castlehow
2d3c69d178 Fixed eslint issue 2019-08-01 20:13:46 +08:00
nathan-castlehow
b837653cf1 Merged Master into feature branch and fixed conflicts 2019-08-01 20:12:58 +08:00
nathan-castlehow
eeca031c86 Merge upstream into master 2019-08-01 19:56:38 +08:00
nathan-castlehow
918a8627e9 Merge upstream into master 2019-08-01 19:55:21 +08:00
nathan-castlehow
86370edd1e Merge branch 'feat-run-prettier-on-markdown' of https://github.com/nathan-castlehow/Boostnote into feat-run-prettier-on-markdown 2019-08-01 18:36:40 +08:00
nathan-castlehow
1173631255 feat(prettierOnMarkdown): Forced prettier options to always have parser set to markdown when used. 2019-08-01 18:36:22 +08:00
nathan-castlehow
911fd9a004 feat(prettierOnMarkdown): Changed Prettier require to use import 2019-08-01 18:36:21 +08:00
nathan-castlehow
0ad3da5bbc feat(prettierOnMarkdown): Changed default hotkey value 2019-08-01 18:36:21 +08:00
nathan-castlehow
89ae2a9516 feat(prettierOnMarkdown):Fixed incorrect options passed to code mirror instance 2019-08-01 18:36:20 +08:00
nathan-castlehow
70892cae05 feat(prettierOnMarkdown):Tweaked spacing on default Prettier Config Value 2019-08-01 18:36:19 +08:00
nathan-castlehow
de0af153bc feat(prettierOnMarkdown):Added prettier config default to config manager 2019-08-01 18:36:19 +08:00
nathan-castlehow
33161e46e6 feat(prettierOnMarkdown): Added support for prettyifing markdown as well as added hot key option. Partial Implementation of Prettier config in configuration screen. TODO Fix defaulting of prettier configuration 2019-08-01 18:36:18 +08:00
nathan-castlehow
7e3c662374 feat(prettierOnMarkdown): Added Reference to prettier in Code Editor and created config file 2019-08-01 18:36:17 +08:00
nathan-castlehow
a39e9c2da6 feat(prettierOnMarkdown): Added Reference To Prettier 2019-08-01 18:36:16 +08:00
mehdi
928e0edf4d forgot to remove commented code 2019-07-29 16:24:42 +09:00
AWolf81
72b8d56245 Merge remote-tracking branch 'upstream/master' into html-to-md 2019-07-28 15:49:16 +02:00
AWolf81
0d36f59036 Create turndown service & use gfm turndown plugin 2019-07-28 15:02:17 +02:00
AWolf81
a3f7d2287a Add dracula theme styles 2019-07-28 11:00:40 +02:00
hikerpig
8edfbd28ed feat: suppport auto generating toc content for the '[TOC]' placeholder, related #3022 2019-07-27 16:55:32 +08:00
amedora
606be4304d Fix 3007 (#3028)
* fix code fences never sanitized

* fix mermaid xss

* Revert "fix mermaid xss"

This reverts commit 1ff179a1bd.

* configuable mermaid HTML label

* add locales for mermaid configuration
2019-07-27 12:39:12 +09:00
Junyoung Choi
329066719e 0.12.1 2019-07-27 11:51:40 +09:00
Junyoung Choi
93b8ef35f7 0.12.1-1 2019-07-26 13:50:44 +09:00
Junyoung Choi
484b003b34 Fix theme paths 2019-07-26 13:49:40 +09:00
Junyoung Choi
ed427130a9 0.12.1-0 2019-07-26 11:51:51 +09:00
Junyoung Choi
807feae540 Merge pull request #3165 from BoostIO/discard-warnings
Discard warnings
2019-07-26 11:45:29 +09:00
Junyoung Choi
4b62e93257 Fix more style warnings 2019-07-26 11:38:05 +09:00
Junyoung Choi
e425417d68 Load bfm mode info script only once 2019-07-26 11:33:05 +09:00
Junyoung Choi
bc1e837466 Set alias to stylus mode inof 2019-07-26 11:32:41 +09:00
Junyoung Choi
95321e33a0 Fix style of FolderItem 2019-07-26 11:32:07 +09:00
Junyoung Choi
5720b313a3 Merge branch 'master' into discard-warnings 2019-07-26 09:12:18 +09:00
hikerpig
f3e2205e69 fix several propType errors raised by 'react.development.js'
some are caused by typo, some are caused by unused propType declarations
2019-07-26 09:11:00 +09:00
Junyoung Choi
410b611b14 Discard all style warnings 2019-07-26 09:03:32 +09:00
Junyoung Choi
1c8af47bac Fix warnings in ToggleModeButton 2019-07-26 08:50:12 +09:00
Junyoung Choi
c8a2baca3c Use default value prop rather than value prop 2019-07-26 08:49:52 +09:00
Junyoung Choi
ac5a323115 Discard unused props 2019-07-26 08:49:21 +09:00
Junyoung Choi
fa65e7feef Fix isTagActive 2019-07-26 08:49:07 +09:00
amedora
71d27d0e55 MarkdownEditor and MarkdownSplitEditor always wrap lines 2019-07-26 07:50:53 +09:00
hikerpig
972d053c83 fix: array access error when token.map is null, fix #3123 2019-07-26 07:49:59 +09:00
Matt Gabriel
7797661489 Fixed permissions 2019-07-26 07:40:18 +09:00
Junyoung Choi
9f9e036c68 0.12.0 2019-07-24 16:57:04 +09:00
Tobias Doll
e940253caf Using single quotes for empty string 2019-07-22 16:43:21 +09:00
Tobias Doll
2b4d20b94e Dont highlight search term if search field is emptied 2019-07-22 16:43:21 +09:00
nathan-castlehow
f88fc23e58 Removed Paste / Cut options from preview context menu as they are not relevant in preview mode 2019-07-22 16:42:57 +09:00
nathan-castlehow
caf1f92fef Removed SetTimeout on Markdown Preview Context menu 2019-07-22 16:42:57 +09:00
nathan-castlehow
f2a02a25a7 Fixed Test Fail Issue due to incorrect Require for context menu items 2019-07-22 16:42:57 +09:00
nathan-castlehow
e85767b4a0 feat: Added Context Menu for markdown preview mode and copy url when hyperlink 2019-07-22 16:42:57 +09:00
amedora
c83e5cc7d8 add locales 2019-07-22 16:42:08 +09:00
amedora
71f565f66b fix UI change handler 2019-07-22 16:42:08 +09:00
amedora
769407b3df add "Wrap Line" button 2019-07-22 16:42:08 +09:00
amedora
e7615ed6d7 add "Wrap Line" button handlers 2019-07-22 16:42:08 +09:00
amedora
fe508307b2 make lineWrapping configurable 2019-07-22 16:42:08 +09:00
hikerpig
c2a26a8547 improvement: refactor buildStyle to NamedParameters style, and add some jsdoc 2019-07-21 15:28:12 +08:00
hikerpig
addf9b920f tweak MarkdownPreview style to optimize overflow scrollbar display, fix #2902 2019-07-21 15:28:12 +08:00
Baptiste Augrain
4e30d4b8fb fix URLs by using the correct path separator ('/' for an url and not path.sep) 2019-07-20 00:58:51 +09:00
Baptiste Augrain
850561613b fix gallery on windows 2019-07-20 00:58:51 +09:00
AWolf81
782d71ddb0 path fix for MacOs 2019-07-17 17:25:13 +09:00
AWolf81
a0799d19f8 fix app path 2019-07-17 17:25:13 +09:00
AWolf81
ba6eb4f26f fix path Linux 2019-07-17 17:25:13 +09:00
AWolf81
38fcee35c2 fix test for Windows (fs.rmdir throws dir not empty error) 2019-07-17 17:25:13 +09:00
AWolf81
4af7106e01 change theme path usage and remove relative path 2019-07-17 17:25:13 +09:00
AWolf81
bd52226ae2 change process.env check 2019-07-17 17:25:13 +09:00
ehhc
cb7ac77c61 Update attachmentManagement.js
Deleting unneeded stupid log-message
2019-07-10 11:42:00 +09:00
ehhc
55a7ee1f91 Debounce deletion of un-referenced attachments --> don't fixes but mitigates the problems of #3103 2019-07-10 11:42:00 +09:00
mehdi
80a63f7404 refactor: move the config down to editor setting 2019-07-08 19:27:23 +09:00
amorist
d37210a0d0 fix chinese language new note tab style 2019-07-08 13:14:56 +09:00
amedora
0c7a1e8f17 apply theme color to slider in MarkdownSplitEditor 2019-07-08 13:14:30 +09:00
Kazumi Harada
3d7ab40674 [update] apply the method of fixLocalURLS before exporting to pdf 2019-07-08 13:14:10 +09:00
Kazumi Harada
b8de51be57 [update] adjust code style 2019-07-08 13:13:52 +09:00
Kazumi Harada
1ce72b91ca [update] by force, insert the default value to the customCSS config 2019-07-08 13:13:52 +09:00
Kazumi Harada
3f96587a70 [update] move default value of the customCSS field to ConfigManager 2019-07-08 13:13:52 +09:00
Huachao Mao
c012bbd54a Update lock/unlock icons in editor #1760 2019-07-08 13:13:30 +09:00
David Thomason
a50852306e Loading insert day hotkey triggers from config.hotkey 2019-07-08 13:13:06 +09:00
David Thomason
db396ec107 Added insert date & time info to HotkeyTab settings 2019-07-08 13:13:06 +09:00
David Thomason
6b868658aa Changed "insert date & time" hotkey on mac from Shift-Cmd-/ to Alt-Cmd-/ 2019-07-08 13:13:06 +09:00
JianXu
9fe9e1a1c4 Add menu item "About" to "Help" 2019-07-08 13:12:22 +09:00
AWolf81
aeb77e5a40 Remove package-lock file & use startsWith for https check 2019-07-08 00:05:26 +02:00
nathan-castlehow
1d59d89588 feat(prettierOnMarkdown): Forced prettier options to always have parser set to markdown when used. 2019-07-03 09:28:36 +08:00
nathan-castlehow
bde357f952 feat(prettierOnMarkdown): Changed Prettier require to use import 2019-07-03 09:03:24 +08:00
AWolf81
558c091205 fix linting 2019-06-30 00:18:52 +02:00
AWolf81
f67175e628 fix test 2019-06-30 00:03:54 +02:00
AWolf81
390f6d545f fix PropTypes 2019-06-30 00:03:25 +02:00
AWolf81
44efb0178c Merge branch 'master' into html-to-md
# Conflicts:
#	browser/main/modals/NewNoteModal.js
#	package-lock.json
#	package.json
#	yarn.lock
2019-06-29 23:25:52 +02:00
AWolf81
37eee26bdf fix linting & routing 2019-06-29 23:21:32 +02:00
Junyoung Choi
b4251a793b Merge pull request #3037 from AWolf81/fix-search-issue-unicode
Simplified search input & fixed chinese character input
2019-06-29 00:41:24 +09:00
roottool
6736a08240 Fixes that TOC hasn't id attribute when the title is all 2-byte characters (#2994)
* Fix: 2-byte character support of slug

* Fix: Decodes slug to display slug

* Fix: Removed a logic of replaceDiacritics

* Fix: Fixed slugify to pass tests

* Fix: Fixed not to remove underscore

* Adds the test for slugify.js

* Fix: Fix to jump to heading

* Added a comment

* Fix: Created click event only linking to heading

* Fix: Fix to use handleLinkClick(e)

* Fix: Changed the regex rule

* Fix: Changed the regex rule of extractId
2019-06-29 00:40:16 +09:00
mehdi
6e45ee6a38 add Date ISO 8601 format 2019-06-24 06:10:31 +09:00
nathan-castlehow
ed4a670f0a feat(prettierOnMarkdown): Changed default hotkey value 2019-06-23 13:54:17 +08:00
nathan-castlehow
fbb9afe34f feat(prettierOnMarkdown):Fixed incorrect options passed to code mirror instance 2019-06-23 13:42:14 +08:00
nathan-castlehow
020bc11bd7 feat(prettierOnMarkdown):Tweaked spacing on default Prettier Config Value 2019-06-23 13:41:52 +08:00
nathan-castlehow
ae0837e29b feat(prettierOnMarkdown):Added prettier config default to config manager 2019-06-23 13:41:34 +08:00
nathan-castlehow
f0380ef733 feat(prettierOnMarkdown): Added support for prettyifing markdown as well as added hot key option. Partial Implementation of Prettier config in configuration screen. TODO Fix defaulting of prettier configuration 2019-06-23 13:40:20 +08:00
nathan-castlehow
25bdaf9f00 feat(prettierOnMarkdown): Added Reference to prettier in Code Editor and created config file 2019-06-23 13:34:47 +08:00
nathan-castlehow
ef1809305c feat(prettierOnMarkdown): Added Reference To Prettier 2019-06-23 13:34:47 +08:00
Callum Booth
ba34458feb changes if state to be more readable 2019-06-10 20:27:55 +01:00
Callum Booth
a2fb50a71c adds destructed fontFamily from props 2019-06-10 19:21:33 +01:00
Callum booth
b15a4007ee merge from master 2019-06-10 19:13:58 +01:00
nathan-castlehow
090b5c32f0 feat: Added Context Menu for markdown preview mode and copy url when hyperlink 2019-06-09 13:28:53 +08:00
KZ
49c75e3599 Merge pull request #3059 from BoostIO/update-funding-yml
Update FUNDING.yml
2019-06-06 10:44:34 +09:00
KZ
05765642d9 Update FUNDING.yml 2019-06-06 10:39:09 +09:00
amedora
f62eba9d7b Fix #2922 (#2998)
* update katex to 0.10.1

* Update snapshot for markdown-test
2019-06-03 13:57:16 +09:00
amedora
bc27fd0acc disable fuzzy link 2019-06-03 13:54:40 +09:00
Ryota Kusano
244a28c7d2 Adjust continuous quotation pattern 2019-05-29 22:55:05 +09:00
Ryota Kusano
edac4d3fed Fix to remove unnecessary escape charactor erorr on IDE 2019-05-29 22:55:05 +09:00
Ryota Kusano
e402929cca Fix regular expression related checkbox logic 2019-05-29 22:55:05 +09:00
AWolf81
639bfbe549 apply search term on enter key 2019-05-29 09:02:34 +02:00
AWolf81
2e380ceb02 use Connected-React-Router to navigate 2019-05-29 08:29:53 +02:00
AWolf81
f26dea2420 Merge branch 'master' into feature-tag-links 2019-05-29 08:11:51 +02:00
AWolf81
60e841e5a2 fix focus loss by checking switchPrieview prop change 2019-05-29 07:52:22 +02:00
AWolf81
25d055e560 Merge branch 'master' into fix-search-issue-unicode
# Conflicts:
#	browser/main/TopBar/index.js
#	yarn.lock
2019-05-29 07:42:20 +02:00
Morten Lautrup
052fb3df5b Specify wich images should not be draggable
Make only images and spans inside buttons undraggable
2019-05-29 13:02:37 +09:00
Morten Lautrup
929f475354 Make buttons undraggable 2019-05-29 13:02:37 +09:00
AWolf81
1afa02bbb3 fix local link detection by creating a link tag to parse the input string 2019-05-29 12:20:49 +09:00
AWolf81
3c39dc3cec change redirecting to connected-react-router 2019-05-29 12:20:49 +09:00
AWolf81
7e8f46c4f3 remove commented imports 2019-05-29 12:20:49 +09:00
AWolf81
333b0584a4 address review comments - add production/dev main.html & remove comments 2019-05-29 12:20:49 +09:00
AWolf81
f7a648903e fix auto-scroll 2019-05-29 12:20:49 +09:00
AWolf81
6ec687ef15 add React & Redux devtools 2019-05-29 12:20:49 +09:00
AWolf81
b6212f4bfe Update dependencies & change to React-router v5 2019-05-29 12:20:49 +09:00
AWolf81
9794149fae Merge branch 'master' into fix-search-issue-unicode 2019-05-28 20:18:13 +02:00
AWolf81
edc9d8bd4d use composition input 2019-05-28 20:16:02 +02:00
Junyoung Choi
76335f78ac v0.11.17 2019-05-26 18:49:44 +09:00
Junyoung Choi
56192f0758 Merge pull request #3034 from AWolf81/enable-markdownlint-option
Enable Markdown lint option
2019-05-26 18:38:30 +09:00
Junyoung Choi
63eb8584e7 Hide markdown lint settings when markdown lint is disabled 2019-05-26 18:32:24 +09:00
Zubata SMRTKA
aa4d06fb1e formatting 2019-05-26 18:22:04 +09:00
AWolf81
c70cca2776 remove console.log 2019-05-26 10:07:43 +02:00
AWolf81
1a38771f1a remove console.logs & improve error handling 2019-05-26 09:57:40 +02:00
AWolf81
25728e51d2 simplified search input & fixed focus loss issue 2019-05-25 21:01:51 +02:00
AWolf81
02576c48b6 move enableMarkdownLint checkbox to customMarkdownLintConfig area (like at allow custom css) 2019-05-25 07:31:21 +02:00
AWolf81
4263309d89 Merge branch 'add-markdownlint-rules-form' of github.com:roottool/Boostnote into enable-markdownlint-option 2019-05-25 07:21:26 +02:00
AWolf81
b68b367b3c Toggle linting gutter with document.querySelector and style.display 2019-05-25 07:16:03 +02:00
tool root
2cffb50884 Fix: Changed height of form 2019-05-25 08:16:25 +09:00
tool root
3b473a5f7a Fix: Changed the function name 2019-05-25 08:04:40 +09:00
tool root
8b82c448af Fix: Changed the function name 2019-05-25 07:58:43 +09:00
AWolf81
270a015514 WIP: Add MarkdownLint enable setting. Gutter toggle not working as expected. 2019-05-24 19:06:15 +02:00
Junyoung Choi
f02125e411 Update FUNDING.yml 2019-05-24 10:10:29 +09:00
KZ
b0f2694745 Create FUNDING.yml 2019-05-24 10:09:49 +09:00
roottool
2497bdb124 Fix: Removed changes to debug the app 2019-05-22 22:45:09 +09:00
Junyoung Choi
d5d564f789 v0.11.16 2019-05-22 15:20:34 +09:00
Junyoung Choi
49e48f7adc Update yarn.lock 2019-05-22 12:23:28 +09:00
amedora
c5484fbb88 Merge branch 'master' into filter-tags-and-folders 2019-05-21 09:50:39 +09:00
amedora
0d4b6252e8 add locales 'filter tags/folders...' 2019-05-21 09:45:52 +09:00
amedora
7529feb4a5 add placeholder to show 'filter tags/folders...' 2019-05-21 09:26:59 +09:00
Darío Hereñú
079aaec21e Fixed typo on string 137
* plus some translations added
2019-05-13 14:50:53 +09:00
AWolf81
1601292db7 add react hooks section 2019-05-13 14:05:57 +09:00
roottool
c82dbddc74 Fix markdownlint result desplay works properly 2019-05-12 13:13:32 +09:00
AWolf81
5f96e314fd add tag link handling with :tag:#tag syntax 2019-05-11 09:30:10 +02:00
David Dreher
9a6ee9d2ef change return handling of sortByAlphabetical 2019-05-09 15:45:54 +09:00
David Dreher
2cbbe7aeda Check if the float is quals (abs value is greather 0.01) and return the sub value when not. 2019-05-09 15:45:54 +09:00
David Dreher
19fc1fd674 rename const 2019-05-09 15:45:54 +09:00
David Dreher
5b63bedc0d fix issue #2894: sort alphabetical will now parse float values starting at all titles and compare these. 2019-05-09 15:45:54 +09:00
amedora
12229a1719 allow to expand snippets following $ character 2019-05-09 15:41:33 +09:00
amedora
a7f802db7c Merge branch 'master' into filter-tags-and-folders 2019-05-09 09:23:17 +09:00
roottool
0d6c952721 Added a newline 2019-05-09 05:30:46 +09:00
roottool
c42eb41fb3 Fix: Fixed that default rule was shifted by indent 2019-05-09 05:29:43 +09:00
roottool
2da4f1df32 Fix: Rewrote the code to inline 2019-05-09 04:11:05 +09:00
roottool
69a62d1b73 Fix: Changed variable name 2019-05-09 04:06:27 +09:00
roottool
61e054024b Fix: Removed unnecessary css code 2019-05-07 04:44:23 +09:00
roottool
0a5c4c092a Fix: Improved for the app not to need to reload 2019-05-07 04:22:03 +09:00
roottool
f1597f8e84 Fix: Poped up the lint tooptip 2019-05-07 03:26:47 +09:00
roottool
f3ca893aea Ajusted markdownlint config editor to code editor 2019-05-07 02:30:41 +09:00
roottool
ecfeedeff3 Fix: Removed unnecessary caution 2019-05-07 00:44:27 +09:00
roottool
a162bab591 Fix: Improved for the app not to need to reload 2019-05-07 00:42:55 +09:00
roottool
79a29c3461 Merge branch 'master' into add-markdownlint-rules-form 2019-05-06 20:53:59 +09:00
Junyoung Choi
377901606e Merge pull request #2846 from roottool/IntroduceMarkdownLint#864
Introduce markdownlint #864
2019-05-06 18:31:01 +09:00
Nguyễn Việt Hưng
8cd24a5734 fixed open empty link 2 2019-05-06 18:30:20 +09:00
Baptiste Augrain
ba913b77e7 use constants and fix cursor color 2019-05-06 18:28:57 +09:00
Baptiste Augrain
6012fc929e add Nord theme to CodeMirror 2019-05-06 18:28:57 +09:00
roottool
33d1700548 Change styleName of caution 2019-04-29 10:01:55 +09:00
roottool
8b3beb45c6 Added a caution of the custom markdownlint rules 2019-04-29 09:17:25 +09:00
roottool
4ba4e68833 Added markdownlint rules form 2019-04-29 08:54:28 +09:00
roottool
11bed72bed Introduced jsonlint-mod 2019-04-29 08:49:19 +09:00
roottool
7fb22f3f07 Translated from English to Japanese 2019-04-29 03:24:16 +09:00
roottool
788900e31a Added markdownlint rules form 2019-04-29 03:23:25 +09:00
roottool
03b1adca12 Merge master branch into this branch 2019-04-29 01:57:16 +09:00
Charles Wang
aecf2eb08d Bug fix check in. 2019-04-28 12:31:24 -04:00
Christian Dimas
895aee3e6c add default value to custom CSS editor 2019-04-28 12:30:38 -04:00
David Dreher
c667e1465d After select an other note now scroll to top 2019-04-28 12:30:13 -04:00
David Dreher
3c9d614e1f Change the save botton at the about tab to the same style as the 'See IssueHunt' button at the crowdfunding tab 2019-04-28 12:29:33 -04:00
David Dreher
971e0cb61e change missing font color for the themes at the preferences headlines 2019-04-28 12:29:33 -04:00
David Dreher
67d78f6603 Replace the width calc with a fixed value 2019-04-28 12:29:33 -04:00
David Dreher
ed0b370d49 Change paragraph headlines at the crowdfunding tab 2019-04-28 12:29:33 -04:00
David Dreher
e5b8f4d372 Use same styles for all preferences tabs 2019-04-28 12:29:33 -04:00
Junyoung Choi
2999e490ad Merge pull request #2509 from lvarado/mojave-dark-mode
enable dark mode support for macOS mojave
2019-04-28 12:26:28 -04:00
Nguyễn Việt Hưng
156a2c6521 added check empty for safety reason 2019-04-23 22:58:38 -04:00
Nguyễn Việt Hưng
48bb9d3242 fixed empty link point to main file 2019-04-23 22:58:38 -04:00
steve-o
b2ee4f0c66 update electron-packager to 12.2.0 2019-04-22 10:02:19 -05:00
steve-o
9b68f34a31 remove hashes from lockfile (except those present in master branch) 2019-04-22 10:00:46 -05:00
alvarado
f8944eb8b3 Merge branch 'master' into mojave-dark-mode 2019-04-22 09:45:16 -05:00
David Dreher
3634194e4d Fix for #2836, Initialize storage ignores now tags from skipped notes 2019-04-15 10:57:20 +09:00
Abner Soares Alves Junior
038b87cf70 Fix test moving from Ava to JEST 2019-04-15 10:56:35 +09:00
Abner Soares Alves Junior
5ca6bbea11 Fix some linter issues 2019-04-15 10:56:35 +09:00
Abner Soares Alves Junior
7b9b4d56a7 Fix the position of title mark when replacing 2019-04-15 10:56:35 +09:00
Abner Soares Alves Junior
d81e69bf00 Add parse and fetch pasted markdown titles with url 2019-04-15 10:56:35 +09:00
linxiuda
5e134f990e fix newNoteModal create note twice after double click quickly 2019-04-15 10:55:26 +09:00
David Dreher
1675e04f90 Fix for #2782: CodeEditor.handleChange will now update the toc if available when a headline was modified 2019-04-15 10:54:45 +09:00
Todt
0a72acd899 Modified style 2019-04-15 10:44:16 +09:00
amedora
aa6c9680da Increase arrow size on sidebar 2019-04-15 10:43:52 +09:00
milotodt
d41663143b Repush due to pipeline issues 2019-04-15 10:43:10 +09:00
milotodt
9db363865c Slight refactor 2019-04-15 10:43:10 +09:00
milotodt
9b03a32eec removed uneeded line of code 2019-04-15 10:43:10 +09:00
milotodt
eeec1b12b5 adjusted handleAttachmentDrop 2019-04-15 10:43:10 +09:00
Milo Todt
e655c2078b Update MarkdownPreview.js 2019-04-12 18:02:23 +09:00
Milo Todt
3426191e59 Update MarkdownPreview.js 2019-04-12 18:02:23 +09:00
Todt
b1c77ae59c Changes and refactor of HandleMouseDown 2019-04-12 18:02:23 +09:00
Jamie Luckett
d17ff4afba Update productDescription
Fixed grammatical error
2019-04-12 18:01:40 +09:00
Garfield Lee
0131bbbbed Update all locales copyright year (#2881)
* Update all locales copyright year

* Fix js-sequence-diagrams
2019-04-12 18:01:05 +09:00
Boris Pruessmann
7f55fc4b56 Removed erroneous parameter 2019-04-12 14:33:38 +09:00
amedora
aea9673b78 Add "Ctrl + /" and "Shift + Ctrl + /" to insert today's date and time (#1765) 2019-04-12 14:32:09 +09:00
anasasilva
89e9a84ea6 fixing package.json 2019-04-08 21:10:42 +09:00
anasasilva
fba9afd6f5 Shorter code 2019-04-08 21:10:42 +09:00
anasasilva
c474d972cb Supports relative and absolute path 2019-04-08 21:10:42 +09:00
anasasilva
3d6f670e8d regex fixed again 2019-04-08 21:10:42 +09:00
anasasilva
9d47f319a0 absolute path 2019-04-08 21:10:42 +09:00
anasasilva
7106f042da Regex fixed 2019-04-08 21:10:42 +09:00
anasasilva
ea6e56842f Error message when the image path is wrong 2019-04-08 21:10:42 +09:00
anasasilva
65926fea73 fixing identation and regex 2019-04-08 21:10:42 +09:00
anasasilva
85cb94d99d importAttachments 2019-04-08 21:10:42 +09:00
amedora
db78f1b91e fix search input visuality for Monokai 2019-04-03 16:14:52 +09:00
amedora
04e0523cac fix .extra-buttons vertical position 2019-04-03 14:50:54 +09:00
HarlanLuo
885b9d2c26 remove unused ref 2019-04-03 14:40:14 +09:00
HarlanLuo
51aff20d65 improve style of sidenav 2019-04-03 14:40:13 +09:00
HarlanLuo
8dc5214c9e new feature: filter tags and folder list 2019-04-03 14:40:12 +09:00
Ryo Shibayama
1be208d96b Fix locale json format, add some translations 2019-04-02 14:09:39 +09:00
Max Schmitt
22d494d3f1 README: removed mobile app mention (#2828) 2019-03-25 11:10:09 +09:00
Nguyen Viet Hung
5b99132f66 Adjust find function
Co-Authored-By: dredav <dredav@users.noreply.github.com>
2019-03-25 11:06:13 +09:00
David Dreher
91d04b99d1 change filter function to find, find will match the first note with requested key 2019-03-25 11:06:13 +09:00
David Dreher
70e57cf738 jumpNoteByHashHandler will now try to find the location for the note that will be selected 2019-03-25 11:06:13 +09:00
Ryo Shibayama
2f00cec52b Upgrade Travis CI node and npm versions 2019-03-25 11:04:05 +09:00
Junyoung Choi
fee966996f Fix wrong binding (#2940) 2019-03-22 15:33:29 +09:00
Nguyen Viet Hung
bff081a263 fixed copy button (#2914) 2019-03-21 01:27:51 +09:00
Nguyen Viet Hung
b5b56f7af1 Refactor code editor by moving the expand snippet out to a separate file (#2864)
* refactored CodeEditor by moving Snippet out

* fixed typo
2019-03-21 01:26:53 +09:00
Junyoung Choi
3f7f0e677d Merge pull request #2819 from ZeroX-DG/feature/edit-from-preview
[Feature] Edit from preview
2019-03-21 01:23:11 +09:00
Junyoung Choi
2b29d96d61 Merge pull request #2678 from AgentEpsilon/export-pdf
Export a Markdown note as PDF
2019-03-21 01:20:12 +09:00
Junyoung Choi
9f13645127 Merge pull request #2545 from Danmou/patch-1
Add ~ and _ to autoclosing brackets
2019-03-21 01:19:25 +09:00
Junyoung Choi
bbbbe9a121 Merge pull request #2493 from opw0011/zh-locale
Add missing Chinese translation in preference page
2019-03-21 01:18:36 +09:00
Junyoung Choi
46ecf0af88 Merge pull request #2868 from dredav/issue-2859
[Fix] Cloning a note will now clone more note properties
2019-03-21 01:18:09 +09:00
Callum booth
93f0d3c1cf fixes #2903 - Rearrange layout of columns 2019-03-19 20:39:34 +00:00
Evan Miller
2a0906d88e Rehid printout BrowserWindow 2019-03-18 10:02:20 -04:00
Luis Reinoso
8ec7d19f30 Remove unnecessary console.log 2019-03-12 22:38:10 -05:00
Luis Reinoso
a0f5a06c73 Add send wakatimeHeartBeat on constructor CodeEditor to fix when change from Markdown to Snippet #2810 2019-03-12 22:14:10 -05:00
Luis Reinoso
39a98e795f Add preferences plugins wakatime key #2810 2019-03-12 22:05:30 -05:00
Luis Reinoso
57705cf41b Add less WakatimeHeartBeat requests #2810 2019-03-12 20:40:27 -05:00
Luis Reinoso
052c70bb38 Add support to snippetNote #2810 2019-03-12 20:38:55 -05:00
Luis Reinoso
6dc88262c9 Add wakatime-plugin #2810 2019-03-12 20:02:24 -05:00
Daniel Mouritzen
116244384e Add ~ and _ to autoclosing brackets 2019-03-04 08:37:29 +01:00
Daniel Mouritzen
29775040d1 Merge branch 'master' into patch-1 2019-03-04 08:33:29 +01:00
Evan Miller
177888b159 disable webSecurity to render files to pdf 2019-02-11 11:15:08 -05:00
Evan Miller
bc24acd057 add targetDir parameter to outputFormatter 2019-02-11 11:15:08 -05:00
Evan Miller
4d727b0af7 destroy window after printing 2019-02-11 11:15:08 -05:00
Baptiste Augrain
1cdac943ba adding Nord theme and streamlining UI theming 2019-02-11 00:45:34 +01:00
Junyoung Choi
78c00b1722 v0.11.15 2019-02-08 20:33:20 +09:00
Junyoung Choi
2ff655d2dc Check update if the app is packaged 2019-02-08 19:30:46 +09:00
Junyoung Choi
e53717cd87 v0.11.14 2019-02-08 16:46:29 +09:00
David Dreher
d144a5884a fix for issue #2859: Cloning a note will now also copy the properties description, snippets, tags and isStarred 2019-02-06 22:11:35 +01:00
Junyoung Choi
8b8d915ab7 Merge pull request #2642 from daiyam/drop-browser-image
handle all dropped images
2019-02-05 17:55:18 +09:00
Junyoung Choi
54de57ee7b Merge pull request #2863 from zzdjk6/master
FIX #2853 Allow "#" in title
2019-02-05 11:25:21 +09:00
Baptiste Augrain
e0d9cf7f5c fix dropping image from Firefox on Linux 2019-02-04 18:22:10 +01:00
Baptiste Augrain
10ea5d00eb avoid converting SVG 2019-02-04 14:09:11 +01:00
Baptiste Augrain
de76f55fe2 fix gif 2019-02-04 13:54:45 +01:00
Baptiste Augrain
cd301d514c Merge branch 'master' into drop-browser-image 2019-02-04 13:36:32 +01:00
Shenghan Chen
0f232b3d86 FIX #2853 Allow "#" in title
- Only strip the leading # in the title
- Make the finding title logic more straightforward
- Add unit test
2019-02-04 20:07:33 +13:00
Junyoung Choi
53ff693e95 Merge pull request #2852 from Aaron-Bird/bug-gif
[Fix] GIFs don't animate
2019-02-04 12:25:28 +09:00
roottool
c15cc2ecfc added to check Markdown Note is opening 2019-02-02 14:59:04 +09:00
roottool
59b441e524 removed markdown mode 2019-02-02 03:07:59 +09:00
Baptiste Augrain
215484c19a add tests 2019-02-01 16:27:47 +01:00
Junyoung Choi
885f656d34 Merge pull request #2856 from K-Sato1995/fix/grammer-error#2844
[Fix  #2844] Grammar Error In Notes
2019-02-01 16:44:12 +09:00
Baptiste Augrain
851d3ba159 fix image path when the dropped image's url contains a query 2019-01-31 23:26:31 +01:00
Katsuki
62ab444b29 Fix grammer error 2019-01-31 19:33:43 +09:00
roottool
b84ebfa7b3 Merge branch 'master' into IntroduceMarkdownLint#864 2019-01-31 12:44:07 +09:00
Junyoung Choi
f1b929c13b Merge pull request #2848 from daiyam/fix-gallery
fix image path due to bad regex
2019-01-31 10:58:20 +09:00
Aaron-Bird
806139091c fix: GIFs don't animate 2019-01-31 01:08:07 +08:00
Junyoung Choi
6960c8b2d6 Merge pull request #2835 from ehhc/fix_broken_attachments
Strange url-handling reverted + tests modified so that they might fin…
2019-01-30 10:32:20 +09:00
Baptiste Augrain
b1c6c0442f fix guardrails errors 2019-01-29 16:49:51 +01:00
Baptiste Augrain
a85a27f225 - fix bad regex
- improve test
- fix missing 'e' in some functions name
2019-01-29 16:05:30 +01:00
roottool
d5629651d1 modified from require to import 2019-01-29 21:26:57 +09:00
roottool
afbc8eec75 changed variable name 2019-01-29 21:00:47 +09:00
roottool
3a96164c27 display lint result on CodeEditor #864 2019-01-29 20:42:24 +09:00
roottool
e393b5a2ea Add markdownlint #864 2019-01-29 20:41:29 +09:00
Junyoung Choi
950d31ada8 Merge pull request #2809 from roottool/zoom-in-and-out-image#1448
Zoom in and out image #1448
2019-01-28 11:39:14 +09:00
Junyoung Choi
543c31cec6 Merge pull request #2831 from roottool/change_default_editor_font#1995
Change default editor font #1995
2019-01-28 10:02:19 +09:00
Benny O
5920b3515e Merge branch 'master' into zh-locale 2019-01-27 23:04:27 +08:00
roottool
5e87ec2627 Specified MarkdownPreview class 2019-01-27 22:26:31 +09:00
ehhc
7165c4550b possibly fix for the broken test... 2019-01-26 18:16:39 +01:00
ehhc
472496d59c possibly fix for the broken test... 2019-01-26 17:53:31 +01:00
ehhc
127da40256 possibly fix for the broken test... 2019-01-26 17:37:04 +01:00
ehhc
a113b99de0 Strange url-handling reverted + tests modified so that they might find that issue in the future + test modified so that they both contain tests for posix and windows path separator -> fixes #2834 2019-01-26 17:21:53 +01:00
roottool
1683d63f33 Modified indent 2019-01-26 01:03:37 +09:00
roottool
4cce52f9ce changed default editor font #1995 2019-01-25 23:17:05 +09:00
roottool
33fb03066e Reduced the count of calculation 2019-01-25 20:06:43 +09:00
Nguyễn Việt Hưng
5d38937f34 scroll selected line to middle of the editor 2019-01-23 00:20:50 +07:00
Nguyễn Việt Hưng
a2e050b8c5 added jump to line on click preview 2019-01-19 17:03:59 +07:00
Nguyễn Việt Hưng
a7ad56be98 Merge branch 'master' of https://github.com/BoostIO/Boostnote 2019-01-19 16:28:36 +07:00
roottool
294bf742cd change variable name #1448 2019-01-16 23:14:21 +09:00
roottool
ea5970ab1c change a magnification by image size #1448 2019-01-16 22:47:42 +09:00
roottool
72df418953 DRY my code #1448 2019-01-16 18:10:28 +09:00
roottool
f566b567be add zoom-in and zoom-out action of images #1448 2019-01-16 01:54:54 +09:00
Nguyễn Việt Hưng
c6c5e33ee6 Merge branch 'master' of https://github.com/BoostIO/Boostnote 2019-01-11 10:36:59 +07:00
Nguyễn Việt Hưng
9ad0f58095 Merge branch 'master' of https://github.com/BoostIO/Boostnote 2019-01-06 09:35:01 +07:00
Baptiste Augrain
9d43e34cfa Merge branch 'master' into export-yfm 2018-12-28 00:02:38 +01:00
Baptiste Augrain
12f9b9342d Merge branch 'master' into fix-autocomplete-codeblock 2018-12-27 22:57:05 +01:00
Baptiste Augrain
e76bc72667 - data-line attributes might not be directly under the body
- support checkbox preference `When scrolling, synchronize preview with editor`
2018-12-24 11:44:02 +01:00
Baptiste Augrain
9310e5e86c Merge branch 'master' into fix-scroll 2018-12-24 10:33:47 +01:00
Baptiste Augrain
a58b6f1b49 Merge branch 'master' into fix-mermaid-height 2018-12-24 10:06:15 +01:00
Baptiste Augrain
fa157f6f76 fix lint error 2018-12-15 15:57:37 +01:00
Baptiste Augrain
d6a54b8a26 export diagrams 2018-12-15 15:53:49 +01:00
Baptiste Augrain
9813412c8e add export menu in note list's context menu 2018-12-15 14:55:13 +01:00
Baptiste Augrain
d76b7235db export styles of code blocks 2018-12-15 12:42:39 +01:00
Baptiste Augrain
418a789568 fix export note in with split editor 2018-12-15 10:22:02 +01:00
Nguyễn Việt Hưng
b5763ec89d Merge branch 'master' of https://github.com/BoostIO/Boostnote 2018-12-15 13:40:03 +07:00
gregueiras
a19ff6762e Unit tests added 2018-12-13 18:50:02 +00:00
Baptiste Augrain
2d941c3ea3 Merge branch 'master' into export-yfm 2018-12-13 14:35:25 +01:00
Gonçalo Santos
7034e7b620 Delete Slider.styl
I submitted an extra file, it was not necessary
2018-12-12 11:37:59 +00:00
Nguyễn Việt Hưng
a095e8b25c Merge branch 'master' of https://github.com/BoostIO/Boostnote 2018-12-10 11:56:13 +07:00
Evan Miller
b546b9cbe7 InfoPanel automatically adjusts its width 2018-12-03 17:04:04 -05:00
Evan Miller
ce9f76fa63 fixed code style error 2018-12-03 16:55:05 -05:00
Evan Miller
dceed7d84d Fixes exportFolder by making it actually wait for each exportNote 2018-12-03 16:51:59 -05:00
Evan Miller
660a27850f Generate PDF through an Electron BrowserWindow 2018-12-03 16:39:41 -05:00
Evan Miller
0a7fd0288c Use promises for outputFormatter 2018-12-03 16:14:28 -05:00
Evan Miller
33d0a9d3b3 extract html contentformatter 2018-12-03 15:49:41 -05:00
Evan Miller
c1deeaf5f7 Added PDF error to SnippetNoteDetail 2018-12-03 14:40:52 -05:00
Evan Miller
7c1cd50def Add structure for exporting PDFs 2018-12-03 13:12:22 -05:00
Nguyễn Việt Hưng
2ccb541b7f Merge branch 'master' of https://github.com/BoostIO/Boostnote 2018-11-30 10:04:25 +07:00
Nguyễn Việt Hưng
e4a6ff4c70 Merge branch 'master' of https://github.com/ZeroX-DG/Boostnote 2018-11-30 10:04:16 +07:00
gregueiras
cd53a65c14 Code Style Improvements 2018-11-29 13:58:15 +00:00
gregueiras
8b54f5aa69 Removed colons and semi colons 2018-11-29 12:25:14 +00:00
gregueiras
ceed178061 Inverted thumb order 2018-11-29 12:12:14 +00:00
gregueiras
33662974bf Fixed default theme thumb background 2018-11-29 12:05:14 +00:00
gregueiras
36b97fc6a2 Interface Improved 2018-11-29 11:58:36 +00:00
Baptiste Augrain
900f20f164 handle all dropped images 2018-11-28 15:58:58 +01:00
Baptiste Augrain
c2e4bae9dd fix regex to correctly match the src attribute when there is a data-src attribute 2018-11-28 15:00:29 +01:00
Baptiste Augrain
540c608cc6 Merge branch 'master' into fix-scroll 2018-11-28 14:01:18 +01:00
Gonçalo Santos
0f8c627474 Fixed checkbox 2018-11-28 12:09:42 +00:00
gregueiras
f38fef23a0 ThemeManager Created 2018-11-28 12:04:33 +00:00
gregueiras
8be0ea64a5 Scheduled Theme default configuration 2018-11-27 16:41:02 +00:00
gregueiras
1419c71ef5 Border added 2018-11-27 10:40:42 +00:00
gregueiras
e13742445e Format 2018-11-26 22:01:43 +00:00
gregueiras
1d21bb1ea3 Values are now saved 2018-11-26 22:00:02 +00:00
gregueiras
aa38b1f859 Multi tab WIP 2018-11-26 20:11:11 +00:00
Baptiste Augrain
2908884202 drag and drop image from browser 2018-11-25 15:56:11 +01:00
Baptiste Augrain
e723d4cd59 add tests 2018-11-18 19:10:48 +01:00
Baptiste Augrain
9e770ef357 fix bad conditions 2018-11-15 23:52:42 +01:00
Baptiste Augrain
c796b3b30e add YAML front matter when exporting 2018-11-15 22:48:14 +01:00
Baptiste Augrain
168fe212f5 add preference tab 2018-11-15 03:15:11 +01:00
Ivan Chen
0904c62a2d Update locales/zh-CN.json
Co-Authored-By: opw0011 <i.mpeople@gmail.com>
2018-11-11 11:19:38 +08:00
Ivan Chen
817b74cc7f Update locales/zh-TW.json
Co-Authored-By: opw0011 <i.mpeople@gmail.com>
2018-11-11 11:18:57 +08:00
Baptiste Augrain
87515dbd3f separate autocomplete configuration between markdown and within code blocks 2018-11-10 12:20:51 +01:00
Baptiste Augrain
b6b29e02f3 fix lint error 2018-11-08 14:41:25 +01:00
Baptiste Augrain
2fc37d54f2 fix height of mermaid's diagrams 2018-11-08 13:19:46 +01:00
Daniel Mouritzen
d6fe0df24f Add ~ and _ to autoclosing brackets 2018-10-27 18:22:03 +02:00
Daniel Mouritzen
7fb1a06e1e Add ~ and _ to autoclosing brackets 2018-10-27 18:18:51 +02:00
steve-o
b004247478 update electron-packager; enable dark mode support for macOS mojave 2018-10-19 12:16:27 -05:00
Nguyễn Việt Hưng
43bd0b0dd5 Merge branch 'master' of https://github.com/BoostIO/Boostnote 2018-10-18 12:07:40 +07:00
Benny O
28007a33a0 Add missing translation for zh-cn 2018-10-13 19:19:48 +08:00
Benny O
4242e0d329 Add missing translation for zh-tw 2018-10-13 19:18:47 +08:00
Baptiste Augrain
696c2f29b5 implements better positioning between the editor and the preview in the SplitEditor (particularly with lot of images) 2018-09-17 16:59:27 +02:00
Storm Burpee
18aae8cf7b getting very close 2018-05-28 22:12:04 +09:30
Storm Burpee
4a9bc69ac2 starting to write a test 2018-05-28 19:45:09 +09:30
Storm Burpee
d97e62f864 Import note from url with markdown 2018-05-26 17:00:12 +09:30
321 changed files with 23600 additions and 13201 deletions

View File

@@ -7,7 +7,7 @@
"test": {
"presets": ["env" ,"react", "es2015"],
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "${PWD}/webpack.config.js" } ]
[ "babel-plugin-webpack-alias", { "config": "<rootDir>/webpack.config.js" } ]
]
}
}

View File

@@ -1,6 +1,6 @@
{
"extends": ["standard", "standard-jsx", "plugin:react/recommended"],
"plugins": ["react"],
"extends": ["standard", "standard-jsx", "plugin:react/recommended", "prettier"],
"plugins": ["react", "prettier"],
"rules": {
"no-useless-escape": 0,
"prefer-const": ["warn", {
@@ -13,12 +13,15 @@
"react/no-string-refs": 0,
"react/no-find-dom-node": "warn",
"react/no-render-return-value": "warn",
"react/no-deprecated": "warn"
"react/no-deprecated": "warn",
"prettier/prettier": ["error"]
},
"globals": {
"FileReader": true,
"localStorage": true,
"fetch": true
"fetch": true,
"Image": true,
"MutationObserver": true
},
"env": {
"jest": true

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
issuehunt: BoostIo/Boostnote

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@ node_modules/*
*.log
.idea
.vscode
package-lock.json
config.json

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"singleQuote": true,
"semi": false,
"jsxSingleQuote": true
}

View File

@@ -1,10 +1,9 @@
language: node_js
node_js:
- 7
- 8
script:
- npm run lint && npm run test
- yarn jest
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@5.2 && grunt pre-build; fi'
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@6.4 && grunt pre-build; fi'
after_success:
- openssl aes-256-cbc -K $encrypted_440d7f9a3c38_key -iv $encrypted_440d7f9a3c38_iv
-in .snapcraft/travis_snapcraft.cfg -out .snapcraft/snapcraft.cfg -d

View File

@@ -5,19 +5,19 @@ Let us know what is currently happening.
Please include some **screenshots** with the **developer tools** open (console tab) when you report a bug.
If your issue is regarding Boostnote mobile, please open an issue in the Boostnote Mobile repo 👉 https://github.com/BoostIO/boostnote-mobile.
If your issue is regarding the new Boost Note.next, please open an issue in the new repo 👉 https://github.com/BoostIO/BoostNote.next/issues.
-->
# Expected behavior
<!--
Let us know what you think should happen!
Let us know what you think should happen.
-->
# Steps to reproduce
<!--
Please be thorough, issues we can reproduce are easier to fix!
Please be thorough, issues we can reproduce are easier to fix.
-->
1.
@@ -26,8 +26,8 @@ Please be thorough, issues we can reproduce are easier to fix!
# Environment
- Version :
- OS Version and name :
- Boostnote version: <!-- 0.x.x -->
- OS version and name: <!-- Windows 10 / Ubuntu 18.04 / etc -->
<!--
Love Boostnote? Please consider supporting us on IssueHunt:

View File

@@ -3,13 +3,16 @@ Before submitting this PR, please make sure that:
- You have read and understand the contributing.md
- You have checked docs/code_style.md for information on code style
-->
## Description
<!--
Tell us what your PR does.
Please attach a screenshot/ video/gif image describing your PR if possible.
-->
## Issue fixed
<!--
Please list out all issue fixed with this PR here.
-->
@@ -20,6 +23,7 @@ your PR will be reviewed faster if we know exactly what it does.
Change :white_circle: to :radio_button: in all the options that apply
-->
## Type of changes
- :white_circle: Bug fix (Change that fixed an issue)
@@ -34,3 +38,5 @@ Change :white_circle: to :radio_button: in all the options that apply
- :white_circle: I have written test for my code and it has been tested
- :white_circle: All existing tests have been passed
- :white_circle: I have attached a screenshot/video to visualize my change if possible
- :white_circle: This PR will modify the UI or affects the UX
- :white_circle: This PR will add/update/delete a keybinding

File diff suppressed because it is too large Load Diff

View File

@@ -3,4 +3,3 @@
.spellcheck-select
border: none
text-decoration underline wavy red

View File

@@ -7,7 +7,7 @@ import styles from './ColorPicker.styl'
const componentHeight = 330
class ColorPicker extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -18,21 +18,21 @@ class ColorPicker extends React.Component {
this.handleConfirm = this.handleConfirm.bind(this)
}
componentWillReceiveProps (nextProps) {
componentWillReceiveProps(nextProps) {
this.onColorChange(nextProps.color)
}
onColorChange (color) {
onColorChange(color) {
this.setState({
color
})
}
handleConfirm () {
handleConfirm() {
this.props.onConfirm(this.state.color)
}
render () {
render() {
const { onReset, onCancel, targetRect } = this.props
const { color } = this.state
@@ -44,13 +44,22 @@ class ColorPicker extends React.Component {
}
return (
<div styleName='colorPicker' style={{top: `${alignY}px`, left: `${alignX}px`}}>
<div
styleName='colorPicker'
style={{ top: `${alignY}px`, left: `${alignX}px` }}
>
<div styleName='cover' onClick={onCancel} />
<SketchPicker color={color} onChange={this.onColorChange} />
<div styleName='footer'>
<button styleName='btn-reset' onClick={onReset}>Reset</button>
<button styleName='btn-cancel' onClick={onCancel}>Cancel</button>
<button styleName='btn-confirm' onClick={this.handleConfirm}>Confirm</button>
<button styleName='btn-reset' onClick={onReset}>
Reset
</button>
<button styleName='btn-cancel' onClick={onCancel}>
Cancel
</button>
<button styleName='btn-confirm' onClick={this.handleConfirm}>
Confirm
</button>
</div>
</div>
)

View File

@@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
@@ -10,7 +11,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
class MarkdownEditor extends React.Component {
constructor (props) {
constructor(props) {
super(props)
// char codes for ctrl + w
@@ -20,195 +21,246 @@ class MarkdownEditor extends React.Component {
this.supportMdSelectionBold = [16, 17, 186]
this.state = {
status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'CODE',
status:
props.config.editor.switchPreview === 'RIGHTCLICK'
? props.config.editor.delfaultStatus
: 'CODE',
renderValue: props.value,
keyPressed: new Set(),
isLocked: props.isLocked
}
this.lockEditorCode = () => this.handleLockEditor()
this.lockEditorCode = this.handleLockEditor.bind(this)
this.focusEditor = this.focusEditor.bind(this)
this.previewRef = React.createRef()
}
componentDidMount () {
componentDidMount() {
this.value = this.refs.code.value
eventEmitter.on('editor:lock', this.lockEditorCode)
eventEmitter.on('editor:focus', this.focusEditor)
}
componentDidUpdate () {
componentDidUpdate() {
this.value = this.refs.code.value
}
componentWillReceiveProps (props) {
UNSAFE_componentWillReceiveProps(props) {
if (props.value !== this.props.value) {
this.queueRendering(props.value)
}
}
componentWillUnmount () {
componentWillUnmount() {
this.cancelQueue()
eventEmitter.off('editor:lock', this.lockEditorCode)
eventEmitter.off('editor:focus', this.focusEditor)
}
queueRendering (value) {
focusEditor() {
this.setState(
{
status: 'CODE'
},
() => {
if (this.refs.code == null) {
return
}
this.refs.code.focus()
}
)
}
queueRendering(value) {
clearTimeout(this.renderTimer)
this.renderTimer = setTimeout(() => {
this.renderPreview(value)
}, 500)
}
cancelQueue () {
cancelQueue() {
clearTimeout(this.renderTimer)
}
renderPreview (value) {
renderPreview(value) {
this.setState({
renderValue: value
})
}
setValue (value) {
setValue(value) {
this.refs.code.setValue(value)
}
handleChange (e) {
handleChange(e) {
this.value = this.refs.code.value
this.props.onChange(e)
}
handleContextMenu (e) {
handleContextMenu(e) {
if (this.state.isLocked) return
const { config } = this.props
if (config.editor.switchPreview === 'RIGHTCLICK') {
const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW'
this.setState({
status: newStatus
}, () => {
if (newStatus === 'CODE') {
this.refs.code.focus()
} else {
this.refs.preview.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
this.setState(
{
status: newStatus
},
() => {
if (newStatus === 'CODE') {
this.refs.code.focus()
} else {
this.previewRef.current.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
const newConfig = Object.assign({}, config)
newConfig.editor.delfaultStatus = newStatus
ConfigManager.set(newConfig)
})
const newConfig = Object.assign({}, config)
newConfig.editor.delfaultStatus = newStatus
ConfigManager.set(newConfig)
}
)
}
}
handleBlur (e) {
handleBlur(e) {
if (this.state.isLocked) return
this.setState({ keyPressed: new Set() })
const { config } = this.props
if (config.editor.switchPreview === 'BLUR' ||
(config.editor.switchPreview === 'DBL_CLICK' && this.state.status === 'CODE')
if (
config.editor.switchPreview === 'BLUR' ||
(config.editor.switchPreview === 'DBL_CLICK' &&
this.state.status === 'CODE')
) {
const cursorPosition = this.refs.code.editor.getCursor()
this.setState({
status: 'PREVIEW'
}, () => {
this.refs.preview.focus()
this.refs.preview.scrollTo(cursorPosition.line)
})
this.setState(
{
status: 'PREVIEW'
},
() => {
this.previewRef.current.focus()
this.previewRef.current.scrollToLine(cursorPosition.line)
}
)
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
}
handleDoubleClick (e) {
handleDoubleClick(e) {
if (this.state.isLocked) return
this.setState({keyPressed: new Set()})
this.setState({ keyPressed: new Set() })
const { config } = this.props
if (config.editor.switchPreview === 'DBL_CLICK') {
this.setState({
status: 'CODE'
}, () => {
this.refs.code.focus()
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
})
this.setState(
{
status: 'CODE'
},
() => {
this.refs.code.focus()
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
)
}
}
handlePreviewMouseDown (e) {
handlePreviewMouseDown(e) {
this.previewMouseDownedAt = new Date()
}
handlePreviewMouseUp (e) {
handlePreviewMouseUp(e) {
const { config } = this.props
if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) {
this.setState({
status: 'CODE'
}, () => {
this.refs.code.focus()
})
if (
config.editor.switchPreview === 'BLUR' &&
new Date() - this.previewMouseDownedAt < 200
) {
this.setState(
{
status: 'CODE'
},
() => {
this.refs.code.focus()
}
)
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
}
handleCheckboxClick (e) {
handleCheckboxClick(e) {
e.preventDefault()
e.stopPropagation()
const idMatch = /checkbox-([0-9]+)/
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
const checkReplace = /\[x\]/i
const uncheckReplace = /\[ \]/
const checkedMatch = /^(\s*>?)*\s*[+\-*] \[x]/i
const uncheckedMatch = /^(\s*>?)*\s*[+\-*] \[ ]/
const checkReplace = /\[x]/i
const uncheckReplace = /\[ ]/
if (idMatch.test(e.target.getAttribute('id'))) {
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
const lines = this.refs.code.value
.split('\n')
const lineIndex =
parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
const lines = this.refs.code.value.split('\n')
const targetLine = lines[lineIndex]
let newLine = targetLine
if (targetLine.match(checkedMatch)) {
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
newLine = targetLine.replace(checkReplace, '[ ]')
}
if (targetLine.match(uncheckedMatch)) {
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
newLine = targetLine.replace(uncheckReplace, '[x]')
}
this.refs.code.setValue(lines.join('\n'))
this.refs.code.setLineContent(lineIndex, newLine)
}
}
focus () {
focus() {
if (this.state.status === 'PREVIEW') {
this.setState({
status: 'CODE'
}, () => {
this.refs.code.focus()
})
this.setState(
{
status: 'CODE'
},
() => {
this.refs.code.focus()
}
)
} else {
this.refs.code.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
reload () {
reload() {
this.refs.code.reload()
this.cancelQueue()
this.renderPreview(this.props.value)
}
handleKeyDown (e) {
handleKeyDown(e) {
const { config } = this.props
if (this.state.status !== 'CODE') return false
const keyPressed = this.state.keyPressed
keyPressed.add(e.keyCode)
this.setState({ keyPressed })
const isNoteHandlerKey = (el) => { return keyPressed.has(el) }
const isNoteHandlerKey = el => {
return keyPressed.has(el)
}
// These conditions are for ctrl-e and ctrl-w
if (keyPressed.size === this.escapeFromEditor.length &&
!this.state.isLocked && this.state.status === 'CODE' &&
this.escapeFromEditor.every(isNoteHandlerKey)) {
if (
keyPressed.size === this.escapeFromEditor.length &&
!this.state.isLocked &&
this.state.status === 'CODE' &&
this.escapeFromEditor.every(isNoteHandlerKey)
) {
this.handleContextMenu()
if (config.editor.switchPreview === 'BLUR') document.activeElement.blur()
}
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
if (
keyPressed.size === this.supportMdSelectionBold.length &&
this.supportMdSelectionBold.every(isNoteHandlerKey)
) {
this.addMdAroundWord('**')
}
}
addMdAroundWord (mdElement) {
addMdAroundWord(mdElement) {
if (this.refs.code.editor.getSelection()) {
return this.addMdAroundSelection(mdElement)
}
@@ -216,47 +268,64 @@ class MarkdownEditor extends React.Component {
const word = this.refs.code.editor.findWordAt(currentCaret)
const cmDoc = this.refs.code.editor.getDoc()
cmDoc.replaceRange(mdElement, word.anchor)
cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length })
}
addMdAroundSelection (mdElement) {
this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`)
}
handleDropImage (dropEvent) {
dropEvent.preventDefault()
const { storageKey, noteKey } = this.props
this.setState({
status: 'CODE'
}, () => {
this.refs.code.focus()
this.refs.code.editor.execCommand('goDocEnd')
this.refs.code.editor.execCommand('goLineEnd')
this.refs.code.editor.execCommand('newlineAndIndent')
attachmentManagement.handleAttachmentDrop(
this.refs.code,
storageKey,
noteKey,
dropEvent
)
cmDoc.replaceRange(mdElement, {
line: word.head.line,
ch: word.head.ch + mdElement.length
})
}
handleKeyUp (e) {
addMdAroundSelection(mdElement) {
this.refs.code.editor.replaceSelection(
`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`
)
}
handleDropImage(dropEvent) {
dropEvent.preventDefault()
const { storageKey, noteKey } = this.props
this.setState(
{
status: 'CODE'
},
() => {
this.refs.code.focus()
this.refs.code.editor.execCommand('goDocEnd')
this.refs.code.editor.execCommand('goLineEnd')
this.refs.code.editor.execCommand('newlineAndIndent')
attachmentManagement.handleAttachmentDrop(
this.refs.code,
storageKey,
noteKey,
dropEvent
)
}
)
}
handleKeyUp(e) {
const keyPressed = this.state.keyPressed
keyPressed.delete(e.keyCode)
this.setState({ keyPressed })
}
handleLockEditor () {
handleLockEditor() {
this.setState({ isLocked: !this.state.isLocked })
}
render () {
const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props
render() {
const {
className,
value,
config,
storageKey,
noteKey,
linesHighlighted,
getNote,
RTL
} = this.props
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
@@ -264,23 +333,24 @@ class MarkdownEditor extends React.Component {
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const previewStyle = {}
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
if (this.props.ignorePreviewPointerEvents)
previewStyle.pointerEvents = 'none'
const storage = findStorage(storageKey)
return (
<div className={className == null
? 'MarkdownEditor'
: `MarkdownEditor ${className}`
<div
className={
className == null ? 'MarkdownEditor' : `MarkdownEditor ${className}`
}
onContextMenu={(e) => this.handleContextMenu(e)}
onContextMenu={e => this.handleContextMenu(e)}
tabIndex='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyUp={(e) => this.handleKeyUp(e)}
onKeyDown={e => this.handleKeyDown(e)}
onKeyUp={e => this.handleKeyUp(e)}
>
<CodeEditor styleName={this.state.status === 'CODE'
? 'codeEditor'
: 'codeEditor--hide'
<CodeEditor
styleName={
this.state.status === 'CODE' ? 'codeEditor' : 'codeEditor--hide'
}
ref='code'
mode='Boost Flavored Markdown'
@@ -294,25 +364,40 @@ class MarkdownEditor extends React.Component {
enableRulers={config.editor.enableRulers}
rulers={config.editor.rulers}
displayLineNumbers={config.editor.displayLineNumbers}
lineWrapping
matchingPairs={config.editor.matchingPairs}
matchingCloseBefore={config.editor.matchingCloseBefore}
matchingTriples={config.editor.matchingTriples}
explodingPairs={config.editor.explodingPairs}
codeBlockMatchingPairs={config.editor.codeBlockMatchingPairs}
codeBlockMatchingCloseBefore={
config.editor.codeBlockMatchingCloseBefore
}
codeBlockMatchingTriples={config.editor.codeBlockMatchingTriples}
codeBlockExplodingPairs={config.editor.codeBlockExplodingPairs}
scrollPastEnd={config.editor.scrollPastEnd}
storageKey={storageKey}
noteKey={noteKey}
fetchUrlTitle={config.editor.fetchUrlTitle}
enableTableEditor={config.editor.enableTableEditor}
linesHighlighted={linesHighlighted}
onChange={(e) => this.handleChange(e)}
onBlur={(e) => this.handleBlur(e)}
onChange={e => this.handleChange(e)}
onBlur={e => this.handleBlur(e)}
spellCheck={config.editor.spellcheck}
enableSmartPaste={config.editor.enableSmartPaste}
hotkey={config.hotkey}
switchPreview={config.editor.switchPreview}
enableMarkdownLint={config.editor.enableMarkdownLint}
customMarkdownLintConfig={config.editor.customMarkdownLintConfig}
dateFormatISO8601={config.editor.dateFormatISO8601}
prettierConfig={config.editor.prettierConfig}
deleteUnusedAttachments={config.editor.deleteUnusedAttachments}
RTL={RTL}
/>
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
? 'preview'
: 'preview--hide'
<MarkdownPreview
ref={this.previewRef}
styleName={
this.state.status === 'PREVIEW' ? 'preview' : 'preview--hide'
}
style={previewStyle}
theme={config.ui.theme}
@@ -328,21 +413,24 @@ class MarkdownEditor extends React.Component {
smartArrows={config.preview.smartArrows}
breaks={config.preview.breaks}
sanitize={config.preview.sanitize}
ref='preview'
onContextMenu={(e) => this.handleContextMenu(e)}
onDoubleClick={(e) => this.handleDoubleClick(e)}
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
onContextMenu={e => this.handleContextMenu(e)}
onDoubleClick={e => this.handleDoubleClick(e)}
tabIndex='0'
value={this.state.renderValue}
onMouseUp={(e) => this.handlePreviewMouseUp(e)}
onMouseDown={(e) => this.handlePreviewMouseDown(e)}
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
onMouseUp={e => this.handlePreviewMouseUp(e)}
onMouseDown={e => this.handlePreviewMouseDown(e)}
onCheckboxClick={e => this.handleCheckboxClick(e)}
showCopyNotification={config.ui.showCopyNotification}
storagePath={storage.path}
noteKey={noteKey}
customCSS={config.preview.customCSS}
allowCustomCSS={config.preview.allowCustomCSS}
lineThroughCheckbox={config.preview.lineThroughCheckbox}
onDrop={(e) => this.handleDropImage(e)}
getNote={getNote}
export={config.export}
onDrop={e => this.handleDropImage(e)}
RTL={RTL}
/>
</div>
)

File diff suppressed because it is too large Load Diff

View File

@@ -8,163 +8,431 @@ import styles from './MarkdownSplitEditor.styl'
import CSSModules from 'browser/lib/CSSModules'
class MarkdownSplitEditor extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.value = props.value
this.focus = () => this.refs.code.focus()
this.reload = () => this.refs.code.reload()
this.userScroll = true
this.userScroll = props.config.preview.scrollSync
this.state = {
isSliderFocused: false,
codeEditorWidthInPercent: 50
codeEditorWidthInPercent: 50,
codeEditorHeightInPercent: 50
}
}
setValue (value) {
componentDidUpdate(prevProps) {
if (
this.props.config.preview.scrollSync !==
prevProps.config.preview.scrollSync
) {
this.userScroll = this.props.config.preview.scrollSync
}
}
handleCursorActivity(editor) {
if (this.userScroll) {
const previewDoc = _.get(
this,
'refs.preview.refs.root.contentWindow.document'
)
const previewTop = _.get(previewDoc, 'body.scrollTop')
const line = editor.doc.getCursor().line
let top
if (line === 0) {
top = 0
} else {
const blockElements = previewDoc.querySelectorAll('body [data-line]')
const blocks = []
for (const block of blockElements) {
const l = parseInt(block.getAttribute('data-line'))
blocks.push({
line: l,
top: block.offsetTop
})
if (l > line) {
break
}
}
if (blocks.length === 1) {
const block = blockElements[blockElements.length - 1]
blocks.push({
line: editor.doc.size,
top: block.offsetTop + block.offsetHeight
})
}
const i = blocks.length - 1
const ratio =
(blocks[i].top - blocks[i - 1].top) /
(blocks[i].line - blocks[i - 1].line)
const delta = Math.floor(_.get(previewDoc, 'body.clientHeight') / 3)
top =
blocks[i - 1].top +
Math.floor((line - blocks[i - 1].line) * ratio) -
delta
}
this.scrollTo(previewTop, top, y =>
_.set(previewDoc, 'body.scrollTop', y)
)
}
}
setValue(value) {
this.refs.code.setValue(value)
}
handleOnChange (e) {
handleOnChange(e) {
this.value = this.refs.code.value
this.props.onChange(e)
}
handleScroll (e) {
if (!this.props.config.preview.scrollSync) return
const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document')
const codeDoc = _.get(this, 'refs.code.editor.doc')
let srcTop, srcHeight, targetTop, targetHeight
handleEditorScroll(e) {
if (this.userScroll) {
if (e.doc) {
srcTop = _.get(e, 'doc.scrollTop')
srcHeight = _.get(e, 'doc.height')
targetTop = _.get(previewDoc, 'body.scrollTop')
targetHeight = _.get(previewDoc, 'body.scrollHeight')
const previewDoc = _.get(
this,
'refs.preview.refs.root.contentWindow.document'
)
const codeDoc = _.get(this, 'refs.code.editor.doc')
const from = codeDoc.cm.coordsChar({ left: 0, top: 0 }).line
const to = codeDoc.cm.coordsChar({
left: 0,
top: codeDoc.cm.display.lastWrapHeight * 1.125
}).line
const previewTop = _.get(previewDoc, 'body.scrollTop')
let top
if (from === 0) {
top = 0
} else if (to === codeDoc.lastLine()) {
top =
_.get(previewDoc, 'body.scrollHeight') -
_.get(previewDoc, 'body.clientHeight')
} else {
srcTop = _.get(previewDoc, 'body.scrollTop')
srcHeight = _.get(previewDoc, 'body.scrollHeight')
targetTop = _.get(codeDoc, 'scrollTop')
targetHeight = _.get(codeDoc, 'height')
const line = from + Math.floor((to - from) / 3)
const blockElements = previewDoc.querySelectorAll('body [data-line]')
const blocks = []
for (const block of blockElements) {
const l = parseInt(block.getAttribute('data-line'))
blocks.push({
line: l,
top: block.offsetTop
})
if (l > line) {
break
}
}
if (blocks.length === 1) {
const block = blockElements[blockElements.length - 1]
blocks.push({
line: codeDoc.size,
top: block.offsetTop + block.offsetHeight
})
}
const i = blocks.length - 1
const ratio =
(blocks[i].top - blocks[i - 1].top) /
(blocks[i].line - blocks[i - 1].line)
top =
blocks[i - 1].top + Math.floor((line - blocks[i - 1].line) * ratio)
}
const distance = (targetHeight * srcTop / srcHeight) - targetTop
const framerate = 1000 / 60
const frames = 20
const refractory = frames * framerate
this.userScroll = false
let frame = 0
let scrollPos, time
const timer = setInterval(() => {
time = frame / frames
scrollPos = time < 0.5
? 2 * time * time // ease in
: -1 + (4 - 2 * time) * time // ease out
if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + scrollPos * distance)
else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + scrollPos * distance)
if (frame >= frames) {
clearInterval(timer)
setTimeout(() => { this.userScroll = true }, refractory)
}
frame++
}, framerate)
this.scrollTo(previewTop, top, y =>
_.set(previewDoc, 'body.scrollTop', y)
)
}
}
handleCheckboxClick (e) {
handlePreviewScroll(e) {
if (this.userScroll) {
const previewDoc = _.get(
this,
'refs.preview.refs.root.contentWindow.document'
)
const codeDoc = _.get(this, 'refs.code.editor.doc')
const srcTop = _.get(previewDoc, 'body.scrollTop')
const editorTop = _.get(codeDoc, 'scrollTop')
let top
if (srcTop === 0) {
top = 0
} else {
const delta = Math.floor(_.get(previewDoc, 'body.clientHeight') / 3)
const previewTop = srcTop + delta
const blockElements = previewDoc.querySelectorAll('body [data-line]')
const blocks = []
for (const block of blockElements) {
const top = block.offsetTop
blocks.push({
line: parseInt(block.getAttribute('data-line')),
top
})
if (top > previewTop) {
break
}
}
if (blocks.length === 1) {
const block = blockElements[blockElements.length - 1]
blocks.push({
line: codeDoc.size,
top: block.offsetTop + block.offsetHeight
})
}
const i = blocks.length - 1
const from = codeDoc.cm.heightAtLine(blocks[i - 1].line, 'local')
const to = codeDoc.cm.heightAtLine(blocks[i].line, 'local')
const ratio =
(previewTop - blocks[i - 1].top) / (blocks[i].top - blocks[i - 1].top)
top = from + Math.floor((to - from) * ratio) - delta
}
this.scrollTo(editorTop, top, y => codeDoc.cm.scrollTo(0, y))
}
}
handleCheckboxClick(e) {
e.preventDefault()
e.stopPropagation()
const idMatch = /checkbox-([0-9]+)/
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
const checkReplace = /\[x\]/i
const uncheckReplace = /\[ \]/
const checkedMatch = /^(\s*>?)*\s*[+\-*] \[x]/i
const uncheckedMatch = /^(\s*>?)*\s*[+\-*] \[ ]/
const checkReplace = /\[x]/i
const uncheckReplace = /\[ ]/
if (idMatch.test(e.target.getAttribute('id'))) {
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
const lines = this.refs.code.value
.split('\n')
const lineIndex =
parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
const lines = this.refs.code.value.split('\n')
const targetLine = lines[lineIndex]
let newLine = targetLine
if (targetLine.match(checkedMatch)) {
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
newLine = targetLine.replace(checkReplace, '[ ]')
}
if (targetLine.match(uncheckedMatch)) {
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
newLine = targetLine.replace(uncheckReplace, '[x]')
}
this.refs.code.setValue(lines.join('\n'))
this.refs.code.setLineContent(lineIndex, newLine)
}
}
handleMouseMove (e) {
handleMouseMove(e) {
if (this.state.isSliderFocused) {
const rootRect = this.refs.root.getBoundingClientRect()
const rootWidth = rootRect.width
const offset = rootRect.left
let newCodeEditorWidthInPercent = (e.pageX - offset) / rootWidth * 100
if (this.props.isStacking) {
const rootHeight = rootRect.height
const offset = rootRect.top
let newCodeEditorHeightInPercent =
((e.pageY - offset) / rootHeight) * 100
// limit minSize to 10%, maxSize to 90%
if (newCodeEditorWidthInPercent <= 10) {
newCodeEditorWidthInPercent = 10
// limit minSize to 10%, maxSize to 90%
if (newCodeEditorHeightInPercent <= 10) {
newCodeEditorHeightInPercent = 10
}
if (newCodeEditorHeightInPercent >= 90) {
newCodeEditorHeightInPercent = 90
}
this.setState({
codeEditorHeightInPercent: newCodeEditorHeightInPercent
})
} else {
const rootWidth = rootRect.width
const offset = rootRect.left
let newCodeEditorWidthInPercent = ((e.pageX - offset) / rootWidth) * 100
// limit minSize to 10%, maxSize to 90%
if (newCodeEditorWidthInPercent <= 10) {
newCodeEditorWidthInPercent = 10
}
if (newCodeEditorWidthInPercent >= 90) {
newCodeEditorWidthInPercent = 90
}
this.setState({
codeEditorWidthInPercent: newCodeEditorWidthInPercent
})
}
if (newCodeEditorWidthInPercent >= 90) {
newCodeEditorWidthInPercent = 90
}
this.setState({
codeEditorWidthInPercent: newCodeEditorWidthInPercent
})
}
}
handleMouseUp (e) {
handleMouseUp(e) {
e.preventDefault()
this.setState({
isSliderFocused: false
})
}
handleMouseDown (e) {
handleMouseDown(e) {
e.preventDefault()
this.setState({
isSliderFocused: true
})
}
render () {
const {config, value, storageKey, noteKey, linesHighlighted} = this.props
const storage = findStorage(storageKey)
scrollTo(from, to, scroller) {
const distance = to - from
const framerate = 1000 / 60
const frames = 20
const refractory = frames * framerate
this.userScroll = false
let frame = 0
let scrollPos, time
const timer = setInterval(() => {
time = frame / frames
scrollPos =
time < 0.5
? 2 * time * time // ease in
: -1 + (4 - 2 * time) * time // ease out
scroller(from + scrollPos * distance)
if (frame >= frames) {
clearInterval(timer)
setTimeout(() => {
this.userScroll = true
}, refractory)
}
frame++
}, framerate)
}
render() {
const {
config,
value,
storageKey,
noteKey,
linesHighlighted,
getNote,
isStacking,
RTL
} = this.props
let storage
try {
storage = findStorage(storageKey)
} catch (e) {
return <div />
}
let editorStyle = {}
let previewStyle = {}
let sliderStyle = {}
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
editorStyle.fontSize = editorFontSize
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const previewStyle = {}
previewStyle.width = (100 - this.state.codeEditorWidthInPercent) + '%'
if (this.props.ignorePreviewPointerEvents || this.state.isSliderFocused) previewStyle.pointerEvents = 'none'
if (!(editorStyle.fontSize > 0 && editorStyle.fontSize < 132))
editorIndentSize = 4
editorStyle.indentSize = editorIndentSize
editorStyle = Object.assign(
editorStyle,
isStacking
? {
width: '100%',
height: `${this.state.codeEditorHeightInPercent}%`
}
: {
width: `${this.state.codeEditorWidthInPercent}%`,
height: '100%'
}
)
previewStyle = Object.assign(
previewStyle,
isStacking
? {
width: '100%',
height: `${100 - this.state.codeEditorHeightInPercent}%`
}
: {
width: `${100 - this.state.codeEditorWidthInPercent}%`,
height: '100%'
}
)
sliderStyle = Object.assign(
sliderStyle,
isStacking
? {
left: 0,
top: `${this.state.codeEditorHeightInPercent}%`
}
: {
left: `${this.state.codeEditorWidthInPercent}%`,
top: 0
}
)
if (this.props.ignorePreviewPointerEvents || this.state.isSliderFocused)
previewStyle.pointerEvents = 'none'
return (
<div styleName='root' ref='root'
<div
styleName='root'
ref='root'
onMouseMove={e => this.handleMouseMove(e)}
onMouseUp={e => this.handleMouseUp(e)}>
onMouseUp={e => this.handleMouseUp(e)}
>
<CodeEditor
styleName='codeEditor'
ref='code'
width={this.state.codeEditorWidthInPercent + '%'}
width={editorStyle.width}
height={editorStyle.height}
mode='Boost Flavored Markdown'
value={value}
theme={config.editor.theme}
keyMap={config.editor.keyMap}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
fontSize={editorStyle.fontSize}
displayLineNumbers={config.editor.displayLineNumbers}
lineWrapping
matchingPairs={config.editor.matchingPairs}
matchingCloseBefore={config.editor.matchingCloseBefore}
matchingTriples={config.editor.matchingTriples}
explodingPairs={config.editor.explodingPairs}
codeBlockMatchingPairs={config.editor.codeBlockMatchingPairs}
codeBlockMatchingCloseBefore={
config.editor.codeBlockMatchingCloseBefore
}
codeBlockMatchingTriples={config.editor.codeBlockMatchingTriples}
codeBlockExplodingPairs={config.editor.codeBlockExplodingPairs}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
indentSize={editorStyle.indentSize}
enableRulers={config.editor.enableRulers}
rulers={config.editor.rulers}
scrollPastEnd={config.editor.scrollPastEnd}
@@ -173,19 +441,29 @@ class MarkdownSplitEditor extends React.Component {
storageKey={storageKey}
noteKey={noteKey}
linesHighlighted={linesHighlighted}
onChange={(e) => this.handleOnChange(e)}
onScroll={this.handleScroll.bind(this)}
onChange={e => this.handleOnChange(e)}
onScroll={e => this.handleEditorScroll(e)}
onCursorActivity={e => this.handleCursorActivity(e)}
spellCheck={config.editor.spellcheck}
enableSmartPaste={config.editor.enableSmartPaste}
hotkey={config.hotkey}
switchPreview={config.editor.switchPreview}
/>
<div styleName='slider' style={{left: this.state.codeEditorWidthInPercent + '%'}} onMouseDown={e => this.handleMouseDown(e)} >
enableMarkdownLint={config.editor.enableMarkdownLint}
customMarkdownLintConfig={config.editor.customMarkdownLintConfig}
dateFormatISO8601={config.editor.dateFormatISO8601}
deleteUnusedAttachments={config.editor.deleteUnusedAttachments}
RTL={RTL}
/>
<div
styleName={isStacking ? 'slider-hoz' : 'slider'}
style={{ left: sliderStyle.left, top: sliderStyle.top }}
onMouseDown={e => this.handleMouseDown(e)}
>
<div styleName='slider-hitbox' />
</div>
<MarkdownPreview
ref='preview'
style={previewStyle}
styleName='preview'
theme={config.ui.theme}
keyMap={config.editor.keyMap}
fontSize={config.preview.fontSize}
@@ -193,23 +471,27 @@ class MarkdownSplitEditor extends React.Component {
codeBlockTheme={config.preview.codeBlockTheme}
codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize}
scrollPastEnd={config.preview.scrollPastEnd}
smartQuotes={config.preview.smartQuotes}
smartArrows={config.preview.smartArrows}
breaks={config.preview.breaks}
sanitize={config.preview.sanitize}
ref='preview'
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
tabInde='0'
value={value}
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
onScroll={this.handleScroll.bind(this)}
onCheckboxClick={e => this.handleCheckboxClick(e)}
onScroll={e => this.handlePreviewScroll(e)}
showCopyNotification={config.ui.showCopyNotification}
storagePath={storage.path}
noteKey={noteKey}
customCSS={config.preview.customCSS}
allowCustomCSS={config.preview.allowCustomCSS}
lineThroughCheckbox={config.preview.lineThroughCheckbox}
/>
getNote={getNote}
export={config.export}
RTL={RTL}
/>
</div>
)
}

View File

@@ -3,14 +3,36 @@
height 100%
font-size 30px
display flex
flex-wrap wrap
.slider
absolute top bottom
top -2px
width 0
z-index 0
border-left 1px solid $ui-borderColor
.slider-hitbox
absolute top bottom left right
width 7px
left -3px
z-index 10
cursor col-resize
.slider-hoz
absolute left right
.slider-hitbox
absolute left right
width: 100%
height 7px
cursor row-resize
apply-theme(theme)
body[data-theme={theme}]
.root
.slider
border-left 1px solid get-theme-var(theme, 'borderColor')
for theme in 'dark' 'dracula' 'solarized-dark'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -3,12 +3,10 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ModalEscButton.styl'
const ModalEscButton = ({
handleEscButtonClick
}) => (
const ModalEscButton = ({ handleEscButtonClick }) => (
<button styleName='escButton' onClick={handleEscButtonClick}>
<div styleName='esc-mark'>×</div>
<div styleName='esc-text'>esc</div>
<div>esc</div>
</button>
)

View File

@@ -1,24 +1,23 @@
/**
* @fileoverview Micro component for toggle SideNav
*/
* @fileoverview Micro component for toggle SideNav
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './NavToggleButton.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {boolean} isFolded
* @param {Function} handleToggleButtonClick
*/
* @param {boolean} isFolded
* @param {Function} handleToggleButtonClick
*/
const NavToggleButton = ({isFolded, handleToggleButtonClick}) => (
<button styleName='navToggle'
onClick={(e) => handleToggleButtonClick(e)}
>
{isFolded
? <i className='fa fa-angle-double-right' />
: <i className='fa fa-angle-double-left' />
}
const NavToggleButton = ({ isFolded, handleToggleButtonClick }) => (
<button styleName='navToggle' onClick={e => handleToggleButtonClick(e)}>
{isFolded ? (
<i className='fa fa-angle-double-right fa-2x' />
) : (
<i className='fa fa-angle-double-left fa-2x' />
)}
</button>
)

View File

@@ -7,7 +7,7 @@
border-radius 16.5px
height 34px
width 34px
line-height 32px
line-height 100%
padding 0
&:hover
border: 1px solid #1EC38B;
@@ -17,10 +17,16 @@
body[data-theme="white"]
navWhiteButtonColor()
body[data-theme="dark"]
.navToggle
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
apply-theme(theme)
body[data-theme={theme}]
.navToggle:hover
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%)
border 1px solid get-theme-var(theme, 'button--active-backgroundColor')
transition 0.15s
color $ui-dark-text-color
color get-theme-var(theme, 'text-color')
for theme in 'dark' 'dracula' 'solarized-dark'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -3,8 +3,9 @@
*/
import PropTypes from 'prop-types'
import React from 'react'
import { isArray } from 'lodash'
import { isArray, sortBy } from 'lodash'
import invertColor from 'invert-color'
import Emoji from 'react-emoji-render'
import CSSModules from 'browser/lib/CSSModules'
import { getTodoStatus } from 'browser/lib/getTodoStatus'
import styles from './NoteItem.styl'
@@ -21,7 +22,11 @@ const TagElement = ({ tagName, color }) => {
const style = {}
if (color) {
style.backgroundColor = color
style.color = invertColor(color, { black: '#222', white: '#f1f1f1', threshold: 0.3 })
style.color = invertColor(color, {
black: '#222',
white: '#f1f1f1',
threshold: 0.3
})
}
return (
<span styleName='item-bottom-tagList-item' key={tagName} style={style}>
@@ -43,9 +48,13 @@ const TagElementList = (tags, showTagsAlphabetically, coloredTags) => {
}
if (showTagsAlphabetically) {
return _.sortBy(tags).map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
return sortBy(tags).map(tag =>
TagElement({ tagName: tag, color: coloredTags[tag] })
)
} else {
return tags.map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
return tags.map(tag =>
TagElement({ tagName: tag, color: coloredTags[tag] })
)
}
}
@@ -82,13 +91,17 @@ const NoteItem = ({
draggable='true'
>
<div styleName='item-wrapper'>
{note.type === 'SNIPPET_NOTE'
? <i styleName='item-title-icon' className='fa fa-fw fa-code' />
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />}
{note.type === 'SNIPPET_NOTE' ? (
<i styleName='item-title-icon' className='fa fa-fw fa-code' />
) : (
<i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />
)}
<div styleName='item-title'>
{note.title.trim().length > 0
? note.title
: <span styleName='item-title-empty'>{i18n.__('Empty note')}</span>}
{note.title.trim().length > 0 ? (
<Emoji text={note.title} />
) : (
<span styleName='item-title-empty'>{i18n.__('Empty note')}</span>
)}
</div>
<div styleName='item-middle'>
<div styleName='item-middle-time'>{dateDisplay}</div>
@@ -97,7 +110,9 @@ const NoteItem = ({
title={
viewType === 'ALL'
? storageName
: viewType === 'STORAGE' ? folderName : null
: viewType === 'STORAGE'
? folderName
: null
}
styleName='item-middle-app-meta-label'
>
@@ -108,28 +123,36 @@ const NoteItem = ({
</div>
<div styleName='item-bottom'>
<div styleName='item-bottom-tagList'>
{note.tags.length > 0
? TagElementList(note.tags, showTagsAlphabetically, coloredTags)
: <span
{note.tags.length > 0 ? (
TagElementList(note.tags, showTagsAlphabetically, coloredTags)
) : (
<span
style={{ fontStyle: 'italic', opacity: 0.5 }}
styleName='item-bottom-tagList-empty'
>
>
{i18n.__('No tags')}
</span>}
</span>
)}
</div>
<div>
{note.isStarred
? <img
{note.isStarred ? (
<img
styleName='item-star'
src='../resources/icon/icon-starred.svg'
/>
: ''}
{note.isPinned && !pathname.match(/\/starred|\/trash/)
? <i styleName='item-pin' className='fa fa-thumb-tack' />
: ''}
{note.type === 'MARKDOWN_NOTE'
? <TodoProcess todoStatus={getTodoStatus(note.content)} />
: ''}
/>
) : (
''
)}
{note.isPinned && !pathname.match(/\/starred|\/trash/) ? (
<i styleName='item-pin' className='fa fa-thumb-tack' />
) : (
''
)}
{note.type === 'MARKDOWN_NOTE' ? (
<TodoProcess todoStatus={getTodoStatus(note.content)} />
) : (
''
)}
</div>
</div>
</div>
@@ -148,15 +171,14 @@ NoteItem.propTypes = {
tags: PropTypes.array,
isStarred: PropTypes.bool.isRequired,
isTrashed: PropTypes.bool.isRequired,
blog: {
blog: PropTypes.shape({
blogLink: PropTypes.string,
blogId: PropTypes.number
}
})
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired,
handleDragStart: PropTypes.func.isRequired,
handleDragEnd: PropTypes.func.isRequired
handleDragStart: PropTypes.func.isRequired
}
export default CSSModules(NoteItem, styles)

View File

@@ -194,7 +194,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha(#fff, 20%)
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
color $ui-dark-text-color
&:active
transition 0.15s
@@ -207,7 +207,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha(white, 10%)
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
color $ui-dark-text-color
.item-wrapper
@@ -223,13 +223,13 @@ body[data-theme="dark"]
.item-bottom-time
color $ui-dark-text-color
.item-bottom-tagList-item
background-color alpha(white, 10%)
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
color $ui-dark-button--hover-color
.item-bottom-tagList-item
background-color alpha(#fff, 20%)
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
.item-title
color $ui-inactive-text-color
@@ -322,148 +322,82 @@ body[data-theme="solarized-dark"]
color $ui-inactive-text-color
vertical-align middle
body[data-theme="monokai"]
.root
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteList-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.root
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteList-backgroundColor')
.item
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteList-backgroundColor
&:hover
transition 0.15s
// background-color alpha($ui-monokai-noteList-backgroundColor, 20%)
color $ui-monokai-text-color
.item-title
.item-title-icon
.item-bottom-time
.item
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteList-backgroundColor')
&:hover
transition 0.15s
color $ui-monokai-text-color
.item-bottom-tagList-item
// background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%)
color get-theme-var(theme, 'text-color')
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color get-theme-var(theme, 'text-color')
.item-bottom-tagList-item
transition 0.15s
background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%)
color get-theme-var(theme, 'text-color')
&:active
transition 0.15s
background-color alpha($ui-monokai-noteList-backgroundColor, 20%)
color $ui-monokai-text-color
&:active
transition 0.15s
background-color $ui-monokai-noteList-backgroundColor
color $ui-monokai-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-monokai-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-monokai-noteList-backgroundColor, 10%)
color $ui-monokai-text-color
background-color get-theme-var(theme, 'noteList-backgroundColor')
color get-theme-var(theme, 'text-color')
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color get-theme-var(theme, 'text-color')
.item-bottom-tagList-item
transition 0.15s
background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 10%)
color get-theme-var(theme, 'text-color')
.item-wrapper
border-color alpha($ui-monokai-button-backgroundColor, 60%)
.item--active
border-color $ui-monokai-borderColor
background-color $ui-monokai-button-backgroundColor
.item-wrapper
border-color transparent
.item-title
.item-title-icon
.item-bottom-time
color $ui-monokai-active-color
.item-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-monokai-text-color
&:hover
// background-color alpha($ui-monokai-button--active-backgroundColor, 60%)
color #f92672
.item-bottom-tagList-item
background-color alpha(#fff, 20%)
border-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%)
.item-title
color $ui-inactive-text-color
.item-title-icon
color $ui-inactive-text-color
.item-title-empty
color $ui-inactive-text-color
.item-bottom-tagList-item
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
color $ui-inactive-text-color
.item-bottom-tagList-empty
color $ui-inactive-text-color
vertical-align middle
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
.item
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
&:hover
transition 0.15s
// background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
color $ui-dracula-text-color
.item--active
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'button-backgroundColor')
.item-wrapper
border-color transparent
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dracula-text-color
color get-theme-var(theme, 'active-color')
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
color $ui-dracula-text-color
&:active
transition 0.15s
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dracula-noteList-backgroundColor, 10%)
color $ui-dracula-text-color
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
color get-theme-var(theme, 'text-color')
&:hover
// background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%)
color get-theme-var(theme, 'button--hover-color')
.item-bottom-tagList-item
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
.item-wrapper
border-color alpha($ui-dracula-button-backgroundColor, 60%)
.item--active
border-color $ui-dracula-borderColor
background-color $ui-dracula-button-backgroundColor
.item-wrapper
border-color transparent
.item-title
color $ui-inactive-text-color
.item-title-icon
.item-bottom-time
color $ui-dracula-active-color
color $ui-inactive-text-color
.item-title-empty
color $ui-inactive-text-color
.item-bottom-tagList-item
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
&:hover
// background-color alpha($ui-dracula-button--active-backgroundColor, 60%)
color #ff79c6
.item-bottom-tagList-item
background-color alpha(#f8f8f2, 20%)
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
color $ui-inactive-text-color
.item-title
color $ui-inactive-text-color
.item-bottom-tagList-empty
color $ui-inactive-text-color
vertical-align middle
.item-title-icon
color $ui-inactive-text-color
for theme in 'dracula'
apply-theme(theme)
.item-title-empty
color $ui-inactive-text-color
.item-bottom-tagList-item
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
color $ui-inactive-text-color
.item-bottom-tagList-empty
color $ui-inactive-text-color
vertical-align middle
for theme in $themes
apply-theme(theme)

View File

@@ -25,10 +25,8 @@ const NoteItemSimple = ({
pathname,
storage
}) => (
<div styleName={isActive
? 'item-simple--active'
: 'item-simple'
}
<div
styleName={isActive ? 'item-simple--active' : 'item-simple'}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
@@ -36,23 +34,29 @@ const NoteItemSimple = ({
draggable='true'
>
<div styleName='item-simple-title'>
{note.type === 'SNIPPET_NOTE'
? <i styleName='item-simple-title-icon' className='fa fa-fw fa-code' />
: <i styleName='item-simple-title-icon' className='fa fa-fw fa-file-text-o' />
}
{note.isPinned && !pathname.match(/\/starred|\/trash/)
? <i styleName='item-pin' className='fa fa-thumb-tack' />
: ''
}
{note.title.trim().length > 0
? note.title
: <span styleName='item-simple-title-empty'>{i18n.__('Empty note')}</span>
}
{isAllNotesView && <div styleName='item-simple-right'>
<span styleName='item-simple-right-storageName'>
{storage.name}
</span>
</div>}
{note.type === 'SNIPPET_NOTE' ? (
<i styleName='item-simple-title-icon' className='fa fa-fw fa-code' />
) : (
<i
styleName='item-simple-title-icon'
className='fa fa-fw fa-file-text-o'
/>
)}
{note.isPinned && !pathname.match(/\/starred|\/trash/) ? (
<i styleName='item-pin' className='fa fa-thumb-tack' />
) : (
''
)}
{note.title.trim().length > 0 ? (
note.title
) : (
<span styleName='item-simple-title-empty'>{i18n.__('Empty note')}</span>
)}
{isAllNotesView && (
<div styleName='item-simple-right'>
<span styleName='item-simple-right-storageName'>{storage.name}</span>
</div>
)}
</div>
</div>
)

View File

@@ -223,130 +223,73 @@ body[data-theme="solarized-dark"]
padding-left 4px
opacity 0.4
body[data-theme="monokai"]
.root
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteList-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.root
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteList-backgroundColor')
.item-simple
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-monokai-button-backgroundColor, 60%)
color $ui-monokai-text-color
.item-simple
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteList-backgroundColor')
&:hover
transition 0.15s
background-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%)
color get-theme-var(theme, 'text-color')
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color get-theme-var(theme, 'text-color')
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
color get-theme-var(theme, 'text-color')
&:active
transition 0.15s
background-color get-theme-var(theme, 'button--active-backgroundColor')
color get-theme-var(theme, 'text-color')
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color get-theme-var(theme, 'text-color')
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
color get-theme-var(theme, 'text-color')
.item-simple--active
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'button--active-backgroundColor')
.item-simple-wrapper
border-color transparent
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-monokai-text-color
color get-theme-var(theme, 'text-color')
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#fff, 20%)
color $ui-monokai-text-color
&:active
transition 0.15s
background-color $ui-monokai-button--active-backgroundColor
color $ui-monokai-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-monokai-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(white, 10%)
color $ui-monokai-text-color
.item-simple--active
border-color $ui-monokai-borderColor
background-color $ui-monokai-button--active-backgroundColor
.item-simple-wrapper
border-color transparent
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
color get-theme-var(theme, 'text-color')
&:hover
// background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%)
color #c0392b
.item-simple-bottom-tagList-item
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
color $ui-monokai-text-color
.item-simple-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-monokai-text-color
&:hover
// background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-simple-bottom-tagList-item
background-color alpha(#fff, 20%)
.item-simple-title
color $ui-dark-text-color
border-bottom $ui-dark-borderColor
.item-simple-right
float right
.item-simple-right-storageName
padding-left 4px
opacity 0.4
color $ui-dark-text-color
border-bottom $ui-dark-borderColor
.item-simple-right
float right
.item-simple-right-storageName
padding-left 4px
opacity 0.4
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
for theme in 'dracula'
apply-theme(theme)
.item-simple
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-dracula-button-backgroundColor, 60%)
color $ui-dracula-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#f8f8f2, 20%)
color $ui-dracula-text-color
&:active
transition 0.15s
background-color $ui-dracula-button--active-backgroundColor
color $ui-dracula-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
.item-simple--active
border-color $ui-dracula-borderColor
background-color $ui-dracula-button--active-backgroundColor
.item-simple-wrapper
border-color transparent
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
&:hover
// background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-simple-bottom-tagList-item
background-color alpha(#f8f8f2, 20%)
.item-simple-title
color $ui-dark-text-color
border-bottom $ui-dark-borderColor
.item-simple-right
float right
.item-simple-right-storageName
padding-left 4px
opacity 0.4
for theme in $themes
apply-theme(theme)

View File

@@ -6,7 +6,7 @@ const electron = require('electron')
const { shell } = electron
class RealtimeNotification extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -14,38 +14,46 @@ class RealtimeNotification extends React.Component {
}
}
componentDidMount () {
componentDidMount() {
this.fetchNotifications()
}
fetchNotifications () {
const notificationsUrl = 'https://raw.githubusercontent.com/BoostIO/notification/master/notification.json'
fetchNotifications() {
const notificationsUrl =
'https://raw.githubusercontent.com/BoostIO/notification/master/notification.json'
fetch(notificationsUrl)
.then(response => {
return response.json()
})
.then(json => {
this.setState({notifications: json.notifications})
this.setState({ notifications: json.notifications })
})
}
handleLinkClick (e) {
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
render () {
render() {
const { notifications } = this.state
const link = notifications.length > 0
? <a styleName='notification-link' href={notifications[0].linkUrl}
onClick={(e) => this.handleLinkClick(e)}
>
Info: {notifications[0].text}
</a>
: ''
const link =
notifications.length > 0 ? (
<a
styleName='notification-link'
href={notifications[0].linkUrl}
onClick={e => this.handleLinkClick(e)}
>
Info: {notifications[0].text}
</a>
) : (
''
)
return (
<div styleName='notification-area' style={this.props.style}>{link}</div>
<div styleName='notification-area' style={this.props.style}>
{link}
</div>
)
}
}

View File

@@ -30,36 +30,20 @@ body[data-theme="dark"]
&:hover
color #5CB85C
apply-theme(theme)
body[data-theme={theme}]
.notification-area
background-color none
body[data-theme="solarized-dark"]
.notification-area
background-color none
.notification-link
color get-theme-var(theme, 'text-color')
border none
background-color get-theme-var(theme, 'button-backgroundColor')
&:hover
color get-theme-var(theme, 'button--hover-color')
.notification-link
color $ui-solarized-dark-text-color
border none
background-color $ui-solarized-dark-button-backgroundColor
&:hover
color #5CB85C
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="monokai"]
.notification-area
background-color none
.notification-link
color $ui-monokai-text-color
border none
background-color $ui-monokai-button-backgroundColor
&:hover
color #5CB85C
body[data-theme="dracula"]
.notification-area
background-color none
.notification-link
color $ui-dracula-text-color
border none
background-color $ui-dracula-button-backgroundColor
&:hover
color #ff79c6
for theme in $themes
apply-theme(theme)

View File

@@ -16,54 +16,70 @@ import i18n from 'browser/lib/i18n'
* @return {React.Component}
*/
const SideNavFilter = ({
isFolded, isHomeActive, handleAllNotesButtonClick,
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick, counterDelNote,
counterTotalNote, counterStarredNote, handleFilterButtonContextMenu
isFolded,
isHomeActive,
handleAllNotesButtonClick,
isStarredActive,
handleStarredButtonClick,
isTrashedActive,
handleTrashedButtonClick,
counterDelNote,
counterTotalNote,
counterStarredNote,
handleFilterButtonContextMenu
}) => (
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
<button
styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
onClick={handleAllNotesButtonClick}
>
<div styleName='iconWrap'>
<img src={isHomeActive
? '../resources/icon/icon-all-active.svg'
: '../resources/icon/icon-all.svg'
}
<img
src={
isHomeActive
? '../resources/icon/icon-all-active.svg'
: '../resources/icon/icon-all.svg'
}
/>
</div>
<span styleName='menu-button-label'>{i18n.__('All Notes')}</span>
<span styleName='counters'>{counterTotalNote}</span>
</button>
<button styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
<button
styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
onClick={handleStarredButtonClick}
>
<div styleName='iconWrap'>
<img src={isStarredActive
? '../resources/icon/icon-star-active.svg'
: '../resources/icon/icon-star-sidenav.svg'
}
<img
src={
isStarredActive
? '../resources/icon/icon-star-active.svg'
: '../resources/icon/icon-star-sidenav.svg'
}
/>
</div>
<span styleName='menu-button-label'>{i18n.__('Starred')}</span>
<span styleName='counters'>{counterStarredNote}</span>
</button>
<button styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
onClick={handleTrashedButtonClick} onContextMenu={handleFilterButtonContextMenu}
<button
styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
onClick={handleTrashedButtonClick}
onContextMenu={handleFilterButtonContextMenu}
>
<div styleName='iconWrap'>
<img src={isTrashedActive
? '../resources/icon/icon-trash-active.svg'
: '../resources/icon/icon-trash-sidenav.svg'
}
<img
src={
isTrashedActive
? '../resources/icon/icon-trash-active.svg'
: '../resources/icon/icon-trash-sidenav.svg'
}
/>
</div>
<span styleName='menu-button-label'>{i18n.__('Trash')}</span>
<span styleName='counters'>{counterDelNote}</span>
</button>
</div>
)
@@ -74,7 +90,7 @@ SideNavFilter.propTypes = {
isStarredActive: PropTypes.bool.isRequired,
isTrashedActive: PropTypes.bool.isRequired,
handleStarredButtonClick: PropTypes.func.isRequired,
handleTrashdButtonClick: PropTypes.func.isRequired
handleTrashedButtonClick: PropTypes.func.isRequired
}
export default CSSModules(SideNavFilter, styles)

View File

@@ -1,5 +1,5 @@
.menu
margin-bottom 30px
margin-bottom 20px
.menu-button
navButtonColor()
@@ -180,129 +180,51 @@ body[data-theme="dark"]
.menu-button-label
color $ui-dark-text-color
apply-theme(theme)
body[data-theme={theme}]
.menu-button
&:active
background-color get-theme-var(theme, 'noteList-backgroundColor')
color get-theme-var(theme, 'text-color')
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
body[data-theme="solarized-dark"]
.menu-button
&:active
background-color $ui-solarized-dark-noteList-backgroundColor
color $ui-solarized-dark-text-color
&:hover
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
.menu-button--active
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
.menu-button-label
color $ui-solarized-dark-text-color
&:hover
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
.menu-button--active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
color $ui-solarized-dark-text-color
color get-theme-var(theme, 'text-color')
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
.menu-button-label
color get-theme-var(theme, 'text-color')
.menu-button-star--active
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
.menu-button-label
color $ui-solarized-dark-text-color
&:hover
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
.menu-button-star--active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
color $ui-solarized-dark-text-color
color get-theme-var(theme, 'text-color')
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
.menu-button-label
color get-theme-var(theme, 'text-color')
.menu-button-trash--active
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
.menu-button-label
color $ui-solarized-dark-text-color
&:hover
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
.menu-button-trash--active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
color $ui-solarized-dark-text-color
color get-theme-var(theme, 'text-color')
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
.menu-button-label
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.menu-button
&:active
background-color $ui-monokai-noteList-backgroundColor
color $ui-monokai-text-color
&:hover
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.menu-button--active
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
.menu-button-label
color $ui-monokai-text-color
&:hover
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
.menu-button-label
color $ui-monokai-text-color
.menu-button-star--active
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
.menu-button-label
color $ui-monokai-text-color
&:hover
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
.menu-button-label
color $ui-monokai-text-color
.menu-button-trash--active
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
.menu-button-label
color $ui-monokai-text-color
&:hover
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
.menu-button-label
color $ui-monokai-text-color
body[data-theme="dracula"]
.menu-button
&:active
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color
.menu-button-star--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color
.menu-button-trash--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -5,7 +5,7 @@ import context from 'browser/lib/context'
import i18n from 'browser/lib/i18n'
class SnippetTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -14,7 +14,7 @@ class SnippetTab extends React.Component {
}
}
componentWillUpdate (nextProps) {
componentWillUpdate(nextProps) {
if (nextProps.snippet.name !== this.props.snippet.name) {
this.setState({
name: nextProps.snippet.name
@@ -22,34 +22,34 @@ class SnippetTab extends React.Component {
}
}
handleClick (e) {
handleClick(e) {
this.props.onClick(e)
}
handleContextMenu (e) {
handleContextMenu(e) {
context.popup([
{
label: i18n.__('Rename'),
click: (e) => this.handleRenameClick(e)
click: e => this.handleRenameClick(e)
}
])
}
handleRenameClick (e) {
handleRenameClick(e) {
this.startRenaming()
}
handleNameInputBlur (e) {
handleNameInputBlur(e) {
this.handleRename()
}
handleNameInputChange (e) {
handleNameInputChange(e) {
this.setState({
name: e.target.value
})
}
handleNameInputKeyDown (e) {
handleNameInputKeyDown(e) {
switch (e.keyCode) {
case 13:
this.handleRename()
@@ -63,84 +63,87 @@ class SnippetTab extends React.Component {
}
}
handleRename () {
this.setState({
isRenaming: false
}, () => {
if (this.props.snippet.name !== this.state.name) {
this.props.onRename(this.state.name)
handleRename() {
this.setState(
{
isRenaming: false
},
() => {
if (this.props.snippet.name !== this.state.name) {
this.props.onRename(this.state.name)
}
}
})
)
}
handleDeleteButtonClick (e) {
handleDeleteButtonClick(e) {
this.props.onDelete(e)
}
startRenaming () {
this.setState({
isRenaming: true
}, () => {
this.refs.name.focus()
this.refs.name.select()
})
startRenaming() {
this.setState(
{
isRenaming: true
},
() => {
this.refs.name.focus()
this.refs.name.select()
}
)
}
handleDragStart (e) {
handleDragStart(e) {
e.dataTransfer.dropEffect = 'move'
this.props.onDragStart(e)
}
handleDrop (e) {
handleDrop(e) {
this.props.onDrop(e)
}
render () {
render() {
const { isActive, snippet, isDeletable } = this.props
return (
<div styleName={isActive
? 'root--active'
: 'root'
}
>
{!this.state.isRenaming
? <button styleName='button'
onClick={(e) => this.handleClick(e)}
onDoubleClick={(e) => this.handleRenameClick(e)}
onContextMenu={(e) => this.handleContextMenu(e)}
onDragStart={(e) => this.handleDragStart(e)}
onDrop={(e) => this.handleDrop(e)}
<div styleName={isActive ? 'root--active' : 'root'}>
{!this.state.isRenaming ? (
<button
styleName='button'
onClick={e => this.handleClick(e)}
onDoubleClick={e => this.handleRenameClick(e)}
onContextMenu={e => this.handleContextMenu(e)}
onDragStart={e => this.handleDragStart(e)}
onDrop={e => this.handleDrop(e)}
draggable='true'
>
{snippet.name.trim().length > 0
? snippet.name
: <span styleName='button-unnamed'>
{i18n.__('Unnamed')}
</span>
}
{snippet.name.trim().length > 0 ? (
snippet.name
) : (
<span>{i18n.__('Unnamed')}</span>
)}
</button>
: <input styleName='input'
) : (
<input
styleName='input'
ref='name'
value={this.state.name}
onChange={(e) => this.handleNameInputChange(e)}
onBlur={(e) => this.handleNameInputBlur(e)}
onKeyDown={(e) => this.handleNameInputKeyDown(e)}
onChange={e => this.handleNameInputChange(e)}
onBlur={e => this.handleNameInputBlur(e)}
onKeyDown={e => this.handleNameInputKeyDown(e)}
/>
}
{isDeletable &&
<button styleName='deleteButton'
onClick={(e) => this.handleDeleteButtonClick(e)}
)}
{isDeletable && (
<button
styleName='deleteButton'
onClick={e => this.handleDeleteButtonClick(e)}
>
<i className='fa fa-times' />
</button>
}
)}
</div>
)
}
}
SnippetTab.propTypes = {
}
SnippetTab.propTypes = {}
export default CSSModules(SnippetTab, styles)

View File

@@ -100,103 +100,43 @@ body[data-theme="dark"]
color $ui-dark-text-color
transition 0.15s
body[data-theme="solarized-dark"]
.root
border-color $ui-solarized-dark-borderColor
&:hover
background-color $ui-solarized-dark-noteDetail-backgroundColor
transition 0.15s
apply-theme(theme)
body[data-theme={theme}]
.root
border-color get-theme-var(theme, 'borderColor')
&:hover
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
transition 0.15s
.deleteButton
color get-theme-var(theme, 'text-color')
transition 0.15s
.button
color get-theme-var(theme, 'text-color')
transition 0.15s
.root--active
color get-theme-var(theme, 'active-color')
background-color get-theme-var(theme, 'button-backgroundColor')
border-color get-theme-var(theme, 'borderColor')
.deleteButton
color $ui-solarized-dark-button--active-color
transition 0.15s
color get-theme-var(theme, 'text-color')
.button
color $ui-solarized-dark-button--active-color
transition 0.15s
color get-theme-var(theme, 'active-color')
.root--active
color $ui-solarized-dark-button--active-color
background-color $ui-solarized-dark-button-backgroundColor
border-color $ui-solarized-dark-borderColor
.deleteButton
color $ui-solarized-dark-button--active-color
.button
color $ui-solarized-dark-button--active-color
border none
color $ui-inactive-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
.button
border none
color $ui-solarized-dark-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
.input
background-color $ui-solarized-dark-noteDetail-backgroundColor
color $ui-solarized-dark-button--active-color
transition 0.15s
body[data-theme="monokai"]
.root
border-color $ui-monokai-borderColor
&:hover
background-color $ui-monokai-noteDetail-backgroundColor
.input
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
color get-theme-var(theme, 'text-color')
transition 0.15s
.deleteButton
color $ui-monokai-text-color
transition 0.15s
.button
color $ui-monokai-text-color
transition 0.15s
.root--active
color $ui-monokai-active-color
background-color $ui-monokai-button-backgroundColor
border-color $ui-monokai-borderColor
.deleteButton
color $ui-monokai-text-color
.button
color $ui-monokai-active-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.button
border none
color $ui-inactive-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
.input
background-color $ui-monokai-noteDetail-backgroundColor
color $ui-monokai-text-color
transition 0.15s
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
&:hover
background-color $ui-dracula-noteDetail-backgroundColor
transition 0.15s
.deleteButton
color $ui-dracula-text-color
transition 0.15s
.button
color $ui-dracula-text-color
transition 0.15s
.root--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
border-color $ui-dracula-borderColor
.deleteButton
color $ui-dracula-text-color
.button
color $ui-dracula-active-color
.button
border none
color $ui-inactive-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
.input
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -21,7 +21,9 @@ const FolderIcon = ({ className, color, isActive }) => {
/**
* @param {boolean} isActive
* @param {object} tooltipRef,
* @param {Function} handleButtonClick
* @param {Function} handleMouseEnter
* @param {Function} handleContextMenu
* @param {string} folderName
* @param {string} folderColor
@@ -35,7 +37,9 @@ const FolderIcon = ({ className, color, isActive }) => {
const StorageItem = ({
styles,
isActive,
tooltipRef,
handleButtonClick,
handleMouseEnter,
handleContextMenu,
folderName,
folderColor,
@@ -49,13 +53,15 @@ const StorageItem = ({
<button
styleName={isActive ? 'folderList-item--active' : 'folderList-item'}
onClick={handleButtonClick}
onMouseEnter={handleMouseEnter}
onContextMenu={handleContextMenu}
onDrop={handleDrop}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
{!isFolded &&
<DraggableIcon className={styles['folderList-item-reorder']} />}
{!isFolded && (
<DraggableIcon className={styles['folderList-item-reorder']} />
)}
<span
styleName={
isFolded ? 'folderList-item-name--folded' : 'folderList-item-name'
@@ -70,18 +76,23 @@ const StorageItem = ({
? _.truncate(folderName, { length: 1, omission: '' })
: folderName}
</span>
{!isFolded &&
_.isNumber(noteCount) &&
<span styleName='folderList-item-noteCount'>{noteCount}</span>}
{isFolded &&
<span styleName='folderList-item-tooltip'>{folderName}</span>}
{!isFolded && _.isNumber(noteCount) && (
<span styleName='folderList-item-noteCount'>{noteCount}</span>
)}
{isFolded && (
<span styleName='folderList-item-tooltip' ref={tooltipRef}>
{folderName}
</span>
)}
</button>
)
}
StorageItem.propTypes = {
isActive: PropTypes.bool.isRequired,
tooltipRef: PropTypes.object,
handleButtonClick: PropTypes.func,
handleMouseEnter: PropTypes.func,
handleContextMenu: PropTypes.func,
folderName: PropTypes.string.isRequired,
folderColor: PropTypes.string,

View File

@@ -60,6 +60,7 @@
border-bottom-right-radius 2px
height 34px
line-height 32px
transition-property opacity
.folderList-item:hover, .folderList-item--active:hover
.folderList-item-tooltip
@@ -120,59 +121,28 @@ body[data-theme="dark"]
color $ui-dark-text-color
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
body[data-theme="solarized-dark"]
.folderList-item
&:hover
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
&:active
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.folderList-item
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
&:active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.folderList-item--active
@extend .folderList-item
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
&:active
background-color $ui-solarized-dark-button-backgroundColor
&:hover
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
.folderList-item--active
@extend .folderList-item
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
&:active
background-color get-theme-var(theme, 'button-backgroundColor')
&:hover
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
body[data-theme="monokai"]
.folderList-item
&:hover
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
&:active
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.folderList-item--active
@extend .folderList-item
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
&:active
background-color $ui-monokai-button-backgroundColor
&:hover
color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor
body[data-theme="dracula"]
.folderList-item
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
&:active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.folderList-item--active
@extend .folderList-item
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
&:active
background-color $ui-dracula-button-backgroundColor
&:hover
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
for theme in $themes
apply-theme(theme)

View File

@@ -1,18 +1,20 @@
/**
* @fileoverview Micro component for showing StorageList
*/
* @fileoverview Micro component for showing StorageList
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './StorageList.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {Array} storageList
*/
* @param {Array} storageList
*/
const StorageList = ({storageList, isFolded}) => (
const StorageList = ({ storageList, isFolded }) => (
<div styleName={isFolded ? 'storageList-folded' : 'storageList'}>
{storageList.length > 0 ? storageList : (
{storageList.length > 0 ? (
storageList
) : (
<div styleName='storageList-empty'>No storage mount.</div>
)}
</div>

View File

@@ -1,30 +1,58 @@
/**
* @fileoverview Micro component for showing TagList.
*/
* @fileoverview Micro component for showing TagList.
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './TagListItem.styl'
import CSSModules from 'browser/lib/CSSModules'
/**
* @param {string} name
* @param {Function} handleClickTagListItem
* @param {Function} handleClickNarrowToTag
* @param {boolean} isActive
* @param {boolean} isRelated
* @param {string} bgColor tab backgroundColor
*/
* @param {string} name
* @param {Function} handleClickTagListItem
* @param {Function} handleClickNarrowToTag
* @param {boolean} isActive
* @param {boolean} isRelated
* @param {string} bgColor tab backgroundColor
*/
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count, color}) => (
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
{isRelated
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
const TagListItem = ({
name,
handleClickTagListItem,
handleClickNarrowToTag,
handleContextMenu,
isActive,
isRelated,
count,
color
}) => (
<div
styleName='tagList-itemContainer'
onContextMenu={e => handleContextMenu(e, name)}
>
{isRelated ? (
<button
styleName={
isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'
}
onClick={() => handleClickNarrowToTag(name)}
>
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
</button>
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
}
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-color' style={{backgroundColor: color || 'transparent'}} />
) : (
<div
styleName={
isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'
}
/>
)}
<button
styleName={isActive ? 'tagList-item-active' : 'tagList-item'}
onClick={() => handleClickTagListItem(name)}
>
<span
styleName='tagList-item-color'
style={{ backgroundColor: color || 'transparent' }}
/>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>

View File

@@ -94,23 +94,30 @@ body[data-theme="white"]
.tagList-item-count
color $ui-text-color
body[data-theme="dark"]
.tagList-item
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
&:active
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.tagList-item
color get-theme-var(theme, 'inactive-text-color')
&:hover
color get-theme-var(theme, 'text-color')
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%)
&:active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button--active-backgroundColor')
.tagList-item-active
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
&:active
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
&:hover
color $ui-dark-text-color
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
.tagList-item-count
color $ui-dark-button--active-color
.tagList-item-active
background-color get-theme-var(theme, 'button--active-backgroundColor')
color get-theme-var(theme, 'text-color')
&:active
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%)
&:hover
color get-theme-var(theme, 'text-color')
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%)
.tagList-item-count
color get-theme-var(theme, 'button--active-color')
for theme in 'dark'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -11,17 +11,20 @@ import styles from './TodoListPercentage.styl'
* @param {number} percentageOfTodo
*/
const TodoListPercentage = ({
percentageOfTodo, onClearCheckboxClick
}) => (
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
const TodoListPercentage = ({ percentageOfTodo, onClearCheckboxClick }) => (
<div
styleName='percentageBar'
style={{ display: isNaN(percentageOfTodo) ? 'none' : '' }}
>
<div styleName='progressBar' style={{ width: `${percentageOfTodo}%` }}>
<div styleName='progressBarInner'>
<p styleName='percentageText'>{percentageOfTodo}%</p>
</div>
</div>
<div styleName='todoClear'>
<p styleName='todoClearText' onClick={(e) => onClearCheckboxClick(e)}>clear</p>
<p styleName='todoClearText' onClick={e => onClearCheckboxClick(e)}>
clear
</p>
</div>
</div>
)

View File

@@ -71,25 +71,19 @@ body[data-theme="solarized-dark"]
.todoClearText
color #fdf6e3
body[data-theme="monokai"]
.percentageBar
background-color: $ui-monokai-borderColor
apply-theme(theme)
body[data-theme={theme}]
.percentageBar
background-color: get-theme-var(theme, 'borderColor')
.progressBar
background-color $ui-monokai-active-color
.progressBar
background-color get-theme-var(theme, 'active-color')
.percentageText
color $ui-monokai-text-color
.percentageText
color get-theme-var(theme, 'text-color')
body[data-theme="dracula"]
.percentageBar
background-color $ui-dracula-borderColor
for theme in 'dracula'
apply-theme(theme)
.progressBar
background-color: $ui-dracula-active-color
.percentageText
color $ui-dracula-text-color
.percentageText
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -8,27 +8,30 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './TodoProcess.styl'
const TodoProcess = ({
todoStatus: {
total: totalTodo,
completed: completedTodo
}
todoStatus: { total: totalTodo, completed: completedTodo }
}) => (
<div styleName='todo-process' style={{display: totalTodo > 0 ? '' : 'none'}}>
<div
styleName='todo-process'
style={{ display: totalTodo > 0 ? '' : 'none' }}
>
<div styleName='todo-process-text'>
<i className='fa fa-fw fa-check-square-o' />
{completedTodo} of {totalTodo}
</div>
<div styleName='todo-process-bar'>
<div styleName='todo-process-bar--inner' style={{width: parseInt(completedTodo / totalTodo * 100) + '%'}} />
<div
styleName='todo-process-bar--inner'
style={{ width: parseInt((completedTodo / totalTodo) * 100) + '%' }}
/>
</div>
</div>
)
TodoProcess.propTypes = {
todoStatus: {
todoStatus: PropTypes.exact({
total: PropTypes.number.isRequired,
completed: PropTypes.number.isRequired
}
})
}
export default CSSModules(TodoProcess, styles)

View File

@@ -124,40 +124,34 @@ hr
border-bottom solid 1px borderColor
margin 15px 0
h1, h2, h3, h4, h5, h6
margin 1em 0 1.5em
line-height 1.4
font-weight bold
word-wrap break-word
padding .2em 0 .2em
h1
font-size 2.55em
padding-bottom 0.3em
line-height 1.2em
line-height 1.2
border-bottom solid 1px borderColor
margin 1em 0 0.44em
&:first-child
margin-top 0
h2
font-size 1.75em
padding-bottom 0.3em
line-height 1.225em
line-height 1.225
border-bottom solid 1px borderColor
margin 1em 0 0.57em
&:first-child
margin-top 0
h3
font-size 1.5em
line-height 1.43em
margin 1em 0 0.66em
line-height 1.43
h4
font-size 1.25em
line-height 1.4em
margin 1em 0 0.8em
line-height 1.4
h5
font-size 1em
line-height 1.4em
margin 1em 0 1em
line-height 1.1
h6
font-size 1em
line-height 1.4em
margin 1em 0 1em
color #777
p
line-height 1.6em
@@ -165,6 +159,7 @@ p
white-space pre-line
word-wrap break-word
img
cursor zoom-in
max-width 100%
strong, b
font-weight bold
@@ -362,7 +357,10 @@ admonition_types = {
danger: {color: #c2185b, icon: "block"},
caution: {color: #ffa726, icon: "warning"},
error: {color: #d32f2f, icon: "error_outline"},
attention: {color: #455a64, icon: "priority_high"}
question: {color: #64dd17, icon: "help_outline"},
quote: {color: #9e9e9e, icon: "format_quote"},
abstract: {color: #00b0ff, icon: "subject"},
attention: {color: #455a64, icon: "priority_high"},
}
for name, val in admonition_types
@@ -423,6 +421,9 @@ pre.fence
canvas, svg
max-width 100% !important
svg[ratio]
width 100%
.gallery
width 100%
height 50vh
@@ -443,6 +444,44 @@ pre.fence
color $ui-text-color
background-color $ui-tag-backgroundColor
.markdownIt-TOC-wrapper
list-style none
position fixed
right 0
top 0
margin-left 15px
z-index 1000
transition transform .2s ease-in-out
transform translateX(100%)
.markdownIt-TOC
display block
max-height 90vh
overflow-y auto
padding 25px
padding-left 38px
&,
&:before
background-color $ui-dark-backgroundColor
color: $ui-dark-text-color
&:hover
transform translateX(-15px)
&:before
content 'TOC'
position absolute
width 60px
height 30px
top 60px
left -29px
display flex
align-items center
justify-content center
transform-origin top left
transform rotate(-90deg)
themeDarkBackground = darken(#21252B, 10%)
themeDarkText = #f9f9f9
themeDarkBorder = lighten(themeDarkBackground, 20%)
@@ -510,137 +549,63 @@ body[data-theme="dark"]
color $ui-dark-text-color
background-color $ui-dark-tag-backgroundColor
themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
themeSolarizedDarkTableBorder = themeDarkBorder
.markdownIt-TOC-wrapper
&,
&:before
background-color darken(themeDarkBackground, 5%)
color themeDarkText
body[data-theme="solarized-dark"]
color $ui-solarized-dark-text-color
border-color themeDarkBorder
background-color $ui-solarized-dark-noteDetail-backgroundColor
table
thead
tr
background-color themeSolarizedDarkTableHead
th
border-color themeSolarizedDarkTableBorder
&:last-child
border-right solid 1px themeSolarizedDarkTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeSolarizedDarkTableOdd
tr:nth-child(2n)
background-color themeSolarizedDarkTableEven
td
border-color themeSolarizedDarkTableBorder
&:last-child
border-right solid 1px themeSolarizedDarkTableBorder
dl
apply-theme(theme)
body[data-theme={theme}]
color get-theme-var(theme, 'text-color')
border-color themeDarkBorder
background-color themeSolarizedDarkTableHead
dt
border-color themeDarkBorder
dd
border-color themeDarkBorder
background-color $ui-solarized-dark-noteDetail-backgroundColor
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
table
thead
tr
background-color get-theme-var(theme, 'table-head-backgroundColor')
th
border-color get-theme-var(theme, 'table-borderColor')
&:last-child
border-right solid 1px get-theme-var(theme, 'table-borderColor')
tbody
tr:nth-child(2n + 1)
background-color get-theme-var(theme, 'table-odd-backgroundColor')
tr:nth-child(2n)
background-color get-theme-var(theme, 'table-even-backgroundColor')
td
border-color get-theme-var(theme, 'table-borderColor')
&:last-child
border-right solid 1px get-theme-var(theme, 'table-borderColor')
kbd
background-color get-theme-var(theme, 'kbd-backgroundColor')
color get-theme-var(theme, 'kbd-color')
pre.fence
.gallery
.carousel-main, .carousel-footer
background-color $ui-solarized-dark-noteDetail-backgroundColor
.prev, .next
color $ui-solarized-dark-button--active-color
background-color $ui-solarized-dark-button-backgroundColor
dl
border-color themeDarkBorder
background-color get-theme-var(theme, 'table-head-backgroundColor')
dt
border-color themeDarkBorder
dd
border-color themeDarkBorder
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor
themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
themeMonokaiTableHead = themeMonokaiTableEven
themeMonokaiTableBorder = themeDarkBorder
pre.fence
.gallery
.carousel-main, .carousel-footer
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
.prev, .next
color get-theme-var(theme, 'button--active-color')
background-color get-theme-var(theme, 'button-backgroundColor')
body[data-theme="monokai"]
color $ui-monokai-text-color
border-color themeDarkBorder
background-color $ui-monokai-noteDetail-backgroundColor
table
thead
tr
background-color themeMonokaiTableHead
th
border-color themeMonokaiTableBorder
&:last-child
border-right solid 1px themeMonokaiTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeMonokaiTableOdd
tr:nth-child(2n)
background-color themeMonokaiTableEven
td
border-color themeMonokaiTableBorder
&:last-child
border-right solid 1px themeMonokaiTableBorder
kbd
background-color themeDarkBackground
.markdownIt-TOC-wrapper
&,
&:before
background-color darken(get-theme-var(theme, 'noteDetail-backgroundColor'), 15%)
color themeDarkText
dl
border-color themeDarkBorder
background-color themeMonokaiTableHead
dt
border-color themeDarkBorder
dd
border-color themeDarkBorder
background-color $ui-monokai-noteDetail-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
pre.fence
.gallery
.carousel-main, .carousel-footer
background-color $ui-monokai-noteDetail-backgroundColor
.prev, .next
color $ui-monokai-button--active-color
background-color $ui-monokai-button-backgroundColor
themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
themeDraculaTableHead = themeDraculaTableEven
themeDraculaTableBorder = themeDarkBorder
body[data-theme="dracula"]
color $ui-dracula-text-color
border-color themeDarkBorder
background-color $ui-dracula-noteDetail-backgroundColor
table
thead
tr
background-color themeDraculaTableHead
th
border-color themeDraculaTableBorder
&:last-child
border-right solid 1px themeDraculaTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeDraculaTableOdd
tr:nth-child(2n)
background-color themeDraculaTableEven
td
border-color themeDraculaTableBorder
&:last-child
border-right solid 1px themeDraculaTableBorder
kbd
background-color themeDarkBackground
dl
border-color themeDarkBorder
background-color themeDraculaTableHead
dt
border-color themeDarkBorder
dd
border-color themeDarkBorder
background-color $ui-dracula-noteDetail-backgroundColor
pre.fence
.gallery
.carousel-main, .carousel-footer
background-color $ui-dracula-noteDetail-backgroundColor
.prev, .next
color $ui-dracula-button--active-color
background-color $ui-dracula-button-backgroundColor
for theme in $themes
apply-theme(theme)

View File

@@ -1,4 +1,5 @@
import mermaidAPI from 'mermaid'
import mermaidAPI from 'mermaid/dist/mermaid.min.js'
import uiThemes from 'browser/lib/ui-themes'
// fixes bad styling in the mermaid dark theme
const darkThemeStyling = `
@@ -6,11 +7,11 @@ const darkThemeStyling = `
fill: white;
}`
function getRandomInt (min, max) {
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
function getId () {
function getId() {
const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let id = 'm-'
for (let i = 0; i < 7; i++) {
@@ -19,20 +20,48 @@ function getId () {
return id
}
function render (element, content, theme) {
function render(element, content, theme, enableHTMLLabel) {
try {
const height = element.attributes.getNamedItem('data-height')
if (height && height.value !== 'undefined') {
const isPredefined = height && height.value !== 'undefined'
if (isPredefined) {
element.style.height = height.value + 'vh'
}
const isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
const isDarkTheme = uiThemes.some(
item => item.name === theme && item.isDark
)
mermaidAPI.initialize({
theme: isDarkTheme ? 'dark' : 'default',
themeCSS: isDarkTheme ? darkThemeStyling : '',
useMaxWidth: false
flowchart: {
htmlLabels: enableHTMLLabel
},
gantt: {
useWidth: element.clientWidth
}
})
mermaidAPI.render(getId(), content, (svgGraph) => {
mermaidAPI.render(getId(), content, svgGraph => {
element.innerHTML = svgGraph
if (!isPredefined) {
const el = element.firstChild
const viewBox = el.getAttribute('viewBox').split(' ')
let ratio = viewBox[2] / viewBox[3]
if (el.style.maxWidth) {
const maxWidth = parseFloat(el.style.maxWidth)
ratio *= el.parentNode.clientWidth / maxWidth
}
el.setAttribute('ratio', ratio)
el.setAttribute('height', el.parentNode.clientWidth / ratio)
}
})
} catch (e) {
element.className = 'mermaid-error'

View File

@@ -0,0 +1,78 @@
export const languageMaps = {
brainfuck: 'Brainfuck',
cpp: 'C++',
cs: 'C#',
clojure: 'Clojure',
'clojure-repl': 'ClojureScript',
cmake: 'CMake',
coffeescript: 'CoffeeScript',
crystal: 'Crystal',
css: 'CSS',
d: 'D',
dart: 'Dart',
delphi: 'Pascal',
diff: 'Diff',
django: 'Django',
dockerfile: 'Dockerfile',
ebnf: 'EBNF',
elm: 'Elm',
erlang: 'Erlang',
'erlang-repl': 'Erlang',
fortran: 'Fortran',
fsharp: 'F#',
gherkin: 'Gherkin',
go: 'Go',
groovy: 'Groovy',
haml: 'HAML',
haskell: 'Haskell',
haxe: 'Haxe',
http: 'HTTP',
ini: 'toml',
java: 'Java',
javascript: 'JavaScript',
json: 'JSON',
julia: 'Julia',
kotlin: 'Kotlin',
less: 'LESS',
livescript: 'LiveScript',
lua: 'Lua',
markdown: 'Markdown',
mathematica: 'Mathematica',
nginx: 'Nginx',
nsis: 'NSIS',
objectivec: 'Objective-C',
ocaml: 'Ocaml',
perl: 'Perl',
php: 'PHP',
powershell: 'PowerShell',
properties: 'Properties files',
protobuf: 'ProtoBuf',
python: 'Python',
puppet: 'Puppet',
q: 'Q',
r: 'R',
ruby: 'Ruby',
rust: 'Rust',
sas: 'SAS',
scala: 'Scala',
scheme: 'Scheme',
scss: 'SCSS',
shell: 'Shell',
smalltalk: 'Smalltalk',
sml: 'SML',
sql: 'SQL',
stylus: 'Stylus',
swift: 'Swift',
tcl: 'Tcl',
tex: 'LaTex',
typescript: 'TypeScript',
twig: 'Twig',
vbnet: 'VB.NET',
vbscript: 'VBScript',
verilog: 'Verilog',
vhdl: 'VHDL',
xml: 'HTML',
xquery: 'XQuery',
yaml: 'YAML',
elixir: 'Elixir'
}

View File

@@ -1,5 +1,5 @@
import CSSModules from 'react-css-modules'
export default function (component, styles) {
return CSSModules(component, styles, {errorWhenNotFound: false})
export default function(component, styles) {
return CSSModules(component, styles, { handleNotFoundStyleName: 'log' })
}

View File

@@ -11,6 +11,10 @@ const languages = [
name: 'Chinese (zh-TW)',
locale: 'zh-TW'
},
{
name: 'Czech',
locale: 'cs'
},
{
name: 'Danish',
locale: 'da'
@@ -62,24 +66,25 @@ const languages = [
{
name: 'Spanish',
locale: 'es-ES'
}, {
},
{
name: 'Turkish',
locale: 'tr'
}, {
},
{
name: 'Thai',
locale: 'th'
}
]
module.exports = {
getLocales () {
return languages.reduce(function (localeList, locale) {
getLocales() {
return languages.reduce(function(localeList, locale) {
localeList.push(locale.locale)
return localeList
}, [])
},
getLanguages () {
getLanguages() {
return languages
}
}

View File

@@ -1,43 +1,43 @@
class MutableMap {
constructor (iterable) {
constructor(iterable) {
this._map = new Map(iterable)
Object.defineProperty(this, 'size', {
get: () => this._map.size,
set: function (value) {
set: function(value) {
this['size'] = value
}
})
}
get (...args) {
get(...args) {
return this._map.get(...args)
}
set (...args) {
set(...args) {
return this._map.set(...args)
}
delete (...args) {
delete(...args) {
return this._map.delete(...args)
}
has (...args) {
has(...args) {
return this._map.has(...args)
}
clear (...args) {
clear(...args) {
return this._map.clear(...args)
}
forEach (...args) {
forEach(...args) {
return this._map.forEach(...args)
}
[Symbol.iterator] () {
[Symbol.iterator]() {
return this._map[Symbol.iterator]()
}
map (cb) {
map(cb) {
const result = []
for (const [key, value] of this._map) {
result.push(cb(value, key))
@@ -45,7 +45,7 @@ class MutableMap {
return result
}
toJS () {
toJS() {
const result = {}
for (let [key, value] of this._map) {
if (value instanceof MutableSet || value instanceof MutableMap) {
@@ -58,42 +58,42 @@ class MutableMap {
}
class MutableSet {
constructor (iterable) {
constructor(iterable) {
this._set = new Set(iterable)
Object.defineProperty(this, 'size', {
get: () => this._set.size,
set: function (value) {
set: function(value) {
this['size'] = value
}
})
}
add (...args) {
add(...args) {
return this._set.add(...args)
}
delete (...args) {
delete(...args) {
return this._set.delete(...args)
}
forEach (...args) {
forEach(...args) {
return this._set.forEach(...args)
}
[Symbol.iterator] () {
[Symbol.iterator]() {
return this._set[Symbol.iterator]()
}
map (cb) {
map(cb) {
const result = []
this._set.forEach(function (value, key) {
this._set.forEach(function(value, key) {
result.push(cb(value, key))
})
return result
}
toJS () {
toJS() {
return Array.from(this._set)
}
}

View File

@@ -5,13 +5,13 @@ const BOOSTNOTERC = '.boostnoterc'
const homePath = global.process.env.HOME || global.process.env.USERPROFILE
const _boostnotercPath = path.join(homePath, BOOSTNOTERC)
export function parse (boostnotercPath = _boostnotercPath) {
export function parse(boostnotercPath = _boostnotercPath) {
if (!sander.existsSync(boostnotercPath)) return {}
try {
return JSON.parse(sander.readFileSync(boostnotercPath).toString())
} catch (e) {
console.warn(e)
console.warn('Your .boostnoterc is broken so it\'s not used.')
console.warn("Your .boostnoterc is broken so it's not used.")
return {}
}
}

View File

@@ -0,0 +1,92 @@
import crypto from 'crypto'
import fs from 'fs'
import consts from './consts'
class SnippetManager {
constructor() {
this.defaultSnippet = [
{
id: crypto.randomBytes(16).toString('hex'),
name: 'Dummy text',
prefix: ['lorem', 'ipsum'],
content:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
}
]
this.snippets = []
this.expandSnippet = this.expandSnippet.bind(this)
this.init = this.init.bind(this)
this.assignSnippets = this.assignSnippets.bind(this)
}
init() {
if (fs.existsSync(consts.SNIPPET_FILE)) {
try {
this.snippets = JSON.parse(
fs.readFileSync(consts.SNIPPET_FILE, { encoding: 'UTF-8' })
)
} catch (error) {
console.log('Error while parsing snippet file')
}
return
}
fs.writeFileSync(
consts.SNIPPET_FILE,
JSON.stringify(this.defaultSnippet, null, 4),
'utf8'
)
this.snippets = this.defaultSnippet
}
assignSnippets(snippets) {
this.snippets = snippets
}
expandSnippet(wordBeforeCursor, cursor, cm) {
const templateCursorString = ':{}'
for (let i = 0; i < this.snippets.length; i++) {
if (this.snippets[i].prefix.indexOf(wordBeforeCursor.text) === -1) {
continue
}
if (this.snippets[i].content.indexOf(templateCursorString) !== -1) {
const snippetLines = this.snippets[i].content.split('\n')
let cursorLineNumber = 0
let cursorLinePosition = 0
let cursorIndex
for (let j = 0; j < snippetLines.length; j++) {
cursorIndex = snippetLines[j].indexOf(templateCursorString)
if (cursorIndex !== -1) {
cursorLineNumber = j
cursorLinePosition = cursorIndex
break
}
}
cm.replaceRange(
this.snippets[i].content.replace(templateCursorString, ''),
wordBeforeCursor.range.from,
wordBeforeCursor.range.to
)
cm.setCursor({
line: cursor.line + cursorLineNumber,
ch: cursorLinePosition + cursor.ch - wordBeforeCursor.text.length
})
} else {
cm.replaceRange(
this.snippets[i].content,
wordBeforeCursor.range.from,
wordBeforeCursor.range.to
)
}
return true
}
return false
}
}
const manager = new SnippetManager()
export default manager

View File

@@ -1,44 +1,44 @@
import { Point } from '@susisu/mte-kernel'
export default class TextEditorInterface {
constructor (editor) {
constructor(editor) {
this.editor = editor
this.doc = editor.getDoc()
this.transaction = false
}
getCursorPosition () {
getCursorPosition() {
const { line, ch } = this.doc.getCursor()
return new Point(line, ch)
}
setCursorPosition (pos) {
setCursorPosition(pos) {
this.doc.setCursor({
line: pos.row,
ch: pos.column
})
}
setSelectionRange (range) {
setSelectionRange(range) {
this.doc.setSelection(
{ line: range.start.row, ch: range.start.column },
{ line: range.end.row, ch: range.end.column }
)
}
getLastRow () {
getLastRow() {
return this.doc.lineCount() - 1
}
acceptsTableEdit () {
acceptsTableEdit() {
return true
}
getLine (row) {
getLine(row) {
return this.doc.getLine(row)
}
insertLine (row, line) {
insertLine(row, line) {
const lastRow = this.getLastRow()
if (row > lastRow) {
const lastLine = this.getLine(lastRow)
@@ -56,7 +56,7 @@ export default class TextEditorInterface {
}
}
deleteLine (row) {
deleteLine(row) {
const lastRow = this.getLastRow()
if (row >= lastRow) {
if (lastRow > 0) {
@@ -76,15 +76,11 @@ export default class TextEditorInterface {
)
}
} else {
this.doc.replaceRange(
'',
{ line: row, ch: 0 },
{ line: row + 1, ch: 0 }
)
this.doc.replaceRange('', { line: row, ch: 0 }, { line: row + 1, ch: 0 })
}
}
replaceLines (startRow, endRow, lines) {
replaceLines(startRow, endRow, lines) {
const lastRow = this.getLastRow()
if (endRow > lastRow) {
const lastLine = this.getLine(lastRow)
@@ -102,7 +98,7 @@ export default class TextEditorInterface {
}
}
transact (func) {
transact(func) {
this.transaction = true
func()
this.transaction = false

View File

@@ -3,17 +3,18 @@ import i18n from 'browser/lib/i18n'
const { remote } = electron
const { dialog } = remote
export function confirmDeleteNote (confirmDeletion, permanent) {
export function confirmDeleteNote(confirmDeletion, permanent) {
if (confirmDeletion || permanent) {
const alertConfig = {
ype: 'warning',
type: 'warning',
message: i18n.__('Confirm note deletion'),
detail: i18n.__('This will permanently remove this note.'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
}
const dialogButtonIndex = dialog.showMessageBox(
remote.getCurrentWindow(), alertConfig
remote.getCurrentWindow(),
alertConfig
)
return dialogButtonIndex === 0

View File

@@ -3,18 +3,59 @@ const fs = require('sander')
const { remote } = require('electron')
const { app } = remote
const themePath = process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './node_modules/codemirror/theme')
: require('path').resolve('./node_modules/codemirror/theme')
const themes = fs.readdirSync(themePath)
.map((themePath) => {
return themePath.substring(0, themePath.lastIndexOf('.'))
})
themes.splice(themes.indexOf('solarized'), 1, 'solarized dark', 'solarized light')
const CODEMIRROR_THEME_PATH = 'node_modules/codemirror/theme'
const CODEMIRROR_EXTRA_THEME_PATH = 'extra_scripts/codemirror/theme'
const snippetFile = process.env.NODE_ENV !== 'test'
? path.join(app.getPath('userData'), 'snippets.json')
: '' // return nothing as we specified different path to snippets.json in test
const isProduction = process.env.NODE_ENV === 'production'
const paths = [
isProduction
? path.join(app.getAppPath(), CODEMIRROR_THEME_PATH)
: path.resolve(CODEMIRROR_THEME_PATH),
isProduction
? path.join(app.getAppPath(), CODEMIRROR_EXTRA_THEME_PATH)
: path.resolve(CODEMIRROR_EXTRA_THEME_PATH)
]
const themes = paths
.map(directory =>
fs.readdirSync(directory).map(file => {
const name = file.substring(0, file.lastIndexOf('.'))
return {
name,
path: path.join(directory, file),
className: `cm-s-${name}`
}
})
)
.reduce((accumulator, value) => accumulator.concat(value), [])
.sort((a, b) => a.name.localeCompare(b.name))
themes.splice(
themes.findIndex(({ name }) => name === 'solarized'),
1,
{
name: 'solarized dark',
path: path.join(paths[0], 'solarized.css'),
className: `cm-s-solarized cm-s-dark`
},
{
name: 'solarized light',
path: path.join(paths[0], 'solarized.css'),
className: `cm-s-solarized cm-s-light`
}
)
themes.splice(0, 0, {
name: 'default',
path: path.join(paths[0], 'elegant.css'),
className: `cm-s-default`
})
const snippetFile =
process.env.NODE_ENV !== 'test'
? path.join(app.getPath('userData'), 'snippets.json')
: '' // return nothing as we specified different path to snippets.json in test
const consts = {
FOLDER_COLORS: [
@@ -35,7 +76,7 @@ const consts = {
'Dodger Blue',
'Violet Eggplant'
],
THEMES: ['default'].concat(themes),
THEMES: themes,
SNIPPET_FILE: snippetFile,
DEFAULT_EDITOR_FONT_FAMILY: [
'Monaco',

View File

@@ -1,9 +1,9 @@
const { remote } = require('electron')
const { Menu, MenuItem } = remote
function popup (templates) {
function popup(templates) {
const menu = new Menu()
templates.forEach((item) => {
templates.forEach(item => {
menu.append(new MenuItem(item))
})
menu.popup(remote.getCurrentWindow())

View File

@@ -1,6 +1,12 @@
const {remote} = require('electron')
const {Menu} = remote.require('electron')
import i18n from 'browser/lib/i18n'
import fs from 'fs'
const { remote } = require('electron')
const { Menu } = remote.require('electron')
const { clipboard } = remote.require('electron')
const { shell } = remote.require('electron')
const spellcheck = require('./spellcheck')
const uri2path = require('file-uri-to-path')
/**
* Creates the context menu that is shown when there is a right click in the editor of a (not-snippet) note.
@@ -10,11 +16,16 @@ const spellcheck = require('./spellcheck')
* @param {MouseEvent} event that has triggered the creation of the context menu
* @returns {Electron.Menu} The created electron context menu
*/
const buildEditorContextMenu = function (editor, event) {
if (editor == null || event == null || event.pageX == null || event.pageY == null) {
const buildEditorContextMenu = function(editor, event) {
if (
editor == null ||
event == null ||
event.pageX == null ||
event.pageY == null
) {
return null
}
const cursor = editor.coordsChar({left: event.pageX, top: event.pageY})
const cursor = editor.coordsChar({ left: event.pageX, top: event.pageY })
const wordRange = editor.findWordAt(cursor)
const word = editor.getRange(wordRange.anchor, wordRange.head)
const existingMarks = editor.findMarks(wordRange.anchor, wordRange.head) || []
@@ -34,32 +45,108 @@ const buildEditorContextMenu = function (editor, event) {
isMisspelled: isMisspelled,
spellingSuggestions: suggestion
}
const template = [{
role: 'cut'
}, {
role: 'copy'
}, {
role: 'paste'
}, {
role: 'selectall'
}]
const template = [
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
{
role: 'selectall'
}
]
if (selection.isMisspelled) {
const suggestions = selection.spellingSuggestions
template.unshift.apply(template, suggestions.map(function (suggestion) {
return {
label: suggestion,
click: function (suggestion) {
if (editor != null) {
editor.replaceRange(suggestion.label, wordRange.anchor, wordRange.head)
template.unshift.apply(
template,
suggestions
.map(function(suggestion) {
return {
label: suggestion,
click: function(suggestion) {
if (editor != null) {
editor.replaceRange(
suggestion.label,
wordRange.anchor,
wordRange.head
)
}
}
}
}
}
}).concat({
type: 'separator'
}))
})
.concat({
type: 'separator'
})
)
}
return Menu.buildFromTemplate(template)
}
module.exports = buildEditorContextMenu
/**
* Creates the context menu that is shown when there is a right click Markdown preview of a (not-snippet) note.
* @param {MarkdownPreview} markdownPreview
* @param {MouseEvent} event that has triggered the creation of the context menu
* @returns {Electron.Menu} The created electron context menu
*/
const buildMarkdownPreviewContextMenu = function(markdownPreview, event) {
if (
markdownPreview == null ||
event == null ||
event.pageX == null ||
event.pageY == null
) {
return null
}
// Default context menu inclusions
const template = [
{
role: 'copy'
},
{
role: 'selectall'
}
]
if (
event.target.tagName.toLowerCase() === 'a' &&
event.target.getAttribute('href')
) {
// Link opener for files on the local system pointed to by href
const href = event.target.href
const isLocalFile = href.startsWith('file:')
if (isLocalFile) {
const absPath = uri2path(href)
try {
if (fs.lstatSync(absPath).isFile()) {
template.push({
label: i18n.__('Show in explorer'),
click: e => shell.showItemInFolder(absPath)
})
}
} catch (e) {
console.log(
'Error while evaluating if the file is locally available',
e
)
}
}
// Add option to context menu to copy url
template.push({
label: i18n.__('Copy Url'),
click: e => clipboard.writeText(href)
})
}
return Menu.buildFromTemplate(template)
}
module.exports = {
buildEditorContextMenu: buildEditorContextMenu,
buildMarkdownPreviewContextMenu: buildMarkdownPreviewContextMenu
}

View File

@@ -1,4 +1,4 @@
export default function convertModeName (name) {
export default function convertModeName(name) {
switch (name) {
case 'ejs':
return 'Embedded Javascript'

View File

@@ -1,5 +1,21 @@
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl'], alias: ['styl']})
CodeMirror.modeInfo.push({name: 'Elixir', mime: 'text/x-elixir', mode: 'elixir', ext: ['ex']})
const stylusCodeInfo = CodeMirror.modeInfo.find(info => info.name === 'Stylus')
if (stylusCodeInfo == null) {
CodeMirror.modeInfo.push({
name: 'Stylus',
mime: 'text/x-styl',
mode: 'stylus',
ext: ['styl'],
alias: ['styl']
})
} else {
stylusCodeInfo.alias = ['styl']
}
CodeMirror.modeInfo.push({
name: 'Elixir',
mime: 'text/x-elixir',
mode: 'elixir',
ext: ['ex']
})

View File

@@ -8,7 +8,7 @@ import moment from 'moment'
* @param {mixed}
* @return {string}
*/
export function formatDate (date) {
export function formatDate(date) {
const m = moment(date)
if (!m.isValid()) {
throw Error('Invalid argument.')

View File

@@ -1,4 +1,8 @@
export function findNoteTitle (value, enableFrontMatterTitle, frontMatterTitleField = 'title') {
export function findNoteTitle(
value,
enableFrontMatterTitle,
frontMatterTitleField = 'title'
) {
const splitted = value.split('\n')
let title = null
let isInsideCodeBlock = false
@@ -6,8 +10,13 @@ export function findNoteTitle (value, enableFrontMatterTitle, frontMatterTitleFi
if (splitted[0] === '---') {
let line = 0
while (++line < splitted.length) {
if (enableFrontMatterTitle && splitted[line].startsWith(frontMatterTitleField + ':')) {
title = splitted[line].substring(frontMatterTitleField.length + 1).trim()
if (
enableFrontMatterTitle &&
splitted[line].startsWith(frontMatterTitleField + ':')
) {
title = splitted[line]
.substring(frontMatterTitleField.length + 1)
.trim()
break
}
@@ -22,11 +31,15 @@ export function findNoteTitle (value, enableFrontMatterTitle, frontMatterTitleFi
if (title === null) {
splitted.some((line, index) => {
const trimmedLine = line.trim()
const trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
const trimmedNextLine =
splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
if (trimmedLine.match('```')) {
isInsideCodeBlock = !isInsideCodeBlock
}
if (isInsideCodeBlock === false && (trimmedLine.match(/^# +/) || trimmedNextLine.match(/^=+$/))) {
if (
isInsideCodeBlock === false &&
(trimmedLine.match(/^# +/) || trimmedNextLine.match(/^=+$/))
) {
title = trimmedLine
return true
}
@@ -35,7 +48,7 @@ export function findNoteTitle (value, enableFrontMatterTitle, frontMatterTitleFi
if (title === null) {
title = ''
splitted.some((line) => {
splitted.some(line => {
if (line.trim().length > 0) {
title = line.trim()
return true

View File

@@ -1,10 +1,11 @@
const _ = require('lodash')
export function findStorage (storageKey) {
export function findStorage(storageKey) {
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
const storage = _.find(cachedStorageList, {key: storageKey})
if (storage === undefined) throw new Error('Target storage doesn\'t exist.')
if (!_.isArray(cachedStorageList))
throw new Error("Target storage doesn't exist.")
const storage = _.find(cachedStorageList, { key: storageKey })
if (storage === undefined) throw new Error("Target storage doesn't exist.")
return storage
}

View File

@@ -1,14 +1,14 @@
export function getTodoStatus (content) {
export function getTodoStatus(content) {
const splitted = content.split('\n')
let numberOfTodo = 0
let numberOfCompletedTodo = 0
splitted.forEach((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/^[\+\-\*] \[(\s|x)\] ./i)) {
splitted.forEach(line => {
const trimmedLine = line.trim().replace(/^(>\s*)*/, '')
if (trimmedLine.match(/^[+\-*] \[(\s|x)] ./i)) {
numberOfTodo++
}
if (trimmedLine.match(/^[\+\-\*] \[x\] ./i)) {
if (trimmedLine.match(/^[+\-*] \[x] ./i)) {
numberOfCompletedTodo++
}
})
@@ -19,7 +19,7 @@ export function getTodoStatus (content) {
}
}
export function getTodoPercentageOfCompleted (content) {
export function getTodoPercentageOfCompleted(content) {
const state = getTodoStatus(content)
return Math.floor(state.completed / state.total * 100)
return Math.floor((state.completed / state.total) * 100)
}

View File

@@ -7,9 +7,9 @@
* @return {string}
*/
export function decodeEntities (text) {
export function decodeEntities(text) {
var entities = [
['apos', '\''],
['apos', "'"],
['amp', '&'],
['lt', '<'],
['gt', '>'],
@@ -24,16 +24,16 @@ export function decodeEntities (text) {
return text
}
export function encodeEntities (text) {
export function encodeEntities(text) {
const entities = [
['\'', 'apos'],
["'", 'apos'],
['<', 'lt'],
['>', 'gt'],
['\\?', '#63'],
['\\$', '#36']
]
entities.forEach((entity) => {
entities.forEach(entity => {
text = text.replace(new RegExp(entity[0], 'g'), `&${entity[1]};`)
})
return text

View File

@@ -8,9 +8,10 @@ const i18n = new (require('i18n-2'))({
// setup some locales - other locales default to the first locale
locales: getLocales(),
extension: '.json',
directory: process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './locales')
: path.resolve('./locales'),
directory:
process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './locales')
: path.resolve('./locales'),
devMode: false
})

View File

@@ -1,8 +1,7 @@
const crypto = require('crypto')
const _ = require('lodash')
const uuidv4 = require('uuid/v4')
module.exports = function (uuid) {
module.exports = function(uuid) {
if (typeof uuid === typeof true && uuid) {
return uuidv4()
}

View File

@@ -1,35 +1,44 @@
'use strict'
module.exports = function definitionListPlugin (md) {
module.exports = function definitionListPlugin(md) {
var isSpace = md.utils.isSpace
// Search `[:~][\n ]`, returns next pos after marker on success
// or -1 on fail.
function skipMarker (state, line) {
function skipMarker(state, line) {
let start = state.bMarks[line] + state.tShift[line]
const max = state.eMarks[line]
if (start >= max) { return -1 }
if (start >= max) {
return -1
}
// Check bullet
const marker = state.src.charCodeAt(start++)
if (marker !== 0x7E/* ~ */ && marker !== 0x3A/* : */) { return -1 }
if (marker !== 0x7e /* ~ */ && marker !== 0x3a /* : */) {
return -1
}
const pos = state.skipSpaces(start)
// require space after ":"
if (start === pos) { return -1 }
if (start === pos) {
return -1
}
return start
}
function markTightParagraphs (state, idx) {
function markTightParagraphs(state, idx) {
const level = state.level + 2
let i
let l
for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
if (
state.tokens[i].level === level &&
state.tokens[i].type === 'paragraph_open'
) {
state.tokens[i + 2].hidden = true
state.tokens[i].hidden = true
i += 2
@@ -37,7 +46,7 @@ module.exports = function definitionListPlugin (md) {
}
}
function deflist (state, startLine, endLine, silent) {
function deflist(state, startLine, endLine, silent) {
var ch,
contentStart,
ddLine,
@@ -63,28 +72,38 @@ module.exports = function definitionListPlugin (md) {
if (silent) {
// quirk: validation mode validates a dd block only, not a whole deflist
if (state.ddIndent < 0) { return false }
if (state.ddIndent < 0) {
return false
}
return skipMarker(state, startLine) >= 0
}
nextLine = startLine + 1
if (nextLine >= endLine) { return false }
if (nextLine >= endLine) {
return false
}
if (state.isEmpty(nextLine)) {
nextLine++
if (nextLine >= endLine) { return false }
if (nextLine >= endLine) {
return false
}
}
if (state.sCount[nextLine] < state.blkIndent) { return false }
if (state.sCount[nextLine] < state.blkIndent) {
return false
}
contentStart = skipMarker(state, nextLine)
if (contentStart < 0) { return false }
if (contentStart < 0) {
return false
}
// Start list
listTokIdx = state.tokens.length
tight = true
token = state.push('dl_open', 'dl', 1)
token.map = listLines = [ startLine, 0 ]
token.map = listLines = [startLine, 0]
//
// Iterate list items
@@ -100,34 +119,38 @@ module.exports = function definitionListPlugin (md) {
// needed to break out of the second one
//
/* eslint no-labels:0,block-scoped-var:0 */
OUTER:
for (;;) {
OUTER: for (;;) {
prevEmptyEnd = false
token = state.push('dt_open', 'dt', 1)
token.map = [ dtLine, dtLine ]
token.map = [dtLine, dtLine]
token = state.push('inline', '', 0)
token.map = [ dtLine, dtLine ]
token.content = state.getLines(dtLine, dtLine + 1, state.blkIndent, false).trim()
token.map = [dtLine, dtLine]
token.content = state
.getLines(dtLine, dtLine + 1, state.blkIndent, false)
.trim()
token.children = []
token = state.push('dt_close', 'dt', -1)
for (;;) {
token = state.push('dd_open', 'dd', 1)
token.map = itemLines = [ ddLine, 0 ]
token.map = itemLines = [ddLine, 0]
pos = contentStart
max = state.eMarks[ddLine]
offset = state.sCount[ddLine] + contentStart - (state.bMarks[ddLine] + state.tShift[ddLine])
offset =
state.sCount[ddLine] +
contentStart -
(state.bMarks[ddLine] + state.tShift[ddLine])
while (pos < max) {
ch = state.src.charCodeAt(pos)
if (isSpace(ch)) {
if (ch === 0x09) {
offset += 4 - offset % 4
offset += 4 - (offset % 4)
} else {
offset++
}
@@ -153,8 +176,11 @@ module.exports = function definitionListPlugin (md) {
state.parentType = 'deflist'
newEndLine = ddLine
while (++newEndLine < endLine && (state.sCount[newEndLine] >= state.sCount[ddLine] || state.isEmpty(newEndLine))) {
}
while (
++newEndLine < endLine &&
(state.sCount[newEndLine] >= state.sCount[ddLine] ||
state.isEmpty(newEndLine))
) {}
oldLineMax = state.lineMax
state.lineMax = newEndLine
@@ -169,7 +195,7 @@ module.exports = function definitionListPlugin (md) {
}
// Item become loose if finish with empty line,
// but we should filter last element, because it means list finish
prevEmptyEnd = (state.line - ddLine) > 1 && state.isEmpty(state.line - 1)
prevEmptyEnd = state.line - ddLine > 1 && state.isEmpty(state.line - 1)
state.tShift[ddLine] = oldTShift
state.sCount[ddLine] = oldSCount
@@ -182,11 +208,17 @@ module.exports = function definitionListPlugin (md) {
itemLines[1] = nextLine = state.line
if (nextLine >= endLine) { break OUTER }
if (nextLine >= endLine) {
break OUTER
}
if (state.sCount[nextLine] < state.blkIndent) { break OUTER }
if (state.sCount[nextLine] < state.blkIndent) {
break OUTER
}
contentStart = skipMarker(state, nextLine)
if (contentStart < 0) { break }
if (contentStart < 0) {
break
}
ddLine = nextLine
@@ -194,20 +226,36 @@ module.exports = function definitionListPlugin (md) {
// insert DD tag and repeat checking
}
if (nextLine >= endLine) { break }
if (nextLine >= endLine) {
break
}
dtLine = nextLine
if (state.isEmpty(dtLine)) { break }
if (state.sCount[dtLine] < state.blkIndent) { break }
if (state.isEmpty(dtLine)) {
break
}
if (state.sCount[dtLine] < state.blkIndent) {
break
}
ddLine = dtLine + 1
if (ddLine >= endLine) { break }
if (state.isEmpty(ddLine)) { ddLine++ }
if (ddLine >= endLine) { break }
if (ddLine >= endLine) {
break
}
if (state.isEmpty(ddLine)) {
ddLine++
}
if (ddLine >= endLine) {
break
}
if (state.sCount[ddLine] < state.blkIndent) { break }
if (state.sCount[ddLine] < state.blkIndent) {
break
}
contentStart = skipMarker(state, ddLine)
if (contentStart < 0) { break }
if (contentStart < 0) {
break
}
// go to the next loop iteration:
// insert DT and DD tags and repeat checking
@@ -228,5 +276,7 @@ module.exports = function definitionListPlugin (md) {
return true
}
md.block.ruler.before('paragraph', 'deflist', deflist, { alt: [ 'paragraph', 'reference' ] })
md.block.ruler.before('paragraph', 'deflist', deflist, {
alt: ['paragraph', 'reference']
})
}

View File

@@ -1,9 +1,9 @@
'use strict'
module.exports = function (md, renderers, defaultRenderer) {
module.exports = function(md, renderers, defaultRenderer) {
const paramsRE = /^[ \t]*([\w+#-]+)?(?:\(((?:\s*\w[-\w]*(?:=(?:'(?:.*?[^\\])?'|"(?:.*?[^\\])?"|(?:[^'"][^\s]*)))?)*)\))?(?::([^:]*)(?::(\d+))?)?\s*$/
function fence (state, startLine, endLine, silent) {
function fence(state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
@@ -12,7 +12,7 @@ module.exports = function (md, renderers, defaultRenderer) {
}
const marker = state.src.charCodeAt(pos)
if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) {
if (marker !== 0x7e /* ~ */ && marker !== 0x60 /* ` */) {
return false
}
@@ -46,7 +46,10 @@ module.exports = function (md, renderers, defaultRenderer) {
if (pos < max && state.sCount[nextLine] < state.blkIndent) {
break
}
if (state.src.charCodeAt(pos) !== marker || state.sCount[nextLine] - state.blkIndent >= 4) {
if (
state.src.charCodeAt(pos) !== marker ||
state.sCount[nextLine] - state.blkIndent >= 4
) {
continue
}
@@ -127,10 +130,12 @@ module.exports = function (md, renderers, defaultRenderer) {
})
for (const name in renderers) {
md.renderer.rules[`${name}_fence`] = (tokens, index) => renderers[name](tokens[index])
md.renderer.rules[`${name}_fence`] = (tokens, index) =>
renderers[name](tokens[index])
}
if (defaultRenderer) {
md.renderer.rules['_fence'] = (tokens, index) => defaultRenderer(tokens[index])
md.renderer.rules['_fence'] = (tokens, index) =>
defaultRenderer(tokens[index])
}
}

View File

@@ -1,14 +1,19 @@
'use strict'
module.exports = function frontMatterPlugin (md) {
function frontmatter (state, startLine, endLine, silent) {
if (startLine !== 0 || state.src.substr(startLine, state.eMarks[0]) !== '---') {
module.exports = function frontMatterPlugin(md) {
function frontmatter(state, startLine, endLine, silent) {
if (
startLine !== 0 ||
state.src.substr(startLine, state.eMarks[0]) !== '---'
) {
return false
}
let line = 0
while (++line < state.lineMax) {
if (state.src.substring(state.bMarks[line], state.eMarks[line]) === '---') {
if (
state.src.substring(state.bMarks[line], state.eMarks[line]) === '---'
) {
state.line = line + 1
return true
@@ -19,6 +24,6 @@ module.exports = function frontMatterPlugin (md) {
}
md.block.ruler.before('table', 'frontmatter', frontmatter, {
alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
alt: ['paragraph', 'reference', 'blockquote', 'list']
})
}

View File

@@ -4,7 +4,7 @@ import sanitizeHtml from 'sanitize-html'
import { escapeHtmlCharacters } from './utils'
import url from 'url'
module.exports = function sanitizePlugin (md, options) {
module.exports = function sanitizePlugin(md, options) {
options = options || {}
md.core.ruler.after('linkify', 'sanitize_inline', state => {
@@ -15,7 +15,7 @@ module.exports = function sanitizePlugin (md, options) {
options
)
}
if (state.tokens[tokenIdx].type === '_fence') {
if (state.tokens[tokenIdx].type.match(/.*_fence$/)) {
// escapeHtmlCharacters has better performance
state.tokens[tokenIdx].content = escapeHtmlCharacters(
state.tokens[tokenIdx].content,
@@ -38,15 +38,20 @@ module.exports = function sanitizePlugin (md, options) {
}
const tagRegex = /<([A-Z][A-Z0-9]*)\s*((?:\s*[A-Z][A-Z0-9]*(?:=("|')(?:[^\3]+?)\3)?)*)\s*\/?>|<\/([A-Z][A-Z0-9]*)\s*>/i
const attributesRegex = /([A-Z][A-Z0-9]*)(?:=("|')([^\2]+?)\2)?/ig
const attributesRegex = /([A-Z][A-Z0-9]*)(?:=("|')([^\2]+?)\2)?/gi
function sanitizeInline (html, options) {
function sanitizeInline(html, options) {
let match = tagRegex.exec(html)
if (!match) {
return ''
}
const { allowedTags, allowedAttributes, selfClosing, allowedSchemesAppliedToAttributes } = options
const {
allowedTags,
allowedAttributes,
selfClosing,
allowedSchemesAppliedToAttributes
} = options
if (match[1] !== undefined) {
// opening tag
@@ -65,9 +70,17 @@ function sanitizeInline (html, options) {
name = match[1].toLowerCase()
value = match[3]
if (allowedAttributes['*'].indexOf(name) !== -1 || (allowedAttributes[tag] && allowedAttributes[tag].indexOf(name) !== -1)) {
if (
allowedAttributes['*'].indexOf(name) !== -1 ||
(allowedAttributes[tag] && allowedAttributes[tag].indexOf(name) !== -1)
) {
if (allowedSchemesAppliedToAttributes.indexOf(name) !== -1) {
if (naughtyHRef(value, options) || (tag === 'iframe' && name === 'src' && naughtyIFrame(value, options))) {
if (
naughtyHRef(value, options) ||
(tag === 'iframe' &&
name === 'src' &&
naughtyIFrame(value, options))
) {
continue
}
}
@@ -94,8 +107,12 @@ function sanitizeInline (html, options) {
}
}
function naughtyHRef (href, options) {
function naughtyHRef(href, options) {
// href = href.replace(/[\x00-\x20]+/g, '')
if (!href) {
// No href
return false
}
href = href.replace(/<\!\-\-.*?\-\-\>/g, '')
const matches = href.match(/^([a-zA-Z]+)\:/)
@@ -113,7 +130,7 @@ function naughtyHRef (href, options) {
return options.allowedSchemes.indexOf(scheme) === -1
}
function naughtyIFrame (src, options) {
function naughtyIFrame(src, options) {
try {
const parsed = url.parse(src, false, true)

View File

@@ -12,7 +12,7 @@ const hasProp = Object.prototype.hasOwnProperty
/**
* From @enyaxu/markdown-it-anchor
*/
function uniqueSlug (slug, slugs, opts) {
function uniqueSlug(slug, slugs, opts) {
let uniq = slug
let i = opts.uniqueSlugStartIndex
while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}`
@@ -20,28 +20,24 @@ function uniqueSlug (slug, slugs, opts) {
return uniq
}
function linkify (token) {
token.content = mdlink(token.content, '#' + token.slug)
function linkify(token) {
token.content = mdlink(token.content, `#${decodeURI(token.slug)}`)
return token
}
const TOC_MARKER_START = '<!-- toc -->'
const TOC_MARKER_END = '<!-- tocstop -->'
const tocRegex = new RegExp(`${TOC_MARKER_START}[\\s\\S]*?${TOC_MARKER_END}`)
/**
* Takes care of proper updating given editor with TOC.
* If TOC doesn't exit in the editor, it's inserted at current caret position.
* Otherwise,TOC is updated in place.
* @param editor CodeMirror editor to be updated with TOC
*/
export function generateInEditor (editor) {
const tocRegex = new RegExp(`${TOC_MARKER_START}[\\s\\S]*?${TOC_MARKER_END}`)
function tocExistsInEditor () {
return tocRegex.test(editor.getValue())
}
function updateExistingToc () {
export function generateInEditor(editor) {
function updateExistingToc() {
const toc = generate(editor.getValue())
const search = editor.getSearchCursor(tocRegex)
while (search.findNext()) {
@@ -49,24 +45,30 @@ export function generateInEditor (editor) {
}
}
function addTocAtCursorPosition () {
const toc = generate(editor.getRange(editor.getCursor(), {line: Infinity}))
function addTocAtCursorPosition() {
const toc = generate(
editor.getRange(editor.getCursor(), { line: Infinity })
)
editor.replaceRange(wrapTocWithEol(toc, editor), editor.getCursor())
}
if (tocExistsInEditor()) {
if (tocExistsInEditor(editor)) {
updateExistingToc()
} else {
addTocAtCursorPosition()
}
}
export function tocExistsInEditor(editor) {
return tocRegex.test(editor.getValue())
}
/**
* Generates MD TOC based on MD document passed as string.
* @param markdownText MD document
* @returns generatedTOC String containing generated TOC
*/
export function generate (markdownText) {
export function generate(markdownText) {
const slugs = {}
const opts = {
uniqueSlugStartIndex: 1
@@ -86,13 +88,17 @@ export function generate (markdownText) {
return TOC_MARKER_START + EOL + EOL + md + EOL + EOL + TOC_MARKER_END
}
function wrapTocWithEol (toc, editor) {
function wrapTocWithEol(toc, editor) {
const leftWrap = editor.getCursor().ch === 0 ? '' : EOL
const rightWrap = editor.getLine(editor.getCursor().line).length === editor.getCursor().ch ? '' : EOL
const rightWrap =
editor.getLine(editor.getCursor().line).length === editor.getCursor().ch
? ''
: EOL
return leftWrap + toc + rightWrap
}
export default {
generate,
generateInEditor
generateInEditor,
tocExistsInEditor
}

View File

@@ -2,24 +2,28 @@ import markdownit from 'markdown-it'
import sanitize from './markdown-it-sanitize-html'
import emoji from 'markdown-it-emoji'
import math from '@rokt33r/markdown-it-math'
import mdurl from 'mdurl'
import smartArrows from 'markdown-it-smartarrows'
import markdownItTocAndAnchor from '@hikerpig/markdown-it-toc-and-anchor'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import katex from 'katex'
import { lastFindInArray } from './utils'
import { escapeHtmlCharacters, lastFindInArray } from './utils'
function createGutter (str, firstLineNumber) {
function createGutter(str, firstLineNumber) {
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
const lastLineNumber = (str.match(/\n/g) || []).length + firstLineNumber - 1
const lines = []
for (let i = firstLineNumber; i <= lastLineNumber; i++) {
lines.push('<span class="CodeMirror-linenumber">' + i + '</span>')
}
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
return (
'<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
)
}
class Markdown {
constructor (options = {}) {
constructor(options = {}) {
const config = ConfigManager.get()
const defaultOptions = {
typographer: config.preview.smartQuotes,
@@ -27,36 +31,138 @@ class Markdown {
html: true,
xhtmlOut: true,
breaks: config.preview.breaks,
sanitize: 'STRICT'
sanitize: 'STRICT',
onFence: () => {}
}
const updatedOptions = Object.assign(defaultOptions, options)
this.md = markdownit(updatedOptions)
this.md.linkify.set({ fuzzyLink: false })
if (updatedOptions.sanitize !== 'NONE') {
const allowedTags = ['iframe', 'input', 'b',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'h8', 'br', 'b', 'i', 'strong', 'em', 'a', 'pre', 'code', 'img', 'tt',
'div', 'ins', 'del', 'sup', 'sub', 'p', 'ol', 'ul', 'table', 'thead', 'tbody', 'tfoot', 'blockquote',
'dl', 'dt', 'dd', 'kbd', 'q', 'samp', 'var', 'hr', 'ruby', 'rt', 'rp', 'li', 'tr', 'td', 'th', 's', 'strike', 'summary', 'details'
const allowedTags = [
'iframe',
'input',
'b',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'h7',
'h8',
'br',
'b',
'i',
'strong',
'em',
'a',
'pre',
'code',
'img',
'tt',
'div',
'ins',
'del',
'sup',
'sub',
'p',
'ol',
'ul',
'table',
'thead',
'tbody',
'tfoot',
'blockquote',
'dl',
'dt',
'dd',
'kbd',
'q',
'samp',
'var',
'hr',
'ruby',
'rt',
'rp',
'li',
'tr',
'td',
'th',
's',
'strike',
'summary',
'details'
]
const allowedAttributes = [
'abbr', 'accept', 'accept-charset',
'accesskey', 'action', 'align', 'alt', 'axis',
'border', 'cellpadding', 'cellspacing', 'char',
'charoff', 'charset', 'checked',
'clear', 'cols', 'colspan', 'color',
'compact', 'coords', 'datetime', 'dir',
'disabled', 'enctype', 'for', 'frame',
'headers', 'height', 'hreflang',
'hspace', 'ismap', 'label', 'lang',
'maxlength', 'media', 'method',
'multiple', 'name', 'nohref', 'noshade',
'nowrap', 'open', 'prompt', 'readonly', 'rel', 'rev',
'rows', 'rowspan', 'rules', 'scope',
'selected', 'shape', 'size', 'span',
'start', 'summary', 'tabindex', 'target',
'title', 'type', 'usemap', 'valign', 'value',
'vspace', 'width', 'itemprop'
'abbr',
'accept',
'accept-charset',
'accesskey',
'action',
'align',
'alt',
'axis',
'border',
'cellpadding',
'cellspacing',
'char',
'charoff',
'charset',
'checked',
'clear',
'cols',
'colspan',
'color',
'compact',
'coords',
'datetime',
'dir',
'disabled',
'enctype',
'for',
'frame',
'headers',
'height',
'hreflang',
'hspace',
'ismap',
'label',
'lang',
'maxlength',
'media',
'method',
'multiple',
'name',
'nohref',
'noshade',
'nowrap',
'open',
'prompt',
'readonly',
'rel',
'rev',
'rows',
'rowspan',
'rules',
'scope',
'selected',
'shape',
'size',
'span',
'start',
'summary',
'tabindex',
'target',
'title',
'type',
'usemap',
'valign',
'value',
'vspace',
'width',
'itemprop'
]
if (updatedOptions.sanitize === 'ALLOW_STYLES') {
@@ -69,20 +175,20 @@ class Markdown {
allowedTags,
allowedAttributes: {
'*': allowedAttributes,
'a': ['href'],
'div': ['itemscope', 'itemtype'],
'blockquote': ['cite'],
'del': ['cite'],
'ins': ['cite'],
'q': ['cite'],
'img': ['src', 'width', 'height'],
'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
'input': ['type', 'id', 'checked']
a: ['href'],
div: ['itemscope', 'itemtype'],
blockquote: ['cite'],
del: ['cite'],
ins: ['cite'],
q: ['cite'],
img: ['src', 'width', 'height'],
iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
input: ['type', 'id', 'checked']
},
allowedIframeHostnames: ['www.youtube.com'],
selfClosing: [ 'img', 'br', 'hr', 'input' ],
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ],
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
selfClosing: ['img', 'br', 'hr', 'input'],
allowedSchemes: ['http', 'https', 'ftp', 'mailto'],
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
allowProtocolRelative: true
})
}
@@ -95,7 +201,7 @@ class Markdown {
inlineClose: config.preview.latexInlineClose,
blockOpen: config.preview.latexBlockOpen,
blockClose: config.preview.latexBlockClose,
inlineRenderer: function (str) {
inlineRenderer: function(str) {
let output = ''
try {
output = katex.renderToString(str.trim())
@@ -104,7 +210,7 @@ class Markdown {
}
return output
},
blockRenderer: function (str) {
blockRenderer: function(str) {
let output = ''
try {
output = katex.renderToString(str.trim(), { displayMode: true })
@@ -121,96 +227,177 @@ class Markdown {
slugify: require('./slugify')
})
this.md.use(require('markdown-it-kbd'))
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']})
this.md.use(require('markdown-it-admonition'), {
types: [
'note',
'hint',
'attention',
'caution',
'danger',
'error',
'quote',
'abstract',
'question'
]
})
this.md.use(require('markdown-it-abbr'))
this.md.use(require('markdown-it-sub'))
this.md.use(require('markdown-it-sup'))
this.md.use(md => {
markdownItTocAndAnchor(md, {
toc: true,
tocPattern: /\[TOC\]/i,
anchorLink: false,
appendIdToHeading: false
})
md.renderer.rules.toc_open = () => '<div class="markdownIt-TOC-wrapper">'
md.renderer.rules.toc_close = () => '</div>'
})
this.md.use(require('./markdown-it-deflist'))
this.md.use(require('./markdown-it-frontmatter'))
this.md.use(require('./markdown-it-fence'), {
chart: token => {
if (token.parameters.hasOwnProperty('yaml')) {
token.parameters.format = 'yaml'
}
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="chart" data-height="${token.parameters.height}" data-format="${token.parameters.format || 'json'}">${token.content}</div>
</pre>`
},
flowchart: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
},
gallery: token => {
const content = token.content.split('\n').slice(0, -1).map(line => {
const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line)
if (match) {
return match[1]
} else {
return line
this.md.use(
require('./markdown-it-fence'),
{
chart: token => {
if (token.parameters.hasOwnProperty('yaml')) {
token.parameters.format = 'yaml'
}
}).join('\n')
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="gallery" data-autoplay="${token.parameters.autoplay}" data-height="${token.parameters.height}">${content}</div>
</pre>`
updatedOptions.onFence('chart', token.parameters.format)
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="chart" data-height="${
token.parameters.height
}" data-format="${token.parameters.format || 'json'}">${
token.content
}</div>
</pre>`
},
flowchart: token => {
updatedOptions.onFence('flowchart')
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="flowchart" data-height="${token.parameters.height}">${
token.content
}</div>
</pre>`
},
gallery: token => {
const content = token.content
.split('\n')
.slice(0, -1)
.map(line => {
const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line)
if (match) {
return mdurl.encode(match[1])
} else {
return mdurl.encode(line)
}
})
.join('\n')
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="gallery" data-autoplay="${
token.parameters.autoplay
}" data-height="${token.parameters.height}">${content}</div>
</pre>`
},
mermaid: token => {
updatedOptions.onFence('mermaid')
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="mermaid" data-height="${token.parameters.height}">${
token.content
}</div>
</pre>`
},
sequence: token => {
updatedOptions.onFence('sequence')
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="sequence" data-height="${token.parameters.height}">${
token.content
}</div>
</pre>`
}
},
mermaid: token => {
return `<pre class="fence" data-line="${token.map[0]}">
token => {
updatedOptions.onFence('code', token.langType)
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="mermaid" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
},
sequence: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="sequence" data-height="${token.parameters.height}">${token.content}</div>
${createGutter(token.content, token.firstLineNumber)}
<code class="${token.langType}">${token.content}</code>
</pre>`
}
}, token => {
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
${createGutter(token.content, token.firstLineNumber)}
<code class="${token.langType}">${token.content}</code>
</pre>`
})
)
const deflate = require('markdown-it-plantuml/lib/deflate')
this.md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) {
const stripTrailingSlash = (url) => url.endsWith('/') ? url.slice(0, -1) : url
const serverAddress = stripTrailingSlash(config.preview.plantUMLServerAddress) + '/svg'
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9)
)
return `${serverAddress}/${zippedCode}`
}
const plantuml = require('markdown-it-plantuml')
const plantUmlStripTrailingSlash = url =>
url.endsWith('/') ? url.slice(0, -1) : url
const plantUmlServerAddress = plantUmlStripTrailingSlash(
config.preview.plantUMLServerAddress
)
const parsePlantUml = function(umlCode, openMarker, closeMarker, type) {
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`${openMarker}\n${s}\n${closeMarker}`, 9)
)
return `${plantUmlServerAddress}/${type}/${zippedCode}`
}
this.md.use(plantuml, {
generateSource: umlCode =>
parsePlantUml(umlCode, '@startuml', '@enduml', 'svg')
})
// Ditaa support
this.md.use(require('markdown-it-plantuml'), {
// Ditaa support. PlantUML server doesn't support Ditaa in SVG, so we set the format as PNG at the moment.
this.md.use(plantuml, {
openMarker: '@startditaa',
closeMarker: '@endditaa',
generateSource: function (umlCode) {
const stripTrailingSlash = (url) => url.endsWith('/') ? url.slice(0, -1) : url
// Currently PlantUML server doesn't support Ditaa in SVG, so we set the format as PNG at the moment.
const serverAddress = stripTrailingSlash(config.preview.plantUMLServerAddress) + '/png'
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startditaa\n${s}\n@endditaa`, 9)
)
return `${serverAddress}/${zippedCode}`
}
generateSource: umlCode =>
parsePlantUml(umlCode, '@startditaa', '@endditaa', 'png')
})
// Mindmap support
this.md.use(plantuml, {
openMarker: '@startmindmap',
closeMarker: '@endmindmap',
generateSource: umlCode =>
parsePlantUml(umlCode, '@startmindmap', '@endmindmap', 'svg')
})
// WBS support
this.md.use(plantuml, {
openMarker: '@startwbs',
closeMarker: '@endwbs',
generateSource: umlCode =>
parsePlantUml(umlCode, '@startwbs', '@endwbs', 'svg')
})
// Gantt support
this.md.use(plantuml, {
openMarker: '@startgantt',
closeMarker: '@endgantt',
generateSource: umlCode =>
parsePlantUml(umlCode, '@startgantt', '@endgantt', 'svg')
})
// Override task item
this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
this.md.block.ruler.at('paragraph', function(
state,
startLine /*, endLine */
) {
let content, terminate, i, l, token
let nextLine = startLine + 1
const terminatorRules = state.md.block.ruler.getRules('paragraph')
@@ -220,10 +407,14 @@ class Markdown {
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
if (state.sCount[nextLine] - state.blkIndent > 3) {
continue
}
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue }
if (state.sCount[nextLine] < 0) {
continue
}
// Some tags can terminate paragraph without empty line.
terminate = false
@@ -233,10 +424,14 @@ class Markdown {
break
}
}
if (terminate) { break }
if (terminate) {
break
}
}
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
content = state
.getLines(startLine, nextLine, state.blkIndent, false)
.trim()
state.line = nextLine
@@ -246,18 +441,31 @@ class Markdown {
if (state.parentType === 'list') {
const match = content.match(/^\[( |x)\] ?(.+)/i)
if (match) {
const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open')
const liToken = lastFindInArray(
state.tokens,
token => token.type === 'list_item_open'
)
if (liToken) {
if (!liToken.attrs) {
liToken.attrs = []
}
if (config.preview.lineThroughCheckbox) {
liToken.attrs.push(['class', `taskListItem${match[1] !== ' ' ? ' checked' : ''}`])
liToken.attrs.push([
'class',
`taskListItem${match[1] !== ' ' ? ' checked' : ''}`
])
} else {
liToken.attrs.push(['class', 'taskListItem'])
}
}
content = `<label class='taskListItem${match[1] !== ' ' ? ' checked' : ''}' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>`
content = `<label class='taskListItem${
match[1] !== ' ' ? ' checked' : ''
}' for='checkbox-${startLine + 1}'><input type='checkbox'${
match[1] !== ' ' ? ' checked' : ''
} id='checkbox-${startLine + 1}'/> ${content.substring(
4,
content.length
)}</label>`
}
}
@@ -271,6 +479,16 @@ class Markdown {
return true
})
this.md.renderer.rules.code_inline = function(tokens, idx) {
const token = tokens[idx]
return (
'<code class="inline">' +
escapeHtmlCharacters(token.content) +
'</code>'
)
}
if (config.preview.smartArrows) {
this.md.use(smartArrows)
}
@@ -278,7 +496,7 @@ class Markdown {
// Add line number attribute for scrolling
const originalRender = this.md.renderer.render
this.md.renderer.render = (tokens, options, env) => {
tokens.forEach((token) => {
tokens.forEach(token => {
switch (token.type) {
case 'blockquote_open':
case 'dd_open':
@@ -287,7 +505,9 @@ class Markdown {
case 'list_item_open':
case 'paragraph_open':
case 'table_open':
token.attrPush(['data-line', token.map[0]])
if (token.map) {
token.attrPush(['data-line', token.map[0]])
}
}
})
const result = originalRender.call(this.md.renderer, tokens, options, env)
@@ -297,7 +517,7 @@ class Markdown {
window.md = this.md
}
render (content) {
render(content) {
if (!_.isString(content)) content = ''
return this.md.render(content)
}

View File

@@ -6,7 +6,7 @@
* @param {string} input
* @return {string}
*/
export function strip (input) {
export function strip(input) {
let output = input
try {
output = output
@@ -22,7 +22,7 @@ export function strip (input) {
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
.replace(/>/g, '')
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
.replace(/^#{1,6}\s*([^#]*)\s*(#{1,6})?/gm, '$1')
.replace(/^#{1,6}\s*/gm, '')
.replace(/(`{3,})(.*?)\1/gm, '$2')
.replace(/^-{3,}\s*$/g, '')
.replace(/`(.+?)`/g, '$1')

View File

@@ -1,14 +1,25 @@
import { hashHistory } from 'react-router'
import dataApi from 'browser/main/lib/dataApi'
import ee from 'browser/main/lib/eventEmitter'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import queryString from 'query-string'
import { push } from 'connected-react-router'
export function createMarkdownNote (storage, folder, dispatch, location, params, config) {
export function createMarkdownNote(
storage,
folder,
dispatch,
location,
params,
config
) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
let tags = []
if (config.ui.tagNewNoteWithFilteringTags && location.pathname.match(/\/tags/)) {
if (
config.ui.tagNewNoteWithFilteringTags &&
location.pathname.match(/\/tags/)
) {
tags = params.tagname.split(' ')
}
@@ -28,25 +39,40 @@ export function createMarkdownNote (storage, folder, dispatch, location, params,
note: note
})
hashHistory.push({
pathname: location.pathname,
query: { key: noteHash }
})
dispatch(
push({
pathname: location.pathname,
search: queryString.stringify({ key: noteHash })
})
)
ee.emit('list:jump', noteHash)
ee.emit('detail:focus')
})
}
export function createSnippetNote (storage, folder, dispatch, location, params, config) {
export function createSnippetNote(
storage,
folder,
dispatch,
location,
params,
config
) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_SNIPPET')
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
let tags = []
if (config.ui.tagNewNoteWithFilteringTags && location.pathname.match(/\/tags/)) {
if (
config.ui.tagNewNoteWithFilteringTags &&
location.pathname.match(/\/tags/)
) {
tags = params.tagname.split(' ')
}
const defaultLanguage = config.editor.snippetDefaultLanguage === 'Auto Detect' ? null : config.editor.snippetDefaultLanguage
const defaultLanguage =
config.editor.snippetDefaultLanguage === 'Auto Detect'
? null
: config.editor.snippetDefaultLanguage
return dataApi
.createNote(storage, {
@@ -70,10 +96,12 @@ export function createSnippetNote (storage, folder, dispatch, location, params,
type: 'UPDATE_NOTE',
note: note
})
hashHistory.push({
pathname: location.pathname,
query: { key: noteHash }
})
dispatch(
push({
pathname: location.pathname,
search: queryString.stringify({ key: noteHash })
})
)
ee.emit('list:jump', noteHash)
ee.emit('detail:focus')
})

View File

@@ -1,7 +1,7 @@
import consts from 'browser/lib/consts'
import isString from 'lodash/isString'
export default function normalizeEditorFontFamily (fontFamily) {
export default function normalizeEditorFontFamily(fontFamily) {
const defaultEditorFontFamily = consts.DEFAULT_EDITOR_FONT_FAMILY
return isString(fontFamily) && fontFamily.length > 0
? [fontFamily].concat(defaultEditorFontFamily).join(', ')

View File

@@ -1,31 +1,38 @@
import _ from 'lodash'
export default function searchFromNotes (notes, search) {
export default function searchFromNotes(notes, search) {
if (search.trim().length === 0) return []
const searchBlocks = search.split(' ').filter(block => { return block !== '' })
const searchBlocks = search.split(' ').filter(block => {
return block !== ''
})
let foundNotes = notes
searchBlocks.forEach((block) => {
searchBlocks.forEach(block => {
foundNotes = findByWordOrTag(foundNotes, block)
})
return foundNotes
}
function findByWordOrTag (notes, block) {
function findByWordOrTag(notes, block) {
let tag = block
if (tag.match(/^#.+/)) {
tag = tag.match(/#(.+)/)[1]
}
const tagRegExp = new RegExp(_.escapeRegExp(tag), 'i')
const wordRegExp = new RegExp(_.escapeRegExp(block), 'i')
return notes.filter((note) => {
if (_.isArray(note.tags) && note.tags.some((_tag) => _tag.match(tagRegExp))) {
return notes.filter(note => {
if (_.isArray(note.tags) && note.tags.some(_tag => _tag.match(tagRegExp))) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return note.description.match(wordRegExp) || note.snippets.some((snippet) => {
return snippet.name.match(wordRegExp) || snippet.content.match(wordRegExp)
})
return (
note.description.match(wordRegExp) ||
note.snippets.some(snippet => {
return (
snippet.name.match(wordRegExp) || snippet.content.match(wordRegExp)
)
})
)
} else if (note.type === 'MARKDOWN_NOTE') {
return note.content.match(wordRegExp)
}

View File

@@ -1,17 +1,15 @@
import diacritics from 'diacritics-map'
module.exports = function slugify(title) {
const slug = encodeURI(
title
.trim()
.replace(/^\s+/, '')
.replace(/\s+$/, '')
.replace(/\s+/g, '-')
.replace(
/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\{\|\}\~\`]/g,
''
)
)
function replaceDiacritics (str) {
return str.replace(/[À-ž]/g, function (ch) {
return diacritics[ch] || ch
})
}
module.exports = function slugify (title) {
let slug = title.trim()
slug = replaceDiacritics(slug)
slug = slug.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-')
return encodeURI(slug).replace(/\-+$/, '')
return slug
}

View File

@@ -12,19 +12,19 @@ const MILLISECONDS_TILL_LIVECHECK = 500
let dictionary = null
let self
function getAvailableDictionaries () {
function getAvailableDictionaries() {
return [
{label: i18n.__('Disabled'), value: SPELLCHECK_DISABLED},
{label: i18n.__('English'), value: 'en_GB'},
{label: i18n.__('German'), value: 'de_DE'},
{label: i18n.__('French'), value: 'fr_FR'}
{ label: i18n.__('Spellcheck disabled'), value: SPELLCHECK_DISABLED },
{ label: i18n.__('English'), value: 'en_GB' },
{ label: i18n.__('German'), value: 'de_DE' },
{ label: i18n.__('French'), value: 'fr_FR' }
]
}
/**
* Only to be used in the tests :)
*/
function setDictionaryForTestsOnly (newDictionary) {
function setDictionaryForTestsOnly(newDictionary) {
dictionary = newDictionary
}
@@ -34,7 +34,7 @@ function setDictionaryForTestsOnly (newDictionary) {
* @param {Codemirror} editor CodeMirror-Editor
* @param {String} lang on of the values from getAvailableDictionaries()-Method
*/
function setLanguage (editor, lang) {
function setLanguage(editor, lang) {
self = this
dictionary = null
@@ -50,8 +50,7 @@ function setLanguage (editor, lang) {
dictionary = new Typo(lang, false, false, {
dictionaryPath: DICTIONARY_PATH,
asyncLoad: true,
loadedCallback: () =>
checkWholeDocument(editor)
loadedCallback: () => checkWholeDocument(editor)
})
}
}
@@ -60,12 +59,12 @@ function setLanguage (editor, lang) {
* Checks the whole content of the editor for typos
* @param {Codemirror} editor CodeMirror-Editor
*/
function checkWholeDocument (editor) {
function checkWholeDocument(editor) {
const lastLine = editor.lineCount() - 1
const textOfLastLine = editor.getLine(lastLine) || ''
const lastChar = textOfLastLine.length
const from = {line: 0, ch: 0}
const to = {line: lastLine, ch: lastChar}
const from = { line: 0, ch: 0 }
const to = { line: lastLine, ch: lastChar }
checkMultiLineRange(editor, from, to)
}
@@ -75,15 +74,18 @@ function checkWholeDocument (editor) {
* @param {line, ch} from starting position of the spellcheck
* @param {line, ch} to end position of the spellcheck
*/
function checkMultiLineRange (editor, from, to) {
function sortRange (pos1, pos2) {
if (pos1.line > pos2.line || (pos1.line === pos2.line && pos1.ch > pos2.ch)) {
return {from: pos2, to: pos1}
function checkMultiLineRange(editor, from, to) {
function sortRange(pos1, pos2) {
if (
pos1.line > pos2.line ||
(pos1.line === pos2.line && pos1.ch > pos2.ch)
) {
return { from: pos2, to: pos1 }
}
return {from: pos1, to: pos2}
return { from: pos1, to: pos2 }
}
const {from: smallerPos, to: higherPos} = sortRange(from, to)
const { from: smallerPos, to: higherPos } = sortRange(from, to)
for (let l = smallerPos.line; l <= higherPos.line; l++) {
const line = editor.getLine(l) || ''
let w = 0
@@ -95,9 +97,9 @@ function checkMultiLineRange (editor, from, to) {
wEnd = higherPos.ch
}
while (w <= wEnd) {
const wordRange = editor.findWordAt({line: l, ch: w})
const wordRange = editor.findWordAt({ line: l, ch: w })
self.checkWord(editor, wordRange)
w += (wordRange.head.ch - wordRange.anchor.ch) + 1
w += wordRange.head.ch - wordRange.anchor.ch + 1
}
}
}
@@ -110,13 +112,15 @@ function checkMultiLineRange (editor, from, to) {
* @param wordRange Object specifying the range that should be checked.
* Having the following structure: <code>{anchor: {line: integer, ch: integer}, head: {line: integer, ch: integer}}</code>
*/
function checkWord (editor, wordRange) {
function checkWord(editor, wordRange) {
const word = editor.getRange(wordRange.anchor, wordRange.head)
if (word == null || word.length <= 3) {
return
}
if (!dictionary.check(word)) {
editor.markText(wordRange.anchor, wordRange.head, {className: styles[CSS_ERROR_CLASS]})
editor.markText(wordRange.anchor, wordRange.head, {
className: styles[CSS_ERROR_CLASS]
})
}
}
@@ -126,32 +130,40 @@ function checkWord (editor, wordRange) {
* @param fromChangeObject codeMirror changeObject describing the start of the editing
* @param toChangeObject codeMirror changeObject describing the end of the editing
*/
function checkChangeRange (editor, fromChangeObject, toChangeObject) {
function checkChangeRange(editor, fromChangeObject, toChangeObject) {
/**
* Calculate the smallest respectively largest position as a start, resp. end, position and return it
* @param start CodeMirror change object
* @param end CodeMirror change object
* @returns {{start: {line: *, ch: *}, end: {line: *, ch: *}}}
*/
function getStartAndEnd (start, end) {
function getStartAndEnd(start, end) {
const possiblePositions = [start.from, start.to, end.from, end.to]
let smallest = start.from
let biggest = end.to
for (const currentPos of possiblePositions) {
if (currentPos.line < smallest.line || (currentPos.line === smallest.line && currentPos.ch < smallest.ch)) {
if (
currentPos.line < smallest.line ||
(currentPos.line === smallest.line && currentPos.ch < smallest.ch)
) {
smallest = currentPos
}
if (currentPos.line > biggest.line || (currentPos.line === biggest.line && currentPos.ch > biggest.ch)) {
if (
currentPos.line > biggest.line ||
(currentPos.line === biggest.line && currentPos.ch > biggest.ch)
) {
biggest = currentPos
}
}
return {start: smallest, end: biggest}
return { start: smallest, end: biggest }
}
if (dictionary === null || editor == null) { return }
if (dictionary === null || editor == null) {
return
}
try {
const {start, end} = getStartAndEnd(fromChangeObject, toChangeObject)
const { start, end } = getStartAndEnd(fromChangeObject, toChangeObject)
// Expand the range to include words after/before whitespaces
start.ch = Math.max(start.ch - 1, 0)
@@ -165,29 +177,40 @@ function checkChangeRange (editor, fromChangeObject, toChangeObject) {
self.checkMultiLineRange(editor, start, end)
} catch (e) {
console.info('Error during the spell check. It might be due to problems figuring out the range of the new text..', e)
console.info(
'Error during the spell check. It might be due to problems figuring out the range of the new text..',
e
)
}
}
function saveLiveSpellCheckFrom (changeObject) {
function saveLiveSpellCheckFrom(changeObject) {
liveSpellCheckFrom = changeObject
}
let liveSpellCheckFrom
const debouncedSpellCheckLeading = _.debounce(saveLiveSpellCheckFrom, MILLISECONDS_TILL_LIVECHECK, {
'leading': true,
'trailing': false
})
const debouncedSpellCheck = _.debounce(checkChangeRange, MILLISECONDS_TILL_LIVECHECK, {
'leading': false,
'trailing': true
})
const debouncedSpellCheckLeading = _.debounce(
saveLiveSpellCheckFrom,
MILLISECONDS_TILL_LIVECHECK,
{
leading: true,
trailing: false
}
)
const debouncedSpellCheck = _.debounce(
checkChangeRange,
MILLISECONDS_TILL_LIVECHECK,
{
leading: false,
trailing: true
}
)
/**
* Handles a keystroke. Buffers the input and performs a live spell check after a certain time. Uses _debounce from lodash to buffer the input
* @param {Codemirror} editor CodeMirror-Editor
* @param changeObject codeMirror changeObject
*/
function handleChange (editor, changeObject) {
function handleChange(editor, changeObject) {
if (dictionary === null) {
return
}
@@ -201,7 +224,7 @@ function handleChange (editor, changeObject) {
* @param word word to be checked
* @returns {String[]} Array of suggestions
*/
function getSpellingSuggestion (word) {
function getSpellingSuggestion(word) {
if (dictionary == null || word == null) {
return []
}
@@ -211,7 +234,7 @@ function getSpellingSuggestion (word) {
/**
* Returns the name of the CSS class used for errors
*/
function getCSSClassName () {
function getCSSClassName() {
return styles[CSS_ERROR_CLASS]
}

9
browser/lib/turndown.js Normal file
View File

@@ -0,0 +1,9 @@
const TurndownService = require('turndown')
const { gfm } = require('turndown-plugin-gfm')
export const createTurndownService = function() {
const turndown = new TurndownService()
turndown.use(gfm)
turndown.remove('script')
return turndown
}

44
browser/lib/ui-themes.js Normal file
View File

@@ -0,0 +1,44 @@
import i18n from 'browser/lib/i18n'
export default [
{
name: 'dark',
label: i18n.__('Dark'),
isDark: true
},
{
name: 'default',
label: i18n.__('Default'),
isDark: false
},
{
name: 'dracula',
label: i18n.__('Dracula'),
isDark: true
},
{
name: 'monokai',
label: i18n.__('Monokai'),
isDark: true
},
{
name: 'nord',
label: i18n.__('Nord'),
isDark: true
},
{
name: 'solarized-dark',
label: i18n.__('Solarized Dark'),
isDark: true
},
{
name: 'vulcan',
label: i18n.__('Vulcan'),
isDark: true
},
{
name: 'white',
label: i18n.__('White'),
isDark: false
}
]

View File

@@ -1,4 +1,4 @@
export function lastFindInArray (array, callback) {
export function lastFindInArray(array, callback) {
for (let i = array.length - 1; i >= 0; --i) {
if (callback(array[i], i, array)) {
return array[i]
@@ -6,7 +6,7 @@ export function lastFindInArray (array, callback) {
}
}
export function escapeHtmlCharacters (
export function escapeHtmlCharacters(
html,
opt = { detectCodeBlock: false, skipSingleQuote: false }
) {
@@ -115,7 +115,7 @@ export function escapeHtmlCharacters (
return html
}
export function isObjectEqual (a, b) {
export function isObjectEqual(a, b) {
const aProps = Object.getOwnPropertyNames(a)
const bProps = Object.getOwnPropertyNames(b)
@@ -132,8 +132,30 @@ export function isObjectEqual (a, b) {
return true
}
export function isMarkdownTitleURL(str) {
return /(^#{1,6}\s)(?:\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(
str
)
}
export function humanFileSize(bytes) {
const threshold = 1000
if (Math.abs(bytes) < threshold) {
return bytes + ' B'
}
var units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
var u = -1
do {
bytes /= threshold
++u
} while (Math.abs(bytes) >= threshold && u < units.length - 1)
return bytes.toFixed(1) + ' ' + units[u]
}
export default {
lastFindInArray,
escapeHtmlCharacters,
isObjectEqual
isObjectEqual,
isMarkdownTitleURL,
humanFileSize
}

View File

@@ -0,0 +1,49 @@
import config from 'browser/main/lib/ConfigManager'
const exec = require('child_process').exec
const path = require('path')
let lastHeartbeat = 0
function sendWakatimeHeartBeat(
storagePath,
noteKey,
storageName,
{ isWrite, hasFileChanges, isFileChange }
) {
if (
config.get().wakatime.isActive &&
!!config.get().wakatime.key &&
(new Date().getTime() - lastHeartbeat > 120000 || isFileChange)
) {
const notePath = path.join(storagePath, 'notes', noteKey + '.cson')
if (!isWrite && !hasFileChanges && !isFileChange) {
return
}
lastHeartbeat = new Date()
const wakatimeKey = config.get().wakatime.key
if (wakatimeKey) {
exec(
`wakatime --file ${notePath} --project '${storageName}' --key ${wakatimeKey} --plugin Boostnote-wakatime`,
(error, stdOut, stdErr) => {
if (error) {
console.log(error)
lastHeartbeat = 0
} else {
console.log(
'wakatime',
'isWrite',
isWrite,
'hasChanges',
hasFileChanges,
'isFileChange',
isFileChange
)
}
}
)
}
}
}
export { sendWakatimeHeartBeat }

View File

@@ -24,23 +24,16 @@ body[data-theme="dark"]
.empty-message
color $ui-dark-inactive-text-color
body[data-theme="solarized-dark"]
.root
background-color $ui-solarized-dark-noteDetail-backgroundColor
border-left 1px solid $ui-solarized-dark-borderColor
.empty-message
color $ui-solarized-dark-text-color
apply-theme(theme)
body[data-theme={theme}]
.root
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
border-left 1px solid get-theme-var(theme, 'borderColor')
.empty-message
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.root
background-color $ui-monokai-noteDetail-backgroundColor
border-left 1px solid $ui-monokai-borderColor
.empty-message
color $ui-monokai-text-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="dracula"]
.root
background-color $ui-dracula-noteDetail-backgroundColor
border-left 1px solid $ui-dracula-borderColor
.empty-message
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -6,7 +6,7 @@ import _ from 'lodash'
import i18n from 'browser/lib/i18n'
class FolderSelect extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -16,24 +16,27 @@ class FolderSelect extends React.Component {
}
}
componentDidMount () {
componentDidMount() {
this.value = this.props.value
}
componentDidUpdate () {
componentDidUpdate() {
this.value = this.props.value
}
handleClick (e) {
this.setState({
status: 'SEARCH',
optionIndex: -1
}, () => {
this.refs.search.focus()
})
handleClick(e) {
this.setState(
{
status: 'SEARCH',
optionIndex: -1
},
() => {
this.refs.search.focus()
}
)
}
handleFocus (e) {
handleFocus(e) {
if (this.state.status === 'IDLE') {
this.setState({
status: 'FOCUS'
@@ -41,7 +44,7 @@ class FolderSelect extends React.Component {
}
}
handleBlur (e) {
handleBlur(e) {
if (this.state.status === 'FOCUS') {
this.setState({
status: 'IDLE'
@@ -49,40 +52,49 @@ class FolderSelect extends React.Component {
}
}
handleKeyDown (e) {
handleKeyDown(e) {
switch (e.keyCode) {
case 13:
if (this.state.status === 'FOCUS') {
this.setState({
status: 'SEARCH',
optionIndex: -1
}, () => {
this.refs.search.focus()
})
this.setState(
{
status: 'SEARCH',
optionIndex: -1
},
() => {
this.refs.search.focus()
}
)
}
break
case 40:
case 38:
if (this.state.status === 'FOCUS') {
this.setState({
status: 'SEARCH',
optionIndex: 0
}, () => {
this.refs.search.focus()
})
this.setState(
{
status: 'SEARCH',
optionIndex: 0
},
() => {
this.refs.search.focus()
}
)
}
break
case 9:
if (e.shiftKey) {
e.preventDefault()
const tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
const previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
const tabbable = document.querySelectorAll(
'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'
)
const previousEl =
tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
if (previousEl != null) previousEl.focus()
}
}
}
handleSearchInputBlur (e) {
handleSearchInputBlur(e) {
if (e.relatedTarget !== this.refs.root) {
this.setState({
status: 'IDLE'
@@ -90,14 +102,17 @@ class FolderSelect extends React.Component {
}
}
handleSearchInputChange (e) {
handleSearchInputChange(e) {
const { folders } = this.props
const search = this.refs.search.value
const optionIndex = search.length > 0
? _.findIndex(folders, (folder) => {
return folder.name.match(new RegExp('^' + _.escapeRegExp(search), 'i'))
})
: -1
const optionIndex =
search.length > 0
? _.findIndex(folders, folder => {
return folder.name.match(
new RegExp('^' + _.escapeRegExp(search), 'i')
)
})
: -1
this.setState({
search: this.refs.search.value,
@@ -105,7 +120,7 @@ class FolderSelect extends React.Component {
})
}
handleSearchInputKeyDown (e) {
handleSearchInputKeyDown(e) {
switch (e.keyCode) {
case 40:
e.stopPropagation()
@@ -121,15 +136,18 @@ class FolderSelect extends React.Component {
break
case 27:
e.stopPropagation()
this.setState({
status: 'FOCUS'
}, () => {
this.refs.root.focus()
})
this.setState(
{
status: 'FOCUS'
},
() => {
this.refs.root.focus()
}
)
}
}
nextOption () {
nextOption() {
let { optionIndex } = this.state
const { folders } = this.props
@@ -141,7 +159,7 @@ class FolderSelect extends React.Component {
})
}
previousOption () {
previousOption() {
const { folders } = this.props
let { optionIndex } = this.state
@@ -153,46 +171,52 @@ class FolderSelect extends React.Component {
})
}
selectOption () {
selectOption() {
const { folders } = this.props
const optionIndex = this.state.optionIndex
const folder = folders[optionIndex]
if (folder != null) {
this.setState({
status: 'FOCUS'
}, () => {
this.setValue(folder.key)
this.refs.root.focus()
})
this.setState(
{
status: 'FOCUS'
},
() => {
this.setValue(folder.key)
this.refs.root.focus()
}
)
}
}
handleOptionClick (storageKey, folderKey) {
return (e) => {
handleOptionClick(storageKey, folderKey) {
return e => {
e.stopPropagation()
this.setState({
status: 'FOCUS'
}, () => {
this.setValue(storageKey + '-' + folderKey)
this.refs.root.focus()
})
this.setState(
{
status: 'FOCUS'
},
() => {
this.setValue(storageKey + '-' + folderKey)
this.refs.root.focus()
}
)
}
}
setValue (value) {
setValue(value) {
this.value = value
this.props.onChange()
}
render () {
render() {
const { className, data, value } = this.props
const splitted = value.split('-')
const storageKey = splitted.shift()
const folderKey = splitted.shift()
let options = []
data.storageMap.forEach((storage, index) => {
storage.folders.forEach((folder) => {
storage.folders.forEach(folder => {
options.push({
storage: storage,
folder: folder
@@ -200,68 +224,78 @@ class FolderSelect extends React.Component {
})
})
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
const currentOption = options.filter(
option =>
option.storage.key === storageKey && option.folder.key === folderKey
)[0]
if (this.state.search.trim().length > 0) {
const filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
options = options.filter((option) => filter.test(option.folder.name))
options = options.filter(option => filter.test(option.folder.name))
}
const optionList = options
.map((option, index) => {
return (
<div styleName={index === this.state.optionIndex
const optionList = options.map((option, index) => {
return (
<div
styleName={
index === this.state.optionIndex
? 'search-optionList-item--active'
: 'search-optionList-item'
}
key={option.storage.key + '-' + option.folder.key}
onClick={(e) => this.handleOptionClick(option.storage.key, option.folder.key)(e)}
}
key={option.storage.key + '-' + option.folder.key}
onClick={e =>
this.handleOptionClick(option.storage.key, option.folder.key)(e)
}
>
<span
styleName='search-optionList-item-name'
style={{ borderColor: option.folder.color }}
>
<span styleName='search-optionList-item-name'
style={{borderColor: option.folder.color}}
>
{option.folder.name}
<span styleName='search-optionList-item-name-surfix'>in {option.storage.name}</span>
{option.folder.name}
<span styleName='search-optionList-item-name-surfix'>
in {option.storage.name}
</span>
</div>
)
})
</span>
</div>
)
})
return (
<div className={_.isString(className)
? 'FolderSelect ' + className
: 'FolderSelect'
<div
className={
_.isString(className) ? 'FolderSelect ' + className : 'FolderSelect'
}
styleName={this.state.status === 'SEARCH'
? 'root--search'
: this.state.status === 'FOCUS'
? 'root--focus'
: 'root'
styleName={
this.state.status === 'SEARCH'
? 'root--search'
: this.state.status === 'FOCUS'
? 'root--focus'
: 'root'
}
ref='root'
tabIndex='0'
onClick={(e) => this.handleClick(e)}
onFocus={(e) => this.handleFocus(e)}
onBlur={(e) => this.handleBlur(e)}
onKeyDown={(e) => this.handleKeyDown(e)}
onClick={e => this.handleClick(e)}
onFocus={e => this.handleFocus(e)}
onBlur={e => this.handleBlur(e)}
onKeyDown={e => this.handleKeyDown(e)}
>
{this.state.status === 'SEARCH'
? <div styleName='search'>
<input styleName='search-input'
{this.state.status === 'SEARCH' ? (
<div styleName='search'>
<input
styleName='search-input'
ref='search'
value={this.state.search}
placeholder={i18n.__('Folder...')}
onChange={(e) => this.handleSearchInputChange(e)}
onBlur={(e) => this.handleSearchInputBlur(e)}
onKeyDown={(e) => this.handleSearchInputKeyDown(e)}
onChange={e => this.handleSearchInputChange(e)}
onBlur={e => this.handleSearchInputBlur(e)}
onKeyDown={e => this.handleSearchInputKeyDown(e)}
/>
<div styleName='search-optionList'
ref='optionList'
>
<div styleName='search-optionList' ref='optionList'>
{optionList}
</div>
</div>
: <div styleName='idle' style={{color: currentOption.folder.color}}>
) : currentOption ? (
<div styleName='idle' style={{ color: currentOption.folder.color }}>
<div styleName='idle-label'>
<i className='fa fa-folder' />
<span styleName='idle-label-name'>
@@ -269,8 +303,7 @@ class FolderSelect extends React.Component {
</span>
</div>
</div>
}
) : null}
</div>
)
}
@@ -280,11 +313,13 @@ FolderSelect.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.string,
folders: PropTypes.arrayOf(PropTypes.shape({
key: PropTypes.string,
name: PropTypes.string,
color: PropTypes.string
}))
folders: PropTypes.arrayOf(
PropTypes.shape({
key: PropTypes.string,
name: PropTypes.string,
color: PropTypes.string
})
)
}
export default CSSModules(FolderSelect, styles)

View File

@@ -134,54 +134,39 @@ body[data-theme="dark"]
.search-optionList-item-name-surfix
color $ui-dark-inactive-text-color
body[data-theme="monokai"]
.root
color $ui-dark-text-color
&:hover
color white
background-color $ui-monokai-button--hover-backgroundColor
border-color $ui-monokai-borderColor
apply-theme(theme)
body[data-theme={theme}]
.root
&:hover
background-color get-theme-var(theme, 'button--hover-backgroundColor')
border-color get-theme-var(theme, 'borderColor')
.search-optionList
color white
border-color $ui-monokai-borderColor
background-color $ui-monokai-button-backgroundColor
.search-input
color get-theme-var(theme, 'text-color')
background-color transparent
border-color get-theme-var(theme, 'borderColor')
.search-optionList-item
&:hover
background-color lighten($ui-monokai-button--hover-backgroundColor, 15%)
.search-optionList
color get-theme-var(theme, 'text-color')
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'button-backgroundColor')
.search-optionList-item--active
background-color $ui-monokai-button--active-backgroundColor
color $ui-monokai-button--active-color
&:hover
background-color $ui-monokai-button--active-backgroundColor
color $ui-monokai-button--active-color
.search-optionList-item-name-surfix
color $ui-monokai-inactive-text-color
.search-optionList-item
&:hover
background-color lighten(get-theme-var(theme, 'button--hover-backgroundColor'), 15%)
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
&:hover
color #f8f8f2
background-color $ui-dark-button--hover-backgroundColor
border-color $ui-dracula-borderColor
.search-optionList-item--active
background-color get-theme-var(theme, 'button--active-backgroundColor')
color get-theme-var(theme, 'button--active-color')
&:hover
background-color get-theme-var(theme, 'button--active-backgroundColor')
color get-theme-var(theme, 'button--active-color')
.search-optionList
color #f8f8f2
border-color $ui-dracula-borderColor
background-color $ui-dracula-button-backgroundColor
.search-optionList-item-name-surfix
color get-theme-var(theme, 'inactive-text-color')
.search-optionList-item
&:hover
background-color lighten($ui-dracula-button--hover-backgroundColor, 15%)
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.search-optionList-item--active
background-color $ui-dracula-button--active-backgroundColor
color $ui-dracula-button--active-color
&:hover
background-color $ui-dark-button--hover-backgroundColor
color $ui-dracula-button--active-color
.search-optionList-item-name-surfix
color $ui-dracula-inactive-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -0,0 +1,71 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FromUrlButton.styl'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
class FromUrlButton extends React.Component {
constructor(props) {
super(props)
this.state = {
isActive: false
}
}
handleMouseDown(e) {
this.setState({
isActive: true
})
}
handleMouseUp(e) {
this.setState({
isActive: false
})
}
handleMouseLeave(e) {
this.setState({
isActive: false
})
}
render() {
const { className } = this.props
return (
<button
className={
_.isString(className) ? 'FromUrlButton ' + className : 'FromUrlButton'
}
styleName={
this.state.isActive || this.props.isActive ? 'root--active' : 'root'
}
onMouseDown={e => this.handleMouseDown(e)}
onMouseUp={e => this.handleMouseUp(e)}
onMouseLeave={e => this.handleMouseLeave(e)}
onClick={this.props.onClick}
>
<img
styleName='icon'
src={
this.state.isActive || this.props.isActive
? '../resources/icon/icon-external.svg'
: '../resources/icon/icon-external.svg'
}
/>
<span styleName='tooltip'>{i18n.__('Convert URL to Markdown')}</span>
</button>
)
}
}
FromUrlButton.propTypes = {
isActive: PropTypes.bool,
onClick: PropTypes.func,
className: PropTypes.string
}
export default CSSModules(FromUrlButton, styles)

View File

@@ -0,0 +1,41 @@
.root
top 45px
topBarButtonRight()
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 50px
right 125px
width 90px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.root--active
@extend .root
transition 0.15s
color $ui-favorite-star-button-color
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)
.icon
transition transform 0.15s
height 13px
body[data-theme="dark"]
.root
topBarButtonDark()
&:hover
transition 0.2s
color alpha($ui-favorite-star-button-color, 0.6)

View File

@@ -5,14 +5,18 @@ import styles from './FullscreenButton.styl'
import i18n from 'browser/lib/i18n'
const OSX = global.process.platform === 'darwin'
const FullscreenButton = ({
onClick
}) => {
const FullscreenButton = ({ onClick }) => {
const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B'
return (
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Fullscreen')}({hotkey})</span>
<button
styleName='control-fullScreenButton'
title={i18n.__('Fullscreen')}
onMouseDown={e => onClick(e)}
>
<img src='../resources/icon/icon-full.svg' />
<span lang={i18n.locale} styleName='tooltip'>
{i18n.__('Fullscreen')}({hotkey})
</span>
</button>
)
}

View File

@@ -4,12 +4,8 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoButton.styl'
import i18n from 'browser/lib/i18n'
const InfoButton = ({
onClick
}) => (
<button styleName='control-infoButton'
onClick={(e) => onClick(e)}
>
const InfoButton = ({ onClick }) => (
<button styleName='control-infoButton' onClick={e => onClick(e)}>
<img className='infoButton' src='../resources/icon/icon-info.svg' />
<span styleName='tooltip'>{i18n.__('Info')}</span>
</button>

View File

@@ -6,28 +6,47 @@ import copy from 'copy-to-clipboard'
import i18n from 'browser/lib/i18n'
class InfoPanel extends React.Component {
copyNoteLink () {
const {noteLink} = this.props
copyNoteLink() {
const { noteLink } = this.props
this.refs.noteLink.select()
copy(noteLink)
}
render () {
render() {
const {
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
storageName,
folderName,
noteLink,
updatedAt,
createdAt,
exportAsMd,
exportAsTxt,
exportAsHtml,
exportAsPdf,
wordCount,
letterCount,
type,
print
} = this.props
return (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div
className='infoPanel'
styleName='control-infoButton-panel'
style={{ display: 'none' }}
>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>{i18n.__('MODIFICATION DATE')}</p>
<p styleName='modification-date-desc'>
{i18n.__('MODIFICATION DATE')}
</p>
</div>
<hr />
{type === 'SNIPPET_NOTE'
? ''
: <div styleName='count-wrap'>
{type === 'SNIPPET_NOTE' ? (
''
) : (
<div styleName='count-wrap'>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{wordCount}</p>
<p styleName='infoPanel-sub-count'>{i18n.__('Words')}</p>
@@ -37,12 +56,9 @@ class InfoPanel extends React.Component {
<p styleName='infoPanel-sub-count'>{i18n.__('Letters')}</p>
</div>
</div>
}
)}
{type === 'SNIPPET_NOTE'
? ''
: <hr />
}
{type === 'SNIPPET_NOTE' ? '' : <hr />}
<div>
<p styleName='infoPanel-default'>{storageName}</p>
@@ -60,8 +76,18 @@ class InfoPanel extends React.Component {
</div>
<div>
<input styleName='infoPanel-noteLink' ref='noteLink' value={noteLink} onClick={(e) => { e.target.select() }} />
<button onClick={() => this.copyNoteLink()} styleName='infoPanel-copyButton'>
<input
styleName='infoPanel-noteLink'
ref='noteLink'
defaultValue={noteLink}
onClick={e => {
e.target.select()
}}
/>
<button
onClick={() => this.copyNoteLink()}
styleName='infoPanel-copyButton'
>
<i className='fa fa-clipboard' />
</button>
<p styleName='infoPanel-sub'>{i18n.__('NOTE LINK')}</p>
@@ -70,22 +96,39 @@ class InfoPanel extends React.Component {
<hr />
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
<button
styleName='export--enable'
onClick={e => exportAsMd(e, 'export-md')}
>
<i className='fa fa-file-code-o' />
<p>{i18n.__('.md')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
<button
styleName='export--enable'
onClick={e => exportAsTxt(e, 'export-txt')}
>
<i className='fa fa-file-text-o' />
<p>{i18n.__('.txt')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
<button
styleName='export--enable'
onClick={e => exportAsHtml(e, 'export-html')}
>
<i className='fa fa-html5' />
<p>{i18n.__('.html')}</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e, 'print')}>
<button
styleName='export--enable'
onClick={e => exportAsPdf(e, 'export-pdf')}
>
<i className='fa fa-file-pdf-o' />
<p>{i18n.__('.pdf')}</p>
</button>
<button styleName='export--enable' onClick={e => print(e, 'print')}>
<i className='fa fa-print' />
<p>{i18n.__('Print')}</p>
</button>
@@ -104,6 +147,7 @@ InfoPanel.propTypes = {
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired,
wordCount: PropTypes.number,
letterCount: PropTypes.number,
type: PropTypes.string.isRequired,

View File

@@ -15,7 +15,7 @@
right 25px
position absolute
padding 20px 25px 0 25px
width 300px
// width 300px
overflow auto
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
@@ -138,162 +138,49 @@
.export--unable
cursor not-allowed
body[data-theme="dark"]
.control-infoButton-panel
background-color $ui-dark-noteList-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.control-infoButton-panel
background-color get-theme-var(theme, 'noteList-backgroundColor')
.control-infoButton-panel-trash
background-color $ui-dark-noteList-backgroundColor
.control-infoButton-panel-trash
background-color get-theme-var(theme, 'noteList-backgroundColor')
.modification-date
color $ui-dark-text-color
.modification-date
color get-theme-var(theme, 'text-color')
.modification-date-desc
color $ui-inactive-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-dark-text-color
.infoPanel-defaul-count
color get-theme-var(theme, 'text-color')
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-dark-text-color
.infoPanel-default
color get-theme-var(theme, 'text-color')
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-dark-borderColor, 60%)
color $ui-dark-text-color
.infoPanel-noteLink
background-color alpha(get-theme-var(theme, 'borderColor'), 20%)
color get-theme-var(theme, 'text-color')
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-dark-borderColor, 20%)
color $ui-dark-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha(get-theme-var(theme, 'borderColor'), 20%)
color get-theme-var(theme, 'text-color')
p
color $ui-dark-inactive-text-color
&:hover
color get-theme-var(theme, 'text-color')
body[data-theme="solarized-dark"]
.control-infoButton-panel
background-color $ui-solarized-dark-noteList-backgroundColor
for theme in 'dark' 'solarized-dark' 'dracula'
apply-theme(theme)
.control-infoButton-panel-trash
background-color $ui-solarized-ark-noteList-backgroundColor
.modification-date
color $ui-solarized-ark-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-solarized-dark-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-solarized-ark-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-solarized-dark-borderColor, 20%)
color $ui-solarized-dark-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-solarized-dark-borderColor, 20%)
color $ui-solarized-ark-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-solarized-ark-text-color
body[data-theme="monokai"]
.control-infoButton-panel
background-color $ui-monokai-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-monokai-noteList-backgroundColor
.modification-date
color $ui-monokai-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-monokai-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-monokai-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-monokai-borderColor, 20%)
color $ui-monokai-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-monokai-borderColor, 20%)
color $ui-monokai-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-monokai-text-color
body[data-theme="dracula"]
.control-infoButton-panel
background-color $ui-dracula-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-dracula-noteList-backgroundColor
.modification-date
color $ui-dracula-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-dracula-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-dracula-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -5,9 +5,20 @@ import styles from './InfoPanel.styl'
import i18n from 'browser/lib/i18n'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml
storageName,
folderName,
updatedAt,
createdAt,
exportAsMd,
exportAsTxt,
exportAsHtml,
exportAsPdf
}) => (
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div
className='infoPanel'
styleName='control-infoButton-panel-trash'
style={{ display: 'none' }}
>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>{i18n.__('MODIFICATION DATE')}</p>
@@ -21,7 +32,10 @@ const InfoPanelTrashed = ({
</div>
<div>
<p styleName='infoPanel-default'><text styleName='infoPanel-trash'>Trash</text>{folderName}</p>
<p styleName='infoPanel-default'>
<text styleName='infoPanel-trash'>Trash</text>
{folderName}
</p>
<p styleName='infoPanel-sub'>{i18n.__('FOLDER')}</p>
</div>
@@ -31,22 +45,34 @@ const InfoPanelTrashed = ({
</div>
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
<button
styleName='export--enable'
onClick={e => exportAsMd(e, 'export-md')}
>
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
<button
styleName='export--enable'
onClick={e => exportAsTxt(e, 'export-txt')}
>
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
<button
styleName='export--enable'
onClick={e => exportAsHtml(e, 'export-html')}
>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--unable'>
<button
styleName='export--enable'
onClick={e => exportAsPdf(e, 'export-pdf')}
>
<i className='fa fa-file-pdf-o' />
<p>.pdf</p>
</button>
@@ -61,7 +87,8 @@ InfoPanelTrashed.propTypes = {
createdAt: PropTypes.string.isRequired,
exportAsMd: PropTypes.func.isRequired,
exportAsTxt: PropTypes.func.isRequired,
exportAsHtml: PropTypes.func.isRequired
exportAsHtml: PropTypes.func.isRequired,
exportAsPdf: PropTypes.func.isRequired
}
export default CSSModules(InfoPanelTrashed, styles)

View File

@@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
@@ -9,7 +10,6 @@ import StarButton from './StarButton'
import TagSelect from './TagSelect'
import FolderSelect from './FolderSelect'
import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import markdown from 'browser/lib/markdownTextHelper'
import StatusBar from '../StatusBar'
@@ -30,111 +30,147 @@ import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
import striptags from 'striptags'
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
import markdownToc from 'browser/lib/markdown-toc-generator'
import queryString from 'query-string'
import { replace } from 'connected-react-router'
import ToggleDirectionButton from 'browser/main/Detail/ToggleDirectionButton'
class MarkdownNoteDetail extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
isMovingNote: false,
note: Object.assign({
title: '',
content: '',
linesHighlighted: []
}, props.note),
note: Object.assign(
{
title: '',
content: '',
linesHighlighted: []
},
props.note
),
isLockButtonShown: props.config.editor.type !== 'SPLIT',
isLocked: false,
editorType: props.config.editor.type,
switchPreview: props.config.editor.switchPreview
switchPreview: props.config.editor.switchPreview,
RTL: false
}
this.dispatchTimer = null
this.generateToc = this.handleGenerateToc.bind(this)
this.toggleLockButton = this.handleToggleLockButton.bind(this)
this.generateToc = () => this.handleGenerateToc()
this.handleUpdateContent = this.handleUpdateContent.bind(this)
this.handleSwitchStackDirection = this.handleSwitchStackDirection.bind(this)
this.getNote = this.getNote.bind(this)
}
focus () {
focus() {
this.refs.content.focus()
}
componentDidMount () {
componentDidMount() {
ee.on('editor:orientation', this.handleSwitchStackDirection)
ee.on('topbar:togglelockbutton', this.toggleLockButton)
ee.on('topbar:toggledirectionbutton', () => this.handleSwitchDirection())
ee.on('topbar:togglemodebutton', () => {
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
const reversedType =
this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
this.handleSwitchMode(reversedType)
})
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
ee.on('code:generate-toc', this.generateToc)
// Focus content if using blur or double click
if (this.state.switchPreview === 'BLUR' || this.state.switchPreview === 'DBL_CLICK') this.focus()
}
componentWillReceiveProps (nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
const isNewNote = nextProps.note.key !== this.props.note.key
const hasDeletedTags = nextProps.note.tags.length < this.props.note.tags.length
const hasDeletedTags =
nextProps.note.tags.length < this.props.note.tags.length
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
if (this.saveQueue != null) this.saveNow()
this.setState(
{
note: Object.assign({ linesHighlighted: [] }, nextProps.note)
},
() => {
this.refs.content.reload()
if (this.refs.tags) this.refs.tags.reset()
}
)
}
// Focus content if using blur or double click
// --> Moved here from componentDidMount so a re-render during search won't set focus to the editor
const { switchPreview } = nextProps.config.editor
if (this.state.switchPreview !== switchPreview) {
this.setState({
note: Object.assign({linesHighlighted: []}, nextProps.note)
}, () => {
this.refs.content.reload()
if (this.refs.tags) this.refs.tags.reset()
switchPreview
})
if (switchPreview === 'BLUR' || switchPreview === 'DBL_CLICK') {
console.log('setting focus', switchPreview)
this.focus()
}
}
}
componentWillUnmount () {
componentWillUnmount() {
ee.off('topbar:togglelockbutton', this.toggleLockButton)
ee.on('topbar:toggledirectionbutton', this.handleSwitchDirection)
ee.off('code:generate-toc', this.generateToc)
if (this.saveQueue != null) this.saveNow()
}
handleUpdateTag () {
handleUpdateTag() {
const { note } = this.state
if (this.refs.tags) note.tags = this.refs.tags.value
this.updateNote(note)
}
handleUpdateContent () {
handleUpdateContent() {
const { note } = this.state
note.content = this.refs.content.value
note.title = markdown.strip(striptags(findNoteTitle(note.content, this.props.config.editor.enableFrontMatterTitle, this.props.config.editor.frontMatterTitleField)))
let title = findNoteTitle(
note.content,
this.props.config.editor.enableFrontMatterTitle,
this.props.config.editor.frontMatterTitleField
)
title = striptags(title)
title = markdown.strip(title)
note.title = title
this.updateNote(note)
}
updateNote (note) {
updateNote(note) {
note.updatedAt = new Date()
this.setState({note}, () => {
this.setState({ note }, () => {
this.save()
})
}
save () {
save() {
clearTimeout(this.saveQueue)
this.saveQueue = setTimeout(() => {
this.saveNow()
}, 1000)
}
saveNow () {
saveNow() {
const { note, dispatch } = this.props
clearTimeout(this.saveQueue)
this.saveQueue = null
dataApi
.updateNote(note.storage, note.key, this.state.note)
.then((note) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
dataApi.updateNote(note.storage, note.key, this.state.note).then(note => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
})
}
handleFolderChange (e) {
handleFolderChange(e) {
const { note } = this.state
const value = this.refs.folder.value
const splitted = value.split('-')
@@ -143,60 +179,71 @@ class MarkdownNoteDetail extends React.Component {
dataApi
.moveNote(note.storage, note.key, newStorageKey, newFolderKey)
.then((newNote) => {
this.setState({
isMovingNote: true,
note: Object.assign({}, newNote)
}, () => {
const { dispatch, location } = this.props
dispatch({
type: 'MOVE_NOTE',
originNote: note,
note: newNote
})
hashHistory.replace({
pathname: location.pathname,
query: {
key: newNote.key
}
})
this.setState({
isMovingNote: false
})
})
.then(newNote => {
this.setState(
{
isMovingNote: true,
note: Object.assign({}, newNote)
},
() => {
const { dispatch, location } = this.props
dispatch({
type: 'MOVE_NOTE',
originNote: note,
note: newNote
})
dispatch(
replace({
pathname: location.pathname,
search: queryString.stringify({
key: newNote.key
})
})
)
this.setState({
isMovingNote: false
})
}
)
})
}
handleStarButtonClick (e) {
handleStarButtonClick(e) {
const { note } = this.state
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
if (!note.isStarred)
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
note.isStarred = !note.isStarred
this.setState({
note
}, () => {
this.save()
})
this.setState(
{
note
},
() => {
this.save()
}
)
}
exportAsFile () {
exportAsFile() {}
}
exportAsMd () {
exportAsMd() {
ee.emit('export:save-md')
}
exportAsTxt () {
exportAsTxt() {
ee.emit('export:save-text')
}
exportAsHtml () {
exportAsHtml() {
ee.emit('export:save-html')
}
handleKeyDown (e) {
exportAsPdf() {
ee.emit('export:save-pdf')
}
handleKeyDown(e) {
switch (e.keyCode) {
// tab key
case 9:
@@ -206,7 +253,11 @@ class MarkdownNoteDetail extends React.Component {
} else if (e.ctrlKey && e.shiftKey) {
e.preventDefault()
this.jumpPrevTab()
} else if (!e.ctrlKey && !e.shiftKey && e.target === this.refs.description) {
} else if (
!e.ctrlKey &&
!e.shiftKey &&
e.target === this.refs.description
) {
e.preventDefault()
this.focusEditor()
}
@@ -214,9 +265,8 @@ class MarkdownNoteDetail extends React.Component {
// I key
case 73:
{
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
const isSuper =
global.process.platform === 'darwin' ? e.metaKey : e.ctrlKey
if (isSuper) {
e.preventDefault()
this.handleInfoButtonClick(e)
@@ -226,17 +276,17 @@ class MarkdownNoteDetail extends React.Component {
}
}
handleTrashButtonClick (e) {
handleTrashButtonClick(e) {
const { note } = this.state
const { isTrashed } = note
const { confirmDeletion } = this.props.config.ui
if (isTrashed) {
if (confirmDeleteNote(confirmDeletion, true)) {
const {note, dispatch} = this.props
const { note, dispatch } = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
.then(data => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
@@ -252,103 +302,139 @@ class MarkdownNoteDetail extends React.Component {
if (confirmDeleteNote(confirmDeletion, false)) {
note.isTrashed = true
this.setState({
note
}, () => {
this.save()
})
this.setState(
{
note
},
() => {
this.save()
}
)
ee.emit('list:next')
}
}
}
handleUndoButtonClick (e) {
handleUndoButtonClick(e) {
const { note } = this.state
note.isTrashed = false
this.setState({
note
}, () => {
this.save()
this.refs.content.reload()
ee.emit('list:next')
})
this.setState(
{
note
},
() => {
this.save()
this.refs.content.reload()
ee.emit('list:next')
}
)
}
handleFullScreenButton (e) {
handleFullScreenButton(e) {
ee.emit('editor:fullscreen')
}
handleLockButtonMouseDown (e) {
handleLockButtonMouseDown(e) {
e.preventDefault()
ee.emit('editor:lock')
this.setState({ isLocked: !this.state.isLocked })
if (this.state.isLocked) this.focus()
}
getToggleLockButton () {
return this.state.isLocked ? '../resources/icon/icon-previewoff-on.svg' : '../resources/icon/icon-previewoff-off.svg'
getToggleLockButton() {
return this.state.isLocked
? '../resources/icon/icon-lock.svg'
: '../resources/icon/icon-unlock.svg'
}
handleDeleteKeyDown (e) {
handleDeleteKeyDown(e) {
if (e.keyCode === 27) this.handleDeleteCancelButtonClick(e)
}
handleToggleLockButton (event, noteStatus) {
handleToggleLockButton(event, noteStatus) {
// first argument event is not used
if (noteStatus === 'CODE') {
this.setState({isLockButtonShown: true})
this.setState({ isLockButtonShown: true })
} else {
this.setState({isLockButtonShown: false})
this.setState({ isLockButtonShown: false })
}
}
handleGenerateToc () {
handleGenerateToc() {
const editor = this.refs.content.refs.code.editor
markdownToc.generateInEditor(editor)
}
handleFocus (e) {
handleFocus(e) {
this.focus()
}
handleInfoButtonClick (e) {
handleInfoButtonClick(e) {
const infoPanel = document.querySelector('.infoPanel')
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
if (infoPanel.style)
infoPanel.style.display =
infoPanel.style.display === 'none' ? 'inline' : 'none'
}
print (e) {
print(e) {
ee.emit('print')
}
handleSwitchMode (type) {
handleSwitchMode(type) {
// If in split mode, hide the lock button
this.setState({ editorType: type, isLockButtonShown: !(type === 'SPLIT') }, () => {
this.focus()
const newConfig = Object.assign({}, this.props.config)
newConfig.editor.type = type
ConfigManager.set(newConfig)
})
this.setState(
{ editorType: type, isLockButtonShown: type !== 'SPLIT' },
() => {
this.focus()
const newConfig = Object.assign({}, this.props.config)
newConfig.editor.type = type
ConfigManager.set(newConfig)
}
)
}
handleDeleteNote () {
handleSwitchStackDirection() {
this.setState(
prevState => ({ isStacking: !prevState.isStacking }),
() => {
this.focus()
const newConfig = Object.assign({}, this.props.config)
newConfig.ui.isStacking = this.state.isStacking
ConfigManager.set(newConfig)
}
)
}
handleSwitchDirection() {
if (!this.props.config.editor.rtlEnabled) {
return
}
// If in split mode, hide the lock button
const direction = this.state.RTL
this.setState({ RTL: !direction })
}
handleDeleteNote() {
this.handleTrashButtonClick()
}
handleClearTodo () {
handleClearTodo() {
const { note } = this.state
const splitted = note.content.split('\n')
const clearTodoContent = splitted.map((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/\[x\]/i)) {
return line.replace(/\[x\]/i, '[ ]')
} else {
return line
}
}).join('\n')
const clearTodoContent = splitted
.map(line => {
const trimmedLine = line.trim()
if (trimmedLine.match(/\[x\]/i)) {
return line.replace(/\[x\]/i, '[ ]')
} else {
return line
}
})
.join('\n')
note.content = clearTodoContent
this.refs.content.setValue(note.content)
@@ -356,157 +442,200 @@ class MarkdownNoteDetail extends React.Component {
this.updateNote(note)
}
renderEditor () {
getNote() {
return this.state.note
}
renderEditor() {
const { config, ignorePreviewPointerEvents } = this.props
const { note } = this.state
const { note, isStacking } = this.state
if (this.state.editorType === 'EDITOR_PREVIEW') {
return <MarkdownEditor
ref='content'
styleName='body-noteEditor'
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent.bind(this)}
isLocked={this.state.isLocked}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
return (
<MarkdownEditor
ref='content'
styleName='body-noteEditor'
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
getNote={this.getNote}
RTL={config.editor.rtlEnabled && this.state.RTL}
/>
)
} else {
return <MarkdownSplitEditor
ref='content'
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
return (
<MarkdownSplitEditor
ref='content'
config={config}
value={note.content}
storageKey={note.storage}
noteKey={note.key}
isStacking={isStacking}
linesHighlighted={note.linesHighlighted}
onChange={this.handleUpdateContent}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
getNote={this.getNote}
RTL={config.editor.rtlEnabled && this.state.RTL}
/>
)
}
}
render () {
const { data, location, config } = this.props
render() {
const { data, dispatch, location, config } = this.props
const { note, editorType } = this.state
const storageKey = note.storage
const folderKey = note.folder
const options = []
data.storageMap.forEach((storage, index) => {
storage.folders.forEach((folder) => {
storage.folders.forEach(folder => {
options.push({
storage: storage,
folder: folder
})
})
})
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
const trashTopBar = <div styleName='info'>
<div styleName='info-left'>
<RestoreButton onClick={(e) => this.handleUndoButtonClick(e)} />
</div>
<div styleName='info-right'>
<PermanentDeleteButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanelTrashed
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsHtml={this.exportAsHtml}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
/>
</div>
</div>
const currentOption = _.find(
options,
option =>
option.storage.key === storageKey && option.folder.key === folderKey
)
const detailTopBar = <div styleName='info'>
<div styleName='info-left'>
<div styleName='info-left-top'>
<FolderSelect styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={(e) => this.handleFolderChange(e)}
// currentOption may be undefined
const storageName = _.get(currentOption, 'storage.name') || ''
const folderName = _.get(currentOption, 'folder.name') || ''
const trashTopBar = (
<div styleName='info'>
<div styleName='info-left'>
<RestoreButton onClick={e => this.handleUndoButtonClick(e)} />
</div>
<div styleName='info-right'>
<PermanentDeleteButton
onClick={e => this.handleTrashButtonClick(e)}
/>
<InfoButton onClick={e => this.handleInfoButtonClick(e)} />
<InfoPanelTrashed
storageName={storageName}
folderName={folderName}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsHtml={this.exportAsHtml}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsPdf={this.exportAsPdf}
/>
</div>
<TagSelect
ref='tags'
value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data}
onChange={this.handleUpdateTag.bind(this)}
coloredTags={config.coloredTags}
/>
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
</div>
<div styleName='info-right'>
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
)
{(() => {
const imgSrc = `${this.getToggleLockButton()}`
const lockButtonComponent =
<button styleName='control-lockButton'
onFocus={(e) => this.handleFocus(e)}
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
>
<img styleName='iconInfo' src={imgSrc} />
{this.state.isLocked ? <span styleName='tooltip'>Unlock</span> : <span styleName='tooltip'>Lock</span>}
</button>
const detailTopBar = (
<div styleName='info'>
<div styleName='info-left'>
<div>
<FolderSelect
styleName='info-left-top-folderSelect'
value={this.state.note.storage + '-' + this.state.note.folder}
ref='folder'
data={data}
onChange={e => this.handleFolderChange(e)}
/>
</div>
return (
this.state.isLockButtonShown ? lockButtonComponent : ''
)
})()}
<TagSelect
ref='tags'
value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data}
dispatch={dispatch}
onChange={this.handleUpdateTag.bind(this)}
coloredTags={config.coloredTags}
/>
<TodoListPercentage
onClearCheckboxClick={e => this.handleClearTodo(e)}
percentageOfTodo={getTodoPercentageOfCompleted(note.content)}
/>
</div>
<div styleName='info-right'>
<ToggleModeButton
onClick={e => this.handleSwitchMode(e)}
editorType={editorType}
/>
{this.props.config.editor.rtlEnabled && (
<ToggleDirectionButton
onClick={e => this.handleSwitchDirection(e)}
isRTL={this.state.RTL}
/>
)}
<StarButton
onClick={e => this.handleStarButtonClick(e)}
isActive={note.isStarred}
/>
<FullscreenButton onClick={(e) => this.handleFullScreenButton(e)} />
{(() => {
const imgSrc = `${this.getToggleLockButton()}`
const lockButtonComponent = (
<button
styleName='control-lockButton'
onFocus={e => this.handleFocus(e)}
onMouseDown={e => this.handleLockButtonMouseDown(e)}
>
<img src={imgSrc} />
{this.state.isLocked ? (
<span styleName='tooltip'>Unlock</span>
) : (
<span styleName='tooltip'>Lock</span>
)}
</button>
)
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
return this.state.isLockButtonShown ? lockButtonComponent : ''
})()}
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<FullscreenButton onClick={e => this.handleFullScreenButton(e)} />
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](:note:${location.query.key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsHtml={this.exportAsHtml}
wordCount={note.content.split(' ').length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
print={this.print}
/>
<TrashButton onClick={e => this.handleTrashButtonClick(e)} />
<InfoButton onClick={e => this.handleInfoButtonClick(e)} />
<InfoPanel
storageName={storageName}
folderName={folderName}
noteLink={`[${note.title}](:note:${
queryString.parse(location.search).key
})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}
exportAsTxt={this.exportAsTxt}
exportAsHtml={this.exportAsHtml}
exportAsPdf={this.exportAsPdf}
wordCount={note.content.trim().split(/\s+/g).length}
letterCount={note.content.replace(/\r?\n/g, '').length}
type={note.type}
print={this.print}
/>
</div>
</div>
</div>
)
return (
<div className='NoteDetail'
<div
className='NoteDetail'
style={this.props.style}
styleName='root'
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyDown={e => this.handleKeyDown(e)}
>
{location.pathname === '/trashed' ? trashTopBar : detailTopBar}
<div styleName='body'>
{this.renderEditor()}
</div>
<div styleName='body'>{this.renderEditor()}</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
@@ -520,9 +649,7 @@ class MarkdownNoteDetail extends React.Component {
MarkdownNoteDetail.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array,
note: PropTypes.shape({
}),
note: PropTypes.shape({}),
style: PropTypes.shape({
left: PropTypes.number
}),

View File

@@ -15,7 +15,7 @@
.control-lockButton
topBarButtonRight()
position absolute
right 225px
right 265px
&:hover .tooltip
opacity 1
@@ -66,18 +66,14 @@ body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
apply-theme(theme)
body[data-theme={theme}]
.root
border-left 1px solid get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
body[data-theme="solarized-dark"]
.root
border-left 1px solid $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="monokai"]
.root
border-left 1px solid $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
for theme in $themes
apply-theme(theme)

View File

@@ -15,6 +15,14 @@ $info-margin-under-border = 30px
padding 0 20px
z-index 99
.info > div
> button
-webkit-user-drag none
user-select none
> img, span
-webkit-user-drag none
user-select none
.info-left
padding 0 10px
width 100%
@@ -94,17 +102,14 @@ body[data-theme="dark"]
.undo-button
topBarButtonDark()
body[data-theme="solarized-dark"]
.info
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.info
border-color get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
body[data-theme="monokai"]
.info
border-color $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="dracula"]
.info
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
for theme in $themes
apply-theme(theme)

View File

@@ -4,13 +4,9 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
import i18n from 'browser/lib/i18n'
const PermanentDeleteButton = ({
onClick
}) => (
<button styleName='control-trashButton--in-trash'
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
const PermanentDeleteButton = ({ onClick }) => (
<button styleName='control-trashButton--in-trash' onClick={e => onClick(e)}>
<img src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>{i18n.__('Permanent Delete')}</span>
</button>
)

View File

@@ -4,12 +4,8 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './RestoreButton.styl'
import i18n from 'browser/lib/i18n'
const RestoreButton = ({
onClick
}) => (
<button styleName='control-restoreButton'
onClick={onClick}
>
const RestoreButton = ({ onClick }) => (
<button styleName='control-restoreButton' onClick={onClick}>
<i className='fa fa-undo fa-fw' styleName='iconRestore' />
<span styleName='tooltip'>{i18n.__('Restore')}</span>
</button>

File diff suppressed because it is too large Load Diff

View File

@@ -156,78 +156,35 @@ body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
body[data-theme="solarized-dark"]
.root
border-left 1px solid $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.root
border-left 1px solid get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
.body
background-color $ui-solarized-dark-noteDetail-backgroundColor
.body
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
.body .description textarea
background-color $ui-solarized-dark-noteDetail-backgroundColor
color $ui-solarized-dark-text-color
border 1px solid $ui-solarized-dark-borderColor
.body .description textarea
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
color get-theme-var(theme, 'text-color')
border 1px solid get-theme-var(theme, 'borderColor')
.tabList .tabButton
border-color $ui-solarized-dark-borderColor
.tabList .tabButton
border-color get-theme-var(theme, 'borderColor')
.tabButton
&:hover
color $ui-solarized-dark-button--active-color
background-color $ui-solarized-dark-noteDetail-backgroundColor
transition 0.15s
.tabButton
&:hover
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
transition 0.15s
.tabList
background-color $ui-solarized-dark-noteDetail-backgroundColor
color $ui-solarized-dark-text-color
.tabList
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.root
border-left 1px solid $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.body
background-color $ui-monokai-noteDetail-backgroundColor
.body .description textarea
background-color $ui-monokai-noteDetail-backgroundColor
color $ui-monokai-text-color
border 1px solid $ui-monokai-borderColor
.tabList .tabButton
border-color $ui-monokai-borderColor
.tabButton
&:hover
color $ui-monokai-text-color
background-color $ui-monokai-noteDetail-backgroundColor
.tabList
background-color $ui-monokai-noteDetail-backgroundColor
color $ui-monokai-text-color
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
.body
background-color $ui-dracula-noteDetail-backgroundColor
.body .description textarea
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
border 1px solid $ui-dracula-borderColor
.tabList .tabButton
border-color $ui-dracula-borderColor
.tabButton
&:hover
color $ui-dracula-text-color
background-color $ui-dracula-noteDetail-backgroundColor
.tabList
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -6,7 +6,7 @@ import _ from 'lodash'
import i18n from 'browser/lib/i18n'
class StarButton extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -14,47 +14,51 @@ class StarButton extends React.Component {
}
}
handleMouseDown (e) {
handleMouseDown(e) {
this.setState({
isActive: true
})
}
handleMouseUp (e) {
handleMouseUp(e) {
this.setState({
isActive: false
})
}
handleMouseLeave (e) {
handleMouseLeave(e) {
this.setState({
isActive: false
})
}
render () {
render() {
const { className } = this.props
return (
<button className={_.isString(className)
? 'StarButton ' + className
: 'StarButton'
<button
className={
_.isString(className) ? 'StarButton ' + className : 'StarButton'
}
styleName={this.state.isActive || this.props.isActive
? 'root--active'
: 'root'
styleName={
this.state.isActive || this.props.isActive ? 'root--active' : 'root'
}
onMouseDown={(e) => this.handleMouseDown(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
onMouseLeave={(e) => this.handleMouseLeave(e)}
onClick={this.props.onClick}>
<img styleName='icon'
src={this.state.isActive || this.props.isActive
? '../resources/icon/icon-starred.svg'
: '../resources/icon/icon-star.svg'
onMouseDown={e => this.handleMouseDown(e)}
onMouseUp={e => this.handleMouseUp(e)}
onMouseLeave={e => this.handleMouseLeave(e)}
onClick={this.props.onClick}
>
<img
styleName='icon'
src={
this.state.isActive || this.props.isActive
? '../resources/icon/icon-starred.svg'
: '../resources/icon/icon-star.svg'
}
/>
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Star')}</span>
<span lang={i18n.locale} styleName='tooltip'>
{i18n.__('Star')}
</span>
</button>
)
}

View File

@@ -8,9 +8,10 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import i18n from 'browser/lib/i18n'
import ee from 'browser/main/lib/eventEmitter'
import Autosuggest from 'react-autosuggest'
import { push } from 'connected-react-router'
class TagSelect extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -19,15 +20,20 @@ class TagSelect extends React.Component {
}
this.handleAddTag = this.handleAddTag.bind(this)
this.handleRenameTag = this.handleRenameTag.bind(this)
this.onInputBlur = this.onInputBlur.bind(this)
this.onInputChange = this.onInputChange.bind(this)
this.onInputKeyDown = this.onInputKeyDown.bind(this)
this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this)
this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this)
this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(
this
)
this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(
this
)
this.onSuggestionSelected = this.onSuggestionSelected.bind(this)
}
addNewTag (newTag) {
addNewTag(newTag) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
newTag = newTag.trim().replace(/ +/g, '_')
@@ -43,9 +49,7 @@ class TagSelect extends React.Component {
}
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
value = _.isArray(value) ? value.slice() : []
if (!_.includes(value, newTag)) {
value.push(newTag)
@@ -55,68 +59,87 @@ class TagSelect extends React.Component {
value = _.sortBy(value)
}
this.setState({
newTag: ''
}, () => {
this.value = value
this.props.onChange()
})
this.setState(
{
newTag: ''
},
() => {
this.value = value
this.props.onChange()
}
)
}
buildSuggestions () {
this.suggestions = _.sortBy(this.props.data.tagNoteMap.map(
(tag, name) => ({
name,
nameLC: name.toLowerCase(),
size: tag.size
})
).filter(
tag => tag.size > 0
), ['name'])
buildSuggestions() {
this.suggestions = _.sortBy(
this.props.data.tagNoteMap
.map((tag, name) => ({
name,
nameLC: name.toLowerCase(),
size: tag.size
}))
.filter(tag => tag.size > 0),
['name']
)
}
componentDidMount () {
componentDidMount() {
this.value = this.props.value
this.buildSuggestions()
ee.on('editor:add-tag', this.handleAddTag)
ee.on('sidebar:rename-tag', this.handleRenameTag)
}
componentDidUpdate () {
componentDidUpdate() {
this.value = this.props.value
}
componentWillUnmount () {
componentWillUnmount() {
ee.off('editor:add-tag', this.handleAddTag)
ee.off('sidebar:rename-tag', this.handleRenameTag)
}
handleAddTag () {
handleAddTag() {
this.refs.newTag.input.focus()
}
handleTagLabelClick (tag) {
const { router } = this.context
router.push(`/tags/${tag}`)
handleRenameTag(event, tagChange) {
const { value } = this.props
const { tag, updatedTag } = tagChange
const newTags = value.slice()
newTags[value.indexOf(tag)] = updatedTag
this.value = newTags
this.props.onChange()
}
handleTagRemoveButtonClick (tag) {
handleTagLabelClick(tag) {
const { dispatch } = this.props
// Note: `tag` requires encoding later.
// E.g. % in tag is a problem (see issue #3170) - encodeURIComponent(tag) is not working.
dispatch(push(`/tags/${tag}`))
}
handleTagRemoveButtonClick(tag) {
this.removeTagByCallback((value, tag) => {
value.splice(value.indexOf(tag), 1)
}, tag)
}
onInputBlur (e) {
onInputBlur(e) {
this.submitNewTag()
}
onInputChange (e, { newValue, method }) {
onInputChange(e, { newValue, method }) {
this.setState({
newTag: newValue
})
}
onInputKeyDown (e) {
onInputKeyDown(e) {
switch (e.keyCode) {
case 9:
e.preventDefault()
@@ -132,17 +155,18 @@ class TagSelect extends React.Component {
}
}
onSuggestionsClearRequested () {
onSuggestionsClearRequested() {
this.setState({
suggestions: []
})
}
onSuggestionsFetchRequested ({ value }) {
onSuggestionsFetchRequested({ value }) {
const valueLC = value.toLowerCase()
const suggestions = _.filter(
this.suggestions,
tag => !_.includes(this.value, tag.name) && tag.nameLC.indexOf(valueLC) !== -1
tag =>
!_.includes(this.value, tag.name) && tag.nameLC.indexOf(valueLC) !== -1
)
this.setState({
@@ -150,22 +174,20 @@ class TagSelect extends React.Component {
})
}
onSuggestionSelected (event, { suggestion, suggestionValue }) {
onSuggestionSelected(event, { suggestion, suggestionValue }) {
this.addNewTag(suggestionValue)
}
removeLastTag () {
this.removeTagByCallback((value) => {
removeLastTag() {
this.removeTagByCallback(value => {
value.pop()
})
}
removeTagByCallback (callback, tag = null) {
removeTagByCallback(callback, tag = null) {
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
value = _.isArray(value) ? value.slice() : []
callback(value, tag)
value = _.uniq(value)
@@ -173,7 +195,7 @@ class TagSelect extends React.Component {
this.props.onChange()
}
reset () {
reset() {
this.buildSuggestions()
this.setState({
@@ -181,51 +203,60 @@ class TagSelect extends React.Component {
})
}
submitNewTag () {
submitNewTag() {
this.addNewTag(this.refs.newTag.input.value)
}
render () {
render() {
const { value, className, showTagsAlphabetically, coloredTags } = this.props
const tagList = _.isArray(value)
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
const wrapperStyle = {}
const textStyle = {}
const BLACK = '#333333'
const WHITE = '#f1f1f1'
const color = coloredTags[tag]
const invertedColor = color && invertColor(color, { black: BLACK, white: WHITE })
let iconRemove = '../resources/icon/icon-x.svg'
if (color) {
wrapperStyle.backgroundColor = color
textStyle.color = invertedColor
}
if (invertedColor === WHITE) {
iconRemove = '../resources/icon/icon-x-light.svg'
}
return (
<span styleName='tag'
key={tag}
style={wrapperStyle}
>
<span styleName='tag-label' style={textStyle} onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
<button styleName='tag-removeButton'
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
>
<img className='tag-removeButton-icon' src={iconRemove} width='8px' />
</button>
</span>
)
})
? (showTagsAlphabetically ? _.sortBy(value) : value).map(tag => {
const wrapperStyle = {}
const textStyle = {}
const BLACK = '#333333'
const WHITE = '#f1f1f1'
const color = coloredTags[tag]
const invertedColor =
color && invertColor(color, { black: BLACK, white: WHITE })
let iconRemove = '../resources/icon/icon-x.svg'
if (color) {
wrapperStyle.backgroundColor = color
textStyle.color = invertedColor
}
if (invertedColor === WHITE) {
iconRemove = '../resources/icon/icon-x-light.svg'
}
return (
<span styleName='tag' key={tag} style={wrapperStyle}>
<span
styleName='tag-label'
style={textStyle}
onClick={e => this.handleTagLabelClick(tag)}
>
#{tag}
</span>
<button
styleName='tag-removeButton'
onClick={e => this.handleTagRemoveButtonClick(tag)}
>
<img
className='tag-removeButton-icon'
src={iconRemove}
width='8px'
/>
</button>
</span>
)
})
: []
const { newTag, suggestions } = this.state
return (
<div className={_.isString(className)
? 'TagSelect ' + className
: 'TagSelect'
<div
className={
_.isString(className) ? 'TagSelect ' + className : 'TagSelect'
}
styleName='root'
>
@@ -237,11 +268,7 @@ class TagSelect extends React.Component {
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
getSuggestionValue={suggestion => suggestion.name}
renderSuggestion={suggestion => (
<div>
{suggestion.name}
</div>
)}
renderSuggestion={suggestion => <div>{suggestion.name}</div>}
inputProps={{
placeholder: i18n.__('Add tag...'),
value: newTag,
@@ -255,11 +282,8 @@ class TagSelect extends React.Component {
}
}
TagSelect.contextTypes = {
router: PropTypes.shape({})
}
TagSelect.propTypes = {
dispatch: PropTypes.func,
className: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func,

View File

@@ -54,35 +54,20 @@ body[data-theme="dark"]
.tag-label
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.tag
background-color $ui-solarized-dark-tag-backgroundColor
apply-theme(theme)
body[data-theme={theme}]
.tag
background-color get-theme-var(theme, 'tag-backgroundColor')
.tag-removeButton
border-color $ui-button--focus-borderColor
background-color transparent
.tag-removeButton
border-color $ui-button--focus-borderColor
background-color transparent
.tag-label
color $ui-solarized-dark-text-color
.tag-label
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.tag
background-color $ui-monokai-tag-backgroundColor
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.tag-removeButton
border-color $ui-button--focus-borderColor
background-color transparent
.tag-label
color $ui-monokai-text-color
body[data-theme="dracula"]
.tag
background-color $ui-dracula-tag-backgroundColor
.tag-removeButton
border-color $ui-dracula-button--focus-borderColor
background-color transparent
.tag-label
color $ui-dracula-borderColor
for theme in $themes
apply-theme(theme)

View File

@@ -0,0 +1,26 @@
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleDirectionButton.styl'
import i18n from 'browser/lib/i18n'
const ToggleDirectionButton = ({ onClick, isRTL }) => (
<div styleName='control-toggleModeButton'>
<div styleName={isRTL ? 'active' : undefined} onClick={() => onClick()}>
<img src={!isRTL ? '../resources/icon/icon-left-to-right.svg' : ''} />
</div>
<div styleName={!isRTL ? 'active' : undefined} onClick={() => onClick()}>
<img src={!isRTL ? '' : '../resources/icon/icon-right-to-left.svg'} />
</div>
<span lang={i18n.locale} styleName='tooltip'>
{i18n.__('Toggle Direction')}
</span>
</div>
)
ToggleDirectionButton.propTypes = {
onClick: PropTypes.func.isRequired,
isRTL: PropTypes.bool.isRequired
}
export default CSSModules(ToggleDirectionButton, styles)

View File

@@ -0,0 +1,85 @@
.control-toggleModeButton
height 25px
border-radius 50px
background-color #F4F4F4
width 52px
display flex
align-items center
position: relative
top 2px
margin-left 5px
.active
background-color #1EC38B
width 33px
height 24px
box-shadow 2px 0px 7px #eee
z-index 1
div
width 40px
height 100%
border-radius 50%
display flex
align-items center
justify-content center
cursor pointer
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 33px
left -10px
z-index 200
width 80px
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.tooltip:lang(ja)
@extend .tooltip
left -8px
width 70px
.control-toggleModeButton
-webkit-user-drag none
user-select none
> div img
-webkit-user-drag none
user-select none
body[data-theme="dark"]
.control-fullScreenButton
topBarButtonDark()
.control-toggleModeButton
background-color #3A404C
.active
background-color #1EC38B
box-shadow 2px 0px 7px #444444
body[data-theme="solarized-dark"]
.control-toggleModeButton
background-color #002B36
.active
background-color #1EC38B
box-shadow 2px 0px 7px #222222
apply-theme(theme)
body[data-theme={theme}]
.control-toggleModeButton
background-color get-theme-var(theme, 'borderColor')
.active
background-color get-theme-var(theme, 'active-color')
box-shadow 2px 0px 7px #222222
for theme in 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -4,23 +4,41 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleModeButton.styl'
import i18n from 'browser/lib/i18n'
const ToggleModeButton = ({
onClick, editorType
}) => (
const ToggleModeButton = ({ onClick, editorType }) => (
<div styleName='control-toggleModeButton'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
<div
styleName={editorType === 'SPLIT' ? 'active' : undefined}
onClick={() => onClick('SPLIT')}
>
<img
src={
editorType === 'EDITOR_PREVIEW'
? '../resources/icon/icon-mode-markdown-off-active.svg'
: ''
}
/>
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
<div
styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : undefined}
onClick={() => onClick('EDITOR_PREVIEW')}
>
<img
src={
editorType === 'EDITOR_PREVIEW'
? ''
: '../resources/icon/icon-mode-split-on-active.svg'
}
/>
</div>
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
<span lang={i18n.locale} styleName='tooltip'>
{i18n.__('Toggle Mode')}
</span>
</div>
)
ToggleModeButton.propTypes = {
onClick: PropTypes.func.isRequired,
editorType: PropTypes.string.Required
editorType: PropTypes.string.isRequired
}
export default CSSModules(ToggleModeButton, styles)

View File

@@ -26,6 +26,13 @@
&:hover .tooltip
opacity 1
.control-toggleModeButton
-webkit-user-drag none
user-select none
> div img
-webkit-user-drag none
user-select none
.tooltip
tooltip()
position absolute
@@ -62,16 +69,16 @@ body[data-theme="solarized-dark"]
background-color #1EC38B
box-shadow 2px 0px 7px #222222
body[data-theme="monokai"]
.control-toggleModeButton
background-color #373831
.active
background-color #f92672
box-shadow 2px 0px 7px #222222
apply-theme(theme)
body[data-theme={theme}]
.control-toggleModeButton
background-color get-theme-var(theme, 'borderColor')
.active
background-color get-theme-var(theme, 'active-color')
box-shadow 2px 0px 7px #222222
body[data-theme="dracula"]
.control-toggleModeButton
background-color #44475a
.active
background-color #bd93f9
box-shadow 2px 0px 7px #222222
for theme in 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -4,14 +4,12 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
import i18n from 'browser/lib/i18n'
const TrashButton = ({
onClick
}) => (
<button styleName='control-trashButton'
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Trash')}</span>
const TrashButton = ({ onClick }) => (
<button styleName='control-trashButton' onClick={e => onClick(e)}>
<img src='../resources/icon/icon-trash.svg' />
<span lang={i18n.locale} styleName='tooltip'>
{i18n.__('Trash')}
</span>
</button>
)

Some files were not shown because too many files have changed in this diff Show More