Browse Source

implement themes

bug-fixes
master
Silberengel 1 month ago
parent
commit
d654aa426e
  1. 2
      README.md
  2. 4
      package-lock.json
  3. 2
      package.json
  4. 6
      public/healthz.json
  5. 1241
      src/app.css
  6. 14
      src/app.html
  7. 1
      src/lib/components/content/MarkdownRenderer.svelte
  8. 2
      src/lib/modules/reactions/FeedReactionButtons.svelte
  9. 2
      src/lib/utils/url-cleaner.ts
  10. 2
      src/routes/+layout.svelte
  11. 6
      src/routes/about/+page.svelte
  12. 92
      src/routes/settings/+page.svelte
  13. 65
      tailwind.config.js

2
README.md

@ -50,7 +50,7 @@ docker run -p 9876:9876 aitherboard @@ -50,7 +50,7 @@ docker run -p 9876:9876 aitherboard
## Core Features
- **Threads & Discussions** - Create and participate in threaded conversations
- **Feed** - Twitter-like feed posts (kind 1) with real-time updates
- **Feed** - socialmedia-like feed posts (kind 1) with real-time updates
- **Comments** - Flat-threaded comments on threads and posts
- **Reactions** - Upvote, downvote, and react to content with custom GIFs and emojis
- **Profiles** - View and manage user profiles with payment addresses

