- Fix the toolbar wrapping onto two rows: its minimum width was tuned
for an older, narrower button set and never widened back out after
reverting to bare icon buttons.
- Drop the "Time elapsed"/"Ctrl+Enter" readout next to Submit - it was
clutter; the underlying timer logic keeps running unseen.
- Move the RanaZe brand mark above the question-count row instead of
sharing a line with it.
- Progress text drops the word "Question" everywhere ("1 of 10"), and
core/dynamic sessions now cycle against a configurable block size
(Settings > Question block size, default 10) instead of counting up
forever.
Replace the default Godot boot splash and exe icon with the RanaZe
mark so a Windows build reads as RanaZe from first launch, not the
engine it's built on.
Varying label widths (especially when a button hides itself, e.g.
"Labs" on a question with no lab results) broke even spacing between
icons. Every toolbar button already has a tooltip_text carrying the
same label as hover text, so drop the labels/wrapper containers
entirely and go back to plain, uniformly-spaced 44x44 icon buttons.
- A toolbar icon that hides itself at runtime (labs, notes,
flashcard, bookmark) left its text label behind, showing as an
orphaned label floating in a gap. The label's wrapper now tracks
the button's own visibility.
- The theme toggle used a distinct circular pill shape; changed it
to match the same rounded-square box every other toolbar icon uses.
The 11 labeled groups are wider than the old bare-icon buttons, so
the toolbar's allotted minimum width (tuned for the old layout) was
too narrow and forced a wrap onto two rows. Widened it and raised
the compact-mode breakpoint to match.
Core sessions show a per-question stopwatch that counts up (no fixed
deadline, just a pacing signal), not a countdown - it was incorrectly
labeled "Time remaining" like the simulation/Timed countdown; now
reads "Time elapsed" to match what the number actually does.
The 6-item + "More" overflow popup made the toolbar feel sparse and
hid frequently-used actions (flag, report, flashcard, theme) behind
an extra click. Reverting to a flat toolbar where every action is
directly visible and labeled, including text size (grouped A-/A+
under one "Text size" caption instead of behind a popup).
Adapts the quiz screen's header, toolbar, choice cards, and bottom bar
to the designer's new layout:
- Header: adds the RanaZe logo/wordmark and a question-progress bar +
percentage alongside the existing "Question N of M" text.
- Toolbar: consolidates 12 icon-only/chip buttons into 6 labeled items
(Text size, Calculator, Clear highlight, Notes, Bookmark, More), with
a new "More" popup holding the rest (labs, flag, report, flashcard,
theme toggle, feedback). The underlying buttons are reparented, not
duplicated, so every existing theming/visibility/state call site
keeps working unmodified.
- Category tag: shows the question's discipline (falling back to
system) above the stem.
- Choice cards: every choice now renders as a subtly-bordered rounded
card even before answering, not just a flat divider row; the
currently-selected pre-submit choice shows a checkmark.
- Bottom bar: merges the Skip/Submit row and the timer row into one -
Skip as a plain link, a centered "X of Y" progress bar, and a
time slot that shows the existing countdown when a limit applies or
a new count-up stopwatch ("Time elapsed") otherwise, plus a
Ctrl+Enter/Cmd+Enter submit shortcut with a visible hint.
Replaces the OS/window icon with the new dark-background logo design,
and derives a transparent shield-mark cutout (via chroma-key against
the flat background) for the in-app login/sidebar mark and the web
loading-screen logo. Brand.ranaze_mark_texture() no longer needs the
AtlasTexture crop since the new source asset is already tightly
cropped to the mark.
Previously only the correct choice and any choice the student actually
selected got a percentage badge once reviewing; never-selected
distractors were left blank. Now every choice shows its selection
percentage as soon as the question is answered, matching the earlier
change that made unselected distractors render red like the rest of
the review UI.
node_added already fires once per node as it enters the tree (including
every descendant of a newly-added subtree), so the recursive call_deferred
in _on_node_added was redundant and held a raw Node reference across a
frame boundary. If that node got freed before the deferred call fired
(e.g. a container torn down and rebuilt the same frame), the engine's
message queue couldn't marshal the argument and threw "Cannot convert
argument 1 from Object to Object". Wiring synchronously removes the
window entirely.
Also silences the intentional integer division in _duration_text,
matching the same annotation already applied to _elapsed_simulated_text.
Renames locals/params that shadowed base-class properties (Node.name,
Control.size, BoxContainer.alignment), prefixes an intentionally-unused
parameter, and annotates a deliberate integer division so the console
stays clean during development.
The connected-accounts "Connect" button in progress_profile.gd
referenced res://assets/icons/lucide/link.svg, which didn't exist,
causing a resource-load error whenever the Steam/Google linking row
rendered.
Steam uses a theme-aware monochrome glyph (light/dark variants) since
the button background flips between navy and white; Google keeps its
official 4-color mark, which reads fine on either background.
Guarded the answer-review render path (teaching cards, review
follow-ups, distractor explanations) on current_answer/pending_correct_advance
directly rather than solely on correct_review_panel.visible, and added
the same guard inside _render_unselected_distractor_explanations
itself. This closes a path where a fresh, never-answered question
could show wrong-answer explanations immediately, before any
selection or submission.
Also: unselected distractors now render in the same red "wrong" card
style as selected/locked-wrong choices once a question is being
reviewed, instead of staying in the plain default row style. And
removed the redundant percentage badge that was floating above the
in-progress follow-up quiz prompt, since the choice button above it
already shows the same percentage.
Two real gaps meant the account-synced zoom (from the previous
"sync through the account" change) never reached the screen:
_load_cache() updated AccountProfileService.preferences but never
emitted profile_changed, so question_player.gd/ccs_service.gd (already
built and listening before login even happens) never found out a
cached value had loaded. And nothing forced a server refresh
right after login - it only fired if the user happened to open the
profile/progress screen, so a fresh device/session had no path to
the true synced value at all.
main.gd now calls AccountProfileService.refresh() once per login
(not on every silent token refresh) so the real preferences GET
always runs right after signing in, on any device.
Also added Question bank/CCS case text size steppers to the Settings
screen next to dark mode, per user request, reusing the same
AccountProfileService/CCSService plumbing as the in-screen A-/A+
buttons.
Drop the redundant header row (duplicate percentage badge + collapse
chevron), the "Why this is appropriate/less appropriate:"/"Why this
is incorrect:" heading labels, and the nested sub-box - now that
these cards live inside their own choice's colored border, all of
that was repeating what the color and position already say. Both the
selected-wrong-choice resolved follow-up and the never-selected
distractor cards now just show the plain explanation sentence, with
the leading "Correct."/"Incorrect." verdict word stripped too.
Removing the toggle chevron removes the only way to reach the
collapse/expand behavior, so that machinery (_collapse_resolved_followups,
_toggle_followup_state, the "collapsed" state field) comes out too
rather than linger unreachable.
Local user://settings.cfg meant zoom level didn't follow a student
across devices/browsers. Question bank and CCS text scale now live on
the user's study profile in Mongo (QuestionTextScale/CcsTextScale),
synced through a new PUT /api/profile/me/text-scale endpoint and the
existing preferences GET/cache already used for study preferences.
Both question_player.gd and ccs_service.gd read the synced value at
startup and react to AccountProfileService.profile_changed so a value
pulled down after login (or changed from another session) applies
without a restart. Backend validates the 0.85-1.35 range and updates
only the field(s) provided via a targeted $set, so it can't clobber
the other scale or the unrelated study-profile preferences.
Question bank and CCS case text-size (A-/A+) previously reset to 1.0
on every app launch. Both now load their saved scale from
user://settings.cfg at startup and save on every adjustment, mirroring
the existing pattern used for dark/light mode and sound preferences.
The flag-media-questions CLI command only caught questions that
existed in the DB when it ran once. Any question created, imported,
or edited afterward could add media without ever being flagged for
review, since nothing gated new writes going forward. CreateAsync,
UpdateAsync, and UpsertManyByExternalIdAsync now all run new/updated
questions through ApplyMediaReviewGate, which flags any question that
has media and isn't already flagged.
The "WHY OTHER OPTIONS ARE INCORRECT" aggregate section duplicated
explanations that selected wrong choices already showed inside their
own resolved follow-up quiz. Removed that section entirely; choices
the user never picked now get their "why incorrect" explanation
docked directly inside their own card (reusing the same followup
panel/attached-card styling), so every distractor's explanation lives
with its choice instead of in a separate list at the bottom.
The choice button above the panel already shows the letter badge,
choice text, and percentage, so repeating them in the panel header
was redundant now that the panel is docked inside the choice's card.
Keeps the collapse toggle, now named FollowupToggle since the old
FollowupSummary button it lived on is gone.
Now that the explanation panel is docked inside the correct choice's
own green card, the heading was redundant with the badge/border color
already conveying it's the correct answer.
Flag question now uses a clean vector flag (dark/light SVG variants,
replacing a hand-drawn pixel-art icon that got muddied by the shared
toolbar icon-tint override). Report question is now a megaphone
instead of a second flag glyph, since both toolbar buttons looked
identical before.
The correct-answer explanation and the wrong-answer follow-up quiz
previously rendered as separate bordered boxes sitting below the
entire choice list. Both now dock inside their own choice's card:
the explanation panel reparents onto the correct choice's row, the
follow-up panel was already there but visually detached. A shared
_attached_panel_box style drops the shared border/corner between the
choice button and its panel so they read as one continuous card.
Also drop a stale custom_minimum_size(0, 54) reset left over in
_set_choice_button_text from before the answer row redesign - it was
silently overriding the button's real 68px minimum height on every
question load and on every elimination/strikethrough toggle, which is
why choice rows never actually got taller and why striking a choice
through visibly shrank its card.
Fixes the large empty gap below the answer choices reported after the
answer-row redesign - the stem+choices VBox was pinned to the top of an
expand-filled scroll panel with all the leftover height sitting unused
below choice E. Now centers that block when unanswered and reverts to
top alignment once the explanation/review content grows underneath it.
StyleBoxFlat-drawn circles kept looking rough at this size even after
tuning corner detail/antialiasing, so the badges are now two small SVG
assets (outline and filled) tinted per-state via modulate instead -
crisp regardless of size. Also increased row height, badge size, and
margins across the board; the previous pass's padding bump was too
timid and the rows still read as cramped.
icon_circle_box's perfectly-circular StyleBoxFlat (corner radius equal
to half the box size, no straight edges between corners) is a known
Godot degenerate case that draws a faint seam through the middle when
antialiasing is on and looks faceted at low corner_detail. Bumping
corner_detail and disabling antialiasing renders a clean circle at
these small badge/icon sizes instead - affects every icon_circle_box
call site (answer badges, stat tiles, avatars, etc.), not just the
question player.
Unselected/eliminated choices now sit in a flat list with a thin
bottom divider instead of each getting its own bordered box, and only
the selected/correct/wrong choice pops out as a rounded, tinted card -
closer to the reference mockup and less visually busy than every
option looking like an separate boxed card.
A choice that was crossed out (right-click) and also happened to be
locked as the submitted-wrong choice, or revealed as the correct
answer, silently lost its strikethrough because the percentage-badge
branches took precedence over the elimination check in the same
if/elif chain. Decoupled the two so a choice can show both the
strikethrough and its reveal percentage at once.
The warning was purely a token-expiry countdown with no concept of
user activity, so it interrupted people who had been clicking through
questions the whole time. AuthState now tracks real input activity
globally and silently refreshes the session in the background when
the user has interacted recently, only surfacing the "Stay logged in?"
dialog for sessions that are genuinely idle. A failed background
refresh still surfaces the dialog rather than silently logging the
user out.
Flagged questions disappear from every student-facing surface (search,
exam simulation sampling, the missed-question review queue, bookmarks,
direct fetch) while staying published - flagging and unpublishing are
now separate, reversible actions with their own audit trail (who
flagged it, when, why). Backend-only for now: SUPER-only flag/unflag/
list endpoints, no client UI. Includes a one-time CLI sweep
(flag-media-questions) to flag every existing question with an image
so they can be reviewed.
Matches the mockup: the "Show Answer" button is replaced by an in-card
"Tap card to reveal answer" hint, and clicking anywhere on the card
reveals it (guarded the same way Space already was - only while at the
live card and not yet revealed).
System decks/notes are owned by a reserved sentinel user id, reusing
all existing per-user store/index plumbing unchanged. Regular users
see them under a separate "Official Decks" section, can study them
with fully independent per-user FSRS progress (lazily materialized on
first visit), but can never edit or archive them - enforced both in
the client (no Edit/Archive controls) and server-side (ownership guard
on every mutating endpoint). Includes a small idempotent seed command
(`dotnet run -- seed-flashcards`) for testing, since there's no in-app
authoring UI yet.
The study screen was still treated as a fullscreen immersive view (like
the question player), hiding the main nav sidebar entirely. The mockup
and the user's request both call for keeping the app's left menu while
studying, with the deck panel living inside the screen alongside it.
Add real session-history navigation (Previous/Next/Shuffle), compact
side panels, and a segmented progress row to match the mockup layout.
Flashcards now opens straight into Study, with library management
moved into the Options menu ("Manage Flashcards") and Export hidden
from the UI (Import stays). Shuffle is backed by a real, due-before-new
randomized pick on the backend, not a decorative toggle.