Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6172332be7 | |||
| 74e3eaefcf | |||
| 00baa0231b | |||
| 36bf38a92c | |||
| b430b67df9 |
@@ -18,43 +18,56 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: HTML-Validierung (newtab.html existiert)
|
- name: Projektstruktur prüfen
|
||||||
run: |
|
run: |
|
||||||
echo "Prüfe Projektstruktur..."
|
echo "Prüfe Projektstruktur..."
|
||||||
test -f manifest.json || (echo "FEHLER: manifest.json fehlt!" && exit 1)
|
test -f manifest.json || (echo "FEHLER: manifest.json fehlt!" && exit 1)
|
||||||
test -f manifest.firefox.json || (echo "FEHLER: manifest.firefox.json fehlt!" && exit 1)
|
test -f manifest.firefox.json || (echo "FEHLER: manifest.firefox.json fehlt!" && exit 1)
|
||||||
|
test -f manifest.opera.json || (echo "FEHLER: manifest.opera.json fehlt!" && exit 1)
|
||||||
test -f newtab.html || (echo "FEHLER: newtab.html fehlt!" && exit 1)
|
test -f newtab.html || (echo "FEHLER: newtab.html fehlt!" && exit 1)
|
||||||
test -d src/js || (echo "FEHLER: src/js/ fehlt!" && exit 1)
|
test -d src/js || (echo "FEHLER: src/js/ fehlt!" && exit 1)
|
||||||
|
test -d src/js/opera || (echo "FEHLER: src/js/opera/ fehlt!" && exit 1)
|
||||||
test -d src/css || (echo "FEHLER: src/css/ fehlt!" && exit 1)
|
test -d src/css || (echo "FEHLER: src/css/ fehlt!" && exit 1)
|
||||||
test -d assets/icons || (echo "FEHLER: assets/icons/ fehlt!" && exit 1)
|
test -d assets/icons || (echo "FEHLER: assets/icons/ fehlt!" && exit 1)
|
||||||
test -d assets/themes || (echo "FEHLER: assets/themes/ fehlt!" && exit 1)
|
test -d assets/themes || (echo "FEHLER: assets/themes/ fehlt!" && exit 1)
|
||||||
|
test -d assets/fonts || (echo "FEHLER: assets/fonts/ fehlt!" && exit 1)
|
||||||
echo "Projektstruktur OK"
|
echo "Projektstruktur OK"
|
||||||
|
|
||||||
- name: Manifest-Validierung
|
- name: Manifest-Validierung (alle 3)
|
||||||
run: |
|
run: |
|
||||||
echo "Prüfe manifest.json..."
|
echo "Prüfe Manifests..."
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import json, sys
|
import json, sys
|
||||||
|
|
||||||
with open('manifest.json') as f:
|
with open('manifest.json') as f:
|
||||||
m = json.load(f)
|
m = json.load(f)
|
||||||
assert m.get('manifest_version') == 3, 'Manifest V3 erwartet'
|
assert m.get('manifest_version') == 3, 'Chrome: Manifest V3 erwartet'
|
||||||
assert m.get('name'), 'Name fehlt'
|
assert m.get('name'), 'Chrome: Name fehlt'
|
||||||
assert m.get('version'), 'Version fehlt'
|
assert m.get('version'), 'Chrome: Version fehlt'
|
||||||
assert 'storage' in m.get('permissions', []), 'Storage Permission fehlt'
|
assert 'storage' in m.get('permissions', []), 'Chrome: Storage Permission fehlt'
|
||||||
print('manifest.json (V3) OK — Version:', m['version'])
|
print('manifest.json (V3) OK — Version:', m['version'])
|
||||||
|
|
||||||
with open('manifest.firefox.json') as f:
|
with open('manifest.firefox.json') as f:
|
||||||
mf = json.load(f)
|
mf = json.load(f)
|
||||||
assert mf.get('manifest_version') == 2, 'Firefox Manifest V2 erwartet'
|
assert mf.get('manifest_version') == 3, 'Firefox: Manifest V3 erwartet'
|
||||||
assert mf['version'] == m['version'], 'Versionen stimmen nicht überein!'
|
assert mf['version'] == m['version'], 'Firefox: Version stimmt nicht mit Chrome überein!'
|
||||||
print('manifest.firefox.json (V2) OK — Version:', mf['version'])
|
assert 'browser_specific_settings' in mf, 'Firefox: browser_specific_settings fehlt'
|
||||||
|
print('manifest.firefox.json (V3) OK — Version:', mf['version'])
|
||||||
|
|
||||||
|
with open('manifest.opera.json') as f:
|
||||||
|
mo = json.load(f)
|
||||||
|
assert mo.get('manifest_version') == 3, 'Opera: Manifest V3 erwartet'
|
||||||
|
assert mo['version'] == m['version'], 'Opera: Version stimmt nicht mit Chrome überein!'
|
||||||
|
assert 'tabs' in mo.get('permissions', []), 'Opera: Tabs Permission fehlt'
|
||||||
|
assert 'background' in mo, 'Opera: Background Service Worker fehlt'
|
||||||
|
print('manifest.opera.json (V3) OK — Version:', mo['version'])
|
||||||
"
|
"
|
||||||
|
|
||||||
- name: JavaScript Syntax-Check
|
- name: JavaScript Syntax-Check
|
||||||
run: |
|
run: |
|
||||||
echo "Prüfe JavaScript-Syntax..."
|
echo "Prüfe JavaScript-Syntax..."
|
||||||
ERRORS=0
|
ERRORS=0
|
||||||
for f in src/js/*.js; do
|
for f in src/js/*.js src/js/opera/*.js; do
|
||||||
if ! node --check "$f" 2>&1; then
|
if ! node --check "$f" 2>&1; then
|
||||||
echo "SYNTAX-FEHLER in $f"
|
echo "SYNTAX-FEHLER in $f"
|
||||||
ERRORS=$((ERRORS + 1))
|
ERRORS=$((ERRORS + 1))
|
||||||
@@ -77,12 +90,18 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
MANIFEST_VER=$(python3 -c "import json; print(json.load(open('manifest.json'))['version'])")
|
MANIFEST_VER=$(python3 -c "import json; print(json.load(open('manifest.json'))['version'])")
|
||||||
FIREFOX_VER=$(python3 -c "import json; print(json.load(open('manifest.firefox.json'))['version'])")
|
FIREFOX_VER=$(python3 -c "import json; print(json.load(open('manifest.firefox.json'))['version'])")
|
||||||
|
OPERA_VER=$(python3 -c "import json; print(json.load(open('manifest.opera.json'))['version'])")
|
||||||
HTML_VER=$(grep -oP 'Version \K[0-9]+\.[0-9]+\.[0-9]+' newtab.html || echo 'NICHT GEFUNDEN')
|
HTML_VER=$(grep -oP 'Version \K[0-9]+\.[0-9]+\.[0-9]+' newtab.html || echo 'NICHT GEFUNDEN')
|
||||||
echo "manifest.json: $MANIFEST_VER"
|
echo "manifest.json: $MANIFEST_VER"
|
||||||
echo "manifest.firefox.json: $FIREFOX_VER"
|
echo "manifest.firefox.json: $FIREFOX_VER"
|
||||||
|
echo "manifest.opera.json: $OPERA_VER"
|
||||||
echo "newtab.html: $HTML_VER"
|
echo "newtab.html: $HTML_VER"
|
||||||
if [ "$MANIFEST_VER" != "$FIREFOX_VER" ]; then
|
if [ "$MANIFEST_VER" != "$FIREFOX_VER" ]; then
|
||||||
echo "FEHLER: Versionen in Manifests stimmen nicht überein!"
|
echo "FEHLER: Chrome/Firefox Versionen stimmen nicht überein!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$MANIFEST_VER" != "$OPERA_VER" ]; then
|
||||||
|
echo "FEHLER: Chrome/Opera Versionen stimmen nicht überein!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$MANIFEST_VER" != "$HTML_VER" ]; then
|
if [ "$MANIFEST_VER" != "$HTML_VER" ]; then
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Release — erstellt ZIP-Pakete für Chrome und Firefox bei neuem Tag
|
# Release — erstellt ZIP-Pakete für Chrome, Firefox und Opera bei neuem Tag
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -25,18 +25,25 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip" \
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip" \
|
||||||
manifest.json newtab.html src/ assets/ \
|
manifest.json newtab.html src/js/*.js src/css/ assets/ \
|
||||||
-x "*.git*" "dist/*" ".github/*"
|
-x "*.git*" "dist/*" ".github/*" "src/js/opera/*"
|
||||||
|
|
||||||
- name: Firefox ZIP erstellen (Manifest V2)
|
- name: Firefox ZIP erstellen (Manifest V3)
|
||||||
run: |
|
run: |
|
||||||
# manifest.firefox.json wird zu manifest.json für Firefox
|
|
||||||
cp manifest.json manifest.chrome-backup.json
|
cp manifest.json manifest.chrome-backup.json
|
||||||
cp manifest.firefox.json manifest.json
|
cp manifest.firefox.json manifest.json
|
||||||
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip" \
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip" \
|
||||||
manifest.json newtab.html src/ assets/ \
|
manifest.json newtab.html src/js/*.js src/css/ assets/ \
|
||||||
-x "*.git*" "dist/*" ".github/*" "manifest.chrome-backup.json" "manifest.firefox.json"
|
-x "*.git*" "dist/*" ".github/*" "manifest.chrome-backup.json" "manifest.firefox.json" "src/js/opera/*"
|
||||||
# Wiederherstellen
|
mv manifest.chrome-backup.json manifest.json
|
||||||
|
|
||||||
|
- name: Opera/Opera GX ZIP erstellen (Manifest V3 + Workaround)
|
||||||
|
run: |
|
||||||
|
cp manifest.json manifest.chrome-backup.json
|
||||||
|
cp manifest.opera.json manifest.json
|
||||||
|
zip -r "dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip" \
|
||||||
|
manifest.json newtab.html src/js/*.js src/js/opera/ src/css/ assets/ \
|
||||||
|
-x "*.git*" "dist/*" ".github/*" "manifest.chrome-backup.json" "manifest.opera.json"
|
||||||
mv manifest.chrome-backup.json manifest.json
|
mv manifest.chrome-backup.json manifest.json
|
||||||
|
|
||||||
- name: SHA256 Checksummen erstellen
|
- name: SHA256 Checksummen erstellen
|
||||||
@@ -53,8 +60,9 @@ jobs:
|
|||||||
## Hellion NewTab ${{ steps.version.outputs.tag }}
|
## Hellion NewTab ${{ steps.version.outputs.tag }}
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
- **Chrome / Edge / Brave / Opera / Vivaldi:** `hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip` herunterladen und entpacken
|
- **Chrome / Edge / Brave / Vivaldi:** `hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip`
|
||||||
- **Firefox:** `hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip` herunterladen und entpacken
|
- **Firefox:** `hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip`
|
||||||
|
- **Opera / Opera GX:** `hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip`
|
||||||
|
|
||||||
Siehe [README](README.md) für die vollständige Installationsanleitung.
|
Siehe [README](README.md) für die vollständige Installationsanleitung.
|
||||||
|
|
||||||
@@ -63,5 +71,6 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-chrome.zip
|
||||||
dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-firefox.zip
|
||||||
|
dist/hellion-newtab-${{ steps.version.outputs.tag }}-opera.zip
|
||||||
dist/checksums-sha256.txt
|
dist/checksums-sha256.txt
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|||||||
+2
-1
@@ -2,7 +2,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
CLAUDE.md
|
||||||
# Editor
|
# Editor
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
@@ -14,6 +14,7 @@ dist/
|
|||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
node_modules/
|
node_modules/
|
||||||
|
/xpi/
|
||||||
|
|
||||||
# Persönliche Backup-Dateien (nicht ins Repo)
|
# Persönliche Backup-Dateien (nicht ins Repo)
|
||||||
favorites_*.html
|
favorites_*.html
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# ⬡ Hellion Dashboard — Changelog
|
||||||
|
|
||||||
|
Alle relevanten Änderungen pro Version. Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.5.2 — 21.03.2026
|
||||||
|
|
||||||
|
#### Neue Features
|
||||||
|
|
||||||
|
- **Custom Dialog-System** — Native `confirm()` und `alert()` durch Frosted-Glass-Dialoge ersetzt (`dialog.js`)
|
||||||
|
- **Onboarding** — 6-stufiger Willkommens-Flow beim ersten Start mit Boards, Themes, Features und Backup-Hinweis
|
||||||
|
- **Backup-Reminder** — Erinnert alle 7 Tage an JSON-Export, warnt vor Datenverlust bei Browser-Reset
|
||||||
|
- **Theme-Modal** — Theme-Picker als eigenes Modal aus Settings ausgelagert, eigener Header-Button
|
||||||
|
- **Accordion-Settings** — Alle Settings-Sektionen einklappbar mit Chevron (About/Danger Zone standardmäßig zu)
|
||||||
|
|
||||||
|
#### Verbesserungen
|
||||||
|
|
||||||
|
- Fonts von Google Fonts API auf lokale WOFF2-Dateien umgestellt (DSGVO)
|
||||||
|
- Ungenutzte Font-Dateien entfernt (~388 KB gespart)
|
||||||
|
- `innerHTML` komplett durch `createElement`/`createElementNS` ersetzt (XSS-Schutz)
|
||||||
|
- SVG-Icons via `createElementNS` statt Inline-HTML
|
||||||
|
- Drag & Drop: Inline-Styles durch CSS-Klassen ersetzt (`.drag-ghost`, `.drag-over`, `.dragging-source`)
|
||||||
|
- Suchleisten-Toggle von DATA nach BEHAVIOR verschoben
|
||||||
|
- Nicht implementiertes "Quick Save" UI-Element entfernt
|
||||||
|
- Onboarding wiederholbar über Settings → Help
|
||||||
|
|
||||||
|
#### Opera / Opera GX
|
||||||
|
|
||||||
|
- `manifest.opera.json` hinzugefügt (MV3 mit Workaround-Skripten)
|
||||||
|
- `src/js/opera/background.js` — Tab-Management gegen Opera Speed Dial
|
||||||
|
- `src/js/opera/redirect.js` — Content Script Redirect bei `document_start`
|
||||||
|
|
||||||
|
#### Firefox
|
||||||
|
|
||||||
|
- `manifest.firefox.json` auf Manifest V3 migriert
|
||||||
|
- `browser_specific_settings` mit Gecko-ID und `data_collection_permissions`
|
||||||
|
|
||||||
|
#### Build & CI
|
||||||
|
|
||||||
|
- GitHub Actions: Release erstellt jetzt 3 ZIP-Pakete (Chrome, Firefox, Opera)
|
||||||
|
- Quality-Check prüft alle 3 Manifests und Opera-Ordner
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.2.0 — 20.03.2026
|
||||||
|
|
||||||
|
- Projektstruktur in `src/js/`, `src/css/`, `assets/` aufgeteilt
|
||||||
|
- JS in 10 Module aufgeteilt (storage, state, themes, boards, drag, settings, search, sticky, data, app)
|
||||||
|
- Firefox-Kompatibilität (`manifest.firefox.json`, Manifest V3)
|
||||||
|
- Vivaldi bestätigt kompatibel
|
||||||
|
- Theme-Bildpfade korrigiert (Settings Preview)
|
||||||
|
- URL-Validierung bei Bookmark-Erstellung
|
||||||
|
- JSON-Import mit Board- und Bookmark-Struktur-Validierung
|
||||||
|
- XSS-Schutz: `createElement` statt `innerHTML` für Bookmarks
|
||||||
|
- Storage-Quota-Prüfung mit Warnung bei 8 MB+
|
||||||
|
- Event Delegation für Bookmark-Klicks (Performance)
|
||||||
|
- Responsive Design (Tablet 768px, Smartphone 480px)
|
||||||
|
- Sticky Note Header-Kollision behoben
|
||||||
|
- FileReader-Fehlerbehandlung für Hintergrundbild-Upload
|
||||||
|
- GitHub Actions: Security Scan, Code Quality, Release Automation
|
||||||
|
- 3 Themes ersetzt: Astronaut → Nebula, Cosmic Clock → Crescent, Void Mage → Event Horizon
|
||||||
|
- Alle Theme-Bilder lizenzrechtlich geprüft und dokumentiert
|
||||||
|
- LICENSE (CC BY-NC-SA 4.0), SECURITY.md und DISCLAIMER.md hinzugefügt
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.0 — 20.03.2026
|
||||||
|
|
||||||
|
- 5 neue Themes (Merchantman, Julia & Jin, SC Sunset, Hellion HUD, Hellion Energy)
|
||||||
|
- Suchleiste (Google / DuckDuckGo / Bing)
|
||||||
|
- Sticky Note Widget
|
||||||
|
- JSON Export & Import
|
||||||
|
- Datum neben der Uhr
|
||||||
|
- About / Impressum in Settings
|
||||||
|
- Board Blur-Funktion (Privat-Modus)
|
||||||
|
- Drag & Drop auf Pointer Events umgestellt
|
||||||
|
- Opera / Opera GX Kompatibilität
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.0.0 — 20.03.2026
|
||||||
|
|
||||||
|
- Initiales Release
|
||||||
|
- Boards & Bookmarks mit Drag & Drop
|
||||||
|
- 3 Themes (Nebula, Crescent, Event Horizon)
|
||||||
|
- HTML-Import (Browser-Lesezeichen)
|
||||||
|
- Settings Panel
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Hellion NewTab** — [Hellion Online Media](https://hellion-media.de) — JonKazama-Hellion
|
||||||
+1
-1
@@ -44,4 +44,4 @@ Der Entwickler behält sich das Recht vor, diese Extension jederzeit zu ändern,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Hellion NewTab** — [Hellion Online Media](https://hellion-media.de) — JonKazama-Hellion
|
**Hellion NewTab** — [Hellion Online Media - Florian Wathing](https://hellion-media.de) — JonKazama-Hellion
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# ⬡ Hellion NewTab v1.2.0
|
# ⬡ Hellion Dashboard v1.5.2
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ Ein persönlicher Bookmark-Dashboard als Browser-Extension.
|
|||||||
Boards, Drag & Drop, 8 Themes, Suchleiste, Sticky Notes — alles im Browser, alles offline.
|
Boards, Drag & Drop, 8 Themes, Suchleiste, Sticky Notes — alles im Browser, alles offline.
|
||||||
Keine externe Datenübertragung, keine Tracker, keine Analytics, keine Werbung.
|
Keine externe Datenübertragung, keine Tracker, keine Analytics, keine Werbung.
|
||||||
|
|
||||||
Entwickelt von **[Hellion Online Media](https://hellion-media.de)** — JonKazama-Hellion.
|
Entwickelt von **[Hellion Online Media — Florian Wathling](https://hellion-media.de)** — JonKazama-Hellion.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,20 +54,20 @@ Was angezeigt wird, ist was gespeichert ist. Keine Magie.
|
|||||||
### 8 Themes
|
### 8 Themes
|
||||||
|
|
||||||
| Theme | Akzent | Stil |
|
| Theme | Akzent | Stil |
|
||||||
| --- | --- | --- |
|
|---|---|---|
|
||||||
| Nebula | Magenta | Cosmic Nebula |
|
| Nebula | `#b359ff` Magenta | Cosmic Nebula |
|
||||||
| Crescent | Gold | Minimalist Night |
|
| Crescent | `#d4bd8a` Gold | Minimalist Night |
|
||||||
| Event Horizon | Orange | Deep Space |
|
| Event Horizon | `#9d5cff` Purple | Deep Space |
|
||||||
| Merchantman | Teal | Industrial Sci-Fi |
|
| Merchantman | `#2eb8b8` Emerald | Industrial Sci-Fi |
|
||||||
| Julia & Jin | Blau | FFXIV Night |
|
| Julia & Jin | `#7db3ff` Aetherial Blue | FFXIV Night |
|
||||||
| SC Sunset | Amber | Planet-Side |
|
| SC Sunset | `#ff8c3d` Amber | Planet-Side |
|
||||||
| Hellion HUD | Grün | Circuit Board |
|
| Hellion HUD | `#32ff6a` Neon Green | Circuit Board |
|
||||||
| Hellion Energy | Matrix-Grün | Tactical |
|
| Hellion Energy | `#1eff8e` Acid Green | Tactical |
|
||||||
|
|
||||||
### Bild-Credits
|
### Bild-Credits
|
||||||
|
|
||||||
| Theme | Quelle | Lizenz |
|
| Theme | Quelle | Lizenz |
|
||||||
| --- | --- | --- |
|
|---|---|---|
|
||||||
| Nebula | [Temel / mrwashingt0n](https://pixabay.com/de/users/mrwashingt0n-15745216/) auf Pixabay | Pixabay License (frei) |
|
| Nebula | [Temel / mrwashingt0n](https://pixabay.com/de/users/mrwashingt0n-15745216/) auf Pixabay | Pixabay License (frei) |
|
||||||
| Crescent | [Daniil Silantev](https://unsplash.com) auf Unsplash | Unsplash License (frei) |
|
| Crescent | [Daniil Silantev](https://unsplash.com) auf Unsplash | Unsplash License (frei) |
|
||||||
| Event Horizon | Eigenes Werk — Stillframe von [hellion-initiative.online](https://hellion-initiative.online) | Hellion Online Media |
|
| Event Horizon | Eigenes Werk — Stillframe von [hellion-initiative.online](https://hellion-initiative.online) | Hellion Online Media |
|
||||||
@@ -77,55 +77,83 @@ Was angezeigt wird, ist was gespeichert ist. Keine Magie.
|
|||||||
| Hellion HUD | Eigenes Werk — AI-generiert und nachbearbeitet für hellion-media.de | Hellion Online Media |
|
| Hellion HUD | Eigenes Werk — AI-generiert und nachbearbeitet für hellion-media.de | Hellion Online Media |
|
||||||
| Hellion Energy | Eigenes Werk — AI-generiert für hellion-media.de | Hellion Online Media |
|
| Hellion Energy | Eigenes Werk — AI-generiert für hellion-media.de | Hellion Online Media |
|
||||||
|
|
||||||
### Settings
|
### Onboarding & Dialoge
|
||||||
|
|
||||||
|
- 6-stufiger Willkommens-Flow beim ersten Start
|
||||||
|
- Custom Frosted-Glass-Dialoge statt nativer Browser-Popups
|
||||||
|
- Backup-Reminder alle 7 Tage (warnt vor Datenverlust bei Browser-Reset)
|
||||||
|
|
||||||
|
### Settings (Accordion)
|
||||||
|
|
||||||
|
- Einklappbare Sektionen mit Chevron — About/Danger Zone standardmäßig geschlossen
|
||||||
- Compact Mode — reduziert Abstände für mehr Bookmarks
|
- Compact Mode — reduziert Abstände für mehr Bookmarks
|
||||||
- Shorten Titles — kürzt lange Titel auf eine Zeile
|
- Shorten Titles — kürzt lange Titel auf eine Zeile
|
||||||
- Open in New Tab — Bookmarks in neuem Tab öffnen
|
- Open in New Tab — Bookmarks in neuem Tab öffnen
|
||||||
- Show Descriptions — Beschreibungen unter Bookmarks anzeigen
|
- Show Descriptions — Beschreibungen unter Bookmarks anzeigen
|
||||||
- Hide Extra Bookmarks — Boards ab 5/10/20 Bookmarks einklappen
|
- Hide Extra Bookmarks — Boards ab 5/10/20 Bookmarks einklappen
|
||||||
- Hintergrundbild — URL oder lokaler Upload
|
|
||||||
- Suchleiste ein/ausblenden
|
- Suchleiste ein/ausblenden
|
||||||
- JSON Export / Import
|
- JSON Export / Import
|
||||||
|
- Onboarding wiederholbar
|
||||||
- Danger Zone — Reset aller Daten
|
- Danger Zone — Reset aller Daten
|
||||||
|
|
||||||
|
### Theme-Picker (eigener Header-Button)
|
||||||
|
|
||||||
|
- 8 Themes als zentriertes Modal
|
||||||
|
- Hintergrundbild per URL oder lokaler Upload
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Browser-Kompatibilität
|
## Browser-Kompatibilität
|
||||||
|
|
||||||
| Browser | Status | Manifest |
|
| Browser | Status | Manifest |
|
||||||
| --- | --- | --- |
|
|---|---|---|
|
||||||
| Chrome | Kompatibel | V3 (`manifest.json`) |
|
| Chrome | ✅ Kompatibel | V3 (`manifest.json`) |
|
||||||
| Edge | Kompatibel | V3 (`manifest.json`) |
|
| Edge | ✅ Kompatibel | V3 (`manifest.json`) |
|
||||||
| Brave | Kompatibel | V3 (`manifest.json`) |
|
| Brave | ✅ Kompatibel | V3 (`manifest.json`) |
|
||||||
| Opera | Kompatibel | V3 (`manifest.json`) |
|
| Opera | ✅ Kompatibel | V3 (`manifest.opera.json`) |
|
||||||
| Opera GX | Kompatibel | V3 (`manifest.json`) |
|
| Opera GX | ✅ Kompatibel | V3 (`manifest.opera.json`) |
|
||||||
| Vivaldi | Kompatibel | V3 (`manifest.json`) |
|
| Vivaldi | ✅ Kompatibel | V3 (`manifest.json`) |
|
||||||
| Firefox | Kompatibel | V2 (`manifest.firefox.json`) |
|
| Firefox | ✅ Kompatibel | V3 (`manifest.firefox.json`) |
|
||||||
|
|
||||||
> **Firefox-Hinweis:** Firefox verwendet aktuell Manifest V2. Mozilla arbeitet an MV3-Support —
|
> **Firefox-Hinweis:** Ab v1.2.0 läuft die Extension auf Manifest V3 — identisch zu Chrome/Edge.
|
||||||
> sobald stabil, wird die Extension migriert. MV2 wird von Mozilla weiterhin unterstützt.
|
> `manifest.firefox.json` bleibt als separate Datei erhalten für Firefox-spezifische Anpassungen.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Chrome / Edge / Brave / Opera / Opera GX / Vivaldi
|
### Chrome / Edge / Brave / Vivaldi
|
||||||
|
|
||||||
```text
|
```text
|
||||||
1. Repository als ZIP herunterladen oder git clone
|
1. Repository als ZIP herunterladen oder git clone
|
||||||
2. chrome://extensions öffnen (oder edge:// / brave:// / opera://)
|
2. chrome://extensions öffnen (bzw. edge:// / brave://)
|
||||||
3. Entwicklermodus aktivieren
|
3. Entwicklermodus aktivieren
|
||||||
4. "Entpackte Erweiterung laden" → Ordner auswählen in dem manifest.json liegt
|
4. "Entpackte Erweiterung laden" → Ordner auswählen in dem manifest.json liegt
|
||||||
5. Neuen Tab öffnen
|
5. Neuen Tab öffnen
|
||||||
```
|
```
|
||||||
|
|
||||||
### Firefox
|
### Opera / Opera GX
|
||||||
|
|
||||||
Firefox benötigt `manifest.json` im Format V2.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# manifest.json durch Firefox-Version ersetzen:
|
# manifest.opera.json als manifest.json verwenden:
|
||||||
|
copy manifest.opera.json manifest.json # Windows
|
||||||
|
cp manifest.opera.json manifest.json # Linux/Mac
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. opera://extensions öffnen
|
||||||
|
2. Entwicklermodus aktivieren
|
||||||
|
3. "Entpackte Erweiterung laden" → Ordner auswählen
|
||||||
|
4. Neuen Tab öffnen
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Opera-Hinweis:** Opera GX priorisiert Speed Dial — der enthaltene Workaround
|
||||||
|
> übernimmt die New-Tab-Seite zuverlässig. Details: [src/js/opera/README.md](src/js/opera/README.md)
|
||||||
|
|
||||||
|
### Firefox
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# manifest.firefox.json als manifest.json verwenden:
|
||||||
copy manifest.firefox.json manifest.json # Windows
|
copy manifest.firefox.json manifest.json # Windows
|
||||||
cp manifest.firefox.json manifest.json # Linux/Mac
|
cp manifest.firefox.json manifest.json # Linux/Mac
|
||||||
```
|
```
|
||||||
@@ -144,7 +172,7 @@ cp manifest.firefox.json manifest.json # Linux/Mac
|
|||||||
## Browser-Bookmarks exportieren & importieren
|
## Browser-Bookmarks exportieren & importieren
|
||||||
|
|
||||||
| Browser | Export-Pfad |
|
| Browser | Export-Pfad |
|
||||||
| --- | --- |
|
|---|---|
|
||||||
| Chrome / Edge | Einstellungen → Lesezeichen → Exportieren |
|
| Chrome / Edge | Einstellungen → Lesezeichen → Exportieren |
|
||||||
| Firefox | Lesezeichen → Alle Lesezeichen → Importieren und Sichern → Als HTML exportieren |
|
| Firefox | Lesezeichen → Alle Lesezeichen → Importieren und Sichern → Als HTML exportieren |
|
||||||
|
|
||||||
@@ -166,11 +194,11 @@ Die exportierte `.html`-Datei über den **Import**-Button in der Extension laden
|
|||||||
## Tech-Stack
|
## Tech-Stack
|
||||||
|
|
||||||
| Komponente | Details |
|
| Komponente | Details |
|
||||||
| --- | --- |
|
|---|---|
|
||||||
| Sprache | JavaScript (Vanilla ES2020, keine Frameworks) |
|
| Sprache | JavaScript (Vanilla ES2020, keine Frameworks) |
|
||||||
| Styling | CSS Custom Properties (Theme-System) |
|
| Styling | CSS Custom Properties (Theme-System) |
|
||||||
| Fonts | Google Fonts (Rajdhani, Inter, Cinzel) |
|
| Fonts | Lokale Fonts (Rajdhani, Inter, Cinzel) |
|
||||||
| Storage | chrome.storage.local / localStorage Fallback |
|
| Storage | `chrome.storage.local` / `localStorage` Fallback |
|
||||||
| Favicons | Google Favicons API (`/s2/favicons`) |
|
| Favicons | Google Favicons API (`/s2/favicons`) |
|
||||||
| Drag & Drop | Pointer Events API (nativ) |
|
| Drag & Drop | Pointer Events API (nativ) |
|
||||||
| Build | Kein Build-Schritt — direkt lauffähig |
|
| Build | Kein Build-Schritt — direkt lauffähig |
|
||||||
@@ -182,10 +210,12 @@ Die exportierte `.html`-Datei über den **Import**-Button in der Extension laden
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
hellion-newtab/
|
hellion-newtab/
|
||||||
├── manifest.json # Chrome, Edge, Brave, Opera, Vivaldi (MV3)
|
├── manifest.json # Chrome, Edge, Brave, Vivaldi (MV3)
|
||||||
├── manifest.firefox.json # Firefox (MV2)
|
├── manifest.firefox.json # Firefox (MV3)
|
||||||
|
├── manifest.opera.json # Opera / Opera GX (MV3 + Workaround)
|
||||||
├── newtab.html # Haupt-HTML (UI-Struktur, Modals, Settings Panel)
|
├── newtab.html # Haupt-HTML (UI-Struktur, Modals, Settings Panel)
|
||||||
├── LICENSE # CC BY-NC-SA 4.0
|
├── LICENSE # CC BY-NC-SA 4.0
|
||||||
|
├── CHANGELOG.md # Versionshistorie
|
||||||
├── SECURITY.md # Sicherheitsrichtlinie und Meldeprozess
|
├── SECURITY.md # Sicherheitsrichtlinie und Meldeprozess
|
||||||
├── DISCLAIMER.md # Haftungsausschluss
|
├── DISCLAIMER.md # Haftungsausschluss
|
||||||
│
|
│
|
||||||
@@ -193,27 +223,25 @@ hellion-newtab/
|
|||||||
│ ├── js/
|
│ ├── js/
|
||||||
│ │ ├── storage.js # Storage Abstraction + Quota-Prüfung
|
│ │ ├── storage.js # Storage Abstraction + Quota-Prüfung
|
||||||
│ │ ├── state.js # Globaler State, Defaults, Hilfsfunktionen
|
│ │ ├── state.js # Globaler State, Defaults, Hilfsfunktionen
|
||||||
|
│ │ ├── dialog.js # Custom Dialog-System (HellionDialog.alert/confirm)
|
||||||
│ │ ├── themes.js # Theme-Definitionen & Anwendungslogik
|
│ │ ├── themes.js # Theme-Definitionen & Anwendungslogik
|
||||||
│ │ ├── boards.js # Board/Bookmark Rendering, Event Delegation, Modals
|
│ │ ├── boards.js # Board/Bookmark Rendering, Event Delegation, Modals
|
||||||
│ │ ├── drag.js # Drag & Drop (Pointer Events, Board + Bookmark)
|
│ │ ├── drag.js # Drag & Drop (Pointer Events, Board + Bookmark)
|
||||||
│ │ ├── settings.js # Settings Panel (Toggles, Theme-Picker, Background)
|
│ │ ├── settings.js # Settings Panel, Theme-Modal, Accordion
|
||||||
│ │ ├── search.js # Suchleiste (Google, DuckDuckGo, Bing)
|
│ │ ├── search.js # Suchleiste (Google, DuckDuckGo, Bing)
|
||||||
│ │ ├── sticky.js # Sticky Note Widget (Drag, Persist, Toggle)
|
│ │ ├── sticky.js # Sticky Note Widget (Drag, Persist, Toggle)
|
||||||
│ │ ├── data.js # JSON Export / Import mit Validierung
|
│ │ ├── data.js # JSON Export / Import mit Validierung
|
||||||
│ │ └── app.js # Init, Clock, globale Events (Einstiegspunkt)
|
│ │ ├── onboarding.js # Mehrstufiger Willkommens-Flow
|
||||||
|
│ │ ├── app.js # Init, Clock, globale Events (Einstiegspunkt)
|
||||||
|
│ │ └── opera/ # Opera GX Workaround-Skripte
|
||||||
|
│ │ ├── background.js # Tab-Management gegen Speed Dial
|
||||||
|
│ │ └── redirect.js # Content Script Redirect
|
||||||
│ └── css/
|
│ └── css/
|
||||||
│ └── main.css # Styles + Theme-System + Responsive Breakpoints
|
│ └── main.css # Styles + Theme-System + Responsive Breakpoints
|
||||||
│
|
│
|
||||||
├── assets/
|
├── assets/
|
||||||
|
│ ├── fonts/ # Lokale Fonts (Rajdhani, Inter, Cinzel)
|
||||||
│ ├── themes/ # 8 Theme-Hintergrundbilder
|
│ ├── themes/ # 8 Theme-Hintergrundbilder
|
||||||
│ │ ├── bg-nebula.jpg
|
|
||||||
│ │ ├── bg-crescent.jpg
|
|
||||||
│ │ ├── bg-event-horizon.jpg
|
|
||||||
│ │ ├── bg-merchantman.webp
|
|
||||||
│ │ ├── bg-julia-jin.png
|
|
||||||
│ │ ├── bg-sc-sunset.jpg
|
|
||||||
│ │ ├── bg-hellion-hud.png
|
|
||||||
│ │ └── bg-hellion-energy.jpg
|
|
||||||
│ └── icons/
|
│ └── icons/
|
||||||
│ ├── icon16.png
|
│ ├── icon16.png
|
||||||
│ ├── icon48.png
|
│ ├── icon48.png
|
||||||
@@ -223,16 +251,16 @@ hellion-newtab/
|
|||||||
└── workflows/
|
└── workflows/
|
||||||
├── security.yml # CodeQL-Analyse + Dependency Review
|
├── security.yml # CodeQL-Analyse + Dependency Review
|
||||||
├── quality.yml # Struktur, Manifest, Syntax, Versions-Konsistenz
|
├── quality.yml # Struktur, Manifest, Syntax, Versions-Konsistenz
|
||||||
└── release.yml # ZIP-Pakete (Chrome + Firefox) + SHA256 Checksummen
|
└── release.yml # ZIP-Pakete (Chrome + Firefox + Opera) + SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
### Design-Prinzipien
|
### Design-Prinzipien
|
||||||
|
|
||||||
- **Zero Dependencies** — Kein npm, kein Build, kein Framework. Direkt lauffähig
|
- **Zero Dependencies** — Kein npm, kein Build, kein Framework. Direkt lauffähig
|
||||||
- **Privacy First** — Alle Daten lokal, kein Server-Kontakt
|
- **Privacy First** — Alle Daten lokal, kein Server-Kontakt
|
||||||
- **Modular** — 10 JS-Dateien mit klarer Zuständigkeit
|
- **Modular** — 12 JS-Dateien mit klarer Zuständigkeit
|
||||||
- **Responsive** — Tablet (768px) und Smartphone (480px) Breakpoints
|
- **Responsive** — Tablet (768px) und Smartphone (480px) Breakpoints
|
||||||
- **Secure** — createElement statt innerHTML, URL-Validierung, Storage-Fehlerbehandlung
|
- **Secure** — `createElement` statt `innerHTML`, URL-Validierung, Storage-Fehlerbehandlung
|
||||||
- **Event Delegation** — Ein Listener pro Board-Liste statt pro Bookmark (Performance)
|
- **Event Delegation** — Ein Listener pro Board-Liste statt pro Bookmark (Performance)
|
||||||
- **Theme-System** — CSS Custom Properties, 8 Themes, Custom-Background-Support
|
- **Theme-System** — CSS Custom Properties, 8 Themes, Custom-Background-Support
|
||||||
|
|
||||||
@@ -257,14 +285,14 @@ hellion-newtab/
|
|||||||
### Release (`release.yml`)
|
### Release (`release.yml`)
|
||||||
|
|
||||||
- **Trigger** — Bei Git-Tag (`v*`)
|
- **Trigger** — Bei Git-Tag (`v*`)
|
||||||
- **Pakete** — Chrome-ZIP (MV3) + Firefox-ZIP (MV2)
|
- **Pakete** — Chrome-ZIP + Firefox-ZIP + Opera-ZIP (alle MV3)
|
||||||
- **Checksummen** — SHA256 für alle Artefakte
|
- **Checksummen** — SHA256 für alle Artefakte
|
||||||
- **GitHub Release** — Automatisch mit Installationsanleitung
|
- **GitHub Release** — Automatisch mit Installationsanleitung
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Release erstellen:
|
# Release erstellen:
|
||||||
git tag v1.2.0
|
git tag v1.5.2
|
||||||
git push origin v1.2.0
|
git push origin v1.5.2
|
||||||
# → GitHub Action erstellt automatisch Release mit ZIP-Dateien
|
# → GitHub Action erstellt automatisch Release mit ZIP-Dateien
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -304,7 +332,7 @@ Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
|||||||
Vollständige Lizenz: [LICENSE](LICENSE) | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
Vollständige Lizenz: [LICENSE](LICENSE) | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
|---|---|
|
||||||
| **Entwickler** | Florian Wathling |
|
| **Entwickler** | Florian Wathling |
|
||||||
| **Unternehmen** | Hellion Online Media |
|
| **Unternehmen** | Hellion Online Media |
|
||||||
| **Web** | [hellion-media.de](https://hellion-media.de) |
|
| **Web** | [hellion-media.de](https://hellion-media.de) |
|
||||||
@@ -314,54 +342,12 @@ Vollständige Lizenz: [LICENSE](LICENSE) | [CC BY-NC-SA 4.0](https://creativecom
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
### v1.2.0 — 20.03.2026
|
|
||||||
|
|
||||||
- Projektstruktur in `src/js/`, `src/css/`, `assets/` aufgeteilt
|
|
||||||
- JS in 10 Module aufgeteilt (storage, state, themes, boards, drag, settings, search, sticky, data, app)
|
|
||||||
- Firefox-Kompatibilität (`manifest.firefox.json`, Manifest V2)
|
|
||||||
- Vivaldi bestätigt kompatibel
|
|
||||||
- Theme-Bildpfade korrigiert (Settings Preview)
|
|
||||||
- URL-Validierung bei Bookmark-Erstellung
|
|
||||||
- JSON-Import mit Board- und Bookmark-Struktur-Validierung
|
|
||||||
- XSS-Schutz: createElement statt innerHTML für Bookmarks
|
|
||||||
- Storage-Quota-Prüfung mit Warnung bei 8 MB+
|
|
||||||
- Event Delegation für Bookmark-Klicks (Performance)
|
|
||||||
- Responsive Design (Tablet 768px, Smartphone 480px)
|
|
||||||
- Sticky Note Header-Kollision behoben
|
|
||||||
- FileReader-Fehlerbehandlung für Hintergrundbild-Upload
|
|
||||||
- GitHub Actions: Security Scan, Code Quality, Release Automation
|
|
||||||
- 3 Themes ersetzt: Astronaut → Nebula, Cosmic Clock → Crescent, Void Mage → Event Horizon
|
|
||||||
- Alle Theme-Bilder lizenzrechtlich geprüft und dokumentiert
|
|
||||||
- LICENSE (CC BY-NC-SA 4.0), SECURITY.md und DISCLAIMER.md hinzugefügt
|
|
||||||
|
|
||||||
### v1.1.0 — 20.03.2026
|
|
||||||
|
|
||||||
- 5 neue Themes (Merchantman, Julia & Jin, SC Sunset, Hellion HUD, Hellion Energy)
|
|
||||||
- Suchleiste (Google / DuckDuckGo / Bing)
|
|
||||||
- Sticky Note Widget
|
|
||||||
- JSON Export & Import
|
|
||||||
- Datum neben der Uhr
|
|
||||||
- About / Impressum in Settings
|
|
||||||
- Board Blur-Funktion (Privat-Modus)
|
|
||||||
- Drag & Drop auf Pointer Events umgestellt
|
|
||||||
- Opera / Opera GX Kompatibilität
|
|
||||||
|
|
||||||
### v1.0.0 — 20.03.2026
|
|
||||||
|
|
||||||
- Initiales Release
|
|
||||||
- Boards & Bookmarks mit Drag & Drop
|
|
||||||
- 3 Themes (Nebula, Crescent, Event Horizon)
|
|
||||||
- HTML-Import (Browser-Lesezeichen)
|
|
||||||
- Settings Panel
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Einsatz von AI
|
### Einsatz von AI
|
||||||
|
|
||||||
AI (Claude Code, Opus 4.6 von Anthropic) wurde als Hilfsmittel eingesetzt — für Fehleridentifikation, Code-Review und Qualitätssicherung. Architektur, Features und alle Entscheidungen sind Eigenleistung.
|
AI (Claude Code, Opus 4.6 von Anthropic) wurde als Hilfsmittel eingesetzt — für Fehleridentifikation, Code-Review und Qualitätssicherung. Architektur, Features und alle Entscheidungen sind Eigenleistung.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Hellion NewTab** — [Hellion Online Media](https://hellion-media.de) — JonKazama-Hellion
|
> Vollständige Versionshistorie: [CHANGELOG.md](CHANGELOG.md)
|
||||||
|
|
||||||
|
**Hellion NewTab** — [Hellion Online Media](https://hellion-media.de) — JonKazama-Hellion
|
||||||
+1
-1
@@ -73,4 +73,4 @@ Keine Permissions für: Tabs, History, Web Requests, Downloads, Clipboard oder H
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Hellion NewTab** — [Hellion Online Media](https://hellion-media.de) — JonKazama-Hellion
|
**Hellion Dashboard** — [Hellion Online Media - Florian Wathling](https://hellion-media.de) — JonKazama-Hellion
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 109 B After Width: | Height: | Size: 827 B |
Binary file not shown.
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 3.8 KiB |
+19
-5
@@ -1,26 +1,40 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 3,
|
||||||
"name": "Hellion NewTab",
|
"name": "Hellion NewTab",
|
||||||
"version": "1.2.0",
|
"version": "1.5.2",
|
||||||
"description": "Personal bookmark dashboard — local, private, no account needed. By Hellion Online Media.",
|
"description": "Personal bookmark dashboard — local, private, no account needed. By Hellion Online Media.",
|
||||||
"author": "Florian Wathling – hellion-media.de",
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
"homepage_url": "https://hellion-media.de",
|
"homepage_url": "https://hellion-media.de",
|
||||||
|
|
||||||
"chrome_url_overrides": {
|
"chrome_url_overrides": {
|
||||||
"newtab": "newtab.html"
|
"newtab": "newtab.html"
|
||||||
},
|
},
|
||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
"bookmarks"
|
"bookmarks"
|
||||||
],
|
],
|
||||||
|
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "hellion-newtab@hellion-media.de",
|
"id": "hellion-newtab@hellion-media.de",
|
||||||
"strict_min_version": "109.0"
|
"strict_min_version": "142.0",
|
||||||
|
"data_collection_permissions": {
|
||||||
|
"required": [
|
||||||
|
"none"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"web_accessible_resources": [
|
||||||
|
{
|
||||||
|
"resources": ["assets/fonts/*.woff2"],
|
||||||
|
"matches": ["<all_urls>"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "assets/icons/icon16.png",
|
"16": "assets/icons/icon16.png",
|
||||||
"48": "assets/icons/icon48.png",
|
"48": "assets/icons/icon48.png",
|
||||||
"128": "assets/icons/icon128.png"
|
"128": "assets/icons/icon128.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-2
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Hellion NewTab",
|
"name": "Hellion NewTab",
|
||||||
"version": "1.2.0",
|
"version": "1.5.2",
|
||||||
"description": "Personal bookmark dashboard — local, private, no account needed. By Hellion Online Media.",
|
"description": "Personal bookmark dashboard — local, private, no account needed. By Hellion Online Media.",
|
||||||
"author": "Florian Wathling – hellion-media.de",
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
"homepage_url": "https://hellion-media.de",
|
"homepage_url": "https://hellion-media.de",
|
||||||
"chrome_url_overrides": {
|
"chrome_url_overrides": {
|
||||||
"newtab": "newtab.html"
|
"newtab": "newtab.html"
|
||||||
@@ -12,6 +12,12 @@
|
|||||||
"storage",
|
"storage",
|
||||||
"bookmarks"
|
"bookmarks"
|
||||||
],
|
],
|
||||||
|
"web_accessible_resources": [
|
||||||
|
{
|
||||||
|
"resources": ["assets/fonts/*.woff2"],
|
||||||
|
"matches": ["<all_urls>"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "assets/icons/icon16.png",
|
"16": "assets/icons/icon16.png",
|
||||||
"48": "assets/icons/icon48.png",
|
"48": "assets/icons/icon48.png",
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Hellion Dashboard (GX Native)",
|
||||||
|
"version": "1.5.2",
|
||||||
|
"description": "Ersetzt die Opera GX Startseite durch dein persönliches, leistungsoptimiertes Hellion Dashboard. Schnell, sauber und werbefrei.",
|
||||||
|
"author": "Hellion Online Media - Florian Wathling",
|
||||||
|
"homepage_url": "https://hellion-media.de",
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"tabs",
|
||||||
|
"storage",
|
||||||
|
"bookmarks"
|
||||||
|
],
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"service_worker": "src/js/opera/background.js"
|
||||||
|
},
|
||||||
|
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": [
|
||||||
|
"https://*.opera.com/startpage*",
|
||||||
|
"http://*.opera.com/startpage*"
|
||||||
|
],
|
||||||
|
"js": ["src/js/opera/redirect.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"web_accessible_resources": [
|
||||||
|
{
|
||||||
|
"resources": ["assets/fonts/*.woff2"],
|
||||||
|
"matches": ["<all_urls>"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"action": {
|
||||||
|
"default_title": "Hellion Dashboard"
|
||||||
|
},
|
||||||
|
|
||||||
|
"icons": {
|
||||||
|
"16": "assets/icons/icon16.png",
|
||||||
|
"48": "assets/icons/icon48.png",
|
||||||
|
"128": "assets/icons/icon128.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
+267
-225
@@ -3,10 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Hellion NewTab</title>
|
<title>Hellion Dashboard</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Inter:wght@300;400;500&display=swap" rel="stylesheet" />
|
|
||||||
<link rel="stylesheet" href="src/css/main.css" />
|
<link rel="stylesheet" href="src/css/main.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -38,6 +35,10 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 013 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
||||||
Note
|
Note
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn-icon" id="btnTheme" title="Theme wählen">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 100 20 4 4 0 01-1-7.9 1 1 0 011-.1h1a2 2 0 002-2V7a5 5 0 00-3-4.5"/><circle cx="7" cy="10" r="1.5"/><circle cx="13" cy="6" r="1.5"/><circle cx="17" cy="10" r="1.5"/><circle cx="9" cy="17" r="1.5"/></svg>
|
||||||
|
Theme
|
||||||
|
</button>
|
||||||
<button class="btn-icon" id="btnSettings" title="Einstellungen">
|
<button class="btn-icon" id="btnSettings" title="Einstellungen">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||||||
Settings
|
Settings
|
||||||
@@ -76,7 +77,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- HIDDEN FILE INPUT FOR IMPORT -->
|
<!-- HIDDEN FILE INPUT FOR IMPORT -->
|
||||||
<input type="file" id="importInput" accept=".html,.htm" style="display:none" />
|
<input type="file" id="importInput" accept=".html,.htm" class="hidden" />
|
||||||
|
|
||||||
<!-- SETTINGS PANEL -->
|
<!-- SETTINGS PANEL -->
|
||||||
<div class="panel-overlay" id="settingsOverlay"></div>
|
<div class="panel-overlay" id="settingsOverlay"></div>
|
||||||
@@ -88,248 +89,280 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
<!-- APPEARANCE -->
|
<!-- APPEARANCE -->
|
||||||
<section class="settings-section">
|
<section class="settings-section" data-section="appearance">
|
||||||
<!-- THEME PICKER -->
|
<button class="settings-section-title" type="button">
|
||||||
<h3 class="settings-section-title">THEME</h3>
|
<span class="section-chevron">▸</span>
|
||||||
<div class="theme-grid">
|
APPEARANCE
|
||||||
<div class="theme-card active" data-value="nebula">
|
</button>
|
||||||
<img class="theme-card-img" src="assets/themes/bg-nebula.jpg" alt="Nebula" />
|
<div class="section-content">
|
||||||
<span class="theme-card-label">Nebula</span>
|
<div class="setting-row">
|
||||||
<span class="theme-card-check">✓</span>
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Compact mode</span>
|
||||||
|
<span class="setting-desc">Reduce spacing to show more bookmarks</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="settingCompact" /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="crescent">
|
<div class="setting-row">
|
||||||
<img class="theme-card-img" src="assets/themes/bg-crescent.jpg" alt="Crescent" />
|
<div class="setting-info">
|
||||||
<span class="theme-card-label">Crescent</span>
|
<span class="setting-label">Shorten long titles</span>
|
||||||
<span class="theme-card-check">✓</span>
|
<span class="setting-desc">Shorten title to one line with "…"</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-card" data-value="event-horizon">
|
<label class="toggle"><input type="checkbox" id="settingShorten" /><span class="slider"></span></label>
|
||||||
<img class="theme-card-img" src="assets/themes/bg-event-horizon.jpg" alt="Event Horizon" />
|
|
||||||
<span class="theme-card-label">Event Horizon</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
|
||||||
<div class="theme-card" data-value="merchantman">
|
|
||||||
<img class="theme-card-img" src="assets/themes/bg-merchantman.webp" alt="Merchantman" />
|
|
||||||
<span class="theme-card-label">Merchantman</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
|
||||||
<div class="theme-card" data-value="julia-jin">
|
|
||||||
<img class="theme-card-img" src="assets/themes/bg-julia-jin.png" alt="Julia & Jin" />
|
|
||||||
<span class="theme-card-label">Julia & Jin</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
|
||||||
<div class="theme-card" data-value="sc-sunset">
|
|
||||||
<img class="theme-card-img" src="assets/themes/bg-sc-sunset.jpg" alt="SC Sunset" />
|
|
||||||
<span class="theme-card-label">SC Sunset</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
|
||||||
<div class="theme-card" data-value="hellion-hud">
|
|
||||||
<img class="theme-card-img" src="assets/themes/bg-hellion-hud.png" alt="Hellion HUD" />
|
|
||||||
<span class="theme-card-label">HUD</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
|
||||||
<div class="theme-card" data-value="hellion-energy">
|
|
||||||
<img class="theme-card-img" src="assets/themes/bg-hellion-energy.jpg" alt="Hellion Energy" />
|
|
||||||
<span class="theme-card-label">Energy</span>
|
|
||||||
<span class="theme-card-check">✓</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<h3 class="settings-section-title">APPEARANCE</h3>
|
<!-- BEHAVIOR -->
|
||||||
|
<section class="settings-section" data-section="behavior">
|
||||||
<div class="setting-row">
|
<button class="settings-section-title" type="button">
|
||||||
<div class="setting-info">
|
<span class="section-chevron">▸</span>
|
||||||
<span class="setting-label">Compact mode</span>
|
BEHAVIOR
|
||||||
<span class="setting-desc">Reduce spacing to show more bookmarks</span>
|
</button>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Open links in new tab</span>
|
||||||
|
<span class="setting-desc">Open bookmarks in a new browser tab</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="settingNewTab" checked /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingCompact" /><span class="slider"></span></label>
|
<div class="setting-row">
|
||||||
</div>
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Show bookmark descriptions</span>
|
||||||
<div class="setting-row">
|
<span class="setting-desc">Display saved descriptions below bookmark titles</span>
|
||||||
<div class="setting-info">
|
</div>
|
||||||
<span class="setting-label">Shorten long titles</span>
|
<label class="toggle"><input type="checkbox" id="settingShowDesc" /><span class="slider"></span></label>
|
||||||
<span class="setting-desc">Shorten title to one line with "…"</span>
|
</div>
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Hide extra bookmarks in long boards</span>
|
||||||
|
<span class="setting-desc">Automatically hides extra bookmarks in long boards</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="settingHideExtra" /><span class="slider"></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-row" id="visibleCountRow">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Visible bookmarks before hide</span>
|
||||||
|
<span class="setting-desc">Choose how many bookmarks are shown</span>
|
||||||
|
</div>
|
||||||
|
<select class="select-input" id="settingVisibleCount">
|
||||||
|
<option value="5">5</option>
|
||||||
|
<option value="10" selected>10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Suchleiste anzeigen</span>
|
||||||
|
<span class="setting-desc">Suchleiste unter dem Header ein/aus</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="settingShowSearch" checked /><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
<label class="toggle"><input type="checkbox" id="settingShorten" /><span class="slider"></span></label>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- DATA -->
|
||||||
|
<section class="settings-section" data-section="data">
|
||||||
|
<button class="settings-section-title" type="button">
|
||||||
|
<span class="section-chevron">▸</span>
|
||||||
|
DATA
|
||||||
|
</button>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Export Boards</span>
|
||||||
|
<span class="setting-desc">Alle Boards als JSON sichern</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-small" id="btnExportJSON">Export</button>
|
||||||
|
</div>
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Import Boards</span>
|
||||||
|
<span class="setting-desc">JSON-Backup wiederherstellen</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-small" id="btnImportJSON">Import</button>
|
||||||
|
<input type="file" id="jsonImportInput" accept=".json" class="hidden" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- HELP -->
|
||||||
|
<section class="settings-section" data-section="help">
|
||||||
|
<button class="settings-section-title" type="button">
|
||||||
|
<span class="section-chevron">▸</span>
|
||||||
|
HELP
|
||||||
|
</button>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Onboarding wiederholen</span>
|
||||||
|
<span class="setting-desc">Willkommens-Tour erneut anzeigen</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-small" id="btnRestartOnboarding">Start</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ABOUT / IMPRESSUM -->
|
||||||
|
<section class="settings-section" data-section="about">
|
||||||
|
<button class="settings-section-title" type="button">
|
||||||
|
<span class="section-chevron">▸</span>
|
||||||
|
ABOUT
|
||||||
|
</button>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="about-block">
|
||||||
|
<div class="about-logo">⬡ HELLION NEWTAB</div>
|
||||||
|
<div class="about-version">Version 1.5.2 · by Hellion Online Media</div>
|
||||||
|
|
||||||
|
<div class="about-links">
|
||||||
|
<a href="https://hellion-media.de/impressum" target="_blank" class="about-link">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||||
|
Impressum
|
||||||
|
</a>
|
||||||
|
<a href="https://hellion-media.de" target="_blank" class="about-link">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>
|
||||||
|
hellion-media.de
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-divider"></div>
|
||||||
|
|
||||||
|
<div class="about-info-row">
|
||||||
|
<span class="about-info-label">Entwickler</span>
|
||||||
|
<span class="about-info-value">Florian Wathling</span>
|
||||||
|
</div>
|
||||||
|
<div class="about-info-row">
|
||||||
|
<span class="about-info-label">Unternehmen</span>
|
||||||
|
<span class="about-info-value">Hellion Online Media</span>
|
||||||
|
</div>
|
||||||
|
<div class="about-info-row">
|
||||||
|
<span class="about-info-label">Lizenz</span>
|
||||||
|
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" class="about-info-value about-link-subtle">CC BY-NC-SA 4.0</a>
|
||||||
|
</div>
|
||||||
|
<div class="about-info-row">
|
||||||
|
<span class="about-info-label">Datenspeicherung</span>
|
||||||
|
<span class="about-info-value">100% lokal · Kein Server · Kein Account</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-divider"></div>
|
||||||
|
|
||||||
|
<div class="about-bugreport">
|
||||||
|
<span class="about-info-label about-info-label-block">Bug Report / Feedback</span>
|
||||||
|
<a href="mailto:kontakt@hellion-media.de?subject=Hellion NewTab – Bug Report" class="about-link-mail">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
||||||
|
kontakt@hellion-media.de
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-browsers">
|
||||||
|
<span class="about-info-label about-info-label-block">Kompatible Browser</span>
|
||||||
|
<div class="about-browser-tags">
|
||||||
|
<span class="browser-tag">Chrome</span>
|
||||||
|
<span class="browser-tag">Edge</span>
|
||||||
|
<span class="browser-tag">Firefox</span>
|
||||||
|
<span class="browser-tag">Opera</span>
|
||||||
|
<span class="browser-tag">Opera GX</span>
|
||||||
|
<span class="browser-tag">Brave</span>
|
||||||
|
<span class="browser-tag">Vivaldi</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- DANGER ZONE -->
|
||||||
|
<section class="settings-section" data-section="danger">
|
||||||
|
<button class="settings-section-title danger" type="button">
|
||||||
|
<span class="section-chevron">▸</span>
|
||||||
|
DANGER ZONE
|
||||||
|
</button>
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Reset all data</span>
|
||||||
|
<span class="setting-desc">Deletes all boards and bookmarks</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-danger" id="btnResetAll">Reset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- THEME PICKER MODAL -->
|
||||||
|
<div class="modal-overlay" id="themeOverlay">
|
||||||
|
<div class="theme-modal" id="themeModal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<span>Theme wählen</span>
|
||||||
|
<button class="btn-close" id="btnCloseTheme">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="theme-grid">
|
||||||
|
<div class="theme-card active" data-value="nebula">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-nebula.jpg" alt="Nebula" />
|
||||||
|
<span class="theme-card-label">Nebula</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="crescent">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-crescent.jpg" alt="Crescent" />
|
||||||
|
<span class="theme-card-label">Crescent</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="event-horizon">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-event-horizon.jpg" alt="Event Horizon" />
|
||||||
|
<span class="theme-card-label">Event Horizon</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="merchantman">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-merchantman.webp" alt="Merchantman" />
|
||||||
|
<span class="theme-card-label">Merchantman</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="julia-jin">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-julia-jin.png" alt="Julia & Jin" />
|
||||||
|
<span class="theme-card-label">Julia & Jin</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="sc-sunset">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-sc-sunset.jpg" alt="SC Sunset" />
|
||||||
|
<span class="theme-card-label">SC Sunset</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="hellion-hud">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-hellion-hud.png" alt="Hellion HUD" />
|
||||||
|
<span class="theme-card-label">HUD</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
<div class="theme-card" data-value="hellion-energy">
|
||||||
|
<img class="theme-card-img" src="assets/themes/bg-hellion-energy.jpg" alt="Hellion Energy" />
|
||||||
|
<span class="theme-card-label">Energy</span>
|
||||||
|
<span class="theme-card-check">✓</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="theme-modal-section">
|
||||||
|
<h3 class="settings-section-title">BACKGROUND</h3>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label">Background image</span>
|
<span class="setting-label">Background image URL</span>
|
||||||
<span class="setting-desc">Custom wallpaper URL or upload</span>
|
<span class="setting-desc">Custom wallpaper URL</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-small" id="btnChangeBg">Change</button>
|
<button class="btn-small" id="btnChangeBg">Change</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-row hidden" id="bgInputRow">
|
||||||
<div class="setting-row" id="bgInputRow" style="display:none">
|
|
||||||
<input type="text" class="text-input full-width" id="bgUrlInput" placeholder="https://... or leave empty for default" />
|
<input type="text" class="text-input full-width" id="bgUrlInput" placeholder="https://... or leave empty for default" />
|
||||||
<button class="btn-small" id="btnApplyBg">Apply</button>
|
<button class="btn-small" id="btnApplyBg">Apply</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label">Background file upload</span>
|
<span class="setting-label">Background file upload</span>
|
||||||
<span class="setting-desc">Use a local image as background</span>
|
<span class="setting-desc">Use a local image as background</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-small" id="btnBgFile">Upload</button>
|
<button class="btn-small" id="btnBgFile">Upload</button>
|
||||||
<input type="file" id="bgFileInput" accept="image/*" style="display:none" />
|
<input type="file" id="bgFileInput" accept="image/*" class="hidden" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- BEHAVIOR -->
|
|
||||||
<section class="settings-section">
|
|
||||||
<h3 class="settings-section-title">BEHAVIOR</h3>
|
|
||||||
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Open links in new tab</span>
|
|
||||||
<span class="setting-desc">Open bookmarks in a new browser tab</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle"><input type="checkbox" id="settingNewTab" checked /><span class="slider"></span></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Show bookmark descriptions</span>
|
|
||||||
<span class="setting-desc">Display saved descriptions below bookmark titles</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle"><input type="checkbox" id="settingShowDesc" /><span class="slider"></span></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Hide extra bookmarks in long boards</span>
|
|
||||||
<span class="setting-desc">Automatically hides extra bookmarks in long boards</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle"><input type="checkbox" id="settingHideExtra" /><span class="slider"></span></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-row" id="visibleCountRow">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Visible bookmarks before hide</span>
|
|
||||||
<span class="setting-desc">Choose how many bookmarks are shown</span>
|
|
||||||
</div>
|
|
||||||
<select class="select-input" id="settingVisibleCount">
|
|
||||||
<option value="5">5</option>
|
|
||||||
<option value="10" selected>10</option>
|
|
||||||
<option value="20">20</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Quick Save shortcut</span>
|
|
||||||
<span class="setting-desc">Save current page to a board quickly</span>
|
|
||||||
</div>
|
|
||||||
<span class="setting-badge" id="quickSaveBadge">Not set</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- ABOUT / IMPRESSUM -->
|
|
||||||
<section class="settings-section">
|
|
||||||
<h3 class="settings-section-title">ABOUT</h3>
|
|
||||||
|
|
||||||
<div class="about-block">
|
|
||||||
<div class="about-logo">⬡ HELLION NEWTAB</div>
|
|
||||||
<div class="about-version">Version 1.2.0 · by Hellion Online Media</div>
|
|
||||||
|
|
||||||
<div class="about-links">
|
|
||||||
<a href="https://hellion-media.de/impressum" target="_blank" class="about-link">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
|
||||||
Impressum
|
|
||||||
</a>
|
|
||||||
<a href="https://hellion-media.de" target="_blank" class="about-link">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/></svg>
|
|
||||||
hellion-media.de
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about-divider"></div>
|
|
||||||
|
|
||||||
<div class="about-info-row">
|
|
||||||
<span class="about-info-label">Entwickler</span>
|
|
||||||
<span class="about-info-value">Florian Wathling</span>
|
|
||||||
</div>
|
|
||||||
<div class="about-info-row">
|
|
||||||
<span class="about-info-label">Unternehmen</span>
|
|
||||||
<span class="about-info-value">Hellion Online Media</span>
|
|
||||||
</div>
|
|
||||||
<div class="about-info-row">
|
|
||||||
<span class="about-info-label">Lizenz</span>
|
|
||||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" class="about-info-value" style="color:var(--text-secondary);text-decoration:none">CC BY-NC-SA 4.0</a>
|
|
||||||
</div>
|
|
||||||
<div class="about-info-row">
|
|
||||||
<span class="about-info-label">Datenspeicherung</span>
|
|
||||||
<span class="about-info-value">100% lokal · Kein Server · Kein Account</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about-divider"></div>
|
|
||||||
|
|
||||||
<div class="about-bugreport">
|
|
||||||
<span class="about-info-label" style="display:block;margin-bottom:6px">Bug Report / Feedback</span>
|
|
||||||
<a href="mailto:kontakt@hellion-media.de?subject=Hellion NewTab – Bug Report" class="about-link-mail">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
|
||||||
kontakt@hellion-media.de
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about-browsers">
|
|
||||||
<span class="about-info-label" style="display:block;margin-bottom:6px">Kompatible Browser</span>
|
|
||||||
<div class="about-browser-tags">
|
|
||||||
<span class="browser-tag">Chrome</span>
|
|
||||||
<span class="browser-tag">Edge</span>
|
|
||||||
<span class="browser-tag">Firefox</span>
|
|
||||||
<span class="browser-tag">Opera</span>
|
|
||||||
<span class="browser-tag">Opera GX</span>
|
|
||||||
<span class="browser-tag">Brave</span>
|
|
||||||
<span class="browser-tag">Vivaldi</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- DATA -->
|
|
||||||
<section class="settings-section">
|
|
||||||
<h3 class="settings-section-title">DATA</h3>
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Export Boards</span>
|
|
||||||
<span class="setting-desc">Alle Boards als JSON sichern</span>
|
|
||||||
</div>
|
|
||||||
<button class="btn-small" id="btnExportJSON">Export</button>
|
|
||||||
</div>
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Import Boards</span>
|
|
||||||
<span class="setting-desc">JSON-Backup wiederherstellen</span>
|
|
||||||
</div>
|
|
||||||
<button class="btn-small" id="btnImportJSON">Import</button>
|
|
||||||
<input type="file" id="jsonImportInput" accept=".json" style="display:none" />
|
|
||||||
</div>
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Suchleiste anzeigen</span>
|
|
||||||
<span class="setting-desc">Suchleiste unter dem Header ein/aus</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle"><input type="checkbox" id="settingShowSearch" checked /><span class="slider"></span></label>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- DANGER ZONE -->
|
|
||||||
<section class="settings-section">
|
|
||||||
<h3 class="settings-section-title danger">DANGER ZONE</h3>
|
|
||||||
<div class="setting-row">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Reset all data</span>
|
|
||||||
<span class="setting-desc">Deletes all boards and bookmarks</span>
|
|
||||||
</div>
|
|
||||||
<button class="btn-danger" id="btnResetAll">Reset</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</div>
|
||||||
|
|
||||||
<!-- ADD BOARD MODAL -->
|
<!-- ADD BOARD MODAL -->
|
||||||
<div class="modal-overlay" id="addBoardOverlay">
|
<div class="modal-overlay" id="addBoardOverlay">
|
||||||
@@ -356,8 +389,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="text" class="text-input full-width" id="newBmTitle" placeholder="Title..." maxlength="60" />
|
<input type="text" class="text-input full-width" id="newBmTitle" placeholder="Title..." maxlength="60" />
|
||||||
<input type="url" class="text-input full-width" id="newBmUrl" placeholder="https://..." style="margin-top:8px" />
|
<input type="url" class="text-input full-width modal-input-spaced" id="newBmUrl" placeholder="https://..." />
|
||||||
<input type="text" class="text-input full-width" id="newBmDesc" placeholder="Description (optional)" style="margin-top:8px" />
|
<input type="text" class="text-input full-width modal-input-spaced" id="newBmDesc" placeholder="Description (optional)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn-primary" id="btnConfirmBookmark">Add</button>
|
<button class="btn-primary" id="btnConfirmBookmark">Add</button>
|
||||||
@@ -381,10 +414,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ONBOARDING -->
|
||||||
|
<div class="dialog-overlay" id="onboardingOverlay">
|
||||||
|
<div class="onboarding-modal" id="onboardingModal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Storage muss zuerst -->
|
<!-- Storage muss zuerst -->
|
||||||
<script src="src/js/storage.js"></script>
|
<script src="src/js/storage.js"></script>
|
||||||
<!-- State & Hilfsfunktionen -->
|
<!-- State & Hilfsfunktionen -->
|
||||||
<script src="src/js/state.js"></script>
|
<script src="src/js/state.js"></script>
|
||||||
|
<!-- Dialog-System (vor Features, wird überall gebraucht) -->
|
||||||
|
<script src="src/js/dialog.js"></script>
|
||||||
<!-- Theme-System -->
|
<!-- Theme-System -->
|
||||||
<script src="src/js/themes.js"></script>
|
<script src="src/js/themes.js"></script>
|
||||||
<!-- Features -->
|
<!-- Features -->
|
||||||
@@ -394,6 +434,8 @@
|
|||||||
<script src="src/js/search.js"></script>
|
<script src="src/js/search.js"></script>
|
||||||
<script src="src/js/sticky.js"></script>
|
<script src="src/js/sticky.js"></script>
|
||||||
<script src="src/js/data.js"></script>
|
<script src="src/js/data.js"></script>
|
||||||
|
<!-- Onboarding -->
|
||||||
|
<script src="src/js/onboarding.js"></script>
|
||||||
<!-- Einstiegspunkt zuletzt -->
|
<!-- Einstiegspunkt zuletzt -->
|
||||||
<script src="src/js/app.js"></script>
|
<script src="src/js/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# ⬡ Hellion Dashboard — Design & Theme-System
|
||||||
|
|
||||||
|
Leitfaden für das visuelle Design des Hellion Dashboards. Definiert wie Themes aufgebaut
|
||||||
|
sind und welche Patterns konsistent eingehalten werden — für eine immersive, fokussierte
|
||||||
|
Nutzererfahrung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design-Säulen
|
||||||
|
|
||||||
|
| Säule | Beschreibung |
|
||||||
|
|---|---|
|
||||||
|
| **Immersion** | Das Interface wirkt wie ein HUD das über der Szenerie schwebt — kein Fremdkörper |
|
||||||
|
| **Visual Clarity** | Gezielter `blur`-Einsatz trennt UI und Hintergrundbild — reduziert Reizüberflutung |
|
||||||
|
| **Harmonie** | Jedes Theme zieht seine Farben aus den dominanten Lichtquellen des Hintergrundbildes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anatomie eines Themes
|
||||||
|
|
||||||
|
Jedes Theme folgt dieser Variablen-Struktur in `main.css`.
|
||||||
|
Für ein neues Theme diesen Block kopieren und anpassen:
|
||||||
|
|
||||||
|
```css
|
||||||
|
[data-theme="dein-theme-name"] {
|
||||||
|
/* 1. AKZENTE — Die Lichtquelle */
|
||||||
|
--accent: #HEXCODE; /* Hauptfarbe (Neon/Licht) */
|
||||||
|
--accent-dim: rgba(R, G, B, 0.12); /* Subtiler Hintergrund */
|
||||||
|
--accent-glow: rgba(R, G, B, 0.08); /* Glow für Logo & Uhr */
|
||||||
|
--border-accent: rgba(R, G, B, 0.25); /* Fokus-Rahmen */
|
||||||
|
|
||||||
|
/* 2. BASIS — Das Fundament */
|
||||||
|
--bg-primary: #HEXCODE; /* Dunkelster Punkt im Bild */
|
||||||
|
--bg-board: rgba(R, G, B, 0.55); /* Glas-Effekt der Boards */
|
||||||
|
|
||||||
|
/* 3. TEXT — Kontrast */
|
||||||
|
--text-primary: #FFFFFF; /* Klar lesbar, leicht getönt */
|
||||||
|
--text-secondary: #A0A0A0; /* Entsättigt für weniger Rauschen */
|
||||||
|
|
||||||
|
/* 4. OVERLAY — Vignette */
|
||||||
|
--overlay-bg: radial-gradient(
|
||||||
|
circle at center,
|
||||||
|
transparent 0%,
|
||||||
|
var(--bg-primary) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UI-Patterns
|
||||||
|
|
||||||
|
### Frosted Glass
|
||||||
|
|
||||||
|
Hardware-beschleunigter Blur für Lesbarkeit auf komplexen Hintergründen:
|
||||||
|
|
||||||
|
```css
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
```
|
||||||
|
|
||||||
|
Erzeugt Tiefe und visuelle Ruhe hinter Text und UI-Elementen.
|
||||||
|
|
||||||
|
### Typografie-Hierarchie
|
||||||
|
|
||||||
|
| Font | Einsatz |
|
||||||
|
|---|---|
|
||||||
|
| **Rajdhani** | Display — Uhr, Titel, Logo. Alles was nach "System" aussieht |
|
||||||
|
| **Inter** | Body — Bookmark-Titel, Listen, interaktive Elemente |
|
||||||
|
| **Cinzel** | Fantasy — Exklusiv für Themes mit majestätischem oder antikem Vibe (Crescent, Julia & Jin) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Theme-Übersicht
|
||||||
|
|
||||||
|
| Theme | Akzentfarbe | Stimmung |
|
||||||
|
|---|---|---|
|
||||||
|
| Nebula | `#b359ff` Magenta | Chill, Cosmic |
|
||||||
|
| Crescent | `#d4bd8a` Gold | Luxury, Night |
|
||||||
|
| Event Horizon | `#9d5cff` Purple | Deep Space, Void |
|
||||||
|
| Merchantman | `#2eb8b8` Emerald | Industrial, Alien |
|
||||||
|
| Julia & Jin | `#7db3ff` Aetherial Blue | FFXIV Night |
|
||||||
|
| SC Sunset | `#ff8c3d` Amber | Emotional, Horizon |
|
||||||
|
| Hellion HUD | `#32ff6a` Neon Green | Tactical, Admin |
|
||||||
|
| Hellion Energy | `#1eff8e` Acid Green | Overdrive, Power |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADHS-Optimierung
|
||||||
|
|
||||||
|
Bei Hintergrundbildern mit vielen Details (z.B. Julia & Jin) den Board-Alpha erhöhen
|
||||||
|
und den Blur verstärken — das dimmt das Hintergrundrauschen und lässt das Gehirn
|
||||||
|
schneller die relevanten Informationen erfassen:
|
||||||
|
|
||||||
|
```css
|
||||||
|
--bg-board: rgba(R, G, B, 0.65);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Entwickelt von **[Hellion Online Media — Florian Wathling](https://hellion-media.de)** — JonKazama-Hellion
|
||||||
+440
-158
@@ -1,22 +1,60 @@
|
|||||||
/* =============================================
|
/* =============================================
|
||||||
HELLION NEWTAB — Theme System
|
HELLION Dashboard — Theme System
|
||||||
Themes: nebula | crescent | event-horizon
|
Themes: nebula | crescent | event-horizon | merchantman | julia-jin | sc-sunset | hellion-hud | hellion-energy
|
||||||
============================================= */
|
============================================= */
|
||||||
|
/* =============================================
|
||||||
|
Lokale Schriftarten
|
||||||
|
(für schnelleren Zugriff und Offline-Verfügbarkeit sowie DSGVO-Konformität)
|
||||||
|
- Rajdhani: Hauptschriftart für Überschriften und UI-Elemente
|
||||||
|
- Inter: Fließtext und allgemeine Lesbarkeit
|
||||||
|
- Cinzel: Alternative Display-Schriftart für bestimmte Themes
|
||||||
|
============================================= */
|
||||||
|
/* Rajdhani - Lokal */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Rajdhani';
|
||||||
|
src: url('../../assets/fonts/Rajdhani-Regular.woff2') format('woff2');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Inter:wght@300;400;500&family=Cinzel:wght@400;600&display=swap');
|
@font-face {
|
||||||
|
font-family: 'Rajdhani';
|
||||||
|
src: url('../../assets/fonts/Rajdhani-Bold.woff2') format('woff2');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inter - Lokal */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
src: url('../../assets/fonts/Inter-VariableFont_opsz,wght.woff2') format('woff2');
|
||||||
|
font-weight: 300 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cinzel - Lokal */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Cinzel';
|
||||||
|
src: url('../../assets/fonts/Cinzel-VariableFont_wght.woff2') format('woff2');
|
||||||
|
font-weight: 400 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
/* ---- BASE VARIABLES (Nebula = Default) ---- */
|
/* ---- BASE VARIABLES (Nebula = Default) ---- */
|
||||||
:root {
|
:root {
|
||||||
--accent: #d65cff;
|
--accent: #b359ff;
|
||||||
--accent-dim: rgba(214, 92, 255, 0.15);
|
--accent-dim: rgba(179, 89, 255, 0.12);
|
||||||
--accent-glow: rgba(214, 92, 255, 0.08);
|
--accent-glow: rgba(179, 89, 255, 0.05);
|
||||||
--border-accent: rgba(214, 92, 255, 0.35);
|
--border-accent: rgba(179, 92, 255, 0.25);
|
||||||
--bg-primary: #0a060e;
|
--bg-primary: #050308;
|
||||||
--bg-board: rgba(12, 8, 18, 0.48);
|
--bg-board: rgba(10, 6, 14, 0.55);
|
||||||
--border: rgba(255, 255, 255, 0.06);
|
--border: rgba(255, 255, 255, 0.06);
|
||||||
--text-primary: #e0d8f0;
|
--text-primary: #e5dff2;
|
||||||
--text-secondary: #8a7a9e;
|
--text-secondary: #a395b8;
|
||||||
--text-muted: #4a3860;
|
--text-muted: #5a4a75;
|
||||||
--danger: #e05555;
|
--danger: #e05555;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
@@ -25,34 +63,34 @@
|
|||||||
--board-width: 240px;
|
--board-width: 240px;
|
||||||
--spacing: 10px;
|
--spacing: 10px;
|
||||||
--spacing-compact: 5px;
|
--spacing-compact: 5px;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(10,6,14,0.85) 0%, rgba(10,6,14,0.42) 45%, rgba(10,6,14,0.90) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
||||||
--header-bg: rgba(10,6,14,0.90);
|
--header-bg: rgba(10,6,14,0.90);
|
||||||
--board-hover-border: rgba(214,92,255,0.22);
|
--board-hover-border: rgba(179,89,255,0.18);
|
||||||
--toggle-on-bg: rgba(214,92,255,0.22);
|
--toggle-on-bg: rgba(214,92,255,0.22);
|
||||||
--logo-shadow: rgba(214,92,255,0.45);
|
--logo-shadow: rgba(179,89,255,0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: NEBULA (magenta / cosmic nebula)
|
THEME: NEBULA (magenta / cosmic nebula)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="nebula"] {
|
[data-theme="nebula"] {
|
||||||
--accent: #d65cff;
|
--accent: #b359ff;
|
||||||
--accent-dim: rgba(214, 92, 255, 0.14);
|
--accent-dim: rgba(179, 89, 255, 0.12);
|
||||||
--accent-glow: rgba(214, 92, 255, 0.06);
|
--accent-glow: rgba(179, 89, 255, 0.05);
|
||||||
--border-accent: rgba(214, 92, 255, 0.32);
|
--border-accent: rgba(179, 92, 255, 0.25);
|
||||||
--bg-primary: #0a060e;
|
--bg-primary: #050308;
|
||||||
--bg-board: rgba(12, 8, 18, 0.48);
|
--bg-board: rgba(10, 6, 14, 0.55);
|
||||||
--border: rgba(255, 255, 255, 0.055);
|
--border: rgba(255, 255, 255, 0.055);
|
||||||
--text-primary: #e0d8f0;
|
--text-primary: #e5dff2;
|
||||||
--text-secondary: #8a7a9e;
|
--text-secondary: #a395b8;
|
||||||
--text-muted: #4a3860;
|
--text-muted: #5a4a75;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(10,6,14,0.88) 0%, rgba(10,6,14,0.35) 45%, rgba(10,6,14,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(10,6,14,0.3) 0%, rgba(5,3,8,0.85) 100%);
|
||||||
--header-bg: rgba(10,6,14,0.92);
|
--header-bg: rgba(10,6,14,0.92);
|
||||||
--board-hover-border: rgba(214,92,255,0.22);
|
--board-hover-border: rgba(179,89,255,0.18);
|
||||||
--toggle-on-bg: rgba(214,92,255,0.22);
|
--toggle-on-bg: rgba(214,92,255,0.22);
|
||||||
--logo-shadow: rgba(214,92,255,0.50);
|
--logo-shadow: rgba(179,89,255,0.35);
|
||||||
}
|
}
|
||||||
[data-theme="nebula"] .board { border-color: rgba(214,92,255,0.10); }
|
[data-theme="nebula"] .board { border-color: rgba(214,92,255,0.10); }
|
||||||
[data-theme="nebula"] .bm-item:hover { background: rgba(214,92,255,0.05); }
|
[data-theme="nebula"] .bm-item:hover { background: rgba(214,92,255,0.05); }
|
||||||
@@ -61,23 +99,24 @@
|
|||||||
THEME: CRESCENT (gold / minimalist night)
|
THEME: CRESCENT (gold / minimalist night)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="crescent"] {
|
[data-theme="crescent"] {
|
||||||
--accent: #c8a84a;
|
--accent: #d4bd8a;
|
||||||
--accent-dim: rgba(200, 168, 74, 0.16);
|
--accent-dim: rgba(212, 189, 138, 0.12);
|
||||||
--accent-glow: rgba(200, 168, 74, 0.07);
|
--accent-glow: rgba(212, 189, 138, 0.05);
|
||||||
--border-accent: rgba(200, 168, 74, 0.35);
|
--border-accent: rgba(212, 189, 138, 0.25);
|
||||||
--bg-primary: #080a12;
|
--bg-primary: #06080f;
|
||||||
--bg-board: rgba(10, 14, 26, 0.50);
|
--bg-board: rgba(8, 12, 22, 0.45);
|
||||||
--border: rgba(200, 168, 74, 0.09);
|
--border: rgba(212, 189, 138, 0.10);
|
||||||
--text-primary: #d8dce8;
|
--text-primary: #e2e6f0;
|
||||||
--text-secondary: #6a7090;
|
--text-secondary: #7a84a5;
|
||||||
--text-muted: #363c55;
|
--text-muted: #3d4563;
|
||||||
--font-display: 'Cinzel', serif;
|
--font-display: 'Cinzel', serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(8,10,18,0.88) 0%, rgba(8,10,18,0.30) 50%, rgba(8,10,18,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(6,8,15,0.2) 0%, rgba(6,8,15,0.9) 100%);
|
||||||
--header-bg: rgba(8,10,18,0.93);
|
--header-bg: rgba(6,8,15,0.95);
|
||||||
--board-hover-border: rgba(200,168,74,0.26);
|
--board-hover-border: rgba(212, 189, 138, 0.20);
|
||||||
--toggle-on-bg: rgba(200,168,74,0.22);
|
--toggle-on-bg: rgba(200,168,74,0.22);
|
||||||
--logo-shadow: rgba(200,168,74,0.55);
|
--logo-shadow: rgba(212, 189, 138, 0.40);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="crescent"] .logo { font-family: 'Cinzel', serif; letter-spacing: 4px; }
|
[data-theme="crescent"] .logo { font-family: 'Cinzel', serif; letter-spacing: 4px; }
|
||||||
@@ -87,158 +126,177 @@
|
|||||||
[data-theme="crescent"] .board { border-color: rgba(200,168,74,0.10); }
|
[data-theme="crescent"] .board { border-color: rgba(200,168,74,0.10); }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: EVENT HORIZON (orange-violet / deep space)
|
THEME: EVENT HORIZON (Cosmic Purple / deep space)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="event-horizon"] {
|
[data-theme="event-horizon"] {
|
||||||
--accent: #e08030;
|
--accent: #9d5cff;
|
||||||
--accent-dim: rgba(224, 128, 48, 0.15);
|
--accent-dim: rgba(157, 92, 255, 0.12);
|
||||||
--accent-glow: rgba(224, 128, 48, 0.07);
|
--accent-glow: rgba(157, 92, 255, 0.08);
|
||||||
--border-accent: rgba(224, 128, 48, 0.35);
|
--border-accent: rgba(157, 92, 255, 0.28);
|
||||||
--bg-primary: #06050c;
|
--bg-primary: #040308;
|
||||||
--bg-board: rgba(8, 6, 16, 0.52);
|
--bg-board: rgba(8, 5, 15, 0.45);
|
||||||
--border: rgba(224, 128, 48, 0.08);
|
--border: rgba(157, 92, 255, 0.12);
|
||||||
--text-primary: #d8d0e8;
|
--text-primary: #e6e0f5;
|
||||||
--text-secondary: #7a6898;
|
--text-secondary: #9284b0;
|
||||||
--text-muted: #3c3058;
|
--text-muted: #4a3d66;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(6,5,12,0.90) 0%, rgba(6,5,12,0.38) 50%, rgba(6,5,12,0.94) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(4,3,8,0.2) 0%, rgba(4,3,8,0.95) 100%);
|
||||||
--header-bg: rgba(6,5,12,0.93);
|
--header-bg: rgba(4,3,8,0.96);
|
||||||
--board-hover-border: rgba(224,128,48,0.26);
|
--board-hover-border: rgba(157, 92, 255, 0.22);
|
||||||
--toggle-on-bg: rgba(224,128,48,0.22);
|
--toggle-on-bg: rgba(224,128,48,0.22);
|
||||||
--logo-shadow: rgba(224,128,48,0.55);
|
--logo-shadow: rgba(157, 92, 255, 0.45);
|
||||||
}
|
}
|
||||||
[data-theme="event-horizon"] .board { border-color: rgba(224,128,48,0.10); }
|
[data-theme="event-horizon"] .board { border-color: rgba(157, 92, 255, 0.15); }
|
||||||
[data-theme="event-horizon"] .bm-item:hover { background: rgba(224,128,48,0.05); }
|
[data-theme="event-horizon"] .bm-item:hover { background: rgba(157, 92, 255, 0.08); }
|
||||||
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: MERCHANTMAN (teal / industrial sci-fi)
|
THEME: MERCHANTMAN (Emerald / industrial sci-fi)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="merchantman"] {
|
[data-theme="merchantman"] {
|
||||||
--accent: #4ecfcf;
|
--accent: #2eb8b8;
|
||||||
--accent-dim: rgba(78, 207, 207, 0.14);
|
--accent-dim: rgba(46, 184, 184, 0.12);
|
||||||
--accent-glow: rgba(78, 207, 207, 0.07);
|
--accent-glow: rgba(46, 184, 184, 0.06);
|
||||||
--border-accent: rgba(78, 207, 207, 0.32);
|
--border-accent: rgba(46, 184, 184, 0.25);
|
||||||
--bg-primary: #060d0d;
|
--bg-primary: #040808;
|
||||||
--bg-board: rgba(6, 14, 16, 0.50);
|
--bg-board: rgba(6, 10, 10, 0.58);
|
||||||
--border: rgba(78, 207, 207, 0.09);
|
--border: rgba(46, 184, 184, 0.10);
|
||||||
--text-primary: #c8e8e8;
|
--text-primary: #d1e8e8;
|
||||||
--text-secondary: #5a8888;
|
--text-secondary: #6a8f8f;
|
||||||
--text-muted: #2e5050;
|
--text-muted: #344f4f;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(160deg, rgba(6,13,13,0.88) 0%, rgba(6,13,13,0.40) 50%, rgba(6,13,13,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at 30% 40%, rgba(4,8,8,0.2) 0%, rgba(4,8,8,0.92) 100%);
|
||||||
--header-bg: rgba(6,13,13,0.93);
|
--header-bg: rgba(4,8,8,0.95);
|
||||||
--board-hover-border: rgba(78,207,207,0.26);
|
--board-hover-border: rgba(46, 184, 184, 0.20);
|
||||||
--toggle-on-bg: rgba(78,207,207,0.22);
|
--toggle-on-bg: rgba(78,207,207,0.22);
|
||||||
--logo-shadow: rgba(78,207,207,0.55);
|
--logo-shadow: rgba(46, 184, 184, 0.45);
|
||||||
}
|
}
|
||||||
[data-theme="merchantman"] .board { border-color: rgba(78,207,207,0.10); }
|
[data-theme="merchantman"] .board { border-color: rgba(46, 184, 184, 0.12); }
|
||||||
[data-theme="merchantman"] .bm-item:hover { background: rgba(78,207,207,0.05); }
|
[data-theme="merchantman"] .bm-item:hover { background: rgba(46, 184, 184, 0.06); }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: JULIA & JIN (blue night / FFXIV)
|
THEME: JULIA & JIN (Aetherial Night / FFXIV)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="julia-jin"] {
|
[data-theme="julia-jin"] {
|
||||||
--accent: #5b9fff;
|
--accent: #7db3ff;
|
||||||
--accent-dim: rgba(91, 159, 255, 0.15);
|
--accent-dim: rgba(125, 179, 255, 0.12);
|
||||||
--accent-glow: rgba(91, 159, 255, 0.07);
|
--accent-glow: rgba(125, 179, 255, 0.08);
|
||||||
--border-accent: rgba(91, 159, 255, 0.35);
|
--border-accent: rgba(125, 179, 255, 0.30);
|
||||||
--bg-primary: #06080f;
|
--bg-primary: #03050a;
|
||||||
--bg-board: rgba(8, 12, 26, 0.52);
|
--bg-board: rgba(7, 10, 20, 0.60);
|
||||||
--border: rgba(91, 159, 255, 0.09);
|
--border: rgba(125, 179, 255, 0.12);
|
||||||
--text-primary: #ccd8f8;
|
--text-primary: #eef2ff;
|
||||||
--text-secondary: #5c72a8;
|
--text-secondary: #8ba0d1;
|
||||||
--text-muted: #2e3a60;
|
--text-muted: #465685;
|
||||||
--font-display: 'Cinzel', serif;
|
--font-display: 'Cinzel', serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(6,8,15,0.85) 0%, rgba(6,8,15,0.38) 50%, rgba(6,8,15,0.90) 100%);
|
--overlay-bg: linear-gradient(180deg, rgba(3,5,10,0.85) 0%, rgba(3,5,10,0.25) 50%, rgba(3,5,10,0.92) 100%);
|
||||||
--header-bg: rgba(6,8,15,0.92);
|
--header-bg: rgba(3,5,10,0.94);
|
||||||
--board-hover-border: rgba(91,159,255,0.26);
|
--board-hover-border: rgba(125, 179, 255, 0.22);
|
||||||
--toggle-on-bg: rgba(91,159,255,0.22);
|
--toggle-on-bg: rgba(91,159,255,0.22);
|
||||||
--logo-shadow: rgba(91,159,255,0.55);
|
--logo-shadow: rgba(125, 179, 255, 0.50);
|
||||||
}
|
}
|
||||||
[data-theme="julia-jin"] .logo { font-family: 'Cinzel', serif; letter-spacing: 4px; }
|
[data-theme="julia-jin"] .logo { font-family: 'Cinzel', serif; letter-spacing: 5px; text-transform: uppercase; }
|
||||||
[data-theme="julia-jin"] .clock { font-family: 'Cinzel', serif; }
|
[data-theme="julia-jin"] .clock { font-family: 'Cinzel', serif; font-weight: 600; }
|
||||||
[data-theme="julia-jin"] .board-title { letter-spacing: 2px; }
|
[data-theme="julia-jin"] .board-title { letter-spacing: 2px; font-weight: 500; }
|
||||||
[data-theme="julia-jin"] .board { border-color: rgba(91,159,255,0.10); }
|
[data-theme="julia-jin"] .board { border-color: rgba(125, 179, 255, 0.15); backdrop-filter: blur(4px); }
|
||||||
[data-theme="julia-jin"] .bm-item:hover { background: rgba(91,159,255,0.06); }
|
[data-theme="julia-jin"] .bm-item:hover { background: rgba(125, 179, 255, 0.08); }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: SC SUNSET (amber / planet-side)
|
THEME: SC Sunset - Horizon Glow
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="sc-sunset"] {
|
[data-theme="sc-sunset"] {
|
||||||
--accent: #f07c30;
|
--accent: #ff8c3d;
|
||||||
--accent-dim: rgba(240, 124, 48, 0.15);
|
--accent-dim: rgba(255, 140, 61, 0.12);
|
||||||
--accent-glow: rgba(240, 124, 48, 0.07);
|
--accent-glow: rgba(255, 140, 61, 0.08);
|
||||||
--border-accent: rgba(240, 124, 48, 0.35);
|
--border-accent: rgba(255, 140, 61, 0.28);
|
||||||
--bg-primary: #0c0805;
|
--bg-primary: #080503;
|
||||||
--bg-board: rgba(18, 10, 5, 0.50);
|
--bg-board: rgba(15, 10, 8, 0.55);
|
||||||
--border: rgba(240, 124, 48, 0.09);
|
--border: rgba(255, 140, 61, 0.10);
|
||||||
--text-primary: #f0dcc8;
|
--text-primary: #fce8d5;
|
||||||
--text-secondary: #906050;
|
--text-secondary: #b08568;
|
||||||
--text-muted: #503828;
|
--text-muted: #634a3a;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(12,8,5,0.90) 0%, rgba(12,8,5,0.35) 50%, rgba(12,8,5,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at 50% 60%, rgba(8,5,3,0.1) 0%, rgba(8,5,3,0.92) 100%);
|
||||||
--header-bg: rgba(12,8,5,0.93);
|
--header-bg: rgba(8,5,3,0.94);
|
||||||
--board-hover-border: rgba(240,124,48,0.26);
|
--board-hover-border: rgba(255, 140, 61, 0.22);
|
||||||
--toggle-on-bg: rgba(240,124,48,0.22);
|
--toggle-on-bg: rgba(240,124,48,0.22);
|
||||||
--logo-shadow: rgba(240,124,48,0.55);
|
--logo-shadow: rgba(255, 140, 61, 0.45);
|
||||||
}
|
}
|
||||||
[data-theme="sc-sunset"] .board { border-color: rgba(240,124,48,0.10); }
|
[data-theme="sc-sunset"] .board {
|
||||||
[data-theme="sc-sunset"] .bm-item:hover { background: rgba(240,124,48,0.05); }
|
border-color: rgba(255, 140, 61, 0.15);
|
||||||
|
backdrop-filter: blur(6px);}
|
||||||
|
[data-theme="sc-sunset"] .bm-item:hover {
|
||||||
|
background: rgba(255, 140, 61, 0.08); }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: HELLION HUD (circuit board / red+green)
|
THEME: HELLION HUD (circuit board / red+green)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="hellion-hud"] {
|
[data-theme="hellion-hud"] {
|
||||||
--accent: #22cc44;
|
--accent: #32ff6a;
|
||||||
--accent-dim: rgba(34, 204, 68, 0.14);
|
--accent-dim: rgba(50, 255, 106, 0.10);
|
||||||
--accent-glow: rgba(34, 204, 68, 0.07);
|
--accent-glow: rgba(50, 255, 106, 0.05);
|
||||||
--border-accent: rgba(34, 204, 68, 0.30);
|
--border-accent: rgba(50, 255, 106, 0.25);
|
||||||
--bg-primary: #080a08;
|
--bg-primary: #030503;
|
||||||
--bg-board: rgba(8, 12, 8, 0.50);
|
--bg-board: rgba(5, 8, 5, 0.65);
|
||||||
--border: rgba(34, 204, 68, 0.09);
|
--border: rgba(50, 255, 106, 0.12);
|
||||||
--text-primary: #c8e8cc;
|
--text-primary: #d4ffd9;
|
||||||
--text-secondary: #507050;
|
--text-secondary: #6e9975;
|
||||||
--text-muted: #2c402c;
|
--text-muted: #334d38;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(160deg, rgba(8,10,8,0.88) 0%, rgba(8,10,8,0.42) 50%, rgba(8,10,8,0.92) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(3,5,3,0.15) 0%, rgba(3,5,3,0.95) 100%);
|
||||||
--header-bg: rgba(8,10,8,0.93);
|
--header-bg: rgba(3,5,3,0.96);
|
||||||
--board-hover-border: rgba(34,204,68,0.24);
|
--board-hover-border: rgba(50, 255, 106, 0.20);
|
||||||
--toggle-on-bg: rgba(34,204,68,0.20);
|
--toggle-on-bg: rgba(34,204,68,0.20);
|
||||||
--logo-shadow: rgba(34,204,68,0.50);
|
--logo-shadow: rgba(50, 255, 106, 0.40);
|
||||||
|
--danger: #ff4d4d;
|
||||||
|
}
|
||||||
|
[data-theme="hellion-hud"] .board {
|
||||||
|
border-color: rgba(50, 255, 106, 0.15);
|
||||||
|
backdrop-filter: blur(8px) brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="hellion-hud"] .clock {
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
text-shadow: 0 0 10px var(--accent-glow);
|
||||||
}
|
}
|
||||||
[data-theme="hellion-hud"] .board { border-color: rgba(34,204,68,0.10); }
|
|
||||||
[data-theme="hellion-hud"] .bm-item:hover { background: rgba(34,204,68,0.05); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
THEME: HELLION ENERGY (matrix / tactical green)
|
THEME: HELLION ENERGY (matrix / tactical green)
|
||||||
============================================ */
|
============================================ */
|
||||||
[data-theme="hellion-energy"] {
|
[data-theme="hellion-energy"] {
|
||||||
--accent: #00e87a;
|
--accent: #1eff8e;
|
||||||
--accent-dim: rgba(0, 232, 122, 0.13);
|
--accent-dim: rgba(30, 255, 142, 0.12);
|
||||||
--accent-glow: rgba(0, 232, 122, 0.06);
|
--accent-glow: rgba(30, 255, 142, 0.08);
|
||||||
--border-accent: rgba(0, 232, 122, 0.28);
|
--border-accent: rgba(30, 255, 142, 0.30);
|
||||||
--bg-primary: #040a06;
|
--bg-primary: #020503;
|
||||||
--bg-board: rgba(4, 12, 6, 0.52);
|
--bg-board: rgba(4, 7, 5, 0.60);
|
||||||
--border: rgba(0, 232, 122, 0.08);
|
--border: rgba(30, 255, 142, 0.12);
|
||||||
--text-primary: #b8f0d0;
|
--text-primary: #e0ffef;
|
||||||
--text-secondary: #3a7050;
|
--text-secondary: #5e997a;
|
||||||
--text-muted: #1e3a28;
|
--text-muted: #2a4d3a;
|
||||||
--font-display: 'Rajdhani', sans-serif;
|
--font-display: 'Rajdhani', sans-serif;
|
||||||
--font-body: 'Inter', sans-serif;
|
--font-body: 'Inter', sans-serif;
|
||||||
--overlay-bg: linear-gradient(180deg, rgba(4,10,6,0.90) 0%, rgba(4,10,6,0.40) 50%, rgba(4,10,6,0.94) 100%);
|
--overlay-bg: radial-gradient(circle at center, rgba(2,5,3,0.1) 0%, rgba(2,5,3,0.95) 100%);
|
||||||
--header-bg: rgba(4,10,6,0.94);
|
--header-bg: rgba(2,5,3,0.96);
|
||||||
--board-hover-border: rgba(0,232,122,0.22);
|
--board-hover-border: rgba(30, 255, 142, 0.25);
|
||||||
--toggle-on-bg: rgba(0,232,122,0.18);
|
--toggle-on-bg: rgba(0,232,122,0.18);
|
||||||
--logo-shadow: rgba(0,232,122,0.50);
|
--logo-shadow: rgba(30, 255, 142, 0.60);
|
||||||
|
}
|
||||||
|
[data-theme="hellion-energy"] .board {
|
||||||
|
border-color: rgba(30, 255, 142, 0.15);
|
||||||
|
backdrop-filter: blur(10px) saturate(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="hellion-energy"] .bm-item:hover {
|
||||||
|
background: rgba(30, 255, 142, 0.10);
|
||||||
|
box-shadow: inset 0 0 10px rgba(30, 255, 142, 0.05);
|
||||||
}
|
}
|
||||||
[data-theme="hellion-energy"] .board { border-color: rgba(0,232,122,0.09); }
|
|
||||||
[data-theme="hellion-energy"] .bm-item:hover { background: rgba(0,232,122,0.05); }
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
BASE STYLES
|
BASE STYLES
|
||||||
@@ -485,6 +543,9 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
.settings-section-title {
|
.settings-section-title {
|
||||||
font-family: var(--font-display); font-size: 10px; font-weight: 700;
|
font-family: var(--font-display); font-size: 10px; font-weight: 700;
|
||||||
letter-spacing: 2px; color: var(--text-muted); padding: 10px 18px 6px; text-transform: uppercase;
|
letter-spacing: 2px; color: var(--text-muted); padding: 10px 18px 6px; text-transform: uppercase;
|
||||||
|
cursor: pointer; user-select: none;
|
||||||
|
display: flex; align-items: center; gap: 6px;
|
||||||
|
background: none; border: none; width: 100%;
|
||||||
}
|
}
|
||||||
.settings-section-title.danger { color: var(--danger); }
|
.settings-section-title.danger { color: var(--danger); }
|
||||||
|
|
||||||
@@ -537,14 +598,18 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
text-align: center; text-transform: uppercase;
|
text-align: center; text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-card[data-value="nebula"] .theme-card-label { color: #d65cff; }
|
.theme-card[data-value="nebula"] .theme-card-label { color: #b359ff; } /* Deep Magenta */
|
||||||
.theme-card[data-value="crescent"] .theme-card-label { color: #c8a84a; }
|
.theme-card[data-value="crescent"] .theme-card-label { color: #d4bd8a; } /* Champagne Gold */
|
||||||
.theme-card[data-value="event-horizon"] .theme-card-label { color: #e08030; }
|
.theme-card[data-value="event-horizon"] .theme-card-label { color: #9d5cff; } /* Cosmic Purple */
|
||||||
.theme-card[data-value="merchantman"] .theme-card-label { color: #4ecfcf; }
|
.theme-card[data-value="merchantman"] .theme-card-label { color: #2eb8b8; } /* Emerald Teal */
|
||||||
.theme-card[data-value="julia-jin"] .theme-card-label { color: #5b9fff; }
|
.theme-card[data-value="julia-jin"] .theme-card-label { color: #7db3ff; } /* Aether Blue */
|
||||||
.theme-card[data-value="sc-sunset"] .theme-card-label { color: #f07c30; }
|
.theme-card[data-value="sc-sunset"] .theme-card-label { color: #ff8c3d; } /* Amber Sunset */
|
||||||
.theme-card[data-value="hellion-hud"] .theme-card-label { color: #22cc44; }
|
.theme-card[data-value="hellion-hud"] .theme-card-label { color: #32ff6a; } /* Neon Green */
|
||||||
.theme-card[data-value="hellion-energy"] .theme-card-label { color: #00e87a; }
|
.theme-card[data-value="hellion-energy"] .theme-card-label { color: #1eff8e; } /* Acid Green */
|
||||||
|
.theme-card:hover .theme-card-label {
|
||||||
|
text-shadow: 0 0 8px currentColor;
|
||||||
|
transition: text-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.theme-card-check {
|
.theme-card-check {
|
||||||
position: absolute; top: 5px; right: 5px;
|
position: absolute; top: 5px; right: 5px;
|
||||||
@@ -606,12 +671,13 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
.btn-primary:hover { filter: brightness(1.15); box-shadow: 0 0 18px var(--accent-glow); }
|
.btn-primary:hover { filter: brightness(1.15); box-shadow: 0 0 18px var(--accent-glow); }
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
padding: 5px 12px; background: rgba(224,85,85,0.1);
|
padding: 7px 16px; background: rgba(224,85,85,0.15);
|
||||||
border: 1px solid rgba(224,85,85,0.3); border-radius: var(--radius-sm);
|
border: 1px solid rgba(224,85,85,0.3); border-radius: var(--radius-sm);
|
||||||
color: var(--danger); font-family: var(--font-body); font-size: 12px;
|
color: var(--danger); font-family: var(--font-display); font-size: 12px;
|
||||||
|
font-weight: 600; letter-spacing: 1px; text-transform: uppercase;
|
||||||
cursor: pointer; transition: all 0.15s;
|
cursor: pointer; transition: all 0.15s;
|
||||||
}
|
}
|
||||||
.btn-danger:hover { background: rgba(224,85,85,0.2); }
|
.btn-danger:hover { background: rgba(224,85,85,0.25); border-color: rgba(224,85,85,0.5); }
|
||||||
|
|
||||||
.btn-close {
|
.btn-close {
|
||||||
background: none; border: none; color: var(--text-muted); font-size: 14px;
|
background: none; border: none; color: var(--text-muted); font-size: 14px;
|
||||||
@@ -906,6 +972,220 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
::-webkit-scrollbar-track { background: transparent; }
|
::-webkit-scrollbar-track { background: transparent; }
|
||||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
CUSTOM DIALOG SYSTEM
|
||||||
|
============================================ */
|
||||||
|
.dialog-overlay {
|
||||||
|
position: fixed; inset: 0; z-index: 400;
|
||||||
|
background: rgba(0,0,0,0.65); backdrop-filter: blur(7px);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
opacity: 0; pointer-events: none; transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.dialog-overlay.active { opacity: 1; pointer-events: all; }
|
||||||
|
|
||||||
|
.dialog-box {
|
||||||
|
background: rgba(10,9,20,0.98); border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius); width: 340px; max-width: 90vw;
|
||||||
|
backdrop-filter: blur(28px);
|
||||||
|
transform: translateY(8px) scale(0.98); transition: transform 0.2s;
|
||||||
|
box-shadow: 0 20px 60px rgba(0,0,0,0.7);
|
||||||
|
}
|
||||||
|
.dialog-overlay.active .dialog-box { transform: translateY(0) scale(1); }
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
display: flex; align-items: center; gap: 10px;
|
||||||
|
padding: 14px 16px 12px; border-bottom: 1px solid var(--border);
|
||||||
|
font-family: var(--font-display); font-size: 14px; font-weight: 600;
|
||||||
|
letter-spacing: 1.5px; color: var(--text-primary); text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-icon {
|
||||||
|
width: 20px; height: 20px; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.dialog-icon.type-info { color: var(--accent); }
|
||||||
|
.dialog-icon.type-success { color: #4ade80; }
|
||||||
|
.dialog-icon.type-warning { color: #f59e0b; }
|
||||||
|
.dialog-icon.type-danger { color: var(--danger); }
|
||||||
|
|
||||||
|
.dialog-body {
|
||||||
|
padding: 16px;
|
||||||
|
font-family: var(--font-body); font-size: 13px; line-height: 1.6;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-actions {
|
||||||
|
padding: 10px 16px 14px;
|
||||||
|
display: flex; justify-content: flex-end; gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent; border: 1px solid var(--border);
|
||||||
|
color: var(--text-secondary); padding: 7px 16px;
|
||||||
|
border-radius: var(--radius-sm); font-family: var(--font-display);
|
||||||
|
font-size: 12px; font-weight: 600; letter-spacing: 1px;
|
||||||
|
text-transform: uppercase; cursor: pointer;
|
||||||
|
transition: background 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: rgba(255,255,255,0.04); border-color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
ONBOARDING
|
||||||
|
============================================ */
|
||||||
|
.onboarding-modal {
|
||||||
|
background: rgba(10,9,20,0.98); border: 1px solid var(--border);
|
||||||
|
border-radius: 12px; width: 460px; max-width: 92vw;
|
||||||
|
backdrop-filter: blur(28px);
|
||||||
|
transform: translateY(8px) scale(0.98); transition: transform 0.25s;
|
||||||
|
box-shadow: 0 24px 80px rgba(0,0,0,0.7);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.dialog-overlay.active .onboarding-modal { transform: translateY(0) scale(1); }
|
||||||
|
|
||||||
|
.onboarding-skip {
|
||||||
|
position: absolute; top: 14px; right: 16px;
|
||||||
|
background: none; border: none; color: var(--text-muted);
|
||||||
|
font-family: var(--font-body); font-size: 12px;
|
||||||
|
cursor: pointer; letter-spacing: 0.5px;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
.onboarding-skip:hover { color: var(--text-secondary); }
|
||||||
|
|
||||||
|
.onboarding-slide {
|
||||||
|
padding: 32px 28px 20px; text-align: center;
|
||||||
|
animation: onboardingFadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
@keyframes onboardingFadeIn {
|
||||||
|
from { opacity: 0; transform: translateX(12px); }
|
||||||
|
to { opacity: 1; transform: translateX(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-hero {
|
||||||
|
font-size: 48px; margin-bottom: 16px; line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-title {
|
||||||
|
font-family: var(--font-display); font-size: 20px; font-weight: 700;
|
||||||
|
letter-spacing: 2px; text-transform: uppercase;
|
||||||
|
color: var(--text-primary); margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-text {
|
||||||
|
font-family: var(--font-body); font-size: 13px; line-height: 1.7;
|
||||||
|
color: var(--text-secondary); max-width: 360px; margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-feature-list {
|
||||||
|
list-style: none; padding: 0; margin: 14px auto 0;
|
||||||
|
text-align: left; max-width: 320px;
|
||||||
|
}
|
||||||
|
.onboarding-feature-list li {
|
||||||
|
font-family: var(--font-body); font-size: 13px;
|
||||||
|
color: var(--text-secondary); padding: 5px 0;
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
}
|
||||||
|
.onboarding-feature-list li::before {
|
||||||
|
content: ''; width: 6px; height: 6px; border-radius: 50%;
|
||||||
|
background: var(--accent); flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-theme-grid {
|
||||||
|
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
.onboarding-theme-chip {
|
||||||
|
padding: 6px 4px; border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--border); background: rgba(255,255,255,0.03);
|
||||||
|
font-family: var(--font-body); font-size: 11px; color: var(--text-secondary);
|
||||||
|
text-align: center; cursor: default;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-footer {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 14px 28px 20px; border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-dots {
|
||||||
|
display: flex; gap: 6px;
|
||||||
|
}
|
||||||
|
.onboarding-dot {
|
||||||
|
width: 8px; height: 8px; border-radius: 50%;
|
||||||
|
background: var(--border); transition: background 0.2s, transform 0.2s;
|
||||||
|
}
|
||||||
|
.onboarding-dot.active {
|
||||||
|
background: var(--accent); transform: scale(1.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onboarding-nav {
|
||||||
|
display: flex; gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
THEME PICKER MODAL
|
||||||
|
============================================ */
|
||||||
|
.theme-modal {
|
||||||
|
background: rgba(8,8,16,0.96);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
backdrop-filter: blur(28px);
|
||||||
|
max-width: 520px; width: 90%;
|
||||||
|
max-height: 85vh; overflow-y: auto;
|
||||||
|
scrollbar-width: thin; scrollbar-color: var(--border) transparent;
|
||||||
|
}
|
||||||
|
.theme-modal .theme-grid {
|
||||||
|
padding: 12px 16px 16px;
|
||||||
|
}
|
||||||
|
.theme-modal-section {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
}
|
||||||
|
.theme-modal-section .settings-section-title {
|
||||||
|
padding: 12px 0 8px;
|
||||||
|
cursor: default; user-select: auto;
|
||||||
|
}
|
||||||
|
.theme-modal-section .setting-row {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
ACCORDION SETTINGS
|
||||||
|
============================================ */
|
||||||
|
.section-chevron {
|
||||||
|
font-size: 10px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.settings-section.open .section-chevron {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
.section-content {
|
||||||
|
max-height: 0; overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease-out;
|
||||||
|
}
|
||||||
|
.settings-section.open .section-content {
|
||||||
|
max-height: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
UTILITY CLASSES
|
||||||
|
============================================ */
|
||||||
|
.hidden { display: none; }
|
||||||
|
.accent-text { color: var(--accent); }
|
||||||
|
.dim { opacity: 0.4; }
|
||||||
|
.drag-ghost {
|
||||||
|
position: fixed; opacity: 0.75; pointer-events: none; z-index: 9999;
|
||||||
|
transform: rotate(1.5deg) scale(1.02);
|
||||||
|
box-shadow: 0 12px 40px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
.bm-item.drag-over { background: rgba(255,160,50,0.07); }
|
||||||
|
.bm-item.dragging-source { opacity: 0.4; }
|
||||||
|
.about-info-label-block { display: block; margin-bottom: 6px; }
|
||||||
|
.about-link-subtle { color: var(--text-secondary); text-decoration: none; }
|
||||||
|
.modal-input-spaced { margin-top: 8px; }
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
RESPONSIVE — Mobile & Tablet
|
RESPONSIVE — Mobile & Tablet
|
||||||
============================================ */
|
============================================ */
|
||||||
@@ -928,6 +1208,7 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
|
|
||||||
.settings-panel { width: 320px; }
|
.settings-panel { width: 320px; }
|
||||||
.theme-grid { grid-template-columns: 1fr 1fr; }
|
.theme-grid { grid-template-columns: 1fr 1fr; }
|
||||||
|
.theme-modal { max-width: 400px; }
|
||||||
|
|
||||||
.search-bar { max-width: 400px; }
|
.search-bar { max-width: 400px; }
|
||||||
}
|
}
|
||||||
@@ -955,6 +1236,7 @@ body.show-desc .bm-desc { display: block; }
|
|||||||
|
|
||||||
.settings-panel { width: 100%; }
|
.settings-panel { width: 100%; }
|
||||||
.theme-grid { grid-template-columns: 1fr 1fr; gap: 6px; }
|
.theme-grid { grid-template-columns: 1fr 1fr; gap: 6px; }
|
||||||
|
.theme-modal { max-width: 100%; width: calc(100vw - 32px); }
|
||||||
|
|
||||||
.search-bar-wrapper { padding: 6px 10px; }
|
.search-bar-wrapper { padding: 6px 10px; }
|
||||||
.search-bar { max-width: 100%; }
|
.search-bar { max-width: 100%; }
|
||||||
|
|||||||
+57
-3
@@ -19,6 +19,54 @@ async function init() {
|
|||||||
initStickyNote();
|
initStickyNote();
|
||||||
initDataButtons();
|
initDataButtons();
|
||||||
Store.checkQuota();
|
Store.checkQuota();
|
||||||
|
|
||||||
|
// Onboarding beim ersten Start
|
||||||
|
const onboardingDone = await Store.get('onboardingDone');
|
||||||
|
if (!onboardingDone) {
|
||||||
|
Onboarding.start();
|
||||||
|
} else {
|
||||||
|
// Backup-Reminder (nur wenn Onboarding schon durch ist)
|
||||||
|
await checkBackupReminder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- BACKUP REMINDER ----
|
||||||
|
const BACKUP_INTERVAL_MS = 7 * 24 * 60 * 60 * 1000; // 7 Tage
|
||||||
|
|
||||||
|
async function checkBackupReminder() {
|
||||||
|
const lastReminder = await Store.get('lastBackupReminder');
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Beim allerersten Mal: Timestamp setzen, aber noch nicht nerven
|
||||||
|
if (!lastReminder) {
|
||||||
|
await Store.set('lastBackupReminder', now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - lastReminder < BACKUP_INTERVAL_MS) return;
|
||||||
|
|
||||||
|
// Nur erinnern wenn es Boards gibt die sich lohnen zu sichern
|
||||||
|
if (boards.length === 0) return;
|
||||||
|
|
||||||
|
const doBackup = await HellionDialog.confirm(
|
||||||
|
'Du hast seit über einer Woche kein Backup gemacht. Beim Löschen der Browserdaten gehen deine Boards verloren. Jetzt sichern?',
|
||||||
|
{ type: 'warning', title: 'Backup-Erinnerung', confirmText: 'Jetzt sichern', cancelText: 'Später' }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (doBackup) {
|
||||||
|
// JSON-Export auslösen (gleiche Logik wie btnExportJSON)
|
||||||
|
const data = { version: '1.5.2', exported: new Date().toISOString(), boards, settings };
|
||||||
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'hellion-newtab-backup-' + new Date().toISOString().slice(0, 10) + '.json';
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timestamp immer aktualisieren (egal ob gesichert oder "Später")
|
||||||
|
await Store.set('lastBackupReminder', now);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- CLOCK & DATE ----
|
// ---- CLOCK & DATE ----
|
||||||
@@ -50,12 +98,18 @@ function bindGlobalEvents() {
|
|||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
const imported = parseBookmarkHtml(await file.text());
|
const imported = parseBookmarkHtml(await file.text());
|
||||||
if (imported.length === 0) { alert('Keine Bookmarks gefunden.'); return; }
|
if (imported.length === 0) {
|
||||||
|
await HellionDialog.alert('Keine Bookmarks in dieser Datei gefunden.', { type: 'warning', title: 'Import' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
boards = [...boards, ...imported];
|
boards = [...boards, ...imported];
|
||||||
await saveBoards();
|
await saveBoards();
|
||||||
renderBoards();
|
renderBoards();
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
alert(`✓ ${imported.length} Board(s) mit ${imported.reduce((s,b) => s + b.bookmarks.length, 0)} Bookmarks importiert.`);
|
await HellionDialog.alert(
|
||||||
|
`${imported.length} Board(s) mit ${imported.reduce((s,b) => s + b.bookmarks.length, 0)} Bookmarks importiert.`,
|
||||||
|
{ type: 'success', title: 'Import erfolgreich' }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Board Modal
|
// Add Board Modal
|
||||||
@@ -86,7 +140,7 @@ function bindGlobalEvents() {
|
|||||||
const url = document.getElementById('newBmUrl').value.trim();
|
const url = document.getElementById('newBmUrl').value.trim();
|
||||||
const desc = document.getElementById('newBmDesc').value.trim();
|
const desc = document.getElementById('newBmDesc').value.trim();
|
||||||
if (!title || !url) return;
|
if (!title || !url) return;
|
||||||
try { new URL(url); } catch { alert('Ungültige URL. Bitte mit https:// beginnen.'); return; }
|
try { new URL(url); } catch { await HellionDialog.alert('Ungültige URL. Bitte mit https:// beginnen.', { type: 'warning', title: 'URL ungültig' }); return; }
|
||||||
const board = boards.find(b => b.id === pendingBookmarkBoardId);
|
const board = boards.find(b => b.id === pendingBookmarkBoardId);
|
||||||
if (!board) return;
|
if (!board) return;
|
||||||
board.bookmarks.push({ id: uid(), title, url, desc });
|
board.bookmarks.push({ id: uid(), title, url, desc });
|
||||||
|
|||||||
+105
-33
@@ -6,16 +6,67 @@
|
|||||||
let pendingBookmarkBoardId = null;
|
let pendingBookmarkBoardId = null;
|
||||||
let pendingRenameCallback = null;
|
let pendingRenameCallback = null;
|
||||||
|
|
||||||
|
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erzeugt ein SVG-Element mit Attributen und Kinder-Elementen.
|
||||||
|
* @param {string} tag - SVG-Tag (z.B. 'svg', 'circle', 'line')
|
||||||
|
* @param {Object} attrs - Attribute als Key-Value
|
||||||
|
* @param {Array} children - Kind-Elemente
|
||||||
|
* @returns {SVGElement}
|
||||||
|
*/
|
||||||
|
function svgEl(tag, attrs, children) {
|
||||||
|
const el = document.createElementNS(SVG_NS, tag);
|
||||||
|
if (attrs) {
|
||||||
|
for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
|
||||||
|
}
|
||||||
|
if (children) {
|
||||||
|
for (const child of children) el.appendChild(child);
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Erzeugt das 6-Punkt Drag-Handle SVG */
|
||||||
|
function createDragHandleSvg() {
|
||||||
|
return svgEl('svg', { width: '10', height: '14', viewBox: '0 0 10 14', fill: 'currentColor' }, [
|
||||||
|
svgEl('circle', { cx: '2', cy: '2', r: '1.5' }),
|
||||||
|
svgEl('circle', { cx: '8', cy: '2', r: '1.5' }),
|
||||||
|
svgEl('circle', { cx: '2', cy: '7', r: '1.5' }),
|
||||||
|
svgEl('circle', { cx: '8', cy: '7', r: '1.5' }),
|
||||||
|
svgEl('circle', { cx: '2', cy: '12', r: '1.5' }),
|
||||||
|
svgEl('circle', { cx: '8', cy: '12', r: '1.5' }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Erzeugt das Plus-Icon SVG */
|
||||||
|
function createPlusSvg() {
|
||||||
|
return svgEl('svg', { width: '11', height: '11', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' }, [
|
||||||
|
svgEl('line', { x1: '12', y1: '5', x2: '12', y2: '19' }),
|
||||||
|
svgEl('line', { x1: '5', y1: '12', x2: '19', y2: '12' }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- RENDER ----
|
// ---- RENDER ----
|
||||||
function renderBoards() {
|
function renderBoards() {
|
||||||
const wrapper = document.getElementById('boardsWrapper');
|
const wrapper = document.getElementById('boardsWrapper');
|
||||||
wrapper.innerHTML = '';
|
wrapper.replaceChildren();
|
||||||
|
|
||||||
if (boards.length === 0) {
|
if (boards.length === 0) {
|
||||||
wrapper.innerHTML = `<div class="empty-state">
|
const empty = document.createElement('div');
|
||||||
No boards yet. Click <strong style="color:var(--accent)">+ Board</strong> to create one,
|
empty.className = 'empty-state';
|
||||||
or use <strong style="color:var(--accent)">Import</strong> to load your browser bookmarks.
|
|
||||||
</div>`;
|
const boardStrong = document.createElement('strong');
|
||||||
|
boardStrong.className = 'accent-text';
|
||||||
|
boardStrong.textContent = '+ Board';
|
||||||
|
|
||||||
|
const importStrong = document.createElement('strong');
|
||||||
|
importStrong.className = 'accent-text';
|
||||||
|
importStrong.textContent = 'Import';
|
||||||
|
|
||||||
|
empty.append(
|
||||||
|
'No boards yet. Click ', boardStrong, ' to create one, or use ', importStrong, ' to load your browser bookmarks.'
|
||||||
|
);
|
||||||
|
wrapper.appendChild(empty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,43 +82,59 @@ function createBoardEl(board) {
|
|||||||
// Header
|
// Header
|
||||||
const header = document.createElement('div');
|
const header = document.createElement('div');
|
||||||
header.className = 'board-header';
|
header.className = 'board-header';
|
||||||
header.innerHTML = `
|
|
||||||
<span class="board-drag-handle" title="Board verschieben">
|
const dragHandle = document.createElement('span');
|
||||||
<svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor">
|
dragHandle.className = 'board-drag-handle';
|
||||||
<circle cx="2" cy="2" r="1.5"/><circle cx="8" cy="2" r="1.5"/>
|
dragHandle.title = 'Board verschieben';
|
||||||
<circle cx="2" cy="7" r="1.5"/><circle cx="8" cy="7" r="1.5"/>
|
dragHandle.appendChild(createDragHandleSvg());
|
||||||
<circle cx="2" cy="12" r="1.5"/><circle cx="8" cy="12" r="1.5"/>
|
|
||||||
</svg>
|
const titleSpanHeader = document.createElement('span');
|
||||||
</span>
|
titleSpanHeader.className = 'board-title';
|
||||||
<span class="board-title" title="${escHtml(board.title)}">${escHtml(board.title)}</span>
|
titleSpanHeader.title = board.title;
|
||||||
<div class="board-actions">
|
titleSpanHeader.textContent = board.title;
|
||||||
<button class="board-action-btn btn-blur-board" title="${board.blurred ? 'Unblur' : 'Blur (privat)'}">🔒</button>
|
|
||||||
<button class="board-action-btn btn-rename-board" title="Umbenennen">✎</button>
|
const actions = document.createElement('div');
|
||||||
<button class="board-action-btn btn-delete-board" title="Löschen">✕</button>
|
actions.className = 'board-actions';
|
||||||
</div>
|
|
||||||
`;
|
const btnBlur = document.createElement('button');
|
||||||
|
btnBlur.className = 'board-action-btn btn-blur-board';
|
||||||
|
btnBlur.title = board.blurred ? 'Unblur' : 'Blur (privat)';
|
||||||
|
btnBlur.textContent = '\uD83D\uDD12';
|
||||||
|
|
||||||
|
const btnRename = document.createElement('button');
|
||||||
|
btnRename.className = 'board-action-btn btn-rename-board';
|
||||||
|
btnRename.title = 'Umbenennen';
|
||||||
|
btnRename.textContent = '\u270E';
|
||||||
|
|
||||||
|
const btnDelete = document.createElement('button');
|
||||||
|
btnDelete.className = 'board-action-btn btn-delete-board';
|
||||||
|
btnDelete.title = 'Löschen';
|
||||||
|
btnDelete.textContent = '\u2715';
|
||||||
|
|
||||||
|
actions.append(btnBlur, btnRename, btnDelete);
|
||||||
|
header.append(dragHandle, titleSpanHeader, actions);
|
||||||
|
|
||||||
// Blur-Overlay
|
// Blur-Overlay
|
||||||
const blurOverlay = document.createElement('div');
|
const blurOverlay = document.createElement('div');
|
||||||
blurOverlay.className = 'board-blur-overlay';
|
blurOverlay.className = 'board-blur-overlay';
|
||||||
div.appendChild(blurOverlay);
|
div.appendChild(blurOverlay);
|
||||||
|
|
||||||
header.querySelector('.btn-blur-board').addEventListener('click', async e => {
|
btnBlur.addEventListener('click', async e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
board.blurred = !board.blurred;
|
board.blurred = !board.blurred;
|
||||||
div.classList.toggle('blurred', board.blurred);
|
div.classList.toggle('blurred', board.blurred);
|
||||||
e.currentTarget.title = board.blurred ? 'Unblur' : 'Blur (privat)';
|
btnBlur.title = board.blurred ? 'Unblur' : 'Blur (privat)';
|
||||||
await saveBoards();
|
await saveBoards();
|
||||||
});
|
});
|
||||||
|
|
||||||
blurOverlay.addEventListener('click', async () => {
|
blurOverlay.addEventListener('click', async () => {
|
||||||
board.blurred = false;
|
board.blurred = false;
|
||||||
div.classList.remove('blurred');
|
div.classList.remove('blurred');
|
||||||
header.querySelector('.btn-blur-board').title = 'Blur (privat)';
|
btnBlur.title = 'Blur (privat)';
|
||||||
await saveBoards();
|
await saveBoards();
|
||||||
});
|
});
|
||||||
|
|
||||||
header.querySelector('.btn-rename-board').addEventListener('click', e => {
|
btnRename.addEventListener('click', e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openRenameModal(board.title, async newName => {
|
openRenameModal(board.title, async newName => {
|
||||||
if (!newName.trim()) return;
|
if (!newName.trim()) return;
|
||||||
@@ -77,11 +144,16 @@ function createBoardEl(board) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
header.querySelector('.btn-delete-board').addEventListener('click', e => {
|
btnDelete.addEventListener('click', async e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (confirm(`Board "${board.title}" löschen?`)) {
|
const ok = await HellionDialog.confirm(
|
||||||
|
`Board "${board.title}" wirklich löschen?`,
|
||||||
|
{ type: 'danger', title: 'Board löschen', confirmText: 'Löschen' }
|
||||||
|
);
|
||||||
|
if (ok) {
|
||||||
boards = boards.filter(b => b.id !== board.id);
|
boards = boards.filter(b => b.id !== board.id);
|
||||||
saveBoards().then(renderBoards);
|
await saveBoards();
|
||||||
|
renderBoards();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,7 +199,8 @@ function createBoardEl(board) {
|
|||||||
// Add Bookmark
|
// Add Bookmark
|
||||||
const addBtn = document.createElement('button');
|
const addBtn = document.createElement('button');
|
||||||
addBtn.className = 'add-bm-btn';
|
addBtn.className = 'add-bm-btn';
|
||||||
addBtn.innerHTML = `<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> Add link`;
|
addBtn.appendChild(createPlusSvg());
|
||||||
|
addBtn.append(' Add link');
|
||||||
addBtn.addEventListener('click', () => openAddBookmarkModal(board.id));
|
addBtn.addEventListener('click', () => openAddBookmarkModal(board.id));
|
||||||
div.appendChild(addBtn);
|
div.appendChild(addBtn);
|
||||||
|
|
||||||
@@ -148,13 +221,12 @@ function createBmEl(bm) {
|
|||||||
favicon.height = 14;
|
favicon.height = 14;
|
||||||
favicon.src = getFaviconUrl(bm.url);
|
favicon.src = getFaviconUrl(bm.url);
|
||||||
favicon.addEventListener('error', function() {
|
favicon.addEventListener('error', function() {
|
||||||
this.style.display = 'none';
|
this.classList.add('hidden');
|
||||||
this.nextElementSibling.style.display = 'flex';
|
this.nextElementSibling.classList.remove('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
const fallback = document.createElement('div');
|
const fallback = document.createElement('div');
|
||||||
fallback.className = 'bm-favicon-fallback';
|
fallback.className = 'bm-favicon-fallback hidden';
|
||||||
fallback.style.display = 'none';
|
|
||||||
fallback.textContent = bm.title.charAt(0).toUpperCase();
|
fallback.textContent = bm.title.charAt(0).toUpperCase();
|
||||||
|
|
||||||
const textDiv = document.createElement('div');
|
const textDiv = document.createElement('div');
|
||||||
@@ -273,4 +345,4 @@ function parseBookmarkHtml(html) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
+11
-4
@@ -11,7 +11,7 @@ function initDataButtons() {
|
|||||||
|
|
||||||
// Export
|
// Export
|
||||||
btnExport.addEventListener('click', () => {
|
btnExport.addEventListener('click', () => {
|
||||||
const data = { version: '1.2.0', exported: new Date().toISOString(), boards, settings };
|
const data = { version: '1.5.2', exported: new Date().toISOString(), boards, settings };
|
||||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
@@ -42,13 +42,20 @@ function initDataButtons() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (validBoards.length === 0) throw new Error('Keine gültigen Boards gefunden');
|
if (validBoards.length === 0) throw new Error('Keine gültigen Boards gefunden');
|
||||||
if (!confirm(`${validBoards.length} Boards importieren? Bestehende Daten bleiben erhalten.`)) return;
|
const ok = await HellionDialog.confirm(
|
||||||
|
`${validBoards.length} Boards importieren? Bestehende Daten bleiben erhalten.`,
|
||||||
|
{ type: 'info', title: 'JSON Import' }
|
||||||
|
);
|
||||||
|
if (!ok) return;
|
||||||
boards = [...boards, ...validBoards];
|
boards = [...boards, ...validBoards];
|
||||||
await saveBoards();
|
await saveBoards();
|
||||||
renderBoards();
|
renderBoards();
|
||||||
alert(`✓ ${validBoards.length} Board(s) importiert.`);
|
await HellionDialog.alert(
|
||||||
|
`${validBoards.length} Board(s) erfolgreich importiert.`,
|
||||||
|
{ type: 'success', title: 'Import erfolgreich' }
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Fehler beim Import: ' + err.message);
|
await HellionDialog.alert('Fehler beim Import: ' + err.message, { type: 'danger', title: 'Import fehlgeschlagen' });
|
||||||
}
|
}
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/* =============================================
|
||||||
|
HELLION NEWTAB — dialog.js
|
||||||
|
Custom Dialog System (ersetzt native alert/confirm)
|
||||||
|
============================================= */
|
||||||
|
|
||||||
|
const HellionDialog = {
|
||||||
|
/** SVG-Icons je nach Dialog-Typ */
|
||||||
|
_icons: {
|
||||||
|
info: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>',
|
||||||
|
success: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
|
||||||
|
warning: '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
|
||||||
|
danger: '<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>'
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erzeugt das SVG-Icon-Element
|
||||||
|
* @param {string} type - info | success | warning | danger
|
||||||
|
* @returns {SVGElement}
|
||||||
|
*/
|
||||||
|
_createIcon(type) {
|
||||||
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
svg.setAttribute('width', '20');
|
||||||
|
svg.setAttribute('height', '20');
|
||||||
|
svg.setAttribute('viewBox', '0 0 24 24');
|
||||||
|
svg.setAttribute('fill', 'none');
|
||||||
|
svg.setAttribute('stroke', 'currentColor');
|
||||||
|
svg.setAttribute('stroke-width', '2');
|
||||||
|
svg.setAttribute('stroke-linecap', 'round');
|
||||||
|
svg.setAttribute('stroke-linejoin', 'round');
|
||||||
|
svg.className.baseVal = 'dialog-icon type-' + type;
|
||||||
|
// SVG-Pfade müssen per innerHTML gesetzt werden (kein User-Input, nur statische Pfade)
|
||||||
|
svg.innerHTML = this._icons[type] || this._icons.info;
|
||||||
|
return svg;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt und zeigt einen Dialog
|
||||||
|
* @param {Object} config
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
_show(config) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'dialog-overlay';
|
||||||
|
|
||||||
|
const box = document.createElement('div');
|
||||||
|
box.className = 'dialog-box';
|
||||||
|
|
||||||
|
// Header
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'dialog-header';
|
||||||
|
header.appendChild(this._createIcon(config.type));
|
||||||
|
const titleSpan = document.createElement('span');
|
||||||
|
titleSpan.textContent = config.title;
|
||||||
|
header.appendChild(titleSpan);
|
||||||
|
|
||||||
|
// Body
|
||||||
|
const body = document.createElement('div');
|
||||||
|
body.className = 'dialog-body';
|
||||||
|
body.textContent = config.message;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
const actions = document.createElement('div');
|
||||||
|
actions.className = 'dialog-actions';
|
||||||
|
|
||||||
|
function cleanup(result) {
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
document.removeEventListener('keydown', keyHandler);
|
||||||
|
setTimeout(() => overlay.remove(), 200);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel-Button (nur bei confirm)
|
||||||
|
if (config.isConfirm) {
|
||||||
|
const cancelBtn = document.createElement('button');
|
||||||
|
cancelBtn.className = 'btn-secondary';
|
||||||
|
cancelBtn.textContent = config.cancelText;
|
||||||
|
cancelBtn.addEventListener('click', () => cleanup(false));
|
||||||
|
actions.appendChild(cancelBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm/OK-Button
|
||||||
|
const confirmBtn = document.createElement('button');
|
||||||
|
confirmBtn.className = config.type === 'danger' && config.isConfirm ? 'btn-danger' : 'btn-primary';
|
||||||
|
confirmBtn.textContent = config.confirmText;
|
||||||
|
confirmBtn.addEventListener('click', () => cleanup(config.isConfirm ? true : undefined));
|
||||||
|
actions.appendChild(confirmBtn);
|
||||||
|
|
||||||
|
box.append(header, body, actions);
|
||||||
|
overlay.appendChild(box);
|
||||||
|
|
||||||
|
// Overlay-Klick schließt
|
||||||
|
overlay.addEventListener('click', e => {
|
||||||
|
if (e.target === overlay) cleanup(config.isConfirm ? false : undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keyboard
|
||||||
|
function keyHandler(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
cleanup(config.isConfirm ? true : undefined);
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
cleanup(config.isConfirm ? false : undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', keyHandler);
|
||||||
|
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
// Nächster Frame für CSS-Transition
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
overlay.classList.add('active');
|
||||||
|
confirmBtn.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt einen Alert-Dialog (ersetzt window.alert)
|
||||||
|
* @param {string} message - Nachricht
|
||||||
|
* @param {Object} [options] - { title, confirmText, type }
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
alert(message, options) {
|
||||||
|
const opts = options || {};
|
||||||
|
return this._show({
|
||||||
|
message,
|
||||||
|
title: opts.title || 'Hinweis',
|
||||||
|
confirmText: opts.confirmText || 'OK',
|
||||||
|
cancelText: '',
|
||||||
|
type: opts.type || 'info',
|
||||||
|
isConfirm: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt einen Confirm-Dialog (ersetzt window.confirm)
|
||||||
|
* @param {string} message - Nachricht
|
||||||
|
* @param {Object} [options] - { title, confirmText, cancelText, type }
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
confirm(message, options) {
|
||||||
|
const opts = options || {};
|
||||||
|
return this._show({
|
||||||
|
message,
|
||||||
|
title: opts.title || 'Bestätigung',
|
||||||
|
confirmText: opts.confirmText || 'OK',
|
||||||
|
cancelText: opts.cancelText || 'Abbrechen',
|
||||||
|
type: opts.type || 'info',
|
||||||
|
isConfirm: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
+42
-31
@@ -37,13 +37,11 @@ function initBoardDragDrop() {
|
|||||||
|
|
||||||
// Ghost
|
// Ghost
|
||||||
const ghost = boardEl.cloneNode(true);
|
const ghost = boardEl.cloneNode(true);
|
||||||
ghost.style.cssText = `
|
ghost.className += ' drag-ghost';
|
||||||
position:fixed; left:${rect.left}px; top:${rect.top}px;
|
ghost.style.left = rect.left + 'px';
|
||||||
width:${rect.width}px; height:${rect.height}px;
|
ghost.style.top = rect.top + 'px';
|
||||||
opacity:0.75; pointer-events:none; z-index:9999;
|
ghost.style.width = rect.width + 'px';
|
||||||
transform:rotate(1.5deg) scale(1.02);
|
ghost.style.height = rect.height + 'px';
|
||||||
box-shadow:0 12px 40px rgba(0,0,0,0.6);
|
|
||||||
`;
|
|
||||||
document.body.appendChild(ghost);
|
document.body.appendChild(ghost);
|
||||||
|
|
||||||
// Placeholder
|
// Placeholder
|
||||||
@@ -104,29 +102,42 @@ function initBoardDragDrop() {
|
|||||||
function initBookmarkDragDrop(listEl, board) {
|
function initBookmarkDragDrop(listEl, board) {
|
||||||
let dragSrcBmId = null;
|
let dragSrcBmId = null;
|
||||||
|
|
||||||
listEl.querySelectorAll('.bm-item').forEach(item => {
|
listEl.addEventListener('dragstart', e => {
|
||||||
item.addEventListener('dragstart', e => {
|
const item = e.target.closest('.bm-item');
|
||||||
dragSrcBmId = item.dataset.bmId;
|
if (!item) return;
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
dragSrcBmId = item.dataset.bmId;
|
||||||
setTimeout(() => item.style.opacity = '0.4', 0);
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
});
|
setTimeout(() => item.classList.add('dragging-source'), 0);
|
||||||
item.addEventListener('dragend', () => { item.style.opacity = ''; });
|
});
|
||||||
item.addEventListener('dragover', e => {
|
|
||||||
e.preventDefault();
|
listEl.addEventListener('dragend', e => {
|
||||||
item.style.background = 'rgba(255,160,50,0.07)';
|
const item = e.target.closest('.bm-item');
|
||||||
});
|
if (item) item.classList.remove('dragging-source');
|
||||||
item.addEventListener('dragleave', () => { item.style.background = ''; });
|
});
|
||||||
item.addEventListener('drop', async e => {
|
|
||||||
e.preventDefault(); e.stopPropagation();
|
listEl.addEventListener('dragover', e => {
|
||||||
item.style.background = '';
|
e.preventDefault();
|
||||||
const targetBmId = item.dataset.bmId;
|
const item = e.target.closest('.bm-item');
|
||||||
if (!dragSrcBmId || dragSrcBmId === targetBmId) return;
|
if (item) item.classList.add('drag-over');
|
||||||
const srcIdx = board.bookmarks.findIndex(b => b.id === dragSrcBmId);
|
});
|
||||||
const tgtIdx = board.bookmarks.findIndex(b => b.id === targetBmId);
|
|
||||||
const [moved] = board.bookmarks.splice(srcIdx, 1);
|
listEl.addEventListener('dragleave', e => {
|
||||||
board.bookmarks.splice(tgtIdx, 0, moved);
|
const item = e.target.closest('.bm-item');
|
||||||
await saveBoards();
|
if (item) item.classList.remove('drag-over');
|
||||||
renderBoards();
|
});
|
||||||
});
|
|
||||||
|
listEl.addEventListener('drop', async e => {
|
||||||
|
e.preventDefault(); e.stopPropagation();
|
||||||
|
const item = e.target.closest('.bm-item');
|
||||||
|
if (!item) return;
|
||||||
|
item.classList.remove('drag-over');
|
||||||
|
const targetBmId = item.dataset.bmId;
|
||||||
|
if (!dragSrcBmId || dragSrcBmId === targetBmId) return;
|
||||||
|
const srcIdx = board.bookmarks.findIndex(b => b.id === dragSrcBmId);
|
||||||
|
const tgtIdx = board.bookmarks.findIndex(b => b.id === targetBmId);
|
||||||
|
const [moved] = board.bookmarks.splice(srcIdx, 1);
|
||||||
|
board.bookmarks.splice(tgtIdx, 0, moved);
|
||||||
|
await saveBoards();
|
||||||
|
renderBoards();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/* =============================================
|
||||||
|
HELLION NEWTAB — onboarding.js
|
||||||
|
Mehrstufiger Willkommens-Flow beim ersten Start
|
||||||
|
============================================= */
|
||||||
|
|
||||||
|
const Onboarding = {
|
||||||
|
currentSlide: 0,
|
||||||
|
|
||||||
|
slides: [
|
||||||
|
{
|
||||||
|
hero: '\u2B21',
|
||||||
|
title: 'Willkommen bei Hellion Dashboard',
|
||||||
|
text: 'Dein neuer Browser-Startbildschirm. Minimalistisch, schnell und vollst\u00E4ndig lokal \u2014 keine Cloud, kein Account, keine Datensammlung.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hero: '\uD83D\uDCCB',
|
||||||
|
title: 'Boards & Bookmarks',
|
||||||
|
features: [
|
||||||
|
'Erstelle Boards mit dem \u201E+ Board\u201C Button oben',
|
||||||
|
'Importiere Browser-Lesezeichen \u00FCber den \u201EImport\u201C Button im Header',
|
||||||
|
'Drag & Drop zum Umsortieren von Boards und Links',
|
||||||
|
'Blur-Modus f\u00FCr private Boards (\uD83D\uDD12 Icon)'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hero: '\uD83C\uDFA8',
|
||||||
|
title: '8 handgefertigte Themes',
|
||||||
|
text: 'Klicke auf den „Theme" Button im Header um dein Theme zu wählen. Jedes hat seinen eigenen Stil und Farbpalette.',
|
||||||
|
showThemes: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hero: '\u26A1',
|
||||||
|
title: 'Weitere Features',
|
||||||
|
features: [
|
||||||
|
'Suchleiste mit Google, DuckDuckGo oder Bing',
|
||||||
|
'Sticky Notes f\u00FCr schnelle Notizen',
|
||||||
|
'Funktioniert komplett offline \u2014 alles lokal gespeichert'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hero: '\uD83D\uDEE1\uFE0F',
|
||||||
|
title: 'Backups nicht vergessen!',
|
||||||
|
text: 'Deine Daten sind lokal im Browser gespeichert. Wenn du Browserdaten l\u00F6schst, gehen sie verloren! Sichere regelm\u00E4\u00DFig \u00FCber Settings \u2192 Data \u2192 Export. Wir erinnern dich alle 7 Tage daran.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hero: '\uD83D\uDE80',
|
||||||
|
title: 'Bereit!',
|
||||||
|
text: 'Klicke auf \u201E+ Board\u201C um dein erstes Board zu erstellen, oder nutze den \u201EImport\u201C Button im Header um deine Browser-Lesezeichen zu importieren.'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
/** Startet das Onboarding */
|
||||||
|
start() {
|
||||||
|
this.currentSlide = 0;
|
||||||
|
this._render();
|
||||||
|
this._bindKeyboard();
|
||||||
|
const overlay = document.getElementById('onboardingOverlay');
|
||||||
|
requestAnimationFrame(() => overlay.classList.add('active'));
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Schlie\u00DFt das Onboarding und speichert den Status */
|
||||||
|
async _finish() {
|
||||||
|
const overlay = document.getElementById('onboardingOverlay');
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
document.removeEventListener('keydown', this._keyHandler);
|
||||||
|
await Store.set('onboardingDone', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Rendert den aktuellen Slide */
|
||||||
|
_render() {
|
||||||
|
const modal = document.getElementById('onboardingModal');
|
||||||
|
modal.replaceChildren();
|
||||||
|
|
||||||
|
const slide = this.slides[this.currentSlide];
|
||||||
|
const isLast = this.currentSlide === this.slides.length - 1;
|
||||||
|
|
||||||
|
// Skip-Button (nicht auf letztem Slide)
|
||||||
|
if (!isLast) {
|
||||||
|
const skip = document.createElement('button');
|
||||||
|
skip.className = 'onboarding-skip';
|
||||||
|
skip.textContent = '\u00DCberspringen';
|
||||||
|
skip.addEventListener('click', () => this._finish());
|
||||||
|
modal.appendChild(skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slide-Content
|
||||||
|
const slideEl = document.createElement('div');
|
||||||
|
slideEl.className = 'onboarding-slide';
|
||||||
|
|
||||||
|
const hero = document.createElement('div');
|
||||||
|
hero.className = 'onboarding-hero';
|
||||||
|
hero.textContent = slide.hero;
|
||||||
|
slideEl.appendChild(hero);
|
||||||
|
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.className = 'onboarding-title';
|
||||||
|
title.textContent = slide.title;
|
||||||
|
slideEl.appendChild(title);
|
||||||
|
|
||||||
|
if (slide.text) {
|
||||||
|
const text = document.createElement('div');
|
||||||
|
text.className = 'onboarding-text';
|
||||||
|
text.textContent = slide.text;
|
||||||
|
slideEl.appendChild(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slide.features) {
|
||||||
|
const list = document.createElement('ul');
|
||||||
|
list.className = 'onboarding-feature-list';
|
||||||
|
slide.features.forEach(f => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = f;
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
slideEl.appendChild(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slide.showThemes) {
|
||||||
|
const grid = document.createElement('div');
|
||||||
|
grid.className = 'onboarding-theme-grid';
|
||||||
|
const themeNames = ['Nebula', 'Crescent', 'Event Horizon', 'Merchantman', 'Julia & Jin', 'SC Sunset', 'Hellion HUD', 'Hellion Energy'];
|
||||||
|
themeNames.forEach(name => {
|
||||||
|
const chip = document.createElement('div');
|
||||||
|
chip.className = 'onboarding-theme-chip';
|
||||||
|
chip.textContent = name;
|
||||||
|
grid.appendChild(chip);
|
||||||
|
});
|
||||||
|
slideEl.appendChild(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.appendChild(slideEl);
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
const footer = document.createElement('div');
|
||||||
|
footer.className = 'onboarding-footer';
|
||||||
|
|
||||||
|
// Dots
|
||||||
|
const dots = document.createElement('div');
|
||||||
|
dots.className = 'onboarding-dots';
|
||||||
|
for (let i = 0; i < this.slides.length; i++) {
|
||||||
|
const dot = document.createElement('div');
|
||||||
|
dot.className = 'onboarding-dot' + (i === this.currentSlide ? ' active' : '');
|
||||||
|
dots.appendChild(dot);
|
||||||
|
}
|
||||||
|
footer.appendChild(dots);
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
const nav = document.createElement('div');
|
||||||
|
nav.className = 'onboarding-nav';
|
||||||
|
|
||||||
|
if (this.currentSlide > 0) {
|
||||||
|
const backBtn = document.createElement('button');
|
||||||
|
backBtn.className = 'btn-secondary';
|
||||||
|
backBtn.textContent = 'Zur\u00FCck';
|
||||||
|
backBtn.addEventListener('click', () => {
|
||||||
|
this.currentSlide--;
|
||||||
|
this._render();
|
||||||
|
});
|
||||||
|
nav.appendChild(backBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
const startBtn = document.createElement('button');
|
||||||
|
startBtn.className = 'btn-primary';
|
||||||
|
startBtn.textContent = 'Los geht\u2019s!';
|
||||||
|
startBtn.addEventListener('click', () => this._finish());
|
||||||
|
nav.appendChild(startBtn);
|
||||||
|
} else {
|
||||||
|
const nextBtn = document.createElement('button');
|
||||||
|
nextBtn.className = 'btn-primary';
|
||||||
|
nextBtn.textContent = 'Weiter';
|
||||||
|
nextBtn.addEventListener('click', () => {
|
||||||
|
this.currentSlide++;
|
||||||
|
this._render();
|
||||||
|
});
|
||||||
|
nav.appendChild(nextBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.appendChild(nav);
|
||||||
|
modal.appendChild(footer);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Keyboard-Navigation */
|
||||||
|
_bindKeyboard() {
|
||||||
|
this._keyHandler = (e) => {
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.currentSlide < this.slides.length - 1) {
|
||||||
|
this.currentSlide++;
|
||||||
|
this._render();
|
||||||
|
} else {
|
||||||
|
this._finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowLeft' && this.currentSlide > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.currentSlide--;
|
||||||
|
this._render();
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
this._finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', this._keyHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# ⬡ Opera GX — New-Tab Workaround
|
||||||
|
|
||||||
|
Opera GX priorisiert die eigene Speed Dial Seite und ignoriert `chrome_url_overrides`
|
||||||
|
für entpackte Erweiterungen. Um das Hellion Dashboard trotzdem als New-Tab-Seite
|
||||||
|
zu etablieren, kommen zwei zusätzliche Skripte zum Einsatz.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warum zwei extra Skripte?
|
||||||
|
|
||||||
|
| Browser | New-Tab Override | Zusatzaufwand |
|
||||||
|
|---|---|---|
|
||||||
|
| Chrome / Edge / Brave / Vivaldi | `chrome_url_overrides` | Keiner |
|
||||||
|
| Firefox | `chrome_url_overrides` (MV2) | Eigenes Manifest |
|
||||||
|
| Opera / Opera GX | Blockiert durch Speed Dial | Workaround nötig |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was passiert hier?
|
||||||
|
|
||||||
|
### `background.js` — Tab-Management
|
||||||
|
|
||||||
|
Überwacht Tab-Aktivitäten im Hintergrund und greift ein bevor Opera seine Startseite lädt.
|
||||||
|
|
||||||
|
- Erkennt `opera://startpage/` und `chrome://startpage/`
|
||||||
|
- Leitet per `chrome.tabs.update` auf `newtab.html` um
|
||||||
|
- Prüft zusätzlich bei `onActivated` — auch im Hintergrund geladene Tabs werden sofort aktualisiert
|
||||||
|
|
||||||
|
### `redirect.js` — In-Page Redirect
|
||||||
|
|
||||||
|
Einige Opera-Systemprozesse sind so isoliert dass ein externer Eingriff nicht zuverlässig greift.
|
||||||
|
|
||||||
|
- Wird als Content Script in Opera-Startseiten-Bereiche injiziert
|
||||||
|
- Löst den Redirect bei `document_start` aus — minimale Verzögerung, kein Flackern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Datenschutz
|
||||||
|
|
||||||
|
Kein Tracking, keine Speicherung, keine externen Requests.
|
||||||
|
Ausschließlich Standard-Browser-APIs — `chrome.tabs` — um die Kontrolle über den New Tab zurückzugewinnen.
|
||||||
|
|
||||||
|
**100% lokal. 0% Analytics. Wie im gesamten Hellion NewTab Projekt.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Entwickelt von **[Hellion Online Media — Florian Wathling](https://hellion-media.de)** — JonKazama-Hellion
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
const dashboardUrl = chrome.runtime.getURL("newtab.html");
|
||||||
|
|
||||||
|
// Diese URLs wollen wir abfangen
|
||||||
|
const targetUrls = [
|
||||||
|
"chrome://startpage/",
|
||||||
|
"opera://startpage/",
|
||||||
|
"chrome://startpageshared/",
|
||||||
|
"about:blank"
|
||||||
|
];
|
||||||
|
|
||||||
|
function forceRedirect(tabId, url) {
|
||||||
|
if (url && targetUrls.some(target => url.startsWith(target))) {
|
||||||
|
chrome.tabs.update(tabId, { url: dashboardUrl });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Check beim Erstellen
|
||||||
|
chrome.tabs.onCreated.addListener((tab) => {
|
||||||
|
forceRedirect(tab.id, tab.pendingUrl || tab.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Check beim Aktualisieren (Wichtig für Opera GX!)
|
||||||
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||||
|
if (changeInfo.status === "loading" || changeInfo.url) {
|
||||||
|
forceRedirect(tabId, tab.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Intervall-Check: Falls Opera den Event verschluckt
|
||||||
|
chrome.tabs.onActivated.addListener((activeInfo) => {
|
||||||
|
chrome.tabs.get(activeInfo.tabId, (tab) => {
|
||||||
|
if (tab) forceRedirect(tab.id, tab.url);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
(function() {
|
||||||
|
const dashboardUrl = chrome.runtime.getURL("newtab.html");
|
||||||
|
if (window.location.href !== dashboardUrl) {
|
||||||
|
window.location.href = dashboardUrl;
|
||||||
|
}
|
||||||
|
})();
|
||||||
+70
-12
@@ -1,8 +1,9 @@
|
|||||||
/* =============================================
|
/* =============================================
|
||||||
HELLION NEWTAB — settings.js
|
HELLION NEWTAB — settings.js
|
||||||
Settings Panel: Toggles, Hintergrund, Theme-Picker
|
Settings Panel, Theme-Modal, Accordion, Toggles
|
||||||
============================================= */
|
============================================= */
|
||||||
|
|
||||||
|
// ---- SETTINGS PANEL ----
|
||||||
function openSettings() {
|
function openSettings() {
|
||||||
document.getElementById('settingsPanel').classList.add('open');
|
document.getElementById('settingsPanel').classList.add('open');
|
||||||
document.getElementById('settingsOverlay').classList.add('active');
|
document.getElementById('settingsOverlay').classList.add('active');
|
||||||
@@ -12,6 +13,43 @@ function closeSettings() {
|
|||||||
document.getElementById('settingsOverlay').classList.remove('active');
|
document.getElementById('settingsOverlay').classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- THEME MODAL ----
|
||||||
|
function openThemeModal() {
|
||||||
|
const overlay = document.getElementById('themeOverlay');
|
||||||
|
overlay.classList.add('active');
|
||||||
|
}
|
||||||
|
function closeThemeModal() {
|
||||||
|
const overlay = document.getElementById('themeOverlay');
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ACCORDION ----
|
||||||
|
function initAccordion() {
|
||||||
|
const defaultOpen = new Set(['appearance', 'behavior', 'data', 'help']);
|
||||||
|
const sections = document.querySelectorAll('.settings-section[data-section]');
|
||||||
|
|
||||||
|
sections.forEach(section => {
|
||||||
|
const name = section.dataset.section;
|
||||||
|
const title = section.querySelector('.settings-section-title');
|
||||||
|
|
||||||
|
if (defaultOpen.has(name)) {
|
||||||
|
section.classList.add('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
title.addEventListener('click', () => {
|
||||||
|
section.classList.toggle('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
title.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
section.classList.toggle('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- APPLY SETTINGS ----
|
||||||
function applySettings() {
|
function applySettings() {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
body.classList.toggle('compact', settings.compact);
|
body.classList.toggle('compact', settings.compact);
|
||||||
@@ -24,7 +62,7 @@ function applySettings() {
|
|||||||
document.getElementById('settingShowDesc').checked = settings.showDesc;
|
document.getElementById('settingShowDesc').checked = settings.showDesc;
|
||||||
document.getElementById('settingHideExtra').checked = settings.hideExtra;
|
document.getElementById('settingHideExtra').checked = settings.hideExtra;
|
||||||
document.getElementById('settingVisibleCount').value = String(settings.visibleCount);
|
document.getElementById('settingVisibleCount').value = String(settings.visibleCount);
|
||||||
document.getElementById('visibleCountRow').style.opacity = settings.hideExtra ? '1' : '0.4';
|
document.getElementById('visibleCountRow').classList.toggle('dim', !settings.hideExtra);
|
||||||
|
|
||||||
// showSearch: undefined (alter Save) → true
|
// showSearch: undefined (alter Save) → true
|
||||||
if (settings.showSearch === undefined) settings.showSearch = true;
|
if (settings.showSearch === undefined) settings.showSearch = true;
|
||||||
@@ -40,13 +78,21 @@ function applySettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- BIND EVENTS ----
|
||||||
function bindSettingsEvents() {
|
function bindSettingsEvents() {
|
||||||
// Panel
|
// Settings Panel
|
||||||
document.getElementById('settingsOverlay').addEventListener('click', closeSettings);
|
document.getElementById('settingsOverlay').addEventListener('click', closeSettings);
|
||||||
document.getElementById('btnCloseSettings').addEventListener('click', closeSettings);
|
document.getElementById('btnCloseSettings').addEventListener('click', closeSettings);
|
||||||
document.getElementById('btnSettings').addEventListener('click', openSettings);
|
document.getElementById('btnSettings').addEventListener('click', openSettings);
|
||||||
|
|
||||||
// Theme-Picker
|
// Theme Modal
|
||||||
|
document.getElementById('btnTheme').addEventListener('click', openThemeModal);
|
||||||
|
document.getElementById('btnCloseTheme').addEventListener('click', closeThemeModal);
|
||||||
|
document.getElementById('themeOverlay').addEventListener('click', e => {
|
||||||
|
if (e.target === document.getElementById('themeOverlay')) closeThemeModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Theme-Picker (Cards im Theme-Modal)
|
||||||
document.querySelectorAll('.theme-card').forEach(card => {
|
document.querySelectorAll('.theme-card').forEach(card => {
|
||||||
card.addEventListener('click', async () => {
|
card.addEventListener('click', async () => {
|
||||||
const name = card.dataset.value;
|
const name = card.dataset.value;
|
||||||
@@ -59,6 +105,9 @@ function bindSettingsEvents() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Accordion initialisieren
|
||||||
|
initAccordion();
|
||||||
|
|
||||||
// Toggles
|
// Toggles
|
||||||
const toggleMap = {
|
const toggleMap = {
|
||||||
settingCompact: v => { settings.compact = v; document.body.classList.toggle('compact', v); },
|
settingCompact: v => { settings.compact = v; document.body.classList.toggle('compact', v); },
|
||||||
@@ -67,7 +116,7 @@ function bindSettingsEvents() {
|
|||||||
settingShowDesc: v => { settings.showDesc = v; document.body.classList.toggle('show-desc', v); },
|
settingShowDesc: v => { settings.showDesc = v; document.body.classList.toggle('show-desc', v); },
|
||||||
settingHideExtra: v => {
|
settingHideExtra: v => {
|
||||||
settings.hideExtra = v;
|
settings.hideExtra = v;
|
||||||
document.getElementById('visibleCountRow').style.opacity = v ? '1' : '0.4';
|
document.getElementById('visibleCountRow').classList.toggle('dim', !v);
|
||||||
renderBoards();
|
renderBoards();
|
||||||
},
|
},
|
||||||
settingShowSearch: v => {
|
settingShowSearch: v => {
|
||||||
@@ -92,20 +141,19 @@ function bindSettingsEvents() {
|
|||||||
renderBoards();
|
renderBoards();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Background URL
|
// Background URL (im Theme-Modal)
|
||||||
document.getElementById('btnChangeBg').addEventListener('click', () => {
|
document.getElementById('btnChangeBg').addEventListener('click', () => {
|
||||||
const row = document.getElementById('bgInputRow');
|
document.getElementById('bgInputRow').classList.toggle('hidden');
|
||||||
row.style.display = row.style.display === 'none' ? 'flex' : 'none';
|
|
||||||
});
|
});
|
||||||
document.getElementById('btnApplyBg').addEventListener('click', async () => {
|
document.getElementById('btnApplyBg').addEventListener('click', async () => {
|
||||||
const url = document.getElementById('bgUrlInput').value.trim();
|
const url = document.getElementById('bgUrlInput').value.trim();
|
||||||
settings.bgUrl = url;
|
settings.bgUrl = url;
|
||||||
document.getElementById('bgLayer').style.backgroundImage = url ? `url('${url}')` : '';
|
document.getElementById('bgLayer').style.backgroundImage = url ? `url('${url}')` : '';
|
||||||
await saveSettings();
|
await saveSettings();
|
||||||
document.getElementById('bgInputRow').style.display = 'none';
|
document.getElementById('bgInputRow').classList.add('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Background File Upload
|
// Background File Upload (im Theme-Modal)
|
||||||
document.getElementById('btnBgFile').addEventListener('click', () => {
|
document.getElementById('btnBgFile').addEventListener('click', () => {
|
||||||
document.getElementById('bgFileInput').click();
|
document.getElementById('bgFileInput').click();
|
||||||
});
|
});
|
||||||
@@ -119,14 +167,24 @@ function bindSettingsEvents() {
|
|||||||
await saveSettings();
|
await saveSettings();
|
||||||
};
|
};
|
||||||
reader.onerror = () => {
|
reader.onerror = () => {
|
||||||
alert('Fehler beim Lesen der Datei. Bitte eine andere Datei wählen.');
|
HellionDialog.alert('Fehler beim Lesen der Datei. Bitte eine andere Datei wählen.', { type: 'danger', title: 'Dateifehler' });
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Onboarding wiederholen
|
||||||
|
document.getElementById('btnRestartOnboarding').addEventListener('click', () => {
|
||||||
|
closeSettings();
|
||||||
|
Onboarding.start();
|
||||||
|
});
|
||||||
|
|
||||||
// Reset All
|
// Reset All
|
||||||
document.getElementById('btnResetAll').addEventListener('click', async () => {
|
document.getElementById('btnResetAll').addEventListener('click', async () => {
|
||||||
if (!confirm('Wirklich alle Boards und Einstellungen löschen? Nicht rückgängig machbar.')) return;
|
const ok = await HellionDialog.confirm(
|
||||||
|
'Wirklich alle Boards und Einstellungen löschen? Das kann nicht rückgängig gemacht werden.',
|
||||||
|
{ type: 'danger', title: 'Alles zurücksetzen', confirmText: 'Alles löschen' }
|
||||||
|
);
|
||||||
|
if (!ok) return;
|
||||||
boards = [];
|
boards = [];
|
||||||
settings = { compact: false, shortenTitles: false, newTab: true, showDesc: false,
|
settings = { compact: false, shortenTitles: false, newTab: true, showDesc: false,
|
||||||
hideExtra: false, visibleCount: 10, bgUrl: '', theme: 'nebula',
|
hideExtra: false, visibleCount: 10, bgUrl: '', theme: 'nebula',
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ const Store = {
|
|||||||
chrome.storage.local.set({ [key]: value }, () => {
|
chrome.storage.local.set({ [key]: value }, () => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
console.error('Storage-Fehler:', chrome.runtime.lastError.message);
|
console.error('Storage-Fehler:', chrome.runtime.lastError.message);
|
||||||
alert('Speicher voll! Bitte lösche alte Boards oder das Hintergrundbild, um Platz zu schaffen.');
|
HellionDialog.alert('Speicher voll! Bitte lösche alte Boards oder das Hintergrundbild, um Platz zu schaffen.', { type: 'danger', title: 'Speicher voll' });
|
||||||
reject(new Error(chrome.runtime.lastError.message));
|
reject(new Error(chrome.runtime.lastError.message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ const Store = {
|
|||||||
resolve();
|
resolve();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Storage-Fehler:', e.message);
|
console.error('Storage-Fehler:', e.message);
|
||||||
alert('Speicher voll! Bitte lösche alte Boards oder das Hintergrundbild, um Platz zu schaffen.');
|
HellionDialog.alert('Speicher voll! Bitte lösche alte Boards oder das Hintergrundbild, um Platz zu schaffen.', { type: 'danger', title: 'Speicher voll' });
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user