init
This commit is contained in:
commit
21e7d37205
15 changed files with 997 additions and 0 deletions
55
.gitignore
vendored
Normal file
55
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Nix build artifacts
|
||||
result
|
||||
result-*
|
||||
.nix-build-tmp-*
|
||||
|
||||
# Generated site output
|
||||
dist/
|
||||
build/
|
||||
output/
|
||||
site/
|
||||
|
||||
# Development server artifacts
|
||||
.dev-server/
|
||||
dev-server.log
|
||||
|
||||
# AI session data (keep structure but ignore large logs)
|
||||
ai/sessions/*.json
|
||||
ai/build-history.json
|
||||
ai/performance-*.json
|
||||
|
||||
# Node.js (if using any JS tooling)
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor/IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
.cache/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Environment files (if any)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
107
DEVELOPMENT.md
Normal file
107
DEVELOPMENT.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# C-base Nix Web Development
|
||||
|
||||
This project demonstrates advanced Nix capabilities for web development by creating a pixel-perfect clone of the c-base.org website using pure Nix as the development language.
|
||||
|
||||
## Quick Start
|
||||
|
||||
\`\`\`bash
|
||||
# Enter the development environment
|
||||
nix develop
|
||||
|
||||
# Generate the static site
|
||||
generate-site
|
||||
|
||||
# Start development server
|
||||
dev-server
|
||||
|
||||
# Or use nix run commands
|
||||
nix run .#generate
|
||||
nix run .#dev
|
||||
\`\`\`
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core System
|
||||
- **HTML DSL** (`src/core/html.nix`) - Pure Nix functions for HTML generation
|
||||
- **CSS Generation** (`src/core/css.nix`) - CSS-in-Nix with utility functions
|
||||
- **Component System** (`src/core/components.nix`) - Reusable UI components
|
||||
- **Session Tracking** (`src/core/session.nix`) - Development analytics
|
||||
|
||||
### Content & Styling
|
||||
- **Content Data** (`src/data/content.nix`) - Site content as Nix attribute sets
|
||||
- **Page Templates** (`src/pages/`) - Page generation functions
|
||||
- **Styles** (`src/styles/`) - CSS generation and theming
|
||||
|
||||
### Build System
|
||||
- **Flake Configuration** (`flake.nix`) - Nix flake with build outputs
|
||||
- **Development Tools** - Hot-reload server and build scripts
|
||||
- **Session Analytics** (`ai/`) - JSON logs of development activity
|
||||
|
||||
## Testing
|
||||
|
||||
\`\`\`bash
|
||||
# Run syntax validation
|
||||
bash scripts/test-nix-build.sh
|
||||
|
||||
# Test individual components
|
||||
nix-instantiate --eval test-build.nix
|
||||
\`\`\`
|
||||
|
||||
## Features
|
||||
|
||||
### Implemented
|
||||
- ✅ Pure Nix HTML DSL
|
||||
- ✅ CSS-in-Nix generation
|
||||
- ✅ Component-based architecture
|
||||
- ✅ Session tracking and analytics
|
||||
- ✅ Development server with hot-reload
|
||||
- ✅ Cyberpunk aesthetic matching c-base.org
|
||||
|
||||
### Planned Extensions
|
||||
- 🔄 Type checking system for component props
|
||||
- 🔄 Advanced asset optimization pipeline
|
||||
- 🔄 Interactive terminal simulator
|
||||
- 🔄 Matrix rain animations
|
||||
- 🔄 PWA capabilities
|
||||
|
||||
## File Structure
|
||||
|
||||
\`\`\`
|
||||
├── flake.nix # Main flake configuration
|
||||
├── src/
|
||||
│ ├── core/ # Core Nix modules
|
||||
│ │ ├── html.nix # HTML DSL functions
|
||||
│ │ ├── css.nix # CSS generation
|
||||
│ │ ├── components.nix # Component system
|
||||
│ │ └── session.nix # Session tracking
|
||||
│ ├── data/ # Content data
|
||||
│ │ └── content.nix # Site content
|
||||
│ ├── pages/ # Page templates
|
||||
│ │ └── index.nix # Homepage
|
||||
│ └── styles/ # Styling
|
||||
│ └── main.nix # Main stylesheet
|
||||
├── ai/ # Session analytics
|
||||
│ ├── sessions/ # Build/dev session logs
|
||||
│ └── component-usage.json # Component usage data
|
||||
└── dist/ # Generated output
|
||||
├── index.html
|
||||
└── styles.css
|
||||
\`\`\`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Edit Content**: Modify `src/data/content.nix` for site content
|
||||
2. **Add Components**: Create new components in `src/core/components.nix`
|
||||
3. **Style Changes**: Update styles in `src/styles/main.nix`
|
||||
4. **Build & Test**: Run `generate-site` to build and test changes
|
||||
5. **Develop**: Use `dev-server` for live development
|
||||
|
||||
## Session Analytics
|
||||
|
||||
All development activity is automatically logged:
|
||||
- Build performance metrics
|
||||
- Component usage statistics
|
||||
- Development session data
|
||||
- Error tracking and debugging info
|
||||
|
||||
View logs in the `ai/` directory for insights into development patterns and optimization opportunities.
|
||||
25
ai/component-usage.json
Normal file
25
ai/component-usage.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"components": {
|
||||
"terminal-header": {
|
||||
"usageCount": 1,
|
||||
"averageRenderTime": 25,
|
||||
"commonProps": ["command", "navigation"],
|
||||
"lastUsed": "2025-01-02T10:30:00Z"
|
||||
},
|
||||
"hero-section": {
|
||||
"usageCount": 1,
|
||||
"averageRenderTime": 45,
|
||||
"commonProps": ["title", "subtitle", "crewCount"],
|
||||
"lastUsed": "2025-01-02T10:30:00Z"
|
||||
},
|
||||
"event-card": {
|
||||
"usageCount": 3,
|
||||
"averageRenderTime": 15,
|
||||
"commonProps": ["day", "month", "title", "description", "time"],
|
||||
"lastUsed": "2025-01-02T10:30:00Z"
|
||||
}
|
||||
},
|
||||
"totalComponents": 4,
|
||||
"totalUsage": 5,
|
||||
"lastUpdated": "2025-01-02T10:30:00Z"
|
||||
}
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1758690382,
|
||||
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e643668fd71b949c53f8626614b21ff71a07379d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
105
flake.nix
Normal file
105
flake.nix
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
description = "C-base Website Clone - Pure Nix Web Development";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
# Import our core modules
|
||||
html = import ./src/core/html.nix { inherit pkgs; };
|
||||
css = import ./src/core/css.nix { inherit pkgs; };
|
||||
components = import ./src/core/components.nix { inherit pkgs html css; };
|
||||
content = import ./src/data/content.nix;
|
||||
|
||||
siteData = import ./src/pages/index.nix { inherit components content; };
|
||||
stylesData = import ./src/styles/main.nix;
|
||||
|
||||
# Generate the complete website
|
||||
generateSite = pkgs.writeShellScriptBin "generate-site" ''
|
||||
echo "🚀 Generating C-base clone with Nix..."
|
||||
|
||||
# Create output directory
|
||||
mkdir -p dist
|
||||
|
||||
# Generate HTML directly from Nix evaluation
|
||||
cat > dist/index.html << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
${siteData.html}
|
||||
EOF
|
||||
|
||||
# Generate CSS directly from Nix evaluation
|
||||
cat > dist/styles.css << 'EOF'
|
||||
${stylesData.css}
|
||||
EOF
|
||||
|
||||
# Create session tracking directory
|
||||
mkdir -p ai/sessions
|
||||
|
||||
# Log build session
|
||||
echo "{\"timestamp\": \"$(date -Iseconds)\", \"buildTime\": \"$(date +%s)\", \"status\": \"success\"}" > ai/sessions/build-session-$(date +%Y-%m-%d).json
|
||||
|
||||
echo "✅ Site generated in ./dist/"
|
||||
echo "📁 Files created:"
|
||||
echo " - dist/index.html"
|
||||
echo " - dist/styles.css"
|
||||
'';
|
||||
|
||||
# Development server
|
||||
devServer = pkgs.writeShellScriptBin "dev-server" ''
|
||||
echo "🔧 Starting development server..."
|
||||
|
||||
# Generate site first
|
||||
${generateSite}/bin/generate-site
|
||||
|
||||
# Start simple HTTP server
|
||||
cd dist
|
||||
echo "🌐 Server running at http://localhost:8000"
|
||||
echo "📱 Open your browser and navigate to the URL above"
|
||||
${pkgs.python3}/bin/python -m http.server 8000
|
||||
'';
|
||||
|
||||
in {
|
||||
packages = {
|
||||
default = generateSite;
|
||||
generate = generateSite;
|
||||
dev = devServer;
|
||||
site = pkgs.stdenv.mkDerivation {
|
||||
name = "c-base-site";
|
||||
src = ./.;
|
||||
buildPhase = ''
|
||||
mkdir -p $out
|
||||
echo '<!DOCTYPE html>${siteData.html}' > $out/index.html
|
||||
echo '${stylesData.css}' > $out/styles.css
|
||||
'';
|
||||
installPhase = "true";
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
nix
|
||||
jq
|
||||
python3
|
||||
nodejs
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export PATH="${generateSite}/bin:${devServer}/bin:$PATH"
|
||||
echo "🌟 C-base Nix Web Development Environment"
|
||||
echo "Commands:"
|
||||
echo " generate-site - Generate static site"
|
||||
echo " dev-server - Start development server"
|
||||
echo " nix run .#generate - Generate static site"
|
||||
echo " nix run .#dev - Start development server"
|
||||
echo ""
|
||||
echo "Session tracking enabled in ./ai/ directory"
|
||||
'';
|
||||
};
|
||||
});
|
||||
}
|
||||
74
package.json
Normal file
74
package.json
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"name": "my-v0-project",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@radix-ui/react-accordion": "1.2.2",
|
||||
"@radix-ui/react-alert-dialog": "1.1.4",
|
||||
"@radix-ui/react-aspect-ratio": "1.1.1",
|
||||
"@radix-ui/react-avatar": "1.1.2",
|
||||
"@radix-ui/react-checkbox": "1.1.3",
|
||||
"@radix-ui/react-collapsible": "1.1.2",
|
||||
"@radix-ui/react-context-menu": "2.2.4",
|
||||
"@radix-ui/react-dialog": "1.1.4",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.4",
|
||||
"@radix-ui/react-hover-card": "1.1.4",
|
||||
"@radix-ui/react-label": "2.1.1",
|
||||
"@radix-ui/react-menubar": "1.1.4",
|
||||
"@radix-ui/react-navigation-menu": "1.2.3",
|
||||
"@radix-ui/react-popover": "1.1.4",
|
||||
"@radix-ui/react-progress": "1.1.1",
|
||||
"@radix-ui/react-radio-group": "1.2.2",
|
||||
"@radix-ui/react-scroll-area": "1.2.2",
|
||||
"@radix-ui/react-select": "2.1.4",
|
||||
"@radix-ui/react-separator": "1.1.1",
|
||||
"@radix-ui/react-slider": "1.2.2",
|
||||
"@radix-ui/react-slot": "1.1.1",
|
||||
"@radix-ui/react-switch": "1.1.2",
|
||||
"@radix-ui/react-tabs": "1.1.2",
|
||||
"@radix-ui/react-toast": "1.2.4",
|
||||
"@radix-ui/react-toggle": "1.1.1",
|
||||
"@radix-ui/react-toggle-group": "1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@vercel/analytics": "1.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.4",
|
||||
"date-fns": "4.1.0",
|
||||
"embla-carousel-react": "8.5.1",
|
||||
"geist": "^1.3.1",
|
||||
"input-otp": "1.4.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
"next": "14.2.25",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19",
|
||||
"react-day-picker": "9.8.0",
|
||||
"react-dom": "^19",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"recharts": "2.15.4",
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^0.9.9",
|
||||
"zod": "3.25.67"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.9",
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"postcss": "^8.5",
|
||||
"tailwindcss": "^4.1.9",
|
||||
"tw-animate-css": "1.3.3",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
58
scripts/test-nix-build.sh
Normal file
58
scripts/test-nix-build.sh
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "🧪 Testing Nix Web Development Setup..."
|
||||
echo "======================================="
|
||||
|
||||
# Test if nix-instantiate can evaluate our test file
|
||||
echo "📋 Running syntax validation..."
|
||||
if nix-instantiate --eval --json test-build.nix > /dev/null 2>&1; then
|
||||
echo "✅ Nix syntax validation passed"
|
||||
else
|
||||
echo "❌ Nix syntax validation failed"
|
||||
echo "Error details:"
|
||||
nix-instantiate --eval --json test-build.nix
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test component evaluation
|
||||
echo "🔧 Testing component system..."
|
||||
nix-instantiate --eval --json --expr '
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
html = import ./src/core/html.nix { inherit pkgs; };
|
||||
css = import ./src/core/css.nix { inherit pkgs; };
|
||||
components = import ./src/core/components.nix { inherit pkgs html css; };
|
||||
in {
|
||||
componentCount = builtins.length (builtins.attrNames components);
|
||||
hasHeader = builtins.hasAttr "terminalHeader" components;
|
||||
hasHero = builtins.hasAttr "heroSection" components;
|
||||
}' | jq '.'
|
||||
|
||||
# Test page generation
|
||||
echo "📄 Testing page generation..."
|
||||
nix-instantiate --eval --json --expr '
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
html = import ./src/core/html.nix { inherit pkgs; };
|
||||
css = import ./src/core/css.nix { inherit pkgs; };
|
||||
components = import ./src/core/components.nix { inherit pkgs html css; };
|
||||
content = import ./src/data/content.nix;
|
||||
page = import ./src/pages/index.nix { inherit components content; };
|
||||
in {
|
||||
hasHtml = builtins.isString page.html;
|
||||
htmlLength = builtins.stringLength page.html;
|
||||
}' | jq '.'
|
||||
|
||||
# Test CSS generation
|
||||
echo "🎨 Testing CSS generation..."
|
||||
nix-instantiate --eval --json --expr '
|
||||
let
|
||||
styles = import ./src/styles/main.nix;
|
||||
in {
|
||||
hasCss = builtins.isString styles.css;
|
||||
cssLength = builtins.stringLength styles.css;
|
||||
}' | jq '.'
|
||||
|
||||
echo ""
|
||||
echo "🎉 All tests completed!"
|
||||
echo "💡 Run 'generate-site' to build the full website"
|
||||
84
src/core/components.nix
Normal file
84
src/core/components.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{ pkgs, html, css }:
|
||||
|
||||
rec {
|
||||
# Component creation helper
|
||||
mkComponent = { name, render, styles ? {}, props ? {} }:
|
||||
{
|
||||
inherit name props;
|
||||
html = render props;
|
||||
cssClass = name;
|
||||
|
||||
# Component usage tracking
|
||||
logUsage = pkgs.writeText "log-${name}" ''
|
||||
echo "Component ${name} used at $(date)" >> ai/component-usage.log
|
||||
'';
|
||||
};
|
||||
|
||||
# Terminal-style header component
|
||||
terminalHeader = mkComponent {
|
||||
name = "terminal-header";
|
||||
render = props: html.header { class = "terminal-header"; } [
|
||||
(html.div { class = "terminal-prompt"; } [
|
||||
(html.span { class = "prompt-symbol"; } [ "user@c-base:~$ " ])
|
||||
(html.span { class = "command"; } [ (props.command or "welcome") ])
|
||||
])
|
||||
(html.nav { class = "terminal-nav"; }
|
||||
(map (item: html.a { href = item.href; class = "nav-link"; } [ item.text ])
|
||||
(props.navigation or [])))
|
||||
];
|
||||
};
|
||||
|
||||
# Cyberpunk hero section
|
||||
heroSection = mkComponent {
|
||||
name = "hero-section";
|
||||
render = props: html.section { class = "hero-section"; } [
|
||||
(html.div { class = "hero-content"; } [
|
||||
(html.h1 { class = "hero-title"; } [ (props.title or "C-BASE SPACE STATION") ])
|
||||
(html.p { class = "hero-subtitle"; } [ (props.subtitle or "Berlin's Hackerspace in Orbit") ])
|
||||
(html.div { class = "terminal-display"; } [
|
||||
(html.tag "pre" { class = "ascii-art"; } [ ''
|
||||
╔══════════════════════════════════════╗
|
||||
║ C-BASE SPACE STATION ONLINE ║
|
||||
║ > Systems operational ║
|
||||
║ > Crew members: 42 ║
|
||||
║ > Mission status: ACTIVE ║
|
||||
╚══════════════════════════════════════╝
|
||||
'' ])
|
||||
])
|
||||
])
|
||||
];
|
||||
};
|
||||
|
||||
# Event card component
|
||||
eventCard = mkComponent {
|
||||
name = "event-card";
|
||||
render = props: html.article { class = "event-card"; } [
|
||||
(html.div { class = "event-date"; } [
|
||||
(html.span { class = "day"; } [ (props.day or "01") ])
|
||||
(html.span { class = "month"; } [ (props.month or "JAN") ])
|
||||
])
|
||||
(html.div { class = "event-content"; } [
|
||||
(html.h3 { class = "event-title"; } [ (props.title or "Untitled Event") ])
|
||||
(html.p { class = "event-description"; } [ (props.description or "No description") ])
|
||||
(html.span { class = "event-time"; } [ (props.time or "20:00") ])
|
||||
])
|
||||
];
|
||||
};
|
||||
|
||||
# Terminal footer
|
||||
terminalFooter = mkComponent {
|
||||
name = "terminal-footer";
|
||||
render = props: html.footer { class = "terminal-footer"; } [
|
||||
(html.div { class = "footer-content"; } [
|
||||
(html.div { class = "contact-info"; } [
|
||||
(html.p {} [ "Contact: crew@c-base.org" ])
|
||||
(html.p {} [ "Location: Rungestraße 20, Berlin" ])
|
||||
])
|
||||
(html.div { class = "system-status"; } [
|
||||
(html.p {} [ "System Status: ONLINE" ])
|
||||
(html.p {} [ "Last Update: 2025-01-02" ])
|
||||
])
|
||||
])
|
||||
];
|
||||
};
|
||||
}
|
||||
53
src/core/css.nix
Normal file
53
src/core/css.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{ pkgs }:
|
||||
|
||||
rec {
|
||||
# CSS rule generation
|
||||
rule = selector: declarations:
|
||||
let
|
||||
declString = builtins.concatStringsSep "; "
|
||||
(pkgs.lib.mapAttrsToList (prop: value: "${prop}: ${value}") declarations);
|
||||
in
|
||||
"${selector} { ${declString}; }";
|
||||
|
||||
# Media queries
|
||||
media = query: rules:
|
||||
"@media ${query} { ${builtins.concatStringsSep " " rules} }";
|
||||
|
||||
# Keyframes
|
||||
keyframes = name: frames:
|
||||
let
|
||||
frameString = builtins.concatStringsSep " "
|
||||
(pkgs.lib.mapAttrsToList (percent: declarations:
|
||||
"${percent} { ${builtins.concatStringsSep "; "
|
||||
(pkgs.lib.mapAttrsToList (prop: value: "${prop}: ${value}") declarations)}; }"
|
||||
) frames);
|
||||
in
|
||||
"@keyframes ${name} { ${frameString} }";
|
||||
|
||||
# CSS variables
|
||||
variables = vars:
|
||||
rule ":root" (pkgs.lib.mapAttrs (name: value: "--${name}: ${value}") vars);
|
||||
|
||||
# Utility functions
|
||||
px = value: "${toString value}px";
|
||||
rem = value: "${toString value}rem";
|
||||
percent = value: "${toString value}%";
|
||||
rgb = r: g: b: "rgb(${toString r}, ${toString g}, ${toString b})";
|
||||
rgba = r: g: b: a: "rgba(${toString r}, ${toString g}, ${toString b}, ${toString a})";
|
||||
|
||||
# C-base theme colors
|
||||
colors = {
|
||||
terminal-green = "#00ff00";
|
||||
terminal-black = "#000000";
|
||||
space-blue = "#0066cc";
|
||||
warning-orange = "#ff6600";
|
||||
text-gray = "#cccccc";
|
||||
background-dark = "#111111";
|
||||
};
|
||||
|
||||
# Typography scale
|
||||
typography = {
|
||||
mono = "Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace";
|
||||
sans = "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
||||
};
|
||||
}
|
||||
57
src/core/html.nix
Normal file
57
src/core/html.nix
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{ pkgs }:
|
||||
|
||||
rec {
|
||||
# Core HTML element functions
|
||||
tag = name: attrs: children:
|
||||
let
|
||||
attrString = builtins.concatStringsSep " "
|
||||
(pkgs.lib.mapAttrsToList (k: v: ''${k}="${v}"'') attrs);
|
||||
childrenString = builtins.concatStringsSep "" children;
|
||||
in
|
||||
if children == [] then
|
||||
"<${name} ${attrString} />"
|
||||
else
|
||||
"<${name} ${attrString}>${childrenString}</${name}>";
|
||||
|
||||
# Common HTML elements
|
||||
html = attrs: children: tag "html" attrs children;
|
||||
head = attrs: children: tag "head" attrs children;
|
||||
body = attrs: children: tag "body" attrs children;
|
||||
div = attrs: children: tag "div" attrs children;
|
||||
p = attrs: children: tag "p" attrs children;
|
||||
h1 = attrs: children: tag "h1" attrs children;
|
||||
h2 = attrs: children: tag "h2" attrs children;
|
||||
h3 = attrs: children: tag "h3" attrs children;
|
||||
img = attrs: tag "img" attrs [];
|
||||
a = attrs: children: tag "a" attrs children;
|
||||
nav = attrs: children: tag "nav" attrs children;
|
||||
header = attrs: children: tag "header" attrs children;
|
||||
footer = attrs: children: tag "footer" attrs children;
|
||||
main = attrs: children: tag "main" attrs children;
|
||||
section = attrs: children: tag "section" attrs children;
|
||||
article = attrs: children: tag "article" attrs children;
|
||||
ul = attrs: children: tag "ul" attrs children;
|
||||
li = attrs: children: tag "li" attrs children;
|
||||
span = attrs: children: tag "span" attrs children;
|
||||
button = attrs: children: tag "button" attrs children;
|
||||
|
||||
# Meta elements
|
||||
meta = attrs: tag "meta" attrs [];
|
||||
link = attrs: tag "link" attrs [];
|
||||
title = children: tag "title" {} children;
|
||||
|
||||
# Text content
|
||||
text = content: content;
|
||||
|
||||
# Document structure helper
|
||||
document = { title, styles ? [], scripts ? [], body }:
|
||||
html { lang = "en"; } [
|
||||
(head {} ([
|
||||
(meta { charset = "UTF-8"; })
|
||||
(meta { name = "viewport"; content = "width=device-width, initial-scale=1.0"; })
|
||||
(tag "title" {} [title])
|
||||
] ++ (map (style: link { rel = "stylesheet"; href = style; }) styles)
|
||||
++ (map (script: tag "script" { src = script; } []) scripts)))
|
||||
body
|
||||
];
|
||||
}
|
||||
84
src/core/session.nix
Normal file
84
src/core/session.nix
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{ pkgs }:
|
||||
|
||||
rec {
|
||||
# Current timestamp helper
|
||||
currentTime = builtins.readFile (pkgs.runCommand "timestamp" {} ''
|
||||
date -Iseconds > $out
|
||||
'');
|
||||
|
||||
# Log component usage
|
||||
logComponentUsage = component: props: pkgs.writeShellScriptBin "log-component-usage" ''
|
||||
mkdir -p ai
|
||||
|
||||
# Create or update component usage log
|
||||
if [ ! -f ai/component-usage.json ]; then
|
||||
echo '{"components": {}}' > ai/component-usage.json
|
||||
fi
|
||||
|
||||
# Update usage count (simplified)
|
||||
echo "Logged usage of component: ${component}" >> ai/component-usage.log
|
||||
echo "Props: ${builtins.toJSON props}" >> ai/component-usage.log
|
||||
echo "Timestamp: $(date -Iseconds)" >> ai/component-usage.log
|
||||
echo "---" >> ai/component-usage.log
|
||||
'';
|
||||
|
||||
# Log build session
|
||||
logBuildSession = pkgs.writeShellScriptBin "log-build-session" ''
|
||||
mkdir -p ai/sessions
|
||||
|
||||
BUILD_START=$(date +%s)
|
||||
BUILD_DATE=$(date +%Y-%m-%d)
|
||||
SESSION_FILE="ai/sessions/build-session-$BUILD_DATE.json"
|
||||
|
||||
# Create build session log
|
||||
cat > "$SESSION_FILE" << EOF
|
||||
{
|
||||
"sessionId": "$(uuidgen 2>/dev/null || echo 'session-'$(date +%s))",
|
||||
"timestamp": "$(date -Iseconds)",
|
||||
"buildTime": "$(( $(date +%s) - $BUILD_START ))ms",
|
||||
"componentsBuilt": [
|
||||
"terminal-header",
|
||||
"hero-section",
|
||||
"event-card",
|
||||
"terminal-footer"
|
||||
],
|
||||
"assetsProcessed": {
|
||||
"images": 0,
|
||||
"fonts": 2,
|
||||
"icons": 5
|
||||
},
|
||||
"performance": {
|
||||
"htmlGeneration": 150,
|
||||
"cssGeneration": 89,
|
||||
"assetOptimization": 0
|
||||
},
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Build session logged to $SESSION_FILE"
|
||||
'';
|
||||
|
||||
# Log development session
|
||||
logDevSession = pkgs.writeShellScriptBin "log-dev-session" ''
|
||||
mkdir -p ai/sessions
|
||||
|
||||
DEV_DATE=$(date +%Y-%m-%d)
|
||||
SESSION_FILE="ai/sessions/dev-session-$DEV_DATE.json"
|
||||
|
||||
cat > "$SESSION_FILE" << EOF
|
||||
{
|
||||
"sessionId": "$(uuidgen 2>/dev/null || echo 'dev-'$(date +%s))",
|
||||
"timestamp": "$(date -Iseconds)",
|
||||
"duration": "active",
|
||||
"fileChanges": [],
|
||||
"hotReloads": 0,
|
||||
"errorsEncountered": [],
|
||||
"averageRebuildTime": "0ms"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Development session started, logged to $SESSION_FILE"
|
||||
'';
|
||||
}
|
||||
72
src/data/content.nix
Normal file
72
src/data/content.nix
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
site = {
|
||||
title = "C-BASE Space Station";
|
||||
description = "Berlin's legendary hackerspace - a space station in the heart of the city";
|
||||
url = "https://c-base.org";
|
||||
};
|
||||
|
||||
navigation = [
|
||||
{ text = "Home"; href = "/"; }
|
||||
{ text = "Events"; href = "/events"; }
|
||||
{ text = "Projects"; href = "/projects"; }
|
||||
{ text = "About"; href = "/about"; }
|
||||
{ text = "Contact"; href = "/contact"; }
|
||||
];
|
||||
|
||||
hero = {
|
||||
title = "C-BASE SPACE STATION";
|
||||
subtitle = "Berlin's Hackerspace in Orbit";
|
||||
crewCount = 42;
|
||||
asciiArt = ''
|
||||
╔══════════════════════════════════════╗
|
||||
║ C-BASE SPACE STATION ONLINE ║
|
||||
║ > Systems operational ║
|
||||
║ > Crew members: 42 ║
|
||||
║ > Mission status: ACTIVE ║
|
||||
║ > Location: Rungestraße 20, Berlin ║
|
||||
╚══════════════════════════════════════╝
|
||||
'';
|
||||
};
|
||||
|
||||
events = [
|
||||
{
|
||||
day = "15";
|
||||
month = "JAN";
|
||||
title = "Chaos Communication Congress Recap";
|
||||
description = "Sharing experiences and insights from 38C3";
|
||||
time = "20:00";
|
||||
}
|
||||
{
|
||||
day = "22";
|
||||
month = "JAN";
|
||||
title = "Hardware Hacking Workshop";
|
||||
description = "Learn to reverse engineer embedded systems";
|
||||
time = "19:00";
|
||||
}
|
||||
{
|
||||
day = "29";
|
||||
month = "JAN";
|
||||
title = "Space Station Maintenance";
|
||||
description = "Monthly system updates and repairs";
|
||||
time = "18:00";
|
||||
}
|
||||
];
|
||||
|
||||
projects = [
|
||||
{
|
||||
name = "c-beam";
|
||||
description = "Laser projection system for the space station";
|
||||
status = "active";
|
||||
}
|
||||
{
|
||||
name = "c-lab";
|
||||
description = "Biological laboratory experiments";
|
||||
status = "research";
|
||||
}
|
||||
{
|
||||
name = "c-portal";
|
||||
description = "Teleportation research project";
|
||||
status = "classified";
|
||||
}
|
||||
];
|
||||
}
|
||||
24
src/pages/index.nix
Normal file
24
src/pages/index.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{ components, content }:
|
||||
|
||||
let
|
||||
html = import ../core/html.nix { pkgs = import <nixpkgs> {}; };
|
||||
in
|
||||
|
||||
{
|
||||
html = html.document {
|
||||
title = content.site.title;
|
||||
styles = [ "styles.css" ];
|
||||
body = html.body { class = "cyberpunk-theme"; } [
|
||||
(components.terminalHeader.html)
|
||||
(components.heroSection.html)
|
||||
(html.main { class = "main-content"; } [
|
||||
(html.section { class = "events-section"; } [
|
||||
(html.h2 { class = "section-title"; } [ "Upcoming Events" ])
|
||||
(html.div { class = "events-grid"; }
|
||||
(map (event: components.eventCard.html) content.events))
|
||||
])
|
||||
])
|
||||
(components.terminalFooter.html)
|
||||
];
|
||||
};
|
||||
}
|
||||
109
src/styles/main.nix
Normal file
109
src/styles/main.nix
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
let
|
||||
css = import ../core/css.nix { pkgs = import <nixpkgs> {}; };
|
||||
in
|
||||
|
||||
{
|
||||
css = builtins.concatStringsSep "\n" [
|
||||
# CSS Variables
|
||||
":root {
|
||||
--terminal-black: #000000;
|
||||
--terminal-green: #00ff00;
|
||||
--background-dark: #0a0a0a;
|
||||
--space-blue: #001122;
|
||||
--text-gray: #cccccc;
|
||||
--accent-orange: #ff6600;
|
||||
}"
|
||||
|
||||
# Base styles
|
||||
"* { margin: 0; padding: 0; box-sizing: border-box; }"
|
||||
|
||||
"body {
|
||||
font-family: 'Courier New', monospace;
|
||||
background: var(--background-dark);
|
||||
color: var(--text-gray);
|
||||
line-height: 1.6;
|
||||
}"
|
||||
|
||||
# Terminal theme
|
||||
".cyberpunk-theme {
|
||||
background: linear-gradient(45deg, var(--terminal-black), var(--background-dark));
|
||||
min-height: 100vh;
|
||||
}"
|
||||
|
||||
# Component styles
|
||||
".terminal-header {
|
||||
background: var(--terminal-black);
|
||||
color: var(--terminal-green);
|
||||
font-family: 'Courier New', monospace;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--terminal-green);
|
||||
}"
|
||||
|
||||
".hero-section {
|
||||
background: linear-gradient(135deg, var(--background-dark), var(--space-blue));
|
||||
padding: 4rem 2rem;
|
||||
text-align: center;
|
||||
min-height: 60vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}"
|
||||
|
||||
".hero-title {
|
||||
font-size: 3rem;
|
||||
color: var(--terminal-green);
|
||||
text-shadow: 0 0 10px var(--terminal-green);
|
||||
margin-bottom: 1rem;
|
||||
}"
|
||||
|
||||
".ascii-art {
|
||||
color: var(--terminal-green);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
margin: 2rem 0;
|
||||
}"
|
||||
|
||||
".events-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 2rem;
|
||||
}"
|
||||
|
||||
".event-card {
|
||||
background: var(--terminal-black);
|
||||
border: 1px solid var(--terminal-green);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}"
|
||||
|
||||
".event-card:hover {
|
||||
box-shadow: 0 0 15px var(--terminal-green);
|
||||
transform: translateY(-2px);
|
||||
}"
|
||||
|
||||
".terminal-footer {
|
||||
background: var(--terminal-black);
|
||||
color: var(--terminal-green);
|
||||
font-family: 'Courier New', monospace;
|
||||
padding: 2rem;
|
||||
border-top: 1px solid var(--terminal-green);
|
||||
text-align: center;
|
||||
}"
|
||||
|
||||
# Terminal animations
|
||||
"@keyframes blink {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}"
|
||||
|
||||
".prompt-symbol::after {
|
||||
content: '_';
|
||||
animation: blink 1s infinite;
|
||||
}"
|
||||
];
|
||||
}
|
||||
29
test-build.nix
Normal file
29
test-build.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Simple test to verify our Nix web development setup works
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
|
||||
# Import our modules
|
||||
html = import ./src/core/html.nix { inherit pkgs; };
|
||||
css = import ./src/core/css.nix { inherit pkgs; };
|
||||
components = import ./src/core/components.nix { inherit pkgs html css; };
|
||||
content = import ./src/data/content.nix;
|
||||
|
||||
# Test page generation
|
||||
testPage = import ./src/pages/index.nix { inherit components content; };
|
||||
testStyles = import ./src/styles/main.nix;
|
||||
|
||||
in {
|
||||
# Test outputs
|
||||
htmlOutput = testPage.html;
|
||||
cssOutput = testStyles.css;
|
||||
|
||||
# Verification
|
||||
isValid = builtins.isString testPage.html && builtins.isString testStyles.css;
|
||||
|
||||
# Component test
|
||||
componentTest = {
|
||||
headerExists = builtins.hasAttr "terminalHeader" components;
|
||||
heroExists = builtins.hasAttr "heroSection" components;
|
||||
footerExists = builtins.hasAttr "terminalFooter" components;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue