Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,6 @@ SOFTWARE.

Made with ❣️ for the computer science education community

© 2025 AlphsX, Inc.
© 2026 AlphsX, Inc.

</div>
158 changes: 87 additions & 71 deletions frontend/bun.lock

Large diffs are not rendered by default.

38 changes: 19 additions & 19 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,41 @@
"test:coverage": "bun test --coverage"
},
"dependencies": {
"@headlessui/react": "^2.2.9",
"@headlessui/react": "^2.2.10",
"@heroicons/react": "^2.2.0",
"@types/dom-speech-recognition": "^0.0.8",
"@vercel/analytics": "^2.0.1",
"@vercel/speed-insights": "^2.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.38.0",
"lucide-react": "^1.0.1",
"next": "^16.2.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"tailwind-merge": "^3.5.0",
"framer-motion": "^12.40.0",
"lucide-react": "^1.17.0",
"next": "^16.2.6",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"tailwind-merge": "^3.6.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/eslintrc": "~3.3.5",
"@eslint/js": "~9.21.0",
"@next/eslint-plugin-next": "15.1.7",
"@types/bun": "^1.3.11",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/bun": "^1.3.14",
"@types/node": "^25.9.1",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.57.2",
"@typescript-eslint/parser": "^8.57.2",
"autoprefixer": "^10.4.24",
"eslint": "^9.21.0",
"@typescript-eslint/eslint-plugin": "^8.60.0",
"@typescript-eslint/parser": "^8.60.0",
"autoprefixer": "^10.5.0",
"eslint": "^9.39.4",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"globals": "^17.4.0",
"postcss": "^8.5.6",
"eslint-plugin-react-hooks": "^7.1.1",
"globals": "^17.6.0",
"postcss": "^8.5.15",
"postcss-import": "^16.1.1",
"tailwindcss": "^3.4.19",
"typescript": "^5.7.3",
"typescript-eslint": "^8.57.2"
"typescript": "^5.9.3",
"typescript-eslint": "^8.60.0"
},
"overrides": {
"minimatch": "4.2.5",
Expand Down
40 changes: 40 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,46 @@ body {
-moz-osx-font-smoothing: grayscale;
}

/* Sidebar scroll - hidden by default, thin on hover */
.sidebar-scroll {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}

.sidebar-scroll::-webkit-scrollbar {
width: 0;
height: 0;
display: none; /* Chrome/Safari */
}

.sidebar-scroll:hover {
scrollbar-width: thin; /* Firefox */
scrollbar-color: rgba(0, 0, 0, 0.15) transparent;
}

.sidebar-scroll:hover::-webkit-scrollbar {
width: 3px;
height: 0;
display: block;
}

.sidebar-scroll:hover::-webkit-scrollbar-track {
background: transparent;
}

.sidebar-scroll:hover::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.15);
border-radius: 3px;
}

.dark .sidebar-scroll:hover {
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
}

.dark .sidebar-scroll:hover::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.15);
}

