Skip to content

Projects pages - #1330

Merged
microbit-matt-hillsdon merged 34 commits into
home-pagefrom
projects-pages
Sep 15, 2026
Merged

microbit-matt-hillsdon merged 34 commits into
home-pagefrom
projects-pages

Conversation

@microbit-matt-hillsdon

Copy link
Copy Markdown
Collaborator

No description provided.

@microbit-matt-hillsdon
microbit-matt-hillsdon added this pull request to stack #1331 September 14, 2026 08:57
@github-actions

Copy link
Copy Markdown

Preview build will be at
https://review-python-editor-v3.microbit.org/projects-pages/

@microbit-matt-hillsdon
microbit-matt-hillsdon marked this pull request as ready for review September 14, 2026 15:46
Base automatically changed from storage-foundation to home-page September 14, 2026 17:00
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-robert

Copy link
Copy Markdown
Collaborator

It's a lot of change, but I think this looks good. Have not tested the iframe / no indexedDB version.

@microbit-matt-hillsdon

Copy link
Copy Markdown
Collaborator Author

It's a lot of change, but I think this looks good. Have not tested the iframe / no indexedDB version.

I think it's fine for us to review that separately and follow up with fixes on home-page if needed.

@microbit-matt-hillsdon
microbit-matt-hillsdon merged commit eef8bad into home-page Sep 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants