feat(console): offer the new Console in a dismissible modal - #3195
Conversation
Users on the old Console get one interruptive prompt with an explicit choice: try the new Console, or carry on with the old one. Closing it by any route counts as choosing to stay. Built on the existing Modal from $lib/components rather than a bespoke full-screen surface. wizard.showCover() would be genuinely full-screen but has never had a consumer, so it is untested in production. Snoozes for a month with backoff via hideNotification, longer than the banner's week because this is the most interruptive of the three surfaces. Never shown during onboarding. utm_medium=modal keeps it comparable with the banner and the promo card.
bun audit started failing on every branch after an advisory against js-yaml >=4.0.0 <4.3.2, reached through eslint. The existing override allowed 4.3.1, which is vulnerable. Pinned to ^4.3.2 rather than >=4.3.2, which resolved to 5.x and crossed a major boundary. GHSA-2883-xcg3-v3hh
Two defects found by running it rather than reading it. `show` starts false and the parent flips it on, so the reactive exit handler fired on mount and recorded a dismissal before the modal had ever appeared. It now only counts as an exit once the modal has actually been open. Modal also sets show = false on navigation via autoClose, which this component read as the user choosing to stay on the old Console. Navigating is not a choice, so autoClose is off; the only ways out are the close button, Escape, the backdrop, and the two footer buttons. Also moves the copy from the description slot into the body, which left a dead gap above the footer, and drops the modal from size l to m so two lines of text are not stranded in a very wide box.
Replaces the plain text modal with the cover-led layout: a real capture of the new Console, a one line pitch, the retirement notice, and the two choices. The image is the generator export rather than drawn artwork, matching how the promo card carries its cover. The retirement date is the sentence most likely to change the answer, so it sits above the actions in Alert.Inline, the same notice treatment the rest of the console uses, instead of below them as fine print. Dropped the three feature bullets. They restated what the screenshot already showed, and their accent-tinted icon tiles were not a pattern used anywhere else in the console. The retirement wording is still a placeholder pending a confirmed date.
Console (appwrite/console)Project ID: Tip Teams feature lets you group users with membership management and role permissions |
| function close(action: 'try' | 'continue') { | ||
| if (!recorded) { | ||
| recorded = true; | ||
| trackEvent('close_new_console_modal', { source: 'new_console_modal', action }); | ||
| hideNotification('newConsoleModal', { | ||
| coolOffPeriod: COOL_OFF_HOURS, | ||
| exponentialBackoff: true | ||
| }); | ||
| } | ||
|
|
||
| show = false; | ||
| } | ||
|
|
||
| function onKeydown(event: KeyboardEvent) { | ||
| if (show && event.key === 'Escape') close('continue'); | ||
| } |
There was a problem hiding this comment.
Dismissal behavior is untested
The new modal has bespoke handling for Escape, backdrop clicks, action choices, one-time snoozing, and navigation behavior, but none of these observable paths is covered by a test. A later change could silently restore premature dismissal, double-snoozing, or navigation-triggered dismissal. Add behavior-level coverage that exercises user exits and verifies the resulting notification state rather than mirroring constants or source structure.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/components/newConsoleModal.svelte
Line: 24-39
Comment:
**Dismissal behavior is untested**
The new modal has bespoke handling for Escape, backdrop clicks, action choices, one-time snoozing, and navigation behavior, but none of these observable paths is covered by a test. A later change could silently restore premature dismissal, double-snoozing, or navigation-triggered dismissal. Add behavior-level coverage that exercises user exits and verifies the resulting notification state rather than mirroring constants or source structure.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.The 768px crush had two causes: the full-width mobile button claiming the whole line, and the direction breakpoint sitting at isSmallViewport (767px) while .is-full-width-mobile applies at 768px. Dropping fullWidthMobile fixed it on its own; moving the breakpoint to isTabletViewport (1023px) as well just made the banner stack 256px earlier than it needs to, pushing "Try it now" below the copy on ordinary laptop widths. The text is ~64 characters and the button ~100px, which fits comfortably in 768px minus the shell padding, so the breakpoint goes back to 767px. Measured at 375, 767, 768, 900, 1023 and 1440: one row above 767px, stacked and centred below it, and main > header offset matches the stack height exactly at every width.
GHSA-qxc2-j82w-r537 covers @faker-js/faker <=10.4.0 and is rated high, so `bun audit --audit-level high` fails and takes the build job with it on every branch. The repo was on ^9.9.0; the fix landed in 10.6.0. faker is a runtime dependency here, not a dev one: src/lib/helpers/faker.ts ships in the console bundle and powers "generate fake data" in the databases UI. The advisory is specific to helpers.fake(), which this repo never calls, so the exposure was nil, but the audit gate does not read call sites. Nothing in src/ needed changing. Every one of the 20 faker APIs used keeps its v9 signature in the installed v10 typings, including location.latitude and location.longitude, which still return `number` and so still serialise correctly into the point and line column payloads. bun audit --audit-level high clean (18 remaining are moderate/low) bun run check 0 errors bun run test:unit 275/275 bun run build success
|
Addressed the review findings in 39e1b14:
All 290 unit tests, formatting, Svelte check (zero errors), and lint pass. The E2E suite and browser verification are still running. Please reassess the current commit and flag any remaining issues. |

Offer eligible cloud users a dismissible introduction to the new Console, with a cover image, retirement notice, and explicit choices to switch or stay. Either choice, the close button, Escape, or a backdrop click records one 30-day snooze with exponential backoff.
The modal uses a native dialog to contain keyboard focus and isolate the background, restores focus when it closes, and disables console shortcuts while open. Escape closes only the promotion, without reaching a background wizard. A reactive route check suppresses the modal throughout onboarding, including client-side navigation; route changes and parent-driven hiding do not record a dismissal.
The existing top banner remains available after dismissal. Its horizontal layout again uses the 767px breakpoint, retaining the earlier fix for the full-width mobile button. The PR also upgrades
@faker-js/fakerto 10.6.0 and raises thejs-yamloverride to 4.3.2 to address the dependency advisories that blocked CI.The retirement copy currently says “in the coming weeks”; an
@todomarks where to insert a confirmed date when one is available.Validation: all 291 unit tests, formatting, lint, and production build pass. Svelte check reports zero errors (92 existing warnings). The 16 new behavior tests exercise persisted notification eligibility across calendar time, repeated dismissals and exponential backoff, duplicate dismissal events, the switch link, parent hiding, onboarding transitions, Escape isolation, and focus restoration.
Browser verification confirms focus containment and restoration, background isolation, all five explicit exits, correct external navigation, silent onboarding transitions, and usable scrolling on short screens. All three live staging E2E journeys pass; they assert the prompt is absent during onboarding and explicitly dismiss it afterward.