Files
kgpz_web/CLAUDE.md
2025-09-27 18:32:03 +02:00

47 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

KGPZ Web is a Go-based web application for serving historical newspaper data from the KGPZ (Königsberger Gelehrte und Politische Zeitung) digital archive. The application combines server-rendered HTML with HTMX for progressive enhancement, providing a modern web interface for browsing historical content.

Architecture

The application follows a modular Go architecture:

  • Main Application: kgpz_web.go - Entry point and application lifecycle management
  • App Core: app/kgpz.go - Core business logic and data processing
  • Controllers: Route handlers for different content types (issues, agents, places, categories, search, quickfilters)
  • View Models: Data structures for template rendering with pre-processed business logic (viewmodels/)
  • XML Models: Data structures for parsing source XML files (xmlmodels/)
  • Providers: External service integrations (Git, GND, Geonames, XML parsing, search)
  • Templating: Custom template engine with Go template integration and helper functions
  • Views: Frontend assets and templates in views/ directory

Key Components

  1. Data Sources: XML files from Git repository containing historical newspaper metadata
  2. Search: Full-text search powered by Bleve search engine
  3. External Integrations: GND (Gemeinsame Normdatei) for person metadata, Geonames for place data
  4. Template System: Custom engine supporting layouts and partials with embedded filesystem and helper functions

Development Commands

Go Backend

# Run the application in development mode
go run kgpz_web.go

# Build the application
go build -o kgpz_web kgpz_web.go

# Run tests
go test ./helpers/xsdtime/

# Format code
go fmt ./...

# Check for issues
go vet ./...

Note: The project maintainer handles all Go compilation, testing, and error reporting. Claude Code should not run Go build commands or tests - any Go-related errors will be reported directly by the maintainer.

Frontend Assets (from views/ directory)

cd views/

# Development server with hot reloading
npm run dev

# Build production assets
npm run build

# Build CSS with Tailwind
npm run tailwind

# Build CSS with PostCSS
npm run css

# Preview built assets
npm run preview

Configuration

The application uses JSON configuration files:

  • config.dev.json - Development configuration (if exists)
  • config.json - Default configuration (fallback)

Key configuration options:

  • git_url: Source repository URL for data
  • git_branch: Branch to use for data
  • webhook_endpoint: GitHub webhook endpoint for auto-updates
  • debug: Enable debug mode and logging
  • watch: Enable file watching for template hot-reloading

Data Flow

  1. Startup: Application clones/pulls Git repository with XML data
  2. Parsing: XML files parsed into structured models (agents, places, works, issues)
  3. Enrichment: External APIs (GND, Geonames) enrich metadata
  4. Indexing: Full-text search index built using Bleve
  5. Serving: HTTP server serves templated content with HTMX interactions

Key Dependencies

  • Web Framework: Fiber (high-performance HTTP framework)
  • Search: Bleve (full-text search engine)
  • Git Operations: go-git (Git repository operations)
  • Frontend: HTMX + Tailwind CSS for progressive enhancement
  • Build Tools: Vite for asset bundling, PostCSS for CSS processing

Template Structure

Templates are embedded in the binary:

  • Layouts: views/layouts/ - Base page structures
  • Routes: views/routes/ - Page-specific templates
  • Assets: views/assets/ - Compiled CSS and static files

The template system supports nested layouts and automatic reloading in development mode when watch: true is enabled.

Views Directory Structure

The views/ directory contains all frontend templates, assets, and build configuration:

Directory Layout

views/
├── layouts/                 # Base templates and layouts
│   ├── components/          # Shared layout components (_header, _footer, _menu)
│   └── default/            # Default layout (root.gohtml)
├── routes/                 # Page-specific templates
│   ├── akteure/           # Agents/People pages (body.gohtml, head.gohtml)
│   ├── autoren/           # Authors-only pages (body.gohtml, head.gohtml)
│   ├── ausgabe/           # Issue pages with components
│   │   └── components/    # Issue-specific components (_inhaltsverzeichnis, _bilder, etc.)
│   ├── components/        # Shared route components
│   │   ├── _akteur.gohtml          # Main agent component (uses sub-components)
│   │   ├── _akteur_header.gohtml   # Agent name, dates, professions, links
│   │   ├── _akteur_werke.gohtml    # Works section with categorized pieces
│   │   ├── _akteur_beitraege.gohtml # Contributions/pieces with grouping
│   │   └── _unified_piece_entry.gohtml # Universal piece display component
│   ├── datenschutz/       # Privacy policy
│   ├── edition/           # Edition pages
│   ├── filter/            # Quickfilter system
│   ├── kategorie/         # Category pages
│   ├── kontakt/           # Contact pages
│   ├── ort/               # Places pages with overview/detail split
│   │   ├── overview/      # Places grid view (body.gohtml, head.gohtml)
│   │   ├── detail/        # Individual place view (body.gohtml, head.gohtml)
│   │   └── components/    # Place-specific components (_place_card, _place_header, _place_pieces, _back_navigation)
│   ├── piece/             # Multi-issue piece pages
│   │   └── components/    # Piece-specific components (_piece_inhaltsverzeichnis, _piece_sequential_layout)
│   ├── search/            # Search pages
│   └── zitation/          # Citation pages
├── assets/                # Compiled output assets
│   ├── css/              # Compiled CSS files
│   ├── js/               # JavaScript libraries and compiled scripts
│   ├── fonts/            # Font files
│   ├── logo/             # Logo and favicon files
│   └── xslt/             # XSLT transformation files
├── public/               # Static public assets
├── transform/            # Source files for build process
│   ├── main.js          # Main JavaScript entry point
│   └── site.css         # Source CSS with Tailwind directives
└── node_modules/         # NPM dependencies