4
package-lock.json generated

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
{
"name": "aitherboard",
"version": "0.2.0",
"version": "0.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "aitherboard",
"version": "0.2.0",
"version": "0.2.1",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.20.0",

2
package.json

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{
"name": "aitherboard",
"version": "0.2.0",
"version": "0.2.1",
"type": "module",
"author": "silberengel@gitcitadel.com",
"description": "A decentralized messageboard built on the Nostr protocol.",

6
public/healthz.json

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
{
"status": "ok",
"service": "aitherboard",
"version": "0.2.0",
"buildTime": "2026-02-10T11:19:46.024Z",
"version": "0.2.1",
"buildTime": "2026-02-10T22:25:53.381Z",
"gitCommit": "unknown",
"timestamp": 1770722386024
"timestamp": 1770762353381
}

1241
src/app.css

File diff suppressed because it is too large Load Diff

14
src/app.html

@ -27,13 +27,13 @@ @@ -27,13 +27,13 @@
<meta property="og:site_name" content="aitherboard" />
<meta property="og:locale" content="en_US" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://aitherboard.imwald.eu/" />
<meta name="twitter:title" content="aitherboard - Decentralized Messageboard on Nostr" />
<meta name="twitter:description" content="A decentralized messageboard built on the Nostr protocol. Create threads, comment, and react in a censorship-resistant environment." />
<meta name="twitter:image" content="%sveltekit.assets%/og-image.png" />
<meta name="twitter:image:alt" content="aitherboard - Decentralized Messageboard on Nostr" />
<!-- socialmedia -->
<meta name="socialmedia:card" content="summary_large_image" />
<meta name="socialmedia:url" content="https://aitherboard.imwald.eu/" />
<meta name="socialmedia:title" content="aitherboard - Decentralized Messageboard on Nostr" />
<meta name="socialmedia:description" content="A decentralized messageboard built on the Nostr protocol. Create threads, comment, and react in a censorship-resistant environment." />
<meta name="socialmedia:image" content="%sveltekit.assets%/og-image.png" />
<meta name="socialmedia:image:alt" content="aitherboard - Decentralized Messageboard on Nostr" />
<!-- Additional Meta Tags -->
<meta name="theme-color" content="#f1f5f9" />

1
src/lib/components/content/MarkdownRenderer.svelte

@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
import { getHighlightsForEvent, findHighlightMatches, type Highlight } from '../../services/nostr/highlight-service.js';
import { mountComponent } from './mount-component-action.js';
import type { NostrEvent } from '../../types/nostr.js';
// @ts-ignore - highlight.js default export works at runtime
import hljs from 'highlight.js';
// Use VS Code theme for IDE-like appearance
import 'highlight.js/styles/vs2015.css';

2
src/lib/modules/reactions/FeedReactionButtons.svelte

@ -646,6 +646,7 @@ @@ -646,6 +646,7 @@
<span
class="reaction-display {userReaction === content ? 'active' : ''}"
title={content === '+' ? 'Liked' : `Reacted with ${content}`}
role="presentation"
onmouseenter={(e) => {
hoveredReaction = content;
hoveredReactionElement = e.currentTarget;
@ -708,6 +709,7 @@ @@ -708,6 +709,7 @@
<div
class="reaction-tooltip"
style="top: {tooltipPosition.top}px; left: {tooltipPosition.left}px;"
role="tooltip"
onmouseenter={() => {
// Keep tooltip visible when hovering over it
}}

2
src/lib/utils/url-cleaner.ts

@ -27,7 +27,7 @@ const TRACKING_PARAMS = new Set([ @@ -27,7 +27,7 @@ const TRACKING_PARAMS = new Set([
'fbclid', // Facebook
'gclid', // Google Ads
'msclkid', // Microsoft
'twclid', // Twitter
'twclid', // socialmedia
'li_fat_id', // LinkedIn
'igshid', // Instagram

2
src/routes/+layout.svelte

@ -31,10 +31,12 @@ @@ -31,10 +31,12 @@
const textSize = localStorage.getItem('textSize') || 'medium';
const lineSpacing = localStorage.getItem('lineSpacing') || 'normal';
const contentWidth = localStorage.getItem('contentWidth') || 'medium';
const designTheme = localStorage.getItem('designTheme') || 'fog';
document.documentElement.setAttribute('data-text-size', textSize);
document.documentElement.setAttribute('data-line-spacing', lineSpacing);
document.documentElement.setAttribute('data-content-width', contentWidth);
document.documentElement.setAttribute('data-design-theme', designTheme);
// Try to restore session synchronously if possible
// This ensures session is restored before any components render

6
src/routes/about/+page.svelte

@ -21,6 +21,10 @@ @@ -21,6 +21,10 @@
// Changelog for current version
const changelog: Record<string, string[]> = {
'0.2.1': [
'Themes added: Fog, Forum, Socialmedia, and Terminal',
'Find page added: search for content by hashtags, users, and content, full-text search'
],
'0.2.0': [
'Version management and update system',
'About page with product information',
@ -85,7 +89,7 @@ @@ -85,7 +89,7 @@
<div class="section-content">
<ul class="features-list">
<li><strong>Threads & Discussions</strong> - Create and participate in threaded conversations</li>
<li><strong>Feed</strong> - Twitter-like feed posts (kind 1) with real-time updates</li>
<li><strong>Feed</strong> - socialmedia-like feed posts (kind 1) with real-time updates</li>
<li><strong>Comments</strong> - Flat-threaded comments on threads and posts</li>
<li><strong>Reactions</strong> - Upvote, downvote, and react to content. Use custom GIFs and emojis.</li>
<li><strong>Profiles</strong> - View and manage user profiles with payment addresses</li>

92
src/routes/settings/+page.svelte

@ -10,11 +10,13 @@ @@ -10,11 +10,13 @@
type TextSize = 'small' | 'medium' | 'large';
type LineSpacing = 'tight' | 'normal' | 'loose';
type ContentWidth = 'narrow' | 'medium' | 'wide';
type DesignTheme = 'fog' | 'forum' | 'terminal' | 'socialmedia';
let textSize = $state<TextSize>('medium');
let lineSpacing = $state<LineSpacing>('normal');
let contentWidth = $state<ContentWidth>('medium');
let isDark = $state(false);
let designTheme = $state<DesignTheme>('fog');
let expiringEvents = $state(false);
let includeClientTag = $state(true);
@ -36,11 +38,17 @@ @@ -36,11 +38,17 @@
const storedLineSpacing = localStorage.getItem('lineSpacing') as LineSpacing | null;
const storedContentWidth = localStorage.getItem('contentWidth') as ContentWidth | null;
const storedTheme = localStorage.getItem('theme');
const storedDesignTheme = localStorage.getItem('designTheme') as DesignTheme | null;
textSize = storedTextSize || 'medium';
lineSpacing = storedLineSpacing || 'normal';
contentWidth = storedContentWidth || 'medium';
designTheme = storedDesignTheme || 'fog';
// Terminal theme always uses dark mode
if (designTheme === 'terminal') {
isDark = true;
} else {
// Read theme from localStorage first, then fallback to DOM/system preference
if (storedTheme) {
isDark = storedTheme === 'dark';
@ -48,6 +56,7 @@ @@ -48,6 +56,7 @@
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
isDark = document.documentElement.classList.contains('dark') || prefersDark;
}
}
// Apply preferences immediately to ensure consistent layout
applyPreferences();
@ -120,6 +129,15 @@ @@ -120,6 +129,15 @@
document.documentElement.setAttribute('data-content-width', contentWidth);
localStorage.setItem('contentWidth', contentWidth);
// Apply design theme
document.documentElement.setAttribute('data-design-theme', designTheme);
localStorage.setItem('designTheme', designTheme);
// Terminal theme always uses dark mode
if (designTheme === 'terminal') {
isDark = true;
}
// Apply theme
if (isDark) {
document.documentElement.classList.add('dark');
@ -145,11 +163,24 @@ @@ -145,11 +163,24 @@
applyPreferences();
}
function handleThemeToggle() {
function handleModeToggle() {
// Terminal theme always uses dark mode - don't allow toggling
if (designTheme === 'terminal') {
return;
}
isDark = !isDark;
applyPreferences();
}
function handleDesignThemeChange(theme: DesignTheme) {
designTheme = theme;
// Terminal theme always uses dark mode
if (theme === 'terminal') {
isDark = true;
}
applyPreferences();
}
function handleExpiringEventsToggle() {
expiringEvents = !expiringEvents;
localStorage.setItem('aitherboard_expiringEvents', expiringEvents ? 'true' : 'false');
@ -264,22 +295,73 @@ @@ -264,22 +295,73 @@
</p>
</div>
<!-- Theme Toggle -->
<!-- Mode Toggle -->
<div class="preference-section bg-fog-post dark:bg-fog-dark-post border border-fog-border dark:border-fog-dark-border p-4 rounded">
<div class="preference-label mb-3">
<span class="font-semibold text-fog-text dark:text-fog-dark-text">Theme</span>
<span class="font-semibold text-fog-text dark:text-fog-dark-text">Mode</span>
</div>
<div class="preference-controls">
<button
onclick={handleThemeToggle}
onclick={handleModeToggle}
class="toggle-button"
class:active={isDark}
aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
disabled={designTheme === 'terminal'}
aria-label={designTheme === 'terminal' ? 'Terminal theme is always dark' : (isDark ? 'Switch to light mode' : 'Switch to dark mode')}
title={designTheme === 'terminal' ? 'Terminal theme is always dark' : undefined}
>
<Icon name={isDark ? 'sun' : 'moon'} size={16} />
<span>{isDark ? 'Light' : 'Dark'}</span>
</button>
{#if designTheme === 'terminal'}
<span class="text-fog-text-light dark:text-fog-dark-text-light ml-2" style="font-size: 0.875em;">
(Terminal theme is always dark)
</span>
{/if}
</div>
</div>
<!-- Design Theme -->
<div class="preference-section bg-fog-post dark:bg-fog-dark-post border border-fog-border dark:border-fog-dark-border p-4 rounded">
<div class="preference-label mb-3">
<span class="font-semibold text-fog-text dark:text-fog-dark-text">Design Theme</span>
</div>
<div class="preference-controls">
<button
onclick={() => handleDesignThemeChange('fog')}
class="option-button"
class:active={designTheme === 'fog'}
aria-label="Fog theme"
>
Fog
</button>
<button
onclick={() => handleDesignThemeChange('forum')}
class="option-button"
class:active={designTheme === 'forum'}
aria-label="forum theme"
>
Forum
</button>
<button
onclick={() => handleDesignThemeChange('terminal')}
class="option-button"
class:active={designTheme === 'terminal'}
aria-label="Terminal theme"
>
Terminal
</button>
<button
onclick={() => handleDesignThemeChange('socialmedia')}
class="option-button"
class:active={designTheme === 'socialmedia'}
aria-label="socialmedia theme"
>
Social Media
</button>
</div>
<p class="text-fog-text-light dark:text-fog-dark-text-light mt-2" style="font-size: 0.875em;">
Choose a design theme to change the visual style of the interface.
</p>
</div>
<!-- Text Size -->

65
tailwind.config.js

@ -5,7 +5,7 @@ export default { @@ -5,7 +5,7 @@ export default {
theme: {
extend: {
colors: {
// Fog aesthetic color palette (light mode)
// Fog aesthetic color palette (light mode) - default theme
fog: {
bg: '#f1f5f9', // Misty white-gray background
surface: '#f8fafc', // Light surface
@ -26,6 +26,69 @@ export default { @@ -26,6 +26,69 @@ export default {
'text-light': '#a8b8d0', // Lighter text (WCAG AA compliant: 8.5:1 on bg, 4.8:1 on post)
accent: '#64748b', // Soft accent (WCAG AA compliant: 5.8:1 on bg)
highlight: '#475569' // Subtle highlight (WCAG AA compliant: 4.6:1 text on highlight)
},
// forum-style theme (orange/white, vibrant)
forum: {
bg: '#dae0e6', // Light gray background
surface: '#ffffff', // White surface
post: '#ffffff', // White posts
border: '#e4e4e4', // Light gray border
text: '#1c1c1c', // Dark text
'text-light': '#7c7c7c', // Gray text
accent: '#ff4500', // forum orange accent
highlight: '#f6f7f8' // Light gray highlight
},
'forum-dark': {
bg: '#030303', // Very dark background
surface: '#1a1a1b', // Dark surface
post: '#1a1a1b', // Dark post background
border: '#343536', // Dark border
text: '#d7dadc', // Light text
'text-light': '#818384', // Gray text
accent: '#ff4500', // forum orange accent
highlight: '#272729' // Dark highlight
},
// Terminal theme (old-school green on black)
terminal: {
bg: '#000000', // Pure black background
surface: '#0a0a0a', // Very dark surface
post: '#0d0d0d', // Dark post background
border: '#00ff00', // Green border
text: '#00ff00', // Green text
'text-light': '#00cc00', // Darker green text
accent: '#00ff00', // Green accent
highlight: '#001a00' // Dark green highlight
},
'terminal-dark': {
bg: '#000000', // Pure black background
surface: '#0a0a0a', // Very dark surface
post: '#0d0d0d', // Dark post background
border: '#00ff00', // Green border
text: '#00ff00', // Green text
'text-light': '#00cc00', // Darker green text
accent: '#00ff00', // Green accent
highlight: '#001a00' // Dark green highlight
},
// socialmedia-style theme (blue/white, clean)
socialmedia: {
bg: '#ffffff', // White background
surface: '#ffffff', // White surface
post: '#ffffff', // White posts
border: '#eff3f4', // Light gray border
text: '#0f1419', // Dark text
'text-light': '#536471', // Gray text
accent: '#1d9bf0', // socialmedia blue accent
highlight: '#f7f9f9' // Light gray highlight
},
'socialmedia-dark': {
bg: '#000000', // Black background
surface: '#000000', // Black surface
post: '#16181c', // Dark post background
border: '#2f3336', // Dark border
text: '#e7e9ea', // Light text
'text-light': '#71767a', // Gray text
accent: '#1d9bf0', // socialmedia blue accent
highlight: '#16181c' // Dark highlight
}
},
fontFamily: {

Loading…
Cancel
Save