Interface improvements (#74)

* fix: interface style improvements

and components in ejs templates

* fix: improvement of improvements

* Redesign: kind of MD3

* Working url/search switch

* formatting

* Add switch animation, adjust sizes, adjust btn colors, rename bg2 to outline

* Format

---------

Co-authored-by: DarkCat09 <gh@dc09.ru>
This commit is contained in:
Artemy Egorov 2024-02-14 10:23:13 +03:00 committed by GitHub
parent ead78e79ab
commit 3b5f402d77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 250 additions and 183 deletions

50
package-lock.json generated
View File

@ -1,19 +1,19 @@
{ {
"name": "txtdot", "name": "txtdot",
"version": "1.5.1", "version": "1.5.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "txtdot", "name": "txtdot",
"version": "1.5.1", "version": "1.5.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/static": "^6.12.0", "@fastify/static": "^6.12.0",
"@fastify/swagger": "^8.13.0", "@fastify/swagger": "^8.13.0",
"@fastify/swagger-ui": "^1.10.2", "@fastify/swagger-ui": "^2.0.1",
"@fastify/view": "^8.2.0", "@fastify/view": "^8.2.0",
"@mozilla/readability": "^0.4.4", "@mozilla/readability": "^0.5.0",
"axios": "^1.6.5", "axios": "^1.6.5",
"dompurify": "^3.0.8", "dompurify": "^3.0.8",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
@ -21,24 +21,24 @@
"fastify": "^4.25.2", "fastify": "^4.25.2",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"ip-range-check": "^0.2.0", "ip-range-check": "^0.2.0",
"json-schema-to-ts": "^2.12.0", "json-schema-to-ts": "^3.0.0",
"linkedom": "^0.16.6", "linkedom": "^0.16.6",
"micromatch": "^4.0.5" "micromatch": "^4.0.5"
}, },
"devDependencies": { "devDependencies": {
"@types/dompurify": "^3.0.3", "@types/dompurify": "^3.0.5",
"@types/ejs": "^3.1.2", "@types/ejs": "^3.1.5",
"@types/jsdom": "^21.1.1", "@types/jsdom": "^21.1.6",
"@types/micromatch": "^4.0.2", "@types/micromatch": "^4.0.6",
"@types/node": "^20.4.10", "@types/node": "^20.10.6",
"@typescript-eslint/eslint-plugin": "^6.3.0", "@typescript-eslint/eslint-plugin": "^6.18.0",
"@typescript-eslint/parser": "^6.3.0", "@typescript-eslint/parser": "^6.18.0",
"clean-css-cli": "^5.6.2", "clean-css-cli": "^5.6.3",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"eslint": "^8.47.0", "eslint": "^8.56.0",
"prettier": "^3.1.0", "prettier": "^3.1.1",
"tsc-watch": "^6.0.4", "tsc-watch": "^6.0.4",
"typescript": "^5.1.6" "typescript": "^5.3.3"
} }
}, },
"node_modules/@aashutoshrathi/word-wrap": { "node_modules/@aashutoshrathi/word-wrap": {
@ -233,9 +233,9 @@
} }
}, },
"node_modules/@fastify/swagger-ui": { "node_modules/@fastify/swagger-ui": {
"version": "1.10.2", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.2.tgz", "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-2.0.1.tgz",
"integrity": "sha512-f2mRqtblm6eRAFQ3e8zSngxVNEtiYY7rISKQVjPA++ZsWc5WYlPVTb6Bx0G/zy0BIoucNqDr/Q2Vb/kTYkOq1A==", "integrity": "sha512-sQnufSdQ5kJxaTxBisWYQjkunECuRymYRZYEZEEPpmLUzzZoS22tDLVumb3c1TV4MAlD3L1LTLpxLSXcFL+OZw==",
"dependencies": { "dependencies": {
"@fastify/static": "^6.0.0", "@fastify/static": "^6.0.0",
"fastify-plugin": "^4.0.0", "fastify-plugin": "^4.0.0",
@ -317,9 +317,9 @@
} }
}, },
"node_modules/@mozilla/readability": { "node_modules/@mozilla/readability": {
"version": "0.4.4", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.4.4.tgz", "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.5.0.tgz",
"integrity": "sha512-MCgZyANpJ6msfvVMi6+A0UAsvZj//4OHREYUB9f2087uXHVoU+H+SWhuihvb1beKpM323bReQPRio0WNk2+V6g==", "integrity": "sha512-Z+CZ3QaosfFaTqvhQsIktyGrjFjSC0Fa4EMph4mqKnWhmyoGICsV/8QK+8HpXut6zV7zwfWwqDmEjtk1Qf6EgQ==",
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
} }
@ -2273,9 +2273,9 @@
} }
}, },
"node_modules/json-schema-to-ts": { "node_modules/json-schema-to-ts": {
"version": "2.12.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-2.12.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.0.0.tgz",
"integrity": "sha512-uTde38yBm5lzJSRPWRaasxZo72pb+JGE4iUksNdNfAkFaLhV4N9akeBxPPUpZy5onINt9Zo0oTLrAoEXyZESiQ==", "integrity": "sha512-2adDesYifYEXYxNySx3gG0RR69rDWIjqAFzK/JPXdOvjHLZ/UP6d2rkpy6a+AxyhtRp2SvFPZ4+EW36jBinUbA==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",

View File

@ -1,6 +1,6 @@
{ {
"name": "txtdot", "name": "txtdot",
"version": "1.5.1", "version": "1.5.2",
"private": true, "private": true,
"description": "", "description": "",
"main": "dist/app.js", "main": "dist/app.js",

View File

@ -1,5 +1,5 @@
export default { export default {
version: '1.5.1', version: '1.5.2',
description: description:
'txtdot is an HTTP proxy that parses only text, links and pictures from pages reducing internet bandwidth usage, removing ads and heavy scripts', 'txtdot is an HTTP proxy that parses only text, links and pictures from pages reducing internet bandwidth usage, removing ads and heavy scripts',
}; };

View File

@ -3,28 +3,34 @@
} }
:root { :root {
--bg: #fff; --bg: #fdfbff;
--fg: #111; --fg: #1a1c1e;
--bg2: #bbb; --outline: #74777f;
--fg2: #333;
--accent: #0070cc; /* hsl(207, 100%, 40%) */ --accent: #255fa4;
--accent-hl: #003866; /* hsl(207, 100%, 20%) */ --accent-hl: #003060;
--error: #ff9400; --btn-hl: #4178c1;
--btn2-hl: rgba(189, 199, 220, 0.5);
--error: #ff897d;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--bg: #222; --bg: #1a1c1e;
--fg: #eee; --fg: #e3e2e6;
--bg2: #444; --outline: #8e9199;
--fg2: #bbb;
--accent: #33a3ff; /* hsl(207, 100%, 60%) */ --accent: #a6c8ff;
--accent-hl: #99d1ff; /* hsl(207, 100%, 80%) */ --accent-hl: #d6e3ff;
--btn-hl: #79adf9;
--btn2-hl: rgba(189, 199, 220, 0.2);
--error: #ffb4ab;
} }
} }
@ -48,28 +54,41 @@ main {
.menu { .menu {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
column-gap: 0.25rem; flex-wrap: wrap;
gap: 0.375rem;
align-items: flex-start;
} }
.button { .button {
padding: 0.25rem 0.75rem; padding: 0.25rem 1rem;
border: 0.125rem solid var(--accent); border: none;
border-radius: 0.25rem; border-radius: 1rem;
background: var(--accent);
color: var(--bg);
background: var(--bg);
color: var(--fg);
text-decoration: none; text-decoration: none;
font-size: 1rem; font-size: 1rem;
cursor: pointer; cursor: pointer;
transition: background-color 0.15s ease-out;
} }
.button.secondary {
border-color: var(--bg2);
}
.button:hover { .button:hover {
background: var(--bg2); background: var(--btn-hl);
}
.button.secondary {
background: var(--bg);
color: var(--fg); color: var(--fg);
border: 1px solid var(--outline);
}
.button.secondary:hover {
background: var(--btn2-hl);
} }
a { a {

20
static/form-inputs.css Normal file
View File

@ -0,0 +1,20 @@
input[type='text'],
select {
outline: 0;
border: 0;
border-bottom: 1px var(--outline) solid;
padding: 0.25rem 0.25rem;
background: var(--bg);
color: var(--fg);
font-size: 1rem;
width: 100%;
transition: border-bottom 0.25s ease-out;
}
input[type='text']:focus,
select:focus {
border-bottom-color: var(--accent);
}

View File

@ -7,6 +7,7 @@
gap: 0.5rem 0.25rem; gap: 0.5rem 0.25rem;
width: fit-content; width: fit-content;
margin-top: 0.5rem;
} }
.input-row { .input-row {
@ -23,41 +24,83 @@
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
gap: 0.25rem; gap: 0.25rem;
font-size: 1rem;
} }
label { label {
font-size: 0.9rem; font-size: 0.9rem;
} }
#url, .switch-label {
#search { display: flex;
width: 100%; flex-direction: row;
height: 100%; /* shrink to #submit height */ align-items: center;
gap: 0.5rem;
cursor: pointer;
}
outline: none; .switch-btn {
border: 0; width: 3rem;
border-bottom: 0.125rem solid var(--fg2); height: 1.75rem;
border-radius: 1rem;
background: var(--outline);
padding: 0.375rem;
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
}
.switch-btn::before {
content: '';
display: inline-block;
width: 1rem;
height: 1rem;
transform: translateX(0);
transition:
transform 0.15s ease-out,
opacity 0.15s ease-out;
border-radius: 1rem;
background: var(--bg); background: var(--bg);
color: var(--fg);
font-size: 1rem;
}
#url::placeholder {
color: var(--fg2);
opacity: 1;
} }
#submit { .switch-btn:hover::before {
font-size: 1rem; opacity: 0.7;
} }
select { #switch-search {
border: 0; display: none;
border-bottom: 0.125rem solid var(--accent); }
background: var(--bg); #switch-search:checked ~ .switch-label > .switch-btn {
color: var(--fg); background: var(--accent);
}
font-weight: 500;
font-size: 0.9rem; #switch-search:checked ~ .switch-label > .switch-btn::before {
/* track width - handle width - (2 * horizontal track padding) */
transform: translateX(calc(3rem - 1rem - 2 * 0.375rem));
}
#switch-search:not(:checked) ~ .main-form-search {
display: none;
}
#switch-search:not(:checked) ~ .main-form-url {
display: grid;
}
#switch-search:checked ~ .main-form-search {
display: grid;
}
#switch-search:checked ~ .main-form-url {
display: none;
} }

View File

@ -1,9 +1,5 @@
.menu .button {
font-size: 0.9rem;
}
.title { .title {
border-bottom: 0.125rem solid var(--bg2); border-bottom: 0.125rem solid var(--outline);
padding-bottom: 0.125rem; padding-bottom: 0.125rem;
font-weight: 600; font-weight: 600;
} }
@ -11,6 +7,7 @@
a { a {
color: var(--accent); color: var(--accent);
} }
a:hover { a:hover {
color: var(--accent-hl); color: var(--accent-hl);
} }

View File

@ -1,25 +1,12 @@
.right { .form-search {
display: flex; display: flex;
flex-direction: row;
gap: 0.25rem 0.375rem;
margin-left: auto; margin-left: auto;
} }
#search { @media (max-width: 504px) {
width: 100%; .form-search {
height: 100%; /* shrink to #submit height */ margin: 0.25rem;
outline: none;
border: 0;
border-bottom: 0.125rem solid var(--fg2);
background: var(--bg);
color: var(--fg);
font-size: 1rem;
margin-right: 0.5rem;
margin-left: 0.5rem;
} }
#search::placeholder {
color: var(--fg2);
opacity: 1;
} }

View File

@ -0,0 +1,51 @@
<% search = config.search.enabled %>
<% if (search) { %>
<input type="checkbox" id="switch-search" checked>
<label for="switch-search" class="switch-label">
<span>URL</span>
<span class="switch-btn"></span>
<span>Search</span>
</label>
<form action="/search" method="get" class="input-grid main-form-search">
<div class="input">
<input type="text" name="q" id="search" placeholder="Search">
</div>
<div class="input">
<input type="submit" id="submit" class="button" value="Go">
</div>
</form>
<% } %>
<form action="/get" method="get" class="input-grid <%= search ? "main-form-url" : "" %>">
<div class="input">
<input type="text" name="url" id="url" placeholder="URL">
</div>
<div class="input">
<input type="submit" id="submit" class="button" value="Parse">
</div>
<div class="input-row">
<div class="input">
<label for="engine">Engine</label>
<select name="engine">
<option value="" selected>Default</option>
<% engineList.forEach((engine) => { %>
<option value="<%= engine %>">
<%= engine %>
</option>
<% }) %>
</select>
</div>
<div class="input">
<label for="format">Format</label>
<select name="format">
<option value="html" selected>HTML</option>
<option value="text">Text</option>
</select>
</div>
</div>
</form>

View File

@ -0,0 +1,14 @@
<div class="menu">
<a class="button secondary" href="/">Home</a>
<a class="button secondary" href="<%= remoteUrl %>">Original page</a>
<%
if (config.search.enabled) {
%>
<form class="form-search" action="/search" method="get">
<input type="text" name="q" id="search" placeholder="Search">
<input class="button" type="submit" value="Go"/>
</form>
<%
}
%>
</div>

View File

@ -0,0 +1,6 @@
<%
if (config.search.enabled) {
%><link rel="stylesheet" href="/static/search.css">
<link rel="stylesheet" href="/static/form-inputs.css"><%
}
%>

View File

@ -1,38 +1,19 @@
<!DOCTYPE html> <!doctype html>
<html lang="<%= parsed.lang %>"> <html lang="<%= parsed.lang %>">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow" />
<title><%= parsed.title %></title> <title><%= parsed.title %></title>
<link rel="stylesheet" href="/static/common.css"> <link rel="stylesheet" href="/static/common.css" />
<link rel="stylesheet" href="/static/get.css"> <link rel="stylesheet" href="/static/get.css" />
<% <%- include('./components/search-styles.ejs') %>
if (config.search.enabled) {
%><link rel="stylesheet" href="/static/search.css"><%
}
%>
</head> </head>
<body> <body>
<main> <main>
<div class="menu"> <%- include('./components/menu.ejs') %>
<a class="button secondary" href="/">Home</a> <p class="title"><%= parsed.title %></p>
<a class="button secondary" href="<%= remoteUrl %>">Original page</a>
<%
if (config.search.enabled) {
%>
<form class="right" action="/search" method="get">
<input type="text" name="q" id="search" placeholder="Search">
<input class="button" type="submit" value="Go"/>
</form>
<%
}
%>
</div>
<p class="title">
<%= parsed.title %>
</p>
<%- parsed.content %> <%- parsed.content %>
</main> </main>
</body> </body>