Template System

Layout Templates (layouts/):

  • default/root.gohtml: Base HTML structure with head, HTMX, Alpine.js setup
  • components/_header.gohtml: Site header with navigation
  • components/_footer.gohtml: Site footer
  • components/_menu.gohtml: Main navigation menu

Route Templates (routes/): Each route has dedicated head.gohtml and body.gohtml files following Go template conventions:

  • Pages use German naming: akteure (agents), ausgabe (issues), ort (places), etc.
  • Component partials prefixed with _ (e.g., _akteur.gohtml, _inhaltsverzeichnis.gohtml)
  • HTMX-powered interactions for dynamic content loading

Template Features:

  • Go template syntax with custom functions from templating/engine.go
  • Block template inheritance system
  • HTMX integration for progressive enhancement
  • Conditional development/production asset loading
  • Template helper functions for UI components (PageIcon, BeilagePageIcon)
  • Pre-processed view models to minimize template logic

Frontend Assets

JavaScript Stack:

  • HTMX: Core interactivity and AJAX requests
  • Web Components: Custom elements for self-contained functionality (replaced Alpine.js)
  • Modular Architecture: ES6 modules with focused responsibilities
  • Event-Driven Architecture: Custom events for inter-component communication
  • Build Tool: Vite for module bundling and development server

CSS Stack:

  • Tailwind CSS v4: Utility-first CSS framework
  • PostCSS: CSS processing pipeline
  • RemixIcon: Icon font library
  • Custom Fonts: Typography setup in assets/css/fonts.css

JavaScript Module Structure:

  • main.js: Entry point, HTMX event handling, citation link management, page backdrop styling
  • akteure.js: AkteureScrollspy web component for agents/authors navigation
  • issue.js: Newspaper layout, page navigation, modal functions, citation generation
  • single-page-viewer.js: SinglePageViewer web component for image modal display
  • scroll-to-top.js: ScrollToTopButton web component for floating scroll button
  • places.js: PlacesFilter web component for real-time place search filtering

Build Process:

  • Source: transform/main.js and transform/site.css
  • Output: Compiled to assets/scripts.js and assets/style.css
  • Vite Config: Production build targeting ES modules
  • PostCSS Config: Tailwind CSS processing

Asset Loading Strategy

The root template conditionally loads assets based on environment:

  • Development: Uses dev favicon, enables hot reloading
  • Production: Optimized assets, production favicon
  • Module imports: ES6 modules with setup() function from compiled scripts
  • Deferred loading: HTMX and Alpine.js loaded with defer attribute

Template Architecture & Best Practices

View Model Philosophy

The application follows a logic-in-Go, presentation-in-templates approach:

  • View Models (viewmodels/issue_view.go): Pre-process all business logic, calculations, and data transformations
  • Templates: Focus purely on presentation using pre-calculated data
  • Helper Functions (templating/engine.go): Reusable UI components and formatting

Key View Model Features

  • Pre-calculated metadata: Page icons, grid layouts, visibility flags
  • Grouped data structures: Complex relationships resolved in Go
  • Template helpers: PageIcon(), BeilagePageIcon() for consistent UI components

Template Organization

Ausgabe (Issue) Templates:

  • body.gohtml: Main layout structure with conditional rendering
  • components/_inhaltsverzeichnis.gohtml: Table of contents with pre-processed page data
  • components/_newspaper_layout.gohtml: Newspaper page grid with absolute positioning
  • components/_bilder.gohtml: Simple image gallery fallback
  • Interactive highlighting system with intersection observer and scroll detection

JavaScript Integration

  • Progressive Enhancement: HTMX + Alpine.js for interactivity
  • Real-time Highlighting: Intersection Observer API with scroll fallback (issue view)
  • Scrollspy Navigation: Multi-item highlighting system for agents/authors pages
  • Page Navigation: Smooth scrolling with visibility detection
  • HTMX Integration: Automatic cleanup and re-initialization on page swaps
  • Responsive Design: Mobile-optimized with proper touch interactions

Development Workflow

  1. Backend changes: Modify Go files, restart server
  2. Template changes: Edit templates in views/, automatic reload if watching enabled
  3. CSS changes: Run npm run css or npm run tailwind in views directory
  4. JavaScript changes: Edit transform/main.js, run npm run build
  5. Full rebuild: go build for backend, npm run build for frontend assets

Adding New Template Logic

  1. First: Add business logic to view models in Go
  2. Second: Create reusable template helper functions if needed
  3. Last: Use pre-processed data in templates for presentation only

Multi-Issue Piece View (/beitrag/)

The application supports viewing pieces/articles that span multiple issues through a dedicated piece view interface that aggregates content chronologically.

URL Structure & Routing

URL Pattern: /beitrag/:id where ID is the piece's XML ID

  • Example: /beitrag/piece-abc123 (piece with XML ID "piece-abc123")
  • Route Definition: PIECE_URL = "/beitrag/:id" in app/kgpz.go
  • Controller: controllers.GetPiece(k.Library) handles piece lookup and rendering

Architecture & Components

Controller (controllers/piece_controller.go):

  • Looks up pieces directly by XML ID
  • Handles piece aggregation across multiple issues
  • Returns 404 for invalid IDs or non-existent pieces

