Projects pages - #1330
Merged
Merged
Projects pages#1330
Conversation
microbit-matt-hillsdon
added this pull request to stack #1331
September 14, 2026 08:57
|
Preview build will be at |
microbit-matt-hillsdon
marked this pull request as ready for review
September 14, 2026 15:46
Adds the carousel package and its swiper peer, includes its source in the Panda extraction, and extends dev:link-ui so a local ../ui checkout supplies it alongside ui and ui-patterns.
The home and projects pages take the root, as decided in the multi-project plan. Documentation links to the editor's old /:tab/:slug paths redirect to the same tab under /project; a #project: link at the root redirects to the editor with its hash, since microbit.org pages still link there. Anything else is the shared not-found page. Iframe controller mode keeps the editor at the root as its only route: the embedding page owns the URL and there are no other pages. The mode is provided through context so the URL builders pick the right shape. The host strips a #project: hash from the current URL rather than the boot URL, which no longer names the page the editor is on after the redirect.
Projects lists, creates, opens, renames, duplicates and deletes projects and keeps the file system on the open one. The host now takes the file system's persistent storage as a promise that Projects resolves when a project is first opened, so the pages can render without choosing one; later opens go through FileSystem.switchStorage. The database opens at boot and reports itself active; the editor route's loader asks Projects for a project, sharing one choice between concurrent calls. The tab's project is chosen as before, then the most recent project rather than a new one, so a straight-to-editor bookmark reopens your work. A new project is created with the starter program so it can be opened by switching, which does not run the host's initialisation. Pending writes are stamped with the edit time rather than the flush time, so "last modified" orders projects by when they were edited. Flushes report a change and the IndexedDB storage no longer closes the shared connection. Tabs tell each other about changes over a BroadcastChannel; a tab with the changed project open reloads it, or lets go of a deleted one. The router is created once at module level: a browser router runs its loaders on creation and StrictMode's double render made two, each creating a project.
Composed from @microbit/ui-patterns' project components and @microbit/ui-carousel, with app-local layout, banner, resource cards and what each action does. The home page shows the banner, the user's projects with a new-project card and a link to see them all, and rows of project ideas, teacher resources and help. The projects page has search, sort, multi-select and a toolbar that slides in from the bottom on narrow screens. Both take their list from the route loader, so there is no empty flash, and stay current through Projects' change events. Without the projects database the pages redirect to the editor, which is then the whole app. A #project: link at the root goes to the editor with its hash. Opening a project resets the editor's file to main.py. Theme images resolve per file, as in ml-trainer, so the branded package only ships what differs; the OSS defaults are neutral placeholders. Project management events follow ml-trainer's names and are documented. The carousel's catalog joins the compiled messages.
Shown only when the projects database is active and not in iframe mode, where there is no home page to go to.
Page objects for the home and projects pages, with the card and menu helpers shared between them, and specs for creating, opening, renaming, duplicating and deleting projects from cards and the toolbar, search and sort, the legacy documentation redirects, not-found, a #project: link at the root, and a new tab opening the most recent project.
With the sidebar collapsed there is no room for a home button beside the logo, so the logo links to the home page when the projects database is active outside iframe mode. Otherwise it keeps its microbit.org link, as the fallback and iframe cases have no home page to go to. Short term; revisit with the header. E2e gets an App.goHome helper for the link.
ProjectIcon mirrors ml-trainer's component of the same name. On the projects page it drops below the selection checkbox's 60px hover strip so the two do not crowd the corner; the home row gets a matching inset. The theme's spacing scale is smaller than Panda's default, so the margins were measured in the browser rather than taken from ml-trainer.
PendingMigration holds the link the app booted with and hands it out once. The Projects session takes it when the editor first chooses a project and creates one from it, ahead of the tab's current project, so a link no longer overwrites whatever was open. Without the database the host takes it as before and writes it into the single implicit project. The editor route's loader redirects off the hash once the link has become a project; the session cannot strip it itself because the router commits the navigation, hash included, after the loader returns.
A hex is a whole program, so it always becomes a new project, whether dropped on the editor, chosen from the Files tab, or imported from the home page. Other files join the open project from the editor, replacing any with the same names, and become a new project from the home page, where a single script becomes main.py and names the project. Ideas from the documentation open as new projects too. Without the projects database a hex or idea replaces the single implicit project, with no confirmation. ProjectImporter (src/project/project-import.tsx) holds this for both surfaces. The confirm-replace dialog, the choose-main-script dialog and the replace-or-add flow go, along with Reset project; replacing a program now means importing from the home page. The Project tab is Files, Open is Add files and Open leaves the action bar. FileSystem.filesFromHex reads a hex with a separate file system so the open project is untouched; the MicroPython hex is fetched once for both. project_import gains a surface param; project_reset is retired.
Use the solid button variant for Import, rename the row to "My projects" with a "View all" link, and add the info tooltip explaining that projects are stored in the browser. The tooltip text is copied from ml-trainer. Tidying the catalogs also removes translations of strings that an earlier commit dropped from English, which the CI tidy check requires.
The carousel fixes slides at 260px. Copying ml-trainer's token width gave 225px under this app's dense preset, leaving slack inside every slide so the resource rows looked more widely spaced than the projects row.
…ent with ml-trainer Add dev:link-theme, which builds ../python-editor-v3-microbit and symlinks it into node_modules, so the branded home page is the routine local view. Rename the help row to "Help" and point the banner's Learn more at the brand's user guide link, hidden when a deployment has none.
A failed flush raises a saveerror event on the Projects session, logged to Sentry once per session because writes are per keystroke. The toast and its strings are ml-trainer's so Crowdin dedupes them: a quota error says browser storage is full, anything else that the save failed. Page actions that hit the quota show the same toast; their other failures keep the generic unexpected-error toast, since "failed to save" would misdescribe a failed delete or rename.
…der, and drop file names from project cards BrandConfig gains AppLogo and OrgLogo with ml-trainer's LogoProps shape so the brand packages match. The header shows the organisation logo, a divider and the wordmark at the prototype's sizes; the OSS build has a text wordmark and no organisation logo. The header buttons are pinned to ml-trainer's 48px/24px because this app's dense preset would shrink them; the menus take a css override for that. Project cards no longer list file names, which almost always read main.py. The names are still loaded for the projects page search.
"Python for the BBC micro:bit" and "Write a program, try it in the simulator, then send it to your micro:bit." replace the flash and MicroPython wording, which is ours rather than a student's.
…iner's arrow The ghost variant did not stand out on the brand colour. BackArrow is copied from ml-trainer unchanged rather than promoted to @microbit/ui.
…menus The menus' sidebar variant, made for the editor's black chrome, brought its white-pill hover to the purple header. The family shows no hover state on icon buttons in the brand-coloured bar, so the pages pass the base recipe's plain variant; the editor keeps sidebar.
…one, and confirm before replacing files The editor's drop target had moved from around the editor to around the router when the pages arrived, so a drop on a page took the editor path. On the projects page in a tab with no project open yet that write waited silently on the storage promise and landed in whatever project was opened next; on the home page the nested page target took the drop and the outer overlay, never seeing a drag-leave, stayed over the editor. The target now wraps the editor routes only, and the projects page has its own with the home page's semantics: the files make a new project that opens in the editor. Without the projects database a hex or idea asks before replacing an edited project again, with the strings from main so translations survive; the before-unload prompt only guards closing the tab, not an in-tab replace. Adding files that would overwrite existing ones asks first in every mode. load-error-mixed no longer claims a hex replaces the project.
…ebar notice's More button The pages had no notice; they now show ml-trainer's band with its longer copy and a Feedback button, under the header. The editor keeps its short sidebar notice. Both are English only, as they show on non-public stages only. The welcome video is reached from the welcome dialog alone.
The three animated gifs give way to the site's stills, so the imports and the OSS placeholders change extension.
A 4x3 grey rectangle for cards and an empty SVG for the banner background, as ml-trainer has, in place of drawn illustrations.
- ProjectSearchInput, ProjectSortInput and ProjectSortDirection. - useProjectActions takes selectedIds as an array; its delete action is `delete`. useProjectPageActions passes the array through. - ProjectCard's onToggleSelected. - Skip-to-toolbar uses ProjectsToolbar's focus handle instead of a wrapper ref and DOM query. - sortProjects collates in the app locale.
Copyright, cookies, privacy and terms; the app store badges are left out. The copyright holder comes from the brand package, as in ml-trainer.
@microbit/ui 0.5.0, ui-carousel 0.4.0 and ui-patterns 0.7.0 replace the linked ../ui checkout. The theme version in build.yml is a temporary build from the theme package's projects-pages branch.
Two bugs, both only visible where the app isn't served from the root, so neither shows in local development and both are live on the review build. The editor's loader redirected to `request.url`'s pathname to drop the hash, but that carries the basename and `redirect` adds it again, landing on /v/3/v/3/project. It now strips the basename first. The home page loader only took a hash starting with #project:, while microbit.org links put it behind the v2 editor's #import: prefix. Such a link reached the home page with its program silently dropped.
The assertions took the app to be at the root, so on the review and staging deployments the home page's URL, which is the base URL without its trailing slash, matched nothing. appUrlPattern builds the expected URL from E2E_BASE_URL, as appUrl already does, and is stricter than the patterns it replaces: /projects no longer satisfies an assertion for /project.
Tests that drive only the home or projects page never took the app fixture, so on Foundation builds they met the compliance notice. It is a modal dialog, which hides the page from the accessibility tree, and the headings they wait for did not exist.
The html reporter writes playwright-report/, so the upload step found nothing and failing runs left no screenshots or traces behind.
react-router renders the route at "/" as the bare basename, so the app's own home page link is /projects-pages, not /projects-pages/. The deployments serve that; vite preview, which the e2e tests run against, returned its own 404 page, so reloading the home page failed.
The sidebar logo links home now that there is a home page, but the default brand config left squareLogo and horizontalLogo unset. The boxes that hold them take their height from their content, so an OSS build had a zero-height link and no way to reach the home page. It also stopped the e2e tests running against a build without the brand package.
… bottom FileDropTarget fixed its height at 100% of its parent, which suited the editor's full-height pane but sized the home page's target to one viewport regardless of the content inside it. The rows overflowed the target and the new footer was laid out at the one-viewport mark, roughly half way down the page, with the drag overlay only dimming the first screen. The target now takes its layout from the caller: the editor keeps the full height, the home and projects pages grow with their content. On tall windows the footer is pushed to the bottom of the window.
microbit-matt-hillsdon
force-pushed
the
projects-pages
branch
from
September 14, 2026 17:00
02e47c5 to
8d7434a
Compare
Collaborator
|
It's a lot of change, but I think this looks good. Have not tested the iframe / no indexedDB version. |
Collaborator
Author
I think it's fine for us to review that separately and follow up with fixes on home-page if needed. |
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.
No description provided.