View File

@ -9,71 +9,20 @@
<link rel="stylesheet" href="/static/common.css"> <link rel="stylesheet" href="/static/common.css">
<link rel="stylesheet" href="/static/index.css"> <link rel="stylesheet" href="/static/index.css">
<link rel="stylesheet" href="/static/form.css"> <link rel="stylesheet" href="/static/form.css">
<link rel="stylesheet" href="/static/form-inputs.css">
</head> </head>
<body> <body>
<main> <main>
<header> <header>
<h1>txt<span class="dot">.</span></h1> <h1>txt<span class="dot">.</span></h1>
<p class="menu"> <div class="menu">
<a href="https://github.com/TxtDot/txtdot/releases/latest" class="button secondary">v<%= publicConfig.version %></a> <a href="https://github.com/TxtDot/txtdot/releases/latest" class="button secondary">v<%= publicConfig.version %></a>
<a href="https://github.com/txtdot/txtdot" class="button secondary">GitHub</a> <a href="https://github.com/txtdot/txtdot" class="button secondary">GitHub</a>
<a href="https://txtdot.github.io/documentation" class="button secondary">Docs</a> <a href="https://txtdot.github.io/documentation" class="button secondary">Docs</a>
</p> </div>
<p><%= publicConfig.description %></p> <p><%= publicConfig.description %></p>
</header> </header>
<% <%- include('./components/form-main.ejs') %>
if (config.search.enabled) {
%>
<form action="/search" method="get" class="input-grid">
<div class="input">
<input type="text" name="q" id="search" placeholder="Search">
</div>
<div class="input">
<input type="submit" id="submit" class="button" value="Go">
</div>
</form>
<%
%><details style="margin-top: 1rem;"><summary>Advanced</summary><%
}
%>
<form action="/get" method="get" class="input-grid">
<div class="input">
<input type="text" name="url" id="url" placeholder="URL">
</div>
<div class="input">
<input type="submit" id="submit" class="button" value="Parse">
</div>
<div class="input-row">
<div class="input">
<label for="engine">Engine</label>
<select name="engine">
<option value="" selected>Default</option>
<% engineList.forEach((engine)=> {
%>
<option value="<%= engine %>">
<%= engine %>
</option>
<%
})
%>
</select>
</div>
<div class="input">
<label for="format">Format</label>
<select name="format">
<option value="html" selected>HTML</option>
<option value="text">Text</option>
</select>
</div>
</div>
</form>
<%
if (config.search.enabled) {
%></details><%
}
%>
</main> </main>
</body> </body>
</html> </html>