View Model (viewmodels/piece_view.go):

  • PieceVM struct aggregates data from multiple issues
  • AllIssueRefs []xmlmodels.IssueRef - chronologically ordered issue references
  • AllPages []PiecePageEntry - sequential page data with image paths
  • Pre-processes page icons, grid layouts, and visibility flags
  • Resolves image paths using registry system

Template System (views/routes/piece/):

  • body.gohtml - Two-column layout with Inhaltsverzeichnis and sequential pages
  • head.gohtml - Page metadata and title generation
  • components/_piece_inhaltsverzeichnis.gohtml - Table of contents with piece content
  • components/_piece_sequential_layout.gohtml - Chronological page display

Key Features

Multi-Issue Aggregation:

  • Pieces spanning multiple issues are unified in a single view
  • Chronological ordering preserves reading sequence across issue boundaries
  • Issue context (year/number) displayed with each page for reference

Component Reuse:

  • Reuses _inhaltsverzeichnis_eintrag template for consistent content display
  • Integrates with existing _newspaper_layout components for single-page viewer
  • Shares highlighting system and navigation patterns with issue view

Sequential Layout:

  • Two-column responsive design: Inhaltsverzeichnis (1/3) + Page Layout (2/3)
  • Left-aligned page indicators with format: [icon] YYYY Nr. XX, PageNum
  • No grid constraints - simple sequential flow for multi-issue reading

Highlighting System Integration:

  • Uses same intersection observer system as issue view (main.js)
  • Page links in Inhaltsverzeichnis turn red when corresponding page is visible
  • Page indicators above images also highlight during scroll
  • Automatic scroll-to-highlighted functionality

Template Integration

Helper Functions (templating/engine.go):

  • GetPieceURL(year, issueNum, page int) string - generates piece URLs
  • Reuses existing PageIcon() for consistent icon display
  • getImagePathFromRegistry() for proper image path resolution

Data Attributes for JavaScript:

  • data-page-container on page containers for scroll detection
  • data-page-number on Inhaltsverzeichnis links for highlighting
  • newspaper-page-container class for intersection observer
  • inhalts-entry class for hover and highlighting behavior

Responsive Behavior:

  • Mobile: Single column with collapsible Inhaltsverzeichnis
  • Desktop: Fixed two-column layout with sticky table of contents
  • Single-page viewer integration with proper navigation buttons

Usage Examples

Linking to Pieces:

<a href="{{ GetPieceURL $piece.ID }}">
    gesamten beitrag anzeigen
</a>

Page Navigation in Inhaltsverzeichnis:

<a href="/{{ $pageEntry.IssueYear }}/{{ $pageEntry.IssueNumber }}/{{ $pageEntry.PageNumber }}"
   class="page-number-inhalts" data-page-number="{{ $pageEntry.PageNumber }}">
   {{ $issueRef.When.Day }}.{{ $issueRef.When.Month }}.{{ $issueRef.When.Year }} [Nr. {{ $pageEntry.IssueNumber }}], {{ $pageEntry.PageNumber }}
</a>

Error Handling

Invalid IDs: Returns 404 for non-existent piece IDs Missing Pieces: Returns 404 when piece lookup fails in XML data Missing Images: Graceful fallback with "Keine Bilder verfügbar" message Cross-Issue Navigation: Handles pieces spanning non-consecutive issues

Direct Page Navigation System

The application provides a direct page navigation system that allows users to jump directly to any page by specifying year and page number, regardless of which issue contains that page.

URL Structure

New URL Format: All page links now use path parameters instead of hash fragments:

  • Before: /1771/42#page-166
  • After: /1771/42/166

This change applies to all page links throughout the application, including:

  • Page sharing/citation links
  • Inhaltsverzeichnis page navigation
  • Single page viewer navigation

Page Jump Interface

Location: Available on year overview pages (/jahrgang/:year)

Features:

  • Year Selection: Dropdown with all available years (1764-1779)
  • Page Input: Numeric input with validation
  • HTMX Integration: Real-time error feedback without page reload
  • Auto-redirect: Successful lookups redirect to /year/issue/page

URL Patterns:

  • Form Submission: POST /jump with form data
  • Direct URL: GET /jump/:year/:page (redirects to found issue)

Error Handling

Comprehensive Validation:

  • Invalid Year: Years outside 1764-1779 range
  • Invalid Page: Non-numeric or negative page numbers
  • Page Not Found: Page doesn't exist in any issue of specified year
  • Form Preservation: Error responses maintain user input for correction

HTMX Error Responses:

  • Form replaced with error version showing red borders and error messages
  • Specific error targeting (year field vs. page field)
  • Graceful degradation with clear user feedback

Auto-Scroll Implementation

URL-Based Navigation:

  • Pages accessed via /year/issue/page auto-scroll to target page
  • JavaScript detects path-based page numbers (not hash fragments)
  • Smooth scrolling with proper timing for layout initialization
  • Automatic highlighting in Inhaltsverzeichnis

Technical Implementation:

// Auto-scroll on page load if targetPage is specified
const pathParts = window.location.pathname.split('/');
if (pathParts.length >= 4 && !isNaN(pathParts[pathParts.length - 1])) {
    const pageNumber = pathParts[pathParts.length - 1];
    // Scroll to page container and highlight
}

Controller Architecture

