Merge remote-tracking branch 'origin/t35'

# Conflicts:
#	src/views/tabs.ejs
This commit is contained in:
zadam 2019-07-28 22:38:02 +02:00
commit 385b79ad34
21 changed files with 686 additions and 15683 deletions

View File

@ -7,6 +7,7 @@ const sqlInit = require('./src/services/sql_init');
const cls = require('./src/services/cls'); const cls = require('./src/services/cls');
const url = require("url"); const url = require("url");
const port = require('./src/services/port'); const port = require('./src/services/port');
const env = require('./src/services/env');
const appIconService = require('./src/services/app_icon'); const appIconService = require('./src/services/app_icon');
const windowStateKeeper = require('electron-window-state'); const windowStateKeeper = require('electron-window-state');
const contextMenu = require('electron-context-menu'); const contextMenu = require('electron-context-menu');
@ -74,7 +75,7 @@ async function createMainWindow() {
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
}, },
icon: path.join(__dirname, 'images/app-icons/png/256x256.png') icon: path.join(__dirname, 'images/app-icons/png/256x256' + (env.isDev() ? '-dev' : '') + '.png')
}); });
mainWindowState.manage(win); mainWindowState.manage(win);

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

193
package-lock.json generated
View File

@ -77,6 +77,42 @@
} }
} }
}, },
"@develar/schema-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.1.0.tgz",
"integrity": "sha512-qjCqB4ctMig9Gz5bd6lkdFr3bO6arOdQqptdBSpF1ZpCnjofieCciEzkoS9ujY9cMGyllYSCSmBJ3x9OKHXzoA==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0"
},
"dependencies": {
"ajv": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}
}
},
"@electron/get": { "@electron/get": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-1.3.1.tgz", "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.3.1.tgz",
@ -770,26 +806,27 @@
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
}, },
"app-builder-bin": { "app-builder-bin": {
"version": "3.4.0", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.0.tgz", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.2.tgz",
"integrity": "sha512-ZyQqfFnyjAmXep29PmuMIu2KvGlfHiziz2/I38zOFe5kqnkMqeNNn/qmnwD6wdCKKeHqBuZtoWQnft8q0YyLpw==", "integrity": "sha512-E9VvUrZm60oLl0rvLw/ErvZiEOIHkulZbJ9C9TWMD4ftNrkXFc2+a5JGlD19Bwvj329d4qz91zs/hWD01q18TQ==",
"dev": true "dev": true
}, },
"app-builder-lib": { "app-builder-lib": {
"version": "21.1.1", "version": "21.1.5",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-21.1.1.tgz", "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-21.1.5.tgz",
"integrity": "sha512-nG5w9xjv4c16ki+pyBPqJvUCtQY97XQNsA4dEBryq6vClwDTkwKrl7aS0cXjo3dsWKsf/5EIcLrV0tfIiXUqJA==", "integrity": "sha512-hLsSVSstMTq33a9jbBStrF0e+fa6eWXWIpsatPvHnJlkjx7dAZ5d2cZPe/1bAO3bCL3afpRl5j+HU853eGfDCg==",
"dev": true, "dev": true,
"requires": { "requires": {
"7zip-bin": "~5.0.3", "7zip-bin": "~5.0.3",
"@develar/schema-utils": "~2.1.0",
"async-exit-hook": "^2.0.1", "async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9", "bluebird-lst": "^1.0.9",
"builder-util": "21.1.1", "builder-util": "21.1.5",
"builder-util-runtime": "8.3.0", "builder-util-runtime": "8.3.0",
"chromium-pickle-js": "^0.2.0", "chromium-pickle-js": "^0.2.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"ejs": "^2.6.2", "ejs": "^2.6.2",
"electron-publish": "21.1.1", "electron-publish": "21.1.5",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"hosted-git-info": "^2.7.1", "hosted-git-info": "^2.7.1",
"is-ci": "^2.0.0", "is-ci": "^2.0.0",
@ -798,9 +835,9 @@
"lazy-val": "^1.0.4", "lazy-val": "^1.0.4",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"normalize-package-data": "^2.5.0", "normalize-package-data": "^2.5.0",
"read-config-file": "4.0.1", "read-config-file": "5.0.0",
"sanitize-filename": "^1.6.1", "sanitize-filename": "^1.6.1",
"semver": "^6.2.0", "semver": "^6.3.0",
"temp-file": "^3.3.4" "temp-file": "^3.3.4"
}, },
"dependencies": { "dependencies": {
@ -1577,14 +1614,14 @@
} }
}, },
"builder-util": { "builder-util": {
"version": "21.1.1", "version": "21.1.5",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-21.1.1.tgz", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-21.1.5.tgz",
"integrity": "sha512-+t6pbMo/COTYDfqfRDrWDi9tuPxBctb0S9PVt8Kgd+K0eqD1lvbF05rrkiQpXIrhb6cndXgcl3FG18Oqgy5YPg==", "integrity": "sha512-RBr06OnoTKTkg0W7Om2dnuKe8I82CiciU9EdsNnLO8DAfH0ZVCPsVpc3qYmr7g/7cH3zXA3yyQP73QDGNpbVYQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"7zip-bin": "~5.0.3", "7zip-bin": "~5.0.3",
"@types/debug": "^4.1.4", "@types/debug": "^4.1.4",
"app-builder-bin": "3.4.0", "app-builder-bin": "3.4.2",
"bluebird-lst": "^1.0.9", "bluebird-lst": "^1.0.9",
"builder-util-runtime": "8.3.0", "builder-util-runtime": "8.3.0",
"chalk": "^2.4.2", "chalk": "^2.4.2",
@ -2243,9 +2280,9 @@
} }
}, },
"content-security-policy-builder": { "content-security-policy-builder": {
"version": "2.0.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz", "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz",
"integrity": "sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w==" "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ=="
}, },
"content-type": { "content-type": {
"version": "1.0.4", "version": "1.0.4",
@ -2869,18 +2906,17 @@
} }
}, },
"dmg-builder": { "dmg-builder": {
"version": "21.1.1", "version": "21.1.5",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-21.1.1.tgz", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-21.1.5.tgz",
"integrity": "sha512-WsafJ+LkG20ddVfKfNlJC6AMRKTvLMJhAwAmBHzb+KWhMZVs9/3QY2yd2BIpvWYRfUD/lWd+LmclTNWw7540Cw==", "integrity": "sha512-o9rZua7mzNzZIo9SZJi57ZXe4zjNaajxEJ0LO/8k2z8Q2CL57RIaJZ95sA6G0dC1/g5Gm4cmW3O5nv9Ur8Ayow==",
"dev": true, "dev": true,
"requires": { "requires": {
"app-builder-lib": "~21.1.1", "app-builder-lib": "~21.1.5",
"bluebird-lst": "^1.0.9", "bluebird-lst": "^1.0.9",
"builder-util": "~21.1.1", "builder-util": "~21.1.5",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"iconv-lite": "^0.5.0", "iconv-lite": "^0.5.0",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"parse-color": "^1.0.0",
"sanitize-filename": "^1.6.1" "sanitize-filename": "^1.6.1"
}, },
"dependencies": { "dependencies": {
@ -3088,21 +3124,21 @@
} }
}, },
"electron-builder": { "electron-builder": {
"version": "21.1.1", "version": "21.1.5",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-21.1.1.tgz", "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-21.1.5.tgz",
"integrity": "sha512-4A3h0dhqSranoX7cM0eqkzABeHABH9Nch12003XIZ9MUPV4jL97nK5WBbbVG06qglYTV9x62XAqPPDS+KBOsEg==", "integrity": "sha512-2tEgBESGWQ661dC9/rZlpbtmhlofoNPwnkO6KUuCZUBYpFMRvpMzH2IF7vQYsCst78yxnrV0CtiBePhM1i+DfA==",
"dev": true, "dev": true,
"requires": { "requires": {
"app-builder-lib": "21.1.1", "app-builder-lib": "21.1.5",
"bluebird-lst": "^1.0.9", "bluebird-lst": "^1.0.9",
"builder-util": "21.1.1", "builder-util": "21.1.5",
"builder-util-runtime": "8.3.0", "builder-util-runtime": "8.3.0",
"chalk": "^2.4.2", "chalk": "^2.4.2",
"dmg-builder": "21.1.1", "dmg-builder": "21.1.5",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"is-ci": "^2.0.0", "is-ci": "^2.0.0",
"lazy-val": "^1.0.4", "lazy-val": "^1.0.4",
"read-config-file": "4.0.1", "read-config-file": "5.0.0",
"sanitize-filename": "^1.6.1", "sanitize-filename": "^1.6.1",
"update-notifier": "^3.0.1", "update-notifier": "^3.0.1",
"yargs": "^13.3.0" "yargs": "^13.3.0"
@ -3372,15 +3408,15 @@
"dev": true "dev": true
}, },
"package-json": { "package-json": {
"version": "6.4.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/package-json/-/package-json-6.4.0.tgz", "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
"integrity": "sha512-bd1T8OBG7hcvMd9c/udgv6u5v9wISP3Oyl9Cm7Weop8EFwrtcQDnS2sb6zhwqus2WslSr5wSTIPiTTpxxmPm7Q==", "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"got": "^9.6.0", "got": "^9.6.0",
"registry-auth-token": "^3.4.0", "registry-auth-token": "^4.0.0",
"registry-url": "^5.0.0", "registry-url": "^5.0.0",
"semver": "^6.1.1" "semver": "^6.2.0"
} }
}, },
"prepend-http": { "prepend-http": {
@ -3390,12 +3426,12 @@
"dev": true "dev": true
}, },
"registry-auth-token": { "registry-auth-token": {
"version": "3.4.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.0.0.tgz",
"integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "integrity": "sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw==",
"dev": true, "dev": true,
"requires": { "requires": {
"rc": "^1.1.6", "rc": "^1.2.8",
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
@ -3901,13 +3937,13 @@
} }
}, },
"electron-publish": { "electron-publish": {
"version": "21.1.1", "version": "21.1.5",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-21.1.1.tgz", "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-21.1.5.tgz",
"integrity": "sha512-cM5XLFolIB5NnVpBKewcUM3Ggg+wm+LeKRYHjcB9ri58cbPKxaN+fRUXAZs3m1U9t7SIgjZy83sUSi3asxMXgQ==", "integrity": "sha512-EgFxZJBrb+EBUcEwg8RNKouoBaOEpACnjLi+ipUsk72x+/6kzhcofAYBchIkGns7meengoXy7QdoiThgpr6rew==",
"dev": true, "dev": true,
"requires": { "requires": {
"bluebird-lst": "^1.0.9", "bluebird-lst": "^1.0.9",
"builder-util": "~21.1.1", "builder-util": "~21.1.5",
"builder-util-runtime": "8.3.0", "builder-util-runtime": "8.3.0",
"chalk": "^2.4.2", "chalk": "^2.4.2",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
@ -6488,9 +6524,9 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
}, },
"helmet": { "helmet": {
"version": "3.19.0", "version": "3.20.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.19.0.tgz", "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.20.0.tgz",
"integrity": "sha512-l58Q3unSpYatlurvFzkCbTRQ8oWUmdXbOs7h+pnwQbFJRhRJDjER6UMyqHxp9iFtWPcVA05VLcUGSi0EXIv7GA==", "integrity": "sha512-Ob+TqmQFZ5f7WgP8kBbAzNPsbf6p1lOj5r+327/ymw/IILWih3wcx9u/u/S8Mwv5wbBkO7Li6x5s23t3COhUKw==",
"requires": { "requires": {
"depd": "2.0.0", "depd": "2.0.0",
"dns-prefetch-control": "0.2.0", "dns-prefetch-control": "0.2.0",
@ -6499,7 +6535,7 @@
"feature-policy": "0.3.0", "feature-policy": "0.3.0",
"frameguard": "3.1.0", "frameguard": "3.1.0",
"helmet-crossdomain": "0.4.0", "helmet-crossdomain": "0.4.0",
"helmet-csp": "2.7.1", "helmet-csp": "2.8.0",
"hide-powered-by": "1.1.0", "hide-powered-by": "1.1.0",
"hpkp": "2.0.0", "hpkp": "2.0.0",
"hsts": "2.2.0", "hsts": "2.2.0",
@ -6522,12 +6558,12 @@
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
}, },
"helmet-csp": { "helmet-csp": {
"version": "2.7.1", "version": "2.8.0",
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.1.tgz", "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.8.0.tgz",
"integrity": "sha512-sCHwywg4daQ2mY0YYwXSZRsgcCeerUwxMwNixGA7aMLkVmPTYBl7gJoZDHOZyXkqPrtuDT3s2B1A+RLI7WxSdQ==", "integrity": "sha512-MlCPeM0Sm3pS9RACRihx70VeTHmkQwa7sum9EK1tfw1VZyvFU0dBWym9nHh3CRkTRNlyNm/WFCMvuh9zXkOjNw==",
"requires": { "requires": {
"camelize": "1.0.0", "camelize": "1.0.0",
"content-security-policy-builder": "2.0.0", "content-security-policy-builder": "2.1.0",
"dasherize": "2.0.0", "dasherize": "2.0.0",
"platform": "1.3.5" "platform": "1.3.5"
} }
@ -9701,23 +9737,6 @@
"xml2js": "^0.4.5" "xml2js": "^0.4.5"
} }
}, },
"parse-color": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz",
"integrity": "sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=",
"dev": true,
"requires": {
"color-convert": "~0.5.0"
},
"dependencies": {
"color-convert": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=",
"dev": true
}
}
},
"parse-glob": { "parse-glob": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
@ -10875,13 +10894,11 @@
} }
}, },
"read-config-file": { "read-config-file": {
"version": "4.0.1", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-4.0.1.tgz", "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-5.0.0.tgz",
"integrity": "sha512-5caED3uo2IAZMPcbh/9hx/O29s2430RLxtnFDdzxpH/epEpawOrQnGBHueotIXUrGPPIgdNQN+S/CIp2WmiSfw==", "integrity": "sha512-jIKUu+C84bfnKxyJ5j30CxCqgXWYjZLXuVE/NYlMEpeni+dhESgAeZOZd0JZbg1xTkMmnCdxksDoarkOyfEsOg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.10.1",
"ajv-keywords": "^3.4.1",
"dotenv": "^8.0.0", "dotenv": "^8.0.0",
"dotenv-expand": "^5.1.0", "dotenv-expand": "^5.1.0",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
@ -10890,30 +10907,12 @@
"lazy-val": "^1.0.4" "lazy-val": "^1.0.4"
}, },
"dependencies": { "dependencies": {
"ajv": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"esprima": { "esprima": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true "dev": true
}, },
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"js-yaml": { "js-yaml": {
"version": "3.13.1", "version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
@ -10923,12 +10922,6 @@
"argparse": "^1.0.7", "argparse": "^1.0.7",
"esprima": "^4.0.0" "esprima": "^4.0.0"
} }
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
} }
} }
}, },
@ -11424,9 +11417,9 @@
} }
}, },
"semver": { "semver": {
"version": "6.2.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}, },
"semver-diff": { "semver-diff": {
"version": "2.1.0", "version": "2.1.0",

View File

@ -40,7 +40,7 @@
"express-session": "1.16.2", "express-session": "1.16.2",
"file-type": "12.0.1", "file-type": "12.0.1",
"fs-extra": "8.1.0", "fs-extra": "8.1.0",
"helmet": "3.19.0", "helmet": "3.20.0",
"html": "1.0.0", "html": "1.0.0",
"html2plaintext": "2.1.2", "html2plaintext": "2.1.2",
"http-proxy-agent": "^2.1.0", "http-proxy-agent": "^2.1.0",
@ -64,7 +64,7 @@
"rimraf": "2.6.3", "rimraf": "2.6.3",
"sanitize-filename": "1.6.1", "sanitize-filename": "1.6.1",
"sax": "1.2.4", "sax": "1.2.4",
"semver": "6.2.0", "semver": "6.3.0",
"serve-favicon": "2.5.0", "serve-favicon": "2.5.0",
"session-file-store": "1.3.1", "session-file-store": "1.3.1",
"simple-node-logger": "18.12.23", "simple-node-logger": "18.12.23",
@ -79,7 +79,7 @@
"devDependencies": { "devDependencies": {
"devtron": "1.4.0", "devtron": "1.4.0",
"electron": "6.0.0-beta.14", "electron": "6.0.0-beta.14",
"electron-builder": "21.1.1", "electron-builder": "21.1.5",
"electron-compile": "6.4.4", "electron-compile": "6.4.4",
"electron-installer-debian": "2.0.0", "electron-installer-debian": "2.0.0",
"electron-packager": "14.0.2", "electron-packager": "14.0.2",

View File

@ -42,6 +42,7 @@ import noteAutocompleteService from './services/note_autocomplete.js';
import macInit from './services/mac_init.js'; import macInit from './services/mac_init.js';
import cssLoader from './services/css_loader.js'; import cssLoader from './services/css_loader.js';
import dateNoteService from './services/date_notes.js'; import dateNoteService from './services/date_notes.js';
import sidebarService from './services/sidebar.js';
window.glob.isDesktop = utils.isDesktop; window.glob.isDesktop = utils.isDesktop;
window.glob.isMobile = utils.isMobile; window.glob.isMobile = utils.isMobile;

View File

@ -0,0 +1,93 @@
import NoteInfoWidget from "../widgets/note_info.js";
import LinkMapWidget from "../widgets/link_map.js";
import NoteRevisionsWidget from "../widgets/note_revisions.js";
const WIDGET_TPL = `
<div class="card widget">
<div class="card-header">
<button class="btn btn-sm widget-title" data-toggle="collapse" data-target="#collapseOne">
Collapsible Group Item
</button>
<div class="widget-header-actions"></div>
</div>
<div id="collapseOne" class="collapse show body-wrapper">
<div class="card-body"></div>
</div>
</div>
`;
let widgetIdCtr = 1;
class Sidebar {
/**
* @param {TabContext} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.$sidebar = ctx.$tabContent.find(".note-detail-sidebar");
this.$widgets = this.$sidebar.find(".note-detail-widgets");
this.$showSideBarButton = this.ctx.$tabContent.find(".show-sidebar-button");
this.$showSideBarButton.hide();
this.$hideSidebarButton = this.$sidebar.find(".hide-sidebar-button");
this.$hideSidebarButton.click(() => {
this.$sidebar.hide();
this.$showSideBarButton.show();
});
this.$showSideBarButton.click(() => {
this.$sidebar.show();
this.$showSideBarButton.hide();
});
}
async noteLoaded() {
this.$widgets.empty();
this.addNoteInfoWidget();
this.addLinkMapWidget();
this.addNoteRevisionsWidget();
}
async addNoteInfoWidget() {
const $widget = this.createWidgetElement();
const noteInfoWidget = new NoteInfoWidget(this.ctx, $widget);
await noteInfoWidget.renderBody();
this.$widgets.append($widget);
}
async addLinkMapWidget() {
const $widget = this.createWidgetElement();
const linkMapWidget = new LinkMapWidget(this.ctx, $widget);
await linkMapWidget.renderBody();
this.$widgets.append($widget);
}
async addNoteRevisionsWidget() {
const $widget = this.createWidgetElement();
const noteRevisionsWidget = new NoteRevisionsWidget(this.ctx, $widget);
await noteRevisionsWidget.renderBody();
this.$widgets.append($widget);
}
createWidgetElement() {
const widgetId = 'widget-' + widgetIdCtr++;
const $widget = $(WIDGET_TPL);
$widget.find('[data-target]').attr('data-target', "#" + widgetId);
$widget.find('.body-wrapper').attr('id', widgetId);
return $widget;
}
}
export default Sidebar;

View File

@ -18,6 +18,7 @@ import noteDetailRelationMap from "./note_detail_relation_map.js";
import noteDetailProtectedSession from "./note_detail_protected_session.js"; import noteDetailProtectedSession from "./note_detail_protected_session.js";
import protectedSessionService from "./protected_session.js"; import protectedSessionService from "./protected_session.js";
import linkService from "./link.js"; import linkService from "./link.js";
import Sidebar from "./sidebar.js";
const $tabContentsContainer = $("#note-tab-container"); const $tabContentsContainer = $("#note-tab-container");
@ -61,6 +62,7 @@ class TabContext {
this.attributes = new Attributes(this); this.attributes = new Attributes(this);
if (utils.isDesktop()) { if (utils.isDesktop()) {
this.sidebar = new Sidebar(this);
this.noteType = new NoteTypeContext(this); this.noteType = new NoteTypeContext(this);
} }
@ -131,6 +133,10 @@ class TabContext {
this.showPaths(); this.showPaths();
if (this.sidebar) {
this.sidebar.noteLoaded();
}
console.debug(`Switched tab ${this.tabId} to ${this.noteId}`); console.debug(`Switched tab ${this.tabId} to ${this.noteId}`);
} }

View File

@ -69,10 +69,19 @@ class TabRow {
} }
setupEvents() { setupEvents() {
window.addEventListener('resize', _ => { const resizeListener = _ => {
this.cleanUpPreviouslyDraggedTabs(); this.cleanUpPreviouslyDraggedTabs();
this.layoutTabs(); this.layoutTabs();
}); };
// ResizeObserver exists only in FF69
if (typeof ResizeObserver !== "undefined") {
new ResizeObserver(resizeListener).observe(this.el);
}
else {
// for older firefox
window.addEventListener('resize', resizeListener);
}
this.tabEls.forEach((tabEl) => this.setTabCloseEventListener(tabEl)); this.tabEls.forEach((tabEl) => this.setTabCloseEventListener(tabEl));
} }

View File

@ -0,0 +1,230 @@
import libraryLoader from "../services/library_loader.js";
import linkMapDialog from "../dialogs/link_map.js";
import server from "../services/server.js";
import treeCache from "../services/tree_cache.js";
import linkService from "../services/link.js";
let linkMapContainerIdCtr = 1;
const TPL = `
<div style="outline: none; overflow: hidden;">
<div class="link-map-container"></div>
</div>
`;
const linkOverlays = [
[ "Arrow", {
location: 1,
id: "arrow",
length: 10,
width: 10,
foldback: 0.7
} ]
];
class LinkMapWidget {
/**
* @param {TabContext} ctx
* @param {jQuery} $widget
*/
constructor(ctx, $widget) {
this.ctx = ctx;
this.$widget = $widget;
this.$title = this.$widget.find('.widget-title');
this.$title.text("Link map");
this.$headerActions = this.$widget.find('.widget-header-actions');
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
$showFullButton.click(() => {
linkMapDialog.showDialog();
});
this.$headerActions.append($showFullButton);
}
async renderBody() {
const $body = this.$widget.find('.card-body');
$body.html(TPL);
this.$linkMapContainer = $body.find('.link-map-container');
this.$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
await libraryLoader.requireLibrary(libraryLoader.LINK_MAP);
jsPlumb.ready(() => {
this.initJsPlumbInstance();
this.initPanZoom();
this.loadNotesAndRelations();
});
}
async loadNotesAndRelations() {
this.cleanup();
const linkTypes = [ "hyper", "image", "relation", "relation-map" ];
const maxNotes = 50;
const noteId = this.ctx.note.noteId;
const links = await server.post(`notes/${noteId}/link-map`, {
linkTypes,
maxNotes,
maxDepth: 1
});
const noteIds = new Set(links.map(l => l.noteId).concat(links.map(l => l.targetNoteId)));
if (noteIds.size === 0) {
noteIds.add(noteId);
}
// preload all notes
const notes = await treeCache.getNotes(Array.from(noteIds));
const graph = new Springy.Graph();
graph.addNodes(...noteIds);
graph.addEdges(...links.map(l => [l.noteId, l.targetNoteId]));
const layout = new Springy.Layout.ForceDirected(
graph,
400.0, // Spring stiffness
400.0, // Node repulsion
0.5 // Damping
);
const getNoteBox = noteId => {
const noteBoxId = this.noteIdToId(noteId);
const $existingNoteBox = $("#" + noteBoxId);
if ($existingNoteBox.length > 0) {
return $existingNoteBox;
}
const note = notes.find(n => n.noteId === noteId);
const $noteBox = $("<div>")
.addClass("note-box")
.prop("id", noteBoxId);
linkService.createNoteLink(noteId, note.title).then($link => {
$noteBox.append($("<span>").addClass("title").append($link));
});
if (noteId === noteId) {
$noteBox.addClass("link-map-active-note");
}
this.$linkMapContainer.append($noteBox);
this.jsPlumbInstance.draggable($noteBox[0], {
start: params => {
renderer.stop();
},
drag: params => {},
stop: params => {}
});
return $noteBox;
};
this.renderer = new Springy.Renderer(
layout,
() => {},
(edge, p1, p2) => {
const connectionId = edge.source.id + '-' + edge.target.id;
if ($("#" + connectionId).length > 0) {
return;
}
getNoteBox(edge.source.id);
getNoteBox(edge.target.id);
const connection = this.jsPlumbInstance.connect({
source: this.noteIdToId(edge.source.id),
target: this.noteIdToId(edge.target.id),
type: 'link'
});
connection.canvas.id = connectionId;
},
(node, p) => {
const $noteBox = getNoteBox(node.id);
const middleW = this.$linkMapContainer.width() / 2;
const middleH = this.$linkMapContainer.height() / 2;
$noteBox
.css("left", (middleW + p.x * 100) + "px")
.css("top", (middleH + p.y * 100) + "px");
},
() => {},
() => {},
() => {
this.jsPlumbInstance.repaintEverything();
}
);
this.renderer.start();
}
initPanZoom() {
if (this.pzInstance) {
return;
}
this.pzInstance = panzoom(this.$linkMapContainer[0], {
maxZoom: 2,
minZoom: 0.3,
smoothScroll: false,
filterKey: function (e, dx, dy, dz) {
// if ALT is pressed then panzoom should bubble the event up
// this is to preserve ALT-LEFT, ALT-RIGHT navigation working
return e.altKey;
}
});
}
cleanup() {
if (this.renderer) {
this.renderer.stop();
}
// delete all endpoints and connections
// this is done at this point (after async operations) to reduce flicker to the minimum
this.jsPlumbInstance.deleteEveryEndpoint();
// without this we still end up with note boxes remaining in the canvas
this.$linkMapContainer.empty();
// reset zoom/pan
this.pzInstance.zoomTo(0, 0, 0.7);
this.pzInstance.moveTo(0, 0);
}
initJsPlumbInstance() {
if (this.jsPlumbInstance) {
this.cleanup();
return;
}
this.jsPlumbInstance = jsPlumb.getInstance({
Endpoint: ["Blank", {}],
ConnectionOverlays: linkOverlays,
PaintStyle: { stroke: "var(--muted-text-color)", strokeWidth: 1 },
HoverPaintStyle: { stroke: "var(--main-text-color)", strokeWidth: 1 },
Container: this.$linkMapContainer.attr("id")
});
this.jsPlumbInstance.registerConnectionType("link", { anchor: "Continuous", connector: "Straight", overlays: linkOverlays });
}
noteIdToId(noteId) {
return "link-map-note-" + noteId;
}
}
export default LinkMapWidget;

View File

@ -0,0 +1,59 @@
const TPL = `
<table class="note-info-table">
<tr>
<th>Note ID</th>
<td class="note-info-note-id"></td>
</tr>
<tr>
<th>Created</th>
<td class="note-info-date-created"></td>
</tr>
<tr>
<th>Modified</th>
<td class="note-info-date-modified"></td>
</tr>
<tr>
<th>Type</th>
<td class="note-info-type"></td>
</tr>
<tr>
<th>MIME</th>
<td class="note-info-mime"></td>
</tr>
</table>
`;
class NoteInfoWidget {
/**
* @param {TabContext} ctx
* @param {jQuery} $widget
*/
constructor(ctx, $widget) {
this.ctx = ctx;
this.$widget = $widget;
this.$title = this.$widget.find('.widget-title');
this.$title.text("Note info");
}
async renderBody() {
const $body = this.$widget.find('.card-body');
$body.html(TPL);
const $noteId = $body.find(".note-info-note-id");
const $dateCreated = $body.find(".note-info-date-created");
const $dateModified = $body.find(".note-info-date-modified");
const $type = $body.find(".note-info-type");
const $mime = $body.find(".note-info-mime");
const note = this.ctx.note;
$noteId.text(note.noteId);
$dateCreated.text(note.dateCreated);
$dateModified.text(note.dateModified);
$type.text(note.type);
$mime.text(note.mime);
}
}
export default NoteInfoWidget;

View File

@ -0,0 +1,44 @@
import server from "../services/server.js";
const TPL = `
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
</ul>
`;
class NoteRevisionsWidget {
/**
* @param {TabContext} ctx
* @param {jQuery} $widget
*/
constructor(ctx, $widget) {
this.ctx = ctx;
this.$widget = $widget;
this.$title = this.$widget.find('.widget-title');
this.$title.text("Note revisions");
}
async renderBody() {
const $body = this.$widget.find('.card-body');
const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revisions`);
if (revisionItems.length === 0) {
$body.text("No revisions yet...");
return;
}
$body.html(TPL);
const $list = $body.find('.note-revision-list');
for (const item of revisionItems) {
$list.append($('<li>').append($("<a>", {
'data-action': 'note-revision',
'data-note-path': this.ctx.note.noteId,
'data-note-revision-id': item.noteRevisionId,
href: 'javascript:'
}).text(item.dateModifiedFrom)));
}
}
}
export default NoteRevisionsWidget;

View File

@ -133,6 +133,11 @@ li.dropdown-submenu:hover > ul.dropdown-menu {
border-style: solid; border-style: solid;
} }
body {
/* FF basic styling */
scrollbar-color: var(--active-item-background-color) var(--main-background-color);
}
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 12px; width: 12px;
} }
@ -142,8 +147,12 @@ li.dropdown-submenu:hover > ul.dropdown-menu {
border: 1px solid var(--main-border-color); border: 1px solid var(--main-border-color);
} }
#note-info-table td, #note-info-table th { ::-webkit-scrollbar-corner {
padding: 15px; background-color: inherit;
}
.widget .note-info-table td, .widget .note-info-table th {
padding: 5px;
} }
[data-toggle="tooltip"] span { [data-toggle="tooltip"] span {

View File

@ -1,10 +1,10 @@
#link-map-container { .link-map-container {
position: relative; position: relative;
height: calc(95vh - 130px); height: 300px;
outline: none; /* remove dotted outline on click */ outline: none; /* remove dotted outline on click */
} }
#link-map-container .note-box { .link-map-container .note-box {
padding: 8px; padding: 8px;
position: absolute !important; position: absolute !important;
background-color: var(--accented-background-color); background-color: var(--accented-background-color);
@ -23,16 +23,16 @@
overflow: hidden; overflow: hidden;
} }
#link-map-container .note-box:hover { .link-map-container .note-box:hover {
background-color: var(--more-accented-background-color); background-color: var(--more-accented-background-color);
} }
#link-map-container .note-box .title { .link-map-container .note-box .title {
font-size: larger; font-size: larger;
font-weight: 600; font-weight: 600;
} }
#link-map-container .floating-button { .link-map-container .floating-button {
position: absolute !important; position: absolute !important;
z-index: 100; z-index: 100;
} }

View File

@ -103,11 +103,64 @@ ul.fancytree-container {
} }
.note-tab-content { .note-tab-content {
display: flex;
flex-direction: row;
height: 100%;
width: 100%;
}
.note-detail-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.note-detail-sidebar {
min-width: 350px;
overflow: auto;
padding-top: 12px;
padding-left: 7px;
font-size: 90%;
}
.note-detail-sidebar .card {
border: 0;
}
.note-detail-sidebar .card-header {
background: inherit;
padding: 3px 10px 3px 10px;
width: 100%;
background-color: var(--button-background-color);
border-color: var(--button-border-color);
border-width: 1px 0 1px 0;
border-style: solid;
display: flex;
justify-content: space-between;
}
.note-detail-sidebar .widget-title {
border-radius: 0;
padding: 0;
border: 0;
background: inherit;
font-weight: bold;
}
.note-detail-sidebar .widget-header-actions {
}
.note-detail-sidebar .widget-header-action {
color: var(--link-color) !important;
cursor: pointer;
}
.note-detail-sidebar .card-body {
width: 100%;
padding: 8px;
border: 0;
}
.note-detail-component-wrapper { .note-detail-component-wrapper {
flex-grow: 100; flex-grow: 100;
position: relative; position: relative;

View File

@ -19,14 +19,21 @@ async function getLinks(noteIds, linkTypes) {
async function getLinkMap(req) { async function getLinkMap(req) {
const {noteId} = req.params; const {noteId} = req.params;
const {linkTypes, maxNotes} = req.body; const {linkTypes, maxNotes, maxDepth} = req.body;
let noteIds = new Set([noteId]); let noteIds = new Set([noteId]);
let links = []; let links = [];
let depth = 0;
while (true) { while (true) {
const newLinks = await getLinks(noteIds, linkTypes); links = await getLinks(noteIds, linkTypes);
const newNoteIds = new Set(newLinks.map(l => l.noteId).concat(newLinks.map(l => l.targetNoteId)));
if (depth === maxDepth) {
break;
}
const newNoteIds = new Set(links.map(l => l.noteId).concat(links.map(l => l.targetNoteId)));
if (newNoteIds.size === noteIds.size) { if (newNoteIds.size === noteIds.size) {
// no new note discovered, no need to search any further // no new note discovered, no need to search any further
@ -39,7 +46,8 @@ async function getLinkMap(req) {
} }
noteIds = newNoteIds; noteIds = newNoteIds;
links = newLinks;
depth++;
} }
// keep only links coming from and targetting some note in the noteIds set // keep only links coming from and targetting some note in the noteIds set

View File

@ -1,6 +1,7 @@
"use strict"; "use strict";
const sql = require('./sql'); const sql = require('./sql');
const log = require('./log');
const sqlInit = require('./sql_init'); const sqlInit = require('./sql_init');
const utils = require('./utils'); const utils = require('./utils');
const passwordEncryptionService = require('./password_encryption'); const passwordEncryptionService = require('./password_encryption');
@ -22,7 +23,7 @@ async function checkAuth(req, res, next) {
// currently we're doing that for file upload because handling form data seems to be difficult // currently we're doing that for file upload because handling form data seems to be difficult
async function checkApiAuthOrElectron(req, res, next) { async function checkApiAuthOrElectron(req, res, next) {
if (!req.session.loggedIn && !utils.isElectron()) { if (!req.session.loggedIn && !utils.isElectron()) {
res.status(401).send("Not authorized"); reject(req, res, "Not authorized");
} }
else { else {
next(); next();
@ -31,7 +32,7 @@ async function checkApiAuthOrElectron(req, res, next) {
async function checkApiAuth(req, res, next) { async function checkApiAuth(req, res, next) {
if (!req.session.loggedIn) { if (!req.session.loggedIn) {
res.status(401).send("Not authorized"); reject(req, res, "Not authorized");
} }
else { else {
next(); next();
@ -49,7 +50,7 @@ async function checkAppInitialized(req, res, next) {
async function checkAppNotInitialized(req, res, next) { async function checkAppNotInitialized(req, res, next) {
if (await sqlInit.isDbInitialized()) { if (await sqlInit.isDbInitialized()) {
res.status(400).send("App already initialized."); reject(req, res, "App already initialized.");
} }
else { else {
next(); next();
@ -60,13 +61,19 @@ async function checkToken(req, res, next) {
const token = req.headers.authorization; const token = req.headers.authorization;
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) { if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
res.status(401).send("Not authorized"); reject(req, res, "Not authorized");
} }
else { else {
next(); next();
} }
} }
function reject(req, res, message) {
log.info(`${req.method} ${req.path} rejected with 401 ${message}`);
res.status(401).send(message);
}
async function checkBasicAuth(req, res, next) { async function checkBasicAuth(req, res, next) {
const header = req.headers.authorization || ''; const header = req.headers.authorization || '';
const token = header.split(/\s+/).pop() || ''; const token = header.split(/\s+/).pop() || '';

View File

@ -6,7 +6,7 @@
</head> </head>
<body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;"> <body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
<noscript>Trilium requires JavaScript to be enabled.</noscript> <noscript>Trilium requires JavaScript to be enabled.</noscript>
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) minmax(0, <%= rightPaneWidthPercent %>fr)"> <div id="container" style="display: none; grid-template-columns: <%= leftPaneWidthPercent %>fr <%= rightPaneWidthPercent %>fr">
<div id="header" class="hide-toggle"> <div id="header" class="hide-toggle">
<div id="history-navigation" style="display: none;"> <div id="history-navigation" style="display: none;">
<a id="history-back-button" title="Go to previous note." class="icon-action jam jam-arrow-square-left"></a> <a id="history-back-button" title="Go to previous note." class="icon-action jam jam-arrow-square-left"></a>
@ -106,7 +106,7 @@
</div> </div>
</div> </div>
<div id="left-pane" class="hide-toggle"> <div id="left-pane" class="hide-toggle" style="resize: horizontal; overflow: scroll;">
<div id="global-buttons"> <div id="global-buttons">
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action jam jam-plus-circle"></a> <a id="create-top-level-note-button" title="Create new top level note" class="icon-action jam jam-plus-circle"></a>

7
src/views/sidebar.ejs Normal file
View File

@ -0,0 +1,7 @@
<div class="note-detail-sidebar">
<div style="text-align: center; margin-bottom: 10px;">
<button class="hide-sidebar-button" style="background: none; border: none;">hide sidebar <span class="jam jam-chevron-right"></span></button>
</div>
<div class="note-detail-widgets"></div>
</div>

View File

@ -4,42 +4,46 @@
<div id="note-tab-container"> <div id="note-tab-container">
<div class="note-tab-content note-tab-content-template"> <div class="note-tab-content note-tab-content-template">
<% include title.ejs %> <div class="note-detail-content">
<% include title.ejs %>
<div class="note-detail-script-area"></div> <div class="note-detail-script-area"></div>
<table class="note-detail-promoted-attributes"></table> <table class="note-detail-promoted-attributes"></table>
<div class="note-detail-component-wrapper"> <div class="note-detail-component-wrapper">
<div class="note-detail-text note-detail-component"> <div class="note-detail-text note-detail-component">
<div class="note-detail-text-editor" tabindex="10000"></div> <div class="note-detail-text-editor" tabindex="10000"></div>
</div>
<div class="note-detail-code note-detail-component">
<div class="note-detail-code-editor"></div>
</div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>
<% include details/file.ejs %>
<% include details/image.ejs %>
<% include details/relation_map.ejs %>
<% include details/protected_session_password.ejs %>
<div class="children-overview hide-toggle"></div>
</div> </div>
<div class="note-detail-code note-detail-component"> <div class="attribute-list">
<div class="note-detail-code-editor"></div> <button class="btn btn-sm show-attributes-button">Attributes:</button>
<span class="attribute-list-inner"></span>
</div> </div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>
<% include details/file.ejs %>
<% include details/image.ejs %>
<% include details/relation_map.ejs %>
<% include details/protected_session_password.ejs %>
<div class="children-overview hide-toggle"></div>
</div> </div>
<div class="attribute-list"> <% include sidebar.ejs %>
<button class="btn btn-sm show-attributes-button">Attributes:</button>
<span class="attribute-list-inner"></span>
</div>
</div> </div>
</div> </div>

View File

@ -73,6 +73,8 @@
<a class="dropdown-item show-note-info-button">Note info</a> <a class="dropdown-item show-note-info-button">Note info</a>
</div> </div>
</div> </div>
<button class="btn btn-sm icon-button jam jam-chevron-left show-sidebar-button" title="Show sidebar"></button>
</div> </div>
</div> </div>
</div> </div>