Showing preview only (4,606K chars total). Download the full file or copy to clipboard to get everything.
Repository: BoostIO/BoostNote-Legacy
Branch: master
Commit: 789926bc7606
Files: 367
Total size: 4.3 MB
Directory structure:
gitextract_1f_tfmxn/
├── .babelrc
├── .boostnoterc.sample
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .prettierrc
├── .snapcraft/
│ └── travis_snapcraft.cfg
├── .travis.yml
├── FAQ.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── PULL_REQUEST_TEMPLATE.md
├── __mocks__/
│ └── electron.js
├── appdmg.json
├── browser/
│ ├── components/
│ │ ├── CodeEditor.js
│ │ ├── CodeEditor.styl
│ │ ├── ColorPicker.js
│ │ ├── ColorPicker.styl
│ │ ├── MarkdownEditor.js
│ │ ├── MarkdownEditor.styl
│ │ ├── MarkdownPreview.js
│ │ ├── MarkdownSplitEditor.js
│ │ ├── MarkdownSplitEditor.styl
│ │ ├── ModalEscButton.js
│ │ ├── ModalEscButton.styl
│ │ ├── NavToggleButton.js
│ │ ├── NavToggleButton.styl
│ │ ├── NoteItem.js
│ │ ├── NoteItem.styl
│ │ ├── NoteItemSimple.js
│ │ ├── NoteItemSimple.styl
│ │ ├── RealtimeNotification.js
│ │ ├── RealtimeNotification.styl
│ │ ├── SideNavFilter.js
│ │ ├── SideNavFilter.styl
│ │ ├── SnippetTab.js
│ │ ├── SnippetTab.styl
│ │ ├── StorageItem.js
│ │ ├── StorageItem.styl
│ │ ├── StorageList.js
│ │ ├── StorageList.styl
│ │ ├── TagListItem.js
│ │ ├── TagListItem.styl
│ │ ├── TodoListPercentage.js
│ │ ├── TodoListPercentage.styl
│ │ ├── TodoProcess.js
│ │ ├── TodoProcess.styl
│ │ ├── markdown.styl
│ │ └── render/
│ │ └── MermaidRender.js
│ ├── lib/
│ │ ├── CMLanguageList.js
│ │ ├── CSSModules.js
│ │ ├── Languages.js
│ │ ├── Mutable.js
│ │ ├── RcParser.js
│ │ ├── SnippetManager.js
│ │ ├── TextEditorInterface.js
│ │ ├── confirmDeleteNote.js
│ │ ├── consts.js
│ │ ├── context.js
│ │ ├── contextMenuBuilder.js
│ │ ├── convertModeName.js
│ │ ├── customMeta.js
│ │ ├── date-formatter.js
│ │ ├── findNoteTitle.js
│ │ ├── findStorage.js
│ │ ├── getTodoStatus.js
│ │ ├── htmlTextHelper.js
│ │ ├── i18n.js
│ │ ├── keygen.js
│ │ ├── markdown-it-deflist.js
│ │ ├── markdown-it-fence.js
│ │ ├── markdown-it-frontmatter.js
│ │ ├── markdown-it-sanitize-html.js
│ │ ├── markdown-toc-generator.js
│ │ ├── markdown.js
│ │ ├── markdown2.js
│ │ ├── markdownTextHelper.js
│ │ ├── newNote.js
│ │ ├── normalizeEditorFontFamily.js
│ │ ├── search.js
│ │ ├── slugify.js
│ │ ├── spellcheck.js
│ │ ├── turndown.js
│ │ ├── ui-themes.js
│ │ ├── utils.js
│ │ └── wakatime-plugin.js
│ ├── main/
│ │ ├── Detail/
│ │ │ ├── Detail.styl
│ │ │ ├── DetailVars.styl
│ │ │ ├── FolderSelect.js
│ │ │ ├── FolderSelect.styl
│ │ │ ├── FromUrlButton.js
│ │ │ ├── FromUrlButton.styl
│ │ │ ├── FullscreenButton.js
│ │ │ ├── FullscreenButton.styl
│ │ │ ├── InfoButton.js
│ │ │ ├── InfoButton.styl
│ │ │ ├── InfoPanel.js
│ │ │ ├── InfoPanel.styl
│ │ │ ├── InfoPanelTrashed.js
│ │ │ ├── MarkdownNoteDetail.js
│ │ │ ├── MarkdownNoteDetail.styl
│ │ │ ├── NoteDetailInfo.styl
│ │ │ ├── PermanentDeleteButton.js
│ │ │ ├── RestoreButton.js
│ │ │ ├── RestoreButton.styl
│ │ │ ├── SnippetNoteDetail.js
│ │ │ ├── SnippetNoteDetail.styl
│ │ │ ├── StarButton.js
│ │ │ ├── StarButton.styl
│ │ │ ├── TagSelect.js
│ │ │ ├── TagSelect.styl
│ │ │ ├── ToggleDirectionButton.js
│ │ │ ├── ToggleDirectionButton.styl
│ │ │ ├── ToggleModeButton.js
│ │ │ ├── ToggleModeButton.styl
│ │ │ ├── TrashButton.js
│ │ │ ├── TrashButton.styl
│ │ │ └── index.js
│ │ ├── DevTools/
│ │ │ ├── index.dev.js
│ │ │ ├── index.js
│ │ │ └── index.prod.js
│ │ ├── Main.js
│ │ ├── Main.styl
│ │ ├── NewNoteButton/
│ │ │ ├── NewNoteButton.styl
│ │ │ └── index.js
│ │ ├── NoteList/
│ │ │ ├── NoteList.styl
│ │ │ └── index.js
│ │ ├── SideNav/
│ │ │ ├── ListButton.js
│ │ │ ├── PreferenceButton.js
│ │ │ ├── PreferenceButton.styl
│ │ │ ├── SearchButton.js
│ │ │ ├── SearchButton.styl
│ │ │ ├── SideNav.styl
│ │ │ ├── StorageItem.js
│ │ │ ├── StorageItem.styl
│ │ │ ├── SwitchButton.styl
│ │ │ ├── TagButton.js
│ │ │ └── index.js
│ │ ├── StatusBar/
│ │ │ ├── StatusBar.styl
│ │ │ └── index.js
│ │ ├── TopBar/
│ │ │ ├── TopBar.styl
│ │ │ └── index.js
│ │ ├── global.styl
│ │ ├── index.js
│ │ ├── lib/
│ │ │ ├── AwsMobileAnalyticsConfig.js
│ │ │ ├── Commander.js
│ │ │ ├── ConfigManager.js
│ │ │ ├── ThemeManager.js
│ │ │ ├── ZoomManager.js
│ │ │ ├── dataApi/
│ │ │ │ ├── addStorage.js
│ │ │ │ ├── attachmentManagement.js
│ │ │ │ ├── copyFile.js
│ │ │ │ ├── createFolder.js
│ │ │ │ ├── createNote.js
│ │ │ │ ├── createNoteFromUrl.js
│ │ │ │ ├── createSnippet.js
│ │ │ │ ├── deleteFolder.js
│ │ │ │ ├── deleteNote.js
│ │ │ │ ├── deleteSnippet.js
│ │ │ │ ├── exportFolder.js
│ │ │ │ ├── exportNote.js
│ │ │ │ ├── exportNoteAs.js
│ │ │ │ ├── exportStorage.js
│ │ │ │ ├── exportTag.js
│ │ │ │ ├── fetchSnippet.js
│ │ │ │ ├── formatHTML.js
│ │ │ │ ├── formatMarkdown.js
│ │ │ │ ├── formatPDF.js
│ │ │ │ ├── getContentFormatter.js
│ │ │ │ ├── getFilename.js
│ │ │ │ ├── index.js
│ │ │ │ ├── init.js
│ │ │ │ ├── migrateFromV5Storage.js
│ │ │ │ ├── migrateFromV6Storage.js
│ │ │ │ ├── moveNote.js
│ │ │ │ ├── removeStorage.js
│ │ │ │ ├── renameStorage.js
│ │ │ │ ├── reorderFolder.js
│ │ │ │ ├── resolveStorageData.js
│ │ │ │ ├── resolveStorageNotes.js
│ │ │ │ ├── toggleStorage.js
│ │ │ │ ├── updateFolder.js
│ │ │ │ ├── updateNote.js
│ │ │ │ └── updateSnippet.js
│ │ │ ├── eventEmitter.js
│ │ │ ├── ipcClient.js
│ │ │ ├── modal.js
│ │ │ ├── notify.js
│ │ │ ├── shortcut.js
│ │ │ └── shortcutManager.js
│ │ ├── modals/
│ │ │ ├── CreateFolderModal.js
│ │ │ ├── CreateFolderModal.styl
│ │ │ ├── CreateMarkdownFromURLModal.js
│ │ │ ├── CreateMarkdownFromURLModal.styl
│ │ │ ├── NewNoteModal.js
│ │ │ ├── NewNoteModal.styl
│ │ │ ├── PreferencesModal/
│ │ │ │ ├── Blog.js
│ │ │ │ ├── ConfigTab.styl
│ │ │ │ ├── Crowdfunding.js
│ │ │ │ ├── Crowdfunding.styl
│ │ │ │ ├── ExportTab.js
│ │ │ │ ├── FolderItem.js
│ │ │ │ ├── FolderItem.styl
│ │ │ │ ├── FolderList.js
│ │ │ │ ├── FolderList.styl
│ │ │ │ ├── HotkeyTab.js
│ │ │ │ ├── InfoTab.js
│ │ │ │ ├── InfoTab.styl
│ │ │ │ ├── PluginsTab.js
│ │ │ │ ├── PreferencesModal.styl
│ │ │ │ ├── SnippetEditor.js
│ │ │ │ ├── SnippetList.js
│ │ │ │ ├── SnippetTab.js
│ │ │ │ ├── SnippetTab.styl
│ │ │ │ ├── StorageItem.js
│ │ │ │ ├── StorageItem.styl
│ │ │ │ ├── StoragesTab.js
│ │ │ │ ├── StoragesTab.styl
│ │ │ │ ├── Tab.styl
│ │ │ │ ├── UiTab.js
│ │ │ │ └── index.js
│ │ │ ├── RenameFolderModal.js
│ │ │ ├── RenameModal.styl
│ │ │ └── RenameTagModal.js
│ │ └── store.js
│ └── styles/
│ ├── Detail/
│ │ └── TagSelect.styl
│ ├── index.styl
│ ├── mixins/
│ │ ├── alert.styl
│ │ ├── btn.styl
│ │ ├── circle.styl
│ │ ├── fullsize.styl
│ │ ├── input.styl
│ │ ├── marked.styl
│ │ ├── tooltip.styl
│ │ └── util.styl
│ ├── shared/
│ │ └── btn.styl
│ ├── theme/
│ │ └── dark.styl
│ └── vars.styl
├── contributing.md
├── dev-scripts/
│ └── dev.js
├── dictionaries/
│ ├── de_DE/
│ │ ├── LICENSE
│ │ ├── de_DE.aff
│ │ └── de_DE.dic
│ ├── en_GB/
│ │ ├── LICENSE
│ │ ├── en_GB.aff
│ │ └── en_GB.dic
│ └── fr_FR/
│ ├── fr_FR.aff
│ └── fr_FR.dic
├── docs/
│ ├── build.md
│ ├── code_style.md
│ ├── de/
│ │ ├── build.md
│ │ └── debug.md
│ ├── debug.md
│ ├── fr/
│ │ ├── build.md
│ │ └── debug.md
│ ├── jp/
│ │ ├── build.md
│ │ └── debug.md
│ ├── ko/
│ │ ├── build.md
│ │ └── debug.md
│ ├── pt_BR/
│ │ ├── build.md
│ │ └── debug.md
│ ├── ru/
│ │ ├── build.md
│ │ └── debug.md
│ ├── zh_CN/
│ │ ├── build.md
│ │ └── debug.md
│ └── zh_TW/
│ └── build.md
├── extra_scripts/
│ ├── boost/
│ │ └── boostNewLineIndentContinueMarkdownList.js
│ └── codemirror/
│ ├── addon/
│ │ ├── edit/
│ │ │ └── closebrackets.js
│ │ └── hyperlink/
│ │ └── hyperlink.js
│ ├── mode/
│ │ ├── bfm/
│ │ │ ├── bfm.css
│ │ │ └── bfm.js
│ │ └── gfm/
│ │ └── gfm.js
│ └── theme/
│ └── nord.css
├── gruntfile.js
├── index.js
├── lib/
│ ├── ipcServer.js
│ ├── main-app.js
│ ├── main-menu.js
│ ├── main-window.js
│ ├── main.development.html
│ ├── main.production.html
│ └── touchbar-menu.js
├── locales/
│ ├── cs.json
│ ├── da.json
│ ├── de.json
│ ├── en.json
│ ├── es-ES.json
│ ├── fa.json
│ ├── fr.json
│ ├── hu.json
│ ├── it.json
│ ├── ja.json
│ ├── ko.json
│ ├── no.json
│ ├── pl.json
│ ├── pt-BR.json
│ ├── pt-PT.json
│ ├── ru.json
│ ├── sq.json
│ ├── th.json
│ ├── tr.json
│ ├── zh-CN.json
│ └── zh-TW.json
├── package.json
├── prettier.config
├── readme.md
├── resources/
│ ├── app.icns
│ └── dmg.icns
├── snap/
│ ├── gui/
│ │ └── boostnote.desktop
│ └── snapcraft.yaml
├── tests/
│ ├── .gitignore
│ ├── components/
│ │ ├── TagListItem.snapshot.test.js
│ │ └── __snapshots__/
│ │ └── TagListItem.snapshot.test.js.snap
│ ├── dataApi/
│ │ ├── addStorage.js
│ │ ├── attachmentManagement.test.js
│ │ ├── copyFile.test.js
│ │ ├── createFolder.test.js
│ │ ├── createNote.test.js
│ │ ├── createNoteFromUrl.test.js
│ │ ├── createSnippet.test.js
│ │ ├── deleteFolder.test.js
│ │ ├── deleteNote-test.js
│ │ ├── deleteSnippet-test.js
│ │ ├── exportFolder-test.js
│ │ ├── exportStorage-test.js
│ │ ├── init.js
│ │ ├── migrateFromV6Storage-test.js
│ │ ├── moveNote-test.js
│ │ ├── removeStorage-test.js
│ │ ├── renameStorage-test.js
│ │ ├── reorderFolder-test.js
│ │ ├── toggleStorage-test.js
│ │ ├── updateFolder-test.js
│ │ ├── updateNote-test.js
│ │ └── updateSnippet-test.js
│ ├── date-formatter-test.js
│ ├── fixtures/
│ │ ├── TestDummy.js
│ │ └── markdowns.js
│ ├── helpers/
│ │ ├── setup-browser-env.js
│ │ └── setup-electron-mock.js
│ ├── jest.js
│ └── lib/
│ ├── __snapshots__/
│ │ └── markdown.test.js.snap
│ ├── boostnoterc/
│ │ ├── .boostnoterc.all
│ │ ├── .boostnoterc.invalid
│ │ └── .boostnoterc.valid
│ ├── contextMenuBuilder.test.js
│ ├── escapeHtmlCharacters.test.js
│ ├── find-storage.test.js
│ ├── find-title.test.js
│ ├── get-todo-status.test.js
│ ├── html-text-helper.test.js
│ ├── markdown-text-helper.test.js
│ ├── markdown-toc-generator.test.js
│ ├── markdown.test.js
│ ├── normalize-editor-font-family.test.js
│ ├── rc-parser.test.js
│ ├── search.test.js
│ ├── slugify.test.js
│ ├── snapshots/
│ │ └── markdown-test.js.md
│ ├── spellcheck.test.js
│ ├── themeManager.test.js
│ └── utils.test.js
├── webpack-production.config.js
├── webpack-skeleton.js
└── webpack.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": ["react", "es2015"],
"env": {
"development": {
"presets": ["react-hmre"]
},
"test": {
"presets": ["env" ,"react", "es2015"],
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "<rootDir>/webpack.config.js" } ]
]
}
}
}
================================================
FILE: .boostnoterc.sample
================================================
{
"amaEnabled": true,
"editor": {
"fontFamily": "Monaco, Consolas",
"fontSize": "14",
"indentSize": "2",
"indentType": "space",
"keyMap": "vim",
"switchPreview": "BLUR",
"theme": "monokai"
},
"hotkey": {
"toggleMain": "Cmd + Alt + L"
},
"isSideNavFolded": false,
"listStyle": "DEFAULT",
"listWidth": 174,
"navWidth": 200,
"preview": {
"codeBlockTheme": "dracula",
"fontFamily": "Lato",
"fontSize": "14",
"lineNumber": true
},
"sortBy": {
"default": "UPDATED_AT"
},
"sortTagsBy": "ALPHABETICAL",
"ui": {
"defaultNote": "ALWAYS_ASK",
"disableDirectWrite": false,
"theme": "default"
},
"zoom": 1
}
================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Space indentation
[*]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# The indent size used in the `package.json` file cannot be changed
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
[{*.yml,*.yaml,package.json}]
indent_style = space
indent_size = 2
================================================
FILE: .eslintignore
================================================
node_modules/
compiled/
dist/
extra_scripts/
================================================
FILE: .eslintrc
================================================
{
"extends": ["standard", "standard-jsx", "plugin:react/recommended", "prettier"],
"plugins": ["react", "prettier"],
"rules": {
"no-useless-escape": 0,
"prefer-const": ["warn", {
"destructuring": "all"
}],
"no-unused-vars": "warn",
"no-undef": "warn",
"no-lone-blocks": "warn",
"react/prop-types": 0,
"react/no-string-refs": 0,
"react/no-find-dom-node": "warn",
"react/no-render-return-value": "warn",
"react/no-deprecated": "warn",
"prettier/prettier": ["error"]
},
"globals": {
"FileReader": true,
"localStorage": true,
"fetch": true,
"Image": true,
"MutationObserver": true
},
"env": {
"jest": true
}
}
================================================
FILE: .github/FUNDING.yml
================================================
issuehunt: BoostIo/Boostnote
================================================
FILE: .gitignore
================================================
.DS_Store
.env
Desktop.ini
Thumbs.db
node_modules/*
!node_modules/boost
/dist
/compiled
/secret
*.log
.idea
.vscode
package-lock.json
config.json
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true,
"semi": false,
"jsxSingleQuote": true
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- 8
script:
- npm run lint && npm run test
- '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
sudo: required
services:
- docker
deploy:
'on':
branch: master
provider: script
script: if [ ${TRAVIS_NODE_VERSION} = "stable" ];then docker run -v $(pwd):$(pwd) -t snapcore/snapcraft sh -c "apt update -qq
&& cd $(pwd) && snapcraft && snapcraft push *.snap --release edge"; fi
skip_cleanup: true
================================================
FILE: FAQ.md
================================================
# Frequently Asked Questions
<details><summary>Allowing dangerous HTML tags</summary>
Sometimes it is useful to allow dangerous HTML tags to add interactivity to your notebook. One of the example is to use details/summary as a way to expand/collaps your todo-list.
* How to enable:
* Go to **Preferences** → **Interface** → **Sanitization** → **Allow dangerous html tags**
* Example note: Multiple todo-list
* Create new notes
* Paste the below code, and you'll see that you can expand/collaps the todo-list, and you can have multiple todo-list in your note.
```html
<details><summary>What I want to do</summary>
- [x] Create an awesome feature X
- [ ] Do my homework
</details>
```
</details>
## Other questions
You can ask [here][ISSUES]
[ISSUES]: https://github.com/BoostIO/Boostnote/issues
================================================
FILE: ISSUE_TEMPLATE.md
================================================
# Current behavior
<!--
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 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.
-->
# Steps to reproduce
<!--
Please be thorough, issues we can reproduce are easier to fix.
-->
1.
2.
3.
# Environment
- Boostnote version: <!-- 0.x.x -->
- OS version and name: <!-- Windows 10 / Ubuntu 18.04 / etc -->
<!--
Love Boostnote? Please consider supporting us on IssueHunt:
👉 https://issuehunt.io/repos/53266139
-->
================================================
FILE: LICENSE
================================================
GPL-3.0
Boostnote - an open source note-taking app made for programmers just like you.
Copyright (C) 2017 - 2019 BoostIO
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
================================================
FILE: PULL_REQUEST_TEMPLATE.md
================================================
<!--
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.
-->
<!--
Please make sure you fill in these checkboxes,
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)
- :white_circle: Breaking change (Change that can cause existing functionality to change)
- :white_circle: Improvement (Change that improves the code. Maybe performance or development improvement)
- :white_circle: Feature (Change that adds new functionality)
- :white_circle: Documentation change (Change that modifies documentation. Maybe typo fixes)
## Checklist:
- :white_circle: My code follows [the project code style](docs/code_style.md)
- :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: __mocks__/electron.js
================================================
module.exports = {
require: jest.genMockFunction(),
match: jest.genMockFunction(),
app: jest.genMockFunction(),
remote: jest.genMockFunction(),
dialog: jest.genMockFunction()
}
================================================
FILE: appdmg.json
================================================
{
"title": "Boostnote",
"icon": "resources/dmg.icns",
"background": "resources/boostnote-install.png",
"icon-size": 80,
"contents": [
{ "x": 448, "y": 344, "type": "link", "path": "/Applications" },
{ "x": 192, "y": 344, "type": "file", "path": "dist/Boostnote-darwin-x64/Boostnote.app" }
]
}
================================================
FILE: browser/components/CodeEditor.js
================================================
import PropTypes from 'prop-types'
import React from 'react'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import hljs from 'highlight.js'
import 'codemirror-mode-elixir'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
import convertModeName from 'browser/lib/convertModeName'
import { options, TableEditor, Alignment } from '@susisu/mte-kernel'
import TextEditorInterface from 'browser/lib/TextEditorInterface'
import eventEmitter from 'browser/main/lib/eventEmitter'
import iconv from 'iconv-lite'
import { isMarkdownTitleURL } from 'browser/lib/utils'
import styles from '../components/CodeEditor.styl'
const { ipcRenderer, remote, clipboard } = require('electron')
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
const spellcheck = require('browser/lib/spellcheck')
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
.buildEditorContextMenu
import { createTurndownService } from '../lib/turndown'
import { languageMaps } from '../lib/CMLanguageList'
import snippetManager from '../lib/SnippetManager'
import { findStorage } from 'browser/lib/findStorage'
import { sendWakatimeHeartBeat } from 'browser/lib/wakatime-plugin'
import {
generateInEditor,
tocExistsInEditor
} from 'browser/lib/markdown-toc-generator'
import markdownlint from 'markdownlint'
import Jsonlint from 'jsonlint-mod'
import { DEFAULT_CONFIG } from '../main/lib/ConfigManager'
import prettier from 'prettier'
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
const buildCMRulers = (rulers, enableRulers) =>
enableRulers
? rulers.map(ruler => ({
column: ruler
}))
: []
function translateHotkey(hotkey) {
return hotkey
.replace(/\s*\+\s*/g, '-')
.replace(/Command/g, 'Cmd')
.replace(/Control/g, 'Ctrl')
}
export default class CodeEditor extends React.Component {
constructor(props) {
super(props)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
leading: false,
trailing: true
})
this.changeHandler = (editor, changeObject) =>
this.handleChange(editor, changeObject)
this.highlightHandler = (editor, changeObject) =>
this.handleHighlight(editor, changeObject)
this.focusHandler = () => {
ipcRenderer.send('editor:focused', true)
}
this.debouncedDeletionOfAttachments = _.debounce(
attachmentManagement.deleteAttachmentsNotPresentInNote,
30000
)
this.blurHandler = (editor, e) => {
ipcRenderer.send('editor:focused', false)
if (e == null) return null
let el = e.relatedTarget
while (el != null) {
if (el === this.refs.root) {
return
}
el = el.parentNode
}
this.props.onBlur != null && this.props.onBlur(e)
const { storageKey, noteKey } = this.props
if (this.props.deleteUnusedAttachments === true) {
this.debouncedDeletionOfAttachments(
this.editor.getValue(),
storageKey,
noteKey
)
}
}
this.pasteHandler = (editor, e) => {
e.preventDefault()
this.handlePaste(editor, false)
}
this.loadStyleHandler = e => {
this.editor.refresh()
}
this.searchHandler = (e, msg) => this.handleSearch(msg)
this.searchState = null
this.scrollToLineHandeler = this.scrollToLine.bind(this)
this.getCodeEditorLintConfig = this.getCodeEditorLintConfig.bind(this)
this.validatorOfMarkdown = this.validatorOfMarkdown.bind(this)
this.formatTable = () => this.handleFormatTable()
if (props.switchPreview !== 'RIGHTCLICK') {
this.contextMenuHandler = function(editor, event) {
const menu = buildEditorContextMenu(editor, event)
if (menu != null) {
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
}
}
}
this.editorActivityHandler = () => this.handleEditorActivity()
this.turndownService = createTurndownService()
// wakatime
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (storage)
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite: false,
hasFileChanges: false,
isFileChange: true
})
}
handleSearch(msg) {
const cm = this.editor
const component = this
if (component.searchState) cm.removeOverlay(component.searchState)
if (msg.length < 1) return
cm.operation(function() {
component.searchState = makeOverlay(msg, 'searching')
cm.addOverlay(component.searchState)
function makeOverlay(query, style) {
query = new RegExp(
query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'),
'gi'
)
return {
token: function(stream) {
query.lastIndex = stream.pos
var match = query.exec(stream.string)
if (match && match.index === stream.pos) {
stream.pos += match[0].length || 1
return style
} else if (match) {
stream.pos = match.index
} else {
stream.skipToEnd()
}
}
}
}
})
}
handleFormatTable() {
this.tableEditor.formatAll(
options({
textWidthOptions: {}
})
)
}
handleEditorActivity() {
if (this.props.onCursorActivity) {
this.props.onCursorActivity(this.editor)
}
if (!this.textEditorInterface.transaction) {
this.updateTableEditorState()
}
}
updateDefaultKeyMap() {
const { hotkey } = this.props
const self = this
const expandSnippet = snippetManager.expandSnippet
this.defaultKeyMap = CodeMirror.normalizeKeyMap({
Tab: function(cm) {
const cursor = cm.getCursor()
const line = cm.getLine(cursor.line)
const cursorPosition = cursor.ch
const charBeforeCursor = line.substr(cursorPosition - 1, 1)
if (cm.somethingSelected()) cm.indentSelection('add')
else {
const tabs = cm.getOption('indentWithTabs')
if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) {
cm.execCommand('goLineStart')
if (tabs) {
cm.execCommand('insertTab')
} else {
cm.execCommand('insertSoftTab')
}
cm.execCommand('goLineEnd')
} else if (
!charBeforeCursor.match(/\t|\s|\r|\n|\$/) &&
cursor.ch > 1
) {
// text expansion on tab key if the char before is alphabet
const wordBeforeCursor = self.getWordBeforeCursor(
line,
cursor.line,
cursor.ch
)
if (expandSnippet(wordBeforeCursor, cursor, cm) === false) {
if (tabs) {
cm.execCommand('insertTab')
} else {
cm.execCommand('insertSoftTab')
}
}
} else {
if (tabs) {
cm.execCommand('insertTab')
} else {
cm.execCommand('insertSoftTab')
}
}
}
},
'Cmd-Left': function(cm) {
cm.execCommand('goLineLeft')
},
'Cmd-T': function(cm) {
// Do nothing
},
[translateHotkey(hotkey.insertDate)]: function(cm) {
const dateNow = new Date()
if (self.props.dateFormatISO8601) {
cm.replaceSelection(dateNow.toISOString().split('T')[0])
} else {
cm.replaceSelection(dateNow.toLocaleDateString())
}
},
[translateHotkey(hotkey.insertDateTime)]: function(cm) {
const dateNow = new Date()
if (self.props.dateFormatISO8601) {
cm.replaceSelection(dateNow.toISOString())
} else {
cm.replaceSelection(dateNow.toLocaleString())
}
},
Enter: 'boostNewLineAndIndentContinueMarkdownList',
'Ctrl-C': cm => {
if (cm.getOption('keyMap').substr(0, 3) === 'vim') {
document.execCommand('copy')
}
return CodeMirror.Pass
},
[translateHotkey(hotkey.prettifyMarkdown)]: cm => {
// Default / User configured prettier options
const currentConfig = JSON.parse(self.props.prettierConfig)
// Parser type will always need to be markdown so we override the option before use
currentConfig.parser = 'markdown'
// Get current cursor position
const cursorPos = cm.getCursor()
currentConfig.cursorOffset = cm.doc.indexFromPos(cursorPos)
// Prettify contents of editor
const formattedTextDetails = prettier.formatWithCursor(
cm.doc.getValue(),
currentConfig
)
const formattedText = formattedTextDetails.formatted
const formattedCursorPos = formattedTextDetails.cursorOffset
cm.doc.setValue(formattedText)
// Reset Cursor position to be at the same markdown as was before prettifying
const newCursorPos = cm.doc.posFromIndex(formattedCursorPos)
cm.doc.setCursor(newCursorPos)
},
[translateHotkey(hotkey.sortLines)]: cm => {
const selection = cm.doc.getSelection()
const appendLineBreak = /\n$/.test(selection)
const sorted = _.split(selection.trim(), '\n').sort()
const sortedString =
_.join(sorted, '\n') + (appendLineBreak ? '\n' : '')
cm.doc.replaceSelection(sortedString)
},
[translateHotkey(hotkey.pasteSmartly)]: cm => {
this.handlePaste(cm, true)
}
})
}
updateTableEditorState() {
const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions)
if (active) {
if (this.extraKeysMode !== 'editor') {
this.extraKeysMode = 'editor'
this.editor.setOption('extraKeys', this.editorKeyMap)
}
} else {
if (this.extraKeysMode !== 'default') {
this.extraKeysMode = 'default'
this.editor.setOption('extraKeys', this.defaultKeyMap)
this.tableEditor.resetSmartCursor()
}
}
}
componentDidMount() {
const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props
eventEmitter.on('line:jump', this.scrollToLineHandeler)
snippetManager.init()
this.updateDefaultKeyMap()
this.value = this.props.value
this.editor = CodeMirror(this.refs.root, {
rulers: buildCMRulers(rulers, enableRulers),
value: this.props.value,
linesHighlighted: this.props.linesHighlighted,
lineNumbers: this.props.displayLineNumbers,
lineWrapping: this.props.lineWrapping,
theme: this.props.theme,
indentUnit: this.props.indentSize,
tabSize: this.props.indentSize,
indentWithTabs: this.props.indentType !== 'space',
keyMap: this.props.keyMap,
scrollPastEnd: this.props.scrollPastEnd,
inputStyle: 'textarea',
dragDrop: false,
direction: RTL ? 'rtl' : 'ltr',
rtlMoveVisually: RTL,
foldGutter: true,
lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false,
gutters: [
'CodeMirror-linenumbers',
'CodeMirror-foldgutter',
'CodeMirror-lint-markers'
],
autoCloseBrackets: {
codeBlock: {
pairs: this.props.codeBlockMatchingPairs,
closeBefore: this.props.codeBlockMatchingCloseBefore,
triples: this.props.codeBlockMatchingTriples,
explode: this.props.codeBlockExplodingPairs
},
markdown: {
pairs: this.props.matchingPairs,
closeBefore: this.props.matchingCloseBefore,
triples: this.props.matchingTriples,
explode: this.props.explodingPairs
}
},
extraKeys: this.defaultKeyMap,
prettierConfig: this.props.prettierConfig
})
document.querySelector(
'.CodeMirror-lint-markers'
).style.display = enableMarkdownLint ? 'inline-block' : 'none'
if (!this.props.mode && this.props.value && this.props.autoDetect) {
this.autoDetectLanguage(this.props.value)
} else {
this.setMode(this.props.mode)
}
this.editor.on('focus', this.focusHandler)
this.editor.on('blur', this.blurHandler)
this.editor.on('change', this.changeHandler)
this.editor.on('gutterClick', this.highlightHandler)
this.editor.on('paste', this.pasteHandler)
if (this.props.switchPreview !== 'RIGHTCLICK') {
this.editor.on('contextmenu', this.contextMenuHandler)
}
eventEmitter.on('top:search', this.searchHandler)
eventEmitter.emit('code:init')
this.editor.on('scroll', this.scrollHandler)
this.editor.on('cursorActivity', this.editorActivityHandler)
const editorTheme = document.getElementById('editorTheme')
editorTheme.addEventListener('load', this.loadStyleHandler)
CodeMirror.Vim.defineEx('quit', 'q', this.quitEditor)
CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor)
CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor)
CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor)
CodeMirror.Vim.map('ZZ', ':q', 'normal')
this.textEditorInterface = new TextEditorInterface(this.editor)
this.tableEditor = new TableEditor(this.textEditorInterface)
if (this.props.spellCheck) {
this.editor.addPanel(this.createSpellCheckPanel(), { position: 'bottom' })
}
eventEmitter.on('code:format-table', this.formatTable)
this.tableEditorOptions = options({
smartCursor: true
})
this.editorKeyMap = CodeMirror.normalizeKeyMap({
Tab: () => {
this.tableEditor.nextCell(this.tableEditorOptions)
},
'Shift-Tab': () => {
this.tableEditor.previousCell(this.tableEditorOptions)
},
Enter: () => {
this.tableEditor.nextRow(this.tableEditorOptions)
},
'Ctrl-Enter': () => {
this.tableEditor.escape(this.tableEditorOptions)
},
'Cmd-Enter': () => {
this.tableEditor.escape(this.tableEditorOptions)
},
'Shift-Ctrl-Left': () => {
this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions)
},
'Shift-Cmd-Left': () => {
this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions)
},
'Shift-Ctrl-Right': () => {
this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions)
},
'Shift-Cmd-Right': () => {
this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions)
},
'Shift-Ctrl-Up': () => {
this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions)
},
'Shift-Cmd-Up': () => {
this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions)
},
'Shift-Ctrl-Down': () => {
this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions)
},
'Shift-Cmd-Down': () => {
this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions)
},
'Ctrl-Left': () => {
this.tableEditor.moveFocus(0, -1, this.tableEditorOptions)
},
'Cmd-Left': () => {
this.tableEditor.moveFocus(0, -1, this.tableEditorOptions)
},
'Ctrl-Right': () => {
this.tableEditor.moveFocus(0, 1, this.tableEditorOptions)
},
'Cmd-Right': () => {
this.tableEditor.moveFocus(0, 1, this.tableEditorOptions)
},
'Ctrl-Up': () => {
this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions)
},
'Cmd-Up': () => {
this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions)
},
'Ctrl-Down': () => {
this.tableEditor.moveFocus(1, 0, this.tableEditorOptions)
},
'Cmd-Down': () => {
this.tableEditor.moveFocus(1, 0, this.tableEditorOptions)
},
'Ctrl-K Ctrl-I': () => {
this.tableEditor.insertRow(this.tableEditorOptions)
},
'Cmd-K Cmd-I': () => {
this.tableEditor.insertRow(this.tableEditorOptions)
},
'Ctrl-L Ctrl-I': () => {
this.tableEditor.deleteRow(this.tableEditorOptions)
},
'Cmd-L Cmd-I': () => {
this.tableEditor.deleteRow(this.tableEditorOptions)
},
'Ctrl-K Ctrl-J': () => {
this.tableEditor.insertColumn(this.tableEditorOptions)
},
'Cmd-K Cmd-J': () => {
this.tableEditor.insertColumn(this.tableEditorOptions)
},
'Ctrl-L Ctrl-J': () => {
this.tableEditor.deleteColumn(this.tableEditorOptions)
},
'Cmd-L Cmd-J': () => {
this.tableEditor.deleteColumn(this.tableEditorOptions)
},
'Alt-Shift-Ctrl-Left': () => {
this.tableEditor.moveColumn(-1, this.tableEditorOptions)
},
'Alt-Shift-Cmd-Left': () => {
this.tableEditor.moveColumn(-1, this.tableEditorOptions)
},
'Alt-Shift-Ctrl-Right': () => {
this.tableEditor.moveColumn(1, this.tableEditorOptions)
},
'Alt-Shift-Cmd-Right': () => {
this.tableEditor.moveColumn(1, this.tableEditorOptions)
},
'Alt-Shift-Ctrl-Up': () => {
this.tableEditor.moveRow(-1, this.tableEditorOptions)
},
'Alt-Shift-Cmd-Up': () => {
this.tableEditor.moveRow(-1, this.tableEditorOptions)
},
'Alt-Shift-Ctrl-Down': () => {
this.tableEditor.moveRow(1, this.tableEditorOptions)
},
'Alt-Shift-Cmd-Down': () => {
this.tableEditor.moveRow(1, this.tableEditorOptions)
}
})
if (this.props.enableTableEditor) {
this.editor.on('changes', this.editorActivityHandler)
}
this.setState({
clientWidth: this.refs.root.clientWidth
})
this.initialHighlighting()
}
getWordBeforeCursor(line, lineNumber, cursorPosition) {
let wordBeforeCursor = ''
const originCursorPosition = cursorPosition
const emptyChars = /\t|\s|\r|\n|\$/
// to prevent the word is long that will crash the whole app
// the safeStop is there to stop user to expand words that longer than 20 chars
const safeStop = 20
while (cursorPosition > 0) {
const currentChar = line.substr(cursorPosition - 1, 1)
// if char is not an empty char
if (!emptyChars.test(currentChar)) {
wordBeforeCursor = currentChar + wordBeforeCursor
} else if (wordBeforeCursor.length >= safeStop) {
throw new Error('Stopped after 20 loops for safety reason !')
} else {
break
}
cursorPosition--
}
return {
text: wordBeforeCursor,
range: {
from: {
line: lineNumber,
ch: originCursorPosition
},
to: {
line: lineNumber,
ch: cursorPosition
}
}
}
}
quitEditor() {
document.querySelector('textarea').blur()
}
componentWillUnmount() {
this.editor.off('focus', this.focusHandler)
this.editor.off('blur', this.blurHandler)
this.editor.off('change', this.changeHandler)
this.editor.off('paste', this.pasteHandler)
eventEmitter.off('top:search', this.searchHandler)
this.editor.off('scroll', this.scrollHandler)
this.editor.off('cursorActivity', this.editorActivityHandler)
this.editor.off('contextmenu', this.contextMenuHandler)
const editorTheme = document.getElementById('editorTheme')
editorTheme.removeEventListener('load', this.loadStyleHandler)
spellcheck.setLanguage(null, spellcheck.SPELLCHECK_DISABLED)
eventEmitter.off('code:format-table', this.formatTable)
if (this.props.enableTableEditor) {
this.editor.off('changes', this.editorActivityHandler)
}
}
componentDidUpdate(prevProps, prevState) {
let needRefresh = false
const {
rulers,
enableRulers,
enableMarkdownLint,
customMarkdownLintConfig
} = this.props
if (prevProps.mode !== this.props.mode) {
this.setMode(this.props.mode)
}
if (prevProps.theme !== this.props.theme) {
this.editor.setOption('theme', this.props.theme)
// editor should be refreshed after css loaded
}
if (prevProps.fontSize !== this.props.fontSize) {
needRefresh = true
}
if (prevProps.fontFamily !== this.props.fontFamily) {
needRefresh = true
}
if (prevProps.keyMap !== this.props.keyMap) {
needRefresh = true
}
if (prevProps.RTL !== this.props.RTL) {
this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr')
this.editor.setOption('rtlMoveVisually', this.props.RTL)
}
if (
prevProps.enableMarkdownLint !== enableMarkdownLint ||
prevProps.customMarkdownLintConfig !== customMarkdownLintConfig
) {
if (!enableMarkdownLint) {
this.editor.setOption('lint', { default: false })
document.querySelector('.CodeMirror-lint-markers').style.display =
'none'
} else {
this.editor.setOption('lint', this.getCodeEditorLintConfig())
document.querySelector('.CodeMirror-lint-markers').style.display =
'inline-block'
}
needRefresh = true
}
if (
prevProps.enableRulers !== enableRulers ||
prevProps.rulers !== rulers
) {
this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers))
}
if (prevProps.indentSize !== this.props.indentSize) {
this.editor.setOption('indentUnit', this.props.indentSize)
this.editor.setOption('tabSize', this.props.indentSize)
}
if (prevProps.indentType !== this.props.indentType) {
this.editor.setOption('indentWithTabs', this.props.indentType !== 'space')
}
if (prevProps.displayLineNumbers !== this.props.displayLineNumbers) {
this.editor.setOption('lineNumbers', this.props.displayLineNumbers)
}
if (prevProps.lineWrapping !== this.props.lineWrapping) {
this.editor.setOption('lineWrapping', this.props.lineWrapping)
}
if (prevProps.scrollPastEnd !== this.props.scrollPastEnd) {
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
}
if (
prevProps.matchingPairs !== this.props.matchingPairs ||
prevProps.matchingCloseBefore !== this.props.matchingCloseBefore ||
prevProps.matchingTriples !== this.props.matchingTriples ||
prevProps.explodingPairs !== this.props.explodingPairs ||
prevProps.codeBlockMatchingPairs !== this.props.codeBlockMatchingPairs ||
prevProps.codeBlockMatchingCloseBefore !==
this.props.codeBlockMatchingCloseBefore ||
prevProps.codeBlockMatchingTriples !==
this.props.codeBlockMatchingTriples ||
prevProps.codeBlockExplodingPairs !== this.props.codeBlockExplodingPairs
) {
const autoCloseBrackets = {
codeBlock: {
pairs: this.props.codeBlockMatchingPairs,
closeBefore: this.props.codeBlockMatchingCloseBefore,
triples: this.props.codeBlockMatchingTriples,
explode: this.props.codeBlockExplodingPairs
},
markdown: {
pairs: this.props.matchingPairs,
closeBefore: this.props.matchingCloseBefore,
triples: this.props.matchingTriples,
explode: this.props.explodingPairs
}
}
this.editor.setOption('autoCloseBrackets', autoCloseBrackets)
}
if (prevProps.enableTableEditor !== this.props.enableTableEditor) {
if (this.props.enableTableEditor) {
this.editor.on('cursorActivity', this.editorActivityHandler)
this.editor.on('changes', this.editorActivityHandler)
} else {
this.editor.off('cursorActivity', this.editorActivityHandler)
this.editor.off('changes', this.editorActivityHandler)
}
this.extraKeysMode = 'default'
this.editor.setOption('extraKeys', this.defaultKeyMap)
}
if (prevProps.hotkey !== this.props.hotkey) {
this.updateDefaultKeyMap()
if (this.extraKeysMode === 'default') {
this.editor.setOption('extraKeys', this.defaultKeyMap)
}
}
if (this.state.clientWidth !== this.refs.root.clientWidth) {
this.setState({
clientWidth: this.refs.root.clientWidth
})
needRefresh = true
}
if (prevProps.spellCheck !== this.props.spellCheck) {
if (this.props.spellCheck === false) {
spellcheck.setLanguage(this.editor, spellcheck.SPELLCHECK_DISABLED)
const elem = document.getElementById('editor-bottom-panel')
elem.parentNode.removeChild(elem)
} else {
this.editor.addPanel(this.createSpellCheckPanel(), {
position: 'bottom'
})
}
}
if (
prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments
) {
this.editor.setOption(
'deleteUnusedAttachments',
this.props.deleteUnusedAttachments
)
}
if (needRefresh) {
this.editor.refresh()
}
}
getCodeEditorLintConfig() {
const { mode } = this.props
const checkMarkdownNoteIsOpen = mode === 'Boost Flavored Markdown'
return checkMarkdownNoteIsOpen
? {
getAnnotations: this.validatorOfMarkdown,
async: true
}
: false
}
validatorOfMarkdown(text, updateLinting) {
const { customMarkdownLintConfig } = this.props
let lintConfigJson
try {
Jsonlint.parse(customMarkdownLintConfig)
lintConfigJson = JSON.parse(customMarkdownLintConfig)
} catch (err) {
eventEmitter.emit('APP_SETTING_ERROR')
return
}
const lintOptions = {
strings: {
content: text
},
config: lintConfigJson
}
return markdownlint(lintOptions, (err, result) => {
if (!err) {
const foundIssues = []
const splitText = text.split('\n')
result.content.map(item => {
let ruleNames = ''
item.ruleNames.map((ruleName, index) => {
ruleNames += ruleName
ruleNames += index === item.ruleNames.length - 1 ? ': ' : '/'
})
const lineNumber = item.lineNumber - 1
foundIssues.push({
from: CodeMirror.Pos(lineNumber, 0),
to: CodeMirror.Pos(lineNumber, splitText[lineNumber].length),
message: ruleNames + item.ruleDescription,
severity: 'warning'
})
})
updateLinting(foundIssues)
}
})
}
setMode(mode) {
let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text'))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
this.editor.setOption('mode', syntax.mime)
CodeMirror.autoLoadMode(this.editor, syntax.mode)
}
handleChange(editor, changeObject) {
this.debouncedDeletionOfAttachments.cancel()
spellcheck.handleChange(editor, changeObject)
// The current note contains an toc. We'll check for changes on headlines.
// origin is undefined when markdownTocGenerator replace the old tod
if (tocExistsInEditor(editor) && changeObject.origin !== undefined) {
let requireTocUpdate
// Check if one of the changed lines contains a headline
for (let line = 0; line < changeObject.text.length; line++) {
if (
this.linePossibleContainsHeadline(
editor.getLine(changeObject.from.line + line)
)
) {
requireTocUpdate = true
break
}
}
if (!requireTocUpdate) {
// Check if one of the removed lines contains a headline
for (let line = 0; line < changeObject.removed.length; line++) {
if (this.linePossibleContainsHeadline(changeObject.removed[line])) {
requireTocUpdate = true
break
}
}
}
if (requireTocUpdate) {
generateInEditor(editor)
}
}
this.updateHighlight(editor, changeObject)
this.value = editor.getValue()
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (this.props.onChange) {
this.props.onChange(editor)
}
const isWrite = !!this.props.onChange
const hasFileChanges = isWrite
if (storage) {
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite,
hasFileChanges,
isFileChange: false
})
}
}
linePossibleContainsHeadline(currentLine) {
// We can't check if the line start with # because when some write text before
// the # we also need to update the toc
return currentLine.includes('# ')
}
incrementLines(start, linesAdded, linesRemoved, editor) {
const highlightedLines = editor.options.linesHighlighted
const totalHighlightedLines = highlightedLines.length
const offset = linesAdded - linesRemoved
// Store new items to be added as we're changing the lines
const newLines = []
let i = totalHighlightedLines
while (i--) {
const lineNumber = highlightedLines[i]
// Interval that will need to be updated
// Between start and (start + offset) remove highlight
if (lineNumber >= start) {
highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1)
// Lines that need to be relocated
if (lineNumber >= start + linesRemoved) {
newLines.push(lineNumber + offset)
}
}
}
// Adding relocated lines
highlightedLines.push(...newLines)
if (this.props.onChange) {
this.props.onChange(editor)
}
}
handleHighlight(editor, changeObject) {
const lines = editor.options.linesHighlighted
if (!lines.includes(changeObject)) {
lines.push(changeObject)
editor.addLineClass(
changeObject,
'text',
'CodeMirror-activeline-background'
)
} else {
lines.splice(lines.indexOf(changeObject), 1)
editor.removeLineClass(
changeObject,
'text',
'CodeMirror-activeline-background'
)
}
if (this.props.onChange) {
this.props.onChange(editor)
}
}
updateHighlight(editor, changeObject) {
const linesAdded = changeObject.text.length - 1
const linesRemoved = changeObject.removed.length - 1
// If no lines added or removed return
if (linesAdded === 0 && linesRemoved === 0) {
return
}
let start = changeObject.from.line
switch (changeObject.origin) {
case '+insert", "undo':
start += 1
break
case 'paste':
case '+delete':
case '+input':
if (changeObject.to.ch !== 0 || changeObject.from.ch !== 0) {
start += 1
}
break
default:
return
}
this.incrementLines(start, linesAdded, linesRemoved, editor)
}
moveCursorTo(row, col) {}
scrollToLine(event, num) {
const cursor = {
line: num,
ch: 1
}
this.editor.setCursor(cursor)
const top = this.editor.charCoords({ line: num, ch: 0 }, 'local').top
const middleHeight = this.editor.getScrollerElement().offsetHeight / 2
this.editor.scrollTo(null, top - middleHeight - 5)
}
focus() {
this.editor.focus()
}
blur() {
this.editor.blur()
}
reload() {
// Change event shouldn't be fired when switch note
this.editor.off('change', this.changeHandler)
this.value = this.props.value
this.editor.setValue(this.props.value)
this.editor.clearHistory()
this.restartHighlighting()
this.editor.on('change', this.changeHandler)
this.editor.refresh()
// wakatime
const { storageKey, noteKey } = this.props
const storage = findStorage(storageKey)
if (storage)
sendWakatimeHeartBeat(storage.path, noteKey, storage.name, {
isWrite: false,
hasFileChanges: false,
isFileChange: true
})
}
setValue(value) {
const cursor = this.editor.getCursor()
this.editor.setValue(value)
this.editor.setCursor(cursor)
}
/**
* Update content of one line
* @param {Number} lineNumber
* @param {String} content
*/
setLineContent(lineNumber, content) {
const prevContent = this.editor.getLine(lineNumber)
const prevContentLength = prevContent ? prevContent.length : 0
this.editor.replaceRange(
content,
{ line: lineNumber, ch: 0 },
{ line: lineNumber, ch: prevContentLength }
)
}
handleDropImage(dropEvent) {
dropEvent.preventDefault()
const { storageKey, noteKey } = this.props
attachmentManagement.handleAttachmentDrop(
this,
storageKey,
noteKey,
dropEvent
)
}
insertAttachmentMd(imageMd) {
this.editor.replaceSelection(imageMd)
}
autoDetectLanguage(content) {
const res = hljs.highlightAuto(content, Object.keys(languageMaps))
this.setMode(languageMaps[res.language])
}
handlePaste(editor, forceSmartPaste) {
const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props
const isURL = str =>
/(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str)
const isInLinkTag = editor => {
const startCursor = editor.getCursor('start')
const prevChar = editor.getRange(
{
line: startCursor.line,
ch: startCursor.ch - 2
},
{
line: startCursor.line,
ch: startCursor.ch
}
)
const endCursor = editor.getCursor('end')
const nextChar = editor.getRange(
{
line: endCursor.line,
ch: endCursor.ch
},
{
line: endCursor.line,
ch: endCursor.ch + 1
}
)
return prevChar === '](' && nextChar === ')'
}
const isInFencedCodeBlock = editor => {
const cursor = editor.getCursor()
let token = editor.getTokenAt(cursor)
if (token.state.fencedState) {
return true
}
let line = (line = cursor.line - 1)
while (line >= 0) {
token = editor.getTokenAt({
ch: 3,
line
})
if (token.start === token.end) {
--line
} else if (token.type === 'comment') {
if (line > 0) {
token = editor.getTokenAt({
ch: 3,
line: line - 1
})
return token.type !== 'comment'
} else {
return true
}
} else {
return false
}
}
return false
}
const pastedTxt = clipboard.readText()
if (isInFencedCodeBlock(editor)) {
this.handlePasteText(editor, pastedTxt)
} else if (
fetchUrlTitle &&
isMarkdownTitleURL(pastedTxt) &&
!isInLinkTag(editor)
) {
this.handlePasteUrl(editor, pastedTxt)
} else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) {
this.handlePasteUrl(editor, pastedTxt)
} else if (attachmentManagement.isAttachmentLink(pastedTxt)) {
attachmentManagement
.handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt)
.then(modifiedText => {
this.editor.replaceSelection(modifiedText)
})
} else {
const image = clipboard.readImage()
if (!image.isEmpty()) {
attachmentManagement.handlePasteNativeImage(
this,
storageKey,
noteKey,
image
)
} else if (enableSmartPaste || forceSmartPaste) {
const pastedHtml = clipboard.readHTML()
if (pastedHtml.length > 0) {
this.handlePasteHtml(editor, pastedHtml)
} else {
this.handlePasteText(editor, pastedTxt)
}
} else {
this.handlePasteText(editor, pastedTxt)
}
}
if (!this.props.mode && this.props.autoDetect) {
this.autoDetectLanguage(editor.doc.getValue())
}
}
handleScroll(e) {
if (this.props.onScroll) {
this.props.onScroll(e)
}
}
handlePasteUrl(editor, pastedTxt) {
let taggedUrl = `<${pastedTxt}>`
let urlToFetch = pastedTxt
let titleMark = ''
if (isMarkdownTitleURL(pastedTxt)) {
const pastedTxtSplitted = pastedTxt.split(' ')
titleMark = `${pastedTxtSplitted[0]} `
urlToFetch = pastedTxtSplitted[1]
taggedUrl = `<${urlToFetch}>`
}
editor.replaceSelection(taggedUrl)
const isImageReponse = response => {
return (
response.headers.has('content-type') &&
response.headers.get('content-type').match(/^image\/.+$/)
)
}
const replaceTaggedUrl = replacement => {
const value = editor.getValue()
const cursor = editor.getCursor()
const newValue = value.replace(taggedUrl, titleMark + replacement)
const newCursor = Object.assign({}, cursor, {
ch: cursor.ch + newValue.length - (value.length - titleMark.length)
})
editor.setValue(newValue)
editor.setCursor(newCursor)
}
fetch(urlToFetch, {
method: 'get'
})
.then(response => {
if (isImageReponse(response)) {
return this.mapImageResponse(response, urlToFetch)
} else {
return this.mapNormalResponse(response, urlToFetch)
}
})
.then(replacement => {
replaceTaggedUrl(replacement)
})
.catch(e => {
replaceTaggedUrl(pastedTxt)
})
}
handlePasteHtml(editor, pastedHtml) {
const markdown = this.turndownService.turndown(pastedHtml)
editor.replaceSelection(markdown)
}
handlePasteText(editor, pastedTxt) {
editor.replaceSelection(pastedTxt)
}
mapNormalResponse(response, pastedTxt) {
return this.decodeResponse(response).then(body => {
return new Promise((resolve, reject) => {
try {
const parsedBody = new window.DOMParser().parseFromString(
body,
'text/html'
)
const escapePipe = str => {
return str.replace('|', '\\|')
}
const linkWithTitle = `[${escapePipe(
parsedBody.title
)}](${pastedTxt})`
resolve(linkWithTitle)
} catch (e) {
reject(e)
}
})
})
}
initialHighlighting() {
if (this.editor.options.linesHighlighted == null) {
return
}
const totalHighlightedLines = this.editor.options.linesHighlighted.length
const totalAvailableLines = this.editor.lineCount()
for (let i = 0; i < totalHighlightedLines; i++) {
const lineNumber = this.editor.options.linesHighlighted[i]
if (lineNumber > totalAvailableLines) {
// make sure that we skip the invalid lines althrough this case should not be happened.
continue
}
this.editor.addLineClass(
lineNumber,
'text',
'CodeMirror-activeline-background'
)
}
}
restartHighlighting() {
this.editor.options.linesHighlighted = this.props.linesHighlighted
this.initialHighlighting()
}
mapImageResponse(response, pastedTxt) {
return new Promise((resolve, reject) => {
try {
const url = response.url
const name = url.substring(url.lastIndexOf('/') + 1)
const imageLinkWithName = ``
resolve(imageLinkWithName)
} catch (e) {
reject(e)
}
})
}
decodeResponse(response) {
const headers = response.headers
const _charset = headers.has('content-type')
? this.extractContentTypeCharset(headers.get('content-type'))
: undefined
return response.arrayBuffer().then(buff => {
return new Promise((resolve, reject) => {
try {
const charset =
_charset !== undefined && iconv.encodingExists(_charset)
? _charset
: 'utf-8'
resolve(iconv.decode(Buffer.from(buff), charset).toString())
} catch (e) {
reject(e)
}
})
})
}
extractContentTypeCharset(contentType) {
return contentType
.split(';')
.filter(str => {
return str
.trim()
.toLowerCase()
.startsWith('charset')
})
.map(str => {
return str.replace(/['"]/g, '').split('=')[1]
})[0]
}
render() {
const { className, fontSize, fontFamily, width, height } = this.props
const normalisedFontFamily = normalizeEditorFontFamily(fontFamily)
return (
<div
className={className == null ? 'CodeEditor' : `CodeEditor ${className}`}
ref='root'
tabIndex='-1'
style={{
fontFamily: normalisedFontFamily,
fontSize,
width,
height
}}
onDrop={e => this.handleDropImage(e)}
/>
)
}
createSpellCheckPanel() {
const panel = document.createElement('div')
panel.className = 'panel bottom'
panel.id = 'editor-bottom-panel'
const dropdown = document.createElement('select')
dropdown.title = 'Spellcheck'
dropdown.className = styles['spellcheck-select']
dropdown.addEventListener('change', e =>
spellcheck.setLanguage(this.editor, dropdown.value)
)
const options = spellcheck.getAvailableDictionaries()
for (const op of options) {
const option = document.createElement('option')
option.value = op.value
option.innerHTML = op.label
dropdown.appendChild(option)
}
panel.appendChild(dropdown)
return panel
}
}
CodeEditor.propTypes = {
value: PropTypes.string,
enableRulers: PropTypes.bool,
rulers: PropTypes.arrayOf(Number),
mode: PropTypes.string,
className: PropTypes.string,
onBlur: PropTypes.func,
onChange: PropTypes.func,
readOnly: PropTypes.bool,
autoDetect: PropTypes.bool,
spellCheck: PropTypes.bool,
enableMarkdownLint: PropTypes.bool,
customMarkdownLintConfig: PropTypes.string,
deleteUnusedAttachments: PropTypes.bool,
RTL: PropTypes.bool
}
CodeEditor.defaultProps = {
readOnly: false,
theme: 'xcode',
keyMap: 'sublime',
fontSize: 14,
fontFamily: 'Monaco, Consolas',
indentSize: 4,
indentType: 'space',
autoDetect: false,
spellCheck: false,
enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint,
customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig,
prettierConfig: DEFAULT_CONFIG.editor.prettierConfig,
deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments,
RTL: false
}
================================================
FILE: browser/components/CodeEditor.styl
================================================
.codeEditor-typo
text-decoration underline wavy red
.spellcheck-select
border: none
================================================
FILE: browser/components/ColorPicker.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import { SketchPicker } from 'react-color'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ColorPicker.styl'
const componentHeight = 330
class ColorPicker extends React.Component {
constructor(props) {
super(props)
this.state = {
color: this.props.color || '#939395'
}
this.onColorChange = this.onColorChange.bind(this)
this.handleConfirm = this.handleConfirm.bind(this)
}
componentWillReceiveProps(nextProps) {
this.onColorChange(nextProps.color)
}
onColorChange(color) {
this.setState({
color
})
}
handleConfirm() {
this.props.onConfirm(this.state.color)
}
render() {
const { onReset, onCancel, targetRect } = this.props
const { color } = this.state
const clientHeight = document.body.clientHeight
const alignX = targetRect.right + 4
let alignY = targetRect.top
if (targetRect.top + componentHeight > clientHeight) {
alignY = targetRect.bottom - componentHeight
}
return (
<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>
</div>
</div>
)
}
}
ColorPicker.propTypes = {
color: PropTypes.string,
targetRect: PropTypes.object,
onConfirm: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired
}
export default CSSModules(ColorPicker, styles)
================================================
FILE: browser/components/ColorPicker.styl
================================================
.colorPicker
position fixed
z-index 2
display flex
flex-direction column
.cover
position fixed
top 0
right 0
bottom 0
left 0
.footer
display flex
justify-content center
z-index 2
align-items center
& > button + button
margin-left 10px
.btn-cancel,
.btn-confirm,
.btn-reset
vertical-align middle
height 25px
margin-top 2.5px
border-radius 2px
border none
padding 0 5px
background-color $default-button-background
&:hover
background-color $default-button-background--hover
.btn-confirm
background-color #1EC38B
&:hover
background-color darken(#1EC38B, 25%)
================================================
FILE: browser/components/MarkdownEditor.js
================================================
/* eslint-disable camelcase */
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './MarkdownEditor.styl'
import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import eventEmitter from 'browser/main/lib/eventEmitter'
import { findStorage } from 'browser/lib/findStorage'
import ConfigManager from 'browser/main/lib/ConfigManager'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
class MarkdownEditor extends React.Component {
constructor(props) {
super(props)
// char codes for ctrl + w
this.escapeFromEditor = [17, 87]
// ctrl + shift + ;
this.supportMdSelectionBold = [16, 17, 186]
this.state = {
status:
props.config.editor.switchPreview === 'RIGHTCLICK'
? props.config.editor.delfaultStatus
: 'CODE',
renderValue: props.value,
keyPressed: new Set(),
isLocked: props.isLocked
}
this.lockEditorCode = this.handleLockEditor.bind(this)
this.focusEditor = this.focusEditor.bind(this)
this.previewRef = React.createRef()
}
componentDidMount() {
this.value = this.refs.code.value
eventEmitter.on('editor:lock', this.lockEditorCode)
eventEmitter.on('editor:focus', this.focusEditor)
}
componentDidUpdate() {
this.value = this.refs.code.value
}
UNSAFE_componentWillReceiveProps(props) {
if (props.value !== this.props.value) {
this.queueRendering(props.value)
}
}
componentWillUnmount() {
this.cancelQueue()
eventEmitter.off('editor:lock', this.lockEditorCode)
eventEmitter.off('editor:focus', this.focusEditor)
}
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() {
clearTimeout(this.renderTimer)
}
renderPreview(value) {
this.setState({
renderValue: value
})
}
setValue(value) {
this.refs.code.setValue(value)
}
handleChange(e) {
this.value = this.refs.code.value
this.props.onChange(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.previewRef.current.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
const newConfig = Object.assign({}, config)
newConfig.editor.delfaultStatus = newStatus
ConfigManager.set(newConfig)
}
)
}
}
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')
) {
const cursorPosition = this.refs.code.editor.getCursor()
this.setState(
{
status: 'PREVIEW'
},
() => {
this.previewRef.current.focus()
this.previewRef.current.scrollToLine(cursorPosition.line)
}
)
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
}
handleDoubleClick(e) {
if (this.state.isLocked) return
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)
}
)
}
}
handlePreviewMouseDown(e) {
this.previewMouseDownedAt = new Date()
}
handlePreviewMouseUp(e) {
const { config } = this.props
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) {
e.preventDefault()
e.stopPropagation()
const idMatch = /checkbox-([0-9]+)/
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 targetLine = lines[lineIndex]
let newLine = targetLine
if (targetLine.match(checkedMatch)) {
newLine = targetLine.replace(checkReplace, '[ ]')
}
if (targetLine.match(uncheckedMatch)) {
newLine = targetLine.replace(uncheckReplace, '[x]')
}
this.refs.code.setLineContent(lineIndex, newLine)
}
}
focus() {
if (this.state.status === 'PREVIEW') {
this.setState(
{
status: 'CODE'
},
() => {
this.refs.code.focus()
}
)
} else {
this.refs.code.focus()
}
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
}
reload() {
this.refs.code.reload()
this.cancelQueue()
this.renderPreview(this.props.value)
}
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)
}
// 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)
) {
this.handleContextMenu()
if (config.editor.switchPreview === 'BLUR') document.activeElement.blur()
}
if (
keyPressed.size === this.supportMdSelectionBold.length &&
this.supportMdSelectionBold.every(isNoteHandlerKey)
) {
this.addMdAroundWord('**')
}
}
addMdAroundWord(mdElement) {
if (this.refs.code.editor.getSelection()) {
return this.addMdAroundSelection(mdElement)
}
const currentCaret = this.refs.code.editor.getCursor()
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
)
}
)
}
handleKeyUp(e) {
const keyPressed = this.state.keyPressed
keyPressed.delete(e.keyCode)
this.setState({ keyPressed })
}
handleLockEditor() {
this.setState({ isLocked: !this.state.isLocked })
}
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
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const previewStyle = {}
if (this.props.ignorePreviewPointerEvents)
previewStyle.pointerEvents = 'none'
const storage = findStorage(storageKey)
return (
<div
className={
className == null ? 'MarkdownEditor' : `MarkdownEditor ${className}`
}
onContextMenu={e => this.handleContextMenu(e)}
tabIndex='-1'
onKeyDown={e => this.handleKeyDown(e)}
onKeyUp={e => this.handleKeyUp(e)}
>
<CodeEditor
styleName={
this.state.status === 'CODE' ? 'codeEditor' : 'codeEditor--hide'
}
ref='code'
mode='Boost Flavored Markdown'
value={value}
theme={config.editor.theme}
keyMap={config.editor.keyMap}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
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)}
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
ref={this.previewRef}
styleName={
this.state.status === 'PREVIEW' ? 'preview' : 'preview--hide'
}
style={previewStyle}
theme={config.ui.theme}
keyMap={config.editor.keyMap}
fontSize={config.preview.fontSize}
fontFamily={config.preview.fontFamily}
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}
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)}
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}
onDrop={e => this.handleDropImage(e)}
RTL={RTL}
/>
</div>
)
}
}
MarkdownEditor.propTypes = {
className: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ignorePreviewPointerEvents: PropTypes.bool
}
export default CSSModules(MarkdownEditor, styles)
================================================
FILE: browser/components/MarkdownEditor.styl
================================================
.root
position relative
.codeEditor
absolute top bottom left right
.hide
z-index 0
opacity 0
pointer-events none
.codeEditor--hide
@extend .codeEditor
@extend .hide
.preview
display block
absolute top bottom left right
background-color white
height 100%
width 100%
.preview--hide
@extend .preview
@extend .hide
================================================
FILE: browser/components/MarkdownPreview.js
================================================
import PropTypes from 'prop-types'
import React from 'react'
import { connect } from 'react-redux'
import Markdown from 'browser/lib/markdown'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import consts from 'browser/lib/consts'
import Raphael from 'raphael'
import flowchart from 'flowchart'
import mermaidRender from './render/MermaidRender'
import SequenceDiagram from '@rokt33r/js-sequence-diagrams'
import Chart from 'chart.js'
import eventEmitter from 'browser/main/lib/eventEmitter'
import config from 'browser/main/lib/ConfigManager'
import htmlTextHelper from 'browser/lib/htmlTextHelper'
import convertModeName from 'browser/lib/convertModeName'
import copy from 'copy-to-clipboard'
import mdurl from 'mdurl'
import exportNote from 'browser/main/lib/dataApi/exportNote'
import formatMarkdown from 'browser/main/lib/dataApi/formatMarkdown'
import formatHTML, {
CSS_FILES,
buildStyle,
getCodeThemeLink,
getStyleParams,
escapeHtmlCharactersInCodeTag
} from 'browser/main/lib/dataApi/formatHTML'
import formatPDF from 'browser/main/lib/dataApi/formatPDF'
import yaml from 'js-yaml'
import i18n from 'browser/lib/i18n'
import path from 'path'
import { remote, shell } from 'electron'
import attachmentManagement from '../main/lib/dataApi/attachmentManagement'
import filenamify from 'filenamify'
import { render } from 'react-dom'
import Carousel from 'react-image-carousel'
import { push } from 'connected-react-router'
import ConfigManager from '../main/lib/ConfigManager'
import uiThemes from 'browser/lib/ui-themes'
import { buildMarkdownPreviewContextMenu } from 'browser/lib/contextMenuBuilder'
const dialog = remote.dialog
const scrollBarStyle = `
::-webkit-scrollbar {
${config.get().ui.showScrollBar ? '' : 'display: none;'}
width: 12px;
}
::-webkit-scrollbar-thumb {
${config.get().ui.showScrollBar ? '' : 'display: none;'}
background-color: rgba(0, 0, 0, 0.15);
}
::-webkit-scrollbar-track-piece {
background-color: inherit;
}
`
const scrollBarDarkStyle = `
::-webkit-scrollbar {
${config.get().ui.showScrollBar ? '' : 'display: none;'}
width: 12px;
}
::-webkit-scrollbar-thumb {
${config.get().ui.showScrollBar ? '' : 'display: none;'}
background-color: rgba(0, 0, 0, 0.3);
}
::-webkit-scrollbar-track-piece {
background-color: inherit;
}
`
// return the line number of the line that used to generate the specified element
// return -1 if the line is not found
function getSourceLineNumberByElement(element) {
let isHasLineNumber = element.dataset.line !== undefined
let parent = element
while (!isHasLineNumber && parent.parentElement !== null) {
parent = parent.parentElement
isHasLineNumber = parent.dataset.line !== undefined
}
return parent.dataset.line !== undefined ? parseInt(parent.dataset.line) : -1
}
class MarkdownPreview extends React.Component {
constructor(props) {
super(props)
this.contextMenuHandler = e => this.handleContextMenu(e)
this.mouseDownHandler = e => this.handleMouseDown(e)
this.mouseUpHandler = e => this.handleMouseUp(e)
this.DoubleClickHandler = e => this.handleDoubleClick(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
leading: false,
trailing: true
})
this.checkboxClickHandler = e => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
this.saveAsPdfHandler = () => this.handleSaveAsPdf()
this.printHandler = () => this.handlePrint()
this.resizeHandler = _.throttle(this.handleResize.bind(this), 100)
this.linkClickHandler = this.handleLinkClick.bind(this)
this.initMarkdown = this.initMarkdown.bind(this)
this.initMarkdown()
}
initMarkdown() {
const { smartQuotes, sanitize, breaks } = this.props
this.markdown = new Markdown({
typographer: smartQuotes,
sanitize,
breaks
})
}
handleCheckboxClick(e) {
this.props.onCheckboxClick(e)
}
handleScroll(e) {
if (this.props.onScroll) {
this.props.onScroll(e)
}
}
handleContextMenu(event) {
const menu = buildMarkdownPreviewContextMenu(this, event)
const switchPreview = ConfigManager.get().editor.switchPreview
if (menu != null && switchPreview !== 'RIGHTCLICK') {
menu.popup(remote.getCurrentWindow())
} else if (_.isFunction(this.props.onContextMenu)) {
this.props.onContextMenu(event)
}
}
handleDoubleClick(e) {
if (this.props.onDoubleClick != null) this.props.onDoubleClick(e)
}
handleMouseDown(e) {
const config = ConfigManager.get()
const clickElement = e.target
const targetTag = clickElement.tagName // The direct parent HTML of where was clicked ie "BODY" or "DIV"
const lineNumber = getSourceLineNumberByElement(clickElement) // Line location of element clicked.
if (
config.editor.switchPreview === 'RIGHTCLICK' &&
e.buttons === 2 &&
config.editor.type === 'SPLIT'
) {
eventEmitter.emit('topbar:togglemodebutton', 'CODE')
}
if (e.ctrlKey) {
if (config.editor.type === 'SPLIT') {
if (lineNumber !== -1) {
eventEmitter.emit('line:jump', lineNumber)
}
} else {
if (lineNumber !== -1) {
eventEmitter.emit('editor:focus')
eventEmitter.emit('line:jump', lineNumber)
}
}
}
if (this.props.onMouseDown != null && targetTag === 'BODY')
this.props.onMouseDown(e)
}
handleMouseUp(e) {
if (!this.props.onMouseUp) return
if (e.target != null && e.target.tagName === 'A') {
return null
}
if (this.props.onMouseUp != null) this.props.onMouseUp(e)
}
handleSaveAsText() {
this.exportAsDocument('txt')
}
handleSaveAsMd() {
this.exportAsDocument('md', formatMarkdown(this.props))
}
handleSaveAsHtml() {
this.exportAsDocument('html', formatHTML(this.props))
}
handleSaveAsPdf() {
this.exportAsDocument('pdf', formatPDF(this.props))
}
handlePrint() {
this.refs.root.contentWindow.print()
}
exportAsDocument(fileType, contentFormatter) {
const note = this.props.getNote()
const options = {
defaultPath: filenamify(note.title, {
replacement: '_'
}),
filters: [{ name: 'Documents', extensions: [fileType] }],
properties: ['openFile', 'createDirectory']
}
dialog.showSaveDialog(remote.getCurrentWindow(), options, filename => {
if (filename) {
const storagePath = this.props.storagePath
exportNote(storagePath, note, filename, contentFormatter)
.then(res => {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message: `Exported to ${filename}`,
buttons: [i18n.__('Ok')]
})
})
.catch(err => {
dialog.showErrorBox(
'Export error',
err ? err.message || err : 'Unexpected error during export'
)
throw err
})
}
})
}
fixDecodedURI(node) {
if (
node &&
node.children.length === 1 &&
typeof node.children[0] === 'string'
) {
const { innerText, href } = node
node.innerText = mdurl.decode(href) === innerText ? href : innerText
}
}
getScrollBarStyle() {
const { theme } = this.props
return uiThemes.some(item => item.name === theme && item.isDark)
? scrollBarDarkStyle
: scrollBarStyle
}
componentDidMount() {
const { onDrop } = this.props
this.refs.root.setAttribute('sandbox', 'allow-scripts')
this.refs.root.contentWindow.document.body.addEventListener(
'contextmenu',
this.contextMenuHandler
)
let styles = `
<style id='style'></style>
<link rel="stylesheet" id="codeTheme">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
${this.getScrollBarStyle()}
</style>
`
CSS_FILES.forEach(file => {
styles += `<link rel="stylesheet" href="${file}">`
})
this.refs.root.contentWindow.document.head.innerHTML = styles
this.rewriteIframe()
this.applyStyle()
this.refs.root.contentWindow.document.addEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.addEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.addEventListener(
'drop',
onDrop || this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'scroll',
this.scrollHandler
)
this.refs.root.contentWindow.addEventListener('resize', this.resizeHandler)
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
eventEmitter.on('export:save-pdf', this.saveAsPdfHandler)
eventEmitter.on('print', this.printHandler)
}
componentWillUnmount() {
const { onDrop } = this.props
this.refs.root.contentWindow.document.body.removeEventListener(
'contextmenu',
this.contextMenuHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'drop',
onDrop || this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'scroll',
this.scrollHandler
)
this.refs.root.contentWindow.removeEventListener(
'resize',
this.resizeHandler
)
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
eventEmitter.off('export:save-pdf', this.saveAsPdfHandler)
eventEmitter.off('print', this.printHandler)
}
componentDidUpdate(prevProps) {
// actual rewriteIframe function should be called only once
let needsRewriteIframe = false
if (prevProps.value !== this.props.value) needsRewriteIframe = true
if (
prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize ||
prevProps.mermaidHTMLLabel !== this.props.mermaidHTMLLabel ||
prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks ||
prevProps.lineThroughCheckbox !== this.props.lineThroughCheckbox
) {
this.initMarkdown()
needsRewriteIframe = true
}
if (
prevProps.fontFamily !== this.props.fontFamily ||
prevProps.fontSize !== this.props.fontSize ||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
prevProps.codeBlockTheme !== this.props.codeBlockTheme ||
prevProps.lineNumber !== this.props.lineNumber ||
prevProps.showCopyNotification !== this.props.showCopyNotification ||
prevProps.theme !== this.props.theme ||
prevProps.scrollPastEnd !== this.props.scrollPastEnd ||
prevProps.allowCustomCSS !== this.props.allowCustomCSS ||
prevProps.customCSS !== this.props.customCSS ||
prevProps.RTL !== this.props.RTL
) {
this.applyStyle()
needsRewriteIframe = true
}
if (needsRewriteIframe) {
this.rewriteIframe()
}
// Should scroll to top after selecting another note
if (prevProps.noteKey !== this.props.noteKey) {
this.scrollTo(0, 0)
}
}
applyStyle() {
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS,
RTL
} = getStyleParams(this.props)
this.getWindow().document.getElementById(
'codeTheme'
).href = getCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS,
RTL
)
}
rewriteIframe() {
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.removeEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
el.removeEventListener('click', this.linkClickHandler)
}
)
const {
theme,
indentSize,
showCopyNotification,
storagePath,
noteKey,
sanitize,
mermaidHTMLLabel
} = this.props
let { value, codeBlockTheme } = this.props
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
if (sanitize === 'NONE') {
const splitWithCodeTag = value.split('```')
value = escapeHtmlCharactersInCodeTag(splitWithCodeTag)
}
const renderedHTML = this.markdown.render(value)
attachmentManagement.migrateAttachments(value, storagePath, noteKey)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
renderedHTML,
storagePath
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.addEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
}
)
codeBlockTheme = consts.THEMES.find(theme => theme.name === codeBlockTheme)
const codeBlockThemeClassName = codeBlockTheme
? codeBlockTheme.className
: 'cm-s-default'
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.code code'),
el => {
let syntax = CodeMirror.findModeByName(convertModeName(el.className))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML =
'<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = e => {
e.preventDefault()
e.stopPropagation()
copy(content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
}
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
el.parentNode.className += ` ${codeBlockThemeClassName}`
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
})
})
}
)
const opts = {}
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.flowchart'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = flowchart.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message
}
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.sequence'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = SequenceDiagram.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, { theme: 'simple' })
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message
}
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.chart'),
el => {
try {
const format = el.attributes.getNamedItem('data-format').value
const chartConfig =
format === 'yaml'
? yaml.load(el.innerHTML)
: JSON.parse(el.innerHTML)
el.innerHTML = ''
const canvas = document.createElement('canvas')
el.appendChild(canvas)
const height = el.attributes.getNamedItem('data-height')
if (height && height.value !== 'undefined') {
el.style.height = height.value + 'vh'
canvas.height = height.value + 'vh'
}
// eslint-disable-next-line no-unused-vars
const chart = new Chart(canvas, chartConfig)
} catch (e) {
el.className = 'chart-error'
el.innerHTML = 'chartjs diagram parse error: ' + e.message
}
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.mermaid'),
el => {
mermaidRender(
el,
htmlTextHelper.decodeEntities(el.innerHTML),
theme,
mermaidHTMLLabel
)
}
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.gallery'),
el => {
const images = el.innerHTML.split(/\n/g).filter(i => i.length > 0)
el.innerHTML = ''
const height = el.attributes.getNamedItem('data-height')
if (height && height.value !== 'undefined') {
el.style.height = height.value + 'vh'
}
let autoplay = el.attributes.getNamedItem('data-autoplay')
if (autoplay && autoplay.value !== 'undefined') {
autoplay = parseInt(autoplay.value, 10) || 0
} else {
autoplay = 0
}
render(<Carousel images={images} autoplay={autoplay} />, el)
}
)
const markdownPreviewIframe = document.querySelector('.MarkdownPreview')
const rect = markdownPreviewIframe.getBoundingClientRect()
const config = { attributes: true, subtree: true }
const imgObserver = new MutationObserver(mutationList => {
for (const mu of mutationList) {
if (mu.target.className === 'carouselContent-enter-done') {
this.setImgOnClickEventHelper(mu.target, rect)
break
}
}
})
const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll(
'img'
)
for (const img of imgList) {
const parentEl = img.parentElement
this.setImgOnClickEventHelper(img, rect)
imgObserver.observe(parentEl, config)
}
const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll(
'a'
)
for (const a of aList) {
a.removeEventListener('click', this.linkClickHandler)
a.addEventListener('click', this.linkClickHandler)
}
}
setImgOnClickEventHelper(img, rect) {
img.onclick = () => {
const widthMagnification = document.body.clientWidth / img.width
const heightMagnification = document.body.clientHeight / img.height
const baseOnWidth = widthMagnification < heightMagnification
const magnification = baseOnWidth
? widthMagnification
: heightMagnification
const zoomImgWidth = img.width * magnification
const zoomImgHeight = img.height * magnification
const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2
const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2
const originalImgTop = img.y + rect.top
const originalImgLeft = img.x + rect.left
const originalImgRect = {
top: `${originalImgTop}px`,
left: `${originalImgLeft}px`,
width: `${img.width}px`,
height: `${img.height}px`
}
const zoomInImgRect = {
top: `${baseOnWidth ? zoomImgTop : 0}px`,
left: `${baseOnWidth ? 0 : zoomImgLeft}px`,
width: `${zoomImgWidth}px`,
height: `${zoomImgHeight}px`
}
const animationSpeed = 300
const zoomImg = document.createElement('img')
zoomImg.src = img.src
zoomImg.style = `
position: absolute;
top: ${baseOnWidth ? zoomImgTop : 0}px;
left: ${baseOnWidth ? 0 : zoomImgLeft}px;
width: ${zoomImgWidth};
height: ${zoomImgHeight}px;
`
zoomImg.animate([originalImgRect, zoomInImgRect], animationSpeed)
const overlay = document.createElement('div')
overlay.style = `
background-color: rgba(0,0,0,0.5);
cursor: zoom-out;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: ${document.body.clientHeight}px;
z-index: 100;
`
overlay.onclick = () => {
zoomImg.style = `
position: absolute;
top: ${originalImgTop}px;
left: ${originalImgLeft}px;
width: ${img.width}px;
height: ${img.height}px;
`
const zoomOutImgAnimation = zoomImg.animate(
[zoomInImgRect, originalImgRect],
animationSpeed
)
zoomOutImgAnimation.onfinish = () => overlay.remove()
}
overlay.appendChild(zoomImg)
document.body.appendChild(overlay)
}
}
handleResize() {
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'),
el => {
el.setAttribute('height', el.clientWidth / el.getAttribute('ratio'))
}
)
}
focus() {
this.refs.root.focus()
}
getWindow() {
return this.refs.root.contentWindow
}
/**
* @public
* @param {Number} targetLine
*/
scrollToLine(targetLine) {
const blocks = this.getWindow().document.querySelectorAll(
'body [data-line]'
)
for (let index = 0; index < blocks.length; index++) {
let block = blocks[index]
const line = parseInt(block.getAttribute('data-line'))
if (line > targetLine || index === blocks.length - 1) {
block = blocks[index - 1]
block != null && this.scrollTo(0, block.offsetTop)
break
}
}
}
/**
* `document.body.scrollTo`
* @param {Number} x
* @param {Number} y
*/
scrollTo(x, y) {
this.getWindow().document.body.scrollTo(x, y)
}
preventImageDroppedHandler(e) {
e.preventDefault()
e.stopPropagation()
}
notify(title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join(
'file://',
global.__dirname,
'../../resources/app.png'
)
}
return new window.Notification(title, options)
}
handleLinkClick(e) {
e.preventDefault()
e.stopPropagation()
const el = e.target.closest('a[href]')
if (!el) return
const rawHref = el.getAttribute('href')
const { dispatch } = this.props
if (!rawHref) return // not checked href because parser will create file://... string for [empty link]()
const parser = document.createElement('a')
parser.href = rawHref
const isStartWithHash = rawHref[0] === '#'
const { href, hash } = parser
const linkHash = hash === '' ? rawHref : hash // needed because we're having special link formats that are removed by parser e.g. :line:10
const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html
const regexNoteInternalLink = new RegExp(`${extractIdRegex.source}(.+)`)
if (isStartWithHash || regexNoteInternalLink.test(rawHref)) {
const posOfHash = linkHash.indexOf('#')
if (posOfHash > -1) {
const extractedId = linkHash.slice(posOfHash + 1)
const targetId = mdurl.encode(extractedId)
const targetElement = this.getWindow().document.getElementById(targetId)
if (targetElement != null) {
this.scrollTo(0, targetElement.offsetTop)
}
return
}
}
// this will match the new uuid v4 hash and the old hash
// e.g.
// :note:1c211eb7dcb463de6490 and
// :note:7dd23275-f2b4-49cb-9e93-3454daf1af9c
const regexIsNoteLink = /^:note:([a-zA-Z0-9-]{20,36})$/
if (regexIsNoteLink.test(linkHash)) {
eventEmitter.emit('list:jump', linkHash.replace(':note:', ''))
return
}
const regexIsLine = /^:line:[0-9]/
if (regexIsLine.test(linkHash)) {
const numberPattern = /\d+/g
const lineNumber = parseInt(linkHash.match(numberPattern)[0])
eventEmitter.emit('line:jump', lineNumber)
return
}
// this will match the old link format storage.key-note.key
// e.g.
// 877f99c3268608328037-1c211eb7dcb463de6490
const regexIsLegacyNoteLink = /^(.{20})-(.{20})$/
if (regexIsLegacyNoteLink.test(linkHash)) {
eventEmitter.emit('list:jump', linkHash.split('-')[1])
return
}
const regexIsTagLink = /^:tag:([\w]+)$/
if (regexIsTagLink.test(rawHref)) {
const tag = rawHref.match(regexIsTagLink)[1]
dispatch(push(`/tags/${encodeURIComponent(tag)}`))
return
}
// other case
this.openExternal(href)
}
openExternal(href) {
try {
const success =
shell.openExternal(href) || shell.openExternal(decodeURI(href))
if (!success) console.error('failed to open url ' + href)
} catch (e) {
// URI Error threw from decodeURI
console.error(e)
}
}
render() {
const { className, style, tabIndex } = this.props
return (
<iframe
className={
className != null ? 'MarkdownPreview ' + className : 'MarkdownPreview'
}
style={style}
tabIndex={tabIndex}
ref='root'
/>
)
}
}
MarkdownPreview.propTypes = {
onClick: PropTypes.func,
onDoubleClick: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseDown: PropTypes.func,
onContextMenu: PropTypes.func,
className: PropTypes.string,
value: PropTypes.string,
showCopyNotification: PropTypes.bool,
storagePath: PropTypes.string,
smartQuotes: PropTypes.bool,
smartArrows: PropTypes.bool,
breaks: PropTypes.bool
}
export default connect(
null,
null,
null,
{ forwardRef: true }
)(MarkdownPreview)
================================================
FILE: browser/components/MarkdownSplitEditor.js
================================================
import React from 'react'
import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import { findStorage } from 'browser/lib/findStorage'
import _ from 'lodash'
import styles from './MarkdownSplitEditor.styl'
import CSSModules from 'browser/lib/CSSModules'
class MarkdownSplitEditor extends React.Component {
constructor(props) {
super(props)
this.value = props.value
this.focus = () => this.refs.code.focus()
this.reload = () => this.refs.code.reload()
this.userScroll = props.config.preview.scrollSync
this.state = {
isSliderFocused: false,
codeEditorWidthInPercent: 50,
codeEditorHeightInPercent: 50
}
}
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) {
this.value = this.refs.code.value
this.props.onChange(e)
}
handleEditorScroll(e) {
if (this.userScroll) {
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 {
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)
}
this.scrollTo(previewTop, top, y =>
_.set(previewDoc, 'body.scrollTop', y)
)
}
}
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*>?)*\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 targetLine = lines[lineIndex]
let newLine = targetLine
if (targetLine.match(checkedMatch)) {
newLine = targetLine.replace(checkReplace, '[ ]')
}
if (targetLine.match(uncheckedMatch)) {
newLine = targetLine.replace(uncheckReplace, '[x]')
}
this.refs.code.setLineContent(lineIndex, newLine)
}
}
handleMouseMove(e) {
if (this.state.isSliderFocused) {
const rootRect = this.refs.root.getBoundingClientRect()
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 (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
})
}
}
}
handleMouseUp(e) {
e.preventDefault()
this.setState({
isSliderFocused: false
})
}
handleMouseDown(e) {
e.preventDefault()
this.setState({
isSliderFocused: true
})
}
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 (!(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'
onMouseMove={e => this.handleMouseMove(e)}
onMouseUp={e => this.handleMouseUp(e)}
>
<CodeEditor
ref='code'
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={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={editorStyle.indentSize}
enableRulers={config.editor.enableRulers}
rulers={config.editor.rulers}
scrollPastEnd={config.editor.scrollPastEnd}
fetchUrlTitle={config.editor.fetchUrlTitle}
enableTableEditor={config.editor.enableTableEditor}
storageKey={storageKey}
noteKey={noteKey}
linesHighlighted={linesHighlighted}
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}
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}
theme={config.ui.theme}
keyMap={config.editor.keyMap}
fontSize={config.preview.fontSize}
fontFamily={config.preview.fontFamily}
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}
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
tabInde='0'
value={value}
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>
)
}
}
export default CSSModules(MarkdownSplitEditor, styles)
================================================
FILE: browser/components/MarkdownSplitEditor.styl
================================================
.root
width 100%
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)
================================================
FILE: browser/components/ModalEscButton.js
================================================
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ModalEscButton.styl'
const ModalEscButton = ({ handleEscButtonClick }) => (
<button styleName='escButton' onClick={handleEscButtonClick}>
<div styleName='esc-mark'>×</div>
<div>esc</div>
</button>
)
ModalEscButton.propTypes = {
handleEscButtonClick: PropTypes.func.isRequired
}
export default CSSModules(ModalEscButton, styles)
================================================
FILE: browser/components/ModalEscButton.styl
================================================
.escButton
height 50px
position absolute
background-color transparent
color $ui-inactive-text-color
border none
top 1px
right 10px
text-align center
width top-bar--height
height top-bar-height
.esc-mark
font-size 28px
margin-top -5px
margin-bottom -7px
================================================
FILE: browser/components/NavToggleButton.js
================================================
/**
* @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
*/
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>
)
NavToggleButton.propTypes = {
isFolded: PropTypes.bool.isRequired,
handleToggleButtonClick: PropTypes.func.isRequired
}
export default CSSModules(NavToggleButton, styles)
================================================
FILE: browser/components/NavToggleButton.styl
================================================
.navToggle
navButtonColor()
display block
position absolute
left 5px
bottom 5px
border-radius 16.5px
height 34px
width 34px
line-height 100%
padding 0
&:hover
border: 1px solid #1EC38B;
background-color: alpha(#1EC38B, 30%)
border-radius: 50%;
body[data-theme="white"]
navWhiteButtonColor()
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 get-theme-var(theme, 'text-color')
for theme in 'dark' 'dracula' 'solarized-dark'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/NoteItem.js
================================================
/**
* @fileoverview Note item component.
*/
import PropTypes from 'prop-types'
import React from 'react'
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'
import TodoProcess from './TodoProcess'
import i18n from 'browser/lib/i18n'
/**
* @description Tag element component.
* @param {string} tagName
* @param {string} color
* @return {React.Component}
*/
const TagElement = ({ tagName, color }) => {
const style = {}
if (color) {
style.backgroundColor = color
style.color = invertColor(color, {
black: '#222',
white: '#f1f1f1',
threshold: 0.3
})
}
return (
<span styleName='item-bottom-tagList-item' key={tagName} style={style}>
#{tagName}
</span>
)
}
/**
* @description Tag element list component.
* @param {Array|null} tags
* @param {boolean} showTagsAlphabetically
* @param {Object} coloredTags
* @return {React.Component}
*/
const TagElementList = (tags, showTagsAlphabetically, coloredTags) => {
if (!isArray(tags)) {
return []
}
if (showTagsAlphabetically) {
return sortBy(tags).map(tag =>
TagElement({ tagName: tag, color: coloredTags[tag] })
)
} else {
return tags.map(tag =>
TagElement({ tagName: tag, color: coloredTags[tag] })
)
}
}
/**
* @description Note item component when using normal display mode.
* @param {boolean} isActive
* @param {Object} note
* @param {Function} handleNoteClick
* @param {Function} handleNoteContextMenu
* @param {Function} handleDragStart
* @param {Object} coloredTags
* @param {string} dateDisplay
*/
const NoteItem = ({
isActive,
note,
dateDisplay,
handleNoteClick,
handleNoteContextMenu,
handleDragStart,
pathname,
storageName,
folderName,
viewType,
showTagsAlphabetically,
coloredTags
}) => (
<div
styleName={isActive ? 'item--active' : 'item'}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
onDragStart={e => handleDragStart(e, note)}
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' />
)}
<div styleName='item-title'>
{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>
<div styleName='item-middle-app-meta'>
<div
title={
viewType === 'ALL'
? storageName
: viewType === 'STORAGE'
? folderName
: null
}
styleName='item-middle-app-meta-label'
>
{viewType === 'ALL' && storageName}
{viewType === 'STORAGE' && folderName}
</div>
</div>
</div>
<div styleName='item-bottom'>
<div styleName='item-bottom-tagList'>
{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>
)}
</div>
<div>
{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)} />
) : (
''
)}
</div>
</div>
</div>
</div>
)
NoteItem.propTypes = {
isActive: PropTypes.bool.isRequired,
dateDisplay: PropTypes.string.isRequired,
coloredTags: PropTypes.object,
note: PropTypes.shape({
storage: PropTypes.string.isRequired,
key: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
title: PropTypes.string.isrequired,
tags: PropTypes.array,
isStarred: PropTypes.bool.isRequired,
isTrashed: PropTypes.bool.isRequired,
blog: PropTypes.shape({
blogLink: PropTypes.string,
blogId: PropTypes.number
})
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired,
handleDragStart: PropTypes.func.isRequired
}
export default CSSModules(NoteItem, styles)
================================================
FILE: browser/components/NoteItem.styl
================================================
$control-height = 30px
.root
absolute left bottom
top $topBar-height - 1
background-color $ui-noteList-backgroundColor
.item
position relative
padding 0 20px
user-select none
cursor pointer
background-color $ui-noteList-backgroundColor
transition 0.2s
&:hover
background-color alpha($ui-button--active-backgroundColor, 20%)
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-text-color
.item-bottom-tagList-item
background-color alpha(white, 0.6)
color $ui-text-color
.item-star
color $ui-favorite-star-button-color
&:active
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-text-color
.item-bottom-tagList-item
background-color alpha(white, 0.6)
color $ui-text-color
.item-wrapper
padding 15px 0
border-bottom $ui-border
position relative
.item--active
@extend .item
background-color alpha($ui-button--active-backgroundColor, 60%)
color $ui-text-color
.item-title
.item-title-empty
.item-bottom-tagList-empty
.item-bottom-time
.item-title-icon
color $ui-text-color
.item-bottom-tagList-item
background-color alpha(white, 0.6)
color $ui-text-color
.item-wrapper
border-color transparent
.item-star
color $ui-favorite-star-button-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
.item-title-icon
position relative
font-size 12px
color $ui-inactive-text-color
top 2px
.item-title
font-size 15px
font-weight 700
position relative
top -12px
left 20px
padding 0px 15px 0px 0px
margin-bottom 4px
overflow ellipsis
color $ui-inactive-text-color
.item-title-empty
font-weight normal
color $ui-inactive-text-color
.item-middle
font-size 13px
padding-left 2px
padding-bottom 2px
.item-middle-time
color $ui-inactive-text-color
display inline-block
.item-middle-app-meta
float right
.item-middle-app-meta-label
opacity 0.4
color $ui-inactive-text-color
padding 0 3px
white-space nowrap
text-overflow ellipsis
overflow hidden
max-width 200px
.item-bottom
position relative
bottom 0px
margin-top 10px
font-size 12px
line-height 20px
overflow ellipsis
display block
.item-bottom-tagList
flex 1
overflow ellipsis
line-height 25px
padding-left 2px
margin-right 40px
.item-bottom-tagList-item
font-size 11px
margin-right 8px
padding 0
box-sizing border-box
border-radius 2px
padding 4px
vertical-align middle
background-color white
color $ui-inactive-text-color
.item-bottom-time
color $ui-inactive-text-color
font-size 13px
padding-left 2px
padding-bottom 2px
.item-star
position absolute
right 2px
top 5px
color alpha($ui-favorite-star-button-color, 60%)
font-size 12px
padding 0
border-radius 17px
.item-pin
position absolute
right 25px
top 7px
color #E54D42
font-size 14px
padding 0
border-radius 17px
body[data-theme="white"]
.item
background-color $ui-white-noteList-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
&:active
background-color $ui-button--active-backgroundColor
.item--active
@extend .item
background-color $ui-button--active-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
.item
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
color $ui-dark-text-color
&:active
transition 0.15s
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
color $ui-dark-text-color
.item-wrapper
border-color alpha($ui-dark-button--active-backgroundColor, 60%)
.item--active
border-color $ui-dark-borderColor
background-color $ui-dark-button--active-backgroundColor
.item-wrapper
border-color transparent
.item-title
.item-title-icon
.item-bottom-time
color $ui-dark-text-color
.item-bottom-tagList-item
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color $ui-dark-button--hover-color
.item-bottom-tagList-item
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
.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="solarized-dark"]
.root
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteList-backgroundColor
.item
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteList-backgroundColor
&:hover
transition 0.15s
// background-color alpha($ui-solarized-dark-noteList-backgroundColor, 20%)
color $ui-solarized-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-solarized-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-solarized-dark-noteList-backgroundColor, 20%)
color $ui-solarized-dark-text-color
&:active
transition 0.15s
background-color $ui-solarized-dark-noteList-backgroundColor
color $ui-solarized-dark-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-solarized-dark-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-solarized-dark-noteList-backgroundColor, 10%)
color $ui-solarized-dark-text-color
.item-wrapper
border-color alpha($ui-solarized-dark-button--active-backgroundColor, 60%)
.item--active
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-button-backgroundColor
.item-wrapper
border-color transparent
.item-title
.item-title-icon
.item-bottom-time
color $ui-solarized-dark-text-color
.item-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-solarized-dark-text-color
&:hover
// background-color alpha($ui-solarized-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-bottom-tagList-item
background-color alpha(#fff, 20%)
.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
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 get-theme-var(theme, 'borderColor')
background-color get-theme-var(theme, 'noteList-backgroundColor')
&:hover
transition 0.15s
// 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 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(get-theme-var(theme, 'button-backgroundColor'), 60%)
.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
color get-theme-var(theme, 'active-color')
.item-bottom-tagList-item
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-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
for theme in 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/NoteItemSimple.js
================================================
/**
* @fileoverview Note item component with simple display mode.
*/
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteItemSimple.styl'
import i18n from 'browser/lib/i18n'
/**
* @description Note item component when using simple display mode.
* @param {boolean} isActive
* @param {Object} note
* @param {Function} handleNoteClick
* @param {Function} handleNoteContextMenu
* @param {Function} handleDragStart
*/
const NoteItemSimple = ({
isActive,
isAllNotesView,
note,
handleNoteClick,
handleNoteContextMenu,
handleDragStart,
pathname,
storage
}) => (
<div
styleName={isActive ? 'item-simple--active' : 'item-simple'}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
onDragStart={e => handleDragStart(e, note)}
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>
)}
</div>
</div>
)
NoteItemSimple.propTypes = {
isActive: PropTypes.bool.isRequired,
note: PropTypes.shape({
storage: PropTypes.string.isRequired,
key: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
title: PropTypes.string.isrequired
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired,
handleDragStart: PropTypes.func.isRequired
}
export default CSSModules(NoteItemSimple, styles)
================================================
FILE: browser/components/NoteItemSimple.styl
================================================
$control-height = 30px
.root
absolute left bottom
top $topBar-height - 1
background-color $ui-noteList-backgroundColor
.item-simple
position relative
padding 0 20px
user-select none
cursor pointer
background-color $ui-noteList-backgroundColor
transition 0.2s
&:hover
background-color alpha($ui-button--active-backgroundColor, 20%)
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
color $ui-text-color
&:active
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
color $ui-text-color
.item-simple--active
@extend .item-simple
background-color alpha($ui-button--active-backgroundColor, 60%)
color $ui-text-color
.item-simple-title
.item-simple-title-empty
border-color transparent
color $ui-text-color
.item-simple-title-icon
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 40%)
color #e74c3c
.menu-button-label
color $ui-text-color
.item-simple-title
font-size 13px
height 40px
padding-right 20px
box-sizing border-box
line-height 24px
padding-top 8px
overflow ellipsis
color $ui-inactive-text-color
border-bottom $ui-border
position relative
.item-simple-title-icon
font-size 12px
color $ui-inactive-text-color
padding-right 6px
.item-simple-title-empty
font-weight normal
color $ui-inactive-text-color
.item-pin
position absolute
right 0px
top 12px
color #E54D42
font-size 14px
padding 0
border-radius 17px
body[data-theme="white"]
.item-simple
background-color $ui-white-noteList-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
&:active
background-color $ui-button--active-backgroundColor
.item-simple--active
@extend .item-simple
background-color $ui-button--active-backgroundColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
.item-simple
border-color $ui-dark-borderColor
background-color $ui-dark-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#fff, 20%)
color $ui-dark-text-color
&:active
transition 0.15s
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(white, 10%)
color $ui-dark-text-color
.item-simple--active
border-color $ui-dark-borderColor
background-color $ui-dark-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-dark-text-color
.item-simple-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-dark-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-inactive-text-color
border-color alpha($ui-dark-button--active-backgroundColor, 60%)
.item-simple-title-icon
color $ui-darkinactive-text-color
.item-simple-title-empty
color $ui-dark-inactive-text-color
body[data-theme="solarized-dark"]
.root
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteList-backgroundColor
.item-simple
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color $ui-solarized-dark-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-solarized-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#fff, 20%)
color $ui-solarized-dark-text-color
&:active
transition 0.15s
// background-color $ui-solarized-dark-button--active-backgroundColor
color $ui-dark-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-solarized-dark-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(white, 10%)
color $ui-solarized-dark-text-color
.item-simple--active
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-tag-backgroundColor
.item-simple-wrapper
border-color transparent
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
color $ui-dark-text-color
.item-simple-bottom-time
color $ui-solarized-dark-text-color
.item-simple-bottom-tagList-item
background-color alpha(white, 10%)
color $ui-solarized-dark-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
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 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
color get-theme-var(theme, 'text-color')
.item-simple-bottom-tagList-item
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
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 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/RealtimeNotification.js
================================================
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './RealtimeNotification.styl'
const electron = require('electron')
const { shell } = electron
class RealtimeNotification extends React.Component {
constructor(props) {
super(props)
this.state = {
notifications: []
}
}
componentDidMount() {
this.fetchNotifications()
}
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 })
})
}
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
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>
) : (
''
)
return (
<div styleName='notification-area' style={this.props.style}>
{link}
</div>
)
}
}
RealtimeNotification.propTypes = {}
export default CSSModules(RealtimeNotification, styles)
================================================
FILE: browser/components/RealtimeNotification.styl
================================================
.notification-area
z-index 1000
font-size 12px
position: relative
top: 12px
background-color none
.notification-link
position absolute
text-decoration none
color #282A36
font-size 14px
border 1px solid #6FA8E6
background-color alpha(#6FA8E6, 0.2)
padding 5px 12px
border-radius 2px
transition 0.2s
&:hover
color #1378BD
body[data-theme="dark"]
.notification-area
background-color none
.notification-link
color #fff
border 1px solid alpha(#5CB85C, 0.6)
background-color alpha(#5CB85C, 0.2)
transition 0.2s
&:hover
color #5CB85C
apply-theme(theme)
body[data-theme={theme}]
.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')
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/SideNavFilter.js
================================================
/**
* @fileoverview Filter for all notes.
*/
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SideNavFilter.styl'
import i18n from 'browser/lib/i18n'
/**
* @param {boolean} isFolded
* @param {boolean} isHomeActive
* @param {Function} handleAllNotesButtonClick
* @param {boolean} isStarredActive
* @param {Function} handleStarredButtonClick
* @return {React.Component}
*/
const SideNavFilter = ({
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'}
onClick={handleAllNotesButtonClick}
>
<div styleName='iconWrap'>
<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'}
onClick={handleStarredButtonClick}
>
<div styleName='iconWrap'>
<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}
>
<div styleName='iconWrap'>
<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>
)
SideNavFilter.propTypes = {
isFolded: PropTypes.bool,
isHomeActive: PropTypes.bool.isRequired,
handleAllNotesButtonClick: PropTypes.func.isRequired,
isStarredActive: PropTypes.bool.isRequired,
isTrashedActive: PropTypes.bool.isRequired,
handleStarredButtonClick: PropTypes.func.isRequired,
handleTrashedButtonClick: PropTypes.func.isRequired
}
export default CSSModules(SideNavFilter, styles)
================================================
FILE: browser/components/SideNavFilter.styl
================================================
.menu
margin-bottom 20px
.menu-button
navButtonColor()
height 36px
padding 0 15px 0 20px
font-size 14px
width 100%
text-align left
overflow ellipsis
display flex
align-items center
&:hover, &:active, &:active:hover
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.iconWrap
width 20px
text-align center
.counters
float right
color $ui-inactive-text-color
.menu-button--active
@extend .menu-button
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
&:hover
color #1EC38B
.menu-button-star--active
@extend .menu-button
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
.menu-button-trash--active
@extend .menu-button
SideNavFilter()
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.menu-button-label, .counters
color #1EC38B
.menu-button-label
margin-left 10px
flex 1
.menu--folded
@extend .menu
.menu-button, .menu-button--active, .menu-button-star--active, .menu-button-trash--active
text-align center
padding 0 12px
&:hover .menu-button-label
transition opacity 0.15s
opacity 1
color $ui-tooltip-text-color
background-color $ui-tooltip-backgroundColor
.menu-button-label
position fixed
display inline-block
height 36px
left 44px
padding 0 10px
margin-left 0
overflow ellipsis
z-index 10
line-height 32px
border-top-right-radius 2px
border-bottom-right-radius 2px
pointer-events none
opacity 0
font-size 13px
.counters
display none
body[data-theme="white"]
.menu-button
navWhiteButtonColor()
.counters
color $ui-inactive-text-color
.menu-button--active
color #e74c3c
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #e74c3c
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #e74c3c
.menu-button-label
color $ui-text-color
.menu-button-star--active
color #F9BF3B
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #F9BF3B
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #F9BF3B
.menu-button-label
color $ui-text-color
.menu-button-trash--active
color #5D9E36
background-color $ui-button--active-backgroundColor
.menu-button-label
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #5D9E36
.menu-button-label
color $ui-text-color
&:active, &:active:hover
background-color alpha($ui-button--active-backgroundColor, 50%)
color #5D9E36
.menu-button-label
color $ui-text-color
body[data-theme="dark"]
.menu-button
&:active
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
.menu-button--active
color #c0392b
background-color $ui-dark-button--active-backgroundColor
.menu-button-label
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color #c0392b
.menu-button-label
color $ui-dark-text-color
.menu-button-star--active
color $ui-favorite-star-button-color
background-color $ui-dark-button--active-backgroundColor
.menu-button-label
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color $ui-favorite-star-button-color
.menu-button-label
color $ui-dark-text-color
.menu-button-trash--active
color #5D9E36
background-color $ui-dark-button--active-backgroundColor
.menu-button-label
color $ui-dark-text-color
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
color #5D9E36
.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')
.menu-button--active
color get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
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 get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
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 get-theme-var(theme, 'text-color')
background-color get-theme-var(theme, 'button-backgroundColor')
.menu-button-label
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')
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/SnippetTab.js
================================================
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SnippetTab.styl'
import context from 'browser/lib/context'
import i18n from 'browser/lib/i18n'
class SnippetTab extends React.Component {
constructor(props) {
super(props)
this.state = {
isRenaming: false,
name: props.snippet.name
}
}
componentWillUpdate(nextProps) {
if (nextProps.snippet.name !== this.props.snippet.name) {
this.setState({
name: nextProps.snippet.name
})
}
}
handleClick(e) {
this.props.onClick(e)
}
handleContextMenu(e) {
context.popup([
{
label: i18n.__('Rename'),
click: e => this.handleRenameClick(e)
}
])
}
handleRenameClick(e) {
this.startRenaming()
}
handleNameInputBlur(e) {
this.handleRename()
}
handleNameInputChange(e) {
this.setState({
name: e.target.value
})
}
handleNameInputKeyDown(e) {
switch (e.keyCode) {
case 13:
this.handleRename()
break
case 27:
this.setState((prevState, props) => ({
name: props.snippet.name,
isRenaming: false
}))
break
}
}
handleRename() {
this.setState(
{
isRenaming: false
},
() => {
if (this.props.snippet.name !== this.state.name) {
this.props.onRename(this.state.name)
}
}
)
}
handleDeleteButtonClick(e) {
this.props.onDelete(e)
}
startRenaming() {
this.setState(
{
isRenaming: true
},
() => {
this.refs.name.focus()
this.refs.name.select()
}
)
}
handleDragStart(e) {
e.dataTransfer.dropEffect = 'move'
this.props.onDragStart(e)
}
handleDrop(e) {
this.props.onDrop(e)
}
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)}
draggable='true'
>
{snippet.name.trim().length > 0 ? (
snippet.name
) : (
<span>{i18n.__('Unnamed')}</span>
)}
</button>
) : (
<input
styleName='input'
ref='name'
value={this.state.name}
onChange={e => this.handleNameInputChange(e)}
onBlur={e => this.handleNameInputBlur(e)}
onKeyDown={e => this.handleNameInputKeyDown(e)}
/>
)}
{isDeletable && (
<button
styleName='deleteButton'
onClick={e => this.handleDeleteButtonClick(e)}
>
<i className='fa fa-times' />
</button>
)}
</div>
)
}
}
SnippetTab.propTypes = {}
export default CSSModules(SnippetTab, styles)
================================================
FILE: browser/components/SnippetTab.styl
================================================
.root
position relative
flex 1
min-width 70px
overflow hidden
border-left 1px solid $ui-borderColor
border-top 1px solid $ui-borderColor
&:hover
background-color alpha($ui-button--active-backgroundColor, 20%)
.deleteButton
color: $ui-text-color
visibility visible
transition 0.15s
.button
color: $ui-text-color
transition 0.15s
.root--active
@extend .root
min-width 100px
background-color alpha($ui-button--active-backgroundColor, 60%)
.deleteButton
visibility visible
color: $ui-text-color
transition 0.15s
.button
font-weight bold
color: $ui-text-color
transition 0.15s
.button
width 100%
height 29px
overflow ellipsis
text-align left
padding-right 23px
border none
background-color transparent
transition 0.15s
border-left 4px solid transparent
color $ui-inactive-text-color
.deleteButton
position absolute
top 5px
height 20px
right 5px
width 20px
text-align center
border none
padding 0
color $ui-inactive-text-color
background-color transparent
border-radius 2px
visibility hidden
.input
height 29px
border $ui-active-color
padding 0 5px
width 100%
outline none
body[data-theme="default"], body[data-theme="white"]
.root--active
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
border-top 1px solid $ui-dark-borderColor
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
transition 0.15s
.button
color $ui-dark-text-color
transition 0.15s
.deleteButton
color $ui-dark-text-color
transition 0.15s
.root--active
background-color $ui-dark-button--active-backgroundColor
border-left 1px solid $ui-dark-borderColor
border-top 1px solid $ui-dark-borderColor
.button
color $ui-dark-text-color
.deleteButton
color $ui-dark-text-color
.button
border none
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
.input
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
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 get-theme-var(theme, 'text-color')
.button
color get-theme-var(theme, '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 get-theme-var(theme, 'noteDetail-backgroundColor')
color get-theme-var(theme, 'text-color')
transition 0.15s
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/StorageItem.js
================================================
/**
* @fileoverview Micro component for showing storage.
*/
import PropTypes from 'prop-types'
import React from 'react'
import styles from './StorageItem.styl'
import CSSModules from 'browser/lib/CSSModules'
import _ from 'lodash'
import { SortableHandle } from 'react-sortable-hoc'
const DraggableIcon = SortableHandle(({ className }) => (
<i className={`fa ${className}`} />
))
const FolderIcon = ({ className, color, isActive }) => {
const iconStyle = isActive ? 'fa-folder-open-o' : 'fa-folder-o'
return (
<i className={`fa ${iconStyle} ${className}`} style={{ color: color }} />
)
}
/**
* @param {boolean} isActive
* @param {object} tooltipRef,
* @param {Function} handleButtonClick
* @param {Function} handleMouseEnter
* @param {Function} handleContextMenu
* @param {string} folderName
* @param {string} folderColor
* @param {boolean} isFolded
* @param {number} noteCount
* @param {Function} handleDrop
* @param {Function} handleDragEnter
* @param {Function} handleDragOut
* @return {React.Component}
*/
const StorageItem = ({
styles,
isActive,
tooltipRef,
handleButtonClick,
handleMouseEnter,
handleContextMenu,
folderName,
folderColor,
isFolded,
noteCount,
handleDrop,
handleDragEnter,
handleDragLeave
}) => {
return (
<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']} />
)}
<span
styleName={
isFolded ? 'folderList-item-name--folded' : 'folderList-item-name'
}
>
<FolderIcon
styleName='folderList-item-icon'
color={folderColor}
isActive={isActive}
/>
{isFolded
? _.truncate(folderName, { length: 1, omission: '' })
: 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,
isFolded: PropTypes.bool.isRequired,
handleDragEnter: PropTypes.func.isRequired,
handleDragLeave: PropTypes.func.isRequired,
noteCount: PropTypes.number
}
export default CSSModules(StorageItem, styles)
================================================
FILE: browser/components/StorageItem.styl
================================================
.root
width 100%
user-select none
.folderList-item
display flex
width 100%
height 34px
background-color transparent
color $ui-inactive-text-color
padding 0
text-align left
border none
overflow ellipsis
font-size 14px
align-items: center
&:first-child
margin-top 0
&:hover
color #1EC38B;
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition background-color 0.15s
&:active
color $$ui-button-default-color
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.folderList-item--active
@extend .folderList-item
color #1EC38B
background-color alpha($ui-button-default--active-backgroundColor, 20%)
&:hover
color #1EC38B;
background-color alpha($ui-button-default--active-backgroundColor, 50%)
.folderList-item-name
display block
flex 1
padding-right: 10px
border-width 0 0 0 2px
border-style solid
border-color transparent
overflow hidden
text-overflow ellipsis
.folderList-item-noteCount
float right
line-height 26px
padding-right 15px
font-size 13px
.folderList-item-tooltip
tooltip()
position fixed
padding 0 10px
left 44px
z-index 10
pointer-events none
opacity 0
border-top-right-radius 2px
border-bottom-right-radius 2px
height 34px
line-height 32px
transition-property opacity
.folderList-item:hover, .folderList-item--active:hover
.folderList-item-tooltip
opacity 1
.folderList-item-name--folded
@extend .folderList-item-name
padding-left 7px
.folderList-item-icon
font-size 9px
.folderList-item-icon
padding-right: 10px
.folderList-item-reorder
font-size: 9px
padding: 10px 8px 10px 9px;
color: rgba(147, 147, 149, 0.3)
cursor: ns-resize
&:before
content: "\f142 \f142"
body[data-theme="white"]
.folderList-item
color $ui-inactive-text-color
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
transition background-color 0.15s
&:active
color $ui-text-color
background-color $ui-button--active-backgroundColor
.folderList-item--active
@extend .folderList-item
color $ui-text-color
background-color $ui-button--active-backgroundColor
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 50%)
body[data-theme="dark"]
.folderList-item
&:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
&:active
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
.folderList-item--active
@extend .folderList-item
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
&: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%)
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 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')
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/StorageList.js
================================================
/**
* @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
*/
const StorageList = ({ storageList, isFolded }) => (
<div styleName={isFolded ? 'storageList-folded' : 'storageList'}>
{storageList.length > 0 ? (
storageList
) : (
<div styleName='storageList-empty'>No storage mount.</div>
)}
</div>
)
StorageList.propTypes = {
storageList: PropTypes.arrayOf(PropTypes.element).isRequired
}
export default CSSModules(StorageList, styles)
================================================
FILE: browser/components/StorageList.styl
================================================
.storageList
absolute left right
bottom 37px
top 180px
overflow-y auto
.storageList-folded
@extend .storageList
width 44px
.storageList-empty
padding 0 10px
margin-top 15px
line-height 24px
color $ui-inactive-text-color
body[data-theme="dark"]
.storageList-empty
color $ui-dark-inactive-text-color
.root-folded
.storageList-empty
white-space nowrap
transform rotate(90deg)
================================================
FILE: browser/components/TagListItem.js
================================================
/**
* @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
*/
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' }}
/>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
</span>
</button>
</div>
)
TagListItem.propTypes = {
name: PropTypes.string.isRequired,
handleClickTagListItem: PropTypes.func.isRequired,
color: PropTypes.string
}
export default CSSModules(TagListItem, styles)
================================================
FILE: browser/components/TagListItem.styl
================================================
.tagList-itemContainer
display flex
.tagList-item
display flex
flex 1
width 100%
height 26px
background-color transparent
color $ui-inactive-text-color
padding 0
margin-bottom 5px
text-align left
border none
overflow ellipsis
font-size 13px
&:first-child
margin-top 0
&:hover
color $ui-button-default-color
background-color alpha($ui-button-default--active-backgroundColor, 20%)
transition background-color 0.15s
&:active, &:active:hover
color $ui-button-default-color
background-color $ui-button-default--active-backgroundColor
.tagList-itemNarrow
composes tagList-item
flex none
width 20px
padding 0 4px
.tagList-item-active
background-color $ui-button-default--active-backgroundColor
display flex
flex 1
width 100%
height 26px
padding 0
margin-bottom 5px
text-align left
border none
overflow ellipsis
font-size 13px
color $ui-button-default-color
&:hover
background-color alpha($ui-button-default--active-backgroundColor, 60%)
transition 0.2s
.tagList-itemNarrow-active
composes tagList-item-active
flex none
width 20px
padding 0 4px
.tagList-item-name
display block
flex 1
padding 0 8px 0 4px
height 26px
line-height 26px
border-width 0 0 0 2px
border-style solid
border-color transparent
overflow hidden
text-overflow ellipsis
.tagList-item-count
float right
line-height 26px
padding-right 15px
font-size 13px
.tagList-item-color
height 26px
width 3px
display inline-block
body[data-theme="white"]
.tagList-item
color $ui-inactive-text-color
&:hover
color $ui-text-color
background-color alpha($ui-button--active-backgroundColor, 20%)
&:active
color $ui-text-color
background-color $ui-button--active-backgroundColor
.tagList-item-active
background-color $ui-button--active-backgroundColor
color $ui-text-color
&:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
.tagList-item-count
color $ui-text-color
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 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)
================================================
FILE: browser/components/TodoListPercentage.js
================================================
/**
* @fileoverview Percentage of todo achievement.
*/
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
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}%` }}>
<div styleName='progressBarInner'>
<p styleName='percentageText'>{percentageOfTodo}%</p>
</div>
</div>
<div styleName='todoClear'>
<p styleName='todoClearText' onClick={e => onClearCheckboxClick(e)}>
clear
</p>
</div>
</div>
)
TodoListPercentage.propTypes = {
percentageOfTodo: PropTypes.number.isRequired,
onClearCheckboxClick: PropTypes.func.isRequired
}
export default CSSModules(TodoListPercentage, styles)
================================================
FILE: browser/components/TodoListPercentage.styl
================================================
.percentageBar
display: flex
position absolute
top 72px
right 0px
left 0px
background-color #DADFE1
width 100%
height: 17px
font-size: 12px
z-index 100
border-radius 2px
.progressBar
background-color: #1EC38B
height 17px
border-radius 2px
transition 0.4s cubic-bezier(0.4, 0.4, 0, 1)
.progressBarInner
padding 0 10px
min-width 1px
height 100%
display -webkit-box
display box
justify-content center
align-items center
.percentageText
color #f4f4f4
font-weight 600
.todoClear
display flex
justify-content: flex-end
position absolute
z-index 120
width 100%
height 100%
padding 2px 10px
.todoClearText
color #f4f4f4
cursor pointer
font-weight 500
body[data-theme="dark"]
.percentageBar
background-color #444444
.progressBar
background-color: #1EC38B
.percentageText
color $ui-dark-text-color
.todoClearText
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.percentageBar
background-color #002b36
.progressBar
background-color: #2aa198
.percentageText
color #fdf6e3
.todoClearText
color #fdf6e3
apply-theme(theme)
body[data-theme={theme}]
.percentageBar
background-color: get-theme-var(theme, 'borderColor')
.progressBar
background-color get-theme-var(theme, 'active-color')
.percentageText
color get-theme-var(theme, 'text-color')
for theme in 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/TodoProcess.js
================================================
/**
* @fileoverview Percentage of todo achievement.
*/
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TodoProcess.styl'
const TodoProcess = ({
todoStatus: { total: totalTodo, completed: completedTodo }
}) => (
<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>
</div>
)
TodoProcess.propTypes = {
todoStatus: PropTypes.exact({
total: PropTypes.number.isRequired,
completed: PropTypes.number.isRequired
})
}
export default CSSModules(TodoProcess, styles)
================================================
FILE: browser/components/TodoProcess.styl
================================================
.todo-process
font-size 12px
display flex
padding-top 15px
width 85%
.todo-process-text
display inline-block
padding-right 10px
white-space nowrap
text-overflow ellipsis
color $ui-inactive-text-color
i
color $ui-inactive-text-color
padding-right 5px
.todo-process-bar
display inline-block
margin auto
height 4px
border-radius 10px
background-color #DADFE1
border-radius 2px
flex-grow 1
border 1px solid alpha(#6C7A89, 10%)
.todo-process-bar--inner
height 100%
border-radius 5px
background-color #6C7A89
transition 0.3s
body[data-theme="dark"]
.todo-process
color $ui-dark-text-color
.todo-process-bar
background-color #363A3D
.todo-process-text
color $ui-inactive-text-color
.todo-process-bar--inner
background-color: alpha(#939395, 50%)
================================================
FILE: browser/components/markdown.styl
================================================
global-reset()
borderColor = #D0D0D0 // using
highlightenBorderColor = darken(borderColor, 20%)
invBorderColor = #404849
brandBorderColor = #3FB399
focusBorderColor = #369DCD
buttonBorderColor = #4C4C4C
lightButtonColor = #898989
hoverBackgroundColor= transparentify(#444, 4%) // using
inactiveTextColor = #888 // using
textColor = #4D4D4D // using
backgroundColor= white
fontSize= 14px // using
shadowColor= #C5C5C5
invBackgroundColor = #4C4C4C
invTextColor = white
btnColor = #888
btnHighlightenColor = #000
brandColor = #2BAC8F
popupShadow = 0 0 5px 0 #888
tableHeadBgColor = white
tableOddBgColor = #F9F9F9
tableEvenBgColor = white
facebookColor= #3b5998
githubBtn= #201F1F
// using
successBackgroundColor= #E0F0D9
successTextColor= #3E753F
errorBackgroundColor= #F2DEDE
errorTextColor= #A64444
infoBackgroundColor= #D9EDF7
infoTextColor= #34708E
popupZIndex= 500
body
font-size 16px
padding 15px
font-family helvetica, arial, sans-serif
line-height 1.6
overflow-x hidden
background-color $ui-noteDetail-backgroundColor
// do not allow display line breaks
.katex-display > .katex
white-space nowrap
// allow inline line breaks
.katex
white-space initial
.katex .katex-html
display inline-flex
.katex .mfrac>.vlist>span:nth-child(2)
top 0 !important
.katex-error
background-color errorBackgroundColor
color errorTextColor
padding 5px
margin -5px
border-radius 5px
.flowchart-error, .sequence-error .chart-error
background-color errorBackgroundColor
color errorTextColor
padding 5px
border-radius 5px
justify-content left
li
label.taskListItem
margin-left -1.8em
&.checked
text-decoration line-through
opacity 0.5
&.taskListItem.checked
text-decoration line-through
opacity 0.5
div.math-rendered
text-align center
.math-failed
background-color alpha(red, 0.1)
color darken(red, 15%)
padding 5px
margin 5px 0
border-radius 5px
sup
position relative
top -.4em
font-size 0.8em
vertical-align top
sub
position relative
bottom -.4em
font-size 0.8em
vertical-align top
a
color brandColor
text-decoration none
padding 5px
border-radius 5px
margin -5px
transition .1s
img
vertical-align sub
&:hover
color lighten(brandColor, 5%)
text-decoration underline
background-color alpha(#FFC95C, 0.3)
&:visited
color brandColor
hr
border-top none
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
line-height 1.2
border-bottom solid 1px borderColor
&:first-child
margin-top 0
h2
font-size 1.75em
line-height 1.225
border-bottom solid 1px borderColor
&:first-child
margin-top 0
h3
font-size 1.5em
line-height 1.43
h4
font-size 1.25em
line-height 1.4
h5
font-size 1em
line-height 1.1
h6
font-size 1em
color #777
p
line-height 1.6em
margin 0 0 1em
white-space pre-line
word-wrap break-word
img
cursor zoom-in
max-width 100%
strong, b
font-weight bold
em, i
font-style italic
s, del, strike
text-decoration line-through
u
text-decoration underline
blockquote
border-left solid 4px brandBorderColor
margin 0 0 1em
padding 0 25px
ul
list-style-type disc
padding-left 2em
margin-bottom 1em
li
display list-item
&.taskListItem
list-style none
&>input
margin-left -1.6em
&>p
margin-left -1.8em
p
margin 0
&>li>ul, &>li>ol
margin 0
&>li>ul
list-style-type circle
&>li>ul
list-style-type square
ol
list-style-type decimal
padding-left 2em
margin-bottom 1em
li
display list-item
p
margin 0
&>li>ul, &>li>ol
margin 0
code
padding 0.2em 0.4em
background-color #f7f7f7
border-radius 3px
font-size 1em
text-decoration none
margin-right 2px
pre
padding 0.5rem !important
border solid 1px #D1D1D1
border-radius 5px
overflow-x auto
margin 0 0 1rem
display flex
line-height 1.4em
code
background-color inherit
margin 0
padding 0
border none
border-radius 0
&.CodeMirror
height initial
flex-wrap wrap
&>code
flex 1
overflow-x auto
&.mermaid svg
max-width 100% !important
&>span.filename
margin -0.5rem 100% 0.5rem -0.5rem
padding 0.125rem 0.375rem
background-color #777;
color white
&:empty
display none
&>span.lineNumber
display none
font-size 1em
padding 0.5rem 0
margin -0.5rem 0.5rem -0.5rem -0.5rem
border-right 1px solid
text-align right
border-top-left-radius 4px
border-bottom-left-radius 4px
&.CodeMirror-gutters
position initial
top initial
left initial
min-height 0 !important
&>span
display block
padding 0 .5em 0
table
display block
width 100%
margin 0 0 1em
overflow-x auto
thead
tr
background-color tableHeadBgColor
th
border-style solid
padding 6px 13px
line-height 1.6
border-width 1px 0 2px 1px
border-color borderColor
font-weight bold
&:last-child
border-right solid 1px borderColor
tbody
tr:nth-child(2n + 1)
background-color tableOddBgColor
tr:nth-child(2n)
background-color tableEvenBgColor
td
border-style solid
padding 6px 13px
line-height 1.6
border-width 0 0 1px 1px
border-color borderColor
&:last-child
border-right solid 1px borderColor
kbd
background-color #fafbfc
border solid 1px borderColor
border-bottom-color btnColor
border-radius 3px
box-shadow inset 0 -1px 0 #959da5
display inline-block
font-size .8em
line-height 1
padding 3px 5px
$admonition
box-shadow 0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)
position relative
margin 1.5625em 0
padding 0 1.2rem
border-left .4rem solid #448aff
border-radius .2rem
overflow auto
html .admonition>:last-child
margin-bottom 1.2rem
.admonition .admonition
margin 1em 0
.admonition p
margin-top: 0.5em
$admonition-icon
position absolute
left 1.2rem
font-family: "Material Icons"
font-weight: normal;
font-style: normal;
font-size: 24px
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
$admonition-title
margin 0 -1.2rem
padding .8rem 1.2rem .8rem 4rem
border-bottom .1rem solid rgba(68,138,255,.1)
background-color rgba(68,138,255,.1)
font-weight 700
.admonition>.admonition-title:last-child
margin-bottom 0
admonition_types = {
note: {color: #0288D1, icon: "note"},
hint: {color: #009688, icon: "info_outline"},
danger: {color: #c2185b, icon: "block"},
caution: {color: #ffa726, icon: "warning"},
error: {color: #d32f2f, icon: "error_outline"},
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
.admonition.{name}
@extend $admonition
border-left-color: val[color]
.admonition.{name}>.admonition-title
@extend $admonition-title
border-bottom-color: .1rem solid rgba(val[color], 0.2)
background-color: rgba(val[color], 0.2)
.admonition.{name}>.admonition-title:before
@extend $admonition-icon
color: val[color]
content: val[icon]
dl
margin 2rem 0
padding 0
display flex
width 100%
flex-wrap wrap
align-items flex-start
border-bottom 1px solid borderColor
background-color tableHeadBgColor
dt
border-top 1px solid borderColor
font-weight bold
text-align right
overflow hidden
flex-basis 20%
padding 0.4rem 0.9rem
box-sizing border-box
dd
border-top 1px solid borderColor
flex-basis 80%
padding 0.4rem 0.9rem
min-height 2.5rem
background-color $ui-noteDetail-backgroundColor
box-sizing border-box
dd + dd
margin-left 20%
pre.fence
flex-wrap wrap
.chart, .flowchart, .mermaid, .sequence
display flex
justify-content center
background-color white
max-width 100%
flex-grow 1
canvas, svg
max-width 100% !important
svg[ratio]
width 100%
.gallery
width 100%
height 50vh
.carousel
.carousel-main img
min-width auto
max-width 100%
min-height auto
max-height 100%
.carousel-footer::-webkit-scrollbar-corner
background-color transparent
.carousel-main, .carousel-footer
background-color $ui-noteDetail-backgroundColor
.prev, .next
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%)
themeDarkPreview = $ui-dark-noteDetail-backgroundColor
themeDarkTableOdd = themeDarkPreview
themeDarkTableEven = darken(themeDarkPreview, 10%)
themeDarkTableHead = themeDarkTableEven
themeDarkTableBorder = themeDarkBorder
themeDarkModalButtonDefault = themeDarkPreview
themeDarkModalButtonDanger = #BF360C
body[data-theme="dark"]
color themeDarkText
border-color themeDarkBorder
background-color themeDarkPreview
a:hover
background-color alpha(lighten(brandColor, 30%), 0.2) !important
code
color #EA6730
border-color darken(themeDarkBorder, 10%)
background-color lighten(themeDarkPreview, 5%)
pre
border-color lighten(#21252B, 20%)
code
background-color transparent
label.taskListItem
background-color themeDarkPreview
table
thead
tr
background-color themeDarkTableHead
th
border-color themeDarkTableBorder
&:last-child
border-right solid 1px themeDarkTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeDarkTableOdd
tr:nth-child(2n)
background-color themeDarkTableEven
td
border-color themeDarkTableBorder
&:last-child
border-right solid 1px themeDarkTableBorder
kbd
background-color themeDarkBorder
color themeDarkText
dl
border-color themeDarkBorder
background-color themeDarkTableHead
dt
border-color themeDarkBorder
dd
border-color themeDarkBorder
background-color themeDarkPreview
pre.fence
.gallery
.carousel-main, .carousel-footer
background-color $ui-dark-noteDetail-backgroundColor
.prev, .next
color $ui-dark-text-color
background-color $ui-dark-tag-backgroundColor
.markdownIt-TOC-wrapper
&,
&:before
background-color darken(themeDarkBackground, 5%)
color themeDarkText
apply-theme(theme)
body[data-theme={theme}]
color get-theme-var(theme, 'text-color')
border-color themeDarkBorder
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')
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')
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')
.markdownIt-TOC-wrapper
&,
&:before
background-color darken(get-theme-var(theme, 'noteDetail-backgroundColor'), 15%)
color themeDarkText
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)
================================================
FILE: browser/components/render/MermaidRender.js
================================================
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 = `
.loopText tspan {
fill: white;
}`
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
function getId() {
const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let id = 'm-'
for (let i = 0; i < 7; i++) {
id += pool[getRandomInt(0, 16)]
}
return id
}
function render(element, content, theme, enableHTMLLabel) {
try {
const height = element.attributes.getNamedItem('data-height')
const isPredefined = height && height.value !== 'undefined'
if (isPredefined) {
element.style.height = height.value + 'vh'
}
const isDarkTheme = uiThemes.some(
item => item.name === theme && item.isDark
)
mermaidAPI.initialize({
theme: isDarkTheme ? 'dark' : 'default',
themeCSS: isDarkTheme ? darkThemeStyling : '',
flowchart: {
htmlLabels: enableHTMLLabel
},
gantt: {
useWidth: element.clientWidth
}
})
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'
element.innerHTML = 'mermaid diagram parse error: ' + e.message
}
}
export default render
================================================
FILE: browser/lib/CMLanguageList.js
================================================
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'
}
================================================
FILE: browser/lib/CSSModules.js
================================================
import CSSModules from 'react-css-modules'
export default function(component, styles) {
return CSSModules(component, styles, { handleNotFoundStyleName: 'log' })
}
================================================
FILE: browser/lib/Languages.js
================================================
const languages = [
{
name: 'Albanian',
locale: 'sq'
},
{
name: 'Chinese (zh-CN)',
locale: 'zh-CN'
},
{
name: 'Chinese (zh-TW)',
locale: 'zh-TW'
},
{
name: 'Czech',
locale: 'cs'
},
{
name: 'Danish',
locale: 'da'
},
{
name: 'English',
locale: 'en'
},
{
name: 'French',
locale: 'fr'
},
{
name: 'German',
locale: 'de'
},
{
name: 'Hungarian',
locale: 'hu'
},
{
name: 'Japanese',
locale: 'ja'
},
{
name: 'Korean',
locale: 'ko'
},
{
name: 'Norwegian',
locale: 'no'
},
{
name: 'Polish',
locale: 'pl'
},
{
name: 'Portuguese (PT-BR)',
locale: 'pt-BR'
},
{
name: 'Portuguese (PT-PT)',
locale: 'pt-PT'
},
{
name: 'Russian',
locale: 'ru'
},
{
name: 'Spanish',
locale: 'es-ES'
},
{
name: 'Turkish',
locale: 'tr'
},
{
name: 'Thai',
locale: 'th'
}
]
module.exports = {
getLocales() {
return languages.reduce(function(localeList, locale) {
localeList.push(locale.locale)
return localeList
}, [])
},
getLanguages() {
return languages
}
}
================================================
FILE: browser/lib/Mutable.js
================================================
class MutableMap {
constructor(iterable) {
this._map = new Map(iterable)
Object.defineProperty(this, 'size', {
get: () => this._map.size,
set: function(value) {
this['size'] = value
}
})
}
get(...args) {
return this._map.get(...args)
}
set(...args) {
return this._map.set(...args)
}
delete(...args) {
return this._map.delete(...args)
}
has(...args) {
return this._map.has(...args)
}
clear(...args) {
return this._map.clear(...args)
}
forEach(...args) {
return this._map.forEach(...args)
}
[Symbol.iterator]() {
return this._map[Symbol.iterator]()
}
map(cb) {
const result = []
for (const [key, value] of this._map) {
result.push(cb(value, key))
}
return result
}
toJS() {
const result = {}
for (let [key, value] of this._map) {
if (value instanceof MutableSet || value instanceof MutableMap) {
value = value.toJS()
}
result[key] = value
}
return result
}
}
class MutableSet {
constructor(iterable) {
this._set = new Set(iterable)
Object.defineProperty(this, 'size', {
get: () => this._set.size,
set: function(value) {
this['size'] = value
}
})
}
add(...args) {
return this._set.add(...args)
}
delete(..
gitextract_1f_tfmxn/ ├── .babelrc ├── .boostnoterc.sample ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .prettierrc ├── .snapcraft/ │ └── travis_snapcraft.cfg ├── .travis.yml ├── FAQ.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── __mocks__/ │ └── electron.js ├── appdmg.json ├── browser/ │ ├── components/ │ │ ├── CodeEditor.js │ │ ├── CodeEditor.styl │ │ ├── ColorPicker.js │ │ ├── ColorPicker.styl │ │ ├── MarkdownEditor.js │ │ ├── MarkdownEditor.styl │ │ ├── MarkdownPreview.js │ │ ├── MarkdownSplitEditor.js │ │ ├── MarkdownSplitEditor.styl │ │ ├── ModalEscButton.js │ │ ├── ModalEscButton.styl │ │ ├── NavToggleButton.js │ │ ├── NavToggleButton.styl │ │ ├── NoteItem.js │ │ ├── NoteItem.styl │ │ ├── NoteItemSimple.js │ │ ├── NoteItemSimple.styl │ │ ├── RealtimeNotification.js │ │ ├── RealtimeNotification.styl │ │ ├── SideNavFilter.js │ │ ├── SideNavFilter.styl │ │ ├── SnippetTab.js │ │ ├── SnippetTab.styl │ │ ├── StorageItem.js │ │ ├── StorageItem.styl │ │ ├── StorageList.js │ │ ├── StorageList.styl │ │ ├── TagListItem.js │ │ ├── TagListItem.styl │ │ ├── TodoListPercentage.js │ │ ├── TodoListPercentage.styl │ │ ├── TodoProcess.js │ │ ├── TodoProcess.styl │ │ ├── markdown.styl │ │ └── render/ │ │ └── MermaidRender.js │ ├── lib/ │ │ ├── CMLanguageList.js │ │ ├── CSSModules.js │ │ ├── Languages.js │ │ ├── Mutable.js │ │ ├── RcParser.js │ │ ├── SnippetManager.js │ │ ├── TextEditorInterface.js │ │ ├── confirmDeleteNote.js │ │ ├── consts.js │ │ ├── context.js │ │ ├── contextMenuBuilder.js │ │ ├── convertModeName.js │ │ ├── customMeta.js │ │ ├── date-formatter.js │ │ ├── findNoteTitle.js │ │ ├── findStorage.js │ │ ├── getTodoStatus.js │ │ ├── htmlTextHelper.js │ │ ├── i18n.js │ │ ├── keygen.js │ │ ├── markdown-it-deflist.js │ │ ├── markdown-it-fence.js │ │ ├── markdown-it-frontmatter.js │ │ ├── markdown-it-sanitize-html.js │ │ ├── markdown-toc-generator.js │ │ ├── markdown.js │ │ ├── markdown2.js │ │ ├── markdownTextHelper.js │ │ ├── newNote.js │ │ ├── normalizeEditorFontFamily.js │ │ ├── search.js │ │ ├── slugify.js │ │ ├── spellcheck.js │ │ ├── turndown.js │ │ ├── ui-themes.js │ │ ├── utils.js │ │ └── wakatime-plugin.js │ ├── main/ │ │ ├── Detail/ │ │ │ ├── Detail.styl │ │ │ ├── DetailVars.styl │ │ │ ├── FolderSelect.js │ │ │ ├── FolderSelect.styl │ │ │ ├── FromUrlButton.js │ │ │ ├── FromUrlButton.styl │ │ │ ├── FullscreenButton.js │ │ │ ├── FullscreenButton.styl │ │ │ ├── InfoButton.js │ │ │ ├── InfoButton.styl │ │ │ ├── InfoPanel.js │ │ │ ├── InfoPanel.styl │ │ │ ├── InfoPanelTrashed.js │ │ │ ├── MarkdownNoteDetail.js │ │ │ ├── MarkdownNoteDetail.styl │ │ │ ├── NoteDetailInfo.styl │ │ │ ├── PermanentDeleteButton.js │ │ │ ├── RestoreButton.js │ │ │ ├── RestoreButton.styl │ │ │ ├── SnippetNoteDetail.js │ │ │ ├── SnippetNoteDetail.styl │ │ │ ├── StarButton.js │ │ │ ├── StarButton.styl │ │ │ ├── TagSelect.js │ │ │ ├── TagSelect.styl │ │ │ ├── ToggleDirectionButton.js │ │ │ ├── ToggleDirectionButton.styl │ │ │ ├── ToggleModeButton.js │ │ │ ├── ToggleModeButton.styl │ │ │ ├── TrashButton.js │ │ │ ├── TrashButton.styl │ │ │ └── index.js │ │ ├── DevTools/ │ │ │ ├── index.dev.js │ │ │ ├── index.js │ │ │ └── index.prod.js │ │ ├── Main.js │ │ ├── Main.styl │ │ ├── NewNoteButton/ │ │ │ ├── NewNoteButton.styl │ │ │ └── index.js │ │ ├── NoteList/ │ │ │ ├── NoteList.styl │ │ │ └── index.js │ │ ├── SideNav/ │ │ │ ├── ListButton.js │ │ │ ├── PreferenceButton.js │ │ │ ├── PreferenceButton.styl │ │ │ ├── SearchButton.js │ │ │ ├── SearchButton.styl │ │ │ ├── SideNav.styl │ │ │ ├── StorageItem.js │ │ │ ├── StorageItem.styl │ │ │ ├── SwitchButton.styl │ │ │ ├── TagButton.js │ │ │ └── index.js │ │ ├── StatusBar/ │ │ │ ├── StatusBar.styl │ │ │ └── index.js │ │ ├── TopBar/ │ │ │ ├── TopBar.styl │ │ │ └── index.js │ │ ├── global.styl │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── AwsMobileAnalyticsConfig.js │ │ │ ├── Commander.js │ │ │ ├── ConfigManager.js │ │ │ ├── ThemeManager.js │ │ │ ├── ZoomManager.js │ │ │ ├── dataApi/ │ │ │ │ ├── addStorage.js │ │ │ │ ├── attachmentManagement.js │ │ │ │ ├── copyFile.js │ │ │ │ ├── createFolder.js │ │ │ │ ├── createNote.js │ │ │ │ ├── createNoteFromUrl.js │ │ │ │ ├── createSnippet.js │ │ │ │ ├── deleteFolder.js │ │ │ │ ├── deleteNote.js │ │ │ │ ├── deleteSnippet.js │ │ │ │ ├── exportFolder.js │ │ │ │ ├── exportNote.js │ │ │ │ ├── exportNoteAs.js │ │ │ │ ├── exportStorage.js │ │ │ │ ├── exportTag.js │ │ │ │ ├── fetchSnippet.js │ │ │ │ ├── formatHTML.js │ │ │ │ ├── formatMarkdown.js │ │ │ │ ├── formatPDF.js │ │ │ │ ├── getContentFormatter.js │ │ │ │ ├── getFilename.js │ │ │ │ ├── index.js │ │ │ │ ├── init.js │ │ │ │ ├── migrateFromV5Storage.js │ │ │ │ ├── migrateFromV6Storage.js │ │ │ │ ├── moveNote.js │ │ │ │ ├── removeStorage.js │ │ │ │ ├── renameStorage.js │ │ │ │ ├── reorderFolder.js │ │ │ │ ├── resolveStorageData.js │ │ │ │ ├── resolveStorageNotes.js │ │ │ │ ├── toggleStorage.js │ │ │ │ ├── updateFolder.js │ │ │ │ ├── updateNote.js │ │ │ │ └── updateSnippet.js │ │ │ ├── eventEmitter.js │ │ │ ├── ipcClient.js │ │ │ ├── modal.js │ │ │ ├── notify.js │ │ │ ├── shortcut.js │ │ │ └── shortcutManager.js │ │ ├── modals/ │ │ │ ├── CreateFolderModal.js │ │ │ ├── CreateFolderModal.styl │ │ │ ├── CreateMarkdownFromURLModal.js │ │ │ ├── CreateMarkdownFromURLModal.styl │ │ │ ├── NewNoteModal.js │ │ │ ├── NewNoteModal.styl │ │ │ ├── PreferencesModal/ │ │ │ │ ├── Blog.js │ │ │ │ ├── ConfigTab.styl │ │ │ │ ├── Crowdfunding.js │ │ │ │ ├── Crowdfunding.styl │ │ │ │ ├── ExportTab.js │ │ │ │ ├── FolderItem.js │ │ │ │ ├── FolderItem.styl │ │ │ │ ├── FolderList.js │ │ │ │ ├── FolderList.styl │ │ │ │ ├── HotkeyTab.js │ │ │ │ ├── InfoTab.js │ │ │ │ ├── InfoTab.styl │ │ │ │ ├── PluginsTab.js │ │ │ │ ├── PreferencesModal.styl │ │ │ │ ├── SnippetEditor.js │ │ │ │ ├── SnippetList.js │ │ │ │ ├── SnippetTab.js │ │ │ │ ├── SnippetTab.styl │ │ │ │ ├── StorageItem.js │ │ │ │ ├── StorageItem.styl │ │ │ │ ├── StoragesTab.js │ │ │ │ ├── StoragesTab.styl │ │ │ │ ├── Tab.styl │ │ │ │ ├── UiTab.js │ │ │ │ └── index.js │ │ │ ├── RenameFolderModal.js │ │ │ ├── RenameModal.styl │ │ │ └── RenameTagModal.js │ │ └── store.js │ └── styles/ │ ├── Detail/ │ │ └── TagSelect.styl │ ├── index.styl │ ├── mixins/ │ │ ├── alert.styl │ │ ├── btn.styl │ │ ├── circle.styl │ │ ├── fullsize.styl │ │ ├── input.styl │ │ ├── marked.styl │ │ ├── tooltip.styl │ │ └── util.styl │ ├── shared/ │ │ └── btn.styl │ ├── theme/ │ │ └── dark.styl │ └── vars.styl ├── contributing.md ├── dev-scripts/ │ └── dev.js ├── dictionaries/ │ ├── de_DE/ │ │ ├── LICENSE │ │ ├── de_DE.aff │ │ └── de_DE.dic │ ├── en_GB/ │ │ ├── LICENSE │ │ ├── en_GB.aff │ │ └── en_GB.dic │ └── fr_FR/ │ ├── fr_FR.aff │ └── fr_FR.dic ├── docs/ │ ├── build.md │ ├── code_style.md │ ├── de/ │ │ ├── build.md │ │ └── debug.md │ ├── debug.md │ ├── fr/ │ │ ├── build.md │ │ └── debug.md │ ├── jp/ │ │ ├── build.md │ │ └── debug.md │ ├── ko/ │ │ ├── build.md │ │ └── debug.md │ ├── pt_BR/ │ │ ├── build.md │ │ └── debug.md │ ├── ru/ │ │ ├── build.md │ │ └── debug.md │ ├── zh_CN/ │ │ ├── build.md │ │ └── debug.md │ └── zh_TW/ │ └── build.md ├── extra_scripts/ │ ├── boost/ │ │ └── boostNewLineIndentContinueMarkdownList.js │ └── codemirror/ │ ├── addon/ │ │ ├── edit/ │ │ │ └── closebrackets.js │ │ └── hyperlink/ │ │ └── hyperlink.js │ ├── mode/ │ │ ├── bfm/ │ │ │ ├── bfm.css │ │ │ └── bfm.js │ │ └── gfm/ │ │ └── gfm.js │ └── theme/ │ └── nord.css ├── gruntfile.js ├── index.js ├── lib/ │ ├── ipcServer.js │ ├── main-app.js │ ├── main-menu.js │ ├── main-window.js │ ├── main.development.html │ ├── main.production.html │ └── touchbar-menu.js ├── locales/ │ ├── cs.json │ ├── da.json │ ├── de.json │ ├── en.json │ ├── es-ES.json │ ├── fa.json │ ├── fr.json │ ├── hu.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── no.json │ ├── pl.json │ ├── pt-BR.json │ ├── pt-PT.json │ ├── ru.json │ ├── sq.json │ ├── th.json │ ├── tr.json │ ├── zh-CN.json │ └── zh-TW.json ├── package.json ├── prettier.config ├── readme.md ├── resources/ │ ├── app.icns │ └── dmg.icns ├── snap/ │ ├── gui/ │ │ └── boostnote.desktop │ └── snapcraft.yaml ├── tests/ │ ├── .gitignore │ ├── components/ │ │ ├── TagListItem.snapshot.test.js │ │ └── __snapshots__/ │ │ └── TagListItem.snapshot.test.js.snap │ ├── dataApi/ │ │ ├── addStorage.js │ │ ├── attachmentManagement.test.js │ │ ├── copyFile.test.js │ │ ├── createFolder.test.js │ │ ├── createNote.test.js │ │ ├── createNoteFromUrl.test.js │ │ ├── createSnippet.test.js │ │ ├── deleteFolder.test.js │ │ ├── deleteNote-test.js │ │ ├── deleteSnippet-test.js │ │ ├── exportFolder-test.js │ │ ├── exportStorage-test.js │ │ ├── init.js │ │ ├── migrateFromV6Storage-test.js │ │ ├── moveNote-test.js │ │ ├── removeStorage-test.js │ │ ├── renameStorage-test.js │ │ ├── reorderFolder-test.js │ │ ├── toggleStorage-test.js │ │ ├── updateFolder-test.js │ │ ├── updateNote-test.js │ │ └── updateSnippet-test.js │ ├── date-formatter-test.js │ ├── fixtures/ │ │ ├── TestDummy.js │ │ └── markdowns.js │ ├── helpers/ │ │ ├── setup-browser-env.js │ │ └── setup-electron-mock.js │ ├── jest.js │ └── lib/ │ ├── __snapshots__/ │ │ └── markdown.test.js.snap │ ├── boostnoterc/ │ │ ├── .boostnoterc.all │ │ ├── .boostnoterc.invalid │ │ └── .boostnoterc.valid │ ├── contextMenuBuilder.test.js │ ├── escapeHtmlCharacters.test.js │ ├── find-storage.test.js │ ├── find-title.test.js │ ├── get-todo-status.test.js │ ├── html-text-helper.test.js │ ├── markdown-text-helper.test.js │ ├── markdown-toc-generator.test.js │ ├── markdown.test.js │ ├── normalize-editor-font-family.test.js │ ├── rc-parser.test.js │ ├── search.test.js │ ├── slugify.test.js │ ├── snapshots/ │ │ └── markdown-test.js.md │ ├── spellcheck.test.js │ ├── themeManager.test.js │ └── utils.test.js ├── webpack-production.config.js ├── webpack-skeleton.js └── webpack.config.js
SYMBOL INDEX (982 symbols across 143 files)
FILE: browser/components/CodeEditor.js
function translateHotkey (line 44) | function translateHotkey(hotkey) {
class CodeEditor (line 51) | class CodeEditor extends React.Component {
method constructor (line 52) | constructor(props) {
method handleSearch (line 130) | handleSearch(msg) {
method handleFormatTable (line 164) | handleFormatTable() {
method handleEditorActivity (line 172) | handleEditorActivity() {
method updateDefaultKeyMap (line 182) | updateDefaultKeyMap() {
method updateTableEditorState (line 300) | updateTableEditorState() {
method componentDidMount (line 316) | componentDidMount() {
method getWordBeforeCursor (line 535) | getWordBeforeCursor(line, lineNumber, cursorPosition) {
method quitEditor (line 572) | quitEditor() {
method componentWillUnmount (line 576) | componentWillUnmount() {
method componentDidUpdate (line 597) | componentDidUpdate(prevProps, prevState) {
method getCodeEditorLintConfig (line 752) | getCodeEditorLintConfig() {
method validatorOfMarkdown (line 764) | validatorOfMarkdown(text, updateLinting) {
method setMode (line 804) | setMode(mode) {
method handleChange (line 812) | handleChange(editor, changeObject) {
method linePossibleContainsHeadline (line 871) | linePossibleContainsHeadline(currentLine) {
method incrementLines (line 877) | incrementLines(start, linesAdded, linesRemoved, editor) {
method handleHighlight (line 912) | handleHighlight(editor, changeObject) {
method updateHighlight (line 935) | updateHighlight(editor, changeObject) {
method moveCursorTo (line 966) | moveCursorTo(row, col) {}
method scrollToLine (line 968) | scrollToLine(event, num) {
method focus (line 979) | focus() {
method blur (line 983) | blur() {
method reload (line 987) | reload() {
method setValue (line 1008) | setValue(value) {
method setLineContent (line 1019) | setLineContent(lineNumber, content) {
method handleDropImage (line 1029) | handleDropImage(dropEvent) {
method insertAttachmentMd (line 1040) | insertAttachmentMd(imageMd) {
method autoDetectLanguage (line 1044) | autoDetectLanguage(content) {
method handlePaste (line 1049) | handlePaste(editor, forceSmartPaste) {
method handleScroll (line 1161) | handleScroll(e) {
method handlePasteUrl (line 1167) | handlePasteUrl(editor, pastedTxt) {
method handlePasteHtml (line 1217) | handlePasteHtml(editor, pastedHtml) {
method handlePasteText (line 1222) | handlePasteText(editor, pastedTxt) {
method mapNormalResponse (line 1226) | mapNormalResponse(response, pastedTxt) {
method initialHighlighting (line 1248) | initialHighlighting() {
method restartHighlighting (line 1270) | restartHighlighting() {
method mapImageResponse (line 1275) | mapImageResponse(response, pastedTxt) {
method decodeResponse (line 1288) | decodeResponse(response) {
method extractContentTypeCharset (line 1308) | extractContentTypeCharset(contentType) {
method render (line 1322) | render() {
method createSpellCheckPanel (line 1342) | createSpellCheckPanel() {
FILE: browser/components/ColorPicker.js
class ColorPicker (line 9) | class ColorPicker extends React.Component {
method constructor (line 10) | constructor(props) {
method componentWillReceiveProps (line 21) | componentWillReceiveProps(nextProps) {
method onColorChange (line 25) | onColorChange(color) {
method handleConfirm (line 31) | handleConfirm() {
method render (line 35) | render() {
FILE: browser/components/MarkdownEditor.js
class MarkdownEditor (line 13) | class MarkdownEditor extends React.Component {
method constructor (line 14) | constructor(props) {
method componentDidMount (line 39) | componentDidMount() {
method componentDidUpdate (line 45) | componentDidUpdate() {
method UNSAFE_componentWillReceiveProps (line 49) | UNSAFE_componentWillReceiveProps(props) {
method componentWillUnmount (line 55) | componentWillUnmount() {
method focusEditor (line 61) | focusEditor() {
method queueRendering (line 75) | queueRendering(value) {
method cancelQueue (line 82) | cancelQueue() {
method renderPreview (line 86) | renderPreview(value) {
method setValue (line 92) | setValue(value) {
method handleChange (line 96) | handleChange(e) {
method handleContextMenu (line 101) | handleContextMenu(e) {
method handleBlur (line 126) | handleBlur(e) {
method handleDoubleClick (line 149) | handleDoubleClick(e) {
method handlePreviewMouseDown (line 166) | handlePreviewMouseDown(e) {
method handlePreviewMouseUp (line 170) | handlePreviewMouseUp(e) {
method handleCheckboxClick (line 188) | handleCheckboxClick(e) {
method focus (line 214) | focus() {
method reload (line 230) | reload() {
method handleKeyDown (line 236) | handleKeyDown(e) {
method addMdAroundWord (line 263) | addMdAroundWord(mdElement) {
method addMdAroundSelection (line 277) | addMdAroundSelection(mdElement) {
method handleDropImage (line 283) | handleDropImage(dropEvent) {
method handleKeyUp (line 308) | handleKeyUp(e) {
method handleLockEditor (line 314) | handleLockEditor() {
method render (line 318) | render() {
FILE: browser/components/MarkdownPreview.js
function getSourceLineNumberByElement (line 78) | function getSourceLineNumberByElement(element) {
class MarkdownPreview (line 88) | class MarkdownPreview extends React.Component {
method constructor (line 89) | constructor(props) {
method initMarkdown (line 113) | initMarkdown() {
method handleCheckboxClick (line 122) | handleCheckboxClick(e) {
method handleScroll (line 126) | handleScroll(e) {
method handleContextMenu (line 132) | handleContextMenu(event) {
method handleDoubleClick (line 142) | handleDoubleClick(e) {
method handleMouseDown (line 146) | handleMouseDown(e) {
method handleMouseUp (line 176) | handleMouseUp(e) {
method handleSaveAsText (line 184) | handleSaveAsText() {
method handleSaveAsMd (line 188) | handleSaveAsMd() {
method handleSaveAsHtml (line 192) | handleSaveAsHtml() {
method handleSaveAsPdf (line 196) | handleSaveAsPdf() {
method handlePrint (line 200) | handlePrint() {
method exportAsDocument (line 204) | exportAsDocument(fileType, contentFormatter) {
method fixDecodedURI (line 238) | fixDecodedURI(node) {
method getScrollBarStyle (line 250) | getScrollBarStyle() {
method componentDidMount (line 258) | componentDidMount() {
method componentWillUnmount (line 316) | componentWillUnmount() {
method componentDidUpdate (line 358) | componentDidUpdate(prevProps) {
method applyStyle (line 400) | applyStyle() {
method rewriteIframe (line 431) | rewriteIframe() {
method setImgOnClickEventHelper (line 660) | setImgOnClickEventHelper(img, rect) {
method handleResize (line 731) | handleResize() {
method focus (line 740) | focus() {
method getWindow (line 744) | getWindow() {
method scrollToLine (line 752) | scrollToLine(targetLine) {
method scrollTo (line 774) | scrollTo(x, y) {
method preventImageDroppedHandler (line 778) | preventImageDroppedHandler(e) {
method notify (line 783) | notify(title, options) {
method handleLinkClick (line 794) | handleLinkClick(e) {
method openExternal (line 867) | openExternal(href) {
method render (line 878) | render() {
FILE: browser/components/MarkdownSplitEditor.js
class MarkdownSplitEditor (line 10) | class MarkdownSplitEditor extends React.Component {
method constructor (line 11) | constructor(props) {
method componentDidUpdate (line 24) | componentDidUpdate(prevProps) {
method handleCursorActivity (line 33) | handleCursorActivity(editor) {
method setValue (line 90) | setValue(value) {
method handleOnChange (line 94) | handleOnChange(e) {
method handleEditorScroll (line 99) | handleEditorScroll(e) {
method handlePreviewScroll (line 164) | handlePreviewScroll(e) {
method handleCheckboxClick (line 221) | handleCheckboxClick(e) {
method handleMouseMove (line 247) | handleMouseMove(e) {
method handleMouseUp (line 289) | handleMouseUp(e) {
method handleMouseDown (line 296) | handleMouseDown(e) {
method scrollTo (line 303) | scrollTo(from, to, scroller) {
method render (line 332) | render() {
FILE: browser/components/RealtimeNotification.js
class RealtimeNotification (line 8) | class RealtimeNotification extends React.Component {
method constructor (line 9) | constructor(props) {
method componentDidMount (line 17) | componentDidMount() {
method fetchNotifications (line 21) | fetchNotifications() {
method handleLinkClick (line 33) | handleLinkClick(e) {
method render (line 38) | render() {
FILE: browser/components/SnippetTab.js
class SnippetTab (line 7) | class SnippetTab extends React.Component {
method constructor (line 8) | constructor(props) {
method componentWillUpdate (line 17) | componentWillUpdate(nextProps) {
method handleClick (line 25) | handleClick(e) {
method handleContextMenu (line 29) | handleContextMenu(e) {
method handleRenameClick (line 38) | handleRenameClick(e) {
method handleNameInputBlur (line 42) | handleNameInputBlur(e) {
method handleNameInputChange (line 46) | handleNameInputChange(e) {
method handleNameInputKeyDown (line 52) | handleNameInputKeyDown(e) {
method handleRename (line 66) | handleRename() {
method handleDeleteButtonClick (line 79) | handleDeleteButtonClick(e) {
method startRenaming (line 83) | startRenaming() {
method handleDragStart (line 95) | handleDragStart(e) {
method handleDrop (line 100) | handleDrop(e) {
method render (line 104) | render() {
FILE: browser/components/render/MermaidRender.js
function getRandomInt (line 10) | function getRandomInt(min, max) {
function getId (line 14) | function getId() {
function render (line 23) | function render(element, content, theme, enableHTMLLabel) {
FILE: browser/lib/Languages.js
method getLocales (line 81) | getLocales() {
method getLanguages (line 87) | getLanguages() {
FILE: browser/lib/Mutable.js
class MutableMap (line 1) | class MutableMap {
method constructor (line 2) | constructor(iterable) {
method get (line 12) | get(...args) {
method set (line 16) | set(...args) {
method delete (line 20) | delete(...args) {
method has (line 24) | has(...args) {
method clear (line 28) | clear(...args) {
method forEach (line 32) | forEach(...args) {
method map (line 40) | map(cb) {
method toJS (line 48) | toJS() {
method [Symbol.iterator] (line 36) | [Symbol.iterator]() {
class MutableSet (line 60) | class MutableSet {
method constructor (line 61) | constructor(iterable) {
method add (line 71) | add(...args) {
method delete (line 75) | delete(...args) {
method forEach (line 79) | forEach(...args) {
method map (line 87) | map(cb) {
method toJS (line 96) | toJS() {
method [Symbol.iterator] (line 83) | [Symbol.iterator]() {
FILE: browser/lib/RcParser.js
constant BOOSTNOTERC (line 4) | const BOOSTNOTERC = '.boostnoterc'
function parse (line 8) | function parse(boostnotercPath = _boostnotercPath) {
FILE: browser/lib/SnippetManager.js
class SnippetManager (line 5) | class SnippetManager {
method constructor (line 6) | constructor() {
method init (line 22) | init() {
method assignSnippets (line 41) | assignSnippets(snippets) {
method expandSnippet (line 45) | expandSnippet(wordBeforeCursor, cursor, cm) {
FILE: browser/lib/TextEditorInterface.js
class TextEditorInterface (line 3) | class TextEditorInterface {
method constructor (line 4) | constructor(editor) {
method getCursorPosition (line 10) | getCursorPosition() {
method setCursorPosition (line 15) | setCursorPosition(pos) {
method setSelectionRange (line 22) | setSelectionRange(range) {
method getLastRow (line 29) | getLastRow() {
method acceptsTableEdit (line 33) | acceptsTableEdit() {
method getLine (line 37) | getLine(row) {
method insertLine (line 41) | insertLine(row, line) {
method deleteLine (line 59) | deleteLine(row) {
method replaceLines (line 83) | replaceLines(startRow, endRow, lines) {
method transact (line 101) | transact(func) {
FILE: browser/lib/confirmDeleteNote.js
function confirmDeleteNote (line 6) | function confirmDeleteNote(confirmDeletion, permanent) {
FILE: browser/lib/consts.js
constant CODEMIRROR_THEME_PATH (line 6) | const CODEMIRROR_THEME_PATH = 'node_modules/codemirror/theme'
constant CODEMIRROR_EXTRA_THEME_PATH (line 7) | const CODEMIRROR_EXTRA_THEME_PATH = 'extra_scripts/codemirror/theme'
FILE: browser/lib/context.js
function popup (line 4) | function popup(templates) {
FILE: browser/lib/convertModeName.js
function convertModeName (line 1) | function convertModeName(name) {
FILE: browser/lib/date-formatter.js
function formatDate (line 11) | function formatDate(date) {
FILE: browser/lib/findNoteTitle.js
function findNoteTitle (line 1) | function findNoteTitle(
FILE: browser/lib/findStorage.js
function findStorage (line 3) | function findStorage(storageKey) {
FILE: browser/lib/getTodoStatus.js
function getTodoStatus (line 1) | function getTodoStatus(content) {
function getTodoPercentageOfCompleted (line 22) | function getTodoPercentageOfCompleted(content) {
FILE: browser/lib/htmlTextHelper.js
function decodeEntities (line 10) | function decodeEntities(text) {
function encodeEntities (line 27) | function encodeEntities(text) {
FILE: browser/lib/markdown-it-deflist.js
function skipMarker (line 8) | function skipMarker(state, line) {
function markTightParagraphs (line 32) | function markTightParagraphs(state, idx) {
function deflist (line 49) | function deflist(state, startLine, endLine, silent) {
FILE: browser/lib/markdown-it-fence.js
function fence (line 6) | function fence(state, startLine, endLine, silent) {
FILE: browser/lib/markdown-it-frontmatter.js
function frontmatter (line 4) | function frontmatter(state, startLine, endLine, silent) {
FILE: browser/lib/markdown-it-sanitize-html.js
function sanitizeInline (line 43) | function sanitizeInline(html, options) {
function naughtyHRef (line 110) | function naughtyHRef(href, options) {
function naughtyIFrame (line 133) | function naughtyIFrame(src, options) {
FILE: browser/lib/markdown-toc-generator.js
function uniqueSlug (line 15) | function uniqueSlug(slug, slugs, opts) {
function linkify (line 23) | function linkify(token) {
constant TOC_MARKER_START (line 28) | const TOC_MARKER_START = '<!-- toc -->'
constant TOC_MARKER_END (line 29) | const TOC_MARKER_END = '<!-- tocstop -->'
function generateInEditor (line 39) | function generateInEditor(editor) {
function tocExistsInEditor (line 62) | function tocExistsInEditor(editor) {
function generate (line 71) | function generate(markdownText) {
function wrapTocWithEol (line 91) | function wrapTocWithEol(toc, editor) {
FILE: browser/lib/markdown.js
function createGutter (line 13) | function createGutter(str, firstLineNumber) {
class Markdown (line 25) | class Markdown {
method constructor (line 26) | constructor(options = {}) {
method render (line 520) | render(content) {
FILE: browser/lib/markdownTextHelper.js
function strip (line 9) | function strip(input) {
FILE: browser/lib/newNote.js
function createMarkdownNote (line 7) | function createMarkdownNote(
function createSnippetNote (line 53) | function createSnippetNote(
FILE: browser/lib/normalizeEditorFontFamily.js
function normalizeEditorFontFamily (line 4) | function normalizeEditorFontFamily(fontFamily) {
FILE: browser/lib/search.js
function searchFromNotes (line 3) | function searchFromNotes(notes, search) {
function findByWordOrTag (line 16) | function findByWordOrTag(notes, block) {
FILE: browser/lib/spellcheck.js
constant CSS_ERROR_CLASS (line 7) | const CSS_ERROR_CLASS = 'codeEditor-typo'
constant SPELLCHECK_DISABLED (line 8) | const SPELLCHECK_DISABLED = 'NONE'
constant DICTIONARY_PATH (line 9) | const DICTIONARY_PATH = '../dictionaries'
constant MILLISECONDS_TILL_LIVECHECK (line 10) | const MILLISECONDS_TILL_LIVECHECK = 500
function getAvailableDictionaries (line 15) | function getAvailableDictionaries() {
function setDictionaryForTestsOnly (line 27) | function setDictionaryForTestsOnly(newDictionary) {
function setLanguage (line 37) | function setLanguage(editor, lang) {
function checkWholeDocument (line 62) | function checkWholeDocument(editor) {
function checkMultiLineRange (line 77) | function checkMultiLineRange(editor, from, to) {
function checkWord (line 115) | function checkWord(editor, wordRange) {
function checkChangeRange (line 133) | function checkChangeRange(editor, fromChangeObject, toChangeObject) {
function saveLiveSpellCheckFrom (line 187) | function saveLiveSpellCheckFrom(changeObject) {
function handleChange (line 213) | function handleChange(editor, changeObject) {
function getSpellingSuggestion (line 227) | function getSpellingSuggestion(word) {
function getCSSClassName (line 237) | function getCSSClassName() {
FILE: browser/lib/utils.js
function lastFindInArray (line 1) | function lastFindInArray(array, callback) {
function escapeHtmlCharacters (line 9) | function escapeHtmlCharacters(
function isObjectEqual (line 118) | function isObjectEqual(a, b) {
function isMarkdownTitleURL (line 135) | function isMarkdownTitleURL(str) {
function humanFileSize (line 141) | function humanFileSize(bytes) {
FILE: browser/lib/wakatime-plugin.js
function sendWakatimeHeartBeat (line 6) | function sendWakatimeHeartBeat(
FILE: browser/main/Detail/FolderSelect.js
class FolderSelect (line 8) | class FolderSelect extends React.Component {
method constructor (line 9) | constructor(props) {
method componentDidMount (line 19) | componentDidMount() {
method componentDidUpdate (line 23) | componentDidUpdate() {
method handleClick (line 27) | handleClick(e) {
method handleFocus (line 39) | handleFocus(e) {
method handleBlur (line 47) | handleBlur(e) {
method handleKeyDown (line 55) | handleKeyDown(e) {
method handleSearchInputBlur (line 97) | handleSearchInputBlur(e) {
method handleSearchInputChange (line 105) | handleSearchInputChange(e) {
method handleSearchInputKeyDown (line 123) | handleSearchInputKeyDown(e) {
method nextOption (line 150) | nextOption() {
method previousOption (line 162) | previousOption() {
method selectOption (line 174) | selectOption() {
method handleOptionClick (line 192) | handleOptionClick(storageKey, folderKey) {
method setValue (line 207) | setValue(value) {
method render (line 212) | render() {
FILE: browser/main/Detail/FromUrlButton.js
class FromUrlButton (line 8) | class FromUrlButton extends React.Component {
method constructor (line 9) | constructor(props) {
method handleMouseDown (line 17) | handleMouseDown(e) {
method handleMouseUp (line 23) | handleMouseUp(e) {
method handleMouseLeave (line 29) | handleMouseLeave(e) {
method render (line 35) | render() {
FILE: browser/main/Detail/FullscreenButton.js
constant OSX (line 7) | const OSX = global.process.platform === 'darwin'
FILE: browser/main/Detail/InfoPanel.js
class InfoPanel (line 8) | class InfoPanel extends React.Component {
method copyNoteLink (line 9) | copyNoteLink() {
method render (line 15) | render() {
FILE: browser/main/Detail/MarkdownNoteDetail.js
class MarkdownNoteDetail (line 37) | class MarkdownNoteDetail extends React.Component {
method constructor (line 38) | constructor(props) {
method focus (line 67) | focus() {
method componentDidMount (line 71) | componentDidMount() {
method UNSAFE_componentWillReceiveProps (line 84) | UNSAFE_componentWillReceiveProps(nextProps) {
method componentWillUnmount (line 116) | componentWillUnmount() {
method handleUpdateTag (line 123) | handleUpdateTag() {
method handleUpdateContent (line 129) | handleUpdateContent() {
method updateNote (line 145) | updateNote(note) {
method save (line 152) | save() {
method saveNow (line 159) | saveNow() {
method handleFolderChange (line 173) | handleFolderChange(e) {
method handleStarButtonClick (line 211) | handleStarButtonClick(e) {
method exportAsFile (line 228) | exportAsFile() {}
method exportAsMd (line 230) | exportAsMd() {
method exportAsTxt (line 234) | exportAsTxt() {
method exportAsHtml (line 238) | exportAsHtml() {
method exportAsPdf (line 242) | exportAsPdf() {
method handleKeyDown (line 246) | handleKeyDown(e) {
method handleTrashButtonClick (line 279) | handleTrashButtonClick(e) {
method handleUndoButtonClick (line 319) | handleUndoButtonClick(e) {
method handleFullScreenButton (line 336) | handleFullScreenButton(e) {
method handleLockButtonMouseDown (line 340) | handleLockButtonMouseDown(e) {
method getToggleLockButton (line 347) | getToggleLockButton() {
method handleDeleteKeyDown (line 353) | handleDeleteKeyDown(e) {
method handleToggleLockButton (line 357) | handleToggleLockButton(event, noteStatus) {
method handleGenerateToc (line 366) | handleGenerateToc() {
method handleFocus (line 371) | handleFocus(e) {
method handleInfoButtonClick (line 375) | handleInfoButtonClick(e) {
method print (line 382) | print(e) {
method handleSwitchMode (line 386) | handleSwitchMode(type) {
method handleSwitchStackDirection (line 399) | handleSwitchStackDirection() {
method handleSwitchDirection (line 411) | handleSwitchDirection() {
method handleDeleteNote (line 420) | handleDeleteNote() {
method handleClearTodo (line 424) | handleClearTodo() {
method getNote (line 445) | getNote() {
method renderEditor (line 449) | renderEditor() {
method render (line 488) | render() {
FILE: browser/main/Detail/SnippetNoteDetail.js
class SnippetNoteDetail (line 39) | class SnippetNoteDetail extends React.Component {
method constructor (line 40) | constructor(props) {
method componentDidMount (line 66) | componentDidMount() {
method componentWillReceiveProps (line 81) | componentWillReceiveProps(nextProps) {
method componentWillUnmount (line 116) | componentWillUnmount() {
method handleGenerateToc (line 121) | handleGenerateToc() {
method handleChange (line 130) | handleChange(e) {
method save (line 148) | save() {
method saveNow (line 155) | saveNow() {
method handleFolderChange (line 169) | handleFolderChange(e) {
method handleStarButtonClick (line 207) | handleStarButtonClick(e) {
method exportAsFile (line 224) | exportAsFile() {}
method handleTrashButtonClick (line 226) | handleTrashButtonClick(e) {
method handleUndoButtonClick (line 266) | handleUndoButtonClick(e) {
method handleFullScreenButton (line 282) | handleFullScreenButton(e) {
method handleTabMoveLeftButtonClick (line 286) | handleTabMoveLeftButtonClick(e) {
method handleTabMoveRightButtonClick (line 313) | handleTabMoveRightButtonClick(e) {
method handleTabPlusButtonClick (line 337) | handleTabPlusButtonClick(e) {
method handleTabButtonClick (line 341) | handleTabButtonClick(e, index) {
method handleTabDragStart (line 347) | handleTabDragStart(e, index) {
method handleTabDrop (line 351) | handleTabDrop(e, index) {
method handleTabDeleteButtonClick (line 368) | handleTabDeleteButtonClick(e, index) {
method deleteSnippetByIndex (line 386) | deleteSnippetByIndex(index) {
method renameSnippetByIndex (line 416) | renameSnippetByIndex(index, name) {
method handleModeOptionClick (line 441) | handleModeOptionClick(index, name) {
method handleCodeChange (line 464) | handleCodeChange(index) {
method handleKeyDown (line 484) | handleKeyDown(e) {
method handleModeButtonClick (line 539) | handleModeButtonClick(e, index) {
method handleIndentTypeButtonClick (line 554) | handleIndentTypeButtonClick(e) {
method handleIndentSizeButtonClick (line 567) | handleIndentSizeButtonClick(e) {
method handleWrapLineButtonClick (line 584) | handleWrapLineButtonClick(e) {
method handleIndentSizeItemClick (line 597) | handleIndentSizeItemClick(e, indentSize) {
method handleIndentTypeItemClick (line 613) | handleIndentTypeItemClick(e, indentType) {
method handleWrapLineItemClick (line 629) | handleWrapLineItemClick(e, lineWrapping) {
method focus (line 645) | focus() {
method moveToTab (line 649) | moveToTab(tab) {
method getArrowsState (line 684) | getArrowsState() {
method addSnippet (line 696) | addSnippet() {
method jumpNextTab (line 737) | jumpNextTab() {
method jumpPrevTab (line 748) | jumpPrevTab() {
method focusEditor (line 761) | focusEditor() {
method handleInfoButtonClick (line 765) | handleInfoButtonClick(e) {
method showWarning (line 772) | showWarning(e, msg) {
method render (line 792) | render() {
FILE: browser/main/Detail/StarButton.js
class StarButton (line 8) | class StarButton extends React.Component {
method constructor (line 9) | constructor(props) {
method handleMouseDown (line 17) | handleMouseDown(e) {
method handleMouseUp (line 23) | handleMouseUp(e) {
method handleMouseLeave (line 29) | handleMouseLeave(e) {
method render (line 35) | render() {
FILE: browser/main/Detail/TagSelect.js
class TagSelect (line 13) | class TagSelect extends React.Component {
method constructor (line 14) | constructor(props) {
method addNewTag (line 36) | addNewTag(newTag) {
method buildSuggestions (line 73) | buildSuggestions() {
method componentDidMount (line 86) | componentDidMount() {
method componentDidUpdate (line 95) | componentDidUpdate() {
method componentWillUnmount (line 99) | componentWillUnmount() {
method handleAddTag (line 104) | handleAddTag() {
method handleRenameTag (line 108) | handleRenameTag(event, tagChange) {
method handleTagLabelClick (line 118) | handleTagLabelClick(tag) {
method handleTagRemoveButtonClick (line 126) | handleTagRemoveButtonClick(tag) {
method onInputBlur (line 132) | onInputBlur(e) {
method onInputChange (line 136) | onInputChange(e, { newValue, method }) {
method onInputKeyDown (line 142) | onInputKeyDown(e) {
method onSuggestionsClearRequested (line 158) | onSuggestionsClearRequested() {
method onSuggestionsFetchRequested (line 164) | onSuggestionsFetchRequested({ value }) {
method onSuggestionSelected (line 177) | onSuggestionSelected(event, { suggestion, suggestionValue }) {
method removeLastTag (line 181) | removeLastTag() {
method removeTagByCallback (line 187) | removeTagByCallback(callback, tag = null) {
method reset (line 198) | reset() {
method submitNewTag (line 206) | submitNewTag() {
method render (line 210) | render() {
FILE: browser/main/Detail/index.js
constant OSX (line 15) | const OSX = global.process.platform === 'darwin'
class Detail (line 17) | class Detail extends React.Component {
method constructor (line 18) | constructor(props) {
method componentDidMount (line 29) | componentDidMount() {
method componentWillUnmount (line 34) | componentWillUnmount() {
method render (line 39) | render() {
FILE: browser/main/Main.js
class Main (line 27) | class Main extends React.Component {
method constructor (line 28) | constructor(props) {
method getChildContext (line 50) | getChildContext() {
method init (line 59) | init() {
method componentDidMount (line 149) | componentDidMount() {
method componentWillUnmount (line 191) | componentWillUnmount() {
method changeRoutePush (line 201) | changeRoutePush(event, destination) {
method toggleMenuBarVisible (line 206) | toggleMenuBarVisible() {
method handleLeftSlideMouseDown (line 215) | handleLeftSlideMouseDown(e) {
method handleRightSlideMouseDown (line 222) | handleRightSlideMouseDown(e) {
method handleMouseUp (line 229) | handleMouseUp(e) {
method handleMouseMove (line 269) | handleMouseMove(e) {
method handleFullScreenButton (line 295) | handleFullScreenButton(e) {
method hideLeftLists (line 309) | hideLeftLists(noteDetail, noteList, mainBody) {
method showLeftLists (line 317) | showLeftLists(noteDetail, noteList, mainBody) {
method render (line 323) | render() {
FILE: browser/main/NewNoteButton/index.js
constant OSX (line 15) | const OSX = window.process.platform === 'darwin'
class NewNoteButton (line 17) | class NewNoteButton extends React.Component {
method constructor (line 18) | constructor(props) {
method componentDidMount (line 26) | componentDidMount() {
method componentWillUnmount (line 30) | componentWillUnmount() {
method handleNewNoteButtonClick (line 34) | handleNewNoteButtonClick(e) {
method resolveTargetFolder (line 72) | resolveTargetFolder() {
method showMessageBox (line 99) | showMessageBox(message) {
method render (line 107) | render() {
FILE: browser/main/NoteList/index.js
constant WP_POST_PATH (line 29) | const WP_POST_PATH = '/wp/v2/posts'
function sortByCreatedAt (line 33) | function sortByCreatedAt(a, b) {
function sortByAlphabetical (line 37) | function sortByAlphabetical(a, b) {
function sortByUpdatedAt (line 57) | function sortByUpdatedAt(a, b) {
function findNoteByKey (line 61) | function findNoteByKey(notes, noteKey) {
function findNotesByKeys (line 65) | function findNotesByKeys(notes, noteKeys) {
function getNoteKey (line 69) | function getNoteKey(note) {
class NoteList (line 73) | class NoteList extends React.Component {
method constructor (line 74) | constructor(props) {
method componentDidMount (line 116) | componentDidMount() {
method componentWillReceiveProps (line 128) | componentWillReceiveProps(nextProps) {
method resetScroll (line 134) | resetScroll() {
method componentWillUnmount (line 138) | componentWillUnmount() {
method componentDidUpdate (line 150) | componentDidUpdate(prevProps) {
method focusNote (line 213) | focusNote(selectedNoteKeys, noteKey, pathname) {
method getNoteKeyFromTargetIndex (line 230) | getNoteKeyFromTargetIndex(targetIndex) {
method selectPriorNote (line 236) | selectPriorNote() {
method selectNextNote (line 266) | selectNextNote() {
method jumpNoteByHashHandler (line 303) | jumpNoteByHashHandler(event, noteHash) {
method handleNoteListKeyDown (line 327) | handleNoteListKeyDown(e) {
method handleNoteListKeyUp (line 367) | handleNoteListKeyUp(e) {
method handleNoteListBlur (line 377) | handleNoteListBlur() {
method getNotes (line 384) | getNotes() {
method getContextNotes (line 438) | getContextNotes() {
method sortByPin (line 459) | sortByPin(unorderedNotes) {
method getNoteIndexByKey (line 474) | getNoteIndexByKey(noteKey) {
method handleNoteClick (line 482) | handleNoteClick(e, uniqueKey) {
method handleSortByChange (line 552) | handleSortByChange(e) {
method handleListStyleButtonClick (line 571) | handleListStyleButtonClick(e, style) {
method handleListDirectionButtonClick (line 585) | handleListDirectionButtonClick(e, direction) {
method alertIfSnippet (line 599) | alertIfSnippet(msg) {
method handleDragStart (line 622) | handleDragStart(e, note) {
method handleExportClick (line 638) | handleExportClick(e, note, fileType) {
method handleNoteContextMenu (line 670) | handleNoteContextMenu(e, uniqueKey) {
method updateSelectedNotes (line 784) | updateSelectedNotes(updateFunc, cleanSelection = true) {
method pinToTop (line 816) | pinToTop() {
method restoreNote (line 823) | restoreNote() {
method deleteNote (line 830) | deleteNote() {
method cloneNote (line 889) | cloneNote() {
method copyNoteLink (line 936) | copyNoteLink(note) {
method navigate (line 941) | navigate(sender, pathname) {
method save (line 953) | save(note) {
method publishMarkdown (line 963) | publishMarkdown() {
method publishMarkdownNow (line 972) | publishMarkdownNow() {
method confirmPublishError (line 1032) | confirmPublishError() {
method confirmPublish (line 1044) | confirmPublish(note) {
method openBlog (line 1057) | openBlog(note) {
method importFromFile (line 1062) | importFromFile() {
method handleDrop (line 1073) | handleDrop(e) {
method addNotesFromFiles (line 1083) | addNotesFromFiles(filepaths) {
method getTargetIndex (line 1129) | getTargetIndex() {
method resolveTargetFolder (line 1138) | resolveTargetFolder() {
method showMessageBox (line 1164) | showMessageBox(message) {
method getNoteStorage (line 1172) | getNoteStorage(note) {
method getNoteFolder (line 1177) | getNoteFolder(note) {
method getViewType (line 1185) | getViewType() {
method render (line 1195) | render() {
FILE: browser/main/SideNav/StorageItem.js
class StorageItem (line 21) | class StorageItem extends React.Component {
method constructor (line 22) | constructor(props) {
method handleHeaderContextMenu (line 33) | handleHeaderContextMenu(e) {
method handleUnlinkStorageClick (line 73) | handleUnlinkStorageClick(e) {
method handleExportStorageClick (line 99) | handleExportStorageClick(e, fileType) {
method handleToggleButtonClick (line 134) | handleToggleButtonClick(e) {
method handleAddFolderButtonClick (line 149) | handleAddFolderButtonClick(e) {
method handleHeaderInfoClick (line 157) | handleHeaderInfoClick(e) {
method handleFolderButtonClick (line 162) | handleFolderButtonClick(folderKey) {
method handleFolderMouseEnter (line 169) | handleFolderMouseEnter(e, tooltipRef, isFolded) {
method handleFolderButtonContextMenu (line 178) | handleFolderButtonContextMenu(e, folder) {
method handleRenameFolderClick (line 218) | handleRenameFolderClick(e, folder) {
method handleExportFolderClick (line 226) | handleExportFolderClick(e, folder, fileType) {
method handleFolderDeleteClick (line 262) | handleFolderDeleteClick(e, folder) {
method handleDragEnter (line 284) | handleDragEnter(e, key) {
method handleDragLeave (line 294) | handleDragLeave(e) {
method dropNote (line 304) | dropNote(storage, folder, dispatch, location, noteData) {
method handleDrop (line 329) | handleDrop(e, storage, folder, dispatch, location) {
method render (line 340) | render() {
FILE: browser/main/SideNav/index.js
function matchActiveTags (line 31) | function matchActiveTags(tags, activeTags) {
class SideNav (line 35) | class SideNav extends React.Component {
method constructor (line 37) | constructor(props) {
method componentDidMount (line 59) | componentDidMount() {
method componentWillUnmount (line 63) | componentWillUnmount() {
method deleteTag (line 67) | deleteTag(tag) {
method handleMenuButtonClick (line 122) | handleMenuButtonClick(e) {
method handleSearchButtonClick (line 126) | handleSearchButtonClick(e) {
method handleSearchInputClear (line 134) | handleSearchInputClear(e) {
method handleSearchInputChange (line 140) | handleSearchInputChange(e) {
method handleHomeButtonClick (line 146) | handleHomeButtonClick(e) {
method handleStarredButtonClick (line 151) | handleStarredButtonClick(e) {
method handleTagContextMenu (line 156) | handleTagContextMenu(e, tag) {
method handleExportTagClick (line 204) | handleExportTagClick(e, tag, fileType) {
method dismissColorPicker (line 233) | dismissColorPicker() {
method displayColorPicker (line 241) | displayColorPicker(tagName, rect) {
method handleRenameTagClick (line 253) | handleRenameTagClick(tagName) {
method handleColorPickerConfirm (line 263) | handleColorPickerConfirm(color) {
method handleColorPickerReset (line 284) | handleColorPickerReset() {
method handleToggleButtonClick (line 305) | handleToggleButtonClick(e) {
method handleTrashedButtonClick (line 322) | handleTrashedButtonClick(e) {
method handleSwitchFoldersButtonClick (line 327) | handleSwitchFoldersButtonClick() {
method handleSwitchTagsButtonClick (line 332) | handleSwitchTagsButtonClick() {
method onSortEnd (line 337) | onSortEnd(storage) {
method SideNavComponent (line 346) | SideNavComponent(isFolded) {
method tagListComponent (line 452) | tagListComponent() {
method getRelatedTags (line 507) | getRelatedTags(activeTags, noteMap) {
method getTagActive (line 519) | getTagActive(path, tag) {
method getActiveTags (line 523) | getActiveTags(path) {
method handleClickTagListItem (line 529) | handleClickTagListItem(name) {
method handleSortTagsByChange (line 534) | handleSortTagsByChange(e) {
method handleClickNarrowToTag (line 548) | handleClickNarrowToTag(tag) {
method emptyTrash (line 560) | emptyTrash(entries) {
method handleFilterButtonContextMenu (line 578) | handleFilterButtonContextMenu(event) {
method render (line 591) | render() {
FILE: browser/main/StatusBar/index.js
class StatusBar (line 30) | class StatusBar extends React.Component {
method constructor (line 31) | constructor(props) {
method componentDidMount (line 38) | componentDidMount() {
method componentWillUnmount (line 44) | componentWillUnmount() {
method updateApp (line 50) | updateApp() {
method handleZoomButtonClick (line 63) | handleZoomButtonClick(e) {
method handleZoomMenuItemClick (line 76) | handleZoomMenuItemClick(zoomFactor) {
method handleZoomInMenuItem (line 85) | handleZoomInMenuItem() {
method handleZoomOutMenuItem (line 90) | handleZoomOutMenuItem() {
method handleZoomResetMenuItem (line 95) | handleZoomResetMenuItem() {
method render (line 99) | render() {
FILE: browser/main/TopBar/index.js
class TopBar (line 13) | class TopBar extends React.Component {
method constructor (line 14) | constructor(props) {
method componentDidMount (line 51) | componentDidMount() {
method componentWillUnmount (line 66) | componentWillUnmount() {
method handleSearchClearButton (line 71) | handleSearchClearButton(e) {
method handleKeyDown (line 83) | handleKeyDown(e) {
method handleSearchChange (line 107) | handleSearchChange(e) {
method handleSearchFocus (line 112) | handleSearchFocus(e) {
method handleSearchBlur (line 118) | handleSearchBlur(e) {
method handleOnSearchFocus (line 137) | handleOnSearchFocus() {
method handleCodeInit (line 146) | handleCodeInit() {
method render (line 150) | render() {
FILE: browser/main/index.js
function notify (line 94) | function notify(...args) {
function updateApp (line 98) | function updateApp() {
function downloadUpdate (line 111) | function downloadUpdate() {
FILE: browser/main/lib/AwsMobileAnalyticsConfig.js
constant AWS (line 1) | const AWS = require('aws-sdk')
constant AMA (line 2) | const AMA = require('aws-sdk-mobile-analytics')
function convertPlatformName (line 25) | function convertPlatformName(platformName) {
function getSendEventCond (line 37) | function getSendEventCond() {
function initAwsMobileAnalytics (line 44) | function initAwsMobileAnalytics() {
function recordDynamicCustomEvent (line 54) | function recordDynamicCustomEvent(type, options = {}) {
function recordStaticCustomEvent (line 65) | function recordStaticCustomEvent() {
FILE: browser/main/lib/Commander.js
function bind (line 3) | function bind(name, el) {
function release (line 10) | function release(el) {
function fire (line 14) | function fire(command) {
FILE: browser/main/lib/ConfigManager.js
constant OSX (line 6) | const OSX = global.process.platform === 'darwin'
constant DEFAULT_MARKDOWN_LINT_CONFIG (line 15) | const DEFAULT_MARKDOWN_LINT_CONFIG = `{
constant DEFAULT_CSS_CONFIG (line 19) | const DEFAULT_CSS_CONFIG = `
constant DEFAULT_CONFIG (line 35) | const DEFAULT_CONFIG = {
function validate (line 158) | function validate(config) {
function _save (line 167) | function _save(config) {
function get (line 171) | function get() {
function set (line 220) | function set(updates) {
function assignConfigValues (line 263) | function assignConfigValues(originalConfig, rcConfig) {
function rewriteHotkey (line 307) | function rewriteHotkey(config) {
FILE: browser/main/lib/ZoomManager.js
function _init (line 8) | function _init() {
function _saveZoom (line 12) | function _saveZoom(zoomFactor) {
function setZoom (line 16) | function setZoom(zoomFactor, noSave = false) {
function getZoom (line 21) | function getZoom() {
FILE: browser/main/lib/dataApi/addStorage.js
constant CSON (line 7) | const CSON = require('@rokt33r/season')
function addStorage (line 19) | function addStorage(input) {
FILE: browser/main/lib/dataApi/attachmentManagement.js
constant STORAGE_FOLDER_PLACEHOLDER (line 13) | const STORAGE_FOLDER_PLACEHOLDER = ':storage'
constant DESTINATION_FOLDER (line 14) | const DESTINATION_FOLDER = 'attachments'
constant PATH_SEPARATORS (line 15) | const PATH_SEPARATORS =
function getImage (line 23) | function getImage(file) {
function getOrientation (line 59) | function getOrientation(file) {
function fixRotate (line 112) | function fixRotate(file) {
function copyAttachment (line 166) | function copyAttachment(
function createAttachmentDestinationFolder (line 238) | function createAttachmentDestinationFolder(destinationStoragePath, noteK...
function migrateAttachments (line 259) | function migrateAttachments(markdownContent, storagePath, noteKey) {
function fixLocalURLS (line 295) | function fixLocalURLS(renderedHTML, storagePath) {
function generateAttachmentMarkdown (line 329) | function generateAttachmentMarkdown(fileName, path, showPreview) {
function handleAttachmentDrop (line 341) | function handleAttachmentDrop(codeEditor, storageKey, noteKey, dropEvent) {
function handlePasteImageEvent (line 459) | function handlePasteImageEvent(
function handlePasteNativeImage (line 520) | function handlePasteNativeImage(codeEditor, storageKey, noteKey, image) {
function getAttachmentsInMarkdownContent (line 568) | function getAttachmentsInMarkdownContent(markdownContent) {
function getAbsolutePathsOfAttachmentsInContent (line 595) | function getAbsolutePathsOfAttachmentsInContent(markdownContent, storage...
function importAttachments (line 616) | function importAttachments(markDownContent, filepath, storageKey, noteKe...
function moveAttachments (line 675) | function moveAttachments(oldPath, newPath, noteKey, newNoteKey, noteCont...
function replaceNoteKeyWithNewNoteKey (line 691) | function replaceNoteKeyWithNewNoteKey(noteContent, oldNoteKey, newNoteKe...
function replaceStorageReferences (line 715) | function replaceStorageReferences(input, noteKey, destinationFolder) {
function deleteAttachmentFolder (line 750) | function deleteAttachmentFolder(storageKey, noteKey) {
function deleteAttachmentsNotPresentInNote (line 766) | function deleteAttachmentsNotPresentInNote(
function getAttachmentsPathAndStatus (line 841) | function getAttachmentsPathAndStatus(markdownContent, storageKey, noteKe...
function removeAttachmentsByPaths (line 914) | function removeAttachmentsByPaths(attachments) {
function cloneAttachments (line 939) | function cloneAttachments(oldNote, newNote) {
function generateFileNotFoundMarkdown (line 979) | function generateFileNotFoundMarkdown() {
function isAttachmentLink (line 994) | function isAttachmentLink(text) {
function handleAttachmentLinkPaste (line 1021) | function handleAttachmentLinkPaste(storageKey, noteKey, linkText) {
FILE: browser/main/lib/dataApi/copyFile.js
function copyFile (line 11) | function copyFile(srcPath, dstPath) {
FILE: browser/main/lib/dataApi/createFolder.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function createFolder (line 25) | function createFolder(storageKey, input) {
FILE: browser/main/lib/dataApi/createNote.js
constant CSON (line 6) | const CSON = require('@rokt33r/season')
function validateInput (line 9) | function validateInput(input) {
function createNote (line 43) | function createNote(storageKey, input) {
FILE: browser/main/lib/dataApi/createNoteFromUrl.js
function validateUrl (line 9) | function validateUrl(str) {
constant ERROR_MESSAGES (line 21) | const ERROR_MESSAGES = {
function createNoteFromUrl (line 29) | function createNoteFromUrl(
FILE: browser/main/lib/dataApi/createSnippet.js
function createSnippet (line 6) | function createSnippet(snippetFile) {
FILE: browser/main/lib/dataApi/deleteFolder.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function deleteFolder (line 21) | function deleteFolder(storageKey, folderKey) {
FILE: browser/main/lib/dataApi/deleteNote.js
function deleteNote (line 7) | function deleteNote(storageKey, noteKey) {
FILE: browser/main/lib/dataApi/deleteSnippet.js
function deleteSnippet (line 5) | function deleteSnippet(snippet, snippetFile) {
FILE: browser/main/lib/dataApi/exportFolder.js
function exportFolder (line 26) | function exportFolder(storageKey, folderKey, fileType, exportDir, config) {
FILE: browser/main/lib/dataApi/exportNote.js
function exportNote (line 19) | function exportNote(storageKey, note, targetPath, outputFormatter) {
function prepareTasks (line 49) | function prepareTasks(tasks, storagePath, targetPath) {
function saveToFile (line 63) | function saveToFile(data, filename) {
function rollbackExport (line 77) | function rollbackExport(tasks) {
FILE: browser/main/lib/dataApi/exportNoteAs.js
function exportNoteAs (line 12) | function exportNoteAs(note, filename, fileType, config) {
FILE: browser/main/lib/dataApi/exportStorage.js
function exportStorage (line 27) | function exportStorage(storageKey, fileType, exportDir, config) {
FILE: browser/main/lib/dataApi/exportTag.js
function exportTag (line 12) | function exportTag(data, tag, fileType, exportDir, config) {
FILE: browser/main/lib/dataApi/fetchSnippet.js
function fetchSnippet (line 4) | function fetchSnippet(id, snippetFile) {
FILE: browser/main/lib/dataApi/formatHTML.js
constant CSS_FILES (line 21) | const CSS_FILES = [
function unprefix (line 44) | function unprefix(file) {
function formatHTML (line 73) | function formatHTML(props) {
function getStyleParams (line 528) | function getStyleParams(props) {
function getCodeThemeLink (line 572) | function getCodeThemeLink(name) {
function buildStyle (line 580) | function buildStyle(
function escapeHtmlCharactersInCodeTag (line 777) | function escapeHtmlCharactersInCodeTag(splitWithCodeTag) {
FILE: browser/main/lib/dataApi/formatMarkdown.js
function formatMarkdown (line 15) | function formatMarkdown(props) {
function getFrontMatter (line 79) | function getFrontMatter(markdown) {
function replaceFrontMatter (line 92) | function replaceFrontMatter(markdown, metadata) {
FILE: browser/main/lib/dataApi/formatPDF.js
function formatPDF (line 4) | function formatPDF(props) {
FILE: browser/main/lib/dataApi/getContentFormatter.js
function getContentFormatter (line 11) | function getContentFormatter(storage, fileType, config) {
FILE: browser/main/lib/dataApi/getFilename.js
function getFilename (line 14) | function getFilename(note, fileType, directory, deduplicator) {
FILE: browser/main/lib/dataApi/init.js
constant CSON (line 8) | const CSON = require('@rokt33r/season')
function init (line 24) | function init() {
FILE: browser/main/lib/dataApi/migrateFromV5Storage.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function migrateFromV5Storage (line 9) | function migrateFromV5Storage(storageKey, data) {
function importAll (line 26) | function importAll(storage, data) {
FILE: browser/main/lib/dataApi/migrateFromV6Storage.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function migrateFromV5Storage (line 7) | function migrateFromV5Storage(storagePath) {
FILE: browser/main/lib/dataApi/moveNote.js
constant CSON (line 4) | const CSON = require('@rokt33r/season')
function moveNote (line 10) | function moveNote(storageKey, noteKey, newStorageKey, newFolderKey) {
FILE: browser/main/lib/dataApi/removeStorage.js
function removeStorage (line 7) | function removeStorage(key) {
FILE: browser/main/lib/dataApi/renameStorage.js
function renameStorage (line 9) | function renameStorage(key, name) {
FILE: browser/main/lib/dataApi/reorderFolder.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function reorderFolder (line 20) | function reorderFolder(storageKey, oldIndex, newIndex) {
FILE: browser/main/lib/dataApi/resolveStorageData.js
constant CSON (line 3) | const CSON = require('@rokt33r/season')
function resolveStorageData (line 6) | function resolveStorageData(storageCache) {
FILE: browser/main/lib/dataApi/resolveStorageNotes.js
constant CSON (line 3) | const CSON = require('@rokt33r/season')
function resolveStorageNotes (line 5) | function resolveStorageNotes(storage) {
FILE: browser/main/lib/dataApi/toggleStorage.js
function toggleStorage (line 9) | function toggleStorage(key, isOpen) {
FILE: browser/main/lib/dataApi/updateFolder.js
constant CSON (line 4) | const CSON = require('@rokt33r/season')
function updateFolder (line 25) | function updateFolder(storageKey, folderKey, input) {
FILE: browser/main/lib/dataApi/updateNote.js
constant CSON (line 4) | const CSON = require('@rokt33r/season')
function validateInput (line 7) | function validateInput(input) {
function updateNote (line 82) | function updateNote(storageKey, noteKey, input) {
FILE: browser/main/lib/dataApi/updateSnippet.js
function updateSnippet (line 4) | function updateSnippet(snippet, snippetFile) {
FILE: browser/main/lib/eventEmitter.js
function on (line 4) | function on(name, listener) {
function off (line 8) | function off(name, listener) {
function once (line 12) | function once(name, listener) {
function emit (line 16) | function emit(name, ...args) {
FILE: browser/main/lib/modal.js
class ModalBase (line 6) | class ModalBase extends React.Component {
method constructor (line 7) | constructor(props) {
method close (line 16) | close() {
method render (line 30) | render() {
function openModal (line 51) | function openModal(component, props) {
function closeModal (line 68) | function closeModal() {
function isModalOpen (line 75) | function isModalOpen() {
FILE: browser/main/lib/notify.js
function notify (line 3) | function notify(title, options) {
FILE: browser/main/lib/shortcutManager.js
function updateShortcut (line 18) | function updateShortcut(newHotkey) {
function formatShortcut (line 24) | function formatShortcut(shortcut) {
function applyShortcuts (line 28) | function applyShortcuts(shortcuts) {
FILE: browser/main/modals/CreateFolderModal.js
class CreateFolderModal (line 12) | class CreateFolderModal extends React.Component {
method constructor (line 13) | constructor(props) {
method componentDidMount (line 21) | componentDidMount() {
method handleCloseButtonClick (line 26) | handleCloseButtonClick(e) {
method handleChange (line 30) | handleChange(e) {
method handleKeyDown (line 36) | handleKeyDown(e) {
method handleInputKeyDown (line 42) | handleInputKeyDown(e) {
method handleConfirmButtonClick (line 49) | handleConfirmButtonClick(e) {
method confirm (line 53) | confirm() {
method render (line 77) | render() {
FILE: browser/main/modals/CreateMarkdownFromURLModal.js
class CreateMarkdownFromURLModal (line 9) | class CreateMarkdownFromURLModal extends React.Component {
method constructor (line 10) | constructor(props) {
method componentDidMount (line 20) | componentDidMount() {
method handleCloseButtonClick (line 25) | handleCloseButtonClick(e) {
method handleChange (line 29) | handleChange(e) {
method handleKeyDown (line 35) | handleKeyDown(e) {
method handleInputKeyDown (line 41) | handleInputKeyDown(e) {
method handleConfirmButtonClick (line 48) | handleConfirmButtonClick(e) {
method showError (line 52) | showError(message) {
method hideError (line 59) | hideError() {
method confirm (line 66) | confirm() {
method render (line 80) | render() {
FILE: browser/main/modals/NewNoteModal.js
class NewNoteModal (line 11) | class NewNoteModal extends React.Component {
method constructor (line 12) | constructor(props) {
method componentDidMount (line 18) | componentDidMount() {
method handleCloseButtonClick (line 22) | handleCloseButtonClick(e) {
method handleCreateMarkdownFromUrlClick (line 26) | handleCreateMarkdownFromUrlClick(e) {
method handleMarkdownNoteButtonClick (line 38) | handleMarkdownNoteButtonClick(e) {
method handleMarkdownNoteButtonKeyDown (line 56) | handleMarkdownNoteButtonKeyDown(e) {
method handleSnippetNoteButtonClick (line 63) | handleSnippetNoteButtonClick(e) {
method handleSnippetNoteButtonKeyDown (line 81) | handleSnippetNoteButtonKeyDown(e) {
method handleKeyDown (line 88) | handleKeyDown(e) {
method render (line 94) | render() {
FILE: browser/main/modals/PreferencesModal/Blog.js
class Blog (line 13) | class Blog extends React.Component {
method constructor (line 14) | constructor(props) {
method handleLinkClick (line 23) | handleLinkClick(e) {
method clearMessage (line 28) | clearMessage() {
method componentDidMount (line 36) | componentDidMount() {
method handleBlogChange (line 59) | handleBlogChange(e) {
method handleSaveButtonClick (line 89) | handleSaveButtonClick(e) {
method render (line 104) | render() {
FILE: browser/main/modals/PreferencesModal/Crowdfunding.js
class Crowdfunding (line 9) | class Crowdfunding extends React.Component {
method constructor (line 10) | constructor(props) {
method handleLinkClick (line 16) | handleLinkClick(e) {
method render (line 21) | render() {
FILE: browser/main/modals/PreferencesModal/ExportTab.js
class ExportTab (line 13) | class ExportTab extends React.Component {
method constructor (line 14) | constructor(props) {
method clearMessage (line 22) | clearMessage() {
method componentDidMount (line 30) | componentDidMount() {
method componentWillUnmount (line 55) | componentWillUnmount() {
method handleSaveButtonClick (line 60) | handleSaveButtonClick(e) {
method handleExportChange (line 76) | handleExportChange(e) {
method render (line 102) | render() {
FILE: browser/main/modals/PreferencesModal/FolderItem.js
class FolderItem (line 12) | class FolderItem extends React.Component {
method constructor (line 13) | constructor(props) {
method handleEditChange (line 27) | handleEditChange(e) {
method handleConfirmButtonClick (line 36) | handleConfirmButtonClick(e) {
method confirm (line 40) | confirm() {
method handleColorButtonClick (line 58) | handleColorButtonClick(e) {
method handleColorChange (line 80) | handleColorChange(color) {
method handleColorPickerClose (line 85) | handleColorPickerClose(event) {
method handleCancelButtonClick (line 92) | handleCancelButtonClick(e) {
method handleFolderItemBlur (line 98) | handleFolderItemBlur(e) {
method renderEdit (line 109) | renderEdit(e) {
method handleDeleteConfirmButtonClick (line 184) | handleDeleteConfirmButtonClick(e) {
method renderDelete (line 195) | renderDelete() {
method handleEditButtonClick (line 221) | handleEditButtonClick(e) {
method handleDeleteButtonClick (line 236) | handleDeleteButtonClick(e) {
method renderIdle (line 242) | renderIdle() {
method render (line 271) | render() {
class Handle (line 303) | class Handle extends React.Component {
method render (line 304) | render() {
class SortableFolderItemComponent (line 313) | class SortableFolderItemComponent extends React.Component {
method render (line 314) | render() {
FILE: browser/main/modals/PreferencesModal/FolderList.js
class FolderList (line 11) | class FolderList extends React.Component {
method render (line 12) | render() {
class SortableFolderListComponent (line 58) | class SortableFolderListComponent extends React.Component {
method constructor (line 59) | constructor(props) {
method render (line 73) | render() {
FILE: browser/main/modals/PreferencesModal/HotkeyTab.js
class HotkeyTab (line 13) | class HotkeyTab extends React.Component {
method constructor (line 14) | constructor(props) {
method componentDidMount (line 23) | componentDidMount() {
method componentWillUnmount (line 59) | componentWillUnmount() {
method handleSaveButtonClick (line 64) | handleSaveButtonClick(e) {
method handleHintToggleButtonClick (line 79) | handleHintToggleButtonClick(e) {
method handleHotkeyChange (line 85) | handleHotkeyChange(e) {
method clearMessage (line 112) | clearMessage() {
method render (line 120) | render() {
FILE: browser/main/modals/PreferencesModal/InfoTab.js
class InfoTab (line 14) | class InfoTab extends React.Component {
method constructor (line 15) | constructor(props) {
method componentDidMount (line 26) | componentDidMount() {
method handleLinkClick (line 32) | handleLinkClick(e) {
method handleConfigChange (line 37) | handleConfigChange(e) {
method handleSubscriptionFormSubmit (line 47) | handleSubscriptionFormSubmit(e) {
method handleSubscriptionFormEmailChange (line 83) | handleSubscriptionFormEmailChange(e) {
method handleSaveButtonClick (line 89) | handleSaveButtonClick(e) {
method infoMessage (line 117) | infoMessage() {
method handleAutoUpdateChange (line 122) | handleAutoUpdateChange() {
method render (line 128) | render() {
FILE: browser/main/modals/PreferencesModal/PluginsTab.js
class PluginsTab (line 14) | class PluginsTab extends React.Component {
method constructor (line 15) | constructor(props) {
method componentDidMount (line 23) | componentDidMount() {
method componentWillUnmount (line 46) | componentWillUnmount() {
method checkWakatimePluginRequirement (line 51) | checkWakatimePluginRequirement() {
method handleSaveButtonClick (line 77) | handleSaveButtonClick(e) {
method handleIsWakatimePluginActiveChange (line 96) | handleIsWakatimePluginActiveChange(e) {
method handleWakatimeKeyChange (line 113) | handleWakatimeKeyChange(e) {
method clearMessage (line 133) | clearMessage() {
method render (line 141) | render() {
FILE: browser/main/modals/PreferencesModal/SnippetEditor.js
class SnippetEditor (line 20) | class SnippetEditor extends React.Component {
method componentDidMount (line 21) | componentDidMount() {
method componentWillUnmount (line 66) | componentWillUnmount() {
method onSnippetChanged (line 70) | onSnippetChanged(newSnippet) {
method onSnippetNameOrPrefixChanged (line 75) | onSnippetNameOrPrefixChanged(newSnippet) {
method saveSnippet (line 84) | saveSnippet() {
method render (line 93) | render() {
FILE: browser/main/modals/PreferencesModal/SnippetList.js
class SnippetList (line 9) | class SnippetList extends React.Component {
method constructor (line 10) | constructor(props) {
method componentDidMount (line 17) | componentDidMount() {
method reloadSnippetList (line 22) | reloadSnippetList() {
method handleSnippetContextMenu (line 29) | handleSnippetContextMenu(snippet) {
method deleteSnippet (line 38) | deleteSnippet(snippet) {
method handleSnippetClick (line 50) | handleSnippetClick(snippet) {
method createSnippet (line 54) | createSnippet() {
method defineSnippetStyleName (line 68) | defineSnippetStyleName(snippet) {
method render (line 82) | render() {
FILE: browser/main/modals/PreferencesModal/SnippetTab.js
class SnippetTab (line 13) | class SnippetTab extends React.Component {
method constructor (line 14) | constructor(props) {
method notify (line 22) | notify(title, options) {
method handleSnippetNameOrPrefixChange (line 33) | handleSnippetNameOrPrefixChange() {
method handleSnippetSelect (line 42) | handleSnippetSelect(snippet) {
method onSnippetNameOrPrefixChanged (line 55) | onSnippetNameOrPrefixChanged(e, type) {
method handleDeleteSnippet (line 66) | handleDeleteSnippet(snippet) {
method handleCopySnippet (line 73) | handleCopySnippet(e) {
method render (line 84) | render() {
FILE: browser/main/modals/PreferencesModal/StorageItem.js
class StorageItem (line 14) | class StorageItem extends React.Component {
method constructor (line 15) | constructor(props) {
method handleNewFolderButtonClick (line 23) | handleNewFolderButtonClick(e) {
method handleExternalButtonClick (line 43) | handleExternalButtonClick() {
method handleUnlinkButtonClick (line 48) | handleUnlinkButtonClick(e) {
method handleLabelClick (line 74) | handleLabelClick(e) {
method handleLabelChange (line 86) | handleLabelChange(e) {
method handleLabelBlur (line 92) | handleLabelBlur(e) {
method render (line 105) | render() {
FILE: browser/main/modals/PreferencesModal/StoragesTab.js
function browseFolder (line 15) | function browseFolder() {
class StoragesTab (line 34) | class StoragesTab extends React.Component {
method constructor (line 35) | constructor(props) {
method loadAttachmentStorage (line 50) | loadAttachmentStorage() {
method handleAddStorageButton (line 69) | handleAddStorageButton(e) {
method handleLinkClick (line 85) | handleLinkClick(e) {
method handleRemoveUnusedAttachments (line 90) | handleRemoveUnusedAttachments(attachments) {
method renderList (line 97) | renderList() {
method handleAddStorageBrowseButtonClick (line 183) | handleAddStorageBrowseButtonClick(e) {
method handleAddStorageChange (line 200) | handleAddStorageChange(e) {
method handleAddStorageCreateButton (line 209) | handleAddStorageCreateButton(e) {
method handleAddStorageCancelButton (line 228) | handleAddStorageCancelButton(e) {
method renderAddStorage (line 234) | renderAddStorage() {
method renderContent (line 318) | renderContent() {
method render (line 329) | render() {
FILE: browser/main/modals/PreferencesModal/UiTab.js
constant OSX (line 18) | const OSX = global.process.platform === 'darwin'
class UiTab (line 23) | class UiTab extends React.Component {
method constructor (line 24) | constructor(props) {
method componentDidMount (line 32) | componentDidMount() {
method componentWillUnmount (line 69) | componentWillUnmount() {
method handleUIChange (line 74) | handleUIChange(e) {
method handleSaveUIClick (line 199) | handleSaveUIClick(e) {
method clearMessage (line 219) | clearMessage() {
method formatTime (line 227) | formatTime(time) {
method render (line 242) | render() {
FILE: browser/main/modals/PreferencesModal/index.js
class Preferences (line 20) | class Preferences extends React.Component {
method constructor (line 21) | constructor(props) {
method componentDidMount (line 33) | componentDidMount() {
method switchTeam (line 39) | switchTeam(teamId) {
method handleNavButtonClick (line 43) | handleNavButtonClick(tab) {
method handleEscButtonClick (line 49) | handleEscButtonClick() {
method renderContent (line 53) | renderContent() {
method handleKeyDown (line 117) | handleKeyDown(e) {
method getContentBoundingBox (line 123) | getContentBoundingBox() {
method haveToSaveNotif (line 127) | haveToSaveNotif(type, message) {
method render (line 131) | render() {
FILE: browser/main/modals/RenameFolderModal.js
class RenameFolderModal (line 10) | class RenameFolderModal extends React.Component {
method constructor (line 11) | constructor(props) {
method componentDidMount (line 19) | componentDidMount() {
method handleCloseButtonClick (line 24) | handleCloseButtonClick(e) {
method handleChange (line 28) | handleChange(e) {
method handleKeyDown (line 34) | handleKeyDown(e) {
method handleInputKeyDown (line 40) | handleInputKeyDown(e) {
method handleConfirmButtonClick (line 47) | handleConfirmButtonClick(e) {
method confirm (line 51) | confirm() {
method render (line 69) | render() {
FILE: browser/main/modals/RenameTagModal.js
class RenameTagModal (line 16) | class RenameTagModal extends React.Component {
method constructor (line 17) | constructor(props) {
method componentDidMount (line 34) | componentDidMount() {
method handleChange (line 39) | handleChange(e) {
method handleKeyDown (line 47) | handleKeyDown(e) {
method handleInputKeyDown (line 53) | handleInputKeyDown(e) {
method handleConfirm (line 60) | handleConfirm() {
method showError (line 67) | showError(message) {
method renameTag (line 74) | renameTag(tag, updatedTag) {
method render (line 147) | render() {
FILE: browser/main/store.js
function defaultDataMap (line 9) | function defaultDataMap() {
function data (line 21) | function data(state = defaultDataMap(), action) {
function config (line 367) | function config(state = defaultConfig, action) {
function status (line 393) | function status(state = defaultStatus, action) {
function updateStarredChange (line 403) | function updateStarredChange(oldNote, note, state, uniqueKey) {
function updateFolderChange (line 414) | function updateFolderChange(oldNote, note, state, folderKey, uniqueKey) {
function updateTagChanges (line 432) | function updateTagChanges(oldNote, note, state, uniqueKey) {
function assignToTags (line 441) | function assignToTags(tags, state, uniqueKey) {
function removeFromTags (line 449) | function removeFromTags(tags, state, uniqueKey) {
function getOrInitItem (line 461) | function getOrInitItem(target, key) {
FILE: dev-scripts/dev.js
function startServer (line 18) | function startServer() {
function startElectron (line 51) | function startElectron() {
FILE: extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js
function incrementRemainingMarkdownListNumbers (line 55) | function incrementRemainingMarkdownListNumbers(cm, pos) {
FILE: extra_scripts/codemirror/addon/edit/closebrackets.js
function getOption (line 33) | function getOption(conf, name) {
function ensureBound (line 40) | function ensureBound(chars) {
function handler (line 48) | function handler(ch) {
function getConfig (line 52) | function getConfig(cm) {
function handleBackspace (line 64) | function handleBackspace(cm) {
function handleEnter (line 81) | function handleEnter(cm) {
function contractSelection (line 105) | function contractSelection(sel) {
function handleChar (line 111) | function handleChar(cm, ch) {
function charsAround (line 185) | function charsAround(cm, pos) {
function stringStartsAfter (line 191) | function stringStartsAfter(cm, pos) {
FILE: extra_scripts/codemirror/addon/hyperlink/hyperlink.js
class HyperLink (line 27) | class HyperLink {
method constructor (line 28) | constructor(cm) {
method getUrl (line 64) | getUrl(el) {
method specialLinkHandler (line 86) | specialLinkHandler(e, rawHref, linkHash) {
method onMouseDown (line 140) | onMouseDown(e) {
method onMouseEnter (line 176) | onMouseEnter(e) {
method onMouseLeave (line 193) | onMouseLeave(e) {
method onMouseMove (line 203) | onMouseMove(e) {
method showInfo (line 212) | showInfo(relatedTo) {
FILE: extra_scripts/codemirror/mode/bfm/bfm.js
function getMode (line 23) | function getMode(name, params, config, cm) {
FILE: extra_scripts/codemirror/mode/gfm/gfm.js
function blankLine (line 30) | function blankLine(state) {
FILE: gruntfile.js
constant WIN (line 6) | const WIN = process.platform === 'win32'
function getTarget (line 244) | function getTarget() {
function generateRule (line 303) | function generateRule(selector, bgColor, fgColor) {
FILE: index.js
function execMainApp (line 7) | function execMainApp() {
FILE: lib/ipcServer.js
function toggleMainWindow (line 10) | function toggleMainWindow() {
FILE: lib/main-app.js
function checkUpdate (line 40) | function checkUpdate(manualTriggered = false) {
FILE: lib/main-menu.js
constant LINUX (line 10) | const LINUX = process.platform === 'linux'
method click (line 26) | click() {
method click (line 63) | click() {
method click (line 83) | click() {
method click (line 90) | click() {
method click (line 97) | click() {
method click (line 104) | click() {
method click (line 116) | click() {
method click (line 127) | click() {
method click (line 134) | click() {
method click (line 141) | click() {
method click (line 148) | click() {
method click (line 161) | click() {
method click (line 167) | click() {
method click (line 177) | click() {
method click (line 187) | click() {
method click (line 205) | click() {
method click (line 261) | click() {
method click (line 274) | click() {
method click (line 281) | click() {
method click (line 291) | click() {
method click (line 298) | click() {
method click (line 308) | click() {
method click (line 318) | click() {
method click (line 325) | click() {
method click (line 331) | click() {
method click (line 341) | click() {
method click (line 348) | click() {
method click (line 355) | click() {
method click (line 408) | click() {
method click (line 414) | click() {
method click (line 420) | click() {
method click (line 426) | click() {
method click (line 435) | click() {
method click (line 443) | click() {
method click (line 449) | click() {
method click (line 455) | click() {
method click (line 468) | click() {
FILE: lib/main-window.js
function storeWindowSize (line 102) | function storeWindowSize() {
FILE: tests/dataApi/addStorage.js
constant CSON (line 18) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/createFolder.test.js
constant CSON (line 17) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/createNote.test.js
constant CSON (line 16) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/createNoteFromUrl.test.js
constant CSON (line 16) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/deleteFolder.test.js
constant CSON (line 21) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/deleteNote-test.js
constant CSON (line 18) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/migrateFromV6Storage-test.js
constant CSON (line 16) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/moveNote-test.js
constant CSON (line 17) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/reorderFolder-test.js
constant CSON (line 18) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/updateFolder-test.js
constant CSON (line 18) | const CSON = require('@rokt33r/season')
FILE: tests/dataApi/updateNote-test.js
constant CSON (line 18) | const CSON = require('@rokt33r/season')
FILE: tests/fixtures/TestDummy.js
constant CSON (line 5) | const CSON = require('@rokt33r/season')
function dummyFolder (line 8) | function dummyFolder(override = {}) {
function dummyBoostnoteJSONData (line 20) | function dummyBoostnoteJSONData(override = {}, isLegacy = false) {
function dummyNote (line 46) | function dummyNote(override = {}) {
function dummyStorage (line 100) | function dummyStorage(storagePath, override = {}) {
function dummyLegacyStorage (line 144) | function dummyLegacyStorage(storagePath, override = {}) {
FILE: tests/helpers/setup-browser-env.js
method getItem (line 24) | getItem() {
FILE: tests/lib/markdown-toc-generator.test.js
constant EOL (line 8) | const EOL = require('os').EOL
FILE: tests/lib/rc-parser.test.js
function filePath (line 62) | function filePath(filename) {
FILE: tests/lib/themeManager.test.js
method constructor (line 17) | constructor() {
Condensed preview — 367 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,851K chars).
[
{
"path": ".babelrc",
"chars": 291,
"preview": "{\n \"presets\": [\"react\", \"es2015\"],\n \"env\": {\n \"development\": {\n \"presets\": [\"react-hmre\"]\n },\n \"test\": {"
},
{
"path": ".boostnoterc.sample",
"chars": 791,
"preview": "{\n \"amaEnabled\": true,\n \"editor\": {\n \"fontFamily\": \"Monaco, Consolas\",\n \"fontSize\": \"14\",\n \"i"
},
{
"path": ".editorconfig",
"chars": 385,
"preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Space indentation\n[*]\ni"
},
{
"path": ".eslintignore",
"chars": 44,
"preview": "node_modules/\ncompiled/\ndist/\nextra_scripts/"
},
{
"path": ".eslintrc",
"chars": 700,
"preview": "{\n \"extends\": [\"standard\", \"standard-jsx\", \"plugin:react/recommended\", \"prettier\"],\n \"plugins\": [\"react\", \"prettier\"],"
},
{
"path": ".github/FUNDING.yml",
"chars": 29,
"preview": "issuehunt: BoostIo/Boostnote\n"
},
{
"path": ".gitignore",
"chars": 146,
"preview": ".DS_Store\n.env\nDesktop.ini\nThumbs.db\nnode_modules/*\n!node_modules/boost\n/dist\n/compiled\n/secret\n*.log\n.idea\n.vscode\npack"
},
{
"path": ".prettierrc",
"chars": 68,
"preview": "{\n \"singleQuote\": true,\n \"semi\": false,\n \"jsxSingleQuote\": true\n}"
},
{
"path": ".travis.yml",
"chars": 692,
"preview": "language: node_js\nnode_js:\n - 8\nscript:\n - npm run lint && npm run test\n - 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAV"
},
{
"path": "FAQ.md",
"chars": 813,
"preview": "# Frequently Asked Questions\n\n\n<details><summary>Allowing dangerous HTML tags</summary>\n\nSometimes it is useful to allow"
},
{
"path": "ISSUE_TEMPLATE.md",
"chars": 727,
"preview": "# Current behavior\n\n<!--\nLet us know what is currently happening.\n\nPlease include some **screenshots** with the **develo"
},
{
"path": "LICENSE",
"chars": 736,
"preview": "GPL-3.0\n\nBoostnote - an open source note-taking app made for programmers just like you.\n\nCopyright (C) 2017 - 2019 Boost"
},
{
"path": "PULL_REQUEST_TEMPLATE.md",
"chars": 1433,
"preview": "<!--\nBefore submitting this PR, please make sure that:\n- You have read and understand the contributing.md\n- You have che"
},
{
"path": "__mocks__/electron.js",
"chars": 187,
"preview": "module.exports = {\n require: jest.genMockFunction(),\n match: jest.genMockFunction(),\n app: jest.genMockFunction(),\n "
},
{
"path": "appdmg.json",
"chars": 314,
"preview": "{\n \"title\": \"Boostnote\",\n \"icon\": \"resources/dmg.icns\",\n \"background\": \"resources/boostnote-install.png\",\n \"icon-siz"
},
{
"path": "browser/components/CodeEditor.js",
"chars": 42138,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport _ from 'lodash'\nimport CodeMirror from 'codemirror'\n"
},
{
"path": "browser/components/CodeEditor.styl",
"chars": 89,
"preview": ".codeEditor-typo\n text-decoration underline wavy red\n\n.spellcheck-select\n border: none\n"
},
{
"path": "browser/components/ColorPicker.js",
"chars": 1939,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport { SketchPicker } from 'react-color'\nimport CSSModule"
},
{
"path": "browser/components/ColorPicker.styl",
"chars": 620,
"preview": ".colorPicker\n position fixed\n z-index 2\n display flex\n flex-direction column\n\n.cover\n position fixed\n top 0\n righ"
},
{
"path": "browser/components/MarkdownEditor.js",
"chars": 13122,
"preview": "/* eslint-disable camelcase */\nimport PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'brow"
},
{
"path": "browser/components/MarkdownEditor.styl",
"chars": 345,
"preview": ".root\n position relative\n\n.codeEditor\n absolute top bottom left right\n\n.hide\n z-index 0\n opacity 0\n pointer-events "
},
{
"path": "browser/components/MarkdownPreview.js",
"chars": 27589,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { connect } from 'react-redux'\nimport Markdown from "
},
{
"path": "browser/components/MarkdownSplitEditor.js",
"chars": 14501,
"preview": "import React from 'react'\nimport CodeEditor from 'browser/components/CodeEditor'\nimport MarkdownPreview from 'browser/co"
},
{
"path": "browser/components/MarkdownSplitEditor.styl",
"chars": 705,
"preview": ".root\n width 100%\n height 100%\n font-size 30px\n display flex\n flex-wrap wrap\n .slider\n absolute top bottom\n "
},
{
"path": "browser/components/ModalEscButton.js",
"chars": 476,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/components/ModalEscButton.styl",
"chars": 279,
"preview": ".escButton\n height 50px\n position absolute\n background-color transparent\n color $ui-inactive-text-color\n border non"
},
{
"path": "browser/components/NavToggleButton.js",
"chars": 776,
"preview": "/**\n * @fileoverview Micro component for toggle SideNav\n */\nimport PropTypes from 'prop-types'\nimport React from 'react'"
},
{
"path": "browser/components/NavToggleButton.styl",
"chars": 745,
"preview": ".navToggle\n navButtonColor()\n display block\n position absolute\n left 5px\n bottom 5px\n border-radius 16.5px\n heigh"
},
{
"path": "browser/components/NoteItem.js",
"chars": 5052,
"preview": "/**\n * @fileoverview Note item component.\n */\nimport PropTypes from 'prop-types'\nimport React from 'react'\nimport { isAr"
},
{
"path": "browser/components/NoteItem.styl",
"chars": 10897,
"preview": "$control-height = 30px\n\n.root\n absolute left bottom\n top $topBar-height - 1\n background-color $ui-noteList-background"
},
{
"path": "browser/components/NoteItemSimple.js",
"chars": 2201,
"preview": "/**\n * @fileoverview Note item component with simple display mode.\n */\nimport PropTypes from 'prop-types'\nimport React f"
},
{
"path": "browser/components/NoteItemSimple.styl",
"chars": 9001,
"preview": "$control-height = 30px\n\n.root\n absolute left bottom\n top $topBar-height - 1\n background-color $ui-noteList-background"
},
{
"path": "browser/components/RealtimeNotification.js",
"chars": 1382,
"preview": "import React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styles from './RealtimeNotification.sty"
},
{
"path": "browser/components/RealtimeNotification.styl",
"chars": 1031,
"preview": ".notification-area\n z-index 1000\n font-size 12px\n position: relative\n top: 12px\n background-color none\n\n.notificati"
},
{
"path": "browser/components/SideNavFilter.js",
"chars": 2817,
"preview": "/**\n * @fileoverview Filter for all notes.\n */\nimport PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSMo"
},
{
"path": "browser/components/SideNavFilter.styl",
"chars": 6396,
"preview": ".menu\n margin-bottom 20px\n\n.menu-button\n navButtonColor()\n height 36px\n padding 0 15px 0 20px\n font-size 14px\n wid"
},
{
"path": "browser/components/SnippetTab.js",
"chars": 3180,
"preview": "import React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styles from './SnippetTab.styl'\nimport "
},
{
"path": "browser/components/SnippetTab.styl",
"chars": 3471,
"preview": ".root\n position relative\n flex 1\n min-width 70px\n overflow hidden\n border-left 1px solid $ui-borderColor\n border-t"
},
{
"path": "browser/components/StorageItem.js",
"chars": 2853,
"preview": "/**\n * @fileoverview Micro component for showing storage.\n */\nimport PropTypes from 'prop-types'\nimport React from 'reac"
},
{
"path": "browser/components/StorageItem.styl",
"chars": 3826,
"preview": ".root\n width 100%\n user-select none\n\n.folderList-item\n display flex\n width 100%\n height 34px\n background-color tra"
},
{
"path": "browser/components/StorageList.js",
"chars": 657,
"preview": "/**\n * @fileoverview Micro component for showing StorageList\n */\nimport PropTypes from 'prop-types'\nimport React from 'r"
},
{
"path": "browser/components/StorageList.styl",
"chars": 413,
"preview": ".storageList\n absolute left right\n bottom 37px\n top 180px\n overflow-y auto\n\n.storageList-folded\n @extend .storageLi"
},
{
"path": "browser/components/TagListItem.js",
"chars": 1758,
"preview": "/**\n * @fileoverview Micro component for showing TagList.\n */\nimport PropTypes from 'prop-types'\nimport React from 'reac"
},
{
"path": "browser/components/TagListItem.styl",
"chars": 3040,
"preview": ".tagList-itemContainer\n display flex\n\n.tagList-item\n display flex\n flex 1\n width 100%\n height 26px\n background-col"
},
{
"path": "browser/components/TodoListPercentage.js",
"chars": 988,
"preview": "/**\n * @fileoverview Percentage of todo achievement.\n */\n\nimport PropTypes from 'prop-types'\nimport React from 'react'\ni"
},
{
"path": "browser/components/TodoListPercentage.styl",
"chars": 1490,
"preview": ".percentageBar\n display: flex\n position absolute\n top 72px\n right 0px\n left 0px\n background-color #DADFE1\n width "
},
{
"path": "browser/components/TodoProcess.js",
"chars": 931,
"preview": "/**\n * @fileoverview Percentage of todo achievement.\n */\n\nimport PropTypes from 'prop-types'\nimport React from 'react'\ni"
},
{
"path": "browser/components/TodoProcess.styl",
"chars": 822,
"preview": ".todo-process\n font-size 12px\n display flex\n padding-top 15px\n width 85%\n\n.todo-process-text\n display inline-block\n"
},
{
"path": "browser/components/markdown.styl",
"chars": 13758,
"preview": "global-reset()\n\nborderColor = #D0D0D0 // using\nhighlightenBorderColor = darken(borderColor, 20%)\ninvBorderColor = #40484"
},
{
"path": "browser/components/render/MermaidRender.js",
"chars": 1837,
"preview": "import mermaidAPI from 'mermaid/dist/mermaid.min.js'\nimport uiThemes from 'browser/lib/ui-themes'\n\n// fixes bad styling "
},
{
"path": "browser/lib/CMLanguageList.js",
"chars": 1493,
"preview": "export const languageMaps = {\n brainfuck: 'Brainfuck',\n cpp: 'C++',\n cs: 'C#',\n clojure: 'Clojure',\n 'clojure-repl'"
},
{
"path": "browser/lib/CSSModules.js",
"chars": 166,
"preview": "import CSSModules from 'react-css-modules'\n\nexport default function(component, styles) {\n return CSSModules(component, "
},
{
"path": "browser/lib/Languages.js",
"chars": 1182,
"preview": "const languages = [\n {\n name: 'Albanian',\n locale: 'sq'\n },\n {\n name: 'Chinese (zh-CN)',\n locale: 'zh-CN'"
},
{
"path": "browser/lib/Mutable.js",
"chars": 1798,
"preview": "class MutableMap {\n constructor(iterable) {\n this._map = new Map(iterable)\n Object.defineProperty(this, 'size', {"
},
{
"path": "browser/lib/RcParser.js",
"chars": 564,
"preview": "import path from 'path'\nimport sander from 'sander'\n\nconst BOOSTNOTERC = '.boostnoterc'\nconst homePath = global.process."
},
{
"path": "browser/lib/SnippetManager.js",
"chars": 2831,
"preview": "import crypto from 'crypto'\nimport fs from 'fs'\nimport consts from './consts'\n\nclass SnippetManager {\n constructor() {\n"
},
{
"path": "browser/lib/TextEditorInterface.js",
"chars": 2506,
"preview": "import { Point } from '@susisu/mte-kernel'\n\nexport default class TextEditorInterface {\n constructor(editor) {\n this."
},
{
"path": "browser/lib/confirmDeleteNote.js",
"chars": 618,
"preview": "import electron from 'electron'\nimport i18n from 'browser/lib/i18n'\nconst { remote } = electron\nconst { dialog } = remot"
},
{
"path": "browser/lib/consts.js",
"chars": 2141,
"preview": "const path = require('path')\nconst fs = require('sander')\nconst { remote } = require('electron')\nconst { app } = remote\n"
},
{
"path": "browser/lib/context.js",
"chars": 318,
"preview": "const { remote } = require('electron')\nconst { Menu, MenuItem } = remote\n\nfunction popup(templates) {\n const menu = new"
},
{
"path": "browser/lib/contextMenuBuilder.js",
"chars": 4075,
"preview": "import i18n from 'browser/lib/i18n'\nimport fs from 'fs'\n\nconst { remote } = require('electron')\nconst { Menu } = remote."
},
{
"path": "browser/lib/convertModeName.js",
"chars": 298,
"preview": "export default function convertModeName(name) {\n switch (name) {\n case 'ejs':\n return 'Embedded Javascript'\n "
},
{
"path": "browser/lib/customMeta.js",
"chars": 467,
"preview": "import CodeMirror from 'codemirror'\nimport 'codemirror-mode-elixir'\n\nconst stylusCodeInfo = CodeMirror.modeInfo.find(inf"
},
{
"path": "browser/lib/date-formatter.js",
"chars": 358,
"preview": "/**\n * @fileoverview Formatting date string.\n */\nimport moment from 'moment'\n\n/**\n * @description Return date string. Fo"
},
{
"path": "browser/lib/findNoteTitle.js",
"chars": 1353,
"preview": "export function findNoteTitle(\n value,\n enableFrontMatterTitle,\n frontMatterTitleField = 'title'\n) {\n const splitted"
},
{
"path": "browser/lib/findStorage.js",
"chars": 431,
"preview": "const _ = require('lodash')\n\nexport function findStorage(storageKey) {\n const cachedStorageList = JSON.parse(localStora"
},
{
"path": "browser/lib/getTodoStatus.js",
"chars": 631,
"preview": "export function getTodoStatus(content) {\n const splitted = content.split('\\n')\n let numberOfTodo = 0\n let numberOfCom"
},
{
"path": "browser/lib/htmlTextHelper.js",
"chars": 768,
"preview": "/**\n * @fileoverview Text trimmer for html.\n */\n\n/**\n * @param {string} text\n * @return {string}\n */\n\nexport function de"
},
{
"path": "browser/lib/i18n.js",
"chars": 508,
"preview": "const path = require('path')\nconst { remote } = require('electron')\nconst { app } = remote\nconst { getLocales } = requir"
},
{
"path": "browser/lib/keygen.js",
"chars": 245,
"preview": "const crypto = require('crypto')\nconst uuidv4 = require('uuid/v4')\n\nmodule.exports = function(uuid) {\n if (typeof uuid "
},
{
"path": "browser/lib/markdown-it-deflist.js",
"chars": 6521,
"preview": "'use strict'\n\nmodule.exports = function definitionListPlugin(md) {\n var isSpace = md.utils.isSpace\n\n // Search `[:~][\\"
},
{
"path": "browser/lib/markdown-it-fence.js",
"chars": 3375,
"preview": "'use strict'\n\nmodule.exports = function(md, renderers, defaultRenderer) {\n const paramsRE = /^[ \\t]*([\\w+#-]+)?(?:\\(((?"
},
{
"path": "browser/lib/markdown-it-frontmatter.js",
"chars": 622,
"preview": "'use strict'\n\nmodule.exports = function frontMatterPlugin(md) {\n function frontmatter(state, startLine, endLine, silent"
},
{
"path": "browser/lib/markdown-it-sanitize-html.js",
"chars": 3554,
"preview": "'use strict'\n\nimport sanitizeHtml from 'sanitize-html'\nimport { escapeHtmlCharacters } from './utils'\nimport url from 'u"
},
{
"path": "browser/lib/markdown-toc-generator.js",
"chars": 2536,
"preview": "/**\n * @fileoverview Markdown table of contents generator\n */\n\nimport { EOL } from 'os'\nimport toc from 'markdown-toc'\ni"
},
{
"path": "browser/lib/markdown.js",
"chars": 14739,
"preview": "import markdownit from 'markdown-it'\nimport sanitize from './markdown-it-sanitize-html'\nimport emoji from 'markdown-it-e"
},
{
"path": "browser/lib/markdown2.js",
"chars": 0,
"preview": ""
},
{
"path": "browser/lib/markdownTextHelper.js",
"chars": 959,
"preview": "/**\n * @fileoverview Text trimmer for markdown note.\n */\n\n/**\n * @param {string} input\n * @return {string}\n */\nexport fu"
},
{
"path": "browser/lib/newNote.js",
"chars": 2376,
"preview": "import dataApi from 'browser/main/lib/dataApi'\nimport ee from 'browser/main/lib/eventEmitter'\nimport AwsMobileAnalyticsC"
},
{
"path": "browser/lib/normalizeEditorFontFamily.js",
"chars": 372,
"preview": "import consts from 'browser/lib/consts'\nimport isString from 'lodash/isString'\n\nexport default function normalizeEditorF"
},
{
"path": "browser/lib/search.js",
"chars": 1122,
"preview": "import _ from 'lodash'\n\nexport default function searchFromNotes(notes, search) {\n if (search.trim().length === 0) retur"
},
{
"path": "browser/lib/slugify.js",
"chars": 304,
"preview": "module.exports = function slugify(title) {\n const slug = encodeURI(\n title\n .trim()\n .replace(/^\\s+/, '')\n"
},
{
"path": "browser/lib/spellcheck.js",
"chars": 7361,
"preview": "import styles from '../components/CodeEditor.styl'\nimport i18n from 'browser/lib/i18n'\n\nconst Typo = require('typo-js')\n"
},
{
"path": "browser/lib/turndown.js",
"chars": 251,
"preview": "const TurndownService = require('turndown')\nconst { gfm } = require('turndown-plugin-gfm')\n\nexport const createTurndownS"
},
{
"path": "browser/lib/ui-themes.js",
"chars": 677,
"preview": "import i18n from 'browser/lib/i18n'\n\nexport default [\n {\n name: 'dark',\n label: i18n.__('Dark'),\n isDark: true"
},
{
"path": "browser/lib/utils.js",
"chars": 5648,
"preview": "export function lastFindInArray(array, callback) {\n for (let i = array.length - 1; i >= 0; --i) {\n if (callback(arra"
},
{
"path": "browser/lib/wakatime-plugin.js",
"chars": 1247,
"preview": "import config from 'browser/main/lib/ConfigManager'\nconst exec = require('child_process').exec\nconst path = require('pat"
},
{
"path": "browser/main/Detail/Detail.styl",
"chars": 836,
"preview": ".root\n absolute top bottom right\n display flex\n align-items center\n justify-content center\n\n.empty\n height 320px\n "
},
{
"path": "browser/main/Detail/DetailVars.styl",
"chars": 322,
"preview": "/**\n * Varibales for note detail space.\n */\n\n// Margin on the left side and the right side for NoteDetail component.\n$no"
},
{
"path": "browser/main/Detail/FolderSelect.js",
"chars": 7630,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/FolderSelect.styl",
"chars": 4139,
"preview": ".root\n position relative\n border solid 1px transparent\n vertical-align middle\n border-radius 2px\n height 30px\n tra"
},
{
"path": "browser/main/Detail/FromUrlButton.js",
"chars": 1587,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/FromUrlButton.styl",
"chars": 723,
"preview": ".root\n top 45px\n topBarButtonRight()\n &:hover\n transition 0.2s\n color alpha($ui-favorite-star-button-color, 0.6"
},
{
"path": "browser/main/Detail/FullscreenButton.js",
"chars": 835,
"preview": "import PropTypes from 'prop-types'\r\nimport React from 'react'\r\nimport CSSModules from 'browser/lib/CSSModules'\r\nimport s"
},
{
"path": "browser/main/Detail/FullscreenButton.styl",
"chars": 405,
"preview": ".control-fullScreenButton\n top 80px\n topBarButtonRight()\n &:hover .tooltip\n opacity 1\n\n.tooltip\n tooltip()\n posi"
},
{
"path": "browser/main/Detail/InfoButton.js",
"chars": 545,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/InfoButton.styl",
"chars": 370,
"preview": ".control-infoButton\n top 10px\n topBarButtonRight()\n &:hover .tooltip\n opacity 1\n\n.tooltip\n tooltip()\n position a"
},
{
"path": "browser/main/Detail/InfoPanel.js",
"chars": 4320,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/InfoPanel.styl",
"chars": 3853,
"preview": ".control-infoPanel\n position fixed\n pointer-events none\n top 50px\n z-index 200\n line-height normal\n border-radius "
},
{
"path": "browser/main/Detail/InfoPanelTrashed.js",
"chars": 2334,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/MarkdownNoteDetail.js",
"chars": 18386,
"preview": "/* eslint-disable camelcase */\nimport PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'brow"
},
{
"path": "browser/main/Detail/MarkdownNoteDetail.styl",
"chars": 1526,
"preview": "@import('NoteDetailInfo')\n@import('DetailVars')\n\n.root\n absolute top right bottom\n border-left 1px solid alpha(#DEDEDE"
},
{
"path": "browser/main/Detail/NoteDetailInfo.styl",
"chars": 2322,
"preview": "@import('DetailVars')\n\n$info-height = 60px\n$info-margin-under-border = 30px\n\n.info\n absolute top left right\n left 0\n "
},
{
"path": "browser/main/Detail/PermanentDeleteButton.js",
"chars": 580,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/RestoreButton.js",
"chars": 545,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/RestoreButton.styl",
"chars": 349,
"preview": ".control-restoreButton\n top 115px\n topBarButtonRight()\n &:hover .tooltip\n opacity 1\n\n.tooltip\n tooltip()\n positi"
},
{
"path": "browser/main/Detail/SnippetNoteDetail.js",
"chars": 31286,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/SnippetNoteDetail.styl",
"chars": 4205,
"preview": "@import('NoteDetailInfo')\n@import('DetailVars')\n\n.root\n absolute top bottom right\n border-left 1px solid alpha(#DEDEDE"
},
{
"path": "browser/main/Detail/StarButton.js",
"chars": 1584,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/StarButton.styl",
"chars": 774,
"preview": ".root\n top 45px\n topBarButtonRight()\n &:hover\n transition 0.2s\n color alpha($ui-favorite-star-button-color, 0.6"
},
{
"path": "browser/main/Detail/TagSelect.js",
"chars": 7242,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport invertColor from 'invert-color'\nimport CSSModules fr"
},
{
"path": "browser/main/Detail/TagSelect.styl",
"chars": 1397,
"preview": ".root\n display flex\n align-items center\n user-select none\n vertical-align middle\n width 96%\n overflow-x auto\n whi"
},
{
"path": "browser/main/Detail/ToggleDirectionButton.js",
"chars": 907,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/ToggleDirectionButton.styl",
"chars": 1605,
"preview": ".control-toggleModeButton\n height 25px\n border-radius 50px\n background-color #F4F4F4\n width 52px\n display flex\n al"
},
{
"path": "browser/main/Detail/ToggleModeButton.js",
"chars": 1177,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/ToggleModeButton.styl",
"chars": 1587,
"preview": ".control-toggleModeButton\n height 25px\n border-radius 50px\n background-color #F4F4F4\n width 52px\n display flex\n al"
},
{
"path": "browser/main/Detail/TrashButton.js",
"chars": 560,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Detail/TrashButton.styl",
"chars": 490,
"preview": ".control-trashButton\n top 115px\n topBarButtonRight()\n &:hover .tooltip\n opacity 1\n\n.tooltip\n tooltip()\n position"
},
{
"path": "browser/main/Detail/index.js",
"chars": 3855,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/DevTools/index.dev.js",
"chars": 406,
"preview": "import React from 'react'\nimport { createDevTools } from 'redux-devtools'\nimport LogMonitor from 'redux-devtools-log-mon"
},
{
"path": "browser/main/DevTools/index.js",
"chars": 278,
"preview": "/* eslint-disable no-undef */\nif (process.env.NODE_ENV === 'development') {\n // eslint-disable-next-line global-require"
},
{
"path": "browser/main/DevTools/index.prod.js",
"chars": 114,
"preview": "import React from 'react'\n\nconst DevTools = () => <div />\nDevTools.instrument = () => {}\n\nexport default DevTools\n"
},
{
"path": "browser/main/Main.js",
"chars": 12991,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/Main.styl",
"chars": 598,
"preview": ".root\n absolute top left bottom right\n\n.body\n absolute right top bottom\n left $sideNav-width\n\n.body--expanded\n @exte"
},
{
"path": "browser/main/NewNoteButton/NewNoteButton.styl",
"chars": 1792,
"preview": ".root\n position relative\n background-color $ui-noteList-backgroundColor\n height $topBar-height - 1\n margin-left: aut"
},
{
"path": "browser/main/NewNoteButton/index.js",
"chars": 3348,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/NoteList/NoteList.styl",
"chars": 2051,
"preview": "$control-height = 30px\n\n.root\n absolute left bottom\n top $topBar-height - 1\n background-color $ui-noteList-background"
},
{
"path": "browser/main/NoteList/index.js",
"chars": 38841,
"preview": "/* global electron */\nimport PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/C"
},
{
"path": "browser/main/SideNav/ListButton.js",
"chars": 749,
"preview": "import PropTypes from 'prop-types'\r\nimport React from 'react'\r\nimport CSSModules from 'browser/lib/CSSModules'\r\nimport s"
},
{
"path": "browser/main/SideNav/PreferenceButton.js",
"chars": 575,
"preview": "import PropTypes from 'prop-types'\r\nimport React from 'react'\r\nimport CSSModules from 'browser/lib/CSSModules'\r\nimport s"
},
{
"path": "browser/main/SideNav/PreferenceButton.styl",
"chars": 1106,
"preview": ".top-menu-preference\n navButtonColor()\n width 2em\n background-color transparent\n &:hover\n color $ui-button-defaul"
},
{
"path": "browser/main/SideNav/SearchButton.js",
"chars": 703,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/SideNav/SearchButton.styl",
"chars": 1172,
"preview": ".top-menu-search\n navButtonColor()\n position relative\n margin-right 6px\n top 3px\n width 2em\n background-color tran"
},
{
"path": "browser/main/SideNav/SideNav.styl",
"chars": 3811,
"preview": ".root\n absolute top left bottom\n width $sideNav-width\n background-color #2E3235\n user-select none\n color $ui-text-c"
},
{
"path": "browser/main/SideNav/StorageItem.js",
"chars": 13092,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/SideNav/StorageItem.styl",
"chars": 4755,
"preview": ".root\n width 100%\n user-select none\n padding-top 20px\n\n.header\n position relative\n height 36px\n width 100%\n margi"
},
{
"path": "browser/main/SideNav/SwitchButton.styl",
"chars": 1074,
"preview": ".non-active-button\n color $ui-inactive-text-color\n font-size 16px\n border 0\n background-color transparent\n transiti"
},
{
"path": "browser/main/SideNav/TagButton.js",
"chars": 743,
"preview": "import PropTypes from 'prop-types'\r\nimport React from 'react'\r\nimport CSSModules from 'browser/lib/CSSModules'\r\nimport s"
},
{
"path": "browser/main/SideNav/index.js",
"chars": 19369,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { push } from 'connected-react-router'\nimport CSSMod"
},
{
"path": "browser/main/StatusBar/StatusBar.styl",
"chars": 1757,
"preview": "@import('../Detail/DetailVars')\n\n.root\n position absolute\n bottom 10px\n right 10px\n z-index 100\n display flex\n\n.bla"
},
{
"path": "browser/main/StatusBar/index.js",
"chars": 3403,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/TopBar/TopBar.styl",
"chars": 5584,
"preview": ".root\n position relative\n background-color $ui-noteList-backgroundColor\n height $topBar-height - 1\n\n.root--expanded\n "
},
{
"path": "browser/main/TopBar/index.js",
"chars": 5374,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/global.styl",
"chars": 3145,
"preview": "global-reset()\n@import '../styles/vars.styl'\n\nDEFAULT_FONTS = 'OpenSans', helvetica, arial, sans-serif\n\nhtml, body\n wid"
},
{
"path": "browser/main/index.js",
"chars": 5166,
"preview": "import { Provider } from 'react-redux'\nimport Main from './Main'\nimport { store, history } from './store'\nimport React, "
},
{
"path": "browser/main/lib/AwsMobileAnalyticsConfig.js",
"chars": 2160,
"preview": "const AWS = require('aws-sdk')\nconst AMA = require('aws-sdk-mobile-analytics')\nconst ConfigManager = require('browser/ma"
},
{
"path": "browser/main/lib/Commander.js",
"chars": 555,
"preview": "let callees = []\n\nfunction bind(name, el) {\n callees.push({\n name: name,\n element: el\n })\n}\n\nfunction release(el"
},
{
"path": "browser/main/lib/ConfigManager.js",
"chars": 8536,
"preview": "import _ from 'lodash'\nimport RcParser from 'browser/lib/RcParser'\nimport i18n from 'browser/lib/i18n'\nimport ee from 'b"
},
{
"path": "browser/main/lib/ThemeManager.js",
"chars": 1491,
"preview": "import ConfigManager from 'browser/main/lib/ConfigManager'\nimport uiThemes from 'browser/lib/ui-themes'\n\nconst saveChang"
},
{
"path": "browser/main/lib/ZoomManager.js",
"chars": 516,
"preview": "import ConfigManager from './ConfigManager'\n\nconst electron = require('electron')\nconst { remote } = electron\n\n_init()\n\n"
},
{
"path": "browser/main/lib/dataApi/addStorage.js",
"chars": 2484,
"preview": "const _ = require('lodash')\nconst keygen = require('browser/lib/keygen')\nconst resolveStorageData = require('./resolveSt"
},
{
"path": "browser/main/lib/dataApi/attachmentManagement.js",
"chars": 36425,
"preview": "const uniqueSlug = require('unique-slug')\nconst fs = require('fs')\nconst path = require('path')\nconst findStorage = requ"
},
{
"path": "browser/main/lib/dataApi/copyFile.js",
"chars": 761,
"preview": "import fs from 'fs'\nimport fx from 'fs-extra'\nimport path from 'path'\n\n/**\n * @description Copy a file from source to de"
},
{
"path": "browser/main/lib/dataApi/createFolder.js",
"chars": 1351,
"preview": "const _ = require('lodash')\nconst keygen = require('browser/lib/keygen')\nconst path = require('path')\nconst resolveStora"
},
{
"path": "browser/main/lib/dataApi/createNote.js",
"chars": 2623,
"preview": "const sander = require('sander')\nconst resolveStorageData = require('./resolveStorageData')\nconst _ = require('lodash')\n"
},
{
"path": "browser/main/lib/dataApi/createNoteFromUrl.js",
"chars": 2888,
"preview": "const http = require('http')\nconst https = require('https')\nconst { createTurndownService } = require('../../../lib/turn"
},
{
"path": "browser/main/lib/dataApi/createSnippet.js",
"chars": 826,
"preview": "import fs from 'fs'\nimport crypto from 'crypto'\nimport consts from 'browser/lib/consts'\nimport fetchSnippet from 'browse"
},
{
"path": "browser/main/lib/dataApi/deleteFolder.js",
"chars": 1704,
"preview": "const _ = require('lodash')\nconst path = require('path')\nconst resolveStorageData = require('./resolveStorageData')\ncons"
},
{
"path": "browser/main/lib/dataApi/deleteNote.js",
"chars": 1007,
"preview": "const resolveStorageData = require('./resolveStorageData')\nconst path = require('path')\nconst sander = require('sander')"
},
{
"path": "browser/main/lib/dataApi/deleteSnippet.js",
"chars": 619,
"preview": "import fs from 'fs'\nimport consts from 'browser/lib/consts'\nimport fetchSnippet from 'browser/main/lib/dataApi/fetchSnip"
},
{
"path": "browser/main/lib/dataApi/exportFolder.js",
"chars": 1688,
"preview": "import { findStorage } from 'browser/lib/findStorage'\nimport resolveStorageData from './resolveStorageData'\nimport resol"
},
{
"path": "browser/main/lib/dataApi/exportNote.js",
"chars": 2462,
"preview": "import copyFile from 'browser/main/lib/dataApi/copyFile'\nimport { findStorage } from 'browser/lib/findStorage'\n\nconst fs"
},
{
"path": "browser/main/lib/dataApi/exportNoteAs.js",
"chars": 541,
"preview": "import { findStorage } from 'browser/lib/findStorage'\nimport exportNote from './exportNote'\nimport getContentFormatter f"
},
{
"path": "browser/main/lib/dataApi/exportStorage.js",
"chars": 2101,
"preview": "import { findStorage } from 'browser/lib/findStorage'\nimport resolveStorageData from './resolveStorageData'\nimport resol"
},
{
"path": "browser/main/lib/dataApi/exportTag.js",
"chars": 638,
"preview": "import exportNoteAs from './exportNoteAs'\nimport getFilename from './getFilename'\n\n/**\n * @param {Object} data\n * @param"
},
{
"path": "browser/main/lib/dataApi/fetchSnippet.js",
"chars": 523,
"preview": "import fs from 'fs'\nimport consts from 'browser/lib/consts'\n\nfunction fetchSnippet(id, snippetFile) {\n return new Promi"
},
{
"path": "browser/main/lib/dataApi/formatHTML.js",
"chars": 18247,
"preview": "import path from 'path'\nimport fileUrl from 'file-url'\nimport fs from 'fs'\nimport { remote } from 'electron'\nimport cons"
},
{
"path": "browser/main/lib/dataApi/formatMarkdown.js",
"chars": 2558,
"preview": "import attachmentManagement from './attachmentManagement'\nimport yaml from 'js-yaml'\nimport path from 'path'\n\nconst deli"
},
{
"path": "browser/main/lib/dataApi/formatPDF.js",
"chars": 718,
"preview": "import formatHTML from './formatHTML'\nimport { remote } from 'electron'\n\nexport default function formatPDF(props) {\n re"
},
{
"path": "browser/main/lib/dataApi/getContentFormatter.js",
"chars": 1972,
"preview": "import formatMarkdown from './formatMarkdown'\nimport formatHTML from './formatHTML'\nimport formatPDF from './formatPDF'\n"
},
{
"path": "browser/main/lib/dataApi/getFilename.js",
"chars": 794,
"preview": "import filenamify from 'filenamify'\nimport i18n from 'browser/lib/i18n'\nimport path from 'path'\n\n/**\n * @param {Object} "
},
{
"path": "browser/main/lib/dataApi/index.js",
"chars": 1246,
"preview": "const dataApi = {\n init: require('./init'),\n toggleStorage: require('./toggleStorage'),\n addStorage: require('./addSt"
},
{
"path": "browser/main/lib/dataApi/init.js",
"chars": 2809,
"preview": "'use strict'\nconst _ = require('lodash')\nconst resolveStorageData = require('./resolveStorageData')\nconst resolveStorage"
},
{
"path": "browser/main/lib/dataApi/migrateFromV5Storage.js",
"chars": 3387,
"preview": "const _ = require('lodash')\nconst keygen = require('browser/lib/keygen')\nconst resolveStorageData = require('./resolveSt"
},
{
"path": "browser/main/lib/dataApi/migrateFromV6Storage.js",
"chars": 3097,
"preview": "const path = require('path')\nconst sander = require('sander')\nconst keygen = require('browser/lib/keygen')\nconst _ = req"
},
{
"path": "browser/main/lib/dataApi/moveNote.js",
"chars": 3306,
"preview": "const resolveStorageData = require('./resolveStorageData')\nconst _ = require('lodash')\nconst path = require('path')\ncons"
},
{
"path": "browser/main/lib/dataApi/removeStorage.js",
"chars": 598,
"preview": "const _ = require('lodash')\n\n/**\n * @param {String} key\n * @return {key}\n */\nfunction removeStorage(key) {\n let rawStor"
},
{
"path": "browser/main/lib/dataApi/renameStorage.js",
"chars": 883,
"preview": "const _ = require('lodash')\nconst resolveStorageData = require('./resolveStorageData')\n\n/**\n * @param {String} key\n * @p"
},
{
"path": "browser/main/lib/dataApi/reorderFolder.js",
"chars": 1106,
"preview": "const _ = require('lodash')\n_.move = require('lodash-move').default\nconst path = require('path')\nconst resolveStorageDat"
},
{
"path": "browser/main/lib/dataApi/resolveStorageData.js",
"chars": 1201,
"preview": "const _ = require('lodash')\nconst path = require('path')\nconst CSON = require('@rokt33r/season')\nconst migrateFromV6Stor"
},
{
"path": "browser/main/lib/dataApi/resolveStorageNotes.js",
"chars": 1175,
"preview": "const sander = require('sander')\nconst path = require('path')\nconst CSON = require('@rokt33r/season')\n\nfunction resolveS"
},
{
"path": "browser/main/lib/dataApi/toggleStorage.js",
"chars": 781,
"preview": "const _ = require('lodash')\nconst resolveStorageData = require('./resolveStorageData')\n\n/**\n * @param {String} key\n * @p"
},
{
"path": "browser/main/lib/dataApi/updateFolder.js",
"chars": 1323,
"preview": "const _ = require('lodash')\nconst path = require('path')\nconst resolveStorageData = require('./resolveStorageData')\ncons"
},
{
"path": "browser/main/lib/dataApi/updateNote.js",
"chars": 4167,
"preview": "const resolveStorageData = require('./resolveStorageData')\nconst _ = require('lodash')\nconst path = require('path')\ncons"
},
{
"path": "browser/main/lib/dataApi/updateSnippet.js",
"chars": 1270,
"preview": "import fs from 'fs'\nimport consts from 'browser/lib/consts'\n\nfunction updateSnippet(snippet, snippetFile) {\n return new"
},
{
"path": "browser/main/lib/eventEmitter.js",
"chars": 435,
"preview": "const electron = require('electron')\nconst { ipcRenderer, remote } = electron\n\nfunction on(name, listener) {\n ipcRender"
},
{
"path": "browser/main/lib/ipcClient.js",
"chars": 682,
"preview": "import ConfigManager from './ConfigManager'\n\nconst nodeIpc = require('node-ipc')\nconst { remote, ipcRenderer } = require"
},
{
"path": "browser/main/lib/modal.js",
"chars": 1876,
"preview": "import React from 'react'\nimport { Provider } from 'react-redux'\nimport ReactDOM from 'react-dom'\nimport { store } from "
},
{
"path": "browser/main/lib/notify.js",
"chars": 316,
"preview": "const path = require('path')\n\nfunction notify(title, options) {\n if (process.platform === 'win32') {\n options.icon ="
},
{
"path": "browser/main/lib/shortcut.js",
"chars": 337,
"preview": "import ee from 'browser/main/lib/eventEmitter'\n\nmodule.exports = {\n toggleMode: () => {\n ee.emit('topbar:togglemodeb"
},
{
"path": "browser/main/lib/shortcutManager.js",
"chars": 1029,
"preview": "import Mousetrap from 'mousetrap'\nimport CM from 'browser/main/lib/ConfigManager'\nimport ee from 'browser/main/lib/event"
},
{
"path": "browser/main/modals/CreateFolderModal.js",
"chars": 2844,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/modals/CreateFolderModal.styl",
"chars": 1800,
"preview": ".root\n modal()\n width 500px\n height 270px\n overflow hidden\n position relative\n\n.header\n height 80px\n margin-botto"
},
{
"path": "browser/main/modals/CreateMarkdownFromURLModal.js",
"chars": 2920,
"preview": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styl"
},
{
"path": "browser/main/modals/CreateMarkdownFromURLModal.styl",
"chars": 1698,
"preview": ".root\n modal()\n width 500px\n height 270px\n overflow hidden\n position relative\n\n.header\n height 80px\n margin-botto"
},
{
"path": "browser/main/modals/NewNoteModal.js",
"chars": 4490,
"preview": "import React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styles from './NewNoteModal.styl'\nimpor"
},
{
"path": "browser/main/modals/NewNoteModal.styl",
"chars": 1391,
"preview": ".root\n modal()\n max-width 540px\n overflow hidden\n position relative\n\n.header\n height 50px\n font-size 18px\n line-h"
},
{
"path": "browser/main/modals/PreferencesModal/Blog.js",
"chars": 6583,
"preview": "import React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styles from './ConfigTab.styl'\nimport C"
},
{
"path": "browser/main/modals/PreferencesModal/ConfigTab.styl",
"chars": 6434,
"preview": "@import('./Tab')\n\n.container\n display flex\n flex-direction column\n align-items center\n justify-content center\n posi"
},
{
"path": "browser/main/modals/PreferencesModal/Crowdfunding.js",
"chars": 3170,
"preview": "import React from 'react'\nimport CSSModules from 'browser/lib/CSSModules'\nimport styles from './Crowdfunding.styl'\nimpor"
}
]
// ... and 167 more files (download for full content)
About this extraction
This page contains the full source code of the BoostIO/BoostNote-Legacy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 367 files (4.3 MB), approximately 1.2M tokens, and a symbol index with 982 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.