feat: begin adding settings menu, move theme to settings
This commit is contained in:
parent
9e6e763064
commit
c3ed901738
18 changed files with 444 additions and 19 deletions
|
|
@ -13,6 +13,8 @@
|
||||||
#include "Routes/ImageProxy.h"
|
#include "Routes/ImageProxy.h"
|
||||||
#include "Routes/Images.h"
|
#include "Routes/Images.h"
|
||||||
#include "Routes/Search.h"
|
#include "Routes/Search.h"
|
||||||
|
#include "Routes/Settings.h"
|
||||||
|
#include "Routes/SettingsSave.h"
|
||||||
#include "Scraping/Scraping.h"
|
#include "Scraping/Scraping.h"
|
||||||
|
|
||||||
Config global_config;
|
Config global_config;
|
||||||
|
|
@ -97,6 +99,8 @@ int main() {
|
||||||
set_handler("/search", results_handler);
|
set_handler("/search", results_handler);
|
||||||
set_handler("/images", images_handler);
|
set_handler("/images", images_handler);
|
||||||
set_handler("/proxy", image_proxy_handler);
|
set_handler("/proxy", image_proxy_handler);
|
||||||
|
set_handler("/settings", settings_handler);
|
||||||
|
set_handler("/save_settings", settings_save_handler);
|
||||||
|
|
||||||
fprintf(stderr, "[INFO] Starting Omnisearch on %s:%d\n", cfg.host, cfg.port);
|
fprintf(stderr, "[INFO] Starting Omnisearch on %s:%d\n", cfg.host, cfg.port);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
#include "Home.h"
|
#include "Home.h"
|
||||||
|
#include "../Utility/Utility.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int home_handler(UrlParams *params) {
|
int home_handler(UrlParams *params) {
|
||||||
(void)params;
|
(void)params;
|
||||||
|
char *theme = get_theme("");
|
||||||
|
|
||||||
TemplateContext ctx = new_context();
|
TemplateContext ctx = new_context();
|
||||||
|
context_set(&ctx, "theme", theme);
|
||||||
char *rendered_html = render_template("home.html", &ctx);
|
char *rendered_html = render_template("home.html", &ctx);
|
||||||
send_response(rendered_html);
|
send_response(rendered_html);
|
||||||
|
|
||||||
free(rendered_html);
|
free(rendered_html);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
|
free(theme);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Images.h"
|
#include "Images.h"
|
||||||
#include "../Scraping/ImageScraping.h"
|
#include "../Scraping/ImageScraping.h"
|
||||||
#include "../Utility/Unescape.h"
|
#include "../Utility/Unescape.h"
|
||||||
|
#include "../Utility/Utility.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
int images_handler(UrlParams *params) {
|
int images_handler(UrlParams *params) {
|
||||||
|
|
@ -29,6 +30,11 @@ int images_handler(UrlParams *params) {
|
||||||
snprintf(two_prev_str, sizeof(two_prev_str), "%d", page > 2 ? page - 2 : 0);
|
snprintf(two_prev_str, sizeof(two_prev_str), "%d", page > 2 ? page - 2 : 0);
|
||||||
snprintf(two_next_str, sizeof(two_next_str), "%d", page + 2);
|
snprintf(two_next_str, sizeof(two_next_str), "%d", page + 2);
|
||||||
context_set(&ctx, "query", raw_query);
|
context_set(&ctx, "query", raw_query);
|
||||||
|
|
||||||
|
char *theme = get_theme("");
|
||||||
|
context_set(&ctx, "theme", theme);
|
||||||
|
free(theme);
|
||||||
|
|
||||||
context_set(&ctx, "page", page_str);
|
context_set(&ctx, "page", page_str);
|
||||||
context_set(&ctx, "prev_page", prev_str);
|
context_set(&ctx, "prev_page", prev_str);
|
||||||
context_set(&ctx, "next_page", next_str);
|
context_set(&ctx, "next_page", next_str);
|
||||||
|
|
@ -39,7 +45,7 @@ int images_handler(UrlParams *params) {
|
||||||
context_set(&ctx, "query", display_query);
|
context_set(&ctx, "query", display_query);
|
||||||
|
|
||||||
if (!raw_query || strlen(raw_query) == 0) {
|
if (!raw_query || strlen(raw_query) == 0) {
|
||||||
send_response("<h1>No query provided</h1>");
|
send_redirect("/");
|
||||||
if (display_query)
|
if (display_query)
|
||||||
free(display_query);
|
free(display_query);
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../Scraping/Scraping.h"
|
#include "../Scraping/Scraping.h"
|
||||||
#include "../Utility/Display.h"
|
#include "../Utility/Display.h"
|
||||||
#include "../Utility/Unescape.h"
|
#include "../Utility/Unescape.h"
|
||||||
|
#include "../Utility/Utility.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
@ -401,12 +402,17 @@ int results_handler(UrlParams *params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
context_set(&ctx, "query", raw_query);
|
context_set(&ctx, "query", raw_query);
|
||||||
|
|
||||||
|
char *theme = get_theme("");
|
||||||
|
context_set(&ctx, "theme", theme);
|
||||||
|
free(theme);
|
||||||
|
|
||||||
char page_str[16];
|
char page_str[16];
|
||||||
snprintf(page_str, sizeof(page_str), "%d", page);
|
snprintf(page_str, sizeof(page_str), "%d", page);
|
||||||
context_set(&ctx, "page", page_str);
|
context_set(&ctx, "page", page_str);
|
||||||
|
|
||||||
if (!raw_query || strlen(raw_query) == 0) {
|
if (!raw_query || strlen(raw_query) == 0) {
|
||||||
send_response("<h1>No query provided</h1>");
|
send_redirect("/");
|
||||||
free_context(&ctx);
|
free_context(&ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
src/Routes/Settings.c
Normal file
30
src/Routes/Settings.c
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "../Utility/Utility.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int settings_handler(UrlParams *params) {
|
||||||
|
const char *query = "";
|
||||||
|
if (params) {
|
||||||
|
for (int i = 0; i < params->count; i++) {
|
||||||
|
if (strcmp(params->params[i].key, "q") == 0) {
|
||||||
|
query = params->params[i].value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *theme = get_theme("system");
|
||||||
|
|
||||||
|
TemplateContext ctx = new_context();
|
||||||
|
context_set(&ctx, "query", query);
|
||||||
|
context_set(&ctx, "theme", theme);
|
||||||
|
char *rendered_html = render_template("settings.html", &ctx);
|
||||||
|
send_response(rendered_html);
|
||||||
|
|
||||||
|
free(rendered_html);
|
||||||
|
free(theme);
|
||||||
|
free_context(&ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
8
src/Routes/Settings.h
Normal file
8
src/Routes/Settings.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef SETTINGS_H
|
||||||
|
#define SETTINGS_H
|
||||||
|
|
||||||
|
#include <beaker.h>
|
||||||
|
|
||||||
|
int settings_handler(UrlParams *params);
|
||||||
|
|
||||||
|
#endif
|
||||||
28
src/Routes/SettingsSave.c
Normal file
28
src/Routes/SettingsSave.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "SettingsSave.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int settings_save_handler(UrlParams *params) {
|
||||||
|
const char *theme = "";
|
||||||
|
const char *query = "";
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
for (int i = 0; i < params->count; i++) {
|
||||||
|
if (strcmp(params->params[i].key, "theme") == 0) {
|
||||||
|
theme = params->params[i].value;
|
||||||
|
} else if (strcmp(params->params[i].key, "q") == 0) {
|
||||||
|
query = params->params[i].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(theme) > 0) {
|
||||||
|
set_cookie("theme", theme, "Fri, 31 Dec 2038 23:59:59 GMT", "/", false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
char redirect_url[512];
|
||||||
|
snprintf(redirect_url, sizeof(redirect_url), "/settings?q=%s", query);
|
||||||
|
send_redirect(redirect_url);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
8
src/Routes/SettingsSave.h
Normal file
8
src/Routes/SettingsSave.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef SETTINGS_SAVE_H
|
||||||
|
#define SETTINGS_SAVE_H
|
||||||
|
|
||||||
|
#include <beaker.h>
|
||||||
|
|
||||||
|
int settings_save_handler(UrlParams *params);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
#include "Utility.h"
|
#include "Utility.h"
|
||||||
|
#include <beaker.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
int hex_to_int(char c) {
|
int hex_to_int(char c) {
|
||||||
if (c >= '0' && c <= '9')
|
if (c >= '0' && c <= '9')
|
||||||
|
|
@ -9,3 +12,14 @@ int hex_to_int(char c) {
|
||||||
return c - 'A' + 10;
|
return c - 'A' + 10;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *get_theme(const char *default_theme) {
|
||||||
|
char *cookie = get_cookie("theme");
|
||||||
|
if (cookie &&
|
||||||
|
(strcmp(cookie, "light") == 0 ||
|
||||||
|
strcmp(cookie, "dark") == 0)) {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
free(cookie);
|
||||||
|
return strdup(default_theme);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@
|
||||||
#define UTILITY_H
|
#define UTILITY_H
|
||||||
|
|
||||||
int hex_to_int(char c);
|
int hex_to_int(char c);
|
||||||
|
char *get_theme(const char *default_theme);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
232
static/main.css
232
static/main.css
|
|
@ -25,12 +25,19 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color:var(--bg-main);
|
background-color:var(--bg-main);
|
||||||
|
background-image:radial-gradient(circle at top right, var(--bg-card) 0%, var(--bg-main) 100%);
|
||||||
|
background-attachment:fixed;
|
||||||
color:var(--text-primary);
|
color:var(--text-primary);
|
||||||
font-family:system-ui,-apple-system,sans-serif;
|
font-family:system-ui,-apple-system,sans-serif;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
|
min-height:100%;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,13 +51,12 @@ img[src=""] {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: radial-gradient(circle at top right, var(--bg-card) 0%, var(--bg-main) 100%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-home .container {
|
.view-home .container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 580px;
|
max-width: 580px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -100,11 +106,56 @@ img[src=""] {
|
||||||
background:var(--bg-card);
|
background:var(--bg-card);
|
||||||
color:var(--text-primary);
|
color:var(--text-primary);
|
||||||
border-color:var(--border);
|
border-color:var(--border);
|
||||||
|
text-decoration:none;
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
padding:10px 24px;
|
||||||
|
border-radius:8px;
|
||||||
|
font-weight:600;
|
||||||
|
font-size:0.9rem;
|
||||||
|
cursor:pointer;
|
||||||
|
transition:all 0.2s;
|
||||||
|
border:1px solid var(--border);
|
||||||
}
|
}
|
||||||
.view-home .btn-secondary:hover {
|
.view-home .btn-secondary:hover {
|
||||||
background:var(--border);
|
background:var(--border);
|
||||||
border-color:var(--text-secondary);
|
border-color:var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
.home-settings-btn {
|
||||||
|
position:fixed;
|
||||||
|
top:27px;
|
||||||
|
right:60px;
|
||||||
|
width:24px;
|
||||||
|
height:24px;
|
||||||
|
background-color:var(--text-primary);
|
||||||
|
-webkit-mask-image:url('/static/settings.svg');
|
||||||
|
mask-image:url('/static/settings.svg');
|
||||||
|
mask-size:contain;
|
||||||
|
mask-repeat:no-repeat;
|
||||||
|
mask-position:center;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
.nav-settings-icon {
|
||||||
|
width:24px;
|
||||||
|
height:24px;
|
||||||
|
flex-shrink:0;
|
||||||
|
margin-left:auto;
|
||||||
|
margin-top:3px;
|
||||||
|
background-color:var(--text-secondary);
|
||||||
|
-webkit-mask-image:url('/static/settings.svg');
|
||||||
|
mask-image:url('/static/settings.svg');
|
||||||
|
mask-size:100% 100%;
|
||||||
|
transition:background-color 0.2s;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
.nav-settings-icon:hover,
|
||||||
|
.nav-settings-icon.active {
|
||||||
|
background-color:var(--text-primary);
|
||||||
|
}
|
||||||
|
.nav-settings-link {
|
||||||
|
display:none;
|
||||||
|
margin-left:auto;
|
||||||
|
}
|
||||||
header {
|
header {
|
||||||
display:flex;
|
display:flex;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
|
|
@ -112,6 +163,7 @@ header {
|
||||||
padding:15px 60px;
|
padding:15px 60px;
|
||||||
border-bottom:1px solid var(--border);
|
border-bottom:1px solid var(--border);
|
||||||
background:var(--bg-main);
|
background:var(--bg-main);
|
||||||
|
width:100%;
|
||||||
}
|
}
|
||||||
.search-form {
|
.search-form {
|
||||||
flex-grow:1;
|
flex-grow:1;
|
||||||
|
|
@ -145,11 +197,11 @@ h1 span {
|
||||||
padding:0 60px;
|
padding:0 60px;
|
||||||
border-bottom:1px solid var(--border);
|
border-bottom:1px solid var(--border);
|
||||||
background:var(--bg-main);
|
background:var(--bg-main);
|
||||||
|
width:100%;
|
||||||
}
|
}
|
||||||
.nav-container {
|
.nav-container {
|
||||||
display:flex;
|
display:flex;
|
||||||
gap:30px;
|
gap:30px;
|
||||||
max-width:1200px;
|
|
||||||
}
|
}
|
||||||
.nav-tabs a {
|
.nav-tabs a {
|
||||||
padding:14px 0;
|
padding:14px 0;
|
||||||
|
|
@ -168,6 +220,9 @@ h1 span {
|
||||||
color:var(--accent);
|
color:var(--accent);
|
||||||
border-bottom-color:var(--accent);
|
border-bottom-color:var(--accent);
|
||||||
}
|
}
|
||||||
|
.nav-right {
|
||||||
|
margin-left:auto;
|
||||||
|
}
|
||||||
.image-results-container {
|
.image-results-container {
|
||||||
padding:30px 60px;
|
padding:30px 60px;
|
||||||
}
|
}
|
||||||
|
|
@ -473,19 +528,23 @@ h1 span {
|
||||||
|
|
||||||
|
|
||||||
@media (max-width:1200px) {
|
@media (max-width:1200px) {
|
||||||
body {
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
.content-layout {
|
.content-layout {
|
||||||
grid-template-columns:1fr;
|
grid-template-columns:1fr;
|
||||||
padding:20px 30px;
|
padding:20px 30px;
|
||||||
gap:20px;
|
gap:20px;
|
||||||
}
|
}
|
||||||
|
header {
|
||||||
|
gap:20px;
|
||||||
|
}
|
||||||
.results-container,.infobox-sidebar {
|
.results-container,.infobox-sidebar {
|
||||||
grid-column:1;
|
grid-column:1;
|
||||||
max-width:100%;
|
max-width:100%;
|
||||||
}
|
}
|
||||||
|
.settings-layout {
|
||||||
|
padding:20px 30px;
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
.infobox-sidebar {
|
.infobox-sidebar {
|
||||||
order:-1;
|
order:-1;
|
||||||
}
|
}
|
||||||
|
|
@ -498,9 +557,11 @@ h1 span {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width:768px) {
|
@media (max-width:768px) {
|
||||||
body {
|
.nav-settings-icon {
|
||||||
padding-left: 16px;
|
display:none;
|
||||||
padding-right: 16px;
|
}
|
||||||
|
.nav-settings-link {
|
||||||
|
display:inline;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
flex-direction:column;
|
flex-direction:column;
|
||||||
|
|
@ -532,7 +593,7 @@ h1 span {
|
||||||
font-size:0.95rem;
|
font-size:0.95rem;
|
||||||
}
|
}
|
||||||
.content-layout {
|
.content-layout {
|
||||||
padding:16px;
|
padding:16px 16px 16px 40px;
|
||||||
gap:16px;
|
gap:16px;
|
||||||
}
|
}
|
||||||
.result {
|
.result {
|
||||||
|
|
@ -591,7 +652,6 @@ h1 span {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transform: translateY(-5vh);
|
|
||||||
padding:20px 16px;
|
padding:20px 16px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
@ -621,6 +681,12 @@ h1 span {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width:600px) {
|
@media (max-width:600px) {
|
||||||
|
.content-layout {
|
||||||
|
padding:16px 16px 16px 28px;
|
||||||
|
}
|
||||||
|
.settings-layout {
|
||||||
|
padding:12px 0;
|
||||||
|
}
|
||||||
header {
|
header {
|
||||||
padding:12px 12px;
|
padding:12px 12px;
|
||||||
}
|
}
|
||||||
|
|
@ -657,3 +723,145 @@ h1 span {
|
||||||
font-size:0.75rem;
|
font-size:0.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-layout {
|
||||||
|
padding:30px 60px 30px 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-container {
|
||||||
|
max-width:700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-title {
|
||||||
|
font-size:1.8rem;
|
||||||
|
font-weight:700;
|
||||||
|
margin:0 0 32px 0;
|
||||||
|
letter-spacing:-0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
background:var(--bg-card);
|
||||||
|
border:1px solid var(--border);
|
||||||
|
border-radius:12px;
|
||||||
|
padding:24px;
|
||||||
|
margin-bottom:32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-title {
|
||||||
|
font-size:1.1rem;
|
||||||
|
font-weight:700;
|
||||||
|
margin:0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-desc {
|
||||||
|
color:var(--text-secondary);
|
||||||
|
font-size:0.9rem;
|
||||||
|
margin:0 0 20px 0;
|
||||||
|
line-height:1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-field {
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:space-between;
|
||||||
|
padding:10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-field + .settings-field {
|
||||||
|
border-top:1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-label {
|
||||||
|
font-size:0.95rem;
|
||||||
|
color:var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select {
|
||||||
|
padding:8px 12px;
|
||||||
|
border-radius:8px;
|
||||||
|
border:1px solid var(--border);
|
||||||
|
background:var(--bg-main);
|
||||||
|
color:var(--text-primary);
|
||||||
|
font-size:0.9rem;
|
||||||
|
outline:none;
|
||||||
|
cursor:pointer;
|
||||||
|
transition:border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-select:focus {
|
||||||
|
border-color:var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions {
|
||||||
|
display:flex;
|
||||||
|
gap:12px;
|
||||||
|
margin-top:8px;
|
||||||
|
padding-bottom:40px;
|
||||||
|
justify-content:flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions .btn-primary {
|
||||||
|
background:var(--accent);
|
||||||
|
color:var(--bg-main);
|
||||||
|
border:1px solid transparent;
|
||||||
|
padding:10px 24px;
|
||||||
|
border-radius:8px;
|
||||||
|
font-weight:600;
|
||||||
|
font-size:0.9rem;
|
||||||
|
cursor:pointer;
|
||||||
|
transition:all 0.2s;
|
||||||
|
touch-action:manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions .btn-primary:hover {
|
||||||
|
filter:brightness(1.1);
|
||||||
|
transform:translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions .btn-secondary {
|
||||||
|
background:var(--bg-card);
|
||||||
|
color:var(--text-primary);
|
||||||
|
border:1px solid var(--border);
|
||||||
|
padding:10px 24px;
|
||||||
|
border-radius:8px;
|
||||||
|
font-weight:600;
|
||||||
|
font-size:0.9rem;
|
||||||
|
cursor:pointer;
|
||||||
|
transition:all 0.2s;
|
||||||
|
touch-action:manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions .btn-secondary:hover {
|
||||||
|
background:var(--border);
|
||||||
|
border-color:var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:768px) {
|
||||||
|
.settings-layout {
|
||||||
|
padding:12px;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.settings-container {
|
||||||
|
max-width:100%;
|
||||||
|
}
|
||||||
|
.settings-title {
|
||||||
|
font-size:1.4rem;
|
||||||
|
margin-bottom:24px;
|
||||||
|
}
|
||||||
|
.settings-section {
|
||||||
|
padding:16px;
|
||||||
|
}
|
||||||
|
.settings-field {
|
||||||
|
flex-direction:column;
|
||||||
|
align-items:stretch;
|
||||||
|
gap:8px;
|
||||||
|
}
|
||||||
|
.settings-actions {
|
||||||
|
flex-direction:column;
|
||||||
|
}
|
||||||
|
.settings-actions .btn-primary,
|
||||||
|
.settings-actions .btn-secondary {
|
||||||
|
width:100%;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
4
static/settings.svg
Normal file
4
static/settings.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="3"/>
|
||||||
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 829 B |
10
static/theme-dark.css
Normal file
10
static/theme-dark.css
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
:root {
|
||||||
|
--bg-main: #121212;
|
||||||
|
--bg-card: #1e1e1e;
|
||||||
|
--border: #333333;
|
||||||
|
--text-primary: #ffffff;
|
||||||
|
--text-secondary: #a0a0a0;
|
||||||
|
--text-muted: #d1d1d1;
|
||||||
|
--accent: #e2e2e2;
|
||||||
|
--accent-glow: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
10
static/theme-light.css
Normal file
10
static/theme-light.css
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
:root {
|
||||||
|
--bg-main: #ffffff;
|
||||||
|
--bg-card: #f8f9fa;
|
||||||
|
--border: #e0e0e0;
|
||||||
|
--text-primary: #1a1a1a;
|
||||||
|
--text-secondary: #5f6368;
|
||||||
|
--text-muted: #757575;
|
||||||
|
--accent: #202124;
|
||||||
|
--accent-glow: rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,9 @@
|
||||||
OmniSearch
|
OmniSearch
|
||||||
</title>
|
</title>
|
||||||
<link rel="stylesheet" href="static/main.css">
|
<link rel="stylesheet" href="static/main.css">
|
||||||
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
{{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}}
|
||||||
|
{{if theme == "dark"}}<link rel="stylesheet" href="static/theme-dark.css">{{endif}}
|
||||||
|
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
||||||
<link rel="search"
|
<link rel="search"
|
||||||
type="application/opensearchdescription+xml"
|
type="application/opensearchdescription+xml"
|
||||||
title="OmniSearch" href="/opensearch.xml">
|
title="OmniSearch" href="/opensearch.xml">
|
||||||
|
|
@ -30,11 +32,13 @@
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="btnI" value="1" class="btn-secondary">
|
<button type="submit" name="btnI" value="1" class="btn-secondary">
|
||||||
Surprise me
|
Surprise me
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="/settings" class="home-settings-btn" title="Settings"></a>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,10 @@
|
||||||
<title>
|
<title>
|
||||||
OmniSearch Images - {{query}}
|
OmniSearch Images - {{query}}
|
||||||
</title>
|
</title>
|
||||||
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
||||||
<link rel="stylesheet" href="static/main.css">
|
<link rel="stylesheet" href="static/main.css">
|
||||||
|
{{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}}
|
||||||
|
{{if theme == "dark"}}<link rel="stylesheet" href="static/theme-dark.css">{{endif}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="images-view">
|
<body class="images-view">
|
||||||
|
|
@ -20,6 +22,7 @@
|
||||||
<input name="q" autocomplete="off"="text" class="search-box" placeholder="Search for images..."
|
<input name="q" autocomplete="off"="text" class="search-box" placeholder="Search for images..."
|
||||||
value="{{query}}">
|
value="{{query}}">
|
||||||
</form>
|
</form>
|
||||||
|
<a href="/settings?q={{query}}" class="nav-settings-icon" title="Settings"></a>
|
||||||
</header>
|
</header>
|
||||||
<nav class="nav-tabs">
|
<nav class="nav-tabs">
|
||||||
<div class="nav-container">
|
<div class="nav-container">
|
||||||
|
|
@ -29,6 +32,9 @@
|
||||||
<a href="/images?q={{query}}" class="active">
|
<a href="/images?q={{query}}" class="active">
|
||||||
Images
|
Images
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/settings?q={{query}}" class="nav-settings-link">
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<main class="image-results-container">
|
<main class="image-results-container">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@
|
||||||
OmniSearch - {{query}}
|
OmniSearch - {{query}}
|
||||||
</title>
|
</title>
|
||||||
<link rel="stylesheet" href="static/main.css">
|
<link rel="stylesheet" href="static/main.css">
|
||||||
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
{{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}}
|
||||||
|
{{if theme == "dark"}}<link rel="stylesheet" href="static/theme-dark.css">{{endif}}
|
||||||
|
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
||||||
<link rel="search"
|
<link rel="search"
|
||||||
type="application/opensearchdescription+xml"
|
type="application/opensearchdescription+xml"
|
||||||
title="OmniSearch" href="/opensearch.xml">
|
title="OmniSearch" href="/opensearch.xml">
|
||||||
|
|
@ -24,6 +26,7 @@
|
||||||
<input name="q" type="text" class="search-box" autocomplete="off" placeholder="Search the web..."
|
<input name="q" type="text" class="search-box" autocomplete="off" placeholder="Search the web..."
|
||||||
value="{{query}}">
|
value="{{query}}">
|
||||||
</form>
|
</form>
|
||||||
|
<a href="/settings?q={{query}}" class="nav-settings-icon" title="Settings"></a>
|
||||||
</header>
|
</header>
|
||||||
<nav class="nav-tabs">
|
<nav class="nav-tabs">
|
||||||
<div class="nav-container">
|
<div class="nav-container">
|
||||||
|
|
@ -33,6 +36,9 @@
|
||||||
<a href="/images?q={{query}}">
|
<a href="/images?q={{query}}">
|
||||||
Images
|
Images
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/settings?q={{query}}" class="nav-settings-link">
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="content-layout">
|
<div class="content-layout">
|
||||||
|
|
|
||||||
67
templates/settings.html
Normal file
67
templates/settings.html
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
|
||||||
|
<title>
|
||||||
|
OmniSearch - Settings
|
||||||
|
</title>
|
||||||
|
<link rel="stylesheet" href="static/main.css">
|
||||||
|
{{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}}
|
||||||
|
{{if theme == "dark"}}<link rel="stylesheet" href="static/theme-dark.css">{{endif}}
|
||||||
|
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
||||||
|
<link rel="search"
|
||||||
|
type="application/opensearchdescription+xml"
|
||||||
|
title="OmniSearch" href="/opensearch.xml">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="settings-view">
|
||||||
|
<header>
|
||||||
|
<h1>
|
||||||
|
Omni<span>Search</span>
|
||||||
|
</h1>
|
||||||
|
<form action="/search" method="GET" class="search-form">
|
||||||
|
<input name="q" type="text" class="search-box" autocomplete="off" placeholder="Search the web..."
|
||||||
|
value="{{query}}">
|
||||||
|
</form>
|
||||||
|
<a href="/settings?q={{query}}" class="nav-settings-icon active" title="Settings"></a>
|
||||||
|
</header>
|
||||||
|
<nav class="nav-tabs">
|
||||||
|
<div class="nav-container">
|
||||||
|
<a href="/search?q={{query}}">
|
||||||
|
All
|
||||||
|
</a>
|
||||||
|
<a href="/images?q={{query}}">
|
||||||
|
Images
|
||||||
|
</a>
|
||||||
|
<a href="/settings" class="active nav-settings-link">
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="settings-layout">
|
||||||
|
<main class="settings-container">
|
||||||
|
<form action="/save_settings" method="GET">
|
||||||
|
<input type="hidden" name="q" value="{{query}}">
|
||||||
|
<section class="settings-section">
|
||||||
|
<h3 class="settings-section-title">Theme</h3>
|
||||||
|
<p class="settings-section-desc">Choose your preferred colour scheme.</p>
|
||||||
|
<div class="settings-field">
|
||||||
|
<label class="settings-label" for="theme">Appearance</label>
|
||||||
|
<select id="theme" name="theme" class="settings-select">
|
||||||
|
<option value="system" {{if theme == "system"}}selected{{endif}}>System</option>
|
||||||
|
<option value="light" {{if theme == "light"}}selected{{endif}}>Light</option>
|
||||||
|
<option value="dark" {{if theme == "dark"}}selected{{endif}}>Dark</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button type="submit" class="btn-primary">Save Settings</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Add table
Reference in a new issue