Repository navigation
feat(admin): ordered three-level settings use the steps control - #1747
Merged
Merged
Conversation
…itch The quiz reset on leaving now has three levels stored in quiz_reset_on_leave (0 Off, 1 Moderate, 2 Strict; existing forms keep their behaviour). Strict also counts the window losing the focus, after a global grace period (Settings -> Certificates), ignoring focus moving into an iframe of the page. "Start again" reloads the page so shuffled options are shuffled again. Adds AdminUI::render_tri_toggle (severity and traffic palettes, pure CSS over native radios) and an enum type to both autosave endpoints. Closes #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
The track is 30px high and its ends are half that; the `pill` role (12px) would leave them visibly squared, so the literal stays and the budget says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
AdminUI::render_steps() draws an ordered scale of 3 to 11 values as a rail with one native radio per stop, filled up to the chosen one. It takes palette (primary, severity, traffic), per-step tones, labels (all or selected), size, shape and fluid; outside 3-11 options it prints a select and calls _doing_it_wrong() naming the control that fits. Styles live in the new ffc-control-* family in ffc-common.css, through tokens only, with no radius or z-index literal (the radius budget goes back to 5). The quiz's Leaving the Window level uses it with the severity palette. CLAUDE.md gains a Controls section saying which control fits which kind of value. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
The four geofence display modes, the two private-calendar displays and the geolocation IP API fallback move from selects to AdminUI::render_steps(), ordered from the most shown (or most permissive) to the least. Stored values are unchanged, and a value the scale does not know shows the select's old default. The API fallback wears the severity palette; the display modes keep the neutral one. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
3 of 4 tasks
# Conflicts: # languages/ffcertificate-pt_BR.l10n.php # languages/ffcertificate-pt_BR.mo # languages/ffcertificate-pt_BR.po # languages/ffcertificate.pot
rpgmem
marked this pull request as ready for review
October 13, 2026 21:40
rpgmem
enabled auto-merge (squash)
October 13, 2026 21:40
5 tasks done
6 tasks done
rpgmem
added a commit
that referenced
this pull request
Oct 13, 2026
…tab, plugin icon and "View details" (#1753) * docs(changelog): backfill the 6.38.0 release commit reference (#1711) Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(updates): give the plugin an icon on Dashboard → Updates (#1713) * feat(updates): give the plugin an icon on Dashboard → Updates GitHubUpdater now sends an icons map (1x, 2x, svg) in the update object and the View details payload, pointing at assets/img/brand/ in the installed copy, so WordPress stops drawing its grey placeholder. The directory also ships black, white, dark-screen and outline variants. PluginIconAssetsTest ties each advertised URL to a file of the right size that the release zip keeps. Closes #1712 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(updates): ship every icon variant as PNG too Each SVG variant now has a 128 px and a 256 px PNG beside it in assets/img/brand/, rendered from the SVG, and PluginIconAssetsTest checks every one exists at its size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * docs(changelog): name the PNG variants Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(updates): fill the View details modal with a banner and the readme's tabs (#1715) Closes #1714 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(updates): translate View details and open screenshots in an overlay (#1717) * feat(updates): fill the View details modal with a banner and the readme's tabs Closes #1714 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(updates): translate View details and open screenshots in an overlay Closes #1716 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(updates): read the asset suffix instead of assuming .min SCRIPT_DEBUG may already be defined by an earlier test in the process, which made the enqueued URL drop the .min suffix in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * test: read the clock on both sides of calls that snap to a boundary (#1720) The rate-limit window tests, the certificates dashboard midnight test and the export filename test read the expected value from the clock once, before or after the call, against code that rounds the current time to a minute, hour, day, week, month or site-timezone midnight. A run that crossed the boundary between the two reads failed with nothing changed. They now read it before and after and accept either. CLAUDE.md records the clock rule, and that a readme.txt change carries languages/readme-pt_BR.txt in the same PR, with the release cross-check. Closes #1718 Closes #1719 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * chore(i18n): compile the .l10n.php in wp i18n make-php's shape (#1722) The compiler wrote every .po header and no domain. It now writes WP-CLI's shape: domain from X-Domain, six headers in WP-CLI's order, then messages, with an integer-like key bare as PHP stores it. The recompiled catalogue is byte-identical to `wp i18n make-php`'s output; only the header keys changed. CLAUDE.md records the shape, and how to run the fresh-install checks against the container's MariaDB without the uninstall step that would delete a symlinked checkout. Closes #1721 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * fix(admin): dark mode for what core paints, docs navigation overflow (#1724) * fix(admin): extend dark mode to native controls and fix the docs navigation overflow - Declare color-scheme: dark on admin screens, so scrollbars and the time picker's clock follow the theme. - Style the colour picker's label and Iris popup, .button-link, checkboxes and radios with the palette. - Make the Quick Navigation search box fill its card; it was 25em in a 280px column and scrolled the card sideways. - Restore the padding core's .widefat tfoot rule removed from the Geolocation "Add new location" fields. Closes #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): cover links, title actions, notices and core boxes in dark mode A sweep of every FFC admin screen in Chromium flagged what core paints by itself on the dark canvas: links (1.87-2.65:1), "Add New" / "Export CSV", disabled primary buttons (core uses !important), colour inputs, the file button, notice links, the Publish box footer, row actions and Screen Options. Each now reads the palette. Also: the form builder's row title reads the text colour instead of the grip's grey, the submissions ID column is sized for six digits, and the QR generator's required mark uses ffc-required. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(audience): give the booking details title the theme's text colour The admin modal is appended to <body>, outside .wrap, so its heading took core's near-black h3 and vanished on the dark card. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the dark disabled primary button a forced-colors contour ForcedColorsContourTest caught it: the rule paints a ground, so under forced colours the button would lose its boundary. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * fix(admin): dark mode on the Personal Calendars and Certificate templates screens (#1726) * fix(admin): extend dark mode to native controls and fix the docs navigation overflow - Declare color-scheme: dark on admin screens, so scrollbars and the time picker's clock follow the theme. - Style the colour picker's label and Iris popup, .button-link, checkboxes and radios with the palette. - Make the Quick Navigation search box fill its card; it was 25em in a 280px column and scrolled the card sideways. - Restore the padding core's .widefat tfoot rule removed from the Geolocation "Add new location" fields. Closes #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): cover links, title actions, notices and core boxes in dark mode A sweep of every FFC admin screen in Chromium flagged what core paints by itself on the dark canvas: links (1.87-2.65:1), "Add New" / "Export CSV", disabled primary buttons (core uses !important), colour inputs, the file button, notice links, the Publish box footer, row actions and Screen Options. Each now reads the palette. Also: the form builder's row title reads the text colour instead of the grip's grey, the submissions ID column is sized for six digits, and the QR generator's required mark uses ffc-required. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(audience): give the booking details title the theme's text colour The admin modal is appended to <body>, outside .wrap, so its heading took core's near-black h3 and vanished on the dark card. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): dark mode on the Personal Calendars and Certificate templates screens Their lists and editors are WordPress post screens that AdminAssetsManager::is_ffc_page() never matches, so the toggle that puts ffc-dark-mode on <html> never loaded and they rendered light. SelfSchedulingAdmin and CertTemplateAdminScreen now enqueue the palette and the toggle themselves. The false TOGGLE_NOT_NEEDED reason for the calendar editor is rewritten, and the stale SelfSchedulingAdmin entry leaves the register. The rich-text e-mail editor's tabs, toolbar and status bar, and the "Add title" placeholder, now read the palette. Closes #1725 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the dark disabled primary button a forced-colors contour ForcedColorsContourTest caught it: the rule paints a ground, so under forced colours the button would lose its boundary. Refs #1723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the dark active TinyMCE button a forced-colors contour Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(public-design): form fields follow the form's font, and Arial is offered (#1728) Fields, selects, textareas and buttons inherit the font the labels use instead of the browser's own. Public Design offers the same three fonts as Email Signature Generator and Institutional Email: the theme's (the default), the plugin's system font, or Arial. Closes #1727 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(forms): checkbox fields render their options, and every field carries limits (#1733) A "Checkbox" field fell through to a generic input styled as a 45px text box, ignored its options and posted `on`. Options were one comma-separated string, so none could contain a comma. Required, length, range and "is one of the options" were enforced by the browser only. - Core\FormField reads a field's options (the new `choices` list or the legacy string) and its limits, and sanitises an editor row. - The field builder edits options one per row, shuffles, caps checkbox selections, and takes min/max length (ceilings 255 / 2,000), number ranges with decimal places and date ranges. A new row is a copy of the server template instead of a second markup built in JS. - Frontend\FormFieldRenderer renders radio/checkbox groups as a fieldset, the limits as attributes and a hint, and a counter; ffc-form-fields.js shuffles, counts and enforces the cap. - Submission\FieldRulesGuard enforces every rule on the server. Closes #1729 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(forms): standard fields validate as they claim, and required tags protect their fields (#1735) * feat(forms): checkbox fields render their options, and every field carries limits A "Checkbox" field fell through to a generic input styled as a 45px text box, ignored its options and posted `on`. Options were one comma-separated string, so none could contain a comma. Required, length, range and "is one of the options" were enforced by the browser only. - Core\FormField reads a field's options (the new `choices` list or the legacy string) and its limits, and sanitises an editor row. - The field builder edits options one per row, shuffles, caps checkbox selections, and takes min/max length (ceilings 255 / 2,000), number ranges with decimal places and date ranges. A new row is a copy of the server template instead of a second markup built in JS. - Frontend\FormFieldRenderer renders radio/checkbox groups as a fieldset, the limits as attributes and a hint, and a counter; ffc-form-fields.js shuffles, counts and enforces the cap. - Submission\FieldRulesGuard enforces every rule on the server. Closes #1729 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * wip: #1734 * feat(forms): standard fields validate as they claim, and required tags protect their fields - cpf_rf: an invalid CPF/RF blocks the submit through custom validity and its message stays until corrected; maxlength 14, numeric keyboard. - email: an invalid address is reported as such, checked with is_email(), instead of "Email address is required." - Name fields need at least two words and 4 characters, in the browser and in FieldRulesGuard. - "Required Certificate Tags" moves from Advanced to a new Certificates tab; the layout editor and the template screen list the configured tags instead of a fixed three. - A field a required tag calls for is badged and amber in the builder, always required, with a read-only name and no Remove; saving a form without one warns which tag has no field. Closes #1734 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(forms): forced-colors contour for the required-tag badge, canonical email in the sanitizer The badge and the locked field name paint only a ground, which forced-colors discards, so both take a contour. The sanitizer canonicalises the address before judging it, as IdentifierIdiomTest requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * fix(settings): the Certificates tab saves the required tags one per line (#1737) The section read the ffc_settings array after get_post_array() had run every element through sanitize_text_field(), which turns line breaks into spaces, so the Save button stored the list on one line and it read as one tag. The textarea is now read raw and sanitised as a textarea, and the reader also splits on whitespace, so a list already stored on one line reads again. Closes #1736 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * fix(frontend): the core installs its API when the FFC namespace already exists (#1740) ffc-form-fields.js prints before ffc-core.js and creates window.FFC, so the core's `window.FFC = window.FFC || {…}` skipped its own object and every certificate form submit failed on FFC.request is not a function. The core now installs its API over an existing namespace, keeping what was already there, and keeps modules registered before it loaded. Closes #1739 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(quiz): per-field scoring with correct answers, exact-match checkbox and range answers (#1738) * feat(quiz): per-field scoring with correct answers, exact-match checkbox and range answers (in progress) QuizQuestion reads a field's quiz configuration with the pre-#1730 rule as the fallback; QuizScorer scores choice fields by correct option(s) or by points per option, text when answered and number/date by a correct range; the editor gets a per-field quiz block with per-option correct marks and points; show-correct marks each question right or wrong; {{correct_count}} and {{question_count}}. Tests, documentation and catalogues follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(quiz): tests, documentation and catalogues for per-field scoring QuizScorer and QuizQuestion tests (each mode, legacy forms, the exact checkbox set, the floored sum, ranges, the ampersand an option can carry); the save handler stores the option rows and fits the question; the builder renders the aligned marks and the quiz block; the per-question verdict on the form. The Quiz topic describes the modes and the two new tokens; the verdict and the option mark take a context so "Correct" does not read as the verb. A level-9 row-shape cast is fixed in the persister. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(quiz): the admin quiz toggle test no longer expects the old points input The per-field quiz settings moved to the field builder, which has its own tests; this one keeps checking the form-level settings the toggle shows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(quiz): stub _x where the form editor renders the field builder The option mark now takes a translation context, and the metabox renderer test renders the builder rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(certificates): required form fields as their own list (#1742) Settings → Certificates keeps the layout tags and, apart, a table of the fields every certificate form must have (name, label, type; default name, cpf_rf, email). Email cannot be removed. {{auth_code}} stays forced; {{schedule}} is asked only of a form with an Event Schedule. A new form starts with the table's fields; in the builder their name, type and required flag are locked, and a form missing one offers an "Add the required fields" button. The save handler forces the table's type. An install that never saved the table derives it from the old tag list, and keeps the schedule check until the tab is saved again. Refs #1741 Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> * feat(quiz): window-leave reset, unpassed attempts are not certificates, manual grading (#1743) * feat(quiz): optional reset when the participant leaves the window A quiz setting, off by default, clears the scored answers when the participant switches tabs or minimises the browser. It listens to visibilitychange, not blur, so a notification, a screen reader or a click into an embedded video does not trigger it. The form warns before the participant starts, and on return an alertdialog (the shared modal) says why. Name, CPF/RF and unscored fields stay. Leaving is not an attempt. The browser counts the exits in a hidden field kept in sessionStorage; a reset quiz stores the count, capped at 999, as _quiz_focus_losses, shown read-only on the submission screen. A deterrent, not a lock, and the documentation says so. Refs #1731 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(quiz): an attempt that did not pass is not a certificate process_submission() inserts every quiz attempt as an issued certificate and schedules the email pipeline; the persister lowers the status to the attempt's result right after. Nothing downstream read the status, so a first attempt that failed was emailed a working certificate link and its auth code verified. Core\SubmissionStatus names the statuses and which of them is a certificate. The async pipeline, verification by auth code and by magic link, the REST lookup and the user dashboard (list and count) now skip a quiz attempt that did not pass. Closes #1744 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(quiz): manual grading of open answers A text or textarea question can be set to "Manual grading". An attempt with such an answer is stored as "Quiz: Awaiting Grading": no result, no certificate, and the same CPF/RF cannot start another attempt while it waits. The submission edit screen grades it through its own form (admin_post_ffc_grade_submission, capability ffc_grade_certificates, seeded from ffc_edit_certificates); saving publishes the attempt or records Retry/Failed, and emails the participant the result through two new editable templates. Closes #1732 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(quiz): seed the grade-cap flag in the integration harness The bootstrap smoke tests stub every one-shot capability flag so the Loader does not run migrations against the harness's fake $wpdb; the flag #1732 added was missing, so the grant ran and failed on wpdb::get_blog_prefix(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(quiz): strict window-leave level on a reusable steps control (#1746) * feat(quiz): strict window-leave level on a reusable three-position switch The quiz reset on leaving now has three levels stored in quiz_reset_on_leave (0 Off, 1 Moderate, 2 Strict; existing forms keep their behaviour). Strict also counts the window losing the focus, after a global grace period (Settings -> Certificates), ignoring focus moving into an iframe of the page. "Start again" reloads the page so shuffled options are shuffled again. Adds AdminUI::render_tri_toggle (severity and traffic palettes, pure CSS over native radios) and an enum type to both autosave endpoints. Closes #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(css): budget the three-position switch's pill radius The track is 30px high and its ends are half that; the `pill` role (12px) would leave them visibly squared, so the literal stays and the budget says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): steps control replaces the three-position switch AdminUI::render_steps() draws an ordered scale of 3 to 11 values as a rail with one native radio per stop, filled up to the chosen one. It takes palette (primary, severity, traffic), per-step tones, labels (all or selected), size, shape and fluid; outside 3-11 options it prints a select and calls _doing_it_wrong() naming the control that fits. Styles live in the new ffc-control-* family in ffc-common.css, through tokens only, with no radius or z-index literal (the radius budget goes back to 5). The quiz's Leaving the Window level uses it with the severity palette. CLAUDE.md gains a Controls section saying which control fits which kind of value. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the steps fieldset's phpcs annotation a real reason Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(admin): ordered three-level settings use the steps control (#1747) * feat(quiz): strict window-leave level on a reusable three-position switch The quiz reset on leaving now has three levels stored in quiz_reset_on_leave (0 Off, 1 Moderate, 2 Strict; existing forms keep their behaviour). Strict also counts the window losing the focus, after a global grace period (Settings -> Certificates), ignoring focus moving into an iframe of the page. "Start again" reloads the page so shuffled options are shuffled again. Adds AdminUI::render_tri_toggle (severity and traffic palettes, pure CSS over native radios) and an enum type to both autosave endpoints. Closes #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(css): budget the three-position switch's pill radius The track is 30px high and its ends are half that; the `pill` role (12px) would leave them visibly squared, so the literal stays and the budget says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): steps control replaces the three-position switch AdminUI::render_steps() draws an ordered scale of 3 to 11 values as a rail with one native radio per stop, filled up to the chosen one. It takes palette (primary, severity, traffic), per-step tones, labels (all or selected), size, shape and fluid; outside 3-11 options it prints a select and calls _doing_it_wrong() naming the control that fits. Styles live in the new ffc-control-* family in ffc-common.css, through tokens only, with no radius or z-index literal (the radius budget goes back to 5). The quiz's Leaving the Window level uses it with the severity palette. CLAUDE.md gains a Controls section saying which control fits which kind of value. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): ordered three-level settings use the steps control The four geofence display modes, the two private-calendar displays and the geolocation IP API fallback move from selects to AdminUI::render_steps(), ordered from the most shown (or most permissive) to the least. Stored values are unchanged, and a value the scale does not know shows the select's old default. The API fallback wears the severity palette; the display modes keep the neutral one. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the steps fieldset's phpcs annotation a real reason Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(admin): the on/off toggle joins the ffc-control family (#1749) * feat(quiz): strict window-leave level on a reusable three-position switch The quiz reset on leaving now has three levels stored in quiz_reset_on_leave (0 Off, 1 Moderate, 2 Strict; existing forms keep their behaviour). Strict also counts the window losing the focus, after a global grace period (Settings -> Certificates), ignoring focus moving into an iframe of the page. "Start again" reloads the page so shuffled options are shuffled again. Adds AdminUI::render_tri_toggle (severity and traffic palettes, pure CSS over native radios) and an enum type to both autosave endpoints. Closes #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(css): budget the three-position switch's pill radius The track is 30px high and its ends are half that; the `pill` role (12px) would leave them visibly squared, so the literal stays and the budget says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): steps control replaces the three-position switch AdminUI::render_steps() draws an ordered scale of 3 to 11 values as a rail with one native radio per stop, filled up to the chosen one. It takes palette (primary, severity, traffic), per-step tones, labels (all or selected), size, shape and fluid; outside 3-11 options it prints a select and calls _doing_it_wrong() naming the control that fits. Styles live in the new ffc-control-* family in ffc-common.css, through tokens only, with no radius or z-index literal (the radius budget goes back to 5). The quiz's Leaving the Window level uses it with the severity palette. CLAUDE.md gains a Controls section saying which control fits which kind of value. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): ordered three-level settings use the steps control The four geofence display modes, the two private-calendar displays and the geolocation IP API fallback move from selects to AdminUI::render_steps(), ordered from the most shown (or most permissive) to the least. Stored values are unchanged, and a value the scale does not know shows the select's old default. The API fallback wears the severity palette; the display modes keep the neutral one. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the steps fieldset's phpcs annotation a real reason Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): the on/off toggle joins the ffc-control family `.ffc-toggle*` becomes `.ffc-control-toggle*` everywhere it is printed (PHP, templates, JS-built markup, CSS and tests). The toggle gains the family's hover and pressed halo, a greyed disabled state instead of an opacity fade, and an optional `mark` that draws a check or a cross on the knob from the Core\Icons registry (new Icons::MASKS custom properties). The user dashboard's notification toggles drop their second implementation, which hid the checkbox with display: none and so took it out of keyboard reach. Closes #1748 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): toggle mark layers on the token scale; dashboard radius budget locked in The mark's z-index reads --ffc-z-raised and the knob keeps the default stacking, so ffc-common.css adds no layer literal. Removing the dashboard's private toggle switch left one radius literal fewer, so the budget ratchets down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * feat(admin): segmented control; steps and segmented keep their layout in a .form-table (#1751) * feat(quiz): strict window-leave level on a reusable three-position switch The quiz reset on leaving now has three levels stored in quiz_reset_on_leave (0 Off, 1 Moderate, 2 Strict; existing forms keep their behaviour). Strict also counts the window losing the focus, after a global grace period (Settings -> Certificates), ignoring focus moving into an iframe of the page. "Start again" reloads the page so shuffled options are shuffled again. Adds AdminUI::render_tri_toggle (severity and traffic palettes, pure CSS over native radios) and an enum type to both autosave endpoints. Closes #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * test(css): budget the three-position switch's pill radius The track is 30px high and its ends are half that; the `pill` role (12px) would leave them visibly squared, so the literal stays and the budget says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): steps control replaces the three-position switch AdminUI::render_steps() draws an ordered scale of 3 to 11 values as a rail with one native radio per stop, filled up to the chosen one. It takes palette (primary, severity, traffic), per-step tones, labels (all or selected), size, shape and fluid; outside 3-11 options it prints a select and calls _doing_it_wrong() naming the control that fits. Styles live in the new ffc-control-* family in ffc-common.css, through tokens only, with no radius or z-index literal (the radius budget goes back to 5). The quiz's Leaving the Window level uses it with the severity palette. CLAUDE.md gains a Controls section saying which control fits which kind of value. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): ordered three-level settings use the steps control The four geofence display modes, the two private-calendar displays and the geolocation IP API fallback move from selects to AdminUI::render_steps(), ordered from the most shown (or most permissive) to the least. Stored values are unchanged, and a value the scale does not know shows the select's old default. The API fallback wears the severity palette; the display modes keep the neutral one. Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): give the steps fieldset's phpcs annotation a real reason Refs #1745 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): the on/off toggle joins the ffc-control family `.ffc-toggle*` becomes `.ffc-control-toggle*` everywhere it is printed (PHP, templates, JS-built markup, CSS and tests). The toggle gains the family's hover and pressed halo, a greyed disabled state instead of an opacity fade, and an optional `mark` that draws a check or a cross on the knob from the Core\Icons registry (new Icons::MASKS custom properties). The user dashboard's notification toggles drop their second implementation, which hid the checkbox with display: none and so took it out of keyboard reach. Closes #1748 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): toggle mark layers on the token scale; dashboard radius budget locked in The mark's z-index reads --ffc-z-raised and the knob keeps the default stacking, so ffc-common.css adds no layer literal. Removing the dashboard's private toggle switch left one radius literal fewer, so the budget ratchets down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * feat(admin): segmented control for small unordered choices AdminUI::render_segmented() draws 2 to 6 options with no order between them as one row of native radios, the chosen one filled. Out of range it prints a select and tells the developer. Dark Mode, Code Editor Theme and the geofence Time Behavior use it, with the same stored values; the autosave badge anchors after the control so it never breaks the checked-label rule. Closes #1750 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA * fix(admin): steps and segmented controls keep their layout in a .form-table WordPress's forms.css makes every label in a .form-table fieldset inline-block with a forced margin, which outranked the stop's flex column and left each mark 28px left of its rail on every settings table that adopted the steps control. Both controls' option labels now outrank it; measured in a local WordPress, every mark sits on its stop's centre. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA --------- Co-authored-by: Claude <noreply@anthropic.com> * release: 6.39.0 — bump, finalize CHANGELOG, review docs (#1752) Bumps the three version sites, renames [Unreleased] to [6.39.0] (2026-10-13) and condenses it by story (25 bullets to 15, every cited issue kept), rewrites the Upgrade Notice, and brings the readme's Quiz Mode line, in both languages, up to per-field scoring, manual grading and the leave-the-window reset. Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1745. Stacked on #1746: until that merges, this diff also shows its commits. After the merge,
developcomes in through a merge commit and the diff shrinks to this change alone.Problem
Seven settings choose among three ordered values and were still plain selects. A select hides the order and shows only one choice at a time.
Change
AdminUI::render_steps()(from #1746) now draws these seven settings, ordered from the most shown or most permissive value to the least:ffc_geofence[datetime_hide_mode_*])ffc_geofence[geo_hide_mode])ffc_ss_/ffc_aud_private_display_mode)api_fallback)message,show_message,gps_only) instead of the first step.<label for>of each row became plain text, since the fieldset carries its own legend.Tests
New
StepsControlAdoptionTest:Mutation-checked: swapping two options in the API fallback makes the order test fail.
Rendering:
Ran: touched tests, i18n and CSS guards, PHPCS and PHPStan.
Checklist
develop; no version bumpCHANGELOG.md: one bullet citing the issue.potand.po, catalogues recompileddevelopis merged in🤖 Generated with Claude Code
https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
Generated by Claude Code