Merge branch 'develop' into master
|
@ -50,7 +50,8 @@
|
|||
"import/first": "error",
|
||||
"import/no-namespace": "error",
|
||||
"import/no-default-export": "error",
|
||||
"babel/no-unused-expressions": "error"
|
||||
"babel/no-unused-expressions": "error",
|
||||
"node/no-callback-literal": "off"
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
|
|
|
@ -4,7 +4,7 @@ COPY entrypoint.sh /entrypoint.sh
|
|||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y build-essential git-core unzip curl pkg-config rpm
|
||||
RUN curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
|
||||
RUN curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -
|
||||
RUN apt-get install -y nodejs
|
||||
RUN npm i -g grunt-cli
|
||||
|
||||
|
|
|
@ -9,11 +9,12 @@ jobs:
|
|||
steps:
|
||||
- name: Get current git tag
|
||||
id: get_tag
|
||||
uses: keeweb/get-tag@v2
|
||||
uses: keeweb/get-git-tag@v3.0.2
|
||||
with:
|
||||
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
|
||||
tagRegexGroup: 1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: keeweb/keeweb
|
||||
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
|
||||
|
@ -36,11 +37,12 @@ jobs:
|
|||
steps:
|
||||
- name: Get current git tag
|
||||
id: get_tag
|
||||
uses: keeweb/get-tag@v2
|
||||
uses: keeweb/get-git-tag@v3.0.2
|
||||
with:
|
||||
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
|
||||
tagRegexGroup: 1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: keeweb/keeweb
|
||||
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
|
||||
|
@ -97,11 +99,12 @@ jobs:
|
|||
steps:
|
||||
- name: Get current git tag
|
||||
id: get_tag
|
||||
uses: keeweb/get-tag@v2
|
||||
uses: keeweb/get-git-tag@v3.0.2
|
||||
with:
|
||||
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
|
||||
tagRegexGroup: 1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: keeweb/keeweb
|
||||
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
|
||||
|
@ -126,7 +129,8 @@ jobs:
|
|||
mkdir keys
|
||||
echo "$CODESIGN" > keys/codesign.json
|
||||
xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "$APPLE_ID_USERNAME" -p "$APPLE_DEPLOY_PASSWORD"
|
||||
- uses: keeweb/import-codesign-certs@v1
|
||||
- name: Import certificates
|
||||
uses: keeweb/import-codesign-certs@v1
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
|
@ -145,11 +149,12 @@ jobs:
|
|||
steps:
|
||||
- name: Get current git tag
|
||||
id: get_tag
|
||||
uses: keeweb/get-tag@v2
|
||||
uses: keeweb/get-git-tag@v3.0.2
|
||||
with:
|
||||
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
|
||||
tagRegexGroup: 1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: keeweb/keeweb
|
||||
ref: ${{ github.repository == 'keeweb/keeweb' && github.sha || 'develop' }}
|
||||
|
@ -220,16 +225,18 @@ jobs:
|
|||
steps:
|
||||
- name: Get current git tag
|
||||
id: get_tag
|
||||
uses: keeweb/get-tag@v2
|
||||
uses: keeweb/get-git-tag@v3.0.2
|
||||
with:
|
||||
tagRegex: "^v(\\d+\\.\\d+\\.\\d+)$"
|
||||
tagRegexGroup: 1
|
||||
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
|
||||
- name: Setup GCloud
|
||||
uses: google-github-actions/github-actions/setup-gcloud@master
|
||||
with:
|
||||
version: '285.0.0'
|
||||
service_account_key: ${{ secrets.GCP_SA_KEY }}
|
||||
export_default_credentials: true
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: keeweb/keeweb
|
||||
path: keeweb
|
||||
|
|
|
@ -6,11 +6,13 @@ jobs:
|
|||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: 'gh-pages'
|
||||
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
|
||||
- name: Setup GCloud
|
||||
uses: google-github-actions/github-actions/setup-gcloud@master
|
||||
with:
|
||||
version: '285.0.0'
|
||||
service_account_key: ${{ secrets.GCP_SA_KEY }}
|
||||
|
|
16
Gruntfile.js
|
@ -78,11 +78,6 @@ module.exports = function (grunt) {
|
|||
expand: true,
|
||||
nonull: true
|
||||
},
|
||||
favicon: {
|
||||
src: 'app/favicon.png',
|
||||
dest: 'tmp/favicon.png',
|
||||
nonull: true
|
||||
},
|
||||
icons: {
|
||||
cwd: 'app/icons/',
|
||||
src: ['*.png', '*.svg'],
|
||||
|
@ -111,13 +106,6 @@ module.exports = function (grunt) {
|
|||
expand: true,
|
||||
nonull: true
|
||||
},
|
||||
fonts: {
|
||||
src: 'node_modules/font-awesome/fonts/fontawesome-webfont.*',
|
||||
dest: 'tmp/fonts/',
|
||||
nonull: true,
|
||||
expand: true,
|
||||
flatten: true
|
||||
},
|
||||
'desktop-html': {
|
||||
src: 'dist/index.html',
|
||||
dest: 'tmp/desktop/app/index.html',
|
||||
|
@ -246,7 +234,7 @@ module.exports = function (grunt) {
|
|||
algo: 'sha512',
|
||||
expected: {
|
||||
style: 1,
|
||||
script: 3
|
||||
script: 2
|
||||
}
|
||||
},
|
||||
app: {
|
||||
|
@ -454,7 +442,7 @@ module.exports = function (grunt) {
|
|||
options: {
|
||||
title: 'KeeWeb',
|
||||
icon: 'graphics/icon.icns',
|
||||
background: 'graphics/background.png',
|
||||
background: 'graphics/dmg-background.png',
|
||||
'background-color': '#E0E6F9',
|
||||
'icon-size': 80,
|
||||
window: { size: { width: 658, height: 498 } },
|
||||
|
|
BIN
app/favicon.png
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 685 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -1 +1 @@
|
|||
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="922.667" height="922.667" viewBox="0 0 692.000000 692.000000"><path d="M266.9 57.4c-.2.2-4 .6-8.3 1-4.4.4-8.2.8-8.5 1-.3.2-2.7.7-5.3 1-5.5.7-21.6 4.8-29.8 7.5-46.3 15.4-93.3 48.9-126.5 90.1-25.7 31.8-46.8 74.8-52.9 107.4-.5 2.8-1.5 8.3-2.1 12.1-1.9 11.6-2.6 35.9-1.3 45 .3 2.7.9 7 1.2 9.5 3.3 27.4 16.9 55.4 36.1 74.5 12.1 12.1 27.3 22.7 40 27.8 3.9 1.6 7.9 3.3 9 3.7 1.1.5 4.3 1.5 7 2.4 4.4 1.3 7.1 1.9 18.5 4.1 12.1 2.4 42.4 2.4 56.5 0 1.7-.3 4.4-.7 6-1 1.7-.3 4.1-.7 5.5-1 1.4-.3 3.7-.7 5.1-1 4.5-.8 19.1-5.2 28.4-8.6 16.8-6.1 42.9-20.2 59.4-32.2l5.3-3.9 130.7 130.5C567.7 654 571.7 657.8 577.5 660c6.2 2.2 15.3 2.9 21.4 1.6 12.8-2.7 27.8-15.7 34.7-30.1 2.5-5.3 2.9-7.1 2.9-15-.1-15.8 1.1-14.3-49.6-65.1l-44-44 18.1-18.2 18.1-18.1 6.7 7.1c20.7 21.9 32.6 32.8 38.6 35.3 5.6 2.4 11.5-1.2 26.5-15.8 16.1-15.8 21.8-23.8 20-28.6-.9-2.3-4.6-7.8-7.4-10.7-.5-.6-4.1-4.8-8-9.2-7.2-8.3-64.7-65.9-91-91.1-8.2-7.9-16-14.7-17.3-15.2-6.8-2.6-12.6.8-28.4 16.3-13.1 12.9-19.8 21.8-19.8 26.3 0 5.6 13.3 21 37.4 43.4l5.8 5.4-18.1 18.1-18.1 18.1-67-67c-36.9-36.8-67-67.3-67-67.7 0-.4 3-5.1 6.7-10.4 16.1-23.5 30.3-53.8 35.8-76.4.7-2.8 4.3-22.5 5.3-28.5 1.1-6.5.8-41.1-.3-48.5-1.4-9.2-6.4-26.8-10-35.3-9.9-23.3-28.4-44.9-50.5-58.8-21.8-13.8-47.4-20.5-78.6-20.7-7.3-.1-13.3 0-13.5.2zm22 74c12.1 2.8 22.6 7.6 31.1 14.4 13.3 10.7 23.7 27.4 26.3 42.7 6 34.4-9.5 65.3-40.5 80.7-16.1 7.9-40.9 8.5-58.1 1.3-4.4-1.8-5.2-1.9-4-.3 1.1 1.4 4 9.8 4.8 13.9 5.3 25.7-.6 47.7-17.7 66-8.9 9.6-19.7 16.4-31.3 19.8-9.7 2.9-11.7 3.1-22.2 3.2-24.8 0-47.2-12.3-61-33.3-15.4-23.7-15.3-56.4.4-79.3 11.1-16.2 26.6-27.1 44.3-31.1 14.4-3.2 32.9-1.7 43.9 3.7 2 .9 3.7 1.6 3.8 1.5.2-.2-.8-3.4-2.2-7.3-3.4-9.7-3.7-11.4-4-23.3-.4-17.1 3.8-30.7 13.8-44 11.4-15.2 26.5-24.8 44.7-28.4 6.2-1.2 22.9-1.4 27.9-.2z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-64 -64 640 640"><path d="M136.736 512c-4.892-.008-10.322-2.061-15.92-4.997-11.941-6.265-20.13-14.185-13.293-27.218l166.101-316.638c-1.972-1.022-3.619-1.88-4.743-2.469-59.892-31.418-86.463-77.87-64.952-118.877 24.726-47.135 101.304-55.69 171.042-19.107 69.738 36.584 106.227 104.451 81.501 151.586-21.264 40.536-76.784 44.151-132.61 14.866-1.538-.807-4.033-2.103-6.857-3.567L150.768 502.471c-3.632 6.924-8.488 9.538-14.032 9.529zm-30.582-67.835l-3.144-1.649-.7-.366-42.546-22.32v-.001l-10.256-5.38c-1.738-.911-2.407-3.057-1.496-4.794l13.465-25.667c.906-1.728 3.036-2.399 4.768-1.509l.028.013 20.513 10.761c1.736.911 3.883.241 4.794-1.496l13.466-25.667c.911-1.737.24-3.883-1.497-4.794l-10.256-5.382-10.256-5.38c-1.737-.911-2.406-3.057-1.495-4.794l13.464-25.669c.911-1.737 3.058-2.406 4.794-1.494l10.258 5.38 26.697 14.005 16.548 8.681c.007.003.014.007.021.011l3.125 1.639-14.142 26.957-.973 1.855c0 .001-.001.003-.002.004l-20.063 38.245-2.041 3.888-11.424 21.779c-.002.004-.005.009-.008.014zm67.232-128.162l-3.145-1.651-14.938-7.837-11.794-6.187c-1.737-.911-2.408-3.057-1.497-4.794l13.466-25.667c.911-1.737 3.058-2.408 4.794-1.497l11.795 6.188 14.939 7.837c.007.003.014.007.021.01l3.125 1.64-1.651 3.145-6.731 12.834-6.733 12.833c-.003.005-.006.008-.009.013zm219.778-151.914c8.857.016 15.784-3.552 20.386-12.326 14.027-26.737-11.92-67.987-57.953-92.135-46.032-24.148-94.719-22.049-108.746 4.688-14.026 26.737 24.987 44.293 71.02 68.441 30.928 16.224 57.156 31.296 75.293 31.332z"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1007 KiB After Width: | Height: | Size: 1013 KiB |
Before Width: | Height: | Size: 947 KiB After Width: | Height: | Size: 960 KiB |
Before Width: | Height: | Size: 1020 KiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 322 KiB |
Before Width: | Height: | Size: 405 KiB After Width: | Height: | Size: 408 KiB |
|
@ -19,7 +19,6 @@
|
|||
"
|
||||
/>
|
||||
<meta name="application-name" content="KeeWeb" />
|
||||
<meta name="kw-signature" content="" />
|
||||
<meta name="kw-config" content="(no-config)" />
|
||||
<meta
|
||||
name="viewport"
|
||||
|
@ -72,7 +71,6 @@
|
|||
/>
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="stylesheet" href="css/app.css?__inline=true" />
|
||||
<script src="js/vendor.js?__inline=true"></script>
|
||||
<script src="js/app.js?__inline=true"></script>
|
||||
<script src="js/runtime.js?__inline=true"></script>
|
||||
</head>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M339 314.9L175.4 32h161.2l163.6 282.9H339zm-137.5 23.6L120.9 480h310.5L512 338.5H201.5zM154.1 67.4L0 338.5 80.6 480 237 208.8 154.1 67.4z"/></svg>
|
Before Width: | Height: | Size: 217 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="256" height="256" version="1.1" viewBox="0 0 256 256"><g transform="translate(296.64282,-100.61434)"><g transform="translate(222.85714,-11.428576)"><g transform="matrix(0.83394139,0,0,0.83394139,-86.101383,10.950635)"><path d="m-419.5 365.94c-18.48-4.62-28.77-19.31-28.81-41.1-0.01-6.97 0.49-10.31 2.23-14.79 4.26-10.99 15.55-19.27 30.41-22.33 7.39-1.52 9.67-3.15 9.67-6.92 0-1.18 0.88-4.71 1.95-7.83 4.88-14.2 13.93-26.03 23.59-30.87 10.11-5.07 15.22-6.21 27.45-6.14 17.38 0.09 26.04 3.86 38.17 16.6l6.67 7 5.97-2.07c28.91-10.01 57.73 7.03 60.06 35.49l0.64 7.79 5.69 2.04c16.26 5.83 23.9 18.06 22.52 36.04-0.91 11.76-6.4 21.15-15.11 25.81l-4.09 2.19-91 0.18c-69.93 0.13-92.16-0.11-96-1.07zM-487.72 353.36" fill="#000"/><path d="m-487.72 353.36c-10.79-2.56-22.22-12.09-27.58-22.99-3.04-6.18-3.2-7.09-3.2-18.03 0-10.4 0.26-12.07 2.68-17.23 5.1-10.89 14.88-18.75 27.15-21.84 2.59-0.65 5.02-1.69 5.41-2.31 0.38-0.62 0.81-4 0.95-7.5 0.85-21.78 15.15-40.97 35.1-47.14 10.78-3.33 24.33-2.51 36.05 2.18 3.72 1.49 3.3 1.81 11.16-8.5 4.65-6.1 14.05-13.68 21.74-17.55 8.3-4.17 16.94-6.09 27.26-6.07 28.86 0.07 53.73 18.12 62.92 45.67 2.94 8.8 2.79 11.27-0.67 11.34-1.51 0.03-5.85 0.86-9.63 1.85l-6.88 1.79-6.28-6.28c-17.7-17.7-46.59-21.53-71.15-9.42-9.81 4.84-17.7 11.78-23.65 20.83-4.25 6.45-9.66 18.48-9.66 21.47 0 2.12-1.72 3.18-9.05 5.58-22.69 7.44-35.94 24.63-35.93 46.62 0 8 2.06 17.8 4.93 23.41 1.08 2.11 1.68 4.13 1.34 4.47-0.88 0.88-29.11 0.58-33.01-0.35z" /></g></g></g></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M465.69 311.917V200.083H363.175v111.834zm-69.897-18.639a4.66 4.66 0 01-4.659-4.659v-65.237a4.66 4.66 0 014.659-4.66h37.278a4.66 4.66 0 014.66 4.66v65.237a4.66 4.66 0 01-4.66 4.66h-37.278z"/><path d="M400.453 251.34v-23.299h27.959v23.299zm0 32.618v-23.299h27.959v23.299zM46.31 187.656v136.688c0 8.564 6.968 15.532 15.532 15.532h21.746V204.742a4.66 4.66 0 019.319 0v135.134h245.415c8.565 0 15.533-6.968 15.533-15.532V187.656c0-8.564-6.968-15.532-15.533-15.532H61.842c-8.565 0-15.532 6.968-15.532 15.532zm241.936 68.6c.122 9.637-7.529 17.293-17.166 17.416-9.359-.354-17.016-8.006-16.662-17.366-.122-9.636 7.529-17.293 16.889-16.938 9.637-.124 17.293 7.528 16.939 16.888zm-41.921-28.528a4.658 4.658 0 011.679 6.372c-3.865 6.631-5.909 14.266-5.909 22.082v.014c-.143 8.152 1.746 15.601 5.603 22.206a4.66 4.66 0 01-8.048 4.7c-4.73-8.099-7.057-17.196-6.873-26.984v-.021c0-9.381 2.48-18.637 7.176-26.69a4.657 4.657 0 016.372-1.679zm-23.7-17.48a4.659 4.659 0 011.395 6.441c-7.725 11.993-11.78 25.622-11.613 39.406-.164 14.287 3.794 28.031 11.327 39.791a4.66 4.66 0 01-7.849 5.027c-8.511-13.288-12.909-28.806-12.799-44.815-.13-15.503 4.387-30.927 13.099-44.455a4.66 4.66 0 016.44-1.395zm-22.433-16.741a4.656 4.656 0 011.272 6.466c-11.136 16.59-17.074 35.936-17.174 55.945-.244 20.163 5.497 39.619 16.606 56.34a4.66 4.66 0 01-7.762 5.158c-11.901-17.914-18.172-38.71-18.172-60.224 0-.452.003-.901.008-1.354.108-21.815 6.594-42.941 18.756-61.059a4.66 4.66 0 016.466-1.272z"/></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -167,9 +167,7 @@ const AutoType = {
|
|||
} else {
|
||||
if (!windowInfo.url) {
|
||||
// try to find a URL in the title
|
||||
const urlMatcher = new RegExp(
|
||||
'https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)'
|
||||
);
|
||||
const urlMatcher = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\\+.~#?&\/=]*)/;
|
||||
const urlMatches = urlMatcher.exec(windowInfo.title);
|
||||
windowInfo.url = urlMatches && urlMatches.length > 0 ? urlMatches[0] : null;
|
||||
}
|
||||
|
@ -243,7 +241,7 @@ const AutoType = {
|
|||
this.selectEntryView.remove();
|
||||
this.selectEntryView = null;
|
||||
this.hideWindow(() => {
|
||||
if (result) {
|
||||
if (result?.entry) {
|
||||
this.activeWindowMatches(evt.windowInfo, (matches, activeWindowInfo) => {
|
||||
if (matches) {
|
||||
this.runAndHandleResult(result, evt.windowInfo.id);
|
||||
|
|
|
@ -150,7 +150,7 @@ const ChalRespCalculator = {
|
|||
header: Locale.yubiKeyNoKeyHeader,
|
||||
body: Locale.yubiKeyNoKeyBody.replace('{}', serial),
|
||||
buttons: [Alerts.buttons.cancel],
|
||||
iconSvg: 'usb-token',
|
||||
icon: 'usb-token',
|
||||
cancel: () => {
|
||||
logger.info('No key alert closed');
|
||||
|
||||
|
@ -173,7 +173,7 @@ const ChalRespCalculator = {
|
|||
header: Locale.yubiKeyTouchRequestedHeader,
|
||||
body: Locale.yubiKeyTouchRequestedBody.replace('{}', serial),
|
||||
buttons: [Alerts.buttons.cancel],
|
||||
iconSvg: 'usb-token',
|
||||
icon: 'usb-token',
|
||||
cancel: () => {
|
||||
logger.info('Touch alert closed');
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ const FeatureTester = {
|
|||
test() {
|
||||
return Promise.resolve()
|
||||
.then(() => this.checkWebAssembly())
|
||||
.then(() => this.checkLocalStorage());
|
||||
.then(() => this.checkLocalStorage())
|
||||
.then(() => this.checkWebCrypto());
|
||||
},
|
||||
|
||||
checkWebAssembly() {
|
||||
|
@ -28,6 +29,12 @@ const FeatureTester = {
|
|||
} catch (e) {
|
||||
throw 'LocalStorage is not supported';
|
||||
}
|
||||
},
|
||||
|
||||
checkWebCrypto() {
|
||||
if (!global.crypto.subtle) {
|
||||
throw 'WebCrypto is not supported';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ const IdleTracker = {
|
|||
const idleMinutes = (Date.now() - this.actionTime) / 1000 / 60;
|
||||
const maxIdleMinutes = AppSettingsModel.idleMinutes;
|
||||
if (maxIdleMinutes && idleMinutes > maxIdleMinutes) {
|
||||
Events.emit('before-user-idle');
|
||||
Events.emit('user-idle');
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import kdbxweb from 'kdbxweb';
|
||||
import { RuntimeInfo } from 'const/runtime-info';
|
||||
import { Links } from 'const/links';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { SettingsManager } from 'comp/settings/settings-manager';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
|
||||
const DateFormat = {
|
||||
months() {
|
||||
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { month: 'long' });
|
||||
const months = [];
|
||||
for (let month = 0; month < 12; month++) {
|
||||
months.push(format.format(new Date(2008, month)));
|
||||
}
|
||||
return months;
|
||||
},
|
||||
|
||||
weekDays() {
|
||||
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { weekday: 'long' });
|
||||
const weekdays = [];
|
||||
for (let day = 1; day < 8; day++) {
|
||||
weekdays.push(format.format(new Date(2007, 9, 6 + day)));
|
||||
}
|
||||
return weekdays;
|
||||
},
|
||||
|
||||
shortWeekDays() {
|
||||
const format = new Intl.DateTimeFormat(SettingsManager.activeLocale, { weekday: 'short' });
|
||||
const weekdays = [];
|
||||
for (let day = 1; day < 8; day++) {
|
||||
weekdays.push(format.format(new Date(Date.UTC(2007, 9, 6 + day))));
|
||||
}
|
||||
return weekdays;
|
||||
},
|
||||
|
||||
dtStr(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? new Intl.DateTimeFormat(SettingsManager.activeLocale, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'medium'
|
||||
}).format(dt)
|
||||
: '';
|
||||
},
|
||||
|
||||
dStr(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? new Intl.DateTimeFormat(SettingsManager.activeLocale, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}).format(dt)
|
||||
: '';
|
||||
},
|
||||
|
||||
dtStrFs(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? dt.getFullYear() +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getMonth() + 1, 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getDate(), 2) +
|
||||
'T' +
|
||||
StringFormat.pad(dt.getHours(), 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getMinutes(), 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getSeconds(), 2)
|
||||
: '';
|
||||
}
|
||||
};
|
||||
|
||||
export { DateFormat };
|
|
@ -2,32 +2,26 @@ import { Events } from 'framework/events';
|
|||
import { Features } from 'util/features';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
const appleThemes = {
|
||||
macdark: 'setGenThemeMacDark'
|
||||
};
|
||||
|
||||
const extraThemes = Features.isMac || Features.isiOS ? appleThemes : {};
|
||||
|
||||
const SettingsManager = {
|
||||
neutralLocale: null,
|
||||
activeLocale: 'en',
|
||||
activeLocale: 'en-US',
|
||||
activeTheme: null,
|
||||
|
||||
allLocales: {
|
||||
'en': 'English',
|
||||
'en-US': 'English',
|
||||
'de-DE': 'Deutsch',
|
||||
'fr-FR': 'Français'
|
||||
},
|
||||
|
||||
allThemes: {
|
||||
dark: 'setGenThemeDark',
|
||||
light: 'setGenThemeLight',
|
||||
fb: 'setGenThemeFb',
|
||||
db: 'setGenThemeDb',
|
||||
sd: 'setGenThemeSd',
|
||||
sl: 'setGenThemeSl',
|
||||
wh: 'setGenThemeWh',
|
||||
te: 'setGenThemeTe',
|
||||
hc: 'setGenThemeHc',
|
||||
...extraThemes
|
||||
hc: 'setGenThemeHc'
|
||||
},
|
||||
|
||||
customLocales: {},
|
||||
|
@ -46,7 +40,7 @@ const SettingsManager = {
|
|||
},
|
||||
|
||||
getDefaultTheme() {
|
||||
return Features.isMac ? 'macdark' : 'fb';
|
||||
return 'dark';
|
||||
},
|
||||
|
||||
setTheme(theme) {
|
||||
|
@ -83,7 +77,7 @@ const SettingsManager = {
|
|||
return;
|
||||
}
|
||||
let localeValues;
|
||||
if (loc !== 'en') {
|
||||
if (loc !== 'en-US') {
|
||||
if (this.customLocales[loc]) {
|
||||
localeValues = this.customLocales[loc];
|
||||
} else {
|
||||
|
@ -100,8 +94,8 @@ const SettingsManager = {
|
|||
|
||||
getBrowserLocale() {
|
||||
const language = (navigator.languages && navigator.languages[0]) || navigator.language;
|
||||
if (language && language.lastIndexOf('en', 0) === 0) {
|
||||
return 'en';
|
||||
if (language && language.startsWith('en')) {
|
||||
return 'en-US';
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ const DefaultAppSettings = {
|
|||
allowIframes: false, // allow displaying the app in IFrames
|
||||
useGroupIconForEntries: false, // automatically use group icon when creating new entries
|
||||
enableUsb: true, // enable interaction with USB devices
|
||||
nativeArgon2: true, // use native argon2 module
|
||||
fieldLabelDblClickAutoType: false, // trigger auto-type by doubleclicking field label
|
||||
|
||||
yubiKeyShowIcon: true, // show an icon to open OTP codes from YubiKey
|
||||
|
@ -66,6 +65,7 @@ const DefaultAppSettings = {
|
|||
|
||||
webdav: true, // enable WebDAV integration
|
||||
webdavSaveMethod: 'move', // how to save files with WebDAV: "move" or "put"
|
||||
webdavStatReload: false, // WebDAV: reload the file instead of relying on Last-Modified
|
||||
|
||||
gdrive: true, // enable Google Drive integration
|
||||
gdriveClientId: null, // custom Google Drive client id
|
||||
|
|
|
@ -3,12 +3,12 @@ const IconMap = [
|
|||
'globe',
|
||||
'exclamation-triangle',
|
||||
'server',
|
||||
'thumb-tack',
|
||||
'comments-o',
|
||||
'thumbtack',
|
||||
'comments',
|
||||
'puzzle-piece',
|
||||
'pencil-square-o',
|
||||
'edit',
|
||||
'plug',
|
||||
'newspaper-o',
|
||||
'address-card',
|
||||
'paperclip',
|
||||
'camera',
|
||||
'wifi',
|
||||
|
@ -18,32 +18,32 @@ const IconMap = [
|
|||
'certificate',
|
||||
'bullseye',
|
||||
'desktop',
|
||||
'envelope-o',
|
||||
'envelope',
|
||||
'cog',
|
||||
'clipboard',
|
||||
'paper-plane-o',
|
||||
'television',
|
||||
'paper-plane',
|
||||
'newspaper',
|
||||
'bolt',
|
||||
'inbox',
|
||||
'floppy-o',
|
||||
'hdd-o',
|
||||
'dot-circle-o',
|
||||
'expeditedssl',
|
||||
'save',
|
||||
'hdd',
|
||||
'dot-circle',
|
||||
'user-lock',
|
||||
'terminal',
|
||||
'print',
|
||||
'map-signs',
|
||||
'project-diagram',
|
||||
'flag-checkered',
|
||||
'wrench',
|
||||
'laptop',
|
||||
'archive',
|
||||
'credit-card',
|
||||
'windows',
|
||||
'clock-o',
|
||||
'clock',
|
||||
'search',
|
||||
'flask',
|
||||
'gamepad',
|
||||
'trash-o',
|
||||
'sticky-note-o',
|
||||
'trash',
|
||||
'sticky-note',
|
||||
'ban',
|
||||
'question-circle',
|
||||
'cube',
|
||||
|
@ -53,20 +53,20 @@ const IconMap = [
|
|||
'unlock-alt',
|
||||
'lock',
|
||||
'check',
|
||||
'pencil',
|
||||
'picture-o',
|
||||
'pencil-alt',
|
||||
'image',
|
||||
'book',
|
||||
'list-alt',
|
||||
'user-secret',
|
||||
'cutlery',
|
||||
'utensils',
|
||||
'home',
|
||||
'star-o',
|
||||
'star',
|
||||
'linux',
|
||||
'map-pin',
|
||||
'apple',
|
||||
'wikipedia-w',
|
||||
'usd',
|
||||
'calendar',
|
||||
'dollar-sign',
|
||||
'signature',
|
||||
'mobile'
|
||||
];
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ const Links = {
|
|||
BetaWebApp: 'https://beta.keeweb.info',
|
||||
License: 'https://github.com/keeweb/keeweb/blob/master/LICENSE',
|
||||
LicenseApache: 'https://opensource.org/licenses/Apache-2.0',
|
||||
LicenseLinkCCBY40: 'https://creativecommons.org/licenses/by/4.0/',
|
||||
UpdateDesktop: 'https://github.com/keeweb/keeweb/releases/download/v{ver}/UpdateDesktop.zip',
|
||||
ReleaseNotes: 'https://github.com/keeweb/keeweb/blob/master/release-notes.md#release-notes',
|
||||
SelfHostedDropbox: 'https://github.com/keeweb/keeweb#self-hosting',
|
||||
|
|
|
@ -25,7 +25,7 @@ const Resizable = {
|
|||
if (dragInfo.auto !== undefined) {
|
||||
this.$el.css(dragInfo.prop, dragInfo.auto);
|
||||
} else {
|
||||
this.$el.css(dragInfo.prop, 'auto');
|
||||
this.$el.css(dragInfo.prop, '');
|
||||
}
|
||||
this.fixSize(dragInfo);
|
||||
this.emit('view-resize', null);
|
||||
|
@ -55,7 +55,7 @@ const Resizable = {
|
|||
const propLower = prop.toLowerCase();
|
||||
const min = this.getSizeProp('min' + prop);
|
||||
const max = this.getSizeProp('max' + prop);
|
||||
const auto = this.getSizeProp('auto' + prop) || 'auto';
|
||||
const auto = this.getSizeProp('auto' + prop);
|
||||
const startSize = this.$el[propLower]();
|
||||
return { startSize, prop: propLower, min, max, auto };
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@ class View extends EventEmitter {
|
|||
model = undefined;
|
||||
options = {};
|
||||
views = {};
|
||||
hidden = false;
|
||||
hidden = undefined;
|
||||
removed = false;
|
||||
modal = undefined;
|
||||
eventListeners = {};
|
||||
|
@ -239,6 +239,10 @@ class View extends EventEmitter {
|
|||
if (visible === undefined) {
|
||||
visible = this.hidden;
|
||||
}
|
||||
if (this.hidden === !visible) {
|
||||
this.debugLogger?.debug('Toggle: noop', visible);
|
||||
return;
|
||||
}
|
||||
this.hidden = !visible;
|
||||
if (this.modal) {
|
||||
if (visible) {
|
||||
|
@ -258,7 +262,7 @@ class View extends EventEmitter {
|
|||
}
|
||||
|
||||
isHidden() {
|
||||
return this.hidden;
|
||||
return !!this.hidden;
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
|
|
|
@ -4,4 +4,3 @@ import 'hbs-helpers/ifeq';
|
|||
import 'hbs-helpers/ifneq';
|
||||
import 'hbs-helpers/ifemptyoreq';
|
||||
import 'hbs-helpers/res';
|
||||
import 'hbs-helpers/svg';
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import Handlebars from 'hbs';
|
||||
|
||||
Handlebars.registerHelper('svg', (name, cls) => {
|
||||
const icon = require(`svg/${name}.svg`).default;
|
||||
if (typeof cls === 'string') {
|
||||
return `<svg class="${cls}"` + icon.substr(4);
|
||||
}
|
||||
return icon;
|
||||
});
|
|
@ -1,35 +1,4 @@
|
|||
{
|
||||
"months": [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
],
|
||||
"monthsShort": [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec"
|
||||
],
|
||||
"weekdays": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
"weekdaysShort": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||||
|
||||
"retToApp": "return to app",
|
||||
"name": "name",
|
||||
"icon": "icon",
|
||||
|
@ -400,14 +369,15 @@
|
|||
"setGenDownloadAndRestart": "Download update and restart",
|
||||
"setGenAppearance": "Appearance",
|
||||
"setGenTheme": "Theme",
|
||||
"setGenThemeDark": "Dark",
|
||||
"setGenThemeLight": "Light",
|
||||
"setGenThemeFb": "Flat blue",
|
||||
"setGenThemeDb": "Dark brown",
|
||||
"setGenThemeWh": "White",
|
||||
"setGenThemeTe": "Terminal",
|
||||
"setGenThemeHc": "High contrast",
|
||||
"setGenThemeSd": "Solarized dark",
|
||||
"setGenThemeSl": "Solarized light",
|
||||
"setGenThemeMacDark": "macOS Dark",
|
||||
"setGenMoreThemes": "More themes",
|
||||
"setGenLocale": "Language",
|
||||
"setGenLocOther": "other languages are available as plugins",
|
||||
"setGenFontSize": "Font size",
|
||||
|
@ -621,7 +591,7 @@
|
|||
"setAboutTitle": "About",
|
||||
"setAboutBuilt": "This app is built with these awesome tools",
|
||||
"setAboutLic": "License",
|
||||
"setAboutLicComment": "The app itself and all included components which are not in public domain are licensed under MIT license",
|
||||
"setAboutLicComment": "The app itself and all included components are licensed under MIT license",
|
||||
"setAboutFirst": "This is an open-source app created by {}",
|
||||
"setAboutSecond": "and licensed under {}.",
|
||||
"setAboutSource": "The source code and issues are on {}.",
|
||||
|
@ -659,7 +629,9 @@
|
|||
|
||||
"webdavSaveMethod": "Save method",
|
||||
"webdavSaveMove": "Upload a temporary file and move",
|
||||
"webdavSavePut": "Overwrite kdbx file with PUT",
|
||||
"webdavSavePut": "Overwrite the kdbx file with PUT",
|
||||
"webdavNoLastModified": "Last-Modified HTTP header is absent",
|
||||
"webdavStatReload": "Always reload the file instead of relying on Last-Modified HTTP header",
|
||||
|
||||
"launcherSave": "Save Passwords Database",
|
||||
"launcherFileFilter": "KeePass files",
|
||||
|
@ -686,5 +658,6 @@
|
|||
"yubiKeyTouchRequestedHeader": "Touch your YubiKey",
|
||||
"yubiKeyTouchRequestedBody": "Please touch your YubiKey with serial number {}",
|
||||
"yubiKeyDisabledErrorHeader": "USB is disabled",
|
||||
"yubiKeyDisabledErrorBody": "YubiKey is required to open this file, please enable USB devices in settings."
|
||||
"yubiKeyDisabledErrorBody": "YubiKey is required to open this file, please enable USB devices in settings.",
|
||||
"yubiKeyErrorWithCode": "YubiKey error, code {}."
|
||||
}
|
||||
|
|
|
@ -56,10 +56,10 @@
|
|||
"notes": "Notizen",
|
||||
"entry": "Eintrag",
|
||||
"group": "Gruppe",
|
||||
"noTitle": "Keinen Titel",
|
||||
"noTitle": "Kein Titel",
|
||||
"or": "oder",
|
||||
"history": "Verlauf",
|
||||
"template": "Template",
|
||||
"template": "Vorlage",
|
||||
"notImplemented": "Nicht implementiert",
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"discardChanges": "Änderungen verwerfen",
|
||||
|
@ -86,8 +86,8 @@
|
|||
"menuSetAbout": "Info",
|
||||
"menuSetDevices": "Geräte",
|
||||
"menuAlertNoTags": "Keine Tags",
|
||||
"menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt “Tags”) hinzugefügt werden.",
|
||||
"menuEmptyTrash": "Leere Papierkorb",
|
||||
"menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt „Tags“) hinzugefügt werden.",
|
||||
"menuEmptyTrash": "Papierkorb leeren",
|
||||
"menuEmptyTrashAlert": "Papierkorb leeren?",
|
||||
"menuEmptyTrashAlertBody": "Die Einträge können nicht wiederhergestellt werden",
|
||||
"menuItemCollapsed": "Doppelklick zum Aufklappen",
|
||||
|
@ -109,21 +109,21 @@
|
|||
"genLen": "Länge",
|
||||
"genNewPass": "Neues Passwort",
|
||||
"genPresetDefault": "Standardeinstellung",
|
||||
"genPresetDerived": "unverändert",
|
||||
"genPresetDerived": "Wie das alte Passwort",
|
||||
"genPresetPronounceable": "aussprechbar",
|
||||
"genPresetMed": "durchschnittlich lang",
|
||||
"genPresetMed": "Durchschnittliche Länge",
|
||||
"genPresetLong": "lang",
|
||||
"genPresetPin4": "4-stellige PIN",
|
||||
"genPresetMac": "MAC-Adresse",
|
||||
"genPresetHash128": "128-Bit Hash",
|
||||
"genPresetHash256": "256-Bit Hash",
|
||||
"genPresetHash128": "128-Bit-Hash",
|
||||
"genPresetHash256": "256-Bit-Hash",
|
||||
"genHidePass": "Passwort ausblenden",
|
||||
"genShowPass": "Passwort einblenden",
|
||||
"grpTitle": "Gruppe",
|
||||
"grpSearch": "Suche für Einträge in dieser Gruppe aktivieren",
|
||||
"grpAutoType": "Auto-Type aktivieren",
|
||||
"grpAutoTypeSeq": "Auto-Type Abfolge",
|
||||
"grpAutoTypeSeqDefault": "Standard Auto-Type Abfolge verwenden",
|
||||
"grpAutoTypeSeq": "Auto-Type-Sequenz",
|
||||
"grpAutoTypeSeqDefault": "Standard-Auto-Type-Sequenz verwenden",
|
||||
"grpTrash": "Gruppe und alle enthaltenen Einträge löschen",
|
||||
"tagTitle": "Tag",
|
||||
"tagTrash": "Diesen Tag von allen Einträgen entfernen",
|
||||
|
@ -131,14 +131,14 @@
|
|||
"tagTrashQuestion": "Diesen Tag von allen Einträgen entfernen?",
|
||||
"tagTrashQuestionBody": "Der Tag wird von allen Einträgen entfernt. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"tagExists": "Tag existiert bereits",
|
||||
"tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte wählen Sie einen anderen Namen.",
|
||||
"tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte einen anderen Namen wählen.",
|
||||
"tagBadName": "Ungültiger Name",
|
||||
"tagBadNameBody": "Der Tag-Name darf nicht die folgenden Zeichen enthalten: {}. Bitte entfernen Sie sie.",
|
||||
"tagBadNameBody": "Der Tag-Name darf folgende Zeichen nicht enthalten: {}. Bitte diese entfernen.",
|
||||
"genPsTitle": "Generator-Voreinstellungen",
|
||||
"genPsCreate": "Neue Voreinstellung",
|
||||
"genPsDelete": "Voreinstellung löschen",
|
||||
"genPsNew": "Voreinstellung",
|
||||
"genPsEnabled": "In Voreinstellungs-Liste anzeigen",
|
||||
"genPsEnabled": "In Voreinstellungsliste anzeigen",
|
||||
"genPsDefault": "Standardmäßig ausgewählt",
|
||||
"genPsDefaultLength": "Standardlänge",
|
||||
"genPsUpper": "Lateinische Großbuchstaben",
|
||||
|
@ -151,16 +151,16 @@
|
|||
"genPsInclude": "Zusätzliche Symbole angeben",
|
||||
"genPsExample": "Beispiel für ein generiertes Passwort",
|
||||
"genPsPattern": "Muster",
|
||||
"genPsPatternHelp": "Mit Mustern können eigene Regeln zur Auswahl von Zeichen festgelegt werden. Beispielsweise werden mit 1-AA Passwörter generiert, die mit einer Ziffer starten, gefolgt von einem Bindestrich und zwei Großbuchstaben. Die folgenden Symbole stehen zur Verfügung:",
|
||||
"genPsPatternHelp": "Mit Mustern können eigene Regeln zur Auswahl von Zeichen festgelegt werden. Beispielsweise werden mit 1-AA Passwörter generiert, die mit einer Ziffer starten, gefolgt von einem Bindestrich und zwei Großbuchstaben. Folgende Symbole stehen zur Verfügung:",
|
||||
"genPsAllRanges": "Alle Zeichen",
|
||||
"genPsIncluded": "Zusätzliche oben hinzugefügte Zeichen",
|
||||
"keyChangeTitleRemote": "Hauptschlüssel geändert",
|
||||
"keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte geben Sie das neue Passwort ein.",
|
||||
"keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte das neue Passwort eingeben.",
|
||||
"keyChangeTitleExpired": "Hauptschlüssel abgelaufen",
|
||||
"keyChangeMessageExpired": "Der Hauptschlüssel für diese Datenkbank ist abgelaufen. Bitte geben Sie einen neuen Schlüssel ein.",
|
||||
"keyChangeRepeatPassword": "Passwort (Wiederholung)",
|
||||
"keyEnter": "Enter",
|
||||
"iconFavTitle": "Website-Icon herunterladen und verwenden",
|
||||
"iconFavTitle": "Website-Symbol herunterladen und verwenden",
|
||||
"iconSelCustom": "Eigenes Icon auswählen",
|
||||
"listEmptyTitle": "Leer",
|
||||
"listEmptyAdd": "Zum Anlegen neuer Einträge den obigen {} Button benutzen",
|
||||
|
@ -168,9 +168,9 @@
|
|||
"listNoWebsite": "keine Website",
|
||||
"listNoUser": "kein Benutzer",
|
||||
"listNoAttachments": "keine Anhänge",
|
||||
"listAddTemplateHeader": "Templates",
|
||||
"listAddTemplateBody1": "Templates ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Template-Eintrag hinzu und klicken Sie auf {}, um dieses Template zu benutzen.",
|
||||
"listAddTemplateBody2": "Sie können Ihre Templates in der Gruppe “{}” finden.",
|
||||
"listAddTemplateHeader": "Vorlagen",
|
||||
"listAddTemplateBody1": "Vorlagen ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Vorlage-Eintrag hinzu und klicken Sie auf {}, um diese Vorlage zu verwenden.",
|
||||
"listAddTemplateBody2": "Sie können Ihre Vorlagen in der Gruppe “{}” finden.",
|
||||
"searchAddNew": "Hinzufügen",
|
||||
"searchSort": "Sortierung",
|
||||
"searchCreated": "Erstelldatum",
|
||||
|
@ -202,17 +202,17 @@
|
|||
"openKeyFile": "Schlüsseldatei",
|
||||
"openKeyFileDropbox": "(von Dropbox)",
|
||||
"openDropHere": "Dateien hier ablegen",
|
||||
"openFailedRead": "Konnte die Datei nicht lesen",
|
||||
"openFailedRead": "Datei konnte nicht gelesen werden",
|
||||
"openNothingFound": "Nichts gefunden",
|
||||
"openNothingFoundBody": "Es konnten keine kompatiblen Dateien gefunden werden.",
|
||||
"openSelectFile": "Datei auswählen",
|
||||
"openSelectFileBody": "Wählen Sie eine Datei zum Öffnen aus",
|
||||
"openPassFor": "Passwort für",
|
||||
"openRemoveLastQuestion": "Lokale Datei löschen?",
|
||||
"openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie sie unwiederbringlich löschen?",
|
||||
"openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie diese unwiederbringlich löschen?",
|
||||
"openRemoveLastQuestionModBody": "Die Datei, die Sie löschen möchten, beinhaltet lokale Änderungen. Möchten Sie diese Änderungen verwerfen und die Datei löschen?",
|
||||
"openLocalFile": "Lokale Datei",
|
||||
"openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie sie aus den Einstellungen heraus exportieren.",
|
||||
"openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie diese aus den Einstellungen heraus exportieren.",
|
||||
"openLocalFileDontShow": "Nicht wieder anzeigen",
|
||||
"openWrongFile": "Inkompatibles Dateiformat",
|
||||
"openWrongFileBody": "Dieses Dateiformat ist nicht kompatibel. Die App unterstützt KeePass-Datenbanken im kdbx-Format.",
|
||||
|
@ -221,10 +221,10 @@
|
|||
"openUrl": "URL",
|
||||
"openUrlDesc": "https://server/pfad/datei.kdbx, oder einfach datei.kdbx",
|
||||
"openUser": "Benutzername",
|
||||
"openUserDesc": "WebDAV Server Benutzername (falls erforderlich)",
|
||||
"openUserDesc": "WebDAV-Server-Benutzername (falls erforderlich)",
|
||||
"openUserPlaceholder": "kein Benutzername",
|
||||
"openPass": "Passwort",
|
||||
"openPassDesc": "WebDAV Server-Passwort (nicht das KDBX-Datei-Passwort)",
|
||||
"openPassDesc": "WebDAV-Server-Passwort (nicht das KDBX-Datei-Passwort)",
|
||||
"openPassPlaceholder": "kein Passwort",
|
||||
"openConfigError": "Fehler: {}",
|
||||
"openConfigErrorNotFound": "Datei nicht gefunden",
|
||||
|
@ -238,12 +238,13 @@
|
|||
"openChalRespLoading": "Liste mit YubiKeys wird geladen",
|
||||
"openChalRespSelectYubiKey": "Wählen Sie den YubiKey aus, den Sie benutzen wollen",
|
||||
"openChalRespErrorEmpty": "Keine YubiKeys gefunden.",
|
||||
"openChalRespErrorEmptyMac": "Verwenden Sie dieses Feature zum ersten Mal auf MacOS? KeeWeb muss in Ihren Sicherheitseinstellung im Input-Monitoring-Abschnitt hinzugefügt werden.",
|
||||
"detAttDownload": "Umschalttaste + Klick auf den Anhang-Button zum Herunterladen oder",
|
||||
"detAttDelToRemove": "Backspace zum entfernen",
|
||||
"detAttSave": "Speichern unter...",
|
||||
"detAttDelToRemove": "Backspace zum Entfernen",
|
||||
"detAttSave": "Speichern unter …",
|
||||
"detEmpty": "Ihre Passwörter werden hier angezeigt",
|
||||
"detGroupRestore": "Ziehen Sie diese Gruppe in eine andere Gruppe als den Papierkorb, um sie wiederherzustellen.",
|
||||
"detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf um den entsprechenden Zustand des Eintrags anzuzeigen",
|
||||
"detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf, um den entsprechenden Zustand des Eintrags anzuzeigen",
|
||||
"detHistoryReturn": "zurück zum Eintrag",
|
||||
"detHistoryRevert": "Auf diese Version zurücksetzen",
|
||||
"detHistoryDel": "Version löschen",
|
||||
|
@ -277,7 +278,7 @@
|
|||
"detAttachments": "Anhänge",
|
||||
"detDelFromTrash": "Aus dem Papierkorb löschen?",
|
||||
"detDelFromTrashBody": "Es wird keine Möglichkeit zur Wiederherstellung geben.",
|
||||
"detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das “Papierkorb” leeren-Icon im Papierkorb-Menüpunkt.",
|
||||
"detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das „Papierkorb leeren“-Symbol im Papierkorb-Menüpunkt.",
|
||||
"detDelToTrash": "Diesen Eintrag löschen?",
|
||||
"detDelToTrashBody": "Der Eintrag wird in den Papierkorb verschoben.",
|
||||
"detFieldCopied": "Kopiert",
|
||||
|
@ -299,12 +300,12 @@
|
|||
"detAutoType": "Auto-Type",
|
||||
"detAutoTypeSettings": "Auto-Type Einstellungen",
|
||||
"detAutoTypeEnabled": "Auto-Type für diesen Eintrag aktivieren",
|
||||
"detAutoTypeSequence": "Tastensequenz",
|
||||
"detAutoTypeSequence": "Tastenfolge",
|
||||
"detAutoTypeInput": "Eingabe",
|
||||
"detAutoTypeShortcutsDesc": "{} oder {} während die App inaktiv ist",
|
||||
"detAutoTypeObfuscation": "Echte Tastenanschläge mit zufälligen mischen",
|
||||
"detAutoTypeWindow": "Fenster",
|
||||
"detAutoTypeInputWindow": "Fenster-Titel",
|
||||
"detAutoTypeInputWindow": "Fenstertitel",
|
||||
"detSetupOtpAlert": "QR-Code einscannen",
|
||||
"detSetupOtpAlertBody": "Bitte kopieren Sie den QR-Code, der auf der Autorisierungsseite angezeigt wird.",
|
||||
"detSetupOtpAlertBody1": "1. Gehen Sie zur Autorisierungsseite",
|
||||
|
@ -317,14 +318,14 @@
|
|||
"detSetupOtpAlertBodyWith": "mit {}",
|
||||
"detOtpImageError": "Fehler beim Einlesen des Bildes",
|
||||
"detOtpImageErrorBody": "Entschuldigung, wir konnten das Bildformat nicht lesen. Bitte kontaktieren Sie den Hersteller mit Einzelheiten zu diesem Fehler.",
|
||||
"detOtpImageReading": "QR-Code wird eingelesen...",
|
||||
"detOtpImageReading": "QR-Code wird eingelesen …",
|
||||
"detOtpQrError": "QR-Code Lesefehler",
|
||||
"detOtpQrErrorBody": "Der QR-Code konnte leider nicht eingelesen werden. Bitte versuchen Sie es noch einmal oder kontaktieren Sie den Hersteller mit Einzelheiten zum aufgetretenen Fehler.",
|
||||
"detOtpQrWrong": "Fehlerhafter QR-Code",
|
||||
"detOtpQrWrongBody": "Ihr QR-Code wurde erfolgreich eingelesen, enthält jedoch keinen Einmal-Code.",
|
||||
"detOtpField": "Einmal-Code",
|
||||
"detOtpClickToTouch": "Zum Erzeugen hier klicken",
|
||||
"detOtpGenerating": "Wird erzeugt...",
|
||||
"detOtpGenerating": "Wird erzeugt …",
|
||||
"detOtpTouch": "Berühren Sie Ihren {}",
|
||||
"detLockField": "Dieses Feld sperren, damit der Inhalt nicht durchsuchbar ist und angezeigt wird. Um ihn anzuzeigen, muss er angeklickt werden.",
|
||||
"detUnlockField": "Dieses Feld entsperren. Dies führt dazu, dass der Inhalt direkt angezeigt wird und durchsuchbar ist.",
|
||||
|
@ -334,8 +335,8 @@
|
|||
"autoTypeEntryFields": "Feld-Platzhalter",
|
||||
"autoTypeModifiers": "Hilfstasten",
|
||||
"autoTypeKeys": "Tasten",
|
||||
"autoTypeLink": "mehr...",
|
||||
"autoTypeError": "Auto-Type Fehler",
|
||||
"autoTypeLink": "mehr …",
|
||||
"autoTypeError": "Auto-Type-Fehler",
|
||||
"autoTypeErrorGeneric": "Während der Ausführung von Auto-Type ist ein Fehler aufgetreten: {}",
|
||||
"autoTypeErrorGlobal": "Um globale Tastenkürzel zu nutzen, wechseln Sie bitte zu der Anwendung, in der Sie das Passwort eingeben wollen.",
|
||||
"autoTypeErrorNotInstalled": "{} ist nicht installiert",
|
||||
|
@ -343,7 +344,7 @@
|
|||
"autoTypeMsgNoWindow": "Wir konnten den Titel des aktiven Fensters leider nicht auslesen. Fangen Sie einfach an zu tippen, um nach dem richtigen Eintrag zu suchen.",
|
||||
"autoTypeMsgMatchedByWindow": "Passwort auswählen für {}",
|
||||
"autoTypeNoMatches": "keine passenden Einträge",
|
||||
"autoTypeSelectionHint": "Die Auto-Type Abfolge eintippen",
|
||||
"autoTypeSelectionHint": "Die Auto-Type-Sequenz eintippen",
|
||||
"autoTypeSelectionHintAction": "Nur das Passwort eintippen",
|
||||
"autoTypeSelectionHintOpt": "Nur den Benutzernamen eintippen",
|
||||
"autoTypeSelectionHintShift": "Andere Felder",
|
||||
|
@ -375,12 +376,12 @@
|
|||
"setGenNewVersion": "Eine neue Version der App ist verfügbar und wurde heruntergeladen",
|
||||
"setGenReleaseNotes": "Versionshinweise anzeigen",
|
||||
"setGenReloadToUpdate": "Seite aktualisieren, um das Update durchzuführen",
|
||||
"setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und sie automatisch installieren, ein automatisches Update von Ihrer Version ist aber nicht möglich.",
|
||||
"setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und diese automatisch installieren, ein automatisches Update Ihrer Version ist aber nicht möglich.",
|
||||
"setGenDownloadUpdate": "Update herunterladen",
|
||||
"setGenUpdateAuto": "Automatisch herunterladen und installieren",
|
||||
"setGenUpdateCheck": "Auf Updates überprüfen aber nicht installieren",
|
||||
"setGenUpdateCheck": "Auf Updates überprüfen, aber nicht installieren",
|
||||
"setGenNoUpdate": "Nie nach Updates suchen",
|
||||
"setGenUpdateChecking": "Suche nach Updates",
|
||||
"setGenUpdateChecking": "Updates werden gesucht",
|
||||
"setGenCheckUpdate": "Nach Updates suchen",
|
||||
"setGenErrorChecking": "Updatesuche fehlgeschlagen",
|
||||
"setGenLastCheckSuccess": "Letzte erfolgreiche Überprüfung am {}",
|
||||
|
@ -388,21 +389,21 @@
|
|||
"setGenCheckedAt": "Überprüft am",
|
||||
"setGenLatestVer": "Sie haben die aktuellste Version",
|
||||
"setGenNewVer": "Neue Version {} verfügbar, veröffentlicht am",
|
||||
"setGenDownloadingUpdate": "Update wird heruntergeladen...",
|
||||
"setGenExtractingUpdate": "Update wird entpackt...",
|
||||
"setGenDownloadingUpdate": "Update wird heruntergeladen …",
|
||||
"setGenExtractingUpdate": "Update wird entpackt …",
|
||||
"setGenCheckErr": "Beim Herunterladen der neuen Version ist ein Fehler aufgetreten",
|
||||
"setGenNeverChecked": "Nie nach Updates gesucht",
|
||||
"setGenRestartToUpdate": "App neu starten, um das Update durchzuführen",
|
||||
"setGenDownloadAndRestart": "Update herunterladen und App neu starten",
|
||||
"setGenAppearance": "Oberfläche",
|
||||
"setGenTheme": "Theme",
|
||||
"setGenThemeFb": "Flat blue",
|
||||
"setGenThemeDb": "Dark brown",
|
||||
"setGenThemeWh": "White",
|
||||
"setGenThemeFb": "Mattblau",
|
||||
"setGenThemeDb": "Dunkelbraun",
|
||||
"setGenThemeWh": "Weiß",
|
||||
"setGenThemeTe": "Terminal",
|
||||
"setGenThemeHc": "High contrast",
|
||||
"setGenThemeSd": "Solarized dark",
|
||||
"setGenThemeSl": "Solarized light",
|
||||
"setGenThemeHc": "Hoher Kontrast",
|
||||
"setGenThemeSd": "Solarisiert dunkel",
|
||||
"setGenThemeSl": "Solarisiert hell",
|
||||
"setGenThemeMacDark": "macOS Dunkel",
|
||||
"setGenLocale": "Sprache",
|
||||
"setGenLocOther": "Andere Sprachen sind als Plugins verfügbar.",
|
||||
|
@ -410,15 +411,15 @@
|
|||
"setGenFontSizeNormal": "Standard",
|
||||
"setGenFontSizeLarge": "Groß",
|
||||
"setGenFontSizeLargest": "Größte",
|
||||
"setGenTitlebarStyle": "Fenster-Stil (erfordert Neustart des Programms)",
|
||||
"setGenTitlebarStyle": "Fensterstil (erfordert Neustart des Programms)",
|
||||
"setGenTitlebarStyleDefault": "Standard",
|
||||
"setGenTitlebarStyleHidden": "Eigener Titel",
|
||||
"setGenTitlebarStyleHiddenInset": "Eigener Titel, verschiebbares Fenster",
|
||||
"setGenShowSubgroups": "Einträge aus allen Untergruppen anzeigen",
|
||||
"setGenTableView": "Einträge in Tabellenansicht anzeigen",
|
||||
"setGenColorfulIcons": "Eigene Icons in der Listenansicht farbig anzeigen",
|
||||
"setGenColorfulIcons": "Eigene Symbole in der Listenansicht farbig anzeigen",
|
||||
"setGenUseMarkdown": "Markdown in Notizen",
|
||||
"setGenUseGroupIconForEntries": "Für neue Einträge automatisch das Gruppen-Icon verwenden",
|
||||
"setGenUseGroupIconForEntries": "Für neue Einträge automatisch das Gruppensymbol verwenden",
|
||||
"setGenDirectAutotype": "Wenn nur ein passender Eintrag gefunden wird, diesen automatisch für Auto-Type auswählen",
|
||||
"setGenFunction": "Arbeitsweise",
|
||||
"setGenAutoSyncOnClose": "Beim Schließen speichern und synchronisieren",
|
||||
|
@ -449,19 +450,19 @@
|
|||
"setGenStorageLogout": "Abmelden",
|
||||
"setGenShowAdvanced": "Erweiterte Einstellungen anzeigen",
|
||||
"setGenDevTools": "Entwicklerwerkzeuge anzeigen",
|
||||
"setGenTryBeta": "Beta-Version bis zum Schließen der App ausprobieren",
|
||||
"setGenTryBeta": "Betaversion bis zum Schließen der App ausprobieren",
|
||||
"setGenTryBetaWarning": "Ungespeicherte Dateien",
|
||||
"setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und Klicken Sie erneut auf diesen Button",
|
||||
"setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und klicken erneut auf diese Schaltfläche",
|
||||
"setGenShowAppLogs": "App-Logs anzeigen",
|
||||
"setGenReloadApp": "App neu laden",
|
||||
"setGenFieldLabelDblClickAutoType": "Auto-Type durch anklicken von Beschriftungen aktivieren",
|
||||
"setGenFieldLabelDblClickAutoType": "Auto-Type durch Anklicken von Beschriftungen aktivieren",
|
||||
"setFilePath": "Dateipfad",
|
||||
"setFileStorage": "Diese Datei wird von {} geladen.",
|
||||
"setFileIntl": "Diese Datei ist im internen App-Speicher abgelegt",
|
||||
"setFileLocalHint": "Sie wollen nahtlos mit lokalen Dateien arbeiten?",
|
||||
"setFileDownloadApp": "Laden Sie die Desktop-App herunter",
|
||||
"setFileSave": "Speichern",
|
||||
"setFileSaveTo": "Speichern unter...",
|
||||
"setFileSaveTo": "Speichern unter …",
|
||||
"setFileClose": "Sperren",
|
||||
"setFileSync": "Synchronisierung",
|
||||
"setFileSyncVerb": "Synchronisieren",
|
||||
|
@ -475,7 +476,7 @@
|
|||
"setFileConfirmPass": "Masterpasswort bestätigen",
|
||||
"setFilePassChange": "Um das Passwort zu ändern, geben Sie es im Bestätigungsfeld an",
|
||||
"setFilePassChanged": "Das Passwort wurde geändert. Lassen Sie das Feld leer, um das bisherige Passwort zu behalten.",
|
||||
"setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie sie erneut ein",
|
||||
"setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie diese erneut ein",
|
||||
"setFileKeyFile": "Schlüsseldatei",
|
||||
"setFileSelKeyFile": "Schlüsseldatei auswählen",
|
||||
"setFileNames": "Namen",
|
||||
|
@ -492,7 +493,7 @@
|
|||
"setFileBackupPath": "Sicherungspfad",
|
||||
"setFileBackupTime": "Sicherungen anlegen",
|
||||
"setFileBackupNow": "Jetzt sichern",
|
||||
"setFileBackupNowWorking": "Sichern...",
|
||||
"setFileBackupNowWorking": "Ausführung läuft …",
|
||||
"setFileBackupError": "Sicherungsfehler",
|
||||
"setFileBackupErrorDescription": "Fehler beim Schreiben der Sicherungsdatei",
|
||||
"setFileBackupErrorIsDir": "Der Sicherungspfad ist ungültig",
|
||||
|
@ -512,9 +513,9 @@
|
|||
"setFileUseGenKeyFile": "Generierte Schlüsseldatei benutzen",
|
||||
"setFileUseOldKeyFile": "Alte Schlüsseldatei benutzen",
|
||||
"setFileGenKeyFile": "Neue Schlüsseldatei generieren",
|
||||
"setFileDontUseKeyFile": "Keine Schlüsseldatei benutzen",
|
||||
"setFileDontUseKeyFile": "Keine Schlüsseldatei verwenden",
|
||||
"setFileEmptyPass": "Leeres Passwort",
|
||||
"setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern macht sie vollkommen ungeschützt. Möchten Sie sie trotzdem speichern?",
|
||||
"setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern, macht diese vollkommen ungeschützt. Möchten Sie dies trotzdem tun?",
|
||||
"setFileSaveError": "Fehler beim Speichern",
|
||||
"setFileSaveErrorBody": "Schreibfehler beim Speichern",
|
||||
"setFileAlreadyExists": "Existiert bereits",
|
||||
|
@ -529,14 +530,16 @@
|
|||
"setFileDeviceIntro": "Einmal-Codes aus diesem {} werden in der App angezeigt.",
|
||||
"setFileDeviceSettings": "Einstellungen",
|
||||
"setFileYubiKey": "YubiKey",
|
||||
"setFileDontUseYubiKey": "Keinen YubiKey benutzen",
|
||||
"setFileDontUseYubiKey": "Keinen YubiKey verwenden",
|
||||
"setFileRefreshYubiKeyList": "Liste aktualisieren",
|
||||
"setFileYubiKeyHeader": "YubiKey",
|
||||
"setFileYubiKeyBody": "Es ist gefährlich, einen YubiKey als Teil des Hauptschlüssels zu verwenden. Falls etwas schief geht, besteht möglicherweise kein Zugriff mehr auf die Passwörter. Haben Sie ein Backup Ihrer Datei erstellt, bevor diese Einstellung geändert wurde?",
|
||||
"setFileYubiKeyErrorEmptyMac": "Scheinbar wurde ein YubiKey angeschlossen, er ist aber nicht sichtbar. KeeWeb muss in den Sicherheitseinstellungen im Abschnitt Input-Monitoring hinzugefügt werden, um auf YubiKeys zugreifen zu können.",
|
||||
"setShTitle": "Tastenkürzel",
|
||||
"setShShowAll": "alle Einträge anzeigen",
|
||||
"setShColors": "mit Farben markierte Einträge anzeigen",
|
||||
"setShTrash": "Papierkorb öffnen",
|
||||
"setShFind": "Suchfeld aktivieren; oder: einfach anfangen, den Suchbegriff zu tippen",
|
||||
"setShFind": "Suchen oder einfach beginnen zu tippen",
|
||||
"setShClearSearch": "Suchfeld leeren",
|
||||
"setShCopyPass": "Passwort bzw. ausgewähltes Feld kopieren",
|
||||
"setShCopyUser": "Benutzername kopieren",
|
||||
|
@ -545,8 +548,8 @@
|
|||
"setShPrev": "zum vorherigen Eintrag gehen",
|
||||
"setShNext": "zum nächsten Eintrag gehen",
|
||||
"setShCreateEntry": "Neuer Eintrag",
|
||||
"setShOpen": "öffnen / neu",
|
||||
"setShSave": "alle Dateien speichern",
|
||||
"setShOpen": "Öffnen / Neu",
|
||||
"setShSave": "Alle Dateien speichern",
|
||||
"setShGen": "Passwort generieren",
|
||||
"setShSet": "App-Einstellungen",
|
||||
"setShCopyPassGlobal": "Passwort kopieren",
|
||||
|
@ -556,12 +559,12 @@
|
|||
"setShRestoreApp": "{} öffnen",
|
||||
"setShGlobal": "Diese systemweiten Tastenkürzel funktionieren auch, wenn die App im Hintergrund läuft. Klicken Sie auf einen Eintrag, um ein eigenes Kürzel festzulegen.",
|
||||
"setShLock": "Datenbank sperren",
|
||||
"setShEdit": "Drücken Sie eine Tastenkombination, um diese als Kürzel zu setzen",
|
||||
"setShEdit": "Drücken Sie eine Tastenkombination, um diese als Kürzel zu festzulegen",
|
||||
"setPlInstallTitle": "Neue Plugins installieren",
|
||||
"setPlInstallDesc": "KeeWeb-Plugins fügen Funktionen, Themes und Sprachen zu KeeWeb hinzu. Plugins werden mit denselben Rechten ausgeführt wie KeeWeb selbst, sie können auf all Ihre Passwörter zugreifen oder diese ändern. Installieren Sie niemals Plugins, denen Sie nicht vertrauen.",
|
||||
"setPlInstallLabel": "Plugin-URL",
|
||||
"setPlInstallBtn": "Installieren",
|
||||
"setPlInstallBtnProgress": "Installiere",
|
||||
"setPlInstallBtnProgress": "Installation läuft",
|
||||
"setPlUninstallBtn": "Deinstallieren",
|
||||
"setPlDisableBtn": "Deaktivieren",
|
||||
"setPlEnableBtn": "Aktivieren",
|
||||
|
@ -575,7 +578,7 @@
|
|||
"setPlLoadTime": "geladen in {}",
|
||||
"setPlLastUpdate": "Letzte Prüfung auf Updates",
|
||||
"setPlLoadError": "Fehler beim Laden des Plugins",
|
||||
"setPlGalleryLoading": "Plugins werden geladen, bitte warten Sie einen Moment",
|
||||
"setPlGalleryLoading": "Plugins werden geladen, bitte einen Moment warten",
|
||||
"setPlGalleryLoadError": "Fehler beim Laden der Plugins",
|
||||
"setPlInstallUrlTitle": "Plugin von einer URL hinzufügen",
|
||||
"setPlInstallUrlDesc": "Wenn das Plugin nicht in der Galerie verfügbar ist, können Sie es manuell von einer URL installieren",
|
||||
|
@ -587,19 +590,35 @@
|
|||
"setPlTranslateLink": "übersetzen Sie die App in Ihre Sprache",
|
||||
"setPlAutoUpdate": "Automatisch aktualisieren",
|
||||
"setPlLoadGallery": "Plugin-Galerie laden",
|
||||
"setDevicesTitle": "Geräte",
|
||||
"setDevicesEnableUsb": "Interaktion mit USB-Geräten aktivieren",
|
||||
"setDevicesYubiKeyIntro": "Es wird empfehlen {} zu lesen, bevor ein YubiKey verwendet wird.",
|
||||
"setDevicesYubiKeyIntroLink": "dieses Dokument",
|
||||
"setDevicesYubiKeyToolsDesc": "Um YubiKey in diesem Modus verwenden zu können, müssen Sie vorher das Tool {} installieren.",
|
||||
"setDevicesYubiKeyToolsDesc2": "{}, um mehr Informationen über dieses Tool zu erhalten.",
|
||||
"setDevicesYubiKeyToolsDescLink": "Hier klicken",
|
||||
"setDevicesYubiKeyToolsStatusChecking": "Prüfung, ob {} installiert ist",
|
||||
"setDevicesYubiKeyToolsStatusOk": "{} ist installiert",
|
||||
"setDevicesYubiKeyToolsStatusError": "{} ist nicht installiert oder funktioniert nicht korrekt",
|
||||
"setDevicesYubiKeyOtpTitle": "Einmal-Codes",
|
||||
"setDevicesYubiKeyOtpDesc": "Ein YubiKey kann genutzt werden, um Einmal-Codes für verschiedene Dienste zu erzeugen.",
|
||||
"setDevicesYubiKeyOtpShowIcon": "Das Symbol auf der Startseite anzeigen",
|
||||
"setDevicesYubiKeyOtpAutoOpen": "Einmal-Codes automatisch laden, wenn Dateien geöffnet sind",
|
||||
"setDevicesYubiKeyOtpMatchEntries": "Passende Einmal-Codes in den Einträgen anzeigen",
|
||||
"setDevicesYubiKeyChalRespTitle": "Challenge-Response",
|
||||
"setDevicesYubiKeyChalRespDesc": "Es ist auch möglich, einen YubiKey im Challenge-Response-Modus zu verwenden, um einen Teil des Private Schlüssels, der zum Verschlüsseln der Dateien verwendet wird, auf dem YubiKey zu speichern.",
|
||||
"setDevicesYubiKeyChalRespShow": "Optionen zum Verwenden eines YubiKeys beim Öffnen von Dateien anzeigen",
|
||||
"setDevicesYubiKeyStuckWorkaround": "YubiKey entfernen und neu verbinden, falls der Ladeprozess hängt",
|
||||
"setDevicesYubiKeyRememberChalResp": "Challenge-Response-Einstellungen merken, während die App geöffnet ist",
|
||||
"setAboutTitle": "Über",
|
||||
"setAboutBuilt": "Diese App wurde mit den folgenden Werkzeugen erstellt",
|
||||
"setAboutBuilt": "Diese App wurde mit folgenden Werkzeugen erstellt",
|
||||
"setAboutLic": "Lizenz",
|
||||
"setAboutLicComment": "Die App selbst und alle enthaltenen Komponenten, die nicht Public Domain sind, stehen unter der MIT-Lizenz.",
|
||||
"setAboutFirst": "Dies ist eine Open-Source App, erstellt von {}",
|
||||
"setAboutSecond": "und lizenziert unter {}.",
|
||||
"setAboutSource": "Quellcode und Tickets befinden sich auf {}.",
|
||||
"setHelpFormat": "Dateiformat",
|
||||
"setHelpFormatBody": "Dies ist eine Portierung der {} App, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100% kompatibel und sollte in beiden Anwendungen nutzbar sein.",
|
||||
"setHelpFormatBody": "Dies ist eine Portierung der App {}, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100 % kompatibel und sollte in beiden Anwendungen nutzbar sein.",
|
||||
"setHelpProblems": "Probleme?",
|
||||
"setHelpProblems1": "Falls etwas schief geht, {}",
|
||||
"setHelpProblems2": "oder {}",
|
||||
|
@ -612,14 +631,15 @@
|
|||
"setHelpUpdates": "Neuigkeiten",
|
||||
"setHelpTwitter": "Twitter",
|
||||
"dropboxSetupDesc": "Etwas Konfiguration ist notwendig, um Dropbox in einer selbst gehosteten Web-App zu verwenden. Bitte erstellen Sie eine eigene Dropbox-App und tragen Sie ihren App-Schlüssel unten ein.",
|
||||
"dropboxAppKey": "Dropbox App-Schlüssel",
|
||||
"dropboxAppKey": "Dropbox-App-Schlüssel",
|
||||
"dropboxAppKeyDesc": "Kopieren Sie den Schlüssel aus Ihrer Dropbox-App (Entwicklereinstellungen)",
|
||||
"dropboxAppSecret": "Dropbox App Secret",
|
||||
"dropboxAppSecretDesc": "Das App Secret ist neben dem App Key zu finden",
|
||||
"dropboxAppKeyHint": "Ihr App-Schlüssel",
|
||||
"dropboxAppSecret": "Dropbox-App-Secret",
|
||||
"dropboxAppSecretDesc": "Das App-Secret ist neben dem App-Schlüssel zu finden",
|
||||
"dropboxFolder": "App-Ordner",
|
||||
"dropboxFolderDesc": "Wenn Ihre App mit der gesamten Dropbox verlinkt ist (anstatt mit einem bestimmten Ordner), geben Sie hier den Pfad zum Ordner mit Ihren KDBX-Dateien an.",
|
||||
"dropboxFolderSettingsDesc": "Wählen Sie einen Ordner in Ihrer Dropbox, in dem die Dateien gespeichert werden (standardmäßig das Stammverzeichnis)",
|
||||
"dropboxFolderPlaceholder": "Standard-Ordner",
|
||||
"dropboxFolderPlaceholder": "Standardordner",
|
||||
"dropboxLink": "App verlinken mit",
|
||||
"dropboxLinkApp": "App-Ordner (Apps/KeeWeb)",
|
||||
"dropboxLinkFull": "Gesamte Dropbox oder beliebiger Ordner",
|
||||
|
@ -628,10 +648,10 @@
|
|||
"webdavSaveMethod": "Speichermethode",
|
||||
"webdavSaveMove": "Eine temporäre Datei hochladen und verschieben",
|
||||
"webdavSavePut": "kdbx-Datei mittels PUT überschreiben",
|
||||
"launcherSave": "Passwort-Datenbank speichern",
|
||||
"launcherSave": "Passwortdatenbank speichern",
|
||||
"launcherFileFilter": "KeePass-Dateien",
|
||||
"authPopupRequired": "Pop-Ups blockiert",
|
||||
"authPopupRequiredBody": "Bitte erlauben Sie Pop-Ups in Ihrem Browser.",
|
||||
"authPopupRequired": "Pop-ups blockiert",
|
||||
"authPopupRequiredBody": "Bitte erlauben Sie Pop-ups in Ihrem Browser.",
|
||||
"exportFileInfo": "Dateiinformationen",
|
||||
"exportHtmlName": "Name",
|
||||
"exportHtmlDate": "Exportdatum",
|
||||
|
@ -641,5 +661,13 @@
|
|||
"importCsvRun": "Importieren",
|
||||
"importIgnoreField": "Ignorieren",
|
||||
"importTo": "Einträge werden importiert nach:",
|
||||
"importNewFile": "Neue Datei"
|
||||
"importNewFile": "Neue Datei",
|
||||
"yubiKeyStuckError": "Der YubiKey scheint nicht mehr zu funktionieren. Auto-Repair kann in den App-Einstellungen aktiviert werden.",
|
||||
"yubiKeyNoKeyHeader": "YubiKey benötigt",
|
||||
"yubiKeyNoKeyBody": "Bitte den YubiKey mit der Seriennummer {} anschließen",
|
||||
"yubiKeySlot": "Anschluss",
|
||||
"yubiKeyTouchRequestedHeader": "Bitte den YubiKey berühren",
|
||||
"yubiKeyTouchRequestedBody": "Bitte den YubiKey mit der Seriennummer {} berühren",
|
||||
"yubiKeyDisabledErrorHeader": "USB ist deaktiviert",
|
||||
"yubiKeyDisabledErrorBody": "YubiKey ist zum Öffnen dieser Datei erforderlich. Bitte USB-Geräte in den Einstellungen aktivieren."
|
||||
}
|
|
@ -397,6 +397,8 @@
|
|||
"setGenDownloadAndRestart": "Télécharger la mise à jour et redémarrer",
|
||||
"setGenAppearance": "Apparence",
|
||||
"setGenTheme": "Thème",
|
||||
"setGenThemeDark": "Sombre",
|
||||
"setGenThemeLight": "Clair",
|
||||
"setGenThemeFb": "Bleu plat",
|
||||
"setGenThemeDb": "Marron foncé",
|
||||
"setGenThemeWh": "Blanc",
|
||||
|
@ -404,6 +406,7 @@
|
|||
"setGenThemeHc": "Contraste élevé",
|
||||
"setGenThemeSd": "Solarized dark",
|
||||
"setGenThemeSl": "Solarized light",
|
||||
"setGenMoreThemes": "Plus de thèmes",
|
||||
"setGenThemeMacDark": "Mode sombre de macOS",
|
||||
"setGenLocale": "Langue",
|
||||
"setGenLocOther": "d'autres langues sont disponibles en tant que modules",
|
||||
|
@ -648,6 +651,8 @@
|
|||
"webdavSaveMethod": "Méthode de sauvegarde",
|
||||
"webdavSaveMove": "Envoyer un fichier temporaire et le déplacer",
|
||||
"webdavSavePut": "Ecraser le fichier kdbx avec PUT",
|
||||
"webdavNoLastModified": "L'entête HTTP \"Last-Modified\" est absent",
|
||||
"webdavStatReload": "Toujours recharger le fichier au lieu de se fier à l'entête HTTP \"Last-Modified\"",
|
||||
"launcherSave": "Sauvegarder base des mots de passe",
|
||||
"launcherFileFilter": "Fichiers Keepass",
|
||||
"authPopupRequired": "Les pop-up sont bloquées",
|
||||
|
@ -667,5 +672,8 @@
|
|||
"yubiKeyNoKeyBody": "Merci d'insérer votre YubiKey avec le numéro de série {}",
|
||||
"yubiKeySlot": "port",
|
||||
"yubiKeyTouchRequestedHeader": "Touchez votre YubiKey",
|
||||
"yubiKeyTouchRequestedBody": "Merci de toucher votre YubiKey avec le numéro de série {}"
|
||||
"yubiKeyTouchRequestedBody": "Merci de toucher votre YubiKey avec le numéro de série {}",
|
||||
"yubiKeyDisabledErrorHeader": "L'USB est désactivé",
|
||||
"yubiKeyDisabledErrorBody": "Yubikey est nécessaire pour ouvrir ce fichier, merci d'activer les appareils USB dans les paramètres",
|
||||
"yubiKeyErrorWithCode": "Erreur Yubikey code {}."
|
||||
}
|
|
@ -16,7 +16,7 @@ import { YubiKeyOtpModel } from 'models/external/yubikey-otp-model';
|
|||
import { MenuModel } from 'models/menu/menu-model';
|
||||
import { PluginManager } from 'plugins/plugin-manager';
|
||||
import { Features } from 'util/features';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { UrlFormat } from 'util/formatting/url-format';
|
||||
import { IdGenerator } from 'util/generators/id-generator';
|
||||
import { Locale } from 'util/locale';
|
||||
|
|
|
@ -21,6 +21,15 @@ class AppSettingsModel extends Model {
|
|||
if (data.rememberKeyFiles === true) {
|
||||
data.rememberKeyFiles = 'data';
|
||||
}
|
||||
if (data.locale === 'en') {
|
||||
data.locale = 'en-US';
|
||||
}
|
||||
if (data.theme === 'macdark') {
|
||||
data.theme = 'dark';
|
||||
}
|
||||
if (data.theme === 'wh') {
|
||||
data.theme = 'light';
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
|
|
|
@ -48,7 +48,7 @@ function getIcon(ext) {
|
|||
case 'log':
|
||||
case 'rtf':
|
||||
case 'pem':
|
||||
return 'file-text-o';
|
||||
return 'file-alt';
|
||||
case 'html':
|
||||
case 'htm':
|
||||
case 'js':
|
||||
|
@ -74,9 +74,9 @@ function getIcon(ext) {
|
|||
case 'yml':
|
||||
case 'asm':
|
||||
case 'bat':
|
||||
return 'file-code-o';
|
||||
return 'file-code';
|
||||
case 'pdf':
|
||||
return 'file-pdf-o';
|
||||
return 'file-pdf';
|
||||
case 'zip':
|
||||
case 'rar':
|
||||
case 'bz':
|
||||
|
@ -89,16 +89,16 @@ function getIcon(ext) {
|
|||
case 'ace':
|
||||
case 'dmg':
|
||||
case 'jar':
|
||||
return 'file-archive-o';
|
||||
return 'file-archive';
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
return 'file-word-o';
|
||||
return 'file-word';
|
||||
case 'xls':
|
||||
case 'xlsx':
|
||||
return 'file-excel-o';
|
||||
return 'file-excel';
|
||||
case 'ppt':
|
||||
case 'pptx':
|
||||
return 'file-powerpoint-o';
|
||||
return 'file-powerpoint';
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
case 'png':
|
||||
|
@ -108,7 +108,7 @@ function getIcon(ext) {
|
|||
case 'svg':
|
||||
case 'ico':
|
||||
case 'psd':
|
||||
return 'file-image-o';
|
||||
return 'file-image';
|
||||
case 'avi':
|
||||
case 'mp4':
|
||||
case '3gp':
|
||||
|
@ -117,13 +117,13 @@ function getIcon(ext) {
|
|||
case 'mpeg':
|
||||
case 'mpg':
|
||||
case 'mpe':
|
||||
return 'file-video-o';
|
||||
return 'file-video';
|
||||
case 'mp3':
|
||||
case 'wav':
|
||||
case 'flac':
|
||||
return 'file-audio-o';
|
||||
return 'file-audio';
|
||||
}
|
||||
return 'file-o';
|
||||
return 'file';
|
||||
}
|
||||
|
||||
function getMimeType(ext) {
|
||||
|
|
|
@ -71,7 +71,7 @@ class YubiKeyOtpModel extends ExternalOtpDeviceModel {
|
|||
id: this.entryId(code.title, code.user),
|
||||
device: this,
|
||||
deviceSubId: serial,
|
||||
icon: 'clock-o',
|
||||
icon: 'clock',
|
||||
title: code.title,
|
||||
user: code.user,
|
||||
needsTouch: code.needsTouch
|
||||
|
|
|
@ -47,6 +47,7 @@ MenuItemModel.defineModelProperties({
|
|||
shortcut: null,
|
||||
options: null,
|
||||
cls: null,
|
||||
iconCls: null,
|
||||
disabled: false,
|
||||
visible: true,
|
||||
drag: false,
|
||||
|
@ -57,7 +58,8 @@ MenuItemModel.defineModelProperties({
|
|||
defaultItem: false,
|
||||
page: null,
|
||||
editable: false,
|
||||
file: null
|
||||
file: null,
|
||||
section: null
|
||||
});
|
||||
|
||||
export { MenuItemModel };
|
||||
|
|
|
@ -42,7 +42,7 @@ class MenuModel extends Model {
|
|||
this.trashSection = new MenuSectionModel([
|
||||
{
|
||||
locTitle: 'menuTrash',
|
||||
icon: 'trash',
|
||||
icon: 'trash-alt',
|
||||
shortcut: Keys.DOM_VK_D,
|
||||
filterKey: 'trash',
|
||||
filterValue: true,
|
||||
|
@ -51,7 +51,7 @@ class MenuModel extends Model {
|
|||
]);
|
||||
Colors.AllColors.forEach((color) => {
|
||||
const option = {
|
||||
cls: 'fa ' + color + '-color',
|
||||
cls: `fa ${color}-color`,
|
||||
value: color,
|
||||
filterValue: color
|
||||
};
|
||||
|
@ -66,10 +66,51 @@ class MenuModel extends Model {
|
|||
]);
|
||||
|
||||
this.generalSection = new MenuSectionModel([
|
||||
{ locTitle: 'menuSetGeneral', icon: 'cog', page: 'general', active: true }
|
||||
{
|
||||
locTitle: 'menuSetGeneral',
|
||||
icon: 'cog',
|
||||
page: 'general',
|
||||
section: 'top',
|
||||
active: true
|
||||
},
|
||||
{
|
||||
locTitle: 'setGenAppearance',
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
section: 'appearance',
|
||||
active: true
|
||||
},
|
||||
{
|
||||
locTitle: 'setGenFunction',
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
section: 'function',
|
||||
active: true
|
||||
},
|
||||
{
|
||||
locTitle: 'setGenLock',
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
section: 'lock',
|
||||
active: true
|
||||
},
|
||||
{
|
||||
locTitle: 'setGenStorage',
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
section: 'storage',
|
||||
active: true
|
||||
},
|
||||
{
|
||||
locTitle: 'advanced',
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
section: 'advanced',
|
||||
active: true
|
||||
}
|
||||
]);
|
||||
this.shortcutsSection = new MenuSectionModel([
|
||||
{ locTitle: 'shortcuts', icon: 'keyboard-o', page: 'shortcuts' }
|
||||
{ locTitle: 'shortcuts', icon: 'keyboard', page: 'shortcuts' }
|
||||
]);
|
||||
this.pluginsSection = new MenuSectionModel([
|
||||
{ locTitle: 'plugins', icon: 'puzzle-piece', page: 'plugins' }
|
||||
|
@ -116,9 +157,8 @@ class MenuModel extends Model {
|
|||
this.colorsItem.options.forEach((opt) => {
|
||||
opt.active = opt === sel.option;
|
||||
});
|
||||
const selColor =
|
||||
sel.item === this.colorsItem && sel.option ? sel.option.value + '-color' : '';
|
||||
this.colorsItem.cls = 'menu__item-colors ' + selColor;
|
||||
this.colorsItem.iconCls =
|
||||
sel.item === this.colorsItem && sel.option ? sel.option.value + '-color' : null;
|
||||
const filterKey = sel.item.filterKey;
|
||||
const filterValue = (sel.option || sel.item).filterValue;
|
||||
const filter = {};
|
||||
|
@ -127,6 +167,7 @@ class MenuModel extends Model {
|
|||
} else if (sections === this.menus.settings) {
|
||||
Events.emit('set-page', {
|
||||
page: sel.item.page,
|
||||
section: sel.item.section,
|
||||
file: sel.item.file
|
||||
});
|
||||
}
|
||||
|
|
|
@ -434,7 +434,7 @@ class Plugin extends Model {
|
|||
delete SettingsManager.allLocales[locale.name];
|
||||
delete SettingsManager.customLocales[locale.name];
|
||||
if (SettingsManager.activeLocale === locale.name) {
|
||||
AppSettingsModel.locale = 'en';
|
||||
AppSettingsModel.locale = 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,13 +24,14 @@ const ThemeVars = {
|
|||
|
||||
apply(cssStyle) {
|
||||
this.init();
|
||||
const lines = ThemeVarsScss.split('\n');
|
||||
for (const line of lines) {
|
||||
const match = line.match(/\s*([^:]+):\s*(.*?),?\s*$/);
|
||||
if (!match) {
|
||||
continue;
|
||||
const matches = ThemeVarsScss.replace(/[\n\s]+/g, '').matchAll(/([\w\-]+):([^:]+),(\$)?/g);
|
||||
for (let [, name, def, last] of matches) {
|
||||
if (last && def.endsWith(')')) {
|
||||
// definitions are written like this:
|
||||
// map-merge((def:val, def:val, ..., last-def:val),$t)
|
||||
// so, the last item has "),$" captured, here we're removing that bracket
|
||||
def = def.substr(0, def.length - 1);
|
||||
}
|
||||
const [, name, def] = match;
|
||||
const propName = '--' + name;
|
||||
const currentValue = cssStyle.getPropertyValue(propName);
|
||||
if (currentValue) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
const EntryPresenter = function (descField, noColor, activeEntryId) {
|
||||
|
|
|
@ -82,7 +82,8 @@ class StorageDropbox extends StorageBase {
|
|||
clientSecret: this._getSecret(),
|
||||
pkce: true,
|
||||
width: 600,
|
||||
height: 400
|
||||
height: 400,
|
||||
urlParams: { 'token_access_type': 'offline' }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class StorageGDrive extends StorageBase {
|
|||
name = 'gdrive';
|
||||
enabled = true;
|
||||
uipos = 30;
|
||||
iconSvg = 'google-drive';
|
||||
icon = 'google-drive';
|
||||
|
||||
_baseUrl = 'https://www.googleapis.com/drive/v3';
|
||||
_baseUrlUpload = 'https://www.googleapis.com/upload/drive/v3';
|
||||
|
|
|
@ -8,7 +8,7 @@ class StorageOneDrive extends StorageBase {
|
|||
name = 'onedrive';
|
||||
enabled = true;
|
||||
uipos = 40;
|
||||
iconSvg = 'onedrive';
|
||||
icon = 'onedrive';
|
||||
|
||||
_baseUrl = 'https://graph.microsoft.com/v1.0/me';
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import kdbxweb from 'kdbxweb';
|
||||
import { StorageBase } from 'storage/storage-base';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
class StorageWebDav extends StorageBase {
|
||||
name = 'webdav';
|
||||
|
@ -48,6 +50,12 @@ class StorageWebDav extends StorageBase {
|
|||
type: 'select',
|
||||
value: this.appSettings.webdavSaveMethod || 'default',
|
||||
options: { default: 'webdavSaveMove', put: 'webdavSavePut' }
|
||||
},
|
||||
{
|
||||
id: 'webdavStatReload',
|
||||
title: 'webdavStatReload',
|
||||
type: 'checkbox',
|
||||
value: !!this.appSettings.webdavStatReload
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -64,33 +72,67 @@ class StorageWebDav extends StorageBase {
|
|||
method: 'GET',
|
||||
path,
|
||||
user: opts ? opts.user : null,
|
||||
password: opts ? opts.password : null
|
||||
password: opts ? opts.password : null,
|
||||
nostat: this.appSettings.webdavStatReload
|
||||
},
|
||||
callback
|
||||
? (err, xhr, stat) => {
|
||||
callback(err, xhr.response, stat);
|
||||
if (this.appSettings.webdavStatReload) {
|
||||
this._calcStatByContent(xhr).then((stat) =>
|
||||
callback(err, xhr.response, stat)
|
||||
);
|
||||
} else {
|
||||
callback(err, xhr.response, stat);
|
||||
}
|
||||
}
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
stat(path, opts, callback) {
|
||||
this._request(
|
||||
{
|
||||
op: 'Stat',
|
||||
method: 'HEAD',
|
||||
path,
|
||||
user: opts ? opts.user : null,
|
||||
password: opts ? opts.password : null
|
||||
},
|
||||
callback
|
||||
? (err, xhr, stat) => {
|
||||
callback(err, stat);
|
||||
}
|
||||
: null
|
||||
this._statRequest(
|
||||
path,
|
||||
opts,
|
||||
'Stat',
|
||||
callback ? (err, xhr, stat) => callback(err, stat) : null
|
||||
);
|
||||
}
|
||||
|
||||
_statRequest(path, opts, op, callback) {
|
||||
if (this.appSettings.webdavStatReload) {
|
||||
this._request(
|
||||
{
|
||||
op,
|
||||
method: 'GET',
|
||||
path,
|
||||
user: opts ? opts.user : null,
|
||||
password: opts ? opts.password : null,
|
||||
nostat: true
|
||||
},
|
||||
callback
|
||||
? (err, xhr) => {
|
||||
this._calcStatByContent(xhr).then((stat) => callback(err, xhr, stat));
|
||||
}
|
||||
: null
|
||||
);
|
||||
} else {
|
||||
this._request(
|
||||
{
|
||||
op,
|
||||
method: 'HEAD',
|
||||
path,
|
||||
user: opts ? opts.user : null,
|
||||
password: opts ? opts.password : null
|
||||
},
|
||||
callback
|
||||
? (err, xhr, stat) => {
|
||||
callback(err, xhr, stat);
|
||||
}
|
||||
: null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
save(path, opts, data, callback, rev) {
|
||||
const cb = function (err, xhr, stat) {
|
||||
if (callback) {
|
||||
|
@ -104,143 +146,113 @@ class StorageWebDav extends StorageBase {
|
|||
user: opts ? opts.user : null,
|
||||
password: opts ? opts.password : null
|
||||
};
|
||||
const that = this;
|
||||
this._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:stat',
|
||||
method: 'HEAD'
|
||||
},
|
||||
(err, xhr, stat) => {
|
||||
let useTmpPath = this.appSettings.webdavSaveMethod !== 'put';
|
||||
if (err) {
|
||||
if (!err.notFound) {
|
||||
return cb(err);
|
||||
} else {
|
||||
that.logger.debug('Save: not found, creating');
|
||||
useTmpPath = false;
|
||||
}
|
||||
} else if (stat.rev !== rev) {
|
||||
that.logger.debug('Save error', path, 'rev conflict', stat.rev, rev);
|
||||
return cb({ revConflict: true }, xhr, stat);
|
||||
}
|
||||
if (useTmpPath) {
|
||||
that._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:put',
|
||||
method: 'PUT',
|
||||
path: tmpPath,
|
||||
data,
|
||||
nostat: true
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
that._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:stat',
|
||||
method: 'HEAD'
|
||||
},
|
||||
(err, xhr, stat) => {
|
||||
if (err) {
|
||||
that._request({
|
||||
...saveOpts,
|
||||
op: 'Save:delete',
|
||||
method: 'DELETE',
|
||||
path: tmpPath
|
||||
});
|
||||
return cb(err, xhr, stat);
|
||||
}
|
||||
if (stat.rev !== rev) {
|
||||
that.logger.debug(
|
||||
'Save error',
|
||||
path,
|
||||
'rev conflict',
|
||||
stat.rev,
|
||||
rev
|
||||
);
|
||||
that._request({
|
||||
...saveOpts,
|
||||
op: 'Save:delete',
|
||||
method: 'DELETE',
|
||||
path: tmpPath
|
||||
});
|
||||
return cb({ revConflict: true }, xhr, stat);
|
||||
}
|
||||
let movePath = path;
|
||||
if (movePath.indexOf('://') < 0) {
|
||||
if (movePath.indexOf('/') === 0) {
|
||||
movePath =
|
||||
location.protocol + '//' + location.host + movePath;
|
||||
} else {
|
||||
movePath = location.href
|
||||
.replace(/\?(.*)/, '')
|
||||
.replace(/[^/]*$/, movePath);
|
||||
}
|
||||
}
|
||||
that._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:move',
|
||||
method: 'MOVE',
|
||||
path: tmpPath,
|
||||
nostat: true,
|
||||
headers: {
|
||||
Destination: encodeURI(movePath),
|
||||
'Overwrite': 'T'
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
that._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:stat',
|
||||
method: 'HEAD'
|
||||
},
|
||||
(err, xhr, stat) => {
|
||||
cb(err, xhr, stat);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
this._statRequest(path, opts, 'Save:stat', (err, xhr, stat) => {
|
||||
let useTmpPath = this.appSettings.webdavSaveMethod !== 'put';
|
||||
if (err) {
|
||||
if (!err.notFound) {
|
||||
return cb(err);
|
||||
} else {
|
||||
that._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:put',
|
||||
method: 'PUT',
|
||||
data,
|
||||
nostat: true
|
||||
},
|
||||
(err) => {
|
||||
this.logger.debug('Save: not found, creating');
|
||||
useTmpPath = false;
|
||||
}
|
||||
} else if (stat.rev !== rev) {
|
||||
this.logger.debug('Save error', path, 'rev conflict', stat.rev, rev);
|
||||
return cb({ revConflict: true }, xhr, stat);
|
||||
}
|
||||
if (useTmpPath) {
|
||||
this._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:put',
|
||||
method: 'PUT',
|
||||
path: tmpPath,
|
||||
data,
|
||||
nostat: true
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
this._statRequest(path, opts, 'Save:stat', (err, xhr, stat) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
this._request({
|
||||
...saveOpts,
|
||||
op: 'Save:delete',
|
||||
method: 'DELETE',
|
||||
path: tmpPath
|
||||
});
|
||||
return cb(err, xhr, stat);
|
||||
}
|
||||
that._request(
|
||||
if (stat.rev !== rev) {
|
||||
this.logger.debug(
|
||||
'Save error',
|
||||
path,
|
||||
'rev conflict',
|
||||
stat.rev,
|
||||
rev
|
||||
);
|
||||
this._request({
|
||||
...saveOpts,
|
||||
op: 'Save:delete',
|
||||
method: 'DELETE',
|
||||
path: tmpPath
|
||||
});
|
||||
return cb({ revConflict: true }, xhr, stat);
|
||||
}
|
||||
let movePath = path;
|
||||
if (movePath.indexOf('://') < 0) {
|
||||
if (movePath.indexOf('/') === 0) {
|
||||
movePath = location.protocol + '//' + location.host + movePath;
|
||||
} else {
|
||||
movePath = location.href
|
||||
.replace(/\?(.*)/, '')
|
||||
.replace(/[^/]*$/, movePath);
|
||||
}
|
||||
}
|
||||
this._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:stat',
|
||||
method: 'HEAD'
|
||||
op: 'Save:move',
|
||||
method: 'MOVE',
|
||||
path: tmpPath,
|
||||
nostat: true,
|
||||
headers: {
|
||||
Destination: encodeURI(movePath),
|
||||
'Overwrite': 'T'
|
||||
}
|
||||
},
|
||||
(err, xhr, stat) => {
|
||||
cb(err, xhr, stat);
|
||||
(err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
this._statRequest(path, opts, 'Save:stat', (err, xhr, stat) => {
|
||||
cb(err, xhr, stat);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this._request(
|
||||
{
|
||||
...saveOpts,
|
||||
op: 'Save:put',
|
||||
method: 'PUT',
|
||||
data,
|
||||
nostat: true
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
this._statRequest(path, opts, 'Save:stat', (err, xhr, stat) => {
|
||||
cb(err, xhr, stat);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fileOptsToStoreOpts(opts, file) {
|
||||
|
@ -276,21 +288,20 @@ class StorageWebDav extends StorageBase {
|
|||
}
|
||||
|
||||
_request(config, callback) {
|
||||
const that = this;
|
||||
if (config.rev) {
|
||||
that.logger.debug(config.op, config.path, config.rev);
|
||||
this.logger.debug(config.op, config.path, config.rev);
|
||||
} else {
|
||||
that.logger.debug(config.op, config.path);
|
||||
this.logger.debug(config.op, config.path);
|
||||
}
|
||||
const ts = that.logger.ts();
|
||||
const ts = this.logger.ts();
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.addEventListener('load', () => {
|
||||
if ([200, 201, 204].indexOf(xhr.status) < 0) {
|
||||
that.logger.debug(
|
||||
this.logger.debug(
|
||||
config.op + ' error',
|
||||
config.path,
|
||||
xhr.status,
|
||||
that.logger.ts(ts)
|
||||
this.logger.ts(ts)
|
||||
);
|
||||
let err;
|
||||
switch (xhr.status) {
|
||||
|
@ -312,35 +323,35 @@ class StorageWebDav extends StorageBase {
|
|||
}
|
||||
const rev = xhr.getResponseHeader('Last-Modified');
|
||||
if (!rev && !config.nostat) {
|
||||
that.logger.debug(
|
||||
this.logger.debug(
|
||||
config.op + ' error',
|
||||
config.path,
|
||||
'no headers',
|
||||
that.logger.ts(ts)
|
||||
this.logger.ts(ts)
|
||||
);
|
||||
if (callback) {
|
||||
callback('No Last-Modified header', xhr);
|
||||
callback(Locale.webdavNoLastModified, xhr);
|
||||
callback = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const completedOpName =
|
||||
config.op + (config.op.charAt(config.op.length - 1) === 'e' ? 'd' : 'ed');
|
||||
that.logger.debug(completedOpName, config.path, rev, that.logger.ts(ts));
|
||||
this.logger.debug(completedOpName, config.path, rev, this.logger.ts(ts));
|
||||
if (callback) {
|
||||
callback(null, xhr, rev ? { rev } : null);
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
xhr.addEventListener('error', () => {
|
||||
that.logger.debug(config.op + ' error', config.path, that.logger.ts(ts));
|
||||
this.logger.debug(config.op + ' error', config.path, this.logger.ts(ts));
|
||||
if (callback) {
|
||||
callback('network error', xhr);
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
xhr.addEventListener('abort', () => {
|
||||
that.logger.debug(config.op + ' error', config.path, 'aborted', that.logger.ts(ts));
|
||||
this.logger.debug(config.op + ' error', config.path, 'aborted', this.logger.ts(ts));
|
||||
if (callback) {
|
||||
callback('aborted', xhr);
|
||||
callback = null;
|
||||
|
@ -369,6 +380,23 @@ class StorageWebDav extends StorageBase {
|
|||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
_calcStatByContent(xhr) {
|
||||
if (
|
||||
xhr.status !== 200 ||
|
||||
xhr.responseType !== 'arraybuffer' ||
|
||||
!xhr.response ||
|
||||
!xhr.response.byteLength
|
||||
) {
|
||||
this.logger.debug('Cannot calculate rev by content');
|
||||
return null;
|
||||
}
|
||||
return kdbxweb.CryptoEngine.sha256(xhr.response).then((hash) => {
|
||||
const rev = kdbxweb.ByteUtils.bytesToHex(hash).substr(0, 10);
|
||||
this.logger.debug('Calculated rev by content', `${xhr.response.byteLength} bytes`, rev);
|
||||
return { rev };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { StorageWebDav };
|
||||
|
|
|
@ -15,7 +15,6 @@ const MaxRequestRetries = 3;
|
|||
class StorageBase {
|
||||
name = null;
|
||||
icon = null;
|
||||
iconSvg = null;
|
||||
enabled = false;
|
||||
system = false;
|
||||
uipos = null;
|
||||
|
@ -244,7 +243,8 @@ class StorageBase {
|
|||
'state': session.state,
|
||||
'redirect_uri': session.redirectUri,
|
||||
'response_type': 'code',
|
||||
...pkceParams
|
||||
...pkceParams,
|
||||
...opts.urlParams
|
||||
});
|
||||
|
||||
if (listener) {
|
||||
|
|
|
@ -16,7 +16,6 @@ const Features = {
|
|||
!isDesktop &&
|
||||
!/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href),
|
||||
isLocal: location.origin.indexOf('localhost') >= 0,
|
||||
canUseWasmInWebWorker: !isDesktop && !/Chrome/.test(navigator.appVersion), // TODO: enable it back in Chrome
|
||||
|
||||
supportsTitleBarStyles() {
|
||||
return this.isMac;
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import { StringFormat } from 'util/formatting/string-format';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
const DateFormat = {
|
||||
dtStr(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? this.dStr(dt) +
|
||||
' ' +
|
||||
StringFormat.pad(dt.getHours(), 2) +
|
||||
':' +
|
||||
StringFormat.pad(dt.getMinutes(), 2) +
|
||||
':' +
|
||||
StringFormat.pad(dt.getSeconds(), 2)
|
||||
: '';
|
||||
},
|
||||
|
||||
dStr(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? dt.getDate() + ' ' + Locale.monthsShort[dt.getMonth()] + ' ' + dt.getFullYear()
|
||||
: '';
|
||||
},
|
||||
|
||||
dtStrFs(dt) {
|
||||
if (typeof dt === 'number') {
|
||||
dt = new Date(dt);
|
||||
}
|
||||
return dt
|
||||
? dt.getFullYear() +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getMonth() + 1, 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getDate(), 2) +
|
||||
'T' +
|
||||
StringFormat.pad(dt.getHours(), 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getMinutes(), 2) +
|
||||
'-' +
|
||||
StringFormat.pad(dt.getSeconds(), 2)
|
||||
: '';
|
||||
}
|
||||
};
|
||||
|
||||
export { DateFormat };
|
|
@ -1,7 +1,6 @@
|
|||
import kdbxweb from 'kdbxweb';
|
||||
import { Logger } from 'util/logger';
|
||||
import { Features } from 'util/features';
|
||||
import { AppSettingsModel } from 'models/app-settings-model';
|
||||
import { NativeModules } from 'comp/launcher/native-modules';
|
||||
|
||||
const logger = new Logger('argon2');
|
||||
|
@ -29,7 +28,7 @@ const KdbxwebInit = {
|
|||
if (!global.WebAssembly) {
|
||||
return Promise.reject('WebAssembly is not supported');
|
||||
}
|
||||
if (Features.isDesktop && AppSettingsModel.nativeArgon2) {
|
||||
if (Features.isDesktop) {
|
||||
logger.debug('Using native argon2');
|
||||
this.runtimeModule = {
|
||||
hash(args) {
|
||||
|
@ -113,116 +112,70 @@ const KdbxwebInit = {
|
|||
totalMemory
|
||||
);
|
||||
|
||||
if (Features.canUseWasmInWebWorker) {
|
||||
const memoryDecl = `var wasmMemory=new WebAssembly.Memory({initial:${initialMemory},maximum:${totalMemory}});`;
|
||||
const moduleDecl =
|
||||
'var Module={' +
|
||||
'wasmJSMethod: "native-wasm",' +
|
||||
'wasmBinary: Uint8Array.from(atob("' +
|
||||
wasmBinaryBase64 +
|
||||
'"), c => c.charCodeAt(0)),' +
|
||||
'print(...args) { postMessage({op:"log",args}) },' +
|
||||
'printErr(...args) { postMessage({op:"log",args}) },' +
|
||||
'postRun:' +
|
||||
this.workerPostRun.toString() +
|
||||
',' +
|
||||
'calcHash:' +
|
||||
this.calcHash.toString() +
|
||||
',' +
|
||||
'wasmMemory:wasmMemory,' +
|
||||
'buffer:wasmMemory.buffer,' +
|
||||
'TOTAL_MEMORY:' +
|
||||
initialMemory * WASM_PAGE_SIZE +
|
||||
'}';
|
||||
const script = argon2LoaderCode.replace(
|
||||
/^var Module.*?}/,
|
||||
memoryDecl + moduleDecl
|
||||
);
|
||||
const blob = new Blob([script], { type: 'application/javascript' });
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const worker = new Worker(objectUrl);
|
||||
const onMessage = (e) => {
|
||||
switch (e.data.op) {
|
||||
case 'log':
|
||||
logger.debug(...e.data.args);
|
||||
break;
|
||||
case 'postRun':
|
||||
logger.debug(
|
||||
'WebAssembly runtime loaded (web worker)',
|
||||
logger.ts(ts)
|
||||
);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
clearTimeout(loadTimeout);
|
||||
worker.removeEventListener('message', onMessage);
|
||||
this.runtimeModule = {
|
||||
hash(args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
worker.postMessage(args);
|
||||
const onHashMessage = (e) => {
|
||||
worker.removeEventListener(
|
||||
'message',
|
||||
onHashMessage
|
||||
);
|
||||
worker.terminate();
|
||||
KdbxwebInit.runtimeModule = null;
|
||||
if (!e.data || e.data.error || !e.data.hash) {
|
||||
const ex =
|
||||
(e.data && e.data.error) ||
|
||||
'unexpected error';
|
||||
logger.error('Worker error', ex);
|
||||
reject(ex);
|
||||
} else {
|
||||
resolve(e.data.hash);
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', onHashMessage);
|
||||
});
|
||||
}
|
||||
};
|
||||
resolve(this.runtimeModule);
|
||||
break;
|
||||
default:
|
||||
logger.error('Unknown message', e.data);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject('Load error');
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', onMessage);
|
||||
} else {
|
||||
// Chrome and Electron crash if we use WASM in WebWorker
|
||||
// see https://github.com/keeweb/keeweb/issues/1263
|
||||
const wasmMemory = new WebAssembly.Memory({
|
||||
initial: initialMemory,
|
||||
maximum: totalMemory
|
||||
});
|
||||
global.Module = {
|
||||
wasmJSMethod: 'native-wasm',
|
||||
wasmBinary: Uint8Array.from(atob(wasmBinaryBase64), (c) => c.charCodeAt(0)),
|
||||
print(...args) {
|
||||
logger.debug(...args);
|
||||
},
|
||||
printErr(...args) {
|
||||
logger.debug(...args);
|
||||
},
|
||||
postRun: () => {
|
||||
logger.debug('WebAssembly runtime loaded (main thread)', logger.ts(ts));
|
||||
const memoryDecl = `var wasmMemory=new WebAssembly.Memory({initial:${initialMemory},maximum:${totalMemory}});`;
|
||||
const moduleDecl =
|
||||
'var Module={' +
|
||||
'wasmJSMethod: "native-wasm",' +
|
||||
'wasmBinary: Uint8Array.from(atob("' +
|
||||
wasmBinaryBase64 +
|
||||
'"), c => c.charCodeAt(0)),' +
|
||||
'print(...args) { postMessage({op:"log",args}) },' +
|
||||
'printErr(...args) { postMessage({op:"log",args}) },' +
|
||||
'postRun:' +
|
||||
this.workerPostRun.toString() +
|
||||
',' +
|
||||
'calcHash:' +
|
||||
this.calcHash.toString() +
|
||||
',' +
|
||||
'wasmMemory:wasmMemory,' +
|
||||
'buffer:wasmMemory.buffer,' +
|
||||
'TOTAL_MEMORY:' +
|
||||
initialMemory * WASM_PAGE_SIZE +
|
||||
'}';
|
||||
const script = argon2LoaderCode.replace(/^var Module.*?}/, memoryDecl + moduleDecl);
|
||||
const blob = new Blob([script], { type: 'application/javascript' });
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const worker = new Worker(objectUrl);
|
||||
const onMessage = (e) => {
|
||||
switch (e.data.op) {
|
||||
case 'log':
|
||||
logger.debug(...e.data.args);
|
||||
break;
|
||||
case 'postRun':
|
||||
logger.debug('WebAssembly runtime loaded (web worker)', logger.ts(ts));
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
clearTimeout(loadTimeout);
|
||||
resolve({
|
||||
hash: (args) => {
|
||||
const hash = this.calcHash(global.Module, args);
|
||||
global.Module.unloadRuntime();
|
||||
global.Module = undefined;
|
||||
return Promise.resolve(hash);
|
||||
worker.removeEventListener('message', onMessage);
|
||||
this.runtimeModule = {
|
||||
hash(args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
worker.postMessage(args);
|
||||
const onHashMessage = (e) => {
|
||||
worker.removeEventListener('message', onHashMessage);
|
||||
worker.terminate();
|
||||
KdbxwebInit.runtimeModule = null;
|
||||
if (!e.data || e.data.error || !e.data.hash) {
|
||||
const ex =
|
||||
(e.data && e.data.error) || 'unexpected error';
|
||||
logger.error('Worker error', ex);
|
||||
reject(ex);
|
||||
} else {
|
||||
resolve(e.data.hash);
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', onHashMessage);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
wasmMemory,
|
||||
buffer: wasmMemory.buffer,
|
||||
TOTAL_MEMORY: initialMemory * WASM_PAGE_SIZE
|
||||
};
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(argon2LoaderCode);
|
||||
}
|
||||
};
|
||||
resolve(this.runtimeModule);
|
||||
break;
|
||||
default:
|
||||
logger.error('Unknown message', e.data);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject('Load error');
|
||||
}
|
||||
};
|
||||
worker.addEventListener('message', onMessage);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
|
|
@ -427,6 +427,7 @@ class AppView extends View {
|
|||
minimizeInsteadOfClose
|
||||
) {
|
||||
Launcher.minimizeApp();
|
||||
this.appMinimized();
|
||||
return Launcher.preventExit(e);
|
||||
}
|
||||
}
|
||||
|
@ -533,7 +534,6 @@ class AppView extends View {
|
|||
saveAndLock(complete) {
|
||||
let pendingCallbacks = 0;
|
||||
const errorFiles = [];
|
||||
const that = this;
|
||||
this.model.files.forEach(function (file) {
|
||||
if (!file.dirty) {
|
||||
return;
|
||||
|
@ -549,7 +549,7 @@ class AppView extends View {
|
|||
errorFiles.push(file.name);
|
||||
}
|
||||
if (--pendingCallbacks === 0) {
|
||||
if (errorFiles.length && that.model.files.hasDirtyFiles()) {
|
||||
if (errorFiles.length && this.model.files.hasDirtyFiles()) {
|
||||
if (!Alerts.alertDisplayed) {
|
||||
const alertBody =
|
||||
errorFiles.length > 1
|
||||
|
@ -564,7 +564,7 @@ class AppView extends View {
|
|||
complete(false);
|
||||
}
|
||||
} else {
|
||||
that.closeAllFilesAndShowFirst();
|
||||
this.closeAllFilesAndShowFirst();
|
||||
if (complete) {
|
||||
complete(true);
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ class AutoTypeSelectView extends View {
|
|||
if (entry.fields.otp) {
|
||||
options.push({
|
||||
value: '{TOTP}',
|
||||
icon: 'clock-o',
|
||||
icon: 'clock',
|
||||
text: Locale.autoTypeSelectionOtp
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Locale } from 'util/locale';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { AppModel } from 'models/app-model';
|
||||
import { FieldViewReadOnly } from 'views/fields/field-view-read-only';
|
||||
import { FieldViewOtp } from 'views/fields/field-view-otp';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { View } from 'framework/views/view';
|
||||
import { Alerts } from 'comp/ui/alerts';
|
||||
import { Keys } from 'const/keys';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
import { Locale } from 'util/locale';
|
||||
import { Copyable } from 'framework/views/copyable';
|
||||
|
|
|
@ -73,6 +73,7 @@ class DetailsView extends View {
|
|||
);
|
||||
this.onKey(Keys.DOM_VK_B, this.copyUserName, KeyHandler.SHORTCUT_ACTION);
|
||||
this.onKey(Keys.DOM_VK_U, this.copyUrl, KeyHandler.SHORTCUT_ACTION);
|
||||
this.onKey(Keys.DOM_VK_2, this.copyOtp, KeyHandler.SHORTCUT_OPT);
|
||||
if (AutoType.enabled) {
|
||||
this.onKey(Keys.DOM_VK_T, () => this.autoType(), KeyHandler.SHORTCUT_ACTION);
|
||||
}
|
||||
|
@ -235,7 +236,7 @@ class DetailsView extends View {
|
|||
if (fieldView.isHidden()) {
|
||||
moreOptions.push({
|
||||
value: 'add:' + fieldView.model.name,
|
||||
icon: 'pencil',
|
||||
icon: 'pencil-alt',
|
||||
text: Locale.detMenuAddField.replace('{}', fieldView.model.title)
|
||||
});
|
||||
}
|
||||
|
@ -262,11 +263,11 @@ class DetailsView extends View {
|
|||
text: Locale.detMenuHideEmpty
|
||||
});
|
||||
}
|
||||
moreOptions.push({ value: 'otp', icon: 'clock-o', text: Locale.detSetupOtp });
|
||||
moreOptions.push({ value: 'otp', icon: 'clock', text: Locale.detSetupOtp });
|
||||
if (AutoType.enabled) {
|
||||
moreOptions.push({
|
||||
value: 'auto-type',
|
||||
icon: 'keyboard-o',
|
||||
icon: 'keyboard',
|
||||
text: Locale.detAutoTypeSettings
|
||||
});
|
||||
}
|
||||
|
@ -456,7 +457,7 @@ class DetailsView extends View {
|
|||
return;
|
||||
}
|
||||
|
||||
this.model.initOtpGenerator();
|
||||
this.model.initOtpGenerator?.();
|
||||
if (this.model.external) {
|
||||
return;
|
||||
}
|
||||
|
@ -823,7 +824,7 @@ class DetailsView extends View {
|
|||
Alerts.yesno({
|
||||
header: Locale.detDelToTrash,
|
||||
body: Locale.detDelToTrashBody,
|
||||
icon: 'trash',
|
||||
icon: 'trash-alt',
|
||||
success: doMove
|
||||
});
|
||||
} else {
|
||||
|
@ -864,19 +865,19 @@ class DetailsView extends View {
|
|||
if (this.model.external) {
|
||||
options.push({
|
||||
value: 'det-copy-otp',
|
||||
icon: 'clipboard',
|
||||
icon: 'copy',
|
||||
text: Locale.detMenuCopyOtp
|
||||
});
|
||||
} else {
|
||||
options.push({
|
||||
value: 'det-copy-password',
|
||||
icon: 'clipboard',
|
||||
icon: 'copy',
|
||||
text: Locale.detMenuCopyPassword
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
value: 'det-copy-user',
|
||||
icon: 'clipboard',
|
||||
icon: 'copy',
|
||||
text: Locale.detMenuCopyUser
|
||||
});
|
||||
}
|
||||
|
@ -886,13 +887,13 @@ class DetailsView extends View {
|
|||
if (canCopy) {
|
||||
options.push({
|
||||
value: 'copy-to-clipboard',
|
||||
icon: 'copy',
|
||||
icon: 'clipboard',
|
||||
text: Locale.detCopyEntryToClipboard
|
||||
});
|
||||
}
|
||||
}
|
||||
if (AutoType.enabled) {
|
||||
options.push({ value: 'det-auto-type', icon: 'keyboard-o', text: Locale.detAutoType });
|
||||
options.push({ value: 'det-auto-type', icon: 'keyboard', text: Locale.detAutoType });
|
||||
}
|
||||
Events.emit('show-context-menu', Object.assign(e, { options }));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Pikaday from 'pikaday';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { Locale } from 'util/locale';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { FieldViewText } from 'views/fields/field-view-text';
|
||||
|
||||
class FieldViewDate extends FieldViewText {
|
||||
|
@ -30,9 +29,9 @@ class FieldViewDate extends FieldViewText {
|
|||
i18n: {
|
||||
previousMonth: '',
|
||||
nextMonth: '',
|
||||
months: Locale.months,
|
||||
weekdays: Locale.weekdays,
|
||||
weekdaysShort: Locale.weekdaysShort
|
||||
months: DateFormat.months(),
|
||||
weekdays: DateFormat.weekDays(),
|
||||
weekdaysShort: DateFormat.shortWeekDays()
|
||||
}
|
||||
});
|
||||
this.picker.adjustPosition = this.adjustPickerPosition.bind(this);
|
||||
|
|
|
@ -61,7 +61,7 @@ class FieldViewText extends FieldView {
|
|||
Events.on('click', fieldValueBlurBound);
|
||||
this.stopBlurListener = () => Events.off('click', fieldValueBlurBound);
|
||||
this.listenTo(Events, 'main-window-will-close', this.externalEndEdit);
|
||||
this.listenTo(Events, 'user-idle', this.externalEndEdit);
|
||||
this.listenTo(Events, 'before-user-idle', this.externalEndEdit);
|
||||
if (this.model.multiline) {
|
||||
this.setInputHeight();
|
||||
}
|
||||
|
|
|
@ -180,6 +180,8 @@ class FieldView extends View {
|
|||
textEqual = this.value.equals(newVal);
|
||||
} else if (newVal && newVal.isProtected) {
|
||||
textEqual = newVal.equals(this.value);
|
||||
} else if (newVal instanceof Date && this.value instanceof Date) {
|
||||
textEqual = newVal.toDateString() === this.value.toDateString();
|
||||
} else {
|
||||
textEqual = isEqual(this.value, newVal);
|
||||
}
|
||||
|
@ -236,7 +238,7 @@ class FieldView extends View {
|
|||
}
|
||||
|
||||
if (AutoType.enabled && this.model.sequence) {
|
||||
options.push({ value: 'autotype', icon: 'keyboard-o', text: Locale.detAutoTypeField });
|
||||
options.push({ value: 'autotype', icon: 'keyboard', text: Locale.detAutoTypeField });
|
||||
}
|
||||
|
||||
const rect = this.$el[0].getBoundingClientRect();
|
||||
|
@ -321,9 +323,9 @@ class FieldView extends View {
|
|||
|
||||
const actions = [];
|
||||
if (this.value) {
|
||||
actions.push({ name: 'copy', icon: 'clipboard' });
|
||||
actions.push({ name: 'copy', icon: 'copy' });
|
||||
}
|
||||
actions.push({ name: 'edit', icon: 'pencil' });
|
||||
actions.push({ name: 'edit', icon: 'pencil-alt' });
|
||||
if (this.value instanceof kdbxweb.ProtectedValue) {
|
||||
actions.push({ name: 'reveal', icon: 'eye' });
|
||||
}
|
||||
|
|
|
@ -51,9 +51,10 @@ class IconSelectView extends View {
|
|||
return;
|
||||
}
|
||||
this.downloadingFavicon = true;
|
||||
this.$el.find('.icon-select__icon-download>i').addClass('fa-spinner fa-spin');
|
||||
this.$el.find('.icon-select__icon-download>i').addClass('spin');
|
||||
this.$el
|
||||
.find('.icon-select__icon-download')
|
||||
.addClass('icon-select__icon--progress')
|
||||
.removeClass('icon-select__icon--download-error');
|
||||
const url = this.getIconUrl(true);
|
||||
const img = document.createElement('img');
|
||||
|
@ -62,19 +63,20 @@ class IconSelectView extends View {
|
|||
img.onload = () => {
|
||||
this.setSpecialImage(img, 'download');
|
||||
this.$el.find('.icon-select__icon-download img').remove();
|
||||
this.$el.find('.icon-select__icon-download>i').removeClass('fa-spinner fa-spin');
|
||||
this.$el.find('.icon-select__icon-download>i').removeClass('spin');
|
||||
this.$el
|
||||
.find('.icon-select__icon-download')
|
||||
.removeClass('icon-select__icon--progress')
|
||||
.addClass('icon-select__icon--custom-selected')
|
||||
.append(img);
|
||||
this.downloadingFavicon = false;
|
||||
};
|
||||
img.onerror = (e) => {
|
||||
logger.error('Favicon download error: ' + url, e);
|
||||
this.$el.find('.icon-select__icon-download>i').removeClass('fa-spinner fa-spin');
|
||||
this.$el.find('.icon-select__icon-download>i').removeClass('spin');
|
||||
this.$el
|
||||
.find('.icon-select__icon-download')
|
||||
.removeClass('icon-select__icon--custom-selected')
|
||||
.removeClass('icon-select__icon--custom-selected icon-select__icon--progress')
|
||||
.addClass('icon-select__icon--download-error');
|
||||
this.downloadingFavicon = false;
|
||||
};
|
||||
|
|
|
@ -39,64 +39,64 @@ class ListSearchView extends View {
|
|||
this.sortOptions = [
|
||||
{
|
||||
value: 'title',
|
||||
icon: 'sort-alpha-asc',
|
||||
icon: 'sort-alpha-down',
|
||||
loc: () =>
|
||||
StringFormat.capFirst(Locale.title) + ' ' + this.addArrow(Locale.searchAZ)
|
||||
},
|
||||
{
|
||||
value: '-title',
|
||||
icon: 'sort-alpha-desc',
|
||||
icon: 'sort-alpha-down-alt',
|
||||
loc: () =>
|
||||
StringFormat.capFirst(Locale.title) + ' ' + this.addArrow(Locale.searchZA)
|
||||
},
|
||||
{
|
||||
value: 'website',
|
||||
icon: 'sort-alpha-asc',
|
||||
icon: 'sort-alpha-down',
|
||||
loc: () =>
|
||||
StringFormat.capFirst(Locale.website) + ' ' + this.addArrow(Locale.searchAZ)
|
||||
},
|
||||
{
|
||||
value: '-website',
|
||||
icon: 'sort-alpha-desc',
|
||||
icon: 'sort-alpha-down-alt',
|
||||
loc: () =>
|
||||
StringFormat.capFirst(Locale.website) + ' ' + this.addArrow(Locale.searchZA)
|
||||
},
|
||||
{
|
||||
value: 'user',
|
||||
icon: 'sort-alpha-asc',
|
||||
icon: 'sort-alpha-down',
|
||||
loc: () => StringFormat.capFirst(Locale.user) + ' ' + this.addArrow(Locale.searchAZ)
|
||||
},
|
||||
{
|
||||
value: '-user',
|
||||
icon: 'sort-alpha-desc',
|
||||
icon: 'sort-alpha-down-alt',
|
||||
loc: () => StringFormat.capFirst(Locale.user) + ' ' + this.addArrow(Locale.searchZA)
|
||||
},
|
||||
{
|
||||
value: 'created',
|
||||
icon: 'sort-numeric-asc',
|
||||
icon: 'sort-numeric-down',
|
||||
loc: () => Locale.searchCreated + ' ' + this.addArrow(Locale.searchON)
|
||||
},
|
||||
{
|
||||
value: '-created',
|
||||
icon: 'sort-numeric-desc',
|
||||
icon: 'sort-numeric-down-alt',
|
||||
loc: () => Locale.searchCreated + ' ' + this.addArrow(Locale.searchNO)
|
||||
},
|
||||
{
|
||||
value: 'updated',
|
||||
icon: 'sort-numeric-asc',
|
||||
icon: 'sort-numeric-down',
|
||||
loc: () => Locale.searchUpdated + ' ' + this.addArrow(Locale.searchON)
|
||||
},
|
||||
{
|
||||
value: '-updated',
|
||||
icon: 'sort-numeric-desc',
|
||||
icon: 'sort-numeric-down-alt',
|
||||
loc: () => Locale.searchUpdated + ' ' + this.addArrow(Locale.searchNO)
|
||||
},
|
||||
{
|
||||
value: '-attachments',
|
||||
icon: 'sort-amount-desc',
|
||||
icon: 'sort-amount-down',
|
||||
loc: () => Locale.searchAttachments
|
||||
},
|
||||
{ value: '-rank', icon: 'sort-amount-desc', loc: () => Locale.searchRank }
|
||||
{ value: '-rank', icon: 'sort-amount-down', loc: () => Locale.searchRank }
|
||||
];
|
||||
this.sortIcons = {};
|
||||
this.sortOptions.forEach((opt) => {
|
||||
|
|
|
@ -37,6 +37,7 @@ class MenuItemView extends View {
|
|||
this.listenTo(this.model, 'change:active', this.changeActive);
|
||||
this.listenTo(this.model, 'change:expanded', this.changeExpanded);
|
||||
this.listenTo(this.model, 'change:cls', this.changeCls);
|
||||
this.listenTo(this.model, 'change:iconCls', this.changeIconCls);
|
||||
this.listenTo(this.model, 'delete', this.remove);
|
||||
this.listenTo(this.model, 'insert', this.insertItem);
|
||||
const shortcut = this.model.shortcut;
|
||||
|
@ -108,6 +109,16 @@ class MenuItemView extends View {
|
|||
this.$el.addClass(cls);
|
||||
}
|
||||
|
||||
changeIconCls(model, cls, oldCls) {
|
||||
const iconEl = this.el.querySelector('.menu__item-icon');
|
||||
if (oldCls) {
|
||||
iconEl.classList.remove(oldCls);
|
||||
}
|
||||
if (cls) {
|
||||
iconEl.classList.add(cls);
|
||||
}
|
||||
}
|
||||
|
||||
mouseover(e) {
|
||||
if (!e.button) {
|
||||
this.$el.addClass('menu__item--hover');
|
||||
|
|
|
@ -20,7 +20,6 @@ class MenuView extends View {
|
|||
|
||||
minWidth = 130;
|
||||
maxWidth = 300;
|
||||
autoWidth = 150;
|
||||
|
||||
constructor(model, options) {
|
||||
super(model, options);
|
||||
|
|
|
@ -162,20 +162,16 @@ class OpenView extends View {
|
|||
|
||||
getLastOpenFiles() {
|
||||
return this.model.fileInfos.map((fileInfo) => {
|
||||
let icon = 'file-text';
|
||||
let icon = 'file-alt';
|
||||
const storage = Storage[fileInfo.storage];
|
||||
if (storage && storage.icon) {
|
||||
icon = storage.icon;
|
||||
}
|
||||
if (storage && storage.iconSvg) {
|
||||
icon = null;
|
||||
}
|
||||
return {
|
||||
id: fileInfo.id,
|
||||
name: fileInfo.name,
|
||||
path: this.getDisplayedPath(fileInfo),
|
||||
icon,
|
||||
iconSvg: storage ? storage.iconSvg : undefined
|
||||
icon
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -691,7 +687,7 @@ class OpenView extends View {
|
|||
Alerts.error({
|
||||
header: Locale.openError,
|
||||
body: Locale.openErrorDescription,
|
||||
pre: err.toString()
|
||||
pre: this.errorToString(err)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -793,16 +789,12 @@ class OpenView extends View {
|
|||
dir: true
|
||||
});
|
||||
}
|
||||
const listView = new StorageFileListView({
|
||||
files,
|
||||
showHiddenFiles: config && config.showHiddenFiles
|
||||
});
|
||||
const listView = new StorageFileListView({ files });
|
||||
listView.on('selected', (file) => {
|
||||
if (file.dir) {
|
||||
this.listStorage(storage, {
|
||||
dir: file.path,
|
||||
prevDir: (config && config.dir) || '',
|
||||
showHiddenFiles: true
|
||||
prevDir: (config && config.dir) || ''
|
||||
});
|
||||
} else {
|
||||
this.openStorageFile(storage, file);
|
||||
|
@ -811,7 +803,7 @@ class OpenView extends View {
|
|||
Alerts.alert({
|
||||
header: Locale.openSelectFile,
|
||||
body: Locale.openSelectFileBody,
|
||||
icon: storage.icon || 'files-o',
|
||||
icon: storage.icon || 'file-alt',
|
||||
buttons: [{ result: '', title: Locale.alertCancel }],
|
||||
esc: '',
|
||||
click: '',
|
||||
|
@ -1031,7 +1023,7 @@ class OpenView extends View {
|
|||
Alerts.error({
|
||||
header: Locale.openError,
|
||||
body: Locale.openErrorDescription,
|
||||
pre: err.toString()
|
||||
pre: this.errorToString(err)
|
||||
});
|
||||
}
|
||||
this.otpDevice = null;
|
||||
|
@ -1068,13 +1060,24 @@ class OpenView extends View {
|
|||
|
||||
Alerts.alert({
|
||||
header: Locale.openChalRespHeader,
|
||||
iconSvg: 'usb-token',
|
||||
icon: 'usb-token',
|
||||
buttons: [{ result: '', title: Locale.alertCancel }],
|
||||
esc: '',
|
||||
click: '',
|
||||
view: chalRespView
|
||||
});
|
||||
}
|
||||
|
||||
errorToString(err) {
|
||||
const str = err.toString();
|
||||
if (str !== {}.toString()) {
|
||||
return str;
|
||||
}
|
||||
if (err.ykError && err.code) {
|
||||
return Locale.yubiKeyErrorWithCode.replace('{}', err.code);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export { OpenView };
|
||||
|
|
|
@ -12,6 +12,7 @@ class SettingsAboutView extends View {
|
|||
version: RuntimeInfo.version,
|
||||
licenseLink: Links.License,
|
||||
licenseLinkApache: Links.LicenseApache,
|
||||
licenseLinkCCBY40: Links.LicenseLinkCCBY40,
|
||||
repoLink: Links.Repo,
|
||||
donationLink: Links.Donation,
|
||||
isDesktop: Features.isDesktop
|
||||
|
|
|
@ -8,7 +8,7 @@ import { YubiKey } from 'comp/app/yubikey';
|
|||
import { UsbListener } from 'comp/app/usb-listener';
|
||||
import { Links } from 'const/links';
|
||||
import { AppSettingsModel } from 'models/app-settings-model';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { UrlFormat } from 'util/formatting/url-format';
|
||||
import { PasswordPresenter } from 'util/formatting/password-presenter';
|
||||
import { Locale } from 'util/locale';
|
||||
|
@ -85,7 +85,6 @@ class SettingsFileView extends View {
|
|||
storageProviders.push({
|
||||
name: prv.name,
|
||||
icon: prv.icon,
|
||||
iconSvg: prv.iconSvg,
|
||||
own: name === fileStorage,
|
||||
backup: prv.backup
|
||||
});
|
||||
|
@ -337,7 +336,7 @@ class SettingsFileView extends View {
|
|||
Alerts.alert({
|
||||
header: '',
|
||||
body: '',
|
||||
icon: storage.icon || 'files-o',
|
||||
icon: storage.icon || 'file-alt',
|
||||
buttons: [Alerts.buttons.ok, Alerts.buttons.cancel],
|
||||
esc: '',
|
||||
opaque: true,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { AppSettingsModel } from 'models/app-settings-model';
|
|||
import { UpdateModel } from 'models/update-model';
|
||||
import { SemVer } from 'util/data/semver';
|
||||
import { Features } from 'util/features';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { Locale } from 'util/locale';
|
||||
import { SettingsLogsView } from 'views/settings/settings-logs-view';
|
||||
import { SettingsPrvView } from 'views/settings/settings-prv-view';
|
||||
|
@ -23,7 +23,7 @@ class SettingsGeneralView extends View {
|
|||
template = template;
|
||||
|
||||
events = {
|
||||
'change .settings__general-theme': 'changeTheme',
|
||||
'click .settings__general-theme': 'changeTheme',
|
||||
'change .settings__general-locale': 'changeLocale',
|
||||
'change .settings__general-font-size': 'changeFontSize',
|
||||
'change .settings__general-expand': 'changeExpandGroups',
|
||||
|
@ -205,20 +205,29 @@ class SettingsGeneralView extends View {
|
|||
}
|
||||
|
||||
changeTheme(e) {
|
||||
const theme = e.target.value;
|
||||
AppSettingsModel.theme = theme;
|
||||
const theme = e.target.closest('.settings__general-theme').dataset.theme;
|
||||
if (theme === '...') {
|
||||
this.goToPlugins();
|
||||
} else {
|
||||
AppSettingsModel.theme = theme;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
changeLocale(e) {
|
||||
const locale = e.target.value;
|
||||
if (locale === '...') {
|
||||
e.target.value = AppSettingsModel.locale || 'en';
|
||||
this.appModel.menu.select({
|
||||
item: this.appModel.menu.pluginsSection.items[0]
|
||||
});
|
||||
return;
|
||||
e.target.value = AppSettingsModel.locale || 'en-US';
|
||||
this.goToPlugins();
|
||||
} else {
|
||||
AppSettingsModel.locale = locale;
|
||||
}
|
||||
AppSettingsModel.locale = locale;
|
||||
}
|
||||
|
||||
goToPlugins() {
|
||||
this.appModel.menu.select({
|
||||
item: this.appModel.menu.pluginsSection.items[0]
|
||||
});
|
||||
}
|
||||
|
||||
changeFontSize(e) {
|
||||
|
|
|
@ -26,7 +26,7 @@ class SettingsHelpView extends View {
|
|||
issueLink:
|
||||
Links.Repo +
|
||||
'/issues/new?body=' +
|
||||
encodeURIComponent('!please describe your issue here!\n\n' + appInfo),
|
||||
encodeURIComponent('# please describe your issue here\n\n' + appInfo),
|
||||
desktopLink: Links.Desktop,
|
||||
webAppLink: Links.WebApp,
|
||||
appInfo
|
||||
|
|
|
@ -6,10 +6,12 @@ import template from 'templates/settings/settings-logs-view.hbs';
|
|||
class SettingsLogsView extends View {
|
||||
parent = '.settings__general-advanced';
|
||||
template = template;
|
||||
levelToColor = { debug: 'muted', warn: 'yellow', error: 'red' };
|
||||
|
||||
render() {
|
||||
const logs = Logger.getLast().map((item) => ({
|
||||
level: item.level,
|
||||
color: this.levelToColor[item.level],
|
||||
msg:
|
||||
'[' +
|
||||
StringFormat.padStr(item.level.toUpperCase(), 5) +
|
||||
|
|
|
@ -10,7 +10,7 @@ import { PluginManager } from 'plugins/plugin-manager';
|
|||
import { Comparators } from 'util/data/comparators';
|
||||
import { SemVer } from 'util/data/semver';
|
||||
import { Features } from 'util/features';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { DateFormat } from 'comp/i18n/date-format';
|
||||
import { Locale } from 'util/locale';
|
||||
import template from 'templates/settings/settings-plugins.hbs';
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ class SettingsPrvView extends View {
|
|||
|
||||
events = {
|
||||
'change .settings__general-prv-field-sel': 'changeField',
|
||||
'input .settings__general-prv-field-txt': 'changeField'
|
||||
'input .settings__general-prv-field-txt': 'changeField',
|
||||
'change .settings__general-prv-field-check': 'changeCheckbox'
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -29,6 +30,13 @@ class SettingsPrvView extends View {
|
|||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
changeCheckbox(e) {
|
||||
const id = e.target.dataset.id;
|
||||
const value = !!e.target.checked;
|
||||
const storage = Storage[this.model.name];
|
||||
storage.applySetting(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
export { SettingsPrvView };
|
||||
|
|
|
@ -32,7 +32,7 @@ class SettingsView extends View {
|
|||
}
|
||||
|
||||
setPage(e) {
|
||||
let { page, file } = e;
|
||||
let { page, section, file } = e;
|
||||
if (page === 'file' && file && file.external) {
|
||||
page = 'file-external';
|
||||
}
|
||||
|
@ -48,6 +48,20 @@ class SettingsView extends View {
|
|||
this.file = file;
|
||||
this.page = page;
|
||||
this.pageResized();
|
||||
this.scrollToSection(section);
|
||||
}
|
||||
|
||||
scrollToSection(section) {
|
||||
let scrollEl;
|
||||
if (section) {
|
||||
scrollEl = this.views.page.el.querySelector(`#${section}`);
|
||||
}
|
||||
if (!scrollEl) {
|
||||
scrollEl = this.views.page.el.querySelector(`h1`);
|
||||
}
|
||||
if (scrollEl) {
|
||||
scrollEl.scrollIntoView(true);
|
||||
}
|
||||
}
|
||||
|
||||
returnToApp() {
|
||||
|
|
|
@ -14,7 +14,7 @@ class StorageFileListView extends View {
|
|||
constructor(model) {
|
||||
super(model);
|
||||
this.allStorageFiles = {};
|
||||
this.showHiddenFiles = !!this.model.showHiddenFiles;
|
||||
this.showHiddenFiles = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -27,13 +27,11 @@ class StorageFileListView extends View {
|
|||
dir: file.dir
|
||||
};
|
||||
});
|
||||
const visibleFiles = files.filter((f) => !f.dir && f.kdbx);
|
||||
const visibleFiles = files.filter((f) => f.dir || f.kdbx);
|
||||
const canShowHiddenFiles = visibleFiles.length && files.length > visibleFiles.length;
|
||||
if (!this.showHiddenFiles) {
|
||||
if (visibleFiles.length > 0) {
|
||||
files = visibleFiles;
|
||||
} else {
|
||||
this.showHiddenFiles = true;
|
||||
}
|
||||
}
|
||||
const density = files.length > 14 ? 3 : files.length > 7 ? 2 : 1;
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
&__menu {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
width: 150px;
|
||||
width: 15em;
|
||||
background-color: var(--secondary-background-color);
|
||||
@include mobile {
|
||||
&:not(.menu-visible) {
|
||||
display: none;
|
||||
|
@ -92,11 +93,14 @@
|
|||
}
|
||||
|
||||
&__list {
|
||||
flex: 0 0 250px;
|
||||
flex: 0 0 25em;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
.titlebar-hidden & {
|
||||
padding-top: $titlebar-padding-tiny;
|
||||
}
|
||||
@include mobile {
|
||||
flex: 1 1;
|
||||
.app--details-visible & {
|
||||
|
@ -134,6 +138,7 @@
|
|||
&__footer {
|
||||
flex: 0 0 auto;
|
||||
border-top: light-border();
|
||||
background-color: var(--secondary-background-color);
|
||||
}
|
||||
|
||||
&__beta {
|
||||
|
@ -141,5 +146,8 @@
|
|||
text-align: center;
|
||||
background-color: var(--error-color);
|
||||
color: var(--text-contrast-error-color);
|
||||
> .fa {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
box-sizing: border-box;
|
||||
z-index: $z-index-no-modal;
|
||||
opacity: 1;
|
||||
padding: $base-padding;
|
||||
padding: $medium-padding;
|
||||
|
||||
.titlebar-hidden & {
|
||||
padding-top: $titlebar-padding-small;
|
||||
|
@ -108,10 +108,10 @@
|
|||
}
|
||||
}
|
||||
&__item {
|
||||
@include area-selectable(right);
|
||||
@include area-selectable();
|
||||
&--active,
|
||||
&--active:hover {
|
||||
@include area-selected(right);
|
||||
@include area-selected();
|
||||
cursor: pointer;
|
||||
}
|
||||
&--active {
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
height: $mobile-back-button-height;
|
||||
font-size: 1.2em;
|
||||
> i {
|
||||
margin-right: 0.3em;
|
||||
margin-right: 0.7em;
|
||||
font-size: 1.2em;
|
||||
vertical-align: text-bottom;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -0.15em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,12 +40,10 @@
|
|||
padding: 3px 6px 1px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-radius: $base-border-radius;
|
||||
border-radius: var(--input-border-radius);
|
||||
border: 1px solid transparent;
|
||||
height: 36px;
|
||||
line-height: 34px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
height: 1.4em;
|
||||
line-height: 1.4em;
|
||||
white-space: nowrap;
|
||||
&:hover {
|
||||
transition: border-color $base-duration $base-timing;
|
||||
|
@ -54,34 +54,38 @@
|
|||
}
|
||||
}
|
||||
input.details__header-title-input {
|
||||
height: 42px;
|
||||
line-height: 34px;
|
||||
height: calc(1.4em + 6px);
|
||||
line-height: 1.4em;
|
||||
user-select: text;
|
||||
flex: 1;
|
||||
margin: 0 6px;
|
||||
padding: 0 6px;
|
||||
font-size: $large-header-font-size;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
min-width: 0;
|
||||
@include mobile {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&-color,
|
||||
&-icon {
|
||||
&-color {
|
||||
user-select: none;
|
||||
@include area-selectable();
|
||||
display: inline;
|
||||
font-size: $large-header-font-size;
|
||||
height: 1em;
|
||||
padding-top: 0.1em;
|
||||
}
|
||||
&-icon {
|
||||
width: 1.4em;
|
||||
@include area-selectable();
|
||||
user-select: none;
|
||||
display: inline;
|
||||
font-size: $large-header-font-size;
|
||||
padding-top: 0.1em;
|
||||
border-radius: var(--block-border-radius);
|
||||
width: 1.8em;
|
||||
height: 1.5em;
|
||||
text-align: center;
|
||||
}
|
||||
&-icon-img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&__colors-popup {
|
||||
|
@ -92,8 +96,7 @@
|
|||
border-radius: $base-border-radius;
|
||||
background: var(--background-color);
|
||||
box-shadow: 0 0 3px var(--background-color);
|
||||
top: 13px;
|
||||
left: 6px;
|
||||
left: 0.2em;
|
||||
font-size: $large-header-font-size;
|
||||
&:hover,
|
||||
.details__header-color:hover & {
|
||||
|
@ -102,7 +105,7 @@
|
|||
}
|
||||
}
|
||||
&-item {
|
||||
padding: 8px 12px;
|
||||
padding: 0 12px 0;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
@ -116,8 +119,7 @@
|
|||
content: $fa-var-bookmark;
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 8px;
|
||||
left: 0.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +232,7 @@
|
|||
color: transparent;
|
||||
}
|
||||
.details__field--editable & {
|
||||
border-radius: $base-border-radius;
|
||||
border-radius: var(--input-border-radius);
|
||||
&:hover {
|
||||
@include nomobile {
|
||||
transition: border-color $base-duration $base-timing;
|
||||
|
@ -265,7 +267,7 @@
|
|||
> textarea {
|
||||
margin: 0;
|
||||
padding: 0 $base-padding-h;
|
||||
line-height: $details-field-line-height + 2px;
|
||||
line-height: $details-field-line-height;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
.details__field--protected & {
|
||||
|
@ -319,7 +321,7 @@
|
|||
flex: 0 0 auto;
|
||||
}
|
||||
&-btn {
|
||||
@include position(absolute, 0 0 null null);
|
||||
@include position(absolute, -0.2em 0 null null);
|
||||
color: var(--muted-color);
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
|
@ -327,7 +329,7 @@
|
|||
}
|
||||
&:before {
|
||||
@include position(absolute, 0 0 null null);
|
||||
@include fa-icon();
|
||||
@include fa-icon;
|
||||
cursor: pointer;
|
||||
padding: 0.3em $base-padding-h;
|
||||
}
|
||||
|
@ -445,7 +447,7 @@
|
|||
align-self: flex-start;
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
padding-top: 0.25em;
|
||||
padding-top: 0.1em;
|
||||
.details__field:hover & {
|
||||
opacity: 1;
|
||||
transition: opacity $slow-transition-in;
|
||||
|
@ -483,36 +485,26 @@
|
|||
}
|
||||
|
||||
&__attachment {
|
||||
user-select: none;
|
||||
@include area-selectable();
|
||||
@include bg-btn();
|
||||
align-self: flex-end;
|
||||
flex: 0 1 auto;
|
||||
border: light-border();
|
||||
margin-right: $small-spacing;
|
||||
margin-top: $small-spacing;
|
||||
padding: $base-padding;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
i {
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
&--active {
|
||||
border-bottom: 1px solid var(--action-color);
|
||||
&--active,
|
||||
&--active:hover {
|
||||
@include bg-btn-active();
|
||||
}
|
||||
}
|
||||
|
||||
&__attachment-add {
|
||||
@include icon-btn();
|
||||
user-select: none;
|
||||
align-self: flex-end;
|
||||
flex: 0 0 auto;
|
||||
color: var(--muted-color);
|
||||
border: 1px solid transparent;
|
||||
margin-right: $small-spacing;
|
||||
padding: $base-padding;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
transition: color $base-duration $base-timing;
|
||||
&:hover {
|
||||
color: var(--medium-color);
|
||||
}
|
||||
|
@ -640,9 +632,8 @@
|
|||
}
|
||||
&-item {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
transform: translateX(-48%);
|
||||
&:hover {
|
||||
color: var(--text-semi-muted-color);
|
||||
}
|
||||
|
@ -721,10 +712,9 @@
|
|||
padding: $base-padding;
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
@include area-selectable(bottom);
|
||||
@include area-selectable();
|
||||
&--selected {
|
||||
background-color: var(--secondary-background-color);
|
||||
border-bottom: selected-hover-border();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -734,6 +724,12 @@
|
|||
float: right;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
&-post {
|
||||
margin-left: $tiny-spacing;
|
||||
margin-right: $small-spacing;
|
||||
position: relative;
|
||||
top: 0.2em;
|
||||
}
|
||||
&-pre,
|
||||
&-post {
|
||||
display: none;
|
||||
|
@ -744,9 +740,11 @@
|
|||
padding: $base-padding;
|
||||
font-size: 1.2em;
|
||||
> i {
|
||||
margin-right: 0.3em;
|
||||
margin-right: 0.7em;
|
||||
font-size: 1.2em;
|
||||
vertical-align: text-bottom;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -0.15em;
|
||||
}
|
||||
&-pre {
|
||||
display: inline;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
&__db {
|
||||
flex: 0 0 auto;
|
||||
@include area-selectable(top);
|
||||
@include area-selectable-on-secondary();
|
||||
position: relative;
|
||||
padding: $medium-padding;
|
||||
padding-right: 1.3em;
|
||||
|
@ -22,6 +22,9 @@
|
|||
color: var(--medium-color);
|
||||
}
|
||||
}
|
||||
&:first-of-type {
|
||||
padding-left: $base-spacing;
|
||||
}
|
||||
|
||||
&--expanded {
|
||||
flex: 1;
|
||||
|
@ -64,7 +67,7 @@
|
|||
|
||||
&__btn {
|
||||
flex: 0 0 auto;
|
||||
@include area-selectable(top);
|
||||
@include area-selectable-on-secondary();
|
||||
padding: $base-padding;
|
||||
.standalone & {
|
||||
padding-top: $base-padding-v;
|
||||
|
@ -73,6 +76,9 @@
|
|||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
width: 1em;
|
||||
&:last-of-type {
|
||||
padding-right: $base-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
&__update-icon {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
float: right;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
& ~ .gen__top-btn {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
@ -45,6 +44,11 @@
|
|||
}
|
||||
}
|
||||
&__check-hide {
|
||||
&-label {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
& + label.gen__check-hide-label:before {
|
||||
@include fa-icon;
|
||||
content: $fa-var-eye;
|
||||
|
@ -54,6 +58,9 @@
|
|||
content: $fa-var-eye-slash;
|
||||
color: inherit;
|
||||
}
|
||||
&:not([disabled]) + label.gen__check-hide-label:hover:before {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
&__btn-wrap {
|
||||
text-align: center;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
&__content,
|
||||
&__buttons {
|
||||
padding: $base-padding;
|
||||
padding: $medium-padding;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
}
|
||||
&-field {
|
||||
width: 100%;
|
||||
height: 2.5em;
|
||||
@include mobile {
|
||||
font-size: 1.05em !important;
|
||||
box-shadow: none !important;
|
||||
|
@ -71,11 +72,12 @@
|
|||
top: 0.5em;
|
||||
}
|
||||
}
|
||||
&-btn-new {
|
||||
@include icon-btn;
|
||||
}
|
||||
&-btn-new,
|
||||
&-btn-sort {
|
||||
@include icon-btn;
|
||||
height: 2.5em;
|
||||
line-height: 2.3em;
|
||||
padding: 0 $base-padding-h;
|
||||
}
|
||||
&-btn-menu {
|
||||
display: none;
|
||||
|
@ -98,6 +100,7 @@
|
|||
flex-wrap: wrap;
|
||||
&-text {
|
||||
flex: 100%;
|
||||
padding: $base-padding-v 0;
|
||||
}
|
||||
}
|
||||
&-check {
|
||||
|
@ -127,15 +130,15 @@
|
|||
}
|
||||
|
||||
&__item {
|
||||
padding: 6px 10px 0;
|
||||
padding: $base-padding;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
@include nomobile {
|
||||
@include area-selectable(right);
|
||||
@include area-selectable();
|
||||
&--active,
|
||||
&--active:hover {
|
||||
@include area-selected(right);
|
||||
@include area-selected();
|
||||
}
|
||||
}
|
||||
@include mobile {
|
||||
|
@ -147,7 +150,8 @@
|
|||
}
|
||||
|
||||
&:not(.list__item--table) {
|
||||
height: 3rem;
|
||||
border-radius: var(--block-border-radius);
|
||||
margin: 0 $small-spacing;
|
||||
}
|
||||
|
||||
&--expired {
|
||||
|
@ -158,9 +162,12 @@
|
|||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: 2px;
|
||||
margin-right: 0.2em;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
@include mobile {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
@ -197,6 +204,10 @@
|
|||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin-bottom: $tiny-spacing;
|
||||
.list__item--active & {
|
||||
color: var(--selected-item-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
@include scrollbar-on-hover;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: $small-spacing 0;
|
||||
|
||||
&--grow {
|
||||
flex: 1;
|
||||
|
@ -65,25 +66,23 @@
|
|||
display: block;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
@include position(absolute, 50% null null 1em);
|
||||
@include position(absolute, 50% null null 1.6em);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
@include area-selectable();
|
||||
@include area-selectable-on-secondary();
|
||||
padding: $base-padding;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
border-right: selected-transparent-border();
|
||||
.menu__item--hover > & {
|
||||
border-right: selected-hover-border();
|
||||
}
|
||||
border-radius: var(--block-border-radius);
|
||||
margin: 0 $small-spacing;
|
||||
|
||||
.menu__item--active > &,
|
||||
.menu__item--active.menu__item--hover > & {
|
||||
@include area-selected(right);
|
||||
@include area-selected-on-secondary();
|
||||
}
|
||||
|
||||
.menu__item > .menu__item > & {
|
||||
|
@ -127,6 +126,8 @@
|
|||
|
||||
&-icon {
|
||||
width: 0.8em;
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
&--image {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
|
@ -160,11 +161,11 @@
|
|||
display: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 1.1em;
|
||||
top: 0.75em;
|
||||
right: 1.2em;
|
||||
top: 0.55em;
|
||||
cursor: pointer;
|
||||
transition: opacity $base-duration $base-timing, color $base-duration $base-timing;
|
||||
color: var(--muted-color);
|
||||
color: var(--clickable-on-secondary-color);
|
||||
&:hover {
|
||||
color: var(--medium-color);
|
||||
}
|
||||
|
|
|
@ -24,20 +24,19 @@
|
|||
}
|
||||
|
||||
&__icon {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 20px;
|
||||
transition: color $base-duration $base-timing;
|
||||
@include icon-btn();
|
||||
color: var(--open-icon-color);
|
||||
padding-top: 0;
|
||||
margin: 0.2em 0.5em;
|
||||
&:hover {
|
||||
color: var(--medium-color);
|
||||
}
|
||||
&:focus {
|
||||
.open--show-focus & {
|
||||
outline: focused-outline();
|
||||
box-shadow: focused-box-shadow();
|
||||
}
|
||||
}
|
||||
&-i,
|
||||
&-svg {
|
||||
&-i {
|
||||
font-size: 4em;
|
||||
}
|
||||
&-text {
|
||||
|
@ -46,29 +45,6 @@
|
|||
color: var(--medium-color);
|
||||
}
|
||||
}
|
||||
&-svg {
|
||||
line-height: 0;
|
||||
> svg {
|
||||
@include size(1em);
|
||||
}
|
||||
}
|
||||
@include mobile() {
|
||||
&-i,
|
||||
&-svg {
|
||||
font-size: 4.6em;
|
||||
}
|
||||
&-text {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.open__icons--lower & {
|
||||
margin: 14px;
|
||||
&-i,
|
||||
&-svg {
|
||||
font-size: 4.2em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__pass {
|
||||
|
@ -100,22 +76,26 @@
|
|||
padding: 0.6em $base-spacing;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
border-radius: var(--block-border-radius);
|
||||
@include mobile {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
color: var(--muted-color);
|
||||
line-height: 3em;
|
||||
height: 3.2em;
|
||||
> i {
|
||||
font-size: 3em;
|
||||
line-height: 1;
|
||||
@include mobile {
|
||||
line-height: 0.8;
|
||||
line-height: 1.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.open--show-focus & {
|
||||
&-enter-btn:focus,
|
||||
&-opening-icon:focus {
|
||||
outline: focused-outline();
|
||||
box-shadow: focused-box-shadow();
|
||||
}
|
||||
}
|
||||
&-enter-btn {
|
||||
|
@ -204,6 +184,7 @@
|
|||
padding-left: $base-padding-h;
|
||||
height: 2em;
|
||||
&-key-file {
|
||||
border-radius: var(--block-border-radius);
|
||||
.open--file:not(.open--opening) & {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -235,7 +216,7 @@
|
|||
}
|
||||
&:focus {
|
||||
.open--show-focus & {
|
||||
outline: focused-outline();
|
||||
box-shadow: focused-box-shadow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,10 +246,11 @@
|
|||
}
|
||||
&-img {
|
||||
fill: var(--muted-color);
|
||||
width: 2em;
|
||||
width: 1em;
|
||||
position: relative;
|
||||
top: -0.25em;
|
||||
margin-right: $base-padding-h;
|
||||
top: -0.22em;
|
||||
font-size: 1.5em;
|
||||
margin-right: $tiny-spacing;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
fill: var(--text-color);
|
||||
|
@ -297,20 +279,16 @@
|
|||
}
|
||||
color: var(--muted-color);
|
||||
padding: $base-padding;
|
||||
border-radius: var(--block-border-radius);
|
||||
&:focus {
|
||||
.open--show-focus & {
|
||||
outline: focused-outline();
|
||||
box-shadow: focused-box-shadow();
|
||||
}
|
||||
}
|
||||
&-icon {
|
||||
width: 2em;
|
||||
&--svg > svg {
|
||||
vertical-align: middle;
|
||||
@include size(1em);
|
||||
path {
|
||||
fill: var(--muted-color);
|
||||
}
|
||||
}
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
}
|
||||
&-text {
|
||||
flex-grow: 1;
|
||||
|
@ -373,7 +351,7 @@
|
|||
&__file {
|
||||
cursor: pointer;
|
||||
padding: $base-padding;
|
||||
border-radius: $base-border-radius;
|
||||
border-radius: var(--block-border-radius);
|
||||
box-sizing: border-box;
|
||||
flex-basis: 100%;
|
||||
@include nomobile {
|
||||
|
@ -410,6 +388,7 @@
|
|||
&__item {
|
||||
padding: $base-padding;
|
||||
cursor: pointer;
|
||||
border-radius: var(--block-border-radius);
|
||||
&:hover {
|
||||
background-color: var(--action-background-color-focus-tr);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
position: relative;
|
||||
|
||||
&__content {
|
||||
margin: $base-padding;
|
||||
margin: $medium-padding;
|
||||
}
|
||||
|
||||
> .scroller {
|
||||
|
@ -58,6 +58,12 @@
|
|||
&-post {
|
||||
display: none;
|
||||
}
|
||||
&-post {
|
||||
margin-left: $tiny-spacing;
|
||||
margin-right: $small-spacing;
|
||||
position: relative;
|
||||
top: 0.2em;
|
||||
}
|
||||
cursor: pointer;
|
||||
@include mobile {
|
||||
line-height: $mobile-back-button-height;
|
||||
|
@ -65,9 +71,11 @@
|
|||
padding: $base-padding;
|
||||
font-size: 1.2em;
|
||||
> i {
|
||||
margin-right: 0.3em;
|
||||
margin-right: 0.7em;
|
||||
font-size: 1.2em;
|
||||
vertical-align: text-bottom;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -0.15em;
|
||||
}
|
||||
&-pre {
|
||||
display: inline;
|
||||
|
@ -130,31 +138,31 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&__file-master-pass-warning,
|
||||
&__file-confirm-master-pass-warning {
|
||||
font-weight: normal;
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
&__file {
|
||||
&-master-pass-warning,
|
||||
&-confirm-master-pass-warning {
|
||||
font-weight: normal;
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__file-save-to {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-right: $base-padding-h;
|
||||
text-align: center;
|
||||
> i,
|
||||
> svg {
|
||||
display: block;
|
||||
font-size: 3em;
|
||||
padding: $base-padding-px;
|
||||
margin: auto;
|
||||
}
|
||||
> svg {
|
||||
@include size(1em);
|
||||
}
|
||||
&:hover {
|
||||
transition: color $base-duration $base-timing;
|
||||
color: var(--medium-color);
|
||||
&-save-to {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-right: $base-padding-h;
|
||||
text-align: center;
|
||||
|
||||
> i {
|
||||
display: block;
|
||||
font-size: 3em;
|
||||
padding: $base-padding-px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transition: color $base-duration $base-timing;
|
||||
color: var(--medium-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +182,49 @@
|
|||
&__general-prv-logout {
|
||||
margin-bottom: $base-padding-v;
|
||||
}
|
||||
&__general-themes {
|
||||
width: calc(100% - 10em);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10em, 1fr));
|
||||
grid-gap: $base-spacing;
|
||||
margin-bottom: $base-spacing;
|
||||
@include mobile {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&__general-theme {
|
||||
padding: $base-padding;
|
||||
border: light-border();
|
||||
border-radius: var(--input-border-radius);
|
||||
text-align: center;
|
||||
background: var(--background-color);
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
body & {
|
||||
--focus-shadow-spread: unset;
|
||||
--form-box-shadow-color-focus: unset;
|
||||
--form-box-shadow-color-hover: unset;
|
||||
}
|
||||
&-name {
|
||||
border-bottom: light-border();
|
||||
padding-bottom: $tiny-spacing;
|
||||
margin-bottom: $small-spacing;
|
||||
}
|
||||
&-button {
|
||||
margin-bottom: $base-padding-v;
|
||||
}
|
||||
&-plugins-icon {
|
||||
font-size: 3em;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: form-box-shadow-hover();
|
||||
}
|
||||
&--selected,
|
||||
&--selected:hover {
|
||||
border: 1px solid var(--action-color);
|
||||
box-shadow: form-box-shadow-focus();
|
||||
}
|
||||
}
|
||||
&__logs {
|
||||
user-select: text;
|
||||
margin-top: $base-padding-v;
|
||||
|
@ -181,17 +232,6 @@
|
|||
&-log {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
&--debug {
|
||||
opacity: 0.8;
|
||||
}
|
||||
&--info {
|
||||
}
|
||||
&--warn {
|
||||
color: $yellow;
|
||||
}
|
||||
&--error {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__plugins {
|
||||
|
@ -293,6 +333,8 @@
|
|||
}
|
||||
|
||||
&__head-icon {
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
padding: $base-padding;
|
||||
padding: $medium-padding;
|
||||
|
||||
&__space {
|
||||
flex: 1;
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
@import 'override-mixins';
|
||||
@import 'colors';
|
||||
@import 'variables';
|
||||
@import 'properties';
|
||||
@import 'media';
|
||||
@import 'icon-font';
|
||||
@import 'body';
|
||||
@import 'grid-settings';
|
||||
@import 'buttons';
|
||||
|
|
|
@ -72,26 +72,56 @@
|
|||
}
|
||||
}
|
||||
|
||||
.btn ~ .btn,
|
||||
button ~ button {
|
||||
margin-left: $small-spacing;
|
||||
}
|
||||
|
||||
@mixin icon-btn($error: false) {
|
||||
@include area-selectable(bottom);
|
||||
padding: 0.7em 0.6em 0;
|
||||
height: 1.6em;
|
||||
@include area-selectable();
|
||||
padding: $medium-padding;
|
||||
text-align: center;
|
||||
border-radius: var(--block-border-radius);
|
||||
transition: color $base-duration $base-timing;
|
||||
> i {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@if $error {
|
||||
color: var(--muted-color);
|
||||
&:hover {
|
||||
border-color: var(--error-color);
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.svg-btn {
|
||||
svg path {
|
||||
transition: fill $base-duration $base-timing;
|
||||
fill: var(--text-color);
|
||||
@mixin bg-btn() {
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
-webkit-app-region: no-drag;
|
||||
padding: $medium-padding;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-radius: var(--block-border-radius);
|
||||
background-color: var(--intermediate-background-color);
|
||||
transition: color $base-duration $base-timing;
|
||||
> i {
|
||||
margin-right: 0.4em;
|
||||
line-height: inherit;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
&:hover svg path {
|
||||
fill: var(--medium-color);
|
||||
&:hover {
|
||||
background-color: var(--intermediate-background-color);
|
||||
color: var(--medium-color);
|
||||
transform: background-color $base-duration $base-timing;
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--intermediate-pressed-background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin bg-btn-active() {
|
||||
color: var(--selected-item-text-color);
|
||||
background-color: var(--selected-item-color);
|
||||
}
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
$black: #111;
|
||||
$white: #d8e5f1;
|
||||
$red: #df3c06;
|
||||
$orange: #fbac45;
|
||||
$yellow: #e9d92a;
|
||||
$green: #0dc94b;
|
||||
$blue: #4e6af8;
|
||||
$violet: #d946db;
|
||||
|
||||
$all-colors: (
|
||||
'white': $white,
|
||||
'black': $black,
|
||||
'red': $red,
|
||||
'orange': $orange,
|
||||
'yellow': $yellow,
|
||||
'green': $green,
|
||||
'blue': $blue,
|
||||
'violet': $violet
|
||||
$dark-colors: (
|
||||
white-color: #d8e5f1,
|
||||
black-color: #111,
|
||||
red-color: #ed5f5e,
|
||||
orange-color: #e8873a,
|
||||
yellow-color: #f7c644,
|
||||
green-color: #78b756,
|
||||
blue-color: #2f7cf7,
|
||||
violet-color: #e55d9c
|
||||
);
|
||||
|
||||
@each $col, $val in $all-colors {
|
||||
.#{$col}-color {
|
||||
color: #{$val};
|
||||
$light-colors: (
|
||||
white-color: #d8e5f1,
|
||||
black-color: #111,
|
||||
red-color: #d04745,
|
||||
orange-color: #e9873a,
|
||||
yellow-color: #f7c84e,
|
||||
green-color: #79b656,
|
||||
blue-color: #2f7cf7,
|
||||
violet-color: #e55d9c
|
||||
);
|
||||
|
||||
body {
|
||||
@each $name, $value in $dark-colors {
|
||||
--#{$name}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
@each $name in map-keys($dark-colors) {
|
||||
.#{$name} {
|
||||
color: var(--#{$name});
|
||||
}
|
||||
}
|
||||
.muted-color {
|
||||
|
|
|
@ -72,7 +72,7 @@ input:not([type]) {
|
|||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--form-box-border-focus);
|
||||
border-color: var(--form-box-border-color-focus);
|
||||
box-shadow: form-box-shadow-focus();
|
||||
outline: none;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ select {
|
|||
border-color: var(--accent-border-color);
|
||||
}
|
||||
&:focus {
|
||||
border-color: var(--form-box-border-focus);
|
||||
border-color: var(--form-box-border-color-focus);
|
||||
box-shadow: form-box-shadow-focus();
|
||||
outline: none;
|
||||
}
|
||||
|
@ -185,6 +185,10 @@ input[type='checkbox'] {
|
|||
display: inline-block;
|
||||
width: 1.3em;
|
||||
color: var(--text-color);
|
||||
font-size: 1.2em;
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
top: 0.08em;
|
||||
}
|
||||
&:checked + label:before {
|
||||
content: $fa-var-check-square-o;
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('fontawesome.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@mixin fa-icon {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
line-height: $base-line-height;
|
||||
}
|
||||
|
||||
.fa {
|
||||
@include fa-icon;
|
||||
}
|
||||
|
||||
$fa-glyph-counter: 61440; // 0xf000
|
||||
|
||||
@function next-fa-glyph() {
|
||||
$fa-glyph-counter: $fa-glyph-counter + 1 !global;
|
||||
|
||||
$lo-part: $fa-glyph-counter % 256;
|
||||
$hi-part: ($fa-glyph-counter - $lo-part) / 256;
|
||||
$hex-num-str: str-slice(#{rgb($hi-part, $lo-part, 1)}, 2, 5);
|
||||
|
||||
$glyph: unquote('"\\#{$hex-num-str}"');
|
||||
|
||||
@return $glyph;
|
||||
}
|
||||
|
||||
// position fixes for icons that need to be shifted because they're special
|
||||
|
||||
.fa-keyboard {
|
||||
@include position(relative, 0.1em null null null);
|
||||
}
|
||||
|
||||
// icons listed below will be automatically added to the generated icon font, see build/loaders/fontawesome-loader.js
|
||||
// if the icon has "-o" suffix, it will be used from the "regular" font, otherwise from "solid" or "brands"
|
||||
// -o is used because it's similar to an empty bullet and this used to be a convention in fontawesome 4
|
||||
|
||||
$fa-var-square: next-fa-glyph();
|
||||
$fa-var-square-o: next-fa-glyph();
|
||||
$fa-var-check-square-o: next-fa-glyph();
|
||||
$fa-var-bookmark: next-fa-glyph();
|
||||
$fa-var-bookmark-o: next-fa-glyph();
|
||||
$fa-var-eye: next-fa-glyph();
|
||||
$fa-var-eye-slash: next-fa-glyph();
|
||||
$fa-var-bolt: next-fa-glyph();
|
||||
$fa-var-unlock: next-fa-glyph();
|
||||
$fa-var-lock: next-fa-glyph();
|
||||
$fa-var-check: next-fa-glyph();
|
||||
$fa-var-times: next-fa-glyph();
|
||||
$fa-var-folder: next-fa-glyph();
|
||||
$fa-var-folder-open: next-fa-glyph();
|
||||
$fa-var-ban: next-fa-glyph();
|
||||
$fa-var-dropbox: next-fa-glyph();
|
||||
$fa-var-google-drive: next-fa-glyph();
|
||||
$fa-var-plus: next-fa-glyph();
|
||||
$fa-var-ellipsis-h: next-fa-glyph();
|
||||
$fa-var-ellipsis-v: next-fa-glyph();
|
||||
$fa-var-magic: next-fa-glyph();
|
||||
$fa-var-cog: next-fa-glyph();
|
||||
$fa-var-server: next-fa-glyph();
|
||||
$fa-var-file-alt: next-fa-glyph();
|
||||
$fa-var-file-alt-o: next-fa-glyph();
|
||||
$fa-var-file-code: next-fa-glyph();
|
||||
$fa-var-file-pdf: next-fa-glyph();
|
||||
$fa-var-file-archive: next-fa-glyph();
|
||||
$fa-var-file-word: next-fa-glyph();
|
||||
$fa-var-file-excel: next-fa-glyph();
|
||||
$fa-var-file-powerpoint: next-fa-glyph();
|
||||
$fa-var-file-image: next-fa-glyph();
|
||||
$fa-var-file-video: next-fa-glyph();
|
||||
$fa-var-file-audio: next-fa-glyph();
|
||||
$fa-var-onedrive: next-fa-glyph();
|
||||
$fa-var-question: next-fa-glyph();
|
||||
$fa-var-sign-out-alt: next-fa-glyph();
|
||||
$fa-var-sync-alt: next-fa-glyph();
|
||||
$fa-var-level-down-alt: next-fa-glyph();
|
||||
$fa-var-tag: next-fa-glyph();
|
||||
$fa-var-tags: next-fa-glyph();
|
||||
$fa-var-th-large: next-fa-glyph();
|
||||
$fa-var-trash: next-fa-glyph();
|
||||
$fa-var-trash-alt: next-fa-glyph();
|
||||
$fa-var-keyboard: next-fa-glyph();
|
||||
$fa-var-puzzle-piece: next-fa-glyph();
|
||||
$fa-var-usb: next-fa-glyph();
|
||||
$fa-var-info: next-fa-glyph();
|
||||
$fa-var-info-circle: next-fa-glyph();
|
||||
$fa-var-key: next-fa-glyph();
|
||||
$fa-var-globe: next-fa-glyph();
|
||||
$fa-var-exclamation-triangle: next-fa-glyph();
|
||||
$fa-var-thumbtack: next-fa-glyph();
|
||||
$fa-var-comments: next-fa-glyph();
|
||||
$fa-var-edit: next-fa-glyph();
|
||||
$fa-var-plug: next-fa-glyph();
|
||||
$fa-var-newspaper: next-fa-glyph();
|
||||
$fa-var-paperclip: next-fa-glyph();
|
||||
$fa-var-camera: next-fa-glyph();
|
||||
$fa-var-wifi: next-fa-glyph();
|
||||
$fa-var-link: next-fa-glyph();
|
||||
$fa-var-battery-three-quarters: next-fa-glyph();
|
||||
$fa-var-bars: next-fa-glyph();
|
||||
$fa-var-barcode: next-fa-glyph();
|
||||
$fa-var-certificate: next-fa-glyph();
|
||||
$fa-var-signature: next-fa-glyph();
|
||||
$fa-var-bullseye: next-fa-glyph();
|
||||
$fa-var-desktop: next-fa-glyph();
|
||||
$fa-var-envelope: next-fa-glyph();
|
||||
$fa-var-clipboard: next-fa-glyph();
|
||||
$fa-var-paper-plane: next-fa-glyph();
|
||||
$fa-var-address-card: next-fa-glyph();
|
||||
$fa-var-inbox: next-fa-glyph();
|
||||
$fa-var-save: next-fa-glyph();
|
||||
$fa-var-hdd: next-fa-glyph();
|
||||
$fa-var-dot-circle: next-fa-glyph();
|
||||
$fa-var-user-lock: next-fa-glyph();
|
||||
$fa-var-terminal: next-fa-glyph();
|
||||
$fa-var-print: next-fa-glyph();
|
||||
$fa-var-project-diagram: next-fa-glyph();
|
||||
$fa-var-flag-checkered: next-fa-glyph();
|
||||
$fa-var-wrench: next-fa-glyph();
|
||||
$fa-var-laptop: next-fa-glyph();
|
||||
$fa-var-archive: next-fa-glyph();
|
||||
$fa-var-credit-card: next-fa-glyph();
|
||||
$fa-var-windows: next-fa-glyph();
|
||||
$fa-var-clock: next-fa-glyph();
|
||||
$fa-var-search: next-fa-glyph();
|
||||
$fa-var-flask: next-fa-glyph();
|
||||
$fa-var-gamepad: next-fa-glyph();
|
||||
$fa-var-sticky-note: next-fa-glyph();
|
||||
$fa-var-sticky-note-o: next-fa-glyph();
|
||||
$fa-var-question-circle: next-fa-glyph();
|
||||
$fa-var-cube: next-fa-glyph();
|
||||
$fa-var-folder-o: next-fa-glyph();
|
||||
$fa-var-folder-open-o: next-fa-glyph();
|
||||
$fa-var-database: next-fa-glyph();
|
||||
$fa-var-unlock-alt: next-fa-glyph();
|
||||
$fa-var-pencil-alt: next-fa-glyph();
|
||||
$fa-var-image: next-fa-glyph();
|
||||
$fa-var-book: next-fa-glyph();
|
||||
$fa-var-list-alt: next-fa-glyph();
|
||||
$fa-var-user-secret: next-fa-glyph();
|
||||
$fa-var-utensils: next-fa-glyph();
|
||||
$fa-var-home: next-fa-glyph();
|
||||
$fa-var-star: next-fa-glyph();
|
||||
$fa-var-linux: next-fa-glyph();
|
||||
$fa-var-map-pin: next-fa-glyph();
|
||||
$fa-var-apple: next-fa-glyph();
|
||||
$fa-var-wikipedia-w: next-fa-glyph();
|
||||
$fa-var-dollar-sign: next-fa-glyph();
|
||||
$fa-var-mobile: next-fa-glyph();
|
||||
$fa-var-spinner: next-fa-glyph();
|
||||
$fa-var-minus-circle: next-fa-glyph();
|
||||
$fa-var-keeweb: next-fa-glyph();
|
||||
$fa-var-copy: next-fa-glyph();
|
||||
$fa-var-clone: next-fa-glyph();
|
||||
$fa-var-chevron-down: next-fa-glyph();
|
||||
$fa-var-chevron-left: next-fa-glyph();
|
||||
$fa-var-qrcode: next-fa-glyph();
|
||||
$fa-var-sort-alpha-down: next-fa-glyph();
|
||||
$fa-var-sort-alpha-down-alt: next-fa-glyph();
|
||||
$fa-var-sort-numeric-down: next-fa-glyph();
|
||||
$fa-var-sort-numeric-down-alt: next-fa-glyph();
|
||||
$fa-var-sort-amount-down: next-fa-glyph();
|
||||
$fa-var-language: next-fa-glyph();
|
||||
$fa-var-circle: next-fa-glyph();
|
||||
$fa-var-circle-o: next-fa-glyph();
|
||||
$fa-var-arrow-circle-left: next-fa-glyph();
|
||||
$fa-var-cloud-download-alt: next-fa-glyph();
|
||||
$fa-var-caret-down: next-fa-glyph();
|
||||
$fa-var-long-arrow-alt-left: next-fa-glyph();
|
||||
$fa-var-long-arrow-alt-right: next-fa-glyph();
|
||||
$fa-var-github-alt: next-fa-glyph();
|
||||
$fa-var-code: next-fa-glyph();
|
||||
$fa-var-html5: next-fa-glyph();
|
||||
$fa-var-chrome: next-fa-glyph();
|
||||
$fa-var-firefox-browser: next-fa-glyph();
|
||||
$fa-var-safari: next-fa-glyph();
|
||||
$fa-var-opera: next-fa-glyph();
|
||||
$fa-var-edge: next-fa-glyph();
|
||||
$fa-var-twitter: next-fa-glyph();
|
||||
$fa-var-paint-brush: next-fa-glyph();
|
||||
$fa-var-at: next-fa-glyph();
|
||||
$fa-var-usb-token: next-fa-glyph();
|
||||
$fa-var-bell: next-fa-glyph();
|
|
@ -0,0 +1,9 @@
|
|||
body {
|
||||
--focus-shadow-spread: 3px;
|
||||
--button-border-radius: 3px;
|
||||
--input-border-radius: 4px;
|
||||
--block-border-radius: 5px;
|
||||
--selected-item-text-color: var(--text-color);
|
||||
--open-icon-color: var(--text-color);
|
||||
--dropdown-box-shadow-color: rgba(0, 0, 0, 0.1);
|
||||
}
|