Introduce interactive distro matching with swipable cards and personalized recommendations

Refactors the UI to use a new DistroMatch component, replacing CardStack and DynamicBackground. Implements swipable cards for desktop environments and critical questions, calculating distro scores based on user preferences to provide personalized recommendations. Adds detailed distro data and roast messages for a dynamic results screen.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e522d728-b28a-437f-b576-83935afe7ea0
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 11cbc7d9-3397-474c-b460-1d052c247525
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/55e426a8-5a2b-421b-8bd4-30481ae0063b/e522d728-b28a-437f-b576-83935afe7ea0/38Ou2AY
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
yusufipk
2025-12-17 14:10:56 +00:00
parent 2d26d5d4f9
commit 8a036b2a00
8 changed files with 2283 additions and 15 deletions
+166
View File
@@ -0,0 +1,166 @@
import distrosData from '@/data/distros.json';
export interface ScoredDistro {
id: string;
name: string;
score: number;
colorPalette: string[];
tagline: string;
downloadUrl: string;
defaultDE: string;
pros: string[];
cons: string[];
matchReasons: string[];
}
export interface UserPreferences {
likedDEs: string[];
dislikedDEs: string[];
superLikedDEs: string[];
nvidia: boolean;
lowRam: boolean;
gaming: boolean;
privacy: boolean;
systemdHate: boolean;
beginner: boolean;
rolling: boolean;
oldHardware: boolean;
}
const DE_SCORES: Record<string, string[]> = {
kde: ['cachyos', 'endeavouros', 'manjaro', 'kubuntu', 'kdeneon', 'opensuse', 'fedora', 'nobara', 'bazzite', 'garuda', 'artix', 'devuan'],
plasma: ['cachyos', 'endeavouros', 'manjaro', 'kubuntu', 'kdeneon', 'opensuse', 'fedora', 'nobara', 'bazzite', 'garuda', 'artix', 'devuan'],
gnome: ['ubuntu', 'fedora', 'popos', 'zorin', 'debian', 'tails', 'endeavouros', 'manjaro'],
xfce: ['mxlinux', 'xubuntu', 'kali', 'qubes', 'endeavouros', 'manjaro', 'devuan'],
cinnamon: ['mint', 'endeavouros', 'manjaro', 'devuan'],
mate: ['mxlinux', 'mint', 'parrot', 'ubuntu', 'endeavouros'],
lxqt: ['lubuntu', 'endeavouros', 'artix', 'devuan'],
budgie: ['endeavouros', 'manjaro', 'cachyos'],
pantheon: ['elementary'],
cosmic: ['popos'],
'cosmic/gnome': ['popos'],
dde: ['deepin'],
deepin: ['deepin'],
i3: ['endeavouros', 'manjaro', 'garuda', 'arch', 'artix', 'void', 'cachyos', 'kali', 'qubes'],
sway: ['endeavouros', 'manjaro', 'garuda', 'arch', 'artix', 'void', 'cachyos', 'nixos', 'alpine'],
hyprland: ['cachyos', 'endeavouros', 'garuda', 'nixos', 'arch'],
'i3/sway': ['endeavouros', 'manjaro', 'garuda', 'arch', 'artix', 'void', 'cachyos'],
};
export function calculateDistroScores(preferences: UserPreferences): ScoredDistro[] {
const scores: Record<string, number> = {};
const matchReasons: Record<string, string[]> = {};
distrosData.distros.forEach(distro => {
scores[distro.id] = 0;
matchReasons[distro.id] = [];
});
preferences.superLikedDEs.forEach(de => {
const deKey = de.toLowerCase().replace(/\s+/g, '').replace('plasma', '');
const matchingDistros = DE_SCORES[deKey] || [];
matchingDistros.forEach(distroId => {
scores[distroId] = (scores[distroId] || 0) + 15;
if (!matchReasons[distroId]) matchReasons[distroId] = [];
matchReasons[distroId].push(`${de} masaüstü ortamını çok beğendin`);
});
});
preferences.likedDEs.forEach(de => {
const deKey = de.toLowerCase().replace(/\s+/g, '').replace('plasma', '');
const matchingDistros = DE_SCORES[deKey] || [];
matchingDistros.forEach(distroId => {
scores[distroId] = (scores[distroId] || 0) + 8;
if (!matchReasons[distroId]?.some(r => r.includes(de))) {
matchReasons[distroId]?.push(`${de} desteği mevcut`);
}
});
});
preferences.dislikedDEs.forEach(de => {
const deKey = de.toLowerCase().replace(/\s+/g, '').replace('plasma', '');
const matchingDistros = DE_SCORES[deKey] || [];
matchingDistros.forEach(distroId => {
scores[distroId] = (scores[distroId] || 0) - 5;
});
});
distrosData.criticalQuestions.forEach(question => {
const prefKey = question.id as keyof UserPreferences;
const userAnswer = preferences[prefKey];
if (userAnswer === true && question.impact.yes) {
question.impact.yes.boost?.forEach(distroId => {
scores[distroId] = (scores[distroId] || 0) + 10;
const reasonMap: Record<string, string> = {
nvidia: 'NVIDIA desteği mükemmel',
ram_low: 'Düşük RAM kullanımı',
gaming: 'Oyun için optimize edilmiş',
privacy: 'Gizlilik odaklı',
systemd_hate: 'Systemd kullanmıyor',
beginner: 'Yeni başlayanlar için uygun',
rolling: 'Sürekli güncel kalır',
old_hardware: 'Eski donanımda harika çalışır'
};
if (reasonMap[question.id] && !matchReasons[distroId]?.includes(reasonMap[question.id])) {
matchReasons[distroId]?.push(reasonMap[question.id]);
}
});
question.impact.yes.penalty?.forEach(distroId => {
scores[distroId] = (scores[distroId] || 0) - 8;
});
}
});
distrosData.distros.forEach(distro => {
if (preferences.lowRam && distro.minRAM > 2) {
scores[distro.id] -= 10;
}
if (preferences.nvidia && distro.nvidiaSupport === 'excellent') {
scores[distro.id] += 5;
} else if (preferences.nvidia && distro.nvidiaSupport === 'poor') {
scores[distro.id] -= 10;
}
if (preferences.beginner && distro.difficulty === 'beginner') {
scores[distro.id] += 5;
if (!matchReasons[distro.id]?.includes('Kolay kurulum ve kullanım')) {
matchReasons[distro.id]?.push('Kolay kurulum ve kullanım');
}
} else if (preferences.beginner && (distro.difficulty === 'advanced' || distro.difficulty === 'expert')) {
scores[distro.id] -= 15;
}
if (!preferences.beginner && (distro.difficulty === 'advanced' || distro.difficulty === 'expert')) {
scores[distro.id] += 3;
if (!matchReasons[distro.id]?.includes('İleri düzey kullanıcılar için')) {
matchReasons[distro.id]?.push('İleri düzey kullanıcılar için');
}
}
});
const sortedDistros = distrosData.distros
.map(distro => ({
id: distro.id,
name: distro.name,
score: scores[distro.id] || 0,
colorPalette: distro.colorPalette,
tagline: distro.tagline,
downloadUrl: distro.downloadUrl,
defaultDE: distro.defaultDE,
pros: distro.pros,
cons: distro.cons,
matchReasons: (matchReasons[distro.id] || []).slice(0, 3)
}))
.sort((a, b) => b.score - a.score);
return sortedDistros.slice(0, 3);
}
export function getTopDistros(preferences: UserPreferences): ScoredDistro[] {
return calculateDistroScores(preferences);
}