Skip to content

compat: Print the web's element prop - #42

Closed
Tzyito wants to merge 1 commit into
longbridge:mainfrom
Tzyito:web-element
Closed

Tzyito wants to merge 1 commit into
longbridge:mainfrom
Tzyito:web-element

Conversation

@Tzyito

@Tzyito Tzyito commented Nov 9, 2026

Copy link
Copy Markdown

NSH adds a view prop of the web, web={{ element }} (longbridge/nsh#1130): the HTML element the web draws a view or text as where role cannot say it (p, nav, article, time, ...). The checker prints it with the other mounted props, so the case that sets it can check it.

It is behind nsh_has_web_element, detected in build.rs from the host props as windowControl is, so the checker still builds against NSH checkouts without it and this can merge on its own.

How to test

NSH_COMPAT_APP=browser cargo xtask compat in an NSH checkout of longbridge/nsh#1130 (feat/web-pages): the props-view-web-element steps print element (article, time, p) and pass. modules and props pass too.

🤖 Generated with Claude Code

NSH adds a view prop of the web, `web={{ element }}`: the HTML element
the web draws a view or text as where `role` cannot say it (`p`, `nav`,
`article`, `time`, ...). The checker prints it with the other mounted
props, so the case that sets it can check it.

It is behind `nsh_has_web_element`, detected in `build.rs` from the host
props as `windowControl` is, so the checker still builds against NSH
checkouts without it and this can merge on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Tzyito
Tzyito requested a review from huacnlee as a code owner November 9, 2026 05:56
huacnlee added a commit to longbridge/nsh that referenced this pull request Nov 9, 2026
…1130)

## Description

Sites published with `nsh package --target web` are single pages to a
browser and to search engines: links are no `<a href>`, every page has
the app's title and no
description, the markup is `div`s, and a right click on a link offers
nothing. This makes a published app a site:

- **Document elements.** The web draws a view or text as the HTML
element its props say it is: `role="header"` is
`h1`–`h6` by `accessibilityLevel` (`h2` without one), a `Link` with an
`href` is `<a href>` (from the `href` prop
`Link` already sends its view, #1289), and `web={{ element }}` names
only what `role` cannot say (`p`, `nav`,
`header`, `footer`, `main`, `article`, `section`, `aside`, `ul`, `ol`,
`li`, `strong`, `em`, `blockquote`,
`figure`, `figcaption`, `time`, `address`). A plain click on a link
stays the app's (`Link`'s `onOpen`); a click
with a modifier or the middle button opens `href`; a right click shows
the browser's link menu unless the app
shows its own (`onContextMenu`). Other platforms ignore `web={{ element
}}`.
- **The page's head.** `nsh/web`'s `Head` sets the title, description,
canonical URL, language, meta and JSON-LD
while it is mounted; a later one wins for what it sets. Native hosts
render nothing for it.
- **Data the page was served with.** `runModule(code, { params })` gives
the app its `host.params` on the web as
the native hosts do, and the packaged page passes the JSON of a `<script
type="application/json"
id="nsh-params">` it carries, so a server that already has the first
screen's data hands it over with the page.

A URL for each page is #982's: `UrlScheme.url()` / `setUrl` and
`NavStack`'s `onChange`, with the app mapping its
routes to addresses. An earlier `NavStack` `linking` prop is removed
from this PR (`ab92b96b`), following review.
The text fix this PR carried is #1225, merged on its own.

No rendering on a server is part of this; pages still render in the
browser.

## Public API

- `web={{ element?: WebElement }}` on every view and text
(`WebHostProps`; `WebElement` = `"p" | "nav" | "header" |
"footer" | "main" | "article" | "section" | "aside" | "ul" | "ol" | "li"
| "strong" | "em" | "blockquote" |
"figure" | "figcaption" | "time" | "address"`): the HTML element the web
draws it as where `role` cannot say it.
- Rust: `nsh_core::WebElement`, `Prop::WebElement(Option<WebElement>)`,
`ViewProps::web_element`, builder method
  `.web_element(WebElement)`.
- Module `nsh/web`: `Head(props: HeadProps): JSX.Element`, `HeadProps {
title?, description?, canonical?, lang?,
meta?: HeadMeta[], jsonLd? }`, `HeadMeta { name?, property?, content }`.
- Web runtime: `Player.runModule(entry, { params })`; wasm export
`nsh_set_host_params(ptr, len)` (internal, named
  only in `docs/dev/web.md`).
- Packaged web page: reads `<script type="application/json"
id="nsh-params">` as the app's `host.params`.

No new top-level props: headings and links come from the existing `role`
/ `accessibilityLevel` and `Link`'s `href`.

## How to Test

- `cargo test -p nsh-core -p nsh-js` (`web_element_is_a_view_prop`,
`nsh/web`'s declarations, `WebHostProps`).
- `node --test crates/nsh-platform-web/web/nsh.test.mjs
crates/nsh-platform-web/web/browser/page.test.mjs`
(the element of a heading, a link and `web={{ element }}`; heads; a
link's context menu).
- `cargo xtask compat`: `browser`'s `props-view-web-element`, `modules`'
`mod-web`. The `web.element` steps need
longbridge/nsh-compat#42 (the checker prints `element`); it builds
against `main` too, so it can merge first.
- By hand: `nsh package <app> --target web --run` with an app using
them; view the source (Cmd+Option+U), check
  the head, right click a link.

## Checklist

- [x] Docs: `docs/dev/web.md`, `docs/dev/elements.md`,
`docs/dev/js-modules.md`, `website/` (English and zh-CN).
- [x] Compat cases for the new API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: tzyito <zhiyong.tian@longbridge-inc.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Jason Lee <huacnlee@gmail.com>
madcodelife added a commit that referenced this pull request Nov 9, 2026
longbridge/nsh#1323 put `EmojiPicker` behind nsh-base's `emoji-picker`
feature. NSH's own compat run passes `--features nsh-base/emoji-picker`,
but this repository's CI runs plain `cargo test`. As a result, the base
and gallery apps fail to load ("Could not find export 'EmojiPicker' in
module 'nsh/base'"), and `checkout_apps_run` fails, on `main` since
dae4166 and on #42.

This change declares the feature in the manifest, as #33 did for
`shader`. That fixes CI and local runs alike.

Verified locally on macOS against NSH main (fc8058ed) with #42 merged
in. With the feature on and longbridge/nsh#1431 applied, `cargo test`
passes 32/32. #1431 fixes an engine panic in `cancel_press` that the
same apps hit next.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@madcodelife

Copy link
Copy Markdown
Member

Thank you, @Tzyito! #43 landed the same change a few hours earlier (cfee7a5): web.element is printed from p.web_element behind nsh_has_web_element, and its probe also checks the spec's platform is Web, so another platform's element prop cannot turn it on. This branch now conflicts with main in build.rs and adds nothing on top, so I'm closing it as superseded by #43.

@Tzyito
Tzyito deleted the web-element branch November 10, 2026 05:50
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