diff --git a/.github/workflows/electron-build.yml b/.github/workflows/electron-build.yml index abe6845..40429c8 100644 --- a/.github/workflows/electron-build.yml +++ b/.github/workflows/electron-build.yml @@ -19,8 +19,8 @@ jobs: with: node-version: '22' - - name: Installation des dépendances - run: npm install + - name: Install dependencies + run: npm ci - name: Build de l'application (Windows) run: npm run build diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a97b36c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,40 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "3DS: Build CIA (Docker)", + "type": "shell", + "command": "docker run --rm -v \"${workspaceFolder}:/project\" -w /project/3ds devkitpro/devkitarm:latest bash -c 'source /etc/profile.d/devkit-env.sh && make cia'", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [], + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "detail": "Compile le projet 3DS en .cia en utilisant l'image Docker de devkitPro." + }, + { + "label": "3DS: Clean Build (Docker)", + "type": "shell", + "command": "docker run --rm -v \"${workspaceFolder}:/project\" -w /project/3ds devkitpro/devkitarm:latest bash -c 'source /etc/profile.d/devkit-env.sh && make clean'", + "group": "build", + "problemMatcher": [], + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": true + }, + "detail": "Nettoie les fichiers de build du projet 3DS." + } + ] +} diff --git a/3ds-build.yml b/3ds-build.yml new file mode 100644 index 0000000..115e067 --- /dev/null +++ b/3ds-build.yml @@ -0,0 +1,37 @@ +name: Build 3DS App (CIA) + +on: + push: + branches: + - "AppPhoneAPK&IPA" # Déclenché sur la même branche que les autres builds + paths: + - '3ds/**' # Uniquement si des fichiers dans 3ds/ sont modifiés + - 'SRC/**' # Ou si les sources web sont modifiées + workflow_dispatch: + +jobs: + build-3ds: + runs-on: ubuntu-latest + container: devkitpro/devkitarm:latest + + steps: + - name: Checkout du code + uses: actions/checkout@v4 + + - name: Préparation des fichiers RomFS + run: | + echo "Copie des fichiers web vers le dossier romfs..." + mkdir -p 3ds/romfs + cp -r SRC/* 3ds/romfs/ + + - name: Compilation de l'application 3DS + working-directory: ./3ds + run: | + echo "Lancement du build 3DS via make..." + make cia + + - name: Upload de l'artefact CIA + uses: actions/upload-artifact@v4 + with: + name: OpenPlanetsMaps-3DS-CIA + path: 3ds/OpenPlanetsMaps.cia \ No newline at end of file diff --git a/3ds/Makefile b/3ds/Makefile new file mode 100644 index 0000000..f3917c8 --- /dev/null +++ b/3ds/Makefile @@ -0,0 +1,32 @@ +# 3ds/Makefile + +# --- Configuration du Projet --- +TARGET := OpenPlanetsMaps_3DS # Nom du .elf, .3dsx, .smdh +CIA_TARGET := OpenPlanetsMaps.cia # Nom du fichier .cia final +NAME := OpenPlanetsMaps +AUTHOR := Tezca +DESCRIPTION := Visionneur de données astronomiques + +# --- Fichiers et Dossiers --- +SOURCES := source +DATA := romfs +INCLUDES := include + +# --- Options de compilation --- +CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++17 + +# --- Bibliothèques --- +# Ajoutez des bibliothèques ici si nécessaire (ex: -lcitro2d) +LIBS := -lcitro2d -lctru -lm + +# Variables d'environnement devkitPro +ifeq ($(strip $(DEVKITPRO)),) +$(error "Veuillez définir la variable d'environnement DEVKITPRO.") +endif + +# --- Système de Build (ne pas modifier) --- +include $(DEVKITPRO)/lib/dkp-rules/dkp.mk + +# --- Cibles de Build --- +# Les cibles 'all', 'cia', et 'clean' sont gérées automatiquement par dkp.mk +# Il n'est plus nécessaire de les définir manuellement. diff --git a/3ds/banner.png b/3ds/banner.png new file mode 100644 index 0000000..4a49e54 Binary files /dev/null and b/3ds/banner.png differ diff --git a/3ds/icon.png b/3ds/icon.png new file mode 100644 index 0000000..8261348 Binary files /dev/null and b/3ds/icon.png differ diff --git a/3ds/include/main.h b/3ds/include/main.h new file mode 100644 index 0000000..e69de29 diff --git a/3ds/romfs/admin.php b/3ds/romfs/admin.php new file mode 100644 index 0000000..26f05ae --- /dev/null +++ b/3ds/romfs/admin.php @@ -0,0 +1,109 @@ + "SET NAMES utf8", PDO::ATTR_EMULATE_PREPARES => false]; + $pdo = new PDO("mysql:host=$host;port=3306;dbname=$dbname;charset=utf8;protocol=tcp", $db_user, $db_pass, $options); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $planete = $_POST['planete']; + $nom = $_POST['nom']; + $date = $_POST['date']; + $url = $_POST['url']; + $description = $_POST['description']; + + $allowedTables = [ + 'mercury' => 'MERCURY', 'venus' => 'VENUS', 'earth' => 'EARTH', + 'mars' => 'MARS', 'jupiter' => 'JUPITER', 'saturn' => 'SATURN', + 'uranus' => 'URANUS', 'neptune' => 'NEPTUNE' + ]; + + if (array_key_exists($planete, $allowedTables)) { + $nomTable = $allowedTables[$planete]; + $stmt = $pdo->prepare("INSERT INTO $nomTable (title, date, url, explanation) VALUES (?, ?, ?, ?)"); + $stmt->execute([$nom, $date, $url, $description]); + + $message = "

Row successfully added to the $nomTable table!

"; + } + } catch (PDOException $e) { + $message = "

SQL Error: " . $e->getMessage() . "

"; + } +} +?> + + + + + Admin Panel - OpenPlanetsMaps + + + + +
+ +

Admin Login

+

+
+ +
+
+ +
+ + +

Add a row

+ +
+ +
+
+
+
+
+ +
+ + +
+ + \ No newline at end of file diff --git a/3ds/romfs/api.php b/3ds/romfs/api.php new file mode 100644 index 0000000..300dbc1 --- /dev/null +++ b/3ds/romfs/api.php @@ -0,0 +1,82 @@ + "SET NAMES utf8", PDO::ATTR_EMULATE_PREPARES => false]; + $pdo = new PDO("mysql:host=$host;port=3306;dbname=$dbname;charset=utf8;protocol=tcp", $username, $password, $options); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $planetId = isset($_GET['planet']) ? $_GET['planet'] : ''; + + $allowedTables = [ + 'mercury' => 'MERCURY', + 'venus' => 'VENUS', + 'earth' => 'EARTH', + 'mars' => 'MARS', + 'jupiter' => 'JUPITER', + 'saturn' => 'SATURN', + 'uranus' => 'URANUS', + 'neptune' => 'NEPTUNE', + 'sun' => 'SUN', + 'moon' => 'MOON', + ]; + + if ($planetId === 'quiz') { + $queries = []; + $seed = date('Ymd'); + foreach ($allowedTables as $key => $table) { + $planetName = ucfirst($key); + $queries[] = "(SELECT explanation, '$planetName' AS planete_nom, url FROM $table WHERE explanation IS NOT NULL AND explanation != '' ORDER BY RAND($seed) LIMIT 3)"; + } + $unionQuery = implode(' UNION ALL ', $queries); + + $stmt = $pdo->prepare("SELECT * FROM ($unionQuery) as all_data ORDER BY RAND($seed) LIMIT 10"); + $stmt->execute(); + $results = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $questions = []; + $allPlanets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune','Sun' , 'Moon']; + + foreach ($results as $row) { + $correctEn = $row['planete_nom']; + + $optionsEn = [$correctEn]; + $others = array_diff($allPlanets, [$correctEn]); + shuffle($others); + $optionsEn = array_merge($optionsEn, array_slice($others, 0, 3)); + shuffle($optionsEn); + + $text = substr($row['explanation'], 0, 200) . '...'; + + $questions[] = [ + 'q' => "Which celestial body does this description refer to? « " . $text . " »", + 'options' => $optionsEn, + 'a' => $correctEn, + 'img' => isset($row['url']) ? $row['url'] : '' + ]; + } + + echo json_encode($questions); + exit; + } + + if (!array_key_exists($planetId, $allowedTables)) { + echo json_encode([]); + exit; + } + + $tableName = $allowedTables[$planetId]; + $stmt = $pdo->prepare("SELECT * FROM $tableName"); + $stmt->execute(); + + $data = $stmt->fetchAll(PDO::FETCH_ASSOC); + echo json_encode($data); +} catch (PDOException $e) { + http_response_code(500); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); +} \ No newline at end of file diff --git a/3ds/romfs/contact.php b/3ds/romfs/contact.php new file mode 100644 index 0000000..72710ca --- /dev/null +++ b/3ds/romfs/contact.php @@ -0,0 +1,88 @@ +Format non autorisé. Seuls les fichiers JPG, PNG et PDF sont acceptés.

"; + $has_error = true; + } elseif ($file_size > $max_size) { + $message_status = "

Le fichier est trop volumineux (maximum 5 Mo).

"; + $has_error = true; + } else { + $upload_dir = 'UPLOADS/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0777, true); + } + + $safe_file_name = time() . '_' . preg_replace('/[^a-zA-Z0-9.\-_]/', '', $file_name); + $target_path = $upload_dir . $safe_file_name; + + if (move_uploaded_file($file_tmp_name, $target_path)) { + $file_path = $target_path; + } else { + $message_status = "

Erreur lors de la sauvegarde du fichier.

"; + $has_error = true; + } + } + } + + if (!$has_error) { + try { + $host = '172.22.0.3'; + $dbname = 'OpenPlanetsMaps'; + $db_user = 'root'; + $db_pass = 'admin123'; + + $options = [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8", PDO::ATTR_EMULATE_PREPARES => false]; + $pdo = new PDO("mysql:host=$host;port=3306;dbname=$dbname;charset=utf8;protocol=tcp", $db_user, $db_pass, $options); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $stmt = $pdo->prepare("INSERT INTO CONTACT (email, objet, demande, fichier, date_demande) VALUES (?, ?, ?, ?, NOW())"); + $stmt->execute([$email, $subject, $message, $file_path]); + + $message_status = "

Votre demande a été enregistrée avec succès dans la base de données !

"; + } catch (PDOException $e) { + $message_status = "

Erreur de base de données : " . htmlspecialchars($e->getMessage()) . "

"; + } + } +} +?> + + + + + Contact - OpenPlanetsMaps + + + +
+

Contact

+ +
+
+
+
+
+ +
+ +
+ + \ No newline at end of file diff --git a/3ds/romfs/index.html b/3ds/romfs/index.html new file mode 100644 index 0000000..44a86f1 --- /dev/null +++ b/3ds/romfs/index.html @@ -0,0 +1,397 @@ + + + + + + + OpenPlanetsMaps + + + + +
+
+ +
+ +

Open Planets Maps

+
+ + +
+
+ + + +
+
+
+ +
+
+
+

Space Exploration

+

Navigate through the solar system and discover the unique characteristics of each planet with our interactive maps.

+
+
+

Open Data

+

Access a topographic database from space agencies, centralized in an open source tool.

+
+
+
+
Solar System - 8 Planets - Open Source Mapping - Topographic Data - Interactive Exploration...
+
+
Ready for launch? Open the side menu (☰) and select a destination to begin your journey.
+
+ +
+ +
+ +
+
Mercury is the closest planet to the Sun and the smallest in the solar system. Its surface is heavily cratered, resembling our Moon, and it experiences extreme temperature variations.
+
+
Distance from the Sun: 58 million km  •  Temperature: -180°C to 430°C  •  Surface area: 74.8 million km²  •  Length of day: 59 Earth days
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Venus is the second planet in the solar system and the hottest. Its highly dense atmosphere causes an extreme greenhouse effect, and its surface is masked by thick clouds of sulfuric acid.
+
+
Distance from the Sun: 108 million km  •  Average temperature: 462°C  •  Surface area: 460 million km²  •  Length of day: 243 Earth days
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Our home planet, Earth, is the third planet in the solar system and the only one known to harbor life. It is distinguished by its vast oceans of liquid water that cover more than 70% of its surface.
+
+
Distance from the Sun: 150 million km (1 AU)  •  Average temperature: 15°C  •  Surface area: 510 million km²  •  Natural satellites: 1 (The Moon)
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Nicknamed the « Red Planet » due to the iron oxide on its surface, Mars has polar ice caps, the largest volcano in the solar system (Olympus Mons), and an immense canyon (Valles Marineris).
+
+
Distance from the Sun: 227.9 million km  •  Average temperature: -63°C  •  Surface area: 144.8 million km²  •  Gravity: 3.72 m/s²
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Jupiter is the largest planet in our solar system. This gas giant is famous for its Great Red Spot, a gigantic anticyclonic storm, and possesses dozens of fascinating moons.
+
+
Distance from the Sun: 778 million km  •  Average temperature: -110°C  •  Surface area: 61.4 billion km²  •  Natural satellites: 95 known
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Instantly recognizable by its spectacular system of rings composed of ice and rock particles, Saturn is a vast gas giant with extremely violent winds.
+
+
Distance from the Sun: 1.43 billion km  •  Average temperature: -140°C  •  Surface area: 42.7 billion km²  •  Natural satellites: 146 known
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
Uranus is an ice giant whose axis of rotation is highly tilted, making it appear to « roll » along its orbit. Its beautiful cyan tint is due to the presence of methane.
+
+
Distance from the Sun: 2.87 billion km  •  Average temperature: -195°C  •  Surface area: 8.1 billion km²  •  Length of year: 84 Earth years
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ +
+ +
+
The farthest planet from the Sun is another ice giant, Neptune. It is known for its deep azure blue color and its supersonic winds, the fastest in the entire solar system.
+
+
Distance from the Sun: 4.5 billion km  •  Average temperature: -200°C  •  Surface area: 7.6 billion km²  •  Wind speed: up to 2,100 km/h
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ + +
The Sun is the star at the center of our solar system. It alone accounts for about 99.8% of the solar system's mass and provides the energy necessary to sustain life on Earth.
+
+
Type: Yellow dwarf  •  Surface temperature: 5,500°C  •  Distance to Earth: 150 million km (1 AU)  •  Age: 4.6 billion years
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+ + +
The Moon is Earth's only natural satellite. It is the fifth largest satellite in the solar system and the only non-terrestrial celestial body to have been visited by humans.
+
+
Distance to Earth: 384,400 km  •  Temperature: -173°C to 127°C  •  Gravity: 1.62 m/s²  •  Orbit duration: 27.3 days
+
+
+ + + + + + + + + + + +
NameImageDate
Waiting for data...
+
+
+ +
+
Test your knowledge of the solar system! Answer the questions below to see if you are a true space expert.
+ +
+ ◈ DAILY CHALLENGE +

Every day at midnight, the onboard computer compiles our archives to generate a new unique 10-question Quiz. Practice daily to perfect your knowledge!

+
+ +
+

Loading the Quiz...

+
+
+
◈ SOURCE — MISSION IMAGE
+ +
+
+

+ +
+
+
+ + + +
+
+ +
+ ◈ SYSTEM / ABOUT +

OpenPlanetsMaps

+

v1.0 — 2026

+
+
+
+

Project

+

OpenPlanetsMaps is an open source tool for interactive mapping of the solar system. Topographic data comes from the main global space agencies.

+
+
+

Data & Sources

+
    +
  • NASA — National Aeronautics and Space Administration
  • +
  • ESA — European Space Agency
  • +
  • JAXA — Japan Aerospace Exploration Agency
  • +
  • USGS — Astrogeology Science Center
  • +
+
+
+

3D Models

+
    +
  • Sketchfab — Hosting & rendering of interactive models
  • +
  • Models under Creative Commons licenses
  • +
+
+
+

Technologies

+
    +
  • HTML / CSS / JS — Front-end
  • +
  • PHP + MySQL — Back-end & database
  • +
  • Orbitron, Space Grotesk, Space Mono — Google Fonts
  • +
+
+
+

License

+

This project is freely distributed. Scientific data remains the property of their respective organizations.

+
+
+ +
+ + + + + \ No newline at end of file diff --git a/3ds/romfs/main.js b/3ds/romfs/main.js new file mode 100644 index 0000000..debf403 --- /dev/null +++ b/3ds/romfs/main.js @@ -0,0 +1,401 @@ +function toggleSidebar() { + document.getElementById('sidebar').classList.toggle('open'); +} + +function showLayer(layerId) { + document.querySelectorAll('.layer').forEach(layer => { + layer.classList.remove('active'); + }); + + document.getElementById('layer-' + layerId).classList.add('active'); + + document.body.className = ''; + document.body.classList.add('theme-' + layerId); + + if (layerId === 'Quiz') { + initQuiz(); + } else if (layerId !== 'home') { + fetchData(layerId); + } + + toggleSidebar(); +} + +const planetData = {}; +const planetCols = {}; + +function wireSearch(planetId) { + const layer = document.getElementById('layer-' + planetId); + if (!layer) return; + const input = layer.querySelector('.search-bar input'); + if (!input) return; + + const fresh = input.cloneNode(true); + input.parentNode.replaceChild(fresh, input); + + fresh.addEventListener('input', () => { + filterTable(planetId, fresh.value.trim()); + }); +} + +function filterTable(planetId, query) { + const tbody = document.getElementById('tbody-' + planetId); + if (!tbody) return; + + const rows = Array.from(tbody.querySelectorAll('tr')); + const cols = planetCols[planetId] || []; + const data = planetData[planetId] || []; + + const titleIndices = cols.reduce((acc, col, i) => { + const c = col.toLowerCase(); + if (c === 'name' || c.includes('title')) { + acc.push(i); + } + return acc; + }, []); + + const searchIndices = titleIndices.length > 0 + ? titleIndices + : cols.reduce((acc, col, i) => { + const c = col.toLowerCase(); + if (!c.includes('url') && !c.includes('desc') && !c.includes('expl') && c !== 'image' && c !== 'img') { + acc.push(i); + } + return acc; + }, []); + + const tokens = query.toLowerCase().split(/\s+/).filter(Boolean); + + const tableContainer = tbody.closest('.table-container'); + const showMoreBtn = tableContainer ? tableContainer.querySelector('.show-more-btn') : null; + + if (tokens.length === 0) { + rows.forEach((tr, index) => { + tr.style.display = ''; + if (data.length > 10) { + if (index >= 10) tr.classList.add('hidden-row'); + else tr.classList.remove('hidden-row'); + } + }); + if (showMoreBtn) showMoreBtn.style.display = ''; + return; + } + + if (showMoreBtn) showMoreBtn.style.display = 'none'; + + rows.forEach((tr, index) => { + const rowObj = data[index]; + if (!rowObj) { tr.style.display = 'none'; return; } + + const haystack = searchIndices + .map(i => String(rowObj[cols[i]] || '').toLowerCase()) + .join(' '); + + const matches = tokens.every(token => haystack.includes(token)); + + tr.classList.remove('hidden-row'); + tr.style.display = matches ? '' : 'none'; + }); +} + +async function fetchData(planetId) { + const tbody = document.getElementById('tbody-' + planetId); + if (!tbody) return; + + if (tbody.getAttribute('data-loaded') === 'true') { + wireSearch(planetId); + return; + } + + tbody.innerHTML = 'Loading data from the database...'; + + try { + const response = await fetch(`api.php?planet=${planetId}`); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `HTTP Error: ${response.status}`); + } + + const data = await response.json(); + + tbody.innerHTML = ''; + + if (data.length === 0) { + tbody.innerHTML = 'No data available at the moment.'; + return; + } + + const columns = Object.keys(data[0]).filter(col => col.toLowerCase() !== 'copyright'); + + planetData[planetId] = data; + planetCols[planetId] = columns; + + const thead = tbody.parentElement.querySelector('thead'); + thead.innerHTML = '' + columns.map(col => { + const colLower = col.toLowerCase(); + const isDesc = colLower.includes('desc') || colLower.includes('expl'); + const isUrl = colLower.includes('url') || colLower === 'image' || colLower === 'img'; + + let className = ''; + if (isDesc) className = ' class="col-desc"'; + else if (isUrl) className = ' class="col-url"'; + return `${col.toUpperCase()}`; + }).join('') + ''; + + data.forEach((row, index) => { + const tr = document.createElement('tr'); + + if (index >= 10) tr.classList.add('hidden-row'); + + let rowHtml = ''; + columns.forEach(col => { + const colLower = col.toLowerCase(); + const colLabel = col.toUpperCase(); + if (colLower.includes('url') || colLower === 'image' || colLower === 'img') { + const url = row[col]; + if (url && url.trim() !== '') { + rowHtml += ` + + ${url} + + `; + } else { + rowHtml += `N/A`; + } + } else if (colLower.includes('desc') || colLower.includes('expl')) { + const text = row[col] || ''; + let fontSizeStyle = ''; + if (text.length > 200) fontSizeStyle = ' font-size: 0.75rem;'; + else if (text.length > 100) fontSizeStyle = ' font-size: 0.85rem;'; + rowHtml += `${text}`; + } else { + rowHtml += `${row[col]}`; + } + }); + + tr.innerHTML = rowHtml; + tbody.appendChild(tr); + }); + + const tableContainer = tbody.closest('.table-container'); + if (tableContainer) { + tableContainer.style.overflowX = 'auto'; + tableContainer.style.maxWidth = '100%'; + tableContainer.style.WebkitOverflowScrolling = 'touch'; + } + const existingBtn = tableContainer.querySelector('.show-more-btn'); + if (existingBtn) existingBtn.remove(); + + if (data.length > 10) { + const btn = document.createElement('button'); + btn.innerHTML = 'Show more ⬇'; + btn.className = 'show-more-btn'; + btn.onclick = () => { + tbody.querySelectorAll('.hidden-row').forEach(row => row.classList.remove('hidden-row')); + btn.remove(); + }; + tableContainer.appendChild(btn); + } + + tbody.setAttribute('data-loaded', 'true'); + + wireSearch(planetId); + + } catch (error) { + console.error("Database connection error:", error); + tbody.innerHTML = `Technical error: ${error.message}`; + } +} + +let quizQuestions = []; +let currentQuizIndex = 0; +let quizScore = 0; + +async function initQuiz() { + currentQuizIndex = 0; + quizScore = 0; + document.getElementById('quiz-restart-btn').style.display = 'none'; + document.getElementById('quiz-score').textContent = ''; + + if (quizQuestions.length === 0) { + document.getElementById('quiz-question').textContent = "Loading today's Quiz..."; + document.getElementById('quiz-options').innerHTML = ''; + + try { + const response = await fetch(`api.php?planet=quiz`); + if (!response.ok) { + let errMsg = "Network error"; + try { + const errData = await response.json(); + if (errData.error) errMsg = errData.error; + } catch (e) {} + throw new Error(errMsg); + } + quizQuestions = await response.json(); + + if (quizQuestions.length === 0) { + document.getElementById('quiz-question').textContent = "Not enough data in the database to generate a quiz."; + return; + } + } catch (error) { + console.error("Quiz Error:", error); + document.getElementById('quiz-question').textContent = "Error: " + error.message; + return; + } + } + + showQuizQuestion(); +} + +function showQuizQuestion() { + const preview = document.getElementById('quiz-img-preview'); + + if (currentQuizIndex >= quizQuestions.length) { + document.getElementById('quiz-question').textContent = "Today's Quiz finished!"; + document.getElementById('quiz-options').innerHTML = ""; + document.getElementById('quiz-score').textContent = `Your score: ${quizScore} / ${quizQuestions.length}`; + document.getElementById('quiz-restart-btn').style.display = 'inline-block'; + if (preview) preview.classList.remove('visible'); + return; + } + + const qObj = quizQuestions[currentQuizIndex]; + document.getElementById('quiz-question').innerText = `Question ${currentQuizIndex + 1}/${quizQuestions.length}:\n\n${qObj.q}`; + + const optionsContainer = document.getElementById('quiz-options'); + optionsContainer.innerHTML = ''; + + qObj.options.forEach(opt => { + const btn = document.createElement('button'); + btn.className = 'quiz-btn'; + btn.textContent = opt; + btn.onclick = () => checkAnswer(opt, qObj.a, btn); + optionsContainer.appendChild(btn); + }); + + if (preview) { + preview.classList.remove('visible'); + const oldImg = preview.querySelector('img'); + const oldVid = preview.querySelector('video'); + if (oldImg) { oldImg.src = ''; oldImg.style.display = 'none'; } + if (oldVid) { oldVid.pause(); oldVid.src = ''; oldVid.style.display = 'none'; } + + if (qObj.img && qObj.img.trim() !== '') { + const url = qObj.img.trim(); + const isVideo = /\.(mp4|webm|ogg|mov)(\?.*)?$/i.test(url); + const caption = preview.querySelector('.quiz-img-caption'); + + requestAnimationFrame(() => { + if (isVideo) { + let vid = preview.querySelector('video'); + if (!vid) { + vid = document.createElement('video'); + vid.controls = true; + vid.preload = 'metadata'; + vid.style.display = 'none'; + preview.querySelector('.quiz-img-label').insertAdjacentElement('afterend', vid); + } + vid.src = url; + vid.style.display = 'block'; + if (oldImg) oldImg.style.display = 'none'; + } else { + if (oldImg) { + oldImg.src = url; + oldImg.alt = 'Image associated with the question'; + oldImg.style.display = 'block'; + } + if (oldVid) oldVid.style.display = 'none'; + } + caption.textContent = url; + preview.classList.add('visible'); + }); + } + } +} + +function checkAnswer(selected, correct, btn) { + const optionsContainer = document.getElementById('quiz-options'); + const buttons = optionsContainer.querySelectorAll('.quiz-btn'); + + buttons.forEach(b => { + b.disabled = true; + b.style.cursor = 'default'; + }); + + if (selected === correct) { + btn.classList.add('correct'); + quizScore++; + } else { + btn.classList.add('wrong'); + buttons.forEach(b => { + if (b.textContent === correct) b.classList.add('correct'); + }); + } + + setTimeout(() => { + currentQuizIndex++; + showQuizQuestion(); + }, 1800); +} + +function openCredits() { + document.getElementById('credits-panel').classList.add('open'); + document.getElementById('credits-overlay').classList.add('open'); + document.body.style.overflow = 'hidden'; +} + +function closeCredits() { + document.getElementById('credits-panel').classList.remove('open'); + document.getElementById('credits-overlay').classList.remove('open'); + document.body.style.overflow = ''; +} + +document.addEventListener('DOMContentLoaded', () => { + const overlay = document.getElementById('credits-overlay'); + if (overlay) overlay.addEventListener('click', closeCredits); + + document.addEventListener('keydown', e => { + if (e.key === 'Escape') closeCredits(); + }); + + const isMobile = window.innerWidth <= 768 || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + const isElectron = navigator.userAgent.toLowerCase().includes('electron'); + const isCapacitor = window.Capacitor !== undefined; // Détecte l'application mobile native + + if (isCapacitor) { + document.body.classList.add('is-mobile-app'); + } + + if (isMobile || isElectron || isCapacitor) { + const iframes = document.querySelectorAll('iframe'); + iframes.forEach(iframe => { + const fallback = document.createElement('div'); + if (isElectron) { + fallback.textContent = "3D models are not compatible on the desktop app"; + } else if (isCapacitor) { + fallback.textContent = "3D models are not compatible on the mobile app"; + } else { + fallback.textContent = "functionality not available on mobile devices"; + } + fallback.style.display = "flex"; + fallback.style.alignItems = "center"; + fallback.style.justifyContent = "center"; + + // Correction du responsive : on empêche la boîte de déborder de l'écran + fallback.style.width = "100%"; + fallback.style.maxWidth = "100%"; + fallback.style.boxSizing = "border-box"; + + fallback.style.minHeight = "200px"; + fallback.style.backgroundColor = "#1e293b"; + fallback.style.color = "#f87171"; + fallback.style.border = "1px solid #334155"; + fallback.style.borderRadius = "8px"; + fallback.style.padding = "20px"; + fallback.style.textAlign = "center"; + + iframe.parentNode.replaceChild(fallback, iframe); + }); + } +}); \ No newline at end of file diff --git a/3ds/romfs/style.css b/3ds/romfs/style.css new file mode 100644 index 0000000..8441bc3 --- /dev/null +++ b/3ds/romfs/style.css @@ -0,0 +1,593 @@ +@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Orbitron:wght@400;600;700;900&family=Space+Mono:wght@400;700&display=swap'); + +:root { + --theme-color: #4f8eff; + --theme-glow: rgba(79,142,255,.35); + --theme-subtle:rgba(79,142,255,.08); + --bg-deep: #020408; --bg-mid: #060d18; + --text-primary: #e8f0ff; --text-secondary: #7a9bc4; --text-muted: #3d5478; + --border: rgba(79,142,255,.15); +} +body.theme-home { --theme-color:#4f8eff; --theme-glow:rgba(79,142,255,.4); --bg-deep:#020408; --bg-mid:#060d18; } +body.theme-mercury { --theme-color:#c0a080; --theme-glow:rgba(192,160,128,.4); --bg-deep:#0c0906; --bg-mid:#1a1008; } +body.theme-venus { --theme-color:#ff9230; --theme-glow:rgba(255,146,48,.4); --bg-deep:#0d0600; --bg-mid:#1e0e00; } +body.theme-earth { --theme-color:#00cfff; --theme-glow:rgba(0,207,255,.4); --bg-deep:#010b10; --bg-mid:#031520; } +body.theme-mars { --theme-color:#ff5040; --theme-glow:rgba(255,80,64,.4); --bg-deep:#0d0200; --bg-mid:#1e0600; } +body.theme-jupiter { --theme-color:#ffb347; --theme-glow:rgba(255,179,71,.4); --bg-deep:#0d0800; --bg-mid:#1e1200; } +body.theme-saturn { --theme-color:#f5d060; --theme-glow:rgba(245,208,96,.4); --bg-deep:#0c0900; --bg-mid:#1c1500; } +body.theme-uranus { --theme-color:#40e8d0; --theme-glow:rgba(64,232,208,.4); --bg-deep:#000c0b; --bg-mid:#001a18; } +body.theme-neptune { --theme-color:#2060ff; --theme-glow:rgba(32,96,255,.4); --bg-deep:#000208; --bg-mid:#000510; } +body.theme-sun { --theme-color:#ffcc44; --theme-glow:rgba(255,180,40,.55); --theme-subtle:rgba(255,180,40,.09); --bg-deep:#0d0500; --bg-mid:#200c00; --border:rgba(255,180,40,.2); --text-primary:#fff5dc; --text-secondary:#c4923a; --text-muted:#6b4010; } +body.theme-moon { --theme-color:#c8cfe8; --theme-glow:rgba(200,207,232,.35); --theme-subtle:rgba(200,207,232,.06); --bg-deep:#060608; --bg-mid:#0e0f14; --border:rgba(200,207,232,.12); --text-primary:#dde2f0; --text-secondary:#7e8ba8; --text-muted:#3c4258; } +body.theme-Quiz { --theme-color:#7eb8f7; --theme-glow:rgba(126,184,247,.35); --theme-subtle:rgba(126,184,247,.07); --bg-deep:#020510; --bg-mid:#060e24; --border:rgba(126,184,247,.13); --text-primary:#ddeeff; --text-secondary:#6a90b8; --text-muted:#2e4a6a; } + +*,*::before,*::after { margin:0; padding:0; box-sizing:border-box; } + +html, body { max-width:100vw; overflow-x:hidden; } +body { + background:var(--bg-deep); color:var(--text-primary); + font-family:'Space Grotesk',sans-serif; + display:flex; flex-direction:column; min-height:100vh; overflow-x:hidden; + transition:background-color .8s ease, color .3s ease; +} + +.bg-animated { + position:fixed; inset:0; z-index:-2; + background:radial-gradient(ellipse at 20% 10%, var(--bg-mid) 0%, var(--bg-deep) 60%); + transition:background 1s ease; +} +.bg-animated::before, .bg-animated::after { + content:''; position:absolute; inset:0; +} +.bg-animated::before { + background-image: + radial-gradient(1px 1px at 10% 15%,rgba(255,255,255,.8) 0%,transparent 100%), + radial-gradient(1px 1px at 25% 40%,rgba(255,255,255,.5) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 40% 8%,rgba(255,255,255,.9) 0%,transparent 100%), + radial-gradient(1px 1px at 55% 60%,rgba(255,255,255,.4) 0%,transparent 100%), + radial-gradient(1px 1px at 70% 25%,rgba(255,255,255,.7) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 80% 70%,rgba(255,255,255,.6) 0%,transparent 100%), + radial-gradient(1px 1px at 90% 45%,rgba(255,255,255,.5) 0%,transparent 100%), + radial-gradient(1px 1px at 15% 80%,rgba(255,255,255,.6) 0%,transparent 100%), + radial-gradient(1px 1px at 35% 90%,rgba(255,255,255,.4) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 60% 85%,rgba(255,255,255,.7) 0%,transparent 100%), + radial-gradient(1px 1px at 82% 15%,rgba(255,255,255,.8) 0%,transparent 100%), + radial-gradient(1px 1px at 5% 55%,rgba(255,255,255,.5) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 92% 88%,rgba(255,255,255,.6) 0%,transparent 100%), + radial-gradient(1px 1px at 67% 5%,rgba(255,255,255,.9) 0%,transparent 100%); + animation:starTwinkle 8s ease-in-out infinite alternate; +} +.bg-animated::after { + background-image: + radial-gradient(1px 1px at 18% 30%,rgba(255,255,255,.6) 0%,transparent 100%), + radial-gradient(1px 1px at 33% 65%,rgba(255,255,255,.4) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 48% 22%,rgba(255,255,255,.7) 0%,transparent 100%), + radial-gradient(1px 1px at 63% 75%,rgba(255,255,255,.5) 0%,transparent 100%), + radial-gradient(1px 1px at 75% 48%,rgba(255,255,255,.8) 0%,transparent 100%), + radial-gradient(1px 1px at 88% 30%,rgba(255,255,255,.4) 0%,transparent 100%), + radial-gradient(1.5px 1.5px at 12% 68%,rgba(255,255,255,.6) 0%,transparent 100%), + radial-gradient(1px 1px at 96% 60%,rgba(255,255,255,.5) 0%,transparent 100%), + radial-gradient(1px 1px at 52% 38%,rgba(255,255,255,.3) 0%,transparent 100%); + animation:starTwinkle 12s ease-in-out infinite alternate-reverse; +} +body::before { + content:''; position:fixed; inset:0; z-index:-1; pointer-events:none; + background: + radial-gradient(ellipse 40% 30% at 15% 20%, var(--theme-glow), transparent), + radial-gradient(ellipse 25% 35% at 85% 75%, var(--theme-glow), transparent); + transition:background 1s ease; +} +body.theme-sun::before { + background: + radial-gradient(ellipse 50% 35% at 80% 15%,rgba(255,150,20,.3),transparent), + radial-gradient(ellipse 35% 50% at 10% 80%,rgba(255,100,0,.2),transparent); +} +body.theme-moon::before { + background: + radial-gradient(ellipse 45% 30% at 85% 10%,rgba(200,207,232,.18),transparent), + radial-gradient(ellipse 30% 45% at 5% 90%,rgba(150,160,200,.12),transparent); +} +@keyframes starTwinkle { + 0% { opacity:.5; } + 50% { opacity:1; } + 100% { opacity:.65; } +} + +#safe-area-block { + position: sticky; top: 0; z-index: 101; + width: 100%; + height: env(safe-area-inset-top); + background: var(--bg-deep); +} + +header { + display:flex; justify-content:space-between; align-items:center; + padding:0 2rem; + height:64px; + background:rgba(2,4,8,.75); backdrop-filter:blur(20px); + border-bottom:1px solid var(--border); + position:sticky; top:env(safe-area-inset-top); z-index:100; + transition:border-color .6s ease; +} +header::after { + content:''; position:absolute; bottom:0; left:0; right:0; height:1px; + background:linear-gradient(90deg,transparent,var(--theme-color),transparent); + opacity:.6; transition:background .6s ease; +} +header h1 { + font-family:'Orbitron',monospace; font-size:1.1rem; font-weight:700; + letter-spacing:.15em; text-transform:uppercase; + color:var(--theme-color); text-shadow:0 0 20px var(--theme-glow); + transition:color .6s ease, text-shadow .6s ease; +} +header h1:hover { animation:glitch .4s steps(1) forwards; } + +.menu-icon, .help-btn { + cursor:pointer; background:transparent; + border:1px solid var(--border); border-radius:6px; padding:.45rem 1rem; + color:var(--text-secondary); font-family:'Space Mono',monospace; + font-size:.8rem; letter-spacing:.08em; + transition:all .2s ease; position:relative; overflow:hidden; +} +.menu-icon::before, .help-btn::before { + content:''; position:absolute; inset:0; + background:var(--theme-subtle); opacity:0; transition:opacity .2s ease; +} +.menu-icon:hover::before, .help-btn:hover::before { opacity:1; } +.menu-icon:hover, .help-btn:hover { + border-color:var(--theme-color); color:var(--theme-color); + box-shadow:0 0 12px var(--theme-glow); +} + +.sidebar { + position:fixed; top:0; left:-300px; width:280px; height:100vh; + background:rgba(2,6,14,.96); backdrop-filter:blur(24px); + border-right:1px solid var(--border); + transition:left .35s cubic-bezier(.4,0,.2,1), border-color .6s ease; + z-index:1000; padding-top:5rem; overflow-y:auto; +} +.sidebar::before { + content:'NAVIGATION'; position:absolute; top:1.5rem; left:1.5rem; + font-family:'Space Mono',monospace; font-size:.65rem; letter-spacing:.25em; + color:var(--text-muted); +} +.sidebar.open { left:0; } +.sidebar ul { list-style:none; } +.sidebar li { + padding:.9rem 1.75rem; cursor:pointer; + font-family:'Orbitron',monospace; font-size:.7rem; font-weight:600; + letter-spacing:.12em; text-transform:uppercase; color:var(--text-secondary); + transition:all .2s ease; border-left:2px solid transparent; position:relative; +} +.sidebar li::after { + content:'→'; position:absolute; right:1.5rem; top:50%; + transform:translateY(-50%) translateX(-4px); + opacity:0; transition:all .2s ease; font-family:monospace; +} +.sidebar li:hover { + color:var(--theme-color); border-left-color:var(--theme-color); + background:var(--theme-subtle); padding-left:2.25rem; + text-shadow:0 0 10px var(--theme-glow); +} +.sidebar li:hover::after { opacity:1; transform:translateY(-50%) translateX(0); } +.sidebar li.menu-category { + font-family:'Space Mono',monospace; font-size:.6rem; letter-spacing:.3em; + color:var(--text-muted); cursor:default; border-left:none; + padding:1.2rem 1.75rem .4rem; pointer-events:none; +} +.sidebar li.menu-category::after { display:none; } +.sidebar li.menu-category:hover { background:none; padding-left:1.75rem; text-shadow:none; color:var(--text-muted); } + +#content-area { + flex:1; padding:2.5rem; display:flex; + max-width:1400px; margin:0 auto; width:100%; +} +.layer { + display:none; width:100%; flex-direction:column; flex:1; text-align:center; + animation:layerReveal .5s cubic-bezier(.4,0,.2,1) both; +} +.layer.active { display:flex; } + +.main-img { + margin-bottom:2.5rem; position:relative; + border-radius:12px; overflow:hidden; border:1px solid var(--border); + box-shadow:0 20px 60px rgba(0,0,0,.6), 0 0 60px var(--theme-glow); + transition:box-shadow .6s ease, border-color .6s ease, transform .4s ease; +} +.main-img:hover { transform:perspective(1200px) rotateX(1deg) scale(1.005); } +.main-img::before { + content:''; position:absolute; inset:0; border-radius:12px; + box-shadow:inset 0 0 30px rgba(0,0,0,.3); z-index:1; pointer-events:none; +} +.main-img iframe, .main-img img { width:100%; height:70vh; border:none; display:block; } +.main-img img { object-fit:cover; } +.sketchfab-embed-wrapper p { font-family:'Space Mono',monospace; font-size:.65rem; color:var(--text-muted); padding:.4rem .75rem; text-align:right; } +.sketchfab-embed-wrapper a { color:var(--theme-color); text-decoration:none; } + +.flex-row { + display:flex; flex-wrap:wrap; justify-content:center; + gap:1.5rem; margin:0 auto 2.5rem; max-width:900px; +} +.flex-row .box { flex:1; min-width:280px; } +.box { + padding:1.75rem; background:rgba(255,255,255,.02); + border:1px solid var(--border); border-radius:10px; + text-align:left; position:relative; overflow:hidden; + transition:border-color .3s, box-shadow .3s, transform .3s; +} +.box::before { + content:''; position:absolute; top:0; left:-100%; right:100%; height:2px; + background:linear-gradient(90deg,transparent,var(--theme-color),transparent); + transition:left .4s ease, right .4s ease; +} +.box:hover::before { left:0; right:0; } +.box:hover { border-color:var(--theme-color); box-shadow:0 8px 30px var(--theme-glow); transform:translateY(-2px); } +.box h2 { + font-family:'Orbitron',monospace; font-size:.75rem; font-weight:700; + letter-spacing:.2em; text-transform:uppercase; + color:var(--theme-color); margin-bottom:.85rem; text-shadow:0 0 15px var(--theme-glow); +} +.box p { line-height:1.7; color:var(--text-secondary); font-size:.9rem; } + +.desc-box { + max-width:780px; margin:0 auto 2.5rem; line-height:1.75; + color:var(--text-secondary); font-size:.95rem; padding:1.25rem 1.75rem; + background:rgba(255,255,255,.02); border:1px solid var(--border); border-radius:8px; + border-left:3px solid var(--theme-color); text-align:left; + transition:border-color .6s ease; +} +#layer-home .desc-box { + font-family:'Space Mono',monospace; font-size:.85rem; + color:var(--theme-color); text-align:center; + background:var(--theme-subtle); border:1px solid var(--theme-color); + letter-spacing:.05em; opacity:.8; +} + +.ticker-wrap { + width:100%; overflow:hidden; padding:.65rem 0; margin-bottom:2.5rem; + border-top:1px solid var(--border); border-bottom:1px solid var(--border); + position:relative; transition:border-color .6s ease; +} +.ticker-wrap::before { + content:'◈ TELEMETRY'; position:absolute; left:0; top:50%; transform:translateY(-50%); + font-family:'Space Mono',monospace; font-size:.6rem; letter-spacing:.2em; + color:var(--theme-color); background:var(--bg-deep); padding:0 .75rem; z-index:1; + transition:color .6s, background .6s; +} +.ticker-wrap::after { + content:''; position:absolute; right:0; top:0; bottom:0; width:80px; + background:linear-gradient(90deg,transparent,var(--bg-deep)); z-index:1; + transition:background .6s; +} +.ticker { + display:inline-block; white-space:nowrap; + animation:marquee 20s linear infinite; + font-family:'Space Mono',monospace; font-size:.75rem; + letter-spacing:.08em; color:var(--text-secondary); padding-left:7rem; +} +.ticker:hover { animation-play-state:paused; } + +.search-bar { margin-bottom:2rem; position:relative; display:flex; justify-content:center; } +.search-bar::before { + content:'⌕'; position:absolute; left:calc(50% - 210px); top:50%; + transform:translateY(-50%); font-size:1.1rem; color:var(--text-muted); + pointer-events:none; z-index:1; transition:color .2s; +} +.search-bar:focus-within::before { color:var(--theme-color); } +.search-bar input { + width:100%; max-width:460px; padding:.7rem 1.25rem .7rem 2.5rem; + background:rgba(255,255,255,.03); border:1px solid var(--border); border-radius:6px; + color:var(--text-primary); font-family:'Space Mono',monospace; + font-size:.8rem; letter-spacing:.05em; outline:none; + transition:border-color .2s, box-shadow .2s, background .2s; +} +.search-bar input::placeholder { color:var(--text-muted); } +.search-bar input:focus { + border-color:var(--theme-color); background:var(--theme-subtle); + box-shadow:0 0 20px var(--theme-glow); +} + +.table-container { margin-bottom:3rem; } +.data-table { width:100%; max-width:1300px; margin:0 auto; border-collapse:collapse; font-size:.88rem; } +.data-table thead tr { background:rgba(255,255,255,.03); } +.data-table th { + padding:.85rem 1.1rem; text-align:center; + font-family:'Space Mono',monospace; font-size:.65rem; font-weight:700; + letter-spacing:.2em; text-transform:uppercase; color:var(--theme-color); + border-bottom:1px solid var(--theme-color); border-right:1px solid var(--border); + position:relative; transition:color .6s, border-color .6s; +} +.data-table th::after { + content:''; position:absolute; bottom:-1px; left:0; right:0; height:2px; + background:var(--theme-color); transform:scaleX(0); transition:transform .3s; + box-shadow:0 0 8px var(--theme-glow); +} +.data-table th:hover::after { transform:scaleX(1); } +.data-table th:last-child, .data-table td:last-child { border-right:none; } +.data-table td { + padding:.85rem 1.1rem; border-bottom:1px solid var(--border); + border-right:1px solid var(--border); color:var(--text-secondary); + transition:all .2s ease; vertical-align:middle; +} +.data-table tbody tr { transition:background .2s; animation:rowFadeIn .4s both; } +.data-table tbody tr:nth-child(1) { animation-delay:.05s; } +.data-table tbody tr:nth-child(2) { animation-delay:.10s; } +.data-table tbody tr:nth-child(3) { animation-delay:.15s; } +.data-table tbody tr:nth-child(4) { animation-delay:.20s; } +.data-table tbody tr:nth-child(5) { animation-delay:.25s; } +.data-table tbody tr:nth-child(6) { animation-delay:.30s; } +.data-table tbody tr:nth-child(7) { animation-delay:.35s; } +.data-table tbody tr:nth-child(8) { animation-delay:.40s; } +.data-table tbody tr:nth-child(9) { animation-delay:.45s; } +.data-table tbody tr:nth-child(10) { animation-delay:.50s; } +.data-table tbody tr:hover td { background:var(--theme-subtle); color:var(--text-primary); } +.data-table tbody tr:hover td:first-child { border-left:2px solid var(--theme-color); padding-left:calc(1.1rem - 2px); } +.col-desc { width:40%; min-width:280px; text-align:left !important; font-size:.85rem; line-height:1.55; } +.col-url { width:15%; max-width:320px; word-break:break-all; font-family:'Space Mono',monospace; font-size:.72rem; } +.col-url a { color:var(--theme-color) !important; text-decoration:none !important; opacity:.85; transition:opacity .2s, text-shadow .2s; } +.col-url a:hover { opacity:1; text-shadow:0 0 10px var(--theme-glow); text-decoration:underline !important; } +.hidden-row { display:none !important; } + +.show-more-btn { + display:inline-flex; align-items:center; gap:.5rem; + margin:1.5rem auto 0; padding:.6rem 1.75rem; + background:transparent; color:var(--theme-color); + border:1px solid var(--theme-color); border-radius:4px; cursor:pointer; + font-family:'Space Mono',monospace; font-size:.75rem; + letter-spacing:.15em; text-transform:uppercase; + position:relative; overflow:hidden; transition:all .25s ease; +} +.show-more-btn::before { + content:''; position:absolute; inset:0; + background:var(--theme-subtle); transform:scaleX(0); transform-origin:left; transition:transform .3s; +} +.show-more-btn:hover::before { transform:scaleX(1); } +.show-more-btn:hover { box-shadow:0 0 24px var(--theme-glow); transform:translateY(1px); } + +.site-footer { + text-align:center; padding:1.25rem 2rem; + background:rgba(2,4,8,.7); backdrop-filter:blur(10px); + color:var(--text-muted); font-family:'Space Mono',monospace; + font-size:.7rem; letter-spacing:.15em; + border-top:1px solid var(--border); + cursor:pointer; user-select:none; + transition:border-color .6s, background .2s; +} +.site-footer::before { content:'◈ '; color:var(--theme-color); transition:color .6s; } +.site-footer:hover { background:rgba(255,255,255,.03); } +.footer-credits-hint { opacity:.5; font-style:italic; transition:opacity .2s; } +.site-footer:hover .footer-credits-hint { opacity:1; color:var(--theme-color); } + +.daily-challenge-box { + margin:0 auto 2rem; max-width:680px; padding:1rem 1.5rem; + background:var(--theme-subtle); border:1px solid var(--border); + border-left:3px solid var(--theme-color); border-radius:8px; text-align:left; +} +.daily-challenge-title { + display:block; font-family:'Orbitron',monospace; font-size:.7rem; font-weight:700; + letter-spacing:.25em; text-transform:uppercase; + color:var(--theme-color); text-shadow:0 0 12px var(--theme-glow); margin-bottom:.5rem; +} +.daily-challenge-desc { font-size:.85rem; line-height:1.6; color:var(--text-secondary); } + +#quiz-container { + margin:0 auto 3rem; max-width:680px; padding:2.25rem 2.5rem; + background:rgba(255,255,255,.02); border:1px solid var(--border); border-radius:12px; + text-align:center; position:relative; overflow:hidden; + box-shadow:0 0 40px var(--theme-glow), inset 0 0 60px rgba(0,0,0,.3); + display:flex; flex-direction:column; min-height:400px; +} +#quiz-container::before { + content:''; position:absolute; top:0; left:0; right:0; height:2px; + background:linear-gradient(90deg,transparent,var(--theme-color),transparent); +} +#quiz-question { + font-size:.95rem; font-weight:500; line-height:1.6; + color:var(--text-primary); margin-bottom:1.5rem; + white-space:pre-line; text-align:left; flex-grow:1; + display:flex; align-items:center; min-height:120px; +} +#quiz-options { display:grid; grid-template-columns:1fr 1fr; gap:.75rem; margin-bottom:1rem; width:100%; } + +.quiz-btn { + background:rgba(255,255,255,.03); color:var(--text-primary); + border:1px solid var(--border); border-radius:8px; padding:.9rem 1.1rem; + font-family:'Space Grotesk',sans-serif; font-size:.9rem; font-weight:500; + cursor:pointer; text-align:left; position:relative; overflow:hidden; + transition:all .2s ease; + display:flex; align-items:center; min-height:54px; +} +.quiz-btn::before { + content:''; position:absolute; inset:0; + background:var(--theme-subtle); opacity:0; transition:opacity .2s; +} +.quiz-btn:hover:not(:disabled) { + border-color:var(--theme-color); box-shadow:0 0 16px var(--theme-glow); transform:translateY(-1px); +} +.quiz-btn:hover:not(:disabled)::before { opacity:1; } +.quiz-btn.correct { + background:rgba(74,222,128,.15) !important; border-color:#4ade80 !important; + color:#86efac !important; box-shadow:0 0 20px rgba(74,222,128,.3) !important; + animation:correctPulse .35s ease forwards; +} +.quiz-btn.correct::after { content:'✓'; position:absolute; right:1rem; top:50%; transform:translateY(-50%); color:#4ade80; pointer-events:none; } +.quiz-btn.wrong { + background:rgba(248,113,113,.15) !important; border-color:#f87171 !important; + color:#fca5a5 !important; box-shadow:0 0 20px rgba(248,113,113,.25) !important; + animation:shake .3s ease forwards; +} +.quiz-btn.wrong::after { content:'✗'; position:absolute; right:1rem; top:50%; transform:translateY(-50%); color:#f87171; pointer-events:none; } + +#quiz-score { + margin-top:1.5rem; font-family:'Orbitron',monospace; font-size:1.1rem; font-weight:700; + letter-spacing:.1em; color:var(--theme-color); text-shadow:0 0 20px var(--theme-glow); +} +#quiz-restart-btn { + display:none; margin-top:1.25rem; padding:.65rem 2rem; + background:transparent; color:var(--theme-color); + border:1px solid var(--theme-color); border-radius:6px; cursor:pointer; + font-family:'Space Mono',monospace; font-size:.75rem; + letter-spacing:.15em; text-transform:uppercase; transition:all .25s; +} +#quiz-restart-btn:hover { background:var(--theme-subtle); box-shadow:0 0 20px var(--theme-glow); transform:translateY(-1px); } + +#quiz-img-preview { + max-width:640px; margin:1.5rem auto 0; + border:1px solid var(--border); border-radius:10px; overflow:hidden; + background:rgba(0,0,0,.3); opacity:0; max-height:0; text-align:left; + transition:opacity .5s, max-height .5s, border-color .6s; +} +#quiz-img-preview.visible { opacity:1; max-height:480px; } +.quiz-img-label { + padding:.5rem 1rem; font-family:'Space Mono',monospace; font-size:.6rem; + letter-spacing:.2em; color:var(--theme-color); + border-bottom:1px solid var(--border); background:rgba(255,255,255,.02); +} +#quiz-img-preview img, #quiz-img-preview video { + display:block; width:100%; max-height:320px; background:#000; +} +#quiz-img-preview img { object-fit:contain; } +#quiz-img-preview video { outline:none; } +#quiz-img-preview img[src=""], #quiz-img-preview video[src=""] { display:none; } +.quiz-img-caption { + padding:.5rem 1rem; font-family:'Space Mono',monospace; font-size:.65rem; + color:var(--text-muted); word-break:break-all; border-top:1px solid var(--border); + white-space:nowrap; overflow:hidden; text-overflow:ellipsis; +} + +#credits-overlay { + position:fixed; inset:0; z-index:2000; + background:rgba(0,0,0,.7); backdrop-filter:blur(6px); + opacity:0; pointer-events:none; transition:opacity .35s; +} +#credits-overlay.open { opacity:1; pointer-events:all; } + +#credits-panel { + position:fixed; top:0; right:-480px; z-index:2001; + width:min(460px,95vw); height:100vh; + background:rgba(4,8,18,.97); backdrop-filter:blur(24px); + border-left:1px solid var(--border); + display:flex; flex-direction:column; + transition:right .4s cubic-bezier(.4,0,.2,1), border-color .6s; + overflow:hidden; +} +#credits-panel::before { + content:''; position:absolute; top:0; left:0; bottom:0; width:2px; + background:linear-gradient(180deg,transparent,var(--theme-color),transparent); + opacity:.6; +} +#credits-panel.open { right:0; } + +.credits-close { + position:absolute; top:1rem; right:1rem; + background:transparent; border:1px solid var(--border); border-radius:6px; + color:var(--text-muted); width:32px; height:32px; font-size:.8rem; cursor:pointer; + display:flex; align-items:center; justify-content:center; + transition:all .2s; z-index:1; +} +.credits-close:hover { border-color:var(--theme-color); color:var(--theme-color); box-shadow:0 0 10px var(--theme-glow); } + +.credits-header { padding:2.5rem 2rem 1.5rem; border-bottom:1px solid var(--border); flex-shrink:0; } +.credits-tag { + display:block; font-family:'Space Mono',monospace; font-size:.6rem; + letter-spacing:.25em; color:var(--theme-color); margin-bottom:.75rem; + text-shadow:0 0 10px var(--theme-glow); +} +.credits-title { + font-family:'Orbitron',monospace; font-size:1.4rem; font-weight:900; + letter-spacing:.1em; color:var(--text-primary); + text-shadow:0 0 30px var(--theme-glow); margin-bottom:.25rem; +} +.credits-version { font-family:'Space Mono',monospace; font-size:.7rem; color:var(--text-muted); letter-spacing:.1em; } + +.credits-body { flex:1; overflow-y:auto; padding:1.5rem 2rem; scrollbar-width:thin; scrollbar-color:var(--theme-color) transparent; } +.credits-section { margin-bottom:2rem; } +.credits-section h3 { + font-family:'Space Mono',monospace; font-size:.65rem; font-weight:700; + letter-spacing:.2em; text-transform:uppercase; color:var(--theme-color); + margin-bottom:.75rem; padding-bottom:.4rem; border-bottom:1px solid var(--border); +} +.credits-section p { font-size:.88rem; line-height:1.7; color:var(--text-secondary); } +.credits-section ul { list-style:none; display:flex; flex-direction:column; gap:.4rem; } +.credits-section ul li { + font-size:.85rem; color:var(--text-secondary); padding:.35rem .6rem; + border-left:1px solid var(--border); transition:border-color .2s, background .2s; +} +.credits-section ul li:hover { border-left-color:var(--theme-color); background:var(--theme-subtle); } +.credits-section ul li span { font-family:'Space Mono',monospace; font-size:.75rem; color:var(--theme-color); margin-right:.5rem; text-shadow:0 0 8px var(--theme-glow); } + +.credits-footer { padding:1rem 2rem; border-top:1px solid var(--border); font-family:'Space Mono',monospace; font-size:.62rem; letter-spacing:.15em; color:var(--text-muted); flex-shrink:0; } + +::-webkit-scrollbar { width:6px; } +::-webkit-scrollbar-track { background:var(--bg-deep); } +::-webkit-scrollbar-thumb { background:var(--theme-color); border-radius:3px; } +::selection { background:var(--theme-glow); color:#fff; } + +@keyframes layerReveal { + from { opacity:0; transform:translateY(16px); filter:blur(4px); } + to { opacity:1; transform:translateY(0); filter:blur(0); } +} +@keyframes rowFadeIn { + from { opacity:0; transform:translateX(-8px); } + to { opacity:1; transform:translateX(0); } +} +@keyframes marquee { + from { transform:translateX(100vw); } + to { transform:translateX(-100%); } +} +@keyframes glitch { + 0%,30%,50%,100% { text-shadow:0 0 20px var(--theme-glow); transform:translate(0); } + 10% { text-shadow:-2px 0 var(--theme-color),2px 0 rgba(255,0,100,.6); transform:translate(-1px,0); } + 20% { text-shadow:2px 0 var(--theme-color),-2px 0 rgba(0,200,255,.6); transform:translate(1px,0); } + 40% { text-shadow:-3px 0 rgba(255,0,100,.5),3px 0 var(--theme-color); transform:translate(2px,-1px); } + 60% { text-shadow:2px 0 rgba(0,200,255,.6),-1px 0 var(--theme-color); transform:translate(-1px,1px); } +} +@keyframes correctPulse { + 0%,100% { transform:scale(1); } + 40% { transform:scale(1.025); } +} +@keyframes shake { + 0%,100% { transform:translateX(0); } + 25% { transform:translateX(-5px); } + 50% { transform:translateX(5px); } + 75% { transform:translateX(-3px); } +} +@media (max-width:768px) { + #layer-Quiz .desc-box, #layer-Quiz .daily-challenge-box { display:none; } +} +@media (max-width:520px) { + header { padding:0 1rem; } + header h1 { font-size:.85rem; letter-spacing:.05em; text-align:center; } + .menu-icon, .help-btn { padding:.4rem .6rem; font-size:.7rem; } + #content-area { padding:1.5rem 1rem; } + .flex-row .box { min-width:100%; } + .desc-box { padding:1.25rem 1rem; font-size:.85rem; } + #quiz-options { grid-template-columns:1fr; gap:0.6rem; } + #quiz-container { padding:1.25rem 1rem; height:auto; min-height:auto; justify-content:center; overflow:visible; margin-bottom:1rem; } + #quiz-question { min-height:auto; font-size:.88rem; align-items:center; justify-content:center; text-align:center; margin-top:0; margin-bottom:1rem; } + .quiz-btn { min-height:44px; font-size:.8rem; padding:.6rem .75rem; justify-content:center; text-align:center; } + #quiz-img-preview.visible { max-height:none; margin-bottom:1rem; text-align:center; } + #quiz-img-preview img, #quiz-img-preview video { max-height:160px; width:auto; max-width:100%; margin:0 auto; } +} + +body.page-form { background-color: #0f172a; color: #e0e0e0; display: flex; flex-direction: row; justify-content: center; align-items: center; padding: 2rem; min-height: 100vh; overflow-x: auto; } +.admin-box { background-color: #1e293b; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); width: 100%; max-width: 500px; border: 1px solid #334155; } +.admin-box h1 { color: #00bfff; text-align: center; margin-bottom: 1.5rem; } +.form-group { margin-bottom: 1rem; } +.form-group label { display: block; margin-bottom: 0.5rem; color: #b9bbbe; font-size: 0.9rem; } +.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 0.75rem; background: #0f172a; border: 1px solid #334155; border-radius: 4px; color: white; font-family: inherit; } +.btn-submit { width: 100%; padding: 0.75rem; background-color: #00bfff; color: white; border: none; border-radius: 4px; font-weight: 600; cursor: pointer; margin-top: 1rem; transition: background 0.2s; } +.btn-submit:hover { background-color: #009acd; } +.links { text-align: center; margin-top: 1.5rem; display: flex; flex-direction: column; gap: 0.5rem; } +.links a { color: #94a3b8; text-decoration: none; font-size: 0.9rem; transition: color 0.2s; } +.links a:hover { color: #ffffff; } +input[type="file"] { cursor: pointer; padding: 0.6rem; } +input[type="file"]::file-selector-button { background: #334155; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; margin-right: 1rem; transition: background 0.2s; } +input[type="file"]::file-selector-button:hover { background: #475569; } + +/* Correction absolue pour App Mobile Native (Capacitor) */ +body.is-mobile-app #safe-area-block { height: max(env(safe-area-inset-top), 45px); } +body.is-mobile-app header { top: max(env(safe-area-inset-top), 45px); } +body.is-mobile-app .sidebar { padding-top: calc(5rem + max(env(safe-area-inset-top), 45px)); } +body.is-mobile-app .sidebar::before { top: calc(1.5rem + max(env(safe-area-inset-top), 45px)); } +body.is-mobile-app .credits-close { top: calc(1rem + max(env(safe-area-inset-top), 45px)); } \ No newline at end of file diff --git a/3ds/source/main.cpp b/3ds/source/main.cpp new file mode 100644 index 0000000..3600c8f --- /dev/null +++ b/3ds/source/main.cpp @@ -0,0 +1,92 @@ +// 3ds/source/main.cpp +#include <3ds.h> +#include +#include +#include + +// Définition des couleurs pour plus de clarté (Format: RGBA) +#define CLR_BG C2D_Color32(0x06, 0x0d, 0x18, 0xFF) // #060d18 +#define CLR_TEXT C2D_Color32(0xe8, 0xf0, 0xff, 0xFF) // #e8f0ff +#define CLR_PRIMARY C2D_Color32(0x4f, 0x8e, 0xff, 0xFF) // #4f8eff + +// Fonction pour lire un fichier depuis le romfs +char* readFile(const char* path) { + FILE* file = fopen(path, "r"); + if (!file) { + return NULL; + } + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + char* buffer = (char*)malloc(size + 1); + if (!buffer) { + fclose(file); + return NULL; + } + + fread(buffer, 1, size, file); + buffer[size] = '\0'; + fclose(file); + return buffer; +} + +int main(int argc, char* argv[]) { + // --- Initialisation --- + romfsInit(); + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); + + // Crée un "canevas" pour l'écran supérieur + C3D_RenderTarget* top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); + + // Prépare un buffer pour le texte + C2D_TextBuf textBuf = C2D_TextBufNew(4096); + C2D_Text text; + + // Lecture du fichier main.js + char* js_content = readFile("romfs:/main.js"); + + // --- Boucle principale de l'application --- + while (aptMainLoop()) { + hidScanInput(); + u32 kDown = hidKeysDown(); + + if (kDown & KEY_START) { + free(js_content); // Libère la mémoire avant de quitter + break; // Quitte l'application + } + + // --- Phase de dessin --- + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + + // Dessin sur l'écran du haut + C2D_TargetClear(top, CLR_BG); + C2D_SceneBegin(top); + + // Dessine du texte + C2D_TextBufClear(textBuf); + C2D_TextParse(&text, textBuf, "Lecture de romfs:/main.js reussie !"); + C2D_TextOptimize(&text); + C2D_DrawText(&text, C2D_WithColor, 20.0f, 20.0f, 0.5f, 0.5f, 0.5f, CLR_PRIMARY); + + if (js_content) { + C2D_TextParse(&text, textBuf, js_content); + C2D_TextOptimize(&text); + C2D_DrawText(&text, C2D_WithColor, 20.0f, 50.0f, 0.5f, 0.4f, 0.4f, CLR_TEXT); + } + + C3D_FrameEnd(0); + } + + // --- Libération des ressources --- + C2D_TextBufDelete(textBuf); + C2D_Fini(); + C3D_Fini(); + romfsExit(); + gfxExit(); + return 0; +} diff --git a/c_cpp_properties.json b/c_cpp_properties.json new file mode 100644 index 0000000..f2efac5 --- /dev/null +++ b/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "3DS DevkitARM", + "includePath": [ + "${workspaceFolder}/**", + "C:/devkitPro/libctru/include", // Chemin Windows standard + "/opt/devkitpro/libctru/include" // Chemin Linux/macOS standard + ], + "defines": [ + "__3DS__" + ], + "compilerPath": "C:/devkitPro/devkitARM/bin/arm-none-eabi-g++.exe", // Aide l'intellisense + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "windows-gcc-arm" + } + ], + "version": 4 +} \ No newline at end of file