Dark theme with two-lines design (#28)
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/chrome
|
||||
/src/node_modules
|
||||
/src/css
|
||||
/src/js
|
||||
/src/*.log
|
||||
|
2
Makefile
@@ -11,9 +11,9 @@ CHROME_FILES := manifest.json \
|
||||
*.css \
|
||||
*.png \
|
||||
popup/*.html \
|
||||
popup/*.css \
|
||||
popup/*.svg
|
||||
CHROME_FILES := $(wildcard $(addprefix src/,$(CHROME_FILES))) \
|
||||
src/css/popup.dist.css \
|
||||
src/js/background.dist.js \
|
||||
src/js/popup.dist.js \
|
||||
src/js/inject.dist.js
|
||||
|
@@ -1,11 +1,12 @@
|
||||
BROWSERIFY := node_modules/.bin/browserify
|
||||
PRETTIER := node_modules/.bin/prettier
|
||||
LESSC := node_modules/.bin/lessc
|
||||
|
||||
CLEAN_FILES := js
|
||||
PRETTIER_FILES := $(wildcard *.js popup/*.js *.css popup/*.css)
|
||||
PRETTIER_FILES := $(wildcard *.js popup/*.js *.less popup/*.less)
|
||||
|
||||
.PHONY: all
|
||||
all: deps prettier js/background.dist.js js/popup.dist.js js/inject.dist.js
|
||||
all: deps prettier css/popup.dist.css js/background.dist.js js/popup.dist.js js/inject.dist.js
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
@@ -15,6 +16,10 @@ deps:
|
||||
prettier: $(PRETTIER) $(PRETTIER_FILES)
|
||||
$(PRETTIER) --write $(PRETTIER_FILES)
|
||||
|
||||
css/popup.dist.css: $(LESSC) popup/popup.less
|
||||
[ -d css ] || mkdir -p css
|
||||
$(LESSC) popup/popup.less css/popup.dist.css
|
||||
|
||||
js/background.dist.js: $(BROWSERIFY) background.js
|
||||
[ -d js ] || mkdir -p js
|
||||
$(BROWSERIFY) -o js/background.dist.js background.js
|
||||
|
@@ -401,7 +401,7 @@ async function receiveMessage(message, sender, sendResponse) {
|
||||
// no user-configured stores, so use the default store
|
||||
settings.stores.default = {
|
||||
id: "default",
|
||||
name: "default",
|
||||
name: "pass",
|
||||
path: response.data.defaultStore.path,
|
||||
settings: response.data.defaultStore.settings
|
||||
};
|
||||
|
@@ -1,17 +0,0 @@
|
||||
html,
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #0a0;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
margin: 0 6px;
|
||||
padding: 1px 4px;
|
||||
}
|
@@ -15,15 +15,16 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"chrome-extension-async": "^3.0.0",
|
||||
"fuzzysort": "^1.1.0",
|
||||
"chrome-extension-async": "^3.3.0",
|
||||
"fuzzysort": "^1.1.4",
|
||||
"mithril": "^1.1.0",
|
||||
"moment": "^2.22.1",
|
||||
"moment": "^2.22.2",
|
||||
"sha1": "^1.1.1",
|
||||
"tldjs": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^16.2.0",
|
||||
"prettier": "^1.12.0"
|
||||
"browserify": "^16.2.3",
|
||||
"less": "^3.8.1",
|
||||
"prettier": "^1.14.3"
|
||||
}
|
||||
}
|
||||
|
@@ -1,44 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32.025562 24.291258"
|
||||
height="24.291258"
|
||||
width="32.025562"
|
||||
id="svg2"
|
||||
version="1.1">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<path
|
||||
id="path817"
|
||||
d="M 32,0 H 7.676162 L 0,11.793103 l 7.916042,12.464255 24.109522,0.0339 z"
|
||||
style="fill:#d40000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00038135px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<g
|
||||
transform="matrix(0.74244039,0,0,0.68917867,4.4293442,0.9133566)"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="g825">
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 12.471062,8.3881031 27.653541,23.611032"
|
||||
id="path819" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 27.620082,8.3808186 12.36939,23.630587"
|
||||
id="path821" />
|
||||
</g>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.025562 24.291258">
|
||||
<path d="M 32,0 H 7.676162 L 0,11.793103 l 7.916042,12.464255 24.109522,0.0339 z"
|
||||
style="fill:#b40000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00038135px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<g transform="matrix(0.74244039,0,0,0.68917867,4.4293442,0.9133566)"
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
|
||||
<path d="M 12.471062,8.3881031 27.653541,23.611032"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path d="M 27.620082,8.3808186 12.36939,23.630587"
|
||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1,4 @@
|
||||
<?xml version="1.0" ?><svg height="21px" version="1.1" viewBox="0 0 20 21" width="20px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><desc/><defs/><g fill="none" fill-rule="evenodd" id="Page-1" stroke="none" stroke-width="1"><g fill="#000000" id="Core" opacity="0.9" transform="translate(-464.000000, -254.000000)"><g id="history" transform="translate(464.000000, 254.500000)"><path d="M10.5,0 C7,0 3.9,1.9 2.3,4.8 L0,2.5 L0,9 L6.5,9 L3.7,6.2 C5,3.7 7.5,2 10.5,2 C14.6,2 18,5.4 18,9.5 C18,13.6 14.6,17 10.5,17 C7.2,17 4.5,14.9 3.4,12 L1.3,12 C2.4,16 6.1,19 10.5,19 C15.8,19 20,14.7 20,9.5 C20,4.3 15.7,0 10.5,0 L10.5,0 Z M9,5 L9,10.1 L13.7,12.9 L14.5,11.6 L10.5,9.2 L10.5,5 L9,5 L9,5 Z" id="Shape"/></g></g></g></svg>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 21" stroke="#c4c4c4" fill="#c4c4c4">
|
||||
<path d="M10.5,0 C7,0 3.9,1.9 2.3,4.8 L0,2.5 L0,9 L6.5,9 L3.7,6.2 C5,3.7 7.5,2 10.5,2 C14.6,2 18,5.4 18,9.5 C18,13.6 14.6,17 10.5,17 C7.2,17 4.5,14.9 3.4,12 L1.3,12 C2.4,16 6.1,19 10.5,19 C15.8,19 20,14.7 20,9.5 C20,4.3 15.7,0 10.5,0 L10.5,0 Z M9,5 L9,10.1 L13.7,12.9 L14.5,11.6 L10.5,9.2 L10.5,5 L9,5 L9,5 Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 813 B After Width: | Height: | Size: 438 B |
@@ -1 +1,4 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 100 100" height="100px" id="Layer_1" version="1.1" viewBox="0 0 100 100" width="100px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Captions"/><path d="M70,0C53.432,0,40,13.432,40,30v10L30,50L20,60L10,70L0,80v10v10h10h10V90h10V80h10V70h10V60h10h10 c16.568,0,30-13.432,30-30S86.568,0,70,0z M70,40c-5.522,0-10-4.478-10-10s4.478-10,10-10s10,4.478,10,10S75.522,40,70,40z"/></svg>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" stroke="#c4c4c4" fill="#c4c4c4">
|
||||
<path d="M70,0C53.432,0,40,13.432,40,30v10L30,50L20,60L10,70L0,80v10v10h10h10V90h10V80h10V70h10V60h10h10c16.568,0,30-13.432,30-30S86.568,0,70,0z M70,40c-5.522,0-10-4.478-10-10s4.478-10,10-10s10,4.478,10,10S75.522,40,70,40z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 354 B |
@@ -1,11 +1,4 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="459px" height="459px" viewBox="0 0 459 459" style="enable-background:new 0 0 459 459;" xml:space="preserve">
|
||||
<g>
|
||||
<g id="share">
|
||||
<path d="M459,216.75L280.5,38.25v102c-178.5,25.5-255,153-280.5,280.5C63.75,331.5,153,290.7,280.5,290.7v104.55L459,216.75z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 459 459" stroke="#c4c4c4" fill="#c4c4c4">
|
||||
<path d="M459,216.75L280.5,38.25v102c-178.5,25.5-255,153-280.5,280.5C63.75,331.5,153,290.7,280.5,290.7v104.55L459,216.75z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 657 B After Width: | Height: | Size: 253 B |
@@ -1 +1,6 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 100 100" height="100px" id="Layer_1" version="1.1" viewBox="0 0 100 100" width="100px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Captions"/><path d="M64.238,54.239C67.879,48.719,70,42.107,70,35C70,15.67,54.33,0,35,0S0,15.67,0,35s15.67,35,35,35 c7.107,0,13.718-2.121,19.238-5.761L90,100l10-10L64.238,54.239z M10,35c0-13.785,11.215-25,25-25c13.785,0,25,11.215,25,25 S48.785,60,35,60C21.215,60,10,48.785,10,35z"/></svg>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" stroke="#c4c4c4" fill="#c4c4c4">
|
||||
<path d="M64.238,54.239C67.879,48.719,70,42.107,70,35C70,15.67,54.33,0,35,0S0,15.67,0,35s15.67,35,35,35
|
||||
c7.107,0,13.718-2.121,19.238-5.761L90,100l10-10L64.238,54.239zM10,35c0-13.785,11.215-25,25-25c13.785,
|
||||
0,25,11.215,25,25S48.785,60,35,60C21.215,60,10,48.785,10,35z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 414 B |
@@ -1,15 +1,11 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 350 350" style="enable-background:new 0 0 350 350;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M175,171.173c38.914,0,70.463-38.318,70.463-85.586C245.463,38.318,235.105,0,175,0s-70.465,38.318-70.465,85.587
|
||||
C104.535,132.855,136.084,171.173,175,171.173z"/>
|
||||
<path d="M41.909,301.853C41.897,298.971,41.885,301.041,41.909,301.853L41.909,301.853z"/>
|
||||
<path d="M308.085,304.104C308.123,303.315,308.098,298.63,308.085,304.104L308.085,304.104z"/>
|
||||
<path d="M307.935,298.397c-1.305-82.342-12.059-105.805-94.352-120.657c0,0-11.584,14.761-38.584,14.761
|
||||
s-38.586-14.761-38.586-14.761c-81.395,14.69-92.803,37.805-94.303,117.982c-0.123,6.547-0.18,6.891-0.202,6.131
|
||||
c0.005,1.424,0.011,4.058,0.011,8.651c0,0,19.592,39.496,133.08,39.496c113.486,0,133.08-39.496,133.08-39.496
|
||||
c0-2.951,0.002-5.003,0.005-6.399C308.062,304.575,308.018,303.664,307.935,298.397z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 350" stroke="#c4c4c4" fill="#c4c4c4">
|
||||
<path d="M175,171.173c38.914,0,70.463-38.318,70.463-85.586C245.463,38.318,235.105,0,175,0s-70.465,38.318-70.465,85.587
|
||||
C104.535,132.855,136.084,171.173,175,171.173z"/>
|
||||
<path d="M41.909,301.853C41.897,298.971,41.885,301.041,41.909,301.853L41.909,301.853z"/>
|
||||
<path d="M308.085,304.104C308.123,303.315,308.098,298.63,308.085,304.104L308.085,304.104z"/>
|
||||
<path d="M307.935,298.397c-1.305-82.342-12.059-105.805-94.352-120.657c0,0-11.584,14.761-38.584,14.761
|
||||
s-38.586-14.761-38.586-14.761c-81.395,14.69-92.803,37.805-94.303,117.982c-0.123,6.547-0.18,6.891-0.202,6.131
|
||||
c0.005,1.424,0.011,4.058,0.011,8.651c0,0,19.592,39.496,133.08,39.496c113.486,0,133.08-39.496,133.08-39.496
|
||||
c0-2.951,0.002-5.003,0.005-6.399C308.062,304.575,308.018,303.664,307.935,298.397z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 925 B |
@@ -53,7 +53,6 @@ function attach(element) {
|
||||
* @return []Vnode
|
||||
*/
|
||||
function view(ctl, params) {
|
||||
var badges = Object.keys(this.settings.stores).length > 1;
|
||||
var nodes = [];
|
||||
nodes.push(m(this.searchPart));
|
||||
|
||||
@@ -77,20 +76,23 @@ function view(ctl, params) {
|
||||
onkeydown: keyHandler.bind(result)
|
||||
},
|
||||
[
|
||||
badges ? m("div.store.badge", result.store.name) : null,
|
||||
m("div.name", [
|
||||
m.trust(result.display),
|
||||
result.recent.when > 0
|
||||
? m("div.recent", {
|
||||
title:
|
||||
"Used here " +
|
||||
result.recent.count +
|
||||
" time" +
|
||||
(result.recent.count > 1 ? "s" : "") +
|
||||
", last used " +
|
||||
Moment(new Date(result.recent.when)).fromNow()
|
||||
})
|
||||
: null
|
||||
m("div.line1", [
|
||||
m("div.store.badge", result.store.name),
|
||||
m("div.path", [m.trust(result.path)]),
|
||||
result.recent.when > 0
|
||||
? m("div.recent", {
|
||||
title:
|
||||
"Used here " +
|
||||
result.recent.count +
|
||||
" time" +
|
||||
(result.recent.count > 1 ? "s" : "") +
|
||||
", last " +
|
||||
Moment(new Date(result.recent.when)).fromNow()
|
||||
})
|
||||
: null
|
||||
]),
|
||||
m("div.line2", [m.trust(result.display)])
|
||||
]),
|
||||
m("div.action.copy-password", {
|
||||
tabindex: 0,
|
||||
@@ -119,7 +121,7 @@ function view(ctl, params) {
|
||||
/**
|
||||
* Run a search
|
||||
*
|
||||
* @param string s Search string
|
||||
* @param string s Search string
|
||||
* @return void
|
||||
*/
|
||||
function search(s) {
|
||||
@@ -128,7 +130,13 @@ function search(s) {
|
||||
s = s.trim();
|
||||
|
||||
// get candidate list
|
||||
var candidates = this.logins.map(result => Object.assign(result, { display: result.login }));
|
||||
var candidates = this.logins.map(candidate => {
|
||||
let lastSlashIndex = candidate.login.lastIndexOf("/") + 1;
|
||||
return Object.assign(candidate, {
|
||||
path: candidate.login.substr(0, lastSlashIndex),
|
||||
display: candidate.login.substr(lastSlashIndex)
|
||||
});
|
||||
});
|
||||
var mostRecent = null;
|
||||
if (this.currentDomainOnly) {
|
||||
var recent = candidates.filter(function(login) {
|
||||
@@ -166,31 +174,128 @@ function search(s) {
|
||||
});
|
||||
|
||||
if (s.length) {
|
||||
var filter = s.split(/\s+/);
|
||||
// fuzzy-search first word & add highlighting
|
||||
if (fuzzyFirstWord) {
|
||||
candidates = FuzzySort.go(filter[0], candidates, {
|
||||
let filter = s.split(/\s+/);
|
||||
let fuzzyFilter = fuzzyFirstWord ? filter[0] : "";
|
||||
let substringFilters = filter.slice(fuzzyFirstWord ? 1 : 0).map(w => w.toLowerCase());
|
||||
|
||||
// First reduce the list by running the substring search
|
||||
substringFilters.forEach(function(word) {
|
||||
candidates = candidates.filter(c => c.login.toLowerCase().indexOf(word) >= 0);
|
||||
});
|
||||
|
||||
// Then run the fuzzy filter
|
||||
let fuzzyResults = {};
|
||||
if (fuzzyFilter) {
|
||||
candidates = FuzzySort.go(fuzzyFilter, candidates, {
|
||||
keys: ["login", "store.name"],
|
||||
allowTypo: false
|
||||
}).map(result =>
|
||||
Object.assign(result.obj, {
|
||||
display: result[0]
|
||||
? FuzzySort.highlight(result[0], "<em>", "</em>")
|
||||
: result.obj.login
|
||||
})
|
||||
);
|
||||
}).map(result => {
|
||||
fuzzyResults[result.obj.login] = result;
|
||||
return result.obj;
|
||||
});
|
||||
}
|
||||
// substring-search to refine against each remaining word
|
||||
filter.slice(fuzzyFirstWord ? 1 : 0).forEach(function(word) {
|
||||
candidates = candidates.filter(
|
||||
login => login.login.toLowerCase().indexOf(word.toLowerCase()) >= 0
|
||||
);
|
||||
});
|
||||
|
||||
// Finally highlight all matches
|
||||
candidates = candidates.map(c => highlightMatches(c, fuzzyResults, substringFilters));
|
||||
}
|
||||
|
||||
// Prefix root entries with slash to let them have some visible path
|
||||
candidates.forEach(c => {
|
||||
c.path = c.path || "/";
|
||||
});
|
||||
|
||||
this.results = candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight filter matches
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param object entry password entry
|
||||
* @param object fuzzyResults positions of fuzzy filter matches
|
||||
* @param array substringFilters list of substring filters applied
|
||||
* @return object entry with highlighted matches
|
||||
*/
|
||||
function highlightMatches(entry, fuzzyResults, substringFilters) {
|
||||
// Add all positions of the fuzzy search to the array
|
||||
let matches = (fuzzyResults[entry.login] && fuzzyResults[entry.login][0]
|
||||
? fuzzyResults[entry.login][0].indexes
|
||||
: []
|
||||
).slice();
|
||||
|
||||
// Add all positions of substring searches to the array
|
||||
let login = entry.login.toLowerCase();
|
||||
for (let word of substringFilters) {
|
||||
let startIndex = login.indexOf(word);
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
matches.push(startIndex + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the final array of matches before
|
||||
matches = sortUnique(matches, (a, b) => a - b);
|
||||
|
||||
const OPEN = "<em>";
|
||||
const CLOSE = "</em>";
|
||||
let highlighted = "";
|
||||
var matchesIndex = 0;
|
||||
var opened = false;
|
||||
for (var i = 0; i < entry.login.length; ++i) {
|
||||
var char = entry.login[i];
|
||||
|
||||
if (i == entry.path.length) {
|
||||
if (opened) {
|
||||
highlighted += CLOSE;
|
||||
}
|
||||
var path = highlighted;
|
||||
highlighted = "";
|
||||
if (opened) {
|
||||
highlighted += OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches[matchesIndex] === i) {
|
||||
matchesIndex++;
|
||||
if (!opened) {
|
||||
opened = true;
|
||||
highlighted += OPEN;
|
||||
}
|
||||
} else {
|
||||
if (opened) {
|
||||
opened = false;
|
||||
highlighted += CLOSE;
|
||||
}
|
||||
}
|
||||
highlighted += char;
|
||||
}
|
||||
if (opened) {
|
||||
opened = false;
|
||||
highlighted += CLOSE;
|
||||
}
|
||||
let display = highlighted;
|
||||
|
||||
return Object.assign(entry, {
|
||||
path: path,
|
||||
display: display
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort and remove duplicates
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param array array items to sort
|
||||
* @param function comparator sort comparator
|
||||
* @return array sorted items without duplicates
|
||||
*/
|
||||
function sortUnique(array, comparator) {
|
||||
return array
|
||||
.sort(comparator)
|
||||
.filter((elem, index, arr) => index == !arr.length || arr[index - 1] != elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle result key presses
|
||||
*
|
||||
|
@@ -1,162 +0,0 @@
|
||||
html,
|
||||
body {
|
||||
min-width: 260px;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar,
|
||||
body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logins {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
height: inherit;
|
||||
max-height: 217px; /* 7 x 31px login part*/
|
||||
}
|
||||
|
||||
.part {
|
||||
border-bottom: 1px solid #eee;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.part:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.part > .badge:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.part.error {
|
||||
color: #f00;
|
||||
white-space: normal;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.part.notice {
|
||||
color: #090;
|
||||
white-space: normal;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.part.search {
|
||||
padding: 6px 28px 6px 6px;
|
||||
background-image: url("icon-search.svg");
|
||||
background-position: top 6px right 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 18px;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.part.search:focus-within {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.part.search > .hint {
|
||||
background-color: #66c;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.part.search > .hint > .remove-hint {
|
||||
background-image: url("icon-bs-delete.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
height: 12px;
|
||||
margin: 3px 0 3px 4px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.part.search > input[type="text"] {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.part.login {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.logins:not(:hover):not(:focus-within) .part.login:first-child > .name {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.part.login:hover,
|
||||
.part.login:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.part.login > .name:hover,
|
||||
.part.login > .name:focus,
|
||||
.part.login > .action:hover,
|
||||
.part.login > .action:focus,
|
||||
.part.login:focus > .name {
|
||||
background-color: #eee;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.part.login > .name {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 19px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.part.login > .name > .recent {
|
||||
background-image: url("icon-history.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
margin-left: 4px;
|
||||
width: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.part.login > .action {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 19px;
|
||||
width: 30px;
|
||||
height: 19px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.past.login > .action:hover {
|
||||
background-color: #f00;
|
||||
}
|
||||
|
||||
.part.login > .action.copy-password {
|
||||
background-image: url("icon-key.svg");
|
||||
}
|
||||
|
||||
.part.login > .action.copy-user {
|
||||
background-image: url("icon-user.svg");
|
||||
}
|
||||
|
||||
.part.login > .action.launch {
|
||||
background-image: url("icon-launch.svg");
|
||||
background-position-y: 5.5px;
|
||||
}
|
||||
|
||||
.part.login em {
|
||||
color: #00c;
|
||||
font-style: normal;
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="../global.css" />
|
||||
<link rel="stylesheet" type="text/css" href="popup.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../css/popup.dist.css" />
|
||||
<script src="../js/popup.dist.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
209
src/popup/popup.less
Normal file
@@ -0,0 +1,209 @@
|
||||
@bg-color: #414141;
|
||||
@default-bg-color: #393939;
|
||||
@hover-bg-color: #363636;
|
||||
@text-color: #c4c4c4;
|
||||
@error-text-color: #f00;
|
||||
@dim-text-color: #808080;
|
||||
|
||||
@input-bg-color: #4a4a4a;
|
||||
@active-input-bg-color: #4f4f4f;
|
||||
@input-text-color: #eee;
|
||||
|
||||
@hint-bg-color: #d79921;
|
||||
@hint-color: #363636;
|
||||
|
||||
@match-text-color: @hint-bg-color;
|
||||
|
||||
@login-height: 53px;
|
||||
@max-logins-height: @login-height * 7;
|
||||
@login-part-padding: 6px;
|
||||
@login-part-height: @login-height - 2 * @login-part-padding;
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: sans;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-width: 260px;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
background-color: @bg-color;
|
||||
color: @text-color;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar,
|
||||
body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logins {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
height: inherit;
|
||||
max-height: @max-logins-height;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #626262;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin-right: 8px;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
|
||||
.part {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.part:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.part > .badge:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.part.error {
|
||||
color: @error-text-color;
|
||||
white-space: normal;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.part.notice {
|
||||
white-space: normal;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.part.search {
|
||||
padding: 6px 28px 6px 6px;
|
||||
background-image: url("/popup/icon-search.svg");
|
||||
background-position: top 6px right 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 18px;
|
||||
background-color: @input-bg-color;
|
||||
}
|
||||
|
||||
.part.search:focus-within {
|
||||
background-color: @active-input-bg-color;
|
||||
}
|
||||
|
||||
.part.search > .hint {
|
||||
background-color: @hint-bg-color;
|
||||
color: @hint-color;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.part.search > .hint > .remove-hint {
|
||||
background-image: url("/popup/icon-bs-delete.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
cursor: pointer;
|
||||
height: 12px;
|
||||
margin: 3px 0 3px 4px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.part.search > input[type="text"] {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
color: @input-text-color;
|
||||
}
|
||||
|
||||
.part.search > input[type="text"]::placeholder {
|
||||
color: @dim-text-color;
|
||||
}
|
||||
|
||||
.logins:not(:hover):not(:focus-within) .part.login:first-child > .name {
|
||||
background-color: @default-bg-color;
|
||||
}
|
||||
|
||||
.part.login > .name:hover,
|
||||
.part.login > .name:focus,
|
||||
.part.login > .action:hover,
|
||||
.part.login > .action:focus,
|
||||
.part.login:focus > .name {
|
||||
background-color: @hover-bg-color;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.part.login {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
height: @login-height;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: @login-part-height;
|
||||
padding: @login-part-padding;
|
||||
|
||||
.line1 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 12px;
|
||||
|
||||
.recent {
|
||||
background-image: url("/popup/icon-history.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
margin-left: 8px;
|
||||
width: 9.5px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.line2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 19px;
|
||||
width: 30px;
|
||||
height: @login-part-height;
|
||||
padding: @login-part-padding;
|
||||
|
||||
&.copy-password {
|
||||
background-image: url("/popup/icon-key.svg");
|
||||
}
|
||||
|
||||
&.copy-user {
|
||||
background-image: url("/popup/icon-user.svg");
|
||||
}
|
||||
|
||||
&.launch {
|
||||
background-image: url("/popup/icon-launch.svg");
|
||||
background-position-y: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.part.login em {
|
||||
color: @match-text-color;
|
||||
font-style: normal;
|
||||
}
|