You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-09-05 20:56:22 +02:00
Compare commits
166 Commits
server-v2.
...
android-v2
Author | SHA1 | Date | |
---|---|---|---|
|
9036bc4fd1 | ||
|
0f41a2d00a | ||
|
3ee0c7f440 | ||
|
89ada6432b | ||
|
445d691103 | ||
|
de757026d4 | ||
|
5e8ed8bc24 | ||
|
0a739e099d | ||
|
f3b1f07a75 | ||
|
1b91646d92 | ||
|
885f0e1557 | ||
|
2933b7eb2a | ||
|
9f252ea673 | ||
|
db497ee0a5 | ||
|
31fcd0ed1d | ||
|
5d7d597147 | ||
|
6ebec0cbf8 | ||
|
f4b39633ea | ||
|
4c8e2fba7d | ||
|
288ae1b463 | ||
|
e3c9bcbec6 | ||
|
49180fbf88 | ||
|
486ac6db7b | ||
|
b3fc8fbc93 | ||
|
980190ec09 | ||
|
cebf043284 | ||
|
4c11bbf0da | ||
|
69170dd362 | ||
|
1afcb27601 | ||
|
68469bc1a5 | ||
|
a4e279270b | ||
|
2f7ab7e92e | ||
|
46202beb9b | ||
|
dd705680f1 | ||
|
00163f58d0 | ||
|
df9c460363 | ||
|
603c8338c0 | ||
|
fca5875c21 | ||
|
2941ee0590 | ||
|
5d71787835 | ||
|
2b2070aabe | ||
|
bb464d8a59 | ||
|
00a3014131 | ||
|
f51b0c6d06 | ||
|
01825e6d3e | ||
|
b7bb5acd41 | ||
|
638905e1b8 | ||
|
e05c89aa11 | ||
|
f5f7981dba | ||
|
23d9ba7bf1 | ||
|
095db5d0d2 | ||
|
5718001a33 | ||
|
91907aa5dd | ||
|
d70dca3139 | ||
|
630584c80e | ||
|
b152879086 | ||
|
2238ef5fd0 | ||
|
6bb0318c2f | ||
|
c668bb0370 | ||
|
360293949c | ||
|
8289ce3749 | ||
|
1bb4c38699 | ||
|
bc977bf43f | ||
|
c98edf89fa | ||
|
c6b67126fb | ||
|
c1aa57a72c | ||
|
fdba6c076e | ||
|
f8d0bb472e | ||
|
3a18da49f3 | ||
|
5143a81749 | ||
|
f283970186 | ||
|
abf50565cb | ||
|
287057fea6 | ||
|
8ac6017c02 | ||
|
8cdb73bd65 | ||
|
35e6aaceb7 | ||
|
52287c30bf | ||
|
a9daa4f772 | ||
|
316e31f937 | ||
|
94f4ba3053 | ||
|
8825133675 | ||
|
2fd724911d | ||
|
12704bfb4d | ||
|
070fd0434b | ||
|
a105306dc7 | ||
|
70035f49d1 | ||
|
066dcbc6e4 | ||
|
597454d39d | ||
|
6bc70ed8dc | ||
|
cfce397df8 | ||
|
0274fd1790 | ||
|
47da47e12d | ||
|
7088be01c8 | ||
|
6a1f3ace67 | ||
|
4ae3bec7e4 | ||
|
970c1fb423 | ||
|
810018b41f | ||
|
e0bfa0dbe6 | ||
|
ca7e68ba4b | ||
|
3fcdeb08d9 | ||
|
ebaddfa4a8 | ||
|
2ab049305e | ||
|
98bfb65ead | ||
|
09cbe3cb7c | ||
|
fd322edaab | ||
|
09ed92bc26 | ||
|
fb6069de6d | ||
|
39056ae1aa | ||
|
d031a04a2c | ||
|
ed0f0fae01 | ||
|
c185f00006 | ||
|
3117133be2 | ||
|
b92c650f5d | ||
|
9dbf5e02eb | ||
|
e1016b8af4 | ||
|
3ba4a1de72 | ||
|
a683f12622 | ||
|
89184a3f9f | ||
|
24dbede6c1 | ||
|
70e623e741 | ||
|
5c77317735 | ||
|
9684b38f7e | ||
|
3dfe43204d | ||
|
16148b2255 | ||
|
df14ffdee2 | ||
|
f6ed5eb064 | ||
|
a14115bfd7 | ||
|
dd68d6cf2c | ||
|
7d3555d62c | ||
|
af472528a2 | ||
|
5c8b0ec1cb | ||
|
5e20e890d8 | ||
|
50dc656f65 | ||
|
b36cf46a06 | ||
|
1781334374 | ||
|
71140a638f | ||
|
6ba0fce237 | ||
|
c033a343c1 | ||
|
898c96a566 | ||
|
b83fa133b2 | ||
|
ec7fec8b59 | ||
|
b2fb4f2ea2 | ||
|
c74e51a58e | ||
|
7cba4be498 | ||
|
19b396f2ec | ||
|
4ed7c340a0 | ||
|
fe770917fd | ||
|
f2168d3bca | ||
|
5d4ebc6e14 | ||
|
3cf0841775 | ||
|
e5b6ecc50b | ||
|
f451633a51 | ||
|
e813d15ef2 | ||
|
b5b02d8d7b | ||
|
2660ff3af6 | ||
|
59cdcaf8d1 | ||
|
d6f4ebfff5 | ||
|
e03655bc4d | ||
|
9426a2170c | ||
|
b47a541976 | ||
|
4df5ad5c7a | ||
|
b214c8e42a | ||
|
360ac40f50 | ||
|
7c00bc5d0e | ||
|
6f0f3d586e | ||
|
863d894af1 |
@@ -1,12 +1,19 @@
|
||||
_mydocs/
|
||||
_releases/
|
||||
.git/
|
||||
.yarn/cache/
|
||||
**/.DS_Store
|
||||
**/node_modules
|
||||
Assets/
|
||||
.git/
|
||||
_releases/
|
||||
packages/app-desktop
|
||||
packages/app-cli
|
||||
packages/app-mobile
|
||||
packages/app-clipper
|
||||
packages/generator-joplin
|
||||
packages/plugin-repo-cli
|
||||
docs/
|
||||
lerna-debug.log
|
||||
packages/app-cli/
|
||||
packages/app-clipper/
|
||||
packages/app-desktop/
|
||||
packages/app-mobile/
|
||||
packages/generator-joplin/
|
||||
packages/plugin-repo-cli/
|
||||
packages/server/db-*.sqlite
|
||||
packages/server/temp
|
||||
packages/server/dist/
|
||||
packages/server/logs/
|
||||
packages/server/temp/
|
||||
|
@@ -235,6 +235,9 @@ packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map
|
||||
packages/app-desktop/gui/ErrorBoundary.d.ts
|
||||
packages/app-desktop/gui/ErrorBoundary.js
|
||||
packages/app-desktop/gui/ErrorBoundary.js.map
|
||||
packages/app-desktop/gui/FolderIconBox.d.ts
|
||||
packages/app-desktop/gui/FolderIconBox.js
|
||||
packages/app-desktop/gui/FolderIconBox.js.map
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.d.ts
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js.map
|
||||
@@ -1147,6 +1150,9 @@ packages/lib/models/NoteTag.js.map
|
||||
packages/lib/models/Resource.d.ts
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/Resource.js.map
|
||||
packages/lib/models/Resource.test.d.ts
|
||||
packages/lib/models/Resource.test.js
|
||||
packages/lib/models/Resource.test.js.map
|
||||
packages/lib/models/ResourceLocalState.d.ts
|
||||
packages/lib/models/ResourceLocalState.js
|
||||
packages/lib/models/ResourceLocalState.js.map
|
||||
@@ -1927,6 +1933,9 @@ packages/renderer/headerAnchor.js.map
|
||||
packages/renderer/htmlUtils.d.ts
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.js.map
|
||||
packages/renderer/htmlUtils.test.d.ts
|
||||
packages/renderer/htmlUtils.test.js
|
||||
packages/renderer/htmlUtils.test.js.map
|
||||
packages/renderer/index.d.ts
|
||||
packages/renderer/index.js
|
||||
packages/renderer/index.js.map
|
||||
|
1
.github/scripts/run_ci.sh
vendored
1
.github/scripts/run_ci.sh
vendored
@@ -37,6 +37,7 @@ echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME"
|
||||
echo "GITHUB_REF=$GITHUB_REF"
|
||||
echo "RUNNER_OS=$RUNNER_OS"
|
||||
echo "GIT_TAG_NAME=$GIT_TAG_NAME"
|
||||
echo "BUILD_SEQUENCIAL=$BUILD_SEQUENCIAL"
|
||||
|
||||
echo "IS_CONTINUOUS_INTEGRATION=$IS_CONTINUOUS_INTEGRATION"
|
||||
echo "IS_PULL_REQUEST=$IS_PULL_REQUEST"
|
||||
|
25
.github/stale.yml
vendored
25
.github/stale.yml
vendored
@@ -1,25 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "good first issue"
|
||||
- "upstream"
|
||||
- "backlog"
|
||||
- "high"
|
||||
- "medium"
|
||||
- "spec"
|
||||
- "cannot reproduce"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
|
||||
You may comment on the issue and I will leave it open.
|
||||
Thank you for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
|
||||
only: issues
|
23
.github/workflows/close-stale-issues.yml
vendored
Normal file
23
.github/workflows/close-stale-issues.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: 'Close stale issues'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 16 * * *'
|
||||
permissions:
|
||||
issues: write
|
||||
jobs:
|
||||
ProcessStaleIssues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
# Use this to do a dry run from a pull request
|
||||
# debug-only: true
|
||||
stale-issue-message: "Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may comment on the issue and I will leave it open. Thank you for your contributions."
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
operations-per-run: 1000
|
||||
exempt-issue-labels: 'good first issue,upstream,backlog,high,medium,spec,cannot reproduce,enhancement'
|
||||
stale-issue-label: 'stale'
|
||||
close-issue-message: 'Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, feel free to create a new issue with up-to-date information.'
|
||||
# Don't process pull requests at all
|
||||
days-before-pr-stale: -1
|
20
.github/workflows/github-actions-main.yml
vendored
20
.github/workflows/github-actions-main.yml
vendored
@@ -74,6 +74,7 @@ jobs:
|
||||
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
run: |
|
||||
"${GITHUB_WORKSPACE}/.github/scripts/run_ci.sh"
|
||||
|
||||
@@ -84,10 +85,21 @@ jobs:
|
||||
CSC_LINK: ${{ secrets.WINDOWS_CSC_LINK }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
# To ensure that the operations stop on failure, all commands
|
||||
# should be on one line with "&&" in between.
|
||||
run: |
|
||||
yarn install
|
||||
cd packages/app-desktop
|
||||
yarn run dist
|
||||
yarn install && cd packages/app-desktop && yarn run dist
|
||||
|
||||
# Build and package the Windows app, without publishing it, just to
|
||||
# verify that the build process hasn't been broken.
|
||||
- name: Build Windows app (no publishing)
|
||||
if: runner.os == 'Windows' && !startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
run: |
|
||||
yarn install && cd packages/app-desktop && yarn run dist --publish=never
|
||||
|
||||
ServerDockerImage:
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -122,6 +134,8 @@ jobs:
|
||||
corepack enable
|
||||
|
||||
- name: Build Docker Image
|
||||
env:
|
||||
BUILD_SEQUENCIAL: 1
|
||||
run: |
|
||||
yarn install
|
||||
yarn run buildServerDocker --tag-name server-v0.0.0
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@@ -225,6 +225,9 @@ packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.js.map
|
||||
packages/app-desktop/gui/ErrorBoundary.d.ts
|
||||
packages/app-desktop/gui/ErrorBoundary.js
|
||||
packages/app-desktop/gui/ErrorBoundary.js.map
|
||||
packages/app-desktop/gui/FolderIconBox.d.ts
|
||||
packages/app-desktop/gui/FolderIconBox.js
|
||||
packages/app-desktop/gui/FolderIconBox.js.map
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.d.ts
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js.map
|
||||
@@ -1137,6 +1140,9 @@ packages/lib/models/NoteTag.js.map
|
||||
packages/lib/models/Resource.d.ts
|
||||
packages/lib/models/Resource.js
|
||||
packages/lib/models/Resource.js.map
|
||||
packages/lib/models/Resource.test.d.ts
|
||||
packages/lib/models/Resource.test.js
|
||||
packages/lib/models/Resource.test.js.map
|
||||
packages/lib/models/ResourceLocalState.d.ts
|
||||
packages/lib/models/ResourceLocalState.js
|
||||
packages/lib/models/ResourceLocalState.js.map
|
||||
@@ -1917,6 +1923,9 @@ packages/renderer/headerAnchor.js.map
|
||||
packages/renderer/htmlUtils.d.ts
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.js.map
|
||||
packages/renderer/htmlUtils.test.d.ts
|
||||
packages/renderer/htmlUtils.test.js
|
||||
packages/renderer/htmlUtils.test.js.map
|
||||
packages/renderer/index.d.ts
|
||||
packages/renderer/index.js
|
||||
packages/renderer/index.js.map
|
||||
|
@@ -672,7 +672,7 @@ footer .bottom-links-row p {
|
||||
|
||||
.news-page img,
|
||||
.news-item-page img {
|
||||
max-width: 650px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
@@ -728,6 +728,23 @@ footer .bottom-links-row p {
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
MEDIUM VIEW
|
||||
- Make menu bar elements smaller and closer to each others
|
||||
so that everything fit.
|
||||
*****************************************************************/
|
||||
|
||||
@media (max-width: 990px) {
|
||||
#nav-section > .container {
|
||||
max-width: 960px;
|
||||
}
|
||||
|
||||
#nav-section .button-link {
|
||||
padding: 4px 12px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
NARROW VIEW
|
||||
- Top right menu is displayed
|
||||
@@ -740,6 +757,23 @@ footer .bottom-links-row p {
|
||||
padding-bottom: 130px;
|
||||
}
|
||||
|
||||
#menu-mobile .social-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#menu-mobile .social-links a {
|
||||
margin-left: 15px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#menu-mobile .social-links .social-link-mastodon,
|
||||
#menu-mobile .social-links .social-link-reddit,
|
||||
#menu-mobile .social-links .social-link-patreon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.front-page h1 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
@@ -857,7 +891,7 @@ footer .bottom-links-row p {
|
||||
}
|
||||
|
||||
#menu-mobile .button-link {
|
||||
padding: 10px 15px;
|
||||
padding: 4px 12px;
|
||||
font-size: 16px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
@@ -1,15 +1,6 @@
|
||||
<footer class="darkblue-bg">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-12 social-links">
|
||||
<a href="https://twitter.com/joplinapp" title="Joplin Twitter feed"><i class="fab fa-twitter"></i></a>
|
||||
<a href="https://mastodon.social/@joplinapp" title="Joplin Mastodon feed"><i class="fab fa-mastodon"></i></a>
|
||||
<a href="https://www.patreon.com/joplin" title="Joplin Patreon"><i class="fab fa-patreon"></i></a>
|
||||
<a href="https://discord.gg/VSj7AFHvpq" title="Joplin Discord chat"><i class="fab fa-discord"></i></a>
|
||||
<a href="https://www.reddit.com/r/joplinapp/" title="Joplin Subreddit"><i class="fab fa-reddit"></i></a>
|
||||
<a href="https://github.com/laurent22/joplin/" title="Joplin GitHub repository"><i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{> socialFeeds}}
|
||||
|
||||
<div class="row bottom-links-row">
|
||||
<div class="col-12 col-md-6">
|
||||
|
@@ -12,7 +12,8 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-9 text-right d-none d-md-block">
|
||||
<a href="{{baseUrl}}/news/" class="fw500">What's New</a>
|
||||
<a href="https://twitter.com/joplinapp" title="Joplin Twitter feed" class="fw500"><i class="fab fa-twitter"></i></a>
|
||||
<a href="{{baseUrl}}/news/" class="fw500">News</a>
|
||||
<a href="{{baseUrl}}/help/" class="fw500">Help</a>
|
||||
<a href="{{forumUrl}}" class="fw500">Forum</a>
|
||||
{{#showJoplinCloudLinks}}
|
||||
@@ -43,7 +44,7 @@
|
||||
</div>
|
||||
|
||||
<div class="text-center menu-mobile-top">
|
||||
<a href="{{baseUrl}}/news/" class="fw500 mobile-menu-link">What's New</a>
|
||||
<a href="{{baseUrl}}/news/" class="fw500 mobile-menu-link">News</a>
|
||||
<a href="{{baseUrl}}/help/" class="fw500 mobile-menu-link">Help</a>
|
||||
<a href="{{forumUrl}}" class="fw500 mobile-menu-link">Forum</a>
|
||||
</div>
|
||||
@@ -59,6 +60,8 @@
|
||||
{{#showToc}}
|
||||
<div id="toc-mobile">{{{tocHtml}}}</div>
|
||||
{{/showToc}}
|
||||
|
||||
{{> socialFeeds}}
|
||||
|
||||
<div>
|
||||
<p class="light-blue mobile-menu-link-bottom text-center">
|
||||
|
10
Assets/WebsiteAssets/templates/partials/socialFeeds.mustache
Normal file
10
Assets/WebsiteAssets/templates/partials/socialFeeds.mustache
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-12 social-links">
|
||||
<a class="social-link-twitter" href="https://twitter.com/joplinapp" title="Joplin Twitter feed"><i class="fab fa-twitter"></i></a>
|
||||
<a class="social-link-mastodon" href="https://mastodon.social/@joplinapp" title="Joplin Mastodon feed"><i class="fab fa-mastodon"></i></a>
|
||||
<a class="social-link-patreon" href="https://www.patreon.com/joplin" title="Joplin Patreon"><i class="fab fa-patreon"></i></a>
|
||||
<a class="social-link-discord" href="https://discord.gg/VSj7AFHvpq" title="Joplin Discord chat"><i class="fab fa-discord"></i></a>
|
||||
<a class="social-link-reddit" href="https://www.reddit.com/r/joplinapp/" title="Joplin Subreddit"><i class="fab fa-reddit"></i></a>
|
||||
<a class="social-link-github" href="https://github.com/laurent22/joplin/" title="Joplin GitHub repository"><i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
</div>
|
9
BUILD.md
9
BUILD.md
@@ -18,10 +18,9 @@ There are also a few forks of existing packages under the "fork-*" name.
|
||||
|
||||
## Required dependencies
|
||||
|
||||
- Install node 16+ - https://nodejs.org/en/
|
||||
- [Enable yarn](https://yarnpkg.com/getting-started/install): `corepack enable`
|
||||
- macOS: Install Cocoapods - `brew install cocoapods`
|
||||
- Windows: Install Windows Build Tools - `yarn install -g windows-build-tools --vs2015`
|
||||
- Install Node 16+. On Windows, also install the build tools - https://nodejs.org/en/
|
||||
- [Enable Yarn](https://yarnpkg.com/getting-started/install): `corepack enable`
|
||||
- macOS: Install Cocoapods - `brew install cocoapods`. Apple Silicon [may require libvips](https://github.com/laurent22/joplin/pull/5966#issuecomment-1007158597) - `brew install vips`.
|
||||
- Linux: Install dependencies - `sudo apt install build-essential libnss3 libsecret-1-dev python rsync`
|
||||
|
||||
## Building
|
||||
@@ -60,7 +59,7 @@ Normally the **bundler** should start automatically with the application. If it
|
||||
## Building the clipper
|
||||
|
||||
cd packages/app-clipper/popup
|
||||
yarn run watch # To watch for changes
|
||||
npm run watch # To watch for changes
|
||||
|
||||
To test the extension please refer to the relevant pages for each browser: [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension#Trying_it_out) / [Chrome](https://developer.chrome.com/docs/extensions/mv3/getstarted/). Please note that the extension in dev mode will only connect to a dev instance of the desktop app (and vice-versa).
|
||||
|
||||
|
@@ -31,12 +31,12 @@ Joplin is available in multiple languages thanks to the help of its users. You c
|
||||
|
||||
If you want to start contributing to the project's code, please follow these guidelines before creating a pull request:
|
||||
|
||||
- Explain WHY you want to add this change. Explain it inside the pull request and you may link to an issue for additional information, but the PR should gives a clear overview of why you want to add this.
|
||||
- Explain WHY you want to add this change. Explain it inside the pull request and you may link to an issue for additional information, but the PR should give a clear overview of why you want to add this.
|
||||
- Bug fixes are always welcome. Start by reviewing the [list of bugs](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
||||
- A good way to easily start contributing is to pick and work on a [good first issue](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). We try to make these issues as clear as possible and provide basic info on how the code should be changed, and if something is unclear feel free to ask for more information on the issue.
|
||||
- Before adding a new feature, ask about it in the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue) or the [Joplin Forum](https://discourse.joplinapp.org/), or check if existing discussions exist to make sure the new functionality is desired.
|
||||
- **Changes that will consist in more than 50 lines of code should be discussed the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
|
||||
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, makes sure it still work in the other apps. Usually it does, but keep this in mind.
|
||||
- **Changes that will consist of more than 50 lines of code should be discussed on the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
|
||||
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, make sure it still works in the other apps. Usually it does, but keep this in mind.
|
||||
- Pull requests that make many changes using an automated tool, like for spell fixing, styling, etc. will not be accepted. An exception would be if the changes have been discussed in the forum and someone has agreed to review **and test** the pull request.
|
||||
- Pull requests that make address multiple issues will most likely stall and eventually be closed. This is because we might be fine with one of the changes but not with others and untangling that kind of pull request is too much hassle both for maintainers and the person who submitted it. So most of the time someone gives up and the PR gets closed. So please keep the pull request focused on one issue.
|
||||
|
||||
|
@@ -1,88 +1,79 @@
|
||||
FROM node:16-bullseye
|
||||
# =============================================================================
|
||||
# Build stage
|
||||
# =============================================================================
|
||||
|
||||
FROM node:16-bullseye AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
python \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Download the init tool Tini and make it executable for use in the final image
|
||||
ADD https://github.com/krallin/tini/releases/download/v0.19.0/tini-static /tini
|
||||
RUN chmod u+x /tini
|
||||
|
||||
# Enables Yarn
|
||||
RUN corepack enable
|
||||
|
||||
RUN echo "Node: $(node --version)"
|
||||
RUN echo "Npm: $(npm --version)"
|
||||
RUN echo "Yarn: $(yarn --version)"
|
||||
WORKDIR /build
|
||||
|
||||
COPY .yarn/plugins ./.yarn/plugins
|
||||
COPY .yarn/releases ./.yarn/releases
|
||||
COPY package.json .
|
||||
COPY .yarnrc.yml .
|
||||
COPY yarn.lock .
|
||||
COPY gulpfile.js .
|
||||
COPY tsconfig.json .
|
||||
COPY packages/turndown ./packages/turndown
|
||||
COPY packages/turndown-plugin-gfm ./packages/turndown-plugin-gfm
|
||||
COPY packages/fork-htmlparser2 ./packages/fork-htmlparser2
|
||||
COPY packages/server/package*.json ./packages/server/
|
||||
COPY packages/fork-sax ./packages/fork-sax
|
||||
COPY packages/fork-uslug ./packages/fork-uslug
|
||||
COPY packages/htmlpack ./packages/htmlpack
|
||||
COPY packages/renderer ./packages/renderer
|
||||
COPY packages/tools ./packages/tools
|
||||
COPY packages/lib ./packages/lib
|
||||
COPY packages/server ./packages/server
|
||||
|
||||
# For some reason there's both a .yarn/cache and .yarn/berry/cache that are
|
||||
# being generated, and both have the same content. Not clear why it does this
|
||||
# but we can delete it anyway. We can delete the cache because we use
|
||||
# `nodeLinker: node-modules`. If we ever implement Zero Install, we'll need to
|
||||
# keep the cache.
|
||||
#
|
||||
# Note that `yarn install` ignores `NODE_ENV=production` and will install dev
|
||||
# dependencies too, but this is fine because we need them to build the app.
|
||||
|
||||
RUN BUILD_SEQUENCIAL=1 yarn install --inline-builds \
|
||||
&& yarn cache clean \
|
||||
&& rm -rf .yarn/berry
|
||||
|
||||
# =============================================================================
|
||||
# Final stage - we copy only the relevant files from the build stage and start
|
||||
# from a smaller base image.
|
||||
# =============================================================================
|
||||
|
||||
FROM node:16-bullseye-slim
|
||||
|
||||
ARG user=joplin
|
||||
|
||||
RUN useradd --create-home --shell /bin/bash $user
|
||||
|
||||
USER $user
|
||||
|
||||
ENV NODE_ENV development
|
||||
|
||||
WORKDIR /home/$user
|
||||
|
||||
RUN mkdir /home/$user/logs
|
||||
|
||||
# Install the root scripts but don't run postinstall (which would bootstrap
|
||||
# and build TypeScript files, but we don't have the TypeScript files at
|
||||
# this point)
|
||||
|
||||
COPY --chown=$user:$user package*.json ./
|
||||
COPY --chown=$user:$user .yarn ./.yarn
|
||||
COPY --chown=$user:$user .yarnrc.yml .
|
||||
COPY --chown=$user:$user yarn.lock .
|
||||
|
||||
RUN yarn install --inline-builds --mode=skip-build
|
||||
|
||||
# To take advantage of the Docker cache, we first copy all the package.json
|
||||
# and package-lock.json files, as they rarely change, and then bootstrap
|
||||
# all the packages.
|
||||
#
|
||||
# Note that bootstrapping the packages will run all the postinstall
|
||||
# scripts, which means that for packages that have such scripts, we need to
|
||||
# copy all the files.
|
||||
#
|
||||
# We can't run boostrap with "--ignore-scripts" because that would
|
||||
# prevent certain sub-packages, such as sqlite3, from being built
|
||||
|
||||
COPY --chown=$user:$user packages/fork-sax/package*.json ./packages/fork-sax/
|
||||
COPY --chown=$user:$user packages/fork-uslug/package*.json ./packages/fork-uslug/
|
||||
COPY --chown=$user:$user packages/htmlpack/package*.json ./packages/htmlpack/
|
||||
COPY --chown=$user:$user packages/renderer/package*.json ./packages/renderer/
|
||||
COPY --chown=$user:$user packages/tools/package*.json ./packages/tools/
|
||||
COPY --chown=$user:$user packages/lib/package*.json ./packages/lib/
|
||||
COPY --chown=$user:$user tsconfig.json .
|
||||
|
||||
# The following have postinstall scripts so we need to copy all the files.
|
||||
# Since they should rarely change this is not an issue
|
||||
|
||||
COPY --chown=$user:$user packages/turndown ./packages/turndown
|
||||
COPY --chown=$user:$user packages/turndown-plugin-gfm ./packages/turndown-plugin-gfm
|
||||
COPY --chown=$user:$user packages/fork-htmlparser2 ./packages/fork-htmlparser2
|
||||
COPY --chown=$user:$user packages/server/package*.json ./packages/server/
|
||||
|
||||
# Then bootstrap only, without compiling the TypeScript files
|
||||
|
||||
RUN yarn install --inline-builds --mode=skip-build
|
||||
|
||||
# Now copy the source files. Put lib and server last as they are more likely to change.
|
||||
|
||||
COPY --chown=$user:$user packages/fork-sax ./packages/fork-sax
|
||||
COPY --chown=$user:$user packages/fork-uslug ./packages/fork-uslug
|
||||
COPY --chown=$user:$user packages/htmlpack ./packages/htmlpack
|
||||
COPY --chown=$user:$user packages/renderer ./packages/renderer
|
||||
COPY --chown=$user:$user packages/tools ./packages/tools
|
||||
COPY --chown=$user:$user packages/lib ./packages/lib
|
||||
COPY --chown=$user:$user packages/server ./packages/server
|
||||
|
||||
# Finally build everything, in particular the TypeScript files.
|
||||
|
||||
RUN yarn run build
|
||||
COPY --chown=$user:$user --from=builder /build/packages /home/$user/packages
|
||||
COPY --chown=$user:$user --from=builder /tini /usr/local/bin/tini
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV RUNNING_IN_DOCKER=1
|
||||
EXPOSE ${APP_PORT}
|
||||
|
||||
CMD [ "yarn", "--prefix", "packages/server", "start" ]
|
||||
# Use Tini to start Joplin Server:
|
||||
# https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#handling-kernel-signals
|
||||
WORKDIR /home/$user/packages/server
|
||||
ENTRYPOINT ["tini", "--"]
|
||||
CMD ["node", "dist/app.js"]
|
||||
|
||||
# Build-time metadata
|
||||
# https://github.com/opencontainers/image-spec/blob/master/annotations.md
|
||||
|
@@ -219,6 +219,8 @@ then
|
||||
Type=Application
|
||||
Categories=Office;
|
||||
MimeType=x-scheme-handler/joplin;
|
||||
X-GNOME-SingleWindow=true // should be removed eventually as it was upstream to be an XDG specification
|
||||
SingleMainWindow=true
|
||||
EOF
|
||||
|
||||
# Update application icons
|
||||
|
15
LICENSE
15
LICENSE
@@ -1,15 +1,20 @@
|
||||
All code in this repository is licensed under the MIT License **unless a
|
||||
directory contains a LICENSE file**, in which case that LICENSE file applies to
|
||||
the code in that sub-directory.
|
||||
directory contains a LICENSE or LICENSE.md file**, in which case that file
|
||||
applies to the code in that sub-directory.
|
||||
|
||||
For example, packages/fork-sax contains a ISC LICENSE file, thus all code under
|
||||
the packages/fork-sax directory is licensed under ISC.
|
||||
For example, packages/server contains a LICENSE.md file, thus all code under the
|
||||
packages/server directory is licensed under that license.
|
||||
|
||||
For example, packages/app-cli does NOT contain a LICENSE file, thus all code
|
||||
under that directory is licensed under the default license, which is MIT.
|
||||
|
||||
* * *
|
||||
|
||||
Joplin® is a trademark of Cozic Ltd registered in the European Union, with
|
||||
filing number 018544315.
|
||||
|
||||
* * *
|
||||
|
||||
Logo and Icon License
|
||||
|
||||
The Joplin logos and icons are copyright (c) Laurent Cozic, all rights reserved,
|
||||
@@ -20,7 +25,7 @@ icons please contact the author in order to get a permission.
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2021 Laurent Cozic
|
||||
Copyright (c) 2016-2022 Laurent Cozic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
120
README.md
120
README.md
@@ -6,11 +6,13 @@
|
||||
|
||||
<img width="64" src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/LinuxIcons/256x256.png" align="left" /> **Joplin** is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](#markdown).
|
||||
|
||||
Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
|
||||
Notes exported from Evernote [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
|
||||
|
||||
The notes can be [synchronised](#synchronisation) with various cloud services including [Nextcloud](https://nextcloud.com/), Dropbox, OneDrive, WebDAV or the file system (for example with a network directory). When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around.
|
||||
The notes can be securely [synchronised](#synchronisation) using [end-to-end encryption](#encryption) with various cloud services including Nextcloud, Dropbox, OneDrive and [Joplin Cloud](https://joplinapp.org/plans/).
|
||||
|
||||
The application is available for Windows, Linux, macOS, Android and iOS (the terminal app also works on FreeBSD). A [Web Clipper](https://github.com/laurent22/joplin/blob/dev/readme/clipper.md), to save web pages and screenshots from your browser, is also available for [Firefox](https://addons.mozilla.org/firefox/addon/joplin-web-clipper/) and [Chrome](https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek?hl=en-GB).
|
||||
Full text search is available on all platforms to quickly find the information you need. The app can be customised using plugins and themes, and you can also easily create your own.
|
||||
|
||||
The application is available for Windows, Linux, macOS, Android and iOS. A [Web Clipper](https://github.com/laurent22/joplin/blob/dev/readme/clipper.md), to save web pages and screenshots from your browser, is also available for [Firefox](https://addons.mozilla.org/firefox/addon/joplin-web-clipper/) and [Chrome](https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek?hl=en-GB).
|
||||
|
||||
<div class="top-screenshot"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/home-top-img.png" style="max-width: 100%; max-height: 35em;"></div>
|
||||
|
||||
@@ -76,9 +78,10 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/3061769?s=96&v=4"/></br>[c-nagy](https://github.com/c-nagy) | <img width="50" src="https://avatars2.githubusercontent.com/u/70780798?s=96&v=4"/></br>[cabottech](https://github.com/cabottech) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/4862947?s=96&v=4"/></br>[chrootlogin](https://github.com/chrootlogin) | <img width="50" src="https://avatars2.githubusercontent.com/u/82579431?s=96&v=4"/></br>[clmntsl](https://github.com/clmntsl) | <img width="50" src="https://avatars2.githubusercontent.com/u/808091?s=96&v=4"/></br>[cuongtransc](https://github.com/cuongtransc) | <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1439535?s=96&v=4"/></br>[fbloise](https://github.com/fbloise) | <img width="50" src="https://avatars2.githubusercontent.com/u/38898566?s=96&v=4"/></br>[h4sh5](https://github.com/h4sh5) | <img width="50" src="https://avatars2.githubusercontent.com/u/3266447?s=96&v=4"/></br>[iamwillbar](https://github.com/iamwillbar) | <img width="50" src="https://avatars2.githubusercontent.com/u/37297218?s=96&v=4"/></br>[Jesssullivan](https://github.com/Jesssullivan) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1248504?s=96&v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars2.githubusercontent.com/u/1168659?s=96&v=4"/></br>[nicholashead](https://github.com/nicholashead) | <img width="50" src="https://avatars2.githubusercontent.com/u/5782817?s=96&v=4"/></br>[piccobit](https://github.com/piccobit) | <img width="50" src="https://avatars2.githubusercontent.com/u/47742?s=96&v=4"/></br>[ravenscroftj](https://github.com/ravenscroftj) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/73081837?s=96&v=4"/></br>[thismarty](https://github.com/thismarty) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[thomasbroussard](https://github.com/thomasbroussard) | | |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1248504?s=96&v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars2.githubusercontent.com/u/5588131?s=96&v=4"/></br>[kianenigma](https://github.com/kianenigma) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars2.githubusercontent.com/u/1168659?s=96&v=4"/></br>[nicholashead](https://github.com/nicholashead) | <img width="50" src="https://avatars2.githubusercontent.com/u/5782817?s=96&v=4"/></br>[piccobit](https://github.com/piccobit) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/47742?s=96&v=4"/></br>[ravenscroftj](https://github.com/ravenscroftj) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/73081837?s=96&v=4"/></br>[thismarty](https://github.com/thismarty) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[thomasbroussard](https://github.com/thomasbroussard) |
|
||||
| | | | |
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
|
||||
<!-- TOC -->
|
||||
@@ -125,6 +128,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
- Development
|
||||
|
||||
- [How to build the apps](https://github.com/laurent22/joplin/blob/dev/BUILD.md)
|
||||
- [Writing a technical spec](https://github.com/laurent22/joplin/blob/dev/readme/technical_spec.md)
|
||||
- [End-to-end encryption spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/e2ee.md)
|
||||
- [Note History spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/history.md)
|
||||
- [Sync Lock spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md)
|
||||
@@ -135,11 +139,11 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
- [Server: Delta Sync](https://github.com/laurent22/joplin/blob/dev/readme/spec/server_delta_sync.md)
|
||||
- [Server: Sharing](https://github.com/laurent22/joplin/blob/dev/readme/spec/server_sharing.md)
|
||||
|
||||
- Google Summer of Code 2021
|
||||
- Google Summer of Code 2022
|
||||
|
||||
- [Google Summer of Code 2021](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2021/index.md)
|
||||
- [How to submit a GSoC pull request](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2021/pull_request_guidelines.md)
|
||||
- [Project Ideas](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2021/ideas.md)
|
||||
- [Google Summer of Code 2022](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2022/index.md)
|
||||
- [How to submit a GSoC pull request](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2022/pull_request_guidelines.md)
|
||||
- [Project Ideas](https://github.com/laurent22/joplin/blob/dev/readme/gsoc2022/ideas.md)
|
||||
|
||||
- About
|
||||
|
||||
@@ -300,10 +304,10 @@ To add a **Bucket Policy** from the AWS S3 Web Console, navigate to the **Permis
|
||||
{
|
||||
"Sid": "VisualEditor0",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": [
|
||||
"s3:ListBucket",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteObjectVersion",
|
||||
"s3:PutObject"
|
||||
@@ -472,24 +476,6 @@ Notes are sorted by "relevance". Currently it means the notes that contain the r
|
||||
|
||||
In the desktop application, press <kbd>Ctrl+P</kbd> or <kbd>Cmd+P</kbd> and type a note title or part of its content to jump to it. Or type <kbd>#</kbd> followed by a tag name, or <kbd>@</kbd> followed by a notebook name.
|
||||
|
||||
# Privacy
|
||||
|
||||
Joplin values your privacy and security by giving you complete control over your information and digital footprint.
|
||||
|
||||
Joplin applications do not send any data to any service without your authorisation. Any data that Joplin saves, such as notes or images, are saved to your own device and you are free to delete this data at any time.
|
||||
|
||||
Joplin has many modern features, some of which use third-party services. You can disable any or all of these features in the application settings. These features are:
|
||||
|
||||
|Feature | Description | Default|
|
||||
|--------|-------------|--------|
|
||||
|Auto-update|Joplin periodically connects to GitHub to check for new releases.|Enabled|
|
||||
|Geo-location|Joplin saves geo-location information in note properties when you create a note.|Enabled|
|
||||
|Synchronisation|Joplin supports synchronisation of your notes across multiple devices. If you choose to synchronise with a third-party, such as OneDrive, the notes will be sent to your OneDrive account, in which case the third-party privacy policy applies.|Disabled|
|
||||
|
||||
Joplin is developed as an open-source application and the source code is freely available online to inspect.
|
||||
|
||||
For any question about Joplin privacy, please leave a message on the [Joplin Forum](https://discourse.joplinapp.org/).
|
||||
|
||||
# Donations
|
||||
|
||||
Donations to Joplin support the development of the project. Developing quality applications mostly takes time, but there are also some expenses, such as digital certificates to sign the applications, app store fees, hosting, etc. Most of all, your donation will make it possible to keep up the current development standard.
|
||||
@@ -530,47 +516,47 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 93%
|
||||
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 27%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 67%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 53%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 93%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:s.robin@tutanota.de) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 51%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 26%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 65%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 51%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Benjamin Weis](mailto:benjamin.weis@gmx.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 50%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/us.png" width="16px"/> | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Mora](mailto:francisco.m.collao@gmail.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 30%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 93%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 100%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 34%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Albano Battistella](mailto:albano_battistella@hotmail.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 81%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 85%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | Alexander Dawson | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 64%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 84%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Felipe Viggiano](mailto:felipeviggiano@gmail.com) | 94%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 84%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 59%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 93%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Mora](mailto:francisco.m.collao@gmail.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 29%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 98%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 33%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Albano Battistella](mailto:albano_battistella@hotmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 76%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 83%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | Alexander Dawson | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 63%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 82%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Felipe Viggiano](mailto:felipeviggiano@gmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 82%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 58%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 43%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vi.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 93%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 83%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 87%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 93%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 76%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [南宫小骏](mailto:jackytsu@vip.qq.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [SiderealArt](mailto:nelson22768384@gmail.com) | 90%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 41%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 81%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 85%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 74%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [horaceyoung](mailto:yonghaoharry@gmail.com) | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [SiderealArt](mailto:nelson22768384@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 87%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Contributors
|
||||
|
16
gulpfile.js
16
gulpfile.js
@@ -20,6 +20,22 @@ const tasks = {
|
||||
await utils.execCommandVerbose('git', ['push']);
|
||||
},
|
||||
},
|
||||
build: {
|
||||
fn: async () => {
|
||||
// Building everything in parallel seems to be unreliable on CI as
|
||||
// certain scripts randomly fail with missing files or folder, or
|
||||
// cannot delete certain directories (eg. copyPluginAssets or
|
||||
// copyApplicationAssets). Because of this, on CI, we run the build
|
||||
// sequencially. Locally we run it in parallel, which is much
|
||||
// faster, especially when having to rebuild after adding a
|
||||
// dependency.
|
||||
if (process.env.BUILD_SEQUENCIAL === '1') {
|
||||
await utils.execCommandVerbose('yarn', ['run', 'buildSequential']);
|
||||
} else {
|
||||
await utils.execCommandVerbose('yarn', ['run', 'buildParallel']);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
utils.registerGulpTasks(gulp, tasks);
|
||||
|
@@ -12,7 +12,8 @@
|
||||
"node": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn workspaces foreach --verbose --interlaced --parallel run build && yarn run tsc",
|
||||
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 run build && yarn run tsc",
|
||||
"buildSequential": "yarn workspaces foreach --verbose --interlaced run build && yarn run tsc",
|
||||
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
|
||||
"buildCommandIndex": "gulp buildCommandIndex",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
|
||||
@@ -28,9 +29,9 @@
|
||||
"linter-ci": "eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
|
||||
"postinstall": "yarn run build",
|
||||
"publishAll": "git pull && yarn run build && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
|
||||
"releaseAndroid": "yarn run build && export PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" && node packages/tools/release-android.js",
|
||||
"postinstall": "gulp build",
|
||||
"publishAll": "git pull && yarn run buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
|
||||
"releaseAndroid": "PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" node packages/tools/release-android.js",
|
||||
"releaseAndroidClean": "node packages/tools/release-android.js",
|
||||
"releaseCli": "node packages/tools/release-cli.js",
|
||||
"releaseClipper": "node packages/tools/release-clipper.js",
|
||||
|
@@ -0,0 +1,4 @@
|
||||
<img src=":/39e66095b2cd427e9f13464487514d2e" alt="">
|
||||
<img src=":/39e66095b2cd427e9f13464487514d2e" alt="" title="some-title">
|
||||
<img src=":/39e66095b2cd427e9f13464487514d2e" alt="some-alt-text">
|
||||
<img src=":/39e66095b2cd427e9f13464487514d2e" alt="some-alt-text" title="some-title">
|
@@ -0,0 +1,4 @@
|
||||

|
||||

|
||||

|
||||

|
@@ -19,7 +19,7 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>Joplin Web Clipper</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
2
packages/app-desktop/.gitignore
vendored
2
packages/app-desktop/.gitignore
vendored
@@ -12,5 +12,5 @@ runForSharingCommands-*
|
||||
runForTestingCommands-*
|
||||
style.min.css
|
||||
build/lib/
|
||||
vendor/
|
||||
vendor/*
|
||||
!vendor/loadEmojiLib.js
|
||||
|
@@ -24,7 +24,6 @@ import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||
import appReducer, { createAppDefaultState } from './app.reducer';
|
||||
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
const fs = require('fs-extra');
|
||||
import Tag from '@joplin/lib/models/Tag';
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
const packageInfo = require('./packageInfo.js');
|
||||
@@ -63,6 +62,7 @@ import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import checkForUpdates from './checkForUpdates';
|
||||
import { AppState } from './app.reducer';
|
||||
import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
|
||||
import eventManager from '@joplin/lib/eventManager';
|
||||
// import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||
|
||||
const pluginClasses = [
|
||||
@@ -234,23 +234,6 @@ class Application extends BaseApplication {
|
||||
});
|
||||
}
|
||||
|
||||
async loadCustomCss(filePath: string) {
|
||||
let cssString = '';
|
||||
if (await fs.pathExists(filePath)) {
|
||||
try {
|
||||
cssString = await fs.readFile(filePath, 'utf-8');
|
||||
|
||||
} catch (error) {
|
||||
let msg = error.message ? error.message : '';
|
||||
msg = `Could not load custom css from ${filePath}\n${msg}`;
|
||||
error.message = msg;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return cssString;
|
||||
}
|
||||
|
||||
private async checkForLegacyTemplates() {
|
||||
const templatesDir = `${Setting.value('profileDir')}/templates`;
|
||||
if (await shim.fsDriver().exists(templatesDir)) {
|
||||
@@ -463,8 +446,9 @@ class Application extends BaseApplication {
|
||||
|
||||
await this.checkForLegacyTemplates();
|
||||
|
||||
// Note: Auto-update currently doesn't work in Linux: it downloads the update
|
||||
// but then doesn't install it on exit.
|
||||
// Note: Auto-update is a misnomer in the code.
|
||||
// The code below only checks, if a new version is available.
|
||||
// We only allow Windows and macOS users to automatically check for updates
|
||||
if (shim.isWindows() || shim.isMac()) {
|
||||
const runAutoUpdateCheck = () => {
|
||||
if (Setting.value('autoUpdateEnabled')) {
|
||||
@@ -523,6 +507,12 @@ class Application extends BaseApplication {
|
||||
|
||||
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); }, (path: string) => bridge().openItem(path));
|
||||
|
||||
// Forwards the local event to the global event manager, so that it can
|
||||
// be picked up by the plugin manager.
|
||||
ResourceEditWatcher.instance().on('resourceChange', (event: any) => {
|
||||
eventManager.emit('resourceChange', event);
|
||||
});
|
||||
|
||||
RevisionService.instance().runInBackground();
|
||||
|
||||
// Make it available to the console window - useful to call revisionService.collectRevisions()
|
||||
|
@@ -9,6 +9,13 @@ interface LastSelectedPath {
|
||||
directory: string;
|
||||
}
|
||||
|
||||
interface OpenDialogOptions {
|
||||
properties?: string[];
|
||||
defaultPath?: string;
|
||||
createDirectory?: boolean;
|
||||
filters?: any[];
|
||||
}
|
||||
|
||||
export class Bridge {
|
||||
|
||||
private electronWrapper_: ElectronAppWrapper;
|
||||
@@ -155,14 +162,14 @@ export class Bridge {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async showOpenDialog(options: any = null) {
|
||||
async showOpenDialog(options: OpenDialogOptions = null) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
let fileType = 'file';
|
||||
if (options.properties && options.properties.includes('openDirectory')) fileType = 'directory';
|
||||
if (!('defaultPath' in options) && (this.lastSelectedPaths_ as any)[fileType]) options.defaultPath = (this.lastSelectedPaths_ as any)[fileType];
|
||||
if (!('createDirectory' in options)) options.createDirectory = true;
|
||||
const { filePaths } = await dialog.showOpenDialog(this.window(), options);
|
||||
const { filePaths } = await dialog.showOpenDialog(this.window(), options as any);
|
||||
if (filePaths && filePaths.length) {
|
||||
(this.lastSelectedPaths_ as any)[fileType] = dirname(filePaths[0]);
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
overflowY: 'scroll',
|
||||
padding: theme.configScreenPadding,
|
||||
// padding: theme.configScreenPadding,
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
});
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useState, useRef, useEffect } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import DialogButtonRow, { ClickEvent } from '../DialogButtonRow';
|
||||
import Dialog from '../Dialog';
|
||||
@@ -8,25 +8,34 @@ import StyledInput from '../style/StyledInput';
|
||||
import { IconSelector, ChangeEvent } from './IconSelector';
|
||||
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import { FolderIcon } from '@joplin/lib/services/database/types';
|
||||
import { FolderEntity, FolderIcon, FolderIconType } from '@joplin/lib/services/database/types';
|
||||
import Button from '../Button/Button';
|
||||
import bridge from '../../services/bridge';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import FolderIconBox from '../FolderIconBox';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
dispatch: Function;
|
||||
folderId: string;
|
||||
parentId: string;
|
||||
}
|
||||
|
||||
export default function(props: Props) {
|
||||
const [folderTitle, setFolderTitle] = useState('');
|
||||
const [folderIcon, setFolderIcon] = useState<FolderIcon>();
|
||||
const titleInputRef = useRef(null);
|
||||
|
||||
const isNew = !props.folderId;
|
||||
|
||||
useAsyncEffect(async (event: AsyncEffectEvent) => {
|
||||
if (isNew) return;
|
||||
|
||||
const folder = await Folder.load(props.folderId);
|
||||
if (event.cancelled) return;
|
||||
setFolderTitle(folder.title);
|
||||
setFolderIcon(Folder.unserializeIcon(folder.icon));
|
||||
}, [props.folderId]);
|
||||
}, [props.folderId, isNew]);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
props.dispatch({
|
||||
@@ -35,6 +44,14 @@ export default function(props: Props) {
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
titleInputRef.current.focus();
|
||||
|
||||
setTimeout(() => {
|
||||
titleInputRef.current.select();
|
||||
}, 100);
|
||||
}, []);
|
||||
|
||||
const onButtonRowClick = useCallback(async (event: ClickEvent) => {
|
||||
if (event.buttonName === 'cancel') {
|
||||
onClose();
|
||||
@@ -42,15 +59,29 @@ export default function(props: Props) {
|
||||
}
|
||||
|
||||
if (event.buttonName === 'ok') {
|
||||
await Folder.save({
|
||||
id: props.folderId,
|
||||
const folder: FolderEntity = {
|
||||
title: folderTitle,
|
||||
icon: Folder.serializeIcon(folderIcon),
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
if (!isNew) folder.id = props.folderId;
|
||||
if (props.parentId) folder.parent_id = props.parentId;
|
||||
|
||||
try {
|
||||
const savedFolder = await Folder.save(folder, { userSideValidation: true });
|
||||
onClose();
|
||||
|
||||
props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: savedFolder.id,
|
||||
});
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}, [onClose, folderTitle, folderIcon, props.folderId]);
|
||||
}, [onClose, folderTitle, folderIcon, props.folderId, props.parentId]);
|
||||
|
||||
const onFolderTitleChange = useCallback((event: any) => {
|
||||
setFolderTitle(event.target.value);
|
||||
@@ -64,23 +95,54 @@ export default function(props: Props) {
|
||||
setFolderIcon(null);
|
||||
}, []);
|
||||
|
||||
const onBrowseClick = useCallback(async () => {
|
||||
const filePaths = await bridge().showOpenDialog({
|
||||
filters: [
|
||||
{
|
||||
name: _('Images'),
|
||||
extensions: ['jpg', 'jpeg', 'png'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (filePaths.length !== 1) return;
|
||||
const filePath = filePaths[0];
|
||||
|
||||
try {
|
||||
const dataUrl = await shim.imageToDataUrl(filePath, 256);
|
||||
setFolderIcon(icon => {
|
||||
return {
|
||||
...icon,
|
||||
emoji: '',
|
||||
name: '',
|
||||
type: FolderIconType.DataUrl,
|
||||
dataUrl,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
await bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function renderForm() {
|
||||
return (
|
||||
<div>
|
||||
<div className="form">
|
||||
<div className="form-input-group">
|
||||
<label>{_('Title')}</label>
|
||||
<StyledInput type="text" value={folderTitle} onChange={onFolderTitleChange}/>
|
||||
<StyledInput type="text" ref={titleInputRef} value={folderTitle} onChange={onFolderTitleChange}/>
|
||||
</div>
|
||||
|
||||
<div className="form-input-group">
|
||||
<label>{_('Icon')}</label>
|
||||
<div className="icon-selector-row">
|
||||
{ folderIcon && <div className="foldericon"><FolderIconBox folderIcon={folderIcon} /></div> }
|
||||
<IconSelector
|
||||
title={_('Select emoji...')}
|
||||
icon={folderIcon}
|
||||
onChange={onFolderIconChange}
|
||||
/>
|
||||
<Button ml={1} title={_('Clear')} onClick={onClearClick}/>
|
||||
<Button ml={1} title={_('Select file...')} onClick={onBrowseClick}/>
|
||||
{ folderIcon && <Button ml={1} title={_('Clear')} onClick={onClearClick}/> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,10 +158,12 @@ export default function(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
const dialogTitle = isNew ? _('Create notebook') : _('Edit notebook');
|
||||
|
||||
function renderDialogWrapper() {
|
||||
return (
|
||||
<div className="dialog-root">
|
||||
<DialogTitle title={_('Edit notebook')}/>
|
||||
<DialogTitle title={dialogTitle}/>
|
||||
{renderContent()}
|
||||
<DialogButtonRow
|
||||
themeId={props.themeId}
|
||||
|
@@ -3,7 +3,7 @@ import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import { loadScript } from '../utils/loadScript';
|
||||
import Button from '../Button/Button';
|
||||
import { FolderIcon } from '@joplin/lib/services/database/types';
|
||||
import { FolderIcon, FolderIconType } from '@joplin/lib/services/database/types';
|
||||
import bridge from '../../services/bridge';
|
||||
|
||||
export interface ChangeEvent {
|
||||
@@ -15,6 +15,7 @@ type ChangeHandler = (event: ChangeEvent)=> void;
|
||||
interface Props {
|
||||
onChange: ChangeHandler;
|
||||
icon: FolderIcon | null;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const IconSelector = (props: Props) => {
|
||||
@@ -62,7 +63,7 @@ export const IconSelector = (props: Props) => {
|
||||
});
|
||||
|
||||
const onEmoji = (selection: FolderIcon) => {
|
||||
props.onChange({ value: selection });
|
||||
props.onChange({ value: { ...selection, type: FolderIconType.Emoji } });
|
||||
};
|
||||
|
||||
p.on('emoji', onEmoji);
|
||||
@@ -78,16 +79,25 @@ export const IconSelector = (props: Props) => {
|
||||
picker.togglePicker(buttonRef.current);
|
||||
}, [picker]);
|
||||
|
||||
const buttonText = props.icon ? props.icon.emoji : '...';
|
||||
// const buttonText = props.icon ? props.icon.emoji : '...';
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={!picker}
|
||||
ref={buttonRef}
|
||||
onClick={onClick}
|
||||
title={buttonText}
|
||||
isSquare={true}
|
||||
fontSize={20}
|
||||
title={props.title}
|
||||
/>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <Button
|
||||
// disabled={!picker}
|
||||
// ref={buttonRef}
|
||||
// onClick={onClick}
|
||||
// title={buttonText}
|
||||
// isSquare={true}
|
||||
// fontSize={20}
|
||||
// />
|
||||
// );
|
||||
};
|
||||
|
@@ -1,4 +1,13 @@
|
||||
.icon-selector-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-selector-row > .foldericon {
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
border: 1px solid var(--joplin-divider-color);
|
||||
padding: 5px;
|
||||
background-color: var(--joplin-background-color);
|
||||
}
|
17
packages/app-desktop/gui/FolderIconBox.tsx
Normal file
17
packages/app-desktop/gui/FolderIconBox.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { FolderIcon, FolderIconType } from '@joplin/lib/services/database/types';
|
||||
|
||||
interface Props {
|
||||
folderIcon: FolderIcon;
|
||||
}
|
||||
|
||||
export default function(props: Props) {
|
||||
const folderIcon = props.folderIcon;
|
||||
|
||||
if (folderIcon.type === FolderIconType.Emoji) {
|
||||
return <span style={{ fontSize: 20 }}>{folderIcon.emoji}</span>;
|
||||
} else if (folderIcon.type === FolderIconType.DataUrl) {
|
||||
return <img style={{ width: 20, height: 20 }} src={folderIcon.dataUrl} />;
|
||||
} else {
|
||||
throw new Error(`Unsupported folder icon type: ${folderIcon.type}`);
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ export default function styles(themeId: number) {
|
||||
return {
|
||||
container: {
|
||||
...theme.containerStyle,
|
||||
padding: theme.configScreenPadding,
|
||||
// padding: theme.configScreenPadding,
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
},
|
||||
actionsContainer: {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
import { Options } from './openFolderDialog';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'newFolder',
|
||||
@@ -9,35 +8,15 @@ export const declaration: CommandDeclaration = {
|
||||
iconName: 'fa-book',
|
||||
};
|
||||
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context: CommandContext, parentId: string = null) => {
|
||||
comp.setState({
|
||||
promptOptions: {
|
||||
label: _('Notebook title:'),
|
||||
onClose: async (answer: string) => {
|
||||
if (answer) {
|
||||
let folder = null;
|
||||
try {
|
||||
const toSave: any = { title: answer };
|
||||
if (parentId) toSave.parent_id = parentId;
|
||||
folder = await Folder.save(toSave, { userSideValidation: true });
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
const options: Options = {
|
||||
isNew: true,
|
||||
parentId: parentId,
|
||||
};
|
||||
|
||||
if (folder) {
|
||||
comp.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
comp.setState({ promptOptions: null });
|
||||
},
|
||||
},
|
||||
});
|
||||
void CommandService.instance().execute('openFolderDialog', options);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
|
||||
export interface Options {
|
||||
isNew?: boolean;
|
||||
folderId?: string;
|
||||
parentId?: string;
|
||||
}
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openFolderDialog',
|
||||
label: () => _('Edit'),
|
||||
@@ -8,13 +14,22 @@ export const declaration: CommandDeclaration = {
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (context: CommandContext, folderId: string) => {
|
||||
execute: async (context: CommandContext, options: Options = null) => {
|
||||
options = {
|
||||
isNew: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.isNew && !('parentId' in options)) throw new Error('parentId mst be specified when creating a new folder');
|
||||
if (!options.isNew && !('folderId' in options)) throw new Error('folderId property is required');
|
||||
|
||||
context.dispatch({
|
||||
type: 'DIALOG_OPEN',
|
||||
name: 'editFolder',
|
||||
isOpen: true,
|
||||
props: {
|
||||
folderId,
|
||||
folderId: options.folderId,
|
||||
parentId: options.parentId,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@@ -89,6 +89,7 @@ interface Props {
|
||||
['spellChecker.language']: string;
|
||||
plugins: PluginStates;
|
||||
customCss: string;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
const commandNames: string[] = menuCommandNames();
|
||||
@@ -249,7 +250,11 @@ function useMenu(props: Props) {
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
const pluginCommandNames = props.pluginMenuItems.map((view: any) => view.commandName);
|
||||
const menuItemDic = menuUtils.commandsToMenuItems(commandNames.concat(pluginCommandNames), (commandName: string) => onMenuItemClickRef.current(commandName));
|
||||
const menuItemDic = menuUtils.commandsToMenuItems(
|
||||
commandNames.concat(pluginCommandNames),
|
||||
(commandName: string) => onMenuItemClickRef.current(commandName),
|
||||
props.locale
|
||||
);
|
||||
|
||||
const quitMenuItem = {
|
||||
label: _('Quit'),
|
||||
@@ -342,6 +347,12 @@ function useMenu(props: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
importItems.push({ type: 'separator' });
|
||||
importItems.push({
|
||||
label: _('Other applications...'),
|
||||
click: () => { void bridge().openExternal('https://discourse.joplinapp.org/t/importing-notes-from-other-notebook-applications/22425'); },
|
||||
});
|
||||
|
||||
exportItems.push(
|
||||
menuItemDic.exportPdf
|
||||
);
|
||||
@@ -830,7 +841,7 @@ function useMenu(props: Props) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
};
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss]);
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss, props.locale]);
|
||||
|
||||
useMenuStates(menu, props);
|
||||
|
||||
@@ -872,6 +883,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
|
||||
return {
|
||||
menuItemProps: menuUtils.commandsToMenuItemProps(commandNames.concat(pluginCommandNames(state.pluginService.plugins)), whenClauseContext),
|
||||
locale: state.settings.locale,
|
||||
routeName: state.route.routeName,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
layoutButtonSequence: state.settings.layoutButtonSequence,
|
||||
|
@@ -67,7 +67,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
|
||||
usePluginServiceRegistration(ref);
|
||||
|
||||
const { resetScroll, editor_scroll, setEditorPercentScroll, setViewerPercentScroll, editor_resize,
|
||||
const { resetScroll, editor_scroll, setEditorPercentScroll, setViewerPercentScroll, editor_resize, editor_update, getLineScrollPercent,
|
||||
} = useScrollHandler(editorRef, webviewRef, props.onScroll);
|
||||
|
||||
const codeMirror_change = useCallback((newBody: string) => {
|
||||
@@ -576,9 +576,14 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
const arg0 = args && args.length >= 1 ? args[0] : null;
|
||||
|
||||
if (msg.indexOf('checkboxclick:') === 0) {
|
||||
const newBody = shared.toggleCheckbox(msg, props.content);
|
||||
const { line, from, to } = shared.toggleCheckboxRange(msg, props.content);
|
||||
if (editorRef.current) {
|
||||
editorRef.current.updateBody(newBody);
|
||||
// To cancel CodeMirror's layout drift, the scroll position
|
||||
// is recorded before updated, and then it is restored.
|
||||
// Ref. https://github.com/laurent22/joplin/issues/5890
|
||||
const percent = getLineScrollPercent();
|
||||
editorRef.current.replaceRange(line, from, to);
|
||||
setEditorPercentScroll(percent);
|
||||
}
|
||||
} else if (msg === 'percentScroll') {
|
||||
const percent = arg0;
|
||||
@@ -838,6 +843,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
onEditorPaste={onEditorPaste}
|
||||
isSafeMode={props.isSafeMode}
|
||||
onResize={editor_resize}
|
||||
onUpdate={editor_update}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@@ -94,6 +94,7 @@ export interface EditorProps {
|
||||
onEditorPaste: any;
|
||||
isSafeMode: boolean;
|
||||
onResize: any;
|
||||
onUpdate: any;
|
||||
}
|
||||
|
||||
function Editor(props: EditorProps, ref: any) {
|
||||
@@ -148,6 +149,14 @@ function Editor(props: EditorProps, ref: any) {
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
}, []);
|
||||
|
||||
const editor_resize = useCallback((cm: any) => {
|
||||
props.onResize(cm);
|
||||
}, [props.onResize]);
|
||||
|
||||
const editor_update = useCallback((cm: any) => {
|
||||
props.onUpdate(cm);
|
||||
}, [props.onUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorParent.current) return () => {};
|
||||
|
||||
@@ -190,7 +199,8 @@ function Editor(props: EditorProps, ref: any) {
|
||||
cm.on('paste', editor_paste);
|
||||
cm.on('drop', editor_drop);
|
||||
cm.on('dragover', editor_drag);
|
||||
cm.on('refresh', props.onResize);
|
||||
cm.on('refresh', editor_resize);
|
||||
cm.on('update', editor_update);
|
||||
|
||||
// It's possible for searchMarkers to be available before the editor
|
||||
// In these cases we set the markers asap so the user can see them as
|
||||
@@ -204,7 +214,8 @@ function Editor(props: EditorProps, ref: any) {
|
||||
cm.off('paste', editor_paste);
|
||||
cm.off('drop', editor_drop);
|
||||
cm.off('dragover', editor_drag);
|
||||
cm.off('refresh', props.onResize);
|
||||
cm.off('refresh', editor_resize);
|
||||
cm.off('update', editor_update);
|
||||
editorParent.current.removeChild(cm.getWrapperElement());
|
||||
setEditor(null);
|
||||
};
|
||||
|
@@ -8,6 +8,7 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
const [scrollbarMarks, setScrollbarMarks] = useState(null);
|
||||
const [previousKeywordValue, setPreviousKeywordValue] = useState(null);
|
||||
const [previousIndex, setPreviousIndex] = useState(null);
|
||||
const [previousSearchTimestamp, setPreviousSearchTimestamp] = useState(0);
|
||||
const [overlayTimeout, setOverlayTimeout] = useState(null);
|
||||
const overlayTimeoutRef = useRef(null);
|
||||
overlayTimeoutRef.current = overlayTimeout;
|
||||
@@ -51,7 +52,7 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
// Highlights the currently active found work
|
||||
// It's possible to get tricky with this fucntions and just use findNext/findPrev
|
||||
// but this is fast enough and works more naturally with the current search logic
|
||||
function highlightSearch(cm: any, searchTerm: RegExp, index: number, scrollTo: boolean) {
|
||||
function highlightSearch(cm: any, searchTerm: RegExp, index: number, scrollTo: boolean, withSelection: boolean) {
|
||||
const cursor = cm.getSearchCursor(searchTerm);
|
||||
|
||||
let match: any = null;
|
||||
@@ -64,7 +65,13 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (scrollTo) cm.setSelection(match.from, match.to);
|
||||
if (scrollTo) {
|
||||
if (withSelection) {
|
||||
cm.setSelection(match.from, match.to);
|
||||
} else {
|
||||
cm.scrollTo(match);
|
||||
}
|
||||
}
|
||||
return cm.markText(match.from, match.to, { className: 'cm-search-marker-selected' });
|
||||
}
|
||||
|
||||
@@ -90,7 +97,7 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
|
||||
CodeMirror.defineExtension('setMarkers', function(keywords: any, options: any) {
|
||||
if (!options) {
|
||||
options = { selectedIndex: 0 };
|
||||
options = { selectedIndex: 0, searchTimestamp: 0 };
|
||||
}
|
||||
|
||||
clearMarkers();
|
||||
@@ -107,16 +114,15 @@ export default function useEditorSearch(CodeMirror: any) {
|
||||
const searchTerm = getSearchTerm(keyword);
|
||||
|
||||
// We only want to scroll the first keyword into view in the case of a multi keyword search
|
||||
const scrollTo = i === 0 && (previousKeywordValue !== keyword.value || previousIndex !== options.selectedIndex ||
|
||||
// If there is only one choice, scrollTo should be true. The below is a dummy of nMatches === 1.
|
||||
options.selectedIndex === 0);
|
||||
const scrollTo = i === 0 && (previousKeywordValue !== keyword.value || previousIndex !== options.selectedIndex || options.searchTimestamp !== previousSearchTimestamp);
|
||||
|
||||
const match = highlightSearch(this, searchTerm, options.selectedIndex, scrollTo);
|
||||
const match = highlightSearch(this, searchTerm, options.selectedIndex, scrollTo, !!options.withSelection);
|
||||
if (match) marks.push(match);
|
||||
}
|
||||
|
||||
setMarkers(marks);
|
||||
setPreviousIndex(options.selectedIndex);
|
||||
setPreviousSearchTimestamp(options.searchTimestamp);
|
||||
|
||||
// SEARCHOVERLAY
|
||||
// We only want to highlight all matches when there is only 1 search term
|
||||
|
@@ -7,6 +7,10 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
const ignoreNextEditorScrollTime_ = useRef(Date.now());
|
||||
const ignoreNextEditorScrollEventCount_ = useRef(0);
|
||||
const delayedSetEditorPercentScrollTimeoutID_ = useRef(null);
|
||||
const scrollTopIsUncertain_ = useRef(true);
|
||||
const lastResizeHeight_ = useRef(NaN);
|
||||
const lastLinesHeight_ = useRef(NaN);
|
||||
const restoreEditorPercentScrollTimeoutId_ = useRef<any>(null);
|
||||
|
||||
// Ignores one next scroll event for a short time.
|
||||
const ignoreNextEditorScrollEvent = () => {
|
||||
@@ -54,23 +58,33 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
// calculates editor's GUI-dependent pixel-based raw percent
|
||||
const newEditorPercent = translateScrollPercentL2E(cm, scrollPercent_.current);
|
||||
const oldEditorPercent = cm.getScrollPercent();
|
||||
const oldEditorPercent = scrollTopIsUncertain_.current ? NaN : cm.getScrollPercent();
|
||||
if (!(Math.abs(newEditorPercent - oldEditorPercent) < 1e-8)) {
|
||||
ignoreNextEditorScrollEvent();
|
||||
cm.setScrollPercent(newEditorPercent);
|
||||
}
|
||||
scrollTopIsUncertain_.current = false;
|
||||
} else {
|
||||
retry += 1;
|
||||
if (retry <= 10) {
|
||||
if (retry <= 3) {
|
||||
delayedSetEditorPercentScrollTimeoutID_.current = shim.setTimeout(fn, 50);
|
||||
}
|
||||
scrollTopIsUncertain_.current = true;
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
};
|
||||
fn();
|
||||
};
|
||||
|
||||
const restoreEditorPercentScroll = () => {
|
||||
if (isCodeMirrorReady(editorRef.current)) {
|
||||
if (restoreEditorPercentScrollTimeoutId_.current) {
|
||||
shim.clearTimeout(restoreEditorPercentScrollTimeoutId_.current);
|
||||
restoreEditorPercentScrollTimeoutId_.current = null;
|
||||
}
|
||||
const cm = editorRef.current;
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
lastLinesHeight_.current = cm.heightAtLine(cm.lineCount()) - cm.heightAtLine(0);
|
||||
setEditorPercentScrollInternal(scrollPercent_.current);
|
||||
}
|
||||
};
|
||||
@@ -90,22 +104,27 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
}, [scheduleOnScroll]);
|
||||
|
||||
const editor_scroll = useCallback(() => {
|
||||
if (isNextEditorScrollEventIgnored()) return;
|
||||
|
||||
const ignored = isNextEditorScrollEventIgnored();
|
||||
const cm = editorRef.current;
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
if (scrollTopIsUncertain_.current) return;
|
||||
const editorPercent = Math.max(0, Math.min(1, cm.getScrollPercent()));
|
||||
if (!isNaN(editorPercent)) {
|
||||
// when switching to another note, the percent can sometimes be NaN
|
||||
// this is coming from `gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.ts`
|
||||
// when CodeMirror returns scroll info with heigth == clientHeigth
|
||||
// https://github.com/laurent22/joplin/issues/4797
|
||||
|
||||
// calculates GUI-independent line-based percent
|
||||
const percent = translateScrollPercentE2L(cm, editorPercent);
|
||||
scrollPercent_.current = percent;
|
||||
setViewerPercentScroll(percent);
|
||||
if (!ignored) {
|
||||
// calculates GUI-independent line-based percent
|
||||
const percent = translateScrollPercentE2L(cm, editorPercent);
|
||||
scrollPercent_.current = percent;
|
||||
setViewerPercentScroll(percent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scrollTopIsUncertain_.current = true;
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
}, [setViewerPercentScroll]);
|
||||
|
||||
@@ -113,17 +132,60 @@ export default function useScrollHandler(editorRef: any, webviewRef: any, onScro
|
||||
scrollPercent_.current = 0;
|
||||
if (editorRef.current) {
|
||||
editorRef.current.setScrollPercent(0);
|
||||
scrollTopIsUncertain_.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const editor_resize = useCallback((cm) => {
|
||||
if (cm) {
|
||||
restoreEditorPercentScroll();
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
// This handler is called when resized and refreshed.
|
||||
// Only when resized, the scroll position is restored.
|
||||
const info = cm.getScrollInfo();
|
||||
const height = info.height - info.clientHeight;
|
||||
if (height !== lastResizeHeight_.current) {
|
||||
// When resized, restoring is performed immediately.
|
||||
restoreEditorPercentScroll();
|
||||
lastResizeHeight_.current = height;
|
||||
}
|
||||
} else {
|
||||
scrollTopIsUncertain_.current = true;
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// When heights of lines are updated in CodeMirror, 'update' events are raised.
|
||||
// If such an update event is raised, scroll position should be restored.
|
||||
// See https://github.com/laurent22/joplin/issues/5981
|
||||
const editor_update = useCallback((cm) => {
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
const linesHeight = cm.heightAtLine(cm.lineCount()) - cm.heightAtLine(0);
|
||||
if (lastLinesHeight_.current !== linesHeight) {
|
||||
// To avoid cancelling intentional scroll position changes,
|
||||
// restoring is performed in a timeout handler.
|
||||
if (!restoreEditorPercentScrollTimeoutId_.current) {
|
||||
restoreEditorPercentScrollTimeoutId_.current = shim.setTimeout(restoreEditorPercentScroll, 10);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scrollTopIsUncertain_.current = true;
|
||||
lastResizeHeight_.current = NaN;
|
||||
lastLinesHeight_.current = NaN;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getLineScrollPercent = useCallback(() => {
|
||||
const cm = editorRef.current;
|
||||
if (isCodeMirrorReady(cm)) {
|
||||
const ePercent = cm.getScrollPercent();
|
||||
return translateScrollPercentE2L(cm, ePercent);
|
||||
} else {
|
||||
return scrollPercent_.current;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll, editor_resize,
|
||||
resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll, editor_resize, editor_update, getLineScrollPercent,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -92,6 +92,7 @@ export default function useNoteSearchBar() {
|
||||
selectedIndex: localSearch.selectedIndex,
|
||||
separateWordSearch: false,
|
||||
searchTimestamp: localSearch.timestamp,
|
||||
withSelection: true,
|
||||
},
|
||||
keywords: [
|
||||
{
|
||||
|
@@ -4,6 +4,7 @@ interface SearchMarkersOptions {
|
||||
searchTimestamp: number;
|
||||
selectedIndex: number;
|
||||
separateWordSearch: boolean;
|
||||
withSelection?: boolean;
|
||||
}
|
||||
|
||||
export interface SearchMarkers {
|
||||
|
@@ -56,7 +56,15 @@ function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, s
|
||||
});
|
||||
}
|
||||
},
|
||||
enabledCondition: '!modalDialogVisible && markdownEditorPaneVisible && oneNoteSelected && noteIsMarkdown',
|
||||
|
||||
// We disable the editor commands whenever a modal dialog is visible,
|
||||
// otherwise the user might type something in a dialog and accidentally
|
||||
// change something in the editor. However, we still enable them for
|
||||
// GotoAnything so that it's possible to type eg `textBold` and bold the
|
||||
// currently selected text.
|
||||
//
|
||||
// https://github.com/laurent22/joplin/issues/5707
|
||||
enabledCondition: '(!modalDialogVisible || gotoAnythingVisible) && markdownEditorPaneVisible && oneNoteSelected && noteIsMarkdown',
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -167,8 +167,8 @@ class NoteSearchBarComponent extends React.Component {
|
||||
type="text"
|
||||
style={{ width: 200, marginRight: 5, backgroundColor: this.backgroundColor, color: theme.color }}
|
||||
/>
|
||||
{allowScrolling ? nextButton : null}
|
||||
{allowScrolling ? previousButton : null}
|
||||
{allowScrolling ? nextButton : null}
|
||||
{allowScrolling ? matchesFoundString : null}
|
||||
{!allowScrolling ? viewerWarning : null}
|
||||
</div>
|
||||
|
@@ -56,7 +56,7 @@ class PromptDialog extends React.Component {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: width,
|
||||
height: height - paddingTop,
|
||||
height: height,
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
display: visible ? 'flex' : 'none',
|
||||
alignItems: 'flex-start',
|
||||
|
@@ -15,6 +15,7 @@ export const Root = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 30px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
@@ -138,6 +139,21 @@ function SearchBar(props: Props) {
|
||||
}
|
||||
}, [props.notesParentType, onExitSearch]);
|
||||
|
||||
// When the searchbar is remounted, exit the search if it was previously open
|
||||
// or else other buttons stay hidden (e.g. when opening Layout Editor and closing it)
|
||||
// https://github.com/laurent22/joplin/issues/5953
|
||||
useEffect(() => {
|
||||
if (props.notesParentType === 'Search' || props.isFocused) {
|
||||
if (props.isFocused) {
|
||||
props.dispatch({
|
||||
type: 'FOCUS_CLEAR',
|
||||
field: 'globalSearch',
|
||||
});
|
||||
}
|
||||
void onExitSearch(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Root className="search-bar">
|
||||
<SearchInput
|
||||
|
@@ -17,11 +17,12 @@ import Folder from '@joplin/lib/models/Folder';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Tag from '@joplin/lib/models/Tag';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
import { FolderEntity, FolderIcon } from '@joplin/lib/services/database/types';
|
||||
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
||||
import { store } from '@joplin/lib/reducer';
|
||||
import PerFolderSortOrderService from '../../services/sortOrder/PerFolderSortOrderService';
|
||||
import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
import FolderIconBox from '../FolderIconBox';
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('@joplin/lib/components/shared/side-menu-shared.js');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
@@ -77,6 +78,12 @@ function ExpandLink(props: any) {
|
||||
);
|
||||
}
|
||||
|
||||
const renderFolderIcon = (folderIcon: FolderIcon) => {
|
||||
if (!folderIcon) return null;
|
||||
|
||||
return <div style={{ marginRight: 5, display: 'flex' }}><FolderIconBox folderIcon={folderIcon}/></div>;
|
||||
};
|
||||
|
||||
function FolderItem(props: any) {
|
||||
const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, folderIcon, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
|
||||
|
||||
@@ -84,8 +91,6 @@ function FolderItem(props: any) {
|
||||
|
||||
const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null;
|
||||
|
||||
const icon = folderIcon ? <span style={{ fontSize: 20, marginRight: 5 }}>{folderIcon.emoji}</span> : null;
|
||||
|
||||
return (
|
||||
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth} ${selected ? 'selected' : ''}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
|
||||
<ExpandLink themeId={props.themeId} hasChildren={hasChildren} folderId={folderId} onClick={onFolderToggleClick_} isExpanded={isExpanded}/>
|
||||
@@ -105,7 +110,7 @@ function FolderItem(props: any) {
|
||||
}}
|
||||
onDoubleClick={onFolderToggleClick_}
|
||||
>
|
||||
{icon}<span className="title" style={{ lineHeight: 0 }}>{folderTitle}</span>
|
||||
{renderFolderIcon(folderIcon)}<span className="title" style={{ lineHeight: 0 }}>{folderTitle}</span>
|
||||
{shareIcon} {noteCountComp}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
@@ -294,7 +299,7 @@ class SidebarComponent extends React.Component<Props, State> {
|
||||
);
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('openFolderDialog', itemId)));
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('openFolderDialog', { folderId: itemId })));
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
|
||||
|
@@ -193,34 +193,36 @@ export default function(props: Props) {
|
||||
const onJoplinCloudLoginClick = useCallback(async () => {
|
||||
setJoplinCloudLoginInProgress(true);
|
||||
|
||||
let result = null;
|
||||
|
||||
try {
|
||||
const result = await SyncTargetJoplinCloud.checkConfig({
|
||||
result = await SyncTargetJoplinCloud.checkConfig({
|
||||
password: () => joplinCloudPassword,
|
||||
path: () => Setting.value('sync.10.path'),
|
||||
userContentPath: () => Setting.value('sync.10.userContentPath'),
|
||||
username: () => joplinCloudEmail,
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
Setting.setValue('sync.target', 10);
|
||||
Setting.setValue('sync.10.username', joplinCloudEmail);
|
||||
Setting.setValue('sync.10.password', joplinCloudPassword);
|
||||
await Setting.saveAll();
|
||||
|
||||
alert(_('Thank you! Your Joplin Cloud account is now setup and ready to use.'));
|
||||
|
||||
closeDialog(props.dispatch);
|
||||
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Main',
|
||||
});
|
||||
} else {
|
||||
alert(_('There was an error setting up your Joplin Cloud account. Please verify your email and password and try again. Error was:\n\n%s', result.errorMessage));
|
||||
}
|
||||
} finally {
|
||||
setJoplinCloudLoginInProgress(false);
|
||||
}
|
||||
|
||||
if (result.ok) {
|
||||
Setting.setValue('sync.target', 10);
|
||||
Setting.setValue('sync.10.username', joplinCloudEmail);
|
||||
Setting.setValue('sync.10.password', joplinCloudPassword);
|
||||
await Setting.saveAll();
|
||||
|
||||
alert(_('Thank you! Your Joplin Cloud account is now setup and ready to use.'));
|
||||
|
||||
closeDialog(props.dispatch);
|
||||
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Main',
|
||||
});
|
||||
} else {
|
||||
alert(_('There was an error setting up your Joplin Cloud account. Please verify your email and password and try again. Error was:\n\n%s', result.errorMessage));
|
||||
}
|
||||
}, [joplinCloudEmail, joplinCloudPassword, props.dispatch]);
|
||||
|
||||
const onJoplinCloudCreateAccountClick = useCallback(() => {
|
||||
@@ -230,10 +232,10 @@ export default function(props: Props) {
|
||||
function renderJoplinCloudLoginForm() {
|
||||
return (
|
||||
<JoplinCloudLoginForm>
|
||||
<div>{_('Login below.')} <CreateAccountLink href="#" onClick={onJoplinCloudCreateAccountClick}>{_('Or create an account.')}</CreateAccountLink></div>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<div style={{ fontSize: '16px' }}>{_('Login below.')} <CreateAccountLink href="#" onClick={onJoplinCloudCreateAccountClick}>{_('Or create an account.')}</CreateAccountLink></div>
|
||||
<FormLabel>{_('Email')}</FormLabel>
|
||||
<StyledInput type="email" onChange={onJoplinCloudEmailChange}/>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormLabel>{_('Password')}</FormLabel>
|
||||
<StyledInput type="password" onChange={onJoplinCloudPasswordChange}/>
|
||||
<SelectButton mt="1.3em" disabled={joplinCloudLoginInProgress} level={ButtonLevel.Primary} title={_('Login')} onClick={onJoplinCloudLoginClick}/>
|
||||
</JoplinCloudLoginForm>
|
||||
|
@@ -1,14 +1,15 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const CommandService = require('@joplin/lib/services/CommandService').default;
|
||||
|
||||
class TagItemComponent extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign({}, theme.tagStyle);
|
||||
const title = this.props.title;
|
||||
const { title, id } = this.props;
|
||||
|
||||
return <span style={style}>{title}</span>;
|
||||
return <button style={style} onClick={() => CommandService.instance().execute('openTag', id)}>{title}</button>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -42,6 +42,7 @@ function TagList(props: Props) {
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const props = {
|
||||
title: tags[i].title,
|
||||
id: tags[i].id,
|
||||
key: tags[i].id,
|
||||
};
|
||||
output.push(<TagItem {...props} />);
|
||||
|
@@ -277,6 +277,9 @@
|
||||
let restoreAndRefreshTimeoutID_ = null;
|
||||
let restoreAndRefreshTimeout_ = Date.now();
|
||||
|
||||
// If 'noteRenderComplete' message is ongoing, resizing should not trigger a 'percentScroll' messsage.
|
||||
let noteRenderCompleteMessageIsOngoing_ = false;
|
||||
|
||||
// A callback anonymous function invoked when the scroll height changes.
|
||||
const onRendering = observeRendering((cause, height, heightChanged) => {
|
||||
if (!alreadyAllImagesLoaded && !scrollmap.isPresent()) {
|
||||
@@ -285,6 +288,7 @@
|
||||
alreadyAllImagesLoaded = true;
|
||||
scrollmap.refresh();
|
||||
restorePercentScroll();
|
||||
noteRenderCompleteMessageIsOngoing_ = true;
|
||||
ipcProxySendToHost('noteRenderComplete');
|
||||
return;
|
||||
}
|
||||
@@ -293,6 +297,8 @@
|
||||
const restoreAndRefresh = () => {
|
||||
scrollmap.refresh();
|
||||
restorePercentScroll();
|
||||
// To ensures Editor's scroll position is synced with Viewer's
|
||||
if (!noteRenderCompleteMessageIsOngoing_) ipcProxySendToHost('percentScroll', percentScroll_);
|
||||
};
|
||||
const now = Date.now();
|
||||
if (now < restoreAndRefreshTimeout_) {
|
||||
@@ -343,11 +349,13 @@
|
||||
|
||||
if (scrollmap.isPresent()) {
|
||||
// Now, ready to receive scrollToHash/setPercentScroll from Editor.
|
||||
noteRenderCompleteMessageIsOngoing_ = true;
|
||||
ipcProxySendToHost('noteRenderComplete');
|
||||
}
|
||||
}
|
||||
|
||||
ipc.setPercentScroll = (event) => {
|
||||
noteRenderCompleteMessageIsOngoing_ = false;
|
||||
setPercentScroll(event.percent);
|
||||
}
|
||||
|
||||
|
@@ -151,7 +151,7 @@ General classes
|
||||
|
||||
body, button {
|
||||
color: var(--joplin-color);
|
||||
font-size: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
div, span, a {
|
||||
@@ -159,7 +159,7 @@ div, span, a {
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
|
||||
&.-no-top-margin {
|
||||
margin-top: 0;
|
||||
@@ -193,7 +193,7 @@ div.form,
|
||||
|
||||
p {
|
||||
&.-small {
|
||||
font-size: 13px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.7.0",
|
||||
"version": "2.7.10",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
@@ -19,6 +19,7 @@ const { mergeOverlappingIntervals } = require('@joplin/lib/ArrayUtils.js');
|
||||
import markupLanguageUtils from '../utils/markupLanguageUtils';
|
||||
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { MarkupToHtml } from '@joplin/renderer';
|
||||
|
||||
const logger = Logger.create('GotoAnything');
|
||||
|
||||
@@ -81,7 +82,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
private inputRef: any;
|
||||
private itemListRef: any;
|
||||
private listUpdateIID_: any;
|
||||
private markupToHtml_: any;
|
||||
private markupToHtml_: MarkupToHtml;
|
||||
private userCallback_: any = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
|
@@ -10,6 +10,12 @@
|
||||
|
||||
# ./runForTesting.sh 1 createUsers,createData,reset,e2ee,sync && ./runForTesting.sh 2 reset,e2ee,sync && ./runForTesting.sh 1
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
# First user has E2EE, but second one doesn't:
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
# ./runForTesting.sh 1 createUsers,createData,reset,e2ee,sync && ./runForTesting.sh 2 reset,sync && ./runForTesting.sh 1
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
# Without E2EE:
|
||||
# ----------------------------------------------------------------------------------
|
||||
@@ -41,6 +47,11 @@ if [ "$USER_NUM" = "1a" ]; then
|
||||
USER_PROFILE_NUM=1a
|
||||
fi
|
||||
|
||||
if [ "$USER_NUM" = "1b" ]; then
|
||||
USER_NUM=1
|
||||
USER_PROFILE_NUM=1b
|
||||
fi
|
||||
|
||||
COMMANDS=($(echo $2 | tr "," "\n"))
|
||||
PROFILE_DIR=~/.config/joplindev-desktop-$USER_PROFILE_NUM
|
||||
|
||||
|
@@ -17,6 +17,7 @@ export default function stateToWhenClauseContext(state: AppState, options: WhenC
|
||||
markdownEditorPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('editor'),
|
||||
markdownViewerPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('viewer'),
|
||||
modalDialogVisible: !!Object.keys(state.visibleDialogs).length,
|
||||
gotoAnythingVisible: !!state.visibleDialogs['gotoAnything'],
|
||||
sidebarVisible: !!state.mainLayout && layoutItemProp(state.mainLayout, 'sideBar', 'visible'),
|
||||
noteListHasNotes: !!state.notes.length,
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import time from '@joplin/lib/time';
|
||||
|
||||
const logger = Logger.create('BackOffHandler');
|
||||
|
||||
@@ -52,13 +51,21 @@ export default class BackOffHandler {
|
||||
|
||||
this.waitCount_++;
|
||||
|
||||
// For now don't actually apply a backoff and don't abort.
|
||||
|
||||
logger.warn(`Plugin ${this.pluginId_}: Applying a backoff of ${interval} seconds due to frequent plugin API calls. Consider reducing the number of calls, caching the data, or requesting more data per call. API call was: `, path, args, `[Wait count: ${this.waitCount_}]`);
|
||||
|
||||
if (this.waitCount_ > this.maxWaitCount_) throw new Error(`Plugin ${this.pluginId_}: More than ${this.maxWaitCount_} API alls are waiting - aborting. Please consider queuing the API calls in your plugins to reduce the load on the application.`);
|
||||
|
||||
await time.sleep(interval);
|
||||
if (this.waitCount_ > this.maxWaitCount_) logger.error(`Plugin ${this.pluginId_}: More than ${this.maxWaitCount_} API alls are waiting - aborting. Please consider queuing the API calls in your plugins to reduce the load on the application.`);
|
||||
|
||||
this.waitCount_--;
|
||||
|
||||
|
||||
|
||||
// if (this.waitCount_ > this.maxWaitCount_) throw new Error(`Plugin ${this.pluginId_}: More than ${this.maxWaitCount_} API alls are waiting - aborting. Please consider queuing the API calls in your plugins to reduce the load on the application.`);
|
||||
|
||||
// await time.sleep(interval);
|
||||
|
||||
// this.waitCount_--;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@ export default class PerFolderSortOrderService {
|
||||
this.loadSharedSortOrder();
|
||||
eventManager.appStateOn('notesParentType', this.onFolderSelectionMayChange.bind(this, 'notesParentType'));
|
||||
eventManager.appStateOn('selectedFolderId', this.onFolderSelectionMayChange.bind(this, 'selectedFolderId'));
|
||||
this.previousFolderId = Setting.value('activeFolderId');
|
||||
}
|
||||
|
||||
public static isSet(folderId: string): boolean {
|
||||
|
@@ -30,7 +30,10 @@ function convertJsx(path) {
|
||||
|
||||
if (fileIsNewerThan(jsxPath, jsPath)) {
|
||||
console.info(`Compiling ${jsxPath}...`);
|
||||
const result = spawnSync('yarn', ['run', 'babel', '--presets', 'react', '--out-file', jsPath, jsxPath]);
|
||||
|
||||
// { shell: true } is needed to get it working on Windows:
|
||||
// https://discourse.joplinapp.org/t/attempting-to-build-on-windows/22559/12
|
||||
const result = spawnSync('yarn', ['run', 'babel', '--presets', 'react', '--out-file', jsPath, jsxPath], { shell: true });
|
||||
if (result.status !== 0) {
|
||||
const msg = [];
|
||||
if (result.stdout) msg.push(result.stdout.toString());
|
||||
|
@@ -1,5 +1,29 @@
|
||||
const { copy, mkdirp, remove } = require('fs-extra');
|
||||
|
||||
const msleep = async (ms) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, ms);
|
||||
});
|
||||
};
|
||||
|
||||
// Same as copyApplicationAssets - probably both scripts should be merged in
|
||||
// one.
|
||||
const withRetry = async (fn) => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
try {
|
||||
await fn();
|
||||
return;
|
||||
} catch (error) {
|
||||
console.warn(`withRetry: Failed calling function - will retry (${i})`, error);
|
||||
await msleep(1000 + i * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('withRetry: Could not run function after multiple attempts');
|
||||
};
|
||||
|
||||
async function main() {
|
||||
const rootDir = `${__dirname}/..`;
|
||||
|
||||
@@ -12,11 +36,11 @@ async function main() {
|
||||
for (const action of ['delete', 'copy']) {
|
||||
for (const destDir of destDirs) {
|
||||
if (action === 'delete') {
|
||||
await remove(destDir);
|
||||
await withRetry(() => remove(destDir));
|
||||
} else {
|
||||
console.info(`Copying to ${destDir}`);
|
||||
await mkdirp(destDir);
|
||||
await copy(sourceDir, destDir, { overwrite: true });
|
||||
await withRetry(() => mkdirp(destDir));
|
||||
await withRetry(() => copy(sourceDir, destDir, { overwrite: true }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
packages/app-desktop/vendor/loadEmojiLib.js
vendored
Normal file
2
packages/app-desktop/vendor/loadEmojiLib.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import { EmojiButton } from './lib/@joeattardi/emoji-button/dist/index.js';
|
||||
window.EmojiButton = EmojiButton;
|
@@ -146,8 +146,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097665
|
||||
versionName "2.7.0"
|
||||
versionCode 2097666
|
||||
versionName "2.7.1"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const React = require('react');
|
||||
const Component = React.Component;
|
||||
const { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Alert } = require('react-native');
|
||||
const { Easing, Animated, TouchableOpacity, Text, StyleSheet, ScrollView, View, Alert, Image } = require('react-native');
|
||||
const { connect } = require('react-redux');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
@@ -74,7 +74,7 @@ class SideMenuContentComponent extends Component {
|
||||
|
||||
styles.folderButton = Object.assign({}, styles.button);
|
||||
styles.folderButton.paddingLeft = 0;
|
||||
styles.folderButtonText = Object.assign({}, styles.buttonText);
|
||||
styles.folderButtonText = Object.assign({}, styles.buttonText, { paddingLeft: 0 });
|
||||
styles.folderButtonSelected = Object.assign({}, styles.folderButton);
|
||||
styles.folderButtonSelected.backgroundColor = theme.selectedColor;
|
||||
styles.folderIcon = Object.assign({}, theme.icon);
|
||||
@@ -219,6 +219,18 @@ class SideMenuContentComponent extends Component {
|
||||
if (actionDone === 'auth') this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||
}
|
||||
|
||||
renderFolderIcon(theme, folderIcon) {
|
||||
if (!folderIcon) return null;
|
||||
|
||||
if (folderIcon.type === 1) { // FolderIconType.Emoji
|
||||
return <Text style={{ fontSize: theme.fontSize, marginRight: 4 }}>{folderIcon.emoji}</Text>;
|
||||
} else if (folderIcon.type === 2) { // FolderIconType.DataUrl
|
||||
return <Image style={{ width: 20, height: 20, marginRight: 4, resizeMode: 'contain' }} source={{ uri: folderIcon.dataUrl }}/>;
|
||||
} else {
|
||||
throw new Error(`Unsupported folder icon type: ${folderIcon.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
renderFolderItem(folder, selected, hasChildren, depth) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
@@ -228,6 +240,7 @@ class SideMenuContentComponent extends Component {
|
||||
height: 36,
|
||||
alignItems: 'center',
|
||||
paddingRight: theme.marginRight,
|
||||
paddingLeft: 10,
|
||||
};
|
||||
if (selected) folderButtonStyle.backgroundColor = theme.selectedColor;
|
||||
folderButtonStyle.paddingLeft = depth * 10 + theme.marginLeft;
|
||||
@@ -253,7 +266,6 @@ class SideMenuContentComponent extends Component {
|
||||
);
|
||||
|
||||
const folderIcon = Folder.unserializeIcon(folder.icon);
|
||||
const icon = folderIcon ? `${folderIcon.emoji} ` : '';
|
||||
|
||||
return (
|
||||
<View key={folder.id} style={{ flex: 1, flexDirection: 'row' }}>
|
||||
@@ -267,8 +279,9 @@ class SideMenuContentComponent extends Component {
|
||||
}}
|
||||
>
|
||||
<View style={folderButtonStyle}>
|
||||
{this.renderFolderIcon(theme, folderIcon)}
|
||||
<Text numberOfLines={1} style={this.styles().folderButtonText}>
|
||||
{icon + Folder.displayTitle(folder)}
|
||||
{Folder.displayTitle(folder)}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
@@ -43,6 +43,7 @@ module.exports = {
|
||||
'@joplin/renderer': path.resolve(__dirname, '../renderer/'),
|
||||
'@joplin/tools': path.resolve(__dirname, '../tools/'),
|
||||
'@joplin/fork-htmlparser2': path.resolve(__dirname, '../fork-htmlparser2/'),
|
||||
'@joplin/fork-uslug': path.resolve(__dirname, '../fork-uslug/'),
|
||||
},
|
||||
{
|
||||
get: (target, name) => {
|
||||
@@ -60,5 +61,6 @@ module.exports = {
|
||||
path.resolve(__dirname, '../renderer'),
|
||||
path.resolve(__dirname, '../tools'),
|
||||
path.resolve(__dirname, '../fork-htmlparser2'),
|
||||
path.resolve(__dirname, '../fork-uslug'),
|
||||
],
|
||||
};
|
||||
|
@@ -1 +1 @@
|
||||
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiNhYmIyYmY7YmFja2dyb3VuZDojMjgyYzM0fS5obGpzLWtleXdvcmQsLmhsanMtb3BlcmF0b3IsLmhsanMtcGF0dGVybi1tYXRjaHtjb2xvcjojZjkyNjcyfS5obGpzLWZ1bmN0aW9uLC5obGpzLXBhdHRlcm4tbWF0Y2ggLmhsanMtY29uc3RydWN0b3J7Y29sb3I6IzYxYWVlZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXN7Y29sb3I6I2E2ZTIyZX0uaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5ne2NvbG9yOiNmZDk3MWZ9LmhsanMtbW9kdWxlLWFjY2VzcyAuaGxqcy1tb2R1bGV7Y29sb3I6IzdlNTdjMn0uaGxqcy1jb25zdHJ1Y3Rvcntjb2xvcjojZTJiOTNkfS5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZ3tjb2xvcjojOWNjYzY1fS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2IxOGViMTtmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYXtjb2xvcjojYzY3OGRkfS5obGpzLWRlbGV0aW9uLC5obGpzLW5hbWUsLmhsanMtc2VjdGlvbiwuaGxqcy1zZWxlY3Rvci10YWcsLmhsanMtc3Vic3R7Y29sb3I6I2UwNmM3NX0uaGxqcy1saXRlcmFse2NvbG9yOiM1NmI2YzJ9LmhsanMtYWRkaXRpb24sLmhsanMtYXR0cmlidXRlLC5obGpzLW1ldGEgLmhsanMtc3RyaW5nLC5obGpzLXJlZ2V4cCwuaGxqcy1zdHJpbmd7Y29sb3I6Izk4YzM3OX0uaGxqcy1idWlsdF9pbiwuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSwuaGxqcy10aXRsZS5jbGFzc197Y29sb3I6I2U2YzA3Yn0uaGxqcy1hdHRyLC5obGpzLW51bWJlciwuaGxqcy1zZWxlY3Rvci1hdHRyLC5obGpzLXNlbGVjdG9yLWNsYXNzLC5obGpzLXNlbGVjdG9yLXBzZXVkbywuaGxqcy10ZW1wbGF0ZS12YXJpYWJsZSwuaGxqcy10eXBlLC5obGpzLXZhcmlhYmxle2NvbG9yOiNkMTlhNjZ9LmhsanMtYnVsbGV0LC5obGpzLWxpbmssLmhsanMtbWV0YSwuaGxqcy1zZWxlY3Rvci1pZCwuaGxqcy1zeW1ib2wsLmhsanMtdGl0bGV7Y29sb3I6IzYxYWVlZX0uaGxqcy1lbXBoYXNpc3tmb250LXN0eWxlOml0YWxpY30uaGxqcy1zdHJvbmd7Zm9udC13ZWlnaHQ6NzAwfS5obGpzLWxpbmt7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX0=`;
|
||||
module.exports = `LyoKCkF0b20gT25lIERhcmsgV2l0aCBzdXBwb3J0IGZvciBSZWFzb25NTCBieSBHaWRpIE1vcnJpcywgYmFzZWQgb2ZmIHdvcmsgYnkgRGFuaWVsIEdhbWFnZQoKT3JpZ2luYWwgT25lIERhcmsgU3ludGF4IHRoZW1lIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2F0b20vb25lLWRhcmstc3ludGF4CgoqLwouaGxqcyB7CiAgY29sb3I6ICNhYmIyYmY7CiAgYmFja2dyb3VuZDogIzI4MmMzNDsKfQouaGxqcy1rZXl3b3JkLCAuaGxqcy1vcGVyYXRvciB7CiAgY29sb3I6ICNGOTI2NzI7Cn0KLmhsanMtcGF0dGVybi1tYXRjaCB7CiAgY29sb3I6ICNGOTI2NzI7Cn0KLmhsanMtcGF0dGVybi1tYXRjaCAuaGxqcy1jb25zdHJ1Y3RvciB7CiAgY29sb3I6ICM2MWFlZWU7Cn0KLmhsanMtZnVuY3Rpb24gewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWZ1bmN0aW9uIC5obGpzLXBhcmFtcyB7CiAgY29sb3I6ICNBNkUyMkU7Cn0KLmhsanMtZnVuY3Rpb24gLmhsanMtcGFyYW1zIC5obGpzLXR5cGluZyB7CiAgY29sb3I6ICNGRDk3MUY7Cn0KLmhsanMtbW9kdWxlLWFjY2VzcyAuaGxqcy1tb2R1bGUgewogIGNvbG9yOiAjN2U1N2MyOwp9Ci5obGpzLWNvbnN0cnVjdG9yIHsKICBjb2xvcjogI2UyYjkzZDsKfQouaGxqcy1jb25zdHJ1Y3RvciAuaGxqcy1zdHJpbmcgewogIGNvbG9yOiAjOUNDQzY1Owp9Ci5obGpzLWNvbW1lbnQsIC5obGpzLXF1b3RlIHsKICBjb2xvcjogI2IxOGViMTsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtZG9jdGFnLCAuaGxqcy1mb3JtdWxhIHsKICBjb2xvcjogI2M2NzhkZDsKfQouaGxqcy1zZWN0aW9uLCAuaGxqcy1uYW1lLCAuaGxqcy1zZWxlY3Rvci10YWcsIC5obGpzLWRlbGV0aW9uLCAuaGxqcy1zdWJzdCB7CiAgY29sb3I6ICNlMDZjNzU7Cn0KLmhsanMtbGl0ZXJhbCB7CiAgY29sb3I6ICM1NmI2YzI7Cn0KLmhsanMtc3RyaW5nLCAuaGxqcy1yZWdleHAsIC5obGpzLWFkZGl0aW9uLCAuaGxqcy1hdHRyaWJ1dGUsIC5obGpzLW1ldGEgLmhsanMtc3RyaW5nIHsKICBjb2xvcjogIzk4YzM3OTsKfQouaGxqcy1idWlsdF9pbiwKLmhsanMtdGl0bGUuY2xhc3NfLAouaGxqcy1jbGFzcyAuaGxqcy10aXRsZSB7CiAgY29sb3I6ICNlNmMwN2I7Cn0KLmhsanMtYXR0ciwgLmhsanMtdmFyaWFibGUsIC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLCAuaGxqcy10eXBlLCAuaGxqcy1zZWxlY3Rvci1jbGFzcywgLmhsanMtc2VsZWN0b3ItYXR0ciwgLmhsanMtc2VsZWN0b3ItcHNldWRvLCAuaGxqcy1udW1iZXIgewogIGNvbG9yOiAjZDE5YTY2Owp9Ci5obGpzLXN5bWJvbCwgLmhsanMtYnVsbGV0LCAuaGxqcy1saW5rLCAuaGxqcy1tZXRhLCAuaGxqcy1zZWxlY3Rvci1pZCwgLmhsanMtdGl0bGUgewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQouaGxqcy1saW5rIHsKICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKfQo=`;
|
@@ -1 +1 @@
|
||||
module.exports = `cHJlIGNvZGUuaGxqc3tkaXNwbGF5OmJsb2NrO292ZXJmbG93LXg6YXV0bztwYWRkaW5nOjFlbX1jb2RlLmhsanN7cGFkZGluZzozcHggNXB4fS5obGpze2NvbG9yOiMzODNhNDI7YmFja2dyb3VuZDojZmFmYWZhfS5obGpzLWNvbW1lbnQsLmhsanMtcXVvdGV7Y29sb3I6I2EwYTFhNztmb250LXN0eWxlOml0YWxpY30uaGxqcy1kb2N0YWcsLmhsanMtZm9ybXVsYSwuaGxqcy1rZXl3b3Jke2NvbG9yOiNhNjI2YTR9LmhsanMtZGVsZXRpb24sLmhsanMtbmFtZSwuaGxqcy1zZWN0aW9uLC5obGpzLXNlbGVjdG9yLXRhZywuaGxqcy1zdWJzdHtjb2xvcjojZTQ1NjQ5fS5obGpzLWxpdGVyYWx7Y29sb3I6IzAxODRiYn0uaGxqcy1hZGRpdGlvbiwuaGxqcy1hdHRyaWJ1dGUsLmhsanMtbWV0YSAuaGxqcy1zdHJpbmcsLmhsanMtcmVnZXhwLC5obGpzLXN0cmluZ3tjb2xvcjojNTBhMTRmfS5obGpzLWF0dHIsLmhsanMtbnVtYmVyLC5obGpzLXNlbGVjdG9yLWF0dHIsLmhsanMtc2VsZWN0b3ItY2xhc3MsLmhsanMtc2VsZWN0b3ItcHNldWRvLC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLC5obGpzLXR5cGUsLmhsanMtdmFyaWFibGV7Y29sb3I6Izk4NjgwMX0uaGxqcy1idWxsZXQsLmhsanMtbGluaywuaGxqcy1tZXRhLC5obGpzLXNlbGVjdG9yLWlkLC5obGpzLXN5bWJvbCwuaGxqcy10aXRsZXtjb2xvcjojNDA3OGYyfS5obGpzLWJ1aWx0X2luLC5obGpzLWNsYXNzIC5obGpzLXRpdGxlLC5obGpzLXRpdGxlLmNsYXNzX3tjb2xvcjojYzE4NDAxfS5obGpzLWVtcGhhc2lze2ZvbnQtc3R5bGU6aXRhbGljfS5obGpzLXN0cm9uZ3tmb250LXdlaWdodDo3MDB9LmhsanMtbGlua3t0ZXh0LWRlY29yYXRpb246dW5kZXJsaW5lfQ==`;
|
||||
module.exports = `LyoKCkF0b20gT25lIExpZ2h0IGJ5IERhbmllbCBHYW1hZ2UKT3JpZ2luYWwgT25lIExpZ2h0IFN5bnRheCB0aGVtZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9hdG9tL29uZS1saWdodC1zeW50YXgKCmJhc2U6ICAgICNmYWZhZmEKbW9uby0xOiAgIzM4M2E0Mgptb25vLTI6ICAjNjg2Yjc3Cm1vbm8tMzogICNhMGExYTcKaHVlLTE6ICAgIzAxODRiYgpodWUtMjogICAjNDA3OGYyCmh1ZS0zOiAgICNhNjI2YTQKaHVlLTQ6ICAgIzUwYTE0ZgpodWUtNTogICAjZTQ1NjQ5Cmh1ZS01LTI6ICNjOTEyNDMKaHVlLTY6ICAgIzk4NjgwMQpodWUtNi0yOiAjYzE4NDAxCgoqLwoKLmhsanMgewogIGNvbG9yOiAjMzgzYTQyOwogIGJhY2tncm91bmQ6ICNmYWZhZmE7Cn0KCi5obGpzLWNvbW1lbnQsCi5obGpzLXF1b3RlIHsKICBjb2xvcjogI2EwYTFhNzsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KCi5obGpzLWRvY3RhZywKLmhsanMta2V5d29yZCwKLmhsanMtZm9ybXVsYSB7CiAgY29sb3I6ICNhNjI2YTQ7Cn0KCi5obGpzLXNlY3Rpb24sCi5obGpzLW5hbWUsCi5obGpzLXNlbGVjdG9yLXRhZywKLmhsanMtZGVsZXRpb24sCi5obGpzLXN1YnN0IHsKICBjb2xvcjogI2U0NTY0OTsKfQoKLmhsanMtbGl0ZXJhbCB7CiAgY29sb3I6ICMwMTg0YmI7Cn0KCi5obGpzLXN0cmluZywKLmhsanMtcmVnZXhwLAouaGxqcy1hZGRpdGlvbiwKLmhsanMtYXR0cmlidXRlLAouaGxqcy1tZXRhIC5obGpzLXN0cmluZyB7CiAgY29sb3I6ICM1MGExNGY7Cn0KCi5obGpzLWF0dHIsCi5obGpzLXZhcmlhYmxlLAouaGxqcy10ZW1wbGF0ZS12YXJpYWJsZSwKLmhsanMtdHlwZSwKLmhsanMtc2VsZWN0b3ItY2xhc3MsCi5obGpzLXNlbGVjdG9yLWF0dHIsCi5obGpzLXNlbGVjdG9yLXBzZXVkbywKLmhsanMtbnVtYmVyIHsKICBjb2xvcjogIzk4NjgwMTsKfQoKLmhsanMtc3ltYm9sLAouaGxqcy1idWxsZXQsCi5obGpzLWxpbmssCi5obGpzLW1ldGEsCi5obGpzLXNlbGVjdG9yLWlkLAouaGxqcy10aXRsZSB7CiAgY29sb3I6ICM0MDc4ZjI7Cn0KCi5obGpzLWJ1aWx0X2luLAouaGxqcy10aXRsZS5jbGFzc18sCi5obGpzLWNsYXNzIC5obGpzLXRpdGxlIHsKICBjb2xvcjogI2MxODQwMTsKfQoKLmhsanMtZW1waGFzaXMgewogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoKLmhsanMtbGluayB7CiAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7Cn0K`;
|
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
hash:"dd2315568bb7795f97cee26a47e9b82b", files: {
|
||||
hash:"ea13a22d0df59339b671f6b5700e2914", files: {
|
||||
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
module.exports = `d09GMgABAAAAAA1oAA4AAAAAG5wAAA0RAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAgRQIDAmXFxEICo8Ai1YBNgIkA14LMgAEIAWIYgeBHAx/G8kYIxFmk1QA+IsE3hD16QuQENMSYXJ4+9F5hfA6Tq6zy/sjJJn14Wlbvf8nGGYoJbd0rMKI2DAKBF2jCrfwrnG3jYsIvchWL7oh+Hb9vn4zTcBxyIHEsWbncP0PMA4b/h9t/awowdPc8MA3g9iXwbIG/p0EzeyXoqaop6tOBrfMnHeEmFZ7hJ2mYvTqERU4fVRvN6X/b660///kaK5IiskT2ApZYyazu/cymaXsAVG2hKQAFTkiUO3J+roTuq++ylTIqg5Zxn8mCgpd1yXakKZViWI5+LSbPwAo/ZkcBwdQD30AwOHy8cmHiA+Kx4Bu4Acdvv0Or1J1ijuRR15fMbJ3TUO9gblLBo41M0iSZHOLaJcGpMixIQv3n72dDgMVZBFiNdnngMvoU/RdhgncGiSFfeX1BsjCxdpLgbSBEPbl74V716abHHPU4H87/0v4T/3eH++Jb1pBkPyk7RFEC9jTNEtv/kHzeAP//JRIWhP+Kifq7Qmq3X/BNVx2aAvnPagHhTYZQBxLDBAgxycBCh2JR7Zgw6ch3IucHk53Pidv3vVOUbWVPHnZibRaRdorqxLmdkeE054WrqE4K44aHnHoRL2nC0GUhbXkfkusUQqk1ICSc3YQy9lAVTrOOcBAU8FqdyQ4biU4ifASnfcZEgdYmDevo8qlUTe3AaKUAlpHLszN594J1cv/q8tW46ohsGE7lS3X7eRk2Yu83QQxNzCPdn7PyAEFbw2g8AD/zhQSTSAodtD0BJJBYrhbQAFpKiQe/mNdKil55JVMFl8mhl5OwKBoO07jTQ84KOd2gFeUdHNiBgphtWjaByU6QiuEbYhWI6/Jw4j3ArVuomZFJBE2wZWI+e5AJQb1jgapVY6Sd+bzTR9Qo9JSRGc3CWgAiSVUfL01gjKJGXUL1qB6/IAWQgfLFfWPdVkflKBhUd8qzEVgd6LDm71U2XySL6Mu7EfAhy3YTm92z7bzQ9N37osKLfdNLbo2JIloLYNCqMaq2qF6SsjMBKNbUgHg+LAae49UT3LzBknqXkcD1DhdN2dGxTlqMzLzcNeCOPG6FThlEwhBzkC8v9YAPZYqaLSX0ErUFEkiooDWqAJJfusGy+i5e+xbq28tiqlb7lIOGFChvxUwopIymIiCmRiwEAtbiIOtxMM2UsB2EmAHKZ0RUAP5erRI+NWi4O+E9sMSarMT/dEPZbGBAKIQSAwEEQvBxEEI8RBKCggjAcJJaUYbtBBDTHhBF2tslBq7hOMQ0TFXa6wgxoi5BWIhzeWNPOvWbCpbcKrJnM+BOBedn8yIvVXOT6fpTh5kIB6d+ZPkJ0APbZaJ2vWQSFE6xAyYb18eTL+Q1J7JU1urCd54M5AEtZrbnAIK/RcKJKM51wZSyHMhPCyZMhWVutkVNT0NBEX3e3rVe+Y2t05p4XyGSBxSPJmevhlIAzDBKqySnRklbx33VpHaPk0yUHqCGjl5sBIp00TrMj9l3nb6SQ+rskPXCatbc4wmBKoP4kixWfFtEWqxmIvcE/4ZKMuRwbA7coOwO3dbZR6aJ99ddSbBBpt1a84QOIMqdGlab9HOsrW0rCw4F067c56EabBhVEoNidbinunm0JeXAxk49CAJLqRYu5bSMntDkChpJhQKj1lZfa5BeFC9HGypUWveQTu2EBzsNq/MUq9egd3Omm8Ue7xjSSj7sq3w0z1WeYme5zDj3FmWA1OD3BlnO0ltbqrVG26lonIH2VpGHsf86Q6dPyLNBDsLRqRbL7MiYNbXp23DYNlglQcKo4yCJwxzTK3ctyYyp2hUhfeqjuH4T8wjUAxBzDH7t7bmvM8TfbbTKgYkDgMqSfS2X4etDRt3eo0LaGjAsmieykUn3YZmV8nuUQUhbJLv3D0jUiGUXgqmqanRrA9KsAqVjULSpjQMKx8NeKCoYwURqGQEWEUEqhsCxVhDBGoZAdqIgL0hkI91RMDBCNBJBOobAoXYQAT2MgJsJAJNDYESbCYCLYwAW4lAW0OgANuJQAcjwE4ixV3RqxlQW90N9mrOS+vBXOgNwbTGPOgLZHJAv54JA3qmGtxgHgwFsh4wrGfBiJ4Fo3oWjEVlw7ieDRN6Nkzq2ZNMRd+uZOkbKLNgTip0rTLBVbOvK+eIAQHxetMiYRY3iAMYgfwM0M+QRG/XZg7Fssh+jAA5I8v6bzcJFqFqlyF0t/349n2ZLRInsXUSczDR5TaIphtqPm/eIPOOD3pejzJLS3Ab3niXnmZ/y77UcXb3/IokGa+v/qxpHW7bu52vhBtEzzJ52PGFy3MBmTev/OPfWlq43nCe840/bX8aFl371BBdElzuRdFjWp7b+LvubTK/Ii0Zl1zrf9nf2kfULhWR9rkMomljo+6CKxreLpvLci4804r55dpNx8red67cBh1myQi3YT/TPeFv2M67zPnmZZY3lrm4XclgCFlft59/eb0b61uWsMKvT1/CEv75r8bj7jbrcbj93pClJbhrgs9k1foMMlz2BwnRm+Y9d9Nx4QfjXDdm3p8ZFrpRd8H7W035mzUvfuJwtH0kuS0Wz/eip/VDcesFLX7zm2GbjqelBeO6fb34TmnBca20w217w7boMW3UbZTcJXpsV81xD/vc9V3Lf5vm4xavMxiWTSaHwzWPq2PJ6P7TsKgRt5DKhV1Q3AfzJJcau/FUxEFaPsD/6kDju+6mJriv85iWm10eknTON79P5s0XKmzj0oevJzcsJhiXjR3xCWsJQtTrNzF2/8TNw8k2ayZfOT3JV1sz4hc9vJnob2fYGx979oH8hexc796BT769kb/x7y3nnitMaLQ8/Wbw1ZNTfJU184fQajuE7zPK/A/5JM/p75Q+lFgSnvJViG+pTkXE6qMH/ar1v9gafvRNlBL7ee3EnVC2qHNehvDJyzl89uMXSF5R25+o4DJDywtOeWpBUP2netTZPNviUR53Duaoud8sH3rlrXa/9ZYw136fPUUidPK7VN7mLgq+i6EGc8LEh2JykapGiXy6sb7ieU/Yfveza3U+9FT8qeZd+qZEBXS0UpGmqHOmIiLgzg7daYItYmi/dmeRBE1cKV6Io1Tf1Lyr5otQHx0vJJ/lV6ziNJ81hc3O+eyuU0GL3Kmb6rGXznWEqEU+seCiSwp2aRp3II9WVSfjUi5/Q59pdLWiglcmt8fplqotCyATWlVFYt5g+bE/09Lva916Bcg4cjz/hOt6nmk42We7N/LeqP7afhshCQnc42eyTGdBewETt+DFtuNvyp89K3JME5aeuVqmOBhcv2XlqdQrf6ssYY5zk4HXJx2VVhtWy3h3yDf27I198Q2n7RU8MvvELq8/2b5/c1tPv0OnvoOC+aVfSi6x9bxQ6iqbVeufYfyf6TXUq/h7eSHw2YzEcGMY0nZ4fLziHAWUAW7EYplDiF7PLUyRw/sEzXSVQ35H/IGffL7e/dVu/S06NTTCF4Vf5PPHFF8UKDU6y/f6XL+84vU7/fyUXxRM/WWhmpIzDT63OK/k44n2fF2Kg41yAFL/UuZd7gbvvzSJu5Zh/3biaDBnuJNnCWhc1O0+ZkxmYJkSGmEayhiiBbBQykR2nQrU2Ta9RkbLEObLxajAJwMe6aLno79u0Wb9JkjMlwDw7ti/15dv+Lfz3z3ctdwNAARQgItzPJODhnqVItr77TZAkJSb/Y0E8tRyIC4F/RCvnlMHFgNSkw5/YEwrlej3OM2s0+lyuKu8/1oXIMDfzj6bmC8xknbi94BULv8eBzXrbGtRhbpuDXVmGm4dKyK5mRLjGBBWCeAKoN6BwOSKDhQa3NOBQYmnOrAIDVUHDsGRsCTf10FNu0nd+9QgKT+AQiNGuYzr06PXJFm4DhFkCeLESRdzk/FtfFmRLhP0crhjLW/Y1W+HLtFku3fOSpTePF4SpslSnWqTjPmpbTq9dIhLaYy2Pp1a1OozV5qoRldLnTKoKB1XYqRaTZbleE516VFWq3ckgyxnGTHHRUEp65Yax4FGDDt/inRpkiRLldqyCJFdVdFHsU3PMpOWWg1pa6m9+rGc5dwI/Cc5InwwL62+Acql2g5za5fRztWqzzYl46cmUJt8WpdOsr+6lFVoM7RAPhKRJMWc7GWXnuCWtRF2y5mJ4z6ZSg5i1YEdTmAnTqlOG5RsleqSkLpbWUuvNEo/l/Sly6RA9Lx9nh7Pmrusm7/uUlmbaVN9UdOw3eCSMi6qTWt6vup6+aQMoo/4JUebjN3TJ3T4To52HiurBYFBpE/8WJVKlPHFUyRj9Z5eAr/la3rvHRJU8JUjV55mrUHxJt4iDGFxNeEITxREIEoiKqaG++Li8uPKp0+MS1BO9g12dv0vYaIVEnxp64j0iHytoGlqcdqN0lH64Zka5xohR+tXycftM9418AVrQOR6UOGAewEoj2dmUPSCyTnVztW8c5wMdFKo4z7BDcLSaR02J5nc29nXoIq8h3jPHHtkjVamcZ1FAAAA`;
|
||||
module.exports = `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`;
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -550,6 +550,7 @@ async function initialize(dispatch: Function) {
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
BaseItem.encryptionService_ = EncryptionService.instance();
|
||||
BaseItem.shareService_ = ShareService.instance();
|
||||
Resource.shareService_ = ShareService.instance();
|
||||
DecryptionWorker.instance().dispatch = dispatch;
|
||||
DecryptionWorker.instance().setLogger(mainLogger);
|
||||
DecryptionWorker.instance().setKvStore(KvStore.instance());
|
||||
|
@@ -10,6 +10,9 @@
|
||||
"keywords": [
|
||||
"joplin-plugin"
|
||||
],
|
||||
"files": [
|
||||
"publish"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.0.14",
|
||||
"chalk": "^4.1.0",
|
||||
|
@@ -54,6 +54,7 @@ import { loadMasterKeysFromSettings, migrateMasterPassword } from './services/e2
|
||||
import SyncTargetNone from './SyncTargetNone';
|
||||
import { setRSA } from './services/e2ee/ppk';
|
||||
import RSA from './services/e2ee/RSA.node';
|
||||
import Resource from './models/Resource';
|
||||
|
||||
const appLogger: LoggerWrapper = Logger.create('App');
|
||||
|
||||
@@ -855,6 +856,7 @@ export default class BaseApplication {
|
||||
|
||||
BaseItem.encryptionService_ = EncryptionService.instance();
|
||||
BaseItem.shareService_ = ShareService.instance();
|
||||
Resource.shareService_ = ShareService.instance();
|
||||
DecryptionWorker.instance().setLogger(globalLogger);
|
||||
DecryptionWorker.instance().setEncryptionService(EncryptionService.instance());
|
||||
DecryptionWorker.instance().setKvStore(KvStore.instance());
|
||||
|
@@ -98,14 +98,14 @@ class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
// We could implement that here, but the above workaround saves some code.
|
||||
|
||||
static async checkConfig(options) {
|
||||
const fileApi = await SyncTargetAmazonS3.newFileApi_(SyncTargetAmazonS3.id(), options);
|
||||
fileApi.requestRepeatCount_ = 0;
|
||||
|
||||
const output = {
|
||||
ok: false,
|
||||
errorMessage: '',
|
||||
};
|
||||
try {
|
||||
const fileApi = await SyncTargetAmazonS3.newFileApi_(SyncTargetAmazonS3.id(), options);
|
||||
fileApi.requestRepeatCount_ = 0;
|
||||
|
||||
const headBucketReq = new Promise((resolve, reject) => {
|
||||
fileApi.driver().api().send(
|
||||
|
||||
|
@@ -35,7 +35,7 @@ export const runtime = (): CommandRuntime => {
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
reg.logger().info('Not authentified with sync target - please check your credential.');
|
||||
reg.logger().error('Not authenticated with sync target - please check your credentials.');
|
||||
return 'error';
|
||||
}
|
||||
|
||||
@@ -43,8 +43,13 @@ export const runtime = (): CommandRuntime => {
|
||||
try {
|
||||
sync = await reg.syncTarget().synchronizer();
|
||||
} catch (error) {
|
||||
reg.logger().info('Could not acquire synchroniser:');
|
||||
reg.logger().info(error);
|
||||
reg.logger().error('Could not initialise synchroniser: ');
|
||||
reg.logger().error(error);
|
||||
error.message = `Could not initialise synchroniser: ${error.message}`;
|
||||
utils.store.dispatch({
|
||||
type: 'SYNC_REPORT_UPDATE',
|
||||
report: { errors: [error] },
|
||||
});
|
||||
return 'error';
|
||||
}
|
||||
|
||||
|
@@ -248,7 +248,7 @@ shared.toggleIsTodo_onPress = function(comp) {
|
||||
comp.setState(newState);
|
||||
};
|
||||
|
||||
shared.toggleCheckbox = function(ipcMessage, noteBody) {
|
||||
function toggleCheckboxLine(ipcMessage, noteBody) {
|
||||
const newBody = noteBody.split('\n');
|
||||
const p = ipcMessage.split(':');
|
||||
const lineIndex = Number(p[p.length - 1]);
|
||||
@@ -281,7 +281,18 @@ shared.toggleCheckbox = function(ipcMessage, noteBody) {
|
||||
} else {
|
||||
line = line.replace(/- \[x\] /i, '- [ ] ');
|
||||
}
|
||||
return [newBody, lineIndex, line];
|
||||
}
|
||||
|
||||
shared.toggleCheckboxRange = function(ipcMessage, noteBody) {
|
||||
const [lineIndex, line] = toggleCheckboxLine(ipcMessage, noteBody).slice(1);
|
||||
const from = { line: lineIndex, ch: 0 };
|
||||
const to = { line: lineIndex, ch: line.length };
|
||||
return { line, from, to };
|
||||
};
|
||||
|
||||
shared.toggleCheckbox = function(ipcMessage, noteBody) {
|
||||
const [newBody, lineIndex, line] = toggleCheckboxLine(ipcMessage, noteBody);
|
||||
newBody[lineIndex] = line;
|
||||
return newBody.join('\n');
|
||||
};
|
||||
|
@@ -105,7 +105,7 @@ shared.synchronize_press = async function(comp) {
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
reg.logger().info('Not authentified with sync target - please check your credential.');
|
||||
reg.logger().error('Not authenticated with sync target - please check your credentials.');
|
||||
return 'error';
|
||||
}
|
||||
|
||||
@@ -113,8 +113,13 @@ shared.synchronize_press = async function(comp) {
|
||||
try {
|
||||
sync = await reg.syncTarget().synchronizer();
|
||||
} catch (error) {
|
||||
reg.logger().info('Could not acquire synchroniser:');
|
||||
reg.logger().info(error);
|
||||
reg.logger().error('Could not initialise synchroniser: ');
|
||||
reg.logger().error(error);
|
||||
error.message = `Could not initialise synchroniser: ${error.message}`;
|
||||
comp.props.dispatch({
|
||||
type: 'SYNC_REPORT_UPDATE',
|
||||
report: { errors: [error] },
|
||||
});
|
||||
return 'error';
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
const urlUtils = require('./urlUtils.js');
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
const htmlparser2 = require('@joplin/fork-htmlparser2');
|
||||
const { escapeHtml } = require('./string-utils.js');
|
||||
|
||||
// [\s\S] instead of . for multiline matching
|
||||
@@ -138,40 +137,6 @@ class HtmlUtils {
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
public stripHtml(html: string) {
|
||||
const output: string[] = [];
|
||||
|
||||
const tagStack: any[] = [];
|
||||
|
||||
const currentTag = () => {
|
||||
if (!tagStack.length) return '';
|
||||
return tagStack[tagStack.length - 1];
|
||||
};
|
||||
|
||||
const disallowedTags = ['script', 'style', 'head', 'iframe', 'frameset', 'frame', 'object', 'base'];
|
||||
|
||||
const parser = new htmlparser2.Parser({
|
||||
|
||||
onopentag: (name: string) => {
|
||||
tagStack.push(name.toLowerCase());
|
||||
},
|
||||
|
||||
ontext: (decodedText: string) => {
|
||||
if (disallowedTags.includes(currentTag())) return;
|
||||
output.push(decodedText);
|
||||
},
|
||||
|
||||
onclosetag: (name: string) => {
|
||||
if (currentTag() === name.toLowerCase()) tagStack.pop();
|
||||
},
|
||||
|
||||
}, { decodeEntities: true });
|
||||
|
||||
parser.write(html);
|
||||
parser.end();
|
||||
|
||||
return output.join('').replace(/\s+/g, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
export default new HtmlUtils();
|
||||
|
@@ -79,7 +79,6 @@
|
||||
"Auto": "تلقائي",
|
||||
"Auto-pair braces, parenthesis, quotations, etc.": "ادراج النصف الآخر للأقواس و علامات الإقتباس.",
|
||||
"Automatically switch theme to match system theme": "التبديل تلقائيا للتنسيق لمطابقة تنسيق النضام",
|
||||
"Automatically update the application": "تحديث التطبيق آلياً",
|
||||
"Back": "عودة",
|
||||
"Bold": "غامق",
|
||||
"Browse all plugins": "تصفح جميع المكونات الإضافية",
|
||||
@@ -485,7 +484,6 @@
|
||||
"Note: Does not work in all desktop environments.": "ملاحظة: لا تعمل في كل بيئات سطح المكتب.",
|
||||
"Note: When a note is shared, it will no longer be encrypted on the server.": "ملاحظة: عند مشاركة ملاحظة ، لن يتم تشفيرها على الخادم.",
|
||||
"Notebook list growth factor": "عامل نمو قائمة المفكرة",
|
||||
"Notebook title:": "عنوان دفتر الملاحظات:",
|
||||
"Notebook: %s": "دفتر ملاحظات: \"%s\"",
|
||||
"Notebooks": "دفاتر ملاحظات",
|
||||
"Notebooks cannot be named \"%s\", which is a reserved title.": "لا يمكن تسمية دفتر الملاحظات بـ \"%s\" لأنه عنوان محجوز.",
|
||||
|
@@ -40,7 +40,6 @@
|
||||
"Authentication was not completed (did not receive an authentication token).": "Оторизирането не беше завършено (не получих оторизационен жетон).",
|
||||
"Authorisation token:": "Оторизационен жетон:",
|
||||
"Auto": "Автоматичен",
|
||||
"Automatically update the application": "Автоматично обновление на приложението",
|
||||
"Back": "Назад",
|
||||
"Bold": "Удебелен",
|
||||
"Browse...": "Търсене...",
|
||||
@@ -281,7 +280,6 @@
|
||||
"Note properties": "Атрибути на бележката",
|
||||
"Note title": "Заглавие на бележката",
|
||||
"Note: Does not work in all desktop environments.": "Внимание: Не работи с всички десктоп среди.",
|
||||
"Notebook title:": "Заглавие на тетрадка:",
|
||||
"Notebooks": "Тетрадки",
|
||||
"Notebooks cannot be named \"%s\", which is a reserved title.": "Тетрадки не могат да имат име \"%s\", защото това е запазено заглавие.",
|
||||
"Notes and settings are stored in: %s": "Бележките и настройките са запазени в: %s",
|
||||
|
@@ -52,7 +52,6 @@
|
||||
"Auto": "Automatski",
|
||||
"Auto-pair braces, parenthesis, quotations, etc.": "Automatski piši u paru sve zagrade, navodnike itd.",
|
||||
"Automatically switch theme to match system theme": "Automatski podesi temu prema sistemskoj",
|
||||
"Automatically update the application": "Automatski ažuriraj aplikaciju",
|
||||
"Back": "Nazad",
|
||||
"Bold": "Masna slova",
|
||||
"Browse all plugins": "Pogledaj dodatke",
|
||||
@@ -353,7 +352,6 @@
|
||||
"Note: Does not work in all desktop environments.": "Napomena: neće raditi na svim sistemima.",
|
||||
"Note: When a note is shared, it will no longer be encrypted on the server.": "Napomena: bilješka koja se podijeli s drugima više neće biti šifrirana na serveru.",
|
||||
"Notebook list growth factor": "Faktor rasta popisa bilježnica",
|
||||
"Notebook title:": "Naziv bilježnice:",
|
||||
"Notebook: %s": "Bilježnica: %s",
|
||||
"Notebooks": "Bilježnice",
|
||||
"Notebooks cannot be named \"%s\", which is a reserved title.": "Bilježnica se ne može nazvati \"%s\", jer je to rezervisani naziv.",
|
||||
|
@@ -79,10 +79,9 @@
|
||||
"Auto": "Automàtic",
|
||||
"Auto-pair braces, parenthesis, quotations, etc.": "Aparellament automàtic de claus, parèntesis, cites, etc.",
|
||||
"Automatically switch theme to match system theme": "Canvia de tema automàticament per a coincidir amb el tema del sistema",
|
||||
"Automatically update the application": "Actualitza automàticament l'aplicació",
|
||||
"Back": "Enrere",
|
||||
"Bold": "Negreta",
|
||||
"Browse all plugins": "Navega per tots els connectors",
|
||||
"Browse all plugins": "Navega per totes les extensions",
|
||||
"Browse...": "Navega...",
|
||||
"Bulleted List": "Llista de pics",
|
||||
"Cancel": "Cancel·la",
|
||||
@@ -101,6 +100,7 @@
|
||||
"Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.": "No es pot actualitzar el testimoni: manquen les dades d'autenticació. Reiniciar la sincronització pot solucionar el problema.",
|
||||
"Cannot save %s \"%s\" because it is larger than the allowed limit (%s)": "No es pot desar %s «%s» perquè és més gran que el límit permès (%s)",
|
||||
"Cannot save %s \"%s\" because it would go over the total allowed size (%s) for this account": "No es pot desar %s «%s» perquè se'n passaria de l'espai total (%s) d'aquest compte",
|
||||
"Cannot share encrypted notebook with recipient %s because they have not enabled end-to-end encryption. They may do so from the screen Configuration > Encryption.": "No es pot compartir el bloc de notes xifrat amb el destinatari %sperquè no té habilitat el xifrat d'extrem a extrem. Pot fer-ho des de la finestra Configuració > Xifrat",
|
||||
"Change application layout": "Canvia la disposició de l'aplicació",
|
||||
"Change language": "Canvia la llengua",
|
||||
"Characters": "Caràcters",
|
||||
@@ -139,10 +139,12 @@
|
||||
"Conflicted: %d": "Conflictius: %d",
|
||||
"Conflicts": "Conflictes",
|
||||
"Conflicts (attachments)": "Conflictes (adjunts)",
|
||||
"Content provided by %s": "Contingut proporcionat per %s",
|
||||
"Convert to note": "Converteix a nota",
|
||||
"Convert to todo": "Converteix a llistat de tasques pendents",
|
||||
"Copy": "Copia",
|
||||
"Copy dev mode command to clipboard": "Copia l'ordre en mode de desenvolupament al porta-retalls",
|
||||
"Copy external link": "Copia l'enllaç extern",
|
||||
"Copy Link Address": "Copia l'adreça de l'enllaç",
|
||||
"Copy Markdown link": "Copia l'enllaç Markdown",
|
||||
"Copy path to clipboard": "Copia el camí al porta-retalls",
|
||||
@@ -150,9 +152,12 @@
|
||||
"Copy token": "Copia testimoni",
|
||||
"Could not authorise application:\n\n%s\n\nPlease try again.": "No s'ha pogut autoritzar l'aplicació:\n\n%s\n\nTorneu-ho a provar.",
|
||||
"Could not connect to Joplin Server. Please check the Synchronisation options in the config screen. Full error was:\n\n%s": "No s'ha pogut connectar amb el Joplin Server. Comproveu la configuració en la pantalla de Sincronització. L'error complet és: %s",
|
||||
"Could not connect to plugin repository.": "No s'ha pogut connectar al repositori d'extensions",
|
||||
"Could not export notes: %s": "No s'han pogut exportar les notes: %s",
|
||||
"Could not install plugin: %s": "No s'ha pogut instal·lar el complement: %s",
|
||||
"Could not install plugin: %s": "No s'ha pogut instal·lar l'extensió: %s",
|
||||
"Could not respond to the invitation. Please try again, or check with the notebook owner if they are still sharing it.\n\nThe error was: \"%s\"": "No s'ha pogut contestar a la invitació. Torneu-ho a provar, o comproveu amb el propietari del bloc de notes si encara l'està compartint.\n\nL'error és \"%s\"",
|
||||
"Could not upgrade master key: %s": "No s'ha pogut actualitzar la clau mestra: %s",
|
||||
"Could not verify the share status of this notebook - aborting. Please try again when you are connected to the internet.": "No s'ha pogut verificar l'estat de compartició d'aquest bloc de notes - s'està avortant. Torneu a provar quan tingueu connexió a Internet.",
|
||||
"Create a notebook": "Crea un bloc de notes nou",
|
||||
"Created": "Creació",
|
||||
"created date": "data de creació",
|
||||
@@ -192,7 +197,7 @@
|
||||
"Delete note?": "Voleu suprimir la nota?",
|
||||
"Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.": "Voleu suprimir el bloc de notes \"%s\"? \n\nTambé se suprimiran totes les notes i els subblocs d'aquest bloc de notes.",
|
||||
"Delete notebook? All notes and sub-notebooks within this notebook will also be deleted.": "Voleu suprimir el bloc de notes? També se suprimiran totes les notes i els subblocs d'aquest bloc de notes.",
|
||||
"Delete plugin \"%s\"?": "Voleu suprimir el complement \"%s\"?",
|
||||
"Delete plugin \"%s\"?": "Voleu suprimir l'extensió \"%s\"?",
|
||||
"Delete these %d notes?": "Voleu suprimir aquestes %d notes?",
|
||||
"Delete this invitation? The recipient will no longer have access to this shared notebook.": "Voleu suprimir aquesta invitació? El destinatari ja no tindrà accés a aquest quadern compartit.",
|
||||
"Deleted local items: %d.": "Elements locals suprimits: %d.",
|
||||
@@ -223,6 +228,7 @@
|
||||
"Displays version information": "Mostra la informació de versió",
|
||||
"Do it now": "Fes-ho ara",
|
||||
"Do not ask for confirmation.": "No demanis confirmació.",
|
||||
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": "No perdeu les contrasenyes perquè, per motius de seguretat, és l'única forma de desxifrar les dades. Per habilitar el xifrat, introduïu la vostra contrasenya.",
|
||||
"Download": "Baixa",
|
||||
"Download and install the relevant extension for your browser:": "Descarregueu i instal·leu l'extensió adient per al vostre navegador:",
|
||||
"Downloaded": "Descarregat",
|
||||
@@ -274,12 +280,16 @@
|
||||
"Enable Web Clipper Service": "Activa el servei del porta-retalls de webs",
|
||||
"Enable ~sub~ syntax": "Activa la sintaxi ~sub~",
|
||||
"Enabled": "Activat",
|
||||
"Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target.": "Habilitar el xifrat significa que totes les notes i adjunts es tornaran a sincronitzar i s'enviaran de forma xifrada a la destinació de sincronització.",
|
||||
"Encrypted": "Xifrat",
|
||||
"Encrypted items cannot be modified": "Els elements xifrats no es poden modificar",
|
||||
"Encrypted notebooks cannot be renamed": "No es pot canviar el nom dels blocs de notes xifrats",
|
||||
"Encryption": "Xifratge",
|
||||
"Encryption Config": "Configuració del xifratge",
|
||||
"Encryption is: %s": "El xifratge és: %s",
|
||||
"Encryption keys": "Claus de xifrat",
|
||||
"Encryption:": "Xifrat:",
|
||||
"End-to-end encryption": "Xifrat d'extrem a extrem",
|
||||
"Enter code here": "Entreu el codi aquí",
|
||||
"Enter master password:": "Introduïu una contrasenya mestra:",
|
||||
"Enter notebook title": "Entra el títol del bloc de notes",
|
||||
@@ -288,7 +298,7 @@
|
||||
"Error opening note in editor: %s": "S'ha produït un error en l'obrir la nota amb l'editor: %s",
|
||||
"Error. Please check that URL, username, password, etc. are correct and that the sync target is accessible. The reported error was:": "Error. Comproveu que l’URL, el nom d’usuari, la contrasenya, etc. siguin correctes i que el servei a sincronitzar sigui accessible. L'error reportat ha estat:",
|
||||
"Error: %s": "Error: %s",
|
||||
"Errors only": "Només els errors",
|
||||
"Errors only": "Només errors",
|
||||
"Evernote Export File (as HTML)": "Exporta com a fitxer de l'Evernote (com a HTML)",
|
||||
"Evernote Export File (as Markdown)": "Exporta com a fitxer de l'Evernote (com a Markdown)",
|
||||
"Exits the application.": "Surt de l'aplicació.",
|
||||
@@ -321,11 +331,13 @@
|
||||
"For information on how to customise the shortcuts please visit %s": "Per a obtenir informació sobre com personalitzar les dreceres de teclat, visiteu %s",
|
||||
"For more information about End-To-End Encryption (E2EE) and advice on how to enable it please check the documentation:": "Per a més informació sobre el xifratge d'extrem a extrem (E2EE de l'anglès) i consells sobre com activar-lo, llegiu la documentació:",
|
||||
"For the list of keyboard shortcuts and config options, type `help keymap`": "Per a llistar les dreceres de teclat i opcions de configuració, escriviu «help keymap»",
|
||||
"Force path style": "Força l'estil del camí",
|
||||
"Forward": "Endavant",
|
||||
"Found: %d.": "Trobades: %d.",
|
||||
"FTS enabled: %d": "FTS activat: %d",
|
||||
"Full changelog": "Registre complet de canvis",
|
||||
"General": "General",
|
||||
"Generated": "Generat",
|
||||
"Generating link...": "Generant enllaç...",
|
||||
"Get it now:": "Obteniu-lo ara:",
|
||||
"Get pre-releases when checking for updates": "Obtén prellançaments quan cerqui actualitzacions",
|
||||
@@ -336,8 +348,10 @@
|
||||
"Heading": "Capçalera",
|
||||
"Help": "Ajuda",
|
||||
"Hide %s": "Amaga %s",
|
||||
"Hide disabled keys": "Amaga les claus deshabilitades",
|
||||
"Hide Joplin": "Amaga el Joplin",
|
||||
"Highlight": "Ressalta",
|
||||
"Home": "Inici",
|
||||
"Horizontal Rule": "Línia horitzontal",
|
||||
"HTML Directory": "Directori HTML",
|
||||
"HTML File": "Fitxer HTML",
|
||||
@@ -375,8 +389,10 @@
|
||||
"Invalid answer: %s": "La resposta no és vàlida: %s",
|
||||
"Invalid command: \"%s\"": "Ordre no vàlida: «%s»",
|
||||
"Invalid option value: \"%s\". Possible values are: %s.": "El valor de l'opció no és vàlid: «%s». Els valors possibles són: %s.",
|
||||
"Invalid password": "Contrasenya incorrecta",
|
||||
"Italic": "Cursiva",
|
||||
"Item \"%s\" could not be downloaded: %s": "L'element \"%s\" no s'ha pogut descarregar: %s",
|
||||
"Items": "Elements",
|
||||
"Items that cannot be decrypted": "Elements que no s'han pogut desxifrar",
|
||||
"Items that cannot be synchronised": "Elements que no s'han pogut sincronitzar",
|
||||
"Joplin can synchronise your notes using various providers. Select one from the list below.": "Joplin pot sincronitzar les notes utilitzant diversos proveïdors. Seleccioneu-ne un de la llista inferior.",
|
||||
@@ -399,12 +415,14 @@
|
||||
"Keyboard Shortcut": "Drecera de teclat",
|
||||
"Keyboard Shortcuts": "Dreceres de teclat",
|
||||
"Keychain Supported: %s": "Clauer admès: %s",
|
||||
"Keys that need upgrading": "Claus que requereixen actualització",
|
||||
"Landscape": "Paisatge",
|
||||
"Language": "Llengua",
|
||||
"Last error: %s": "Últim error: %s",
|
||||
"Later": "Més tard",
|
||||
"Layout": "Disposició",
|
||||
"Layout button sequence": "Seqüència del botó de disposició",
|
||||
"Leave notebook...": "Surt del bloc de notes",
|
||||
"Legal": "Legal",
|
||||
"Letter": "Carta",
|
||||
"Light": "Clar",
|
||||
@@ -412,6 +430,7 @@
|
||||
"Link has been copied to clipboard!": "L'enllaç s'ha copiat al porta-retalls!",
|
||||
"Links with protocol \"%s\" are not supported": "Els enllaços amb el protocol %s no estan suportats",
|
||||
"List item": "Element de llista",
|
||||
"Loaded": "S'ha carregat",
|
||||
"Location": "Ubicació",
|
||||
"Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at \"%s\" and resume the operation.": "El fitxer de bloqueig està ús. Si sabeu que no s'està executant cap sincronització ara mateix, podeu suprimir el fitxer de bloqueig a «%s» i reprendre l'operació.",
|
||||
"Log": "Registre",
|
||||
@@ -419,22 +438,30 @@
|
||||
"Login below.": "Identifiqueu-vos baix.",
|
||||
"Login with Dropbox": "Inicia sessió amb Dropbox",
|
||||
"Login with OneDrive": "Inicia sessió amb OneDrive",
|
||||
"Logout": "Desconnecta",
|
||||
"Make a donation": "Donatius",
|
||||
"Manage your plugins": "Gestioneu els connectors",
|
||||
"Manage master password": "Gestiona la contrasenya mestra",
|
||||
"Manage master password...": "Gestiona la contrasenya mestra...",
|
||||
"Manage your plugins": "Gestioneu les extensions",
|
||||
"Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status`, `decrypt-file`, and `target-status`.": "Gestiona la configuració E2EE. Les ordres són `enable`, `disable`, `decrypt`, `status`, `decrypt-file`, i `target-status`.",
|
||||
"Manual": "Manual",
|
||||
"Markdown": "Markdown",
|
||||
"Markdown + Front Matter": "Markdown + Front Matter",
|
||||
"Marks a to-do as done.": "Marca un llistat de tasques pendents com a fet.",
|
||||
"Marks a to-do as non-completed.": "Marca un llistat de tasques pendents com a no finalitzat.",
|
||||
"Markup": "Marcatge",
|
||||
"Master Key %s": "Clau mestra %s",
|
||||
"Master password": "Contrasenya mestra",
|
||||
"Master password:": "Contrasenya mestra:",
|
||||
"Max concurrent connections": "Connexions simultànies màximes",
|
||||
"Missing keys": "Claus que falten",
|
||||
"Missing Master Keys": "Manquen les claus mestres",
|
||||
"Missing required argument: %s": "Manca un argument requerit: %s",
|
||||
"Mobile data - auto-sync disabled": "Dades mòbils - sincronització automàtica desactivada",
|
||||
"More info": "Més informació",
|
||||
"More information": "Més informació",
|
||||
"More than one item match \"%s\". Please narrow down your query.": "Hi ha més d'un element que coincideix amb «%s». Restringiu la consulta.",
|
||||
"Move %d notes to notebook \"%s\"?": "Voleu moure %d notes al bloc de notes «%s»?",
|
||||
"Move %d notes to notebook \"%s\"?": "Voleu moure %dnotes al bloc de notes \"%s\"?",
|
||||
"Move to notebook": "Mou al bloc de notes",
|
||||
"Move to notebook...": "Mou al bloc de notes...",
|
||||
"Move to notebook:": "Mou al bloc de notes:",
|
||||
@@ -468,6 +495,7 @@
|
||||
"Nord": "Nord",
|
||||
"Not authentified with %s. Please provide any missing credentials.": "No esteu autenticats amb %s. Proporcioneu les credencials que falten.",
|
||||
"Not downloaded": "No descarregat",
|
||||
"Not generated": "No s'ha generat",
|
||||
"note": "nota",
|
||||
"Note": "Nota",
|
||||
"Note area growth factor": "Factor de creixement de l'àrea de nota",
|
||||
@@ -485,11 +513,12 @@
|
||||
"Note&book": "&Blocs de notes",
|
||||
"Note: Does not work in all desktop environments.": "Nota: no funciona en tots els entorns d'escriptori.",
|
||||
"Note: When a note is shared, it will no longer be encrypted on the server.": "Nota: quan es comparteix una nota, deixa d'estar xifrada al servidor.",
|
||||
"Notebook": "Bloc de notes",
|
||||
"Notebook list growth factor": "Factor de creixement de la llista de blocs de notes",
|
||||
"Notebook title:": "Títol del bloc de notes:",
|
||||
"Notebook: %s": "Blocs de notes: %s",
|
||||
"Notebooks": "Blocs de notes",
|
||||
"Notebooks cannot be named \"%s\", which is a reserved title.": "Els blocs de notes no poden tenir el nom «%s», és un títol reservat.",
|
||||
"Notes": "otes",
|
||||
"Notes and settings are stored in: %s": "Les notes i la configuració es desen a: %s",
|
||||
"Notes can only be created within a notebook.": "Només podeu crear notes en un bloc de notes.",
|
||||
"Numbered List": "Llista numerada",
|
||||
@@ -522,19 +551,21 @@
|
||||
"PDF File": "Fitxer PDF",
|
||||
"Permission needed": "Cal permís",
|
||||
"Permission to use camera": "Permís per a utilitzar la càmera",
|
||||
"Please click on \"%s\" to proceed": "Feu clic a \"%s\" per a continuar",
|
||||
"Please confirm that you would like to re-encrypt your complete database.": "Confirmeu que voleu tornar a xifrar la vostra base de dades completa.",
|
||||
"Please enter your password in the master key list below before upgrading the key.": "Entreu la vostra contrasenya en la llista de claus mestres d'aquí a sota abans d'actualitzar la clau.",
|
||||
"Please note that if it is a large notebook, it may take a few minutes for all the notes to show up on the recipient's device.": "Teniu en compte que si és un bloc de notes llarg, pot necessitar uns quants minuts fins que totes les notes es mostren al dispositiu del destinatari.",
|
||||
"Please open the following URL in your browser to authenticate the application. The application will create a directory in \"Apps/Joplin\" and will only read and write files in this directory. It will have no access to any files outside this directory nor to any other personal data. No data will be shared with any third party.": "Obriu l'URL següent al navegador per a autenticar l'aplicació. L'aplicació crearà un directori a «Aplicacions/Joplin» i només llegirà i escriurà fitxers en aquest directory. No tindrà accés a cap fitxer fora d'aquest directori ni a cap dada personal. No es compartirà cap dada amb terceres parts.",
|
||||
"Please select a notebook first.": "Cal que primer seleccioneu un bloc de notes.",
|
||||
"Please select the note or notebook to be deleted first.": "Primer seleccioneu la nota o el bloc de notes que vulgueu suprimir.",
|
||||
"Please select where the sync status should be exported to": "Seleccioneu on s'hauria d'exportar l'estat de la sincronització",
|
||||
"Please specify import format for %s": "Indiqueu el format d'importació per a %s",
|
||||
"Please specify the notebook where the notes should be imported to.": "Indiqueu el bloc de notes on s'haurien d'importar les notes.",
|
||||
"Please upgrade Joplin to use this plugin": "Si us plau, actualitzeu Joplin per a utilitzar aquest connector",
|
||||
"Please upgrade Joplin to use this plugin": "Si us plau, actualitzeu Joplin per a utilitzar aquesta extensió",
|
||||
"Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.": "Espereu que tots els adjunts hagin estat descarregats i desxifrats. També podeu canviar a %s per a editar la nota.",
|
||||
"Please wait...": "Espereu...",
|
||||
"Plugin tools": "Eines dels connectors",
|
||||
"Plugins": "Connectors",
|
||||
"Plugin tools": "Eines de les extensions",
|
||||
"Plugins": "Extensions",
|
||||
"Portrait": "Retrat",
|
||||
"Possible keys/values:": "Valors/claus possibles:",
|
||||
"Possible values: %s.": "Valors possibles: %s.",
|
||||
@@ -545,12 +576,13 @@
|
||||
"Press Ctrl+D or type \"exit\" to exit the application": "Premeu Ctrl+D o escriviu «exit» per a sortir de l'aplicació",
|
||||
"Press the shortcut": "Premeu la drecera",
|
||||
"Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the shortcut.": "Premeu la drecera i premeu RETORN. O, premeu RETROCÉS per a esborrar la drecera.",
|
||||
"Press to set the decryption password.": "Premeu per a establir la contrasenya de desxifratge.",
|
||||
"Press to set the decryption password.": "Premeu per a definir la contrasenya de desxifrat.",
|
||||
"Previous versions of this note": "Versions prèvies d'aquesta nota",
|
||||
"Print": "Imprimeix",
|
||||
"Privacy Policy": "Política de privacitat",
|
||||
"Profile Version: %s": "Versió del perfil: %s",
|
||||
"Properties": "Propietats",
|
||||
"Public-private key pair:": "Parell de claus pública-privada:",
|
||||
"Publish note...": "Publica la nota...",
|
||||
"Publish Notes": "Publica notes",
|
||||
"Publish notes to the internet": "Publica les notes a internet",
|
||||
@@ -575,6 +607,7 @@
|
||||
"Rename tag:": "Canvia el nom de l'etiqueta:",
|
||||
"Renames the given <item> (note or notebook) to <name>.": "Canvia el nom de la nota o bloc de notes indicat de <item> a <name>.",
|
||||
"Renew token": "Renova el testimoni",
|
||||
"Reset master password": "Torna a definir la contrasenya mestra",
|
||||
"Resources: %d.": "Recursos: %d.",
|
||||
"Restart and upgrade": "Reinicia i actualitza",
|
||||
"Restart now": "Reinicia ara",
|
||||
@@ -587,7 +620,13 @@
|
||||
"Reverses the sorting order.": "Inverteix el criteri d'ordenació.",
|
||||
"Revision: %s (%s)": "Revisió: %s (%s)",
|
||||
"Runs the commands contained in the text file. There should be one command per line.": "Executa les ordres contingudes al fitxer de text. Hi ha d'haver una ordre per línia.",
|
||||
"Safe mode is currently active. Note rendering and all plugins are temporarily disabled.": "El mode segur està actiu actualment. La renderització de notes i tots els connectors estan temporalment inhabilitats.",
|
||||
"S3": "S3",
|
||||
"S3 access key": "Clau d'acces d'S3",
|
||||
"S3 bucket": "Bucket S3",
|
||||
"S3 region": "Regió S3",
|
||||
"S3 secret key": "Clau secreta S3",
|
||||
"S3 URL": "URL S3",
|
||||
"Safe mode is currently active. Note rendering and all plugins are temporarily disabled.": "El mode segur està actiu actualment. La renderització de notes i totes les extensions estan temporalment inhabilitades.",
|
||||
"Save": "Desa",
|
||||
"Save alarm": "Desa l'alarma",
|
||||
"Save as...": "Anomena i desa...",
|
||||
@@ -608,6 +647,7 @@
|
||||
"Server is running on port %d": "El servidor està corrent en el port %d",
|
||||
"Set alarm": "Estableix una alarma",
|
||||
"Set alarm:": "Estableix una alarma:",
|
||||
"Set it to 0 to make it take the complete available space. Recommended width is 600.": "Definiu-ho a 0 perquè ocupe tot l'espai disponible. L'amplària recomanada és 600.",
|
||||
"Set the password": "Establiu la contrasenya",
|
||||
"Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s": "Estableix la propietat <name> de la <note> indicada al [valor] donat. Les propietats possibles són:\n\n%s",
|
||||
"Share": "Comparteix",
|
||||
@@ -618,7 +658,9 @@
|
||||
"Show Advanced Settings": "Mostra opcions avançades",
|
||||
"Show all": "Mostra-ho tot",
|
||||
"Show completed to-dos": "Mostra els llistats de tasques pendents finalitzats",
|
||||
"Show disabled keys": "Mostra les claus deshabilitades",
|
||||
"Show note counts": "Mostra el nombre de notes",
|
||||
"Show sort order buttons": "Mostra els botons de canviar l'ordenació",
|
||||
"Show tray icon": "Mostra la icona a la safata",
|
||||
"Sidebar": "Barra lateral",
|
||||
"Size": "Mida",
|
||||
@@ -629,7 +671,7 @@
|
||||
"Solarised Light": "Solaritzat clar",
|
||||
"Some items cannot be decrypted.": "Alguns elements no s'han pogut desxifrar.",
|
||||
"Some items cannot be synchronised.": "Alguns elements no s'han pogut sincronitzar.",
|
||||
"Some items cannot be synchronised. Press for more info.": "Alguns elements no s'han pogut sincronitzar. Premeu per a més informació.",
|
||||
"Some items cannot be synchronised. Press for more info.": "Alguns elements no es poden sincronitzar. Premeu per a més informació.",
|
||||
"Sort notebooks by": "Ordena els blocs de notes per",
|
||||
"Sort notes by": "Ordena les notes per",
|
||||
"Sort selected lines": "Ordena les línies seleccionades",
|
||||
@@ -689,6 +731,7 @@
|
||||
"Tagged: %d.": "Etiquetats: %d.",
|
||||
"Tags": "Etiquetes",
|
||||
"Take photo": "Fes una foto",
|
||||
"Tasks": "Tasques",
|
||||
"Text editor command": "Ordre de l'editor de text",
|
||||
"Thank you! Your Joplin Cloud account is now setup and ready to use.": "Gràcies! El vostre compte de Joplin Cloud està preparat per a utilitzar-se.",
|
||||
"The app is now going to close. Please relaunch it to complete the process.": "L'aplicació es tancarà ara. Si us plau, torneu-la a executar per a completar el procés.",
|
||||
@@ -704,14 +747,19 @@
|
||||
"The editor command (may include arguments) that will be used to open a note. If none is provided it will try to auto-detect the default editor.": "L'ordre de l'editor (que pot incloure arguments) que s'usarà per a obrir una nota. Si no se'n proporciona cap, es detectarà automàticament l'editor predeterminat.",
|
||||
"The factor property sets how the item will grow or shrink to fit the available space in its container with respect to the other items. Thus an item with a factor of 2 will take twice as much space as an item with a factor of 1.Restart app to see changes.": "La propietat factor defineix com l’element creixerà o es reduirà per a ajustar-se a l’espai disponible al seu contenidor respecte als altres elements. Per tant, un element amb un factor de 2 ocuparà el doble d’espai que un element amb un factor d'1. Reinicieu l’aplicació per a veure els canvis.",
|
||||
"The following attachments are being watched for changes:": "S'estan vigilant els següents adjunts per si hi ha canvis:",
|
||||
"The following keys use an out-dated encryption algorithm and it is recommended to upgrade them. The upgraded key will still be able to decrypt and encrypt your data as usual.": "Les següents claus utilitzen un algorisme de xifrat obsolet i és recomanable actualitzar-les. La clau actualitzada podrà xifrar i desxifrar les dades com és habitual.",
|
||||
"The Joplin mobile app does not currently support this type of link: %s": "L'aplicació mòbil del Joplin, ara per ara, no admet aquest tipus d'enllaç: %s",
|
||||
"The Joplin team has vetted this plugin and it meets our standards for security and performance.": "L'equip de Joplin ha comprovat aquesta extensió i compleix els nostres estàndards de seguretat i rendiment.",
|
||||
"The keys with these IDs are used to encrypt some of your items, however the application does not currently have access to them. It is likely they will eventually be downloaded via synchronisation.": "Les claus amb aquests ID s'utilitzen per a xifrar alguns elements. Tot i això, l'aplicació no hi té accés. És probable que es baixen en algun moment via sincronització.",
|
||||
"The master key has been upgraded successfully!": "La clau mestra s'ha actualitzat amb èxit!",
|
||||
"The master keys with these IDs are used to encrypt some of your items, however the application does not currently have access to them. It is likely they will eventually be downloaded via synchronisation.": "Les claus mestres amb aquests ID s'usen per a xifrar alguns dels elements. Tot i això l'aplicació actualment no hi té accés. Probablement es baixaran via sincronització.",
|
||||
"The note \"%s\" has been successfully restored to the notebook \"%s\".": "La nota «%s» s'ha restaurat amb èxit en el bloc de notes «%s».",
|
||||
"The notebook could not be saved: %s": "No s'ha pogut desar el bloc de notes: %s",
|
||||
"The notes have been imported: %s": "Notes s'han importat: %s",
|
||||
"The possible commands are:": "Les ordres possibles són:",
|
||||
"The recipient could not be removed from the list. Please try again.\n\nThe error was: \"%s\"": "El destinatari no s'ha pogut eliminar de la llista. Torneu-ho a provar.\n\nL'error era \"%s\"",
|
||||
"The sync target needs to be upgraded before Joplin can sync. The operation may take a few minutes to complete and the app needs to be restarted. To proceed please click on the link.": "Cal actualitzar l'objectiu de sincronització abans que Joplin pugui sincronitzar. L'operació pot trigar uns minuts a completar-se i cal reiniciar l'aplicació. Per a continuar, feu clic a l'enllaç.",
|
||||
"The sync target needs to be upgraded. Press this banner to proceed.": "La destinació de sincronització s'ha d'actualitzar. Premeu aquest bàner per procedir.",
|
||||
"The tag \"%s\" already exists. Please choose a different name.": "L'etiqueta \"%s\" ja existeix. Escolliu un nom diferent.",
|
||||
"The target to synchronise to. Each sync target may have additional parameters which are named as `sync.NUM.NAME` (all documented below).": "L'objectiu on se sincronitzarà. Cada objectiu pot tenir paràmetres addicionals que s'anomenen «sync.NUM.NAME» (tots documentats a sota).",
|
||||
"The Web Clipper needs your authorisation to access your data.": "El porta-retalls web necessita autorització per a accedir a les vostres dades.",
|
||||
@@ -725,7 +773,7 @@
|
||||
"There was an error downloading this attachment:": "Hi ha hagut un error en baixar aquest adjunt:",
|
||||
"There was an error setting up your Joplin Cloud account. Please verify your email and password and try again. Error was:\n\n%s": "S'ha produït un error configurant el vostre compte de Joplin Cloud. Verifiqueu el vostre correu electrònic i la contrasenya, i proveu de nou. L'error ha sigut:\n\n%s",
|
||||
"These items will remain on the device but will not be uploaded to the sync target. In order to find these items, either search for the title or the ID (which is displayed in brackets above).": "Aquests elements es mantindran al dispositiu, però no es pujaran a la destinació de sincronització. Per a poder trobar aquests elements, podeu cercar pel títol o la ID (que es mostra entre claudàtors a sobre).",
|
||||
"These plugins enhance the Markdown renderer with additional features. Please note that, while these features might be useful, they are not standard Markdown and thus most of them will only work in Joplin. Additionally, some of them are *incompatible* with the WYSIWYG editor. If you open a note that uses one of these plugins in that editor, you will lose the plugin formatting. It is indicated below which plugins are compatible or not with the WYSIWYG editor.": "Aquestes extensions milloren el renderitzador de Markdown amb funcions addicionals. Tingueu en compte que, tot i que aquestes funcions poden ser útils, no són Markdown estàndard i, per tant, la majoria només funcionaran dins de Joplin. A més, alguns d’ells són *incompatibles* amb l’editor WYSIWYG. Si obriu una nota que utilitza un d'aquests connectors en aquest editor, perdreu el format del connector. A continuació s’indica quins connectors són compatibles o no amb l’editor WYSIWYG.",
|
||||
"These plugins enhance the Markdown renderer with additional features. Please note that, while these features might be useful, they are not standard Markdown and thus most of them will only work in Joplin. Additionally, some of them are *incompatible* with the WYSIWYG editor. If you open a note that uses one of these plugins in that editor, you will lose the plugin formatting. It is indicated below which plugins are compatible or not with the WYSIWYG editor.": "Aquestes extensions milloren el renderitzador de Markdown amb funcions addicionals. Tingueu en compte que, tot i que aquestes funcions poden ser útils, no són Markdown estàndard i, per tant, la majoria només funcionaran dins de Joplin. A més, alguneas d’ells són *incompatibles* amb l’editor WYSIWYG. Si obriu una nota que utilitza un d'aquestes extensions en aquest editor, perdreu el format de l'extensió. A continuació s’indica quines extensions són compatibles o no amb l’editor WYSIWYG.",
|
||||
"This attachment is not downloaded or not decrypted yet": "Aquest adjunt no ha estat descarregat o desxifrat encara",
|
||||
"This attachment is not downloaded or not decrypted yet.": "Aquest adjunt no s'ha descarregat o no s'ha desxifrat encara.",
|
||||
"This authorisation token is only needed to allow third-party applications to access Joplin.": "Aquest testimoni d'autorització només és necessari per a permetre l'accés d'aplicacions de tercers al Joplin.",
|
||||
@@ -738,10 +786,12 @@
|
||||
"This service allows the browser extension to communicate with Joplin. When enabling it your firewall may ask you to give permission to Joplin to listen to a particular port.": "Aquest servei permet que l'extensió del navegador pugui comunicar-se amb el Joplin. En activar-la, el tallafoc us podria demanar de donar permís al Joplin per a escoltar un port determinat.",
|
||||
"This will allow Joplin to run in the background. It is recommended to enable this setting so that your notes are constantly being synchronised, thus reducing the number of conflicts.": "Això permetrà que Joplin s’executi en segon pla. Es recomana habilitar aquesta configuració perquè les notes se sincronitzin constantment, reduint així el nombre de conflictes.",
|
||||
"This will open a new screen. Save your current changes?": "Això obrirà una nova pantalla. Voleu desar els canvis actuals?",
|
||||
"This will remove the notebook from your collection and you will no longer have access to its content. Do you wish to continue?": "Això eliminarà el bloc de notes de la vostra col·lecció i no hi tindreu accés al contingut. Voleu continuar?",
|
||||
"Time format": "Format horari",
|
||||
"title": "títol",
|
||||
"Title": "Títol",
|
||||
"To allow Joplin to synchronise with Dropbox, please follow the steps below:": "Per a permetre que el Joplin sincronitzi amb el Dropbox, seguiu les passes següents:",
|
||||
"To continue, please enter your master password below.": "Per a continuar, introduïu la contrasenya mestra.",
|
||||
"To delete a tag, untag the associated notes.": "Per a suprimir una etiqueta, traieu l'etiqueta en les notes associades.",
|
||||
"To delete: %d": "Per a suprimir: %d",
|
||||
"To enter command line mode, press \":\"": "Per a anar al mode de línia d'ordres, premeu «:»",
|
||||
@@ -758,8 +808,10 @@
|
||||
"Toggle editors": "Canvia disposició de l'editor",
|
||||
"Toggle external editing": "Activa/desactiva l'edició externa",
|
||||
"Toggle note list": "Mostra o amaga llista de notes",
|
||||
"Toggle own sort order": "Canvia l'ordenació",
|
||||
"Toggle safe mode": "Activa/desactiva el mode segur",
|
||||
"Toggle sidebar": "Mostra o amaga barra lateral",
|
||||
"Toggle sort order field": "Canvia el camp d'ordenació",
|
||||
"Token has been copied to the clipboard!": "El testimoni s'ha copiat al porta-retalls!",
|
||||
"Tools": "Eines",
|
||||
"Total: %d/%d": "Total: %d/%d",
|
||||
@@ -799,6 +851,7 @@
|
||||
"Use this to rebuild the search index if there is a problem with search. It may take a long time depending on the number of notes.": "Utilitzeu això per a reconstruir l'índex de cerca si hi ha un problema amb la cerca. Pot trigar molt en funció del nombre de notes.",
|
||||
"Used for most text in the markdown editor. If not found, a generic proportional (variable width) font is used.": "S'utilitza per a la majoria de text a l'editor Markdown. Si no es troba, s'utilitza un tipus de lletra genèric proporcional (amplada variable).",
|
||||
"Used where a fixed width font is needed to lay out text legibly (e.g. tables, checkboxes, code). If not found, a generic monospace (fixed width) font is used.": "S'utilitza quan es necessita un tipus de lletra d'amplada fixa per a mostrar text de manera llegible (p. ex. taules, caselles de selecció, codi). Si no es troba, s'utilitza un tipus de lletra genèric monoespai (amplada fixa).",
|
||||
"Users": "Usuaris",
|
||||
"Valid": "Vàlid",
|
||||
"View": "Vista",
|
||||
"View on map": "Mostra-ho al mapa",
|
||||
@@ -828,6 +881,8 @@
|
||||
"You may use the tool below to re-encrypt your data, for example if you know that some of your notes are encrypted with an obsolete encryption method.": "Podeu utilitzar l'eina següent per tornar a xifrar les vostres dades, per exemple si sabeu que algunes de les vostres notes estan xifrades amb un mètode de xifratge obsolet.",
|
||||
"Your choice: ": "La vostra tria: ",
|
||||
"Your data is going to be re-encrypted and synced again.": "Les vostres dades es tornaran a xifrar i sincronitzar.",
|
||||
"Your master password is needed to decrypt some of your data.": "La contrasenya mestra és necessària per a desxifrar algunes dades.",
|
||||
"Your password is needed to decrypt some of your data. Type `:e2ee decrypt` to set it.": "La contrasenya és necessària per a desxifrar algunes dades. Teclegeu `:e2ee decrypt` per definir-la.",
|
||||
"Your permission to use your camera is required.": "Es necessita el vostre permís per a utilitzar la càmera.",
|
||||
"Your version: %s": "La vostra versió: %s",
|
||||
"Zoom In": "Ampliar",
|
||||
|
@@ -77,7 +77,6 @@
|
||||
"Auto": "Automaticky",
|
||||
"Auto-pair braces, parenthesis, quotations, etc.": "Automaticky zdvojit uvozovky, závorky, apod.",
|
||||
"Automatically switch theme to match system theme": "Automaticky přepnout téma vzhledu podle nastavení systému",
|
||||
"Automatically update the application": "Automaticky aktualizovat aplikaci",
|
||||
"Back": "Zpět",
|
||||
"Bold": "Tučně",
|
||||
"Browse all plugins": "Procházet všechna rozšíření",
|
||||
@@ -466,7 +465,6 @@
|
||||
"Note: Does not work in all desktop environments.": "Poznámka: Nefunguje v některých desktopových prostředích.",
|
||||
"Note: When a note is shared, it will no longer be encrypted on the server.": "Poznámka: pokud je poznámka sdílena, už nebude na serveru šifrována.",
|
||||
"Notebook list growth factor": "Růstový faktor seznamu notebooků",
|
||||
"Notebook title:": "Název zápisníku:",
|
||||
"Notebook: %s": "Zápisník: %s",
|
||||
"Notebooks": "Zápisníky",
|
||||
"Notebooks cannot be named \"%s\", which is a reserved title.": "Zápisník se nemůže jmenovat \"%s\", tento název je rezervován.",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user