/* Custom scrollbar */
::-webkit-scrollbar {
width: 6px;
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export default function BankersAlgorithmPage() {

<div
ref={mainContainerRef}
className="swipe-container flex h-screen bg-gray-50 text-gray-900 dark:text-gray-50 transition-all duration-300"
className="swipe-container flex h-screen overflow-hidden bg-gray-50 text-gray-900 dark:text-gray-50 transition-all duration-300"
style={{backgroundColor: 'var(--page-bg, #f9fafb)'}}
>
{/* Mobile Sidebar Overlay */}
Expand All @@ -182,7 +182,7 @@ export default function BankersAlgorithmPage() {
}`}
>
<div
className="absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity duration-300 ease-out"
className="absolute inset-0 bg-black/50 transition-opacity duration-300 ease-out"
onClick={(e) => {
if (e.target === e.currentTarget) {
setIsSidebarOpen(false);
Expand Down Expand Up @@ -217,7 +217,7 @@ export default function BankersAlgorithmPage() {
</div>

{/* Mobile System Controls */}
<div className="flex-1 overflow-y-auto">
<div className="flex-1 overflow-y-auto overflow-x-hidden sidebar-scroll">
<SystemControls
processCount={algorithmState.processCount}
resourceCount={algorithmState.resourceCount}
Expand Down Expand Up @@ -320,7 +320,7 @@ export default function BankersAlgorithmPage() {
</div>

{/* Desktop System Controls */}
<div className="flex-1 overflow-y-auto">
<div className="flex-1 overflow-y-auto overflow-x-hidden sidebar-scroll">
<SystemControls
processCount={algorithmState.processCount}
resourceCount={algorithmState.resourceCount}
Expand Down Expand Up @@ -408,7 +408,7 @@ export default function BankersAlgorithmPage() {
</div>

{/* Main Content */}
<div className="flex-1 flex flex-col relative">
<div className="flex-1 flex flex-col relative min-w-0 overflow-hidden">
{/* Top Bar */}
<header
className={`absolute top-0 inset-x-0 z-20 transition-all duration-700 ease-in-out ${
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/bankers-algorithm/CountControl.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import React, {useRef, useCallback, useEffect} from 'react';
import {Plus, Minus} from 'lucide-react';

interface CountControlProps {
label: string;
Expand Down Expand Up @@ -187,12 +188,12 @@ export const CountControl: React.FC<CountControlProps> = ({
onMouseLeave={handleMouseLeave}
onTouchStart={handleMouseDown('decrement')}
onTouchEnd={handleMouseUp}
className="btn-control w-8 h-8 rounded-full flex items-center justify-center font-bold text-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed touch-manipulation hover:scale-105 hover:shadow-md"
className="btn-control w-8 h-8 rounded-full flex items-center justify-center transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed touch-manipulation hover:scale-105 hover:shadow-md"
disabled={disabled || count <= minValue}
title={`Decrease ${accessibleLabel} count (hold to repeat)`}
aria-label={`Decrease ${accessibleLabel} count`}
>
<Minus className="h-4 w-4" strokeWidth={2.5} />
</button>
<span
className="w-8 text-center text-lg font-semibold text-gray-900 dark:text-gray-100"
Expand All @@ -207,12 +208,12 @@ export const CountControl: React.FC<CountControlProps> = ({
onMouseLeave={handleMouseLeave}
onTouchStart={handleMouseDown('increment')}
onTouchEnd={handleMouseUp}
className="btn-control w-8 h-8 rounded-full flex items-center justify-center font-bold text-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed touch-manipulation hover:scale-105 hover:shadow-md"
className="btn-control w-8 h-8 rounded-full flex items-center justify-center transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed touch-manipulation hover:scale-105 hover:shadow-md"
disabled={disabled || count >= maxValue}
title={`Increase ${accessibleLabel} count (hold to repeat)`}
aria-label={`Increase ${accessibleLabel} count`}
>
+
<Plus className="h-4 w-4" strokeWidth={2.5} />
</button>
</div>
<div
Expand Down
24 changes: 6 additions & 18 deletions frontend/src/components/bankers-algorithm/RequestPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,33 +393,21 @@ export const RequestPanel: React.FC<RequestPanelProps> = ({
borderColor: 'var(--input-border, #e1e1e1)',
}}
>
<div className="max-h-64 overflow-y-auto py-1">
<div className="max-h-64 overflow-y-auto p-1.5 space-y-0.5">
{processOptions.map((option, index) => (
<button
key={option.value}
type="button"
onClick={() => handleProcessSelect(option.value)}
className="w-full px-5 py-3 text-left flex items-center justify-between transition-colors duration-150 animate-item-slide-in"
className={`w-full px-4 py-2.5 text-left flex items-center justify-between transition-all duration-150 animate-item-slide-in rounded-2xl ${
selectedProcess === option.value
? 'bg-[var(--button-hover-bg,#f3f4f6)] font-semibold'
: 'hover:bg-[var(--button-hover-bg,#f3f4f6)]'
}`}
style={{
backgroundColor:
selectedProcess === option.value
? 'var(--need-bg, #f9fafb)'
: 'transparent',
color: 'var(--foreground)',
animationDelay: `${index * 30}ms`,
}}
onMouseEnter={(e) => {
if (selectedProcess !== option.value) {
e.currentTarget.style.backgroundColor =
'var(--button-hover-bg, #f3f4f6)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor =
selectedProcess === option.value
? 'var(--need-bg, #f9fafb)'
: 'transparent';
}}
>
<span className="text-base font-medium">
{option.label}
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/ui/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const Toast: React.FC<ToastProps> = ({toast, onDismiss}) => {
let bgColor = '';
let iconColor = '';
let textColor = '';
let hoverBgColor = '';

switch (toast.type) {
case 'success':
Expand All @@ -55,27 +56,30 @@ const Toast: React.FC<ToastProps> = ({toast, onDismiss}) => {
'bg-green-50/40 dark:bg-green-900/20 border-green-200/50 dark:border-green-800';
iconColor = 'text-green-500 dark:text-green-400';
textColor = 'text-green-800 dark:text-green-200';
hoverBgColor = 'hover:bg-green-500/10 dark:hover:bg-green-400/10';
break;
case 'error':
Icon = ShieldAlert;
bgColor =
'bg-red-50/40 dark:bg-red-900/20 border-red-200/50 dark:border-red-800';
iconColor = 'text-red-500 dark:text-red-400';
textColor = 'text-red-800 dark:text-red-200';
hoverBgColor = 'hover:bg-red-500/10 dark:hover:bg-red-400/10';
break;
case 'info':
Icon = ShieldCheck;
bgColor =
'bg-purple-50/40 dark:bg-purple-900/20 border-purple-200/50 dark:border-purple-800';
iconColor = 'text-purple-500 dark:text-purple-400';
textColor = 'text-purple-800 dark:text-purple-200';
hoverBgColor = 'hover:bg-purple-500/10 dark:hover:bg-purple-400/10';
break;
}

return (
<div
className={`
relative flex items-start space-x-3 p-4 rounded-lg border shadow-lg backdrop-blur-sm
relative flex items-start space-x-3 p-4 rounded-2xl border shadow-lg backdrop-blur-sm
transition-all duration-300 ease-out
${bgColor}
${isVisible && !isExiting ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'}
Expand All @@ -91,7 +95,7 @@ const Toast: React.FC<ToastProps> = ({toast, onDismiss}) => {
</div>
<button
onClick={handleDismiss}
className={`flex-shrink-0 p-1 rounded-md hover:bg-black/5 dark:hover:bg-white/5 transition-colors ${textColor}`}
className={`flex-shrink-0 flex items-center justify-center h-7 w-7 rounded-full active:scale-95 transition-all duration-200 ${textColor} ${hoverBgColor}`}
>
<X className="h-4 w-4" />
</button>
Expand Down
Loading