Commit Graph
100 Commits
Author SHA1 Message Date
Adrien Ufferte 89b0ad05ec Merge pull request #327 from Excello-Recherche-Education/optimization
Optimization global
2026-06-11 12:03:20 +02:00
Adrien Ufferte b975b5e805 Debug possible corrupted scene file 2026-06-11 11:49:29 +02:00
Adrien Ufferte 30c1a9863f Optimization global 2026-06-11 11:16:03 +02:00
Adrien Ufferte 90ce89f880 Merge pull request #326 from Excello-Recherche-Education/debug
Debug
2026-06-11 10:42:47 +02:00
Adrien Ufferte dadc6f69ef Add missing file uid 2026-06-11 10:32:15 +02:00
Adrien Ufferte 5bba77c8ea Show a specific error message for each language pack failure
All failure paths of the download/extract/swap flow displayed the same
generic ERROR_DOWNLOADING popup, which made user reports impossible to
map to an actual cause (the recent .DS_Store swap failure surfaced as a
plain "download error" even though the download and extraction had both
succeeded).

- Replace the magic error indices with a DownloadError enum, so call
  sites read _show_error(DownloadError.REPLACE_FAILED) instead of
  _show_error(2) with a comment.
- Add three specific messages with fr/es/pt_BR/it translations:
  - ERROR_EXTRACTING_PACKAGE: archive missing or extraction failed
  - ERROR_INVALID_PACKAGE: extracted pack failed the validity check
    (kept distinct from INVALID_LANGUAGE_DIRECTORY, which describes the
    local pack found while offline)
  - ERROR_REPLACING_PACKAGE: previous pack could not be removed or the
    new one could not be renamed into place; tells the user to restart
    and retry rather than suggesting a download problem
- ERROR_DOWNLOADING is now used only for actual download/server
  failures.

Localization completeness check, naming lint and GUT suite (65/65) all
pass.
2026-06-11 10:23:14 +02:00
Adrien Ufferte 239643c325 Fix directory deletion failing on hidden files, breaking the pack swap
The new extract/verify/swap flow surfaced a long-standing bug in
Utils.clean_dir(). On macOS, browsing a folder in the Finder drops a
hidden .DS_Store file in it, and clean_dir() had two flaws around that:

- DirAccess listings skip hidden files by default, so .DS_Store was
  never listed nor removed and the directory was never actually empty.
- The return value of dir.remove(subfolder) was ignored, so the failed
  removal of a subdirectory (non-empty because of its own hidden file)
  was silently swallowed and clean_dir() still reported success.

As a result, delete_directory_recursive() emptied the old language pack
but could not delete its directory, the rename of the new pack onto
that still-existing directory failed, and the downloader showed the
download-error popup. Since the version on disk was never updated, the
app re-downloaded and failed again in a loop on every launch. The old
pre-swap flow had the same deletion failure but masked it: it extracted
directly into the existing directory and ignored rename errors.

- clean_dir() now lists hidden files (include_hidden) and checks every
  removal, trying to remove everything (best effort, so callers like
  clear_all_local_data still wipe as much as possible) and reporting
  the first error encountered instead of a false OK.
- The downloader verifies that the previous pack directory is really
  gone before renaming the new one into place; if not, it aborts the
  swap, keeps the temporary directory and shows the error instead of
  attempting a rename that cannot succeed.
- Add regression tests covering hidden-file deletion, full content
  cleanup and the missing-directory error path.

Verified with the GUT test suite (65/65 passing).
2026-06-11 10:15:40 +02:00
Adrien Ufferte 0b9d5f216e Extract language packs to a temp directory and verify before swapping
Follow-up to the previous download fix: the old pack was no longer
deleted before the download, but it was still deleted at the start of
the extraction step. If the app crashed mid-extraction or the archive
was truncated, the device was left without a valid pack; worse, a
partially extracted directory containing language.db would pass
is_language_directory_valid() on the next offline launch and be
accepted as a working pack.

The extraction thread now follows an extract / verify / swap sequence:

1. Extract the archive into <lang>_tmp instead of the final location,
   leaving the current pack untouched and avoiding any risk of merging
   old and new files.
2. Validate the extracted pack (non-empty, language.db present) before
   touching the current one; on failure, discard the temp directory and
   the archive, show the invalid-directory error, and keep playing with
   the current pack.
3. Only then delete the previous pack and rename the new one into
   place. The window without a valid pack shrinks from the whole
   extraction to two filesystem operations.

If the final rename fails, the temp directory is kept (the data is
intact on disk) and the error popup is shown; the next launch detects
the missing pack and downloads it again. A leftover temp directory from
a crash is removed at the start of the next extraction.

Verified with the GUT test suite (62/62 passing).
2026-06-11 09:44:50 +02:00
Adrien Ufferte 896e2740b3 Compress large textures to VRAM formats and enable 2D mipmaps
All 747 imported PNGs were imported losslessly (compress/mode=0) without
mipmaps. Lossless textures are decompressed to raw RGBA8 in GPU memory,
so the big gameplay assets were extremely expensive: the 4100x2448
bush_curtain alone used ~40 MB of VRAM, and the 6400x8001 minigame
spritesheets ~205 MB each. Summed over the affected files, the
worst-case footprint was ~2.4 GB of texture memory for a game that
targets low-end 1-2 GB tablets with the gl_compatibility renderer. The
absence of mipmaps also caused shimmering and wasted bandwidth whenever
these textures were drawn below their native size, which is almost
always the case with the 2560x1800 canvas_items stretch.

- Switch the 119 textures with a dimension >= 512 px to VRAM
  compression (compress/mode=2) with generated mipmaps. The project
  already enables etc2_astc import, so mobile gets ETC2/ASTC and
  desktop gets S3TC/BPTC. The total footprint for these textures drops
  from ~2.4 GB raw RGBA8 to ~770 MB including mipmaps (~3x smaller
  resident set, plus faster texture uploads).
- Keep small UI textures (< 512 px, 626 files) lossless: their VRAM
  cost is negligible and block-compression artifacts would be most
  visible on small crisp UI art.
- Set the default 2D texture filter to Linear Mipmap so the generated
  mipmaps are actually sampled by canvas items (without this, mipmap
  generation would only add memory).
- Remove viewport/hdr_2d=true: HDR 2D allocates RGBA16F framebuffers,
  doubling framebuffer memory and bandwidth at 2560x1800 on exactly the
  GPUs that can least afford it. No scene uses glow or HDR values (no
  WorldEnvironment anywhere), so this was pure cost. As false is the
  default value, Godot drops the line entirely.

Texture quality should be eyeballed in the editor on the largest
illustrations; any texture where compression artifacts are noticeable
can be switched back to lossless individually.

Verified with the GUT test suite (62/62 passing).
2026-06-11 08:45:57 +02:00
Adrien Ufferte 3b597d92d5 Debug test parsing error 2026-06-11 08:15:31 +02:00
Adrien Ufferte 921d13b0cb Keep the current language pack until the new one is downloaded
The package downloader deleted the installed language pack as soon as a
version difference with the server was detected, before the download had
even started. If the download then failed (connection drop, server
error), the device was left with an empty language directory: on the
next offline launch, the directory check failed and the app was unusable
until internet access was restored.

The early cleanup was also redundant: _copy_data() already removes the
existing directory right before extracting the new archive, i.e. after
the download has succeeded (HTTP 200).

Changes in package_downloader.gd:
- Remove the pre-download cleanup of the current language directory; the
  previous pack now stays usable for offline play until the new one has
  been fully downloaded.
- Check the return value of HTTPRequest.request() and show the download
  error popup when the request cannot be started, instead of leaving the
  screen waiting forever on a request that was never sent.
- Surface extraction failures (missing archive, unzip error) through the
  error popup instead of silently returning from the extraction thread
  and leaving the user stuck on a frozen progress screen.

Changes in login.gd:
- Handle the result of UserDataManager.login_student(), which was
  previously logged and ignored. The synchronization performed right
  before the login can delete or move the student locally (e.g. the
  server requested a local deletion), in which case the child was sent
  to the gardens scene with no student session loaded, ending up on a
  dead screen. On failure, play the wrong-password feedback and reset
  the code keyboard instead of changing scene.

Verified with the GUT test suite (58/58 passing).
2026-06-11 08:09:56 +02:00
Adrien Ufferte c77e9b90ec Fix synchronization reliability: freed popup refs and unvalidated payloads
The UserDatabaseSynchronizer outlives the scenes that use it, which made
the periodic background synchronization fragile in two ways:

1. A freed loading popup permanently broke the sync.
   The teacher settings screen injects its LoadingPopup into the
   synchronizer but never cleared the reference on scene exit. Since a
   freed Object is not equal to null in Godot 4, the next timer-driven
   synchronization called into the freed node, crashed the coroutine
   after `synchronizing` had been set to true, and every subsequent
   synchronize() call was then silently cancelled by the re-entrancy
   guard until the app restarted.
   - All popup guards now use is_instance_valid() (plus is_inside_tree()
     where get_tree() is needed).
   - The teacher settings screen clears the synchronizer's popup
     reference in _exit_tree().

2. Server payloads were applied without shape validation.
   _apply_server_response() indexed JSON fields (lesson unlock data,
   remediation score tuples, confusion matrix entries) without checking
   their types, as flagged by the five "TODO ADD SECURITY" comments. A
   single malformed or legacy record raised a script error mid-sync,
   which also wedged the `synchronizing` flag as described above.
   - Lesson unlock entries, remediation score pairs and confusion matrix
     entries are now validated before use; malformed records are logged
     and skipped individually so one bad record cannot abort the whole
     synchronization or corrupt local student data.
   - The three copy-pasted remediation parsing blocks are factored into
     a single _parse_score_remediation() helper.

Verified with the GUT test suite (58/58 passing).
2026-06-10 17:00:31 +02:00
Adrien Ufferte 48ad70d7db Debug completed game color on the minigame wheel 2026-06-10 09:58:39 +02:00
Adrien Ufferte e21c3aec21 Debug prof_tool 2026-06-09 16:31:56 +02:00
Adrien Ufferte 10e66ffffc Merge pull request #325 from Excello-Recherche-Education/optimize-turtle-minigame
Optimize turtle minigame
2026-06-09 15:46:43 +02:00
Adrien Ufferte 87c53ba5c0 Dynamically adapt POOL SIZE for ring effect 2026-06-09 12:21:40 +02:00
Adrien Ufferte 4ee513c543 Handle turtle in boss minigame 2026-06-09 11:50:40 +02:00
Adrien Ufferte f50a739b3d Optimize turtle minigame 2026-06-09 11:45:50 +02:00
Adrien Ufferte 708f0fe091 Auto-heal language account at every login 2026-06-09 10:52:42 +02:00
Adrien Ufferte be2ddba6db Merge pull request #324 from Excello-Recherche-Education/prevent-crash-frog
Prevent crash when launching frog minigame
2026-06-09 10:07:08 +02:00
Adrien Ufferte 07770f0a01 Prevent crash when launching frog minigame 2026-06-09 09:40:12 +02:00
Adrien Ufferte 94c841bf7a Debug device selection list too long 2026-06-08 18:11:48 +02:00
Adrien Ufferte cb2edd07de Export settings 2026-06-08 17:51:11 +02:00
Adrien Ufferte 5fffa870b1 Change version number 2026-06-06 19:23:19 +02:00
Adrien Ufferte 3da2428f55 Merge pull request #323 from Excello-Recherche-Education/wheel
New minigame wheel system
2026-06-06 19:21:17 +02:00
Adrien Ufferte fcd84fdb35 Protect against out of bounds for minigame index 2026-06-06 18:11:45 +02:00
Adrien Ufferte fe696c8b20 debug lessons numbers different from 3 2026-06-06 17:44:17 +02:00
Adrien Ufferte 2db21b597b Minigames wheel version 2 2026-06-06 11:13:22 +02:00
Adrien Ufferte 42118fad1b Merge pull request #322 from Excello-Recherche-Education/prevent-crash
Prevent boss minigame crash
2026-06-01 09:52:31 +02:00
Adrien Ufferte bab4d8da27 Prevent boss minigame crash 2026-06-01 09:46:41 +02:00
Adrien Ufferte 3146a33b08 Merge pull request #321 from Excello-Recherche-Education/stars-colors
New star colors in background of gardens
2026-05-28 16:58:05 +02:00
Adrien Ufferte a5c374ab24 New star colors in background of gardens 2026-05-28 16:27:00 +02:00
Adrien Ufferte 8e65df45b8 Merge pull request #320 from Excello-Recherche-Education/debug
Security if language pack has less than 3 exercices in lesson
2026-05-28 15:53:16 +02:00
Adrien Ufferte 6a45cd4fc2 Security if language pack has less than 3 exercices in lesson 2026-05-28 15:42:15 +02:00
Adrien Ufferte 268c7e49cf Merge pull request #319 from Excello-Recherche-Education/help-speech-one-time
Ensure the help speech of Kalulu is triggered only once
2026-05-28 15:11:37 +02:00
Adrien Ufferte 0ac4967486 Ensure the help speech of Kalulu is triggered only once when the threshold is crossed, and not every mistake after that. 2026-05-28 13:38:11 +02:00
Adrien Ufferte 612d58496a Merge pull request #318 from Excello-Recherche-Education/new-garden-02
New gardens
2026-05-28 12:56:38 +02:00
Adrien Ufferte 2c2e6f654a Debug boss button texture 2026-05-28 12:05:30 +02:00
Adrien Ufferte 14f1d68a47 Update boss button 2026-05-28 11:59:10 +02:00
Adrien Ufferte 9e512ec4a5 Boss buttons does not stop scrolling 2026-05-28 11:42:27 +02:00
Adrien Ufferte 0e55e5d464 Small corrections for gardens assets 2026-05-28 10:29:23 +02:00
Adrien Ufferte c9bdedb576 Bold font for lesson buttons 2026-05-28 10:29:10 +02:00
Adrien Ufferte ba8646c1bd Place clouds behind UI buttons 2026-05-28 10:21:51 +02:00
Adrien Ufferte d55c05b53d Debug clouds "world bounds" 2026-05-28 09:31:11 +02:00
Adrien Ufferte 8f4a1be939 Cleaning code for cloud manager 2026-05-26 16:40:56 +02:00
Adrien Ufferte 606b87d1b2 Last 3 gardens 2026-05-26 15:56:41 +02:00
Adrien Ufferte ab1ad7f27e Red Parakeet garden (09) 2026-05-26 14:30:58 +02:00
Adrien Ufferte 847f04c5a7 Monkey garden (08) 2026-05-26 14:11:52 +02:00
Adrien Ufferte b16580094e Frog garden (07) 2026-05-26 13:47:58 +02:00
Adrien Ufferte e5d628897c Garden Caterpillar (06) 2026-05-26 11:32:46 +02:00
Adrien Ufferte fdd597f5d6 Yellow Parakeet garden (05) 2026-05-26 11:08:06 +02:00
Adrien Ufferte 942a3b47ec Gardens animals transform normalization 2026-05-26 10:55:25 +02:00
Adrien Ufferte 953260c31d crabs garden 2026-05-26 10:51:29 +02:00
Adrien Ufferte 7ce680d14c Ant garden (03) 2026-04-30 16:15:13 +02:00
Adrien Ufferte bebc449f56 cleaning 2026-04-30 16:15:13 +02:00
Adrien Ufferte 6f0163a8db Stratify garden clouds in scroll-space, not world-space
Initial spawning placed clouds uniformly across [0, spawn_width], which
broke down at the edges of the scroll range whenever the parallax factor
was anything other than exactly 1.0:

- With parallax < 1.0 (background feel), a cloud is only ever visible at
  scrolls in [(drift_x - screen_w) / parallax, drift_x / parallax]. So a
  far cloud at drift_x near spawn_width (e.g. 28000 with parallax 0.5)
  needs scroll ~56000 to be seen, far beyond max_scroll. About half the
  spawn range was a dead zone the user could never reach.

- With parallax > 1.0 (foreground feel — what the gardens scene now uses),
  the opposite happens: clouds visible at the right edge of the scroll
  range live at drift_x = max_scroll * parallax + screen_w, which is well
  past spawn_width. The right side of the gardens stayed empty because no
  cloud was ever spawned that far out.

Add a max_scroll parameter to configure_world() and stratify in scroll
space: each cloud gets a target_scroll value in its own slot of
[0, max_scroll], and its drift_x is placed at target_scroll * parallax +
horiz_jitter. The cloud's drift_x range now matches the scroll positions
where it can actually be visible, regardless of whether parallax is below
or above 1.0. _reset_cloud is updated correspondingly to wrap clouds to
the right edge of their individual visibility range rather than the
world's right edge.

Also have configure_world refresh screen_width every call so the manager
picks up the correct viewport size even when the parent invokes it later
in the ready sequence.
2026-04-30 16:15:13 +02:00
Adrien Ufferte ad571da545 Spread initial cloud spawn positions via stratified sampling
Replace pure randf_range across the spawn width with stratified placement:
the spawn range is split into N equal slots (N = number of clouds) and each
cloud is placed at a random position inside its own slot. This caps the
maximum gap between clouds at one slot width, eliminating the "all clouds
on one side" failure mode of independent random draws while keeping each
cloud's exact position random within its band so the layout still feels
natural rather than grid-regular.