Page Jump Controller (controllers/page_jump_controller.go):

  • FindIssueByYearAndPage() - Lookup function for issue containing specific page
  • GetPageJump() - Handles direct URL navigation (/jump/:year/:page)
  • GetPageJumpForm() - Handles form submissions (POST /jump)
  • Error rendering with HTML form generation

Issue Controller Updates (controllers/ausgabe_controller.go):

  • Enhanced to handle optional page parameter in /:year/:issue/:page?
  • Page validation against issue page ranges
  • Target page passed to template for auto-scroll JavaScript

JavaScript Functions (views/transform/main.js):

  • copyPagePermalink() - Generates /year/issue/page URLs
  • generatePageCitation() - Uses new URL format for citations
  • scrollToPageFromURL() - URL-based navigation (replaces hash-based)

Template Integration:

  • Page links updated throughout templates to use new URL format
  • Maintains backward compatibility for beilage/supplement pages (still uses hash)
  • HTMX navigation preserved with new URL structure

Usage Examples

Direct Page Access:

http://127.0.0.1:8080/1771/42/166  # Direct link to page 166

Page Jump Form:

<form hx-post="/jump" hx-swap="outerHTML">
    <select name="year">...</select>
    <input type="number" name="page" />
    <button type="submit">Zur Seite springen</button>
</form>

Link Generation:

// New format for regular pages
const pageUrl = `/${year}/${issue}/${pageNumber}`;
// Old format still used for beilage pages
const beilageUrl = `${window.location.pathname}#beilage-1-page-${pageNumber}`;

Quickfilter System (/filter)

The application provides a universal quickfilter system accessible from any page via a header button, offering quick access to common navigation and filtering tools.

Architecture & Integration

Header Integration (views/layouts/components/_header.gohtml & _menu.gohtml):

  • Universal Access: Schnellfilter button available in every page header
  • Expandable Design: Header expands downwards to show filter content
  • HTMX-Powered: Dynamic loading of filter content without page refresh
  • Seamless UI: Integrates with existing header styling and layout

Controller (controllers/filter_controller.go):

  • GetQuickFilter(kgpz *xmlmodels.Library) - Renders filter interface
  • Uses "clear" layout for partial HTML fragments
  • Dynamically extracts available years from issue data

Template System (views/routes/filter/body.gohtml):

  • Clean, responsive filter interface with modern styling
  • Expandable structure for future filter options
  • Integrates existing functionality (page jump) in unified interface

Current Features

Page Jump Integration:

  • Moved from year pages: "Direkt zu Seite springen" functionality relocated from /jahrgang/ pages to header
  • Universal availability: Now accessible from any page in the application
  • Same functionality: Year dropdown, page input, error handling, HTMX validation
  • Consistent UX: Maintains all existing behavior and error feedback

UI Components:

  • Toggle Button: Filter icon in header with hover effects and visual feedback
  • Expandable Container: Header expands naturally to accommodate filter content
  • Responsive Design: Mobile-friendly with proper touch interactions
  • Click-Outside Close: Filter closes when clicking outside the container

Technical Implementation

URL Structure:

  • Filter Endpoint: GET /filter - Renders filter interface using clear layout
  • Route Configuration: FILTER_URL = "/filter" defined in app/kgpz.go

JavaScript Functionality (views/layouts/components/_menu.gohtml):

// Toggle filter visibility
function toggleFilter() {
    const filterContainer = document.getElementById('filter-container');
    const filterButton = document.getElementById('filter-toggle');

    if (filterContainer.classList.contains('hidden')) {
        filterContainer.classList.remove('hidden');
        filterButton.classList.add('bg-slate-200');
    } else {
        filterContainer.classList.add('hidden');
        filterButton.classList.remove('bg-slate-200');
    }
}

// Close filter when clicking outside
document.addEventListener('click', function(event) {
    // Automatic close functionality
});

HTMX Integration:

<button id="filter-toggle"
        hx-get="/filter"
        hx-target="#filter-container > div"
        hx-swap="innerHTML"
        onclick="toggleFilter()">
    <i class="ri-filter-2-line"></i> Schnellfilter
</button>

Layout System

Header Expansion:

  • Natural Flow: Filter container expands header downwards using normal document flow
  • Content Displacement: Page content moves down automatically when filter is open
  • Visual Consistency: Uses same bg-slate-50 background as header
  • Centered Content: Filter content centered within expanded header area

Template Structure:

<!-- Header container expands naturally -->
<div id="filter-container" class="mt-6 hidden">
    <div class="flex justify-center">
        <!-- Filter content loaded here via HTMX -->
    </div>
</div>

Extensible Design

Future Enhancement Ready:

  • Modular template structure allows easy addition of new filter options
  • Controller can be extended to handle additional filter types
  • Template includes placeholder section for "Weitere Filter"
  • Architecture supports complex filtering without performance impact

Data Processing:

  • Efficient year extraction from issue data using same pattern as year_view.go
  • Sorted year list generation with proper deduplication
  • Ready for additional data aggregation (categories, agents, places)

Usage Examples

Template Integration:

<!-- Filter automatically available in all pages via header -->
<!-- No additional template includes needed -->

Controller Extension:

// Example of extending filter data
data := fiber.Map{
    "AvailableYears": availableYears,
    "Categories":     categories,     // Future enhancement
    "TopAgents":      topAgents,      // Future enhancement
}

Migration Impact

Improved User Experience:

  • Reduced Page Clutter: Removed page jump form from year overview pages
  • Universal Access: Page jumping now available from anywhere in the application
  • Cleaner Year Pages: /jahrgang/ pages now focus purely on year navigation
  • Consistent Interface: Single location for all quick navigation tools

