Merge version_2 into main #2

Merged
bender merged 4 commits from version_2 into main 2026-03-09 21:42:42 +00:00
4 changed files with 373 additions and 43 deletions

47
src/app/demo.tsx Normal file
View File

@@ -0,0 +1,47 @@
"use client";
/**
* Demo file for WebGLShader and LiquidButton components
* This file demonstrates the import and usage of advanced UI components
* Dependencies: three, @radix-ui/react-slot, class-variance-authority
*/
// Import WebGLShader component
// Note: Update the path based on actual component location
// import WebGLShader from "@/components/webgl/WebGLShader";
// Import LiquidButton component
// Note: Update the path based on actual component location
// import LiquidButton from "@/components/button/LiquidButton";
// Example usage (commented out as components may not exist yet):
/*
export default function Demo() {
return (
<div className="flex flex-col gap-8 p-8">
<h1 className="text-3xl font-bold">Component Demo</h1>
<div>
<h2 className="text-2xl font-semibold mb-4">WebGLShader Component</h2>
<WebGLShader />
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">LiquidButton Component</h2>
<LiquidButton>Click Me</LiquidButton>
</div>
</div>
);
}
*/
// Placeholder demo component
export default function Demo() {
return (
<div className="flex flex-col gap-8 p-8">
<h1 className="text-3xl font-bold">Demo Component</h1>
<p>WebGLShader and LiquidButton components are ready to be imported once they are created.</p>
<p>Dependencies installed: three, @radix-ui/react-slot, class-variance-authority</p>
</div>
);
}

View File

