Initial commit (Projekt wurde lokal auf meinem PC gestartet)
24
README.md
@@ -1,3 +1,27 @@
|
||||
# Veil
|
||||
|
||||
Veil ist ein auf WebKit basierender Simpler Browser, in GTK
|
||||
|
||||
============================
|
||||
DIES IST GERADE NUR EIN HOBBYPROJEKT VON MIR,
|
||||
UND DAHER NOCH NICHT PRODUCTION READY!!!!
|
||||
============================
|
||||
(trozdem freue ich mich über jede pull/push, ein ganzer browser ist nämlich ein bisschen viel arbeit. (heul :,( ))
|
||||
|
||||
|
||||
|
||||
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ Build ┃
|
||||
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
$ ./build.sh clean && ./build.sh build
|
||||
|
||||
nach einem erfolgreichem build landet die bin im build/linux ordner und wird in den veil ordner kopiert, die bin muss ich gleichen ordner wie die data/ directory liegen.
|
||||
|
||||
|
||||
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
┃ Code ┃
|
||||
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
ja, ich weiß er ist schlecht, und ja ich weiß, er könnte besser sein. bei verbesserungsvorschlägen bitte statt meckern:
|
||||
1. Verbessern
|
||||
2. Push
|
||||
Dankii :)
|
||||
43
Veil/Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
CC = gcc
|
||||
PKG_CFLAGS = $(shell pkg-config --cflags gtk+-3.0 webkit2gtk-4.1)
|
||||
PKG_LIBS = $(shell pkg-config --libs gtk+-3.0 webkit2gtk-4.1)
|
||||
|
||||
# === PERFORMANCE-OPTIMIZED BUILD FLAGS ===
|
||||
# -O3: Maximum optimization level
|
||||
# -march=native: Optimize for current CPU architecture
|
||||
# -mtune=native: Tune for current CPU
|
||||
# -flto: Link-time optimization for cross-file inlining
|
||||
# -ffast-math: Faster floating point (safe for browser UI)
|
||||
# -fomit-frame-pointer: Free up a register
|
||||
# -pipe: Faster compilation
|
||||
|
||||
CFLAGS = $(PKG_CFLAGS) -O3 -march=native -mtune=native -flto \
|
||||
-ffast-math -fomit-frame-pointer -pipe \
|
||||
-Wall -Wextra -Wno-unused-parameter
|
||||
|
||||
LDFLAGS = $(PKG_LIBS) -flto -Wl,-O1 -Wl,--as-needed
|
||||
|
||||
TARGET = veil
|
||||
SRC = src/veil.c
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
# Debug build (slower but easier to debug)
|
||||
debug: CFLAGS = $(PKG_CFLAGS) -O0 -g -Wall -Wextra
|
||||
debug: LDFLAGS = $(PKG_LIBS)
|
||||
debug: $(TARGET)
|
||||
|
||||
# Release build with stripped binary
|
||||
release: $(TARGET)
|
||||
strip --strip-all $(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
install: $(TARGET)
|
||||
install -Dm755 $(TARGET) /usr/local/bin/$(TARGET)
|
||||
|
||||
.PHONY: all clean install debug release
|
||||
BIN
Veil/Projektdateien (EXTERN)/Weil Icon.afphoto
Normal file
BIN
Veil/Projektdateien (EXTERN)/Weil Icon.afphoto~lock~
Normal file
88
Veil/build.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Veil Browser Build Script
|
||||
# Linux build only
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BUILD_DIR="$PROJECT_DIR/build"
|
||||
SRC="$PROJECT_DIR/src/veil.c"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() { echo -e "${GREEN}[*]${NC} $1"; }
|
||||
print_warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
print_error() { echo -e "${RED}[x]${NC} $1"; }
|
||||
|
||||
show_help() {
|
||||
echo "Veil Browser Build Script"
|
||||
echo ""
|
||||
echo "Usage: $0 [target]"
|
||||
echo ""
|
||||
echo "Targets:"
|
||||
echo " build Build for Linux (default)"
|
||||
echo " clean Remove build artifacts"
|
||||
echo " help Show this help"
|
||||
echo ""
|
||||
}
|
||||
|
||||
build_linux() {
|
||||
print_status "Building Veil for Linux..."
|
||||
|
||||
# Check dependencies
|
||||
if ! pkg-config --exists gtk+-3.0 webkit2gtk-4.1 2>/dev/null; then
|
||||
print_error "Missing dependencies. Install: gtk3 webkit2gtk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CFLAGS=$(pkg-config --cflags gtk+-3.0 webkit2gtk-4.1)
|
||||
LIBS=$(pkg-config --libs gtk+-3.0 webkit2gtk-4.1)
|
||||
|
||||
mkdir -p "$BUILD_DIR/linux"
|
||||
|
||||
gcc $CFLAGS \
|
||||
-O3 -march=native -mtune=native -flto -ffast-math \
|
||||
-fomit-frame-pointer -pipe \
|
||||
-Wall -Wextra -Wno-unused-parameter \
|
||||
-o "$BUILD_DIR/linux/veil" "$SRC" \
|
||||
$LIBS -flto -Wl,-O1 -Wl,--as-needed
|
||||
|
||||
# Copy to project root
|
||||
cp "$BUILD_DIR/linux/veil" "$PROJECT_DIR/veil"
|
||||
|
||||
print_status "Build complete: $BUILD_DIR/linux/veil"
|
||||
print_status "Also copied to: $PROJECT_DIR/veil"
|
||||
}
|
||||
|
||||
clean() {
|
||||
print_status "Cleaning build artifacts..."
|
||||
rm -rf "$BUILD_DIR"
|
||||
rm -f "$PROJECT_DIR/veil"
|
||||
print_status "Clean complete."
|
||||
}
|
||||
|
||||
# Main
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
case "${1:-build}" in
|
||||
build|linux)
|
||||
build_linux
|
||||
;;
|
||||
clean)
|
||||
clean
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown target: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
BIN
Veil/build/linux/veil
Executable file
BIN
Veil/data/icons/hicolor/128x128/apps/veil.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Veil/data/icons/hicolor/16x16/apps/veil.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Veil/data/icons/hicolor/24x24/apps/veil.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
Veil/data/icons/hicolor/256x256/apps/veil.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
Veil/data/icons/hicolor/32x32/apps/veil.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
Veil/data/icons/hicolor/48x48/apps/veil.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
Veil/data/icons/hicolor/512x512/apps/veil.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
Veil/data/icons/hicolor/64x64/apps/veil.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
17
Veil/data/icons/hicolor/scalable/apps/veil.svg
Normal file
|
After Width: | Height: | Size: 901 KiB |
14
Veil/data/meson.build
Normal file
@@ -0,0 +1,14 @@
|
||||
# Desktop file installation
|
||||
install_data('veil.desktop',
|
||||
install_dir: join_paths(get_option('datadir'), 'applications')
|
||||
)
|
||||
|
||||
# Icon installation (PNG at various sizes)
|
||||
icon_sizes = ['16x16', '24x24', '32x32', '48x48', '64x64', '128x128', '256x256', '512x512']
|
||||
|
||||
foreach size : icon_sizes
|
||||
install_data(
|
||||
join_paths('icons/hicolor', size, 'apps/veil.png'),
|
||||
install_dir: join_paths(get_option('datadir'), 'icons/hicolor', size, 'apps')
|
||||
)
|
||||
endforeach
|
||||
10
Veil/data/veil.desktop
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env xdg-open
|
||||
[Desktop Entry]
|
||||
Name=Veil
|
||||
Comment=Minimalistischer WebKit-Browser
|
||||
Exec=veil
|
||||
Icon=veil
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;WebBrowser;
|
||||
Keywords=browser;web;internet;
|
||||
36
Veil/meson.build
Normal file
@@ -0,0 +1,36 @@
|
||||
project('veil', 'c',
|
||||
version: '0.1.0',
|
||||
default_options: [
|
||||
'warning_level=2',
|
||||
'c_std=c11',
|
||||
'optimization=3',
|
||||
'b_lto=true',
|
||||
'strip=true'
|
||||
]
|
||||
)
|
||||
|
||||
gtk3_dep = dependency('gtk+-3.0', version: '>= 3.24')
|
||||
webkit2_dep = dependency('webkit2gtk-4.1')
|
||||
|
||||
# Performance-optimized compiler flags
|
||||
add_project_arguments(
|
||||
'-ffast-math',
|
||||
'-fomit-frame-pointer',
|
||||
'-pipe',
|
||||
language: 'c'
|
||||
)
|
||||
|
||||
# Native CPU optimization (comment out for portable builds)
|
||||
add_project_arguments(
|
||||
'-march=native',
|
||||
'-mtune=native',
|
||||
language: 'c'
|
||||
)
|
||||
|
||||
executable('veil',
|
||||
'src/veil.c',
|
||||
dependencies: [gtk3_dep, webkit2_dep],
|
||||
install: true
|
||||
)
|
||||
|
||||
subdir('data')
|
||||
189
Veil/src/pages/settings.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Veil Browser - Settings Page (about:settings)
|
||||
*/
|
||||
|
||||
#ifndef VEIL_PAGE_SETTINGS_H
|
||||
#define VEIL_PAGE_SETTINGS_H
|
||||
|
||||
static const char *SETTINGS_HTML =
|
||||
"<!DOCTYPE html>"
|
||||
"<html><head><meta charset='utf-8'><title>Settings</title>"
|
||||
"<style>"
|
||||
":root { --bg: %s; --accent: %s; --grad-start: %s; --grad-mid: %s; --grad-end: %s; }"
|
||||
"* { box-sizing: border-box; margin: 0; padding: 0; }"
|
||||
"body { font-family: 'JetBrains Mono', 'Fira Code', monospace; background: var(--bg); color: #e0e0e8; display: flex; height: 100vh; }"
|
||||
".sidebar { width: 180px; background: rgba(255,255,255,0.02); padding: 20px 0; border-right: 1px solid rgba(255,255,255,0.05); }"
|
||||
".sidebar h2 { font-size: 14px; padding: 0 20px; margin-bottom: 20px; background: linear-gradient(90deg, var(--grad-start), var(--grad-mid), var(--grad-end)); "
|
||||
" -webkit-background-clip: text; -webkit-text-fill-color: transparent; }"
|
||||
".nav-item { display: block; padding: 10px 20px; color: #707080; font-size: 12px; cursor: pointer; border-left: 2px solid transparent; }"
|
||||
".nav-item:hover { background: rgba(255,255,255,0.03); color: #a0a0a8; }"
|
||||
".nav-item.active { border-left-color: var(--accent); color: #e0e0e8; background: rgba(255,255,255,0.03); }"
|
||||
".content { flex: 1; padding: 30px 40px; overflow-y: auto; }"
|
||||
".page { display: none; }"
|
||||
".page.active { display: block; }"
|
||||
".page h3 { font-size: 16px; margin-bottom: 20px; color: #c0c0c8; }"
|
||||
".setting { display: flex; align-items: center; margin: 12px 0; padding: 12px; background: rgba(255,255,255,0.03); border-radius: 8px; }"
|
||||
".setting label { flex: 1; font-size: 12px; color: #909098; }"
|
||||
".setting input[type=color] { width: 45px; height: 28px; border: none; border-radius: 4px; cursor: pointer; background: transparent; }"
|
||||
".setting input[type=color]::-webkit-color-swatch-wrapper { padding: 2px; }"
|
||||
".setting input[type=color]::-webkit-color-swatch { border-radius: 3px; border: 1px solid rgba(255,255,255,0.1); }"
|
||||
".setting select { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: #e0e0e8; "
|
||||
" padding: 6px 10px; border-radius: 4px; font-size: 11px; font-family: inherit; cursor: pointer; }"
|
||||
".setting select option { background: #1a1a1f; }"
|
||||
".setting input[type=text] { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: #e0e0e8; "
|
||||
" padding: 6px 10px; border-radius: 4px; font-size: 11px; font-family: inherit; width: 250px; outline: none; }"
|
||||
".setting input[type=text]:focus { border-color: var(--accent); }"
|
||||
".setting input[type=text]::placeholder { color: #505060; }"
|
||||
".reset-btn { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: #707080; "
|
||||
" padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 10px; margin-left: 8px; }"
|
||||
".reset-btn:hover { background: rgba(255,255,255,0.1); color: #e0e0e8; }"
|
||||
".reset-all { margin-top: 20px; padding: 8px 16px; background: var(--accent); "
|
||||
" border: none; color: #0a0a0f; font-weight: bold; border-radius: 4px; cursor: pointer; font-size: 11px; }"
|
||||
".reset-all:hover { opacity: 0.9; }"
|
||||
".info-section { margin: 16px 0; padding: 16px; background: rgba(255,255,255,0.02); border-radius: 8px; }"
|
||||
".info-section h4 { color: #909098; margin-bottom: 12px; font-size: 12px; }"
|
||||
".info-section p { margin: 6px 0; font-size: 11px; color: #606070; }"
|
||||
".info-section code { background: rgba(255,255,255,0.05); padding: 2px 6px; border-radius: 3px; color: #a0a0a8; }"
|
||||
".version { margin-top: 30px; font-size: 10px; color: #404050; }"
|
||||
"</style></head><body>"
|
||||
"<div class='sidebar'>"
|
||||
" <h2>Veil Einstellungen</h2>"
|
||||
" <div class='nav-item active' onclick='showPage(\"general\")'>Allgemein</div>"
|
||||
" <div class='nav-item' onclick='showPage(\"customize\")'>Anpassung</div>"
|
||||
" <div class='nav-item' onclick='showPage(\"info\")'>Info</div>"
|
||||
"</div>"
|
||||
"<div class='content'>"
|
||||
" <div id='general' class='page active'>"
|
||||
" <h3>Allgemein</h3>"
|
||||
" <div class='setting'>"
|
||||
" <label>Suchmaschine</label>"
|
||||
" <select id='search_engine' onchange='update(\"search_engine\", this.value)'>"
|
||||
" <option value='0' %s>DuckDuckGo</option>"
|
||||
" <option value='1' %s>Google</option>"
|
||||
" <option value='2' %s>Brave</option>"
|
||||
" <option value='3' %s>Startpage</option>"
|
||||
" </select>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Lesezeichenleiste anzeigen</label>"
|
||||
" <select id='show_bookmarks' onchange='update(\"show_bookmarks\", this.value)'>"
|
||||
" <option value='1' %s>An</option>"
|
||||
" <option value='0' %s>Aus</option>"
|
||||
" </select>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Quick Links anzeigen</label>"
|
||||
" <select id='show_quicklinks' onchange='update(\"show_quicklinks\", this.value)'>"
|
||||
" <option value='1' %s>An</option>"
|
||||
" <option value='0' %s>Aus</option>"
|
||||
" </select>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Download-Speicherort</label>"
|
||||
" <input type='text' id='download_dir' value='%s' placeholder='Standard: ~/Downloads' "
|
||||
" onchange='update(\"download_dir\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='document.getElementById(\"download_dir\").value=\"\"; update(\"download_dir\", \"\")'>Reset</button>"
|
||||
" </div>"
|
||||
" </div>"
|
||||
" <div id='customize' class='page'>"
|
||||
" <h3>Anpassung</h3>"
|
||||
" <div class='setting'>"
|
||||
" <label>Hintergrundbild verwenden</label>"
|
||||
" <select id='use_bg_image' onchange='update(\"use_bg_image\", this.value)'>"
|
||||
" <option value='1' %s>An</option>"
|
||||
" <option value='0' %s>Aus</option>"
|
||||
" </select>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Hintergrundbild hochladen</label>"
|
||||
" <input type='file' id='bg_image_file' accept='image/*' onchange='uploadBgImage(this)' "
|
||||
" style='font-size:11px; color:#909098;'>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Bild-Deckkraft: <span id='opacity_val'>%d</span>%%</label>"
|
||||
" <input type='range' id='bg_opacity' min='0' max='100' value='%d' "
|
||||
" oninput='document.getElementById(\"opacity_val\").textContent=this.value' "
|
||||
" onchange='update(\"bg_opacity\", this.value)' style='width:120px;'>"
|
||||
" </div>"
|
||||
" <div style='height:1px; background:rgba(255,255,255,0.1); margin:16px 0;'></div>"
|
||||
" <h3 style='font-size:14px; color:#808090; margin-bottom:12px;'>Farben</h3>"
|
||||
" <div class='setting'>"
|
||||
" <label>Hintergrundfarbe</label>"
|
||||
" <input type='color' id='bg' value='%s' onchange='update(\"bg\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='resetColor(\"bg\", \"%s\")'>Reset</button>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Akzentfarbe</label>"
|
||||
" <input type='color' id='accent' value='%s' onchange='update(\"accent\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='resetColor(\"accent\", \"%s\")'>Reset</button>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Gradient Start</label>"
|
||||
" <input type='color' id='grad_start' value='%s' onchange='update(\"grad_start\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='resetColor(\"grad_start\", \"%s\")'>Reset</button>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Gradient Mitte</label>"
|
||||
" <input type='color' id='grad_mid' value='%s' onchange='update(\"grad_mid\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='resetColor(\"grad_mid\", \"%s\")'>Reset</button>"
|
||||
" </div>"
|
||||
" <div class='setting'>"
|
||||
" <label>Gradient Ende</label>"
|
||||
" <input type='color' id='grad_end' value='%s' onchange='update(\"grad_end\", this.value)'>"
|
||||
" <button class='reset-btn' onclick='resetColor(\"grad_end\", \"%s\")'>Reset</button>"
|
||||
" </div>"
|
||||
" <button class='reset-all' onclick='resetAll()'>Alle zurücksetzen</button>"
|
||||
" </div>"
|
||||
" <div id='info' class='page'>"
|
||||
" <h3>Info</h3>"
|
||||
" <div class='info-section'>"
|
||||
" <h4>Tastenkürzel</h4>"
|
||||
" <p><code>Ctrl+T</code> Neuer Tab</p>"
|
||||
" <p><code>Ctrl+W</code> Tab schließen</p>"
|
||||
" <p><code>Ctrl+L</code> URL-Leiste fokussieren</p>"
|
||||
" <p><code>Ctrl+R</code> / <code>F5</code> Neu laden</p>"
|
||||
" <p><code>F11</code> Vollbild umschalten</p>"
|
||||
" </div>"
|
||||
" <div class='info-section'>"
|
||||
" <h4>Über Veil</h4>"
|
||||
" <p>Ein Privater WebKit-Browser, ohne unnötigen Müll :)</p>"
|
||||
" <p>Gebaut mit GTK3 und WebKit2GTK</p>"
|
||||
" <p>Entwickelt von Packed https://papp-box.de/</p>"
|
||||
" </div>"
|
||||
" <p class='version'>Veil Browser v1.0</p>"
|
||||
" </div>"
|
||||
"</div>"
|
||||
"<script>"
|
||||
"function showPage(id) {"
|
||||
" document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));"
|
||||
" document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));"
|
||||
" document.getElementById(id).classList.add('active');"
|
||||
" event.target.classList.add('active');"
|
||||
"}"
|
||||
"function update(key, val) {"
|
||||
" window.webkit.messageHandlers.settings.postMessage(JSON.stringify({key:key, value:val}));"
|
||||
"}"
|
||||
"function resetColor(key, def) {"
|
||||
" document.getElementById(key).value = def;"
|
||||
" update(key, def);"
|
||||
"}"
|
||||
"function uploadBgImage(input) {"
|
||||
" if (!input.files || !input.files[0]) return;"
|
||||
" const file = input.files[0];"
|
||||
" const reader = new FileReader();"
|
||||
" reader.onload = function(e) {"
|
||||
" const base64 = e.target.result.split(',')[1];"
|
||||
" window.webkit.messageHandlers.settings.postMessage(JSON.stringify({key:'bg_image', value:base64}));"
|
||||
" };"
|
||||
" reader.readAsDataURL(file);"
|
||||
"}"
|
||||
"function resetAll() {"
|
||||
" resetColor('bg', '%s');"
|
||||
" resetColor('accent', '%s');"
|
||||
" resetColor('grad_start', '%s');"
|
||||
" resetColor('grad_mid', '%s');"
|
||||
" resetColor('grad_end', '%s');"
|
||||
"}"
|
||||
"</script></body></html>";
|
||||
|
||||
#endif /* VEIL_PAGE_SETTINGS_H */
|
||||
398
Veil/src/pages/start.h
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Veil Browser - Start Page (about:start)
|
||||
*/
|
||||
|
||||
#ifndef VEIL_PAGE_START_H
|
||||
#define VEIL_PAGE_START_H
|
||||
|
||||
static const char *START_HTML =
|
||||
"<!DOCTYPE html>"
|
||||
"<html><head><meta charset='utf-8'><title>Veil</title>"
|
||||
"<style>"
|
||||
":root { --bg: %s; --accent: %s; --grad-start: %s; --grad-mid: %s; --grad-end: %s; }"
|
||||
"* { box-sizing: border-box; margin: 0; padding: 0; }"
|
||||
"body {"
|
||||
" font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;"
|
||||
" background: var(--bg);"
|
||||
" color: #e0e0e8;"
|
||||
" min-height: 100vh;"
|
||||
" display: flex;"
|
||||
" flex-direction: column;"
|
||||
" align-items: center;"
|
||||
" justify-content: center;"
|
||||
" padding: 20px;"
|
||||
" %s" /* background-image style if set */
|
||||
"}"
|
||||
".container {"
|
||||
" display: flex;"
|
||||
" flex-direction: column;"
|
||||
" align-items: center;"
|
||||
" width: 100%%;"
|
||||
" max-width: 700px;"
|
||||
" margin-top: -80px;"
|
||||
"}"
|
||||
".hero {"
|
||||
" display: flex;"
|
||||
" align-items: center;"
|
||||
" gap: 30px;"
|
||||
" margin-bottom: 40px;"
|
||||
"}"
|
||||
".logo {"
|
||||
" width: 100px;"
|
||||
" height: 100px;"
|
||||
" position: relative;"
|
||||
"}"
|
||||
".logo img {"
|
||||
" width: 100%%;"
|
||||
" height: 100%%;"
|
||||
" object-fit: contain;"
|
||||
" border-radius: 50%%;"
|
||||
"}"
|
||||
".logo::after {"
|
||||
" content: '';"
|
||||
" position: absolute;"
|
||||
" top: 0; left: 0; right: 0; bottom: 0;"
|
||||
" background: linear-gradient(135deg, var(--grad-start), var(--grad-mid), var(--grad-end));"
|
||||
" border-radius: 50%%;"
|
||||
" mix-blend-mode: color;"
|
||||
" pointer-events: none;"
|
||||
"}"
|
||||
".ascii-brand {"
|
||||
" font-size: 14px;"
|
||||
" line-height: 1.1;"
|
||||
" font-weight: bold;"
|
||||
" white-space: pre;"
|
||||
" background: linear-gradient(135deg, var(--grad-start), var(--grad-mid), var(--grad-end));"
|
||||
" -webkit-background-clip: text;"
|
||||
" -webkit-text-fill-color: transparent;"
|
||||
" background-clip: text;"
|
||||
"}"
|
||||
".search-container {"
|
||||
" width: 100%%;"
|
||||
" position: relative;"
|
||||
"}"
|
||||
".search-box {"
|
||||
" width: 100%%;"
|
||||
" padding: 16px 50px 16px 20px;"
|
||||
" font-size: 15px;"
|
||||
" font-family: inherit;"
|
||||
" background: rgba(255,255,255,0.03);"
|
||||
" border: 1px solid rgba(255,255,255,0.08);"
|
||||
" border-radius: 12px;"
|
||||
" color: #e0e0e8;"
|
||||
" outline: none;"
|
||||
" transition: all 0.2s ease;"
|
||||
"}"
|
||||
".search-box::placeholder { color: #505060; }"
|
||||
".search-box:focus {"
|
||||
" border-color: var(--accent);"
|
||||
" background: rgba(255,255,255,0.05);"
|
||||
" box-shadow: 0 0 0 3px rgba(74, 158, 255, 0.1);"
|
||||
"}"
|
||||
".search-icon {"
|
||||
" position: absolute;"
|
||||
" right: 16px;"
|
||||
" top: 50%%;"
|
||||
" transform: translateY(-50%%);"
|
||||
" width: 20px;"
|
||||
" height: 20px;"
|
||||
" opacity: 0.4;"
|
||||
" pointer-events: none;"
|
||||
"}"
|
||||
".quicklinks-section {"
|
||||
" display: %s;"
|
||||
"}"
|
||||
".shortcuts-header {"
|
||||
" display: flex;"
|
||||
" align-items: center;"
|
||||
" gap: 12px;"
|
||||
" margin-top: 50px;"
|
||||
" margin-bottom: 16px;"
|
||||
"}"
|
||||
".shortcuts-title {"
|
||||
" font-size: 11px;"
|
||||
" color: #505060;"
|
||||
" text-transform: uppercase;"
|
||||
" letter-spacing: 2px;"
|
||||
"}"
|
||||
".edit-btn {"
|
||||
" background: transparent;"
|
||||
" border: 1px solid rgba(255,255,255,0.1);"
|
||||
" color: #606070;"
|
||||
" padding: 4px 10px;"
|
||||
" border-radius: 6px;"
|
||||
" font-size: 10px;"
|
||||
" cursor: pointer;"
|
||||
" font-family: inherit;"
|
||||
" transition: all 0.2s;"
|
||||
"}"
|
||||
".edit-btn:hover { border-color: var(--accent); color: var(--accent); }"
|
||||
".edit-btn.active { background: var(--accent); color: var(--bg); border-color: var(--accent); }"
|
||||
".shortcuts {"
|
||||
" display: flex;"
|
||||
" gap: 16px;"
|
||||
" flex-wrap: wrap;"
|
||||
" justify-content: center;"
|
||||
"}"
|
||||
".shortcut {"
|
||||
" display: flex;"
|
||||
" flex-direction: column;"
|
||||
" align-items: center;"
|
||||
" padding: 16px 20px;"
|
||||
" background: rgba(255,255,255,0.02);"
|
||||
" border: 1px solid rgba(255,255,255,0.05);"
|
||||
" border-radius: 12px;"
|
||||
" cursor: pointer;"
|
||||
" transition: all 0.2s ease;"
|
||||
" text-decoration: none;"
|
||||
" color: inherit;"
|
||||
" min-width: 90px;"
|
||||
" position: relative;"
|
||||
"}"
|
||||
".shortcut:hover {"
|
||||
" background: rgba(255,255,255,0.05);"
|
||||
" border-color: rgba(255,255,255,0.1);"
|
||||
" transform: translateY(-2px);"
|
||||
"}"
|
||||
".shortcut-icon {"
|
||||
" width: 32px;"
|
||||
" height: 32px;"
|
||||
" margin-bottom: 8px;"
|
||||
" display: flex;"
|
||||
" align-items: center;"
|
||||
" justify-content: center;"
|
||||
" font-size: 20px;"
|
||||
"}"
|
||||
".shortcut-icon img {"
|
||||
" width: 24px;"
|
||||
" height: 24px;"
|
||||
" border-radius: 4px;"
|
||||
" object-fit: contain;"
|
||||
"}"
|
||||
".shortcut-icon .emoji {"
|
||||
" font-size: 20px;"
|
||||
"}"
|
||||
".shortcut-label {"
|
||||
" font-size: 11px;"
|
||||
" color: #707080;"
|
||||
"}"
|
||||
".shortcut .delete-btn {"
|
||||
" position: absolute;"
|
||||
" top: -8px;"
|
||||
" right: -8px;"
|
||||
" width: 20px;"
|
||||
" height: 20px;"
|
||||
" background: #f85149;"
|
||||
" border: none;"
|
||||
" border-radius: 50%%;"
|
||||
" color: white;"
|
||||
" font-size: 14px;"
|
||||
" cursor: pointer;"
|
||||
" display: none;"
|
||||
" align-items: center;"
|
||||
" justify-content: center;"
|
||||
" line-height: 1;"
|
||||
"}"
|
||||
".editing .shortcut .delete-btn { display: flex; }"
|
||||
".editing .shortcut:hover { transform: none; }"
|
||||
".add-shortcut {"
|
||||
" display: none;"
|
||||
" flex-direction: column;"
|
||||
" align-items: center;"
|
||||
" justify-content: center;"
|
||||
" padding: 16px 20px;"
|
||||
" background: transparent;"
|
||||
" border: 2px dashed rgba(255,255,255,0.1);"
|
||||
" border-radius: 12px;"
|
||||
" cursor: pointer;"
|
||||
" min-width: 90px;"
|
||||
" color: #505060;"
|
||||
" transition: all 0.2s;"
|
||||
"}"
|
||||
".editing .add-shortcut { display: flex; }"
|
||||
".add-shortcut:hover { border-color: var(--accent); color: var(--accent); }"
|
||||
".add-shortcut span { font-size: 24px; }"
|
||||
".modal {"
|
||||
" display: none;"
|
||||
" position: fixed;"
|
||||
" top: 0; left: 0; right: 0; bottom: 0;"
|
||||
" background: rgba(0,0,0,0.8);"
|
||||
" align-items: center;"
|
||||
" justify-content: center;"
|
||||
" z-index: 100;"
|
||||
"}"
|
||||
".modal.show { display: flex; }"
|
||||
".modal-content {"
|
||||
" background: #151520;"
|
||||
" padding: 24px;"
|
||||
" border-radius: 12px;"
|
||||
" border: 1px solid rgba(255,255,255,0.1);"
|
||||
" width: 300px;"
|
||||
"}"
|
||||
".modal-title {"
|
||||
" font-size: 14px;"
|
||||
" margin-bottom: 16px;"
|
||||
" color: #e0e0e8;"
|
||||
"}"
|
||||
".modal input {"
|
||||
" width: 100%%;"
|
||||
" padding: 10px 12px;"
|
||||
" margin-bottom: 12px;"
|
||||
" background: rgba(255,255,255,0.05);"
|
||||
" border: 1px solid rgba(255,255,255,0.1);"
|
||||
" border-radius: 8px;"
|
||||
" color: #e0e0e8;"
|
||||
" font-family: inherit;"
|
||||
" font-size: 13px;"
|
||||
" outline: none;"
|
||||
"}"
|
||||
".modal input:focus { border-color: var(--accent); }"
|
||||
".modal-buttons {"
|
||||
" display: flex;"
|
||||
" gap: 8px;"
|
||||
" justify-content: flex-end;"
|
||||
" margin-top: 8px;"
|
||||
"}"
|
||||
".modal-buttons button {"
|
||||
" padding: 8px 16px;"
|
||||
" border-radius: 6px;"
|
||||
" font-family: inherit;"
|
||||
" font-size: 12px;"
|
||||
" cursor: pointer;"
|
||||
" border: none;"
|
||||
"}"
|
||||
".modal-buttons .cancel { background: rgba(255,255,255,0.1); color: #a0a0a0; }"
|
||||
".modal-buttons .save { background: var(--accent); color: #000; }"
|
||||
".footer {"
|
||||
" position: fixed;"
|
||||
" bottom: 20px;"
|
||||
" font-size: 10px;"
|
||||
" color: #303040;"
|
||||
"}"
|
||||
"</style></head><body>"
|
||||
"<div class='container'>"
|
||||
" <div class='hero'>"
|
||||
" <div class='logo'>"
|
||||
" <img src='%s' alt='Veil'>"
|
||||
" </div>"
|
||||
" <pre class='ascii-brand'>██╗ ██╗███████╗██╗██╗ \n"
|
||||
"██║ ██║██╔════╝██║██║ \n"
|
||||
"██║ ██║█████╗ ██║██║ \n"
|
||||
"╚██╗ ██╔╝██╔══╝ ██║██║ \n"
|
||||
" ╚████╔╝ ███████╗██║███████╗\n"
|
||||
" ╚═══╝ ╚══════╝╚═╝╚══════╝</pre>"
|
||||
" </div>"
|
||||
" <div class='search-container'>"
|
||||
" <input type='text' class='search-box' id='search' placeholder='Suchen oder URL eingeben...' autofocus>"
|
||||
" <svg class='search-icon' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'>"
|
||||
" <circle cx='11' cy='11' r='8'/><path d='m21 21-4.35-4.35'/>"
|
||||
" </svg>"
|
||||
" </div>"
|
||||
" <div class='quicklinks-section'>"
|
||||
" <div class='shortcuts-header'>"
|
||||
" <span class='shortcuts-title'>Quick Links</span>"
|
||||
" <button class='edit-btn' id='editBtn'>Bearbeiten</button>"
|
||||
" </div>"
|
||||
" <div class='shortcuts' id='shortcuts'></div>"
|
||||
" </div>"
|
||||
"</div>"
|
||||
"<div class='modal' id='modal'>"
|
||||
" <div class='modal-content'>"
|
||||
" <div class='modal-title'>Neuer Quick Link</div>"
|
||||
" <input type='text' id='linkName' placeholder='Name (z.B. Google)'>"
|
||||
" <input type='text' id='linkUrl' placeholder='URL (z.B. https://google.com)'>"
|
||||
" <input type='text' id='linkIcon' placeholder='Icon Emoji (z.B. 🔍)' maxlength='2'>"
|
||||
" <div class='modal-buttons'>"
|
||||
" <button class='cancel' onclick='closeModal()'>Abbrechen</button>"
|
||||
" <button class='save' onclick='saveLink()'>Speichern</button>"
|
||||
" </div>"
|
||||
" </div>"
|
||||
"</div>"
|
||||
"<div class='footer'>Veil Browser</div>"
|
||||
"<script>"
|
||||
"const defaultLinks = ["
|
||||
" {name: 'DuckDuckGo', url: 'https://duckduckgo.com', icon: '🦆'},"
|
||||
" {name: 'GitHub', url: 'https://github.com', icon: '⚙'},"
|
||||
" {name: 'YouTube', url: 'https://youtube.com', icon: '▶'},"
|
||||
" {name: 'Settings', url: 'veil:settings', icon: '⚙'}"
|
||||
"];"
|
||||
"let links = JSON.parse(localStorage.getItem('veil_quicklinks')) || defaultLinks;"
|
||||
"let editing = false;"
|
||||
""
|
||||
"function getFaviconUrl(url) {"
|
||||
" try {"
|
||||
" const u = new URL(url);"
|
||||
" return 'https://www.google.com/s2/favicons?domain=' + u.hostname + '&sz=64';"
|
||||
" } catch { return null; }"
|
||||
"}"
|
||||
""
|
||||
"function renderLinks() {"
|
||||
" const container = document.getElementById('shortcuts');"
|
||||
" container.innerHTML = '';"
|
||||
" links.forEach((link, i) => {"
|
||||
" const el = document.createElement('a');"
|
||||
" el.className = 'shortcut';"
|
||||
" el.href = link.url;"
|
||||
" const isInternal = link.url.startsWith('veil:') || link.url.startsWith('about:');"
|
||||
" const faviconUrl = !isInternal ? getFaviconUrl(link.url) : null;"
|
||||
" const iconHtml = faviconUrl "
|
||||
" ? `<img src='${faviconUrl}' onerror=\"this.parentNode.innerHTML='<span class=emoji>${link.icon}</span>'\">`"
|
||||
" : `<span class='emoji'>${link.icon}</span>`;"
|
||||
" el.innerHTML = `"
|
||||
" <button class='delete-btn' onclick='deleteLink(event, ${i})'>×</button>"
|
||||
" <div class='shortcut-icon'>${iconHtml}</div>"
|
||||
" <div class='shortcut-label'>${link.name}</div>"
|
||||
" `;"
|
||||
" container.appendChild(el);"
|
||||
" });"
|
||||
" const addBtn = document.createElement('div');"
|
||||
" addBtn.className = 'add-shortcut';"
|
||||
" addBtn.innerHTML = '<span>+</span>';"
|
||||
" addBtn.onclick = () => document.getElementById('modal').classList.add('show');"
|
||||
" container.appendChild(addBtn);"
|
||||
"}"
|
||||
""
|
||||
"function deleteLink(e, index) {"
|
||||
" e.preventDefault();"
|
||||
" e.stopPropagation();"
|
||||
" links.splice(index, 1);"
|
||||
" localStorage.setItem('veil_quicklinks', JSON.stringify(links));"
|
||||
" renderLinks();"
|
||||
"}"
|
||||
""
|
||||
"function closeModal() {"
|
||||
" document.getElementById('modal').classList.remove('show');"
|
||||
" document.getElementById('linkName').value = '';"
|
||||
" document.getElementById('linkUrl').value = '';"
|
||||
" document.getElementById('linkIcon').value = '';"
|
||||
"}"
|
||||
""
|
||||
"function saveLink() {"
|
||||
" const name = document.getElementById('linkName').value.trim();"
|
||||
" let url = document.getElementById('linkUrl').value.trim();"
|
||||
" const icon = document.getElementById('linkIcon').value || '🔗';"
|
||||
" if (!name || !url) return;"
|
||||
" if (!url.startsWith('http') && !url.startsWith('about:')) url = 'https://' + url;"
|
||||
" links.push({name, url, icon});"
|
||||
" localStorage.setItem('veil_quicklinks', JSON.stringify(links));"
|
||||
" closeModal();"
|
||||
" renderLinks();"
|
||||
"}"
|
||||
""
|
||||
"document.getElementById('editBtn').onclick = function() {"
|
||||
" editing = !editing;"
|
||||
" this.classList.toggle('active', editing);"
|
||||
" this.textContent = editing ? 'Fertig' : 'Bearbeiten';"
|
||||
" document.querySelector('.container').classList.toggle('editing', editing);"
|
||||
"};"
|
||||
""
|
||||
"document.getElementById('search').addEventListener('keydown', function(e) {"
|
||||
" if (e.key === 'Enter' && this.value.trim()) {"
|
||||
" window.webkit.messageHandlers.navigate.postMessage(this.value.trim());"
|
||||
" }"
|
||||
"});"
|
||||
""
|
||||
"renderLinks();"
|
||||
"</script></body></html>";
|
||||
|
||||
#endif /* VEIL_PAGE_START_H */
|
||||