Agents/Authors View System (/akteure/ and /autoren/)

The application provides sophisticated person and organization browsing through dual view systems with advanced navigation and filtering capabilities.

Dual View Architecture

General Agents View (/akteure/):

  • Displays all persons and organizations mentioned in the newspaper
  • Supports letter-based navigation (A-Z)
  • Individual person pages with detailed information
  • Two-column layout with scrollspy navigation on large screens

Authors-Only View (/autoren/):

  • Filtered view showing only people who authored pieces (Beiträge)
  • Single-page display of all authors regardless of starting letter
  • No alphabet navigation (all authors shown together)
  • Same advanced layout and scrollspy functionality

URL Structure & Navigation

URL Patterns:

  • /akteure/ - All persons overview
  • /akteure/a - Persons starting with letter "A"
  • /akteure/{id} - Individual person page
  • /akteure/autoren - Authors-only filtered view

Toggle Navigation:

  • Checkbox interface: "Nur Autoren anzeigen" switches between views
  • HTMX-powered transitions with URL history management
  • Unchecking returns to /akteure/a (letter A starting point)

Template Architecture & Components

Modular Template System (views/routes/components/):

  • _akteur.gohtml - Main component using sub-components
  • _akteur_header.gohtml - Name, life dates, professions, external links
  • _akteur_werke.gohtml - Works section with categorized pieces
  • _akteur_beitraege.gohtml - Contributions/pieces with grouping

Component Benefits:

  • Reusable across different view contexts
  • Maintainable separation of concerns
  • Consistent styling and behavior
  • Easy customization for specific views (authors vs. full agents)

Advanced Scrollspy Navigation

Full-Height Sidebar (2XL+ screens only):

  • Fixed 320px width with full viewport height
  • Sticky positioning that follows scroll
  • Complete name list with smooth scrolling navigation
  • Automatic cleanup on HTMX page transitions

Multi-Item Highlighting:

  • Highlights ALL currently visible authors simultaneously
  • Red left border indicating visible items (matches issue view pattern)
  • Header visibility detection (name, life data, professions must be fully visible)
  • Real-time updates during scroll with 50ms debouncing

Visual Features:

  • Larger text (text-base) for better readability
  • Closer spacing (py-1) for more names visible
  • Smooth transitions and hover effects
  • Blue background highlighting for active items

Controller Architecture

Unified Controller (controllers/akteur_controller.go):

  • Handles both general agents and authors-only views
  • Special routing for "autoren" parameter
  • Template path switching based on view type
  • Letter-based filtering and ID lookup

View Models (viewmodels/agent_view.go):

  • AgentsView() - General person lookup by letter/ID
  • AuthorsView() - Filtered view of piece authors only
  • AuthorsListView struct with sorting and letter availability
  • Pre-processed agent data for efficient template rendering

Template Features & Data Processing

Enhanced Data Presentation:

  • Grouped pieces by title and work reference
  • Category combination with proper German grammar ("und" vs. "mit")
  • Inline citation format: DD.MM.YYYY/ISSUENO, PPP[-PPP]
  • Works section showing review/commentary pieces
  • External link integration (Wikipedia, GND, VIAF)

Text Sizing & Hierarchy:

  • Large serif names (text-2xl font-serif font-bold)
  • Readable life dates and professions (text-xl)
  • Appropriately sized content text (text-lg)
  • Larger pill text (text-sm) matching issue view standards

JavaScript Integration

HTMX-Safe Scrollspy (views/transform/main.js):

  • Proper event listener cleanup on page navigation
  • Memory leak prevention with timeout management
  • Auto-initialization detection for .author-section elements
  • Smooth scroll behavior for sidebar navigation

Performance Optimizations:

  • Debounced scroll handling (50ms)
  • Efficient viewport calculations using getBoundingClientRect()
  • Minimal DOM queries with cached element references
  • Responsive behavior with automatic sidebar hiding

Responsive Design

Desktop Experience (2XL+ screens):

  • Two-column layout: 320px sidebar + flexible content area
  • Fixed scrollspy navigation with full name list
  • Multi-author highlighting system
  • Smooth scrolling between authors

Mobile Experience (< 2XL screens):

  • Single-column layout with full-width content
  • Hidden scrollspy navigation (saves space)
  • Touch-optimized interactions
  • Same content organization and functionality

Data Categories & Processing

Comprehensive Category Support:

  • All 29 XML-defined categories supported
  • Dynamic category detection and grouping
  • Proper German grammar rules for combinations
  • Author filtering for non-current-user pieces

Helper Functions (templating/engine.go):

  • merge, append, slice - Data manipulation
  • sortStrings, unique - Array processing
  • joinWithUnd - German grammar formatting
  • Enhanced data processing for complex template logic

Usage Examples

Template Integration:

{{ template "_akteur_header" $agent }}
{{ template "_akteur_werke" $agent }}
{{ template "_akteur_beitraege" $agent }}

Scrollspy Navigation:

<a href="#author-{{ $id }}"
   class="scrollspy-link border-l-4 border-transparent"
   data-target="author-{{ $id }}">
   {{ index $agent.Names 0 }}
</a>

HTMX Toggle:

<input type="checkbox"
       hx-get="/akteure/autoren"
       hx-target="body"
       hx-push-url="true">

Error Handling & Edge Cases

Template Safety:

  • Null checks for GND data and agent information
  • Graceful fallback for missing names or professions
  • Safe handling of empty work/piece lists
  • Error boundaries for external link data