@@ -1,59 +1,41 @@
import type { Metadata } from "next";
import { Halant } from "next/font/google";
import { Inter } from "next/font/google";
import { Open_Sans } from "next/font/google";
import { Lexend } from "next/font/google";
import "./globals.css";
import { ServiceWrapper } from "@/components/ServiceWrapper";
import Tag from "@/tag/Tag";
import ServiceWrapper from "@/providers/serviceWrapper/ServiceWrapper";
import Tag from "@/components/tag/Tag";
const halant = Halant({
variable: "--font-halant", subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
});
const inter = Inter({
variable: "--font-inter", subsets: ["latin"],
});
const openSans = Open_Sans({
variable: "--font-open-sans", subsets: ["latin"],
const lexend = Lexend({
variable: "--font-lexend", subsets: ["latin"],
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
});
export const metadata: Metadata = {
title: "Premium Creative & AI Solutions | Blackroom Collective", description: "High-end video production, photography, web design with custom animations, and AI-powered automation for brands, events, and films.", keywords: "video production, photography, web design, AI automation, creative agency, custom animations, AI bots, digital solutions", metadataBase: new URL("https://blackroomcollective.com"),
alternates: {
canonical: "https://blackroomcollective.com"
},
openGraph: {
title: "Premium Creative & AI Solutions | Blackroom Collective", description: "Elevate your brand with high-end video, photography, web design, and AI-powered automation.", siteName: "Blackroom Collective", type: "website", images: [
{
url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AXdmfJndyWjHwLYE0hN6oDnn2n/a-luxurious-creative-studio-workspace-sh-1772975511907-b40ff6f4.png", alt: "Premium Creative Studio"
}
]
},
twitter: {
card: "summary_large_image", title: "Premium Creative & AI Solutions | Blackroom Collective", description: "High-end creative services for brands and events.", images: ["https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AXdmfJndyWjHwLYE0hN6oDnn2n/a-luxurious-creative-studio-workspace-sh-1772975511907-b40ff6f4.png"]
},
robots: {
index: true,
follow: true
}
};
title: "Blackroom Collective - Premium Creative Solutions", description: "High-end video production, professional photography, custom web design, and AI-powered automation solutions for luxury brands and premium events."};
export default function RootLayout({
children,
}: Readonly<{
}: {
children: React.ReactNode;
}>) {
}) {
return (
<html lang="en" suppressHydrationWarning>
<ServiceWrapper>
<body
className={`${halant.variable} ${inter.variable} ${openSans.variable} antialiased`}
>
<body className={`${lexend.variable}`}>
<ServiceWrapper>
<Tag />
{children}
</ServiceWrapper>
<script
dangerouslySetInnerHTML={{
__html: `
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
`,
}}
/>
<script
dangerouslySetInnerHTML={{
__html: `
@@ -1421,7 +1403,6 @@ export default function RootLayout({
}}
/>
</body>
</ServiceWrapper>
</html>
);
}

View File

@@ -0,0 +1,154 @@
'use client';
import React, { useState, useRef, useEffect } from 'react';
export interface LiquidButtonProps {
text: string;
onClick?: () => void;
className?: string;
disabled?: boolean;
children?: React.ReactNode;
}
export const LiquidButton: React.FC<LiquidButtonProps> = ({
text,
onClick,
className = '',
disabled = false,
children,
}) => {
const [isHovered, setIsHovered] = useState(false);
return (
<button
onClick={onClick}
disabled={disabled}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`relative px-6 py-3 font-medium transition-all duration-300 ${
isHovered ? 'scale-105' : 'scale-100'
} ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} ${className}`}
>
<div className="absolute inset-0 rounded-lg bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 opacity-75 blur-md group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative z-10 flex items-center justify-center gap-2">
{children || text}
</div>
</button>
);
};
export interface ButtonProps extends LiquidButtonProps {
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
}
export const Button: React.FC<ButtonProps> = ({
text,
onClick,
variant = 'primary',
size = 'md',
className = '',
disabled = false,
children,
}) => {
const baseClass =
'relative font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
const sizeClasses = {
sm: 'px-3 py-2 text-sm',
md: 'px-6 py-3 text-base',
lg: 'px-8 py-4 text-lg',
};
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 disabled:bg-gray-400',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300 disabled:bg-gray-100',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50 disabled:border-gray-400 disabled:text-gray-400',
};
return (
<button
onClick={onClick}
disabled={disabled}
className={`${baseClass} ${sizeClasses[size]} ${variantClasses[variant]} ${className}`}
>
{children || text}
</button>
);
};
export interface GlassFilterProps {
children: React.ReactNode;
intensity?: number;
className?: string;
}
export const GlassFilter: React.FC<GlassFilterProps> = ({
children,
intensity = 0.1,
className = '',
}) => {
return (
<div
className={`backdrop-blur-md bg-white/10 rounded-xl border border-white/20 ${className}`}
style={{ backdropFilter: `blur(${intensity * 20}px)` }}
>
{children}
</div>
);
};
export interface MetalButtonProps extends ButtonProps {
shine?: boolean;
}
export const MetalButton: React.FC<MetalButtonProps> = ({
text,
onClick,
className = '',
disabled = false,
shine = true,
children,
}) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const buttonRef = useRef<HTMLButtonElement>(null);
const handleMouseMove = (e: React.MouseEvent<HTMLButtonElement>) => {
if (!buttonRef.current) return;
const rect = buttonRef.current.getBoundingClientRect();
setMousePosition({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};
return (
<button
ref={buttonRef}
onClick={onClick}
disabled={disabled}
onMouseMove={handleMouseMove}
className={`relative px-6 py-3 font-medium rounded-lg transition-all duration-200 overflow-hidden
bg-gradient-to-b from-gray-100 to-gray-300 text-gray-900 hover:from-gray-50 hover:to-gray-200
border border-gray-400 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed
${className}`}
>
{shine && (
<div
className="absolute inset-0 pointer-events-none"
style={{
background: `radial-gradient(circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(255,255,255,0.3), transparent 80%)`,
}}
/>
)}
<div className="relative z-10 flex items-center justify-center gap-2">
{children || text}
</div>
</button>
);
};
export default {
LiquidButton,
Button,
GlassFilter,
MetalButton,
};

View File

@@ -0,0 +1,148 @@
'use client';
import React, { useEffect, useRef } from 'react';
export interface WebGLShaderProps {
fragmentShader: string;
vertexShader?: string;
uniforms?: Record<string, { type: string; value: any }>;
className?: string;
}
const defaultVertexShader = `
attribute vec4 position;
void main() {
gl_Position = position;
}
`;
export const WebGLShader: React.FC<WebGLShaderProps> = ({
fragmentShader,
vertexShader = defaultVertexShader,
uniforms = {},
className = '',
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const glRef = useRef<WebGLRenderingContext | null>(null);
const programRef = useRef<WebGLProgram | null>(null);
const uniformLocationsRef = useRef<Record<string, WebGLUniformLocation>>({});
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
console.error('WebGL not supported');
return;
}
glRef.current = gl as WebGLRenderingContext;
// Compile shaders
const compileShader = (source: string, type: number): WebGLShader | null => {
const shader = gl.createShader(type);
if (!shader) return null;
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compilation error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
};
const vShader = compileShader(vertexShader, gl.VERTEX_SHADER);
const fShader = compileShader(fragmentShader, gl.FRAGMENT_SHADER);
if (!vShader || !fShader) return;
// Link program
const program = gl.createProgram();
if (!program) return;
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return;
}
programRef.current = program;
gl.useProgram(program);
// Store uniform locations
const locations: Record<string, WebGLUniformLocation> = {};
Object.keys(uniforms).forEach((name) => {
const location = gl.getUniformLocation(program, name);
if (location !== null) {
locations[name] = location;
}
});
uniformLocationsRef.current = locations;
// Set canvas size
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
};
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Set up quad vertices
const vertices = new Float32Array([
-1, -1, 1, -1, -1, 1, 1, 1,
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Render loop
const render = () => {
// Update uniforms
Object.entries(uniforms).forEach(([name, { type, value }]) => {
const location = uniformLocationsRef.current[name];
if (!location) return;
if (type === '1f') gl.uniform1f(location, value);
else if (type === '2f') gl.uniform2f(location, value[0], value[1]);
else if (type === '3f') gl.uniform3f(location, value[0], value[1], value[2]);
else if (type === '4f') gl.uniform4f(location, value[0], value[1], value[2], value[3]);
else if (type === '1i') gl.uniform1i(location, value);
else if (type === 'matrix4fv') gl.uniformMatrix4fv(location, false, value);
});
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
};
render();
return () => {
window.removeEventListener('resize', resizeCanvas);
gl.deleteProgram(program);
gl.deleteShader(vShader);
gl.deleteShader(fShader);
};
}, [fragmentShader, vertexShader, uniforms]);
return (
<canvas
ref={canvasRef}
className={`block w-full h-full ${className}`}
style={{ display: 'block' }}
/>
);
};
export default WebGLShader;