The sprite list is shuffled before slot assignment so identical duplicated
templates don't end up in adjacent slots in scene-tree order.
2026-04-30 16:15:13 +02:00
Adrien Ufferte 63436f1782 Anchor garden clouds to world coordinates for recycling
Previously clouds in the gardens recycled based on their rendered (viewport)
position. When the user scrolled to the far right, parallax pushed every
cloud's rendered X far into the negative, triggering all of them to wrap
to the right viewport edge. Their drift_x was thus warped to roughly
scroll_offset + screen_width — so when the user scrolled back left, every
cloud sat in the world to the right of the visible area, leaving the left
side of the gardens empty.

Switch CloudsManager to use world-anchored recycling whenever spawn_width
is set: a cloud is only recycled when its drift_x (its world coordinate)
falls past the world's left edge, and it respawns at the world's right edge
rather than the viewport's. The legacy viewport-anchored path is preserved
for the minigames that don't set spawn_width.
2026-04-30 16:15:13 +02:00
Adrien Ufferte de395ab036 Add scrolling clouds to the gardens scene
Extend CloudsManager with optional scroll-driven parallax and auto-population:

- New scroll_offset property feeds an external horizontal scroll value;
  each cloud's rendered X is drift_x - scroll_offset * parallax_factor.
- min_parallax_factor / max_parallax_factor are interpolated by the same
  depth factor that drives speed and scale, so closer clouds parallax more.
- spawn_width + clouds_per_screen + configure_world() let the manager
  duplicate its template children to cover wider scrollable worlds.
- Defaults preserve the previous behavior, so parakeets / penguin / monkeys
  / boss minigames keep their existing per-frame drift unchanged.