Navigation Robustness:

  • 404 handling for invalid agent IDs
  • Automatic fallback for missing letters
  • Smooth transitions between view modes
  • Proper state management across HTMX swaps

Places System (/ort/) with Geonames Integration

The application provides comprehensive place browsing with sophisticated geographic information integration through the Geonames API, offering modern place names, coordinates, and Wikipedia links.

Architecture & Data Flow

Geonames Provider (providers/geonames/):

  • Local JSON file caching system for offline operation
  • API integration with geonames.org for geographic data enrichment
  • Structured data models for places, coordinates, alternate names, and external links
  • Automatic fallback between cached data and live API calls

Places Controller (controllers/ort_controller.go):

  • Handles both overview (/ort/) and individual place views (/ort/{id})
  • Integrates Geonames data with XML place data
  • Template rendering with pre-processed geographic information

View Models (viewmodels/place_view.go):

  • PlaceVM struct for individual place display with Geonames integration
  • PlacesOverviewVM for places listing with geographic context
  • Pre-processed modern country names and local toponyms

Geonames Data Integration

Template Functions (app/kgpz.go):

  • GetGeonames - Retrieves cached or live Geonames data for places
  • Geographic data accessible throughout all templates
  • String manipulation functions for name comparisons and formatting

Data Structure:

type GeonamesPlace struct {
    GeonameID       int                    `json:"geonameId"`
    Name            string                 `json:"name"`
    ToponymName     string                 `json:"toponymName"`
    CountryName     string                 `json:"countryName"`
    Lat             string                 `json:"lat"`
    Lng             string                 `json:"lng"`
    AlternateNames  []AlternateName        `json:"alternateNames"`
    WikipediaURL    string                 `json:"wikipediaURL"`
}

Modern Place Name Display Logic

German Name Priority System:

  1. Primary: Search for German ("de") alternate names in Geonames data
  2. Preferred Names: Prioritize names with IsPreferredName = true
  3. Fallback: Use ToponymName if no German names available
  4. Display Rule: Only show modern names if they differ from historical German names

Implementation:

{{- $modernName := "" -}}
{{- $hasGermanName := false -}}
{{- range $altName := $geonames.AlternateNames -}}
{{- if eq $altName.Lang "de" -}}
{{- $hasGermanName = true -}}
{{- if $altName.IsPreferredName -}}
{{- $modernName = $altName.Name -}}
{{- break -}}
{{- else if eq $modernName "" -}}
{{- $modernName = $altName.Name -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not $hasGermanName -}}
{{- $modernName = $geonames.ToponymName -}}
{{- end -}}
{{- if and (ne $modernName "") (ne (lower $modernName) (lower $mainPlaceName)) -}}, {{ $modernName }}{{- end }}

Country Name Translations

Supported Countries with German Translations:

  • France → "heutiges Frankreich"
  • United Kingdom → "heutiges Großbritannien"
  • Russia → "heutiges Russland"
  • Czech Republic/Czechia → "heutiges Tschechien"
  • Netherlands/The Netherlands → "heutige Niederlande"
  • Poland → "heutiges Polen"
  • Switzerland → "heutige Schweiz"
  • Latvia → "heutiges Lettland"
  • Sweden → "heutiges Schweden"
  • Austria → "heutiges Österreich"
  • Belgium → "heutiges Belgien"
  • Slovakia → "heutige Slowakei"
  • Finland → "heutiges Finnland"
  • Denmark → "heutiges Dänemark"

Display Format: "heutiges [Country], [Local Name]" (only when local name differs)

Geographic Features

Coordinate Integration:

  • Clickable coordinates linking to OpenStreetMap
  • Format: https://www.openstreetmap.org/?mlat={lat}&mlon={lng}&zoom=12
  • Visual styling with map pin icons and hover effects

External Links:

  • Wikipedia Integration: Automatic Wikipedia link detection and display
  • Geonames Links: Direct links to Geonames.org entries
  • Consistent icon styling with external link security (target="_blank", rel="noopener noreferrer")

Template Structure

Individual Place View (views/routes/ort/body.gohtml):

  • Header Section: Place name with back navigation styled like agent pages
  • Geographic Info: Modern country name with local toponyms when different
  • Coordinates: Clickable OpenStreetMap links with proper formatting
  • External Links: Wikipedia and Geonames integration with appropriate icons
  • Linked Articles: "Verlinkte Beiträge" section showing associated newspaper pieces

Places Overview (views/routes/ort/):

  • Streamlined Layout: Removed non-functional alphabet navigation
  • Card Grid: Responsive place cards with consistent heights (h-24)
  • Geographic Context: Modern country names with local toponyms in overview cards
  • Clean Design: Focus on essential information without cluttered indicators

Navigation & UI Improvements

Back Navigation:

  • Style Consistency: Matches agent page back button styling
  • Typography: Large, bold text with arrow icon (ri-arrow-left-line)
  • Simplified Text: "Orte" instead of "Zurück zur Übersicht"
  • Color Scheme: Gray text with black hover, transition effects

External Link Styling:

  • Consistent Icons: Smaller Geonames symbols (text-xl instead of text-2xl)
  • No Underlines: no-underline class for cleaner appearance
  • Hover Effects: Opacity transitions for better user feedback

Data Processing & Caching

Geonames Provider Features:

  • Local Caching: JSON files stored locally to reduce API dependency
  • Automatic Fallback: Graceful degradation when API unavailable
  • Data Enrichment: Combines XML place data with geographic information
  • Performance: Cached lookups for frequently accessed places

