From 3ef5d4e476d6569e10df2c9e5769220d273e6fed Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 16:54:56 -0500 Subject: [PATCH 1/6] chore: add docker configuration --- .dockerignore | 9 ++ Dockerfile | 15 ++++ README.md | 10 +++ app/SemverVisualizerModern.tsx | 116 +++++++++++++++++--------- app/SemverVisualizerModern_backup.tsx | 1 - app/hooks/useStateManagement.ts | 16 ++-- docker-compose.yml | 8 ++ package-lock.json | 48 ----------- package.json | 2 - 9 files changed, 130 insertions(+), 95 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 app/SemverVisualizerModern_backup.tsx create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b073ba2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +npm-debug.log +Dockerfile +.dockerignore +docker-compose.yml +.git +.gitignore +.next +out diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ce4e804 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# Build Next.js app and run in production mode +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build +RUN npm prune --omit=dev + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app . +EXPOSE 3000 +CMD ["npm", "start"] diff --git a/README.md b/README.md index 32ca97f..75d8f59 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,16 @@ npm run dev Open [http://localhost:3000](http://localhost:3000) to see the app. +### Docker + +Build and run the app in a container: + +```bash +docker compose up --build +``` + +Visit once the server starts. + ## Deployment ✅ **Currently deployed on Cloudflare Pages** at [semver.agenticinsights.com](https://semver.agenticinsights.com) diff --git a/app/SemverVisualizerModern.tsx b/app/SemverVisualizerModern.tsx index cf8e648..16c28fb 100644 --- a/app/SemverVisualizerModern.tsx +++ b/app/SemverVisualizerModern.tsx @@ -1,8 +1,8 @@ 'use client'; -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useReducer } from 'react'; import { motion } from 'framer-motion'; -import Confetti from 'react-confetti'; +import dynamic from 'next/dynamic'; import { Download, Moon, @@ -38,7 +38,12 @@ import { // Magic UI Components import { BorderBeam } from '@/components/magicui/border-beam'; -import { Particles } from '@/components/magicui/particles'; + +const Confetti = dynamic(() => import('react-confetti'), { ssr: false }); +const Particles = dynamic( + () => import('@/components/magicui/particles').then((m) => m.Particles), + { ssr: false } +); // Local Components import { VersionDisplay } from './components/VersionDisplay'; @@ -64,9 +69,37 @@ import { getRandomElement } from './types'; +interface VersionState { + current: Version; + next: Version; + pending: PendingChanges; +} + +type VersionAction = + | { type: 'setCurrent'; value: Version } + | { type: 'setNext'; value: Version } + | { type: 'setPending'; value: PendingChanges }; + +const versionReducer = (state: VersionState, action: VersionAction): VersionState => { + switch (action.type) { + case 'setCurrent': + return { ...state, current: action.value }; + case 'setNext': + return { ...state, next: action.value }; + case 'setPending': + return { ...state, pending: action.value }; + default: + return state; + } +}; + const SemverVisualizerModern: React.FC = () => { - const [currentVersion, setCurrentVersion] = useState({ major: 0, minor: 1, patch: 0 }); - const [nextVersion, setNextVersion] = useState({ major: 0, minor: 1, patch: 0 }); + const [versionState, dispatchVersion] = useReducer(versionReducer, { + current: { major: 0, minor: 1, patch: 0 }, + next: { major: 0, minor: 1, patch: 0 }, + pending: { breaking: 0, feat: 0, fix: 0 }, + }); + const { current: currentVersion, next: nextVersion, pending: pendingChanges } = versionState; const [allCommits, setAllCommits] = useState([]); const [unreleasedCommits, setUnreleasedCommits] = useState([]); const [releases, setReleases] = useState([]); @@ -76,7 +109,6 @@ const SemverVisualizerModern: React.FC = () => { const [darkMode, setDarkMode] = useState(true); const [soundEnabled, setSoundEnabled] = useState(false); const [isReleasing, setIsReleasing] = useState(false); - const [pendingChanges, setPendingChanges] = useState({ breaking: 0, feat: 0, fix: 0 }); const [celebrateVersion, setCelebrateVersion] = useState<'major' | 'minor' | 'patch' | null>(null); const [animateNextVersion, setAnimateNextVersion] = useState<'major' | 'minor' | 'patch' | null>(null); const [showConfetti, setShowConfetti] = useState(false); @@ -99,21 +131,26 @@ const SemverVisualizerModern: React.FC = () => { }; const calculateNextVersion = useCallback((commits: Commit[], current: Version): Version => { - let hasBreaking = false; - let hasFeat = false; - let hasFix = false; - - commits.forEach(commit => { - if (commit.type === 'breaking') hasBreaking = true; - else if (commit.type === 'feat') hasFeat = true; - else if (commit.type === 'fix') hasFix = true; - }); + let impact: 'breaking' | 'feat' | 'fix' | null = null; + for (const commit of commits) { + if (commit.type === 'breaking') { + impact = 'breaking'; + break; + } + if (commit.type === 'feat' && impact !== 'feat') { + impact = 'feat'; + } else if (!impact && commit.type === 'fix') { + impact = 'fix'; + } + } - if (hasBreaking) { + if (impact === 'breaking') { return { major: current.major + 1, minor: 0, patch: 0 }; - } else if (hasFeat) { + } + if (impact === 'feat') { return { major: current.major, minor: current.minor + 1, patch: 0 }; - } else if (hasFix) { + } + if (impact === 'fix') { return { major: current.major, minor: current.minor, patch: current.patch + 1 }; } return current; @@ -137,11 +174,11 @@ const SemverVisualizerModern: React.FC = () => { loadState( calculateNextVersion, currentVersion, - setCurrentVersion, + v => dispatchVersion({ type: 'setCurrent', value: v }), setAllCommits, setUnreleasedCommits, - setNextVersion, - setPendingChanges, + v => dispatchVersion({ type: 'setNext', value: v }), + changes => dispatchVersion({ type: 'setPending', value: changes }), setReleases, setDarkMode, setSoundEnabled, @@ -152,12 +189,12 @@ const SemverVisualizerModern: React.FC = () => { const handleClearData = useCallback(() => { clearData( - setCurrentVersion, - setNextVersion, + v => dispatchVersion({ type: 'setCurrent', value: v }), + v => dispatchVersion({ type: 'setNext', value: v }), setAllCommits, setUnreleasedCommits, setReleases, - setPendingChanges + changes => dispatchVersion({ type: 'setPending', value: changes }) ); }, [clearData]); @@ -167,11 +204,11 @@ const SemverVisualizerModern: React.FC = () => { const handleImportData = useCallback(() => { importData( - setCurrentVersion, + v => dispatchVersion({ type: 'setCurrent', value: v }), setAllCommits, setReleases, setUnreleasedCommits, - setPendingChanges + changes => dispatchVersion({ type: 'setPending', value: changes }) ); }, [importData]); @@ -192,16 +229,19 @@ const SemverVisualizerModern: React.FC = () => { setUnreleasedCommits(prev => { const updated = [newCommit, ...prev]; const newNext = calculateNextVersion(updated, currentVersion); - setNextVersion(newNext); - - const changes = { breaking: 0, feat: 0, fix: 0 }; - updated.forEach(c => { - if (c.type === 'breaking') changes.breaking++; - else if (c.type === 'feat') changes.feat++; - else if (c.type === 'fix') changes.fix++; - }); - setPendingChanges(changes); - + dispatchVersion({ type: 'setNext', value: newNext }); + + const changes = updated.reduce( + (acc, c) => { + if (c.type === 'breaking') acc.breaking++; + else if (c.type === 'feat') acc.feat++; + else if (c.type === 'fix') acc.fix++; + return acc; + }, + { breaking: 0, feat: 0, fix: 0 } + ); + dispatchVersion({ type: 'setPending', value: changes }); + return updated; }); @@ -255,9 +295,9 @@ const SemverVisualizerModern: React.FC = () => { setCelebrateVersion(versionType); setTimeout(() => setCelebrateVersion(null), 2000); - setCurrentVersion(nextVersion); + dispatchVersion({ type: 'setCurrent', value: nextVersion }); setUnreleasedCommits([]); - setPendingChanges({ breaking: 0, feat: 0, fix: 0 }); + dispatchVersion({ type: 'setPending', value: { breaking: 0, feat: 0, fix: 0 } }); setIsReleasing(false); setShowConfetti(true); diff --git a/app/SemverVisualizerModern_backup.tsx b/app/SemverVisualizerModern_backup.tsx deleted file mode 100644 index 6942d59..0000000 --- a/app/SemverVisualizerModern_backup.tsx +++ /dev/null @@ -1 +0,0 @@ -// Backup of original file \ No newline at end of file diff --git a/app/hooks/useStateManagement.ts b/app/hooks/useStateManagement.ts index f257d0b..38e2894 100644 --- a/app/hooks/useStateManagement.ts +++ b/app/hooks/useStateManagement.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useCallback } from 'react'; import { Version, Commit, Release, STORAGE_KEY } from '../types'; @@ -89,12 +90,15 @@ export const useStateManagement = () => { const newNext = calculateNextVersion(commits, state.currentVersion || currentVersion); setNextVersion(newNext); - const changes = { breaking: 0, feat: 0, fix: 0 }; - commits.forEach((c: Commit) => { - if (c.type === 'breaking') changes.breaking++; - else if (c.type === 'feat') changes.feat++; - else if (c.type === 'fix') changes.fix++; - }); + const changes = commits.reduce( + (acc, c: Commit) => { + if (c.type === 'breaking') acc.breaking++; + else if (c.type === 'feat') acc.feat++; + else if (c.type === 'fix') acc.fix++; + return acc; + }, + { breaking: 0, feat: 0, fix: 0 } + ); setPendingChanges(changes); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..47ce844 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.9' +services: + app: + build: . + ports: + - '3000:3000' + environment: + - NODE_ENV=production diff --git a/package-lock.json b/package-lock.json index d5c223a..3e2813b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,10 @@ "clsx": "^2.1.1", "framer-motion": "^12.23.12", "lucide-react": "^0.539.0", - "motion": "^12.23.12", "next": "15.4.6", "react": "19.1.0", "react-confetti": "^6.4.0", "react-dom": "19.1.0", - "react-tooltip": "^5.29.1", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7" }, @@ -5571,12 +5569,6 @@ "url": "https://polar.sh/cva" } }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -8945,32 +8937,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/motion": { - "version": "12.23.12", - "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.12.tgz", - "integrity": "sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==", - "license": "MIT", - "dependencies": { - "framer-motion": "^12.23.12", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/motion-dom": { "version": "12.23.12", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz", @@ -9863,20 +9829,6 @@ } } }, - "node_modules/react-tooltip": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.29.1.tgz", - "integrity": "sha512-rmJmEb/p99xWhwmVT7F7riLG08wwKykjHiMGbDPloNJk3tdI73oHsVOwzZ4SRjqMdd5/xwb/4nmz0RcoMfY7Bw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.6.1", - "classnames": "^2.3.0" - }, - "peerDependencies": { - "react": ">=16.14.0", - "react-dom": ">=16.14.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", diff --git a/package.json b/package.json index 8db8281..c7b3132 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,10 @@ "clsx": "^2.1.1", "framer-motion": "^12.23.12", "lucide-react": "^0.539.0", - "motion": "^12.23.12", "next": "15.4.6", "react": "19.1.0", "react-confetti": "^6.4.0", "react-dom": "19.1.0", - "react-tooltip": "^5.29.1", "tailwind-merge": "^3.3.1", "tailwindcss-animate": "^1.0.7" }, From 2dfa3f8728f4d75136720d80857f6c582fc91a6b Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 17:08:37 -0500 Subject: [PATCH 2/6] fix: replace motion/react with framer-motion imports --- components/magicui/animated-beam.tsx | 6 ++---- components/magicui/border-beam.tsx | 2 +- components/magicui/magic-card.tsx | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/components/magicui/animated-beam.tsx b/components/magicui/animated-beam.tsx index 4dd04a7..23d45e3 100644 --- a/components/magicui/animated-beam.tsx +++ b/components/magicui/animated-beam.tsx @@ -1,6 +1,6 @@ "use client"; -import { motion } from "motion/react"; +import { motion } from "framer-motion"; import { RefObject, useEffect, useId, useState } from "react"; import { cn } from "@/lib/utils"; @@ -94,9 +94,7 @@ export const AnimatedBeam: React.FC = ({ // Initialize ResizeObserver const resizeObserver = new ResizeObserver((entries) => { // For all entries, recalculate the path - for (let entry of entries) { - updatePath(); - } + entries.forEach(() => updatePath()); }); // Observe the container element diff --git a/components/magicui/border-beam.tsx b/components/magicui/border-beam.tsx index 842c32b..8973485 100644 --- a/components/magicui/border-beam.tsx +++ b/components/magicui/border-beam.tsx @@ -1,7 +1,7 @@ "use client"; import { cn } from "@/lib/utils"; -import { motion, MotionStyle, Transition } from "motion/react"; +import { motion, MotionStyle, Transition } from "framer-motion"; interface BorderBeamProps { /** diff --git a/components/magicui/magic-card.tsx b/components/magicui/magic-card.tsx index 58b71ef..a8d4a1a 100644 --- a/components/magicui/magic-card.tsx +++ b/components/magicui/magic-card.tsx @@ -1,6 +1,6 @@ "use client"; -import { motion, useMotionTemplate, useMotionValue } from "motion/react"; +import { motion, useMotionTemplate, useMotionValue } from "framer-motion"; import React, { useCallback, useEffect, useRef } from "react"; import { cn } from "@/lib/utils"; From d602e90e984ad1b364cc4994310e9b8a7fec2b78 Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 17:20:12 -0500 Subject: [PATCH 3/6] Fix hydration mismatch from server-side time --- app/IDELayout.tsx | 16 +++++++++++----- app/SemverVisualizerModern.tsx | 2 +- app/components/ControlPanel.tsx | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/IDELayout.tsx b/app/IDELayout.tsx index 32a2862..23106e4 100644 --- a/app/IDELayout.tsx +++ b/app/IDELayout.tsx @@ -39,13 +39,19 @@ export default function IDELayout({ children }: IDELayoutProps) { const [terminalOpen, setTerminalOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false); - const [currentTime, setCurrentTime] = useState(new Date()); + const [currentTime, setCurrentTime] = useState(""); useEffect(() => { - const timer = setInterval(() => { - setCurrentTime(new Date()); - }, 1000); + const updateTime = () => + setCurrentTime( + new Date().toLocaleTimeString("en-US", { + hour: "2-digit", + minute: "2-digit", + }) + ); + updateTime(); + const timer = setInterval(updateTime, 1000); return () => clearInterval(timer); }, []); @@ -216,7 +222,7 @@ export default function IDELayout({ children }: IDELayoutProps) { main ● Port 30020 - {currentTime.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })} + {currentTime || '--:--'} )} diff --git a/app/SemverVisualizerModern.tsx b/app/SemverVisualizerModern.tsx index 16c28fb..d948cbb 100644 --- a/app/SemverVisualizerModern.tsx +++ b/app/SemverVisualizerModern.tsx @@ -352,7 +352,7 @@ const SemverVisualizerModern: React.FC = () => { return ( -
+
{/* Background Particles */} = ({
{/* Commit Type Buttons */} -
+
{Object.entries(commitTypeConfig).slice(0, 4).map(([type, config]) => { const Icon = config.icon; return ( From 4931e42f6c4c2734e2967e25e1326d1986237404 Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 17:28:20 -0500 Subject: [PATCH 4/6] Reset dropdown menu state after actions --- app/SemverVisualizerModern.tsx | 47 ++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/app/SemverVisualizerModern.tsx b/app/SemverVisualizerModern.tsx index d948cbb..595489f 100644 --- a/app/SemverVisualizerModern.tsx +++ b/app/SemverVisualizerModern.tsx @@ -115,6 +115,7 @@ const SemverVisualizerModern: React.FC = () => { const [showRoadmap, setShowRoadmap] = useState(false); const [dataLoaded, setDataLoaded] = useState(false); const [isSaving, setIsSaving] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); // Custom hooks const { playSound } = useAudio(soundEnabled); @@ -212,6 +213,14 @@ const SemverVisualizerModern: React.FC = () => { ); }, [importData]); + const handleMenuSelect = useCallback( + (action: () => void) => () => { + setMenuOpen(false); + action(); + }, + [] + ); + const addCommit = useCallback((type: CommitType) => { const versionBefore = currentVersionString; const newCommit: Commit = { @@ -412,54 +421,54 @@ const SemverVisualizerModern: React.FC = () => { )}
- + - setShowEducation(true)}> + setShowEducation(true))}> Learn Semantic Versioning - - setShowHistory(true)}> + + setShowHistory(true))}> Release History {releases.length > 0 && `(${releases.length})`} - - setShowRoadmap(true)}> + + setShowRoadmap(true))}> View Roadmap - + - - setDarkMode(!darkMode)}> + + setDarkMode(!darkMode))}> {darkMode ? : } {darkMode ? 'Light' : 'Dark'} Mode - - setSoundEnabled(!soundEnabled)}> + + setSoundEnabled(!soundEnabled))}> {soundEnabled ? : } {soundEnabled ? 'Disable' : 'Enable'} Sound - + - - + + Export Data - - + + Import Data - - From 066db45510d6f8ee83d91f00e753efa79d1cac8a Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 17:52:12 -0500 Subject: [PATCH 5/6] Remove dark mode support and related styling --- app/SemverVisualizerModern.tsx | 29 ++------------ app/components/RoadmapModal.tsx | 3 +- app/globals.css | 57 --------------------------- app/hooks/useStateManagement.ts | 4 -- app/types.ts | 14 +++---- components/magicui/shimmer-button.tsx | 2 +- components/ui/button.tsx | 8 ++-- tailwind.config.js | 3 +- 8 files changed, 18 insertions(+), 102 deletions(-) diff --git a/app/SemverVisualizerModern.tsx b/app/SemverVisualizerModern.tsx index 595489f..4dde761 100644 --- a/app/SemverVisualizerModern.tsx +++ b/app/SemverVisualizerModern.tsx @@ -3,10 +3,8 @@ import React, { useState, useEffect, useCallback, useReducer } from 'react'; import { motion } from 'framer-motion'; import dynamic from 'next/dynamic'; -import { +import { Download, - Moon, - Sun, Volume2, VolumeX, GitBranch, @@ -106,7 +104,6 @@ const SemverVisualizerModern: React.FC = () => { const [animationSpeed, setAnimationSpeed] = useState<'paused' | 'slow' | 'normal' | 'fast'>('normal'); const [showEducation, setShowEducation] = useState(false); const [showHistory, setShowHistory] = useState(false); - const [darkMode, setDarkMode] = useState(true); const [soundEnabled, setSoundEnabled] = useState(false); const [isReleasing, setIsReleasing] = useState(false); const [celebrateVersion, setCelebrateVersion] = useState<'major' | 'minor' | 'patch' | null>(null); @@ -164,12 +161,11 @@ const SemverVisualizerModern: React.FC = () => { allCommits, unreleasedCommits, releases, - darkMode, soundEnabled, animationSpeed, setIsSaving ); - }, [saveState, currentVersion, allCommits, unreleasedCommits, releases, darkMode, soundEnabled, animationSpeed]); + }, [saveState, currentVersion, allCommits, unreleasedCommits, releases, soundEnabled, animationSpeed]); const handleLoadState = useCallback(() => { loadState( @@ -181,7 +177,6 @@ const SemverVisualizerModern: React.FC = () => { v => dispatchVersion({ type: 'setNext', value: v }), changes => dispatchVersion({ type: 'setPending', value: changes }), setReleases, - setDarkMode, setSoundEnabled, setAnimationSpeed, setDataLoaded @@ -331,7 +326,7 @@ const SemverVisualizerModern: React.FC = () => { handleSaveState(); }, 1000); return () => clearTimeout(timer); - }, [currentVersion, allCommits, unreleasedCommits, releases, darkMode, soundEnabled, animationSpeed, dataLoaded]); // Removed handleSaveState to prevent infinite loop + }, [currentVersion, allCommits, unreleasedCommits, releases, soundEnabled, animationSpeed, dataLoaded]); // Removed handleSaveState to prevent infinite loop // Auto-generate commits useEffect(() => { @@ -350,15 +345,6 @@ const SemverVisualizerModern: React.FC = () => { return () => clearInterval(interval); }, [animationSpeed]); // Removed addCommit to prevent infinite loop - useEffect(() => { - const root = document.documentElement; - if (darkMode) { - root.classList.add('dark'); - } else { - root.classList.remove('dark'); - } - }, [darkMode]); - return (
@@ -367,7 +353,7 @@ const SemverVisualizerModern: React.FC = () => { className="absolute inset-0" quantity={50} ease={80} - color={darkMode ? "#ffffff" : "#000000"} + color="#ffffff" refresh /> @@ -443,13 +429,6 @@ const SemverVisualizerModern: React.FC = () => { View Roadmap - - - setDarkMode(!darkMode))}> - {darkMode ? : } - {darkMode ? 'Light' : 'Dark'} Mode - - setSoundEnabled(!soundEnabled))}> {soundEnabled ? : } {soundEnabled ? 'Disable' : 'Enable'} Sound diff --git a/app/components/RoadmapModal.tsx b/app/components/RoadmapModal.tsx index dc57045..5167224 100644 --- a/app/components/RoadmapModal.tsx +++ b/app/components/RoadmapModal.tsx @@ -18,7 +18,6 @@ export const RoadmapModal: React.FC = ({ isOpen, onClose }) = 'Real-time version calculation following semver rules', 'Animated commit stream with smooth transitions', 'Sound effects for different commit types', - 'Dark/Light theme toggle', 'Release history tracking', 'Educational tooltips and guide', 'Magic UI/shadcn component integration', @@ -48,7 +47,7 @@ export const RoadmapModal: React.FC = ({ isOpen, onClose }) = Feature Roadmap - Track our progress and see what's coming next + Track our progress and see what's coming next diff --git a/app/globals.css b/app/globals.css index c81bfda..2ec3657 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,7 +1,5 @@ @import "tailwindcss"; -@custom-variant dark (&:is(.dark *)); - @layer base { :root { --background: 0 0% 100%; @@ -26,27 +24,6 @@ --radius: 0.5rem; } - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9%; - } } @layer base { @@ -152,40 +129,6 @@ --sidebar-ring: oklch(0.708 0 0); } -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - @layer base { * { @apply border-border outline-ring/50; diff --git a/app/hooks/useStateManagement.ts b/app/hooks/useStateManagement.ts index 38e2894..466f9e4 100644 --- a/app/hooks/useStateManagement.ts +++ b/app/hooks/useStateManagement.ts @@ -9,7 +9,6 @@ export const useStateManagement = () => { allCommits: Commit[], unreleasedCommits: Commit[], releases: Release[], - darkMode: boolean, soundEnabled: boolean, animationSpeed: string, setIsSaving: (saving: boolean) => void @@ -34,7 +33,6 @@ export const useStateManagement = () => { timestamp: c.timestamp.toISOString() })) })), - darkMode, soundEnabled, animationSpeed }; @@ -56,7 +54,6 @@ export const useStateManagement = () => { setNextVersion: (version: Version) => void, setPendingChanges: (changes: any) => void, setReleases: (releases: Release[]) => void, - setDarkMode: (dark: boolean) => void, setSoundEnabled: (enabled: boolean) => void, setAnimationSpeed: (speed: any) => void, setDataLoaded: (loaded: boolean) => void @@ -115,7 +112,6 @@ export const useStateManagement = () => { } // Restore settings - if (state.darkMode !== undefined) setDarkMode(state.darkMode); if (state.soundEnabled !== undefined) setSoundEnabled(state.soundEnabled); if (state.animationSpeed) setAnimationSpeed(state.animationSpeed); diff --git a/app/types.ts b/app/types.ts index 0fc319e..2bfde57 100644 --- a/app/types.ts +++ b/app/types.ts @@ -1,10 +1,9 @@ -import { - GitCommit, - Zap, - Bug, - FileText, - Palette, - Wrench, +import { + Zap, + Bug, + FileText, + Palette, + Wrench, TestTube, Package, Sparkles @@ -54,7 +53,6 @@ export const commitMessages = { ], feat: [ 'add user dashboard', - 'implement dark mode toggle', 'add export to PDF functionality', 'introduce real-time notifications', 'add multi-language support' diff --git a/components/magicui/shimmer-button.tsx b/components/magicui/shimmer-button.tsx index a8f1cc5..48ea624 100644 --- a/components/magicui/shimmer-button.tsx +++ b/components/magicui/shimmer-button.tsx @@ -39,7 +39,7 @@ const ShimmerButton = React.forwardRef( } as CSSProperties } className={cn( - "group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap border border-white/10 px-6 py-3 text-white [background:var(--bg)] [border-radius:var(--radius)] dark:text-black", + "group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap border border-white/10 px-6 py-3 text-white [background:var(--bg)] [border-radius:var(--radius)]", "transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px", className )} diff --git a/components/ui/button.tsx b/components/ui/button.tsx index a2df8dc..bbebfe9 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -5,20 +5,20 @@ import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20", outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { diff --git a/tailwind.config.js b/tailwind.config.js index 7cb7e37..5df3dc2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - darkMode: ["class"], + darkMode: false, content: [ './pages/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', @@ -73,5 +73,6 @@ module.exports = { }, }, }, + // eslint-disable-next-line @typescript-eslint/no-require-imports plugins: [require("tailwindcss-animate")], } \ No newline at end of file From 8fa72062c46838aeafe877cea6153659df74d3c7 Mon Sep 17 00:00:00 2001 From: Vaskin Kissoyan Date: Sun, 31 Aug 2025 18:04:56 -0500 Subject: [PATCH 6/6] Default to dark color variables --- app/globals.css | 96 ++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/app/globals.css b/app/globals.css index 2ec3657..334487c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,25 +2,25 @@ @layer base { :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; --radius: 0.5rem; } @@ -96,37 +96,37 @@ :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); + --background: oklch(0.205 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); } @layer base {