In gardens.tscn, add a CloudsLayer (CanvasLayer, layer = 1) on top of the
existing UI with three template cloud sprites (transparent via modulate
alpha, mouse passthrough by virtue of being Sprite2D). gardens.gd configures
the cloud world width to garden_count * GARDEN_SIZE after lessons are set
up, then forwards scroll_container.scroll_horizontal to clouds.scroll_offset
each frame so clouds drift naturally and parallax with garden scrolling.
2026-04-30 16:15:13 +02:00
Adrien Ufferte a58e59f656 New garden 02 (and cleaning) 2026-04-30 16:15:13 +02:00
Adrien Ufferte 5553394a2c Merge pull request #317 from Excello-Recherche-Education/prof-tools-bug
Prof tool 1.4.1 export settings
2026-04-30 16:09:19 +02:00
Adrien Ufferte e6a8655bb8 Prof tool 1.4.1 export settings 2026-04-30 16:07:49 +02:00
Adrien Ufferte e35b3766e1 Merge pull request #316 from Excello-Recherche-Education/prof-tools-bug
Debug save sentences modified
2026-04-30 15:52:42 +02:00
Adrien Ufferte 750fe21da2 Debug save sentences modified 2026-04-30 15:50:11 +02:00
Adrien Ufferte 728819cac1 Fix ScrollContainer single-child warning in gardens scene
Move BossButtons, LockedLine and UnlockedLine out of the ScrollContainer
so it only contains the HBoxContainer that drives its scroll size.
BossButtons now tracks scrolling via a manual position offset in _process,
mirroring what the lines and parallax background already do.
2026-04-28 14:00:45 +02:00
Adrien Ufferte 6b86d35dfe Penguin text alignment 2026-04-28 12:06:34 +02:00
Adrien Ufferte 512038313c debug-coconut-text 2026-04-27 15:53:22 +02:00
Adrien Ufferte c4f59ef274 Merge pull request #315 from Excello-Recherche-Education/penguin-signs
Take opener and terminator sign in sentences for the penguin minigame
2026-04-27 13:57:59 +02:00
Adrien Ufferte 810f504a16 Take opener and terminator sign in sentences for the penguin minigame 2026-04-27 13:45:45 +02:00
Adrien Ufferte a6455cbbe5 Merge pull request #314 from Excello-Recherche-Education/turtle-text-center
Centers text on turtles, and make font a little smaller to fit double…
2026-04-27 11:10:02 +02:00
Adrien Ufferte f95ff50c15 Centers text on turtles, and make font a little smaller to fit double letters 2026-04-27 11:06:48 +02:00
Adrien Ufferte 6f00c4cc4b Merge pull request #313 from Excello-Recherche-Education/turtles-disappear-speed
Turtles disappear quickly when colliding with each other
2026-04-27 10:49:33 +02:00
Adrien Ufferte 91f4d57ef7 Turtles disappear quickly when colliding with each other 2026-04-27 10:46:11 +02:00
Adrien Ufferte f1b16cde17 Merge pull request #312 from Excello-Recherche-Education/parakeets-letters
Parakeets maximum letters = 1
2026-04-24 16:07:27 +02:00
Adrien Ufferte 06019e97d2 Parakeets maximum letters = 1 2026-04-24 15:57:26 +02:00
Adrien Ufferte b80eac1313 Merge pull request #311 from Excello-Recherche-Education/debug-highlight
Debug highlight fx system
2026-04-24 15:44:22 +02:00
Adrien Ufferte cf46d84b56 Debug highlight fx system 2026-04-24 12:06:13 +02:00
Adrien Ufferte a37dbd9dd7 Merge pull request #310 from Excello-Recherche-Education/debug-difficulty
Debug difficulty system
2026-04-24 11:09:07 +02:00
Adrien Ufferte 1d387d1ea6 Debug difficulty system 2026-04-24 10:41:40 +02:00
Adrien Ufferte a1faf7e154 Merge pull request #309 from Excello-Recherche-Education/add-moving-clouds
Add moving clouds
2026-04-24 09:24:18 +02:00
Adrien Ufferte d4c883da47 Add moving clouds to penguin minigame 2026-04-23 17:09:06 +02:00
Adrien Ufferte 9c6ffa1b28 Add clouds to monkeys minigame 2026-04-23 17:03:11 +02:00
Adrien Ufferte 0a51a42410 Merge pull request #308 from Excello-Recherche-Education/optimize-monkeys-minigame
Optimize monkeys minigame
2026-04-23 16:54:39 +02:00
Adrien Ufferte 3c4d85f032 Nomenclature 2026-04-23 16:43:33 +02:00
Adrien Ufferte 915b6c45ee Further optimize monkeys initialization 2026-04-23 16:33:24 +02:00
Adrien Ufferte d2b406da06 Optimize monkey architecture for coconut and fx 2026-04-23 16:09:35 +02:00
Adrien Ufferte bac87eb46a Add frame delay in _setup_minigame() to optimize minigame loading and avoid ram overload 2026-04-23 15:49:36 +02:00
Adrien Ufferte 3d8399a341 Merge pull request #307 from Excello-Recherche-Education/Improve-boss-score-gauge
Better color and special effect when victory threshold reached
2026-04-23 15:17:35 +02:00
Adrien Ufferte 72c01cc338 Better color and special effect when victory threshold reached 2026-04-23 15:15:01 +02:00
Adrien Ufferte 00face5e26 Merge pull request #306 from Excello-Recherche-Education/ants-sentence-position
Ants sentence position
2026-04-23 13:56:24 +02:00
Adrien Ufferte d9a1a35e13 Debug long sentences and background positioning 2026-04-23 12:02:33 +02:00
Adrien Ufferte 8e58ad537f Center sentence 2026-04-23 12:02:14 +02:00
Adrien Ufferte 195f2305d5 Position lower and more to the right 2026-04-23 11:30:56 +02:00
Adrien Ufferte a71f451ecc Merge pull request #305 from Excello-Recherche-Education/ants-travel-together
Ants spawn all at once and travel together
2026-04-23 11:22:10 +02:00
Adrien Ufferte b307c86e4f Ants spawn all at once and travel together 2026-04-23 11:19:50 +02:00
Adrien Ufferte 9bc76488c0 Merge pull request #304 from Excello-Recherche-Education/debug-sparks-fx
Sparks fx now appears correctly on the "first" video button of look a…
2026-04-23 10:40:19 +02:00
Adrien Ufferte 9d518d038f Sparks fx now appears correctly on the "first" video button of look and learn. 2026-04-23 10:36:35 +02:00
Adrien Ufferte 193ac84e15 Merge pull request #303 from Excello-Recherche-Education/dev-option-larger-input
Larger button for developer menu
2026-04-20 12:09:34 +02:00
Adrien Ufferte 9afd95213a Larger button for developer menu 2026-04-20 12:00:47 +02:00
Adrien Ufferte 32ecaf698a Merge pull request #302 from Excello-Recherche-Education/readable-login-error
Readable login error
2026-04-20 11:10:48 +02:00