Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ notes. Entries describe what users experience, not internal refactors.

### Fixed

- The language menu no longer flashes open and stretches the page header while
the page is still loading.
- Select lists and the dictation privacy note open above the AI assistant panel
instead of behind it.
- Local references attached while saved chat history is loading remain attached
when the conversation finishes restoring.
- Reloading after a completed save restores the saved document instead of an
Expand Down
65 changes: 65 additions & 0 deletions docs/explorations/2026-10-24-ranui-0-5-2-language-menu-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ranui 0.5.2: the language menu no longer flashes open

## Symptom

On the first load of `/`, the localized landing pages, the content pages and
`/history`, the language menu briefly showed all seven languages in a column.
The header stretched from 38 px to about 300 px, then snapped back.

## Cause

The menu is an `<r-popover>` whose links are written straight into the HTML
(`bin/pages/chrome.mjs`, `index.html`). Until the element is defined, the
browser lays those links out in flow. On the static pages the definition comes
from a deferred IIFE, and on `/history` from the Vite bundle.

`home.css` had `:not(:defined)` fallbacks for `r-button` and `r-theme-switch`,
but nothing hid `r-content`. Measured on production with the defining scripts
blocked, the menu host was 215–223 × 246–266 px on `/`, `/zh-CN/`, `/help` and
`/history`.

## Fix: in ranui, not here

The fix was made in the component library (chaxus/ran#444, released as ranui
0.5.2). `ranui.css` now ships `:not(:defined)` rules for every component whose
light-DOM children must stay hidden until the element upgrades: r-popover's
`r-content`, r-select's options, r-tabs, r-modal, r-reasoning, r-route,
r-markdown, r-math, r-mermaid and r-scratch.

This site vendors that sheet as `public/ran-tokens.css`. `bin/build.sh`
re-syncs it on every build, and every page links it in `<head>` before first
paint. Upgrading ranui is therefore the whole fix; no site CSS was added.

ranui also documents `<r-content hidden>` / `<r-option hidden>`, which lets the
browser hide that content even when the sheet is missing. It is not used here:
the sheet is already loaded before first paint on every page, so the attribute
would only add markup tied to the component's internals.

The `r-button` / `r-theme-switch` fallbacks in `home.css` stay. They reserve
the site's own button sizes before upgrade, which is a different problem from
content leaking out.

## Same upgrade

- **ranuts 0.5.2 and @alixex/ranview 0.2.2.** They ship with ranui as one
release set, so the minimum-release-age exclusion pin moves to ranview 0.2.2.
- **DOMPurify 3.4.17.** ranui 0.5.2 depends on it, but the workspace override
still forced 3.4.16, which has one critical and one high advisory. The
override now names 3.4.17. This is dependency hygiene, not a user-facing
change: only ranui's Markdown, Mermaid and Math chunks import DOMPurify, and
this app loads none of them.
- **Lockfile resolution.** The existing lockfile kept ranview 0.2.1, published
within the release-age window. With the exclusion moved to 0.2.2, pnpm
refused it before resolving. The lockfile was resolved once with both
versions allowed, then the temporary 0.2.1 entry was removed. A
`--frozen-lockfile` install passes against the final policy.

## Test and reverse validation

`test/e2e/lang-menu-before-upgrade.spec.ts` runs on `/`, `/zh-CN/`, `/help`
and `/history`. It blocks `/ranui-iife/*.iife.js` and `/assets/*.js` so
`r-popover` is never defined. It then asserts that the list has no height and
the menu stays shorter than 48 px.

With `public/ran-tokens.css` swapped back to the 0.5.1 copy, all four cases
fail, with the menu at 245–266 px. With 0.5.2 they pass.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"@ranuts/converter": "workspace:*",
"@ranuts/shared": "workspace:*",
"pdfjs-dist": "5.4.624",
"ranui": "0.5.1",
"ranuts": "0.5.1"
"ranui": "0.5.2",
"ranuts": "0.5.2"
}
}
4 changes: 2 additions & 2 deletions packages/chat-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
},
"dependencies": {
"marked": "16.4.2",
"ranui": "0.5.1",
"ranuts": "0.5.1"
"ranui": "0.5.2",
"ranuts": "0.5.2"
},
"devDependencies": {
"typescript": "^6.0.3"
Expand Down
4 changes: 2 additions & 2 deletions packages/converter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
},
"dependencies": {
"@ranuts/shared": "workspace:*",
"ranui": "0.5.1",
"ranuts": "0.5.1"
"ranui": "0.5.2",
"ranuts": "0.5.2"
},
"devDependencies": {
"typescript": "^6.0.3"
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"prepare": "tsc -p tsconfig.json"
},
"dependencies": {
"ranuts": "0.5.1"
"ranuts": "0.5.2"
},
"devDependencies": {
"typescript": "^6.0.3"
Expand Down
62 changes: 31 additions & 31 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ packages:
minimumReleaseAgeExclude:
- ranui
- ranuts
- '@alixex/ranview@0.2.1'
- '@alixex/ranview@0.2.2'
patchedDependencies:
'@mlc-ai/web-llm@0.2.85': patches/@mlc-ai__web-llm@0.2.85.patch
'@wllama/wllama@3.6.1': patches/@wllama__wllama@3.6.1.patch
# Security fixes for transitive dependencies; remove when upstream ranges
# select patched releases (KaTeX currently remains on Mermaid's 0.16 range).
overrides:
dompurify: 3.4.16
dompurify: 3.4.17
katex: 0.18.2
source-map-js: 1.2.2
undici: 8.10.2
2 changes: 1 addition & 1 deletion public/ran-tokens.css

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions test/e2e/lang-menu-before-upgrade.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect, test } from './lib/l0';

// The language menu is an <r-popover> whose seven links are written straight
// into the page. Until the element is defined (a deferred IIFE on the static
// pages, the Vite bundle on /history) the browser lays those links out in flow
// unless ranui.css (vendored as /ran-tokens.css) hides them; the header used to
// grow from 38px to ~300px and snap back. Withhold every script that could
// define it and check the menu stays a single trigger.
test.use({ serviceWorkers: 'block' });

for (const path of ['/', '/zh-CN/', '/help', '/history']) {
test(`${path}: the language menu does not expand before r-popover is defined`, async ({ page }) => {
await page.route('**/ranui-iife/*.iife.js', (route) => route.abort());
await page.route('**/assets/*.js', (route) => route.abort());
await page.goto(path, { waitUntil: 'load' });

const menu = page.locator('.lang-menu').first();
await expect(menu).toBeAttached();
const state = await menu.evaluate((host) => ({
defined: Boolean(customElements.get('r-popover')),
height: host.getBoundingClientRect().height,
listHeight: host.querySelector('.lang-list')?.getBoundingClientRect().height ?? 0,
}));
expect(state.defined).toBe(false);
expect(state.listHeight).toBe(0);
expect(state.height).toBeLessThan(48);
});
}
Loading