Template Integration:

  • Helper Functions: Accessible via GetGeonames template function
  • Error Handling: Safe handling of missing or incomplete geographic data
  • Responsive Design: Geographic information hidden for German places to reduce clutter

Usage Examples

Template Integration:

{{ $geonames := GetGeonames .model.SelectedPlace.Place.ID }}
{{ if and (ne $geonames nil) (ne $geonames.CountryName "Germany") }}
    <p>{{ $geonames.CountryName }}</p>
{{ end }}

External Links:

{{ if ne $geonames.WikipediaURL "" }}
    <a href="{{ $geonames.WikipediaURL }}" target="_blank" rel="noopener noreferrer">
        <i class="ri-wikipedia-line"></i> Wikipedia
    </a>
{{ end }}

Coordinates:

{{ if and (ne $geonames.Lat "") (ne $geonames.Lng "") }}
    <a href="https://www.openstreetmap.org/?mlat={{ $geonames.Lat }}&mlon={{ $geonames.Lng }}&zoom=12"
       target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-700 underline">
        {{ $geonames.Lat }}, {{ $geonames.Lng }}
    </a>
{{ end }}

Error Handling & Fallbacks

Geographic Data Safety:

  • Graceful handling of missing Geonames data
  • Safe template evaluation with null checks
  • Fallback display for places without geographic information
  • Error boundaries for external API failures

Template Robustness:

  • Case-insensitive name comparisons using lower template function
  • Proper handling of empty alternate names arrays
  • Safe string manipulation with whitespace control
  • Consistent behavior across detail and overview templates

Unified Piece Entry System

The application uses a centralized component for displaying newspaper pieces (Beiträge) consistently across all views, ensuring uniform citation formatting and category-dependent descriptions throughout the interface.

Core Component

Central Template (views/routes/components/_unified_piece_entry.gohtml):

  • Universal component for piece display across all view contexts
  • Handles both viewmodels.PieceByIssue and xmlmodels.Piece data structures
  • Contains comprehensive category-specific descriptions (29+ categories)
  • Unified citation formatting and place tag generation
  • Supports different display modes for various page contexts

Data Structure Handling

Multi-Type Support:

{{- /* Handle different piece types */ -}}
{{- $piece := $pieceInput -}}
{{- $isContinuation := false -}}
{{- if eq $displayMode "issue" -}}
  {{- $piece = $pieceInput.Piece -}}
  {{- $isContinuation = $pieceInput.IsContinuation -}}
{{- end -}}

Display Mode Parameters:

  • issue - For issue table of contents with continuation handling
  • piece - For multi-issue piece views
  • place - For place-associated pieces with colon format
  • akteure - For agent/author contribution lists

Category-Dependent Descriptions

Comprehensive Category Support:

  • Reviews: "Rezension zu [work title]" with linked authors
  • Obituaries: "Nachruf auf [person name]" with agent category detection
  • Advertisements: "Anzeige" with multiple place support
  • Letters: "Brief" with sender/recipient information
  • Announcements: Category-specific formatting for each type
  • Academic: "Dissertation", "Disputation", "Programm" with institutional context

Special Category Handling:

{{- range $agentref := $piece.AgentRefs -}}
  {{- if eq $agentref.Category "nachruf" -}}
    {{- $agent := GetAgent $agentref.Ref -}}
    Nachruf auf <a href="/akteure/{{ $agentref.Ref }}">{{ index $agent.Names 0 }}</a>
  {{- end -}}
{{- end -}}

Place Tag Integration

Clickable Place Links:

  • Automatic place tag generation for all pieces
  • Links to place detail pages (/ort/{id})
  • Multiple place support for advertisements and events
  • Conditional display based on piece category

Implementation:

{{ if and (ne (len $piece.PlaceRefs) 0) $ShowPlaceTags }}
  {{ range $index, $placeRef := $piece.PlaceRefs }}
    {{ if gt $index 0 }}, {{ end }}
    <a href="/ort/{{ $placeRef.Ref }}" class="place-tag">{{ $placeName }}</a>
  {{ end }}
{{ end }}

Usage Across Views

Current Integration Points:

  • Issue View (_inhaltsverzeichnis.gohtml): Table of contents with continuation handling
  • Agent Views (_akteur_beitraege.gohtml): Author contribution lists
  • Place Views (_place_pieces.gohtml): Place-associated pieces
  • Piece Views (_piece_inhaltsverzeichnis.gohtml): Multi-issue piece displays

Template Call Pattern:

{{ template "_unified_piece_entry" (dict
   "Piece" $piece
   "DisplayMode" "issue"
   "ShowPlaceTags" true
   "UseColonFormat" false
   "ShowContinuation" true
) }}

Parameters & Configuration

Required Parameters:

  • Piece - The piece data structure (either type)
  • DisplayMode - Context for formatting ("issue", "piece", "place", "akteure")

Optional Parameters:

  • ShowPlaceTags (bool) - Whether to display clickable place links
  • UseColonFormat (bool) - Use colon separator for place-specific formatting
  • ShowContinuation (bool) - Show continuation indicators for multi-part pieces
  • CurrentActorID (string) - Exclude current agent from author links in agent views

Category Formatting Rules

Natural Language Descriptions:

  • Proper German grammar with gender-appropriate articles
  • Work titles in italics with proper author attribution
  • Place names as clickable tags when relevant
  • Agent references with appropriate relationship indicators

Title Fallback Logic:

  1. Use piece title if available
  2. Fall back to incipit (opening words) if no title
  3. Generate category-specific description
  4. Handle special cases (reviews, obituaries, advertisements)

Maintenance & Extension

Adding New Categories:

  1. Add new category case in the main conditional block
  2. Implement category-specific description logic
  3. Handle agent/place/work references as needed
  4. Test across all view contexts

Modifying Display Logic:

  • Edit only _unified_piece_entry.gohtml
  • Changes automatically apply to all views
  • Test with different piece types and display modes
  • Verify place tag and agent link functionality

Error Handling

Template Safety:

  • Null checks for all piece data fields
  • Safe handling of missing titles, authors, or places
  • Graceful fallback for unknown categories
  • Type-safe access to different piece structures

Data Validation:

  • Proper handling of empty agent/place reference arrays
  • Safe string manipulation and concatenation
  • Conditional display based on data availability
  • Consistent behavior across different piece types

Agent Relationship System

The application handles complex relationships between agents (persons/organizations) and both works and pieces (Beiträge), with specific formatting for different contributor roles.

Contributor Categories

Supported Agent Categories:

  • Empty/"autor": Primary authors (no special suffix)
  • "übersetzer": Translators - displayed with "(Übers.)" suffix
  • "herausgeber": Editors - displayed with "(Hrsg.)" suffix
  • "nachruf": Obituary subjects - special handling for "Nachruf auf [person]"

Work Relationships (_akteur_werke.gohtml)

Role Qualification in Works Bibliography:

  • Works display person's relationship to each work as a prefix
  • Authors: No prefix (default relationship)
  • Translators: (Übers.) [Work Title]
  • Editors: (Hrsg.) [Work Title]

Example Output:

Werke
(Übers.) Herrn Johann Ludwigs Bianconi Zehn Sendschreiben an Herrn Marchese Philippo Hercolani...
Rezension: 1.2.1765/9, S. 33-34

Die Aufklärung der Philosophie (Leipzig: Breitkopf 1766)
Rezension: 1.5.1766/15, S. 45-48

Implementation Details:

  • Role detection via $w.Item.AgentRefs matching current person ID
  • Prefix added before existing Citation.HTML content
  • Break after finding person's role for performance
  • Maintains all existing formatting and external links

Piece Relationships (Unified System)

Multi-Role Contributor Display:

  • Authors, translators, and editors can all contribute to single pieces
  • Consistent formatting across all view contexts
  • Proper comma separation and German conjunction handling

Display Patterns:

Author1, Author2, Translator1 (Übers.), Editor1 (Hrsg.): [Piece Content]
Schmidt (Übers.), Müller (Hrsg.): Rezension von: Goethe, Faust
Translator1 (Übers.): Übersetzung aus: Voltaire, Candide

Work Citation Enhancement:

  • Work contributors shown with roles in piece citations
  • Example: Rezension von: Giovanni Lodovico Bianconi, Dorothea Henriette Runckel (Übers.), Zehn Sendschreiben
  • Maintains work author, translator, and editor relationships

Context-Sensitive Display

Agent Detail Pages (/akteure/{id}):

  • Works Section: Shows person's role as prefix (Übers.) or (Hrsg.)
  • Beiträge Section: Groups pieces by title and contributors, shows other collaborators
  • Current Agent Exclusion: Hides current person from contributor lists in their own page

Issue Views (/year/issue):

  • Full contributor display with all roles
  • Shows piece authors, translators, and editors in table of contents
  • Work citations include all work contributors with roles

Place Views (/ort/{id}):

  • Colon format: Author (Übers.): [Content]
  • Regular format: Author (Übers.), [Content]
  • Place-specific formatting for associated pieces

Multi-Issue Piece Views (/beitrag/{id}):

  • Consistent contributor display across issue boundaries
  • Maintains role information in sequential display
  • Work citations preserve contributor relationships

Technical Implementation

Variable Collections:

{{- $authors := slice -}}
{{- $translators := slice -}}
{{- $editors := slice -}}
{{- range $agentref := $piece.AgentRefs -}}
  {{- if eq $agentref.Category "übersetzer" -}}
    {{- $translators = append $translators (dict "ID" $agentref.Ref "Name" $agentName) -}}
  {{- else if eq $agentref.Category "herausgeber" -}}
    {{- $editors = append $editors (dict "ID" $agentref.Ref "Name" $agentName) -}}
  {{- end -}}
{{- end -}}

Role Detection Logic:

{{- range $workAgentRef := $work.AgentRefs -}}
  {{- if eq $workAgentRef.Ref $currentPersonID -}}
    {{- if eq $workAgentRef.Category "übersetzer" -}}
      {{- $personRole = "(Übers.) " -}}
    {{- else if eq $workAgentRef.Category "herausgeber" -}}
      {{- $personRole = "(Hrsg.) " -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

Formatting Functions:

  • joinWithUnd - German conjunction for multiple contributors
  • Current actor exclusion logic for agent detail pages
  • Comma separation with proper spacing for role suffixes

Error Handling

Relationship Safety:

  • Graceful handling of missing agent references
  • Safe access to agent data via GetAgent function
  • Null checks for agent names and IDs
  • Fallback display for unknown or malformed relationships

Performance Considerations:

  • Break statements after finding target relationships
  • Efficient grouping by contributor combinations
  • Minimal DOM manipulation for role prefixes
  • Cached agent lookups where possible